How to get Original User after Impersonating another user? - spring

I am using Switch user filter given by spring for Impersonating an user.
How can I get the Original user Who is Impersonating in the SwitchUserFilter.
Steps I am doing:
Ex.
1. Log in with User1
2. Impersonting to the User2. (user1 impersonate User2)
3. In Filter I am getting **authentication.getName()** as **User2**
4. While switching back to Original I am getiing **authentication.getName()** as **Null**
Now My Need is I want to get the original user (User1) in the filter at the time of swtichback.
Can it be possible.
Please suggest.
Let me know anyone needs any more inputs. please comment.
Thanks in advance.

This is how you can access the original User :
Collection<? extends GrantedAuthority> authorities = SecurityContextHolder.getContext().getAuthentication().getAuthorities();
for (GrantedAuthority grantedAuthority : authorities) {
if (SwitchUserFilter.ROLE_PREVIOUS_ADMINISTRATOR.equals(grantedAuthority.getAuthority())) {
System.out.println(((SwitchUserGrantedAuthority) grantedAuthority).getSource().getPrincipal());
}
}

add this custom method in UserJwTController in jhipster generated application
#PostMapping("/authenticate-externalnodes")
public ResponseEntity<JWTToken> authenticateExternalnodes(#Valid #RequestBody LoginVM loginVM) {
// Get Roles for user via username
Set<Authority> authorities = userService.getUserWithAuthoritiesByLogin(loginVM.getUsername()).get()
.getAuthorities();
// Create Granted Authority Rules
Set<GrantedAuthority> grantedAuthorities = new HashSet<>();
for (Authority authority : authorities) {
grantedAuthorities.add(new SimpleGrantedAuthority(authority.getName()));
}
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(
loginVM.getUsername(), "", grantedAuthorities);
Authentication authentication = authenticationToken;
SecurityContextHolder.getContext().setAuthentication(authentication);
boolean rememberMe = (loginVM.isRememberMe() == null) ? false : loginVM.isRememberMe();
String jwt = tokenProvider.createToken(authentication, rememberMe);
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.add(JWTFilter.AUTHORIZATION_HEADER, "Bearer " + jwt);
return new ResponseEntity<>(new JWTToken(jwt), httpHeaders, HttpStatus.OK);
}

Related

Extending a Jhipster JWT (Spring) monolith application to support impersonation

I have generated a jhipster angular/java application that is using JWT authentication.
I now want to extend the application to support impersonation.
I am interested in achieving the following:
Impersonation by admin: Allowing the admin user to login as any other user
Impersonation granted to user: Allowing another user that has been granted the right to impersonate a user (granted by the user itself) to login as that other user.
Audit - recording changes (audit function) - the audit trail must be able to distinguish between the actual user and an impersonated user and record this in the audit trail.
I see that Spring supports impersonation but it is unclear to me how I can implement it properly in my Jhipster application given that JWT is used. I am not sure if the Spring route is appropriate for JHipster-JWT-Monolith application - I am of the opinion that it not the right approach.
While there are some incomplete information on various other posts, after an extensive search I have been unable to find a post that can provide clear step by step guide on this. If somebody can do that for me it would be greatly appreciated. I expect others would also find such an answer very useful.
Thanks in advance.
Fergal
You just need to add below method in UserJwtController.java
#PostMapping("/authenticate-externalnodes")
public ResponseEntity<JWTToken> authenticateExternalnodes(#Valid #RequestBody LoginVM loginVM) {
// Get Roles for user via username
Set<Authority> authorities = userService.getUserWithAuthoritiesByLogin(loginVM.getUsername()).get()
.getAuthorities();
// Create Granted Authority Rules
Set<GrantedAuthority> grantedAuthorities = new HashSet<>();
for (Authority authority : authorities) {
grantedAuthorities.add(new SimpleGrantedAuthority(authority.getName()));
}
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(
loginVM.getUsername(), "", grantedAuthorities);
Authentication authentication = authenticationToken;
SecurityContextHolder.getContext().setAuthentication(authentication);
boolean rememberMe = (loginVM.isRememberMe() == null) ? false : loginVM.isRememberMe();
String jwt = tokenProvider.createToken(authentication, rememberMe);
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.add(JWTFilter.AUTHORIZATION_HEADER, "Bearer " + jwt);
return new ResponseEntity<>(new JWTToken(jwt), httpHeaders, HttpStatus.OK);
}

Get the authority of the user at the time of deleting it in jhipster

I need to get the authority of the user which I am going to delete. My attempt as follows.
#DeleteMapping("/users/{login:" + Constants.LOGIN_REGEX + "}")
#Timed
#Secured({AuthoritiesConstants.ADMIN, AuthoritiesConstants.LECTURER})
public ResponseEntity<Void> deleteUser(#PathVariable String login) {
log.debug("REST request to delete User: {}", login);
boolean hasAuthorityAdmin = false;
boolean hasAuthorityMember = false;
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
hasAuthorityAdmin = authorities.contains(new SimpleGrantedAuthority(AuthoritiesConstants.ADMIN));
hasAuthorityMember = authorities.contains(new SimpleGrantedAuthority(AuthoritiesConstants.MEMBER));
if (hasAuthorityAdmin) {
// delete user
userService.deleteUser(login);
} else {
if (hasAuthorityMember) {
// delete user if it is a student
if (**x**.contains(AuthoritiesConstants.STUDENT)) {
userService.deleteUser(login);
}
}
}
return ResponseEntity.ok().headers(HeaderUtil.createAlert("userManagement.deleted", login)).build();
}
There instead of x I need a method to retrieve it? That means I need to retrieve the authority which I am going to delete. So anyone have idea. This is inside the userResource.java. Can anyone help me with the code?
Suppose that I am logged in as an member. Then I am going to delete student. So when I am click the delete button of the student record, should be able to get the ROLE_STUDENT via a method.
This should do it :
if (hasAuthorityMember) {
Optional<User> user = userService.getUserWithAuthoritiesByLogin(login);
Set<Authority> currentUserAuthorities = user.get().getAuthorities();
for(Authority auth : currentUserAuthorities) {
// delete user if it is a student
if(auth.getName().equals(AuthoritiesConstants.STUDENT)) {
userService.deleteUser(login);
}
}
}
With UserService, you get the user and his authorities thanks to its login, and for each of its authorities (if there are many), we check the name of the authority. If corresponds to Student, you then delete the user.

Spring security : auto login issue

i am trying to auto login user after signup. Here is code for auto login
private boolean autoLogin(HttpServletRequest request, User user) {
SimpleGrantedAuthority auth = new SimpleGrantedAuthority("ADMIN");
Collection<SimpleGrantedAuthority> authorities = new HashSet<SimpleGrantedAuthority>();
authorities.add(auth);
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(
user.getEmail(), user.getPassword(), authorities);
token.setDetails(new WebAuthenticationDetails(request));
authenticationManager.authenticate(token);
SecurityContextHolder.getContext().setAuthentication(token);
return true;
}
and inside an interceptor that check logged in user code is
Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
Problem is when i debug the code (after auto login) the principal object has logged in user's email address instead of UserDetails object.
Things working fine when i log in useing spring security login form.
You're missing re-assigning the return from AuthenticationManager.authenticate().
This line:
authenticationManager.authenticate(token);
should be:
token = authenticationManager.authenticate(token);
That should fix things.

Spring Security: Set GrantedAuthorities

Is there anyway to set the List<GrantedAuthority> in the Authentication/UserDetailsImpl object? In my application, I have two layers of security, one for logging in (which uses my custom login authenticator, in the class I set the Authentication object using the UsernamePasswordAuthenticationToken) and one for a "challenge question" where the user is prompted to answer a particular question.
What I want to do is add a GrantedAuthority to the current List<GrantedAuthority>, which was created during the login process, after the user answers the challenge question.
Is this possible?
you can do it with following code:
Collection<SimpleGrantedAuthority> oldAuthorities = (Collection<SimpleGrantedAuthority>)SecurityContextHolder.getContext().getAuthentication().getAuthorities();
SimpleGrantedAuthority authority = new SimpleGrantedAuthority("ROLE_ANOTHER");
List<SimpleGrantedAuthority> updatedAuthorities = new ArrayList<SimpleGrantedAuthority>();
updatedAuthorities.add(authority);
updatedAuthorities.addAll(oldAuthorities);
SecurityContextHolder.getContext().setAuthentication(
new UsernamePasswordAuthenticationToken(
SecurityContextHolder.getContext().getAuthentication().getPrincipal(),
SecurityContextHolder.getContext().getAuthentication().getCredentials(),
updatedAuthorities)
);
The UserDetails.getAuthorities() method just returns a Collection<GrantedAuthority> object. You can use the appropriate Collection method to add your new authority to that collection.
Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
if (principal instanceof UserDetails) {
((UserDetails) principal).getAuthorities().add(New GrantedAuthorityImpl("ROLE_FOO"));
}
Selah.

Log user in with remember-me functionality in Spring 3.1

I currently log users in programmatically (like when they login through Facebook or other means than using my login form) with:
SecurityContextHolder.getContext().setAuthentication(
new UsernamePasswordAuthenticationToken(user, "", authorities)
);
What I want to do instead is log the user in as if they set the remember-me option on in the login form. So I'm guessing I need to use the RememberMeAuthenticationToken instead of the UsernamePasswordAuthenticationToken? But what do I put for the key argument of the constructor?
RememberMeAuthenticationToken(String key, Object principal, Collection<? extends GrantedAuthority> authorities)
UPDATE: I'm using the Persistent Token Approach described here. So there is no key like in the Simple Hash-Based Token Approach.
I assume you already have <remember-me> set in your configuration.
The way remember-me works is it sets a cookie that is recognized when the user comes back to the site after their session has expired.
You'll have to subclass the RememberMeServices (TokenBased or PersistentTokenBased) you are using and make the onLoginSuccess() public. For example:
public class MyTokenBasedRememberMeServices extends PersistentTokenBasedRememberMeServices {
#Override
public void onLoginSuccess(HttpServletRequest request, HttpServletResponse response, Authentication successfulAuthentication) {
super.onLoginSuccess(request, response, successfulAuthentication);
}
}
<remember-me services-ref="rememberMeServices"/>
<bean id="rememberMeServices" class="foo.MyTokenBasedRememberMeServices">
<property name="userDetailsService" ref="myUserDetailsService"/>
<!-- etc -->
</bean>
Inject your RememberMeServices into the bean where you are doing the programmatic login. Then call onLoginSuccess() on it, using the UsernamePasswordAuthenticationToken that you created. That will set the cookie.
UsernamePasswordAuthenticationToken auth =
new UsernamePasswordAuthenticationToken(user, "", authorities);
SecurityContextHolder.getContext().setAuthentication(auth);
getRememberMeServices().onLoginSuccess(request, response, auth);
UPDATE
#at improved upon this, with no subclassing of RememberMeServices:
UsernamePasswordAuthenticationToken auth =
new UsernamePasswordAuthenticationToken(user, "", authorities);
SecurityContextHolder.getContext().setAuthentication(auth);
// This wrapper is important, it causes the RememberMeService to see
// "true" for the "_spring_security_remember_me" parameter.
HttpServletRequestWrapper wrapper = new HttpServletRequestWrapper(request) {
#Override public String getParameter(String name) { return "true"; }
};
getRememberMeServices().loginSuccess(wrapper, response, auth);
This is the source for the constructor.
public RememberMeAuthenticationToken(String key, Object principal, Collection<? extends GrantedAuthority> authorities) {
super(authorities);
if ((key == null) || ("".equals(key)) || (principal == null) || "".equals(principal)) {
throw new IllegalArgumentException("Cannot pass null or empty values to constructor");
}
this.keyHash = key.hashCode();
this.principal = principal;
setAuthenticated(true);
}
The key is hashed and its used to determine whether the authentication used for this user in the security context is not a 'forged' one.
Have a look at the RememberMeAuthenicationProvider source.
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
if (!supports(authentication.getClass())) {
return null;
}
if (this.key.hashCode() != ((RememberMeAuthenticationToken) authentication).getKeyHash()) {
throw new BadCredentialsException(messages.getMessage("RememberMeAuthenticationProvider.incorrectKey",
"The presented RememberMeAuthenticationToken does not contain the expected key"));
}
return authentication;
}
So to answer your question, you need to pass the hash code of the key field of the Authentication representing the user.

Resources