GeneralManager 0.69.2__tar.gz → 0.70.0__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.
- {generalmanager-0.69.2/src/GeneralManager.egg-info → generalmanager-0.70.0}/PKG-INFO +1 -1
- {generalmanager-0.69.2 → generalmanager-0.70.0}/pyproject.toml +1 -1
- {generalmanager-0.69.2 → generalmanager-0.70.0/src/GeneralManager.egg-info}/PKG-INFO +1 -1
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/_types/permission.py +2 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/api/graphql_errors.py +8 -3
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/api/graphql_resolvers.py +48 -21
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/api/graphql_search.py +16 -4
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/permission/base_permission.py +104 -26
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/permission/manager_based_permission.py +290 -97
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/permission/permission_checks.py +140 -35
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/public_api_registry.py +4 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/LICENSE +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/README.md +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/setup.cfg +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/GeneralManager.egg-info/SOURCES.txt +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/GeneralManager.egg-info/dependency_links.txt +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/GeneralManager.egg-info/requires.txt +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/GeneralManager.egg-info/top_level.txt +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/__init__.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/_types/__init__.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/_types/api.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/_types/bucket.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/_types/cache.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/_types/dataframes.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/_types/factory.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/_types/general_manager.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/_types/interface.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/_types/manager.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/_types/measurement.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/_types/rule.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/_types/search.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/_types/utils.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/api/__init__.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/api/graphql.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/api/graphql_as_of.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/api/graphql_mutations.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/api/graphql_prefetch.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/api/graphql_relations.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/api/graphql_subscription_consumer.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/api/graphql_subscriptions.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/api/graphql_view.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/api/graphql_warmup.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/api/graphql_warmup_registry.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/api/graphql_warmup_tasks.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/api/mutation.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/api/notification_batching.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/api/property.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/api/registry.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/api/remote_api.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/api/remote_invalidation.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/api/remote_invalidation_client.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/apps.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/as_of.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/bootstrap.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/bucket/__init__.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/bucket/base_bucket.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/bucket/calculation_bucket.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/bucket/database_bucket.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/bucket/group_bucket.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/bucket/indexing.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/bucket/request_bucket.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/cache/__init__.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/cache/cache_decorator.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/cache/cache_tracker.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/cache/data_change_context.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/cache/dependency_cache.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/cache/dependency_index.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/cache/dependency_matching.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/cache/dependency_publish.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/cache/dependency_shards.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/cache/model_dependency_collector.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/cache/run_context.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/cache/run_context_lru.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/cache/signals.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/chat/__init__.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/chat/audit.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/chat/bootstrap.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/chat/checks.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/chat/consumer.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/chat/errors.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/chat/evals/__init__.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/chat/evals/__main__.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/chat/evals/baseline.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/chat/evals/datasets/basic_queries.yaml +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/chat/evals/datasets/demo_readiness.yaml +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/chat/evals/datasets/edge_cases.yaml +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/chat/evals/datasets/follow_ups.yaml +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/chat/evals/datasets/large_schema.yaml +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/chat/evals/datasets/multi_hop.yaml +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/chat/evals/diagnostics.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/chat/evals/fingerprints.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/chat/evals/fixtures.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/chat/evals/judges/__init__.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/chat/evals/judges/answer_quality.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/chat/evals/judges/contract.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/chat/evals/judges/result_accuracy.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/chat/evals/judges/tool_sequence.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/chat/evals/readiness.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/chat/evals/runner.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/chat/evals/traces.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/chat/grounding.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/chat/models.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/chat/providers/__init__.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/chat/providers/_shared.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/chat/providers/anthropic.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/chat/providers/base.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/chat/providers/google.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/chat/providers/ollama.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/chat/providers/openai.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/chat/rate_limits.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/chat/routing.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/chat/schema_index.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/chat/settings.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/chat/signals.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/chat/system_prompt.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/chat/tool_metadata.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/chat/tools.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/chat/views.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/conf.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/dataframes/__init__.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/dataframes/measurements.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/factory/__init__.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/factory/auto_factory.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/factory/factories.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/factory/factory_methods.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/interface/__init__.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/interface/base_interface.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/interface/bundles/__init__.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/interface/bundles/calculation.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/interface/bundles/database.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/interface/bundles/remote_manager.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/interface/bundles/request.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/interface/capabilities/__init__.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/interface/capabilities/base.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/interface/capabilities/builtin.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/interface/capabilities/calculation/__init__.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/interface/capabilities/calculation/_compat.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/interface/capabilities/calculation/lifecycle.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/interface/capabilities/configuration.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/interface/capabilities/core/observability.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/interface/capabilities/core/utils.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/interface/capabilities/exceptions.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/interface/capabilities/existing_model/__init__.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/interface/capabilities/existing_model/_compat.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/interface/capabilities/existing_model/resolution.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/interface/capabilities/factory.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/interface/capabilities/orm/__init__.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/interface/capabilities/orm/_compat.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/interface/capabilities/orm/history.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/interface/capabilities/orm/lifecycle.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/interface/capabilities/orm/mutations.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/interface/capabilities/orm/support.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/interface/capabilities/orm_utils/__init__.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/interface/capabilities/orm_utils/django_manager_utils.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/interface/capabilities/orm_utils/field_descriptors.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/interface/capabilities/orm_utils/payload_normalizer.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/interface/capabilities/read_only/__init__.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/interface/capabilities/read_only/_compat.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/interface/capabilities/read_only/_types.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/interface/capabilities/read_only/lifecycle.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/interface/capabilities/read_only/management.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/interface/capabilities/registry.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/interface/capabilities/remote_manager.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/interface/capabilities/request/__init__.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/interface/infrastructure/__init__.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/interface/infrastructure/startup_hooks.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/interface/infrastructure/system_checks.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/interface/interfaces/__init__.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/interface/interfaces/calculation.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/interface/interfaces/database.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/interface/interfaces/existing_model.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/interface/interfaces/read_only.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/interface/interfaces/remote_manager.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/interface/interfaces/request.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/interface/manifests/__init__.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/interface/manifests/capability_builder.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/interface/manifests/capability_manifest.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/interface/manifests/capability_models.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/interface/orm_interface.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/interface/requests.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/interface/utils/__init__.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/interface/utils/database_interface_protocols.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/interface/utils/errors.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/interface/utils/history.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/interface/utils/models.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/logging.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/management/commands/chat_cleanup.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/management/commands/cleanup_upload_intents.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/management/commands/graphql_warmup.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/management/commands/graphql_warmup_refresh_due.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/management/commands/search_index.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/management/commands/search_reconcile.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/management/commands/seed_manager_landscape.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/management/commands/shell.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/management/commands/workflow_drain_outbox.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/management/commands/workflow_replay_dead_letters.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/manager/__init__.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/manager/general_manager.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/manager/group_manager.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/manager/input.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/manager/meta.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/measurement/__init__.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/measurement/measurement.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/measurement/measurement_field.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/metrics/__init__.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/metrics/graphql.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/migrations/0001_initial.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/migrations/0002_workflow_outbox_scaling_indexes.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/migrations/0003_workflow_execution_correlation_constraint.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/migrations/0004_chat_persistence_models.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/migrations/0004_search_index_state.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/migrations/0005_merge_chat_search.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/migrations/0006_chat_pending_confirmation_scoped_ids.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/migrations/0007_upload_intent.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/migrations/0008_upload_finalization_versions.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/migrations/0009_upload_cleanup_state.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/migrations/0010_chat_pending_confirmation_portable_uniqueness.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/migrations/0011_search_index_state_dirty_generation.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/migrations/__init__.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/models.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/permission/__init__.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/permission/audit.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/permission/graphql_capabilities.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/permission/mutation_permission.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/permission/permission_data_manager.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/permission/utils.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/py.typed +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/rule/__init__.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/rule/handler.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/rule/rule.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/search/__init__.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/search/async_tasks.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/search/backend.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/search/backend_registry.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/search/backends/__init__.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/search/backends/dev.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/search/backends/meilisearch.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/search/backends/opensearch.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/search/backends/typesense.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/search/checks.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/search/config.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/search/indexer.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/search/invalidation.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/search/m2m_invalidation.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/search/models.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/search/reconciliation.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/search/registry.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/search/tasks.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/search/utils.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/seeding/__init__.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/seeding/manager_landscape.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/uploads/__init__.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/uploads/adapters.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/uploads/checks.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/uploads/config.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/uploads/errors.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/uploads/finalization.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/uploads/graphql.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/uploads/graphql_types.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/uploads/metrics.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/uploads/models.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/uploads/public.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/uploads/s3.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/uploads/services.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/uploads/tokens.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/uploads/types.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/uploads/urls.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/uploads/views.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/utils/__init__.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/utils/args_to_kwargs.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/utils/filter_parser.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/utils/format_string.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/utils/json_encoder.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/utils/make_cache_key.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/utils/none_to_zero.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/utils/path_mapping.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/utils/public_api.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/utils/testing.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/utils/type_checks.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/workflow/__init__.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/workflow/actions.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/workflow/backend_registry.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/workflow/backends/__init__.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/workflow/backends/celery.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/workflow/backends/local.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/workflow/backends/n8n.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/workflow/config.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/workflow/engine.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/workflow/event_registry.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/workflow/events.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/workflow/models.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/workflow/signal_bridge.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/workflow/tasks.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/workflow/telemetry.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/tests/test_settings.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/tests/test_urls.py +0 -0
- {generalmanager-0.69.2 → generalmanager-0.70.0}/tests/testing_asgi.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: GeneralManager
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.70.0
|
|
4
4
|
Summary: Modular Django-based data management framework with ORM, GraphQL, fine-grained permissions, rule validation, calculations and caching.
|
|
5
5
|
Author-email: Tim Kleindick <tkleindick@yahoo.de>
|
|
6
6
|
License: MIT License
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "GeneralManager"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.70.0"
|
|
8
8
|
description = "Modular Django-based data management framework with ORM, GraphQL, fine-grained permissions, rule validation, calculations and caching."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
authors = [{ name = "Tim Kleindick", email = "tkleindick@yahoo.de" }]
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: GeneralManager
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.70.0
|
|
4
4
|
Summary: Modular Django-based data management framework with ORM, GraphQL, fine-grained permissions, rule validation, calculations and caching.
|
|
5
5
|
Author-email: Tim Kleindick <tkleindick@yahoo.de>
|
|
6
6
|
License: MIT License
|
|
@@ -16,6 +16,7 @@ __all__ = [
|
|
|
16
16
|
"OverrideManagerPermission",
|
|
17
17
|
"PermissionAuditEvent",
|
|
18
18
|
"PermissionDataManager",
|
|
19
|
+
"PermissionFilterDecision",
|
|
19
20
|
"configure_audit_logger",
|
|
20
21
|
"configure_audit_logger_from_settings",
|
|
21
22
|
"mutation_capability",
|
|
@@ -44,6 +45,7 @@ from general_manager.permission.manager_based_permission import (
|
|
|
44
45
|
)
|
|
45
46
|
from general_manager.permission.audit import PermissionAuditEvent
|
|
46
47
|
from general_manager.permission.permission_data_manager import PermissionDataManager
|
|
48
|
+
from general_manager.permission.permission_checks import PermissionFilterDecision
|
|
47
49
|
from general_manager.permission.audit import configure_audit_logger
|
|
48
50
|
from general_manager.permission.audit import configure_audit_logger_from_settings
|
|
49
51
|
from general_manager.permission.graphql_capabilities import mutation_capability
|
|
@@ -655,7 +655,7 @@ def get_read_permission_filter(
|
|
|
655
655
|
|
|
656
656
|
Returns:
|
|
657
657
|
A ``ReadPermissionPlan`` with ``filters``, ``requires_instance_check``,
|
|
658
|
-
and ``
|
|
658
|
+
``instance_check_reasons``, and ``decision`` fields. ``ReadPermissionPlan`` is an
|
|
659
659
|
internal adapter from ``general_manager.permission.base_permission``, not
|
|
660
660
|
a stable public import path, but generated resolvers rely on these
|
|
661
661
|
fields.
|
|
@@ -677,7 +677,8 @@ def get_read_permission_filter(
|
|
|
677
677
|
``("no_prefilter_backend",)``.
|
|
678
678
|
|
|
679
679
|
Managers without a permission factory receive an empty filter plan and
|
|
680
|
-
do not require instance checks
|
|
680
|
+
do not require instance checks, with an explicit ``"allow_all"``
|
|
681
|
+
decision. This is a default-allow read policy for
|
|
681
682
|
managers that do not define ``Permission``; no additional GraphQL
|
|
682
683
|
permission filtering or per-object read authorization is applied by this
|
|
683
684
|
helper. More generally, ``filters=[]`` with
|
|
@@ -715,4 +716,8 @@ def get_read_permission_filter(
|
|
|
715
716
|
)
|
|
716
717
|
if permission_attribute is not None:
|
|
717
718
|
raise InvalidReadPermissionConfigurationError(generalManagerClass.__name__)
|
|
718
|
-
return ReadPermissionPlan(
|
|
719
|
+
return ReadPermissionPlan(
|
|
720
|
+
filters=[],
|
|
721
|
+
requires_instance_check=False,
|
|
722
|
+
decision="allow_all",
|
|
723
|
+
)
|
{generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/api/graphql_resolvers.py
RENAMED
|
@@ -90,12 +90,17 @@ class UnsupportedExcludeNoneRelationFilterError(ValueError):
|
|
|
90
90
|
|
|
91
91
|
@dataclass(slots=True)
|
|
92
92
|
class ReadAuthorizationResult(Generic[GeneralManagerT]):
|
|
93
|
-
"""Aggregate outcome from GraphQL read prefiltering and row authorization.
|
|
93
|
+
"""Aggregate outcome from GraphQL read prefiltering and row authorization.
|
|
94
|
+
|
|
95
|
+
Counts remain ``None`` when authorization can return a bucket lazily without
|
|
96
|
+
inspecting or counting it. They are populated when a row-level gate actually
|
|
97
|
+
scans candidates; deny-all plans use known zero counts.
|
|
98
|
+
"""
|
|
94
99
|
|
|
95
100
|
queryset: Bucket[GeneralManagerT]
|
|
96
|
-
candidate_count: int
|
|
97
|
-
authorized_count: int
|
|
98
|
-
denied_count: int
|
|
101
|
+
candidate_count: int | None
|
|
102
|
+
authorized_count: int | None
|
|
103
|
+
denied_count: int | None
|
|
99
104
|
backend_shape: str
|
|
100
105
|
requires_instance_check: bool
|
|
101
106
|
instance_check_reasons: tuple[str, ...]
|
|
@@ -266,7 +271,7 @@ def apply_read_authorization(
|
|
|
266
271
|
final row gate runs and an aggregate log event is emitted only if the final
|
|
267
272
|
authorization result still requires instance checks.
|
|
268
273
|
Unrestricted plans that do not require instance checks return the original
|
|
269
|
-
queryset
|
|
274
|
+
queryset without evaluating it to compute counts.
|
|
270
275
|
"""
|
|
271
276
|
permission_plan = get_read_permission_filter(general_manager_class, info)
|
|
272
277
|
backend_shape = get_backend_shape(general_manager_class)
|
|
@@ -275,16 +280,40 @@ def apply_read_authorization(
|
|
|
275
280
|
backend_shape=backend_shape,
|
|
276
281
|
)
|
|
277
282
|
|
|
283
|
+
if permission_plan.decision == "deny_all":
|
|
284
|
+
return ReadAuthorizationResult(
|
|
285
|
+
queryset=queryset.none(),
|
|
286
|
+
candidate_count=0,
|
|
287
|
+
authorized_count=0,
|
|
288
|
+
denied_count=0,
|
|
289
|
+
backend_shape=backend_shape,
|
|
290
|
+
requires_instance_check=False,
|
|
291
|
+
instance_check_reasons=(),
|
|
292
|
+
)
|
|
293
|
+
|
|
294
|
+
if permission_plan.decision == "allow_all":
|
|
295
|
+
return ReadAuthorizationResult(
|
|
296
|
+
queryset=queryset,
|
|
297
|
+
candidate_count=None,
|
|
298
|
+
authorized_count=None,
|
|
299
|
+
denied_count=None,
|
|
300
|
+
backend_shape=backend_shape,
|
|
301
|
+
requires_instance_check=False,
|
|
302
|
+
instance_check_reasons=(),
|
|
303
|
+
)
|
|
304
|
+
|
|
278
305
|
filtered_queryset = queryset
|
|
279
|
-
if
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
306
|
+
if (
|
|
307
|
+
not permission_plan.requires_instance_check
|
|
308
|
+
and len(permission_plan.filters) == 1
|
|
309
|
+
and not permission_plan.filters[0].get("filter")
|
|
310
|
+
and not permission_plan.filters[0].get("exclude")
|
|
311
|
+
):
|
|
283
312
|
result = ReadAuthorizationResult(
|
|
284
313
|
queryset=queryset,
|
|
285
|
-
candidate_count=
|
|
286
|
-
authorized_count=
|
|
287
|
-
denied_count=
|
|
314
|
+
candidate_count=None,
|
|
315
|
+
authorized_count=None,
|
|
316
|
+
denied_count=None,
|
|
288
317
|
backend_shape=backend_shape,
|
|
289
318
|
requires_instance_check=False,
|
|
290
319
|
instance_check_reasons=instance_check_reasons,
|
|
@@ -332,19 +361,18 @@ def filter_queryset_by_read_permission(
|
|
|
332
361
|
Apply final row-level read authorization to a bucket.
|
|
333
362
|
|
|
334
363
|
When an instance gate is not required, or the manager has no Permission
|
|
335
|
-
class, the original queryset is returned
|
|
364
|
+
class, the original queryset is returned without eagerly computing counts.
|
|
336
365
|
Otherwise each candidate is checked with ``can_read_instance()``. Authorized
|
|
337
366
|
rows with an ``identification["id"]`` are rebuilt through an ``id__in``
|
|
338
367
|
filter; authorized rows without an id are unioned back as concrete manager
|
|
339
368
|
instances.
|
|
340
369
|
"""
|
|
341
370
|
if not requires_instance_check:
|
|
342
|
-
candidate_count = len(queryset)
|
|
343
371
|
return ReadAuthorizationResult(
|
|
344
372
|
queryset=queryset,
|
|
345
|
-
candidate_count=
|
|
346
|
-
authorized_count=
|
|
347
|
-
denied_count=
|
|
373
|
+
candidate_count=None,
|
|
374
|
+
authorized_count=None,
|
|
375
|
+
denied_count=None,
|
|
348
376
|
backend_shape=backend_shape,
|
|
349
377
|
requires_instance_check=False,
|
|
350
378
|
instance_check_reasons=instance_check_reasons,
|
|
@@ -354,12 +382,11 @@ def filter_queryset_by_read_permission(
|
|
|
354
382
|
general_manager_class, "Permission", None
|
|
355
383
|
)
|
|
356
384
|
if PermissionClass is None:
|
|
357
|
-
candidate_count = len(queryset)
|
|
358
385
|
return ReadAuthorizationResult(
|
|
359
386
|
queryset=queryset,
|
|
360
|
-
candidate_count=
|
|
361
|
-
authorized_count=
|
|
362
|
-
denied_count=
|
|
387
|
+
candidate_count=None,
|
|
388
|
+
authorized_count=None,
|
|
389
|
+
denied_count=None,
|
|
363
390
|
backend_shape=backend_shape,
|
|
364
391
|
requires_instance_check=False,
|
|
365
392
|
instance_check_reasons=instance_check_reasons,
|
|
@@ -382,6 +382,11 @@ def passes_permission_filters(
|
|
|
382
382
|
"""
|
|
383
383
|
if permission_plan is None:
|
|
384
384
|
permission_plan = get_read_permission_filter(instance.__class__, info)
|
|
385
|
+
decision = getattr(permission_plan, "decision", "conditional")
|
|
386
|
+
if decision == "allow_all":
|
|
387
|
+
return True
|
|
388
|
+
if decision == "deny_all":
|
|
389
|
+
return False
|
|
385
390
|
permission_filters = permission_plan.filters
|
|
386
391
|
if not permission_filters:
|
|
387
392
|
return can_read_instance(instance, info)
|
|
@@ -1046,14 +1051,21 @@ def register_search_query(
|
|
|
1046
1051
|
for manager_class in manager_classes:
|
|
1047
1052
|
type_label = manager_class.__name__
|
|
1048
1053
|
permission_plan = get_read_permission_filter(manager_class, info)
|
|
1054
|
+
decision = getattr(permission_plan, "decision", "conditional")
|
|
1055
|
+
if decision == "deny_all":
|
|
1056
|
+
continue
|
|
1049
1057
|
backend_shape = get_backend_shape(manager_class)
|
|
1050
1058
|
instance_check_reasons = resolve_instance_check_reasons(
|
|
1051
1059
|
permission_plan,
|
|
1052
1060
|
backend_shape=backend_shape,
|
|
1053
1061
|
)
|
|
1054
|
-
filter_groups =
|
|
1055
|
-
parsed_filters
|
|
1056
|
-
|
|
1062
|
+
filter_groups = (
|
|
1063
|
+
parsed_filters or None
|
|
1064
|
+
if decision == "allow_all"
|
|
1065
|
+
else merge_permission_filters(
|
|
1066
|
+
parsed_filters,
|
|
1067
|
+
permission_plan.filters,
|
|
1068
|
+
)
|
|
1057
1069
|
)
|
|
1058
1070
|
total_hits_for_manager = 0
|
|
1059
1071
|
candidate_hits_for_manager = 0
|
|
@@ -1127,7 +1139,7 @@ def register_search_query(
|
|
|
1127
1139
|
total += total_hits_for_manager
|
|
1128
1140
|
if not manager_total_is_exact:
|
|
1129
1141
|
total_is_exact = False
|
|
1130
|
-
if permission_plan.requires_instance_check:
|
|
1142
|
+
if decision == "conditional" and permission_plan.requires_instance_check:
|
|
1131
1143
|
logger.info(
|
|
1132
1144
|
"graphql read authorization summary",
|
|
1133
1145
|
context={
|
{generalmanager-0.69.2 → generalmanager-0.70.0}/src/general_manager/permission/base_permission.py
RENAMED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
from abc import ABC, abstractmethod
|
|
6
|
-
from collections.abc import Callable, Mapping
|
|
6
|
+
from collections.abc import Callable, Collection, Mapping
|
|
7
7
|
from dataclasses import dataclass
|
|
8
8
|
from typing import TYPE_CHECKING, Literal, TypeAlias, TypedDict, cast
|
|
9
9
|
|
|
@@ -16,7 +16,10 @@ from general_manager.permission.audit import (
|
|
|
16
16
|
audit_logging_enabled,
|
|
17
17
|
emit_permission_audit_event,
|
|
18
18
|
)
|
|
19
|
-
from general_manager.permission.permission_checks import
|
|
19
|
+
from general_manager.permission.permission_checks import (
|
|
20
|
+
PermissionFilterDecision,
|
|
21
|
+
permission_functions,
|
|
22
|
+
)
|
|
20
23
|
from general_manager.permission.permission_data_manager import PermissionDataManager
|
|
21
24
|
from general_manager.permission.utils import (
|
|
22
25
|
PermissionNotFoundError,
|
|
@@ -33,9 +36,15 @@ UserLike: TypeAlias = AbstractBaseUser | AnonymousUser
|
|
|
33
36
|
ReadPermissionReason: TypeAlias = Literal[
|
|
34
37
|
"unfilterable_read_rule",
|
|
35
38
|
"based_on_class_context",
|
|
39
|
+
"compound_exclude_semantics",
|
|
36
40
|
"filter_key_conflict",
|
|
37
41
|
"no_prefilter_backend",
|
|
38
42
|
]
|
|
43
|
+
ReadPermissionDecision: TypeAlias = Literal[
|
|
44
|
+
"allow_all",
|
|
45
|
+
"deny_all",
|
|
46
|
+
"conditional",
|
|
47
|
+
]
|
|
39
48
|
|
|
40
49
|
|
|
41
50
|
class PermissionConstraint(TypedDict, total=False):
|
|
@@ -98,6 +107,9 @@ class ReadPermissionPlan:
|
|
|
98
107
|
least one read rule could not be converted into a backend prefilter.
|
|
99
108
|
``"based_on_class_context"`` means delegated permission logic required
|
|
100
109
|
class-level context that cannot be represented as a row prefilter.
|
|
110
|
+
``"compound_exclude_semantics"`` means multiple ANDed exclusion predicates
|
|
111
|
+
were conservatively merged into one safe prefilter but require row-level
|
|
112
|
+
authorization to preserve sequential exclusion semantics.
|
|
101
113
|
``"filter_key_conflict"`` means generated filter keys conflicted while
|
|
102
114
|
composing permission filters. ``"no_prefilter_backend"`` means the interface
|
|
103
115
|
backend cannot enforce read prefilters fully. Reason tuples are request
|
|
@@ -106,11 +118,19 @@ class ReadPermissionPlan:
|
|
|
106
118
|
``requires_instance_check=False`` should normally use an empty reasons tuple.
|
|
107
119
|
Non-empty reasons in that state are diagnostic only and do not force an
|
|
108
120
|
instance check by themselves.
|
|
121
|
+
|
|
122
|
+
``decision`` describes the complete read authorization outcome.
|
|
123
|
+
``"allow_all"`` and ``"deny_all"`` are definitive and therefore use no
|
|
124
|
+
prefilters or instance check. ``"conditional"`` means authorization still
|
|
125
|
+
depends on the listed row constraints, an instance check, or both. The
|
|
126
|
+
default remains conditional so legacy constructors retain their safe
|
|
127
|
+
behavior.
|
|
109
128
|
"""
|
|
110
129
|
|
|
111
130
|
filters: list[PermissionConstraint]
|
|
112
131
|
requires_instance_check: bool = True
|
|
113
132
|
instance_check_reasons: tuple[ReadPermissionReason, ...] = ()
|
|
133
|
+
decision: ReadPermissionDecision = "conditional"
|
|
114
134
|
|
|
115
135
|
|
|
116
136
|
class PermissionCheckError(PermissionError):
|
|
@@ -131,6 +151,29 @@ class PermissionCheckError(PermissionError):
|
|
|
131
151
|
)
|
|
132
152
|
|
|
133
153
|
|
|
154
|
+
class _InvalidPermissionFilterResultError(TypeError):
|
|
155
|
+
"""Raised when a registered read-filter callback returns an invalid shape."""
|
|
156
|
+
|
|
157
|
+
def __init__(
|
|
158
|
+
self,
|
|
159
|
+
permission: str,
|
|
160
|
+
result: object,
|
|
161
|
+
*,
|
|
162
|
+
part_name: str | None = None,
|
|
163
|
+
unsupported_keys: Collection[object] | None = None,
|
|
164
|
+
) -> None:
|
|
165
|
+
if unsupported_keys:
|
|
166
|
+
rendered_keys = ", ".join(sorted(repr(key) for key in unsupported_keys))
|
|
167
|
+
detail = f"unsupported constraint keys: {rendered_keys}"
|
|
168
|
+
elif part_name is None:
|
|
169
|
+
detail = f"unsupported {type(result).__name__}"
|
|
170
|
+
else:
|
|
171
|
+
detail = f"a non-mapping '{part_name}' constraint"
|
|
172
|
+
super().__init__(
|
|
173
|
+
f"Permission filter for fragment '{permission}' returned {detail}."
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
|
|
134
177
|
class BasePermission(ABC):
|
|
135
178
|
"""Abstract base class defining CRUD permission checks for managers."""
|
|
136
179
|
|
|
@@ -604,25 +647,67 @@ class BasePermission(ABC):
|
|
|
604
647
|
PermissionConstraint: A mapping with optional ``"filter"`` and
|
|
605
648
|
``"exclude"`` dictionaries whose lookup values are typed as
|
|
606
649
|
``object``. Superusers and unfilterable permissions that return
|
|
607
|
-
``None`` both resolve to empty ``filter`` and
|
|
650
|
+
``None`` or a static decision both resolve to empty ``filter`` and
|
|
651
|
+
``exclude`` mappings.
|
|
608
652
|
|
|
609
653
|
Raises:
|
|
610
654
|
PermissionNotFoundError: If no permission function matches the leading name in `permission`.
|
|
611
655
|
"""
|
|
612
|
-
|
|
656
|
+
permission_filter = self._get_permission_filter_result(permission)
|
|
657
|
+
if permission_filter is None or isinstance(
|
|
658
|
+
permission_filter, PermissionFilterDecision
|
|
659
|
+
):
|
|
613
660
|
return {"filter": {}, "exclude": {}}
|
|
661
|
+
return permission_filter
|
|
662
|
+
|
|
663
|
+
def _get_permission_filter_result(
|
|
664
|
+
self,
|
|
665
|
+
permission: str,
|
|
666
|
+
) -> PermissionConstraint | PermissionFilterDecision | None:
|
|
667
|
+
"""Resolve and normalize one permission-filter callback result.
|
|
668
|
+
|
|
669
|
+
Static decisions and ``None`` are preserved for read-plan composition.
|
|
670
|
+
Mapping results are copied into plain constraint dictionaries. Invalid
|
|
671
|
+
callback results fail here so query planning never accidentally treats
|
|
672
|
+
them as authorization constraints.
|
|
673
|
+
"""
|
|
674
|
+
if self._is_superuser():
|
|
675
|
+
return PermissionFilterDecision.ALLOW_ALL
|
|
614
676
|
permission_function, *config = permission.split(":")
|
|
615
677
|
if permission_function not in permission_functions:
|
|
616
678
|
raise PermissionNotFoundError(permission)
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
self.request_user, config
|
|
621
|
-
),
|
|
679
|
+
result: object = permission_functions[permission_function]["permission_filter"](
|
|
680
|
+
self.request_user,
|
|
681
|
+
config,
|
|
622
682
|
)
|
|
623
|
-
if
|
|
624
|
-
return
|
|
625
|
-
|
|
683
|
+
if result is None or isinstance(result, PermissionFilterDecision):
|
|
684
|
+
return result
|
|
685
|
+
if not isinstance(result, Mapping):
|
|
686
|
+
raise _InvalidPermissionFilterResultError(permission, result)
|
|
687
|
+
unsupported_keys = set(result).difference(("filter", "exclude"))
|
|
688
|
+
if unsupported_keys:
|
|
689
|
+
raise _InvalidPermissionFilterResultError(
|
|
690
|
+
permission,
|
|
691
|
+
result,
|
|
692
|
+
unsupported_keys=unsupported_keys,
|
|
693
|
+
)
|
|
694
|
+
|
|
695
|
+
constraint: PermissionConstraint = {}
|
|
696
|
+
for part_name in ("filter", "exclude"):
|
|
697
|
+
if part_name not in result:
|
|
698
|
+
continue
|
|
699
|
+
part = result[part_name]
|
|
700
|
+
if not isinstance(part, Mapping):
|
|
701
|
+
raise _InvalidPermissionFilterResultError(
|
|
702
|
+
permission,
|
|
703
|
+
result,
|
|
704
|
+
part_name=part_name,
|
|
705
|
+
)
|
|
706
|
+
if part_name == "filter":
|
|
707
|
+
constraint["filter"] = dict(part)
|
|
708
|
+
else:
|
|
709
|
+
constraint["exclude"] = dict(part)
|
|
710
|
+
return constraint
|
|
626
711
|
|
|
627
712
|
def _get_permission_filter_info(
|
|
628
713
|
self, permission: str
|
|
@@ -631,8 +716,9 @@ class BasePermission(ABC):
|
|
|
631
716
|
Resolve filter/exclude constraints and whether the permission is query-filterable.
|
|
632
717
|
|
|
633
718
|
Superusers return empty filter/exclude mappings marked filterable. When
|
|
634
|
-
a permission filter returns ``None
|
|
635
|
-
``False`` to signal that callers must keep
|
|
719
|
+
a permission filter returns ``None`` or a static decision, empty
|
|
720
|
+
mappings are returned with ``False`` to signal that callers must keep
|
|
721
|
+
a row-level instance check until static decisions are planned directly.
|
|
636
722
|
|
|
637
723
|
Returns:
|
|
638
724
|
``(constraint, is_filterable)``.
|
|
@@ -641,18 +727,10 @@ class BasePermission(ABC):
|
|
|
641
727
|
PermissionNotFoundError: If no registered permission function
|
|
642
728
|
matches the leading permission name.
|
|
643
729
|
"""
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
raise PermissionNotFoundError(permission)
|
|
649
|
-
permission_filter = cast(
|
|
650
|
-
PermissionConstraint | None,
|
|
651
|
-
permission_functions[permission_function]["permission_filter"](
|
|
652
|
-
self.request_user, config
|
|
653
|
-
),
|
|
654
|
-
)
|
|
655
|
-
if permission_filter is None:
|
|
730
|
+
permission_filter = self._get_permission_filter_result(permission)
|
|
731
|
+
if permission_filter is None or isinstance(
|
|
732
|
+
permission_filter, PermissionFilterDecision
|
|
733
|
+
):
|
|
656
734
|
return {"filter": {}, "exclude": {}}, False
|
|
657
735
|
return permission_filter, True
|
|
658
736
|
|