diff --git a/index.html b/index.html index 97e7a21..d743f89 100644 --- a/index.html +++ b/index.html @@ -1,5 +1,5 @@ - + diff --git a/src/main/java/org/waterproofingdata/wpdauth/controller/UsersController.java b/src/main/java/org/waterproofingdata/wpdauth/controller/UsersController.java index 406993a..7a77be9 100644 --- a/src/main/java/org/waterproofingdata/wpdauth/controller/UsersController.java +++ b/src/main/java/org/waterproofingdata/wpdauth/controller/UsersController.java @@ -31,68 +31,153 @@ public class UsersController { @Autowired private UsersService userService; -/* - @ApiParam( - name = "firstName", - type = "String", - value = "First Name of the user", - example = "Vatsal", - required = true) - @RequestParam String firstName) - */ @PostMapping("/login") - @ApiOperation(value = "${UserController.login}") + @ApiOperation( + value = "${UserController.login}", + notes = "From a valid username and password, this method returns the JWT Token to be used in secure methods." + ) @ApiResponses(value = {// - @ApiResponse(code = 400, message = "Something went wrong"), // - @ApiResponse(code = 422, message = "Invalid username/password supplied")}) + @ApiResponse(code = 400, message = "Something went wrong"), // + @ApiResponse(code = 404, message = "Invalid username/password supplied") + } + ) public String login(// - @ApiParam("Username") @RequestParam String username, // - @ApiParam("Password") @RequestParam String password) { - return userService.login(username, password); + @ApiParam( + name = "username", + type = "String", + value = "username of the user", + example = "This is an unique field, and consumers should be aware of it. By convention, WP6 should send the user phone number (i.e. (99)99999-9999).", + required = true + ) + @RequestParam String username, // + @ApiParam( + name = "password", + type = "String", + value = "password of the user", + example = "i.e. P@s5w0rD", + required = true + ) + @RequestParam String password + ) { + return userService.login(username, password); } @PostMapping("/signup") - @ApiOperation(value = "${UserController.signup}") + @ApiOperation( + value = "${UserController.signup}", + notes = "This the signup method to create new users. By defaul all users are created as inactive. To activate, the method ${UserController.activate} should be invoked." + ) @ApiResponses(value = {// - @ApiResponse(code = 400, message = "Something went wrong"), // - @ApiResponse(code = 403, message = "Access denied"), // - @ApiResponse(code = 422, message = "Username is already in use")}) - public String signup(@ApiParam("Signup User") @RequestBody UsersRequestDTO user) { - return userService.signup(CustomMapper.map(user, Users.class)); + @ApiResponse(code = 400, message = "Something went wrong"), // + @ApiResponse(code = 422, message = "Required parameters should be provided") + } + ) + public String signup( + @ApiParam( + name = "user", + value = "Signup User", + required = true + ) + @RequestBody UsersRequestDTO user + ) { + return userService.signup(CustomMapper.map(user, Users.class)); } @PostMapping("/sendadminkeybyemailcemaden") - @ApiOperation(value = "${UserController.sendadminkeybyemailcemaden}") + @PreAuthorize("hasRole('ROLE_INSTITUTION')") + @ApiOperation( + value = "${UserController.sendadminkeybyemailcemaden}", + authorizations = {@Authorization(value="apiKey")}, + notes = "This method is used for role 'ROLE_INSTITUTION'. To activate these users, a key is sent to the EduCemandenOrg e-mail and the user should inform this key to proceed." + ) @ApiResponses(value = {// - @ApiResponse(code = 400, message = "Something went wrong"), // - @ApiResponse(code = 403, message = "Access denied"), // - @ApiResponse(code = 422, message = "Username is already in use")}) - public void sendadminkeybyemailcemaden(@ApiParam("Emailcemaden") @PathVariable String emailcemaden, @ApiParam("Username") @PathVariable String username) { - userService.sendAdminKeyByEmailCemaden(emailcemaden, username); + @ApiResponse(code = 400, message = "Something went wrong"), // + @ApiResponse(code = 403, message = "Access denied"), // + @ApiResponse(code = 404, message = "User or Email Cemaden not found"), // + @ApiResponse(code = 500, message = "Expired or invalid JWT token") + } + ) + public void sendadminkeybyemailcemaden( + @ApiParam( + name = "emailcemaden", + type = "String", + value = "Emailcemaden associated to the user", + example = "The Cemaden e-mail registred in the database. The key will be sent to this e-mail, and the user should be inform this key to proceed.", + required = true + ) + @RequestParam String emailcemaden, // + @ApiParam( + name = "username", + type = "String", + value = "username of the user", + example = "This is an unique field, and consumers should be aware of it. By convention, WP6 should send the user phone number (i.e. (99)99999-9999).", + required = true + ) + @RequestParam String username + ) { + userService.sendAdminKeyByEmailCemaden(emailcemaden, username); } @PostMapping("/activate") @PreAuthorize("hasRole('ROLE_INSTITUTION') or hasRole('ROLE_CLIENT')") - @ApiOperation(value = "${UserController.activate}", authorizations = { @Authorization(value="apiKey") }) + @ApiOperation( + value = "${UserController.activate}", + authorizations = { @Authorization(value="apiKey") }, + notes = "This is the user activation method." + ) @ApiResponses(value = {// @ApiResponse(code = 400, message = "Something went wrong"), // @ApiResponse(code = 403, message = "Access denied"), // - @ApiResponse(code = 404, message = "The user doesn't exist"), // - @ApiResponse(code = 500, message = "Expired or invalid JWT token")}) - public String activate(@ApiParam("Username") @PathVariable String username, @ApiParam("ActivationKey") @PathVariable String activationkey) { - userService.activate(username, activationkey); - return username; + @ApiResponse(code = 422, message = "User or ActivationKey registration issues"), // + @ApiResponse(code = 500, message = "Expired or invalid JWT token") + } + ) + public String activate( + @ApiParam( + name = "username", + type = "String", + value = "username of the user", + example = "This is an unique field, and consumers should be aware of it. By convention, WP6 should send the user phone number (i.e. (99)99999-9999).", + required = true + ) + @RequestParam String username, // + @ApiParam( + name = "activationkey", + type = "String", + value = "Activation Key to activate the user", + example = "If user belongs to 'ROLE_INSTITUTION' the key should be collected from the emailcemaden, previously sent by ${UserController.sendadminkeybyemailcemaden}. If the user belongs to 'ROLE_CLIENT' the key should be provided by a 'ROLE_INSTITUTION' valid user.", + required = true + ) + @RequestParam String activationkey + ) { + userService.activate(username, activationkey); + return username; } @GetMapping(value = "/{username}") @PreAuthorize("hasRole('ROLE_ADMIN')") - @ApiOperation(value = "${UserController.search}", response = UsersResponseDTO.class, authorizations = { @Authorization(value="apiKey") }) + @ApiOperation( + value = "${UserController.search}", + response = UsersResponseDTO.class, + authorizations = { @Authorization(value="apiKey") }, + notes = "This is the user search method by username." + ) @ApiResponses(value = {// - @ApiResponse(code = 400, message = "Something went wrong"), // - @ApiResponse(code = 403, message = "Access denied"), // - @ApiResponse(code = 404, message = "The user doesn't exist"), // - @ApiResponse(code = 500, message = "Expired or invalid JWT token")}) - public UsersResponseDTO search(@ApiParam("Username") @PathVariable String username) { + @ApiResponse(code = 403, message = "Access denied"), // + @ApiResponse(code = 404, message = "The user doesn't exist"), // + @ApiResponse(code = 500, message = "Expired or invalid JWT token") + } + ) + public UsersResponseDTO search( + @ApiParam( + name = "username", + type = "String", + value = "username of the user", + example = "This is an unique field, and consumers should be aware of it. By convention, WP6 should send the user phone number (i.e. (99)99999-9999).", + required = true + ) + @RequestParam String username + ) { UsersResponseDTO urDTO = CustomMapper.map(userService.search(username), UsersResponseDTO.class); urDTO.setEduCemadenOrganization(userService.findEduCemadenOrganizationById(urDTO.getId())); urDTO.setProviderActivationKey(userService.findProviderActivationKeyById(urDTO.getId())); @@ -101,7 +186,12 @@ public class UsersController { @GetMapping(value = "/me") @PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_CLIENT')") - @ApiOperation(value = "${UserController.me}", response = UsersResponseDTO.class, authorizations = { @Authorization(value="apiKey") }) + @ApiOperation( + value = "${UserController.me}", + response = UsersResponseDTO.class, + authorizations = { @Authorization(value="apiKey") }, + notes = "This is the user search method by token." + ) @ApiResponses(value = {// @ApiResponse(code = 400, message = "Something went wrong"), // @ApiResponse(code = 403, message = "Access denied"), // diff --git a/src/main/java/org/waterproofingdata/wpdauth/service/UsersService.java b/src/main/java/org/waterproofingdata/wpdauth/service/UsersService.java index df5c1f7..fae0c1c 100644 --- a/src/main/java/org/waterproofingdata/wpdauth/service/UsersService.java +++ b/src/main/java/org/waterproofingdata/wpdauth/service/UsersService.java @@ -6,6 +6,7 @@ import javax.servlet.http.HttpServletRequest; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; +import org.springframework.mail.MailException; import org.springframework.mail.SimpleMailMessage; import org.springframework.mail.javamail.JavaMailSender; import org.springframework.security.authentication.AuthenticationManager; @@ -66,11 +67,18 @@ public class UsersService { public String login(String username, String password) { try { - authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username, password)); - return jwtTokenProvider.createToken(username, usersRepository.findByUsername(username).getRoles()); + Users u = search(username); + authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username, password)); + return jwtTokenProvider.createToken(username, u.getRoles()); } - catch (AuthenticationException e) { - throw new CustomException("Invalid username/password supplied", HttpStatus.NOT_FOUND); + catch (CustomException ce) { + throw ce; + } + catch (AuthenticationException ae) { + throw new CustomException("Invalid username/password supplied", HttpStatus.NOT_FOUND); + } + catch (Exception e) { + throw new CustomException("Something went wrong", HttpStatus.BAD_REQUEST); } } @@ -105,8 +113,13 @@ public class UsersService { user.setPassword(passwordEncoder.encode(user.getPassword())); user.setActive(0); - usersRepository.save(user); - return jwtTokenProvider.createToken(user.getUsername(), user.getRoles()); + try { + usersRepository.save(user); + return jwtTokenProvider.createToken(user.getUsername(), user.getRoles()); + } + catch (Exception e) { + throw new CustomException("Something went wrong", HttpStatus.BAD_REQUEST); + } } else { throw new CustomException("Username is already in use", HttpStatus.UNPROCESSABLE_ENTITY); @@ -126,7 +139,12 @@ public class UsersService { message.setTo(emailcemaden); message.setSubject("Envio de código para alteração de senha"); message.setText(String.format("Olá! O usuário '%s' solicitou a ativação dele para ADMIN dessa Instituição, por isso você está recebendo esse código: '%s'. Se estiver correto, informe esse código ao solicitante e peça para entrar no aplicativo para prosseguir.", user.getNickname(), uuid)); - mailSender.send(message); + try { + mailSender.send(message); + } + catch (MailException me) { + throw new CustomException("Something went wrong", HttpStatus.BAD_REQUEST); + } UsersEducemadenOrganizations userEducemadenOrg = new UsersEducemadenOrganizations(); userEducemadenOrg.setUsersid(user.getId()); @@ -189,7 +207,7 @@ public class UsersService { throw new CustomException("Admin users should be activated through database.", HttpStatus.UNPROCESSABLE_ENTITY); } else { - throw new CustomException("There is a problem with this User registration and it can not be activated.", HttpStatus.UNPROCESSABLE_ENTITY); + throw new CustomException("There is a problem with this User registration and it can not be activated.", HttpStatus.BAD_REQUEST); } } diff --git a/src/test/java/org/waterproofingdata/wpdauth/integrationtest/UsersServiceIntegrationTest.java b/src/test/java/org/waterproofingdata/wpdauth/integrationtest/UsersServiceIntegrationTest.java index 0bec2dc..f511474 100644 --- a/src/test/java/org/waterproofingdata/wpdauth/integrationtest/UsersServiceIntegrationTest.java +++ b/src/test/java/org/waterproofingdata/wpdauth/integrationtest/UsersServiceIntegrationTest.java @@ -57,7 +57,7 @@ public class UsersServiceIntegrationTest { "Expected usersService.login(xpto, xpto) to throw, but it didn't" ); - assertTrue(thrown.getMessage().contains("Invalid username/password supplied")); + assertTrue(thrown.getMessage().contains("The user doesn't exist")); assertEquals(HttpStatus.NOT_FOUND, thrown.getHttpStatus()); } diff --git a/swagger.yaml b/swagger.yaml index a48e722..4f0393d 100644 --- a/swagger.yaml +++ b/swagger.yaml @@ -37,12 +37,12 @@ paths: - Authorization: - global deprecated: false - /forgotpasswords/loginbyemailandanswers: + /forgotpasswords/loginbyusernameandanswers: post: tags: - forgotpasswords - summary: ${ForgotPasswordsController.loginbyemailandanswers} - operationId: loginbyemailandanswersUsingPOST + summary: ${ForgotPasswordsController.loginbyusernameandanswers} + operationId: loginbyusernameandanswersUsingPOST consumes: - application/json produces: @@ -56,9 +56,9 @@ paths: type: array items: $ref: '#/definitions/ForgotPasswordsQuestionsUsersAnswersRequestDTO' - - name: email + - name: username in: query - description: Email + description: Username required: false type: string allowEmptyValue: false @@ -77,26 +77,26 @@ paths: - Authorization: - global deprecated: false - /forgotpasswords/loginbyemailandkey: + /forgotpasswords/loginbyusernameandkey: post: tags: - forgotpasswords - summary: ${ForgotPasswordsController.loginbyemailandkey} - operationId: loginbyemailandkeyUsingPOST + summary: ${ForgotPasswordsController.loginbyusernameandkey} + operationId: loginbyusernameandkeyUsingPOST consumes: - application/json produces: - '*/*' parameters: - - name: email + - name: key in: query - description: Email + description: Key required: false type: string allowEmptyValue: false - - name: key + - name: username in: query - description: Key + description: Username required: false type: string allowEmptyValue: false @@ -113,26 +113,26 @@ paths: - Authorization: - global deprecated: false - /forgotpasswords/passwordupdatebyemail: + /forgotpasswords/passwordupdatebyusername: post: tags: - forgotpasswords - summary: ${ForgotPasswordsController.passwordupdatebyemail} - operationId: passwordupdatebyemailUsingPOST + summary: ${ForgotPasswordsController.passwordupdatebyusername} + operationId: passwordupdatebyusernameUsingPOST consumes: - application/json produces: - '*/*' parameters: - - name: email + - name: newPassword in: query - description: Email + description: NewPassword required: false type: string allowEmptyValue: false - - name: newPassword + - name: username in: query - description: NewPassword + description: Username required: false type: string allowEmptyValue: false @@ -202,6 +202,12 @@ paths: required: false type: string allowEmptyValue: false + - name: username + in: query + description: Username + required: false + type: string + allowEmptyValue: false responses: '200': description: OK @@ -218,6 +224,7 @@ paths: tags: - users summary: ${UserController.activate} + description: This is the user activation method. operationId: activateUsingPOST consumes: - application/json @@ -225,15 +232,19 @@ paths: - '*/*' parameters: - name: activationkey - in: path - description: ActivationKey - required: false + in: query + description: Activation Key to activate the user + required: true type: string + allowEmptyValue: false + x-example: If user belongs to 'ROLE_INSTITUTION' the key should be collected from the emailcemaden, previously sent by ${UserController.sendadminkeybyemailcemaden}. If the user belongs to 'ROLE_CLIENT' the key should be provided by a 'ROLE_INSTITUTION' valid user. - name: username - in: path - description: Username - required: false + in: query + description: username of the user + required: true type: string + allowEmptyValue: false + x-example: This is an unique field, and consumers should be aware of it. By convention, WP6 should send the user phone number (i.e. (99)99999-9999). responses: '200': description: OK @@ -243,8 +254,8 @@ paths: description: Something went wrong '403': description: Access denied - '404': - description: The user doesn't exist + '422': + description: User or ActivationKey registration issues '500': description: Expired or invalid JWT token security: @@ -257,6 +268,7 @@ paths: tags: - users summary: ${UserController.login} + description: From a valid username and password, this method returns the JWT Token to be used in secure methods. operationId: loginUsingPOST consumes: - application/json @@ -265,16 +277,18 @@ paths: parameters: - name: password in: query - description: Password - required: false + description: password of the user + required: true type: string allowEmptyValue: false + x-example: i.e. P@s5w0rD - name: username in: query - description: Username - required: false + description: username of the user + required: true type: string allowEmptyValue: false + x-example: This is an unique field, and consumers should be aware of it. By convention, WP6 should send the user phone number (i.e. (99)99999-9999). responses: '200': description: OK @@ -282,7 +296,7 @@ paths: type: string '400': description: Something went wrong - '422': + '404': description: Invalid username/password supplied security: - Authorization: @@ -293,6 +307,7 @@ paths: tags: - users summary: ${UserController.me} + description: This is the user search method by token. operationId: whoamiUsingGET produces: - '*/*' @@ -329,11 +344,54 @@ paths: - Authorization: - global deprecated: false + /users/sendadminkeybyemailcemaden: + post: + tags: + - users + summary: ${UserController.sendadminkeybyemailcemaden} + description: This method is used for role 'ROLE_INSTITUTION'. To activate these users, a key is sent to the EduCemandenOrg e-mail and the user should inform this key to proceed. + operationId: sendadminkeybyemailcemadenUsingPOST + consumes: + - application/json + produces: + - '*/*' + parameters: + - name: emailcemaden + in: query + description: Emailcemaden associated to the user + required: true + type: string + allowEmptyValue: false + x-example: The Cemaden e-mail registred in the database. The key will be sent to this e-mail, and the user should be inform this key to proceed. + - name: username + in: query + description: username of the user + required: true + type: string + allowEmptyValue: false + x-example: This is an unique field, and consumers should be aware of it. By convention, WP6 should send the user phone number (i.e. (99)99999-9999). + responses: + '200': + description: OK + '400': + description: Something went wrong + '403': + description: Access denied + '404': + description: User or Email Cemaden not found + '500': + description: Expired or invalid JWT token + security: + - Authorization: + - global + - apiKey: [] + deprecated: false /users/signup: post: tags: - users summary: ${UserController.signup} + description: This the signup method to create new users. By defaul all users are created as inactive. To activate, the method ${UserController.activate} should be invoked. operationId: signupUsingPOST consumes: - application/json @@ -343,7 +401,7 @@ paths: - in: body name: user description: Signup User - required: false + required: true schema: $ref: '#/definitions/UsersRequestDTO' responses: @@ -353,10 +411,8 @@ paths: type: string '400': description: Something went wrong - '403': - description: Access denied '422': - description: Username is already in use + description: Required parameters should be provided security: - Authorization: - global @@ -366,22 +422,23 @@ paths: tags: - users summary: ${UserController.search} + description: This is the user search method by username. operationId: searchUsingGET produces: - '*/*' parameters: - name: username - in: path - description: Username - required: false + in: query + description: username of the user + required: true type: string + allowEmptyValue: false + x-example: This is an unique field, and consumers should be aware of it. By convention, WP6 should send the user phone number (i.e. (99)99999-9999). responses: '200': description: OK schema: $ref: '#/definitions/UsersResponseDTO' - '400': - description: Something went wrong '403': description: Access denied '404': @@ -402,14 +459,14 @@ definitions: EduCemadenOrganizations: type: object properties: - activationkey: - type: string active: type: string address: type: string creation_date: type: string + email: + type: string id: type: integer format: int32 @@ -455,49 +512,185 @@ definitions: answer: type: string title: ForgotPasswordsQuestionsUsersAnswersRequestDTO - UsersRequestDTO: + UsersProviderActivationKey: type: object properties: - username: - type: string - password: + activationkey: type: string - title: UsersRequestDTO - UsersResponseDTO: - type: object - properties: id: type: integer format: int32 + usersid: + type: integer + format: int32 + title: UsersProviderActivationKey + UsersRequestDTO: + type: object + required: + - city + - nickname + - password + - roles + - state + - termsofusage + - username + properties: username: type: string + example: This is an unique field, and consumers should be aware of it. By convention, WP6 should send the user phone number (i.e. (99)99999-9999). + description: username of the user. + nickname: + type: string + example: i.e. beth2021. This is an unique field, and consumers should be aware of it. + description: nickname of the user. + password: + type: string + example: i.e. P@s5w0rD. + description: password of the user. + dateofborn: + type: string + format: date + example: i.e. 01/12/1978. Format should be dd/MM/yyyy. + description: Date of Born of the user. + gender: + type: string + example: i.e. M. 'M' stands for Male, 'F' Female, 'N' Not Informed + description: Gender of the user. + enum: + - '{@code M' + - F + - N} + state: + type: string + example: i.e. SP. State should be 2 characteres (UF) + description: state of the user. + city: + type: string + example: i.e. 'Governador Valadares'. + description: city of the user. + institutiontype: + type: string + example: i.e. 'E'. E stands for 'School', D 'Civil Defense', N 'No governamental', O 'others' + description: institution type of the user. + enum: + - '{@code E' + - D + - 'N' + - O} + institution: + type: string + example: i.e. 'Colegio Imaginario'. + description: institution of the user. + securityquestion: + type: string + example: i.e. 'What is my favorite color?'. + description: security question of the user. + securityanswer: + type: string + example: i.e. 'Blue'. + description: security answer of the user. + termsofusage: + type: boolean + example: false + description: terms of usage of the user. roles: type: array + example: i.e. ROLE_CLIENT. + description: roles of the user. items: type: string enum: - ROLE_ADMIN - ROLE_INSTITUTION - ROLE_CLIENT - eduCemadenOrganization: - $ref: '#/definitions/EduCemadenOrganizations' - rolesProviderActivationKeys: - type: array - items: - $ref: '#/definitions/UsersRolesproviderActivationKey' - title: UsersResponseDTO - UsersRolesproviderActivationKey: + title: UsersRequestDTO + UsersResponseDTO: type: object + required: + - active + - city + - id + - nickname + - state + - termsofusage + - username properties: - activationkey: - type: string id: type: integer format: int32 - rolesid: - type: integer - format: int32 - usersid: + example: This is a SERIAL and Primary Key field. + description: id of the user. + username: + type: string + example: This is an unique field, and consumers should be aware of it. By convention, WP6 should send the user phone number (i.e. (99)99999-9999). + description: username of the user. + nickname: + type: string + example: i.e. beth2021. This is an unique field, and consumers should be aware of it. + description: nickname of the user. + dateofborn: + type: string + format: date + example: i.e. 01/12/1978. Format should be dd/MM/yyyy. + description: Date of Born of the user. + gender: + type: string + example: i.e. M. 'M' stands for Male, 'F' Female, 'N' Not Informed + description: Gender of the user. + enum: + - '{@code M' + - F + - N} + state: + type: string + example: i.e. SP. State should be 2 characteres (UF) + description: state of the user. + city: + type: string + example: i.e. 'Governador Valadares'. + description: city of the user. + institutiontype: + type: string + example: i.e. 'E'. E stands for 'School', D 'Civil Defense', N 'No governamental', O 'others' + description: institution type of the user. + enum: + - '{@code E' + - D + - 'N' + - O} + institution: + type: string + example: i.e. 'Colegio Imaginario'. + description: institution of the user. + securityquestion: + type: string + example: i.e. 'What is my favorite color?'. + description: security question of the user. + securityanswer: + type: string + example: i.e. 'Blue'. + description: security answer of the user. + termsofusage: + type: boolean + example: false + description: terms of usage of the user. + active: type: integer format: int32 - title: UsersRolesproviderActivationKey + example: i.e. true. + description: whether user is active or not. + role: + type: string + example: i.e. 'ROLE_ADMIN' means system administrator, 'ROLE_INSTITUTION' means institution administrator, 'ROLE_CLIENT' means regular users + description: institution type of the user. + enum: + - '{@code ROLE_ADMIN' + - ROLE_INSTITUTION + - ROLE_CLIENT} + eduCemadenOrganization: + description: which Educational Cemaden Organization the user belongs. + $ref: '#/definitions/EduCemadenOrganizations' + providerActivationKey: + description: If this user can provide an activation key for other users ('ROLE_INSTITUTION'), this field will store the values. + $ref: '#/definitions/UsersProviderActivationKey' + title: UsersResponseDTO