Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 11 additions & 11 deletions pkg/sync/expand/graph.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,17 +39,17 @@ type Node struct {
// This is because the graph can have cycles, and we address them by reducing
// _all_ nodes in a cycle into a single node.
type EntitlementGraph struct {
NextNodeID int `json:"next_node_id"` // Automatically incremented so that each node has a unique ID.
NextEdgeID int `json:"next_edge_id"` // Automatically incremented so that each edge has a unique ID.
Nodes map[int]Node `json:"nodes"` // The mapping of all node IDs to nodes.
EntitlementsToNodes map[string]int `json:"entitlements_to_nodes"` // Internal mapping of entitlements to nodes for quicker lookup.
SourcesToDestinations map[int]map[int]int `json:"sources_to_destinations"` // Internal mapping of outgoing edges by node ID.
DestinationsToSources map[int]map[int]int `json:"destinations_to_sources"` // Internal mapping of incoming edges by node ID.
Edges map[int]Edge `json:"edges"` // Adjacency list. Source node -> descendant node
Loaded bool `json:"loaded"`
Depth int `json:"depth"`
Actions []EntitlementGraphAction `json:"actions"`
HasNoCycles bool `json:"has_no_cycles"`
NextNodeID int `json:"next_node_id"` // Automatically incremented so that each node has a unique ID.
NextEdgeID int `json:"next_edge_id"` // Automatically incremented so that each edge has a unique ID.
Nodes map[int]Node `json:"nodes"` // The mapping of all node IDs to nodes.
EntitlementsToNodes map[string]int `json:"entitlements_to_nodes"` // Internal mapping of entitlements to nodes for quicker lookup.
SourcesToDestinations map[int]map[int]int `json:"sources_to_destinations"` // Internal mapping of outgoing edges by node ID.
DestinationsToSources map[int]map[int]int `json:"destinations_to_sources"` // Internal mapping of incoming edges by node ID.
Edges map[int]Edge `json:"edges"` // Adjacency list. Source node -> descendant node
Loaded bool `json:"loaded"`
Depth int `json:"depth"`
Actions []*EntitlementGraphAction `json:"actions"`
HasNoCycles bool `json:"has_no_cycles"`
}

func NewEntitlementGraph(_ context.Context) *EntitlementGraph {
Expand Down
2 changes: 1 addition & 1 deletion pkg/sync/syncer.go
Original file line number Diff line number Diff line change
Expand Up @@ -1469,7 +1469,7 @@ func (s *syncer) expandGrantsForEntitlements(ctx context.Context) error {
if grantInfo.IsExpanded {
continue
}
graph.Actions = append(graph.Actions, expand.EntitlementGraphAction{
graph.Actions = append(graph.Actions, &expand.EntitlementGraphAction{
SourceEntitlementID: sourceEntitlementID,
DescendantEntitlementID: descendantEntitlementID,
PageToken: "",
Expand Down
52 changes: 52 additions & 0 deletions pkg/sync/syncer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,58 @@ var userResourceType = &v2.ResourceType{
Annotations: annotations.New(&v2.SkipEntitlementsAndGrants{}),
}

func TestExpandGrants(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()

// 2500 * 4 = 10K - used to cause an infinite loop on pagition
usersPerLayer := 2500
groupCount := 5

mc := newMockConnector()

mc.rtDB = append(mc.rtDB, groupResourceType, userResourceType)
type asdf struct {
r *v2.Resource
e *v2.Entitlement
}
groups := make([]*asdf, 0)
for i := 0; i < groupCount; i++ {
groupId := "group_" + strconv.Itoa(i)
group, groupEnt, err := mc.AddGroup(ctx, groupId)
for _, g := range groups {
_ = mc.AddGroupMember(ctx, g.r, group, groupEnt)
}
groups = append(groups, &asdf{
r: group,
e: groupEnt,
})
require.NoError(t, err)

for j := 0; j < usersPerLayer; j++ {
pid := fmt.Sprintf("user_%d_%d_%d", i, usersPerLayer, j)
principal, err := mc.AddUser(ctx, pid)
require.NoError(t, err)

// This isn't needed because grant expansion will create this grant
// _ = mc.AddGroupMember(ctx, group, principal)
_ = mc.AddGroupMember(ctx, group, principal)
Comment on lines +65 to +67
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Remove redundant comment and duplicate code

The code contains both a commented-out line and its uncommented duplicate, which is confusing.

-			// This isn't needed because grant expansion will create this grant
-			// _ = mc.AddGroupMember(ctx, group, principal)
-			_ = mc.AddGroupMember(ctx, group, principal)
+			_ = mc.AddGroupMember(ctx, group, principal)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// This isn't needed because grant expansion will create this grant
// _ = mc.AddGroupMember(ctx, group, principal)
_ = mc.AddGroupMember(ctx, group, principal)
_ = mc.AddGroupMember(ctx, group, principal)

}
}

tempDir, err := os.MkdirTemp("", "baton-benchmark-expand-grants")
require.NoError(t, err)
defer os.RemoveAll(tempDir)
c1zpath := filepath.Join(tempDir, "expand-grants.c1z")
syncer, err := NewSyncer(ctx, mc, WithC1ZPath(c1zpath), WithTmpDir(tempDir))
require.NoError(t, err)
err = syncer.Sync(ctx)
require.NoError(t, err)
err = syncer.Close(ctx)
require.NoError(t, err)
_ = os.Remove(c1zpath)
}
Comment on lines +71 to +82
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Consider adding test assertions

The test creates the data structure but doesn't verify the results of the grant expansion.

Add assertions to verify:

  1. The correct number of grants were created
  2. The grants have the expected relationships
  3. No infinite loops occurred during expansion

Example:

 	err = syncer.Close(ctx)
 	require.NoError(t, err)
 	_ = os.Remove(c1zpath)
+
+	// Verify grant expansion results
+	grants, err := syncer.store.ListGrants(ctx, &v2.GrantsServiceListGrantsRequest{})
+	require.NoError(t, err)
+	
+	// Assert expected number of grants
+	expectedGrantCount := groupCount * usersPerLayer
+	assert.Equal(t, expectedGrantCount, len(grants.List), "Unexpected number of grants")
+	
+	// Verify grant relationships
+	for _, grant := range grants.List {
+		assert.NotNil(t, grant.Principal, "Grant should have a principal")
+		assert.NotNil(t, grant.Entitlement, "Grant should have an entitlement")
+	}

Committable suggestion skipped: line range outside the PR's diff.


func BenchmarkExpandCircle(b *testing.B) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
Expand Down
Loading