Files
calendar/packages/features/ee/dsync/lib/getAttributesFromScimPayload.ts
T

116 lines
3.3 KiB
TypeScript

import type { DirectorySyncEvent } from "@boxyhq/saml-jackson";
import logger from "@calcom/lib/logger";
import { safeStringify } from "@calcom/lib/safeStringify";
const log = logger.getSubLogger({ prefix: ["getAttributesFromScimPayload"] });
type ScimUserAttributeName = string;
type ScimUserAttributeValue = string | string[];
/**
* event.data.raw has this format
* {
* "schemas": [
* "urn:ietf:params:scim:schemas:core:2.0:User",
* "segment",
* "territory"
* ],
* "userName": "member@samldemo.com",
* "name": {
* "givenName": "Member SAML Demo",
* "familyName": "Member SAML Demo"
* },
* "emails": [
* {
* "primary": true,
* "value": "member@samldemo.com"
* }
* ],
* "displayName": "Member SAML Demo",
* "territory": {
* "territory": "NAM"
* },
* "segment": {
* "segment": "SMB"
* },
* "externalId": "00ukzk1wrsKZqofit5d7",
* "groups": [],
* "active": true,
* "id": "b36ba9fa-783b-44e6-a770-a652cb9d71ba"
* }
*
* Transforms above to
* {
* "territory": "NAM",
* "segment": "SMB"
* }
*/
function getAttributesFromScimPayload(
event: DirectorySyncEvent
): Record<ScimUserAttributeName, ScimUserAttributeValue> {
const scimUserAttributes: Record<ScimUserAttributeName, ScimUserAttributeValue> = {};
if (event.event !== "user.created" && event.event !== "user.updated") {
log.error("getAttributesFromScimPayload", `Unsupported event: ${event.event}`);
return scimUserAttributes;
}
const raw = event.data.raw;
raw.schemas.forEach((schema: unknown) => {
if (schema === "urn:ietf:params:scim:schemas:core:2.0:User") {
// Core schema has payload in the root
const { schemas: _schemas, ...namespaceData } = raw;
collectAttributes(namespaceData);
return;
}
const namespaceName = schema;
if (typeof namespaceName !== "string") {
log.error(
"getAttributesFromScimPayload",
`Namespace name is not a string ${safeStringify(namespaceName)}`
);
return;
}
const namespaceData = raw[namespaceName];
if (!namespaceData) {
log.warn("getAttributesFromScimPayload", `Namespace data for ${namespaceName} is null. Ignoring it.`);
return;
}
collectAttributes(namespaceData);
});
log.debug("Collected attributes", `Attributes: ${safeStringify(scimUserAttributes)}`);
return scimUserAttributes;
function collectAttributes(data: Record<string, unknown>) {
Object.entries(data).forEach(([customAttributeName, value]) => {
if (!value) {
log.warn(
"getAttributesFromScimPayload",
`Custom attribute ${customAttributeName} is null. Ignoring it.`
);
return;
}
if (scimUserAttributes[customAttributeName]) {
log.warn(
"getAttributesFromScimPayload",
`Custom attribute ${customAttributeName} already exists. Might be coming from different namespace. Ignoring it.`
);
return;
}
// TODO: Support number as well as Attribute support number type
if (
typeof value === "string" ||
(value instanceof Array && value.every((item) => typeof item === "string"))
) {
scimUserAttributes[customAttributeName] = value;
}
});
}
}
export default getAttributesFromScimPayload;