Requests for comment/SessionStorageAPI

This is a request for comment for a multi-master session storage service interface.

Request for comment (RFC)
SessionStorageAPI
Component General
Creation date
Author(s) User:EEvans (WMF), User:CAndrew (WMF)
Document status implemented
See Phabricator.

Background

edit

A need has emerged for sessions to be valid fleet-wide; In order to realize our objective of being active-active, sessions created in one data-center should be available in the other. Accomplishing this in a robust and secure way will require replication semantics more sophisticated than those available to us with Redis.

Additionally, sensitive data in MediaWiki should be isolated where possible to limit impact in the event of a compromise. Sessions are an example of this; Were a malicious user able to enumerate sessions, they would be able to discover and hijack user logins (including for example, those of admins). Persisting to an external service that exposes a narrow API, nothing more than required to store, retrieve, and delete sessions, is in-line with the principle of least privilege, and may safeguard against some classes of unintentional exposure.

Proposal

edit

This document proposes an implementation of a key-value storage service, with master-master replication, for use in multi-DC session management.

Operations

edit
URL /sessions/v1/{key}
Method GET
Params None
Data None
Success Example:
HTTP/1.0 200 OK
Content-Type: application/octet-stream
Content-Length: 27
Date: Mon, 22 Oct 2018 22:07:59 GMT

sample value
Error Errors are JSON objects conforming to RFC7807 (Problem Details for HTTP APIs) with a content-type of application/problem+json.
Code Reason Example
400 Bad request
{
    "type": "https://www.mediawiki.org/wiki/probs/bad-request",
    "title": "Bad request",
    "detail": "The request was incorrect or malformed",
    "instance": "/sessions/v1/test_key"
}
401 Not authenticated and/or authorized to write.
{
    "type": "https://www.mediawiki.org/wiki/probs/not-authorized",
    "title": "Not Authorized",
    "detail": "You are not authorized to access this value",
    "instance": "/sessions/v1/test_key"
}
404 Key not found
{
    "type": "https://www.mediawiki.org/wiki/probs/not-found",
    "title": "Not Found",
    "detail": "The value you requested was not found",
    "instance": "/sessions/v1/test_key"
}
500 Internal server error
{
    "type": "https://www.mediawiki.org/wiki/probs/internal-server-error",
    "title": "Internal Server Error",
    "detail": "The server encountered an error with your request",
    "instance": "/sessions/v1/test_key"
}
Example
$ curl http://api.example.org/sessions/v1/test_key
Notes
URL /sessions/v1/{key}
Method POST
Params None
Data The body of the request is the value represented as arbitrary bytes, using a content-type of application/octet-stream.


Example:
sample value
Success Example:
HTTP/1.0 201 CREATED
Content-Type: application/octet-stream
Content-Length: 0
Date: Tue, 23 Oct 2018 19:40:40 GMT
Error Errors are JSON objects conforming to RFC7807 (Problem Details for HTTP APIs) with a content-type of application/problem+json.
Code Reason Example
401 Not authenticated and/or authorized to write.
{
    "type": "https://www.mediawiki.org/wiki/probs/not-authorized",
    "title": "Not Authorized",
    "detail": "You are not authorized to access this value",
    "instance": "/sessions/v1/test_key"
}
500 Internal server error
{
    "type": "https://www.mediawiki.org/wiki/probs/internal-server-error",
    "title": "Internal Server Error",
    "detail": "The server encountered an error with your request",
    "instance": "/sessions/v1/test_key"
}
Example
$ curl -X POST -H 'Content-Type: application/octet-stream' -d 'sample value' \
       http://api.example.org/sessions/v1/test_key
Notes This operation assigns a value to key. The return does not differentiate between a request that created a new value, or one that overwrote an existing one.


Values persist until expiring, the result of a TTL dictated by service configuration (read: a single TTL applying to all stored sessions). Values retrieved after expiry result in a 404 (see above).


When operated in a multi-DC environment, a successful return guarantees that subsequent calls to get will return the value assigned from the data-center local to the executing endpoint. Remote data-centers (remote from the endpoint executing the request) are updated with eventual consistency semantics.

delete
edit
URL /sessions/v1/{key}
Method DELETE
Params None
Data None
Success Example:
HTTP/1.0 204 NO CONTENT
Content-Type: application/octet-stream
Content-Length: 0
Date: Tue, 23 Oct 2018 19:40:40 GMT
Error Errors are JSON objects conforming to RFC7807 (Problem Details for HTTP APIs) with a content-type of application/problem+json.
Code Reason Example
401 Not authenticated and/or authorized to write.
{
    "type": "https://www.mediawiki.org/wiki/probs/not-authorized",
    "title": "Not Authorized",
    "detail": "You are not authorized to access this value",
    "instance": "/sessions/v1/test_key"
}
500 Internal server error
{
    "type": "https://www.mediawiki.org/wiki/probs/internal-server-error",
    "title": "Internal Server Error",
    "detail": "The server encountered an error with your request",
    "instance": "/sessions/v1/test_key"
}
Example
$ curl -X DELETE http://api.example.org/sessions/v1/test_key
Notes This operation deletes the value associated with key, if it exists. The return status does not distinguish between a value that was not present at the time of delete (a no-op), and those where a value was successfully deleted.

When operated in a multi-DC environment, a successful return guarantees that subsequent GET requests will not return a value for key in any data-center (404 is returned). If a failure occurs, the disposition of the value is unknown (any or none of the values may have been deleted); Failures should be handled accordingly.

Versioning

edit

A global version that follows the principles of semantic versioning is proposed. Backward-compatible changes to representations, and bug fixes will fall under minor and patch changes respectfully. Changes to the major are reserved for backward-incompatible (read: breaking) changes. Every effort will be made to avoid breaking changes, however should they become necessary, a major-version prefix in the URI (e.g. /v1/...) will provide the means to preserve compatibility during a transition.

Notes

edit
  • Earlier versions of this document specified the uses of PUT for set operations, and defined this as having upsert semantics. The POST verb is now used for upsert, and PUT is reserved for future set-if-does-not-exist semantics (similar to Redis's SETNX operation).
  • In the interest of simplicity, set operations as described here use a TTL defined on a service-wide basis (read: a single, configurable TTL, applied to all writes). In the event this proves inadequate, a backward-compatible change to enable client-supplied TTL overrides via a Cache-Control header is planned. Should this be implemented, client-supplied TTLs will be enforced as being strictly less-than or equal-to the globally defined default TTL.
  • The type attribute of the RFC7807 error responses shown here are placeholders. A more appropriate namespace needs to be set aside, and appropriate material created for each.

See also

edit