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

Object-level permissions for hosting providers and data centers #481

Merged
merged 28 commits into from Jul 12, 2023

Conversation

tortila
Copy link
Contributor

@tortila tortila commented Jun 14, 2023

This is a draft pull request - documentation is still missing!
Closes #335.

This pull request introduces object-level permission system, which can be used to assign arbitrary number of users a permission to manage (through admin or API) an arbitrary number of hosting providers and data centers.

Scope of changes:

  • introduced a library django-guardian to provide a back-end for object-level permissions,
  • introduced custom permissions: manage_provider for Hostingprovider model and manage_datacenter for Datacenter model. The permissions are defined as singletons for re-using in the app (new module permissions.py).
  • extended GuardedModelAdminMixin provided by django-guardian so that only admins can access permission management screens, and only custom model permissions are displayed (excluding default Django permissions),
  • created a data migration to assign global permissions (i.e. to all objects in the database) for the admin group,
  • implemented data migrations so that existing relationships between Users and Hostingproviders, and Users and Datacenters are expressed as object-level permissions,
  • new fields: Hostingprovider.created_by and Datacenter.created_by, data populated for existing records based on the User.hostingprovider reference.
  • removed FK reference from User to Hostingprovider,
  • Both models, Hostingprovider and Datacenter have now helper properties: users and users_explicit_perms that each returns a queryset of related users based on defined permissions,
  • User model has now helper properties: hosting_providers and data_centers that each returns a queryset of related objects based on defined permissions,
  • admins for Hostingprovider, Datacenter and User models now include a list of related objects based on defined permissions,
  • provider portal home page lists all available hosting providers for a user based on defined permissions,
  • IP ranges / ASN API was updated to return objects across all providers that the requesting user has access to,
  • added new tests to cover the above & updated existing ones.

To do:

  • modify the ProviderSharedSecretView API, so that the provider is explicitly identified (this will be handled in a separate task),
  • add documentation,
  • deploy to staging & verify the functionality

@tortila tortila marked this pull request as ready for review June 14, 2023 14:54
@tortila
Copy link
Contributor Author

tortila commented Jun 14, 2023

@tortila tortila temporarily deployed to staging June 14, 2023 15:55 — with GitHub Actions Inactive
@tortila tortila temporarily deployed to staging June 14, 2023 16:19 — with GitHub Actions Inactive
@tortila tortila force-pushed the oz-object-level-permissions branch from 32deb62 to 671bdbf Compare June 14, 2023 16:57
@tortila tortila temporarily deployed to staging June 14, 2023 16:58 — with GitHub Actions Inactive
@tortila tortila force-pushed the oz-object-level-permissions branch from 671bdbf to b71ff07 Compare June 21, 2023 13:03
Copy link
Member

@mrchrisadams mrchrisadams left a comment

Choose a reason for hiding this comment

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

Hi Oliwia! This looks good!

I see no issues with the logic - my feedback is mainly about style and consistency.

There are are a couple of places where I couldn't understand why we might use one class created in this PR over a built in one (like with Permissions), but otherwise I'm happy.

We discussed the precautions we might take before merging this into production, and I think I'll need to speak to to you in off github and in Zulip about how we sequence deployment and running the migration.

Thanks again for this picking up this massive task.

apps/accounts/models/hosting.py Show resolved Hide resolved
apps/accounts/models/hosting.py Show resolved Hide resolved
@@ -46,21 +46,18 @@ class TestHostingProvider:
"compensatie",
),
)
@pytest.mark.django_db
Copy link
Member

Choose a reason for hiding this comment

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

I see you using this decorator over paassing db into the function, and we have a mix of use throughout our test suite.

I'm open to us settling on either, but I'm not sure about the reasoning either way.

Is there a reason for your preference here? I can see some value in splitting it out as a decorator, as it lets us decorate an entire test class, that needs to hit the db, but all the new tests seem to use function name as a namespacing strategy, so it wasn't so clear to me why we'd use one over the other.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It's only a matter of personal preference - I prefer to have a decorator over adding a function argument. But frankly I didn't think much of it, and I don't think it's an impactful decision for the codebase. If you have a strong preference let me know, I can change this to a function parameter

Copy link
Member

Choose a reason for hiding this comment

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

See below - we can punt this til later. No decisions or changes needed for right now.

apps/accounts/tests/test_admin.py Show resolved Hide resolved
apps/accounts/tests/test_admin.py Show resolved Hide resolved
apps/greencheck/api/views.py Show resolved Hide resolved
@@ -81,9 +77,12 @@ def test_get_queryset_staff(
assert gip in qs

def test_get_queryset_non_staff(
self, db, rf, hosting_provider_with_sample_user, green_ip,
self,
db,
Copy link
Member

Choose a reason for hiding this comment

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

If we're going to use the django_db mark in pytest passing in db as a fixture, would you mind making the change here too so it's consistent in this PR?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It looks like we use both version across our test cases. In each new test I went with the variant that is already used in other tests in the same module. Do you want to take the decision now about this and update all occurrences? I don't see it as important, but let me know if you think otherwise

Copy link
Member

Choose a reason for hiding this comment

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

Nah, not's not a priority.

Let's keep this as is and create an issue to review this where make a recorded decision and then apply it to all future PRs instead. This PR is already ginormous.

apps/greencheck/tests/test_api_asn.py Outdated Show resolved Hide resolved
@tortila tortila force-pushed the oz-object-level-permissions branch from b71ff07 to 78da57d Compare June 23, 2023 11:37
@tortila tortila temporarily deployed to staging June 23, 2023 12:39 — with GitHub Actions Inactive
…add migration to assign permissions to existing objects
…splay related objects based on object-level permissions
@tortila tortila force-pushed the oz-object-level-permissions branch from 24fa649 to 364b6fd Compare July 12, 2023 11:55
@tortila tortila merged commit 4b345f1 into master Jul 12, 2023
1 check passed
@tortila tortila deleted the oz-object-level-permissions branch July 12, 2023 14:21
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.

Users should be able to manage more than 1 hosting provider
2 participants