Vaadin 21 Migration to View-Based Access Control - RolesAllowed not working - spring-boot

This is a follow up question to this question.
I migrated my Vaadin 20 application to 21 to use view-based access control. The Annotations #PermitAll and #AnonymousAllowed are working fine. However when I try to restrict a route to a specific user role with #RolesAllowed I can't get access to this site (being logged in with a user who has this role).
Is there some special code required to get Vaadin to recognize the roles of my authenticated user?
Role restricted page:
#Component
#Route(value = "admin", layout = MainLayout.class, absolute = true)
#RolesAllowed("admin")
#UIScope
public class AdminView ...
SecurityConfig
#EnableWebSecurity
#Configuration
public class SecurityConfiguration extends VaadinWebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
super.configure(http);
setLoginView(http, LoginView.class, "/login");
}
#Autowired
private UserDetailsService userDetailsService;
#Autowired
private PasswordEncoder passwordEncoder;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
super.configure(auth);
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder);
}
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Override
public void configure(WebSecurity web) throws Exception {
super.configure(web);
web.ignoring().antMatchers("/images/**");
}
}

The roles you pass into #RolesAllowed are case-sensitive and should match the roles you have in Spring Security. Most likely in your case, you want to use #RolesAllowed({"ROLE_ADMIN"}). You can read more in the docs here https://vaadin.com/docs/v21/flow/integrations/spring/view-based-access-control/#annotating-the-view-classes

After a lot of debugging, I found the problem, the implementation of the getAuthorities() Function in my implementation of UserDetails.java was incorrect. A working dummy version with one role looks something like this:
#Override
#JsonIgnore
public Collection<? extends GrantedAuthority> getAuthorities() {
return List.of( new SimpleGrantedAuthority("ROLE_" + "admin"));
}
Important was to add "ROLE_" in front of the actual role name. Then I can use #RolesAllowed("admin") in the view class.

Related

Spring Security Permitting even unauthenticated user? whats wrong with this code?

I doing a simple poc of spring security but am running in to issues.
After extending WebSecurityConfigurerAdapter and implementing own UserDetailsService, spring security is permitting all users even without authentication. Please point the flaw in below code
...
#EnableWebSecurity
public class MyWebSecurity extends WebSecurityConfigurerAdapter {
#Autowired
UserDetailsService myUserDtlSrv;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(myUserDtlSrv);
}
#Bean
public PasswordEncoder passwordEncoder() {
return NoOpPasswordEncoder.getInstance();
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
}
}
my implementation of UserDetailsService
...
#Service
public class MyUserDtlSrv implements UserDetailsService {
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
return new User("a", "a", Arrays.asList());
}
}
Does not spring security secure every url (resource by default)?
You need to specify what requests are authorized. The following fragment
authorizes authenticated (by form login) requests.
#Override
protected void configure(HttpSecurity http) throws Exception {
http.antMatcher("/**")
.csrf(t -> t.disable())
.authorizeRequests(t -> t.anyRequest().authenticated())
.formLogin(Customizer.withDefaults());
}
}

Add users dynamically in Spring Java

This code I use to add users from my database that can authenticate but the problem this code is executed once , I want to have users that register how can I achieve that ?
I have this solution How to adding new user to Spring Security in runtime but I coudn't add it to my actual code please help.
this is my code
#Configuration
#EnableWebSecurity
protected static class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
DataSource dataSource;
#Autowired
UserRepository userRepository;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
for (UsersEntity user : userRepository.findAll())
if (user.getUsername() != null && user.getPassword() != null)
auth.
inMemoryAuthentication()
.passwordEncoder(UsersEntity.ENCODE_PASS)
.withUser(user.getUsername()).password(user.getPassword())
.roles("USER");
}
#Bean
#Override
public AuthenticationManager authenticationManagerBean()
throws Exception {
return super.authenticationManagerBean();
}
}
You can simply set another authenticationProvider.
#Autowired
private MyAuthenticationProvider authenticationProvider;
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authenticationProvider);
}
Simply implement your own MyAuthenticationProvider that asks your UserRepository for each login attempt. Or another way would be to simply use basic jdbc:
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.jdbcAuthentication().dataSource(dataSource)
.usersByUsernameQuery(
"select username,password, enabled from users where username=?")
.authoritiesByUsernameQuery(
"select username, role from user_roles where username=?");
}
...of course, you would need to set your own queries there.

Spring Security avoid create a table for roles

I'm learning Spring Security. I have my login system ready and I want to add roles. I've seen many tutorials and docs about it and I couldn't find what I'm looking for.
I don't want to create an extra table for Roles, because my table user has a column named "type" and I want to use it for authorization. The value of that column can be "person", "teacher" or "organization". So, I want to based the role system on that column, not in a OneToMany o ManyToMany relationship with a table named "role".
How can I configure that?
Thanks
UPDATED
I forgot, I'm using Spring Data. This is the code I'm using
#Configuration
#EnableWebSecurity
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {
private AuthenticationProvider authenticationProvider;
#Autowired
#Qualifier("daoAuthenticationProvider")
public void setAuthenticationProvider(AuthenticationProvider authenticationProvider) {
this.authenticationProvider = authenticationProvider;
}
#Bean
public PasswordEncoder passwordEncoder(BCryptPasswordEncoder passwordEncoder){
return passwordEncoder;
}
#Bean
public DaoAuthenticationProvider daoAuthenticationProvider(BCryptPasswordEncoder passwordEncoder,
UserDetailsService userDetailsService){
DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider();
daoAuthenticationProvider.setPasswordEncoder(passwordEncoder);
daoAuthenticationProvider.setUserDetailsService(userDetailsService);
return daoAuthenticationProvider;
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().ignoringAntMatchers("/h2-console").disable()
.authorizeRequests().antMatchers("/").authenticated()
.antMatchers("/console/**").permitAll()
.antMatchers("/static/**").permitAll()
.antMatchers("/profile").hasAuthority("PERSON")
.and().formLogin().loginPage("/login").permitAll()
.and().exceptionHandling().accessDeniedPage("/login")
.and().logout().permitAll()
http.headers().frameOptions().disable();
}
#Autowired
public void configureAuthManager(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception{
authenticationManagerBuilder
.jdbcAuthentication().authoritiesByUsernameQuery("select type from users where username = ?").and()
.authenticationProvider(authenticationProvider);
}
}
You can define a UserDetailsService with a PasswordEncoder in Java Config like following:
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired private PersonRepository personRepository;
#Override
#Autowired
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.userDetailsService(username -> {
Person person = personRepository.findByUsername(username);
if (person == null) throw new UsernameNotFoundException("Invalid user");
return new User(person.getUsername(),
person.getPassword(),
Collections.singleton(new SimpleGrantedAuthority(person.getType())));
})
.passwordEncoder(passwordEncoder())
}
// Rest of the configuration
}
In the above example, i supposed you have a PersonRespository that has access to your user information. With this UserDetailsService you won't need your AuthenticationProvider. Also, User resides in org.springframework.security.core.userdetails package.

Spring security form logging and outh2 in same app

I have written a sample spring application which have some rest services protected using spring-security-oauth2. Now I want to move these services to the original application which uses spring security form login.
In the original application I want rest services to be protected from spring-security-oauth2 and other spring controllers to be protected using a form login. What I want to know is, is this approach is right or wrong, if right, how could I complete this action.
This is Sample app codes, which uses ouath2,
#Configuration
#EnableWebSecurity
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
private CustomUserDetailsService userDetailsService; // Is this really needed?
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService);
}
#Override
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
#Configuration
public class OAuth2ServerConfiguration {
private static final String RESOURCE_ID = "restservice";
#Configuration
#EnableResourceServer
protected static class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
#Override
public void configure(ResourceServerSecurityConfigurer resources) {
// #formatter:off
resources.resourceId(RESOURCE_ID);
// #formatter:on
}
#Override
public void configure(HttpSecurity http) throws Exception {
// http.authorizeRequests().antMatchers("/test").not().permitAll();
http.authorizeRequests().antMatchers("/test").authenticated();
}
}
#Configuration
#EnableAuthorizationServer
protected static class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {
private TokenStore tokenStore = new InMemoryTokenStore();
#Autowired
// #Qualifier("authenticationManagerBean")
private AuthenticationManager authenticationManager;
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
// #formatter:off
endpoints.tokenStore(this.tokenStore).authenticationManager(this.authenticationManager);
// #formatter:on
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
// #formatter:off
clients.inMemory().withClient("clientapp").authorizedGrantTypes("password", "refresh_token").authorities("USER")
.scopes("read", "write").resourceIds(RESOURCE_ID).secret("123456");
// #formatter:on
}
#Bean
#Primary
public DefaultTokenServices tokenServices() {
DefaultTokenServices tokenServices = new DefaultTokenServices();
tokenServices.setSupportRefreshToken(true);
tokenServices.setTokenStore(this.tokenStore);
return tokenServices;
}
}
}
Here is a part of the original app configuration.
#EnableWebSecurity
#Configuration
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class MyStaysureSecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
authenticationManagerBuilder.inMemoryAuthentication().withUser("mycompany").password("mypsswd").roles("USER");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/rest/*", "/api-docs/**").permitAll().antMatchers("/**").authenticated().and().formLogin().defaultSuccessUrl("/dashboard").and().csrf().disable();
}
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
Spring Security is built on an ordered list of filter chains, and for each request the first one with a matching path handles the authentication. You have 3 filter chains in your combined app, one created by #EnableAuthorizationServer (with default order=0), one created by #EnableResourceServer (with default order=3), and one created by your MyStaysureSecurityConfiguration (also with order=0). You aren't allowed to have 2 filters with the same order so you need to re-arrange them and give them request matchers that make sense for your use case. Maybe you didn't need the #EnableAuthorizationServer anyway (it was unclear from the question)? In any case it is pretty simple - you have 2 choices (roughly):
exclude the oauth2 resources from the request matchers in your MyStaysureSecurityConfiguration and allow them to be handled by the resource server filter.
re-order the resource server filter to a lower order and give it a request matcher that only matches the oauth2 resources.

How to configure Spring for LDAP and JDBC?

In my web application, I have to use Spring Security and get the user's authentication details using LDAP and the user's authorization details using JDBC. The user submits a form and I get the username and password from it.
How Do I get the username and password in my WebSecurityConfig file?
How do I configure the authorization and authentication?
My WebSecurityConfig:
#Configuration
#EnableWebMvcSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().authenticated().and().formLogin()
.loginPage("/").permitAll();
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth)
throws Exception {
auth.ldapAuthentication().userDnPatterns("uid={0},ou=people")
.groupSearchBase("ou=groups").contextSource(contextSource());
}
#Bean
public DefaultSpringSecurityContextSource contextSource() {
DefaultSpringSecurityContextSource contextSource = new DefaultSpringSecurityContextSource(
"ldap://mjkoldc-03.red.com");
contextSource.setUserDn("mj\\" + email);
contextSource.setPassword(password);
contextSource.setReferral("follow");
contextSource.afterPropertiesSet();
return contextSource;
}
}
Previously I was getting details using LDAPTemplate:
LdapQuery query = query().base("dc=metaljunction,dc=com")
.attributes("GivenName", "sn", "mail", "MobilePhone")
.where("ObjectClass").is("user").and("SamAccountName")
.is(email);
If you what to authentificate agains LDAP and Autorize (get user roles from JDBC) you should implement LdapAuthoritiesPopulator.
public class CustomAuthoritiesPopulator implements LdapAuthoritiesPopulator {
#Override
public Collection<? extends GrantedAuthority> getGrantedAuthorities(DirContextOperations userData, String username) {
Collection<GrantedAuthority> gas = new HashSet<GrantedAuthority>();
gas.add(new SimpleGrantedAuthority("ADMIN"));
return gas;
}
}
and add it to your SecurityConfig
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.ldapAuthentication()
.ldapAuthoritiesPopulator(new CustomAuthoritiesPopulator())
.userSearchFilter("yourfilter")
.contextSource(contextSource());
}
}
Like this all users authentificated via LDAP will automatically get "ROLE_ADMIN".

Resources