writing junit test for spring security login with database user - spring

Hello I am trying to write test for spring user login.
Basically my program uses registration where user provides username, password and email to register to database. Then with registered username and password you can log in to application.
public AuthenticationResponse login(LoginRequest loginRequest) {
try{
Authentication authentication = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(loginRequest.getUsername(),
loginRequest.getPassword()));
SecurityContextHolder.getContext().setAuthentication(authentication);
return AuthenticationResponse.builder()
.token(jwtProvider.generateToken(authentication))
.refreshToken(refreshTokenService.generateRefreshToken().getRefreshToken())
.expiresAt(Instant.now().plusMillis(jwtProvider.getJwtExpirationInMillis()))
.build();
} catch (BadCredentialsException e){
throw new AuthException("Incorrect username or password");
}
}
But I cant figure out how to write spring boot junit5 test for login. First time I am trying to write these spring tests. Where do I need start?

You can write unit test by mocking every dependency and check if correct methods were called with verify()
You can create test with spring context up, and just mocking db layer, then you'll be able to test more then one class
You can create test with spring context but instead of mocking db layer you can stub your db layer (implement dao which will use cache). This way you can insert user into cache and during testing login you will return that user from cache
Same as above but using h2 database
Same as above but using test containers which will start docker image of your actual database. Then you will be able to test whole flow with db constraints

Related

Why is the JPA repository called from spring schedular not able to get the authentication from Security Context

I have a springboot application where with authentication available in SecurityContext post login. Any call from Rest Controller to persist any entity, getCurrentAuditor() method is called which returns the current principle which is used for auto updating the created date column.
I created an schedular using spring "awaitility" dependency. However, this schedular calls an update on a entity. When update is called and spring authentication is checked, it comes as null, even though i have logged in from front end. From front end i am able to persist other entities and gets the authentication object as well.
As per my understanding, this might be happening because the schedular starts as soon as Springboot kicks in and making save request independently. If that understanding is correct, how should i resolve this?
If the Scheduler can use a "system" user for update the entity, you can do something like the following and in the scheduler code perform the authentication:
public void authenticate() {
Authentication auth = authenticationManager.authenticate(getBatch());
SecurityContext sc = SecurityContextHolder.getContext();
sc.setAuthentication(auth);
}
public UsernamePasswordAuthenticationToken getBatch() {
return UsernamePasswordAuthenticationTokenBuilder.anUsernamePasswordAuthenticationToken()
.withCredentials(batchProperties.getPassword()).withUserCode(batchProperties.getUser()).withUserDto(
userDtoFactory.getBatch()).build();
}

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...

spring #transactional does not work with multitenancy

I am developing multi-tenancy application using spring 4/hibernate 4 (no jpa). My user data for all tenants is on master database. Rest all tables are on individual tenant database. Using Spring security to authenticate the user. If user gets authenticated then I set tenatIdentifier.
For User Management each tenant admin needs to get connected to master database irrespective of their own tenantIdentifier. I am doing some thing like below.
My service method
#Transactional
public void saveUser() {
dao.saveUser();
}
My Dao method
public void saveUser() {
Session session = sessionFactory.withOptions().tenantIdentifier("master").openSession();
//Transaction transaction = session.beginTransaction();
//some dummy code to show case my problem
User user1 = new User();
user1.setUserName("manisha");
user1.setPassword("password");
user1.setFirstName("manisha first");
user1.setLastName("manisha last");
user1.setEmail("manisha#email.com");
user1.setState("Active");
user1.setTenantName("manisha");
session.persist(user1);
//transaction.commit();
session.close();
}
The problem is my user does not get persist unless I uncomment "transaction" code. So it seems in my case spring #Transactional does not work.
On the other hand for all other tables which resides on tenant db and if I get session using sessionFactory.getCurrentSession(), then data persists well inside tenant database. If any error occurs, it gets rolled back properly. So spring #Transactional annotation seems to be working as expected.
What am I doing wrong here?. Any help is most welcome

How to test REST in spring app with spring security

I've got spring web application with jersey rest services. However rest is secured via spring security and login process is very hard to perform from unit test code. I'd like to test rest services with whole spring security disabled. Is it even possible?
One of the advantages of annotation based web services is that you can unit-test them easily.
class WebServiceEndpoint {
#Path("/foo/{fooId}")
#POST
#Produces({ MediaType.APPLICATION_XML })
public Response doFoo(#PathParam("fooId") Integer fooId) {
/// ... web service endpoint implementation
}
}
If you're using Spring's servlet filter for security, then there shouldn't be any security-related code in the doFoo method, so you can just create a new WebServiceEndpoint class and call the method. So that's one way of 'disabling' security.
When you say the login process is 'hard', what do you mean? If you've succeeded in logging in once, then you can just reuse the same code in your other unit tests (e.g. in a #Before method).
Just test it as a pojo. Pass in whatever, return whatever, don't load an app context at all - that would be an integration test.
The ability to easily test functionality without the framework loaded is one of the key advantages of spring.
You don't say what's "hard," so I'm assuming that you've got something in your REST service, i.e. in the java method that you want to test, which requires authentication results. Spring has utilities for mocking the authentication results. For example, you can do the following in a #Before setup method:
Object principal = null; // fix this
Object credentials = null; // fix this
Authentication auth = new org.springframework.security.authentication.TestingAuthenticationToken(principal, credentials);
SecurityContextHolder.getContext().setAuthentication(auth);
But again, you haven't said what problem you're actually trying to solve...

Resources