I want to create a "Impersonate" feature in my JSF application. This functionality would provide the Administrator with the ability to access the application authenticated with a low-level user without even knowing the password.
I though it would be a simple setUserPrincipal, similar to what I use to get the current logged in User
FacesContext.getCurrentInstance().getExternalContext().getUserPrincipal(), but I couldn't find any "setUserPrincipal" method inside javax.faces.context.ExternalContext...
In short, what I want is to programmatically change the current logged in user, so the admin can impersonate any other user, without informing the credentials. Is it possible?
Thanks
I strongly advise against playing with authentication/authorization unless you really don't have alternatives.
Anyway, leave out JSF, it comes in the game too late.
The simplest way is to provide a customized request, supplied with a filter:
#WebFilter(filterName = "impersonateFilter", urlPatterns = "/*", asyncSupported = true)
public class ImpersonateFilter implements Filter
{
#Override
public void init(FilterConfig filterConfig) throws ServletException
{
// do nothing
}
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException
{
HttpServletRequest httpRequest = (HttpServletRequest) request;
ImpersonateRequest impersonateRequest = new ImpersonateRequest(httpRequest);
chain.doFilter(impersonateRequest, response);
}
#Override
public void destroy()
{
// do nothing
}
public static class ImpersonateRequest extends HttpServletRequestWrapper
{
protected Principal principal;
public ImpersonateRequest(HttpServletRequest request)
{
super(request);
HttpSession session = request.getSession(false);
if(session != null)
{
principal = (Principal) session.getAttribute(ImpersonateRequest.class.getName());
}
}
#Override
public Principal getUserPrincipal()
{
if(principal == null)
{
principal = super.getUserPrincipal();
}
return principal;
}
public void setUserPrincipal(Principal principal)
{
this.principal = principal;
getSession().setAttribute(ImpersonateRequest.class.getName(), principal);
}
#Override
public String getRemoteUser()
{
return principal == null ? super.getRemoteUser() : principal.getName();
}
}
}
Something like this should suffice.
Related
I am using Servlet Filter to intercept request which has cookies present and then trying to override specific cookie value using ThreadLocal.
But the overridden value is not getting reflected. Also, it is not able to add a new cookie when tried. Couldn't able to figure out what I am doing wrong.
Controller Class has two get endpoints. With cookie endpoint I am trying to add a cookie in the response, to intercept and test it when I hit override endpoint afterwards.
#RestController
public class FilterController {
#Autowired
AlphaCookie alphaCookie;
#Autowired
AlphaCookieFilter alphaCookieFilter;
#GetMapping("override") // testing endpoint for overriding cookie
public String cookieTest() {
this.alphaCookieFilter.persist(new AlphaCookie("newValue"));
return "Cookie Overriden, { AlphaCookie: newValue }";
}
#GetMapping("cookie") // to add cookie for testing
public String addCookieToBrowser(HttpServletResponse httpServletResponse) {
Cookie cookie = new Cookie("AlphaCookie", "oldValue");
cookie.setMaxAge(3600);
httpServletResponse.addCookie(cookie);
return "Cookie added, { AlphaCookie: old }";
}
}
Filter to intercept the request and check for the specific cookie, also override the cookie
#Component("alphaCookieFilter")
public class AlphaCookieFilter implements Filter {
#Autowired
private ApplicationContext applicationContext;
public static final ThreadLocal<HttpServletResponse> RESPONSE_HOLDER = new ThreadLocal<>();
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
RESPONSE_HOLDER.set((HttpServletResponse) response);
String activeCookie = null;
if (httpServletRequest.getCookies() != null) {
for (Cookie cookie: httpServletRequest.getCookies()) {
if ("AlphaCookie".equals(cookie.getName())) {
activeCookie = cookie.getValue();
}
}
}
this.applicationContext.getBean("alphaCookie", AlphaCookie.class)
.override(new AlphaCookie(activeCookie));
chain.doFilter(request, response);
RESPONSE_HOLDER.remove();
}
public void persist(AlphaCookie alphaCookie) {
Cookie cookie = new Cookie("AlphaCookie", alphaCookie.getActiveCookie());
cookie.setDomain("code.org");
cookie.setPath("/");
cookie.setMaxAge(-1);
cookie.setSecure(true);
cookie.setHttpOnly(true);
RESPONSE_HOLDER.get().addCookie(cookie);
}
}
POJO to store the cookie value
#Component("alphaCookie")
public class AlphaCookie implements Serializable {
private static final long serialVersionUID = 1L;
private String activeCookie;
public AlphaCookie() {
super();
}
public AlphaCookie(String activeCookie) {
this();
this.activeCookie = activeCookie;
}
public void override(AlphaCookie alphaCookie) {
synchronized (this) {
this.activeCookie = alphaCookie.activeCookie;
}
}
public String getActiveCookie() {
return this.activeCookie;
}
}
On debugging I found below properites are not allowing me to override the cookie value.
cookie.setDomain("code.org");
cookie.setSecure(true);
---------Resolved-----------
For local testing
We should always set the domain name to localhost
set cookie.setSecure to false as localhost is not secure protocol (not HTTPS)
How to redirect my previous page after SSO Login in spring security
I used set userReferer as true ,
But not able achieve it. please suggest some sample code or site.
Spring security with IDP we are using
public class LoginSuccessHandler extends SimpleUrlAuthenticationSuccessHandler
implements AuthenticationSuccessHandler {
private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();
public LoginSuccessHandler() {
super();
setUseReferer(true);
}
#Override
public void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
/// some code
//set our response to OK status
httpServletResponse.setStatus(HttpServletResponse.SC_OK);
String targetUrl = determineTargetUrl(authentication);
httpServletResponse.sendRedirect(targetUrl);
}
}
Once the user gets authenticated on IDP (Identity provider) side then SP (Service provider) receives assertions or response from IDP. That response would be validated on SP side. Upon response validation, this class OAuthUserLoginSuccessHandler would be called, where you can extract the information from the response provided by IDP and proceed with redirection as per the following code.
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
public class OAuthUserLoginSuccessHandler extends
SavedRequestAwareAuthenticationSuccessHandler {
public OAuthUserLoginSuccessHandler() {}
#Override
public void onAuthenticationSuccess(final HttpServletRequest request,
final HttpServletResponse response, final Authentication authentication)
throws IOException, ServletException {
if (authentication.getPrincipal() instanceof UserDetails
|| authentication.getDetails() instanceof UserDetails) {
UserDetails details;
if (authentication.getPrincipal() instanceof UserDetails) {
details = (UserDetails) authentication.getPrincipal();
} else {
details = (UserDetails) authentication.getDetails();
}
String username = details.getUsername();
// get user info from datastore using username
// some code
String redirectUri; // get target uri either from relay state or from datastore
if (null != redirectUri) {
response.sendRedirect(redirectUri);
return;
}
}
super.onAuthenticationSuccess(request, response, authentication);
}
}
I have been going square trying to implement a new RESTful web service using Spring.IO. I've worked through perhaps 30 different online examples that suppose to provide this example but none of them worked 'out of the box'.
A further complication is that 95% of examples use XML configuration exclusively, which in my personal opinion is not as readable as a pure java configuration.
After a great many hours I have managed to cobble together something that 'works' but I would very much be interested in feedback as to my particular implementation. Specifically:
Assuming the client authorisation token is not compromised is my implementation secure.
I was unable to correctly AutoWire the AuthenticationTokenProcessingFilter class in WebSecurityConfig due to the constructor requiring an AuthenticationManager. If there is a way to do this that would help clean things up a little.
The main application class:
#ComponentScan({"webservice"})
#Configuration
#EnableAutoConfiguration
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
The WebSecurityConfig class (AFAIK this does the majority of the job previously performed by the XML):
#Configuration
#EnableWebMvcSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
AuthenticationTokenProcessingFilter authenticationTokenProcessingFilter;
#Autowired
CustomAuthenticationEntryPoint customAuthenticationEntryPoint;
#Override
protected void configure(HttpSecurity http) throws Exception {
authenticationTokenProcessingFilter =
new AuthenticationTokenProcessingFilter(authenticationManager());
http
.csrf().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests().antMatchers("/login").permitAll()
.anyRequest().authenticated()
.and()
.addFilterBefore(authenticationTokenProcessingFilter, AnonymousAuthenticationFilter.class)
.httpBasic().authenticationEntryPoint(customAuthenticationEntryPoint);
}
}
The AuthenticationTokenProcessingFilter which performs the token authentication on client connect:
public class AuthenticationTokenProcessingFilter extends GenericFilterBean {
AuthenticationManager authManager;
public AuthenticationTokenProcessingFilter(AuthenticationManager authManager) {
this.authManager = authManager;
}
#Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
#SuppressWarnings("unchecked")
Map<String, String[]> parms = request.getParameterMap();
if(parms.containsKey("authToken")) {
String token = parms.get("authToken")[0];
// Validate the token
User user = TokenUtils.getUserFromToken(token);
// If we managed to get a user we can finish the authentication
if (user!=null) {
//Add a default authority for all users
List<GrantedAuthority> grantedAuths = new ArrayList();
grantedAuths.add(new SimpleGrantedAuthority("ROLE_ADMIN"));
// build an Authentication object with the user's info
AbstractAuthenticationToken authentication =
new UsernamePasswordAuthenticationToken(user, token, grantedAuths);
// set the authentication into the SecurityContext
SecurityContextHolder.getContext().setAuthentication(authentication);
}
}
// continue thru the filter chain
chain.doFilter(request, response);
}
}
The TokenUtils class used by AuthenticationTokenProcessingFilter:
public class TokenUtils {
/**
* Get an authorisation token for the provided userId
*
* #param userId
* #return
*/
public static String getToken(long userId) {
throw new UnsupportedOperationException("Not implemented yet!");
}
/**
* Attempt to get a user for the provided token
*
* #param token
* #return User if found, otherwise null
*/
public static User getUserFromToken(String token) {
throw new UnsupportedOperationException("Not implemented yet!");
}
}
The CustomAuthenticationEntryPoint, which returns a 403 if the user was not successfully authenticated:
#Component
public class CustomAuthenticationEntryPoint implements AuthenticationEntryPoint {
#Override
public void commence(HttpServletRequest request, HttpServletResponse response,
AuthenticationException authException) throws IOException, ServletException {
response.sendError( HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized: Authentication token was either missing or invalid." );
}
}
An finally my web service entry points:
#RestController
public class EntryPoints {
#RequestMapping(value = "/login", method={RequestMethod.POST})
public LoginResponse login(#RequestParam(value="username", required=true) String username,
#RequestParam(value="password", required=true) String password) {
LoginRequest loginRequest = new LoginRequest(username, password);
//Authenticate the user using the provided credentials
//If succesfull return authentication token
//return new LoginResponse(token);
throw new UnsupportedOperationException("Not implemented yet!");
}
#RequestMapping(value = "/account", method={RequestMethod.POST})
public AccountResponse account(#RequestParam(value="accountId", required=true) long accountId) {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
//Return the request account information
throw new UnsupportedOperationException("Not implemented yet!");
}
}
I need to do some checks before every page is loaded to see if there's a need to redirect the user to another page (for security reasons).
When I was using JSF 2.0 I used a phase listener to do this job. Now that I'm using JSF 2.2 and all my beans are not JSF beans anymore, but CDI beans, I think I'm offered better choices to do this (or not?).
I've heard of the viewAction event, but I wouldn't like to be repeating metadata on every page (only if there's no other option).
So what's the best approach to implement this scenario in JSF 2.2 with CDI?
UPDATE (after #skuntsel suggestion)
This is the filter that I'm using for now. I would like to use it only after authentication to simplify its code. By the way, if you can see any mistake in it, I would appreciate if you told me.
#WebFilter("/*")
public class SolicitacoesFilter implements Filter
{
// I can't just use #Inject private User _user, because it needs to be initialized
// only when the user is authenticated. Otherwise an exception is thrown. If this
// filter was called only after the authentication I could use the mentioned code.
private User _user;
#Inject
private Instance<User> _userGetter;
#Override
public void init(FilterConfig filterConfig) throws ServletException
{
}
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException
{
if (initializeUser(request))
{
if (_user.isProvisoryPassword())
{
// Redirect to another page...
return;
}
if (_user.getStatus() != Status.ACTIVE)
{
// Redirect to another page...
return;
}
}
chain.doFilter(request, response);
}
#Override
public void destroy()
{
}
private boolean initializeUser(ServletRequest request)
{
boolean userAuthenticated = ((HttpServletRequest) request).getUserPrincipal() != null;
if (userAuthenticated)
{
if (_user == null)
{
_user = _userGetter.get();
}
}
else
{
_user = null;
}
return _user != null;
}
}
Ok, what are the purposes of your redirection need ?
If it's about checking session User for authentification purposes, use filter:
Let's assume there is login form at : http://www.domain.com/login.jsf.
Once the user fires connect button, we want to redirect him to http://www.domain.com/member/welcome.jsf, and avoid other people not to access the member/welcome.jsf domain, I mean all the pages which are in http://www.domain.com/member/....
Here a simple design:
#WebFilter("/member/*")
public class SecurityCheck implements Filter {
public void init(FilterConfig filterConfig) throws ServletException {
}
#Override
public void doFilter(ServletRequest req, ServletResponse res,
FilterChain chain) throws ServletException, IOException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
HttpSession session = request.getSession();
if (session == null || session.getAttribute("User") == null) {
response.sendRedirect(request.getContextPath() + "/index.xhtml"); // No logged-in user found, so redirect to login page.
} else {
chain.doFilter(req, res); // Logged-in user found, so just continue request.
}
}
#Override
public void destroy() {
// Cleanup global variables if necessary.
}
Other case, use:
<h:link></h:link>,or <h:commandLink></h:commandLink> // Checking in the managed Beans method
You can also xml file, for redirection.
I would like to track user's sessions. I am interested in getting the user logname, context he accessed and the time when he accessed a certain context.
I was thinking of using a class that implements HttpSessionListener (overriding sessionCreated(final HttpSessionEvent se) and sessionDestroyed(final HttpSessionEvent se)) but on these methods I don't get access to the request (from where I could pull the user's logname and context he accessed).
Any suggestions are welcome.
I think a servlet Filter is more suitable for what you want. I suggest to write a custom filter around all the urls you want to track.
In the doFilter() method you have access to the HttpServletRequest as needed. From the request object you can get HttpSession too.
Here is an example:
#WebFilter("/*")
public class TrackingFilter implements Filter {
private FilterConfig filterConfig;
#Override
public void init(FilterConfig config) throws ServletException {
this.filterConfig = config;
}
#Override
public void doFilter(ServletRequest req, ServletResponse res,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpSession session = request.getSession(false);
String loggedInUser = "Unregistered user";
//assuming you have a session attribute named user with the username
if(session != null && session.getAttribute("user") != null) {
loggedInUser = (String) session.getAttribute("user");
}
Date accessedDate = new Date();
filterConfig.getServletContext().log(
String.format("%s accessed context %s on %tF %tT",
loggedInUser, request.getRequestURI() ,
accessedDate, accessedDate)
);
chain.doFilter(req, res);
}
#Override
public void destroy() {
}
}
See also: JavaEE6 tutorial section about filters.