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

Column-level permissions #110

Closed
jace opened this issue May 31, 2017 · 1 comment
Closed

Column-level permissions #110

jace opened this issue May 31, 2017 · 1 comment

Comments

@jace
Copy link
Member

jace commented May 31, 2017

As outlined in #100 subitem 6, Coaster's current PermissionMixin mechanism is inadequate for query-based API calls (like GraphQL), as PermissionMixin only controls access to an API endpoint. We need a finegrained mechanism that determines read and write access to each column in a model.

We need a new RoleAccessMixin that provides mechanisms for defining and controlling access per column. It works like this:

  1. Access is defined in terms of roles rather than permissions.

  2. For each column, a list of read- and write-access roles are defined. (To be decided: does write access automatically imply read access? It does not in the case of password fields, but are there any other common examples?)

  3. Access may be defined by adding a __roles__ dictionary to the model that uses the column name (or name of any attribute on the model) as the key, with the value as either a dictionary with write and read keys, or a two-tuple in (write_roles_list, read_roles_list) order, or a single-member tuple (write_roles_list) where read access is implicitly (and only) granted to anyone with write access. The roles are always a list. Subclasses must define roles as __roles__ = ParentClass.__roles__ + {…}. (Subclassing will require a bit more consideration, for whenever we actually run into the problem.)

  4. This variable syntax for __roles__ makes defining it easy but reading it harder, so either we need a metaclass that rewrites it (similar to LabeledEnum's), or we restrict syntax to just the dictionary method {'column': {'read': ['all'], 'write': ['owner']}.

  5. Analogous to PermissionMixin's permissions method, which accepts user and inherited parameters, we now have a roles method that takes the same parameters and returns all roles available to the user.

  6. The roles method also accepts a token object instead of a user object. Tokens (as defined in API access architecture #100 subitem 3) using libmacaroons can provide access to a subset of a user's resources. It is unclear at this time how tokens will be defined, and a separate ticket is pending.

  7. The role names all and user are reserved. The all role is always present, and the user role is granted to anyone logged in (ie, the user parameter to the roles method is a valid user or token).

  8. Beware of having an owner role. As we've learnt in our user concepts exercise, creator and owner are distinct concepts. It helps to define these clearly at the start, perhaps as additional comments on this ticket.

  9. We still need a mechanism to control access. A method named access_for (tentative name) takes a user, token or roles parameter (any one). If it gets user or token, it calls the roles method to get a list of roles. It then returns a wrapper dictionary object (with attribute access for easier syntax in Python code) that contains only the attributes that were in the __roles__ dictionary and for which the caller has a matching role. For the sake of efficiency, it lazy-loads contents.

  10. The GraphQL API always returns the result of the access_for method, never the raw database object.

@jace
Copy link
Member Author

jace commented May 31, 2017

Duplicate of #109 (thanks to GitHub's temporary breakage).

@jace jace closed this as completed May 31, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant