Grails/SpringSecurity: Getting locale in LogoutHandler - grails

In a grails app, I'm successfully getting the language the user has selected(added to url, "...?lang=xx_XX") like this:
def locale = RequestContextUtils.getLocale(request)
Using springsecurity, and got a special logout handler set up that works fine
grails.plugins.springsecurity.logout.handlerNames = ['securityContextLogoutHandler', 'myLogoutHandler']
I need to get the user selected locale in myLogoutHandler, but the following is not working (it only shows the browser default locale, not the one selected by the user)
class MyLogoutHandler implements LogoutHandler {
void logout(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) {
def locale2 = RequestContextUtils.getLocale(httpServletRequest);
}
}
I have also tried getting the session locale with:
RequestContextHolder.currentRequestAttributes().getProperties()
but that gives the same result, anyone got an idea how to get the locale from the MyLogoutHandler?

Workaround
The session appears to be cleared by spring-security but you can still send in parameters.
so in the controller for the logout page I got:
def index = {
def myParam = "bar"
redirect(uri: SpringSecurityUtils.securityConfig.logout.filterProcessesUrl + "?foo=" + myParam) // '/j_spring_security_logout'
}
And I just get the parameter in the LogoutHandler:
void logout(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) {
def myParam = httpServletRequest.parameterMap.get("foo")[0]
....
}

I can get the locales by using the code below:
class CustomLogoutSuccessHandler extends SimpleUrlLogoutSuccessHandler {
private static final ThreadLocal<Authentication> AUTH_HOLDER = new ThreadLocal<Authentication>()
void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
AUTH_HOLDER.set authentication
// reading locales...
request.locales.each {locale ->println locale.toString()}
try {
super.handle(request, response, authentication)
}
finally {
AUTH_HOLDER.remove()
}
}
#Override
protected String determineTargetUrl(HttpServletRequest request, HttpServletResponse response) {
Authentication auth = AUTH_HOLDER.get()
String url = super.determineTargetUrl(request, response)
// do something with the url based on session data..
url
}
}

Related

Spring Security SAML2, sending language code in <Extensions> -element

I'm currently migrating from Spring Security SAML Extension to Spring Security SAML2 and use case requires language code to be sent in Extensions -element.
With Spring Security SAML Extension this was done by:
Extending SAMLEntryPoint and storing locale as relayState to SAMLMessageContext like this:
public class CustomSAMLEntryPoint extends SAMLEntryPoint {
private String relayState;
#Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authenticationException) throws IOException, ServletException {
//read your request parameter
setRelayState(request.getParameter("locale"));
super.commence(request, response, authenticationException);
}
#Override
protected WebSSOProfileOptions getProfileOptions(SAMLMessageContext samlMessageContext, AuthenticationException authenticationException) throws MetadataProviderException {
//set the relayState to your SAML message context
samlMessageContext.setRelayState(getRelayState());
return super.getProfileOptions(samlMessageContext, authenticationException);
}
private void setRelayState(String relayState) {
this.relayState = relayState;
}
private String getRelayState() {
return relayState;
}
}
Extending WebSSOProfileImpl and using previously set relayState value to generate Extensions -element:
public class CustomWebSSOProfileImpl extends WebSSOProfileImpl {
#Override
protected AuthnRequest getAuthnRequest(SAMLMessageContext context, WebSSOProfileOptions options, AssertionConsumerService assertionConsumer, SingleSignOnService bindingService) throws SAMLException, MetadataProviderException {
AuthnRequest authnRequest = super.getAuthnRequest(context, options, assertionConsumer, bindingService);
authnRequest.setExtensions(buildExtensions(context.getRelayState()));
return authnRequest;
}
}
How could this same functionality be done with Spring Security Core SAML2? Is there some similar way than using SAMLMessageContext and relayState?
I could customize AuthenticationEntryPoint as well as authentication request creation but there seems to be no way to move locale between these two.
public AuthenticationEntryPoint authenticationEntryPoint() {
final AuthenticationEntryPoint authenticationEntryPoint = new LoginUrlAuthenticationEntryPoint(
"/saml2/authenticate/sp");
return (request, response, exception) -> {
String locale = request.getParameter("locale");
// Where shoud locale be stored???
authenticationEntryPoint.commence(request, response, exception);
};
}
#Bean
public Saml2AuthenticationRequestFactory authenticationRequestFactory() {
final OpenSamlAuthenticationRequestFactory authenticationRequestFactory = new OpenSamlAuthenticationRequestFactory();
authenticationRequestFactory.setAuthenticationRequestContextConverter(context -> {
final AuthnRequest request = new AuthnRequestBuilder().buildObject();
request.setAssertionConsumerServiceURL(context.getAssertionConsumerServiceUrl());
request.setDestination(context.getDestination());
request.setID("A" + UUID.randomUUID());
request.setIssueInstant(new DateTime());
final Issuer issuer = new IssuerBuilder().buildObject();
issuer.setValue(context.getIssuer());
request.setIssuer(issuer);
// Where can locale be read from???
request.setExtensions(buildLanguageExtensions(???);
return request;
});
return authenticationRequestFactory;
}

Session expiration management with Spring Session for a single page app

I use Spring Session and I am having issues with session management especially dealing with session expiration.
The idea is to return a custom Http Header to the client e.g. X-Application-Session-Is-New if the session has expired.
Here is what I came up with:
public class SessionDestroyedFilter extends OncePerRequestFilter {
//TODO: not always invoked!!
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
if (isAjaxRequest(request) && (isRequestedSessionInvalid(request) || isSessionNew(request))) {
response.addHeader("X-Application-Session-Is-New", "true");
}
filterChain.doFilter(request, response);
}
private boolean isRequestedSessionInvalid(HttpServletRequest request) {
return !request.isRequestedSessionIdValid();
}
private boolean isSessionNew(HttpServletRequest request) {
return request.getSession(false).isNew();
}
private boolean isAjaxRequest(HttpServletRequest request) {
return "XMLHttpRequest".equals(request.getHeader("X-Requested-With"));
}
}
The issue is that my filter does not seem to be always invoked upon session expiration perhaps because the request is not an ajax request and a new session ID is immediately created after that.
Can anyone please point me to an appropriate strategy to deal with session expiration on single page apps?
EDIT Following is currently not possible (instead do decoration)
Here are some related github issues (comment on them to fix them faster)
https://github.com/spring-projects/spring-session/issues/243
https://github.com/spring-projects/spring-session/issues/112
To accomplish this you have to use your own HttpSessionStrategy.
Here is an example if you are using CookieHttpSessionStrategy (the default one)
public class CustomHttpSessionStrategy extends CookieHttpSessionStrategy {
#Override
public void onInvalidateSession(HttpServletRequest request, HttpServletResponse response) {
super.onInvalidateSession(request, response);
response.addHeader("X-Application-Session-Is-New", "true");
}
}
If you want to add your header on new sessions also consider overriding onNewSession(Session, HttpServletRequest, HttpServletResponse).
This is Wrong
Please see my other answer
You should move your logic for adding X-Application-Session-Is-New after request is processed.
Try something like
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
filterChain.doFilter(request, response);
if (isAjaxRequest(request) && (isRequestedSessionInvalid(request) || isSessionNew(request))) {
response.addHeader("X-Application-Session-Is-New", "true");
}
}

Grails and Spring Security Plugin: Redirecting user upon login based on roles

I want to redirect a user upon a successful login based on his roles. I am following this example but the method never executes(print does not happen).
My resources.groovy:
beans = {
authenticationSuccessHandler(UrlRedirectEventListener)
{
def conf = SpringSecurityUtils.securityConfig
requestCache = ref('requestCache')
defaultTargetUrl = conf.successHandler.defaultTargetUrl
alwaysUseDefaultTargetUrl = conf.successHandler.alwaysUseDefault
targetUrlParameter = conf.successHandler.targetUrlParameter
useReferer = conf.successHandler.useReferer
redirectStrategy = ref('redirectStrategy')
defaultUrl= "/"
adminUrl = "/admin/"
userUrl = "/user/"
}
}
And my UrlRedirectEventListener
class UrlRedirectEventListener
extends SavedRequestAwareAuthenticationSuccessHandler
{
#Override
protected String determineTargetUrl(HttpServletRequest request,
HttpServletResponse response) {
boolean hasBoth = SpringSecurityUtils.ifAllGranted("ROLE_USER,ROLE_ADMIN");
boolean hasUser = SpringSecurityUtils.ifAnyGranted("ROLE_USER");
boolean hasAdmin = SpringSecurityUtils.ifAnyGranted("ROLE_ADMIN");
println("hasBoth:"+hasBoth+ " hasUser:"+hasUser+ " hasAdmin:"+hasAdmin)
if( hasBoth ){
return defaultLoginUrl;
}else if ( hasUser ){
return userUrl ;
}else if ( hasAdmin ){
return adminUrl ;
}else{
return super.determineTargetUrl(request, response);
}
}
private String defaultLoginUrl;
private String mmrLoginUrl;
private String pubApiLoginUrl;
...setters for urls
}
What am I missing?
I am using Grails 2.0.4 and Spring Security Plugin 2 RC2.0
Thanks!
Edit:
I also have this controller to test that my bean is created, which shows up correctly.
class TestController {
AuthenticationSuccessHandler authenticationSuccessHandler
def index()
{
render( authenticationSuccessHandler )
}
}
After looking at the source, I figured it out. The problem was that SavedRequestAwareAuthenticationSuccessHandler's method onAuthenticationSuccess would never reach the overridden method determineTargetUrl with the simple login scheme I was using( the default that is generated by the plugin ). I had to actually override onAuthenticationSuccess instead and put my logic there.
The source in question:
SavedRequestAwareAuthenticationSuccessHandler extends SimpleUrlAuthenticationSuccessHandler extends AbstractAuthenticationTargetUrlRequestHandler
My updated UrlRedirectEventListener, with most copy-pasted from SavedRequestAwareAuthenticationSuccessHandler:
#Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
Authentication authentication) throws ServletException, IOException {
SavedRequest savedRequest = requestCache.getRequest(request, response);
if (savedRequest == null) {
super.onAuthenticationSuccess(request, response, authentication);
return;
}
String targetUrlParameter = getTargetUrlParameter();
if (isAlwaysUseDefaultTargetUrl() || (targetUrlParameter != null && StringUtils.hasText(request.getParameter(targetUrlParameter)))) {
requestCache.removeRequest(request, response);
super.onAuthenticationSuccess(request, response, authentication);
return;
}
def targetUrl = savedRequest.getRedirectUrl();
if( targetUrl != null && targetUrl != "" && targetUrl.endsWith("/myApp/") ) // I only want to differently handle "/". If a user hits /user/, or any other url, attempt to send him there. Spring Security will deny him access based on his privileges if necessary.
targetUrl = this.determineTargetUrl( request, response ) // This is the method defined above. Only thing changed there is I removed the `super` call
clearAuthenticationAttributes(request);
logger.info("Redirecting to Url: " + targetUrl);
getRedirectStrategy().sendRedirect(request, response, targetUrl);
}

JSF page redirect

We have a JSF2.0 application deployed in weblogic-10.3.4 , we have a requirement to give a user generic url ,say (http://web/apply?7777 ) . When user access this page ,based on query string value , user will be re-directed to client specific page,which can be one of 10 different pages.
So one approach is to have a apply.jsf page ,which has got a pre-render event ,which will re-direct the user to different page based on query string,
Is there any other better approach? not to have apply.xhtml.
Note: In web.xml ,we defined pageNotFound.xhtml in case if the page is not found.
You could use a simple servlet filter for this.
#WebFilter("/apply")
public class ApplyFilter implements Filter {
#Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
String queryString = request.getQueryString();
String redirectURL = determineItBasedOnQueryString(queryString);
if (redirectURL != null) {
response.sendRedirect(redirectURL);
} else {
response.sendError(HttpServletResponse.SC_NOT_FOUND);
}
}
// ...
}

Get original request url in Spring Security form-login page

I have the following declared in my spring security configuration file (http://www.springframework.org/schema/security/spring-security-2.0.1.xsd):
<form-login login-page="/login.html" />
What Spring Security does is redirect the user to that page if they don't have the correct authentication credentials. How can I get the url of the page the user was trying to get to?
Original request is represented by the SavedRequest object, which can be accessed as a session attribute named SPRING_SECURITY_SAVED_REQUEST_KEY.
in my case i did something like this and it worked for me.
#Autowired
private LoggedUserListener loggedUserListener;
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.antMatchers("/find/**","/","/Application/**")
.access("hasRole('ROLE_USER')")
.successHandler(loggedUserListener)
//some other stuff
}
#Component
public class LoggedUserListener implements AuthenticationSuccessHandler{
#Autowired
private UserRepo userRepo;
#Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
Authentication authentication) throws IOException, ServletException {
HttpSession session = request.getSession();
SavedRequest savedRequest = (SavedRequest) session.getAttribute("SPRING_SECURITY_SAVED_REQUEST");
if(savedRequest != null) {
User u = userRepo.findByUsername(authentication.getName());
u.setLastLogin(new Date());
u.setAccountNonLocked(false);
userRepo.save(u);
response.sendRedirect(savedRequest.getRedirectUrl());
}
}
}
In Spring Security 4
The original request is represented by the DefaultSavedRequest object, which can be accessed as a session attribute named SPRING_SECURITY_SAVED_REQUEST.
#RequestMapping(value = "/login", method = RequestMethod.GET)
public String login(HttpSession session) {
DefaultSavedRequest savedRequest = (DefaultSavedRequest) session.getAttribute("SPRING_SECURITY_SAVED_REQUEST");
}

Resources