SCIMing through the Bermuda Triangle

Back in the days of IdentityIQ 7.0 there weren’t a lot of options when it came to implementing a SCIM client and fewer still implementing a SCIM Server. When considering a design the engineering team opted to create two artifacts during deployment, a common piece and a server-side piece. The idea was to potentially open source the common piece and keep it abstracted away from the core IdentityIQ objects. For a number of releases, this has gone largely unnoticed, until now. In this blog, we’ll Navigate the disorienting waters of the scim-common.jar file.

*My grandfather, Ray Annino, was a great scholar, chemist and watercolor artist. He often painted sailing scenes and lighthouses of the New England area. Here we see the Provincetown Harbor at low tide.

Compass searches for scim-common don’t reveal anything related to SCIM. The “7_3_IdentityIQ_SCIM_API_Reference.pdf” documentation included within the IdentityIQ artifacts details each SCIM endpoint but not any information on how to consume the data from a java client.

You feel unsettled and confused as the SCIM triangle disorients you even more…

What is SCIM? 

The acronym SCIM stands for System for Cross-domain Identity Management. You can read more about them here:

RFC Protocol https://tools.ietf.org/html/rfc7644
RFC Schema https://tools.ietf.org/html/rfc7643

You might be thinking, is this guy really going to link to RFC’s within the first few paragraphs of me reading this blog? Yes, yes I am. These RFC’s are actually very well written and not overly lengthy. Plus they have the added bonus of being co-authored by one of SailPoint’s Architects Kelly Grizzle. What the specs boil down to are a few specific REST endpoints whose payload is JSON data. The endpoints are:

  • ServiceProviderConfig – this should be a publicly open link where the client can determine what authorization is supported among other features that are supported by the SCIM Server
  • ResourceTypes – these are the list of Resource definitions for stuff the SCIM Server will expose. Things like the endpoint URL, its schema and the ResourceType name can be found here
  • Schemas – Each ResourceType references a schema that defines the object structure of the Resource object. You would be able to tell that that the Schema for the User ResourceType contains a field userName and read a description of what that field is
  • Users – This is the main ResourceType within the specification. I represents a user in the system.
  • Groups – Groups are a bundle of users that contain access rights or entitlements granted via membership of the Group. The semantics of group membership are defined by the service provider and are out of scope of the specification.

There’s also support in the standard to define your own endpoints and ResourceTypes for however you define the Identity Management space in your organization.  We’ve done that within SailPoint with endpoints like Accounts, Entitlements and Roles. Writing a SCIM compliant client means you could write an identity retrieval endpoint that interfaces with IdentityIQ. This same client could be used to retrieve information from another Identity Management vendor who has also implemented SCIM. Imagine a scenario where all applications within an organization implemented SCIM. SailPoint would no longer need 60+ Application types, they could use the SCIM 2.0 application type to interface with ALL OF THEM!

API Objects

Without a nautical map, you’re stuck using the sun and stars as your guide.

The most useful thing as it relates to this little API is David Lee’s post which contains the Javadoc for the API. The API is fairly sparse and implements part of the RFC by defining SCIM objects and assists with serializing and deserializing these objects from sources like InputStreams and String values. An added benefit of the scim-common library is its method of using the Schema to parse the raw data and transform them to a Resource. You can also extend these objects and create your own POJO objects to represent the SCIM server Resources or use the generic Resource object to represent all the SCIM Server Resources.

Context

SCIMContext will be the first interface you’ll want to implement when working with the API. It’s pretty lightweight:

public ResourceType getResourceType(String resourceTypeIdOrName) throws SCIMException;

public Schema getSchema(String schemaId) throws SCIMException;

Thanks to AbstractSCIMContext you won’t have to do much but extend and initialize a schema and resource types map. By initializing AbstractSCIMContext you tell the SCIMClient how your SCIM objects will be defined and what they represent. For this exercise the implementation will use Apache http client to fetch this information from the SCIM server. However, in order to read the ResourceTypes and Schemas endpoint, the API needs to know how the schemas are defined for these endpoints. AbstractSCIMContext has a number of ‘fixed’ schemas (3 actually: ServiceProviderConfig, Schemas and ResourceTypes) that don’t change because they are defined by the RFC. These endpoints help APIs understand how the ResourceTypes User and Group are structured as well as defining a structure for any custom ResourceTypes.  In other words, the Schemas and ResourceTypes endpoints can be used to parse the User and Group endpoints. Our SCIM API did not include a bootstrapping SCIM context to read the Schemas and ResourceTypes endpoints, so let’s create one called BootstrapSCIMContext.

private class BootstrapSCIMContext extends AbstractSCIMContext { }

Now on to implementing these methods so we can read User and Group endpoints from a remote server. AbstractSCIMContext has a few protected methods that will make things easier to implement retrieving the metadata on our remote SCIM server.

protected void initResourceTypeMap() throws SCIMException;

protected void initSchemaMap() throws JSONException;
These waters have turned rather choppy, a thick fog has rolled in as you struggle to see what danger lies beyond.

For now, let’s not worry about how these methods have a slightly different signature. The abstract implementation uses two maps to store in-memory copies of the ResourceTypes and Schemas where the key is the id of the object and value the ResourceType or Schema object itself. Overriding these methods to call the super implementation first allows the SCIMContext to initialize the ‘fixed’ schema, after which we can use the BootstrapSCIMContext to read the User and Group objects from the SCIM Server. If your head is starting to ache, you are not alone as these waters have turned rather choppy, a thick fog has rolled in as you struggle to see what danger lies beyond.

Here’s a sample of ClientSCIMContext.initSchemaMap:

protected void initSchemaMap(JSONArray array) throws JSONException {

// initializes SCIM RFC Schema definitions

super.initSchemaMap(array);

// Grab the schemas from the server and stuff them into the map.

try {

ListResponse<Schema> schemas = getSchemas();

for (Schema schema : schemas.getResources()) {

String key = (null != schema.getId()) ? schema.getId() : schema.getName();

getSchemaMap().put(key, schema);

}

} catch (Exception e) {

throw new JSONException(e);

}

}

This example wouldn’t be complete without giving you a sample of what getSchemas() does, so let’s do that before your vessel sinks from a rogue wave or whatever the latest theory is that makes these waters so difficult to navigate.

Serialization

public ListResponse<Schema> getSchemas() throws Exception {

HttpEntity response = getRaw(this.url + "Schemas");

GenericType<ListResponse<Schema>> gt = new GenericType<ListResponse<Schema>>() { };

ListResponse<Schema> schemas = readList(new BootstrapSCIMContext(), response, gt);

debug(String.format("endpoint returned %s schemas", schemas.getTotalResults()));

return schemas;

}

We are using the helper method getRaw and the Apache Http Components API to perform an HTTP request using the correct Authentication headers and request to return an Apache HttpEntity response. We are fetching ALL the schema elements on the server and want to return a ListResponse of Schema objects. You may notice two things. One, we are reading a Schemas endpoint, so we need to use the BootstrapSCIMContext to initialize the SCIM schema definition before we read the Schemas endpoint. (it’s so meta, right?) The other interesting bit here is the use of the GenericType object. Due to type inference since the introduction of Generics we need to perform a special trick to tell the SCIM API the expected response is a ListResponse<Schema> using the GenericType object from the javax web services API. We are using another helper method, readList, which uses the SCIM API JSONReader.fromInputStream to transform the InputStream received from the Apache HttpEntity response into more friendly SCIM objects.

Putting it all together

For our final nautical maneuver, we’re going to use the information in the previous sections to retrieve a known User resource with an id. For that, we’ll hit the Users endpoint appending a ‘/theIdOfTheUser’ to the end of the URL so we retrieve only one result (instead of hitting /Users where that would return all users). Now that we’ve filled in the gaps along the starboard side of our sailing vessel we can jump straight to using our SCIMClient class to retrieve a resource in the main method.

SCIMClient client = new SCIMClient(url, creds, write);

UserResource res = (UserResource)client.get(ResourceType.RESOURCE_TYPE_USER, UserResource.class, id);

We are giving a couple of hints to the API like ResourceType id as well as the class used to represent SCIMObject and the API takes care of deserializing the raw response into a UserResource. In order to run the class you will need to include a minimal number of jar files in the classpath.

  • scim-common.jar – okay, this one is sort of obvious but worthy listing here
  • json.jar – this is a tiny JSON library that provides the foundation for absorbing JSON requests
  • httpcore-4.4.10.jar – this is from the Apache httpcomponents project
  • httpclient-4.5.6 – also from Apache httpcomponents
  • commons-logging-1.1.3.jar – Apache commons logging required by the Apache httpcomponents library
  • jaxrs-ri-2.22.2.jar – standard jaxrs reference implementation

and to run the SCIMClient from the command line:

java -cp bin:lib/json/json.jar:lib/scim-common.jar:lib/commons-logging-1.1.3.jar

:lib/httpcore-4.4.10.jar:lib/httpclient-4.5.6.jar:lib/jaxrs-ri-2.22.2.jar cannino.SCIMClient http://<host>:<port>/identityiq/scim/v2/ user:password <id of user>

should return the output:

>>>>>>>>>>>>>>>>

GET http://<host>:<port>/identityiq/scim/v2/Users/2c9084ee692e85f301692e86248d00d1

<<<<<<<<<<<<<<<< 

Status: 200

Response entity: ResponseEntityProxy{[Content-Type: application/scim+json;charset=UTF-8,Content-Length: 2279,Chunked: false]}

================

++++++ Initializing SCIM context

------ Initializing SCIM context completed

>>>>>>>>>>>>>>>> 

GET http://<host>:<port>/identityiq/scim/v2/ResourceTypes

<<<<<<<<<<<<<<<< 

Status: 200

Response entity: ResponseEntityProxy{[Content-Type: application/scim+json;charset=UTF-8,Content-Length: 6708,Chunked: false]}

================

endpoint returned 11 resource types

>>>>>>>>>>>>>>>> 

GET http://<host>:<port>/identityiq/scim/v2/Schemas

<<<<<<<<<<<<<<<< 

Status: 200

Response entity: ResponseEntityProxy{[Content-Type: application/scim+json;charset=UTF-8,Chunked: true]}

================

endpoint returned 16 schemas

UserResource username is: Howard.Rose

returned in 1287 milliseconds

You’re free to output whatever attribute you like, in this example we are simply using the username but any attribute can be retrieved by accessing the getters of the UserResource object. Also, note the duration of time the SCIMClient took to return. Remember we had to fetch the Schema and ResourceType endpoints the first time we deserialize the SCIM Response. Subsequent calls would not incur that cost as the metadata endpoints are now stored in memory. I’d like to further disclaimer this sample code by saying SCIM communication should occur over TLS and OAuth could be used instead of Basic Authentication to authenticate to the SCIM server. The entire SCIMClient source code can be downloaded here. With that, I hope you’ve found this blog useful and enlightening, Happy SCIM’ing!

Discussion