package data
import (
"fmt"
"strings"
"github.com/endophage/gotuf/errors"
)
// Canonical base role names
const (
CanonicalRootRole = "root"
CanonicalTargetsRole = "targets"
CanonicalSnapshotRole = "snapshot"
CanonicalTimestampRole = "timestamp"
)
var ValidRoles = map[string]string{
CanonicalRootRole: CanonicalRootRole,
CanonicalTargetsRole: CanonicalTargetsRole,
CanonicalSnapshotRole: CanonicalSnapshotRole,
CanonicalTimestampRole: CanonicalTimestampRole,
}
func SetValidRoles(rs map[string]string) {
// iterate ValidRoles
for k := range ValidRoles {
if v, ok := rs[k]; ok {
ValidRoles[k] = v
}
}
}
func RoleName(role string) string {
if r, ok := ValidRoles[role]; ok {
return r
}
return role
}
func CanonicalRole(role string) string {
name := strings.ToLower(role)
if _, ok := ValidRoles[name]; ok {
// The canonical version is always lower case
// se ensure we return name, not role
return name
}
targetsBase := fmt.Sprintf("%s/", ValidRoles[CanonicalTargetsRole])
if strings.HasPrefix(name, targetsBase) {
role = strings.TrimPrefix(role, targetsBase)
role = fmt.Sprintf("%s/%s", CanonicalTargetsRole, role)
return role
}
for r, v := range ValidRoles {
if role == v {
return r
}
}
return ""
}
// ValidRole only determines the name is semantically
// correct. For target delegated roles, it does NOT check
// the the appropriate parent roles exist.
func ValidRole(name string) bool {
name = strings.ToLower(name)
if v, ok := ValidRoles[name]; ok {
return name == v
}
targetsBase := fmt.Sprintf("%s/", ValidRoles[CanonicalTargetsRole])
if strings.HasPrefix(name, targetsBase) {
return true
}
for _, v := range ValidRoles {
if name == v {
return true
}
}
return false
}
type RootRole struct {
KeyIDs []string `json:"keyids"`
Threshold int `json:"threshold"`
}
type Role struct {
RootRole
Name string `json:"name"`
Paths []string `json:"paths,omitempty"`
PathHashPrefixes []string `json:"path_hash_prefixes,omitempty"`
Email string `json:"email,omitempty"`
}
func NewRole(name string, threshold int, keyIDs, paths, pathHashPrefixes []string) (*Role, error) {
if len(paths) > 0 && len(pathHashPrefixes) > 0 {
return nil, errors.ErrInvalidRole{}
}
if threshold < 1 {
return nil, errors.ErrInvalidRole{}
}
if !ValidRole(name) {
return nil, errors.ErrInvalidRole{}
}
return &Role{
RootRole: RootRole{
KeyIDs: keyIDs,
Threshold: threshold,
},
Name: name,
Paths: paths,
PathHashPrefixes: pathHashPrefixes,
}, nil
}
func (r Role) IsValid() bool {
return !(len(r.Paths) > 0 && len(r.PathHashPrefixes) > 0)
}
func (r Role) ValidKey(id string) bool {
for _, key := range r.KeyIDs {
if key == id {
return true
}
}
return false
}
func (r Role) CheckPaths(path string) bool {
for _, p := range r.Paths {
if strings.HasPrefix(path, p) {
return true
}
}
return false
}
func (r Role) CheckPrefixes(hash string) bool {
for _, p := range r.PathHashPrefixes {
if strings.HasPrefix(hash, p) {
return true
}
}
return false
}
func (r Role) IsDelegation() bool {
targetsBase := fmt.Sprintf("%s/", ValidRoles[CanonicalTargetsRole])
return strings.HasPrefix(r.Name, targetsBase)
}