expandTransitiveAccessRules expands any transitive access rules in the provided list of rules. This involves looking up the referenced resource, determining its dependencies, and adding the necessary access rules for those dependencies. For example, a transitive access rule on a report will add acce
(ctx context.Context, instanceID string, claims *SecurityClaims)
| 726 | // This involves looking up the referenced resource, determining its dependencies, and adding the necessary access rules for those dependencies. |
| 727 | // For example, a transitive access rule on a report will add access rules for the underlying metrics view, explore, and any fields or rows that are accessible in the report. |
| 728 | func (p *securityEngine) expandTransitiveAccessRules(ctx context.Context, instanceID string, claims *SecurityClaims) ([]*runtimev1.SecurityRule, error) { |
| 729 | hasTransitive := false |
| 730 | transitiveCount := 0 |
| 731 | var rules []*runtimev1.SecurityRule |
| 732 | for _, rule := range claims.AdditionalRules { |
| 733 | if rule.GetTransitiveAccess() == nil { |
| 734 | rules = append(rules, rule) |
| 735 | continue |
| 736 | } |
| 737 | hasTransitive = true |
| 738 | // If the rule is a transitive access rule, we need to resolve it |
| 739 | resName := rule.GetTransitiveAccess().GetResource() |
| 740 | if resName == nil { |
| 741 | return nil, fmt.Errorf("transitive access rule has no resource") |
| 742 | } |
| 743 | ctr, err := p.rt.Controller(ctx, instanceID) |
| 744 | if err != nil { |
| 745 | return nil, fmt.Errorf("failed to get controller: %w", err) |
| 746 | } |
| 747 | res, err := ctr.Get(ctx, resName, false) |
| 748 | if err != nil { |
| 749 | if errors.Is(err, drivers.ErrResourceNotFound) { |
| 750 | // resource not found, skip transitive access |
| 751 | continue |
| 752 | } |
| 753 | return nil, fmt.Errorf("failed to get resource %q of kind %q: %w", resName.Name, resName.Kind, err) |
| 754 | } |
| 755 | resolvedRules, err := ctr.reconciler(res.Meta.Name.Kind).ResolveTransitiveAccess(ctx, claims, res) |
| 756 | if err != nil { |
| 757 | return nil, fmt.Errorf("failed to resolve transitive access rule: %w", err) |
| 758 | } |
| 759 | transitiveCount++ |
| 760 | rules = append(rules, resolvedRules...) |
| 761 | } |
| 762 | |
| 763 | if hasTransitive && transitiveCount == 0 { |
| 764 | // add access: false rule as no transitive access could be resolved |
| 765 | rules = append(rules, &runtimev1.SecurityRule{ |
| 766 | Rule: &runtimev1.SecurityRule_Access{ |
| 767 | Access: &runtimev1.SecurityRuleAccess{ |
| 768 | Allow: false, |
| 769 | }, |
| 770 | }, |
| 771 | }) |
| 772 | } |
| 773 | // gather all conditions kinds and resources mentioned in the rules so that we can add a single security rule access policy for them at the end. |
| 774 | // making sure only a single rule with the exclusive flag is added, otherwise we may get false rejections depending on which rule with the exclusive flag is evaluated first |
| 775 | var mergedRules []*runtimev1.SecurityRule |
| 776 | var conditionKinds []string |
| 777 | var conditionResources []*runtimev1.ResourceName |
| 778 | var conditionExpression string |
| 779 | // merge all access rules with an exclusive flag set in single rule |
| 780 | for _, rule := range rules { |
| 781 | if access := rule.GetAccess(); access != nil && access.Exclusive { |
| 782 | if access.ConditionExpression != "" { |
| 783 | if conditionExpression != "" { |
| 784 | conditionExpression = fmt.Sprintf("(%s) OR (%s)", conditionExpression, access.ConditionExpression) |
| 785 | } else { |
no test coverage detected