diff options
author | Gabriel Arakaki Giovanini <mail@gabrielgio.me> | 2023-06-29 23:33:02 +0200 |
---|---|---|
committer | Gabriel Arakaki Giovanini <mail@gabrielgio.me> | 2023-06-29 23:33:02 +0200 |
commit | 7a414da9a802d5eeee911b3536790a061e1d7503 (patch) | |
tree | e009cd94e72bf908701ca6067833ed14f6860b43 /pkg/service | |
parent | 1ae70dbd9124675d4a510954619b01edd5f1f6c3 (diff) | |
download | lens-7a414da9a802d5eeee911b3536790a061e1d7503.tar.gz lens-7a414da9a802d5eeee911b3536790a061e1d7503.tar.bz2 lens-7a414da9a802d5eeee911b3536790a061e1d7503.zip |
ref: Move all controller under the same folder
Move all controller to the same folder and rename them to service.
Moving them to the same folder allow an easier setup for testing.
Diffstat (limited to 'pkg/service')
-rw-r--r-- | pkg/service/auth.go | 76 | ||||
-rw-r--r-- | pkg/service/auth_test.go | 78 | ||||
-rw-r--r-- | pkg/service/errors.go | 8 | ||||
-rw-r--r-- | pkg/service/filesystem.go | 91 | ||||
-rw-r--r-- | pkg/service/main_test.go | 121 |
5 files changed, 374 insertions, 0 deletions
diff --git a/pkg/service/auth.go b/pkg/service/auth.go new file mode 100644 index 0000000..4358a8a --- /dev/null +++ b/pkg/service/auth.go @@ -0,0 +1,76 @@ +package service + +import ( + "context" + + "golang.org/x/crypto/bcrypt" + + "git.sr.ht/~gabrielgio/img/pkg/database/repository" + "git.sr.ht/~gabrielgio/img/pkg/ext" +) + +type AuthController struct { + authRepository repository.AuthRepository + userRepository repository.UserRepository + key []byte +} + +func NewAuthController( + authRepository repository.AuthRepository, + userRepository repository.UserRepository, + key []byte, +) *AuthController { + return &AuthController{ + authRepository: authRepository, + userRepository: userRepository, + key: key, + } +} + +func (c *AuthController) Login(ctx context.Context, username, password []byte) ([]byte, error) { + id, err := c.authRepository.GetIDByUsername(ctx, string(username)) + if err != nil { + return nil, err + } + + hashedPassword, err := c.authRepository.GetPassword(ctx, id) + if err != nil { + return nil, err + } + + if err := bcrypt.CompareHashAndPassword(hashedPassword, password); err != nil { + return nil, err + } + + token := &ext.Token{ + UserID: id, + Username: string(username), + } + return ext.WriteToken(token, c.key) +} + +// InitialRegister register a initial user, it will validate if there is another +// user stored already. If so an error `InvlidaInput` will be returned +func (c *AuthController) InitialRegister(ctx context.Context, username, password []byte, path []byte) error { + exist, err := c.userRepository.Any(ctx) + if err != nil { + return err + } + + if exist { + return InvlidInput + } + + hash, err := bcrypt.GenerateFromPassword(password, bcrypt.MinCost) + if err != nil { + return err + } + + _, err = c.userRepository.Create(ctx, &repository.CreateUser{ + Username: string(username), + Password: hash, + Path: string(path), + }) + + return err +} diff --git a/pkg/service/auth_test.go b/pkg/service/auth_test.go new file mode 100644 index 0000000..35b2475 --- /dev/null +++ b/pkg/service/auth_test.go @@ -0,0 +1,78 @@ +//go:build unit + +package service + +import ( + "context" + "testing" + + "git.sr.ht/~gabrielgio/img/pkg/database/repository" + "git.sr.ht/~gabrielgio/img/pkg/ext" + "git.sr.ht/~gabrielgio/img/pkg/testkit" +) + +type ( + scene struct { + ctx context.Context + authRepository repository.AuthRepository + userRepository repository.UserRepository + controller *AuthController + } +) + +var ( + key = []byte("6368616e676520746869732070617373") +) + +func setUp() *scene { + userRepository := NewUserRepository() + return &scene{ + ctx: context.Background(), + authRepository: userRepository, + userRepository: userRepository, + controller: NewAuthController(userRepository, userRepository, key), + } +} + +func TestInitialRegisterAndLogin(t *testing.T) { + testCases := []struct { + name string + username string + password []byte + }{ + { + name: "Normal register", + username: "username", + password: []byte("this is an password"), + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + scene := setUp() + + err := scene.controller.InitialRegister(scene.ctx, []byte(tc.username), tc.password, []byte("/")) + testkit.TestFatalError(t, "Register", err) + + users, err := scene.userRepository.List(scene.ctx) + userID := users[0].ID + + user, err := scene.userRepository.Get(scene.ctx, userID) + testkit.TestFatalError(t, "Get", err) + testkit.TestValue(t, "Register", tc.username, user.Username) + + auth, err := scene.controller.Login(scene.ctx, []byte(tc.username), tc.password) + testkit.TestFatalError(t, "Login", err) + + token, err := ext.ReadToken(auth, key) + testkit.TestFatalError(t, "Login", err) + + testkit.TestValue(t, "Login", tc.username, token.Username) + testkit.TestValue(t, "Login", userID, token.UserID) + }) + } +} + +func remove[T any](slice []T, s int) []T { + return append(slice[:s], slice[s+1:]...) +} diff --git a/pkg/service/errors.go b/pkg/service/errors.go new file mode 100644 index 0000000..6598464 --- /dev/null +++ b/pkg/service/errors.go @@ -0,0 +1,8 @@ +package service + +import "errors" + +var ( + NotFound = errors.New("Not found") + InvlidInput = errors.New("Invalid Input") +) diff --git a/pkg/service/filesystem.go b/pkg/service/filesystem.go new file mode 100644 index 0000000..3516ce2 --- /dev/null +++ b/pkg/service/filesystem.go @@ -0,0 +1,91 @@ +package service + +import ( + "io/fs" + "net/url" + "path" + "strings" + + "git.sr.ht/~gabrielgio/img/pkg/database/repository" +) + +type ( + FileSystemController struct { + repository repository.FileSystemRepository + } + + DirectoryParam struct { + Name string + UrlEncodedPath string + } + + FileParam struct { + UrlEncodedPath string + Info fs.FileInfo + } + + Page struct { + History []*DirectoryParam + Files []*FileParam + } +) + +func NewFileSystemController(repository repository.FileSystemRepository) *FileSystemController { + return &FileSystemController{ + repository: repository, + } +} + +func getHistory(filepath string) []*DirectoryParam { + var ( + paths = strings.Split(filepath, "/") + result = make([]*DirectoryParam, 0, len(paths)) + acc = "" + ) + + // add root folder + result = append(result, &DirectoryParam{ + Name: "...", + UrlEncodedPath: "", + }) + + if len(paths) == 1 && paths[0] == "" { + return result + } + + for _, p := range paths { + acc = path.Join(acc, p) + result = append(result, &DirectoryParam{ + Name: p, + UrlEncodedPath: url.QueryEscape(acc), + }) + } + return result +} + +func (self *FileSystemController) GetPage(filepath string) (*Page, error) { + decodedPath, err := url.QueryUnescape(filepath) + if err != nil { + return nil, err + } + + files, err := self.repository.List(decodedPath) + if err != nil { + return nil, err + } + + params := make([]*FileParam, 0, len(files)) + for _, info := range files { + fullPath := path.Join(decodedPath, info.Name()) + scapedFullPath := url.QueryEscape(fullPath) + params = append(params, &FileParam{ + Info: info, + UrlEncodedPath: scapedFullPath, + }) + } + + return &Page{ + Files: params, + History: getHistory(decodedPath), + }, nil +} diff --git a/pkg/service/main_test.go b/pkg/service/main_test.go new file mode 100644 index 0000000..5c10ecd --- /dev/null +++ b/pkg/service/main_test.go @@ -0,0 +1,121 @@ +//go:build unit + +package service + +import ( + "context" + "errors" + + "git.sr.ht/~gabrielgio/img/pkg/database/repository" +) + +type ( + User struct { + ID uint + Username string + Name string + Password []byte + IsAdmin bool + Path string + } + + Users map[uint]*User + + UserRepository struct { + icount uint + users Users + } +) + +var _ repository.UserRepository = &UserRepository{} +var _ repository.AuthRepository = &UserRepository{} + +func NewUserRepository() *UserRepository { + return &UserRepository{ + users: make(map[uint]*User), + } +} + +func (u *User) ToModel() *repository.User { + return &repository.User{ + ID: u.ID, + Username: u.Username, + Name: u.Name, + IsAdmin: u.IsAdmin, + Path: u.Path, + } +} + +func (u Users) ToModels() []*repository.User { + users := make([]*repository.User, 0, len(u)) + for _, i := range u { + users = append(users, i.ToModel()) + } + return users +} + +func (u *UserRepository) Get(ctx context.Context, id uint) (*repository.User, error) { + if user, ok := u.users[id]; ok { + return user.ToModel(), nil + } + + return nil, errors.New("Not Found") +} + +func (u *UserRepository) List(_ context.Context) ([]*repository.User, error) { + return u.users.ToModels(), nil +} + +func (u *UserRepository) Create(_ context.Context, createUser *repository.CreateUser) (uint, error) { + id := u.furtherID() + u.users[id] = &User{ + ID: id, + Name: createUser.Name, + Username: createUser.Username, + Path: createUser.Path, + Password: createUser.Password, + } + return id, nil +} + +func (u *UserRepository) Update(_ context.Context, id uint, updateUser *repository.UpdateUser) error { + user, ok := u.users[id] + if !ok { + return errors.New("Invalid ID") + } + + user.Name = updateUser.Name + user.Username = updateUser.Username + if updateUser.Password != "" { + user.Password = []byte(updateUser.Password) + } + + return nil +} + +func (u *UserRepository) Any(_ context.Context) (bool, error) { + return len(u.users) > 0, nil +} + +func (u *UserRepository) GetIDByUsername(ctx context.Context, username string) (uint, error) { + for id, u := range u.users { + if u.Username == username { + return id, nil + } + } + + return 0, errors.New("Not Found") +} + +func (u *UserRepository) GetPassword(ctx context.Context, id uint) ([]byte, error) { + if user, ok := u.users[id]; ok { + return []byte(user.Password), nil + } + + return nil, errors.New("Not Found") +} + +func (u *UserRepository) furtherID() uint { + u.icount++ + return u.icount +} |