def test_tuakiri_login_with_org(client): """ Test logging attempt via Shibboleth. If a user logs in from an organisation that isn't onboared, the user should be informed about that and redirected to the login page. """ org = Organisation(name="THE ORGANISATION") org.save() rv = client.get("/Tuakiri/login", headers={ "Auedupersonsharedtoken": "ABC111", "Sn": "LAST NAME/SURNAME/FAMILY NAME", 'Givenname': "FIRST NAME/GIVEN NAME", "Mail": "*****@*****.**", "O": "THE ORGANISATION", "Displayname": "TEST USER FROM THE ORGANISATION" }, follow_redirects=True) u = User.get(email="*****@*****.**") assert u.organisation == org assert org in u.organisations assert u.edu_person_shared_token == "ABC111" assert b"Your organisation (INCOGNITO) is not onboarded" not in rv.data uo = UserOrg.get(user=u, org=org) assert not uo.is_admin
def test_models(test_db, scope="session"): Organisation.insert_many((dict(name="Organisation #%d" % i, email="admin@org%d.org.nz" % i, tuakiri_name="Organisation #%d" % i, orcid_client_id="client-%d" % i, orcid_secret="secret-%d" % i, confirmed=(i % 2 == 0)) for i in range(10))).execute() User.insert_many( (dict(name="Test User #%d" % i, first_name="Test_%d" % i, last_name="User_%d" % i, email="user%d@org%d.org.nz" % (i, i * 4 % 10), edu_person_shared_token="EDU PERSON SHARED TOKEN #%d" % i, confirmed=(i % 3 != 0), roles=Role.SUPERUSER if i % 42 == 0 else Role.ADMIN if i % 13 == 0 else Role.RESEARCHER) for i in range(60))).execute() UserOrg.insert_many( (dict(is_admin=((u + o) % 23 == 0), user=u, org=o) for (u, o) in product(range(2, 60, 4), range(2, 10)))).execute() UserOrg.insert_many( (dict(is_admin=True, user=43, org=o) for o in range(1, 11))).execute() yield test_db
def test_link_orcid_auth_callback(name, request_ctx): """Test ORCID callback - the user authorized the organisation access to the ORCID profile.""" with request_ctx("/auth") as ctx: org = Organisation(name="THE ORGANISATION", confirmed=True) org.save() test_user = User( name=name, email="*****@*****.**", username="******", organisation=org, orcid="ABC123", confirmed=True) login_user(test_user, remember=True) rv = ctx.app.full_dispatch_request() assert rv.status_code == 302, "If the user is already affiliated, the user should be redirected ..." assert "profile" in rv.location, "redirection to 'profile' showing the ORCID" u = User.get(username="******") assert u.orcid == "ABC-123-456-789" assert u.access_token == "ABC123" if name: assert u.name == name, "The user name should be changed" else: assert u.name == "NEW TEST", "the user name should be set from record coming from ORCID"
def test_user_org_link(test_models): assert User.get(id=43).admin_for.count() == 10 assert User.get(id=1).admin_for.count() == 0 assert User.get(id=42).admin_for.count() > 0 assert User.get(id=2).organisations.count() > 0 assert Organisation.get(id=1).admins.count() == 1 assert Organisation.get(id=5).users.count() > 0 assert Organisation.get(id=5).admins.count() > 0
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(name="THE ORGANISATION", confirmed=True) org.save() test_user = User( email="*****@*****.**", username="******", organisation=org, orcid=None, confirmed=True) login_user(test_user, remember=True) rv = ctx.app.full_dispatch_request() assert rv.status_code == 302 assert rv.location == url_for("link")
def get_org(self): url = self.base_url + "groups/" + self.organisation response = requests.get(url) if response.status_code == 404: return {} response.raise_for_status() data = response.json() res = { "login": data["path"], "description": data["description"], "nom": data["name"], "organisation_url": data["web_url"], "avatar_url": self.avatar_url(data["avatar_url"]), "site_web": None, "adresse": None, "email": None, "est_verifiee": None, "nombre_repertoires": len(data["projects"]), "date_creation": None, "plateforme": "GitLab", } return Organisation(**res)
def test_profile(request_ctx): """Test an affilated user profile and ORCID data retrieval.""" with request_ctx("/profile") as ctx: org = Organisation(name="THE ORGANISATION", confirmed=True) org.save() test_user = User( email="*****@*****.**", username="******", organisation=org, orcid="ABC123", confirmed=True) login_user(test_user, remember=True) rv = ctx.app.full_dispatch_request() assert rv.status_code == 200 assert b"TEST1234567890" in rv.data
def get_org(self): base_url = self.url("orgs/" + self.organisation) response = requests.get(base_url, headers=self.github_headers()) if response.status_code == 404: return {} response.raise_for_status() data = response.json() mapping = [ ("login", "login"), ("description", "description"), ("nom", "name"), ("organisation_url", "html_url"), ("avatar_url", "avatar_url"), ("site_web", "blog"), ("adresse", "location"), ("email", "email"), ("est_verifiee", "is_verified"), ("nombre_repertoires", "public_repos"), ("date_creation", "created_at"), ] current_dict = {} for key, json_key in mapping: try: current_dict[key] = self.clean_data(data[json_key], key) except KeyError: current_dict[key] = None current_dict["plateforme"] = "GitHub" return Organisation(**current_dict)
def test_link(request_ctx): """Test a user affiliation initialization.""" with request_ctx("/link") as ctx: org = Organisation(name="THE ORGANISATION", confirmed=True) org.save() test_user = User( name="TEST USER 123", email="*****@*****.**", username="******", 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_link_already_affiliated(request_ctx): """Test a user affiliation initialization if the uerer is already affilated.""" with request_ctx("/link") as ctx: org = Organisation(name="THE ORGANISATION", confirmed=True) org.save() test_user = User( email="*****@*****.**", name="TEST USER", username="******", organisation=org, orcid="ABC123", confirmed=True) test_user.save() login_user(test_user, remember=True) uo = UserOrg(user=test_user, org=org) uo.save() rv = ctx.app.full_dispatch_request() assert rv.status_code == 302, "If the user is already affiliated, the user should be redirected ..." assert "profile" in rv.location, "redirection to 'profile' showing the ORCID"
def iati_organisations__iati_organisation(self, element): organisation = Organisation() organisation.organisation_identifier = element.xpath('iati-identifier/text()')[0] self.organisation_identifier = organisation.organisation_identifier organisation.code = self.organisation_identifier organisation.last_updated_datetime = self.validate_date(element.attrib.get('last-updated-datetime')) if '{http://www.w3.org/XML/1998/namespace}lang' in element.attrib: organisation.default_lang = self.get_or_none(codelist_models.Language,code=element.attrib['{http://www.w3.org/XML/1998/namespace}lang']) organisation.iati_version_id = self.VERSION organisation.default_currency = self.get_or_none(codelist_models.Currency,code=element.attrib.get('default-currency')) organisation.save() # add to reporting organisation and recipient_organisation RecipientOrgBudget.objects.filter(recipient_org_identifier=self.organisation_identifier).update(recipient_org=organisation) ReportingOrg.objects.filter(reporting_org_identifier=self.organisation_identifier).update(reporting_org=organisation) self.register_model('Organisation', organisation) # store element return element
def organisation_basic(): data = request.get_json(force=True) name = data['name'] desc = data['desc'] organisation = Organisation(name=name, desc=desc) session.add(organisation) session.commit() return jsonify({ 'status': "registered", 'name': organisation.name, 'desc': organisation.desc })
def test_reset_db(request_ctx): """Test reset_db function for 'testing' cycle reset.""" with request_ctx("/reset_db") as ctx: org = Organisation(name="THE ORGANISATION") org.save() u = User(email="*****@*****.**", name="TEST USER", username="******", roles=Role.SUPERUSER, orcid=None, confirmed=True) u.save() root = User(email="*****@*****.**", name="The root", username="******", roles=Role.SUPERUSER, orcid=None, confirmed=True) root.save() assert User.select().count() == 2 assert Organisation.select().count() == 1 login_user(u, remember=True) rv = ctx.app.full_dispatch_request() assert User.select().count() == 1 assert Organisation.select().count() == 0 assert rv.status_code == 302 assert rv.location == url_for("logout")
def confirm_organisation(token): """Registration confirmations. TODO: expand the spect as soon as the reqirements get sorted out. """ clientSecret_url = None email = confirm_token(token) user = current_user if not email: app.error("token '%s'", token) app.login_manager.unauthorized() if user.email != email: flash( "The invitation to on-board the organisation wasn't sent to your email address...", "danger") return redirect(url_for("login")) # TODO: support for mutliple orgs and admins # TODO: admin role asigning to an exiting user # TODO: support for org not participating in Tuakiri form = OrgConfirmationForm() # For now only GET method is implemented will need post method for organisation # to enter client secret and client key for orcid if request.method == 'POST': if not form.validate(): flash('Please fill in all fields and try again!', "danger") else: organisation = Organisation.get(email=email) if (not (user is None) and (not (organisation is None))): # Update Organisation organisation.confirmed = True organisation.orcid_client_id = form.orgOricdClientId.data organisation.orcid_secret = form.orgOrcidClientSecret.data organisation.save() # Update Orcid User user.confirmed = True user.save() with app.app_context(): msg = Message("Welcome to OrcidhHub", recipients=[email]) msg.body = "Congratulations your emailid has been confirmed and " \ "organisation onboarded successfully." mail.send(msg) flash("Your Onboarding is Completed!!!", "success") return redirect(url_for("login")) elif request.method == 'GET': form.orgEmailid.data = email form.orgName.data = user.organisation.name flash( """If you currently don't know Client id and Client Secret, Please request those by clicking on link 'Take me to ORCiD to obtain Client iD and Client Secret' and come back to this same place once you have them within 15 days""", "warning") clientSecret_url = iri_to_uri( MEMBER_API_FORM_BASE_URL) + "?" + urlencode( dict(new_existing=NEW_CREDENTIALS, note=NOTE_ORCID + " " + user.organisation.name, contact_email=email, contact_name=user.name, org_name=user.organisation.name, cred_type=CRED_TYPE_PREMIUM, app_name=APP_NAME + " at " + user.organisation.name, app_description=APP_DESCRIPTION + " at " + user.organisation.name, app_url=APP_URL, redirect_uri_1=redirect_uri)) return render_template('orgconfirmation.html', clientSecret_url=clientSecret_url, form=form)
def invite_organisation(): """Invite an organisation to register. Flow: * Hub administrort (super user) invokes the page, * Fills in the form with the organisation and organisation technica contatct data (including an email address); * Submits the form; * A secure registration token gets ceated; * An email message with confirmation link gets created and sent off to the technical contact. """ form = OrgRegistrationForm() if request.method == "POST": if not form.validate(): flash("Please fill in all fields and try again.", "danger") else: email = form.orgEmailid.data org_name = form.orgName.data try: User.get(User.email == form.orgEmailid.data) flash( "This Email address is already an Admin for one of the organisation", "warning") except User.DoesNotExist: pass finally: # TODO: organisation can have mutiple admins: # TODO: user OrgAdmin try: org = Organisation.get(name=org_name) # TODO: fix it! org.email = email except Organisation.DoesNotExist: org = Organisation(name=org_name, email=email) org.save() try: user = User.get(email=email) user.roles |= Role.ADMIN user.organisation = org except User.DoesNotExist: user = User( name=form.orgName.data, email=form.orgEmailid.data, confirmed=True, # In order to let the user in... roles=Role.ADMIN, organisation=org) user.save() # Note: Using app context due to issue: https://github.com/mattupstate/flask-mail/issues/63 with app.app_context(): msg = Message("Welcome to OrcidhHub", recipients=[str(form.orgEmailid.data)]) token = generate_confirmation_token(form.orgEmailid.data) # TODO: do it with templates msg.body = "Your organisation is just one step behind to get onboarded" \ " please click on following link to get onboarded " \ "https://" + environ.get("ENV", "dev") + ".orcidhub.org.nz" + \ url_for("confirm_organisation", token=token) mail.send(msg) flash( "Organisation Onboarded Successfully!!! Email Communication has been sent to Admin", "success") return render_template('registration.html', form=form)
import models from models import Organisation, User, Role models.drop_talbes() models.create_tables() org0 = Organisation(name="The Royal Society of NewZealand", email="*****@*****.**", tuakiri_name="The Royal Society of NewZealand", orcid_client_id="client-123", orcid_secret="secret-123", confirmed=True) org0.save() super_user = User(name="The Royal Society of NewZealand", email="*****@*****.**", edu_person_shared_token="aaRtDix1l2z43M0vvWTBpBuf_ek", confirmed=True, roles=Role.SUPERUSER) super_user.save() super_user = User(name="The Root", email="*****@*****.**", confirmed=True, roles=Role.SUPERUSER) super_user.save()
def reset_db_with_fixtures(db=db): db.drop_all() db.create_all() #----------------------------------------------------------------------------# # Fixtures - organisation #----------------------------------------------------------------------------# org0 = Organisation( auth0_id='auth0|60c58135612d820070a5f049', name='Test Organisation', description='Test organisation authenticated through Auth0', website='http://mywebsite.com', phone_contact='1111111', email_contact='*****@*****.**') org0.insert() org1 = Organisation(name='Pet Welfare Society', description='Open your heart to a cat in need', website='https://www.catwelfare.org/', email_contact='*****@*****.**', phone_contact='96111111') org1.insert() org2 = Organisation( name='East Youths', description='An organisation of youths for the community', website='eastyouths.org', email_contact='*****@*****.**') org2.insert() #----------------------------------------------------------------------------# # Fixtures - users #----------------------------------------------------------------------------# u0 = User(auth0_id='auth0|60c58174612d820070a5f057', name='Test User', age=17, email_contact='*****@*****.**', phone_contact='1111111', join_date=datetime(2020, 5, 21, 21, 30, 0), skills=['cooking', 'web development']) u0.insert() u1 = User(name='User01', age=31, email_contact='*****@*****.**', phone_contact='1111111', join_date=datetime(2020, 7, 1, 0, 0, 0), skills=['counselling']) u1.insert() u2 = User(name='User02', age=45, email_contact='*****@*****.**', phone_contact='1111111', join_date=datetime(2019, 12, 12, 0, 0, 0), skills=None) u2.insert() u3 = User(name='User03', age=28, email_contact='*****@*****.**', phone_contact='1111111', join_date=datetime(2020, 12, 12, 0, 0, 0), skills=['counselling']) u3.insert() #----------------------------------------------------------------------------# # Fixtures - Events #----------------------------------------------------------------------------# e0 = Event(name='test event 0', description='this is a test event', start_datetime=datetime(2021, 1, 12, 10, 0, 0), end_datetime=datetime(2021, 1, 12, 12, 0, 0), address='London SW1A 0AA, UK', organisation=org0, participants=[u0, u1]) e0.insert() e1 = Event(name='test event 1', description='this is a test event', start_datetime=datetime(2021, 1, 12, 17, 0, 0), end_datetime=datetime(2021, 1, 12, 18, 0, 0), address='London SW1A 0AA, UK', organisation=org1, participants=[u0]) e1.insert() e2 = Event(name='test event 2', description='this is a test event', start_datetime=datetime(2021, 3, 1, 10, 0, 0), end_datetime=datetime(2021, 3, 1, 12, 0, 0), address='London SW1A 0AA, UK', organisation=org2, participants=[u1, u3]) e2.insert() e3 = Event(name='test event 3', description='this is a test event', start_datetime=datetime(2021, 4, 1, 10, 0, 0), end_datetime=datetime(2021, 4, 1, 12, 0, 0), address='London SW1A 0AA, UK', organisation=org2, participants=[u0, u1, u3]) e3.insert() e4 = Event(name='test event 4', description='this is a test event', start_datetime=datetime(2021, 5, 1, 10, 0, 0), end_datetime=datetime(2021, 5, 1, 12, 0, 0), address='London SW1A 0AA, UK', organisation=org2, participants=[u0, u2, u3]) e4.insert()
def test_org_count(test_models): assert Organisation.select().count() == 10
def shib_login(): """Shibboleth authenitcation handler. The (Apache) location should requier authentication using Shibboleth, e.g., <Location /Tuakiri> AuthType shibboleth ShibRequireSession On require valid-user ShibUseHeaders On </Location> Flow: * recieve redicected request from SSO with authentication data in HTTP headers * process headeers * if the organisation isn't on-boarded, reject further access and redirect to the main loging page; * if the user isn't registered add the user with data received from Shibboleth * if the request has returning destination (next), redirect the user to it; * else choose the next view based on the role of the user: ** for a researcher, affiliation; ** for super user, the on-boarding of an organisation; ** for organisation administrator or technical contact, the completion of the on-boarding. """ _next = request.args.get('_next') token = request.headers.get("Auedupersonsharedtoken") last_name = request.headers['Sn'] first_name = request.headers['Givenname'] email = request.headers['Mail'] session["shib_O"] = shib_org_name = request.headers['O'] name = request.headers.get('Displayname') try: # TODO: need a separate field for org name comimg from Tuakiri org = Organisation.get(name=shib_org_name) except Organisation.DoesNotExist: org = None try: user = User.get(User.email == email) # Add Shibboleth meta data if they are missing if not user.edu_person_shared_token: user.edu_person_shared_token = token if not user.name or org is not None and user.name == org.name and name: user.name = name if not user.first_name and first_name: user.firts_name = first_name if not user.last_name and last_name: user.last_name = last_name if not user.confirmed: user.confirmed = True # TODO: keep login auditing (last_loggedin_at... etc) except User.DoesNotExist: user = User.create(email=email, name=name, first_name=first_name, last_name=last_name, confirmed=True, roles=Role.RESEARCHER, edu_person_shared_token=token) if org is not None and org not in user.organisations: UserOrg.create(user=user, org=org) # TODO: need to find out a simple way of tracking # the organization user is logged in from: if org != user.organisation: user.organisation = org user.save() login_user(user) if _next: return redirect(_next) elif user.is_superuser: return redirect(url_for("invite_organisation")) elif org and org.confirmed: return redirect(url_for("link")) else: flash("Your organisation (%s) is not onboarded" % shib_org_name, "danger") return redirect(url_for("login"))
def reset_db(): """Reset the DB for a new testing cycle.""" User.delete().where(~(User.name**"royal" | User.name**"%root%")).execute() Organisation.delete().where(~(Organisation.name % "%Royal%")).execute() return redirect(url_for("logout"))
def generate_base_organisation(name: str) -> Organisation: org = Organisation(name=name) db.session.add(org) db.session.commit() return org
def test_user_org_link_user_constraint(test_models): org = Organisation.get(id=1) uo = UserOrg(user=999999, org=org) with pytest.raises(User.DoesNotExist): uo.save()
def get(self, organisation_id): user = Organisation.from_id(organisation_id) return marshal(user, full_fields)
def makeOrganisation(name, list_id, enquete_id, color): # Keep a list of added users for the deletion upon error userlist = [] # Create a batch operation batch = {"operations":[]} config = MailChimpConfig() # Create a organisation new_organisation = Organisation(name=name, color=color) new_organisation.save() # Retrieve the enquete enquete = Enquete.objects.get(id=enquete_id) # Create a invulmoment new_invulmoment = Invulmoment(organisation=new_organisation, enquete=enquete, time=date.today()) new_invulmoment.save() # Get the list members list_endpoint_string = 'lists/' + list_id + '/members' list_endpoint = urlparse.urljoin(config.api_root, list_endpoint_string) params = { # Pagination in API v3.0 is always done with count and offset 'count': 1000, 'offset': 0 } list_response = requests.get(list_endpoint, auth=('apikey', config.apikey), params=params, verify=False) # Add a new Merge tag to the list mergetags_endpoint_string = 'lists/' + list_id +'/merge-fields' mergetags_endpoint = urlparse.urljoin(config.api_root, mergetags_endpoint_string) mergetags_post_data = {"tag":"PWD", "name":"Password", "required":False, "public":False, "type":"text"} mergetags_response = requests.post(mergetags_endpoint, auth=('apikey', config.apikey), verify=False, data=json.dumps(mergetags_post_data)) if mergetags_response.reason != "OK": new_organisation.delete() new_invulmoment.delete() return "Response error!: " + mergetags_response.reason + " , bestaat de Password Mergetag al in Mailchimp?" for member in list_response.json()['members']: userCheck = User.objects.filter(username=member['email_address'].lower()) if not userCheck.count() == 0: new_invulmoment.delete() new_organisation.delete() if len(userlist) > 0: for user in userlist: user.delete() return "Gebruiker bestaat al: " + member['email_address'].lower() + " (Geen organisatie aangemaakt!)" # For every user add them to the db and patch them to mailchimp (provide pwd merge tag) newUser = User(username=member['email_address'].lower(), email=member['email_address']) password = User.objects.make_random_password() newUser.set_password(password) newUser.save() userlist.append(newUser) # Patch the user (add it to batch operation) path_string = "/lists/" + list_id + "/members/" + member['id'] operation_data = {"merge_fields":{"PWD":password}} operation = {"method":"PATCH", "path":path_string, "body":json.dumps(operation_data)} batch["operations"].append(operation) # Add the user to the organisation new_organisation.members.add(newUser) # Create a profiel object for this user # newProfiel = Profiel(user=newUser, invulmoment=new_invulmoment) # newProfiel.save() batch_endpoint = urlparse.urljoin(config.api_root, "batches") batch_response = requests.post(batch_endpoint, auth=('apikey', config.apikey), verify=False, data=json.dumps(batch)) print batch_response.text if batch_response.reason != "OK": new_invulmoment.delete() new_organisation.delete() if len(userlist) > 0: for user in userlist: user.delete() return "Response Error!: " + batch_response.reason return True
def post(self, *args, **kwargs): """Register an orgsanition""" if request.is_json: current_user = get_current_user() if not current_user: return jsonify({ 'status': False, 'msg': 'User doesn\'t exist', 'data': None }), 404 if db.session.query(user_organisation_table). \ filter(user_organisation_table.c.user_id == current_user.id).first(): return jsonify({ 'status': False, 'msg': 'A user can be associated with only one organisation', 'data': None }), 400 request_data = request.get_json() if Organisation.query.filter_by( name=request_data.get('name')).first(): return jsonify({ 'status': False, 'msg': 'Organisation already exists', 'data': None }), 400 serializer = OrganisationRegistrationSerializer() try: validated_request_data = serializer.load(request_data) validated_request_data['passcode'] = generate_password_hash( password=validated_request_data.pop('passcode')) validated_request_data['registered_by'] = current_user.id organisation = Organisation(**validated_request_data) organisation.user_organisation.append(current_user) db.session.add(organisation) db.session.commit() return jsonify({ 'status': True, 'msg': 'Organisation registation successful', 'data': { 'id': organisation.id } }), 201 except ValidationError as err: return jsonify({ 'status': False, 'msg': 'Validation failed', 'data': None, 'errors': err.messages }), 400 return jsonify({ 'status': False, 'msg': 'Invalid JSON', 'data': None }), 400