- 앱 배포 요청 받은후 request ID 저장 로직 추가

- authorization token 판별 로직 추가
This commit is contained in:
hyunjujeong 2023-08-17 18:32:20 +09:00
parent d32521e412
commit a3a7ce77cf
20 changed files with 370 additions and 36 deletions

View File

@ -15,6 +15,10 @@ jar {
enabled = false
}
ext {
jjwtVersion = "0.11.5"
}
repositories {
mavenCentral()
}
@ -26,6 +30,9 @@ dependencies {
implementation("org.springframework.boot:spring-boot-starter-amqp")
implementation("org.springframework.boot:spring-boot-starter-web")
implementation("org.springframework.integration:spring-integration-mqtt")
implementation("io.jsonwebtoken:jjwt-api:${jjwtVersion}")
runtimeOnly("io.jsonwebtoken:jjwt-impl:${jjwtVersion}")
runtimeOnly("io.jsonwebtoken:jjwt-jackson:${jjwtVersion}")
runtimeOnly("org.postgresql:postgresql")
testImplementation("org.springframework.boot:spring-boot-starter-test")
annotationProcessor "org.springframework.boot:spring-boot-configuration-processor"

View File

@ -3,6 +3,7 @@ package inc.sdt.blokworks.devicedeployer.application;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import inc.sdt.blokworks.devicedeployer.domain.AssetApp;
import inc.sdt.blokworks.devicedeployer.domain.DeployRequest;
import inc.sdt.blokworks.devicedeployer.domain.OutboundMessage;
import inc.sdt.blokworks.devicedeployer.domain.OperationType;
import inc.sdt.blokworks.devicedeployer.infrastructure.mqtt.InboundDeployMessagePayload;
@ -14,33 +15,42 @@ import org.eclipse.paho.client.mqttv3.MqttException;
import org.eclipse.paho.client.mqttv3.MqttMessage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.Page;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Mono;
import java.util.Optional;
@Service
public class DefaultDeployerService implements DeployerService{
private final Logger log;
private final IMqttClient mqttClient;
private final ObjectMapper objectMapper;
private final DeployerRepositoryDelegate deployerRepositoryDelegate;
private String requestId;
private final DeployRequestRepositoryDelegate requestRepositoryDelegate;
private final String filePath;
public DefaultDeployerService(IMqttClient mqttClient,
ObjectMapper objectMapper,
DeployerRepositoryDelegate deployerRepositoryDelegate) {
DeployerRepositoryDelegate deployerRepositoryDelegate,
DeployRequestRepositoryDelegate requestRepositoryDelegate,
@Value("${stackbase.api.host}") String filePath) {
this.log = LoggerFactory.getLogger(this.getClass());
this.mqttClient = mqttClient;
this.objectMapper = objectMapper;
this.deployerRepositoryDelegate = deployerRepositoryDelegate;
this.requestRepositoryDelegate = requestRepositoryDelegate;
this.filePath = filePath;
}
@Override
public void publish(OutboundMessage deployMessage, String assetCode) {
log.info("[publish]");
log.info("[publish] deployMessage = {}, assetCode = {}", deployMessage, assetCode);
final String url = filePath + deployMessage.getFileId();
try {
OutboundMessagePayload payload = new OutboundMessagePayload(
deployMessage.getFileId(),
url,
deployMessage.getName(),
deployMessage.getCommand(),
deployMessage.getEnv(),
@ -53,24 +63,29 @@ public class DefaultDeployerService implements DeployerService{
message.setPayload(bytes);
mqttClient.publish("/assets/"+assetCode+"/apps/deploy", message);
requestId = deployMessage.getRequestId();
log.info("[publish] message = {}", message);
}catch (JsonProcessingException | MqttException e) {
throw new IllegalArgumentException();
}
}
@Override
public DeployRequest save(DeployRequest deployRequest) {
log.info("[save] deployRequest = {}", deployRequest);
requestRepositoryDelegate.save(deployRequest);
return deployRequest;
}
@Override
public Mono<Void> apply(InboundDeployMessagePayload payload) {
log.info("[apply] inboundDeployMessagePayload = {}", payload);
// 배포된 앱 정보 저장
// request Id 판별
if(requestId.equals(payload.requestId())) {
log.info("[apply] payload = {}", payload);
Optional<DeployRequest> deployRequest = requestRepositoryDelegate.findByRequestId(payload.requestId());
if(deployRequest.isPresent()) {
return Mono.just(payload)
.doOnNext(deployerRepositoryDelegate)
.then();
}else {
throw new ConflictException("This process is already exists.");
throw new IllegalArgumentException();
}
}

View File

@ -0,0 +1,10 @@
package inc.sdt.blokworks.devicedeployer.application;
import inc.sdt.blokworks.devicedeployer.domain.DeployRequest;
import java.util.Optional;
public interface DeployRequestRepositoryDelegate {
DeployRequest save(DeployRequest deployRequest);
Optional<DeployRequest> findByRequestId(String requestId);
}

View File

@ -1,6 +1,7 @@
package inc.sdt.blokworks.devicedeployer.application;
import inc.sdt.blokworks.devicedeployer.domain.AssetApp;
import inc.sdt.blokworks.devicedeployer.domain.DeployRequest;
import inc.sdt.blokworks.devicedeployer.domain.OutboundMessage;
import inc.sdt.blokworks.devicedeployer.infrastructure.mqtt.InboundDeployMessagePayload;
import org.springframework.data.domain.Page;
@ -10,5 +11,6 @@ import java.util.function.Function;
public interface DeployerService extends Function<InboundDeployMessagePayload, Mono<Void>> {
void publish(OutboundMessage assetApp, String assetCode);
DeployRequest save(DeployRequest deployRequest);
Page<AssetApp> getAll(String assetCode, int page, int size);
}

View File

@ -0,0 +1,83 @@
package inc.sdt.blokworks.devicedeployer.domain;
public class DeployRequest {
private String requestId;
private String assetCode;
private String appName;
private OperationType operationType;
protected DeployRequest() {}
public DeployRequest(String requestId, String assetCode, String appName, OperationType operationType) {
this.requestId = requestId;
this.assetCode = assetCode;
this.appName = appName;
this.operationType = operationType;
}
public String getRequestId() {
return requestId;
}
public String getAssetCode() {
return assetCode;
}
public String getAppName() {
return appName;
}
public OperationType getOperationType() {
return operationType;
}
@Override
public String toString() {
return "DeployRequest{" +
"requestId='" + requestId + '\'' +
", assetCode='" + assetCode + '\'' +
", appName='" + appName + '\'' +
", operationType='" + operationType + '\'' +
'}';
}
public static Builder builder() {
return new Builder();
}
public static final class Builder {
private String requestId;
private String assetCode;
private String appName;
private OperationType operationType;
public Builder requestId(String requestId) {
this.requestId = requestId;
return this;
}
public Builder assetCode(String assetCode) {
this.assetCode = assetCode;
return this;
}
public Builder appName(String appName) {
this.appName = appName;
return this;
}
public Builder operationType(OperationType operationType) {
this.operationType = operationType;
return this;
}
public DeployRequest build() {
DeployRequest deployRequest = new DeployRequest();
deployRequest.requestId = this.requestId;
deployRequest.assetCode = this.assetCode;
deployRequest.appName = this.appName;
deployRequest.operationType = this.operationType;
return deployRequest;
}
}
}

View File

@ -8,7 +8,6 @@ public class OutboundMessage {
private String name;
private HashMap<String, String> env;
private String command;
private OperationType operationType;
private String requestId;
protected OutboundMessage() {}
@ -29,10 +28,6 @@ public class OutboundMessage {
return command;
}
public OperationType getOperationType() {
return operationType;
}
public void setRequestId(String requestId) {
this.requestId = requestId;
}
@ -48,7 +43,6 @@ public class OutboundMessage {
", name='" + name + '\'' +
", env=" + env +
", command='" + command + '\'' +
", operationType=" + operationType +
", requestId='" + requestId + '\'' +
'}';
}
@ -62,7 +56,6 @@ public class OutboundMessage {
private String name;
private HashMap<String, String> env;
private String command;
private OperationType operationType;
private String requestId;
public Builder fileId(String fileId) {
@ -85,11 +78,6 @@ public class OutboundMessage {
return this;
}
public Builder operationType(OperationType operationType) {
this.operationType = operationType;
return this;
}
public Builder requestId(String requestId) {
this.requestId = requestId;
return this;
@ -101,7 +89,6 @@ public class OutboundMessage {
deployMessage.name = this.name;
deployMessage.env = this.env;
deployMessage.command = this.command;
deployMessage.operationType = this.operationType;
deployMessage.requestId = this.requestId;
return deployMessage;
}

View File

@ -5,10 +5,10 @@ import inc.sdt.blokworks.devicedeployer.domain.Status;
public record InboundDeployMessagePayload(
Status status,
String assetCode,
String requestId,
String name,
Long size,
Long releasedAt,
Long updatedAt,
String requestId
Long updatedAt
) {
}

View File

@ -2,8 +2,6 @@ package inc.sdt.blokworks.devicedeployer.infrastructure.relational;
import jakarta.persistence.*;
import java.sql.Timestamp;
@Entity(name = "asset_app")
class AssetAppEntity {
@Id

View File

@ -23,6 +23,7 @@ public class AssetAppRelationalRepository implements DeployerRepositoryDelegate
@Override
public void accept(InboundDeployMessagePayload inboundDeployMessagePayload) {
log.info("[accept] payload = {}", inboundDeployMessagePayload);
this.save(this.fromMessage(inboundDeployMessagePayload));
}

View File

@ -0,0 +1,50 @@
package inc.sdt.blokworks.devicedeployer.infrastructure.relational;
import inc.sdt.blokworks.devicedeployer.domain.OperationType;
import jakarta.persistence.*;
@Entity(name = "deploy_request")
public class DeployRequestEntity {
@Id
@GeneratedValue(strategy = GenerationType.UUID)
@Column(name = "id")
private String id;
@Column(name = "request_id")
private String requestId;
@Column(name = "asset_code", length = 255)
private String assetCode;
@Column(name = "app_name", length = 255)
private String appName;
@Enumerated(EnumType.STRING)
@Column(name = "operation_type", length = 255)
private OperationType operationType;
protected DeployRequestEntity() {}
public DeployRequestEntity(String requestId, String assetCode, String appName, OperationType operationType) {
this.requestId = requestId;
this.assetCode = assetCode;
this.appName = appName;
this.operationType = operationType;
}
public String getId() {
return id;
}
public String getRequestId() {
return requestId;
}
public String getAssetCode() {
return assetCode;
}
public String getAppName() {
return appName;
}
public OperationType getOperationType() {
return operationType;
}
}

View File

@ -0,0 +1,7 @@
package inc.sdt.blokworks.devicedeployer.infrastructure.relational;
import org.springframework.data.jpa.repository.JpaRepository;
public interface DeployRequestJpaRepository extends JpaRepository<DeployRequestEntity, String> {
DeployRequestEntity findByRequestId(String requestId);
}

View File

@ -0,0 +1,54 @@
package inc.sdt.blokworks.devicedeployer.infrastructure.relational;
import inc.sdt.blokworks.devicedeployer.application.DeployRequestRepositoryDelegate;
import inc.sdt.blokworks.devicedeployer.domain.DeployRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import java.util.Optional;
@Component
public class DeployRequestRelationalRepository implements DeployRequestRepositoryDelegate {
private final Logger log;
private final DeployRequestJpaRepository deployRequestJpaRepository;
public DeployRequestRelationalRepository(DeployRequestJpaRepository deployRequestJpaRepository) {
this.log = LoggerFactory.getLogger(this.getClass());
this.deployRequestJpaRepository = deployRequestJpaRepository;
}
@Override
public DeployRequest save(DeployRequest deployRequest) {
log.info("[save] deployRequest = {}", deployRequest);
DeployRequestEntity entity = this.toEntity(deployRequest);
deployRequestJpaRepository.save(entity);
return deployRequest;
}
@Override
public Optional<DeployRequest> findByRequestId(String requestId) {
log.info("[findByRequestId] requestId = {}", requestId);
DeployRequestEntity entity = deployRequestJpaRepository.findByRequestId(requestId);
log.info("[findByRequestId] entity = {}", entity);
return Optional.of(this.fromEntity(entity));
}
private DeployRequestEntity toEntity(DeployRequest deployRequest) {
return new DeployRequestEntity(
deployRequest.getRequestId(),
deployRequest.getAssetCode(),
deployRequest.getAppName(),
deployRequest.getOperationType()
);
}
private DeployRequest fromEntity(DeployRequestEntity entity) {
return DeployRequest.builder()
.requestId(entity.getRequestId())
.assetCode(entity.getAssetCode())
.appName(entity.getAppName())
.operationType(entity.getOperationType())
.build();
}
}

View File

@ -2,15 +2,14 @@ package inc.sdt.blokworks.devicedeployer.presentation;
import inc.sdt.blokworks.devicedeployer.application.DeployerService;
import inc.sdt.blokworks.devicedeployer.application.ProcessService;
import inc.sdt.blokworks.devicedeployer.domain.AssetApp;
import inc.sdt.blokworks.devicedeployer.domain.OperationType;
import inc.sdt.blokworks.devicedeployer.domain.OutboundMessage;
import inc.sdt.blokworks.devicedeployer.domain.*;
import inc.sdt.blokworks.devicedeployer.domain.Process;
import inc.sdt.blokworks.devicedeployer.infrastructure.amqp.ResourceMapping;
import jakarta.servlet.http.HttpSession;
import jakarta.validation.Valid;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.Page;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;
@ -47,6 +46,15 @@ public class DeployerController {
String requestId = UUID.randomUUID().toString();
OutboundMessage outboundMessage = outboundMessageResourceConverter.fromResource(assetAppResource);
outboundMessage.setRequestId(requestId);
DeployRequest deployRequest = DeployRequest.builder()
.requestId(requestId)
.assetCode(assetCode)
.appName(outboundMessage.getName())
.operationType(OperationType.DEPLOY)
.build();
DeployRequest request = deployerService.save(deployRequest);
deployerService.publish(outboundMessage, assetCode);
}

View File

@ -44,11 +44,11 @@ public class MqttMessageHandler {
.map(p -> new InboundDeployMessagePayload(
p.status(),
p.assetCode(),
id,
p.name(),
p.size(),
p.releasedAt(),
p.updatedAt(),
id))
p.updatedAt()))
.flatMap(deployerService)
.subscribe();
}else {

View File

@ -0,0 +1,31 @@
package inc.sdt.blokworks.devicedeployer.presentation.configuration;
import inc.sdt.blokworks.devicedeployer.presentation.filter.AuthorizationFilter;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Component
class WebConfiguration implements WebMvcConfigurer {
private final AuthorizationFilter authorizationFilter;
public WebConfiguration(AuthorizationFilter authorizationFilter) {
this.authorizationFilter = authorizationFilter;
}
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*")
.allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
.maxAge(3600);
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(authorizationFilter).addPathPatterns(
"/assets/**"
);
}
}

View File

@ -0,0 +1,14 @@
package inc.sdt.blokworks.devicedeployer.presentation.exception;
public class UnauthorizedException extends RuntimeException {
private static final String DEFAULT_MESSAGE = "Unauthorized";
public UnauthorizedException() {
super(DEFAULT_MESSAGE);
}
public UnauthorizedException(String message) {
super(message);
}
}

View File

@ -0,0 +1,50 @@
package inc.sdt.blokworks.devicedeployer.presentation.filter;
import inc.sdt.blokworks.devicedeployer.presentation.exception.UnauthorizedException;
import io.jsonwebtoken.ExpiredJwtException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import java.util.NoSuchElementException;
@Component
public class AuthorizationFilter implements HandlerInterceptor {
private final Logger log;
private final String secretKey;
public AuthorizationFilter(@Value("${application.security.jwt.secret-key}") String secretKey) {
this.secretKey = secretKey;
this.log = LoggerFactory.getLogger(this.getClass());
}
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
log.info("[preHandle]");
if(request.getHeader("Authorization") == null) {
throw new NoSuchElementException("token does not exist.");
}
String token = request.getHeader("Authorization").substring("Bearer ".length());
byte[] keyBytes = io.jsonwebtoken.io.Decoders.BASE64.decode(secretKey);
javax.crypto.SecretKey secretKey = io.jsonwebtoken.security.Keys.hmacShaKeyFor(keyBytes);
try {
io.jsonwebtoken.Claims claims = io.jsonwebtoken.Jwts
.parserBuilder()
.setSigningKey(secretKey)
.build()
.parseClaimsJws(token)
.getBody();
request.setAttribute("userId", claims.get("id").toString());
return true;
}catch (ExpiredJwtException e) {
throw new UnauthorizedException("Token has expired");
}
}
}

View File

@ -27,4 +27,8 @@ iam:
username: ${IAM_AMQP_CREDENTIALS_USERNAME}
password: ${IAM_AMQP_CREDENTIALS_PASSWORD}
exchange: ${IAM_AMQP_EXCHANGE}
routing-key: ${IAM_AMQP_ROUTING_KEY}
routing-key: ${IAM_AMQP_ROUTING_KEY}
stackbase:
api:
host: https://sdt-site-bucket.s3.ap-northeast-2.amazonaws.com/app/

View File

@ -13,8 +13,13 @@ spring:
inbound:
mqtt:
url: tcp://localhost:1883
#url: tcp://13.209.39.139:32259
username: sdt
password: 251327
topics:
- /assets/+/command-req/+
- /assets/+/apps/process
- /assets/+/apps/process
stackbase:
api:
host: https://sdt-site-bucket.s3.ap-northeast-2.amazonaws.com/app/

View File

@ -5,12 +5,20 @@ spring:
name: device-deployer
datasource:
driver-class-name: org.postgresql.Driver
application:
security:
jwt:
secret-key: D3KJ3G92IWW3W3QWZLU416IN4T9AFYEWM84P5HKHRFEA8C4I1HWALFXGP2HYD87Q
inbound:
mqtt:
url: tcp://192.168.1.162:32102
url: tcp://13.209.39.139:32259
username: sdt
password: 251327
topics:
- /assets/+/command-req/+
- /assets/+/apps/process
- /assets/+/apps/process
stackbase:
api:
host: https://sdt-site-bucket.s3.ap-northeast-2.amazonaws.com/app/