package targets import ( "errors" "github.com/theupdateframework/go-tuf/data" "github.com/theupdateframework/go-tuf/internal/sets" "github.com/theupdateframework/go-tuf/verify" ) type Delegation struct { Delegator string Delegatee data.DelegatedRole DB *verify.DB } type delegationsIterator struct { stack []Delegation target string visitedRoles map[string]struct{} parents map[string]string } var ErrTopLevelTargetsRoleMissing = errors.New("tuf: top level targets role missing from top level keys DB") // NewDelegationsIterator initialises an iterator with a first step // on top level targets. func NewDelegationsIterator(target string, topLevelKeysDB *verify.DB) (*delegationsIterator, error) { targetsRole := topLevelKeysDB.GetRole("targets") if targetsRole == nil { return nil, ErrTopLevelTargetsRoleMissing } i := &delegationsIterator{ target: target, stack: []Delegation{ { Delegatee: data.DelegatedRole{ Name: "targets", KeyIDs: sets.StringSetToSlice(targetsRole.KeyIDs), Threshold: targetsRole.Threshold, }, DB: topLevelKeysDB, }, }, visitedRoles: make(map[string]struct{}), parents: make(map[string]string), } return i, nil } func (d *delegationsIterator) Next() (value Delegation, ok bool) { if len(d.stack) == 0 { return Delegation{}, false } delegation := d.stack[len(d.stack)-1] d.stack = d.stack[:len(d.stack)-1] // 5.6.7.1: If this role has been visited before, then skip this role (so // that cycles in the delegation graph are avoided). roleName := delegation.Delegatee.Name if _, ok := d.visitedRoles[roleName]; ok { return d.Next() } d.visitedRoles[roleName] = struct{}{} // 5.6.7.2 trim delegations to visit, only the current role and its delegations // will be considered // https://github.com/theupdateframework/specification/issues/168 if delegation.Delegatee.Terminating { // Empty the stack. d.stack = d.stack[0:0] } return delegation, true } func (d *delegationsIterator) Add(roles []data.DelegatedRole, delegator string, db *verify.DB) error { for i := len(roles) - 1; i >= 0; i-- { // Push the roles onto the stack in reverse so we get an preorder traversal // of the delegations graph. r := roles[i] matchesPath, err := r.MatchesPath(d.target) if err != nil { return err } if matchesPath { delegation := Delegation{ Delegator: delegator, Delegatee: r, DB: db, } d.stack = append(d.stack, delegation) d.parents[r.Name] = delegator } } return nil } func (d *delegationsIterator) Parent(role string) string { return d.parents[role] }