Merge pull request #884 from anoek/rbac

User and Project migration & testing
This commit is contained in:
Wayne Witzel III 2016-02-09 09:17:47 -05:00
commit ca67772d9f
8 changed files with 181 additions and 3 deletions

View File

@ -208,6 +208,11 @@ class Migration(migrations.Migration):
name='resource',
field=awx.main.fields.ImplicitResourceField(related_name='+', to='main.Resource', null=b'True'),
),
migrations.AddField(
model_name='organization',
name='member_role',
field=awx.main.fields.ImplicitRoleField(related_name='+', to='main.Role', null=b'True'),
),
migrations.AddField(
model_name='project',
name='admin_role',

View File

@ -12,8 +12,10 @@ class Migration(migrations.Migration):
]
operations = [
migrations.RunPython(rbac.migrate_users),
migrations.RunPython(rbac.migrate_organization),
migrations.RunPython(rbac.migrate_credential),
migrations.RunPython(rbac.migrate_team),
migrations.RunPython(rbac.migrate_inventory),
migrations.RunPython(rbac.migrate_projects),
]

View File

@ -1,5 +1,15 @@
from collections import defaultdict
def migrate_users(apps, schema_editor):
migrations = list()
User = apps.get_model('auth', "User")
Role = apps.get_model('main', "Role")
for user in User.objects.all():
if user.is_superuser:
Role.singleton('System Administrator').members.add(user)
migrations.append(user)
return migrations
def migrate_organization(apps, schema_editor):
migrations = defaultdict(list)
organization = apps.get_model('main', "Organization")
@ -76,3 +86,49 @@ def migrate_inventory(apps, schema_editor):
migrations[inventory.name]['teams'] = teams
migrations[inventory.name]['users'] = users
return migrations
def migrate_projects(apps, schema_editor):
'''
I can see projects when:
X I am a superuser.
X I am an admin in an organization associated with the project.
X I am a user in an organization associated with the project.
X I am on a team associated with the project.
X I have been explicitly granted permission to run/check jobs using the
project.
X I created the project but it isn't associated with an organization
I can change/delete when:
X I am a superuser.
X I am an admin in an organization associated with the project.
X I created the project but it isn't associated with an organization
'''
migrations = defaultdict(lambda: defaultdict(set))
Project = apps.get_model('main', 'Project')
Permission = apps.get_model('main', 'Permission')
for project in Project.objects.all():
if project.organization is None and project.created_by is not None:
project.admin_role.members.add(project.created_by)
migrations[project.name]['users'].add(project.created_by)
for team in project.teams.all():
team.member_role.children.add(project.member_role)
migrations[project.name]['teams'].add(team)
if project.organization is not None:
for user in project.organization.users.all():
project.member_role.members.add(user)
migrations[project.name]['users'].add(user)
for perm in Permission.objects.filter(project=project):
# All perms at this level just imply a user or team can read
if perm.team:
team.member_role.children.add(project.member_role)
migrations[project.name]['teams'].add(team)
if perm.user:
project.member_role.members.add(perm.user)
migrations[project.name]['users'].add(perm.user)
return migrations

View File

@ -54,11 +54,18 @@ class Organization(CommonModel, ResourceMixin):
)
admin_role = ImplicitRoleField(
role_name='Organization Administrator',
parent_role='singleton:System Administrator',
resource_field='resource',
permissions = {'all': True}
)
auditor_role = ImplicitRoleField(
role_name='Organization Auditor',
parent_role='singleton:System Auditor',
resource_field='resource',
permissions = {'read': True}
)
member_role = ImplicitRoleField(
role_name='Organization Member',
resource_field='resource',
permissions = {'read': True}
)

View File

@ -229,13 +229,12 @@ class Project(UnifiedJobTemplate, ProjectOptions, ResourceMixin):
)
member_role = ImplicitRoleField(
role_name='Project Member',
parent_role='admin',
resource_field='resource',
permissions = {'usage': True}
permissions = {'read': True}
)
scm_update_role = ImplicitRoleField(
role_name='Project Updater',
parent_role='admin',
parent_role='admin_role',
resource_field='resource',
permissions = {'scm_update': True}
)

View File

@ -2,6 +2,7 @@ import pytest
from awx.main.models.credential import Credential
from awx.main.models.inventory import Inventory
from awx.main.models.projects import Project
from awx.main.models.organization import (
Organization,
Team,
@ -23,6 +24,15 @@ def user():
def team(organization):
return Team.objects.create(organization=organization, name='test-team')
@pytest.fixture
def project(organization):
return Project.objects.create(name="test-project", organization=organization, description="test-project-desc")
@pytest.fixture
def user_project(user):
owner = user('owner')
return Project.objects.create(name="test-user-project", created_by=owner, description="test-user-project-desc")
@pytest.fixture
def organization():
return Organization.objects.create(name="test-org", description="test-org-desc")

View File

@ -0,0 +1,79 @@
import pytest
from awx.main.migrations import _rbac as rbac
from awx.main.models import Permission
from django.apps import apps
@pytest.mark.django_db
def test_project_user_project(user_project, project, user):
u = user('owner')
assert user_project.accessible_by(u, {'read': True}) is False
assert project.accessible_by(u, {'read': True}) is False
migrations = rbac.migrate_projects(apps, None)
assert len(migrations[user_project.name]['users']) == 1
assert len(migrations[user_project.name]['teams']) == 0
assert user_project.accessible_by(u, {'read': True}) is True
assert project.accessible_by(u, {'read': True}) is False
@pytest.mark.django_db
def test_project_accessible_by_sa(user, project):
u = user('systemadmin', is_superuser=True)
assert project.accessible_by(u, {'read': True}) is False
su_migrations = rbac.migrate_users(apps, None)
migrations = rbac.migrate_projects(apps, None)
assert len(su_migrations) == 1
assert len(migrations[project.name]['users']) == 0
assert len(migrations[project.name]['teams']) == 0
assert project.accessible_by(u, {'read': True, 'write': True}) is True
@pytest.mark.django_db
def test_project_org_members(user, organization, project):
admin = user('orgadmin')
member = user('orgmember')
assert project.accessible_by(admin, {'read': True}) is False
assert project.accessible_by(member, {'read': True}) is False
organization.admin_role.members.add(admin)
organization.member_role.members.add(member)
rbac.migrate_organization(apps, None)
migrations = rbac.migrate_projects(apps, None)
assert len(migrations[project.name]['users']) == 0
assert len(migrations[project.name]['teams']) == 0
assert project.accessible_by(admin, {'read': True, 'write': True}) is True
assert project.accessible_by(member, {'read': True}) is False
@pytest.mark.django_db
def test_project_team(user, team, project):
nonmember = user('nonmember')
member = user('member')
team.users.add(member)
project.teams.add(team)
assert project.accessible_by(nonmember, {'read': True}) is False
assert project.accessible_by(member, {'read': True}) is False
rbac.migrate_team(apps, None)
migrations = rbac.migrate_projects(apps, None)
assert len(migrations[project.name]['users']) == 0
assert len(migrations[project.name]['teams']) == 1
assert project.accessible_by(member, {'read': True}) is True
assert project.accessible_by(nonmember, {'read': True}) is False
@pytest.mark.django_db
def test_project_explicit_permission(user, team, project):
u = user('user')
p = Permission(user=u, project=project, permission_type='check')
p.save()
assert project.accessible_by(u, {'read': True}) is False
migrations = rbac.migrate_projects(apps, None)
assert len(migrations[project.name]['users']) == 1
assert project.accessible_by(u, {'read': True}) is True

View File

@ -0,0 +1,20 @@
import pytest
from awx.main.migrations import _rbac as rbac
from awx.main.models import Role
from django.apps import apps
@pytest.mark.django_db
def test_user_admin(user_project, project, user):
joe = user('joe', is_superuser = False)
admin = user('admin', is_superuser = True)
sa = Role.singleton('System Administrator')
assert sa.members.filter(id=joe.id).exists() is False
assert sa.members.filter(id=admin.id).exists() is False
migrations = rbac.migrate_users(apps, None)
assert sa.members.filter(id=joe.id).exists() is False
assert sa.members.filter(id=admin.id).exists() is True
assert len(migrations) == 1