def test_get_block_schemas(app, test_communities): """"Test getting list of given community's schemas.""" with app.app_context(): community = Community.create_community('name1', 'desc1') community_id = community.id community_2 = Community.create_community('name2', 'desc2') community_id2 = community_2.id block_schema_1 = BlockSchema.create_block_schema(community_id, 'abc') block_schema_2 = BlockSchema.create_block_schema(community_id2, 'abc2') block_schema_3 = BlockSchema.create_block_schema(community_id2, 'abc3') with app.test_client() as client: headers = [('Content-Type', 'application/json'), ('Accept', 'application/json')] res = client.get( url_for( 'b2share_schemas.block_schema_list', community_id=community_id2 ), headers=headers) assert res.status_code == 200 response_data = json.loads(res.get_data(as_text=True)) assert isinstance(response_data['schemas'], list) assert len(response_data['schemas']) == 2 assert response_data['schemas'][0]['name'] == block_schema_2.name assert response_data['schemas'][1]['name'] == block_schema_3.name
def update_sets(): """Check that each community has a corresponding oai set""" for community in Community.get_all(): dirty = False oaiset = OAISet.query.filter( OAISet.spec == str(community.id)).one_or_none() if not oaiset: click.secho('Adding new oai set {}'.format(community.name), fg='yellow', bold=True) oaiset = OAISet(spec=str(community.id), name=community.name, description=community.description) dirty = True else: if oaiset.name != community.name: oaiset.name = community.name dirty = True click.secho('Update name for set {}'.format(oaiset.spec), fg='yellow', bold=True) if oaiset.description != community.description: oaiset.description = community.description dirty = True click.secho('Update description for set {}'.format( oaiset.spec), fg='yellow', bold=True) if dirty: db.session.add(oaiset) db.session.commit()
def community_schema_list(verbose, community): """Lists all community schema versions for this b2share instance (filtered for a community).""" comm = None if community: comm = get_community_by_name_or_id(community) community_id = None if comm: community_id = comm.id if verbose: click.secho("filtering for community %s" % comm) try: community_schemas = \ CommunitySchema.get_all_community_schemas(community_id=community_id) except CommunitySchemaDoesNotExistError: raise click.ClickException("""No community schemas found, community parameter was: %s""" % community) click.secho( """COMMUNITY ID\t\t\t\tNAME\t\tVERSION\tROOT SCHEMA\tRELEASED\t\t\t\tBLOCK SCHEMA ID""" ) for cs in community_schemas: cs_comm = Community.get(id=cs.community) click.secho("%s\t%s\t%d\t%d\t\t%s\t\t%s" % (cs.community, cs_comm.name[0:15].ljust(15), cs.version, cs.root_schema, cs.released, cs.block_schema_id))
def block_schema_list(verbose, community): """Lists all block schemas for this b2share instance (filtered for a community).""" comm = None if community: comm = get_community_by_name_or_id(community) community_id = None if comm: community_id = comm.id if verbose: click.secho("filtering for community %s" % comm) try: block_schemas = \ BlockSchema.get_all_block_schemas(community_id=community_id) except BlockSchemaDoesNotExistError: raise click.ClickException("""No block schemas found, community parameter was: %s""" % community) click.secho( """BLOCK SCHEMA ID\t\t\t\tNAME\t\tMAINTAINER\tDEPRECATED\t#VERSIONS""") for bs in block_schemas: bs_comm = Community.get(id=bs.community) click.secho( "%s\t%s\t%s\t%s\t\t%d" % (bs.id, bs.name[0:15].ljust(15), bs_comm.name[0:15].ljust(15), bs.deprecated, len(bs.versions)))
def test_unassign_community_roles(app, test_users, test_community, login_user): with app.app_context(): community = Community.get(id=test_community.id) com_admin = create_user('test_community_admin', roles=[community.admin_role]) db.session.commit() headers = [('Content-Type', 'application/json'), ('Accept', 'application/json')] with app.app_context(): with app.test_client() as client: # send the request as global admin login_user(test_users['admin'], client) res = client.delete( url_for( 'invenio_accounts_rest.assign_role', user_id=com_admin.id, role_id=test_community.admin_role_id, ), headers=headers ) assert res.status_code == 204 with app.app_context(): user_roles = User.query.get(com_admin.id).roles assert test_community.admin_role_id \ not in [role.id for role in user_roles]
def test_unassign_community_roles(app, test_users, test_community, login_user): with app.app_context(): community = Community.get(id=test_community.id) com_admin = create_user('test_community_admin', roles=[community.admin_role]) db.session.commit() headers = [('Content-Type', 'application/json'), ('Accept', 'application/json')] with app.app_context(): with app.test_client() as client: # send the request as global admin login_user(test_users['admin'], client) res = client.delete(url_for( 'invenio_accounts_rest.assign_role', user_id=com_admin.id, role_id=test_community.admin_role_id, ), headers=headers) assert res.status_code == 204 with app.app_context(): user_roles = User.query.get(com_admin.id).roles assert test_community.admin_role_id \ not in [role.id for role in user_roles]
def __init__(self, record=None, previous_record=None): """Constructor. Args: record: data submitted for the new deposit """ super(CreateDepositPermission, self).__init__() self.record = record if record is not None: needs = set() community = Community.get(record['community']) publication_state = record.get('publication_state', 'draft') if publication_state != 'draft' or community.restricted_submission: needs.add(create_deposit_need_factory()) needs.add(create_deposit_need_factory( community=record['community'], publication_state=publication_state, )) elif not community.restricted_submission: needs.add(AuthenticatedNeed) self.permissions.add(StrictDynamicPermission(*needs)) if previous_record: # we allow only the owner of a record to # create a new version of it. needs = set() for owner_id in previous_record['_deposit']['owners']: needs.add(UserNeed(owner_id)) self.permissions.add(StrictDynamicPermission(*needs))
def update_sets(): """Check that each community has a corresponding oai set""" for community in Community.get_all(): dirty = False oaiset = OAISet.query.filter(OAISet.spec == str(community.id)).one_or_none() if not oaiset: click.secho('Adding new oai set {}'.format(community.name), fg='yellow', bold=True) oaiset = OAISet(spec=str(community.id), name=community.name, description=community.description) dirty = True else: if oaiset.name != community.name: oaiset.name = community.name dirty = True click.secho('Update name for set {}'.format(oaiset.spec), fg='yellow', bold=True) if oaiset.description != community.description: oaiset.description = community.description dirty = True click.secho('Update description for set {}'.format(oaiset.spec), fg='yellow', bold=True) if dirty: db.session.add(oaiset) db.session.commit()
def __init__(self, record=None, previous_record=None): """Constructor. Args: record: data submitted for the new deposit """ super(CreateDepositPermission, self).__init__() self.record = record if record is not None: needs = set() community = Community.get(record['community']) publication_state = record.get('publication_state', 'draft') if publication_state != 'draft' or community.restricted_submission: needs.add(create_deposit_need_factory()) needs.add(create_deposit_need_factory( community=record['community'], publication_state=publication_state, )) elif not community.restricted_submission: needs.add(AuthenticatedNeed) self.permissions.add(StrictDynamicPermission(*needs)) if previous_record: # we allow only the owner of a record to # create a new version of it. needs = set() for owner_id in previous_record['_deposit']['owners']: needs.add(UserNeed(owner_id)) self.permissions.add(StrictDynamicPermission(*needs))
def test_deposit_create_permission(app, test_users, login_user, test_communities): """Test record draft creation.""" headers = [('Content-Type', 'application/json'), ('Accept', 'application/json')] with app.app_context(): community_name = 'MyTestCommunity1' record_data = generate_record_data(community=community_name) community_id = test_communities[community_name] community = Community.get(community_id) creator = create_user('creator') need = create_deposit_need_factory(str(community_id)) allowed = create_user('allowed', permissions=[need]) com_member = create_user('com_member', roles=[community.member_role]) com_admin = create_user('com_admin', roles=[community.admin_role]) deposit, pid, record = create_record(record_data, creator) def restrict_creation(restricted): community.update({'restricted_submission':restricted}) db.session.commit() def test_creation(expected_code, user=None, version_of=None): with app.test_client() as client: if user is not None: login_user(user, client) draft_create_res = client.post( url_for('b2share_records_rest.b2rec_list', version_of=version_of), data=json.dumps(record_data), headers=headers ) assert draft_create_res.status_code == expected_code # test creating a deposit with anonymous user restrict_creation(False) test_creation(401) restrict_creation(True) test_creation(401) # test creating a deposit with a logged in user restrict_creation(False) test_creation(201, creator) restrict_creation(True) test_creation(403, creator) # test with a use who is allowed test_creation(201, allowed) # test with a community member and admin test_creation(201, com_member) test_creation(201, com_admin) # test creating a new version test_creation(401, None, version_of=pid.pid_value) test_creation(403, com_member, version_of=pid.pid_value) restrict_creation(True) test_creation(403, creator, version_of=pid.pid_value) restrict_creation(False) test_creation(201, creator, version_of=pid.pid_value)
def test_deposit_create_permission(app, test_users, login_user, test_communities): """Test record draft creation.""" headers = [('Content-Type', 'application/json'), ('Accept', 'application/json')] with app.app_context(): community_name = 'MyTestCommunity1' record_data = generate_record_data(community=community_name) community_id = test_communities[community_name] community = Community.get(community_id) creator = create_user('creator') need = create_deposit_need_factory(str(community_id)) allowed = create_user('allowed', permissions=[need]) com_member = create_user('com_member', roles=[community.member_role]) com_admin = create_user('com_admin', roles=[community.admin_role]) deposit, pid, record = create_record(record_data, creator) def restrict_creation(restricted): community.update({'restricted_submission': restricted}) db.session.commit() def test_creation(expected_code, user=None, version_of=None): with app.test_client() as client: if user is not None: login_user(user, client) draft_create_res = client.post(url_for( 'b2share_records_rest.b2rec_list', version_of=version_of), data=json.dumps(record_data), headers=headers) assert draft_create_res.status_code == expected_code # test creating a deposit with anonymous user restrict_creation(False) test_creation(401) restrict_creation(True) test_creation(401) # test creating a deposit with a logged in user restrict_creation(False) test_creation(201, creator) restrict_creation(True) test_creation(403, creator) # test with a use who is allowed test_creation(201, allowed) # test with a community member and admin test_creation(201, com_member) test_creation(201, com_admin) # test creating a new version test_creation(401, None, version_of=pid.pid_value) test_creation(403, com_member, version_of=pid.pid_value) restrict_creation(True) test_creation(403, creator, version_of=pid.pid_value) restrict_creation(False) test_creation(201, creator, version_of=pid.pid_value)
def test_modify_metadata_published_record_permissions(app, test_communities, login_user, test_users): """Test record's metadata modification with REST API.""" admin = test_users['admin'] with app.app_context(): creator = create_user('creator') non_creator = create_user('non-creator') record_data = generate_record_data(open_access=True) community = Community.get(id=record_data['community']) com_admin = create_user('com_admin', roles=[community.admin_role]) com_member = create_user('com_member', roles=[community.member_role]) def test_modify(status, user=None): patch = [{ "op": "replace", "path": "/titles", "value": [{ 'title': 'newtitle' }] }] with app.test_client() as client: _, record_pid, record = create_record(record_data, creator) if user is not None: login_user(user, client) # test patching the document headers = [('Content-Type', 'application/json-patch+json'), ('Accept', 'application/json')] request_res = client.patch(url_for( 'b2share_records_rest.b2rec_item', pid_value=record_pid.pid_value), data=json.dumps(patch), headers=headers) assert request_res.status_code == status # _, record_pid, record = create_record(record_data, creator) # test putting the document # data = dict(record) # apply_patch(data, patch) # headers = [('Content-Type', 'application/json'), # ('Accept', 'application/json')] # request_res = client.put( # url_for('b2share_records_rest.b2rec_item', # pid_value=record_pid.pid_value), # data=json.dumps(data), # headers=headers) # assert request_res.status_code == status # test with anonymous user test_modify(401) test_modify(403, non_creator) test_modify(200, creator) test_modify(403, com_member) test_modify(200, com_admin) test_modify(200, admin)
def commit(self): """Store changes on current instance in database. This method extends the default implementation by publishing the deposition when 'publication_state' is set to 'published'. """ if self.model is None or self.model.json is None: raise MissingModelError() # automatically make embargoed records private if self.get('embargo_date') and self.get('open_access'): if is_under_embargo(self): self['open_access'] = False if 'community' in self: try: community = Community.get(self['community']) except CommunityDoesNotExistError as e: raise InvalidDepositError('Community {} does not exist.'.format( self['community'])) from e workflow = publication_workflows[community.publication_workflow] workflow(self.model, self) # publish the deposition if needed if (self['publication_state'] == PublicationStates.published.name # check invenio-deposit status so that we do not loop and self['_deposit']['status'] != PublicationStates.published.name): # Retrieve previous version in order to reindex it later. previous_version_pid = None # Save the previous "last" version for later use if self.versioning.parent.status == PIDStatus.REDIRECTED and \ self.versioning.has_children: previous_version_pid = self.versioning.last_child previous_version_uuid = str(RecordUUIDProvider.get( previous_version_pid.pid_value ).pid.object_uuid) super(Deposit, self).publish() # publish() already calls commit() # Register parent PID if necessary and update redirect self.versioning.update_redirect() # Reindex previous version. This is needed in order to update # the is_last_version flag if previous_version_pid is not None: self.indexer.index_by_id(previous_version_uuid) # save the action for later indexing if g: g.deposit_action = 'publish' else: super(Deposit, self).commit() if g: g.deposit_action = 'update-metadata' return self
def test_deposit_publish_permissions(app, login_user, test_communities, test_users): """Test deposit publication with HTTP PATCH.""" with app.app_context(): community_name = 'MyTestCommunity1' record_data = generate_record_data(community=community_name) admin = test_users['admin'] creator = create_user('creator') non_creator = create_user('non-creator') community = Community.get(name=community_name) com_member = create_user('com_member', roles=[community.member_role]) com_admin = create_user('com_admin', roles=[community.admin_role]) def test_publish(status, user=None): deposit = create_deposit(record_data, creator) deposit.submit() headers = [('Content-Type', 'application/json-patch+json'), ('Accept', 'application/json')] with app.test_client() as client: if user is not None: login_user(user, client) request_res = client.patch(url_for( 'b2share_deposit_rest.b2dep_item', pid_value=deposit.pid.pid_value), data=json.dumps([{ "op": "replace", "path": "/publication_state", "value": PublicationStates.published.name }, { "op": "replace", "path": "/titles", "value": [{ 'title': 'newtitle' }] }]), headers=headers) assert request_res.status_code == status # test with anonymous user test_publish(401) test_publish(403, non_creator) test_publish(403, creator) test_publish(200, admin) test_publish(403, com_member) test_publish(200, com_admin)
def test_modify_metadata_published_record_permissions(app, test_communities, login_user, test_users): """Test record's metadata modification with REST API.""" admin = test_users['admin'] with app.app_context(): creator = create_user('creator') non_creator = create_user('non-creator') record_data = generate_record_data(open_access=True) community = Community.get(id=record_data['community']) com_admin = create_user('com_admin', roles=[community.admin_role]) com_member = create_user('com_member', roles=[community.member_role]) def test_modify(status, user=None): patch = [{ "op": "replace", "path": "/titles", "value": [{'title':'newtitle'}] }] with app.test_client() as client: _, record_pid, record = create_record(record_data, creator) if user is not None: login_user(user, client) # test patching the document headers = [('Content-Type', 'application/json-patch+json'), ('Accept', 'application/json')] request_res = client.patch( url_for('b2share_records_rest.b2rec_item', pid_value=record_pid.pid_value), data=json.dumps(patch), headers=headers) assert request_res.status_code == status # _, record_pid, record = create_record(record_data, creator) # test putting the document # data = dict(record) # apply_patch(data, patch) # headers = [('Content-Type', 'application/json'), # ('Accept', 'application/json')] # request_res = client.put( # url_for('b2share_records_rest.b2rec_item', # pid_value=record_pid.pid_value), # data=json.dumps(data), # headers=headers) # assert request_res.status_code == status # test with anonymous user test_modify(401) test_modify(403, non_creator) test_modify(200, creator) test_modify(403, com_member) test_modify(200, com_admin) test_modify(200, admin)
def test_get_block_schemas(app, test_communities): """"Test getting list of given community's schemas.""" with app.app_context(): community = Community.create_community('name1', 'desc1') community_id = community.id community_2 = Community.create_community('name2', 'desc2') community_id2 = community_2.id block_schema_1 = BlockSchema.create_block_schema(community_id, 'abc') block_schema_2 = BlockSchema.create_block_schema(community_id2, 'abc2') block_schema_3 = BlockSchema.create_block_schema(community_id2, 'abc3') with app.test_client() as client: headers = [('Content-Type', 'application/json'), ('Accept', 'application/json')] res = client.get(url_for('b2share_schemas.block_schema_list', community_id=community_id2), headers=headers) assert res.status_code == 200 response_data = json.loads(res.get_data(as_text=True)) assert isinstance(response_data['schemas'], list) assert len(response_data['schemas']) == 2 assert response_data['schemas'][0]['name'] == block_schema_2.name assert response_data['schemas'][1]['name'] == block_schema_3.name
def test_deposit_modify_published_permissions(app, login_user, test_communities, test_users): """Test deposit edition after its publication. FIXME: This test should evolve when we allow deposit edition. """ with app.app_context(): community_name = 'MyTestCommunity1' record_data = generate_record_data(community=community_name) admin = test_users['admin'] creator = create_user('creator') non_creator = create_user('non-creator') community = Community.get(name=community_name) com_member = create_user('com_member', roles=[community.member_role]) com_admin = create_user('com_admin', roles=[community.admin_role]) deposit = create_deposit(record_data, creator) deposit.submit() deposit.publish() def test_edit(status, user=None): headers = [('Content-Type', 'application/json-patch+json'), ('Accept', 'application/json')] with app.test_client() as client: if user is not None: login_user(user, client) request_res = client.patch(url_for( 'b2share_deposit_rest.b2dep_item', pid_value=deposit.pid.pid_value), data=json.dumps([{ "op": "replace", "path": "/publication_state", "value": PublicationStates.draft.name }]), headers=headers) assert request_res.status_code == status # test with anonymous user test_edit(401) test_edit(403, non_creator) test_edit(403, creator) test_edit(403, admin) test_edit(403, com_member) test_edit(403, com_admin)
def test_updating_block_schema(app, test_communities): """Test updating a block schema.""" with app.app_context(): community = Community.create_community('name1', 'desc1') community_id = community.id block_schema = BlockSchema.create_block_schema(community_id, 'original') with app.test_client() as client: headers = [('Content-Type', 'application/json'), ('Accept', 'application/json')] res = client.patch(url_for('b2share_schemas.block_schema_item', schema_id=block_schema.id), data=json.dumps({'name': 'abc'}), headers=headers) assert res.status_code == 200
def test_community(app, test_communities): """Initialize member and admin of a community.""" CommunityRef = namedtuple( 'CommunityRef', ['name', 'id', 'admin_role_id', 'member_role_id', 'admin', 'member']) with app.app_context(): community_name = 'MyTestCommunity1' community = CommunityAPI.get(name=community_name) admin_role_id = community.admin_role.id member_role_id = community.member_role.id com_admin = create_user('com_admin', roles=[community.admin_role]) com_member = create_user('com_member', roles=[community.member_role]) db.session.commit() return CommunityRef(community_name, community.id, admin_role_id, member_role_id, com_admin, com_member)
def test_community(app, test_communities): """Initialize member and admin of a community.""" CommunityRef = namedtuple('CommunityRef',[ 'name', 'id', 'admin_role_id', 'member_role_id', 'admin', 'member' ]) with app.app_context(): community_name = 'MyTestCommunity1' community = CommunityAPI.get(name=community_name) admin_role_id = community.admin_role.id member_role_id = community.member_role.id com_admin = create_user('com_admin', roles=[community.admin_role]) com_member = create_user('com_member', roles=[community.member_role]) db.session.commit() return CommunityRef(community_name, community.id, admin_role_id, member_role_id, com_admin, com_member)
def test_deposit(app, test_communities, login_user, authentication): """Test record submission with classic login and access token.""" with app.app_context(): allowed_user = create_user('allowed') scopes = current_oauth2server.scope_choices() allowed_token = Token.create_personal( 'allowed_token', allowed_user.id, scopes=[s[0] for s in scopes] ) # application authentication token header allowed_headers = [('Authorization', 'Bearer {}'.format(allowed_token.access_token))] other_user = create_user('other') other_token = Token.create_personal( 'other_token', other_user.id, scopes=[s[0] for s in scopes] ) # application authentication token header other_headers = [('Authorization', 'Bearer {}'.format(other_token.access_token))] community_name = 'MyTestCommunity1' community = Community.get(name=community_name) com_admin = create_user('com_admin', roles=[community.admin_role]) com_admin_token = Token.create_personal( 'com_admin_token', com_admin.id, scopes=[s[0] for s in scopes] ) # application authentication token header com_admin_headers = [('Authorization', 'Bearer {}'.format(com_admin_token.access_token))] test_records_data = [generate_record_data(community=community_name) for idx in range(1,3)] db.session.commit() if authentication == 'user/password': subtest_deposit(app, test_communities, allowed_user, other_user, com_admin, [], [], [], login_user, test_records_data) else: subtest_deposit(app, test_communities, allowed_user, other_user, com_admin, allowed_headers, other_headers, com_admin_headers, lambda u, c: 42, test_records_data)
def test_deposit(app, test_communities, login_user, authentication): """Test record submission with classic login and access token.""" with app.app_context(): allowed_user = create_user('allowed') scopes = current_oauth2server.scope_choices() allowed_token = Token.create_personal('allowed_token', allowed_user.id, scopes=[s[0] for s in scopes]) # application authentication token header allowed_headers = [('Authorization', 'Bearer {}'.format(allowed_token.access_token))] other_user = create_user('other') other_token = Token.create_personal('other_token', other_user.id, scopes=[s[0] for s in scopes]) # application authentication token header other_headers = [('Authorization', 'Bearer {}'.format(other_token.access_token))] community_name = 'MyTestCommunity1' community = Community.get(name=community_name) com_admin = create_user('com_admin', roles=[community.admin_role]) com_admin_token = Token.create_personal('com_admin_token', com_admin.id, scopes=[s[0] for s in scopes]) # application authentication token header com_admin_headers = [ ('Authorization', 'Bearer {}'.format(com_admin_token.access_token)) ] test_records_data = [ generate_record_data(community=community_name) for idx in range(1, 3) ] db.session.commit() if authentication == 'user/password': subtest_deposit(app, test_communities, allowed_user, other_user, com_admin, [], [], [], login_user, test_records_data) else: subtest_deposit(app, test_communities, allowed_user, other_user, com_admin, allowed_headers, other_headers, com_admin_headers, lambda u, c: 42, test_records_data)
def test_deposit_modify_published_permissions(app, login_user, test_communities, test_users): """Test deposit edition after its publication. FIXME: This test should evolve when we allow deposit edition. """ with app.app_context(): community_name = 'MyTestCommunity1' record_data = generate_record_data(community=community_name) admin = test_users['admin'] creator = create_user('creator') non_creator = create_user('non-creator') community = Community.get(name=community_name) com_member = create_user('com_member', roles=[community.member_role]) com_admin = create_user('com_admin', roles=[community.admin_role]) deposit = create_deposit(record_data, creator) deposit.submit() deposit.publish() def test_edit(status, user=None): headers = [('Content-Type', 'application/json-patch+json'), ('Accept', 'application/json')] with app.test_client() as client: if user is not None: login_user(user, client) request_res = client.patch( url_for('b2share_deposit_rest.b2dep_item', pid_value=deposit.pid.pid_value), data=json.dumps([{ "op": "replace", "path": "/publication_state", "value": PublicationStates.draft.name }]), headers=headers) assert request_res.status_code == status # test with anonymous user test_edit(401) test_edit(403, non_creator) test_edit(403, creator) test_edit(403, admin) test_edit(403, com_member) test_edit(403, com_admin)
def test_deposit_publish_permissions(app, login_user, test_communities, test_users): """Test deposit publication with HTTP PATCH.""" with app.app_context(): community_name = 'MyTestCommunity1' record_data = generate_record_data(community=community_name) admin = test_users['admin'] creator = create_user('creator') non_creator = create_user('non-creator') community = Community.get(name=community_name) com_member = create_user('com_member', roles=[community.member_role]) com_admin = create_user('com_admin', roles=[community.admin_role]) def test_publish(status, user=None): deposit = create_deposit(record_data, creator) deposit.submit() headers = [('Content-Type', 'application/json-patch+json'), ('Accept', 'application/json')] with app.test_client() as client: if user is not None: login_user(user, client) request_res = client.patch( url_for('b2share_deposit_rest.b2dep_item', pid_value=deposit.pid.pid_value), data=json.dumps([{ "op": "replace", "path": "/publication_state", "value": PublicationStates.published.name }, { "op": "replace", "path": "/titles", "value": [{'title':'newtitle'}] }]), headers=headers) assert request_res.status_code == status # test with anonymous user test_publish(401) test_publish(403, non_creator) test_publish(403, creator) test_publish(200, admin) test_publish(403, com_member) test_publish(200, com_admin)
def block_schema_list_versions(verbose, block_schema_id): """show the version number and release date of the versions of a block schema.""" try: UUID(block_schema_id, version=4) except ValueError: raise click.BadParameter("""BLOCK_SCHEMA_ID is not a valid UUID (hexadecimal numbers and dashes e.g. fa52bec3-a847-4602-8af5-b8d41a5215bc )""") try: block_schema = BlockSchema.get_block_schema(schema_id=block_schema_id) except BlockSchemaDoesNotExistError: raise click.BadParameter("No block_schema with id %s" % block_schema_id) click.secho( "BLOCK SCHEMA VERSIONS FOR community %s, block schema %s" % (Community.get(id=block_schema.community).name, block_schema.name)) click.secho("Version no.\tRelease date") for bl_schema_version in block_schema.versions: click.secho("%s\t%s" % (bl_schema_version.version, bl_schema_version.released))
def commit(self): """Store changes on current instance in database. This method extends the default implementation by publishing the deposition when 'publication_state' is set to 'published'. """ if self.model is None or self.model.json is None: raise MissingModelError() # automatically make embargoed records private if self.get('embargo_date') and self.get('open_access'): if is_under_embargo(self): self['open_access'] = False if 'community' in self: try: community = Community.get(self['community']) except CommunityDoesNotExistError as e: raise InvalidDepositError( 'Community {} does not exist.'.format( self['community'])) from e workflow = publication_workflows[community.publication_workflow] workflow(self.model, self) # publish the deposition if needed if (self['publication_state'] == PublicationStates.published.name # check invenio-deposit status so that we do not loop and self['_deposit']['status'] != PublicationStates.published.name): super(Deposit, self).publish() # publish() already calls commit() # save the action for later indexing if g: g.deposit_action = 'publish' else: super(Deposit, self).commit() if g: g.deposit_action = 'update-metadata' return self
def __init__(self, record=None): """Constructor. Args: record: data submitted for the new deposit """ super(CreateDepositPermission, self).__init__() self.record = record if record is not None: needs = set() community = Community.get(record['community']) publication_state = record.get('publication_state', 'draft') if publication_state != 'draft' or community.restricted_submission: needs.add(create_deposit_need_factory()) needs.add( create_deposit_need_factory( community=record['community'], publication_state=publication_state, )) elif not community.restricted_submission: needs.add(AuthenticatedNeed) self.permissions.add(StrictDynamicPermission(*needs))
def test_new_community_set_schema_cmd(app, login_user, tmp_location): """Test adding a community and setting its schema using CLI commands.""" with app.app_context(): tmp_location.default = True db.session.merge(tmp_location) db.session.commit() runner = CliRunner() script_info = ScriptInfo(create_app=lambda info: app) comm_name = 'MyCom' # Create a new community on the command line args = ['create', comm_name, 'MyCom description', 'eudat.png'] result = runner.invoke(communities_cmd, args, obj=script_info) assert result.exit_code == 0 community = Community.get(name=comm_name) assert community assert community.name == comm_name # Create a schema for this new community with runner.isolated_filesystem(): with open("schema.json", "w") as f: f.write(json.dumps(test_schema)) # check RootSchemaDoesNotExistError with pytest.raises(RootSchemaDoesNotExistError): update_or_set_community_schema(comm_name, 'schema.json') # check RootSchemaDoesNotExistError via cli args = ['set_schema', comm_name, 'schema.json'] result = runner.invoke(communities_cmd, args, obj=script_info) assert result.exit_code != 0 # initialize the root schema in the test environment result = runner.invoke(schemas_cmd, ['init', '--ignore-mismatches'], obj=script_info) assert result.exit_code == 0 # Make custom schema via cli args = ['set_schema', comm_name, 'schema.json'] result = runner.invoke(communities_cmd, args, obj=script_info) assert result.exit_code == 0 community_name = 'MyCom' community = Community.get(name=community_name) # Get the block schema id community_schema = CommunitySchema.get_community_schema( community.id).community_schema props = json.loads(community_schema).get('properties') assert len(props) == 1 block_schema_id = props.popitem()[0] # Test the community schema by publishing a record with app.test_client() as client: user = create_user('allowed') login_user(user, client) record_data = { 'titles': [{ 'title': 'My Test Record' }], 'community': str(community.id), 'open_access': True, 'community_specific': { block_schema_id: { 'test_field1': "string value", 'test_field2': 10, } }, } headers = [('Content-Type', 'application/json'), ('Accept', 'application/json')] draft_res = client.post(url_for('b2share_records_rest.b2rec_list'), data=json.dumps(record_data), headers=headers) draft_data = json.loads(draft_res.get_data(as_text=True)) assert draft_res.status_code == 201 headers = [('Content-Type', 'application/json-patch+json'), ('Accept', 'application/json')] draft_submit_res = client.patch(url_for( 'b2share_deposit_rest.b2dep_item', pid_value=draft_data['id']), data=json.dumps([{ "op": "replace", "path": "/publication_state", "value": "submitted", }]), headers=headers) assert draft_submit_res.status_code == 200
def test_deposit_files_permissions(app, test_communities, login_user, test_users): """Test deposit read with HTTP GET.""" with app.app_context(): community_name = 'MyTestCommunity1' admin = test_users['admin'] creator = create_user('creator') non_creator = create_user('non-creator') community = Community.get(name=community_name) com_member = create_user('com_member', roles=[community.member_role]) com_admin = create_user('com_admin', roles=[community.admin_role]) uploaded_files = { 'myfile1.dat': b'contents1', 'myfile2.dat': b'contents2' } test_record_data = generate_record_data(community=community_name) def test_files_access(draft_access, submitted_access, published_access, user=None): def get_file(deposit, file_access): with app.test_client() as client: if user is not None: login_user(user, client) subtest_file_bucket_permissions( client, deposit.files.bucket, access_level=file_access, is_authenticated=user is not None ) deposit = create_deposit(test_record_data, creator, uploaded_files) get_file(deposit, file_access=draft_access) deposit = create_deposit(test_record_data, creator, uploaded_files) deposit.submit() get_file(deposit, file_access=submitted_access) deposit = create_deposit(test_record_data, creator, uploaded_files) deposit.submit() deposit.publish() get_file(deposit, file_access=published_access) # Anonymous user test_files_access(draft_access=None, submitted_access=None, published_access=None) # Non creator user test_files_access(user=non_creator, draft_access=None, submitted_access=None, published_access=None) # Creator test_files_access(user=creator, draft_access='write', submitted_access='write', published_access=None) # Admin test_files_access(user=admin, draft_access='write', submitted_access='write', published_access=None) # Community member test_files_access(user=com_member, draft_access=None, submitted_access=None, published_access=None) # Community admin test_files_access(user=com_admin, draft_access=None, submitted_access='write', published_access=None)
def test_make_record_with_no_file_and_search(app, test_communities, login_user): '''Test for issue https://github.com/EUDAT-B2SHARE/b2share/issues/1073''' def url_for(*args, **kwargs): with app.app_context(): return flask_url_for(*args, **kwargs) with app.app_context(): community_name = 'MyTestCommunity1' record_data = generate_record_data(community=community_name) allowed_user = create_user('allowed') community = Community.get(name=community_name) com_admin = create_user('com_admin', roles=[community.admin_role]) db.session.commit() with app.test_client() as client: login_user(allowed_user, client) headers = [('Content-Type', 'application/json'), ('Accept', 'application/json')] patch_headers = [('Content-Type', 'application/json-patch+json'), ('Accept', 'application/json')] # create record without files draft_create_res = client.post( url_for('b2share_records_rest.b2rec_list'), data=json.dumps(record_data), headers=headers) assert draft_create_res.status_code == 201 draft_create_data = json.loads(draft_create_res.get_data(as_text=True)) # submit the record draft_submit_res = client.patch(url_for( 'b2share_deposit_rest.b2dep_item', pid_value=draft_create_data['id']), data=json.dumps([{ "op": "replace", "path": "/publication_state", "value": PublicationStates.submitted.name }]), headers=patch_headers) assert draft_submit_res.status_code == 200 with app.test_client() as client: login_user(com_admin, client) # publish record draft_publish_res = client.patch(url_for( 'b2share_deposit_rest.b2dep_item', pid_value=draft_create_data['id']), data=json.dumps([{ "op": "replace", "path": "/publication_state", "value": PublicationStates.published.name }]), headers=patch_headers) assert draft_publish_res.status_code == 200 draft_publish_data = json.loads( draft_publish_res.get_data(as_text=True)) # get record record_get_res = client.get(url_for( 'b2share_records_rest.b2rec_item', pid_value=draft_publish_data['id']), headers=headers) assert record_get_res.status_code == 200 record_get_data = json.loads(record_get_res.get_data(as_text=True)) with app.test_client() as client: # refresh index to make records searchable with app.app_context(): current_search._client.indices.refresh() with app.test_client() as client: # test search, for crashes record_search_res = client.get( url_for('b2share_records_rest.b2rec_list'), data='', headers=headers) assert record_search_res.status_code == 200 record_search_data = json.loads( record_search_res.get_data(as_text=True)) assert len(record_search_data['hits']['hits']) == 1 record_hit = record_search_data['hits']['hits'][0] # TODO: the following assert should work: # assert record_hit == record_get_data # -- but currently it doesn't because records with no files # do not have a 'files' link assert record_hit['metadata'] == record_get_data['metadata']
def test_deposit_files_permissions(app, test_communities, login_user, test_users): """Test deposit read with HTTP GET.""" with app.app_context(): community_name = 'MyTestCommunity1' admin = test_users['admin'] creator = create_user('creator') non_creator = create_user('non-creator') community = Community.get(name=community_name) com_member = create_user('com_member', roles=[community.member_role]) com_admin = create_user('com_admin', roles=[community.admin_role]) uploaded_files = { 'myfile1.dat': b'contents1', 'myfile2.dat': b'contents2' } test_record_data = generate_record_data(community=community_name) def test_files_access(draft_access, submitted_access, published_access, user=None): def get_file(deposit, file_access): with app.test_client() as client: if user is not None: login_user(user, client) subtest_file_bucket_permissions(client, deposit.files.bucket, access_level=file_access, is_authenticated=user is not None) deposit = create_deposit(test_record_data, creator, uploaded_files) get_file(deposit, file_access=draft_access) deposit = create_deposit(test_record_data, creator, uploaded_files) deposit.submit() get_file(deposit, file_access=submitted_access) deposit = create_deposit(test_record_data, creator, uploaded_files) deposit.submit() deposit.publish() get_file(deposit, file_access=published_access) # Anonymous user test_files_access(draft_access=None, submitted_access=None, published_access=None) # Non creator user test_files_access(user=non_creator, draft_access=None, submitted_access=None, published_access=None) # Creator test_files_access(user=creator, draft_access='write', submitted_access='write', published_access=None) # Admin test_files_access(user=admin, draft_access='write', submitted_access='write', published_access=None) # Community member test_files_access(user=com_member, draft_access=None, submitted_access=None, published_access=None) # Community admin test_files_access(user=com_admin, draft_access=None, submitted_access='write', published_access=None)
def test_deposit_read_permissions(app, login_user, test_users, test_communities): """Test deposit read with HTTP GET.""" with app.app_context(): community_name = 'MyTestCommunity1' record_data = generate_record_data(community=community_name) community = Community.get(name=community_name) admin = test_users['admin'] creator = create_user('creator') non_creator = create_user('non-creator') com_member = create_user('com_member', roles=[community.member_role]) com_admin = create_user('com_admin', roles=[community.admin_role]) def test_get(deposit, status, user=None): with app.test_client() as client: if user is not None: login_user(user, client) headers = [('Accept', 'application/json')] request_res = client.get(url_for( 'b2share_deposit_rest.b2dep_item', pid_value=deposit.pid.pid_value), headers=headers) assert request_res.status_code == status # test with anonymous user deposit = create_deposit(record_data, creator) test_get(deposit, 401) deposit.submit() test_get(deposit, 401) deposit.publish() test_get(deposit, 401) deposit = create_deposit(record_data, creator) test_get(deposit, 403, non_creator) deposit.submit() test_get(deposit, 403, non_creator) deposit.publish() test_get(deposit, 403, non_creator) deposit = create_deposit(record_data, creator) test_get(deposit, 200, creator) deposit.submit() test_get(deposit, 200, creator) deposit.publish() test_get(deposit, 200, creator) deposit = create_deposit(record_data, creator) test_get(deposit, 200, admin) deposit.submit() test_get(deposit, 200, admin) deposit.publish() test_get(deposit, 200, admin) deposit = create_deposit(record_data, creator) test_get(deposit, 403, com_member) deposit.submit() test_get(deposit, 403, com_member) deposit.publish() test_get(deposit, 403, com_member) deposit = create_deposit(record_data, creator) test_get(deposit, 403, com_admin) deposit.submit() test_get(deposit, 200, com_admin) deposit.publish() test_get(deposit, 200, com_admin)
def test_deposit_publish(app, test_users, test_communities, login_user): """Test record draft publication with HTTP PATCH.""" with app.app_context(): community_name = 'MyTestCommunity1' creator = test_users['deposits_creator'] record_data = generate_record_data(community=community_name) community = Community.get(name=community_name) com_admin = create_user('com_admin', roles=[community.admin_role]) deposit = create_deposit(record_data, creator) deposit_id = deposit.id deposit.submit() db.session.commit() with app.test_client() as client: login_user(com_admin, client) headers = [('Content-Type', 'application/json-patch+json'), ('Accept', 'application/json')] draft_patch_res = client.patch(url_for( 'b2share_deposit_rest.b2dep_item', pid_value=deposit.pid.pid_value), data=json.dumps([{ "op": "replace", "path": "/publication_state", "value": PublicationStates.published.name }]), headers=headers) assert draft_patch_res.status_code == 200 draft_patch_data = json.loads( draft_patch_res.get_data(as_text=True)) expected_metadata = build_expected_metadata( record_data, PublicationStates.published.name, owners=[creator.id], draft=True, PID=draft_patch_data['metadata'].get('ePIC_PID'), DOI=draft_patch_data['metadata'].get('DOI'), ) with app.app_context(): deposit = Deposit.get_record(deposit_id) with app.test_client() as client: login_user(creator, client) assert expected_metadata == draft_patch_data['metadata'] assert (deposit['publication_state'] == PublicationStates.published.name) subtest_self_link(draft_patch_data, draft_patch_res.headers, client) pid, published = deposit.fetch_published() # check that the published record and the deposit are equal except # for the schema cleaned_deposit = { f: v for f, v in deposit.items() if f != '$schema' } cleaned_published = { f: v for f, v in deposit.items() if f != '$schema' } assert cleaned_published == cleaned_deposit # check "published" link assert draft_patch_data['links']['publication'] == \ url_for('b2share_records_rest.{0}_item'.format( RecordUUIDProvider.pid_type ), pid_value=pid.pid_value, _external=True) # check that the published record content match the deposit headers = [('Accept', 'application/json')] self_response = client.get( draft_patch_data['links']['publication'], headers=headers) assert self_response.status_code == 200 published_data = json.loads(self_response.get_data(as_text=True)) # we don't want to compare the links and dates cleaned_published_data = deepcopy(published_data) # the published record has an extra empty 'files' array assert cleaned_published_data['files'] == [] del cleaned_published_data['files'] cleaned_draft_data = deepcopy(draft_patch_data) for item in [cleaned_published_data, cleaned_draft_data]: del item['links'] del item['created'] del item['updated'] del item['metadata']['$schema'] assert cleaned_draft_data == cleaned_published_data
def community(self, metadata, root): try: c = Community.get(id=metadata['community']) root.append(E.community(c.name)) except CommunityDoesNotExistError: root.append(E.community('unknown'))
def test_new_community_set_schema_cmd(app, login_user, tmp_location): """Test adding a community and setting its schema using CLI commands.""" with app.app_context(): tmp_location.default = True db.session.merge(tmp_location) db.session.commit() runner = CliRunner() script_info = ScriptInfo(create_app=lambda info: app) comm_name = 'MyCom' # Create a new community on the command line args = ['create', comm_name, 'MyCom description', 'eudat.png'] result = runner.invoke(communities_cmd, args, obj=script_info) assert result.exit_code == 0 community = Community.get(name=comm_name) assert community assert community.name == comm_name # Create a schema for this new community with runner.isolated_filesystem(): with open("schema.json", "w") as f: f.write(json.dumps(test_schema)) # check RootSchemaDoesNotExistError with pytest.raises(RootSchemaDoesNotExistError): update_or_set_community_schema(comm_name, 'schema.json') # check RootSchemaDoesNotExistError via cli args = ['set_schema', comm_name, 'schema.json'] result = runner.invoke(communities_cmd, args, obj=script_info) assert result.exit_code != 0 # initialize the root schema in the test environment result = runner.invoke(schemas_cmd, ['init'], obj=script_info) assert result.exit_code == 0 # Make custom schema via cli args = ['set_schema', comm_name, 'schema.json'] result = runner.invoke(communities_cmd, args, obj=script_info) assert result.exit_code == 0 community_name = 'MyCom' community = Community.get(name=community_name) # Get the block schema id community_schema = CommunitySchema.get_community_schema( community.id).community_schema props = json.loads(community_schema).get('properties') assert len(props) == 1 block_schema_id = props.popitem()[0] # Test the community schema by publishing a record with app.test_client() as client: user = create_user('allowed') login_user(user, client) record_data = { 'titles': [{'title':'My Test Record'}], 'community': str(community.id), 'open_access': True, 'community_specific': { block_schema_id: { 'test_field1': "string value", 'test_field2': 10, } }, } headers = [('Content-Type', 'application/json'), ('Accept', 'application/json')] draft_res = client.post(url_for('b2share_records_rest.b2rec_list'), data=json.dumps(record_data), headers=headers) draft_data = json.loads(draft_res.get_data(as_text=True)) assert draft_res.status_code == 201 headers = [('Content-Type', 'application/json-patch+json'), ('Accept', 'application/json')] draft_submit_res = client.patch( url_for('b2share_deposit_rest.b2dep_item', pid_value=draft_data['id']), data=json.dumps([{ "op": "replace", "path": "/publication_state", "value": "submitted", }]), headers=headers) assert draft_submit_res.status_code == 200
def commit(self): """Store changes on current instance in database. This method extends the default implementation by publishing the deposition when 'publication_state' is set to 'published'. """ if 'external_pids' in self: deposit_id = self['_deposit']['id'] recid = PersistentIdentifier.query.filter_by( pid_value=deposit_id).first() assert recid.status == 'R' record_bucket = RecordsBuckets.query.filter_by( record_id=recid.pid_value).first() bucket = Bucket.query.filter_by(id=record_bucket.bucket_id).first() object_versions = ObjectVersion.query.filter_by( bucket_id=bucket.id).all() key_to_pid = { ext_pid.get('key'): ext_pid.get('ePIC_PID') for ext_pid in self['external_pids'] } # for the existing files for object_version in object_versions: if object_version.file is None or \ object_version.file.storage_class != 'B': continue # check that they are still in the file pids list or remove if object_version.key not in key_to_pid: ObjectVersion.delete(bucket, object_version.key) # check that the uri is still the same or update it elif object_version.file.uri != \ key_to_pid[object_version.key]: db.session.query(FileInstance).\ filter(FileInstance.id == object_version.file_id).\ update({"uri": key_to_pid[object_version.key]}) create_b2safe_file(self['external_pids'], bucket) del self['external_pids'] if self.model is None or self.model.json is None: raise MissingModelError() # automatically make embargoed records private if self.get('embargo_date') and self.get('open_access'): if is_under_embargo(self): self['open_access'] = False if 'community' in self: try: community = Community.get(self['community']) except CommunityDoesNotExistError as e: raise InvalidDepositError('Community {} does not exist.'.format( self['community'])) from e workflow = publication_workflows[community.publication_workflow] workflow(self.model, self) # publish the deposition if needed if (self['publication_state'] == PublicationStates.published.name # check invenio-deposit status so that we do not loop and self['_deposit']['status'] != PublicationStates.published.name): # Retrieve previous version in order to reindex it later. previous_version_pid = None # Save the previous "last" version for later use if self.versioning.parent.status == PIDStatus.REDIRECTED and \ self.versioning.has_children: previous_version_pid = self.versioning.last_child previous_version_uuid = str(RecordUUIDProvider.get( previous_version_pid.pid_value ).pid.object_uuid) external_pids = generate_external_pids(self) if external_pids: self['_deposit']['external_pids'] = external_pids super(Deposit, self).publish() # publish() already calls commit() # Register parent PID if necessary and update redirect self.versioning.update_redirect() # Reindex previous version. This is needed in order to update # the is_last_version flag if previous_version_pid is not None: self.indexer.index_by_id(previous_version_uuid) # save the action for later indexing if g: g.deposit_action = 'publish' else: super(Deposit, self).commit() if g: g.deposit_action = 'update-metadata' return self
def community(self, metadata, root): c = Community.get(id=metadata['community']) root.append(E.community(c.name))
def test_deposit_publish(app, test_users, test_communities, login_user): """Test record draft publication with HTTP PATCH.""" with app.app_context(): community_name = 'MyTestCommunity1' creator = test_users['deposits_creator'] record_data = generate_record_data(community=community_name) community = Community.get(name=community_name) com_admin = create_user('com_admin', roles=[community.admin_role]) deposit = create_deposit(record_data, creator) deposit_id = deposit.id deposit.submit() db.session.commit() with app.test_client() as client: login_user(com_admin, client) headers = [('Content-Type', 'application/json-patch+json'), ('Accept', 'application/json')] draft_patch_res = client.patch( url_for('b2share_deposit_rest.b2dep_item', pid_value=deposit.pid.pid_value), data=json.dumps([{ "op": "replace", "path": "/publication_state", "value": PublicationStates.published.name }]), headers=headers) assert draft_patch_res.status_code == 200 draft_patch_data = json.loads( draft_patch_res.get_data(as_text=True)) expected_metadata = build_expected_metadata( record_data, PublicationStates.published.name, owners=[creator.id], draft=True, PID=draft_patch_data['metadata'].get('ePIC_PID'), DOI=draft_patch_data['metadata'].get('DOI'), ) with app.app_context(): deposit = Deposit.get_record(deposit_id) with app.test_client() as client: login_user(creator, client) assert expected_metadata == draft_patch_data['metadata'] assert (deposit['publication_state'] == PublicationStates.published.name) subtest_self_link(draft_patch_data, draft_patch_res.headers, client) pid, published = deposit.fetch_published() # check that the published record and the deposit are equal except # for the schema cleaned_deposit = {f: v for f, v in deposit.items() if f != '$schema'} cleaned_published = {f: v for f, v in deposit.items() if f != '$schema'} assert cleaned_published == cleaned_deposit # check "published" link assert draft_patch_data['links']['publication'] == \ url_for('b2share_records_rest.{0}_item'.format( RecordUUIDProvider.pid_type ), pid_value=pid.pid_value, _external=True) # check that the published record content match the deposit headers = [('Accept', 'application/json')] self_response = client.get( draft_patch_data['links']['publication'], headers=headers ) assert self_response.status_code == 200 published_data = json.loads(self_response.get_data( as_text=True)) # we don't want to compare the links and dates cleaned_published_data = deepcopy(published_data) # the published record has an extra empty 'files' array assert cleaned_published_data['files'] == [] del cleaned_published_data['files'] cleaned_draft_data = deepcopy(draft_patch_data) for item in [cleaned_published_data, cleaned_draft_data]: del item['links'] del item['created'] del item['updated'] del item['metadata']['$schema'] assert cleaned_draft_data == cleaned_published_data
def test_deposit_search_permissions(app, draft_deposits, submitted_deposits, test_users, login_user, test_communities): """Test deposit search permissions.""" with app.app_context(): # flush the indices so that indexed deposits are searchable current_search_client.indices.flush('*') admin = test_users['admin'] creator = test_users['deposits_creator'] non_creator = create_user('non-creator') permission_to_read_all_submitted_deposits = read_deposit_need_factory( community=str(test_communities['MyTestCommunity2']), publication_state='submitted', ) allowed_role = create_role( 'allowed_role', permissions=[permission_to_read_all_submitted_deposits]) user_allowed_by_role = create_user('user-allowed-by-role', roles=[allowed_role]) user_allowed_by_permission = create_user( 'user-allowed-by-permission', permissions=[permission_to_read_all_submitted_deposits]) community = Community.get(test_communities['MyTestCommunity2']) com_member = create_user('com_member', roles=[community.member_role]) com_admin = create_user('com_admin', roles=[community.admin_role]) search_deposits_url = url_for('b2share_records_rest.b2rec_list', drafts=1, size=100) headers = [('Content-Type', 'application/json'), ('Accept', 'application/json')] def test_search(status, expected_deposits, user=None): with app.test_client() as client: if user is not None: login_user(user, client) deposit_search_res = client.get(search_deposits_url, headers=headers) assert deposit_search_res.status_code == status # test the response data only when the user is allowed to # search for deposits if status != 200: return deposit_search_data = json.loads( deposit_search_res.get_data(as_text=True)) assert deposit_search_data['hits']['total'] == \ len(expected_deposits) deposit_pids = [ hit['id'] for hit in deposit_search_data['hits']['hits'] ] expected_deposit_pids = [ dep.deposit_id.hex for dep in expected_deposits ] deposit_pids.sort() expected_deposit_pids.sort() assert deposit_pids == expected_deposit_pids test_search(200, draft_deposits + submitted_deposits, creator) test_search(200, draft_deposits + submitted_deposits, admin) test_search(401, [], None) test_search(200, [], non_creator) # search for submitted records community2_deposits = [ dep for dep in submitted_deposits if dep.data['community'] == str( test_communities['MyTestCommunity2']) ] test_search(200, community2_deposits, user_allowed_by_role) test_search(200, community2_deposits, user_allowed_by_permission) # community admin should have access to all submitted records # in their community test_search(200, [], com_member) test_search(200, community2_deposits, com_admin)
def commit(self): """Store changes on current instance in database. This method extends the default implementation by publishing the deposition when 'publication_state' is set to 'published'. """ if 'external_pids' in self: deposit_id = self['_deposit']['id'] recid = PersistentIdentifier.query.filter_by( pid_value=deposit_id).first() assert recid.status == 'R' record_bucket = RecordsBuckets.query.filter_by( record_id=recid.pid_value).first() bucket = Bucket.query.filter_by(id=record_bucket.bucket_id).first() object_versions = ObjectVersion.query.filter_by( bucket_id=bucket.id).all() key_to_pid = { ext_pid.get('key'): ext_pid.get('ePIC_PID') for ext_pid in self['external_pids'] } # for the existing files for object_version in object_versions: if object_version.file is None or \ object_version.file.storage_class != 'B': continue # check that they are still in the file pids list or remove if object_version.key not in key_to_pid: ObjectVersion.delete(bucket, object_version.key) # check that the uri is still the same or update it elif object_version.file.uri != \ key_to_pid[object_version.key]: db.session.query(FileInstance).\ filter(FileInstance.id == object_version.file_id).\ update({"uri": key_to_pid[object_version.key]}) create_b2safe_file(self['external_pids'], bucket) del self['external_pids'] if self.model is None or self.model.json is None: raise MissingModelError() # automatically make embargoed records private if self.get('embargo_date') and self.get('open_access'): if is_under_embargo(self): self['open_access'] = False if 'community' in self: try: community = Community.get(self['community']) except CommunityDoesNotExistError as e: raise InvalidDepositError('Community {} does not exist.'.format( self['community'])) from e workflow = publication_workflows[community.publication_workflow] workflow(self.model, self) # publish the deposition if needed if (self['publication_state'] == PublicationStates.published.name # check invenio-deposit status so that we do not loop and self['_deposit']['status'] != PublicationStates.published.name): # Retrieve previous version in order to reindex it later. previous_version_pid = None # Save the previous "last" version for later use if self.versioning.parent.status == PIDStatus.REDIRECTED and \ self.versioning.has_children: previous_version_pid = self.versioning.last_child previous_version_uuid = str(RecordUUIDProvider.get( previous_version_pid.pid_value ).pid.object_uuid) external_pids = generate_external_pids(self) if external_pids: self['_deposit']['external_pids'] = external_pids super(Deposit, self).publish() # publish() already calls commit() # Register parent PID if necessary and update redirect self.versioning.update_redirect() # Reindex previous version. This is needed in order to update # the is_last_version flag if previous_version_pid is not None: self.indexer.index_by_id(previous_version_uuid) # save the action for later indexing if g: g.deposit_action = 'publish' else: super(Deposit, self).commit() if g: g.deposit_action = 'update-metadata' return self
def test_deposit_read_permissions(app, login_user, test_users, test_communities): """Test deposit read with HTTP GET.""" with app.app_context(): community_name = 'MyTestCommunity1' record_data = generate_record_data(community=community_name) community = Community.get(name=community_name) admin = test_users['admin'] creator = create_user('creator') non_creator = create_user('non-creator') com_member = create_user('com_member', roles=[community.member_role]) com_admin = create_user('com_admin', roles=[community.admin_role]) def test_get(deposit, status, user=None): with app.test_client() as client: if user is not None: login_user(user, client) headers = [('Accept', 'application/json')] request_res = client.get( url_for('b2share_deposit_rest.b2dep_item', pid_value=deposit.pid.pid_value), headers=headers) assert request_res.status_code == status # test with anonymous user deposit = create_deposit(record_data, creator) test_get(deposit, 401) deposit.submit() test_get(deposit, 401) deposit.publish() test_get(deposit, 401) deposit = create_deposit(record_data, creator) test_get(deposit, 403, non_creator) deposit.submit() test_get(deposit, 403, non_creator) deposit.publish() test_get(deposit, 403, non_creator) deposit = create_deposit(record_data, creator) test_get(deposit, 200, creator) deposit.submit() test_get(deposit, 200, creator) deposit.publish() test_get(deposit, 200, creator) deposit = create_deposit(record_data, creator) test_get(deposit, 200, admin) deposit.submit() test_get(deposit, 200, admin) deposit.publish() test_get(deposit, 200, admin) deposit = create_deposit(record_data, creator) test_get(deposit, 403, com_member) deposit.submit() test_get(deposit, 403, com_member) deposit.publish() test_get(deposit, 403, com_member) deposit = create_deposit(record_data, creator) test_get(deposit, 403, com_admin) deposit.submit() test_get(deposit, 200, com_admin) deposit.publish() test_get(deposit, 200, com_admin)
def test_deposit_search_permissions(app, draft_deposits, submitted_deposits, test_users, login_user, test_communities): """Test deposit search permissions.""" with app.app_context(): # flush the indices so that indexed deposits are searchable current_search_client.indices.flush('*') admin = test_users['admin'] creator = test_users['deposits_creator'] non_creator = create_user('non-creator') permission_to_read_all_submitted_deposits = read_deposit_need_factory( community=str(test_communities['MyTestCommunity2']), publication_state='submitted', ) allowed_role = create_role( 'allowed_role', permissions=[ permission_to_read_all_submitted_deposits ] ) user_allowed_by_role = create_user('user-allowed-by-role', roles=[allowed_role]) user_allowed_by_permission = create_user( 'user-allowed-by-permission', permissions=[ permission_to_read_all_submitted_deposits ] ) community = Community.get(test_communities['MyTestCommunity2']) com_member = create_user('com_member', roles=[community.member_role]) com_admin = create_user('com_admin', roles=[community.admin_role]) search_deposits_url = url_for( 'b2share_records_rest.b2rec_list', drafts=1, size=100) headers = [('Content-Type', 'application/json'), ('Accept', 'application/json')] def test_search(status, expected_deposits, user=None): with app.test_client() as client: if user is not None: login_user(user, client) deposit_search_res = client.get( search_deposits_url, headers=headers) assert deposit_search_res.status_code == status # test the response data only when the user is allowed to # search for deposits if status != 200: return deposit_search_data = json.loads( deposit_search_res.get_data(as_text=True)) assert deposit_search_data['hits']['total'] == \ len(expected_deposits) deposit_pids = [hit['id'] for hit in deposit_search_data['hits']['hits']] expected_deposit_pids = [dep.deposit_id.hex for dep in expected_deposits] deposit_pids.sort() expected_deposit_pids.sort() assert deposit_pids == expected_deposit_pids test_search(200, draft_deposits + submitted_deposits, creator) test_search(200, draft_deposits + submitted_deposits, admin) test_search(401, [], None) test_search(200, [], non_creator) # search for submitted records community2_deposits = [dep for dep in submitted_deposits if dep.data['community'] == str(test_communities['MyTestCommunity2'])] test_search(200, community2_deposits, user_allowed_by_role) test_search(200, community2_deposits, user_allowed_by_permission) # community admin should have access to all submitted records # in their community test_search(200, [], com_member) test_search(200, community2_deposits, com_admin)
def test_make_record_with_no_file_and_search(app, test_communities, login_user): '''Test for issue https://github.com/EUDAT-B2SHARE/b2share/issues/1073''' def url_for(*args, **kwargs): with app.app_context(): return flask_url_for(*args, **kwargs) with app.app_context(): community_name = 'MyTestCommunity1' record_data = generate_record_data(community=community_name) allowed_user = create_user('allowed') community = Community.get(name=community_name) com_admin = create_user('com_admin', roles=[community.admin_role]) db.session.commit() with app.test_client() as client: login_user(allowed_user, client) headers = [('Content-Type', 'application/json'), ('Accept', 'application/json')] patch_headers = [('Content-Type', 'application/json-patch+json'), ('Accept', 'application/json')] # create record without files draft_create_res = client.post( url_for('b2share_records_rest.b2rec_list'), data=json.dumps(record_data), headers=headers) assert draft_create_res.status_code == 201 draft_create_data = json.loads( draft_create_res.get_data(as_text=True)) # submit the record draft_submit_res = client.patch( url_for('b2share_deposit_rest.b2dep_item', pid_value=draft_create_data['id']), data=json.dumps([{ "op": "replace", "path": "/publication_state", "value": PublicationStates.submitted.name }]), headers=patch_headers) assert draft_submit_res.status_code == 200 with app.test_client() as client: login_user(com_admin, client) # publish record draft_publish_res = client.patch( url_for('b2share_deposit_rest.b2dep_item', pid_value=draft_create_data['id']), data=json.dumps([{ "op": "replace", "path": "/publication_state", "value": PublicationStates.published.name }]), headers=patch_headers) assert draft_publish_res.status_code == 200 draft_publish_data = json.loads( draft_publish_res.get_data(as_text=True)) # get record record_get_res = client.get( url_for('b2share_records_rest.b2rec_item', pid_value=draft_publish_data['id']), headers=headers) assert record_get_res.status_code == 200 record_get_data = json.loads( record_get_res.get_data(as_text=True)) with app.test_client() as client: # refresh index to make records searchable with app.app_context(): current_search._client.indices.refresh() with app.test_client() as client: # test search, for crashes record_search_res = client.get( url_for('b2share_records_rest.b2rec_list'), data='', headers=headers) assert record_search_res.status_code == 200 record_search_data = json.loads( record_search_res.get_data(as_text=True)) assert len(record_search_data['hits']['hits']) == 1 record_hit = record_search_data['hits']['hits'][0] # TODO: the following assert should work: # assert record_hit == record_get_data # -- but currently it doesn't because records with no files # do not have a 'files' link assert record_hit['metadata'] == record_get_data['metadata']
def test_file_download_statistics(app, test_community, test_users, test_records, login_user): """Test checking a record's DOI using CLI commands.""" with app.app_context(): def url_for(*args, **kwargs): """Generate url using flask.url_for and the current app ctx.""" with app.app_context(): return flask_url_for(*args, **kwargs) # create user that will create the record and the files scopes = current_oauth2server.scope_choices() allowed_user = create_user('allowed') scopes = current_oauth2server.scope_choices() allowed_token = Token.create_personal('allowed_token', allowed_user.id, scopes=[s[0] for s in scopes]) # application authentication token header allowed_headers = [('Authorization', 'Bearer {}'.format(allowed_token.access_token))] community_name = 'MyTestCommunity1' community = Community.get(name=community_name) com_admin = create_user('com_admin2', roles=[community.admin_role]) com_admin_token = Token.create_personal('com_admin_token', com_admin.id, scopes=[s[0] for s in scopes]) # application authentication token header com_admin_headers = [ ('Authorization', 'Bearer {}'.format(com_admin_token.access_token)), ('User-Agent', 'Mozilla/5.0 (Windows NT 6.1; WOW64) ' 'AppleWebKit/537.36 (KHTML, like Gecko)' 'Chrome/45.0.2454.101 Safari/537.36') ] publish_headers = [('Content-Type', 'application/json-patch+json'), ('Accept', 'application/json')] + com_admin_headers submit_headers = [('Content-Type', 'application/json-patch+json'), ('Accept', 'application/json')] + allowed_headers stats_headers = [('Content-Type', 'application/json')] test_records_data = [ generate_record_data(community=test_community.name) for idx in range(1, 3) ] for record_data in test_records_data: with app.test_client() as client: login_user(allowed_user, client) record_list_url = (lambda **kwargs: url_for( 'b2share_records_rest.b2rec_list', **kwargs)) headers = [('Content-Type', 'application/json'), ('Accept', 'application/json')] + allowed_headers draft_create_res = client.post(record_list_url(), data=json.dumps(record_data), headers=headers) assert draft_create_res.status_code == 201 draft_create_data = json.loads( draft_create_res.get_data(as_text=True)) uploaded_files = { 'myfile1.html': b'contents1', 'myfile2.html': b'contents2' } for file_key, file_content in uploaded_files.items(): # Test file upload headers = [('Accept', '*/*'), ('Content-Type', 'text/html; charset=utf-8') ] + allowed_headers object_url = '{0}/{1}'.format( draft_create_data['links']['files'], file_key) file_put_res = client.put( object_url, input_stream=BytesIO(file_content), headers=headers) assert file_put_res.status_code == 200 file_put_data = json.loads( file_put_res.get_data(as_text=True)) assert 'created' in file_put_data bucket_id = draft_create_data['links']['files'].split( '/')[-1] # make sure that downloads from deposits are skipped client.get( url_for('invenio_files_rest.object_api', bucket_id=bucket_id, key=file_key)) assert process_events(['file-download']) == \ [('file-download', (0, 0))] # test draft submit draft_submit_res = client.patch( url_for('b2share_deposit_rest.b2dep_item', pid_value=draft_create_data['id']), data=json.dumps([{ "op": "replace", "path": "/publication_state", "value": PublicationStates.submitted.name }]), headers=submit_headers) assert draft_submit_res.status_code == 200 with app.test_client() as client: login_user(com_admin, client) # test draft publish draft_publish_res = client.patch( url_for('b2share_deposit_rest.b2dep_item', pid_value=draft_create_data['id']), data=json.dumps([{ "op": "replace", "path": "/publication_state", "value": PublicationStates.published.name }]), headers=publish_headers) assert draft_publish_res.status_code == 200 draft_publish_data = json.loads( draft_publish_res.get_data(as_text=True)) # Test record GET record_get_res = client.get(url_for( 'b2share_records_rest.b2rec_item', pid_value=draft_publish_data['id']), headers=headers) assert record_get_res.status_code == 200 record_get_data = json.loads( record_get_res.get_data(as_text=True)) # make sure that templates are in the ES list(current_search.put_templates()) # test that a record is accessible through the rest api file1 = record_get_data['files'][0] # download once client.get(url_for('invenio_files_rest.object_api', bucket_id=file1['bucket'], key=file1['key']), headers=com_admin_headers) # make sure that the queue contains the event assert list( current_queues.queues['stats-file-download'].consume()) # download again client.get(url_for('invenio_files_rest.object_api', bucket_id=file1['bucket'], key=file1['key']), headers=com_admin_headers) process_events(['file-download']) current_search_client.indices.refresh('*') # make sure that new index for events is created in ES current_search_client.indices.exists( index='events-stats-file-download') aggregate_events(['file-download-agg']) current_search_client.indices.refresh('*') # make sure that new aggregation index is created in ES current_search_client.indices.exists( index='stats-file-download') stats_ret = client.post(url_for('invenio_stats.stat_query'), data=json.dumps({ 'mystat': { 'stat': 'bucket-file-download-total', 'params': { 'start_date': '2017-01-01', 'bucket_id': file1['bucket'], } } }), headers=stats_headers) stats_ret_data = json.loads(stats_ret.get_data(as_text=True)) assert stats_ret_data['mystat']['buckets'][0]['value'] == 1.0