API.md 16.8 KB
Newer Older
ale's avatar
ale committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
API
===

This document defines a generic API to expose user accounts and
associated resources to UIs and management tools, so that they can
delegate privileged operations to a dedicated service.

The focus is on making adding resources and functionality as simple as
possible, to support diverse service ecosystems.

We'd like to make it possible to create a generic, extensible UI for
account management, which would deal with the UX workflows, leaving
the implementation-specific details to the (custom) accountserver
service. Such application would initially support standard operations
on resources: adding functionality should require only adding a bit of
UI-related code to the application (and the implementation-specific
support on the server side).

## Data model

The accountserver data model provides a high-level representation of
user accounts and associated resources, decoupled from the underlying
database implementation (which likely consists of inter-related SQL
tables or LDAP objects). Such high-level representation is targeted at
humans, either users themselves or administrators.

The top-level object is an *account*, representing a user's identity.
Accounts contain a number of *resources* (of different types), which
it owns - meaning that it has administrative control over them.

Resources can be of different types (mailboxes, websites, mailing
lists, etc), each associated with a different service. In some cases,
resources can themselves contain other resources, to represent a close
association of some form (the semantics will be service-specific, an
example could be a website and its associated MySQL database, which
should be co-located).

Resources have a *name*, which must be globally unique for resources
of the same type. Resources within an account are uniquely identified
by an *ID*, which combines type and name and is a globally unique
identifier:

    <id> = <type>/<name>

The resource type `account` identifies the account itself.

Each resource type supports a number of *actions* that can modify the
state of resources. The specific actions and their semantics will need
to be documented (see [Schema](#schema) below), and need to be agreed
upon by the UIs/tools and the accountserver, but this scheme is easily
extensible.

## Interface

55
56
The accountserver provides an HTTP(S) endpoint operating as a RPC
server.
ale's avatar
ale committed
57

58
59
Request bodies should be JSON-encoded, same as responses, and must
have a Content-Type of *application/json*.
ale's avatar
ale committed
60

61
62
63
In case of errors, either with the request (400), or with the server
or its backends (500), the response body will contain a short error
message in text form.
ale's avatar
ale committed
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99

### Authentication

All requests should be authenticated. We assume that there is a way to
identify certain users as *administrators* (for instance via group
membership). Action handlers in the accountserver implementation have
the option of choosing between two different ACL checks (and only
these two):

* *user*:
  * allow a user access if the resource belongs to the user's own
    account
  * allow administrators access to this action for any account
* *admin-only*:
  * only administrators have access to this action

In more detail, there are two options that could be implemented,
depending on the specifics of the authentication system used:

* *Trust the UI and tools*: authenticate the connection with the
  accountserver, and trust the front-ends with their own internal
  access control checks (so that, for instance, user x can not modify
  data belonging to user y).
* *End-to-end verification*: pass an authentication token for the user
  from the front-ends to the accountserver, and verify it on every
  request. This puts the trust on the authentication system itself,
  regardless of potential issues in the front-end implementation.


## Schema

The *schema* describes the known resource types and the actions that
can be performed on them, along with their parameters.

What follows is a sample schema, every resource type is described along with
the attributes that it supports and the actions that can be performed
100
on it. The relative types are defined in [types.go](types.go).
ale's avatar
ale committed
101

102
### user
ale's avatar
ale committed
103
104
105
106
107
108

Represents a user account, or an *identity*. It is the primary
identificator for users in the system.

Attributes:

109
110
* `name` - name of the user
* `status` - one of *active*, *inactive*, or other states that might be
ale's avatar
ale committed
111
112
  necessary to implement more complex state machines related to
  account activation
113
* `has_2fa` - true if 2FA is enabled (whether OTP or U2F)
ale's avatar
ale committed
114
115
* `u2f_registrations`: list of U2F registration IDs (not the keys
  themselves though)
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
* `app_specific_passwords` - list of application-specific passwords
  (without the passwords), each with the following attributes:
  * `id` - ID of the password (used to delete it)
  * `comment` - user-written comment identifying this client
* `resources` - list of resources associated with the user

### resource

A resource represents an account with a specific service. Every
resource has a unique *ID* (a path-like structure), and a *type* to
identify which service it is associated with. Resources can be *owned*
by a user, in which case their ID will contain the user's name, or
they can be *global* and not being strictly associated with an
individual users (such is the case of mailing lists, for example).

Also, most resources in a partition-based service are *sharded*, that
is, they are associated with a specific *shard* identifying the host
holding the user's data.

Resources have a number of common attributes, and some type-specific
attributes. The common ones are:

* `id` - the resource ID. IDs are basically opaque (they must make
  sense to the backend, not to users), and should only be used with
  this API. The only exception to the opaqueness is that the first
  path component of the ID is the resource *type*.
* `name` - resource name (for display purposes)
* `parent_id` - the parent resource ID, for nested resources (like
  databases).
* `status` - resource status (active, inactive, etc)
* `shard` - current shard
* `original_shard` - old shard, this is irrelevant to the user except
  that if *shard* != *original_shard*, we may want to communicate that
  the account is being moved by the automation.
* `group` - some resources are grouped together for both display and
  practical purposes (some operations will apply to all them as a
  whole). This is an opaque pseudo-random identifier that, in a list
  of resources, will be set to identify one or more groups of
  resources.

Type-specific attributes are included in a dictionary named after the
resource type itself, for instance:

```
{
  "id": "email/test@example.com/...",
  "email": {
    "aliases": ["test2@example.com]
  },
  ...
}
```

A list of resource types and their attributes is included below.

#### email

Email-specific attributes:

* `aliases` - a list of aliases that will receive email for the same
  mailbox.
* `maildir` - base path for the mailbox storage
* `quota_limit` - quota limit (not implemented)
* `quota_usage` - quota usage (not implemented)
ale's avatar
ale committed
180

181
#### list
ale's avatar
ale committed
182

183
Represents a mailing list.
ale's avatar
ale committed
184

185
186
* `admins` - list of admin email addresses (whether internal or external)
* `public` - if true, the list archives will be public
ale's avatar
ale committed
187

188
#### website, domain
ale's avatar
ale committed
189

190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
*Websites* are sub-domains of a hosting domain, while *domains* are
hosted on their own public DNS domain (and have associated DNS
entries, etc).

* `url` - public URL for the site
* `parent_domain` - for websites only, the parent domain
* `accept_mail` - for domains, whether to accept email and allow the
  creation of mailbox accounts on this domain.
* `options` - list of options (which are just strings with special
  meaning, documented elsewhere).
* `document_root` - document root of the website

#### dav

A WebDAV account is associated with a home directory hosting one or
more sites.

* `homedir` - home directory for this hosting account

#### database

Databases have a database name and a database user.

* `db_user` - name of the user associated with the database
ale's avatar
ale committed
214

ale's avatar
ale committed
215

ale's avatar
ale committed
216
217
218
219
220
221
222
223
224
225
### Example

This is an example of a simple user, as returned by `/api/user/get`:

```
{
    "name": "uno@example.com",
    "lang": "it",
    "uid": 19475,
    "has_2fa": false,
ale's avatar
ale committed
226
227
    "has_otp": false,
    "has_encryption_keys": true,
ale's avatar
ale committed
228
229
230
231
232
    "password_recovery_hint": "bla bla bla",
    "u2f_registrations": null,
    "resources": [
        {
            "id": "web/uno@example.com/uno",
ale's avatar
ale committed
233
            "type": "web",
ale's avatar
ale committed
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
            "name": "autistici.org/uno",
            "status": "active",
            "shard": "host2",
            "original_shard": "host2",
            "group": "uno",
            "website": {
                "url": "https://www.autistici.org/uno/",
                "uid": 19475,
                "parent_domain": "autistici.org",
                "accept_mail": false,
                "quota_usage": 0,
                "document_root": "/home/users/example.com/uno/html-uno"
            }
        },
        {
            "id": "db/uno@example.com/alias=uno/unodb",
ale's avatar
ale committed
250
            "type": "db",
ale's avatar
ale committed
251
252
253
254
255
256
257
258
259
260
261
262
263
            "name": "unodb",
            "parent_id": "web/uno@example.com/uno",
            "status": "active",
            "shard": "host2",
            "original_shard": "host2",
            "group": "uno",
            "database": {
                "db_user": "unodb",
                "cleartext_password": "password"
            }
        },
        {
            "id": "domain/uno@example.com/example.com",
ale's avatar
ale committed
264
            "type": "domain",
ale's avatar
ale committed
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
            "name": "example.com",
            "status": "active",
            "shard": "host2",
            "original_shard": "host2",
            "group": "uno",
            "website": {
                "url": "https://example.com/",
                "uid": 19475,
                "accept_mail": true,
                "quota_usage": 0,
                "document_root": "/home/users/example.com/uno/html-example.com"
            }
        },
        {
            "id": "dav/uno@example.com/uno",
ale's avatar
ale committed
280
            "type": "dav",
ale's avatar
ale committed
281
282
283
284
285
286
287
288
289
290
291
292
            "name": "uno",
            "status": "active",
            "shard": "host2",
            "original_shard": "host2",
            "group": "uno",
            "dav": {
                "uid": 19475,
                "homedir": "/home/users/example.com/uno"
            }
        },
        {
            "id": "email/uno@example.com/uno@example.com",
ale's avatar
ale committed
293
            "type": "email",
ale's avatar
ale committed
294
295
296
297
298
299
300
301
302
303
304
305
306
307
            "name": "uno@example.com",
            "status": "active",
            "shard": "host2",
            "original_shard": "host2",
            "email": {
                "maildir": "example.com/uno/",
                "quota_limit": 0,
                "quota_usage": 0
            }
        }
    ]
}
```

ale's avatar
ale committed
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408

# API Reference

## User endpoints

The following API endpoints invoke operations on an individual
user. Access is allowed for admins, and for the user itself.

Most requests dealing with encryption keys are so-called *privileged*
requests, and will require the user's password in the *cur_password*
parameter in order to decrypt the existing encryption keys.

### `/api/user/get`

Retrieve information about a user and all its associated resources.

Request parameters:

* `username` - user to fetch
* `sso` - SSO ticket

### `/api/user/change_password`

Change the primary authentication password for a user. This operation
will update the user's storage encryption keys, or initialize them if
they do not exist.

Request parameters:

* `username` - name of the user
* `sso` - SSO ticket
* `cur_password` - current valid password for the user
* `password` - new password (unencrypted)

### `/api/user/set_password_recovery_hint`

Sets the secondary authentication password (a hint / response pair,
used to recover the primary credentials) for a user. This operation
will update the user's storage encryption keys, or initialize them if
they do not exist yet.

Request parameters:

* `username` - name of the user
* `sso` - SSO ticket
* `cur_password` - current valid password for the user
* `hint` - the secondary authentication hint
* `response` - the secondary authentication response (effectively a
  password, unencrypted)

### `/api/user/enable_otp`

Enable TOTP for a user. The server can generate a new TOTP secret if
necessary, or it can be generated by the caller (usually better as it
allows for a better validation UX).

Request parameters:

* `username` - name of the user
* `sso` - SSO ticket
* `totp_secret` - new TOTP secret (optional)

### `/api/user/disable_otp`

Disable TOTP for a user. Existing 2FA credentials will be wiped as
well.

Request parameters:

* `username` - name of the user
* `sso` - SSO ticket

### `/api/user/create_app_specific_password`

Create a new application-specific password. 2FA must already be
enabled for the user. A new random password will be generated by the
server and returned in the response. A new copy of the encryption key
will be encrypted with the new application-specific password.

ASPs are identified by a unique random ID that is also automatically
generated by the server.

Request parameters:

* `username` - name of the user
* `sso` - SSO ticket
* `cur_password` - current valid password for the user
* `service` - service that the password should be valid for
* `notes` - a user-controlled comment about the client

### `/api/user/delete_app_specific_password`

Delete an application-specific password (and the associated encryption
key).

Request parameters:

* `username` - name of the user
* `sso` - SSO ticket
* `asp_id` - ID of the app-specific password

409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
### `/api/user/update`

Allows the caller to update a (very limited) selected set of fields on
a user object. It is a catch-all function for very simple changes that
don't justify their own specialized method.

Non-authentication request attributes are all optional.

Request parameters:

* `username` - user to fetch
* `sso` - SSO ticket
* `lang` - set the preferred language for this user
* `u2f_registrations` - set the list of U2F registrations

424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
### `/api/user/create`

Create a new user (admin-only). Will also create all the resources
that are included in the request. A large number of attributes on the
user and resource objects can be safely omitted, as they will be
filled in by the server.

The returned response will include the password for the new user (in
cleartext).

Request parameters:

* `sso` - SSO ticket
* `user` - user to create, with resources

439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
### `/api/recover_password`

Special endpoint for password recovery, used by the login FE service
(sso-server) to retrieve the password recovery hint, and to trigger
password recovery with the user-provided recovery password.

The recovery workflow should look like this: first, the sso-server
sends a PasswordRecoveryRequest with only the *username* field set,
retrieving the password recovery hint for the user. Then it presents
the hint to the user, and asks for both the recovery password and the
new password to be set if recovery is successful. These are finally
sent to the accountserver in another PasswordRecoveryRequest.

Request parameters:

* `username` - user name
* `recovery_password` - recovery password. Account recovery will only
  be attempted if this field is not empty, otherwise we'll just return
  the password hint.
* `password` - new password to be set for the account

Response parameters:

* `hint` - the password recovery hint to show the user
ale's avatar
ale committed
463
464
465
466
467
468
469
470

## Resource endpoints

These API endpoints manipulate individual resources regardless of
which user they belong to. Access is normally granted to admins and to
the user that owns a resource, but some operations are restricted to
admins only.

471
### `/api/resource/set_status`
ale's avatar
ale committed
472

473
Modify the status of a resource (admin-only).
ale's avatar
ale committed
474
475
476
477
478

Request parameters:

* `resource_id` - resource ID
* `sso` - SSO ticket
479
480
* `status` - new status (one of *active*, *inactive*, *readonly*
  or *archived*, depending on the resource type)
ale's avatar
ale committed
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505

### `/api/resource/move`

Move a resource between shards (admin-only).

Resources that are part of a group (for instance websites and DAV
accounts) are moved together, so this request might end up moving more
than one resource.

Request parameters:

* `resource_id` - resource ID
* `sso` - SSO ticket
* `shard` - new shard

### `/api/resource/create`

Create one or more resources associated with a user. Note that if
creating multiple resources, they must all belong to the same user.

Request parameters:

* `sso` - SSO ticket
* `resources` - list of resource objects to create

506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532


## Type-specific resource endpoints

These API endpoints are specific to resources of certain types.

### `/api/resource/reset_password`

Reset the password associated with a resource (if the resource type
supports it). It will generate a random password and return it in
cleartext in the response.

Request parameters:

* `resource_id` - resource ID
* `sso` - SSO ticket

### `/api/resource/email/add_alias`

Add a new alias to an email resource.

Request parameters:

* `resource_id` - resource ID
* `sso` - SSO ticket
* `addr` - email address of the new alias

ale's avatar
ale committed
533
### `/api/resource/email/delete_alias`
534
535
536
537
538
539
540
541
542

Delete an alias from an email resource.

Request parameters:

* `resource_id` - resource ID
* `sso` - SSO ticket
* `addr` - email address of the alias to delete