From 9fa550199ec905de9d67c31d21967df86cfa3418 Mon Sep 17 00:00:00 2001 From: Jeremy Grelle Date: Wed, 2 Aug 2023 17:49:56 -0400 Subject: [PATCH 01/15] Fix docker.gradle.ejs invalid config. --- generators/server/templates/gradle/docker.gradle.ejs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/generators/server/templates/gradle/docker.gradle.ejs b/generators/server/templates/gradle/docker.gradle.ejs index 27879902..7e31650f 100644 --- a/generators/server/templates/gradle/docker.gradle.ejs +++ b/generators/server/templates/gradle/docker.gradle.ejs @@ -2,6 +2,7 @@ dockerfile { baseImage = "openjdk:15-alpine" args("-Xmx128m") } + dockerBuild { images = ["<%= baseName %>:$project.version", "<%= baseName %>:latest"] } @@ -17,9 +18,7 @@ dockerfileNative { baseImage = "oracle/graalvm-ce:20.3.0-java11" args("-Xmx64m") } - - - + dockerBuildNative { images = ["<%= baseName %>:$project.version", "<%= baseName %>:latest"] } From 9805a5fd979803a72d86527c389c79a585358106 Mon Sep 17 00:00:00 2001 From: Jeremy Grelle Date: Wed, 2 Aug 2023 17:50:53 -0400 Subject: [PATCH 02/15] Disable environment deduction since they're explicitly specified. --- generators/server/templates/src/main/java/package/App.java.ejs | 1 + 1 file changed, 1 insertion(+) diff --git a/generators/server/templates/src/main/java/package/App.java.ejs b/generators/server/templates/src/main/java/package/App.java.ejs index 4fd209ab..163302a7 100644 --- a/generators/server/templates/src/main/java/package/App.java.ejs +++ b/generators/server/templates/src/main/java/package/App.java.ejs @@ -67,6 +67,7 @@ public class <%= mainClass %> { ApplicationContext context = Micronaut.build(args) .mainClass(<%= mainClass %>.class) + .deduceEnvironment(false) .environments(environments.toArray(new String[0])) .start(); From b2ba80a47421844a1131cda42eb6529440123fa6 Mon Sep 17 00:00:00 2001 From: Jeremy Grelle Date: Wed, 2 Aug 2023 17:57:31 -0400 Subject: [PATCH 03/15] Update Micronaut framework to 3.9.5 and application plugin to 3.7.9. --- generators/constants.cjs | 2 +- generators/server/templates/gradle.properties.ejs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/generators/constants.cjs b/generators/constants.cjs index 339ca700..1028b506 100644 --- a/generators/constants.cjs +++ b/generators/constants.cjs @@ -43,7 +43,7 @@ module.exports = { }, }, versions: { - micronaut: '3.9.1', + micronaut: '3.9.5', micronautData: '3.10.0', micronautOpenApi: '2.3.1', rxJava3: '2.3.0', diff --git a/generators/server/templates/gradle.properties.ejs b/generators/server/templates/gradle.properties.ejs index e8748b6c..7beba09c 100644 --- a/generators/server/templates/gradle.properties.ejs +++ b/generators/server/templates/gradle.properties.ejs @@ -65,7 +65,7 @@ jaxb_runtime_version=2.3.2 <%_ } _%> # gradle plugin version -micronaut_plugin_version=3.5.1 +micronaut_plugin_version=3.7.9 git_properties_plugin_version=2.4.1 <%_ if (!skipClient) { _%> gradle_node_plugin_version=2.2.4 From b6b8e4b43a6f604bcf393db611bb41b1786bfdac Mon Sep 17 00:00:00 2001 From: Jeremy Grelle Date: Wed, 2 Aug 2023 18:09:15 -0400 Subject: [PATCH 04/15] Clean up Micronaut dependencies in build.gradle --- generators/server/templates/build.gradle.ejs | 24 ++++++++------------ 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/generators/server/templates/build.gradle.ejs b/generators/server/templates/build.gradle.ejs index f23a522b..834f8ada 100644 --- a/generators/server/templates/build.gradle.ejs +++ b/generators/server/templates/build.gradle.ejs @@ -272,15 +272,13 @@ dependencies { // import JHipster dependencies BOM if (!project.hasProperty("gae")) { implementation platform("io.github.jhipster:jhipster-dependencies:${jhipster_dependencies_version}") - annotationProcessor platform("io.micronaut:micronaut-bom:${micronautVersion}") - implementation platform("io.micronaut:micronaut-bom:${micronautVersion}") } annotationProcessor "io.micronaut:micronaut-validation" <%_ if (databaseType === 'sql') { _%> - annotationProcessor "io.micronaut.data:micronaut-data-processor:$micronaut_data_version" + annotationProcessor "io.micronaut.data:micronaut-data-processor" <%_ } _%> - annotationProcessor "io.micronaut.openapi:micronaut-openapi:$micronaut_openapi_version" + annotationProcessor "io.micronaut.openapi:micronaut-openapi" // Use ", version: jhipster_dependencies_version, changing: true" if you want // to use a SNAPSHOT release instead of a stable release @@ -387,12 +385,12 @@ dependencies { liquibaseRuntime sourceSets.main.compileClasspath <%_ } _%> - implementation "io.micronaut:micronaut-inject:$micronautVersion" - implementation "io.micronaut:micronaut-validation:$micronautVersion" - implementation "io.micronaut:micronaut-runtime:$micronautVersion" - implementation "io.micronaut:micronaut-http-client:$micronautVersion" - implementation "io.micronaut:micronaut-http-server-netty:$micronautVersion" - implementation "io.micronaut:micronaut-management:$micronautVersion" + implementation "io.micronaut:micronaut-inject" + implementation "io.micronaut:micronaut-validation" + implementation "io.micronaut:micronaut-runtime" + implementation "io.micronaut:micronaut-http-client" + implementation "io.micronaut:micronaut-http-server-netty" + implementation "io.micronaut:micronaut-management" implementation "io.micronaut.security:micronaut-security-jwt" <%_ if (authenticationType == 'oauth2') { _%> implementation "io.micronaut.security:micronaut-security-oauth2" @@ -527,10 +525,8 @@ dependencies { <%_ } _%> // Micronaut test deps - testAnnotationProcessor platform("io.micronaut:micronaut-bom:$micronautVersion") - testAnnotationProcessor "io.micronaut.data:micronaut-data-processor:$micronaut_data_version" + testAnnotationProcessor "io.micronaut.data:micronaut-data-processor" testAnnotationProcessor "org.glassfish.jaxb:jaxb-runtime:$jaxb_runtime_version" - testImplementation platform("io.micronaut:micronaut-bom:$micronautVersion") testImplementation "io.micronaut.test:micronaut-test-junit5" testImplementation "org.junit.jupiter:junit-jupiter-engine" @@ -560,7 +556,7 @@ dependencies { testImplementation "org.testcontainers:kafka" <%_ } _%> - implementation("io.micronaut.rxjava3:micronaut-rxjava3:$rxJava3Version") + implementation("io.micronaut.rxjava3:micronaut-rxjava3") implementation("io.micronaut.rxjava3:micronaut-rxjava3-http-client:$rxJava3Version") //jhipster-needle-gradle-dependency - JHipster will add additional dependencies here From 08b682bbee8816cc1043027dc68233ab88c548e2 Mon Sep 17 00:00:00 2001 From: Jeremy Grelle Date: Wed, 2 Aug 2023 18:22:53 -0400 Subject: [PATCH 05/15] Refactor SecurityHeaderFilter SecurityHeaderFilter is updated to implement HttpServerFilter (as OncePerRequestHttpServerFilter is deprecated), and the implmentation is updated to use Flowable instead of the framework-internal Publishers class. --- .../java/package/security/SecurityHeaderFilter.java.ejs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/generators/server/templates/src/main/java/package/security/SecurityHeaderFilter.java.ejs b/generators/server/templates/src/main/java/package/security/SecurityHeaderFilter.java.ejs index 990037be..ee19e082 100644 --- a/generators/server/templates/src/main/java/package/security/SecurityHeaderFilter.java.ejs +++ b/generators/server/templates/src/main/java/package/security/SecurityHeaderFilter.java.ejs @@ -4,14 +4,14 @@ import io.micronaut.core.async.publisher.Publishers; import io.micronaut.http.HttpRequest; import io.micronaut.http.MutableHttpResponse; import io.micronaut.http.annotation.Filter; -import io.micronaut.http.filter.OncePerRequestHttpServerFilter; +import io.micronaut.http.filter.HttpServerFilter; import io.micronaut.http.filter.ServerFilterChain; import org.reactivestreams.Publisher; import static io.micronaut.http.annotation.Filter.MATCH_ALL_PATTERN; @Filter(patterns = {MATCH_ALL_PATTERN}) -public class SecurityHeaderFilter extends OncePerRequestHttpServerFilter { +public class SecurityHeaderFilter implements HttpServerFilter { private static final String X_FRAME_OPTIONS_HEADER = "X-Frame-Options"; private static final String X_CONTENT_TYPE_OPTIONS_HEADER = "X-Content-Type-Options"; @@ -21,9 +21,8 @@ public class SecurityHeaderFilter extends OncePerRequestHttpServerFilter { private static final String CONTENT_SECURITY_POLICY_HEADER = "Content-Security-Policy"; @Override - protected Publisher> doFilterOnce(HttpRequest request, ServerFilterChain chain) { - - return Publishers.map(chain.proceed(request), mutableHttpResponse -> { + public Publisher> doFilter(HttpRequest request, ServerFilterChain chain) { + return Flowable.fromPublisher(chain.proceed(request)).map(mutableHttpResponse -> { addSecurityHeaders(mutableHttpResponse); return mutableHttpResponse; }); From 7cce526f0a4376c371212a6b5b424f8450f70919 Mon Sep 17 00:00:00 2001 From: Jeremy Grelle Date: Wed, 2 Aug 2023 18:30:54 -0400 Subject: [PATCH 06/15] Refactor DatabaseAuthenticationProvider Updates the AuthenticationProvider implementation to bind the login attempts to the LoginVM class and use an injected instance of the Micronaut Validator class. Corrects creation of the AuthenticationResponse on successful validation to have the specific user's authorities set. --- .../DatabaseAuthenticationProvider.java.ejs | 61 +++++++++++-------- 1 file changed, 36 insertions(+), 25 deletions(-) diff --git a/generators/server/templates/src/main/java/package/security/DatabaseAuthenticationProvider.java.ejs b/generators/server/templates/src/main/java/package/security/DatabaseAuthenticationProvider.java.ejs index 36ec234e..a283668e 100644 --- a/generators/server/templates/src/main/java/package/security/DatabaseAuthenticationProvider.java.ejs +++ b/generators/server/templates/src/main/java/package/security/DatabaseAuthenticationProvider.java.ejs @@ -3,21 +3,22 @@ package <%=packageName%>.security; import <%=packageName%>.domain.Authority; import <%=packageName%>.domain.User; import <%=packageName%>.repository.UserRepository; +import <%=packageName%>.web.rest.vm.LoginVM; import io.micronaut.core.annotation.Nullable; import io.micronaut.http.HttpRequest; import io.micronaut.security.authentication.AuthenticationProvider; import io.micronaut.security.authentication.AuthenticationRequest; import io.micronaut.security.authentication.AuthenticationResponse; -import io.micronaut.validation.validator.constraints.EmailValidator; -import <%=packageName%>.repository.AuthorityRepository; +import io.micronaut.validation.validator.Validator; import io.reactivex.rxjava3.core.Flowable; +import jakarta.inject.Singleton; import org.reactivestreams.Publisher; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import jakarta.inject.Singleton; +import javax.validation.ConstraintViolation; import java.util.List; -import java.util.Locale; +import java.util.Set; import java.util.stream.Collectors; @Singleton @@ -26,39 +27,49 @@ public class DatabaseAuthenticationProvider implements AuthenticationProvider { private final Logger log = LoggerFactory.getLogger(DatabaseAuthenticationProvider.class); private final UserRepository userRepository; private final PasswordEncoder passwordEncoder; - private final AuthorityRepository authorityRepository; + private final Validator validator; - public DatabaseAuthenticationProvider(UserRepository userRepository, PasswordEncoder passwordEncoder, AuthorityRepository authorityRepository) { + public DatabaseAuthenticationProvider( + UserRepository userRepository, + PasswordEncoder passwordEncoder, + Validator validator) { this.userRepository = userRepository; this.passwordEncoder = passwordEncoder; - this.authorityRepository = authorityRepository; + this.validator = validator; } @Override - public Publisher authenticate(@Nullable HttpRequest httpRequest, AuthenticationRequest authenticationRequest) { - String username = authenticationRequest.getIdentity().toString(); + public Publisher authenticate( + @Nullable HttpRequest httpRequest, + AuthenticationRequest authenticationRequest + ) { - log.debug("Authenticating {}", username); + LoginVM loginAttempt = new LoginVM(); + loginAttempt.setUsername(authenticationRequest.getIdentity().toString()); + loginAttempt.setPassword(authenticationRequest.getSecret().toString()); - if (new EmailValidator().isValid(username, null)) { - return Flowable.just(userRepository.findOneByEmail(username) - .filter(user -> passwordEncoder.matches(authenticationRequest.getSecret().toString(), user.getPassword())) - .map(user -> createMicronautSecurityUser(username, user)) - .orElse(new NotAuthenticatedResponse("Invalid username or password"))); - } + log.debug("Authenticating {}", loginAttempt.getUsername()); - String lowercaseLogin = username.toLowerCase(Locale.ENGLISH); - return Flowable.just(userRepository.findOneByLogin(lowercaseLogin) - .filter(user -> passwordEncoder.matches(authenticationRequest.getSecret().toString(), user.getPassword())) - .map(user -> createMicronautSecurityUser(lowercaseLogin, user)) - .orElse(new NotAuthenticatedResponse("Invalid username or password"))); + Set> violations = validator.validate(loginAttempt); + if (violations.size() == 0) { + return Flowable.just( + userRepository + .findOneByLogin(loginAttempt.getUsername()) + .filter(user -> passwordEncoder.matches(loginAttempt.getPassword(), user.getPassword())) + .map(this::createMicronautSecurityUser) + .orElse(new NotAuthenticatedResponse("Invalid username or password")) + ); + } else { + log.debug("User validation failed with violations {}", violations); + return Flowable.just(new NotAuthenticatedResponse("Invalid username or password")); + } } - private AuthenticationResponse createMicronautSecurityUser(String lowercaseLogin, User user) { + private AuthenticationResponse createMicronautSecurityUser(User user) { if (!user.getActivated()) { - return new NotAuthenticatedResponse("User " + lowercaseLogin + " was not activated"); + return new NotAuthenticatedResponse("User " + user.getLogin() + " was not activated"); } - List grantedAuthorities = authorityRepository.findAll().stream().map(Authority::getName).collect(Collectors.toList()); + List grantedAuthorities = user.getAuthorities().stream().map(Authority::getName).collect(Collectors.toList()); return AuthenticationResponse.success(user.getLogin(), grantedAuthorities); } -} +} \ No newline at end of file From b04f02d4b13ed91ae175322fb8d966c9312e4856 Mon Sep 17 00:00:00 2001 From: Jeremy Grelle Date: Thu, 3 Aug 2023 07:29:54 -0400 Subject: [PATCH 07/15] Fix CORS test generation. --- .../package/config/CorsController.java.ejs | 3 +- .../java/package/config/CorsTest.java.ejs | 58 ++++++++++--------- 2 files changed, 32 insertions(+), 29 deletions(-) diff --git a/generators/server/templates/src/test/java/package/config/CorsController.java.ejs b/generators/server/templates/src/test/java/package/config/CorsController.java.ejs index 67b1a3a5..26863e2f 100644 --- a/generators/server/templates/src/test/java/package/config/CorsController.java.ejs +++ b/generators/server/templates/src/test/java/package/config/CorsController.java.ejs @@ -2,6 +2,7 @@ package <%=packageName%>.config; import io.micronaut.http.annotation.Controller; import io.micronaut.http.annotation.Get; +import io.micronaut.http.annotation.Post; import io.micronaut.security.annotation.Secured; import io.micronaut.security.rules.SecurityRule; @@ -9,7 +10,7 @@ import io.micronaut.security.rules.SecurityRule; @Secured(SecurityRule.IS_ANONYMOUS) public class CorsController { - @Get("/api/test-cors") + @Post("/api/test-cors") public void testCorsOnApiPath() { } diff --git a/generators/server/templates/src/test/java/package/config/CorsTest.java.ejs b/generators/server/templates/src/test/java/package/config/CorsTest.java.ejs index a14c2fb4..94c30f6b 100644 --- a/generators/server/templates/src/test/java/package/config/CorsTest.java.ejs +++ b/generators/server/templates/src/test/java/package/config/CorsTest.java.ejs @@ -3,14 +3,18 @@ package <%=packageName%>.config; <%_ if (cacheProvider === 'redis') { _%> import <%= packageName %>.RedisTestContainerExtension; <%_ } _%> +import io.micronaut.context.annotation.Property; +import io.micronaut.core.util.CollectionUtils; import io.micronaut.http.HttpHeaders; import io.micronaut.http.HttpRequest; import io.micronaut.http.HttpResponse; import io.micronaut.http.client.annotation.Client; import io.micronaut.rxjava3.http.client.Rx3HttpClient; import io.micronaut.test.extensions.junit5.annotation.MicronautTest; +import io.micronaut.test.support.TestPropertyProvider; import jakarta.inject.Inject; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; <%_ if (cacheProvider === 'redis') { _%> import org.junit.jupiter.api.extension.ExtendWith; @@ -24,27 +28,16 @@ import static org.assertj.core.api.Assertions.assertThat; @ExtendWith(RedisTestContainerExtension.class) <%_ } _%> @MicronautTest +@TestInstance(TestInstance.Lifecycle.PER_CLASS) public class CorsTest { @Inject @Client("/") Rx3HttpClient client; + @Property(name="micronaut.server.cors.enabled", value="true") @Test public void testCorsFilterOnApiPath() { - Map props = new LinkedHashMap<>(); -<%_ if (serviceDiscoveryType === 'consul') { _%> - props.put("consul.client.registration.enabled", false); - props.put("consul.client.config.enabled", false); -<%_ } else if (serviceDiscoveryType === 'eureka') { _%> - props.put("eureka.client.registration.enabled", false); - props.put("spring.cloud.config.enabled", false); -<%_ } _%> - props.put("micronaut.server.cors.enabled", true); - props.put("micronaut.server.cors.single-header", true); - props.put("micronaut.server.cors.configurations.default.allowed-methods", Arrays.asList("GET", "POST", "PUT", "DELETE")); - props.put("micronaut.server.cors.configurations.default.max-age", 1800L); - props.put("micronaut.server.cors.configurations.default.allow-credentials", true); HttpResponse response = client.exchange( HttpRequest.OPTIONS("/api/test-cors") @@ -62,28 +55,37 @@ public class CorsTest { @Test public void testCorsFilterDeactivated() throws Exception { - Map props = new LinkedHashMap<>(); -<%_ if (serviceDiscoveryType === 'consul') { _%> - props.put("consul.client.registration.enabled", false); - props.put("consul.client.config.enabled", false); -<%_ } else if (serviceDiscoveryType === 'eureka') { _%> - props.put("eureka.client.registration.enabled", false); - props.put("spring.cloud.config.enabled", false); -<%_ } _%> - props.put("micronaut.server.cors.enabled", false); - props.put("micronaut.server.cors.single-header", true); - props.put("micronaut.server.cors.configurations.default.allowed-methods", Arrays.asList("GET", "POST", "PUT", "DELETE")); - props.put("micronaut.server.cors.configurations.default.max-age", 1800L); - props.put("micronaut.server.cors.configurations.default.allow-credentials", true); HttpResponse response = client.exchange( - HttpRequest.GET("/api/test-cors") + HttpRequest.GET("/test/test-cors") .header(HttpHeaders.ORIGIN, "other.domain.com") - .header(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, "POST")).blockingFirst(); + .header(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, "GET")).blockingFirst(); assertThat(response.getStatus().getCode()).isEqualTo(200); assertThat(response.getHeaders().contains(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN)).isFalse(); } + @Override + public Map getProperties() { + return CollectionUtils.mapOf( +<%# TODO - Set these properties with values appropriate for Micronaut if needed %> +<%_ if (serviceDiscoveryType === 'consul') { _%> + "consul.client.registration.enabled", "false", + "consul.client.config.enabled", "false", +<%_ } else if (serviceDiscoveryType === 'eureka') { _%> + "eureka.client.registration.enabled", "false", + "spring.cloud.config.enabled", "false", +<%_ } _%> + "micronaut.server.cors.single-header", "true", + "micronaut.server.cors.localhost-pass-through", "true", + "micronaut.server.cors.configurations.default.allowed-methods[0]", "GET", + "micronaut.server.cors.configurations.default.allowed-methods[1]", "POST", + "micronaut.server.cors.configurations.default.allowed-methods[2]", "PUT", + "micronaut.server.cors.configurations.default.allowed-methods[3]", "DELETE", + "micronaut.server.cors.configurations.default.max-age", "1800L", + "micronaut.server.cors.configurations.default.allow-credentials", "true" + ); + } + } From 5a70b74ec4d86704695464d5b2f19054718660b1 Mon Sep 17 00:00:00 2001 From: Jeremy Grelle Date: Thu, 3 Aug 2023 07:33:39 -0400 Subject: [PATCH 08/15] Fix MailServiceIT test generation. --- .../src/test/java/package/service/MailServiceIT.java.ejs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/generators/server/templates/src/test/java/package/service/MailServiceIT.java.ejs b/generators/server/templates/src/test/java/package/service/MailServiceIT.java.ejs index 17a71374..b4ecc123 100644 --- a/generators/server/templates/src/test/java/package/service/MailServiceIT.java.ejs +++ b/generators/server/templates/src/test/java/package/service/MailServiceIT.java.ejs @@ -31,6 +31,7 @@ import java.net.URI; import java.net.URL; import java.nio.charset.Charset; import java.util.Properties; +import java.util.concurrent.CompletableFuture; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -70,7 +71,7 @@ public class MailServiceIT { @BeforeEach public void setup() { MockitoAnnotations.openMocks(this); - doNothing().when(mailer).sendMail(any(Email.class)); + when(mailer.sendMail(any(Email.class))).thenReturn(new CompletableFuture<>()); mailService = new MailService(jHipsterProperties, mailer, messageSource, templateEngine); } From 081d6b7e00455b630043e675622f61dd8ad566e8 Mon Sep 17 00:00:00 2001 From: Jeremy Grelle Date: Thu, 3 Aug 2023 07:39:37 -0400 Subject: [PATCH 09/15] Fix auth provider to meet test expectations. --- .../main/java/package/repository/UserRepository.java.ejs | 6 ++++++ .../security/DatabaseAuthenticationProvider.java.ejs | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/generators/server/templates/src/main/java/package/repository/UserRepository.java.ejs b/generators/server/templates/src/main/java/package/repository/UserRepository.java.ejs index 4620398c..4f77e0d1 100644 --- a/generators/server/templates/src/main/java/package/repository/UserRepository.java.ejs +++ b/generators/server/templates/src/main/java/package/repository/UserRepository.java.ejs @@ -55,6 +55,12 @@ public interface UserRepository extends JpaRepository public Optional findOneByEmail(String email); + @EntityGraph(attributePaths = "authorities") +<%_ if (usesCache) { _%> + @Cacheable(cacheNames = "usersByLoginOrEmail") +<%_ } _%> + public Optional findOneByLoginIgnoreCaseOrEmail(String login, String email); + public Page findAllByLoginNot(String login, Pageable pageable); public void update(@Id <% if (authenticationType !== 'oauth2') { %>Long<% } else { %>String<% } %> id, Instant createdDate); diff --git a/generators/server/templates/src/main/java/package/security/DatabaseAuthenticationProvider.java.ejs b/generators/server/templates/src/main/java/package/security/DatabaseAuthenticationProvider.java.ejs index a283668e..142a52ff 100644 --- a/generators/server/templates/src/main/java/package/security/DatabaseAuthenticationProvider.java.ejs +++ b/generators/server/templates/src/main/java/package/security/DatabaseAuthenticationProvider.java.ejs @@ -54,7 +54,7 @@ public class DatabaseAuthenticationProvider implements AuthenticationProvider { if (violations.size() == 0) { return Flowable.just( userRepository - .findOneByLogin(loginAttempt.getUsername()) + .findOneByLoginIgnoreCaseOrEmail(loginAttempt.getUsername(), loginAttempt.getUsername()) .filter(user -> passwordEncoder.matches(loginAttempt.getPassword(), user.getPassword())) .map(this::createMicronautSecurityUser) .orElse(new NotAuthenticatedResponse("Invalid username or password")) From 400a55761df7e5211394e76611c8e66dabd64839 Mon Sep 17 00:00:00 2001 From: Jeremy Grelle Date: Thu, 3 Aug 2023 07:44:16 -0400 Subject: [PATCH 10/15] Missing import in SecurityHeaderFilter. --- .../src/main/java/package/security/SecurityHeaderFilter.java.ejs | 1 + 1 file changed, 1 insertion(+) diff --git a/generators/server/templates/src/main/java/package/security/SecurityHeaderFilter.java.ejs b/generators/server/templates/src/main/java/package/security/SecurityHeaderFilter.java.ejs index ee19e082..b3750195 100644 --- a/generators/server/templates/src/main/java/package/security/SecurityHeaderFilter.java.ejs +++ b/generators/server/templates/src/main/java/package/security/SecurityHeaderFilter.java.ejs @@ -6,6 +6,7 @@ import io.micronaut.http.MutableHttpResponse; import io.micronaut.http.annotation.Filter; import io.micronaut.http.filter.HttpServerFilter; import io.micronaut.http.filter.ServerFilterChain; +import io.reactivex.rxjava3.core.Flowable; import org.reactivestreams.Publisher; import static io.micronaut.http.annotation.Filter.MATCH_ALL_PATTERN; From 72fc795f13a76bab0c76aacaa6a1caa09c44981f Mon Sep 17 00:00:00 2001 From: Jeremy Grelle Date: Thu, 3 Aug 2023 07:50:59 -0400 Subject: [PATCH 11/15] Add missing implements clause to CorsTest. --- .../templates/src/test/java/package/config/CorsTest.java.ejs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/generators/server/templates/src/test/java/package/config/CorsTest.java.ejs b/generators/server/templates/src/test/java/package/config/CorsTest.java.ejs index 94c30f6b..98fa6a46 100644 --- a/generators/server/templates/src/test/java/package/config/CorsTest.java.ejs +++ b/generators/server/templates/src/test/java/package/config/CorsTest.java.ejs @@ -29,7 +29,7 @@ import static org.assertj.core.api.Assertions.assertThat; <%_ } _%> @MicronautTest @TestInstance(TestInstance.Lifecycle.PER_CLASS) -public class CorsTest { +public class CorsTest implements TestPropertyProvider { @Inject @Client("/") From ffeeb612dab0247bf27a1a5b43f56f5bd18b4778 Mon Sep 17 00:00:00 2001 From: Jeremy Grelle Date: Thu, 3 Aug 2023 08:46:10 -0400 Subject: [PATCH 12/15] Clean up user caching. --- .../java/package/config/CacheConfiguration.java.ejs | 11 +---------- .../java/package/repository/UserRepository.java.ejs | 12 +++++------- .../main/java/package/service/UserService.java.ejs | 9 +++++---- .../java/package/web/rest/UserResourceIT.java.ejs | 9 ++++----- 4 files changed, 15 insertions(+), 26 deletions(-) diff --git a/generators/server/templates/src/main/java/package/config/CacheConfiguration.java.ejs b/generators/server/templates/src/main/java/package/config/CacheConfiguration.java.ejs index 33d1991f..450b0edc 100644 --- a/generators/server/templates/src/main/java/package/config/CacheConfiguration.java.ejs +++ b/generators/server/templates/src/main/java/package/config/CacheConfiguration.java.ejs @@ -1,7 +1,5 @@ package <%=packageName%>.config; -import java.net.URI; -import java.net.URISyntaxException; import java.util.Properties; import <%=packageName%>.util.JHipsterProperties; @@ -13,18 +11,12 @@ import java.time.Duration; import org.ehcache.config.builders.*; import org.ehcache.jsr107.Eh107Configuration; import org.ehcache.jsr107.EhcacheCachingProvider; - <%_ if (enableHibernateCache) { _%> -import org.hibernate.cache.jcache.ConfigSettings; - <%_ } _%> <%_ } _%> <%_ if (cacheProvider === 'caffeine') { _%> import com.github.benmanes.caffeine.jcache.spi.CaffeineCachingProvider; import com.github.benmanes.caffeine.jcache.configuration.CaffeineConfiguration; import java.util.OptionalLong; import java.util.concurrent.TimeUnit; - <%_ if (enableHibernateCache) { _%> -import org.hibernate.cache.jcache.ConfigSettings; - <%_ } _%> <%_ } _%> <%_ if (cacheProvider === 'redis') { _%> import org.redisson.Redisson; @@ -121,8 +113,7 @@ public class CacheConfiguration { private void customizeCacheManager(CacheManager cm) { <%_ if (!skipUserManagement || (authenticationType === 'oauth2' && databaseType !== 'no')) { _%> - createCache(cm, <%=packageName%>.repository.UserRepository.USERS_BY_LOGIN_CACHE); - createCache(cm, <%=packageName%>.repository.UserRepository.USERS_BY_EMAIL_CACHE); + createCache(cm, <%=packageName%>.repository.UserRepository.USERS_CACHE); <%_ if (enableHibernateCache) { _%> createCache(cm, <%=packageName%>.domain.User.class.getName()); createCache(cm, <%=packageName%>.domain.Authority.class.getName()); diff --git a/generators/server/templates/src/main/java/package/repository/UserRepository.java.ejs b/generators/server/templates/src/main/java/package/repository/UserRepository.java.ejs index 4f77e0d1..284b6d69 100644 --- a/generators/server/templates/src/main/java/package/repository/UserRepository.java.ejs +++ b/generators/server/templates/src/main/java/package/repository/UserRepository.java.ejs @@ -25,9 +25,7 @@ import java.time.Instant; public interface UserRepository extends JpaRepositoryLong<% } else { %>String<% } %>> { <%_ if (usesCache) { _%> - public static String USERS_BY_LOGIN_CACHE = "usersByLogin"; - - public static String USERS_BY_EMAIL_CACHE = "usersByEmail"; + public static String USERS_CACHE = "usersByLogin"; <%_ } _%> <% if (authenticationType !== 'oauth2') { %> @@ -45,23 +43,23 @@ public interface UserRepository extends JpaRepository - @Cacheable(cacheNames = "usersByLogin") + @Cacheable(cacheNames = USERS_CACHE) <%_ } _%> public Optional findOneByLogin(String login); @EntityGraph(attributePaths = "authorities") <%_ if (usesCache) { _%> - @Cacheable(cacheNames = "usersByEmail") + @Cacheable(cacheNames = USERS_CACHE) <%_ } _%> public Optional findOneByEmail(String email); @EntityGraph(attributePaths = "authorities") <%_ if (usesCache) { _%> - @Cacheable(cacheNames = "usersByLoginOrEmail") + @Cacheable(cacheNames = USERS_CACHE) <%_ } _%> public Optional findOneByLoginIgnoreCaseOrEmail(String login, String email); - public Page findAllByLoginNot(String login, Pageable pageable); + public Page findByLoginNotEqual(String login, Pageable pageable); public void update(@Id <% if (authenticationType !== 'oauth2') { %>Long<% } else { %>String<% } %> id, Instant createdDate); } diff --git a/generators/server/templates/src/main/java/package/service/UserService.java.ejs b/generators/server/templates/src/main/java/package/service/UserService.java.ejs index 6b603035..6dae6f50 100644 --- a/generators/server/templates/src/main/java/package/service/UserService.java.ejs +++ b/generators/server/templates/src/main/java/package/service/UserService.java.ejs @@ -18,6 +18,7 @@ import <%=packageName%>.security.PasswordEncoder; <%_ } _%> <%_ if (usesCache) { _%> import io.micronaut.cache.CacheManager; +import io.micronaut.cache.interceptor.ParametersKey; <%_ } _%> import io.micronaut.data.model.Page; import io.micronaut.data.model.Pageable; @@ -320,7 +321,7 @@ public class UserService { @ReadOnly public Page getAllManagedUsers(Pageable pageable) { - Page userPage = userRepository.findAllByLoginNot(Constants.ANONYMOUS_USER, pageable); + Page userPage = userRepository.findByLoginNotEqual(Constants.ANONYMOUS_USER, pageable); return Page.of(userPage.getContent().stream().map(UserDTO::new).collect(Collectors.toList()), pageable, userPage.getTotalSize()); } @@ -370,9 +371,9 @@ public class UserService { <%_ if (usesCache) { _%> private void clearUserCaches(User user) { - Objects.requireNonNull( - cacheManager.getCache(UserRepository.USERS_BY_LOGIN_CACHE)).invalidate(user.getLogin()); - Objects.requireNonNull(cacheManager.getCache(UserRepository.USERS_BY_EMAIL_CACHE)).invalidate(user.getEmail()); + Objects.requireNonNull(cacheManager.getCache(UserRepository.USERS_CACHE)).invalidate(user.getLogin()); + Objects.requireNonNull(cacheManager.getCache(UserRepository.USERS_CACHE)).invalidate(user.getEmail()); + Objects.requireNonNull(cacheManager.getCache(UserRepository.USERS_CACHE)).invalidate(new ParametersKey(user.getLogin(), user.getEmail())); } <%_ } _%> diff --git a/generators/server/templates/src/test/java/package/web/rest/UserResourceIT.java.ejs b/generators/server/templates/src/test/java/package/web/rest/UserResourceIT.java.ejs index aebf38d5..ecf44745 100644 --- a/generators/server/templates/src/test/java/package/web/rest/UserResourceIT.java.ejs +++ b/generators/server/templates/src/test/java/package/web/rest/UserResourceIT.java.ejs @@ -173,8 +173,7 @@ public class UserResourceIT { @BeforeEach public void setup() { - cacheManager.getCache(UserRepository.USERS_BY_LOGIN_CACHE).invalidateAll(); - cacheManager.getCache(UserRepository.USERS_BY_EMAIL_CACHE).invalidateAll(); + cacheManager.getCache(UserRepository.USERS_CACHE).invalidateAll(); } <%_ } _%> @@ -314,7 +313,7 @@ public class UserResourceIT { @Test public void getUser() throws Exception { <%_ if (usesCache) { _%> - assertThat(cacheManager.getCache(UserRepository.USERS_BY_LOGIN_CACHE).get(user.getLogin(), User.class)).isNotPresent(); + assertThat(cacheManager.getCache(UserRepository.USERS_CACHE).get(user.getLogin(), User.class)).isNotPresent(); <%_ } _%> // Get the user @@ -328,7 +327,7 @@ public class UserResourceIT { assertThat(u.getLangKey()).isEqualTo(DEFAULT_LANGKEY); <%_ if (usesCache) { _%> - assertThat(cacheManager.getCache(UserRepository.USERS_BY_LOGIN_CACHE).get(user.getLogin(), User.class)).isPresent(); + assertThat(cacheManager.getCache(UserRepository.USERS_CACHE).get(user.getLogin(), User.class)).isPresent(); <%_ } _%> } @@ -504,7 +503,7 @@ public class UserResourceIT { <%_ if (usesCache) { _%> assertThat( - cacheManager.getCache(UserRepository.USERS_BY_LOGIN_CACHE).get(user.getLogin(), User.class)).isEmpty(); + cacheManager.getCache(UserRepository.USERS_CACHE).get(user.getLogin(), User.class)).isEmpty(); <%_ } _%> // Validate the database is empty From 640ae5eb12270bf5062985053237713c2505bd50 Mon Sep 17 00:00:00 2001 From: Jeremy Grelle Date: Fri, 4 Aug 2023 10:51:19 -0400 Subject: [PATCH 13/15] Add instructions for using dev version. --- README.md | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/README.md b/README.md index 29aa502c..03fe527f 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,51 @@ > This project is a [Micronaut](https://micronaut.io) blueprint for [JHipster](https://jhipster.tech). > While we are working to create a complete experience, there are likely some gaps. > Please [let us know](https://github.com/jhipster/generator-jhipster-micronaut/issues) if you encounter issues. +> +## Installing and Using the Active Development Version + +This currently active development branch is based on the latest production release of JHipster - v7.9.3 + +As a pre-requisite, you must have NodeJS v16.x installed, along with the bundled version of NPM. + +1. Start by installing JHipster v7.9.3 with + +``` +npm install -g generator-jhipster +``` + +2. Then install this in-development blueprint by +``` +git clone https://github.com/jeremyg484/generator-jhipster-micronaut.git +cd generator-jhipster-micronaut +git checkout micronaut-3 +npm link generator-jhipster +npm install +npm link +``` + +3. Next you can create a new application with the development version of this blueprint by executing: +``` +mkdir my-project +cd my-project +npm link "generator-jhipster-micronaut" +mhipster --skip-jhipster-dependencies +``` + +This will execute the mhipster CLI tool that walks you through a series of steps to generate the code for your application. + +Alternatively, you can generate an application based on one of the samples in https://github.com/jhipster/jdl-samples + +For example, to generate a default gradle-based application, execute: + +``` +mkdir my-jdl-project +cd my-jdl-project +npm link "generator-jhipster-micronaut" +mhipster jdl default-gradle --skip-jhipster-dependencies +``` + +You will re-execute the commands in step 3 for any new application that you would like to generate with this in-development version. # Greetings, Micronaut Hipster! From dd3d2eb03a10a09d1ee0d5ce1f162f24d6e365ce Mon Sep 17 00:00:00 2001 From: Jeremy Grelle Date: Mon, 7 Aug 2023 15:53:02 -0400 Subject: [PATCH 14/15] Generate working Maven configuration. --- generators/constants.cjs | 5 ++- generators/server/templates/pom.xml.ejs | 50 ++++++++++++++++--------- 2 files changed, 35 insertions(+), 20 deletions(-) diff --git a/generators/constants.cjs b/generators/constants.cjs index 1028b506..c2826cce 100644 --- a/generators/constants.cjs +++ b/generators/constants.cjs @@ -45,9 +45,10 @@ module.exports = { versions: { micronaut: '3.9.5', micronautData: '3.10.0', - micronautOpenApi: '2.3.1', + micronautOpenApi: '4.8.7', rxJava3: '2.3.0', - hibernate: '5.5.9.Final', + hibernate: '5.6.15.Final', + prometheusSimpleclient: '0.16.0', jackson: '2.13.3', javassist: '3.27.0-GA', // Should match Hibernate deps javaxMail: '2.0.1', diff --git a/generators/server/templates/pom.xml.ejs b/generators/server/templates/pom.xml.ejs index 02723b35..49c52d5a 100644 --- a/generators/server/templates/pom.xml.ejs +++ b/generators/server/templates/pom.xml.ejs @@ -12,6 +12,12 @@ _%> jar <%= humanizedBaseName %> + + io.micronaut + micronaut-parent + <%= versions.micronaut %> + + <%_ if (JHIPSTER_DEPENDENCIES_VERSION.endsWith('-SNAPSHOT')) { _%> @@ -34,8 +40,10 @@ _%> <%=packageName%>.<%=mainClass%> - 3.3.9 + netty <%= JAVA_VERSION %> + <%= JAVA_VERSION %> + <%= JAVA_VERSION %> <%_ if (!skipClient) { _%> v<%= NODE_VERSION %> <%_ if (clientPackageManager === 'npm') { _%> @@ -63,6 +71,7 @@ _%> <%= versions.micronaut %> <%= versions.micronautData %> <%= versions.micronautOpenApi %> + <%= versions.prometheusSimpleclient %> <%= versions.hibernate %> @@ -118,12 +127,12 @@ _%> - - com.fasterxml.jackson - jackson-bom - ${jackson.version} - import + + io.micronaut + micronaut-bom + ${micronaut.version} pom + import io.github.jhipster @@ -133,13 +142,6 @@ _%> import - - io.micronaut - micronaut-bom - ${micronaut.version} - pom - import - @@ -485,11 +487,6 @@ if (devDatabaseType === 'postgresql' || prodDatabaseType === 'postgresql') { _%> compile <%_ } _%> - - jakarta.mail - jakarta.mail-api - ${javax-mail.version} - io.micronaut.test micronaut-test-junit5 @@ -521,10 +518,20 @@ if (devDatabaseType === 'postgresql' || prodDatabaseType === 'postgresql') { _%> jakarta.mail ${javax-mail.version} + + com.sun.activation + jakarta.activation + ${javax-mail.version} + io.micrometer micrometer-registry-prometheus + + io.prometheus + simpleclient + ${prometheus-simpleclient.version} + io.dropwizard.metrics metrics-core @@ -586,6 +593,8 @@ if (devDatabaseType === 'postgresql' || prodDatabaseType === 'postgresql') { _%> ${java.version} -parameters + -Amicronaut.processing.group=<%= packageName %> + -Amicronaut.processing.module=<%= dasherizedBaseName %> @@ -599,6 +608,11 @@ if (devDatabaseType === 'postgresql' || prodDatabaseType === 'postgresql') { _%> hibernate-jpamodelgen ${hibernate.version} + + io.micronaut + micronaut-inject-java + ${micronaut.version} + io.micronaut micronaut-validation From 146e12831ba40ee426ad586c0e621678b84ab399 Mon Sep 17 00:00:00 2001 From: Jeremy Grelle Date: Mon, 7 Aug 2023 18:51:14 -0400 Subject: [PATCH 15/15] Correct JSON property name to use kebab case as expected by client. --- .../main/java/package/config/ActiveProfilesInfoSource.java.ejs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/generators/server/templates/src/main/java/package/config/ActiveProfilesInfoSource.java.ejs b/generators/server/templates/src/main/java/package/config/ActiveProfilesInfoSource.java.ejs index f31ad0c0..ba9c661f 100644 --- a/generators/server/templates/src/main/java/package/config/ActiveProfilesInfoSource.java.ejs +++ b/generators/server/templates/src/main/java/package/config/ActiveProfilesInfoSource.java.ejs @@ -21,7 +21,7 @@ public class ActiveProfilesInfoSource implements InfoSource { @Override public Publisher getSource() { HashMap map = new HashMap<>(1); - map.put("activeProfiles", environment.getActiveNames()); + map.put("active-profiles", environment.getActiveNames()); return Publishers.just(PropertySource.of("active-profiles", map)); } }