@@ -632,64 +632,74 @@ def is_request_valid(request_id, access_mapping):
632632def accept_user_access_requests (auth_user , request_id ):
633633 """ Grant for individual user access requests """
634634 json_response = {}
635- access_mapping = UserAccessMapping .get_access_request (request_id )
636- if not is_request_valid (request_id , access_mapping ):
637- json_response ["error" ] = USER_REQUEST_IN_PROCESS_ERR_MSG .format (
638- request_id = request_id ,
639- )
640- return json_response
641-
642- requester = access_mapping .user_identity .user
643- if auth_user .user == requester :
644- json_response ["error" ] = SELF_APPROVAL_ERROR_MSG
645- return json_response
635+ try :
636+ with transaction .atomic ():
637+ # F-027: lock the mapping row so the validity/approver-state check and
638+ # the processing() status write are atomic. Without the lock two
639+ # concurrent approvers can both pass the check and double-dispatch the
640+ # grant task. The Celery dispatch inside run_accept_request_task now
641+ # only fires for the single request that wins the lock.
642+ access_mapping = (
643+ UserAccessMapping .objects .select_for_update ()
644+ .filter (request_id = request_id )
645+ .first ()
646+ )
647+ if not is_request_valid (request_id , access_mapping ):
648+ json_response ["error" ] = USER_REQUEST_IN_PROCESS_ERR_MSG .format (
649+ request_id = request_id ,
650+ )
651+ return json_response
646652
647- access_label = access_mapping .access .access_label
653+ requester = access_mapping .user_identity .user
654+ if auth_user .user == requester :
655+ json_response ["error" ] = SELF_APPROVAL_ERROR_MSG
656+ return json_response
648657
649- try :
650- permissions = _get_approver_permissions (
651- access_mapping .access .access_tag , access_label
652- )
653- approver_permissions = permissions ["approver_permissions" ]
654- if not helpers .check_user_permissions (
655- auth_user , list (approver_permissions .values ())
656- ):
657- logger .debug (USER_REQUEST_PERMISSION_DENIED_ERR_MSG )
658- json_response ["error" ] = USER_REQUEST_PERMISSION_DENIED_ERR_MSG
659- return json_response
658+ access_label = access_mapping .access .access_label
660659
661- is_primary_approver = (
662- access_mapping .is_pending ()
663- and auth_user .user .has_permission (approver_permissions ["1" ])
664- )
665- is_secondary_approver = (
666- access_mapping .is_secondary_pending ()
667- and auth_user .user .has_permission (approver_permissions ["2" ])
668- )
660+ permissions = _get_approver_permissions (
661+ access_mapping .access .access_tag , access_label
662+ )
663+ approver_permissions = permissions ["approver_permissions" ]
664+ if not helpers .check_user_permissions (
665+ auth_user , list (approver_permissions .values ())
666+ ):
667+ logger .debug (USER_REQUEST_PERMISSION_DENIED_ERR_MSG )
668+ json_response ["error" ] = USER_REQUEST_PERMISSION_DENIED_ERR_MSG
669+ return json_response
669670
670- if not (is_primary_approver or is_secondary_approver ):
671- logger .debug (USER_REQUEST_PERMISSION_DENIED_ERR_MSG )
672- json_response ["error" ] = USER_REQUEST_PERMISSION_DENIED_ERR_MSG
673- return json_response
674- if is_primary_approver and "2" in approver_permissions :
675- access_mapping .approver_1 = auth_user .user
676- access_mapping .update_access_status ("SecondaryPending" )
677- json_response ["msg" ] = USER_REQUEST_SECONDARY_PENDING_MSG .format (
678- request_id = request_id , approved_by = auth_user .username
671+ is_primary_approver = (
672+ access_mapping .is_pending ()
673+ and auth_user .user .has_permission (approver_permissions ["1" ])
679674 )
680- logger .debug (
681- USER_REQUEST_SECONDARY_PENDING_MSG .format (
675+ is_secondary_approver = (
676+ access_mapping .is_secondary_pending ()
677+ and auth_user .user .has_permission (approver_permissions ["2" ])
678+ )
679+
680+ if not (is_primary_approver or is_secondary_approver ):
681+ logger .debug (USER_REQUEST_PERMISSION_DENIED_ERR_MSG )
682+ json_response ["error" ] = USER_REQUEST_PERMISSION_DENIED_ERR_MSG
683+ return json_response
684+ if is_primary_approver and "2" in approver_permissions :
685+ access_mapping .approver_1 = auth_user .user
686+ access_mapping .update_access_status ("SecondaryPending" )
687+ json_response ["msg" ] = USER_REQUEST_SECONDARY_PENDING_MSG .format (
682688 request_id = request_id , approved_by = auth_user .username
683689 )
684- )
685- else :
686- json_response = run_accept_request_task (
687- is_primary_approver ,
688- access_mapping ,
689- auth_user = auth_user ,
690- request_id = request_id ,
691- access_label = access_label ,
692- )
690+ logger .debug (
691+ USER_REQUEST_SECONDARY_PENDING_MSG .format (
692+ request_id = request_id , approved_by = auth_user .username
693+ )
694+ )
695+ else :
696+ json_response = run_accept_request_task (
697+ is_primary_approver ,
698+ access_mapping ,
699+ auth_user = auth_user ,
700+ request_id = request_id ,
701+ access_label = access_label ,
702+ )
693703 except Exception as exception :
694704 return process_error_response (exception )
695705
0 commit comments