def test_clean_credential_with_ssh_type(credentialtype_ssh, job_template): credential = Credential(name='My Credential', credential_type=credentialtype_ssh) credential.save() job_template.credential = credential job_template.full_clean()
def test_prevent_multiple_machine_creds_at_launch(get, post, job_template, admin, machine_credential): other_cred = Credential(credential_type=machine_credential.credential_type, name="Second", inputs={'username': '******'}) other_cred.save() creds = [machine_credential.pk, other_cred.pk] url = reverse('api:job_template_launch', kwargs={'pk': job_template.pk}) resp = post(url, {'credentials': creds}, admin) assert 'Cannot assign multiple Machine credentials.' in smart_str(resp.content)
def test_field_removal(put, organization, admin, credentialtype_ssh): params = { 'name': 'Best credential ever', 'credential_type': 1, 'inputs': { 'username': '******', 'password': '', } } cred = Credential(credential_type=credentialtype_ssh, name='Best credential ever', organization=organization, inputs={ 'username': u'jim', 'password': u'secret' }) cred.save() params['organization'] = organization.id response = put(reverse('api:credential_detail', kwargs={'pk': cred.pk}), params, admin) assert response.status_code == 200 cred = Credential.objects.all()[:1].get() assert cred.inputs['username'] == 'joe' assert 'password' not in cred.inputs
def test_ssh_bad_key_unlock_not_checked(put, organization, admin, credentialtype_ssh): params = { 'name': 'Best credential ever', 'credential_type': 1, 'inputs': { 'username': '******', 'ssh_key_data': 'invalid-key', 'ssh_key_unlock': 'unchecked-unlock', } } cred = Credential(credential_type=credentialtype_ssh, name='Best credential ever', organization=organization, inputs={ 'username': u'oscar', 'ssh_key_data': 'invalid-key', 'ssh_key_unlock': 'unchecked-unlock', }) cred.save() params['organization'] = organization.id response = put(reverse('api:credential_detail', kwargs={'pk': cred.pk}), params, admin) assert response.status_code == 400 assert response.data['inputs']['ssh_key_data'] == [ 'Invalid certificate or key: invalid-key...' ] assert 'ssh_key_unlock' not in response.data['inputs']
def test_secret_encryption_previous_value(patch, organization, admin, credentialtype_ssh): params = { 'inputs': { 'username': '******', 'password': '******', } } cred = Credential(credential_type=credentialtype_ssh, name='Best credential ever', organization=organization, inputs={ 'username': u'jim', 'password': u'secret' }) cred.save() assert decrypt_field(cred, 'password') == 'secret' response = patch(reverse('api:credential_detail', kwargs={'pk': cred.pk}), params, admin) assert response.status_code == 200 cred = Credential.objects.all()[:1].get() assert cred.inputs['username'] == 'joe' assert cred.inputs['password'].startswith('$encrypted$UTF8$AES') assert decrypt_field(cred, 'password') == 'secret'
def job_template_with_ids(job_template_factory): # Create non-persisted objects with IDs to send to job_template_factory ssh_type = CredentialType(kind='ssh') credential = Credential(id=1, pk=1, name='testcred', credential_type=ssh_type) net_type = CredentialType(kind='net') net_cred = Credential(id=2, pk=2, name='testnetcred', credential_type=net_type) cloud_type = CredentialType(kind='aws') cloud_cred = Credential(id=3, pk=3, name='testcloudcred', credential_type=cloud_type) inv = Inventory(id=11, pk=11, name='testinv') proj = Project(id=14, pk=14, name='testproj') jt_objects = job_template_factory('testJT', project=proj, inventory=inv, credential=credential, cloud_credential=cloud_cred, network_credential=net_cred, persisted=False) jt = jt_objects.job_template jt.organization = Organization(id=1, pk=1, name='fooOrg') return jt
def test_ssh_unlock_with_prior_value(put, organization, admin, credentialtype_ssh): params = { 'name': 'Best credential ever', 'credential_type': 1, 'inputs': { 'username': '******', 'ssh_key_data': '$encrypted$', 'ssh_key_unlock': 'new-unlock', } } cred = Credential(credential_type=credentialtype_ssh, name='Best credential ever', organization=organization, inputs={ 'username': u'joe', 'ssh_key_data': EXAMPLE_ENCRYPTED_PRIVATE_KEY, 'ssh_key_unlock': 'old-unlock' }) cred.save() params['organization'] = organization.id response = put(reverse('api:credential_detail', kwargs={'pk': cred.pk}), params, admin) assert response.status_code == 200 cred = Credential.objects.all()[:1].get() assert decrypt_field(cred, 'ssh_key_unlock') == 'new-unlock'
def test_ssh_unlock_not_needed(put, organization, admin, credentialtype_ssh): params = { 'name': 'Best credential ever', 'credential_type': 1, 'inputs': { 'username': '******', 'ssh_key_data': '$encrypted$', 'ssh_key_unlock': 'superfluous-key-unlock', } } cred = Credential(credential_type=credentialtype_ssh, name='Best credential ever', organization=organization, inputs={ 'username': u'joe', 'ssh_key_data': EXAMPLE_PRIVATE_KEY, }) cred.save() params['organization'] = organization.id response = put(reverse('api:credential_detail', kwargs={'pk': cred.pk}), params, admin) assert response.status_code == 400 assert response.data['inputs']['ssh_key_unlock'] == [ 'should not be set when SSH key is not encrypted.' ]
def handle(self, *args, **kwargs): changed = False # Create a default organization as the first superuser found. try: superuser = User.objects.filter(is_superuser=True).order_by('pk')[0] except IndexError: superuser = None with impersonate(superuser): with disable_computed_fields(): if not Organization.objects.exists(): o = Organization.objects.create(name='Default') p = Project( name='Demo Project', scm_type='git', scm_url='https://github.com/ansible/ansible-tower-samples', scm_update_on_launch=True, scm_update_cache_timeout=0, organization=o, ) p.save(skip_update=True) ssh_type = CredentialType.objects.filter(namespace='ssh').first() c = Credential.objects.create( credential_type=ssh_type, name='Demo Credential', inputs={'username': superuser.username}, created_by=superuser ) c.admin_role.members.add(superuser) public_galaxy_credential = Credential( name='Ansible Galaxy', managed_by_tower=True, credential_type=CredentialType.objects.get(kind='galaxy'), inputs={'url': 'https://galaxy.ansible.com/'}, ) public_galaxy_credential.save() o.galaxy_credentials.add(public_galaxy_credential) i = Inventory.objects.create(name='Demo Inventory', organization=o, created_by=superuser) Host.objects.create( name='localhost', inventory=i, variables="ansible_connection: local\nansible_python_interpreter: '{{ ansible_playbook_python }}'", created_by=superuser, ) jt = JobTemplate.objects.create(name='Demo Job Template', playbook='hello_world.yml', project=p, inventory=i) jt.credentials.add(c) print('Default organization added.') print('Demo Credential, Inventory, and Job Template added.') changed = True if changed: print('(changed: True)') else: print('(changed: False)')
def test_unique_hash_with_unicode(): ct = CredentialType(name=u'Väult', kind='vault') cred = Credential(id=4, name=u'Iñtërnâtiônàlizætiøn', credential_type=ct, inputs={u'vault_id': u'🐉🐉🐉'}, credential_type_id=42) assert cred.unique_hash(display=True) == u'Väult (id=🐉🐉🐉)'
def test_credential_encryption_with_ask(organization_factory, credentialtype_ssh): org = organization_factory('test').organization cred = Credential(credential_type=credentialtype_ssh, name="Bob's Credential", inputs={'password': '******'}, organization=org) cred.save() assert Credential.objects.count() == 1 cred = Credential.objects.all()[:1].get() assert cred.inputs['password'] == 'ASK'
def test_clean_credential_with_invalid_type_xfail(credentialtype_aws, job_template): credential = Credential(name='My Credential', credential_type=credentialtype_aws) credential.save() with pytest.raises(ValidationError): job_template.credential = credential job_template.full_clean()
def galaxy_credential(): galaxy_type = CredentialType.objects.create(kind='galaxy') cred = Credential(created=now(), modified=now(), name='Ansible Galaxy', managed=True, credential_type=galaxy_type, inputs={'url': 'https://galaxy.ansible.com/'}) cred.save()
def test_credential_encryption(organization_factory, credentialtype_ssh): org = organization_factory('test').organization cred = Credential(credential_type=credentialtype_ssh, name="Bob's Credential", inputs={'password': '******'}, organization=org) cred.save() assert Credential.objects.count() == 1 cred = Credential.objects.all()[:1].get() assert cred.inputs['password'].startswith('$encrypted$') assert decrypt_field(cred, 'password') == 'testing123'
def mk_credential(name, credential_type='ssh', persisted=True): if persisted: type_, status = CredentialType.objects.get_or_create(kind=credential_type) type_.save() else: type_ = CredentialType.defaults[credential_type]() cred = Credential(credential_type=type_, name=name) if persisted: cred.save() return cred
def test_prompted_credential_replaced_on_launch(get, post, job_template, admin, machine_credential): # If a JT has a credential that needs a password, but the launch POST # specifies credential that does not require any passwords cred2 = Credential(name='second-cred', inputs=machine_credential.inputs, credential_type=machine_credential.credential_type) cred2.inputs['password'] = '******' cred2.save() job_template.credentials.add(cred2) url = reverse('api:job_template_launch', kwargs={'pk': job_template.pk}) resp = post(url, {}, admin, expect=400) resp = post(url, {'credentials': [machine_credential.pk]}, admin, expect=201) assert 'job' in resp.data
def test_credential_with_multiple_secrets(organization_factory, credentialtype_ssh): org = organization_factory('test').organization cred = Credential( credential_type=credentialtype_ssh, name="Bob's Credential", inputs={'ssh_key_data': 'SOMEKEY', 'ssh_key_unlock': 'testing123'}, organization=org ) cred.save() assert Credential.objects.count() == 1 cred = Credential.objects.all()[:1].get() assert cred.inputs['ssh_key_data'].startswith('$encrypted$') assert decrypt_field(cred, 'ssh_key_data') == 'SOMEKEY' assert cred.inputs['ssh_key_unlock'].startswith('$encrypted$') assert decrypt_field(cred, 'ssh_key_unlock') == 'testing123'
def job_template_with_ids(job_template_factory): # Create non-persisted objects with IDs to send to job_template_factory credential = Credential(id=1, pk=1, name='testcred', kind='ssh') net_cred = Credential(id=2, pk=2, name='testnetcred', kind='net') cloud_cred = Credential(id=3, pk=3, name='testcloudcred', kind='aws') vault_cred = Credential(id=4, pk=4, name='testnetcred', kind='vault') inv = Inventory(id=11, pk=11, name='testinv') proj = Project(id=14, pk=14, name='testproj') jt_objects = job_template_factory( 'testJT', project=proj, inventory=inv, credential=credential, cloud_credential=cloud_cred, network_credential=net_cred, persisted=False) jt_objects.job_template.vault_credential = vault_cred return jt_objects.job_template
def test_custom_cred_with_empty_encrypted_field(): ct = CredentialType(name='My Custom Cred', kind='custom', inputs={ 'fields': [{ 'id': 'some_field', 'label': 'My Field', 'secret': True }] }) cred = Credential(id=4, name='Testing 1 2 3', credential_type=ct, inputs={}) assert cred.encrypt_field('some_field', None) is None
def test_ssh_migration(): cred = Credential(name='My Credential') with migrate(cred, 'ssh'): cred.__dict__.update({ 'username': '******', 'password': '******', 'ssh_key_data': EXAMPLE_PRIVATE_KEY, 'ssh_key_unlock': 'keypass', 'become_method': 'sudo', 'become_username': '******', 'become_password': '******', }) assert cred.credential_type.name == 'Machine' assert cred.inputs['username'] == 'bob' assert cred.inputs['password'].startswith('$encrypted$') assert decrypt_field(cred, 'password') == 'secret' assert cred.inputs['ssh_key_data'].startswith('$encrypted$') assert decrypt_field(cred, 'ssh_key_data') == EXAMPLE_PRIVATE_KEY assert cred.inputs['ssh_key_unlock'].startswith('$encrypted$') assert decrypt_field(cred, 'ssh_key_unlock') == 'keypass' assert cred.inputs['become_method'] == 'sudo' assert cred.inputs['become_username'] == 'superuser' assert cred.inputs['become_password'].startswith('$encrypted$') assert decrypt_field(cred, 'become_password') == 'superpassword' assert Credential.objects.count() == 1
def test_credential_creation_validation_failure(inputs): type_ = CredentialType( kind='cloud', name='SomeCloud', managed=True, inputs={ 'fields': [{ 'id': 'username', 'label': 'Username for SomeCloud', 'type': 'string' }, { 'id': 'flag', 'label': 'Some Boolean Flag', 'type': 'boolean' }] }, ) cred = Credential(credential_type=type_, name="Bob's Credential", inputs=inputs) field = cred._meta.get_field('inputs') with pytest.raises(Exception) as e: field.validate(inputs, cred) assert e.type in (ValidationError, DRFValidationError)
def test_cloud_credential_type_mutability(patch, organization, admin, credentialtype_ssh, credentialtype_aws): cred = Credential( credential_type=credentialtype_aws, name='Best credential ever', organization=organization, inputs={ 'username': u'jim', 'password': u'pass' } ) cred.save() jt = JobTemplate() jt.save() jt.credentials.add(cred) def _change_credential_type(): return patch( reverse('api:credential_detail', kwargs={'pk': cred.pk}), { 'credential_type': credentialtype_ssh.pk, 'inputs': { 'username': u'jim', 'password': u'pass' } }, admin ) response = _change_credential_type() assert response.status_code == 400 expected = ['You cannot change the credential type of the credential, ' 'as it may break the functionality of the resources using it.'] assert response.data['credential_type'] == expected response = patch( reverse('api:credential_detail', kwargs={'pk': cred.pk}), {'name': 'Worst credential ever'}, admin ) assert response.status_code == 200 assert Credential.objects.get(pk=cred.pk).name == 'Worst credential ever' jt.delete() response = _change_credential_type() assert response.status_code == 200
def credential(): ssh_type = CredentialType.defaults['ssh']() return Credential( id=43, name='example-cred', credential_type=ssh_type, inputs={'username': '******', 'password': '******'} )
def test_invalid_credential_type_at_launch(get, post, job_template, admin, kind): cred_type = CredentialType.defaults[kind]() cred_type.save() cred = Credential(name='Some Cred', credential_type=cred_type, inputs={ 'username': '******', 'password': '******', }) cred.save() url = reverse('api:job_template_launch', kwargs={'pk': job_template.pk}) resp = post(url, {'credentials': [cred.pk]}, admin, expect=400) assert 'Cannot assign a Credential of kind `{}`'.format( kind) in resp.data.get('credentials', []) assert Job.objects.count() == 0
def test_clean_credential_with_custom_types(credentialtype_aws, credentialtype_net, job_template): aws = Credential(name='AWS Credential', credential_type=credentialtype_aws) aws.save() net = Credential(name='Net Credential', credential_type=credentialtype_net) net.save() job_template.extra_credentials.add(aws) job_template.extra_credentials.add(net) job_template.full_clean()
def test_invalid_clean_insights_credential(): cred_type = CredentialType.defaults['scm']() cred = Credential(credential_type=cred_type) inv = Inventory(insights_credential=cred) with pytest.raises(ValidationError) as e: inv.clean_insights_credential() assert json.dumps(str(e.value)) == json.dumps(str([u"Credential kind must be 'insights'."]))
def test_invalid_kind_clean_insights_credential(): cred_type = CredentialType.defaults['insights']() insights_cred = Credential(credential_type=cred_type) inv = Inventory(kind='smart', insights_credential=insights_cred) with pytest.raises(ValidationError) as e: inv.clean_insights_credential() assert json.dumps(str(e.value)) == json.dumps(str([u'Assignment not allowed for Smart Inventory']))
def test_vault_validation(organization, inputs, valid): cred_type = CredentialType.defaults['vault']() cred_type.save() cred = Credential(credential_type=cred_type, name="Best credential ever", inputs=inputs, organization=organization) cred.save() if valid: cred.full_clean() else: with pytest.raises(Exception) as e: cred.full_clean() assert e.type in (ValidationError, serializers.ValidationError)
def test_vault_only_migration(): cred = Credential(name='My Credential') with migrate(cred, 'ssh'): cred.__dict__.update({ 'vault_password': '******', }) assert cred.credential_type.name == 'Vault' assert cred.inputs['vault_password'].startswith('$encrypted$') assert decrypt_field(cred, 'vault_password') == 'vault' assert Credential.objects.count() == 1
def test_cred_unique_org_name_kind(organization_factory, credentialtype_ssh): objects = organization_factory("test") cred = Credential(name="test", credential_type=credentialtype_ssh, organization=objects.organization) cred.save() with pytest.raises(IntegrityError): cred = Credential(name="test", credential_type=credentialtype_ssh, organization=objects.organization) cred.save()