* Introduce Superadmin field in OAuthClient configuration * Implement IsSuperadmin method in OAuthRegistry * Update identityAdmin to check superadmin status via OAuthRegistry * Add tests for OAuthRegistry superadmin functionality
This commit is contained in:
@@ -31,6 +31,7 @@ auth:
|
|||||||
- id: "oauth-client"
|
- id: "oauth-client"
|
||||||
client_id: ""
|
client_id: ""
|
||||||
client_secret: ""
|
client_secret: ""
|
||||||
|
superadmin: false
|
||||||
description: "optional OAuth client credentials"
|
description: "optional OAuth client credentials"
|
||||||
|
|
||||||
database:
|
database:
|
||||||
|
|||||||
@@ -19,11 +19,19 @@ import (
|
|||||||
type identityAdmin struct {
|
type identityAdmin struct {
|
||||||
pool *pgxpool.Pool
|
pool *pgxpool.Pool
|
||||||
keyring *auth.Keyring
|
keyring *auth.Keyring
|
||||||
|
oauthRegistry *auth.OAuthRegistry
|
||||||
logger *slog.Logger
|
logger *slog.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
func newIdentityAdmin(pool *pgxpool.Pool, keyring *auth.Keyring, logger *slog.Logger) *identityAdmin {
|
func newIdentityAdmin(pool *pgxpool.Pool, keyring *auth.Keyring, oauthRegistry *auth.OAuthRegistry, logger *slog.Logger) *identityAdmin {
|
||||||
return &identityAdmin{pool: pool, keyring: keyring, logger: logger}
|
return &identityAdmin{pool: pool, keyring: keyring, oauthRegistry: oauthRegistry, logger: logger}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *identityAdmin) isSuperadmin(keyID string) bool {
|
||||||
|
if a.keyring != nil && a.keyring.IsSuperadmin(keyID) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return a.oauthRegistry != nil && a.oauthRegistry.IsSuperadmin(keyID)
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadIdentityKeyring(ctx context.Context, pool *pgxpool.Pool, keyring *auth.Keyring) error {
|
func loadIdentityKeyring(ctx context.Context, pool *pgxpool.Pool, keyring *auth.Keyring) error {
|
||||||
@@ -71,7 +79,7 @@ func (a *identityAdmin) handler() http.Handler {
|
|||||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
if r.Method != http.MethodGet {
|
if r.Method != http.MethodGet {
|
||||||
keyID, ok := auth.KeyIDFromContext(r.Context())
|
keyID, ok := auth.KeyIDFromContext(r.Context())
|
||||||
if !ok || a.keyring == nil || !a.keyring.IsSuperadmin(keyID) {
|
if !ok || !a.isSuperadmin(keyID) {
|
||||||
writeJSON(w, http.StatusForbidden, map[string]string{"error": "superadmin API key required"})
|
writeJSON(w, http.StatusForbidden, map[string]string{"error": "superadmin API key required"})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -200,7 +200,7 @@ func routes(logger *slog.Logger, cfg *config.Config, info buildinfo.Info, db *st
|
|||||||
enrichmentRetryer := tools.NewEnrichmentRetryer(context.Background(), db, bgMetadata, cfg.Capture, cfg.AI.Metadata.Timeout, activeProjects, logger)
|
enrichmentRetryer := tools.NewEnrichmentRetryer(context.Background(), db, bgMetadata, cfg.Capture, cfg.AI.Metadata.Timeout, activeProjects, logger)
|
||||||
backfillTool := tools.NewBackfillTool(db, bgEmbeddings, activeProjects, logger)
|
backfillTool := tools.NewBackfillTool(db, bgEmbeddings, activeProjects, logger)
|
||||||
adminActions := newAdminActions(backfillTool, enrichmentRetryer, logger)
|
adminActions := newAdminActions(backfillTool, enrichmentRetryer, logger)
|
||||||
identityAdmin := newIdentityAdmin(db.Pool(), keyring, logger)
|
identityAdmin := newIdentityAdmin(db.Pool(), keyring, oauthRegistry, logger)
|
||||||
|
|
||||||
toolSet := mcpserver.ToolSet{
|
toolSet := mcpserver.ToolSet{
|
||||||
Capture: tools.NewCaptureTool(db, embeddings, cfg.Capture, activeProjects, enrichmentRetryer, backfillTool),
|
Capture: tools.NewCaptureTool(db, embeddings, cfg.Capture, activeProjects, enrichmentRetryer, backfillTool),
|
||||||
|
|||||||
@@ -31,3 +31,18 @@ func (o *OAuthRegistry) Lookup(clientID string, clientSecret string) (string, bo
|
|||||||
}
|
}
|
||||||
return "", false
|
return "", false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsSuperadmin reports whether keyID (as returned by Lookup) belongs to an
|
||||||
|
// OAuth client configured with superadmin: true.
|
||||||
|
func (o *OAuthRegistry) IsSuperadmin(keyID string) bool {
|
||||||
|
for _, client := range o.clients {
|
||||||
|
id := client.ID
|
||||||
|
if id == "" {
|
||||||
|
id = client.ClientID
|
||||||
|
}
|
||||||
|
if id == keyID {
|
||||||
|
return client.Superadmin
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|||||||
@@ -32,6 +32,26 @@ func TestNewOAuthRegistryAndLookup(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestOAuthRegistryIsSuperadmin(t *testing.T) {
|
||||||
|
registry, err := NewOAuthRegistry([]config.OAuthClient{
|
||||||
|
{ID: "oauth-admin", ClientID: "admin-id", ClientSecret: "admin-secret", Superadmin: true},
|
||||||
|
{ID: "oauth-client", ClientID: "client-id", ClientSecret: "client-secret"},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("NewOAuthRegistry() error = %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !registry.IsSuperadmin("oauth-admin") {
|
||||||
|
t.Fatal("IsSuperadmin(oauth-admin) = false, want true")
|
||||||
|
}
|
||||||
|
if registry.IsSuperadmin("oauth-client") {
|
||||||
|
t.Fatal("IsSuperadmin(oauth-client) = true, want false")
|
||||||
|
}
|
||||||
|
if registry.IsSuperadmin("unknown") {
|
||||||
|
t.Fatal("IsSuperadmin(unknown) = true, want false")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestMiddlewareAllowsOAuthBasicAuthAndSetsContext(t *testing.T) {
|
func TestMiddlewareAllowsOAuthBasicAuthAndSetsContext(t *testing.T) {
|
||||||
oauthRegistry, err := NewOAuthRegistry([]config.OAuthClient{{
|
oauthRegistry, err := NewOAuthRegistry([]config.OAuthClient{{
|
||||||
ID: "oauth-client",
|
ID: "oauth-client",
|
||||||
|
|||||||
@@ -66,6 +66,7 @@ type OAuthClient struct {
|
|||||||
ID string `yaml:"id"`
|
ID string `yaml:"id"`
|
||||||
ClientID string `yaml:"client_id"`
|
ClientID string `yaml:"client_id"`
|
||||||
ClientSecret string `yaml:"client_secret"`
|
ClientSecret string `yaml:"client_secret"`
|
||||||
|
Superadmin bool `yaml:"superadmin"`
|
||||||
Description string `yaml:"description"`
|
Description string `yaml:"description"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user