Compare commits
1
Commits
NCBACK-162
...
NCBACK-6
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f4e5ea2976 |
@@ -19,7 +19,7 @@ import ru.soune.nocopy.dto.file.ChunkUploadResponse;
|
||||
import ru.soune.nocopy.dto.file.CompleteUploadResponse;
|
||||
import ru.soune.nocopy.dto.file.FileEntityResponse;
|
||||
import ru.soune.nocopy.dto.file.UploadProgress;
|
||||
import ru.soune.nocopy.entity.AuthToken;
|
||||
import ru.soune.nocopy.entity.auth.AuthToken;
|
||||
import ru.soune.nocopy.entity.file.FileStatus;
|
||||
import ru.soune.nocopy.entity.file.UploadStatus;
|
||||
import ru.soune.nocopy.exception.*;
|
||||
|
||||
@@ -6,7 +6,7 @@ import org.springframework.web.bind.annotation.*;
|
||||
import ru.soune.nocopy.dto.ChangePasswordRequest;
|
||||
import ru.soune.nocopy.dto.UserDTO;
|
||||
import ru.soune.nocopy.dto.UserRequest;
|
||||
import ru.soune.nocopy.entity.AuthToken;
|
||||
import ru.soune.nocopy.entity.auth.AuthToken;
|
||||
import ru.soune.nocopy.entity.User;
|
||||
import ru.soune.nocopy.exception.InvalidUserEmail;
|
||||
import ru.soune.nocopy.exception.NotFoundAuthToken;
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
package ru.soune.nocopy.controller;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import ru.soune.nocopy.dto.BaseResponse;
|
||||
import ru.soune.nocopy.dto.LoginAnswer;
|
||||
import ru.soune.nocopy.dto.MessageCode;
|
||||
import ru.soune.nocopy.entity.User;
|
||||
import ru.soune.nocopy.entity.auth.AuthToken;
|
||||
import ru.soune.nocopy.service.AuthService;
|
||||
import ru.soune.nocopy.service.VkOAuthService;
|
||||
import ru.soune.nocopy.service.auth.ExternalAuthUserService;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api")
|
||||
@RequiredArgsConstructor
|
||||
@Slf4j
|
||||
public class VkAuthController {
|
||||
|
||||
private final VkOAuthService vkOAuthService;
|
||||
|
||||
private final AuthService authService;
|
||||
|
||||
private final ExternalAuthUserService externalAuthUserService;
|
||||
|
||||
@GetMapping("/v{version}/vk/authorization/{code}/{state}/{codeVerifier}/{deviceId}")
|
||||
public BaseResponse vkAuth(@PathVariable("code") String code,
|
||||
@PathVariable("state") String state,
|
||||
@PathVariable("codeVerifier") String codeVerifier,
|
||||
@PathVariable("deviceId") String deviceId) {
|
||||
try {
|
||||
Map<String, Object> authMap = vkOAuthService.authTokenMap(code, state, codeVerifier, deviceId);
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
|
||||
String bodyJson = (String) authMap.get("body");
|
||||
Map<String, Object> body = mapper.readValue(bodyJson, Map.class);
|
||||
|
||||
String accessToken = (String) body.get("access_token");
|
||||
long vkUserId = ((Number) body.get("user_id")).longValue();
|
||||
Integer expiresIn = (Integer) body.get("expires_in");
|
||||
String refreshToken = (String) body.get("refresh_token");
|
||||
|
||||
Map<String, Object> vkUserInfo = vkOAuthService.userVkInfo(accessToken);
|
||||
log.info("vkUserInfo {}", vkUserInfo);
|
||||
|
||||
String userInfoBodyJson = (String) vkUserInfo.get("body");
|
||||
Map<String, Object> userInfoBody = mapper.readValue(userInfoBodyJson, Map.class);
|
||||
Map<String, Object> user = (Map<String, Object>) userInfoBody.get("user");
|
||||
|
||||
String firstName = (String) user.get("first_name");
|
||||
String lastName = (String) user.get("last_name");
|
||||
String fullName = firstName + " " + lastName;
|
||||
|
||||
User userEntity = externalAuthUserService.findOrCreateUserFromVK(Long.toString(vkUserId), fullName,
|
||||
null, accessToken, refreshToken, expiresIn);
|
||||
|
||||
AuthToken ourToken = authService.createTokenForUser(userEntity);
|
||||
|
||||
LoginAnswer loginAnswer = new LoginAnswer();
|
||||
loginAnswer.setToken(ourToken.getToken());
|
||||
loginAnswer.setFieldErrors(new ArrayList<>());
|
||||
|
||||
return new BaseResponse(20009, MessageCode.SUCCESS.getCode(),
|
||||
MessageCode.SUCCESS.getDescription(), loginAnswer);
|
||||
} catch (Exception e) {
|
||||
log.error("VK auth error: ", e);
|
||||
|
||||
LoginAnswer loginAnswer = new LoginAnswer();
|
||||
loginAnswer.setToken(null);
|
||||
|
||||
List<Map<String, String>> errors = new ArrayList<>();
|
||||
Map<String, String> error = new HashMap<>();
|
||||
error.put("field", "vk_auth");
|
||||
error.put("message", e.getMessage());
|
||||
errors.add(error);
|
||||
|
||||
loginAnswer.setFieldErrors(errors);
|
||||
|
||||
return new BaseResponse(20009, MessageCode.SUCCESS.getCode(),
|
||||
MessageCode.SUCCESS.getDescription(), loginAnswer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
package ru.soune.nocopy.dto;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
public record AuthResponse (
|
||||
boolean success,
|
||||
String message,
|
||||
String token,
|
||||
LocalDateTime expiresAt
|
||||
) {}
|
||||
@@ -6,11 +6,14 @@ import jakarta.validation.constraints.Size;
|
||||
import lombok.*;
|
||||
import org.springframework.data.annotation.CreatedDate;
|
||||
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
|
||||
import ru.soune.nocopy.entity.auth.AuthToken;
|
||||
import ru.soune.nocopy.entity.auth.ExternalAuthProvider;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
@Entity
|
||||
@Table(name = "users")
|
||||
@@ -19,18 +22,19 @@ import java.util.List;
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@ToString
|
||||
@Builder
|
||||
@EntityListeners(AuditingEntityListener.class)
|
||||
public class User {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long Id;
|
||||
private Long id;
|
||||
|
||||
@Column(name = "full_name", nullable = false)
|
||||
private String fullName;
|
||||
|
||||
@Size(max = 1024)
|
||||
@Column(name = "email", nullable = false, length = 1024, unique = true)
|
||||
@Size(max = 255)
|
||||
@Column(name = "email", nullable = false, length = 255, unique = true)
|
||||
private String email;
|
||||
|
||||
@Column(name = "company")
|
||||
@@ -90,4 +94,14 @@ public class User {
|
||||
@JsonIgnore
|
||||
@ToString.Exclude
|
||||
private List<ImageProtection> imageProtections = new ArrayList<>();
|
||||
|
||||
@OneToMany(mappedBy = "user", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
|
||||
@ToString.Exclude
|
||||
private List<ExternalAuthProvider> externalAuthProviders = new ArrayList<>();
|
||||
|
||||
public Optional<ExternalAuthProvider> getProvider(ExternalAuthProvider.ProviderType providerType) {
|
||||
return externalAuthProviders.stream()
|
||||
.filter(p -> p.getProviderType() == providerType)
|
||||
.findFirst();
|
||||
}
|
||||
}
|
||||
|
||||
+3
-2
@@ -1,9 +1,10 @@
|
||||
package ru.soune.nocopy.entity;
|
||||
package ru.soune.nocopy.entity.auth;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import lombok.*;
|
||||
import org.springframework.data.annotation.CreatedDate;
|
||||
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
|
||||
import ru.soune.nocopy.entity.User;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@@ -30,7 +31,7 @@ public class AuthToken {
|
||||
private String token;
|
||||
|
||||
@Column(name = "expires_at", nullable = false)
|
||||
private LocalDateTime expiresAt = LocalDateTime.now().plusDays(30);
|
||||
private LocalDateTime expiresAt = LocalDateTime.now().plusHours(1);
|
||||
|
||||
@CreatedDate
|
||||
@Column(name = "created_at", updatable = false, nullable = false)
|
||||
@@ -0,0 +1,89 @@
|
||||
package ru.soune.nocopy.entity.auth;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import lombok.*;
|
||||
import org.springframework.data.annotation.CreatedDate;
|
||||
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
|
||||
import ru.soune.nocopy.entity.User;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Entity
|
||||
@Table(name = "external_auth_providers",
|
||||
uniqueConstraints = @UniqueConstraint(
|
||||
columnNames = {"provider_type", "provider_user_id"}
|
||||
),
|
||||
indexes = {
|
||||
@Index(name = "idx_provider_type_user_id",
|
||||
columnList = "provider_type, provider_user_id"),
|
||||
@Index(name = "idx_user_id_provider_type",
|
||||
columnList = "user_id, provider_type")
|
||||
})
|
||||
@Getter @Setter
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
@EntityListeners(AuditingEntityListener.class)
|
||||
public class ExternalAuthProvider {
|
||||
|
||||
public enum ProviderType {
|
||||
VK, YANDEX
|
||||
}
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "user_id", nullable = false, foreignKey = @ForeignKey(name = "fk_extauth_user"))
|
||||
@ToString.Exclude
|
||||
private User user;
|
||||
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Column(name = "provider_type", nullable = false, length = 20)
|
||||
private ProviderType providerType;
|
||||
|
||||
@Column(name = "provider_user_id", nullable = false, length = 255)
|
||||
private String providerUserId;
|
||||
|
||||
@Column(name = "access_token", length = 2048)
|
||||
private String accessToken;
|
||||
|
||||
@Column(name = "refresh_token", length = 2048)
|
||||
private String refreshToken;
|
||||
|
||||
@Column(name = "token_expires_at")
|
||||
private LocalDateTime tokenExpiresAt;
|
||||
|
||||
@Column(name = "email", length = 255)
|
||||
private String email;
|
||||
|
||||
@Column(name = "phone", length = 20)
|
||||
private String phoneNumber;
|
||||
|
||||
@CreatedDate
|
||||
@Column(name = "linked_at", updatable = false)
|
||||
private LocalDateTime linkedAt;
|
||||
|
||||
@Column(name = "last_used_at")
|
||||
private LocalDateTime lastUsedAt;
|
||||
|
||||
@Column(name = "is_active")
|
||||
@Builder.Default
|
||||
private Boolean isActive = true;
|
||||
|
||||
@PrePersist
|
||||
public void prePersist() {
|
||||
if (this.linkedAt == null) {
|
||||
this.linkedAt = LocalDateTime.now();
|
||||
}
|
||||
if (this.lastUsedAt == null) {
|
||||
this.lastUsedAt = LocalDateTime.now();
|
||||
}
|
||||
}
|
||||
|
||||
@PreUpdate
|
||||
public void preUpdate() {
|
||||
this.lastUsedAt = LocalDateTime.now();
|
||||
}
|
||||
}
|
||||
@@ -6,10 +6,9 @@ import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
import ru.soune.nocopy.dto.*;
|
||||
import ru.soune.nocopy.dto.file.*;
|
||||
import ru.soune.nocopy.entity.AuthToken;
|
||||
import ru.soune.nocopy.entity.auth.AuthToken;
|
||||
import ru.soune.nocopy.entity.file.FileEntity;
|
||||
import ru.soune.nocopy.entity.file.FileStatus;
|
||||
import ru.soune.nocopy.entity.file.FileType;
|
||||
import ru.soune.nocopy.exception.FileEntityNotFoundException;
|
||||
import ru.soune.nocopy.exception.NotFoundAuthToken;
|
||||
import ru.soune.nocopy.repository.AuthTokenRepository;
|
||||
|
||||
@@ -10,7 +10,7 @@ import org.springframework.validation.BindingResult;
|
||||
import org.springframework.validation.FieldError;
|
||||
import ru.soune.nocopy.dto.*;
|
||||
import ru.soune.nocopy.dto.file.*;
|
||||
import ru.soune.nocopy.entity.AuthToken;
|
||||
import ru.soune.nocopy.entity.auth.AuthToken;
|
||||
import ru.soune.nocopy.entity.file.FileType;
|
||||
import ru.soune.nocopy.entity.file.FileUploadSession;
|
||||
import ru.soune.nocopy.exception.*;
|
||||
|
||||
@@ -4,7 +4,7 @@ import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Component;
|
||||
import ru.soune.nocopy.dto.*;
|
||||
import ru.soune.nocopy.entity.AuthToken;
|
||||
import ru.soune.nocopy.entity.auth.AuthToken;
|
||||
import ru.soune.nocopy.exception.NotValidFieldException;
|
||||
import ru.soune.nocopy.repository.UserRepository;
|
||||
import ru.soune.nocopy.service.AuthService;
|
||||
|
||||
@@ -4,7 +4,7 @@ import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Component;
|
||||
import ru.soune.nocopy.dto.*;
|
||||
import ru.soune.nocopy.entity.AuthToken;
|
||||
import ru.soune.nocopy.entity.auth.AuthToken;
|
||||
import ru.soune.nocopy.entity.User;
|
||||
import ru.soune.nocopy.exception.NotValidFieldException;
|
||||
import ru.soune.nocopy.repository.AuthTokenRepository;
|
||||
|
||||
@@ -7,7 +7,7 @@ import org.springframework.stereotype.Component;
|
||||
import org.springframework.validation.BeanPropertyBindingResult;
|
||||
import org.springframework.validation.BindingResult;
|
||||
import ru.soune.nocopy.dto.*;
|
||||
import ru.soune.nocopy.entity.AuthToken;
|
||||
import ru.soune.nocopy.entity.auth.AuthToken;
|
||||
import ru.soune.nocopy.exception.NotValidFieldException;
|
||||
import ru.soune.nocopy.exception.ValidationException;
|
||||
import ru.soune.nocopy.handler.validator.RegRequestValidator;
|
||||
|
||||
@@ -2,7 +2,7 @@ package ru.soune.nocopy.repository;
|
||||
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
import ru.soune.nocopy.entity.AuthToken;
|
||||
import ru.soune.nocopy.entity.auth.AuthToken;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.Optional;
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
package ru.soune.nocopy.repository;
|
||||
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
import ru.soune.nocopy.entity.User;
|
||||
import ru.soune.nocopy.entity.auth.ExternalAuthProvider;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
@Repository
|
||||
public interface ExternalAuthProviderRepository extends JpaRepository<ExternalAuthProvider, Long> {
|
||||
|
||||
Optional<ExternalAuthProvider> findByProviderTypeAndProviderUserId(
|
||||
ExternalAuthProvider.ProviderType providerType,
|
||||
String providerUserId
|
||||
);
|
||||
|
||||
List<ExternalAuthProvider> findByUser(User user);
|
||||
|
||||
boolean existsByProviderTypeAndProviderUserId(
|
||||
ExternalAuthProvider.ProviderType providerType,
|
||||
String providerUserId
|
||||
);
|
||||
}
|
||||
@@ -2,7 +2,10 @@ package ru.soune.nocopy.repository;
|
||||
|
||||
import jakarta.validation.constraints.Size;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
import ru.soune.nocopy.entity.User;
|
||||
import ru.soune.nocopy.entity.auth.ExternalAuthProvider;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
@@ -10,4 +13,10 @@ public interface UserRepository extends JpaRepository<User, Long> {
|
||||
Optional<User> findByEmail(String email);
|
||||
boolean existsByEmail(String email);
|
||||
boolean existsByPhone(@Size(min = 11, max = 14) String phone);
|
||||
@Query("SELECT u FROM User u JOIN u.externalAuthProviders p " +
|
||||
"WHERE p.providerType = :providerType AND p.providerUserId = :providerUserId")
|
||||
Optional<User> findByExternalProvider(
|
||||
@Param("providerType") ExternalAuthProvider.ProviderType providerType,
|
||||
@Param("providerUserId") String providerUserId
|
||||
);
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import ru.soune.nocopy.dto.*;
|
||||
import ru.soune.nocopy.entity.AuthToken;
|
||||
import ru.soune.nocopy.entity.auth.AuthToken;
|
||||
import ru.soune.nocopy.entity.User;
|
||||
import ru.soune.nocopy.exception.NotValidFieldException;
|
||||
import ru.soune.nocopy.repository.AuthTokenRepository;
|
||||
@@ -87,6 +87,20 @@ public class AuthService {
|
||||
.ifPresent(authTokenRepository::delete);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public AuthToken createTokenForUser(User user) {
|
||||
AuthToken authToken = new AuthToken();
|
||||
authToken.setToken(generateAuthToken());
|
||||
authToken.setUser(user);
|
||||
authToken.setExpiresAt(LocalDateTime.now().plusHours(1));
|
||||
authToken.setIsActive(true);
|
||||
|
||||
user.setLastLoginAt(LocalDateTime.now());
|
||||
userRepository.save(user);
|
||||
|
||||
return authTokenRepository.save(authToken);
|
||||
}
|
||||
|
||||
private String generateAuthToken() {
|
||||
byte[] bytes = new byte[32];
|
||||
secureRandom.nextBytes(bytes);
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
package ru.soune.nocopy.service;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.http.*;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@Service
|
||||
public class VkOAuthService {
|
||||
@Value("${auth.vk.oauth2}")
|
||||
String oauthVkPath;
|
||||
|
||||
@Value("${auth.vk.user_info}")
|
||||
String vkUserInfo;
|
||||
|
||||
@Value("${auth.vk.client_id}")
|
||||
String clientId;
|
||||
|
||||
@Value("${auth.vk.redirect_uri}")
|
||||
String redirectUri;
|
||||
|
||||
public Map<String, Object> authTokenMap(String code, String state, String codeVerifier, String deviceId) {
|
||||
MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
|
||||
params.add("grant_type", "authorization_code");
|
||||
params.add("code_verifier", codeVerifier);
|
||||
params.add("redirect_uri", redirectUri);
|
||||
params.add("code", code);
|
||||
params.add("client_id", clientId);
|
||||
params.add("device_id", deviceId);
|
||||
params.add("state", state);
|
||||
params.add("scope", "email,phone");
|
||||
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
|
||||
|
||||
return sendRequest(params, headers,oauthVkPath);
|
||||
}
|
||||
|
||||
public Map<String, Object> userVkInfo(String accessToken) {
|
||||
MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
|
||||
params.add("access_token", accessToken);
|
||||
params.add("client_id", clientId);
|
||||
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
|
||||
|
||||
|
||||
return sendRequest(params, headers, vkUserInfo);
|
||||
}
|
||||
|
||||
private Map<String, Object> sendRequest(MultiValueMap<String, String> params, HttpHeaders headers, String url) {
|
||||
Map<String, Object> info = new HashMap<>();
|
||||
RestTemplate restTemplate = new RestTemplate();
|
||||
ResponseEntity<String> response = restTemplate.exchange(url, HttpMethod.POST,
|
||||
new HttpEntity<>(params, headers), String.class);
|
||||
|
||||
info.put("status", response.getStatusCodeValue());
|
||||
info.put("body", response.getBody());
|
||||
info.put("headers", response.getHeaders());
|
||||
info.put("success", response.getStatusCode().is2xxSuccessful());
|
||||
|
||||
return info;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
package ru.soune.nocopy.service.auth;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import ru.soune.nocopy.entity.User;
|
||||
import ru.soune.nocopy.entity.auth.ExternalAuthProvider;
|
||||
import ru.soune.nocopy.repository.ExternalAuthProviderRepository;
|
||||
import ru.soune.nocopy.repository.UserRepository;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Optional;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class ExternalAuthUserService {
|
||||
private final UserRepository userRepository;
|
||||
|
||||
private final ExternalAuthProviderRepository externalAuthProviderRepository;
|
||||
|
||||
@Transactional
|
||||
public User findOrCreateUserFromVK(String vkUserId, String fullName, String email,
|
||||
String vkAccessToken, String vkRefreshToken, Integer expiresIn) {
|
||||
|
||||
Optional<User> existingByVkId = userRepository.findByExternalProvider(ExternalAuthProvider.ProviderType.VK,
|
||||
vkUserId);
|
||||
|
||||
if (existingByVkId.isPresent()) {
|
||||
User user = existingByVkId.get();
|
||||
updateVKProvider(user, vkAccessToken, vkRefreshToken, expiresIn, email);
|
||||
|
||||
return user;
|
||||
}
|
||||
|
||||
if (email != null && !email.isBlank()) {
|
||||
Optional<User> existingByEmail = userRepository.findByEmail(email);
|
||||
|
||||
if (existingByEmail.isPresent()) {
|
||||
User user = existingByEmail.get();
|
||||
linkVKProvider(user, vkUserId, vkAccessToken, vkRefreshToken, expiresIn, email);
|
||||
|
||||
return user;
|
||||
}
|
||||
}
|
||||
|
||||
return createNewUserWithVK(vkUserId, fullName, email, vkAccessToken, vkRefreshToken, expiresIn);
|
||||
}
|
||||
|
||||
private User createNewUserWithVK(String vkUserId, String fullName, String email,
|
||||
String vkAccessToken, String vkRefreshToken, Integer expiresIn) {
|
||||
User newUser = User.builder()
|
||||
.fullName(fullName)
|
||||
.email(email)
|
||||
.password("generateRandomPassword()")
|
||||
.isActive(true)
|
||||
.build();
|
||||
|
||||
User savedUser = userRepository.save(newUser);
|
||||
|
||||
linkVKProvider(savedUser, vkUserId, vkAccessToken, vkRefreshToken, expiresIn, email);
|
||||
|
||||
return savedUser;
|
||||
}
|
||||
|
||||
private void linkVKProvider(User user, String vkUserId, String vkAccessToken, String vkRefreshToken,
|
||||
Integer expiresIn, String email) {
|
||||
ExternalAuthProvider provider = ExternalAuthProvider.builder()
|
||||
.user(user)
|
||||
.providerType(ExternalAuthProvider.ProviderType.VK)
|
||||
.providerUserId(vkUserId)
|
||||
.accessToken(vkAccessToken)
|
||||
.refreshToken(vkRefreshToken)
|
||||
.tokenExpiresAt(LocalDateTime.now().plusSeconds(expiresIn))
|
||||
.email(email)
|
||||
.isActive(true)
|
||||
.build();
|
||||
|
||||
externalAuthProviderRepository.save(provider);
|
||||
|
||||
user.getExternalAuthProviders().add(provider);
|
||||
}
|
||||
|
||||
private void updateVKProvider(User user, String vkAccessToken, String vkRefreshToken,
|
||||
Integer expiresIn, String email) {
|
||||
Optional<ExternalAuthProvider> existingProvider = user.getProvider(ExternalAuthProvider.ProviderType.VK);
|
||||
|
||||
if (existingProvider.isPresent()) {
|
||||
ExternalAuthProvider provider = existingProvider.get();
|
||||
provider.setAccessToken(vkAccessToken);
|
||||
provider.setTokenExpiresAt(LocalDateTime.now().plusSeconds(expiresIn));
|
||||
provider.setEmail(email);
|
||||
provider.setRefreshToken(vkRefreshToken);
|
||||
provider.setLastUsedAt(LocalDateTime.now());
|
||||
|
||||
externalAuthProviderRepository.save(provider);
|
||||
}
|
||||
}
|
||||
|
||||
private String generateRandomPassword() {
|
||||
return java.util.UUID.randomUUID().toString();
|
||||
}
|
||||
}
|
||||
@@ -32,6 +32,13 @@ file:
|
||||
temp-ttl-hours: ${TEMP_TTL_HOURS:72} # 3 дня
|
||||
session-expiry-hours: ${SESSION_EXPIRY_HOURS:24}
|
||||
|
||||
auth:
|
||||
vk:
|
||||
oauth2: "https://id.vk.ru/oauth2/auth"
|
||||
user_info: "https://id.vk.ru/oauth2/user_info"
|
||||
client_id: "54389559"
|
||||
redirect_uri: "http://localhost"
|
||||
|
||||
security:
|
||||
cors:
|
||||
allowed-origins: "*"
|
||||
|
||||
@@ -9,7 +9,7 @@ import org.mockito.MockitoAnnotations;
|
||||
import org.springframework.context.MessageSource;
|
||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||
import ru.soune.nocopy.dto.LoginRequest;
|
||||
import ru.soune.nocopy.entity.AuthToken;
|
||||
import ru.soune.nocopy.entity.auth.AuthToken;
|
||||
import ru.soune.nocopy.entity.User;
|
||||
import ru.soune.nocopy.exception.NotValidationPasswordException;
|
||||
import ru.soune.nocopy.exception.UserNotFoundException;
|
||||
|
||||
Reference in New Issue
Block a user