Skip to content
Snippets Groups Projects
Commit cf87a481 authored by Martin Weise's avatar Martin Weise
Browse files

Added authentication for the container service, nice

Former-commit-id: c1692293
parent c3324e10
Branches
Tags
1 merge request!42Fixed the query service tests
Showing
with 174 additions and 90 deletions
......@@ -9,48 +9,12 @@
<version>0.0.1-SNAPSHOT</version>
</parent>
<artifactId>report</artifactId>
<artifactId>api</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>fda-authentication-service-report</name>
<name>fda-authentication-service-api</name>
<description>
This module is only intended for the pipeline coverage report. See the detailed report in the
respective modules
</description>
<properties>
<jacoco.version>0.8.7</jacoco.version>
</properties>
<dependencies>
<dependency>
<groupId>at.tuwien</groupId>
<artifactId>rest-service</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>at.tuwien</groupId>
<artifactId>services</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>${jacoco.version}</version>
<executions>
<execution>
<id>report-aggregate</id>
<phase>verify</phase>
<goals>
<goal>report-aggregate</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
\ No newline at end of file
package at.tuwien.endpoints;
import at.tuwien.api.user.JwtResponseDto;
import at.tuwien.api.user.LoginRequestDto;
import at.tuwien.api.auth.JwtResponseDto;
import at.tuwien.api.auth.LoginRequestDto;
import at.tuwien.api.user.UserDto;
import at.tuwien.mapper.UserMapper;
import at.tuwien.service.AuthenticationService;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiResponse;
......@@ -12,6 +14,7 @@ import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
import java.security.Principal;
@Log4j2
@RestController
......@@ -20,10 +23,12 @@ import javax.validation.Valid;
@RequestMapping("/api/auth")
public class AuthenticationEndpoint {
private final UserMapper userMapper;
private final AuthenticationService authenticationService;
@Autowired
public AuthenticationEndpoint(AuthenticationService authenticationService) {
public AuthenticationEndpoint(UserMapper userMapper, AuthenticationService authenticationService) {
this.userMapper = userMapper;
this.authenticationService = authenticationService;
}
......@@ -38,4 +43,14 @@ public class AuthenticationEndpoint {
.body(response);
}
@PutMapping
@ApiOperation(value = "Authenticates a token")
@ApiResponses({
@ApiResponse(code = 201, message = "Successfully authenticated a user.")
})
public ResponseEntity<UserDto> authenticateUser(Principal principal) {
return ResponseEntity.accepted()
.body(userMapper.principalToUserDto(principal));
}
}
\ No newline at end of file
package at.tuwien.endpoints;
import at.tuwien.api.user.SignupRequestDto;
import at.tuwien.api.auth.SignupRequestDto;
import at.tuwien.api.user.UserDto;
import at.tuwien.entities.user.User;
import at.tuwien.exception.RoleNotFoundException;
......
......@@ -2,12 +2,10 @@ package at.tuwien.auth;
import at.tuwien.service.impl.UserDetailsServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;
......@@ -18,13 +16,11 @@ import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@Slf4j
@Component
public class AuthTokenFilter extends OncePerRequestFilter {
private final JwtUtils jwtUtils;
private final UserDetailsServiceImpl userDetailsService;
@Autowired
public AuthTokenFilter(JwtUtils jwtUtils, UserDetailsServiceImpl userDetailsService) {
this.jwtUtils = jwtUtils;
this.userDetailsService = userDetailsService;
......
package at.tuwien.auth;
import at.tuwien.api.user.UserDetailsDto;
import at.tuwien.api.user.UserDto;
import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
......@@ -24,7 +25,7 @@ public class JwtUtils {
private Integer expire;
public String generateJwtToken(Authentication authentication) {
final UserDto userPrincipal = (UserDto) authentication.getPrincipal();
final UserDetailsDto userPrincipal = (UserDetailsDto) authentication.getPrincipal();
final Algorithm algorithm = Algorithm.HMAC512(secret);
return JWT.create()
.withSubject(userPrincipal.getUsername())
......
......@@ -38,7 +38,7 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
}
@Bean
public AuthTokenFilter authenticationJwtTokenFilter() {
public AuthTokenFilter authTokenFilter() {
return new AuthTokenFilter(jwtUtils, userDetailsService);
}
......@@ -86,7 +86,7 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
/* our private endpoints */
.anyRequest().authenticated();
/* add JWT token filter */
http.addFilterBefore(authenticationJwtTokenFilter(),
http.addFilterBefore(authTokenFilter(),
UsernamePasswordAuthenticationFilter.class
);
}
......
package at.tuwien.mapper;
import at.tuwien.api.user.SignupRequestDto;
import at.tuwien.api.auth.JwtResponseDto;
import at.tuwien.api.auth.SignupRequestDto;
import at.tuwien.api.user.GrantedAuthorityDto;
import at.tuwien.api.user.UserDetailsDto;
import at.tuwien.api.user.UserDto;
import at.tuwien.entities.user.RoleType;
import at.tuwien.entities.user.User;
import org.mapstruct.Mapper;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import java.util.List;
import java.security.Principal;
import java.util.Objects;
import java.util.stream.Collectors;
@Mapper(componentModel = "spring")
public interface UserMapper {
org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(UserMapper.class);
User signupRequestDtoToUser(SignupRequestDto data);
UserDetailsDto userToUserDetailsDto(User data);
default JwtResponseDto principalToJwtResponseDto(Object data) {
final UserDetailsDto details = (UserDetailsDto) data;
return JwtResponseDto.builder()
.id(details.getId())
.username(details.getUsername())
.email(details.getEmail())
.roles(details.getAuthorities()
.stream()
.filter(Objects::nonNull)
.map(GrantedAuthority::getAuthority)
.collect(Collectors.toList()))
.build();
}
default UserDto userToUserDto(User data) {
return UserDto.builder()
.id(data.getId())
......@@ -23,7 +47,33 @@ public interface UserMapper {
.password(data.getPassword())
.authorities(data.getRoles()
.stream()
.map(role -> new SimpleGrantedAuthority(role.name()))
.map(this::roleTypeToGrantedAuthorityDto)
.collect(Collectors.toList()))
.build();
}
default GrantedAuthority roleTypeToGrantedAuthority(RoleType data) {
return new SimpleGrantedAuthority(data.name());
}
default GrantedAuthorityDto roleTypeToGrantedAuthorityDto(RoleType data) {
return GrantedAuthorityDto.builder()
.authority(data.name())
.build();
}
default GrantedAuthorityDto grantedAuthorityToGrantedAuthority(GrantedAuthority data) {
return GrantedAuthorityDto.builder()
.authority(data.getAuthority())
.build();
}
default UserDto principalToUserDto(Principal principal) {
final UsernamePasswordAuthenticationToken token = (UsernamePasswordAuthenticationToken) principal;
return UserDto.builder()
.authorities(token.getAuthorities()
.stream()
.map(this::grantedAuthorityToGrantedAuthority)
.collect(Collectors.toList()))
.build();
}
......
package at.tuwien.service;
import at.tuwien.api.user.JwtResponseDto;
import at.tuwien.api.user.LoginRequestDto;
import at.tuwien.api.auth.JwtResponseDto;
import at.tuwien.api.auth.LoginRequestDto;
public interface AuthenticationService {
......
package at.tuwien.service;
import at.tuwien.api.user.SignupRequestDto;
import at.tuwien.api.user.UserDto;
import at.tuwien.api.auth.SignupRequestDto;
import at.tuwien.entities.user.User;
import at.tuwien.exception.RoleNotFoundException;
import at.tuwien.exception.UserEmailExistsException;
......
package at.tuwien.service.impl;
import at.tuwien.api.user.JwtResponseDto;
import at.tuwien.api.user.LoginRequestDto;
import at.tuwien.api.user.UserDto;
import at.tuwien.api.auth.JwtResponseDto;
import at.tuwien.api.auth.LoginRequestDto;
import at.tuwien.auth.JwtUtils;
import at.tuwien.mapper.UserMapper;
import at.tuwien.service.AuthenticationService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
import java.util.stream.Collectors;
@Slf4j
@Service
public class AuthenticationServiceImpl implements AuthenticationService {
private final JwtUtils jwtUtils;
private final UserMapper userMapper;
private final AuthenticationManager authenticationManager;
@Autowired
public AuthenticationServiceImpl(JwtUtils jwtUtils, AuthenticationManager authenticationManager) {
public AuthenticationServiceImpl(JwtUtils jwtUtils, UserMapper userMapper,
AuthenticationManager authenticationManager) {
this.jwtUtils = jwtUtils;
this.userMapper = userMapper;
this.authenticationManager = authenticationManager;
}
@Override
@Transactional
public JwtResponseDto authenticate(LoginRequestDto data) {
final UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(data.getUsername(), data.getPassword());
final UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(data.getUsername(),
data.getPassword());
final Authentication authentication = authenticationManager.authenticate(token);
SecurityContextHolder.getContext().setAuthentication(authentication);
final String jwt = jwtUtils.generateJwtToken(authentication);
final UserDto userDetails = (UserDto) authentication.getPrincipal();
final List<String> roles = userDetails.getAuthorities().stream()
.map(GrantedAuthority::getAuthority)
.collect(Collectors.toList());
return JwtResponseDto.builder()
.id(userDetails.getId())
.username(userDetails.getUsername())
.email(userDetails.getEmail())
.roles(roles)
.token(jwt)
.build();
final JwtResponseDto response = userMapper.principalToJwtResponseDto(authentication.getPrincipal());
response.setToken(jwtUtils.generateJwtToken(authentication));
return response;
}
}
package at.tuwien.service.impl;
import at.tuwien.api.user.UserDetailsDto;
import at.tuwien.entities.user.User;
import at.tuwien.mapper.UserMapper;
import at.tuwien.repositories.UserRepository;
......@@ -9,6 +10,9 @@ import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.stream.Collectors;
@Slf4j
@Service
......@@ -24,10 +28,18 @@ public class UserDetailsServiceImpl implements UserDetailsService {
}
@Override
@Transactional(readOnly = true)
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
final User user = userRepository.findByUsername(username)
.orElseThrow(() -> new UsernameNotFoundException("User Not Found with username: " + username));
return userMapper.userToUserDto(user);
log.debug("loaded user {}", user);
final UserDetailsDto details = userMapper.userToUserDetailsDto(user);
details.setAuthorities(user.getRoles()
.stream()
.map(userMapper::roleTypeToGrantedAuthority)
.collect(Collectors.toList()));
log.debug("mapped user {}", details);
return details;
}
}
package at.tuwien.service.impl;
import at.tuwien.api.user.SignupRequestDto;
import at.tuwien.api.auth.SignupRequestDto;
import at.tuwien.entities.user.RoleType;
import at.tuwien.entities.user.User;
import at.tuwien.exception.RoleNotFoundException;
......@@ -79,4 +79,5 @@ public class UserServiceImpl implements UserService {
log.debug("created user {}", entity);
return entity;
}
}
......@@ -38,7 +38,7 @@ public class ContainerEndpoint {
}
@Transactional
@GetMapping("/")
@GetMapping
@ApiOperation(value = "List all containers", notes = "Lists the containers in the metadata database.")
@ApiResponses({
@ApiResponse(code = 200, message = "All containers are listed."),
......@@ -53,7 +53,7 @@ public class ContainerEndpoint {
}
@Transactional
@PostMapping("/")
@PostMapping
@ApiOperation(value = "Creates a new container", notes = "Creates a new container whose image is registered in the metadata database too.")
@ApiResponses({
@ApiResponse(code = 201, message = "Successfully created a new container."),
......
......@@ -43,7 +43,7 @@ public class ImageEndpoint {
}
@Transactional
@GetMapping("/")
@GetMapping
@ApiOperation(value = "List all images", notes = "Lists the images in the metadata database.")
@ApiResponses({
@ApiResponse(code = 200, message = "All images are listed."),
......@@ -58,7 +58,7 @@ public class ImageEndpoint {
}
@Transactional
@PostMapping("/")
@PostMapping
@ApiOperation(value = "Creates a new image", notes = "Creates a new image in the metadata database.")
@ApiResponses({
@ApiResponse(code = 201, message = "Successfully created a new image."),
......
......@@ -26,4 +26,4 @@ eureka:
client.serviceUrl.defaultZone: http://fda-discovery-service:9090/eureka/
fda:
ready.path: /ready
auth.url: http://fda-authentication-service:9097/api/auth
\ No newline at end of file
auth.endpoint: http://fda-authentication-service:9097
\ No newline at end of file
......@@ -26,4 +26,4 @@ eureka:
client.serviceUrl.defaultZone: http://localhost:9090/eureka/
fda:
ready.path: ./ready
auth.url: http://localhost:9097/api/auth
\ No newline at end of file
auth.endpoint: http://localhost:9097
\ No newline at end of file
package at.tuwien.auth;
import at.tuwien.gateway.AuthenticationServiceGateway;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;
......@@ -17,16 +17,21 @@ import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@Slf4j
@Component
public class AuthTokenFilter extends OncePerRequestFilter {
private final AuthenticationServiceGateway authenticationServiceGateway;
public AuthTokenFilter(AuthenticationServiceGateway authenticationServiceGateway) {
this.authenticationServiceGateway = authenticationServiceGateway;
}
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
String jwt = parseJwt(request);
log.debug("parsed jwt {}", jwt);
if (jwt != null) {
final UserDetails userDetails = userDetailsService.loadUserByUsername(username);
final UserDetails userDetails = authenticationServiceGateway.validate(jwt);
final UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(
userDetails, null, userDetails.getAuthorities());
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
......
package at.tuwien.config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.DefaultUriBuilderFactory;
@Configuration
public class GatewayConfig {
@Value("${fda.auth.endpoint}")
private String authEndpoint;
@Bean
public RestTemplate restTemplate() {
final RestTemplate restTemplate = new RestTemplate();
restTemplate.setUriTemplateHandler(new DefaultUriBuilderFactory(authEndpoint));
return restTemplate;
}
}
package at.tuwien.config;
import at.tuwien.auth.AuthTokenFilter;
import at.tuwien.gateway.AuthenticationServiceGateway;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
......@@ -9,6 +11,7 @@ import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
......@@ -20,9 +23,16 @@ import javax.servlet.http.HttpServletResponse;
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
private final AuthenticationServiceGateway authenticationServiceGateway;
@Autowired
public WebSecurityConfig(AuthenticationServiceGateway authenticationServiceGateway) {
this.authenticationServiceGateway = authenticationServiceGateway;
}
@Bean
public AuthTokenFilter authTokenFilter() {
return new AuthTokenFilter()
return new AuthTokenFilter(authenticationServiceGateway);
}
@Override
......@@ -51,6 +61,10 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
.antMatchers(HttpMethod.GET, "/api/image/**").permitAll()
/* our private endpoints */
.anyRequest().authenticated();
/* add JWT token filter */
http.addFilterBefore(authTokenFilter(),
UsernamePasswordAuthenticationFilter.class
);
}
@Bean
......
package at.tuwien.gateway;
import org.springframework.security.core.userdetails.UserDetails;
public interface AuthenticationServiceGateway {
/**
* Validates a token
*
* @param token The token
* @return User details on success
*/
UserDetails validate(String token);
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment