Skip to content

Commit

Permalink
Revert "Revert RBAC Roles (ansible#1205)" (ansible#1212)
Browse files Browse the repository at this point in the history
This reverts commit 67dbd31.

No-Issue
  • Loading branch information
newswangerd authored and awcrosby committed Jul 29, 2022
1 parent ce49459 commit 3e42e26
Show file tree
Hide file tree
Showing 27 changed files with 296 additions and 188 deletions.
1 change: 1 addition & 0 deletions CHANGES/1092.misc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Create galaxy_ng specific Roles
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
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,5 +1,15 @@
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 @@ -8,28 +18,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
4 changes: 3 additions & 1 deletion galaxy_ng/app/access_control/statements/__init__.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
from .standalone import STANDALONE_STATEMENTS
from .insights import INSIGHTS_STATEMENTS
from .pulp_container import PULP_CONTAINER_VIEWSETS
from .roles import VIEWSETS

__all__ = (
STANDALONE_STATEMENTS,
INSIGHTS_STATEMENTS,
PULP_CONTAINER_VIEWSETS
PULP_CONTAINER_VIEWSETS,
VIEWSETS
)
67 changes: 67 additions & 0 deletions galaxy_ng/app/access_control/statements/roles.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
VIEWSETS = {
"CollectionViewSet": {
"LOCKED_ROLES": {
"galaxy.collection_admin": [
"galaxy.change_namespace",
"galaxy.delete_namespace",
"galaxy.view_namespace",
"galaxy.upload_to_namespace",
"ansible.delete_collection",
],
}
},
"ContainerRepositoryViewSet": {
"LOCKED_ROLES": {
"galaxy.execution_environment_admin": [
"container.delete_containerrepository",
"container.namespace_change_containerdistribution",
"container.namespace_modify_content_containerpushrepository",
"container.namespace_push_containerdistribution",
"container.add_containernamespace",
"container.change_containernamespace",
],
}
},
"NamespaceViewSet": {
"LOCKED_ROLES": {
"galaxy.content_admin": [
"ansible.modify_ansible_repo_content",
],
"galaxy.namespace_owner": [
"galaxy.add_namespace",
"galaxy.change_namespace",
"galaxy.delete_namespace",
"galaxy.view_namespace",
"galaxy.upload_to_namespace",
"ansible.delete_collection",
],
"galaxy.publisher": [
"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",
],
},
},
"SyncListViewSet": {
"LOCKED_ROLES": {
"galaxy.synclist_owner": [
"galaxy.add_synclist",
"galaxy.change_synclist",
"galaxy.delete_synclist",
"galaxy.view_synclist",
"ansible.change_collectionremote",
],
}
},
}
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()
)
8 changes: 4 additions & 4 deletions galaxy_ng/app/api/ui/viewsets/my_synclist.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@

from django.http import HttpResponse
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

from galaxy_ng.app import models
Expand All @@ -25,9 +26,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(),
)

# TODO: on UI click of synclist toggle the UI makes 2 calls
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
Loading

0 comments on commit 3e42e26

Please sign in to comment.