mirror of
https://github.com/ansible/awx.git
synced 2026-05-07 17:37:37 -02:30
Merge pull request #12227 from ansible/vaultcredentialsbug
Prevent edit of vault ID once credential is created.
This commit is contained in:
@@ -2664,6 +2664,13 @@ class CredentialSerializer(BaseSerializer):
|
|||||||
|
|
||||||
return credential_type
|
return credential_type
|
||||||
|
|
||||||
|
def validate_inputs(self, inputs):
|
||||||
|
if self.instance and self.instance.credential_type.kind == "vault":
|
||||||
|
if 'vault_id' in inputs and inputs['vault_id'] != self.instance.inputs['vault_id']:
|
||||||
|
raise ValidationError(_('Vault IDs cannot be changed once they have been created.'))
|
||||||
|
|
||||||
|
return inputs
|
||||||
|
|
||||||
|
|
||||||
class CredentialSerializerCreate(CredentialSerializer):
|
class CredentialSerializerCreate(CredentialSerializer):
|
||||||
|
|
||||||
|
|||||||
@@ -532,6 +532,49 @@ def test_vault_password_required(post, organization, admin):
|
|||||||
assert 'required fields (vault_password)' in j.job_explanation
|
assert 'required fields (vault_password)' in j.job_explanation
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.django_db
|
||||||
|
def test_vault_id_immutable(post, patch, organization, admin):
|
||||||
|
vault = CredentialType.defaults['vault']()
|
||||||
|
vault.save()
|
||||||
|
response = post(
|
||||||
|
reverse('api:credential_list'),
|
||||||
|
{
|
||||||
|
'credential_type': vault.pk,
|
||||||
|
'organization': organization.id,
|
||||||
|
'name': 'Best credential ever',
|
||||||
|
'inputs': {'vault_id': 'password', 'vault_password': 'password'},
|
||||||
|
},
|
||||||
|
admin,
|
||||||
|
)
|
||||||
|
assert response.status_code == 201
|
||||||
|
assert Credential.objects.count() == 1
|
||||||
|
response = patch(
|
||||||
|
reverse('api:credential_detail', kwargs={'pk': response.data['id']}), {'inputs': {'vault_id': 'password2', 'vault_password': 'password'}}, admin
|
||||||
|
)
|
||||||
|
assert response.status_code == 400
|
||||||
|
assert response.data['inputs'][0] == 'Vault IDs cannot be changed once they have been created.'
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.django_db
|
||||||
|
def test_patch_without_vault_id_valid(post, patch, organization, admin):
|
||||||
|
vault = CredentialType.defaults['vault']()
|
||||||
|
vault.save()
|
||||||
|
response = post(
|
||||||
|
reverse('api:credential_list'),
|
||||||
|
{
|
||||||
|
'credential_type': vault.pk,
|
||||||
|
'organization': organization.id,
|
||||||
|
'name': 'Best credential ever',
|
||||||
|
'inputs': {'vault_id': 'password', 'vault_password': 'password'},
|
||||||
|
},
|
||||||
|
admin,
|
||||||
|
)
|
||||||
|
assert response.status_code == 201
|
||||||
|
assert Credential.objects.count() == 1
|
||||||
|
response = patch(reverse('api:credential_detail', kwargs={'pk': response.data['id']}), {'name': 'worst_credential_ever'}, admin)
|
||||||
|
assert response.status_code == 200
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Net Credentials
|
# Net Credentials
|
||||||
#
|
#
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
/* eslint-disable react/jsx-no-useless-fragment */
|
/* eslint-disable react/jsx-no-useless-fragment */
|
||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
|
import { useLocation } from 'react-router-dom';
|
||||||
import { useField, useFormikContext } from 'formik';
|
import { useField, useFormikContext } from 'formik';
|
||||||
import { shape, string } from 'prop-types';
|
import { shape, string } from 'prop-types';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
@@ -31,6 +32,7 @@ function CredentialInput({
|
|||||||
fieldOptions,
|
fieldOptions,
|
||||||
isFieldGroupValid,
|
isFieldGroupValid,
|
||||||
credentialKind,
|
credentialKind,
|
||||||
|
isVaultIdDisabled,
|
||||||
...rest
|
...rest
|
||||||
}) {
|
}) {
|
||||||
const [fileName, setFileName] = useState('');
|
const [fileName, setFileName] = useState('');
|
||||||
@@ -148,6 +150,7 @@ function CredentialInput({
|
|||||||
onChange={(value, event) => {
|
onChange={(value, event) => {
|
||||||
subFormField.onChange(event);
|
subFormField.onChange(event);
|
||||||
}}
|
}}
|
||||||
|
isDisabled={isVaultIdDisabled}
|
||||||
validated={isValid ? 'default' : 'error'}
|
validated={isValid ? 'default' : 'error'}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
@@ -167,6 +170,7 @@ CredentialInput.defaultProps = {
|
|||||||
|
|
||||||
function CredentialField({ credentialType, fieldOptions }) {
|
function CredentialField({ credentialType, fieldOptions }) {
|
||||||
const { values: formikValues } = useFormikContext();
|
const { values: formikValues } = useFormikContext();
|
||||||
|
const location = useLocation();
|
||||||
const requiredFields = credentialType?.inputs?.required || [];
|
const requiredFields = credentialType?.inputs?.required || [];
|
||||||
const isRequired = requiredFields.includes(fieldOptions.id);
|
const isRequired = requiredFields.includes(fieldOptions.id);
|
||||||
const validateField = () => {
|
const validateField = () => {
|
||||||
@@ -242,6 +246,15 @@ function CredentialField({ credentialType, fieldOptions }) {
|
|||||||
<BecomeMethodField fieldOptions={fieldOptions} isRequired={isRequired} />
|
<BecomeMethodField fieldOptions={fieldOptions} isRequired={isRequired} />
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let disabled = false;
|
||||||
|
if (
|
||||||
|
credentialType.kind === 'vault' &&
|
||||||
|
location.pathname.endsWith('edit') &&
|
||||||
|
fieldOptions.id === 'vault_id'
|
||||||
|
) {
|
||||||
|
disabled = true;
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<CredentialPluginField
|
<CredentialPluginField
|
||||||
fieldOptions={fieldOptions}
|
fieldOptions={fieldOptions}
|
||||||
@@ -251,6 +264,7 @@ function CredentialField({ credentialType, fieldOptions }) {
|
|||||||
<CredentialInput
|
<CredentialInput
|
||||||
isFieldGroupValid={isValid}
|
isFieldGroupValid={isValid}
|
||||||
fieldOptions={fieldOptions}
|
fieldOptions={fieldOptions}
|
||||||
|
isVaultIdDisabled={disabled}
|
||||||
/>
|
/>
|
||||||
</CredentialPluginField>
|
</CredentialPluginField>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -13,6 +13,12 @@ const fieldOptions = {
|
|||||||
secret: true,
|
secret: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
jest.mock('react-router-dom', () => ({
|
||||||
|
...jest.requireActual('react-router-dom'),
|
||||||
|
useLocation: () => ({
|
||||||
|
pathname: '/credentials/3/edit',
|
||||||
|
}),
|
||||||
|
}));
|
||||||
describe('<CredentialField />', () => {
|
describe('<CredentialField />', () => {
|
||||||
let wrapper;
|
let wrapper;
|
||||||
test('renders correctly without initial value', () => {
|
test('renders correctly without initial value', () => {
|
||||||
@@ -113,4 +119,33 @@ describe('<CredentialField />', () => {
|
|||||||
expect(wrapper.find('TextInput').props().value).toBe('');
|
expect(wrapper.find('TextInput').props().value).toBe('');
|
||||||
expect(wrapper.find('TextInput').props().placeholder).toBe('ENCRYPTED');
|
expect(wrapper.find('TextInput').props().placeholder).toBe('ENCRYPTED');
|
||||||
});
|
});
|
||||||
|
test('Should check to see if the ability to edit vault ID is disabled after creation.', () => {
|
||||||
|
const vaultCredential = credentialTypes.find((type) => type.id === 3);
|
||||||
|
const vaultFieldOptions = {
|
||||||
|
id: 'vault_id',
|
||||||
|
label: 'Vault Identifier',
|
||||||
|
type: 'string',
|
||||||
|
secret: true,
|
||||||
|
};
|
||||||
|
wrapper = mountWithContexts(
|
||||||
|
<Formik
|
||||||
|
initialValues={{
|
||||||
|
passwordPrompts: {},
|
||||||
|
inputs: {
|
||||||
|
password: 'password',
|
||||||
|
vault_id: 'vault_id',
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{() => (
|
||||||
|
<CredentialField
|
||||||
|
fieldOptions={vaultFieldOptions}
|
||||||
|
credentialType={vaultCredential}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</Formik>
|
||||||
|
);
|
||||||
|
expect(wrapper.find('CredentialInput').props().isDisabled).toBe(true);
|
||||||
|
expect(wrapper.find('KeyIcon').length).toBe(1);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user