def _make_user(role_name, organisation='org', organisation_is_shared=True, access=None): name = role_name if organisation: make_organisation(organisation, is_shared=organisation_is_shared) name = organisation + name email = '{name}@rero.ch'.format(name=name) datastore = app.extensions['security'].datastore user = datastore.find_user(email=email) if user: record = UserRecord.get_user_by_email(email) return record user = datastore.create_user(email=email, password=hash_password('123456'), active=True) datastore.commit() role = datastore.find_role(role_name) if not role: role = Role(name=role_name) role.users.append(user) db.session.add(role) if access: db.session.add(ActionUsers.allow(ActionNeed(access), user=user)) db.session.commit() data = { 'pid': name, 'email': email, 'first_name': name[0].upper() + name[1:], 'last_name': 'Doe', 'role': role_name } if organisation: data['organisation'] = { '$ref': 'https://sonar.ch/api/organisations/{organisation}'.format( organisation=organisation) } record = UserRecord.create(data, dbcommit=True) record.reindex() db.session.commit() return record
def test_get_reachable_roles(app): """Test get roles covered by the given role.""" roles = UserRecord.get_reachable_roles(UserRecord.ROLE_MODERATOR) assert len(roles) == 3 assert UserRecord.ROLE_MODERATOR in roles assert UserRecord.ROLE_SUBMITTER in roles assert UserRecord.ROLE_USER in roles roles = UserRecord.get_reachable_roles('unknown_role') assert not roles
def _make_user(role_name, organisation='org'): make_organisation(organisation) name = role_name if organisation: name = organisation + name email = '{name}@rero.ch'.format(name=name) datastore = app.extensions['security'].datastore user = datastore.find_user(email=email) if user: record = UserRecord.get_user_by_email(email) return record user = datastore.create_user(email=email, password=hash_password('123456'), active=True) datastore.commit() role = datastore.find_role(role_name) if not role: role = Role(name=role_name) role.users.append(user) db.session.add(role) db.session.add( ActionUsers.allow(ActionNeed( '{role}-access'.format(role=role_name)), user=user)) db.session.commit() record = UserRecord.create( { 'pid': name, 'email': email, 'full_name': name, 'roles': [role_name], 'organisation': { '$ref': 'https://sonar.ch/api/organisations/{organisation}'.format( organisation=organisation) } }, dbcommit=True) record.reindex() db.session.commit() return record
def read(cls, user, record): """Read permission check. :param user: Logged user. :param recor: Record to check. :returns: True is action can be done. """ if not current_user_record: return False # Can read himself in all cases if current_user_record['pid'] == record['pid']: return True # If not admin, no access if not current_user_record.is_admin: return False # Superuser is allowed if current_user_record.is_superuser: return True # Cannot read superusers records if UserRecord.ROLE_SUPERUSER == record['role']: return False user = UserRecord.get_record_by_pid(record['pid']) user = user.replace_refs() if not user.get('organisation'): return True return current_organisation['pid'] == user['organisation']['pid']
def post_process_serialize_search(self, results, pid_fetcher): """Post process the search results.""" if current_user_record: # Remove organisation facet for non super users if not current_user_record.is_superuser: results['aggregations'].pop('organisation', {}) # Remove user facet for non moderators users if not current_user_record.is_moderator: results['aggregations'].pop('user', {}) # Add organisation name for org_term in results.get('aggregations', {}).get('organisation', {}).get('buckets', []): organisation = OrganisationRecord.get_record_by_pid( org_term['key']) if organisation: org_term['name'] = organisation['name'] # Add user name for org_term in results.get('aggregations', {}).get('user', {}).get('buckets', []): user = UserRecord.get_record_by_pid(org_term['key']) if user: org_term['name'] = '{last_name}, {first_name}'.format( last_name=user['last_name'], first_name=user['first_name']) return super(JSONSerializer, self).post_process_serialize_search(results, pid_fetcher)
def aggregations(self): """Get the search result aggregations.""" aggregations = self._results.aggregations.to_dict() if current_user_record: # Remove organisation facet for non super users if not current_user_record.is_superuser: aggregations.pop('organisation', {}) # Remove user facet for non moderators users if not current_user_record.is_moderator: aggregations.pop('user', {}) # Add organisation name for org_term in aggregations.get('organisation', {}).get('buckets', []): organisation = OrganisationRecord.get_record_by_pid( org_term['key']) if organisation: org_term['name'] = organisation['name'] # Add user name for org_term in aggregations.get('user', {}).get('buckets', []): user = UserRecord.get_record_by_pid(org_term['key']) if user: org_term['name'] = '{last_name}, {first_name}'.format( last_name=user['last_name'], first_name=user['first_name']) return aggregations
def test_user_registered_handler(app, roles, user_without_role): """Test user confirmed signal.""" assert not user_without_role.roles user_registered_handler(app, user_without_role, None) assert user_without_role.roles[0].name == 'user' user = UserRecord.get_user_by_email(user_without_role.email) assert not user
def test_get_user_by_current_user(app, client, user_without_role, user): """Test getting a user with email taken from logged user.""" record = UserRecord.get_user_by_current_user(current_user) assert record is None login_user_via_view(client, email=user_without_role.email, password='******') record = UserRecord.get_user_by_current_user(current_user) assert record is None client.get(url_for_security('logout')) login_user_via_view(client, email=user['email'], password='******') record = UserRecord.get_user_by_current_user(current_user) assert 'email' in record assert user['email'] == '*****@*****.**'
def test_user_resolver(app, organisation, roles): """Test user resolver.""" UserRecord.create({ 'pid': '1', 'full_name': 'Jules Brochu', 'email': '*****@*****.**', 'roles': ['user'], 'organisation': { '$ref': 'https://sonar.ch/api/organisations/org' } }) record = DepositRecord.create( {'user': { '$ref': 'https://sonar.ch/api/users/1' }}, with_bucket=False) assert record.replace_refs().get('user')['email'] == '*****@*****.**'
def post_process_serialize_search(self, results, pid_fetcher): """Post process the search results.""" # Add user name for org_term in results.get('aggregations', {}).get('user', {}).get('buckets', []): user = UserRecord.get_record_by_pid(org_term['key']) if user: org_term['name'] = '{last_name}, {first_name}'.format( last_name=user['last_name'], first_name=user['first_name']) return super(JSONSerializer, self).post_process_serialize_search(results, pid_fetcher)
def test_delete(app, admin): """Test removing record.""" admin.delete(dbcommit=True, delindex=True) deleted = UserRecord.get_record(admin.id, with_deleted=True) assert deleted.id == admin.id with app.app_context(): datastore = app.extensions['security'].datastore user = datastore.find_user(email='*****@*****.**') assert not user.roles assert not user.is_active
def user_registered_handler(app, user, confirm_token): """Called when a new user is registered. :param app: App context. :param user: User account. """ # Add a default role to user role = datastore.find_role(UserRecord.ROLE_USER) datastore.add_role_to_user(user, role) datastore.commit() # Create user record user_record = UserRecord.get_user_by_email(user.email) if not user_record: user_record = UserRecord.create( { 'full_name': user.email, 'email': user.email, 'roles': [UserRecord.ROLE_USER] }, dbcommit=True) user_record.reindex()
def publish(pid=None): """Publish a deposit or send a message for review.""" deposit = DepositRecord.get_record_by_pid(pid) if not deposit or deposit[ 'step'] != DepositRecord.STEP_DIFFUSION or deposit[ 'status'] not in [ DepositRecord.STATUS_IN_PROGRESS, DepositRecord.STATUS_ASK_FOR_CHANGES ]: abort(400) user = UserRecord.get_record_by_ref_link(deposit['user']['$ref']) # Deposit can be validated directly if user.is_granted(UserRecord.ROLE_MODERATOR): deposit['status'] = DepositRecord.STATUS_VALIDATED # Create document based on deposit deposit.create_document() else: deposit['status'] = DepositRecord.STATUS_TO_VALIDATE subdivision = SubdivisionRecord.get_record_by_ref_link( user['subdivision']['$ref']) if user.get('subdivision') else None moderators_emails = user.get_moderators_emails( subdivision['pid'] if subdivision else None) email_subject = _('Deposit to validate') if subdivision: email_subject += f' ({get_language_value(subdivision["name"])})' if moderators_emails: # Send an email to validators send_email( moderators_emails, email_subject, 'deposits/email/validation', { 'deposit': deposit, 'user': user, 'link': current_app.config.get('SONAR_APP_ANGULAR_URL') }, False) deposit.log_action(user, 'submit') deposit.commit() deposit.reindex() db.session.commit() return make_response()
def guess_organisation(self, data, **kwargs): """Guess organisation from current logged user. :param data: Dict of user data. :returns: Modified dict of user data. """ # Organisation already attached to user, we do nothing. if data.get('organisation'): return data # Store current user organisation in new user. user = UserRecord.get_user_by_current_user(current_user) if user.get('organisation'): data['organisation'] = user['organisation'] return data
def dump(self, record, data): """Dump the data for indexing.""" if data['metadata'].get('user'): data['metadata']['user'] = { 'pid': UserRecord.get_pid_by_ref_link( data['metadata']['user']['$ref']) } if data['metadata'].get('organisation'): organisation = OrganisationRecord.get_record_by_ref_link( data['metadata']['organisation']['$ref']) data['metadata']['organisation'] = { 'pid': organisation['pid'], 'name': organisation['name'] }
def test_is_role_property(organisation, roles): """Test if user is in a particular role.""" user = UserRecord.create( { 'full_name': 'John Doe', 'email': '*****@*****.**', 'roles': [UserRecord.ROLE_MODERATOR], 'organisation': { '$ref': 'https://sonar.ch/api/organisations/org' } }, dbcommit=True) assert user.is_user assert user.is_submitter assert user.is_moderator assert not user.is_admin assert not user.is_superuser
def test_get_moderators(app, organisation, roles): """Test search for moderators.""" user = UserRecord.create( { 'full_name': 'John Doe', 'email': '*****@*****.**', 'roles': [UserRecord.ROLE_MODERATOR], 'organisation': { '$ref': 'https://sonar.ch/api/organisations/org' } }, dbcommit=True) user.reindex() moderators = UserSearch().get_moderators() assert list(moderators) moderators = UserSearch().get_moderators('not_existing_organisation') assert not list(moderators)
def logged_user(): """Current logged user informations in JSON.""" if current_user.is_anonymous: return jsonify({}) user = UserRecord.get_user_by_current_user(current_user) if user and 'resolve' in request.args: user = user.replace_refs() data = {} if user: data['metadata'] = user.dumps() data['metadata']['is_superuser'] = user.is_superuser data['metadata']['is_admin'] = user.is_admin data['metadata']['is_moderator'] = user.is_moderator data['metadata']['is_submitter'] = user.is_submitter data['metadata']['is_user'] = user.is_user data['metadata']['permissions'] = { 'users': { 'add': UserPermission.create(user), 'list': UserPermission.list(user) }, 'documents': { 'add': DocumentPermission.create(user), 'list': DocumentPermission.list(user) }, 'organisations': { 'add': OrganisationPermission.create(user), 'list': OrganisationPermission.list(user) }, 'deposits': { 'add': DepositPermission.create(user), 'list': DepositPermission.list(user) } } # TODO: If an organisation is associated to user and only when running # tests, organisation cannot not be encoded to JSON after call of # user.replace_refs() --> check why return jsonify(data)
def test_is_granted(app, organisation, roles): """Test if user is granted with a role.""" user = UserRecord.create( { 'full_name': 'John Doe', 'email': '*****@*****.**', 'roles': [UserRecord.ROLE_MODERATOR], 'organisation': { '$ref': 'https://sonar.ch/api/organisations/org' } }, dbcommit=True) assert not user.is_granted(UserRecord.ROLE_ADMIN) assert not user.is_granted('fake_role') assert user.is_granted(UserRecord.ROLE_MODERATOR) assert user.is_granted(UserRecord.ROLE_USER) del user['roles'] assert not user.is_granted(UserRecord.ROLE_MODERATOR)
def _load_user(self, record): """Check data integrity and load user. :param record: Record to check. :returns: User record. """ if not current_user_record: raise UserNotLoggedError if not record['metadata'].get('validation', {}).get('user'): raise Exception('No user stored in record') record_user = UserRecord.get_record_by_ref_link( record['metadata']['validation']['user']['$ref']) if not record_user: raise UserRecordNotFoundError if not self._user_can_moderate() and not self._user_is_owner_of_record( record_user): raise UserIsNotOwnerOfRecordError return record_user
def test_get_moderators_emails(app, organisation, roles): """Test getting list of moderators emails.""" user = UserRecord.create( { 'full_name': 'John Doe', 'email': '*****@*****.**', 'roles': [UserRecord.ROLE_MODERATOR], 'organisation': { '$ref': 'https://sonar.ch/api/organisations/org' } }, dbcommit=True) user.reindex() emails = user.get_moderators_emails() assert emails assert '*****@*****.**' in emails user['organisation'] = { '$ref': 'https://sonar.ch/api/organisations/not-existing' } emails = user.get_moderators_emails() assert not emails
def test_review(client, db, user, moderator, deposit): """Test reviewing a deposit.""" url = url_for('deposits.review', pid=deposit['pid']) headers = { 'Content-Type': 'application/json', 'Accept': 'application/json' } # Deposit is not in status to validate response = client.post(url) assert response.status_code == 400 # No payload posted deposit['status'] = 'to_validate' deposit.commit() db.session.commit() response = client.post(url) assert response.status_code == 400 # Invalid action response = client.post(url, data=json.dumps({ 'action': 'unknown', 'comment': None }), headers=headers) assert response.status_code == 400 # User is not a moderator response = client.post(url, data=json.dumps({ 'action': 'approve', 'comment': None, 'user': { '$ref': UserRecord.get_ref_link( 'users', user['pid']) } }), headers=headers) assert response.status_code == 403 login_user_via_view(client, email=moderator['email'], password='******') # Valid approval request response = client.post(url, data=json.dumps({ 'action': 'approve', 'comment': None, 'user': { '$ref': UserRecord.get_ref_link( 'users', moderator['pid']) } }), headers=headers) assert response.status_code == 200 # Valid refusal request deposit['status'] = 'to_validate' deposit.commit() db.session.commit() response = client.post(url, data=json.dumps({ 'action': 'reject', 'comment': 'Sorry deposit is not valid', 'user': { '$ref': UserRecord.get_ref_link( 'users', moderator['pid']) } }), headers=headers) assert response.status_code == 200 # Valid ask for changes request deposit['status'] = 'to_validate' deposit.commit() db.session.commit() response = client.post(url, data=json.dumps({ 'action': 'ask_for_changes', 'comment': None, 'user': { '$ref': UserRecord.get_ref_link( 'users', moderator['pid']) } }), headers=headers) assert response.status_code == 200
def review(pid=None): """Review a deposit and change the deposit status depending on action.""" deposit = DepositRecord.get_record_by_pid(pid) if not deposit or deposit['status'] != DepositRecord.STATUS_TO_VALIDATE: abort(400) payload = request.get_json() if not payload: abort(400) if 'action' not in payload or 'user' not in payload or payload[ 'action'] not in [ DepositRecord.REVIEW_ACTION_APPROVE, DepositRecord.REVIEW_ACTION_REJECT, DepositRecord.REVIEW_ACTION_ASK_FOR_CHANGES ]: abort(400) user = UserRecord.get_record_by_ref_link(payload['user']['$ref']) if not user or not user.is_moderator: abort(403) subject = None status = None if payload['action'] == DepositRecord.REVIEW_ACTION_APPROVE: subject = _('Deposit approval') status = DepositRecord.STATUS_VALIDATED # Create document based on deposit deposit.create_document() elif payload['action'] == DepositRecord.REVIEW_ACTION_REJECT: subject = _('Deposit rejection') status = DepositRecord.STATUS_REJECTED else: subject = _('Ask for changes on deposit') status = DepositRecord.STATUS_ASK_FOR_CHANGES deposit['status'] = status # Log action deposit.log_action(payload['user'], payload['action'], payload['comment']) # Load user who creates the deposit deposit_user = UserRecord.get_record_by_ref_link(deposit['user']['$ref']) send_email( [deposit_user['email']], subject, 'deposits/email/{action}'.format(action=payload['action']), { 'deposit': deposit, 'deposit_user': deposit_user, 'user': user, 'date': datetime.now().strftime('%d.%m.%Y %H:%M:%S'), 'comment': payload['comment'], 'link': current_app.config.get('SONAR_APP_ANGULAR_URL') }, False) deposit.commit() deposit.reindex() db.session.commit() return make_response(jsonify(deposit))