Overview

Entitlement resource in IDHub maps to Group in Jira

After creating a directory in the Jira Admin account for a specific domain, the provisioning APIs for the Group Resource can be called using the directory ID.

Entitlement Resource Schema Configuration

Resource Type

Get types of resources available on a SCIM service provider (e.g., Users and Groups). This is used to get all resources of the SCIM provider. Filtering, pagination and sorting are not supported.

To fetch Group resource type schema from SCIM service provider, call https://api.atlassian.com/scim/directory/{directoryId}/ResourceTypes/Group API with Authorization and Accept request header. The response of the API is as follows

{
  "schemas": [
    "urn:ietf:params:scim:schemas:core:2.0:ResourceType"
  ],
  "id": "Group",
  "name": "Group",
  "description": "Group",
  "endpoint": "/Groups",
  "schema": "urn:ietf:params:scim:schemas:core:2.0:Group",
  "meta": {
    "location": "https://api.atlassian.com/scim/directory/{directoryId}/ResourceTypes/Group",
    "resourceType": "ResourceType"
  }
}
CODE

Group Schema

To fetch Group resource schema from SCIM provider, call https://api.atlassian.com/scim/directory/{directoryId}/Schemas/urn:ietf:params:scim:schemas:core:2.0:Group API with Authorization and Accept request header. The response of the API is as follows

{
  "id": "urn:ietf:params:scim:schemas:core:2.0:Group",
  "name": "Group",
  "description": "Group",
  "attributes": [
    {
      "name": "displayName",
      "description": "A human-readable name for the Group. REQUIRED.",
      "mutability": "immutable",
      "type": "string",
      "multiValued": false,
      "caseExact": false,
      "returned": "default",
      "required": true
    },
    {
      "name": "members",
      "description": "A list of members of the Group.",
      "mutability": "readWrite",
      "type": "complex",
      "multiValued": true,
      "returned": "default",
      "required": false,
      "subAttributes": [
        {
          "name": "value",
          "description": "Identifier of the member of this Group.",
          "mutability": "immutable",
          "type": "string",
          "multiValued": false,
          "caseExact": false,
          "returned": "default",
          "required": false
        },
        {
          "name": "$ref",
          "referenceTypes": [
            "User",
            "Group"
          ],
          "description": "The URI corresponding to a SCIM resource that is a member of this Group.",
          "mutability": "immutable",
          "type": "reference",
          "multiValued": false,
          "caseExact": false,
          "returned": "default",
          "required": false
        }
      ]
    },
    {
      "name" : "meta",
      "multiValued" : false,
      "description" : "A complex attribute containing resource metadata.",
      "mutability" : "readOnly",
      "subAttributes" : [
        {
          "name" : "resourceType",
          "multiValued" : false,
          "description" : "The name of the resource type of the resource.",
          "mutability" : "readOnly",
          "caseExact" : true
        },
        {
          "name" : "created",
          "multiValued" : false,
          "type" : "dateTime",
          "description" : "The \"DateTime\" that the resource was added to the service provider."
        },
        {
          "name" : "lastModified",
          "multiValued" : false,
          "type" : "dateTime",
          "description" : "The most recent DateTime that the details of this resource were updated at the service provider.  If this resource has never been modified since its initial creation, the value MUST be the same as the value of \"created\"."
        },
        {
          "name" : "location",
          "multiValued" : false,
          "description" : "The URI where the resource is available",
          "mutability" : "readOnly",
          "caseExact" : true
        },
        {
          "name" : "version",
          "multiValued" : false,
          "description" : "The version of the resource being returned.  This value must be the same as the entity-tag (ETag) HTTP response header (see Sections 2.1 and 2.3 of [RFC7232]).  This attribute has \"caseExact\" as \"true\".  Service provider support for this attribute is optional and subject to the service provider's support for versioning (see Section 3.14 of [RFC7644]).  If a service provider provides \"version\" (entity-tag) for a representation and the generation of that entity-tag does not satisfy all of the characteristics of a strong validator (see Section 2.1 of [RFC7232]), then the origin server MUST mark the \"version\" (entity-tag) as weak by prefixing its opaque value with \"W/\" (case sensitive).",
          "mutability" : "readOnly",
          "caseExact" : true
        }
      ]
    },
    {
      "name" : "schemas",
      "multiValued" : true,
      "description" : "The \"schemas\" attribute is a REQUIRED attribute and is an array of Strings containing URIs that are used to indicate the namespaces of the SCIM schemas that define the attributes present in the current JSON structure.  This attribute may be used by parsers to define the attributes present in the JSON structure that is the body to an HTTP request or response.  Each String value must be a unique URI.  All representations of SCIM schemas MUST include a non-empty array with value(s) of the URIs supported by that representation.  The \"schemas\" attribute for a resource MUST only contain values defined as \"schema\" and \"schemaExtensions\" for the resource's defined \"resourceType\".  Duplicate values MUST NOT be included.  Value order is not specified and MUST NOT impact behavior.",
      "mutability" : "readOnly",
      "returned" : "always",
      "caseExact" : true,
      "required" : true
    }
  ] 
}
CODE

Sample Group

A sample Group created in Jira is given below

{
  "schemas": [
    "urn:ietf:params:scim:schemas:core:2.0:Group"
  ],
  "id": "d84adcec-0818-4852-aad3-cbe79a614e1c",
  "displayName": "Developers Group",
  "members": [
    {
      "display": "jerome",
      "type": "User",
      "value": "ff2862db-864d-48db-93a4-162e2427ee6a",
      "$ref": "https://api.atlassian.com/scim/directory/{directoryId}/Users/{userId}"
    }
  ],
  "meta": {
    "created": "2021-06-15T11:41:20.571618Z",
    "location": "https://api.atlassian.com/scim/directory/{directoryId}/Groups/{groupId}",
    "lastModified": "2021-06-15T11:41:20.571618Z",
    "resourceType": "Group"
  }
}
CODE

Member Schema

Schema Representation

Sample Member Payload

{
   "schemas":[
      "urn:ietf:params:scim:api:messages:2.0:PatchOp"
   ],
   "Operations":[
      {
         "op":"add",
         "path":"members",
         "value":[
            {
               "value":"<userId>",
               "display":"<userEmail>"
            },
            {
               "value":"<userId>",
               "display":"<userEmail>"
            },
            {
               "value":"<userId>",
               "display":"<userEmail>"
            }
         ]
      }
   ]
}
CODE
{
   "schemas":[
      "urn:ietf:params:scim:api:messages:2.0:PatchOp"
   ],
   "Operations":[
      {
         "op":"add",
         "path":"members",
         "value":[
            {
               "value":"c6993c94-dbda-40f1-b6f0-18c855522ade",
               "display":"dave.meyer@demotime.authteam.com"
            },
            {
               "value":"f0ae48f7-1466-445e-85ea-e83ef754aefd",
               "display":"lingbo.lu@demotime.authteam.com"
            },
            {
               "value":"432d6f10-2e28-454e-be99-0f8c732a046f",
               "display":"joanna@demotime.authteam.com"
            }
         ]
      }
   ]
}
CODE

Project Roles

Get project roles for project :

GET /rest/api/2/project/{projectIdOrKey}/role

URL : 'https://your-domain.atlassian.net/rest/api/2/project/{projectIdOrKey}/role'

Returns a list of project roles for the project returning the name and self URL for each role.

Sample Response for Get Project Roles associated with the Project with a Project ID.

{
  "Administrators": "https://your-domain.atlassian.net/rest/api/2/project/MKY/role/10002",
  "Users": "https://your-domain.atlassian.net/rest/api/2/project/MKY/role/10001",
  "Developers": "https://your-domain.atlassian.net/rest/api/2/project/MKY/role/10000"
}
CODE

Get project role for project :

GET /rest/api/2/project/{projectIdOrKey}/role/{id}

URL : 'https://your-domain.atlassian.net/rest/api/2/project/{projectIdOrKey}/role/{id}'

Returns a project role's details and actors associated with the project. The list of actors is sorted by display name.

Sample Response for Get Project Role Details and Actors (Users) from Project for a particular Role ID and Project ID.

{
  "self": "https://your-domain.atlassian.net/rest/api/2/project/MKY/role/10360",
  "name": "Developers",
  "id": 10360,
  "description": "A project role that represents developers in a project",
  "actors": [
    {
      "id": 10240,
      "displayName": "jira-developers",
      "type": "atlassian-group-role-actor",
      "name": "jira-developers",
      "actorGroup": {
        "name": "jira-developers",
        "displayName": "jira-developers"
      }
    },
    {
      "id": 10241,
      "displayName": "Mia Krystof",
      "type": "atlassian-user-role-actor",
      "actorUser": {
        "accountId": "5b10a2844c20165700ede21g"
      }
    }
  ],
  "scope": {
    "type": "PROJECT",
    "project": {
      "id": "10000",
      "key": "KEY",
      "name": "Next Gen Project"
    }
  }
}
CODE

#Note : Entitlement name should be added as : <Role Name> in <Project Name> project

Entitlement Schema

{
  "schemas" : [
    "urn:ietf:params:scim:schemas:core:2.0:Schema"
  ],
  "id" : "urn:sath:params:scim:api:jira:1.0:Entitlement"
  "name" : "Entitlement"
  "description" : "To update and sync Jira Entitlements like Groups and Project Roles"
  "attributes": [
    {
      "name" : "id"
      "description" : "The complex id of the Entitlement. Format: Kind~<EntitlementID>. When this is a Project Role, Format should include Role ID and Project ID. Format: Kind~<RoleID>~<ProjectID>. REQUIRED."
      "required" : true
      "returned" : "always",
      "multivalued" : false
    },
    {
      "name" : "externalId",
      "description" : "Custom field containing unique record identifiers",
      "returned" : "always",
      "multiValued" : false,
    },
    {
      "name" : "displayName"
      "description" : "The displayName of the Entitlement. Format: Kind~<Entitlement Name>. When this is a Project Role, Format should include Role Name and Project Name. Format: '<RoleName> in <ProjectName> project'. REQUIRED."
      "required" : true
      "returned" : "always",
      "multivalued" : false
    },
    {
      "name" : "meta",
      "multiValued" : false,
      "description" : "A complex attribute containing resource metadata.",
      "mutability" : "readOnly",
      "subAttributes" : [
        {
          "name" : "resourceType",
          "multiValued" : false,
          "description" : "The name of the resource type of the resource.",
          "mutability" : "readOnly",
          "caseExact" : true
        },
        {
          "name" : "created",
          "multiValued" : false,
          "type" : "dateTime",
          "description" : "The \"DateTime\" that the resource was added to the service provider."
        },
        {
          "name" : "lastModified",
          "multiValued" : false,
          "type" : "dateTime",
          "description" : "The most recent DateTime that the details of this resource were updated at the service provider.  If this resource has never been modified since its initial creation, the value MUST be the same as the value of \"created\"."
        },
        {
          "name" : "location",
          "multiValued" : false,
          "description" : "The URI where the resource is available",
          "mutability" : "readOnly",
          "caseExact" : true
        },
        {
          "name" : "version",
          "multiValued" : false,
          "description" : "The version of the resource being returned.  This value must be the same as the entity-tag (ETag) HTTP response header (see Sections 2.1 and 2.3 of [RFC7232]).  This attribute has \"caseExact\" as \"true\".  Service provider support for this attribute is optional and subject to the service provider's support for versioning (see Section 3.14 of [RFC7644]).  If a service provider provides \"version\" (entity-tag) for a representation and the generation of that entity-tag does not satisfy all of the characteristics of a strong validator (see Section 2.1 of [RFC7232]), then the origin server MUST mark the \"version\" (entity-tag) as weak by prefixing its opaque value with \"W/\" (case sensitive).",
          "mutability" : "readOnly",
          "caseExact" : true
        }
      ]
    },
     {
      "name" : "schemas",
      "multiValued" : true,
      "description" : "The \"schemas\" attribute is a REQUIRED attribute and is an array of Strings containing URIs that are used to indicate the namespaces of the SCIM schemas that define the attributes present in the current JSON structure.  This attribute may be used by parsers to define the attributes present in the JSON structure that is the body to an HTTP request or response.  Each String value must be a unique URI.  All representations of SCIM schemas MUST include a non-empty array with value(s) of the URIs supported by that representation.  The \"schemas\" attribute for a resource MUST only contain values defined as \"schema\" and \"schemaExtensions\" for the resource's defined \"resourceType\".  Duplicate values MUST NOT be included.  Value order is not specified and MUST NOT impact behavior.",
      "mutability" : "readOnly",
      "returned" : "always",
      "caseExact" : true,
      "required" : true
    }
  ]
}
CODE

Implementation

The following methods of the target system connector interface defined in the connector SPI needs to be implemented for Entitlement(Group, Space) resource.

Get Schema

public Schema getSchema() throws InternalServerErrorException, NotImplementedException {
    for Group resource, call https://api.atlassian.com/scim/directory/{directoryId}/Schemas/urn:ietf:params:scim:schemas:core:2.0:Group with Authorization and Accept header 
    for Space resource, create schema
    if error occurs during fetching schema definition
	    throw InternalServerErrorException
	if no logic is implemented 
	    throw NotImplementedException
    return fetched JSON object with the schema definition       
}
CODE

Get Resource Type

public ResourceType getResourceType() throws InternalServerErrorException, NotImplementedException {
    for Group resource, call https://api.atlassian.com/scim/directory/{directoryId}/ResourceTypes/Group with Authorization and Accept header
    if error occurs during fetching resource type
	    throw InternalServerErrorException
	if no logic is implemented 
	    throw NotImplementedException
    return fetched resource type
}
CODE

Get SCIM Resource Service Information

public ResourceServiceInformation getScimResourceServiceInformation() throws InternalServerErrorException, NotImplementedException {
    for Group resource, call https://api.atlassian.com/scim/directory/{directoryId}/ServiceProviderConfig with Authorization and Accept header
    if error occurs during fetching SCIM information
	    throw InternalServerErrorException
	if no logic is implemented 
	    throw NotImplementedException
    return fetched SCIM information for resource
}
CODE

Get Health

public Health getHealth()
		throws InternalServerErrorException, NotImplementedException {
	implement /health actuator to fetch the health of the target system 
	if error occurs during fetching health information
	    throw InternalServerErrorException
	if no logic is implemented 
	    throw NotImplementedException
    return fetched health information in SCIM+JSON format or String containing SCIM+JSON
}
CODE

Create Resource

public IdhubObject postResource(String resource) throws BadRequestException, ConflictException, InternalServerErrorException, NotImplementedException {
    //for Group resource
    create a JSON object containing groupName as mandatory parameter
    call https://api.atlassian.com/scim/directory/{directoryId}/Groups with Authorization, Accept and Content as headers and JSON user object as the payload 
    from the api response, contruct the IDHubObject by mapping parameters with the resonse object
    create a JSON object with id, subject object containing type as User/Group and identifier as the corresponding Id of the type, operation object containing key and target as mandatory parameters
    if resource is unparsable, syntactically incorrect, or violates schema
	    throw BadRequestException
	if api or service provider refuses to create a new, duplicate resource
	    throw ConflictException
    if error occurs during resource creation
	    throw InternalServerErrorException
	if no logic is implemented 
	    throw NotImplementedException
    return the IDHubObject
}
CODE

Get Resource

public IdhubObject getResource(String id, Set<String> attributes, Set<String> excludedAttributes) throws NotFoundException, InternalServerErrorException, NotImplementedException {
    //for Group resource
    call https://api.atlassian.com/scim/directory/{directoryId}/Groups/{id} with Authorization and Accept as headers
    from the api response, contruct the IDHubObject by mapping parameters with the resonse object
    if no resource exists for the given id
	    throw NotFoundException
	if error occurs during resource fetch
	    throw InternalServerErrorException
	if no logic is implemented 
	    throw NotImplementedException 
    return the IDHubObject
}
CODE

Replace Resource

public IdhubObject putResource(String id, String resource) throws BadRequestException, ConflictException, NotFoundException, InternalServerErrorException, NotImplementedException {
    //for Group resource
    create a JSON object containing groupName as mandatory parameter
    call https://api.atlassian.com/scim/directory/{directoryId}/Groups/{id} with Authorization, Accept and Content as headers and JSON user object as the payload 
    from the api response, contruct the IDHubObject by mapping parameters with the resonse object
    if resource is unparsable, syntactically incorrect, or violates schema
	    throw BadRequestException
	if specified version number does not match the resource's latest version number
	    throw ConflictException
    if no resource exists for the given id
	    throw NotFoundException
	if error occurs during resource creation
	    throw InternalServerErrorException
	if no logic is implemented 
	    throw NotImplementedException 
    return the IDHubObject
}
CODE

Update Resource

public IdhubObject patchResource(String id, PatchOp patchOperations) throws BadRequestException, ConflictException, NotFoundException, InternalServerErrorException, NotImplementedException {
    //for Group resource, other Groups or Accounts can be added as members of the group 
    create a PatchOp object
    call https://api.atlassian.com/scim/directory/{directoryId}/Groups/{id} with Authorization, Accept and Content as headers and PatchOp object as the payload
    from the api response, contruct the IDHubObject by mapping parameters with the resonse object
    if resource is unparsable, syntactically incorrect, or violates schema
	    throw BadRequestException
	if specified version number does not match the resource's latest version number
	    throw ConflictException
    if no resource exists for the given id
	    throw NotFoundException
	if error occurs during resource creation
	    throw InternalServerErrorException
	if no logic is implemented 
	    throw NotImplementedException
    return the IdhubObject
}
CODE

Delete Resource 

public void deleteResource(String id) throws ConflictException, NotFoundException, InternalServerErrorException, NotImplementedException {
    //for Group resource
    call https://api.atlassian.com/scim/directory/{directoryId}/Groups/{id} with Authorization as header
    if specified version number does not match the resource's latest version number
	    throw ConflictException
    if no resource exists for the given id
	    throw NotFoundException
	if error occurs during resource creation
	    throw InternalServerErrorException
	if no logic is implemented 
	    throw NotImplementedException
}
CODE

Search Resource

public ListResponse searchResource(SearchRequest searchRequest) throws BadRequestException, PayloadTooLargeException, InternalServerErrorException, NotImplementedException {
    if the filter parameter is empty
		return all(*) instances of the given resource type are returned
	otherwise
        set query as per the requirement
    call https://your-domain.atlassian.net/rest/api/2/groupuserpicker?query={query} with Authorization and Accept as headers where query=<string> where the string can be a displayName, emailAddress or a substring of either displayName or emailAddress
    and can be used for querying users, groups
    in case of multiple pages, pagination can be done using start and limit in query parameters
    if searchRequest is unparsable, syntactically incorrect, or violates schema 
	    throw BadRequestException
    if payload size in bytes exceeds the max payload size
	    throw PayloadTooLargeException
	if error occurs during resource search
	    throw InternalServerErrorException
	if no logic is implemented 
	    throw NotImplementedException
    return the list of resource objects matching the searchRequest
}
CODE