11package com .objectcomputing .checkins .security ;
22
3+ import com .nimbusds .jwt .JWT ;
4+ import com .nimbusds .jwt .JWTClaimsSet ;
5+ import com .nimbusds .jwt .JWTParser ;
36import com .objectcomputing .checkins .Environments ;
47import com .objectcomputing .checkins .services .memberprofile .MemberProfile ;
58import com .objectcomputing .checkins .services .memberprofile .MemberProfileServices ;
1417import io .micronaut .http .HttpResponse ;
1518import io .micronaut .http .MediaType ;
1619import 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 .*;
2221import io .micronaut .http .cookie .Cookie ;
2322import io .micronaut .http .cookie .SameSite ;
2423import io .micronaut .http .netty .cookies .NettyCookie ;
2928import io .micronaut .security .event .LoginSuccessfulEvent ;
3029import io .micronaut .security .handlers .LoginHandler ;
3130import io .micronaut .security .rules .SecurityRule ;
31+ import io .micronaut .security .token .jwt .generator .JwtTokenGenerator ;
3232import org .slf4j .Logger ;
3333import org .slf4j .LoggerFactory ;
3434
3535import 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 .*;
4238import 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}
0 commit comments