diff --git a/users/src/main/java/com/mif13/authServer/AuthServerApplication.java b/users/src/main/java/com/mif13/authServer/AuthServerApplication.java index bfce5ec7384811668ab668f8e50013e24d3b35ce..001f838bf3436fb2a0dc4e000da2feb41758ef90 100644 --- a/users/src/main/java/com/mif13/authServer/AuthServerApplication.java +++ b/users/src/main/java/com/mif13/authServer/AuthServerApplication.java @@ -3,6 +3,7 @@ package com.mif13.authServer; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; + // extends SpringBootServletInitializer for .war generation @SpringBootApplication public class AuthServerApplication extends SpringBootServletInitializer { diff --git a/users/src/main/java/com/mif13/authServer/controllers/UserOperations.java b/users/src/main/java/com/mif13/authServer/controllers/UserOperations.java index 0fe38b45333e903cf25a4cf7e18877a02e044d8a..bcff91e85d212fc3467a8bcf6bfd8de9f7e3bcaa 100644 --- a/users/src/main/java/com/mif13/authServer/controllers/UserOperations.java +++ b/users/src/main/java/com/mif13/authServer/controllers/UserOperations.java @@ -3,30 +3,25 @@ package com.mif13.authServer.controllers; import com.mif13.authServer.dao.UsersDao; import com.mif13.authServer.model.User; import com.mif13.authServer.utils.JwtHelper; - -import java.io.Console; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; import javax.naming.AuthenticationException; -import javax.ws.rs.POST; - import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.annotation.Bean; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; import org.springframework.util.MultiValueMap; import org.springframework.util.MultiValueMapAdapter; -import org.springframework.web.bind.annotation.*; - -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.media.Content; -import io.swagger.v3.oas.annotations.responses.ApiResponse; -import io.swagger.v3.oas.annotations.responses.ApiResponses; -import org.springframework.web.servlet.config.annotation.CorsRegistry; -import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestHeader; +import org.springframework.web.bind.annotation.RequestParam; @Controller public class UserOperations { @@ -38,7 +33,6 @@ public class UserOperations { this.usersRepo = usersRepo; } - /** * Procédure de login utilisée par un utilisateur * @@ -53,10 +47,10 @@ public class UserOperations { @ApiResponse( responseCode = "204 No Content", description = "User succesfully logged in", - content = { - @Content () - } - ), + content = { + @Content() + } + ), @ApiResponse( responseCode = "401 Unauthorized", description = "Login failed", @@ -120,16 +114,15 @@ public class UserOperations { return response; } - @Operation(summary = "Réalise la déconnexion.") @ApiResponses(value = { @ApiResponse( responseCode = "204 No Content", description = "User succesfully logged out", - content = { - @Content () - } - ), + content = { + @Content() + } + ), @ApiResponse( responseCode = "401 Unauthorized", description = "", @@ -160,7 +153,7 @@ public class UserOperations { } catch (Exception e) { e.printStackTrace(); response = new ResponseEntity<>(HttpStatus.UNAUTHORIZED); - } + } return response; } @@ -177,16 +170,16 @@ public class UserOperations { @ApiResponse( responseCode = "204 No Content", description = "", - content = { - @Content () - } - ) + content = { + @Content() + } + ) }) @GetMapping("/authenticate") public ResponseEntity<Void> authenticate(@RequestParam("jwt") String jwt, @RequestParam("origin") String origin) { ResponseEntity<Void> response = new ResponseEntity<>(HttpStatus.NO_CONTENT); - + return response; } } \ No newline at end of file diff --git a/users/src/main/java/com/mif13/authServer/controllers/UserRestController.java b/users/src/main/java/com/mif13/authServer/controllers/UserRestController.java index e7f903c6ef8a89c53a7d894faf900c2a5c73dc45..e02838a01662372441f5d30004b4e1bd52b1c1e5 100644 --- a/users/src/main/java/com/mif13/authServer/controllers/UserRestController.java +++ b/users/src/main/java/com/mif13/authServer/controllers/UserRestController.java @@ -1,32 +1,37 @@ package com.mif13.authServer.controllers; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; import com.mif13.authServer.dao.UsersDao; import com.mif13.authServer.exception.InvalidPasswordException; +import com.mif13.authServer.exception.InvalidUsernameException; import com.mif13.authServer.exception.UserCreationException; import com.mif13.authServer.model.User; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; import java.util.Optional; import java.util.regex.PatternSyntaxException; - - import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.annotation.Bean; -import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.ui.Model; -import org.springframework.web.bind.annotation.*; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.server.ResponseStatusException; import org.springframework.web.servlet.ModelAndView; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.media.Content; -import io.swagger.v3.oas.annotations.media.Schema; -import io.swagger.v3.oas.annotations.responses.ApiResponse; -import io.swagger.v3.oas.annotations.responses.ApiResponses; -import org.springframework.web.servlet.config.annotation.CorsRegistry; -import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; - - @RestController @RequestMapping(value = "users") public class UserRestController { @@ -64,24 +69,20 @@ public class UserRestController { } )} ) - @GetMapping(value = "/{id}", produces = {MediaType.APPLICATION_JSON_VALUE, MediaType.APPLICATION_XML_VALUE}) + @GetMapping(value = "/{id}", produces = {MediaType.APPLICATION_JSON_VALUE, + MediaType.APPLICATION_XML_VALUE}) //@CrossOrigin(origins = {"http://localhost", "http://192.168.75.68", "https://192.168.75.68"}) public ResponseEntity<User> getUserAsJsonOrXml(@PathVariable String id) { - ResponseEntity<User> response; Optional<User> optionalUser = usersRepo.get(id); if (optionalUser.isPresent()) { User user = optionalUser.get(); - response = new ResponseEntity<>(user, HttpStatus.OK); - return response; + return new ResponseEntity<>(user, HttpStatus.OK); } else { - response = new ResponseEntity<>(HttpStatus.NOT_FOUND); + throw new ResponseStatusException(HttpStatus.NOT_FOUND, "User not found"); } - - return response; } - @GetMapping(value = "/{id}", produces = MediaType.TEXT_HTML_VALUE) public ModelAndView getUserAsHtml(@PathVariable String id, Model model) { // "user" nom du template HTML (sans extension) @@ -104,23 +105,45 @@ public class UserRestController { return modelAndView; } - private ResponseEntity<Void> createUser(User user) { - ResponseEntity<Void> response; + private ResponseEntity<Void> createUserResponse(String login, String password) { try { - Optional<User> optionalUser = usersRepo.get(user.getLogin()); - if (optionalUser.isEmpty()) { - usersRepo.save(user); - response = new ResponseEntity<>(HttpStatus.CREATED); - } else { - response = new ResponseEntity<>(HttpStatus.FORBIDDEN); + User user = new User(login, password); + + try { + HttpStatus statusCode; + Optional<User> optionalUser = usersRepo.get(user.getLogin()); + if (optionalUser.isEmpty()) { + usersRepo.save(user); + statusCode = HttpStatus.CREATED; + } else { + statusCode = HttpStatus.FORBIDDEN; + } + return new ResponseEntity<>(statusCode); + + } catch (PatternSyntaxException e) { + // Si les patterns RegEx de validation sont pas valides + e.printStackTrace(); + throw new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR); } - } catch (PatternSyntaxException e) { + + } catch (UserCreationException e) { e.printStackTrace(); - response = new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR); + + String errMsg; + if (e instanceof InvalidUsernameException) { + errMsg = "Login does not match the requirements"; + } else if (e instanceof InvalidPasswordException) { + errMsg = "Password does not match the requirements"; + } else { + errMsg = "Failed to create a user"; + } + + // Par defaut, pour des raisons de securite, + // le message d'erreur n'est pas affiche a l'utilisateur + throw new ResponseStatusException(HttpStatus.BAD_REQUEST, errMsg, e); } - return response; } @Operation(summary = "Create a new user") @@ -159,33 +182,30 @@ public class UserRestController { @PostMapping(consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE) public ResponseEntity<Void> createUserFromUrlEncoded(@RequestParam("login") String login, @RequestParam("password") String password) { - ResponseEntity<Void> response; - - try { - User user = new User(login, password); - response = createUser(user); - } catch (UserCreationException e) { - e.printStackTrace(); - response = new ResponseEntity<>(HttpStatus.BAD_REQUEST); - } - return response; + return createUserResponse(login, password); } /** * Cree un User a partir d'un JSON. * - * /!\ Attention /!\ - * Renvoie 400 BAD_REQUEST si le login ou le password sont invalides - * (et produit une HttpMessageNotReadableException) - * - * @param user JSON converti en User (converter de Spring) + * @param json JSON contenant un login et un password * @return contenu vide **/ @PostMapping(consumes = MediaType.APPLICATION_JSON_VALUE) - public ResponseEntity<Void> createUserFromJson(@RequestBody User user) { - return createUser(user); - } + public ResponseEntity<Void> createUserFromJson(@RequestBody String json) { + + try { + ObjectMapper objectMapper = new ObjectMapper(); + JsonNode jsonTree = objectMapper.readTree(json); + String login = jsonTree.get("login").asText(); + String password = jsonTree.get("password").asText(); + return createUserResponse(login, password); + } catch (JsonProcessingException e) { + e.printStackTrace(); + throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Failed to process JSON", e); + } + } @Operation(summary = "Modify password of a user by its id") @ApiResponses(value = { @@ -211,10 +231,28 @@ public class UserRestController { } ) }) - @PutMapping("/{id}") - public ResponseEntity<Void> modifyUserPassword(@PathVariable String id, - @RequestParam("new_password") String newPassword) { - ResponseEntity<Void> response; + @PutMapping(value = "/{id}", consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE) + public ResponseEntity<Void> modifyUserPasswordFromUrlEncoded(@PathVariable String id, + @RequestParam("newPassword") String newPassword) { + return modifyUserPassword(id, newPassword); + } + + @PutMapping(value = "/{id}", consumes = MediaType.APPLICATION_JSON_VALUE) + public ResponseEntity<Void> modifyUserPasswordFromJson(@PathVariable String id, + @RequestBody String json) { + try { + ObjectMapper objectMapper = new ObjectMapper(); + JsonNode jsonTree = objectMapper.readTree(json); + String newPassword = jsonTree.get("newPassword").asText(); + return modifyUserPassword(id, newPassword); + + } catch (JsonProcessingException e) { + e.printStackTrace(); + throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Failed to process JSON", e); + } + } + + private ResponseEntity<Void> modifyUserPassword(String id, String newPassword) { Optional<User> optionalUser = usersRepo.get(id); if (optionalUser.isPresent()) { @@ -223,20 +261,19 @@ public class UserRestController { try { if (User.verifyPassword(newPassword)) { user.setPassword(newPassword); - response = new ResponseEntity<>(HttpStatus.NO_CONTENT); + return new ResponseEntity<>(HttpStatus.NO_CONTENT); } else { - throw new InvalidPasswordException("Password is too weak"); + throw new InvalidPasswordException("Password does not match the requirements"); } } catch (InvalidPasswordException e) { e.printStackTrace(); - response = new ResponseEntity<>(HttpStatus.BAD_REQUEST); + throw new ResponseStatusException(HttpStatus.BAD_REQUEST, e.getMessage(), e); } } else { - response = new ResponseEntity<>(HttpStatus.NOT_FOUND); + throw new ResponseStatusException(HttpStatus.NOT_FOUND, "User not found"); } - return response; } @Operation(summary = "Delete a user on the database, by its id") @@ -265,19 +302,16 @@ public class UserRestController { }) @DeleteMapping("/{id}") public ResponseEntity<Void> deleteUser(@PathVariable String id) { - ResponseEntity<Void> response; Optional<User> optionalUser = usersRepo.get(id); if (optionalUser.isPresent()) { User user = optionalUser.get(); usersRepo.delete(user); - response = new ResponseEntity<>(HttpStatus.NO_CONTENT); + return new ResponseEntity<>(HttpStatus.NO_CONTENT); } else { - response = new ResponseEntity<>(HttpStatus.NOT_FOUND); + throw new ResponseStatusException(HttpStatus.NOT_FOUND, "User not found"); } - - return response; } } diff --git a/users/src/main/java/com/mif13/authServer/dao/Dao.java b/users/src/main/java/com/mif13/authServer/dao/Dao.java index 66c972c723702656fd407b8d277f208e8a86b66a..9714e15756dfda404febe147070961778803a639 100644 --- a/users/src/main/java/com/mif13/authServer/dao/Dao.java +++ b/users/src/main/java/com/mif13/authServer/dao/Dao.java @@ -4,8 +4,10 @@ import java.util.Optional; import java.util.Set; public interface Dao<T> { + /** * Récupère un utilisateur enregistré + * * @param id Login de l'utilisateur * @return Un java.util.Optional qui contient (ou pas) l'utilisateur */ @@ -13,25 +15,29 @@ public interface Dao<T> { /** * Récupère tous les utilisateurs enregistrés + * * @return Un Set de login */ Set<String> getAll(); /** * Crée un utilisateur et le sauvegarde + * * @param t L'utilisateur à créer */ void save(T t); /** * Modifie un utilisateur enregistré - * @param t L'utilisateur à modifier + * + * @param t L'utilisateur à modifier * @param password Le nouveau password */ void update(T t, String password); /** * Supprime un utilisateur enregistré + * * @param t L'utilisateur à supprimer */ void delete(T t); diff --git a/users/src/main/java/com/mif13/authServer/exception/InvalidPasswordException.java b/users/src/main/java/com/mif13/authServer/exception/InvalidPasswordException.java index 9365e49baee47e0eac75c9df18eb8c647ec907d9..52eb63430a36bc58b8ac33f84d59e06471524ee6 100644 --- a/users/src/main/java/com/mif13/authServer/exception/InvalidPasswordException.java +++ b/users/src/main/java/com/mif13/authServer/exception/InvalidPasswordException.java @@ -1,7 +1,10 @@ package com.mif13.authServer.exception; public class InvalidPasswordException extends UserCreationException { - public InvalidPasswordException() {} + + public InvalidPasswordException() { + } + public InvalidPasswordException(String errorMessage) { super(errorMessage); } diff --git a/users/src/main/java/com/mif13/authServer/exception/InvalidUsernameException.java b/users/src/main/java/com/mif13/authServer/exception/InvalidUsernameException.java index ab7558b4c0ccfa8061877331b4a369d137ab9df6..1d63742dafcdfa23f460780f2eaf696a114801bc 100644 --- a/users/src/main/java/com/mif13/authServer/exception/InvalidUsernameException.java +++ b/users/src/main/java/com/mif13/authServer/exception/InvalidUsernameException.java @@ -1,7 +1,10 @@ package com.mif13.authServer.exception; public class InvalidUsernameException extends UserCreationException { - public InvalidUsernameException() {} + + public InvalidUsernameException() { + } + public InvalidUsernameException(String errorMessage) { super(errorMessage); } diff --git a/users/src/main/java/com/mif13/authServer/exception/UserCreationException.java b/users/src/main/java/com/mif13/authServer/exception/UserCreationException.java index 89831e30ed87e7a38d2aecbd360270b19fbaaccb..dfe63a8235bc5e576f544afcf77655fd6fb2c4bd 100644 --- a/users/src/main/java/com/mif13/authServer/exception/UserCreationException.java +++ b/users/src/main/java/com/mif13/authServer/exception/UserCreationException.java @@ -1,7 +1,10 @@ package com.mif13.authServer.exception; public class UserCreationException extends Exception { - public UserCreationException() {} + + public UserCreationException() { + } + public UserCreationException(String errorMessage) { super(errorMessage); } diff --git a/users/src/main/java/com/mif13/authServer/model/User.java b/users/src/main/java/com/mif13/authServer/model/User.java index b2bda88b408241f124967a0fe30c57713dda3b25..35fafdaff9ed05c0a471bf7a8c1994958e86b8b3 100644 --- a/users/src/main/java/com/mif13/authServer/model/User.java +++ b/users/src/main/java/com/mif13/authServer/model/User.java @@ -7,6 +7,7 @@ import java.util.regex.PatternSyntaxException; import javax.naming.AuthenticationException; public class User { + private final String login; private String password; @@ -26,6 +27,17 @@ public class User { } } + public static boolean verifyLogin(String login) throws PatternSyntaxException { + String regex = "^[a-zA-Z][a-zA-Z0-9._-]{3,20}$"; + return login.matches(regex); + } + + public static boolean verifyPassword(String password) throws PatternSyntaxException { + String regex = + "^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[!@#&()–[{}]:;',?/*~$^+=<>]).{8,25}$"; + return password.matches(regex); + } + public String getLogin() { return login; } @@ -59,15 +71,4 @@ public class User { "^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[!@#&()–[{}]:;',?/*~$^+=<>]).{8,25}$"; return password.matches(regex); } - - public static boolean verifyLogin(String login) throws PatternSyntaxException { - String regex = "^[a-zA-Z][a-zA-Z0-9._-]{3,20}$"; - return login.matches(regex); - } - - public static boolean verifyPassword(String password) throws PatternSyntaxException { - String regex = - "^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[!@#&()–[{}]:;',?/*~$^+=<>]).{8,25}$"; - return password.matches(regex); - } } \ No newline at end of file diff --git a/users/src/main/java/com/mif13/authServer/utils/CorsConfig.java b/users/src/main/java/com/mif13/authServer/utils/CorsConfig.java index f20be3745b320bc445c867ec0774563dbf3598ad..984b06c1a68c51829a994476406e77915efbf722 100644 --- a/users/src/main/java/com/mif13/authServer/utils/CorsConfig.java +++ b/users/src/main/java/com/mif13/authServer/utils/CorsConfig.java @@ -9,35 +9,39 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; public class CorsConfig { @Bean - public WebMvcConfigurer configure(){ + public WebMvcConfigurer configure() { return new WebMvcConfigurer() { @Override - public void addCorsMappings(CorsRegistry registry){ - + public void addCorsMappings(CorsRegistry registry) { + // CORS for /login POST - CORS pas reglé registry.addMapping("/login") .allowedMethods("POST") .allowedHeaders("*", "Authorization") - .allowedOrigins("http://localhost", "http://192.168.75.68", "https://192.168.75.68","https://localhost"); + .allowedOrigins("http://localhost", "http://192.168.75.68", + "https://192.168.75.68", "https://localhost"); // CORS for /lougout POST - CORS pas reglé registry.addMapping("/logout") .allowedMethods("POST") .allowedHeaders("*", "Authorization") - .allowedOrigins("http://localhost", "http://192.168.75.68", "https://192.168.75.68","https://localhost"); + .allowedOrigins("http://localhost", "http://192.168.75.68", + "https://192.168.75.68", "https://localhost"); // CORS for /authenticate GET - Fonctionne registry.addMapping("/authenticate") .allowedMethods("GET") .allowedHeaders("*") - .allowedOrigins("http://localhost", "http://192.168.75.68", "https://192.168.75.68","https://localhost"); + .allowedOrigins("http://localhost", "http://192.168.75.68", + "https://192.168.75.68", "https://localhost"); // CORS for /users/{id} GET - Fonctionne registry.addMapping("/{id}") .allowedMethods("GET") .allowedHeaders("*") - .allowedOrigins("http://localhost", "http://192.168.75.68", "https://192.168.75.68","https://localhost"); - + .allowedOrigins("http://localhost", "http://192.168.75.68", + "https://192.168.75.68", "https://localhost"); + } }; } diff --git a/users/src/main/java/com/mif13/authServer/utils/JwtHelper.java b/users/src/main/java/com/mif13/authServer/utils/JwtHelper.java index b3dfa6e9a58af3e44823d998908ad8fd0040ff00..c66b1fc5a8e690bc80a633255e03a05737328e82 100644 --- a/users/src/main/java/com/mif13/authServer/utils/JwtHelper.java +++ b/users/src/main/java/com/mif13/authServer/utils/JwtHelper.java @@ -6,9 +6,8 @@ import com.auth0.jwt.exceptions.JWTCreationException; import com.auth0.jwt.exceptions.JWTVerificationException; import com.auth0.jwt.interfaces.DecodedJWT; import com.auth0.jwt.interfaces.JWTVerifier; - -import javax.validation.constraints.NotNull; import java.util.Date; +import javax.validation.constraints.NotNull; /** * Classe qui centralise les opérations de validation et de génération d'un token "métier", @@ -67,5 +66,4 @@ public class JwtHelper { .sign(algorithm); } - }