REST services for Resources Management
This page describes how to create a REST service so that it can be used by the Authorization Service to provide a lifecycle for a particular type of computing resources.
This is required only for the "push" integration mode described in the Resources lifecycle and eligibility integration page.
If you are a service manager and you would like to get a lifecycle management for the resources provided by your service (as is currently the case, for example, for computing accounts, Oracle accounts or websites) you can provide a REST interface over your service, that the Authorization Service will call to manage resources.
In particular, you will need to provide:
- A way to retrieve the schema of the resources managed by service.
- Methods for the actual resources management operations (create, read, update, delete).
Schema
Your REST service must provide a json schema defining the types it manages and the attributes your service understands and can manage.
Please check the provided example schema. You will only need to customize the name of your resource and remove the types or attributes you don't need.
The available types are managed resource types (e.g. Website), accounts and groups.
Managed Resource Types
These are the types that describe your resources. Regardless of the resource type, the attributes are:
Attribute Name | Type | Description |
---|---|---|
Id (required) | Guid | An immutable and unique identifier for an object, that will never be reused. |
Name (required) | String | An immutable and unique name for the resource. Can be reused. |
State | String (Enum) | The simplified state of the resource. |
OwnerId | Guid | The Id of the owner of the resource. See the Account type. |
Owner | String | The UPN (login) of the owner of the resource. |
AdministratorsId | Guid | The Id of the administrators of the resource. See the Group type. |
Administrators | String | The name of the group of administrators of the resource. |
Account
Attribute Name | Type | Description |
---|---|---|
Id (required) | Guid | An immutable and unique identifier for the account, that will never be reused. |
Name (required) | String | The account's UPN (login). Immutable and unique, can be reused. |
AccountType | String | The account type (Person, Secondary, Service) |
String | The account's email | |
FirstName | String | |
LastName | String | |
Department | String | |
Group | String | |
Section | String | |
OwnerId | Guid | The Id of the owner of the account (pointer to an account). |
Owner | String | The UPN (login) of the owner of the account. |
Group
Attribute Name | Type | Description |
---|---|---|
Id (required) | Guid | An immutable and unique identifier for the group, that will never be reused. |
Name (required) | String | The group's name. Immutable and unique, can be reused. |
String | The group's email | |
OwnerId | Guid | The Id of the owner of the group (pointer to an account). |
Owner | String | The UPN (login) of the owner of the group. |
The ID attribute
Each type must define an ID attribute, that will be populated by the Authorization Service with a unique identifier (a GUID).
Your service needs to store this identifier and be able to identify the objects it manages based on that identifier.
Resource Management Operations
The Authorization Service will call rest methods on some base address (e.g. https://websites-management.cern.ch/api
)
followed by the name of the resource as defined in the schema. For example, to get the complete list of websites in the
system, it will issue a GET operation at https://websites-management.cern.ch/api/website
.
Operation | Verb | URL | Status codes and return values |
---|---|---|---|
Get all resources of type website | GET | /website | 200 + json serialization of envelope with result, resources, pagination and delta data. |
Create a new website | POST | /website | 201 (Created) + envelope with json serialization of created object. |
Modify an existing website | PUT or PATCH | /website/id | 200 + envelope with json serialization of created object. |
Delete a website | DELETE | /website/id | 204 (No Content). |
To update an existing resource, the service can either issue a PUT or a PATCH operation. This can be configured when the REST service is being setup, and cannot be changed later.
Return data is always expected to be contained in an envelope. This is for uniformity with other system parts and
to provide support for pagination and delta operations in GET
methods.
Write operations (create, update, delete) should be reasonably fast, because each write operation is blocking for the Authorization Service synchronization mechanism. If an operation (e.g. the creation of a resource) takes a long time, it is better to define a “status” attribute, which should be set to “pending” when the service receives a POST call, and is then updated by the system until it reaches a final “completed” state. This allows the POST call to return immediately; if later the resource ends in a “failed” state, it can simply be deleted, so that the service will re-try to create it at the next round.
Create (POST)
To create an object, the system will issue a POST
with the full definition of the object in the body.
The expected response is an envelope with the created object in the data
field.
Example:
Method: POST /api/website
Request body:
{
"id" : "fa58fb40-e2c2-42db-8e76-a6aa6b1bfab5",
"name" : "some-website",
"owner" : "bdc32740-1dcd-4d3a-a491-9fdc364b9e1d",
"aliases" : [
"a-site-about-something",
"an-amazing-site"
]
}
Response body:
{
"data" : {
"id" : "fa58fb40-e2c2-42db-8e76-a6aa6b1bfab5",
"name" : "some-website",
"owner" : "bdc32740-1dcd-4d3a-a491-9fdc364b9e1d",
"aliases" : [
"a-site-about-something",
"an-amazing-site"
]
}
}
Update (PUT)
To update an object through a PUT
operation, the system will issue a PUT
with the full definition of the object in
the body.
The expected response is an envelope with the full definition of the updated object in the data
field.
Example:
Method: POST /api/website/fa58fb40-e2c2-42db-8e76-a6aa6b1bfab5
Request body:
{
"id" : "fa58fb40-e2c2-42db-8e76-a6aa6b1bfab5",
"name" : "the-name-was-changed",
"owner" : "bdc32740-1dcd-4d3a-a491-9fdc364b9e1d",
"aliases" : [
"a-site-about-something",
"an-amazing-site"
]
}
Response body:
{
"data" : {
"id" : "fa58fb40-e2c2-42db-8e76-a6aa6b1bfab5",
"name" : "the-name-was-changed",
"owner" : "bdc32740-1dcd-4d3a-a491-9fdc364b9e1d",
"aliases" : [
"a-site-about-something",
"an-amazing-site"
]
}
}
Update (PATCH)
To update an object through a PATCH
operation, the system will issue a PATCH
with the object's changed attributes in
the body.
The expected response is an envelope with the full definition of the updated object in the data
field.
Example:
Method: PATCH /api/website/fa58fb40-e2c2-42db-8e76-a6aa6b1bfab5
Request body:
{
{
"op": "replace",
"path": "/name",
"value": "the-name-was-changed"
}
}
Response body:
{
"data" : {
"id" : "fa58fb40-e2c2-42db-8e76-a6aa6b1bfab5",
"name" : "the-name-was-changed",
"owner" : "bdc32740-1dcd-4d3a-a491-9fdc364b9e1d",
"aliases" : [
"a-site-about-something",
"an-amazing-site"
]
}
}
Delete (DELETE)
To delete an object, the system will issue a DELETE
with no body.
Example:
Method: DELETE /api/website/fa58fb40-e2c2-42db-8e76-a6aa6b1bfab5
Read operations (GET)
After the Authorization Service has created, updated and deleted the resouces it needed to, it reads back data from the REST service, to determine if the objects are actually in the state they are expected to be.
This can be done with full import operations, where the service asks the REST API “give me all the resources as they are now”, or with delta import operations, where the REST API asks the external system “give me all the changes since a certain situation”.
Reading is the most critical operation in terms of performance for the synchronization process, and therefore it's also the one that requires more work to be supported.
The two key features that you will need to implement are pagination and support for delta operations.
Pagination
The data is always expected to be contained in an envelope, which must have this format:
{
// Pagination information
"pagination": {
// Relative URL to invoke to continue the enumeration. REQUIRED.
// The Authorization Service will keep calling GET with the 'next' URL until this field is empty.
"next": "/api/person?limit=5&lastId=id005&nextDelta=eyJQZXJzb25zRGVsdGEiOjE1LCJXZWJzaXRlc0RlbHRhIjowfQ==",
// Total number of objects. Optional, useful for troubleshooting.
"total": 15,
// Maximum number of objects returned. Optional, useful for troubleshooting.
"limit": 5
},
"delta": {
// Information that your service will need to receive to provide a delta import from the current situation.
// If your service supports delta operations, this field is REQUIRED.
"token": "eyJQZXJzb25zRGVsdGEiOjE1LCJXZWJzaXRlc0RlbHRhIjowfQ=="
},
// An array with the actual objects
"data": [
{
"id": "id001",
"name": "Amelia Gabriela"
},
// more objects ...
{
"id": "id005",
"name": "Clotilda Karin"
}
]
}
The REST API must return in the pagination:next
field the relative URL to call to continue the enumeration.
Full Import
To retrieve the complete list of persons, the MA will start with this call:
GET /api/person?limit=1000
The REST API will return:
{
"data": [ /* objects here... */ ],
"pagination": {
"next": "/api/person?limit=1000&lastId=id005&nextDelta=eyJQZXJzb25zRGVsdGEiOjE1LCJXZWJzaXRlc0RlbHRhIjowfQ=="
},
"delta": {
"token": "eyJQZXJzb25zRGVsdGEiOjE1LCJXZWJzaXRlc0RlbHRhIjowfQ=="
}
}
Since the pagination:next
field is not empty, the Authorization Service will call:
GET /api/person?limit=5&lastId=id005&nextDelta=eyJQZXJzb25zRGVsdGEiOjE1LCJXZWJzaXRlc0RlbHRhIjowfQ==
Note that the REST API is passing back to itself the value of the delta token, so that continuing the operation can be completely stateless for the REST service.
If the REST API returns:
{
"data": [ /* more objects here... */ ],
"pagination": {
"next": null
},
"delta": {
"token": "eyJQZXJzb25zRGVsdGEiOjE1LCJXZWJzaXRlc0RlbHRhIjowfQ=="
}
}
Since the pagination:next
field is empty, the Authorization Service will consider that the import operation is
completed.
Delta Import
Delta import operations are used by MIM to ask to the REST service what has changed since the last import operation.
If the last import operation was the full import of the previous example, the first delta import call will be
GET /api/person?limit=1000&delta=eyJQZXJzb25zRGVsdGEiOjE1LCJXZWJzaXRlc0RlbHRhIjowfQ==
The presence of the delta
parameter indicates to the REST service that this is a delta operation. Therefore, the
service must return to MIM all the changes since the moment indicated by the value of the delta parameter in the query
string.
Let’s suppose that since the moment indicated by the delta value, there have been 3 changes in the connected system:
The person with ID id1001 was created. The person with ID id1002 was modified. The person with ID id1003 was deleted.
The REST API must return something like this:
{
"data": [ // note that now we have operation type and object definition
{
"operation": "add",
"object": {
"ID": "id1001",
"Name": "Keyser Söze"
}
},
{
"operation": "modify",
"object": {
"ID": "id1002",
"Name": "Roger Verbal Kint"
}
},
{
"operation": "delete",
"object": {
"ID": "id1003" // only ID is relevant in case of delete
}
}
],
"pagination": {
"total": 3,
"limit": 1000,
"token": null
},
"delta": {
// a new token that represents the data to pass back at the next delta
// operation
"token": "QWxsIHdvcmsgYW5kIG5vIHBsYXkgbWFrZXMgSmFjayBhIGR1bGwgYm95Lg=="
}
}
Note that now the data
array does not simply contain objects, but the operation type and the object definition.
In case of added or modified object, the REST service must return the definition of the whole object in its current
state, while for deleted objects only the ID is relevant.