API.md 17.1 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
### `/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:

ale's avatar
ale committed
419
* `username` - user to modify
420 421 422 423
* `sso` - SSO ticket
* `lang` - set the preferred language for this user
* `u2f_registrations` - set the list of U2F registrations

ale's avatar
ale committed
424 425 426 427 428 429 430 431 432 433 434
### `/api/user/admin_update`

A privileged version of the above (admin-only), that allows updates to
user status and other privileged internal attributes.

Request parameters:

* `username` - user to modify
* `sso` - SSO ticket
* `status` - if not empty, new user status

435 436 437 438 439 440 441 442 443 444 445 446 447 448 449
### `/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

450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473
### `/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
474 475 476 477 478 479 480 481

## 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.

482
### `/api/resource/set_status`
ale's avatar
ale committed
483

484
Modify the status of a resource (admin-only).
ale's avatar
ale committed
485 486 487 488 489

Request parameters:

* `resource_id` - resource ID
* `sso` - SSO ticket
490 491
* `status` - new status (one of *active*, *inactive*, *readonly*
  or *archived*, depending on the resource type)
ale's avatar
ale committed
492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516

### `/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

517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543


## 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
544
### `/api/resource/email/delete_alias`
545 546 547 548 549 550 551 552 553

Delete an alias from an email resource.

Request parameters:

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