From d2d510b50555cd8c7c26193f32bc7b3e2416f0d6 Mon Sep 17 00:00:00 2001 From: Ignacio Vidal Date: Tue, 12 May 2026 19:16:08 +0100 Subject: [PATCH] squash --- .github/workflows/samples-jaxrs.yaml | 1 + bin/configs/jaxrs-spec-quarkus-security.yaml | 11 + .../JakartaSecurityAnnotationProcessor.java | 114 ++++++++++- .../jaxrs/JavaJAXRSSpecServerCodegenTest.java | 192 ++++++++++++++++++ .../quarkus-and-multi-scoped-warns.yaml | 31 +++ ...kus-and-with-apikey-and-scoped-oauth2.yaml | 31 +++ .../quarkus-global-empty-security.yaml | 29 +++ .../quarkus-global-scopes-op-override.yaml | 37 ++++ .../quarkus-global-with-scopes.yaml | 32 +++ .../quarkus-microprofile-coexist-scoped.yaml | 26 +++ .../jaxrs-spec/quarkus-mixed-security.yaml | 56 +++++ .../quarkus-oauth2-multiple-scopes.yaml | 28 +++ .../quarkus-oauth2-or-different-scopes.yaml | 29 +++ .../quarkus-oauth2-single-scope.yaml | 35 ++++ ...uarkus-or-empty-anonymous-with-scopes.yaml | 27 +++ .../jaxrs-spec/quarkus-security/.dockerignore | 4 + .../.openapi-generator-ignore | 23 +++ .../quarkus-security/.openapi-generator/FILES | 11 + .../.openapi-generator/VERSION | 1 + .../jaxrs-spec/quarkus-security/README.md | 15 ++ .../jaxrs-spec/quarkus-security/pom.xml | 160 +++++++++++++++ .../java/org/openapitools/api/AdminApi.java | 26 +++ .../org/openapitools/api/AdminOrUserApi.java | 26 +++ .../openapitools/api/AuthenticatedApi.java | 26 +++ .../java/org/openapitools/api/PublicApi.java | 25 +++ .../src/main/docker/Dockerfile.jvm | 34 ++++ .../src/main/docker/Dockerfile.native | 22 ++ .../src/main/resources/META-INF/openapi.yaml | 66 ++++++ .../src/main/resources/application.properties | 5 + 29 files changed, 1118 insertions(+), 5 deletions(-) create mode 100644 bin/configs/jaxrs-spec-quarkus-security.yaml create mode 100644 modules/openapi-generator/src/test/resources/3_0/jaxrs-spec/quarkus-and-multi-scoped-warns.yaml create mode 100644 modules/openapi-generator/src/test/resources/3_0/jaxrs-spec/quarkus-and-with-apikey-and-scoped-oauth2.yaml create mode 100644 modules/openapi-generator/src/test/resources/3_0/jaxrs-spec/quarkus-global-empty-security.yaml create mode 100644 modules/openapi-generator/src/test/resources/3_0/jaxrs-spec/quarkus-global-scopes-op-override.yaml create mode 100644 modules/openapi-generator/src/test/resources/3_0/jaxrs-spec/quarkus-global-with-scopes.yaml create mode 100644 modules/openapi-generator/src/test/resources/3_0/jaxrs-spec/quarkus-microprofile-coexist-scoped.yaml create mode 100644 modules/openapi-generator/src/test/resources/3_0/jaxrs-spec/quarkus-mixed-security.yaml create mode 100644 modules/openapi-generator/src/test/resources/3_0/jaxrs-spec/quarkus-oauth2-multiple-scopes.yaml create mode 100644 modules/openapi-generator/src/test/resources/3_0/jaxrs-spec/quarkus-oauth2-or-different-scopes.yaml create mode 100644 modules/openapi-generator/src/test/resources/3_0/jaxrs-spec/quarkus-oauth2-single-scope.yaml create mode 100644 modules/openapi-generator/src/test/resources/3_0/jaxrs-spec/quarkus-or-empty-anonymous-with-scopes.yaml create mode 100644 samples/server/petstore/jaxrs-spec/quarkus-security/.dockerignore create mode 100644 samples/server/petstore/jaxrs-spec/quarkus-security/.openapi-generator-ignore create mode 100644 samples/server/petstore/jaxrs-spec/quarkus-security/.openapi-generator/FILES create mode 100644 samples/server/petstore/jaxrs-spec/quarkus-security/.openapi-generator/VERSION create mode 100644 samples/server/petstore/jaxrs-spec/quarkus-security/README.md create mode 100644 samples/server/petstore/jaxrs-spec/quarkus-security/pom.xml create mode 100644 samples/server/petstore/jaxrs-spec/quarkus-security/src/gen/java/org/openapitools/api/AdminApi.java create mode 100644 samples/server/petstore/jaxrs-spec/quarkus-security/src/gen/java/org/openapitools/api/AdminOrUserApi.java create mode 100644 samples/server/petstore/jaxrs-spec/quarkus-security/src/gen/java/org/openapitools/api/AuthenticatedApi.java create mode 100644 samples/server/petstore/jaxrs-spec/quarkus-security/src/gen/java/org/openapitools/api/PublicApi.java create mode 100644 samples/server/petstore/jaxrs-spec/quarkus-security/src/main/docker/Dockerfile.jvm create mode 100644 samples/server/petstore/jaxrs-spec/quarkus-security/src/main/docker/Dockerfile.native create mode 100644 samples/server/petstore/jaxrs-spec/quarkus-security/src/main/resources/META-INF/openapi.yaml create mode 100644 samples/server/petstore/jaxrs-spec/quarkus-security/src/main/resources/application.properties diff --git a/.github/workflows/samples-jaxrs.yaml b/.github/workflows/samples-jaxrs.yaml index a6a1fc384b41..698f4ae7261d 100644 --- a/.github/workflows/samples-jaxrs.yaml +++ b/.github/workflows/samples-jaxrs.yaml @@ -37,6 +37,7 @@ jobs: - samples/server/petstore/jaxrs-spec-swagger-annotations - samples/server/petstore/jaxrs-spec-swagger-v3-annotations-jakarta - samples/server/petstore/jaxrs-spec-swagger-v3-annotations + - samples/server/petstore/jaxrs-spec/quarkus-security steps: - uses: actions/checkout@v5 - uses: actions/setup-java@v5 diff --git a/bin/configs/jaxrs-spec-quarkus-security.yaml b/bin/configs/jaxrs-spec-quarkus-security.yaml new file mode 100644 index 000000000000..579036d1569a --- /dev/null +++ b/bin/configs/jaxrs-spec-quarkus-security.yaml @@ -0,0 +1,11 @@ +generatorName: jaxrs-spec +outputDir: samples/server/petstore/jaxrs-spec/quarkus-security +library: quarkus +inputSpec: modules/openapi-generator/src/test/resources/3_0/jaxrs-spec/quarkus-mixed-security.yaml +templateDir: modules/openapi-generator/src/main/resources/JavaJaxRS/spec +additionalProperties: + artifactId: jaxrs-spec-quarkus-security + hideGenerationTimestamp: "true" + useJakartaEe: "true" + useJakartaSecurityAnnotations: "true" + interfaceOnly: "true" diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JakartaSecurityAnnotationProcessor.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JakartaSecurityAnnotationProcessor.java index fe0780c303e4..2323ec0f932b 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JakartaSecurityAnnotationProcessor.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/JakartaSecurityAnnotationProcessor.java @@ -21,9 +21,12 @@ import io.swagger.v3.oas.models.Operation; import io.swagger.v3.oas.models.security.SecurityRequirement; import io.swagger.v3.oas.models.security.SecurityScheme; +import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; +import java.util.Set; +import java.util.TreeSet; import org.openapitools.codegen.CodegenOperation; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -38,10 +41,20 @@ * is still correct for the OR group. * *

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: + *

+ * + *

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 +