Skip to content

Commit 1e03380

Browse files
authored
feat: allows approval of group access
Allow user with specific permission to approve/decline group access. The approved access to group will be provided to all the approved members of the group.
1 parent 247c564 commit 1e03380

File tree

8 files changed

+440
-43
lines changed

8 files changed

+440
-43
lines changed

Access/accessrequest_helper.py

Lines changed: 271 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
import logging
22
import time
3+
from Access.views_helper import execute_group_access
4+
5+
from BrowserStackAutomation.settings import DECLINE_REASONS, MAIL_APPROVER_GROUPS
36
import datetime
47
import json
58
from django.db import transaction
@@ -9,12 +12,12 @@
912
)
1013
from Access.models import (
1114
UserAccessMapping,
15+
GroupAccessMapping,
1216
User,
1317
GroupV2,
1418
AccessV2,
1519
)
1620
from Access.background_task_manager import background_task
17-
from BrowserStackAutomation.settings import DECLINE_REASONS
1821
from . import helpers as helper
1922

2023
logger = logging.getLogger(__name__)
@@ -28,6 +31,7 @@
2831
"msg": "Access already granted or request in pending state. {access_label}",
2932
}
3033
REQUEST_PROCESS_MSG = "The Request ({request_id}) is now being processed"
34+
REQUEST_DECLINED_MSG = "The Request ({request_id}) is now declined"
3135
REQUEST_ERR_MSG = {
3236
"error_msg": "Invalid Request",
3337
"msg": "Please Contact Admin",
@@ -54,6 +58,24 @@
5458
USER_REQUEST_DECLINE_MSG = "Declined Request {request_id} - Reason: {decline_reason}"
5559
USER_REQUEST_SECONDARY_PENDING_MSG = "The Request ({request_id}) is approved by {approved_by} \
5660
Pending on secondary approver"
61+
INVALID_REQUEST_ERROR_MSG = "Error in request not found OR Invalid request type"
62+
ALREADY_PROCESSED_REQUEST_MSG = "An Already Approved/Declined/Processing Request \
63+
({request_id}) was accessed by {user}"
64+
SELF_APPROVAL_ERROR_MSG = (
65+
"You cannot approve your own request. Please ask other admins to do that"
66+
)
67+
ERROR_APPROVING_REQUEST_LOG_MSG = "Error Occured in acceptGroupAccess : {error} \
68+
Error Occured while approving request {request_id}"
69+
ERROR_APPROVING_REQUEST_DSP_MSG = (
70+
"Error Occured while accepting the request. Please contact the Admin - {error}"
71+
)
72+
SKIPPING_ACCESS_GRANT_MSG = (
73+
"Skipping group access grant for user {username} as user is not active"
74+
)
75+
APPROVAL_PROCESS_STARTED_MSG = "Process has been started for the Approval of request \
76+
- {request_id} - Approver: {approver}"
77+
ERROR_DECLINING_REQUEST_LOG_MSG = "Error in Decline of request {request_id}. \
78+
Error:{error} .Please contact admin."
5779

5880

5981
def get_request_access(request):
@@ -202,12 +224,26 @@ def get_decline_access_request(request, access_type, request_id):
202224
)
203225
request_ids.extend(current_ids)
204226
access_type = access_type.rsplit("-", 1)[0]
227+
elif access_type == "clubGroupAccess":
228+
for value in [request_id]: # ready for bulk decline
229+
return_ids.append(value)
230+
group_name, date_suffix = value.rsplit("-", 1)
231+
current_ids = list(
232+
GroupAccessMapping.get_pending_access_mapping(
233+
request_id=group_name
234+
).filter(request_id__contains=date_suffix)
235+
)
236+
request_ids.extend(current_ids)
237+
access_type = "groupAccess"
205238
else:
206239
request_ids = [request_id]
207240
for current_request_id in request_ids:
208-
response = decline_individual_access(
209-
request, access_type, current_request_id, reason
210-
)
241+
if access_type == "groupAccess":
242+
response = decline_group_access(request, current_request_id, reason)
243+
else:
244+
response = decline_individual_access(
245+
request, access_type, current_request_id, reason
246+
)
211247
if "error" in response:
212248
response["success"] = False
213249
else:
@@ -246,8 +282,9 @@ def get_pending_accesses_from_modules(access_user):
246282
process_group_requests(pending_accesses["group_requests"], group_requests)
247283

248284
logger.info(
249-
"Time to fetch pending requests of access module: %s - %s " %
250-
access_module_tag, str(time.time() - access_module_start_time)
285+
"Time to fetch pending requests of access module: %s - %s "
286+
% access_module_tag,
287+
str(time.time() - access_module_start_time),
251288
)
252289

253290
return individual_requests, list(group_requests.values())
@@ -265,7 +302,7 @@ def process_individual_requests(
265302
"club_id": club_id,
266303
"userEmail": accessrequest["userEmail"],
267304
"accessReason": accessrequest["accessReason"],
268-
"accessType": accessrequest["access_type"],
305+
"accessType": accessrequest["access_desc"],
269306
"access_tag": accessrequest["access_tag"],
270307
"requested_on": accessrequest["requested_on"],
271308
"sla_breached": helpers.sla_breached(accessrequest["requested_on"]),
@@ -649,9 +686,7 @@ def decline_individual_access(request, access_type, request_id, reason):
649686
)
650687
return json_response
651688

652-
json_response = validate_approver_permissions(
653-
access_mapping, access_type, request
654-
)
689+
json_response = validate_approver_permissions(access_mapping, access_type, request)
655690
if "error" in json_response:
656691
return json_response
657692

@@ -688,3 +723,229 @@ def decline_individual_access(request, access_type, request_id, reason):
688723
decline_reason=reason,
689724
)
690725
return json_response
726+
727+
728+
def accept_group_access(request, request_id):
729+
json_response = {}
730+
731+
group_mapping = GroupAccessMapping.get_by_request_id(request_id=request_id)
732+
if not group_mapping:
733+
logger.debug(INVALID_REQUEST_ERROR_MSG)
734+
json_response["error"] = INVALID_REQUEST_ERROR_MSG
735+
return json_response
736+
737+
try:
738+
access_type = group_mapping.access.access_tag
739+
access_label = group_mapping.access.access_label
740+
741+
permissions = _get_approver_permissions(access_type, access_label)
742+
approver_permissions = permissions["approver_permissions"]
743+
744+
if not helper.check_user_permissions(
745+
request.user, list(approver_permissions.values())
746+
):
747+
logger.debug(USER_REQUEST_PERMISSION_DENIED_ERR_MSG)
748+
return create_error_response(
749+
error_msg=USER_REQUEST_PERMISSION_DENIED_ERR_MSG
750+
)
751+
752+
if not (group_mapping.is_pending() or group_mapping.is_secondary_pending()):
753+
logger.warning(
754+
ALREADY_PROCESSED_REQUEST_MSG.format(
755+
request_id=request_id, user=request.user.username
756+
)
757+
)
758+
return create_error_response(
759+
error_msg=USER_REQUEST_IN_PROCESS_ERR_MSG.format(request_id=request_id)
760+
)
761+
elif group_mapping.is_self_approval(approver=request.user.user):
762+
return create_error_response(error_msg=SELF_APPROVAL_ERROR_MSG)
763+
else:
764+
is_primary_approver, is_secondary_approver = is_valid_approver(
765+
request=request,
766+
group_mapping=group_mapping,
767+
approver_permissions=approver_permissions,
768+
)
769+
if not (is_primary_approver or is_secondary_approver):
770+
logger.debug(USER_REQUEST_PERMISSION_DENIED_ERR_MSG)
771+
return create_error_response(
772+
error_msg=USER_REQUEST_PERMISSION_DENIED_ERR_MSG
773+
)
774+
if is_primary_approver and "2" in approver_permissions:
775+
group_mapping.set_primary_approver(request.user.user)
776+
json_response["msg"] = USER_REQUEST_SECONDARY_PENDING_MSG.format(
777+
request_id=request_id, approved_by=request.user.username
778+
)
779+
group_mapping.update_access_status(current_status="SecondaryPending")
780+
logger.debug(
781+
USER_REQUEST_SECONDARY_PENDING_MSG.format(
782+
request_id=request_id, approved_by=request.user.username
783+
)
784+
)
785+
else:
786+
if is_primary_approver:
787+
group_mapping.set_primary_approver(request.user.user)
788+
else:
789+
group_mapping.set_secondary_approver(request.user.user)
790+
json_response["msg"] = REQUEST_ACCESS_AUTO_APPROVED_MSG["title"].format(
791+
request_id=request_id
792+
)
793+
794+
userMappingsList = create_members_user_access_mappings(
795+
group_mapping=group_mapping, access_type=access_type
796+
)
797+
798+
group_mapping.approve_access()
799+
execute_group_access(userMappingsList)
800+
logger.debug(
801+
APPROVAL_PROCESS_STARTED_MSG.format(
802+
request_id=request_id, approver=request.user.username
803+
)
804+
)
805+
return json_response
806+
except Exception as e:
807+
logger.exception(e)
808+
destination = [group_mapping.requested_by.email]
809+
notifications.send_accept_group_access_failed(
810+
destination=destination, request_id=request_id, error=str(e)
811+
)
812+
return create_error_response(
813+
error_msg=ERROR_APPROVING_REQUEST_DSP_MSG.format(error=str(e))
814+
)
815+
816+
817+
def decline_group_access(request, request_id, reason):
818+
json_response = {}
819+
820+
group_mapping = GroupAccessMapping.get_by_request_id(request_id=request_id)
821+
if not group_mapping:
822+
logger.error(INVALID_REQUEST_ERROR_MSG)
823+
json_response["error"] = INVALID_REQUEST_ERROR_MSG
824+
return json_response
825+
826+
access_type = group_mapping.access.access_tag
827+
828+
json_response = validate_approver_permissions(
829+
group_mapping, access_type, request, request_id
830+
)
831+
if "error" in json_response:
832+
return json_response
833+
834+
if not is_request_valid(request_id=request_id, access_mapping=group_mapping):
835+
json_response["error"] = USER_REQUEST_IN_PROCESS_ERR_MSG.format(
836+
request_id=request_id
837+
)
838+
logger.warning(
839+
ALREADY_PROCESSED_REQUEST_MSG.format(
840+
request_id=request_id, user=request.user.username
841+
)
842+
)
843+
return json_response
844+
845+
try:
846+
with transaction.atomic():
847+
group_mapping.decline_access(decline_reason=reason)
848+
if group_mapping.get_primary_approver() is not None:
849+
group_mapping.set_secondary_approver(approver=request.user.user)
850+
else:
851+
group_mapping.set_primary_approver(request.user.user)
852+
853+
destination = [group_mapping.requested_by.email]
854+
destination.extend(MAIL_APPROVER_GROUPS)
855+
856+
notifications.send_group_access_declined(
857+
destination=destination,
858+
group_name=group_mapping.group.name,
859+
requester=group_mapping.requested_by.user.username,
860+
decliner=request.user.username,
861+
request_id=request_id,
862+
declined_access=group_mapping.access.access_tag,
863+
reason=reason,
864+
)
865+
logger.debug(
866+
USER_REQUEST_DECLINE_MSG.format(
867+
request_id=request_id, decline_reason=reason
868+
)
869+
)
870+
json_response = {}
871+
json_response["msg"] = REQUEST_DECLINED_MSG.format(request_id=request_id)
872+
return json_response
873+
except Exception as e:
874+
logger.exception(e)
875+
destination = [request.user.email]
876+
notifications.send_decline_group_access_failed(
877+
destination=destination, request_id=request_id, error=str(e)
878+
)
879+
return create_error_response(
880+
error_msg=ERROR_DECLINING_REQUEST_LOG_MSG.format(
881+
request_id=request_id, error=str(str(e))
882+
)
883+
)
884+
885+
886+
def create_error_response(error_msg):
887+
json_response = {}
888+
json_response["error"] = error_msg
889+
return json_response
890+
891+
892+
def is_valid_approver(request, group_mapping, approver_permissions):
893+
is_primary_approver = (
894+
group_mapping.is_pending()
895+
and request.user.user.has_permission(approver_permissions["1"])
896+
)
897+
is_secondary_approver = (
898+
group_mapping.is_secondary_pending()
899+
and request.user.user.has_permission(approver_permissions["2"])
900+
)
901+
return is_primary_approver, is_secondary_approver
902+
903+
904+
def create_members_user_access_mappings(group_mapping, access_type):
905+
user_mappings_list = []
906+
with transaction.atomic():
907+
for membership in group_mapping.group.get_all_approved_members():
908+
user = membership.user
909+
access = group_mapping.access
910+
approver_1 = group_mapping.get_primary_approver()
911+
approver_2 = group_mapping.get_secondary_approver()
912+
reason = (
913+
"Added for group request "
914+
+ group_mapping.request_id
915+
+ " - "
916+
+ group_mapping.request_reason
917+
)
918+
request_id = (
919+
user.user.username
920+
+ "-"
921+
+ group_mapping.access.access_tag
922+
+ "-"
923+
+ group_mapping.request_id.rsplit("-", 1)[-1]
924+
)
925+
if not user.get_accesses_by_access_tag_and_status(
926+
access_tag=access.access_tag, status=["Approved"]
927+
):
928+
existing_mapping = UserAccessMapping.get_access_request(
929+
request_id=request_id
930+
)
931+
if not existing_mapping:
932+
user_identity = user.get_or_create_active_identity(
933+
access_tag=access_type
934+
)
935+
user_mapping = UserAccessMapping.create(
936+
request_id=request_id,
937+
user_identity=user_identity,
938+
access=access,
939+
approver_1=approver_1,
940+
approver_2=approver_2,
941+
request_reason=reason,
942+
access_type="Group",
943+
status="Processing",
944+
)
945+
else:
946+
logger.debug("Regranting " + request_id)
947+
user_mapping = existing_mapping
948+
existing_mapping.set_processing()
949+
950+
user_mappings_list.append(user_mapping)
951+
return user_mappings_list

Access/group_helper.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -511,7 +511,7 @@ def accept_member(request, requestId, shouldRender=True):
511511
context["error"] = REQUEST_NOT_FOUND_ERROR + str(e)
512512
return context
513513
try:
514-
if membership.is_already_processed():
514+
if not membership.is_pending():
515515
logger.warning(
516516
"An Already Approved/Declined/Processing Request was accessed by - "
517517
+ request.user.username

0 commit comments

Comments
 (0)