Dropwizard: BasicAuth - basic-authentication

Using Dropwizard Authentication 0.9.0-SNAPSHOT
I want to check the credentials against database user (UserDAO).
I get the following exception
! org.hibernate.HibernateException: No session currently bound to
execution context
How to bind the session to the Authenticator?
Or are there better ways to check against the database user?
The Authenticator Class
package com.example.helloworld.auth;
import com.example.helloworld.core.User;
import com.example.helloworld.db.UserDAO;
import com.google.common.base.Optional;
import io.dropwizard.auth.AuthenticationException;
import io.dropwizard.auth.Authenticator;
import io.dropwizard.auth.basic.BasicCredentials;
public class ExampleAuthenticator implements Authenticator<BasicCredentials, User> {
UserDAO userDAO;
public ExampleAuthenticator(UserDAO userDAO) {
this.userDAO = userDAO;
}
#Override
public Optional<User> authenticate(BasicCredentials credentials) throws AuthenticationException {
Optional<User> user;
user = (Optional<User>) this.userDAO.findByEmail(credentials.getUsername());
if ("secret".equals(credentials.getPassword())) {
return Optional.of(new User(credentials.getUsername()));
}
return Optional.absent();
}
}
The Application Class
#Override
public void run(HelloWorldConfiguration configuration, Environment environment) throws Exception {
final UserDAO userDAO = new UserDAO(hibernate.getSessionFactory());
environment.jersey().register(new AuthDynamicFeature(
new BasicCredentialAuthFilter.Builder<User>()
.setAuthenticator(new ExampleAuthenticator(userDAO))
.setAuthorizer(new ExampleAuthorizer())
.setRealm("SUPER SECRET STUFF")
.buildAuthFilter()));
environment.jersey().register(RolesAllowedDynamicFeature.class);
//If you want to use #Auth to inject a custom Principal type into your resource
environment.jersey().register(new AuthValueFactoryProvider.Binder(User.class));
environment.jersey().register(new UserResource(userDAO));

To get auth to work with 0.9+ you need the following. You can refer to this particular changeset as an example.
Include the dependency.
<dependency>
<groupId>io.dropwizard</groupId>
<artifactId>dropwizard-auth</artifactId>
<version>${dropwizard.version}</version>
</dependency>
Register auth related stuff.
private void registerAuthRelated(Environment environment) {
UnauthorizedHandler unauthorizedHandler = new UnAuthorizedResourceHandler();
AuthFilter basicAuthFilter = new BasicCredentialAuthFilter.Builder<User>()
.setAuthenticator(new BasicAuthenticator())
.setAuthorizer(new UserAuthorizer())
.setRealm("shire")
.setUnauthorizedHandler(unauthorizedHandler)
.setPrefix("Basic")
.buildAuthFilter();
environment.jersey().register(new AuthDynamicFeature(basicAuthFilter));
environment.jersey().register(RolesAllowedDynamicFeature.class);
environment.jersey().register(new AuthValueFactoryProvider.Binder(User.class));
environment.jersey().register(unauthorizedHandler);
}
A basic authenticator
public class BasicAuthenticator<C, P> implements Authenticator<BasicCredentials, User> {
#Override
public Optional<User> authenticate(BasicCredentials credentials) throws AuthenticationException {
//do no authentication yet. Let all users through
return Optional.fromNullable(new User(credentials.getUsername(), credentials.getPassword()));
}
}
UnAuthorizedHandler
public class UnAuthorizedResourceHandler implements UnauthorizedHandler {
#Context
private HttpServletRequest request;
#Override
public Response buildResponse(String prefix, String realm) {
Response.Status unauthorized = Response.Status.UNAUTHORIZED;
return Response.status(unauthorized).type(MediaType.APPLICATION_JSON_TYPE).entity("Can't touch this...").build();
}
#Context
public void setRequest(HttpServletRequest request) {
this.request = request;
}
}
Authorizer
public class UserAuthorizer<P> implements Authorizer<User>{
/**
* Decides if access is granted for the given principal in the given role.
*
* #param principal a {#link Principal} object, representing a user
* #param role a user role
* #return {#code true}, if the access is granted, {#code false otherwise}
*/
#Override
public boolean authorize(User principal, String role) {
return true;
}
}
Finally use it in your resource
#GET
public Response hello(#Auth User user){
return Response.ok().entity("You got permission!").build();
}

You're going to need code in your Application class that looks like this
environment.jersey().register(AuthFactory.binder(new BasicAuthFactory<>(
new ExampleAuthenticator(userDAO), "AUTHENTICATION", User.class)));
Then you can use the #Auth tag on a User parameter for a method and any incoming authentication credentials will hit the authenticate method, allowing you to return the correct User object or absent if the credentials are not in your database.
EDIT: Works for Dropwizard v0.8.4

On Latest versions starting from 0.9 onward, you can use "#Context" annotation in resource class methods as shown below:
#RolesAllowed("EMPLOYEE")
#Path("/emp")
#GET
#Produces(MediaType.APPLICATION_JSON)
public Response getEmployeeResponse(#Context SecurityContext context) {
SimplePrincipal sp = (SimplePrincipal) context.getUserPrincipal();
return Response.ok("{\"Hello\": \"Mr. " + sp.getUsername() + "\"( Valuable emp )}").build();
}

Related

How to add custom argument in Spring controller method argument (like: #RequestParam, Model)?

This is a typical Spring controller method.
public ResponseEntity<RestApiResponse<OTPResponse>> sendOtp(HttpServletRequest request) {
UserDetails userDetails = (UserDetails) request.getSession().getAttribute(SessionKey.USER_DETAILS);
// Do some work
//...
}
To get the username I have to copy the bellow line over and over again in every other controller method.
UserDetails userDetails = (UserDetails) request.getSession().getAttribute(SessionKey.USER_DETAILS);
Can I do this as follows ?
// #UserDetails is some kind of imaginary annotation
public ResponseEntity<RestApiResponse<OTPResponse>> sendOtp(#UserDetails UserDetails userDetails) {
userDetails.getUsername();
// Do some work
//....
}
Can I intercept the request, get the userDetails from request and inject that as controller method argument?
you can use below code as util method
public UserDetails getUser(){
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
if (!(auth instanceof AnonymousAuthenticationToken)) {
return (UserDetails) auth.getPrincipal();
}
return null;
}
Another way of doing same.
#RequestMapping(method = RequestMethod.GET)
public ModelAndView anyMethodNameGoesHere(Principal principal) {
final String loggedInUserName = principal.getName();
}
You can intercept requests and do it yourself, however spring-security already have such feature. It is called #AuthenticationPrincipal.
You can use it as follow:
#GetMapping
String hello(#AuthenticationPrincipal java.security.Principal principal) {
return principal.getName();
}
If having a Principal isn't enough for your usecase, User also works:
#GetMapping
String hello(#AuthenticationPrincipal org.springframework.security.core.userdetails.User user) {
return user.getUsername();
}
You can even inject your custom user details this way if you want.
Please have a look at documentation.

Sprint Rest AuthenticationPrincipal returns customUser with only null values

I am trying to get the currently logged in user (basic auth) in a spring boot REST backend:
#RequestMapping(value = "/someURL", method = RequestMethod.GET)
public #ResponseBody Map someMethod(
Authentication auth, // <==== works
#AuthenticationPrincipal(expression = "liquidoUserModel") UserModel liquidoUserModel
)
{
log.debug(auth.getPrincipal()) // <==== THIS WORKS
log.debug(liquidoUserModel) // <==== returns an intance with empty fields
}
Here is my custom UserModel
#Data // Lombok magic for all the getters and setters
#Entity
#RequiredArgsConstructor
#Table(name = "users")
public class UserModel {
#Id
Long id;
#NonNull
#Column(unique = true)
public String email;
[...]
}
And this is my UserDetailsService
public class LiquidoUserDetailsService implements UserDetailsService {
#Autowired
UserRepo userRepo;
#Override
public LiquidoAuthUser loadUserByUsername(String email) throws UsernameNotFoundException {
UserModel userModel = userRepo.findByEmail(email);
return new LiquidoAuthUser(userModel.getEmail(), userModel.getPasswordHash(), getGrantedAuthorities(userModel), userModel);
}
}
And finally the LiquidoAuthUser
public class LiquidoAuthUser extends User {
private UserModel liquidoUserModel;
public LiquidoAuthUser(String username, String password, Collection<? extends GrantedAuthority> authorities, UserModel liquidoUserModel) {
super(username, password, authorities);
this.liquidoUserModel = liquidoUserModel;
}
public UserModel getLiquidoUserModel() {
return liquidoUserModel;
}
public void setLiquidoUserModel(UserModel userModel) {
this.liquidoUserModel = userModel;
}
}
And of course I have the #EnableWebMvc annotation on my main SpringApplication class.
My problem: How can I get the currently authenticated custom UserModel in the REST handler?
The strange thing: I actually can get my custom user from the Authentication auth object. Why does the #AuthenticationPrincipal not work?
I am using spring-security-4.1.3-RELEASE
Full code is open source at https://github.com/Doogiemuc/liquido-backend-spring
I tried and debug your code but not able to find issue of
#AuthenticationPrincipal. Typically this annotation is resolved by AuthenticationPrincipalArgumentResolver class of spring security web annotation. By using #EnableWebSecurity you will automatically have this added to your Spring MVC configuration. Need to be debug more on AuthenticationPrincipalArgumentResolver that for time being I will suggest go with Authentication class and get your object.

Bypass Spring Security authentication Session Popup with custom Restful LoginController

Using spring security to secure Rest API.
by integrating spring security all goes fine, until I try to develop my own controller which gonna handle the user authentication based on sessionManagement.
Here is my spring security configuration java code :
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private AuthenticationEntryPoint authEntryPoint;
#Autowired
private UserDetailsService userDetailsService;
/**
*
* Kind of links provided based on the User authorises USER - SUPER_USER - SIMPLE_USER ..etc
*
*/
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
.and()
.authorizeRequests()
.antMatchers("/loginPage").permitAll()
.antMatchers("/securetAPI1/**","/securetAPI2/**","/securetAPI3/**","/securetAPI5/**").access("hasRole('ADMIN')")
.and().httpBasic().authenticationEntryPoint(authEntryPoint);
}
/**
* Build Authentication and attach it to the appContext
*
* #param auth
* #throws Exception
*/
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService);
}
}
Then my AuthentivationEntryPoint contains this code:
#Component
public class AuthenticationEntryPoint extends BasicAuthenticationEntryPoint {
/**
* Authentication Entry Point on our application which check if the user allowed or not,
* and give response back based on servlet on the case of failure
*
*/
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authEx)
throws IOException, ServletException {
response.addHeader("WWW-Authenticate", "Basic realm=" +getRealmName());
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
PrintWriter writer = response.getWriter();
writer.println("HTTP Status 401 - " + authEx.getMessage());
}
#Override
public void afterPropertiesSet() throws Exception {
setRealmName("MBO");
super.afterPropertiesSet();
}
}
And of the UserDetailsService class contain this snippet code:
#Service
public class TheUserDetailsService implements UserDetailsService {
#Autowired
private UserRepository userRepository;
/**
*
* retrieve authenticate user by his Role to verify it later if he is autorized to access the API or Not
*
*/
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User retrievedUser = userRepository.findByUserName(username);
List<GrantedAuthority> authorities = this.createUserAuthorities(retrievedUser.getUserRole());
return (UserDetails) this.buildUserForAuthentication(retrievedUser, authorities);
}
private User buildUserForAuthentication(User user, List<GrantedAuthority> authorities) {
User usr = new User(user.getUsername(), user.getPassword(), user.isEnabled(), true, true, true, authorities);
return usr;
}
/**
* give the user authority based on his Role in database, which gonna be converted by our method to SIMPLE.GRANTED.AUTHORITY
* to be processed later in Servlet intercepting and check authorities
*
* One USER can have multiple authorities for that reason the return back a List<GrantedAuthority>.
* #param userRoles
* #return
*/
private List<GrantedAuthority> createUserAuthorities(Set<UserRole> userRoles) {
Set<GrantedAuthority> authsList = new HashSet<GrantedAuthority>();
// create user's authorities
userRoles.forEach(userRole -> {
authsList.add(new SimpleGrantedAuthority(userRole.getRole()));
});
List<GrantedAuthority> Result = new ArrayList<GrantedAuthority>(authsList);
return Result;
}
}
So till now when even i try to access one of this Rest API "/securetAPI1/**" "/securetAPI2/**" "/securetAPI3/**" "/securetAPI5/**" from browser its show me this spring security session authentication popup
Any one can suggest me correction or any tutorial who to build my Login RestController that submit user-sessionID and bypass the spring security authentication popup for the rest of API accessAttempt?
My RestController Login try:
#RestController
#RequestMapping(value="/")
public class LoginController {
#Autowired
private UserDetailsService userDetail;
#RequestMapping(value="/loginPage", method = RequestMethod.POST)
public void login(#RequestBody UserDto user, HttpServletResponse response) throws IOException{
if(user.getUsername() != null && user.getPassword() != null) {
UserDetails userDetails = userDetail.loadUserByUsername(user.getUsername());
if(userDetails != null) {
response.sendRedirect("/application-context/securetAPI1/");
}
else
System.out.println("Failed");
}
}
}

Override UserAuthenticationConverter for JWT OAuth Tokens

I am trying to create a spring resource server secured with oauth2.
I am using auth0 for my auth2 service, and I have an api and client configured with scopes.
I have a resource server that mostly works. It is secured, and I can use #EnableGlobalMethodSecurity and #PreAuthorize("#oauth2.hasScope('profile:read')") to limit access to tokens with that scope.
However, when I try to get the Principal or the OAuth2Authentication they are both null. I've configured the resource server to use the JWK key-set-uri.
I suspect that this has to do with the DefaultUserAuthenticationConverter trying to read the the 'user_name' claim form the JWT, but it needs to be reading it from the 'sub' claim, and I don't know how to change this behaviour.
First create a UserAuthenticationConverter:
public class OidcUserAuthenticationConverter implements UserAuthenticationConverter {
final String SUB = "sub";
#Override
public Map<String, ?> convertUserAuthentication(Authentication userAuthentication) {
throw new UnsupportedOperationException();
}
#Override
public Authentication extractAuthentication(Map<String, ?> map) {
if (map.containsKey(SUB)) {
Object principal = map.get(SUB);
Collection<? extends GrantedAuthority> authorities = null;
return new UsernamePasswordAuthenticationToken(principal, "N/A", authorities);
}
return null;
}
}
Then configure spring to use it like so:
#Configuration
public class OidcJwkTokenStoreConfiguration {
private final ResourceServerProperties resource;
public OidcJwkTokenStoreConfiguration(ResourceServerProperties resource) {
this.resource = resource;
}
#Bean
public TokenStore jwkTokenStore() {
DefaultAccessTokenConverter tokenConverter = new DefaultAccessTokenConverter();
tokenConverter.setUserTokenConverter(new OidcUserAuthenticationConverter());
return new JwkTokenStore(this.resource.getJwk().getKeySetUri(), tokenConverter);
}
}

How to access token additionalInformation to validate expression-based access control

I succesfully added user_id additionnal information on the generated tokens on the authorization server side by implementing a TokenEnhancer. Here is a token generated:
{"access_token":"ccae1713-00d4-49c2-adbf-e699c525d53e","token_type":"bearer","expires_in":31512,"scope":"end-user","user_id":2}
Now, on the Resource server side, which is a completely separate spring project communicating through a RemoteTokenServices, i would like to use theses informations with method expression-based access control. For example i would like to use the added user_id data (it is Spring Data JPA repository for use with Spring Data Rest):
#PreAuthorize("#oauth2.hasScope('admin') or #id == authentication.principal.user_id")
#Override
UserAccount findOne (#P("id") Integer id);
The #oauth2.hasScope('admin') works as expected but the #id == authentication.principal.user_id" part obviously not.
how can i access to the additional data added to the token on expression-based access control ?
So i've found myself. The key interface is UserAuthenticationConverter.
Using the default provided DefaultUserAuthenticationConverter class, we can set a UserDetailsService which is used to set authentication.principal with the UserDetail object returned by the UserDetailsService. Without that, authentication.principal is only set with the token username as a String.
Here is an extract of my ResourceServerConfigAdapter:
#Configuration
#EnableResourceServer
protected static class ResourceServerConfiguration
extends ResourceServerConfigurerAdapter {
#Bean
UserDetailsService userDetailsService () {
return new UserDetailsServiceImpl();
}
#Bean
public UserAuthenticationConverter userAuthenticationConverter () {
DefaultUserAuthenticationConverter duac
= new DefaultUserAuthenticationConverter();
duac.setUserDetailsService(userDetailsService());
return duac;
}
#Bean
public AccessTokenConverter accessTokenConverter() {
DefaultAccessTokenConverter datc
= new DefaultAccessTokenConverter();
datc.setUserTokenConverter(userAuthenticationConverter());
return datc;
}
#Bean
RemoteTokenServices getRemoteTokenServices () {
RemoteTokenServices rts = new RemoteTokenServices();
rts.setCheckTokenEndpointUrl(
"http://localhost:15574/oauth/check_token");
rts.setAccessTokenConverter(accessTokenConverter());
rts.setClientId("client");
rts.setClientSecret("pass");
return rts;
}
...
}
Another method is to override the DefaultUserAuthenticationManager and provide a custom public Authentication extractAuthentication(Map<String, ?> map).
Once this is done, we can use the user data on expression-based access control like that:
#PreAuthorize("#oauth2.hasScope('admin') or #id == authentication.principal.userAccount.id")
#Override
UserAccount findOne (#P("id") Integer id);
Note that userAccount is my original DOMAIN user object. It could be everything the UserDetailsService returns.
EDIT:
To answer to Valentin Despa, here is my UserDetailsService implementation:
#Component
public class UserDetailsServiceImpl implements UserDetailsService {
#Autowired
UserAccountRepository userAccountRepository;
public UserDetails loadUserByUsername (String username)
throws UsernameNotFoundException {
// Fetch user from repository
UserAccount ua = this.userAccountRepository
.findByEmail(username);
// If nothing throws Exception
if (ua == null) {
throw new UsernameNotFoundException(
"No user found having this username");
}
// Convert it to a UserDetails object
return new UserDetailsImpl(ua);
}
}

Resources