def test_access_component_valid(minimal_record, parent, identity_simple,
                                users):
    record = RDMRecord.create(minimal_record, parent=parent)
    component = AccessComponent(RDMRecordService())
    component.create(identity_simple, minimal_record, record)

    assert len(record.parent.access.owners) > 0
Exemple #2
0
def required_external_pids_cmp():
    service = RDMRecordService(config=TestServiceConfigRequiredExternalPID,
                               pids_service=PIDsService(
                                   config=TestServiceConfigRequiredExternalPID,
                                   manager_cls=PIDManager))
    c = PIDsComponent(service=service)
    c.uow = UnitOfWork()
    return c
Exemple #3
0
def no_pids_cmp():
    service = RDMRecordService(config=TestServiceConfigNoPIDs,
                               pids_service=PIDsService(
                                   config=TestServiceConfigNoPIDs,
                                   manager_cls=PIDManager))
    c = PIDsComponent(service=service)
    c.uow = UnitOfWork()
    return c
def test_access_component_unknown_owner(minimal_record, parent,
                                        identity_simple, users):
    record = RDMRecord.create(minimal_record, parent=parent)
    record.parent["access"]["owned_by"] = [{"user": -1337}]
    component = AccessComponent(RDMRecordService())

    # both should work, since 'access.owned_by' is ignored for anybody
    # other than system processes
    component.create(identity_simple, minimal_record, record)
    component.update_draft(identity_simple, minimal_record, record)
def test_access_component_unknown_owner_with_system_process(
        minimal_record, parent, users):
    record = RDMRecord.create(minimal_record, parent=parent)
    record.parent["access"]["owned_by"] = [{"user": -1337}]
    component = AccessComponent(RDMRecordService())

    with pytest.raises(ValidationError):
        component.create(system_identity, minimal_record, record)

    with pytest.raises(ValidationError):
        component.update_draft(system_identity, minimal_record, record)
def test_access_component_set_owner(minimal_record, parent, users):
    user = users[0]
    identity = Identity(user.id)
    identity.provides.add(UserNeed(user.id))

    record = RDMRecord.create(minimal_record, parent=parent)
    record.parent["access"]["owned_by"] = []
    component = AccessComponent(RDMRecordService())
    component.create(identity, minimal_record, record)

    assert len(record.access.owners) == 1
    assert list(record.access.owners)[0].resolve() == user
def test_edtf_dumper_query(app, db, location, minimal_record, users):
    """Test edft extension queries."""
    identity = Identity(users[0].id)
    identity.provides.add(UserNeed(users[0].id))
    identity.provides.add(SystemRoleNeed('any_user'))
    identity.provides.add(SystemRoleNeed('authenticated_user'))
    login_user(users[0], remember=True)

    date = "2021-01-01"
    minimal_record["metadata"]["publication_date"] = date
    minimal_record["metadata"]["dates"] = [{"date": date}]

    # Create the record
    service = RDMRecordService(
        config=app.config.get(RDMRecordService.config_name),
    )
    record = service.create(identity, minimal_record)
    RDMDraft.index.refresh()

    # Search for it
    assert service.search_drafts(
        identity,
        {"q": "metadata.publication_date_range:[2020 TO 2021]"},
    ).total == 1

    assert service.search_drafts(
        identity,
        {"q": "metadata.publication_date_range:[2020-12-31 TO 2021-01-02]"},
    ).total == 1

    assert service.search_drafts(
        identity,
        {"q": "metadata.publication_date_range:[2022 TO 2023]"},
    ).total == 0
    def init_services(self, app):
        """Initialize services."""

        from invenio_rdm_records.services import RDMRecordService

        service_config = GeoRecordServiceConfig
        service_config.permission_policy_cls = obj_or_import_string(
            app.config.get("RECORDS_PERMISSIONS_RECORD_POLICY"),
            default=GeoRecordPermissionPolicy)

        self.records_service = RDMRecordService(
            config=self._filter_record_service_config(app, service_config),
            files_service=FileService(GeoRecordFilesServiceConfig),
            draft_files_service=FileService(GeoDraftFilesServiceConfig))
def test_access_component_unknown_grant_subject(minimal_record, parent,
                                                identity_simple, users):
    record = RDMRecord.create(minimal_record, parent=parent)
    record.parent["access"]["grants"] = [{
        "subject": "user",
        "id": "-1337",
        "level": "view"
    }]
    component = AccessComponent(RDMRecordService())

    with pytest.raises(ValidationError):
        component.create(identity_simple, minimal_record, record)

    with pytest.raises(ValidationError):
        component.update_draft(identity_simple, minimal_record, record)
def test_metadata_component(minimal_record, parent, identity_simple, location):
    """Test the metadata component."""
    record = RDMRecord.create(minimal_record, parent=parent)
    draft = RDMDraft.new_version(record)

    assert 'publication_date' in record.metadata
    assert 'title' in record.metadata

    component = MetadataComponent(RDMRecordService())
    component.new_version(identity_simple, draft=draft, record=record)

    # Make sure publication_date was NOT copied, but that title WAS copied
    assert 'publication_date' not in draft.metadata
    assert 'title' in draft.metadata

    # make sure the reference management is correct
    assert 'publication_date' in record.metadata
def test_access_component_update_access_via_json(minimal_record, parent, users,
                                                 identity_simple):
    next_year = arrow.utcnow().datetime + timedelta(days=+365)
    restricted_access = {
        "record": "restricted",
        "files": "restricted",
        "embargo": {
            "until": next_year.strftime("%Y-%m-%d"),
            "active": True,
            "reason": "nothing in particular",
        },
    }
    minimal_record["access"] = restricted_access

    # create an initially restricted-access record
    record = RDMRecord.create(minimal_record, parent=parent)
    component = AccessComponent(RDMRecordService())
    component.create(identity_simple, minimal_record, record)

    prot = record.access.protection
    assert record.access.embargo is not None
    assert "embargo" in record["access"]
    assert prot.record == record["access"]["record"] == "restricted"
    assert prot.files == record["access"]["files"] == "restricted"

    # update the record's access via new json data
    # (instead of via the record's access object)
    new_data = minimal_record.copy()
    public_access = {
        "grants": [],
        "record": "public",
        "files": "public",
    }
    new_data["access"] = public_access
    component.create(identity_simple, new_data, record)

    prot = record.access.protection
    assert not record.access.embargo
    assert "embargo" not in record["access"]
    assert prot.record == record["access"]["record"] == "public"
    assert prot.files == record["access"]["files"] == "public"
Exemple #12
0
def ui_blueprint(app):
    """Dynamically registers routes (allows us to rely on config)."""
    blueprint = Blueprint(
        'invenio_app_rdm',
        __name__,
        template_folder='templates',
        static_folder='static',
    )

    service = RDMRecordService()

    @blueprint.before_app_first_request
    def init_menu():
        """Initialize menu before first request."""
        item = current_menu.submenu('main.deposit')
        item.register('invenio_app_rdm.deposits_user', 'Uploads', order=1)

    search_url = app.config.get('RDM_RECORDS_UI_SEARCH_URL', '/search')

    @blueprint.route(search_url)
    def search():
        """Search page."""
        return render_template(current_app.config['SEARCH_BASE_TEMPLATE'])

    @blueprint.route(app.config.get('RDM_RECORDS_UI_NEW_URL', '/uploads/new'))
    def deposits_create():
        """Record creation page."""
        forms_config = dict(createUrl=("/api/records"),
                            vocabularies=Vocabularies.dump(),
                            current_locale=str(current_i18n.locale))
        return render_template(
            current_app.config['DEPOSITS_FORMS_BASE_TEMPLATE'],
            forms_config=forms_config,
            record=dump_empty(RDMRecordSchema),
            files=dict(default_preview=None,
                       enabled=True,
                       entries=[],
                       links={}),
            searchbar_config=dict(searchUrl=search_url))

    @blueprint.route(
        app.config.get('RDM_RECORDS_UI_EDIT_URL', '/uploads/<pid_value>'))
    def deposits_edit(pid_value):
        """Deposit edit page."""
        links_config = RDMDraftResourceConfig.links_config
        draft = service.read_draft(id_=pid_value,
                                   identity=g.identity,
                                   links_config=links_config)

        files_service = RDMDraftFilesService()
        files_list = files_service.list_files(
            id_=pid_value,
            identity=g.identity,
            links_config=RDMDraftFilesResourceConfig.links_config)

        forms_config = dict(apiUrl=f"/api/records/{pid_value}/draft",
                            vocabularies=Vocabularies.dump(),
                            current_locale=str(current_i18n.locale))

        # Dereference relations (languages, licenses, etc.)
        draft._record.relations.dereference()
        serializer = UIJSONSerializer()
        record = serializer.serialize_object_to_dict(draft.to_dict())

        # TODO: get the `is_published` field when reading the draft
        from invenio_pidstore.errors import PIDUnregistered
        try:
            service.draft_cls.pid.resolve(pid_value, registered_only=True)
            record["is_published"] = True
        except PIDUnregistered:
            record["is_published"] = False

        return render_template(
            current_app.config['DEPOSITS_FORMS_BASE_TEMPLATE'],
            forms_config=forms_config,
            record=record,
            files=files_list.to_dict(),
            searchbar_config=dict(searchUrl=search_url))

    @blueprint.route(
        app.config.get('RDM_RECORDS_UI_SEARCH_USER_URL', '/uploads'))
    def deposits_user():
        """List of user deposits page."""
        return render_template(current_app.config['DEPOSITS_UPLOADS_TEMPLATE'],
                               searchbar_config=dict(searchUrl=search_url))

    @blueprint.route('/coming-soon')
    def coming_soon():
        """Route to display on soon-to-come features."""
        return render_template('invenio_app_rdm/coming_soon_page.html')

    @blueprint.app_template_filter()
    def make_files_preview_compatible(files):
        """Processes a list of RecordFiles to a list of FileObjects.

        This is needed to make the objects compatible with invenio-previewer.
        """
        file_objects = []
        for file in files:
            file_objects.append(
                FileObject(obj=files[file].object_version, data={}).dumps())
        return file_objects

    @blueprint.app_template_filter()
    def select_preview_file(files, default_preview=None):
        """Get list of files and select one for preview."""
        selected = None

        try:
            for f in sorted(files or [], key=itemgetter('key')):
                file_type = splitext(f['key'])[1][1:].lower()
                if is_previewable(file_type):
                    if selected is None:
                        selected = f
                    elif f['key'] == default_preview:
                        selected = f
        except KeyError:
            pass
        return selected

    @blueprint.app_template_filter()
    def to_previewer_files(record):
        """Get previewer-compatible files list."""
        return [
            FileObject(obj=f.object_version, data=f.metadata or {})
            for f in record.files.values()
        ]

    @blueprint.app_template_filter('can_list_files')
    def can_list_files(record):
        """Permission check if current user can list files of record.

        The current_user is used under the hood by flask-principal.

        Once we move to Single-Page-App approach, we likely want to enforce
        permissions at the final serialization level (only).
        """
        PermissionPolicy = get_record_permission_policy()
        return PermissionPolicy(action='read_files', record=record).can()

    @blueprint.app_template_filter('pid_url')
    def pid_url(identifier, scheme=None, url_scheme='https'):
        """Convert persistent identifier into a link."""
        if scheme is None:
            try:
                scheme = idutils.detect_identifier_schemes(identifier)[0]
            except IndexError:
                scheme = None
        try:
            if scheme and identifier:
                return idutils.to_url(identifier,
                                      scheme,
                                      url_scheme=url_scheme)
        except Exception:
            current_app.logger.warning(
                f"URL generation for identifier {identifier} failed.",
                exc_info=True)
        return ''

    @blueprint.app_template_filter('doi_identifier')
    def doi_identifier(identifiers):
        """Extract DOI from sequence of identifiers."""
        for identifier in identifiers:
            # TODO: extract this "DOI" constant to a registry?
            if identifier == 'doi':
                return identifiers[identifier]

    @blueprint.app_template_filter('vocabulary_title')
    def vocabulary_title(dict_key, vocabulary_key, alt_key=None):
        """Returns formatted vocabulary-corresponding human-readable string.

        In some cases the dict needs to be reconstructed. `alt_key` will be the
        key while `dict_key` will become the value.
        """
        if alt_key:
            dict_key = {alt_key: dict_key}
        vocabulary = Vocabularies.get_vocabulary(vocabulary_key)
        return vocabulary.get_title_by_dict(dict_key) if vocabulary else ""

    @blueprint.app_template_filter('dereference_record')
    def dereference_record(record):
        """Returns the UI serialization of a record."""
        record.relations.dereference()

        return record

    @blueprint.app_template_filter('serialize_ui')
    def serialize_ui(record):
        """Returns the UI serialization of a record."""
        serializer = UIJSONSerializer()
        # We need a dict not a string
        return serializer.serialize_object_to_dict(record)

    return blueprint
Exemple #13
0
def not_req_unmanaged_pid_cmp():
    service = RDMRecordService(config=TestServiceNReqUnmanConfig())

    return ExternalPIDsComponent(service=service)
Exemple #14
0
def req_managed_pid_cmp():
    service = RDMRecordService(config=TestServiceReqManConfig())

    return ExternalPIDsComponent(service=service)
Exemple #15
0
def test_grant_tokens_dumper_query(app, db, minimal_record, users,
                                   identity_simple, location):
    """Test grant token dumper extension queries."""
    id1 = str(users[0].id)
    id2 = str(users[1].id)
    grant1 = Grant.from_dict({"subject": "user", "id": id1, "level": "view"})
    grant2 = Grant.from_dict({"subject": "user", "id": id2, "level": "manage"})
    grant3 = Grant.from_dict({
        "subject": "user",
        "id": id1,
        "level": "viewfull"
    })

    minimal_record["access"]["grants"] = [
        grant1.to_dict(),
        grant2.to_dict(),
    ]

    # Create the record
    service = RDMRecordService(config=app.config.get(
        RDMRecordService.config_name), )

    service.create(identity_simple, minimal_record)

    minimal_record["access"]["grants"] = [
        grant2.to_dict(),
    ]

    service.create(identity_simple, minimal_record)

    RDMDraft.index.refresh()

    # Search for it
    assert (service.search(
        identity_simple,
        {
            "q": "access.grant_tokens:{}".format(grant1.to_token())
        },
        status="draft",
    ).total == 1)

    assert (service.search(
        identity_simple,
        {
            "q": "access.grant_tokens:{}".format(grant2.to_token())
        },
        status="draft",
    ).total == 2)

    assert (service.search(
        identity_simple,
        {
            "q": "access.grant_tokens:{}".format(grant3.to_token())
        },
        status="draft",
    ).total == 0)

    assert (service.search(
        identity_simple,
        {
            "q": "access.grant_tokens:actually.invalid.token"
        },
        status="draft",
    ).total == 0)
Exemple #16
0
def no_required_pids_service():
    return RDMRecordService(config=TestServiceConfigNoRequiredPIDs,
                            pids_service=PIDsService(
                                config=TestServiceConfigNoRequiredPIDs,
                                manager_cls=PIDManager))