mirror of
https://github.com/ansible/awx.git
synced 2026-03-13 23:17:32 -02:30
started access refactoring, added UserAccess and updated how ALL permissions is checked
This commit is contained in:
@@ -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):
|
||||
'''
|
||||
|
||||
@@ -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]:
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user