Authentication and Authorization

Background reading: Pyramid’s security system.

I extend Pyramid’s built in ACL based security system with my pyramid_localroles plugin so we can map permissions to roles (e.g. ‘role.lab_submitter’) rather than directly to users.

For more on roles and local roles see:

Authentication

An authentication policy identifies who you are, returning a user id. We use pyramid_multiauth to extract authentication from any of Persona, session cookies, or HTTP basic auth (access keys).

Authorization

From the authenticated user id, the groupfinder in authorization.py maps the user id to a number of “principals”, user or group identifiers. We lookup the user object and add groups based on the properties:

  • groups [<string>..] - global groups like ‘admin’. Generates: group.admin.
  • submits_for: [lab..] - allow editing based on object.lab property. Generates: submits_for.<lab-uuid>.
  • viewing_groups: [<string..>] - allow viewing of in progress data based on object.award.viewing_group (ENCODE, GGR, REMC.) Generates: viewing_group.ENCODE.

Views are protected by permissions (view, edit, etc.)

When you PUT to /experiment/ENCSR123ABC/ then Pyramid will traverse to the experiment object (see: Location aware resources) and lookup a view for to PUT which is protected with the edit permission.

At this point my pyramid_localroles plugin steps in and extends the authenticated principals passed to the ACLAuthorizationPolicy (the global groups that apply across the whole site) with location aware local roles such as role.lab_submitter and role.viewing_group_member by reference to the __ac_local_roles__ method (base.py) of the context object which returns a mapping based on the context object’s ‘lab’ and award property, e.g:

{
  'submits_for.<context-lab-uuid>': ['role.lab_submitter'],
  'viewing_group.<context-award-viewing_group>'': ['role.viewing_group_member'],
}

The ACL authorization policy will then lookup the Access Control List on the experiment object (the ‘context’) by looking at its __acl__ property/method, and then the __acl__ property/methods of its parents (the /experiments collection and the root object.) We define an __acl__ method on the EncodedRoot object (root.py), Collection and Item objects (base.py.) The __acl__ method for an Item returns a different ACL list depending on the object’s ‘status’. This way we allow lab submitters to edit their own ‘in progress’ objects but not ‘released’ objects.

Permissions

  • add
  • add_unvalidated (admin)
  • edit
  • edit_unvalidated (admin)
  • expand (system)
  • forms - who can see forms
  • impersonate (admin)
  • import_items (admin)
  • index (system)
  • list
  • search
  • submit_for_any (admin)
  • view
  • view_details - protection of user contact information
  • view_raw (admin)
  • visible_for_edit - hiding deleted child objects from edit

This gnu grep expression will extract a list of permissions (brew tap homebrew/dupes; brew install grep):

$ ggrep --no-filename -roP "(?<=permission[=(]['\"])[^'\"]+" src/ | sort | uniq