// Walk to parent, and either create or update this delegation. We can only create a new delegation if we're given keys // Ensure all updates are valid, by checking against parent ancestor paths and ensuring the keys meet the role threshold. func delegationUpdateVisitor(roleName string, addKeys data.KeyList, removeKeys, addPaths, removePaths []string, clearAllPaths bool, newThreshold int) walkVisitorFunc { return func(tgt *data.SignedTargets, validRole data.DelegationRole) interface{} { var err error // Validate the changes underneath this restricted validRole for adding paths, reject invalid path additions if len(addPaths) != len(data.RestrictDelegationPathPrefixes(validRole.Paths, addPaths)) { return data.ErrInvalidRole{Role: roleName, Reason: "invalid paths to add to role"} } // Try to find the delegation and amend it using our changelist var delgRole *data.Role for _, role := range tgt.Signed.Delegations.Roles { if role.Name == roleName { // Make a copy and operate on this role until we validate the changes keyIDCopy := make([]string, len(role.KeyIDs)) copy(keyIDCopy, role.KeyIDs) pathsCopy := make([]string, len(role.Paths)) copy(pathsCopy, role.Paths) delgRole = &data.Role{ RootRole: data.RootRole{ KeyIDs: keyIDCopy, Threshold: role.Threshold, }, Name: role.Name, Paths: pathsCopy, } delgRole.RemovePaths(removePaths) if clearAllPaths { delgRole.Paths = []string{} } delgRole.AddPaths(addPaths) delgRole.RemoveKeys(removeKeys) break } } // We didn't find the role earlier, so create it only if we have keys to add if delgRole == nil { if len(addKeys) > 0 { delgRole, err = data.NewRole(roleName, newThreshold, addKeys.IDs(), addPaths) if err != nil { return err } } else { // If we can't find the role and didn't specify keys to add, this is an error return data.ErrInvalidRole{Role: roleName, Reason: "cannot create new delegation without keys"} } } // Add the key IDs to the role and the keys themselves to the parent for _, k := range addKeys { if !utils.StrSliceContains(delgRole.KeyIDs, k.ID()) { delgRole.KeyIDs = append(delgRole.KeyIDs, k.ID()) } } // Make sure we have a valid role still if len(delgRole.KeyIDs) < delgRole.Threshold { return data.ErrInvalidRole{Role: roleName, Reason: "insufficient keys to meet threshold"} } // NOTE: this closure CANNOT error after this point, as we've committed to editing the SignedTargets metadata in the repo object. // Any errors related to updating this delegation must occur before this point. // If all of our changes were valid, we should edit the actual SignedTargets to match our copy for _, k := range addKeys { tgt.Signed.Delegations.Keys[k.ID()] = k } foundAt := utils.FindRoleIndex(tgt.Signed.Delegations.Roles, delgRole.Name) if foundAt < 0 { tgt.Signed.Delegations.Roles = append(tgt.Signed.Delegations.Roles, delgRole) } else { tgt.Signed.Delegations.Roles[foundAt] = delgRole } tgt.Dirty = true utils.RemoveUnusedKeys(tgt) return StopWalk{} } }