mirror of
https://github.com/luxfi/kms.git
synced 2026-07-27 05:54:18 +00:00
auth: neutral example org identifiers
This commit is contained in:
@@ -18,7 +18,7 @@ One BIP39 mnemonic is shared across all Lux-derived L1s (Lux, Hanzo,
|
||||
Zoo, Pars). Each chain reads it from `providers/<org>/deploy-mnemonic`
|
||||
under its own org-scoped JWT — same bytes, different KMS paths, N
|
||||
independent auth boundaries. Each tenant is jurisdictionally separate
|
||||
and holds its own mnemonic in `providers/liquidity/*`. See
|
||||
and holds its own mnemonic in `providers/<org>/*`. See
|
||||
`~/work/lux/CLAUDE.md` §"Mnemonic + Key Derivation" for the
|
||||
canonical reference (paths, derivation formula, IAM apps required).
|
||||
|
||||
|
||||
+3
-3
@@ -35,12 +35,12 @@ import (
|
||||
// against. The org binding follows two flavors of IAM JWT:
|
||||
//
|
||||
// 1. User tokens (Google OAuth, password) — `owner` IS the org slug.
|
||||
// Example: a user signs in to organization=liquidity, the JWT
|
||||
// carries owner="liquidity", name="<username>".
|
||||
// Example: a user signs in to organization=org1, the JWT
|
||||
// carries owner="org1", name="<username>".
|
||||
// 2. Application tokens (client_credentials) — `owner`="admin"
|
||||
// (the parent record), and the application's `name` carries the
|
||||
// org as a prefix per the documented `<org>-<service>` naming
|
||||
// convention. Example: kms-app for liquidity is name="kms",
|
||||
// convention. Example: kms-app for org1 is name="org1-kms",
|
||||
// owner="admin", type="application".
|
||||
//
|
||||
// `roles` carries the IAM role list (kms-admin override).
|
||||
|
||||
+26
-26
@@ -42,7 +42,7 @@ func TestRequireOrgJWT_missingBearer(t *testing.T) {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}))
|
||||
|
||||
req := httptest.NewRequest(http.MethodGet, "/v1/kms/orgs/liquidity/secrets/iam/X", nil)
|
||||
req := httptest.NewRequest(http.MethodGet, "/v1/kms/orgs/org1/secrets/iam/X", nil)
|
||||
rec := httptest.NewRecorder()
|
||||
mux.ServeHTTP(rec, req)
|
||||
if rec.Code != http.StatusUnauthorized {
|
||||
@@ -57,7 +57,7 @@ func TestRequireOrgJWT_invalidBearerShape(t *testing.T) {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}))
|
||||
|
||||
req := httptest.NewRequest(http.MethodGet, "/v1/kms/orgs/liquidity/secrets/iam/X", nil)
|
||||
req := httptest.NewRequest(http.MethodGet, "/v1/kms/orgs/org1/secrets/iam/X", nil)
|
||||
req.Header.Set("Authorization", "Token abc")
|
||||
rec := httptest.NewRecorder()
|
||||
mux.ServeHTTP(rec, req)
|
||||
@@ -79,7 +79,7 @@ func TestRequireOrgJWT_validTokenForCorrectOrg(t *testing.T) {
|
||||
Subject: "user-z",
|
||||
Expiry: jwt.NewNumericDate(time.Now().Add(time.Hour)),
|
||||
},
|
||||
Owner: "liquidity", // user-token case: owner IS the org
|
||||
Owner: "org1", // user-token case: owner IS the org
|
||||
})
|
||||
|
||||
called := false
|
||||
@@ -89,7 +89,7 @@ func TestRequireOrgJWT_validTokenForCorrectOrg(t *testing.T) {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}))
|
||||
|
||||
req := httptest.NewRequest(http.MethodGet, "/v1/kms/orgs/liquidity/secrets/iam/X", nil)
|
||||
req := httptest.NewRequest(http.MethodGet, "/v1/kms/orgs/org1/secrets/iam/X", nil)
|
||||
req.Header.Set("Authorization", "Bearer "+tok)
|
||||
rec := httptest.NewRecorder()
|
||||
mux.ServeHTTP(rec, req)
|
||||
@@ -108,14 +108,14 @@ func TestRequireOrgJWT_crossOrgRejected(t *testing.T) {
|
||||
|
||||
auth := newOrgJWTAuth(iam.URL, "")
|
||||
|
||||
// Token for org=mlc, request for org=liquidity → 403.
|
||||
// Token for org=org2, request for org=org1 → 403.
|
||||
tok := signOrgClaims(t, signer, orgClaims{
|
||||
Claims: jwt.Claims{
|
||||
Issuer: iam.URL,
|
||||
Subject: "mlc-app",
|
||||
Subject: "org2-app",
|
||||
Expiry: jwt.NewNumericDate(time.Now().Add(time.Hour)),
|
||||
},
|
||||
Owner: "mlc",
|
||||
Owner: "org2",
|
||||
})
|
||||
|
||||
mux := http.NewServeMux()
|
||||
@@ -123,7 +123,7 @@ func TestRequireOrgJWT_crossOrgRejected(t *testing.T) {
|
||||
t.Fatal("inner handler must NOT be called for cross-org token")
|
||||
}))
|
||||
|
||||
req := httptest.NewRequest(http.MethodGet, "/v1/kms/orgs/liquidity/secrets/iam/X", nil)
|
||||
req := httptest.NewRequest(http.MethodGet, "/v1/kms/orgs/org1/secrets/iam/X", nil)
|
||||
req.Header.Set("Authorization", "Bearer "+tok)
|
||||
rec := httptest.NewRecorder()
|
||||
mux.ServeHTTP(rec, req)
|
||||
@@ -155,7 +155,7 @@ func TestRequireOrgJWT_kmsAdminCrossesOrgs(t *testing.T) {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}))
|
||||
|
||||
req := httptest.NewRequest(http.MethodGet, "/v1/kms/orgs/liquidity/secrets/iam/X", nil)
|
||||
req := httptest.NewRequest(http.MethodGet, "/v1/kms/orgs/org1/secrets/iam/X", nil)
|
||||
req.Header.Set("Authorization", "Bearer "+tok)
|
||||
rec := httptest.NewRecorder()
|
||||
mux.ServeHTTP(rec, req)
|
||||
@@ -177,7 +177,7 @@ func TestRequireOrgJWT_expiredRejected(t *testing.T) {
|
||||
Subject: "old",
|
||||
Expiry: jwt.NewNumericDate(time.Now().Add(-time.Hour)),
|
||||
},
|
||||
Owner: "liquidity",
|
||||
Owner: "org1",
|
||||
})
|
||||
|
||||
mux := http.NewServeMux()
|
||||
@@ -185,7 +185,7 @@ func TestRequireOrgJWT_expiredRejected(t *testing.T) {
|
||||
t.Fatal("must reject expired token")
|
||||
}))
|
||||
|
||||
req := httptest.NewRequest(http.MethodGet, "/v1/kms/orgs/liquidity/secrets/iam/X", nil)
|
||||
req := httptest.NewRequest(http.MethodGet, "/v1/kms/orgs/org1/secrets/iam/X", nil)
|
||||
req.Header.Set("Authorization", "Bearer "+tok)
|
||||
rec := httptest.NewRecorder()
|
||||
mux.ServeHTTP(rec, req)
|
||||
@@ -208,7 +208,7 @@ func TestRequireOrgJWT_wrongIssuerRejected(t *testing.T) {
|
||||
Subject: "u",
|
||||
Expiry: jwt.NewNumericDate(time.Now().Add(time.Hour)),
|
||||
},
|
||||
Owner: "liquidity",
|
||||
Owner: "org1",
|
||||
})
|
||||
|
||||
mux := http.NewServeMux()
|
||||
@@ -216,7 +216,7 @@ func TestRequireOrgJWT_wrongIssuerRejected(t *testing.T) {
|
||||
t.Fatal("must reject wrong issuer")
|
||||
}))
|
||||
|
||||
req := httptest.NewRequest(http.MethodGet, "/v1/kms/orgs/liquidity/secrets/iam/X", nil)
|
||||
req := httptest.NewRequest(http.MethodGet, "/v1/kms/orgs/org1/secrets/iam/X", nil)
|
||||
req.Header.Set("Authorization", "Bearer "+tok)
|
||||
rec := httptest.NewRecorder()
|
||||
mux.ServeHTTP(rec, req)
|
||||
@@ -238,11 +238,11 @@ func TestRequireOrgJWT_applicationToken_orgFromName(t *testing.T) {
|
||||
tok := signOrgClaims(t, signer, orgClaims{
|
||||
Claims: jwt.Claims{
|
||||
Issuer: iam.URL,
|
||||
Subject: "admin/kms",
|
||||
Subject: "admin/org1-kms",
|
||||
Expiry: jwt.NewNumericDate(time.Now().Add(time.Hour)),
|
||||
},
|
||||
Owner: "admin",
|
||||
Name: "kms",
|
||||
Name: "org1-kms",
|
||||
Type: "application",
|
||||
})
|
||||
|
||||
@@ -251,7 +251,7 @@ func TestRequireOrgJWT_applicationToken_orgFromName(t *testing.T) {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}))
|
||||
|
||||
req := httptest.NewRequest(http.MethodGet, "/v1/kms/orgs/liquidity/secrets/iam/X", nil)
|
||||
req := httptest.NewRequest(http.MethodGet, "/v1/kms/orgs/org1/secrets/iam/X", nil)
|
||||
req.Header.Set("Authorization", "Bearer "+tok)
|
||||
rec := httptest.NewRecorder()
|
||||
mux.ServeHTTP(rec, req)
|
||||
@@ -260,7 +260,7 @@ func TestRequireOrgJWT_applicationToken_orgFromName(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// An application named "kms" must NOT be able to read mlc's
|
||||
// An application named "org1-kms" must NOT be able to read org2's
|
||||
// secrets — the name prefix is the only org binding, no fall-through
|
||||
// to other orgs.
|
||||
func TestRequireOrgJWT_applicationToken_crossOrgRejected(t *testing.T) {
|
||||
@@ -273,11 +273,11 @@ func TestRequireOrgJWT_applicationToken_crossOrgRejected(t *testing.T) {
|
||||
tok := signOrgClaims(t, signer, orgClaims{
|
||||
Claims: jwt.Claims{
|
||||
Issuer: iam.URL,
|
||||
Subject: "admin/kms",
|
||||
Subject: "admin/org1-kms",
|
||||
Expiry: jwt.NewNumericDate(time.Now().Add(time.Hour)),
|
||||
},
|
||||
Owner: "admin",
|
||||
Name: "kms",
|
||||
Name: "org1-kms",
|
||||
Type: "application",
|
||||
})
|
||||
|
||||
@@ -286,7 +286,7 @@ func TestRequireOrgJWT_applicationToken_crossOrgRejected(t *testing.T) {
|
||||
t.Fatal("must reject app token reaching across orgs")
|
||||
}))
|
||||
|
||||
req := httptest.NewRequest(http.MethodGet, "/v1/kms/orgs/mlc/secrets/iam/X", nil)
|
||||
req := httptest.NewRequest(http.MethodGet, "/v1/kms/orgs/org2/secrets/iam/X", nil)
|
||||
req.Header.Set("Authorization", "Bearer "+tok)
|
||||
rec := httptest.NewRecorder()
|
||||
mux.ServeHTTP(rec, req)
|
||||
@@ -313,7 +313,7 @@ func TestRequireOrgJWT_tagPicksOrg(t *testing.T) {
|
||||
Owner: "admin",
|
||||
Name: "some-app",
|
||||
Type: "application",
|
||||
Tag: "liquidity",
|
||||
Tag: "org1",
|
||||
})
|
||||
|
||||
mux := http.NewServeMux()
|
||||
@@ -321,7 +321,7 @@ func TestRequireOrgJWT_tagPicksOrg(t *testing.T) {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}))
|
||||
|
||||
req := httptest.NewRequest(http.MethodGet, "/v1/kms/orgs/liquidity/secrets/iam/X", nil)
|
||||
req := httptest.NewRequest(http.MethodGet, "/v1/kms/orgs/org1/secrets/iam/X", nil)
|
||||
req.Header.Set("Authorization", "Bearer "+tok)
|
||||
rec := httptest.NewRecorder()
|
||||
mux.ServeHTTP(rec, req)
|
||||
@@ -352,7 +352,7 @@ func TestRequireOrgJWT_missingOwnerClaimRejected(t *testing.T) {
|
||||
t.Fatal("must reject token with no resolvable org")
|
||||
}))
|
||||
|
||||
req := httptest.NewRequest(http.MethodGet, "/v1/kms/orgs/liquidity/secrets/iam/X", nil)
|
||||
req := httptest.NewRequest(http.MethodGet, "/v1/kms/orgs/org1/secrets/iam/X", nil)
|
||||
req.Header.Set("Authorization", "Bearer "+tok)
|
||||
rec := httptest.NewRecorder()
|
||||
mux.ServeHTTP(rec, req)
|
||||
@@ -376,11 +376,11 @@ func TestRequireOrgJWT_splitJwksAndIssuer(t *testing.T) {
|
||||
tok := signOrgClaims(t, signer, orgClaims{
|
||||
Claims: jwt.Claims{
|
||||
Issuer: publicIssuer, // minted with public host
|
||||
Subject: "admin/kms",
|
||||
Subject: "admin/org1-kms",
|
||||
Expiry: jwt.NewNumericDate(time.Now().Add(time.Hour)),
|
||||
},
|
||||
Owner: "admin",
|
||||
Name: "kms",
|
||||
Name: "org1-kms",
|
||||
Type: "application",
|
||||
})
|
||||
|
||||
@@ -389,7 +389,7 @@ func TestRequireOrgJWT_splitJwksAndIssuer(t *testing.T) {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}))
|
||||
|
||||
req := httptest.NewRequest(http.MethodGet, "/v1/kms/orgs/liquidity/secrets/iam/X", nil)
|
||||
req := httptest.NewRequest(http.MethodGet, "/v1/kms/orgs/org1/secrets/iam/X", nil)
|
||||
req.Header.Set("Authorization", "Bearer "+tok)
|
||||
rec := httptest.NewRecorder()
|
||||
mux.ServeHTTP(rec, req)
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1024" height="1024" viewBox="0 0 1024 1024">
|
||||
<defs>
|
||||
<linearGradient id="lqdtyGrad" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<linearGradient id="brandGrad" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#3B82F6"/>
|
||||
<stop offset="100%" style="stop-color:#1D4ED8"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<rect width="1024" height="1024" rx="200" fill="url(#lqdtyGrad)"/>
|
||||
<rect width="1024" height="1024" rx="200" fill="url(#brandGrad)"/>
|
||||
<path d="M 220 220 L 220 720 L 680 720 L 680 620 L 330 620 L 330 220 Z" fill="white"/>
|
||||
<path d="M 400 500 Q 500 420 600 500 Q 700 580 800 500" stroke="rgba(255,255,255,0.4)" stroke-width="12" fill="none" stroke-linecap="round"/>
|
||||
<path d="M 400 560 Q 500 480 600 560 Q 700 640 800 560" stroke="rgba(255,255,255,0.25)" stroke-width="8" fill="none" stroke-linecap="round"/>
|
||||
|
||||
|
Before Width: | Height: | Size: 765 B After Width: | Height: | Size: 765 B |
Reference in New Issue
Block a user