From b22c67dc0cca44127d80d3cfa50b3b4010747057 Mon Sep 17 00:00:00 2001 From: Shrey Garg <sgarg48@asu.edu> Date: Mon, 13 Jun 2022 10:10:53 +0000 Subject: [PATCH] "Resolve "Implement filesystem stores" See merge request danet/gosdn!322 --- .gitignore | 1 + controller/northbound/server/role.go | 1 - controller/nucleus/deviceFilesystemStore.go | 3 +- .../nucleus/deviceFilesystemStore_test.go | 5 +- controller/nucleus/errors/errors.go | 2 +- controller/nucleus/pndFilesystemStore.go | 5 +- controller/nucleus/pndFilesystemStore_test.go | 5 +- controller/nucleus/sbiFilesystemStore.go | 3 +- controller/nucleus/sbiFilesystemStore_test.go | 5 +- controller/rbac/roleFileSystemStore.go | 166 +++++++++++++++ controller/rbac/roleFileSystemStore_test.go | 185 ++++++++++++++++ controller/rbac/roleStore.go | 7 +- controller/rbac/userFileSystemStore.go | 166 +++++++++++++++ controller/rbac/userFileSystemStore_test.go | 201 ++++++++++++++++++ controller/rbac/userStore.go | 7 +- .../filesystem-settings.go | 6 +- 16 files changed, 739 insertions(+), 29 deletions(-) create mode 100644 controller/rbac/roleFileSystemStore.go create mode 100644 controller/rbac/roleFileSystemStore_test.go create mode 100644 controller/rbac/userFileSystemStore.go create mode 100644 controller/rbac/userFileSystemStore_test.go rename controller/{nucleus/filesystem => store}/filesystem-settings.go (61%) diff --git a/.gitignore b/.gitignore index 6c56d29b9..5fa2ffa0f 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 fd742a96e..4cb05b6e1 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 fc57e48cf..0d9db1f89 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 e3179ad31..520a30a52 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 8c30a9f42..54be6a617 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 61a42808c..beba95994 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 3480c6a53..29119f9f3 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 52d0f9894..64fdeb481 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 38ea67d47..4ec3de489 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 000000000..02a2baaee --- /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 000000000..cfc033ee8 --- /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 4482168fa..35b9a5127 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 000000000..11f2184c7 --- /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 000000000..4261e982e --- /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 1524abe9c..e157d8bf0 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 8df6e31e7..5a23dd0b9 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" ) -- GitLab