diff --git a/awx/main/access.py b/awx/main/access.py index 231ece8042..90a9002259 100644 --- a/awx/main/access.py +++ b/awx/main/access.py @@ -600,6 +600,10 @@ class CredentialAccess(BaseAccess): if not self.can_add(data): return False + if obj.organization: + if self.user in obj.organization.admin_role: + return True + return self.user in obj.owner_role def can_delete(self, obj): diff --git a/awx/main/migrations/0008_v300_rbac_changes.py b/awx/main/migrations/0008_v300_rbac_changes.py index 9c4b193d47..50ce375f5f 100644 --- a/awx/main/migrations/0008_v300_rbac_changes.py +++ b/awx/main/migrations/0008_v300_rbac_changes.py @@ -86,7 +86,11 @@ class Migration(migrations.Migration): name='credential', 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 diff --git a/awx/main/migrations/_rbac.py b/awx/main/migrations/_rbac.py index b7933f1a11..34f57ed773 100644 --- a/awx/main/migrations/_rbac.py +++ b/awx/main/migrations/_rbac.py @@ -123,10 +123,10 @@ def attrfunc(attr_path): return attr def _update_credential_parents(org, cred): - org.admin_role.children.add(cred.owner_role) + cred.organization = org cred.save() -def _discover_credentials(instances, cred, orgfunc): +def _discover_credentials(apps, instances, cred, orgfunc): '''_discover_credentials will find shared credentials across organizations. If a shared credential is found, it will duplicate the credential, ensure the proper role permissions are added to the new @@ -139,6 +139,8 @@ def _discover_credentials(instances, cred, orgfunc): orgfunc is a function that when called with an instance from instances will produce an Organization object. ''' + Credential = apps.get_model('main', "Credential") + orgs = defaultdict(list) for inst in instances: try: @@ -161,17 +163,38 @@ def _discover_credentials(instances, cred, orgfunc): _update_credential_parents(org, cred) else: # Create a new credential - cred.pk = None - cred.save() - - # Unlink the old information from the new credential - cred.owner_role, cred.use_role = None, None - cred.save() + new_cred = Credential.objects.create( + kind = cred.kind, + cloud = cred.cloud, + host = cred.host, + username = cred.username, + password = cred.password, + security_token = cred.security_token, + project = cred.project, + domain = cred.domain, + ssh_key_data = cred.ssh_key_data, + ssh_key_unlock = cred.ssh_key_unlock, + become_method = cred.become_method, + become_username = cred.become_username, + become_password = cred.become_password, + vault_password = cred.vault_password, + authorize = cred.authorize, + authorize_password = cred.authorize_password, + client = cred.client, + secret = cred.secret, + subscription = cred.subscription, + tenant = cred.tenant, + created = cred.created, + modified = cred.modified, + created_by_id = cred.created_by_id, + modified_by_id = cred.modified_by_id, + ) for i in orgs[org]: - i.credential = cred + i.credential = new_cred i.save() - _update_credential_parents(org, cred) + + _update_credential_parents(org, new_cred) @log_migration def migrate_credential(apps, schema_editor): @@ -187,7 +210,7 @@ def migrate_credential(apps, schema_editor): if len(results) == 1: _update_credential_parents(results[0].inventory.organization, cred) else: - _discover_credentials(results, cred, attrfunc('inventory.organization')) + _discover_credentials(apps, results, cred, attrfunc('inventory.organization')) logger.info(smart_text(u"added Credential(name={}, kind={}, host={}) at organization level".format(cred.name, cred.kind, cred.host))) projs = Project.objects.filter(credential=cred).all() diff --git a/awx/main/models/credential.py b/awx/main/models/credential.py index d1e6e91d93..07cf77e97d 100644 --- a/awx/main/models/credential.py +++ b/awx/main/models/credential.py @@ -78,6 +78,14 @@ class Credential(PasswordFieldsModel, CommonModelNameNotUnique, ResourceMixin): on_delete=models.CASCADE, related_name='deprecated_credentials', ) + organization = models.ForeignKey( + 'Organization', + null=True, + default=None, + blank=True, + on_delete=models.CASCADE, + related_name='credentials', + ) kind = models.CharField( max_length=32, choices=KIND_CHOICES, @@ -209,7 +217,10 @@ class Credential(PasswordFieldsModel, CommonModelNameNotUnique, ResourceMixin): ], ) use_role = ImplicitRoleField( - parent_role=['owner_role'] + parent_role=[ + 'organization.admin_role', + 'owner_role', + ] ) read_role = ImplicitRoleField(parent_role=[ 'singleton:' + ROLE_SINGLETON_SYSTEM_AUDITOR,