mirror of
https://github.com/ansible/awx.git
synced 2026-01-13 11:00:03 -03:30
AC-537 Add remaining API/field validation for credentials and other objects using credentials.
AC-630 Added validation of cloud_credential kind on job template and job, set environment variables based on cloud credential. AC-610 Require a credential for a cloud inventory source. AC-457 Do not set password when using hg over ssh.
This commit is contained in:
parent
735da6bff6
commit
11d2f76546
@ -333,17 +333,14 @@ class ProjectSerializer(BaseSerializer):
|
||||
args=(obj.last_update.pk,))
|
||||
return res
|
||||
|
||||
def _get_scm_type(self, attrs, source=None):
|
||||
if self.object:
|
||||
return attrs.get(source or 'scm_type', self.object.scm_type) or u''
|
||||
else:
|
||||
return attrs.get(source or 'scm_type', u'') or u''
|
||||
|
||||
def validate_local_path(self, attrs, source):
|
||||
# Don't allow assigning a local_path used by another project.
|
||||
# Don't allow assigning a local_path when scm_type is set.
|
||||
valid_local_paths = Project.get_local_path_choices()
|
||||
scm_type = self._get_scm_type(attrs)
|
||||
if self.object:
|
||||
scm_type = attrs.get('scm_type', self.object.scm_type) or u''
|
||||
else:
|
||||
scm_type = attrs.get('scm_type', u'') or u''
|
||||
if self.object and not scm_type:
|
||||
valid_local_paths.append(self.object.local_path)
|
||||
if scm_type:
|
||||
@ -352,71 +349,6 @@ class ProjectSerializer(BaseSerializer):
|
||||
raise serializers.ValidationError('Invalid path choice')
|
||||
return attrs
|
||||
|
||||
def validate_scm_type(self, attrs, source):
|
||||
scm_type = self._get_scm_type(attrs, source)
|
||||
attrs[source] = scm_type
|
||||
return attrs
|
||||
|
||||
def validate_scm_url(self, attrs, source):
|
||||
scm_type = self._get_scm_type(attrs)
|
||||
scm_url = unicode(attrs.get(source, None) or '')
|
||||
if not scm_type:
|
||||
return attrs
|
||||
try:
|
||||
scm_url = update_scm_url(scm_type, scm_url)
|
||||
except ValueError, e:
|
||||
raise serializers.ValidationError((e.args or ('Invalid SCM URL',))[0])
|
||||
scm_url_parts = urlparse.urlsplit(scm_url)
|
||||
if scm_type and not any(scm_url_parts):
|
||||
raise serializers.ValidationError('SCM URL is required')
|
||||
return attrs
|
||||
|
||||
#def validate_scm_username(self, attrs, source):
|
||||
# if self.object:
|
||||
# scm_type = attrs.get('scm_type', self.object.scm_type) or ''
|
||||
# scm_url = unicode(attrs.get('scm_url', self.object.scm_url) or '')
|
||||
# scm_username = attrs.get('scm_username', self.object.scm_username) or ''
|
||||
# else:
|
||||
# scm_type = attrs.get('scm_type', '') or ''
|
||||
# scm_url = unicode(attrs.get('scm_url', '') or '')
|
||||
# scm_username = attrs.get('scm_username', '') or ''
|
||||
# if not scm_type:
|
||||
# return attrs
|
||||
# try:
|
||||
# if scm_url and scm_username:
|
||||
# update_scm_url(scm_type, scm_url, scm_username)
|
||||
# except ValueError, e:
|
||||
# raise serializers.ValidationError((e.args or ('Invalid SCM username',))[0])
|
||||
# return attrs
|
||||
|
||||
#def validate_scm_password(self, attrs, source):
|
||||
# if self.object:
|
||||
# scm_type = attrs.get('scm_type', self.object.scm_type) or ''
|
||||
# scm_url = unicode(attrs.get('scm_url', self.object.scm_url) or '')
|
||||
# scm_username = attrs.get('scm_username', self.object.scm_username) or ''
|
||||
# scm_password = attrs.get('scm_password', self.object.scm_password) or ''
|
||||
# else:
|
||||
# scm_type = attrs.get('scm_type', '') or ''
|
||||
# scm_url = unicode(attrs.get('scm_url', '') or '')
|
||||
# scm_username = attrs.get('scm_username', '') or ''
|
||||
# scm_password = attrs.get('scm_password', '') or ''
|
||||
# if not scm_type:
|
||||
# return attrs
|
||||
# try:
|
||||
# try:
|
||||
# if scm_url and scm_username:
|
||||
# update_scm_url(scm_type, scm_url, scm_username)
|
||||
# except ValueError:
|
||||
# pass
|
||||
# else:
|
||||
# if scm_url and scm_username and scm_password:
|
||||
# update_scm_url(scm_type, scm_url, scm_username, '**')
|
||||
# except ValueError, e:
|
||||
# raise serializers.ValidationError((e.args or ('Invalid SCM password',))[0])
|
||||
# return attrs
|
||||
|
||||
# FIXME: Validate combination of SCM URL and credential!
|
||||
|
||||
|
||||
class ProjectPlaybooksSerializer(ProjectSerializer):
|
||||
|
||||
|
||||
@ -128,8 +128,7 @@ class BaseModel(models.Model):
|
||||
continue
|
||||
if hasattr(self, 'clean_%s' % f.name):
|
||||
try:
|
||||
setattr(self, f.attname,
|
||||
getattr(self, 'clean_%s' % f.name)())
|
||||
setattr(self, f.name, getattr(self, 'clean_%s' % f.name)())
|
||||
except ValidationError, e:
|
||||
errors[f.name] = e.messages
|
||||
if errors:
|
||||
|
||||
@ -561,6 +561,21 @@ class InventorySource(PrimordialModel):
|
||||
editable=False,
|
||||
)
|
||||
|
||||
def clean_credential(self):
|
||||
if not self.source:
|
||||
return None
|
||||
cred = self.credential
|
||||
if cred:
|
||||
if self.source == 'ec2' and cred.kind != 'aws':
|
||||
raise ValidationError('Credential kind must be "aws" for an '
|
||||
'"ec2" source')
|
||||
if self.source == 'rax' and cred.kind != 'rax':
|
||||
raise ValidationError('Credential kind must be "rax" for a '
|
||||
'"rax" source')
|
||||
elif self.source in ('ec2', 'rax'):
|
||||
raise ValidationError('Credential is required for a cloud source')
|
||||
return cred
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
new_instance = not bool(self.pk)
|
||||
# If update_fields has been specified, add our field names to it,
|
||||
|
||||
@ -106,6 +106,19 @@ class JobTemplate(CommonModel):
|
||||
default='',
|
||||
)
|
||||
|
||||
def clean_credential(self):
|
||||
cred = self.credential
|
||||
if cred and cred.kind != 'ssh':
|
||||
raise ValidationError('Credential kind must be "ssh"')
|
||||
return cred
|
||||
|
||||
def clean_cloud_credential(self):
|
||||
cred = self.cloud_credential
|
||||
if cred and cred.kind not in ('aws', 'rax'):
|
||||
raise ValidationError('Cloud credential kind must be "aws" or '
|
||||
'"rax"')
|
||||
return cred
|
||||
|
||||
def create_job(self, **kwargs):
|
||||
'''
|
||||
Create a new job based on this template.
|
||||
@ -238,6 +251,19 @@ class Job(CommonTask):
|
||||
through='JobHostSummary',
|
||||
)
|
||||
|
||||
def clean_credential(self):
|
||||
cred = self.credential
|
||||
if cred and cred.kind != 'ssh':
|
||||
raise ValidationError('Credential kind must be "ssh"')
|
||||
return cred
|
||||
|
||||
def clean_cloud_credential(self):
|
||||
cred = self.cloud_credential
|
||||
if cred and cred.kind not in ('aws', 'rax'):
|
||||
raise ValidationError('Cloud credential kind must be "aws" or '
|
||||
'"rax"')
|
||||
return cred
|
||||
|
||||
def get_absolute_url(self):
|
||||
return reverse('api:job_detail', args=(self.pk,))
|
||||
|
||||
|
||||
@ -255,6 +255,22 @@ class Credential(CommonModelNameNotUnique):
|
||||
def get_absolute_url(self):
|
||||
return reverse('api:credential_detail', args=(self.pk,))
|
||||
|
||||
def clean_username(self):
|
||||
username = self.username or ''
|
||||
if not username and self.kind == 'aws':
|
||||
raise ValidationError('Access key required for "aws" credential')
|
||||
if not username and self.kind == 'rax':
|
||||
raise ValidationError('Username required for "rax" credential')
|
||||
return username
|
||||
|
||||
def clean_password(self):
|
||||
password = self.password or ''
|
||||
if not password and self.kind == 'aws':
|
||||
raise ValidationError('Secret key required for "aws" credential')
|
||||
if not password and self.kind == 'rax':
|
||||
raise ValidationError('API key required for "rax" credential')
|
||||
return password
|
||||
|
||||
def clean_ssh_key_unlock(self):
|
||||
if self.pk:
|
||||
ssh_key_data = decrypt_field(self, 'ssh_key_data')
|
||||
|
||||
@ -10,6 +10,7 @@ import logging
|
||||
import os
|
||||
import re
|
||||
import shlex
|
||||
import urlparse
|
||||
import uuid
|
||||
|
||||
# PyYAML
|
||||
@ -28,6 +29,7 @@ from django.utils.timezone import now, make_aware, get_default_timezone
|
||||
# AWX
|
||||
from awx.lib.compat import slugify
|
||||
from awx.main.models.base import *
|
||||
from awx.main.utils import update_scm_url
|
||||
|
||||
__all__ = ['Project', 'ProjectUpdate']
|
||||
|
||||
@ -154,6 +156,49 @@ class Project(CommonModel):
|
||||
null=True, # FIXME: Remove
|
||||
)
|
||||
|
||||
def clean_scm_type(self):
|
||||
return self.scm_type or ''
|
||||
|
||||
def clean_scm_url(self):
|
||||
scm_url = unicode(self.scm_url or '')
|
||||
if not self.scm_type:
|
||||
return ''
|
||||
try:
|
||||
scm_url = update_scm_url(self.scm_type, scm_url,
|
||||
check_special_cases=False)
|
||||
except ValueError, e:
|
||||
raise ValidationError((e.args or ('Invalid SCM URL',))[0])
|
||||
scm_url_parts = urlparse.urlsplit(scm_url)
|
||||
if self.scm_type and not any(scm_url_parts):
|
||||
raise ValidationError('SCM URL is required')
|
||||
return unicode(self.scm_url or '')
|
||||
|
||||
def clean_credential(self):
|
||||
if not self.scm_type:
|
||||
return None
|
||||
cred = self.credential
|
||||
if cred:
|
||||
if cred.kind != 'scm':
|
||||
raise ValidationError('Credential kind must be "scm"')
|
||||
try:
|
||||
scm_url = update_scm_url(self.scm_type, self.scm_url,
|
||||
check_special_cases=False)
|
||||
scm_url_parts = urlparse.urlsplit(scm_url)
|
||||
# Prefer the username/password in the URL, if provided.
|
||||
scm_username = scm_url_parts.username or cred.username or ''
|
||||
if scm_url_parts.password or cred.password:
|
||||
scm_password = '********'
|
||||
else:
|
||||
scm_password = ''
|
||||
try:
|
||||
update_scm_url(self.scm_type, self.scm_url, scm_username,
|
||||
scm_password)
|
||||
except ValueError, e:
|
||||
raise ValidationError((e.args or ('Invalid credential',))[0])
|
||||
except ValueError:
|
||||
pass
|
||||
return cred
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
new_instance = not bool(self.pk)
|
||||
# If update_fields has been specified, add our field names to it,
|
||||
|
||||
@ -337,6 +337,16 @@ class RunJob(BaseTask):
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
# Set environment variables for cloud credentials.
|
||||
cloud_cred = job.cloud_credential
|
||||
if cloud_cred and cloud_cred.kind == 'aws':
|
||||
env['AWS_ACCESS_KEY'] = cloud_cred.username
|
||||
env['AWS_SECRET_KEY'] = decrypt_field(cloud_cred, 'password')
|
||||
# FIXME: Add EC2_URL, maybe EC2_REGION!
|
||||
elif cloud_cred and cloud_cred.kind == 'rax':
|
||||
env['RAX_USERNAME'] = cloud_cred.username
|
||||
env['RAX_API_KEY'] = decrypt_field(cloud_cred, 'password')
|
||||
|
||||
return env
|
||||
|
||||
def build_args(self, job, **kwargs):
|
||||
@ -523,9 +533,8 @@ class RunProjectUpdate(BaseTask):
|
||||
**kwargs)
|
||||
project = project_update.project
|
||||
if project.credential:
|
||||
value = decrypt_field(project.credential, 'ssh_key_unlock')
|
||||
if value not in ('', 'ASK'):
|
||||
passwords['scm_key_unlock'] = value
|
||||
passwords['scm_key_unlock'] = decrypt_field(project.credential,
|
||||
'ssh_key_unlock')
|
||||
passwords['scm_username'] = project.credential.username
|
||||
passwords['scm_password'] = decrypt_field(project.credential,
|
||||
'password')
|
||||
@ -549,36 +558,26 @@ class RunProjectUpdate(BaseTask):
|
||||
extra_vars = {}
|
||||
project = project_update.project
|
||||
scm_type = project.scm_type
|
||||
scm_url = update_scm_url(scm_type, project.scm_url)
|
||||
scm_url = update_scm_url(scm_type, project.scm_url,
|
||||
check_special_cases=False)
|
||||
scm_url_parts = urlparse.urlsplit(scm_url)
|
||||
scm_username = kwargs.get('passwords', {}).get('scm_username', '')
|
||||
scm_username = scm_username or scm_url_parts.username or ''
|
||||
scm_password = kwargs.get('passwords', {}).get('scm_password', '')
|
||||
scm_password = scm_password or scm_url_parts.password or ''
|
||||
if scm_username and scm_password not in ('ASK', ''):
|
||||
# Prefer the username/password in the URL, if provided.
|
||||
scm_username = scm_url_parts.username or scm_username or ''
|
||||
scm_password = scm_url_parts.password or scm_password or ''
|
||||
if scm_username:
|
||||
if scm_type == 'svn':
|
||||
# FIXME: Need to somehow escape single/double quotes in username/password
|
||||
extra_vars['scm_username'] = scm_username
|
||||
extra_vars['scm_password'] = scm_password
|
||||
if scm_url_parts.scheme == 'svn+ssh':
|
||||
scm_url = update_scm_url(scm_type, scm_url, scm_username, False)
|
||||
else:
|
||||
scm_url = update_scm_url(scm_type, scm_url, False, False)
|
||||
scm_password = False
|
||||
if scm_url_parts.scheme != 'svn+ssh':
|
||||
scm_username = False
|
||||
elif scm_url_parts.scheme == 'ssh':
|
||||
scm_url = update_scm_url(scm_type, scm_url, scm_username, False)
|
||||
else:
|
||||
scm_url = update_scm_url(scm_type, scm_url, scm_username,
|
||||
scm_password)
|
||||
elif scm_username:
|
||||
if scm_type == 'svn':
|
||||
extra_vars['scm_username'] = scm_username
|
||||
extra_vars['scm_password'] = ''
|
||||
if scm_url_parts.scheme == 'svn+ssh':
|
||||
scm_url = update_scm_url(scm_type, scm_url, scm_username, False)
|
||||
else:
|
||||
scm_url = update_scm_url(scm_type, scm_url, False, False)
|
||||
else:
|
||||
scm_url = update_scm_url(scm_type, scm_url, scm_username, False)
|
||||
scm_password = False
|
||||
scm_url = update_scm_url(scm_type, scm_url, scm_username,
|
||||
scm_password)
|
||||
return scm_url, extra_vars
|
||||
|
||||
def build_args(self, project_update, **kwargs):
|
||||
@ -604,7 +603,6 @@ class RunProjectUpdate(BaseTask):
|
||||
'scm_clean': project.scm_clean,
|
||||
'scm_delete_on_update': scm_delete_on_update,
|
||||
})
|
||||
#print extra_vars
|
||||
args.extend(['-e', json.dumps(extra_vars)])
|
||||
args.append('project_update.yml')
|
||||
|
||||
|
||||
@ -512,9 +512,11 @@ class ProjectsTest(BaseTest):
|
||||
|
||||
# Test with encrypted ssh key and no unlock password.
|
||||
with self.current_user(self.super_django_user):
|
||||
data = dict(name='zyx', user=self.super_django_user.pk, kind='ssh',
|
||||
data = dict(name='wxy', user=self.super_django_user.pk, kind='ssh',
|
||||
ssh_key_data=TEST_SSH_KEY_DATA_LOCKED)
|
||||
self.post(url, data, expect=400)
|
||||
data['ssh_key_unlock'] = TEST_SSH_KEY_DATA_UNLOCK
|
||||
self.post(url, data, expect=201)
|
||||
|
||||
# FIXME: Check list as other users.
|
||||
|
||||
@ -784,18 +786,19 @@ class ProjectUpdatesTest(BaseTransactionTest):
|
||||
('hg', 'https://user:pass@host.xz/path/to/repo/#rev', None, 'https://testuser:pass@host.xz/path/to/repo/#rev', 'https://testuser:testpass@host.xz/path/to/repo/#rev'),
|
||||
('hg', 'https://user:pass@host.xz:8443/path/to/repo#rev', None, 'https://testuser:pass@host.xz:8443/path/to/repo#rev', 'https://testuser:testpass@host.xz:8443/path/to/repo#rev'),
|
||||
# - ssh://[user@]host[:port]/[path][#revision]
|
||||
('hg', 'ssh://host.xz/path/to/repo/', None, 'ssh://testuser@host.xz/path/to/repo/', ValueError),
|
||||
('hg', 'ssh://host.xz:1022/path/to/repo', None, 'ssh://testuser@host.xz:1022/path/to/repo', ValueError),
|
||||
('hg', 'ssh://user@host.xz/path/to/repo/', None, 'ssh://testuser@host.xz/path/to/repo/', ValueError),
|
||||
('hg', 'ssh://user@host.xz:1022/path/to/repo', None, 'ssh://testuser@host.xz:1022/path/to/repo', ValueError),
|
||||
('hg', 'ssh://user:pass@host.xz/path/to/repo/', ValueError, ValueError, ValueError),
|
||||
('hg', 'ssh://user:pass@host.xz:1022/path/to/repo', ValueError, ValueError, ValueError),
|
||||
('hg', 'ssh://host.xz/path/to/repo/#rev', None, 'ssh://testuser@host.xz/path/to/repo/#rev', ValueError),
|
||||
('hg', 'ssh://host.xz:1022/path/to/repo#rev', None, 'ssh://testuser@host.xz:1022/path/to/repo#rev', ValueError),
|
||||
('hg', 'ssh://user@host.xz/path/to/repo/#rev', None, 'ssh://testuser@host.xz/path/to/repo/#rev', ValueError),
|
||||
('hg', 'ssh://user@host.xz:1022/path/to/repo#rev', None, 'ssh://testuser@host.xz:1022/path/to/repo#rev', ValueError),
|
||||
('hg', 'ssh://user:pass@host.xz/path/to/repo/#rev', ValueError, ValueError, ValueError),
|
||||
('hg', 'ssh://user:pass@host.xz:1022/path/to/repo#rev', ValueError, ValueError, ValueError),
|
||||
# Password is always stripped out for hg when using SSH.
|
||||
('hg', 'ssh://host.xz/path/to/repo/', None, 'ssh://testuser@host.xz/path/to/repo/', 'ssh://testuser@host.xz/path/to/repo/'),
|
||||
('hg', 'ssh://host.xz:1022/path/to/repo', None, 'ssh://testuser@host.xz:1022/path/to/repo', 'ssh://testuser@host.xz:1022/path/to/repo'),
|
||||
('hg', 'ssh://user@host.xz/path/to/repo/', None, 'ssh://testuser@host.xz/path/to/repo/', 'ssh://testuser@host.xz/path/to/repo/'),
|
||||
('hg', 'ssh://user@host.xz:1022/path/to/repo', None, 'ssh://testuser@host.xz:1022/path/to/repo', 'ssh://testuser@host.xz:1022/path/to/repo'),
|
||||
('hg', 'ssh://user:pass@host.xz/path/to/repo/', 'ssh://user@host.xz/path/to/repo/', 'ssh://testuser@host.xz/path/to/repo/', 'ssh://testuser@host.xz/path/to/repo/'),
|
||||
('hg', 'ssh://user:pass@host.xz:1022/path/to/repo', 'ssh://user@host.xz:1022/path/to/repo', 'ssh://testuser@host.xz:1022/path/to/repo', 'ssh://testuser@host.xz:1022/path/to/repo'),
|
||||
('hg', 'ssh://host.xz/path/to/repo/#rev', None, 'ssh://testuser@host.xz/path/to/repo/#rev', 'ssh://testuser@host.xz/path/to/repo/#rev'),
|
||||
('hg', 'ssh://host.xz:1022/path/to/repo#rev', None, 'ssh://testuser@host.xz:1022/path/to/repo#rev', 'ssh://testuser@host.xz:1022/path/to/repo#rev'),
|
||||
('hg', 'ssh://user@host.xz/path/to/repo/#rev', None, 'ssh://testuser@host.xz/path/to/repo/#rev', 'ssh://testuser@host.xz/path/to/repo/#rev'),
|
||||
('hg', 'ssh://user@host.xz:1022/path/to/repo#rev', None, 'ssh://testuser@host.xz:1022/path/to/repo#rev', 'ssh://testuser@host.xz:1022/path/to/repo#rev'),
|
||||
('hg', 'ssh://user:pass@host.xz/path/to/repo/#rev', 'ssh://user@host.xz/path/to/repo/#rev', 'ssh://testuser@host.xz/path/to/repo/#rev', 'ssh://testuser@host.xz/path/to/repo/#rev'),
|
||||
('hg', 'ssh://user:pass@host.xz:1022/path/to/repo#rev', 'ssh://user@host.xz:1022/path/to/repo#rev', 'ssh://testuser@host.xz:1022/path/to/repo#rev', 'ssh://testuser@host.xz:1022/path/to/repo#rev'),
|
||||
# Special case for bitbucket URLs:
|
||||
('hg', 'ssh://hg@bitbucket.org/foo/bar', None, ValueError, ValueError),
|
||||
('hg', 'ssh://hg@altssh.bitbucket.org:443/foo/bar', None, ValueError, ValueError),
|
||||
@ -842,7 +845,7 @@ class ProjectUpdatesTest(BaseTransactionTest):
|
||||
(isinstance(e, type) and issubclass(e, Exception)))
|
||||
for url_opts in urls_to_test:
|
||||
scm_type, url, new_url, new_url_u, new_url_up = url_opts
|
||||
#print url
|
||||
#print scm_type, url
|
||||
new_url = new_url or url
|
||||
new_url_u = new_url_u or url
|
||||
new_url_up = new_url_up or url
|
||||
@ -1078,6 +1081,78 @@ class ProjectUpdatesTest(BaseTransactionTest):
|
||||
else:
|
||||
self.check_project_update(project, should_fail=should_still_fail)
|
||||
|
||||
def test_create_project_with_scm(self):
|
||||
scm_url = getattr(settings, 'TEST_GIT_PUBLIC_HTTPS',
|
||||
'https://github.com/ansible/ansible.github.com.git')
|
||||
if not all([scm_url]):
|
||||
self.skipTest('no public git repo defined for https!')
|
||||
projects_url = reverse('api:project_list')
|
||||
credentials_url = reverse('api:credential_list')
|
||||
# Test basic project creation without a credential.
|
||||
project_data = {
|
||||
'name': 'my public git project over https',
|
||||
'scm_type': 'git',
|
||||
'scm_url': scm_url,
|
||||
}
|
||||
with self.current_user(self.super_django_user):
|
||||
self.post(projects_url, project_data, expect=201)
|
||||
# Test with an invalid URL.
|
||||
project_data = {
|
||||
'name': 'my local git project',
|
||||
'scm_type': 'git',
|
||||
'scm_url': 'file:///path/to/repo.git',
|
||||
}
|
||||
with self.current_user(self.super_django_user):
|
||||
self.post(projects_url, project_data, expect=400)
|
||||
# Test creation with a credential.
|
||||
credential_data = {
|
||||
'name': 'my scm credential',
|
||||
'kind': 'scm',
|
||||
'user': self.super_django_user.pk,
|
||||
'username': 'testuser',
|
||||
'password': 'testpass',
|
||||
}
|
||||
with self.current_user(self.super_django_user):
|
||||
response = self.post(credentials_url, credential_data, expect=201)
|
||||
credential_id = response['id']
|
||||
project_data = {
|
||||
'name': 'my git project over https with credential',
|
||||
'scm_type': 'git',
|
||||
'scm_url': scm_url,
|
||||
'credential': credential_id,
|
||||
}
|
||||
with self.current_user(self.super_django_user):
|
||||
self.post(projects_url, project_data, expect=201)
|
||||
# Test creation with an invalid credential type.
|
||||
ssh_credential_data = {
|
||||
'name': 'my ssh credential',
|
||||
'kind': 'ssh',
|
||||
'user': self.super_django_user.pk,
|
||||
'username': 'testuser',
|
||||
'password': 'testpass',
|
||||
}
|
||||
with self.current_user(self.super_django_user):
|
||||
response = self.post(credentials_url, ssh_credential_data,
|
||||
expect=201)
|
||||
ssh_credential_id = response['id']
|
||||
project_data = {
|
||||
'name': 'my git project with invalid credential type',
|
||||
'scm_type': 'git',
|
||||
'scm_url': scm_url,
|
||||
'credential': ssh_credential_id,
|
||||
}
|
||||
with self.current_user(self.super_django_user):
|
||||
self.post(projects_url, project_data, expect=400)
|
||||
# Test special case for github/bitbucket URLs.
|
||||
project_data = {
|
||||
'name': 'my github project over ssh',
|
||||
'scm_type': 'git',
|
||||
'scm_url': 'ssh://git@github.com/ansible/ansible.github.com.git',
|
||||
'credential': credential_id,
|
||||
}
|
||||
with self.current_user(self.super_django_user):
|
||||
self.post(projects_url, project_data, expect=201)
|
||||
|
||||
def test_public_git_project_over_https(self):
|
||||
scm_url = getattr(settings, 'TEST_GIT_PUBLIC_HTTPS',
|
||||
'https://github.com/ansible/ansible.github.com.git')
|
||||
@ -1142,8 +1217,8 @@ class ProjectUpdatesTest(BaseTransactionTest):
|
||||
scm_password=scm_password,
|
||||
)
|
||||
should_error = bool('github.com' in scm_url and scm_username != 'git')
|
||||
self.check_project_update(project2, should_fail=True,
|
||||
should_error=should_error)
|
||||
self.check_project_update(project2, should_fail=None)#,
|
||||
#should_error=should_error)
|
||||
|
||||
def create_local_git_repo(self):
|
||||
repo_dir = tempfile.mkdtemp()
|
||||
|
||||
@ -128,7 +128,8 @@ def decrypt_field(instance, field_name):
|
||||
value = cipher.decrypt(encrypted)
|
||||
return value.rstrip('\x00')
|
||||
|
||||
def update_scm_url(scm_type, url, username=True, password=True):
|
||||
def update_scm_url(scm_type, url, username=True, password=True,
|
||||
check_special_cases=True):
|
||||
'''
|
||||
Update the given SCM URL to add/replace/remove the username/password. When
|
||||
username/password is True, preserve existing username/password, when
|
||||
@ -198,16 +199,19 @@ def update_scm_url(scm_type, url, username=True, password=True):
|
||||
netloc_password = ''
|
||||
|
||||
# Special handling for github/bitbucket SSH URLs.
|
||||
special_git_hosts = ('github.com', 'bitbucket.org', 'altssh.bitbucket.org')
|
||||
if scm_type == 'git' and parts.scheme == 'ssh' and parts.hostname in special_git_hosts and netloc_username != 'git':
|
||||
raise ValueError('Username must be "git" for SSH access to %s.' % parts.hostname)
|
||||
if scm_type == 'git' and parts.scheme == 'ssh' and parts.hostname in special_git_hosts and netloc_password:
|
||||
raise ValueError('Password not allowed for SSH access to %s.' % parts.hostname)
|
||||
special_hg_hosts = ('bitbucket.org', 'altssh.bitbucket.org')
|
||||
if scm_type == 'hg' and parts.scheme == 'ssh' and parts.hostname in special_hg_hosts and netloc_username != 'hg':
|
||||
raise ValueError('Username must be "hg" for SSH access to %s.' % parts.hostname)
|
||||
if scm_type == 'hg' and parts.scheme == 'ssh' and netloc_password:
|
||||
raise ValueError('Password not supported for SSH with Mercurial.')
|
||||
if check_special_cases:
|
||||
special_git_hosts = ('github.com', 'bitbucket.org', 'altssh.bitbucket.org')
|
||||
if scm_type == 'git' and parts.scheme == 'ssh' and parts.hostname in special_git_hosts and netloc_username != 'git':
|
||||
raise ValueError('Username must be "git" for SSH access to %s.' % parts.hostname)
|
||||
if scm_type == 'git' and parts.scheme == 'ssh' and parts.hostname in special_git_hosts and netloc_password:
|
||||
#raise ValueError('Password not allowed for SSH access to %s.' % parts.hostname)
|
||||
netloc_password = ''
|
||||
special_hg_hosts = ('bitbucket.org', 'altssh.bitbucket.org')
|
||||
if scm_type == 'hg' and parts.scheme == 'ssh' and parts.hostname in special_hg_hosts and netloc_username != 'hg':
|
||||
raise ValueError('Username must be "hg" for SSH access to %s.' % parts.hostname)
|
||||
if scm_type == 'hg' and parts.scheme == 'ssh' and netloc_password:
|
||||
#raise ValueError('Password not supported for SSH with Mercurial.')
|
||||
netloc_password = ''
|
||||
|
||||
if netloc_username and parts.scheme != 'file':
|
||||
netloc = u':'.join(filter(None, [netloc_username, netloc_password]))
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user