def test_record_restricted_api_read(app, api_client, user_info, status): """Test permissions of a restricted record through the API.""" if user_info: login_user_via_session(api_client, email=user_info['email']) assert api_client.get('/literature/1090628').status_code == status
def test_record_public_detailed_read(app, app_client, user_info, status): """Test that a record can be read by everybody through web interface.""" if user_info: # Login as user login_user_via_session(app_client, email=user_info['email']) assert app_client.get('/literature/111').status_code == status
def edit_workflow(workflow_app): app_client = workflow_app.test_client() user = User.query.filter_by(email='*****@*****.**').one() login_user_via_session(app_client, user=user) record = { '$schema': 'http://localhost:5000/schemas/records/hep.json', 'arxiv_eprints': [ { 'categories': [ 'nucl-th' ], 'value': '1802.03287' } ], 'control_number': 123, 'document_type': ['article'], 'titles': [{'title': 'Resource Pooling in Large-Scale Content Delivery Systems'}], 'self': {'$ref': 'http://localhost:5000/schemas/records/hep.json'}, '_collections': ['Literature'] } factory = TestRecordMetadata.create_from_kwargs(json=record) eng_uuid = start('edit_article', data=factory.record_metadata.json) obj = WorkflowEngine.from_uuid(eng_uuid).objects[0] assert obj.status == ObjectStatus.WAITING assert obj.extra_data['callback_url'] return obj
def test_record_public_api_read(app, app_client, user_info, status): """Test that a record can be read by everybody through the API.""" if user_info: # Login as user login_user_via_session(app_client, email=user_info['email']) assert app_client.get('/old-literature/1497201').status_code == status
def test_edit_article_callback_redirects_to_rt( edit_workflow, workflow_api_client, mocked_external_services, workflow_app, ): user_info = {'email': '*****@*****.**'} login_user_via_session(workflow_api_client, email=user_info['email']) edit_workflow.extra_data['curation_ticket_id'] = 1234 payload = { "_id": edit_workflow.id, "_extra_data": edit_workflow.extra_data, "metadata": edit_workflow.data, } response = workflow_api_client.put( edit_workflow.extra_data['callback_url'], data=json.dumps(payload), content_type='application/json', ) expected_redirect = get_rt_link_for_ticket(1234) assert response.status_code == 200 data = json.loads(response.data) assert 'redirect_url' in data assert data['redirect_url'] == expected_redirect
def test_responses_with_etag(workflow_app): factory = TestRecordMetadata.create_from_kwargs( json={'titles': [{'title': 'Etag version'}]} ) workflow_id = build_workflow(factory.record_metadata.json).id obj = workflow_object_class.get(workflow_id) obj.save() db.session.commit() workflow_url = '/api/holdingpen/{}'.format(obj.id) with workflow_app.test_client() as client: login_user_via_session(client, email='*****@*****.**') response = client.get(workflow_url) assert response.status_code == 200 etag = response.headers['ETag'] last_modified = response.headers['Last-Modified'] response = client.get( workflow_url, headers={'If-Modified-Since': last_modified}) assert response.status_code == 304 response = client.get(workflow_url, headers={'If-None-Match': etag}) assert response.status_code == 304 response = client.get(workflow_url, headers={'If-None-Match': 'Jessica Jones'}) assert response.status_code == 200
def test_load_user_collections(app_client, user_info, collections): if user_info: login_user_via_session(app_client, email=user_info['email']) with app_client.get('/'): assert 'restricted_collection' not in session load_user_collections(current_app, current_user) assert session['restricted_collections'] == collections
def log_in_as_cataloger(api_client): """Ensure that we're logged in as a privileged user.""" login_user_via_session(api_client, email='*****@*****.**') yield SessionActivity.query.delete() db.session.commit()
def log_in_as_scientist(api_client): """Ensure that we're logged in as an unprivileged user.""" login_user_via_session(api_client, email='*****@*****.**') yield SessionActivity.query.delete() db.session.commit()
def test_record_restricted_detailed_read(app, app_client, user_info, status): """Test permissions of restricted record accessing detailed view.""" if user_info: # Login as user login_user_via_session(app_client, email=user_info['email']) assert app_client.get('/old-literature/1090628').status_code == status
def test_record_public_detailed_read(app, sample_record, users, user_info, status): """Test that a record can be read by everybody through web interface.""" with app.test_client() as client: if user_info: # Login as user login_user_via_session(client, email=user_info['email']) assert client.get("/literature/123").status_code == status
def test_record_api_update(app, api_client, sample_record, user_info, status): """Test that a record can be updated only from admin and cataloger users through the API.""" if user_info: login_user_via_session(api_client, email=user_info['email']) resp = api_client.put('/literature/111/db', data=json.dumps(sample_record.dumps()), content_type='application/json') assert resp.status_code == status
def test_record_restricted_detailed_read(app, restricted_record, users, user_info, status): """Test permissions of restricted record accessing detailed view.""" with app.test_client() as client: if user_info: # Login as user login_user_via_session(client, email=user_info['email']) assert client.get("/literature/222").status_code == status
def test_edit_article_view(workflow_api_client): login_user_via_session(workflow_api_client, email='*****@*****.**') factory = TestRecordMetadata.create_from_kwargs(json={}) control_number = factory.record_metadata.json['control_number'] endpoint_url = "/workflows/edit_article/{}".format(control_number) response = workflow_api_client.get(endpoint_url) assert response.status_code == 302 assert "/editor/holdingpen/" in response.headers['Location']
def test_login_user_via_session(app): """Test the login-via-view function/hack.""" email = '*****@*****.**' password = '******' with app.app_context(): user = testutils.create_test_user(email, password) with app.test_client() as client: assert not testutils.client_authenticated(client) testutils.login_user_via_session(client, email=user.email) assert testutils.client_authenticated(client)
def test_record_api_update_restricted_record(app, api_client, user_info, status): """Test that a restricted record can be updated only by users with the right permission.""" if user_info: login_user_via_session(api_client, email=user_info['email']) record = get_db_record('lit', 1090628) resp = api_client.put('/literature/1090628', data=json.dumps(record), content_type='application/json') assert resp.status_code == status
def test_edit_article_view_sets_user_id(workflow_api_client): user = User.query.filter_by(email='*****@*****.**').one() login_user_via_session(workflow_api_client, user=user) factory = TestRecordMetadata.create_from_kwargs(json={}) control_number = factory.record_metadata.json['control_number'] endpoint_url = "/workflows/edit_article/{}".format(control_number) response = workflow_api_client.get(endpoint_url) wflw_id = response.headers['Location'].split('/')[-1] wflw = workflow_object_class.get(wflw_id) assert wflw.id_user == int(user.get_id())
def test_index_new(app, test_client, users): """Test index view.""" with app.test_request_context(): index_url = url_for('invenio_deposit_ui.index') new_url = url_for('invenio_deposit_ui.new') for u in [index_url, new_url]: res = test_client.get(u) assert res.status_code == 302 assert '/login/' in res.location login_user_via_session(test_client, user=users[0]) for u in [index_url, new_url]: assert test_client.get(u).status_code == 200
def _call_workflow_resolve_api(app, workflow_id, data): """Calls to the workflow resolve endpoint. :param app: flask app to use :param workflow_id: id of the workflow to accept. :param action: action taken (normally on of 'reject', accept_core', 'accept') """ client = app.test_client() login_user_via_session(client, email='*****@*****.**') return client.post( '/api/holdingpen/%s/action/resolve' % workflow_id, data=json.dumps(data), content_type='application/json', )
def test_rejected(views_fixture, monkeypatch): """Test rejected.""" # Mock session id monkeypatch.setattr('invenio_oauthclient._compat._create_identifier', lambda: '1234') monkeypatch.setattr( 'invenio_oauthclient.views.client._create_identifier', lambda: '1234') app = views_fixture oauth = app.extensions['oauthlib.client'] # Mock user id user = MagicMock() user.get_id = MagicMock(return_value=1) user.is_authenticated = MagicMock(return_value=True) with app.test_client() as c: login_user_via_session(c, user) # First call login to be redirected res = c.get(url_for('invenio_oauthclient.login', remote_app='full')) assert res.status_code == 302 assert res.location.startswith( oauth.remote_apps['full'].authorize_url ) # Mock response to imitate an invalid response. Here, an # example from GitHub when the code is expired. mock_response(app.extensions['oauthlib.client'], 'full', data=dict( error_uri='http://developer.github.com/v3/oauth/' '#bad-verification-code', error_description='The code passed is ' 'incorrect or expired.', error='bad_verification_code', )) # Imitate that the user authorized our request in the remote # application (however, the remote app will son reply with an # error) state = serializer.dumps({ 'app': 'full', 'sid': '1234', 'next': None, }) res = c.get(url_for( 'invenio_oauthclient.authorized', remote_app='full', code='test', state=state )) assert res.status_code == 302
def test_edit_article_start_permission( workflow_app, workflow_api_client, user_info, expected_status_code, mocked_external_services, ): if user_info: login_user_via_session(workflow_api_client, email=user_info['email']) factory = TestRecordMetadata.create_from_kwargs(json={}) control_number = factory.record_metadata.json['control_number'] endpoint_url = "/workflows/edit_article/{}".format(control_number) response = workflow_api_client.get(endpoint_url) assert response.status_code == expected_status_code
def test_all_records_are_there(app_client): # Use superadmin user to ensure we can visit all records login_user_via_session(app_client, email='*****@*****.**') failed = [] for record in [record.json for record in RecordMetadata.query.all()]: try: absolute_url = record['self']['$ref'] relative_url = absolute_url.partition('api')[2] response = app_client.get(relative_url) assert response.status_code == 200 except Exception: failed.append(record['control_number']) assert failed == []
def test_edit_article_view_sets_curation_ticket_if_referrer_is_rt_ticket(workflow_api_client): login_user_via_session(workflow_api_client, email='*****@*****.**') factory = TestRecordMetadata.create_from_kwargs(json={}) control_number = factory.record_metadata.json['control_number'] endpoint_url = "/workflows/edit_article/{}".format(control_number) response = workflow_api_client.get( endpoint_url, headers={'Referer': get_rt_link_for_ticket(1234)} ) assert response.status_code == 302 assert "/editor/holdingpen/" in response.headers['Location'] wflw_id = response.headers['Location'].split('/')[-1] wflw = workflow_object_class.get(wflw_id) assert wflw.extra_data.get('curation_ticket_id') == 1234
def test_holdingpen_author_views_access(app, app_client, users): """Check permissions of author related views.""" # An anonymous user is redirected to login page res = app_client.get(url_for('inspirehep_authors.newreview')) assert res.status_code == 302 assert 'login' in res.location res = app_client.post( url_for('inspirehep_authors.reviewhandler')) assert res.status_code == 302 assert 'login' in res.location res = app_client.get( url_for('inspirehep_authors.holdingpenreview')) assert res.status_code == 302 assert 'login' in res.location # Logged-in user with no permission should receive 403 login_user_via_session(app_client, email='*****@*****.**') res = app_client.get(url_for('inspirehep_authors.newreview')) assert res.status_code == 403 res = app_client.post( url_for('inspirehep_authors.reviewhandler')) assert res.status_code == 403 res = app_client.get( url_for('inspirehep_authors.holdingpenreview')) assert res.status_code == 403 # User with admin-holdingpen-authors action allowed can access # XXX: 400 is returned because no object id is passed to the views login_user_via_session(app_client, email='*****@*****.**') res = app_client.get(url_for('inspirehep_authors.newreview')) assert res.status_code == 400 res = app_client.post( url_for('inspirehep_authors.reviewhandler')) assert res.status_code == 400 res = app_client.get( url_for('inspirehep_authors.holdingpenreview')) assert res.status_code == 400
def test_headers_info(app, users): """Test if session and user id is set response header.""" u = users[0] url = url_for_security('change_password') with app.app_context(): with app.test_client() as client: response = client.get(url) # Not logged in, so only session id available assert not testutils.client_authenticated(client) assert 'X-Session-ID' in response.headers assert 'X-User-ID' not in response.headers # Login testutils.login_user_via_session(client, email=u['email']) response = client.get(url) cookie = requests.utils.dict_from_cookiejar(client.cookie_jar) assert response.headers['X-Session-ID'] == \ cookie['session'].split('.')[0] assert int(response.headers['X-User-ID']) == u['id']
def test_edit_article_callback_permission( user_info, expected_status_code, edit_workflow, workflow_api_client, mocked_external_services, ): if user_info: login_user_via_session(workflow_api_client, email=user_info['email']) else: with workflow_api_client.session_transaction() as sess: sess['user_id'] = None payload = { "_id": edit_workflow.id, "_extra_data": edit_workflow.extra_data, "metadata": edit_workflow.data, } response = workflow_api_client.put( edit_workflow.extra_data['callback_url'], data=json.dumps(payload), content_type='application/json' ) assert response.status_code == expected_status_code
def test_tombstone(app, app_client, deposit, deposit_file, users): """Test tombstone for edit pages.""" with app.test_request_context(): record_url = url_for( 'invenio_records_ui.recid', pid_value=deposit['_deposit']['id']) deposit_url = url_for( 'invenio_deposit_ui.depid', pid_value=deposit['_deposit']['id']) delete_url = url_for( 'zenodo_deposit.delete', pid_value=deposit['_deposit']['id']) deposit.publish() recid, record = deposit.fetch_published() recid.delete() deposit.pid.delete() assert app_client.post(record_url).status_code == 302 assert app_client.get(delete_url).status_code == 302 assert app_client.get(deposit_url).status_code == 410 login_user_via_session(app_client, email=users[0]['email']) assert app_client.post(record_url).status_code == 410 assert app_client.get(deposit_url).status_code == 410 assert app_client.get(delete_url).status_code == 410 login_user_via_session(app_client, email=users[2]['email']) assert app_client.get(delete_url).status_code == 410
def test_edit_article_callback_does_not_have_redirect_url_if_no_ticket_id( edit_workflow, workflow_api_client, mocked_external_services, workflow_app, ): user_info = {'email': '*****@*****.**'} login_user_via_session(workflow_api_client, email=user_info['email']) payload = { "_id": edit_workflow.id, "_extra_data": edit_workflow.extra_data, "metadata": edit_workflow.data, } response = workflow_api_client.put( edit_workflow.extra_data['callback_url'], data=json.dumps(payload), content_type='application/json', ) assert response.status_code == 200 data = json.loads(response.data) assert 'redirect_url' not in data
def test_deposit_ui_login(app, app_client, deposit, deposit_file, users): """Test login on deposit views.""" with app.test_request_context(): record_url = url_for( 'invenio_records_ui.recid', pid_value=deposit['_deposit']['id']) delete_url = url_for( 'zenodo_deposit.delete', pid_value=deposit['_deposit']['id']) deposit_url = url_for( 'invenio_deposit_ui.depid', pid_value=deposit['_deposit']['id']) new_url = url_for('invenio_deposit_ui.new') index_url = url_for('invenio_deposit_ui.index') # Unauthenticated users assert app_client.get(index_url).status_code == 302 assert app_client.get(new_url).status_code == 302 assert app_client.get(deposit_url).status_code == 302 assert app_client.get(delete_url).status_code == 302 deposit.publish() assert app_client.get(record_url).status_code == 200 assert app_client.post(record_url).status_code == 302 # Login user NOT owner of deposit login_user_via_session(app_client, email=users[1]['email']) # Can list deposits, create new, and view record. assert app_client.get(index_url).status_code == 200 assert app_client.get(new_url).status_code == 200 assert app_client.get(record_url).status_code == 200 # - cannot view deposit or put record in edit mode assert app_client.get(deposit_url).status_code == 403 assert app_client.post(record_url).status_code == 403 assert app_client.get(delete_url).status_code == 403 # Login owner of deposit login_user_via_session(app_client, email=users[0]['email']) # - can view deposit or put record in edit mode res = app_client.post(record_url) assert res.status_code == 302 assert 'login' not in res.location assert app_client.get(deposit_url).status_code == 200 assert app_client.get(delete_url).status_code == 403 login_user_via_session(app_client, email=users[2]['email'])
def _login(client, user, users): """Login user and return url.""" login_user_via_session(client, user=User.query.get(user.id)) return user
def test_update(client, make_user, superuser, admin, moderator, submitter, user): """Test update users permissions.""" headers = { 'Content-Type': 'application/json', 'Accept': 'application/json' } # Not logged res = client.put(url_for('invenio_records_rest.user_item', pid_value=user['pid']), data=json.dumps(user.dumps()), headers=headers) assert res.status_code == 401 # Logged as user and update himself login_user_via_session(client, email=user['email']) res = client.put(url_for('invenio_records_rest.user_item', pid_value=user['pid']), data=json.dumps(user.dumps()), headers=headers) assert res.status_code == 200 # Logged as user and update other login_user_via_session(client, email=user['email']) res = client.put(url_for('invenio_records_rest.user_item', pid_value=moderator['pid']), data=json.dumps(moderator.dumps()), headers=headers) assert res.status_code == 403 # Logged as submitter login_user_via_session(client, email=submitter['email']) res = client.put(url_for('invenio_records_rest.user_item', pid_value=submitter['pid']), data=json.dumps(submitter.dumps()), headers=headers) assert res.status_code == 200 login_user_via_session(client, email=submitter['email']) res = client.put(url_for('invenio_records_rest.user_item', pid_value=user['pid']), data=json.dumps(user.dumps()), headers=headers) assert res.status_code == 403 # Logged as moderator login_user_via_session(client, email=moderator['email']) res = client.put(url_for('invenio_records_rest.user_item', pid_value=moderator['pid']), data=json.dumps(moderator.dumps()), headers=headers) assert res.status_code == 200 login_user_via_session(client, email=moderator['email']) res = client.put(url_for('invenio_records_rest.user_item', pid_value=user['pid']), data=json.dumps(user.dumps()), headers=headers) assert res.status_code == 403 # Logged as admin login_user_via_session(client, email=admin['email']) res = client.put(url_for('invenio_records_rest.user_item', pid_value=user['pid']), data=json.dumps(user.dumps()), headers=headers) assert res.status_code == 200 # Logged as admin, try to update super user login_user_via_session(client, email=admin['email']) res = client.put(url_for('invenio_records_rest.user_item', pid_value=superuser['pid']), data=json.dumps(superuser.dumps()), headers=headers) assert res.status_code == 403 # Logged as admin of other organisation other_admin = make_user('admin', 'org2') login_user_via_session(client, email=other_admin['email']) res = client.put(url_for('invenio_records_rest.user_item', pid_value=user['pid']), data=json.dumps(user.dumps()), headers=headers) assert res.status_code == 403 # Logged as superuser login_user_via_session(client, email=superuser['email']) res = client.put(url_for('invenio_records_rest.user_item', pid_value=user['pid']), data=json.dumps(user.dumps()), headers=headers) assert res.status_code == 200
def test_create(client, organisation, superuser, admin, moderator, submitter, user): """Test create users permissions.""" headers = { 'Content-Type': 'application/json', 'Accept': 'application/json' } user_json = { 'email': '*****@*****.**', 'full_name': 'User', 'roles': ['user'] } # Not logged res = client.post(url_for('invenio_records_rest.user_list'), data=json.dumps(user_json), headers=headers) assert res.status_code == 401 # User login_user_via_session(client, email=user['email']) res = client.post(url_for('invenio_records_rest.user_list'), data=json.dumps(user_json), headers=headers) assert res.status_code == 403 # submitter login_user_via_session(client, email=submitter['email']) res = client.post(url_for('invenio_records_rest.user_list'), data=json.dumps(user_json), headers=headers) assert res.status_code == 403 # Moderator login_user_via_session(client, email=moderator['email']) res = client.post(url_for('invenio_records_rest.user_list'), data=json.dumps(user_json), headers=headers) assert res.status_code == 403 # Admin login_user_via_session(client, email=admin['email']) res = client.post(url_for('invenio_records_rest.user_list'), data=json.dumps(user_json), headers=headers) assert res.status_code == 201 assert res.json['metadata']['organisation'][ '$ref'] == 'https://sonar.ch/api/organisations/org' # Super user user_json['organisation'] = { '$ref': 'https://sonar.ch/api/organisations/{organisation}'.format( organisation=organisation['pid']) } login_user_via_session(client, email=superuser['email']) res = client.post(url_for('invenio_records_rest.user_list'), data=json.dumps(user_json), headers=headers) assert res.status_code == 201 assert res.json['metadata']['organisation'][ '$ref'] == 'https://sonar.ch/api/organisations/org'
def test_items_automatic_checkin(client, librarian_martigny_no_email, patron_martigny_no_email, loc_public_martigny, item_type_standard_martigny, loc_public_saxon, item_lib_martigny, json_header, circulation_policies): """Test automatic checkin for items.""" login_user_via_session(client, librarian_martigny_no_email.user) item = item_lib_martigny item_pid = item.pid patron_pid = patron_martigny_no_email.pid location = loc_public_martigny # loose item res, _ = postdata(client, 'api_item.lose', dict(item_pid=item_pid)) assert res.status_code == 200 # return missing item res, data = postdata(client, 'api_item.automatic_checkin', dict(item_barcode=item.get('barcode'))) assert res.status_code == 200 item_data = data.get('metadata') actions = data.get('action_applied') assert item_data.get('status') == ItemStatus.ON_SHELF assert LoanAction.RETURN_MISSING in actions # checkout res, data = postdata(client, 'api_item.checkout', dict(item_pid=item_pid, patron_pid=patron_pid)) assert res.status_code == 200 actions = data.get('action_applied') loan_pid = actions[LoanAction.CHECKOUT].get('pid') # checkin res, data = postdata( client, 'api_item.checkin', dict(item_pid=item_pid, pid=loan_pid, transaction_location_pid=loc_public_saxon.pid)) assert res.status_code == 200 item_data = data.get('metadata') actions = data.get('action_applied') assert item_data.get('status') == ItemStatus.IN_TRANSIT assert LoanAction.CHECKIN in actions # receive res, data = postdata( client, 'api_item.automatic_checkin', dict(item_pid=item_pid, pid=loan_pid, transaction_location_pid=loc_public_martigny.pid)) assert res.status_code == 200 item_data = data.get('metadata') actions = data.get('action_applied') assert item_data.get('status') == ItemStatus.ON_SHELF assert LoanAction.RECEIVE in actions # checkout res, _ = postdata(client, 'api_item.checkout', dict(item_pid=item_pid, patron_pid=patron_pid)) assert res.status_code == 200 # checkin res, data = postdata(client, 'api_item.automatic_checkin', dict(item_barcode=item.get('barcode'))) assert res.status_code == 200 item_data = data.get('metadata') actions = data.get('action_applied') assert item_data.get('status') == ItemStatus.ON_SHELF assert LoanAction.CHECKIN in actions
def test_delete(client, db, make_deposit, superuser, admin, moderator, submitter, user): """Test delete deposits permissions.""" deposit1 = make_deposit('submitter', 'org') deposit2 = make_deposit('submitter', 'org2') # Not logged res = client.delete( url_for('invenio_records_rest.depo_item', pid_value=deposit1['pid'])) assert res.status_code == 401 # Logged as user login_user_via_session(client, email=user['email']) res = client.delete( url_for('invenio_records_rest.depo_item', pid_value=deposit1['pid'])) assert res.status_code == 403 # Logged as submitter login_user_via_session(client, email=submitter['email']) res = client.delete( url_for('invenio_records_rest.depo_item', pid_value=deposit2['pid'])) assert res.status_code == 403 deposit1['status'] = DepositRecord.STATUS_VALIDATED deposit1.commit() db.session.commit() res = client.delete( url_for('invenio_records_rest.depo_item', pid_value=deposit1['pid'])) assert res.status_code == 403 deposit1['status'] = DepositRecord.STATUS_IN_PROGRESS deposit1.commit() db.session.commit() res = client.delete( url_for('invenio_records_rest.depo_item', pid_value=deposit1['pid'])) assert res.status_code == 204 deposit1 = make_deposit('submitter', 'org') # Logged as moderator login_user_via_session(client, email=moderator['email']) res = client.delete( url_for('invenio_records_rest.depo_item', pid_value=deposit2['pid'])) assert res.status_code == 403 res = client.delete( url_for('invenio_records_rest.depo_item', pid_value=deposit1['pid'])) assert res.status_code == 204 deposit1 = make_deposit('submitter', 'org') # Logged as admin login_user_via_session(client, email=admin['email']) res = client.delete( url_for('invenio_records_rest.depo_item', pid_value=deposit2['pid'])) assert res.status_code == 403 res = client.delete( url_for('invenio_records_rest.depo_item', pid_value=deposit1['pid'])) assert res.status_code == 204 deposit1 = make_deposit('submitter', 'org') # Logged as superuser login_user_via_session(client, email=superuser['email']) res = client.delete( url_for('invenio_records_rest.depo_item', pid_value=deposit1['pid'])) assert res.status_code == 204
def test_read(client, db, make_deposit, make_user, superuser, admin, moderator, submitter, user, subdivision, subdivision2): """Test read deposits permissions.""" deposit1 = make_deposit('submitter', 'org') deposit2 = make_deposit('submitter', 'org2') # Not logged res = client.get( url_for('invenio_records_rest.depo_item', pid_value=deposit1['pid'])) assert res.status_code == 401 # Logged as user login_user_via_session(client, email=user['email']) res = client.get( url_for('invenio_records_rest.depo_item', pid_value=deposit1['pid'])) assert res.status_code == 403 # Logged as submitter login_user_via_session(client, email=submitter['email']) res = client.get( url_for('invenio_records_rest.depo_item', pid_value=deposit1['pid'])) assert res.status_code == 200 res = client.get( url_for('invenio_records_rest.depo_item', pid_value=deposit2['pid'])) assert res.status_code == 403 # Logged as moderator login_user_via_session(client, email=moderator['email']) res = client.get( url_for('invenio_records_rest.depo_item', pid_value=deposit1['pid'])) assert res.status_code == 200 # Moderator has subdivision, I can read a deposit of my subdivision sub_pid = deposit1["diffusion"]["subdivisions"][0]["$ref"].split('/')[-1] moderator['subdivision'] = { '$ref': f'https://sonar.ch/api/subdivisions/{sub_pid}' } moderator.commit() moderator.reindex() db.session.commit() res = client.get( url_for('invenio_records_rest.depo_item', pid_value=deposit1['pid'])) assert res.status_code == 200 # Moderator has subdivision, I cannot read deposit outside of his # subdivision moderator['subdivision'] = { '$ref': f'https://sonar.ch/api/subdivisions/{subdivision2["pid"]}' } moderator.commit() moderator.reindex() db.session.commit() res = client.get( url_for('invenio_records_rest.depo_item', pid_value=deposit1['pid'])) assert res.status_code == 403 # Cannot read deposit of other organisations res = client.get( url_for('invenio_records_rest.depo_item', pid_value=deposit2['pid'])) assert res.status_code == 403 # Logged as admin login_user_via_session(client, email=admin['email']) res = client.get( url_for('invenio_records_rest.depo_item', pid_value=deposit1['pid'])) assert res.status_code == 200 # Cannot read deposit of other organisations res = client.get( url_for('invenio_records_rest.depo_item', pid_value=deposit2['pid'])) assert res.status_code == 403 # Logged as admin of other organisation other_admin = make_user('admin', 'org2') login_user_via_session(client, email=other_admin['email']) res = client.get( url_for('invenio_records_rest.depo_item', pid_value=deposit1['pid'])) assert res.status_code == 403 # Logged as superuser login_user_via_session(client, email=superuser['email']) res = client.get( url_for('invenio_records_rest.depo_item', pid_value=deposit1['pid'])) assert res.status_code == 200
def test_create_over_and_due_soon_notifications_task( client, librarian_martigny_no_email, patron_martigny_no_email, item_lib_martigny, circ_policy_short_martigny, loc_public_martigny, lib_martigny): """Test overdue and due_soon loans.""" login_user_via_session(client, librarian_martigny_no_email.user) item = item_lib_martigny item_pid = item.pid patron_pid = patron_martigny_no_email.pid # checkout res, data = postdata( client, 'api_item.checkout', dict( item_pid=item_pid, patron_pid=patron_pid, transaction_location_pid=loc_public_martigny.pid, transaction_user_pid=librarian_martigny_no_email.pid, )) assert res.status_code == 200 loan_pid = data.get('action_applied')[LoanAction.CHECKOUT].get('pid') loan = Loan.get_record_by_pid(loan_pid) # test due_soon notification end_date = datetime.now(timezone.utc) + timedelta(days=3) loan['end_date'] = end_date.isoformat() loan.update(loan, dbcommit=True, reindex=True) due_soon_loans = get_due_soon_loans() assert due_soon_loans[0].get('pid') == loan_pid create_over_and_due_soon_notifications() flush_index(NotificationsSearch.Meta.index) flush_index(LoansSearch.Meta.index) assert loan.is_notified(notification_type='due_soon') # test overdue notification end_date = datetime.now(timezone.utc) - timedelta(days=7) loan['end_date'] = end_date.isoformat() loan.update(loan, dbcommit=True, reindex=True) overdue_loans = get_overdue_loans() assert overdue_loans[0].get('pid') == loan_pid create_over_and_due_soon_notifications() flush_index(NotificationsSearch.Meta.index) flush_index(LoansSearch.Meta.index) assert loan.is_notified(notification_type='overdue') assert number_of_reminders_sent(loan) == 1 # checkin the item to put it back to it's original state res, _ = postdata( client, 'api_item.checkin', dict(item_pid=item_pid, pid=loan_pid, transaction_location_pid=loc_public_martigny.pid, transaction_user_pid=librarian_martigny_no_email.pid)) assert res.status_code == 200
def test_simple_workflow(app, db, es, users, location, cds_jsonresolver, project_metadata, json_headers, deposit_rest, data_file_1, data_file_2): """Test project simple workflow.""" def check_connection(videos, project): """check project <---> video connection.""" assert all({"$reference": video.ref} in project['videos'] for video in videos) assert len(videos) == len(project['videos']) project_schema = ('https://cdslabs.cern.ch/schemas/' 'deposits/records/project-v1.0.0.json') video_schema = ('https://cdslabs.cern.ch/schemas/' 'deposits/records/video-v1.0.0.json') with app.test_client() as client: login_user_via_session(client, email=User.query.get(users[0]).email) # [[ CREATE NEW PROJECT ]] res = client.post(url_for('invenio_deposit_rest.project_list'), data=json.dumps(project_metadata), headers=json_headers) # check returned value assert res.status_code == 201 project_dict = json.loads(res.data.decode('utf-8')) assert project_dict['metadata']['videos'] == [] assert project_dict['metadata']['title']['title'] == 'my project' assert project_dict['links']['bucket'].startswith( 'http://localhost/files/') assert all( link.startswith('http://localhost/deposits/project') for (key, link) in project_dict['links'].items() if key not in ['html', 'bucket']) # check database project_id = project_dict['metadata']['_deposit']['id'] project = project_resolver(project_id) assert project['$schema'] == project_schema # [[ ADD A NEW EMPTY VIDEO_1 ]] res = client.post(url_for('invenio_deposit_rest.video_list'), data=json.dumps({ '_project_id': project_dict['metadata']['_deposit']['id'], }), headers=json_headers) # check returned value assert res.status_code == 201 video_1_dict = json.loads(res.data.decode('utf-8')) assert video_1_dict['metadata']['_project_id'] == project_id assert all( link.startswith('http://localhost/deposits/video') for (key, link) in video_1_dict['links'].items() if key not in ['html', 'bucket']) # check database: connection project <---> videos video_ids = [video_1_dict['metadata']['_deposit']['id']] [video_1] = video_resolver(video_ids) check_connection([video_1], project_resolver( project_dict['metadata']['_deposit']['id'])) assert video_1['$schema'] == video_schema # [[ GET THE VIDEO 1 ]] res = client.get(video_1_dict['links']['self'], headers=json_headers) # check returned value assert res.status_code == 200 video_1_dict = json.loads(res.data.decode('utf-8')) assert video_1_dict['metadata']['_files'] == [] # [[ ADD A NEW EMPTY VIDEO_2 ]] res = client.post(url_for('invenio_deposit_rest.video_list'), data=json.dumps({ '_project_id': project_dict['metadata']['_deposit']['id'], }), headers=json_headers) # check returned value assert res.status_code == 201 video_2_dict = json.loads(res.data.decode('utf-8')) assert video_2_dict['metadata']['_project_id'] == project_id assert all( link.startswith('http://localhost/deposits/video') for (key, link) in video_2_dict['links'].items() if key not in ['html', 'bucket']) # check database: connection project <---> videos video_ids = [ video_1_dict['metadata']['_deposit']['id'], video_2_dict['metadata']['_deposit']['id'] ] [video_1, video_2] = video_resolver(video_ids) check_connection([video_1, video_2], project_resolver( project_dict['metadata']['_deposit']['id'])) assert video_2['$schema'] == video_schema # [[ ADD A FILE INSIDE VIDEO_1 ]] res = client.post(url_for( 'invenio_deposit_rest.video_files', pid_value=video_1_dict['metadata']['_deposit']['id']), data=data_file_1, content_type='multipart/form-data') # check returned value assert res.status_code == 201 file_1 = json.loads(res.data.decode('utf-8')) assert file_1['checksum'] == 'md5:eb88ae1e3666e6fe96a33ea72aab630e' assert file_1['filesize'] == 24 assert file_1['filename'] == 'test.json' assert file_1['id'] # check database: connection project <---> videos [video_1 ] = video_resolver([video_1_dict['metadata']['_deposit']['id']]) assert video_1['_files'][0]['key'] == 'test.json' video_ids = [ video_1_dict['metadata']['_deposit']['id'], video_2_dict['metadata']['_deposit']['id'] ] check_connection( video_resolver(video_ids), project_resolver(project_dict['metadata']['_deposit']['id'])) # [[ GET THE VIDEO 1 ]] res = client.get(video_1_dict['links']['self'], headers=json_headers) # check video metadata assert res.status_code == 200 video_1_dict = json.loads(res.data.decode('utf-8')) assert len(video_1_dict['metadata']['_files']) == 1 myfile = video_1_dict['metadata']['_files'][0] assert myfile['links']['self'].startswith('/api/files/') assert myfile['checksum'] == 'md5:eb88ae1e3666e6fe96a33ea72aab630e' assert myfile['completed'] is True assert 'version_id' in myfile assert myfile['key'] == 'test.json' assert myfile['progress'] == 100 assert myfile['size'] == 24 # [[ PUBLISH VIDEO_1 ]] # Not need to send _files del video_1_dict['metadata']['_files'] res = client.post(url_for('invenio_deposit_rest.video_actions', pid_value=video_1['_deposit']['id'], action='publish'), headers=json_headers) # check returned value assert res.status_code == 202 video_1_dict = json.loads(res.data.decode('utf-8')) assert video_1_dict['metadata']['_deposit']['status'] == 'published' assert video_1_dict['metadata']['recid'] == 1 assert video_1_dict['metadata']['_project_id'] == project_id # check database: connection project <---> videos video_ids = [ video_1_dict['metadata']['_deposit']['id'], video_2_dict['metadata']['_deposit']['id'] ] check_connection( video_resolver(video_ids), project_resolver(project_dict['metadata']['_deposit']['id'])) # [[ ADD A VIDEO INSIDE VIDEO_2 ]] res = client.post(url_for( 'invenio_deposit_rest.video_files', pid_value=video_2_dict['metadata']['_deposit']['id']), data=data_file_2, content_type='multipart/form-data') # check returned value assert res.status_code == 201 file_2 = json.loads(res.data.decode('utf-8')) assert file_2['checksum'] == 'md5:95405c14852500dcbb6dbfd9e27a3594' assert file_2['filesize'] == 26 assert file_2['filename'] == 'test2.json' # check connection project <---> videos [video_2 ] = video_resolver([video_2_dict['metadata']['_deposit']['id']]) assert video_2['_files'][0]['key'] == 'test2.json' video_ids = [ video_1_dict['metadata']['_deposit']['id'], video_2_dict['metadata']['_deposit']['id'] ] check_connection( video_resolver(video_ids), project_resolver(project_dict['metadata']['_deposit']['id'])) # [[ PUBLISH THE PROJECT ]] res = client.post(url_for('invenio_deposit_rest.project_actions', pid_value=project['_deposit']['id'], action='publish'), headers=json_headers) # check returned value assert res.status_code == 202 project_dict = json.loads(res.data.decode('utf-8')) assert project_dict['metadata']['_deposit']['status'] == 'published' assert project_dict['metadata']['recid'] == 3 assert project_dict['metadata']['videos'][0] == { '$reference': '/record/1' } assert project_dict['metadata']['videos'][1] == { '$reference': '/record/2' } # check database: connection project <---> videos video_ids = [ video_1_dict['metadata']['_deposit']['id'], video_2_dict['metadata']['_deposit']['id'] ] check_connection( video_resolver(video_ids), project_resolver(project_dict['metadata']['_deposit']['id'])) # [[ EDIT THE PROJECT ]] res = client.post(url_for( 'invenio_deposit_rest.project_actions', pid_value=project_dict['metadata']['_deposit']['id'], action='edit'), headers=json_headers) # check returned value assert res.status_code == 201 project_dict = json.loads(res.data.decode('utf-8')) assert project_dict['metadata']['_deposit']['status'] == 'draft' # check database project = project_resolver(project_dict['metadata']['_deposit']['id']) assert project['_deposit']['status'] == 'draft' # [[ MODIFY PROJECT ]] project_dict['metadata']['title']['title'] = 'new project title' # Not need to send _files del project_dict['metadata']['_files'] res = client.put(url_for( 'invenio_deposit_rest.project_item', pid_value=project_dict['metadata']['_deposit']['id']), data=json.dumps(project_dict['metadata']), headers=json_headers) # check returned value assert res.status_code == 200 project_dict = json.loads(res.data.decode('utf-8')) assert project_dict['metadata']['title']['title'] ==\ 'new project title' assert all( link.startswith('http://localhost/deposits/project') for (key, link) in project_dict['links'].items() if key not in ['html', 'bucket']) # check database project = project_resolver(project_dict['metadata']['_deposit']['id']) assert project['title']['title'] == 'new project title' # [[ DISCARD PROJECT ]] res = client.post(url_for( 'invenio_deposit_rest.project_actions', pid_value=project_dict['metadata']['_deposit']['id'], action='discard'), headers=json_headers) # check returned value assert res.status_code == 201 project_dict = json.loads(res.data.decode('utf-8')) assert project_dict['metadata']['title']['title'] == 'my project' # check database project = project_resolver(project_dict['metadata']['_deposit']['id']) assert project['title']['title'] == 'my project'
def test_patrons_count(client, patron_sion_no_email, librarian_martigny_no_email, system_librarian_sion_no_email): """Test number of email address.""" librarian_email = librarian_martigny_no_email.get('email') url = url_for('api_patrons.number_of_patrons', q=librarian_email) res = client.get(url) assert res.status_code == 401 login_user_via_session(client, patron_sion_no_email.user) res = client.get(url) assert res.status_code == 403 login_user_via_session(client, librarian_martigny_no_email.user) # malformed url res = client.get(url) assert res.status_code == 400 # librarian email url = url_for('api_patrons.number_of_patrons', q='email:"{email}"'.format( email=librarian_martigny_no_email.get('email'))) res = client.get(url) assert res.status_code == 200 assert get_json(res) == dict(hits=dict(total=1)) # patron email url = url_for( 'api_patrons.number_of_patrons', q='email:"{email}"'.format(email=patron_sion_no_email.get('email'))) res = client.get(url) assert res.status_code == 200 assert get_json(res) == dict(hits=dict(total=1)) # patron email excluding itself url = url_for('api_patrons.number_of_patrons', q='email:"{email}" NOT pid:{pid}'.format( email=patron_sion_no_email.get('email'), pid=patron_sion_no_email.pid)) res = client.get(url) assert get_json(res) == dict(hits=dict(total=0)) # patron email excluding itself url = url_for('api_patrons.number_of_patrons', q='email:"{email}"'.format(email='*****@*****.**')) res = client.get(url) assert get_json(res) == dict(hits=dict(total=0)) # librarian email uppercase url = url_for('api_patrons.number_of_patrons', q='email:"{email}"'.format(email=librarian_email.upper())) res = client.get(url) assert get_json(res) == dict(hits=dict(total=1)) # librarian email with spaces url = url_for('api_patrons.number_of_patrons', q='email:" {email} "'.format(email=librarian_email.upper())) res = client.get(url) assert get_json(res) == dict(hits=dict(total=1)) # system librarian email containing a + char url = url_for( 'api_patrons.number_of_patrons', q='email:"{email}"'.format( email=system_librarian_sion_no_email.get('email').upper())) res = client.get(url) assert get_json(res) == dict(hits=dict(total=1)) # patron username url = url_for('api_patrons.number_of_patrons', q='username:"******"'.format( username=patron_sion_no_email.get('username'))) res = client.get(url) assert res.status_code == 200 assert get_json(res) == dict(hits=dict(total=1))
def test_pending_loans_order(client, librarian_martigny, patron_martigny, loc_public_martigny, item_type_standard_martigny, item2_lib_martigny, json_header, patron2_martigny, patron_sion, circulation_policies): """Test sort of pending loans.""" login_user_via_session(client, librarian_martigny.user) library_pid = librarian_martigny\ .replace_refs()['libraries'][0]['pid'] res, _ = postdata( client, 'api_item.librarian_request', dict( item_pid=item2_lib_martigny.pid, patron_pid=patron_sion.pid, pickup_location_pid=loc_public_martigny.pid, transaction_user_pid=librarian_martigny.pid, transaction_location_pid=loc_public_martigny.pid ) ) res, _ = postdata( client, 'api_item.librarian_request', dict( item_pid=item2_lib_martigny.pid, patron_pid=patron_martigny.pid, pickup_location_pid=loc_public_martigny.pid, transaction_user_pid=librarian_martigny.pid, transaction_location_pid=loc_public_martigny.pid ) ) assert res.status_code == 200 res, _ = postdata( client, 'api_item.librarian_request', dict( item_pid=item2_lib_martigny.pid, patron_pid=patron2_martigny.pid, pickup_location_pid=loc_public_martigny.pid, transaction_user_pid=librarian_martigny.pid, transaction_location_pid=loc_public_martigny.pid ) ) assert res.status_code == 200 # sort by pid asc res = client.get( url_for( 'api_item.requested_loans', library_pid=library_pid, sort='pid')) assert res.status_code == 200 data = get_json(res) loans = data['hits']['hits'][0]['item']['pending_loans'] assert loans[2]['pid'] > loans[1]['pid'] > loans[0]['pid'] # sort by pid desc res = client.get( url_for( 'api_item.requested_loans', library_pid=library_pid, sort='-pid')) assert res.status_code == 200 data = get_json(res) loans = data['hits']['hits'][0]['item']['pending_loans'] assert loans[2]['pid'] < loans[1]['pid'] < loans[0]['pid'] # sort by transaction desc res = client.get( url_for( 'api_item.requested_loans', library_pid=library_pid, sort='-transaction_date')) assert res.status_code == 200 data = get_json(res) loans = data['hits']['hits'][0]['item']['pending_loans'] assert loans[2]['pid'] < loans[1]['pid'] < loans[0]['pid'] # sort by patron_pid asc res = client.get( url_for( 'api_item.requested_loans', library_pid=library_pid, sort='patron_pid')) assert res.status_code == 200 data = get_json(res) loans = data['hits']['hits'][0]['item']['pending_loans'] assert loans[0]['patron_pid'] == patron_sion.pid assert loans[1]['patron_pid'] == patron_martigny.pid assert loans[2]['patron_pid'] == patron2_martigny.pid # sort by invalid field res = client.get( url_for( 'api_item.requested_loans', library_pid=library_pid, sort='does not exist')) assert res.status_code == 500 data = get_json(res) assert 'RequestError(400' in data['status']
def test_patron_transaction_event_secure_api_create( client, librarian_martigny, librarian_sion, patron_transaction_overdue_event_martigny, system_librarian_martigny, system_librarian_sion): """Test patron transction event secure api create.""" # Martigny login_user_via_session(client, librarian_martigny.user) post_entrypoint = 'invenio_records_rest.ptre_list' patron_event = deepcopy(patron_transaction_overdue_event_martigny) del patron_event['pid'] res, _ = postdata( client, post_entrypoint, patron_event ) # librarian is authorized to create a patron event in its library. assert res.status_code == 201 patron_event_2 = deepcopy(patron_transaction_overdue_event_martigny) del patron_event_2['pid'] res, _ = postdata( client, post_entrypoint, patron_event_2 ) # librarian is can create a patron event in other libraries. assert res.status_code == 201 login_user_via_session(client, system_librarian_martigny.user) res, _ = postdata( client, post_entrypoint, patron_event_2 ) # sys_librarian is authorized to create any patron event in its org. assert res.status_code == 201 # Sion login_user_via_session(client, librarian_sion.user) patron_event_3 = deepcopy(patron_transaction_overdue_event_martigny) del patron_event_3['pid'] res, _ = postdata( client, post_entrypoint, patron_event_3 ) # librarian is not authorized to create a patron event at other org. assert res.status_code == 403 login_user_via_session(client, system_librarian_sion.user) res, _ = postdata( client, post_entrypoint, patron_event_3 ) # sys_librarian can not to create a patron event in other org. assert res.status_code == 403
def test_records_rest(api_app, users, es, api_project_published, vtt_headers, datacite_headers, json_headers, smil_headers, drupal_headers, extra_metadata, _deposit_metadata): """Test view.""" indexer = RecordIndexer() (project, video_1, video_2) = api_project_published pid, record_project = project.fetch_published() vid, record_video = video_1.fetch_published() bucket_id = str(video_1['_buckets']['deposit']) # index project project.indexer.index(record_project) # index video record_video['_files'] = get_files_metadata(bucket_id) record_video['_deposit'].update(_deposit_metadata) record_video.update(extra_metadata) record_video.commit() indexer.index(record_video) sleep(1) with api_app.test_client() as client: login_user_via_session(client, email=User.query.get(users[0]).email) url = url_for('invenio_records_rest.recid_item', pid_value=pid.pid_value) url2 = url_for('invenio_records_rest.recid_item', pid_value=vid.pid_value) # try get json res = client.get(url, headers=json_headers) assert res.status_code == 200 project_dict = json.loads(res.data.decode('utf-8')) assert project_dict['metadata']['_deposit']['id'] == project[ '_deposit']['id'] # try get smil res = client.get(url, headers=smil_headers) assert res.status_code == 400 res = client.get(url2, headers=smil_headers) assert res.status_code == 200 # try get vtt res = client.get(url, headers=vtt_headers) assert res.status_code == 400 res = client.get(url2, headers=vtt_headers) assert res.status_code == 200 # try get drupal report_number = u'CERN-MOVIE-2017-1-1' file_frame = 'http://cds.cern.ch/api/files/123/frame-1.jpg' with mock.patch('cds.modules.records.api.CDSFileObject._link', return_value=file_frame): res = client.get(url2, headers=drupal_headers) # test legacy api/mediaexport url legacy_url = u'/mediaexport?id={id}'.format(id=report_number) assert client.get(legacy_url, follow_redirects=False) \ .status_code == 301 res_legacy = client.get(legacy_url, follow_redirects=True) assert res.status_code == 200 assert res_legacy.status_code == 200 drupal = json.loads(res.data.decode('utf-8')) thumbnail = u'http://cds.cern.ch/api/files/123/frame-1.jpg' expected = { u'entries': [{ u'entry': { u'caption_en': u'in tempor reprehenderit enim eiusmod', u'caption_fr': u'france caption', u'copyright_date': u'2017', u'copyright_holder': u'CERN', u'creation_date': u'2017-03-02', u'directors': u'paperone, pluto', u'entry_date': u'2017-09-25', u'id': report_number, u'keywords': u'keyword1, keyword2', u'license_body': u'GPLv2', u'license_url': u'http://license.cern.ch', u'producer': u'nonna papera, zio paperino', u'record_id': u'1', u'thumbnail': thumbnail, u'title_en': u'My english title', u'title_fr': u'My french title', u'type': u'video', u'video_length': u'00:01:00', } }] } assert expected == drupal drupal_legacy = json.loads(res_legacy.data.decode('utf-8')) assert expected == drupal_legacy # try get datacite res = client.get(url2, headers=datacite_headers) assert res.status_code == 200 assert res.data.decode('utf-8').startswith('<?xml version=') # test corner cases del record_video['title_translations'] del record_video['description_translations'] record_video.commit() db.session.commit() with api_app.test_client() as client: # try get drupal file_frame = 'http://cds.cern.ch/api/files/123/frame-1.jpg' with mock.patch('cds.modules.records.api.CDSFileObject._link', return_value=file_frame): res = client.get(url2, headers=drupal_headers) assert res.status_code == 200 drupal = json.loads(res.data.decode('utf-8')) thumbnail = u'http://cds.cern.ch/api/files/123/frame-1.jpg' expected = { u'entries': [{ u'entry': { u'caption_en': u'in tempor reprehenderit enim eiusmod', u'caption_fr': u'', u'copyright_date': u'2017', u'copyright_holder': u'CERN', u'creation_date': u'2017-03-02', u'directors': u'paperone, pluto', u'entry_date': u'2017-09-25', u'id': u'CERN-MOVIE-2017-1-1', u'keywords': u'keyword1, keyword2', u'license_body': u'GPLv2', u'license_url': u'http://license.cern.ch', u'producer': u'nonna papera, zio paperino', u'record_id': u'1', u'thumbnail': thumbnail, u'title_en': u'My english title', u'title_fr': u'', u'type': u'video', u'video_length': u'00:01:00', } }] } assert expected == drupal
def test_system_librarian_permissions(client, json_header, system_librarian_martigny_no_email, patron_martigny_no_email, patron_type_adults_martigny, librarian_fully_no_email): """Test system_librarian permissions.""" # Login as system_librarian login_user_via_session(client, system_librarian_martigny_no_email.user) record = { "$schema": "https://ils.rero.ch/schemas/patrons/patron-v0.0.1.json", "first_name": "first_name", "last_name": "Last_name", "street": "Avenue Leopold-Robert, 132", "postal_code": "1920", "city": "Martigny", "birth_date": "1967-06-07", "phone": "+41324993111" } # can retrieve all type of users. list_url = url_for('invenio_records_rest.ptrn_list') res = client.get(list_url) assert res.status_code == 200 data = get_json(res) assert data['hits']['total'] == 3 # can manage all types of patron roles role_url = url_for('api_patrons.get_roles_management_permissions') res = client.get(role_url) assert res.status_code == 200 data = get_json(res) assert 'system_librarian' in data['allowed_roles'] # can create all type of users. system_librarian = deepcopy(record) librarian = deepcopy(record) patron = deepcopy(record) counter = 1 records = [{ 'data': patron, 'role': ['patron'], 'patron_type': { '$ref': 'https://ils.rero.ch/api/patron_types/ptty2' } }, { 'data': librarian, 'role': ['librarian'], 'library': { '$ref': 'https://ils.rero.ch/api/libraries/lib1' } }, { 'data': system_librarian, 'role': ['librarian', 'system_librarian'], 'library': { '$ref': 'https://ils.rero.ch/api/libraries/lib1' } }] for record in records: counter += 1 data = record['data'] data['roles'] = record['role'] data['barcode'] = 'barcode' + str(counter) data['email'] = str(counter) + '@domain.com' if record.get('patron_type'): data['patron_type'] = record['patron_type'] if record.get('library'): data['library'] = record['library'] with mock.patch('rero_ils.modules.patrons.api.' 'send_reset_password_instructions'): res, _ = postdata(client, 'invenio_records_rest.ptrn_list', data) assert res.status_code == 201 user = get_json(res)['metadata'] user_pid = user.get('pid') record_url = url_for('invenio_records_rest.ptrn_item', pid_value=user_pid) res = client.get(record_url) assert res.status_code == 200 user = get_json(res)['metadata'] # can update all type of user records. user['first_name'] = 'New Name' + str(counter) res = client.put(record_url, data=json.dumps(user), headers=json_header) assert res.status_code == 200 # can delete all type of user records. record_url = url_for('invenio_records_rest.ptrn_item', pid_value=user_pid) res = client.delete(record_url) assert res.status_code == 204
def test_pttr_permissions_api(client, patron_martigny_no_email, system_librarian_martigny_no_email, librarian_martigny_no_email, patron_transaction_overdue_martigny, patron_transaction_overdue_saxon, patron_transaction_overdue_sion): """Test patron transactions permissions api.""" pttr_permissions_url = url_for('api_blueprint.permissions', route_name='patron_transactions') pttr_martigny_permission_url = url_for( 'api_blueprint.permissions', route_name='patron_transactions', record_pid=patron_transaction_overdue_martigny.pid) pttr_saxon_permission_url = url_for( 'api_blueprint.permissions', route_name='patron_transactions', record_pid=patron_transaction_overdue_saxon.pid) pttr_sion_permission_url = url_for( 'api_blueprint.permissions', route_name='patron_transactions', record_pid=patron_transaction_overdue_sion.pid) # Not logged res = client.get(pttr_permissions_url) assert res.status_code == 401 # Logged as patron login_user_via_session(client, patron_martigny_no_email.user) res = client.get(pttr_permissions_url) assert res.status_code == 403 # Logged as librarian # * lib can 'list' and 'read' pttr of its own organisation # * lib can 'create', 'update', 'delete' only for its library # * lib can't 'read' acq_account of others organisation. # * lib can't 'create', 'update', 'delete' acq_account for other org/lib login_user_via_session(client, librarian_martigny_no_email.user) res = client.get(pttr_martigny_permission_url) assert res.status_code == 200 data = get_json(res) assert data['read']['can'] assert data['list']['can'] assert data['create']['can'] assert data['update']['can'] # 'delete' should be true but return false because an event is linked # assert data['delete']['can'] res = client.get(pttr_saxon_permission_url) assert res.status_code == 200 data = get_json(res) assert data['read']['can'] assert data['list']['can'] assert data['update']['can'] # 'delete' should be true but return false because an event is linked # assert not data['delete']['can'] res = client.get(pttr_sion_permission_url) assert res.status_code == 200 data = get_json(res) assert not data['read']['can'] assert data['list']['can'] assert not data['update']['can'] assert not data['delete']['can'] # Logged as system librarian # * sys_lib can do everything about pttr of its own organisation # * sys_lib can't do anything about pttr of other organisation login_user_via_session(client, system_librarian_martigny_no_email.user) res = client.get(pttr_saxon_permission_url) assert res.status_code == 200 data = get_json(res) assert data['read']['can'] assert data['list']['can'] assert data['create']['can'] assert data['update']['can'] # 'delete' should be true but return false because an event is linked # assert data['delete']['can'] res = client.get(pttr_sion_permission_url) assert res.status_code == 200 data = get_json(res) assert not data['read']['can'] assert not data['update']['can'] assert not data['delete']['can']
def test_assign_without_to_author(inspire_app, override_config): with override_config(FEATURE_FLAG_ENABLE_BAI_PROVIDER=True, FEATURE_FLAG_ENABLE_BAI_CREATION=True): cataloger = create_user(role="cataloger") from_author = create_record("aut", data={"name": { "value": "Urhan, Harun" }}) literature1 = create_record( "lit", data={ "authors": [{ "curated_relation": False, "full_name": "Urhan, Harun", "record": { "$ref": f"http://localhost:5000/api/authors/{from_author['control_number']}" }, }] }, ) literature2 = create_record( "lit", data={ "authors": [{ "curated_relation": False, "full_name": "Urhan, H", "record": { "$ref": f"http://localhost:5000/api/authors/{from_author['control_number']}" }, }] }, ) with inspire_app.test_client() as client: login_user_via_session(client, email=cataloger.email) response = client.post( "/assign/author", data=orjson.dumps({ "literature_recids": [ literature1["control_number"], literature2["control_number"], ], "from_author_recid": from_author["control_number"], }), content_type="application/json", ) response_status_code = response.status_code assert response_status_code == 200 stub_author_id = response.json["stub_author_id"] literature1_after = LiteratureRecord.get_record_by_pid_value( literature1["control_number"]) literature1_author = literature1_after["authors"][0] literature1_author_recid = get_recid_from_ref(literature1_author["record"]) assert literature1_author_recid != from_author["control_number"] assert literature1_author_recid == stub_author_id assert not literature1_author.get("curated_relation") literature2_after = LiteratureRecord.get_record_by_pid_value( literature1["control_number"]) literature2_author = literature2_after["authors"][0] literature2_author_recid = get_recid_from_ref(literature2_author["record"]) assert literature2_author_recid != from_author["control_number"] assert literature2_author_recid == stub_author_id assert not literature2_author.get("curated_relation") author = AuthorsRecord.get_record_by_pid_value(stub_author_id) assert author["stub"] is True assert author["name"] == { "value": "Urhan, Harun", "name_variants": ["Urhan, H"] } assert get_values_for_schema(author["ids"], "INSPIRE BAI")[0] == "H.Urhan.2" assert (get_values_for_schema(author["ids"], "INSPIRE BAI")[0] != get_values_for_schema(from_author["ids"], "INSPIRE BAI")[0])
def test_create(client, db, deposit_json, bucket_location, superuser, admin, moderator, submitter, user, subdivision): """Test create deposits permissions.""" headers = { 'Content-Type': 'application/json', 'Accept': 'application/json' } # Not logged res = client.post(url_for('invenio_records_rest.depo_list'), data=json.dumps(deposit_json), headers=headers) assert res.status_code == 401 # User login_user_via_session(client, email=user['email']) deposit_json['user'] = { '$ref': 'https://sonar.ch/api/users/{pid}'.format(pid=user['pid']) } res = client.post(url_for('invenio_records_rest.depo_list'), data=json.dumps(deposit_json), headers=headers) assert res.status_code == 403 # submitter login_user_via_session(client, email=submitter['email']) submitter['subdivision'] = { '$ref': 'https://sonar.ch/api/subdivisions/{subdivision}'.format( subdivision=subdivision['pid']) } submitter.commit() submitter.reindex() db.session.commit() deposit_json['user'] = { '$ref': 'https://sonar.ch/api/users/{pid}'.format(pid=submitter['pid']) } del deposit_json['diffusion']['subdivisions'] res = client.post(url_for('invenio_records_rest.depo_list'), data=json.dumps(deposit_json), headers=headers) assert res.status_code == 201 assert res.json['metadata']['diffusion']['subdivisions'] # Moderator login_user_via_session(client, email=moderator['email']) deposit_json['user'] = { '$ref': 'https://sonar.ch/api/users/{pid}'.format(pid=moderator['pid']) } res = client.post(url_for('invenio_records_rest.depo_list'), data=json.dumps(deposit_json), headers=headers) assert res.status_code == 201 # Admin login_user_via_session(client, email=admin['email']) deposit_json['user'] = { '$ref': 'https://sonar.ch/api/users/{pid}'.format(pid=admin['pid']) } res = client.post(url_for('invenio_records_rest.depo_list'), data=json.dumps(deposit_json), headers=headers) assert res.status_code == 201 # Super user login_user_via_session(client, email=superuser['email']) deposit_json['user'] = { '$ref': 'https://sonar.ch/api/users/{pid}'.format(pid=superuser['pid']) } res = client.post(url_for('invenio_records_rest.depo_list'), data=json.dumps(deposit_json), headers=headers) assert res.status_code == 201
def test_video_events_on_download_check_index(api_app, webhooks, db, api_project, access_token, json_headers, users): """Test deposit events.""" (project, video_1, video_2) = api_project prepare_videos_for_publish([video_1, video_2]) project_depid = project['_deposit']['id'] video_1_depid = video_1['_deposit']['id'] bucket_id = video_1._bucket.id with api_app.test_request_context(): url = url_for( 'invenio_webhooks.event_list', receiver_id='downloader', access_token=access_token) with mock.patch('requests.get') as mock_request, \ api_app.test_client() as client: login_user_via_session(client, email=User.query.get(users[0]).email) file_size = 1024 * 1024 mock_request.return_value = type( 'Response', (object, ), { 'raw': BytesIO(b'\x00' * file_size), 'headers': {'Content-Length': file_size} }) payload = dict( uri='http://example.com/test.pdf', bucket_id=str(bucket_id), deposit_id=video_1_depid, key='test.pdf') resp = client.post(url, headers=json_headers, data=json.dumps(payload)) assert resp.status_code == 201 # run indexer RecordIndexer().process_bulk_queue() sleep(2) deposit = deposit_video_resolver(video_1_depid) file_dumps = deposit._get_files_dump() assert len(file_dumps) == 1 def search_record(url): res = client.get(url, headers=json_headers) assert res.status_code == 200 data = json.loads( res.data.decode('utf-8') )['hits']['hits'][0]['metadata'] return data # check if the tasks states and files are inside elasticsearch # -> check video url_video_deposit = url_for('invenio_deposit_rest.video_list', q='_deposit.id:{0}'.format(video_1_depid), access_token=access_token) data = search_record(url_video_deposit) assert data['_cds']['state']['file_download'] == states.SUCCESS assert file_dumps == data['_files'] # -> check project url_project_deposit = url_for( 'invenio_deposit_rest.project_list', q='_deposit.id:{0}'.format(project_depid), access_token=access_token) search_record(url_project_deposit) assert data['_cds']['state']['file_download'] == states.SUCCESS # [[ EDIT VIDEO ]] deposit = deposit_video_resolver(video_1_depid) video_edited = deepcopy(deposit) del video_edited['_files'] del video_edited['_cds']['state'] reset_oauth2() res = client.put( url_for('invenio_deposit_rest.video_item', pid_value=video_1_depid), data=json.dumps(video_edited), headers=json_headers ) assert res.status_code == 200 # check if the tasks states and files are inside elasticsearch # -> check video data = search_record(url_video_deposit) assert data['_cds']['state']['file_download'] == states.SUCCESS assert file_dumps == data['_files'] # -> check project url_project_deposit = url_for( 'invenio_deposit_rest.project_list', q='_deposit.id:{0}'.format(project_depid), access_token=access_token) search_record(url_project_deposit) assert data['_cds']['state']['file_download'] == states.SUCCESS # [[ PUBLISH THE PROJECT ]] reset_oauth2() res = client.post( url_for('invenio_deposit_rest.project_actions', pid_value=project['_deposit']['id'], action='publish', ), headers=json_headers) assert res.status_code == 202 # run indexer RecordIndexer().process_bulk_queue() sleep(2) deposit = deposit_video_resolver(video_1_depid) # check if the files are inside elasticsearch # -> check video deposit data = search_record(url_video_deposit) assert data['_cds']['state']['file_download'] == states.SUCCESS assert file_dumps == data['_files'] # check video record pid, record = deposit.fetch_published() url = url_for('invenio_records_rest.recid_list', q='_deposit.pid.value:{0}'.format(pid.pid_value)) data = search_record(url) assert record['_files'] == data['_files']
def test_update(client, make_deposit, superuser, admin, moderator, submitter, user): """Test update deposits permissions.""" deposit1 = make_deposit('submitter', 'org') deposit2 = make_deposit('submitter', 'org2') headers = { 'Content-Type': 'application/json', 'Accept': 'application/json' } # Not logged res = client.put(url_for('invenio_records_rest.depo_item', pid_value=deposit1['pid']), data=json.dumps(deposit1.dumps()), headers=headers) assert res.status_code == 401 # Logged as user login_user_via_session(client, email=user['email']) res = client.put(url_for('invenio_records_rest.depo_item', pid_value=deposit1['pid']), data=json.dumps(deposit1.dumps()), headers=headers) assert res.status_code == 403 # Logged as submitter login_user_via_session(client, email=submitter['email']) res = client.put(url_for('invenio_records_rest.depo_item', pid_value=deposit1['pid']), data=json.dumps(deposit1.dumps()), headers=headers) assert res.status_code == 200 res = client.put(url_for('invenio_records_rest.depo_item', pid_value=deposit2['pid']), data=json.dumps(deposit2.dumps()), headers=headers) assert res.status_code == 403 # Logged as moderator login_user_via_session(client, email=moderator['email']) res = client.put(url_for('invenio_records_rest.depo_item', pid_value=deposit1['pid']), data=json.dumps(deposit1.dumps()), headers=headers) assert res.status_code == 200 res = client.put(url_for('invenio_records_rest.depo_item', pid_value=deposit2['pid']), data=json.dumps(deposit2.dumps()), headers=headers) assert res.status_code == 403 # Logged as admin login_user_via_session(client, email=admin['email']) res = client.put(url_for('invenio_records_rest.depo_item', pid_value=deposit1['pid']), data=json.dumps(deposit1.dumps()), headers=headers) assert res.status_code == 200 res = client.put(url_for('invenio_records_rest.depo_item', pid_value=deposit2['pid']), data=json.dumps(deposit2.dumps()), headers=headers) assert res.status_code == 403 # Logged as superuser login_user_via_session(client, email=superuser['email']) res = client.put(url_for('invenio_records_rest.depo_item', pid_value=deposit1['pid']), data=json.dumps(deposit1.dumps()), headers=headers) assert res.status_code == 200 login_user_via_session(client, email=superuser['email']) res = client.put(url_for('invenio_records_rest.depo_item', pid_value=deposit2['pid']), data=json.dumps(deposit2.dumps()), headers=headers) assert res.status_code == 200
def test_token_getter_setter(views_fixture, monkeypatch): """Test token getter setter.""" # Mock session id monkeypatch.setattr('invenio_oauthclient._compat._create_identifier', lambda: '1234') monkeypatch.setattr( 'invenio_oauthclient.views.client._create_identifier', lambda: '1234') app = views_fixture oauth = app.extensions['oauthlib.client'] # Mock user user = MagicMock() user.id = 1 user.get_id = MagicMock(return_value=1) user.is_anonymous = False with app.test_client() as c: login_user_via_session(c, user) # First call login to be redirected res = c.get(url_for('invenio_oauthclient.login', remote_app='full')) assert res.status_code == 302 assert res.location.startswith( oauth.remote_apps['full'].authorize_url ) state = parse_qs(urlparse(res.location).query)['state'][0] # Mock resposen class mock_response(app.extensions['oauthlib.client'], 'full') # Imitate that the user authorized our request in the remote # application. c.get(url_for( 'invenio_oauthclient.authorized', remote_app='full', code='test', state=state, )) # Assert if everything is as it should be. from flask import session as flask_session assert flask_session['oauth_token_full'] == \ ('test_access_token', '') t = RemoteToken.get(1, 'fullid') assert t.remote_account.client_id == 'fullid' assert t.access_token == 'test_access_token' assert RemoteToken.query.count() == 1 # Mock a new authorized request mock_response(app.extensions['oauthlib.client'], 'full', data={ 'access_token': 'new_access_token', 'scope': "", 'token_type': 'bearer' }) c.get(url_for( 'invenio_oauthclient.authorized', remote_app='full', code='test', state=state )) t = RemoteToken.get(1, 'fullid') assert t.access_token == 'new_access_token' assert RemoteToken.query.count() == 1 val = token_getter( app.extensions['oauthlib.client'].remote_apps['full']) assert val == ('new_access_token', '') # Disconnect account res = c.get(url_for( 'invenio_oauthclient.disconnect', remote_app='full', )) assert res.status_code == 302 assert res.location.endswith( url_for('invenio_oauthclient_settings.index') ) # Assert that remote account have been removed. t = RemoteToken.get(1, 'fullid') assert t is None # TODO: Figure out what is leaving session open & blocked db.session.close()
def test_items_in_transit_between_locations( client, librarian_martigny_no_email, patron_martigny_no_email, loc_public_martigny, item_type_standard_martigny, loc_restricted_martigny, item_lib_martigny, json_header, circulation_policies): """Test item in-transit scenarios.""" login_user_via_session(client, librarian_martigny_no_email.user) item = item_lib_martigny item_pid = item.pid patron_pid = patron_martigny_no_email.pid location = loc_public_martigny # request to pick at another location res, data = postdata( client, 'api_item.librarian_request', dict(item_pid=item_pid, pickup_location_pid=loc_restricted_martigny.pid, patron_pid=patron_pid)) assert res.status_code == 200 item_data = data.get('metadata') actions = data.get('action_applied') assert item_data.get('status') == ItemStatus.ON_SHELF assert actions.get(LoanAction.REQUEST) loan_pid = actions[LoanAction.REQUEST].get('pid') item = Item.get_record_by_pid(item_pid) # validate (send) request res, data = postdata(client, 'api_item.validate_request', dict(item_pid=item_pid, pid=loan_pid)) assert res.status_code == 200 item_data = data.get('metadata') actions = data.get('action_applied') assert item_data.get('status') == ItemStatus.IN_TRANSIT assert actions.get(LoanAction.VALIDATE) # checkout action to req patron is possible without the receive action res = client.get( url_for('api_item.item', item_barcode=item.get('barcode'), patron_pid=patron_pid)) assert res.status_code == 200 data = get_json(res) actions = data.get('metadata').get('item').get('actions') assert 'checkout' in actions # checkout res, data = postdata( client, 'api_item.checkout', dict(item_pid=item_pid, patron_pid=patron_pid, pid=loan_pid)) assert res.status_code == 200 assert Item.get_record_by_pid(item_pid).get('status') == ItemStatus.ON_LOAN # checkin at location other than item location res, data = postdata( client, 'api_item.checkin', dict(item_pid=item_pid, pid=loan_pid, transaction_location_pid=loc_restricted_martigny.pid)) assert res.status_code == 200 item_data = data.get('metadata') actions = data.get('action_applied') assert item_data.get('status') == ItemStatus.IN_TRANSIT assert actions.get(LoanAction.CHECKIN) loan_pid = actions[LoanAction.CHECKIN].get('pid') loan = actions[LoanAction.CHECKIN] assert loan.get('state') == 'ITEM_IN_TRANSIT_TO_HOUSE' # a new checkout res, data = postdata(client, 'api_item.checkout', dict(item_pid=item_pid, patron_pid=patron_pid)) assert res.status_code == 200 assert Item.get_record_by_pid(item_pid).get('status') == ItemStatus.ON_LOAN actions = data.get('action_applied') loan_pid = actions[LoanAction.CHECKOUT].get('pid') # checkin at the request location res, _ = postdata( client, 'api_item.checkin', dict(item_pid=item.pid, pid=loan_pid, transaction_location_pid=location.pid)) assert res.status_code == 200
def test_multiple_loans_on_item_error( client, patron_martigny_no_email, patron2_martigny_no_email, loc_public_martigny, item_type_standard_martigny, item_lib_martigny, json_header, circulation_policies, loc_public_fully, librarian_martigny_no_email): """Test MultipleLoansOnItemError.""" login_user_via_session(client, librarian_martigny_no_email.user) item = item_lib_martigny checked_patron = patron2_martigny_no_email.pid requested_patron = patron_martigny_no_email.pid location = loc_public_martigny # checkout to checked_patron res, data = postdata( client, 'api_item.checkout', dict(item_pid=item.pid, patron_pid=checked_patron, transaction_location_pid=location.pid)) assert res.status_code == 200 assert Item.get_record_by_pid(item.pid).get('status') == ItemStatus.ON_LOAN item_data = data.get('metadata') actions = data.get('action_applied') assert item_data.get('status') == ItemStatus.ON_LOAN assert actions.get(LoanAction.CHECKOUT) loan_pid = actions[LoanAction.CHECKOUT].get('pid') item = Item.get_record_by_pid(item.pid) # request by requested patron to pick at another location res, data = postdata( client, 'api_item.librarian_request', dict(item_pid=item.pid, pickup_location_pid=loc_public_fully.pid, patron_pid=requested_patron)) assert res.status_code == 200 item_data = data.get('metadata') actions = data.get('action_applied') assert item_data.get('status') == ItemStatus.ON_LOAN assert actions.get(LoanAction.REQUEST) req_loan_pid = actions[LoanAction.REQUEST].get('pid') item = Item.get_record_by_pid(item.pid) # checkin at the request location res, data = postdata( client, 'api_item.checkin', dict(item_pid=item.pid, pid=loan_pid, transaction_location_pid=loc_public_fully.pid)) assert res.status_code == 200 # test the returned three actions loans = data.get('action_applied') checked_in_loan = loans.get(LoanAction.CHECKIN) cancelled_loan = loans.get(LoanAction.CANCEL) validated_loan = loans.get(LoanAction.VALIDATE) assert checked_in_loan.get('pid') == cancelled_loan.get('pid') assert validated_loan.get('pid') == req_loan_pid assert Loan.get_record_by_pid(loan_pid).get('state') == 'CANCELLED' new_loan = Loan.get_record_by_pid(req_loan_pid) assert new_loan.get('state') == 'ITEM_AT_DESK' assert Item.get_record_by_pid(item.pid).get('status') == \ ItemStatus.AT_DESK # cancel request res, _ = postdata(client, 'api_item.cancel_loan', dict(item_pid=item.pid, pid=req_loan_pid)) assert res.status_code == 200
def test_items_requests(client, librarian_martigny_no_email, patron_martigny_no_email, loc_public_martigny, item_type_standard_martigny, lib_martigny, item_lib_martigny, json_header, circulation_policies): """Test requesting an item and validation.""" login_user_via_session(client, librarian_martigny_no_email.user) item = item_lib_martigny item_pid = item.pid patron = patron_martigny_no_email patron_pid = patron.pid library_pid = librarian_martigny_no_email.replace_refs()['library']['pid'] location = loc_public_martigny assert not item.patron_request_rank(patron.get('barcode')) assert not item.is_requested_by_patron(patron.get('barcode')) # request res, data = postdata( client, 'api_item.librarian_request', dict(item_pid=item_pid, pickup_location_pid=location.pid, patron_pid=patron_pid)) assert res.status_code == 200 item_data = data.get('metadata') actions = data.get('action_applied') assert item_data.get('status') == ItemStatus.ON_SHELF assert actions.get(LoanAction.REQUEST) loan_pid = actions[LoanAction.REQUEST].get('pid') item = Item.get_record_by_pid(item_pid) assert item.patron_request_rank(patron.get('barcode')) == 1 assert item.is_requested_by_patron(patron.get('barcode')) # test can not request item already requested to patron res = client.get( url_for('api_item.can_request', item_pid=item_pid, library_pid=lib_martigny.pid, patron_barcode=patron.get('barcode'))) assert res.status_code == 200 data = get_json(res) assert not data.get('can_request') # checkout res, data = postdata(client, 'api_item.checkout', dict(item_pid=item_pid, pid=loan_pid)) assert res.status_code == 200 item_data = data.get('metadata') actions = data.get('action_applied') assert item_data.get('status') == ItemStatus.ON_LOAN assert actions.get(LoanAction.CHECKOUT) res, data = postdata(client, 'api_item.checkin', dict(item_pid=item_pid, pid=loan_pid)) assert res.status_code == 200 item_data = data.get('metadata') actions = data.get('action_applied') assert item_data.get('status') == ItemStatus.ON_SHELF assert actions.get(LoanAction.CHECKIN) # request res, data = postdata( client, 'api_item.librarian_request', dict(item_pid=item_pid, pickup_location_pid=location.pid, patron_pid=patron_pid)) assert res.status_code == 200 item_data = data.get('metadata') actions = data.get('action_applied') assert item_data.get('status') == ItemStatus.ON_SHELF assert actions.get(LoanAction.REQUEST) loan_pid = actions[LoanAction.REQUEST].get('pid') # get requests to validate res = client.get( url_for('api_item.requested_loans', library_pid='not exists')) assert res.status_code == 500 res = client.get( url_for('api_item.requested_loans', library_pid=library_pid)) assert res.status_code == 200 data = get_json(res) assert data['hits']['total'] == 1 assert len(data['hits']['hits']) == 1 # validate request res, data = postdata(client, 'api_item.validate_request', dict(item_pid=item_pid, pid=loan_pid)) assert res.status_code == 200 item_data = data.get('metadata') actions = data.get('action_applied') assert item_data.get('status') == ItemStatus.AT_DESK assert actions.get(LoanAction.VALIDATE) # checkout res, data = postdata(client, 'api_item.checkout', dict(item_pid=item_pid, pid=loan_pid)) assert res.status_code == 200 item_data = data.get('metadata') actions = data.get('action_applied') assert item_data.get('status') == ItemStatus.ON_LOAN assert actions.get(LoanAction.CHECKOUT) res, data = postdata(client, 'api_item.checkin', dict(item_pid=item_pid, pid=loan_pid)) assert res.status_code == 200 item_data = data.get('metadata') actions = data.get('action_applied') assert item_data.get('status') == ItemStatus.ON_SHELF assert actions.get(LoanAction.CHECKIN)
def login_user(client, user): """Log user in.""" flask_security.login_user(user, remember=True) login_user_via_session(client, email=user.email)
def test_recall_notification(client, patron_sion, lib_sion, json_header, patron_sion_without_email1, item_lib_sion, librarian_sion, circulation_policies, loc_public_sion, mailbox): """Test recall notification.""" mailbox.clear() login_user_via_session(client, librarian_sion.user) res, data = postdata( client, 'api_item.checkout', dict( item_pid=item_lib_sion.pid, patron_pid=patron_sion.pid, transaction_location_pid=loc_public_sion.pid, transaction_user_pid=librarian_sion.pid, )) assert res.status_code == 200 loan_pid = data.get('action_applied')[LoanAction.CHECKOUT].get('pid') loan = Loan.get_record_by_pid(loan_pid) assert not loan.is_notified( notification_type=Notification.RECALL_NOTIFICATION_TYPE) # test notification permissions res, data = postdata( client, 'api_item.librarian_request', dict(item_pid=item_lib_sion.pid, pickup_location_pid=loc_public_sion.pid, patron_pid=patron_sion_without_email1.pid, transaction_library_pid=lib_sion.pid, transaction_user_pid=librarian_sion.pid)) assert res.status_code == 200 request_loan_pid = data.get('action_applied')[LoanAction.REQUEST].get( 'pid') flush_index(NotificationsSearch.Meta.index) assert loan.is_notified( notification_type=Notification.RECALL_NOTIFICATION_TYPE) notification = get_notification( loan, notification_type=Notification.RECALL_NOTIFICATION_TYPE) assert notification.loan_pid == loan.pid assert not loan.is_notified( notification_type=Notification.AVAILABILITY_NOTIFICATION_TYPE) assert not get_notification( loan, notification_type=Notification.AVAILABILITY_NOTIFICATION_TYPE) # one new email for the patron assert mailbox[0].recipients == [patron_sion.dumps()['email']] mailbox.clear() # cancel request res, _ = postdata( client, 'api_item.cancel_item_request', dict(item_pid=item_lib_sion.pid, pid=request_loan_pid, transaction_user_pid=librarian_sion.pid, transaction_location_pid=loc_public_sion.pid)) assert res.status_code == 200 # no new notification is send for the second time res, _ = postdata( client, 'api_item.librarian_request', dict(item_pid=item_lib_sion.pid, pickup_location_pid=loc_public_sion.pid, patron_pid=patron_sion_without_email1.pid, transaction_library_pid=lib_sion.pid, transaction_user_pid=librarian_sion.pid)) assert res.status_code == 200 flush_index(NotificationsSearch.Meta.index) assert loan.is_notified( notification_type=Notification.RECALL_NOTIFICATION_TYPE) notification = get_notification( loan, notification_type=Notification.RECALL_NOTIFICATION_TYPE) assert notification.loan_pid == loan.pid assert not loan.is_notified( notification_type=Notification.AVAILABILITY_NOTIFICATION_TYPE) assert not get_notification( loan, notification_type=Notification.AVAILABILITY_NOTIFICATION_TYPE) assert len(mailbox) == 0
def test_delete(client, make_user, superuser, admin, moderator, submitter, user): """Test delete users permissions.""" # Not logged res = client.delete( url_for('invenio_records_rest.user_item', pid_value=user['pid'])) assert res.status_code == 401 # Logged as user login_user_via_session(client, email=user['email']) res = client.delete( url_for('invenio_records_rest.user_item', pid_value=user['pid'])) assert res.status_code == 403 # Logged as submitter login_user_via_session(client, email=submitter['email']) res = client.delete( url_for('invenio_records_rest.user_item', pid_value=user['pid'])) assert res.status_code == 403 # Logged as moderator login_user_via_session(client, email=moderator['email']) res = client.delete( url_for('invenio_records_rest.user_item', pid_value=user['pid'])) assert res.status_code == 403 # Logged as admin login_user_via_session(client, email=admin['email']) res = client.delete( url_for('invenio_records_rest.user_item', pid_value=user['pid'])) assert res.status_code == 204 # Create a new user user = make_user('user', 'org3') # Logged as admin of other organisation other_admin = make_user('admin', 'org2') login_user_via_session(client, email=other_admin['email']) res = client.delete( url_for('invenio_records_rest.user_item', pid_value=user['pid'])) assert res.status_code == 403 # Logged as superuser login_user_via_session(client, email=superuser['email']) res = client.delete( url_for('invenio_records_rest.user_item', pid_value=user['pid'])) assert res.status_code == 204
def test_edit_article_workflow_deleting(workflow_app, mocked_external_services): app_client = workflow_app.test_client() login_user_via_session(app_client, email='*****@*****.**') record = { '$schema': 'http://localhost:5000/schemas/records/hep.json', 'arxiv_eprints': [{ 'categories': ['nucl-th'], 'value': '1802.03287' }], 'control_number': 123, 'document_type': ['article'], 'titles': [{ 'title': 'Resource Pooling in Large-Scale Content Delivery Systems' }], 'self': { '$ref': 'http://localhost:5000/schemas/records/hep.json' }, '_collections': ['Literature'] } factory = TestRecordMetadata.create_from_kwargs(json=record) eng_uuid = start('edit_article', data=factory.record_metadata.json) obj = WorkflowEngine.from_uuid(eng_uuid).objects[0] assert obj.status == ObjectStatus.WAITING assert obj.extra_data['callback_url'] record = get_db_record('lit', 123) search = LiteratureSearch() search.get_source(record.id) # simulate changes in the editor and save obj.data['deleted'] = True payload = { 'id': obj.id, 'metadata': obj.data, '_extra_data': obj.extra_data } app_client.put(obj.extra_data['callback_url'], data=json.dumps(payload), content_type='application/json') obj = WorkflowEngine.from_uuid(eng_uuid).objects[0] assert obj.status == ObjectStatus.WAITING # waiting for robot_upload assert obj.data['deleted'] is True do_robotupload_callback( app=workflow_app, workflow_id=obj.id, recids=[obj.data['control_number']], ) record = get_db_record('lit', 123) assert record['deleted'] is True with pytest.raises(NotFoundError): search.get_source(record.id) obj = WorkflowEngine.from_uuid(eng_uuid).objects[0] assert obj.status == ObjectStatus.COMPLETED pending_records = WorkflowsPendingRecord.query.filter_by( workflow_id=obj.id).all() assert not pending_records
def test_library_secure_api_update(client, lib_fully, lib_martigny, librarian_martigny, librarian_sion, json_header, system_librarian_martigny, system_librarian_sion): """Test library secure api update.""" # Martigny login_user_via_session(client, librarian_martigny.user) record_url = url_for('invenio_records_rest.lib_item', pid_value=lib_martigny.pid) lib_martigny['name'] = 'New Name' res = client.put(record_url, data=json.dumps(lib_martigny), headers=json_header) # a librarian is authorized to update its library in its org assert res.status_code == 200 record_url = url_for('invenio_records_rest.lib_item', pid_value=lib_fully.pid) lib_fully['name'] = 'New Name' res = client.put(record_url, data=json.dumps(lib_fully), headers=json_header) # a librarian is not authorized to update an external library of its org assert res.status_code == 403 login_user_via_session(client, system_librarian_martigny.user) res = client.put(record_url, data=json.dumps(lib_fully), headers=json_header) # a sys_librarian is authorized to update any library of its org assert res.status_code == 200 record_url = url_for('invenio_records_rest.lib_item', pid_value=lib_martigny.pid) lib_martigny['name'] = 'New Name 2' res = client.put(record_url, data=json.dumps(lib_martigny), headers=json_header) # a sys_librarian is authorized to update any library of its org assert res.status_code == 200 # Sion login_user_via_session(client, librarian_sion.user) record_url = url_for('invenio_records_rest.lib_item', pid_value=lib_fully.pid) lib_fully['name'] = 'New Name 2' res = client.put(record_url, data=json.dumps(lib_fully), headers=json_header) # librarian is not authorized to update an external library of another org assert res.status_code == 403 login_user_via_session(client, system_librarian_sion.user) res = client.put(record_url, data=json.dumps(lib_fully), headers=json_header) # sys_lib is not authorized to update an external library of another org assert res.status_code == 403
def test_assign_from_an_author_to_another_that_is_not_stub(inspire_app): cataloger = create_user(role="cataloger") author_data = { "name": { "value": "Aad, Georges", "preferred_name": "Georges Aad" }, "ids": [{ "value": "G.Aad.1", "schema": "INSPIRE BAI" }], "stub": False, } from_author = create_record("aut") to_author = create_record("aut", data=author_data) literature = create_record( "lit", data={ "authors": [ { "full_name": "Urhan, Ahmet", "record": { "$ref": "http://localhost:5000/api/authors/17200" }, }, { "full_name": "Urhan, Harun", "record": { "$ref": f"http://localhost:5000/api/authors/{from_author['control_number']}" }, }, ] }, ) with inspire_app.test_client() as client: login_user_via_session(client, email=cataloger.email) response = client.post( "/assign/author", data=orjson.dumps({ "literature_recids": [literature["control_number"]], "from_author_recid": from_author["control_number"], "to_author_recid": to_author["control_number"], }), content_type="application/json", ) response_status_code = response.status_code assert response_status_code == 200 literature_after = LiteratureRecord.get_record_by_pid_value( literature["control_number"]) literature_author = literature_after["authors"][1] assert literature_author["record"] == { "$ref": f"http://localhost:5000/api/authors/{to_author['control_number']}" } assert literature_author["curated_relation"] assert literature_author["ids"] == to_author["ids"] to_author_after = AuthorsRecord.get_record_by_pid_value( to_author["control_number"]) assert not to_author_after["stub"]
def test_read(client, make_user, superuser, admin, moderator, submitter, user): """Test read users permissions.""" # Not logged res = client.get( url_for('invenio_records_rest.user_item', pid_value=user['pid'])) assert res.status_code == 401 # Logged as user and read himself login_user_via_session(client, email=user['email']) res = client.get( url_for('invenio_records_rest.user_item', pid_value=user['pid'])) assert res.status_code == 200 assert res.json['metadata']['permissions'] == { 'delete': False, 'read': True, 'update': True } # Logged as user and read other login_user_via_session(client, email=user['email']) res = client.get( url_for('invenio_records_rest.user_item', pid_value=moderator['pid'])) assert res.status_code == 403 # Logged as submitter login_user_via_session(client, email=submitter['email']) res = client.get( url_for('invenio_records_rest.user_item', pid_value=submitter['pid'])) assert res.status_code == 200 assert res.json['metadata']['permissions'] == { 'delete': False, 'read': True, 'update': True } login_user_via_session(client, email=submitter['email']) res = client.get( url_for('invenio_records_rest.user_item', pid_value=user['pid'])) assert res.status_code == 403 # Logged as moderator login_user_via_session(client, email=moderator['email']) res = client.get( url_for('invenio_records_rest.user_item', pid_value=moderator['pid'])) assert res.status_code == 200 assert res.json['metadata']['permissions'] == { 'delete': False, 'read': True, 'update': True } login_user_via_session(client, email=moderator['email']) res = client.get( url_for('invenio_records_rest.user_item', pid_value=user['pid'])) assert res.status_code == 403 # Logged as admin login_user_via_session(client, email=admin['email']) res = client.get( url_for('invenio_records_rest.user_item', pid_value=user['pid'])) assert res.status_code == 200 assert res.json['metadata']['permissions'] == { 'delete': True, 'read': True, 'update': True } # Logged as admin, try to read superuser login_user_via_session(client, email=admin['email']) res = client.get( url_for('invenio_records_rest.user_item', pid_value=superuser['pid'])) assert res.status_code == 403 # Logged as admin of other organisation other_admin = make_user('admin', 'org2') login_user_via_session(client, email=other_admin['email']) res = client.get( url_for('invenio_records_rest.user_item', pid_value=user['pid'])) assert res.status_code == 403 # Logged as superuser login_user_via_session(client, email=superuser['email']) res = client.get( url_for('invenio_records_rest.user_item', pid_value=user['pid'])) assert res.status_code == 200 assert res.json['metadata']['permissions'] == { 'delete': True, 'read': True, 'update': True }
def test_ebook_links(app, client, testdata, json_headers, admin): """Test ebook links when login_required.""" # re-index documents to resolve eitems ri = RecordIndexer() for rec in testdata["documents"]: ri.index(rec) current_search.flush_and_refresh(index="*") def _test_list(endpoint): """Get list.""" url = url_for(endpoint) res = client.get(url, headers=json_headers) return res.get_json()["hits"]["hits"] def _test_read(endpoint, pid): """Get record.""" url = url_for(endpoint, pid_value=pid) res = client.get(url, headers=json_headers) return res.get_json() def _get_item(doc, eitem_pid): """Get item from the document record.""" return [ eitem for eitem in doc["eitems"]["hits"] if eitem["pid"] == eitem_pid ][0] def assert_urls(urls): """Test urls.""" protected = urls[0] assert protected["login_required"] assert protected["value"] == "http://protected-cds-ils.ch/" login_required_url = app.config["CDS_ILS_EZPROXY_URL"].format( url=protected["value"]) assert protected["login_required_url"] == login_required_url not_protected = urls[1] assert not not_protected["login_required"] assert not_protected["value"] == "http://cds-ils.ch/" assert "login_required_url" not in not_protected EITEM_PID = "eitemid-2" DOC_PID = "docid-2" # documents/literature search endpoint for endpoint in [ "invenio_records_rest.docid_list", "invenio_records_rest.litid_list", ]: records = _test_list(endpoint) doc = [r for r in records if r["metadata"]["pid"] == DOC_PID][0] eitem = _get_item(doc["metadata"], EITEM_PID) assert_urls(eitem["urls"]) # test doc item endpoint doc = _test_read("invenio_records_rest.docid_item", DOC_PID) eitem = _get_item(doc["metadata"], EITEM_PID) assert_urls(eitem["urls"]) # eitems endpoint login_user_via_session(client, email=admin.email) records = _test_list("invenio_records_rest.eitmid_list") eitem = [r for r in records if r["metadata"]["pid"] == EITEM_PID][0] assert_urls(eitem["metadata"]["urls"]) eitem = _test_read("invenio_records_rest.eitmid_item", EITEM_PID) assert_urls(eitem["metadata"]["urls"])
def test_librarian_permissions( client, system_librarian_martigny_no_email, json_header, patron_martigny_no_email, librarian_fully_no_email, patron_martigny_data_tmp, lib_saxon): """Test librarian permissions.""" # Login as librarian login_user_via_session(client, librarian_fully_no_email.user) record = { "$schema": "https://ils.rero.ch/schema/patrons/patron-v0.0.1.json", "first_name": "first_name", "last_name": "Last_name", "street": "Avenue Leopold-Robert, 132", "postal_code": "1920", "city": "Martigny", "birth_date": "1967-06-07", "patron_type": {"$ref": "https://ils.rero.ch/api/patron_types/ptty1"}, "library": {"$ref": "https://ils.rero.ch/api/libraries/lib1"}, "phone": "+41324993111" } # can retrieve all type of users. list_url = url_for('invenio_records_rest.ptrn_list') res = client.get(list_url) assert res.status_code == 200 data = get_json(res) assert data['hits']['total'] == 3 # can create all type of users except system_librarians post_entrypoint = 'invenio_records_rest.ptrn_list' system_librarian = deepcopy(record) librarian = deepcopy(record) librarian_saxon = deepcopy(record) librarian_saxon['library'] = \ {"$ref": "https://ils.rero.ch/api/libraries/lib2"} librarian['library'] = \ {"$ref": "https://ils.rero.ch/api/libraries/lib3"} patron = deepcopy(record) patron['library'] = \ {"$ref": "https://ils.rero.ch/api/libraries/lib3"} counter = 1 for record in [ {'data': patron, 'role': 'patron'}, {'data': librarian, 'role': 'librarian'}, ]: counter += 1 data = record['data'] data['roles'] = [record['role']] data['barcode'] = 'barcode' + str(counter) data['email'] = str(counter) + '@domain.com' with mock.patch('rero_ils.modules.patrons.api.' 'send_reset_password_instructions'): res, _ = postdata( client, post_entrypoint, data ) assert res.status_code == 201 user = get_json(res)['metadata'] user_pid = user.get('pid') record_url = url_for('invenio_records_rest.ptrn_item', pid_value=user_pid) res = client.get(record_url) assert res.status_code == 200 user = get_json(res)['metadata'] # can update all type of user records except system_librarian. user['first_name'] = 'New Name' + str(counter) res = client.put( record_url, data=json.dumps(user), headers=json_header ) assert res.status_code == 200 # can not add the role system_librarian to user user['roles'] = ['system_librarian'] res = client.put( record_url, data=json.dumps(user), headers=json_header ) assert res.status_code == 403 # can delete all type of user records except system_librarian. record_url = url_for('invenio_records_rest.ptrn_item', pid_value=user_pid) res = client.delete(record_url) assert res.status_code == 204 # can not create librarians of same libray. counter = 1 for record in [ {'data': librarian_saxon, 'role': 'librarian'}, ]: counter += 1 data = record['data'] data['roles'] = [record['role']] data['barcode'] = 'barcode' + str(counter) data['email'] = str(counter) + '@domain.com' with mock.patch('rero_ils.modules.patrons.api.' 'send_reset_password_instructions'): res, _ = postdata( client, post_entrypoint, data ) assert res.status_code == 403 system_librarian['roles'] = ['system_librarian'] system_librarian['barcode'] = 'barcode' system_librarian['email'] = '*****@*****.**' with mock.patch('rero_ils.modules.patrons.api.' 'send_reset_password_instructions'): res, _ = postdata( client, post_entrypoint, system_librarian, ) assert res.status_code == 403 # can update all type of user records except system_librarian. record_url = url_for('invenio_records_rest.ptrn_item', pid_value=system_librarian_martigny_no_email.pid) system_librarian_martigny_no_email['first_name'] = 'New Name' res = client.put( record_url, data=json.dumps(system_librarian_martigny_no_email), headers=json_header ) assert res.status_code == 403 # can delete all type of user records except system_librarian. sys_librarian_pid = system_librarian_martigny_no_email.get('pid') record_url = url_for('invenio_records_rest.ptrn_item', pid_value=sys_librarian_pid) res = client.delete(record_url) assert res.status_code == 403