You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

128 lines
3.2 KiB

package lsat
import (
"errors"
"fmt"
"strconv"
"strings"
)
const (
// CondServices is the condition used for a services caveat.
CondServices = "services"
// CondCapabilitiesSuffix is the condition suffix used for a service's
// capabilities caveat. For example, the condition of a capabilities
// caveat for a service named `loop` would be `loop_capabilities`.
CondCapabilitiesSuffix = "_capabilities"
)
var (
// ErrNoServices is an error returned when we attempt to decode the
// services included in a caveat.
ErrNoServices = errors.New("no services found")
// ErrInvalidService is an error returned when we attempt to decode a
// service with an invalid format.
ErrInvalidService = errors.New("service must be of the form " +
"\"name:tier\"")
)
// ServiceTier represents the different possible tiers of an LSAT-enabled
// service.
type ServiceTier uint8
const (
// BaseTier is the base tier of an LSAT-enabled service. This tier
// should be used for any new LSATs that are not part of a service tier
// upgrade.
BaseTier ServiceTier = iota
)
// Service contains the details of an LSAT-enabled service.
type Service struct {
// Name is the name of the LSAT-enabled service.
Name string
// Tier is the tier of the LSAT-enabled service.
Tier ServiceTier
}
// NewServicesCaveat creates a new services caveat with the provided caveats.
func NewServicesCaveat(services ...Service) (Caveat, error) {
value, err := encodeServicesCaveatValue(services...)
if err != nil {
return Caveat{}, err
}
return Caveat{
Condition: CondServices,
Value: value,
}, nil
}
// encodeServicesCaveatValue encodes a list of services into the expected format
// of a services caveat's value.
func encodeServicesCaveatValue(services ...Service) (string, error) {
if len(services) == 0 {
return "", ErrNoServices
}
var s strings.Builder
for i, service := range services {
if service.Name == "" {
return "", errors.New("missing service name")
}
fmtStr := "%v:%v"
if i < len(services)-1 {
fmtStr += ","
}
fmt.Fprintf(&s, fmtStr, service.Name, uint8(service.Tier))
}
return s.String(), nil
}
// decodeServicesCaveatValue decodes a list of services from the expected format
// of a services caveat's value.
func decodeServicesCaveatValue(s string) ([]Service, error) {
if s == "" {
return nil, ErrNoServices
}
rawServices := strings.Split(s, ",")
services := make([]Service, 0, len(rawServices))
for _, rawService := range rawServices {
serviceInfo := strings.Split(rawService, ":")
if len(serviceInfo) != 2 {
return nil, ErrInvalidService
}
name, tierStr := serviceInfo[0], serviceInfo[1]
if name == "" {
return nil, fmt.Errorf("%w: %v", ErrInvalidService,
"empty name")
}
tier, err := strconv.Atoi(tierStr)
if err != nil {
return nil, fmt.Errorf("%w: %v", ErrInvalidService, err)
}
services = append(services, Service{
Name: name,
Tier: ServiceTier(tier),
})
}
return services, nil
}
// NewCapabilitiesCaveat creates a new capabilities caveat for the given
// service.
func NewCapabilitiesCaveat(serviceName string, capabilities string) Caveat {
return Caveat{
Condition: serviceName + CondCapabilitiesSuffix,
Value: capabilities,
}
}