iop/apps/edge/internal/openai/principal.go

143 lines
4.6 KiB
Go

package openai
import (
"context"
"crypto/sha256"
"crypto/subtle"
"encoding/hex"
"net/http"
"strings"
"iop/packages/go/config"
)
// Principal source tags. These label how an openAIPrincipal was resolved and
// are surfaced in dispatch metadata for usage/audit purposes.
const (
principalSourceToken = "principal_token"
principalSourceLegacy = "legacy_bearer_token"
principalSourceUnknown = "unknown"
)
// Dispatch metadata keys carrying the resolved principal. These are set from
// the authenticated request context only; metadata.user from the caller's
// request body is never used as an identity source.
const (
principalMetaRef = "iop_principal_ref"
principalMetaAlias = "iop_principal_alias"
principalMetaToken = "iop_token_ref"
principalMetaSource = "iop_principal_source"
)
// openAIPrincipal is the caller identity resolved from an OpenAI-compatible
// bearer token via IOP bearer-token-operation mapping. It never carries the
// raw token value.
type openAIPrincipal struct {
PrincipalRef string
PrincipalAlias string
TokenRef string
Source string
}
type principalContextKey struct{}
// withPrincipal returns a context carrying the resolved caller principal.
func withPrincipal(ctx context.Context, p openAIPrincipal) context.Context {
return context.WithValue(ctx, principalContextKey{}, p)
}
// principalFromContext returns the caller principal stored by withAuth, if
// any.
func principalFromContext(ctx context.Context) (openAIPrincipal, bool) {
p, ok := ctx.Value(principalContextKey{}).(openAIPrincipal)
return p, ok
}
// principalMetadata renders the request context's principal as dispatch
// metadata. It returns nil when no principal was resolved for the context.
func principalMetadata(ctx context.Context) map[string]string {
p, ok := principalFromContext(ctx)
if !ok {
return nil
}
meta := map[string]string{principalMetaSource: p.Source}
if p.PrincipalRef != "" {
meta[principalMetaRef] = p.PrincipalRef
}
if p.PrincipalAlias != "" {
meta[principalMetaAlias] = p.PrincipalAlias
}
if p.TokenRef != "" {
meta[principalMetaToken] = p.TokenRef
}
return meta
}
// bearerTokenFromHeader extracts the raw bearer token value from an
// Authorization header, or "" if the header is missing or malformed.
func bearerTokenFromHeader(authHeader string) string {
const prefix = "Bearer "
if !strings.HasPrefix(authHeader, prefix) {
return ""
}
return strings.TrimPrefix(authHeader, prefix)
}
// resolvePrincipal authenticates an OpenAI-compatible bearer token against
// cfg.PrincipalTokens (hash mapping, checked first) and cfg.BearerToken
// (legacy fallback). It returns the resolved principal and whether the
// caller is authenticated. When neither principal_tokens nor bearer_token is
// configured, the endpoint is open: authenticated=true with an
// unknown-source principal, matching the pre-existing no-auth default.
func resolvePrincipal(cfg config.EdgeOpenAIConf, authHeader string) (openAIPrincipal, bool) {
hasPrincipalTokens := len(cfg.PrincipalTokens) > 0
hasLegacyToken := strings.TrimSpace(cfg.BearerToken) != ""
if !hasPrincipalTokens && !hasLegacyToken {
return openAIPrincipal{Source: principalSourceUnknown}, true
}
token := bearerTokenFromHeader(authHeader)
if token == "" {
return openAIPrincipal{}, false
}
if hasPrincipalTokens {
if p, ok := matchPrincipalToken(cfg.PrincipalTokens, token); ok {
return p, true
}
}
if hasLegacyToken {
expected := []byte(cfg.BearerToken)
if subtle.ConstantTimeCompare([]byte(token), expected) == 1 {
return openAIPrincipal{Source: principalSourceLegacy}, true
}
}
return openAIPrincipal{}, false
}
// matchPrincipalToken hashes token and compares it against each configured
// entry's stored hash using a constant-time comparison per candidate.
func matchPrincipalToken(tokens []config.OpenAIPrincipalTokenConf, token string) (openAIPrincipal, bool) {
sum := sha256.Sum256([]byte(token))
hash := hex.EncodeToString(sum[:])
for _, t := range tokens {
configured := strings.ToLower(strings.TrimSpace(t.TokenHashSHA256))
if subtle.ConstantTimeCompare([]byte(hash), []byte(configured)) == 1 {
return openAIPrincipal{
PrincipalRef: t.PrincipalRef,
PrincipalAlias: t.PrincipalAlias,
TokenRef: t.TokenRef,
Source: principalSourceToken,
}, true
}
}
return openAIPrincipal{}, false
}
// principalFromRequest resolves r's caller principal from its Authorization
// header against cfg.
func principalFromRequest(r *http.Request, cfg config.EdgeOpenAIConf) (openAIPrincipal, bool) {
return resolvePrincipal(cfg, r.Header.Get("Authorization"))
}