[jaxrs-spec][quarkus] Emit @RolesAllowed({scope}) for OAuth2 and OpenID Connect operations with explicit scopes#23752
Open
Ignacio-Vidal wants to merge 1 commit into
Conversation
2def231 to
54089bc
Compare
wing328
reviewed
May 12, 2026
| @@ -0,0 +1,11 @@ | |||
| generatorName: jaxrs-spec | |||
| outputDir: samples/server/petstore/jaxrs-spec/quarkus-security | |||
Member
There was a problem hiding this comment.
please add this to the github workflow so that CI will test it moving forward
Contributor
Author
There was a problem hiding this comment.
Done.
This PR (#23672) builds on top of #23680. Which option would you prefer?
- Merge [jaxrs-spec][quarkus] Emit @RolesAllowed({"**"}) for HTTP Basic, Bearer, api-key and OAuth2 or OpenID with empty scopes and rename "useQuarkusSecurityAnnotations" to "useJakartaSecurityAnnotations" #23680 first and second rebase this against master
- Merge this ([jaxrs-spec][quarkus] Emit @RolesAllowed({scope}) for OAuth2 and OpenID Connect operations with explicit scopes #23752) directly?
Member
There was a problem hiding this comment.
please rebase to resolve merge conflicts when you've time and then i'll merge it
Contributor
Author
There was a problem hiding this comment.
@wing328 - done, rebased and tested in a separate project as usual:
- See the api spec with all cases covered in this MR
- See the adapter implementing the generated interface
- See the http.requests using intellij client
- See the integration tests using the generated client with microprofile library
54089bc to
7fca67e
Compare
Contributor
There was a problem hiding this comment.
4 issues found across 50 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="samples/server/petstore/jaxrs-spec/quarkus-security/src/main/docker/Dockerfile.native">
<violation number="1" location="samples/server/petstore/jaxrs-spec/quarkus-security/src/main/docker/Dockerfile.native:22">
P2: Container runs as root because no non-root `USER` is set before startup.</violation>
</file>
<file name="docs/generators/jaxrs-cxf-cdi.md">
<violation number="1" location="docs/generators/jaxrs-cxf-cdi.md:85">
P2: Docs overstate `useJakartaSecurityAnnotations` by claiming `@PermitAll` generation even though this PR only implements `@RolesAllowed`.</violation>
</file>
<file name="samples/server/petstore/jaxrs-spec/quarkus-security/src/main/docker/Dockerfile.jvm">
<violation number="1" location="samples/server/petstore/jaxrs-spec/quarkus-security/src/main/docker/Dockerfile.jvm:17">
P1: This sample container is based on Java 8, but the Quarkus 3 sample it runs requires a newer supported JRE, so the image is incompatible and can fail at runtime.</violation>
</file>
<file name="docs/generators/jaxrs-spec.md">
<violation number="1" location="docs/generators/jaxrs-spec.md:86">
P3: The option description overstates current behavior by advertising `@PermitAll` generation even though that path is intentionally deferred and not implemented yet.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
7fca67e to
357218f
Compare
357218f to
d2d510b
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This is part 3 of #23691 to improve authentication and authorisation support in the
jaxrs-spec/quarkusserver generator, building on #23680.What this PR does
Extends the
useJakartaSecurityAnnotationsflag (introduced in #23680) to also emit@jakarta.annotation.security.RolesAllowed({"scope1","scope2"})on JAX-RS interface methods and implementation stubs for operations whose OAuth2 or OpenID Connect security requirements carry explicit scopes.PR #23680 covered the any-authenticated-user case (
@RolesAllowed({"**"})). This PR covers the scoped-role case: when every OR alternative has at least one scope, the generator emits a single@RolesAllowedwhose value is the deduplicated, alphabetically sorted union of all scopes across those alternatives.The wildcard and scoped emissions are mutually exclusive per operation: if any OR alternative qualifies as "any authenticated user", the wildcard wins and no scoped annotation is emitted on that operation.
Semantic rules
When
@RolesAllowed({scope})is emitted@RolesAllowed({union_of_all_scopes})@RolesAllowed({"**"})wins — PR #23680 path- {})@PermitAll(next PR)WARNlogOR scope union
When all OR alternatives are scoped, their scope lists are unioned, deduplicated, and sorted alphabetically:
AND group — single scoped scheme
A single
SecurityRequirementmap with multiple keys is an AND group. The generator emits scoped roles only when at most one scheme in the AND group carries scopes. Unscoped co-required schemes (HTTP, API key, mTLS) contribute nothing to the scope list:If two or more schemes in the same AND group carry competing scope sets, Quarkus cannot enforce AND-of-different-scopes with a single annotation, so no annotation is emitted and a
WARNis logged.Global vs per-operation security
Operations with no
securityfield inheritopenapi.security. Operations with an explicitsecurity: []override produce no annotation regardless of the global block. A globalsecurity: []block (top-level empty list) also suppresses emission on inheriting operations.Why scoped and wildcard are mutually exclusive
Emitting both
@RolesAllowed({"**"})and@RolesAllowed({"admin"})on the same method would cause Quarkus to apply both interceptors with AND semantics — net effect is the stricter annotation alone, which contradicts the OR intent of the spec. The processor short-circuits in the wildcard branch and never evaluates the scoped path for the same operation.Implementation notes
Single vendor extension. The processor sets a single
x-jakarta-roles-allowedvendor extension whose value is aList<String>, and collapses what was previously two mutually-exclusive flags (x-jakarta-any-rolesboolean andx-jakarta-roles-allowedlist) into one["**"]for the wildcard case (any-authenticated-user).Processor changes in
JakartaSecurityAnnotationProcessor:x-jakarta-roles-allowed = ["**"]instead of the separatex-jakarta-any-rolesboolean.collectRolesAllowedScopes(returns the deduplicated sorted union as aList<String>, ornullif the operation does not qualify).collectAndGroupScopes(extracts the scope list from a single AND group, enforcing the single-scoped-scheme rule).resolveSchemeshelper so both code paths share theopenAPI.getComponents()...getSecuritySchemes()lookup.applyToshort-circuits after setting the wildcard extension, then falls through to the scoped path. Mutual exclusion is enforced by the data shape (one extension), not by template logic.No changes to
JavaJAXRSSpecServerCodegen. The existingfromOperationoverride from #23680 already delegates to the processor.Template change in
jakartaSecurityAnnotations.mustache:.0(the list's first element being truthy) to emit the annotation exactly once.{{openbrace}}/{{closebrace}}(globally defined inDefaultCodegen) avoid Mustache parser ambiguity when{immediately follows a tag close.apiInterface.mustacheandapiMethod.mustache.New CI sample
bin/configs/jaxrs-spec-quarkus-security.yamlexercises all four annotation paths (security: [], empty-scope, single-scope, OR-union-scope) from a single spec fixture. The generated sample is committed undersamples/server/petstore/jaxrs-spec/quarkus-security/.Test coverage
A new parameterised test (
quarkusEmitsExpectedScopedRolesAllowedCount) drives a 16-row@DataProvidercovering:quarkus-oauth2-single-scope.yamlquarkus-oauth2-multiple-scopes.yamlquarkus-oauth2-or-different-scopes.yamlquarkus-oauth2-with-scopes.yamlquarkus-openidconnect-with-scopes.yamlquarkus-global-with-scopes.yamlquarkus-global-scopes-op-override.yamlquarkus-and-with-apikey-and-scoped-oauth2.yamlquarkus-and-multi-scoped-warns.yamlquarkus-oauth2-or-empty-and-scoped.yamlquarkus-or-empty-anonymous-with-scopes.yamlThe data provider exercises both
interfaceOnly=true(apiInterface.mustache) andinterfaceOnly=false(apiMethod.mustache) for the most representative fixtures, so regressions in either template path are caught.Additional targeted tests verify scope content and ordering (
quarkusEmitsAlphabeticallySortedDeduplicatedScopes,quarkusOrAlternativesProduceUnionedScopes,quarkusGlobalScopesAreInheritedByOperationsWithoutOverride,quarkusOperationOverrideShadowsGlobalScopedSecurity), MicroProfile coexistence (quarkusScopedJakartaCoexistsWithMicroProfileAnnotations), mutual exclusion (quarkusNeverEmitsBothWildcardAndScopedRolesAllowedOnSameOperation), global empty-security inheritance (quarkusGlobalEmptySecurityListEmitsNothing), and end-to-end validation of the CI sample's four endpoints (quarkusMixedSecuritySampleEmitsAllExpectedAnnotations). All 32 existing PR #23680 test rows continue to pass unchanged.Closes #23691 (partial —
@PermitAllfor anonymous/unannotated operations remains for the next PR).@wing328 @jmini @MelleD
PR checklist
Commit all changed files.
This is important, as CI jobs will verify all generator outputs of your HEAD commit as it would merge with master.
These must match the expectations made by your contribution.
You may regenerate an individual generator by passing the relevant config(s) as an argument to the script, for example
./bin/generate-samples.sh bin/configs/java*.IMPORTANT: Do NOT purge/delete any folders/files (e.g. tests) when regenerating the samples as manually written tests may be removed.
master(upcoming7.x.0minor release - breaking changes with fallbacks),8.0.x(breaking changes without fallbacks)"fixes #123"present in the PR description)Summary by cubic
Adds
@jakarta.annotation.security.RolesAllowedto Quarkus JAX‑RS stubs for any‑authenticated operations and for OAuth2/OpenID Connect operations with explicit scopes, via a singlex-jakarta-roles-allowedvendor extension. Includes a new Quarkus sample and CI wiring.New Features
@jakarta.annotation.security.RolesAllowed({"**"})when any OR alternative is unscoped (HTTP, API key, mTLS, or OAuth2/OIDC with no scopes); anonymous (- {}) emits nothing. Wildcard and scoped roles are mutually exclusive per operation.security, coexists withuseMicroProfileOpenAPIAnnotations, and renders on interface and implementation templates viax-jakarta-roles-allowed.samples/server/petstore/jaxrs-spec/quarkus-security, workflow entry, and targeted tests covering OAuth2/OIDC, OR/AND groups, global/override cases, and mutual exclusion.Migration
useQuarkusSecurityAnnotationswithuseJakartaSecurityAnnotations.useJakartaEe=trueandlibrary=quarkus(validated).--additional-properties=useJakartaEe=true,useJakartaSecurityAnnotations=true,library=quarkus.Written for commit d2d510b. Summary will update on new commits.