mirror of
https://github.com/ansible/awx.git
synced 2026-03-13 15:09:32 -02:30
Merge pull request #3262 from ryanpetrello/openstack_cred_verify_ssl
add verify_ssl to OpenStack credential type Reviewed-by: https://github.com/softwarefactory-project-zuul[bot]
This commit is contained in:
@@ -671,6 +671,7 @@ class CredentialTypeInputField(JSONSchemaField):
|
|||||||
'multiline': {'type': 'boolean'},
|
'multiline': {'type': 'boolean'},
|
||||||
'secret': {'type': 'boolean'},
|
'secret': {'type': 'boolean'},
|
||||||
'ask_at_runtime': {'type': 'boolean'},
|
'ask_at_runtime': {'type': 'boolean'},
|
||||||
|
'default': {},
|
||||||
},
|
},
|
||||||
'additionalProperties': False,
|
'additionalProperties': False,
|
||||||
'required': ['id', 'label'],
|
'required': ['id', 'label'],
|
||||||
@@ -714,6 +715,14 @@ class CredentialTypeInputField(JSONSchemaField):
|
|||||||
# If no type is specified, default to string
|
# If no type is specified, default to string
|
||||||
field['type'] = 'string'
|
field['type'] = 'string'
|
||||||
|
|
||||||
|
if 'default' in field:
|
||||||
|
default = field['default']
|
||||||
|
_type = {'string': str, 'boolean': bool}[field['type']]
|
||||||
|
if type(default) != _type:
|
||||||
|
raise django_exceptions.ValidationError(
|
||||||
|
_('{} is not a {}').format(default, field['type'])
|
||||||
|
)
|
||||||
|
|
||||||
for key in ('choices', 'multiline', 'format', 'secret',):
|
for key in ('choices', 'multiline', 'format', 'secret',):
|
||||||
if key in field and field['type'] != 'string':
|
if key in field and field['type'] != 'string':
|
||||||
raise django_exceptions.ValidationError(
|
raise django_exceptions.ValidationError(
|
||||||
|
|||||||
@@ -445,6 +445,9 @@ class Credential(PasswordFieldsModel, CommonModelNameNotUnique, ResourceMixin):
|
|||||||
try:
|
try:
|
||||||
return decrypt_field(self, field_name)
|
return decrypt_field(self, field_name)
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
|
for field in self.credential_type.inputs.get('fields', []):
|
||||||
|
if field['id'] == field_name and 'default' in field:
|
||||||
|
return field['default']
|
||||||
if 'default' in kwargs:
|
if 'default' in kwargs:
|
||||||
return kwargs['default']
|
return kwargs['default']
|
||||||
raise AttributeError
|
raise AttributeError
|
||||||
@@ -452,6 +455,9 @@ class Credential(PasswordFieldsModel, CommonModelNameNotUnique, ResourceMixin):
|
|||||||
return self.inputs[field_name]
|
return self.inputs[field_name]
|
||||||
if 'default' in kwargs:
|
if 'default' in kwargs:
|
||||||
return kwargs['default']
|
return kwargs['default']
|
||||||
|
for field in self.credential_type.inputs.get('fields', []):
|
||||||
|
if field['id'] == field_name and 'default' in field:
|
||||||
|
return field['default']
|
||||||
raise AttributeError(field_name)
|
raise AttributeError(field_name)
|
||||||
|
|
||||||
def has_input(self, field_name):
|
def has_input(self, field_name):
|
||||||
@@ -970,6 +976,11 @@ ManagedCredentialType(
|
|||||||
'It is only needed for Keystone v3 authentication '
|
'It is only needed for Keystone v3 authentication '
|
||||||
'URLs. Refer to Ansible Tower documentation for '
|
'URLs. Refer to Ansible Tower documentation for '
|
||||||
'common scenarios.')
|
'common scenarios.')
|
||||||
|
}, {
|
||||||
|
'id': 'verify_ssl',
|
||||||
|
'label': ugettext_noop('Verify SSL'),
|
||||||
|
'type': 'boolean',
|
||||||
|
'default': True,
|
||||||
}],
|
}],
|
||||||
'required': ['username', 'password', 'host', 'project']
|
'required': ['username', 'password', 'host', 'project']
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1167,10 +1167,12 @@ class RunJob(BaseTask):
|
|||||||
project_name=credential.get_input('project', default=''))
|
project_name=credential.get_input('project', default=''))
|
||||||
if credential.has_input('domain'):
|
if credential.has_input('domain'):
|
||||||
openstack_auth['domain_name'] = credential.get_input('domain', default='')
|
openstack_auth['domain_name'] = credential.get_input('domain', default='')
|
||||||
|
verify_state = credential.get_input('verify_ssl', default=True)
|
||||||
openstack_data = {
|
openstack_data = {
|
||||||
'clouds': {
|
'clouds': {
|
||||||
'devstack': {
|
'devstack': {
|
||||||
'auth': openstack_auth,
|
'auth': openstack_auth,
|
||||||
|
'verify': verify_state,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -1875,6 +1877,7 @@ class RunInventoryUpdate(BaseTask):
|
|||||||
openstack_auth['domain_name'] = credential.get_input('domain', default='')
|
openstack_auth['domain_name'] = credential.get_input('domain', default='')
|
||||||
|
|
||||||
private_state = inventory_update.source_vars_dict.get('private', True)
|
private_state = inventory_update.source_vars_dict.get('private', True)
|
||||||
|
verify_state = credential.get_input('verify_ssl', default=True)
|
||||||
# Retrieve cache path from inventory update vars if available,
|
# Retrieve cache path from inventory update vars if available,
|
||||||
# otherwise create a temporary cache path only for this update.
|
# otherwise create a temporary cache path only for this update.
|
||||||
cache = inventory_update.source_vars_dict.get('cache', {})
|
cache = inventory_update.source_vars_dict.get('cache', {})
|
||||||
@@ -1887,6 +1890,7 @@ class RunInventoryUpdate(BaseTask):
|
|||||||
'clouds': {
|
'clouds': {
|
||||||
'devstack': {
|
'devstack': {
|
||||||
'private': private_state,
|
'private': private_state,
|
||||||
|
'verify': verify_state,
|
||||||
'auth': openstack_auth,
|
'auth': openstack_auth,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1353,6 +1353,40 @@ def test_openstack_create_ok(post, organization, admin, version, params):
|
|||||||
assert response.status_code == 201
|
assert response.status_code == 201
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.django_db
|
||||||
|
@pytest.mark.parametrize('verify_ssl, expected', [
|
||||||
|
[None, True],
|
||||||
|
[True, True],
|
||||||
|
[False, False],
|
||||||
|
])
|
||||||
|
def test_openstack_verify_ssl(get, post, organization, admin, verify_ssl, expected):
|
||||||
|
openstack = CredentialType.defaults['openstack']()
|
||||||
|
openstack.save()
|
||||||
|
inputs = {
|
||||||
|
'username': 'some_user',
|
||||||
|
'password': 'some_password',
|
||||||
|
'project': 'some_project',
|
||||||
|
'host': 'some_host',
|
||||||
|
}
|
||||||
|
if verify_ssl is not None:
|
||||||
|
inputs['verify_ssl'] = verify_ssl
|
||||||
|
params = {
|
||||||
|
'credential_type': openstack.id,
|
||||||
|
'inputs': inputs,
|
||||||
|
'name': 'Best credential ever',
|
||||||
|
'organization': organization.id
|
||||||
|
}
|
||||||
|
response = post(
|
||||||
|
reverse('api:credential_list', kwargs={'version': 'v2'}),
|
||||||
|
params,
|
||||||
|
admin
|
||||||
|
)
|
||||||
|
assert response.status_code == 201
|
||||||
|
|
||||||
|
cred = Credential.objects.get(pk=response.data['id'])
|
||||||
|
assert cred.get_input('verify_ssl') == expected
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.django_db
|
@pytest.mark.django_db
|
||||||
@pytest.mark.parametrize('version, params', [
|
@pytest.mark.parametrize('version, params', [
|
||||||
['v1', {}],
|
['v1', {}],
|
||||||
|
|||||||
@@ -271,6 +271,74 @@ def test_create_with_required_inputs(get, post, admin):
|
|||||||
assert required == ['api_token']
|
assert required == ['api_token']
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.django_db
|
||||||
|
@pytest.mark.parametrize('default, status_code', [
|
||||||
|
['some default string', 201],
|
||||||
|
[None, 400],
|
||||||
|
[True, 400],
|
||||||
|
[False, 400],
|
||||||
|
])
|
||||||
|
@pytest.mark.parametrize('secret', [True, False])
|
||||||
|
def test_create_with_default_string(get, post, admin, default, status_code, secret):
|
||||||
|
response = post(reverse('api:credential_type_list'), {
|
||||||
|
'kind': 'cloud',
|
||||||
|
'name': 'MyCloud',
|
||||||
|
'inputs': {
|
||||||
|
'fields': [{
|
||||||
|
'id': 'api_token',
|
||||||
|
'label': 'API Token',
|
||||||
|
'type': 'string',
|
||||||
|
'secret': secret,
|
||||||
|
'default': default,
|
||||||
|
}],
|
||||||
|
'required': ['api_token'],
|
||||||
|
},
|
||||||
|
'injectors': {}
|
||||||
|
}, admin)
|
||||||
|
assert response.status_code == status_code
|
||||||
|
if status_code == 201:
|
||||||
|
cred = Credential(
|
||||||
|
credential_type=CredentialType.objects.get(pk=response.data['id']),
|
||||||
|
name='My Custom Cred'
|
||||||
|
)
|
||||||
|
assert cred.get_input('api_token') == default
|
||||||
|
elif status_code == 400:
|
||||||
|
assert "{} is not a string".format(default) in json.dumps(response.data)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.django_db
|
||||||
|
@pytest.mark.parametrize('default, status_code', [
|
||||||
|
['some default string', 400],
|
||||||
|
[None, 400],
|
||||||
|
[True, 201],
|
||||||
|
[False, 201],
|
||||||
|
])
|
||||||
|
def test_create_with_default_bool(get, post, admin, default, status_code):
|
||||||
|
response = post(reverse('api:credential_type_list'), {
|
||||||
|
'kind': 'cloud',
|
||||||
|
'name': 'MyCloud',
|
||||||
|
'inputs': {
|
||||||
|
'fields': [{
|
||||||
|
'id': 'api_token',
|
||||||
|
'label': 'API Token',
|
||||||
|
'type': 'boolean',
|
||||||
|
'default': default,
|
||||||
|
}],
|
||||||
|
'required': ['api_token'],
|
||||||
|
},
|
||||||
|
'injectors': {}
|
||||||
|
}, admin)
|
||||||
|
assert response.status_code == status_code
|
||||||
|
if status_code == 201:
|
||||||
|
cred = Credential(
|
||||||
|
credential_type=CredentialType.objects.get(pk=response.data['id']),
|
||||||
|
name='My Custom Cred'
|
||||||
|
)
|
||||||
|
assert cred.get_input('api_token') == default
|
||||||
|
elif status_code == 400:
|
||||||
|
assert "{} is not a boolean".format(default) in json.dumps(response.data)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.django_db
|
@pytest.mark.django_db
|
||||||
@pytest.mark.parametrize('inputs', [
|
@pytest.mark.parametrize('inputs', [
|
||||||
True,
|
True,
|
||||||
|
|||||||
@@ -105,7 +105,10 @@ def test_safe_env_returns_new_copy():
|
|||||||
assert build_safe_env(env) is not env
|
assert build_safe_env(env) is not env
|
||||||
|
|
||||||
|
|
||||||
def test_openstack_client_config_generation(mocker):
|
@pytest.mark.parametrize("source,expected", [
|
||||||
|
(None, True), (False, False), (True, True)
|
||||||
|
])
|
||||||
|
def test_openstack_client_config_generation(mocker, source, expected):
|
||||||
update = tasks.RunInventoryUpdate()
|
update = tasks.RunInventoryUpdate()
|
||||||
credential_type = CredentialType.defaults['openstack']()
|
credential_type = CredentialType.defaults['openstack']()
|
||||||
inputs = {
|
inputs = {
|
||||||
@@ -113,8 +116,10 @@ def test_openstack_client_config_generation(mocker):
|
|||||||
'username': 'demo',
|
'username': 'demo',
|
||||||
'password': 'secrete',
|
'password': 'secrete',
|
||||||
'project': 'demo-project',
|
'project': 'demo-project',
|
||||||
'domain': 'my-demo-domain',
|
'domain': 'my-demo-domain'
|
||||||
}
|
}
|
||||||
|
if source is not None:
|
||||||
|
inputs['verify_ssl'] = source
|
||||||
credential = Credential(pk=1, credential_type=credential_type, inputs=inputs)
|
credential = Credential(pk=1, credential_type=credential_type, inputs=inputs)
|
||||||
|
|
||||||
cred_method = mocker.Mock(return_value=credential)
|
cred_method = mocker.Mock(return_value=credential)
|
||||||
@@ -136,7 +141,8 @@ def test_openstack_client_config_generation(mocker):
|
|||||||
'username': 'demo',
|
'username': 'demo',
|
||||||
'domain_name': 'my-demo-domain',
|
'domain_name': 'my-demo-domain',
|
||||||
},
|
},
|
||||||
'private': True
|
'verify': expected,
|
||||||
|
'private': True,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -153,6 +159,7 @@ def test_openstack_client_config_generation_with_private_source_vars(mocker, sou
|
|||||||
'password': 'secrete',
|
'password': 'secrete',
|
||||||
'project': 'demo-project',
|
'project': 'demo-project',
|
||||||
'domain': None,
|
'domain': None,
|
||||||
|
'verify_ssl': True,
|
||||||
}
|
}
|
||||||
credential = Credential(pk=1, credential_type=credential_type, inputs=inputs)
|
credential = Credential(pk=1, credential_type=credential_type, inputs=inputs)
|
||||||
|
|
||||||
@@ -174,6 +181,7 @@ def test_openstack_client_config_generation_with_private_source_vars(mocker, sou
|
|||||||
'project_name': 'demo-project',
|
'project_name': 'demo-project',
|
||||||
'username': 'demo'
|
'username': 'demo'
|
||||||
},
|
},
|
||||||
|
'verify': True,
|
||||||
'private': expected
|
'private': expected
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1145,6 +1153,7 @@ class TestJobCredentials(TestJobExecution):
|
|||||||
' password: secret',
|
' password: secret',
|
||||||
' project_name: tenant-name',
|
' project_name: tenant-name',
|
||||||
' username: bob',
|
' username: bob',
|
||||||
|
' verify: true',
|
||||||
''
|
''
|
||||||
])
|
])
|
||||||
return ['successful', 0]
|
return ['successful', 0]
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ function BaseInputController (strings) {
|
|||||||
scope.state._displayPromptOnLaunch = true;
|
scope.state._displayPromptOnLaunch = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (scope.state._value) {
|
if (typeof scope.state._value !== 'undefined') {
|
||||||
scope.state._edit = true;
|
scope.state._edit = true;
|
||||||
scope.state._preEditValue = scope.state._value;
|
scope.state._preEditValue = scope.state._value;
|
||||||
|
|
||||||
@@ -37,6 +37,8 @@ function BaseInputController (strings) {
|
|||||||
scope.state._isBeingReplaced = false;
|
scope.state._isBeingReplaced = false;
|
||||||
scope.state._activeModel = '_displayValue';
|
scope.state._activeModel = '_displayValue';
|
||||||
}
|
}
|
||||||
|
} else if (typeof scope.state.default !== 'undefined') {
|
||||||
|
scope.state._value = scope.state.default;
|
||||||
}
|
}
|
||||||
|
|
||||||
form.register(type, scope);
|
form.register(type, scope);
|
||||||
|
|||||||
@@ -136,6 +136,12 @@ ordered fields for that type:
|
|||||||
"multiline": false # if true, the field should be rendered
|
"multiline": false # if true, the field should be rendered
|
||||||
# as multi-line for input entry
|
# as multi-line for input entry
|
||||||
# (only applicable to `type=string`)
|
# (only applicable to `type=string`)
|
||||||
|
"default": "default value" # optional, can be used to provide a
|
||||||
|
# default value if the field is left empty
|
||||||
|
# when creating a credential of this type
|
||||||
|
# credential forms will use this value
|
||||||
|
# as a prefill when making credentials of
|
||||||
|
# this type
|
||||||
},{
|
},{
|
||||||
# field 2...
|
# field 2...
|
||||||
},{
|
},{
|
||||||
|
|||||||
Reference in New Issue
Block a user