Skip to content

Commit 06407b9

Browse files
lesebclaude
andauthored
feat(storage)!: enforce AuthorizedSqlStore usage for APIs requiring access control (#5776)
## Summary - Rename `sqlstore_impl` → `_sqlstore_impl` (private) and introduce `authorized_sqlstore()` factory as the only supported way to obtain a SQL store for API use - Replace all direct `sqlstore_impl` + `AuthorizedSqlStore` two-step patterns across providers with the new single-call `authorized_sqlstore()` factory - Add a pre-commit hook (`enforce-authorized-sqlstore`) that blocks direct `_sqlstore_impl` usage outside `core/storage/sqlstore/` - Remove the unused `agent_state` KVStore from responses persistence config, completing the migration from #5757 ## Test plan - [ ] Unit tests pass: `uv run pytest tests/unit/ -x --tb=short` - [ ] Pre-commit checks pass: `uv run pre-commit run --all-files` - [ ] Integration tests pass in replay mode - [ ] Verify the new pre-commit hook catches direct `sqlstore_impl` usage Signed-off-by: Sébastien Han <seb@redhat.com> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 748cb69 commit 06407b9

31 files changed

Lines changed: 61 additions & 93 deletions

File tree

.pre-commit-config.yaml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -308,6 +308,26 @@ repos:
308308
}
309309
exit 0
310310
311+
- id: enforce-authorized-sqlstore
312+
name: Enforce AuthorizedSqlStore usage
313+
entry: bash
314+
language: system
315+
types: [python]
316+
pass_filenames: false
317+
always_run: true
318+
files: ^src/
319+
args:
320+
- -c
321+
- |
322+
grep -rn --include="*.py" -E 'sqlstore_impl' src/ \
323+
| grep -v 'core/storage/sqlstore/' && {
324+
echo;
325+
echo "❌ Direct sqlstore_impl usage is not allowed outside core/storage/sqlstore/.";
326+
echo "Use authorized_sqlstore() from ogx.core.storage.sqlstore.authorized_sqlstore instead.";
327+
echo;
328+
exit 1;
329+
} || true
330+
311331
- id: check-file-size
312332
name: Check Python file size limit
313333
entry: python scripts/check_file_size.py

docs/docs/providers/responses/inline_builtin.mdx

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,6 @@ Meta's reference implementation of an agent system that can use tools, access ve
1515
| Field | Type | Required | Default | Description |
1616
|-------|------|----------|---------|-------------|
1717
| `persistence` | `ResponsesPersistenceConfig` | No | | |
18-
| `persistence.agent_state` | `KVStoreReference` | No | | |
19-
| `persistence.agent_state.namespace` | `str` | No | | Key prefix for KVStore backends |
20-
| `persistence.agent_state.backend` | `str` | No | | Name of backend from storage.backends |
2118
| `persistence.responses` | `ResponsesStoreReference` | No | | |
2219
| `persistence.responses.table_name` | `str` | No | openai_responses | Name of the table to use for storing OpenAI responses |
2320
| `persistence.responses.backend` | `str` | No | | Name of backend from storage.backends |
@@ -98,9 +95,6 @@ Be concise, structured, and focused on helping the next LLM seamlessly continue
9895

9996
```yaml
10097
persistence:
101-
agent_state:
102-
namespace: agents
103-
backend: kv_default
10498
responses:
10599
table_name: responses
106100
backend: sql_default

src/ogx/core/connectors/connectors.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,7 @@
1212

1313
from ogx.core.access_control.datatypes import AccessRule
1414
from ogx.core.datatypes import StackConfig
15-
from ogx.core.storage.sqlstore.authorized_sqlstore import AuthorizedSqlStore
16-
from ogx.core.storage.sqlstore.sqlstore import sqlstore_impl
15+
from ogx.core.storage.sqlstore.authorized_sqlstore import authorized_sqlstore
1716
from ogx.log import get_logger
1817
from ogx.providers.utils.tools.mcp import get_mcp_server_info, list_mcp_tools
1918
from ogx_api import (
@@ -61,8 +60,7 @@ def __init__(self, config: ConnectorServiceConfig):
6160
if not connectors_ref:
6261
raise ServiceNotEnabledError("storage.stores.connectors")
6362

64-
base_sql_store = sqlstore_impl(connectors_ref)
65-
self.sql_store = AuthorizedSqlStore(base_sql_store, self.policy)
63+
self.sql_store = authorized_sqlstore(connectors_ref, self.policy)
6664

6765
async def initialize(self) -> None:
6866
"""Initialize the connector service."""

src/ogx/core/conversations/conversations.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,7 @@
1313
from ogx.core.access_control.datatypes import AccessRule
1414
from ogx.core.conversations.validation import CONVERSATION_ID_PATTERN
1515
from ogx.core.datatypes import StackConfig
16-
from ogx.core.storage.sqlstore.authorized_sqlstore import AuthorizedSqlStore
17-
from ogx.core.storage.sqlstore.sqlstore import sqlstore_impl
16+
from ogx.core.storage.sqlstore.authorized_sqlstore import authorized_sqlstore
1817
from ogx.log import get_logger
1918
from ogx_api import (
2019
Api,
@@ -74,8 +73,7 @@ def __init__(self, config: ConversationServiceConfig, deps: dict[Api, Any]):
7473
if not conversations_ref:
7574
raise ServiceNotEnabledError("storage.stores.conversations")
7675

77-
base_sql_store = sqlstore_impl(conversations_ref)
78-
self.sql_store = AuthorizedSqlStore(base_sql_store, self.policy)
76+
self.sql_store = authorized_sqlstore(conversations_ref, self.policy)
7977

8078
async def initialize(self) -> None:
8179
"""Initialize the store and create tables."""

src/ogx/core/prompts/prompts.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,7 @@
1111

1212
from ogx.core.access_control.datatypes import AccessRule
1313
from ogx.core.datatypes import StackConfig
14-
from ogx.core.storage.sqlstore.authorized_sqlstore import AuthorizedSqlStore
15-
from ogx.core.storage.sqlstore.sqlstore import sqlstore_impl
14+
from ogx.core.storage.sqlstore.authorized_sqlstore import authorized_sqlstore
1615
from ogx_api import (
1716
Api,
1817
CreatePromptRequest,
@@ -62,8 +61,7 @@ def __init__(self, config: PromptServiceConfig, deps: dict[Api, Any]):
6261
if not prompts_ref:
6362
raise ServiceNotEnabledError("storage.stores.prompts")
6463

65-
base_sql_store = sqlstore_impl(prompts_ref)
66-
self.sql_store = AuthorizedSqlStore(base_sql_store, self.policy)
64+
self.sql_store = authorized_sqlstore(prompts_ref, self.policy)
6765

6866
async def initialize(self) -> None:
6967
await self.sql_store.create_table(

src/ogx/core/storage/sqlstore/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,5 @@
1414
SqlStore as SqlStore,
1515
)
1616

17+
from .authorized_sqlstore import authorized_sqlstore as authorized_sqlstore
1718
from .sqlstore import * # noqa: F401,F403

src/ogx/core/storage/sqlstore/authorized_sqlstore.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@
2020
from ogx.core.access_control.datatypes import AccessRule, Action, Scope
2121
from ogx.core.datatypes import User
2222
from ogx.core.request_headers import get_authenticated_user
23-
from ogx.core.storage.datatypes import StorageBackendType
23+
from ogx.core.storage.datatypes import SqlStoreReference, StorageBackendType
24+
from ogx.core.storage.sqlstore.sqlstore import _sqlstore_impl
2425
from ogx.log import get_logger
2526
from ogx_api import PaginatedResponse
2627
from ogx_api.internal.sqlstore import ColumnDefinition, ColumnType, SqlStore
@@ -81,6 +82,14 @@ def __init__(self, record_id: str, table_name: str, owner: User | None):
8182
self.owner = owner
8283

8384

85+
def authorized_sqlstore(reference: SqlStoreReference, policy: list[AccessRule]) -> "AuthorizedSqlStore":
86+
"""Create an AuthorizedSqlStore from a store reference and access policy.
87+
88+
This is the only supported way to obtain a SQL store for API use.
89+
"""
90+
return AuthorizedSqlStore(_sqlstore_impl(reference), policy)
91+
92+
8493
class AuthorizedSqlStore:
8594
"""
8695
Authorization layer for SqlStore that provides access control functionality.

src/ogx/core/storage/sqlstore/sqlstore.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ def get_pip_packages(store_config: dict | SqlStoreConfig) -> list[str]:
4545
return store_config.pip_packages()
4646

4747

48-
def sqlstore_impl(reference: SqlStoreReference) -> SqlStore:
48+
def _sqlstore_impl(reference: SqlStoreReference) -> SqlStore:
4949
"""Get or create a SqlStore instance for the given store reference.
5050
5151
Args:

src/ogx/distributions/ci-tests/config.yaml

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -211,9 +211,6 @@ providers:
211211
provider_type: inline::builtin
212212
config:
213213
persistence:
214-
agent_state:
215-
namespace: agents
216-
backend: kv_default
217214
responses:
218215
table_name: responses
219216
backend: sql_default

src/ogx/distributions/ci-tests/run-with-postgres-store.yaml

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -211,9 +211,6 @@ providers:
211211
provider_type: inline::builtin
212212
config:
213213
persistence:
214-
agent_state:
215-
namespace: agents
216-
backend: kv_default
217214
responses:
218215
table_name: responses
219216
backend: sql_default

0 commit comments

Comments
 (0)