Skip to content

Commit

Permalink
Merge pull request #132 from Tech-Harbor/Bezsmertnyi
Browse files Browse the repository at this point in the history
Bezsmertnyi
  • Loading branch information
Vladik-gif authored Jun 1, 2024
2 parents 057c196 + 7a4ea6a commit 79f93c4
Show file tree
Hide file tree
Showing 62 changed files with 608 additions and 303 deletions.
1 change: 1 addition & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ dependencies {
implementation group: 'org.springdoc', name: 'springdoc-openapi-starter-webmvc-ui', version: '2.3.0'
implementation group: 'org.springframework.boot', name: 'spring-boot-starter-validation', version: '3.2.1'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation group: 'org.springframework.boot', name: 'spring-boot-starter-websocket', version: '3.2.2'
implementation group: 'org.springframework.boot', name: 'spring-boot-starter-mail', version: '3.2.1'
implementation group: 'org.springframework.boot', name: 'spring-boot-configuration-processor', version: '3.2.0'
}
Expand Down
2 changes: 2 additions & 0 deletions documentation/Requirements.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
4.2.1 Приклад: loginTest - ця назва дає зрозуміти що Тест перевіряє успішність авторизації.
4.2.2 Приклад: loginNotTest - ця назва дає зрозуміти що Тест перевіряє не успішність авторизації.
3.3 Тести проводяться на рівні серверного слоя API.
3.4 Тести потрібно писати тоді, коли сервесна частина додатку не буде змінюватися в цілому, а тільки частково
3.4.1 Тести пишуться лише тоді, коли закінчилася розробка сервесної частини
4 GraphQL
4.1 Якщо запит має багато даних, то треба переробляти самий цей запит на GraphQL.
5 Загальні вимоги до всього API
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
contact = @Contact(),
title = "Oranger API ",
description = "Documentation for access to the Oranger resources via REST API.",
version = "1.1.0"
version = "1.2.1"
)
)
@SecurityScheme(
Expand Down
25 changes: 10 additions & 15 deletions src/main/java/com/example/backend/mail/MailServiceImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import com.example.backend.security.service.JwtTokenService;
import com.example.backend.web.User.store.dto.UserSecurityDTO;
import freemarker.template.Configuration;
import jakarta.mail.internet.MimeMessage;
import lombok.AllArgsConstructor;
import lombok.SneakyThrows;
import org.springframework.mail.javamail.JavaMailSender;
Expand All @@ -12,7 +11,6 @@

import java.io.StringWriter;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

import static com.example.backend.utils.general.Constants.JWT;
Expand All @@ -37,10 +35,9 @@ public void sendEmail(final UserSecurityDTO user, final MailType type, final Pro

@SneakyThrows
private void sendRegistrationEmail(final UserSecurityDTO user) {
MimeMessage mimeMessage = mailSender.createMimeMessage();
String emailContent = getRegistrationEmailContent(user);

MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, false, UTF_8);
final var mimeMessage = mailSender.createMimeMessage();
final var emailContent = getRegistrationEmailContent(user);
final var helper = new MimeMessageHelper(mimeMessage, false, UTF_8);

helper.setSubject("???, " + user.lastname());
helper.setTo(user.email());
Expand All @@ -51,8 +48,8 @@ private void sendRegistrationEmail(final UserSecurityDTO user) {

@SneakyThrows
private String getRegistrationEmailContent(final UserSecurityDTO user) {
StringWriter writer = new StringWriter();
Map<String, Object> model = new HashMap<>();
final var writer = new StringWriter();
final var model = new HashMap<String, Object>();

model.put("username", user.lastname());
model.put(JWT, jwtTokenService.generateUserEmailDataToken(user));
Expand All @@ -64,10 +61,9 @@ private String getRegistrationEmailContent(final UserSecurityDTO user) {

@SneakyThrows
private void sendNewPassword(final UserSecurityDTO user) {
MimeMessage mimePasswordMessage = mailSender.createMimeMessage();
String passwordContent = getNewPasswordContent(user);

MimeMessageHelper helper = new MimeMessageHelper(mimePasswordMessage, false, UTF_8);
final var mimePasswordMessage = mailSender.createMimeMessage();
final var passwordContent = getNewPasswordContent(user);
final var helper = new MimeMessageHelper(mimePasswordMessage, false, UTF_8);

helper.setSubject("Update Password, " + user.lastname());
helper.setTo(user.email());
Expand All @@ -78,9 +74,8 @@ private void sendNewPassword(final UserSecurityDTO user) {

@SneakyThrows
private String getNewPasswordContent(final UserSecurityDTO user) {
StringWriter writer = new StringWriter();

Map<String, Object> model = new HashMap<>();
final var writer = new StringWriter();
final var model = new HashMap<String, Object>();

model.put("username", user.lastname());
model.put(JWT, jwtTokenService.generateUserPasswordDataToken(user));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public class AuthenticationConfig {

@Bean
public AuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
final var authProvider = new DaoAuthenticationProvider();
authProvider.setUserDetailsService(myUserDetailsService);
authProvider.setPasswordEncoder(myPasswordEncoder.passwordEncoder());
return authProvider;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
import com.example.backend.security.models.response.AuthResponse;
import com.example.backend.security.service.AuthService;
import com.example.backend.utils.annotations.*;
import com.example.backend.web.User.store.dto.UserInfoDTO;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import io.swagger.v3.oas.annotations.tags.Tag;
Expand All @@ -23,7 +22,7 @@
@AllArgsConstructor
@RequestMapping("/api")
@Slf4j
@Tag(name = "Authentication", description = "Authentication User and Update Password, personal office users")
@Tag(name = "Authentication")
public class AuthController {

private final AuthService authService;
Expand All @@ -33,7 +32,6 @@ public class AuthController {
private static final String FORM_CHANGE_PASSWORD_URI = "/change-password";
private static final String REQUEST_EMAIL_UPDATE_PASSWORD = "/request/email";
private static final String ACTIVE_USER = "/active";
private static final String PROFILE_USER = "/profile";
private static final String SEND_MESSAGE_EMAIL_NOT_ACTIVE = "/sendMessageEmailActive";

@PostMapping(SIGNUP_URI)
Expand Down Expand Up @@ -65,13 +63,6 @@ public void updatePassword(@RequestHeader(AUTHORIZATION) final String jwt,
authService.formUpdatePassword(jwt, passwordRequest);
}

@GetMapping(PROFILE_USER)
@Operation(summary = "Information about the user who is authorized and logged into the system")
@ApiResponseInfoOK
public UserInfoDTO profile(@RequestHeader(AUTHORIZATION) final String accessToken) {
return authService.profileUser(accessToken);
}

@PostMapping(REQUEST_EMAIL_UPDATE_PASSWORD)
@Operation(summary = "Change password using email")
@ApiResponseEmailOK
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package com.example.backend.security.jwt;

import com.example.backend.security.service.JwtService;
import com.example.backend.security.service.details.MyUserDetails;
import com.example.backend.security.service.details.MyUserDetailsService;
import jakarta.servlet.FilterChain;
import jakarta.servlet.http.HttpServletRequest;
Expand Down Expand Up @@ -32,9 +31,9 @@ protected void doFilterInternal(
final HttpServletResponse response,
final FilterChain filterChain) {

var jwt = getTokenHeaders(request);
final var jwt = getTokenHeaders(request);

var userData = getExtractUserData(jwt);
final var userData = getExtractUserData(jwt);

getSecurityContextHolder(request, userData, jwt);

Expand Down Expand Up @@ -63,7 +62,7 @@ private String getExtractUserData(final String jwt) {
private void getSecurityContextHolder(final HttpServletRequest request, final String userData, final String jwt) {
if (StringUtils.isNoneEmpty(userData) && SecurityContextHolder.getContext().getAuthentication() == null) {

var userDetails = (MyUserDetails) userDetailsService.loadUserByUsername(userData);
final var userDetails = userDetailsService.loadUserByUsername(userData);

if (jwtService.isTokenValid(jwt, userDetails)) {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@
import jakarta.validation.constraints.NotNull;
import lombok.Builder;

import static com.example.backend.utils.general.Constants.EMPTY_FIELD;

@Builder
public record AuthRequest(
@NotNull @NotBlank(message = "Поле не повинно бути порожнім") @Email String email,
@NotNull @NotBlank(message = "Поле не повинно бути порожнім") String password) { }
@NotNull @NotBlank(message = EMPTY_FIELD) @Email String email,
@NotNull @NotBlank(message = EMPTY_FIELD) String password) { }
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,24 @@
import jakarta.validation.constraints.Email;
import lombok.Builder;

import static com.example.backend.utils.general.Constants.EMPTY_FIELD;

@Builder
public record RegisterRequest(
@NotNull @NotBlank(message = "Поле не повинно бути порожнім")
@Size(min = 2, max = 20, message = "Довжина firstname має бути між 2 та 20 символами")
@NotNull @NotBlank(message = EMPTY_FIELD)
@Size(min = 2, max = 20, message = "Довжина lastname має бути між 2 та 20 символами")
@Pattern(regexp = "^[A-Z][a-z]*(\\s(([a-z]{1,3})|(([a-z]+\\')?[A-Z][a-z]*)))*$",
message = "lastname має відповідати вказаному формату") String lastname,
@NotNull @NotBlank(message = "Поле не повинно бути порожнім")
@NotNull @NotBlank(message = EMPTY_FIELD)
@Size(min = 2, max = 20, message = "Довжина firstname має бути між 2 та 20 символами")
@Pattern(regexp = "^[A-Z][a-z]*(\\s(([a-z]{1,3})|(([a-z]+\\')?[A-Z][a-z]*)))*$",
message = "firstname має відповідати вказаному формату") String firstname,
@NotNull @NotBlank(message = "Поле не повинно бути порожнім")
@NotNull @NotBlank(message = EMPTY_FIELD)
@Size(min = 12, max = 12, message = "Номер не валідний, повинен містить 12 символів") String phone,
@NotNull @NotBlank(message = "Поле не повинно бути порожнім")
@NotNull @NotBlank(message = EMPTY_FIELD)
@Email(message = "Введіть коректну адресу електронної пошти з @")
@Size(min = 7, max = 35, message = "Довжина email має бути між 7 та 35 символами") String email,
@NotNull @NotBlank(message = "Поле не повинно бути порожнім")
@NotNull @NotBlank(message = EMPTY_FIELD)
@Size(min = 7, max = 20, message = "Довжина password має бути між 7 та 20 символами")
@Pattern(regexp = "^(?=.*\\d)[A-Za-z\\d]+$",
message = "password має відповідати вказаному формату") String password) { }
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
import com.example.backend.security.models.request.PasswordRequest;
import com.example.backend.security.models.request.RegisterRequest;
import com.example.backend.security.models.response.AuthResponse;
import com.example.backend.web.User.store.dto.UserInfoDTO;

public interface AuthService {
/**
Expand Down Expand Up @@ -55,12 +54,4 @@ public interface AuthService {
* This method sends a letter to the user's mail if he did not have time to activate the account the first time
*/
void sendEmailActive(EmailRequest emailRequest);
/**
* Retrieves user information based on a JWT token.
*
* @param accessToken The JWT token used for identifying and extracting user data
* @return UserInfoDTO containing the user's information
* @throws RuntimeException if a user with the extracted data is not found
*/
UserInfoDTO profileUser(String accessToken);
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import com.example.backend.web.User.UserService;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.stereotype.Service;

Expand All @@ -18,7 +17,7 @@ public class MyUserDetailsService implements UserDetailsService {

@Override
@SneakyThrows
public UserDetails loadUserByUsername(final String username) {
public MyUserDetails loadUserByUsername(final String username) {
var user = userService.getBySecurityEmail(username).orElseThrow(
() -> notFoundRequestException("Email not found")
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,25 +8,24 @@
import com.example.backend.security.models.request.RegisterRequest;
import com.example.backend.security.models.response.AuthResponse;
import com.example.backend.security.service.AuthService;
import com.example.backend.security.service.JwtService;
import com.example.backend.security.service.JwtTokenService;
import com.example.backend.utils.general.Helpers;
import com.example.backend.utils.general.MyPasswordEncoder;
import com.example.backend.web.User.UserService;
import com.example.backend.web.User.store.UserEntity;
import com.example.backend.web.User.store.dto.UserInfoDTO;
import com.example.backend.web.User.store.factory.UserInfoFactory;
import jakarta.transaction.Transactional;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;
import jakarta.transaction.Transactional;

import java.util.Properties;

import static com.example.backend.utils.enums.RegisterAuthStatus.JWT;
import static com.example.backend.utils.enums.Role.USER;
import static com.example.backend.utils.enums.Status.OFFLINE;
import static com.example.backend.utils.exception.RequestException.badRequestException;

@Service
Expand All @@ -37,10 +36,9 @@ public class AuthServiceImpl implements AuthService {
private final AuthenticationManager authenticationManager;
private final MyPasswordEncoder myPasswordEncoder;
private final JwtTokenService jwtTokenService;
private final UserInfoFactory userInfoFactory;
private final UserService userService;
private final MailService mailService;
private final JwtService jwtService;
private final Helpers helpers;

@Override
@Transactional
Expand All @@ -61,6 +59,7 @@ public void signup(final RegisterRequest registerRequest) {
.registerAuthStatus(JWT)
.enabled(false)
.role(USER)
.status(OFFLINE)
.build();

final var userSecurityDTO = userService.mySecuritySave(user);
Expand Down Expand Up @@ -98,9 +97,7 @@ public AuthResponse login(final AuthRequest authRequest) {
@Override
@Transactional
public void formUpdatePassword(final String jwt, final PasswordRequest passwordRequest) {
final var token = jwtService.extractUserData(jwt.substring(7));

var userPassword = userService.getByEmail(token);
final var userPassword = helpers.tokenUserEmail(jwt);

userPassword.ifPresent(user -> {
user.setPassword(myPasswordEncoder.passwordEncoder().encode(passwordRequest.password()));
Expand Down Expand Up @@ -128,9 +125,7 @@ public void requestEmailUpdatePassword(final EmailRequest emailRequest) {
@Override
@Transactional
public void activeUser(final String jwt) {
final var token = jwtService.extractUserData(jwt.substring(7));

final var activeUserTrue = userService.getByEmail(token);
final var activeUserTrue = helpers.tokenUserEmail(jwt);

activeUserTrue.ifPresent(user -> {
user.setEnabled(true);
Expand All @@ -155,15 +150,4 @@ public void sendEmailActive(final EmailRequest emailRequest) {
}
);
}

@Override
public UserInfoDTO profileUser(final String accessToken) {
final var token = jwtService.extractUserData(accessToken.substring(7));

final var user = userService.getByUserData(token);

log.info("Info {}", user);

return userInfoFactory.apply(user);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.example.backend.utils.annotations;

import io.swagger.v3.oas.annotations.responses.ApiResponse;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@ApiResponse(responseCode = "204", description = "No Content")
@ResponseStatus(value = HttpStatus.NO_CONTENT)
public @interface ApiResponseDelete { }
5 changes: 5 additions & 0 deletions src/main/java/com/example/backend/utils/enums/Status.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.example.backend.utils.enums;

public enum Status {
ONLINE, OFFLINE
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ public class Constants {
public static final String TYPE = "type";
public static final String ROLE = "role";
public static final String BEARER_AUTHENTICATION = "Bearer Authentication";
public static final String EMPTY_FIELD = "Поле не повинно бути порожнім";
public static final Date DATE_TIME_MILLIS = new Date(System.currentTimeMillis());
public static final String[] PERMIT_ALL = {
"/graphiql"
Expand Down
Loading

0 comments on commit 79f93c4

Please sign in to comment.