Fix/eng 11829 2 - #11885
Conversation
292149b to
4c48222
Compare
| sentry.log_message( | ||
| f'CAS response ORCID attributes: user=[{user._id}], orcidId=[{orcid_id}], ' | ||
| f'orcidAccessToken=[{"present" if access_token else "missing"}]', | ||
| level=logging.INFO, | ||
| ) |
There was a problem hiding this comment.
Only log message to sentry if access token or refresh token are missing, level is warning
| # }, | ||
| # ... | ||
| # } | ||
| external_identity_access_token = DateTimeAwareJSONField(default=dict, blank=True) |
There was a problem hiding this comment.
external_identity_tokens
| access_token = cas_resp.attributes.get('orcidAccessToken', None) | ||
| refresh_token = cas_resp.attributes.get('orcidRefreshToken', None) |
There was a problem hiding this comment.
Move into if external_credential:
| external_credential['id'], | ||
| access_token, | ||
| refresh_token, | ||
| ) |
| f'found {len(orcid_tokens)} ORCID token(s) to revoke', | ||
| level=logging.INFO, | ||
| ) | ||
| for orcid_id, orcid_token in orcid_tokens.items(): |
There was a problem hiding this comment.
We need to check if there is an external_identity but there is no matching external_identity_tokens, we need to fail the GDPR delete with relevant error messsage.
| 'client_secret': website_settings.ORCID_OAUTH_CLIENT_SECRET, | ||
| 'token': orcid_token, | ||
| }, | ||
| timeout=5, |
There was a problem hiding this comment.
Future, we probably want a retry just in case
| ) | ||
| sentry.log_message( | ||
| f'[GDPR delete] user={self._id}: ORCID id={orcid_id} revoked, ' | ||
| f'status_code={response.status_code}, response_text={response.text}, response={response}', |
There was a problem hiding this comment.
we probably don't need the full response
| f'status_code={response.status_code}, response_text={response.text}, response={response}', | ||
| level=logging.INFO, | ||
| ) | ||
| response.raise_for_status() |
There was a problem hiding this comment.
Add unit tests for success, invalid client_id/secret, invalid token, empty token
| response.raise_for_status() | ||
| except requests.exceptions.RequestException as e: | ||
| sentry.log_message( | ||
| f'[GDPR delete] Failed to revoke ORCID token for user {self._id}: {e}', |
There was a problem hiding this comment.
Add the orcid id just in case user has mulitple
| 'Unable to revoke this user\'s ORCID access right now because ORCID\'s ' | ||
| 'service could not be reached. Please try the GDPR delete again later.' |
There was a problem hiding this comment.
rephrase to a more general message like fail to revoke
f757b97 to
eebddd6
Compare
| provider = settings.EXTERNAL_IDENTITY_PROFILE['OrcidProfile'] | ||
| user.external_identity_access_token.setdefault(provider, {})[orcid_id] = { | ||
| 'access_token': access_token, | ||
| 'refresh_token': refresh_token, |
There was a problem hiding this comment.
Remove refresh_token due to CAS limit
| else: | ||
| raise UserStateError( | ||
| 'User do not have connected ORCID' | ||
| ) |
There was a problem hiding this comment.
Move this to if statement first,
if failure
raise/return
the rest for success
| self.external_accounts.clear() | ||
|
|
||
| self.external_identity = {} | ||
| self.external_identity_access_token = {} |
| SPAM_SUBMIT_TASK_HARD_TIME_LIMIT = 90 | ||
|
|
||
| CAS_SERVER_URL = 'http://localhost:8080' | ||
| CAS_ORCID_REVOKE_SHARED_SECRET = os.environ.get('CAS_ORCID_REVOKE_SHARED_SECRET', 'changeme') |
cslzchen
left a comment
There was a problem hiding this comment.
3rd pass done 🌟
This is the main PR now, please update ticket title.
| # <external_id>: { | ||
| # "access_token" : <token>, | ||
| # } |
There was a problem hiding this comment.
I was able to WAR-Overlay the apereo & pac4j library (see here) to include refresh token. Please add the refresh token back.
| # A user has at most one ORCID identity, so there is at most one token entry to revoke. | ||
| orcid_id, token_entry = next(iter(self.external_identity_tokens.get('ORCID', {}).items()), (None, None)) |
There was a problem hiding this comment.
Please write a small python code snippet for CE to run to check if we have any users with more than one ORCiD ID (via cloud request).
| ''' | ||
| # A user has at most one ORCID identity, so there is at most one token entry to revoke. | ||
| orcid_id, token_entry = next(iter(self.external_identity_tokens.get('ORCID', {}).items()), (None, None)) | ||
| orcid_token = token_entry.get('access_token') if token_entry else None |
There was a problem hiding this comment.
Similarly, add refresh_token back and make it optional (just in case some user's privacy settings in ORCiD doesn't release refresh token to us.
| raise UserStateError( | ||
| 'User do not have connected ORCID' | ||
| ) | ||
| else: |
There was a problem hiding this comment.
No need for else since error would have been raised
| import osf.utils.fields | ||
|
|
||
|
|
||
| class Migration(migrations.Migration): |
There was a problem hiding this comment.
Rename to 0053_gdpr_delete_and_orcid_revoke
| sentry.log_message( | ||
| f'CAS response ORCID attributes: user=[{user._id}], orcidId=[{orcid_id}], ' | ||
| f'orcidAccessToken=[{"present" if access_token else "missing"}]', | ||
| level=logging.WARNING, | ||
| ) |
There was a problem hiding this comment.
Future (new ticket), change this to debug log and only log empty access token error after we dev tested on staging1.
| 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"}]', | ||
| level=logging.INFO, | ||
| ) |
| # 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) |
There was a problem hiding this comment.
Similarly, let's get refresh token as optional.
| user = { | ||
| 'external_id_provider': external_credential['provider'], | ||
| 'external_id': external_credential['id'], | ||
| 'external_id_access_token': cas_resp.attributes.get('orcidAccessToken', None), |
| SPAM_SUBMIT_TASK_HARD_TIME_LIMIT = 90 | ||
|
|
||
| CAS_SERVER_URL = 'http://localhost:8080' | ||
| CAS_ORCID_REVOKE_SHARED_SECRET = os.environ.get('CAS_ORCID_REVOKE_SHARED_SECRET', 'changeme') |
There was a problem hiding this comment.
Why do we still need this one?
Ticket
Purpose
Changes
Side Effects
QE Notes
CE Notes
Documentation