diff --git a/core/processor/src/main/java/io/quarkus/annotation/processor/Constants.java b/core/processor/src/main/java/io/quarkus/annotation/processor/Constants.java index 9795032c37a5c..a46b2d74ba7c1 100644 --- a/core/processor/src/main/java/io/quarkus/annotation/processor/Constants.java +++ b/core/processor/src/main/java/io/quarkus/annotation/processor/Constants.java @@ -60,6 +60,9 @@ final public class Constants { public static final String ANNOTATION_CONFIG_DOC_MAP_KEY = "io.quarkus.runtime.annotations.ConfigDocMapKey"; public static final String ANNOTATION_CONFIG_DOC_SECTION = "io.quarkus.runtime.annotations.ConfigDocSection"; + public static final String ANNOTATION_CONFIG_WITH_NAME = "io.smallrye.config.WithName"; + public static final String ANNOTATION_CONFIG_WITH_DEFAULT = "io.smallrye.config.WithDefault"; + public static final Set SUPPORTED_ANNOTATIONS_TYPES = Set.of(ANNOTATION_BUILD_STEP, ANNOTATION_CONFIG_GROUP, ANNOTATION_CONFIG_ROOT, ANNOTATION_TEMPLATE, ANNOTATION_RECORDER); diff --git a/core/processor/src/main/java/io/quarkus/annotation/processor/generate_doc/ConfigDoItemFinder.java b/core/processor/src/main/java/io/quarkus/annotation/processor/generate_doc/ConfigDoItemFinder.java index ef8542ccfc9de..f7b9447c448d6 100644 --- a/core/processor/src/main/java/io/quarkus/annotation/processor/generate_doc/ConfigDoItemFinder.java +++ b/core/processor/src/main/java/io/quarkus/annotation/processor/generate_doc/ConfigDoItemFinder.java @@ -3,6 +3,8 @@ import static io.quarkus.annotation.processor.Constants.ANNOTATION_CONFIG_DOC_MAP_KEY; import static io.quarkus.annotation.processor.Constants.ANNOTATION_CONFIG_DOC_SECTION; import static io.quarkus.annotation.processor.Constants.ANNOTATION_CONFIG_ITEM; +import static io.quarkus.annotation.processor.Constants.ANNOTATION_CONFIG_WITH_DEFAULT; +import static io.quarkus.annotation.processor.Constants.ANNOTATION_CONFIG_WITH_NAME; import static io.quarkus.annotation.processor.Constants.ANNOTATION_CONVERT_WITH; import static io.quarkus.annotation.processor.Constants.ANNOTATION_DEFAULT_CONVERTER; import static io.quarkus.annotation.processor.Constants.DOT; @@ -137,7 +139,7 @@ private List recursivelyFindConfigItems(Element element, String r } for (Element enclosedElement : element.getEnclosedElements()) { - if (!enclosedElement.getKind().isField() && (!enclosedElement.getKind().equals(ElementKind.METHOD) || !isMapping)) { + if (!enclosedElement.getKind().isField() && (!isMapping || !enclosedElement.getKind().equals(ElementKind.METHOD))) { continue; } @@ -153,7 +155,7 @@ private List recursivelyFindConfigItems(Element element, String r String name = null; String defaultValue = NO_DEFAULT; String defaultValueDoc = EMPTY; - final TypeMirror typeMirror = enclosedElement.asType(); + final TypeMirror typeMirror = unwrapTypeMirror(enclosedElement.asType()); String type = typeMirror.toString(); List acceptedValues = null; final TypeElement clazz = (TypeElement) element; @@ -217,6 +219,19 @@ private List recursivelyFindConfigItems(Element element, String r || annotationName.equals(ANNOTATION_CONVERT_WITH)) { useHyphenateEnumValue = false; } + + // Mappings + if (isMapping) { + for (Map.Entry entry : annotationMirror + .getElementValues().entrySet()) { + Object value = entry.getValue().getValue(); + if (annotationName.equals(ANNOTATION_CONFIG_WITH_NAME)) { + name = parentName + DOT + value; + } else if (annotationName.equals(ANNOTATION_CONFIG_WITH_DEFAULT)) { + defaultValue = value.toString(); + } + } + } } if (isDeprecated) { @@ -247,24 +262,8 @@ private List recursivelyFindConfigItems(Element element, String r boolean list = false; boolean optional = false; if (!typeMirror.getKind().isPrimitive()) { - TypeElement typeElement; - DeclaredType declaredType; - if (typeMirror instanceof DeclaredType) { - declaredType = (DeclaredType) typeMirror; - typeElement = (TypeElement) declaredType.asElement(); - } else if (typeMirror instanceof ExecutableType) { - ExecutableType executableType = (ExecutableType) typeMirror; - TypeMirror returnType = executableType.getReturnType(); - if (returnType instanceof DeclaredType) { - declaredType = ((DeclaredType) returnType); - typeElement = (TypeElement) declaredType.asElement(); - } else { - continue; - } - } else { - continue; - } - + DeclaredType declaredType = (DeclaredType) typeMirror; + TypeElement typeElement = (TypeElement) declaredType.asElement(); Name qualifiedName = typeElement.getQualifiedName(); optional = qualifiedName.toString().startsWith(Optional.class.getName()) || qualifiedName.contentEquals(Map.class.getName()); @@ -313,14 +312,7 @@ private List recursivelyFindConfigItems(Element element, String r || typeInString.startsWith(Set.class.getName()) || realTypeMirror.getKind() == TypeKind.ARRAY)) { list = true; - DeclaredType declaredRealType; - if (typeMirror instanceof DeclaredType) { - declaredRealType = (DeclaredType) typeMirror; - } else { - ExecutableType executableType = (ExecutableType) typeMirror; - TypeMirror returnType = executableType.getReturnType(); - declaredRealType = ((DeclaredType) returnType); - } + DeclaredType declaredRealType = (DeclaredType) typeMirror; typeArguments = declaredRealType.getTypeArguments(); if (!typeArguments.isEmpty()) { realTypeMirror = typeArguments.get(0); @@ -378,6 +370,19 @@ private List recursivelyFindConfigItems(Element element, String r return configDocItems; } + private TypeMirror unwrapTypeMirror(TypeMirror typeMirror) { + if (typeMirror instanceof DeclaredType) { + return typeMirror; + } + + if (typeMirror instanceof ExecutableType) { + ExecutableType executableType = (ExecutableType) typeMirror; + return executableType.getReturnType(); + } + + return typeMirror; + } + private boolean isConfigGroup(String type) { if (type.startsWith("java.") || PRIMITIVE_TYPES.contains(type)) { return false; diff --git a/core/processor/src/main/java/io/quarkus/annotation/processor/generate_doc/ConfigDocItemScanner.java b/core/processor/src/main/java/io/quarkus/annotation/processor/generate_doc/ConfigDocItemScanner.java index 50b08aea4ab67..fe79f0a4861a8 100644 --- a/core/processor/src/main/java/io/quarkus/annotation/processor/generate_doc/ConfigDocItemScanner.java +++ b/core/processor/src/main/java/io/quarkus/annotation/processor/generate_doc/ConfigDocItemScanner.java @@ -88,6 +88,8 @@ public void addConfigRoot(final PackageElement pkg, TypeElement clazz) { for (AnnotationMirror mirror : clazz.getAnnotationMirrors()) { if (mirror.getAnnotationType().toString().equals(Constants.ANNOTATION_CONFIG_MAPPING)) { + isMapping = true; + name = Constants.EMPTY; for (Entry entry : mirror.getElementValues() .entrySet()) { if ("prefix()".equals(entry.getKey().toString())) { @@ -145,7 +147,7 @@ public Set scanExtensionsConfigurationItems(Properties * Loads the list of configuration items per configuration root * */ - private Properties loadAllExtensionConfigItemsParConfigRoot() throws IOException { + private Properties loadAllExtensionConfigItemsParConfigRoot() { return allExtensionGeneratedDocs.asProperties(); } @@ -153,9 +155,8 @@ private Properties loadAllExtensionConfigItemsParConfigRoot() throws IOException * Update extensions config roots. We need to gather the complete list of configuration roots of an extension * when generating the documentation. * - * @throws IOException */ - private void updateConfigurationRootsList(Map.Entry> entry) throws IOException { + private void updateConfigurationRootsList(Map.Entry> entry) { String extensionFileName = entry.getKey().getFileName(); String clazz = entry.getKey().getClazz().getQualifiedName().toString(); configurationRootsParExtensionFileName.put(extensionFileName, clazz); diff --git a/core/runtime/src/main/java/io/quarkus/runtime/configuration/ConfigUtils.java b/core/runtime/src/main/java/io/quarkus/runtime/configuration/ConfigUtils.java index 7cf026e96b83e..97271638fc52f 100644 --- a/core/runtime/src/main/java/io/quarkus/runtime/configuration/ConfigUtils.java +++ b/core/runtime/src/main/java/io/quarkus/runtime/configuration/ConfigUtils.java @@ -110,6 +110,8 @@ public static SmallRyeConfigBuilder configBuilder(final boolean runTime, final b } if (runTime || bootstrap) { builder.addDefaultSources(); + // Validator only for runtime. We cannot use the current validator for build time (chicken / egg problem) + builder.addDiscoveredValidator(); builder.withDefaultValue(UUID_KEY, UUID.randomUUID().toString()); builder.withSources(new DotEnvConfigSourceProvider()); builder.withSources( @@ -179,7 +181,6 @@ public OptionalInt getPriority() { builder.addDefaultInterceptors(); builder.addDiscoveredInterceptors(); builder.addDiscoveredConverters(); - builder.addDiscoveredValidator(); return builder; } diff --git a/extensions/hibernate-validator/runtime/src/main/resources/META-INF/services/io.smallrye.config.ConfigValidator b/extensions/hibernate-validator/runtime/src/main/resources/META-INF/services/io.smallrye.config.ConfigValidator index cf5de21457647..5c35a7ee00e44 100644 --- a/extensions/hibernate-validator/runtime/src/main/resources/META-INF/services/io.smallrye.config.ConfigValidator +++ b/extensions/hibernate-validator/runtime/src/main/resources/META-INF/services/io.smallrye.config.ConfigValidator @@ -1,2 +1 @@ -#io.smallrye.config.validator.BeanValidationConfigValidatorImpl io.quarkus.hibernate.validator.runtime.HibernateBeanValidationConfigValidator diff --git a/extensions/resteasy-classic/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/JaxRsSecurityConfig.java b/extensions/resteasy-classic/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/JaxRsSecurityConfig.java index 4264a3c7d4dbf..43e86578d7811 100644 --- a/extensions/resteasy-classic/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/JaxRsSecurityConfig.java +++ b/extensions/resteasy-classic/resteasy/runtime/src/main/java/io/quarkus/resteasy/runtime/JaxRsSecurityConfig.java @@ -24,7 +24,6 @@ public class JaxRsSecurityConfig { * * The role of '**' means any authenticated user, which is equivalent to the {@link io.quarkus.security.Authenticated} * annotation. - * */ @ConfigItem public Optional> defaultRolesAllowed; diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive-common/deployment/src/main/java/io/quarkus/resteasy/reactive/common/deployment/ResteasyReactiveCommonProcessor.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive-common/deployment/src/main/java/io/quarkus/resteasy/reactive/common/deployment/ResteasyReactiveCommonProcessor.java index c4d4de44f6ac4..726497eea0877 100644 --- a/extensions/resteasy-reactive/quarkus-resteasy-reactive-common/deployment/src/main/java/io/quarkus/resteasy/reactive/common/deployment/ResteasyReactiveCommonProcessor.java +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive-common/deployment/src/main/java/io/quarkus/resteasy/reactive/common/deployment/ResteasyReactiveCommonProcessor.java @@ -15,8 +15,6 @@ import javax.ws.rs.Priorities; import javax.ws.rs.ext.RuntimeDelegate; -import org.eclipse.microprofile.config.Config; -import org.eclipse.microprofile.config.ConfigProvider; import org.jboss.jandex.AnnotationTarget; import org.jboss.jandex.ClassInfo; import org.jboss.jandex.CompositeIndex; @@ -45,6 +43,7 @@ import io.quarkus.deployment.builditem.CombinedIndexBuildItem; import io.quarkus.deployment.builditem.nativeimage.ReflectiveClassBuildItem; import io.quarkus.deployment.builditem.nativeimage.ServiceProviderBuildItem; +import io.quarkus.resteasy.reactive.common.runtime.JaxRsSecurityConfig; import io.quarkus.resteasy.reactive.common.runtime.ResteasyReactiveConfig; import io.quarkus.resteasy.reactive.spi.AbstractInterceptorBuildItem; import io.quarkus.resteasy.reactive.spi.AdditionalResourceClassBuildItem; @@ -66,22 +65,15 @@ public class ResteasyReactiveCommonProcessor { private static final int LEGACY_WRITER_PRIORITY = Priorities.USER / 2; // writers are compared by increased priority @BuildStep - void setUpDenyAllJaxRs(CombinedIndexBuildItem index, + void setUpDenyAllJaxRs( + CombinedIndexBuildItem index, ResteasyReactiveConfig rrConfig, + JaxRsSecurityConfig securityConfig, Optional resteasyDeployment, BuildProducer additionalSecuredClasses) { - Config config = ConfigProvider.getConfig(); - - // we do this in order to avoid having 'io.quarkus.resteasy.reactive.common.runtime.JaxRsSecurityConfig' conflict with 'io.quarkus.resteasy.runtime.JaxRsSecurityConfig' - Optional denyUnannotatedEndpointsConfig = config - .getOptionalValue("quarkus.security.jaxrs.deny-unannotated-endpoints", Boolean.class); - Optional> defaultRolesAllowedConfig = config - .getOptionalValues("quarkus.security.jaxrs.default-roles-allowed", String.class); - - if (denyUnannotatedEndpointsConfig.orElse(false) && resteasyDeployment.isPresent()) { - final List classes = new ArrayList<>(); - + if (securityConfig.denyJaxRs() && resteasyDeployment.isPresent()) { + List classes = new ArrayList<>(); Set resourceClasses = resteasyDeployment.get().getResult().getScannedResourcePaths().keySet(); for (DotName className : resourceClasses) { ClassInfo classInfo = index.getIndex().getClassByName(className); @@ -91,9 +83,8 @@ void setUpDenyAllJaxRs(CombinedIndexBuildItem index, } additionalSecuredClasses.produce(new AdditionalSecuredClassesBuildItem(classes)); - } else if (defaultRolesAllowedConfig.isPresent() && resteasyDeployment.isPresent()) { - - final List classes = new ArrayList<>(); + } else if (securityConfig.defaultRolesAllowed().isPresent() && resteasyDeployment.isPresent()) { + List classes = new ArrayList<>(); Set resourceClasses = resteasyDeployment.get().getResult().getScannedResourcePaths().keySet(); for (DotName className : resourceClasses) { ClassInfo classInfo = index.getIndex().getClassByName(className); @@ -102,7 +93,7 @@ void setUpDenyAllJaxRs(CombinedIndexBuildItem index, } } additionalSecuredClasses - .produce(new AdditionalSecuredClassesBuildItem(classes, defaultRolesAllowedConfig)); + .produce(new AdditionalSecuredClassesBuildItem(classes, securityConfig.defaultRolesAllowed())); } } diff --git a/extensions/resteasy-reactive/quarkus-resteasy-reactive-common/runtime/src/main/java/io/quarkus/resteasy/reactive/common/runtime/JaxRsSecurityConfig.java b/extensions/resteasy-reactive/quarkus-resteasy-reactive-common/runtime/src/main/java/io/quarkus/resteasy/reactive/common/runtime/JaxRsSecurityConfig.java new file mode 100644 index 0000000000000..394d53631bfc4 --- /dev/null +++ b/extensions/resteasy-reactive/quarkus-resteasy-reactive-common/runtime/src/main/java/io/quarkus/resteasy/reactive/common/runtime/JaxRsSecurityConfig.java @@ -0,0 +1,30 @@ +package io.quarkus.resteasy.reactive.common.runtime; + +import java.util.List; +import java.util.Optional; + +import io.quarkus.runtime.annotations.ConfigPhase; +import io.quarkus.runtime.annotations.ConfigRoot; +import io.smallrye.config.ConfigMapping; +import io.smallrye.config.WithDefault; +import io.smallrye.config.WithName; + +@ConfigRoot(phase = ConfigPhase.BUILD_AND_RUN_TIME_FIXED) +@ConfigMapping(prefix = "quarkus.security.jaxrs") +public interface JaxRsSecurityConfig { + /** + * if set to true, access to all JAX-RS resources will be denied by default + */ + @WithName("deny-unannotated-endpoints") + @WithDefault("false") + boolean denyJaxRs(); + + /** + * If no security annotations are affecting a method then they will default to requiring these roles, + * (equivalent to adding an @RolesAllowed annotation with the roles to every endpoint class). + * + * The role of '**' means any authenticated user, which is equivalent to the {@link io.quarkus.security.Authenticated} + * annotation. + */ + Optional> defaultRolesAllowed(); +}