diff --git a/internal/controller/controller.go b/internal/controller/controller.go index 149c081d..8c732463 100644 --- a/internal/controller/controller.go +++ b/internal/controller/controller.go @@ -6,6 +6,7 @@ import ( "reflect" "soarca/internal/capability" + "soarca/internal/capability/http" "soarca/internal/capability/ssh" "soarca/internal/decomposer" "soarca/internal/executer" @@ -16,7 +17,7 @@ import ( "github.com/gin-gonic/gin" mongo "soarca/database/mongodb" - playbookRepo "soarca/database/playbook" + playbookrepository "soarca/database/playbook" routes "soarca/routes" ) @@ -28,34 +29,26 @@ func init() { log = logger.Logger(reflect.TypeOf(Empty{}).PkgPath(), logger.Info, "", logger.Json) } -func InitializeAppComponents() error { - app := gin.New() - log.Info("Testing if this works") +type Controller struct { + playbookRepo playbookrepository.IPlaybookRepository +} - initDatabase := utils.GetEnv("DATABASE", "false") - if initDatabase == "true" { - errDatabase := InitializeDatabase(app) - if errDatabase != nil { - log.Error("Failed to init core") - return errDatabase - } - } - errCore := InitializeCore(app) +var mainController = Controller{} - if errCore != nil { - log.Error("Failed to init core") - return errCore - } +func (controller *Controller) NewDecomposer() decomposer.IDecomposer { + ssh := new(ssh.SshCapability) + capabilities := map[string]capability.ICapability{ssh.GetType(): ssh} - port := utils.GetEnv("PORT", "8080") - err := app.Run(":" + port) - if err != nil { - log.Error("failed to run gin") - } - return err + http := new(http.HttpCapability) + capabilities[http.GetType()] = http + + executer := executer.New(capabilities) + guid := new(guid.Guid) + decompose := decomposer.New(executer, guid) + return decompose } -func InitializeDatabase(app *gin.Engine) error { +func (controller *Controller) setupDatabase() error { mongo.LoadComponent() log.Info("SOARCA API Trying to start") @@ -71,28 +64,59 @@ func InitializeDatabase(app *gin.Engine) error { if err != nil { return err } - // defer database.GetMongoClient().CloseMongoDB() + controller.playbookRepo = playbookrepository.SetupPlaybookRepository(mongo.GetCacaoRepo(), mongo.DefaultLimitOpts()) - playbookRepo := playbookRepo.SetupPlaybookRepository(mongo.GetCacaoRepo(), mongo.DefaultLimitOpts()) + return nil +} - // setup database routes - err = routes.Database(app, playbookRepo) +func (controller *Controller) GetDatabaseInstance() playbookrepository.IPlaybookRepository { + return controller.playbookRepo +} + +func Initialize() error { + app := gin.New() + log.Info("Testing if info log works") + log.Debug("Testing if debug log works") + log.Trace("Testing if Trace log works") + + errCore := initializeCore(app) + + if errCore != nil { + log.Error("Failed to init core") + return errCore + } + + port := utils.GetEnv("PORT", "8080") + err := app.Run(":" + port) + if err != nil { + log.Error("failed to run gin") + } + log.Info("exit") return err } -func InitializeCore(app *gin.Engine) error { - ssh := new(ssh.SshCapability) - capabilities := map[string]capability.ICapability{ssh.GetType(): ssh} - executer := executer.New(capabilities) - guid := new(guid.Guid) - decompose := decomposer.New(executer, guid) +func initializeCore(app *gin.Engine) error { - err := routes.Api(app, decompose) + err := routes.Api(app, &mainController) if err != nil { log.Error(err) return err } + + initDatabase := utils.GetEnv("DATABASE", "false") + if initDatabase == "true" { + err = mainController.setupDatabase() + if err != nil { + log.Error(err) + return err + } + err = routes.Database(app, &mainController) + if err != nil { + log.Error(err) + return err + } + } routes.Logging(app) routes.Swagger(app) return err diff --git a/internal/controller/database/controller_database.go b/internal/controller/database/controller_database.go new file mode 100644 index 00000000..2525bd25 --- /dev/null +++ b/internal/controller/database/controller_database.go @@ -0,0 +1,9 @@ +package database + +import ( + playbookrepository "soarca/database/playbook" +) + +type IController interface { + GetDatabaseInstance() playbookrepository.IPlaybookRepository +} diff --git a/internal/controller/decomposer/controller_decomposer.go b/internal/controller/decomposer/controller_decomposer.go new file mode 100644 index 00000000..15bbff29 --- /dev/null +++ b/internal/controller/decomposer/controller_decomposer.go @@ -0,0 +1,9 @@ +package decomposer + +import ( + "soarca/internal/decomposer" +) + +type IController interface { + NewDecomposer() decomposer.IDecomposer +} diff --git a/main.go b/main.go index 2bd0129d..2c83dcd3 100644 --- a/main.go +++ b/main.go @@ -42,7 +42,7 @@ func main() { log.Warning("Failed to read env variable, but will continue") } - errinit := controller.InitializeAppComponents() + errinit := controller.Initialize() if errinit != nil { log.Fatal("Something Went wrong with setting-up the app, msg: ", errinit) panic(errinit) diff --git a/routes/playbook/playbook_api.go b/routes/playbook/playbook_api.go index 6803990c..3dae458b 100644 --- a/routes/playbook/playbook_api.go +++ b/routes/playbook/playbook_api.go @@ -5,19 +5,20 @@ import ( "net/http" "strconv" - playbookRepository "soarca/database/playbook" + playbookrepository "soarca/database/playbook" + "soarca/internal/controller/database" "github.com/gin-gonic/gin" ) // A PlaybookController implements the playbook API endpoints is dependent on a database. type playbookController struct { - playbookRepo playbookRepository.IPlaybookRepository + playbookRepo playbookrepository.IPlaybookRepository } // NewPlaybookController makes a new instance of playbookControler -func NewPlaybookController(playbookRepo playbookRepository.IPlaybookRepository) *playbookController { - return &playbookController{playbookRepo: playbookRepo} +func NewPlaybookController(controller database.IController) *playbookController { + return &playbookController{playbookRepo: controller.GetDatabaseInstance()} } // getAllPlaybooks GET handler for obtaining all the playbooks in the database and return this to the gin context in json format diff --git a/routes/playbook/playbook_endpoints.go b/routes/playbook/playbook_endpoints.go index 7608c086..284cfc99 100644 --- a/routes/playbook/playbook_endpoints.go +++ b/routes/playbook/playbook_endpoints.go @@ -1,7 +1,7 @@ package playbook import ( - playbookRepository "soarca/database/playbook" + "soarca/internal/controller/database" "github.com/gin-gonic/gin" ) @@ -12,8 +12,8 @@ import ( // GET /playbook/playbook-id // PUT /playbook/playbook-id // DELETE /playbook/playbook-id -func Routes(route *gin.Engine, playbookRepo playbookRepository.IPlaybookRepository) { - playbookController := NewPlaybookController(playbookRepo) +func Routes(route *gin.Engine, controller database.IController) { + playbookController := NewPlaybookController(controller) playbook := route.Group("/playbook") { playbook.GET("/", playbookController.getAllPlaybooks) diff --git a/routes/router.go b/routes/router.go index d2e36d9f..e5c7d4b1 100644 --- a/routes/router.go +++ b/routes/router.go @@ -1,8 +1,8 @@ package routes import ( - playbookRepository "soarca/database/playbook" - "soarca/internal/decomposer" + "soarca/internal/controller/database" + "soarca/internal/controller/decomposer" coa_routes "soarca/routes/coa" operator "soarca/routes/operator" playbook_routes "soarca/routes/playbook" @@ -19,9 +19,9 @@ import ( // Requires database dependency injection. func Database(app *gin.Engine, - playbookRepo playbookRepository.IPlaybookRepository, + controller database.IController, ) error { - playbook_routes.Routes(app, playbookRepo) + playbook_routes.Routes(app, controller) return nil } @@ -30,12 +30,12 @@ func Logging(app *gin.Engine) { } func Api(app *gin.Engine, - decomposer decomposer.IDecomposer, + controller decomposer.IController, ) error { log.Trace("Trying to setup all Routes") // gin.SetMode(gin.ReleaseMode) - trigger_api := trigger.New(decomposer) + trigger_api := trigger.New(controller) coa_routes.Routes(app) diff --git a/routes/trigger/trigger_api.go b/routes/trigger/trigger_api.go index bc17c487..9da8f7a5 100644 --- a/routes/trigger/trigger_api.go +++ b/routes/trigger/trigger_api.go @@ -5,7 +5,7 @@ import ( "net/http" "reflect" - "soarca/internal/decomposer" + "soarca/internal/controller/decomposer" "soarca/logger" "soarca/models/decoder" "soarca/routes/error" @@ -26,16 +26,18 @@ func init() { } type TriggerApi struct { - decomposer decomposer.IDecomposer + controller decomposer.IController } -func New(decomposer decomposer.IDecomposer) *TriggerApi { +func New(controller decomposer.IController) *TriggerApi { instance := TriggerApi{} - instance.decomposer = decomposer + instance.controller = controller return &instance } func (trigger *TriggerApi) Execute(context *gin.Context) { + // create new decomposer when execute is called + decomposer := trigger.controller.NewDecomposer() jsonData, errIo := io.ReadAll(context.Request.Body) if errIo != nil { log.Error("failed") @@ -52,7 +54,7 @@ func (trigger *TriggerApi) Execute(context *gin.Context) { "POST /trigger/playbook", "") return } - executionDetail, errDecomposer := trigger.decomposer.Execute(*playbook) + executionDetail, errDecomposer := decomposer.Execute(*playbook) if errDecomposer != nil { error.SendErrorResponse(context, http.StatusBadRequest, "Failed to decode playbook", diff --git a/test/unittest/mocks/mock_controller/database/mock_database_controller.go b/test/unittest/mocks/mock_controller/database/mock_database_controller.go new file mode 100644 index 00000000..ddabc4a8 --- /dev/null +++ b/test/unittest/mocks/mock_controller/database/mock_database_controller.go @@ -0,0 +1,16 @@ +package mock_database_controller + +import ( + playbookrepository "soarca/database/playbook" + + "github.com/stretchr/testify/mock" +) + +type Mock_Controller struct { + mock.Mock +} + +func (mock *Mock_Controller) GetDatabaseInstance() playbookrepository.IPlaybookRepository { + args := mock.Called() + return args.Get(0).(playbookrepository.IPlaybookRepository) +} diff --git a/test/unittest/mocks/mock_controller/decomposer/mock_decomposer_controller.go b/test/unittest/mocks/mock_controller/decomposer/mock_decomposer_controller.go new file mode 100644 index 00000000..2138b283 --- /dev/null +++ b/test/unittest/mocks/mock_controller/decomposer/mock_decomposer_controller.go @@ -0,0 +1,16 @@ +package mock_decomposer_controller + +import ( + "soarca/internal/decomposer" + + "github.com/stretchr/testify/mock" +) + +type Mock_Controller struct { + mock.Mock +} + +func (mock *Mock_Controller) NewDecomposer() decomposer.IDecomposer { + args := mock.Called() + return args.Get(0).(decomposer.IDecomposer) +} diff --git a/test/unittest/routes/playbook_api/playbook_api_test.go b/test/unittest/routes/playbook_api/playbook_api_test.go index 05bc3779..4208f6ec 100644 --- a/test/unittest/routes/playbook_api/playbook_api_test.go +++ b/test/unittest/routes/playbook_api/playbook_api_test.go @@ -14,6 +14,7 @@ import ( "soarca/models/cacao" "soarca/models/decoder" playbookRouter "soarca/routes/playbook" + mock_database_controller "soarca/test/unittest/mocks/mock_controller/database" mock_playbook "soarca/test/unittest/mocks/playbook" "github.com/gin-gonic/gin" @@ -35,6 +36,8 @@ const jsonTestPlayBookMeta = `{ func TestGetPlaybookMetas(t *testing.T) { app := gin.New() + + mockController := new(mock_database_controller.Mock_Controller) mockPlaybook := new(mock_playbook.MockPlaybook) var dummyPlaybookMeta api.PlaybookMeta @@ -56,10 +59,12 @@ func TestGetPlaybookMetas(t *testing.T) { t.Fail() return } + mockController.On("GetDatabaseInstance").Return(mockPlaybook) + mockPlaybook.On("GetPlaybookMetas").Return(dummyPlaybookMetas, nil) w := httptest.NewRecorder() - playbookRouter.Routes(app, mockPlaybook) + playbookRouter.Routes(app, mockController) req, _ := http.NewRequest("GET", "/playbook/meta/", nil) app.ServeHTTP(w, req) @@ -80,7 +85,9 @@ func TestGetPlaybooks(t *testing.T) { app := gin.New() gin.SetMode(gin.DebugMode) + mockController := new(mock_database_controller.Mock_Controller) mockPlaybook := new(mock_playbook.MockPlaybook) + mockController.On("GetDatabaseInstance").Return(mockPlaybook) dummyPlaybook := decoder.DecodeValidate(byteValue) if dummyPlaybook == nil { fmt.Println("got an nil playbook pointer") @@ -101,7 +108,7 @@ func TestGetPlaybooks(t *testing.T) { } mockPlaybook.On("GetPlaybooks").Return(playbooks, nil) w := httptest.NewRecorder() - playbookRouter.Routes(app, mockPlaybook) + playbookRouter.Routes(app, mockController) req, _ := http.NewRequest("GET", "/playbook/", nil) app.ServeHTTP(w, req) @@ -120,7 +127,9 @@ func TestGetPlaybookByID(t *testing.T) { byteValue, _ := io.ReadAll(jsonFile) app := gin.New() + mockController := new(mock_database_controller.Mock_Controller) mockPlaybook := new(mock_playbook.MockPlaybook) + mockController.On("GetDatabaseInstance").Return(mockPlaybook) dummyPlaybook := decoder.DecodeValidate(byteValue) mockPlaybook.On("Read", dummyPlaybook.ID).Return(*dummyPlaybook, nil) marshalledDummyPlaybook, err := json.Marshal(dummyPlaybook) @@ -131,7 +140,7 @@ func TestGetPlaybookByID(t *testing.T) { } w := httptest.NewRecorder() - playbookRouter.Routes(app, mockPlaybook) + playbookRouter.Routes(app, mockController) req, _ := http.NewRequest("GET", fmt.Sprintf("/playbook/%s", dummyPlaybook.ID), nil) app.ServeHTTP(w, req) @@ -150,7 +159,9 @@ func TestPostPlaybook(t *testing.T) { byteValue, _ := io.ReadAll(jsonFile) app := gin.New() + mockController := new(mock_database_controller.Mock_Controller) mockPlaybook := new(mock_playbook.MockPlaybook) + mockController.On("GetDatabaseInstance").Return(mockPlaybook) dummyPlaybook := decoder.DecodeValidate(byteValue) if dummyPlaybook == nil { @@ -168,7 +179,7 @@ func TestPostPlaybook(t *testing.T) { mockPlaybook.On("Create", &pointerDummyObject).Return(*dummyPlaybook, nil) w := httptest.NewRecorder() - playbookRouter.Routes(app, mockPlaybook) + playbookRouter.Routes(app, mockController) req, _ := http.NewRequest("POST", "/playbook/", bytes.NewBuffer(marshalledDummyPlaybook)) app.ServeHTTP(w, req) @@ -187,7 +198,9 @@ func TestDeletePlaybook(t *testing.T) { byteValue, _ := io.ReadAll(jsonFile) app := gin.New() + mockController := new(mock_database_controller.Mock_Controller) mockPlaybook := new(mock_playbook.MockPlaybook) + mockController.On("GetDatabaseInstance").Return(mockPlaybook) dummyPlaybook := decoder.DecodeValidate(byteValue) if dummyPlaybook == nil { @@ -197,7 +210,7 @@ func TestDeletePlaybook(t *testing.T) { } mockPlaybook.On("Delete", dummyPlaybook.ID).Return(nil) w := httptest.NewRecorder() - playbookRouter.Routes(app, mockPlaybook) + playbookRouter.Routes(app, mockController) req, _ := http.NewRequest("DELETE", fmt.Sprintf("/playbook/%s", dummyPlaybook.ID), nil) app.ServeHTTP(w, req) assert.Equal(t, 200, w.Code) @@ -213,7 +226,9 @@ func TestUpdatePlaybook(t *testing.T) { byteValue, _ := io.ReadAll(jsonFile) app := gin.New() + mockController := new(mock_database_controller.Mock_Controller) mockPlaybook := new(mock_playbook.MockPlaybook) + mockController.On("GetDatabaseInstance").Return(mockPlaybook) dummyPlaybook := decoder.DecodeValidate(byteValue) if dummyPlaybook == nil { @@ -231,7 +246,7 @@ func TestUpdatePlaybook(t *testing.T) { mockPlaybook.On("Update", dummyPlaybook.ID, &pointerDummyObject).Return(*dummyPlaybook, nil) w := httptest.NewRecorder() - playbookRouter.Routes(app, mockPlaybook) + playbookRouter.Routes(app, mockController) req, _ := http.NewRequest("PUT", fmt.Sprintf("/playbook/%s", dummyPlaybook.ID), bytes.NewBuffer(marshalledDummyPlaybook)) app.ServeHTTP(w, req) diff --git a/test/unittest/routes/trigger_api/tigger_api_test.go b/test/unittest/routes/trigger_api/tigger_api_test.go index cbc48270..cedefe5b 100644 --- a/test/unittest/routes/trigger_api/tigger_api_test.go +++ b/test/unittest/routes/trigger_api/tigger_api_test.go @@ -12,6 +12,7 @@ import ( "soarca/internal/decomposer" "soarca/models/cacao" "soarca/routes/trigger" + mock_decomposer_controller "soarca/test/unittest/mocks/mock_controller/decomposer" "soarca/test/unittest/mocks/mock_decomposer" "github.com/gin-gonic/gin" @@ -30,11 +31,13 @@ func TestExecutionOfPlaybook(t *testing.T) { app := gin.New() gin.SetMode(gin.DebugMode) mock_decomposer := new(mock_decomposer.Mock_Decomposer) + mock_controller := new(mock_decomposer_controller.Mock_Controller) + mock_controller.On("NewDecomposer").Return(mock_decomposer) playbook := cacao.Decode(byteValue) mock_decomposer.On("Execute", *playbook).Return(&decomposer.ExecutionDetails{}, nil) recorder := httptest.NewRecorder() - trigger_api := trigger.New(mock_decomposer) + trigger_api := trigger.New(mock_controller) trigger.Routes(app, trigger_api) request, err := http.NewRequest("POST", "/trigger/playbook", bytes.NewBuffer(byteValue))