Authentication of background tasks using Spring Boot and Spring Security - spring

I have a background task (running with Project Reactor, but I don't think it is relevant) that I need to run in with an authenticated user to get through some #PreAuthorize annotated methods.
I'm doing something like this:
Authentication authentication = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(login, password));
SecurityContextHolder.getContext().setAuthentication(authentication);
But when I trace into the authenticationManager call, I find that it is using Spring-Boot's default InMemoryUserDetailsService, rather than my custom authentication configuration. This happens regardless of whether I run the authentication in a web request thread, or in the background thread.
I don't know if it is relevant, but I am running this code in an integration test, with these annotations (among others):
#SpringApplicationConfiguration(classes=MyAppConfiguration.class)
#WebAppConfiguration
#IntegrationTest({"server.port:0"})
In addition to this problem, my test makes an authenticated web request to my server, and that authenticates just fine. So I know at least the web portion of my system is using the correct authenication configuration.
Here is my authentication configuration:
#EnableWebMvcSecurity
#EnableGlobalMethodSecurity(jsr250Enabled=true, prePostEnabled=true)
public abstract class BaseSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
public LocalUserDetailsService localUserDetailsService;
#Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(localUserDetailsService);
}
#Override
public void configure(HttpSecurity http) throws Exception {
http.csrf().disable().httpBasic()
.and()
.authorizeRequests()
.antMatchers( "/admin/**" ).hasRole( "ADMIN" )
}

It is hard to tell without your test implementatiton but it matters that you are running it in integration test
Maybe you are forgetting to add `FilterChainProxy to your mockMvc
like this mvc = MockMvcBuilders.webAppContextSetup(context)
.addFilter(springSecurityFilterChain).build();
instance of filterChainPrioxy can be #Autowired into your test class, of course this answer may not make sense, depends of your implementation of test class
---after your comment
this line :
SecurityContextHolder.getContext().setAuthentication(authentication);
assigns security constrains to current thread and does not impact threads running in background, unless your strategy is global and it is not default

Related

Unable to Remove ;jsessionid in a Spring Boot / Web Flow Application's URL when Deployed to Tomcat 8.5

I'm working on a Java application where a user registers a password for his/her account. The following are being used:
Spring Boot
Spring MVC
Spring Web Flow
Spring Security
Thymeleaf
Interceptor (for checking the session in the preHandle method)
For the Spring Security part, there's really no authentication required. I just use it to handle CSRF and the configuration is as follows:
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
// CSRF feature only
http.authorizeRequests().anyRequest().permitAll();
}
}
Now, this is where things get messy. When I deploy it to Tomcat in a Unix environment, ;jsessionid gets appended to the URL and Spring Security is not happy. I have scrounged the Internet and found the following solutions to remove it (alongside my results).
server.servlet.session.tracking-modes=cookie in application.properties does nothing.
web.xml
<session-config>
<tracking-mode>COOKIE</tracking-mode>
</session-config>
or
#Configuration
public class WebConfig implements WebApplicationInitializer {
#Override
public void onStartup(ServletContext servletContext) {
HashSet<SessionTrackingMode> set = new HashSet<>();
set.add(SessionTrackingMode.COOKIE);
servletContext.setSessionTrackingModes(set);
}
}
yields an IllegalArgumentException: The session tracking mode [COOKIE] requested for context [/<context-name>] is not supported by that context
I'm about to pull what remains of my hair off so I reverted any cookie-related changes and thought of just allowing semicolons in the URL (I know, I know, not secure) using the snippet below in the same SecurityConfig class.
#Bean
public HttpFirewall allowUrlSemicolonHttpFirewall() {
StrictHttpFirewall firewall = new StrictHttpFirewall();
firewall.setAllowSemicolon(true);
return firewall;
}
#Override
public void configure(WebSecurity web) throws Exception {
super.configure(web);
web.httpFirewall(allowUrlSemicolonHttpFirewall());
}
And voila! The web flow runs on an infinite redirect.
Questions:
Has anyone ever encountered IllegalArgumentException: The session tracking mode [COOKIE] requested for context [/<context-name>] is not supported by that context before? I've searched far and wide and the closest that I could find is this.
Could the reason behind server.servlet.session.tracking-modes=cookie not working be the same as above?
Could the infinite redirect be caused by http.authorizeRequests().anyRequest().permitAll()? I tried using anonymous() but the result was the same.
Is it possible to know which part exactly is causing the infinite redirect?
Please note that allowing semicolons in the URL is working fine and dandy in my localhost, so I have a hunch that what's causing the redirects is SSL-related. In the same way that locally ;jsessionid is not being appended to the URL.
My next step is to try configuring SSL locally in an attempt to replicate the issue. In the meantime, any help would be highly appreciated. My apologies if there's too much information here; I'm willing to repost it as multiple questions if that's necessary.

Spring Security Basic Auth Password Rotation Issue

Experts,,
I have a spring boot 2.5.5 application(embedded tomcat) where I have to configure the basic auth.
This is the class I have that does the work for me
#Component
#EnableWebSecurity
public class ApplicationSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.anyRequest()
.authenticated()
.and()
.httpBasic();
}
}
The issue is I just need to enter the user/password once in the browser and it works for any subsequent request. Furthermore, I don't need to supply the new username/password after the server restarts which is driving me crazy - the app still works and I can access my APIs/pages.
Even if i assume the browser is somehow saving the username and password it should not work once the server is restarted as the password gets changed - isnt it ?
Update II:
Following the advice from M. Deinum I made the session stateless and it worked. I then went on to implement Basic Auth with InMemoryUserDetailsManager and added the below code and we are back to the same issue again. The credentials seem to be again stored in session and I need not pass them for the subsequent request.
#Autowired
public ApplicationSecurityConfig(PasswordEncoder passwordEncoder) {
this.passwordEncoder = passwordEncoder;
}
#Override
#Bean
protected UserDetailsService userDetailsService() {
UserDetails user = User
.builder()
.username("admin")
.password(passwordEncoder.encode("admin"))
.roles("ADMINISTRATOR")
.build();
return new InMemoryUserDetailsManager(user);
}
This is how I would expect it to work with your current configuration.
When successfully authenticated with basic authentication the browser will send the username/password for all other subsequent requests. So this is as expected.
Another thing is that, by default, Spring Security will use the HTTP Session to store the user information. A session-cookie is also sent with each request so that the session state can be restored for each request.
This session state is, by default for your servlet container, saved to disc when you stop the server, when you restart and the session is still valid (not timed out) it will still have the authentication.
You can fix this by making Spring Security not use a session (set the session mode to stateless).
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
The drawback of this is that it will re-authenticate each request (which takes some time and thus impacts your performance slightly). But it should give an error after restart now, as you changed the password.

Keep session id in case of presence of special parameter in request. Spring Security

Does anybody know if there any way to configure Spring Security in the way that it doesn't change session id if there is some parameter in the request.
For example:
somesite.com/home.jsp?password=encrypted- change session id after
authentication
somesite.com/home.jsp?password=encrypted& keepsessionid - don't
change session id after authentication
I was thinking about filter chain, maybe removing conditionally SessionManagementFilter, but not sure if this is a proper way, and even if this will be working
For someone with the same question. I found the answer. Different session management strategy can be achieved by using multiple http security configuration (inner classes of main security classes). The special case http security configurer should be adjusted to some special request
#Configuration
#Order(1)
public class SpecialCaseSessionHandlingConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.requestMatcher(request -> Check1(request))
.authorizeRequests()
.anyRequest().authenticated();
}
}

My heroku app is requesting a password that I did not put there

I'm new to the process of sending an application to production and I'm using Heroku free plan to test. Today I went to check my app and the API I made using Spring boot is not working and is requesting a login that I didn't do. My app address is https://erik-financial-api.herokuapp.com and when you go there it redirects you to the address https://erik-financial-api.herokuapp.com/login with the following:
I did not make this page and none of the passwords (from my app or from my Heroku account) work on it. This was supposed to be just a REST API for another front-end app. Does anyone know why is this happening?
The code for this project can be found on my GitHub on https://github.com/esscheffer/financial-api
Edit: this seems to be a default spring security login page. I have searched for solutions, but none worked so far. What I have tried:
Add
override fun configure(security: HttpSecurity) {
security.httpBasic().disable()
.formLogin().disable()
}
to my WebSecurityConfigurerAdapter class.
Add http.httpBasic().disable().formLogin().disable() to the configure of my ResourceServerConfigurerAdapter class.
Add (exclude = [SecurityAutoConfiguration::class]) to the #SpringBootApplication sanitation on my application class.
The first 2 tries didn't remove the login page and the last one broke the app, returning 404 for all pages. Note that this only happens when I deploy my application to Heroku. When running locally I don't have this login page or any other problem.
Add a new configuration class com.scheffer.erik.financial.api.config.SecurityConfig, where in the configure method you can disable the HTTP Basic authentication as well as login form based authentication, like below:
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity security) throws Exception {
security
.httpBasic().disable()
.formLogin().disable();
}
}
Do it like this...permit all requests for the home page...I hope it will work for you.
#Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().and().csrf().disable().
authorizeRequests()
.antMatchers("/").permitAll() //OR .antMatchers("/**").permitAll()
.anyRequest().authenticated();
}

How to reload the Configure method of WebSecurityConfigurerAdapter when the application is up and running

I am using spring boot and in spring security we are using "WebSecurityConfigurerAdapter" and using the method
#Override
protected void configure(HttpSecurity http) throws Exception {
AuthenticationFilter authenticationFilter = new AuthenticationFilter(authenticationManager(), tokenService(), externalServiceAuthenticator());
http.addFilterBefore(authenticationFilter, BasicAuthenticationFilter.class)
.csrf().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and().authorizeRequests().antMatchers(externalServiceCaller.getPrivateEndPoints())
.hasAnyAuthority(externalServiceCaller.getAllAuthorities()).anyRequest().authenticated()
.and().authorizeRequests().anyRequest().anonymous()
.and().exceptionHandling().authenticationEntryPoint(unauthorizedEntryPoint())
.and().exceptionHandling().authenticationEntryPoint(forbiddenEntryPoint());
}
This is working fine for existing role and user but when we are adding more users and role at run time (After the application start) then spring security is not able to recognize new role and new user . Is there any way to call the above method again when the application is up and running.
Reload configure(HttpSecurity http) is impossible in runtime, because it's some kind of builder and it's creates some part of the spring security chain when the application is starting - if you'd like to reload the method you have to replace the spring security chain during runtime - it's not so easy and recommended way.
If you need add some user during runtime - implement custom AuthentificationProvider

Resources