GithubHelp home page GithubHelp logo

scimmyjs / scimmy Goto Github PK

View Code? Open in Web Editor NEW
34.0 3.0 7.0 1.12 MB

SCIM m(ade eas)y - SCIM 2.0 library for NodeJS

Home Page: https://scimmyjs.github.io

License: MIT License

JavaScript 100.00%
nodejs scim scim-2 scim2 rfc7643 rfc7644 rfc-7643 rfc-7644

scimmy's Introduction

SCIMMY

SCIMMY - SCIM m(ade eas)y

SCIM 2.0 (System for Cross-domain Identity Management) is a set of standards (RFC7643 and RFC7644) designed to simplify resource provisioning and identity management in cloud-based applications and services. SCIMMY aims to make it easier to rapidly implement code that sends and receives data conforming to these standards. It does this by providing a set of tools that can be used to parse incoming, and format outgoing data according to these standards.

Requirements

Installation and Usage

Through NPM:

$ npm install scimmy

In your code:

import SCIMMY from "scimmy";

// Basic usage with provided resource type implementations
SCIMMY.Resources.declare(SCIMMY.Resources.User)
    .ingress((resource, data) => {/* Your handler for creating or modifying user resources */})
    .egress((resource) => {/* Your handler for retrieving user resources */})
    .degress((resource) => {/* Your handler for deleting user resources */});

// Advanced usage with custom resource type implementations
SCIMMY.Resources.declare(class MyResourceType extends SCIMMY.Types.Resource {
    read() {/* Your handler for retrieving resources */}
    write(data) {/* Your handler for creating or modifying resources */}
    dispose() {/* Your handler for deleting resources */}
    /* ...the rest of your resource type implementation */
});
Questions
  • Why use SCIMMY instead of some other SCIM-related package?

    • Many of the SCIM-related packages available seem to target specific cloud services like GitHub, act as API bridges, or only implement certain parts of the spec (like SCIM-PATCH or scim2-parse-filter). That's all well and good, but SCIMMY aims to implement the entire spec and integrate the protocol directly into your code.
    • As retrieval/consumption of resources from your data model is left up to you to implement, you should be able to use other SCIM-related packages in conjunction with SCIMMY!
  • Will this work with cloud service X/Y/Z?

    • Hopefully, but if not, feel free to open an issue with details of the cloud service you are integrating with. SCIMMY has been tested against Microsoft Entra ID (formerly Azure AD), which didn't appear to have any issues.
  • What about the actual SCIM protocol HTTP endpoints?

    • That's up to you, as we can't be sure exactly how you'd like to integrate SCIM in your code, however we have provided a package with express middleware which uses SCIMMY to implement the endpoints, called SCIMMY Routers

API

SCIMMY exports a singleton class which provides the following interfaces:

  • SCIMMY.Config
    • SCIM Service Provider Configuration container store.
  • SCIMMY.Types
    • SCIMMY classes for implementing schemas and resource types.
  • SCIMMY.Messages
    • Implementations of non-resource SCIM "message" schemas, such as ListResponse and PatchOp.
  • SCIMMY.Schemas
    • Container store for declaring and retrieving schemas implemented by a service provider.
    • Also provides access to bundled schema implementations of SCIM Core Resource Schemas.
  • SCIMMY.Resources
    • Container store for declaring and retrieving resource types implemented by a service provider.
    • Also provides access to bundled resource type implementations of SCIM Core Resource Types.

For more details on how to use SCIMMY, visit the documentation.

scimmy's People

Contributors

brianpeiris avatar dependabot[bot] avatar sleelin avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

scimmy's Issues

EnterpriseUser manager value should not be required

For EnterpriseUser, Scimmy defines the "value" attribute under "manager" as "required"1. The current RFC is confusing on this point, since it uses both "RECOMMENDED", "REQUIRED", and "required: false" in different sections of the RFC23.
However, it seems the intent was to mark it as "recommended", not "required", according to a message about the errata in the IETF mailing list4. Unless there are objections from the Scimmy maintainers or users, I'd suggest following the intent of the spec authors and loosen the schema here.

I ran into this in an edge-case scenario when integrating with Okta, where Okta could send a "manager" structure with a "displayName" attribute, but without a "value" attribute.

Footnotes

  1. https://github.com/scimmyjs/scimmy/blob/v1.2.1/src/lib/schemas/enterpriseuser.js#L24

  2. https://datatracker.ietf.org/doc/html/rfc7643#page-27

  3. https://datatracker.ietf.org/doc/html/rfc7643#page-72

  4. https://mailarchive.ietf.org/arch/msg/scim/nKiaxUkWW9-L5KiUKLBOoA9GuPc/

Cannot Read Properties of Undefined (Reading 'Resources') in NestJS Integration

Description:

I'm encountering an issue while integrating SCIMMY with a NestJS project. After following the installation and initialization steps, I receive the following error when starting the application:

npm run start

> scim@0.0.1 start
> nest start

[Nest] 216727  - 2024-06-10, 11:44:21 a.m.     LOG [NestFactory] Starting Nest application...
[Nest] 216727  - 2024-06-10, 11:44:21 a.m.   ERROR [ExceptionHandler] Cannot read properties of undefined (reading 'Resources')

TypeError: Cannot read properties of undefined (reading 'Resources')
    at new ScimService (git/scim/src/scim/scim.service.ts:7:34)
    at Injector.instantiateClass (git/scim/node_modules/@nestjs/core/injector/injector.js:365:19)
    at callback (git/scim/node_modules/@nestjs/core/injector/injector.js:65:45)
    at Injector.resolveConstructorParams (git/scim/node_modules/@nestjs/core/injector/injector.js:144:24)
    at Injector.loadInstance (git/scim/node_modules/@nestjs/core/injector/injector.js:70:13)
    at Injector.loadProvider (git/scim/node_modules/@nestjs/core/injector/injector.js:97:9)
    at git/scim/node_modules/@nestjs/core/injector/instance-loader.js:56:13
    at async Promise.all (index 3)
    at InstanceLoader.createInstancesOfProviders (git/scim/node_modules/@nestjs/core/injector/instance-loader.js:55:9)
    at git/scim/node_modules/@nestjs/core/injector/instance-loader.js:40:13

Steps to Reproduce:

  1. Install SCIMMY via npm install scimmy.
  2. Create a service in NestJS to initialize SCIMMY.
  3. Import and use SCIMMY.Resources.
  4. Start the application.

Code Example:

import { Injectable } from '@nestjs/common';
import SCIMMY from 'scimmy';

@Injectable()
export class ScimService {
  constructor() {
    const resources = new SCIMMY.Resources.User();

    SCIMMY.Resources.declare(resources)
      .ingress((resource, data) => this.handleUserCreation(resource, data))
      .egress((resource) => this.handleUserRetrieval(resource))
      .degress((resource) => this.handleUserDeletion(resource));
  }

  handleUserCreation(resource, data) {
    return { message: 'User created', resource, data };
  }

  handleUserRetrieval(resource) {
    return { message: 'User retrieved', resource };
  }

  handleUserDeletion(resource) {
    return { message: 'User deleted', resource };
  }
}

Environment:

Node.js version: v22.2.0
NestJS version: 10.0.0
SCIMMY version: 1.2.1
OS: Ubuntu

Additional Context:

The project compiles without issues, but fails at runtime with the mentioned error.

Expected Behavior:
SCIMMY should initialize correctly and allow the application to run without errors.

Sample Source Code:

https://github.com/omidraha/nestjs-example/tree/main/src/scim

PatchOp add/replace does not work for attributes marked with direction "in"

Behavior: when patching a property with direction set to 'in', the attribute is undefined when the ingress handler is called.

In PatchOp.apply(), #target is set to a constructed resource with direction = 'out'. Because of this, when the attribute setter is called in PatchOp.#add, the set does not occur.

PATCH error when replacing sub-attribute paths

I'm running a service created withscimmy through Microsoft's validator tool. This tool is buggy and full of holes, but in this case, I think it's making a legitimate request that isn't being handled correctly by scimmy.

For an existing user with all of these fields, the following request is made:

PATCH /Users/<my-user-id>
  {
    "Operations": [
      {
        "op": "replace",
        "value": {
          "userName": "[email protected]",
          "name.familyName": "Russell",
          "name.givenName": "Josie",
          "active": false
        }
      }
    ],
    "schemas": [
      "urn:ietf:params:scim:api:messages:2.0:PatchOp"
    ]
  }

These two-node paths like name.familyName and name.givenName look correct according to "3.10 Attribute Notation" in RFC 7644, and fit with the PATCH/replace examples given on page 45 of RFC 7644; but in SCIMmy they result in an invalidValue error with the message Value 'Russell' not valid for attribute 'name.familyName' of 'add' operation 1 in PatchOp request body.

Attribute names are case sensitive

Nice library. Thanks. I've been using it to build a simple SCIM demo app.

One problem: the filter matching works using a simple JS attribute lookup & compare (eg. return value[attr] === expected;), which means the attribute names are case sensitive. SCIM attribute names are meant to be case insensitive (RFC 7643, section 2.1 ... "Attribute names are case insensitive").

Unfortunately, the system I'm connecting to uses lower case names. It searches for 'username eq reddog123' and nothing comes back from the SCIMMY match function, because the attribute is called userName not username.

I guess the schema check would also fail if the ingress function got a JSON with unexpected cases for the attribute names.

Getting PatchOp Operations

First off, sorry for adding all these issues. I am trying to implement a enterprise SCIM gateway on top of this.

Another problem I am seeing is the express handlers (views) not passing the operations for the PatchOp

Here's an example PATCH request
Screenshot 2024-02-21 at 2 06 32 PM

Here's my code that handles the request and passes it to a controller.

app.ts

import * as UserController from "./users/users.controller";
...
SCIMMY.Resources.declare(SCIMMY.Resources.User)
    .ingress(UserController.ingress)
    .egress(UserController.egress)
    .degress((resource: any) => {
        console.log(`Request to delete user ${resource}`);
    });  
...

users.controller.ts

import {User as UserResource} from "lib/resources/user";
...
export async function egress(resource: UserResource) {
    console.log("resource", {...resource});

    const orgId = 1;
    const mappedFilters = mapScimToUserFilter(resource.filter ?? []);
    console.dir({mappedFilters}, {depth: null});

    const {handle} = findUserQuery;
    return handle({orgId, filter: mappedFilters});
}

When sending the mentioned PATCH request, my controller doesn't get any details about the PATCH Op

log from the controller

resource {
 id: '295',
 filter: Filter(1) [ { id: [Array] }, expression: 'id eq "295"' ]
}

Do I need to add another patch handler?

ListResponse not paginating as expected

Currently, ListResponse will paginate the results only when the length of the supplied resources array is greater than the supplied itemsPerPage value, which defaults to 20. This process does not take into account a supplied startIndex, meaning more results will be included in a page than expected when totalResults is less than itemsPerPage, effectively ignoring startIndex.

Steps to reproduce:
Instantiate a new ListResponse with a specified startIndex and fewer resources than itemsPerPage

new ListResponse(new Array(11).fill({}).map((_, i) => ({index: i+1})), {startIndex: 10, itemsPerPage: 11});

Expected behaviour:
Resources in the ListResponse should begin from the supplied startIndex

ListResponse {
  schemas: [ 'urn:ietf:params:scim:api:messages:2.0:ListResponse' ],
  totalResults: 11,
  Resources: [
    { index: 11 }
  ],
  startIndex: 10,
  itemsPerPage: 20
}

Actual behaviour:
All resources are included in the ListResponse, and startIndex is ignored

ListResponse {
  schemas: [ 'urn:ietf:params:scim:api:messages:2.0:ListResponse' ],
  totalResults: 11,
  Resources: [
    { index: 1 },  { index: 2 },
    { index: 3 },  { index: 4 },
    { index: 5 },  { index: 6 },
    { index: 7 },  { index: 8 },
    { index: 9 },  { index: 10 },
    { index: 11 }
  ],
  startIndex: 10,
  itemsPerPage: 20
}

Q: How to acquire request context in Resources.ingress/egress/degress?

Some background: we're considering using Scimmy to implement API for SCIM Provisioning in OKTA. Our user structure approximately looks like this: Workspace <-1-M-> Group <-M-M-> User.

This means that different organizations (read Workspaces) might wanna use different OKTA accounts to sync their users with our system. Problem is that during authentication it is known what workspace incoming request is originating from, but when request is passed down to ingress/egress/degress the context is lost, there's no way to identify which workspace should i use to list users from.

Is there any way to pass a context to Resource instance methods? I understand that this issue is in intersection between scimmy-routers and scimmy.

multiValued schema attributes throws 'error expected to be a collection in schema extension'

Hi,
This is an awesome library that does exactly what I want. Thank you so much.
I am trying to extend the User resource and add an multiValued property to it. But it seems to throw an error.
Definition

     const definition = new SCIMMY.Types.SchemaDefinition('Agent', 'urn:ietf:params:scim:schemas:company:Agent', 'Agent', [

            //https://scimmyjs.github.io/SCIMMY.Types.Attribute.html
            new SCIMMY.Types.Attribute('string', 'admin', { required: true, description: 'admin field' }),
            new SCIMMY.Types.Attribute('complex', 'agencies', { required: true, description: 'agencies array field', multiValued: true, direction: 'both' }, [

                new SCIMMY.Types.Attribute('string', 'name', { required: true, description: 'admin field' }),
            ]),
        ]);

        class AgentSchema extends SCIMMY.Types.Schema {
            static get definition() {
                return definition;
            }
        }

        SCIMMY.Resources.declare(SCIMMY.Resources.User.extend(AgentSchema, true))
            .ingress(ingress);

Test

    test('should validate true when Agent has extended attributes', async () => {
        await (new SCIMMY.Resources.User())
            .write({
                'userName': 'someGuy',
                'externalId': '5667',
                'emails': [
                    {
                        value: '[email protected]',
                        type: 'work',
                        primary: true,
                    },
                ],

                'schemas': ['urn:ietf:params:scim:schemas:company:Agent', 'urn:ietf:params:scim:schemas:core:2.0:User'],
                'urn:ietf:params:scim:schemas:domain:Agent': {
                    admin: 'test-admin',
                    agencies: []
                },
            },
            );
    });

This throws following error.

SCIMError: Attribute 'agencies' expected to be a collection in schema extension 'urn:ietf:params:scim:schemas:domain:Agent'

Test code: https://github.com/kanishka3000/scimmy-learning-tests/blob/42678fe3a871354ab85591b8422805df98f695dd/test/agent.test.ts#L49

Possible scenario

It appears that the array is being converted into an object with { 0: 'value', 1: 'value2}. by this function

mixedSource = [source[name.toLowerCase()] ?? {}, namespacedValues ?? {}].reduce(function merge(t, s) {
.

Thanks in advance for assistance.

Replace Patch Operation fails for required properties.

If I try to replace a required property of a resource, I get a 400 error with a message. Here's an example request replacing userName of a `User

Request

PATCH 'http://localhost:5103/scim/Users/user_225'

Body
 {
    "schemas": [
        "urn:ietf:params:scim:api:messages:2.0:PatchOp"
    ],
    "Operations": [
        {
            "op": "replace",
            "path": "userName",
            "value": "New_Username"
        }
    ]
}

Error

"Required attribute 'userName' is missing for 'replace' op of operation 1 in PatchOp request body"

This seems to be happening due to an error that happens while removing the value of the property before adding it again on this line

 if (value === undefined || !multiValued) target[property] = undefined;

Trying to set the value of the property, userName in this case, generates an error from the Resource

SCIMError: Required attribute 'userName' is missing

SCIMMY should support the "display" sub-attribute for members on Group POST and PATCH

RFC 7643, section 2.4 defines "display" as one of the default sub-attributes that could be provided in multi-valued attributes. This also applies to the "members" attribute of the Group resource. Currently SCIMMY will fail to ingress POST or PATCH requests that include members with "display" sub-attributes, since it is not included in the Group schema.
I've run into this when trying to integrate with Okta's SCIM user-provisioning capability.

Usage examples

Hi there, do you know of any example of a project that is using scimmy and syncing users to a database? I'm trying to implement the ingress, degress and egress methods, but I couldn't make it work as of yet.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.