Closed dancoates closed 3 days ago
Attention: Patch coverage is 72.03883%
with 144 lines
in your changes missing coverage. Please review.
Project coverage is 80.67%. Comparing base (
fd2c933
) to head (56054fa
).
Files | Patch % | Lines |
---|---|---|
db/python/connect.py | 75.00% | 20 Missing :warning: |
api/graphql/schema.py | 66.07% | 19 Missing :warning: |
api/routes/project.py | 32.00% | 17 Missing :warning: |
api/routes/analysis.py | 8.33% | 11 Missing :warning: |
db/python/tables/project.py | 85.18% | 8 Missing :warning: |
db/python/layers/seqr.py | 0.00% | 7 Missing :warning: |
api/routes/sample.py | 14.28% | 6 Missing :warning: |
api/utils/db.py | 60.00% | 6 Missing :warning: |
test/data/generate_data.py | 25.00% | 6 Missing :warning: |
api/routes/web.py | 28.57% | 5 Missing :warning: |
... and 18 more |
:umbrella: View full report in Codecov by Sentry.
:loudspeaker: Have feedback on the report? Share it here.
What's your sense with how consistent our terminology is for referring to an authenticated "customer". We use author a lot, but I wonder if that's lazy, or something more accurate.
Yeah I mused on this a bit when working on the changes. In the past I've used the concept of a "viewer" which is defined to mean the currently active or viewing user. I think that name was a bit of a quirk of my last job though, I haven't seen it used elsewhere. "AuthenticatedUser" would be a more verbose but clearer name. At the moment the Connection
class is performing that role, we could probably change that name as we kind of have two concepts of a connection - an API connection and a database connection, leading to a few places where you have connection.connection
.
I think actor
is another term that is used commonly for this
Awesome, super clean!
Not blocking this PR, but can you write up a bit of documentation for how this works, and how developers should interact with it? For example, how project_admin permission works, when that role isn't given by cpg-infra (I see how it works in code, but worth writing down). And any handy methods people should take note of :)
Yeah, good thinking. I'll definitely write up some docs
Context
Metamist currently has a fairly simple permission system. Each project defines a read group and a write group, these two groups define a list of group members. The members of the read group will have readonly access to the project, and the members of the write group have full unrestricted write access to the project.
Write actions within metamist are primarily performed programmatically via the API, but with some upcoming new features there will soon be the need to allow human users to perform a limited set of write actions. Some examples of these new features are:
We do not want to grant full write access to allow users to perform these actions, so it is necessary to create a more nuanced role based permissions system that allows limited access to certain write operations but restricts access to anything destructive.
Solution
Moving away from storing the majority of permissions in groups to creating a direct relationship between users and projects. There is a lot of unnecessary redundancy from the way permissions are stored currently. By convention a
read
group will be suffixed with-read
and similar for a write group. These are then referenced in theread_group_id
andwrite_group_id
columns in theproject
table. To continue extending this would result in a complex system with lots of columns on theproject
table and potentially a situation where the group suffixed with-read
ends up being the write group.This update introduces a new table
project_member
. This table holds the relationship between a member (represented by an email) and the project. In addition it includes arole
column which specifies a role that the user has within that project. Each member can have multiple roles within a project by there being multiple rows for them in theproject_member
table for any given project.In addition to the table updates, this PR also moves much of the permissions checking logic away from the
tables/project.py
file which acts as the interface for projects in the database. Instead this logic is now on the Connection class. This class is available pretty much everywhere throughout the project so it makes it much easier to quickly check permissions. This change also mitigates a class of performance problems that was starting to occur in the graphql resolvers where permissions checks were not being cached between resolvers. The Connection object is constructed once at the beginning of every request and all permission checks from then on are synchronous and should be very fast.A permission check looks like:
Where
ReadAccessRoles
is a set of the project member role enums. If the current user has any one of the specified roles in all of the specified projects the permission check will pass.