init:wq
This commit is contained in:
11
src/main/java/ru/copperside/AuthServer.java
Normal file
11
src/main/java/ru/copperside/AuthServer.java
Normal file
@@ -0,0 +1,11 @@
|
||||
package ru.copperside;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
@SpringBootApplication
|
||||
public class AuthServer {
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(AuthServer.class, args);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
package ru.copperside.controller;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import ru.copperside.model.authinfo.AuthInfo;
|
||||
import ru.copperside.service.AuthInfoService;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/authinfo")
|
||||
@RequiredArgsConstructor
|
||||
public class AuthInfoController {
|
||||
private final AuthInfoService service;
|
||||
|
||||
// Пример: GET /api/authinfo/user123?type=Secret
|
||||
@GetMapping("/{dataId}")
|
||||
public ResponseEntity<AuthInfo> getByDataIdAndType(
|
||||
@PathVariable String dataId
|
||||
) {
|
||||
return ResponseEntity.ok(service.getByDataIdAndType(dataId));
|
||||
}
|
||||
}
|
||||
*/
|
||||
@@ -0,0 +1,58 @@
|
||||
package ru.copperside.controller;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import ru.copperside.model.authinfo.SecretData;
|
||||
import ru.copperside.model.dto.RolePermissionDto;
|
||||
import ru.copperside.model.enums.AuthenticationType;
|
||||
import ru.copperside.model.permission.Permission;
|
||||
import ru.copperside.service.DebugAuthService;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/debug/auth")
|
||||
@RequiredArgsConstructor
|
||||
public class DebugAuthController {
|
||||
|
||||
private final DebugAuthService service;
|
||||
|
||||
/**
|
||||
* GET /debug/auth/secret/{dataId}?type=Secret
|
||||
* Если type не указан, используется AuthenticationType.Secret.
|
||||
*/
|
||||
@GetMapping("/secret/{dataId}")
|
||||
public ResponseEntity<SecretData> getSecret(
|
||||
@PathVariable String dataId,
|
||||
@RequestParam(required = false) String type
|
||||
) {
|
||||
String resolvedType = (type == null || type.isBlank())
|
||||
? AuthenticationType.Secret.name()
|
||||
: type;
|
||||
|
||||
SecretData secretData = service.getSecretData(dataId, resolvedType);
|
||||
return ResponseEntity.ok(secretData);
|
||||
}
|
||||
|
||||
@GetMapping("/permission/{hierarchyId}")
|
||||
public ResponseEntity<List<RolePermissionDto>> getRolePermission(
|
||||
@PathVariable Long hierarchyId
|
||||
) {
|
||||
return ResponseEntity.ok(service.byHierarchy(hierarchyId));
|
||||
}
|
||||
|
||||
@GetMapping("/personal_permission/{hierarchyId}")
|
||||
public ResponseEntity<List<RolePermissionDto>> getPersonalPermission(
|
||||
@PathVariable Long hierarchyId
|
||||
) {
|
||||
return ResponseEntity.ok(service.PersonalbyHierarchy(hierarchyId));
|
||||
}
|
||||
|
||||
@GetMapping("/permissions/{hierarchyId}")
|
||||
public ResponseEntity<List<Permission>> getPermissions(
|
||||
@PathVariable Long hierarchyId
|
||||
) {
|
||||
return ResponseEntity.ok(service.getPermissions(hierarchyId));
|
||||
}
|
||||
}
|
||||
25
src/main/java/ru/copperside/model/authinfo/AuthInfo.java
Normal file
25
src/main/java/ru/copperside/model/authinfo/AuthInfo.java
Normal file
@@ -0,0 +1,25 @@
|
||||
package ru.copperside.model.authinfo;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import lombok.Builder;
|
||||
import lombok.extern.jackson.Jacksonized;
|
||||
import ru.copperside.model.permission.Permission;
|
||||
import ru.copperside.model.session.SessionSettings;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Jacksonized
|
||||
@Builder
|
||||
public record AuthInfo(
|
||||
Long authId,
|
||||
String dataId,
|
||||
Long hierarchyId,
|
||||
String displayName,
|
||||
Boolean isEnabled,
|
||||
Boolean needActivation,
|
||||
List<Permission> permissions,
|
||||
SessionSettings sessionSettings,
|
||||
JsonNode sessionData,
|
||||
JsonNode privateData,
|
||||
SecretData secretData
|
||||
) { }
|
||||
19
src/main/java/ru/copperside/model/authinfo/SecretData.java
Normal file
19
src/main/java/ru/copperside/model/authinfo/SecretData.java
Normal file
@@ -0,0 +1,19 @@
|
||||
package ru.copperside.model.authinfo;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.extern.jackson.Jacksonized;
|
||||
import ru.copperside.model.enums.AuthenticationType;
|
||||
import ru.copperside.model.enums.KeyUsage;
|
||||
import ru.copperside.model.enums.SecretType;
|
||||
|
||||
import java.util.EnumSet;
|
||||
|
||||
@Jacksonized
|
||||
@Builder
|
||||
public record SecretData(
|
||||
AuthenticationType authType,
|
||||
Boolean isEnabled,
|
||||
EnumSet<KeyUsage> keyUsages,
|
||||
SecretType secretType,
|
||||
String secret
|
||||
) {}
|
||||
8
src/main/java/ru/copperside/model/dto/ParametersDTO.java
Normal file
8
src/main/java/ru/copperside/model/dto/ParametersDTO.java
Normal file
@@ -0,0 +1,8 @@
|
||||
package ru.copperside.model.dto;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonAlias;
|
||||
|
||||
public record ParametersDTO(
|
||||
@JsonAlias({"IsEnabled","isEnabled"}) Boolean isEnabled,
|
||||
@JsonAlias({"NeedActivation","needActivation"}) Boolean needActivation
|
||||
) {}
|
||||
14
src/main/java/ru/copperside/model/dto/RolePermissionDto.java
Normal file
14
src/main/java/ru/copperside/model/dto/RolePermissionDto.java
Normal file
@@ -0,0 +1,14 @@
|
||||
package ru.copperside.model.dto;
|
||||
|
||||
public record RolePermissionDto(
|
||||
Long hierarchyId,
|
||||
Integer level,
|
||||
Long roleId,
|
||||
Long permissionId,
|
||||
String permissionStrId,
|
||||
String settingsJson,
|
||||
Boolean action,
|
||||
String pDataJson,
|
||||
String command,
|
||||
String http
|
||||
) { }
|
||||
10
src/main/java/ru/copperside/model/dto/SecretDTO.java
Normal file
10
src/main/java/ru/copperside/model/dto/SecretDTO.java
Normal file
@@ -0,0 +1,10 @@
|
||||
package ru.copperside.model.dto;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonAlias;
|
||||
|
||||
public record SecretDTO(
|
||||
@JsonAlias({"IsEnable","isEnable"}) Boolean isEnable,
|
||||
@JsonAlias({"KeyUsage","keyUsage"}) Object keyUsage,
|
||||
@JsonAlias({"SecretType","secretType"}) String secretType,
|
||||
@JsonAlias({"Secret","secret"}) String secret
|
||||
) {}
|
||||
21
src/main/java/ru/copperside/model/enums/AuthType.java
Normal file
21
src/main/java/ru/copperside/model/enums/AuthType.java
Normal file
@@ -0,0 +1,21 @@
|
||||
package ru.copperside.model.enums;
|
||||
|
||||
public enum AuthType {
|
||||
Unknown(0),
|
||||
Simple(1),
|
||||
TwoStep(2);
|
||||
|
||||
private final int code;
|
||||
|
||||
AuthType(int code) { this.code = code; }
|
||||
public int code() { return code; }
|
||||
|
||||
public static AuthType fromCode(int code) {
|
||||
return switch (code) {
|
||||
case 0 -> Unknown;
|
||||
case 1 -> Simple;
|
||||
case 2 -> TwoStep;
|
||||
default -> throw new IllegalArgumentException("Unknown AuthType code: " + code);
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package ru.copperside.model.enums;
|
||||
|
||||
public enum AuthenticationType {
|
||||
Secret,
|
||||
Windows;
|
||||
|
||||
/** Возвращает enum по имени, default = Secret. */
|
||||
public static AuthenticationType from(String name) {
|
||||
if (name == null) return Secret;
|
||||
try {
|
||||
return AuthenticationType.valueOf(name);
|
||||
} catch (IllegalArgumentException ex) {
|
||||
return Secret;
|
||||
}
|
||||
}
|
||||
}
|
||||
21
src/main/java/ru/copperside/model/enums/KeyUsage.java
Normal file
21
src/main/java/ru/copperside/model/enums/KeyUsage.java
Normal file
@@ -0,0 +1,21 @@
|
||||
package ru.copperside.model.enums;
|
||||
|
||||
public enum KeyUsage {
|
||||
None(0),
|
||||
Password(1),
|
||||
HMac(2);
|
||||
|
||||
private final int code;
|
||||
|
||||
KeyUsage(int code) { this.code = code; }
|
||||
public int code() { return code; }
|
||||
|
||||
public static KeyUsage fromCode(int code) {
|
||||
return switch (code) {
|
||||
case 0 -> None;
|
||||
case 1 -> Password;
|
||||
case 2 -> HMac;
|
||||
default -> throw new IllegalArgumentException("Unknown KeyUsage code: " + code);
|
||||
};
|
||||
}
|
||||
}
|
||||
6
src/main/java/ru/copperside/model/enums/SecretType.java
Normal file
6
src/main/java/ru/copperside/model/enums/SecretType.java
Normal file
@@ -0,0 +1,6 @@
|
||||
package ru.copperside.model.enums;
|
||||
|
||||
public enum SecretType {
|
||||
EncodedData,
|
||||
PlainTextData
|
||||
}
|
||||
18
src/main/java/ru/copperside/model/permission/Permission.java
Normal file
18
src/main/java/ru/copperside/model/permission/Permission.java
Normal file
@@ -0,0 +1,18 @@
|
||||
package ru.copperside.model.permission;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import lombok.Builder;
|
||||
import lombok.extern.jackson.Jacksonized;
|
||||
|
||||
@Jacksonized
|
||||
@Builder(toBuilder = true)
|
||||
public record Permission(
|
||||
Long permissionId,
|
||||
String permissionStrId,
|
||||
PermissionSettings settings,
|
||||
Boolean action,
|
||||
JsonNode permissionData,
|
||||
String command,
|
||||
String http
|
||||
) {}
|
||||
@@ -0,0 +1,21 @@
|
||||
package ru.copperside.model.permission;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import lombok.Builder;
|
||||
import lombok.extern.jackson.Jacksonized;
|
||||
|
||||
@Jacksonized
|
||||
@Builder(toBuilder = true)
|
||||
public record PermissionSettings(
|
||||
Boolean needLog,
|
||||
Boolean requestLog,
|
||||
Boolean needConfirmationCode
|
||||
) {
|
||||
public PermissionSettings normalize() {
|
||||
return this.toBuilder()
|
||||
.needLog(Boolean.TRUE.equals(needLog))
|
||||
.requestLog(Boolean.TRUE.equals(requestLog))
|
||||
.needConfirmationCode(Boolean.TRUE.equals(needConfirmationCode))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package ru.copperside.model.session;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.extern.jackson.Jacksonized;
|
||||
import ru.copperside.model.enums.AuthType;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.List;
|
||||
|
||||
@Jacksonized
|
||||
@Builder
|
||||
public record SessionSettings(
|
||||
Duration ttl,
|
||||
Boolean autoProlongation,
|
||||
AuthType authType,
|
||||
List<String> authStepTypes,
|
||||
Boolean ignoreConfirmation,
|
||||
Boolean oneActiveSession,
|
||||
Boolean inMemory
|
||||
) {}
|
||||
@@ -0,0 +1,10 @@
|
||||
package ru.copperside.repository;
|
||||
|
||||
import ru.copperside.model.authinfo.AuthInfo;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
public interface AuthInfoRepository {
|
||||
Optional<AuthInfo> findByDataIdAndType(String dataId, String type);
|
||||
Optional<AuthInfo> findByAuthId(Long authId);
|
||||
}
|
||||
@@ -0,0 +1,250 @@
|
||||
/*
|
||||
package ru.copperside.repository;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.jdbc.core.RowMapper;
|
||||
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
|
||||
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
|
||||
import ru.copperside.model.authinfo.*;
|
||||
import ru.copperside.model.enums.AuthType;
|
||||
import ru.copperside.model.enums.KeyUsage;
|
||||
import ru.copperside.model.enums.SecretType;
|
||||
import ru.copperside.model.session.SessionSettings;
|
||||
import ru.copperside.sql.SqlRegistry;
|
||||
import ru.copperside.util.SessionSettingsHelper;
|
||||
|
||||
import static ru.copperside.util.RepoMappingHelper.*;
|
||||
|
||||
import java.sql.ResultSet;
|
||||
import java.time.Duration;
|
||||
import java.util.ArrayList;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
@Repository
|
||||
@RequiredArgsConstructor
|
||||
public class AuthInfoRepositoryJdbc implements AuthInfoRepository {
|
||||
private final NamedParameterJdbcTemplate jdbc;
|
||||
private final ObjectMapper om;
|
||||
private final SqlRegistry sqlRegistry;
|
||||
|
||||
private static final String SQL_INIT = "auth/find_authinfo_init_by_dataid_and_type";
|
||||
private static final String SQL_PRIV = "auth/find_privatedata_by_authid";
|
||||
private static final String SQL_ROLESET = "auth/find_rolesettings_by_authid";
|
||||
|
||||
@Override
|
||||
public Optional<AuthInfo> findByDataIdAndType(String dataId, String type) {
|
||||
var params = new MapSqlParameterSource()
|
||||
.addValue("dataId", dataId)
|
||||
.addValue("type", type);
|
||||
|
||||
RowMapper<AuthInfo> rm = (rs, rn) -> mapAuthInfo(rs);
|
||||
|
||||
List<AuthInfo> list = jdbc.query(sqlRegistry.get(SQL_INIT), params, rm);
|
||||
|
||||
if (!list.isEmpty()) {
|
||||
AuthInfo base = list.get(0);
|
||||
if (base.authId() != null) {
|
||||
JsonNode privateData = loadPrivateData(base.authId());
|
||||
return Optional.of(base.toBuilder().privateData(privateData).build());
|
||||
}
|
||||
}
|
||||
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
private JsonNode loadPrivateData(Long authId) {
|
||||
if (authId == null) return null;
|
||||
var params = new MapSqlParameterSource().addValue("authId", authId);
|
||||
ObjectNode pd = om.createObjectNode();
|
||||
|
||||
jdbc.query(sqlRegistry.get(SQL_PRIV), params, rs -> {
|
||||
String key = rs.getString("KEY");
|
||||
String value = rs.getString("VALUE");
|
||||
try {
|
||||
JsonNode node = om.readTree(value);
|
||||
pd.set(key, node);
|
||||
} catch (Exception e) {
|
||||
pd.put(key, value);
|
||||
}
|
||||
});
|
||||
return pd;
|
||||
}
|
||||
|
||||
private AuthInfo mapAuthInfo(ResultSet rs) {
|
||||
try {
|
||||
Long authId = getLong(rs, "AD_AUTHID"); // AD_AUTHID -> AuthInfo.authId
|
||||
String dataId = rs.getString("AD_DATAID"); // AD_DATAID -> AuthInfo.dataId
|
||||
Long hierarchyId = getLong(rs, "AH_HIERARCHYID"); // AH_HIERARCHYID -> AuthInfo.hierarchyId
|
||||
String displayName = rs.getString("AH_DISPLAYNAME"); // AH_DISPLAYNAME -> AuthInfo.displayName
|
||||
String secretType = rs.getString("AD_TYPE");
|
||||
|
||||
Boolean isEnabled = null;
|
||||
Boolean needActivation = null;
|
||||
|
||||
JsonNode paramsJson = readJsonFlexible(rs, "AI_PARAMETERS", om);
|
||||
if (paramsJson != null && !paramsJson.isNull()) {
|
||||
isEnabled = getBooleanCaseInsensitive(paramsJson, "IsEnabled", "isEnabled");
|
||||
needActivation = getBooleanCaseInsensitive(paramsJson, "NeedActivation", "needActivation");
|
||||
}
|
||||
|
||||
SecretData secretData = null;
|
||||
JsonNode secretJson = readJsonFlexible(rs, "AD_DATA", om);
|
||||
|
||||
if (secretJson != null && !secretJson.isNull()) {
|
||||
Boolean sdIsEnabled = getBooleanCaseInsensitive(secretJson, "IsEnable", "isEnabled");
|
||||
String keyUsageStr = getStringCaseInsensitive(secretJson, "KeyUsage", "keyUsage");
|
||||
String secretTypeStr = getStringCaseInsensitive(secretJson, "SecretType", "secretType");
|
||||
String secretValue = getStringCaseInsensitive(secretJson, "Secret", "secret");
|
||||
|
||||
EnumSet<KeyUsage> keyUsages = parseKeyUsageSet(secretJson, keyUsageStr);
|
||||
SecretType sType = parseSecretType(secretTypeStr);
|
||||
|
||||
secretData = SecretData.builder()
|
||||
.type(secretType) // тип секрета из AD_TYPE (внешний)
|
||||
.isEnabled(sdIsEnabled)
|
||||
.keyUsage(keyUsages)
|
||||
.secretType(sType)
|
||||
.secret(secretValue)
|
||||
.build();
|
||||
}else {
|
||||
secretData = SecretData.builder()
|
||||
.type(secretType)
|
||||
.build();
|
||||
}
|
||||
|
||||
JsonNode aiSettingsJson = readJsonFlexible(rs, "AI_SETTINGS", om);
|
||||
SessionSettings sessionSettings = computeSessionSettings(authId, aiSettingsJson); // ⬅️ главный расчёт
|
||||
|
||||
return AuthInfo.builder()
|
||||
.authId(authId)
|
||||
.dataId(dataId)
|
||||
.hierarchyId(hierarchyId)
|
||||
.displayName(displayName)
|
||||
.isEnabled(isEnabled)
|
||||
.needActivation(needActivation)
|
||||
.permissions(List.of())
|
||||
.sessionSettings(sessionSettings)
|
||||
.sessionData(null)
|
||||
.privateData(null)
|
||||
.secretData(secretData)
|
||||
.build();
|
||||
} catch (Exception e) {
|
||||
throw new IllegalStateException("Failed to map AuthInfo", e);
|
||||
}
|
||||
}
|
||||
|
||||
private SessionSettings computeSessionSettings(Long authId, JsonNode aiSettingsJson) {
|
||||
// userSettings = {}; merge(AI_SETTINGS, false)
|
||||
SessionSettings userSettings = SessionSettingsHelper.merge(
|
||||
SessionSettings.builder().build(),
|
||||
mapSessionSettings(aiSettingsJson),
|
||||
false
|
||||
);
|
||||
|
||||
// Ролевые настройки с ORDER BY LEVEL (накатываем последовательно isRole=true)
|
||||
var resultRef = new AtomicReference<>(SessionSettings.builder().build());
|
||||
var params = new MapSqlParameterSource().addValue("authId", authId);
|
||||
jdbc.query(sqlRegistry.get(SQL_ROLESET), params, rs -> {
|
||||
JsonNode roleJson = readJsonFlexible(rs, "SETTINGS", om);
|
||||
SessionSettings roleSet = mapSessionSettings(roleJson);
|
||||
resultRef.set(SessionSettingsHelper.merge(resultRef.get(), roleSet, true));
|
||||
});
|
||||
|
||||
// финально поверх — userSettings (isRole=false)
|
||||
return SessionSettingsHelper.merge(resultRef.get(), userSettings, false);
|
||||
}
|
||||
|
||||
private SessionSettings mapSessionSettings(JsonNode node) {
|
||||
if (node == null || node.isNull()) return null;
|
||||
|
||||
Duration ttl = null;
|
||||
if (node.hasNonNull("ttlSec")) {
|
||||
ttl = Duration.ofSeconds(node.path("ttlSec").asLong(0));
|
||||
} else if (node.hasNonNull("ttl")) {
|
||||
try { ttl = Duration.parse(node.path("ttl").asText()); } catch (Exception ignore) {}
|
||||
}
|
||||
|
||||
AuthType authType = null;
|
||||
if (node.hasNonNull("authType")) {
|
||||
var at = node.get("authType");
|
||||
if (at.isInt()) {
|
||||
authType = AuthType.fromCode(at.asInt(0));
|
||||
} else {
|
||||
try { authType = AuthType.valueOf(at.asText()); } catch (Exception ignore) {}
|
||||
}
|
||||
}
|
||||
|
||||
List<String> steps = new ArrayList<>();
|
||||
if (node.hasNonNull("authStepTypes") && node.get("authStepTypes").isArray()) {
|
||||
for (JsonNode n : node.get("authStepTypes")) {
|
||||
steps.add(n.asText());
|
||||
}
|
||||
}
|
||||
|
||||
return new SessionSettingsBuilder()
|
||||
.ttl(ttl)
|
||||
.autoProlongation(asNullableBool(node, "autoProlongation"))
|
||||
.authType(authType)
|
||||
.authStepTypes(steps)
|
||||
.ignoreConfirmation(asNullableBool(node, "ignoreConfirmation"))
|
||||
.oneActiveSession(asNullableBool(node, "oneActiveSession"))
|
||||
.inMemory(asNullableBool(node, "inMemory"))
|
||||
.build();
|
||||
}
|
||||
|
||||
private Boolean asNullableBool(JsonNode node, String field) {
|
||||
return node.has(field) && !node.get(field).isNull() ? node.get(field).asBoolean() : null;
|
||||
}
|
||||
|
||||
private static final class SessionSettingsBuilder {
|
||||
private Duration ttl;
|
||||
private Boolean autoProlongation;
|
||||
private AuthType authType;
|
||||
private List<String> authStepTypes;
|
||||
private Boolean ignoreConfirmation;
|
||||
private Boolean oneActiveSession;
|
||||
private Boolean inMemory;
|
||||
|
||||
SessionSettingsBuilder() {}
|
||||
SessionSettingsBuilder(SessionSettings base) {
|
||||
if (base == null) return;
|
||||
this.ttl = base.ttl();
|
||||
this.autoProlongation = base.autoProlongation();
|
||||
this.authType = base.authType();
|
||||
this.authStepTypes = base.authStepTypes();
|
||||
this.ignoreConfirmation = base.ignoreConfirmation();
|
||||
this.oneActiveSession = base.oneActiveSession();
|
||||
this.inMemory = base.inMemory();
|
||||
}
|
||||
|
||||
SessionSettingsBuilder ttl(Duration v) { this.ttl = v; return this; }
|
||||
SessionSettingsBuilder autoProlongation(Boolean v) { this.autoProlongation = v; return this; }
|
||||
SessionSettingsBuilder authType(AuthType v) { this.authType = v; return this; }
|
||||
SessionSettingsBuilder authStepTypes(List<String> v) { this.authStepTypes = v; return this; }
|
||||
SessionSettingsBuilder ignoreConfirmation(Boolean v) { this.ignoreConfirmation = v; return this; }
|
||||
SessionSettingsBuilder oneActiveSession(Boolean v) { this.oneActiveSession = v; return this; }
|
||||
SessionSettingsBuilder inMemory(Boolean v) { this.inMemory = v; return this; }
|
||||
|
||||
SessionSettings build() {
|
||||
return SessionSettings.builder()
|
||||
.ttl(ttl)
|
||||
.autoProlongation(autoProlongation)
|
||||
.authType(authType)
|
||||
.authStepTypes(authStepTypes == null ? null : List.copyOf(authStepTypes))
|
||||
.ignoreConfirmation(ignoreConfirmation)
|
||||
.oneActiveSession(oneActiveSession)
|
||||
.inMemory(inMemory)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
@@ -0,0 +1,12 @@
|
||||
package ru.copperside.repository;
|
||||
|
||||
import ru.copperside.model.dto.RolePermissionDto;
|
||||
import ru.copperside.model.permission.Permission;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface PermissionsRepository {
|
||||
List<RolePermissionDto> findRoleByHierarchyId(Long hierarchyId);
|
||||
List<RolePermissionDto> findPersonalByHierarchyId(Long hierarchyId);
|
||||
List<Permission> findCompiledByHierarchyId(Long hierarchyId);
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
package ru.copperside.repository;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
|
||||
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
|
||||
import org.springframework.stereotype.Repository;
|
||||
import ru.copperside.model.dto.RolePermissionDto;
|
||||
import ru.copperside.sql.SqlRegistry;
|
||||
import ru.copperside.util.PermissionsCompiler;
|
||||
import ru.copperside.model.permission.Permission;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static ru.copperside.util.RepoMappingHelper.*;
|
||||
|
||||
@Repository
|
||||
@RequiredArgsConstructor
|
||||
public class PermissionsRepositoryJdbc implements PermissionsRepository{
|
||||
private final NamedParameterJdbcTemplate namedParameterJdbcTemplate;
|
||||
private final SqlRegistry sqlRegistry;
|
||||
private final ObjectMapper objectMapper;
|
||||
|
||||
@Override
|
||||
public List<RolePermissionDto> findRoleByHierarchyId(Long hierarchyId) {
|
||||
var p = new MapSqlParameterSource().addValue("hierarchyId", hierarchyId);
|
||||
|
||||
return namedParameterJdbcTemplate.query(sqlRegistry.get("auth/find_inherited_role_permissions_by_hierarchyid"), p, (rs, rn) -> {
|
||||
return new RolePermissionDto(
|
||||
rs.getLong("HIERARCHYID"),
|
||||
rs.getInt("LEVEL"),
|
||||
rs.getLong("ROLEID"),
|
||||
rs.getLong("PERMISSIONID"),
|
||||
rs.getString("PERMISSION_STRID"),
|
||||
readLargeText(rs, "SETTINGS"),
|
||||
getNullableBoolean(rs, "ACTION"),
|
||||
readLargeText(rs, "PDATA"),
|
||||
rs.getString("COMMAND"),
|
||||
rs.getString("HTTP")
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<RolePermissionDto> findPersonalByHierarchyId(Long hierarchyId) {
|
||||
var p = new MapSqlParameterSource().addValue("hierarchyId", hierarchyId);
|
||||
|
||||
return namedParameterJdbcTemplate.query(sqlRegistry.get("auth/find_inherited_permissions_by_hierarchyid"), p, (rs, rn) ->
|
||||
new RolePermissionDto(
|
||||
getNullableLong(rs, "HIERARCHYID"),
|
||||
rs.getInt("LEVEL"),
|
||||
getNullableLong(rs, "ROLEID"), // здесь всегда null из SQL
|
||||
rs.getLong("PERMISSIONID"),
|
||||
rs.getString("PERMISSION_STRID"),
|
||||
readLargeText(rs, "SETTINGS"),
|
||||
getNullableBoolean(rs, "ACTION"),
|
||||
readLargeText(rs, "PDATA"),
|
||||
rs.getString("COMMAND"),
|
||||
rs.getString("HTTP")
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Permission> findCompiledByHierarchyId(Long hierarchyId) {
|
||||
var rolePerms = findRoleByHierarchyId(hierarchyId);
|
||||
var personalPerms = findPersonalByHierarchyId(hierarchyId);
|
||||
return new PermissionsCompiler(objectMapper).compile(rolePerms, personalPerms);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package ru.copperside.repository;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
|
||||
public interface PrivateDataRepository {
|
||||
JsonNode findByAuthId(Long authId);
|
||||
}
|
||||
10
src/main/java/ru/copperside/repository/SecretRepository.java
Normal file
10
src/main/java/ru/copperside/repository/SecretRepository.java
Normal file
@@ -0,0 +1,10 @@
|
||||
package ru.copperside.repository;
|
||||
|
||||
|
||||
import ru.copperside.model.authinfo.*;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
public interface SecretRepository {
|
||||
Optional<SecretData> findByDataIdAndType(String dataId, String type);
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
package ru.copperside.repository;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.ObjectReader;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
|
||||
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
|
||||
import org.springframework.stereotype.Repository;
|
||||
import ru.copperside.model.enums.AuthenticationType;
|
||||
import ru.copperside.model.authinfo.SecretData;
|
||||
import ru.copperside.model.enums.SecretType;
|
||||
import ru.copperside.model.enums.KeyUsage;
|
||||
import ru.copperside.model.dto.SecretDTO;
|
||||
import ru.copperside.repository.SecretRepository;
|
||||
import ru.copperside.sql.SqlRegistry;
|
||||
|
||||
import java.util.EnumSet;
|
||||
import java.util.Optional;
|
||||
|
||||
import static ru.copperside.util.RepoMappingHelper.*;
|
||||
|
||||
@Repository
|
||||
@RequiredArgsConstructor
|
||||
public class SecretRepositoryJdbc implements SecretRepository {
|
||||
|
||||
private final NamedParameterJdbcTemplate jdbc;
|
||||
private final ObjectMapper om;
|
||||
private final SqlRegistry sql;
|
||||
|
||||
private final ObjectReader secretReader(ObjectMapper om) {
|
||||
return om.readerFor(SecretDTO.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<SecretData> findByDataIdAndType(String dataId, String type) {
|
||||
var p = new MapSqlParameterSource()
|
||||
.addValue("dataId", dataId)
|
||||
.addValue("type", (type == null || type.isBlank())
|
||||
? AuthenticationType.Secret.name()
|
||||
: type);
|
||||
|
||||
var list = jdbc.query(sql.get("auth/find_secret_by_dataid_and_type"), p, (rs, rn) -> {
|
||||
// тип аутентификации из AD_TYPE, по умолчанию Secret
|
||||
var authType = AuthenticationType.from(rs.getString("AD_TYPE"));
|
||||
|
||||
// читаем AD_DATA -> SecretDTO
|
||||
SecretDTO dto = readJson(rs, "AD_DATA", secretReader(om));
|
||||
|
||||
if (dto == null) {
|
||||
return SecretData.builder()
|
||||
.authType(authType)
|
||||
.keyUsages(EnumSet.of(KeyUsage.None))
|
||||
.build();
|
||||
}
|
||||
|
||||
EnumSet<KeyUsage> usages = parseKeyUsages(dto.keyUsage());
|
||||
SecretType sType = parseSecretType(dto.secretType());
|
||||
|
||||
return SecretData.builder()
|
||||
.authType(authType)
|
||||
.isEnabled(dto.isEnable())
|
||||
.keyUsages(usages.isEmpty() ? EnumSet.of(KeyUsage.None) : usages)
|
||||
.secretType(sType)
|
||||
.secret(dto.secret())
|
||||
.build();
|
||||
});
|
||||
|
||||
return list.stream().findFirst();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package ru.copperside.repository;
|
||||
|
||||
import ru.copperside.model.session.SessionSettings;
|
||||
|
||||
public interface SettingsRepository {
|
||||
SessionSettings loadMerged(Long authId);
|
||||
}
|
||||
24
src/main/java/ru/copperside/service/AuthInfoService.java
Normal file
24
src/main/java/ru/copperside/service/AuthInfoService.java
Normal file
@@ -0,0 +1,24 @@
|
||||
/*package ru.copperside.service;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.data.crossstore.ChangeSetPersister;
|
||||
import org.springframework.stereotype.Service;
|
||||
import ru.copperside.model.authinfo.AuthInfo;
|
||||
import ru.copperside.repository.AuthInfoRepository;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class AuthInfoService {
|
||||
private final AuthInfoRepository repo;
|
||||
|
||||
public AuthInfo getByDataIdAndType(String dataId) {
|
||||
return repo.findByDataIdAndType(dataId, "Secret")
|
||||
.orElseThrow(() -> new NotFoundException(
|
||||
"AuthInfo(init) not found by dataId=" + dataId + ", type=Secret"));
|
||||
}
|
||||
|
||||
public static class NotFoundException extends RuntimeException {
|
||||
public NotFoundException(String msg) { super(msg); }
|
||||
}
|
||||
}
|
||||
*/
|
||||
45
src/main/java/ru/copperside/service/DebugAuthService.java
Normal file
45
src/main/java/ru/copperside/service/DebugAuthService.java
Normal file
@@ -0,0 +1,45 @@
|
||||
package ru.copperside.service;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
import ru.copperside.model.authinfo.SecretData;
|
||||
import ru.copperside.model.dto.RolePermissionDto;
|
||||
import ru.copperside.model.enums.AuthenticationType;
|
||||
import ru.copperside.model.permission.Permission;
|
||||
import ru.copperside.repository.PermissionsRepository;
|
||||
import ru.copperside.repository.SecretRepository;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class DebugAuthService {
|
||||
|
||||
private final SecretRepository secretRepository;
|
||||
private final PermissionsRepository permissionsRepository;
|
||||
|
||||
public SecretData getSecretData(String dataId, String type) {
|
||||
// если не указали type — считаем Secret
|
||||
String resolvedType = (type == null || type.isBlank())
|
||||
? AuthenticationType.Secret.name()
|
||||
: type;
|
||||
|
||||
return secretRepository.findByDataIdAndType(dataId, resolvedType)
|
||||
.orElseThrow(() ->
|
||||
new IllegalArgumentException("Secret not found: dataId=" + dataId + ", type=" + resolvedType));
|
||||
}
|
||||
|
||||
public List<RolePermissionDto> byHierarchy(Long hierarchyId) {
|
||||
return permissionsRepository.findRoleByHierarchyId(hierarchyId);
|
||||
}
|
||||
|
||||
public List<RolePermissionDto> PersonalbyHierarchy(Long hierarchyId) {
|
||||
return permissionsRepository.findPersonalByHierarchyId(hierarchyId);
|
||||
}
|
||||
|
||||
public List<Permission> getPermissions(Long hierarchyId) {
|
||||
return permissionsRepository.findCompiledByHierarchyId(hierarchyId);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
65
src/main/java/ru/copperside/sql/SqlRegistry.java
Normal file
65
src/main/java/ru/copperside/sql/SqlRegistry.java
Normal file
@@ -0,0 +1,65 @@
|
||||
package ru.copperside.sql;
|
||||
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.io.support.ResourcePatternResolver;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
@Slf4j
|
||||
@Component
|
||||
public class SqlRegistry {
|
||||
|
||||
private final Map<String, String> sqls = new ConcurrentHashMap<>();
|
||||
private final ResourcePatternResolver resolver;
|
||||
|
||||
public SqlRegistry(ResourcePatternResolver resolver) {
|
||||
this.resolver = resolver;
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
void init() {
|
||||
try {
|
||||
// важно: слэш после classpath*:
|
||||
Resource[] resources = resolver.getResources("classpath*:/sql/**/*.sql");
|
||||
int count = 0;
|
||||
for (Resource res : resources) {
|
||||
String key = buildKey(res);
|
||||
String sql = new String(res.getInputStream().readAllBytes(), StandardCharsets.UTF_8).trim();
|
||||
sqls.put(key, sql);
|
||||
count++;
|
||||
}
|
||||
log.info("SqlRegistry loaded {} sql files", count);
|
||||
} catch (IOException e) {
|
||||
throw new IllegalStateException("Failed to load SQL resources", e);
|
||||
}
|
||||
}
|
||||
|
||||
private String buildKey(Resource res) throws IOException {
|
||||
// поддерживает и jar:...!/BOOT-INF/classes!/sql/...
|
||||
String url = res.getURL().toString();
|
||||
int idx = url.indexOf("/sql/");
|
||||
if (idx >= 0) {
|
||||
String afterSql = url.substring(idx + 5); // после "/sql/"
|
||||
// убираем всё до возможного "!" для jar-URL (на всякий)
|
||||
int bang = afterSql.indexOf('!');
|
||||
if (bang >= 0) afterSql = afterSql.substring(bang + 1);
|
||||
return afterSql.replaceFirst("\\.sql$", "");
|
||||
}
|
||||
// fallback — по имени файла
|
||||
String name = res.getFilename();
|
||||
if (name == null) throw new IOException("No filename for resource: " + url);
|
||||
return name.replaceFirst("\\.sql$", "");
|
||||
}
|
||||
|
||||
public String get(String key) {
|
||||
String sql = sqls.get(key);
|
||||
if (sql == null) throw new IllegalArgumentException("SQL not found: " + key);
|
||||
return sql;
|
||||
}
|
||||
}
|
||||
123
src/main/java/ru/copperside/util/PermissionsCompiler.java
Normal file
123
src/main/java/ru/copperside/util/PermissionsCompiler.java
Normal file
@@ -0,0 +1,123 @@
|
||||
package ru.copperside.util;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import ru.copperside.model.dto.RolePermissionDto;
|
||||
import ru.copperside.model.permission.Permission;
|
||||
import ru.copperside.model.permission.PermissionSettings;
|
||||
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class PermissionsCompiler {
|
||||
|
||||
private final ObjectMapper objectMapper;
|
||||
|
||||
|
||||
public List<Permission> compile(List<RolePermissionDto> rolePermissions,
|
||||
List<RolePermissionDto> personalPermissions) {
|
||||
|
||||
Map<Long, Permission> compiled = new LinkedHashMap<>();
|
||||
|
||||
int maxLevel = Math.max(
|
||||
rolePermissions.stream()
|
||||
.map(rp -> Optional.ofNullable(rp.level()).orElse(0))
|
||||
.max(Integer::compareTo)
|
||||
.orElse(0),
|
||||
personalPermissions.stream()
|
||||
.map(p -> Optional.ofNullable(p.level()).orElse(0))
|
||||
.max(Integer::compareTo)
|
||||
.orElse(0)
|
||||
);
|
||||
|
||||
for (int level = 1; level <= maxLevel; level++) {
|
||||
final int lvl = level;
|
||||
|
||||
rolePermissions.stream()
|
||||
.filter(rp -> Objects.equals(rp.level(), lvl))
|
||||
.forEach(rp -> {
|
||||
PermissionSettings settings = deserializeSettings(rp.settingsJson());
|
||||
JsonNode pdata = deserializeJson(rp.pDataJson());
|
||||
|
||||
if (compiled.containsKey(rp.permissionId())) {
|
||||
Permission existing = compiled.get(rp.permissionId());
|
||||
existing = existing.toBuilder()
|
||||
.action(true)
|
||||
.permissionData(pdata)
|
||||
.build();
|
||||
compiled.put(rp.permissionId(), existing);
|
||||
} else {
|
||||
compiled.put(rp.permissionId(),
|
||||
Permission.builder()
|
||||
.permissionId(rp.permissionId())
|
||||
.permissionStrId(rp.permissionStrId())
|
||||
.settings(settings)
|
||||
.action(true)
|
||||
.permissionData(pdata)
|
||||
.command(rp.command())
|
||||
.http(rp.http())
|
||||
.build());
|
||||
}
|
||||
});
|
||||
|
||||
// ==== персональные (иерархические) ====
|
||||
personalPermissions.stream()
|
||||
.filter(p -> Objects.equals(p.level(), lvl))
|
||||
.forEach(p -> {
|
||||
PermissionSettings settings = deserializeSettings(p.settingsJson());
|
||||
JsonNode pdata = deserializeJson(p.pDataJson());
|
||||
|
||||
if (compiled.containsKey(p.permissionId())) {
|
||||
Permission existing = compiled.get(p.permissionId());
|
||||
existing = existing.toBuilder()
|
||||
.action(Boolean.TRUE.equals(p.action()))
|
||||
.permissionData(pdata)
|
||||
.build();
|
||||
compiled.put(p.permissionId(), existing);
|
||||
} else {
|
||||
compiled.put(p.permissionId(),
|
||||
Permission.builder()
|
||||
.permissionId(p.permissionId())
|
||||
.permissionStrId(p.permissionStrId())
|
||||
.settings(settings)
|
||||
.action(Boolean.TRUE.equals(p.action()))
|
||||
.permissionData(pdata)
|
||||
.command(p.command())
|
||||
.http(p.http())
|
||||
.build());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// только активные
|
||||
return compiled.values().stream()
|
||||
.filter(Permission::action)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/* ===== helpers ===== */
|
||||
private PermissionSettings deserializeSettings(String json) {
|
||||
if (json == null || json.isBlank()) return null;
|
||||
try {
|
||||
var s = objectMapper.readValue(json, PermissionSettings.class);
|
||||
return s == null ? null : s.normalize();
|
||||
}
|
||||
catch (Exception e) { return null; }
|
||||
}
|
||||
|
||||
private JsonNode deserializeJson(String json) {
|
||||
try {
|
||||
if (json == null || json.isBlank()) {
|
||||
return objectMapper.createObjectNode(); // <-- пустой объект вместо null
|
||||
}
|
||||
JsonNode n = objectMapper.readTree(json);
|
||||
// гарантируем объект
|
||||
return (n == null || !n.isObject()) ? objectMapper.createObjectNode() : n;
|
||||
} catch (Exception e) {
|
||||
return objectMapper.createObjectNode(); // на ошибке тоже пустой объект
|
||||
}
|
||||
}
|
||||
}
|
||||
82
src/main/java/ru/copperside/util/RepoMappingHelper.java
Normal file
82
src/main/java/ru/copperside/util/RepoMappingHelper.java
Normal file
@@ -0,0 +1,82 @@
|
||||
package ru.copperside.util;
|
||||
|
||||
|
||||
import java.io.Reader;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
|
||||
import ru.copperside.model.enums.KeyUsage;
|
||||
import ru.copperside.model.enums.SecretType;
|
||||
|
||||
public final class RepoMappingHelper {
|
||||
private RepoMappingHelper() {}
|
||||
|
||||
public static Long getNullableLong(ResultSet rs, String col) throws SQLException {
|
||||
long v = rs.getLong(col);
|
||||
return rs.wasNull() ? null : v;
|
||||
}
|
||||
|
||||
public static Boolean getNullableBoolean(ResultSet rs, String col) {
|
||||
try {
|
||||
boolean v = rs.getBoolean(col);
|
||||
return rs.wasNull() ? null : v;
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T readJson(ResultSet rs, String col, com.fasterxml.jackson.databind.ObjectReader reader) {
|
||||
try (Reader r = rs.getCharacterStream(col)) {
|
||||
if (r != null) return reader.readValue(r);
|
||||
} catch (Exception ignore) {}
|
||||
try {
|
||||
String s = rs.getString(col);
|
||||
return (s == null || s.isBlank()) ? null : reader.readValue(s);
|
||||
} catch (Exception ignore) {}
|
||||
return null;
|
||||
}
|
||||
|
||||
/** Универсальное чтение CLOB / VARCHAR */
|
||||
public static String readLargeText(ResultSet rs, String col) {
|
||||
// 1) CLOB → Reader
|
||||
try (Reader r = rs.getCharacterStream(col)) {
|
||||
if (r != null) {
|
||||
var sw = new java.io.StringWriter();
|
||||
r.transferTo(sw);
|
||||
return sw.toString();
|
||||
}
|
||||
} catch (Exception ignore) {}
|
||||
// 2) Fallback: обычная строка
|
||||
try {
|
||||
return rs.getString(col);
|
||||
} catch (Exception ignore) {}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static EnumSet<KeyUsage> parseKeyUsages(Object raw) {
|
||||
EnumSet<KeyUsage> set = EnumSet.noneOf(KeyUsage.class);
|
||||
if (raw == null) return EnumSet.of(KeyUsage.None);
|
||||
if (raw instanceof String s) {
|
||||
for (String p : s.split("\\s*,\\s*")) addUsage(set, p);
|
||||
} else if (raw instanceof List<?> list) {
|
||||
for (Object o : list) addUsage(set, String.valueOf(o));
|
||||
}
|
||||
return set.isEmpty() ? EnumSet.of(KeyUsage.None) : set;
|
||||
}
|
||||
|
||||
private static void addUsage(EnumSet<KeyUsage> set, String v) {
|
||||
switch (v.trim().toLowerCase()) {
|
||||
case "password" -> set.add(KeyUsage.Password);
|
||||
case "hmac", "hmacsha256" -> set.add(KeyUsage.HMac);
|
||||
case "none", "" -> set.add(KeyUsage.None);
|
||||
default -> {}
|
||||
}
|
||||
}
|
||||
|
||||
public static SecretType parseSecretType(String s) {
|
||||
if (s == null) return null;
|
||||
try { return SecretType.valueOf(s); } catch (IllegalArgumentException ex) { return null; }
|
||||
}
|
||||
}
|
||||
64
src/main/java/ru/copperside/util/SessionSettingsHelper.java
Normal file
64
src/main/java/ru/copperside/util/SessionSettingsHelper.java
Normal file
@@ -0,0 +1,64 @@
|
||||
package ru.copperside.util;
|
||||
|
||||
import ru.copperside.model.enums.AuthType;
|
||||
import ru.copperside.model.session.SessionSettings;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
public final class SessionSettingsHelper {
|
||||
private SessionSettingsHelper() {}
|
||||
|
||||
public static SessionSettings merge(SessionSettings target, SessionSettings merge, boolean isRole) {
|
||||
if (target == null && merge == null) return null;
|
||||
if (target == null) target = SessionSettings.builder().build();
|
||||
if (merge == null) return target;
|
||||
|
||||
Duration ttl = target.ttl();
|
||||
Boolean auto = target.autoProlongation();
|
||||
AuthType at = target.authType();
|
||||
List<String> steps = target.authStepTypes();
|
||||
Boolean ign = target.ignoreConfirmation();
|
||||
Boolean one = target.oneActiveSession();
|
||||
Boolean mem = target.inMemory();
|
||||
|
||||
if (isRole) {
|
||||
if (merge.ttl() != null) ttl = merge.ttl();
|
||||
if (merge.autoProlongation() != null) auto = merge.autoProlongation();
|
||||
if (merge.authType() != null) at = (at == null) ? merge.authType() : max(at, merge.authType());
|
||||
if (merge.authStepTypes() != null && !merge.authStepTypes().isEmpty()) {
|
||||
Set<String> s = new LinkedHashSet<>();
|
||||
if (steps != null) s.addAll(steps);
|
||||
s.addAll(merge.authStepTypes());
|
||||
steps = List.copyOf(s);
|
||||
}
|
||||
if (merge.ignoreConfirmation() != null) {
|
||||
if (ign == null) ign = merge.ignoreConfirmation();
|
||||
else if (!merge.ignoreConfirmation()) ign = false;
|
||||
}
|
||||
} else {
|
||||
if (merge.ttl() != null) ttl = merge.ttl();
|
||||
if (merge.autoProlongation() != null) auto = merge.autoProlongation();
|
||||
if (merge.authType() != null) at = merge.authType();
|
||||
if (merge.authStepTypes() != null) steps = merge.authStepTypes();
|
||||
if (merge.ignoreConfirmation() != null) ign = merge.ignoreConfirmation();
|
||||
if (merge.oneActiveSession() != null) one = merge.oneActiveSession();
|
||||
}
|
||||
|
||||
mem = Boolean.TRUE.equals(mem) || Boolean.TRUE.equals(merge.inMemory());
|
||||
|
||||
return SessionSettings.builder()
|
||||
.ttl(ttl).autoProlongation(auto).authType(at)
|
||||
.authStepTypes(steps)
|
||||
.ignoreConfirmation(ign).oneActiveSession(one).inMemory(mem)
|
||||
.build();
|
||||
}
|
||||
|
||||
private static AuthType max(AuthType a, AuthType b) {
|
||||
int ca = a == null ? 0 : a.ordinal();
|
||||
int cb = b == null ? 0 : b.ordinal();
|
||||
return (ca >= cb) ? a : b; // Unknown < Simple < TwoStep
|
||||
}
|
||||
}
|
||||
9
src/main/resources/application.yaml
Normal file
9
src/main/resources/application.yaml
Normal file
@@ -0,0 +1,9 @@
|
||||
server:
|
||||
port: 8080
|
||||
|
||||
spring:
|
||||
datasource:
|
||||
url: jdbc:oracle:thin:@127.0.0.1:1521/TKBPAYPDB
|
||||
username: WFMPROP
|
||||
password: WFMPROP
|
||||
driver-class-name: oracle.jdbc.OracleDriver
|
||||
@@ -0,0 +1,21 @@
|
||||
SELECT
|
||||
-- из AUTHDATA
|
||||
ad.AUTHID AS AD_AUTHID,
|
||||
ad.TYPE AS AD_TYPE,
|
||||
ad.DATAID AS AD_DATAID,
|
||||
ad.DATA AS AD_DATA,
|
||||
|
||||
-- из AUTHHIERARCHY + AUTHIDS
|
||||
ah.HIERARCHYID AS AH_HIERARCHYID,
|
||||
ai.AUTHID AS AI_AUTHID,
|
||||
ai.SETTINGS AS AI_SETTINGS,
|
||||
ai.PARAMETERS AS AI_PARAMETERS,
|
||||
ah.DISPLAYNAME AS AH_DISPLAYNAME
|
||||
|
||||
FROM AUTHDATA ad
|
||||
LEFT JOIN AUTHIDS ai
|
||||
ON ai.AUTHID = ad.AUTHID
|
||||
LEFT JOIN AUTHHIERARCHY ah
|
||||
ON ah.AUTHID = ai.AUTHID
|
||||
WHERE ad.DATAID = :dataId
|
||||
AND ad.TYPE = :type
|
||||
@@ -0,0 +1,24 @@
|
||||
SELECT
|
||||
hp.HIERARCHYID AS HIERARCHYID,
|
||||
h."LEVEL" AS "LEVEL",
|
||||
/* у иерархических прав нет роли: оставим NULL как роль */
|
||||
NULL AS ROLEID,
|
||||
|
||||
hp.PERMISSIONID AS PERMISSIONID,
|
||||
p.STRID AS PERMISSION_STRID,
|
||||
p.SETTINGS AS SETTINGS,
|
||||
hp.ACTION AS ACTION,
|
||||
hp.PERMISSIONDATA AS PDATA,
|
||||
p.COMMAND AS COMMAND,
|
||||
p.HTTP AS HTTP
|
||||
FROM HIERARCHYPERMISSION hp
|
||||
JOIN PERMISSIONS p ON p.PERMISSIONID = hp.PERMISSIONID
|
||||
JOIN (
|
||||
SELECT ah.HIERARCHYID, ah."LEVEL"
|
||||
FROM AUTHHIERARCHY ah, AUTHHIERARCHY ah2
|
||||
WHERE ah.LEFTKEY <= ah2.LEFTKEY
|
||||
AND ah.RIGHTKEY >= ah2.RIGHTKEY
|
||||
AND ah2.HIERARCHYID = :hierarchyId
|
||||
ORDER BY ah."LEVEL"
|
||||
) h ON hp.HIERARCHYID = h.HIERARCHYID
|
||||
ORDER BY h."LEVEL", hp.PERMISSIONID
|
||||
@@ -0,0 +1,22 @@
|
||||
SELECT
|
||||
hr.HIERARCHYID AS HIERARCHYID,
|
||||
h."LEVEL" AS "LEVEL",
|
||||
hr.ROLEID AS ROLEID,
|
||||
rp.PERMISSIONID AS PERMISSIONID,
|
||||
p.STRID AS PERMISSION_STRID,
|
||||
p.SETTINGS AS SETTINGS,
|
||||
rp.PDATA AS PDATA,
|
||||
p.COMMAND AS COMMAND,
|
||||
p.HTTP AS HTTP
|
||||
FROM HIERARCHYROLE hr
|
||||
JOIN (
|
||||
SELECT ah.HIERARCHYID, ah."LEVEL"
|
||||
FROM AUTHHIERARCHY ah, AUTHHIERARCHY ah2
|
||||
WHERE ah.LEFTKEY <= ah2.LEFTKEY
|
||||
AND ah.RIGHTKEY >= ah2.RIGHTKEY
|
||||
AND ah2.HIERARCHYID = :hierarchyId
|
||||
ORDER BY ah."LEVEL"
|
||||
) h ON hr.HIERARCHYID = h.HIERARCHYID
|
||||
JOIN ROLEPERMISSIONS rp ON rp.ROLEID = hr.ROLEID
|
||||
JOIN PERMISSIONS p ON p.PERMISSIONID = rp.PERMISSIONID
|
||||
ORDER BY h."LEVEL", hr.ROLEID, rp.PERMISSIONID
|
||||
@@ -0,0 +1,3 @@
|
||||
SELECT KEY, VALUE
|
||||
FROM PRIVATEDATA
|
||||
WHERE AUTHID = :authId
|
||||
11
src/main/resources/sql/auth/find_rolesettings_by_authid.sql
Normal file
11
src/main/resources/sql/auth/find_rolesettings_by_authid.sql
Normal file
@@ -0,0 +1,11 @@
|
||||
SELECT r.SETTINGS
|
||||
FROM (
|
||||
SELECT ah.HIERARCHYID, ah."LEVEL"
|
||||
FROM AUTHHIERARCHY ah, AUTHHIERARCHY ah2
|
||||
WHERE ah.LEFTKEY <= ah2.LEFTKEY
|
||||
AND ah.RIGHTKEY >= ah2.RIGHTKEY
|
||||
AND ah2.AUTHID = :authId
|
||||
) h
|
||||
JOIN HIERARCHYROLE hr ON h.HIERARCHYID = hr.HIERARCHYID
|
||||
JOIN ROLES r ON r.ROLEID = hr.ROLEID
|
||||
ORDER BY h."LEVEL"
|
||||
@@ -0,0 +1,13 @@
|
||||
SELECT
|
||||
ad.AUTHID AS AD_AUTHID,
|
||||
ad.DATAID AS AD_DATAID,
|
||||
ad.TYPE AS AD_TYPE,
|
||||
ad.DATA AS AD_DATA,
|
||||
ai.PARAMETERS AS AI_PARAMETERS,
|
||||
ah.HIERARCHYID AS AH_HIERARCHYID,
|
||||
ah.DISPLAYNAME AS AH_DISPLAYNAME
|
||||
FROM AUTHDATA ad
|
||||
LEFT JOIN AUTHIDS ai ON ai.AUTHID = ad.AUTHID
|
||||
LEFT JOIN AUTHHIERARCHY ah ON ah.AUTHID = ad.AUTHID
|
||||
WHERE ad.DATAID = :dataId
|
||||
AND ad.TYPE = :type
|
||||
Reference in New Issue
Block a user