diff --git a/addons/bitbucket/api.py b/addons/bitbucket/api.py index 92d3bbd690e0..e7176b6395b2 100644 --- a/addons/bitbucket/api.py +++ b/addons/bitbucket/api.py @@ -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): diff --git a/api/institutions/authentication.py b/api/institutions/authentication.py index 5a4b77017727..fc9b828ddb9a 100644 --- a/api/institutions/authentication.py +++ b/api/institutions/authentication.py @@ -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. # # '': { -# 'criteria': 'attribute', -# 'attribute': '', -# 'institutions': { -# '': '', -# '': '', -# ... -# }, -# ... -# } -# -# 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: -# -# '': { -# 'criteria': 'emailDomain', -# 'institutions': { -# '': '': '', +# 'criteria_action': ' +# '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', }, } @@ -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: diff --git a/api_tests/institutions/views/test_institution_auth.py b/api_tests/institutions/views/test_institution_auth.py index 28ff5928ec1b..ca1c4c951c5f 100644 --- a/api_tests/institutions/views/test_institution_auth.py +++ b/api_tests/institutions/views/test_institution_auth.py @@ -14,6 +14,7 @@ from framework.auth.views import send_confirm_email from osf.models import OSFUser +from osf.models.institution import SharedSsoAffiliationFilterCriteriaAction from osf_tests.factories import InstitutionFactory, ProjectFactory, UserFactory from tests.base import capture_signals @@ -31,6 +32,7 @@ def make_payload( family_name='', department='', is_member_of='', + user_roles='', selective_sso_filter='', ): @@ -46,6 +48,7 @@ def make_payload( 'username': username, 'department': department, 'isMemberOf': is_member_of, + 'userRoles': user_roles, 'selectiveSsoFilter': selective_sso_filter, } } @@ -70,7 +73,7 @@ def institution(): @pytest.fixture() -def institution_primary(): +def institution_primary_type_1(): institution = InstitutionFactory() institution._id = 'brown' institution.save() @@ -78,13 +81,29 @@ def institution_primary(): @pytest.fixture() -def institution_secondary(): +def institution_secondary_type_1(): institution = InstitutionFactory() institution._id = 'thepolicylab' institution.save() return institution +@pytest.fixture() +def institution_primary_type_2(): + institution = InstitutionFactory() + institution._id = 'fsu' + institution.save() + return institution + + +@pytest.fixture() +def institution_secondary_type_2(): + institution = InstitutionFactory() + institution._id = 'nationalmaglab' + institution.save() + return institution + + @pytest.fixture() def institution_selective(): institution = InstitutionFactory() @@ -98,6 +117,18 @@ def url_auth_institution(): return '/{0}institutions/auth/'.format(API_BASE) +@pytest.fixture() +def type_2_eligible_user_roles(): + return 'FSU_IAM_AD_MGMT;FSU_MYFSUADMIN;CS_ADMN_STDT_CNT;FSU_IAM_REG;FSU_OSF_MAGLAB;' \ + 'FSU_FULL_IAM_LOOKUP;FSU_OB_Related_Content_FDA;FSU_OB_FI_EVERYONE;FSU_MS_LIC_FULL' + + +@pytest.fixture() +def type_2_ineligible_user_roles(): + return 'IT_Professional;FSU_IAM_AD_MGMT;FSU_MYFSUADMIN;CS_ADMN_STDT_CNT;FSU_IAM_REG;' \ + 'FSU_FULL_IAM_LOOKUP;FSU_OB_Related_Content_FDA;FSU_OB_FI_EVERYONE;FSU_MS_LIC_FULL' + + @pytest.mark.django_db class TestInstitutionAuth: @@ -408,16 +439,270 @@ def test_user_external_unconfirmed(self, app, institution, url_auth_institution) @pytest.mark.django_db -class TestInstitutionAuthnSharedSSO: +class TestInstitutionAuthnSharedSSOCriteriaType2: + + def test_new_user_primary_only(self, app, url_auth_institution, type_2_ineligible_user_roles, + institution_primary_type_2, institution_secondary_type_2): + + username = 'user_created@osf.edu' + assert OSFUser.objects.filter(username=username).count() == 0 + + with capture_signals() as mock_signals: + res = app.post( + url_auth_institution, + make_payload(institution_primary_type_2, username, user_roles=type_2_ineligible_user_roles) + ) + assert res.status_code == 204 + assert mock_signals.signals_sent() == set([signals.user_confirmed]) + + user = OSFUser.objects.filter(username=username).first() + assert user + assert user.fullname == 'Fake User' + assert user.accepted_terms_of_service is None + assert institution_primary_type_2 in user.affiliated_institutions.all() + assert institution_secondary_type_2 not in user.affiliated_institutions.all() + + def test_new_user_primary_and_secondary(self, app, url_auth_institution, type_2_eligible_user_roles, + institution_primary_type_2, institution_secondary_type_2): + + username = 'user_created@osf.edu' + assert OSFUser.objects.filter(username=username).count() == 0 + + with capture_signals() as mock_signals: + res = app.post( + url_auth_institution, + make_payload(institution_primary_type_2, username, user_roles=type_2_eligible_user_roles) + ) + assert res.status_code == 204 + assert mock_signals.signals_sent() == set([signals.user_confirmed]) + + user = OSFUser.objects.filter(username=username).first() + assert user + assert user.fullname == 'Fake User' + assert user.accepted_terms_of_service is None + assert institution_primary_type_2 in user.affiliated_institutions.all() + assert institution_secondary_type_2 in user.affiliated_institutions.all() + + def test_existing_user_primary_only_not_affiliated(self, app, url_auth_institution, type_2_ineligible_user_roles, + institution_primary_type_2, institution_secondary_type_2): + username = 'user_not_affiliated@primary.edu' + user = make_user(username, 'Foo Bar') + user.save() + number_of_affiliations = user.affiliated_institutions.count() + + with capture_signals() as mock_signals: + res = app.post( + url_auth_institution, + make_payload(institution_primary_type_2, username, user_roles=type_2_ineligible_user_roles) + ) + assert res.status_code == 204 + assert not mock_signals.signals_sent() + + user.reload() + assert user.fullname == 'Foo Bar' + assert user.affiliated_institutions.count() == number_of_affiliations + 1 + assert institution_primary_type_2 in user.affiliated_institutions.all() + assert institution_secondary_type_2 not in user.affiliated_institutions.all() + + def test_existing_user_primary_only_affiliated(self, app, url_auth_institution, type_2_ineligible_user_roles, + institution_primary_type_2, institution_secondary_type_2): + username = 'user_affiliated@primary.edu' + user = make_user(username, 'Foo Bar') + user.affiliated_institutions.add(institution_primary_type_2) + user.save() + number_of_affiliations = user.affiliated_institutions.count() + + with capture_signals() as mock_signals: + res = app.post( + url_auth_institution, + make_payload(institution_primary_type_2, username, user_roles=type_2_ineligible_user_roles) + ) + assert res.status_code == 204 + assert not mock_signals.signals_sent() + + user.reload() + assert user.fullname == 'Foo Bar' + assert user.affiliated_institutions.count() == number_of_affiliations + assert institution_primary_type_2 in user.affiliated_institutions.all() + assert institution_secondary_type_2 not in user.affiliated_institutions.all() + + def test_existing_user_both_not_affiliated(self, app, url_auth_institution, type_2_eligible_user_roles, + institution_primary_type_2, institution_secondary_type_2): + + username = 'user_both_not_affiliated@primary.edu' + user = make_user(username, 'Foo Bar') + user.save() + number_of_affiliations = user.affiliated_institutions.count() + + with capture_signals() as mock_signals: + res = app.post( + url_auth_institution, + make_payload(institution_primary_type_2, username, user_roles=type_2_eligible_user_roles) + ) + assert res.status_code == 204 + assert not mock_signals.signals_sent() + + user.reload() + assert user.fullname == 'Foo Bar' + assert user.affiliated_institutions.count() == number_of_affiliations + 2 + assert institution_primary_type_2 in user.affiliated_institutions.all() + assert institution_secondary_type_2 in user.affiliated_institutions.all() + + def test_existing_user_both_affiliated(self, app, url_auth_institution, type_2_eligible_user_roles, + institution_primary_type_2, institution_secondary_type_2): + + username = 'user_both_affiliated@primary.edu' + user = make_user(username, 'Foo Bar') + user.affiliated_institutions.add(institution_primary_type_2) + user.affiliated_institutions.add(institution_secondary_type_2) + user.save() + number_of_affiliations = user.affiliated_institutions.count() + + with capture_signals() as mock_signals: + res = app.post( + url_auth_institution, + make_payload(institution_primary_type_2, username, user_roles=type_2_eligible_user_roles) + ) + assert res.status_code == 204 + assert not mock_signals.signals_sent() + + user.reload() + assert user.fullname == 'Foo Bar' + assert user.affiliated_institutions.count() == number_of_affiliations + assert institution_primary_type_2 in user.affiliated_institutions.all() + assert institution_secondary_type_2 in user.affiliated_institutions.all() + + def test_existing_user_secondary_not_affiliated(self, app, url_auth_institution, type_2_eligible_user_roles, + institution_primary_type_2, institution_secondary_type_2): + + username = 'user_secondary_not@primary.edu' + user = make_user(username, 'Foo Bar') + user.affiliated_institutions.add(institution_primary_type_2) + user.save() + number_of_affiliations = user.affiliated_institutions.count() + + with capture_signals() as mock_signals: + res = app.post( + url_auth_institution, + make_payload(institution_primary_type_2, username, user_roles=type_2_eligible_user_roles) + ) + assert res.status_code == 204 + assert not mock_signals.signals_sent() + + user.reload() + assert user.fullname == 'Foo Bar' + assert user.affiliated_institutions.count() == number_of_affiliations + 1 + assert institution_primary_type_2 in user.affiliated_institutions.all() + assert institution_secondary_type_2 in user.affiliated_institutions.all() + + def test_invalid_criteria_action(self, app, url_auth_institution, type_2_eligible_user_roles, + institution_primary_type_2, institution_secondary_type_2): + + INSTITUTION_SHARED_SSO_MAP.update({ + 'fsu': { + 'attribute_name': 'userRoles', + 'criteria_action': 'invalid_criteria_action', + 'criteria_value': 'FSU_OSF_MAGLAB', + 'institution_id': 'nationalmaglab', + }, + }) + + username = 'user_invalid_criteria_action@primary.edu' + user = make_user(username, 'Foo Bar') + user.affiliated_institutions.add(institution_primary_type_2) + user.save() + number_of_affiliations = user.affiliated_institutions.count() + + with capture_signals() as mock_signals: + res = app.post( + url_auth_institution, + make_payload(institution_primary_type_2, username, user_roles=type_2_eligible_user_roles) + ) + assert res.status_code == 204 + assert not mock_signals.signals_sent() + + user.reload() + assert user.fullname == 'Foo Bar' + assert user.affiliated_institutions.count() == number_of_affiliations + assert institution_primary_type_2 in user.affiliated_institutions.all() + assert institution_secondary_type_2 not in user.affiliated_institutions.all() + + def test_invalid_institution_id(self, app, url_auth_institution, type_2_eligible_user_roles, + institution_primary_type_2, institution_secondary_type_2): + + INSTITUTION_SHARED_SSO_MAP.update({ + 'fsu': { + 'attribute_name': 'userRoles', + 'criteria_action': SharedSsoAffiliationFilterCriteriaAction.CONTAINS.value, + 'criteria_value': 'FSU_OSF_MAGLAB', + 'institution_id': 'invalid_institution_id', + }, + }) + + username = 'user_invalid_institution_id@primary.edu' + user = make_user(username, 'Foo Bar') + user.affiliated_institutions.add(institution_primary_type_2) + user.save() + number_of_affiliations = user.affiliated_institutions.count() + + with capture_signals() as mock_signals: + res = app.post( + url_auth_institution, + make_payload(institution_primary_type_2, username, user_roles=type_2_eligible_user_roles) + ) + assert res.status_code == 204 + assert not mock_signals.signals_sent() + + user.reload() + assert user.fullname == 'Foo Bar' + assert user.affiliated_institutions.count() == number_of_affiliations + assert institution_primary_type_2 in user.affiliated_institutions.all() + assert institution_secondary_type_2 not in user.affiliated_institutions.all() + + def test_empty_criteria_value(self, app, url_auth_institution, + institution_primary_type_2, institution_secondary_type_2): + + INSTITUTION_SHARED_SSO_MAP.update({ + 'fsu': { + 'attribute_name': 'userRoles', + 'criteria_action': SharedSsoAffiliationFilterCriteriaAction.CONTAINS.value, + 'criteria_value': 'FSU_OSF_MAGLAB', + 'institution_id': 'nationalmaglab', + }, + }) + + username = 'user_invalid_criteria_value@primary.edu' + user = make_user(username, 'Foo Bar') + user.affiliated_institutions.add(institution_primary_type_2) + user.save() + number_of_affiliations = user.affiliated_institutions.count() + + with capture_signals() as mock_signals: + res = app.post( + url_auth_institution, + make_payload(institution_primary_type_2, username, user_roles='') + ) + assert res.status_code == 204 + assert not mock_signals.signals_sent() + + user.reload() + assert user.fullname == 'Foo Bar' + assert user.affiliated_institutions.count() == number_of_affiliations + assert institution_primary_type_2 in user.affiliated_institutions.all() + assert institution_secondary_type_2 not in user.affiliated_institutions.all() + + +@pytest.mark.django_db +class TestInstitutionAuthnSharedSSOCriteriaType1: def test_new_user_primary_only(self, app, url_auth_institution, - institution_primary, institution_secondary): + institution_primary_type_1, institution_secondary_type_1): username = 'user_created@osf.edu' assert OSFUser.objects.filter(username=username).count() == 0 with capture_signals() as mock_signals: - res = app.post(url_auth_institution, make_payload(institution_primary, username)) + res = app.post(url_auth_institution, make_payload(institution_primary_type_1, username)) assert res.status_code == 204 assert mock_signals.signals_sent() == set([signals.user_confirmed]) @@ -425,18 +710,20 @@ def test_new_user_primary_only(self, app, url_auth_institution, assert user assert user.fullname == 'Fake User' assert user.accepted_terms_of_service is None - assert institution_primary in user.affiliated_institutions.all() - assert institution_secondary not in user.affiliated_institutions.all() + assert institution_primary_type_1 in user.affiliated_institutions.all() + assert institution_secondary_type_1 not in user.affiliated_institutions.all() def test_new_user_primary_and_secondary(self, app, url_auth_institution, - institution_primary, institution_secondary): + institution_primary_type_1, institution_secondary_type_1): username = 'user_created@osf.edu' assert OSFUser.objects.filter(username=username).count() == 0 with capture_signals() as mock_signals: - res = app.post(url_auth_institution, - make_payload(institution_primary, username, is_member_of='thepolicylab')) + res = app.post( + url_auth_institution, + make_payload(institution_primary_type_1, username, is_member_of='thepolicylab') + ) assert res.status_code == 204 assert mock_signals.signals_sent() == set([signals.user_confirmed]) @@ -444,48 +731,48 @@ def test_new_user_primary_and_secondary(self, app, url_auth_institution, assert user assert user.fullname == 'Fake User' assert user.accepted_terms_of_service is None - assert institution_primary in user.affiliated_institutions.all() - assert institution_secondary in user.affiliated_institutions.all() + assert institution_primary_type_1 in user.affiliated_institutions.all() + assert institution_secondary_type_1 in user.affiliated_institutions.all() def test_existing_user_primary_only_not_affiliated(self, app, url_auth_institution, - institution_primary, institution_secondary): + institution_primary_type_1, institution_secondary_type_1): username = 'user_not_affiliated@primary.edu' user = make_user(username, 'Foo Bar') user.save() number_of_affiliations = user.affiliated_institutions.count() with capture_signals() as mock_signals: - res = app.post(url_auth_institution, make_payload(institution_primary, username)) + res = app.post(url_auth_institution, make_payload(institution_primary_type_1, username)) assert res.status_code == 204 assert not mock_signals.signals_sent() user.reload() assert user.fullname == 'Foo Bar' assert user.affiliated_institutions.count() == number_of_affiliations + 1 - assert institution_primary in user.affiliated_institutions.all() - assert institution_secondary not in user.affiliated_institutions.all() + assert institution_primary_type_1 in user.affiliated_institutions.all() + assert institution_secondary_type_1 not in user.affiliated_institutions.all() def test_existing_user_primary_only_affiliated(self, app, url_auth_institution, - institution_primary, institution_secondary): + institution_primary_type_1, institution_secondary_type_1): username = 'user_affiliated@primary.edu' user = make_user(username, 'Foo Bar') - user.affiliated_institutions.add(institution_primary) + user.affiliated_institutions.add(institution_primary_type_1) user.save() number_of_affiliations = user.affiliated_institutions.count() with capture_signals() as mock_signals: - res = app.post(url_auth_institution, make_payload(institution_primary, username)) + res = app.post(url_auth_institution, make_payload(institution_primary_type_1, username)) assert res.status_code == 204 assert not mock_signals.signals_sent() user.reload() assert user.fullname == 'Foo Bar' assert user.affiliated_institutions.count() == number_of_affiliations - assert institution_primary in user.affiliated_institutions.all() - assert institution_secondary not in user.affiliated_institutions.all() + assert institution_primary_type_1 in user.affiliated_institutions.all() + assert institution_secondary_type_1 not in user.affiliated_institutions.all() def test_existing_user_both_not_affiliated(self, app, url_auth_institution, - institution_primary, institution_secondary): + institution_primary_type_1, institution_secondary_type_1): username = 'user_both_not_affiliated@primary.edu' user = make_user(username, 'Foo Bar') @@ -493,151 +780,161 @@ def test_existing_user_both_not_affiliated(self, app, url_auth_institution, number_of_affiliations = user.affiliated_institutions.count() with capture_signals() as mock_signals: - res = app.post(url_auth_institution, - make_payload(institution_primary, username, is_member_of='thepolicylab')) + res = app.post( + url_auth_institution, + make_payload(institution_primary_type_1, username, is_member_of='thepolicylab') + ) assert res.status_code == 204 assert not mock_signals.signals_sent() user.reload() assert user.fullname == 'Foo Bar' assert user.affiliated_institutions.count() == number_of_affiliations + 2 - assert institution_primary in user.affiliated_institutions.all() - assert institution_secondary in user.affiliated_institutions.all() + assert institution_primary_type_1 in user.affiliated_institutions.all() + assert institution_secondary_type_1 in user.affiliated_institutions.all() def test_existing_user_both_affiliated(self, app, url_auth_institution, - institution_primary, institution_secondary): + institution_primary_type_1, institution_secondary_type_1): username = 'user_both_affiliated@primary.edu' user = make_user(username, 'Foo Bar') - user.affiliated_institutions.add(institution_primary) - user.affiliated_institutions.add(institution_secondary) + user.affiliated_institutions.add(institution_primary_type_1) + user.affiliated_institutions.add(institution_secondary_type_1) user.save() number_of_affiliations = user.affiliated_institutions.count() with capture_signals() as mock_signals: - res = app.post(url_auth_institution, - make_payload(institution_primary, username, is_member_of='thepolicylab')) + res = app.post( + url_auth_institution, + make_payload(institution_primary_type_1, username, is_member_of='thepolicylab') + ) assert res.status_code == 204 assert not mock_signals.signals_sent() user.reload() assert user.fullname == 'Foo Bar' assert user.affiliated_institutions.count() == number_of_affiliations - assert institution_primary in user.affiliated_institutions.all() - assert institution_secondary in user.affiliated_institutions.all() + assert institution_primary_type_1 in user.affiliated_institutions.all() + assert institution_secondary_type_1 in user.affiliated_institutions.all() def test_existing_user_secondary_not_affiliated(self, app, url_auth_institution, - institution_primary, institution_secondary): + institution_primary_type_1, institution_secondary_type_1): username = 'user_secondary_not@primary.edu' user = make_user(username, 'Foo Bar') - user.affiliated_institutions.add(institution_primary) + user.affiliated_institutions.add(institution_primary_type_1) user.save() number_of_affiliations = user.affiliated_institutions.count() with capture_signals() as mock_signals: - res = app.post(url_auth_institution, - make_payload(institution_primary, username, is_member_of='thepolicylab')) + res = app.post( + url_auth_institution, + make_payload(institution_primary_type_1, username, is_member_of='thepolicylab') + ) assert res.status_code == 204 assert not mock_signals.signals_sent() user.reload() assert user.fullname == 'Foo Bar' assert user.affiliated_institutions.count() == number_of_affiliations + 1 - assert institution_primary in user.affiliated_institutions.all() - assert institution_secondary in user.affiliated_institutions.all() + assert institution_primary_type_1 in user.affiliated_institutions.all() + assert institution_secondary_type_1 in user.affiliated_institutions.all() - def test_invalid_criteria_type(self, app, url_auth_institution, - institution_primary, institution_secondary): + def test_invalid_criteria_action(self, app, url_auth_institution, + institution_primary_type_1, institution_secondary_type_1): INSTITUTION_SHARED_SSO_MAP.update({ 'brown': { - 'criteria': 'emailDomain', - 'institutions': { - 'policylab.io': 'thepolicylab', - } + 'attribute_name': 'isMemberOf', + 'criteria_action': 'invalid_criteria_action', + 'criteria_value': 'thepolicylab', + 'institution_id': 'thepolicylab', }, }) - username = 'user_invalid_criteria@primary.edu' + username = 'user_invalid_criteria_action@primary.edu' user = make_user(username, 'Foo Bar') - user.affiliated_institutions.add(institution_primary) + user.affiliated_institutions.add(institution_primary_type_1) user.save() number_of_affiliations = user.affiliated_institutions.count() with capture_signals() as mock_signals: - res = app.post(url_auth_institution, - make_payload(institution_primary, username, is_member_of='thepolicylab')) + res = app.post( + url_auth_institution, + make_payload(institution_primary_type_1, username, is_member_of='thepolicylab') + ) assert res.status_code == 204 assert not mock_signals.signals_sent() user.reload() assert user.fullname == 'Foo Bar' assert user.affiliated_institutions.count() == number_of_affiliations - assert institution_primary in user.affiliated_institutions.all() - assert institution_secondary not in user.affiliated_institutions.all() + assert institution_primary_type_1 in user.affiliated_institutions.all() + assert institution_secondary_type_1 not in user.affiliated_institutions.all() - def test_invalid_secondary_institution(self, app, url_auth_institution, - institution_primary, institution_secondary): + def test_invalid_institution_id(self, app, url_auth_institution, + institution_primary_type_1, institution_secondary_type_1): INSTITUTION_SHARED_SSO_MAP.update({ 'brown': { - 'criteria': 'attribute', - 'attribute': 'isMemberOf', - 'institutions': { - 'thepolicylab': 'brownlab', - } + 'attribute_name': 'isMemberOf', + 'criteria_action': SharedSsoAffiliationFilterCriteriaAction.EQUALS_TO.value, + 'criteria_value': 'thepolicylab', + 'institution_id': 'invalid_institution_id', }, }) - username = 'user_invalid_criteria@primary.edu' + username = 'user_invalid_institution_id@primary.edu' user = make_user(username, 'Foo Bar') - user.affiliated_institutions.add(institution_primary) + user.affiliated_institutions.add(institution_primary_type_1) user.save() number_of_affiliations = user.affiliated_institutions.count() with capture_signals() as mock_signals: - res = app.post(url_auth_institution, - make_payload(institution_primary, username, is_member_of='thepolicylab')) + res = app.post( + url_auth_institution, + make_payload(institution_primary_type_1, username, is_member_of='thepolicylab') + ) assert res.status_code == 204 assert not mock_signals.signals_sent() user.reload() assert user.fullname == 'Foo Bar' assert user.affiliated_institutions.count() == number_of_affiliations - assert institution_primary in user.affiliated_institutions.all() - assert institution_secondary not in user.affiliated_institutions.all() + assert institution_primary_type_1 in user.affiliated_institutions.all() + assert institution_secondary_type_1 not in user.affiliated_institutions.all() - def test_empty_secondary_institution(self, app, url_auth_institution, - institution_primary, institution_secondary): + def test_invalid_criteria_value(self, app, url_auth_institution, + institution_primary_type_1, institution_secondary_type_1): INSTITUTION_SHARED_SSO_MAP.update({ 'brown': { - 'criteria': 'attribute', - 'attribute': 'isMemberOf', - 'institutions': { - 'thepolicylab': 'thepolicylab', - } + 'attribute_name': 'isMemberOf', + 'criteria_action': SharedSsoAffiliationFilterCriteriaAction.EQUALS_TO.value, + 'criteria_value': 'thepolicylab', + 'institution_id': 'thepolicylab', }, }) - username = 'user_invalid_criteria@primary.edu' + username = 'user_invalid_criteria_value@primary.edu' user = make_user(username, 'Foo Bar') - user.affiliated_institutions.add(institution_primary) + user.affiliated_institutions.add(institution_primary_type_1) user.save() number_of_affiliations = user.affiliated_institutions.count() with capture_signals() as mock_signals: - res = app.post(url_auth_institution, - make_payload(institution_primary, username, is_member_of='brownlab')) + res = app.post( + url_auth_institution, + make_payload(institution_primary_type_1, username, is_member_of='invalid_criteria_value') + ) assert res.status_code == 204 assert not mock_signals.signals_sent() user.reload() assert user.fullname == 'Foo Bar' assert user.affiliated_institutions.count() == number_of_affiliations - assert institution_primary in user.affiliated_institutions.all() - assert institution_secondary not in user.affiliated_institutions.all() + assert institution_primary_type_1 in user.affiliated_institutions.all() + assert institution_secondary_type_1 not in user.affiliated_institutions.all() @pytest.mark.django_db diff --git a/osf/migrations/0002_add_lower_index_to_tags_squashed_0247_artifact_finalized_and_deleted.py b/osf/migrations/0002_add_lower_index_to_tags_squashed_0249_schema_response_justification_to_text_field.py similarity index 91% rename from osf/migrations/0002_add_lower_index_to_tags_squashed_0247_artifact_finalized_and_deleted.py rename to osf/migrations/0002_add_lower_index_to_tags_squashed_0249_schema_response_justification_to_text_field.py index 737ba4b17c9e..843537b947ee 100644 --- a/osf/migrations/0002_add_lower_index_to_tags_squashed_0247_artifact_finalized_and_deleted.py +++ b/osf/migrations/0002_add_lower_index_to_tags_squashed_0249_schema_response_justification_to_text_field.py @@ -78,8 +78,190 @@ # osf.migrations.0193_migrate_registered_meta class Migration(migrations.Migration): - - replaces = [('osf', '0002_add_lower_index_to_tags'), ('osf', '0003_auto_20170402_1611'), ('osf', '0004_abstractnode_comment_level'), ('osf', '0003_auto_20170330_1251'), ('osf', '0005_merge'), ('osf', '0006_add_jsonb_index_for_fileversions'), ('osf', '0007_auto_20170404_0857'), ('osf', '0007_auto_20170403_2304'), ('osf', '0008_merge'), ('osf', '0009_auto_20170406_1614'), ('osf', '0010_update_osfuser_permissions'), ('osf', '0011_auto_20170410_1711'), ('osf', '0012_auto_20170411_1548'), ('osf', '0013_auto_20170417_1437'), ('osf', '0013_auto_20170412_0957'), ('osf', '0014_merge'), ('osf', '0015_preprintprovider_domain'), ('osf', '0016_preprintprovider_domain_redirect_enabled'), ('osf', '0017_auto_20170419_1323'), ('osf', '0015_auto_20170421_1244'), ('osf', '0018_merge_20170424_1330'), ('osf', '0016_auto_20170424_1609'), ('osf', '0019_merge_20170424_1956'), ('osf', '0020_auto_20170426_0920'), ('osf', '0021_unique_notificationsettings__ids'), ('osf', '0022_auto_20170503_1818'), ('osf', '0021_retraction_date_retracted'), ('osf', '0023_merge_20170503_1947'), ('osf', '0024_migrate_subject_parents_to_parent'), ('osf', '0025_migrate_preprint_subjects_to_fks'), ('osf', '0026_rename_preprintservice_subjects'), ('osf', '0027_auto_20170428_1435'), ('osf', '0028_auto_20170504_1548'), ('osf', '0029_auto_20170511_1553'), ('osf', '0030_preprint_provider_institution_permissions'), ('osf', '0031_preprintprovider_share_source'), ('osf', '0032_unquote_gd_nodesettings_folder_path'), ('osf', '0033_user_emails_to_fk'), ('osf', '0034_rename_email_user_relation'), ('osf', '0035_metaschema_active'), ('osf', '0036_auto_20170605_1520'), ('osf', '0037_ensure_licenses'), ('osf', '0038_ensure_schemas'), ('osf', '0039_maintenancestate'), ('osf', '0037_remove_emails_for_unconfirmed_users'), ('osf', '0040_merge_20170619_0922'), ('osf', '0041_auto_20170706_1024'), ('osf', '0042_preprintprovider_share_title'), ('osf', '0042_auto_20170707_1019'), ('osf', '0043_merge_20170725_1328'), ('osf', '0044_ever_mentioned_array_to_m2m'), ('osf', '0045_add_view_subject_permissions'), ('osf', '0043_set_share_title'), ('osf', '0046_merge_20170803_1147'), ('osf', '0047_remove_abstractnode_users_watching_node'), ('osf', '0044_basefilenode_uniqueness_index'), ('osf', '0048_merge_20170804_0910'), ('osf', '0049_preprintprovider_preprint_word'), ('osf', '0050_auto_20170809_1511'), ('osf', '0051_remove_invalid_social_entries'), ('osf', '0052_preprintprovider_share_publish_type'), ('osf', '0053_add_quickfiles'), ('osf', '0054_auto_20170823_1555'), ('osf', '0055_auto_20170823_1648'), ('osf', '0053_nodelog_faster_index'), ('osf', '0056_merge_20170831_0832'), ('osf', '0057_order_fileversion_by_date_created'), ('osf', '0054_add_file_version_indices'), ('osf', '0058_merge_20170913_2232'), ('osf', '0055_update_metaschema_active'), ('osf', '0059_merge_20170914_1100'), ('osf', '0060_reviews'), ('osf', '0061_add_reviews_notification_subscription'), ('osf', '0062_accept_preprints'), ('osf', '0060_add_nodelog_should_hide_nid_index'), ('osf', '0063_merge_20171012_1215'), ('osf', '0064_auto_20171019_0918'), ('osf', '0065_preprintservice_original_publication_date'), ('osf', '0065_auto_20171024_1330'), ('osf', '0066_merge_20171121_1050'), ('osf', '0067_auto_20171121_1050'), ('osf', '0068_creator_modified_renames'), ('osf', '0069_skippable_created_modified'), ('osf', '0070_auto_20171121_1805'), ('osf', '0068_draftregistration_deleted'), ('osf', '0071_merge_20171128_0950'), ('osf', '0068_preprintservice_preprint_doi_created'), ('osf', '0069_auto_20171127_1119'), ('osf', '0070_merge_20171127_2232'), ('osf', '0072_merge_20171128_1018'), ('osf', '0073_citationstyle_has_bibliography'), ('osf', '0074_parse_citation_styles'), ('osf', '0074_auto_20171207_1331'), ('osf', '0075_merge_20171207_1511'), ('osf', '0076_action_rename'), ('osf', '0077_preprintprovider_facebook_app_id'), ('osf', '0077_add_maintenance_permissions'), ('osf', '0078_merge_20180206_1148'), ('osf', '0078_ensure_schemas'), ('osf', '0079_merge_20180207_1545'), ('osf', '0080_ensure_schemas'), ('osf', '0077_bannerimage_scheduledbanner'), ('osf', '0078_add_banner_permissions'), ('osf', '0077_ensure_schemas'), ('osf', '0079_merge_20180202_1206'), ('osf', '0081_merge_20180212_0949'), ('osf', '0077_add_noderequest_model'), ('osf', '0082_merge_20180213_1502'), ('osf', '0084_merge_20180308_1821'), ('osf', '0080_add_abstractprovider'), ('osf', '0081_alter_abstractprovider_permissions'), ('osf', '0082_add_preprint_word_choice'), ('osf', '0083_merge_20180228_1619'), ('osf', '0085_merge_20180316_1625'), ('osf', '0086_pre_migrate_collections'), ('osf', '0087_migrate_collections_data'), ('osf', '0088_post_migrate_collections'), ('osf', '0089_auto_20180315_1114'), ('osf', '0090_add_collection_groups_permissions'), ('osf', '0091_notificationsubscription_provider'), ('osf', '0092_populate_notification_subscriptions'), ('osf', '0093_node_subjects'), ('osf', '0094_update_preprintprovider_group_auth'), ('osf', '0095_ensure_licenses'), ('osf', '0095_abstractprovider_allow_commenting'), ('osf', '0096_merge_20180413_1110'), ('osf', '0095_reset_osf_abstractprovider_licenses_acceptable_id_seq'), ('osf', '0096_ensure_schemas'), ('osf', '0097_merge_20180416_1533'), ('osf', '0096_modify_noderequestaction_noderequest'), ('osf', '0097_merge_20180416_1453'), ('osf', '0098_merge_20180416_1807'), ('osf', '0098_auto_20180418_1722'), ('osf', '0099_merge_20180427_1109'), ('osf', '0100_set_access_request_enabled'), ('osf', '0099_migrate_comments_on_deleted_objects'), ('osf', '0101_merge_20180508_1011'), ('osf', '0099_file_version_user_metadata'), ('osf', '0099_add_default_storage_region'), ('osf', '0102_merge_20180509_0846'), ('osf', '0103_set_osf_storage_node_settings_region'), ('osf', '0101_osfuser_accepted_terms_of_service'), ('osf', '0104_merge_20180524_1621'), ('osf', '0095_collectedguidmetadata_subjects'), ('osf', '0096_abstractprovider_primary_collection'), ('osf', '0098_merge_20180424_1329'), ('osf', '0099_whitelistedsharepreprintprovider'), ('osf', '0100_notificationdigest_provider'), ('osf', '0104_merge_20180523_1240'), ('osf', '0105_merge_20180525_1529'), ('osf', '0106_citationstyle_parent_style'), ('osf', '0107_add_dependent_styles'), ('osf', '0108_auto_20180530_1310'), ('osf', '0106_auto_20180601_1341'), ('osf', '0109_merge_20180611_1410'), ('osf', '0110_set_ever_public'), ('osf', '0111_auto_20180605_1240'), ('osf', '0095_add_url_to_licenses'), ('osf', '0096_add_provider_doi_prefixes'), ('osf', '0099_merge_20180426_0930'), ('osf', '0101_merge_20180514_1932'), ('osf', '0104_merge_20180518_1337'), ('osf', '0105_add_identifier_deleted_field'), ('osf', '0106_set_preprint_identifier_category'), ('osf', '0107_merge_20180604_1232'), ('osf', '0112_merge_20180614_1454'), ('osf', '0113_auto_20180615_1308'), ('osf', '0112_ensure_schemas'), ('osf', '0114_merge_20180628_1234'), ('osf', '0115_auto_20180628_1253'), ('osf', '0112_alter_collectionprovider_permissions'), ('osf', '0113_add_view_collectionprovider_to_admin_perm'), ('osf', '0114_merge_20180621_1322'), ('osf', '0115_auto_20180621_1324'), ('osf', '0116_merge_20180703_2258'), ('osf', '0117_auto_20180625_0810'), ('osf', '0118_auto_20180706_1127'), ('osf', '0119_add_registrationprovider_perms_to_admin'), ('osf', '0116_osfuser_deleted'), ('osf', '0117_merge_20180712_1241'), ('osf', '0118_auto_20180716_1216'), ('osf', '0119_add_asset_perms'), ('osf', '0120_merge_20180716_1457'), ('osf', '0121_add_preprintrequest_perms_to_admin'), ('osf', '0122_unique_provider__id_by_type'), ('osf', '0123_auto_20180726_1546'), ('osf', '0083_add_file_fields_for_target'), ('osf', '0084_migrate_node_info_for_target'), ('osf', '0085_finalize_file_node_to_target'), ('osf', '0099_merge_20180505_1027'), ('osf', '0104_merge_20180524_1257'), ('osf', '0106_merge_20180531_0919'), ('osf', '0116_merge_20180706_0901'), ('osf', '0117_set_is_root'), ('osf', '0121_merge_20180801_1458'), ('osf', '0122_auto_20180801_2105'), ('osf', '0124_merge_20180803_1425'), ('osf', '0125_auto_20180808_1942'), ('osf', '0126_update_review_group_names'), ('osf', '0121_generalize_schema_models'), ('osf', '0123_merge_20180803_1346'), ('osf', '0121_remove_support_page_waffle_flag'), ('osf', '0124_merge_20180816_1229'), ('osf', '0127_merge_20180822_1927'), ('osf', '0128_rename_collectionsubmission'), ('osf', '0121_remove_waffle_flags'), ('osf', '0125_merge_20180824_1856'), ('osf', '0128_merge_20180829_0012'), ('osf', '0129_merge_20180910_1926'), ('osf', '0121_remove_wiki_fields_from_node'), ('osf', '0124_merge_20180815_1924'), ('osf', '0130_merge_20180914_1414'), ('osf', '0130_fileversion_region'), ('osf', '0131_merge_20180914_1937'), ('osf', '0127_add_regions_to_all_versions'), ('osf', '0128_merge_20180827_1907'), ('osf', '0129_merge_20180904_2028'), ('osf', '0123_merge_20180802_2004'), ('osf', '0124_merge_20180808_1451'), ('osf', '0125_merge_20180822_1508'), ('osf', '0132_merge_20180914_2001'), ('osf', '0126_update_social_data_format'), ('osf', '0129_merge_20180831_1412'), ('osf', '0133_merge_20180921_0025'), ('osf', '0134_abstractnode_custom_citation'), ('osf', '0135_update_preprint_model_for_divorce'), ('osf', '0136_preprint_node_divorce'), ('osf', '0137_transfer_preprint_service_permissions'), ('osf', '0138_remove_node_preprint_fields'), ('osf', '0135_user_settings_waffles'), ('osf', '0139_merge_20181003_1759'), ('osf', '0130_auto_20180912_2039'), ('osf', '0133_merge_20180920_1604'), ('osf', '0134_add_provider_reg_fks'), ('osf', '0135_migrate_registrations_to_osf_registries_provider'), ('osf', '0136_merge_20181010_2242'), ('osf', '0136_add_ember_auth_register_waffle_flag'), ('osf', '0140_merge_20181011_0021'), ('osf', '0137_merge_20181011_1525'), ('osf', '0141_merge_20181011_2016'), ('osf', '0137_auto_20181012_1756'), ('osf', '0138_merge_20181012_1944'), ('osf', '0142_merge_20181015_1554'), ('osf', '0138_ensure_subjects_and_providers'), ('osf', '0139_merge_20181019_0412'), ('osf', '0129_remove_osfuser_is_claimed'), ('osf', '0140_merge_20181022_1446'), ('osf', '0139_rename_aspredicted_schema'), ('osf', '0141_merge_20181023_1526'), ('osf', '0143_merge_20181023_1807'), ('osf', '0142_remove_forks_flag'), ('osf', '0142_remove_waffle_analytics_flags'), ('osf', '0144_merge_20181113_1420'), ('osf', '0145_add_preprint_contenttype_to_collections'), ('osf', '0142_change_registration_schemas'), ('osf', '0143_merge_20181115_1458'), ('osf', '0146_merge_20181119_2236'), ('osf', '0147_repoint_preprint_pagecounters'), ('osf', '0144_add_prereg_winddown_switches'), ('osf', '0145_add_visible_to_registrationschema'), ('osf', '0146_update_registration_schemas'), ('osf', '0147_blacklistedemaildomain'), ('osf', '0148_merge_20181213_2253'), ('osf', '0149_add_datacite_doi_switch'), ('osf', '0150_fix_deleted_preprints'), ('osf', '0151_auto_20181215_1911'), ('osf', '0152_remove_doi_switches'), ('osf', '0152_ensure_schemas'), ('osf', '0153_merge_20181221_1842'), ('osf', '0154_remove_ember_project_registrations_flag'), ('osf', '0135_add_file_metadata_models'), ('osf', '0136_add_datacite_file_metadata_schema'), ('osf', '0137_add_fm_record_to_osfstorage_files'), ('osf', '0155_merge_20190115_1437'), ('osf', '0156_create_cache_table'), ('osf', '0157_add_storage_usage_flag'), ('osf', '0158_fix_fork_last_logged'), ('osf', '0156_auto_20190306_1648'), ('osf', '0159_merge_20190331_2301'), ('osf', '0160_add_permissions_to_node'), ('osf', '0161_guardian_direct_fks'), ('osf', '0162_post_migrate'), ('osf', '0163_migrate_preprints_to_direct_fks'), ('osf', '0164_add_guardian_to_nodes'), ('osf', '0165_osfgroup'), ('osf', '0156_abstractnode_article_doi'), ('osf', '0160_merge_20190408_1618'), ('osf', '0166_merge_20190429_1632'), ('osf', '0167_auto_20190506_1556'), ('osf', '0161_add_spam_fields_to_user'), ('osf', '0168_merge_20190610_2308'), ('osf', '0162_conference_submissions'), ('osf', '0163_populate_conference_submissions'), ('osf', '0169_merge_20190618_1429'), ('osf', '0170_ensure_schemas'), ('osf', '0171_add_registration_files_count'), ('osf', '0172_ensure_schemas'), ('osf', '0173_ensure_schemas'), ('osf', '0174_add_ab_testing_home_page_version_b_flag'), ('osf', '0175_pagecounter_schema'), ('osf', '0176_pagecounter_data'), ('osf', '0177_pagecounter_index'), ('osf', '0178_apioauth2scope_is_public'), ('osf', '0179_apioauth2personaltoken_scopes_temp'), ('osf', '0180_finalize_token_scopes_mig'), ('osf', '0181_osfuser_contacted_deactivation'), ('osf', '0182_add_custom_file_versions_through'), ('osf', '0183_populate_file_versions_through'), ('osf', '0184_remove_basefilenode_versions'), ('osf', '0185_basefilenode_versions'), ('osf', '0186_make_pagecounter_fields_nonnull'), ('osf', '0187_remove_outdated_contributor_permissions'), ('osf', '0188_deleted_field_mig'), ('osf', '0189_deleted_field_data'), ('osf', '0190_add_schema_block_models'), ('osf', '0191_migrate_schemas_to_schemablocks'), ('osf', '0192_add_registation_responses_fields'), ('osf', '0193_migrate_registered_meta'), ('osf', '0191_abstractnode_external_registered_date'), ('osf', '0192_abstractnode_external_registration_boolean'), ('osf', '0194_merge_20191113_1611'), ('osf', '0195_add_enable_chronos_waffle_flag'), ('osf', '0196_update_schemas'), ('osf', '0197_add_ab_testing_home_page_hero_text_version_b_flag'), ('osf', '0198_draft_node_models'), ('osf', '0199_draft_node_permissions'), ('osf', '0200_auto_20200214_1518'), ('osf', '0201_add_egap_flag'), ('osf', '0202_add_sloan'), ('osf', '0203_auto_20200312_1435'), ('osf', '0204_ensure_schemas'), ('osf', '0205_auto_20200323_1850'), ('osf', '0206_auto_20200528_1319'), ('osf', '0207_ensure_schemas'), ('osf', '0207_update_schemas2'), ('osf', '0208_update_EGAP_schema'), ('osf', '0209_conference_auto_check_spam'), ('osf', '0210_branded_registries'), ('osf', '0211_auto_20200709_1320'), ('osf', '0212_registrationschema_providers'), ('osf', '0213_auto_20200728_1609'), ('osf', '0214_auto_20200701_1658'), ('osf', '0215_auto_20200819_1942'), ('osf', '0216_auto_20200910_1259'), ('osf', '0217_auto_20200901_1344'), ('osf', '0218_auto_20200929_1850'), ('osf', '0219_auto_20201020_1836'), ('osf', '0221_add_schemas'), ('osf', '0222_auto_20201023_2033'), ('osf', '0223_auto_20201026_1843'), ('osf', '0224_population_registration_subscription_notifications'), ('osf', '0225_auto_20201119_2027'), ('osf', '0226_auto_20210224_1610'), ('osf', '0227_add_secondary_data'), ('osf', '0228_abstractnode_branched_from_node'), ('osf', '0229_auto_20210317_2013'), ('osf', '0230_make_run_management_perm'), ('osf', '0231_abstractprovider_default_schema'), ('osf', '0232_abstractnode_ia_url'), ('osf', '0233_auto_20210608_1816'), ('osf', '0234_auto_20210610_1812'), ('osf', '0235_auto_20210823_1310'), ('osf', '0236_auto_20210913_2008'), ('osf', '0237_auto_20210929_1529'), ('osf', '0238_abstractprovider_allow_updates'), ('osf', '0239_notable_email_domains'), ('osf', '0239_auto_20211110_1921'), ('osf', '0240_merge_20211110_2051'), ('osf', '0241_abstractprovider_allow_bulk_uploads'), ('osf', '0242_auto_20220125_1604'), ('osf', '0243_auto_20211025_1353'), ('osf', '0244_auto_20220517_1718'), ('osf', '0245_auto_20220621_1950'), ('osf', '0246_add_outcomes_and_artifacts'), ('osf', '0247_artifact_finalized_and_deleted')] + replaces = [('osf', '0002_add_lower_index_to_tags'), ('osf', '0003_auto_20170402_1611'), + ('osf', '0004_abstractnode_comment_level'), ('osf', '0003_auto_20170330_1251'), ('osf', '0005_merge'), + ('osf', '0006_add_jsonb_index_for_fileversions'), ('osf', '0007_auto_20170404_0857'), + ('osf', '0007_auto_20170403_2304'), ('osf', '0008_merge'), ('osf', '0009_auto_20170406_1614'), + ('osf', '0010_update_osfuser_permissions'), ('osf', '0011_auto_20170410_1711'), + ('osf', '0012_auto_20170411_1548'), ('osf', '0013_auto_20170417_1437'), + ('osf', '0013_auto_20170412_0957'), ('osf', '0014_merge'), ('osf', '0015_preprintprovider_domain'), + ('osf', '0016_preprintprovider_domain_redirect_enabled'), ('osf', '0017_auto_20170419_1323'), + ('osf', '0015_auto_20170421_1244'), ('osf', '0018_merge_20170424_1330'), + ('osf', '0016_auto_20170424_1609'), ('osf', '0019_merge_20170424_1956'), + ('osf', '0020_auto_20170426_0920'), ('osf', '0021_unique_notificationsettings__ids'), + ('osf', '0022_auto_20170503_1818'), ('osf', '0021_retraction_date_retracted'), + ('osf', '0023_merge_20170503_1947'), ('osf', '0024_migrate_subject_parents_to_parent'), + ('osf', '0025_migrate_preprint_subjects_to_fks'), ('osf', '0026_rename_preprintservice_subjects'), + ('osf', '0027_auto_20170428_1435'), ('osf', '0028_auto_20170504_1548'), + ('osf', '0029_auto_20170511_1553'), ('osf', '0030_preprint_provider_institution_permissions'), + ('osf', '0031_preprintprovider_share_source'), ('osf', '0032_unquote_gd_nodesettings_folder_path'), + ('osf', '0033_user_emails_to_fk'), ('osf', '0034_rename_email_user_relation'), + ('osf', '0035_metaschema_active'), ('osf', '0036_auto_20170605_1520'), ('osf', '0037_ensure_licenses'), + ('osf', '0038_ensure_schemas'), ('osf', '0039_maintenancestate'), + ('osf', '0037_remove_emails_for_unconfirmed_users'), ('osf', '0040_merge_20170619_0922'), + ('osf', '0041_auto_20170706_1024'), ('osf', '0042_preprintprovider_share_title'), + ('osf', '0042_auto_20170707_1019'), ('osf', '0043_merge_20170725_1328'), + ('osf', '0044_ever_mentioned_array_to_m2m'), ('osf', '0045_add_view_subject_permissions'), + ('osf', '0043_set_share_title'), ('osf', '0046_merge_20170803_1147'), + ('osf', '0047_remove_abstractnode_users_watching_node'), ('osf', '0044_basefilenode_uniqueness_index'), + ('osf', '0048_merge_20170804_0910'), ('osf', '0049_preprintprovider_preprint_word'), + ('osf', '0050_auto_20170809_1511'), ('osf', '0051_remove_invalid_social_entries'), + ('osf', '0052_preprintprovider_share_publish_type'), ('osf', '0053_add_quickfiles'), + ('osf', '0054_auto_20170823_1555'), ('osf', '0055_auto_20170823_1648'), + ('osf', '0053_nodelog_faster_index'), ('osf', '0056_merge_20170831_0832'), + ('osf', '0057_order_fileversion_by_date_created'), ('osf', '0054_add_file_version_indices'), + ('osf', '0058_merge_20170913_2232'), ('osf', '0055_update_metaschema_active'), + ('osf', '0059_merge_20170914_1100'), ('osf', '0060_reviews'), + ('osf', '0061_add_reviews_notification_subscription'), ('osf', '0062_accept_preprints'), + ('osf', '0060_add_nodelog_should_hide_nid_index'), ('osf', '0063_merge_20171012_1215'), + ('osf', '0064_auto_20171019_0918'), ('osf', '0065_preprintservice_original_publication_date'), + ('osf', '0065_auto_20171024_1330'), ('osf', '0066_merge_20171121_1050'), + ('osf', '0067_auto_20171121_1050'), ('osf', '0068_creator_modified_renames'), + ('osf', '0069_skippable_created_modified'), ('osf', '0070_auto_20171121_1805'), + ('osf', '0068_draftregistration_deleted'), ('osf', '0071_merge_20171128_0950'), + ('osf', '0068_preprintservice_preprint_doi_created'), ('osf', '0069_auto_20171127_1119'), + ('osf', '0070_merge_20171127_2232'), ('osf', '0072_merge_20171128_1018'), + ('osf', '0073_citationstyle_has_bibliography'), ('osf', '0074_parse_citation_styles'), + ('osf', '0074_auto_20171207_1331'), ('osf', '0075_merge_20171207_1511'), ('osf', '0076_action_rename'), + ('osf', '0077_preprintprovider_facebook_app_id'), ('osf', '0077_add_maintenance_permissions'), + ('osf', '0078_merge_20180206_1148'), ('osf', '0078_ensure_schemas'), + ('osf', '0079_merge_20180207_1545'), ('osf', '0080_ensure_schemas'), + ('osf', '0077_bannerimage_scheduledbanner'), ('osf', '0078_add_banner_permissions'), + ('osf', '0077_ensure_schemas'), ('osf', '0079_merge_20180202_1206'), + ('osf', '0081_merge_20180212_0949'), ('osf', '0077_add_noderequest_model'), + ('osf', '0082_merge_20180213_1502'), ('osf', '0084_merge_20180308_1821'), + ('osf', '0080_add_abstractprovider'), ('osf', '0081_alter_abstractprovider_permissions'), + ('osf', '0082_add_preprint_word_choice'), ('osf', '0083_merge_20180228_1619'), + ('osf', '0085_merge_20180316_1625'), ('osf', '0086_pre_migrate_collections'), + ('osf', '0087_migrate_collections_data'), ('osf', '0088_post_migrate_collections'), + ('osf', '0089_auto_20180315_1114'), ('osf', '0090_add_collection_groups_permissions'), + ('osf', '0091_notificationsubscription_provider'), ('osf', '0092_populate_notification_subscriptions'), + ('osf', '0093_node_subjects'), ('osf', '0094_update_preprintprovider_group_auth'), + ('osf', '0095_ensure_licenses'), ('osf', '0095_abstractprovider_allow_commenting'), + ('osf', '0096_merge_20180413_1110'), + ('osf', '0095_reset_osf_abstractprovider_licenses_acceptable_id_seq'), ('osf', '0096_ensure_schemas'), + ('osf', '0097_merge_20180416_1533'), ('osf', '0096_modify_noderequestaction_noderequest'), + ('osf', '0097_merge_20180416_1453'), ('osf', '0098_merge_20180416_1807'), + ('osf', '0098_auto_20180418_1722'), ('osf', '0099_merge_20180427_1109'), + ('osf', '0100_set_access_request_enabled'), ('osf', '0099_migrate_comments_on_deleted_objects'), + ('osf', '0101_merge_20180508_1011'), ('osf', '0099_file_version_user_metadata'), + ('osf', '0099_add_default_storage_region'), ('osf', '0102_merge_20180509_0846'), + ('osf', '0103_set_osf_storage_node_settings_region'), ('osf', '0101_osfuser_accepted_terms_of_service'), + ('osf', '0104_merge_20180524_1621'), ('osf', '0095_collectedguidmetadata_subjects'), + ('osf', '0096_abstractprovider_primary_collection'), ('osf', '0098_merge_20180424_1329'), + ('osf', '0099_whitelistedsharepreprintprovider'), ('osf', '0100_notificationdigest_provider'), + ('osf', '0104_merge_20180523_1240'), ('osf', '0105_merge_20180525_1529'), + ('osf', '0106_citationstyle_parent_style'), ('osf', '0107_add_dependent_styles'), + ('osf', '0108_auto_20180530_1310'), ('osf', '0106_auto_20180601_1341'), + ('osf', '0109_merge_20180611_1410'), ('osf', '0110_set_ever_public'), + ('osf', '0111_auto_20180605_1240'), ('osf', '0095_add_url_to_licenses'), + ('osf', '0096_add_provider_doi_prefixes'), ('osf', '0099_merge_20180426_0930'), + ('osf', '0101_merge_20180514_1932'), ('osf', '0104_merge_20180518_1337'), + ('osf', '0105_add_identifier_deleted_field'), ('osf', '0106_set_preprint_identifier_category'), + ('osf', '0107_merge_20180604_1232'), ('osf', '0112_merge_20180614_1454'), + ('osf', '0113_auto_20180615_1308'), ('osf', '0112_ensure_schemas'), ('osf', '0114_merge_20180628_1234'), + ('osf', '0115_auto_20180628_1253'), ('osf', '0112_alter_collectionprovider_permissions'), + ('osf', '0113_add_view_collectionprovider_to_admin_perm'), ('osf', '0114_merge_20180621_1322'), + ('osf', '0115_auto_20180621_1324'), ('osf', '0116_merge_20180703_2258'), + ('osf', '0117_auto_20180625_0810'), ('osf', '0118_auto_20180706_1127'), + ('osf', '0119_add_registrationprovider_perms_to_admin'), ('osf', '0116_osfuser_deleted'), + ('osf', '0117_merge_20180712_1241'), ('osf', '0118_auto_20180716_1216'), + ('osf', '0119_add_asset_perms'), ('osf', '0120_merge_20180716_1457'), + ('osf', '0121_add_preprintrequest_perms_to_admin'), ('osf', '0122_unique_provider__id_by_type'), + ('osf', '0123_auto_20180726_1546'), ('osf', '0083_add_file_fields_for_target'), + ('osf', '0084_migrate_node_info_for_target'), ('osf', '0085_finalize_file_node_to_target'), + ('osf', '0099_merge_20180505_1027'), ('osf', '0104_merge_20180524_1257'), + ('osf', '0106_merge_20180531_0919'), ('osf', '0116_merge_20180706_0901'), ('osf', '0117_set_is_root'), + ('osf', '0121_merge_20180801_1458'), ('osf', '0122_auto_20180801_2105'), + ('osf', '0124_merge_20180803_1425'), ('osf', '0125_auto_20180808_1942'), + ('osf', '0126_update_review_group_names'), ('osf', '0121_generalize_schema_models'), + ('osf', '0123_merge_20180803_1346'), ('osf', '0121_remove_support_page_waffle_flag'), + ('osf', '0124_merge_20180816_1229'), ('osf', '0127_merge_20180822_1927'), + ('osf', '0128_rename_collectionsubmission'), ('osf', '0121_remove_waffle_flags'), + ('osf', '0125_merge_20180824_1856'), ('osf', '0128_merge_20180829_0012'), + ('osf', '0129_merge_20180910_1926'), ('osf', '0121_remove_wiki_fields_from_node'), + ('osf', '0124_merge_20180815_1924'), ('osf', '0130_merge_20180914_1414'), + ('osf', '0130_fileversion_region'), ('osf', '0131_merge_20180914_1937'), + ('osf', '0127_add_regions_to_all_versions'), ('osf', '0128_merge_20180827_1907'), + ('osf', '0129_merge_20180904_2028'), ('osf', '0123_merge_20180802_2004'), + ('osf', '0124_merge_20180808_1451'), ('osf', '0125_merge_20180822_1508'), + ('osf', '0132_merge_20180914_2001'), ('osf', '0126_update_social_data_format'), + ('osf', '0129_merge_20180831_1412'), ('osf', '0133_merge_20180921_0025'), + ('osf', '0134_abstractnode_custom_citation'), ('osf', '0135_update_preprint_model_for_divorce'), + ('osf', '0136_preprint_node_divorce'), ('osf', '0137_transfer_preprint_service_permissions'), + ('osf', '0138_remove_node_preprint_fields'), ('osf', '0135_user_settings_waffles'), + ('osf', '0139_merge_20181003_1759'), ('osf', '0130_auto_20180912_2039'), + ('osf', '0133_merge_20180920_1604'), ('osf', '0134_add_provider_reg_fks'), + ('osf', '0135_migrate_registrations_to_osf_registries_provider'), ('osf', '0136_merge_20181010_2242'), + ('osf', '0136_add_ember_auth_register_waffle_flag'), ('osf', '0140_merge_20181011_0021'), + ('osf', '0137_merge_20181011_1525'), ('osf', '0141_merge_20181011_2016'), + ('osf', '0137_auto_20181012_1756'), ('osf', '0138_merge_20181012_1944'), + ('osf', '0142_merge_20181015_1554'), ('osf', '0138_ensure_subjects_and_providers'), + ('osf', '0139_merge_20181019_0412'), ('osf', '0129_remove_osfuser_is_claimed'), + ('osf', '0140_merge_20181022_1446'), ('osf', '0139_rename_aspredicted_schema'), + ('osf', '0141_merge_20181023_1526'), ('osf', '0143_merge_20181023_1807'), + ('osf', '0142_remove_forks_flag'), ('osf', '0142_remove_waffle_analytics_flags'), + ('osf', '0144_merge_20181113_1420'), ('osf', '0145_add_preprint_contenttype_to_collections'), + ('osf', '0142_change_registration_schemas'), ('osf', '0143_merge_20181115_1458'), + ('osf', '0146_merge_20181119_2236'), ('osf', '0147_repoint_preprint_pagecounters'), + ('osf', '0144_add_prereg_winddown_switches'), ('osf', '0145_add_visible_to_registrationschema'), + ('osf', '0146_update_registration_schemas'), ('osf', '0147_blacklistedemaildomain'), + ('osf', '0148_merge_20181213_2253'), ('osf', '0149_add_datacite_doi_switch'), + ('osf', '0150_fix_deleted_preprints'), ('osf', '0151_auto_20181215_1911'), + ('osf', '0152_remove_doi_switches'), ('osf', '0152_ensure_schemas'), + ('osf', '0153_merge_20181221_1842'), ('osf', '0154_remove_ember_project_registrations_flag'), + ('osf', '0135_add_file_metadata_models'), ('osf', '0136_add_datacite_file_metadata_schema'), + ('osf', '0137_add_fm_record_to_osfstorage_files'), ('osf', '0155_merge_20190115_1437'), + ('osf', '0156_create_cache_table'), ('osf', '0157_add_storage_usage_flag'), + ('osf', '0158_fix_fork_last_logged'), ('osf', '0156_auto_20190306_1648'), + ('osf', '0159_merge_20190331_2301'), ('osf', '0160_add_permissions_to_node'), + ('osf', '0161_guardian_direct_fks'), ('osf', '0162_post_migrate'), + ('osf', '0163_migrate_preprints_to_direct_fks'), ('osf', '0164_add_guardian_to_nodes'), + ('osf', '0165_osfgroup'), ('osf', '0156_abstractnode_article_doi'), ('osf', '0160_merge_20190408_1618'), + ('osf', '0166_merge_20190429_1632'), ('osf', '0167_auto_20190506_1556'), + ('osf', '0161_add_spam_fields_to_user'), ('osf', '0168_merge_20190610_2308'), + ('osf', '0162_conference_submissions'), ('osf', '0163_populate_conference_submissions'), + ('osf', '0169_merge_20190618_1429'), ('osf', '0170_ensure_schemas'), + ('osf', '0171_add_registration_files_count'), ('osf', '0172_ensure_schemas'), + ('osf', '0173_ensure_schemas'), ('osf', '0174_add_ab_testing_home_page_version_b_flag'), + ('osf', '0175_pagecounter_schema'), ('osf', '0176_pagecounter_data'), ('osf', '0177_pagecounter_index'), + ('osf', '0178_apioauth2scope_is_public'), ('osf', '0179_apioauth2personaltoken_scopes_temp'), + ('osf', '0180_finalize_token_scopes_mig'), ('osf', '0181_osfuser_contacted_deactivation'), + ('osf', '0182_add_custom_file_versions_through'), ('osf', '0183_populate_file_versions_through'), + ('osf', '0184_remove_basefilenode_versions'), ('osf', '0185_basefilenode_versions'), + ('osf', '0186_make_pagecounter_fields_nonnull'), + ('osf', '0187_remove_outdated_contributor_permissions'), ('osf', '0188_deleted_field_mig'), + ('osf', '0189_deleted_field_data'), ('osf', '0190_add_schema_block_models'), + ('osf', '0191_migrate_schemas_to_schemablocks'), ('osf', '0192_add_registation_responses_fields'), + ('osf', '0193_migrate_registered_meta'), ('osf', '0191_abstractnode_external_registered_date'), + ('osf', '0192_abstractnode_external_registration_boolean'), ('osf', '0194_merge_20191113_1611'), + ('osf', '0195_add_enable_chronos_waffle_flag'), ('osf', '0196_update_schemas'), + ('osf', '0197_add_ab_testing_home_page_hero_text_version_b_flag'), ('osf', '0198_draft_node_models'), + ('osf', '0199_draft_node_permissions'), ('osf', '0200_auto_20200214_1518'), + ('osf', '0201_add_egap_flag'), ('osf', '0202_add_sloan'), ('osf', '0203_auto_20200312_1435'), + ('osf', '0204_ensure_schemas'), ('osf', '0205_auto_20200323_1850'), ('osf', '0206_auto_20200528_1319'), + ('osf', '0207_ensure_schemas'), ('osf', '0207_update_schemas2'), ('osf', '0208_update_EGAP_schema'), + ('osf', '0209_conference_auto_check_spam'), ('osf', '0210_branded_registries'), + ('osf', '0211_auto_20200709_1320'), ('osf', '0212_registrationschema_providers'), + ('osf', '0213_auto_20200728_1609'), ('osf', '0214_auto_20200701_1658'), + ('osf', '0215_auto_20200819_1942'), ('osf', '0216_auto_20200910_1259'), + ('osf', '0217_auto_20200901_1344'), ('osf', '0218_auto_20200929_1850'), + ('osf', '0219_auto_20201020_1836'), ('osf', '0221_add_schemas'), ('osf', '0222_auto_20201023_2033'), + ('osf', '0223_auto_20201026_1843'), ('osf', '0224_population_registration_subscription_notifications'), + ('osf', '0225_auto_20201119_2027'), ('osf', '0226_auto_20210224_1610'), + ('osf', '0227_add_secondary_data'), ('osf', '0228_abstractnode_branched_from_node'), + ('osf', '0229_auto_20210317_2013'), ('osf', '0230_make_run_management_perm'), + ('osf', '0231_abstractprovider_default_schema'), ('osf', '0232_abstractnode_ia_url'), + ('osf', '0233_auto_20210608_1816'), ('osf', '0234_auto_20210610_1812'), + ('osf', '0235_auto_20210823_1310'), ('osf', '0236_auto_20210913_2008'), + ('osf', '0237_auto_20210929_1529'), ('osf', '0238_abstractprovider_allow_updates'), + ('osf', '0239_notable_email_domains'), ('osf', '0239_auto_20211110_1921'), + ('osf', '0240_merge_20211110_2051'), ('osf', '0241_abstractprovider_allow_bulk_uploads'), + ('osf', '0242_auto_20220125_1604'), ('osf', '0243_auto_20211025_1353'), + ('osf', '0244_auto_20220517_1718'), ('osf', '0245_auto_20220621_1950'), + ('osf', '0246_add_outcomes_and_artifacts'), ('osf', '0247_artifact_finalized_and_deleted'), + ('osf', '0247_artifact_finalized_and_deleted'), ('osf', '0248_artifact_tweaks'), + ('osf', '0249_schema_response_justification_to_text_field')] dependencies = [ ('guardian', '0001_initial'), @@ -4053,4 +4235,142 @@ class Migration(migrations.Migration): # '\n INSERT INTO osf_subject_parents (from_subject_id, to_subject_id)\n SELECT id, parent_id FROM osf_subject\n WHERE parent_id IS NOT NULL;\n '], # ), + migrations.AlterUniqueTogether( + name='outcomeartifact', + unique_together=set([]), + ), + migrations.AddIndex( + model_name='outcomeartifact', + index=models.Index(fields=['artifact_type', 'outcome'], name='osf_outcome_artifac_5eb92d_idx'), + ), + migrations.AlterField( + model_name='schemaresponse', + name='revision_justification', + field=models.TextField(blank=True, null=True), + ), + ###### Django 3 Migrations ###### + migrations.AlterModelOptions( + name='collectionprovider', + options={}, + ), + migrations.AlterModelOptions( + name='conference', + options={}, + ), + migrations.AlterModelOptions( + name='institution', + options={ + 'permissions': (('view_institutional_metrics', 'Can access metrics endpoints for their Institution'),)}, + ), + migrations.AlterModelOptions( + name='osfuser', + options={}, + ), + migrations.AlterModelOptions( + name='preprint', + options={'permissions': ( + ('read_preprint', 'Can read the preprint'), ('write_preprint', 'Can write the preprint'), + ('admin_preprint', 'Can manage the preprint'))}, + ), + migrations.AlterModelOptions( + name='preprintprovider', + options={}, + ), + migrations.AlterModelOptions( + name='providerassetfile', + options={}, + ), + migrations.AlterModelOptions( + name='registration', + options={}, + ), + migrations.AlterModelOptions( + name='registrationprovider', + options={}, + ), + migrations.AlterModelOptions( + name='scheduledbanner', + options={}, + ), + migrations.AlterModelOptions( + name='subject', + options={'base_manager_name': 'objects'}, + ), + migrations.AlterField( + model_name='abstractnode', + name='moderation_state', + field=models.CharField( + choices=[('undefined', 'Undefined'), ('initial', 'Initial'), ('reverted', 'Reverted'), + ('pending', 'Pending'), ('rejected', 'Rejected'), ('accepted', 'Accepted'), + ('embargo', 'Embargo'), ('pending_embargo_termination', 'PendingEmbargoTermination'), + ('pending_withdraw_request', 'PendingWithdrawRequest'), + ('pending_withdraw', 'PendingWithdraw'), ('withdrawn', 'Withdrawn')], default='initial', + max_length=30), + ), + migrations.AlterField( + model_name='abstractnode', + name='registered_meta', + field=osf.utils.datetime_aware_jsonfield.DateTimeAwareJSONField(blank=True, default=dict, + encoder=osf.utils.datetime_aware_jsonfield.DateTimeAwareJSONEncoder), + ), + migrations.AlterField( + model_name='abstractprovider', + name='additional_providers', + field=django.contrib.postgres.fields.ArrayField(base_field=models.CharField(max_length=200), blank=True, + default=list, size=None), + ), + migrations.AlterField( + model_name='abstractprovider', + name='preprint_word', + field=models.CharField( + choices=[('preprint', 'Preprint'), ('paper', 'Paper'), ('thesis', 'Thesis'), ('work', 'Work'), + ('none', 'None')], default='preprint', max_length=10), + ), + migrations.AlterField( + model_name='abstractprovider', + name='subjects_acceptable', + field=osf.utils.datetime_aware_jsonfield.DateTimeAwareJSONField(blank=True, default=list, + encoder=osf.utils.datetime_aware_jsonfield.DateTimeAwareJSONEncoder), + ), + migrations.AlterField( + model_name='collection', + name='collected_types', + field=models.ManyToManyField( + limit_choices_to={'model__in': ['abstractnode', 'basefilenode', 'collection', 'preprint']}, + related_name='_osf_collection_collected_types_+', to='contenttypes.ContentType'), + ), + migrations.AlterField( + model_name='pagecounter', + name='file', + field=models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, related_name='pagecounters', + to='osf.basefilenode'), + ), + migrations.AlterField( + model_name='pagecounter', + name='resource', + field=models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, related_name='pagecounters', + to='osf.guid'), + ), + migrations.AlterField( + model_name='preprint', + name='ever_public', + field=models.BooleanField(blank=True, default=False), + ), + migrations.AlterField( + model_name='schemaresponse', + name='initiator', + field=models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, to=settings.AUTH_USER_MODEL), + ), + migrations.AlterField( + model_name='schemaresponse', + name='previous_response', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.DO_NOTHING, + related_name='updated_response', to='osf.schemaresponse'), + ), + migrations.AlterField( + model_name='schemaresponse', + name='schema', + field=models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, to='osf.registrationschema'), + ), + ] diff --git a/osf/migrations/0003_auto_20220816_1000.py b/osf/migrations/0003_auto_20220816_1000.py deleted file mode 100644 index 6e0c1d0e8ff5..000000000000 --- a/osf/migrations/0003_auto_20220816_1000.py +++ /dev/null @@ -1,122 +0,0 @@ -# Generated by Django 3.2 on 2022-08-16 10:00 - -from django.conf import settings -import django.contrib.postgres.fields -from django.db import migrations, models -import django.db.models.deletion -import osf.utils.datetime_aware_jsonfield - - -class Migration(migrations.Migration): - - dependencies = [ - ('contenttypes', '0002_remove_content_type_name'), - ('osf', '0002_add_lower_index_to_tags_squashed_0247_artifact_finalized_and_deleted'), - ] - - operations = [ - migrations.AlterModelOptions( - name='collectionprovider', - options={}, - ), - migrations.AlterModelOptions( - name='conference', - options={}, - ), - migrations.AlterModelOptions( - name='institution', - options={'permissions': (('view_institutional_metrics', 'Can access metrics endpoints for their Institution'),)}, - ), - migrations.AlterModelOptions( - name='osfuser', - options={}, - ), - migrations.AlterModelOptions( - name='preprint', - options={'permissions': (('read_preprint', 'Can read the preprint'), ('write_preprint', 'Can write the preprint'), ('admin_preprint', 'Can manage the preprint'))}, - ), - migrations.AlterModelOptions( - name='preprintprovider', - options={}, - ), - migrations.AlterModelOptions( - name='providerassetfile', - options={}, - ), - migrations.AlterModelOptions( - name='registration', - options={}, - ), - migrations.AlterModelOptions( - name='registrationprovider', - options={}, - ), - migrations.AlterModelOptions( - name='scheduledbanner', - options={}, - ), - migrations.AlterModelOptions( - name='subject', - options={'base_manager_name': 'objects'}, - ), - migrations.AlterField( - model_name='abstractnode', - name='moderation_state', - field=models.CharField(choices=[('undefined', 'Undefined'), ('initial', 'Initial'), ('reverted', 'Reverted'), ('pending', 'Pending'), ('rejected', 'Rejected'), ('accepted', 'Accepted'), ('embargo', 'Embargo'), ('pending_embargo_termination', 'PendingEmbargoTermination'), ('pending_withdraw_request', 'PendingWithdrawRequest'), ('pending_withdraw', 'PendingWithdraw'), ('withdrawn', 'Withdrawn')], default='initial', max_length=30), - ), - migrations.AlterField( - model_name='abstractnode', - name='registered_meta', - field=osf.utils.datetime_aware_jsonfield.DateTimeAwareJSONField(blank=True, default=dict, encoder=osf.utils.datetime_aware_jsonfield.DateTimeAwareJSONEncoder), - ), - migrations.AlterField( - model_name='abstractprovider', - name='additional_providers', - field=django.contrib.postgres.fields.ArrayField(base_field=models.CharField(max_length=200), blank=True, default=list, size=None), - ), - migrations.AlterField( - model_name='abstractprovider', - name='preprint_word', - field=models.CharField(choices=[('preprint', 'Preprint'), ('paper', 'Paper'), ('thesis', 'Thesis'), ('work', 'Work'), ('none', 'None')], default='preprint', max_length=10), - ), - migrations.AlterField( - model_name='abstractprovider', - name='subjects_acceptable', - field=osf.utils.datetime_aware_jsonfield.DateTimeAwareJSONField(blank=True, default=list, encoder=osf.utils.datetime_aware_jsonfield.DateTimeAwareJSONEncoder), - ), - migrations.AlterField( - model_name='collection', - name='collected_types', - field=models.ManyToManyField(limit_choices_to={'model__in': ['abstractnode', 'basefilenode', 'collection', 'preprint']}, related_name='_osf_collection_collected_types_+', to='contenttypes.ContentType'), - ), - migrations.AlterField( - model_name='pagecounter', - name='file', - field=models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, related_name='pagecounters', to='osf.basefilenode'), - ), - migrations.AlterField( - model_name='pagecounter', - name='resource', - field=models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, related_name='pagecounters', to='osf.guid'), - ), - migrations.AlterField( - model_name='preprint', - name='ever_public', - field=models.BooleanField(blank=True, default=False), - ), - migrations.AlterField( - model_name='schemaresponse', - name='initiator', - field=models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, to=settings.AUTH_USER_MODEL), - ), - migrations.AlterField( - model_name='schemaresponse', - name='previous_response', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='updated_response', to='osf.schemaresponse'), - ), - migrations.AlterField( - model_name='schemaresponse', - name='schema', - field=models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, to='osf.registrationschema'), - ), - ] diff --git a/osf/migrations/0248_artifact_tweaks.py b/osf/migrations/0248_artifact_tweaks.py new file mode 100644 index 000000000000..95d8aed20297 --- /dev/null +++ b/osf/migrations/0248_artifact_tweaks.py @@ -0,0 +1,33 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.29 on 2022-08-01 19:37 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('osf', '0247_artifact_finalized_and_deleted'), + ] + + operations = [ + migrations.RemoveIndex( + model_name='outcomeartifact', + name='osf_outcome_outcome_a62f5c_idx', + ), + migrations.AlterField( + model_name='outcomeartifact', + name='identifier', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='artifact_metadata', to='osf.Identifier'), + ), + migrations.AlterUniqueTogether( + name='outcomeartifact', + unique_together=set([]), + ), + migrations.AddIndex( + model_name='outcomeartifact', + index=models.Index(fields=['artifact_type', 'outcome'], name='osf_outcome_artifac_5eb92d_idx'), + ), + ] diff --git a/osf/migrations/0249_schema_response_justification_to_text_field.py b/osf/migrations/0249_schema_response_justification_to_text_field.py new file mode 100644 index 000000000000..846dd5bd9ef6 --- /dev/null +++ b/osf/migrations/0249_schema_response_justification_to_text_field.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.29 on 2022-08-03 13:47 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('osf', '0248_artifact_tweaks'), + ] + + operations = [ + migrations.AlterField( + model_name='schemaresponse', + name='revision_justification', + field=models.TextField(blank=True, null=True), + ), + ] diff --git a/osf/models/institution.py b/osf/models/institution.py index ab75cc6baec9..4eb671ea3c0f 100644 --- a/osf/models/institution.py +++ b/osf/models/institution.py @@ -34,6 +34,13 @@ class IntegrationType(Enum): NONE = '' # Institution affiliation is done via email domain whitelist w/o SSO +class SharedSsoAffiliationFilterCriteriaAction(Enum): + """Defines 2 criteria that determines if the secondary institution is eligible for affiliation via shared SSO. + """ + EQUALS_TO = 'equals_to' # Type 1: SSO releases a single-value attribute with an exact value that matches + CONTAINS = 'contains' # Type 2: SSO releases a multi-value attribute, of which one value matches + + class InstitutionManager(models.Manager): def get_queryset(self): diff --git a/osf/models/outcome_artifacts.py b/osf/models/outcome_artifacts.py index c459af7e08ca..a1cac753d4f8 100644 --- a/osf/models/outcome_artifacts.py +++ b/osf/models/outcome_artifacts.py @@ -79,7 +79,8 @@ class OutcomeArtifact(ObjectIDMixin, BaseModel): identifier = models.ForeignKey( 'osf.identifier', null=True, - on_delete=models.CASCADE, + blank=True, + on_delete=models.SET_NULL, related_name='artifact_metadata' ) @@ -97,9 +98,8 @@ class OutcomeArtifact(ObjectIDMixin, BaseModel): objects = ArtifactManager() class Meta: - unique_together = ('outcome', 'identifier', 'artifact_type') indexes = [ - models.Index(fields=['outcome', 'artifact_type']) + models.Index(fields=['artifact_type', 'outcome']) ] ordering = ['artifact_type', 'title'] diff --git a/osf/models/schema_response.py b/osf/models/schema_response.py index daf23680c787..8fc8bf7ea5d7 100644 --- a/osf/models/schema_response.py +++ b/osf/models/schema_response.py @@ -55,7 +55,7 @@ class SchemaResponse(ObjectIDMixin, BaseModel): on_delete=models.DO_NOTHING ) - revision_justification = models.CharField(max_length=2048, null=True, blank=True) + revision_justification = models.TextField(null=True, blank=True) submitted_timestamp = NonNaiveDateTimeField(null=True, blank=True) pending_approvers = models.ManyToManyField('osf.osfuser', related_name='pending_submissions') diff --git a/scripts/populate_institutions.py b/scripts/populate_institutions.py index f57cf651ad9b..647394084d54 100644 --- a/scripts/populate_institutions.py +++ b/scripts/populate_institutions.py @@ -589,6 +589,18 @@ def main(default_args=False): 'email_domains': [], 'delegation_protocol': 'saml-shib', }, + { + '_id': 'nationalmaglab', + 'name': 'National High Magnetic Field Laboratory', + 'description': 'This platform is provided to enable collaboration, sharing, and dissemination of research products from the National High Magnetic Field Laboratory according to the principles of FAIR and open science. All public projects must adhere to National MagLab policies & procedures related to confidentiality and proper data management.', + 'banner_name': 'nationalmaglab-banner.png', + 'logo_name': 'nationalmaglab-shield.png', + 'login_url': SHIBBOLETH_SP_LOGIN.format(encode_uri_component('https://idp.fsu.edu')), + 'logout_url': SHIBBOLETH_SP_LOGOUT.format(encode_uri_component('https://osf.io/goodbye')), + 'domains': [], + 'email_domains': [], + 'delegation_protocol': 'saml-shib', + }, { '_id': 'nesta', 'name': 'Nesta', @@ -674,6 +686,18 @@ def main(default_args=False): 'email_domains': [], 'delegation_protocol': 'saml-shib', }, + { + '_id': 'purdue', + 'name': 'Purdue University', + 'description': 'This open scholarship platform is provided by Purdue University Libraries in partnership with the University\'s Graduate School, Regulatory Affairs, and Research Integrity Office.

All projects must adhere to Purdue\'s Information security, Human subjects research policies, and related data classification and handling procedures. Associated guidance on regulations is available via the Responsible Conductof Research website and the Research Integrity Office. For questions and support please reach out to Purdue\'s OSF contact.', + 'banner_name': 'purdue-banner.png', + 'logo_name': 'purdue-shield.png', + 'login_url': SHIBBOLETH_SP_LOGIN.format(encode_uri_component('https://idp.purdue.edu/idp/shibboleth')), + 'logout_url': SHIBBOLETH_SP_LOGOUT.format(encode_uri_component('https://osf.io/goodbye')), + 'domains': [], + 'email_domains': [], + 'delegation_protocol': 'saml-shib', + }, { '_id': 'sc', 'name': 'University of South Carolina Libraries', @@ -1674,6 +1698,18 @@ def main(default_args=False): 'email_domains': [], 'delegation_protocol': 'saml-shib', }, + { + '_id': 'nationalmaglab', + 'name': 'National High Magnetic Field Laboratory [Test]', + 'description': 'This platform is provided to enable collaboration, sharing, and dissemination of research products from the National High Magnetic Field Laboratory according to the principles of FAIR and open science. All public projects must adhere to National MagLab policies & procedures related to confidentiality and proper data management.', + 'banner_name': 'nationalmaglab-banner.png', + 'logo_name': 'nationalmaglab-shield.png', + 'login_url': SHIBBOLETH_SP_LOGIN.format(encode_uri_component('https://idp.fsu.edu')), + 'logout_url': SHIBBOLETH_SP_LOGOUT.format(encode_uri_component('https://test.osf.io/goodbye')), + 'domains': ['test-osf-nationalmaglab.cos.io'], + 'email_domains': [], + 'delegation_protocol': 'saml-shib', + }, { '_id': 'nesta', 'name': 'Nesta [Test]', @@ -1759,6 +1795,18 @@ def main(default_args=False): 'email_domains': [], 'delegation_protocol': 'saml-shib', }, + { + '_id': 'purdue', + 'name': 'Purdue University [Test]', + 'description': 'This open scholarship platform is provided by Purdue University Libraries in partnership with the University\'s Graduate School, Regulatory Affairs, and Research Integrity Office.

All projects must adhere to Purdue\'s Information security, Human subjects research policies, and related data classification and handling procedures. Associated guidance on regulations is available via the Responsible Conductof Research website and the Research Integrity Office. For questions and support please reach out to Purdue\'s OSF contact.', + 'banner_name': 'purdue-banner.png', + 'logo_name': 'purdue-shield.png', + 'login_url': SHIBBOLETH_SP_LOGIN.format(encode_uri_component('https://idp.purdue.edu/idp/shibboleth')), + 'logout_url': SHIBBOLETH_SP_LOGOUT.format(encode_uri_component('https://test.osf.io/goodbye')), + 'domains': [], + 'email_domains': [], + 'delegation_protocol': 'saml-shib', + }, { '_id': 'sc', 'name': 'University of South Carolina Libraries [Test]', @@ -2192,7 +2240,7 @@ def main(default_args=False): }, { '_id': 'osftype3', - 'name': 'Fake SAML Institution - Shared SSO Primary', + 'name': 'Fake SAML Institution - Shared SSO Primary (Criteria: EQUALS_TO)', 'description': 'Fake OSF Institution Type 3. Its SSO is done via SAML (Shibboleth impl) where OSF-CAS ' 'serves as the SP and the institution as the IdP. This institution is a primary one that ' 'provides shared SSO to secondary institutions.', @@ -2206,7 +2254,7 @@ def main(default_args=False): }, { '_id': 'osftype4', - 'name': 'Fake SAML Institution - Shared SSO Secondary', + 'name': 'Fake SAML Institution - Shared SSO Secondary (Criteria: EQUALS_TO)', 'description': 'Fake OSF Institution Type 3. Its SSO is done via SAML (Shibboleth impl) where OSF-CAS ' 'serves as the SP and the institution as the IdP. This institution is a secondary one that ' 'uses a primary institution\'s SSO.', @@ -2261,6 +2309,34 @@ def main(default_args=False): 'email_domains': [], 'delegation_protocol': 'saml-shib', }, + { + '_id': 'osftype8', + 'name': 'Fake SAML Institution - Shared SSO Primary (Criteria: CONTAINS)', + 'description': 'Fake OSF Institution Type 8. Its SSO is done via SAML (Shibboleth impl) where OSF-CAS ' + 'serves as the SP and the institution as the IdP. This institution is a primary one that ' + 'provides shared SSO to secondary institutions.', + 'banner_name': 'placeholder-banner.png', + 'logo_name': 'placeholder-shield.png', + 'login_url': SHIBBOLETH_SP_LOGIN.format(encode_uri_component('type-8-fake-saml-idp')), + 'logout_url': SHIBBOLETH_SP_LOGOUT.format(encode_uri_component('http://localhost:5000/goodbye')), + 'domains': [], + 'email_domains': [], + 'delegation_protocol': 'saml-shib', + }, + { + '_id': 'osftype9', + 'name': 'Fake SAML Institution - Shared SSO Secondary (Criteria: CONTAINS)', + 'description': 'Fake OSF Institution Type 9. Its SSO is done via SAML (Shibboleth impl) where OSF-CAS ' + 'serves as the SP and the institution as the IdP. This institution is a secondary one that ' + 'uses a primary institution\'s SSO.', + 'banner_name': 'placeholder-banner.png', + 'logo_name': 'placeholder-shield.png', + 'login_url': SHIBBOLETH_SP_LOGIN.format(encode_uri_component('type-9-fake-saml-idp')), + 'logout_url': SHIBBOLETH_SP_LOGOUT.format(encode_uri_component('http://localhost:5000/goodbye')), + 'domains': [], + 'email_domains': [], + 'delegation_protocol': 'saml-shib', + }, ], } diff --git a/website/project/decorators.py b/website/project/decorators.py index af16941afefc..733a02e4a02a 100644 --- a/website/project/decorators.py +++ b/website/project/decorators.py @@ -465,7 +465,7 @@ def check_contributor_auth(node, auth, include_public, include_view_only_anon, i redirect_url = check_key_expired(key=auth.private_key, node=node, url=request.url) if request.headers.get('Content-Type') == 'application/json': raise HTTPError(http_status.HTTP_401_UNAUTHORIZED) - else: + elif user is None: response = redirect(cas.get_login_url(redirect_url)) return response diff --git a/website/static/img/institutions/banners/nationalmaglab-banner.png b/website/static/img/institutions/banners/nationalmaglab-banner.png new file mode 100644 index 000000000000..3b2ea21d906b Binary files /dev/null and b/website/static/img/institutions/banners/nationalmaglab-banner.png differ diff --git a/website/static/img/institutions/banners/purdue-banner.png b/website/static/img/institutions/banners/purdue-banner.png new file mode 100644 index 000000000000..86dfa88b798d Binary files /dev/null and b/website/static/img/institutions/banners/purdue-banner.png differ diff --git a/website/static/img/institutions/shields-rounded-corners/colorado-shield-rounded-corners.png b/website/static/img/institutions/shields-rounded-corners/colorado-shield-rounded-corners.png index eb66ba69492f..0b8e0904b888 100644 Binary files a/website/static/img/institutions/shields-rounded-corners/colorado-shield-rounded-corners.png and b/website/static/img/institutions/shields-rounded-corners/colorado-shield-rounded-corners.png differ diff --git a/website/static/img/institutions/shields-rounded-corners/nationalmaglab-shield-rounded-corners.png b/website/static/img/institutions/shields-rounded-corners/nationalmaglab-shield-rounded-corners.png new file mode 100644 index 000000000000..e841eded429b Binary files /dev/null and b/website/static/img/institutions/shields-rounded-corners/nationalmaglab-shield-rounded-corners.png differ diff --git a/website/static/img/institutions/shields-rounded-corners/purdue-shield-rounded-corners.png b/website/static/img/institutions/shields-rounded-corners/purdue-shield-rounded-corners.png new file mode 100644 index 000000000000..125c22e11e44 Binary files /dev/null and b/website/static/img/institutions/shields-rounded-corners/purdue-shield-rounded-corners.png differ diff --git a/website/static/img/institutions/shields/colorado-shield.png b/website/static/img/institutions/shields/colorado-shield.png index 81cfbee684df..8ce1b8edf31d 100644 Binary files a/website/static/img/institutions/shields/colorado-shield.png and b/website/static/img/institutions/shields/colorado-shield.png differ diff --git a/website/static/img/institutions/shields/nationalmaglab-shield.png b/website/static/img/institutions/shields/nationalmaglab-shield.png new file mode 100644 index 000000000000..c0a2e350ee40 Binary files /dev/null and b/website/static/img/institutions/shields/nationalmaglab-shield.png differ diff --git a/website/static/img/institutions/shields/purdue-shield.png b/website/static/img/institutions/shields/purdue-shield.png new file mode 100644 index 000000000000..45bd49219561 Binary files /dev/null and b/website/static/img/institutions/shields/purdue-shield.png differ