package rulesengine import ( "context" "fmt" "testing" "github.com/stretchr/testify/assert" ) var banners = []Banner{{ BannerID: "a", BannerName: "b", }, { BannerID: "c", BannerName: "d", }, { BannerID: "e", BannerName: "f", }, } var commands = []Command{{ ID: "a", Name: "b", }, { ID: "c", Name: "d", }, { ID: "e", Name: "f", }, } var privileges = []Privilege{{ ID: "a", Name: "b", }, { ID: "c", Name: "d", }, { ID: "e", Name: "f", }, } func TestAssembleGetBannerRule(t *testing.T) { t.Parallel() tests := map[string]struct { in []RuleSegment out []ReadBannerRule err error }{ "simple": { in: []RuleSegment{ { Banner: banners[0], Command: commands[0], Privilege: privileges[0], }, }, out: []ReadBannerRule{ { Command: commands[0], Banners: []BannerPrivOverrides{ { Banner: banners[0], Privileges: []Privilege{ privileges[0], }, }, }, }, }, }, "Two Banners": { in: []RuleSegment{ { Banner: banners[0], Command: commands[0], Privilege: privileges[0], }, { Banner: banners[1], Command: commands[0], Privilege: privileges[0], }, }, out: []ReadBannerRule{ { Command: commands[0], Banners: []BannerPrivOverrides{ { Banner: banners[0], Privileges: []Privilege{ privileges[0], }, }, { Banner: banners[1], Privileges: []Privilege{ privileges[0], }, }, }, }, }, }, "Two Privileges": { in: []RuleSegment{ { Banner: banners[0], Command: commands[0], Privilege: privileges[0], }, { Banner: banners[0], Command: commands[0], Privilege: privileges[1], }, }, out: []ReadBannerRule{ { Command: commands[0], Banners: []BannerPrivOverrides{ { Banner: banners[0], Privileges: []Privilege{ privileges[0], privileges[1], }, }, }, }, }, }, "Two commands": { in: []RuleSegment{ { Banner: banners[0], Command: commands[0], Privilege: privileges[0], }, { Banner: banners[0], Command: commands[1], Privilege: privileges[0], }, }, out: []ReadBannerRule{ { Command: commands[0], Banners: []BannerPrivOverrides{ { Banner: banners[0], Privileges: []Privilege{ privileges[0], }, }, }, }, { Command: commands[1], Banners: []BannerPrivOverrides{ { Banner: banners[0], Privileges: []Privilege{ privileges[0], }, }, }, }, }, }, "No banner": { in: []RuleSegment{ { Command: commands[0], Privilege: privileges[0], }, }, err: fmt.Errorf("error in assembeGetBannerRule: Banner was nil"), }, } for name, tc := range tests { tc := tc t.Run(name, func(t *testing.T) { t.Parallel() res, err := assembleGetBannerRule(tc.in) assert.Equal(t, tc.err, err) assert.Equal(t, tc.out, res) }) } } type MockDS struct { funcResults map[string]any funcErrors map[string]error Dataset } func newMockDS(res map[string]any, errs map[string]error) MockDS { return MockDS{funcResults: res, funcErrors: errs} } func (mds MockDS) ReadDefaultRulesForCommand(_ context.Context, _ string) (rules []RuleSegment, err error) { res, ok := mds.funcResults["ReadDefaultRulesForCommand"].([]RuleSegment) if !ok { return rules, fmt.Errorf("key not found in funcResults or incorrect type") } err, ok = mds.funcErrors["ReadDefaultRulesForCommand"] if !ok { return rules, fmt.Errorf("key not found in funcErrors") } return res, err } // banner admin methods func (mds MockDS) ReadBannerRulesForCommand(_ context.Context, _ string) (rules []RuleSegment, err error) { res, ok := mds.funcResults["ReadBannerRulesForCommand"].([]RuleSegment) if !ok { return rules, fmt.Errorf("key not found in funcResults or incorrect type") } err, ok = mds.funcErrors["ReadBannerRulesForCommand"] if !ok { return rules, fmt.Errorf("key not found in funcErrors") } return res, err } func (mds MockDS) ReadCommand(_ context.Context, _ string) (command Command, err error) { res, ok := mds.funcResults["ReadCommand"].(Command) if !ok { return command, fmt.Errorf("key not found in funcResults or incorrect type") } err, ok = mds.funcErrors["ReadCommand"] if !ok { return command, fmt.Errorf("key not found in funcErrors") } return res, err } var ( testBannerRuleSegment = []RuleSegment{ { Banner: Banner{ BannerName: "testBanner", BannerID: "testBannerID", }, Command: Command{ Name: "testCommand", ID: "testComID", }, Privilege: Privilege{ Name: "testPrivilege", ID: "testPrivID", }, }, } testDefaultRuleSegment = []RuleSegment{{ Command: Command{ Name: "testCommand", ID: "testComID", }, Privilege: Privilege{ Name: "testPrivilege", ID: "testPrivID", }, }} ) func TestReadAllRulesForCommand(t *testing.T) { t.Parallel() tests := map[string]struct { funcResults map[string]interface{} funcErrors map[string]error finalError error expectedRes RuleWithOverrides }{ "No errors": { funcResults: map[string]interface{}{ "ReadBannerRulesForCommand": testBannerRuleSegment, "ReadDefaultRulesForCommand": testDefaultRuleSegment, }, funcErrors: map[string]error{ "ReadBannerRulesForCommand": nil, "ReadDefaultRulesForCommand": nil, }, finalError: nil, expectedRes: RuleWithOverrides{ Command: Command{ Name: "testCommand", ID: "testComID", }, Banners: []BannerPrivOverrides{{ Privileges: []Privilege{{ Name: "testPrivilege", ID: "testPrivID", }, }, Banner: Banner{ BannerName: "testBanner", BannerID: "testBannerID", }, }, }, Default: DefaultRule{Privileges: []Privilege{testDefaultRuleSegment[0].Privilege}}, }, }, "Error with ReadBannerRulesForCommand": { funcResults: map[string]interface{}{ "ReadBannerRulesForCommand": testBannerRuleSegment, "ReadDefaultRulesForCommand": testDefaultRuleSegment, }, funcErrors: map[string]error{ "ReadBannerRulesForCommand": fmt.Errorf("an error occurred in ReadBannerRulesForCommand"), "ReadDefaultRulesForCommand": nil, }, finalError: fmt.Errorf("error finding banner rules: error querying for rules: an error occurred in ReadBannerRulesForCommand"), }, "Error with ReadDefaultRulesForCommand": { funcResults: map[string]interface{}{ "ReadBannerRulesForCommand": testBannerRuleSegment, "ReadDefaultRulesForCommand": testDefaultRuleSegment, }, funcErrors: map[string]error{ "ReadBannerRulesForCommand": nil, "ReadDefaultRulesForCommand": fmt.Errorf("an error occurred in ReadRulesDefaultRulesForCommand"), }, finalError: fmt.Errorf("error finding default rules: an error occurred in ReadRulesDefaultRulesForCommand"), }, "No command in database": { funcResults: map[string]interface{}{ "ReadDefaultRulesForCommand": []RuleSegment(nil), "ReadBannerRulesForCommand": []RuleSegment(nil), "ReadCommand": Command{}, }, funcErrors: map[string]error{ "ReadDefaultRulesForCommand": nil, "ReadBannerRulesForCommand": nil, "ReadCommand": nil, }, expectedRes: RuleWithOverrides{}, }, "No Banner command": { funcResults: map[string]interface{}{ "ReadBannerRulesForCommand": []RuleSegment{}, "ReadDefaultRulesForCommand": testDefaultRuleSegment, }, funcErrors: map[string]error{ "ReadBannerRulesForCommand": nil, "ReadDefaultRulesForCommand": nil, }, finalError: nil, expectedRes: RuleWithOverrides{ Command: Command{ Name: "testCommand", ID: "testComID", }, Banners: []BannerPrivOverrides{}, Default: DefaultRule{Privileges: []Privilege{testDefaultRuleSegment[0].Privilege}}, }, }, "No default command": { funcResults: map[string]interface{}{ "ReadBannerRulesForCommand": testBannerRuleSegment, "ReadDefaultRulesForCommand": []RuleSegment{}, }, funcErrors: map[string]error{ "ReadBannerRulesForCommand": nil, "ReadDefaultRulesForCommand": nil, }, finalError: nil, expectedRes: RuleWithOverrides{ Command: Command{ Name: "testCommand", ID: "testComID", }, Banners: []BannerPrivOverrides{{ Privileges: []Privilege{{ Name: "testPrivilege", ID: "testPrivID", }}, Banner: Banner{ BannerName: "testBanner", BannerID: "testBannerID", }, }}, }, }, } for name, tc := range tests { tc := tc t.Run(name, func(t *testing.T) { t.Parallel() mds := newMockDS(tc.funcResults, tc.funcErrors) reng := RulesEngine{} reng.ds = mds res, err := reng.ReadAllRulesForCommand(context.Background(), "testCommand") assert.Equal(t, tc.finalError, err) if err == nil && tc.finalError == nil { assert.EqualValues(t, tc.expectedRes, res) } }) } } func TestSplitPostRulesToBannerSegment(t *testing.T) { t.Parallel() tests := map[string]struct { inputBanner string inputRules []WriteRule expOut []RuleSegment }{ "simple": { inputBanner: "myBanner", inputRules: []WriteRule{ {Command: "ls", Privileges: []string{"read"}}, }, expOut: []RuleSegment{ {Banner: Banner{BannerName: "myBanner"}, Command: Command{Name: "ls"}, Privilege: Privilege{Name: "read"}}, }, }, "Multiple command": { inputBanner: "myBanner", inputRules: []WriteRule{ {Command: "ls", Privileges: []string{"read"}}, {Command: "cat", Privileges: []string{"read"}}, }, expOut: []RuleSegment{ {Banner: Banner{BannerName: "myBanner"}, Command: Command{Name: "ls"}, Privilege: Privilege{Name: "read"}}, {Banner: Banner{BannerName: "myBanner"}, Command: Command{Name: "cat"}, Privilege: Privilege{Name: "read"}}, }, }, "Multiple Privileges": { inputBanner: "myBanner", inputRules: []WriteRule{ {Command: "ls", Privileges: []string{"read", "write"}}, }, expOut: []RuleSegment{ {Banner: Banner{BannerName: "myBanner"}, Command: Command{Name: "ls"}, Privilege: Privilege{Name: "read"}}, {Banner: Banner{BannerName: "myBanner"}, Command: Command{Name: "ls"}, Privilege: Privilege{Name: "write"}}, }, }, "Multiple Everything": { inputBanner: "aBanner", inputRules: []WriteRule{ {Command: "ls", Privileges: []string{"read", "write"}}, {Command: "cat", Privileges: []string{"read", "readOnly"}}, }, expOut: []RuleSegment{ {Banner: Banner{BannerName: "aBanner"}, Command: Command{Name: "ls"}, Privilege: Privilege{Name: "read"}}, {Banner: Banner{BannerName: "aBanner"}, Command: Command{Name: "ls"}, Privilege: Privilege{Name: "write"}}, {Banner: Banner{BannerName: "aBanner"}, Command: Command{Name: "cat"}, Privilege: Privilege{Name: "read"}}, {Banner: Banner{BannerName: "aBanner"}, Command: Command{Name: "cat"}, Privilege: Privilege{Name: "readOnly"}}, }, }, } for name, tc := range tests { name := name tc := tc t.Run(name, func(t *testing.T) { t.Parallel() out := splitPostRulesToBannerSegment(tc.inputBanner, tc.inputRules) assert.Equal(t, tc.expOut, out) }) } } type readBannerRulesForCommandAndBannerMock struct { Dataset commandName string bannerName string retOut []RuleSegment retErr error } type readBannerRulesForCommandMock struct { Dataset commandName string retOut []RuleSegment retErr error } func (m *readBannerRulesForCommandMock) ReadBannerRulesForCommand(_ context.Context, commandName string) ([]RuleSegment, error) { m.commandName = commandName return m.retOut, m.retErr } func TestReadBannerRulesForCommand(t *testing.T) { t.Parallel() tests := map[string]struct { commandName string retOut []RuleSegment retErr error expOut ReadBannerRule expErr assert.ErrorAssertionFunc }{ "Standard": { commandName: "ls", retOut: []RuleSegment{ {Command: Command{Name: "ls", ID: "a"}, Privilege: Privilege{Name: "ea-read", ID: "b"}, Banner: Banner{BannerName: "myBanner", BannerID: "c"}}, {Command: Command{Name: "ls", ID: "a"}, Privilege: Privilege{Name: "ea-write", ID: "e"}, Banner: Banner{BannerName: "myBanner", BannerID: "c"}}, {Command: Command{Name: "ls", ID: "a"}, Privilege: Privilege{Name: "ea-read", ID: "b"}, Banner: Banner{BannerName: "myBanner2", BannerID: "d"}}, }, retErr: nil, expOut: ReadBannerRule{ Command: Command{Name: "ls", ID: "a"}, Banners: []BannerPrivOverrides{ { Banner: Banner{BannerName: "myBanner", BannerID: "c"}, Privileges: []Privilege{ {Name: "ea-read", ID: "b"}, {Name: "ea-write", ID: "e"}, }, }, { Banner: Banner{BannerName: "myBanner2", BannerID: "d"}, Privileges: []Privilege{ {Name: "ea-read", ID: "b"}, }, }, }, }, expErr: assert.NoError, }, "No output": { commandName: "cat", retOut: nil, retErr: nil, expOut: ReadBannerRule{}, expErr: assert.NoError, }, "Error": { commandName: "ls", retOut: nil, retErr: fmt.Errorf("an error"), expOut: ReadBannerRule{}, expErr: assert.Error, }, "Too Many commands returned": { commandName: "ls", retOut: []RuleSegment{ {Command: Command{Name: "ls", ID: "a"}, Privilege: Privilege{Name: "ea-read", ID: "b"}, Banner: Banner{BannerName: "myBanner", BannerID: "c"}}, {Command: Command{Name: "ls", ID: "a"}, Privilege: Privilege{Name: "ea-write", ID: "e"}, Banner: Banner{BannerName: "myBanner", BannerID: "c"}}, {Command: Command{Name: "ls", ID: "a"}, Privilege: Privilege{Name: "ea-read", ID: "b"}, Banner: Banner{BannerName: "myBanner2", BannerID: "d"}}, {Command: Command{Name: "cat", ID: "b"}, Privilege: Privilege{Name: "ea-read", ID: "b"}, Banner: Banner{BannerName: "myBanner", BannerID: "c"}}, }, retErr: nil, expOut: ReadBannerRule{}, expErr: assert.Error, }, } for name, tc := range tests { tc := tc t.Run(name, func(t *testing.T) { t.Parallel() ds := readBannerRulesForCommandMock{ retOut: tc.retOut, retErr: tc.retErr, } re := New(&ds) out, err := re.ReadBannerRulesForCommand(context.Background(), tc.commandName) assert.Equal(t, tc.commandName, ds.commandName) assert.Equal(t, tc.expOut, out) tc.expErr(t, err) }) } } func (m *readBannerRulesForCommandAndBannerMock) ReadBannerRulesForCommandAndBanner(_ context.Context, bannerName string, commandName string) ([]RuleSegment, error) { m.commandName = commandName m.bannerName = bannerName return m.retOut, m.retErr } func TestReadBannerRulesForCommandAndBanner(t *testing.T) { t.Parallel() tests := map[string]struct { banneName string commandName string retOut []RuleSegment retErr error expOut Rule expErr assert.ErrorAssertionFunc }{ "Standard": { banneName: "myBanner", commandName: "ls", retOut: []RuleSegment{ {Command: Command{Name: "ls", ID: "a"}, Privilege: Privilege{Name: "ea-read", ID: "b"}}, {Command: Command{Name: "ls", ID: "a"}, Privilege: Privilege{Name: "ea-write", ID: "e"}}, }, retErr: nil, expOut: Rule{ Command: Command{Name: "ls", ID: "a"}, Privileges: []Privilege{ {Name: "ea-read", ID: "b"}, {Name: "ea-write", ID: "e"}, }, }, expErr: assert.NoError, }, "No output": { banneName: "myBanner2", commandName: "cat", retOut: nil, retErr: nil, expOut: Rule{}, expErr: assert.NoError, }, "Error": { banneName: "myBanner", commandName: "ls", retOut: nil, retErr: fmt.Errorf("an error"), expOut: Rule{}, expErr: assert.Error, }, "Too Many commands returned": { banneName: "myBanner", commandName: "ls", retOut: []RuleSegment{ {Command: Command{Name: "ls", ID: "a"}, Privilege: Privilege{Name: "ea-read", ID: "b"}, Banner: Banner{BannerName: "myBanner", BannerID: "c"}}, {Command: Command{Name: "ls", ID: "a"}, Privilege: Privilege{Name: "ea-write", ID: "e"}, Banner: Banner{BannerName: "myBanner", BannerID: "c"}}, {Command: Command{Name: "ls", ID: "a"}, Privilege: Privilege{Name: "ea-read", ID: "b"}, Banner: Banner{BannerName: "myBanner2", BannerID: "d"}}, {Command: Command{Name: "cat", ID: "b"}, Privilege: Privilege{Name: "ea-read", ID: "b"}, Banner: Banner{BannerName: "myBanner", BannerID: "c"}}, }, retErr: nil, expOut: Rule{}, expErr: assert.Error, }, } for name, tc := range tests { tc := tc t.Run(name, func(t *testing.T) { t.Parallel() ds := readBannerRulesForCommandAndBannerMock{ retOut: tc.retOut, retErr: tc.retErr, } re := New(&ds) out, err := re.ReadBannerRulesForCommandAndBanner(context.Background(), tc.banneName, tc.commandName) assert.Equal(t, tc.banneName, ds.bannerName) assert.Equal(t, tc.commandName, ds.commandName) assert.Equal(t, tc.expOut, out) tc.expErr(t, err) }) } }