Skip to content

Commit 7d13eb7

Browse files
willveddadriantam
andauthoredJun 20, 2025
Fix: correctly assign weights for intersections with multiple direct types (#455)
* Accounting for multiple direct relations in intersection operations * Handling left and right operands separately * Adding more tests * Fixing algorithm to work with multiple statements, resulting in multiple rewrite edges * Changing require.True => require.Equal * removing unnecessary weights allocation * Adding comment * Update pkg/go/graph/weighted_graph.go Co-authored-by: Adrian Tam <[email protected]> * Pre-allocating combined weights slice --------- Co-authored-by: Adrian Tam <[email protected]>
1 parent bd7c013 commit 7d13eb7

File tree

2 files changed

+652
-288
lines changed

2 files changed

+652
-288
lines changed
 

‎pkg/go/graph/weighted_graph.go

Lines changed: 35 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -499,36 +499,57 @@ func (wg *WeightedAuthorizationModelGraph) calculateNodeWeightWithMixedStrategy(
499499
// not all paths return the same type and the model is not valid.
500500
func (wg *WeightedAuthorizationModelGraph) calculateNodeWeightWithEnforceTypeStrategy(nodeID string) error {
501501
node := wg.nodes[nodeID]
502-
weights := make(map[string]int)
503502
edges := wg.edges[nodeID]
504503

505504
if len(edges) == 0 && node.nodeType != SpecificType && node.nodeType != SpecificTypeWildcard {
506505
return fmt.Errorf("%w: %s node does not have any terminal type to reach to", ErrInvalidModel, node.uniqueLabel)
507506
}
508507

508+
// In intersection, directly-assignable types must be handled separately from rewritten types.
509+
// All types flatten to edges, but directly-assignable weights are OR'd together, and that result
510+
// is then AND'd with the combined weights of the rewritten edges for validity and weight assignment.
511+
directlyAssignableWeights := make(map[string]int, len(edges))
512+
rewriteWeights := make(map[string]int, len(edges))
513+
509514
for _, edge := range edges {
510-
// for but not ensure that the first edge is the left edge
511-
// the first time, take the weights of the edge
512-
if len(weights) == 0 {
513-
for key, value := range edge.weights {
514-
weights[key] = value
515+
if edge.GetEdgeType() == DirectEdge {
516+
for key, weight := range edge.weights {
517+
directlyAssignableWeights[key] = weight
515518
}
516519
continue
517520
}
521+
if len(rewriteWeights) == 0 {
522+
for key, weight := range edge.weights {
523+
rewriteWeights[key] = weight
524+
}
525+
} else {
526+
for key, rewriteWeight := range rewriteWeights {
527+
if _, existsAlready := edge.GetWeights()[key]; existsAlready {
528+
rewriteWeights[key] = int(math.Max(float64(edge.weights[key]), float64(rewriteWeight)))
529+
} else {
530+
delete(rewriteWeights, key)
531+
}
532+
}
533+
}
534+
}
518535

519-
// for AndOperation, remove the key if it is not in the edge, not all edges return the same type
520-
for key := range weights {
521-
if value, ok := edge.weights[key]; !ok {
522-
delete(weights, key)
523-
} else {
524-
weights[key] = int(math.Max(float64(weights[key]), float64(value)))
536+
if len(directlyAssignableWeights) > 0 {
537+
for key := range directlyAssignableWeights {
538+
if _, existsInBoth := rewriteWeights[key]; existsInBoth {
539+
if node.weights == nil {
540+
node.weights = make(map[string]int, int(math.Min(float64(len(rewriteWeights)), float64(len(directlyAssignableWeights)))))
541+
}
542+
node.weights[key] = int(math.Max(float64(rewriteWeights[key]), float64(directlyAssignableWeights[key])))
525543
}
526544
}
545+
} else {
546+
node.weights = rewriteWeights
527547
}
528-
if len(weights) == 0 {
548+
549+
if len(node.weights) == 0 {
529550
return fmt.Errorf("%w: not all paths return the same type for the node %s", ErrInvalidModel, nodeID)
530551
}
531-
node.weights = weights
552+
532553
return nil
533554
}
534555

0 commit comments

Comments
 (0)