Files
CLIProxyAPI/sdk/cliproxy/auth/classification.go
Luis Pater 70053beadb feat(auth): refactor credential kind detection and add dynamic source classification
- Introduced `AuthKind` and `AuthSourceKind` methods for improved credential type and source classification.
- Replaced legacy fallback logic with normalized attribute-based handling in `AuthKind`.
- Consolidated metadata inspection for both API key and OAuth attributes.
- Updated calls to replace direct `AccountInfo` usage with `AuthKind` and `AuthSourceKind`.
- Enhanced unit tests to validate explicit and fallback credential resolution scenarios.
2026-06-23 23:21:33 +08:00

139 lines
3.4 KiB
Go

package auth
import "strings"
const (
AuthKindAPIKey = "apikey"
AuthKindOAuth = "oauth"
AuthSourceConfig = "config"
AuthSourceFile = "file"
AuthSourceGit = "git"
AuthSourceMemory = "memory"
AuthSourceObjectStore = "objectstore"
AuthSourcePostgres = "postgres"
AttributeAPIKey = "api_key"
AttributeAuthKind = "auth_kind"
AttributePath = "path"
AttributeRuntimeOnly = "runtime_only"
AttributeSource = "source"
AttributeSourceBackend = "source_backend"
)
// AuthKind returns the credential kind using explicit metadata first and legacy
// field-shape fallbacks second.
func (a *Auth) AuthKind() string {
if a == nil {
return ""
}
if kind := normalizeAuthKind(authAttribute(a, AttributeAuthKind)); kind != "" {
return kind
}
if kind := normalizeAuthKind(authMetadataString(a, AttributeAuthKind)); kind != "" {
return kind
}
if authAttribute(a, AttributeAPIKey) != "" {
return AuthKindAPIKey
}
if authHasOAuthMetadata(a) {
return AuthKindOAuth
}
return ""
}
// AuthSourceKind returns where the Auth entry came from at runtime.
func (a *Auth) AuthSourceKind() string {
if a == nil {
return ""
}
if strings.EqualFold(authAttribute(a, AttributeRuntimeOnly), "true") {
return AuthSourceMemory
}
if source := normalizeAuthSourceKind(authAttribute(a, AttributeSourceBackend)); source != "" {
return source
}
source := authAttribute(a, AttributeSource)
if source != "" {
sourceLower := strings.ToLower(source)
if strings.HasPrefix(sourceLower, AuthSourceConfig+":") {
return AuthSourceConfig
}
if normalized := normalizeAuthSourceKind(source); normalized != "" {
return normalized
}
return AuthSourceFile
}
if authAttribute(a, AttributePath) != "" {
return AuthSourceFile
}
if strings.TrimSpace(a.FileName) != "" {
return AuthSourceFile
}
return ""
}
func normalizeAuthKind(kind string) string {
switch strings.ToLower(strings.TrimSpace(kind)) {
case AuthKindAPIKey, "api_key", "api-key":
return AuthKindAPIKey
case AuthKindOAuth, "oauth2":
return AuthKindOAuth
default:
return ""
}
}
func normalizeAuthSourceKind(source string) string {
switch strings.ToLower(strings.TrimSpace(source)) {
case AuthSourceConfig:
return AuthSourceConfig
case AuthSourceFile, "filesystem":
return AuthSourceFile
case AuthSourceGit:
return AuthSourceGit
case AuthSourceMemory, "runtime", "runtime_only":
return AuthSourceMemory
case AuthSourceObjectStore, "object-store":
return AuthSourceObjectStore
case AuthSourcePostgres, "postgresql", "database", "db":
return AuthSourcePostgres
default:
return ""
}
}
func authHasOAuthMetadata(auth *Auth) bool {
if auth == nil || len(auth.Metadata) == 0 {
return false
}
for _, key := range []string{"access_token", "refresh_token", "id_token", "email", "token_type", "expires_at", "expired"} {
if authMetadataString(auth, key) != "" {
return true
}
}
if token, ok := auth.Metadata["token"].(map[string]any); ok && len(token) > 0 {
return true
}
return false
}
func authAttribute(auth *Auth, key string) string {
if auth == nil || auth.Attributes == nil {
return ""
}
return strings.TrimSpace(auth.Attributes[key])
}
func authMetadataString(auth *Auth, key string) string {
if auth == nil || auth.Metadata == nil {
return ""
}
switch value := auth.Metadata[key].(type) {
case string:
return strings.TrimSpace(value)
default:
return ""
}
}