package middleware import ( "os" "strings" "git.sendico.io/tech/sxchnge/shared/errors" ) type CORSSettings struct { MaxAge int `yaml:"max_age"` AllowedOrigins []string `yaml:"allowed_origins"` AllowedMethods []string `yaml:"allowed_methods"` AllowedHeaders []string `yaml:"allowed_headers"` ExposedHeaders []string `yaml:"exposed_headers"` AllowCredentials bool `yaml:"allow_credentials"` } func (c CORSSettings) Validate() error { if !c.AllowCredentials { return nil } if len(c.AllowedOrigins) == 0 { return merrors.InvalidArgument("credentialed CORS requires explicit allowed origins", "middleware.CORS.allowed_origins") } for _, origin := range c.AllowedOrigins { if strings.TrimSpace(origin) == "*" { return merrors.InvalidArgument("credentialed CORS does not allow wildcard origins", "middleware.CORS.allowed_origins") } } return nil } type Signature struct { PublicKey any PrivateKey []byte Algorithm string } type SignatureConfig struct { PublicKeyEnv string `yaml:"public_key_env,omitempty"` PrivateKeyEnv string `yaml:"secret_key_env"` Algorithm string `yaml:"algorithm"` } type TokenExpiration struct { Account int `yaml:"account"` Refresh int `yaml:"refresh"` } type TokenConfig struct { Expiration TokenExpiration `yaml:"expiration_hours"` Length int `yaml:"length"` } const MaxAccessTokenTTLHours = 1 func (c TokenConfig) Validate() error { if c.Expiration.Account <= 0 || c.Expiration.Account > MaxAccessTokenTTLHours { return merrors.InvalidArgument("access token expiration must be between 1 and 1 hour", "middleware.token.expiration_hours.account") } if c.Expiration.Refresh <= c.Expiration.Account { return merrors.InvalidArgument("refresh token expiration must exceed access token expiration", "middleware.token.expiration_hours.refresh") } if c.Length < 32 { return merrors.InvalidArgument("refresh token length must be at least 32 bytes", "middleware.token.length") } return nil } type PasswordChecks struct { Digit bool `yaml:"digit"` Upper bool `yaml:"upper"` Lower bool `yaml:"lower"` Special bool `yaml:"special"` MinLength int `yaml:"min_length"` } type PasswordConfig struct { TokenLength int `yaml:"token_length"` Check PasswordChecks `yaml:"check"` } type Config struct { DomainEnv string `yaml:"domain_env"` EndPointEnv string `yaml:"api_endpoint_env"` APIProtocolEnv string `yaml:"api_protocol_env"` Signature SignatureConfig `yaml:"signature"` CORS CORSSettings `yaml:"CORS"` Token TokenConfig `yaml:"token"` Password PasswordConfig `yaml:"password"` LoginRateLimits *LoginRateLimitsConfig `yaml:"login_rate_limits"` } type MapClaims = map[string]any func getKey(osEnv string) any { if len(osEnv) == 0 { return nil } return []byte(os.Getenv(osEnv)) } func SignatureConf(conf *Config) Signature { return Signature{ PrivateKey: []byte(os.Getenv(conf.Signature.PrivateKeyEnv)), PublicKey: getKey(conf.Signature.PublicKeyEnv), Algorithm: conf.Signature.Algorithm, } }