Пример #1
0
def test_cli_full_reindex(app, db, es, capsys, es_acl_prepare, test_users):
    pid, record = create_record(
        {
            '$schema': RECORD_SCHEMA,
            'keywords': ['blah']
        },
        clz=SchemaEnforcingRecord)
    RecordIndexer().index(record)
    current_search_client.indices.flush()
    with db.session.begin_nested():
        acl = ElasticsearchACL(name='test',
                               schemas=[RECORD_SCHEMA],
                               priority=0,
                               operation='get',
                               originator=test_users.u1,
                               record_selector={'term': {
                                   'keywords': 'blah'
                               }})
        db.session.add(acl)
        u = UserActor(name='test',
                      acl=acl,
                      originator=test_users.u1,
                      users=[test_users.u1])
        db.session.add(u)

    # now the record is not indexed and ACL is not in the helper index, check it ...
    retrieved = RecordsSearch(
        index=schema_to_index(RECORD_SCHEMA)[0]).get_record(
            record.id).execute().hits[0].to_dict()
    assert '_invenio_explicit_acls' not in retrieved

    # just a precaution test
    assert current_explicit_acls.enabled_schemas == {RECORD_SCHEMA}

    # and run the reindex - should reindex one record
    from invenio_explicit_acls.cli import full_reindex_impl
    full_reindex_impl(verbose=True, records=True, in_bulk=False)

    captured = capsys.readouterr()
    assert captured.out.strip() == """
Reindexing ACLs
Updating ACL representation for "test" (%s) on schemas ['records/record-v1.0.0.json']
Getting records for schema records/record-v1.0.0.json
   ... collected 1 records
Adding 1 records to indexing queue""".strip() % (acl.id)

    current_search_client.indices.flush()

    retrieved = RecordsSearch(
        index=schema_to_index(RECORD_SCHEMA)[0]).get_record(
            record.id).execute().hits[0].to_dict()
    assert clear_timestamp(retrieved['_invenio_explicit_acls']) == [{
        'id':
        str(acl.id),
        'operation':
        'get',
        'timestamp':
        'cleared',
        'user': [1]
    }]
Пример #2
0
def acl_deleted_reindex(schemas, acl_id):
    """
    ACL has been deleted so reindex all the documents that contain reference to it.

    :param index: the index of documents
    :param record_acl_id:   if of the ACL instance that has been deleted
    """
    logger.info('Reindexing started for deleted ACL=%s', acl_id)

    acl = ACL.query.filter_by(id=acl_id).one_or_none()

    if acl:  # pragma no cover
        raise AttributeError(
            'ACL with id %s is still in the database, '
            'please remove it before calling current_explicit_acls.reindex_acl_removed'
            % acl_id)

    indexer = RecordIndexer()

    query = {
        "nested": {
            "path": "_invenio_explicit_acls",
            "query": {
                "term": {
                    "_invenio_explicit_acls.id": acl_id
                }
            }
        }
    }
    removed_count = 0
    for schema in schemas:
        current_search_client.indices.refresh(index=schema_to_index(schema)[0])
        current_search_client.indices.flush(index=schema_to_index(schema)[0])
        try:
            index, doc_type = schema_to_index(schema)

            for doc in elasticsearch.helpers.scan(current_search_client,
                                                  query={
                                                      "query": query,
                                                      "_source": False,
                                                  },
                                                  index=index,
                                                  **add_doc_type(doc_type)):
                try:
                    indexer.index(Record.get_record(doc['_id']))
                    removed_count += 1
                except NoResultFound:  # pragma no cover
                    continue
                except:  # pragma no cover
                    logger.exception(
                        'Unexpected exception in record reindexing')
                    continue
        except:  # pragma no cover
            logger.exception('Error removing ACL from schema %s', schema)

    logger.info(
        'Reindexing finished for deleted ACL=%s, acl removed from %s records',
        acl_id, removed_count)
Пример #3
0
def test_propertyvalue_acl_prepare_schema_acl(app, db, es, es_acl_prepare,
                                              test_users):
    # should pass as it does nothing
    PropertyValueACL.prepare_schema_acls(RECORD_SCHEMA)

    idx = PropertyValueACL.get_acl_index_name(
        schema_to_index(RECORD_SCHEMA)[0])
    mapping = current_search_client.indices.get_mapping(idx)
    assert idx in mapping

    idx, doc_type = schema_to_index(RECORD_SCHEMA)
    mapping = current_search_client.indices.get_mapping(idx)
    assert '_invenio_explicit_acls' in mapping[idx]['mappings'][doc_type][
        'properties']
Пример #4
0
def test_create_record_check_acl_priority(app, db, es, es_acl_prepare,
                                          test_users):
    with app.test_client() as client:
        with db.session.begin_nested():
            acl1 = DefaultACL(name='default',
                              schemas=[RECORD_SCHEMA],
                              priority=0,
                              originator=test_users.u1,
                              operation='get')
            actor1 = SystemRoleActor(name='auth',
                                     system_role='any_user',
                                     acl=acl1,
                                     originator=test_users.u1)

            acl2 = DefaultACL(name='default',
                              schemas=[RECORD_SCHEMA],
                              priority=1,
                              originator=test_users.u1,
                              operation='get')
            actor2 = SystemRoleActor(name='auth',
                                     system_role='authenticated_user',
                                     acl=acl2,
                                     originator=test_users.u1)

            db.session.add(acl1)
            db.session.add(actor1)
            db.session.add(acl2)
            db.session.add(actor2)

        login(client, test_users.u1)
        response = client.post(records_url(),
                               data=json.dumps({
                                   'title': 'blah',
                                   'contributors': []
                               }),
                               content_type='application/json')
        assert response.status_code == 201
        rest_metadata = get_json(response)['metadata']
        assert 'control_number' in rest_metadata

        index, doctype = schema_to_index(RECORD_SCHEMA)

        rec_md = current_search_client.get(
            index=index,
            doc_type=doctype,
            id=str(
                PersistentIdentifier.get(
                    'recid', rest_metadata['control_number']).object_uuid))

        clear_timestamp(rec_md)

        assert rec_md['_source']['_invenio_explicit_acls'] == [{
            'operation':
            'get',
            'id':
            acl2.id,
            'timestamp':
            'cleared',
            'system_role': ['authenticated_user']
        }]
Пример #5
0
def acl_changed_reindex(acl_id):
    """
    ACL has been changed so reindex all the documents in the given index.

    :param acl_id:   id of ACL instance
    """
    logger.info('Reindexing started for ACL=%s', acl_id)

    timestamp = datetime.datetime.now().astimezone().isoformat()

    acl = ACL.query.filter_by(id=acl_id).one_or_none()

    if not acl:
        # deleted in the meanwhile, so just return
        return  # pragma no cover

    # make sure all indices are flushed so that no resource is obsolete in index
    for schema in acl.schemas:
        current_search_client.indices.flush(index=schema_to_index(schema)[0])

    indexer = RecordIndexer()
    updated_count = 0
    removed_count = 0

    for id in acl.get_matching_resources():
        try:
            rec = Record.get_record(id)
        except:  # pragma no cover
            # record removed in the meanwhile by another thread/process,
            # indexer should have been called to remove it from ES
            # won't test this so pragma no cover
            continue
        try:
            indexer.index(rec)
            updated_count += 1
        except Exception as e:  # pragma no cover
            logger.exception('Error indexing ACL for resource %s: %s', id, e)

    # reindex the resources those were indexed by this acl but no longer should be
    for id in acl.used_in_records(older_than_timestamp=timestamp):
        try:
            rec = Record.get_record(id)
        except NoResultFound:  # pragma no cover
            continue
        except:  # pragma no cover
            logger.exception('Unexpected exception in record reindexing')
            continue

        try:
            removed_count += 1
            indexer.index(rec)
        except:  # pragma no cover
            logger.exception('Error indexing ACL for obsolete resource %s', id)

    logger.info(
        'Reindexing finished for ACL=%s, acl applied to %s records, acl removed from %s records',
        acl_id, updated_count, removed_count)
Пример #6
0
def test_elasticsearch_acl_prepare_schema_acl(app, db, es, es_acl_prepare,
                                              test_users):
    # should pass as it does nothing
    ElasticsearchACL.prepare_schema_acls(RECORD_SCHEMA)

    idx = ElasticsearchACL.get_acl_index_name(
        schema_to_index(RECORD_SCHEMA)[0])
    mapping = current_search_client.indices.get_mapping(idx)
    assert idx in mapping

    idx, doc_type = schema_to_index(RECORD_SCHEMA)
    mapping = current_search_client.indices.get_mapping(idx)
    assert len(mapping) == 1
    key = list(mapping.keys())[0]
    if ES_VERSION[0] < 7:
        assert '_invenio_explicit_acls' in mapping[key]['mappings'][doc_type][
            'properties']
    else:
        assert '_invenio_explicit_acls' in mapping[key]['mappings'][
            'properties']
Пример #7
0
    def prepare_schema_acls(self, schema):
        """
        Prepare ACLs for the given index.

        :param schema: schema for which to prepare the ACLs
        """
        index_name, doc_type = schema_to_index(schema)

        # create a new index where percolate queries will be stored
        acl_index_name = self.get_acl_index_name(index_name)
        # create mapping for acl index

        target_mapping_resource = current_search.mappings[index_name]
        with open(target_mapping_resource) as f:
            mapping = json.load(f)

        acl_doctype_name = current_explicit_acls.acl_doctype_name

        if 'properties' not in mapping['mappings']:
            # ES6-style mapping file
            fk = next(iter(mapping['mappings'].keys()))
            if acl_doctype_name != fk:
                mapping['mappings'][acl_doctype_name] = mapping['mappings'][fk]
                del mapping['mappings'][fk]


            mapping['mappings'][acl_doctype_name]['properties'] = {
                **mapping['mappings'][acl_doctype_name]['properties'],
                "__acl_record_selector": {
                    "type": "percolator"
                },
                "__acl_record_type": {
                    "type": "keyword"
                }
            }
        else:
            # ES7 mapping file
            mapping['mappings']['properties'] = {
                **mapping['mappings']['properties'],
                "__acl_record_selector": {
                    "type": "percolator"
                },
                "__acl_record_type": {
                    "type": "keyword"
                }
            }
        try:
            current_search_client.indices.create(index=acl_index_name, body=mapping)
        except Exception as e:
            logger.error('Error in creating index for ACLs: %s', e)
Пример #8
0
def test_propertyvalue_acl_delete(app, db, es, es_acl_prepare, test_users):
    # should pass as it does nothing
    with db.session.begin_nested():
        acl = PropertyValueACL(name='test',
                               schemas=[RECORD_SCHEMA],
                               priority=0,
                               operation='get',
                               originator=test_users.u1)
        propval = PropertyValue(name='keywords',
                                value='test',
                                acl=acl,
                                originator=test_users.u1)

        db.session.add(acl)
        db.session.add(propval)

    acl.update()
    idx = acl.get_acl_index_name(schema_to_index(RECORD_SCHEMA)[0])
    acl_md = current_search_client.get(
        index=idx,
        doc_type=current_app.config['INVENIO_EXPLICIT_ACLS_DOCTYPE_NAME'],
        id=acl.id)
    # ES7 returns extra:
    acl_md.pop('_seq_no', None)
    acl_md.pop('_primary_term', None)
    assert acl_md == {
        '_id': acl.id,
        '_index': 'invenio_explicit_acls-acl-v1.0.0-records-record-v1.0.0',
        '_source': {
            '__acl_record_selector': {
                'bool': {
                    'must': [{
                        'term': {
                            'keywords': 'test'
                        }
                    }]
                }
            },
            '__acl_record_type': 'propertyvalue'
        },
        '_type': '_doc',
        '_version': 1,
        'found': True
    }
    acl.delete()
    with pytest.raises(elasticsearch.exceptions.NotFoundError):
        current_search_client.get(
            index=idx,
            doc_type=current_app.config['INVENIO_EXPLICIT_ACLS_DOCTYPE_NAME'],
            id=acl.id)
Пример #9
0
 def delete(self):
     """Delete acl from any internal representation / index for the acl."""
     for schema in self.schemas:
         acl_index_name = self.get_acl_index_name(schema_to_index(schema)[0])
         try:
             return current_search_client.delete(
                 index=acl_index_name,
                 **add_doc_type(current_app.config['INVENIO_EXPLICIT_ACLS_DOCTYPE_NAME']),
                 id=self.id,
                 refresh='wait_for'
             )
         except:  # pragma: no cover
             logger.exception('Strange, the ACL has not been indexed: %s', repr(self))
         finally:
             current_search_client.indices.flush(index=acl_index_name)
Пример #10
0
def test_aclserializer(app, db, es, es_acl_prepare, test_users):
    with db.session.begin_nested():
        acl1 = DefaultACL(name='default',
                          schemas=[RECORD_SCHEMA],
                          priority=0,
                          originator=test_users.u1,
                          operation='get')
        actor1 = UserActor(name='auth',
                           users=[test_users.u1],
                           acl=acl1,
                           originator=test_users.u1)

        db.session.add(acl1)
        db.session.add(actor1)

    pid, rec = create_record({'title': 'blah'}, clz=SchemaEnforcingRecord)
    RecordIndexer().index(rec)
    current_search_client.indices.flush()

    assert current_jsonschemas.url_to_path(
        rec['$schema']) in current_explicit_acls.enabled_schemas
    assert list(DefaultACL.get_record_acls(rec)) != []

    index, doc_type = schema_to_index(RECORD_SCHEMA)
    data = current_search_client.get(index=index,
                                     doc_type=doc_type,
                                     id=str(pid.object_uuid))['_source']
    assert '_invenio_explicit_acls' in data
    assert len(data['_invenio_explicit_acls']) == 1

    with app.test_request_context():
        login_user(test_users.u1)
        set_identity(test_users.u1)

        acljson_serializer = ACLJSONSerializer(RecordSchemaV1,
                                               acl_rest_endpoint='recid',
                                               replace_refs=True)
        serialized = json.loads(acljson_serializer.serialize(pid, rec))
        assert serialized['invenio_explicit_acls'] == ["get"]

    with app.test_client() as client:
        login(client, test_users.u1)
        search_results = client.get(url_for('invenio_records_rest.recid_list'))
        search_results = get_json(search_results)
        hits = search_results['hits']['hits']
        assert len(hits) == 1
        assert hits[0]['invenio_explicit_acls'] == ["get"]
Пример #11
0
    def get_matching_resources(self) -> Iterable[str]:
        """
        Get resources that match the ACL.

        :param acl: the acl
        :return:   iterable of resource ids
        """
        for schema in self.schemas:
            index, doc_type = schema_to_index(schema)

            for r in current_search_client.search(index=index,
                                                  **add_doc_type(doc_type),
                                                  body={
                                                      "query": {
                                                          "match_all": {}
                                                      },
                                                      "_source": False,
                                                  })['hits']['hits']:
                yield r['_id']
Пример #12
0
    def used_in_records(self, older_than_timestamp=None):
        """
        Returns IDs of all records that reference the ACL in cached acls in elasticsearch.

        :param older_than_timestamp:    only restrict to records where
                                        the cached ACLs are older than the timestamp
        :return:    An iterable of Record IDs
        """
        for schema in self.schemas:
            index, doc_type = schema_to_index(schema)

            query = [{"term": {"_invenio_explicit_acls.id": str(self.id)}}]

            if older_than_timestamp:
                if isinstance(older_than_timestamp, datetime.datetime):
                    older_than_timestamp = older_than_timestamp.isoformat()
                query.append({
                    "range": {
                        "_invenio_explicit_acls.timestamp": {
                            "lt": older_than_timestamp
                        }
                    }
                })

            query = {
                "nested": {
                    "path": "_invenio_explicit_acls",
                    "score_mode": "min",
                    "query": {
                        "bool": {
                            "must": query
                        }
                    }
                }
            }
            for doc in elasticsearch.helpers.scan(current_search_client,
                                                  query={
                                                      "query": query,
                                                      "_source": False,
                                                  },
                                                  index=index,
                                                  **add_doc_type(doc_type)):
                yield doc['_id']
Пример #13
0
    def get_matching_resources(self) -> Iterable[str]:
        """
        Get resources that match the ACL.

        :param acl: the acl
        :return:   iterable of resource ids
        """
        for schema in self.schemas:
            index_name, doc_type = schema_to_index(schema)
            try:
                for doc in elasticsearch.helpers.scan(
                    current_search_client,
                    query={
                        "query": self.record_selector,
                        "_source": False,
                    },
                    index=index_name, **add_doc_type(doc_type)
                ):
                    yield doc['_id']
            except:  # pragma: no cover
                logger.exception('Error getting resources for schema %s', schema)
Пример #14
0
def test_elasticsearch_acl_update(app, db, es, es_acl_prepare, test_users):
    # should pass as it does nothing
    with db.session.begin_nested():
        acl = ElasticsearchACL(name='test',
                               schemas=[RECORD_SCHEMA],
                               priority=0,
                               operation='get',
                               originator=test_users.u1,
                               record_selector={'term': {
                                   'keywords': 'test'
                               }})
        db.session.add(acl)
    acl.update()  # makes version 1
    acl.update()  # makes version 2
    idx = acl.get_acl_index_name(schema_to_index(RECORD_SCHEMA)[0])
    acl_md = current_search_client.get(
        index=idx,
        doc_type=current_app.config['INVENIO_EXPLICIT_ACLS_DOCTYPE_NAME'],
        id=acl.id)
    # ES7 returns extra:
    acl_md.pop('_seq_no', None)
    acl_md.pop('_primary_term', None)

    print(json.dumps(acl_md, indent=4))
    assert acl_md == {
        '_id': acl.id,
        '_index': 'invenio_explicit_acls-acl-v1.0.0-records-record-v1.0.0',
        '_source': {
            '__acl_record_selector': {
                'term': {
                    'keywords': 'test'
                }
            },
            '__acl_record_type': 'elasticsearch'
        },
        '_type': '_doc',
        '_version': 2,
        'found': True
    }
Пример #15
0
 def update(self):
     """Update any internal representation / index for the acl."""
     body = {
         '__acl_record_selector': self.record_selector,
         '__acl_record_type': self.type
     }
     if logger.isEnabledFor(logging.DEBUG) <= logging.DEBUG:
         logger.debug('get_material_acls: query %s', json.dumps(body, indent=4, ensure_ascii=False))
     schema_indices = [schema_to_index(x)[0] for x in self.schemas]
     acl_index_names = [self.get_acl_index_name(x) for x in schema_indices]
     for acl_idx_name in acl_index_names:
         try:
             resp = current_search_client.index(
                 index=acl_idx_name,
                 **add_doc_type(current_app.config['INVENIO_EXPLICIT_ACLS_DOCTYPE_NAME']),
                 id=self.id,
                 body=body,
                 refresh='wait_for'
             )
             assert resp['result'] in ('created', 'updated')
         finally:
             current_search_client.indices.flush(index=acl_idx_name)
Пример #16
0
    def prepare(self, schema):
        """
        Add ACL support for a given schema.

        This call goes through all registered ACL types
        and lets them do whatever preparation is needed (for example, creating extra indices
        in elasticsearch).

        Then it adds _invenio_explicit_acls field to the elasticsearch index for the given schema
        that wil store preprocessed explicit ACLs for each record

        :param schema:  schema URL of the schema that should be patched with ACL support
        """
        # let each acl model to prepare the schema if needed
        for model in self.acl_models:
            model.prepare_schema_acls(schema)

        # add an extra column containing preprocessed ACLs to the prepared schema
        index_name, doc_type = schema_to_index(schema)

        # print(json.dumps(self._extra_mapping, indent=4))
        current_search_client.indices.put_mapping(index=index_name, doc_type=doc_type, body=self._extra_mapping)
Пример #17
0
def get_from_es(pid, schema='records/record-v1.0.0.json'):
    """Retrieves a record from elasticsearch."""
    index, doctype = schema_to_index(schema)
    return current_search_client.get(index=index,
                                     doc_type=doctype,
                                     id=pid.object_uuid)
Пример #18
0
def test_aclrecordsearch_returnall(app, db, es, es_acl_prepare, test_users):
    with db.session.begin_nested():
        acl1 = ElasticsearchACL(name='test', schemas=[RECORD_SCHEMA],
                                priority=0, operation='get', originator=test_users.u1,
                                record_selector={'term': {
                                    'keywords': 'test'
                                }})
        actor1 = SystemRoleActor(name='auth', system_role='any_user', acl=acl1, originator=test_users.u1)
        db.session.add(acl1)
        db.session.add(actor1)

    current_explicit_acls.reindex_acl(acl1, delayed=False)

    with app.test_client() as client:
        login(client, test_users.u1)
        response = client.post(records_url(),
                               data=json.dumps({'title': 'blah', 'contributors': [], 'keywords': ['blah']}),
                               content_type='application/json')
        assert response.status_code == 201
        rest_metadata = get_json(response)['metadata']
        assert 'control_number' in rest_metadata

    # make sure indices are flushed
    current_search_client.indices.refresh()
    current_search_client.indices.flush()

    index, doc_type = schema_to_index(RECORD_SCHEMA)

    record_uuid = PersistentIdentifier.get('recid', rest_metadata['control_number']).object_uuid
    with app.test_request_context():
        login_user(test_users.u1)
        set_identity(test_users.u1)
        assert current_user == test_users.u1

        # acl1 does not apply to the resource so the search must return no data
        assert not len(
            ACLRecordsSearch(index=index, doc_type=doc_type, operation='get').get_record(record_uuid).execute())

        # when acl_return_all is specified, return all matching records regardless of ACL
        with_all = ACLRecordsSearch(index=index, doc_type=doc_type).acl_return_all().get_record(
            record_uuid).execute().hits
        assert len(with_all) == 1
        assert with_all[0]['_invenio_explicit_acls'] == []

    # add another acl, this one maps to the record
    with db.session.begin_nested():
        acl2 = ElasticsearchACL(name='test', schemas=[RECORD_SCHEMA],
                                priority=0, operation='get', originator=test_users.u1,
                                record_selector={'term': {
                                    'keywords': 'blah'
                                }})
        actor2 = UserActor(name='u2', users=[test_users.u2], acl=acl2, originator=test_users.u1)
        db.session.add(acl2)
        db.session.add(actor2)

    current_explicit_acls.reindex_acl(acl2, delayed=False)

    # make sure indices are flushed
    current_search_client.indices.refresh()
    current_search_client.indices.flush()

    # for the same user acl_return_all() must return the record and effective acls
    with app.test_request_context():
        login_user(test_users.u1)
        set_identity(test_users.u1)

        # when acl_return_all is specified, return all matching records regardless of ACL
        with_all = ACLRecordsSearch(index=index, doc_type=doc_type).acl_return_all().get_record(
            record_uuid).execute().hits
        assert len(with_all) == 1
        assert clear_timestamp(with_all[0].to_dict()['_invenio_explicit_acls']) == [
            {
                'operation': 'get',
                'id': acl2.id,
                'timestamp': 'cleared',
                'user': [2]
            }
        ]

    # for user2 plain ACLRecordsSearch must return the record and effective acls
    with app.test_request_context():
        login_user(test_users.u2)
        set_identity(test_users.u2)
        # when acl_return_all is specified, return all matching records regardless of ACL
        with_all = ACLRecordsSearch(index=index, doc_type=doc_type).get_record(record_uuid).execute().hits
        assert len(with_all) == 1
        assert clear_timestamp(with_all[0].to_dict()['_invenio_explicit_acls']) == [
            {
                'operation': 'get',
                'id': acl2.id,
                'timestamp': 'cleared',
                'user': [2]
            }
        ]
def test_change_acl_mapping(app, db, es, es_acl_prepare, test_users):
    pid, record = create_record({'$schema': RECORD_SCHEMA, 'keywords': ['blah']}, clz=SchemaEnforcingRecord)
    pid1, record1 = create_record({'$schema': RECORD_SCHEMA, 'keywords': ['test']}, clz=SchemaEnforcingRecord)
    RecordIndexer().index(record)
    RecordIndexer().index(record1)
    current_search_client.indices.flush()

    with db.session.begin_nested():
        acl = ElasticsearchACL(name='test', schemas=[RECORD_SCHEMA],
                               priority=0, operation='get', originator=test_users.u1,
                               record_selector={'term': {
                                   'keywords': 'blah'
                               }})
        actor = UserActor(users=[test_users.u1], acl=acl, originator=test_users.u1)
        db.session.add(acl)
        db.session.add(actor)

    current_explicit_acls.reindex_acl(acl, delayed=False)
    current_search_client.indices.flush()

    index, doc_type = schema_to_index(RECORD_SCHEMA)

    hits = current_search_client.search(
        index=index,
        doc_type=doc_type,
        body={
            'query': {
                'nested': {
                    'path': '_invenio_explicit_acls',
                    'query': {
                        'term': {
                            '_invenio_explicit_acls.id': str(acl.id)
                        }
                    }
                }
            },
            '_source': False
        }
    )['hits']['hits']

    assert len(hits) == 1
    assert hits[0]['_id'] == str(pid.object_uuid)

    with db.session.begin_nested():
        acl.record_selector = {
            'term': {
                'keywords': 'test'
            }
        }
        db.session.add(acl)

    current_explicit_acls.reindex_acl(acl, delayed=False)
    current_search_client.indices.flush()

    hits = current_search_client.search(
        index=index,
        doc_type=doc_type,
        body={
            'query': {
                'nested': {
                    'path': '_invenio_explicit_acls',
                    'query': {
                        'term': {
                            '_invenio_explicit_acls.id': str(acl.id)
                        }
                    }
                }
            },
            '_source': False
        }
    )['hits']['hits']

    assert len(hits) == 1
    assert hits[0]['_id'] == str(pid1.object_uuid)
Пример #20
0
def test_used_in_records(app, db, es, es_acl_prepare, test_users):
    with db.session.begin_nested():
        acl1 = ElasticsearchACL(name='test',
                                schemas=[RECORD_SCHEMA],
                                priority=0,
                                operation='get',
                                originator=test_users.u1,
                                record_selector={'term': {
                                    'keywords': 'blah'
                                }})
        actor1 = SystemRoleActor(name='auth',
                                 acl=acl1,
                                 originator=test_users.u1,
                                 system_role='authenticated_user')
        db.session.add(acl1)
        db.session.add(actor1)

        acl2 = ElasticsearchACL(name='test',
                                schemas=[RECORD_SCHEMA],
                                priority=0,
                                operation='get',
                                originator=test_users.u1,
                                record_selector={'term': {
                                    'keywords': 'test'
                                }})
        actor2 = SystemRoleActor(name='noauth',
                                 acl=acl2,
                                 originator=test_users.u1,
                                 system_role='any_user')
        db.session.add(actor2)
        db.session.add(acl2)

    acl1.update()
    acl2.update()

    pid1, record1 = create_record(
        {
            '$schema': RECORD_SCHEMA,
            'keywords': ['blah']
        },
        clz=SchemaEnforcingRecord)
    pid2, record2 = create_record(
        {
            '$schema': RECORD_SCHEMA,
            'keywords': ['test']
        },
        clz=SchemaEnforcingRecord)

    ts1 = datetime.datetime.now(datetime.timezone.utc)
    time.sleep(0.1)
    RecordIndexer().index(record1)
    current_search_client.indices.refresh()
    current_search_client.indices.flush()

    time.sleep(1)
    ts2 = datetime.datetime.now(datetime.timezone.utc)
    time.sleep(0.1)
    RecordIndexer().index(record2)
    current_search_client.indices.refresh()
    current_search_client.indices.flush()

    time.sleep(1)
    ts3 = datetime.datetime.now(datetime.timezone.utc)

    # the records should have cached ACLs, let's check
    idx, doc_type = schema_to_index(RECORD_SCHEMA)
    assert clear_timestamp(
        current_search_client.get(
            index=idx, doc_type=doc_type,
            id=str(record1.id))['_source']['_invenio_explicit_acls']) == [{
                'operation':
                'get',
                'id':
                acl1.id,
                'timestamp':
                'cleared',
                'system_role': ['authenticated_user']
            }]

    assert clear_timestamp(
        current_search_client.get(
            index=idx, doc_type=doc_type,
            id=str(record2.id))['_source']['_invenio_explicit_acls']) == [{
                'operation':
                'get',
                'id':
                acl2.id,
                'timestamp':
                'cleared',
                'system_role': ['any_user']
            }]

    # there should be no resource for acl1 before ts1
    assert list(acl1.used_in_records(older_than_timestamp=ts1)) == []
    # one record before ts2 and ts3
    assert list(
        acl1.used_in_records(older_than_timestamp=ts2)) == [str(record1.id)]
    assert list(
        acl1.used_in_records(older_than_timestamp=ts3)) == [str(record1.id)]
    # and one record before now
    assert list(acl1.used_in_records()) == [str(record1.id)]

    # there should be no resource for acl2 before ts1 and ts2
    assert list(acl2.used_in_records(older_than_timestamp=ts1)) == []
    assert list(acl2.used_in_records(older_than_timestamp=ts2)) == []
    # one record before ts3
    assert list(
        acl2.used_in_records(older_than_timestamp=ts3)) == [str(record2.id)]
    # and one record before now
    assert list(acl2.used_in_records()) == [str(record2.id)]
Пример #21
0
def test_create_acl_after_record(app, db, es, es_acl_prepare, test_users):
    with app.test_client() as client:
        login(client, test_users.u1)
        response = client.post(records_url(),
                               data=json.dumps({
                                   'title': 'blah',
                                   'contributors': []
                               }),
                               content_type='application/json')
        assert response.status_code == 201
        rest_metadata = get_json(response)['metadata']
        assert 'control_number' in rest_metadata

        current_search_client.indices.refresh()
        current_search_client.indices.flush()

        with db.session.begin_nested():
            acl1 = DefaultACL(name='default',
                              schemas=[RECORD_SCHEMA],
                              priority=0,
                              originator=test_users.u1,
                              operation='get')
            actor1 = SystemRoleActor(name='auth',
                                     system_role='any_user',
                                     acl=acl1,
                                     originator=test_users.u1)
            db.session.add(acl1)
            db.session.add(actor1)

        # reindex all resources that might be affected by the ACL change
        current_explicit_acls.reindex_acl(acl1, delayed=False)

        index, doctype = schema_to_index(RECORD_SCHEMA)

        rec_md = current_search_client.get(
            index=index,
            doc_type=doctype,
            id=str(
                PersistentIdentifier.get(
                    'recid', rest_metadata['control_number']).object_uuid))

        clear_timestamp(rec_md)

        assert rec_md['_source']['_invenio_explicit_acls'] == [{
            'operation':
            'get',
            'id':
            acl1.id,
            'timestamp':
            'cleared',
            'system_role': ['any_user']
        }]

        # remove the ACL from the database
        with db.session.begin_nested():
            db.session.delete(acl1)

        # reindex records affected by the removal of ACL
        current_explicit_acls.reindex_acl_removed(acl1, delayed=False)

        # make sure all changes had time to propagate and test
        current_search_client.indices.refresh()
        current_search_client.indices.flush()

        rec_md = current_search_client.get(
            index=index,
            doc_type=doctype,
            id=str(
                PersistentIdentifier.get(
                    'recid', rest_metadata['control_number']).object_uuid))

        # there is no ACL in the database => no acls are defined nor enforced on the record
        print(json.dumps(rec_md, indent=4))
        assert '_invenio_explicit_acls' not in rec_md['_source']