Backend migration
This commit is contained in:
82
backend/gateway/pom.xml
Normal file
82
backend/gateway/pom.xml
Normal file
@@ -0,0 +1,82 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>dev.rheinsw</groupId>
|
||||
<artifactId>rheinsw-backend</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>gateway</artifactId>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>21</maven.compiler.source>
|
||||
<maven.compiler.target>21</maven.compiler.target>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-gateway</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-jpa</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Other Tools -->
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<version>${lombok.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.postgresql</groupId>
|
||||
<artifactId>postgresql</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Tests -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-core</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-junit-jupiter</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.h2database</groupId>
|
||||
<artifactId>h2</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.wiremock</groupId>
|
||||
<artifactId>wiremock-standalone</artifactId>
|
||||
<version>3.13.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>dev.rheinsw</groupId>
|
||||
<artifactId>shared</artifactId>
|
||||
<version>1.0.0</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -0,0 +1,15 @@
|
||||
package dev.rheinsw.gateway;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
/**
|
||||
* @author Bummsa / BoomerHD / Thatsaphorn Atchariyaphap
|
||||
* @since 21.04.25
|
||||
*/
|
||||
@SpringBootApplication
|
||||
public class GatewayApplication {
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(GatewayApplication.class, args);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
package dev.rheinsw.gateway.filter;
|
||||
|
||||
import dev.rheinsw.gateway.service.ApiKeyValidator;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.cloud.gateway.filter.GlobalFilter;
|
||||
import org.springframework.cloud.gateway.route.Route;
|
||||
import org.springframework.cloud.gateway.support.ServerWebExchangeUtils;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
/**
|
||||
* @author Thatsaphorn Atchariyaphap
|
||||
* @since 24.04.25
|
||||
*/
|
||||
@Component
|
||||
@Order(-1)
|
||||
@RequiredArgsConstructor
|
||||
public class ApiKeyGatewayFilter implements GlobalFilter {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(ApiKeyGatewayFilter.class);
|
||||
|
||||
@Value("${gateway.security.frontend-origin}")
|
||||
private String expectedFrontendOrigin;
|
||||
|
||||
private final ApiKeyValidator apiKeyValidator;
|
||||
|
||||
@Override
|
||||
public Mono<Void> filter(ServerWebExchange exchange, org.springframework.cloud.gateway.filter.GatewayFilterChain chain) {
|
||||
String path = exchange.getRequest().getURI().getPath();
|
||||
log.debug("Incoming request for path: {}", path);
|
||||
|
||||
if (path.startsWith("/api/public/")) {
|
||||
log.debug("Public path detected, skipping API key validation");
|
||||
return chain.filter(exchange);
|
||||
}
|
||||
|
||||
String frontendKey = exchange.getRequest().getHeaders().getFirst("X-Frontend-Key");
|
||||
String internalKey = exchange.getRequest().getHeaders().getFirst("X-Internal-Auth");
|
||||
String targetService = resolveTargetServiceFromExchange(exchange);
|
||||
boolean isFrontend = isFrontendRequest(exchange);
|
||||
|
||||
log.debug("Target service resolved: {}, isFrontend: {}", targetService, isFrontend);
|
||||
|
||||
if (frontendKey != null && isFrontend) {
|
||||
log.debug("Validating frontend API key for service: {}", targetService);
|
||||
if (apiKeyValidator.isAuthorized(frontendKey, targetService, true)) {
|
||||
log.debug("Frontend API key authorized");
|
||||
return chain.filter(exchange);
|
||||
}
|
||||
log.warn("Shared API key validation failed for service: {}", targetService);
|
||||
}
|
||||
|
||||
if (internalKey != null) {
|
||||
log.debug("Validating internal API key for service: {}", targetService);
|
||||
if (apiKeyValidator.isAuthorized(internalKey, targetService, false)) {
|
||||
log.debug("Internal API key authorized");
|
||||
return chain.filter(exchange);
|
||||
}
|
||||
log.warn("Internal API key validation failed for service: {}", targetService);
|
||||
}
|
||||
|
||||
log.warn("Unauthorized request to {} from origin {}", path, exchange.getRequest().getHeaders().getFirst("Origin"));
|
||||
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
|
||||
return exchange.getResponse().setComplete();
|
||||
}
|
||||
|
||||
private String resolveTargetServiceFromExchange(ServerWebExchange exchange) {
|
||||
Route route = exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR);
|
||||
return route != null ? route.getId() : "unknown";
|
||||
}
|
||||
|
||||
private boolean isFrontendRequest(ServerWebExchange exchange) {
|
||||
String origin = exchange.getRequest().getHeaders().getFirst("Origin");
|
||||
boolean matches = origin != null && origin.trim().equalsIgnoreCase(expectedFrontendOrigin.trim());
|
||||
log.debug("Origin header: {}, expected: {}, matches: {}", origin, expectedFrontendOrigin, matches);
|
||||
return matches;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
package dev.rheinsw.gateway.model;
|
||||
|
||||
import dev.rheinsw.shared.entity.BaseEntity;
|
||||
import jakarta.persistence.CollectionTable;
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.ElementCollection;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.EnumType;
|
||||
import jakarta.persistence.Enumerated;
|
||||
import jakarta.persistence.FetchType;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.JoinColumn;
|
||||
import jakarta.persistence.Table;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author Thatsaphorn Atchariyaphap
|
||||
* @since 25.04.25
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "api_key")
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
public class ApiKey extends BaseEntity {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
@Column(name = "api_key", unique = true, nullable = false)
|
||||
private String apiKey;
|
||||
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Column(nullable = false)
|
||||
private ApiKeyType type;
|
||||
|
||||
@ElementCollection(fetch = FetchType.EAGER)
|
||||
@CollectionTable(name = "api_key_services", joinColumns = @JoinColumn(name = "api_key_id"))
|
||||
@Column(name = "allowed_service")
|
||||
private Set<String> allowedServices;
|
||||
|
||||
@Column(nullable = false)
|
||||
private boolean enabled;
|
||||
|
||||
@Column(name = "frontend_only", nullable = false)
|
||||
private boolean frontendOnly;
|
||||
|
||||
@Column
|
||||
private String description;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package dev.rheinsw.gateway.model;
|
||||
|
||||
/**
|
||||
* @author Thatsaphorn Atchariyaphap
|
||||
* @since 25.04.25
|
||||
*/
|
||||
public enum ApiKeyType {
|
||||
FRONTEND,
|
||||
INTERNAL;
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package dev.rheinsw.gateway.repository;
|
||||
|
||||
import dev.rheinsw.gateway.model.ApiKey;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* @author Thatsaphorn Atchariyaphap
|
||||
* @since 25.04.25
|
||||
*/
|
||||
public interface ApiKeyRepository extends JpaRepository<ApiKey, Long> {
|
||||
Optional<ApiKey> findByApiKey(String apiKey);
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package dev.rheinsw.gateway.service;
|
||||
|
||||
import dev.rheinsw.gateway.model.ApiKey;
|
||||
import dev.rheinsw.gateway.repository.ApiKeyRepository;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* @author Thatsaphorn Atchariyaphap
|
||||
* @since 25.04.25
|
||||
*/
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
@Slf4j
|
||||
public class ApiKeyValidator {
|
||||
|
||||
private final ApiKeyRepository apiKeyRepository;
|
||||
|
||||
public boolean isAuthorized(String key, String targetService, boolean isFrontendRequest) {
|
||||
return apiKeyRepository.findByApiKey(key)
|
||||
.filter(ApiKey::isEnabled)
|
||||
.filter(apiKey -> !apiKey.isFrontendOnly() || isFrontendRequest)
|
||||
.filter(apiKey -> apiKey.getAllowedServices().contains(targetService))
|
||||
.isPresent();
|
||||
}
|
||||
}
|
||||
41
backend/gateway/src/main/resources/application.yml
Normal file
41
backend/gateway/src/main/resources/application.yml
Normal file
@@ -0,0 +1,41 @@
|
||||
server:
|
||||
port: 8080
|
||||
|
||||
eureka:
|
||||
client:
|
||||
service-url:
|
||||
defaultZone: http://localhost:8761/eureka/
|
||||
|
||||
gateway:
|
||||
security:
|
||||
frontend-origin: ${FRONTEND_ORIGIN}
|
||||
|
||||
spring:
|
||||
application:
|
||||
name: gateway
|
||||
datasource:
|
||||
url: jdbc:postgresql://localhost:5432/rheinsw_dev
|
||||
username: rheinsw
|
||||
password: rheinsw
|
||||
jpa:
|
||||
hibernate:
|
||||
ddl-auto: none
|
||||
show-sql: true
|
||||
properties:
|
||||
hibernate:
|
||||
format_sql: true
|
||||
cloud:
|
||||
gateway:
|
||||
routes:
|
||||
- id: contactService
|
||||
uri: lb://contactService
|
||||
predicates:
|
||||
- Path=/api/contact/**
|
||||
filters:
|
||||
- StripPrefix=1
|
||||
- id: mailService
|
||||
uri: lb://mailService
|
||||
predicates:
|
||||
- Path=/api/mail/**
|
||||
filters:
|
||||
- StripPrefix=1
|
||||
@@ -0,0 +1,58 @@
|
||||
-- 1. Create tables with timestamps correctly
|
||||
|
||||
CREATE TABLE api_key
|
||||
(
|
||||
id BIGINT GENERATED BY DEFAULT AS IDENTITY NOT NULL,
|
||||
api_key VARCHAR(255) NOT NULL,
|
||||
type VARCHAR(255) NOT NULL,
|
||||
enabled BOOLEAN NOT NULL,
|
||||
frontend_only BOOLEAN NOT NULL,
|
||||
createdDateTime TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
modifiedDateTime TIMESTAMP NULL,
|
||||
CONSTRAINT pk_api_key PRIMARY KEY (id)
|
||||
);
|
||||
|
||||
CREATE TABLE api_key_services
|
||||
(
|
||||
api_key_id BIGINT NOT NULL,
|
||||
allowed_service VARCHAR(255),
|
||||
createdDateTime TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
modifiedDateTime TIMESTAMP NULL
|
||||
);
|
||||
|
||||
-- 2. Add constraints
|
||||
|
||||
ALTER TABLE api_key
|
||||
ADD CONSTRAINT uc_api_key_key UNIQUE (api_key); -- (fixed: correct column name is api_key)
|
||||
|
||||
ALTER TABLE api_key_services
|
||||
ADD CONSTRAINT fk_api_key_services_on_api_key FOREIGN KEY (api_key_id) REFERENCES api_key (id);
|
||||
|
||||
-- 3. Function to update modifiedDateTime if any real change occurs
|
||||
|
||||
CREATE
|
||||
OR REPLACE FUNCTION set_modified_datetime()
|
||||
RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
IF
|
||||
(OLD IS DISTINCT FROM NEW) THEN
|
||||
NEW.modifiedDateTime = CURRENT_TIMESTAMP;
|
||||
END IF;
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$
|
||||
LANGUAGE plpgsql;
|
||||
|
||||
-- 4. Triggers to update modifiedDateTime
|
||||
|
||||
CREATE TRIGGER trg_set_modified_datetime_api_key
|
||||
BEFORE UPDATE
|
||||
ON api_key
|
||||
FOR EACH ROW
|
||||
EXECUTE FUNCTION set_modified_datetime();
|
||||
|
||||
CREATE TRIGGER trg_set_modified_datetime_api_key_services
|
||||
BEFORE UPDATE
|
||||
ON api_key_services
|
||||
FOR EACH ROW
|
||||
EXECUTE FUNCTION set_modified_datetime();
|
||||
@@ -0,0 +1,121 @@
|
||||
package dev.rheinsw.gateway.filter;
|
||||
|
||||
import dev.rheinsw.gateway.service.ApiKeyValidator;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
|
||||
import org.springframework.cloud.gateway.route.Route;
|
||||
import org.springframework.cloud.gateway.support.ServerWebExchangeUtils;
|
||||
import org.springframework.mock.http.server.reactive.MockServerHttpRequest;
|
||||
import org.springframework.mock.web.server.MockServerWebExchange;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import static org.mockito.Mockito.*;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
class ApiKeyGatewayFilterTest {
|
||||
|
||||
@Mock
|
||||
private ApiKeyValidator apiKeyValidator;
|
||||
|
||||
@Mock
|
||||
private GatewayFilterChain chain;
|
||||
|
||||
private ApiKeyGatewayFilter filter;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
MockitoAnnotations.openMocks(this);
|
||||
filter = new ApiKeyGatewayFilter(apiKeyValidator);
|
||||
|
||||
// Inject expectedFrontendOrigin manually
|
||||
ReflectionTestUtils.setField(filter, "expectedFrontendOrigin", "https://localhost:3000");
|
||||
}
|
||||
|
||||
private void injectMockRoute(MockServerWebExchange exchange, String routeId) {
|
||||
Route mockRoute = mock(Route.class);
|
||||
when(mockRoute.getId()).thenReturn(routeId);
|
||||
exchange.getAttributes().put(ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR, mockRoute);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldAllowPublicRequestWithoutValidation() {
|
||||
MockServerHttpRequest request = MockServerHttpRequest.get("/api/public/test").build();
|
||||
MockServerWebExchange exchange = MockServerWebExchange.from(request);
|
||||
|
||||
when(chain.filter(exchange)).thenReturn(Mono.empty());
|
||||
|
||||
filter.filter(exchange, chain).block();
|
||||
|
||||
verify(chain, times(1)).filter(exchange);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldAllowValidFrontendApiKey() {
|
||||
MockServerHttpRequest request = MockServerHttpRequest.get("/api/contact/test")
|
||||
.header("Origin", "https://localhost:3000")
|
||||
.header("X-Frontend-Key", "valid-frontend-key")
|
||||
.build();
|
||||
MockServerWebExchange exchange = MockServerWebExchange.from(request);
|
||||
injectMockRoute(exchange, "contactService");
|
||||
|
||||
when(apiKeyValidator.isAuthorized(eq("valid-frontend-key"), eq("contactService"), eq(true))).thenReturn(true);
|
||||
when(chain.filter(exchange)).thenReturn(Mono.empty());
|
||||
|
||||
filter.filter(exchange, chain).block();
|
||||
|
||||
verify(apiKeyValidator).isAuthorized("valid-frontend-key", "contactService", true);
|
||||
verify(chain, times(1)).filter(exchange);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldRejectInvalidFrontendApiKey() {
|
||||
MockServerHttpRequest request = MockServerHttpRequest.get("/api/contact/test")
|
||||
.header("Origin", "https://localhost:3000")
|
||||
.header("X-Frontend-Key", "invalid-frontend-key")
|
||||
.build();
|
||||
MockServerWebExchange exchange = MockServerWebExchange.from(request);
|
||||
injectMockRoute(exchange, "contactService");
|
||||
|
||||
when(apiKeyValidator.isAuthorized(eq("invalid-frontend-key"), eq("contactService"), eq(true))).thenReturn(false);
|
||||
|
||||
filter.filter(exchange, chain).block();
|
||||
|
||||
assertEquals(exchange.getResponse().getStatusCode(), org.springframework.http.HttpStatus.UNAUTHORIZED);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldAllowValidInternalApiKey() {
|
||||
MockServerHttpRequest request = MockServerHttpRequest.get("/api/contact/test")
|
||||
.header("X-Internal-Auth", "valid-internal-key")
|
||||
.build();
|
||||
MockServerWebExchange exchange = MockServerWebExchange.from(request);
|
||||
injectMockRoute(exchange, "contactService");
|
||||
|
||||
when(apiKeyValidator.isAuthorized(eq("valid-internal-key"), eq("contactService"), eq(false))).thenReturn(true);
|
||||
when(chain.filter(exchange)).thenReturn(Mono.empty());
|
||||
|
||||
filter.filter(exchange, chain).block();
|
||||
|
||||
verify(apiKeyValidator).isAuthorized("valid-internal-key", "contactService", false);
|
||||
verify(chain, times(1)).filter(exchange);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldRejectInvalidInternalApiKey() {
|
||||
MockServerHttpRequest request = MockServerHttpRequest.get("/api/contact/test")
|
||||
.header("X-Internal-Auth", "invalid-internal-key")
|
||||
.build();
|
||||
MockServerWebExchange exchange = MockServerWebExchange.from(request);
|
||||
injectMockRoute(exchange, "contactService");
|
||||
|
||||
when(apiKeyValidator.isAuthorized(eq("invalid-internal-key"), eq("contactService"), eq(false))).thenReturn(false);
|
||||
|
||||
filter.filter(exchange, chain).block();
|
||||
|
||||
assertEquals(exchange.getResponse().getStatusCode(), org.springframework.http.HttpStatus.UNAUTHORIZED);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package dev.rheinsw.gateway.security;
|
||||
|
||||
import dev.rheinsw.gateway.model.ApiKey;
|
||||
import dev.rheinsw.gateway.model.ApiKeyType;
|
||||
import dev.rheinsw.gateway.repository.ApiKeyRepository;
|
||||
import dev.rheinsw.gateway.service.ApiKeyValidator;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
class ApiKeyValidatorTest {
|
||||
|
||||
private final ApiKeyRepository repository = mock(ApiKeyRepository.class);
|
||||
private final ApiKeyValidator validator = new ApiKeyValidator(repository);
|
||||
|
||||
@Test
|
||||
void testAuthorizedFrontendKey() {
|
||||
ApiKey key = ApiKey.builder()
|
||||
.apiKey("frontend-key")
|
||||
.type(ApiKeyType.FRONTEND)
|
||||
.enabled(true)
|
||||
.frontendOnly(true)
|
||||
.allowedServices(Set.of("contactService"))
|
||||
.build();
|
||||
|
||||
when(repository.findByApiKey("frontend-key")).thenReturn(Optional.of(key));
|
||||
|
||||
boolean result = validator.isAuthorized("frontend-key", "contactService", true);
|
||||
assertTrue(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testUnauthorizedDueToWrongService() {
|
||||
ApiKey key = ApiKey.builder()
|
||||
.apiKey("internal-key")
|
||||
.type(ApiKeyType.INTERNAL)
|
||||
.enabled(true)
|
||||
.frontendOnly(false)
|
||||
.allowedServices(Set.of("mailService"))
|
||||
.build();
|
||||
|
||||
when(repository.findByApiKey("internal-key")).thenReturn(Optional.of(key));
|
||||
|
||||
boolean result = validator.isAuthorized("internal-key", "contactService", false);
|
||||
assertFalse(result);
|
||||
}
|
||||
}
|
||||
1
backend/gateway/src/test/resources/application.yml
Normal file
1
backend/gateway/src/test/resources/application.yml
Normal file
@@ -0,0 +1 @@
|
||||
spring.cloud.gateway.mvc-discovery.enabled=false
|
||||
Reference in New Issue
Block a user