7.7. UDM HTTP REST API#
UCS provides a UDM HTTP REST API which can be used to inspect, modify, create and delete UDM objects through HTTP requests.
The API is accessible from https://FQHN/univention/udm/
.
See also
For an architectural overview, see UDM HTTP REST API in Univention Corporate Server 5.0 Architecture [3].
7.7.1. Authentication#
To use the API you have to authenticate with a user account which is a member of
an authorized group. The group authorization is managed through the Univention Configuration Registry Variables
directory/manager/rest/authorized-groups/
.
You can authenticate through the following ways:
With user credentials through HTTP basic authentication
An OAuth 2.0 Access Token as JWT through HTTP Bearer authentication
The API comes predefined with the following UCR variables:
directory/manager/rest/authorized-groups/domain-admins
directory/manager/rest/authorized-groups/dc-backup
directory/manager/rest/authorized-groups/dc-slaves
The variables authorize the groups Domain Admins
, DC Backup Hosts
and
DC Slave Hosts
respectively.
To authorize additional groups you just have to create a new UCR variable. If you haven’t already, create the group you want to authorize:
$ udm groups/group create \
--position="cn=groups,$(ucr get ldap/base)" \
--set name="UDM API Users"
Now set the UCR variable to allow the group members to use the API.
$ ucr set directory/manager/rest/authorized-groups/udm-api-users=\
"cn=UDM API Users,cn=groups,$(ucr get ldap/base)"
Note
The authorization of a group only allows the group members to access the API
in the first place. After that, which actions the user can perform with the
API is regulated through ACLs. For example a normal Domain Users
user can’t
create or delete objects.
After you add or modify a Univention Configuration Registry Variable
directory/manager/rest/authorized-groups/
you have to
restart the API service for the changes to take effect.
$ systemctl restart univention-directory-manager-rest
7.7.2. API overview#
You can interact with the API by sending HTTP requests to resources and by using different HTTP methods you can achieve different results.
Verb |
Description |
---|---|
GET |
Retrieve a resource |
POST |
Create a resource |
PUT |
Replace or move a resource |
PATCH |
Modify or move a resource |
DELETE |
Delete a resource |
For an in depth overview over which resources are available, which HTTP methods
are allowed on them and which query parameters are available for a given HTTP
method visit https://FQHN/univention/udm/schema/
with a browser.
To download the OpenAPI schema, use https://FQHN/univention/udm/openapi.json
.
The contract is that the client must always use the latest schema.
You can navigate the OpenAPI schema interactively with a web browser. To enable it, use the following steps:
You need to set the UCR variable
directory/manager/rest/html-view-enabled
totrue
.If you need to insert JSON blobs of objects into the HTML source code, enable it by setting
directory/manager/rest/debug-mode-enabled
totrue
.Restart the UDM HTTP REST API with this command:
$ systemctl restart univention-directory-manager-rest
Finally, visit
https://FQHN/univention/udm/
.
7.7.3. API clients#
The following API clients implemented in Python exist for the UDM HTTP REST API:
python3-univention-directory-mananger-rest-client:
Every UCS system has it installed by default. You can use it the following way:
from univention.admin.rest.client import UDM uri = 'https://ucs-primary.example.com/univention/udm/' udm = UDM.http(uri, 'Administrator', 'univention') module = udm.get('users/user') # 1. create a user obj = module.new() obj.properties['username'] = 'foo' obj.properties['password'] = 'univention' obj.properties['lastname'] = 'foo' obj.save() # 2. search for users (first user) obj = next(module.search('uid=*')) if obj: obj = obj.open() print('Object {}'.format(obj)) # 3. get by dn ldap_base = udm.get_ldap_base() obj = module.get('uid=foo,cn=users,%s' % (ldap_base,)) # 4. get referenced objects e.g. groups pg = obj.objects['primaryGroup'].open() print(pg.dn, pg.properties) print(obj.objects['groups']) # 5. modify obj.properties['description'] = 'foo' obj.save() # 6. move to the ldap base obj.move(ldap_base) # 7. remove obj.delete()
python3-univention-directory-mananger-rest-async-client:
After installing the Debian package on a UCS system, you can use it in the following way:
import asyncio from univention.admin.rest.async_client import UDM uri = 'https://ucs-primary.example.com/univention/udm/' async def main(): async with UDM.http(uri, 'Administrator', 'univention') as udm: module = await udm.get('users/user') # 1. create a user obj = await module.new() obj.properties['username'] = 'foo' obj.properties['password'] = 'univention' obj.properties['lastname'] = 'foo' await obj.save() # 2. search for users (first user) objs = module.search() async for obj in objs: if not obj: continue obj = await obj.open() print('Object {}'.format(obj)) # 3. get by dn ldap_base = await udm.get_ldap_base() obj = await module.get('uid=foo,cn=users,%s' % (ldap_base,)) # 4. get referenced objects e.g. groups pg = await obj.objects['primaryGroup'].open() print(pg.dn, pg.properties) print(obj.objects['groups']) # 5. modify obj.properties['description'] = 'foo' await obj.save() # 6. move to the ldap base await obj.move(ldap_base) # 7. remove await obj.delete()
Python UDM HTTP REST API Client:
7.7.4. API usage examples#
In the following section you will learn how to create, modify, search and delete a user through the API.
While you try out these examples you will often see the “_links” and “_embedded” properties in the responses. These properties are defined by HAL, the Hypertext Application Language, which is used in the API. These properties contain links which can be used to traverse the API. For example the “_links” property of the response to a paginated query could contain the “next” property which points to the next page.
For more information on HAL please refer to the Internet Draft for HAL.
7.7.4.1. Create a user with a POST request#
To create a new user your first want to get a template that describes a valid user and has all attributes filled out with default values.
You can get the template for an UDM module with:
$ curl -X GET -H "Accept: application/json" \
https://${USER}:${PASSWORD}@${FQHN}/univention/udm/${module}/add
So for the users/user module you get the template with:
$ curl -X GET -H "Accept: application/json" \
https://${USER}:${PASSWORD}@${FQHN}/univention/udm/users/user/add
To work with the template, you can save it into a file. To make it
more readable, you can use something like Pythons
json.tool
.
$ curl -X GET -H "Accept: application/json" \
https://${USER}:${PASSWORD}@${FQHN}/univention/udm/users/user/add | \
python3 -m json.tool > user_template.json
The JSON file contains meta information (keys that start with underscore
‘_
’) that aren’t necessary to create a user. These can be filtered out to
make it easier to work with the template file. The following example produces
such a condensed template:
$ curl -X GET -H "Accept: application/json" \
https://${USER}:${PASSWORD}@${FQHN}/univention/udm/users/user/add | \
python3 -c 'import sys, json; \
template = json.load(sys.stdin); \
template = {key:value for key, value in template.items() if not key.startswith("_")}; \
json.dump(template, sys.stdout, indent=4)' > user_template.json
The content of user_template.json
should look something like this.
{
"position": "cn=users,dc=mydomain,dc=intranet",
"objectType": "users/user",
"options": {
"pki": false
},
"policies": {
"policies/pwhistory": [],
"policies/umc": [],
"policies/desktop": []
},
"properties": {
"mobileTelephoneNumber": [],
"certificateSubjectOrganisationalUnit": null,
"groups": [],
"sambahome": null,
"departmentNumber": [],
"certificateSerial": null,
"certificateSubjectCommonName": null,
"primaryGroup": "cn=Domain Users,cn=groups,dc=mydomain,dc=intranet",
"uidNumber": null,
"disabled": false,
"unlock": false,
"street": null,
"postcode": null,
"scriptpath": null,
"sambaPrivileges": [],
"description": null,
"certificateIssuerCommonName": null,
"mailForwardCopyToSelf": false,
"employeeType": null,
"homedrive": null,
"overridePWLength": null,
"title": null,
"mailAlternativeAddress": [],
"userCertificate": null,
"organisation": null,
"homeSharePath": "",
"certificateIssuerOrganisationalUnit": null,
"e-mail": [],
"userexpiry": null,
"pwdChangeNextLogin": false,
"mailHomeServer": null,
"unixhome": "/home/",
"gecos": "",
"sambaUserWorkstations": [],
"preferredLanguage": null,
"certificateIssuerState": null,
"pagerTelephoneNumber": [],
"username": null,
"umcProperty": [],
"certificateIssuerCountry": null,
"homeTelephoneNumber": [],
"shell": "/bin/bash",
"homePostalAddress": [],
"firstname": null,
"certificateIssuerOrganisation": null,
"lastname": null,
"city": null,
"certificateSubjectMail": null,
"mailForwardAddress": [],
"phone": [],
"gidNumber": null,
"birthday": null,
"employeeNumber": null,
"objectFlag": [],
"sambaLogonHours": null,
"certificateSubjectLocation": null,
"displayName": "",
"password": null,
"lockedTime": null,
"sambaRID": null,
"secretary": [],
"certificateSubjectOrganisation": null,
"overridePWHistory": null,
"mailPrimaryAddress": null,
"country": null,
"roomNumber": [],
"certificateSubjectCountry": null,
"locked": false,
"certificateDateNotBefore": null,
"passwordexpiry": null,
"certificateVersion": null,
"homeShare": null,
"certificateIssuerMail": null,
"unlockTime": null,
"serviceprovider": [],
"profilepath": null,
"certificateIssuerLocation": null,
"jpegPhoto": null,
"certificateDateNotAfter": null,
"certificateSubjectState": null
}
}
Now you can modify the attributes the new user should have and send the modified template, through a POST request, to create a new user.
$ curl -X POST -H "Accept: application/json" -H "Content-Type: application/json" \
https://${USER}:${PASSWORD}@${FQHN}/univention/udm/users/user/ --data @user_template.json
7.7.4.2. Search for users with a GET request#
In this example you search for a users/user object where the property
firstname
starts with "Ale"
and the property lastname
ends with
"er"
.
$ curl -X GET -H "Accept: application/json" \
"http://${USER}:${PASSWORD}@${FQHN}/univention/udm/users/user/?query\[firstname\]=Al%2A&query\[lastname\]=%2Aer"
The response should look something like this (some fields where omitted for clarity):
{
"_embedded": {
"udm:object": [
{
"dn": "uid=alexpower,cn=users,dc=mydomain,dc=intranet",
"id": "alexpower",
"objectType": "users/user",
"options": {
"pki": false
},
"policies": {
"policies/desktop": [],
"policies/pwhistory": [],
"policies/umc": []
},
"position": "cn=users,dc=mydomain,dc=intranet",
"properties": {
"birthday": null,
"city": null,
"country": null,
"departmentNumber": [],
"description": null,
"disabled": false,
"displayName": "Alex Power",
"e-mail": [],
"employeeNumber": null,
"employeeType": null,
"firstname": "Alex",
"gecos": "Alex Power",
"gidNumber": 5001,
"groups": [
"cn=Domain Users,cn=groups,dc=mydomain,dc=intranet"
],
"homePostalAddress": [],
"homeShare": null,
"homeSharePath": "alexpower",
"homeTelephoneNumber": [],
"homedrive": null,
"jpegPhoto": null,
"lastname": "Power",
"locked": false,
"lockedTime": "0",
"mailAlternativeAddress": [],
"mailForwardAddress": [],
"mailForwardCopyToSelf": "0",
"mailHomeServer": null,
"mailPrimaryAddress": null,
"mobileTelephoneNumber": [],
"objectFlag": [],
"organisation": null,
"overridePWHistory": null,
"overridePWLength": null,
"pagerTelephoneNumber": [],
"password": null,
"passwordexpiry": null,
"phone": [],
"postcode": null,
"preferredLanguage": null,
"primaryGroup": "cn=Domain Users,cn=groups,dc=mydomain,dc=intranet",
"profilepath": null,
"pwdChangeNextLogin": false,
"roomNumber": [],
"sambaLogonHours": null,
"sambaPrivileges": [],
"sambaRID": 5018,
"sambaUserWorkstations": [],
"sambahome": null,
"scriptpath": null,
"secretary": [],
"serviceprovider": [],
"shell": "/bin/bash",
"street": null,
"title": null,
"uidNumber": 2009,
"umcProperty": {},
"unixhome": "/home/alexpower",
"unlock": false,
"unlockTime": "",
"userexpiry": null,
"username": "alexpower"
},
"uri": "http://10.200.28.110/univention/udm/users/user/uid%3Dalexpower%2Ccn%3Dusers%2Cdc%3Dmydomain%2Cdc%3Dintranet"
}
]
},
"results": 1
}
7.7.4.3. Modify a user with a PUT request#
To modify a user you first get the current state of the user. To prevent modification conflicts you also have to get the entity tag (Etag) of the user resource. The Etag can be found in the response headers; it is used to identify a specific version of a resource.
$ curl -X GET -H "Accept: application/json" --dump-header user.headers \
https://${USER}:${PASSWORD}@${FQHN}/univention/udm/users/user/<dn> \
| python3 -m json.tool > user.json
Caution
You must URL encode <dn>
.
Now you can edit the user in the user.json
file to your liking. After
you are done, send the changed user.json
through a PUT
request to modify the user. To avoid modification conflicts it is required to
send the value of the Etag header, which you saved earlier in the
user.headers
file, as the value for the If-Match
header.
$ curl -X PUT -H "Accept: application/json" \
-H "Content-Type: application/json" \
-H 'If-Match: "<Etag>"' \
"https://${USER}:${PASSWORD}@${FQHN}/univention/udm/users/user/<dn>" --data @user.json
Caution
You must URL encode <dn>
.
The quotes around the Etag are required.
7.7.4.4. Delete a user with a DELETE request#
To delete a user you just have to send a DELETE request to
/univention/udm/users/user/<dn>
. Optionally, you can provide
an If-Match
header, similar to the PUT method described
above, to ensure the deletion is conditional.
$ curl -X DELETE -H "Accept: application/json" \
-H 'If-Match: "<Etag>" \
'https://${USER}:${PASSWORD}@${FQHN}/univention/udm/users/user/<dn>
Caution
You must URL encode <dn>
.
7.7.5. API Error Codes#
The UDM HTTP REST API can respond to requests with the following error codes. The list is not exhaustive:
Code |
Name |
Example Case |
---|---|---|
400 |
Bad Request |
The API doesn’t understand the format of the request. |
401 |
Unauthorized |
The request provide no or wrong credentials for authorization. |
403 |
Forbidden |
User isn’t part of the allowed groups to access the requested resource. |
404 |
Not Found |
The requested resource doesn’t exist. |
406 |
Not Acceptable |
The header field |
412 |
Precondition Failed |
The header |
413 |
Payload Too Large |
The request payload contains a field that exceeds its size limit. |
416 |
Range Not Satisfiable |
In the request, the field |
422 |
Unprocessable Content |
The validation of input parameters failed. |
500 |
Internal Server Error |
Generic error code for internal server errors. |
503 |
Service Unavailable |
The server for the service isn’t available, for example the LDAP server. |