Authorizing Others to Access Your Workflows

For multi-user setups, the Cylc web GUI can be deployed as part of a Jupyter Hub setup where a central service spawns servers on behalf of users.

The Cylc UI Server can be configured to allow specified users to monitor and optionally control workflows running under other user accounts.

This has many use cases including:

  • Collaborative research setups where multiple users need to access the same workflow.

  • Production systems where different levels of support may have different levels of access.

  • Support where administrators may require access to users workflows.

A multi-user Cylc setup consists of three components:

  1. Jupyter Hub

  2. Jupyter Server

  3. Cylc UI Server

And may additionally include other Jupyter Server extensions such as Jupyter Lab to provide a full interactive virtual workstation in the browser.

In order to allow access to other users servers, to permit the monitoring and optionally control of other users workflows, each of these three components must be configured:

  1. Jupyter Hub must be configured to allow connections to other users servers.

  2. The Jupyter Server authorisation policy must be set.

  3. Cylc must be configured with user permissions.

This configuration can all be performed in the same Jupyter / Cylc UI Server configuration file. See UI Server Configuration for more details.

Quick Example:

# /etc/cylc/uiserver/jupyter_config.py

# 1. Jupyter Hub
#    Allow all authenticated users to access, start and stop
#    each other's servers
c.JupyterHub.load_roles = [
    {
        "name": "user",
        "scopes": ["self", "access:servers", "servers"],
    }
]


# 2. Jupyter Server
#    Set a safe authorisation policy for multi-user setups
#    Note: This is ONLY necessary if you are deploying the Cylc UI Server
#          using commands other than `cylc hub` and `cylc hubapp`,
#          otherwise, it is the default.
from cylc.uiserver.authorise import CylcAuthorizer
c.ServerApp.authorizer_class = CylcAuthorizer


# 3. Cylc
#    Delegate permissions to users
c.CylcUIServer.user_authorization = {
    # provide all authenticated users with read-only access to each other's
    # servers
    "*": ["READ"],
}

The rest of this document takes you through each of these configurations, some of the key options and how they relate to their respective systems.

Jupyter Hub Authorisation

By default, Jupyter Hub only allows users to access their own servers.

In order to allow access to other users’ servers, two scopes must be configured:

access:servers

Permits us to connect to another user’s server.

servers

Permits us to start another user’s server.

This is done using the c.JupyterHub.load_roles configuration. For more information see the JupyterHub scopes reference.

Example:

# /etc/cylc/uiserver/jupyter_config.py

c.JupyterHub.load_roles = [
    {
        # allow all authenticated users to access, start and stop
        # each other's servers
        "name": "user",
        "scopes": ["self", "access:servers", "servers"],
    }
]

Jupyter Server Authorisation

Tip

You can skip this section if you are starting Jupyter Hub using cylc hub command and have not overridden the c.JupyterHub.spawner_class configuration (so are spawning the cylc hubapp command).

class cylc.uiserver.authorise.CylcAuthorizer(**kwargs)[source]

Defines a safe default authorization policy for Jupyter Server.

Jupyter Server provides an authorisation layer which gives full permissions to any user who has been granted permission to the Jupyter Hub access:servers scope (see JupyterHub scopes reference). This allows the execution of arbitrary code under another user account.

To prevent this you must define an authorisation policy using c.ServerApp.authorizer_class.

This class defines a policy which blocks all API calls to another user’s server, apart from calls to Cylc interfaces explicitly defined in the Cylc authorisation configuration.

This class is configured as the default authoriser for all Jupyter Server instances spawned via the cylc hubapp command. This is the default if you started Jupyter Hub using the cylc hub command. To see where this default is set, see this file for the appropriate release of cylc-uiserver: https://github.com/cylc/cylc-uiserver/blob/master/cylc/uiserver/jupyter_config.py

If you are launching Jupyter Hub via another command (e.g. jupyterhub) or are overriding jupyterhub.app.JupyterHub.spawner_class, then you will need to configure a safe authorisation policy e.g:

from cylc.uiserver.authorise import CylcAuthorizer
c.ServerApp.authorizer_class = CylcAuthorizer

Note

It is possible to provide read-only access to Jupyter Server extensions such as Jupyter Lab, however, this isn’t advisable as Jupyter Lab does not apply file-system permissions to what another user is allowed to see.

If you wish to grant users access to other user’s Jupyter Lab servers, override this configuration with due care over what you choose to expose.

Cylc User Authorisation

Cylc Authorisation is configurable on a per-user and per-command basis but not on a per-workflow basis.

By default users can only see and interact with their own workflows.

Sites can restrict the permissions that users are allowed to grant each other and can configure default permissions (see Cylc Site Configuration).

Authorization is configured by these two configurations:

Example:

# ~/.cylc/uiserver/jupyter_config.py

c.CylcUIServer.user_authorization = {
    # <user/group>: [<permission>, ...],

    # allow "user1" to monitor my workflows
    "user1": ["READ"],

    # allow "user2" to monitor and trigger tasks in my workflows
    "user2": ["READ", "Trigger"],
}

Users

There are three methods of identifying a user to grant access to:

<username>

Configures permissions for a single user.

group:<groupname>

Configures a user group. For more information, see Group Support.

*

Configures permissions for any authenticated user (see Jupyter Hub authenticators reference for details).

Note

Using glob patterns to match user and group names is not currently supported.

Permissions

Permissions can be granted for each Cylc command individually. For convenience, commands are arranged into groups to avoid having to list them individually:

READ (i.e. read-only access)

A user with read permissions may view workflows, monitor tasks states and open log files, but they cannot interact with the workflows.

  • Read

CONTROL (e.g. start & stop workflows)

A user with control permissions may issue commands to interact with workflows and can start/stop workflows but cannot redefine the workflow configuration itself (without direct filesystem access).

  • Clean

  • Ext-trigger

  • Hold

  • Kill

  • Message

  • Pause

  • Play

  • Poll

  • Release

  • ReleaseHoldPoint

  • Reload

  • Remove

  • Resume

  • SetGraphWindowExtent

  • SetHoldPoint

  • SetOutputs

  • SetVerbosity

  • Stop

  • Trigger

ALL (i.e. full control)

A user with all permissions may alter task configuration so may inject arbitrary code into the workflow.

  • Broadcast

Note

With the exception of Read all of the above permissions map onto the Cylc GraphQL mutations which themselves map onto the command line.

E.G. the Play permission maps onto mutation play in the GraphQL schema and cylc play on the command line.

To find out more about a command, see the GraphQL or CLI documentation.

By default, users have full permissions (READ, CONTROL and ALL) for their own workflows and no permissions for other users’ workflows.

Permissions are additive, so for example, granting READ and CONTROL would provide all of the permissions from those two groups.

The ! character can be used to subtract permissions, e.g. delegating CONTROL and !Stop would provide all control permissions except stop.

Note

Granting access to a group does not automatically grant access to lower groups e.g. granting CONTROL access does not automatically grant READ access.

Examples

# ~/.cylc/uiserver/jupyter_config.py

c.CylcUIServer.user_authorization = {
    "*": ["READ"],
    "group:groupA": ["CONTROL"],
    "user1": ["read", "pause", "!play"],
    "user2": ["!ALL"]
}

In this scenario:

  • "*" represents any authenticated user. They have permission to view all workflows on the GUI.

  • "group:groupA" applies CONTROL permissions to any member of system groupA. Note that, since permissions are inherited, these users will gain READ access from the "*":["READ"] assignment.

  • "user1" will have permission to view workflows, pause but not play workflows, even if user1 is a member of the system groupA. This is due to negations taking precedence over additions.

  • "user2" is not permitted to view workflows, or perform any operations.

Cylc Site Configuration

The c.CylcUIServer.site_authorization configuration allows sites to configure sensible defaults and limits for the permissions users can grant.

It takes the form:

{
  "<owner>": {
    "<user>": {
      "default": [],
      "limit": []
    }
  }
}

Where <owner> is the username of the account that is running a server and <user> is the username of an account trying to connect to it.

Sites can set both limits and defaults for users:

limit

Determines the maximum access users can grant to their workflows.

default

Sets a default access level, which applies if the user does not appear in the user_authorization configuration (via explicit user name or group).

Note, these defaults apply only if a user does not appear in c.CylcUIServer.user_authorization.

  • If a limit is not set but a default is, then the limit is the default.

  • If a default is not set but a limit is, then the default is no access.

Note

As the UI Server runs as the workflow owner, the owner has full control over it and in theory may bypass these restrictions in a variety of ways. As an extreme example, a workflow owner could pass their account credentials to another person. This cannot be prevented by technical means. However, a workflow owner cannot unilaterally gain access to any other user’s account or workflows by configuring their own UI Server.

Note

Changes to the Cylc authorization configuration will take effect when the Cylc UI Server is restarted.

Group Support

Unix-like systems support user groups. Cylc authorization supports granting access by membership of these system groups. You can indicate a system group by using the group: indicator.

System groups are found by get_groups

cylc.uiserver.authorise.get_groups(username)[source]

Return a list of system groups for given user.

Uses os.getgrouplist and os.NGROUPS_MAX to get system groups for a given user. grp.getgrgid then parses these to return a list of group names.

Parameters:

username (str) – username used to check system groups.

Returns:

System groups for username given

Return type:

Tuple[List[str], List[str]]

Example Site Authorization Configuration

Whilst most site configurations will be simpler than the example below, this example provides an indication of the combinations available.

# /etc/cylc/uiserver/jupyter_config.py

c.CylcUIServer.site_authorization = {
    "*": {  # For all ui-server owners,
        "*": {  # Any authenticated user
            "default": "READ",  # Will have default read-only access
        },
        "user1": {  # for all ui-server owners, user1
            "default": ["!ALL"],  # has no privileges by default
        },  # No limit set, so all ui-server owners are unable to permit user1
    },
    "server_owner_1": {  # For specific UI Server owner, server_owner_1
        "*": {  # Any authenticated user
            "default": "READ",  # Will have default read-only access
            "limit": ["READ", "CONTROL"],  # server_owner_1 is able to give away
        },  # READ and CONTROL privileges.
    },
    "server_owner_2": {  # For specific UI Server owner,
        "user2": {  # Specific user2
            "limit": "ALL"  # Can only be granted a maximum of ALL by
        },  # server_owner2, default access for user2 falls back to
        # standard READ only (if server_owner_2/user2 are
        # included in other auth config e.g. the top example),
        # or none if not in any other auth config sections.
        "group:groupA": {  # group denoted with a `group:`
            "default": [
                "READ",
                "CONTROL",
            ]  # groupA has default READ, CONTROL access to server_owner_2's
        },  # workflows
    },
    "group:grp_of_svr_owners": {  # Group of users who own UI Servers
        "group:groupB": {
            "default": "READ",  # can grant groupB users up to READ and CONTROL
            "limit": [  # privileges, without stop and kill
                "READ",
                "CONTROL",
                "!stop",  # operations
                "!kill",
            ],
        },
    },
}

Interacting with Others’ Workflows

If you have been granted access to another user’s workflows, you can view and interact with these workflows. Say, you, userA, wish to interact with userB’s workflows. You can do this by navigating to the URL https://<hub>/user/userB, using the hub of userB. You should authenticate as yourself (userA) and, provided you have the correct permissions, you will see userB’s workflows for interaction.

Note

Operations that are not authorized will appear greyed out on the UI.

Troubleshooting Authorization

If authorization is not performing as expected, check

  • you are permitted by the site configuration to give away access.

  • you have provided read permissions, which enables the user to see your workflows.

  • check the spelling in your configuration. The correct spelling is c.CylcUIServer.user_authorization