Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature 1557/micronaut 3 upgrade #1717

Merged
merged 17 commits into from
Jun 23, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 14 additions & 3 deletions server/build.gradle
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
plugins {
id("com.github.johnrengelman.shadow") version "7.0.0"
id("io.micronaut.application") version "1.5.3"
id("io.micronaut.application") version "2.0.3"
id "idea"
id "jacoco"
id("org.openrewrite.rewrite") version "5.20.0"
}

rewrite {
activeRecipe("org.openrewrite.java.micronaut.Micronaut2to3Migration")
}

version "0.5.7"
Expand Down Expand Up @@ -33,11 +38,11 @@ dependencies {
annotationProcessor("io.micronaut.security:micronaut-security-annotations")
implementation("io.micronaut:micronaut-http-client")
implementation("io.micronaut:micronaut-management")
implementation("io.micronaut:micronaut-runtime")
implementation('io.micronaut:micronaut-runtime:3.4.3')
implementation("io.micronaut.cache:micronaut-cache-ehcache")
implementation("io.micronaut.data:micronaut-data-jdbc")
implementation("io.micronaut.flyway:micronaut-flyway")
implementation("io.micronaut.security:micronaut-security")
implementation("io.micronaut.security:micronaut-security:2.5.0")
implementation("io.micronaut.security:micronaut-security-jwt")
implementation("io.micronaut.security:micronaut-security-oauth2")
implementation("io.micronaut.sql:micronaut-jdbc-hikari")
Expand Down Expand Up @@ -89,6 +94,10 @@ dependencies {
//}
//implementation 'org.postgresql:postgresql:42.2.12'
implementation 'com.google.cloud.sql:postgres-socket-factory:1.0.15'

implementation("io.micronaut.reactor:micronaut-reactor")
implementation('io.projectreactor.addons:reactor-adapter:3.4.8')
testImplementation 'io.projectreactor:reactor-test:3.4.18'
//implementation "io.micronaut.sql:micronaut-jdbc-hikari"
//implementation 'io.micronaut.flyway:micronaut-flyway'
//implementation("io.swagger.core.v3:swagger-annotations")
Expand Down Expand Up @@ -126,6 +135,8 @@ dependencies {
//testImplementation("org.testcontainers:postgresql")

publicResources project(':web-ui')

rewrite('org.openrewrite.recipe:rewrite-micronaut:1.10.0')
}

jacocoTestReport {
Expand Down
2 changes: 1 addition & 1 deletion server/gradle.properties
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
micronautVersion=2.5.7
micronautVersion=3.4.3
#micronautDataVersion=1.0.2
#micronautSecurityVersion=2.0.0
seleniumVersion=3.141.59
Expand Down
3 changes: 2 additions & 1 deletion server/micronaut-cli.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ profile: service
defaultPackage: com.objectcomputing
---
testFramework: junit
sourceLanguage: java
sourceLanguage: java
features: [annotation-api, app-name, gradle, http-client, jackson-databind, java, java-application, junit, logback, micronaut-build, netty-server, openrewrite, readme, shade, yaml]
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
import io.micronaut.http.hateoas.Link;
import io.micronaut.http.server.exceptions.ExceptionHandler;

import javax.inject.Singleton;
import jakarta.inject.Singleton;

@Produces
@Singleton
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import io.micronaut.http.hateoas.Link;
import io.micronaut.http.server.exceptions.ExceptionHandler;

import javax.inject.Singleton;
import jakarta.inject.Singleton;

@Produces
@Singleton
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import io.micronaut.http.hateoas.Link;
import io.micronaut.http.server.exceptions.ExceptionHandler;

import javax.inject.Singleton;
import jakarta.inject.Singleton;

@Produces
@Singleton
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
import io.micronaut.http.hateoas.Link;
import io.micronaut.http.server.exceptions.ExceptionHandler;

import javax.inject.Singleton;
import jakarta.inject.Singleton;

@Produces
@Singleton
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import io.micronaut.context.event.BeanCreatedEvent;
import io.micronaut.context.event.BeanCreatedEventListener;

import javax.inject.Singleton;
import jakarta.inject.Singleton;

@Singleton
@Requires(env = Environment.GOOGLE_COMPUTE)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,13 @@
import io.micronaut.http.filter.HttpServerFilter;
import io.micronaut.http.filter.ServerFilterChain;
import io.micronaut.inject.ExecutableMethod;
import io.micronaut.security.authentication.AuthenticationUserDetailsAdapter;
import io.micronaut.security.authentication.Authentication;
import io.micronaut.web.router.MethodBasedRoute;
import io.reactivex.Flowable;
import org.reactivestreams.Publisher;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.util.Optional;

Expand All @@ -24,7 +25,7 @@ public class RequestLoggingInterceptor implements HttpServerFilter {
public boolean intercept(HttpRequest request) {
String requestVerb = request.getMethodName();
String username = "not authenticated";
Optional<AuthenticationUserDetailsAdapter> auth = request.getAttribute("micronaut.AUTHENTICATION", AuthenticationUserDetailsAdapter.class);
Optional<Authentication> auth = request.getAttribute("micronaut.AUTHENTICATION", Authentication.class);
if (auth.isEmpty()) {
return false; //Seems to fire twice per request. First time without auth, so we just skip that one.
}
Expand All @@ -47,7 +48,7 @@ else if (!auth.get().getName().isBlank()){

@Override
public Publisher<MutableHttpResponse<?>> doFilter(HttpRequest<?> request, ServerFilterChain chain) {
return Flowable.fromCallable(() -> intercept(request))
.switchMap(bool -> chain.proceed(request));
Mono<Boolean> interceptMono = Mono.fromCallable(() -> intercept(request));
return Flux.from(interceptMono).switchMap(bool -> chain.proceed(request));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import io.micronaut.context.annotation.Bean;
import io.micronaut.context.annotation.Factory;

import javax.inject.Named;
import jakarta.inject.Named;

@Factory
public class MailJetConfig {
Expand All @@ -17,7 +17,13 @@ public class MailJetConfig {

@Bean
MailjetClient getClient() {
return new MailjetClient(System.getenv("MJ_APIKEY_PUBLIC"), System.getenv("MJ_APIKEY_PRIVATE"), new ClientOptions("v3.1"));
return new MailjetClient(
ClientOptions
.builder()
.apiKey(System.getenv("MJ_APIKEY_PUBLIC"))
.apiSecretKey(System.getenv("MJ_APIKEY_PRIVATE"))
.build()
);
}

@Bean
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,11 @@
import io.micronaut.security.annotation.Secured;
import io.micronaut.security.rules.SecurityRule;
import io.netty.channel.EventLoopGroup;
import io.reactivex.Single;
import io.reactivex.schedulers.Schedulers;

import javax.inject.Named;
import jakarta.inject.Named;
import reactor.core.publisher.Mono;
import reactor.core.scheduler.Schedulers;

import java.util.concurrent.ExecutorService;

@Controller("/services/email-notifications")
Expand All @@ -31,11 +32,11 @@ public MailJetNotificationController(EmailSender emailSender,
}

@Post()
public Single<? extends HttpResponse<?>> sendEmailReceivesStatus(String subject, String content, String... recipients) {
return Single.fromCallable(() -> emailSender.sendEmailReceivesStatus(subject, content, recipients))
.observeOn(Schedulers.from(eventLoopGroup))
public Mono<? extends HttpResponse<?>> sendEmailReceivesStatus(String subject, String content, String... recipients) {
return Mono.fromCallable(() -> emailSender.sendEmailReceivesStatus(subject, content, recipients))
.publishOn(Schedulers.fromExecutor(eventLoopGroup))
.map(success -> (HttpResponse<?>) HttpResponse.ok())
.subscribeOn(Schedulers.from(ioExecutorService));
.subscribeOn(Schedulers.fromExecutor(ioExecutorService));


}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
import com.mailjet.client.MailjetRequest;
import com.mailjet.client.MailjetResponse;
import com.mailjet.client.errors.MailjetException;
import com.mailjet.client.errors.MailjetSocketTimeoutException;
import com.mailjet.client.resource.Emailv31;
import com.objectcomputing.checkins.exceptions.BadArgException;
import io.micronaut.context.annotation.Property;
Expand Down Expand Up @@ -103,9 +102,6 @@ public void sendEmail(String subject, String content, String... recipients) {
} catch (MailjetException e) {
LOG.error("An unexpected error occurred while sending the upload notification: " + e.getLocalizedMessage(), e);
failedBatches.add(recipientList);
} catch (MailjetSocketTimeoutException e) {
LOG.error("An unexpected timeout occurred while sending the upload notification: " + e.getLocalizedMessage(), e);
failedBatches.add(recipientList);
}
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,17 @@
import com.objectcomputing.checkins.services.role.RoleRepository;
import io.micronaut.core.annotation.NonNull;
import io.micronaut.core.annotation.Nullable;
import io.micronaut.security.authentication.Authentication;
import io.micronaut.security.authentication.AuthenticationResponse;
import io.micronaut.security.authentication.UserDetails;
import io.micronaut.security.oauth2.endpoint.authorization.state.State;
import io.micronaut.security.oauth2.endpoint.token.response.OauthUserDetailsMapper;
import io.micronaut.security.oauth2.endpoint.token.response.OpenIdClaims;
import io.micronaut.security.oauth2.endpoint.token.response.OpenIdTokenResponse;
import io.micronaut.security.oauth2.endpoint.token.response.OpenIdUserDetailsMapper;
import io.micronaut.security.oauth2.endpoint.token.response.*;
import io.micronaut.security.token.config.TokenConfiguration;
import io.micronaut.security.token.jwt.generator.claims.JwtClaims;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.inject.Named;
import javax.inject.Singleton;
import jakarta.inject.Named;
import jakarta.inject.Singleton;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
Expand All @@ -26,57 +23,55 @@

@Named("google")
@Singleton
public class CheckinsOpenIdUserDetailMapper implements OpenIdUserDetailsMapper {
public class CheckinsOpenIdAuthenticationMapper implements OpenIdAuthenticationMapper {

private static final Logger LOG = LoggerFactory.getLogger(CheckinsOpenIdUserDetailMapper.class);
private static final Logger LOG = LoggerFactory.getLogger(CheckinsOpenIdAuthenticationMapper.class);
private final MemberProfileRepository memberProfileRepository;
private final RoleRepository roleRepository;
private final TokenConfiguration tokenConfiguration;

public CheckinsOpenIdUserDetailMapper(MemberProfileRepository memberProfileRepository,
RoleRepository roleRepository,
TokenConfiguration tokenConfiguration) {
public CheckinsOpenIdAuthenticationMapper(MemberProfileRepository memberProfileRepository,
RoleRepository roleRepository,
TokenConfiguration tokenConfiguration) {
LOG.info("Creating an instance of CheckinsOpenIdUserDetailMapper using the constructor");
this.memberProfileRepository = memberProfileRepository;
this.roleRepository = roleRepository;
this.tokenConfiguration = tokenConfiguration;
}

@NonNull
@Override
public UserDetails createUserDetails(String providerName, OpenIdTokenResponse tokenResponse, OpenIdClaims openIdClaims) {
public AuthenticationResponse createAuthentication(String providerName, OpenIdTokenResponse tokenResponse, OpenIdClaims openIdClaims) {
Map<String, Object> claims = buildAttributes(providerName, tokenResponse, openIdClaims);
List<String> roles = getRoles(openIdClaims);
String username = openIdClaims.getSubject();
UserDetails userDetails = new UserDetails(username, roles, claims);
LOG.info("Creating new userdetails for user: {}", userDetails.getUsername());
return userDetails;
LOG.info("Creating new authentication for user: {}", username);
return AuthenticationResponse.success(username, roles, claims);
}

@NonNull
@Override
public AuthenticationResponse createAuthenticationResponse(String providerName, OpenIdTokenResponse tokenResponse, OpenIdClaims openIdClaims, @Nullable State state) {
return createUserDetails(providerName, tokenResponse, openIdClaims);
return createAuthentication(providerName, tokenResponse, openIdClaims);
}

/**
* @param providerName The OpenID provider name
* @param tokenResponse The token response
* @param openIdClaims The OpenID claims
* @return The attributes to set in the {@link UserDetails}
* @return The attributes to set in the {@link Authentication}
*/
protected Map<String, Object> buildAttributes(String providerName, OpenIdTokenResponse tokenResponse, OpenIdClaims openIdClaims) {
Map<String, Object> claims = new HashMap<>(openIdClaims.getClaims());
JwtClaims.ALL_CLAIMS.forEach(claims::remove);
claims.put(OauthUserDetailsMapper.PROVIDER_KEY, providerName);
claims.put(OpenIdUserDetailsMapper.OPENID_TOKEN_KEY, tokenResponse.getIdToken());
claims.put(OauthAuthenticationMapper.PROVIDER_KEY, providerName);
claims.put(OpenIdAuthenticationMapper.OPENID_TOKEN_KEY, tokenResponse.getIdToken());
claims.put(tokenConfiguration.getRolesName(), getRoles(openIdClaims));
return claims;
}

/**
* @param openIdClaims The OpenID claims
* @return The roles to set in the {@link UserDetails}
* @return The roles to set in the {@link Authentication}
*/
protected List<String> getRoles(OpenIdClaims openIdClaims) {
List<String> roles = new ArrayList<>();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
package com.objectcomputing.checkins.security;

import com.objectcomputing.checkins.services.memberprofile.MemberProfile;
import com.objectcomputing.checkins.services.memberprofile.MemberProfileRepository;
import com.objectcomputing.checkins.services.permissions.Permission;
import com.objectcomputing.checkins.services.permissions.PermissionServices;
import com.objectcomputing.checkins.services.refresh_token.RefreshToken;
import com.objectcomputing.checkins.services.refresh_token.RefreshTokenRepository;
import com.objectcomputing.checkins.services.role.Role;
import com.objectcomputing.checkins.services.role.RoleServices;
import io.micronaut.runtime.event.annotation.EventListener;
import io.micronaut.security.authentication.Authentication;
import io.micronaut.security.token.event.RefreshTokenGeneratedEvent;
import io.micronaut.security.token.refresh.RefreshTokenPersistence;
import org.reactivestreams.Publisher;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import jakarta.inject.Singleton;
import reactor.core.publisher.Mono;

import java.util.*;
import java.util.stream.Collectors;

@Singleton
public class CustomRefreshTokenPersistence implements RefreshTokenPersistence {

final Logger LOG = LoggerFactory.getLogger(CustomRefreshTokenPersistence.class);
final MemberProfileRepository memberProfileRepo;
final RefreshTokenRepository refreshTokenRepo;
final PermissionServices permissionServices;
final RoleServices roleServices;


public CustomRefreshTokenPersistence(
MemberProfileRepository memberProfileRepo,
RefreshTokenRepository refreshTokenRepo,
PermissionServices permissionServices,
RoleServices roleServices
)
{
this.memberProfileRepo = memberProfileRepo;
this.refreshTokenRepo = refreshTokenRepo;
this.permissionServices = permissionServices;
this.roleServices = roleServices;
}

@Override
@EventListener
public void persistToken(RefreshTokenGeneratedEvent event) {
LOG.info("in the persist");
refreshTokenRepo.save(new RefreshToken(event.getAuthentication().getName(), event.getRefreshToken()));
}

@Override
public Publisher<Authentication> getAuthentication(String refreshToken) {
Optional<RefreshToken> optToken = refreshTokenRepo.findByRefreshToken(refreshToken);
if (optToken.isEmpty()) {
return null;
} else {
RefreshToken token = optToken.get();
Optional<MemberProfile> optProfile = memberProfileRepo.findByWorkEmail(token.getUserName());
if (optProfile.isEmpty()) {
return null;
} else {
MemberProfile profile = optProfile.get();

Authentication auth = createAuthentication(profile);
return Mono.just(auth);
}
}
}

public Authentication createAuthentication(MemberProfile memberProfile) {
List<Permission> permissions = permissionServices.findUserPermissions(memberProfile.getId());
List<String> permissionsAsString = permissions.stream().map(Permission::getPermission).collect(Collectors.toList());

Set<Role> userRoles = roleServices.findUserRoles(memberProfile.getId());
List<String> rolesAsString = userRoles.stream().map(Role::getRole).collect(Collectors.toList());

Map<String, Object> attributes = new HashMap<>();
attributes.put("permissions", permissionsAsString);
attributes.put("email", memberProfile.getWorkEmail());

return Authentication.build(memberProfile.getWorkEmail(), rolesAsString, attributes);
}
}
Loading