Access development context files#528
Conversation
Jeff-Rowell
left a comment
There was a problem hiding this comment.
Left a few small suggestions, but nothing blocking. These files will be a huge help in getting better consistency and fewer errors/bugs!
| to a group over paths that add individual users to groups directly. Access is designed to make the role-based path | ||
| the natural default. | ||
|
|
||
| **Principle of least privilege.** When implementing permission checks or approval flows, be |
There was a problem hiding this comment.
Could also highlight that we want Access to be transparent, so broad read-only access to all pages is a core design decision in the API and UI.
| ): | ||
| ``` | ||
|
|
||
| **Don't use Flask patterns:** no `g`, no `request` globals, no `abort()` — raise |
There was a problem hiding this comment.
Not sure this is necessary anymore since Flask is gone
| ... | ||
| ``` | ||
|
|
||
| Plugin hooks fire from inside `execute()`. Side-effecting Okta/notification work is spawned as |
| types by the API-provided `type` field rather than by parsing name prefixes, and validates | ||
| names against `ACCESS_CONFIG.NAME_VALIDATION_PATTERN`. | ||
|
|
||
| When querying polymorphically, always use `with_polymorphic` or `selectin_polymorphic` to |
There was a problem hiding this comment.
Should mention that with_polymorphic should be used when loading from many-to-one or one-to-one relationships whereas selectin_polymorphic should be used when loading many-to-many or one-to-many relationships where possible for query efficiency.
| relationships on the model you're querying. Many relationships already exist with the correct | ||
| eager loading strategy and active-record filters baked in (e.g. `active_group_memberships`, | ||
| `active_group_ownerships`, `active_role_associated_group_mappings`). Using a pre-defined | ||
| relationship via `joinedload` or `selectinload` is almost always preferable to writing a |
There was a problem hiding this comment.
Should also mention that joinedload should be used when loading from many-to-one or one-to-one relationships whereas selectinload should be used when loading many-to-many or one-to-many relationships where possible for query efficiency.
| - `OktaUserGroupMember`, `RoleGroupMap`, `OktaGroupTagMap`, `AppTagMap`: time-bounded via | ||
| `ended_at`. Active records: `ended_at.is_(None) OR ended_at > now()`. | ||
|
|
||
| Never query these tables without both filters. Missing one silently returns stale data. |
There was a problem hiding this comment.
Filterless queries are allowed when viewing prior data is the intention such as on audit pages or the access/role/group request/renewal pages
The development context files merged in #528 describe the pre-2.0 plugin behavior; this PR makes the interface async, so bring the two plugin-facing sections up to date. Plugin hooks are now awaited via run_hooks_to_completion (asyncio.wait, not gather) and a failing hook logs at ERROR without breaking the committed operation. The Plugin system section now states that hook impls must be async def across all four surfaces (with the sync metadata/config/status/ validation exceptions), how to do blocking I/O off the event loop, and that app-group-lifecycle mutating hooks receive an AsyncSession. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude context files for Access development
CLAUDE.md: should be automatically loaded when a session is started, contains stack, security/RBAC goals, backend architecture, async patterns, data model, authorization, and common gotchas and an index pointing to the three companion docs
access-dev-frontend.md : frontend guide including the generated src/api/ client, React Query v5, layout/design principles
access-dev-testing.md: local dev, testing, migrations using the uv/ruff/ty toolchain and Alembic
access-dev-sync.md: okta Okta syncer's authority model and notification cadence