diff --git a/awx/api/views/__init__.py b/awx/api/views/__init__.py index 71355f46f7..24b760d8b0 100644 --- a/awx/api/views/__init__.py +++ b/awx/api/views/__init__.py @@ -1440,8 +1440,11 @@ class CredentialExternalTest(SubDetailAPIView): backend_kwargs[field_name] = value backend_kwargs.update(request.data.get('metadata', {})) try: - obj.credential_type.plugin.backend(None, **backend_kwargs) + obj.credential_type.plugin.backend(**backend_kwargs) return Response({}, status=status.HTTP_202_ACCEPTED) + except requests.exceptions.HTTPError as exc: + message = 'HTTP {}\n{}'.format(exc.response.status_code, exc.response.text) + return Response({'inputs': message}, status=status.HTTP_400_BAD_REQUEST) except Exception as exc: return Response({'inputs': str(exc)}, status=status.HTTP_400_BAD_REQUEST) @@ -1489,8 +1492,11 @@ class CredentialTypeExternalTest(SubDetailAPIView): backend_kwargs = request.data.get('inputs', {}) backend_kwargs.update(request.data.get('metadata', {})) try: - obj.plugin.backend(None, **backend_kwargs) + obj.plugin.backend(**backend_kwargs) return Response({}, status=status.HTTP_202_ACCEPTED) + except requests.exceptions.HTTPError as exc: + message = 'HTTP {}\n{}'.format(exc.response.status_code, exc.response.text) + return Response({'inputs': message}, status=status.HTTP_400_BAD_REQUEST) except Exception as exc: return Response({'inputs': str(exc)}, status=status.HTTP_400_BAD_REQUEST) diff --git a/awx/main/credential_plugins/azure_kv.py b/awx/main/credential_plugins/azure_kv.py index 38c6845715..ee193974d0 100644 --- a/awx/main/credential_plugins/azure_kv.py +++ b/awx/main/credential_plugins/azure_kv.py @@ -39,7 +39,7 @@ azure_keyvault_inputs = { } -def azure_keyvault_backend(raw, **kwargs): +def azure_keyvault_backend(**kwargs): url = kwargs['url'] def auth_callback(server, resource, scope): diff --git a/awx/main/credential_plugins/conjur.py b/awx/main/credential_plugins/conjur.py index f5b6ae23d0..645ff16afa 100644 --- a/awx/main/credential_plugins/conjur.py +++ b/awx/main/credential_plugins/conjur.py @@ -47,7 +47,7 @@ conjur_inputs = { } -def conjur_backend(raw, **kwargs): +def conjur_backend(**kwargs): url = kwargs['url'] api_key = kwargs['api_key'] account = quote_plus(kwargs['account']) diff --git a/awx/main/credential_plugins/hashivault.py b/awx/main/credential_plugins/hashivault.py index 89519492b9..2f058d9dad 100644 --- a/awx/main/credential_plugins/hashivault.py +++ b/awx/main/credential_plugins/hashivault.py @@ -53,7 +53,12 @@ hashi_kv_inputs['metadata'].extend([{ hashi_kv_inputs['required'].extend(['api_version', 'secret_key']) hashi_ssh_inputs = copy.deepcopy(base_inputs) -hashi_ssh_inputs['metadata'].extend([{ +hashi_ssh_inputs['metadata'] = [{ + 'id': 'public_key', + 'label': _('Unsigned Public Key'), + 'type': 'string', + 'multiline': True, +}] + hashi_ssh_inputs['metadata'] + [{ 'id': 'role', 'label': _('Role Name'), 'type': 'string', @@ -63,11 +68,11 @@ hashi_ssh_inputs['metadata'].extend([{ 'label': _('Valid Principals'), 'type': 'string', 'help_text': _('Valid principals (either usernames or hostnames) that the certificate should be signed for.'), -}]) -hashi_ssh_inputs['required'].extend(['role']) +}] +hashi_ssh_inputs['required'].extend(['public_key', 'role']) -def kv_backend(raw, **kwargs): +def kv_backend(**kwargs): token = kwargs['token'] url = urljoin(kwargs['url'], 'v1') secret_path = kwargs['secret_path'] @@ -109,7 +114,7 @@ def kv_backend(raw, **kwargs): return json['data'] -def ssh_backend(raw, **kwargs): +def ssh_backend(**kwargs): token = kwargs['token'] url = urljoin(kwargs['url'], 'v1') secret_path = kwargs['secret_path'] @@ -118,7 +123,7 @@ def ssh_backend(raw, **kwargs): sess = requests.Session() sess.headers['Authorization'] = 'Bearer {}'.format(token) json = { - 'public_key': raw + 'public_key': kwargs['public_key'] } if kwargs.get('valid_principals'): json['valid_principals'] = kwargs['valid_principals'] diff --git a/awx/main/models/credential/__init__.py b/awx/main/models/credential/__init__.py index ea24636eb1..2d9f9892d5 100644 --- a/awx/main/models/credential/__init__.py +++ b/awx/main/models/credential/__init__.py @@ -1362,13 +1362,7 @@ class CredentialInputSource(PrimordialModel): backend_kwargs[field_name] = value backend_kwargs.update(self.metadata) - raw = self.target_credential.inputs.get(self.input_field_name) - if self.input_field_name in self.target_credential.credential_type.secret_fields: - raw = decrypt_field(self.target_credential, self.input_field_name) - return backend( - raw, - **backend_kwargs - ) + return backend(**backend_kwargs) def get_absolute_url(self, request=None): view_name = 'api:credential_input_source_detail' diff --git a/docs/credential_plugins.md b/docs/credential_plugins.md index d00e66bdb2..7a4752d7a7 100644 --- a/docs/credential_plugins.md +++ b/docs/credential_plugins.md @@ -124,22 +124,6 @@ setuptools.setup( ) ``` -Fetching vs. Transforming Credential Data ------------------------------------------ -While _most_ credential plugins will be used to _fetch_ secrets from external -systems, they can also be used to *transform* data from Tower _using_ an -external secret management system. An example use case is generating signed -public keys: - -```python -def my_key_signer(unsigned_value_from_awx, **kwargs): - return some_libary.sign( - url=kwargs['url'], - token=kwargs['token'], - public_data=unsigned_value_from_awx - ) -``` - Programmatic Secret Fetching ---------------------------- If you want to programmatically fetch secrets from a supported external secret @@ -288,7 +272,7 @@ HTTP/1.1 200 OK -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -X POST \ - -d '{"user": N, "credential_type": 1, "name": "My SSH", "inputs": {"username": "example", "ssh_key_data": "RSA KEY DATA", "ssh_public_key_data": "UNSIGNED PUBLIC KEY DATA"}}' + -d '{"user": N, "credential_type": 1, "name": "My SSH", "inputs": {"username": "example", "ssh_key_data": "RSA KEY DATA"}}' HTTP/1.1 201 Created { @@ -320,7 +304,7 @@ HTTP/1.1 201 Created -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -X POST \ - -d '{"source_credential": 2, "input_field_name": "password", "metadata": {"secret_path": "/ssh/", "role": "example-role"}}' + -d '{"source_credential": 2, "input_field_name": "password", "metadata": {"public_key": "UNSIGNED PUBLIC KEY", "secret_path": "/ssh/", "role": "example-role"}}' HTTP/1.1 201 Created ```