Skip to content

Commit

Permalink
Match spec for delegation traversal
Browse files Browse the repository at this point in the history
  • Loading branch information
ethan-lowman-dd committed Jul 14, 2021
1 parent 8d755bd commit 15fee6b
Show file tree
Hide file tree
Showing 2 changed files with 47 additions and 22 deletions.
26 changes: 11 additions & 15 deletions client/delegations.go
Original file line number Diff line number Diff line change
Expand Up @@ -149,22 +149,17 @@ type delegation struct {
child data.DelegatedRole
}

type delegationID struct {
parent string
child string
}

type delegationsIterator struct {
stack []delegation
file string
visited map[delegationID]struct{}
stack []delegation
file string
visitedRoles map[string]struct{}
}

func newDelegationsIterator(role data.DelegatedRole, parent string, file string) *delegationsIterator {
i := &delegationsIterator{
file: file,
stack: make([]delegation, 0, 1),
visited: make(map[delegationID]struct{}),
file: file,
stack: make([]delegation, 0, 1),
visitedRoles: make(map[string]struct{}),
}

i.add([]data.DelegatedRole{role}, parent)
Expand All @@ -179,12 +174,13 @@ func (d *delegationsIterator) next() (delegation, bool) {
delegation := d.stack[len(d.stack)-1]
d.stack = d.stack[:len(d.stack)-1]

// 5.6.7.1 cycles protection
id := delegationID{delegation.parent, delegation.child.Name}
if _, ok := d.visited[id]; ok {
// 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.child.Name
if _, ok := d.visitedRoles[roleName]; ok {
return d.next()
}
d.visited[id] = struct{}{}
d.visitedRoles[roleName] = struct{}{}

// 5.6.7.2 trim delegations to visit, only the current role and its delegations
// will be considered
Expand Down
43 changes: 36 additions & 7 deletions client/delegations_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,23 @@ func TestDelegationsIterator(t *testing.T) {
},
"b": {
{Name: "c", Paths: defaultPathPatterns},
},
"c": {
{Name: "d", Paths: defaultPathPatterns},
},
"e": {
{Name: "f", Paths: defaultPathPatterns},
{Name: "g", Paths: defaultPathPatterns},
},
"g": {
{Name: "h", Paths: defaultPathPatterns},
{Name: "i", Paths: defaultPathPatterns},
{Name: "j", Paths: defaultPathPatterns},
},
},
rootDelegation: data.DelegatedRole{Name: "a", Paths: defaultPathPatterns},
file: "",
resultOrder: []string{"a", "b", "c", "d", "e"},
resultOrder: []string{"a", "b", "c", "d", "e", "f", "g", "h", "i", "j"},
},
{
testName: "terminated in b",
Expand Down Expand Up @@ -78,7 +89,7 @@ func TestDelegationsIterator(t *testing.T) {
resultOrder: []string{"a", "e"},
},
{
testName: "cycle avoided",
testName: "cycle avoided 1",
roles: map[string][]data.DelegatedRole{
"a": {
{Name: "b", Paths: defaultPathPatterns},
Expand All @@ -91,7 +102,28 @@ func TestDelegationsIterator(t *testing.T) {
},
rootDelegation: data.DelegatedRole{Name: "a", Paths: defaultPathPatterns},
file: "",
resultOrder: []string{"a", "b", "a", "e", "d"},
resultOrder: []string{"a", "b", "d", "e"},
},

{
testName: "cycle avoided 2",
roles: map[string][]data.DelegatedRole{
"a": {
{Name: "a", Paths: defaultPathPatterns},
{Name: "b", Paths: defaultPathPatterns},
},
"b": {
{Name: "a", Paths: defaultPathPatterns},
{Name: "b", Paths: defaultPathPatterns},
{Name: "c", Paths: defaultPathPatterns},
},
"c": {
{Name: "c", Paths: defaultPathPatterns},
},
},
rootDelegation: data.DelegatedRole{Name: "a", Paths: defaultPathPatterns},
file: "",
resultOrder: []string{"a", "b", "c"},
},
}

Expand All @@ -111,10 +143,7 @@ func TestDelegationsIterator(t *testing.T) {
}
d.add(delegations, r.child.Name)
}
assert.Equal(t, len(tt.resultOrder), len(iterationOrder))
for i, role := range iterationOrder {
assert.Equal(t, tt.resultOrder[i], role)
}
assert.Equal(t, tt.resultOrder, iterationOrder)
})
}
}
Expand Down

0 comments on commit 15fee6b

Please sign in to comment.