Add application creation route.

This commit is contained in:
2019-09-01 18:10:57 +02:00
parent 006ce222d0
commit 5ab829ff64
16 changed files with 267 additions and 24 deletions

View File

@@ -0,0 +1,31 @@
package org.cerberus.controllers;
import org.cerberus.entities.persistence.Application;
import org.cerberus.entities.persistence.User;
import org.cerberus.services.ApplicationService;
import org.cerberus.services.SecurityService;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.security.Principal;
@RestController
@RequestMapping("/api/applications")
public class ApplicationController {
private ApplicationService applicationService;
private SecurityService securityService;
public ApplicationController(ApplicationService applicationService,
SecurityService securityService) {
this.applicationService = applicationService;
this.securityService = securityService;
}
@PostMapping
public Application create(@RequestBody Application application, Principal connectedUser) {
User user = securityService.getAdminUser(connectedUser);
return applicationService.create(application, user);
}
}

View File

@@ -1,13 +1,9 @@
package org.cerberus.controllers;
import org.cerberus.core.config.security.CustomAuthenticationProvider;
import org.cerberus.entities.dto.SignUpDTO;
import org.cerberus.entities.persistence.User;
import org.cerberus.repositories.UserRepository;
import org.cerberus.services.UserService;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler;
import org.springframework.web.bind.annotation.*;
@@ -15,6 +11,8 @@ import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import static org.springframework.http.HttpStatus.NO_CONTENT;
@RestController
@RequestMapping("/api/users")
public class UserController {
@@ -25,23 +23,23 @@ public class UserController {
}
@PostMapping("/login")
public void login(@RequestBody User user, HttpServletResponse response) {
@ResponseStatus(NO_CONTENT)
public void login(@RequestBody User user) {
userService.authenticate(user);
response.setStatus(HttpServletResponse.SC_NO_CONTENT);
}
@GetMapping("/disconnection")
@ResponseStatus(NO_CONTENT)
public void disconnection(HttpServletRequest request, HttpServletResponse response) {
final Authentication auth = SecurityContextHolder.getContext().getAuthentication();
if(auth != null) {
new SecurityContextLogoutHandler().logout(request, response, auth);
}
response.setStatus(HttpServletResponse.SC_NO_CONTENT);
}
@PostMapping("/signup")
public void signUp(@RequestBody SignUpDTO inputData, HttpServletResponse response) {
@ResponseStatus(NO_CONTENT)
public void signUp(@RequestBody SignUpDTO inputData) {
userService.signUp(inputData);
response.setStatus(HttpServletResponse.SC_NO_CONTENT);
}
}

View File

@@ -1,5 +1,6 @@
package org.cerberus.core.config.security;
import org.cerberus.core.constant.RoleSecurity;
import org.springframework.boot.autoconfigure.security.SecurityProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
@@ -19,7 +20,10 @@ import static org.springframework.http.HttpMethod.POST;
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
@EnableGlobalMethodSecurity(
prePostEnabled = true,
securedEnabled = true
)
@Order(SecurityProperties.BASIC_AUTH_ORDER)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@@ -41,8 +45,9 @@ public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
// Permits all
http.httpBasic()
.and()
.authorizeRequests()
.antMatchers(
"/robots.txt"
).permitAll()
@@ -51,8 +56,10 @@ public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
).permitAll()
.antMatchers(POST,
"/api/users/login",
"/api/users/signup"
"/api/users/signup",
"/api/applications"
).permitAll()
.antMatchers("/api/**").authenticated()
.anyRequest().permitAll()
.and()
// Allow to avoid login form at authentication failure from Angular app
@@ -60,9 +67,8 @@ public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
.and()
.addFilterAfter(new XSRFTokenFilter(), CsrfFilter.class)
.csrf()
.csrfTokenRepository(xsrfTokenRepository());
http.httpBasic();
http.csrf().disable();
.csrfTokenRepository(xsrfTokenRepository())
.disable();
}
private CsrfTokenRepository xsrfTokenRepository() {

View File

@@ -0,0 +1,16 @@
package org.cerberus.core.exceptions;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;
@ResponseStatus(value = HttpStatus.FORBIDDEN)
public class ForbiddenException extends BusinessException {
public ForbiddenException(String message) {
super(message);
}
public ForbiddenException(String message, Throwable cause) {
super(message, cause);
}
}

View File

@@ -0,0 +1,16 @@
package org.cerberus.core.exceptions;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;
@ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
public class InternalServerErrorException extends BusinessException {
public InternalServerErrorException(String message) {
super(message);
}
public InternalServerErrorException(String message, Throwable cause) {
super(message, cause);
}
}

View File

@@ -3,6 +3,7 @@ package org.cerberus.entities.persistence;
import org.hibernate.annotations.Proxy;
import javax.persistence.*;
import java.util.LinkedList;
import java.util.List;
import java.util.UUID;
@@ -19,10 +20,10 @@ public class Application {
@Column(nullable = false)
private String serviceName;
@OneToMany(mappedBy = "application")
@OneToMany(mappedBy = "application", cascade = CascadeType.ALL)
private List<ConfigurationFile> configurationFileList;
@OneToMany(mappedBy = "application")
@OneToMany(mappedBy = "application", cascade = CascadeType.ALL)
private List<ApplicationRole> administratorList;
@PrePersist
@@ -55,6 +56,9 @@ public class Application {
}
public List<ConfigurationFile> getConfigurationFileList() {
if(configurationFileList == null) {
configurationFileList = new LinkedList<>();
}
return configurationFileList;
}
@@ -63,6 +67,9 @@ public class Application {
}
public List<ApplicationRole> getAdministratorList() {
if(administratorList == null) {
administratorList = new LinkedList<>();
}
return administratorList;
}

View File

@@ -73,6 +73,7 @@ public class ApplicationRole {
}
public void setUser(User user) {
getId().setUserId(user.getId());
this.user = user;
}
@@ -81,6 +82,7 @@ public class ApplicationRole {
}
public void setApplication(Application application) {
getId().setApplicationId(application.getId());
this.application = application;
}
}

View File

@@ -72,7 +72,7 @@ public class User {
this.password = password;
}
public Boolean getAdmin() {
public Boolean isAdmin() {
return isAdmin;
}

View File

@@ -0,0 +1,9 @@
package org.cerberus.repositories;
import org.cerberus.entities.persistence.Application;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.UUID;
public interface ApplicationRepository extends JpaRepository<Application, UUID> {
}

View File

@@ -0,0 +1,11 @@
package org.cerberus.repositories;
import org.cerberus.entities.persistence.ApplicationRole;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.UUID;
@Repository
public interface ApplicationRoleRepository extends JpaRepository<ApplicationRole, UUID> {
}

View File

@@ -8,8 +8,9 @@ import org.springframework.data.repository.query.Param;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
public interface UserRepository extends JpaRepository<User, String> {
public interface UserRepository extends JpaRepository<User, UUID> {
@Query("SELECT u FROM User u WHERE u.email = :email")
Optional<User> findByEmail(@Param("email") String email);
@@ -18,4 +19,7 @@ public interface UserRepository extends JpaRepository<User, String> {
@Query(value = "SELECT EXISTS(SELECT id FROM \"user\" WHERE email = :email)", nativeQuery = true)
boolean isEmailAlreadyExists(@Param("email") String email);
@Query("SELECT isAdmin FROM User u WHERE u.id = :id")
boolean isAdmin(UUID id);
}

View File

@@ -0,0 +1,26 @@
package org.cerberus.services;
import org.cerberus.core.constant.Role;
import org.cerberus.entities.persistence.Application;
import org.cerberus.entities.persistence.ApplicationRole;
import org.cerberus.entities.persistence.User;
import org.cerberus.repositories.ApplicationRoleRepository;
import org.springframework.stereotype.Service;
@Service
public class ApplicationRoleService {
private ApplicationRoleRepository applicationRoleRepository;
public ApplicationRoleService(ApplicationRoleRepository applicationRoleRepository) {
this.applicationRoleRepository = applicationRoleRepository;
}
public void create(Application application, User user, Role role) {
ApplicationRole applicationRole = new ApplicationRole();
applicationRole.setApplication(application);
applicationRole.setUser(user);
applicationRole.setRole(role);
applicationRoleRepository.save(applicationRole);
}
}

View File

@@ -0,0 +1,40 @@
package org.cerberus.services;
import org.cerberus.core.constant.Role;
import org.cerberus.core.exceptions.InternalServerErrorException;
import org.cerberus.entities.persistence.Application;
import org.cerberus.entities.persistence.ApplicationRole;
import org.cerberus.entities.persistence.User;
import org.cerberus.repositories.ApplicationRepository;
import org.cerberus.validators.ApplicationValidator;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import static org.cerberus.core.constant.Role.MAINTAINER;
@Service
public class ApplicationService {
private ApplicationRepository applicationRepository;
private ApplicationRoleService applicationRoleService;
private ApplicationValidator applicationValidator;
public ApplicationService(ApplicationRepository applicationRepository,
ApplicationRoleService applicationRoleService,
ApplicationValidator applicationValidator) {
this.applicationRepository = applicationRepository;
this.applicationRoleService = applicationRoleService;
this.applicationValidator = applicationValidator;
}
@Transactional
public Application create(Application application, User user) {
applicationValidator.checkAllAttributsConstraints(application);
applicationRepository.save(application);
// Application creator is by default a maintainer
applicationRoleService.create(application, user, MAINTAINER);
return application;
}
}

View File

@@ -0,0 +1,42 @@
package org.cerberus.services;
import org.cerberus.core.exceptions.ForbiddenException;
import org.cerberus.entities.persistence.User;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;
import java.security.Principal;
import java.util.Optional;
@Service
public class SecurityService {
private UserService userService;
public SecurityService(UserService userService) {
this.userService = userService;
}
/**
* Returns the connected user if he's an administrator. Otherwise, a {@link ForbiddenException} will be thrown.
* @param connectedUser The connectedUser
*/
public User getAdminUser(Principal connectedUser) {
Optional<User> user = getUserByPrincipal(connectedUser);
if(user.isEmpty() || !userService.isAdmin(user.get())) {
throw new ForbiddenException("Illegal access attempt.");
}
return user.get();
}
public Optional<User> getUserByPrincipal(final Principal pPrincipal) {
Optional<User> result = Optional.empty();
if(pPrincipal != null) {
SecurityContextHolder.getContext().getAuthentication();
result = userService.findByEmail(pPrincipal.getName());
}
return result;
}
}

View File

@@ -2,6 +2,7 @@ package org.cerberus.services;
import org.cerberus.core.constant.Role;
import org.cerberus.core.config.security.CustomAuthenticationProvider;
import org.cerberus.core.constant.RoleSecurity;
import org.cerberus.core.exceptions.BadRequestException;
import org.cerberus.entities.dto.SignUpDTO;
import org.cerberus.entities.persistence.ApplicationRole;
@@ -36,29 +37,38 @@ public class UserService {
}
public void authenticate(User user) {
checkCredentials(user.getEmail(), user.getPassword());
User authenticatedUser = checkCredentials(user.getEmail(), user.getPassword());
authenticationProvider.authenticate(new UsernamePasswordAuthenticationToken(
user.getEmail(),
user.getPassword(),
fetchGrantedAuthorities(user)
fetchGrantedAuthorities(authenticatedUser)
));
}
void checkCredentials(String email, String password) {
User checkCredentials(String email, String password) {
Optional<User> optUser = userRepository.findByEmail(email);
if(optUser.isEmpty() || !optUser.get().getPassword().equals(password)) {
throw new BadRequestException("Credentials are incorrect.");
}
return optUser.get();
}
Collection<GrantedAuthority> fetchGrantedAuthorities(User user) {
return userRepository.getApplicationRolesByEmail(user.getEmail()).stream()
Collection<GrantedAuthority> grantedAuthorityCollection = userRepository.getApplicationRolesByEmail(user.getEmail())
.stream()
.map(ApplicationRole::getRole)
.map(Role::name)
.map(SimpleGrantedAuthority::new)
.collect(Collectors.toSet());
if(user.isAdmin()) {
grantedAuthorityCollection.add(new SimpleGrantedAuthority(RoleSecurity.ADMIN));
}
return grantedAuthorityCollection;
}
public void signUp(SignUpDTO inputData) {
@@ -70,4 +80,12 @@ public class UserService {
userRepository.save(signUpMapper.toUser(inputData));
}
public boolean isAdmin(User user) {
return userRepository.isAdmin(user.getId());
}
public Optional<User> findByEmail(String email) {
return userRepository.findByEmail(email);
}
}

View File

@@ -0,0 +1,17 @@
package org.cerberus.validators;
import org.cerberus.core.exceptions.BadRequestException;
import org.cerberus.core.utils.StringUtils;
import org.cerberus.entities.persistence.Application;
import org.springframework.stereotype.Component;
@Component
public class ApplicationValidator {
public void checkAllAttributsConstraints(Application application) {
if(StringUtils.isNull(application.getName())
|| StringUtils.isNull(application.getServiceName())) {
throw new BadRequestException("Please fill up all required fields.");
}
}
}