package nixcache
import (
"crypto/rsa"
"errors"
"fmt"
"time"
"github.com/golang-jwt/jwt/v5"
)
type Grant struct {
Pull bool
Push bool
Delete bool
Create bool
ConfigureCache bool
ConfigureRetention bool
}
// attic wants 1 for granted, and omitempty leaves denied ones out
type permission struct {
Pull int `json:"r,omitempty"`
Push int `json:"w,omitempty"`
Delete int `json:"d,omitempty"`
Create int `json:"cc,omitempty"`
ConfigureCache int `json:"cr,omitempty"`
ConfigureRetention int `json:"cq,omitempty"`
}
func bit(b bool) int {
if b {
return 1
}
return 0
}
type claims struct {
jwt.RegisteredClaims
Attic struct {
Caches map[string]permission `json:"caches"`
} `json:"https://jwt.attic.rs/v1"`
}
func MintToken(key *rsa.PrivateKey, sub, cache string, g Grant, expires time.Time) (string, error) {
if key == nil {
return "", errors.New("no signing key")
}
if !validName.MatchString(cache) {
return "", fmt.Errorf("%q is not a plain attic cache name", cache)
}
if g == (Grant{}) {
return "", errors.New("grant allows nothing")
}
if !expires.After(time.Now()) {
return "", errors.New("token would already be expired")
}
c := claims{RegisteredClaims: jwt.RegisteredClaims{
Subject: sub,
ExpiresAt: jwt.NewNumericDate(expires),
}}
c.Attic.Caches = map[string]permission{cache: {
Pull: bit(g.Pull),
Push: bit(g.Push),
Delete: bit(g.Delete),
Create: bit(g.Create),
ConfigureCache: bit(g.ConfigureCache),
ConfigureRetention: bit(g.ConfigureRetention),
}}
return jwt.NewWithClaims(jwt.SigningMethodRS256, c).SignedString(key)
}