The keycloak project ended with four open questions that design could not
settle. A two-VM lab was built to answer them by measurement, and this is
that material: 26 experiments, 125 raw command outputs, 22 browser captures.
Follows the import procedure in README.md.
source/ the originating repository verbatim — 78 documents, 28 SVGs,
8 manifests, plus .source-revision recording the commit
final/ the SSOT
document.md 729 lines written from the 29 experiment documents, not
concatenated: what was predicted, what was measured, and
where the measurement itself was wrong
evidence/raw 125 outputs, flattened to <experiment>__<file> because
the originals collided (01-baseline.txt appeared three
times) and the audit only globs the top level
evidence/meta one per raw file; command and exitCode are null and the
README says why rather than inventing them
evidence/browser 22 captures
assets/ three diagrams through techviz
.techviz/ their VizSpecs
A separate project rather than an addition to keycloak: the B-layer answers
that project's four questions, but the A, C and D layers are about cluster
failure, SSO and operations, and one document.md should hold one subject.
The four question records there can point here through 관계.
Recorded rather than papered over: only three of the 28 diagrams were
remade. The repository forbids hand-drawn SVG and forbids titles inside the
canvas; all 28 originals carry both, so converting them is redrawing, not
reformatting. They stay in source/ and the gap is written into the document.
verify-pipeline.py passes. audit-records.py reports no issues.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
126 lines
8.1 KiB
Plaintext
126 lines
8.1 KiB
Plaintext
BrokerAclManifest.java : 141 줄
|
|
# 자바독이 적어 둔 두 검사
|
|
BrokerAclManifest.java:12 * <p>Written down so the grant can be reviewed and diffed rather than discovered from a broker
|
|
BrokerAclManifest.java:13 * dump. The manifest is what the platform checks itself against at startup: a runtime that holds
|
|
BrokerAclManifest.java:14 * more than it declares is a finding, because the extra permission is the one nobody reasoned
|
|
BrokerAclManifest.java:15 * about.
|
|
BrokerAclManifest.java:16 *
|
|
BrokerAclManifest.java:17 * <p>Destructive permissions are named separately from ordinary ones. {@code DELETE_TOPIC} and
|
|
BrokerAclManifest.java:18 * {@code PURGE} are not "write, but more"; they destroy data an application can never restore, so
|
|
BrokerAclManifest.java:19 * an application runtime declaring one is rejected outright.
|
|
BrokerAclManifest.java:20 *
|
|
|
|
# 그 두 성질에 대응하는 코드가 이 타입 안에 있다
|
|
BrokerAclManifest.java:94 public List<Grant> destructiveGrants() {
|
|
BrokerAclManifest.java:95 return grants.stream().filter(grant -> grant.operation().isDestructive()).toList();
|
|
BrokerAclManifest.java:98 /**
|
|
BrokerAclManifest.java:99 * Refuses a manifest that gives an application runtime destructive power.
|
|
BrokerAclManifest.java:100 *
|
|
BrokerAclManifest.java:101 * @throws MessagingConfigurationException when a destructive grant is declared
|
|
BrokerAclManifest.java:102 */
|
|
BrokerAclManifest.java:103 public void requireApplicationRuntime() {
|
|
BrokerAclManifest.java:104 List<Grant> destructive = destructiveGrants();
|
|
BrokerAclManifest.java:105 if (!destructive.isEmpty()) {
|
|
BrokerAclManifest.java:106 throw new MessagingConfigurationException(
|
|
BrokerAclManifest.java:107 "APPLICATION_HOLDS_DESTRUCTIVE_GRANT",
|
|
BrokerAclManifest.java:108 "principal %s declares %s, which an application runtime must never hold"
|
|
BrokerAclManifest.java:109 .formatted(principal, destructive.stream().map(Grant::operation).toList()));
|
|
BrokerAclManifest.java:110 }
|
|
BrokerAclManifest.java:111 }
|
|
BrokerAclManifest.java:113 /**
|
|
BrokerAclManifest.java:114 * Reports the permissions the runtime holds but never declared.
|
|
BrokerAclManifest.java:115 *
|
|
BrokerAclManifest.java:116 * <p>Excess is the finding, not the shortfall: a missing grant fails loudly on first use, while
|
|
BrokerAclManifest.java:117 * an undeclared extra one sits unnoticed until it is abused.
|
|
BrokerAclManifest.java:118 *
|
|
BrokerAclManifest.java:119 * @param observed the grants the broker actually reports for this principal
|
|
BrokerAclManifest.java:120 * @return the observed grants that this manifest does not declare
|
|
BrokerAclManifest.java:121 */
|
|
BrokerAclManifest.java:122 public Set<Grant> undeclared(Set<Grant> observed) {
|
|
BrokerAclManifest.java:123 Objects.requireNonNull(observed, "observed must not be null");
|
|
BrokerAclManifest.java:124 Set<Grant> extra = new LinkedHashSet<>(observed);
|
|
BrokerAclManifest.java:125 extra.removeAll(Set.copyOf(grants));
|
|
BrokerAclManifest.java:126 return Set.copyOf(extra);
|
|
BrokerAclManifest.java:127 }
|
|
BrokerAclManifest.java:128
|
|
BrokerAclManifest.java:130 * Reports the declared permissions the broker does not actually grant.
|
|
BrokerAclManifest.java:131 *
|
|
BrokerAclManifest.java:132 * @param observed the grants the broker actually reports for this principal
|
|
BrokerAclManifest.java:133 * @return the declared grants that are missing
|
|
BrokerAclManifest.java:134 */
|
|
BrokerAclManifest.java:135 public Set<Grant> missing(Set<Grant> observed) {
|
|
BrokerAclManifest.java:136 Objects.requireNonNull(observed, "observed must not be null");
|
|
BrokerAclManifest.java:137 Set<Grant> absent = new LinkedHashSet<>(grants);
|
|
BrokerAclManifest.java:138 absent.removeAll(observed);
|
|
BrokerAclManifest.java:139 return Set.copyOf(absent);
|
|
BrokerAclManifest.java:140 }
|
|
|
|
# 간결 생성자는 그 검사를 하지 않는다
|
|
BrokerAclManifest.java:81 public BrokerAclManifest {
|
|
BrokerAclManifest.java:82 Objects.requireNonNull(grants, "grants must not be null");
|
|
BrokerAclManifest.java:83 if (principal == null || principal.isBlank()) {
|
|
BrokerAclManifest.java:84 throw new IllegalArgumentException("principal must not be blank");
|
|
BrokerAclManifest.java:85 }
|
|
BrokerAclManifest.java:86 grants = List.copyOf(grants);
|
|
BrokerAclManifest.java:87 }
|
|
|
|
# 그 세 메서드를 부르는 자리 전부
|
|
main · BrokerAclManifest.java:94 public List<Grant> destructiveGrants() {
|
|
main · BrokerAclManifest.java:103 public void requireApplicationRuntime() {
|
|
main · BrokerAclManifest.java:104 List<Grant> destructive = destructiveGrants();
|
|
test · CredentialRuntimeRegistryTest.java:181 assertThatThrownBy(manifest::requireApplicationRuntime)
|
|
test · CredentialRuntimeRegistryTest.java:200 assertThat(manifest.undeclared(Set.of(declared, extra)))
|
|
그중 BrokerAclManifest.java 밖의 main 코드 :
|
|
0 건
|
|
# 이 타입을 만들거나 받는 프로덕션 코드
|
|
main 에서 0 건
|
|
자기 파일 밖에서 이 이름이 나오는 파일 수 : 1
|
|
messaging/messaging-security/src/test/java/dev/caskeleton/messaging/security/CredentialRuntimeRegistryTest.java
|
|
그 시험이 단언하는 것 :
|
|
CredentialRuntimeRegistryTest.java:167 void anApplicationRuntimeMayNotHoldADestructiveGrant() {
|
|
CredentialRuntimeRegistryTest.java:181 assertThatThrownBy(manifest::requireApplicationRuntime)
|
|
CredentialRuntimeRegistryTest.java:187 void aGrantTheBrokerHoldsButNobodyDeclaredIsTheFinding() {
|
|
CredentialRuntimeRegistryTest.java:200 assertThat(manifest.undeclared(Set.of(declared, extra)))
|
|
|
|
# 파괴적 권한이 값으로 표시되는 방식
|
|
BrokerAclManifest.java:49 public enum Operation {
|
|
BrokerAclManifest.java:51 WRITE(false),
|
|
BrokerAclManifest.java:53 READ(false),
|
|
BrokerAclManifest.java:55 DESCRIBE(false),
|
|
BrokerAclManifest.java:57 CREATE(false),
|
|
BrokerAclManifest.java:59 ALTER(true),
|
|
BrokerAclManifest.java:61 DELETE(true),
|
|
BrokerAclManifest.java:63 PURGE(true);
|
|
|
|
# 기동 시점 검증으로 감싸인 프로파일은 셋이다
|
|
KafkaMessagingAutoConfiguration.java:59 return new StartupProfileValidation<>(
|
|
KafkaMessagingAutoConfiguration.java:60 () ->
|
|
KafkaMessagingAutoConfiguration.java:61 java.util.stream.Stream.concat(
|
|
KafkaMessagingAutoConfiguration.java:62 compiled.kafkaBrokers().stream(), profiles.orderedStream())
|
|
MessagingCoreAutoConfiguration.java:221 return new StartupProfileValidation<>(
|
|
MessagingCoreAutoConfiguration.java:222 () ->
|
|
MessagingCoreAutoConfiguration.java:223 java.util.stream.Stream.concat(compiled.security().stream(), profiles.orderedStream())
|
|
MessagingCoreAutoConfiguration.java:224 .toList(),
|
|
RabbitMessagingAutoConfiguration.java:55 return new StartupProfileValidation<>(
|
|
RabbitMessagingAutoConfiguration.java:56 () ->
|
|
RabbitMessagingAutoConfiguration.java:57 java.util.stream.Stream.concat(
|
|
RabbitMessagingAutoConfiguration.java:58 compiled.rabbitBrokers().stream(), profiles.orderedStream())
|
|
|
|
# 파괴 연산 자체를 막는 다른 장치
|
|
main · DestructiveMessagingAdmin.java:21 public interface DestructiveMessagingAdmin {
|
|
main · MessagingAdminService.java:21 * <p>Destructive operations live in {@link DestructiveMessagingAdmin}, a separate interface that an
|
|
main · MessagingAdminAutoConfiguration.java:22 * <p>{@link dev.caskeleton.messaging.admin.runtime.DestructiveMessagingAdmin} is deliberately
|
|
그 인터페이스를 구현하는 main 클래스 : 0 건
|
|
|
|
# 브로커에 권한을 질의하는 코드
|
|
describeAcls 0 파일
|
|
DescribeAclsResult 0 파일
|
|
AclBinding 0 파일
|
|
createAcls 0 파일
|
|
AclOperation 0 파일
|
|
Authorizer 0 파일
|
|
listPermissions 0 파일
|
|
checkPermissions 0 파일
|
|
그 0 들의 의미를 정하는 사실 — Kafka AdminClient 자체의 소스 세트별 분포 :
|
|
test : 5 파일
|