This is a follow up to Automatic theme snap installation notes and Snapd-desktop-integration snap ownership and auto-connection request, outlining a potential REST API to support the snapd-desktop-integration service in its role of installing theme snaps. The main constraints are:
- We don’t want to grant snapd-desktop-integration access to the
snapd-control
interface. - We don’t want to grant every snap the ability to install themes.
- The act of initiating installation of snaps must be protected by Polkit authorisation.
So the plan is to add a new interface whose plug will grant access to just the methods required by snapd-desktop-integration
and no more. Ideally the method used to grant access should be scale to handle future API subsets we might want to expose.
Authentication
At present, snapd provides its REST API on two sockets:
-
/run/snapd.socket
– only available withsnapd-control
-
/run/snapd-snap.socket
– available to all snaps, used by thesnapctl
utility.
A single HTTP server responds to connections on both sockets, with some access decisions based on which socket the client connects to.
The simplest option would be to add a third socket, and have the new interface grant access to it via AppArmor. This is probably not the best option if we expect to see more API subsets emerge.
An alternative would be to reuse the existing snapd-snap.socket
, which has a few advantages:
- The socket is already available to snaps.
- There is a mechanism to identify which snap a client is via
$SNAP_COOKIE
.
Once we have the snap instance name from the cookie, it is a trivial matter to check for a connected plug from our new interface.
There are still some loose ends to tie up:
-
can we fit this access control logic into
Command.canAccess
? Currently the/v2/snapctl
endpoint accepts the cookie through a JSON encoded request body, which we don’t want to read at this early a point. The cookie is essentially a bearer token, so perhaps it would make sense to pass it in theAuthorization
request header? -
the current logic in
Command.canAccess
skips the possibility of a Polkit authorisation check in thec.SnapOK
code path (i.e. requests coming over thesnapd-snap.socket
socket). Rather than complicating the function further, I wonder if we can restructure the access control needs into a list of simpler checks that return one of (granted, rejected, continue), with the last instructing to continue on to the next check.
New REST API Endpoints
GET /v2/themes
This endpoint would be used to check whether there are available but uninstalled snaps that satisfy a set of themes. The endpoint would take the following query parameters:
-
gtk-theme
: GTK theme name -
icon-theme
: icon theme name -
sound-theme
: sound theme name
Any of these parameters can be passed multiple times to check for multiple themes at once. In practice, this is only needed for icon-theme
, since both desktop icons and mouse cursors are distributed as “icon themes”, and the user might pick different versions for each.
This is not just a theoretical concern: as an example, Ubuntu 16.04 in its default configuration sets the desktop icon theme to ubuntu-mono-dark
and mouse cursor theme to DMZ-White
.
The response body would indicate the support status for these themes. Maybe a JSON response something like this?
{
"gtk-themes": {
"theme1": "installed | available | unavailable",
...
},
"icon-themes": {
...
},
"sound-themes": {
...
}
}
Depending on what sort of UI we want to present to the user, it might also be useful to provide the snap metadata for themes in the “available” state, since the client won’t have access to /v2/find
.
POST /v2/themes/install
This endpoint would be used to install available theme snaps. As it installs new snaps, it must be protected by a Polkit authorisation check.
The endpoint accepts a JSON encoded request body of the following form:
{
"gtk-themes": ["gtk-theme1", ...],
"icon-temes": ["icon-theme1", ...],
"sound-themes": ["sound-theme1", ...]
}
As this is a potentially long running task where we might want to present progress to the user, it would make sense to treat this as an async response similar to other endpoints that install or refresh snaps.
Of course, this leaves open the question of how to let the client monitor the progress of the change. We probably don’t want to provide unrestricted access to the /v2/changes/{id}
, for instance. Can we tag the source of the change in some way, and use that to make an access control decision?