Copyright © 2005-2025

Copies of this document may be made for your own use and for distribution to others, provided that you do not charge any fee for such copies and further provided that each copy contains this Copyright Notice, whether distributed in print or electronically.

Overview

This guide describes the RESTful API of the OpenWMS.org UAA (User Authentication & Administration) module and its usage. Some general terms and definitions are explained and declared in the first part of the document whereas in the second part the usage of the API is shown in a more use-case-driven approach.

Resources

A description of all used API resources.

Representation Formats

Basically JSON is used as representation format by default if not otherwise requested or mentioned below. XML format is supported for the index pages as well, but must be requested explicitly. Furthermore a vendor specific JSON format is used to represent resource representations. Therefore it’s absolutely required the Accept header denotes the demanded type. Find the type in the examples below.

Dates in JSON

Date or datetime fields are not treated specially in JSON. Within the scope of this API a date or datetime field is always expected and rendered as JSON String in ISO8601 format with timezone information and milliseconds: yyyy-MM-dd’T’HH:mm:ssXXX.

Embedded Entities

For the sake of convenience some response entities may included embedded entities or even parts of it. A reference to the actual entity is provided as HAL link as well.

Error Responses

Beside the actual representation of resources, the server may result an error response in JSON format that contains basic information about the error occurred. This information is additional to the standard HTTP status code and may help clients to identify the error cause in order to re-phrase and send the request again.

Currently there are two types of errors with their own response formats.

Server Declined Errors

This kind of errors are sent by the server runtime environment even before the request had a chance to be processed.

An example response looks like this:

{
    "timestamp": 1512400854941,
    "status": 500,
    "error": "Internal Server Error",
    "exception": "org.ameba.oauth2.InvalidTokenException",
    "message": "JWT expired at 2017-12-04T15:04:43Z. Current time: 2017-12-04T15:20:54Z, ...",
    "path": "/v1/transport-units?bk=00000000000000004711"
}

The JSON structure contains the following fields.

Property Name Description

timestamp

When the error occurred on server side

status

The http status of the error

error

A short error text

exception

Internal class name of the Java exception type

message

A more descriptive error text describing the error in detail

path

The part of the URI for the REST resource that was queried

API Declined Errors

Those errors are thrown within the REST API validation and processing logic. For example, if a client request does not match the expected format or has produced an error on server side, the API will decline the request and return a response with status client-side error (4xx).

The structure of the response is aligned to the RFC7808. An example response looks like this:

{
    "message": "LocationGroup with name [NOT_EXISTS] not found",
    "messageKey": "owms.common.common.lg.notFoundByName",
    "obj" : [ "NOT_EXISTS" ],
    "httpStatus" : "404",
    "class" : "String"
}

The JSON structure contains the following fields.

Property Name Description

message

A short error text

messageKey

An unique identifier across the API that can be used to identify and translate the error message on the client side

obj

An array of possible passed arguments to the message

httpStatus

The http status of the error

class

The arguments type

Following message keys are currently used:

Message Key Description Action

not.found

The requested resource has not been found

The resource does not exist anymore or has never existed. The resource identifier must be verified

Index

The initial HTTP request to retrieve information about all available resources looks like the following. The Index page is a public available resource and does not need any authentication.

GET /index HTTP/1.1
Host: localhost:8080

The Index resource is returned in the response body with the response status of 200-OK. This main Index lists all primary resource entities to follow next.

HTTP/1.1 200 OK
Content-Type: application/hal+json
Content-Length: 260

{
  "_links" : {
    "user-index" : {
      "href" : "http://localhost:8080/users/index"
    },
    "role-index" : {
      "href" : "http://localhost:8080/roles/index"
    },
    "grant-index" : {
      "href" : "http://localhost:8080/grants/index"
    }
  }
}

A client application only needs to know about the agreed link names and follow the corresponding href link to navigate to further resources.

User

A User represents a human user of the system, usually in front of an UI who interacts with the system. User representations can be created, modified and retrieved. An User is also allowed to authenticate against an UAA endpoint.

User Index

An overview of all possible operations on Users can be found on the User index page that is retrieved with a GET request:

GET /users/index HTTP/1.1
Host: localhost:8080

And the server responds with a HAL+JSON representation to further operations:

HTTP/1.1 200 OK
Content-Type: application/hal+json
Content-Length: 725

{
  "_links" : {
    "users-findbypkey" : {
      "href" : "http://localhost:8080/users/%7BpKey%7D"
    },
    "users-findall" : {
      "href" : "http://localhost:8080/users"
    },
    "users-findgrants" : {
      "href" : "http://localhost:8080/users/%7BpKey%7D/grants"
    },
    "users-create" : {
      "href" : "http://localhost:8080/users"
    },
    "users-save" : {
      "href" : "http://localhost:8080/users"
    },
    "users-saveimage" : {
      "href" : "http://localhost:8080/users/%7BpKey%7D/details/image"
    },
    "users-change-password" : {
      "href" : "http://localhost:8080/users/%7BpKey%7D/password"
    },
    "users-delete" : {
      "href" : "http://localhost:8080/users/%7BpKey%7D"
    }
  }
}

Create an User

To create a new User instance, a POST request must be send to the server with the mandatory fields of the User resource in the request body.

POST /users HTTP/1.1
Content-Type: application/json
Content-Length: 140
Host: localhost:8080

{
  "links" : [ ],
  "username" : "admin2",
  "emailAddresses" : [ {
    "emailAddress" : "admin2@example.com",
    "primary" : true
  } ]
}
Path Type Description

username

String

The unique name of the User in the system

emailAddresses[]

Array

The User’s email addresses

emailAddresses[].emailAddress

String

The actual email address

emailAddresses[].primary

Boolean

Whether this email address is the primary one used in the system. Each User can only have one primary email address

If the User has been created successfully, the server returns the URI to the created resource in the Location header:

HTTP/1.1 201 Created
Location: http://localhost:8080/users/46033140-6938-4aca-b7e4-46079e7a36f6
Content-Type: application/vnd.openwms.uaa.user-v1+json
Content-Length: 377

{
  "ol" : 0,
  "createDt" : "2025-02-04T23:09:57.614447136Z",
  "lastModifiedDt" : "2025-02-04T23:09:57.614447136Z",
  "pKey" : "46033140-6938-4aca-b7e4-46079e7a36f6",
  "username" : "admin2",
  "externalUser" : false,
  "locked" : false,
  "enabled" : true,
  "emailAddresses" : [ {
    "emailAddress" : "admin2@example.com",
    "primary" : true
  } ],
  "roleNames" : [ ]
}

If an User with the same name already exists the server returns an error:

HTTP/1.1 409 Conflict
Content-Type: application/hal+json
Content-Length: 166

{
  "message" : "User with name tester already exists",
  "messageKey" : "user.already.exists",
  "obj" : [ "tester" ],
  "httpStatus" : "409",
  "class" : "String"
}

Find all Users

To find and retrieve an array of all existing Users a client may call a GET request:

GET /users HTTP/1.1
Host: localhost:8080

and returns either an array of Users…​

HTTP/1.1 200 OK
Content-Type: application/vnd.openwms.uaa.user-v1+json
Content-Length: 1662

[ {
  "links" : [ {
    "rel" : "user-findbypkey",
    "href" : "http://localhost:8080/users/96baa849-dd19-4b19-8c5e-895d3b7f405d"
  } ],
  "ol" : 1,
  "createDt" : "2020-06-22T19:02:47.404000000Z",
  "lastModifiedDt" : "2025-02-04T23:10:00.068423000Z",
  "pKey" : "96baa849-dd19-4b19-8c5e-895d3b7f405d",
  "username" : "jenkins",
  "externalUser" : true,
  "lastPasswordChange" : "2020-06-22T19:02:47Z",
  "locked" : true,
  "enabled" : true,
  "expirationDate" : "2020-06-23T19:02:45Z",
  "fullname" : "Mister Jenkins",
  "details" : { },
  "emailAddresses" : [ {
    "emailAddress" : "admin.private@acme.com",
    "primary" : true,
    "fullname" : "Mr. Jenkins"
  }, {
    "emailAddress" : "admin@acme.com",
    "primary" : false,
    "fullname" : "Mr. Jenkins"
  } ],
  "roleNames" : [ "ROLE_ADMIN" ],
  "password" : "{bcrypt}$2a$15$baURCfRsoxem.eOv0IJDsup.9wEmHdiw.j8f0RaMflDbFnQWNipvG"
}, {
  "links" : [ {
    "rel" : "user-findbypkey",
    "href" : "http://localhost:8080/users/96baa849-dd19-4b19-8c5e-895d3b7f405e"
  } ],
  "ol" : 1,
  "createDt" : "2020-06-22T19:02:47.404000000Z",
  "lastModifiedDt" : "2025-02-04T23:10:00.068423000Z",
  "pKey" : "96baa849-dd19-4b19-8c5e-895d3b7f405e",
  "username" : "tester",
  "externalUser" : false,
  "lastPasswordChange" : "2020-06-22T19:02:47Z",
  "locked" : false,
  "enabled" : true,
  "expirationDate" : "2020-06-23T19:02:45Z",
  "fullname" : "Tester",
  "details" : { },
  "emailAddresses" : [ {
    "emailAddress" : "tester@acme.com",
    "primary" : true,
    "fullname" : "Mr. Tester"
  } ],
  "roleNames" : [ ],
  "password" : "{bcrypt}$2a$15$baURCfRsoxem.eOv0IJDsup.9wEmHdiw.j8f0RaMflDbFnQWNipvG"
} ]

or an empty array, but always with a 200-OK.

HTTP/1.1 200 OK
Content-Type: application/vnd.openwms.uaa.user-v1+json
Content-Length: 1649

[ {
  "links" : [ {
    "rel" : "user-findbypkey",
    "href" : "http://localhost:8080/users/96baa849-dd19-4b19-8c5e-895d3b7f405d"
  } ],
  "ol" : 1,
  "createDt" : "2020-06-22T19:02:47.404000000Z",
  "lastModifiedDt" : "2025-02-04T23:09:50.079814000Z",
  "pKey" : "96baa849-dd19-4b19-8c5e-895d3b7f405d",
  "username" : "jenkins",
  "externalUser" : true,
  "lastPasswordChange" : "2020-06-22T19:02:47Z",
  "locked" : true,
  "enabled" : true,
  "expirationDate" : "2020-06-23T19:02:45Z",
  "fullname" : "Mister Jenkins",
  "details" : { },
  "emailAddresses" : [ {
    "emailAddress" : "admin.private@acme.com",
    "primary" : true,
    "fullname" : "Mr. Jenkins"
  }, {
    "emailAddress" : "admin@acme.com",
    "primary" : false,
    "fullname" : "Mr. Jenkins"
  } ],
  "roleNames" : [ ],
  "password" : "{bcrypt}$2a$15$baURCfRsoxem.eOv0IJDsup.9wEmHdiw.j8f0RaMflDbFnQWNipvG"
}, {
  "links" : [ {
    "rel" : "user-findbypkey",
    "href" : "http://localhost:8080/users/96baa849-dd19-4b19-8c5e-895d3b7f405e"
  } ],
  "ol" : 1,
  "createDt" : "2020-06-22T19:02:47.404000000Z",
  "lastModifiedDt" : "2025-02-04T23:09:50.079814000Z",
  "pKey" : "96baa849-dd19-4b19-8c5e-895d3b7f405e",
  "username" : "tester",
  "externalUser" : false,
  "lastPasswordChange" : "2020-06-22T19:02:47Z",
  "locked" : false,
  "enabled" : true,
  "expirationDate" : "2020-06-23T19:02:45Z",
  "fullname" : "Tester",
  "details" : { },
  "emailAddresses" : [ {
    "emailAddress" : "tester@acme.com",
    "primary" : true,
    "fullname" : "Mr. Tester"
  } ],
  "roleNames" : [ ],
  "password" : "{bcrypt}$2a$15$baURCfRsoxem.eOv0IJDsup.9wEmHdiw.j8f0RaMflDbFnQWNipvG"
} ]

Find an User by persistent key

A newly created User can be retrieved by following the URI in the Location header of the response. This URI points to the created resource:

GET /users/96baa849-dd19-4b19-8c5e-895d3b7f405d HTTP/1.1
Host: localhost:8080

If the resource exists the server responds with a 200-OK and the User representation in the response body.

HTTP/1.1 200 OK
Content-Type: application/vnd.openwms.uaa.user-v1+json
Content-Length: 892

{
  "_links" : {
    "user-findbypkey" : {
      "href" : "http://localhost:8080/users/96baa849-dd19-4b19-8c5e-895d3b7f405d"
    }
  },
  "ol" : 1,
  "createDt" : "2020-06-22T19:02:47.404000000Z",
  "lastModifiedDt" : "2025-02-04T23:09:57.674158000Z",
  "pKey" : "96baa849-dd19-4b19-8c5e-895d3b7f405d",
  "username" : "jenkins",
  "externalUser" : true,
  "lastPasswordChange" : "2020-06-22T19:02:47Z",
  "locked" : true,
  "enabled" : true,
  "expirationDate" : "2020-06-23T19:02:45Z",
  "fullname" : "Mister Jenkins",
  "details" : { },
  "emailAddresses" : [ {
    "emailAddress" : "admin.private@acme.com",
    "primary" : true,
    "fullname" : "Mr. Jenkins"
  }, {
    "emailAddress" : "admin@acme.com",
    "primary" : false,
    "fullname" : "Mr. Jenkins"
  } ],
  "roleNames" : [ "ROLE_ADMIN" ],
  "password" : "{bcrypt}$2a$15$baURCfRsoxem.eOv0IJDsup.9wEmHdiw.j8f0RaMflDbFnQWNipvG"
}

Find all Roles that are assigned to a User

A client might ask for all Roles that are assigned to a User resource. Therefore the client needs to query all roles of the primary User:

GET /users/96baa849-dd19-4b19-8c5e-895d3b7f405d/roles HTTP/1.1
Host: localhost:8080

and get an array of Roles back, that could also be empty:

HTTP/1.1 200 OK
Content-Type: application/vnd.openwms.uaa.role-v1+json
Content-Length: 2192

[ {
  "@class" : "org.openwms.core.uaa.api.RoleVO",
  "ol" : 1,
  "pKey" : "1",
  "name" : "ROLE_ADMIN",
  "description" : "Super user role",
  "immutable" : true,
  "users" : [ {
    "links" : [ ],
    "ol" : 1,
    "createDt" : "2020-06-22T19:02:47.404000000Z",
    "lastModifiedDt" : "2025-02-04T23:10:00.152946000Z",
    "pKey" : "96baa849-dd19-4b19-8c5e-895d3b7f405d",
    "username" : "jenkins",
    "externalUser" : true,
    "lastPasswordChange" : "2020-06-22T19:02:47Z",
    "locked" : true,
    "enabled" : true,
    "expirationDate" : "2020-06-23T19:02:45Z",
    "fullname" : "Mister Jenkins",
    "details" : { },
    "emailAddresses" : [ {
      "emailAddress" : "admin.private@acme.com",
      "primary" : true,
      "fullname" : "Mr. Jenkins"
    }, {
      "emailAddress" : "admin@acme.com",
      "primary" : false,
      "fullname" : "Mr. Jenkins"
    } ],
    "roleNames" : [ "ROLE_ADMIN" ],
    "password" : "{bcrypt}$2a$15$baURCfRsoxem.eOv0IJDsup.9wEmHdiw.j8f0RaMflDbFnQWNipvG"
  } ],
  "grants" : [ {
    "@class" : "org.openwms.core.uaa.api.SecurityObjectVO",
    "ol" : 1,
    "createDt" : "2025-02-04T23:10:00.152946000Z",
    "lastModifiedDt" : "2025-02-04T23:10:00.152946000Z",
    "pKey" : "5",
    "name" : "SEC_UAA_USER_MODIFY",
    "description" : "Permission to modify Users"
  }, {
    "@class" : "org.openwms.core.uaa.api.SecurityObjectVO",
    "ol" : 1,
    "createDt" : "2025-02-04T23:10:00.152946000Z",
    "lastModifiedDt" : "2025-02-04T23:10:00.152946000Z",
    "pKey" : "4",
    "name" : "SEC_UAA_USER_CREATE",
    "description" : "Permission to create Users"
  }, {
    "@class" : "org.openwms.core.uaa.api.SecurityObjectVO",
    "ol" : 1,
    "createDt" : "2025-02-04T23:10:00.152946000Z",
    "lastModifiedDt" : "2025-02-04T23:10:00.152946000Z",
    "pKey" : "3",
    "name" : "SEC_UAA_USER_LOOKUP",
    "description" : "Permission to find Users"
  }, {
    "@class" : "org.openwms.core.uaa.api.SecurityObjectVO",
    "ol" : 1,
    "createDt" : "2025-02-04T23:10:00.152946000Z",
    "lastModifiedDt" : "2025-02-04T23:10:00.152946000Z",
    "pKey" : "6",
    "name" : "SEC_UAA_USER_DELETE",
    "description" : "Permission to delete Users"
  } ]
} ]

Find all Grants that belong to a User

A client might ask for all Grants that are assigned to a User resource. Therefore the client needs to query all grants of the primary User: Unresolved directive in 2-users.adoc - include::/home/runner/work/org.openwms.core.uaa.lib/org.openwms.core.uaa.lib/target/generated-snippets/user-findGrantsOfUser/http-request.adoc[]

and get an array of Grants back, that could also be empty: Unresolved directive in 2-users.adoc - include::/home/runner/work/org.openwms.core.uaa.lib/org.openwms.core.uaa.lib/target/generated-snippets/user-findGrantsOfUser/http-response.adoc[]

Modify an existing User

An existing User instance can be modified by sending a PUT request with the User representation as request body.

PUT /users HTTP/1.1
Content-Type: application/json
Content-Length: 233
Host: localhost:8080

{
  "links" : [ ],
  "pKey" : "96baa849-dd19-4b19-8c5e-895d3b7f405e",
  "username" : "superuser",
  "locked" : true,
  "enabled" : false,
  "emailAddresses" : [ {
    "emailAddress" : "admin@example.com",
    "primary" : true
  } ]
}

If the server has correctly updated the User the response is a:

HTTP/1.1 200 OK
Content-Type: application/vnd.openwms.uaa.user-v1+json
Content-Length: 466

{
  "ol" : 0,
  "createDt" : "2020-06-22T19:02:47.404000000Z",
  "lastModifiedDt" : "2025-02-04T23:09:55.107127000Z",
  "pKey" : "96baa849-dd19-4b19-8c5e-895d3b7f405e",
  "username" : "superuser",
  "externalUser" : false,
  "locked" : true,
  "enabled" : false,
  "emailAddresses" : [ {
    "emailAddress" : "admin@example.com",
    "primary" : true
  } ],
  "roleNames" : [ ],
  "password" : "{bcrypt}$2a$15$baURCfRsoxem.eOv0IJDsup.9wEmHdiw.j8f0RaMflDbFnQWNipvG"
}

Change the Users password

Send a POST request to the Users password attribute to set or change the password of an that User.

POST /users/96baa849-dd19-4b19-8c5e-895d3b7f405e/password HTTP/1.1
Content-Type: application/json
Content-Length: 28
Host: localhost:8080

{
  "password" : "welcome"
}

If the server has successfully set the password the response looks like:

HTTP/1.1 200 OK
Content-Type: application/vnd.openwms.uaa.user-v1+json
Content-Length: 534

{
  "ol" : 3,
  "createDt" : "2020-06-22T19:02:47.404000000Z",
  "lastModifiedDt" : "2025-02-04T23:09:55.209838000Z",
  "pKey" : "96baa849-dd19-4b19-8c5e-895d3b7f405e",
  "username" : "superuser",
  "externalUser" : false,
  "lastPasswordChange" : "2025-02-04T23:09:57Z",
  "locked" : true,
  "enabled" : false,
  "details" : { },
  "emailAddresses" : [ {
    "emailAddress" : "admin@example.com",
    "primary" : true
  } ],
  "roleNames" : [ ],
  "password" : "{bcrypt}$2a$04$2/u284AalYZQuuhVnqqBGuGsnT.7lvw7CtgjIb1GFzQ0bEaxUVNQW"
}

Update the image of an existing User

To set the image of an existing User a client needs to send a PATCH request along the image in the UserDetails.

POST /users/96baa849-dd19-4b19-8c5e-895d3b7f405e/details/image HTTP/1.1
Content-Type: application/octet-stream
Content-Length: 18572
Host: localhost:8080

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

If the server has correctly updated the Users image the response looks like:

HTTP/1.1 200 OK

Delete an User

To finally delete all User data a DELETE request with the persistent key of the User is required:

DELETE /users/96baa849-dd19-4b19-8c5e-895d3b7f405e HTTP/1.1
Host: localhost:8080

If the User is not assigned somewhere else (in Roles) and it has been deleted successfully the server responds:

HTTP/1.1 204 No Content

Role

An User could be assigned to multiple Roles and act on behalf of a Role. Additionally a Role assigns Grants to all the Users that are assigned to this Role. By doing so, a Role is also a group of Grants that can be assigned to multiple Users.

Role Index

An overview of all possible operations on Roles can be found on the Role index page that is retrieved with a GET request:

GET /roles/index HTTP/1.1
Host: localhost:8080

And the server responds with a HAL+JSON representation to further operations:

HTTP/1.1 200 OK
Content-Type: application/hal+json
Content-Length: 806

{
  "_links" : {
    "roles-findbypkey" : {
      "href" : "http://localhost:8080/roles/pKey"
    },
    "roles-findall" : {
      "href" : "http://localhost:8080/roles"
    },
    "roles-findusersofrole" : {
      "href" : "http://localhost:8080/roles/pKey/users"
    },
    "roles-findgrantsofrole" : {
      "href" : "http://localhost:8080/roles/pKey/grants"
    },
    "roles-create" : {
      "href" : "http://localhost:8080/roles"
    },
    "roles-save" : {
      "href" : "http://localhost:8080/roles/pKey"
    },
    "roles-delete" : {
      "href" : "http://localhost:8080/roles/pKey"
    },
    "roles-assignuser" : {
      "href" : "http://localhost:8080/roles/pKey/users/userPKey"
    },
    "roles-unassignuser" : {
      "href" : "http://localhost:8080/roles/pKey/users/userPKey"
    }
  }
}

Create a Role

To create a new Role instance, a POST request must be send to the server with the mandatory fields of the Role resource in the request body.

POST /roles HTTP/1.1
Content-Type: application/json
Content-Length: 139
Host: localhost:8080

{
  "@class" : "org.openwms.core.uaa.api.RoleVO",
  "ol" : 0,
  "name" : "ROLE_DEV",
  "description" : "Developers",
  "immutable" : true
}
Path Type Description

ol

Number

Technical versioning field to check the instance version

name

String

Unique name of the Role

immutable

Boolean

Whether or not this Role is immutable. Immutable Roles can’t be modified

description

String

A descriptive text for the Role

If the Role has been created successfully, the server returns the URI to the created resource in the Location header:

HTTP/1.1 201 Created
Location: http://localhost:8080/roles/1236acae-356d-4e6e-ba06-c8b0f69e13d7
Content-Type: application/vnd.openwms.uaa.role-v1+json
Content-Length: 548

{
  "@class" : "org.openwms.core.uaa.api.RoleVO",
  "_links" : {
    "users" : {
      "href" : "http://localhost:8080/roles/1236acae-356d-4e6e-ba06-c8b0f69e13d7/users"
    },
    "grants" : {
      "href" : "http://localhost:8080/roles/1236acae-356d-4e6e-ba06-c8b0f69e13d7/grants"
    },
    "role-findbypkey" : {
      "href" : "http://localhost:8080/roles/1236acae-356d-4e6e-ba06-c8b0f69e13d7"
    }
  },
  "ol" : 0,
  "pKey" : "1236acae-356d-4e6e-ba06-c8b0f69e13d7",
  "name" : "ROLE_DEV",
  "description" : "Developers",
  "immutable" : true
}

If a Role with the same name already exists the server returns an error:

HTTP/1.1 409 Conflict
Content-Type: application/hal+json
Content-Length: 121

{
  "message" : "Role with name [ROLE_ADMIN] already exists",
  "messageKey" : "already.exists",
  "httpStatus" : "409"
}

Find all Roles

To find and retrieve an array of all existing Roles a role may call a GET request:

GET /roles HTTP/1.1
Host: localhost:8080

and returns either an array of Roles or an empty array, but always a 200-OK.

HTTP/1.1 200 OK
Content-Type: application/vnd.openwms.uaa.role-v1+json
Content-Length: 844

[ {
  "@class" : "org.openwms.core.uaa.api.RoleVO",
  "links" : [ {
    "rel" : "users",
    "href" : "http://localhost:8080/roles/1/users"
  }, {
    "rel" : "grants",
    "href" : "http://localhost:8080/roles/1/grants"
  }, {
    "rel" : "role-findbypkey",
    "href" : "http://localhost:8080/roles/1"
  } ],
  "ol" : 1,
  "pKey" : "1",
  "name" : "ROLE_ADMIN",
  "description" : "Super user role",
  "immutable" : true
}, {
  "@class" : "org.openwms.core.uaa.api.RoleVO",
  "links" : [ {
    "rel" : "users",
    "href" : "http://localhost:8080/roles/2/users"
  }, {
    "rel" : "grants",
    "href" : "http://localhost:8080/roles/2/grants"
  }, {
    "rel" : "role-findbypkey",
    "href" : "http://localhost:8080/roles/2"
  } ],
  "ol" : 1,
  "pKey" : "2",
  "name" : "ROLE_OPS",
  "description" : "Operator role",
  "immutable" : true
} ]

Find a Role by persistent key

Each Role has an unique ID the pKey or persistent identifier. To find and return a Role by pKey a client must send a GET request to the Roles resource:

GET /roles/1 HTTP/1.1
Host: localhost:8080
HTTP/1.1 200 OK
Content-Type: application/vnd.openwms.uaa.role-v1+json
Content-Length: 415

{
  "@class" : "org.openwms.core.uaa.api.RoleVO",
  "_links" : {
    "users" : {
      "href" : "http://localhost:8080/roles/1/users"
    },
    "grants" : {
      "href" : "http://localhost:8080/roles/1/grants"
    },
    "role-findbypkey" : {
      "href" : "http://localhost:8080/roles/1"
    }
  },
  "ol" : 1,
  "pKey" : "1",
  "name" : "ROLE_ADMIN",
  "description" : "Super user role",
  "immutable" : true
}

Find all Users that are assigned to a Role

A client might ask for all Users that are assigned to a Role resource. Therefore the client needs to query all users of the primary Role:

GET /roles/1/users HTTP/1.1
Host: localhost:8080

and get an array of Users back, that could also be empty:

HTTP/1.1 200 OK
Content-Type: application/vnd.openwms.uaa.user-v1+json
Content-Length: 779

[ {
  "links" : [ ],
  "ol" : 1,
  "createDt" : "2020-06-22T19:02:47.404000000Z",
  "lastModifiedDt" : "2025-02-04T23:09:49.366583000Z",
  "pKey" : "96baa849-dd19-4b19-8c5e-895d3b7f405d",
  "username" : "jenkins",
  "externalUser" : true,
  "lastPasswordChange" : "2020-06-22T19:02:47Z",
  "locked" : true,
  "enabled" : true,
  "expirationDate" : "2020-06-23T19:02:45Z",
  "fullname" : "Mister Jenkins",
  "details" : { },
  "emailAddresses" : [ {
    "emailAddress" : "admin.private@acme.com",
    "primary" : true,
    "fullname" : "Mr. Jenkins"
  }, {
    "emailAddress" : "admin@acme.com",
    "primary" : false,
    "fullname" : "Mr. Jenkins"
  } ],
  "roleNames" : [ "ROLE_ADMIN" ],
  "password" : "{bcrypt}$2a$15$baURCfRsoxem.eOv0IJDsup.9wEmHdiw.j8f0RaMflDbFnQWNipvG"
} ]

Find all Grants that belong to a Role

A client might ask for all Grants that are assigned to a Role resource. Therefore the client needs to query all grants of the primary Role:

GET /roles/1/grants HTTP/1.1
Host: localhost:8080

and get an array of Grants back, that could also be empty:

HTTP/1.1 200 OK
Content-Type: application/vnd.openwms.uaa.security-object-v1+json
Content-Length: 1104

[ {
  "@class" : "org.openwms.core.uaa.api.SecurityObjectVO",
  "ol" : 1,
  "createDt" : "2025-02-04T23:09:49.618254000Z",
  "lastModifiedDt" : "2025-02-04T23:09:49.618254000Z",
  "pKey" : "5",
  "name" : "SEC_UAA_USER_MODIFY",
  "description" : "Permission to modify Users"
}, {
  "@class" : "org.openwms.core.uaa.api.SecurityObjectVO",
  "ol" : 1,
  "createDt" : "2025-02-04T23:09:49.618254000Z",
  "lastModifiedDt" : "2025-02-04T23:09:49.618254000Z",
  "pKey" : "4",
  "name" : "SEC_UAA_USER_CREATE",
  "description" : "Permission to create Users"
}, {
  "@class" : "org.openwms.core.uaa.api.SecurityObjectVO",
  "ol" : 1,
  "createDt" : "2025-02-04T23:09:49.618254000Z",
  "lastModifiedDt" : "2025-02-04T23:09:49.618254000Z",
  "pKey" : "3",
  "name" : "SEC_UAA_USER_LOOKUP",
  "description" : "Permission to find Users"
}, {
  "@class" : "org.openwms.core.uaa.api.SecurityObjectVO",
  "ol" : 1,
  "createDt" : "2025-02-04T23:09:49.618254000Z",
  "lastModifiedDt" : "2025-02-04T23:09:49.618254000Z",
  "pKey" : "6",
  "name" : "SEC_UAA_USER_DELETE",
  "description" : "Permission to delete Users"
} ]

Modify an existing Role

An existing Role instance can be modified by sending a PUT request with the Role representation as request body. At least the name of the Role, as an identifying attribute must be set.

PUT /roles/1 HTTP/1.1
Content-Type: application/json
Content-Length: 167
Host: localhost:8080

{
  "@class" : "org.openwms.core.uaa.api.RoleVO",
  "ol" : 0,
  "pKey" : "1",
  "name" : "ROLE_SUPER",
  "description" : "Administrators role",
  "immutable" : false
}
Path Type Description

pKey

String

The persistent key must be passed when modifying an existing instance

ol

Number

Technical versioning field to check the instance version

name

String

The name as an identifying attribute of the existing Role, cannot be changed

description

String

A description text to update

immutable

Boolean

Whether the Role is immutable or not

If the server has correctly updated the Role the response contains the updated representation:

HTTP/1.1 200 OK
Content-Type: application/vnd.openwms.uaa.role-v1+json
Content-Length: 419

{
  "@class" : "org.openwms.core.uaa.api.RoleVO",
  "_links" : {
    "users" : {
      "href" : "http://localhost:8080/roles/1/users"
    },
    "grants" : {
      "href" : "http://localhost:8080/roles/1/grants"
    },
    "role-findbypkey" : {
      "href" : "http://localhost:8080/roles/1"
    }
  },
  "ol" : 1,
  "pKey" : "1",
  "name" : "ROLE_SUPER",
  "description" : "Administrators role",
  "immutable" : true
}

If the name of the Role to update is missing, the server responds with an error:

HTTP/1.1 400 Bad Request
Content-Type: application/hal+json
Content-Length: 143

{
  "message" : "Error on validating the input parameters [save.role.name]",
  "messageKey" : "core.validation.error",
  "httpStatus" : "400"
}

Assign an User to a Role

Users can be assigned to one or more Roles. To assign an existing User to an existing Role the client must send a POST request to the primary Role resource:

POST /roles/1/users/96baa849-dd19-4b19-8c5e-895d3b7f405e HTTP/1.1
Host: localhost:8080
Content-Type: application/x-www-form-urlencoded

If the User has been assigned successfully, the server responds with 200-OK:

HTTP/1.1 200 OK
Content-Type: application/vnd.openwms.uaa.role-v1+json
Content-Length: 415

{
  "@class" : "org.openwms.core.uaa.api.RoleVO",
  "_links" : {
    "users" : {
      "href" : "http://localhost:8080/roles/1/users"
    },
    "grants" : {
      "href" : "http://localhost:8080/roles/1/grants"
    },
    "role-findbypkey" : {
      "href" : "http://localhost:8080/roles/1"
    }
  },
  "ol" : 1,
  "pKey" : "1",
  "name" : "ROLE_ADMIN",
  "description" : "Super user role",
  "immutable" : true
}

If the User or the Role do not exist the server responds with an error:

HTTP/1.1 404 Not Found
Content-Type: application/hal+json
Content-Length: 166

{
  "message" : "User with ID UNKNOWN does not exist",
  "messageKey" : "user.pkey.not.exist",
  "obj" : [ "UNKNOWN" ],
  "httpStatus" : "404",
  "class" : "String"
}

Unassign an User from a Role

Already assigned Users can be unassigned from a Role. Therefore the client sends a DELETE request on the secondary User resource of the primary Role resource. Note: This will not delete the User but delete the assignment between the Role and the User.

DELETE /roles/1/users/96baa849-dd19-4b19-8c5e-895d3b7f405d HTTP/1.1
Host: localhost:8080

If the User has been unassigned successfully, the server responds with 200-OK:

HTTP/1.1 200 OK
Content-Type: application/vnd.openwms.uaa.role-v1+json
Content-Length: 415

{
  "@class" : "org.openwms.core.uaa.api.RoleVO",
  "_links" : {
    "users" : {
      "href" : "http://localhost:8080/roles/1/users"
    },
    "grants" : {
      "href" : "http://localhost:8080/roles/1/grants"
    },
    "role-findbypkey" : {
      "href" : "http://localhost:8080/roles/1"
    }
  },
  "ol" : 1,
  "pKey" : "1",
  "name" : "ROLE_ADMIN",
  "description" : "Super user role",
  "immutable" : true
}

If the User or the Role do not exist the server responds with an error:

HTTP/1.1 404 Not Found
Content-Type: application/hal+json
Content-Length: 166

{
  "message" : "Role with ID UNKNOWN does not exist",
  "messageKey" : "role.pkey.not.exist",
  "obj" : [ "UNKNOWN" ],
  "httpStatus" : "404",
  "class" : "String"
}

Delete a Role

To finally delete all Role data, a DELETE request with the persistent key of the Role is required:

DELETE /roles/1 HTTP/1.1
Host: localhost:8080

If the Role has been deleted successfully the server responds:

HTTP/1.1 204 No Content

Grant

A Grant represents a permission to perform an action. A Grant can be assigned to a Role and is always tied to some kind of action that can be taken. It has a unique business key, that is used in API or UI to protect against unauthorized access.

Grant Index

An overview of all possible operations on Grants can be found on the Grant index page that is retrieved with a GET request:

GET /grants/index HTTP/1.1
Host: localhost:8080

And the server responds with a HAL+JSON representation to further operations:

HTTP/1.1 200 OK
Content-Type: application/hal+json
Content-Length: 343

{
  "_links" : {
    "grant-findbypkey" : {
      "href" : "http://localhost:8080/grants/pKey"
    },
    "grant-findall" : {
      "href" : "http://localhost:8080/grants"
    },
    "grant-findallforuser" : {
      "href" : "http://localhost:8080/grants"
    },
    "grant-create" : {
      "href" : "http://localhost:8080/grants"
    }
  }
}

Create a Grant

To create a new Grant instance, a POST request must be send to the server with the mandatory fields of the Grant resource in the request body.

POST /grants HTTP/1.1
Content-Type: application/json
Content-Length: 132
Host: localhost:8080

{
  "@class" : "org.openwms.core.uaa.api.GrantVO",
  "name" : "VIEW_USERS",
  "description" : "Permission to view all users in UI"
}

Not named properties are optional to pass.

Path Type Description

name

String

Unique name of the Grant

description

String

(Optional) A descriptive text for the Grant

If the Grant has been created successfully, the server returns the URI to the created resource in the Location header:

HTTP/1.1 201 Created
Location: http://localhost:8080/grants/b423c558-bc99-4328-8dc6-328d5531e2b3
Content-Type: application/vnd.openwms.uaa.grant-v1+json
Content-Length: 435

{
  "@class" : "org.openwms.core.uaa.api.GrantVO",
  "_links" : {
    "grant-findbypkey" : {
      "href" : "http://localhost:8080/grants/b423c558-bc99-4328-8dc6-328d5531e2b3"
    }
  },
  "ol" : 0,
  "createDt" : "2025-02-04T23:10:00.244818148Z",
  "lastModifiedDt" : "2025-02-04T23:10:00.244818148Z",
  "pKey" : "b423c558-bc99-4328-8dc6-328d5531e2b3",
  "name" : "VIEW_USERS",
  "description" : "Permission to view all users in UI"
}

If a Grant with the same name already exists the server returns an error:

HTTP/1.1 409 Conflict
Content-Type: application/hal+json
Content-Length: 140

{
  "message" : "Grant with name SEC_UAA_USER_LOOKUP already exists",
  "messageKey" : "grant.name.already.exists",
  "httpStatus" : "409"
}

Find all Grants

To retrieve all existing Grants from the server, a client simply needs to do a GET request to the Grants resource.

GET /grants HTTP/1.1
Host: localhost:8080

The server responds with a list representation of all Grants or an empty list:

HTTP/1.1 200 OK
Content-Type: application/vnd.openwms.uaa.grant-v1+json
Content-Length: 1472

[ {
  "@class" : "org.openwms.core.uaa.api.GrantVO",
  "links" : [ {
    "rel" : "grant-findbypkey",
    "href" : "http://localhost:8080/grants/3"
  } ],
  "ol" : 1,
  "createDt" : "2025-02-04T23:10:00.287469000Z",
  "lastModifiedDt" : "2025-02-04T23:10:00.287469000Z",
  "pKey" : "3",
  "name" : "SEC_UAA_USER_LOOKUP",
  "description" : "Permission to find Users"
}, {
  "@class" : "org.openwms.core.uaa.api.GrantVO",
  "links" : [ {
    "rel" : "grant-findbypkey",
    "href" : "http://localhost:8080/grants/4"
  } ],
  "ol" : 1,
  "createDt" : "2025-02-04T23:10:00.287469000Z",
  "lastModifiedDt" : "2025-02-04T23:10:00.287469000Z",
  "pKey" : "4",
  "name" : "SEC_UAA_USER_CREATE",
  "description" : "Permission to create Users"
}, {
  "@class" : "org.openwms.core.uaa.api.GrantVO",
  "links" : [ {
    "rel" : "grant-findbypkey",
    "href" : "http://localhost:8080/grants/5"
  } ],
  "ol" : 1,
  "createDt" : "2025-02-04T23:10:00.287469000Z",
  "lastModifiedDt" : "2025-02-04T23:10:00.287469000Z",
  "pKey" : "5",
  "name" : "SEC_UAA_USER_MODIFY",
  "description" : "Permission to modify Users"
}, {
  "@class" : "org.openwms.core.uaa.api.GrantVO",
  "links" : [ {
    "rel" : "grant-findbypkey",
    "href" : "http://localhost:8080/grants/6"
  } ],
  "ol" : 1,
  "createDt" : "2025-02-04T23:10:00.287469000Z",
  "lastModifiedDt" : "2025-02-04T23:10:00.287469000Z",
  "pKey" : "6",
  "name" : "SEC_UAA_USER_DELETE",
  "description" : "Permission to delete Users"
} ]

Find a Grant by persistent key

Each Grant has an unique ID the pKey or persistent identifier. To find and return a Grant by pKey a client must send a GET request to the Grants resource:

GET /grants/3 HTTP/1.1
Host: localhost:8080
HTTP/1.1 200 OK
Content-Type: application/vnd.openwms.uaa.grant-v1+json
Content-Length: 364

{
  "@class" : "org.openwms.core.uaa.api.GrantVO",
  "_links" : {
    "grant-findbypkey" : {
      "href" : "http://localhost:8080/grants/3"
    }
  },
  "ol" : 1,
  "createDt" : "2025-02-04T23:10:00.342152000Z",
  "lastModifiedDt" : "2025-02-04T23:10:00.342152000Z",
  "pKey" : "3",
  "name" : "SEC_UAA_USER_LOOKUP",
  "description" : "Permission to find Users"
}
Path Type Description

@class

String

Type identifier of the Grant type

ol

Number

Technical versioning field to check the instance version

pKey

String

The technical persistent identifier of the Grant

createDt

String

When the record has been created

lastModifiedDt

String

Timestamp when the record has been updated the last time

name

String

Unique name of the Grant

description

String

A descriptive text for the Grant

Find all Grants of a User

To retrieve all existing Grants from assigned to a User, a client simply needs to do a GET request to the Grants resource and pass the users identity within the X-Identity http header.

GET /grants HTTP/1.1
X-Identity: jenkins
Host: localhost:8080

The server responds with a list representation of all Grants:

HTTP/1.1 200 OK
Content-Type: application/vnd.openwms.uaa.grant-v1+json
Content-Length: 1472

[ {
  "@class" : "org.openwms.core.uaa.api.GrantVO",
  "links" : [ {
    "rel" : "grant-findbypkey",
    "href" : "http://localhost:8080/grants/5"
  } ],
  "ol" : 1,
  "createDt" : "2025-02-04T23:10:00.379610000Z",
  "lastModifiedDt" : "2025-02-04T23:10:00.379610000Z",
  "pKey" : "5",
  "name" : "SEC_UAA_USER_MODIFY",
  "description" : "Permission to modify Users"
}, {
  "@class" : "org.openwms.core.uaa.api.GrantVO",
  "links" : [ {
    "rel" : "grant-findbypkey",
    "href" : "http://localhost:8080/grants/4"
  } ],
  "ol" : 1,
  "createDt" : "2025-02-04T23:10:00.379610000Z",
  "lastModifiedDt" : "2025-02-04T23:10:00.379610000Z",
  "pKey" : "4",
  "name" : "SEC_UAA_USER_CREATE",
  "description" : "Permission to create Users"
}, {
  "@class" : "org.openwms.core.uaa.api.GrantVO",
  "links" : [ {
    "rel" : "grant-findbypkey",
    "href" : "http://localhost:8080/grants/3"
  } ],
  "ol" : 1,
  "createDt" : "2025-02-04T23:10:00.379610000Z",
  "lastModifiedDt" : "2025-02-04T23:10:00.379610000Z",
  "pKey" : "3",
  "name" : "SEC_UAA_USER_LOOKUP",
  "description" : "Permission to find Users"
}, {
  "@class" : "org.openwms.core.uaa.api.GrantVO",
  "links" : [ {
    "rel" : "grant-findbypkey",
    "href" : "http://localhost:8080/grants/6"
  } ],
  "ol" : 1,
  "createDt" : "2025-02-04T23:10:00.379610000Z",
  "lastModifiedDt" : "2025-02-04T23:10:00.379610000Z",
  "pKey" : "6",
  "name" : "SEC_UAA_USER_DELETE",
  "description" : "Permission to delete Users"
} ]

or a HTTP 204-NO CONTENT response if no Grants are assigned to the User:

HTTP/1.1 204 No Content

or a HTTP 404-NOT FOUND response if the User does not exist:

HTTP/1.1 404 Not Found
Content-Type: application/hal+json
Content-Length: 121

{
  "message" : "User with name UNKNOWN does not exist",
  "messageKey" : "user.name.not.exist",
  "httpStatus" : "404"
}