How to initialize Spring Boot security config with default username/password but not crash on second run? - spring-boot

Following the topical guide here and adding a BCrypt password encoder based on Baeldung's example here I have configured my Spring Boot application to use my database (set up separately, not auto-generated by an ORM or something) as its source of user details for authentication. This part of my security configuration (here) looks like this:
#Override
public void configure(AuthenticationManagerBuilder builder) throws Exception {
builder .jdbcAuthentication()
.dataSource(dataSource)
.withUser(User.withUsername("admin").password(passwordEncoder().encode("pass")).roles("SUPER"));
logger.debug("Configured app to use JDBC authentication with default database.");
}
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
On the first run, this works, creating a user called 'admin' with a hashed password and the specified role in the database. (This is a PostgreSQL database for what it's worth.) However, if I try to run the app again, it fails to start, crashing because it tried to create the same user again and got a duplicate primary key error.
What I'd like: I'd like Spring Boot to create the default user if it doesn't already exist, skip over it if one already exists.
Why: It is necessary to be able to log in to a newly initialized copy of the application, sometimes restarting several times, for testing and for experimentation on the developer's machine. My "production" database should already have an 'admin' login and the app should not overwrite it, or crash because it cannot.
My question, therefore, is: How can I initialize a default user in Spring Boot's jdbcAuthentication configuration in such a way that Spring Boot won't crash if the username already exists?
Alternatively: If I could INSERT a default user into the database with SQL when the database is spun up, I wouldn't need to do it in the Spring Boot configuration. But I don't know how to hash a password in an INSERT statement in a way that matches Spring Boot's hashing.
PS: I have another issue with my new configuration breaking some automated test classes (see the other question if interested).

You can use the alternative solution that you have thought using the option .withDefaultSchema() with the jdbcauthentication that you are using. As you have mentioned in that alternative that you may have to figure out way to use hashed password in that script.
Should you have any followup question, this baeldung blog post will help you.
https://www.baeldung.com/spring-security-jdbc-authentication
Hope this helps.

Related

Is there a way to update/modify the password of MongoDB connection at runtime?

The ask is to modify the password of a mongodb connection at runtime in a spring boot project. For example let's assume a mongodb connection is established while starting the application with password 'xyz'.I would like to modify the password at runtime by lets say hitting an api.
I have tried following solutions so far to tackle this issue:
Replacing mongoTemplate bean at runtime: Tried creating a new mongoTemplate Bean with new password at runtime and replaced it in the context using the methods given in following link. The issue with this approach is that the bean is getting refreshed only once. Ideally it should work everytime when the api to update password is being called.
Updating password in mongoCredentials: One of the obvious approach is to update the password directly in mongoCredentials since mongoTemplate uses mongoCredential class to store the credentials information.But the issue is that the password field in MongoCredentials class is 'final static' so we cannot update it even with reflections. Even though there are some tricks to update the final static fields but i'm looking for a more acceptable solution.
There is #RefreshScope in Spring Cloud project exactly for your purpose. Here is Spring's documentation about how it works: https://cloud.spring.io/spring-cloud-static/spring-cloud.html#_refresh_scope.
So all you need to do is update Environment and call org.springframework.cloud.context.scope.refresh.RefreshScope#refresh or org.springframework.cloud.context.scope.refresh.RefreshScope#refreshAll when you need to refresh your context.

Spring-boot LDAP - Property 'userDn' not set

I am running a Spring-boot application which authenticates users via our internal LDAP with spring-security-ldap.
By default it binds with LDAP anonymously.
Property 'userDn' not set - anonymous context will be used for read-write operations
But I want the first bind to be with current username.
Where should I specify the userDn attribute?
Thank you for your advice
When using spring ldap maybe you started from one many tutorials on the web but main of them uses embedded ldap server; embdedded server uses ldif file and doesn't need the manager credetials.
When connecting to an external ldap server you need to specify userDn setting it via managerDn method. Here the snippet of code
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.ldapAuthentication().contextSource().managerDn("uid=admin,ou=system")
.managerPassword("secret")
.......
}
Obviously you need to provide also all the other infos like url, port, etc (and userSearchBase like mvreijn told).
I am not the most knowledgeable person regarding Spring-boot, more so regarding LDAP.
That said, your LDAP configuration properties should be mentioned in your application.properties file and are named spring.ldap.*.
They are mentioned in the documentation here.
When initializing your authentication provider, you can pass important properties like the Base DN (root to search from) and the filter using:
.userSearchBase("ou=<your users container>").userSearchFilter("(uid={0})")
Most likely, your search filter will be uid={0} or cn={0}.

Cas and Spring example: I don't understand "setUserDetailsService"

In this example: https://www.baeldung.com/spring-security-cas-sso
there is this piece of code:
#Bean
public CasAuthenticationProvider casAuthenticationProvider() {
CasAuthenticationProvider provider = new CasAuthenticationProvider();
provider.setServiceProperties(serviceProperties());
provider.setTicketValidator(ticketValidator());
provider.setUserDetailsService(
s -> new User("casuser", "Mellon", true, true, true, true,
AuthorityUtils.createAuthorityList("ROLE_ADMIN")));
provider.setKey("CAS_PROVIDER_LOCALHOST_9000");
return provider;
}
I don't understand this part:
provider.setUserDetailsService(
s -> new User("casuser", "Mellon", true, true, true, true,
AuthorityUtils.createAuthorityList("ROLE_ADMIN")));
what are we supposed to put here ? Am I supposed to create my own UserDetailsService (if yes, how ?) ? I was expected some 'default cas user detail service'...
how does this code work? creating a user to provide a UserDetailsService ?
This is how Spring security works on high level.
User tries to authenticate via some type of UI (part of CAS for example). The UI will pass username/password to Spring. Spring will eventually call UserDetailService.loadUserByUsername and pass the username to it, and if user exists the UserDetailService will return non null UserDetails. In case of null UserDetails or non null one with different password Spring will fail authentication.
CAS is just an authentication server, it leaves open how user is stored. You can choose to use LDAP or database. That choice is based on different implementation of UserDetailService. Look at javadoc again. It has list of default implementations you can use.
See part 5 of your linked tutorial. It shows how you can change both CAS and Spring Boot app to use database as user storage. The key here is that in order for back end to work with CAS server against users stored in database both need to be configured appropriately in order to look up user against database. CAS is configured via application.properties and Spring boot via UserDetailService.
Now to your questions in the comment:
why should the client bother about how cas server store the users ?
Client should not bother with UserDetailService. It is only used by back end service that is secured by CAS.
Just to be sure that I get it tight: if I just need to know 'is that
user connected?' then CAS is enough and I will never use
UserDetailService. But if I need some information about the user
(name, telephone etc..) then I call the UserDetailService to load it
(from db, ldap or whatever).
Yes and no. You dont need to store password in UserDetails but you need to be able to return UserDetails for successful CAS authenticated user. See this part from your linked tutorial:
Note again that the principal in the database that the server uses
must be the same as that of the client applications.

Using/configuring Spring Security with Spring 4 and Hibernate

I want to implement the login/logout (authentication/authorization) system of my Spring 4 MVC application with Spring Security.
Currently I use a very simple hand-made implementation which basically does nothing more than comparing the entered username and MD5 hashed password with the database values by looking up the user by the username using a custom service method and comparing the encrypted passwords.
If the passwords match, the username of the logged in member is saved in the session and a ControllerAdvice looks up the Member object for the user using the username in the session prior to each request. The checkLogin method returns true is username and password match:
#Service("loginService")
#Transactional
public class LoginServiceImpl implements LoginService {
private MemberDao dao;
//more methods
#Override
public boolean checkLogin(String username, String password) {
String hashedPassword = getPasswordHash(password);
return dao.checkLogin(username, hashedPassword);
}
}
This does work but is not a very elegant solution, does not handle different roles and is probably not very secure. Besides I want to become familiar with Spring Security.
Reading the official tutorial for Spring Security (http://docs.spring.io/spring-security/site/docs/4.0.4.RELEASE/reference/htmlsingle/#tech-userdetailsservice) the way to go to authenticate against the Login service method does not become clear to me.
The tutorial discusses authentication direct against the database but I cannot find anything about using a Service method to perform the authentication and in my layered architecture, the database is hidden behind the Servoce and Dao (Hibernate) layers.
Also most examples in the tutorial use XML based instead of Java based configuration which I use for my application.
After having search a lot with search engines, I still have not found a tutorial which implements Spring Security in a Spring MVC application using a familiar layered structure using a Service and Dao layer.
Do I need to bypass Service and DAO/Hibernate layers and authenticate directory against the database? Or write a custom authentication-provider implementing UserDetailsService as described in this post?
Spring Security 3 database authentication with Hibernate
And is configuring Spring Security possible with Java based configuration only? I am a bit lost with this issue so I hope for some hints...

Auto insert default record when deploying Spring MVC App with Spring Security

I am looking for a way to auto insert a default admin account, using JPA, when my spring mvc application is deployed.
My database is generated based on the Entities.
I want to kick off something that will insert a default admin user, assign roles, every time the application is deployed.
It depends on which implementation of JPA you use.
If you use Hibernate you can add import.sql file (that contains records to load) to the class path. More info here.
As a workaround you can also use dbunit tool.
I would recommend having a migration utility that will keep your database in synch with your codebase - these are typically DDL's, but again the queries to insert default admin user, assign roles etc can also be part of this migration utility. There are very good one's available - Flyway is one that I have used, Liquibase is another one.
There is a very good comparison of the different migration utilities on the Flyway homepage also that you can look at.
i use CommandLineRunner interface.
#Component
public class CommandLineAppStartupRunner implements CommandLineRunner {
#Autowired
UserRepository userRepository;
#Override
public void run(String...args) throws Exception {
User admin = new user(firstName);
userRepository.save(admin);
}
}
before the app starts this class will be executed.
you can find other ways here : Guide To Running Logic on Startup in Spring

Resources