Skip to end of metadata
Go to start of metadata


This is an overview of the GPII Preferences Service:  the pieces that make it up and how they are connected.

The Parts

  • Preferences Server, the central piece that allows access to users' preferences
  • Preference Framework, handles preference ontologies
  • OAuth2, authorization service to allow client applications access to users' preferences
  • Database, the storage of the preferences data

Questions to answer

  • Why does FLOE needs a preferences server?  What purposes?
    • Have a common way for multiple projects to store and retrieve preferences
    • Share the same preferences among different projects.  A kind of "portable preferences".
  • What is the actual structure of the preferences themselves?
  • What database to use?
    • GPII used CouchDB but was moving away
  • OAuth2 machinery is integrated with the Flow Manager
  • OAuth2 also has dependencies on the database
    • of course it does, since the credential and other authorization information is in the database
    • but, the database is CouchDB in GPII, so there is a dependency on couchdb access by the authorization service
    • encapsulated in gpii-db-opertions.
  • Serverless http  – suggested by Antranig in #fluid-work

Preferences Server API

This outlines the preferences server endpoints that define what the server provides in terms of accessing user preferences.  The API is RESTful, and is implemented internally using the fluid project's kettle.  Clients can make these http requests using tools such as curl, a web-based form, or some other http request library.

Note that authorization, or OAuth2 is not part of this API.  Getting ahead of this presentation: OAuth2 is a separate service and, in GPII, is built into the cloud based flow manager (CBFM).  That is, one can set up a preferences services and make the requests documented below without any authorization. However, in the case of a deployed production version of the GPII, the preferences server is not available to the outside world, and is accessed via CBFM endpoints that in turn makes these Preferences Server requests. The CBFM defers to the OAuth2 service to verify that the Preferences Server requests are allowed.

Another aspect of the way these requests work is the implied structure of the preferences.  Combined with the ontology handler in the Preferences Framework, the structure is:

  • a container of containers individuated by and ontology, e.g. "ISO-24751", "flat", etc.
    • there seems to be only two ontologies ever mentioned?
  • each ontology has a "preferences" container, that in turn has a "contexts" container.  The context specifies sets of named preferences.  For example, in GPII there is always a set named "gpii-default".
  • each named prefsSet (or context) has a container named "preferences" that lists the actual preferences.
  • the structure of the information within this inner "preferences" container is determined by the ontology.  For example, some ontologies are hierarchical while others are flat.

Even though the preferences are individuated by ontology, the Preferences Server requests always involve one ontology.  For example, if a user's preferences are requested, the preferences for only one ontology is returned.  If there is a request to modify preferences, only one ontology is supplied, although with a "merge", it looks like mulitple ontologies are updated in the backend?

Preferences Server Endpoints

  • GET /health
    • is the Preferences Server running?
    • 200 { "isHealthy": true } or 500 { "isHealthy": false }

    • note that the actual health handler is part of the flowmanager name space.  That's so the same code can be used to handle other /health requests within the full application. Possibly move to an even more general place, such as kettle.
  • GET /ready
    • are the Preferences Server and database both running, and is the connection between the two sound?
    • 200 { "isReady": true } or 503, { "isError": true, "message":"Problem with the database connection" }
  • GET /preferences/:gpiiKey[?view=:view]

    • get the preferences for the given user
    • the user is identified by the gpiiKey, e.g. /preferences/carla
    • the optional view specifies an ontology, e.g. ISO-24751. It defaults to "flat".
    • gpiiKeys are typically UUIDs
    • Note that the payload returned contains only one ontology's preferences sets.
    • the returned payload is a container of sets of preferences, designated by "contexts"
      • { "contexts": "gpii-default": { ... }, "bright": { ... } }
    • If the user's preferences does not contain preferences for the requested ontology (view), the response is an empty preferences set:
      • { "contexts": { } }

    • If the user does not exist, a no such user payload is returned:
      • 404 { "isError": true, "message": "GPII_Key does not exist", "errorCode": "GPII_ERR_NO_GPIIKEY" }
  • POST /preferences/[?view=:view]
    • upload new preferences
    • generates a new GPII Key for these preferences and returns it.
    • optional view specifies an ontology.  It defaults to "flat".
    • the payload submitte is, for example:
      • { "contexts": { "gpii-default": { ... } } }
    • the return payload is a container with the new user id key and the "contexts" structure acquired by the GET /preferences/:gpiiKey described above:
    • 200 { "gpiiKey": "fac425a5-1cea-453f-af61-b0b557dc34b9", "preferences": { "contexts": ... } }
    • the structure of preferences within each context is based on the ontology specified in the request
  • PUT /preferences/:gpiiKey[?merge=boolean&view=view]
    • modify existing preferences, or create new ones if none exist for the given (existing) user
    • the user is identified by the gpiiKey
    • the optional merge argument indicates if the new preferences are merged with the old (true) or replace the old (false, the default)
    • the view is the optional ontology. The default is "flat".
    • if the gpiiKey (the user) does not exist, it is created along with an associated prefsSafe
    • the returned payload is the same as with a POST /preferences request
    • the merge algorithm is complex since it appears to effect multiple ontologies when multiple ontologies are present in the user's preferences sets (verify this, and exactly what it means, although if we drop ontologies, this complex merge operation will not be needed).

OAuth2

An OAuth2 authorization service is used to grant access to the preferences server.  In GPII, as noted above, the authorization requests are routed through the CBFM.  More specifically, the authorization service is a sub-component of the the CBFM.  Documentation of how OAuth2 is used in GPII is available on the gpii wiki: https://wiki.gpii.net/w/GPII_OAuth_2_Guide, and is summarized below.

OAuth2 Workflow

The workflow is two steps:

  1. The OAuth client authenticates with the authorization server, and receives an access token if the client is verified.
  2. The client then uses this access token when making subsequent requests

A more detailed description of this workflow, as used in GPII:

  1. POST a request to the CBFM: https://flowmanager:9082/access_token
    • The body of the POST request contains:
      • grant_type: "password", - the type of access authentication to use.
      • client_id: "pilot_computer", - OAuth2 client identifier, or who is asking for permission to access the preferences
      • client_secret: "pilot-computer-secret" - a secret known only to the client and the OAuth2 authentication service
      • username: "carla" - GPII Key associated with the user whose preferences are sought, typically a UUID
      • password: "dummy" - can be any string.
  2. The return value, if successful is:
    • "access_token": "1ab47092a3fc6cc9f30729aa4581796c" - random 16 bytes
    • "expiresIn": 3600 - number of seconds that the access token is valid (one hour, in this case).
    • "token_type": "Bearer", used in the "Authorization" header of subsequent requests regarding the user specified in the POST request above.

The access token received at step two, which is used in subsequent requests, is reflected in the database as an "App Installation Client" data structure.  Among other fields such as id, revision, expiry time, revoked, and so on, this database "access token" record has a user field ("carla" in the above example) and the access_token value returned at step two above.  It is used by the authorization machinery to permit access to the user's preferences.  An outline of the App Installation Client, or access-token-in-the-database is:

  • type: "gpiiAppInstallationAuthorization"
  • gpiiKey: the user or key that was used in the authorization request posted at step 1 above, called "username" there.
  • accessToken: the same random 16 bytes returned at step 2 above, called "access_token" there.
  • clientId: The client id that this authorization is assigned to.  (what is this?)
  • clientCredentialId: The client credential id that is used to request this authorization. (what is this?)
  • schemaVersion, revoked, time stamps, etc.

One "can" use CBFM endpoints to acquire preferences, but it's not as simple as with the Preferences Server.  The Preferences Server deals just with a user's preferences,  The CBFM requests of a user's preferences must include information about solutions (e.g., ATs) and the result is a set of life cycle instructions.  Using the CBFM invokes a lot of GPII machinery including the MatchMaker and the SolutionsRegistry.

Database

The section is about what is stored in the data base, or the structure of the data.  The actual database used in GPII is CouchDB but that's likely an implementation detail.  Regardless of what database is, the structures or information stored in it are conceptually the same.

At a very high level, the information that is stored in the database are Keys, Preferences Safes, Client Credentials, App Installation Clients, and App Installation Authorizations.  The full documentation regarding these is on the GPII wiki:  https://wiki.gpii.net/w/Keys,_KeyTokens,_and_Preferences

Note that the "Keys, Key Tokens and Preferences" document lists a future version of these structures.  Which version do we want to use.  The following is a description of the past (not future).

With respect to preferences, the two most important of these are Preferences Safes and their Keys.  The rest of the structures have to do with secure access.

Preferences Safes

A safe in which to put preferences.

  • aka prefsSafes
  • where the preferences are saved
  • A container whose top level has a number of atomic fields, and a "preferences" container
    • atoms: id, type (always "prefsSafe", schemaVersion, prefsSafeType ("snapset" vs. "user"), username, password, email, creation and modification dates
    • preferences is a container of ontologies
      • each ontology is named, e.g., "flat", "ISO24751"
      • the value is a container of sets of preferences:
        • the set has a name, e.g., "Carla".
        • the prefsSets themselves are within a container named "contexts"
          • each prefsSet is named, e.g. "gpii-default"
          • a prefSet is a container with a name, and a "preferences" container.
            • it is this innermost "preferences" container that holds the preferences
  • Thus, the path to the actual preferences:
    • { "preferences": { "flat": { "contexts": { "gpii-default": "preferences": { actual preferences here } } } }

  • Example of structure of actual preferences:
    • "http://registry.gpii.net/applications/org.gnome.desktop.a11y.magnifier": { // application, here the GNOME shell magnifier
      "show-cross-hairs": true,
      "mag-factor": 2,
      "mouse-tracking": "push",
      "screen-position": "right-half",
      "scroll-at-edges": true
      }
    • These are literally the same as the actual GNOME magnifier's "gsettings".
      • Are all preferences this one-to-one?
      • Are any preferences given in a generic, non-application specific way?
      • Role of common terms?

Keys

The Key records within the database are a way to connect a user to a prefsSafe, and a set of preferences within that safe.

  • id: user identifier, e.g., "carla"
    • usually a UUID,
    • also used in the internal access token records (App Installation Clients) to control access
  • type: always "gpii-key".  (Always??? (smile) )
  • prefsSafeId: identifier of the associated preferences safe
    • matches a prefsSafe record's "id" field
  • prefsSetId: identifier of a prefsSet (context) within a prefsSafe
  • revoked: boolean declaring if this key has been revoked and can no longer be used to access preferences safes


  • No labels