How can I customize exception mappings of default AuthenticationEventPublisher in Grails Spring Security Plugin - spring-security

Background:
In my project, I needed to have two factor authentication (by sending OTP to registered email) which I implemented by extending DaoAuthenticationProvider.
In order to identify reason of why OTP authentication has failed, my custom authentication provider throws new custom exceptions for example BadOtpException, ExpiredOtpException, ConsumedOtpException. All these exceptions are subclasses of BadCredentialsException. This authentication flow is working fine.
Issue:
The Authentication events that were earlier getting published while I was using DaoAuthenticationProvider are now not getting published with my custom authentication provider.
Cause:
Upon some troubleshooting I figured out that Grails Spring Security Core plugin uses DefaultAuthenticationEventPublisher to publish the events. And this class publishes events on the basis of exception mappings which contain exception name vs event name to resolve the event that needs to be published whenever exception occurs. And these mappings are configured in its constructor.
Since mappings of my custom exceptions are not present in this constructor, the authentication failure events are not getting published.
Tried Solution:
I tried overriding DefaultAuthenticationEventPublisher and added new exception mappings by invoking super.setAdditionalExceptionMappings(). Here is the custom event publisher:
class CustomAuthenticationEventPublisher extends DefaultAuthenticationEventPublisher {
CustomAuthenticationEventPublisher() {
}
CustomAuthenticationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
super(applicationEventPublisher)
println('CustomAuthenticationEventPublisher')
Properties exceptionMappings = new Properties()
exceptionMappings.setProperty(BadOtpException.class.name, AuthenticationFailureBadCredentialsEvent.class.name)
super.setAdditionalExceptionMappings(exceptionMappings)
}
}
In resources.groovy, I registered my custom event publisher using following:
beans = {
authenticationEventPublisher(CustomAuthenticationEventPublisher)
}
But above solution is not working. Even the println() statement in the constructor is not getting logged. Seems like the bean is not getting registered.
Am I doing anything wrong in the above solution?
Is there any other way I can override the exception mappings?

With bit of more troubleshooting, I realized that zero argument constructor of the class CustomAuthenticationEventPublisher was being called instead of the other one.
So I tried setting the exception mappings in constructor and it worked.
Here is the code that worked form me:
class CustomAuthenticationEventPublisher extends DefaultAuthenticationEventPublisher {
CustomAuthenticationEventPublisher() {
println('CustomAuthenticationEventPublisher')
Properties exceptionMappings = new Properties()
exceptionMappings.setProperty(BadOtpException.class.name, AuthenticationFailureBadCredentialsEvent.class.name)
super.setAdditionalExceptionMappings(exceptionMappings)
}
CustomAuthenticationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
super(applicationEventPublisher)
}
}
Thanks.

Related

How to enable GlobalMethodsSecurity when unit test with MockMvc of standaloneSetup

SpringSecurity's #PreAuthorize and #PostAuthorize is ignored when unit testing with MockMvc. But it's OK when access by browser of Postman while normally started the application
I am using Spring 4.3 and Spring security 4.2, not the spring boot. I am using MockMvcBuilders.standaloneSetup to test the controller only. and don't want to use webAppContextSetup to involve the entire application to test.
After check the spring security's source code, I found that the Pre and PostAuthorize is checking by org.springframework.security.access.expression.method.ExpressionBasedPreInvocationAdvice and org.springframework.security.access.expression.method.ExpressionBasedPostInvocationAdvice. But the controller is not include by org.springframework.security.access.prepost.PrePostAnnotationSecurityMetadataSource.
I think this is caused by the controller is not initialized by Spring, so I try to register it to the BeanFactory, but it also fail.
Testing code:
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
mockMvc = standaloneSetup(controllers)
.setValidator(validator)
.apply(springSecurity(filterChainProxy))
.alwaysDo(print())
.build();
}
public void itWillFailWhenUpdateOtherOrg() {
CurrentUser user = new CurrentUser();
user.setOrgId(1);
user.setUsername("testuser");
mockMvc.perform(put("/orgs/-1")
.contentType(APPLICATION_JSON)
.content("{\"name\":\"RootOrg\",\"parent\":100}")
.with(user(user))).andExpect(status().isForbidden());
verify(orgService, never()).update(any());
}
Controller code:
#PutMapping("/org/{id}")
#PreAuthorize("principal.orgId == #orgDO.parent")
public OrgDO update(#PathVariable Integer id, #RequestBody OrgDO orgDO) {
}
When testing, the status code is 200, but not 403.
java.lang.AssertionError: Status
Expected :403
Actual :200
I expect the put request will fail and return status code 403, because of the principal.orgId != #orgDO.parent.
Be sure to NOT include all class to the Spring context, I just want to test the controller class.
Thank you very much.
After few hours of digging here is why:
MockMvcBuilders.standaloneSetup normally get passed a controller instantiated manually (not with Spring and therefore not with AOP). Therefore the PreAuthorize is not intercepted and security check is skipped. You can therefore either #Autowire your controller and pass it to MockMvcBuilders.standaloneSetup (which maybe kind of defies the purpose of using standalone setup since it's alos create the rest controller...) or simply use a WebApplicationContext: MockMvcBuilders.webAppContextSetup with an autowired WepAppContext.

In Grails 3.2.6, Async controller actions lose access to SecurityContextHolder

This wasn't an issue in Grails 2 and only appears to now occur in Grails 3. Any controller that invokes an async task is unable to access the SecurityContextHolder to get logged-in user information while rendering the view....
It appears that in SecurityContextPersistenceFilter, the SecurityContextHolder.clearContext() is being called before DispatcherServlet.processDispatchResult is able to render, making rendering code unable to access logged-in user info stored in SecurityContextHolder :
try {
SecurityContextHolder.setContext(contextBeforeChainExecution);
chain.doFilter(holder.getRequest(), holder.getResponse());
}
finally {
SecurityContext contextAfterChainExecution = SecurityContextHolder
.getContext();
// Crucial removal of SecurityContextHolder contents - do this before anything
// else.
SecurityContextHolder.clearContext();
repo.saveContext(contextAfterChainExecution, holder.getRequest(),
holder.getResponse());
request.removeAttribute(FILTER_APPLIED);
if (debug) {
logger.debug("SecurityContextHolder now cleared, as request processing completed");
}
}
At first I thought the issue was related to the security context not being passed into the Promise's runnable (or some such thing), and set springsecurity.sch.strategyName = "MODE_INHERITABLETHREADLOCAL" to no avail.
Here are some screenshots of showing the debugger:
1) This line in DispatcherServlet is not yet executed. Watch statement at bottom of image shows .getAuthentication != null returns true
2) Before SecurityContextHolder being cleared out in SecurityContextPersistenceFilter:
3) After returning from ha.handle, .getAuthentication() is now null
4) getAuthentication() is now null before rendering view/result
To clarify, I am attempting to access springSecurityService.currentUser from within a custom tag library that is rendering the header of my page in a layout.
So, in a layout.gsp type file:
<header id="header" class="md-whiteframe-1dp">
<g:renderHeader/></header>
with a renderHeader definition like:
def renderHeader = { attrs, body ->
SecUser currentUser = (SecUser) accountService.activeUser
log.info("About to render header, session.id=" + session.id +
(currentUser?.userLogLabel ?: " user=not_logged_in"))
out << render(template: "/header", model: [currentUser : currentUser])
}
I hit this same issue and managed to track it down. I am assuming that you are using the spring security core plugin. The root issue is that the plugin registers an application filter without DispatcherType.ASYNC. If you look at the Spring documentation, spring security supports async. To fix it I created this BeanPostProcessor and put it in my application context.
class SpringSecurityAsyncConfigurer implements BeanPostProcessor {
#Override
Object postProcessBeforeInitialization(final Object bean, final String beanName) throws BeansException {
if (bean in FilterRegistrationBean && "springSecurityFilterChainRegistrationBean".equals(beanName)) {
//for some unknown reason the plugin only has this run for request and error, adding async as the spring documentation
//says this is supported
bean.setDispatcherTypes(EnumSet.<DispatcherType>of(DispatcherType.REQUEST, DispatcherType.ERROR, DispatcherType.ASYNC))
}
bean
}
#Override
Object postProcessAfterInitialization(final Object bean, final String beanName) throws BeansException {
return bean
}
}
Are you doing this in controller or filter (and I mean 'filter' not 'interceptor')?
Because I can use it perfectly fine from a custom TokenFilter without issue.
This is why I argued heavily for moving communication away from business logic and higher up to filters and handler interceptors and to stop tying it to annotations and stuff. They keep running into these issues over and over.
I actually just released a faster version of Grails for API's that takes care of most of these communications issues yesterday for Universities

Error in Zuul SendErrorFilter during forward

When my Zuul Filter is unable to route to a configured URL, the 'RibbonRoutingFilter' class throws a ZuulException saying "Forwarding error" and the control goes to the 'SendErrorFilter' class.
Now when the SendErrorFilter class tries to do a forward, another exception happens during this forward call.
dispatcher.forward(ctx.getRequest(), ctx.getResponse());
The exception happening during this forward call is
Caused by: java.lang.IllegalArgumentException: UT010023: Request org.springframework.cloud.netflix.zuul.filters.pre.Servlet30WrapperFilter$Servlet30RequestWrapper#6dc974ea was not original or a wrapper
at io.undertow.servlet.spec.RequestDispatcherImpl.forward(RequestDispatcherImpl.java:103) ~[undertow-servlet-1.1.3.Final.jar:1.1.3.Final]
at org.springframework.cloud.netflix.zuul.filters.post.SendErrorFilter.run(SendErrorFilter.java:74) ~[spring-cloud-netflix-core-1.0.0.RELEASE.jar:1.0.0.RELEASE]
at com.netflix.zuul.ZuulFilter.runFilter(ZuulFilter.java:112) ~[zuul-core-1.0.28.jar:na]
at com.netflix.zuul.FilterProcessor.processZuulFilter(FilterProcessor.java:197) ~[zuul-core-1.0.28.jar:na]
Finally when the control comes to my custom ZuulErrorFilter , i do not get the original exception. Instead the exception object i get is the one that occurs during the forward.
Update:
I found that a errorPath property can be configured to point to a Error Handling Service. If it is not configured, Zuul by default looks for a service named /error and tries to dispatch to that service. Since we did not have any service for /error , the dispatcher.forward() was throwing error.
Question
How can we skip this fwd to an error handling service ? We have a ErrorFilter to log the error. We do not want to have a error handling service.
We had faced the same issue and there is a simple solution to fix the Undertow "eating" the original exception, following my blog post:
http://blog.jmnarloch.io/2015/09/16/spring-cloud-zuul-error-handling/
You need to set the flag allow-non-standard-wrappers to true. In Spring Boot this is doable through registering custom UndertowDeploymentInfoCustomizer. Example:
#Bean
public UndertowEmbeddedServletContainerFactory embeddedServletContainerFactory() {
UndertowEmbeddedServletContainerFactory factory = new UndertowEmbeddedServletContainerFactory();
factory.addDeploymentInfoCustomizers(new UndertowDeploymentInfoCustomizer() {
#Override
public void customize(DeploymentInfo deploymentInfo) {
deploymentInfo.setAllowNonStandardWrappers(true);
}
});
return factory;
}
Now regarding the question, either way I would highly encourage you to implement your own ErrorController, because otherwise you may experience odd Spring Boot behaviour (in our setup - ralying on the default was always generating the Whitelabel error page with 200 HTTP status code - which never happens on Tomcat in contradiction) and in this way was not consumable by AJAX calls for instance.
Related Github issue: https://github.com/spring-cloud/spring-cloud-netflix/issues/524

Spring Security Rest

I'm having a set of Sping Data Repositories which are all exposed over Rest by using Spring-data-rest project. Now I want to secure the HTTP, so that only registered users can access the http://localhost:8080/rest/ So for this purpose I add #Secured(value = { "ROLE_ADMIN" }) to all the repositories and I also enable the security by specifying the
#EnableGlobalMethodSecurity(securedEnabled = true, jsr250Enabled = true, prePostEnabled = true)
So now what happens is I go to the rest and it's all good - i'm asked to authenticate. Next thing I do is I go to my website (which uses all the repositories to access the database) but my request fails with
nested exception is org.springframework.security.authentication.AuthenticationCredentialsNotFoundException: An Authentication object was not found in the SecurityContext
which is correct because i'm browsing my website as anonymous user.
So my question is: is there a way to provide method authentication for the REST layer only? To me it sounds like a new annotation is needed (something like #EnableRestGlobalMethodSecurity or #EnableRestSecurity)
I don't know if this will solve your problem, however I managed to get something similar, working for me by creating an event handler for my specific repository, and then used the #PreAuthorize annotation to check for permissions, say on beforeCreate. For example:
#RepositoryEventHandler(Account.class)
public class AccountEventHandler {
private final Logger logger = LoggerFactory.getLogger(getClass());
#PreAuthorize("isAuthenticated() and (hasRole('ROLE_USER'))")
#HandleBeforeCreate
public void beforeAccountCreate(Account account) {
logger.debug(String.format("In before create for account '%s'", account.getName()));
}
#PreAuthorize("isAuthenticated() and (hasRole('ROLE_ADMIN'))")
#HandleBeforeSave
public void beforeAccountUpdate(Account account) {
logger.debug(String.format("In before update for account '%s'", account.getName()));
//Don't need to add anything to this method, the #PreAuthorize does the job.
}
}

How do you output the context class using log4net as a service?

I am using Log4Net as a service which is injected into other services using StructureMap.
How do I ensure the log file includes the calling service class context (class name and/or thread) which is making the log4net calls?
Surely the calling class or thread will always be the logging service which doesn't help me understand where the logging calls are really coming from.
EDIT:
Register code:
ObjectFactory.Initialize(x =>
{
x.For<ILog>().AlwaysUnique().Use(s => s.ParentType == null ?
LogManager.GetLogger(s.BuildStack.Current.ConcreteType) :
LogManager.GetLogger(s.ParentType));
});
Service layer:
public class LoggerService : ILoggerService
{
private readonly ILog log;
public LoggerService(ILog logger)
{
log = logger;
log.Info("Logger started {0}".With(logger.Logger.Name));
}
public void Info(string message)
{
log.Info(message);
}
}
In the logging, I am still always getting the LoggerService as the context so I'll never see what actually called the logger. It doesn't seem to be working correctly. I feel like I'm missing something here...
Edit 2:
I've added a pastie link for a console app here:
http://pastie.org/1897389
I would expect the parent class to be logged but it isn't working at the simplest of levels.
You might want to have a look at Castle Dynamic proxy in order to solve it using AOP. There is an example of using it with Structure Map on the Structure Map Google Group.
Ayende has an example of AOP based logging using Log4Net and Windsor.
I use StructureMap in a lot of the code I generate and I have a StructureMap registry which I use to hook the logger into the context of the class that it is injected into.
For Reference, I'm using the 2.6.2 version of StructureMap but should be fine with 2.5+ where the new .For<>().Use<>() format is utilized.
public class CommonsRegistry : Registry
{
public CommonsRegistry()
{
For<ILogger>().AlwaysUnique().Use(s => s.ParentType == null ? new Log4NetLogger(s.BuildStack.Current.ConcreteType) : new Log4NetLogger(s.ParentType.UnderlyingSystemType.Name));
XmlConfigurator.ConfigureAndWatch(new FileInfo(Path.Combine(Path.GetDirectoryName(Assembly.GetAssembly(GetType()).Location), "Log.config")));
}
}
What this registry is doing is for anywhere the ILogger is injected, use the class that it's injected into is where the logging messages are logged to/context of.
*Also, in the second line (XmlConfigurator.ConfigureAndWatch) is where I tell Log4Net to get the logging information from the file "Log.config" instead of the application configuration file, you may or may not like that and can be omitted.
The code I use is a common IOC.Startup routine where I would pass if I would like to use the default registery.
ObjectFactory.Initialize(x =>
{
x.AddRegistry<CommonsRegistry>();
...
}
This gives me the calling class name in the logging instance where messages are logged to automatically and all that is required is to inject the logger into the class.
class foo
{
private readonly ILogger _log;
public foo(ILogger log)
{
_log = log;
}
}
Now the messages are logged as context/class "foo".

Resources