diff --git a/awx/main/access.py b/awx/main/access.py index 5a7ec03263..cf41ac08af 100644 --- a/awx/main/access.py +++ b/awx/main/access.py @@ -16,9 +16,9 @@ from rest_framework.exceptions import ParseError, PermissionDenied # AWX from awx.main.utils import * # noqa from awx.main.models import * # noqa +from awx.main.models.rbac import ALL_PERMISSIONS from awx.api.license import LicenseForbids from awx.main.task_engine import TaskSerializer -from awx.main.conf import tower_settings __all__ = ['get_user_queryset', 'check_user_access'] @@ -52,6 +52,21 @@ access_registry = { # ... } + +def user_or_team(data): + try: + if 'user' in data: + pk = get_pk_from_dict(data, 'user') + return get_object_or_400(User, pk=pk), None + elif 'team' in data: + pk = get_pk_from_dict(data, 'team') + return None, get_object_or_400(Team, pk=pk) + else: + return None, None + except ParseError: + return None, None + + def register_access(model_class, access_class): access_classes = access_registry.setdefault(model_class, []) access_classes.append(access_class) @@ -193,24 +208,16 @@ class UserAccess(BaseAccess): model = User def get_queryset(self): - qs = self.model.objects.filter(is_active=True).distinct() - if self.user.is_superuser: - return qs - if tower_settings.ORG_ADMINS_CAN_SEE_ALL_USERS and self.user.admin_of_organizations.filter(active=True).exists(): - return qs - return qs.filter( - Q(pk=self.user.pk) | - Q(organizations__in=self.user.admin_of_organizations.filter(active=True)) | - Q(organizations__in=self.user.organizations.filter(active=True)) | - Q(teams__in=self.user.teams.filter(active=True)) - ).distinct() + qs = self.model.accessible_objects(self.user, {'read':True}) + return qs def can_add(self, data): if data is not None and 'is_superuser' in data: if to_python_boolean(data['is_superuser'], allow_none=True) and not self.user.is_superuser: return False - return bool(self.user.is_superuser or - self.user.admin_of_organizations.filter(active=True).exists()) + if self.user.is_superuser: + return True + return Organization.accessible_objects(self.user, ALL_PERMISSIONS).filter(active=True).exists() def can_change(self, obj, data): if data is not None and 'is_superuser' in data: @@ -225,7 +232,7 @@ class UserAccess(BaseAccess): # Admin implies changing all user fields. if self.user.is_superuser: return True - return bool(obj.organizations.filter(active=True, admins__in=[self.user]).exists()) + return obj.accessible_by(self.user, {'create': True, 'write':True, 'update':True, 'read':True}) def can_delete(self, obj): if obj == self.user: @@ -235,8 +242,8 @@ class UserAccess(BaseAccess): if obj.is_superuser and super_users.count() == 1: # cannot delete the last active superuser return False - return bool(self.user.is_superuser or - obj.organizations.filter(active=True, admins__in=[self.user]).exists()) + return obj.accessible_by(self.user, {'delete': True}) + class OrganizationAccess(BaseAccess): ''' diff --git a/awx/main/models/mixins.py b/awx/main/models/mixins.py index 6d069ed3d4..d6bb10754d 100644 --- a/awx/main/models/mixins.py +++ b/awx/main/models/mixins.py @@ -51,7 +51,7 @@ class ResourceMixin(models.Model): ''' perms = self.get_permissions(user) - if not perms: + if perms is None: return False for k in permissions: if k not in perms or perms[k] < permissions[k]: diff --git a/awx/main/models/organization.py b/awx/main/models/organization.py index 0cd50d9dcc..9709c2d3d0 100644 --- a/awx/main/models/organization.py +++ b/awx/main/models/organization.py @@ -18,7 +18,11 @@ from django.utils.translation import ugettext_lazy as _ # AWX from awx.main.fields import AutoOneToOneField, ImplicitRoleField from awx.main.models.base import * # noqa -from awx.main.models.rbac import ROLE_SINGLETON_SYSTEM_ADMINISTRATOR, ROLE_SINGLETON_SYSTEM_AUDITOR +from awx.main.models.rbac import ( + ALL_PERMISSIONS, + ROLE_SINGLETON_SYSTEM_ADMINISTRATOR, + ROLE_SINGLETON_SYSTEM_AUDITOR, +) from awx.main.models.mixins import ResourceMixin from awx.main.conf import tower_settings @@ -52,7 +56,7 @@ class Organization(CommonModel, ResourceMixin): admin_role = ImplicitRoleField( role_name='Organization Administrator', parent_role='singleton:' + ROLE_SINGLETON_SYSTEM_ADMINISTRATOR, - permissions = {'all': True} + permissions = ALL_PERMISSIONS, ) auditor_role = ImplicitRoleField( role_name='Organization Auditor', @@ -108,7 +112,7 @@ class Team(CommonModelNameNotUnique, ResourceMixin): admin_role = ImplicitRoleField( role_name='Team Administrator', parent_role='organization.admin_role', - permissions = {'all': True} + permissions = ALL_PERMISSIONS, ) auditor_role = ImplicitRoleField( role_name='Team Auditor', diff --git a/awx/main/models/rbac.py b/awx/main/models/rbac.py index 396dcd71c3..b7b6d50f3e 100644 --- a/awx/main/models/rbac.py +++ b/awx/main/models/rbac.py @@ -31,7 +31,8 @@ ROLE_SINGLETON_SYSTEM_AUDITOR='System Auditor' role_rebuilding_paused = False roles_needing_rebuilding = set() - +ALL_PERMISSIONS = {'create': True, 'read': True, 'update': True, 'delete': True, + 'write': True, 'scm_update': True, 'use': True, 'execute': True} class Role(CommonModelNameNotUnique): ''' @@ -122,18 +123,6 @@ class Role(CommonModelNameNotUnique): def grant(self, resource, permissions): # take either the raw Resource or something that includes the ResourceMixin resource = resource if type(resource) is Resource else resource.resource - - if 'all' in permissions and permissions['all']: - del permissions['all'] - permissions['create'] = True - permissions['read'] = True - permissions['write'] = True - permissions['update'] = True - permissions['delete'] = True - permissions['scm_update'] = True - permissions['use'] = True - permissions['execute'] = True - permission = RolePermission(role=self, resource=resource) for k in permissions: setattr(permission, k, int(permissions[k])) @@ -256,8 +245,8 @@ class RolePermission(CreatedModifiedModel): create = models.IntegerField(default = 0) read = models.IntegerField(default = 0) write = models.IntegerField(default = 0) - update = models.IntegerField(default = 0) delete = models.IntegerField(default = 0) + update = models.IntegerField(default = 0) execute = models.IntegerField(default = 0) scm_update = models.IntegerField(default = 0) use = models.IntegerField(default = 0) diff --git a/awx/main/signals.py b/awx/main/signals.py index 6451da0fe6..01b2bb9d34 100644 --- a/awx/main/signals.py +++ b/awx/main/signals.py @@ -130,58 +130,6 @@ def sync_superuser_status_to_rbac(sender, instance, **kwargs): else: Role.singleton(ROLE_SINGLETON_SYSTEM_ADMINISTRATOR).members.remove(instance) -def sync_user_to_team_members_role(sender, reverse, model, instance, pk_set, action, **kwargs): - 'When a user is added or removed from Team.users, ensure that is reflected in Team.member_role' - if action == 'post_add' or action == 'pre_remove': - if reverse: - for team in Team.objects.filter(id__in=pk_set).all(): - if action == 'post_add': - team.member_role.members.add(instance) - if action == 'pre_remove': - team.member_role.members.remove(instance) - else: - for user in User.objects.filter(id__in=pk_set).all(): - if action == 'post_add': - instance.member_role.members.add(user) - if action == 'pre_remove': - instance.member_role.members.remove(user) - -def sync_admin_to_org_admin_role(sender, reverse, model, instance, pk_set, action, **kwargs): - 'When a user is added or removed from Organization.admins, ensure that is reflected in Organization.admin_role' - if action == 'post_add' or action == 'pre_remove': - if reverse: - for org in Organization.objects.filter(id__in=pk_set).all(): - if action == 'post_add': - org.admin_role.members.add(instance) - if action == 'pre_remove': - org.admin_role.members.remove(instance) - else: - for user in User.objects.filter(id__in=pk_set).all(): - if action == 'post_add': - instance.admin_role.members.add(user) - if action == 'pre_remove': - instance.admin_role.members.remove(user) - -def sync_user_to_org_members_role(sender, reverse, model, instance, pk_set, action, **kwargs): - 'When a user is added or removed from Organization.users, ensure that is reflected in Organization.member_role' - if action == 'post_add' or action == 'pre_remove': - if reverse: - for org in Organization.objects.filter(id__in=pk_set).all(): - if action == 'post_add': - org.member_role.members.add(instance) - org.admin_role.children.add(instance.resource.admin_role) - if action == 'pre_remove': - org.member_role.members.remove(instance) - org.admin_role.children.remove(instance.resource.admin_role) - else: - for user in User.objects.filter(id__in=pk_set).all(): - if action == 'post_add': - instance.member_role.members.add(user) - instance.admin_role.children.add(user.resource.admin_role) - if action == 'pre_remove': - instance.member_role.members.remove(user) - instance.admin_role.children.remove(user.resource.admin_role) - def create_user_resource(sender, **kwargs): instance = kwargs['instance'] try: @@ -210,10 +158,7 @@ post_save.connect(emit_job_event_detail, sender=JobEvent) post_save.connect(emit_ad_hoc_command_event_detail, sender=AdHocCommandEvent) m2m_changed.connect(rebuild_role_ancestor_list, Role.parents.through) post_save.connect(sync_superuser_status_to_rbac, sender=User) -m2m_changed.connect(sync_user_to_team_members_role, Team.users.through) post_save.connect(create_user_resource, sender=User) -m2m_changed.connect(sync_user_to_org_members_role, Organization.users.through) -m2m_changed.connect(sync_admin_to_org_admin_role, Organization.admins.through) # Migrate hosts, groups to parent group(s) whenever a group is deleted or # marked as inactive.