diff --git a/.gitignore b/.gitignore index 6c56d29b92f43a22cd9d291e791c613a303ed15c..5fa2ffa0ffd521c3989ebc87ca0a4b36679083d2 100644 --- a/.gitignore +++ b/.gitignore @@ -41,6 +41,7 @@ controller/api/stores_testing/** controller/northbound/server/stores_testing/** controller/nucleus/stores_testing/** controller/nucleus/**/gostructs.go +controller/rbac/stores_testing/** # stores stores/*.json diff --git a/controller/northbound/server/role.go b/controller/northbound/server/role.go index fd742a96e6f21497de4985357d36447fe0090729..4cb05b6e16e885082f2e6a1b88466f000f2a7961 100644 --- a/controller/northbound/server/role.go +++ b/controller/northbound/server/role.go @@ -117,7 +117,6 @@ func (r Role) UpdateRoles(ctx context.Context, request *apb.UpdateRolesRequest) if err != nil { return nil, handleRPCError(labels, err) } - _, err = r.roleService.Get(store.Query{ID: rid}) if err != nil { return nil, status.Errorf(codes.Canceled, "role not found %v", err) diff --git a/controller/nucleus/deviceFilesystemStore.go b/controller/nucleus/deviceFilesystemStore.go index fc57e48cfce24269e906cff34d6f2fb8ac013786..0d9db1f893eacc3d958424b44e1feafead591248 100644 --- a/controller/nucleus/deviceFilesystemStore.go +++ b/controller/nucleus/deviceFilesystemStore.go @@ -8,7 +8,6 @@ import ( "code.fbi.h-da.de/danet/gosdn/controller/interfaces/device" "code.fbi.h-da.de/danet/gosdn/controller/interfaces/southbound" "code.fbi.h-da.de/danet/gosdn/controller/nucleus/errors" - "code.fbi.h-da.de/danet/gosdn/controller/nucleus/filesystem" "code.fbi.h-da.de/danet/gosdn/controller/store" "github.com/google/uuid" ) @@ -23,7 +22,7 @@ type FilesystemDeviceStore struct { // NewFilesystemDeviceStore returns a filesystem implementation for a pnd store. func NewFilesystemDeviceStore(pndUUID uuid.UUID) device.Store { - deviceFilenameForUUID := store.GetStoreFilenameForUUID(pndUUID, filesystem.DeviceFilenameSuffix) + deviceFilenameForUUID := store.GetStoreFilenameForUUID(pndUUID, store.DeviceFilenameSuffix) store.EnsureFilesystemStorePathExists(deviceFilenameForUUID) return &FilesystemDeviceStore{ diff --git a/controller/nucleus/deviceFilesystemStore_test.go b/controller/nucleus/deviceFilesystemStore_test.go index e3179ad319e491b1911c4fe9035e73cdf36dc542..520a30a520901d75ad4e0cedf3197e97433717f5 100644 --- a/controller/nucleus/deviceFilesystemStore_test.go +++ b/controller/nucleus/deviceFilesystemStore_test.go @@ -9,14 +9,13 @@ import ( spb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/southbound" tpb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/transport" "code.fbi.h-da.de/danet/gosdn/controller/interfaces/device" - "code.fbi.h-da.de/danet/gosdn/controller/nucleus/filesystem" "code.fbi.h-da.de/danet/gosdn/controller/store" "github.com/google/uuid" ) func ensureDeviceFilesForTestAreRemoved() { - store.EnsureFilesystemStorePathExists(filesystem.DeviceFilenameSuffix) - wildcartFilename := "*-" + filesystem.DeviceFilenameSuffix + store.EnsureFilesystemStorePathExists(store.DeviceFilenameSuffix) + wildcartFilename := "*-" + store.DeviceFilenameSuffix path := store.GetCompletePathToFileStore(wildcartFilename) files, err := filepath.Glob(path) diff --git a/controller/nucleus/errors/errors.go b/controller/nucleus/errors/errors.go index 8c30a9f429b2396d52afcccac42c431458ea78c7..54be6a61744b799cabc3ee1d7cd79a5c3db7bb4a 100644 --- a/controller/nucleus/errors/errors.go +++ b/controller/nucleus/errors/errors.go @@ -31,7 +31,7 @@ type ErrNotFound struct { } func (e *ErrNotFound) Error() string { - return fmt.Sprintf("ID: %v or Name: %vnot found", e.ID, e.Name) + return fmt.Sprintf("ID: %v or Name: %v not found", e.ID, e.Name) } // ErrAlreadyExists implements the Error interface and is called if a specific ID diff --git a/controller/nucleus/pndFilesystemStore.go b/controller/nucleus/pndFilesystemStore.go index 61a42808c0b25112083c1ee0d11e094abd3052aa..beba95994fef0a9cf1c94dca37096efa9987e2d3 100644 --- a/controller/nucleus/pndFilesystemStore.go +++ b/controller/nucleus/pndFilesystemStore.go @@ -9,7 +9,6 @@ import ( "code.fbi.h-da.de/danet/gosdn/controller/interfaces/device" "code.fbi.h-da.de/danet/gosdn/controller/interfaces/networkdomain" "code.fbi.h-da.de/danet/gosdn/controller/nucleus/errors" - "code.fbi.h-da.de/danet/gosdn/controller/nucleus/filesystem" "code.fbi.h-da.de/danet/gosdn/controller/store" "github.com/google/uuid" log "github.com/sirupsen/logrus" @@ -28,10 +27,10 @@ type FilesystemPndStore struct { // NewFilesystemPndStore returns a filesystem implementation for a pnd store. func NewFilesystemPndStore() FilesystemPndStore { - store.EnsureFilesystemStorePathExists(filesystem.PndFilename) + store.EnsureFilesystemStorePathExists(store.PndFilename) return FilesystemPndStore{ pendingChannels: make(map[uuid.UUID]chan device.Details), - pathToPndFile: store.GetCompletePathToFileStore(filesystem.PndFilename), + pathToPndFile: store.GetCompletePathToFileStore(store.PndFilename), fileMutex: sync.Mutex{}, } } diff --git a/controller/nucleus/pndFilesystemStore_test.go b/controller/nucleus/pndFilesystemStore_test.go index 3480c6a5392c90c7f0ac59e14906c63bcea722fb..29119f9f343710cf91d6500b2393686d65c68734 100644 --- a/controller/nucleus/pndFilesystemStore_test.go +++ b/controller/nucleus/pndFilesystemStore_test.go @@ -6,14 +6,13 @@ import ( "testing" "code.fbi.h-da.de/danet/gosdn/controller/interfaces/networkdomain" - "code.fbi.h-da.de/danet/gosdn/controller/nucleus/filesystem" "code.fbi.h-da.de/danet/gosdn/controller/store" "github.com/google/uuid" ) func ensurePndFileForTestIsRemoved() { - store.EnsureFilesystemStorePathExists(filesystem.PndFilename) - path := store.GetCompletePathToFileStore(filesystem.PndFilename) + store.EnsureFilesystemStorePathExists(store.PndFilename) + path := store.GetCompletePathToFileStore(store.PndFilename) err := os.Remove(path) if err != nil { diff --git a/controller/nucleus/sbiFilesystemStore.go b/controller/nucleus/sbiFilesystemStore.go index 52d0f98944ee93b1d8bef02f2f128d3af85f4b6b..64fdeb48104572f8e268c1e53c4a43d3bfb35024 100644 --- a/controller/nucleus/sbiFilesystemStore.go +++ b/controller/nucleus/sbiFilesystemStore.go @@ -7,7 +7,6 @@ import ( "code.fbi.h-da.de/danet/gosdn/controller/interfaces/southbound" "code.fbi.h-da.de/danet/gosdn/controller/nucleus/errors" - "code.fbi.h-da.de/danet/gosdn/controller/nucleus/filesystem" "code.fbi.h-da.de/danet/gosdn/controller/store" "github.com/google/uuid" @@ -23,7 +22,7 @@ type FilesystemSbiStore struct { // NewFilesystemSbiStore returns a filesystem implementation for a pnd store. func NewFilesystemSbiStore(pndUUID uuid.UUID) southbound.Store { - sbiFilenameForUUID := store.GetStoreFilenameForUUID(pndUUID, filesystem.SbiFilenameSuffix) + sbiFilenameForUUID := store.GetStoreFilenameForUUID(pndUUID, store.SbiFilenameSuffix) store.EnsureFilesystemStorePathExists(sbiFilenameForUUID) return &FilesystemSbiStore{ diff --git a/controller/nucleus/sbiFilesystemStore_test.go b/controller/nucleus/sbiFilesystemStore_test.go index 38ea67d4723ee950e00ba74c05d5b08b286549ae..4ec3de489742d87d2fabef6b02e4ec3fcbb25615 100644 --- a/controller/nucleus/sbiFilesystemStore_test.go +++ b/controller/nucleus/sbiFilesystemStore_test.go @@ -8,14 +8,13 @@ import ( spb "code.fbi.h-da.de/danet/gosdn/api/go/gosdn/southbound" "code.fbi.h-da.de/danet/gosdn/controller/interfaces/southbound" - "code.fbi.h-da.de/danet/gosdn/controller/nucleus/filesystem" "code.fbi.h-da.de/danet/gosdn/controller/store" "github.com/google/uuid" ) func ensureSbiFilesForTestAreRemoved() { - store.EnsureFilesystemStorePathExists(filesystem.SbiFilenameSuffix) - wildcartFilename := "*-" + filesystem.SbiFilenameSuffix + store.EnsureFilesystemStorePathExists(store.SbiFilenameSuffix) + wildcartFilename := "*-" + store.SbiFilenameSuffix path := store.GetCompletePathToFileStore(wildcartFilename) files, err := filepath.Glob(path) diff --git a/controller/rbac/roleFileSystemStore.go b/controller/rbac/roleFileSystemStore.go new file mode 100644 index 0000000000000000000000000000000000000000..02a2baaeeea0e89aa0021695d9cebec19c6dd985 --- /dev/null +++ b/controller/rbac/roleFileSystemStore.go @@ -0,0 +1,166 @@ +package rbac + +import ( + "encoding/json" + "io/ioutil" + "sync" + + "code.fbi.h-da.de/danet/gosdn/controller/interfaces/rbac" + "code.fbi.h-da.de/danet/gosdn/controller/nucleus/errors" + "code.fbi.h-da.de/danet/gosdn/controller/store" +) + +// FileSystemRoleStore is the filesystem implementation of the role store +type FileSystemRoleStore struct { + fileMutex sync.Mutex + pathToRoleFile string +} + +// NewFileSystemRoleStore returns a filesystem implementation for a role store. +func NewFileSystemRoleStore() rbac.RoleStore { + store.EnsureFilesystemStorePathExists(store.RoleFilename) + return &FileSystemRoleStore{ + fileMutex: sync.Mutex{}, + pathToRoleFile: store.GetCompletePathToFileStore(store.RoleFilename), + } +} + +func (s *FileSystemRoleStore) readAllRolesFromFile() ([]rbac.LoadedRole, error) { + var loadedRoles []rbac.LoadedRole + content, err := ioutil.ReadFile(s.pathToRoleFile) + if err != nil { + return nil, err + } + + err = json.Unmarshal(content, &loadedRoles) + if err != nil { + return nil, err + } + + return loadedRoles, nil +} + +func (s *FileSystemRoleStore) writeAllRolesToFile(Roles []rbac.LoadedRole) error { + serializedData, err := json.Marshal(Roles) + if err != nil { + return err + } + + err = ioutil.WriteFile(s.pathToRoleFile, serializedData, 0600) + if err != nil { + return err + } + + return nil +} + +// Add adds a Role to the Role store +func (s *FileSystemRoleStore) Add(RoleToAdd rbac.Role) error { + s.fileMutex.Lock() + defer s.fileMutex.Unlock() + + roles, err := s.readAllRolesFromFile() + if err != nil { + return err + } + + var loadedRole rbac.LoadedRole + loadedRole, err = store.TransformObjectToLoadedObject[rbac.Role, rbac.LoadedRole](RoleToAdd) + if err != nil { + return err + } + + roles = append(roles, loadedRole) + + err = s.writeAllRolesToFile(roles) + if err != nil { + return err + } + + return nil +} + +//Delete deletes a Role from the Role store +func (s *FileSystemRoleStore) Delete(RoleToDelete rbac.Role) error { + s.fileMutex.Lock() + defer s.fileMutex.Unlock() + + roles, err := s.readAllRolesFromFile() + if err != nil { + return err + } + + for i, role := range roles { + if role.ID == RoleToDelete.ID().String() { + //remove item from slice + roles[i] = roles[len(roles)-1] + roles = roles[:len(roles)-1] + + err = s.writeAllRolesToFile(roles) + if err != nil { + return err + } + + return nil + } + } + + return &errors.ErrNotFound{ID: RoleToDelete.ID} +} + +//Get takes a Roles ID and return the Role if found +func (s *FileSystemRoleStore) Get(query store.Query) (rbac.LoadedRole, error) { + s.fileMutex.Lock() + defer s.fileMutex.Unlock() + + var role rbac.LoadedRole + roles, err := s.readAllRolesFromFile() + if err != nil { + return role, err + } + + for _, role := range roles { + if role.ID == query.ID.String() || role.RoleName == query.Name { + return role, nil + } + } + + return role, &errors.ErrNotFound{ID: query.ID, Name: query.Name} +} + +// GetAll returns all the Roles +func (s *FileSystemRoleStore) GetAll() ([]rbac.LoadedRole, error) { + s.fileMutex.Lock() + defer s.fileMutex.Unlock() + + Roles, err := s.readAllRolesFromFile() + return Roles, err +} + +//Update updates an exsisting Role +func (s *FileSystemRoleStore) Update(roleToUpdate rbac.Role) error { + s.fileMutex.Lock() + defer s.fileMutex.Unlock() + + loadedRole, err := store.TransformObjectToLoadedObject[rbac.Role, rbac.LoadedRole](roleToUpdate) + if err != nil { + return err + } + Roles, err := s.readAllRolesFromFile() + if err != nil { + return err + } + + for i, Role := range Roles { + if Role.ID == roleToUpdate.ID().String() { + Roles[i] = loadedRole + err = s.writeAllRolesToFile(Roles) + if err != nil { + return err + } + return nil + } + } + + return &errors.ErrNotFound{ID: roleToUpdate.ID().String()} +} diff --git a/controller/rbac/roleFileSystemStore_test.go b/controller/rbac/roleFileSystemStore_test.go new file mode 100644 index 0000000000000000000000000000000000000000..cfc033ee8b3dd537b7b4421c11e73ffa9b79a814 --- /dev/null +++ b/controller/rbac/roleFileSystemStore_test.go @@ -0,0 +1,185 @@ +package rbac + +import ( + "log" + "os" + "path/filepath" + "reflect" + "testing" + + "code.fbi.h-da.de/danet/gosdn/controller/interfaces/rbac" + "code.fbi.h-da.de/danet/gosdn/controller/store" + "github.com/google/uuid" +) + +func ensureRoleFilesForTestAreRemoved() { + store.EnsureFilesystemStorePathExists(store.RoleFilename) + path := store.GetCompletePathToFileStore(store.RoleFilename) + + files, err := filepath.Glob(path) + + if err != nil { + log.Println(err) + } + for _, f := range files { + if err := os.Remove(f); err != nil { + log.Println(err) + } + } +} + +func TestFileSystemRoleStore_Add(t *testing.T) { + ensureRoleFilesForTestAreRemoved() + + type args struct { + RoleToAdd rbac.Role + } + var AddRole rbac.Role + tests := []struct { + name string + args args + wantErr bool + }{ + {"AddRole1", args{AddRole}, false}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + s := NewRoleStore() + if err := s.Add(tt.args.RoleToAdd); (err != nil) != tt.wantErr { + t.Errorf("FileSystemRoleStore.Add() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func TestFileSystemRoleStore_Delete(t *testing.T) { + ensureRoleFilesForTestAreRemoved() + + type args struct { + RoleToDelete rbac.Role + } + var idtest uuid.UUID + addRole := NewRole(idtest, "testRole", "role", []string{}) + tests := []struct { + name string + args args + wantErr bool + }{ + {"AddRole1", args{ + addRole, + }, false}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + s := NewRoleStore() + s.Add(addRole) + if err := s.Delete(tt.args.RoleToDelete); (err != nil) != tt.wantErr { + t.Errorf("FileSystemRoleStore.Delete() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func TestFileSystemRoleStore_Get(t *testing.T) { + ensureRoleFilesForTestAreRemoved() + + type args struct { + query store.Query + } + var idtest uuid.UUID + var arrTest []string + addRole := NewRole(idtest, "testRole", "role", arrTest) + tests := []struct { + name string + args args + want rbac.LoadedRole + wantErr bool + }{ + {"AddRole1", args{ + store.Query{ + ID: idtest, Name: "test", + }, + }, + rbac.LoadedRole{ID: idtest.String(), RoleName: "testRole", Description: "role", Permissions: arrTest}, + false}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + s := NewRoleStore() + s.Add(addRole) + got, err := s.Get(tt.args.query) + if (err != nil) != tt.wantErr { + t.Errorf("FileSystemRoleStore.Get() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("FileSystemRoleStore.Get() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestFileSystemRoleStore_GetAll(t *testing.T) { + ensureRoleFilesForTestAreRemoved() + var idtest uuid.UUID + var arrTest []string + addRole1 := NewRole(idtest, "testRole1", "role1", arrTest) + addRole2 := NewRole(idtest, "testRole2", "role2", arrTest) + addRole3 := NewRole(idtest, "testRole3", "role3", arrTest) + tests := []struct { + name string + want []rbac.LoadedRole + wantErr bool + }{ + { + "testRole", + []rbac.LoadedRole{{ID: idtest.String(), RoleName: "testRole1", Description: "role1", Permissions: arrTest}, {ID: idtest.String(), RoleName: "testRole2", Description: "role2", Permissions: arrTest}, {ID: idtest.String(), RoleName: "testRole3", Description: "role3", Permissions: arrTest}}, + false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + s := NewRoleStore() + s.Add(addRole1) + s.Add(addRole2) + s.Add(addRole3) + got, err := s.GetAll() + if (err != nil) != tt.wantErr { + t.Errorf("FileSystemRoleStore.GetAll() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("FileSystemRoleStore.GetAll() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestFileSystemRoleStore_Update(t *testing.T) { + ensureRoleFilesForTestAreRemoved() + + var idtest uuid.UUID + var arrTest []string + addRole1 := NewRole(idtest, "testRole1", "role1", arrTest) + type args struct { + roleToUpdate rbac.Role + } + tests := []struct { + name string + args args + wantErr bool + }{ + {"AddRole1", args{ + addRole1, + }, false}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + s := NewRoleStore() + s.Add(addRole1) + if err := s.Update(tt.args.roleToUpdate); (err != nil) != tt.wantErr { + t.Errorf("FileSystemRoleStore.Update() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} diff --git a/controller/rbac/roleStore.go b/controller/rbac/roleStore.go index 4482168fa331fbe70fbee6830df0283d5e49a194..35b9a51276641d34745acf5613f35191580a27cb 100644 --- a/controller/rbac/roleStore.go +++ b/controller/rbac/roleStore.go @@ -19,13 +19,10 @@ func NewRoleStore() rbac.RoleStore { storeMode := store.GetStoreMode() switch storeMode { - case store.Filesystem: - return NewMemoryRoleStore() case store.Database: return &DatabaseRoleStore{"role.json"} - case store.Memory: - return NewMemoryRoleStore() default: - return nil + store := NewFileSystemRoleStore() + return store } } diff --git a/controller/rbac/userFileSystemStore.go b/controller/rbac/userFileSystemStore.go new file mode 100644 index 0000000000000000000000000000000000000000..11f2184c78bd233fd5cde3c0d42e710e8d2c7fa0 --- /dev/null +++ b/controller/rbac/userFileSystemStore.go @@ -0,0 +1,166 @@ +package rbac + +import ( + "encoding/json" + "io/ioutil" + "sync" + + "code.fbi.h-da.de/danet/gosdn/controller/interfaces/rbac" + "code.fbi.h-da.de/danet/gosdn/controller/nucleus/errors" + "code.fbi.h-da.de/danet/gosdn/controller/store" +) + +// FileSystemUserStore is the filesystem implementation of the user store +type FileSystemUserStore struct { + fileMutex sync.Mutex + pathToUserFile string +} + +// NewFileSystemUserStore returns a filesystem implementation for a user store. +func NewFileSystemUserStore() rbac.UserStore { + store.EnsureFilesystemStorePathExists(store.UserFilename) + return &FileSystemUserStore{ + fileMutex: sync.Mutex{}, + pathToUserFile: store.GetCompletePathToFileStore(store.UserFilename), + } +} + +func (s *FileSystemUserStore) readAllUsersFromFile() ([]rbac.LoadedUser, error) { + var loadedUsers []rbac.LoadedUser + content, err := ioutil.ReadFile(s.pathToUserFile) + if err != nil { + return nil, err + } + + err = json.Unmarshal(content, &loadedUsers) + if err != nil { + return nil, err + } + + return loadedUsers, nil +} + +func (s *FileSystemUserStore) writeAllUsersToFile(users []rbac.LoadedUser) error { + serializedData, err := json.Marshal(users) + if err != nil { + return err + } + + err = ioutil.WriteFile(s.pathToUserFile, serializedData, 0600) + if err != nil { + return err + } + + return nil +} + +// Add adds a User to the User store +func (s *FileSystemUserStore) Add(UserToAdd rbac.User) error { + s.fileMutex.Lock() + defer s.fileMutex.Unlock() + + users, err := s.readAllUsersFromFile() + if err != nil { + return err + } + + var loadedUser rbac.LoadedUser + loadedUser, err = store.TransformObjectToLoadedObject[rbac.User, rbac.LoadedUser](UserToAdd) + if err != nil { + return err + } + + users = append(users, loadedUser) + + err = s.writeAllUsersToFile(users) + if err != nil { + return err + } + + return nil +} + +//Delete deletes a User from the User store +func (s *FileSystemUserStore) Delete(userToDelete rbac.User) error { + s.fileMutex.Lock() + defer s.fileMutex.Unlock() + + users, err := s.readAllUsersFromFile() + if err != nil { + return err + } + + for i, user := range users { + if user.ID == userToDelete.ID().String() { + //remove item from slice + users[i] = users[len(users)-1] + users = users[:len(users)-1] + + err = s.writeAllUsersToFile(users) + if err != nil { + return err + } + + return nil + } + } + + return &errors.ErrNotFound{ID: userToDelete.ID} +} + +//Get takes a Users ID and return the User if found +func (s *FileSystemUserStore) Get(query store.Query) (rbac.LoadedUser, error) { + s.fileMutex.Lock() + defer s.fileMutex.Unlock() + + var user rbac.LoadedUser + + users, err := s.readAllUsersFromFile() + if err != nil { + return user, err + } + + for _, user := range users { + if user.ID == query.ID.String() || user.UserName == query.Name { + return user, nil + } + } + return user, &errors.ErrNotFound{ID: query.ID, Name: query.Name} +} + +// GetAll returns all the Users +func (s *FileSystemUserStore) GetAll() ([]rbac.LoadedUser, error) { + s.fileMutex.Lock() + defer s.fileMutex.Unlock() + + Users, err := s.readAllUsersFromFile() + return Users, err +} + +//Update updates an exsisting user +func (s *FileSystemUserStore) Update(userToUpdate rbac.User) error { + s.fileMutex.Lock() + defer s.fileMutex.Unlock() + + loadedUser, err := store.TransformObjectToLoadedObject[rbac.User, rbac.LoadedUser](userToUpdate) + if err != nil { + return err + } + users, err := s.readAllUsersFromFile() + if err != nil { + return err + } + + for i, user := range users { + if user.ID == userToUpdate.ID().String() { + users[i] = loadedUser + err = s.writeAllUsersToFile(users) + if err != nil { + return err + } + return nil + } + } + + return &errors.ErrNotFound{ID: userToUpdate.ID().String()} +} diff --git a/controller/rbac/userFileSystemStore_test.go b/controller/rbac/userFileSystemStore_test.go new file mode 100644 index 0000000000000000000000000000000000000000..4261e982ec15079bfee2240858fba617e724a21e --- /dev/null +++ b/controller/rbac/userFileSystemStore_test.go @@ -0,0 +1,201 @@ +package rbac + +import ( + "log" + "os" + "path/filepath" + "reflect" + "testing" + + "code.fbi.h-da.de/danet/gosdn/controller/interfaces/rbac" + "code.fbi.h-da.de/danet/gosdn/controller/store" + "github.com/google/uuid" +) + +func ensureUserFilesForTestAreRemoved() { + store.EnsureFilesystemStorePathExists(store.UserFilename) + path := store.GetCompletePathToFileStore(store.UserFilename) + + files, err := filepath.Glob(path) + + if err != nil { + log.Println(err) + } + for _, f := range files { + if err := os.Remove(f); err != nil { + log.Println(err) + } + } +} + +func TestFileSystemUserStore_Add(t *testing.T) { + ensureUserFilesForTestAreRemoved() + + type args struct { + UserToAdd rbac.User + } + var idtest uuid.UUID + var role map[string]string + testingUser := NewUser(idtest, "testUser", role, "xyz", "svsvsfbdwbwbev", "svswvasfbw") + tests := []struct { + name string + args args + wantErr bool + }{ + { + "testUser", + args{testingUser}, + false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + s := NewUserStore() + + if err := s.Add(tt.args.UserToAdd); (err != nil) != tt.wantErr { + t.Errorf("FileSystemUserStore.Add() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func TestFileSystemUserStore_Delete(t *testing.T) { + ensureUserFilesForTestAreRemoved() + + type args struct { + UserToDelete rbac.User + } + var idtest uuid.UUID + var role map[string]string + testingUser := NewUser(idtest, "", role, "xyz", "svsvsfbdwbwbev", "svswvasfbw") + tests := []struct { + name string + args args + wantErr bool + }{ + { + "testUser", + args{testingUser}, + false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + s := NewUserStore() + s.Add(testingUser) + if err := s.Delete(tt.args.UserToDelete); (err != nil) != tt.wantErr { + t.Errorf("FileSystemUserStore.Delete() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func TestFileSystemUserStore_Get(t *testing.T) { + ensureUserFilesForTestAreRemoved() + + type args struct { + query store.Query + } + var idtest uuid.UUID + var role map[string]string + testingUser := NewUser(idtest, "", role, "xyz", "svsvsfbdwbwbev", "svswvasfbw") + tests := []struct { + name string + args args + want rbac.LoadedUser + wantErr bool + }{ + { + "testUser", + args{ + store.Query{ID: idtest, Name: "test"}, + }, + rbac.LoadedUser{ID: idtest.String(), UserName: "", Roles: role, Password: "xyz", Token: "svsvsfbdwbwbev", Salt: "svswvasfbw"}, + false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + s := NewUserStore() + s.Add(testingUser) + got, err := s.Get(tt.args.query) + if (err != nil) != tt.wantErr { + t.Errorf("FileSystemUserStore.Get() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("FileSystemUserStore.Get() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestFileSystemUserStore_GetAll(t *testing.T) { + ensureUserFilesForTestAreRemoved() + + var idtest uuid.UUID + var role map[string]string + testingUser1 := NewUser(idtest, "", role, "xyz", "svsvsfbdwbwbevasf", "svswvasfbwasv") + testingUser2 := NewUser(idtest, "", role, "abc", "svsvsfbdwbwbevsav", "svswvasfbwadf") + testingUser3 := NewUser(idtest, "", role, "lmn", "svsvsfbdwbwbevscv", "svswvasfbwasd") + tests := []struct { + name string + want []rbac.LoadedUser + wantErr bool + }{ + { + "testUser", + []rbac.LoadedUser{{ID: idtest.String(), UserName: "", Roles: role, Password: "xyz", Token: "svsvsfbdwbwbevasf", Salt: "svswvasfbwasv"}, {ID: idtest.String(), UserName: "", Roles: role, Password: "abc", Token: "svsvsfbdwbwbevsav", Salt: "svswvasfbwadf"}, {ID: idtest.String(), UserName: "", Roles: role, Password: "lmn", Token: "svsvsfbdwbwbevscv", Salt: "svswvasfbwasd"}}, + false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + s := NewUserStore() + s.Add(testingUser1) + s.Add(testingUser2) + s.Add(testingUser3) + got, err := s.GetAll() + if (err != nil) != tt.wantErr { + t.Errorf("FileSystemUserStore.GetAll() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("FileSystemUserStore.GetAll() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestFileSystemUserStore_Update(t *testing.T) { + ensureUserFilesForTestAreRemoved() + + type args struct { + userToUpdate rbac.User + } + var idtest uuid.UUID + var role map[string]string + testingUser := NewUser(idtest, "", role, "xyz", "svsvsfbdwbwbev", "svswvasfbw") + tests := []struct { + name string + args args + wantErr bool + }{ + { + "testUser", + args{ + testingUser, + }, + false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + s := NewUserStore() + s.Add(testingUser) + if err := s.Update(tt.args.userToUpdate); (err != nil) != tt.wantErr { + t.Errorf("FileSystemUserStore.Update() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} diff --git a/controller/rbac/userStore.go b/controller/rbac/userStore.go index 1524abe9c14f286b8fa641e5b3ab8a5ad1df003d..e157d8bf0db359df1e839e4b71529b34ed8aca2c 100644 --- a/controller/rbac/userStore.go +++ b/controller/rbac/userStore.go @@ -19,13 +19,10 @@ func NewUserStore() rbac.UserStore { storeMode := store.GetStoreMode() switch storeMode { - case store.Filesystem: - return NewMemoryUserStore() case store.Database: return &DatabaseUserStore{"user.json"} - case store.Memory: - return NewMemoryUserStore() default: - return nil + store := NewFileSystemUserStore() + return store } } diff --git a/controller/nucleus/filesystem/filesystem-settings.go b/controller/store/filesystem-settings.go similarity index 61% rename from controller/nucleus/filesystem/filesystem-settings.go rename to controller/store/filesystem-settings.go index 8df6e31e7ea541fa4484c7c83c125bfe8925682d..5a23dd0b9f54d556b795ec0c5ec6a7cac248980f 100644 --- a/controller/nucleus/filesystem/filesystem-settings.go +++ b/controller/store/filesystem-settings.go @@ -1,4 +1,4 @@ -package filesystem +package store const ( // PndFilename is the name of the file where the pnds are stored @@ -7,4 +7,8 @@ const ( DeviceFilenameSuffix string = "deviceStore.json" // SbiFilenameSuffix is the suffix of the file where the sbis are stored SbiFilenameSuffix string = "sbiStore.json" + // UserFilename is the name of the file where the users are stored + UserFilename string = "userStore.json" + // RoleFilename is the name of the file where the roles are stored + RoleFilename string = "roleStore.json" )