Skip to content

Commit b11fae5

Browse files
committed
Bump smallrye-open-api from 3.2.0 to 3.3.0, fix OpenAPI security issues
- Handle method-level `@RolesAllowed` that override class-level `@RolesAllowed` values, fixes #30997 - Render `BaseStream<T, S>` as array of `T` in OpenAPI document, fixes #30248 (via smallrye-open-api 3.3.0) - Do not place scopes in OpenAPI security requirements unless the security scheme is OAuth2 or OIDC, fixes #27373 - Include only OIDC discovery URL in OpenAPI when auto-security is active, fixes #21126 Signed-off-by: Michael Edgar <[email protected]>
1 parent d213830 commit b11fae5

File tree

10 files changed

+251
-213
lines changed

10 files changed

+251
-213
lines changed

bom/application/pom.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@
5858
<smallrye-config.version>3.1.3</smallrye-config.version>
5959
<smallrye-health.version>4.0.1</smallrye-health.version>
6060
<smallrye-metrics.version>4.0.0</smallrye-metrics.version>
61-
<smallrye-open-api.version>3.2.0</smallrye-open-api.version>
61+
<smallrye-open-api.version>3.3.0</smallrye-open-api.version>
6262
<smallrye-graphql.version>2.1.1</smallrye-graphql.version>
6363
<smallrye-opentracing.version>3.0.3</smallrye-opentracing.version>
6464
<smallrye-fault-tolerance.version>6.2.1</smallrye-fault-tolerance.version>
@@ -1848,7 +1848,7 @@
18481848
<artifactId>quarkus-vertx-http-dev-ui-resources</artifactId>
18491849
<version>${project.version}</version>
18501850
</dependency>
1851-
1851+
18521852
<dependency>
18531853
<groupId>io.quarkus</groupId>
18541854
<artifactId>quarkus-reactive-routes</artifactId>

extensions/panache/hibernate-orm-rest-data-panache/deployment/src/test/resources/application.properties

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,6 @@ quarkus.datasource.db-kind=h2
22
quarkus.datasource.jdbc.url=jdbc:h2:mem:test
33

44
quarkus.hibernate-orm.database.generation=drop-and-create
5+
6+
# Scopes will not populate in the OpenAPI document unless the security scheme is Oauth2 or OIDC
7+
quarkus.smallrye-openapi.security-scheme=oauth2-implicit

extensions/smallrye-openapi/deployment/src/main/java/io/quarkus/smallrye/openapi/deployment/SmallRyeOpenApiProcessor.java

Lines changed: 29 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import java.util.ArrayList;
1818
import java.util.Arrays;
1919
import java.util.Collection;
20+
import java.util.Collections;
2021
import java.util.Enumeration;
2122
import java.util.HashMap;
2223
import java.util.HashSet;
@@ -320,42 +321,22 @@ OpenApiFilteredIndexViewBuildItem smallryeOpenApiIndex(CombinedIndexBuildItem co
320321

321322
@BuildStep
322323
void addAutoFilters(BuildProducer<AddToOpenAPIDefinitionBuildItem> addToOpenAPIDefinitionProducer,
324+
List<SecurityInformationBuildItem> securityInformationBuildItems,
323325
OpenApiFilteredIndexViewBuildItem apiFilteredIndexViewBuildItem,
324326
SmallRyeOpenApiConfig config) {
325327

326-
List<AnnotationInstance> rolesAllowedAnnotations = new ArrayList<>();
327-
for (DotName rolesAllowed : SecurityConstants.ROLES_ALLOWED) {
328-
rolesAllowedAnnotations.addAll(apiFilteredIndexViewBuildItem.getIndex().getAnnotations(rolesAllowed));
329-
}
330-
331-
Map<String, List<String>> methodReferences = new HashMap<>();
332-
DotName securityRequirement = DotName.createSimple(SecurityRequirement.class.getName());
333-
for (AnnotationInstance ai : rolesAllowedAnnotations) {
334-
if (ai.target().kind().equals(AnnotationTarget.Kind.METHOD)) {
335-
MethodInfo method = ai.target().asMethod();
336-
if (isValidOpenAPIMethodForAutoAdd(method, securityRequirement)) {
337-
String ref = JandexUtil.createUniqueMethodReference(method.declaringClass(), method);
338-
methodReferences.put(ref, List.of(ai.value().asStringArray()));
339-
}
340-
}
341-
if (ai.target().kind().equals(AnnotationTarget.Kind.CLASS)) {
342-
ClassInfo classInfo = ai.target().asClass();
343-
List<MethodInfo> methods = classInfo.methods();
344-
for (MethodInfo method : methods) {
345-
if (isValidOpenAPIMethodForAutoAdd(method, securityRequirement)) {
346-
String ref = JandexUtil.createUniqueMethodReference(classInfo, method);
347-
methodReferences.put(ref, List.of(ai.value().asStringArray()));
348-
}
349-
}
350-
351-
}
352-
}
353-
354328
// Add a security scheme from config
355329
if (config.securityScheme.isPresent()) {
356330
addToOpenAPIDefinitionProducer
357331
.produce(new AddToOpenAPIDefinitionBuildItem(
358332
new SecurityConfigFilter(config)));
333+
} else {
334+
OASFilter autoSecurityFilter = getAutoSecurityFilter(securityInformationBuildItems, config);
335+
336+
if (autoSecurityFilter != null) {
337+
addToOpenAPIDefinitionProducer
338+
.produce(new AddToOpenAPIDefinitionBuildItem(autoSecurityFilter));
339+
}
359340
}
360341

361342
// Add Auto roles allowed
@@ -407,34 +388,19 @@ private OASFilter getAutoSecurityFilter(List<SecurityInformationBuildItem> secur
407388
config.securitySchemeDescription,
408389
config.basicSecuritySchemeValue);
409390
case oidc:
410-
Optional<SecurityInformationBuildItem.OpenIDConnectInformation> maybeInfo = securityInformationBuildItem
411-
.getOpenIDConnectInformation();
412-
413-
if (maybeInfo.isPresent()) {
414-
SecurityInformationBuildItem.OpenIDConnectInformation info = maybeInfo.get();
415-
416-
AutoUrl authorizationUrl = new AutoUrl(
417-
config.oidcOpenIdConnectUrl.orElse(null),
418-
info.getUrlConfigKey(),
419-
"/protocol/openid-connect/auth");
420-
421-
AutoUrl refreshUrl = new AutoUrl(
422-
config.oidcOpenIdConnectUrl.orElse(null),
423-
info.getUrlConfigKey(),
424-
"/protocol/openid-connect/token");
425-
426-
AutoUrl tokenUrl = new AutoUrl(
427-
config.oidcOpenIdConnectUrl.orElse(null),
428-
info.getUrlConfigKey(),
429-
"/protocol/openid-connect/token/introspect");
430-
431-
return new OpenIDConnectSecurityFilter(
432-
config.securitySchemeName,
433-
config.securitySchemeDescription,
434-
authorizationUrl, refreshUrl, tokenUrl);
435-
436-
}
437-
break;
391+
return securityInformationBuildItem.getOpenIDConnectInformation()
392+
.map(info -> {
393+
AutoUrl openIdConnectUrl = new AutoUrl(
394+
config.oidcOpenIdConnectUrl.orElse(null),
395+
info.getUrlConfigKey(),
396+
"/.well-known/openid-configuration");
397+
398+
return new OpenIDConnectSecurityFilter(
399+
config.securitySchemeName,
400+
config.securitySchemeDescription,
401+
openIdConnectUrl);
402+
})
403+
.orElse(null);
438404
default:
439405
break;
440406
}
@@ -523,7 +489,7 @@ private Map<String, List<String>> getRolesAllowedMethodReferences(
523489
for (MethodInfo method : methods) {
524490
if (isValidOpenAPIMethodForAutoAdd(method, securityRequirement)) {
525491
String ref = JandexUtil.createUniqueMethodReference(classInfo, method);
526-
methodReferences.put(ref, List.of(ai.value().asStringArray()));
492+
methodReferences.putIfAbsent(ref, List.of(ai.value().asStringArray()));
527493
}
528494
}
529495
}
@@ -758,8 +724,7 @@ public void build(BuildProducer<FeatureBuildItem> feature,
758724
nativeImageResources.produce(new NativeImageResourceBuildItem(name));
759725
}
760726

761-
OpenApiDocument finalStoredOpenApiDocument = storeDocument(out, smallRyeOpenApiConfig, staticModel, annotationModel,
762-
openAPIBuildItems);
727+
OpenApiDocument finalStoredOpenApiDocument = storeDocument(out, smallRyeOpenApiConfig, finalDocument.get());
763728
openApiDocumentProducer.produce(new OpenApiDocumentBuildItem(finalStoredOpenApiDocument));
764729
}
765730

@@ -1041,23 +1006,19 @@ private OpenApiDocument loadDocument(OpenAPI staticModel, OpenAPI annotationMode
10411006

10421007
private OpenApiDocument storeDocument(OutputTargetBuildItem out,
10431008
SmallRyeOpenApiConfig smallRyeOpenApiConfig,
1044-
OpenAPI staticModel,
1045-
OpenAPI annotationModel,
1046-
List<AddToOpenAPIDefinitionBuildItem> openAPIBuildItems) throws IOException {
1047-
return storeDocument(out, smallRyeOpenApiConfig, staticModel, annotationModel, openAPIBuildItems, true);
1009+
OpenAPI loadedModel) throws IOException {
1010+
return storeDocument(out, smallRyeOpenApiConfig, loadedModel, true);
10481011
}
10491012

10501013
private OpenApiDocument storeDocument(OutputTargetBuildItem out,
10511014
SmallRyeOpenApiConfig smallRyeOpenApiConfig,
1052-
OpenAPI staticModel,
1053-
OpenAPI annotationModel,
1054-
List<AddToOpenAPIDefinitionBuildItem> openAPIBuildItems,
1015+
OpenAPI loadedModel,
10551016
boolean includeRuntimeFilters) throws IOException {
10561017

10571018
Config config = ConfigProvider.getConfig();
10581019
OpenApiConfig openApiConfig = new OpenApiConfigImpl(config);
10591020

1060-
OpenApiDocument document = prepareOpenApiDocument(staticModel, annotationModel, openAPIBuildItems);
1021+
OpenApiDocument document = prepareOpenApiDocument(loadedModel, null, Collections.emptyList());
10611022

10621023
if (includeRuntimeFilters) {
10631024
document.filter(filter(openApiConfig)); // This usually happens at runtime, so when storing we want to filter here too.
@@ -1074,7 +1035,7 @@ private OpenApiDocument storeDocument(OutputTargetBuildItem out,
10741035
} catch (RuntimeException re) {
10751036
if (includeRuntimeFilters) {
10761037
// This is a Runtime filter, so it might not work at build time. In that case we ignore the filter.
1077-
return storeDocument(out, smallRyeOpenApiConfig, staticModel, annotationModel, openAPIBuildItems, false);
1038+
return storeDocument(out, smallRyeOpenApiConfig, loadedModel, false);
10781039
} else {
10791040
throw re;
10801041
}
@@ -1083,7 +1044,6 @@ private OpenApiDocument storeDocument(OutputTargetBuildItem out,
10831044
boolean shouldStore = smallRyeOpenApiConfig.storeSchemaDirectory.isPresent();
10841045
if (shouldStore) {
10851046
for (Format format : Format.values()) {
1086-
String name = OpenApiConstants.BASE_NAME + format;
10871047
byte[] schemaDocument = OpenApiSerializer.serialize(document.get(), format).getBytes(StandardCharsets.UTF_8);
10881048
storeGeneratedSchema(smallRyeOpenApiConfig, out, schemaDocument, format);
10891049
}

0 commit comments

Comments
 (0)