mainsequence 4.3.22__tar.gz → 4.3.25__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.
- {mainsequence-4.3.22/mainsequence.egg-info → mainsequence-4.3.25}/PKG-INFO +1 -1
- {mainsequence-4.3.22 → mainsequence-4.3.25}/mainsequence/cli/api.py +20 -12
- {mainsequence-4.3.22 → mainsequence-4.3.25}/mainsequence/cli/cli.py +2 -2
- {mainsequence-4.3.22 → mainsequence-4.3.25}/mainsequence/client/agent_runtime_models.py +2 -0
- {mainsequence-4.3.22 → mainsequence-4.3.25}/mainsequence/client/fastapi/auth.py +7 -3
- {mainsequence-4.3.22 → mainsequence-4.3.25}/mainsequence/client/metatables/core.py +2 -2
- {mainsequence-4.3.22 → mainsequence-4.3.25}/mainsequence/client/models_user.py +58 -11
- {mainsequence-4.3.22 → mainsequence-4.3.25/mainsequence.egg-info}/PKG-INFO +1 -1
- {mainsequence-4.3.22 → mainsequence-4.3.25}/pyproject.toml +1 -1
- {mainsequence-4.3.22 → mainsequence-4.3.25}/tests/test_cli.py +15 -10
- {mainsequence-4.3.22 → mainsequence-4.3.25}/tests/test_meta_tables_client_models.py +68 -4
- {mainsequence-4.3.22 → mainsequence-4.3.25}/tests/test_models_user_request_bound_auth.py +92 -0
- {mainsequence-4.3.22 → mainsequence-4.3.25}/LICENSE +0 -0
- {mainsequence-4.3.22 → mainsequence-4.3.25}/README.md +0 -0
- {mainsequence-4.3.22 → mainsequence-4.3.25}/agent_scaffold/AGENTS.md +0 -0
- {mainsequence-4.3.22 → mainsequence-4.3.25}/agent_scaffold/skills/a2a_communication/SKILL.md +0 -0
- {mainsequence-4.3.22 → mainsequence-4.3.25}/agent_scaffold/skills/application_surfaces/api_surfaces/SKILL.md +0 -0
- {mainsequence-4.3.22 → mainsequence-4.3.25}/agent_scaffold/skills/command_center/adapter_from_api/SKILL.md +0 -0
- {mainsequence-4.3.22 → mainsequence-4.3.25}/agent_scaffold/skills/command_center/api_mock_prototyping/SKILL.md +0 -0
- {mainsequence-4.3.22 → mainsequence-4.3.25}/agent_scaffold/skills/command_center/app_components/SKILL.md +0 -0
- {mainsequence-4.3.22 → mainsequence-4.3.25}/agent_scaffold/skills/command_center/connections/SKILL.md +0 -0
- {mainsequence-4.3.22 → mainsequence-4.3.25}/agent_scaffold/skills/command_center/workspace_analysis/SKILL.md +0 -0
- {mainsequence-4.3.22 → mainsequence-4.3.25}/agent_scaffold/skills/command_center/workspace_builder/SKILL.md +0 -0
- {mainsequence-4.3.22 → mainsequence-4.3.25}/agent_scaffold/skills/command_center/workspace_design/SKILL.md +0 -0
- {mainsequence-4.3.22 → mainsequence-4.3.25}/agent_scaffold/skills/dashboards/streamlit/SKILL.md +0 -0
- {mainsequence-4.3.22 → mainsequence-4.3.25}/agent_scaffold/skills/data_access/exploration/SKILL.md +0 -0
- {mainsequence-4.3.22 → mainsequence-4.3.25}/agent_scaffold/skills/data_publishing/data_nodes/SKILL.md +0 -0
- {mainsequence-4.3.22 → mainsequence-4.3.25}/agent_scaffold/skills/data_publishing/meta_table_migrations/SKILL.md +0 -0
- {mainsequence-4.3.22 → mainsequence-4.3.25}/agent_scaffold/skills/data_publishing/meta_tables/SKILL.md +0 -0
- {mainsequence-4.3.22 → mainsequence-4.3.25}/agent_scaffold/skills/maintenance/bug_auditor/SKILL.md +0 -0
- {mainsequence-4.3.22 → mainsequence-4.3.25}/agent_scaffold/skills/ms-markets/SKILL.md +0 -0
- {mainsequence-4.3.22 → mainsequence-4.3.25}/agent_scaffold/skills/platform_operations/access_control_and_sharing/SKILL.md +0 -0
- {mainsequence-4.3.22 → mainsequence-4.3.25}/agent_scaffold/skills/platform_operations/orchestration_and_releases/SKILL.md +0 -0
- {mainsequence-4.3.22 → mainsequence-4.3.25}/agent_scaffold/skills/project_builder/SKILL.md +0 -0
- {mainsequence-4.3.22 → mainsequence-4.3.25}/agent_scaffold/skills/project_to_agent/SKILL.md +0 -0
- {mainsequence-4.3.22 → mainsequence-4.3.25}/mainsequence/__init__.py +0 -0
- {mainsequence-4.3.22 → mainsequence-4.3.25}/mainsequence/__main__.py +0 -0
- {mainsequence-4.3.22 → mainsequence-4.3.25}/mainsequence/bootstrap.py +0 -0
- {mainsequence-4.3.22 → mainsequence-4.3.25}/mainsequence/cli/__init__.py +0 -0
- {mainsequence-4.3.22 → mainsequence-4.3.25}/mainsequence/cli/browser_auth.py +0 -0
- {mainsequence-4.3.22 → mainsequence-4.3.25}/mainsequence/cli/config.py +0 -0
- {mainsequence-4.3.22 → mainsequence-4.3.25}/mainsequence/cli/docker_utils.py +0 -0
- {mainsequence-4.3.22 → mainsequence-4.3.25}/mainsequence/cli/doctor.py +0 -0
- {mainsequence-4.3.22 → mainsequence-4.3.25}/mainsequence/cli/local_ops.py +0 -0
- {mainsequence-4.3.22 → mainsequence-4.3.25}/mainsequence/cli/migrations.py +0 -0
- {mainsequence-4.3.22 → mainsequence-4.3.25}/mainsequence/cli/model_filters.py +0 -0
- {mainsequence-4.3.22 → mainsequence-4.3.25}/mainsequence/cli/project_status.py +0 -0
- {mainsequence-4.3.22 → mainsequence-4.3.25}/mainsequence/cli/pydantic_cli.py +0 -0
- {mainsequence-4.3.22 → mainsequence-4.3.25}/mainsequence/cli/sdk_utils.py +0 -0
- {mainsequence-4.3.22 → mainsequence-4.3.25}/mainsequence/cli/ssh_utils.py +0 -0
- {mainsequence-4.3.22 → mainsequence-4.3.25}/mainsequence/cli/ui.py +0 -0
- {mainsequence-4.3.22 → mainsequence-4.3.25}/mainsequence/client/__init__.py +0 -0
- {mainsequence-4.3.22 → mainsequence-4.3.25}/mainsequence/client/base.py +0 -0
- {mainsequence-4.3.22 → mainsequence-4.3.25}/mainsequence/client/client.py +0 -0
- {mainsequence-4.3.22 → mainsequence-4.3.25}/mainsequence/client/command_center/__init__.py +0 -0
- {mainsequence-4.3.22 → mainsequence-4.3.25}/mainsequence/client/command_center/app_component.py +0 -0
- {mainsequence-4.3.22 → mainsequence-4.3.25}/mainsequence/client/command_center/connections.py +0 -0
- {mainsequence-4.3.22 → mainsequence-4.3.25}/mainsequence/client/command_center/data_models.py +0 -0
- {mainsequence-4.3.22 → mainsequence-4.3.25}/mainsequence/client/command_center/workspace.py +0 -0
- {mainsequence-4.3.22 → mainsequence-4.3.25}/mainsequence/client/command_center/workspace_snapshot.py +0 -0
- {mainsequence-4.3.22 → mainsequence-4.3.25}/mainsequence/client/compute_validation.py +0 -0
- {mainsequence-4.3.22 → mainsequence-4.3.25}/mainsequence/client/data_sources_interfaces/__init__.py +0 -0
- {mainsequence-4.3.22 → mainsequence-4.3.25}/mainsequence/client/data_sources_interfaces/duckdb.py +0 -0
- {mainsequence-4.3.22 → mainsequence-4.3.25}/mainsequence/client/data_sources_interfaces/local_paths.py +0 -0
- {mainsequence-4.3.22 → mainsequence-4.3.25}/mainsequence/client/data_sources_interfaces/sqlite.py +0 -0
- {mainsequence-4.3.22 → mainsequence-4.3.25}/mainsequence/client/dtype_codec.py +0 -0
- {mainsequence-4.3.22 → mainsequence-4.3.25}/mainsequence/client/exceptions.py +0 -0
- {mainsequence-4.3.22 → mainsequence-4.3.25}/mainsequence/client/fastapi/__init__.py +0 -0
- {mainsequence-4.3.22 → mainsequence-4.3.25}/mainsequence/client/metatables/__init__.py +0 -0
- {mainsequence-4.3.22 → mainsequence-4.3.25}/mainsequence/client/models_foundry.py +0 -0
- {mainsequence-4.3.22 → mainsequence-4.3.25}/mainsequence/client/models_helpers.py +0 -0
- {mainsequence-4.3.22 → mainsequence-4.3.25}/mainsequence/client/utils.py +0 -0
- {mainsequence-4.3.22 → mainsequence-4.3.25}/mainsequence/defaults.py +0 -0
- {mainsequence-4.3.22 → mainsequence-4.3.25}/mainsequence/instrumentation/__init__.py +0 -0
- {mainsequence-4.3.22 → mainsequence-4.3.25}/mainsequence/instrumentation/utils.py +0 -0
- {mainsequence-4.3.22 → mainsequence-4.3.25}/mainsequence/logconf.py +0 -0
- {mainsequence-4.3.22 → mainsequence-4.3.25}/mainsequence/meta_tables/__init__.py +0 -0
- {mainsequence-4.3.22 → mainsequence-4.3.25}/mainsequence/meta_tables/__main__.py +0 -0
- {mainsequence-4.3.22 → mainsequence-4.3.25}/mainsequence/meta_tables/compiled_sql/__init__.py +0 -0
- {mainsequence-4.3.22 → mainsequence-4.3.25}/mainsequence/meta_tables/compiled_sql/v1.py +0 -0
- {mainsequence-4.3.22 → mainsequence-4.3.25}/mainsequence/meta_tables/data_nodes/__init__.py +0 -0
- {mainsequence-4.3.22 → mainsequence-4.3.25}/mainsequence/meta_tables/data_nodes/build_operations.py +0 -0
- {mainsequence-4.3.22 → mainsequence-4.3.25}/mainsequence/meta_tables/data_nodes/data_nodes.py +0 -0
- {mainsequence-4.3.22 → mainsequence-4.3.25}/mainsequence/meta_tables/data_nodes/models.py +0 -0
- {mainsequence-4.3.22 → mainsequence-4.3.25}/mainsequence/meta_tables/data_nodes/namespacing.py +0 -0
- {mainsequence-4.3.22 → mainsequence-4.3.25}/mainsequence/meta_tables/data_nodes/persist_managers.py +0 -0
- {mainsequence-4.3.22 → mainsequence-4.3.25}/mainsequence/meta_tables/data_nodes/run_operations.py +0 -0
- {mainsequence-4.3.22 → mainsequence-4.3.25}/mainsequence/meta_tables/data_nodes/utils.py +0 -0
- {mainsequence-4.3.22 → mainsequence-4.3.25}/mainsequence/meta_tables/future_registry.py +0 -0
- {mainsequence-4.3.22 → mainsequence-4.3.25}/mainsequence/meta_tables/hashing.py +0 -0
- {mainsequence-4.3.22 → mainsequence-4.3.25}/mainsequence/meta_tables/migrations/__init__.py +0 -0
- {mainsequence-4.3.22 → mainsequence-4.3.25}/mainsequence/meta_tables/migrations/alembic.py +0 -0
- {mainsequence-4.3.22 → mainsequence-4.3.25}/mainsequence/meta_tables/migrations/env.py +0 -0
- {mainsequence-4.3.22 → mainsequence-4.3.25}/mainsequence/meta_tables/migrations/provider.py +0 -0
- {mainsequence-4.3.22 → mainsequence-4.3.25}/mainsequence/meta_tables/migrations/registry.py +0 -0
- {mainsequence-4.3.22 → mainsequence-4.3.25}/mainsequence/meta_tables/migrations/scaffold.py +0 -0
- {mainsequence-4.3.22 → mainsequence-4.3.25}/mainsequence/meta_tables/migrations/templates/__init__.py +0 -0
- {mainsequence-4.3.22 → mainsequence-4.3.25}/mainsequence/meta_tables/migrations/templates/env.py.mako +0 -0
- {mainsequence-4.3.22 → mainsequence-4.3.25}/mainsequence/meta_tables/migrations/templates/script.py.mako +0 -0
- {mainsequence-4.3.22 → mainsequence-4.3.25}/mainsequence/meta_tables/pydantic_metadata.py +0 -0
- {mainsequence-4.3.22 → mainsequence-4.3.25}/mainsequence/meta_tables/schema_names.py +0 -0
- {mainsequence-4.3.22 → mainsequence-4.3.25}/mainsequence/meta_tables/sqlalchemy_contracts.py +0 -0
- {mainsequence-4.3.22 → mainsequence-4.3.25}/mainsequence/runtime_flags.py +0 -0
- {mainsequence-4.3.22 → mainsequence-4.3.25}/mainsequence.egg-info/SOURCES.txt +0 -0
- {mainsequence-4.3.22 → mainsequence-4.3.25}/mainsequence.egg-info/dependency_links.txt +0 -0
- {mainsequence-4.3.22 → mainsequence-4.3.25}/mainsequence.egg-info/entry_points.txt +0 -0
- {mainsequence-4.3.22 → mainsequence-4.3.25}/mainsequence.egg-info/requires.txt +0 -0
- {mainsequence-4.3.22 → mainsequence-4.3.25}/mainsequence.egg-info/top_level.txt +0 -0
- {mainsequence-4.3.22 → mainsequence-4.3.25}/setup.cfg +0 -0
- {mainsequence-4.3.22 → mainsequence-4.3.25}/tests/test_auth_precedence.py +0 -0
- {mainsequence-4.3.22 → mainsequence-4.3.25}/tests/test_build_operations_hashing.py +0 -0
- {mainsequence-4.3.22 → mainsequence-4.3.25}/tests/test_cli_browser_auth.py +0 -0
- {mainsequence-4.3.22 → mainsequence-4.3.25}/tests/test_cli_migrations.py +0 -0
- {mainsequence-4.3.22 → mainsequence-4.3.25}/tests/test_client.py +0 -0
- {mainsequence-4.3.22 → mainsequence-4.3.25}/tests/test_command_center_app_component_models.py +0 -0
- {mainsequence-4.3.22 → mainsequence-4.3.25}/tests/test_command_center_data_models.py +0 -0
- {mainsequence-4.3.22 → mainsequence-4.3.25}/tests/test_command_center_models.py +0 -0
- {mainsequence-4.3.22 → mainsequence-4.3.25}/tests/test_data_access_mixin_dimension_audit.py +0 -0
- {mainsequence-4.3.22 → mainsequence-4.3.25}/tests/test_data_node_storage_dimension_queries.py +0 -0
- {mainsequence-4.3.22 → mainsequence-4.3.25}/tests/test_data_node_update_flow.py +0 -0
- {mainsequence-4.3.22 → mainsequence-4.3.25}/tests/test_dependency_extras.py +0 -0
- {mainsequence-4.3.22 → mainsequence-4.3.25}/tests/test_duckdb_interface_dimensions.py +0 -0
- {mainsequence-4.3.22 → mainsequence-4.3.25}/tests/test_filter_normalization.py +0 -0
- {mainsequence-4.3.22 → mainsequence-4.3.25}/tests/test_instrumentation.py +0 -0
- {mainsequence-4.3.22 → mainsequence-4.3.25}/tests/test_logconf.py +0 -0
- {mainsequence-4.3.22 → mainsequence-4.3.25}/tests/test_meta_table_migrations.py +0 -0
- {mainsequence-4.3.22 → mainsequence-4.3.25}/tests/test_meta_tables_sqlalchemy_contracts.py +0 -0
- {mainsequence-4.3.22 → mainsequence-4.3.25}/tests/test_pod_project_resolution.py +0 -0
- {mainsequence-4.3.22 → mainsequence-4.3.25}/tests/test_project_batch_jobs_from_file.py +0 -0
- {mainsequence-4.3.22 → mainsequence-4.3.25}/tests/test_run_configuration.py +0 -0
- {mainsequence-4.3.22 → mainsequence-4.3.25}/tests/test_schema_names.py +0 -0
- {mainsequence-4.3.22 → mainsequence-4.3.25}/tests/test_secret_client_model.py +0 -0
- {mainsequence-4.3.22 → mainsequence-4.3.25}/tests/test_source_table_configuration.py +0 -0
- {mainsequence-4.3.22 → mainsequence-4.3.25}/tests/test_sqlite_interface_dimensions.py +0 -0
- {mainsequence-4.3.22 → mainsequence-4.3.25}/tests/test_update_runner_uid_runtime.py +0 -0
- {mainsequence-4.3.22 → mainsequence-4.3.25}/tests/test_update_statistics.py +0 -0
- {mainsequence-4.3.22 → mainsequence-4.3.25}/tests/test_update_uid_guards.py +0 -0
- {mainsequence-4.3.22 → mainsequence-4.3.25}/tests/test_workspace_snapshot.py +0 -0
|
@@ -567,8 +567,8 @@ def get_logged_user_details() -> dict[str, Any]:
|
|
|
567
567
|
Return the authenticated user via SDK client `User.get_logged_user()`.
|
|
568
568
|
|
|
569
569
|
The CLI does not naturally run inside a request context, so this bridge resolves
|
|
570
|
-
the current user
|
|
571
|
-
`X-User-
|
|
570
|
+
the current user UID from the authenticated API session and temporarily binds
|
|
571
|
+
`X-User-UID` plus `Authorization` into
|
|
572
572
|
`mainsequence.client.models_user._CURRENT_AUTH_HEADERS`
|
|
573
573
|
before calling the SDK method.
|
|
574
574
|
"""
|
|
@@ -598,14 +598,13 @@ def get_logged_user_details() -> dict[str, Any]:
|
|
|
598
598
|
try:
|
|
599
599
|
who = authed("GET", AUTH_PATHS["ping"])
|
|
600
600
|
data = who.json() if who.ok else {}
|
|
601
|
-
|
|
602
|
-
data.get("
|
|
603
|
-
or data.get("
|
|
604
|
-
or
|
|
605
|
-
or data.get("user_id")
|
|
601
|
+
user_uid = (
|
|
602
|
+
data.get("uid")
|
|
603
|
+
or (data.get("user") or {}).get("uid")
|
|
604
|
+
or data.get("user_uid")
|
|
606
605
|
)
|
|
607
|
-
if
|
|
608
|
-
raise ApiError("Could not determine the authenticated user
|
|
606
|
+
if user_uid in (None, ""):
|
|
607
|
+
raise ApiError("Could not determine the authenticated user uid.")
|
|
609
608
|
|
|
610
609
|
os.environ["MAINSEQUENCE_AUTH_MODE"] = "jwt"
|
|
611
610
|
os.environ["MAINSEQUENCE_ACCESS_TOKEN"] = access
|
|
@@ -637,17 +636,26 @@ def get_logged_user_details() -> dict[str, Any]:
|
|
|
637
636
|
ClientUser.ROOT_URL = root_url
|
|
638
637
|
headers_token = current_auth_headers.set(
|
|
639
638
|
{
|
|
640
|
-
"X-User-
|
|
639
|
+
"X-User-UID": str(user_uid),
|
|
641
640
|
"Authorization": f"Bearer {access}",
|
|
642
641
|
}
|
|
643
642
|
)
|
|
644
643
|
|
|
645
644
|
user = ClientUser.get_logged_user()
|
|
646
645
|
if isinstance(user, dict):
|
|
646
|
+
user.pop("id", None)
|
|
647
|
+
organization = user.get("organization")
|
|
648
|
+
if isinstance(organization, dict):
|
|
649
|
+
organization.pop("id", None)
|
|
647
650
|
return user
|
|
648
651
|
if hasattr(user, "model_dump"):
|
|
649
|
-
|
|
650
|
-
|
|
652
|
+
payload = user.model_dump()
|
|
653
|
+
payload.pop("id", None)
|
|
654
|
+
organization = payload.get("organization")
|
|
655
|
+
if isinstance(organization, dict):
|
|
656
|
+
organization.pop("id", None)
|
|
657
|
+
return payload
|
|
658
|
+
return {"uid": getattr(user, "uid", None)}
|
|
651
659
|
|
|
652
660
|
except Exception as e:
|
|
653
661
|
err_name = type(e).__name__
|
|
@@ -2601,14 +2601,14 @@ def user_show():
|
|
|
2601
2601
|
|
|
2602
2602
|
organization = user.get("organization")
|
|
2603
2603
|
if isinstance(organization, dict):
|
|
2604
|
-
organization_name = str(organization.get("name") or organization.get("
|
|
2604
|
+
organization_name = str(organization.get("name") or organization.get("uid") or "-")
|
|
2605
2605
|
else:
|
|
2606
2606
|
organization_name = str(organization or "-")
|
|
2607
2607
|
|
|
2608
2608
|
print_kv(
|
|
2609
2609
|
"MainSequence User",
|
|
2610
2610
|
[
|
|
2611
|
-
("
|
|
2611
|
+
("UID", str(user.get("uid") or "-")),
|
|
2612
2612
|
("Username", str(user.get("username") or "-")),
|
|
2613
2613
|
("Email", str(user.get("email") or "-")),
|
|
2614
2614
|
("Organization", organization_name),
|
|
@@ -340,6 +340,7 @@ class UserOrchestratorAgentService(BaseObjectOrm, BasePydanticModel):
|
|
|
340
340
|
agent_uid: str | None = Field(None, description="Public UID of the resolved astro Agent.")
|
|
341
341
|
user_uid: str | None = Field(None, description="Public UID of the owning user.")
|
|
342
342
|
is_ready: bool = Field(False, description="Whether the service runtime is routable.")
|
|
343
|
+
automatic_deployment: bool = Field(False, description="Whether this coding-agent service is eligible for automatic deployment flows.")
|
|
343
344
|
orchestrator_image_has_drift: bool = Field(False, description="Whether the orchestrator image is stale.")
|
|
344
345
|
related_job: Any | None = Field(None, description="Backing job payload or UID when returned by the backend.")
|
|
345
346
|
knative_service_runtime: Any | None = Field(None, description="Backing Knative service runtime payload.")
|
|
@@ -352,6 +353,7 @@ class UserProjectExecutorAgentService(BaseObjectOrm, BasePydanticModel):
|
|
|
352
353
|
uid: str | None = Field(None, description="Public UID of the project executor service.")
|
|
353
354
|
agent_uid: str | None = Field(None, description="Public UID of the resolved executor Agent.")
|
|
354
355
|
is_ready: bool = Field(False, description="Whether the executor runtime is currently ready.")
|
|
356
|
+
automatic_deployment: bool = Field(False, description="Whether this coding-agent service is eligible for automatic deployment flows.")
|
|
355
357
|
image_drift: dict[str, Any] | None = Field(None, description="Executor image drift status payload.")
|
|
356
358
|
project: Any | None = Field(None, description="Owning project payload or UID when returned by the backend.")
|
|
357
359
|
related_job: Any | None = Field(None, description="Backing job payload or UID when returned by the backend.")
|
|
@@ -60,10 +60,11 @@ class LoggedUserContextMiddleware:
|
|
|
60
60
|
authorization_scheme = _authorization_scheme(request.headers)
|
|
61
61
|
logger.info(
|
|
62
62
|
"LoggedUserContextMiddleware request context method=%s path=%s "
|
|
63
|
-
"x-user-id=%r x-username=%r x-resource-release-id=%r x-fastapi-id=%r "
|
|
63
|
+
"x-user-uid=%r x-user-id=%r x-username=%r x-resource-release-id=%r x-fastapi-id=%r "
|
|
64
64
|
"authorization_present=%s authorization_scheme=%r",
|
|
65
65
|
request.method,
|
|
66
66
|
request.url.path,
|
|
67
|
+
request.headers.get("x-user-uid"),
|
|
67
68
|
request.headers.get("x-user-id"),
|
|
68
69
|
request.headers.get("x-username"),
|
|
69
70
|
request.headers.get("x-resource-release-id"),
|
|
@@ -74,9 +75,10 @@ class LoggedUserContextMiddleware:
|
|
|
74
75
|
bound_headers = current_auth_headers.get()
|
|
75
76
|
logger.info(
|
|
76
77
|
"LoggedUserContextMiddleware bound context current_auth_headers_is_none=%s "
|
|
77
|
-
"header_keys=%s x-user-id=%r",
|
|
78
|
+
"header_keys=%s x-user-uid=%r x-user-id=%r",
|
|
78
79
|
bound_headers is None,
|
|
79
80
|
_header_keys(bound_headers),
|
|
81
|
+
_header_get(bound_headers, "x-user-uid"),
|
|
80
82
|
_header_get(bound_headers, "x-user-id"),
|
|
81
83
|
)
|
|
82
84
|
|
|
@@ -93,9 +95,11 @@ class LoggedUserContextMiddleware:
|
|
|
93
95
|
raise
|
|
94
96
|
|
|
95
97
|
request.state.user = user
|
|
98
|
+
request.state.user_uid = user.uid
|
|
96
99
|
request.state.user_id = user.id
|
|
97
100
|
logger.info(
|
|
98
|
-
"LoggedUserContextMiddleware User.get_logged_user resolved user_id=%s for %s %s",
|
|
101
|
+
"LoggedUserContextMiddleware User.get_logged_user resolved user_uid=%s user_id=%s for %s %s",
|
|
102
|
+
request.state.user_uid,
|
|
99
103
|
request.state.user_id,
|
|
100
104
|
request.method,
|
|
101
105
|
request.url.path,
|
|
@@ -500,8 +500,8 @@ class MetaTableOperationScope(BasePydanticModel):
|
|
|
500
500
|
return self
|
|
501
501
|
|
|
502
502
|
|
|
503
|
-
DEFAULT_META_TABLE_OPERATION_MAX_ROWS =
|
|
504
|
-
DEFAULT_META_TABLE_OPERATION_STATEMENT_TIMEOUT_MS =
|
|
503
|
+
DEFAULT_META_TABLE_OPERATION_MAX_ROWS = 1_000
|
|
504
|
+
DEFAULT_META_TABLE_OPERATION_STATEMENT_TIMEOUT_MS = 15_000
|
|
505
505
|
|
|
506
506
|
|
|
507
507
|
class MetaTableOperationLimits(BasePydanticModel):
|
|
@@ -67,6 +67,7 @@ def _logged_user_header_context(
|
|
|
67
67
|
return {
|
|
68
68
|
"header_source": header_source,
|
|
69
69
|
"header_keys": [],
|
|
70
|
+
"x_user_uid": None,
|
|
70
71
|
"x_user_id": None,
|
|
71
72
|
"authorization_present": False,
|
|
72
73
|
"authorization_scheme": None,
|
|
@@ -86,6 +87,12 @@ def _logged_user_header_context(
|
|
|
86
87
|
return {
|
|
87
88
|
"header_source": header_source,
|
|
88
89
|
"header_keys": sorted(str(key) for key in headers.keys()),
|
|
90
|
+
"x_user_uid": (
|
|
91
|
+
normalized_headers.get("X-User-UID")
|
|
92
|
+
or normalized_headers.get("x-user-uid")
|
|
93
|
+
or normalized_headers.get("HTTP_X_USER_UID")
|
|
94
|
+
or normalized_headers.get("http_x_user_uid")
|
|
95
|
+
),
|
|
89
96
|
"x_user_id": (
|
|
90
97
|
normalized_headers.get("X-User-ID")
|
|
91
98
|
or normalized_headers.get("x-user-id")
|
|
@@ -1063,15 +1070,17 @@ class User(UserApiBaseObjectOrm, BasePydanticModel):
|
|
|
1063
1070
|
cls,
|
|
1064
1071
|
*,
|
|
1065
1072
|
normalized_headers: Mapping[str, Any],
|
|
1066
|
-
|
|
1073
|
+
user_uid: str | None = None,
|
|
1074
|
+
user_id: int | None = None,
|
|
1067
1075
|
):
|
|
1076
|
+
identity = user_uid or user_id
|
|
1068
1077
|
username = str(
|
|
1069
1078
|
normalized_headers.get("X-Username")
|
|
1070
1079
|
or normalized_headers.get("x-username")
|
|
1071
1080
|
or normalized_headers.get("X-User-Email")
|
|
1072
1081
|
or normalized_headers.get("x-user-email")
|
|
1073
|
-
or f"user-{
|
|
1074
|
-
).strip() or f"user-{
|
|
1082
|
+
or f"user-{identity}"
|
|
1083
|
+
).strip() or f"user-{identity}"
|
|
1075
1084
|
email = str(
|
|
1076
1085
|
normalized_headers.get("X-User-Email")
|
|
1077
1086
|
or normalized_headers.get("x-user-email")
|
|
@@ -1080,6 +1089,7 @@ class User(UserApiBaseObjectOrm, BasePydanticModel):
|
|
|
1080
1089
|
|
|
1081
1090
|
payload = {
|
|
1082
1091
|
"id": user_id,
|
|
1092
|
+
"uid": user_uid,
|
|
1083
1093
|
"username": username,
|
|
1084
1094
|
"email": email,
|
|
1085
1095
|
"date_joined": None,
|
|
@@ -1138,10 +1148,14 @@ class User(UserApiBaseObjectOrm, BasePydanticModel):
|
|
|
1138
1148
|
cls,
|
|
1139
1149
|
*,
|
|
1140
1150
|
headers: Mapping[str, Any],
|
|
1151
|
+
user_uid: str | None = None,
|
|
1141
1152
|
user_id: int | None = None,
|
|
1142
1153
|
) -> User:
|
|
1143
1154
|
outbound_headers = _build_request_bound_outbound_headers(headers)
|
|
1144
|
-
if
|
|
1155
|
+
if user_uid:
|
|
1156
|
+
url = f"{cls.get_object_url()}/get_user_details/"
|
|
1157
|
+
params = None
|
|
1158
|
+
elif user_id is None:
|
|
1145
1159
|
url = f"{cls.get_object_url()}/get_user_details/"
|
|
1146
1160
|
params = None
|
|
1147
1161
|
else:
|
|
@@ -1170,8 +1184,10 @@ class User(UserApiBaseObjectOrm, BasePydanticModel):
|
|
|
1170
1184
|
Use this when code is running with request-scoped identity context, such
|
|
1171
1185
|
as FastAPI middleware, Streamlit, or code that explicitly binds
|
|
1172
1186
|
`_CURRENT_AUTH_HEADERS`. This method first uses the bound request/user
|
|
1173
|
-
context and only falls back to
|
|
1174
|
-
request headers are present with Bearer auth but no
|
|
1187
|
+
context and only falls back to the current-user details endpoint when
|
|
1188
|
+
request headers are present with Bearer auth but no request-bound user
|
|
1189
|
+
identity header. `X-User-UID` is the public identity header; `X-User-ID`
|
|
1190
|
+
is kept only for legacy request-bound callers.
|
|
1175
1191
|
|
|
1176
1192
|
For standalone authenticated CLI or script code that is not request-bound,
|
|
1177
1193
|
prefer `get_authenticated_user_details()`.
|
|
@@ -1220,6 +1236,12 @@ class User(UserApiBaseObjectOrm, BasePydanticModel):
|
|
|
1220
1236
|
and str(authorization_value).split(" ", 1)[0].lower() == "bearer"
|
|
1221
1237
|
)
|
|
1222
1238
|
|
|
1239
|
+
user_uid_raw = (
|
|
1240
|
+
normalized_headers.get("X-User-UID")
|
|
1241
|
+
or normalized_headers.get("x-user-uid")
|
|
1242
|
+
or normalized_headers.get("HTTP_X_USER_UID")
|
|
1243
|
+
or normalized_headers.get("http_x_user_uid")
|
|
1244
|
+
)
|
|
1223
1245
|
user_id_raw = (
|
|
1224
1246
|
normalized_headers.get("X-User-ID")
|
|
1225
1247
|
or normalized_headers.get("x-user-id")
|
|
@@ -1227,6 +1249,28 @@ class User(UserApiBaseObjectOrm, BasePydanticModel):
|
|
|
1227
1249
|
or normalized_headers.get("http_x_user_id")
|
|
1228
1250
|
)
|
|
1229
1251
|
|
|
1252
|
+
if user_uid_raw not in (None, ""):
|
|
1253
|
+
user_uid = str(user_uid_raw).strip()
|
|
1254
|
+
if not has_bearer_authorization:
|
|
1255
|
+
user = cls._build_request_bound_identity_user(
|
|
1256
|
+
normalized_headers=normalized_headers,
|
|
1257
|
+
user_uid=user_uid,
|
|
1258
|
+
)
|
|
1259
|
+
_CURRENT_USER.set(user)
|
|
1260
|
+
logger.info(
|
|
1261
|
+
"User.get_logged_user resolved user_uid=%s via request identity headers without backend auth",
|
|
1262
|
+
user.uid,
|
|
1263
|
+
)
|
|
1264
|
+
return user
|
|
1265
|
+
|
|
1266
|
+
user = cls._get_request_bound_user(headers=headers, user_uid=user_uid)
|
|
1267
|
+
_CURRENT_USER.set(user)
|
|
1268
|
+
logger.info(
|
|
1269
|
+
"User.get_logged_user resolved user_uid=%s via X-User-UID header",
|
|
1270
|
+
user.uid,
|
|
1271
|
+
)
|
|
1272
|
+
return user
|
|
1273
|
+
|
|
1230
1274
|
if user_id_raw in (None, ""):
|
|
1231
1275
|
if has_bearer_authorization:
|
|
1232
1276
|
outgoing_authorization = None
|
|
@@ -1256,10 +1300,11 @@ class User(UserApiBaseObjectOrm, BasePydanticModel):
|
|
|
1256
1300
|
context = _logged_user_header_context(headers, header_source=header_source)
|
|
1257
1301
|
logger.exception(
|
|
1258
1302
|
"User.get_logged_user failed during bearer fallback; "
|
|
1259
|
-
"header_source=%s header_keys=%s X-User-ID=%r "
|
|
1303
|
+
"header_source=%s header_keys=%s X-User-UID=%r X-User-ID=%r "
|
|
1260
1304
|
"authorization_present=%s authorization_scheme=%r",
|
|
1261
1305
|
context["header_source"],
|
|
1262
1306
|
context["header_keys"],
|
|
1307
|
+
context["x_user_uid"],
|
|
1263
1308
|
context["x_user_id"],
|
|
1264
1309
|
context["authorization_present"],
|
|
1265
1310
|
context["authorization_scheme"],
|
|
@@ -1274,16 +1319,17 @@ class User(UserApiBaseObjectOrm, BasePydanticModel):
|
|
|
1274
1319
|
|
|
1275
1320
|
context = _logged_user_header_context(headers, header_source=header_source)
|
|
1276
1321
|
logger.error(
|
|
1277
|
-
"User.get_logged_user failed: missing X-User-ID in request headers; "
|
|
1278
|
-
"header_source=%s header_keys=%s X-User-ID=%r "
|
|
1322
|
+
"User.get_logged_user failed: missing X-User-UID or X-User-ID in request headers; "
|
|
1323
|
+
"header_source=%s header_keys=%s X-User-UID=%r X-User-ID=%r "
|
|
1279
1324
|
"authorization_present=%s authorization_scheme=%r",
|
|
1280
1325
|
context["header_source"],
|
|
1281
1326
|
context["header_keys"],
|
|
1327
|
+
context["x_user_uid"],
|
|
1282
1328
|
context["x_user_id"],
|
|
1283
1329
|
context["authorization_present"],
|
|
1284
1330
|
context["authorization_scheme"],
|
|
1285
1331
|
)
|
|
1286
|
-
raise RuntimeError("Missing X-User-ID in request headers.")
|
|
1332
|
+
raise RuntimeError("Missing X-User-UID or X-User-ID in request headers.")
|
|
1287
1333
|
|
|
1288
1334
|
try:
|
|
1289
1335
|
user_id = int(str(user_id_raw).strip())
|
|
@@ -1291,10 +1337,11 @@ class User(UserApiBaseObjectOrm, BasePydanticModel):
|
|
|
1291
1337
|
context = _logged_user_header_context(headers, header_source=header_source)
|
|
1292
1338
|
logger.exception(
|
|
1293
1339
|
"User.get_logged_user failed: invalid X-User-ID value; "
|
|
1294
|
-
"header_source=%s header_keys=%s X-User-ID=%r "
|
|
1340
|
+
"header_source=%s header_keys=%s X-User-UID=%r X-User-ID=%r "
|
|
1295
1341
|
"authorization_present=%s authorization_scheme=%r",
|
|
1296
1342
|
context["header_source"],
|
|
1297
1343
|
context["header_keys"],
|
|
1344
|
+
context["x_user_uid"],
|
|
1298
1345
|
context["x_user_id"],
|
|
1299
1346
|
context["authorization_present"],
|
|
1300
1347
|
context["authorization_scheme"],
|
|
@@ -96,10 +96,10 @@ def test_user_show(cli_mod, runner, monkeypatch):
|
|
|
96
96
|
cli_mod,
|
|
97
97
|
"get_logged_user_details",
|
|
98
98
|
lambda: {
|
|
99
|
-
"
|
|
99
|
+
"uid": "user-uid-7",
|
|
100
100
|
"username": "jose",
|
|
101
101
|
"email": "jose@main-sequence.io",
|
|
102
|
-
"organization": {"
|
|
102
|
+
"organization": {"uid": "org-uid-2", "name": "Main Sequence"},
|
|
103
103
|
"is_active": True,
|
|
104
104
|
"is_verified": True,
|
|
105
105
|
"mfa_enabled": False,
|
|
@@ -111,6 +111,7 @@ def test_user_show(cli_mod, runner, monkeypatch):
|
|
|
111
111
|
result = runner.invoke(cli_mod.app, ["user"])
|
|
112
112
|
assert result.exit_code == 0
|
|
113
113
|
assert "MainSequence User" in result.output
|
|
114
|
+
assert "user-uid-7" in result.output
|
|
114
115
|
assert "jose" in result.output
|
|
115
116
|
assert "jose@main-sequence.io" in result.output
|
|
116
117
|
assert "Main Sequence" in result.output
|
|
@@ -121,17 +122,18 @@ def test_user_show_json(cli_mod, runner, monkeypatch):
|
|
|
121
122
|
cli_mod,
|
|
122
123
|
"get_logged_user_details",
|
|
123
124
|
lambda: {
|
|
124
|
-
"
|
|
125
|
+
"uid": "user-uid-7",
|
|
125
126
|
"username": "jose",
|
|
126
127
|
"email": "jose@main-sequence.io",
|
|
127
|
-
"organization": {"
|
|
128
|
+
"organization": {"uid": "org-uid-2", "name": "Main Sequence"},
|
|
128
129
|
},
|
|
129
130
|
)
|
|
130
131
|
|
|
131
132
|
result = runner.invoke(cli_mod.app, ["user", "--json"])
|
|
132
133
|
assert result.exit_code == 0
|
|
133
134
|
payload = json.loads(result.output)
|
|
134
|
-
assert
|
|
135
|
+
assert "id" not in payload
|
|
136
|
+
assert payload["uid"] == "user-uid-7"
|
|
135
137
|
assert payload["username"] == "jose"
|
|
136
138
|
assert payload["organization"]["name"] == "Main Sequence"
|
|
137
139
|
|
|
@@ -5668,7 +5670,7 @@ def test_get_logged_user_details_uses_client_model(cli_mod, monkeypatch):
|
|
|
5668
5670
|
"authed",
|
|
5669
5671
|
lambda method, api_path, body=None: types.SimpleNamespace(
|
|
5670
5672
|
ok=True,
|
|
5671
|
-
json=lambda: {"
|
|
5673
|
+
json=lambda: {"uid": "user-uid-7"},
|
|
5672
5674
|
),
|
|
5673
5675
|
)
|
|
5674
5676
|
|
|
@@ -5722,10 +5724,10 @@ def test_get_logged_user_details_uses_client_model(cli_mod, monkeypatch):
|
|
|
5722
5724
|
captured["headers_seen"] = fake_headers.current
|
|
5723
5725
|
return types.SimpleNamespace(
|
|
5724
5726
|
model_dump=lambda: {
|
|
5725
|
-
"
|
|
5727
|
+
"uid": "user-uid-7",
|
|
5726
5728
|
"username": "jose",
|
|
5727
5729
|
"email": "jose@main-sequence.io",
|
|
5728
|
-
"organization": {"
|
|
5730
|
+
"organization": {"uid": "org-uid-2", "name": "Main Sequence"},
|
|
5729
5731
|
}
|
|
5730
5732
|
)
|
|
5731
5733
|
|
|
@@ -5745,9 +5747,12 @@ def test_get_logged_user_details_uses_client_model(cli_mod, monkeypatch):
|
|
|
5745
5747
|
assert fake_utils.API_ENDPOINT == "https://backend.test/orm/api"
|
|
5746
5748
|
assert fake_utils.AUTH_ENDPOINT == "https://backend.test"
|
|
5747
5749
|
assert captured["jwt"] == ("acc", "ref")
|
|
5748
|
-
assert captured["headers_set"] == {"X-User-
|
|
5749
|
-
assert captured["headers_seen"] == {"X-User-
|
|
5750
|
+
assert captured["headers_set"] == {"X-User-UID": "user-uid-7", "Authorization": "Bearer acc"}
|
|
5751
|
+
assert captured["headers_seen"] == {"X-User-UID": "user-uid-7", "Authorization": "Bearer acc"}
|
|
5750
5752
|
assert captured["headers_reset"] == "token"
|
|
5753
|
+
assert "id" not in out
|
|
5754
|
+
assert "id" not in out["organization"]
|
|
5755
|
+
assert out["uid"] == "user-uid-7"
|
|
5751
5756
|
assert out["username"] == "jose"
|
|
5752
5757
|
|
|
5753
5758
|
|
|
@@ -524,6 +524,70 @@ def test_meta_table_execute_operation_fetches_requested_rows_with_backend_pagina
|
|
|
524
524
|
assert [call["limits"]["max_rows"] for call in calls] == [5, 3, 1]
|
|
525
525
|
|
|
526
526
|
|
|
527
|
+
def test_meta_table_execute_operation_does_not_paginate_upsert(monkeypatch):
|
|
528
|
+
calls = []
|
|
529
|
+
|
|
530
|
+
def fake_make_request(**kwargs):
|
|
531
|
+
calls.append(kwargs["payload"]["json"])
|
|
532
|
+
return _Response(
|
|
533
|
+
{
|
|
534
|
+
"ok": True,
|
|
535
|
+
"operation": "upsert",
|
|
536
|
+
"dialect": "postgresql",
|
|
537
|
+
"row_count": 1,
|
|
538
|
+
"rows": [{"uid": "aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa"}],
|
|
539
|
+
"truncated": True,
|
|
540
|
+
"max_rows": 1,
|
|
541
|
+
"pagination": {
|
|
542
|
+
"limit": 1,
|
|
543
|
+
"offset": 0,
|
|
544
|
+
"returned_count": 1,
|
|
545
|
+
"has_more": True,
|
|
546
|
+
"next_offset": 1,
|
|
547
|
+
},
|
|
548
|
+
}
|
|
549
|
+
)
|
|
550
|
+
|
|
551
|
+
monkeypatch.setattr(meta_table_models, "make_request", fake_make_request)
|
|
552
|
+
monkeypatch.setattr(
|
|
553
|
+
meta_table_models.MetaTable,
|
|
554
|
+
"build_session",
|
|
555
|
+
classmethod(lambda cls: SimpleNamespace(headers={})),
|
|
556
|
+
)
|
|
557
|
+
|
|
558
|
+
result = meta_table_models.MetaTable.execute_operation(
|
|
559
|
+
{
|
|
560
|
+
"operation": "upsert",
|
|
561
|
+
"statement": {
|
|
562
|
+
"sql": (
|
|
563
|
+
"INSERT INTO public.asset (uid) VALUES (%(uid)s::UUID) "
|
|
564
|
+
"ON CONFLICT (uid) DO UPDATE SET uid = %(uid)s::UUID "
|
|
565
|
+
"RETURNING uid"
|
|
566
|
+
),
|
|
567
|
+
"parameters": {"uid": "aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa"},
|
|
568
|
+
},
|
|
569
|
+
"scope": {
|
|
570
|
+
"data_source_uid": "dddddddd-dddd-4ddd-8ddd-dddddddddddd",
|
|
571
|
+
"tables": [
|
|
572
|
+
{
|
|
573
|
+
"meta_table_uid": "aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa",
|
|
574
|
+
"alias": "asset",
|
|
575
|
+
"access": "write",
|
|
576
|
+
}
|
|
577
|
+
],
|
|
578
|
+
},
|
|
579
|
+
"limits": {
|
|
580
|
+
"max_rows": 1,
|
|
581
|
+
"statement_timeout_ms": 15000,
|
|
582
|
+
},
|
|
583
|
+
}
|
|
584
|
+
)
|
|
585
|
+
|
|
586
|
+
assert result["operation"] == "upsert"
|
|
587
|
+
assert len(calls) == 1
|
|
588
|
+
assert calls[0]["limits"]["offset"] == 0
|
|
589
|
+
|
|
590
|
+
|
|
527
591
|
def test_dynamic_table_data_source_issue_migration_connection_posts_scope(monkeypatch):
|
|
528
592
|
captured = {}
|
|
529
593
|
|
|
@@ -855,13 +919,13 @@ def test_compiled_sql_v1_operation_uses_backend_limit_defaults():
|
|
|
855
919
|
},
|
|
856
920
|
)
|
|
857
921
|
|
|
858
|
-
assert operation.limits.max_rows ==
|
|
922
|
+
assert operation.limits.max_rows == 1_000
|
|
859
923
|
assert operation.limits.offset == 0
|
|
860
|
-
assert operation.limits.statement_timeout_ms ==
|
|
924
|
+
assert operation.limits.statement_timeout_ms == 15_000
|
|
861
925
|
assert meta_table_models._payload_json(operation)["limits"] == {
|
|
862
|
-
"max_rows":
|
|
926
|
+
"max_rows": 1_000,
|
|
863
927
|
"offset": 0,
|
|
864
|
-
"statement_timeout_ms":
|
|
928
|
+
"statement_timeout_ms": 15_000,
|
|
865
929
|
}
|
|
866
930
|
|
|
867
931
|
|
|
@@ -73,6 +73,62 @@ def test_get_logged_user_uses_request_bound_headers_for_user_lookup(monkeypatch)
|
|
|
73
73
|
assert "Host" not in captured["headers"]
|
|
74
74
|
|
|
75
75
|
|
|
76
|
+
def test_get_logged_user_uses_request_bound_uid_header_for_user_lookup(monkeypatch):
|
|
77
|
+
monkeypatch.delenv("MAINSEQUENCE_ACCESS_TOKEN", raising=False)
|
|
78
|
+
monkeypatch.delenv("MAINSEQUENCE_REFRESH_TOKEN", raising=False)
|
|
79
|
+
|
|
80
|
+
captured: dict[str, object] = {}
|
|
81
|
+
|
|
82
|
+
class _FakeSession:
|
|
83
|
+
def get(self, url, *, headers=None, params=None, timeout=None):
|
|
84
|
+
captured["url"] = url
|
|
85
|
+
captured["headers"] = headers
|
|
86
|
+
captured["params"] = params
|
|
87
|
+
captured["timeout"] = timeout
|
|
88
|
+
return _FakeResponse(
|
|
89
|
+
{
|
|
90
|
+
"uid": "user-uid-4",
|
|
91
|
+
"username": "jose",
|
|
92
|
+
"email": "jose@main-sequence.io",
|
|
93
|
+
"date_joined": "2026-01-01T00:00:00Z",
|
|
94
|
+
"is_active": True,
|
|
95
|
+
"api_request_limit": 10000,
|
|
96
|
+
"mfa_enabled": False,
|
|
97
|
+
"groups": [],
|
|
98
|
+
"user_permissions": [],
|
|
99
|
+
"organization_teams": [],
|
|
100
|
+
}
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
monkeypatch.setattr(
|
|
104
|
+
models_user_mod.User,
|
|
105
|
+
"build_session",
|
|
106
|
+
classmethod(lambda cls: _FakeSession()),
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
auth_token = models_user_mod._CURRENT_AUTH_HEADERS.set(
|
|
110
|
+
{
|
|
111
|
+
"X-User-UID": "user-uid-4",
|
|
112
|
+
"Authorization": "Bearer inbound-token",
|
|
113
|
+
"Cookie": "sessionid=abc",
|
|
114
|
+
"Host": "frontend.test",
|
|
115
|
+
}
|
|
116
|
+
)
|
|
117
|
+
user_token = models_user_mod._CURRENT_USER.set(None)
|
|
118
|
+
try:
|
|
119
|
+
user = models_user_mod.User.get_logged_user()
|
|
120
|
+
finally:
|
|
121
|
+
models_user_mod._CURRENT_USER.reset(user_token)
|
|
122
|
+
models_user_mod._CURRENT_AUTH_HEADERS.reset(auth_token)
|
|
123
|
+
|
|
124
|
+
assert user.uid == "user-uid-4"
|
|
125
|
+
assert str(captured["url"]).endswith("/user/api/user/get_user_details/")
|
|
126
|
+
assert captured["params"] is None
|
|
127
|
+
assert captured["headers"]["Authorization"] == "Bearer inbound-token"
|
|
128
|
+
assert captured["headers"]["Cookie"] == "sessionid=abc"
|
|
129
|
+
assert "Host" not in captured["headers"]
|
|
130
|
+
|
|
131
|
+
|
|
76
132
|
def test_get_logged_user_returns_header_identity_without_backend_auth(monkeypatch):
|
|
77
133
|
monkeypatch.delenv("MAINSEQUENCE_ACCESS_TOKEN", raising=False)
|
|
78
134
|
monkeypatch.delenv("MAINSEQUENCE_REFRESH_TOKEN", raising=False)
|
|
@@ -108,6 +164,42 @@ def test_get_logged_user_returns_header_identity_without_backend_auth(monkeypatc
|
|
|
108
164
|
assert user.api_request_limit is None
|
|
109
165
|
|
|
110
166
|
|
|
167
|
+
def test_get_logged_user_returns_uid_header_identity_without_backend_auth(monkeypatch):
|
|
168
|
+
monkeypatch.delenv("MAINSEQUENCE_ACCESS_TOKEN", raising=False)
|
|
169
|
+
monkeypatch.delenv("MAINSEQUENCE_REFRESH_TOKEN", raising=False)
|
|
170
|
+
|
|
171
|
+
class _FakeSession:
|
|
172
|
+
def get(self, url, *, headers=None, params=None, timeout=None):
|
|
173
|
+
raise AssertionError("header-only identity should not trigger backend lookup")
|
|
174
|
+
|
|
175
|
+
monkeypatch.setattr(
|
|
176
|
+
models_user_mod.User,
|
|
177
|
+
"build_session",
|
|
178
|
+
classmethod(lambda cls: _FakeSession()),
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
auth_token = models_user_mod._CURRENT_AUTH_HEADERS.set(
|
|
182
|
+
{
|
|
183
|
+
"X-User-UID": "user-uid-4",
|
|
184
|
+
"X-Username": "dashboard-user",
|
|
185
|
+
"X-Dashboard-ID": "dashboard-7",
|
|
186
|
+
}
|
|
187
|
+
)
|
|
188
|
+
user_token = models_user_mod._CURRENT_USER.set(None)
|
|
189
|
+
try:
|
|
190
|
+
user = models_user_mod.User.get_logged_user()
|
|
191
|
+
finally:
|
|
192
|
+
models_user_mod._CURRENT_USER.reset(user_token)
|
|
193
|
+
models_user_mod._CURRENT_AUTH_HEADERS.reset(auth_token)
|
|
194
|
+
|
|
195
|
+
assert user.uid == "user-uid-4"
|
|
196
|
+
assert user.id is None
|
|
197
|
+
assert user.username == "dashboard-user"
|
|
198
|
+
assert user.email == "dashboard-user"
|
|
199
|
+
assert user.date_joined is None
|
|
200
|
+
assert user.api_request_limit is None
|
|
201
|
+
|
|
202
|
+
|
|
111
203
|
def test_get_logged_user_bearer_fallback_uses_request_bound_headers(monkeypatch):
|
|
112
204
|
monkeypatch.delenv("MAINSEQUENCE_ACCESS_TOKEN", raising=False)
|
|
113
205
|
monkeypatch.delenv("MAINSEQUENCE_REFRESH_TOKEN", raising=False)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mainsequence-4.3.22 → mainsequence-4.3.25}/agent_scaffold/skills/a2a_communication/SKILL.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mainsequence-4.3.22 → mainsequence-4.3.25}/agent_scaffold/skills/dashboards/streamlit/SKILL.md
RENAMED
|
File without changes
|
{mainsequence-4.3.22 → mainsequence-4.3.25}/agent_scaffold/skills/data_access/exploration/SKILL.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mainsequence-4.3.22 → mainsequence-4.3.25}/agent_scaffold/skills/maintenance/bug_auditor/SKILL.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mainsequence-4.3.22 → mainsequence-4.3.25}/mainsequence/client/command_center/app_component.py
RENAMED
|
File without changes
|
{mainsequence-4.3.22 → mainsequence-4.3.25}/mainsequence/client/command_center/connections.py
RENAMED
|
File without changes
|
{mainsequence-4.3.22 → mainsequence-4.3.25}/mainsequence/client/command_center/data_models.py
RENAMED
|
File without changes
|
|
File without changes
|
{mainsequence-4.3.22 → mainsequence-4.3.25}/mainsequence/client/command_center/workspace_snapshot.py
RENAMED
|
File without changes
|
|
File without changes
|
{mainsequence-4.3.22 → mainsequence-4.3.25}/mainsequence/client/data_sources_interfaces/__init__.py
RENAMED
|
File without changes
|
{mainsequence-4.3.22 → mainsequence-4.3.25}/mainsequence/client/data_sources_interfaces/duckdb.py
RENAMED
|
File without changes
|
|
File without changes
|
{mainsequence-4.3.22 → mainsequence-4.3.25}/mainsequence/client/data_sources_interfaces/sqlite.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mainsequence-4.3.22 → mainsequence-4.3.25}/mainsequence/meta_tables/compiled_sql/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mainsequence-4.3.22 → mainsequence-4.3.25}/mainsequence/meta_tables/data_nodes/build_operations.py
RENAMED
|
File without changes
|
{mainsequence-4.3.22 → mainsequence-4.3.25}/mainsequence/meta_tables/data_nodes/data_nodes.py
RENAMED
|
File without changes
|
|
File without changes
|
{mainsequence-4.3.22 → mainsequence-4.3.25}/mainsequence/meta_tables/data_nodes/namespacing.py
RENAMED
|
File without changes
|
{mainsequence-4.3.22 → mainsequence-4.3.25}/mainsequence/meta_tables/data_nodes/persist_managers.py
RENAMED
|
File without changes
|
{mainsequence-4.3.22 → mainsequence-4.3.25}/mainsequence/meta_tables/data_nodes/run_operations.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mainsequence-4.3.22 → mainsequence-4.3.25}/mainsequence/meta_tables/sqlalchemy_contracts.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mainsequence-4.3.22 → mainsequence-4.3.25}/tests/test_command_center_app_component_models.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mainsequence-4.3.22 → mainsequence-4.3.25}/tests/test_data_node_storage_dimension_queries.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|