Commit 0cdedceb authored by ale's avatar ale
Browse files

Add more endpoints to the HTTP server and document everything

parent af4fe0c6
......@@ -52,18 +52,15 @@ extensible.
## Interface
The accountserver provides an HTTP endpoint operating as a RPC server.
The accountserver provides an HTTP(S) endpoint operating as a RPC
Request bodies should be JSON-encoded, same as responses.
Request bodies should be JSON-encoded, same as responses, and must
have a Content-Type of *application/json*.
#### get(*account_name*)
Returns (public) information on an account and all the resources
contained in it.
#### action(*account_name*, *resource_id*, *action*, {*args*})
Perform a generic action on the specified resource.
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.
### Authentication
......@@ -100,36 +97,121 @@ 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
on it.
on it. The relative types are defined in [types.go](types.go).
### account
### user
Represents a user account, or an *identity*. It is the primary
identificator for users in the system.
* `status`: one of *active*, *inactive*, or other states that might be
* `name` - name of the user
* `status` - one of *active*, *inactive*, or other states that might be
necessary to implement more complex state machines related to
account activation
* `otp_enabled`: true if OTP 2FA is enabled
* `u2f_enabled`: true if U2F 2FA is enabled
* `has_2fa` - true if 2FA is enabled (whether OTP or U2F)
* `u2f_registrations`: list of U2F registration IDs (not the keys
themselves though)
* `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
* `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
Type-specific attributes are included in a dictionary named after the
resource type itself, for instance:
"id": "email/",
"email": {
"aliases": ["]
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
* `maildir` - base path for the mailbox storage
* `quota_limit` - quota limit (not implemented)
* `quota_usage` - quota usage (not implemented)
#### list
* `set_password`: change primary account password to a new one
* `enable_otp` / `disable_otp`: enable or disable OTP 2FA
* ...
Represents a mailing list.
### email
* `admins` - list of admin email addresses (whether internal or external)
* `public` - if true, the list archives will be public
### mailing list
#### website, domain
### web hosting (website / db / FTP account or equivalent)
*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
# API Reference
......@@ -232,6 +314,21 @@ Request parameters:
* `sso` - SSO ticket
* `asp_id` - ID of the app-specific password
### `/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
Request parameters:
* `sso` - SSO ticket
* `user` - user to create, with resources
## Resource endpoints
......@@ -284,3 +381,40 @@ Request parameters:
* `sso` - SSO ticket
* `resources` - list of resource objects to create
## 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
### `/api/resource/email/add_alias`
Delete an alias from an email resource.
Request parameters:
* `resource_id` - resource ID
* `sso` - SSO ticket
* `addr` - email address of the alias to delete
......@@ -37,6 +37,13 @@ func (s *AccountServer) handleGetUser(tx as.TX, w http.ResponseWriter, r *http.R
func (s *AccountServer) handleCreateUser(tx as.TX, w http.ResponseWriter, r *http.Request) (interface{}, error) {
var req as.CreateUserRequest
return handleJSON(w, r, &req, func(ctx context.Context) (interface{}, error) {
return s.service.CreateUser(ctx, tx, &req)
func (s *AccountServer) handleChangeUserPassword(tx as.TX, w http.ResponseWriter, r *http.Request) (interface{}, error) {
var req as.ChangeUserPasswordRequest
return handleJSON(w, r, &req, func(ctx context.Context) (interface{}, error) {
......@@ -121,6 +128,20 @@ func (s *AccountServer) handleDisableOTP(tx as.TX, w http.ResponseWriter, r *htt
func (s *AccountServer) handleAddEmailAlias(tx as.TX, w http.ResponseWriter, r *http.Request) (interface{}, error) {
var req as.AddEmailAliasRequest
return handleJSON(w, r, &req, func(ctx context.Context) (interface{}, error) {
return &emptyResponse, s.service.AddEmailAlias(ctx, tx, &req)
func (s *AccountServer) handleDeleteEmailAlias(tx as.TX, w http.ResponseWriter, r *http.Request) (interface{}, error) {
var req as.DeleteEmailAliasRequest
return handleJSON(w, r, &req, func(ctx context.Context) (interface{}, error) {
return &emptyResponse, s.service.DeleteEmailAlias(ctx, tx, &req)
func (s *AccountServer) withTx(f txHandler) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
tx, err := s.backend.NewTransaction()
......@@ -149,6 +170,7 @@ func (s *AccountServer) withTx(f txHandler) http.HandlerFunc {
func (s *AccountServer) Handler() http.Handler {
h := http.NewServeMux()
h.HandleFunc("/api/user/get", s.withTx(s.handleGetUser))
h.HandleFunc("/api/user/create", s.withTx(s.handleCreateUser))
h.HandleFunc("/api/user/change_password", s.withTx(s.handleChangeUserPassword))
h.HandleFunc("/api/user/set_password_recovery_hint", s.withTx(s.handleSetPasswordRecoveryHint))
h.HandleFunc("/api/user/enable_otp", s.withTx(s.handleEnableOTP))
......@@ -160,6 +182,8 @@ func (s *AccountServer) Handler() http.Handler {
h.HandleFunc("/api/resource/create", s.withTx(s.handleCreateResources))
h.HandleFunc("/api/resource/move", s.withTx(s.handleMoveResource))
h.HandleFunc("/api/resource/reset_password", s.withTx(s.handleResetResourcePassword))
h.HandleFunc("/api/resource/email/add_alias", s.withTx(s.handleDeleteEmailAlias))
h.HandleFunc("/api/resource/email/delete_alias", s.withTx(s.handleDeleteEmailAlias))
h.HandleFunc("/api/recover_password", s.withTx(s.handleRecoverPassword))
return h
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment