package server import ( "bytes" "encoding/base64" "log" "testing" "code.fbi.h-da.de/danet/gosdn/controller/conflict" "code.fbi.h-da.de/danet/gosdn/controller/interfaces/networkelement" "code.fbi.h-da.de/danet/gosdn/controller/interfaces/plugin" rbacInterfaces "code.fbi.h-da.de/danet/gosdn/controller/interfaces/rbac" "code.fbi.h-da.de/danet/gosdn/controller/mocks" "code.fbi.h-da.de/danet/gosdn/controller/rbac" "code.fbi.h-da.de/danet/gosdn/controller/store" "github.com/google/uuid" "github.com/sethvargo/go-password/password" "golang.org/x/crypto/argon2" ) const pndID = "2043519e-46d1-4963-9a8e-d99007e104b8" const pluginID = "22ecbd5e-37de-43e3-be56-358f9716fd7d" const pendingChangeID = "0992d600-f7d4-4906-9559-409b04d59a5f" const committedChangeID = "804787d6-e5a8-4dba-a1e6-e73f96b0119e" const mneID = "7e0ed8cc-ebf5-46fa-9794-741494914883" var hostname = "manfred" var pndUUID uuid.UUID var pluginUUID uuid.UUID var pendingChangeUUID uuid.UUID var committedChangeUUID uuid.UUID var mneUUID uuid.UUID var mockPnd *mocks.NetworkDomain var mockNetworkElement networkelement.NetworkElement // Name of this file requires _test at the end, because of how the availability of varibales is handled in test files of go packages. // Does not include actual file tests! const adminID = "5c248a22-8eb7-48cf-b392-45680a1863a5" const userID = "57005d13-7a4d-493d-a02b-50ca51c40197" const adminRoleID = "126683ae-5ff2-43ee-92f7-0e2b936f8c77" const randomRoleName = "bertram" var adminRoleMap = map[string]string{pndID: "adminTestRole"} var userRoleMap = map[string]string{pndID: "userTestRole"} func clearAndCreateAuthTestSetup(userService rbacInterfaces.UserService, roleService rbacInterfaces.RoleService) error { //clear setup if changed storedUsers, err := userService.GetAll() if err != nil { return err } for _, u := range storedUsers { err = userService.Delete(u) if err != nil { return err } } storedRoles, err := roleService.GetAll() if err != nil { return err } for _, r := range storedRoles { err = roleService.Delete(r) if err != nil { return err } } // create dataset err = createTestUsers(userService) if err != nil { return err } err = createTestRoles(roleService) if err != nil { return err } return nil } func createTestUsers(userService rbacInterfaces.UserService) error { randomRoleMap := map[string]string{pndID: randomRoleName} // Generate a salt that is 16 characters long with 3 digits, 0 symbols, // allowing upper and lower case letters, disallowing repeat characters. salt, err := password.Generate(16, 3, 0, true, false) if err != nil { return err } testAdminPWD := createHashedAndSaltedPassword("admin", salt) testUserPWD := createHashedAndSaltedPassword("user", salt) testRandPWD := createHashedAndSaltedPassword("aurelius", salt) wrongTokens := []string{"Wrong token"} users := []rbac.User{ {UserID: uuid.MustParse(adminID), UserName: "testAdmin", Roles: adminRoleMap, Password: testAdminPWD}, {UserID: uuid.MustParse(userID), UserName: "testUser", Roles: userRoleMap, Password: testUserPWD}, {UserID: uuid.New(), UserName: "testRandom", Roles: randomRoleMap, Password: testRandPWD, Tokens: wrongTokens}, } wrongTokens = []string{} for _, u := range users { err := userService.Add(rbac.NewUser(u.ID(), u.Name(), u.Roles, u.Password, wrongTokens, salt, conflict.Metadata{ResourceVersion: 0})) if err != nil { return err } } return nil } func createTestRoles(roleService rbacInterfaces.RoleService) error { roles := []rbac.Role{ { RoleID: uuid.MustParse(adminRoleID), RoleName: "adminTestRole", Description: "Admin", Permissions: []string{ "/gosdn.core.CoreService/GetPnd", "/gosdn.core.CoreService/GetPndList", "/gosdn.rbac.UserService/GetUsers", "/gosdn.plugin_internal.PluginInternalService/GetPluginSchema", }, }, { RoleID: uuid.New(), RoleName: "userTestRole", Description: "User", Permissions: []string{ "/gosdn.pnd.PndService/GetChangeList", }, }, { RoleID: uuid.New(), RoleName: randomRoleName, Description: "Not a role", Permissions: []string{ "nope", }, }, } for _, r := range roles { err := roleService.Add(rbac.NewRole(r.ID(), r.Name(), r.Description, r.Permissions)) if err != nil { return err } } return nil } // This is needed as a workaround for a bug where the output of the getUser test falsely was // that it failed while actually passing. Apparently, this can happen when loggers write // the output of test cases. // Solution found here: https://github.com/gotestyourself/gotestsum/issues/141#issuecomment-686243110 func patchLogger(t *testing.T) { orig := log.Writer() buf := new(bytes.Buffer) log.SetOutput(buf) t.Cleanup(func() { // optionally check t.Failed here if you only want to print logs on failure t.Log(buf.String()) log.SetOutput(orig) }) } // Creates a token to be used in auth interceptor tests. If validTokenRequired is set as true, the generated token will also // be attached to the provided user. Else the user won't have the token and can not be authorized. func createTestUserToken(userName string, validTokenRequired bool, userService rbacInterfaces.UserService, jwt *rbac.JWTManager) (string, error) { token, err := jwt.GenerateToken(rbac.User{UserName: userName}) tokens := []string{token} if err != nil { return token, err } if validTokenRequired { user, err := userService.Get(store.Query{Name: userName}) if err != nil { return token, err } user.SetTokens(tokens) err = userService.Update(user) if err != nil { return token, err } } return token, nil } func createHashedAndSaltedPassword(plainPWD, salt string) string { return base64.RawStdEncoding.EncodeToString(argon2.IDKey([]byte(plainPWD), []byte(salt), 1, 64*1024, 4, 32)) } // func getMockPnd(t *testing.T) networkdomain.NetworkDomain { // mockNetworkElement = &nucleus.CommonNetworkElement{ // Plugin: &mocks.Plugin{}, // UUID: mneUUID, // } // mockNetworkElement.(*nucleus.CommonNetworkElement).SetTransport(&mocks.Transport{}) // mockNetworkElement.(*nucleus.CommonNetworkElement).SetName(hostname) // mockPnd = &mocks.NetworkDomain{} // mockPnd.On("ID").Return(pndUUID) // mockPnd.On("GetName").Return("test") // mockPnd.On("GetDescription").Return("test") // mockPnd.On("NetworkElements").Return([]uuid.UUID{mneUUID}) // mockPnd.On("PendingChanges").Return([]uuid.UUID{pendingChangeUUID}) // mockPnd.On("CommittedChanges").Return([]uuid.UUID{committedChangeUUID}) // mockPnd.On("AddNetworkElement", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil) // mockPnd.On("GetNetworkElement", mock.Anything).Return(mockNetworkElement, nil) // mockPnd.On("Commit", mock.Anything).Return(nil) // mockPnd.On("Confirm", mock.Anything).Return(nil) // mockPnd.On("ChangeMNE", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(uuid.Nil, nil) // mockPnd.On("Request", mock.Anything, mock.Anything).Return(nil, nil) // return mockPnd // } func getMockPlugin(t *testing.T) plugin.Plugin { mockPlugin := &mocks.Plugin{} mockPlugin.On("ID").Return(pluginUUID) mockPlugin.On("SchemaTreeGzip").Return([]byte("schema_test"), nil) return mockPlugin } func initUUIDs(t *testing.T) { var err error pndUUID, err = uuid.Parse(pndID) if err != nil { t.Fatal(err) } pluginUUID, err = uuid.Parse(pluginID) if err != nil { t.Fatal(err) } pendingChangeUUID, err = uuid.Parse(pendingChangeID) if err != nil { t.Fatal(err) } committedChangeUUID, err = uuid.Parse(committedChangeID) if err != nil { t.Fatal(err) } mneUUID, err = uuid.Parse(mneID) if err != nil { t.Fatal(err) } } // func createTestNetworkElementServer(t *testing.T, mneServer *NetworkElementServer) { // initUUIDs(t) // eventService := eventservice.NewMockEventService() // pluginService := nucleus.NewPluginServiceMock() // pndStore := nucleus.NewPndStore(pluginService) // pndService := nucleus.NewPndService(pndStore) // mneStore := nucleus.NewNetworkElementStore() // mneService := nucleus.NewNetworkElementService(mneStore, pluginService, eventService) // changeStore := store.NewChangeStore() // *mneServer = *NewNetworkElementServer(mneService, pndService, pluginService, *changeStore) // //t.Cleanup(removeTestStores) // } // func removeTestStores() { // ex, err := os.Executable() // if err != nil { // log.Println(err) // } // exPath := filepath.Dir(ex) // fmt.Println(exPath) // err = os.RemoveAll(exPath + "/stores_testing") // if err != nil { // log.Println(err) // } // } // func cleanMneAndSbiTestStore(mneServer *NetworkElementServer) { // mneToDelete, _ := mneServer.mneService.GetAll() // for _, mne := range mneToDelete { // _ = mneServer.mneService.Delete(mne) // } // } func stringToPointer(str string) *string { return &str }