package rulesengine import ( "context" "fmt" ) func (reng RulesEngine) AddDefaultRules(ctx context.Context, rules WriteRules) (AddRuleResult, error) { // validation if err := rules.Validate(); err != nil { return AddRuleResult{}, err } parts := splitPostDefaultRulesToRuleSegment(rules) return reng.ds.AddDefaultRules(ctx, parts) } // AddDefaultRulesForPrivileges adds default rules for privileges based on the provided data. func (reng RulesEngine) AddDefaultRulesForPrivileges(ctx context.Context, data RuleSets) (AddRuleResult, error) { if err := data.Validate(); err != nil { return AddRuleResult{}, err } var parts []RuleSegment for _, ruleSet := range data { for _, command := range ruleSet.Commands { ruleSegment := RuleSegment{ Command: Command{Name: command}, Privilege: Privilege{Name: ruleSet.Privilege}, } parts = append(parts, ruleSegment) } } return reng.ds.AddDefaultRules(ctx, parts) } // GetDefaultRules retrieves the default rules. These consist of a Privilege with ID associated to a list of Commands and their IDs. // The optional `privilegeNames` argument can be used to filter the output to specific privileges func (reng RulesEngine) GetDefaultRules(ctx context.Context, privilegeNames ...string) ([]ReturnRuleSet, error) { rules := make(map[Privilege][]Command) res := []ReturnRuleSet{} defaultRules, err := reng.ds.ReadAllDefaultRules(ctx) if err != nil { return res, err } // Convert slice of privilegeNames to a map for efficient lookups privilegeNameMap := make(map[string]struct{}) for _, name := range privilegeNames { privilegeNameMap[name] = struct{}{} } for _, rule := range defaultRules { _, ok := privilegeNameMap[rule.Privilege.Name] // If privilegeNames is empty, or the current rule's privilege is in the list, process it if len(privilegeNameMap) == 0 || ok { rules[rule.Privilege] = append(rules[rule.Privilege], rule.Command) } } for priv, cmds := range rules { res = append(res, ReturnRuleSet{Privilege: priv, Commands: cmds}) } return res, nil } func splitPostDefaultRulesToRuleSegment(rules WriteRules) (parts []RuleSegment) { for _, rule := range rules { command := Command{Name: rule.Command} for _, priv := range rule.Privileges { privilege := Privilege{Name: priv} parts = append(parts, RuleSegment{ Command: command, Privilege: privilege, }) } } return parts } func (reng RulesEngine) ReadAllDefaultRules(ctx context.Context) ([]Rule, error) { rulesegments, err := reng.ds.ReadAllDefaultRules(ctx) if err != nil { return []Rule{}, fmt.Errorf("error when reading all default rules: %s", err) } return assembleRules(rulesegments), nil } func (reng RulesEngine) ReadDefaultRulesForCommand(ctx context.Context, commandName string) ([]Rule, error) { ruleSegments, err := reng.ds.ReadDefaultRulesForCommand(ctx, commandName) if err != nil { return []Rule{}, fmt.Errorf("error when reading default rules for command: %s", err) } return assembleRules(ruleSegments), nil } func (reng RulesEngine) DeleteDefaultRule(ctx context.Context, commandName, privilegeName string) (DeleteResult, error) { return reng.ds.DeleteDefaultRule(ctx, commandName, privilegeName) } func assembleRules(ruleSegments []RuleSegment) []Rule { rules := []Rule{} for _, ruleSegment := range ruleSegments { rules = appendRule(rules, ruleSegment) } return rules } func appendRule(rules []Rule, ruleSegment RuleSegment) []Rule { if len(rules) == 0 { return []Rule{{Command: ruleSegment.Command, Privileges: []Privilege{ruleSegment.Privilege}}} } for idx, rule := range rules { if rule.Command.ID == ruleSegment.Command.ID { // dataset handles duplicate instances for us so we dont need to check here rules[idx].Privileges = append(rule.Privileges, ruleSegment.Privilege) return rules } } rules = append(rules, Rule{Command: ruleSegment.Command, Privileges: []Privilege{ruleSegment.Privilege}}) return rules }