oarepo-runtime 1.5.97__tar.gz → 1.5.100__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {oarepo_runtime-1.5.97/oarepo_runtime.egg-info → oarepo_runtime-1.5.100}/PKG-INFO +1 -1
- {oarepo_runtime-1.5.97 → oarepo_runtime-1.5.100}/oarepo_runtime/cli/__init__.py +2 -0
- oarepo_runtime-1.5.100/oarepo_runtime/cli/permissions/__init__.py +6 -0
- oarepo_runtime-1.5.100/oarepo_runtime/cli/permissions/base.py +25 -0
- oarepo_runtime-1.5.100/oarepo_runtime/cli/permissions/evaluate.py +63 -0
- oarepo_runtime-1.5.100/oarepo_runtime/cli/permissions/list.py +239 -0
- oarepo_runtime-1.5.100/oarepo_runtime/cli/permissions/search.py +121 -0
- oarepo_runtime-1.5.100/oarepo_runtime/info/permissions/debug.py +191 -0
- {oarepo_runtime-1.5.97 → oarepo_runtime-1.5.100}/oarepo_runtime/services/config/permissions_presets.py +14 -1
- {oarepo_runtime-1.5.97 → oarepo_runtime-1.5.100}/oarepo_runtime/services/custom_fields/mappings.py +3 -0
- {oarepo_runtime-1.5.97 → oarepo_runtime-1.5.100}/oarepo_runtime/services/facets/params.py +62 -1
- {oarepo_runtime-1.5.97 → oarepo_runtime-1.5.100}/oarepo_runtime/services/schema/ui.py +2 -0
- {oarepo_runtime-1.5.97 → oarepo_runtime-1.5.100}/oarepo_runtime/services/search.py +27 -5
- {oarepo_runtime-1.5.97 → oarepo_runtime-1.5.100/oarepo_runtime.egg-info}/PKG-INFO +1 -1
- {oarepo_runtime-1.5.97 → oarepo_runtime-1.5.100}/oarepo_runtime.egg-info/SOURCES.txt +7 -0
- {oarepo_runtime-1.5.97 → oarepo_runtime-1.5.100}/setup.cfg +1 -1
- oarepo_runtime-1.5.100/tests/pkg_data/__init__.py +0 -0
- {oarepo_runtime-1.5.97 → oarepo_runtime-1.5.100}/LICENSE +0 -0
- {oarepo_runtime-1.5.97 → oarepo_runtime-1.5.100}/MANIFEST.in +0 -0
- {oarepo_runtime-1.5.97 → oarepo_runtime-1.5.100}/README.md +0 -0
- {oarepo_runtime-1.5.97 → oarepo_runtime-1.5.100}/oarepo_runtime/__init__.py +0 -0
- {oarepo_runtime-1.5.97 → oarepo_runtime-1.5.100}/oarepo_runtime/cli/assets.py +0 -0
- {oarepo_runtime-1.5.97 → oarepo_runtime-1.5.100}/oarepo_runtime/cli/base.py +0 -0
- {oarepo_runtime-1.5.97 → oarepo_runtime-1.5.100}/oarepo_runtime/cli/cf.py +0 -0
- {oarepo_runtime-1.5.97 → oarepo_runtime-1.5.100}/oarepo_runtime/cli/check.py +0 -0
- {oarepo_runtime-1.5.97 → oarepo_runtime-1.5.100}/oarepo_runtime/cli/configuration.py +0 -0
- {oarepo_runtime-1.5.97 → oarepo_runtime-1.5.100}/oarepo_runtime/cli/fixtures.py +0 -0
- {oarepo_runtime-1.5.97 → oarepo_runtime-1.5.100}/oarepo_runtime/cli/index.py +0 -0
- {oarepo_runtime-1.5.97 → oarepo_runtime-1.5.100}/oarepo_runtime/cli/validate.py +0 -0
- {oarepo_runtime-1.5.97 → oarepo_runtime-1.5.100}/oarepo_runtime/datastreams/__init__.py +0 -0
- {oarepo_runtime-1.5.97 → oarepo_runtime-1.5.100}/oarepo_runtime/datastreams/asynchronous.py +0 -0
- {oarepo_runtime-1.5.97 → oarepo_runtime-1.5.100}/oarepo_runtime/datastreams/catalogue.py +0 -0
- {oarepo_runtime-1.5.97 → oarepo_runtime-1.5.100}/oarepo_runtime/datastreams/datastreams.py +0 -0
- {oarepo_runtime-1.5.97 → oarepo_runtime-1.5.100}/oarepo_runtime/datastreams/errors.py +0 -0
- {oarepo_runtime-1.5.97 → oarepo_runtime-1.5.100}/oarepo_runtime/datastreams/ext.py +0 -0
- {oarepo_runtime-1.5.97 → oarepo_runtime-1.5.100}/oarepo_runtime/datastreams/fixtures.py +0 -0
- {oarepo_runtime-1.5.97 → oarepo_runtime-1.5.100}/oarepo_runtime/datastreams/json.py +0 -0
- {oarepo_runtime-1.5.97 → oarepo_runtime-1.5.100}/oarepo_runtime/datastreams/readers/__init__.py +0 -0
- {oarepo_runtime-1.5.97 → oarepo_runtime-1.5.100}/oarepo_runtime/datastreams/readers/attachments.py +0 -0
- {oarepo_runtime-1.5.97 → oarepo_runtime-1.5.100}/oarepo_runtime/datastreams/readers/excel.py +0 -0
- {oarepo_runtime-1.5.97 → oarepo_runtime-1.5.100}/oarepo_runtime/datastreams/readers/json.py +0 -0
- {oarepo_runtime-1.5.97 → oarepo_runtime-1.5.100}/oarepo_runtime/datastreams/readers/service.py +0 -0
- {oarepo_runtime-1.5.97 → oarepo_runtime-1.5.100}/oarepo_runtime/datastreams/readers/yaml.py +0 -0
- {oarepo_runtime-1.5.97 → oarepo_runtime-1.5.100}/oarepo_runtime/datastreams/semi_asynchronous.py +0 -0
- {oarepo_runtime-1.5.97 → oarepo_runtime-1.5.100}/oarepo_runtime/datastreams/synchronous.py +0 -0
- {oarepo_runtime-1.5.97 → oarepo_runtime-1.5.100}/oarepo_runtime/datastreams/transformers.py +0 -0
- {oarepo_runtime-1.5.97 → oarepo_runtime-1.5.100}/oarepo_runtime/datastreams/types.py +0 -0
- {oarepo_runtime-1.5.97 → oarepo_runtime-1.5.100}/oarepo_runtime/datastreams/utils.py +0 -0
- {oarepo_runtime-1.5.97 → oarepo_runtime-1.5.100}/oarepo_runtime/datastreams/writers/__init__.py +0 -0
- {oarepo_runtime-1.5.97 → oarepo_runtime-1.5.100}/oarepo_runtime/datastreams/writers/attachments_file.py +0 -0
- {oarepo_runtime-1.5.97 → oarepo_runtime-1.5.100}/oarepo_runtime/datastreams/writers/attachments_service.py +0 -0
- {oarepo_runtime-1.5.97 → oarepo_runtime-1.5.100}/oarepo_runtime/datastreams/writers/service.py +0 -0
- {oarepo_runtime-1.5.97 → oarepo_runtime-1.5.100}/oarepo_runtime/datastreams/writers/utils.py +0 -0
- {oarepo_runtime-1.5.97 → oarepo_runtime-1.5.100}/oarepo_runtime/datastreams/writers/validation_errors.py +0 -0
- {oarepo_runtime-1.5.97 → oarepo_runtime-1.5.100}/oarepo_runtime/datastreams/writers/yaml.py +0 -0
- {oarepo_runtime-1.5.97 → oarepo_runtime-1.5.100}/oarepo_runtime/ext.py +0 -0
- {oarepo_runtime-1.5.97 → oarepo_runtime-1.5.100}/oarepo_runtime/ext_config.py +0 -0
- {oarepo_runtime-1.5.97 → oarepo_runtime-1.5.100}/oarepo_runtime/i18n/__init__.py +0 -0
- {oarepo_runtime-1.5.97 → oarepo_runtime-1.5.100}/oarepo_runtime/info/__init__.py +0 -0
- {oarepo_runtime-1.5.97 → oarepo_runtime-1.5.100}/oarepo_runtime/info/check.py +0 -0
- {oarepo_runtime-1.5.97/oarepo_runtime/records/mappings → oarepo_runtime-1.5.100/oarepo_runtime/info/permissions}/__init__.py +0 -0
- {oarepo_runtime-1.5.97 → oarepo_runtime-1.5.100}/oarepo_runtime/info/views.py +0 -0
- {oarepo_runtime-1.5.97 → oarepo_runtime-1.5.100}/oarepo_runtime/profile.py +0 -0
- {oarepo_runtime-1.5.97 → oarepo_runtime-1.5.100}/oarepo_runtime/proxies.py +0 -0
- {oarepo_runtime-1.5.97 → oarepo_runtime-1.5.100}/oarepo_runtime/records/__init__.py +0 -0
- {oarepo_runtime-1.5.97 → oarepo_runtime-1.5.100}/oarepo_runtime/records/dumpers/__init__.py +0 -0
- {oarepo_runtime-1.5.97 → oarepo_runtime-1.5.100}/oarepo_runtime/records/dumpers/edtf_interval.py +0 -0
- {oarepo_runtime-1.5.97 → oarepo_runtime-1.5.100}/oarepo_runtime/records/dumpers/multilingual_dumper.py +0 -0
- {oarepo_runtime-1.5.97 → oarepo_runtime-1.5.100}/oarepo_runtime/records/entity_resolvers/__init__.py +0 -0
- {oarepo_runtime-1.5.97 → oarepo_runtime-1.5.100}/oarepo_runtime/records/entity_resolvers/proxies.py +0 -0
- {oarepo_runtime-1.5.97/oarepo_runtime/services → oarepo_runtime-1.5.100/oarepo_runtime/records/mappings}/__init__.py +0 -0
- {oarepo_runtime-1.5.97 → oarepo_runtime-1.5.100}/oarepo_runtime/records/mappings/rdm_parent_mapping.json +0 -0
- {oarepo_runtime-1.5.97 → oarepo_runtime-1.5.100}/oarepo_runtime/records/owners/__init__.py +0 -0
- {oarepo_runtime-1.5.97 → oarepo_runtime-1.5.100}/oarepo_runtime/records/owners/registry.py +0 -0
- {oarepo_runtime-1.5.97 → oarepo_runtime-1.5.100}/oarepo_runtime/records/relations/__init__.py +0 -0
- {oarepo_runtime-1.5.97 → oarepo_runtime-1.5.100}/oarepo_runtime/records/relations/base.py +0 -0
- {oarepo_runtime-1.5.97 → oarepo_runtime-1.5.100}/oarepo_runtime/records/relations/internal.py +0 -0
- {oarepo_runtime-1.5.97 → oarepo_runtime-1.5.100}/oarepo_runtime/records/relations/lookup.py +0 -0
- {oarepo_runtime-1.5.97 → oarepo_runtime-1.5.100}/oarepo_runtime/records/relations/pid_relation.py +0 -0
- {oarepo_runtime-1.5.97 → oarepo_runtime-1.5.100}/oarepo_runtime/records/systemfields/__init__.py +0 -0
- {oarepo_runtime-1.5.97 → oarepo_runtime-1.5.100}/oarepo_runtime/records/systemfields/featured_file.py +0 -0
- {oarepo_runtime-1.5.97 → oarepo_runtime-1.5.100}/oarepo_runtime/records/systemfields/has_draftcheck.py +0 -0
- {oarepo_runtime-1.5.97 → oarepo_runtime-1.5.100}/oarepo_runtime/records/systemfields/icu.py +0 -0
- {oarepo_runtime-1.5.97 → oarepo_runtime-1.5.100}/oarepo_runtime/records/systemfields/mapping.py +0 -0
- {oarepo_runtime-1.5.97 → oarepo_runtime-1.5.100}/oarepo_runtime/records/systemfields/owner.py +0 -0
- {oarepo_runtime-1.5.97 → oarepo_runtime-1.5.100}/oarepo_runtime/records/systemfields/record_status.py +0 -0
- {oarepo_runtime-1.5.97 → oarepo_runtime-1.5.100}/oarepo_runtime/records/systemfields/selectors.py +0 -0
- {oarepo_runtime-1.5.97 → oarepo_runtime-1.5.100}/oarepo_runtime/records/systemfields/synthetic.py +0 -0
- {oarepo_runtime-1.5.97 → oarepo_runtime-1.5.100}/oarepo_runtime/resources/__init__.py +0 -0
- {oarepo_runtime-1.5.97 → oarepo_runtime-1.5.100}/oarepo_runtime/resources/file_resource.py +0 -0
- {oarepo_runtime-1.5.97 → oarepo_runtime-1.5.100}/oarepo_runtime/resources/json_serializer.py +0 -0
- {oarepo_runtime-1.5.97 → oarepo_runtime-1.5.100}/oarepo_runtime/resources/localized_ui_json_serializer.py +0 -0
- {oarepo_runtime-1.5.97/oarepo_runtime/services/entity → oarepo_runtime-1.5.100/oarepo_runtime/services}/__init__.py +0 -0
- {oarepo_runtime-1.5.97 → oarepo_runtime-1.5.100}/oarepo_runtime/services/components.py +0 -0
- {oarepo_runtime-1.5.97 → oarepo_runtime-1.5.100}/oarepo_runtime/services/config/__init__.py +0 -0
- {oarepo_runtime-1.5.97 → oarepo_runtime-1.5.100}/oarepo_runtime/services/config/link_conditions.py +0 -0
- {oarepo_runtime-1.5.97 → oarepo_runtime-1.5.100}/oarepo_runtime/services/config/service.py +0 -0
- {oarepo_runtime-1.5.97 → oarepo_runtime-1.5.100}/oarepo_runtime/services/custom_fields/__init__.py +0 -0
- {oarepo_runtime-1.5.97/oarepo_runtime/services/expansions → oarepo_runtime-1.5.100/oarepo_runtime/services/entity}/__init__.py +0 -0
- {oarepo_runtime-1.5.97 → oarepo_runtime-1.5.100}/oarepo_runtime/services/entity/config.py +0 -0
- {oarepo_runtime-1.5.97 → oarepo_runtime-1.5.100}/oarepo_runtime/services/entity/schema.py +0 -0
- {oarepo_runtime-1.5.97 → oarepo_runtime-1.5.100}/oarepo_runtime/services/entity/service.py +0 -0
- {oarepo_runtime-1.5.97/oarepo_runtime/services/relations → oarepo_runtime-1.5.100/oarepo_runtime/services/expansions}/__init__.py +0 -0
- {oarepo_runtime-1.5.97 → oarepo_runtime-1.5.100}/oarepo_runtime/services/expansions/expandable_fields.py +0 -0
- {oarepo_runtime-1.5.97 → oarepo_runtime-1.5.100}/oarepo_runtime/services/expansions/service.py +0 -0
- {oarepo_runtime-1.5.97 → oarepo_runtime-1.5.100}/oarepo_runtime/services/facets/__init__.py +0 -0
- {oarepo_runtime-1.5.97 → oarepo_runtime-1.5.100}/oarepo_runtime/services/facets/base.py +0 -0
- {oarepo_runtime-1.5.97 → oarepo_runtime-1.5.100}/oarepo_runtime/services/facets/date.py +0 -0
- {oarepo_runtime-1.5.97 → oarepo_runtime-1.5.100}/oarepo_runtime/services/facets/enum.py +0 -0
- {oarepo_runtime-1.5.97 → oarepo_runtime-1.5.100}/oarepo_runtime/services/facets/facet_groups_names.py +0 -0
- {oarepo_runtime-1.5.97 → oarepo_runtime-1.5.100}/oarepo_runtime/services/facets/max_facet.py +0 -0
- {oarepo_runtime-1.5.97 → oarepo_runtime-1.5.100}/oarepo_runtime/services/facets/nested_facet.py +0 -0
- {oarepo_runtime-1.5.97 → oarepo_runtime-1.5.100}/oarepo_runtime/services/facets/year_histogram.py +0 -0
- {oarepo_runtime-1.5.97 → oarepo_runtime-1.5.100}/oarepo_runtime/services/files/__init__.py +0 -0
- {oarepo_runtime-1.5.97 → oarepo_runtime-1.5.100}/oarepo_runtime/services/files/components.py +0 -0
- {oarepo_runtime-1.5.97 → oarepo_runtime-1.5.100}/oarepo_runtime/services/files/service.py +0 -0
- {oarepo_runtime-1.5.97 → oarepo_runtime-1.5.100}/oarepo_runtime/services/generators.py +0 -0
- {oarepo_runtime-1.5.97 → oarepo_runtime-1.5.100}/oarepo_runtime/services/permissions/__init__.py +0 -0
- {oarepo_runtime-1.5.97 → oarepo_runtime-1.5.100}/oarepo_runtime/services/permissions/generators.py +0 -0
- {oarepo_runtime-1.5.97 → oarepo_runtime-1.5.100}/oarepo_runtime/services/records/__init__.py +0 -0
- {oarepo_runtime-1.5.97 → oarepo_runtime-1.5.100}/oarepo_runtime/services/records/links.py +0 -0
- {oarepo_runtime-1.5.97/oarepo_runtime/utils → oarepo_runtime-1.5.100/oarepo_runtime/services/relations}/__init__.py +0 -0
- {oarepo_runtime-1.5.97 → oarepo_runtime-1.5.100}/oarepo_runtime/services/relations/components.py +0 -0
- {oarepo_runtime-1.5.97 → oarepo_runtime-1.5.100}/oarepo_runtime/services/relations/errors.py +0 -0
- {oarepo_runtime-1.5.97 → oarepo_runtime-1.5.100}/oarepo_runtime/services/relations/mapping.py +0 -0
- {oarepo_runtime-1.5.97 → oarepo_runtime-1.5.100}/oarepo_runtime/services/results.py +0 -0
- {oarepo_runtime-1.5.97 → oarepo_runtime-1.5.100}/oarepo_runtime/services/schema/__init__.py +0 -0
- {oarepo_runtime-1.5.97 → oarepo_runtime-1.5.100}/oarepo_runtime/services/schema/cf.py +0 -0
- {oarepo_runtime-1.5.97 → oarepo_runtime-1.5.100}/oarepo_runtime/services/schema/i18n.py +0 -0
- {oarepo_runtime-1.5.97 → oarepo_runtime-1.5.100}/oarepo_runtime/services/schema/i18n_ui.py +0 -0
- {oarepo_runtime-1.5.97 → oarepo_runtime-1.5.100}/oarepo_runtime/services/schema/i18n_validation.py +0 -0
- {oarepo_runtime-1.5.97 → oarepo_runtime-1.5.100}/oarepo_runtime/services/schema/marshmallow.py +0 -0
- {oarepo_runtime-1.5.97 → oarepo_runtime-1.5.100}/oarepo_runtime/services/schema/marshmallow_to_json_schema.py +0 -0
- {oarepo_runtime-1.5.97 → oarepo_runtime-1.5.100}/oarepo_runtime/services/schema/oneofschema.py +0 -0
- {oarepo_runtime-1.5.97 → oarepo_runtime-1.5.100}/oarepo_runtime/services/schema/polymorphic.py +0 -0
- {oarepo_runtime-1.5.97 → oarepo_runtime-1.5.100}/oarepo_runtime/services/schema/rdm.py +0 -0
- {oarepo_runtime-1.5.97 → oarepo_runtime-1.5.100}/oarepo_runtime/services/schema/validation.py +0 -0
- {oarepo_runtime-1.5.97 → oarepo_runtime-1.5.100}/oarepo_runtime/tasks.py +0 -0
- {oarepo_runtime-1.5.97 → oarepo_runtime-1.5.100}/oarepo_runtime/translations/cs/LC_MESSAGES/messages.mo +0 -0
- {oarepo_runtime-1.5.97 → oarepo_runtime-1.5.100}/oarepo_runtime/translations/cs/LC_MESSAGES/messages.po +0 -0
- {oarepo_runtime-1.5.97 → oarepo_runtime-1.5.100}/oarepo_runtime/translations/default_translations.py +0 -0
- {oarepo_runtime-1.5.97 → oarepo_runtime-1.5.100}/oarepo_runtime/translations/en/LC_MESSAGES/messages.mo +0 -0
- {oarepo_runtime-1.5.97 → oarepo_runtime-1.5.100}/oarepo_runtime/translations/en/LC_MESSAGES/messages.po +0 -0
- {oarepo_runtime-1.5.97 → oarepo_runtime-1.5.100}/oarepo_runtime/translations/messages.pot +0 -0
- {oarepo_runtime-1.5.97 → oarepo_runtime-1.5.100}/oarepo_runtime/uow.py +0 -0
- {oarepo_runtime-1.5.97/tests/marshmallow_to_json → oarepo_runtime-1.5.100/oarepo_runtime/utils}/__init__.py +0 -0
- {oarepo_runtime-1.5.97 → oarepo_runtime-1.5.100}/oarepo_runtime/utils/functools.py +0 -0
- {oarepo_runtime-1.5.97 → oarepo_runtime-1.5.100}/oarepo_runtime/utils/path.py +0 -0
- {oarepo_runtime-1.5.97 → oarepo_runtime-1.5.100}/oarepo_runtime.egg-info/dependency_links.txt +0 -0
- {oarepo_runtime-1.5.97 → oarepo_runtime-1.5.100}/oarepo_runtime.egg-info/entry_points.txt +0 -0
- {oarepo_runtime-1.5.97 → oarepo_runtime-1.5.100}/oarepo_runtime.egg-info/requires.txt +0 -0
- {oarepo_runtime-1.5.97 → oarepo_runtime-1.5.100}/oarepo_runtime.egg-info/top_level.txt +0 -0
- {oarepo_runtime-1.5.97 → oarepo_runtime-1.5.100}/pyproject.toml +0 -0
- {oarepo_runtime-1.5.97 → oarepo_runtime-1.5.100}/setup.py +0 -0
- {oarepo_runtime-1.5.97/tests/pkg_data → oarepo_runtime-1.5.100/tests/marshmallow_to_json}/__init__.py +0 -0
- {oarepo_runtime-1.5.97 → oarepo_runtime-1.5.100}/tests/marshmallow_to_json/test_datacite_ui_schema.py +0 -0
- {oarepo_runtime-1.5.97 → oarepo_runtime-1.5.100}/tests/marshmallow_to_json/test_simple_schema.py +0 -0
@@ -4,6 +4,7 @@ from .check import check
|
|
4
4
|
from .configuration import configuration_command
|
5
5
|
from .fixtures import fixtures
|
6
6
|
from .index import index
|
7
|
+
from .permissions import permissions
|
7
8
|
from .validate import validate
|
8
9
|
|
9
10
|
__all__ = (
|
@@ -15,4 +16,5 @@ __all__ = (
|
|
15
16
|
"validate",
|
16
17
|
"fixtures",
|
17
18
|
"configuration_command",
|
19
|
+
"permissions",
|
18
20
|
)
|
@@ -0,0 +1,25 @@
|
|
1
|
+
from flask import current_app
|
2
|
+
from flask_principal import Identity, identity_loaded
|
3
|
+
from invenio_access.models import User
|
4
|
+
|
5
|
+
from ..base import oarepo
|
6
|
+
|
7
|
+
|
8
|
+
@oarepo.group()
|
9
|
+
def permissions():
|
10
|
+
"""Commands for checking and explaining permissions."""
|
11
|
+
|
12
|
+
|
13
|
+
def get_user_and_identity(user_id_or_email):
|
14
|
+
try:
|
15
|
+
user_id = int(user_id_or_email)
|
16
|
+
user = User.query.filter_by(id=user_id).one()
|
17
|
+
except ValueError:
|
18
|
+
user = User.query.filter_by(email=user_id_or_email).one()
|
19
|
+
|
20
|
+
identity = Identity(user.id)
|
21
|
+
api_app = current_app.wsgi_app.mounts["/api"]
|
22
|
+
with api_app.app_context():
|
23
|
+
with current_app.test_request_context("/api"):
|
24
|
+
identity_loaded.send(api_app, identity=identity)
|
25
|
+
return user, identity
|
@@ -0,0 +1,63 @@
|
|
1
|
+
import json
|
2
|
+
|
3
|
+
import click
|
4
|
+
from invenio_records_permissions.policies.records import RecordPermissionPolicy
|
5
|
+
from invenio_records_resources.proxies import current_service_registry
|
6
|
+
|
7
|
+
from oarepo_runtime.info.permissions.debug import add_debugging
|
8
|
+
|
9
|
+
from .base import get_user_and_identity, permissions
|
10
|
+
|
11
|
+
|
12
|
+
@permissions.command(name="evaluate")
|
13
|
+
@click.argument("user_id_or_email")
|
14
|
+
@click.argument("service_name")
|
15
|
+
@click.argument("record_id", required=False)
|
16
|
+
@click.option("--data", "-d", help="Data to pass to the policy check")
|
17
|
+
@click.option("--explain/--no-explain", default=False)
|
18
|
+
@click.option("--draft/--published", default=False)
|
19
|
+
def evaluate_permissions(
|
20
|
+
user_id_or_email: str,
|
21
|
+
service_name: str,
|
22
|
+
record_id: str | None = None,
|
23
|
+
data: str | None = None,
|
24
|
+
explain: bool = False,
|
25
|
+
draft: bool = False,
|
26
|
+
):
|
27
|
+
"""Evaluate permissions for a given workflow, community or service."""
|
28
|
+
service = current_service_registry.get(service_name)
|
29
|
+
user, identity = get_user_and_identity(user_id_or_email)
|
30
|
+
|
31
|
+
over = {}
|
32
|
+
if record_id:
|
33
|
+
if draft:
|
34
|
+
over["record"] = service.config.draft_cls.pid.resolve(
|
35
|
+
record_id, registered_only=False
|
36
|
+
)
|
37
|
+
else:
|
38
|
+
over["record"] = service.config.record_cls.pid.resolve(record_id)
|
39
|
+
if data:
|
40
|
+
over["data"] = json.loads(data)
|
41
|
+
|
42
|
+
if explain:
|
43
|
+
over["debug_identity"] = identity
|
44
|
+
add_debugging()
|
45
|
+
|
46
|
+
policy_cls = service.config.permission_policy_cls
|
47
|
+
click.secho(f"Policy: {policy_cls}")
|
48
|
+
|
49
|
+
for action_name in dir(policy_cls):
|
50
|
+
if not action_name.startswith("can_"):
|
51
|
+
continue
|
52
|
+
|
53
|
+
policy: RecordPermissionPolicy = policy_cls(action_name[4:], **over)
|
54
|
+
if explain:
|
55
|
+
click.secho()
|
56
|
+
click.secho(f"## {action_name}")
|
57
|
+
try:
|
58
|
+
if policy.allows(identity):
|
59
|
+
click.secho(f"{action_name}: True", fg="green")
|
60
|
+
else:
|
61
|
+
click.secho(f"{action_name}: False", fg="red")
|
62
|
+
except Exception as e:
|
63
|
+
click.secho(f"{action_name}: {e}", fg="yellow")
|
@@ -0,0 +1,239 @@
|
|
1
|
+
from typing import get_type_hints
|
2
|
+
|
3
|
+
import click
|
4
|
+
|
5
|
+
from oarepo_runtime.info.permissions.debug import add_debugging
|
6
|
+
|
7
|
+
from .base import permissions
|
8
|
+
|
9
|
+
|
10
|
+
@permissions.command(name="list")
|
11
|
+
@click.option("--workflow", "-w", help="Workflow name")
|
12
|
+
@click.option("--community", "-c", help="Community name")
|
13
|
+
@click.option("--service", "-s", help="Service name")
|
14
|
+
def list_permissions(workflow, community, service):
|
15
|
+
"""List all permissions for a given workflow, community or service."""
|
16
|
+
# intentionally import here to enable oarepo-runtime to be used
|
17
|
+
# without oarepo-workflows and oarepo-communities
|
18
|
+
from invenio_communities.communities.records.api import Community
|
19
|
+
from invenio_communities.communities.records.models import CommunityMetadata
|
20
|
+
from oarepo_workflows.proxies import current_oarepo_workflows
|
21
|
+
|
22
|
+
if workflow:
|
23
|
+
wf = current_oarepo_workflows.record_workflows[workflow]
|
24
|
+
permission_policy = wf.permission_policy_cls
|
25
|
+
elif community:
|
26
|
+
community_db = CommunityMetadata.query.filter_by(slug=community).one()
|
27
|
+
community_obj = Community(community_db.data, model=community_db)
|
28
|
+
workflow = community_obj["custom_fields"].get("workflow", "default")
|
29
|
+
wf = current_oarepo_workflows.record_workflows[workflow]
|
30
|
+
permission_policy = wf.permission_policy_cls
|
31
|
+
elif service:
|
32
|
+
from invenio_records_resources.proxies import current_service_registry
|
33
|
+
|
34
|
+
swc = current_service_registry.get(service)
|
35
|
+
permission_policy = swc.config.permission_policy_cls
|
36
|
+
else:
|
37
|
+
raise click.UsageError(
|
38
|
+
"You must specify either --workflow, --community or --service."
|
39
|
+
)
|
40
|
+
|
41
|
+
add_debugging()
|
42
|
+
|
43
|
+
p = permission_policy("read")
|
44
|
+
|
45
|
+
for action_name in dir(permission_policy):
|
46
|
+
if not action_name.startswith("can_"):
|
47
|
+
continue
|
48
|
+
action_permission_generators = getattr(p, action_name)
|
49
|
+
debugs = []
|
50
|
+
for x in action_permission_generators:
|
51
|
+
debugs.append(x.to_debug_dict())
|
52
|
+
|
53
|
+
print("")
|
54
|
+
print(f"## {action_name}")
|
55
|
+
print(get_permission_documentation(permission_policy, action_name))
|
56
|
+
for perm in debugs:
|
57
|
+
print_permission_markdown(perm)
|
58
|
+
|
59
|
+
|
60
|
+
def is_simple_dict(d):
|
61
|
+
return all(isinstance(v, (str, int, float, bool)) for v in d.values())
|
62
|
+
|
63
|
+
|
64
|
+
def format_simple_dict_values(d):
|
65
|
+
# returns k=v, k=v, ...
|
66
|
+
return ", ".join(f"{k}={v}" for k, v in d.items())
|
67
|
+
|
68
|
+
|
69
|
+
def print_permission_markdown(p, level=0):
|
70
|
+
for k, v in p.items():
|
71
|
+
prologue = " " * level + f"- {k}"
|
72
|
+
if v is None or v == {} or v == []:
|
73
|
+
print(prologue)
|
74
|
+
continue
|
75
|
+
elif isinstance(v, dict):
|
76
|
+
if is_simple_dict(v):
|
77
|
+
print(f"{prologue}: {format_simple_dict_values(v)}")
|
78
|
+
else:
|
79
|
+
print(prologue)
|
80
|
+
print_permission_markdown(v, level + 1)
|
81
|
+
elif isinstance(v, list):
|
82
|
+
print(prologue)
|
83
|
+
for x in v:
|
84
|
+
print_permission_markdown(x, level + 1)
|
85
|
+
else:
|
86
|
+
print(" " * (level + 1) + f"{v}")
|
87
|
+
|
88
|
+
|
89
|
+
def get_permission_documentation(policy, permission_can_name):
|
90
|
+
type_hints = get_type_hints(policy, include_extras=True)
|
91
|
+
annotation = type_hints.get(permission_can_name)
|
92
|
+
if annotation:
|
93
|
+
for md in annotation.__metadata__:
|
94
|
+
if isinstance(md, str):
|
95
|
+
return md
|
96
|
+
ret = default_permissions_documentation.get(permission_can_name, "")
|
97
|
+
return "\n".join(x.strip() for x in ret.split("\n"))
|
98
|
+
|
99
|
+
|
100
|
+
default_permissions_documentation = {
|
101
|
+
# records
|
102
|
+
"can_create": """
|
103
|
+
Grants users the ability to create new records in the
|
104
|
+
repository, often assigned to roles like submitters, owners, or curators.
|
105
|
+
The result of this action is typically a draft record.
|
106
|
+
""",
|
107
|
+
"can_read": """
|
108
|
+
Allows users to view or access records, with access
|
109
|
+
possibly dependent on the record's state (e.g., draft or published) and the
|
110
|
+
user's role.
|
111
|
+
""",
|
112
|
+
"can_read_deleted": """
|
113
|
+
Permission to view or access records, including soft-deleted ones. This
|
114
|
+
permission is used to filter search results. It should include an
|
115
|
+
`IfRecordDeleted` permission generator, which grants access to deleted
|
116
|
+
records to a subset of users (e.g., owners, curators, etc.). For
|
117
|
+
`service.read`, this permission is applied when record is deleted and
|
118
|
+
`include_deleted` is passed; otherwise, a `RecordDeletedException` is raised.
|
119
|
+
""",
|
120
|
+
"can_search": """
|
121
|
+
Grants users the ability to search for records within the
|
122
|
+
repository, typically available to all users. The search results are
|
123
|
+
filtered based on the can_read_deleted permission for published records (/api/datasets)
|
124
|
+
and the can_read_draft permission for draft records (/api/user/datasets).
|
125
|
+
""",
|
126
|
+
"can_update": """
|
127
|
+
Grants users the ability to update or modify published records.
|
128
|
+
In NRP repositories, this is normally disabled as updates are performed
|
129
|
+
on draft records, which are then published.
|
130
|
+
""",
|
131
|
+
"can_delete": """
|
132
|
+
Grants users the ability to delete records, often restricted
|
133
|
+
to owners, curators, or specific roles, and dependent on the record's
|
134
|
+
state (e.g., draft). For published records, the deletion is performed
|
135
|
+
via a specialized request, not directly.
|
136
|
+
""",
|
137
|
+
"can_new_version": """
|
138
|
+
Allows users to create a new version of a record. In NRP,
|
139
|
+
this is not used directly but always via a request, which is mostly
|
140
|
+
auto-approved.
|
141
|
+
""",
|
142
|
+
"can_edit": """
|
143
|
+
Grants users the ability to modify the metadata of a record. In NRP, this
|
144
|
+
is not used directly but always via a request, which is mostly auto-approved.
|
145
|
+
""",
|
146
|
+
"can_manage": """
|
147
|
+
Grants users the ability to manage records. Currently it is used just
|
148
|
+
for reindexing records with latest version first (reindex_latest_first)
|
149
|
+
service call, not mapped to REST API.
|
150
|
+
""",
|
151
|
+
"can_manage_record_access": """
|
152
|
+
Grants users the ability to manage record access.
|
153
|
+
""",
|
154
|
+
# draft records
|
155
|
+
"can_read_draft": """
|
156
|
+
Enables users to view or access draft records, typically equivalent to
|
157
|
+
the general 'can_read' permission but specific to drafts.
|
158
|
+
""",
|
159
|
+
"can_delete_draft": """
|
160
|
+
Allows users to delete draft records, typically equivalent to the general
|
161
|
+
'can_delete' permission but specific to drafts.
|
162
|
+
""",
|
163
|
+
"can_update_draft": """
|
164
|
+
Allows users to update draft records. It is typically granted to record
|
165
|
+
owner, but can be restricted by the record status (such as records submitted
|
166
|
+
to review being locked).
|
167
|
+
""",
|
168
|
+
"can_publish": """
|
169
|
+
Grants users the ability to publish records, making them publicly
|
170
|
+
available or finalizing their state. In NRP repositories, this is
|
171
|
+
not used directly but always via a request.
|
172
|
+
""",
|
173
|
+
# files
|
174
|
+
"can_read_files": """
|
175
|
+
Grants users the ability to view or access file metadata associated with records,
|
176
|
+
with access dependent on the record's state and the user's role.
|
177
|
+
""",
|
178
|
+
"can_create_files": """
|
179
|
+
Enables users to create new files within published records in the repository.
|
180
|
+
Normally disabled as files are created in draft records and then published.
|
181
|
+
""",
|
182
|
+
"can_delete_files": """
|
183
|
+
Enables users to delete files associated with published records in the repository.
|
184
|
+
Normally disabled as files can not be deleted from published records.
|
185
|
+
""",
|
186
|
+
"can_update_files": """
|
187
|
+
Enables users to update or modify files within published records in the repository.
|
188
|
+
Normally disabled as files are updated in draft records and then published.
|
189
|
+
""",
|
190
|
+
"can_commit_files": """
|
191
|
+
Allows users to finalize file upload to published records in the repository.
|
192
|
+
Normally disabled as files are uploaded to draft records and then published.
|
193
|
+
""",
|
194
|
+
"can_get_content_files": """
|
195
|
+
Allows users to access or retrieve the content of files on records published
|
196
|
+
in the repository, with access depending on the record's state and the user's role.
|
197
|
+
""",
|
198
|
+
"can_manage_files": """
|
199
|
+
Grants users the ability to manage files (that is, change if the files are
|
200
|
+
required on the record or not).
|
201
|
+
""",
|
202
|
+
"can_read_deleted_files": """
|
203
|
+
Allows users to view or access files associated with deleted records,
|
204
|
+
often restricted to specific conditions.
|
205
|
+
""",
|
206
|
+
"can_set_content_files": """
|
207
|
+
Enables users to upload files to published records. Normally disabled as files
|
208
|
+
are uploaded to draft records and then published.
|
209
|
+
""",
|
210
|
+
# draft files
|
211
|
+
"can_draft_read_files": """
|
212
|
+
Allows users to view or access file metadata associated with draft records,
|
213
|
+
typically equivalent to the general 'can_read_files' permission but specific
|
214
|
+
to drafts.
|
215
|
+
""",
|
216
|
+
"can_draft_create_files": """
|
217
|
+
Allows users to create files specifically for draft records.
|
218
|
+
""",
|
219
|
+
"can_draft_get_content_files": """
|
220
|
+
Allows users to access or retrieve the content of files on draft records.
|
221
|
+
""",
|
222
|
+
"can_draft_set_content_files": """
|
223
|
+
Allows users to upload files to draft records.
|
224
|
+
""",
|
225
|
+
"can_draft_commit_files": """
|
226
|
+
Allows users to finalize file upload to draft records.
|
227
|
+
""",
|
228
|
+
"can_draft_update_files": """
|
229
|
+
Allows users to update or modify files within draft records.
|
230
|
+
""",
|
231
|
+
"can_draft_delete_files": """
|
232
|
+
Allows users to delete files associated with draft records.
|
233
|
+
""",
|
234
|
+
# misc
|
235
|
+
"can_create_or_update_many": """
|
236
|
+
Allows bulk creation or updating of multiple records or files.
|
237
|
+
Not used at the moment.
|
238
|
+
""",
|
239
|
+
}
|
@@ -0,0 +1,121 @@
|
|
1
|
+
import json
|
2
|
+
import sys
|
3
|
+
|
4
|
+
import click
|
5
|
+
import yaml
|
6
|
+
from invenio_records_resources.proxies import current_service_registry
|
7
|
+
|
8
|
+
from oarepo_runtime.info.permissions.debug import add_debugging, merge_communities
|
9
|
+
|
10
|
+
from .base import get_user_and_identity, permissions
|
11
|
+
|
12
|
+
|
13
|
+
@permissions.command(name="search")
|
14
|
+
@click.argument("user_id_or_email")
|
15
|
+
@click.argument("service_name")
|
16
|
+
@click.option("--explain/--no-explain", default=False)
|
17
|
+
@click.option("--user/--published", "user_call", default=False)
|
18
|
+
@click.option("--full-query/--query-filters", default=False)
|
19
|
+
@click.option("--merge-communities", "do_merge_communities", is_flag=True)
|
20
|
+
@click.option("--json/--yaml", "as_json", default=False)
|
21
|
+
def search_permissions(
|
22
|
+
user_id_or_email,
|
23
|
+
service_name,
|
24
|
+
explain,
|
25
|
+
user_call,
|
26
|
+
full_query,
|
27
|
+
do_merge_communities,
|
28
|
+
as_json,
|
29
|
+
):
|
30
|
+
"""Get search parameters for a given service."""
|
31
|
+
try:
|
32
|
+
service = current_service_registry.get(service_name)
|
33
|
+
except KeyError:
|
34
|
+
raise click.UsageError(
|
35
|
+
f"Service {service_name} not found in {current_service_registry._services.keys()}"
|
36
|
+
)
|
37
|
+
user, identity = get_user_and_identity(user_id_or_email)
|
38
|
+
|
39
|
+
permission_policy = service.config.permission_policy_cls
|
40
|
+
|
41
|
+
add_debugging(print_search=explain, print_needs=False, print_excludes=False)
|
42
|
+
|
43
|
+
if full_query:
|
44
|
+
previous_search = service._search
|
45
|
+
|
46
|
+
class NoExecute:
|
47
|
+
def __init__(self, query):
|
48
|
+
self.query = query
|
49
|
+
|
50
|
+
def execute(self):
|
51
|
+
return self.query
|
52
|
+
|
53
|
+
def _patched_search(*args, **kwargs):
|
54
|
+
ret = previous_search(*args, **kwargs)
|
55
|
+
return NoExecute(ret)
|
56
|
+
|
57
|
+
def _patched_result_list(self, identity, results, params, **kwargs):
|
58
|
+
return results
|
59
|
+
|
60
|
+
service._search = _patched_search
|
61
|
+
service.result_list = _patched_result_list
|
62
|
+
|
63
|
+
if user_call:
|
64
|
+
ret = service.search_drafts(identity)
|
65
|
+
else:
|
66
|
+
ret = service.search(identity)
|
67
|
+
ret = ret.to_dict()
|
68
|
+
if do_merge_communities:
|
69
|
+
ret = merge_communities(ret)
|
70
|
+
ret = {
|
71
|
+
"query": ret["query"],
|
72
|
+
}
|
73
|
+
dump_dict(ret, as_json)
|
74
|
+
else:
|
75
|
+
|
76
|
+
over = {}
|
77
|
+
if explain:
|
78
|
+
over["debug_identity"] = identity
|
79
|
+
print("## Explaining search:")
|
80
|
+
|
81
|
+
if user_call:
|
82
|
+
p = permission_policy("read_draft", identity=identity, **over)
|
83
|
+
else:
|
84
|
+
p = permission_policy("read_deleted", identity=identity, **over)
|
85
|
+
query_filters = p.query_filters
|
86
|
+
|
87
|
+
print()
|
88
|
+
print("## Query filters:")
|
89
|
+
for qf in query_filters:
|
90
|
+
dict_qf = qf.to_dict()
|
91
|
+
if explain:
|
92
|
+
dict_qf = merge_communities(dict_qf)
|
93
|
+
dump_dict(dict_qf, as_json)
|
94
|
+
print(json.dumps(dict_qf, indent=2))
|
95
|
+
|
96
|
+
|
97
|
+
def merge_name(d):
|
98
|
+
if isinstance(d, list):
|
99
|
+
return [merge_name(x) for x in d]
|
100
|
+
if isinstance(d, dict):
|
101
|
+
ret = {}
|
102
|
+
for k, v in d.items():
|
103
|
+
v = merge_name(v)
|
104
|
+
if isinstance(v, dict) and "_name" in v:
|
105
|
+
_name = v.pop("_name")
|
106
|
+
_name = _name.split("@")[0].strip()
|
107
|
+
k = f"{k}[{_name}]"
|
108
|
+
ret[k] = v
|
109
|
+
return ret
|
110
|
+
return d
|
111
|
+
|
112
|
+
|
113
|
+
def dump_dict(d, as_json=False):
|
114
|
+
if as_json:
|
115
|
+
print(json.dumps(d, indent=2))
|
116
|
+
else:
|
117
|
+
yaml.safe_dump(
|
118
|
+
merge_name(json.loads(json.dumps(d))),
|
119
|
+
sys.stdout,
|
120
|
+
default_flow_style=False,
|
121
|
+
)
|
@@ -0,0 +1,191 @@
|
|
1
|
+
"""Instrumentors for debugging permissions."""
|
2
|
+
|
3
|
+
import contextvars
|
4
|
+
import inspect
|
5
|
+
import json
|
6
|
+
import sys
|
7
|
+
|
8
|
+
from invenio_records_permissions.generators import ConditionalGenerator, Generator
|
9
|
+
|
10
|
+
|
11
|
+
def generator_to_debug_dict(self: Generator):
|
12
|
+
ret = {
|
13
|
+
"name": self.__class__.__name__,
|
14
|
+
}
|
15
|
+
ret = {}
|
16
|
+
for fld in self.__dict__:
|
17
|
+
if fld.startswith("__"):
|
18
|
+
continue
|
19
|
+
if fld in ("then_", "else_"):
|
20
|
+
continue
|
21
|
+
value = self.__dict__[fld]
|
22
|
+
if not isinstance(value, (str, int, float, bool)):
|
23
|
+
value = str(value)
|
24
|
+
ret[fld] = value
|
25
|
+
|
26
|
+
return {self.__class__.__name__: ret}
|
27
|
+
|
28
|
+
|
29
|
+
def conditional_generator_to_debug_dict(self: ConditionalGenerator):
|
30
|
+
ret = generator_to_debug_dict(self)
|
31
|
+
r = ret[self.__class__.__name__]
|
32
|
+
if self.then_:
|
33
|
+
r["then"] = [x.to_debug_dict() for x in self.then_]
|
34
|
+
if self.else_:
|
35
|
+
r["else"] = [x.to_debug_dict() for x in self.else_]
|
36
|
+
return ret
|
37
|
+
|
38
|
+
|
39
|
+
def get_all_generators():
|
40
|
+
generator_classes = set()
|
41
|
+
queue = [Generator]
|
42
|
+
while queue:
|
43
|
+
gen = queue.pop()
|
44
|
+
generator_classes.add(gen)
|
45
|
+
for cls in gen.__subclasses__():
|
46
|
+
if cls in generator_classes:
|
47
|
+
continue
|
48
|
+
queue.append(cls)
|
49
|
+
return generator_classes
|
50
|
+
|
51
|
+
|
52
|
+
debugging_level = contextvars.ContextVar("debugging_level", default=0)
|
53
|
+
|
54
|
+
|
55
|
+
def debug_needs_output(clz, method_name):
|
56
|
+
method = getattr(clz, method_name)
|
57
|
+
|
58
|
+
def wrapper(self, *args, **kwargs):
|
59
|
+
dd = json.dumps(self.to_debug_dict())
|
60
|
+
print(f"{' ' * debugging_level.get()}{dd}.{method_name} ->", file=sys.stderr)
|
61
|
+
reset = debugging_level.set(debugging_level.get() + 1)
|
62
|
+
ret = method(self, *args, **kwargs)
|
63
|
+
debugging_level.reset(reset)
|
64
|
+
if "debug_identity" in kwargs:
|
65
|
+
matched_needs = set(ret) & set(kwargs["debug_identity"].provides)
|
66
|
+
print(
|
67
|
+
f"{' ' * debugging_level.get()} -> match: {matched_needs}",
|
68
|
+
file=sys.stderr,
|
69
|
+
)
|
70
|
+
else:
|
71
|
+
print(f"{' ' * debugging_level.get()} -> {ret}", file=sys.stderr)
|
72
|
+
return ret
|
73
|
+
|
74
|
+
return wrapper
|
75
|
+
|
76
|
+
|
77
|
+
def debug_search_output(clz, method_name, print_search):
|
78
|
+
method = getattr(clz, method_name)
|
79
|
+
|
80
|
+
def wrapper(self, *args, **kwargs):
|
81
|
+
dd = json.dumps(self.to_debug_dict())
|
82
|
+
if print_search:
|
83
|
+
print(f"{' ' * debugging_level.get()}{dd}.{method_name}:", file=sys.stderr)
|
84
|
+
reset = debugging_level.set(debugging_level.get() + 2)
|
85
|
+
ret = method(self, *args, **kwargs)
|
86
|
+
debugging_level.reset(reset)
|
87
|
+
if isinstance(ret, list):
|
88
|
+
r = merge_communities([x.to_dict() for x in ret])
|
89
|
+
elif ret:
|
90
|
+
r = merge_communities(ret.to_dict())
|
91
|
+
else:
|
92
|
+
r = None
|
93
|
+
if print_search:
|
94
|
+
print(
|
95
|
+
f"{' ' * debugging_level.get()}{dd}.{method_name} -> {r}",
|
96
|
+
file=sys.stderr,
|
97
|
+
)
|
98
|
+
return ret
|
99
|
+
|
100
|
+
wrapper.__module__ = clz.__module__
|
101
|
+
wrapper.__qualname__ = f"{clz.__qualname__}.{method_name}"
|
102
|
+
wrapper.__name__ = method_name
|
103
|
+
return wrapper
|
104
|
+
|
105
|
+
|
106
|
+
def merge_communities(x):
|
107
|
+
if isinstance(x, list):
|
108
|
+
return [merge_communities(y) for y in x]
|
109
|
+
if isinstance(x, dict):
|
110
|
+
ret = {k: merge_communities(v) for k, v in x.items()}
|
111
|
+
if "parent.communities.default" in ret:
|
112
|
+
ret["parent.communities.default"] = "#communities#"
|
113
|
+
if "parent.communities.ids" in ret:
|
114
|
+
ret["parent.communities.ids"] = "#communities#"
|
115
|
+
return ret
|
116
|
+
return x
|
117
|
+
|
118
|
+
|
119
|
+
def get_opensearch_caller():
|
120
|
+
# Get the current call stack
|
121
|
+
stack = inspect.stack()
|
122
|
+
# Extract function names from the stack frames
|
123
|
+
function_names = []
|
124
|
+
state = "skipping_to_opensearch"
|
125
|
+
for frame in stack:
|
126
|
+
module_name = frame.frame.f_globals["__name__"]
|
127
|
+
if state == "skipping_to_opensearch":
|
128
|
+
if module_name.startswith("opensearch_dsl.") or module_name.startswith(
|
129
|
+
"oarepo_runtime.info"
|
130
|
+
):
|
131
|
+
state = "found_opensearch"
|
132
|
+
if state == "found_opensearch":
|
133
|
+
if not module_name.startswith(
|
134
|
+
"opensearch_dsl."
|
135
|
+
) and not module_name.startswith("oarepo_runtime.info"):
|
136
|
+
state = "outside_opensearch"
|
137
|
+
if state == "outside_opensearch":
|
138
|
+
if frame.function == "<lambda>":
|
139
|
+
continue
|
140
|
+
if "self" in frame.frame.f_locals:
|
141
|
+
self_instance = frame.frame.f_locals["self"]
|
142
|
+
class_name = self_instance.__class__.__name__
|
143
|
+
function_names.append(
|
144
|
+
f"{class_name}.{frame.function} @ {frame.filename}:{frame.lineno}"
|
145
|
+
)
|
146
|
+
else:
|
147
|
+
function_names.append(
|
148
|
+
f"{frame.function} @ {frame.filename}:{frame.lineno}"
|
149
|
+
)
|
150
|
+
del frame
|
151
|
+
|
152
|
+
return function_names
|
153
|
+
|
154
|
+
|
155
|
+
def add_debugging(print_needs=True, print_excludes=True, print_search=True):
|
156
|
+
for generator in get_all_generators():
|
157
|
+
if issubclass(generator, ConditionalGenerator):
|
158
|
+
generator.to_debug_dict = conditional_generator_to_debug_dict
|
159
|
+
else:
|
160
|
+
generator.to_debug_dict = generator_to_debug_dict
|
161
|
+
if print_needs and not hasattr(generator.needs, "__is_debug_instrumented__"):
|
162
|
+
generator.needs = debug_needs_output(generator, "needs")
|
163
|
+
generator.needs.__is_debug_instrumented__ = True
|
164
|
+
if print_excludes and not hasattr(
|
165
|
+
generator.excludes, "__is_debug_instrumented__"
|
166
|
+
):
|
167
|
+
generator.excludes = debug_needs_output(generator, "excludes")
|
168
|
+
generator.excludes.__is_debug_instrumented__ = True
|
169
|
+
|
170
|
+
if hasattr(generator, "query_filters"):
|
171
|
+
if not hasattr(generator.query_filters, "__is_debug_instrumented__"):
|
172
|
+
generator.query_filters = debug_search_output(
|
173
|
+
generator, "query_filters", print_search
|
174
|
+
)
|
175
|
+
generator.query_filters.__is_debug_instrumented__ = True
|
176
|
+
if hasattr(generator, "query_filter"):
|
177
|
+
if not hasattr(generator.query_filter, "__is_debug_instrumented__"):
|
178
|
+
generator.query_filter = debug_search_output(
|
179
|
+
generator, "query_filter", print_search
|
180
|
+
)
|
181
|
+
generator.query_filter.__is_debug_instrumented__ = True
|
182
|
+
# try to add _name to queries
|
183
|
+
from opensearch_dsl.query import Query
|
184
|
+
|
185
|
+
previous_init = Query.__init__
|
186
|
+
|
187
|
+
def new_init(self, *args, **kwargs):
|
188
|
+
previous_init(self, *args, **kwargs)
|
189
|
+
self._params["_name"] = get_opensearch_caller()[0]
|
190
|
+
|
191
|
+
Query.__init__ = new_init
|