Arborist is an attribute-based access control (ABAC) policy engine, designed for use with the Gen3 stack. Arborist tracks resources requiring access control, along with actions which users may perform to operate on these resources, and roles, which aggregate permissions to perform one or more actions. Finally, policies tie together a set of resources with a set of roles; when granted to a user, a policy grants authorization to act as one of the roles over one of the resources. Resources are arranged hierarchically like a filesystem, and access to one resource implies access to its subresources.
For example, a simple policy might grant a role metadata-submitter on the
resource /projects/project-abc. Now users which are granted this policy can
perform the actions that being a metadata-submitter entails, for all resources
under project-abc.
In the Gen3 stack, arborist is integrated closely with fence. Fence acts as the central identity provider, issuing user tokens (in the form of JWTs) containing the list of policies in the arborist model which are granted to that user. Other microservices needing to check user authorization to operate on a resource can statelessly verify the user's authorization, making a request to arborist with the user's JWT and receiving a response for the authorization decision.
The key documentation is below. Additional documentation can be found in the /docs directory.
OpenAPI documentation available here.
The YAML file containing the OpenAPI documentation can be found in the docs folder.
You will need these:
- Go
- PostgreSQL
- a Postgres superuser--Arborist uses the Postgres
ltreeextension module, and theCREATE EXTENSIONcommand must be run by a database superuser.
# clone it
go get -u github.com/uc-cdis/arborist
# cd into wherever arborist is cloned (this is the default)
cd ~/go/src/github.com/uc-cdis/arborist
# build the code
make
# set up database; use whatever values for database name etc. you like,
# but PGUSER must be a postgres superuser
export \
PGDATABASE=arborist_test \
PGUSER=username \
PGPASSWORD=password \
PGHOST=localhost \
PGPORT=5432 \
PGSSLMODE=disable
# export any other PG variables as necessary. `PGSSLMODE=disable` is required, though.
# create the database for the first time
createdb
# bring the database schema up to the latest version
./migrations/latest
# example command to run the server (see also `--help`):
./bin/arborist --port 8080 --jwks https://dev.planx-pla.net/user/.well-known/jwksYou can now deploy individual services via Helm! Please refer to the Helm quickstart guide HERE (https://github.com/uc-cdis/arborist/blob/master/docs/quickstart_helm.md)
There is also the Dockerfile in the root directory which is used to build a
Docker image for the server. In short, run this command from the root directory
to build an image:
docker build -t arborist .Run the docker image:
docker run -p 8080:8080 arborist --port 8080 # plus other arguments(This command exposes arborist on port 8080 in the docker image, and maps port 8080 from the docker image onto 8080 on the host machine.) See also Docker documentation for more details on how to use Docker.
Clone/Build/Install all-in-one command:
go get -u github.com/uc-cdis/arboristThe cloned source code can be found under $GOPATH, usually ~/go/ if not set.
In the source folder, you can run go install to rebuild the project. The
executable can be found under $GOPATH/bin/, which you may want to add to your
$PATH if not done yet.
If you have already checked out the repository locally, you can build the executable from there directly, which will include any local changes during development.
Running make will build the code:
makeBe aware that the source code must have been
cloned correctly into
$GOPATH, see also the previous section. go build will not work correctly if
you cloned the repository outside of the location that go expects. One option
to work around this, if you prefer to work with the code elsewhere in the
filesystem, is to create a symlink from the desired location to wherever the
repository lives under $GOPATH.
We will start from the lowest-level definitions, and work upwards.
- Action: a method (identified by a string) on a service (also identified by string) and generally correspond directly to an endpoint or simple operation done through the given service. An example might be something like this:
{
"service": "fence",
"method": "presigned-url-download",
}
- Permission: a combination of an action, and some optional constraints (key-value pairs which restrict the context of the action).
- Role: collections of permissions. Roles are uniquely identified by an ID field.
- Resource: anything that should be access-controlled, organized like
directories of a filesystem. Resources are uniquely identifiable by their full
path. Resources can have "child" resources (subdirectories, or
sub-subdirectories etc., in the filesystem model), where access to one
resource implies access to all the resources below it. A resource might be,
for example, an entire project in a data commons, like
/projects/project-abc. - Policy: combines a set of roles and resources together, with the meaning that the policy allows access to use any of its roles on any of its resources. Policies are granted to users and groups to give them access.
- Group: a set of users. Groups can be granted their own policies; this gives access to that policy for all users in the group.
Ultimately, the flowchart for granting access to users goes something like the following:
- Create roles, resources, and policies to define access.
- One or more of the following:
- Using groups:
- Grant a policy to a group (
/group/{group}/policyendpoint). - Add users to the group (
/group/{group}/userendpoint). - Users in the group now have access.
- Grant a policy to a group (
- For generic permissions which should be granted to all users (including
anonymous users, or only those who are logged in, i.e. have a JWT):
- Grant a policy to the built-in
anonymousand/orlogged-ingroups. (/group/anonymous/policyand/group/logged-in/policyendpoints) - Now users even without a JWT have access using the policies granted to
the
anonymousgroup, and all users with just a JWT have access to the policies for thelogged-ingroup. (All users are implicitly considered part of thelogged-ingroup.)
- Grant a policy to the built-in
- Specifying permissions for individual users directly:
- Grant an individual user a policy (
/user/{username}/policyendpoint). - Now that user has access.
- Grant an individual user a policy (
See development documentation.
See DB diagram.