def test_logout(client): """Test to logout.""" org = Organisation.create(name="THE ORGANISATION:test_logout", tuakiri_name="University of Auckland", confirmed=True, is_email_sent=True) user = User.create(email="*****@*****.**", name="TEST USER", roles=Role.TECHNICAL, confirmed=True, organisation=org) client.login(user) resp = client.get("/logout") # UoA user: assert resp.status_code == 302 assert "Shibboleth.sso" in resp.location assert "uoa-slo" in resp.location org.tuakiri_name = org.name org.save() client.login(user) resp = client.get("/logout") # non-UoA user: assert resp.status_code == 302 assert "Shibboleth.sso" in resp.location assert "uoa-slo" not in resp.location
def test_orcid_login(request_ctx): """Test login from orcid.""" org = Organisation.create(name="THE ORGANISATION", tuakiri_name="THE ORGANISATION", confirmed=False, orcid_client_id="CLIENT ID", orcid_secret="Client Secret", city="CITY", country="COUNTRY", disambiguated_id="ID", disambiguation_source="SOURCE", is_email_sent=True) u = User.create(email="*****@*****.**", name="TEST USER", roles=Role.TECHNICAL, orcid="123", confirmed=True, organisation=org) UserOrg.create(user=u, org=org, is_admin=True) token = utils.generate_confirmation_token(email=u.email, org=org.name) with request_ctx("/orcid/login/" + token.decode("utf-8")) as ctxx: rv = ctxx.app.full_dispatch_request() assert rv.status_code == 200 orcid_authorize = OrcidAuthorizeCall.get(method="GET") assert "&email=test123%40test.test.net" in orcid_authorize.url
def test_orcid_callback(client, mocker): """Test orcid researcher deny flow.""" org = Organisation.create( name="THE ORGANISATION:test_orcid_callback", tuakiri_name="THE ORGANISATION:test_orcid_callback", confirmed=True, orcid_client_id="CLIENT ID", orcid_secret="Client Secret", city="CITY", country="COUNTRY", disambiguated_id="ID", disambiguation_source="RINGGOLD", is_email_sent=True) user = User.create(email="*****@*****.**", name="TEST USER", roles=Role.TECHNICAL, orcid="123", confirmed=True, organisation=org) UserOrg.create(user=user, org=org, is_admin=True) client.login(user) resp = client.get("/auth?error=access_denied&login=2") assert resp.status_code == 302 assert "/link" in resp.location
def test_orcid_login_callback_researcher_flow(patch, patch2, request_ctx): """Test login from orcid callback function for researcher and display profile.""" org = Organisation.create(name="THE ORGANISATION", tuakiri_name="THE ORGANISATION", confirmed=True, orcid_client_id="CLIENT ID", orcid_secret="Client Secret", city="CITY", country="COUNTRY", disambiguated_id="ID", disambiguation_source="SOURCE", is_email_sent=True) u = User.create(email="*****@*****.**", name="TEST USER", roles=Role.RESEARCHER, orcid="123", confirmed=True, organisation=org) UserOrg.create(user=u, org=org, is_admin=False) token = utils.generate_confirmation_token(email=u.email, org=org.name) UserInvitation.create(email=u.email, token=token, affiliations=Affiliation.EMP) OrcidToken.create(user=u, org=org, scope='/read-limited,/activities/update') with request_ctx(): request.args = {"invitation_token": token, "state": "xyz"} session['oauth_state'] = "xyz" resp = authcontroller.orcid_login_callback(request) assert resp.status_code == 302 # display profile assert resp.location.startswith("/profile")
def test_link_orcid_auth_callback_with_affiliation(name, request_ctx): """Test ORCID callback - the user authorized the organisation access to the ORCID profile.""" with patch("orcid_hub.orcid_client.MemberAPI") as m, patch( "orcid_hub.orcid_client.SourceClientId"), request_ctx("/auth?state=xyz") as ctx: org = Organisation.create( name="THE ORGANISATION", confirmed=True, orcid_client_id="CLIENT ID", city="CITY", country="COUNTRY", disambiguated_id="ID", disambiguation_source="SOURCE") test_user = User.create( name=name, email="*****@*****.**", organisation=org, orcid="ABC123", confirmed=True) UserOrg.create(user=test_user, org=org, affiliations=Affiliation.EMP | Affiliation.EDU) login_user(test_user, remember=True) session['oauth_state'] = "xyz" api_mock = m.return_value ctx.app.full_dispatch_request() assert test_user.orcid == "ABC-123-456-789" orcid_token = OrcidToken.get(user=test_user, org=org) assert orcid_token.access_token == "ABC123" api_mock.create_or_update_affiliation.assert_has_calls([ call(affiliation=Affiliation.EDU, initial=True), call(affiliation=Affiliation.EMP, initial=True), ])
def test_link(request_ctx): """Test orcid profile linking.""" org = Organisation.create(name="THE ORGANISATION", tuakiri_name="THE ORGANISATION", confirmed=True, orcid_client_id="CLIENT ID", orcid_secret="Client Secret", city="CITY", country="COUNTRY", disambiguated_id="ID", disambiguation_source="SOURCE", is_email_sent=True) user = User.create(email="*****@*****.**", name="TEST USER", roles=Role.TECHNICAL, orcid="123", confirmed=True, organisation=org) UserOrg.create(user=user, org=org, is_admin=True) with request_ctx("/link") as ctx: login_user(user, remember=True) request.args = ImmutableMultiDict([('error', 'access_denied')]) rv = ctx.app.full_dispatch_request() assert rv.status_code == 200 assert b"<!DOCTYPE html>" in rv.data, "Expected HTML content"
def test_orcid_callback(request_ctx): """Test orcid researcher deny flow.""" org = Organisation.create(name="THE ORGANISATION", tuakiri_name="THE ORGANISATION", confirmed=True, orcid_client_id="CLIENT ID", orcid_secret="Client Secret", city="CITY", country="COUNTRY", disambiguated_id="ID", disambiguation_source="SOURCE", is_email_sent=True) user = User.create(email="*****@*****.**", name="TEST USER", roles=Role.TECHNICAL, orcid="123", confirmed=True, organisation=org) UserOrg.create(user=user, org=org, is_admin=True) with request_ctx("/auth") as ctx: login_user(user, remember=True) request.args = ImmutableMultiDict([('error', 'access_denied'), ('login', '2')]) rv = ctx.app.full_dispatch_request() assert rv.status_code == 302 assert rv.location.startswith("/link")
def test_select_user_org(request_ctx): """Test organisation switch of current user.""" org = Organisation.create( name="THE ORGANISATION:test_select_user_org", tuakiri_name="THE ORGANISATION:test_select_user_org", confirmed=True, orcid_client_id="CLIENT ID", orcid_secret="Client Secret", city="CITY", country="COUNTRY", disambiguated_id="ID", disambiguation_source="RINGGOLD", is_email_sent=True) org2 = Organisation.create( name="THE ORGANISATION2:test_select_user_org", tuakiri_name="THE ORGANISATION2:test_select_user_org", confirmed=True, orcid_client_id="CLIENT ID", orcid_secret="Client Secret", city="CITY", country="COUNTRY", disambiguated_id="ID", disambiguation_source="RINGGOLD", is_email_sent=True) user = User.create( email="*****@*****.**", name="TEST USER", roles=Role.TECHNICAL, orcid="123", confirmed=True, organisation=org) org.save() org2.save() user.save() UserOrg.create(user=user, org=org, is_admin=True) user_org2 = UserOrg.create(user=user, org=org2, is_admin=True) with request_ctx(f"/select/user_org/{user_org2.id}") as ctx: login_user(user, remember=True) resp = ctx.app.full_dispatch_request() assert resp.status_code == 302 assert user.organisation_id != org.id # Current users organisation has been changes from 1 to 2 assert user.organisation_id == org2.id
def test_profile_wo_orcid(request_ctx): """Test a user profile that doesn't hava an ORCID.""" with request_ctx("/profile") as ctx: org = Organisation.create(name="THE ORGANISATION:test_profile", confirmed=True) test_user = User.create( email="*****@*****.**", organisation=org, orcid=None, confirmed=True) login_user(test_user, remember=True) resp = ctx.app.full_dispatch_request() assert resp.status_code == 302 assert resp.location == url_for("link")
def test_link(request_ctx): """Test a user affiliation initialization.""" with request_ctx("/link") as ctx: org = Organisation.create(name="THE ORGANISATION", confirmed=True) test_user = User.create( name="TEST USER 123", email="*****@*****.**", organisation=org, confirmed=True) login_user(test_user, remember=True) rv = ctx.app.full_dispatch_request() assert b"<!DOCTYPE html>" in rv.data, "Expected HTML content" assert b"TEST USER 123" in rv.data, "Expected to have the user name on the page" assert b"*****@*****.**" in rv.data, "Expected to have the user email on the page" assert b"URL_123" in rv.data, "Expected to have ORCiD authorization link on the page"
def test_sync_profile(app, mocker): """Test sync_profile.""" mocker.patch("orcid_api.MemberAPIV20Api.update_employment", return_value=Mock(status=201, headers={'Location': '12344/XYZ/54321'})) mocker.patch("orcid_api.MemberAPIV20Api.update_education", return_value=Mock(status=201, headers={'Location': '12344/XYZ/12345'})) org = Organisation.create( name="THE ORGANISATION:test_sync_profile", tuakiri_name="THE ORGANISATION:test_sync_profile", confirmed=True, orcid_client_id="APP-5ZVH4JRQ0C27RVH5", orcid_secret="Client Secret", city="CITY", country="COUNTRY", disambiguated_id="ID", disambiguation_source="SOURCE") u = User.create(email="*****@*****.**", name="TEST USER", roles=Role.RESEARCHER, orcid="12344", confirmed=True, organisation=org) UserOrg.create(user=u, org=org) access_token = "ACCESS-TOKEN" t = Task.create(org=org, task_type=TaskType.SYNC) api = MemberAPI(org=org) mocker.patch("orcid_hub.orcid_client.MemberAPI.get_record", lambda *args: None) api.sync_profile(task=t, user=u, access_token=access_token) OrcidToken.create(user=u, org=org, scope="/read-limited,/activities/update") mocker.patch("orcid_hub.orcid_client.MemberAPI.get_record", lambda *args: None) api.sync_profile(task=t, user=u, access_token=access_token) assert Log.select().count() > 0 mocker.patch("orcid_hub.orcid_client.MemberAPI.get_record", return_value=get_profile()) api.sync_profile(task=t, user=u, access_token=access_token) last_log = Log.select().order_by(Log.id.desc()).first() assert "Successfully update" in last_log.message
def test_load_task_from_csv(models): org = Organisation.create(name="TEST0") # flake8: noqa test = Task.load_from_csv( """First name Last name email address Organisation Campus/Department City Course or Job title Start date End date Student/Staff FNA LBA [email protected] TEST1 Research Funding Wellington Programme Manager - ORCID 2016-09 Staff FNA LBA [email protected] TEST1 Research Funding Wellington Programme Manager - Insights and Evaluation 2014 Staff FNA LBA [email protected] TEST0 External Affairs Wellington Senior Evaluation Officer 2011 2014 Staff FNA LBA [email protected] TEST0 Policy and Evaluation Wellington Evaluation Officer 2005 2011 Staff FNA LBA [email protected] TEST0 Marsden Fund Wellington Research Assessor 2001 2004 Staff FNB LNB [email protected] TEST1 Communications and Outreach Wellington Projects and Events Coordinator 2013 Staff FNB LNB [email protected] TEST0 Science and Education Group Wellington School Programmes Manager 2008 2013 Staff FNB LNB TEST_FN TEST_LN <*****@*****.**> TEST0 Science and Education Group Wellington Project Manager 2000 2004 Staff FNB LNB [email protected] TEST0 Science and Education Group Wellington Manager Special Programmes 2004 2008 Staff """, filename="TEST.tsv", org=org) assert test.record_count == 9 assert AffiliationRecord.select().count( ) == test.record_count + 10 # The 10 value is from already inserted entries.
def test_logout(request_ctx): """Test to logout.""" user = User.create(email="*****@*****.**", name="TEST USER", roles=Role.TECHNICAL, confirmed=True, organisation=Organisation.create( name="THE ORGANISATION", tuakiri_name="THE ORGANISATION", confirmed=True, is_email_sent=True)) with request_ctx("/logout") as ctx: # UoA user: login_user(user) session["shib_O"] = "University of Auckland" rv = ctx.app.full_dispatch_request() assert rv.status_code == 302 assert "Shibboleth.sso" in rv.location assert "uoa-slo" in rv.location
def test_is_emp_or_edu_record_present(app, mocker): """Test 'is_emp_or_edu_record_present' method.""" mocker.patch.multiple("orcid_hub.app.logger", error=DEFAULT, exception=DEFAULT, info=DEFAULT) org = Organisation.create(name="THE ORGANISATION", confirmed=True, orcid_client_id="CLIENT000") user = User.create( orcid="1001-0001-0001-0001", name="TEST USER 123", email="*****@*****.**", organisation=org, confirmed=True) UserOrg.create(user=user, org=org, affiliation=Affiliation.EDU) api = MemberAPI(user=user, org=org) test_responses = [ None, """{"mock": "data"}""", """{ "employment-summary": [{"source": {"source-client-id": {"path": "CLIENT000"}}, "put-code": 123}], "education-summary": [{"source": {"source-client-id": {"path": "CLIENT000"}}, "put-code": 456}] }""", """{"employment-summary": [], "education-summary": []}""" ] for data in test_responses: with patch.object( api_client.ApiClient, "call_api", return_value=Mock(data=data)) as call_api: api.is_emp_or_edu_record_present(Affiliation.EDU) call_api.assert_called_with( "/v2.0/{orcid}/educations", "GET", {"orcid": "1001-0001-0001-0001"}, {}, {"Accept": "application/json"}, _preload_content=False, _request_timeout=None, _return_http_data_only=True, auth_settings=["orcid_auth"], body=None, callback=None, collection_formats={}, files={}, post_params=[], response_type="Educations") api.is_emp_or_edu_record_present(Affiliation.EMP) call_api.assert_called_with( "/v2.0/{orcid}/employments", "GET", {"orcid": "1001-0001-0001-0001"}, {}, {"Accept": "application/json"}, _preload_content=False, _request_timeout=None, _return_http_data_only=True, auth_settings=["orcid_auth"], body=None, callback=None, collection_formats={}, files={}, post_params=[], response_type="Employments") with patch.object( api_client.ApiClient, "call_api", side_effect=ApiException( reason="FAILURE", status=401)) as call_api: api.is_emp_or_edu_record_present(Affiliation.EDU) app.logger.error.assert_called_with( "For TEST USER 123 ([email protected]) while checking for employment " "and education records, Encountered Exception: (401)\nReason: FAILURE\n") with patch.object( api_client.ApiClient, "call_api", side_effect=Exception("EXCEPTION")) as call_api: api.is_emp_or_edu_record_present(Affiliation.EDU) app.logger.exception.assert_called_with( "Failed to verify presence of employment or education record.")
def test_create_or_update_affiliation(patch, test_db, request_ctx): """Test create or update affiliation.""" org = Organisation.create(name="THE ORGANISATION", tuakiri_name="THE ORGANISATION", confirmed=True, orcid_client_id="APP-5ZVH4JRQ0C27RVH5", orcid_secret="Client Secret", city="CITY", country="COUNTRY", disambiguation_org_id="ID", disambiguation_org_source="SOURCE") u = User.create(email="*****@*****.**", name="TEST USER", username="******", roles=Role.RESEARCHER, orcid="123", confirmed=True, organisation=org) UserOrg.create(user=u, org=org) t = Task.create(org=org, filename="xyz.json", created_by=u, updated_by=u, task_type=0) AffiliationRecord.create(is_active=True, task=t, external_id="Test", first_name="Test", last_name="Test", email="*****@*****.**", orcid="123112311231", organisation="asdasd", affiliation_type="staff", role="Test", department="Test", city="Test", state="Test", country="Test", disambiguated_id="Test", disambiguation_source="Test") UserInvitation.create(invitee=u, inviter=u, org=org, task=t, email="*****@*****.**", token="xyztoken") OrcidToken.create(user=u, org=org, scope="/read-limited,/activities/update", access_token="Test_token") tasks = (Task.select( Task, AffiliationRecord, User, UserInvitation.id.alias("invitation_id"), OrcidToken ).where( AffiliationRecord.processed_at.is_null(), AffiliationRecord.is_active, ((User.id.is_null(False) & User.orcid.is_null(False) & OrcidToken.id.is_null(False)) | ((User.id.is_null() | User.orcid.is_null() | OrcidToken.id.is_null()) & UserInvitation.id.is_null() & (AffiliationRecord.status.is_null() | AffiliationRecord.status.contains("sent").__invert__())))).join( AffiliationRecord, on=(Task.id == AffiliationRecord.task_id)).join( User, JOIN.LEFT_OUTER, on=((User.email == AffiliationRecord.email) | (User.orcid == AffiliationRecord.orcid))).join( Organisation, JOIN.LEFT_OUTER, on=(Organisation.id == Task.org_id)). join(UserInvitation, JOIN.LEFT_OUTER, on=((UserInvitation.email == AffiliationRecord.email) & (UserInvitation.task_id == Task.id))).join( OrcidToken, JOIN.LEFT_OUTER, on=((OrcidToken.user_id == User.id) & (OrcidToken.org_id == Organisation.id) & (OrcidToken.scope.contains("/activities/update")) )).limit(20)) for (task_id, org_id, user), tasks_by_user in groupby( tasks, lambda t: ( t.id, t.org_id, t.affiliation_record.user, )): utils.create_or_update_affiliations(user=user, org_id=org_id, records=tasks_by_user) affiliation_record = AffiliationRecord.get(task=t) assert 12399 == affiliation_record.put_code assert "12344" == affiliation_record.orcid assert "Employment record was updated" in affiliation_record.status
def test_send_email(app): """Test emailing.""" with app.app_context(): # app.config["SERVER_NAME"] = "ORCIDHUB" with patch("emails.message.Message") as msg_cls, patch( "flask.current_app.jinja_env"): msg = msg_cls.return_value = Mock() utils.send_email("template.html", ( "TEST USER", "*****@*****.**", ), subject="TEST") msg_cls.assert_called_once() msg.send.assert_called_once() msg.reset_mock() dkip_key_path = app.config["DKIP_KEY_PATH"] app.config["DKIP_KEY_PATH"] = __file__ utils.send_email("template", ( "TEST USER", "*****@*****.**", ), base="BASE", subject="TEST") msg.dkim.assert_called_once() msg.send.assert_called_once() msg.reset_mock() app.config["DKIP_KEY_PATH"] = "NON-EXISTING FILE..." utils.send_email("template", ( "TEST USER", "*****@*****.**", ), base="BASE", subject="TEST") msg.dkim.assert_not_called() msg.send.assert_called_once() app.config["DKIP_KEY_PATH"] = dkip_key_path # User organisation's logo msg.reset_mock() logo_file = File.create(filename="LOGO.png", data=b"000000000000000000000", mimetype="image/png", token="TOKEN000") org = Organisation.create(name="THE ORGANISATION", tuakiri_name="THE ORGANISATION", confirmed=True, orcid_client_id="APP-5ZVH4JRQ0C27RVH5", orcid_secret="Client Secret", city="CITY", logo=logo_file, country="COUNTRY", disambiguation_org_id="ID", disambiguation_org_source="SOURCE") utils.send_email("template", ( "TEST USER", "*****@*****.**", ), base="BASE {LOGO}", subject="TEST WITH BASE AND LOGO", org=org) msg.send.assert_called_once() _, kwargs = msg_cls.call_args assert kwargs["subject"] == "TEST WITH BASE AND LOGO" assert kwargs["mail_from"] == ( "NZ ORCID HUB", "*****@*****.**", ) expected_html = f"BASE http://{app.config['SERVER_NAME'].lower()}/logo/TOKEN000" assert kwargs["html"] == expected_html assert kwargs["text"] == expected_html + "\n\n" # Using organisation template msg.reset_mock() org.email_template = "TEMPLATE {LOGO}" org.email_template_enabled = True org.save() utils.send_email("template", ( "TEST USER", "*****@*****.**", ), sender=( None, None, ), subject="TEST WITH ORG BASE AND LOGO", org=org) msg.send.assert_called_once() _, kwargs = msg_cls.call_args assert kwargs["subject"] == "TEST WITH ORG BASE AND LOGO" assert kwargs["mail_from"] == ( "NZ ORCID HUB", "*****@*****.**", ) expected_html = f"TEMPLATE http://{app.config['SERVER_NAME'].lower()}/logo/TOKEN000" assert kwargs["html"] == expected_html assert kwargs["text"] == expected_html + "\n\n" # temlates w/o extension and missing template file # login_user(super_user) from jinja2.exceptions import TemplateNotFound with pytest.raises(TemplateNotFound): utils.send_email("missing_template.html", ( "TEST USER", "*****@*****.**", ), logo="LOGO", subject="TEST")
def test_create_or_update_work(email_patch, patch, test_db, request_ctx): """Test create or update work.""" org = Organisation.create(name="THE ORGANISATION", tuakiri_name="THE ORGANISATION", confirmed=True, orcid_client_id="APP-5ZVH4JRQ0C27RVH5", orcid_secret="Client Secret", city="CITY", country="COUNTRY", disambiguation_org_id="ID", disambiguation_org_source="SOURCE") u = User.create(email="*****@*****.**", name="TEST USER", username="******", roles=Role.RESEARCHER, orcid="12344", confirmed=True, organisation=org) UserOrg.create(user=u, org=org) t = Task.create(org=org, filename="xyz.json", created_by=u, updated_by=u, task_type=2) wr = WorkRecord.create(task=t, title="Test titile", sub_title="Test titile", translated_title="Test title", translated_title_language_code="Test", journal_title="Test titile", short_description="Test desc", citation_type="Test", citation_value="Test", type="BOOK_CHAPTER", url="Test org", language_code="en", country="Test", org_name="Test_orgname", city="Test city", region="Test", is_active=True) WorkInvitees.create(work_record=wr, first_name="Test", email="*****@*****.**", orcid="12344", visibility="PUBLIC") WorkExternalId.create(work_record=wr, type="Test_type", value="Test_value", url="Test", relationship="SELF") WorkContributor.create(work_record=wr, contributor_sequence="1", orcid="1213", role="LEAD", name="xyz", email="*****@*****.**") UserInvitation.create(invitee=u, inviter=u, org=org, task=t, email="*****@*****.**", token="xyztoken") OrcidToken.create(user=u, org=org, scope="/read-limited,/activities/update", access_token="Test_token") utils.process_work_records() work_invitees = WorkInvitees.get(orcid=12344) assert 12399 == work_invitees.put_code assert "12344" == work_invitees.orcid
def test_create_or_update_peer_review(email_patch, patch, test_db, request_ctx): """Test create or update peer review.""" org = Organisation.create(name="THE ORGANISATION", tuakiri_name="THE ORGANISATION", confirmed=True, orcid_client_id="APP-5ZVH4JRQ0C27RVH5", orcid_secret="Client Secret", city="CITY", country="COUNTRY", disambiguation_org_id="ID", disambiguation_org_source="SOURCE") u = User.create(email="*****@*****.**", name="TEST USER", username="******", roles=Role.RESEARCHER, orcid="12344", confirmed=True, organisation=org) UserOrg.create(user=u, org=org) t = Task.create(id=12, org=org, filename="xyz.json", created_by=u, updated_by=u, task_type=3) pr = PeerReviewRecord.create(task=t, review_group_id="issn:12131", reviewer_role="reviewer", review_url="xyz", review_type="REVIEW", subject_external_id_type="doi", subject_external_id_value="1212", subject_external_id_url="url/SELF", subject_external_id_relationship="SELF", subject_container_name="Journal title", subject_type="JOURNAL_ARTICLE", subject_name_title="name", subject_name_subtitle="subtitle", subject_name_translated_title_lang_code="en", subject_name_translated_title="sdsd", subject_url="url", convening_org_name="THE ORGANISATION", convening_org_city="auckland", convening_org_region="auckland", convening_org_country="nz", convening_org_disambiguated_identifier="123", convening_org_disambiguation_source="1212", is_active=True) PeerReviewInvitee.create(peer_review_record=pr, first_name="Test", email="*****@*****.**", orcid="12344", visibility="PUBLIC") PeerReviewExternalId.create(peer_review_record=pr, type="Test_type", value="122334_different", url="Test", relationship="SELF") UserInvitation.create(invitee=u, inviter=u, org=org, task=t, email="*****@*****.**", token="xyztoken") OrcidToken.create(user=u, org=org, scope="/read-limited,/activities/update", access_token="Test_token") utils.process_peer_review_records() peer_review_invitees = PeerReviewInvitee.get(orcid=12344) assert 12399 == peer_review_invitees.put_code assert "12344" == peer_review_invitees.orcid
def test_send_user_invitation(test_db, request_ctx): """Test to send user invitation.""" org = Organisation.create(name="THE ORGANISATION", tuakiri_name="THE ORGANISATION", confirmed=True, orcid_client_id="CLIENT ID", orcid_secret="Client Secret", city="CITY", country="COUNTRY", disambiguation_org_id="ID", disambiguation_org_source="SOURCE") inviter = User.create(email="*****@*****.**", name="TEST USER", username="******", roles=Role.RESEARCHER, orcid=None, confirmed=True, organisation=org) u = User(email="*****@*****.**", name="TEST USER", username="******", roles=Role.RESEARCHER, orcid=None, confirmed=True, organisation=org) u.save() user_org = UserOrg(user=u, org=org) user_org.save() task = Task(id=123, org=org) task.save() email = "*****@*****.**" first_name = "TEST" last_name = "Test" affiliation_types = {"staff"} with patch("smtplib.SMTP") as mock_smtp, request_ctx("/") as ctxx: instance = mock_smtp.return_value error = { email: (450, "Requested mail action not taken: mailbox unavailable") } instance.utils.send_user_invitation.return_value = error result = instance.utils.send_user_invitation( inviter=inviter, org=org, email=email, first_name=first_name, last_name=last_name, affiliation_types=affiliation_types, task_id=task.id) rv = ctxx.app.full_dispatch_request() assert rv.status_code == 200 assert instance.utils.send_user_invitation.called # noqa: E712 assert (450, 'Requested mail action not taken: mailbox unavailable' ) == result[email] with patch("orcid_hub.utils.send_email") as send_email: result = utils.send_user_invitation( inviter=inviter.id, org=org.id, email=email, first_name=first_name, last_name=last_name, affiliation_types=affiliation_types, start_date=[1971, 1, 1], end_date=[2018, 5, 29], task_id=task.id) send_email.assert_called_once() assert result == UserInvitation.select().order_by( UserInvitation.id.desc()).first().id
def test_create_or_update_funding(email_patch, patch, test_db, request_ctx): """Test create or update funding.""" org = Organisation.create(name="THE ORGANISATION", tuakiri_name="THE ORGANISATION", confirmed=True, orcid_client_id="APP-5ZVH4JRQ0C27RVH5", orcid_secret="Client Secret", city="CITY", country="COUNTRY", disambiguation_org_id="ID", disambiguation_org_source="SOURCE") u = User.create(email="*****@*****.**", name="TEST USER", username="******", roles=Role.RESEARCHER, orcid="123", confirmed=True, organisation=org) UserOrg.create(user=u, org=org) t = Task.create(org=org, filename="xyz.json", created_by=u, updated_by=u, task_type=1) fr = FundingRecord.create(task=t, title="Test titile", translated_title="Test title", translated_title_language_code="Test", type="GRANT", organization_defined_type="Test org", short_description="Test desc", amount="1000", currency="USD", org_name="Test_orgname", city="Test city", region="Test", country="Test", disambiguated_org_identifier="Test_dis", disambiguation_source="Test_source", is_active=True) FundingInvitees.create(funding_record=fr, first_name="Test", email="*****@*****.**", visibility="PUBLIC", orcid="123") ExternalId.create(funding_record=fr, type="Test_type", value="Test_value", url="Test", relationship="SELF") FundingContributor.create(funding_record=fr, orcid="1213", role="LEAD", name="Contributor", email="*****@*****.**") UserInvitation.create(invitee=u, inviter=u, org=org, task=t, email="*****@*****.**", token="xyztoken") OrcidToken.create(user=u, org=org, scope="/read-limited,/activities/update", access_token="Test_token") utils.process_funding_records() funding_invitees = FundingInvitees.get(orcid=12344) assert 12399 == funding_invitees.put_code assert "12344" == funding_invitees.orcid
def test_sync_profile(app, mocker): """Test sync_profile.""" mocker.patch( "orcid_api_v3.api.DevelopmentMemberAPIV30Api.update_employmentv3", return_value=Mock(status=201, headers={'Location': '12344/XYZ/54321'})) mocker.patch( "orcid_api_v3.api.DevelopmentMemberAPIV30Api.update_educationv3", return_value=Mock(status=201, headers={'Location': '12344/XYZ/12345'})) def sync_profile_mock(*args, **kwargs): utils.sync_profile(*args, **kwargs) return Mock(id="test-test-test-test") mocker.patch("orcid_hub.utils.sync_profile.queue", sync_profile_mock) org = Organisation.create( name="THE ORGANISATION:test_sync_profile", tuakiri_name="THE ORGANISATION:test_sync_profile", confirmed=True, orcid_client_id="APP-5ZVH4JRQ0C27RVH5", orcid_secret="Client Secret", city="CITY", country="NZ", disambiguated_id="ID", disambiguation_source="SOURCE") u = User.create(email="*****@*****.**", name="TEST USER", roles=Role.RESEARCHER, orcid="12344", confirmed=True, organisation=org) UserOrg.create(user=u, org=org) utils.sync_profile(task_id=999999) t = Task.create(org=org, task_type=TaskType.SYNC) mocker.patch("orcid_hub.orcid_client.MemberAPIV3.get_record", lambda *args: None) utils.sync_profile(task_id=t.id, delay=0) resp = get_profile() mocker.patch("orcid_hub.orcid_client.MemberAPIV3.get_record", lambda *args: resp) utils.sync_profile(task_id=t.id, delay=0) resp["activities-summary"]["educations"]["affiliation-group"] = [] mocker.patch("orcid_hub.orcid_client.MemberAPIV3.get_record", lambda *args: resp) utils.sync_profile(task_id=t.id, delay=0) mocker.patch("orcid_hub.orcid_client.MemberAPIV3.update_employmentv3", side_effect=Exception("FAILED")) utils.sync_profile(task_id=t.id, delay=0) resp["activities-summary"]["employments"]["affiliation-group"][0][ "summaries"][0]["employment-summary"]["source"] = None resp["activities-summary"]["employments"]["affiliation-group"][0][ "summaries"][0]["employment-summary"]["source"] = None mocker.patch("orcid_hub.orcid_client.MemberAPIV3.get_record", lambda *args: resp) utils.sync_profile(task_id=t.id, delay=0) org.disambiguated_id = None org.save() utils.sync_profile(task_id=t.id, delay=0) assert Log.select().count() > 0
def test_member_api(app, mocker): """Test MemberAPI extension and wrapper of ORCID API.""" mocker.patch.multiple("orcid_hub.app.logger", error=DEFAULT, exception=DEFAULT, info=DEFAULT) org = Organisation.create(name="THE ORGANISATION", confirmed=True, orcid_client_id="CLIENT000") user = User.create( orcid="1001-0001-0001-0001", name="TEST USER 123", email="*****@*****.**", organisation=org, confirmed=True) UserOrg.create(user=user, org=org, affiliation=Affiliation.EDU) MemberAPI(user=user) assert configuration.access_token is None or configuration.access_token == '' MemberAPI(user=user, org=org) assert configuration.access_token is None or configuration.access_token == '' MemberAPI(user=user, org=org, access_token="ACCESS000") assert configuration.access_token == 'ACCESS000' OrcidToken.create( access_token="ACCESS123", user=user, org=org, scope="/read-limited,/activities/update", expires_in='121') api = MemberAPI(user=user, org=org) assert configuration.access_token == "ACCESS123" with patch.object( api_client.ApiClient, "call_api", side_effect=ApiException( reason="FAILURE", status=401)) as call_api: with patch.object(OrcidToken, "delete") as delete: api.get_record() app.logger.error.assert_called_with("ApiException Occured: (401)\nReason: FAILURE\n") call_api.assert_called_once() delete.assert_called_once() with patch.object( api_client.ApiClient, "call_api", side_effect=ApiException(reason="FAILURE 999", status=999)) as call_api: api.get_record() app.logger.error.assert_called_with("ApiException Occured: (999)\nReason: FAILURE 999\n") with patch.object( api_client.ApiClient, "call_api", side_effect=ApiException( reason="FAILURE", status=401)) as call_api: with patch.object(OrcidToken, "get", side_effect=Exception("FAILURE")) as get: api.get_record() app.logger.exception.assert_called_with( "Exception occured while retriving ORCID Token") call_api.assert_called_once() get.assert_called_once() with patch.object( api_client.ApiClient, "call_api", return_value=( Mock(data=b"""{"mock": "data"}"""), 200, [], )) as call_api: api.get_record() call_api.assert_called_with( f"/v2.0/{user.orcid}", "GET", _preload_content=False, auth_settings=["orcid_auth"], header_params={"Accept": "application/json"}, response_type=None) # Test API call auditing: with patch.object( api_client.RESTClientObject.__base__, "request", return_value=Mock(data=b"""{"mock": "data"}""", status_code=200)) as request_mock: api.get_record() request_mock.assert_called_once_with( _preload_content=False, _request_timeout=None, body=None, headers={ "Accept": "application/json", "User-Agent": "Swagger-Codegen/1.0.0/python", "Authorization": "Bearer ACCESS123" }, method="GET", post_params=None, query_params=None, url="https://api.sandbox.orcid.org/v2.0/1001-0001-0001-0001") api_call = OrcidApiCall.select().first() assert api_call.response == '{"mock": "data"}' assert api_call.url == "https://api.sandbox.orcid.org/v2.0/1001-0001-0001-0001" with patch.object(OrcidApiCall, "create", side_effect=Exception("FAILURE")) as create: api.get_record() create.assert_called_once() with patch.object( api_client.RESTClientObject.__base__, "request", return_value=Mock(data=None, status_code=200)) as request_mock: # api.get_record() OrcidApiCall.delete().execute() api.view_person("1234-XXXX-XXXX-XXXX") api_call = OrcidApiCall.select().first() assert api_call.response is None assert api_call.url == "https://api.sandbox.orcid.org/v2.0/1234-XXXX-XXXX-XXXX/person"
def test_onboard_org(client): """Test to organisation onboarding.""" org = Organisation.create(name="THE ORGANISATION:test_onboard_org", tuakiri_name="THE ORGANISATION:test_onboard_org", confirmed=False, orcid_client_id="CLIENT ID", orcid_secret="Client Secret", city="CITY", country="COUNTRY", disambiguated_id="ID", disambiguation_source="RINGGOLD", is_email_sent=True) u = User.create(email="*****@*****.**", name="TEST USER", roles=Role.TECHNICAL, orcid="123", confirmed=True, organisation=org) second_user = User.create(email="*****@*****.**", name="TEST USER", roles=Role.ADMIN, orcid="1243", confirmed=True, organisation=org) UserOrg.create(user=second_user, org=org, is_admin=True) org_info = OrgInfo.create(name="A NEW ORGANISATION", tuakiri_name="A NEW ORGANISATION") org.tech_contact = u org_info.save() org.save() client.login_root() with patch("orcid_hub.utils.send_email"): resp = client.post("/invite/organisation", data=dict(org_name="A NEW ORGANISATION", org_email="*****@*****.**"), follow_redirects=True) assert User.select().where( User.email == "*****@*****.**").exists() resp = client.post("/invite/organisation", data=dict(org_name="A NEW ORGANISATION", org_email="*****@*****.**", tech_contact='y'), follow_redirects=True) assert User.select().where( User.email == "*****@*****.**").exists() org = Organisation.get(name="A NEW ORGANISATION") user = User.get(email="*****@*****.**") assert user.name is None assert org.tech_contact == user client.logout() resp = client.login(user, **{ "Sn": "TECHNICAL", "Givenname": "CONTACT", "Displayname": "Test User", "shib_O": "NEW ORGANISATION" }, follow_redirects=True) user = User.get(email="*****@*****.**") org = user.organisation assert user.is_tech_contact_of(org) resp = client.get("/confirm/organisation") assert resp.status_code == 200 org = Organisation.get(org.id) assert b"<!DOCTYPE html>" in resp.data, "Expected HTML content" assert b"Take me to ORCID to obtain my Client ID and Client Secret" in resp.data with patch("orcid_hub.authcontroller.requests") as requests: requests.post.return_value = Mock(data=b'XXXX', status_code=200) resp = client.post("/confirm/organisation", data={ "orcid_client_id": "APP-1234567890ABCDEF", "orcid_secret": "12345678-1234-1234-1234-1234567890ab", "country": "NZ", "city": "Auckland", "disambiguated_id": "XYZ123", "disambiguation_source": "RINGGOLD", "name": org.name, "email": user.email, }) assert resp.status_code == 302 url = urlparse(resp.location).path assert url == "/link" resp = client.get(url) client.logout() org = Organisation.get(org.id) assert org.disambiguated_id == "XYZ123" assert org.disambiguation_source == "RINGGOLD" assert org.orcid_client_id == "APP-1234567890ABCDEF" assert org.orcid_secret == "12345678-1234-1234-1234-1234567890ab" user = User.get(email="*****@*****.**") resp = client.login(user, **{ "Sn": "NEW ORGANISATION", "Givenname": "ADMINISTRATOR", "Displayname": "Admin User", "shib_O": "NEW ORGANISATION" }, follow_redirects=True) assert b"Take me to ORCID to allow A NEW ORGANISATION permission to access my ORCID record" in resp.data resp = client.get("/confirm/organisation") assert resp.status_code == 302 assert urlparse(resp.location).path == "/admin/viewmembers/" resp = client.get("/admin/viewmembers/") assert b"*****@*****.**" in resp.data resp = client.get("/admin/viewmembers/export/csv/") assert resp.headers["Content-Type"] == "text/csv; charset=utf-8" assert b"*****@*****.**" in resp.data assert b"*****@*****.**" in resp.data
def test_orcid_login_callback_admin_flow(patch, patch2, request_ctx): """Test login from orcid callback function for Organisation Technical contact.""" org = Organisation.create(name="THE ORGANISATION", tuakiri_name="THE ORGANISATION", confirmed=False, orcid_client_id="CLIENT ID", orcid_secret="Client Secret", city="CITY", country="COUNTRY", disambiguated_id="ID", disambiguation_source="SOURCE", is_email_sent=True) u = User.create(email="*****@*****.**", roles=Role.TECHNICAL, orcid="123", confirmed=False, organisation=org) UserOrg.create(user=u, org=org, is_admin=True) token = utils.generate_confirmation_token(email=u.email, org=org.name) with request_ctx() as resp: request.args = {"invitation_token": token, "state": "xyz"} session['oauth_state'] = "xyz" resp = authcontroller.orcid_login_callback(request) assert resp.status_code == 302 assert resp.location.startswith("/") with request_ctx() as respx: request.args = {"invitation_token": token, "state": "xyzabc"} session['oauth_state'] = "xyz" respx = authcontroller.orcid_login_callback(request) assert respx.status_code == 302 assert respx.location.startswith("/") with request_ctx() as resp: request.args = { "invitation_token": token, "state": "xyz", "error": "access_denied" } session['oauth_state'] = "xyz" resp = authcontroller.orcid_login_callback(request) assert resp.status_code == 302 assert resp.location.startswith("/") with request_ctx() as ct: token = utils.generate_confirmation_token(email=u.email, org=None) request.args = {"invitation_token": token, "state": "xyz"} session['oauth_state'] = "xyz" ctxx = authcontroller.orcid_login_callback(request) assert ctxx.status_code == 302 assert ctxx.location.startswith("/") with request_ctx() as ctxxx: request.args = {"invitation_token": token, "state": "xyzabc"} session['oauth_state'] = "xyz" ctxxx = authcontroller.orcid_login_callback(request) assert ctxxx.status_code == 302 assert ctxxx.location.startswith("/") with request_ctx() as cttxx: request.args = { "invitation_token": token, "state": "xyz", "error": "access_denied" } session['oauth_state'] = "xyz" cttxx = authcontroller.orcid_login_callback(request) assert cttxx.status_code == 302 assert cttxx.location.startswith("/") with request_ctx() as ct: token = utils.generate_confirmation_token(email=u.email, org=None) request.args = {"invitation_token": token, "state": "xyz"} session['oauth_state'] = "xyz" ct = authcontroller.orcid_login_callback(request) assert ct.status_code == 302 assert ct.location.startswith("/") with request_ctx(): request.args = {"invitation_token": None, "state": "xyz"} session['oauth_state'] = "xyz" ct = authcontroller.orcid_login_callback(request) assert ct.status_code == 302 assert ct.location.startswith("/") with request_ctx(): # Test case for catching general exception: invitation token here is integer, so an exception will be thrown. request.args = {"invitation_token": 123, "state": "xyz"} session['oauth_state'] = "xyz" ct = authcontroller.orcid_login_callback(request) assert ct.status_code == 302 assert ct.location.startswith("/") with request_ctx(): # User login via orcid, where organisation is not confirmed. u.orcid = "12121" u.save() request.args = {"invitation_token": None, "state": "xyz"} session['oauth_state'] = "xyz" resp = authcontroller.orcid_login_callback(request) assert resp.status_code == 302 assert resp.location.startswith("/about") with request_ctx(): # User login via orcid, where organisation is confirmed, so showing viewmembers page. org.tech_contact = u org.confirmed = True org.save() request.args = {"invitation_token": None, "state": "xyz"} session['oauth_state'] = "xyz" resp = authcontroller.orcid_login_callback(request) assert resp.status_code == 302 assert resp.location.startswith("/admin/viewmembers/") with request_ctx(): # User login via orcid, where organisation is not confirmed and user is tech, so showing confirm org page. org.confirmed = False org.save() request.args = {"invitation_token": None, "state": "xyz"} session['oauth_state'] = "xyz" resp = authcontroller.orcid_login_callback(request) assert resp.status_code == 302 assert resp.location.startswith("/confirm/organisation")
def test_orcid_login_callback_admin_flow(mocker, client): """Test login from orcid callback function for Organisation Technical contact.""" mocker.patch("orcid_hub.OAuth2Session.fetch_token", side_effect=fetch_token_mock) mocker.patch("orcid_hub.orcid_client.MemberAPIV20Api.view_emails", side_effect=get_record_mock) org = Organisation.create( name="THE ORGANISATION:test_orcid_login_callback_admin_flow", tuakiri_name="THE ORGANISATION:test_orcid_login_callback_admin_flow", confirmed=False, orcid_client_id="CLIENT ID", orcid_secret="Client Secret", city="CITY", country="COUNTRY", disambiguated_id="ID", disambiguation_source="RINGGOLD", is_email_sent=True) u = User.create(email="*****@*****.**", roles=Role.TECHNICAL, orcid="123", confirmed=False, organisation=org) UserOrg.create(user=u, org=org, is_admin=True) token = utils.new_invitation_token() OrgInvitation.create(token=token, univitee=u, email=u.email, org=org) resp = client.get(f"/orcid/login/{token}") assert resp.status_code == 200 assert token.encode() in resp.data state = session["oauth_state"] resp = client.get(f"/auth/?invitation_token={token}&state={state}&login=1") assert resp.status_code == 302 assert urlparse(resp.location).path == "/" resp = client.get( f"/auth/?invitation_token={token}&state={state}abc&login=1") assert resp.status_code == 302 assert urlparse(resp.location).path == "/" resp = client.get( f"/auth/?invitation_token={token}&state={state}&error=access_denied&login=1" ) assert resp.status_code == 302 assert urlparse(resp.location).path == "/" resp = client.get(f"/auth/?state=xyz&login=1") assert resp.status_code == 302 assert urlparse(resp.location).path == "/" resp = client.get( f"/auth/?invitation_token=abc12345&state={state}ABC&login=1") assert resp.status_code == 302 assert urlparse(resp.location).path == "/" # User login via orcid, where organisation is not confirmed. u.orcid = "123" u.save() resp = client.get(f"/auth/?state={state}&login=1") assert resp.status_code == 302 assert urlparse(resp.location).path == "/about" # User login via orcid, where organisation is confirmed, so showing viewmembers page. org.tech_contact = u org.confirmed = True org.save() resp = client.get(f"/auth/?state={state}&login=1") assert resp.status_code == 302 assert urlparse(resp.location).path == "/admin/viewmembers/" # User login via orcid, where organisation is not confirmed and user is tech, so showing confirm org page. org.confirmed = False org.save() resp = client.get(f"/auth/?state={state}&login=1") assert resp.status_code == 302 assert urlparse(resp.location).path == "/confirm/organisation"
def test_orcid_login_callback_researcher_flow(client, mocker): """Test login from orcid callback function for researcher and display profile.""" fetch_token = mocker.patch("orcid_hub.OAuth2Session.fetch_token", side_effect=fetch_token_mock) mocker.patch( "orcid_hub.orcid_client.MemberAPI.create_or_update_affiliation", side_effect=affiliation_mock) org = Organisation.create( name="THE ORGANISATION:test_orcid_login_callback_researcher_flow", tuakiri_name= "THE ORGANISATION:test_orcid_login_callback_researcher_flow", confirmed=True, orcid_client_id="CLIENT ID", orcid_secret="Client Secret", city="CITY", country="COUNTRY", disambiguated_id="ID", disambiguation_source="RINGGOLD", is_email_sent=True) u = User.create(email="*****@*****.**", name="TEST USER", roles=Role.RESEARCHER, orcid="123", confirmed=True, organisation=org) UserOrg.create(user=u, org=org, is_admin=False) token = utils.new_invitation_token() UserInvitation.create(email=u.email, token=token, affiliations=Affiliation.EMP, org=org, invitee=u) resp = client.get(f"/orcid/login/{token}") assert resp.status_code == 200 assert token.encode() in resp.data state = session['oauth_state'] resp = client.get(f"/auth/?state={state}&login=1") assert resp.status_code == 302 assert resp.location.endswith("/link") fetch_token.assert_called_once() resp = client.get(f"/auth/?invitation_token={token}&login=1") assert resp.status_code == 302 assert urlparse(resp.location).path == "/" fetch_token.assert_called_once() resp = client.get(f"/auth/?invitation_token={token}&login=1", follow_redirects=True) assert b"Danger" in resp.data assert b"Something went wrong, Please retry giving permissions " in resp.data fetch_token.reset_mock() resp = client.get(f"/auth/?invitation_token={token}&state={state}", follow_redirects=True) assert b"Warning" in resp.data assert b"The ORCID Hub was not able to automatically write an affiliation" in resp.data OrcidToken.delete().where(OrcidToken.user == u, OrcidToken.org == org).execute() fetch_token.reset_mock() resp = client.get(f"/auth/?invitation_token={token}&state={state}&login=1") assert resp.status_code == 302 assert resp.location.endswith("/profile") fetch_token.assert_called_once() assert OrcidToken.select().where(OrcidToken.user == u).count() == 1 resp = client.get(f"/orcid/login/{token}", follow_redirects=True) assert b"You have already given permission" in resp.data
def test_onboard_org(request_ctx): """Test to organisation onboarding.""" org = Organisation.create(name="THE ORGANISATION", tuakiri_name="THE ORGANISATION", confirmed=False, orcid_client_id="CLIENT ID", orcid_secret="Client Secret", city="CITY", country="COUNTRY", disambiguated_id="ID", disambiguation_source="SOURCE", is_email_sent=True) u = User.create(email="*****@*****.**", name="TEST USER", roles=Role.TECHNICAL, orcid="123", confirmed=True, organisation=org) second_user = User.create(email="*****@*****.**", name="TEST USER", roles=Role.ADMIN, orcid="1243", confirmed=True, organisation=org) org_info = OrgInfo.create(name="THE ORGANISATION", tuakiri_name="THE ORGANISATION") org.tech_contact = u org_info.save() org.save() OrgInvitation.get_or_create(email=u.email, org=org, token="sdsddsd") UserOrg.create(user=u, org=org, is_admin=True) with request_ctx("/confirm/organisation") as ctx: login_user(u) u.save() assert u.is_tech_contact_of(org) rv = ctx.app.full_dispatch_request() assert rv.status_code == 200 assert b"<!DOCTYPE html>" in rv.data, "Expected HTML content" assert b"Take me to ORCID to obtain my Client ID and Client Secret" in rv.data,\ "Expected Button on the confirmation page" with request_ctx("/confirm/organisation") as ctxx: second_user.save() login_user(second_user) rv = ctxx.app.full_dispatch_request() assert rv.status_code == 302 assert rv.location.startswith("/admin/viewmembers/") with request_ctx("/confirm/organisation", method="POST", data={ "orcid_client_id": "APP-FDFN3F52J3M4L34S", "orcid_secret": "4916c2d7-085e-487e-94d0-32450a9cfe6c", "country": "NZ", "city": "Auckland", "disambiguated_id": "xyz", "disambiguation_source": "xyz", "name": "THE ORGANISATION" }) as cttxx: login_user(u) u.save() with patch("orcid_hub.authcontroller.requests") as requests: requests.post.return_value = Mock(data=b'XXXX', status_code=200) rv = cttxx.app.full_dispatch_request() assert rv.status_code == 302 assert rv.location.startswith("/link")