Skip to content

Commit ab60f77

Browse files
feat: adds functionality to handle user access requests for modules (#67)
Users with approver permissions can approve or decline a user's request for access module.
1 parent 6a9b19f commit ab60f77

File tree

11 files changed

+1280
-940
lines changed

11 files changed

+1280
-940
lines changed

Access/accessrequest_helper.py

Lines changed: 247 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,14 @@
22
import logging
33
import time
44
from . import helpers as helper
5+
from Access import notifications
56

67
from BrowserStackAutomation.settings import DECLINE_REASONS
78
from Access.models import UserAccessMapping, User, GroupV2, AccessV2
89
import datetime
910
import json
1011
from django.db import transaction
12+
from Access.background_task_manager import background_task
1113

1214
logger = logging.getLogger(__name__)
1315

@@ -19,6 +21,7 @@
1921
"title": "{access_tag}: Duplicate Request not submitted",
2022
"msg": "Access already granted or request in pending state. {access_label}",
2123
}
24+
REQUEST_PROCESS_MSG = "The Request ({request_id}) is now being processed"
2225
REQUEST_ERR_MSG = {
2326
"error_msg": "Invalid Request",
2427
"msg": "Please Contact Admin",
@@ -27,43 +30,10 @@
2730
"error_msg": "The submitted form is empty. Tried direct access to reqeust access page",
2831
"msg": "Error Occured while submitting your Request. Please contact the Admin",
2932
}
30-
31-
REQUEST_ACCESS_AUTO_APPROVED_MSG = {
32-
"title": "{request_id} Request Approved",
33-
"msg": "Once granted you will receive the update",
34-
}
35-
36-
REQUEST_DB_ERR_MSG = {
37-
"error_msg": "Error Saving Request",
38-
"msg": "Please Contact Admin",
39-
}
40-
REQUEST_IDENTITY_NOT_SETUP_ERR_MSG = {
41-
"error_msg": "Identity not setup",
42-
"msg": "User Identity for module {access_tag} not setup by the user",
43-
}
44-
45-
REQUEST_SUCCESS_MSG = {
46-
"title": "{request_id} Request Submitted",
47-
"msg": "Once approved you will receive the update. {access_label}",
48-
}
49-
REQUEST_DUPLICATE_ERR_MSG = {
50-
"title": "{access_tag}: Duplicate Request not submitted",
51-
"msg": "Access already granted or request in pending state. {access_label}",
52-
}
53-
REQUEST_ERR_MSG = {
54-
"error_msg": "Invalid Request",
55-
"msg": "Please Contact Admin",
56-
}
57-
REQUEST_EMPTY_FORM_ERR_MSG = {
58-
"error_msg": "The submitted form is empty. Tried direct access to reqeust access page",
59-
"msg": "Error Occured while submitting your Request. Please contact the Admin",
60-
}
61-
6233
REQUEST_ACCESS_AUTO_APPROVED_MSG = {
6334
"title": "{request_id} Request Approved",
6435
"msg": "Once granted you will receive the update",
6536
}
66-
6737
REQUEST_DB_ERR_MSG = {
6838
"error_msg": "Error Saving Request",
6939
"msg": "Please Contact Admin",
@@ -72,6 +42,12 @@
7242
"error_msg": "Identity not setup",
7343
"msg": "User Identity for module {access_tag} not setup by the user",
7444
}
45+
USER_REQUEST_IN_PROCESS_ERR_MSG = "The Request ({request_id}) has already been processed. \
46+
Please check Access History for more information"
47+
USER_REQUEST_PERMISSION_DENIED_ERR_MSG = "Permission Denied!"
48+
USER_REQUEST_DECLINE_MSG = "Declined Request {request_id} - Reason: {decline_reason}"
49+
USER_REQUEST_SECONDARY_PENDING_MSG = "The Request ({request_id}) is approved by {approved_by} \
50+
Pending on secondary approver"
7551

7652

7753
def requestAccessGet(request):
@@ -117,6 +93,30 @@ def requestAccessGet(request):
11793
return context
11894

11995

96+
def validate_approver_permissions(access_mapping, access_type, request, request_id):
97+
json_response = {}
98+
99+
access_label = access_mapping.access.access_label
100+
try:
101+
permissions = _get_approver_permissions(access_type, access_label)
102+
except Exception as e:
103+
return process_error_response(e)
104+
105+
approver_permissions = permissions["approver_permissions"]
106+
if "2" in approver_permissions and access_mapping.is_secondary_pending():
107+
if not request.user.user.has_permission(approver_permissions["2"]):
108+
logger.debug(USER_REQUEST_PERMISSION_DENIED_ERR_MSG)
109+
json_response["error"] = USER_REQUEST_PERMISSION_DENIED_ERR_MSG
110+
return json_response
111+
elif access_mapping.is_pending():
112+
if not request.user.user.has_permission(approver_permissions["1"]):
113+
logger.debug(USER_REQUEST_PERMISSION_DENIED_ERR_MSG)
114+
json_response["error"] = USER_REQUEST_PERMISSION_DENIED_ERR_MSG
115+
return json_response
116+
117+
return json_response
118+
119+
120120
def getGrantFailedRequests(request):
121121
try:
122122
failures = UserAccessMapping.objects.filter(
@@ -134,7 +134,7 @@ def getGrantFailedRequests(request):
134134
context = {"failures": failures, "heading": "Grant Failures"}
135135
return context
136136
except Exception as e:
137-
return process_error_response(request, e)
137+
return process_error_response(e)
138138

139139

140140
def get_pending_revoke_failures(request):
@@ -178,7 +178,39 @@ def getPendingRequests(request):
178178

179179
return context
180180
except Exception as e:
181-
return process_error_response(request, e)
181+
return process_error_response(e)
182+
183+
184+
def get_decline_access_request(request, access_type, request_id):
185+
logger.info("Decline Access Request call initiated")
186+
try:
187+
context = {"response": {}}
188+
reason = request.GET["reason"]
189+
request_ids = []
190+
return_ids = []
191+
if access_type.endswith("-club"):
192+
for value in [request_id]:
193+
return_ids.append(value)
194+
current_ids = list(
195+
UserAccessMapping.get_pending_access_mapping(request_id=value)
196+
)
197+
request_ids.extend(current_ids)
198+
access_type = access_type.rsplit("-", 1)[0]
199+
else:
200+
request_ids = [request_id]
201+
for current_request_id in request_ids:
202+
response = decline_individual_access(
203+
request, access_type, current_request_id, reason
204+
)
205+
if "error" in response:
206+
response["success"] = False
207+
else:
208+
response["success"] = True
209+
context["response"][current_request_id] = response
210+
context["returnIds"] = return_ids
211+
return context
212+
except Exception as e:
213+
return process_error_response(e)
182214

183215

184216
def get_pending_accesses_from_modules(access_user):
@@ -191,7 +223,6 @@ def get_pending_accesses_from_modules(access_user):
191223
access_module,
192224
) in helpers.get_available_access_modules().items():
193225
access_module_start_time = time.time()
194-
195226
try:
196227
pending_accesses = access_module.get_pending_accesses(access_user)
197228
except Exception as e:
@@ -282,7 +313,7 @@ def process_group_requests(group_pending_requests, group_requests):
282313
group_requests[club_id]["accessData"].append(accessData)
283314

284315

285-
def process_error_response(request, e):
316+
def process_error_response(e):
286317
logger.debug("Error in request not found OR Invalid request type")
287318
logger.exception(e)
288319
json_response = {}
@@ -475,3 +506,182 @@ def validate_access_labels(access_labels_json, access_type):
475506
)
476507
)
477508
return access_labels
509+
510+
511+
def _get_approver_permissions(access_type, access_label=None):
512+
json_response = {}
513+
514+
access_module = helper.get_available_access_module_from_tag(access_type)
515+
approver_permissions = []
516+
approver_permissions = access_module.fetch_approver_permissions(access_label)
517+
518+
json_response["approver_permissions"] = approver_permissions
519+
if len(json_response) == 0:
520+
raise Exception("Approver Permissions not found for module %s " % access_module)
521+
return json_response
522+
523+
524+
def is_request_valid(request_id, access_mapping):
525+
if access_mapping.is_already_processed():
526+
logger.warning(
527+
USER_REQUEST_IN_PROCESS_ERR_MSG.format(
528+
request_id=request_id,
529+
)
530+
)
531+
return False
532+
533+
return True
534+
535+
536+
def accept_user_access_requests(request, access_type, request_id):
537+
json_response = {}
538+
access_mapping = UserAccessMapping.get_access_request(request_id)
539+
if not is_request_valid(request_id, access_mapping):
540+
json_response["error"] = USER_REQUEST_IN_PROCESS_ERR_MSG.format(
541+
request_id=request_id,
542+
)
543+
return json_response
544+
545+
requester = access_mapping.user_identity.user.email
546+
if request.user.username == requester:
547+
json_response["error"] = USER_REQUEST_PERMISSION_DENIED_ERR_MSG
548+
return json_response
549+
550+
access_label = access_mapping.access.access_label
551+
552+
try:
553+
permissions = _get_approver_permissions(access_type, access_label)
554+
approver_permissions = permissions["approver_permissions"]
555+
if not helper.check_user_permissions(
556+
request.user, list(approver_permissions.values())
557+
):
558+
logger.debug(USER_REQUEST_PERMISSION_DENIED_ERR_MSG)
559+
json_response["error"] = USER_REQUEST_PERMISSION_DENIED_ERR_MSG
560+
return json_response
561+
562+
is_primary_approver = (
563+
access_mapping.is_pending()
564+
and request.user.user.has_permission(approver_permissions["1"])
565+
)
566+
is_secondary_approver = (
567+
access_mapping.is_secondary_pending()
568+
and request.user.user.has_permission(approver_permissions["2"])
569+
)
570+
571+
if not (is_primary_approver or is_secondary_approver):
572+
logger.debug(USER_REQUEST_PERMISSION_DENIED_ERR_MSG)
573+
json_response["error"] = USER_REQUEST_PERMISSION_DENIED_ERR_MSG
574+
return json_response
575+
if is_primary_approver and "2" in approver_permissions:
576+
access_mapping.approver_1 = request.user.user
577+
access_mapping.update_access_status("SecondaryPending")
578+
json_response["msg"] = USER_REQUEST_SECONDARY_PENDING_MSG.format(
579+
request_id=request_id, approved_by=request.user.username
580+
)
581+
logger.debug(
582+
USER_REQUEST_SECONDARY_PENDING_MSG.format(
583+
request_id=request_id, approved_by=request.user.username
584+
)
585+
)
586+
else:
587+
json_response = run_accept_request_task(
588+
is_primary_approver,
589+
access_mapping,
590+
request,
591+
request_id,
592+
access_type,
593+
access_label,
594+
)
595+
except Exception as e:
596+
return process_error_response(e)
597+
598+
return json_response
599+
600+
601+
def run_accept_request_task(
602+
is_primary_approver, access_mapping, request, request_id, access_type, access_label
603+
):
604+
json_response = {}
605+
json_response["status"] = []
606+
if is_primary_approver:
607+
access_mapping.approver_1 = request.user.user
608+
else:
609+
access_mapping.approver_2 = request.user.user
610+
json_response["msg"] = REQUEST_PROCESS_MSG.format(request_id=request_id)
611+
612+
with transaction.atomic():
613+
try:
614+
access_mapping.update_access_status("Processing")
615+
616+
background_task(
617+
"run_accept_request",
618+
json.dumps({"request_id": request_id, "access_type": access_type}),
619+
)
620+
except Exception as e:
621+
logger.exception(e)
622+
raise Exception(
623+
"Error in accepting the request - {request_id}. Please try again.".format(
624+
request_id=request_id
625+
)
626+
)
627+
628+
json_response["status"].append(
629+
{
630+
"title": REQUEST_SUCCESS_MSG["title"].format(request_id=request_id),
631+
"msg": REQUEST_SUCCESS_MSG["msg"].format(
632+
access_label=json.dumps(access_label)
633+
),
634+
}
635+
)
636+
637+
return json_response
638+
639+
640+
def decline_individual_access(request, access_type, request_id, reason):
641+
json_response = {}
642+
access_mapping = UserAccessMapping.get_access_request(request_id)
643+
if not is_request_valid(request_id, access_mapping):
644+
json_response["error"] = USER_REQUEST_IN_PROCESS_ERR_MSG.format(
645+
request_id=request_id,
646+
)
647+
return json_response
648+
649+
json_response = validate_approver_permissions(
650+
access_mapping, access_type, request, request_id
651+
)
652+
if "error" in json_response:
653+
return json_response
654+
655+
with transaction.atomic():
656+
access_mapping.decline_access(reason)
657+
if hasattr(access_mapping, "approver_1"):
658+
access_mapping.decline_reason = reason
659+
if access_mapping.approver_1 is not None:
660+
access_mapping.approver_2 = request.user.user
661+
else:
662+
access_mapping.approver_1 = request.user.user
663+
else:
664+
access_mapping.reason = reason
665+
access_mapping.approver = request.user.username
666+
667+
access_mapping.save()
668+
669+
access_module = helper.get_available_access_module_from_tag(access_type)
670+
access_labels = [access_mapping.access.access_label]
671+
description = access_module.combine_labels_desc(access_labels)
672+
notifications.send_mail_for_request_decline(
673+
request, description, request_id, reason, access_type
674+
)
675+
676+
logger.debug(
677+
USER_REQUEST_DECLINE_MSG.format(
678+
request_id=request_id,
679+
decline_reason=reason,
680+
)
681+
)
682+
json_response = {}
683+
json_response["msg"] = USER_REQUEST_DECLINE_MSG.format(
684+
request_id=request_id,
685+
decline_reason=reason,
686+
)
687+
return json_response

0 commit comments

Comments
 (0)