mirror of
https://github.com/ansible/awx.git
synced 2026-05-20 07:17:40 -02:30
fixed a few issues related to approval role RBAC for normal users
This commit is contained in:
@@ -17,7 +17,7 @@ logger = logging.getLogger('awx.api.permissions')
|
|||||||
|
|
||||||
__all__ = ['ModelAccessPermission', 'JobTemplateCallbackPermission', 'VariableDataPermission',
|
__all__ = ['ModelAccessPermission', 'JobTemplateCallbackPermission', 'VariableDataPermission',
|
||||||
'TaskPermission', 'ProjectUpdatePermission', 'InventoryInventorySourcesUpdatePermission',
|
'TaskPermission', 'ProjectUpdatePermission', 'InventoryInventorySourcesUpdatePermission',
|
||||||
'UserPermission', 'IsSuperUser', 'InstanceGroupTowerPermission',]
|
'UserPermission', 'IsSuperUser', 'InstanceGroupTowerPermission', 'WorkflowApprovalPermission']
|
||||||
|
|
||||||
|
|
||||||
class ModelAccessPermission(permissions.BasePermission):
|
class ModelAccessPermission(permissions.BasePermission):
|
||||||
@@ -196,6 +196,17 @@ class TaskPermission(ModelAccessPermission):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
class WorkflowApprovalPermission(ModelAccessPermission):
|
||||||
|
'''
|
||||||
|
Permission check used by workflow approval and deny views
|
||||||
|
to determine who can has access to approve and deny paused workflow nodes
|
||||||
|
'''
|
||||||
|
|
||||||
|
def check_post_permissions(self, request, view, obj=None):
|
||||||
|
approval = get_object_or_400(view.model, pk=view.kwargs['pk'])
|
||||||
|
return check_user_access(request.user, view.model, 'approve_or_deny', approval)
|
||||||
|
|
||||||
|
|
||||||
class ProjectUpdatePermission(ModelAccessPermission):
|
class ProjectUpdatePermission(ModelAccessPermission):
|
||||||
'''
|
'''
|
||||||
Permission check used by ProjectUpdateView to determine who can update projects
|
Permission check used by ProjectUpdateView to determine who can update projects
|
||||||
|
|||||||
@@ -3527,7 +3527,9 @@ class LaunchConfigurationBaseSerializer(BaseSerializer):
|
|||||||
elif self.instance:
|
elif self.instance:
|
||||||
ujt = self.instance.unified_job_template
|
ujt = self.instance.unified_job_template
|
||||||
if ujt is None:
|
if ujt is None:
|
||||||
return {'workflow_job_template': attrs['workflow_job_template']}
|
if 'workflow_job_template' in attrs:
|
||||||
|
return {'workflow_job_template': attrs['workflow_job_template']}
|
||||||
|
return {}
|
||||||
|
|
||||||
# build additional field survey_passwords to track redacted variables
|
# build additional field survey_passwords to track redacted variables
|
||||||
password_dict = {}
|
password_dict = {}
|
||||||
|
|||||||
@@ -91,7 +91,8 @@ from awx.main.redact import UriCleaner
|
|||||||
from awx.api.permissions import (
|
from awx.api.permissions import (
|
||||||
JobTemplateCallbackPermission, TaskPermission, ProjectUpdatePermission,
|
JobTemplateCallbackPermission, TaskPermission, ProjectUpdatePermission,
|
||||||
InventoryInventorySourcesUpdatePermission, UserPermission,
|
InventoryInventorySourcesUpdatePermission, UserPermission,
|
||||||
InstanceGroupTowerPermission, VariableDataPermission
|
InstanceGroupTowerPermission, VariableDataPermission,
|
||||||
|
WorkflowApprovalPermission
|
||||||
)
|
)
|
||||||
from awx.api import renderers
|
from awx.api import renderers
|
||||||
from awx.api import serializers
|
from awx.api import serializers
|
||||||
@@ -4452,6 +4453,7 @@ class WorkflowApprovalDetail(UnifiedJobDeletionMixin, RetrieveDestroyAPIView):
|
|||||||
class WorkflowApprovalApprove(RetrieveAPIView):
|
class WorkflowApprovalApprove(RetrieveAPIView):
|
||||||
model = models.WorkflowApproval
|
model = models.WorkflowApproval
|
||||||
serializer_class = serializers.WorkflowApprovalViewSerializer
|
serializer_class = serializers.WorkflowApprovalViewSerializer
|
||||||
|
permission_classes = (WorkflowApprovalPermission,)
|
||||||
|
|
||||||
def post(self, request, *args, **kwargs):
|
def post(self, request, *args, **kwargs):
|
||||||
obj = self.get_object()
|
obj = self.get_object()
|
||||||
@@ -4465,6 +4467,7 @@ class WorkflowApprovalApprove(RetrieveAPIView):
|
|||||||
class WorkflowApprovalDeny(RetrieveAPIView):
|
class WorkflowApprovalDeny(RetrieveAPIView):
|
||||||
model = models.WorkflowApproval
|
model = models.WorkflowApproval
|
||||||
serializer_class = serializers.WorkflowApprovalViewSerializer
|
serializer_class = serializers.WorkflowApprovalViewSerializer
|
||||||
|
permission_classes = (WorkflowApprovalPermission,)
|
||||||
|
|
||||||
def post(self, request, *args, **kwargs):
|
def post(self, request, *args, **kwargs):
|
||||||
obj = self.get_object()
|
obj = self.get_object()
|
||||||
|
|||||||
@@ -2799,9 +2799,11 @@ class WorkflowApprovalAccess(BaseAccess):
|
|||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
return super(WorkflowApprovalAccess, self).get_queryset().exclude(
|
return super(WorkflowApprovalAccess, self).get_queryset().exclude(
|
||||||
workflow_approval_template__isnull=False)
|
workflow_approval_template__isnull=True)
|
||||||
|
|
||||||
def can_approve_or_deny(self, obj):
|
def can_approve_or_deny(self, obj):
|
||||||
|
if obj.status != 'pending':
|
||||||
|
return False
|
||||||
wfjt = obj.unified_job_node.workflow_job.unified_job_template
|
wfjt = obj.unified_job_node.workflow_job.unified_job_template
|
||||||
if self.user in wfjt.approval_role or self.user.is_superuser:
|
if self.user in wfjt.approval_role or self.user.is_superuser:
|
||||||
return True
|
return True
|
||||||
|
|||||||
@@ -60,4 +60,14 @@ class Migration(migrations.Migration):
|
|||||||
name='workflow_approval_template',
|
name='workflow_approval_template',
|
||||||
field=models.ManyToManyField(blank=True, to='main.WorkflowApprovalTemplate'),
|
field=models.ManyToManyField(blank=True, to='main.WorkflowApprovalTemplate'),
|
||||||
),
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='organization',
|
||||||
|
name='read_role',
|
||||||
|
field=awx.main.fields.ImplicitRoleField(editable=False, null='True', on_delete=django.db.models.deletion.CASCADE, parent_role=['member_role', 'auditor_role', 'execute_role', 'project_admin_role', 'inventory_admin_role', 'workflow_admin_role', 'notification_admin_role', 'credential_admin_role', 'job_template_admin_role', 'approval_role'], related_name='+', to='main.Role'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='workflowjobtemplate',
|
||||||
|
name='read_role',
|
||||||
|
field=awx.main.fields.ImplicitRoleField(editable=False, null='True', on_delete=django.db.models.deletion.CASCADE, parent_role=['singleton:system_auditor', 'organization.auditor_role', 'execute_role', 'admin_role', 'approval_role'], related_name='+', to='main.Role'),
|
||||||
|
),
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -87,7 +87,7 @@ class Organization(CommonModel, NotificationFieldsModel, ResourceMixin, CustomVi
|
|||||||
'execute_role', 'project_admin_role',
|
'execute_role', 'project_admin_role',
|
||||||
'inventory_admin_role', 'workflow_admin_role',
|
'inventory_admin_role', 'workflow_admin_role',
|
||||||
'notification_admin_role', 'credential_admin_role',
|
'notification_admin_role', 'credential_admin_role',
|
||||||
'job_template_admin_role',],
|
'job_template_admin_role', 'approval_role',],
|
||||||
)
|
)
|
||||||
approval_role = ImplicitRoleField(
|
approval_role = ImplicitRoleField(
|
||||||
parent_role='admin_role',
|
parent_role='admin_role',
|
||||||
|
|||||||
@@ -393,7 +393,8 @@ class WorkflowJobTemplate(UnifiedJobTemplate, WorkflowJobOptions, SurveyJobTempl
|
|||||||
])
|
])
|
||||||
read_role = ImplicitRoleField(parent_role=[
|
read_role = ImplicitRoleField(parent_role=[
|
||||||
'singleton:' + ROLE_SINGLETON_SYSTEM_AUDITOR,
|
'singleton:' + ROLE_SINGLETON_SYSTEM_AUDITOR,
|
||||||
'organization.auditor_role', 'execute_role', 'admin_role'
|
'organization.auditor_role', 'execute_role', 'admin_role',
|
||||||
|
'approval_role',
|
||||||
])
|
])
|
||||||
approval_role = ImplicitRoleField(parent_role=[
|
approval_role = ImplicitRoleField(parent_role=[
|
||||||
'singleton:' + ROLE_SINGLETON_SYSTEM_AUDITOR,
|
'singleton:' + ROLE_SINGLETON_SYSTEM_AUDITOR,
|
||||||
|
|||||||
@@ -523,6 +523,7 @@ class TaskManager():
|
|||||||
workflow_approval = WorkflowApproval.objects.filter(status='pending').prefetch_related('workflow_approval_template')
|
workflow_approval = WorkflowApproval.objects.filter(status='pending').prefetch_related('workflow_approval_template')
|
||||||
now = tz_now()
|
now = tz_now()
|
||||||
for task in workflow_approval:
|
for task in workflow_approval:
|
||||||
|
# TODO: copy the timeout to the job itself at launch time, not the template
|
||||||
approval_timeout_seconds = timedelta(seconds=task.workflow_approval_template.timeout)
|
approval_timeout_seconds = timedelta(seconds=task.workflow_approval_template.timeout)
|
||||||
if task.workflow_approval_template.timeout == 0:
|
if task.workflow_approval_template.timeout == 0:
|
||||||
continue
|
continue
|
||||||
|
|||||||
Reference in New Issue
Block a user