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/networkdomain" "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/nucleus" "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" "github.com/stretchr/testify/mock" "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) 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, Token: "wrong token"}, } for _, u := range users { err := userService.Add(rbac.NewUser(u.ID(), u.Name(), u.Roles, u.Password, "", 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}) if err != nil { return token, err } if validTokenRequired { user, err := userService.Get(store.Query{Name: userName}) if err != nil { return token, err } user.SetToken(token) 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).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) } }