Merge pull request #11 from HideyoshiNakazone/implements-file-service

Implements Storage Service and Refactors Auth Response
This commit is contained in:
2023-08-22 02:58:52 -03:00
committed by GitHub
24 changed files with 510 additions and 151 deletions

View File

@@ -102,6 +102,11 @@
<artifactId>junit</artifactId> <artifactId>junit</artifactId>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.13</version>
</dependency>
</dependencies> </dependencies>
<build> <build>

View File

@@ -1,4 +1,4 @@
package com.hideyoshi.backendportfolio.base.security; package com.hideyoshi.backendportfolio.base.security.config;
import com.hideyoshi.backendportfolio.base.config.RestAuthenticationEntryPointConfig; import com.hideyoshi.backendportfolio.base.config.RestAuthenticationEntryPointConfig;
import com.hideyoshi.backendportfolio.base.security.filter.CustomAuthenticationFilter; import com.hideyoshi.backendportfolio.base.security.filter.CustomAuthenticationFilter;

View File

@@ -0,0 +1,55 @@
package com.hideyoshi.backendportfolio.base.security.model;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.hideyoshi.backendportfolio.base.user.entity.Provider;
import com.hideyoshi.backendportfolio.base.user.entity.Role;
import com.hideyoshi.backendportfolio.base.user.model.TokenDTO;
import com.hideyoshi.backendportfolio.base.user.model.UserDTO;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import javax.validation.constraints.Size;
import java.io.Serializable;
import java.util.List;
@Data
@Builder
@AllArgsConstructor
@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonIgnoreProperties(ignoreUnknown = true)
public class AuthDTO implements Serializable {
private Long id;
private String name;
private String email;
private String username;
private String profilePictureUrl;
private List<Role> roles;
private Provider provider;
private TokenDTO accessToken;
private TokenDTO refreshToken;
public AuthDTO(UserDTO user, TokenDTO accessToken, TokenDTO refreshToken) {
this.id = user.getId();
this.name = user.getName();
this.email = user.getEmail();
this.username = user.getUsername();
this.roles = user.getRoles();
this.provider = user.getProvider();
this.profilePictureUrl = user.getProfilePictureUrl();
this.accessToken = accessToken;
this.refreshToken = refreshToken;
}
}

View File

@@ -15,6 +15,11 @@ public class GithubOAuthMap implements OAuthMap {
return oAuth2User.getAttribute("login"); return oAuth2User.getAttribute("login");
} }
@Override
public String getProfilePicture() {
return this.oAuth2User.getAttribute("avatar_url");
}
@Override @Override
public Provider getProvider() { public Provider getProvider() {
return Provider.GITHUB; return Provider.GITHUB;

View File

@@ -8,11 +8,16 @@ import org.springframework.security.oauth2.core.user.OAuth2User;
@AllArgsConstructor @AllArgsConstructor
public class GoogleOAuthMap implements OAuthMap { public class GoogleOAuthMap implements OAuthMap {
private OAuth2User oauthUser; private OAuth2User oAuth2User;
@Override @Override
public String getPrincipal() { public String getPrincipal() {
return this.oauthUser.getAttribute("given_name"); return this.oAuth2User.getAttribute("given_name");
}
@Override
public String getProfilePicture() {
return this.oAuth2User.getAttribute("picture");
} }
@Override @Override
@@ -20,5 +25,4 @@ public class GoogleOAuthMap implements OAuthMap {
return Provider.GOOGLE; return Provider.GOOGLE;
} }
} }

View File

@@ -7,6 +7,8 @@ public interface OAuthMap {
String getPrincipal(); String getPrincipal();
String getProfilePicture();
Provider getProvider(); Provider getProvider();
} }

View File

@@ -1,8 +1,9 @@
package com.hideyoshi.backendportfolio.base.security.oauth.mapper; package com.hideyoshi.backendportfolio.base.security.oauth.mapper;
import com.hideyoshi.backendportfolio.base.user.entity.Provider; import com.hideyoshi.backendportfolio.base.user.entity.Provider;
import lombok.Getter;
public enum OAuthMapEnum { public enum OAuthMapper {
GOOGLE(GoogleOAuthMap.class, Provider.GOOGLE), GOOGLE(GoogleOAuthMap.class, Provider.GOOGLE),
@@ -10,9 +11,10 @@ public enum OAuthMapEnum {
private final Class oAuthMap; private final Class oAuthMap;
@Getter
private final Provider provider; private final Provider provider;
private OAuthMapEnum(Class oAuthMap, Provider provider) { private OAuthMapper(Class oAuthMap, Provider provider) {
this.oAuthMap = oAuthMap; this.oAuthMap = oAuthMap;
this.provider = provider; this.provider = provider;
} }
@@ -21,12 +23,8 @@ public enum OAuthMapEnum {
return oAuthMap; return oAuthMap;
} }
public Provider getProvider() { public static OAuthMapper byValue(String name) {
return provider; for (OAuthMapper e : values()) {
}
public static OAuthMapEnum byValue(String name) {
for (OAuthMapEnum e : values()) {
if (e.getProvider().getName().equals(name)) { if (e.getProvider().getName().equals(name)) {
return e; return e;
} }

View File

@@ -1,6 +1,7 @@
package com.hideyoshi.backendportfolio.base.security.service; package com.hideyoshi.backendportfolio.base.security.service;
import com.auth0.jwt.algorithms.Algorithm; import com.auth0.jwt.algorithms.Algorithm;
import com.hideyoshi.backendportfolio.base.security.model.AuthDTO;
import com.hideyoshi.backendportfolio.base.user.model.TokenDTO; import com.hideyoshi.backendportfolio.base.user.model.TokenDTO;
import com.hideyoshi.backendportfolio.base.user.model.UserDTO; import com.hideyoshi.backendportfolio.base.user.model.UserDTO;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
@@ -22,16 +23,18 @@ public interface AuthService {
UsernamePasswordAuthenticationToken verifyAccessToken(String authorizationHeader); UsernamePasswordAuthenticationToken verifyAccessToken(String authorizationHeader);
UserDTO refreshAccessToken(String refreshToken, HttpServletRequest request, HttpServletResponse response); AuthDTO refreshAccessToken(String refreshToken, HttpServletRequest request, HttpServletResponse response);
UserDTO signupUser(@Valid UserDTO user, HttpServletRequest request); AuthDTO signupUser(@Valid UserDTO user, HttpServletRequest request);
UserDTO generateUserWithTokens(UserDTO user, HttpServletRequest request); AuthDTO generateUserWithTokens(UserDTO user, HttpServletRequest request);
UserDTO processOAuthPostLogin(@Valid UserDTO user, HttpServletRequest request); AuthDTO processOAuthPostLogin(@Valid UserDTO user, HttpServletRequest request);
void loginUser(HttpServletRequest request, HttpServletResponse response, @Valid UserDTO user) throws IOException; void loginUser(HttpServletRequest request, HttpServletResponse response, @Valid UserDTO user) throws IOException;
void loginOAuthUser(HttpServletRequest request, HttpServletResponse response, OAuth2User user) throws IOException; void loginOAuthUser(HttpServletRequest request, HttpServletResponse response, OAuth2User user) throws IOException;
UserDTO getLoggedUser();
} }

View File

@@ -5,13 +5,15 @@ import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm; import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.DecodedJWT; import com.auth0.jwt.interfaces.DecodedJWT;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import com.hideyoshi.backendportfolio.base.security.model.AuthDTO;
import com.hideyoshi.backendportfolio.base.security.oauth.mapper.OAuthMap; import com.hideyoshi.backendportfolio.base.security.oauth.mapper.OAuthMap;
import com.hideyoshi.backendportfolio.base.security.oauth.mapper.OAuthMapEnum; import com.hideyoshi.backendportfolio.base.security.oauth.mapper.OAuthMapper;
import com.hideyoshi.backendportfolio.base.user.entity.Provider; import com.hideyoshi.backendportfolio.base.user.entity.Provider;
import com.hideyoshi.backendportfolio.base.user.entity.Role; import com.hideyoshi.backendportfolio.base.user.entity.Role;
import com.hideyoshi.backendportfolio.base.user.model.TokenDTO; import com.hideyoshi.backendportfolio.base.user.model.TokenDTO;
import com.hideyoshi.backendportfolio.base.user.model.UserDTO; import com.hideyoshi.backendportfolio.base.user.model.UserDTO;
import com.hideyoshi.backendportfolio.base.user.service.UserService; import com.hideyoshi.backendportfolio.base.user.service.UserService;
import com.hideyoshi.backendportfolio.microservice.storageService.service.StorageService;
import com.hideyoshi.backendportfolio.util.exception.BadRequestException; import com.hideyoshi.backendportfolio.util.exception.BadRequestException;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2; import lombok.extern.log4j.Log4j2;
@@ -21,6 +23,7 @@ import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.oauth2.core.user.OAuth2User; import org.springframework.security.oauth2.core.user.OAuth2User;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.web.servlet.HandlerExceptionResolver; import org.springframework.web.servlet.HandlerExceptionResolver;
@@ -55,6 +58,8 @@ public class AuthServiceImpl implements AuthService {
private final UserService userService; private final UserService userService;
private final StorageService storageService;
@Autowired @Autowired
@Qualifier("handlerExceptionResolver") @Qualifier("handlerExceptionResolver")
private HandlerExceptionResolver resolver; private HandlerExceptionResolver resolver;
@@ -98,7 +103,7 @@ public class AuthServiceImpl implements AuthService {
TokenDTO accessToken = generateAccessToken(user, algorithm, request); TokenDTO accessToken = generateAccessToken(user, algorithm, request);
TokenDTO refreshToken = generateRefreshToken(user, algorithm, request); TokenDTO refreshToken = generateRefreshToken(user, algorithm, request);
HashMap<String,TokenDTO> tokens = new HashMap<>(); HashMap<String, TokenDTO> tokens = new HashMap<>();
tokens.put("accessToken", accessToken); tokens.put("accessToken", accessToken);
tokens.put("refreshToken", refreshToken); tokens.put("refreshToken", refreshToken);
@@ -108,7 +113,9 @@ public class AuthServiceImpl implements AuthService {
@Override @Override
public UsernamePasswordAuthenticationToken verifyAccessToken(String authorizationHeader) { public UsernamePasswordAuthenticationToken verifyAccessToken(String authorizationHeader) {
if (authorizationHeader.startsWith(AUTHORIZATION_TYPE_STRING)) { if (!authorizationHeader.startsWith(AUTHORIZATION_TYPE_STRING)) {
return null;
}
String authorizationToken = authorizationHeader.substring(AUTHORIZATION_TYPE_STRING.length()); String authorizationToken = authorizationHeader.substring(AUTHORIZATION_TYPE_STRING.length());
Algorithm algorithm = Algorithm.HMAC256(TOKEN_SECRET.getBytes()); Algorithm algorithm = Algorithm.HMAC256(TOKEN_SECRET.getBytes());
@@ -123,15 +130,71 @@ public class AuthServiceImpl implements AuthService {
stream(roles).forEach(role -> { stream(roles).forEach(role -> {
authorities.add(new SimpleGrantedAuthority(role)); authorities.add(new SimpleGrantedAuthority(role));
}); });
return new UsernamePasswordAuthenticationToken(username, null, authorities); return new UsernamePasswordAuthenticationToken(username, null, authorities);
} }
return null;
}
@Override @Override
public UserDTO refreshAccessToken(String refreshToken, HttpServletRequest request, HttpServletResponse response) { public AuthDTO generateUserWithTokens(UserDTO user, HttpServletRequest request) {
if (Objects.nonNull(refreshToken)) { Algorithm algorithm = Algorithm.HMAC256(TOKEN_SECRET.getBytes());
HashMap<String, TokenDTO> tokens = this.generateTokens(user, algorithm, request);
HttpSession httpSession = request.getSession();
AuthDTO authObject = new AuthDTO(user, tokens.get("accessToken"), tokens.get("refreshToken"));
httpSession.setAttribute("user", authObject);
return authObject;
}
@Override
public AuthDTO signupUser(@Valid UserDTO user, HttpServletRequest request) {
user.setProvider(Provider.LOCAL);
UserDTO authenticatedUser = this.userService.saveUser(user);
authenticatedUser.setProfilePictureUrl(
this.storageService.getFileUrl(authenticatedUser.getUsername(), "profile")
.getPresignedUrl()
);
return this.generateUserWithTokens(
authenticatedUser,
request
);
}
@Override
public void loginUser(HttpServletRequest request, HttpServletResponse response, @Valid UserDTO user) throws IOException {
user.setProfilePictureUrl(
this.storageService.getFileUrl(user.getUsername(), "profile")
.getPresignedUrl()
);
AuthDTO authObject = this.generateUserWithTokens(
user,
request
);
response.setContentType(APPLICATION_JSON_VALUE);
new ObjectMapper()
.writeValue(response.getOutputStream(), authObject);
}
@Override
public AuthDTO refreshAccessToken(String refreshToken, HttpServletRequest request, HttpServletResponse response) {
if (!Objects.nonNull(refreshToken)) {
resolver.resolveException(
request,
response,
null,
new BadRequestException("Invalid Refresh Token. Please authenticate first.")
);
}
Algorithm algorithm = Algorithm.HMAC256(TOKEN_SECRET.getBytes()); Algorithm algorithm = Algorithm.HMAC256(TOKEN_SECRET.getBytes());
@@ -139,11 +202,14 @@ public class AuthServiceImpl implements AuthService {
DecodedJWT decodedJWT = verifier.verify(refreshToken); DecodedJWT decodedJWT = verifier.verify(refreshToken);
UserDTO user = this.userService.getUser(decodedJWT.getSubject()); UserDTO user = this.userService.getUser(decodedJWT.getSubject());
user.setProfilePictureUrl(
if (Objects.nonNull(user)) { this.storageService.getFileUrl(user.getUsername(), "profile")
.getPresignedUrl()
);
HttpSession httpSession = request.getSession(); HttpSession httpSession = request.getSession();
UserDTO authenticatedUser = user.toResponse( AuthDTO authenticatedUser = new AuthDTO(
user,
this.generateAccessToken(user, algorithm, request), this.generateAccessToken(user, algorithm, request),
new TokenDTO( new TokenDTO(
refreshToken, refreshToken,
@@ -153,48 +219,11 @@ public class AuthServiceImpl implements AuthService {
httpSession.setAttribute("user", authenticatedUser); httpSession.setAttribute("user", authenticatedUser);
return authenticatedUser; return authenticatedUser;
}
} else {
resolver.resolveException(
request,
response,
null,
new BadRequestException("Invalid Refresh Token. Please authenticate first.")
);
}
return null;
}
@Override
public UserDTO signupUser(@Valid UserDTO user, HttpServletRequest request) {
user.setProvider(Provider.LOCAL);
return this.generateUserWithTokens(
this.userService.saveUser(user),
request
);
} }
@Override @Override
public UserDTO generateUserWithTokens(UserDTO user, HttpServletRequest request) { public AuthDTO processOAuthPostLogin(@Valid UserDTO user, HttpServletRequest request) {
Algorithm algorithm = Algorithm.HMAC256(TOKEN_SECRET.getBytes());
HashMap<String, TokenDTO> tokens = this.generateTokens(user, algorithm, request);
HttpSession httpSession = request.getSession();
UserDTO userAuthenticated = user.toResponse(tokens.get("accessToken"), tokens.get("refreshToken"));
httpSession.setAttribute("user", userAuthenticated);
return userAuthenticated;
}
@Override
public UserDTO processOAuthPostLogin(@Valid UserDTO user, HttpServletRequest request) {
if (Objects.nonNull(user.getId())) { if (Objects.nonNull(user.getId())) {
this.userService.alterUser(user.getId(), user); this.userService.alterUser(user.getId(), user);
@@ -203,37 +232,48 @@ public class AuthServiceImpl implements AuthService {
} }
return this.generateUserWithTokens(user, request); return this.generateUserWithTokens(user, request);
} }
@Override @Override
public void loginUser(HttpServletRequest request, HttpServletResponse response, @Valid UserDTO user) throws IOException { public void loginOAuthUser(HttpServletRequest request,
HttpServletResponse response,
OAuth2User oauthUser) throws IOException {
UserDTO authenticatedUser = this.generateUserWithTokens( String clientId = this.getClientFromUrl(request.getRequestURL().toString());
user,
OAuthMap oauthMap = this.generateOAuthMap(clientId, oauthUser);
AuthDTO authObject = this.processOAuthPostLogin(
this.generateUserFromAuthUser(oauthMap, oauthUser),
request request
); );
response.setContentType(APPLICATION_JSON_VALUE); response.setContentType(APPLICATION_JSON_VALUE);
new ObjectMapper() new ObjectMapper()
.writeValue(response.getOutputStream(), authenticatedUser); .writeValue(response.getOutputStream(), authObject);
} }
public void loginOAuthUser(HttpServletRequest request, @Override
HttpServletResponse response, public UserDTO getLoggedUser() {
OAuth2User oauthUser) throws IOException { String username = (String) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
return userService.getUser(username);
}
String[] url = request.getRequestURL().toString().split("/"); private String getClientFromUrl(String url) {
String clientId = url[url.length-1]; String[] urlPartition = url.split("/");
return urlPartition[urlPartition.length - 1];
}
OAuthMap oauthMap = null; private OAuthMap generateOAuthMap(String clientId, OAuth2User oauthUser) {
try { try {
oauthMap = (OAuthMap) OAuthMapEnum.byValue(clientId).getMap() return (OAuthMap) OAuthMapper.byValue(clientId).getMap()
.getDeclaredConstructor(OAuth2User.class).newInstance(oauthUser); .getDeclaredConstructor(OAuth2User.class).newInstance(oauthUser);
} catch (Exception e) { } catch (Exception e) {
throw new BadRequestException("No Such Provider"); throw new BadRequestException("Unsupported OAuth Client.");
}
} }
private UserDTO generateUserFromAuthUser(OAuthMap oauthMap, OAuth2User oauthUser) {
UserDTO user = null; UserDTO user = null;
try { try {
user = this.userService.getUser(oauthMap.getPrincipal()); user = this.userService.getUser(oauthMap.getPrincipal());
@@ -246,16 +286,9 @@ public class AuthServiceImpl implements AuthService {
.provider(oauthMap.getProvider()) .provider(oauthMap.getProvider())
.build(); .build();
} }
user.setProfilePictureUrl(oauthMap.getProfilePicture());
UserDTO authenticatedUser = this.processOAuthPostLogin( return user;
user,
request
);
response.setContentType(APPLICATION_JSON_VALUE);
new ObjectMapper()
.writeValue(response.getOutputStream(), authenticatedUser);
} }
} }

View File

@@ -1,5 +1,6 @@
package com.hideyoshi.backendportfolio.base.session.api; package com.hideyoshi.backendportfolio.base.session.api;
import com.hideyoshi.backendportfolio.base.security.model.AuthDTO;
import com.hideyoshi.backendportfolio.base.session.service.SessionManagerService; import com.hideyoshi.backendportfolio.base.session.service.SessionManagerService;
import com.hideyoshi.backendportfolio.base.user.model.UserDTO; import com.hideyoshi.backendportfolio.base.user.model.UserDTO;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
@@ -19,7 +20,7 @@ public class SessionController {
private final SessionManagerService sessionManagerService; private final SessionManagerService sessionManagerService;
@GetMapping(path = "/validate") @GetMapping(path = "/validate")
public ResponseEntity<UserDTO> validateCurrentSession(HttpSession session) { public ResponseEntity<AuthDTO> validateCurrentSession(HttpSession session) {
return ResponseEntity.ok(this.sessionManagerService.validateSession(session)); return ResponseEntity.ok(this.sessionManagerService.validateSession(session));
} }

View File

@@ -1,12 +1,12 @@
package com.hideyoshi.backendportfolio.base.session.service; package com.hideyoshi.backendportfolio.base.session.service;
import com.hideyoshi.backendportfolio.base.user.model.UserDTO; import com.hideyoshi.backendportfolio.base.security.model.AuthDTO;
import javax.servlet.http.HttpSession; import javax.servlet.http.HttpSession;
public interface SessionManagerService { public interface SessionManagerService {
UserDTO validateSession(HttpSession session); AuthDTO validateSession(HttpSession session);
void destroySession(HttpSession session); void destroySession(HttpSession session);

View File

@@ -1,30 +1,19 @@
package com.hideyoshi.backendportfolio.base.session.service; package com.hideyoshi.backendportfolio.base.session.service;
import com.hideyoshi.backendportfolio.base.user.model.UserDTO; import com.hideyoshi.backendportfolio.base.security.model.AuthDTO;
import com.hideyoshi.backendportfolio.base.user.service.UserService; import com.hideyoshi.backendportfolio.base.user.service.UserService;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import javax.servlet.http.HttpSession; import javax.servlet.http.HttpSession;
import java.util.Objects;
@Service @Service
@RequiredArgsConstructor @RequiredArgsConstructor
public class SessionManagerServiceImpl implements SessionManagerService { public class SessionManagerServiceImpl implements SessionManagerService {
private final UserService userService;
@Override @Override
public UserDTO validateSession(HttpSession session) { public AuthDTO validateSession(HttpSession session) {
return (AuthDTO) session.getAttribute("user");
UserDTO sessionObjects = (UserDTO) session.getAttribute("user");
if (Objects.nonNull(sessionObjects)) {
return this.userService.getUser(sessionObjects.getUsername())
.toResponse(sessionObjects.getAccessToken(), sessionObjects.getRefreshToken());
}
return null;
} }
@Override @Override

View File

@@ -1,28 +1,28 @@
package com.hideyoshi.backendportfolio.base.user.api; package com.hideyoshi.backendportfolio.base.user.api;
import com.hideyoshi.backendportfolio.base.security.model.AuthDTO;
import com.hideyoshi.backendportfolio.base.security.service.AuthService; import com.hideyoshi.backendportfolio.base.security.service.AuthService;
import com.hideyoshi.backendportfolio.base.user.model.TokenDTO; import com.hideyoshi.backendportfolio.base.user.model.TokenDTO;
import com.hideyoshi.backendportfolio.base.user.model.UserDTO; import com.hideyoshi.backendportfolio.base.user.model.UserDTO;
import com.hideyoshi.backendportfolio.base.user.service.UserService; import com.hideyoshi.backendportfolio.base.user.service.UserService;
import com.hideyoshi.backendportfolio.microservice.storageService.enums.FileTypeEnum;
import com.hideyoshi.backendportfolio.microservice.storageService.model.StorageServiceDownloadResponse;
import com.hideyoshi.backendportfolio.microservice.storageService.model.StorageServiceUploadResponse;
import com.hideyoshi.backendportfolio.microservice.storageService.service.StorageService;
import com.hideyoshi.backendportfolio.util.guard.UserResourceGuard; import com.hideyoshi.backendportfolio.util.guard.UserResourceGuard;
import com.hideyoshi.backendportfolio.util.guard.UserResourceGuardEnum; import com.hideyoshi.backendportfolio.util.guard.UserResourceGuardEnum;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2; import lombok.extern.log4j.Log4j2;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.security.core.Authentication;
import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;
import org.springframework.stereotype.Controller; import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.support.ServletUriComponentsBuilder; import org.springframework.web.servlet.support.ServletUriComponentsBuilder;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid; import javax.validation.Valid;
import java.io.IOException;
import java.net.URI; import java.net.URI;
import java.security.Provider;
import java.util.List; import java.util.List;
@Log4j2 @Log4j2
@@ -36,6 +36,8 @@ public class UserController {
private final AuthService authService; private final AuthService authService;
private final StorageService storageService;
@GetMapping @GetMapping
@UserResourceGuard(accessType = UserResourceGuardEnum.ADMIN_USER) @UserResourceGuard(accessType = UserResourceGuardEnum.ADMIN_USER)
public ResponseEntity<List<UserDTO>> getUsers() { public ResponseEntity<List<UserDTO>> getUsers() {
@@ -44,7 +46,7 @@ public class UserController {
@PostMapping("/signup") @PostMapping("/signup")
@UserResourceGuard(accessType = UserResourceGuardEnum.OPEN) @UserResourceGuard(accessType = UserResourceGuardEnum.OPEN)
public ResponseEntity<UserDTO> signupUser(@RequestBody @Valid UserDTO user, HttpServletRequest request) { public ResponseEntity<AuthDTO> signupUser(@RequestBody @Valid UserDTO user, HttpServletRequest request) {
URI uri = URI.create( URI uri = URI.create(
ServletUriComponentsBuilder ServletUriComponentsBuilder
.fromCurrentContextPath() .fromCurrentContextPath()
@@ -55,20 +57,13 @@ public class UserController {
@PostMapping("/login/refresh") @PostMapping("/login/refresh")
@UserResourceGuard(accessType = UserResourceGuardEnum.OPEN) @UserResourceGuard(accessType = UserResourceGuardEnum.OPEN)
public ResponseEntity<UserDTO> refreshAccessToken( public ResponseEntity<AuthDTO> refreshAccessToken(
@RequestBody @Valid TokenDTO refreshToken, @RequestBody @Valid TokenDTO refreshToken,
HttpServletRequest request, HttpServletRequest request,
HttpServletResponse response) { HttpServletResponse response) {
return ResponseEntity.ok(this.authService.refreshAccessToken(refreshToken.getToken(), request, response)); return ResponseEntity.ok(this.authService.refreshAccessToken(refreshToken.getToken(), request, response));
} }
@GetMapping("/login/callback")
@UserResourceGuard(accessType = UserResourceGuardEnum.OPEN)
public void oauthCallback(HttpServletResponse response) throws IOException {
log.info("Teste");
response.sendRedirect("http://localhost:4200");
}
@DeleteMapping("/delete/{id}") @DeleteMapping("/delete/{id}")
@UserResourceGuard(accessType = UserResourceGuardEnum.SAME_USER) @UserResourceGuard(accessType = UserResourceGuardEnum.SAME_USER)
public ResponseEntity<Void> deleteUser(@PathVariable("id") Long id) { public ResponseEntity<Void> deleteUser(@PathVariable("id") Long id) {
@@ -76,4 +71,27 @@ public class UserController {
return new ResponseEntity<>(HttpStatus.NO_CONTENT); return new ResponseEntity<>(HttpStatus.NO_CONTENT);
} }
@PostMapping("/profile-picture")
@UserResourceGuard(accessType = UserResourceGuardEnum.USER)
public StorageServiceUploadResponse addProfilePicture(
@RequestParam FileTypeEnum fileType
) {
UserDTO user = this.authService.getLoggedUser();
return this.storageService.getNewFileUrl(
user.getUsername(),
"profile",
fileType
);
}
@PostMapping("/profile-picture/proccess")
@UserResourceGuard(accessType = UserResourceGuardEnum.USER)
public void processProfilePicture() {
UserDTO user = this.authService.getLoggedUser();
this.storageService.processFile(
user.getUsername(),
"profile"
);
}
} }

View File

@@ -1,6 +1,8 @@
package com.hideyoshi.backendportfolio.base.user.model; package com.hideyoshi.backendportfolio.base.user.model;
import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import lombok.*; import lombok.*;
import javax.validation.constraints.NotNull; import javax.validation.constraints.NotNull;
@@ -10,6 +12,8 @@ import java.util.Date;
@Data @Data
@NoArgsConstructor @NoArgsConstructor
@AllArgsConstructor @AllArgsConstructor
@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonIgnoreProperties(ignoreUnknown = true)
public class TokenDTO implements Serializable { public class TokenDTO implements Serializable {
@NotNull(message = "Invalid AccessToken. Please Authenticate first.") @NotNull(message = "Invalid AccessToken. Please Authenticate first.")

View File

@@ -53,9 +53,7 @@ public class UserDTO implements UserDetails {
@Size(min=1) @Size(min=1)
private List<Role> roles; private List<Role> roles;
private TokenDTO accessToken; private String profilePictureUrl;
private TokenDTO refreshToken;
private Provider provider; private Provider provider;
@@ -116,24 +114,13 @@ public class UserDTO implements UserDetails {
} }
public UserDTO toResponse() { public UserDTO toResponse() {
return UserDTO.builder()
.name(this.name)
.email(this.email)
.username(this.username)
.provider(this.provider)
.build();
}
public UserDTO toResponse(TokenDTO accessToken, TokenDTO refreshToken) {
return UserDTO.builder() return UserDTO.builder()
.id(this.id) .id(this.id)
.name(this.name) .name(this.name)
.email(this.email) .email(this.email)
.username(this.username) .username(this.username)
.provider(this.provider) .provider(this.provider)
.roles(this.roles) .profilePictureUrl(this.profilePictureUrl)
.accessToken(accessToken)
.refreshToken(refreshToken)
.build(); .build();
} }

View File

@@ -0,0 +1,13 @@
package com.hideyoshi.backendportfolio.microservice.storageService.config;
import lombok.Getter;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
@Getter
@Configuration
public class StorageServiceConfig {
@Value("${com.hideyoshi.microservice.storageServicePath}")
private String fileServicePath;
}

View File

@@ -0,0 +1,30 @@
package com.hideyoshi.backendportfolio.microservice.storageService.enums;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.hideyoshi.backendportfolio.util.exception.BadRequestException;
import lombok.Getter;
@Getter
@JsonFormat(shape = JsonFormat.Shape.STRING)
public enum FileTypeEnum {
PNG("png"),
JPEG("jpeg");
private final String fileExtension;
FileTypeEnum(String fileExtension) {
this.fileExtension = fileExtension;
}
public static FileTypeEnum fromValue(String value) {
for (FileTypeEnum e: FileTypeEnum.values()) {
if (e.getFileExtension().equals(value)) {
return e;
}
}
throw new BadRequestException("Invalid FileType.");
}
}

View File

@@ -0,0 +1,12 @@
package com.hideyoshi.backendportfolio.microservice.storageService.enums;
import org.springframework.core.convert.converter.Converter;
import org.springframework.stereotype.Component;
@Component
public class FileTypeEnumConverter implements Converter<String, FileTypeEnum> {
@Override
public FileTypeEnum convert(String value) {
return FileTypeEnum.fromValue(value);
}
}

View File

@@ -0,0 +1,15 @@
package com.hideyoshi.backendportfolio.microservice.storageService.model;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.*;
@Getter
@NoArgsConstructor
@AllArgsConstructor
public class StorageServiceDownloadResponse {
@JsonProperty("presigned_url")
private String presignedUrl;
}

View File

@@ -0,0 +1,20 @@
package com.hideyoshi.backendportfolio.microservice.storageService.model;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.Getter;
import lombok.NonNull;
@Getter
@AllArgsConstructor
public class StorageServiceUploadResponse {
@JsonProperty("presigned_url")
private String presignedUrl;
@JsonProperty("file_key")
private String fileKey;
}

View File

@@ -0,0 +1,166 @@
package com.hideyoshi.backendportfolio.microservice.storageService.service;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.hideyoshi.backendportfolio.microservice.storageService.config.StorageServiceConfig;
import com.hideyoshi.backendportfolio.microservice.storageService.enums.FileTypeEnum;
import com.hideyoshi.backendportfolio.microservice.storageService.model.StorageServiceDownloadResponse;
import com.hideyoshi.backendportfolio.microservice.storageService.model.StorageServiceUploadResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import org.apache.http.NameValuePair;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpHead;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.entity.ByteArrayEntity;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.DefaultRedirectStrategy;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.LaxRedirectStrategy;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.springframework.stereotype.Service;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@Log4j2
@Service
@RequiredArgsConstructor
public class StorageService {
private final ObjectMapper objectMapper;
private final StorageServiceConfig storageServiceConfig;
private final String PARAMETER_USERNAME = "username";
private final String PARAMETER_FILE_POSTFIX = "file_postfix";
private final String PARAMETER_FILE_TYPE = "file_type";
private final String PARAMETER_KEY_STRING = "string_url";
public StorageServiceUploadResponse getNewFileUrl(String username, String filePostfix, FileTypeEnum fileTypeEnum) {
HashMap<String, String> values = new HashMap<>() {{
put(PARAMETER_USERNAME, username);
put(PARAMETER_FILE_POSTFIX, filePostfix);
put(PARAMETER_FILE_TYPE, fileTypeEnum.getFileExtension());
}};
String requestBody = null;
try {
requestBody = objectMapper
.writeValueAsString(values);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
HttpPost request = new HttpPost(URI.create(storageServiceConfig.getFileServicePath() + "/new_file_url"));
request.setHeader("Content-Type", "application/json");
try {
request.setEntity(new ByteArrayEntity(requestBody.getBytes("UTF-8")));
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
CloseableHttpClient httpClient = HttpClientBuilder.create()
.setRedirectStrategy(new LaxRedirectStrategy()).build();
try {
return httpClient.execute(
request,
response -> {
String responseString = EntityUtils.toString(response.getEntity(), "UTF-8");
return objectMapper.readValue(responseString, StorageServiceUploadResponse.class);
}
);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public StorageServiceDownloadResponse getFileUrl(String username, String filePostfix) {
URI uri = null;
try {
uri = new URIBuilder(storageServiceConfig.getFileServicePath() + "/file_url")
.addParameter(PARAMETER_USERNAME, username)
.addParameter(PARAMETER_FILE_POSTFIX, filePostfix)
.build();
} catch (URISyntaxException e) {
throw new RuntimeException(e);
}
HttpGet request = new HttpGet(uri);
request.setHeader("Content-Type", "application/json");
CloseableHttpClient httpClient = HttpClientBuilder.create()
.setRedirectStrategy(new LaxRedirectStrategy()).build();
try {
return httpClient.execute(
request,
response -> {
String responseString = EntityUtils.toString(response.getEntity(), "UTF-8");
return objectMapper.readValue(responseString, StorageServiceDownloadResponse.class);
}
);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public void processFile(String username, String filePostfix) {
HashMap<String, String> values = new HashMap<>() {{
put(PARAMETER_USERNAME, username);
put(PARAMETER_FILE_POSTFIX, filePostfix);
}};
ObjectMapper objectMapper = new ObjectMapper();
String requestBody = null;
try {
requestBody = objectMapper
.writeValueAsString(values);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
HttpPost request = new HttpPost(URI.create(storageServiceConfig.getFileServicePath() + "/process_file"));
request.setHeader("Content-Type", "application/json");
try {
request.setEntity(new ByteArrayEntity(requestBody.getBytes("UTF-8")));
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
CloseableHttpClient httpClient = HttpClientBuilder.create()
.setRedirectStrategy(new LaxRedirectStrategy()).build();
try {
httpClient.execute(
request,
response -> {
String responseString = EntityUtils.toString(response.getEntity(), "UTF-8");
return objectMapper.readValue(responseString, StorageServiceUploadResponse.class);
}
);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}

View File

@@ -4,6 +4,7 @@ import com.fasterxml.jackson.databind.ObjectMapper;
import com.hideyoshi.backendportfolio.base.user.entity.Role; import com.hideyoshi.backendportfolio.base.user.entity.Role;
import com.hideyoshi.backendportfolio.base.user.model.UserDTO; import com.hideyoshi.backendportfolio.base.user.model.UserDTO;
import com.hideyoshi.backendportfolio.base.user.service.UserService; import com.hideyoshi.backendportfolio.base.user.service.UserService;
import lombok.Getter;
import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.servlet.HandlerMapping; import org.springframework.web.servlet.HandlerMapping;
@@ -11,6 +12,7 @@ import org.springframework.web.servlet.HandlerMapping;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import java.util.HashMap; import java.util.HashMap;
@Getter
public enum UserResourceGuardEnum { public enum UserResourceGuardEnum {
USER("user") { USER("user") {
@@ -64,10 +66,6 @@ public enum UserResourceGuardEnum {
ObjectMapper objectMapper, ObjectMapper objectMapper,
HttpServletRequest request); HttpServletRequest request);
public String getAccessType() {
return this.accessType;
}
public static UserResourceGuardEnum byValue(String accessType) { public static UserResourceGuardEnum byValue(String accessType) {
for (UserResourceGuardEnum o : values()) { for (UserResourceGuardEnum o : values()) {
if (o.getAccessType().equals(accessType)) { if (o.getAccessType().equals(accessType)) {

View File

@@ -10,6 +10,9 @@ com:
username: ${DEFAULT_USER_USERNAME} username: ${DEFAULT_USER_USERNAME}
password: ${DEFAULT_USER_PASSWORD} password: ${DEFAULT_USER_PASSWORD}
microservice:
storageServicePath: ${STORAGE_SERVICE_PATH}
server: server:
servlet: servlet:

View File

@@ -40,8 +40,6 @@ class UserServiceImplTest {
private PasswordEncoder passwordEncoder; private PasswordEncoder passwordEncoder;
private AuthService authService;
@BeforeEach @BeforeEach
void setUp() { void setUp() {