diff options
author | Seth Foster <fosterseth@users.noreply.github.com> | 2024-09-13 23:56:43 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-09-13 23:56:43 +0200 |
commit | 3baea0f206fa0901c1811408a39533f941816c0a (patch) | |
tree | cf8a0081a2f308647f5c6b636ff60dc4dee5697b | |
parent | Fix instance UI URL generated by API (#15517) (diff) | |
download | awx-3baea0f206fa0901c1811408a39533f941816c0a.tar.xz awx-3baea0f206fa0901c1811408a39533f941816c0a.zip |
Validate org-user membership from gateway (#15508)
Adding credential and execution environment roles
validates that the user belongs to the same org
as the credential or EE.
In some situations, the user-org membership has not
yet been synced from gateway to controller.
In this case, controller will make a request to
gateway to check if the user is part of the org.
Signed-off-by: Seth Foster <fosterbseth@gmail.com>
-rw-r--r-- | awx/main/models/credential.py | 55 | ||||
-rw-r--r-- | awx/main/models/execution_environments.py | 13 |
2 files changed, 64 insertions, 4 deletions
diff --git a/awx/main/models/credential.py b/awx/main/models/credential.py index 2f4a56a890..53d817e346 100644 --- a/awx/main/models/credential.py +++ b/awx/main/models/credential.py @@ -53,6 +53,11 @@ from awx.main.models import Team, Organization from awx.main.utils import encrypt_field from awx_plugins.credentials import injectors as builtin_injectors +# DAB +from ansible_base.resource_registry.tasks.sync import get_resource_server_client +from ansible_base.resource_registry.utils.settings import resource_server_defined + + __all__ = ['Credential', 'CredentialType', 'CredentialInputSource', 'build_safe_env'] logger = logging.getLogger('awx.main.models.credential') @@ -81,6 +86,46 @@ def build_safe_env(env): return safe_env +def check_resource_server_for_user_in_organization(user, organization, requesting_user): + if not resource_server_defined(): + return False + + if not requesting_user: + return False + + client = get_resource_server_client(settings.RESOURCE_SERVICE_PATH, jwt_user_id=str(requesting_user.resource.ansible_id), raise_if_bad_request=False) + # need to get the organization object_id in resource server, by querying with ansible_id + response = client._make_request(path=f'resources/?ansible_id={str(organization.resource.ansible_id)}', method='GET') + response_json = response.json() + if response.status_code != 200: + logger.error(f'Failed to get organization object_id in resource server: {response_json.get("detail", "")}') + return False + + if response_json.get('count', 0) == 0: + return False + org_id_in_resource_server = response_json['results'][0]['object_id'] + + client.base_url = client.base_url.replace('/api/gateway/v1/service-index/', '/api/gateway/v1/') + # find role assignments with: + # - roles Organization Member or Organization Admin + # - user ansible id + # - organization object id + + response = client._make_request( + path=f'role_user_assignments/?role_definition__name__in=Organization Member,Organization Admin&user__resource__ansible_id={str(user.resource.ansible_id)}&object_id={org_id_in_resource_server}', + method='GET', + ) + response_json = response.json() + if response.status_code != 200: + logger.error(f'Failed to get role user assignments in resource server: {response_json.get("detail", "")}') + return False + + if response_json.get('count', 0) > 0: + return True + + return False + + class Credential(PasswordFieldsModel, CommonModelNameNotUnique, ResourceMixin): """ A credential contains information about how to talk to a remote resource @@ -324,10 +369,16 @@ class Credential(PasswordFieldsModel, CommonModelNameNotUnique, ResourceMixin): else: raise ValueError('{} is not a dynamic input field'.format(field_name)) - def validate_role_assignment(self, actor, role_definition): + def validate_role_assignment(self, actor, role_definition, **kwargs): if self.organization: if isinstance(actor, User): - if actor.is_superuser or Organization.access_qs(actor, 'member').filter(id=self.organization.id).exists(): + if actor.is_superuser: + return + if Organization.access_qs(actor, 'member').filter(id=self.organization.id).exists(): + return + + requesting_user = kwargs.get('requesting_user', None) + if check_resource_server_for_user_in_organization(actor, self.organization, requesting_user): return if isinstance(actor, Team): if actor.organization == self.organization: diff --git a/awx/main/models/execution_environments.py b/awx/main/models/execution_environments.py index 321b38264b..ea74125f8f 100644 --- a/awx/main/models/execution_environments.py +++ b/awx/main/models/execution_environments.py @@ -58,11 +58,20 @@ class ExecutionEnvironment(CommonModel): def get_absolute_url(self, request=None): return reverse('api:execution_environment_detail', kwargs={'pk': self.pk}, request=request) - def validate_role_assignment(self, actor, role_definition): + def validate_role_assignment(self, actor, role_definition, **kwargs): + from awx.main.models.credential import check_resource_server_for_user_in_organization + if self.managed: raise ValidationError({'object_id': _('Can not assign object roles to managed Execution Environments')}) if self.organization_id is None: raise ValidationError({'object_id': _('Can not assign object roles to global Execution Environments')}) - if actor._meta.model_name == 'user' and (not actor.has_obj_perm(self.organization, 'view')): + if actor._meta.model_name == 'user': + if actor.has_obj_perm(self.organization, 'view'): + return + + requesting_user = kwargs.get('requesting_user', None) + if check_resource_server_for_user_in_organization(actor, self.organization, requesting_user): + return + raise ValidationError({'user': _('User must have view permission to Execution Environment organization')}) |