Skip to content

Conversation

mohamedelabbas1996
Copy link
Contributor

@mohamedelabbas1996 mohamedelabbas1996 commented Jan 24, 2025

Summary

This PR introduces fine-grained object-level permissions for the Project model and related entities using Django Guardian. It establishes a roles and permissions framework, including Project Manager, Basic Member, and Identifier roles, with appropriate permissions for each.

List of Changes

  • Implemented object-level permissions for Project and related entities.
  • Added role-based access control (Project Manager, Basic Member, Identifier).
  • Auto-assign Project Manager role to the project owner.
  • Auto-assign Basic Member role to project members.
  • project_id query parameter made optional for all project related entities views.

Related Issues

Closes #703 #219

Detailed Description

This PR implements a fine-grained permissions framework for the Project model and its related entities:
Job, Deployment, SourceImageCollection, S3StorageSource, Site, Device, Identification. This ensures that access is properly restricted and prevents unauthorized modifications to project-related entities.

Roles and Permissions:

  • Project Manager: Full permissions on the project and its entities. Auto-assigned to the project owner upon creation.
  • Basic Member: Can access private project data and star captures. Auto-assigned to project members.
  • Identifier: Can create, update, and delete Identifications created by the user.

Deployment Notes

  • Requires database migrations to support role assignments.
  • Ensure Django Guardian is properly installed and configured.
  • Run manage.py assign_roles --source "Identifiers CSV file URL " to reset permissions and assign roles for existing project owners and members and assign identifier, project manager roles to users based on a CSV file.
  • Post an update on multiple channels (Slack and perhaps over email). Also confirm which projects users should be apart of.

Checklist

  • I have tested these changes appropriately.
  • I have added and/or modified relevant tests.
  • I updated relevant documentation or comments.
  • I have verified that this PR follows the project's coding standards.
  • Any dependent changes have already been merged to main.
  • Merge in main branch & fix conflicts
  • Test with db snapshot (michael), real projects

Follow-up PRs:

  • Read-permissions (filter list views by current user)
  • Support for many to many relationship to projects

Copy link

netlify bot commented Jan 24, 2025

Deploy Preview for ami-dev canceled.

Name Link
🔨 Latest commit 0d67332
🔍 Latest deploy log https://app.netlify.com/sites/ami-dev/deploys/67c0d7e664be8100080b1ae8

@mohamedelabbas1996
Copy link
Contributor Author

Screenshot 2025-01-27 at 00 51 58 Screenshot 2025-01-27 at 00 54 30

@mohamedelabbas1996 mohamedelabbas1996 marked this pull request as ready for review January 27, 2025 06:00
@mohamedelabbas1996 mohamedelabbas1996 changed the title [Draft] User Permissions User Permissions Jan 27, 2025
@mihow
Copy link
Collaborator

mihow commented Jan 27, 2025

This looks really promising, great work @mohamedelabbas1996. I will review asap!

@mihow mihow mentioned this pull request Jan 27, 2025
…ssions

- Added ProjectMixin to require  for all project-related entities
- Ensured project_id is extracted only from URLs matching /projects/<id>/, otherwise falls back to query params
- Implemented permission inheritance from the associated project object
- Exposed user_permissions field in responses to reflect entity  permissions
@mohamedelabbas1996
Copy link
Contributor Author

mohamedelabbas1996 commented Jan 31, 2025

@mihow currently setting require_project to False to stay compatible with the frontend, but we can easily change it when we need

created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)

def get_project(self):
Copy link
Collaborator

@mihow mihow Feb 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Follow up proposal for objects that are shared between multiple projects (have a many2many): check for many2many, if instance belongs to multiple projects, they must be a superuser or permission denied. If a single project, then return that project as usual.

known many2many

Pipeline
Processing Service
Taxa
TaxaList

from ami.main.api.views import DefaultViewSet
from ami.utils.fields import url_boolean_param
from ami.utils.requests import get_active_project, project_id_doc_param
from ami.utils.requests import project_id_doc_param
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can the decorator be moved to the mixin?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried moving it to the mixin but it did not work, it seems that it should be used with a Viewset class

"""
Set the user to the current user.
"""
# Get an instance for the model without saving
Copy link
Collaborator

@mihow mihow Feb 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a pre-save method? or a way to make this reusable in the DefaultViewSet?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

moved it to the DefaultViewset

assign_perm(perm, user, project)

@classmethod
def un_assign_user(cls, user, project):
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"unassign"

@mihow
Copy link
Collaborator

mihow commented Feb 4, 2025

From our call, the two most important roles to start with are:

  • Project manager (can edit project and all related objects under project, start any type of job, etc)
  • Identifier (can suggest IDs / Agree with IDs for any project they are an identifier for)

Identifier role

  • Create identification
  • Delete identification created by current active user (self)

Project manager role

  • Any write or destructive action on child object of project
  • Fine-grained permissions can come for later

@mihow mihow changed the title User Permissions Framework for User Permissions & Roles Feb 5, 2025
@mihow
Copy link
Collaborator

mihow commented Feb 24, 2025

Discussion for next iteration after UI is ready:

  • Associate the Project with Groups in a more direct way (Add custom Group model with a project field, or a Many2Many field on the Project model to permission_groups)

@classmethod
def assign_user(cls, user, project):
# Get or create the Group
group_name = f"{project.pk}_{project.name}_{cls.__name__}"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

one proposal:

class Project
    permission_groups = manytomany(Group, through=ProjectRoleGroup)

class ProjectRoleGroup
    project_id
    group_id
    role_slug

project_role_group = ProjectRoleGroup.objects.get(project=1, role_slug="dfds", group_id=2) 


@classmethod
def assign_user(cls, user, project):
# Get or create the Group
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a @todo comment about making this a formal relationship between groups, roles & project

logger.info(f"Project name: {instance.name}")

for group in groups:
logger.info(f"Old permissions group name: {group.name}")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will you remove this log statement and instead add the old group name to this log statement later on? Then it will only be logged if its actually being modified:

logger.info(f"Changing permission group name from {group.name} to: {new_group_name}")

@annavik
Copy link
Member

annavik commented Feb 25, 2025

Testing Feb 25th

Summary

I have tested for 6 different permission scenarios. For the testing, I have assigned myself different roles from admin and then interacted with the app in different ways.

Works really well now, great work! Just had some minor questions.

Questions

  • When I'm added as member to a project, I still can't see it under "My projects" (I also have to update the project itself from admin). Just want to clarify if this is the plan?
  • Should basic members no longer have access to edit project details?
  • Project managers cannot update, but delete, starred collection. Should deleting this collection be possible?

Test results

Not logged in
Works well!

Basic member
Works well, just a minor question.

Identifier
Works well!

Project manager
Works well, just a minor question.

Staff
Works well!

Superadmin
Works well!

@mohamedelabbas1996
Copy link
Contributor Author

Testing Feb 25th

Summary

I have tested for 6 different permission scenarios. For the testing, I have assigned myself different roles from admin and then interacted with the app in different ways.

Works really well now, great work! Just had some minor questions.

Questions

* When I'm added as member to a project, I still can't see it under "My projects" (I also have to update the project itself from admin). Just want to clarify if this is the plan?

* Should basic members no longer have access to edit project details?

* Project managers cannot update, but delete, starred collection. Should deleting this collection be possible?

Test results

Not logged in Works well!

Basic member Works well, just a minor question.

Identifier Works well!

Project manager Works well, just a minor question.

Staff Works well!

Superadmin Works well!

Thanks so much for doing another round of tests!

  • Yes, currently, a user needs to be explicitly added as a member from the project page in the admin dashboard to see the project under "My Projects." This is the expected behavior for now, but we can discuss adding users automatically based on their assigned roles in a follow-up PR if that makes more sense.

  • Yes, we have restricted Basic Members from editing project details. It makes more sense for this action to be reserved for Project Managers, who have more control over project settings.

  • I agree that deleting a starred collection should be reconsidered. Maybe we could make the starred collection a user-specific entity within a project so that each user has their own starred images collection instead of modifying a shared one? I think this could give users more control over their starred captures.

@mohamedelabbas1996
Copy link
Contributor Author

  • When I'm added as member to a project, I still can't see it under "My projects" (I also have to update the project itself from admin). Just want to clarify if this is the plan?

Now a project appears in the "My Projects" tab if the user assigned to a role in the given project.

try:
with transaction.atomic(): # Ensure DB consistency
for group in Group.objects.filter(pk__in=pk_set):
parts = group.name.split("_") # Expected format: {project_id}_{project_name}_{Role}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will you keep track of any function that is using the group name to relate to projects? So we can refactor at the same time

@mihow mihow merged commit 727ed26 into main Mar 6, 2025
2 checks passed
@mihow mihow deleted the feat/user-permissions branch March 6, 2025 17:46
@mihow
Copy link
Collaborator

mihow commented Mar 6, 2025

Follow-up notes:

  • How to let anyone create a project (model level permission?)
  • Do we need project owners?
  • Who can delete a project? Who can delete identifications and images, etc?
  • Consider adding a ProjectAdmin
  • Project<->Group<->Role relationship (see test branch)
  • Handle occurrences not related to any project.

@mihow
Copy link
Collaborator

mihow commented Mar 6, 2025

@mihow write announcement message about the new roles & project assignements

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

Successfully merging this pull request may close these issues.

Add roles for different user actions (permission groups) Add object-level permissions for Projects & related entities

3 participants