userRepository insecure as userDetailsService needs to look up user - spring

I have implemented a custom UserDetailsService that looks up a user and allows them to get a oauth 2 token using password grant. The service indicates to use a JPA userRepository to look up the user.
This all works, except that now I have a /users endpoint publicly available pulling in all users and passwords to any authenticated user. Ideally I would like to not have this endpoint available at all, but if I put a preAuthorize on it with admin role, then all users are denied while trying to obtain a login token too, so this doesn't work either. How do people get around this issue? I can add a matcher to resource server to only allow admin users, this seems to work, but I don't want the endpoint available at all, it's quite scary to have it there just so UserDetailsService can use it to get access tokens?
Any input on what you guys do would be greatly appreciated.
//Example code:
// Bad public facing endpoint returning all users and passwords
public interface UserRepository extends JpaRepository<User, Long> {
User findOneByUsername(String username);
}
// Custom user details service used by oauth 2 to get access tokens
// uses userRepo above
#Service("userDetailsService")
public class UserService implements UserDetailsService {
#Autowired
private UserRepository userRepository;
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
return userRepository.findOneByUsername(username);
}
}
// class using user details service
#Configuration
#EnableAuthorizationServer
public class OAuth2Config extends AuthorizationServerConfigurerAdapter {
#Qualifier("userDetailsService")
private UserDetailsService userDetailsService;
#Override
public void configure(AuthorizationServerEndpointsConfigurer configurer) throws Exception {
configurer.userDetailsService(userDetailsService);
}
}

I presume that you are using Spring Data?
In that case, you need to tell Spring not to export the endpoint:
#RepositoryRestResource(exported = false)
https://docs.spring.io/spring-data/rest/docs/current/api/org/springframework/data/rest/core/annotation/RepositoryRestResource.html

Related

How to configure Spring Boot Security so that a user is only allowed to update their own profile

I have implemented the basic Spring Boot Security stuff in order to secure my web services. I know that you can grant access to some services only to some user Roles, but is it also possible to grant access to a specified user (user can be dynamic)?
Let's say we have a social app, where every user has their own profile. With the following rest-service, they should be the only one able to edit the profile:
#RestController
public class UserController {
#RequestMapping(method = RequestMethod.PUT, path = "/user/{userId}", ...)
public UserDetails updateUserDetails(#PathVariable("userId") String userId) {
// code for updating the description for the specified user
}}
}
How can i ensure with spring security, that only the user itself can update his personal profile? Any other user should be rejected. Is there an elegant way, how you can configure this behaviour?
I have tried to find a method for that inside my WebSecurityConfig, but with no success.
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
// configure authorization for urls
.authorizeRequests()
// grant access to all users for root path and /home
//.antMatchers("/", "/home").permitAll()
// here i would like to grant access in the way, that only the user is allowed to perform this request by calling url with his userId
.antMatchers(HttpMethod.PUT,"/user/<userId>").and().httpBasic();
}
What is a good approach to implement this behaviour?
I think that the best way to implement something like this would be to inject the Principal (Object containing the user that is logged in for this request) into the controller and then check if the user id or username is matching.
#RestController
public class UserController {
#RequestMapping(method = RequestMethod.PUT, path = "/user/{userId}", ...)
public UserDetails updateUserDetails(#PathVariable("userId") String userId, Principal principal) {
CustomUserDetails userDetails = (CustomUserDetails) principal;
if (userDetails.getUserId().equals(userId)) {
// Update the user
}
}}
}
Note that you will need a custom UserDetails interface if you want to add the user id, because it only provided the username by default. Check this question if you want to know how.
Use #PreAuthorize annotation:
#PreAuthorize("#userId == principal.userId")
#RequestMapping(method = RequestMethod.PUT, path = "/user/{userId}", ...)
public UserDetails updateUserDetails(#PathVariable("userId") String userId) {
// code for updating the description for the specified user
}
This assumes that the class that implements UserDetails interface has a userId property.

Spring - Call a Service method in JSTL

I'm using Spring Security to handle user authentication for my Spring MVC web app. I'm able to get the username from the Authentication object, but my username is the email address, and I want to be able to show the user's actual name in my header.
So I have my custom User class:
class Users{
String name;
String email;
String password;
// getters and setters
}
I thought about using an aop scoped proxy to set the User in the session, as explained in this blog: http://richardchesterwood.blogspot.co.uk/2011/03/using-sessions-in-spring-mvc-including.html . The problem I faced using this approach is that the AuthenticationSuccessHandler is actually a Service and should be stateless. So Spring doesn't autowire a Users object for me in the Service.
So I created a Service method that would get the username (or email) from the Authentication object and return my Users object. This I can use in my Controllers.
#Service
#Transactional
public class UserServiceImpl implements UserService {
#Override
public Users getCurrentUser() {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
User userD = (User)auth.getPrincipal();
Users currentUser = getUserByEmail(userD.getUsername());
return currentUser;
}
}
So is there a way that I can call this Service method from JSTL to get the user's full name, which I can display in my header?
Am also open to suggestions for a better way to implement this.
EDIT:
In my earlier approach using the AuthenticationSuccessHandler, my code goes like this:
#Service("userDetailsService")
#Transactional
public class UserAuthenticationServiceImpl implements AuthenticationSuccessHandler {
#Autowired
Users currentUser;
#Override
public void onAuthenticationSuccess(HttpServletRequest hsr, HttpServletResponse hsr1, Authentication a) throws IOException, ServletException {
User user = (User) a.getPrincipal();
Users user1 = userDao.getUserByEmail(user.getUsername());
currentUser.setName(user1.getName());
currentUser.setUserRoles(user1.getUserRoles());
//currentUser = user1;
}
}
And in my spring-servlet.xml file, I have this:
<bean id="currentUser" class="com.foo.bean.Users" scope="session">
<!-- this next element effects the proxying of the surrounding bean -->
<aop:scoped-proxy/>
</bean>
The problem I'm facing here is that Spring isn't autowiring my currentUser object because the Service isn't in the session scope.
If the only thing you need is the full name just use an AuthenticationSuccessHandler to retrieve the user and add the name to the session (or the full user if you need more then that).
#Override
public void onAuthenticationSuccess(HttpServletRequest req, HttpServletResponse res, Authentication auth) throws IOException, ServletException {
User user = (User) auth.getPrincipal();
Users user1 = userDao.getUserByEmail(user.getUsername());
WebUtils.setSessionAttribute(req, "currentUser" user1);
}
Then in your JSP the only thing you need is ${currentUser.username}.
Although I wouldn't suggest stuffing the full user in the session I would suggest just adding the information needed.
WebUtils.setSessionAttribute(req, "currentUsername" user1.getUsername());
Then in your JSP ${currentUsername} saves you a lot of serialization overhead of the session.

Custom WebSecurityConfigurerAdapter

I have this problem implementing a custom login authentication using SpringBoot and SpringBoot-Security. I made a Bitbucket repository as reference for this thread (within CustomSecuringWeb branch). Before anything else, most of the comments here follows the Securing a Web Application tutorial.
The thing is, I was curious as how could the authentication data is now from the database instead of just memory data (which is very common in production line applications).
Throughout the process I made two attempts (though both attempts are located on the same branch - my bad for that).
Created a custom UserDetailsService implementation
Created a custom AbstractUserDetailsAuthentictionProvider implementation
I don't know where the problem lies, but upon checking the console log both returns that the persistence(even the repository) DI on each custom class where null.
The question is how could I make both attempts working. And (possibly) which one of the two attempts is better than the other.
First of all the two approaches are used for different purpose and not interchangeable.
Case 1:
UserDetailsService is used purely as DAO to locate user information by your authentication and based on that info authenticate user, no authentication should be done within UserDetailsService, just data access.
Specifications clearly mention that. This is what you are looking for.
Case2:
AuthentictionProvider on the other hand is used for providing custom method of authentication, for example if you want to custom authenticate on fields other than login and password you may do that by implementing AuthentictionProvider and supplying this object to your AuthenticationManagerBuilder. I do not think this is what you want to do in you project. You are just looking to implement your authentication based on users stored in database using login and password which is default way.
In above Case 1 where you implemented just UserDetailsService, instance of AuthentictionProvider was created for you in AuthenticationManager by the container and it was DaoAuthenticationProvider since you supplied UserDetailsService which is nothing else but DAO in your system that is used to retrive user for authentication.
Now to your implementation,
in your configuration instead of :
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// auth.userDetailsService(new AdminSecurityService());
auth.authenticationProvider(new AdminSecurityAuthenticationProvider());
}
do something like this
#Autowired
private CustomUserDetailsService userDetailsService;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService);
}
and your CustomUserDetailsService has to implement org.springframework.security.core.userdetails.UserDetailsService
#Service
public class CustomUserDetailsService implements UserDetailsService {
private final AdminRepository userRepository;
#Autowired
public CustomUserDetailsService(AdminRepository userRepository) {
this.userRepository = userRepository;
}
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
Admin user = userRepository.findByLogin(username);
if (user == null) {
throw new UsernameNotFoundException(String.format("User %s does not exist!", username));
}
return new UserRepositoryUserDetails(user);
}
private final static class UserRepositoryUserDetails extends Admin implements UserDetails {
private static final long serialVersionUID = 1L;
private UserRepositoryUserDetails(User user) {
super(user);
}
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return AuthorityUtils.createAuthorityList("ROLE_USER");
}
#Override
public String getUsername() {
return getLogin();//inherited from user
}
#Override
public boolean isAccountNonExpired() {
return true;//not for production just to show concept
}
#Override
public boolean isAccountNonLocked() {
return true;//not for production just to show concept
}
#Override
public boolean isCredentialsNonExpired() {
return true;//not for production just to show concept
}
#Override
public boolean isEnabled() {
return true;//not for production just to show concept
}
//getPassword() is already implemented in User.class
}
}
of course implementation is up to you but you have to be able to provide user password, and rest of the methods in that interface based on the retrieved user (Admin.class in your case). Hope it helps. I did not run this example so if I made some typos go ahead and ask if something does not work. I would also get rid of that 'AuthentictionProvider' from your project if you don't need it.
Here you got documentation:http://docs.spring.io/spring-security/site/docs/4.0.0.RC1/reference/htmlsingle/#tech-userdetailsservice
After comments:
You can set PasswordEncoder in your configure method without too much hassle just do:
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder);
}
#Bean
public PasswordEncoder passwordEncoder(){
PasswordEncoder encoder = new BCryptPasswordEncoder();
return encoder;
}
You can do that because you get access to AbstractDaoAuthenticationConfigurer returned from auth.userDetailsService(userDetailsService) and it allows you to configure DaoAuthenticationProvider, which is your provider of choice when you choose to use UserDetailsService.
You are right PasswordEncoder is set in AuthenticationProvider but you do not have to
implement AuthenticationProvider just use convineince object that is returned from auth.userDetailsService(userDetailsService) and set your encoder on that object which will pass it to AuthenticationPriovider in your case DaoAuthenticationProvider that was already created for you.
Just like roadrunner mentioned in the comment you very rarely need to implement your own AuthenticationProvider usually most of authentication configuration adjustments can be done with the use of AbstractDaoAuthenticationConfigurer which as mentioned above is returned from auth.userDetailsService(userDetailsService).
"And if I ever wanted to add a password encryption. And if I ever wanted to do other authentication (like checking if the user is locked, active, user is still logged-in, etc. [excluding password hashing]) will use the AuthenticationProvider."
No this is done for you as part of standard authentication mechanism
http://docs.spring.io/autorepo/docs/spring-security/3.2.0.RELEASE/apidocs/org/springframework/security/core/userdetails/UserDetails.html
If you look at the interface UserDetails you will see that if any of the above methods returns false authentication will fail.
Implementing AuthenticationProvider is really needed in very nonstandard cases. All standard stuff is pretty much covered by the framework .
In a JDBC way http://www.mkyong.com/spring-security/spring-security-form-login-using-database/ basically, you have to specify the query to retrieve users.
Firstly I would encourage you to read about String Security Core Services.
A key one in this situation is AuthenticationManager that is responsible for deciding if the user is authenticated or not. This is what you configure with AuthenticationManagerBuilder. It's primary implementation in Spring is ProviderManager that allows to define multiple authentication mechanisms in a single applications. The most common use case is that there is one, but it is still handled by this class. Each of those multiple authentication mechanisms is represented by a different AuthenticationProvider. ProviderManager takes a list of AunthenticationProviders an iterates through them to see if any of them can authenticate the user.
What you are interested in is DaoAuthenticationProvider. As the name suggests, it allows to use a Data Access Object to authenticate the user. It uses a standard interface for such DAO - a UserDetailsService. There is a default implementation available in Spring Security, but usually this is the bit you will want to implement yourself. All the rest is provided.
Also, the configuration bit you need is totally independent from Spring Boot. This is how you'd do it in XML:
<sec:authentication-manager >
<sec:authentication-provider user-service-ref="myUserDetailsService" />
</sec:authentication-manager>
And in Java it will be:
#Configuration
#EnableWebSecurity
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
private UserDetailsService myUserDetailsService;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(myUserDetailsService);
}
}
As per UserDetails implementation, usually the one provided by Spring Security is enough. But you can also implement your own if need be.
Usually you will also want a PasswordEncoder. A good one, like BCryptPasswordEncoder:
#Configuration
#EnableWebSecurity
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
private UserDetailsService userDetailsService;
#Autowired
private PasswordEncoder passwordEncoder;
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService)
.passwordEncoder(passwordEncoder);
}
}
Notice that it's a #Bean, so that you can #Autowire it in your UserRepository to encode user passwords as you save them in the database.

Design, Authentication and Authorization strategy needed for a Spring MVC application using Spring Security

I am learning Spring-MVC and Spring-security. I decided to create a project in which I can practice the concepts. What I need help in is coming up with a strategy for authorizing and Authenticating my users. Any examples you can reference or ideas you can provide me would be very helpful for my design.
Application Synopsis:
The application will allow users to create artwork on like an HTML5
canvas and share it with the world. People can comment on the artwork
if they have the permalink to it and are authorized to see it. The
creator of the artwork allows this art to either be public or password
secured.
Here is the usage workflow:
User logs in
-> is directed to their profile page containing list of
several artworks -> User can choose to edit or delete an existing art. ->
User can also create a new art. Each artwork should have a permalink that the
user can share with the world by making it public or password protected/public.
I am not sure where to begin designing this thing as I am a rookie. Can someone provide advice on how to handle this use-case in terms of designing/architecting this application?
Looking at the requirements I can see not much of Spring Security design, this can be easily managed with more or less basic Spring Security configuration.
For authentication part I would go with implementing UserDetails and UserDetailsService interfaces, i.e. creating custom authentication provider. UserDetails covers domain model logic representing Spring Security-aware entity on this layer. This is usually combined with application User entity, like this:
#Entity
#Table(name="APP_USER")
public class User implements Serializable, UserDetails {
#Id
#Column(name="ID")
private Long id;
#Column(name="IS_ACTIVE", nullable=false)
private Boolean isActive;
#Column(name="USERNAME", nullable=false, unique=true)
private String username;
#Column(name="PASSWORD", nullable=false)
private String password;
…
#Override
public String getPassword() {
return password;
}
#Override
public boolean isEnabled() {
return isActive;
}
#Override
public boolean isCredentialsNonExpired() {
return isActive;
}
#Override
public boolean isAccountNonLocked() {
return isActive;
}
#Override
public boolean isAccountNonExpired() {
return isActive;
}
#Override
public Set<GrantedAuthority> getAuthorities() {
Set<GrantedAuthority> authorities = new HashSet<GrantedAuthority>();
authorities.add(new SimpleGrantedAuthority("ROLE_USER"));
return authorities;
}
}
Notice a number of overridden methods, this is a consequence of implementing UserDetails and required for authentication to work properly.
UserDetailsService resides on a service layer and represents Spring Security-compatible implementation of authentication provider which works with UserDetails objects:
#Service
public class UserServiceImpl implements UserService, UserDetailsService {
…
#Override
#Transactional(readOnly=true)
public UserDetails loadUserByUsername(String username) {
return userDao.getUserByUsername(username);
}
}
Here too you are free to combine it with your services, for instance working with application user entity. The example implies using DAO to fetch UserDetails objects which depends on your persistence framework.
Regarding protecting the images with passwords I would not go with Spring Security for this since for me it sounds more like a functional requirement rather than security one.

How to perform RunAs using method security with Spring MVC 3.2 and Spring Security 3.1

I have a web application with Spring MVC 3.2 and Spring Security 3.1
I'm using roles base security and have implemented UserDetailsService and UserDetails to provide GrantedAuthority.
I've enabled global method security with jsr250-annotations
Everything upto here is working as expected with signed in user method access restricted to the declared roles.
I have a further requirement to run certain methods called during application initialisation as a special user with a 'system role' ideally along the lines of JavaEE RunAs.
I'm not sure how to do this in Spring Security.
Should I be trying to create a PreAuthenticatedAuthenticationToken with some made up values and a 'system role' authority.
I could then do something likeSecurityContextHolder.getContext().setAuthentication(token);
when initialising the application.
Alternatively should I be trying to use the RunAsManager. It sounds like what I need but I have not found any simple examples of how I actually could use it.
I'm fairly new to Spring Security and I'm unsure of the best way to proceed.
When my application starts
I run a post construct method in my spring bean to create a special user in memory with a system role.
This user object implements the org.springframework.security.core.userdetails.UserDetails interface.
I then use the user to create a security token org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken
The token is then set in the Security Context.
#Service
#Transactional(readOnly = true)
public class ApplicationConfiguration{
#Inject
MyService myService;
#PostConstruct
#Transactional(readOnly = false)
public void init(){
// ######## Application Starting #######"
// Create a user that meets the contract of the Spring UserDetails interface
UserAccountImpl sysAcc = new UserAccountImpl("system", "system", "system");
UserRole role = new UserRole(Role.SYSTEM_ROLE);
role.addUserPermission(Permission.SYSTEM);
sysAcc.addUserRole(role);
UserDetailsAdapter userDetails = new UserDetailsAdapter(sysAcc);
// Create a token and set the security context
PreAuthenticatedAuthenticationToken token = new PreAuthenticatedAuthenticationToken( userDetails, userDetails.getPassword(), userDetails.getAuthorities());
SecurityContextHolder.getContext().setAuthentication(token);
// Now call service method with roles allowed
myService.initialiseSystem();
}
}
....
public interface MyService {
#RolesAllowed(SYSTEM)
public void initialiseSystem();
}
Do you really need to attach a role to the said app initialization? Why not just extract the code that needs to be run during initialization like so:
public interface Service {
#Secured("hasRole('USER')")
void service();
}
public class DefaultService implements Service {
#Override
public void service() {
doService();
}
public void doService() {
// Implementation here
}
}
...
public class AppInitializer {
#Autowired
private DefaultService service;
public void init() {
service.doService();
}
}
I believe that in this case a good solution for you would be to use the Spring Security OAuth because allow you have a greater integration to custom rules for access via tokens.
http://projects.spring.io/spring-security-oauth/

Resources