scimmy
Version:
SCIMMY - SCIM m(ade eas)y
538 lines (511 loc) • 53.4 kB
JavaScript
import Types from './types.js';
/**
* SCIM User Schema
* @alias SCIMMY.Schemas.User
* @summary
* * Ensures a User instance conforms to the User schema set out in [RFC7643§4.1](https://datatracker.ietf.org/doc/html/rfc7643#section-4.1).
*/
class User extends Types.Schema {
/** @type {"urn:ietf:params:scim:schemas:core:2.0:User"} */
static get id() {
return User.#definition.id;
}
/** @implements {SCIMMY.Types.Schema.definition} */
static get definition() {
return User.#definition;
}
/** @private */
static #definition = new Types.SchemaDefinition("User", "urn:ietf:params:scim:schemas:core:2.0:User", "User Account", [
new Types.Attribute("string", "userName", {required: true, uniqueness: "server", description: "Unique identifier for the User, typically used by the user to directly authenticate to the service provider. Each User MUST include a non-empty userName value. This identifier MUST be unique across the service provider's entire set of Users. REQUIRED."}),
new Types.Attribute("complex", "name", {description: "The components of the user's real name. Providers MAY return just the full name as a single string in the formatted sub-attribute, or they MAY return just the individual component attributes using the other sub-attributes, or they MAY return both. If both variants are returned, they SHOULD be describing the same name, with the formatted name indicating how the component attributes should be combined."}, [
new Types.Attribute("string", "formatted", {description: "The full name, including all middle names, titles, and suffixes as appropriate, formatted for display (e.g. 'Ms. Barbara J Jensen, III')."}),
new Types.Attribute("string", "familyName", {description: "The family name of the User, or last name in most Western languages (e.g. 'Jensen' given the full name 'Ms. Barbara J Jensen, III')."}),
new Types.Attribute("string", "givenName", {description: "The given name of the User, or first name in most Western languages (e.g. 'Barbara' given the full name 'Ms. Barbara J Jensen, III')."}),
new Types.Attribute("string", "middleName", {description: "The middle name(s) of the User (e.g. 'Jane' given the full name 'Ms. Barbara J Jensen, III')."}),
new Types.Attribute("string", "honorificPrefix", {description: "The honorific prefix(es) of the User, or title in most Western languages (e.g. 'Ms.' given the full name 'Ms. Barbara J Jensen, III')."}),
new Types.Attribute("string", "honorificSuffix", {description: "The honorific suffix(es) of the User, or suffix in most Western languages (e.g. 'III' given the full name 'Ms. Barbara J Jensen, III')."})
]),
new Types.Attribute("string", "displayName", {description: "The name of the User, suitable for display to end-users. The name SHOULD be the full name of the User being described, if known."}),
new Types.Attribute("string", "nickName", {description: "The casual way to address the user in real life, e.g. 'Bob' or 'Bobby' instead of 'Robert'. This attribute SHOULD NOT be used to represent a User's username (e.g. 'bjensen' or 'mpepperidge')."}),
new Types.Attribute("reference", "profileUrl", {referenceTypes: ["external"], description: "A fully qualified URL pointing to a page representing the User's online profile."}),
new Types.Attribute("string", "title", {description: "The user's title, such as 'Vice President'."}),
new Types.Attribute("string", "userType", {description: "Used to identify the relationship between the organization and the user. Typical values used might be 'Contractor', 'Employee', 'Intern', 'Temp', 'External', and 'Unknown', but any value may be used."}),
new Types.Attribute("string", "preferredLanguage", {description: "Indicates the User's preferred written or spoken language. Generally used for selecting a localized user interface; e.g. 'en_US' specifies the language English and country US."}),
new Types.Attribute("string", "locale", {description: "Used to indicate the User's default location for purposes of localizing items such as currency, date time format, or numerical representations."}),
new Types.Attribute("string", "timezone", {description: "The User's time zone in the 'Olson' time zone database format, e.g. 'America/Los_Angeles'."}),
new Types.Attribute("boolean", "active", {description: "A Boolean value indicating the User's administrative status."}),
new Types.Attribute("string", "password", {direction: "in", returned: false, description: "The User's cleartext password. This attribute is intended to be used as a means to specify an initial password when creating a new User or to reset an existing User's password."}),
new Types.Attribute("complex", "emails", {multiValued: true, description: "Email addresses for the user. The value SHOULD be canonicalized by the service provider, e.g. 'bjensen@example.com' instead of 'bjensen@EXAMPLE.COM'. Canonical type values of 'work', 'home', and 'other'."}, [
new Types.Attribute("string", "value", {description: "Email addresses for the user. The value SHOULD be canonicalized by the service provider, e.g. 'bjensen@example.com' instead of 'bjensen@EXAMPLE.COM'. Canonical type values of 'work', 'home', and 'other'."}),
new Types.Attribute("string", "display", {description: "A human-readable name, primarily used for display purposes. READ-ONLY."}),
new Types.Attribute("string", "type", {canonicalValues: ["work", "home", "other"], description: "A label indicating the attribute's function, e.g. 'work' or 'home'."}),
new Types.Attribute("boolean", "primary", {description: "A Boolean value indicating the 'primary' or preferred attribute value for this attribute, e.g. the preferred mailing address or primary email address. The primary attribute value 'true' MUST appear no more than once."})
]),
new Types.Attribute("complex", "phoneNumbers", {multiValued: true, uniqueness: false, description: "Phone numbers for the User. The value SHOULD be canonicalized by the service provider according to the format specified in RFC 3966, e.g. 'tel:+1-201-555-0123'. Canonical type values of 'work', 'home', 'mobile', 'fax', 'pager', and 'other'."}, [
new Types.Attribute("string", "value", {description: "Phone number of the User."}),
new Types.Attribute("string", "display", {description: "A human-readable name, primarily used for display purposes. READ-ONLY."}),
new Types.Attribute("string", "type", {canonicalValues: ["work", "home", "mobile", "fax", "pager", "other"], description: "A label indicating the attribute's function, e.g. 'work', 'home', 'mobile'."}),
new Types.Attribute("boolean", "primary", {description: "A Boolean value indicating the 'primary' or preferred attribute value for this attribute, e.g. the preferred phone number or primary phone number. The primary attribute value 'true' MUST appear no more than once."})
]),
new Types.Attribute("complex", "ims", {multiValued: true, uniqueness: false, description: "Instant messaging addresses for the User."}, [
new Types.Attribute("string", "value", {description: "Instant messaging address for the User."}),
new Types.Attribute("string", "display", {description: "A human-readable name, primarily used for display purposes. READ-ONLY."}),
new Types.Attribute("string", "type", {canonicalValues: ["aim", "gtalk", "icq", "xmpp", "msn", "skype", "qq", "yahoo"], description: "A label indicating the attribute's function, e.g. 'aim', 'gtalk', 'xmpp'."}),
new Types.Attribute("boolean", "primary", {description: "A Boolean value indicating the 'primary' or preferred attribute value for this attribute, e.g. the preferred messenger or primary messenger. The primary attribute value 'true' MUST appear no more than once."})
]),
new Types.Attribute("complex", "photos", {multiValued: true, uniqueness: false, description: "URLs of photos of the User."}, [
new Types.Attribute("reference", "value", {referenceTypes: ["external"], description: "URL of a photo of the User."}),
new Types.Attribute("string", "display", {description: "A human-readable name, primarily used for display purposes. READ-ONLY."}),
new Types.Attribute("string", "type", {canonicalValues: ["photo", "thumbnail"], description: "A label indicating the attribute's function, i.e., 'photo' or 'thumbnail'."}),
new Types.Attribute("boolean", "primary", {description: "A Boolean value indicating the 'primary' or preferred attribute value for this attribute, e.g. the preferred photo or thumbnail. The primary attribute value 'true' MUST appear no more than once."})
]),
new Types.Attribute("complex", "addresses", {multiValued: true, description: "A physical mailing address for this User. Canonical type values of 'work', 'home', and 'other'. This attribute is a complex type with the following sub-attributes."}, [
new Types.Attribute("string", "formatted", {description: "The full mailing address, formatted for display or use with a mailing label. This attribute MAY contain newlines."}),
new Types.Attribute("string", "streetAddress", {description: "The full street address component, which may include house number, street name, P.O. box, and multi-line extended street address information. This attribute MAY contain newlines."}),
new Types.Attribute("string", "locality", {description: "The city or locality component."}),
new Types.Attribute("string", "region", {description: "The state or region component."}),
new Types.Attribute("string", "postalCode", {description: "The zip code or postal code component."}),
new Types.Attribute("string", "country", {description: "The country name component."}),
new Types.Attribute("string", "type", {canonicalValues: ["work", "home", "other"], description: "A label indicating the attribute's function, e.g. 'work' or 'home'."}),
new Types.Attribute("boolean", "primary", {description: "A Boolean value indicating the 'primary' or preferred attribute value for this attribute, e.g. the preferred mailing address or primary email address. The primary attribute value 'true' MUST appear no more than once."})
]),
new Types.Attribute("complex", "groups", {direction: "out", mutable: false, multiValued: true, uniqueness: false, description: "A list of groups to which the user belongs, either through direct membership, through nested groups, or dynamically calculated."}, [
new Types.Attribute("string", "value", {direction: "out", mutable: false, description: "The identifier of the User's group."}),
new Types.Attribute("reference", "$ref", {direction: "out", mutable: false, referenceTypes: ["User", "Group"], description: "The URI of the corresponding 'Group' resource to which the user belongs."}),
new Types.Attribute("string", "display", {direction: "out", mutable: false, description: "A human-readable name, primarily used for display purposes. READ-ONLY."}),
new Types.Attribute("string", "type", {direction: "out", mutable: false, canonicalValues: ["direct", "indirect"], description: "A label indicating the attribute's function, e.g. 'direct' or 'indirect'."})
]),
new Types.Attribute("complex", "entitlements", {multiValued: true, uniqueness: false, description: "A list of entitlements for the User that represent a thing the User has."}, [
new Types.Attribute("string", "value", {description: "The value of an entitlement."}),
new Types.Attribute("string", "display", {description: "A human-readable name, primarily used for display purposes. READ-ONLY."}),
new Types.Attribute("string", "type", {description: "A label indicating the attribute's function."}),
new Types.Attribute("boolean", "primary", {description: "A Boolean value indicating the 'primary' or preferred attribute value for this attribute. The primary attribute value 'true' MUST appear no more than once."})
]),
new Types.Attribute("complex", "roles", {multiValued: true, uniqueness: false, description: "A list of roles for the User that collectively represent who the User is, e.g. 'Student', 'Faculty'."}, [
new Types.Attribute("string", "value", {description: "The value of a role."}),
new Types.Attribute("string", "display", {description: "A human-readable name, primarily used for display purposes. READ-ONLY."}),
new Types.Attribute("string", "type", {canonicalValues: [], description: "A label indicating the attribute's function."}),
new Types.Attribute("boolean", "primary", {description: "A Boolean value indicating the 'primary' or preferred attribute value for this attribute. The primary attribute value 'true' MUST appear no more than once."})
]),
new Types.Attribute("complex", "x509Certificates", {multiValued: true, uniqueness: false, description: "A list of certificates issued to the User."}, [
new Types.Attribute("binary", "value", {description: "The value of an X.509 certificate.", caseExact: true}),
new Types.Attribute("string", "display", {description: "A human-readable name, primarily used for display purposes. READ-ONLY."}),
new Types.Attribute("string", "type", {canonicalValues: [], description: "A label indicating the attribute's function."}),
new Types.Attribute("boolean", "primary", {description: "A Boolean value indicating the 'primary' or preferred attribute value for this attribute. The primary attribute value 'true' MUST appear no more than once."})
])
]);
/**
* Instantiates a new user that conforms to the SCIM User schema definition
* @extends SCIMMY.Types.Schema
* @param {Object} resource - the source data to feed through the schema definition
* @param {String} [direction="both"] - whether the resource is inbound from a request or outbound for a response
* @param {String} [basepath] - the base path for resolution of a resource's location
* @param {SCIMMY.Types.Filter} [filters] - attribute filters to apply to the coerced value
* @property {String} userName - unique identifier for the User, typically used by the user to directly authenticate to the service provider. Each User MUST include a non-empty userName value. This identifier MUST be unique across the service provider's entire set of Users. REQUIRED
* @property {Object} [name] - the components of the user's real name. Providers MAY return just the full name as a single string in the formatted sub-attribute, or they MAY return just the individual component attributes using the other sub-attributes, or they MAY return both. If both variants are returned, they SHOULD be describing the same name, with the formatted name indicating how the component attributes should be combined
* @property {String} [name.formatted] - the full name, including all middle names, titles, and suffixes as appropriate, formatted for display (e.g. 'Ms. Barbara J Jensen, III')
* @property {String} [name.familyName] - the family name of the User, or last name in most Western languages (e.g. 'Jensen' given the full name 'Ms. Barbara J Jensen, III')
* @property {String} [name.givenName] - the given name of the User, or first name in most Western languages (e.g. 'Barbara' given the full name 'Ms. Barbara J Jensen, III')
* @property {String} [name.middleName] - the middle name(s) of the User (e.g. 'Jane' given the full name 'Ms. Barbara J Jensen, III')
* @property {String} [name.honorificPrefix] - the honorific prefix(es) of the User, or title in most Western languages (e.g. 'Ms.' given the full name 'Ms. Barbara J Jensen, III')
* @property {String} [name.honorificSuffix] - the honorific suffix(es) of the User, or suffix in most Western languages (e.g. 'III' given the full name 'Ms. Barbara J Jensen, III')
* @property {String} [displayName] - the name of the User, suitable for display to end-users. The name SHOULD be the full name of the User being described, if known
* @property {String} [nickName] - the casual way to address the user in real life, e.g. 'Bob' or 'Bobby' instead of 'Robert'. This attribute SHOULD NOT be used to represent a User's username (e.g. 'bjensen' or 'mpepperidge')
* @property {String} [profileUrl] - a fully qualified URL pointing to a page representing the User's online profile
* @property {String} [title] - the user's title, such as 'Vice President'
* @property {String} [userType] - used to identify the relationship between the organization and the user. Typical values used might be 'Contractor', 'Employee', 'Intern', 'Temp', 'External', and 'Unknown', but any value may be used
* @property {String} [preferredLanguage] - indicates the User's preferred written or spoken language. Generally used for selecting a localized user interface; e.g. 'en_US' specifies the language English and country US
* @property {String} [locale] - used to indicate the User's default location for purposes of localizing items such as currency, date time format, or numerical representations
* @property {String} [timezone] - the User's time zone in the 'Olson' time zone database format, e.g. 'America/Los_Angeles'
* @property {Boolean} [active] - a Boolean value indicating the User's administrative status
* @property {String} [password] - the User's cleartext password. This attribute is intended to be used as a means to specify an initial password when creating a new User or to reset an existing User's password
* @property {Object[]} [emails] - email addresses for the user. The value SHOULD be canonicalized by the service provider, e.g. 'bjensen@example.com' instead of 'bjensen@EXAMPLE.COM'. Canonical type values of 'work', 'home', and 'other'
* @property {String} emails[].value - email addresses for the user. The value SHOULD be canonicalized by the service provider, e.g. 'bjensen@example.com' instead of 'bjensen@EXAMPLE.COM'. Canonical type values of 'work', 'home', and 'other'
* @property {String} [emails[].display] - a human-readable name, primarily used for display purposes
* @property {String} [emails[].type] - a label indicating the attribute's function, e.g. 'work' or 'home'
* @property {Boolean} [emails[].primary] - a Boolean value indicating the 'primary' or preferred attribute value for this attribute, e.g. the preferred mailing address or primary email address. The primary attribute value 'true' MUST appear no more than once
* @property {Object[]} [phoneNumbers] - phone numbers for the User. The value SHOULD be canonicalized by the service provider according to the format specified in RFC 3966, e.g. 'tel:+1-201-555-0123'. Canonical type values of 'work', 'home', 'mobile', 'fax', 'pager', and 'other'
* @property {String} phoneNumbers[].value - phone number of the User
* @property {String} [phoneNumbers[].display] - a human-readable name, primarily used for display purposes
* @property {String} [phoneNumbers[].type] - a label indicating the attribute's function, e.g. 'work', 'home', 'mobile'
* @property {Boolean} [phoneNumbers[].primary] - a Boolean value indicating the 'primary' or preferred attribute value for this attribute, e.g. the preferred phone number or primary phone number. The primary attribute value 'true' MUST appear no more than once
* @property {Object[]} [ims] - instant messaging addresses for the User
* @property {String} ims[].value - instant messaging address for the User
* @property {String} [ims[].display] - a human-readable name, primarily used for display purposes
* @property {String} [ims[].type] - a label indicating the attribute's function, e.g. 'aim', 'gtalk', 'xmpp'
* @property {Boolean} [ims[].primary] - a Boolean value indicating the 'primary' or preferred attribute value for this attribute, e.g. the preferred messenger or primary messenger. The primary attribute value 'true' MUST appear no more than once
* @property {Object[]} [photos] - uRLs of photos of the User
* @property {String} photos[].value - uRL of a photo of the User
* @property {String} [photos[].display] - a human-readable name, primarily used for display purposes
* @property {String} [photos[].type] - a label indicating the attribute's function, i.e., 'photo' or 'thumbnail'
* @property {Boolean} [photos[].primary] - a Boolean value indicating the 'primary' or preferred attribute value for this attribute, e.g. the preferred photo or thumbnail. The primary attribute value 'true' MUST appear no more than once
* @property {Object[]} [addresses] - a physical mailing address for this User. Canonical type values of 'work', 'home', and 'other'
* @property {String} [addresses[].formatted] - the full mailing address, formatted for display or use with a mailing label. This attribute MAY contain newlines
* @property {String} [addresses[].streetAddress] - the full street address component, which may include house number, street name, P.O. box, and multi-line extended street address information
* @property {String} [addresses[].locality] - the city or locality component
* @property {String} [addresses[].region] - the state or region component
* @property {String} [addresses[].postalCode] - the zip code or postal code component
* @property {String} [addresses[].country] - the country name component
* @property {String} [addresses[].type] - a label indicating the attribute's function, e.g. 'work' or 'home'
* @property {Boolean} [addresses[].primary] - a Boolean value indicating the 'primary' or preferred attribute value for this attribute, e.g. the preferred mailing address or primary email address
* @property {Object[]} [groups] - a list of groups to which the user belongs, either through direct membership, through nested groups, or dynamically calculated
* @property {String} groups[].value - the identifier of the User's group
* @property {String} [groups[].$ref] - the URI of the corresponding 'Group' resource to which the user belongs
* @property {String} [groups[].display] - a human-readable name, primarily used for display purposes
* @property {String} [groups[].type] - a label indicating the attribute's function, e.g. 'direct' or 'indirect'
* @property {Object[]} [entitlements] - a list of entitlements for the User that represent a thing the User has
* @property {String} entitlements[].value - the value of an entitlement
* @property {String} [entitlements[].display] - a human-readable name, primarily used for display purposes
* @property {String} [entitlements[].type] - a label indicating the attribute's function
* @property {Boolean} [entitlements[].primary] - a Boolean value indicating the 'primary' or preferred attribute value for this attribute. The primary attribute value 'true' MUST appear no more than once
* @property {Object[]} [roles] - a list of roles for the User that collectively represent who the User is, e.g. 'Student', 'Faculty'
* @property {String} roles[].value - the value of a role
* @property {String} [roles[].display] - a human-readable name, primarily used for display purposes
* @property {String} [roles[].type] - a label indicating the attribute's function
* @property {Boolean} [roles[].primary] - a Boolean value indicating the 'primary' or preferred attribute value for this attribute. The primary attribute value 'true' MUST appear no more than once
* @property {Object[]} [x509Certificates] - a list of certificates issued to the User
* @property {String} x509Certificates[].value - the value of an X.509 certificate
* @property {String} [x509Certificates[].display] - a human-readable name, primarily used for display purposes
* @property {String} [x509Certificates[].type] - a label indicating the attribute's function
* @property {Boolean} [x509Certificates[].primary] - a Boolean value indicating the 'primary' or preferred attribute value for this attribute. The primary attribute value 'true' MUST appear no more than once
*/
constructor(resource, direction = "both", basepath, filters) {
super(resource, direction);
Object.assign(this, User.#definition.coerce(resource, direction, basepath, filters));
}
}
/**
* SCIM Group Schema
* @alias SCIMMY.Schemas.Group
* @summary
* * Ensures a Group instance conforms to the Group schema set out in [RFC7643§4.2](https://datatracker.ietf.org/doc/html/rfc7643#section-4.2).
*/
class Group extends Types.Schema {
/** @type {"urn:ietf:params:scim:schemas:core:2.0:Group"} */
static get id() {
return Group.#definition.id;
}
/** @implements {SCIMMY.Types.Schema.definition} */
static get definition() {
return Group.#definition;
}
/** @private */
static #definition = new Types.SchemaDefinition("Group", "urn:ietf:params:scim:schemas:core:2.0:Group", "Group", [
new Types.Attribute("string", "displayName", {required: true, description: "A human-readable name for the Group. REQUIRED."}),
new Types.Attribute("complex", "members", {multiValued: true, uniqueness: false, description: "A list of members of the Group."}, [
new Types.Attribute("string", "value", {mutable: "immutable", description: "Identifier of the member of this Group."}),
new Types.Attribute("string", "display", {mutable: "immutable", description: "Human-readable name of the member of this Group."}),
new Types.Attribute("reference", "$ref", {mutable: "immutable", referenceTypes: ["User", "Group"], description: "The URI corresponding to a SCIM resource that is a member of this Group."}),
new Types.Attribute("string", "type", {mutable: "immutable", canonicalValues: ["User", "Group"], description: "A label indicating the type of resource, e.g., 'User' or 'Group'."})
])
]);
/**
* Instantiates a new group that conforms to the SCIM Group schema definition
* @extends SCIMMY.Types.Schema
* @param {Object} resource - the source data to feed through the schema definition
* @param {String} [direction="both"] - whether the resource is inbound from a request or outbound for a response
* @param {String} [basepath] - the base path for resolution of a resource's location
* @param {SCIMMY.Types.Filter} [filters] - attribute filters to apply to the coerced value
* @property {String} displayName - a human-readable name for the Group
* @property {Object[]} [members] - a list of members of the Group
* @property {String} members[].value - identifier of the member of this Group
* @property {String} [members[].display] - human-readable name of the member of this Group
* @property {String} [members[].$ref] - the URI corresponding to a SCIM resource that is a member of this Group
* @property {String} [members[].type] - a label indicating the type of resource, e.g., 'User' or 'Group'
*/
constructor(resource, direction = "both", basepath, filters) {
super(resource, direction);
Object.assign(this, Group.#definition.coerce(resource, direction, basepath, filters));
}
}
/**
* SCIM EnterpriseUser Schema
* @alias SCIMMY.Schemas.EnterpriseUser
* @summary
* * Ensures an EnterpriseUser instance conforms to the EnterpriseUser schema extension set out in [RFC7643§4.3](https://datatracker.ietf.org/doc/html/rfc7643#section-4.3).
* * Can be used directly, but is typically used to extend the `SCIMMY.Schemas.User` schema definition.
*/
class EnterpriseUser extends Types.Schema {
/** @type {"urn:ietf:params:scim:schemas:extension:enterprise:2.0:User"} */
static get id() {
return EnterpriseUser.#definition.id;
}
/** @implements {SCIMMY.Types.Schema.definition} */
static get definition() {
return EnterpriseUser.#definition;
}
/** @private */
static #definition = new Types.SchemaDefinition("EnterpriseUser", "urn:ietf:params:scim:schemas:extension:enterprise:2.0:User", "Enterprise User", [
new Types.Attribute("string", "employeeNumber", {description: "Numeric or alphanumeric identifier assigned to a person, typically based on order of hire or association with an organization."}),
new Types.Attribute("string", "costCenter", {description: "Identifies the name of a cost center."}),
new Types.Attribute("string", "organization", {description: "Identifies the name of an organization."}),
new Types.Attribute("string", "division", {description: "Identifies the name of a division."}),
new Types.Attribute("string", "department", {description: "Identifies the name of a department."}),
new Types.Attribute("complex", "manager", {uniqueness: false, description: "The User's manager. A complex type that optionally allows service providers to represent organizational hierarchy by referencing the 'id' attribute of another User."}, [
new Types.Attribute("string", "value", {description: "The id of the SCIM resource representing the User's manager."}),
new Types.Attribute("reference", "$ref", {referenceTypes: ["User"], description: "The URI of the SCIM resource representing the User's manager."}),
new Types.Attribute("string", "displayName", {mutable: false, description: "The displayName of the User's manager."})
])
]);
/**
* Instantiates a new enterprise user that conforms to the SCIM EnterpriseUser schema definition
* @extends SCIMMY.Types.Schema
* @param {Object} resource - the source data to feed through the schema definition
* @param {String} [direction="both"] - whether the resource is inbound from a request or outbound for a response
* @param {String} [basepath] - the base path for resolution of a resource's location
* @param {SCIMMY.Types.Filter} [filters] - attribute filters to apply to the coerced value
* @property {String} [employeeNumber] - numeric or alphanumeric identifier assigned to a person, typically based on order of hire or association with an organization
* @property {String} [costCenter] - identifies the name of a cost center
* @property {String} [organization] - identifies the name of an organization
* @property {String} [division] - identifies the name of a division
* @property {String} [department] - identifies the name of a department
* @property {Object} [manager] - the User's manager
* @property {String} manager.value - the id of the SCIM resource representing the User's manager
* @property {String} [manager.$ref] - the URI of the SCIM resource representing the User's manager
* @property {String} [manager.displayName] - the displayName of the User's manager
*/
constructor(resource, direction = "both", basepath, filters) {
super(resource, direction);
Object.assign(this, EnterpriseUser.#definition.coerce(resource, direction, basepath, filters));
}
}
/**
* SCIM ResourceType Schema
* @alias SCIMMY.Schemas.ResourceType
* @summary
* * Ensures a ResourceType instance conforms to the ResourceType schema set out in [RFC7643§6](https://datatracker.ietf.org/doc/html/rfc7643#section-6).
*/
class ResourceType extends Types.Schema {
/** @type {"urn:ietf:params:scim:schemas:core:2.0:ResourceType"} */
static get id() {
return ResourceType.#definition.id;
}
/** @implements {SCIMMY.Types.Schema.definition} */
static get definition() {
return ResourceType.#definition;
}
/** @private */
static #definition = (() => {
let definition = new Types.SchemaDefinition("ResourceType", "urn:ietf:params:scim:schemas:core:2.0:ResourceType", "Specifies the schema that describes a SCIM resource type", [
new Types.Attribute("string", "name", {direction: "out", required: true, mutable: false, description: "The resource type name. When applicable, service providers MUST specify the name, e.g., 'User'."}),
new Types.Attribute("string", "description", {direction: "out", mutable: false, description: "The resource type's human-readable description. When applicable, service providers MUST specify the description."}),
new Types.Attribute("reference", "endpoint", {direction: "out", required: true, mutable: false, referenceTypes: ["uri"], description: "The resource type's HTTP-addressable endpoint relative to the Base URL, e.g., '/Users'."}),
new Types.Attribute("reference", "schema", {direction: "out", required: true, mutable: false, caseExact: true, referenceTypes: ["uri"], description: "The resource type's primary/base schema URI."}),
new Types.Attribute("complex", "schemaExtensions", {direction: "out", mutable: false, multiValued: true, uniqueness: false, description: "A list of URIs of the resource type's schema extensions."}, [
new Types.Attribute("reference", "schema", {direction: "out", required: true, mutable: false, caseExact: true, referenceTypes: ["uri"], description: "The URI of a schema extension."}),
new Types.Attribute("boolean", "required", {direction: "out", required: true, mutable: false, description: "A Boolean value that specifies whether or not the schema extension is required for the resource type. If true, a resource of this type MUST include this schema extension and also include any attributes declared as required in this schema extension. If false, a resource of this type MAY omit this schema extension."})
])
]);
// Make the ID attribute visible!
Object.assign(definition.attribute("id").config, {
shadow: false, required: false, returned: true, caseExact: false, uniqueness: "none",
description: "The resource type's server unique id. May be the same as the 'name' attribute."
});
return definition;
})();
/**
* Instantiates a new resource type that conforms to the SCIM ResourceType schema definition
* @extends SCIMMY.Types.Schema
* @param {Object} resource - the source data to feed through the schema definition
* @param {String} [basepath] - the base path for resolution of a resource's location
* @property {String} name - the resource type name. When applicable, service providers MUST specify the name, e.g., 'User'
* @property {String} [description] - the resource type's human-readable description. When applicable, service providers MUST specify the description
* @property {String} endpoint - the resource type's HTTP-addressable endpoint relative to the Base URL, e.g., '/Users'
* @property {String} schema - the resource type's primary/base schema URI
* @property {Object[]} [schemaExtensions] - a list of URIs of the resource type's schema extensions
* @property {String} schemaExtensions[].schema - the URI of a schema extension
* @property {Boolean} schemaExtensions[].required - a Boolean value that specifies whether the schema extension is required for the resource type
*/
constructor(resource, basepath) {
super(resource, "out");
Object.assign(this, ResourceType.#definition.coerce(resource, "out", basepath));
}
}
/**
* SCIM Service Provider Configuration Schema
* @alias SCIMMY.Schemas.ServiceProviderConfig
* @summary
* * Ensures a ServiceProviderConfig instance conforms to the Service Provider Configuration schema set out in [RFC7643§5](https://datatracker.ietf.org/doc/html/rfc7643#section-5).
*/
class ServiceProviderConfig extends Types.Schema {
/** @type {"urn:ietf:params:scim:schemas:core:2.0:ServiceProviderConfig"} */
static get id() {
return ServiceProviderConfig.#definition.id;
}
/** @implements {SCIMMY.Types.Schema.definition} */
static get definition() {
return ServiceProviderConfig.#definition;
}
/** @private */
static #definition = new Types.SchemaDefinition(
"ServiceProviderConfig", "urn:ietf:params:scim:schemas:core:2.0:ServiceProviderConfig", "Schema for representing the service provider's configuration", [
new Types.Attribute("reference", "documentationUri", {mutable: false, referenceTypes: ["external"], description: "An HTTP-addressable URL pointing to the service provider's human-consumable help documentation."}),
new Types.Attribute("complex", "patch", {required: true, mutable: false, uniqueness: false, description: "A complex type that specifies PATCH configuration options."}, [
new Types.Attribute("boolean", "supported", {required: true, mutable: false, description: "A Boolean value specifying whether or not the operation is supported."})
]),
new Types.Attribute("complex", "bulk", {required: true, mutable: false, uniqueness: false, description: "A complex type that specifies bulk configuration options."}, [
new Types.Attribute("boolean", "supported", {required: true, mutable: false, description: "A Boolean value specifying whether or not the operation is supported."}),
new Types.Attribute("integer", "maxOperations", {required: true, mutable: false, description: "An integer value specifying the maximum number of operations."}),
new Types.Attribute("integer", "maxPayloadSize", {required: true, mutable: false, description: "An integer value specifying the maximum payload size in bytes."})
]),
new Types.Attribute("complex", "filter", {required: true, mutable: false, uniqueness: false, description: "A complex type that specifies FILTER options."}, [
new Types.Attribute("boolean", "supported", {required: true, mutable: false, description: "A Boolean value specifying whether or not the operation is supported."}),
new Types.Attribute("integer", "maxResults", {required: true, mutable: false, description: "An integer value specifying the maximum number of resources returned in a response."})
]),
new Types.Attribute("complex", "changePassword", {required: true, mutable: false, uniqueness: false, description: "A complex type that specifies configuration options related to changing a password."}, [
new Types.Attribute("boolean", "supported", {required: true, mutable: false, description: "A Boolean value specifying whether or not the operation is supported."})
]),
new Types.Attribute("complex", "sort", {required: true, mutable: false, uniqueness: false, description: "A complex type that specifies sort result options."}, [
new Types.Attribute("boolean", "supported", {required: true, mutable: false, description: "A Boolean value specifying whether or not the operation is supported."})
]),
new Types.Attribute("complex", "etag", {required: true, mutable: false, uniqueness: false, description: "A complex type that specifies ETag configuration options."}, [
new Types.Attribute("boolean", "supported", {required: true, mutable: false, description: "A Boolean value specifying whether or not the operation is supported."})
]),
new Types.Attribute("complex", "authenticationSchemes", {required: true, mutable: false, multiValued: true, uniqueness: false, description: "A complex type that specifies supported authentication scheme properties."}, [
new Types.Attribute("string", "type", {required: true, mutable: false, canonicalValues: ["oauth", "oauth2", "oauthbearertoken", "httpbasic", "httpdigest"], description: "The authentication scheme."}),
new Types.Attribute("string", "name", {required: true, mutable: false, description: "The common authentication scheme name, e.g., HTTP Basic."}),
new Types.Attribute("string", "description", {required: true, mutable: false, description: "A description of the authentication scheme."}),
new Types.Attribute("reference", "specUri", {mutable: false, referenceTypes: ["external"], description: "An HTTP-addressable URL pointing to the authentication scheme's specification."}),
new Types.Attribute("reference", "documentationUri", {mutable: false, referenceTypes: ["external"], description: "An HTTP-addressable URL pointing to the authentication scheme's usage documentation."})
])
]
// Remove ID attribute
).truncate("id");
/**
* Instantiates a new service provider configuration that conforms to the SCIM ServiceProviderConfig schema definition
* @extends SCIMMY.Types.Schema
* @param {Object} resource - the source data to feed through the schema definition
* @param {String} [basepath] - the base path for resolution of a resource's location
* @property {never} id
* @property {String} documentationUri - an HTTP-addressable URL pointing to the service provider's human-consumable help documentation
* @property {Object} patch - a complex type that specifies PATCH configuration options
* @property {Boolean} patch.supported - a Boolean value specifying whether the operation is supported
* @property {Object} bulk - a complex type that specifies bulk configuration options
* @property {Boolean} bulk.supported - a Boolean value specifying whether the operation is supported
* @property {Number} bulk.maxOperations - an integer value specifying the maximum number of operations
* @property {Number} bulk.maxPayloadSize - an integer value specifying the maximum payload size in bytes
* @property {Object} filter - a complex type that specifies FILTER options
* @property {Boolean} filter.supported - a Boolean value specifying whether the operation is supported
* @property {Number} filter.maxResults - an integer value specifying the maximum number of resources returned in a response
* @property {Object} changePassword - a complex type that specifies configuration options related to changing a password
* @property {Boolean} changePassword.supported - a Boolean value specifying whether the operation is supported
* @property {Object} sort - a complex type that specifies sort result options
* @property {Boolean} sort.supported - a Boolean value specifying whether the operation is supported
* @property {Object} etag - a complex type that specifies ETag configuration options
* @property {Boolean} etag.supported - a Boolean value specifying whether the operation is supported
* @property {Object[]} authenticationSchemes - a complex type that specifies supported authentication scheme properties
* @property {String} authenticationSchemes[].type - the authentication scheme
* @property {String} authenticationSchemes[].name - the common authentication scheme name, e.g., HTTP Basic
* @property {String} authenticationSchemes[].description - a description of the authentication scheme
* @property {String} [authenticationSchemes[].specUri] - an HTTP-addressable URL pointing to the authentication scheme's specification
* @property {String} [authenticationSchemes[].documentationUri] - an HTTP-addressable URL pointing to the authentication scheme's usage documentation
*/
constructor(resource, basepath) {
super(resource, "out");
Object.assign(this, ServiceProviderConfig.#definition.coerce(resource, "out", basepath));
}
}
/**
* SCIMMY Schemas Container Class
* @module scimmy/schemas
* @namespace SCIMMY.Schemas
* @description
* SCIMMY provides a singleton class, `SCIMMY.Schemas`, that is used to declare schema definitions implemented by a SCIM Service Provider.
* It also provides access to supplied implementations of core resource type schema definitions.
* It is also used to retrieve a service provider's declared schema definitions to be sent via the Schemas HTTP endpoint.
*
* > **Note:**
* > The `SCIMMY.Schemas` class is a singleton, which means that declared schema definitions
* > will remain the same, regardless of where the class is accessed from within your code.
*
* ## Declaring Definitions
* Schema definitions are typically declared automatically at the same time as resource type instances are declared in `{@link SCIMMY.Resources}`.
* If necessary, schema definitions can be declared manually with the `{@link SCIMMY.Schemas.declare}` method.
* Nested definitions that extend declared schema definitions are also automatically declared to the `SCIMMY.Schemas` class.
* ```
* // Manually declare the EnterpriseUser schema definition
* SCIMMY.Schemas.declare(SCIMMY.Schemas.EnterpriseUser.definition);
* ```
*
* Once declared, schema definitions are made available to the `{@link SCIMMY.Resources.Schema}`
* resource type, which handles formatting them for transmission/consumption according to the Schema Definition schema
* set out in [RFC7643§7](https://datatracker.ietf.org/doc/html/rfc7643#section-7).
*
* Each schema definition must be declared with a unique name, and each name can only be declared once.
* Attempting to declare a new schema definition with a name that has already been declared will throw a TypeError with the
* message `"Schema definition '<name>' already declared with id '<id>'"`, where `<name>` and `<id>` are the name and id,
* respectively, of the existing schema definition.
*
* Similarly, each schema definition can only be declared under one name.
* Attempting to declare an existing schema definition under a new name will throw a TypeError with the message
* `"Schema definition '<id>' already declared with name '<name>'"`, where `<id>` and `<name>` are the id and name,
* respectively, of the existing schema definition.
*
* ```
* // Declaring a schema definition under a different name
* let definition = new SCIMMY.Types.SchemaDefinition("User", "urn:ietf:params:scim:schemas:MyOrg:CustomUser", "MyOrg Custom User");
* SCIMMY.Schemas.declare(definition, "CustomUser");
* ```
*
* ## Modifying Definitions
* Not all SCIM clients and service providers support every attribute defined in the SCIM core schemas,
* and conversely, some custom attributes may not be defined in the core schemas. In such situations,
* it is possible to modify schema definitions using their `{@link SCIMMY.Types.SchemaDefinition#extend extend}`
* and `{@link SCIMMY.Types.SchemaDefinition#truncate truncate}` instance methods.
*
* > **Note:**
* > Like the `SCIMMY.Schemas` class, the schema implementations included in this class are all singletons,
* > and any changes to their schema definitions will persist across any location they are accessed.
*
* ```
* // Remove unsupported "name" sub-attributes from the User schema definition
* SCIMMY.Schemas.User.definition.truncate(["name.middleName", "name.honorificPrefix", "name.honorificSuffix"]);
*
* // Remove unsupported "ims" attribute and its sub-attributes from the User schema
* SCIMMY.Schemas.User.definition.truncate(["ims"]);
*
* // Add custom "mail" attribute to the Group schema definition
* SCIMMY.Schemas.Group.definition.extend([new SCIMMY.Types.Attribute("string", "mail", {required: true})]);
*
* // Extend the User schema definition with the EnterpriseUser schema definition, and make it required
* SCIMMY.Schemas.User.definition.extend(SCIMMY.Schemas.EnterpriseUser.definition, true);
*
* // Remove the EnterpriseUser extension schema definition from the User schema definition
* SCIMMY.Schemas.User.definition.truncate(SCIMMY.Schemas.EnterpriseUser.definition);
* ```
*/
class Schemas {
/**
* Store declared schema definitions for later retrieval
* @private
*/
static #definitions = new Map();
/**
* Map declared schema IDs to declared schema names
* @private
*/
static #idsToNames = new Map();
// Expose built-in schemas without "declaring" them
static User = User;
static Group = Group;
static EnterpriseUser = EnterpriseUser;
static ResourceType = ResourceType;
static ServiceProviderConfig = ServiceProviderConfig;
/**
* Register a SchemaDefinition implementation for exposure via Schemas HTTP endpoint
* @param {SCIMMY.Types.SchemaDefinition} definition - the schema definition to register
* @param {String} [name] - the name of the definition being declared, if different from definition's name property
* @returns {SCIMMY.Schemas} the Schemas class for chaining
*/
static declare(definition, name) {
// Make sure the registering schema definition is valid
if (!definition || !(definition instanceof Types.SchemaDefinition))
throw new TypeError("Registering schema definition must be of type 'SchemaDefinition'");
// Source name from schema definition if config is an object
name = (typeof name === "string" ? name : (definition?.name ?? "")).replace(/\s+/g, "");
// Prevent registering a schema definition under a name that already exists
if (Schemas.#definitions.has(name) && Schemas.#definitions.get(name) !== definition)
throw new TypeError(`Schema definition '${name}' already declared with id '${Schemas.#definitions.get(name).id}'`);
// Prevent registering an existing schema definition under a different name
else if (Schemas.declared(definition) && Schemas.#definitions.get(name) !== definition)
throw new TypeError(`Schema definition '${definition.id}' already d