-
Notifications
You must be signed in to change notification settings - Fork 3
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
Refactor user permissions #454
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not super familiar with this part of the code, but the changes look sensible to me.
for clarity, it might be better to separate the logic change from the black reformatting, but no big deal 🤷
class UserPermissions(): | ||
manage: bool | ||
manage_membership: bool | ||
|
||
def __init__(self, manage, manage_membership): | ||
self.manage = manage | ||
self.manage_membership = manage_membership | ||
|
||
@staticmethod | ||
def _get_user_project_role_scopes(keycloak_client, username, project): | ||
role, scopes = keycloak_client.get_user_project_role_scopes( | ||
username, get_charge_code(project) | ||
) | ||
logger.info("SCOPES===") | ||
logger.info(role) | ||
logger.info(scopes) | ||
return role, scopes | ||
|
||
@staticmethod | ||
def _parse_scopes(scopes): | ||
# NOTE We have these two scopes defined in KC. In implementation, | ||
# we do not differentiate between them. See our docs for the specifics | ||
# https://chameleoncloud.readthedocs.io/en/latest/user/project.html#user-roles | ||
can_manage = "manage" in scopes or "manage-membership" in scopes | ||
return UserPermissions( | ||
manage=can_manage, # Can edit project metadata/allocations | ||
manage_membership=can_manage, # Can update members | ||
) | ||
|
||
@staticmethod | ||
def get_user_permissions(keycloak_client, username, project): | ||
_, scopes = UserPermissions._get_user_project_role_scopes( | ||
keycloak_client, username, project) | ||
return UserPermissions._parse_scopes(scopes) | ||
|
||
@staticmethod | ||
def get_manager_projects(keycloak_client, username): | ||
"""Gets project names for all project this user is a manager of | ||
""" | ||
return [ | ||
project["groupName"] | ||
for project in keycloak_client.get_user_roles(username) | ||
if UserPermissions._parse_scopes(project["scopes"]).manage | ||
] | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this looks like it's centralizing the mapping from keycloak scopes -> portal permissions, rather than a bunch of if-then? scattered around?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes. Now all of the permissions/scope code is here, and a block of code can check against this class for a portal permission. This should prevent bugs with manually parsing/interacting with keycloak scopes each time we check a user's role.
This fixes a bug where managers could do what they were supposed to. It seems this is caused by having 2 scopes in keycloak:
manage
andmanager-members
. We may have differentiated this at one point, but now we do now, there are PIs, managers, and members.This introduces a class UserPermissions that we can use to set these, rather than having to parse keycloak scopes everywhere we want to use permissions.
This also fixes a small bug in a migration.