Skip to content

feat: 0.5.0 — config-driven RBAC seed + JWT tenant resolver#70

Merged
jlc488 merged 2 commits into
mainfrom
feat/bootstrap-seed-rbac
Jun 3, 2026
Merged

feat: 0.5.0 — config-driven RBAC seed + JWT tenant resolver#70
jlc488 merged 2 commits into
mainfrom
feat/bootstrap-seed-rbac

Conversation

@jlc488

@jlc488 jlc488 commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

Two features for 0.5.0, both lowering the "do it yourself" barrier.

1. Config-driven RBAC seed — devslab.kit.bootstrap.seed

Ship starter permissions + roles so consumers don't hand-create them in the console. Declare your domain's permission codes and the roles that group them; the kit creates whatever is missing and grants it, idempotently on boot:

devslab:
  kit:
    bootstrap:
      enabled: true
      seed:
        permissions: [tasks.read, tasks.write, tasks.update, tasks.delete]
        roles:
          viewer: [tasks.read]
          editor: [tasks.read, tasks.write, tasks.update]
          owner:  [tasks.read, tasks.write, tasks.update, tasks.delete]
  • Additive only — every boot creates what's missing and adds listed grants; never revokes or deletes (destructive reconciliation stays the dev-only job of config-sync mirror). Change the config + redeploy → picked up.
  • A permission referenced by a role but absent from permissions is auto-created.
  • Permissions are global; roles are created in bootstrap.tenant-id.
  • DevslabKitProperties.Bootstrap.Seed + DevslabKitBootstrapRunner.seedDeclaredRbac() (shared ensurePermissionId/ensureRoleId helpers factored out of the admin bootstrap).

2. JWT tenant resolver — resolver: jwt now works

devslab.kit.tenant.resolver: jwt previously threw at startup ("requires the not-yet-shipped oauth2-resource-server-starter"). It now resolves the active tenant from the kit-issued bearer token's tenant claim (the token already carries it), falling back to default-tenant-id when there's no token.

  • JwtTenantResolver (tenant-core) — reads Authorization: Bearer, parses via AuthTokenService, uses CurrentUser.tenantId(); same request-access pattern as HeaderTenantResolver. tenant-core gains compileOnly(identity-api) only — a consumer on another resolver never pulls identity.
  • TenantAutoConfiguration case JWT wires it (AuthTokenService via ObjectProvider; clear error if identity is absent).
  • Scope: reads the kit's own HS256 token. Validating external OAuth2/OIDC tokens (JWKS, issuer, configurable claim) remains a separate future concern — the docs say so.

Tests (sample-app, Testcontainers)

  • BootstrapSeedTests (4): declared permissions + roles + grants created, role-only permission auto-created, grants exact.
  • JwtTenantResolverTests (3): wiring, resolves acme from a bearer token, falls back to default without one.

Docs / release surfaces

  • configuration reference + bootstrap/access/tenancy guides (EN/KO); jwt row corrected from "reserved" → working.
  • CHANGELOG 0.5.0 (EN/KO); README + installation + tutorial + roadmap version refs bumped to 0.5.0.

Verification: ./gradlew buildBUILD SUCCESSFUL; new tests 3/0/0 + 4/0/0, suite-wide failures=0 errors=0; mkdocs build --strict clean.


KO 요약: 0.5.0 두 기능 — (1) bootstrap.seed로 스타터 권한·역할을 부팅 시 멱등 시드(추가형, 역할 참조 권한 자동생성), (2) resolver: jwt가 이제 kit 자체 토큰의 tenant 클레임으로 실제 동작(무토큰 시 default 폴백; 외부 OIDC는 범위 밖). 테스트 7개 0/0, 전체 빌드 그린, docs strict clean.

jlc488 added 2 commits June 3, 2026 23:27
devslab.kit.bootstrap.seed provisions starter permissions + roles (with
grants) idempotently on boot, so consumers don't hand-create them in the
console. Declare your domain permission codes and the roles that group them;
the kit creates whatever is missing and grants it.

- DevslabKitProperties.Bootstrap.Seed: permissions (list) + roles (map of
  code -> permission codes)
- DevslabKitBootstrapRunner: seedDeclaredRbac() — idempotent, additive
  (never revokes/deletes); a permission referenced by a role is auto-created;
  permissions global, roles created in bootstrap tenant. Refactored shared
  ensurePermissionId/ensureRoleId helpers out of the admin bootstrap.
- BootstrapSeedTests (sample-app, Testcontainers): declared permissions +
  roles + grants created, role-only permission auto-created, grants exact.
- docs: configuration reference + bootstrap/access guides (EN/KO),
  CHANGELOG 0.5.0, version refs bumped to 0.5.0.

Also corrected the tenant `jwt` resolver note in the configuration reference
to "reserved, not yet shipped" (matches the tenancy guide).

Verification: ./gradlew build BUILD SUCCESSFUL; BootstrapSeedTests 4/0/0;
mkdocs build --strict clean.
`devslab.kit.tenant.resolver: jwt` now resolves the active tenant instead of
failing fast at startup. It reads the kit-issued bearer token (which already
carries a `tenant` claim) and falls back to `default-tenant-id` when there's no
token — e.g. the login request itself.

- JwtTenantResolver (tenant-core): reads `Authorization: Bearer`, parses via
  AuthTokenService, uses CurrentUser.tenantId(); same request-access pattern as
  HeaderTenantResolver. tenant-core gains compileOnly(identity-api) only — a
  consumer on another resolver never pulls identity.
- TenantAutoConfiguration: `case JWT` wires it (AuthTokenService via
  ObjectProvider; clear error if the identity module is absent).
- JwtTenantResolverTests (sample-app): wiring + resolves "acme" from a bearer
  token + falls back to "default" without one.
- docs: tenancy + configuration `jwt` rows go from "reserved" to working,
  scoped to the kit's own HS256 token (external OAuth2/OIDC stays out of scope);
  CHANGELOG 0.5.0.

Scope: this reads the kit's own token. Validating external OAuth2/OIDC tokens
(JWKS, issuer, configurable claim) remains a separate future concern.

Verification: ./gradlew build BUILD SUCCESSFUL; JwtTenantResolverTests 3/0/0,
BootstrapSeedTests 4/0/0; mkdocs build --strict clean.
@jlc488 jlc488 merged commit ce5fa9a into main Jun 3, 2026
2 checks passed
@jlc488 jlc488 deleted the feat/bootstrap-seed-rbac branch June 3, 2026 14:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant