-
-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
87ef152
commit fa910c4
Showing
26 changed files
with
593 additions
and
97 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,10 +1,13 @@ | ||
from django.contrib import admin | ||
|
||
from aw.models import Job, JobExecution, JobPermission, JobPermissionMemberUser, JobPermissionMemberGroup, AwAPIKey | ||
from aw.models import Job, JobExecution, \ | ||
JobPermission, JobPermissionMemberUser, JobPermissionMemberGroup, JobPermissionMapping, \ | ||
AwAPIKey | ||
|
||
admin.site.register(Job) | ||
admin.site.register(JobExecution) | ||
admin.site.register(JobPermission) | ||
admin.site.register(JobPermissionMemberUser) | ||
admin.site.register(JobPermissionMemberGroup) | ||
admin.site.register(JobPermissionMapping) | ||
admin.site.register(AwAPIKey) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,144 @@ | ||
from django.core.exceptions import ObjectDoesNotExist | ||
from rest_framework.views import APIView | ||
from rest_framework import serializers | ||
from rest_framework.response import Response | ||
from drf_spectacular.utils import extend_schema, OpenApiResponse | ||
|
||
from aw.models import Job | ||
from aw.api_endpoints.base import API_PERMISSION, get_api_user, BaseResponse | ||
from aw.api_endpoints.job_util import get_viewable_jobs_serialized, get_job_if_allowed, \ | ||
JobReadResponse | ||
from aw.model.job import CHOICE_JOB_PERMISSION_READ, CHOICE_JOB_PERMISSION_WRITE | ||
|
||
|
||
class JobWriteRequest(JobReadResponse): | ||
pass | ||
|
||
|
||
class JobWriteResponse(BaseResponse): | ||
msg = serializers.CharField() | ||
|
||
|
||
class JobListCreate(APIView): | ||
http_method_names = ['post', 'get'] | ||
serializer_class = JobReadResponse | ||
permission_classes = API_PERMISSION | ||
|
||
@staticmethod | ||
@extend_schema( | ||
request=None, | ||
responses={ | ||
200: OpenApiResponse(JobReadResponse, description='Return list of jobs'), | ||
}, | ||
summary='Return list of all jobs the current user is privileged to view.', | ||
) | ||
def get(request): | ||
return Response(data=get_viewable_jobs_serialized(get_api_user(request)), status=200) | ||
|
||
@extend_schema( | ||
request=None, | ||
responses={ | ||
200: OpenApiResponse(JobWriteResponse, description='Job created'), | ||
400: OpenApiResponse(JobWriteResponse, description='Invalid job data provided'), | ||
}, | ||
summary='Create a new job.', | ||
) | ||
def post(self, request): | ||
serializer = JobWriteRequest(data=request.data) | ||
if not serializer.is_valid(): | ||
return Response(data={'msg': 'Provided job data is not valid'}, status=400) | ||
|
||
serializer.save() | ||
return Response(data={'msg': 'Job created'}, status=200) | ||
|
||
|
||
class JobViewEditDelete(APIView): | ||
http_method_names = ['get', 'delete', 'put'] | ||
serializer_class = JobWriteResponse | ||
permission_classes = API_PERMISSION | ||
|
||
@staticmethod | ||
@extend_schema( | ||
request=None, | ||
responses={ | ||
200: OpenApiResponse(JobReadResponse, description='Return job information'), | ||
404: OpenApiResponse(JobReadResponse, description='Job does not exist'), | ||
}, | ||
summary='Return information about an existing job.', | ||
) | ||
def get(request, job_id: int): | ||
# pylint: disable=E1101 | ||
user = get_api_user(request) | ||
job = get_job_if_allowed( | ||
user=user, | ||
job=Job.objects.filter(id=job_id), | ||
permission_needed=CHOICE_JOB_PERMISSION_READ, | ||
) | ||
if job is None: | ||
return Response(data={'msg': 'Job does not exist or is not viewable'}, status=404) | ||
|
||
return Response(data=JobReadResponse(instance=job).data, status=200) | ||
|
||
@extend_schema( | ||
request=None, | ||
responses={ | ||
200: OpenApiResponse(JobWriteResponse, description='Job deleted'), | ||
403: OpenApiResponse(JobWriteResponse, description='Not privileged to delete the job'), | ||
404: OpenApiResponse(JobWriteResponse, description='Job does not exist'), | ||
}, | ||
summary='Delete an existing job.', | ||
) | ||
def delete(self, request, job_id: int): | ||
# pylint: disable=E1101 | ||
user = get_api_user(request) | ||
try: | ||
result = Job.objects.filter(id=job_id) | ||
|
||
if result.exists(): | ||
result = get_job_if_allowed(user=user, job=result, permission_needed=CHOICE_JOB_PERMISSION_WRITE) | ||
|
||
if result is not None: | ||
result.delete() | ||
return Response(data={'msg': 'Job deleted'}, status=200) | ||
|
||
return Response(data={'msg': 'Not privileged to delete the job'}, status=403) | ||
|
||
except ObjectDoesNotExist: | ||
pass | ||
|
||
return Response(data={'msg': 'Job does not exist'}, status=404) | ||
|
||
@extend_schema( | ||
request=None, | ||
responses={ | ||
200: OpenApiResponse(JobWriteResponse, description='Job updated'), | ||
400: OpenApiResponse(JobWriteResponse, description='Invalid job data provided'), | ||
403: OpenApiResponse(JobWriteResponse, description='Not privileged to modify the job'), | ||
404: OpenApiResponse(JobWriteResponse, description='Job does not exist'), | ||
}, | ||
summary='Delete an existing job.', | ||
) | ||
def put(self, request, job_id: int): | ||
# pylint: disable=E1101 | ||
user = get_api_user(request) | ||
try: | ||
result = Job.objects.filter(id=job_id) | ||
|
||
if result.exists(): | ||
result = get_job_if_allowed(user=user, job=result, permission_needed=CHOICE_JOB_PERMISSION_WRITE) | ||
|
||
if result is None: | ||
return Response(data={'msg': 'Not privileged to modify the job'}, status=403) | ||
|
||
serializer = JobWriteRequest(data=request.data) | ||
if not serializer.is_valid(): | ||
return Response(data={'msg': 'Provided job data is not valid'}, status=400) | ||
|
||
result.update(**serializer.data) | ||
result.save() | ||
return Response(data={'msg': 'Job updated'}, status=200) | ||
|
||
except ObjectDoesNotExist: | ||
pass | ||
|
||
return Response(data={'msg': 'Job does not exist'}, status=404) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
from django.conf import settings | ||
from rest_framework import serializers | ||
|
||
from aw.model.job import Job, JobPermissionMapping, JobPermissionMemberUser, JobPermissionMemberGroup, \ | ||
CHOICE_JOB_PERMISSION_READ | ||
|
||
|
||
class JobReadResponse(serializers.ModelSerializer): | ||
class Meta: | ||
model = Job | ||
fields = Job.api_fields | ||
|
||
|
||
def get_job_if_allowed(user: settings.AUTH_USER_MODEL, job: Job, permission_needed: int) -> (Job, None): | ||
# pylint: disable=E1101 | ||
if job is None: | ||
return None | ||
|
||
# if job has no permissions set | ||
permission_links = JobPermissionMapping.objects.filter(job=job) | ||
if not permission_links.exists(): | ||
return job | ||
|
||
for link in permission_links: | ||
# ignore permission if access-level is too low | ||
if link.permission < permission_needed: | ||
continue | ||
|
||
# if one of the permissions allows the user | ||
if JobPermissionMemberUser.objects.filter(user=user, permission=link.permission).exists(): | ||
return job | ||
|
||
# if one of the permissions allows a group that the user is a member of | ||
groups = JobPermissionMemberGroup.objects.filter(permission=link.permission) | ||
if groups.exists() and user.groups.filter(name__in=[ | ||
group.name for group in groups | ||
]).exists(): | ||
return job | ||
|
||
return None | ||
|
||
|
||
def get_viewable_jobs(user: settings.AUTH_USER_MODEL) -> list[Job]: | ||
# pylint: disable=E1101 | ||
jobs = Job.objects.all() | ||
jobs_viewable = [] | ||
|
||
for job in jobs: | ||
job = get_job_if_allowed(user=user, job=job, permission_needed=CHOICE_JOB_PERMISSION_READ) | ||
if job is not None: | ||
jobs_viewable.append(job) | ||
|
||
return jobs_viewable | ||
|
||
|
||
def get_viewable_jobs_serialized(user: settings.AUTH_USER_MODEL) -> list[dict]: | ||
return [JobReadResponse(instance=job).data for job in get_viewable_jobs(user)] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
FORM_LABEL = { | ||
'jobs': { | ||
'manage': { | ||
'job': { | ||
'environment_vars': 'Environmental Variables', | ||
} | ||
} | ||
} | ||
} | ||
|
||
FORM_HELP = { | ||
'jobs': { | ||
'manage': { | ||
'job': { | ||
'inventory': 'One or multiple inventory files/directories to include for the execution. ' | ||
'Comma-separated list. For details see: ' | ||
'<a href="https://docs.ansible.com/ansible/latest/inventory_guide/intro_inventory.html">' | ||
'Ansible Docs - Inventory</a>', | ||
'limit': 'Ansible inventory hosts or groups to limit the execution to.' | ||
'For details see: ' | ||
'<a href="https://docs.ansible.com/ansible/latest/inventory_guide/intro_patterns.html">' | ||
'Ansible Docs - Limit</a>', | ||
'schedule': 'Schedule for running the job automatically. For format see: ' | ||
'<a href="https://crontab.guru/">crontab.guru</a>', | ||
'environment_vars': 'Environmental variables to be passed to the Ansible execution. ' | ||
'Comma-separated list of key-value pairs. (VAR1=TEST1,VAR2=0)', | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.