Skip to content

Commit

Permalink
Merge pull request #23 from davidslusser/serviceaccount_api_mixin
Browse files Browse the repository at this point in the history
added mixins for encource service account enabled for api reponses
  • Loading branch information
davidslusser committed Jan 2, 2021
2 parents 42a56d0 + 08b3ac9 commit e2ad671
Show file tree
Hide file tree
Showing 5 changed files with 74 additions and 5 deletions.
2 changes: 1 addition & 1 deletion userextensions/__init__.py
Expand Up @@ -7,7 +7,7 @@
"""

__title__ = 'django-userextensions'
__version__ = '0.0.12'
__version__ = '0.0.13'
__author__ = 'David Slusser'
__email__ = 'dbslusser@gmail.com'
__license__ = 'GPL-3.0'
Expand Down
58 changes: 58 additions & 0 deletions userextensions/mixins.py
@@ -0,0 +1,58 @@
import re
from django.http import JsonResponse
from rest_framework import status
from .models import ServiceAccount


class ServiceAccountControlMixin:
""" A mixin for Django Rest Framework viewsets that checks if the request is made from a ServiceAccount and only
allows access to the endpoint if the ServiceAccount is enabled and admin_enabled. If the ServiceAccount is disabled
by the owner or an admin, a 403 error will be received instead of the API response. This currently works for APIs
using TokenAuthentication (rest_framework.authentication.TokenAuthentication). """
def dispatch(self, request, *args, **kwargs):
mo = re.search('Token (\S+)', request.META.get('HTTP_AUTHORIZATION', ''))
if mo:
srv_acct = ServiceAccount.objects.get_object_or_none(user__auth_token__key=mo.group(1))
if srv_acct:
if srv_acct.admin_enabled is False:
return JsonResponse(data={'detail': 'this service account has been administratively disabled'},
status=status.HTTP_403_FORBIDDEN)
elif srv_acct.enabled is False:
return JsonResponse(data={'detail': 'this service account has been disabled'},
status=status.HTTP_403_FORBIDDEN)
return super().dispatch(request, *args, **kwargs)


class AllowOnlyServiceAccountMixin:
""" A mixin for Django Rest Framework viewsets that only allows responses to requests made from ServiceAccounts.
This currently works for APIs using TokenAuthentication (rest_framework.authentication.TokenAuthentication)."""
def dispatch(self, request, *args, **kwargs):
mo = re.search('Token (\S+)', request.META.get('HTTP_AUTHORIZATION', ''))
if mo:
srv_acct = ServiceAccount.objects.get_object_or_none(user__auth_token__key=mo.group(1))
if not srv_acct:
return JsonResponse(data={'detail': 'access to this endpoint is only available to service accounts'},
status=status.HTTP_403_FORBIDDEN)
return super().dispatch(request, *args, **kwargs)


class AllowOnlyEnabledServiceAccountMixin:
""" A mixin for Django Rest Framework viewsets that checks if the request is made from a ServiceAccount and only
allows access to the endpoint if an enabled ServiceAccount made the request. If the ServiceAccount is disabled
by the owner or an admin, a 403 error will be received instead of the API response. This currently works for APIs
using TokenAuthentication (rest_framework.authentication.TokenAuthentication). """
def dispatch(self, request, *args, **kwargs):
mo = re.search('Token (\S+)', request.META.get('HTTP_AUTHORIZATION', ''))
if mo:
srv_acct = ServiceAccount.objects.get_object_or_none(user__auth_token__key=mo.group(1))
if srv_acct:
if srv_acct.admin_enabled is False:
return JsonResponse(data={'detail': 'this service account has been administratively disabled'},
status=status.HTTP_403_FORBIDDEN)
elif srv_acct.enabled is False:
return JsonResponse(data={'detail': 'this service account has been disabled'},
status=status.HTTP_403_FORBIDDEN)
else:
return JsonResponse(data={'detail': 'access to this endpoint is only available to enabled '
'service accounts'}, status=status.HTTP_403_FORBIDDEN)
return super().dispatch(request, *args, **kwargs)
Expand Up @@ -57,7 +57,7 @@ <h1 class="text-primary"><b>User Profile: </b><span class="text-secondary"><smal
<div class="col-sm-3 font-weight-bold text-primary">API Token: </div>
<div class="col-sm-9">
{{ token }} &nbsp;&nbsp;
<a href="#" class="atmo-link-padding atmo-link-text" title="refresh token"
<a href="#" title="refresh token"
onClick="confirmAction('{% url "userextensions:refresh_api_token" %}', 'Update Token', 'This will delete your API token and create a new one. Do you wish to continue?', 'Continue', 'POST');"
role="button" data-toggle="tooltip" data-placement="left">
<i class="fas fa-sync-alt"></i>
Expand Down
3 changes: 2 additions & 1 deletion userextensions/views/ajax.py
Expand Up @@ -29,7 +29,8 @@ def get_users_per_group(request):
obj_id = request.GET['client_response']
obj = Group.objects.get(id=obj_id)
template = loader.get_template('userextensions/ajax/get_users_per_group.htm')
return HttpResponse(json.dumps({'server_response': template.render({'queryset': obj.user_set.all()})}),
return HttpResponse(json.dumps({'server_response': template.render(
{'queryset': obj.user_set.all().filter_by('username')})}),
content_type='application/javascript')
else:
return HttpResponse('Invalid request inputs', status=400)
Expand Down
14 changes: 12 additions & 2 deletions userextensions/views/gui.py
Expand Up @@ -104,7 +104,17 @@ def get(self, request):
select_related('user', 'group', 'user__auth_token').order_by('group__name')

# get list of groups that do not have a service account
context['groups'] = request.user.groups.filter(serviceaccount=None).\
prefetch_related('user_set').order_by('name')
# If the SRV_ACCOUNT_GROUP_FILTER_LIST (list) settings variable is set, only include groups whose name matches a
# provided regex pattern, otherwise include all groups. Set the SRV_ACCOUNT_ENFORCE_MATCHING (bool) settings
# variable to False to allow all groups, but maintain naming of the service account based on regex patterns.
regex_list = getattr(settings, 'SRV_ACCOUNT_GROUP_FILTER_LIST', list())
if regex_list and getattr(settings, 'SRV_ACCOUNT_ENFORCE_MATCHING', True):
group_queryset = request.user.groups.none()
for regex in regex_list:
group_queryset = group_queryset | request.user.groups.filter(serviceaccount=None, name__regex=regex)
else:
group_queryset = request.user.groups.filter(serviceaccount=None)

context['groups'] = group_queryset.prefetch_related('user_set').order_by('name')
context['base_template'] = self.base_template
return render(request, self.template, context=context)

0 comments on commit e2ad671

Please sign in to comment.