Skip to content

Commit 25c6a66

Browse files
committed
Merge branch 'develop' into feature-2486/create-new-pipeline-for-updated-env
2 parents 7e09456 + b5b9943 commit 25c6a66

File tree

14 files changed

+226
-47
lines changed

14 files changed

+226
-47
lines changed

server/build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ plugins {
77
id "jacoco"
88
}
99

10-
version "0.8.16"
10+
version "0.8.17"
1111
group "com.objectcomputing.checkins"
1212

1313
repositories {
Lines changed: 60 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
package com.objectcomputing.checkins.security;
22

3+
import com.nimbusds.jwt.JWT;
4+
import com.nimbusds.jwt.JWTClaimsSet;
5+
import com.nimbusds.jwt.JWTParser;
36
import com.objectcomputing.checkins.Environments;
47
import com.objectcomputing.checkins.services.memberprofile.MemberProfile;
58
import com.objectcomputing.checkins.services.memberprofile.MemberProfileServices;
@@ -14,11 +17,7 @@
1417
import io.micronaut.http.HttpResponse;
1518
import io.micronaut.http.MediaType;
1619
import io.micronaut.http.MutableHttpResponse;
17-
import io.micronaut.http.annotation.Consumes;
18-
import io.micronaut.http.annotation.Controller;
19-
import io.micronaut.http.annotation.Get;
20-
import io.micronaut.http.annotation.Post;
21-
import io.micronaut.http.annotation.Produces;
20+
import io.micronaut.http.annotation.*;
2221
import io.micronaut.http.cookie.Cookie;
2322
import io.micronaut.http.cookie.SameSite;
2423
import io.micronaut.http.netty.cookies.NettyCookie;
@@ -29,16 +28,13 @@
2928
import io.micronaut.security.event.LoginSuccessfulEvent;
3029
import io.micronaut.security.handlers.LoginHandler;
3130
import io.micronaut.security.rules.SecurityRule;
31+
import io.micronaut.security.token.jwt.generator.JwtTokenGenerator;
3232
import org.slf4j.Logger;
3333
import org.slf4j.LoggerFactory;
3434

3535
import java.net.URI;
36-
import java.util.HashMap;
37-
import java.util.HashSet;
38-
import java.util.Iterator;
39-
import java.util.Locale;
40-
import java.util.Map;
41-
import java.util.Set;
36+
import java.text.ParseException;
37+
import java.util.*;
4238
import java.util.stream.Collectors;
4339

4440
@Requires(env = {Environments.LOCAL, Environment.DEVELOPMENT})
@@ -54,66 +50,90 @@ public class ImpersonationController {
5450
private final MemberProfileServices memberProfileServices;
5551
private final RoleServices roleServices;
5652
private final RolePermissionServices rolePermissionServices;
53+
private final JwtTokenGenerator generator;
5754

5855
/**
59-
* @param loginHandler A collaborator which helps to build HTTP response depending on success or failure.
60-
* @param eventPublisher The application event publisher
61-
* @param roleServices Role services
62-
* @param rolePermissionServices Role permission services
63-
* @param memberProfileServices Member profile services
56+
* @param loginHandler A collaborator which helps to build HTTP response depending on success or failure.
57+
* @param eventPublisher The application event publisher
58+
* @param roleServices Role services
59+
* @param rolePermissionServices Role permission services
60+
* @param memberProfileServices Member profile services
61+
* @param generator Generator for creating and signing the new token
6462
*/
6563
public ImpersonationController(LoginHandler loginHandler,
6664
ApplicationEventPublisher eventPublisher,
6765
RoleServices roleServices,
6866
RolePermissionServices rolePermissionServices,
69-
MemberProfileServices memberProfileServices) {
67+
MemberProfileServices memberProfileServices,
68+
JwtTokenGenerator generator) {
7069
this.loginHandler = loginHandler;
7170
this.eventPublisher = eventPublisher;
7271
this.roleServices = roleServices;
7372
this.rolePermissionServices = rolePermissionServices;
7473
this.memberProfileServices = memberProfileServices;
74+
this.generator = generator;
7575
}
7676

7777
@Consumes({MediaType.APPLICATION_FORM_URLENCODED, MediaType.APPLICATION_JSON})
7878
@Post("/begin")
7979
@RequiredPermission(Permission.CAN_IMPERSONATE_MEMBERS)
8080
public HttpResponse<Void> auth(HttpRequest<?> request, String email) {
81-
final Cookie jwt = request.getCookies().get(JWT);
82-
if (jwt == null) {
83-
// The user is required to be logged in. If this is null,
84-
// we are in an impossible state!
85-
LOG.error("Unable to locate the JWT");
81+
final Cookie jwt = request.getCookies().get(JWT);
82+
if (jwt == null) {
83+
// The user is required to be logged in. If this is null,
84+
// we are in an impossible state!
85+
LOG.error("Unable to locate the JWT");
8686
return HttpResponse.unauthorized();
87-
} else {
88-
LOG.info("Processing request to switch to user \'{}\'", email);
87+
} else {
88+
LOG.info("Processing request to switch to user '{}'", email);
8989
Set<MemberProfile> memberProfiles = memberProfileServices.findByValues(null, null, null, null, email, null, Boolean.FALSE);
9090
Iterator<MemberProfile> iterator = memberProfiles.iterator();
91-
if(!iterator.hasNext()) return HttpResponse.badRequest();
91+
if (!iterator.hasNext()) return HttpResponse.badRequest();
9292

9393
MemberProfile memberProfile = iterator.next();
94-
LOG.info("Profile exists for \'{}\'", email);
95-
String firstName = memberProfile.getFirstName() != null ? memberProfile.getFirstName() : "";
96-
String lastName = memberProfile.getLastName() != null ? memberProfile.getLastName() : "";
94+
LOG.info("Profile exists for '{}'", email);
95+
String firstName = memberProfile.getFirstName() != null ? memberProfile.getFirstName() : "";
96+
String lastName = memberProfile.getLastName() != null ? memberProfile.getLastName() : "";
9797
Set<String> roles = roleServices.findUserRoles(memberProfile.getId()).stream().map(role -> role.getRole()).collect(Collectors.toSet());
9898
Set<String> permissions = rolePermissionServices.findUserPermissions(memberProfile.getId()).stream().map(permission -> permission.name()).collect(Collectors.toSet());
9999

100100
Map<String, Object> newAttributes = new HashMap<>();
101-
newAttributes.put("email", memberProfile.getWorkEmail());
102-
newAttributes.put("name", firstName + ' ' + lastName);
103-
newAttributes.put("picture", "");
101+
newAttributes.put("email", memberProfile.getWorkEmail());
102+
newAttributes.put("name", firstName + ' ' + lastName);
103+
newAttributes.put("picture", "");
104104
newAttributes.put("roles", roles);
105105
newAttributes.put("permissions", permissions);
106+
JWTClaimsSet newSet = null;
107+
try {
108+
JWT parse = JWTParser.parse(jwt.getValue());
109+
JWTClaimsSet jwtClaimsSet = parse.getJWTClaimsSet();
110+
Map<String, Object> claims = new HashMap<>();
111+
claims.put("email", memberProfile.getWorkEmail());
112+
claims.put("name", firstName + ' ' + lastName);
113+
claims.put("picture", "");
114+
claims.put("exp", ((Date) jwtClaimsSet.getClaims().get("exp")).getTime());
115+
claims.put("iss", jwtClaimsSet.getClaims().get("iss"));
116+
claims.put("aud", jwtClaimsSet.getClaims().get("aud"));
117+
claims.put("sub", jwtClaimsSet.getClaims().get("sub"));
118+
newSet = JWTClaimsSet.parse(claims);
119+
Optional<String> signed = generator.generateToken(claims);
120+
121+
String token = signed.get();
122+
if (newSet != null) newAttributes.put("openIdToken", token);
123+
} catch (ParseException e) {
124+
throw new RuntimeException(e);
125+
}
106126

107127
LOG.info("Building authentication");
108128
Authentication updatedAuth = Authentication.build(email, roles, newAttributes);
109129
LOG.info("Publishing login");
110-
eventPublisher.publishEvent(new LoginSuccessfulEvent(updatedAuth, null, Locale.getDefault()));
111-
// Store the old JWT to allow the user to revert the impersonation.
130+
eventPublisher.publishEvent(new LoginSuccessfulEvent(updatedAuth, null, Locale.getDefault()));
131+
// Store the old JWT to allow the user to revert the impersonation.
112132
LOG.info("Attempting to swap tokens");
113-
return ((MutableHttpResponse)loginHandler.loginSuccess(updatedAuth, request)).cookie(
114-
new NettyCookie(originalJWT, jwt.getValue()).path("/").sameSite(SameSite.Strict)
115-
.maxAge(jwt.getMaxAge()));
116-
}
133+
return ((MutableHttpResponse) loginHandler.loginSuccess(updatedAuth, request)).cookie(
134+
new NettyCookie(originalJWT, jwt.getValue()).path("/").sameSite(SameSite.Strict)
135+
.maxAge(jwt.getMaxAge()));
136+
}
117137
}
118138

119139
@Produces(MediaType.TEXT_HTML)
@@ -126,13 +146,13 @@ public HttpResponse<Object> revert(HttpRequest<?> request) {
126146
// Swap the OJWT back to the JWT and remove the original JWT
127147
Set<Cookie> cookies = new HashSet<Cookie>();
128148
cookies.add(new NettyCookie(JWT, ojwt.getValue()).path("/")
129-
.sameSite(SameSite.Strict)
130-
.maxAge(ojwt.getMaxAge()).httpOnly());
149+
.sameSite(SameSite.Strict)
150+
.maxAge(ojwt.getMaxAge()).httpOnly());
131151
cookies.add(new NettyCookie(originalJWT, "").path("/").maxAge(0));
132152

133153
// Redirect to "/" while setting the cookies.
134154
return HttpResponse.temporaryRedirect(URI.create("/"))
135-
.cookies(cookies);
155+
.cookies(cookies);
136156
}
137157
}
138158
}

server/src/main/java/com/objectcomputing/checkins/services/kudos/KudosController.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.objectcomputing.checkins.services.kudos;
22

33
import io.micronaut.core.annotation.Nullable;
4+
import io.micronaut.core.convert.format.Format;
45
import io.micronaut.http.HttpStatus;
56
import io.micronaut.http.annotation.Body;
67
import io.micronaut.http.annotation.Controller;
@@ -17,6 +18,7 @@
1718
import jakarta.validation.Valid;
1819
import jakarta.validation.constraints.NotNull;
1920

21+
import java.time.LocalDate;
2022
import java.util.List;
2123
import java.util.UUID;
2224

@@ -53,6 +55,9 @@ public List<KudosResponseDTO> getRecent() {
5355
return kudosServices.getRecent();
5456
}
5557

58+
@Get("/public")
59+
public List<KudosResponseDTO> getPublic(@Nullable @Format("yyyy-MM-dd") LocalDate since) { return kudosServices.getPublic(since); }
60+
5661
@Get("/{id}")
5762
public KudosResponseDTO getById(@NotNull UUID id) {
5863
return kudosServices.getById(id);

server/src/main/java/com/objectcomputing/checkins/services/kudos/KudosRepository.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
import io.micronaut.data.model.query.builder.sql.Dialect;
77
import io.micronaut.data.repository.CrudRepository;
88

9+
import java.time.LocalDate;
10+
import java.util.Arrays;
911
import java.util.List;
1012
import java.util.UUID;
1113

@@ -54,4 +56,15 @@ public interface KudosRepository extends CrudRepository<Kudos, UUID> {
5456
AND (:includePending OR dateapproved IS NOT NULL)""", nativeQuery = true)
5557
List<Kudos> search(@Nullable String senderId, boolean includePending);
5658

59+
@Query(value = """
60+
SELECT
61+
id,
62+
PGP_SYM_DECRYPT(cast(message as bytea), '${aes.key}') as message,
63+
senderid, teamid, datecreated, dateapproved, publiclyVisible
64+
FROM kudos
65+
WHERE publiclyVisible IS TRUE
66+
AND (CAST(:since as date) IS NULL OR dateapproved >= :since)
67+
AND dateapproved IS NOT NULL
68+
ORDER BY datecreated DESC""", nativeQuery = true)
69+
List<Kudos> getPublic(@Nullable LocalDate since);
5770
}

server/src/main/java/com/objectcomputing/checkins/services/kudos/KudosServices.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import io.micronaut.core.annotation.Nullable;
44

5+
import java.time.LocalDate;
56
import java.util.List;
67
import java.util.UUID;
78

@@ -22,4 +23,6 @@ public interface KudosServices {
2223
void delete(UUID id);
2324

2425
List<KudosResponseDTO> findByValues(@Nullable UUID recipientId, @Nullable UUID senderId, @Nullable Boolean isPending);
26+
27+
List<KudosResponseDTO> getPublic(@Nullable LocalDate since);
2528
}

server/src/main/java/com/objectcomputing/checkins/services/kudos/KudosServicesImpl.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import com.objectcomputing.checkins.configuration.CheckInsConfiguration;
2929

3030
import io.micronaut.core.annotation.Nullable;
31+
import io.micronaut.core.convert.format.Format;
3132
import io.micronaut.transaction.annotation.Transactional;
3233

3334
import jakarta.inject.Named;
@@ -296,6 +297,13 @@ public List<KudosResponseDTO> getRecent() {
296297
.toList();
297298
}
298299

300+
public List<KudosResponseDTO> getPublic(@Nullable LocalDate since) {
301+
return kudosRepository.getPublic(since)
302+
.stream()
303+
.map(this::constructKudosResponseDTO)
304+
.toList();
305+
}
306+
299307
private List<KudosResponseDTO> findByPending(boolean isPending) {
300308
if (!hasAdministerKudosPermission()) {
301309
throw new PermissionException(NOT_AUTHORIZED_MSG);
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
micronaut:
2+
server:
3+
cors:
4+
enabled: true
5+
configurations:
6+
web:
7+
allowedOriginsRegex:
8+
- ^http(|s):\/\/localhost:.*$
9+
---
10+
datasources:
11+
default:
12+
url: ${JDBC_URL:`jdbc:postgresql://localhost:5432/checkinsdb`}
13+
username: postgres
14+
password: "postgres"
15+
---
16+
flyway:
17+
enabled: enabled
18+
datasources:
19+
default:
20+
locations:
21+
- "classpath:db/common"
22+
- "classpath:db/dev"

server/src/main/resources/application.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,11 @@ micronaut:
4848
secret: ${ OAUTH_REFRESH_TOKEN_SECRET:'pleaseChangeThisSecretForANewOne' }
4949
access-token:
5050
expiration: 28800
51+
signatures:
52+
secret:
53+
generator:
54+
jws-algorithm: HS256
55+
secret: ${ JWT_GENERATOR_SIGNATURE_SECRET:'pleaseChangeThisSecretForANewOne' }
5156
oauth2:
5257
# callback-uri: ${ OAUTH_CALLBACK_URI }
5358
clients:

server/src/test/java/com/objectcomputing/checkins/security/ImpersonationControllerTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ class ImpersonationControllerTest extends TestContainersSuite implements MemberP
4646

4747
private MemberProfile nonAdmin;
4848
private MemberProfile admin;
49-
private String jwt = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXUyJ9.eyJjb21wYW55IjoiRnV0dXJlRWQiLCJzdWIiOjEsImlzcyI6Imh0dHA6XC9cL2Z1dHVyZWVkLmRldlwvYXBpXC92MVwvc3R1ZGVudFwvbG9naW5cL3VzZXJuYW1lIiwiaWF0IjoiMTQyNzQyNjc3MSIsImV4cCI6IjE0Mjc0MzAzNzEiLCJuYmYiOiIxNDI3NDI2NzcxIiwianRpIjoiNmFlZDQ3MGFiOGMxYTk0MmE0MTViYTAwOTBlMTFlZTUifQ.MmM2YTUwMjEzYTE0OGNhNjk5Y2Y2MjEwZDdkN2Y1OTQ2NWVhZTdmYmI4OTA5YmM1Y2QwYTMzZjUwNTgwY2Y0MQ";
49+
private String jwt = "eyJhbGciOiJSUzI1NiIsImtpZCI6ImVlMTkzZDQ2NDdhYjRhMzU4NWFhOWIyYjNiNDg0YTg3YWE2OGJiNDIiLCJ0eXAiOiJKV1QifQ.eyJpc3MiOiJodHRwczovL2FjY291bnRzLmdvb2dsZS5jb20iLCJhenAiOiI4MzIxNDAwMjA1OTMtMTJxNWh2b2psajc0N24xdnV1cG9rNXZ2aTk5NXJlYzIuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20iLCJhdWQiOiI4MzIxNDAwMjA1OTMtMTJxNWh2b2psajc0N24xdnV1cG9rNXZ2aTk5NXJlYzIuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20iLCJzdWIiOiIxMTczOTEzNDIwMzMxOTg1MTI1ODEiLCJoZCI6Im9iamVjdGNvbXB1dGluZy5jb20iLCJlbWFpbCI6ImtpbWJlcmxpbm1Ab2JqZWN0Y29tcHV0aW5nLmNvbSIsImVtYWlsX3ZlcmlmaWVkIjp0cnVlLCJhdF9oYXNoIjoibENmaWkyWEdYaFEtOC1TeEs2N2Z5QSIsIm5vbmNlIjoiYzRkNTkyNjQtYmY3Ny00M2MwLTliMGMtYjZhYzZjNzgxN2Y1IiwibmFtZSI6Ik1pY2hhZWwgS2ltYmVybGluIiwicGljdHVyZSI6Imh0dHBzOi8vbGgzLmdvb2dsZXVzZXJjb250ZW50LmNvbS9hL0FDZzhvY0tIRHNOdlprclpaUS0xN1NiTTJqNWdXdDdpa09SOTI2UXZ0WFZSSnU5cTRRQl82UHZ4PXM5Ni1jIiwiZ2l2ZW5fbmFtZSI6Ik1pY2hhZWwiLCJmYW1pbHlfbmFtZSI6IktpbWJlcmxpbiIsImlhdCI6MTc0MjQxMjQxMiwiZXhwIjoxNzQyNDE2MDEyfQ.gVgncacfKyzr5_NSMOAu7xGRKnlBA_tg_1JCcfJ4KBMa0Xnvvhhx9Fix252_SQ5xpaG7b-sDApEl1fTcMKqUfYNrj-5s3SzWzCoV6g4NI44YN1j_KyjKFUZ6RWJ79U1U8_DA_wBATQvA-_NNMT8WL9A4muolH-cjoXixymCkl6dgd5QjriEOQC20QYCSbp_nHpeAgbl6fvCh8ZvpK2bHb6zwDmjYuN_xRuQyztfKS1X24nSB0k840Jdw2kUSzXZITUE6zOskapYKlTxcP8BGQ3GPydrNFxGy9Ec6GR3I0J-QhcE5b4mBijae9hgWw6Yz93SSjpm_w8w9D_fO_6yVSA";
5050

5151
@BeforeEach
5252
void setUp() {

server/src/test/java/com/objectcomputing/checkins/services/fixture/KudosFixture.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,17 @@ default Kudos createApprovedKudos(UUID senderId) {
4040
return getKudosRepository().save(kudos);
4141
}
4242

43+
default Kudos createApprovedKudos(UUID senderId, LocalDate approved) {
44+
Kudos kudos = new Kudos();
45+
kudos.setMessage("Default Kudos");
46+
kudos.setSenderId(senderId);
47+
kudos.setPubliclyVisible(true);
48+
kudos.setDateCreated(approved.minusDays(1));
49+
kudos.setDateApproved(approved);
50+
51+
return getKudosRepository().save(kudos);
52+
}
53+
4354
default KudosRecipient createKudosRecipient(UUID kudosId, UUID memberId) {
4455
KudosRecipient kudosRecipient = new KudosRecipient(kudosId, memberId);
4556

0 commit comments

Comments
 (0)