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) }