package rulesengine import ( "context" "fmt" ) func (reng RulesEngine) ReadRulesForBanner(ctx context.Context, bannerName string) ([]Rule, error) { ruleSegments, err := reng.ds.ReadRulesForBanner(ctx, bannerName) if err != nil { return []Rule{}, fmt.Errorf("error when calling ReadRulesForBanner: %s", err) } return assembleRules(ruleSegments), nil } func (reng RulesEngine) ReadRulesForAllBanners(ctx context.Context) ([]ReadBannerRule, error) { bannerRules, err := reng.ds.ReadRulesForAllBanners(ctx) if err != nil { return nil, err } return assembleGetBannerRule(bannerRules) } func assembleGetBannerRule(bannerSegments []RuleSegment) ([]ReadBannerRule, error) { var res []ReadBannerRule for _, segment := range bannerSegments { // since assembleRules and assembleGetBannerRules operate on the same data, we need to check // here if the banners are all defined and fail fast if not. if segment.Banner.BannerID == "" || segment.Banner.BannerName == "" { return res, fmt.Errorf("error in assembeGetBannerRule: Banner was nil") } res = appendBannerSegment(res, segment) } return res, nil } func appendBannerSegment(rules []ReadBannerRule, segment RuleSegment) []ReadBannerRule { for idx, rule := range rules { if rule.Command.ID != segment.Command.ID { continue } rules[idx].Banners = appendPrivToBannerOverlay(rule.Banners, segment.Banner, segment.Privilege) return rules } rules = append(rules, ReadBannerRule{ Command: segment.Command, Banners: []BannerPrivOverrides{{ Banner: segment.Banner, Privileges: []Privilege{ segment.Privilege, }, }}, }) return rules } func appendPrivToBannerOverlay(overrides []BannerPrivOverrides, banner Banner, privilege Privilege) []BannerPrivOverrides { for idx, override := range overrides { if override.Banner.BannerID == banner.BannerID { overrides[idx].Privileges = append(override.Privileges, privilege) return overrides } } overrides = append(overrides, BannerPrivOverrides{ Banner: banner, Privileges: []Privilege{privilege}, }) return overrides } func (reng RulesEngine) DeletePrivilegeFromBannerRule(ctx context.Context, bannerName, commandName, privilegeName string) (DeleteResult, error) { return reng.ds.DeletePrivilegeFromBannerRule(ctx, bannerName, commandName, privilegeName) } // ReadAllRulesForCommand reads all rules associated to a command (Default & Banner specific overrides). // Empty fields will always return the datatype specified and not nil. func (reng RulesEngine) ReadAllRulesForCommand(ctx context.Context, commandName string) (RuleWithOverrides, error) { // read rule for commandName. ruleSegment, err := reng.ds.ReadDefaultRulesForCommand(ctx, commandName) if err != nil { return RuleWithOverrides{}, fmt.Errorf("error finding default rules: %s", err) } defaultRules := assembleRules(ruleSegment) // If defaultRules is empty this could mean one of two things: // 1. the command does not exist and its ok to return early // 2. the command exists but there are no default rules associated to that command // a) There may or may not be banner specific overrides // b) If there are banner specific overrides we need to be able set the commandID without panicking res := RuleWithOverrides{} if len(defaultRules) != 0 { // update resulting command from query res.Command = defaultRules[0].Command res.Default.Privileges = defaultRules[0].Privileges } allBannerRules, err := reng.ReadBannerRulesForCommand(ctx, commandName) if err != nil { return res, fmt.Errorf("error finding banner rules: %s", err) } for _, banner := range allBannerRules.Banners { for _, priv := range banner.Privileges { res.Banners = appendPrivToBannerOverlay(res.Banners, banner.Banner, priv) } res.Command = allBannerRules.Command } if len(res.Banners) == 0 { // make sure the return type is defined res.Banners = []BannerPrivOverrides{} } if len(res.Banners) == 0 && len(res.Default.Privileges) == 0 { return RuleWithOverrides{}, nil } return res, nil } func (reng RulesEngine) AddBannerRules(ctx context.Context, bannerName string, rules WriteRules) (AddRuleResult, error) { parts := splitPostRulesToBannerSegment(bannerName, rules) return reng.ds.AddBannerRules(ctx, parts) } func splitPostRulesToBannerSegment(bannerName string, rules []WriteRule) []RuleSegment { var parts []RuleSegment banner := Banner{BannerName: bannerName} for _, rule := range rules { command := Command{Name: rule.Command} for _, privilege := range rule.Privileges { privilege := Privilege{Name: privilege} parts = append(parts, RuleSegment{ Banner: banner, Command: command, Privilege: privilege, }) } } return parts } func (reng RulesEngine) ReadBannerRulesForCommand(ctx context.Context, commandName string) (ReadBannerRule, error) { parts, err := reng.ds.ReadBannerRulesForCommand(ctx, commandName) if err != nil { return ReadBannerRule{}, fmt.Errorf("error querying for rules: %w", err) } if len(parts) == 0 { return ReadBannerRule{}, nil } rules, err := assembleGetBannerRule(parts) if err != nil { return ReadBannerRule{}, err } if len(rules) != 1 { return ReadBannerRule{}, fmt.Errorf("storage returned unexpected number of rules") } return rules[0], nil } func (reng RulesEngine) ReadBannerRulesForCommandAndBanner(ctx context.Context, bannerName string, commandName string) (Rule, error) { parts, err := reng.ds.ReadBannerRulesForCommandAndBanner(ctx, bannerName, commandName) if err != nil { return Rule{}, fmt.Errorf("error querying for rules: %w", err) } if len(parts) == 0 { return Rule{}, nil } rules := assembleRules(parts) if len(rules) != 1 { return Rule{}, fmt.Errorf("storage returned unexpected number of rules") } return rules[0], nil }