Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Suggestion] Implement Simple WebSecurity for ff4j-console using springboot #371

Closed
neillfontes-sl opened this issue Aug 12, 2019 · 9 comments

Comments

@neillfontes-sl
Copy link

neillfontes-sl commented Aug 12, 2019

After having a look at the documentation and doing some investigation with Spring Security I was able to come up with a very simple Authentication pattern for the WebUI. It consists of the following:

1- Add the following dependency to the pom.xml

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>

2- Add the following annotation to your SpringApplication:

@SpringBootApplication(exclude = { SecurityAutoConfiguration.class })

3- Add this configuration Bean to your project

import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.PasswordEncoder;

@Configuration
@EnableWebSecurity
public class BasicConfiguration extends WebSecurityConfigurerAdapter {

	@Override
	protected void configure(AuthenticationManagerBuilder auth)
			throws Exception {
		PasswordEncoder encoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();
		auth
				.inMemoryAuthentication()
				.withUser("admin")
				.password(encoder.encode("klapaucius"))
				.roles("ADMIN");
	}

	@Override
	protected void configure(HttpSecurity http) throws Exception {
		http
				.authorizeRequests()
				.anyRequest()
				.authenticated()
				.and()
				.httpBasic();
	}
}

And pronto! If you want to access the http://localhost:8080/ff4j-console URL to manage the toggles you will be required to login using the credentials set in the BasicConfiguration class.

For the sake of versioning, these are the dependencies in the pom.xml.

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.1.7.RELEASE</version>
    <relativePath/>
</parent>
...
<dependency>
    <groupId>org.ff4j</groupId>
    <artifactId>ff4j-spring-boot-starter</artifactId>
    <version>1.8</version>
</dependency>
<dependency>
    <groupId>org.ff4j</groupId>
    <artifactId>ff4j-web</artifactId>
    <version>1.8</version>
</dependency>

It is not great but at least allows one to secure the gui with minimal configuration efforts and to not fiddle a lot around.

Yes, one should never commit a password in a codebase, but this at least gives some guidance on how to extend it further, by e.g. customizing the AuthenticationManagerBuilder and adding a third-party storage for a credentials.

If it is useful, adding this to the documentation or a sample might help.

Disclaimer: If did not test if this affects clients fetching the toggles via REST. Also, would be nice to have the logged in user flushed to the ff4j_event table if you are using the Audit feature. This way one would know which user handle made a given change. Of course, this would imply in changes to the embedded ff4j.

@drizztguen77
Copy link
Contributor

I have another way I did it at work using the application.properties to configure the username and password (which we then set from our deployment software). I'll try to explain it tonight when I get home from work and add it to the Spring example code. Most of it is already there anyway.

@clun
Copy link
Collaborator

clun commented Sep 2, 2019

Thank you for this work. Will include security in next release for sure.

@clun
Copy link
Collaborator

clun commented May 2, 2020

I am working on a sample ATM.

Spring Security with some static user list (user1, user2, user3):

  • Secure the console to only ADMIN role
  • Allow toggle with AuthenticationManger

Should be ready this week as covering multiple issues

@clun clun self-assigned this May 2, 2020
@clun
Copy link
Collaborator

clun commented May 3, 2020

FYI I have created a sample following your idea @neillfontes-sl here : https://github.com/ff4j/ff4j-samples/tree/master/spring-boot-2x/ff4j-sample-secured-console

Would make sense to make that part of the starter. Also some dedicated properties in application.yaml to enable or not the security.

@neillfontes-sl
Copy link
Author

Thanks for the update @clun. Good to see that!

On our use-case here we implemented the user/pass combination via environment variables that are set in the helm charts (helm-charts > container > environment variables > springboot app). This allows to decouple the username/password from ff4j's codebase and the value passing happens on the CD, which is working great for us.

@clun
Copy link
Collaborator

clun commented May 4, 2020

But the application.yaml should be in an external volume already no ? Nevermind:

In one of my project I also implement this small logic

if (env var exist) {
 use it
} else if (key in yaml) {
  use it
} else {
 do not secure
}

@neillfontes-sl
Copy link
Author

I have it like:

application.properties

ui.username=${UI_USERNAME}
ui.password=${UI_PASSWORD}

And the configuration bean:

@Configuration
@EnableWebSecurity
public class FF4jSecurityConfig extends WebSecurityConfigurerAdapter {

	@Autowired
	private FF4jBasicAuthenticationEntryPoint authenticationEntryPoint;

	@Value("${ui.username}")
	private String uiUsername;

	@Value("${ui.password}")
	private String uiPassword;

	@Override
	protected void configure(AuthenticationManagerBuilder auth) throws Exception {

		PasswordEncoder encoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();
		auth
				.inMemoryAuthentication()
				.withUser(uiUsername)
				.password(encoder.encode(uiPassword))
				.roles("ADMIN");

	}

	@Override
	protected void configure(HttpSecurity http) throws Exception {

		http.csrf().disable();

		http
				.httpBasic()
				.authenticationEntryPoint(authenticationEntryPoint)
				.and()
				.exceptionHandling()
				.and().csrf().disable()
				.authorizeRequests()
				.antMatchers("/**").hasRole("ADMIN")
				.antMatchers("/").permitAll()
				.anyRequest().authenticated();

		http.addFilterAfter(new CustomFilter(), BasicAuthenticationFilter.class);

	}

	@Bean
	public SimpleUrlAuthenticationFailureHandler getMyFailureHandler() {
		return new SimpleUrlAuthenticationFailureHandler();
	}

	public class CustomFilter extends GenericFilterBean {

		@Override
		public void doFilter(
				ServletRequest request,
				ServletResponse response,
				FilterChain chain) throws IOException, ServletException {
			chain.doFilter(request, response);
		}
	}

}

@clun clun added evolution and removed in-progress labels May 5, 2020
@clun
Copy link
Collaborator

clun commented May 6, 2020

Thks, the part I needed was. Will create a set of env variables

ui.username=${UI_USERNAME:defaultUserName}
ui.password=${UI_PASSWORD:defaulPassword}

@clun
Copy link
Collaborator

clun commented May 9, 2020

Will may close this ticket with 1.8.6 with now a dedicated ticket open within the spring-boot starter
see: ff4j/ff4j-spring-boot-starter-parent#106

In either case, a working sample here :
https://github.com/ff4j/ff4j-samples/tree/master/spring-boot-2x/ff4j-sample-secured-console

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants