Skip to content

Latest commit

 

History

History
761 lines (547 loc) · 20 KB

File metadata and controls

761 lines (547 loc) · 20 KB

Webadmin API

The Side server exposes a webadmin based administration Rest API that follows overall the one implemented in Apache James.

This includes:

  • Domain routes (Without domain alias support)
  • User routes (only user crud, no support for delegation, from header, and JMAP identites).
  • Webamin tasks routes backed by an InMemory, node local, task manager.
  • Healthchecks routes The following healthchecks are implemented: Guice application lifecycle, LDAP User Server, RabbitMQ backend, OpenSearch Backend, Redis backend

It embeds a Prometheus compatible metric endpoint available via GET /metrics.

Additionally, the following endpoints are implemented, and are detailed below.

Back-channel logout endpoint

POST /add-revoked-token

Can be configured in OpenID identity provider in order to support back-channel logout.

Calendar users routes

Allow to oversee users that registered and used the DAV server.

Listing registered users

GET /registeredUsers

Will return the list of registered users:

[
  {
   "email": "james@linagora.com",
   "firstname": "James",
   "lastname": "Bond",
   "id": "248y230r2c"
  },
  ...
]

Registration of a new user

POST /registeredUsers
{
 "email": "james@linagora.com",
 "firstname": "James",
 "lastname": "Bond",
 "id": "248y230r2c"
}

Returns 201 created status code.

Testing existence of a user

HEAD /registeredUsers?email=james@linagora.com

Returns a status code of 200 if the user is registered, 400 instead.

Deleting a registered user

DELETE /registeredUsers?email=james@linagora.com

Deletes the registered user matching the given email.

Status codes:

  • 204 if deleted
  • 400 if the email query parameter is missing
  • 404 if the user does not exist

Updating a user registration

PATCH /registeredUsers?=248y230r2c
{
 "email": "james2@linagora.com",
 "firstname": "James2",
 "lastname": "Bond2"
}

Will set the fields of the user to the following value.

Status code: 204

Import LDAP users

POST /registeredUsers/tasks?task=importFromLDAP&usersPerSecond=100

Will collect all users from LDAP and add them to twake calendar if not exist.

The query parameter usersPerSecond controls the concurrency. Defaults to 100.

This endpoint returns a webdmin task with the following additional information:

  • processedUserCount: integer
  • failedUserCount: integer

Add missing fields to registered users

POST /registeredUsers?action=addMissingFields

Will add missing email and firstnames fields to existing registered users that don't have them. This is a migration task to update legacy user documents.

The email field is extracted from accounts.emails[0]. The firstnames field is computed by splitting the firstname field by spaces (e.g., "Jean Paul" becomes ["Jean", "Paul"]).

This endpoint returns a webadmin task with the following additional information:

{
  "type": "add-missing-fields",
  "timestamp": "2025-11-24T10:15:30.00Z",
  "processedUsers": 100,
  "upgradedUsers": 25,
  "errorCount": 0
}

Where:

  • processedUsers: Total number of users processed
  • upgradedUsers: Number of users that were updated with missing fields
  • errorCount: Number of errors encountered during migration

Status codes:

  • 201: Task successfully submitted
  • 400: Invalid action parameter

Domain member synchronisation

Only enabled if LDAP is configured.

1. Synchronize all domains

POST /addressbook/domain-members?task=sync

Synchronizes LDAP members for all existing domains.

Optional query parameters:

  • ignoredDomains : [String] Comma-separated list of domains to exclude from synchronization

Example:

POST /addressbook/domain-members?task=sync&ignoredDomains=twake.app,example.org

2. Synchronize a single domain

POST /addressbook/domain-members/{domain}?task=sync

Synchronizes LDAP members only for the specified domain.

Task additional information

Both endpoints will return a webadmin task with the following additional information:

"additionalInformation": {
    "type": "sync-domain-members-contacts-ldap-to-dav",
    "domain": null,
    "ignoredDomains": [ "twake.app" ],
    "timestamp": "${json-unit.any-string}",
    "addedCount": 1,
    "addFailureContacts": [],
    "updatedCount": 0,
    "updateFailureContacts": [],
    "deletedCount": 0,
    "deleteFailureContacts": []
}

Calendar events

Calendar event reindexing

POST /calendars?task=reindex&eventsPerSecond=100&calendarsConcurrency=1

Will iterate all registered user calendars and reindex their events.

The query parameter eventsPerSecond controls the indexing rate. Defaults to 100.

The query parameter calendarsConcurrency controls how many calendars can be exported and parsed concurrently for a user. Defaults to 1.

This endpoint returns a webdmin task with the following additional information:

  • processedEventCount: integer
  • failedEventCount: integer

Alarm rescheduling

POST /calendars?task=scheduleAlarms?eventsPerSecond=100

Will iterate all registered user calendar and reschedule future alarms.

The query parameter eventsPerSecond controls the concurrency. Defaults to 100.

This endpoint returns a webdmin task with the following additional information:

  • processedEventCount: integer
  • failedEventCount: integer

Calendar event archival

Calendar events can be archived into a dedicated archival calendar using the Webadmin task framework. This operation is asynchronous and supports both all users and single user modes.

Archive events of all users

POST /calendars?task=archive

This endpoint iterates over all registered users and archives their calendar events matching the provided criteria.

Supported query parameters
Parameter Type Optional Description
createdBefore duration yes Archive events whose DTSTAMP is before now minus the given duration (e.g. 5d, 12h, 1y)
lastModifiedBefore duration yes Archive events whose LAST-MODIFIED is before now minus the given duration (e.g. 5d, 12h, 1y)
masterDtStartBefore duration yes Archive events whose master DTSTART is before now minus the given duration (e.g. 5d, 12h, 1y)
isRejected boolean yes When true, archive only events rejected by the user
isNotRecurring boolean yes When true, archive only non-recurring events (events without RRULE, RDATE, or RECURRENCE-ID)
eventsPerSecond integer yes Throttling parameter controlling processing speed (default: 100)
  • When no criteria parameter is provided, all events are archived.
  • All criteria are combined using AND logic.

Example:

POST /calendars?task=archive&createdBefore=5d&isRejected=true

Archive events of a single user

POST /calendars/{username}?task=archive

Archives calendar events only for the specified user.

The same query parameters are supported as for the all-users endpoint.

  • When no criteria parameter is provided, all events of the user are archived.
  • If the user does not exist, the request fails.

Example:

POST /calendars/john.doe@linagora.com?task=archive&lastModifiedBefore=30d

Task response

Both endpoints return a Webadmin task:

{
  "taskId": "b7c8c3b0-5c5b-4e89-9e56-1a9f0d2e3a42"
}

Task details can be retrieved via:

GET /tasks/{taskId}
GET /tasks/{taskId}/await

Example task result:

{
  "status": "completed",
  "additionalInformation": {
    "archivedEventCount": 12,
    "failedEventCount": 0,
    "criteria": {
      "createdBefore": "2025-12-22T00:00:00Z",
      "lastModifiedBefore": null,
      "masterDtStartBefore": null,
      "rejectedOnly": true,
      "isNotRecurring": true
    }
  }
}

Where:

  • archivedEventCount: Number of events successfully archived
  • failedEventCount: Number of events that failed to be archived
  • criteria: Effective archival criteria applied for the task

Note: When using the single-user archival endpoint (POST /calendars/{username}?task=archive), the task additionalInformation also contains a targetUser property indicating the archived user.

Validation and error cases

Scenario Status
Unknown user (single-user endpoint) 404 Not Found
Invalid query parameter 400 Bad Request

Resource routes

All resource routes are scoped under a domain: /domains/{domain}/resources.

Listing resources

GET /domains/linagora.com/resources

Will list existing resources for that domain:

[
  {
    "name": "Resource name",
    "deleted": false,
    "description": "Descripting",
    "id": "RESOURCE_ID_1",
    "icon": "laptop",
    "domain": "linagora.com",
    "creator":"user1@linagora.com",
    "administrators": [
      {"email": "user1@linagora.com"},
      {"email": "user2@linagora.com"}
    ]
  }
]

Status codes:

  • 200 when returning results
  • 400 when domain name is malformed
  • 404 when domain does not exist

Getting a specific resource

GET /domains/linagora.com/resources/RESOURCE_ID

Will return the corresponding resource:

  {
    "name": "Resource name",
    "deleted": false,
    "description": "Descripting",
    "id": "RESOURCE_ID",
    "creator":"user1@linagora.com",
    "icon": "laptop",
    "domain": "linagora.com",
    "administrators": [
      {"email": "user1@linagora.com"},
      {"email": "user2@linagora.com"}
    ]
  }

Status codes: 404 if domain or resource not found (or resource belongs to another domain), 200 otherwise.

Marking a resource as deleted

DELETE /domains/linagora.com/resources/RESOURCE_ID

Will mark the resource as deleted.

Status codes: 404 if domain or resource not found, 204 otherwise.

Creating a resource

POST /domains/linagora.com/resources
{
  "name": "Resource name",
  "description": "Descripting",
  "creator":"user1@linagora.com",
  "icon": "laptop",
  "administrators": [
    {"email": "user1@linagora.com"},
    {"email": "user2@linagora.com"}
  ]
}

The administrators field is optional. Omitting it (or providing an empty list) creates a resource with no administrator:

POST /domains/linagora.com/resources
{
  "name": "Resource name",
  "description": "Descripting",
  "creator":"user1@linagora.com",
  "icon": "laptop"
}

Will create the following resource.

Status codes:

  • 201 if created. Location header contains the URL to read resource details
  • 400 if invalid: creator/administrator do not exist, or extra fields / invalid JSON
  • 404 if domain does not exist

A resource without administrator is not subject to the validation flow: any event request with this resource is automatically accepted.

Please note that resource administrators:

  • are emailed upon events created that book the resource
  • have delegation write access to the calendar of the resource

Updating a resource

PATCH /domains/linagora.com/resources/RESOURCE_ID
{
  "name": "Resource name 2",
  "description": "Descripting 2",
  "icon": "battery",
  "administrators": [
    {"email": "user2@linagora.com"}
  ]
}

Would update the resource accordingly. Each field is optional; omitting a field leaves it unchanged.

Status codes: 204 if updated, 400 if invalid (e.g. administrator not found), 404 if domain or resource not found.

Please note that resource administrators:

  • are emailed upon events created that book the resource
  • have delegation write access to the calendar of the resource. Removing an administrator revokes this delegation right.

Repositioning write rights for admins on resource calendar

POST /domains/linagora.com/resources?task=repositionWriteRights

Will iterate on each resource and ensure current administrators have delegation write access to the resource calendar, allowing them to accept, reject and counter events in the name of the resource.

Note that existing delegation write access granted to users no longer listed as administrators will not be revoked.

Domain registered users routes

Domain-scoped mirror of the /registeredUsers routes, allowing multi-tenant safe access. The global /registeredUsers routes remain available for global admin operations.

Listing users of a domain

GET /domains/linagora.com/registeredUsers

Returns only users whose email belongs to linagora.com:

[
  {
    "email": "james@linagora.com",
    "firstname": "James",
    "lastname": "Bond",
    "id": "248y230r2c"
  }
]

Optional ?email= filter to retrieve a single user:

GET /domains/linagora.com/registeredUsers?email=james@linagora.com

Returns 404 if the user does not exist or belongs to another domain.

Status codes:

  • 200 on success
  • 400 if domain name is malformed
  • 404 if domain does not exist

Adding a user to a domain

POST /domains/linagora.com/registeredUsers
{
  "email": "james@linagora.com",
  "firstname": "James",
  "lastname": "Bond"
}

The email domain must match the URL domain.

Status codes:

  • 201 if created
  • 400 if a required field is missing or the email domain does not match the URL domain
  • 404 if domain does not exist
  • 409 if a user with that email already exists

Testing existence of a user within a domain

HEAD /domains/linagora.com/registeredUsers?email=james@linagora.com
HEAD /domains/linagora.com/registeredUsers?id=248y230r2c

Returns 200 if the user exists and belongs to that domain, 404 otherwise.

Updating a user within a domain

PATCH /domains/linagora.com/registeredUsers?id=248y230r2c
{
  "email": "james2@linagora.com",
  "firstname": "James2",
  "lastname": "Bond2"
}

Status codes:

  • 204 if updated
  • 400 if required fields are missing
  • 404 if the user does not exist or belongs to another domain
  • 409 if the new email is already taken

Deleting a user within a domain

DELETE /domains/linagora.com/registeredUsers?email=james@linagora.com

Deletes the registered user matching the given email when the user belongs to the domain in the URL.

Status codes:

  • 204 if deleted
  • 400 if the domain name is malformed or the email query parameter is missing
  • 404 if the domain does not exist, the user does not exist, or the user belongs to another domain

Domain admins routes

Manage the list of administrators (admins) for each domain.

Listing domain administrators

GET /domains/{domainName}/admins

Example:

GET /domains/linagora.com/admins

Returns the list of admins of the domain:

[
  "user1@linagora.com",
  "user2@linagora.com"
]

Status codes:

  • 200 when returning the list (can be empty if the domain exists but has no admins).
  • 404 when the domain does not exist.
  • 400 when domainName has an invalid format.

Adding a domain administrator

PUT /domains/{domainName}/admins/{username}

Example:

PUT /domains/linagora.com/admins/user1@linagora.com

Adds the user user1@linagora.com as an admin of domain linagora.com.

Status codes:

  • 204 if successfully added (idempotent: calling multiple times with the same user still returns 204).
  • 404 if the domain or user does not exist.
  • 400 if domainName or username has an invalid format.

Revoking domain administrator rights

DELETE /domains/{domainName}/admins/{username}

Example:

DELETE /domains/linagora.com/admins/user1@linagora.com

Revokes administrator rights of user user1@linagora.com for domain linagora.com.

Status codes:

  • 204 if successful, even if the user exists but was not an admin (idempotent).
  • 404 if the domain or user does not exist.
  • 400 if domainName or username has an invalid format.

Domain-scoped task routes

These routes provide domain-filtered access to the standard webadmin task management endpoints. They are intended for WebAdmin proxies that enforce multi-tenancy based on the domain in the URL. A task is only accessible if it belongs to the specified domain; otherwise a 404 is returned (to avoid leaking task IDs across domains).

The following task types are domain-scoped:

Task type Domain resolution
calendar-archival (single-user) domain extracted from targetUser
DeleteUserDataTask domain extracted from username
sync-domain-members-contacts-ldap-to-dav (single-domain) domain field in additional information

Multi-domain tasks (e.g. archive all users, sync all domains) are not attributed to any specific domain and are therefore not accessible through these routes.

Get task status

GET /domains/{domain}/tasks/{taskId}

Returns the task execution details if the task belongs to the domain. Same response body as GET /tasks/{taskId}.

Status codes:

  • 200: task found and belongs to the domain
  • 400: invalid task id format
  • 404: domain does not exist, task not found, or task does not belong to the domain

Await task completion

GET /domains/{domain}/tasks/{taskId}/await?timeout=3600s

Waits for the task to complete and returns the final execution details. Same semantics as GET /tasks/{taskId}/await.

Optional query parameter:

  • timeout: maximum wait duration (e.g. 3600s, 1d). Defaults to 365 days.

Status codes:

  • 200: task completed and belongs to the domain
  • 400: invalid task id or timeout format
  • 404: domain does not exist, task not found, or task does not belong to the domain
  • 408: timeout reached before task completion

Cancel a task

DELETE /domains/{domain}/tasks/{taskId}

Cancels the task if it belongs to the domain.

Status codes:

  • 204: task cancelled (or already completed/cancelled)
  • 400: invalid task id format
  • 404: domain does not exist, task not found, or task does not belong to the domain

User data deletion

Allows deleting all data associated with a user. This is an asynchronous task that executes multiple deletion steps.

Deleting user data

POST /users/{username}?action=deleteData

Example:

POST /users/james@linagora.com?action=deleteData

Will delete all data associated with the user james@linagora.com.

Query parameters:

  • action=deleteData (required): Triggers the deletion task
  • fromStep={stepName} (optional): Start execution from a specific step, skipping previous ones

Response:

Returns a task ID for async tracking:

{
  "taskId": "464269f0-9314-11ef-a339-d76792bfb514"
}

Status codes:

  • 201: Task successfully submitted
  • 400: Invalid action or missing parameter

Deletion steps

The deletion task executes the following steps in priority order:

Step Name Priority Description
DavCalendarDeletionTaskStep 1 Deletes user's calendars and calendar events
DavContactDeletionTaskStep 2 Deletes user's contacts and address books
CalendarSearchDeletionTaskStep 10 Removes indexed calendar events from OpenSearch
OpenPaaSUserDeletionTaskStep 1000 Deletes user from the OpenPaaS user database

Running from a specific step

To skip certain deletion steps, use the fromStep parameter:

POST /users/james@linagora.com?action=deleteData&fromStep=CalendarSearchDeletionTaskStep

This will skip DavCalendarDeletionTaskStep and DavContactDeletionTaskStep, starting directly from CalendarSearchDeletionTaskStep.