Skip to content
This repository was archived by the owner on Sep 10, 2024. It is now read-only.

Commit bf986f6

Browse files
committed
Document the admin API
1 parent 0f2363c commit bf986f6

File tree

4 files changed

+277
-3
lines changed

4 files changed

+277
-3
lines changed

docs/SUMMARY.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Summary
1+
# Summary
22

33
# Introduction
44

@@ -21,11 +21,13 @@
2121

2222
- [Policy engine](./topics/policy.md)
2323
- [Authorization and sessions](./topics/authorization.md)
24+
- [Use the Admin API](./topics/admin-api.md)
2425

2526
# Reference
2627

2728
- [Configuration file reference](./reference/configuration.md)
2829
- [GraphQL API](./reference/graphql.md)
30+
- [Admin API](./api/index.html)
2931
- [OAuth 2.0 scopes](./reference/scopes.md)
3032
- [Command line tool](./reference/cli/README.md)
3133
- [`config`](./reference/cli/config.md)

docs/reference/configuration.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ http:
4444
# Serve the given folder on the /assets/ path
4545
- name: assets
4646
path: ./share/assets/
47+
# Serve the admin API on the /api/admin/v1/ path. Disabled by default
48+
#- name: adminapi
4749

4850
# List of addresses and ports to listen to
4951
binds:

docs/reference/scopes.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,8 +89,9 @@ The default policy allows any client and any user to request this scope.
8989

9090
### `urn:mas:admin`
9191

92-
This scope allows full access to the MAS [GraphQL API].
93-
It requires the `urn:mas:graphql:*` scope to be present in the request.
92+
This scope grants access to the MAS [Admin API].
93+
94+
Combined with the `urn:mas:graphql:*` scope, it also grants full access to the MAS [GraphQL API].
9495
This allows the authenticated entity to perform any operation on the API, regardless of whether the entity owns the data or not.
9596

9697
The default policy doesn't allow everyone to request this scope.
@@ -105,6 +106,7 @@ It allows:
105106
[authorization code]: ../topics/authorization.md#authorization-code-grant
106107
[device authorization]: ../topics/authorization.md#device-authorization-grant
107108
[GraphQL API]: ./graphql.md
109+
[Admin API]: ../topics/admin-api.md
108110
[Synapse admin API]: https://element-hq.github.io/synapse/latest/usage/administration/admin_api/index.html
109111
[OpenID Connect Core 1.0]: https://openid.net/specs/openid-connect-core-1_0.html
110112
[MSC2967]: https://github.com/matrix-org/matrix-spec-proposals/pull/2967

docs/topics/admin-api.md

Lines changed: 268 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,268 @@
1+
# Admin API
2+
3+
MAS provides a REST-like API for administrators to manage the service.
4+
This API is intended to build tools on top of MAS, and is only available to administrators.
5+
6+
## Enabling the API
7+
8+
The API isn't exposed by default, and must be added to either a public or a private HTTP listener.
9+
It is considered safe to expose the API to the public, as access to it is gated by the `urn:mas:admin` scope.
10+
11+
To enable the API, tweak the [`http.listeners`](../reference/configuration.md#httplisteners) configuration section to add the `adminapi` resource:
12+
13+
```yaml
14+
http:
15+
listeners:
16+
- name: web
17+
resources:
18+
# Other public resources
19+
- name: discovery
20+
#
21+
- name: adminapi
22+
binds:
23+
- address: "[::]:8080"
24+
# or to a separate, internal listener:
25+
- name: internal
26+
resources:
27+
# Other internal resources
28+
- name: health
29+
- name: prometheus
30+
#
31+
- name: adminapi
32+
binds:
33+
- host: localhost
34+
port: 8081
35+
```
36+
37+
## Reference documentation
38+
39+
The API is documented using the [OpenAPI specification](https://spec.openapis.org/oas/v3.1.0).
40+
The API schema is avaialble [here](../api/spec.json).
41+
This schema can be viewed in tools like Swagger UI, available [here](../api/).
42+
43+
If admin API is enabled, MAS will also serve the specification at `/api/spec.json`, with a Swagger UI available at `/api/doc/`.
44+
45+
## Authentication
46+
47+
All requests to the admin API are gated using access tokens obtained using OAuth 2.0 grants.
48+
They must have the [`urn:mas:admin`](../reference/scopes.md#urnmasadmin) scope.
49+
50+
### User-interactive tools
51+
52+
If the intent is to build admin tools where the administrator logs in themselves, interactive grants like the [authorization code] grant or the [device authorization] grant should be used.
53+
54+
In this case, whether the user can request admin access or not is defined by the `can_request_admin` attribute of the user.
55+
56+
To try it out in Swagger UI, a client can be defined statically in the configuration file like this:
57+
58+
```yaml
59+
clients:
60+
- client_id: 01J44Q10GR4AMTFZEEF936DTCM
61+
# For the authorization_code grant, Swagger UI uses the client_secret_post authentication method
62+
client_auth_method: client_secret_post
63+
client_secret: wie9oh2EekeeDeithei9Eipaeh2sohte
64+
redirect_uris:
65+
# The Swagger UI callback in the hosted documentation
66+
- https://matrix-org.github.io/matrix-authentication-service/api/oauth2-redirect.html
67+
# The Swagger UI callback hosted by the service
68+
- https://mas.example.com/api/doc/oauth2-redirect
69+
```
70+
71+
Then, in Swagger UI, click on the "Authorize" button.
72+
In the modal, enter the client ID and client secret **in the `authorizationCode` section**, select the `urn:mas:admin` scope and click on the "Authorize" button.
73+
74+
### Automated tools
75+
76+
If the intent is to build tools that are not meant to be used by humans, the client credentials grant should be used.
77+
78+
In this case, the client must be listed in the [`policy.data.admin_clients`](../reference/configuration.md#policy) configuration option.
79+
80+
```yaml
81+
policy:
82+
data:
83+
admin_clients:
84+
- 01J44QC8BCY7FCFM7WGHQGKMTJ
85+
```
86+
87+
To try it out in Swagger UI, a client can be defined statically in the configuration file like this:
88+
89+
```yaml
90+
clients:
91+
- client_id: 01J44QC8BCY7FCFM7WGHQGKMTJ
92+
# For the client_credentials grant, Swagger UI uses the client_secret_basic authentication method
93+
client_auth_method: client_secret_basic
94+
client_secret: eequie6Oth4Ip2InahT5zuQu8OuPohLi
95+
```
96+
97+
Then, in Swagger UI, click on the "Authorize" button.
98+
In the modal, enter the client ID and client secret **in the `clientCredentials` section**, select the `urn:mas:admin` scope and click on the "Authorize" button.
99+
100+
101+
## General API shape
102+
103+
The API takes inspiration from the [JSON API](https://jsonapi.org/) specification for its request and response shapes.
104+
105+
### Single resource
106+
107+
When querying a single resource, the response is generally shaped like this:
108+
109+
```json
110+
{
111+
"data": {
112+
"type": "type-of-the-resource",
113+
"id": "unique-id-for-the-resource",
114+
"attributes": {
115+
"some-attribute": "some-value"
116+
},
117+
"links": {
118+
"self": "/api/admin/v1/type-of-the-resource/unique-id-for-the-resource"
119+
}
120+
},
121+
"links": {
122+
"self": "/api/admin/v1/type-of-the-resource/unique-id-for-the-resource"
123+
}
124+
}
125+
```
126+
127+
### List of resources
128+
129+
When querying a list of resources, the response is generally shaped like this:
130+
131+
```json
132+
{
133+
"meta": {
134+
"count": 42
135+
},
136+
"data": [
137+
{
138+
"type": "type-of-the-resource",
139+
"id": "unique-id-for-the-resource",
140+
"attributes": {
141+
"some-attribute": "some-value"
142+
},
143+
"links": {
144+
"self": "/api/admin/v1/type-of-the-resource/unique-id-for-the-resource"
145+
}
146+
},
147+
{ "...": "..." },
148+
{ "...": "..." }
149+
],
150+
"links": {
151+
"self": "/api/admin/v1/type-of-the-resource?page[first]=10&page[after]=some-id",
152+
"first": "/api/admin/v1/type-of-the-resource?page[first]=10",
153+
"last": "/api/admin/v1/type-of-the-resource?page[last]=10",
154+
"next": "/api/admin/v1/type-of-the-resource?page[first]=10&page[after]=some-id",
155+
"prev": "/api/admin/v1/type-of-the-resource?page[last]=10&page[before]=some-id"
156+
}
157+
}
158+
```
159+
160+
The `meta` will have the total numer of items in it, and the `links` object contains the links to the next and previous pages, if any.
161+
162+
Pagination is cursor-based, where the ID of items is used as the cursor.
163+
Resources can be paginated forwards using the `page[after]` and `page[first]` parameters, and backwards using the `page[before]` and `page[last]` parameters.
164+
165+
### Error responses
166+
167+
Error responses will use a 4xx or 5xx status code, with the following shape:
168+
169+
```json
170+
{
171+
"errors": [
172+
{
173+
"title": "Error title"
174+
}
175+
]
176+
}
177+
```
178+
179+
Well-known error codes are not yet specified.
180+
181+
## Example
182+
183+
With the following configuration:
184+
185+
```yaml
186+
clients:
187+
- client_id: 01J44RKQYM4G3TNVANTMTDYTX6
188+
client_auth_method: client_secret_basic
189+
client_secret: phoo8ahneir3ohY2eigh4xuu6Oodaewi
190+
191+
policy:
192+
data:
193+
admin_clients:
194+
- 01J44RKQYM4G3TNVANTMTDYTX6
195+
```
196+
197+
`curl` example to list the users that are not locked and have the `can_request_admin` flag set to `true`:
198+
199+
```bash
200+
CLIENT_ID=01J44RKQYM4G3TNVANTMTDYTX6
201+
CLIENT_SECRET=phoo8ahneir3ohY2eigh4xuu6Oodaewi
202+
203+
# Get an access token
204+
curl \
205+
-u "$CLIENT_ID:$CLIENT_SECRET" \
206+
-d "grant_type=client_credentials&scope=urn:mas:admin" \
207+
https://mas.example.com/oauth2/token \
208+
| jq -r '.access_token' \
209+
| read -r ACCESS_TOKEN
210+
211+
# List users (The -g flag prevents curl from interpreting the brackets in the URL)
212+
curl \
213+
-g \
214+
-H "Authorization: Bearer $ACCESS_TOKEN" \
215+
'https://mas.example.com/api/admin/v1/users?filter[can_request_admin]=true&filter[status]=active&page[first]=100' \
216+
| jq
217+
```
218+
219+
<details>
220+
<summary>
221+
Sample output
222+
</summary>
223+
224+
```json
225+
{
226+
"meta": {
227+
"count": 2
228+
},
229+
"data": [
230+
{
231+
"type": "user",
232+
"id": "01J2KDPHTZYW3TAT1SKVAD63SQ",
233+
"attributes": {
234+
"username": "kilgore-trout",
235+
"created_at": "2024-07-12T12:11:46.911578Z",
236+
"locked_at": null,
237+
"can_request_admin": true
238+
},
239+
"links": {
240+
"self": "/api/admin/v1/users/01J2KDPHTZYW3TAT1SKVAD63SQ"
241+
}
242+
},
243+
{
244+
"type": "user",
245+
"id": "01J3G5W8MRMBJ93ZYEGX2BN6NK",
246+
"attributes": {
247+
"username": "quentin",
248+
"created_at": "2024-07-23T16:13:04.024378Z",
249+
"locked_at": null,
250+
"can_request_admin": true
251+
},
252+
"links": {
253+
"self": "/api/admin/v1/users/01J3G5W8MRMBJ93ZYEGX2BN6NK"
254+
}
255+
}
256+
],
257+
"links": {
258+
"self": "/api/admin/v1/users?filter[can_request_admin]=true&filter[status]=active&page[first]=100",
259+
"first": "/api/admin/v1/users?filter[can_request_admin]=true&filter[status]=active&page[first]=100",
260+
"last": "/api/admin/v1/users?filter[can_request_admin]=true&filter[status]=active&page[last]=100"
261+
}
262+
}
263+
```
264+
265+
</details>
266+
267+
[authorization code]: ../topics/authorization.md#authorization-code-grant
268+
[device authorization]: ../topics/authorization.md#device-authorization-grant

0 commit comments

Comments
 (0)