mirror of
https://github.com/ansible/awx.git
synced 2026-03-07 11:41:08 -03:30
Partial support for permission editablity through REST. More TBA.
This commit is contained in:
@@ -168,15 +168,15 @@ class PrimordialModel(models.Model):
|
|||||||
@classmethod
|
@classmethod
|
||||||
def can_user_administrate(cls, user, obj, data):
|
def can_user_administrate(cls, user, obj, data):
|
||||||
# FIXME: do we want a seperate method to override put? This is kind of general purpose
|
# FIXME: do we want a seperate method to override put? This is kind of general purpose
|
||||||
raise exceptions.NotImplementedError()
|
raise Exception("can_user_administrate needs to be implemented in model subclass")
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def can_user_delete(cls, user, obj):
|
def can_user_delete(cls, user, obj):
|
||||||
raise exceptions.NotImplementedError()
|
raise Exception("can_user_delete needs to be implemented in model subclass")
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def can_user_read(cls, user, obj):
|
def can_user_read(cls, user, obj):
|
||||||
raise exceptions.NotImplementedError()
|
raise Exception("can_user_read needs to be implemented in model subclass")
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def can_user_add(cls, user, data):
|
def can_user_add(cls, user, data):
|
||||||
@@ -805,6 +805,37 @@ class Permission(CommonModelNameNotUnique):
|
|||||||
self.permission_type
|
self.permission_type
|
||||||
))
|
))
|
||||||
|
|
||||||
|
def get_absolute_url(self):
|
||||||
|
import lib.urls
|
||||||
|
return reverse(lib.urls.views_PermissionsDetail, args=(self.pk,))
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def can_user_administrate(cls, user, obj, data):
|
||||||
|
if user.is_superuser:
|
||||||
|
return True
|
||||||
|
# a permission can be administrated by a super
|
||||||
|
# or if a user permission, that an admin of a user's organization
|
||||||
|
# or if a team permission, an admin of that team's organization
|
||||||
|
if obj.user and obj.user.organizations.filter(admins__in = [user]).count() > 0:
|
||||||
|
return True
|
||||||
|
if obj.team and obj.team.organization.admins.filter(user=user).count() > 0:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def can_user_read(cls, user, obj):
|
||||||
|
# a permission can be seen by the assigned user or team
|
||||||
|
# or anyone who can administrate that permission
|
||||||
|
if obj.user and obj.user == user:
|
||||||
|
return True
|
||||||
|
if obj.team and obj.team.users.filter(pk = user.pk).count() > 0:
|
||||||
|
return True
|
||||||
|
return cls.can_user_administrate(user, obj, None)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def can_user_delete(cls, user, obj):
|
||||||
|
return cls.can_user_administrate(user, obj, None)
|
||||||
|
|
||||||
# TODO: other job types (later)
|
# TODO: other job types (later)
|
||||||
|
|
||||||
class JobTemplate(CommonModel):
|
class JobTemplate(CommonModel):
|
||||||
|
|||||||
@@ -169,18 +169,42 @@ class TeamSerializer(BaseSerializer):
|
|||||||
users = reverse(lib.urls.views_TeamsUsersList, args=(obj.pk,)),
|
users = reverse(lib.urls.views_TeamsUsersList, args=(obj.pk,)),
|
||||||
credentials = reverse(lib.urls.views_TeamsCredentialsList, args=(obj.pk,)),
|
credentials = reverse(lib.urls.views_TeamsCredentialsList, args=(obj.pk,)),
|
||||||
organization = reverse(lib.urls.views_OrganizationsDetail, args=(obj.organization.pk,)),
|
organization = reverse(lib.urls.views_OrganizationsDetail, args=(obj.organization.pk,)),
|
||||||
|
permissions = reverse(lib.urls.views_TeamsPermissionsList, args=(obj.pk,)),
|
||||||
)
|
)
|
||||||
if obj.created_by:
|
if obj.created_by:
|
||||||
res['created_by'] = reverse(lib.urls.views_UsersDetail, args=(obj.created_by.pk,))
|
res['created_by'] = reverse(lib.urls.views_UsersDetail, args=(obj.created_by.pk,))
|
||||||
return res
|
return res
|
||||||
|
|
||||||
|
class PermissionSerializer(BaseSerializer):
|
||||||
|
|
||||||
|
url = serializers.CharField(source='get_absolute_url', read_only=True)
|
||||||
|
related = serializers.SerializerMethodField('get_related')
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = Permission
|
||||||
|
fields = ( 'url', 'id', 'user', 'team', 'name', 'description', 'creation_date',
|
||||||
|
'project', 'inventory', 'permission_type' )
|
||||||
|
|
||||||
|
def get_related(self, obj):
|
||||||
|
res = dict()
|
||||||
|
if obj.user:
|
||||||
|
res['user'] = reverse(lib.urls.views_UsersDetail, args=(obj.user.pk,))
|
||||||
|
if obj.team:
|
||||||
|
res['team'] = reverse(lib.urls.views_TeamsDetail, args=(obj.team.pk,))
|
||||||
|
if self.project:
|
||||||
|
res['project'] = reverse(lib.urls.views_ProjectsDetail, args=(obj.project.pk,))
|
||||||
|
if self.inventory:
|
||||||
|
res['inventory'] = reverse(lib.urls.views_InventoryDetail, args=(obj.inventory.pk,))
|
||||||
|
if self.created_by:
|
||||||
|
res['created_by'] = reverse(lib.urls.views_UsersDetail, args=(obj.created_by.pk,))
|
||||||
|
|
||||||
class CredentialSerializer(BaseSerializer):
|
class CredentialSerializer(BaseSerializer):
|
||||||
|
|
||||||
# add the URL and related resources
|
# add the URL and related resources
|
||||||
url = serializers.CharField(source='get_absolute_url', read_only=True)
|
url = serializers.CharField(source='get_absolute_url', read_only=True)
|
||||||
related = serializers.SerializerMethodField('get_related')
|
related = serializers.SerializerMethodField('get_related')
|
||||||
|
|
||||||
# FIXME: may want to make soem of these filtered based on user accessing
|
# FIXME: may want to make some of these filtered based on user accessing
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Credential
|
model = Credential
|
||||||
fields = (
|
fields = (
|
||||||
@@ -228,6 +252,7 @@ class UserSerializer(BaseSerializer):
|
|||||||
admin_of_organizations = reverse(lib.urls.views_UsersAdminOrganizationsList, args=(obj.pk,)),
|
admin_of_organizations = reverse(lib.urls.views_UsersAdminOrganizationsList, args=(obj.pk,)),
|
||||||
projects = reverse(lib.urls.views_UsersProjectsList, args=(obj.pk,)),
|
projects = reverse(lib.urls.views_UsersProjectsList, args=(obj.pk,)),
|
||||||
credentials = reverse(lib.urls.views_UsersCredentialsList, args=(obj.pk,)),
|
credentials = reverse(lib.urls.views_UsersCredentialsList, args=(obj.pk,)),
|
||||||
|
permissions = reverse(lib.urls.views_UsersPermissionsList, args=(obj.pk,)),
|
||||||
)
|
)
|
||||||
|
|
||||||
def get_absolute_url_override(self, obj):
|
def get_absolute_url_override(self, obj):
|
||||||
|
|||||||
@@ -386,15 +386,56 @@ class ProjectsTest(BaseTest):
|
|||||||
|
|
||||||
# =====================================================================
|
# =====================================================================
|
||||||
# PERMISSIONS
|
# PERMISSIONS
|
||||||
|
|
||||||
|
user = self.other_django_user
|
||||||
|
team = Team.objects.get(pk=1)
|
||||||
|
organization = Organization.objects.get(pk=1)
|
||||||
|
inventory = Inventory.objects.create(
|
||||||
|
name = 'test inventory',
|
||||||
|
organization = organization,
|
||||||
|
created_by = self.super_django_user
|
||||||
|
)
|
||||||
|
project = Project.objects.get(pk=1)
|
||||||
|
|
||||||
# can add permissions to a user
|
# can add permissions to a user
|
||||||
|
|
||||||
|
user_permission = dict(
|
||||||
|
name='user can deploy a certain project to a certain inventory',
|
||||||
|
# user=user.pk, # no need to specify, this will be automatically filled in
|
||||||
|
inventory=inventory.pk,
|
||||||
|
project=project.pk,
|
||||||
|
permission_type=PERM_INVENTORY_DEPLOY
|
||||||
|
)
|
||||||
|
team_permission = dict(
|
||||||
|
name='team can deploy a certain project to a certain inventory',
|
||||||
|
# team=team.pk, # no need to specify, this will be automatically filled in
|
||||||
|
inventory=inventory.pk,
|
||||||
|
project=project.pk,
|
||||||
|
permission_type=PERM_INVENTORY_DEPLOY
|
||||||
|
)
|
||||||
|
|
||||||
|
url = '/api/v1/users/%s/permissions/' % user.pk
|
||||||
|
self.post(url, user_permission, expect=201, auth=self.get_super_credentials())
|
||||||
|
|
||||||
# can add permissions on a team
|
# can add permissions on a team
|
||||||
|
url = '/api/v1/teams/%s/permissions/' % team.pk
|
||||||
|
self.post(url, team_permission, expect=201, auth=self.get_super_credentials())
|
||||||
|
|
||||||
# can list permissions on a user
|
# can list permissions on a user
|
||||||
|
url = '/api/v1/users/%s/permissions/' % user.pk
|
||||||
|
|
||||||
# can list permissions on a team
|
# can list permissions on a team
|
||||||
|
url = '/api/v1/teams/%s/permissions/' % team.pk
|
||||||
|
|
||||||
# can edit a permission
|
# can edit a permission
|
||||||
# can remove credentials from a user
|
|
||||||
# can remove credentials from a team
|
# can remove permissions from a user
|
||||||
|
# do need to disassociate, just delete it
|
||||||
|
|
||||||
|
# can remove permissions from a team
|
||||||
|
# do need to disassociate, just delete it
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -266,6 +266,23 @@ class TeamsUsersList(BaseSubList):
|
|||||||
return base
|
return base
|
||||||
raise PermissionDenied()
|
raise PermissionDenied()
|
||||||
|
|
||||||
|
class TeamsPermissionsList(BaseSubList):
|
||||||
|
|
||||||
|
model = Permission
|
||||||
|
serializer_class = PermissionSerializer
|
||||||
|
permission_classes = (CustomRbac,)
|
||||||
|
parent_model = Team
|
||||||
|
relationship = 'permissions'
|
||||||
|
postable = True
|
||||||
|
filter_fields = ('name',)
|
||||||
|
inject_primary_key_on_post_as = 'team'
|
||||||
|
|
||||||
|
def _get_queryset(self):
|
||||||
|
team = Team.objects.get(pk=self.kwargs['pk'])
|
||||||
|
if not Team.can_user_administrate(self.request.user, team, None):
|
||||||
|
raise PermissionDenied()
|
||||||
|
return Permission.objects.filter(team = team)
|
||||||
|
|
||||||
|
|
||||||
class TeamsProjectsList(BaseSubList):
|
class TeamsProjectsList(BaseSubList):
|
||||||
|
|
||||||
@@ -423,6 +440,23 @@ class UsersTeamsList(BaseSubList):
|
|||||||
raise PermissionDenied()
|
raise PermissionDenied()
|
||||||
return Team.objects.filter(users__in = [ user ])
|
return Team.objects.filter(users__in = [ user ])
|
||||||
|
|
||||||
|
class UsersPermissionsList(BaseSubList):
|
||||||
|
|
||||||
|
model = Permission
|
||||||
|
serializer_class = PermissionSerializer
|
||||||
|
permission_classes = (CustomRbac,)
|
||||||
|
parent_model = User
|
||||||
|
relationship = 'permissions'
|
||||||
|
postable = True
|
||||||
|
filter_fields = ('name',)
|
||||||
|
inject_primary_key_on_post_as = 'user'
|
||||||
|
|
||||||
|
def _get_queryset(self):
|
||||||
|
user = User.objects.get(pk=self.kwargs['pk'])
|
||||||
|
if not UserHelper.can_user_administrate(self.request.user, user, None):
|
||||||
|
raise PermissionDenied()
|
||||||
|
return Permission.objects.filter(user=user)
|
||||||
|
|
||||||
class UsersProjectsList(BaseSubList):
|
class UsersProjectsList(BaseSubList):
|
||||||
|
|
||||||
model = Project
|
model = Project
|
||||||
@@ -514,6 +548,11 @@ class CredentialsDetail(BaseDetail):
|
|||||||
serializer_class = CredentialSerializer
|
serializer_class = CredentialSerializer
|
||||||
permission_classes = (CustomRbac,)
|
permission_classes = (CustomRbac,)
|
||||||
|
|
||||||
|
class PermissionsDetail(BaseDetail):
|
||||||
|
|
||||||
|
model = Permission
|
||||||
|
serializer_class = PermissionSerializer
|
||||||
|
permission_classes = (CustomRbac,)
|
||||||
|
|
||||||
class InventoryList(BaseList):
|
class InventoryList(BaseList):
|
||||||
|
|
||||||
|
|||||||
@@ -102,6 +102,10 @@ views_TagsDetail = views.TagsDetail.as_view()
|
|||||||
# credentials service
|
# credentials service
|
||||||
views_CredentialsDetail = views.CredentialsDetail.as_view()
|
views_CredentialsDetail = views.CredentialsDetail.as_view()
|
||||||
|
|
||||||
|
# permissions
|
||||||
|
views_UsersPermissionsList = views.UsersPermissionsList.as_view()
|
||||||
|
views_TeamsPermissionsList = views.TeamsPermissionsList.as_view()
|
||||||
|
views_PermissionsDetail = views.PermissionsDetail.as_view()
|
||||||
|
|
||||||
urlpatterns = patterns('',
|
urlpatterns = patterns('',
|
||||||
|
|
||||||
@@ -199,8 +203,9 @@ urlpatterns = patterns('',
|
|||||||
url(r'^api/v1/credentials/(?P<pk>[0-9]+)/$', views_CredentialsDetail),
|
url(r'^api/v1/credentials/(?P<pk>[0-9]+)/$', views_CredentialsDetail),
|
||||||
|
|
||||||
# permissions services
|
# permissions services
|
||||||
# ... users
|
url(r'^api/v1/users/(?P<pk>[0-9]+)/permissions/$', views_UsersPermissionsList),
|
||||||
# ... teams
|
url(r'^api/v1/teams/(?P<pk>[0-9]+)/permissions/$', views_TeamsPermissionsList),
|
||||||
|
url(r'^api/v1/permissions/(?P<pk>[0-9]+)/$', views_PermissionsDetail),
|
||||||
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user