mirror of
https://github.com/ansible/awx.git
synced 2026-04-01 16:25:08 -02:30
File diff suppressed because it is too large
Load Diff
26
awx/main/migrations/0005_rbac_remove_users.py
Normal file
26
awx/main/migrations/0005_rbac_remove_users.py
Normal file
@@ -0,0 +1,26 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('main', '0004_rbac_migrations'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='organization',
|
||||
name='admins',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='organization',
|
||||
name='users',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='team',
|
||||
name='users',
|
||||
),
|
||||
]
|
||||
@@ -195,19 +195,24 @@ def migrate_job_templates(apps, schema_editor):
|
||||
Permission = apps.get_model('main', 'Permission')
|
||||
|
||||
for jt in JobTemplate.objects.all():
|
||||
permission = Permission.objects.filter(
|
||||
inventory=jt.inventory,
|
||||
project=jt.project,
|
||||
active=True,
|
||||
permission_type__in=['create', 'check', 'run'] if jt.job_type == 'check' else ['create', 'run'],
|
||||
)
|
||||
|
||||
for team in Team.objects.all():
|
||||
if Permission.objects.filter(
|
||||
team=team,
|
||||
inventory=jt.inventory,
|
||||
project=jt.project,
|
||||
active=True,
|
||||
permission_type__in=['create', 'check', 'run'] if jt.job_type == 'check' else ['create', 'run']
|
||||
):
|
||||
team.member_role.children.add(jt.executor_role);
|
||||
if permission.filter(team=team).exists():
|
||||
team.member_role.children.add(jt.executor_role)
|
||||
migrations[jt.name]['teams'].add(team)
|
||||
|
||||
|
||||
for user in User.objects.all():
|
||||
if permission.filter(user=user).exists():
|
||||
jt.executor_role.members.add(user)
|
||||
migrations[jt.name]['users'].add(user)
|
||||
|
||||
if jt.accessible_by(user, {'execute': True}):
|
||||
# If the job template is already accessible by the user, because they
|
||||
# are a sytem, organization, or project admin, then don't add an explicit
|
||||
|
||||
@@ -39,7 +39,6 @@ _PythonSerializer.handle_m2m_field = _new_handle_m2m_field
|
||||
from django.contrib.auth.models import User # noqa
|
||||
from awx.main.access import * # noqa
|
||||
User.add_to_class('get_queryset', get_user_queryset)
|
||||
User.add_to_class('can_access', check_user_access)
|
||||
|
||||
# Import signal handlers only after models have been defined.
|
||||
import awx.main.signals # noqa
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -53,7 +57,7 @@ class Organization(CommonModel, NotificationFieldsModel, ResourceMixin):
|
||||
role_name='Organization Administrator',
|
||||
role_description='May manage all aspects of this organization',
|
||||
parent_role='singleton:' + ROLE_SINGLETON_SYSTEM_ADMINISTRATOR,
|
||||
permissions = {'all': True}
|
||||
permissions = ALL_PERMISSIONS,
|
||||
)
|
||||
auditor_role = ImplicitRoleField(
|
||||
role_name='Organization Auditor',
|
||||
@@ -113,7 +117,7 @@ class Team(CommonModelNameNotUnique, ResourceMixin):
|
||||
role_name='Team Administrator',
|
||||
role_description='May manage this team',
|
||||
parent_role='organization.admin_role',
|
||||
permissions = {'all': True}
|
||||
permissions = ALL_PERMISSIONS,
|
||||
)
|
||||
auditor_role = ImplicitRoleField(
|
||||
role_name='Team Auditor',
|
||||
|
||||
@@ -32,7 +32,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):
|
||||
'''
|
||||
@@ -123,18 +124,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]))
|
||||
@@ -261,8 +250,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.
|
||||
|
||||
@@ -69,11 +69,9 @@ def test_credential_access_superuser():
|
||||
assert access.can_delete(credential)
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_credential_access_admin(user, organization, team, credential):
|
||||
def test_credential_access_admin(user, team, credential):
|
||||
u = user('org-admin', False)
|
||||
organization.admins.add(u)
|
||||
team.organization = organization
|
||||
team.save()
|
||||
team.organization.admin_role.members.add(u)
|
||||
|
||||
access = CredentialAccess(u)
|
||||
|
||||
@@ -85,10 +83,16 @@ def test_credential_access_admin(user, organization, team, credential):
|
||||
# unowned credential can be deleted
|
||||
assert access.can_delete(credential)
|
||||
|
||||
team.users.add(u)
|
||||
assert not access.can_change(credential, {'user': u.pk})
|
||||
|
||||
# credential is now part of a team
|
||||
# that is part of an organization
|
||||
# that I am an admin for
|
||||
credential.team = team
|
||||
credential.save()
|
||||
credential.owner_role.rebuild_role_ancestor_list()
|
||||
|
||||
cred = Credential.objects.create(kind='aws', name='test-cred')
|
||||
cred.team = team
|
||||
cred.save()
|
||||
|
||||
# should have can_change access as org-admin
|
||||
assert access.can_change(credential, {'user': u.pk})
|
||||
|
||||
@@ -2,6 +2,7 @@ import pytest
|
||||
|
||||
from awx.main.migrations import _rbac as rbac
|
||||
from awx.main.models import Permission
|
||||
from awx.main.access import InventoryAccess
|
||||
from django.apps import apps
|
||||
|
||||
@pytest.mark.django_db
|
||||
@@ -195,3 +196,39 @@ def test_group_parent_admin(group, permissions, user):
|
||||
|
||||
parent2.admin_role.members.add(u)
|
||||
assert childA.accessible_by(u, permissions['admin'])
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_access_admin(organization, inventory, user):
|
||||
a = user('admin', False)
|
||||
inventory.organization = organization
|
||||
organization.admin_role.members.add(a)
|
||||
|
||||
access = InventoryAccess(a)
|
||||
assert access.can_read(inventory)
|
||||
assert access.can_add(None)
|
||||
assert access.can_add({'organization': organization.id})
|
||||
assert access.can_change(inventory, None)
|
||||
assert access.can_change(inventory, {'organization': organization.id})
|
||||
assert access.can_admin(inventory, None)
|
||||
assert access.can_admin(inventory, {'organization': organization.id})
|
||||
assert access.can_delete(inventory)
|
||||
assert access.can_run_ad_hoc_commands(inventory)
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_access_auditor(organization, inventory, user):
|
||||
u = user('admin', False)
|
||||
inventory.organization = organization
|
||||
organization.auditor_role.members.add(u)
|
||||
|
||||
access = InventoryAccess(u)
|
||||
assert access.can_read(inventory)
|
||||
assert not access.can_add(None)
|
||||
assert not access.can_add({'organization': organization.id})
|
||||
assert not access.can_change(inventory, None)
|
||||
assert not access.can_change(inventory, {'organization': organization.id})
|
||||
assert not access.can_admin(inventory, None)
|
||||
assert not access.can_admin(inventory, {'organization': organization.id})
|
||||
assert not access.can_delete(inventory)
|
||||
assert not access.can_run_ad_hoc_commands(inventory)
|
||||
|
||||
|
||||
|
||||
@@ -57,27 +57,27 @@ def test_organization_access_superuser(cl, organization, user):
|
||||
def test_organization_access_admin(cl, organization, user):
|
||||
'''can_change because I am an admin of that org'''
|
||||
a = user('admin', False)
|
||||
organization.admins.add(a)
|
||||
organization.users.add(user('user', False))
|
||||
organization.admin_role.members.add(a)
|
||||
organization.member_role.members.add(user('user', False))
|
||||
|
||||
access = OrganizationAccess(a)
|
||||
assert access.can_change(organization, None)
|
||||
assert access.can_delete(organization)
|
||||
|
||||
org = access.get_queryset()[0]
|
||||
assert len(org.admins.all()) == 1
|
||||
assert len(org.users.all()) == 1
|
||||
assert len(org.admin_role.members.all()) == 1
|
||||
assert len(org.member_role.members.all()) == 1
|
||||
|
||||
|
||||
@mock.patch.object(BaseAccess, 'check_license', return_value=None)
|
||||
@pytest.mark.django_db
|
||||
def test_organization_access_user(cl, organization, user):
|
||||
access = OrganizationAccess(user('user', False))
|
||||
organization.users.add(user('user', False))
|
||||
organization.member_role.members.add(user('user', False))
|
||||
|
||||
assert not access.can_change(organization, None)
|
||||
assert not access.can_delete(organization)
|
||||
|
||||
org = access.get_queryset()[0]
|
||||
assert len(org.admins.all()) == 0
|
||||
assert len(org.users.all()) == 1
|
||||
assert len(org.admin_role.members.all()) == 0
|
||||
assert len(org.member_role.members.all()) == 1
|
||||
|
||||
@@ -22,7 +22,7 @@ def test_team_migration_user(team, user, permissions):
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_team_access_superuser(team, user):
|
||||
team.users.add(user('member', False))
|
||||
team.member_role.members.add(user('member', False))
|
||||
|
||||
access = TeamAccess(user('admin', True))
|
||||
|
||||
@@ -31,13 +31,13 @@ def test_team_access_superuser(team, user):
|
||||
assert access.can_delete(team)
|
||||
|
||||
t = access.get_queryset()[0]
|
||||
assert len(t.users.all()) == 1
|
||||
assert len(t.organization.admins.all()) == 0
|
||||
assert len(t.member_role.members.all()) == 1
|
||||
assert len(t.organization.admin_role.members.all()) == 0
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_team_access_org_admin(organization, team, user):
|
||||
a = user('admin', False)
|
||||
organization.admins.add(a)
|
||||
organization.admin_role.members.add(a)
|
||||
team.organization = organization
|
||||
team.save()
|
||||
|
||||
@@ -47,13 +47,13 @@ def test_team_access_org_admin(organization, team, user):
|
||||
assert access.can_delete(team)
|
||||
|
||||
t = access.get_queryset()[0]
|
||||
assert len(t.users.all()) == 0
|
||||
assert len(t.organization.admins.all()) == 1
|
||||
assert len(t.member_role.members.all()) == 0
|
||||
assert len(t.organization.admin_role.members.all()) == 1
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_team_access_member(organization, team, user):
|
||||
u = user('member', False)
|
||||
team.users.add(u)
|
||||
team.member_role.members.add(u)
|
||||
team.organization = organization
|
||||
team.save()
|
||||
|
||||
@@ -63,6 +63,6 @@ def test_team_access_member(organization, team, user):
|
||||
assert not access.can_delete(team)
|
||||
|
||||
t = access.get_queryset()[0]
|
||||
assert len(t.users.all()) == 1
|
||||
assert len(t.organization.admins.all()) == 0
|
||||
assert len(t.member_role.members.all()) == 1
|
||||
assert len(t.organization.admin_role.members.all()) == 0
|
||||
|
||||
|
||||
@@ -19,13 +19,13 @@ def test_org_user_admin(user, organization):
|
||||
admin = user('orgadmin')
|
||||
member = user('orgmember')
|
||||
|
||||
organization.users.add(member)
|
||||
organization.member_role.members.add(member)
|
||||
assert not member.resource.accessible_by(admin, {'write':True})
|
||||
|
||||
organization.admins.add(admin)
|
||||
organization.admin_role.members.add(admin)
|
||||
assert member.resource.accessible_by(admin, {'write':True})
|
||||
|
||||
organization.admins.remove(admin)
|
||||
organization.admin_role.members.remove(admin)
|
||||
assert not member.resource.accessible_by(admin, {'write':True})
|
||||
|
||||
@pytest.mark.django_db
|
||||
@@ -33,10 +33,10 @@ def test_org_user_removed(user, organization):
|
||||
admin = user('orgadmin')
|
||||
member = user('orgmember')
|
||||
|
||||
organization.admins.add(admin)
|
||||
organization.users.add(member)
|
||||
organization.admin_role.members.add(admin)
|
||||
organization.member_role.members.add(member)
|
||||
|
||||
assert member.resource.accessible_by(admin, {'write':True})
|
||||
|
||||
organization.users.remove(member)
|
||||
organization.member_role.members.remove(member)
|
||||
assert not member.resource.accessible_by(admin, {'write':True})
|
||||
|
||||
Reference in New Issue
Block a user