Skip to content

Commit

Permalink
Merge branch 'restful-api-rw'
Browse files Browse the repository at this point in the history
  • Loading branch information
tsibley committed Feb 23, 2022
2 parents 4cce457 + 28edaee commit af4bbaf
Show file tree
Hide file tree
Showing 40 changed files with 1,490 additions and 242 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,6 @@ scratch

# test logs
/test/server.log

# docs build
/docs/build/
9 changes: 9 additions & 0 deletions .readthedocs.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
version: 2

sphinx:
configuration: docs/conf.py

python:
install:
- requirements: docs/requirements.txt
20 changes: 20 additions & 0 deletions docs/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Minimal makefile for Sphinx documentation
#

# You can set these variables from the command line, and also
# from the environment for the first two.
SPHINXOPTS ?=
SPHINXBUILD ?= sphinx-build
SOURCEDIR = .
BUILDDIR = build

# Put it first so that "make" without argument is like "make help".
help:
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)

.PHONY: help Makefile

# Catch-all target: route all unknown targets to Sphinx using the new
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
%: Makefile
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
8 changes: 3 additions & 5 deletions docs/api-charon.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Data API between clients and nextstrain.org
# Charon API

The main data communications between clients (such as Auspice) and the nextstrain.org server occur at URLs beginning with `https://nextstrain.org/charon`.
Currently the only client accessing these is Auspice (more specifically, a nextstrain.org customised version of Auspice).
Expand All @@ -14,11 +14,9 @@ Each handler is defined in an file of the same name within `src`.

### Authorization

Each handler is responsible for checking authorization by calling a `Source` class method like so:
Each handler is responsible for checking authorization:
```js
if (!source.visibleToUser(req.user)) {
return helpers.unauthorized(req, res);
}
authz.assertAuthorized(req.user, authz.actions.Read, source);
```

## Tests
Expand Down
46 changes: 27 additions & 19 deletions docs/api-restful.rst
Original file line number Diff line number Diff line change
Expand Up @@ -17,30 +17,15 @@ The API's design tries to:
making it possible to maintain backwards compatibility for existing clients.

The initial functionality is focused on dataset and narrative management
endpoints to support the `nextstrain remote`_ family of commands.[#]_ In the
future, we intend to expand the functionality and make this API a foundation
for serving other software clients and user audiences.

.. [#] Development was motivated by the goal for `Nextstrain CLI`_ to make
requests to nextstrain.org using normal user login credentials instead
instead of making requests directly to S3 using separate, per-user AWS IAM
credentials. An alternative solution of using temporary AWS credentials
provisioned by an AWS Cognito Identity Pool seemed like a clear choice
given we're using Cognito User Pools for authentication, but it wasn't
feasible to appropriately scope the credentials for each group of users due
to limitations of resource tags and IAM policy tag matching.
Proxying through nextstrain.org also gives us a lot more power to make the
API easier for clients to work with (e.g. auto-compressing for them,
setting resource metadata, validating schemas to prevent bad uploads, etc)
and makes backend changes easier to coordinate since clients won't be
directly accessing the storage backend.
endpoints to support the `nextstrain remote`_ family of commands (see
motivation_). In the future, we intend to expand the functionality and make this
API a foundation for serving other software clients and user audiences.

.. _RESTful: https://restfulapi.net
.. _content negotiation: https://developer.mozilla.org/en-US/docs/Web/HTTP/Content_negotiation
.. _nextstrain remote: https://docs.nextstrain.org/projects/cli/en/stable/commands/remote/
.. _Nextstrain CLI: https://docs.nextstrain.org/projects/cli/en/stable/

.. highlight:: none

Synopsis
========
Expand Down Expand Up @@ -277,3 +262,26 @@ The following narrative endpoints exist::
{GET, HEAD} /community/narratives/{user}/{repo}/*

{GET, HEAD} /fetch/narratives/*


.. _motivation:

Motivation
==========

Development was motivated by the goal for `Nextstrain CLI`_ to make requests to
nextstrain.org using normal user login credentials instead instead of making
requests directly to S3 using separate, per-user AWS IAM credentials. An
alternative solution of using temporary AWS credentials provisioned by an AWS
Cognito Identity Pool seemed like a clear choice given we're using Cognito User
Pools for authentication, but it wasn't feasible to appropriately scope the
credentials for each group of users due to limitations of resource tags and IAM
policy tag matching.

Proxying through nextstrain.org also gives us a lot more power to make the API
easier for clients to work with (e.g. auto-compressing for them, setting
resource metadata, validating schemas to prevent bad uploads, etc) and makes
backend changes easier to coordinate since clients won't be directly accessing
the storage backend.

.. _Nextstrain CLI: https://docs.nextstrain.org/projects/cli/en/stable/
133 changes: 133 additions & 0 deletions docs/authz.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
=============
Authorization
=============

nextstrain.org implements a role-based access control (RBAC) system for
authorizing who can view and manage resources likes datasets and narratives.
The same system is used for browser-based visitors and API clients. In this
system:

- Users are members of roles_.
- Objects (e.g. datasets or narratives) have tags_.
- Policies_ contain rules which define an allowed set of actions_ for a given
(role, tag) pair.
- Enforcement_ is performed by calling ``authz.assertAuthorized(user, action, object)``.

Currently roles, tags, and policies are entirely system-defined and hardcoded.
In the future, all three could be, in part, defined by users and
stored/retrieved as needed.

The design of this system is influenced by "`RBAC like it was meant to be`_".

.. _policies:

Policies
========

There is no single policy for all of nextstrain.org but different policies for
different parts of the site. Currently, policies are defined for and attached
to each :term:`Source`.

The design of the system allows for policies to be easily stacked or combined
(e.g. concatenate all the rules), so if necessary we could introduce a global
policy or other policy layers in the future.

Policies are arrays of objects, e.g.::

[
{ tag: authz.tags.Visibility.Public,
role: "*",
allow: [authz.actions.Read],
},
]

All three keys are required:

:tag: ``authz.tags`` symbol, or ``"*"`` to impose no restriction on object tag.
:role: Name of role as a string, or ``"*"`` to impose no restriction on user role.
:allow: List of ``authz.actions`` symbols.

The example above allows any user (anonymous or logged in) to see objects
marked public.

Roles and tags both ensure that policy rules are always many-to-many from the
start, even if a role only contains a single user (e.g. a single Group owner)
or a tag is only used for a single object. This property generally makes
management simpler and more consistent.


.. _roles:

User roles
==========

A user's roles are the names of the AWS Cognito groups of which they are a
member. Anonymous users have no roles.

Roles for a :term:`Nextstrain group <group>` are based on the name of the group
and name of the generic role within the group (viewers, editors, owners), e.g.
``blab/editors``.


.. _tags:

Object tags
===========

Tags are defined in the ``authz.tags`` object. Objects which are passed to
``authz.assertAuthorized()`` must have an ``authzTags`` property that is a Set_
of tags. Objects may choose to explicitly inherit tags propagated from their
container (e.g. a ``Resource`` object inheriting tags from its ``Source``
object).

.. _Set: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set

By using tags on objects, access policies like:

The SciComm team should be able to read and write narratives *but not
datasets*.

and:

The SARS-CoV-2 researchers should be able to read and write *"ncov"*
datasets and narratives in our Group *but not other datasets and
narratives*.

are expressible without requiring any changes to the policy syntax or authz
system design. (The first is implementable with the current tags; the second
would require a new system- or user-defined tag.)

If we implement user-defined tags, a good design to follow is the tag owner
system described in "`RBAC like it was meant to be
<https://tailscale.com/blog/rbac-like-it-was-meant-to-be/>`_".


.. _actions:

Actions
=======

Actions are defined in the ``authz.actions`` object. There are two available
actions: ``Read`` and ``Write``. In comparsion to the common CRUD model,
``Write`` encompasses create, update, and delete.

These two actions provide the only distinctions we need right now. If we need
finer control in the future, we can split up ``Write`` and/or add new actions.


.. _enforcement:

Enforcement
===========

The main enforcement function used to guard access-controlled code is::

authz.assertAuthorized(user, action, object)
It throws an ``AuthzDenied`` exception if the *user* is **not** allowed to
perform the *action* on the *object* as determined by the policy covering the
object (i.e. from the object's ``Source`` currently). Otherwise, it returns
nothing.

It is the responsibility of the enforcement function to determine the policy in
force for the given object.
28 changes: 28 additions & 0 deletions docs/conf.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
"""
Configuration file for the Sphinx documentation builder.
See <https://www.sphinx-doc.org/en/master/usage/configuration.html>.
"""

project = 'nextstrain.org'
copyright = '2022, Trevor Bedford and Richard Neher'
author = 'The Nextstrain Team'

primary_domain = 'js'
highlight_language = 'js'

default_role = 'literal'

html_theme = 'nextstrain-sphinx-theme'

extensions = [
'recommonmark',
'sphinx_markdown_tables',
'sphinx.ext.intersphinx',
]

intersphinx_mapping = {
'docs': ('https://docs.nextstrain.org/en/latest/', None),
'augur': ('https://docs.nextstrain.org/projects/augur/en/stable', None),
'auspice': ('https://docs.nextstrain.org/projects/auspice/en/stable', None),
}
102 changes: 0 additions & 102 deletions docs/glossary.md

This file was deleted.

Loading

0 comments on commit af4bbaf

Please sign in to comment.