diff --git a/cla-backend/cla/routes.py b/cla-backend/cla/routes.py index b8ec153b8..8b3effaf3 100755 --- a/cla-backend/cla/routes.py +++ b/cla-backend/cla/routes.py @@ -38,6 +38,14 @@ get_log_middleware ) +# Check if authenticated user (via bearer token) is the same as user_id - if not raise exception permission denied +# LG: comment this out to tunr off this chekc added after LFID is required everywhere in EasyCLA +def check_user_id_is_current(auth_user, user_id): + auth_user_id = cla.controllers.user.get_or_create_user(auth_user).get_user_id() + if str(user_id) != auth_user_id: + cla.log.debug(f'request_individual_signature - auth user UUID {auth_user_id} is not the same as requested signature UUID {str(user_id)}') + raise cla.auth.AuthError('permission denied') + # # Middleware @@ -103,7 +111,10 @@ def get_health(request): # LG: This is ported to golang and no longer used in dev (still used in prod) @hug.get("/user/{user_id}", versions=2) -def get_user(user_id: hug.types.uuid): +def get_user( + auth_user: check_auth, + user_id: hug.types.uuid +): """ GET: /user/{user_id} @@ -118,6 +129,7 @@ def get_user(user_id: hug.types.uuid): # else: # raise auth_err + check_user_id_is_current(auth_user, user_id) return cla.controllers.user.get_user(user_id=user_id) @@ -139,6 +151,7 @@ def get_user_signatures(auth_user: check_auth, user_id: hug.types.uuid): Returns a list of signatures associated with a user. """ + check_user_id_is_current(auth_user, user_id) return cla.controllers.user.get_user_signatures(user_id) @@ -156,6 +169,7 @@ def get_users_company(auth_user: check_auth, user_company_id: hug.types.uuid): # We can't change API URL to be inclusive yet as this would break all consumers and require acs-cli and lfx-gateway updates @hug.post("/user/{user_id}/request-company-whitelist/{company_id}", versions=2) def request_company_allowlist( + auth_user: check_auth, user_id: hug.types.uuid, company_id: hug.types.uuid, user_name: hug.types.text, @@ -173,6 +187,7 @@ def request_company_allowlist( Performs the necessary actions (ie: send email to manager) when the specified user requests to be added the the specified company's allowlist. """ + # check_user_id_is_current(auth_user, user_id) return cla.controllers.user.request_company_allowlist( user_id, str(company_id), str(user_name), str(user_email), str(project_id), message, str(recipient_name), str(recipient_email), @@ -181,6 +196,7 @@ def request_company_allowlist( @hug.post("/user/{user_id}/invite-company-admin", versions=2) def invite_company_admin( + auth_user: check_auth, user_id: hug.types.uuid, contributor_name: hug.types.text, contributor_email: cla.hug_types.email, @@ -203,6 +219,7 @@ def invite_company_admin( Sends an Email to the prospective CLA Manager to sign up through the ccla console. """ + # check_user_id_is_current(auth_user, user_id) return cla.controllers.user.invite_cla_manager( str(user_id), str(contributor_name), str(contributor_email), str(cla_manager_name), str(cla_manager_email), @@ -212,6 +229,7 @@ def invite_company_admin( @hug.post("/user/{user_id}/request-company-ccla", versions=2) def request_company_ccla( + auth_user: check_auth, user_id: hug.types.uuid, user_email: cla.hug_types.email, company_id: hug.types.uuid, project_id: hug.types.uuid, ): @@ -220,6 +238,7 @@ def request_company_ccla( Sends an Email to an admin of an existing company to sign a CCLA. """ + # check_user_id_is_current(auth_user, user_id) return cla.controllers.user.request_company_ccla(str(user_id), str(user_email), str(company_id), str(project_id)) @@ -236,7 +255,10 @@ def request_company_ccla( # LG: This is ported to golang and no longer used in dev (still used in prod) @hug.get("/user/{user_id}/active-signature", versions=2) -def get_user_active_signature(user_id: hug.types.uuid): +def get_user_active_signature( + auth_user: check_auth, + user_id: hug.types.uuid +): """ GET: /user/{user_id}/active-signature @@ -250,21 +272,27 @@ def get_user_active_signature(user_id: hug.types.uuid): Returns null if the user does not have an active signature. """ + check_user_id_is_current(auth_user, user_id) return cla.controllers.user.get_active_signature(user_id) @hug.get("/user/{user_id}/project/{project_id}/last-signature", versions=2) -def get_user_project_last_signature(user_id: hug.types.uuid, project_id: hug.types.uuid): +def get_user_project_last_signature( + auth_user: check_auth, + user_id: hug.types.uuid, project_id: hug.types.uuid +): """ GET: /user/{user_id}/project/{project_id}/last-signature Returns the user's latest ICLA signature for the project specified. """ + check_user_id_is_current(auth_user, user_id) return cla.controllers.user.get_user_project_last_signature(user_id, project_id) @hug.get("/user/{user_id}/project/{project_id}/last-signature/{company_id}", versions=1) def get_user_project_company_last_signature( + auth_user: check_auth, user_id: hug.types.uuid, project_id: hug.types.uuid, company_id: hug.types.uuid ): """ @@ -272,6 +300,7 @@ def get_user_project_company_last_signature( Returns the user's latest employee signature for the project and company specified. """ + check_user_id_is_current(auth_user, user_id) return cla.controllers.user.get_user_project_company_last_signature(user_id, project_id, company_id) @@ -413,6 +442,7 @@ def get_signatures_user(auth_user: check_auth, user_id: hug.types.uuid): Get all signatures for user specified. """ + check_user_id_is_current(auth_user, user_id) return cla.controllers.signature.get_user_signatures(user_id) @@ -423,6 +453,7 @@ def get_signatures_user_project(auth_user: check_auth, user_id: hug.types.uuid, Get all signatures for user, filtered by project_id specified. """ + check_user_id_is_current(auth_user, user_id) return cla.controllers.signature.get_user_project_signatures(user_id, project_id) @@ -438,6 +469,7 @@ def get_signatures_user_project( Get all signatures for user, filtered by project_id and signature type specified. """ + check_user_id_is_current(auth_user, user_id) return cla.controllers.signature.get_user_project_signatures(user_id, project_id, signature_type) @@ -462,7 +494,7 @@ def get_signatures_project(auth_user: check_auth, project_id: hug.types.uuid): @hug.get("/signatures/company/{company_id}/project/{project_id}", versions=1) -def get_signatures_project_company(company_id: hug.types.uuid, project_id: hug.types.uuid): +def get_signatures_project_company(auth_user: check_auth, company_id: hug.types.uuid, project_id: hug.types.uuid): """ GET: /signatures/company/{company_id}/project/{project_id} @@ -472,7 +504,7 @@ def get_signatures_project_company(company_id: hug.types.uuid, project_id: hug.t @hug.get("/signatures/company/{company_id}/project/{project_id}/employee", versions=1) -def get_project_employee_signatures(company_id: hug.types.uuid, project_id: hug.types.uuid): +def get_project_employee_signatures(auth_user: check_auth, company_id: hug.types.uuid, project_id: hug.types.uuid): """ GET: /signatures/company/{company_id}/project/{project_id} @@ -625,7 +657,7 @@ def get_companies(auth_user: check_auth): @hug.get("/company", versions=2) -def get_all_companies(): +def get_all_companies(auth_user: check_auth): """ GET: /company @@ -635,7 +667,7 @@ def get_all_companies(): @hug.get("/company/{company_id}", versions=2) -def get_company(company_id: hug.types.text): +def get_company(auth_user: check_auth, company_id: hug.types.text): """ GET: /company/{company_id} @@ -645,7 +677,7 @@ def get_company(company_id: hug.types.text): @hug.get("/company/{company_id}/project/unsigned", versions=1) -def get_unsigned_projects_for_company(company_id: hug.types.text): +def get_unsigned_projects_for_company(auth_user: check_auth, company_id: hug.types.text): """ GET: /company/{company_id}/project/unsigned @@ -751,7 +783,7 @@ def put_company_allowlist_csv(body, auth_user: check_auth, company_id: hug.types @hug.get("/companies/{manager_id}", version=1) -def get_manager_companies(manager_id: hug.types.uuid): +def get_manager_companies(auth_user: check_auth, manager_id: hug.types.uuid): """ GET: /companies/{manager_id} @@ -780,7 +812,7 @@ def get_projects(auth_user: check_auth): # LG: This is ported to golang and no longer used in dev (still used in prod). @hug.get("/project/{project_id}", versions=2) -def get_project(project_id: hug.types.uuid): +def get_project(auth_user: check_auth, project_id: hug.types.uuid): """ GET: /project/{project_id} @@ -1015,6 +1047,7 @@ def get_project_configuration_orgs_and_repos(auth_user: check_auth, project_id: @hug.get("/project/{project_id}/document/{document_type}", versions=2) def get_project_document( + auth_user: check_auth, project_id: hug.types.uuid, document_type: hug.types.one_of(["individual", "corporate"]), ): """ @@ -1067,7 +1100,7 @@ def get_project_document_matching_version( @hug.get("/project/{project_id}/companies", versions=2) -def get_project_companies(project_id: hug.types.uuid): +def get_project_companies(auth_user: check_auth, project_id: hug.types.uuid): """ GET: /project/{project_id}/companies s @@ -1217,7 +1250,7 @@ def delete_project_document( 'user_id': 'some-user-uuid'}", ) def request_individual_signature( - request, project_id: hug.types.uuid, user_id: hug.types.uuid, return_url_type=None, return_url=None, + auth_user: check_auth, request, project_id: hug.types.uuid, user_id: hug.types.uuid, return_url_type=None, return_url=None, ): """ POST: /request-individual-signature @@ -1240,8 +1273,10 @@ def request_individual_signature( User should hit the provided URL to initiate the signing process through the signing service provider. """ - return cla.controllers.signing.request_individual_signature(project_id, user_id, return_url_type, return_url, - request=request) + check_user_id_is_current(auth_user, user_id) + return cla.controllers.signing.request_individual_signature( + project_id, user_id, return_url_type, return_url, request=request + ) @hug.post( @@ -1287,7 +1322,7 @@ def request_corporate_signature( Returns a dict of the format: - {'company_id': , + {'company_id': , 'signature_id': , 'project_id': , 'sign_url': } @@ -1311,6 +1346,7 @@ def request_corporate_signature( @hug.post("/request-employee-signature", versions=2) def request_employee_signature( + auth_user: check_auth, project_id: hug.types.uuid, company_id: hug.types.uuid, user_id: hug.types.uuid, @@ -1330,6 +1366,7 @@ def request_employee_signature( require a full DocuSign signature process, which means the sign/callback URLs and document versions may not be populated or reliable. """ + check_user_id_is_current(auth_user, user_id) return cla.controllers.signing.request_employee_signature( project_id, company_id, user_id, return_url_type, return_url ) @@ -1337,6 +1374,7 @@ def request_employee_signature( @hug.post("/check-prepare-employee-signature", versions=2) def check_and_prepare_employee_signature( + auth_user: check_auth, project_id: hug.types.uuid, company_id: hug.types.uuid, user_id: hug.types.uuid ): """ @@ -1349,6 +1387,7 @@ def check_and_prepare_employee_signature( Checks if an employee is ready to sign a CCLA for a company. """ + check_user_id_is_current(auth_user, user_id) return cla.controllers.signing.check_and_prepare_employee_signature(project_id, company_id, user_id) @@ -1390,6 +1429,7 @@ def post_individual_signed_gitlab( Callback URL from signing service upon ICLA signature for a Gitlab user. """ content = request.bounded_stream.read() + # check_user_id_is_current(auth_user, user_id) return cla.controllers.signing.post_individual_signed_gitlab( content, user_id, organization_id, gitlab_repository_id, merge_request_id ) @@ -1403,6 +1443,7 @@ def post_individual_signed_gerrit(request, user_id: hug.types.uuid): Callback URL from signing service upon ICLA signature for a Gerrit user. """ content = request.bounded_stream.read() + # check_user_id_is_current(auth_user, user_id) return cla.controllers.signing.post_individual_signed_gerrit(content, user_id) diff --git a/utils/request_corporate_signature_py_post.sh b/utils/request_corporate_signature_py_post.sh index ad7c1092a..561ea8c4e 100755 --- a/utils/request_corporate_signature_py_post.sh +++ b/utils/request_corporate_signature_py_post.sh @@ -4,10 +4,13 @@ # project_id='88ee12de-122b-4c46-9046-19422054ed8d' # return_url_type='github' # return_url='http://localhost' +# TOKEN='...' - Auth0 JWT bearer token +# XACL='...' - X-ACL # DEBUG=1 ./utils/request_corporate_signature_py_post.sh 862ff296-6508-4f10-9147-2bc2dd7bfe80 88ee12de-122b-4c46-9046-19422054ed8d github 'http://localhost' # ./utils/request_corporate_signature_py_post.sh 0ca30016-6457-466c-bc41-a09560c1f9bf 88ee12de-122b-4c46-9046-19422054ed8d github 'http://localhost' # ./utils/request_corporate_signature_py_post.sh 10bde6b1-3061-4972-9c6a-17dd9a175a5c 88ee12de-122b-4c46-9046-19422054ed8d github 'http://localhost' # Note: this is only for internal usage, it requires 'check_auth' function update in cla-backend/cla/routes.py (see LG:) and can only be tested locally (LG:) +# Note: you can run it in a similar way to utils/get_user_from_token_py.sh if [ -z "$1" ] then @@ -37,6 +40,29 @@ then fi export return_url="$4" +if [ -z "$TOKEN" ] +then + # source ./auth0_token.secret + TOKEN="$(cat ./auth0.token.secret)" +fi + +if [ -z "$TOKEN" ] +then + echo "$0: TOKEN not specified and unable to obtain one" + exit 5 +fi + +if [ -z "$XACL" ] +then + XACL="$(cat ./x-acl.secret)" +fi + +if [ -z "$XACL" ] +then + echo "$0: XACL not specified and unable to obtain one" + exit 6 +fi + if [ -z "$API_URL" ] then export API_URL="http://localhost:5000" @@ -44,6 +70,6 @@ fi if [ ! -z "$DEBUG" ] then - echo "curl -s -XPOST -H 'Content-Type: application/json' '${API_URL}/v1/request-corporate-signature' -d '{\"project_id\":\"${project_id}\",\"company_id\":\"${company_id}\",\"return_url_type\":\"${return_url_type}\",\"return_url\":\"${return_url}\"}' | jq -r '.'" + echo "curl -s -XPOST -H 'X-ACL: ${XACL}' -H 'Authorization: Bearer ${TOKEN}' -H 'Content-Type: application/json' '${API_URL}/v1/request-corporate-signature' -d '{\"project_id\":\"${project_id}\",\"company_id\":\"${company_id}\",\"return_url_type\":\"${return_url_type}\",\"return_url\":\"${return_url}\"}' | jq -r '.'" fi -curl -s -XPOST -H "Content-Type: application/json" "${API_URL}/v1/request-corporate-signature" -d "{\"project_id\":\"${project_id}\",\"company_id\":\"${company_id}\",\"return_url_type\":\"${return_url_type}\",\"return_url\":\"${return_url}\"}" | jq -r '.' +curl -s -XPOST -H "X-ACL: ${XACL}" -H "Authorization: Bearer ${TOKEN}" -H "Content-Type: application/json" "${API_URL}/v1/request-corporate-signature" -d "{\"project_id\":\"${project_id}\",\"company_id\":\"${company_id}\",\"return_url_type\":\"${return_url_type}\",\"return_url\":\"${return_url}\"}" | jq -r '.' diff --git a/utils/request_employee_signature_py_post.sh b/utils/request_employee_signature_py_post.sh index d07ffc138..ec0fc54c6 100755 --- a/utils/request_employee_signature_py_post.sh +++ b/utils/request_employee_signature_py_post.sh @@ -5,7 +5,10 @@ # project_id='88ee12de-122b-4c46-9046-19422054ed8d' # return_url_type='github' # return_url='http://localhost' +# TOKEN='...' - Auth0 JWT bearer token +# XACL='...' - X-ACL # DEBUG=1 ./utils/request_employee_signature_py_post.sh 9dcf5bbc-2492-11ed-97c7-3e2a23ea20b5 862ff296-6508-4f10-9147-2bc2dd7bfe80 88ee12de-122b-4c46-9046-19422054ed8d github 'http://localhost' +# DEBUG=1 TOKEN='...' ./utils/request_employee_signature_py_post.sh 6e1fd921-e850-11ef-b5df-92cef1e60fc3 862ff296-6508-4f10-9147-2bc2dd7bfe80 88ee12de-122b-4c46-9046-19422054ed8d github 'http://localhost' if [ -z "$1" ] then @@ -42,6 +45,29 @@ then fi export return_url="$5" +if [ -z "$TOKEN" ] +then + # source ./auth0_token.secret + TOKEN="$(cat ./auth0.token.secret)" +fi + +if [ -z "$TOKEN" ] +then + echo "$0: TOKEN not specified and unable to obtain one" + exit 6 +fi + +if [ -z "$XACL" ] +then + XACL="$(cat ./x-acl.secret)" +fi + +if [ -z "$XACL" ] +then + echo "$0: XACL not specified and unable to obtain one" + exit 7 +fi + if [ -z "$API_URL" ] then export API_URL="http://localhost:5000" @@ -49,6 +75,6 @@ fi if [ ! -z "$DEBUG" ] then - echo "curl -s -XPOST -H 'Content-Type: application/json' '${API_URL}/v2/request-employee-signature' -d '{\"project_id\":\"${project_id}\",\"user_id\":\"${user_id}\",\"company_id\":\"${company_id}\",\"return_url_type\":\"${return_url_type}\",\"return_url\":\"${return_url}\"}' | jq -r '.'" + echo "curl -s -XPOST -H 'X-ACL: ${XACL}' -H 'Authorization: Bearer ${TOKEN}' -H 'Content-Type: application/json' '${API_URL}/v2/request-employee-signature' -d '{\"project_id\":\"${project_id}\",\"user_id\":\"${user_id}\",\"company_id\":\"${company_id}\",\"return_url_type\":\"${return_url_type}\",\"return_url\":\"${return_url}\"}' | jq -r '.'" fi -curl -s -XPOST -H "Content-Type: application/json" "${API_URL}/v2/request-employee-signature" -d "{\"project_id\":\"${project_id}\",\"user_id\":\"${user_id}\",\"company_id\":\"${company_id}\",\"return_url_type\":\"${return_url_type}\",\"return_url\":\"${return_url}\"}" | jq -r '.' +curl -s -XPOST -H "X-ACL: ${XACL}" -H "Authorization: Bearer ${TOKEN}" -H "Content-Type: application/json" "${API_URL}/v2/request-employee-signature" -d "{\"project_id\":\"${project_id}\",\"user_id\":\"${user_id}\",\"company_id\":\"${company_id}\",\"return_url_type\":\"${return_url_type}\",\"return_url\":\"${return_url}\"}" | jq -r '.' diff --git a/utils/request_individual_signature_py_post.sh b/utils/request_individual_signature_py_post.sh index 024ba16cc..96af5eb15 100755 --- a/utils/request_individual_signature_py_post.sh +++ b/utils/request_individual_signature_py_post.sh @@ -4,7 +4,10 @@ # project_id='88ee12de-122b-4c46-9046-19422054ed8d' # return_url_type='github' # return_url='http://localhost' +# TOKEN='...' - Auth0 JWT bearer token +# XACL='...' - X-ACL header # DEBUG=1 ./utils/request_individual_signature_py_post.sh 9dcf5bbc-2492-11ed-97c7-3e2a23ea20b5 88ee12de-122b-4c46-9046-19422054ed8d github 'http://localhost' +# DEBUG=1 TOKEN='...' ./utils/request_individual_signature_py_post.sh 6e1fd921-e850-11ef-b5df-92cef1e60fc3 88ee12de-122b-4c46-9046-19422054ed8d github 'http://localhost' if [ -z "$1" ] then @@ -39,10 +42,33 @@ then export API_URL="http://localhost:5000" fi +if [ -z "$TOKEN" ] +then + # source ./auth0_token.secret + TOKEN="$(cat ./auth0.token.secret)" +fi + +if [ -z "$TOKEN" ] +then + echo "$0: TOKEN not specified and unable to obtain one" + exit 5 +fi + +if [ -z "$XACL" ] +then + XACL="$(cat ./x-acl.secret)" +fi + +if [ -z "$XACL" ] +then + echo "$0: XACL not specified and unable to obtain one" + exit 6 +fi + if [ ! -z "$DEBUG" ] then - echo "curl -s -XPOST -H 'Content-Type: application/json' '${API_URL}/v2/request-individual-signature' -d '{\"project_id\":\"${project_id}\",\"user_id\":\"${user_id}\",\"return_url_type\":\"${return_url_type}\",\"return_url\":\"${return_url}\"}'" - curl -s -XPOST -H "Content-Type: application/json" "${API_URL}/v2/request-individual-signature" -d "{\"project_id\":\"${project_id}\",\"user_id\":\"${user_id}\",\"return_url_type\":\"${return_url_type}\",\"return_url\":\"${return_url}\"}" + echo "curl -s -XPOST -H 'X-ACL: ${XACL}' -H 'Authorization: Bearer ${TOKEN}' -H 'Content-Type: application/json' '${API_URL}/v2/request-individual-signature' -d '{\"project_id\":\"${project_id}\",\"user_id\":\"${user_id}\",\"return_url_type\":\"${return_url_type}\",\"return_url\":\"${return_url}\"}'" + curl -s -XPOST -H "X-ACL: ${XACL}" -H "Authorization: Bearer ${TOKEN}" -H "Content-Type: application/json" "${API_URL}/v2/request-individual-signature" -d "{\"project_id\":\"${project_id}\",\"user_id\":\"${user_id}\",\"return_url_type\":\"${return_url_type}\",\"return_url\":\"${return_url}\"}" else - curl -s -XPOST -H "Content-Type: application/json" "${API_URL}/v2/request-individual-signature" -d "{\"project_id\":\"${project_id}\",\"user_id\":\"${user_id}\",\"return_url_type\":\"${return_url_type}\",\"return_url\":\"${return_url}\"}" | jq -r '.' + curl -s -XPOST -H "X-ACL: ${XACL}" -H "Authorization: Bearer ${TOKEN}" -H "Content-Type: application/json" "${API_URL}/v2/request-individual-signature" -d "{\"project_id\":\"${project_id}\",\"user_id\":\"${user_id}\",\"return_url_type\":\"${return_url_type}\",\"return_url\":\"${return_url}\"}" | jq -r '.' fi