mirror of
https://github.com/ansible/awx.git
synced 2024-11-01 08:21:15 +03:00
disallow launching with other users prompts
This commit is contained in:
parent
7d36bd1fb2
commit
c3368bc4ff
@ -1488,7 +1488,7 @@ class JobAccess(BaseAccess):
|
|||||||
# Obtain prompts used to start original job
|
# Obtain prompts used to start original job
|
||||||
JobLaunchConfig = obj._meta.get_field('launch_config').related_model
|
JobLaunchConfig = obj._meta.get_field('launch_config').related_model
|
||||||
try:
|
try:
|
||||||
config = obj.launch_config
|
config = JobLaunchConfig.objects.prefetch_related('credentials').get(job=obj)
|
||||||
except JobLaunchConfig.DoesNotExist:
|
except JobLaunchConfig.DoesNotExist:
|
||||||
config = None
|
config = None
|
||||||
|
|
||||||
@ -1496,6 +1496,12 @@ class JobAccess(BaseAccess):
|
|||||||
if obj.job_template is not None:
|
if obj.job_template is not None:
|
||||||
if config is None:
|
if config is None:
|
||||||
prompts_access = False
|
prompts_access = False
|
||||||
|
elif config.prompts_dict() == {}:
|
||||||
|
prompts_access = True
|
||||||
|
elif obj.created_by_id != self.user.pk:
|
||||||
|
prompts_access = False
|
||||||
|
if self.save_messages:
|
||||||
|
self.messages['detail'] = _('Job was launched with prompts provided by another user.')
|
||||||
else:
|
else:
|
||||||
prompts_access = (
|
prompts_access = (
|
||||||
JobLaunchConfigAccess(self.user).can_add({'reference_obj': config}) and
|
JobLaunchConfigAccess(self.user).can_add({'reference_obj': config}) and
|
||||||
@ -1513,7 +1519,7 @@ class JobAccess(BaseAccess):
|
|||||||
|
|
||||||
# job can be relaunched if user could make an equivalent JT
|
# job can be relaunched if user could make an equivalent JT
|
||||||
ret = org_access and credential_access and project_access
|
ret = org_access and credential_access and project_access
|
||||||
if not ret and self.save_messages:
|
if not ret and self.save_messages and not self.messages:
|
||||||
if not obj.job_template:
|
if not obj.job_template:
|
||||||
pretext = _('Job has been orphaned from its job template.')
|
pretext = _('Job has been orphaned from its job template.')
|
||||||
elif config is None:
|
elif config is None:
|
||||||
|
@ -11,6 +11,8 @@ from awx.api.views import RelatedJobsPreventDeleteMixin, UnifiedJobDeletionMixin
|
|||||||
|
|
||||||
from awx.main.models import JobTemplate, User, Job
|
from awx.main.models import JobTemplate, User, Job
|
||||||
|
|
||||||
|
from crum import impersonate
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.django_db
|
@pytest.mark.django_db
|
||||||
def test_extra_credentials(get, organization_factory, job_template_factory, credential):
|
def test_extra_credentials(get, organization_factory, job_template_factory, credential):
|
||||||
@ -33,7 +35,8 @@ def test_job_relaunch_permission_denied_response(
|
|||||||
jt.credentials.add(machine_credential)
|
jt.credentials.add(machine_credential)
|
||||||
jt_user = User.objects.create(username='jobtemplateuser')
|
jt_user = User.objects.create(username='jobtemplateuser')
|
||||||
jt.execute_role.members.add(jt_user)
|
jt.execute_role.members.add(jt_user)
|
||||||
job = jt.create_unified_job()
|
with impersonate(jt_user):
|
||||||
|
job = jt.create_unified_job()
|
||||||
|
|
||||||
# User capability is shown for this
|
# User capability is shown for this
|
||||||
r = get(job.get_absolute_url(), jt_user, expect=200)
|
r = get(job.get_absolute_url(), jt_user, expect=200)
|
||||||
@ -46,6 +49,29 @@ def test_job_relaunch_permission_denied_response(
|
|||||||
assert 'do not have permission' in r.data['detail']
|
assert 'do not have permission' in r.data['detail']
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.django_db
|
||||||
|
def test_job_relaunch_permission_denied_response_other_user(get, post, inventory, project, alice, bob):
|
||||||
|
'''
|
||||||
|
Asserts custom permission denied message corresponding to
|
||||||
|
awx/main/tests/functional/test_rbac_job.py::TestJobRelaunchAccess::test_other_user_prompts
|
||||||
|
'''
|
||||||
|
jt = JobTemplate.objects.create(
|
||||||
|
name='testjt', inventory=inventory, project=project,
|
||||||
|
ask_credential_on_launch=True,
|
||||||
|
ask_variables_on_launch=True)
|
||||||
|
jt.execute_role.members.add(alice, bob)
|
||||||
|
with impersonate(bob):
|
||||||
|
job = jt.create_unified_job(extra_vars={'job_var': 'foo2'})
|
||||||
|
|
||||||
|
# User capability is shown for this
|
||||||
|
r = get(job.get_absolute_url(), alice, expect=200)
|
||||||
|
assert r.data['summary_fields']['user_capabilities']['start']
|
||||||
|
|
||||||
|
# Job has prompted data, launch denied w/ message
|
||||||
|
r = post(reverse('api:job_relaunch', kwargs={'pk':job.pk}), {}, alice, expect=403)
|
||||||
|
assert 'Job was launched with prompts provided by another user' in r.data['detail']
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.django_db
|
@pytest.mark.django_db
|
||||||
def test_job_relaunch_without_creds(post, inventory, project, admin_user):
|
def test_job_relaunch_without_creds(post, inventory, project, admin_user):
|
||||||
jt = JobTemplate.objects.create(
|
jt = JobTemplate.objects.create(
|
||||||
|
@ -19,6 +19,8 @@ from awx.main.models import (
|
|||||||
Credential
|
Credential
|
||||||
)
|
)
|
||||||
|
|
||||||
|
from crum import impersonate
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def normal_job(deploy_jobtemplate):
|
def normal_job(deploy_jobtemplate):
|
||||||
@ -151,11 +153,14 @@ class TestJobRelaunchAccess:
|
|||||||
ask_inventory_on_launch=True,
|
ask_inventory_on_launch=True,
|
||||||
ask_credential_on_launch=True
|
ask_credential_on_launch=True
|
||||||
)
|
)
|
||||||
job_with_links = Job.objects.create(name='existing-job', inventory=inventory, job_template=job_template)
|
u = user('user1', False)
|
||||||
|
job_with_links = Job.objects.create(
|
||||||
|
name='existing-job', inventory=inventory, job_template=job_template,
|
||||||
|
created_by=u
|
||||||
|
)
|
||||||
job_with_links.credentials.add(machine_credential)
|
job_with_links.credentials.add(machine_credential)
|
||||||
JobLaunchConfig.objects.create(job=job_with_links, inventory=inventory)
|
JobLaunchConfig.objects.create(job=job_with_links, inventory=inventory)
|
||||||
job_with_links.launch_config.credentials.add(machine_credential) # credential was prompted
|
job_with_links.launch_config.credentials.add(machine_credential) # credential was prompted
|
||||||
u = user('user1', False)
|
|
||||||
job_template.execute_role.members.add(u)
|
job_template.execute_role.members.add(u)
|
||||||
if inv_access:
|
if inv_access:
|
||||||
job_with_links.inventory.use_role.members.add(u)
|
job_with_links.inventory.use_role.members.add(u)
|
||||||
@ -225,15 +230,30 @@ class TestJobRelaunchAccess:
|
|||||||
|
|
||||||
@pytest.mark.job_runtime_vars
|
@pytest.mark.job_runtime_vars
|
||||||
def test_callback_relaunchable_by_user(self, job_template, rando):
|
def test_callback_relaunchable_by_user(self, job_template, rando):
|
||||||
job = job_template.create_unified_job(
|
with impersonate(rando):
|
||||||
_eager_fields={'launch_type': 'callback'},
|
job = job_template.create_unified_job(
|
||||||
limit='host2'
|
_eager_fields={'launch_type': 'callback'},
|
||||||
)
|
limit='host2'
|
||||||
|
)
|
||||||
assert 'limit' in job.launch_config.prompts_dict() # sanity assertion
|
assert 'limit' in job.launch_config.prompts_dict() # sanity assertion
|
||||||
job_template.execute_role.members.add(rando)
|
job_template.execute_role.members.add(rando)
|
||||||
can_access, messages = rando.can_access_with_errors(Job, 'start', job, validate_license=False)
|
can_access, messages = rando.can_access_with_errors(Job, 'start', job, validate_license=False)
|
||||||
assert can_access, messages
|
assert can_access, messages
|
||||||
|
|
||||||
|
def test_other_user_prompts(self, inventory, project, alice, bob):
|
||||||
|
jt = JobTemplate.objects.create(
|
||||||
|
name='testjt', inventory=inventory, project=project,
|
||||||
|
ask_credential_on_launch=True,
|
||||||
|
ask_variables_on_launch=True)
|
||||||
|
jt.execute_role.members.add(alice, bob)
|
||||||
|
|
||||||
|
with impersonate(bob):
|
||||||
|
job = jt.create_unified_job(extra_vars={'job_var': 'foo2'})
|
||||||
|
|
||||||
|
assert 'job_var' in job.launch_config.extra_data
|
||||||
|
assert bob.can_access(Job, 'start', job, validate_license=False)
|
||||||
|
assert not alice.can_access(Job, 'start', job, validate_license=False)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.django_db
|
@pytest.mark.django_db
|
||||||
class TestJobAndUpdateCancels:
|
class TestJobAndUpdateCancels:
|
||||||
|
@ -87,6 +87,7 @@ class TestJobRelaunchAccess:
|
|||||||
for cred in job_with_prompts.credentials.all():
|
for cred in job_with_prompts.credentials.all():
|
||||||
cred.use_role.members.add(rando)
|
cred.use_role.members.add(rando)
|
||||||
job_with_prompts.inventory.use_role.members.add(rando)
|
job_with_prompts.inventory.use_role.members.add(rando)
|
||||||
|
job_with_prompts.created_by = rando
|
||||||
assert rando.can_access(Job, 'start', job_with_prompts)
|
assert rando.can_access(Job, 'start', job_with_prompts)
|
||||||
|
|
||||||
def test_no_relaunch_after_limit_change(self, inventory, machine_credential, rando):
|
def test_no_relaunch_after_limit_change(self, inventory, machine_credential, rando):
|
||||||
|
Loading…
Reference in New Issue
Block a user