-
Notifications
You must be signed in to change notification settings - Fork 362
[ENG-11829] Revoke ORCID trusted party access during GDPR delete - Part 1 #11885
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
29113fa
3b552de
e93ee76
22cd13d
667cca7
4c47f42
c7b653f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -8,6 +8,9 @@ | |
| from lxml import etree | ||
| import requests | ||
|
|
||
| import logging | ||
|
|
||
| from framework import sentry | ||
| from framework.auth import authenticate, external_first_login_authenticate | ||
| from framework.auth.core import get_user, generate_verification_key | ||
| from framework.auth.utils import print_cas_log, LogLevel | ||
|
|
@@ -253,6 +256,26 @@ def get_profile_url(): | |
|
|
||
| return get_client().get_profile_url() | ||
|
|
||
| def save_orcid_access_token_to_user(user, orcid_id: str, access_token: str, refresh_token: str = None): | ||
| sentry.log_message( | ||
| f'CAS response ORCID attributes: user=[{user._id}], orcidId=[{orcid_id}], ' | ||
| f'orcidAccessToken=[{"present" if access_token else "missing"}], ' | ||
| f'orcidRefreshToken=[{"present" if refresh_token else "missing"}]', | ||
| level=logging.WARNING, | ||
| ) | ||
|
Vlad0n20 marked this conversation as resolved.
Comment on lines
+260
to
+265
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Future (new ticket), change this to debug log and only log empty access token error after we dev tested on staging1. |
||
| if orcid_id and access_token: | ||
| provider = settings.EXTERNAL_IDENTITY_PROFILE['OrcidProfile'] | ||
| token_entry = {'access_token': access_token} | ||
| # Refresh token is optional: not all providers/users release one, depending on ORCID privacy settings. | ||
| if refresh_token: | ||
| token_entry['refresh_token'] = refresh_token | ||
| user.external_identity_tokens.setdefault(provider, {})[orcid_id] = token_entry | ||
| sentry.log_message( | ||
| f'ORCID token stored on external_identity_tokens: user=[{user._id}], ' | ||
| f'provider_id=[{orcid_id}], access_token=[{"present" if access_token else "missing"}], ' | ||
| f'refresh_token=[{"present" if refresh_token else "missing"}]', | ||
| level=logging.INFO, | ||
| ) | ||
|
Comment on lines
+273
to
+278
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ditto |
||
|
|
||
| def make_response_from_ticket(ticket, service_url): | ||
| """ | ||
|
|
@@ -297,7 +320,15 @@ def make_response_from_ticket(ticket, service_url): | |
| # this extra step will guarantee that 2FA are enforced | ||
| # current CAS session created by external login must be cleared first before authentication | ||
| if external_credential: | ||
| access_token = cas_resp.attributes.get('orcidAccessToken', None) | ||
|
cslzchen marked this conversation as resolved.
|
||
| refresh_token = cas_resp.attributes.get('orcidRefreshToken', None) | ||
| user.verification_key = generate_verification_key() | ||
| save_orcid_access_token_to_user( | ||
| user, | ||
| external_credential['id'], | ||
| access_token, | ||
| refresh_token, | ||
| ) | ||
| user.save() | ||
| print_cas_log( | ||
| f'CAS response - redirect existing external IdP login to verification key login: user=[{user._id}]', | ||
|
|
@@ -325,6 +356,8 @@ def make_response_from_ticket(ticket, service_url): | |
| user = { | ||
| 'external_id_provider': external_credential['provider'], | ||
| 'external_id': external_credential['id'], | ||
| 'external_id_access_token': cas_resp.attributes.get('orcidAccessToken', None), | ||
|
cslzchen marked this conversation as resolved.
|
||
| 'external_id_refresh_token': cas_resp.attributes.get('orcidRefreshToken', None), | ||
| 'fullname': fullname, | ||
| 'service_url': service_furl.url, | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -1050,6 +1050,8 @@ def external_login_email_post(): | |
|
|
||
| external_id_provider = session.get('auth_user_external_id_provider', None) | ||
| external_id = session.get('auth_user_external_id', None) | ||
| external_id_access_token = session.get('auth_user_external_id_access_token', None) | ||
| external_id_refresh_token = session.get('auth_user_external_id_refresh_token', None) | ||
|
Comment on lines
+1053
to
+1054
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ditto, out of scope but it's ok |
||
| fullname = session.get('auth_user_fullname', None) or form.name.data | ||
| service_url = session.get('service_url', None) | ||
|
|
||
|
|
@@ -1103,6 +1105,7 @@ def external_login_email_post(): | |
| user.accepted_terms_of_service = timezone.now() | ||
| # 2. add unconfirmed email and send confirmation email | ||
| user.add_unconfirmed_email(clean_email, external_identity=external_identity) | ||
| cas.save_orcid_access_token_to_user(user, external_id, external_id_access_token, external_id_refresh_token) | ||
|
Vlad0n20 marked this conversation as resolved.
|
||
| user.save() | ||
| send_confirm_email_async( | ||
| user, | ||
|
|
@@ -1131,6 +1134,7 @@ def external_login_email_post(): | |
| campaign=None, | ||
| accepted_terms_of_service=accepted_terms_of_service | ||
| ) | ||
| cas.save_orcid_access_token_to_user(user, external_id, external_id_access_token, external_id_refresh_token) | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ditto |
||
| user.save() | ||
| # 3. send confirmation email | ||
| send_confirm_email_async( | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| from django.db import migrations | ||
| import osf.utils.datetime_aware_jsonfield | ||
| import osf.utils.fields | ||
|
|
||
|
|
||
| class Migration(migrations.Migration): | ||
|
cslzchen marked this conversation as resolved.
|
||
|
|
||
| dependencies = [ | ||
| ('osf', '0052_downloadevent_download_channel'), | ||
| ] | ||
|
|
||
| operations = [ | ||
| migrations.AddField( | ||
| model_name='osfuser', | ||
| name='external_identity_tokens', | ||
| field=osf.utils.datetime_aware_jsonfield.DateTimeAwareJSONField(blank=True, default=dict, encoder=osf.utils.datetime_aware_jsonfield.DateTimeAwareJSONEncoder), | ||
| ), | ||
| ] | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Out of the scope of PR but is fine to have it here.