feat(auth): wire JWT validation into /v1/* LLM endpoints
This commit is contained in:
+46
-20
@@ -15,6 +15,16 @@ type rawConfig struct {
|
||||
Routes []rawRoute `yaml:"routes"`
|
||||
Models []rawModel `yaml:"models"`
|
||||
Adapters []rawAdapter `yaml:"adapters"`
|
||||
Auth rawAuth `yaml:"auth"`
|
||||
}
|
||||
|
||||
// rawAuth represents auth configuration in YAML.
|
||||
type rawAuth struct {
|
||||
Enabled bool `yaml:"enabled"`
|
||||
Issuer string `yaml:"issuer"`
|
||||
Audience string `yaml:"audience"`
|
||||
JWKSURL string `yaml:"jwksUrl"`
|
||||
RequiredCapability string `yaml:"requiredCapability"`
|
||||
}
|
||||
|
||||
// rawRoute represents a single route in the YAML configuration.
|
||||
@@ -25,9 +35,10 @@ type rawRoute struct {
|
||||
|
||||
// rawModel represents a single model entry in the YAML configuration.
|
||||
type rawModel struct {
|
||||
Name string `yaml:"name"`
|
||||
Address string `yaml:"address"`
|
||||
Path string `yaml:"path"`
|
||||
Name string `yaml:"name"`
|
||||
Address string `yaml:"address"`
|
||||
Path string `yaml:"path"`
|
||||
AuthRequired *bool `yaml:"authRequired"`
|
||||
}
|
||||
|
||||
// rawAdapter represents a service adapter in the YAML configuration.
|
||||
@@ -64,32 +75,32 @@ type rawUpstream struct {
|
||||
AuthRequired *bool `yaml:"authRequired"`
|
||||
}
|
||||
|
||||
// LoadRoutesAndModelsFromFile loads route, model, and adapter configuration from a YAML file.
|
||||
func LoadRoutesAndModelsFromFile(path string) (map[string]*Route, map[string]*ModelUpstream, []*serviceadapter.ServiceAdapter, error) {
|
||||
// LoadRoutesAndModelsFromFile loads route, model, adapter, and auth configuration from a YAML file.
|
||||
func LoadRoutesAndModelsFromFile(path string) (map[string]*Route, map[string]*ModelUpstream, []*serviceadapter.ServiceAdapter, AuthConfig, error) {
|
||||
data, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return nil, nil, nil, fmt.Errorf("failed to read config file %q: %w", path, err)
|
||||
return nil, nil, nil, AuthConfig{}, fmt.Errorf("failed to read config file %q: %w", path, err)
|
||||
}
|
||||
|
||||
var raw rawConfig
|
||||
if err := yaml.Unmarshal(data, &raw); err != nil {
|
||||
return nil, nil, nil, fmt.Errorf("failed to parse config file %q: %w", path, err)
|
||||
return nil, nil, nil, AuthConfig{}, fmt.Errorf("failed to parse config file %q: %w", path, err)
|
||||
}
|
||||
|
||||
// Load routes
|
||||
routes := make(map[string]*Route)
|
||||
for _, rawRoute := range raw.Routes {
|
||||
if rawRoute.Name == "" {
|
||||
return nil, nil, nil, fmt.Errorf("route has empty name")
|
||||
return nil, nil, nil, AuthConfig{}, fmt.Errorf("route has empty name")
|
||||
}
|
||||
|
||||
if _, exists := routes[rawRoute.Name]; exists {
|
||||
return nil, nil, nil, fmt.Errorf("duplicate route: \"%s\"", rawRoute.Name)
|
||||
return nil, nil, nil, AuthConfig{}, fmt.Errorf("duplicate route: \"%s\"", rawRoute.Name)
|
||||
}
|
||||
|
||||
upstream, err := parseUpstream(rawRoute.Name, rawRoute.Upstream)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
return nil, nil, nil, AuthConfig{}, err
|
||||
}
|
||||
|
||||
routes[rawRoute.Name] = &Route{
|
||||
@@ -102,21 +113,27 @@ func LoadRoutesAndModelsFromFile(path string) (map[string]*Route, map[string]*Mo
|
||||
models := make(map[string]*ModelUpstream)
|
||||
for _, rawModel := range raw.Models {
|
||||
if rawModel.Name == "" {
|
||||
return nil, nil, nil, fmt.Errorf("model has empty name")
|
||||
return nil, nil, nil, AuthConfig{}, fmt.Errorf("model has empty name")
|
||||
}
|
||||
if _, exists := models[rawModel.Name]; exists {
|
||||
return nil, nil, nil, fmt.Errorf("duplicate model: \"%s\"", rawModel.Name)
|
||||
return nil, nil, nil, AuthConfig{}, fmt.Errorf("duplicate model: \"%s\"", rawModel.Name)
|
||||
}
|
||||
if rawModel.Address == "" {
|
||||
return nil, nil, nil, fmt.Errorf("model \"%s\": field 'address' is required", rawModel.Name)
|
||||
return nil, nil, nil, AuthConfig{}, fmt.Errorf("model \"%s\": field 'address' is required", rawModel.Name)
|
||||
}
|
||||
if _, _, err := net.SplitHostPort(rawModel.Address); err != nil {
|
||||
return nil, nil, nil, fmt.Errorf("model \"%s\": invalid address \"%s\": %w", rawModel.Name, rawModel.Address, err)
|
||||
return nil, nil, nil, AuthConfig{}, fmt.Errorf("model \"%s\": invalid address \"%s\": %w", rawModel.Name, rawModel.Address, err)
|
||||
}
|
||||
// Default authRequired to global auth.enabled if not specified per-model
|
||||
authRequired := false
|
||||
if rawModel.AuthRequired != nil {
|
||||
authRequired = *rawModel.AuthRequired
|
||||
}
|
||||
models[rawModel.Name] = &ModelUpstream{
|
||||
Name: rawModel.Name,
|
||||
Address: rawModel.Address,
|
||||
Path: rawModel.Path,
|
||||
Name: rawModel.Name,
|
||||
Address: rawModel.Address,
|
||||
Path: rawModel.Path,
|
||||
AuthRequired: authRequired,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -124,7 +141,7 @@ func LoadRoutesAndModelsFromFile(path string) (map[string]*Route, map[string]*Mo
|
||||
adapters := make([]*serviceadapter.ServiceAdapter, 0, len(raw.Adapters))
|
||||
for _, ra := range raw.Adapters {
|
||||
if ra.ServiceName == "" {
|
||||
return nil, nil, nil, fmt.Errorf("adapter has empty serviceName")
|
||||
return nil, nil, nil, AuthConfig{}, fmt.Errorf("adapter has empty serviceName")
|
||||
}
|
||||
a := &serviceadapter.ServiceAdapter{
|
||||
Name: ra.ServiceName,
|
||||
@@ -152,13 +169,22 @@ func LoadRoutesAndModelsFromFile(path string) (map[string]*Route, map[string]*Mo
|
||||
adapters = append(adapters, a)
|
||||
}
|
||||
|
||||
return routes, models, adapters, nil
|
||||
// Parse auth config
|
||||
authConfig := AuthConfig{
|
||||
Enabled: raw.Auth.Enabled,
|
||||
Issuer: raw.Auth.Issuer,
|
||||
Audience: raw.Auth.Audience,
|
||||
JWKSURL: raw.Auth.JWKSURL,
|
||||
RequiredCapability: raw.Auth.RequiredCapability,
|
||||
}
|
||||
|
||||
return routes, models, adapters, authConfig, nil
|
||||
}
|
||||
|
||||
// LoadRoutesFromFile loads route configuration from a YAML file.
|
||||
// Deprecated: Use LoadRoutesAndModelsFromFile instead.
|
||||
func LoadRoutesFromFile(path string) (map[string]*Route, error) {
|
||||
routes, _, _, err := LoadRoutesAndModelsFromFile(path)
|
||||
routes, _, _, _, err := LoadRoutesAndModelsFromFile(path)
|
||||
return routes, err
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user