Skip to content

Commit

Permalink
Merge pull request #114 from Tech-Harbor/Bezsmertnyi
Browse files Browse the repository at this point in the history
Bezsmertnyi
  • Loading branch information
Vladik-gif authored Apr 27, 2024
2 parents a76f39f + 1b4c347 commit ac8f918
Show file tree
Hide file tree
Showing 31 changed files with 365 additions and 289 deletions.
14 changes: 12 additions & 2 deletions documentation/API.http
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ Content-Type: application/json
Authorization: Bearer

{
"email": "[email protected]",
"password": "Password325231"
}

Expand Down Expand Up @@ -38,12 +39,21 @@ Content-Type: application/json

###Active User, JWT TOKEN
POST http://localhost:8080/api/active
Content-Type: application/x-www-form-urlencoded
Authorization: Bearer
Content-Type: application/json

{
"email": "[email protected]"
}

###Accouth
GET http://localhost:8080/api/accouth
Content-Type: application/x-www-form-urlencoded

###Active Not Users
POST http://localhost:8080/api/sendMessageEmail
POST http://localhost:8080/api/sendMessageEmail
Content-Type: application/json

{
"email": "[email protected]"
}
3 changes: 2 additions & 1 deletion documentation/Requirements.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
2.2.1 Назва метода повинна передавати основну логіку або функціональність, яку він виконує.
2.2.2 Не рекомендується створювати зміну в методі, яка безпосередньо виводиться в return,
обов'язково передавати це значення безпосередньо в return, замість зайвого створення змінної.
2.2.3 Якщо метод більше чим 10 строк коду, є обов'язковим розбивати метод на декілька дрібних методів.
2.3 Кожен інтерфейс повинен реалізовуватися в клас на рівні серверного слоя API.
3 UnitTest | методи, класи
3.1 МетодТест починається з void, обов'язково.
Expand All @@ -24,7 +25,7 @@
5 Загальні вимоги до всього API
5.1 Всі статичні оголошення (static declaration) добавляти в клас Constants, окрім класів Controller
(за потребою, можна добавляти також).
5.2 Якщо більше ніж два параметра використовується в класі, обов'язково створюємо статичні оголошення
5.2 Якщо більше ніж два параметра використовується в класі або API, обов'язково створюємо статичні оголошення
(static declaration) в класі Constants та добавляємо в потрібний клас.
5.3 Всі класи, які потребують більше, ніж два параметра, що передаються з файлу конфігурації application.yaml,
мають бути у вигляді класу Properties.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
import org.springframework.context.annotation.Configuration;
import org.springframework.graphql.execution.RuntimeWiringConfigurer;

import static com.example.backend.utils.Constants.LOCAL_TIME_DATE;
import static com.example.backend.utils.Constants.LOCAL_TIME_DATE_SCALAR;
import static com.example.backend.utils.general.Constants.LOCAL_TIME_DATE;
import static com.example.backend.utils.general.Constants.LOCAL_TIME_DATE_SCALAR;

@Configuration
public class GraphqlConfig {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
import java.util.Date;
import java.util.Locale;

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


public class LocalDateTimeScalarConfig implements Coercing<LocalDateTime, String> {
Expand Down
12 changes: 6 additions & 6 deletions src/main/java/com/example/backend/mail/MailServiceImpl.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package com.example.backend.mail;

import com.example.backend.security.service.JwtService;
import com.example.backend.security.service.JwtTokenService;
import com.example.backend.web.User.UserEntity;
import freemarker.template.Configuration;
import jakarta.mail.internet.MimeMessage;
Expand All @@ -15,16 +15,16 @@
import java.util.Map;
import java.util.Properties;

import static com.example.backend.utils.Constants.JWT;
import static com.example.backend.utils.Constants.UTF_8;
import static com.example.backend.utils.general.Constants.JWT;
import static com.example.backend.utils.general.Constants.UTF_8;

@Service
@AllArgsConstructor
public class MailServiceImpl implements MailService {

private JwtTokenService jwtTokenService;
private Configuration configuration;
private JavaMailSender mailSender;
private JwtService jwtService;

@Override
public void sendEmail(final UserEntity user, final MailType type, final Properties params) {
Expand Down Expand Up @@ -57,7 +57,7 @@ private String getRegistrationEmailContent(final UserEntity user) {
Map<String, Object> model = new HashMap<>();

model.put("username", user.getLastname());
model.put(JWT, jwtService.generateUserEmailDataToken(user));
model.put(JWT, jwtTokenService.generateUserEmailDataToken(user));

configuration.getTemplate("register.ftlh").process(model, writer);

Expand Down Expand Up @@ -87,7 +87,7 @@ private String getNewPasswordContent(final UserEntity user) {
Map<String, Object> model = new HashMap<>();

model.put("username", user.getLastname());
model.put(JWT, jwtService.generateUserPasswordDataToken(user));
model.put(JWT, jwtTokenService.generateUserPasswordDataToken(user));

configuration.getTemplate("newPassword.ftlh").process(model, writer);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.example.backend.security;

import com.example.backend.security.service.details.MyUserDetailsService;
import com.example.backend.utils.MyPasswordEncoder;
import com.example.backend.utils.general.MyPasswordEncoder;
import lombok.AllArgsConstructor;
import lombok.SneakyThrows;
import org.springframework.context.annotation.Bean;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import com.example.backend.security.exception.AuthenticationEntryPointJwt;
import com.example.backend.security.jwt.JwtAuthFilter;
import com.example.backend.security.oauth.AuthGoogle;
import com.example.backend.utils.CorsConfig;
import com.example.backend.utils.general.CorsConfig;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import org.springframework.context.annotation.Bean;
Expand Down Expand Up @@ -42,7 +42,7 @@ public SecurityFilterChain securityFilterChain(final HttpSecurity http) {
)
.httpBasic(Customizer.withDefaults())
.authorizeHttpRequests(request -> request
.requestMatchers("/api/auth/accouth/**").authenticated()
.requestMatchers("/api/accouth/**").authenticated()
.requestMatchers("/graphiql").permitAll()
.anyRequest()
.permitAll()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.validation.annotation.Validated;
Expand All @@ -26,6 +27,7 @@
@RestController
@AllArgsConstructor
@RequestMapping("/api")
@Slf4j
@Tag(name = "Authentication", description = "Authentication User and Update Password, personal office users")
public class AuthController {

Expand All @@ -37,7 +39,7 @@ public class AuthController {
private static final String REQUEST_EMAIL_UPDATE_PASSWORD = "/request/email";
private static final String ACTIVE_USER = "/active";
private static final String INFO = "/accouth";
private static final String SEND_MESSAGE_EMAIL_NOT_ACTIVE = "/sendMessageEmail";
private static final String SEND_MESSAGE_EMAIL_NOT_ACTIVE = "/sendMessageEmailActive";

@PostMapping(SIGNUP_URI)
@SecurityRequirement(name = "Bearer Authentication")
Expand Down Expand Up @@ -139,22 +141,30 @@ public void requestEmailUpdatePassword(@RequestBody @Validated final EmailReques
description = "Ok",
content =
@Content(mediaType = APPLICATION_JSON_VALUE, schema =
@Schema(implementation = AuthRequest.class)
@Schema(implementation = EmailRequest.class)
)
),
}
)
public void activeUser(@RequestHeader(AUTHORIZATION) final String jwt) {
authService.activeUser(jwt);
public void activeUser(@RequestHeader(AUTHORIZATION) final String jwt,
@RequestBody @Validated final EmailRequest emailRequest) {
authService.activeUser(jwt, emailRequest);
}

@PostMapping(SEND_MESSAGE_EMAIL_NOT_ACTIVE)
@Operation(summary = "Re-sending the account activation letter if the first letter was not successful")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "OK")
@ApiResponse(
responseCode = "200",
description = "Ok",
content =
@Content(mediaType = APPLICATION_JSON_VALUE, schema =
@Schema(implementation = EmailRequest.class)
)
),
}
)
public void sendEmailSecondActive() {
authService.sendEmailActive();
public void sendEmailSecondActive(@RequestBody @Validated final EmailRequest emailRequest) {
authService.sendEmailActive(emailRequest);
}
}
53 changes: 35 additions & 18 deletions src/main/java/com/example/backend/security/jwt/JwtAuthFilter.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import static com.example.backend.utils.Constants.BEARER;
import static com.example.backend.utils.general.Constants.BEARER;
import static org.springframework.http.HttpHeaders.AUTHORIZATION;

@Component
Expand All @@ -32,35 +32,52 @@ protected void doFilterInternal(
final HttpServletResponse response,
final FilterChain filterChain) {

final String authHeader = request.getHeader(AUTHORIZATION);
final String jwt, userData;
var jwt = getTokenHeaders(request);

var userData = getExtractUserData(jwt);

getSecurityContextHolder(request, userData, jwt);

filterChain.doFilter(request, response);
}

private String getTokenHeaders(final HttpServletRequest request) {
final var authHeader = request.getHeader(AUTHORIZATION);

if (StringUtils.isNoneEmpty(authHeader) && authHeader.startsWith(BEARER)) {
return authHeader.substring(7);
}

jwt = authHeader.substring(7);
return null;
}

private String getExtractUserData(final String jwt) {

if (StringUtils.isNoneEmpty(jwt)) {
return jwtService.extractUserData(jwt);
}

userData = jwtService.extractUserData(jwt);
return null;
}

if (StringUtils.isNoneEmpty(userData) && SecurityContextHolder.getContext().getAuthentication() == null) {
private void getSecurityContextHolder(final HttpServletRequest request, final String userData, final String jwt) {
if (StringUtils.isNoneEmpty(userData) && SecurityContextHolder.getContext().getAuthentication() == null) {

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

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

var authenticationToken = new UsernamePasswordAuthenticationToken(
userDetails,
null,
userDetails.getAuthorities()
);
final var authenticationToken = new UsernamePasswordAuthenticationToken(
userDetails,
null,
userDetails.getAuthorities()
);

authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));

SecurityContextHolder.getContext().setAuthentication(authenticationToken);
SecurityContextHolder.getContext().setAuthentication(authenticationToken);

response.addHeader(AUTHORIZATION, BEARER + jwt);
}
}
}
filterChain.doFilter(request, response);
}
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
package com.example.backend.security.models.request;

import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Pattern;
import jakarta.validation.constraints.Size;
import jakarta.validation.constraints.*;
import lombok.Builder;

@Builder
public record PasswordRequest(
@NotNull @NotBlank(message = "Поле не повинно бути порожнім") @Email String email,
@NotNull @NotBlank @Size(min = 7, max = 20) @Pattern(regexp = "^(?=.*\\d)[A-Za-z\\d]+$") String password) { }
40 changes: 17 additions & 23 deletions src/main/java/com/example/backend/security/oauth/AuthGoogle.java
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
package com.example.backend.security.oauth;

import com.example.backend.security.service.JwtService;
import com.example.backend.utils.MyPasswordEncoder;
import com.example.backend.security.service.JwtTokenService;
import com.example.backend.utils.general.MyPasswordEncoder;
import com.example.backend.web.User.UserEntity;
import com.example.backend.web.User.UserService;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import org.apache.commons.lang3.RandomStringUtils;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
Expand All @@ -16,25 +17,21 @@
import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler;
import org.springframework.stereotype.Component;

import java.security.SecureRandom;
import java.util.Base64;
import java.util.List;
import java.util.Map;

import static com.example.backend.utils.Constants.EMAIL_KEY;
import static com.example.backend.utils.Constants.EMPTY_LINE;
import static com.example.backend.utils.Constants.DEPLOY_STORE;
import static com.example.backend.utils.Constants.COOK;
import static com.example.backend.utils.enums.RegisterAuthStatus.GOOGLE;
import static com.example.backend.utils.enums.Role.USER;
import static com.example.backend.utils.general.Constants.*;

@Component
@RequiredArgsConstructor
public class AuthGoogle extends SimpleUrlAuthenticationSuccessHandler {

private final MyPasswordEncoder passwordEncoder;
private final JwtTokenService jwtService;
private final UserService userService;
private final JwtService jwtService;

@Override
@SneakyThrows
Expand All @@ -46,26 +43,28 @@ public void onAuthenticationSuccess(final HttpServletRequest request,

if (GOOGLE.name().toLowerCase().equals(oAuth2AuthenticationToken.getAuthorizedClientRegistrationId())) {

final var attributes = ((DefaultOAuth2User) authentication.getPrincipal()).getAttributes();
final var defaultOAuth2User = ((DefaultOAuth2User) authentication.getPrincipal()).getAttributes();

final var email = attributes.getOrDefault(EMAIL_KEY, EMPTY_LINE).toString();
final var defaultOAuth2UserEmail = defaultOAuth2User.getOrDefault(EMAIL_KEY, EMPTY_LINE).toString();

userService.getByEmail(email)
userService.getByEmail(defaultOAuth2UserEmail)
.ifPresentOrElse(user -> SecurityContextHolder.getContext().setAuthentication(
createOAuth2AuthenticationToken(
createOAuth2User(user.getRole().name(), attributes), user.getRole().name(),
oAuth2AuthenticationToken.getAuthorizedClientRegistrationId()
createOAuth2User(user.getRole().name(), defaultOAuth2User), user.getRole().name(),
oAuth2AuthenticationToken.getAuthorizedClientRegistrationId()
)
), () -> {
final var saveUser = createUserEntity(attributes, email);
final var saveUser = createUserEntity(defaultOAuth2User, defaultOAuth2UserEmail);

userService.mySave(saveUser);

SecurityContextHolder.getContext().setAuthentication(
createOAuth2AuthenticationToken(
createOAuth2User(saveUser.getRole().name(), attributes),
saveUser.getRole().name(),
oAuth2AuthenticationToken.getAuthorizedClientRegistrationId()
createOAuth2User(saveUser.getRole().name(), defaultOAuth2User),

saveUser.getRole().name(),

oAuth2AuthenticationToken.getAuthorizedClientRegistrationId()
)
);
}
Expand Down Expand Up @@ -102,11 +101,6 @@ private UserEntity createUserEntity(final Map<String, Object> attributes, final
}

private static String generateRandomPassword() {
SecureRandom secureRandom = new SecureRandom();
byte[] randomBytes = new byte[15];

secureRandom.nextBytes(randomBytes);

return Base64.getEncoder().encodeToString(randomBytes);
return Base64.getEncoder().encodeToString(RandomStringUtils.randomAlphanumeric(15).getBytes());
}
}
Loading

0 comments on commit ac8f918

Please sign in to comment.