Kyoo/auth/config.go
2025-04-23 22:35:52 +02:00

154 lines
3.4 KiB
Go

package main
import (
"context"
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"encoding/json"
"encoding/pem"
"fmt"
"maps"
"os"
"strings"
"time"
"github.com/golang-jwt/jwt/v5"
"github.com/google/uuid"
"github.com/zoriya/kyoo/keibi/dbc"
)
type Configuration struct {
Prefix string
JwtPrivateKey *rsa.PrivateKey
JwtPublicKey *rsa.PublicKey
PublicUrl string
DefaultClaims jwt.MapClaims
FirstUserClaims jwt.MapClaims
GuestClaims jwt.MapClaims
ProtectedClaims []string
ExpirationDelay time.Duration
EnvApiKeys map[string]ApiKeyWToken
}
var DefaultConfig = Configuration{
DefaultClaims: make(jwt.MapClaims),
FirstUserClaims: make(jwt.MapClaims),
ProtectedClaims: []string{"permissions"},
ExpirationDelay: 30 * 24 * time.Hour,
EnvApiKeys: make(map[string]ApiKeyWToken),
}
func LoadConfiguration(db *dbc.Queries) (*Configuration, error) {
ret := DefaultConfig
ret.PublicUrl = os.Getenv("PUBLIC_URL")
ret.Prefix = os.Getenv("KEIBI_PREFIX")
claims := os.Getenv("EXTRA_CLAIMS")
if claims != "" {
err := json.Unmarshal([]byte(claims), &ret.DefaultClaims)
if err != nil {
return nil, err
}
}
maps.Insert(ret.FirstUserClaims, maps.All(ret.DefaultClaims))
claims = os.Getenv("FIRST_USER_CLAIMS")
if claims != "" {
err := json.Unmarshal([]byte(claims), &ret.FirstUserClaims)
if err != nil {
return nil, err
}
} else {
ret.FirstUserClaims = ret.DefaultClaims
}
claims = os.Getenv("GUEST_CLAIMS")
if claims != "" {
err := json.Unmarshal([]byte(claims), &ret.GuestClaims)
if err != nil {
return nil, err
}
}
protected := strings.Split(os.Getenv("PROTECTED_CLAIMS"), ",")
ret.ProtectedClaims = append(ret.ProtectedClaims, protected...)
rsa_pk_path := os.Getenv("RSA_PRIVATE_KEY_PATH")
if rsa_pk_path != "" {
privateKeyData, err := os.ReadFile(rsa_pk_path)
if err != nil {
return nil, err
}
block, _ := pem.Decode(privateKeyData)
if block == nil || block.Type != "RSA PRIVATE KEY" {
return nil, err
}
ret.JwtPrivateKey, err = x509.ParsePKCS1PrivateKey(block.Bytes)
if err != nil {
pkcs8Key, err := x509.ParsePKCS8PrivateKey(block.Bytes)
if err != nil {
return nil, err
}
ret.JwtPrivateKey = pkcs8Key.(*rsa.PrivateKey)
}
} else {
var err error
ret.JwtPrivateKey, err = rsa.GenerateKey(rand.Reader, 4096)
if err != nil {
return nil, err
}
ret.JwtPublicKey = &ret.JwtPrivateKey.PublicKey
}
for _, env := range os.Environ() {
if !strings.HasPrefix(env, "KEIBI_APIKEY_"){
continue
}
v := strings.Split(env, "=")
if strings.HasSuffix(v[0], "_CLAIMS") {
continue
}
name := strings.TrimPrefix(v[0], "KEIBI_APIKEY_")
cstr := os.Getenv(fmt.Sprintf("KEIBI_APIKEY_%s_CLAIMS", name))
var claims jwt.MapClaims
if cstr != "" {
err := json.Unmarshal([]byte(cstr), &claims)
if err != nil {
return nil, err
}
} else {
return nil, fmt.Errorf("missing claims env var KEIBI_APIKEY_%s_CLAIMS", name)
}
name = strings.ToLower(name)
ret.EnvApiKeys[name] = ApiKeyWToken{
ApiKey: ApiKey{
Id: uuid.New(),
Name: name,
Claims: claims,
},
Token: v[1],
}
}
apikeys, err := db.ListApiKeys(context.Background())
if err != nil {
return nil, err
}
for _, key := range apikeys {
if _, defined := ret.EnvApiKeys[key.Name]; defined {
return nil, fmt.Errorf(
"an api key with the name %s is already defined in database. Can't specify a new one via env var",
key.Name,
)
}
}
return &ret, nil
}