Реализованы задачи бэклога 1-5 (1я не до конца)

This commit is contained in:
mi
2026-07-21 12:50:46 +03:00
parent 3b71caf7b3
commit 0d7f7a819f
105 changed files with 7185 additions and 38 deletions
-1
View File
@@ -8,7 +8,6 @@ KEYCLOAK_OTP_MOCK_ENABLED=true
KEYCLOAK_OTP_MOCK_CODE=replace-with-random-6-plus-character-secret
KEYCLOAK_OTP_HMAC_KEY=replace-with-at-least-32-random-bytes
KEYCLOAK_OTP_TTL_SEC=300
KEYCLOAK_OTP_MAX_VERIFY_ATTEMPTS=5
KEYCLOAK_OTP_SETTINGS_MAX_STALE_SEC=300
KEYCLOAK_SETTINGS_BRIDGE_URL=http://api-backend:8000/internal/settings/v1/otp
KEYCLOAK_SETTINGS_BRIDGE_TOKEN=replace-with-service-token
@@ -24,7 +24,6 @@ services:
KEYCLOAK_OTP_MOCK_CODE: ${KEYCLOAK_OTP_MOCK_CODE:?mock code is required}
KEYCLOAK_OTP_HMAC_KEY: ${KEYCLOAK_OTP_HMAC_KEY:?OTP HMAC key is required}
KEYCLOAK_OTP_TTL_SEC: ${KEYCLOAK_OTP_TTL_SEC:-300}
KEYCLOAK_OTP_MAX_VERIFY_ATTEMPTS: ${KEYCLOAK_OTP_MAX_VERIFY_ATTEMPTS:-5}
KEYCLOAK_OTP_SETTINGS_MAX_STALE_SEC: ${KEYCLOAK_OTP_SETTINGS_MAX_STALE_SEC:-300}
KEYCLOAK_SETTINGS_BRIDGE_URL: ${KEYCLOAK_SETTINGS_BRIDGE_URL:-http://api-backend:8000/internal/settings/v1/otp}
KEYCLOAK_SETTINGS_BRIDGE_TOKEN: ${KEYCLOAK_SETTINGS_BRIDGE_TOKEN:?settings bridge token is required}
@@ -8,7 +8,6 @@ final class Config {
static final String MOCK_CODE = required("KEYCLOAK_OTP_MOCK_CODE");
static final byte[] HMAC_KEY = required("KEYCLOAK_OTP_HMAC_KEY").getBytes(java.nio.charset.StandardCharsets.UTF_8);
static final Duration OTP_TTL = Duration.ofSeconds(integer("KEYCLOAK_OTP_TTL_SEC", 300, 30, 900));
static final int MAX_VERIFY_ATTEMPTS = integer("KEYCLOAK_OTP_MAX_VERIFY_ATTEMPTS", 5, 1, 10);
static final Duration SETTINGS_MAX_STALE = Duration.ofSeconds(
integer("KEYCLOAK_OTP_SETTINGS_MAX_STALE_SEC", 300, 30, 3600));
static final URI SETTINGS_URL = URI.create(env("KEYCLOAK_SETTINGS_BRIDGE_URL",
@@ -58,7 +58,7 @@ final class OtpStore {
challenge.createdAt = now;
challenge.expiresAt = now.plus(Config.OTP_TTL);
challenge.verifyAttempts = 0;
challenge.maxVerifyAttempts = Config.MAX_VERIFY_ATTEMPTS;
challenge.maxVerifyAttempts = limits.maxVerifyAttempts();
challenge.settingsVersion = limits.version();
challenge.providerId = "mock-" + Crypto.randomId();
challenge.providerStatus = "accepted";
@@ -17,7 +17,7 @@ final class SettingsBridge {
.connectTimeout(Duration.ofSeconds(2)).build();
private static volatile Cached cached;
record Limits(int maxSendsPer24h, int minSecondsBetween, String version) {}
record Limits(int maxSendsPer24h, int minSecondsBetween, int maxVerifyAttempts, String version) {}
private record Cached(Limits limits, Instant fetchedAt, Instant refreshAfter, String etag) {}
private SettingsBridge() {}
@@ -44,12 +44,14 @@ final class SettingsBridge {
if (response.statusCode() != 200) throw new IllegalStateException("settings_http_" + response.statusCode());
int max = integer(response.body(), "max_send_attempts_per_24h");
int minimum = integer(response.body(), "min_seconds_between_attempts");
int maxVerify = integer(response.body(), "max_verify_attempts");
int ttl = integer(response.body(), "cache_ttl_seconds");
String version = string(response.body(), "version");
if (max < 1 || max > 100 || minimum < 0 || minimum > 86400 || ttl < 1 || ttl > 3600) {
if (max < 1 || max > 100 || minimum < 0 || minimum > 86400
|| maxVerify < 1 || maxVerify > 10 || ttl < 1 || ttl > 3600) {
throw new IllegalStateException("settings_invalid_range");
}
Limits limits = new Limits(max, minimum, version);
Limits limits = new Limits(max, minimum, maxVerify, version);
cached = new Cached(limits, now, now.plusSeconds(ttl),
response.headers().firstValue("ETag").orElse(null));
return limits;