How to integrate waffle NegotiateSecurityFilter spring-security with sparkjava embedded jetty? - spring-security

Our application is using sparkjava http://sparkjava.com/ as the REST framework. The jetty server is embedded in the application (sparkjava default). We are also using spring for dependency injection.
For providing AD authentication, we need to integrate the waffle's NegotiateSecurityFilter.
As per waffle documentation and several online resources including stackoverflow, a DelegatingFilterProxy is required with the name springSecurityFilterChain.
But since we are not using spring MVC, I have to add it as follows:
ServletContextHandler sparkContext = new ServletContextHandler(ServletContextHandler.SESSIONS);
sparkContext.addFilter(new FilterHolder( new DelegatingFilterProxy( "springSecurityFilterChain" ) ),"/*", EnumSet.allOf( DispatcherType.class ));
And since a ContextLoaderListener does not already exist, need to add in the following manner:
sparkContext.addEventListener( new ContextLoaderListener() );
But it gives the error "Cannot initialize context because there is already a root application context present - check whether you have multiple ContextLoader" at time of server startup.
Please let me know a solution in this scenario if you have successfully integrated spring-security DelegatingFilterProxy with embedded jetty and sparkjava (without using spring MVC).

This is how I achieved it finally:
In the main method where I have access to the sparkContext:
ServletContextHandler sparkContext = new ServletContextHandler(ServletContextHandler.SESSIONS);
sparkContext.setContextPath("/");
sparkContext.addServlet(DefaultServlet.class, "/*");
addSPNEGOFilter(sparkContext);
And the implementing methods are as:
private void addSPNEGOFilter(ServletContextHandler sparkContext) {
final ServletHandler handler = new ServletHandler();
final FilterHolder fh = handler.addFilterWithMapping(NegotiateSecurityFilter.class, <SPNEGO_FILTER_PATH>,
EnumSet.allOf(DispatcherType.class));
setNegotiateFilterParams(fh);
sparkContext.addFilter(fh, <SPNEGO_FILTER_PATH>, EnumSet.allOf(DispatcherType.class));
}
Add the following properties to the holder:
private static void setNegotiateFilterParams(final FilterHolder fh) {
fh.setInitParameter("principalFormat", "fqn");
fh.setInitParameter("roleFormat", "both");
fh.setInitParameter("allowGuestLogin", "false");
fh.setInitParameter("impersonate", "false");
fh.setInitParameter("securityFilterProviders",
"waffle.servlet.spi.NegotiateSecurityFilterProvider");
fh.setInitParameter("waffle.servlet.spi.NegotiateSecurityFilterProvider/protocols", "Negotiate");
}

Related

How to configure gemfire as message store for aggregator module in spring cloud data flow

Link - https://github.com/spring-cloud-stream-app-starters/aggregator/tree/master/spring-cloud-starter-stream-processor-aggregator does not list property for gemfire message store
The GemfireMessageStore is configured like this:
#ConditionalOnClass(GemfireMessageStore.class)
#ConditionalOnProperty(prefix = AggregatorProperties.PREFIX,
name = "message-store-type",
havingValue = AggregatorProperties.MessageStoreType.GEMFIRE)
#Import(ClientCacheAutoConfiguration.class)
static class Gemfire {
#Bean
#ConditionalOnMissingBean
public ClientRegionFactoryBean<?, ?> gemfireRegion(GemFireCache cache, AggregatorProperties properties) {
ClientRegionFactoryBean<?, ?> clientRegionFactoryBean = new ClientRegionFactoryBean<>();
clientRegionFactoryBean.setCache(cache);
clientRegionFactoryBean.setName(properties.getMessageStoreEntity());
return clientRegionFactoryBean;
}
#Bean
public MessageGroupStore messageStore(Region<Object, Object> region) {
return new GemfireMessageStore(region);
}
}
The point is that you always can override that ClientRegionFactoryBean with your own.
Or you can take into account that ClientCacheAutoConfiguration is based on the #ClientCacheApplication, which, in turn, allows you to have a ClientCacheConfigurer bean and provide whatever is sufficient for your client cache configuration. Including config and pool. That's right: it is not on the app starter configuration level and you have to right some custom code to be included as a dependency into the final uber jar for target binder-specific application.
More info how to build them is here in Docs: https://docs.spring.io/spring-cloud-stream-app-starters/docs/Einstein.RC1/reference/htmlsingle/#_patching_pre_built_applications
A variety of backend storage options exist through Spring Integration. You can read more about it in spring-cloud-starter-stream-processor-aggregator/README.
Spring Integration docs on this matter are included as a link, and the Gemfire section could be useful.
It'd be also useful to review MessageGroupStore implementation, since it is the foundation for the storage option in aggregator.

Changing the Order of the Spring Security WebFilter

Changing the Order of the Spring Security WebFilter
I have an API Gateway implemented using Spring Cloud Gateway that uses Spring Security. Spring Security for WebFlux is implemented as a WebFilter right at the beginning of the filter chain. So after successful authentication the request would be forwarded to Spring Cloud Gateway's RoutePredicateHandlerMapping, which would try to deduce the destination based on the URL pattern, and then it would go to a FilteringWebHandler to execute the other filters of Spring Cloud Gateway.
My problem is the following: I have implemented a customized authentication algorithm which uses query string and header variables as credentials for authentication according to the requirements of the project, an this is working without any problem. The problem occurred when we needed to add a small customization for the authentication algorithm that is path independent. When the request reaches the WebFilter of Spring Security, pattern matching is not yet done so I do not know which application does it point to, for example:
app1:
-Path: /app1/**
app2:
-Path: /app2/**
Which means that instead of having authentication -> route mapping -> filtering web handler I should do route mapping -> authentication -> filtering web handler. Not that these three components are not similar, one of them is a filter another is a mapper and the last one is web handler. Now I know how to customize them but the problem is that I do not know how to intercept the Netty server building process in order to change the order of these operations. I need to wait for the building process to end and alter the content of the server before it starts. How can I do that?
EDIT: here is the final solution:
So here is how I did it:
Goal: removing the WebFilter of Spring Security from the default HttpHandler, and inserting it between RoutePredicateRouteMapping and the FilteringWebHandler of Spring Cloud Gateway
Why: Because I need to know the Application ID while carrying on my customized authentication process. This Application ID is attached to the request by the RoutePredicateRouteMapping by matching the request's URL to a predefined list.
How did I do it:
1- Removing the WebFilter of Spring Security
I created an HttpHandler bean that invokes the default WebHttpHandlerBuilder and then customize the filters. As a bonus, I removed unneeded filters in order to increase the performance of my API Gateway
#Bean
public HttpHandler httpHandler() {
WebHttpHandlerBuilder webHttpHandlerBuilder = WebHttpHandlerBuilder.applicationContext(this.applicationContext);
MyAuthenticationHandlerAdapter myAuthenticationHandlerAdapter = this.applicationContext.getBean(MY_AUTHENTICATED_HANDLER_BEAN_NAME, MyAuthenticationHandlerAdapter.class);
webHttpHandlerBuilder
.filters(filters ->
myAuthenticationHandlerAdapter.setSecurityFilter(
Collections.singletonList(filters.stream().filter(f -> f instanceof WebFilterChainProxy).map(f -> (WebFilterChainProxy) f).findFirst().orElse(null))
)
);
return webHttpHandlerBuilder.filters(filters -> filters
.removeIf(f -> f instanceof WebFilterChainProxy || f instanceof WeightCalculatorWebFilter || f instanceof OrderedHiddenHttpMethodFilter))
.build();
}
2- Wrapping Spring Cloud Gateway's FilteringWebHandler with Spring Web's FilteringWebHandler with the added WebFilter
I created my own HandlerAdapter which would match against Spring Cloud Gateway's FilteringWebHandler and wrap it with Spring Web's FilteringWebHandler plus the security filter I extracted in the first step
#Bean
public MyAuthenticationHandlerAdapter myAuthenticationHandlerAdapter() {
return new MyAuthenticationHandlerAdapter();
}
public class MyAuthenticationHandlerAdapter implements HandlerAdapter {
#Setter
private List<WebFilter> securityFilter = new ArrayList<>();
#Override
public boolean supports(Object handler) {
return handler instanceof FilteringWebHandler;
}
#Override
public Mono<HandlerResult> handle(ServerWebExchange exchange, Object handler) {
org.springframework.web.server.handler.FilteringWebHandler filteringWebHandler = new org.springframework.web.server.handler.FilteringWebHandler((WebHandler) handler, securityFilter);
Mono<Void> mono = filteringWebHandler.handle(exchange);
return mono.then(Mono.empty());
}
}
This way I could achieve better performance with highly customized HttpHandler pipeline that I suppose to be future-proof
END EDIT
Spring Security for WebFlux is implemented as a WebFilter which is executed almost as soon as a request is received. I have implemented custom authentication converter and authentication manager which would extract some variables from the header and URL and use them for authentication. This is working without any problem.
Now I needed to add another variable taken from RoutePredicateRouteMapping before authentication is done. What I want exactly is to remove the WebFilter (called WebFilterChainProxy) from its current position and put it between the RoutePredicateRouteMapping and the FilteringWeHandler.
Here is how the default process goes:
ChannelOperations calls ReactorHttpHandlerAdapter which calls HttpWebHandlerAdapter, ExceptionHandlingWebHandler, and then org.springframework.web.server.handler.FilterWebHandler.
This WebHandler would invoke its filters and then call the DispatchHandler. One of those filters is the WebFilterChainProxy that does the authentication for Spring Security. So first step is removing the filter from here.
Now the DispatchHandler which is called after the filters would invoke RoutePredicateHandlerMapping, which would analyze the routes and give me the route ID that I need, and then it would call the org.springframework.cloud.gateway.handler.FilteringHandler (this is not the same FilteringHandler above), and that in turn would call the other filters of the Spring Cloud Gateway. What I want here is to invoke the filter after RoutePredicatehandlerMapping and before org.springframework.cloud.gateway.handler.FilteringHandler.
What I ended doing was the following:
I created and WebHttpHandlerBuilder that would remove WebFilterChainProxy and pass it as a parameter to a customized DispatcherHandler. Now that the filter is removed the request would pass the first layers without requiring authentication. In my customized DispatcherHandler I would invoke the RoutePredicateHandlerMapping and then pass the exchange variable to the WebFilterChainProxy to do the authentication before passing it to the org.springframework.cloud.gateway.handler.FilteringHandler, which worked perfectly!
I still think that I'm over engineering it and I hope that there is a way to do it using annotations and configuration beans instead of all these customized classes (WebHttpHandlerBuilder and DispatcherHandler).
You should probably implement that security filter as a proper GatewayFilter, since only those are aware of the other GatewayFilter instances and can be ordered accordingly. In your case, you probably want to order it after the routing one.
Also, please don't cross-post, the Spring team is actively monitoring StackOverflow.
I had a similar problem. The accepted solution, while interesting, was a bit drastic for me. I was able to make it work simply by adding my custom filter before SecurityWebFiltersOrder.AUTHENTICATION in the security configuration. This is similar to what I've done with success in a regular Spring mvc application.
Here's an example using oauth authentication. tokenIntrospector is my custom introspector, and requestInitializationFilter is the filter that grabs the tenant id and stashes it in the context.
#AllArgsConstructor
#Configuration
#EnableWebFluxSecurity
public class WebApiGatewaySecurityConfiguration {
private final GatewayTokenIntrospector tokenIntrospector;
private final GatewayRequestInitializationFilter requestInitializationFilter;
#Bean
public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
// #formatter:off
http
.formLogin().disable()
.csrf().disable()
.oauth2ResourceServer(oauth2ResourceServer ->
oauth2ResourceServer.opaqueToken(c -> c.introspector(tokenIntrospector)))
.addFilterBefore(requestInitializationFilter, SecurityWebFiltersOrder.AUTHENTICATION);
return http.build();
// #formatter:on
}
}

Azure RoleEnvironment.Changing event not being called in ASP.NET MVC 5

I am trying to use the Azure Runtime Reconfiguration Pattern to allow me to change a appSetting in the normal Web.config file via PowerShell (later by Microsoft Azure Web Sites Management Library).
My problem is that the RoleEnvironment.Changing event is not being called in my MVC app, so the web app is being restarted. I have placed event set up code in the MVC Application_Start as described in the Azure article, i.e.
protected void Application_Start()
{
RoleEnvironment.Changing += RoleEnvironment_Changing;
RoleEnvironment.Changed += RoleEnvironment_Changed;
//normal MVC code etc...
AreaRegistration.RegisterAllAreas();
}
The event handlers are a straight copy of the handled from the Azure article and look like this:
private const string CustomSettingName = "TestConfig";
public static string TestConfigValue;
private static void RoleEnvironment_Changing(object sender,
RoleEnvironmentChangingEventArgs e)
{
RoleLogs.Add("RoleEnvironment_Changing: started");
var changedSettings = e.Changes.OfType<RoleEnvironmentConfigurationSettingChange>()
.Select(c => c.ConfigurationSettingName).ToList();
Trace.TraceInformation("Changing notification. Settings being changed: "
+ string.Join(", ", changedSettings));
if (changedSettings
.Any(settingName => !string.Equals(settingName, CustomSettingName,
StringComparison.Ordinal)))
{
Console.WriteLine("Cancelling dynamic configuration change (restarting).");
RoleLogs.Add("RoleEnvironment_Changing: restarting!");
// Setting this to true will restart the role gracefully. If Cancel is not
// set to true, and the change is not handled by the application, the
// application will not use the new value until it is restarted (either
// manually or for some other reason).
e.Cancel = true;
}
else
{
RoleLogs.Add("RoleEnvironment_Changing: change is OK. Not restarting");
Console.WriteLine("Handling configuration change without restarting. ");
}
}
private static void RoleEnvironment_Changed(object sender,
RoleEnvironmentChangedEventArgs e)
{
RoleLogs.Add("RoleEnvironment_ChangED: Starting");
Console.WriteLine("Updating instance with new configuration settings.");
foreach (var settingChange in
e.Changes.OfType<RoleEnvironmentConfigurationSettingChange>())
{
if (string.Equals(settingChange.ConfigurationSettingName,
CustomSettingName,
StringComparison.Ordinal))
{
// Execute a function to update the configuration of the component.
RoleLogs.Add("RoleEnvironment_ChangED: TestConfig has changed");
Console.WriteLine("TestConfig has changed.");
TestConfigValue = RoleEnvironment.GetConfigurationSettingValue(CustomSettingName);
}
}
}
I have added logs which prove that my RoleEnvironment_Changing and RoleEnvironment_Changed are not being called in the MVC WebApp which means the WebApp is restarted when I change an appSetting via PowerShell. This also means the RoleEnvironment.Changing event never gets to the WebJob.
I am using Azure SDK 2.7.0
Any ideas?
UPDATE
#richag gave me an answer, which made me realise that my problem is because I am using a App Service rather than a Cloud Service. This SO answer and plus this video (see at 5:00mins) talks about the difference (Note: the video is old so the name of the web app is different, but the concept is the same).
I don't really want to change this late in the development, and I have worked round the problem another way. Maybe on the next project and will look at Cloud Services as I can see some positives, like better control of my WebJobs configuration.
From the runtime reconfiguration pattern: "Microsoft Azure Cloud Services roles detect and expose two events that are raised when the hosting environment detects a change to the ServiceConfiguration.cscfg files" These events are not fired if you make changes to app.config/web.config files. Only when the cloud service configuration is changed, i.e. if you upload a new configuration file through the azure portal's configure tab or change a setting directly on the azure portal.
According to the debugger, none of the following events are fired when I update the Azure Portal to change an AppSetting for an ASP.NET WebAPI app:
RoleEnvironment.Changing
RoleEnvironment.Changed
RoleEnvironment.StatusCheck
RoleEnvironment.SimultaneousChanging
RoleEnvironment.SimultaneousChanged
RoleEnvironment.Stopping
Do others have different experience?

Grails: Grails3 : doWithWebDescriptor?

I am trying to get further into grails3 and I am unsure about plugin descriptor and doWithWebDescriptor:
src/main/groovy/grails/plugin/plugin/PluginGrailsPlugin.groovy
def doWithWebDescriptor = { xml ->
def listenerNode = xml.'listener'
listenerNode[listenerNode.size() - 1] + {
listener {
'listener-class'(someClass.name)
}
}
}
I tried grails install-templates under grails 3 and no web.xml was generated... I also had a look at the default generated plugin descriptor which did not appear to have doWithWebDescriptor...
Was wondering if this has changed - is it no longer producing a web.xml or if it is what should I be doing to register a listener under grails 3 .
I have managed to get default tomcat websocket listener to work via a spring boot grails app:
It is documented here:
https://github.com/vahidhedayati/testwebsocket-grails3
I have decided to update this post and include all my findings so far on this matter.
More specifically the application.groovy inside your application grails-app/init folder:
This bean initiates default tomcat websocket listener:
#Bean
public ServletListenerRegistrationBean<AnotherWebSocketHandler> httpSessionEventPublisher() {
return new ServletListenerRegistrationBean<AnotherWebSocketHandler>(new AnotherWebSocketHandler());
}
Whilst messing around to reuse in plugin, the findings are:
The above project is a basic grails application which does 2 things, a basic spring socket as well java 1.X Websocket:
Here is how to use Default websocket in a grails 3 plugin
In you plugin descriptor you have something like this:
Closure doWithSpring() {
{->
wsChatConfig DefaultWsChatConfig
}
}
In this plugin I have left both methods of initiating the listener:
#Bean
public ServletContextInitializer myInitializer() {
return new ServletContextInitializer() {
#Override
public void onStartup(ServletContext servletContext) throws ServletException {
servletContext.addListener(WsCamEndpoint)
servletContext.addListener(WsChatFileEndpoint)
}
}
}
// Alternative way
#Bean
public ServletListenerRegistrationBean<WsChatEndpoint> httpSessionEventPublisher() {
return new ServletListenerRegistrationBean<WsChatEndpoint>(new WsChatEndpoint())
}
The top method came in very handy since you can only initialise 1 ServletListenerRegistrationBean and I had to resort to the top method to enable other listeners... I could have just used the top primary for all the calls. Left in for future reference..
With this in place, spring boot now emulates the same as web.xml would when registering a listener. The actual groovy classes that load the websockets from there are as they were i.e. using default websocket calls such as onOpen onMessage etc..
From Grails 3 there's a new way of adding runtime configuration using spring registration beans in the Plugin method doWithSpring. doWithWebDescriptor is not used anymore.
This should work for the Servlet Listeners:
Closure doWithSpring() {{ ->
MyListener(ServletListenerRegistrationBean) {
listener = bean(someClass)
order = Ordered.HIGHEST_PRECEDENCE
}
}}
Disclaimer: I didn't test this code.
Refer to the Gails documentation.

SpringVaadinServlet blocking SpringBoot features

In our Muhuru-Bay-Microgrid-Dashboad project we're using code from https://github.com/xpoft/spring-vaadin in an attempt to get Spring Boot and Vaadin to play nicely. The problem - with this approach we can't access many of the other rest service Spring Boot registers at startup such as
/configprops
/health
/dump
/info
/trace
/mappings
/error
/autoconfig
Our startup code looks like:
#Bean
public ServletRegistrationBean servletRegistrationBean() {
final ServletRegistrationBean servletRegistrationBean
= new ServletRegistrationBean(
new ru.xpoft.vaadin.SpringVaadinServlet(),
"/*", "/VAADIN/*");
return servletRegistrationBean;
}
When we try to access Spring Boot's registered REST services we get redirected to /error - which also doesn't work correctly. Any hints greatly appreciated.
Try to use this addon to integrate Spring Boot and Vaadin:
https://github.com/peholmst/vaadin4spring
It's still in beta, but in my opinion it works much better than the Xpoft addon.
Using https://github.com/peholmst/vaadin4spring with Spring Boot, I had the same problem of getting HTTP 404 when accessing the application's other REST services.
What worked for me was to set VaadinServletConfiguration.SERVLET_URL_MAPPING_PARAMETER_NAME in the spring environment to send the Vaadin UI to a different context path (/ui/*):
#SpringBootApplication
public class AppSpringConfig {
public static void main(String[] args) {
new SpringApplicationBuilder(AppSpringConfig.class).initializers(new ApplicationContextInitializer<ConfigurableApplicationContext>() {
public void initialize(ConfigurableApplicationContext applicationContext)
{
ConfigurableEnvironment appEnvironment = applicationContext.getEnvironment();
Properties props = new Properties();
props.put(VaadinServletConfiguration.SERVLET_URL_MAPPING_PARAMETER_NAME, "/ui/*");
PropertySource< ? > source = new PropertiesPropertySource("vaadin", props);
appEnvironment.getPropertySources().addFirst(source);
}
}).run(args);
}
}

Resources