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

Remove django guardian #1057

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGES/1093.misc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Removing django-guardian and migrating to RBAC Roles
39 changes: 17 additions & 22 deletions galaxy_ng/app/access_control/fields.py
Original file line number Diff line number Diff line change
@@ -1,52 +1,46 @@
from django.contrib.auth.models import Permission
from django.db.models import Q
from django.utils.translation import gettext_lazy as _

from guardian.shortcuts import get_perms_for_model

from rest_framework import serializers
from rest_framework.exceptions import ValidationError

from pulpcore.plugin.models.role import Role

from pulpcore.plugin.util import get_perms_for_model

from galaxy_ng.app.models import auth as auth_models


class GroupPermissionField(serializers.Field):
def _validate_group(self, group_data):
if 'object_permissions' not in group_data:
if 'object_roles' not in group_data:
raise ValidationError(detail={
'groups': _('object_permissions field is required')})
'groups': _('object_roles field is required')})

if 'id' not in group_data and 'name' not in group_data:
raise ValidationError(detail={
'groups': _('id or name field is required')})

perms = group_data['object_permissions']
roles = group_data['object_roles']

if not isinstance(perms, list):
if not isinstance(roles, list):
raise ValidationError(detail={
'groups': _('object_permissions must be a list of strings')})
'groups': _('object_roles must be a list of strings')})

# validate that the permissions exist
for perm in perms:
if '.' in perm:
app_label, codename = perm.split('.', maxsplit=1)
filter_q = Q(content_type__app_label=app_label) & Q(codename=codename)
else:
filter_q = Q(codename=perm)

for role in roles:
# TODO(newswangerd): Figure out how to make this one SQL query instead of
# performing N queries for each permission
Comment on lines 31 to 32

Choose a reason for hiding this comment

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

How about

missing_roles = set(roles) - set(Role.objects.filter(name__in=roles))

?

if not Permission.objects.filter(filter_q).exists():
if not Role.objects.filter(name=role).exists():
raise ValidationError(detail={
'groups': _('Permission {} does not exist').format(perm)})
'groups': _('Role {} does not exist').format(role)})

def to_representation(self, value):
rep = []
for group in value:
rep.append({
'id': group.id,
'name': group.name,
'object_permissions': value[group]
'object_roles': value[group]
})
return rep

Expand All @@ -65,7 +59,10 @@ def to_internal_value(self, data):
group_filter[field] = group_data[field]
try:
group = auth_models.Group.objects.get(**group_filter)
internal[group] = group_data['object_permissions']
if 'object_permissions' in group_data:
internal[group] = group_data['object_permissions']
if 'object_roles' in group_data:
internal[group] = group_data['object_roles']
except auth_models.Group.DoesNotExist:
raise ValidationError(detail={
'groups': _("Group name=%s, id=%s does not exist") % (
Expand All @@ -84,8 +81,6 @@ def to_representation(self, obj):
return []
user = request.user

# guardian's get_perms(user, obj) method only returns user permissions,
# not all permissions a user has.
my_perms = []
for perm in get_perms_for_model(type(obj)).all():
codename = "{}.{}".format(perm.content_type.app_label, perm.codename)
Expand Down
31 changes: 24 additions & 7 deletions galaxy_ng/app/access_control/mixins.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,16 @@
from django.conf import settings
from django.db import transaction
from guardian.shortcuts import get_groups_with_perms, assign_perm, remove_perm
from django.core.exceptions import BadRequest
from django.utils.translation import gettext_lazy as _

from rest_framework.exceptions import ValidationError

from pulpcore.plugin.util import (
assign_role,
remove_role,
get_groups_with_perms_attached_roles
)

from django_lifecycle import hook


Expand All @@ -9,28 +19,35 @@ class GroupModelPermissionsMixin:

@property
def groups(self):
return get_groups_with_perms(self, attach_perms=True)
return get_groups_with_perms_attached_roles(self)

@groups.setter
def groups(self, groups):
self._set_groups(groups)

@transaction.atomic
def _set_groups(self, groups):
# guardian doesn't allow adding permissions to objects that haven't been
# Can't add permissions to objects that haven't been
# saved. When creating new objects, save group data to _groups where it
# can be picked up by the post save hook.
if self._state.adding:
self._groups = groups
else:
current_groups = get_groups_with_perms(self, attach_perms=True)
current_groups = get_groups_with_perms_attached_roles(self)
for group in current_groups:
for perm in current_groups[group]:
remove_perm(perm, group, self)
remove_role(perm, group, self)

for group in groups:
for perm in groups[group]:
assign_perm(perm, group, self)
for role in groups[group]:
try:
assign_role(role, group, self)
except BadRequest:
raise ValidationError(
detail={'groups': _('Role {role} does not exist or does not '
'have any permissions related to this object.'
).format(role=role)}
)

@hook('after_save')
def set_object_groups(self):
Expand Down
16 changes: 16 additions & 0 deletions galaxy_ng/app/access_control/statements/roles.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@
},
"NamespaceViewSet": {
"LOCKED_ROLES": {
"galaxy.content_admin": [
"ansible.modify_ansible_repo_content",
],
"galaxy.namespace_owner": [
"galaxy.add_namespace",
"galaxy.change_namespace",
Expand All @@ -36,6 +39,18 @@
"galaxy.upload_to_namespace",
"ansible.delete_collection",
],
"galaxy.group_admin": [
"galaxy.view_group",
"galaxy.delete_group",
"galaxy.add_group",
"galaxy.change_group",
],
"galaxy.user_admin": [
"galaxy.view_user",
"galaxy.delete_user",
"galaxy.add_user",
"galaxy.change_user",
awcrosby marked this conversation as resolved.
Show resolved Hide resolved
],
},
},
"SyncListViewSet": {
Expand All @@ -45,6 +60,7 @@
"galaxy.change_synclist",
"galaxy.delete_synclist",
"galaxy.view_synclist",
"ansible.change_collectionremote",
],
}
},
Expand Down
2 changes: 1 addition & 1 deletion galaxy_ng/app/api/ui/serializers/execution_environment.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from drf_spectacular.types import OpenApiTypes
from drf_spectacular.utils import extend_schema_field

from guardian.shortcuts import get_users_with_perms
from pulpcore.plugin.util import get_users_with_perms

from pulp_container.app import models as container_models
from pulp_container.app import serializers as container_serializers
Expand Down
4 changes: 2 additions & 2 deletions galaxy_ng/app/api/ui/viewsets/distribution.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from rest_framework import mixins
from pulp_ansible.app import models as pulp_models
from guardian.shortcuts import get_objects_for_user
from pulpcore.plugin.util import get_objects_for_user

from galaxy_ng.app.access_control import access_policy
from galaxy_ng.app.api.ui import serializers, versioning
Expand Down Expand Up @@ -31,7 +31,7 @@ def get_queryset(self):
'galaxy.change_synclist',
any_perm=True,
accept_global_perms=False,
klass=models.SyncList
qs=models.SyncList.objects.all()
)

# TODO: find a better way query this data
Expand Down
4 changes: 2 additions & 2 deletions galaxy_ng/app/api/ui/viewsets/execution_environment.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from django_filters import filters
from django_filters.rest_framework import DjangoFilterBackend, filterset
from drf_spectacular.utils import extend_schema
from guardian.shortcuts import get_objects_for_user
from pulpcore.plugin.util import get_objects_for_user
from pulp_container.app import models as container_models
from pulpcore.plugin import models as core_models
from pulpcore.plugin.serializers import AsyncOperationResponseSerializer
Expand Down Expand Up @@ -47,7 +47,7 @@ class Meta:
def has_permissions(self, queryset, name, value):
perms = self.request.query_params.getlist(name)
namespaces = get_objects_for_user(
self.request.user, perms, klass=container_models.ContainerNamespace)
self.request.user, perms, qs=container_models.ContainerNamespace.objects.all())
return self.queryset.filter(namespace__in=namespaces)


Expand Down
4 changes: 2 additions & 2 deletions galaxy_ng/app/api/ui/viewsets/my_namespace.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from galaxy_ng.app import models
from guardian.shortcuts import get_objects_for_user
from pulpcore.plugin.util import get_objects_for_user

from .namespace import NamespaceViewSet

Expand All @@ -10,5 +10,5 @@ def get_queryset(self):
self.request.user,
('galaxy.change_namespace', 'galaxy.upload_to_namespace'),
any_perm=True,
klass=models.Namespace
qs=models.Namespace.objects.all()
)
7 changes: 3 additions & 4 deletions galaxy_ng/app/api/ui/viewsets/my_synclist.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import logging

from django.shortcuts import get_object_or_404
from guardian.shortcuts import get_objects_for_user
from pulpcore.plugin.util import get_objects_for_user

from rest_framework.decorators import action

Expand Down Expand Up @@ -30,9 +30,8 @@ def get_queryset(self):
return get_objects_for_user(
self.request.user,
"galaxy.change_synclist",
any_perm=True,
accept_global_perms=False,
klass=models.SyncList,
# any_perm=True,
qs=models.SyncList.objects.all(),
)

@action(detail=True, methods=["post"])
Expand Down
11 changes: 5 additions & 6 deletions galaxy_ng/app/auth/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@

from django.conf import settings
from django.db import transaction
from guardian import shortcuts

from pulpcore.plugin.util import get_objects_for_group

from pulp_ansible.app.models import AnsibleDistribution, AnsibleRepository
from rest_framework.authentication import BaseAuthentication
from rest_framework.exceptions import AuthenticationFailed
Expand Down Expand Up @@ -79,11 +81,9 @@ def _ensure_group(self, account_scope, account):
def _ensure_synclists(self, group):
with transaction.atomic():
# check for existing synclists
perms = ['galaxy.view_synclist']

synclists_owned_by_group = \
shortcuts.get_objects_for_group(group, perms, klass=SyncList,
any_perm=False, accept_global_perms=True)
get_objects_for_group(group, 'galaxy.view_synclist', SyncList.objects.all())
if synclists_owned_by_group:
return synclists_owned_by_group

Expand All @@ -105,8 +105,7 @@ def _ensure_synclists(self, group):
},
)

default_synclist.groups = {group: ['galaxy.view_synclist', 'galaxy.add_synclist',
'galaxy.delete_synclist', 'galaxy.change_synclist']}
default_synclist.groups = {group: ['galaxy.synclist_owner']}
default_synclist.save()
return default_synclist

Expand Down
44 changes: 17 additions & 27 deletions galaxy_ng/app/management/commands/maintain-pe-group.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from django.core.management import BaseCommand
from guardian.shortcuts import assign_perm

from pulpcore.plugin.util import assign_role

from galaxy_ng.app.models.auth import Group

Expand All @@ -9,43 +10,32 @@
class Command(BaseCommand):
"""
This command creates or updates a partner engineering group
with a standard set of permissions. Intended to be used for
settings.GALAXY_DEPLOYMENT_MODE==insights.
with a standard set of permissions via Galaxy locked roles.
Intended to be used for settings.GALAXY_DEPLOYMENT_MODE==insights.

$ django-admin maintain-pe-group
"""

help = "Creates/updates partner engineering group with permissions"

def handle(self, *args, **options):
pe_group, created = Group.objects.get_or_create(name=PE_GROUP_NAME)
if created:
pe_group, group_created = Group.objects.get_or_create(name=PE_GROUP_NAME)
if group_created:
self.stdout.write(f"Created group '{PE_GROUP_NAME}'")
else:
self.stdout.write(f"Group '{PE_GROUP_NAME}' already exists")

pe_perms = [
# groups
"galaxy.view_group",
"galaxy.delete_group",
"galaxy.add_group",
"galaxy.change_group",
# users
"galaxy.view_user",
"galaxy.delete_user",
"galaxy.add_user",
"galaxy.change_user",
# collections
"ansible.modify_ansible_repo_content",
"ansible.delete_collection",
# namespaces
"galaxy.add_namespace",
"galaxy.change_namespace",
"galaxy.upload_to_namespace",
"galaxy.delete_namespace",
pe_roles = [
'galaxy.group_admin',
'galaxy.user_admin',
'galaxy.collection_admin',
'galaxy.namespace_owner',
'galaxy.content_admin',
]

for perm in pe_perms:
assign_perm(perm, pe_group)
for role in pe_roles:
assign_role(rolename=role, entity=pe_group)

self.stdout.write(f"Permissions assigned to '{PE_GROUP_NAME}'")
self.stdout.write(
f"Roles assigned to '{PE_GROUP_NAME}'"
)
4 changes: 3 additions & 1 deletion galaxy_ng/app/models/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

from django.contrib.auth import models as auth_models

from pulpcore.plugin.models import Group as PulpGroup

log = logging.getLogger(__name__)

__all__ = (
Expand Down Expand Up @@ -36,7 +38,7 @@ def _make_name(scope, name):
return f"{scope}:{name}"


class Group(auth_models.Group):
class Group(PulpGroup):
objects = GroupManager()

class Meta:
Expand Down
9 changes: 9 additions & 0 deletions galaxy_ng/app/viewsets.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,3 +46,12 @@ class ContentRedirectContentGuardViewSet(
):
queryset = models.ContentRedirectContentGuard.objects.all()
endpoint_name = "contentgaurd"


class AuthViewSet(
pulp_viewsets.NamedModelViewSet,
mixins.RetrieveModelMixin,
mixins.DestroyModelMixin,
):
queryset = models.auth.Group.objects.all()
endpoint_name = "auth"
Loading