A single vendor extension {@code x-jakarta-roles-allowed} carries the value to
- * emit. For the any-authenticated-user case it is set to the singleton list
- * {@code ["**"]}, producing {@code @RolesAllowed({"**"})}. Future PRs will reuse
- * the same extension to emit scoped roles (e.g. {@code ["admin"]}) without needing
- * a second flag or template branch.
+ * emit:
+ *
+ *
{@code ["**"]} for the any-authenticated-user case, producing
+ * {@code @RolesAllowed({"**"})}.
+ *
A sorted, deduplicated list of scope names (e.g. {@code ["admin", "user"]})
+ * when every OR alternative is scoped, producing
+ * {@code @RolesAllowed({"admin","user"})}.
+ *
Unset when the operation does not qualify (anonymous OR alternative,
+ * mixed-scope AND group, etc.).
+ *
+ *
+ *
The wildcard and scoped emissions are mutually exclusive per operation: if any
+ * OR alternative qualifies as "any authenticated user", the wildcard wins and the
+ * scoped path is skipped.
*/
final class JakartaSecurityAnnotationProcessor {
@@ -70,6 +83,11 @@ void applyTo(CodegenOperation op, Operation rawOp, OpenAPI openAPI) {
if (qualifiesForAnyRoles(requirements, schemes)) {
op.vendorExtensions.put(VENDOR_X_JAKARTA_ROLES_ALLOWED, ANY_AUTHENTICATED_ROLE);
+ return; // mutually exclusive -- short-circuit before the scoped path runs
+ }
+ List scopes = collectRolesAllowedScopes(requirements, schemes);
+ if (scopes != null && !scopes.isEmpty()) {
+ op.vendorExtensions.put(VENDOR_X_JAKARTA_ROLES_ALLOWED, scopes);
}
}
@@ -132,7 +150,7 @@ private boolean schemeQualifies(SecurityScheme scheme, List scopes) {
case OAUTH2:
case OPENIDCONNECT:
// Empty scope list means the operation requires authentication but no specific role,
- // so @RolesAllowed({"**"}) is correct. Non-empty scopes belong to a future @RolesAllowed({scope}) PR.
+ // so @RolesAllowed({"**"}) is correct. Non-empty scopes are handled by collectRolesAllowedScopes.
return scopes == null || scopes.isEmpty();
case HTTP:
case APIKEY:
@@ -146,6 +164,92 @@ private boolean schemeQualifies(SecurityScheme scheme, List scopes) {
}
}
+ /**
+ * Returns the deduplicated, alphabetically sorted union of scope names across every OR
+ * alternative, or {@code null} if the requirement set does not qualify (anonymous OR
+ * alternative, mixed-scope AND group, undefined scheme, or no requirements at all).
+ *
+ *
A {@code null} return means the scoped {@code @RolesAllowed} annotation must not
+ * be emitted for this operation.
+ */
+ private List collectRolesAllowedScopes(List requirements,
+ Map schemes) {
+ if (requirements == null || requirements.isEmpty()) {
+ return null;
+ }
+ Set union = new TreeSet<>(); // sorted, deduplicated
+ for (SecurityRequirement requirement : requirements) {
+ if (requirement.isEmpty()) {
+ // Anonymous OR alternative -- defer to @PermitAll (future PR).
+ return null;
+ }
+ List groupScopes = collectAndGroupScopes(requirement, schemes);
+ if (groupScopes == null) {
+ // Unscopable AND group -- bail the entire operation.
+ return null;
+ }
+ union.addAll(groupScopes);
+ }
+ return new ArrayList<>(union);
+ }
+
+ /**
+ * Returns the scope list contributed by a single AND group, or {@code null} if the AND
+ * group cannot be expressed as a single Jakarta {@code @RolesAllowed} annotation.
+ *
+ *
At most ONE scheme in the AND group may have non-empty scopes (the "scoped scheme").
+ * If two or more schemes carry competing scope sets, Quarkus annotations cannot express
+ * the AND-of-different-scope-sets relationship -- we log a warning and return {@code null}.
+ *
+ *
An empty list (not {@code null}) is returned when the AND group is valid but no
+ * scheme contributes scopes; the caller treats that as "no scopes from this alternative".
+ */
+ private List collectAndGroupScopes(SecurityRequirement requirement,
+ Map schemes) {
+ List scopedSchemeScopes = null;
+ int scopedSchemeCount = 0;
+ for (Map.Entry> entry : requirement.entrySet()) {
+ SecurityScheme scheme = schemes.get(entry.getKey());
+ if (scheme == null) {
+ LOGGER.warn("Security requirement references undefined scheme '{}' -- skipping Jakarta scoped @RolesAllowed for this operation.",
+ entry.getKey());
+ return null;
+ }
+ if (scheme.getType() == null) {
+ LOGGER.warn("Security scheme '{}' is missing 'type' -- skipping Jakarta scoped @RolesAllowed.",
+ entry.getKey());
+ return null;
+ }
+ switch (scheme.getType()) {
+ case OAUTH2:
+ case OPENIDCONNECT:
+ List scopes = entry.getValue();
+ if (scopes != null && !scopes.isEmpty()) {
+ scopedSchemeCount++;
+ if (scopedSchemeCount > 1) {
+ LOGGER.warn(
+ "AND-group contains multiple scoped schemes (e.g. '{}'); Jakarta @RolesAllowed cannot express AND of different scope sets -- skipping scoped @RolesAllowed for this operation.",
+ entry.getKey());
+ return null;
+ }
+ scopedSchemeScopes = scopes;
+ }
+ // Unscoped OAuth2/OIDC contributes nothing to the scope list.
+ break;
+ case HTTP:
+ case APIKEY:
+ case MUTUALTLS:
+ // No scope concept; participates in the AND group but contributes no scopes.
+ break;
+ default:
+ LOGGER.warn("Unrecognised security scheme type '{}' -- skipping Jakarta scoped @RolesAllowed.",
+ scheme.getType());
+ return null;
+ }
+ }
+ return scopedSchemeScopes != null ? scopedSchemeScopes : Collections.emptyList();
+ }
+
private static Map resolveSchemes(OpenAPI openAPI) {
if (openAPI.getComponents() != null && openAPI.getComponents().getSecuritySchemes() != null) {
return openAPI.getComponents().getSecuritySchemes();
diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/jaxrs/JavaJAXRSSpecServerCodegenTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/jaxrs/JavaJAXRSSpecServerCodegenTest.java
index d633a448ea29..ccbad01925f8 100644
--- a/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/jaxrs/JavaJAXRSSpecServerCodegenTest.java
+++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/java/jaxrs/JavaJAXRSSpecServerCodegenTest.java
@@ -47,6 +47,14 @@ public class JavaJAXRSSpecServerCodegenTest extends JavaJaxrsBaseTest {
private static final String ROLES_ALLOWED_WILDCARD_PATTERN =
"@jakarta\\.annotation\\.security\\.RolesAllowed\\(\\{" + '"' + "\\*\\*" + '"' + "\\}\\)";
+ // Regex matching @jakarta.annotation.security.RolesAllowed({"scope1","scope2",...}) -- one or more
+ // named scopes. Excludes the {"**"} wildcard form via a negative lookahead on the first quoted value.
+ private static final String ROLES_ALLOWED_SCOPED_PATTERN =
+ "@jakarta\\.annotation\\.security\\.RolesAllowed\\(\\{"
+ + '"' + "(?!\\*\\*" + '"' + ")[^" + '"' + "]+" + '"'
+ + "(?:," + '"' + "[^" + '"' + "]+" + '"' + ")*"
+ + "\\}\\)";
+
@BeforeMethod
public void before() {
codegen = new JavaJAXRSSpecServerCodegen();
@@ -1702,6 +1710,190 @@ public void quarkusJakartaSecurityCoexistsWithMicroProfileAnnotations() throws E
"Expected MicroProfile @SecurityRequirement annotation alongside @RolesAllowed");
}
+ @DataProvider(name = "quarkusJakartaScopedRolesCases")
+ public Object[][] quarkusJakartaScopedRolesCases() {
+ return new Object[][] {
+ // {specPath, interfaceOnly, expectedScopedAnnotationCount, expectedWildcardCount}
+ // OAuth2 scoped -- reuse PR-1 fixtures; must now emit scoped, not wildcard.
+ // Both interfaceOnly modes exercised to guard against regressions in apiInterface.mustache vs apiMethod.mustache.
+ {"src/test/resources/3_0/jaxrs-spec/quarkus-oauth2-with-scopes.yaml", true, 2, 0},
+ {"src/test/resources/3_0/jaxrs-spec/quarkus-oauth2-with-scopes.yaml", false, 2, 0},
+ // Single scope, both interface and implementation modes
+ {"src/test/resources/3_0/jaxrs-spec/quarkus-oauth2-single-scope.yaml", true, 2, 0},
+ {"src/test/resources/3_0/jaxrs-spec/quarkus-oauth2-single-scope.yaml", false, 2, 0},
+ // Multiple scopes and OR-union
+ {"src/test/resources/3_0/jaxrs-spec/quarkus-oauth2-multiple-scopes.yaml", true, 1, 0},
+ {"src/test/resources/3_0/jaxrs-spec/quarkus-oauth2-multiple-scopes.yaml", false, 1, 0},
+ {"src/test/resources/3_0/jaxrs-spec/quarkus-oauth2-or-different-scopes.yaml", true, 1, 0},
+ // OpenID Connect scoped -- reuse PR-1 fixture
+ {"src/test/resources/3_0/jaxrs-spec/quarkus-openidconnect-with-scopes.yaml", true, 2, 0},
+ // Global security inheritance and override paths
+ {"src/test/resources/3_0/jaxrs-spec/quarkus-global-with-scopes.yaml", true, 2, 0},
+ {"src/test/resources/3_0/jaxrs-spec/quarkus-global-scopes-op-override.yaml", true, 1, 0},
+ // AND groups
+ {"src/test/resources/3_0/jaxrs-spec/quarkus-and-with-apikey-and-scoped-oauth2.yaml", true, 1, 0},
+ {"src/test/resources/3_0/jaxrs-spec/quarkus-and-multi-scoped-warns.yaml", true, 0, 0},
+ // GET: unscoped OR scoped -> wildcard wins. POST: scoped-only -> scoped emitted. One of each.
+ // Exercised in both modes since this is the canonical "mutual exclusion at file level" fixture.
+ {"src/test/resources/3_0/jaxrs-spec/quarkus-oauth2-or-empty-and-scoped.yaml", true, 1, 1},
+ {"src/test/resources/3_0/jaxrs-spec/quarkus-oauth2-or-empty-and-scoped.yaml", false, 1, 1},
+ // Anonymous OR alternative -- neither annotation emitted
+ {"src/test/resources/3_0/jaxrs-spec/quarkus-or-empty-anonymous-with-scopes.yaml", true, 0, 0},
+ {"src/test/resources/3_0/jaxrs-spec/quarkus-or-empty-anonymous-with-scopes.yaml", false, 0, 0},
+ };
+ }
+
+ @Test(dataProvider = "quarkusJakartaScopedRolesCases")
+ public void quarkusEmitsExpectedScopedRolesAllowedCount(String specPath, boolean interfaceOnly,
+ int expectedScopedCount, int expectedWildcardCount) throws Exception {
+ final String content = generateQuarkusItemsApi(specPath, interfaceOnly, true, false);
+ Assert.assertEquals(TestUtils.countOccurrences(content, ROLES_ALLOWED_SCOPED_PATTERN),
+ expectedScopedCount,
+ "scoped @RolesAllowed count mismatch for " + specPath);
+ Assert.assertEquals(TestUtils.countOccurrences(content, ROLES_ALLOWED_WILDCARD_PATTERN),
+ expectedWildcardCount,
+ "wildcard @RolesAllowed count mismatch for " + specPath);
+ }
+
+ @Test
+ public void quarkusEmitsAlphabeticallySortedDeduplicatedScopes() throws Exception {
+ final String content = generateQuarkusItemsApi(
+ "src/test/resources/3_0/jaxrs-spec/quarkus-oauth2-multiple-scopes.yaml",
+ true, true, false);
+ assertContainsRolesAllowed(content, "read:items", "write:items");
+ }
+
+ @Test
+ public void quarkusOrAlternativesProduceUnionedScopes() throws Exception {
+ final String content = generateQuarkusItemsApi(
+ "src/test/resources/3_0/jaxrs-spec/quarkus-oauth2-or-different-scopes.yaml",
+ true, true, false);
+ assertContainsRolesAllowed(content, "admin", "user");
+ }
+
+ @Test
+ public void quarkusGlobalScopesAreInheritedByOperationsWithoutOverride() throws Exception {
+ final String content = generateQuarkusItemsApi(
+ "src/test/resources/3_0/jaxrs-spec/quarkus-global-with-scopes.yaml",
+ true, true, false);
+ Assert.assertEquals(TestUtils.countOccurrences(content, ROLES_ALLOWED_SCOPED_PATTERN), 2);
+ assertContainsRolesAllowed(content, "admin");
+ }
+
+ @Test
+ public void quarkusOperationOverrideShadowsGlobalScopedSecurity() throws Exception {
+ final String content = generateQuarkusItemsApi(
+ "src/test/resources/3_0/jaxrs-spec/quarkus-global-scopes-op-override.yaml",
+ true, true, false);
+ Assert.assertEquals(TestUtils.countOccurrences(content, ROLES_ALLOWED_SCOPED_PATTERN), 1,
+ "Expected exactly one scoped @RolesAllowed (GET with user scope)");
+ assertContainsRolesAllowed(content, "user");
+ Assert.assertFalse(
+ content.contains("RolesAllowed({" + '"' + "admin" + '"'),
+ "@RolesAllowed scope 'admin' must be shadowed by the per-operation override");
+ }
+
+ @Test
+ public void quarkusGlobalEmptySecurityListEmitsNothing() throws Exception {
+ // A top-level empty `security: []` block means "no security requirements at all" -- neither
+ // @RolesAllowed nor (future) @PermitAll should be emitted on inheriting operations.
+ final String content = generateQuarkusItemsApi(
+ "src/test/resources/3_0/jaxrs-spec/quarkus-global-empty-security.yaml",
+ true, true, false);
+ Assert.assertEquals(TestUtils.countOccurrences(content, ROLES_ALLOWED_WILDCARD_PATTERN), 0,
+ "Expected no wildcard @RolesAllowed when global security is empty");
+ Assert.assertEquals(TestUtils.countOccurrences(content, ROLES_ALLOWED_SCOPED_PATTERN), 0,
+ "Expected no scoped @RolesAllowed when global security is empty");
+ }
+
+ @Test
+ public void quarkusScopedJakartaCoexistsWithMicroProfileAnnotations() throws Exception {
+ final String content = generateQuarkusItemsApi(
+ "src/test/resources/3_0/jaxrs-spec/quarkus-microprofile-coexist-scoped.yaml",
+ true, true, true);
+ Assert.assertEquals(TestUtils.countOccurrences(content, ROLES_ALLOWED_SCOPED_PATTERN), 1,
+ "Expected exactly one scoped @RolesAllowed annotation");
+ Assert.assertTrue(content.contains("SecurityRequirement"),
+ "Expected MicroProfile @SecurityRequirement annotation alongside scoped @RolesAllowed");
+ }
+
+ @Test
+ public void quarkusNeverEmitsBothWildcardAndScopedRolesAllowedOnSameOperation() throws Exception {
+ // oauth2-scoped-or-api-key: GET has scoped OAuth2 OR unscoped API key -- wildcard wins, 0 scoped.
+ // POST has scoped OAuth2 only -- scoped emitted, 0 wildcard. Verifies they never appear together.
+ final String content = generateQuarkusItemsApi(
+ "src/test/resources/3_0/jaxrs-spec/quarkus-oauth2-scoped-or-api-key.yaml",
+ true, true, false);
+ Assert.assertEquals(TestUtils.countOccurrences(content, ROLES_ALLOWED_WILDCARD_PATTERN), 1,
+ "Expected wildcard @RolesAllowed on GET (API key in OR lifts it)");
+ Assert.assertEquals(TestUtils.countOccurrences(content, ROLES_ALLOWED_SCOPED_PATTERN), 1,
+ "Expected scoped @RolesAllowed on POST (scoped OAuth2 only)");
+ // The two annotations must be on different methods -- total 2 annotations, none doubled
+ Assert.assertEquals(
+ TestUtils.countOccurrences(content, ROLES_ALLOWED_WILDCARD_PATTERN)
+ + TestUtils.countOccurrences(content, ROLES_ALLOWED_SCOPED_PATTERN),
+ 2,
+ "Each method must have at most one @RolesAllowed annotation");
+ }
+
+ @Test
+ public void quarkusMixedSecuritySampleEmitsAllExpectedAnnotations() throws Exception {
+ final File output = Files.createTempDirectory("test").toFile().getCanonicalFile();
+ output.deleteOnExit();
+
+ final OpenAPI openAPI = new OpenAPIParser()
+ .readLocation("src/test/resources/3_0/jaxrs-spec/quarkus-mixed-security.yaml", null, new ParseOptions())
+ .getOpenAPI();
+
+ codegen.setOutputDir(output.getAbsolutePath());
+ codegen.setLibrary(QUARKUS_LIBRARY);
+ codegen.additionalProperties().put(INTERFACE_ONLY, true);
+ codegen.additionalProperties().put(USE_JAKARTA_EE, true);
+ codegen.additionalProperties().put(USE_JAKARTA_SECURITY_ANNOTATIONS, true);
+
+ final DefaultGenerator generator = new DefaultGenerator();
+ final List files = generator.opts(new ClientOptInput().openAPI(openAPI).config(codegen)).generate();
+ validateJavaSourceFiles(files);
+
+ // /public -- security: [] -- no annotation
+ String publicApi = readGeneratedApi(output, "PublicApi.java");
+ Assert.assertEquals(TestUtils.countOccurrences(publicApi, ROLES_ALLOWED_WILDCARD_PATTERN), 0,
+ "PublicApi should not have @RolesAllowed -- security: [] disables it");
+ Assert.assertEquals(TestUtils.countOccurrences(publicApi, ROLES_ALLOWED_SCOPED_PATTERN), 0,
+ "PublicApi should not have scoped @RolesAllowed");
+
+ // /authenticated -- oauth2: [] -- wildcard @RolesAllowed({"**"})
+ String authenticatedApi = readGeneratedApi(output, "AuthenticatedApi.java");
+ Assert.assertEquals(TestUtils.countOccurrences(authenticatedApi, ROLES_ALLOWED_WILDCARD_PATTERN), 1,
+ "AuthenticatedApi should have exactly one wildcard @RolesAllowed");
+
+ // /admin -- oauth2: [admin] -- @RolesAllowed({"admin"})
+ String adminApi = readGeneratedApi(output, "AdminApi.java");
+ Assert.assertEquals(TestUtils.countOccurrences(adminApi, ROLES_ALLOWED_SCOPED_PATTERN), 1);
+ assertContainsRolesAllowed(adminApi, "admin");
+
+ // /admin-or-user -- oauth2: [admin] OR oauth2: [user] -- @RolesAllowed({"admin","user"})
+ String adminOrUserApi = readGeneratedApi(output, "AdminOrUserApi.java");
+ Assert.assertEquals(TestUtils.countOccurrences(adminOrUserApi, ROLES_ALLOWED_SCOPED_PATTERN), 1);
+ assertContainsRolesAllowed(adminOrUserApi, "admin", "user");
+ }
+
+ private static String readGeneratedApi(File outputDir, String apiFileName) throws Exception {
+ return Files.readString(outputDir.toPath().resolve("src/gen/java/org/openapitools/api/" + apiFileName));
+ }
+
+ private static void assertContainsRolesAllowed(String content, String... expectedScopes) {
+ StringBuilder expected = new StringBuilder("@jakarta.annotation.security.RolesAllowed({");
+ for (int i = 0; i < expectedScopes.length; i++) {
+ if (i > 0) expected.append(",");
+ expected.append('"').append(expectedScopes[i]).append('"');
+ }
+ expected.append("})");
+ Assert.assertTrue(
+ content.contains(expected.toString()),
+ "Expected to find: " + expected + "\nin generated content");
+ }
+
private String generateQuarkusItemsApi(String specPath, boolean interfaceOnly, boolean useJakartaSecurity, boolean useMicroProfile) throws Exception {
final File output = Files.createTempDirectory("test").toFile().getCanonicalFile();
output.deleteOnExit();
diff --git a/modules/openapi-generator/src/test/resources/3_0/jaxrs-spec/quarkus-and-multi-scoped-warns.yaml b/modules/openapi-generator/src/test/resources/3_0/jaxrs-spec/quarkus-and-multi-scoped-warns.yaml
new file mode 100644
index 000000000000..024d0ffed9ff
--- /dev/null
+++ b/modules/openapi-generator/src/test/resources/3_0/jaxrs-spec/quarkus-and-multi-scoped-warns.yaml
@@ -0,0 +1,31 @@
+openapi: 3.0.1
+info:
+ title: Quarkus AND group — multiple scoped schemes (should NOT emit annotation)
+ version: '1.0'
+servers:
+ - url: 'http://localhost:8080/'
+paths:
+ /items:
+ get:
+ operationId: getItems
+ summary: AND group with two scoped schemes — unscopable, no annotation emitted
+ security:
+ - oauth2_scheme:
+ - read:items
+ openid_scheme:
+ - write:items
+ responses:
+ '200':
+ description: OK
+components:
+ securitySchemes:
+ oauth2_scheme:
+ type: oauth2
+ flows:
+ clientCredentials:
+ tokenUrl: https://example.com/oauth/token
+ scopes:
+ read:items: Read items
+ openid_scheme:
+ type: openIdConnect
+ openIdConnectUrl: https://example.com/.well-known/openid-configuration
diff --git a/modules/openapi-generator/src/test/resources/3_0/jaxrs-spec/quarkus-and-with-apikey-and-scoped-oauth2.yaml b/modules/openapi-generator/src/test/resources/3_0/jaxrs-spec/quarkus-and-with-apikey-and-scoped-oauth2.yaml
new file mode 100644
index 000000000000..2cd721d206e8
--- /dev/null
+++ b/modules/openapi-generator/src/test/resources/3_0/jaxrs-spec/quarkus-and-with-apikey-and-scoped-oauth2.yaml
@@ -0,0 +1,31 @@
+openapi: 3.0.1
+info:
+ title: Quarkus AND group — API key AND scoped OAuth2 (single scoped scheme)
+ version: '1.0'
+servers:
+ - url: 'http://localhost:8080/'
+paths:
+ /items:
+ get:
+ operationId: getItems
+ summary: AND group with one scopeless scheme (apiKey) and one scoped scheme (oauth2)
+ security:
+ - api_key: []
+ oauth2_scheme:
+ - admin
+ responses:
+ '200':
+ description: OK
+components:
+ securitySchemes:
+ api_key:
+ type: apiKey
+ in: header
+ name: X-API-Key
+ oauth2_scheme:
+ type: oauth2
+ flows:
+ clientCredentials:
+ tokenUrl: https://example.com/oauth/token
+ scopes:
+ admin: Admin access
diff --git a/modules/openapi-generator/src/test/resources/3_0/jaxrs-spec/quarkus-global-empty-security.yaml b/modules/openapi-generator/src/test/resources/3_0/jaxrs-spec/quarkus-global-empty-security.yaml
new file mode 100644
index 000000000000..81946fe3cbff
--- /dev/null
+++ b/modules/openapi-generator/src/test/resources/3_0/jaxrs-spec/quarkus-global-empty-security.yaml
@@ -0,0 +1,29 @@
+openapi: 3.0.1
+info:
+ title: Quarkus global empty security -- no annotation emitted on any operation
+ version: '1.0'
+servers:
+ - url: 'http://localhost:8080/'
+security: []
+paths:
+ /items:
+ get:
+ operationId: getItems
+ summary: Inherits the global empty security list -- no annotation
+ responses:
+ '200':
+ description: OK
+ post:
+ operationId: createItem
+ summary: Same as GET -- inherits the global empty security list
+ responses:
+ '201':
+ description: Created
+components:
+ securitySchemes:
+ oauth2_scheme:
+ type: oauth2
+ flows:
+ clientCredentials:
+ tokenUrl: https://example.com/oauth/token
+ scopes: {}
diff --git a/modules/openapi-generator/src/test/resources/3_0/jaxrs-spec/quarkus-global-scopes-op-override.yaml b/modules/openapi-generator/src/test/resources/3_0/jaxrs-spec/quarkus-global-scopes-op-override.yaml
new file mode 100644
index 000000000000..6c4114176273
--- /dev/null
+++ b/modules/openapi-generator/src/test/resources/3_0/jaxrs-spec/quarkus-global-scopes-op-override.yaml
@@ -0,0 +1,37 @@
+openapi: 3.0.1
+info:
+ title: Quarkus global scoped security with per-operation overrides
+ version: '1.0'
+servers:
+ - url: 'http://localhost:8080/'
+security:
+ - oauth2_scheme:
+ - admin
+paths:
+ /items:
+ get:
+ operationId: getItems
+ summary: Overrides global admin with user scope
+ security:
+ - oauth2_scheme:
+ - user
+ responses:
+ '200':
+ description: OK
+ post:
+ operationId: createItem
+ summary: Disables security entirely — no annotation emitted
+ security: []
+ responses:
+ '201':
+ description: Created
+components:
+ securitySchemes:
+ oauth2_scheme:
+ type: oauth2
+ flows:
+ clientCredentials:
+ tokenUrl: https://example.com/oauth/token
+ scopes:
+ admin: Admin access
+ user: User access
diff --git a/modules/openapi-generator/src/test/resources/3_0/jaxrs-spec/quarkus-global-with-scopes.yaml b/modules/openapi-generator/src/test/resources/3_0/jaxrs-spec/quarkus-global-with-scopes.yaml
new file mode 100644
index 000000000000..f0c3cfd8f2b3
--- /dev/null
+++ b/modules/openapi-generator/src/test/resources/3_0/jaxrs-spec/quarkus-global-with-scopes.yaml
@@ -0,0 +1,32 @@
+openapi: 3.0.1
+info:
+ title: Quarkus global scoped security — inherited by both operations
+ version: '1.0'
+servers:
+ - url: 'http://localhost:8080/'
+security:
+ - oauth2_scheme:
+ - admin
+paths:
+ /items:
+ get:
+ operationId: getItems
+ summary: Inherits global scoped security
+ responses:
+ '200':
+ description: OK
+ post:
+ operationId: createItem
+ summary: Also inherits global scoped security
+ responses:
+ '201':
+ description: Created
+components:
+ securitySchemes:
+ oauth2_scheme:
+ type: oauth2
+ flows:
+ clientCredentials:
+ tokenUrl: https://example.com/oauth/token
+ scopes:
+ admin: Admin access
diff --git a/modules/openapi-generator/src/test/resources/3_0/jaxrs-spec/quarkus-microprofile-coexist-scoped.yaml b/modules/openapi-generator/src/test/resources/3_0/jaxrs-spec/quarkus-microprofile-coexist-scoped.yaml
new file mode 100644
index 000000000000..bdc9483b65c2
--- /dev/null
+++ b/modules/openapi-generator/src/test/resources/3_0/jaxrs-spec/quarkus-microprofile-coexist-scoped.yaml
@@ -0,0 +1,26 @@
+openapi: 3.0.1
+info:
+ title: Quarkus scoped security with MicroProfile OpenAPI annotations enabled
+ version: '1.0'
+servers:
+ - url: 'http://localhost:8080/'
+paths:
+ /items:
+ get:
+ operationId: getItems
+ summary: Both @SecurityRequirement (MicroProfile) and scoped @RolesAllowed (Jakarta) should be emitted
+ security:
+ - oauth2_scheme:
+ - admin
+ responses:
+ '200':
+ description: OK
+components:
+ securitySchemes:
+ oauth2_scheme:
+ type: oauth2
+ flows:
+ clientCredentials:
+ tokenUrl: https://example.com/oauth/token
+ scopes:
+ admin: Admin access
diff --git a/modules/openapi-generator/src/test/resources/3_0/jaxrs-spec/quarkus-mixed-security.yaml b/modules/openapi-generator/src/test/resources/3_0/jaxrs-spec/quarkus-mixed-security.yaml
new file mode 100644
index 000000000000..7db250deb445
--- /dev/null
+++ b/modules/openapi-generator/src/test/resources/3_0/jaxrs-spec/quarkus-mixed-security.yaml
@@ -0,0 +1,56 @@
+openapi: 3.0.1
+info:
+ title: Quarkus mixed security CI sample
+ version: '1.0'
+servers:
+ - url: 'http://localhost:8080/'
+paths:
+ /public:
+ get:
+ operationId: getPublic
+ summary: Explicitly disabled security — no annotation emitted
+ security: []
+ responses:
+ '200':
+ description: OK
+ /authenticated:
+ get:
+ operationId: getAuthenticated
+ summary: Unscoped OAuth2 — emits @RolesAllowed({"**"})
+ security:
+ - oauth2_scheme: []
+ responses:
+ '200':
+ description: OK
+ /admin:
+ get:
+ operationId: getAdmin
+ summary: Single scoped OAuth2 — emits @RolesAllowed({"admin"})
+ security:
+ - oauth2_scheme:
+ - admin
+ responses:
+ '200':
+ description: OK
+ /admin-or-user:
+ get:
+ operationId: getAdminOrUser
+ summary: Two scoped OR alternatives — emits @RolesAllowed({"admin", "user"})
+ security:
+ - oauth2_scheme:
+ - admin
+ - oauth2_scheme:
+ - user
+ responses:
+ '200':
+ description: OK
+components:
+ securitySchemes:
+ oauth2_scheme:
+ type: oauth2
+ flows:
+ clientCredentials:
+ tokenUrl: https://example.com/oauth/token
+ scopes:
+ admin: Admin access
+ user: User access
diff --git a/modules/openapi-generator/src/test/resources/3_0/jaxrs-spec/quarkus-oauth2-multiple-scopes.yaml b/modules/openapi-generator/src/test/resources/3_0/jaxrs-spec/quarkus-oauth2-multiple-scopes.yaml
new file mode 100644
index 000000000000..5074986c69ed
--- /dev/null
+++ b/modules/openapi-generator/src/test/resources/3_0/jaxrs-spec/quarkus-oauth2-multiple-scopes.yaml
@@ -0,0 +1,28 @@
+openapi: 3.0.1
+info:
+ title: Quarkus OAuth2 multiple scopes test
+ version: '1.0'
+servers:
+ - url: 'http://localhost:8080/'
+paths:
+ /items:
+ get:
+ operationId: getItems
+ summary: Get items — multiple scopes on one operation; expect alphabetical emission
+ security:
+ - oauth2_scheme:
+ - write:items
+ - read:items
+ responses:
+ '200':
+ description: OK
+components:
+ securitySchemes:
+ oauth2_scheme:
+ type: oauth2
+ flows:
+ clientCredentials:
+ tokenUrl: https://example.com/oauth/token
+ scopes:
+ read:items: Read items
+ write:items: Write items
diff --git a/modules/openapi-generator/src/test/resources/3_0/jaxrs-spec/quarkus-oauth2-or-different-scopes.yaml b/modules/openapi-generator/src/test/resources/3_0/jaxrs-spec/quarkus-oauth2-or-different-scopes.yaml
new file mode 100644
index 000000000000..d69bef6dfb18
--- /dev/null
+++ b/modules/openapi-generator/src/test/resources/3_0/jaxrs-spec/quarkus-oauth2-or-different-scopes.yaml
@@ -0,0 +1,29 @@
+openapi: 3.0.1
+info:
+ title: Quarkus OAuth2 OR different scopes — union test
+ version: '1.0'
+servers:
+ - url: 'http://localhost:8080/'
+paths:
+ /items:
+ get:
+ operationId: getItems
+ summary: GET — OR two scoped alternatives; expect union {"admin", "user"}
+ security:
+ - oauth2_scheme:
+ - admin
+ - oauth2_scheme:
+ - user
+ responses:
+ '200':
+ description: OK
+components:
+ securitySchemes:
+ oauth2_scheme:
+ type: oauth2
+ flows:
+ clientCredentials:
+ tokenUrl: https://example.com/oauth/token
+ scopes:
+ admin: Admin access
+ user: User access
diff --git a/modules/openapi-generator/src/test/resources/3_0/jaxrs-spec/quarkus-oauth2-single-scope.yaml b/modules/openapi-generator/src/test/resources/3_0/jaxrs-spec/quarkus-oauth2-single-scope.yaml
new file mode 100644
index 000000000000..1a263dc77a66
--- /dev/null
+++ b/modules/openapi-generator/src/test/resources/3_0/jaxrs-spec/quarkus-oauth2-single-scope.yaml
@@ -0,0 +1,35 @@
+openapi: 3.0.1
+info:
+ title: Quarkus OAuth2 single scope test
+ version: '1.0'
+servers:
+ - url: 'http://localhost:8080/'
+paths:
+ /items:
+ get:
+ operationId: getItems
+ summary: Get items — single scoped OAuth2
+ security:
+ - oauth2_scheme:
+ - admin
+ responses:
+ '200':
+ description: OK
+ post:
+ operationId: createItem
+ summary: Create item — single scoped OAuth2
+ security:
+ - oauth2_scheme:
+ - admin
+ responses:
+ '201':
+ description: Created
+components:
+ securitySchemes:
+ oauth2_scheme:
+ type: oauth2
+ flows:
+ clientCredentials:
+ tokenUrl: https://example.com/oauth/token
+ scopes:
+ admin: Admin access
diff --git a/modules/openapi-generator/src/test/resources/3_0/jaxrs-spec/quarkus-or-empty-anonymous-with-scopes.yaml b/modules/openapi-generator/src/test/resources/3_0/jaxrs-spec/quarkus-or-empty-anonymous-with-scopes.yaml
new file mode 100644
index 000000000000..8675086ce934
--- /dev/null
+++ b/modules/openapi-generator/src/test/resources/3_0/jaxrs-spec/quarkus-or-empty-anonymous-with-scopes.yaml
@@ -0,0 +1,27 @@
+openapi: 3.0.1
+info:
+ title: Quarkus anonymous OR alternative with scoped scheme — no annotation emitted
+ version: '1.0'
+servers:
+ - url: 'http://localhost:8080/'
+paths:
+ /items:
+ get:
+ operationId: getItems
+ summary: Scoped OAuth2 OR anonymous — anonymous wins, no annotation emitted (deferred to @PermitAll PR 3)
+ security:
+ - oauth2_scheme:
+ - admin
+ - {}
+ responses:
+ '200':
+ description: OK
+components:
+ securitySchemes:
+ oauth2_scheme:
+ type: oauth2
+ flows:
+ clientCredentials:
+ tokenUrl: https://example.com/oauth/token
+ scopes:
+ admin: Admin access
diff --git a/samples/server/petstore/jaxrs-spec/quarkus-security/.dockerignore b/samples/server/petstore/jaxrs-spec/quarkus-security/.dockerignore
new file mode 100644
index 000000000000..b86c7ac34057
--- /dev/null
+++ b/samples/server/petstore/jaxrs-spec/quarkus-security/.dockerignore
@@ -0,0 +1,4 @@
+*
+!target/*-runner
+!target/*-runner.jar
+!target/lib/*
\ No newline at end of file
diff --git a/samples/server/petstore/jaxrs-spec/quarkus-security/.openapi-generator-ignore b/samples/server/petstore/jaxrs-spec/quarkus-security/.openapi-generator-ignore
new file mode 100644
index 000000000000..7484ee590a38
--- /dev/null
+++ b/samples/server/petstore/jaxrs-spec/quarkus-security/.openapi-generator-ignore
@@ -0,0 +1,23 @@
+# OpenAPI Generator Ignore
+# Generated by openapi-generator https://github.com/openapitools/openapi-generator
+
+# Use this file to prevent files from being overwritten by the generator.
+# The patterns follow closely to .gitignore or .dockerignore.
+
+# As an example, the C# client generator defines ApiClient.cs.
+# You can make changes and tell OpenAPI Generator to ignore just this file by uncommenting the following line:
+#ApiClient.cs
+
+# You can match any string of characters against a directory, file or extension with a single asterisk (*):
+#foo/*/qux
+# The above matches foo/bar/qux and foo/baz/qux, but not foo/bar/baz/qux
+
+# You can recursively match patterns against a directory, file or extension with a double asterisk (**):
+#foo/**/qux
+# This matches foo/bar/qux, foo/baz/qux, and foo/bar/baz/qux
+
+# You can also negate patterns with an exclamation (!).
+# For example, you can ignore all files in a docs folder with the file extension .md:
+#docs/*.md
+# Then explicitly reverse the ignore rule for a single file:
+#!docs/README.md
diff --git a/samples/server/petstore/jaxrs-spec/quarkus-security/.openapi-generator/FILES b/samples/server/petstore/jaxrs-spec/quarkus-security/.openapi-generator/FILES
new file mode 100644
index 000000000000..b25995466d6a
--- /dev/null
+++ b/samples/server/petstore/jaxrs-spec/quarkus-security/.openapi-generator/FILES
@@ -0,0 +1,11 @@
+.dockerignore
+README.md
+pom.xml
+src/gen/java/org/openapitools/api/AdminApi.java
+src/gen/java/org/openapitools/api/AdminOrUserApi.java
+src/gen/java/org/openapitools/api/AuthenticatedApi.java
+src/gen/java/org/openapitools/api/PublicApi.java
+src/main/docker/Dockerfile.jvm
+src/main/docker/Dockerfile.native
+src/main/resources/META-INF/openapi.yaml
+src/main/resources/application.properties
diff --git a/samples/server/petstore/jaxrs-spec/quarkus-security/.openapi-generator/VERSION b/samples/server/petstore/jaxrs-spec/quarkus-security/.openapi-generator/VERSION
new file mode 100644
index 000000000000..ca7bf6e46889
--- /dev/null
+++ b/samples/server/petstore/jaxrs-spec/quarkus-security/.openapi-generator/VERSION
@@ -0,0 +1 @@
+7.23.0-SNAPSHOT
diff --git a/samples/server/petstore/jaxrs-spec/quarkus-security/README.md b/samples/server/petstore/jaxrs-spec/quarkus-security/README.md
new file mode 100644
index 000000000000..578b4ebc0f3a
--- /dev/null
+++ b/samples/server/petstore/jaxrs-spec/quarkus-security/README.md
@@ -0,0 +1,15 @@
+# JAX-RS server with OpenAPI using Quarkus
+
+## Overview
+This server was generated by the [OpenAPI Generator](https://openapi-generator.tech) project. By using an
+[OpenAPI-Spec](https://openapis.org), you can easily generate a server stub.
+
+This is an example of building a OpenAPI-enabled JAX-RS server.
+This example uses the [JAX-RS](https://jax-rs-spec.java.net/) framework and
+the [Eclipse-MicroProfile-OpenAPI](https://github.com/eclipse/microprofile-open-api) addition.
+
+The pom file is configured to use [Quarkus](https://quarkus.io/) as application server.
+
+This project produces a jar that defines some interfaces.
+The jar can be used in combination with another project providing the implementation.
+
diff --git a/samples/server/petstore/jaxrs-spec/quarkus-security/pom.xml b/samples/server/petstore/jaxrs-spec/quarkus-security/pom.xml
new file mode 100644
index 000000000000..2365a84b7e0a
--- /dev/null
+++ b/samples/server/petstore/jaxrs-spec/quarkus-security/pom.xml
@@ -0,0 +1,160 @@
+
+
+ 4.0.0
+ org.openapitools
+ jaxrs-spec-quarkus-security
+ jaxrs-spec-quarkus-security
+ 1.0
+
+
+
+ 3.8.1
+ true
+ 1.8
+ 1.8
+ UTF-8
+ UTF-8
+ 3.0.1.Final
+ 3.0.1.Final
+ quarkus-universe-bom
+ io.quarkus
+ 2.22.1
+ 3.1.0
+ 2.1.1
+ 0.2.10
+
+
+
+
+ ${quarkus.platform.group-id}
+ ${quarkus.platform.artifact-id}
+ ${quarkus.platform.version}
+ pom
+ import
+
+
+
+
+
+ io.quarkus
+ quarkus-resteasy
+
+
+ io.quarkus
+ quarkus-junit5
+ test
+
+
+ io.rest-assured
+ rest-assured
+ test
+
+
+ io.quarkus
+ quarkus-smallrye-openapi
+
+
+ io.quarkus.resteasy.reactive
+ resteasy-reactive
+
+
+ jakarta.ws.rs
+ jakarta.ws.rs-api
+ ${jakarta.ws.rs-version}
+ provided
+
+
+ jakarta.annotation
+ jakarta.annotation-api
+ ${jakarta.annotation-api-version}
+
+
+ org.openapitools
+ jackson-databind-nullable
+ ${jackson-databind-nullable-version}
+
+
+
+
+
+ org.codehaus.mojo
+ build-helper-maven-plugin
+ 1.9.1
+
+
+ add-source
+ generate-sources
+
+ add-source
+
+
+
+ src/gen/java
+
+
+
+
+
+
+ io.quarkus
+ quarkus-maven-plugin
+ ${quarkus-plugin.version}
+
+
+
+ build
+
+
+
+
+
+ maven-compiler-plugin
+ ${compiler-plugin.version}
+
+
+ maven-surefire-plugin
+ ${surefire-plugin.version}
+
+
+ org.jboss.logmanager.LogManager
+
+
+
+
+
+
+
+ native
+
+
+ native
+
+
+
+
+
+ maven-failsafe-plugin
+ ${surefire-plugin.version}
+
+
+
+ integration-test
+ verify
+
+
+
+ ${project.build.directory}/${project.build.finalName}-runner
+
+
+
+
+
+
+
+
+ native
+
+
+
+
diff --git a/samples/server/petstore/jaxrs-spec/quarkus-security/src/gen/java/org/openapitools/api/AdminApi.java b/samples/server/petstore/jaxrs-spec/quarkus-security/src/gen/java/org/openapitools/api/AdminApi.java
new file mode 100644
index 000000000000..3c8fe4796862
--- /dev/null
+++ b/samples/server/petstore/jaxrs-spec/quarkus-security/src/gen/java/org/openapitools/api/AdminApi.java
@@ -0,0 +1,26 @@
+package org.openapitools.api;
+
+
+import jakarta.ws.rs.*;
+import jakarta.ws.rs.core.Response;
+import org.jboss.resteasy.reactive.ResponseStatus;
+
+
+
+import java.io.InputStream;
+import java.util.Map;
+import java.util.List;
+import jakarta.validation.constraints.*;
+import jakarta.validation.Valid;
+
+
+@Path("/admin")
+@jakarta.annotation.Generated(value = "org.openapitools.codegen.languages.JavaJAXRSSpecServerCodegen", comments = "Generator version: 7.23.0-SNAPSHOT")
+public interface AdminApi {
+
+ @GET
+ @ResponseStatus(200)
+ @jakarta.annotation.security.RolesAllowed({"admin"})
+ void getAdmin();
+
+}
diff --git a/samples/server/petstore/jaxrs-spec/quarkus-security/src/gen/java/org/openapitools/api/AdminOrUserApi.java b/samples/server/petstore/jaxrs-spec/quarkus-security/src/gen/java/org/openapitools/api/AdminOrUserApi.java
new file mode 100644
index 000000000000..123d7ecbcfc6
--- /dev/null
+++ b/samples/server/petstore/jaxrs-spec/quarkus-security/src/gen/java/org/openapitools/api/AdminOrUserApi.java
@@ -0,0 +1,26 @@
+package org.openapitools.api;
+
+
+import jakarta.ws.rs.*;
+import jakarta.ws.rs.core.Response;
+import org.jboss.resteasy.reactive.ResponseStatus;
+
+
+
+import java.io.InputStream;
+import java.util.Map;
+import java.util.List;
+import jakarta.validation.constraints.*;
+import jakarta.validation.Valid;
+
+
+@Path("/admin-or-user")
+@jakarta.annotation.Generated(value = "org.openapitools.codegen.languages.JavaJAXRSSpecServerCodegen", comments = "Generator version: 7.23.0-SNAPSHOT")
+public interface AdminOrUserApi {
+
+ @GET
+ @ResponseStatus(200)
+ @jakarta.annotation.security.RolesAllowed({"admin","user"})
+ void getAdminOrUser();
+
+}
diff --git a/samples/server/petstore/jaxrs-spec/quarkus-security/src/gen/java/org/openapitools/api/AuthenticatedApi.java b/samples/server/petstore/jaxrs-spec/quarkus-security/src/gen/java/org/openapitools/api/AuthenticatedApi.java
new file mode 100644
index 000000000000..1ca510b4b413
--- /dev/null
+++ b/samples/server/petstore/jaxrs-spec/quarkus-security/src/gen/java/org/openapitools/api/AuthenticatedApi.java
@@ -0,0 +1,26 @@
+package org.openapitools.api;
+
+
+import jakarta.ws.rs.*;
+import jakarta.ws.rs.core.Response;
+import org.jboss.resteasy.reactive.ResponseStatus;
+
+
+
+import java.io.InputStream;
+import java.util.Map;
+import java.util.List;
+import jakarta.validation.constraints.*;
+import jakarta.validation.Valid;
+
+
+@Path("/authenticated")
+@jakarta.annotation.Generated(value = "org.openapitools.codegen.languages.JavaJAXRSSpecServerCodegen", comments = "Generator version: 7.23.0-SNAPSHOT")
+public interface AuthenticatedApi {
+
+ @GET
+ @ResponseStatus(200)
+ @jakarta.annotation.security.RolesAllowed({"**"})
+ void getAuthenticated();
+
+}
diff --git a/samples/server/petstore/jaxrs-spec/quarkus-security/src/gen/java/org/openapitools/api/PublicApi.java b/samples/server/petstore/jaxrs-spec/quarkus-security/src/gen/java/org/openapitools/api/PublicApi.java
new file mode 100644
index 000000000000..0efb2d428eee
--- /dev/null
+++ b/samples/server/petstore/jaxrs-spec/quarkus-security/src/gen/java/org/openapitools/api/PublicApi.java
@@ -0,0 +1,25 @@
+package org.openapitools.api;
+
+
+import jakarta.ws.rs.*;
+import jakarta.ws.rs.core.Response;
+import org.jboss.resteasy.reactive.ResponseStatus;
+
+
+
+import java.io.InputStream;
+import java.util.Map;
+import java.util.List;
+import jakarta.validation.constraints.*;
+import jakarta.validation.Valid;
+
+
+@Path("/public")
+@jakarta.annotation.Generated(value = "org.openapitools.codegen.languages.JavaJAXRSSpecServerCodegen", comments = "Generator version: 7.23.0-SNAPSHOT")
+public interface PublicApi {
+
+ @GET
+ @ResponseStatus(200)
+ void getPublic();
+
+}
diff --git a/samples/server/petstore/jaxrs-spec/quarkus-security/src/main/docker/Dockerfile.jvm b/samples/server/petstore/jaxrs-spec/quarkus-security/src/main/docker/Dockerfile.jvm
new file mode 100644
index 000000000000..fa24d2b2cbf6
--- /dev/null
+++ b/samples/server/petstore/jaxrs-spec/quarkus-security/src/main/docker/Dockerfile.jvm
@@ -0,0 +1,34 @@
+####
+# This Dockerfile is used in order to build a container that runs the Quarkus application in JVM mode
+#
+# Before building the docker image run:
+#
+# mvn package
+#
+# Then, build the image with:
+#
+# docker build -f src/main/docker/Dockerfile.jvm -t quarkus/jaxrs-spec-quarkus-security-jvm .
+#
+# Then run the container using:
+#
+# docker run -i --rm -p 8080:8080 quarkus/jaxrs-spec-quarkus-security-jvm
+#
+###
+FROM fabric8/java-alpine-openjdk8-jre:1.6.5
+ENV JAVA_OPTIONS="-Dquarkus.http.host=0.0.0.0 -Djava.util.logging.manager=org.jboss.logmanager.LogManager"
+ENV AB_ENABLED=jmx_exporter
+
+# Be prepared for running in OpenShift too
+RUN adduser -G root --no-create-home --disabled-password 1001 \
+ && chown -R 1001 /deployments \
+ && chmod -R "g+rwX" /deployments \
+ && chown -R 1001:root /deployments
+
+COPY target/lib/* /deployments/lib/
+COPY target/*-runner.jar /deployments/app.jar
+EXPOSE 8080
+
+# run with user 1001
+USER 1001
+
+ENTRYPOINT [ "/deployments/run-java.sh" ]
\ No newline at end of file
diff --git a/samples/server/petstore/jaxrs-spec/quarkus-security/src/main/docker/Dockerfile.native b/samples/server/petstore/jaxrs-spec/quarkus-security/src/main/docker/Dockerfile.native
new file mode 100644
index 000000000000..d58f0e70db5b
--- /dev/null
+++ b/samples/server/petstore/jaxrs-spec/quarkus-security/src/main/docker/Dockerfile.native
@@ -0,0 +1,22 @@
+####
+# This Dockerfile is used in order to build a container that runs the Quarkus application in native (no JVM) mode
+#
+# Before building the docker image run:
+#
+# mvn package -Pnative -Dquarkus.native.container-build=true
+#
+# Then, build the image with:
+#
+# docker build -f src/main/docker/Dockerfile.native -t quarkus/jaxrs-spec-quarkus-security .
+#
+# Then run the container using:
+#
+# docker run -i --rm -p 8080:8080 quarkus/jaxrs-spec-quarkus-security
+#
+###
+FROM registry.access.redhat.com/ubi8/ubi-minimal
+WORKDIR /work/
+COPY target/*-runner /work/application
+RUN chmod 775 /work
+EXPOSE 8080
+CMD ["./application", "-Dquarkus.http.host=0.0.0.0"]
\ No newline at end of file
diff --git a/samples/server/petstore/jaxrs-spec/quarkus-security/src/main/resources/META-INF/openapi.yaml b/samples/server/petstore/jaxrs-spec/quarkus-security/src/main/resources/META-INF/openapi.yaml
new file mode 100644
index 000000000000..bb0a1bec03b2
--- /dev/null
+++ b/samples/server/petstore/jaxrs-spec/quarkus-security/src/main/resources/META-INF/openapi.yaml
@@ -0,0 +1,66 @@
+openapi: 3.0.1
+info:
+ title: Quarkus mixed security CI sample
+ version: "1.0"
+servers:
+- url: http://localhost:8080/
+paths:
+ /public:
+ get:
+ operationId: getPublic
+ responses:
+ "200":
+ description: OK
+ security: []
+ summary: Explicitly disabled security — no annotation emitted
+ x-accepts:
+ - application/json
+ /authenticated:
+ get:
+ operationId: getAuthenticated
+ responses:
+ "200":
+ description: OK
+ security:
+ - oauth2_scheme: []
+ summary: "Unscoped OAuth2 — emits @RolesAllowed({\"**\"})"
+ x-accepts:
+ - application/json
+ /admin:
+ get:
+ operationId: getAdmin
+ responses:
+ "200":
+ description: OK
+ security:
+ - oauth2_scheme:
+ - admin
+ summary: "Single scoped OAuth2 — emits @RolesAllowed({\"admin\"})"
+ x-accepts:
+ - application/json
+ /admin-or-user:
+ get:
+ operationId: getAdminOrUser
+ responses:
+ "200":
+ description: OK
+ security:
+ - oauth2_scheme:
+ - admin
+ - oauth2_scheme:
+ - user
+ summary: "Two scoped OR alternatives — emits @RolesAllowed({\"admin\", \"user\"\
+ })"
+ x-accepts:
+ - application/json
+components:
+ schemas: {}
+ securitySchemes:
+ oauth2_scheme:
+ flows:
+ clientCredentials:
+ scopes:
+ admin: Admin access
+ user: User access
+ tokenUrl: https://example.com/oauth/token
+ type: oauth2
diff --git a/samples/server/petstore/jaxrs-spec/quarkus-security/src/main/resources/application.properties b/samples/server/petstore/jaxrs-spec/quarkus-security/src/main/resources/application.properties
new file mode 100644
index 000000000000..83b16e96d391
--- /dev/null
+++ b/samples/server/petstore/jaxrs-spec/quarkus-security/src/main/resources/application.properties
@@ -0,0 +1,5 @@
+# Configuration file
+# key = value
+
+mp.openapi.scan.disable=true
+