Commit 53b347b
authored
feat: Support securitySchemes (#60)
Parses `components.securitySchemes` and `security` requirements (root and per-operation) into a typed model, extracts credentials per scheme, and hands them to consumer-provided validators that decide allow/deny. The library renders RFC 7807 rejections — 401 with `WWW-Authenticate` for missing/malformed credentials, 403 when a validator denies.
### Supported scheme types
- apiKey (in header / query / cookie)
- http bearer
- http basic
- oauth2 / openIdConnect / mutualTLS parse to `SecurityScheme.Unsupported`; `build()` fails if any operation actually references them
### Spec model
- New sealed `SecurityScheme` (`ApiKey`, `HttpBearer`, `HttpBasic`, `Unsupported`) and `Location` enum, under `com.retailsvc.http.spec.security`
- New `SecurityRequirement` record (OR-of-AND map)
- `Spec` gains `securitySchemes` and root `security` components
- `Operation` gains `Optional<List<SecurityRequirement>> security` with override semantics: empty list means "no auth required", absent inherits root
### Public API
- Sealed `Credential` (`ApiKeyCredential`, `BearerCredential`, `BasicCredential`)
- `@FunctionalInterface SchemeValidator`: `Optional<Object> validate(Request, Credential)`
- `Request.principals()` / `principal(name)` / `withPrincipals(map)` — the validator's principal is stashed under its scheme name and survives to the handler via `ScopedValue` rebinding
### Builder
- `.securityValidator(schemeName, validator)` — register per scheme
- `.useExternalAuthentication()` — opt out of in-process enforcement for sidecar deployments (OPA/Envoy in GCP). `SecurityFilter` short-circuits, boot-time validator check is skipped, `principals()` stays empty
### Request pipeline
- New `SecurityFilter` slotted between `RequestPreparationFilter` and `DispatchHandler`; evaluates effective requirements as OR-of-AND, tries each group, rebinds `DispatchHandler.CURRENT` to a `Request` with principals on success, renders rejection on full failure
- `ProblemDetailRenderer` gains a generic `render(status, title, detail)` overload reused for 401/403 bodies
- Boot-time validation in `OpenApiServer.builder().build()`: every scheme name referenced by any operation must exist, must not be `Unsupported`, and must have a registered validator (skipped under `externalAuth`)
### Acceptance & tests
- New `/api/v1/secure/{api-key,bearer,basic,open}` operations and a `securitySchemes` block added to `openapi.json` and `openapi.yaml`. No root-level `security` — existing routes (and the k6 acceptance script) remain unauthenticated.
- `SecurityIT` covers 200 / 401 / 403 + external-auth bypass against the real `HttpServer`
- Unit coverage: scheme + requirement parsing, credential extraction per scheme type, single-scheme allow, 401 with `WWW-Authenticate`, 403, OR-of-AND, `externalAuth` bypass, boot validation (missing validator, unsupported scheme, unknown reference)
### README
- New "Security" section with scheme declaration, validator examples (apiKey / bearer / basic, pattern-matching across types), three principal patterns (domain record / claims map / plain identifier), and the `useExternalAuthentication` opt-out flow for sidecars1 parent 5bd695a commit 53b347b
37 files changed
Lines changed: 4172 additions & 37 deletions
File tree
- docs/superpowers
- plans
- specs
- src
- main/java/com/retailsvc/http
- internal
- spec
- security
- test
- java/com/retailsvc/http
- internal
- spec
- security
- start
- resources
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
305 | 305 | | |
306 | 306 | | |
307 | 307 | | |
| 308 | + | |
| 309 | + | |
| 310 | + | |
| 311 | + | |
| 312 | + | |
| 313 | + | |
| 314 | + | |
| 315 | + | |
| 316 | + | |
| 317 | + | |
| 318 | + | |
| 319 | + | |
| 320 | + | |
| 321 | + | |
| 322 | + | |
| 323 | + | |
| 324 | + | |
| 325 | + | |
| 326 | + | |
| 327 | + | |
| 328 | + | |
| 329 | + | |
| 330 | + | |
| 331 | + | |
| 332 | + | |
| 333 | + | |
| 334 | + | |
| 335 | + | |
| 336 | + | |
| 337 | + | |
| 338 | + | |
| 339 | + | |
| 340 | + | |
| 341 | + | |
| 342 | + | |
| 343 | + | |
| 344 | + | |
| 345 | + | |
| 346 | + | |
| 347 | + | |
| 348 | + | |
| 349 | + | |
| 350 | + | |
| 351 | + | |
| 352 | + | |
| 353 | + | |
| 354 | + | |
| 355 | + | |
| 356 | + | |
| 357 | + | |
| 358 | + | |
| 359 | + | |
| 360 | + | |
| 361 | + | |
| 362 | + | |
| 363 | + | |
| 364 | + | |
| 365 | + | |
| 366 | + | |
| 367 | + | |
| 368 | + | |
| 369 | + | |
| 370 | + | |
| 371 | + | |
| 372 | + | |
| 373 | + | |
| 374 | + | |
| 375 | + | |
| 376 | + | |
| 377 | + | |
| 378 | + | |
| 379 | + | |
| 380 | + | |
| 381 | + | |
| 382 | + | |
| 383 | + | |
| 384 | + | |
| 385 | + | |
| 386 | + | |
| 387 | + | |
| 388 | + | |
| 389 | + | |
| 390 | + | |
| 391 | + | |
| 392 | + | |
| 393 | + | |
| 394 | + | |
| 395 | + | |
| 396 | + | |
| 397 | + | |
| 398 | + | |
| 399 | + | |
| 400 | + | |
| 401 | + | |
| 402 | + | |
| 403 | + | |
| 404 | + | |
| 405 | + | |
| 406 | + | |
| 407 | + | |
| 408 | + | |
| 409 | + | |
| 410 | + | |
| 411 | + | |
| 412 | + | |
| 413 | + | |
| 414 | + | |
| 415 | + | |
| 416 | + | |
| 417 | + | |
| 418 | + | |
| 419 | + | |
| 420 | + | |
| 421 | + | |
| 422 | + | |
| 423 | + | |
| 424 | + | |
| 425 | + | |
| 426 | + | |
| 427 | + | |
| 428 | + | |
| 429 | + | |
| 430 | + | |
| 431 | + | |
| 432 | + | |
| 433 | + | |
| 434 | + | |
| 435 | + | |
| 436 | + | |
| 437 | + | |
| 438 | + | |
| 439 | + | |
| 440 | + | |
| 441 | + | |
| 442 | + | |
| 443 | + | |
| 444 | + | |
| 445 | + | |
| 446 | + | |
| 447 | + | |
| 448 | + | |
| 449 | + | |
| 450 | + | |
| 451 | + | |
| 452 | + | |
| 453 | + | |
| 454 | + | |
| 455 | + | |
| 456 | + | |
| 457 | + | |
| 458 | + | |
| 459 | + | |
| 460 | + | |
| 461 | + | |
| 462 | + | |
| 463 | + | |
| 464 | + | |
| 465 | + | |
| 466 | + | |
| 467 | + | |
| 468 | + | |
| 469 | + | |
| 470 | + | |
| 471 | + | |
| 472 | + | |
| 473 | + | |
| 474 | + | |
| 475 | + | |
| 476 | + | |
| 477 | + | |
| 478 | + | |
| 479 | + | |
| 480 | + | |
| 481 | + | |
| 482 | + | |
| 483 | + | |
| 484 | + | |
| 485 | + | |
| 486 | + | |
| 487 | + | |
| 488 | + | |
| 489 | + | |
| 490 | + | |
| 491 | + | |
| 492 | + | |
| 493 | + | |
| 494 | + | |
| 495 | + | |
| 496 | + | |
| 497 | + | |
| 498 | + | |
| 499 | + | |
| 500 | + | |
| 501 | + | |
| 502 | + | |
| 503 | + | |
| 504 | + | |
308 | 505 | | |
309 | 506 | | |
310 | 507 | | |
| |||
0 commit comments