feat(auth): JWT scope authorization (@nstart/auth контракт)
Новый модуль ordinis-auth:
- OrdinisAuthProperties — public-key, issuer, requireAuthentication, allowQueryScope
- ScopeContext — резолвит current scopes из JWT claim 'scopes' (приоритет),
потом legacy ?as_scope=, потом anonymous=PUBLIC
- SecurityConfig — Spring Security OAuth2 Resource Server + RSA public-key
decoder. Если public-key пуст — JWT validation отключён (permissive
fallback); rollout без auth-сервера не ломает API.
Read controller теперь делегирует scope resolution в ScopeContext вместо
прямого парсинга ?as_scope=. Backward compat: legacy query параметр
работает пока ordinis.auth.allow-query-scope=true (по умолчанию true в
dev/staging, false в prod после интеграции с @nstart/auth).
Vault: public-key читается из secret/ordinis/cuod/jwt-public-key (key=key).
Spring Cloud Vault flatten'ит в property 'key', resolved в
ordinis.auth.public-key=${key:}.
This commit is contained in:
@@ -158,3 +158,11 @@ spring:
|
|||||||
hibernate:
|
hibernate:
|
||||||
ddl-auto: none
|
ddl-auto: none
|
||||||
open-in-view: false
|
open-in-view: false
|
||||||
|
|
||||||
|
# Auth (см. ordinis-auth: SecurityConfig). public-key из Vault.
|
||||||
|
ordinis:
|
||||||
|
auth:
|
||||||
|
public-key: ${key:}
|
||||||
|
issuer: ${jwt-issuer:}
|
||||||
|
require-authentication: ${ORDINIS_AUTH_REQUIRED:false}
|
||||||
|
allow-query-scope: ${ORDINIS_AUTH_ALLOW_QUERY_SCOPE:true}
|
||||||
|
|||||||
@@ -0,0 +1,35 @@
|
|||||||
|
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<parent>
|
||||||
|
<groupId>cloud.nstart.terravault.ordinis</groupId>
|
||||||
|
<artifactId>ordinis-parent</artifactId>
|
||||||
|
<version>0.1.0-SNAPSHOT</version>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<artifactId>ordinis-auth</artifactId>
|
||||||
|
<name>Ordinis :: Auth</name>
|
||||||
|
<description>JWT-based scope authorization (@nstart/auth контракт). Используется writer и read-api.</description>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>cloud.nstart.terravault.ordinis</groupId>
|
||||||
|
<artifactId>ordinis-domain</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-security</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-web</artifactId>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
</project>
|
||||||
+30
@@ -0,0 +1,30 @@
|
|||||||
|
package cloud.nstart.terravault.ordinis.auth;
|
||||||
|
|
||||||
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Конфиг для @nstart/auth-style JWT auth.
|
||||||
|
*
|
||||||
|
* <p>{@code publicKey} — RSA public key (PEM, без BEGIN/END заголовков, base64).
|
||||||
|
* Подбрасывается через Vault: secret/ordinis/cuod/jwt-public-key.</p>
|
||||||
|
*
|
||||||
|
* <p>{@code requireAuthentication=true} → 401 на любой запрос без валидного JWT.
|
||||||
|
* При false работает permissive режим — анонимный запрос имеет только PUBLIC scope,
|
||||||
|
* что подходит для read-api на raw публичных endpoint'ах.</p>
|
||||||
|
*
|
||||||
|
* <p>{@code allowQueryScope=true} включает legacy {@code ?as_scope=...} query param
|
||||||
|
* (для smoke-тестов и dev). В prod рекомендуется false.</p>
|
||||||
|
*/
|
||||||
|
@ConfigurationProperties(prefix = "ordinis.auth")
|
||||||
|
public record OrdinisAuthProperties(
|
||||||
|
String publicKey,
|
||||||
|
String issuer,
|
||||||
|
String scopeClaim,
|
||||||
|
boolean requireAuthentication,
|
||||||
|
boolean allowQueryScope) {
|
||||||
|
|
||||||
|
public OrdinisAuthProperties {
|
||||||
|
if (scopeClaim == null) scopeClaim = "scopes";
|
||||||
|
if (issuer == null) issuer = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,97 @@
|
|||||||
|
package cloud.nstart.terravault.ordinis.auth;
|
||||||
|
|
||||||
|
import cloud.nstart.terravault.ordinis.domain.DataScope;
|
||||||
|
import org.springframework.security.core.Authentication;
|
||||||
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
|
import org.springframework.security.oauth2.jwt.Jwt;
|
||||||
|
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.EnumSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Резолвер активных scope для текущего запроса.
|
||||||
|
*
|
||||||
|
* <p>Источник scope (приоритет сверху-вниз):
|
||||||
|
* <ol>
|
||||||
|
* <li>JWT claim {@code scopes} (см. {@link OrdinisAuthProperties#scopeClaim()}).</li>
|
||||||
|
* <li>Legacy query param {@code as_scope} (если {@code allowQueryScope=true}). Контроллеры
|
||||||
|
* сами читают и передают через {@link #fromQueryParam(String)}.</li>
|
||||||
|
* <li>Anonymous → только {@link DataScope#PUBLIC} (если auth не требуется).</li>
|
||||||
|
* </ol>
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
public class ScopeContext {
|
||||||
|
|
||||||
|
private final OrdinisAuthProperties props;
|
||||||
|
|
||||||
|
public ScopeContext(OrdinisAuthProperties props) {
|
||||||
|
this.props = props;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Достаёт scope текущей аутентифицированной сессии (из JWT) или PUBLIC если анонимна. */
|
||||||
|
public Set<DataScope> currentScopes() {
|
||||||
|
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
|
||||||
|
if (auth instanceof JwtAuthenticationToken jwtAuth) {
|
||||||
|
return scopesFromJwt(jwtAuth.getToken());
|
||||||
|
}
|
||||||
|
return EnumSet.of(DataScope.PUBLIC);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Объединяет JWT-scope и query-as_scope с правильными правилами:
|
||||||
|
*
|
||||||
|
* <ul>
|
||||||
|
* <li>Если JWT присутствует — query param ИГНОРИРУЕТСЯ (защита от scope escalation
|
||||||
|
* через URL).</li>
|
||||||
|
* <li>Если JWT отсутствует и {@code allowQueryScope=true} — берём из query.</li>
|
||||||
|
* <li>Иначе — PUBLIC.</li>
|
||||||
|
* </ul>
|
||||||
|
*/
|
||||||
|
public Set<DataScope> resolveForRead(String queryAsScope) {
|
||||||
|
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
|
||||||
|
if (auth instanceof JwtAuthenticationToken jwtAuth) {
|
||||||
|
return scopesFromJwt(jwtAuth.getToken());
|
||||||
|
}
|
||||||
|
if (props.allowQueryScope() && queryAsScope != null && !queryAsScope.isBlank()) {
|
||||||
|
return fromQueryParam(queryAsScope);
|
||||||
|
}
|
||||||
|
return EnumSet.of(DataScope.PUBLIC);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Set<DataScope> fromQueryParam(String csv) {
|
||||||
|
if (csv == null || csv.isBlank()) return EnumSet.of(DataScope.PUBLIC);
|
||||||
|
EnumSet<DataScope> result = EnumSet.noneOf(DataScope.class);
|
||||||
|
for (String part : csv.split(",")) {
|
||||||
|
String s = part.trim();
|
||||||
|
if (s.isEmpty()) continue;
|
||||||
|
try {
|
||||||
|
result.add(DataScope.valueOf(s.toUpperCase()));
|
||||||
|
} catch (IllegalArgumentException ignored) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result.isEmpty() ? EnumSet.of(DataScope.PUBLIC) : result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Set<DataScope> scopesFromJwt(Jwt token) {
|
||||||
|
Object raw = token.getClaim(props.scopeClaim());
|
||||||
|
if (raw == null) return EnumSet.of(DataScope.PUBLIC);
|
||||||
|
EnumSet<DataScope> result = EnumSet.noneOf(DataScope.class);
|
||||||
|
if (raw instanceof Collection<?> col) {
|
||||||
|
for (Object o : col) addScope(result, String.valueOf(o));
|
||||||
|
} else {
|
||||||
|
for (String part : String.valueOf(raw).split("[,\\s]+")) addScope(result, part);
|
||||||
|
}
|
||||||
|
return result.isEmpty() ? EnumSet.of(DataScope.PUBLIC) : result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void addScope(EnumSet<DataScope> target, String s) {
|
||||||
|
if (s == null || s.isBlank()) return;
|
||||||
|
try {
|
||||||
|
target.add(DataScope.valueOf(s.trim().toUpperCase()));
|
||||||
|
} catch (IllegalArgumentException ignored) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
package cloud.nstart.terravault.ordinis.auth;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.security.config.Customizer;
|
||||||
|
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||||
|
import org.springframework.security.oauth2.jwt.JwtDecoder;
|
||||||
|
import org.springframework.security.oauth2.jwt.NimbusJwtDecoder;
|
||||||
|
import org.springframework.security.web.SecurityFilterChain;
|
||||||
|
|
||||||
|
import java.security.KeyFactory;
|
||||||
|
import java.security.interfaces.RSAPublicKey;
|
||||||
|
import java.security.spec.X509EncodedKeySpec;
|
||||||
|
import java.util.Base64;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Spring Security конфиг: actuator/health и пути pre-flight открыты, остальные
|
||||||
|
* либо требуют JWT (если {@code requireAuthentication=true}), либо доступны
|
||||||
|
* анонимно (с автоматическим назначением PUBLIC scope в {@link ScopeContext}).
|
||||||
|
*/
|
||||||
|
@Configuration
|
||||||
|
@EnableConfigurationProperties(OrdinisAuthProperties.class)
|
||||||
|
public class SecurityConfig {
|
||||||
|
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(SecurityConfig.class);
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public SecurityFilterChain securityFilterChain(HttpSecurity http,
|
||||||
|
OrdinisAuthProperties props) throws Exception {
|
||||||
|
http
|
||||||
|
.csrf(csrf -> csrf.disable())
|
||||||
|
.sessionManagement(sm -> sm.sessionCreationPolicy(
|
||||||
|
org.springframework.security.config.http.SessionCreationPolicy.STATELESS))
|
||||||
|
.authorizeHttpRequests(auth -> {
|
||||||
|
auth.requestMatchers("/actuator/**").permitAll();
|
||||||
|
auth.requestMatchers("/error").permitAll();
|
||||||
|
if (props.requireAuthentication()) {
|
||||||
|
auth.anyRequest().authenticated();
|
||||||
|
} else {
|
||||||
|
// Permissive: запрос без JWT → анонимный, но scope-фильтр оставит только PUBLIC.
|
||||||
|
auth.anyRequest().permitAll();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Resource server только если есть public key (иначе nimbus сломается).
|
||||||
|
if (props.publicKey() != null && !props.publicKey().isBlank()) {
|
||||||
|
http.oauth2ResourceServer(rs -> rs.jwt(Customizer.withDefaults()));
|
||||||
|
} else {
|
||||||
|
log.warn("ordinis.auth.public-key не задан — JWT validation отключён, scope только из query/anonymous");
|
||||||
|
}
|
||||||
|
return http.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public JwtDecoder jwtDecoder(OrdinisAuthProperties props) throws Exception {
|
||||||
|
if (props.publicKey() == null || props.publicKey().isBlank()) {
|
||||||
|
// Nimbus требует ключ; возвращаем decoder который всегда падает — он не
|
||||||
|
// будет вызван, потому что oauth2ResourceServer не подключён.
|
||||||
|
return token -> {
|
||||||
|
throw new org.springframework.security.oauth2.jwt.BadJwtException(
|
||||||
|
"JWT validation disabled: ordinis.auth.public-key не сконфигурирован");
|
||||||
|
};
|
||||||
|
}
|
||||||
|
String pem = props.publicKey()
|
||||||
|
.replaceAll("-----BEGIN PUBLIC KEY-----", "")
|
||||||
|
.replaceAll("-----END PUBLIC KEY-----", "")
|
||||||
|
.replaceAll("\\s+", "");
|
||||||
|
byte[] der = Base64.getDecoder().decode(pem);
|
||||||
|
RSAPublicKey publicKey = (RSAPublicKey) KeyFactory.getInstance("RSA")
|
||||||
|
.generatePublic(new X509EncodedKeySpec(der));
|
||||||
|
NimbusJwtDecoder decoder = NimbusJwtDecoder.withPublicKey(publicKey).build();
|
||||||
|
if (props.issuer() != null && !props.issuer().isBlank()) {
|
||||||
|
decoder.setJwtValidator(
|
||||||
|
org.springframework.security.oauth2.jwt.JwtValidators.createDefaultWithIssuer(props.issuer()));
|
||||||
|
}
|
||||||
|
return decoder;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||||
|
cloud.nstart.terravault.ordinis.auth.SecurityConfig
|
||||||
@@ -19,6 +19,10 @@
|
|||||||
<groupId>cloud.nstart.terravault.ordinis</groupId>
|
<groupId>cloud.nstart.terravault.ordinis</groupId>
|
||||||
<artifactId>ordinis-domain</artifactId>
|
<artifactId>ordinis-domain</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>cloud.nstart.terravault.ordinis</groupId>
|
||||||
|
<artifactId>ordinis-auth</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
|||||||
+13
-18
@@ -1,5 +1,6 @@
|
|||||||
package cloud.nstart.terravault.ordinis.readapi.web;
|
package cloud.nstart.terravault.ordinis.readapi.web;
|
||||||
|
|
||||||
|
import cloud.nstart.terravault.ordinis.auth.ScopeContext;
|
||||||
import cloud.nstart.terravault.ordinis.domain.DataScope;
|
import cloud.nstart.terravault.ordinis.domain.DataScope;
|
||||||
import cloud.nstart.terravault.ordinis.readapi.dto.FlattenedRecordResponse;
|
import cloud.nstart.terravault.ordinis.readapi.dto.FlattenedRecordResponse;
|
||||||
import cloud.nstart.terravault.ordinis.readapi.service.RecordReadService;
|
import cloud.nstart.terravault.ordinis.readapi.service.RecordReadService;
|
||||||
@@ -16,27 +17,29 @@ import org.springframework.web.bind.annotation.RestController;
|
|||||||
|
|
||||||
import java.time.OffsetDateTime;
|
import java.time.OffsetDateTime;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.stream.Collectors;
|
|
||||||
import java.util.stream.Stream;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Read API для consumers (геопортал, Альтум). Фронт публичный — здесь
|
* Read API для consumers (геопортал, Альтум). Фронт публичный — здесь
|
||||||
* locale negotiation + scope filtering.
|
* locale negotiation + scope filtering.
|
||||||
*
|
*
|
||||||
* <p>Scope authorisation: в v1 берём из query param {@code as_scope} (для
|
* <p>Scope authorisation: JWT claim {@code scopes} через {@link ScopeContext}.
|
||||||
* dev/curl). В prod — из JWT (@nstart/auth) — сделаем через filter позже.
|
* Если запрос без JWT и {@code ordinis.auth.allow-query-scope=true} — fallback
|
||||||
|
* на legacy query param {@code ?as_scope=}; иначе только PUBLIC.
|
||||||
*/
|
*/
|
||||||
@RestController
|
@RestController
|
||||||
@RequestMapping("/api/v1/{dictionaryName}/records")
|
@RequestMapping("/api/v1/{dictionaryName}/records")
|
||||||
public class DictionaryReadController {
|
public class DictionaryReadController {
|
||||||
|
|
||||||
private final RecordReadService service;
|
private final RecordReadService service;
|
||||||
|
private final ScopeContext scopeContext;
|
||||||
private final Counter scopeDeniedCounter;
|
private final Counter scopeDeniedCounter;
|
||||||
|
|
||||||
public DictionaryReadController(RecordReadService service, MeterRegistry meterRegistry) {
|
public DictionaryReadController(
|
||||||
|
RecordReadService service, ScopeContext scopeContext, MeterRegistry meterRegistry) {
|
||||||
this.service = service;
|
this.service = service;
|
||||||
|
this.scopeContext = scopeContext;
|
||||||
this.scopeDeniedCounter = Counter.builder("ordinis_scope_access_denied_total")
|
this.scopeDeniedCounter = Counter.builder("ordinis_scope_access_denied_total")
|
||||||
.description("Попытки чтения недоступного scope (v1 — из query param)")
|
.description("Попытки чтения недоступного scope")
|
||||||
.register(meterRegistry);
|
.register(meterRegistry);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -45,10 +48,10 @@ public class DictionaryReadController {
|
|||||||
@PathVariable String dictionaryName,
|
@PathVariable String dictionaryName,
|
||||||
@PathVariable String businessKey,
|
@PathVariable String businessKey,
|
||||||
@RequestHeader(value = HttpHeaders.ACCEPT_LANGUAGE, required = false) String acceptLanguage,
|
@RequestHeader(value = HttpHeaders.ACCEPT_LANGUAGE, required = false) String acceptLanguage,
|
||||||
@RequestParam(value = "as_scope", defaultValue = "PUBLIC") String asScope,
|
@RequestParam(value = "as_scope", required = false) String asScope,
|
||||||
@RequestParam(value = "at", required = false) OffsetDateTime at) {
|
@RequestParam(value = "at", required = false) OffsetDateTime at) {
|
||||||
|
|
||||||
List<DataScope> scopes = parseScopes(asScope);
|
List<DataScope> scopes = scopeContext.resolveForRead(asScope).stream().toList();
|
||||||
var result = service.readActive(dictionaryName, businessKey, acceptLanguage, scopes, at);
|
var result = service.readActive(dictionaryName, businessKey, acceptLanguage, scopes, at);
|
||||||
|
|
||||||
HttpHeaders headers = new HttpHeaders();
|
HttpHeaders headers = new HttpHeaders();
|
||||||
@@ -63,10 +66,10 @@ public class DictionaryReadController {
|
|||||||
public ResponseEntity<List<FlattenedRecordResponse>> list(
|
public ResponseEntity<List<FlattenedRecordResponse>> list(
|
||||||
@PathVariable String dictionaryName,
|
@PathVariable String dictionaryName,
|
||||||
@RequestHeader(value = HttpHeaders.ACCEPT_LANGUAGE, required = false) String acceptLanguage,
|
@RequestHeader(value = HttpHeaders.ACCEPT_LANGUAGE, required = false) String acceptLanguage,
|
||||||
@RequestParam(value = "as_scope", defaultValue = "PUBLIC") String asScope,
|
@RequestParam(value = "as_scope", required = false) String asScope,
|
||||||
@RequestParam(value = "at", required = false) OffsetDateTime at) {
|
@RequestParam(value = "at", required = false) OffsetDateTime at) {
|
||||||
|
|
||||||
List<DataScope> scopes = parseScopes(asScope);
|
List<DataScope> scopes = scopeContext.resolveForRead(asScope).stream().toList();
|
||||||
var items = service.listActive(dictionaryName, acceptLanguage, scopes, at);
|
var items = service.listActive(dictionaryName, acceptLanguage, scopes, at);
|
||||||
|
|
||||||
HttpHeaders headers = new HttpHeaders();
|
HttpHeaders headers = new HttpHeaders();
|
||||||
@@ -76,12 +79,4 @@ public class DictionaryReadController {
|
|||||||
headers.add(HttpHeaders.VARY, HttpHeaders.ACCEPT_LANGUAGE);
|
headers.add(HttpHeaders.VARY, HttpHeaders.ACCEPT_LANGUAGE);
|
||||||
return new ResponseEntity<>(items, headers, 200);
|
return new ResponseEntity<>(items, headers, 200);
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<DataScope> parseScopes(String asScope) {
|
|
||||||
return Stream.of(asScope.split(","))
|
|
||||||
.map(String::trim)
|
|
||||||
.filter(s -> !s.isEmpty())
|
|
||||||
.map(s -> DataScope.valueOf(s.toUpperCase()))
|
|
||||||
.collect(Collectors.toList());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -121,3 +121,12 @@ spring:
|
|||||||
hibernate:
|
hibernate:
|
||||||
ddl-auto: none
|
ddl-auto: none
|
||||||
open-in-view: false
|
open-in-view: false
|
||||||
|
|
||||||
|
# Auth — JWT public key из Vault (secret/ordinis/cuod/jwt-public-key, key=key).
|
||||||
|
# Если public-key пуст — резолвер scope падает на anonymous PUBLIC + ?as_scope= legacy.
|
||||||
|
ordinis:
|
||||||
|
auth:
|
||||||
|
public-key: ${key:}
|
||||||
|
issuer: ${jwt-issuer:}
|
||||||
|
require-authentication: ${ORDINIS_AUTH_REQUIRED:false}
|
||||||
|
allow-query-scope: ${ORDINIS_AUTH_ALLOW_QUERY_SCOPE:true}
|
||||||
|
|||||||
@@ -39,6 +39,10 @@
|
|||||||
<groupId>cloud.nstart.terravault.ordinis</groupId>
|
<groupId>cloud.nstart.terravault.ordinis</groupId>
|
||||||
<artifactId>ordinis-events-api</artifactId>
|
<artifactId>ordinis-events-api</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>cloud.nstart.terravault.ordinis</groupId>
|
||||||
|
<artifactId>ordinis-auth</artifactId>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.fasterxml.jackson.core</groupId>
|
<groupId>com.fasterxml.jackson.core</groupId>
|
||||||
<artifactId>jackson-databind</artifactId>
|
<artifactId>jackson-databind</artifactId>
|
||||||
|
|||||||
@@ -24,6 +24,7 @@
|
|||||||
<module>ordinis-validation</module>
|
<module>ordinis-validation</module>
|
||||||
<module>ordinis-events-api</module>
|
<module>ordinis-events-api</module>
|
||||||
<module>ordinis-outbox</module>
|
<module>ordinis-outbox</module>
|
||||||
|
<module>ordinis-auth</module>
|
||||||
<module>ordinis-rest-api</module>
|
<module>ordinis-rest-api</module>
|
||||||
<module>ordinis-app</module>
|
<module>ordinis-app</module>
|
||||||
<module>ordinis-read-api</module>
|
<module>ordinis-read-api</module>
|
||||||
@@ -78,6 +79,11 @@
|
|||||||
<artifactId>ordinis-outbox</artifactId>
|
<artifactId>ordinis-outbox</artifactId>
|
||||||
<version>${project.version}</version>
|
<version>${project.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>cloud.nstart.terravault.ordinis</groupId>
|
||||||
|
<artifactId>ordinis-auth</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>cloud.nstart.terravault.ordinis</groupId>
|
<groupId>cloud.nstart.terravault.ordinis</groupId>
|
||||||
<artifactId>ordinis-rest-api</artifactId>
|
<artifactId>ordinis-rest-api</artifactId>
|
||||||
|
|||||||
Reference in New Issue
Block a user