Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How do we handle authorization? #275

Open
thomastaylor312 opened this issue Jan 19, 2022 · 0 comments
Open

How do we handle authorization? #275

thomastaylor312 opened this issue Jan 19, 2022 · 0 comments
Labels
question Further information is requested

Comments

@thomastaylor312
Copy link
Contributor

This is an in progress document containing the brainstorming around how we want to handle authorization in bindle. This comes from a HackMD where various maintainers collaborated on figuring things out and it has been cleaned up and updated. Any ideas or feedback is welcome!

Goals/Non-goals

The following goals and non-goals are specifically for 1.0. This doesn't mean we don't want them in the future.

Goals:

  • OAuth tokens can be authorized against resources
  • File-based ACL rules

Non-goals:

  • Database (or other datasource) ACL rule sources
  • Role configuration endpoint (TODO: Maybe we actually want this for 1.0?)
  • Per parcel rules (i.e. access to only specific parcels of a Bindle)

Use cases

Verbs

There are two main verbs used across all bindle resources:

  • get
  • create

For invoices, there is an additional verb called yank. If necessary, this could be changed to delete to match other conventions.

Expected user interactions

Below is the list of expected interactions with the Bindle server covered by authorization flow:

TODO: Allow version specific restrictions

  • Anonymous read access: This will be the default authorization mode for all bindles (a la dockerhub)
  • Read (get) access: A user can only access an invoice and its parcels if logged in and granted access
    • This kind of access can be granted on specific bindles (example.com/foo), but not specific versions of a bindle (example.com/foo/1.0.0)
    • This access can also be granted on any subpath of a bindle. So given a bindle of example.com/catblog/foo/1.0.0, access can be granted to anything under the example.com subpath or it can be granted to example.com/catblog to further restrict access
  • Create (create) access: A user can only create an invoice and upload parcels for invoices if logged in and granted access
    • This kind of access can be granted on specific bindles (example.com/foo), but not specific versions of a bindle (example.com/foo/1.0.0)
    • This access can also be granted on any subpath of a bindle. So given a bindle of example.com/catblog/foo/1.0.0, access can be granted to anything under the example.com subpath or it can be granted to example.com/catblog to further restrict access. In the example.com case, a user would be able to create invoices and upload parcels with any ID starting with example.com. In the example.com/catblog example, a user would be able to create any number of bindles and parcels under that subpath.
  • Yank (yank) access: A user can only yank an invoice if logged in and granted access
    • This kind of access can be granted on specific invoices (example.com/foo), but not specific versions of an invoice (example.com/foo/1.0.0)
    • This access can also be granted on any subpath of a bindle. So given a bindle of example.com/catblog/foo/1.0.0, access can be granted to anything under the example.com subpath or it can be granted to example.com/catblog to further restrict access. In the example.com case, a user would be able to yank invoices with any ID starting with example.com. In the example.com/catblog example, a user would be able to yank any number of invoices under that subpath.

Type restrictions

It is important to note that any access control lists will also need to specify types due to the arbitrarily pathy nature of Bindle IDs. When mapping roles for an invoice, an additional type restriction must be specified. These types are specified below:

  • bindle: Indicates that this path is a full bindle path and isn't a subpath. So a create role on example.com/foo with a bindle type means that only an example.com/foo bindle can be created. It will prevent a user from creating an example.com/foo/bar bindle. This is the default rule if no type is specified
  • subpath: Indicates that this path is a subpath, granting the user the right to do anything with the allowed verb under that subpath. So a create role on example.com/foo with a subpath type means that user can arbitrarily create any bindle starting with the example.com/foo subpath, but CANNOT create a bindle with that exact path.

Authorization flow

This section discusses how the authorization flow will work in our code at a high level.

Groups/User mapping

Both types of authentication must have a type that implements the Authorizable trait:

pub trait Authorizable {
    /// Returns the identity or username of the authenticated user
    fn principal(&self) -> String;

    /// Returns the groups the authenticated user is a member of, generally embedded on something
    /// like a JWT or fetched from an upstream server
    fn groups(&self) -> Vec<String>;
}

This will allow the server to be able to check the authenticated user against the configured ACL list.

Token based

The username from a JWT will be pulled from the sub claim. If this is not present, the token will not be accepted. For simpler group management (i.e. not needing to contact an external server to fetch the groups), we will require the presence of a custom groups claim. This is a common field on many JWTs and so should not be much of a hassle to use with third party OAuth providers. The list of groups from the token would be returned wholesale using the groups() method.

Option 2: Biscuit tokens

Another option could be to use biscuit tokens, as a way to contain the ACL for the request rather than with specific file based ACL rules (as described below)

ACL creation and maintenance

For 1.0, the plan will be to only use a config file for ACL policies (most likely a TOML file). The structure of this file will be determined by the library we end up using for the rules engine and so it is not defined here.

As stated before, database support is non-goal, but is something we want to add in the future. Accessing the rules should be behind a trait for easy extension in the future.

Rules engine

This is the biggest unknown at the current time. Early ideas for the rules engine involve something similar to the Upspin project for handling the ACLs and actual authorization. However, this project is written in Go and would be hard to consume within Rust. It does lead us to the other option of implementing our own rules engine, possibly based on Upspin. The downside of this option, no matter how it is implemented, is that we'd have a whole engine to use from scratch. This upside is we could design it as a separate crate with flexibility in rule sources and contribute something useful to the Rust community. The Oso project also looks interesting, but it is new and little-used. It also has the additional downside of locking us into its custom "Polar" language format for our rules.

@thomastaylor312 thomastaylor312 added the question Further information is requested label Jan 19, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Further information is requested
Projects
None yet
Development

No branches or pull requests

1 participant