Skip to content

Commit 02d4f11

Browse files
committed
docs(tech-design): add task list and unit/integration verify lists to auth.admin plan
11 ordered, independently-committable tasks (T1-T11) with dependencies, plus a verify list spanning type tests, mock behavior, CDK grant assertions, and a sandbox-gated integration suite (incl. negative least-privilege).
1 parent 61d8d62 commit 02d4f11

1 file changed

Lines changed: 67 additions & 0 deletions

File tree

docs/tech-design/BB-auth-cognito-admin-implementation-plan.md

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,73 @@ Port Appendix A into the repo convention (compile-is-the-test, `@ts-expect-error
193193
4. `test-apps/comprehensive` + `test-apps/native-bindings` typecheck after Step 5 fixes.
194194
5. `npm run build --workspace @aws-blocks/bb-auth-cognito` green.
195195

196+
## Task list (implementation order)
197+
198+
Each task is independently committable. T5 (`const O`) is isolated so it can be dropped if review pushes back without unwinding the rest.
199+
200+
| # | Task | Files | Depends on |
201+
|---|---|---|---|
202+
| T1 | Add admin types + gate (`AdminOptions`, `AdminActionsOf`, `AdminSurface`, `AdminDisabled`, `AdminGetterOf`, `GroupAdmin`, `LifecycleAdmin`, `AdminUser`, `AdminCreateInit`); add `admin?` to `AuthCognitoOptions` | `src/types.ts` ||
203+
| T2 | `admin.types-test.ts` (Appendix A) — land FIRST after T1 so the gate is locked before any runtime code | `src/admin.types-test.ts` | T1 |
204+
| T3 | Mock runtime `#admin` + `get admin()` + mutators on live `this.state` | `src/index.ts` | T1 |
205+
| T4 | AWS runtime `#admin` + getter against SDK `Admin*` commands | `src/index.aws.ts` | T1 |
206+
| T5 | `const O` migration on all four entries + fix the 2 `requireRole` call sites *(separable commit)* | `src/index*.ts`, `test-apps/*` | T1 |
207+
| T6 | Browser stub throwing `get admin()` | `src/index.browser.ts` | T1 |
208+
| T7 | CDK second `PolicyStatement` gated on `admin`, scoped by `actions` | `src/index.cdk.ts` | T1 |
209+
| T8 | Mock unit tests | `src/admin.test.ts` | T3 |
210+
| T9 | CDK grant unit tests | `src/index.cdk.test.ts` | T7 |
211+
| T10 | Integration: exercise `auth.admin` through the deployed Lambda backend | `test-apps/comprehensive/*` | T3,T4,T7 |
212+
| T11 | README: opt-in, narrowing, gating, session-freshness | `README.md` | T3,T4,T7 |
213+
214+
## Verify list
215+
216+
### Unit (no AWS account; runs in CI)
217+
Pattern mirrors the existing four test layers in this package.
218+
219+
**Type tests**`src/admin.types-test.ts` (compile-is-the-test, `@ts-expect-error`; run by `tsc --build`):
220+
- [ ] No `admin` opt-in ⇒ `auth.admin` member access is a compile error.
221+
- [ ] `admin: {}` ⇒ both `GroupAdmin` + `LifecycleAdmin` present.
222+
- [ ] `actions: ['groups']` ⇒ lifecycle methods are compile errors; group methods OK.
223+
- [ ] `actions: ['lifecycle']` ⇒ group methods are compile errors; lifecycle OK.
224+
- [ ] `const O` ⇒ group typo (`'editor'`) rejected **without** `as const`.
225+
- [ ] `admin: true` ⇒ construction is a compile error.
226+
- [ ] Default `O` ⇒ admin disabled; `requireRole('nope')` still rejected (const-O regression guard).
227+
228+
**Mock behavior**`src/admin.test.ts` (`node:test`, real mock instance, in-memory):
229+
- [ ] `addUserToGroup` writes to `state.groups`; visible to the same instance's next `requireRole`.
230+
- [ ] `addUserToGroup` to an unseeded group throws `GroupNotFound`.
231+
- [ ] `removeUserFromGroup` filters membership.
232+
- [ ] `createUser` mirrors `signUp` (custom-attr prefixing, password policy); `deleteUser` removes record **and** strips from every group array.
233+
- [ ] `disableUser`/`enableUser` toggle `MockUserRecord.disabled`; **regression:** `signIn` still rejects a disabled user (existing `index.ts:474` behavior unchanged).
234+
- [ ] `resetUserPassword`/`setUserPassword` set the existing `forcePasswordChange` flag (no new `forceChangePassword`).
235+
- [ ] `revokeUserSessions` deletes session records.
236+
- [ ] `scan`/`listUsersInGroup` paginate via `AsyncIterable`.
237+
- [ ] Getter throws the named error when `admin` not enabled (runtime guard for untyped JS callers).
238+
239+
**CDK**`src/index.cdk.test.ts` (`Template.fromStack` + `hasResourceProperties`):
240+
- [ ] No `admin` ⇒ synthesized handler role has **no** `Admin*` statement (byte-identical to today).
241+
- [ ] `actions: ['groups']` ⇒ exactly the 4 group actions, scoped to the pool ARN.
242+
- [ ] `actions: ['lifecycle']` ⇒ exactly the lifecycle/list actions.
243+
- [ ] `admin: {}` (omitted actions) ⇒ union of both sets.
244+
245+
**Cross-cutting**:
246+
- [ ] `tsc --build` clean across all four entries + `admin.types-test.ts`.
247+
- [ ] `conditional-exports.test.ts` (`packages/blocks/src/`) green — confirms no new subpath/export-parity work.
248+
- [ ] `npm run build --workspace @aws-blocks/bb-auth-cognito` green.
249+
- [ ] `test-apps/comprehensive` + `test-apps/native-bindings` typecheck after the T5 call-site fixes.
250+
251+
### Integration (live AWS; sandbox-gated)
252+
Exercises the real deployed surface through the Lambda backend, the way `test-apps/comprehensive/test/sandbox-admin-e2e.ts` already does for client methods. **Prereqs:** deployed `bb-test-*` sandbox, `AWS_PROFILE` with Cognito admin + CFN-describe perms, run from `test-apps/comprehensive`.
253+
- [ ] Deploy comprehensive backend with `admin: { actions: ['groups','lifecycle'] }` on the pool; CFN synth/deploy succeeds and the handler role carries the admin statement.
254+
- [ ] `auth.admin.createUser` → real Cognito user appears (`AdminGetUser` confirms).
255+
- [ ] `auth.admin.addUserToGroup` → then `signIn` + `requireRole('admins')` succeeds (membership took effect on fresh token).
256+
- [ ] Group change on an **existing** session is NOT retroactive until refresh; `revokeUserSessions` forces re-auth and the new claim appears (session-freshness contract).
257+
- [ ] `auth.admin.disableUser` → subsequent `signIn` returns `NotAuthorizedException`; `enableUser` restores it.
258+
- [ ] `setUserPassword(permanent:true)``signIn` with the new password succeeds, no `NEW_PASSWORD_REQUIRED`.
259+
- [ ] `scan` paginates across a >1-page user set.
260+
- [ ] **Negative least-privilege:** a pool deployed WITHOUT `admin` → calling an admin route 500s with an IAM `AccessDenied` server-side (no grant), proving the opt-in gates the grant.
261+
- [ ] Teardown via existing `sandbox-destroy.ts`.
262+
196263
## Open questions (carried from the counter-proposal)
197264
1. Property name: `admin` vs a louder `adminApi`.
198265
2. `actions` granularity: `'groups' | 'lifecycle'` vs 1:1 mapping to individual `Admin*` actions.

0 commit comments

Comments
 (0)