Skip to content

Commit

Permalink
Merge branch 'feature/django_upgrade' of https://github.com/CenterFor…
Browse files Browse the repository at this point in the history
…OpenScience/osf.io into django-3-2

* 'feature/django_upgrade' of https://github.com/CenterForOpenScience/osf.io:
  Re-add non-anonymized fields removed in CenterForOpenScience#10009 (CenterForOpenScience#10022)
  Update shield logo for colorado (UC Boulder)
  Update description for maglab
  [ENG-3249] Improve registration anonymization (CenterForOpenScience#10009)
  "backport" artifact changes and swap schema_response.justification (CenterForOpenScience#10003)
  Add new instn purdue
  Ensure BitBucket token is string, not bytes
  Only redirect to cas if not logged in
  OSFI: Update Shared SSO and Add MagLab/FSU [ENG-3654]
  • Loading branch information
John Tordoff committed Aug 16, 2022
2 parents 9449c15 + 75ffbb0 commit e0aad31
Show file tree
Hide file tree
Showing 20 changed files with 894 additions and 263 deletions.
4 changes: 2 additions & 2 deletions addons/bitbucket/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@
from addons.bitbucket import settings

from framework.exceptions import HTTPError

from osf.utils.fields import ensure_str
from website.util.client import BaseClient


class BitbucketClient(BaseClient):

def __init__(self, access_token=None):
self.access_token = access_token
self.access_token = ensure_str(access_token)

@property
def _default_headers(self):
Expand Down
106 changes: 53 additions & 53 deletions api/institutions/authentication.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,46 +17,38 @@

from osf import features
from osf.models import Institution
from osf.models.institution import SharedSsoAffiliationFilterCriteriaAction

from website.mails import send_mail, WELCOME_OSF4I
from website.settings import OSF_SUPPORT_EMAIL, DOMAIN

logger = logging.getLogger(__name__)

# This map defines how to find the secondary institution IdP which uses the shared SSO of a primary
# IdP. Each map entry has the following format.
# This map defines how to find the secondary institution which uses SSO of a primary one. Each map
# entry has the following format.
#
# '<ID of the primary institution A>': {
# 'criteria': 'attribute',
# 'attribute': '<the attribute name for identifying secondary institutions>',
# 'institutions': {
# '<attribute value for identifying institution A1>': '<ID of secondary institution A1>',
# '<attribute value for identifying institution A2>': '<ID of secondary institution A2>',
# ...
# },
# ...
# }
#
# Currently, the only active criteria is "attribute", which the primary institution IdP releases to
# OSF for us to identify the secondary institution. Another option is "emailDomain". For example:
#
# '<ID of the primary institution B>': {
# 'criteria': 'emailDomain',
# 'institutions': {
# '<the email domain for identifying institution B1>': '<ID of secondary institution B1',
# '<the email domain for identifying institution B2>': '<ID of secondary institution B2',
# ...
# }
# ...
# 'attribute_name': '<the attribute name for identifying secondary institutions>',
# 'criteria_action': '<the action to perform between the attribute value and criteria value',
# 'criteria_value': '<the value that>
# 'institution_id': 'the ID of the secondary institution',
# }
# For now, this map is temporarily defined here but will be moved to settings or be re-implemented
# in model via relationships later. In addition, we should be able to make the attribute name fixed
# since CAS can normalize them into "sharedSsoFilter" ahead of time.
#
INSTITUTION_SHARED_SSO_MAP = {
'brown': {
'criteria': 'attribute',
'attribute': 'isMemberOf',
'institutions': {
'thepolicylab': 'thepolicylab',
},
'attribute_name': 'isMemberOf',
'criteria_action': SharedSsoAffiliationFilterCriteriaAction.EQUALS_TO.value,
'criteria_value': 'thepolicylab',
'institution_id': 'thepolicylab',
},
'fsu': {
'attribute_name': 'userRoles',
'criteria_action': SharedSsoAffiliationFilterCriteriaAction.CONTAINS.value,
'criteria_value': 'FSU_OSF_MAGLAB',
'institution_id': 'nationalmaglab',
},
}

Expand Down Expand Up @@ -158,34 +150,42 @@ def authenticate(self, request):
secondary_institution = None
if provider['id'] in INSTITUTION_SHARED_SSO_MAP:
switch_map = INSTITUTION_SHARED_SSO_MAP[provider['id']]
criteria_type = switch_map.get('criteria')
if criteria_type == 'attribute':
attribute_name = switch_map.get('attribute')
attribute_value = provider['user'].get(attribute_name)
if attribute_value:
secondary_institution_id = switch_map.get(
'institutions',
{},
).get(attribute_value)
logger.info('Institution SSO: primary=[{}], secondary=[{}], '
'username=[{}]'.format(provider['id'], secondary_institution_id, username))
secondary_institution = Institution.load(secondary_institution_id)
if not secondary_institution:
# Log errors and inform Sentry but do not raise an exception if OSF fails
# to load the secondary institution from database
message = 'Institution SSO Error: invalid secondary institution [{}]; ' \
'primary=[{}], username=[{}]'.format(attribute_value, provider['id'], username)
logger.error(message)
sentry.log_message(message)
else:
# SSO from primary institution only
logger.info('Institution SSO: primary=[{}], secondary=[None], '
'username=[{}]'.format(provider['id'], username))
attribute_name = switch_map.get('attribute_name')
criteria_action = switch_map.get('criteria_action')
criteria_value = switch_map.get('criteria_value')
attribute_value = provider['user'].get(attribute_name)
# Check affiliation filter criteria and retrieve the secondary institution ID
secondary_institution_id = None
if criteria_action == SharedSsoAffiliationFilterCriteriaAction.EQUALS_TO.value:
secondary_institution_id = switch_map.get('institution_id') if criteria_value == attribute_value else None
elif criteria_action == SharedSsoAffiliationFilterCriteriaAction.CONTAINS.value:
secondary_institution_id = switch_map.get('institution_id') if criteria_value in attribute_value else None
else:
message = 'Institution SSO Error: invalid criteria [{}]; ' \
'primary=[{}], username=[{}]'.format(criteria_type, provider['id'], username)
message = 'Institution Shared SSO Error: invalid affiliation filter criteria action [{}]; ' \
'primary=[{}], username=[{}]'.format(criteria_action, provider['id'], username)
logger.error(message)
sentry.log_message(message)
# Attempt to load the secondary institution by ID
if secondary_institution_id:
logger.info(
'Institution Shared SSO Eligible: primary=[{}], secondary=[{}], '
'filter=[{}: {} {} {}], username=[{}]'.format(
provider['id'], secondary_institution_id, attribute_name,
attribute_value, criteria_action, criteria_value, username,
),
)
secondary_institution = Institution.load(secondary_institution_id)
if not secondary_institution:
# Log errors and inform Sentry but do not raise an exception if OSF fails
# to load the secondary institution from database
message = 'Institution Shared SSO Warning: invalid secondary institution [{}], primary=[{}], ' \
'username=[{}]'.format(secondary_institution_id, provider['id'], username)
logger.error(message)
sentry.log_message(message)
else:
# SSO from primary institution only
logger.info('Institution SSO: primary=[{}], secondary=[None], '
'username=[{}]'.format(provider['id'], username))

# Use given name and family name to build full name if it is not provided
if given_name and family_name and not fullname:
Expand Down
Loading

0 comments on commit e0aad31

Please sign in to comment.