mirror of
https://github.com/ansible/awx.git
synced 2026-05-20 07:17:40 -02:30
Merge pull request #2329 from wwitzel3/issue-2269
Add Organization FK to Credential
This commit is contained in:
@@ -1619,7 +1619,7 @@ class CredentialSerializer(BaseSerializer):
|
|||||||
model = Credential
|
model = Credential
|
||||||
fields = ('*', 'kind', 'cloud', 'host', 'username',
|
fields = ('*', 'kind', 'cloud', 'host', 'username',
|
||||||
'password', 'security_token', 'project', 'domain',
|
'password', 'security_token', 'project', 'domain',
|
||||||
'ssh_key_data', 'ssh_key_unlock',
|
'ssh_key_data', 'ssh_key_unlock', 'organization',
|
||||||
'become_method', 'become_username', 'become_password',
|
'become_method', 'become_username', 'become_password',
|
||||||
'vault_password', 'subscription', 'tenant', 'secret', 'client',
|
'vault_password', 'subscription', 'tenant', 'secret', 'client',
|
||||||
'authorize', 'authorize_password')
|
'authorize', 'authorize_password')
|
||||||
@@ -1634,13 +1634,16 @@ class CredentialSerializer(BaseSerializer):
|
|||||||
|
|
||||||
def get_related(self, obj):
|
def get_related(self, obj):
|
||||||
res = super(CredentialSerializer, self).get_related(obj)
|
res = super(CredentialSerializer, self).get_related(obj)
|
||||||
|
|
||||||
|
if obj.organization:
|
||||||
|
res['organization'] = reverse('api:organization_detail', args=(obj.organization.pk,))
|
||||||
|
|
||||||
res.update(dict(
|
res.update(dict(
|
||||||
activity_stream = reverse('api:credential_activity_stream_list', args=(obj.pk,)),
|
activity_stream = reverse('api:credential_activity_stream_list', args=(obj.pk,)),
|
||||||
access_list = reverse('api:credential_access_list', args=(obj.pk,)),
|
access_list = reverse('api:credential_access_list', args=(obj.pk,)),
|
||||||
object_roles = reverse('api:credential_object_roles_list', args=(obj.pk,)),
|
object_roles = reverse('api:credential_object_roles_list', args=(obj.pk,)),
|
||||||
owner_users = reverse('api:credential_owner_users_list', args=(obj.pk,)),
|
owner_users = reverse('api:credential_owner_users_list', args=(obj.pk,)),
|
||||||
owner_teams = reverse('api:credential_owner_teams_list', args=(obj.pk,)),
|
owner_teams = reverse('api:credential_owner_teams_list', args=(obj.pk,)),
|
||||||
owner_organizations = reverse('api:credential_owner_organizations_list', args=(obj.pk,)),
|
|
||||||
))
|
))
|
||||||
|
|
||||||
parents = obj.owner_role.parents.exclude(object_id__isnull=True)
|
parents = obj.owner_role.parents.exclude(object_id__isnull=True)
|
||||||
|
|||||||
@@ -168,7 +168,6 @@ credential_urls = patterns('awx.api.views',
|
|||||||
url(r'^(?P<pk>[0-9]+)/object_roles/$', 'credential_object_roles_list'),
|
url(r'^(?P<pk>[0-9]+)/object_roles/$', 'credential_object_roles_list'),
|
||||||
url(r'^(?P<pk>[0-9]+)/owner/users/$', 'credential_owner_users_list'),
|
url(r'^(?P<pk>[0-9]+)/owner/users/$', 'credential_owner_users_list'),
|
||||||
url(r'^(?P<pk>[0-9]+)/owner/teams/$', 'credential_owner_teams_list'),
|
url(r'^(?P<pk>[0-9]+)/owner/teams/$', 'credential_owner_teams_list'),
|
||||||
url(r'^(?P<pk>[0-9]+)/owner/organizations/$', 'credential_owner_organizations_list'),
|
|
||||||
# See also credentials resources on users/teams.
|
# See also credentials resources on users/teams.
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -1358,7 +1358,8 @@ class CredentialList(ListCreateAPIView):
|
|||||||
if 'team' in request.data:
|
if 'team' in request.data:
|
||||||
credential.owner_role.parents.add(team.member_role)
|
credential.owner_role.parents.add(team.member_role)
|
||||||
if 'organization' in request.data:
|
if 'organization' in request.data:
|
||||||
credential.owner_role.parents.add(organization.admin_role)
|
credential.organization = organization
|
||||||
|
credential.save()
|
||||||
|
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
@@ -1388,23 +1389,6 @@ class CredentialOwnerTeamsList(SubListAPIView):
|
|||||||
return self.model.objects.filter(pk__in=teams)
|
return self.model.objects.filter(pk__in=teams)
|
||||||
|
|
||||||
|
|
||||||
class CredentialOwnerOrganizationsList(SubListAPIView):
|
|
||||||
model = Organization
|
|
||||||
serializer_class = OrganizationSerializer
|
|
||||||
parent_model = Credential
|
|
||||||
new_in_300 = True
|
|
||||||
|
|
||||||
def get_queryset(self):
|
|
||||||
credential = get_object_or_404(self.parent_model, pk=self.kwargs['pk'])
|
|
||||||
if not self.request.user.can_access(Credential, 'read', None):
|
|
||||||
raise PermissionDenied()
|
|
||||||
|
|
||||||
content_type = ContentType.objects.get_for_model(self.model)
|
|
||||||
orgs = [c.content_object.pk for c in credential.owner_role.parents.filter(content_type=content_type)]
|
|
||||||
|
|
||||||
return self.model.objects.filter(pk__in=orgs)
|
|
||||||
|
|
||||||
|
|
||||||
class UserCredentialsList(CredentialList):
|
class UserCredentialsList(CredentialList):
|
||||||
|
|
||||||
model = Credential
|
model = Credential
|
||||||
|
|||||||
@@ -600,6 +600,10 @@ class CredentialAccess(BaseAccess):
|
|||||||
if not self.can_add(data):
|
if not self.can_add(data):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
if obj.organization:
|
||||||
|
if self.user in obj.organization.admin_role:
|
||||||
|
return True
|
||||||
|
|
||||||
return self.user in obj.owner_role
|
return self.user in obj.owner_role
|
||||||
|
|
||||||
def can_delete(self, obj):
|
def can_delete(self, obj):
|
||||||
|
|||||||
@@ -86,7 +86,11 @@ class Migration(migrations.Migration):
|
|||||||
name='credential',
|
name='credential',
|
||||||
unique_together=set([]),
|
unique_together=set([]),
|
||||||
),
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='credential',
|
||||||
|
name='organization',
|
||||||
|
field=models.ForeignKey(related_name='credentials', default=None, blank=True, to='main.Organization', null=True),
|
||||||
|
),
|
||||||
|
|
||||||
#
|
#
|
||||||
# New RBAC models and fields
|
# New RBAC models and fields
|
||||||
|
|||||||
@@ -123,7 +123,7 @@ def attrfunc(attr_path):
|
|||||||
return attr
|
return attr
|
||||||
|
|
||||||
def _update_credential_parents(org, cred):
|
def _update_credential_parents(org, cred):
|
||||||
org.admin_role.children.add(cred.owner_role)
|
cred.organization = org
|
||||||
cred.save()
|
cred.save()
|
||||||
|
|
||||||
def _discover_credentials(instances, cred, orgfunc):
|
def _discover_credentials(instances, cred, orgfunc):
|
||||||
@@ -164,13 +164,12 @@ def _discover_credentials(instances, cred, orgfunc):
|
|||||||
cred.pk = None
|
cred.pk = None
|
||||||
cred.save()
|
cred.save()
|
||||||
|
|
||||||
# Unlink the old information from the new credential
|
cred.owner_role, cred.use_role, cred.organization = None, None, None
|
||||||
cred.owner_role, cred.use_role = None, None
|
|
||||||
cred.save()
|
|
||||||
|
|
||||||
for i in orgs[org]:
|
for i in orgs[org]:
|
||||||
i.credential = cred
|
i.credential = cred
|
||||||
i.save()
|
i.save()
|
||||||
|
|
||||||
_update_credential_parents(org, cred)
|
_update_credential_parents(org, cred)
|
||||||
|
|
||||||
@log_migration
|
@log_migration
|
||||||
|
|||||||
@@ -78,6 +78,14 @@ class Credential(PasswordFieldsModel, CommonModelNameNotUnique, ResourceMixin):
|
|||||||
on_delete=models.CASCADE,
|
on_delete=models.CASCADE,
|
||||||
related_name='deprecated_credentials',
|
related_name='deprecated_credentials',
|
||||||
)
|
)
|
||||||
|
organization = models.ForeignKey(
|
||||||
|
'Organization',
|
||||||
|
null=True,
|
||||||
|
default=None,
|
||||||
|
blank=True,
|
||||||
|
on_delete=models.CASCADE,
|
||||||
|
related_name='credentials',
|
||||||
|
)
|
||||||
kind = models.CharField(
|
kind = models.CharField(
|
||||||
max_length=32,
|
max_length=32,
|
||||||
choices=KIND_CHOICES,
|
choices=KIND_CHOICES,
|
||||||
@@ -209,7 +217,10 @@ class Credential(PasswordFieldsModel, CommonModelNameNotUnique, ResourceMixin):
|
|||||||
],
|
],
|
||||||
)
|
)
|
||||||
use_role = ImplicitRoleField(
|
use_role = ImplicitRoleField(
|
||||||
parent_role=['owner_role']
|
parent_role=[
|
||||||
|
'organization.admin_role',
|
||||||
|
'owner_role',
|
||||||
|
]
|
||||||
)
|
)
|
||||||
read_role = ImplicitRoleField(parent_role=[
|
read_role = ImplicitRoleField(parent_role=[
|
||||||
'singleton:' + ROLE_SINGLETON_SYSTEM_AUDITOR,
|
'singleton:' + ROLE_SINGLETON_SYSTEM_AUDITOR,
|
||||||
|
|||||||
@@ -147,8 +147,7 @@ def test_credential_detail(post, get, organization, org_admin):
|
|||||||
response = get(reverse('api:credential_detail', args=(response.data['id'],)), org_admin)
|
response = get(reverse('api:credential_detail', args=(response.data['id'],)), org_admin)
|
||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
summary_fields = response.data['summary_fields']
|
summary_fields = response.data['summary_fields']
|
||||||
assert 'owners' in summary_fields
|
assert 'organization' in summary_fields
|
||||||
assert summary_fields['owners'][0]['id'] == organization.id
|
|
||||||
related_fields = response.data['related']
|
related_fields = response.data['related']
|
||||||
assert 'organization' in related_fields
|
assert 'organization' in related_fields
|
||||||
|
|
||||||
|
|||||||
@@ -118,6 +118,9 @@ def test_cred_job_template(user, team, deploy_jobtemplate):
|
|||||||
|
|
||||||
access = CredentialAccess(a)
|
access = CredentialAccess(a)
|
||||||
rbac.migrate_credential(apps, None)
|
rbac.migrate_credential(apps, None)
|
||||||
|
|
||||||
|
cred.refresh_from_db()
|
||||||
|
|
||||||
assert access.can_change(cred, {'organization': org.pk})
|
assert access.can_change(cred, {'organization': org.pk})
|
||||||
|
|
||||||
org.admin_role.members.remove(a)
|
org.admin_role.members.remove(a)
|
||||||
@@ -135,6 +138,8 @@ def test_cred_multi_job_template_single_org_xfail(user, deploy_jobtemplate):
|
|||||||
|
|
||||||
access = CredentialAccess(a)
|
access = CredentialAccess(a)
|
||||||
rbac.migrate_credential(apps, None)
|
rbac.migrate_credential(apps, None)
|
||||||
|
cred.refresh_from_db()
|
||||||
|
|
||||||
assert not access.can_change(cred, {'organization': org.pk})
|
assert not access.can_change(cred, {'organization': org.pk})
|
||||||
|
|
||||||
@pytest.mark.django_db
|
@pytest.mark.django_db
|
||||||
@@ -149,6 +154,8 @@ def test_cred_multi_job_template_single_org(user, team, deploy_jobtemplate):
|
|||||||
|
|
||||||
access = CredentialAccess(a)
|
access = CredentialAccess(a)
|
||||||
rbac.migrate_credential(apps, None)
|
rbac.migrate_credential(apps, None)
|
||||||
|
cred.refresh_from_db()
|
||||||
|
|
||||||
assert access.can_change(cred, {'organization': org.pk})
|
assert access.can_change(cred, {'organization': org.pk})
|
||||||
|
|
||||||
org.admin_role.members.remove(a)
|
org.admin_role.members.remove(a)
|
||||||
@@ -180,6 +187,7 @@ def test_single_cred_multi_job_template_multi_org(user, organizations, credentia
|
|||||||
|
|
||||||
for jt in jts:
|
for jt in jts:
|
||||||
jt.refresh_from_db()
|
jt.refresh_from_db()
|
||||||
|
credential.refresh_from_db()
|
||||||
|
|
||||||
assert jts[0].credential != jts[1].credential
|
assert jts[0].credential != jts[1].credential
|
||||||
assert access.can_change(jts[0].credential, {'organization': org.pk})
|
assert access.can_change(jts[0].credential, {'organization': org.pk})
|
||||||
|
|||||||
Reference in New Issue
Block a user