From 035207f01278b3f7bd2df24668f21f50d807f058 Mon Sep 17 00:00:00 2001 From: overflowerror Date: Sun, 22 Aug 2021 18:31:50 +0200 Subject: [PATCH] feat: added endpoints to get and update self --- backend/internal/app/data.go | 2 + backend/internal/app/logic.go | 2 + backend/internal/data/models/user.go | 2 +- backend/internal/data/user.go | 21 ++++++ backend/internal/logic/error.go | 12 ++-- backend/internal/logic/user.go | 74 ++++++++++++++++++++++ backend/internal/presentation/auth.go | 9 --- backend/internal/presentation/responses.go | 4 ++ backend/internal/presentation/user.go | 36 +++++++++++ backend/internal/router/routes.go | 4 +- 10 files changed, 150 insertions(+), 16 deletions(-) create mode 100644 backend/internal/logic/user.go create mode 100644 backend/internal/presentation/user.go diff --git a/backend/internal/app/data.go b/backend/internal/app/data.go index 0b9ea5c..a9e62cb 100644 --- a/backend/internal/app/data.go +++ b/backend/internal/app/data.go @@ -10,6 +10,8 @@ type Data interface { CountUsers() (int64, error) AddUser(user *models.User) error + UpdateUser(user *models.User) error + GetUser(id uuid.UUID) (*models.User, error) GetUserByUsername(username string) (*models.User, error) AddUserToGroup(user *models.User, group *models.Group) error DeleteUserFromGroup(user *models.User, group *models.Group) error diff --git a/backend/internal/app/logic.go b/backend/internal/app/logic.go index c2fec68..2aa9fdc 100644 --- a/backend/internal/app/logic.go +++ b/backend/internal/app/logic.go @@ -7,6 +7,8 @@ import ( ) type Logic interface { + UpdateUser(userToUpdate *models.User, currentUser *models.User) error + AuthenticateSession(token string) (*models.User, error) Login(username, password string) (string, error) diff --git a/backend/internal/data/models/user.go b/backend/internal/data/models/user.go index c5b2d00..7da2843 100644 --- a/backend/internal/data/models/user.go +++ b/backend/internal/data/models/user.go @@ -5,7 +5,7 @@ type User struct { Groups []*Group `gorm:"many2many:user_groups;"` Username string `json:"username"` - Password string `json:"-"` + Password string `json:"password"` } func GetDefaultAdminUser() *User { diff --git a/backend/internal/data/user.go b/backend/internal/data/user.go index c9ee0ab..cb20a1e 100644 --- a/backend/internal/data/user.go +++ b/backend/internal/data/user.go @@ -1,6 +1,7 @@ package data import ( + uuid "github.com/satori/go.uuid" "gorm.io/gorm/clause" "threadule/backend/internal/data/models" ) @@ -21,6 +22,26 @@ func (d *Data) AddUser(user *models.User) error { Error } +func (d *Data) UpdateUser(user *models.User) error { + return d.db. + Omit(clause.Associations). + Save(user). + Error +} + +func (d *Data) GetUser(id uuid.UUID) (*models.User, error) { + var user models.User + err := d.db. + Preload("Groups"). + First(&user, id). + Error + if err != nil { + return nil, err + } else { + return &user, nil + } +} + func (d *Data) GetUserByUsername(username string) (*models.User, error) { var user models.User err := d.db. diff --git a/backend/internal/logic/error.go b/backend/internal/logic/error.go index 5720480..8a4d88f 100644 --- a/backend/internal/logic/error.go +++ b/backend/internal/logic/error.go @@ -3,9 +3,11 @@ package logic import "errors" var ( - ErrLoginFailed = errors.New("login failed") - ErrInvalidSession = errors.New("invalid session") - ErrInternalError = errors.New("something went wrong") - ErrInvalidParameter = errors.New("invalid parameter") - ErrNotFound = errors.New("resource not found") + ErrLoginFailed = errors.New("login failed") + ErrInvalidSession = errors.New("invalid session") + ErrInternalError = errors.New("something went wrong") + ErrInvalidParameter = errors.New("invalid parameter") + ErrNotFound = errors.New("resource not found") + ErrInsufficientPrivilege = errors.New("insufficient privilege") + ErrConflict = errors.New("there was a conflict") ) diff --git a/backend/internal/logic/user.go b/backend/internal/logic/user.go new file mode 100644 index 0000000..9b9eb7f --- /dev/null +++ b/backend/internal/logic/user.go @@ -0,0 +1,74 @@ +package logic + +import ( + "threadule/backend/internal/data/models" +) + +func (l *Logic) checkPrivilege(originalUser *models.User, currentUser *models.User) error { + if originalUser.ID == currentUser.ID { + // own user can always be modified + return nil + } + + ok := false + needsAdmin := false + + for _, group := range originalUser.Groups { + if group.AdminGroup { + needsAdmin = true + break + } + } + + for _, group := range currentUser.Groups { + if group.AdminGroup { + // reset needs admin flag + needsAdmin = false + } + if group.ManageUsers { + ok = true + } + } + + if !ok || needsAdmin { + return ErrInsufficientPrivilege + } else { + return nil + } +} + +func (l *Logic) UpdateUser(userToUpdate *models.User, currentUser *models.User) error { + originalUser, err := l.ctx.Data.GetUser(userToUpdate.ID) + if err != nil { + return ErrNotFound + } + + err = l.checkPrivilege(originalUser, currentUser) + if err != nil { + return err + } + + if originalUser.Username != userToUpdate.Username { + _, err = l.ctx.Data.GetUserByUsername(userToUpdate.Username) + if err == nil { + // if no error there exists already a user with that name + return ErrConflict + } + } + + if userToUpdate.Password != "" { + userToUpdate.Password, err = l.hashPassword(userToUpdate.Password) + if err != nil { + return err + } + } + + userToUpdate.Groups = nil + userToUpdate.CreatedAt = originalUser.CreatedAt + + err = l.ctx.Data.UpdateUser(userToUpdate) + + userToUpdate.Groups = originalUser.Groups + userToUpdate.Password = "" + return err +} diff --git a/backend/internal/presentation/auth.go b/backend/internal/presentation/auth.go index ad5a1da..806e316 100644 --- a/backend/internal/presentation/auth.go +++ b/backend/internal/presentation/auth.go @@ -1,7 +1,6 @@ package presentation import ( - "fmt" "net/http" "threadule/backend/internal/presentation/dto" "threadule/backend/internal/web" @@ -28,11 +27,3 @@ func Login(ctx *web.Context) { ErrorResponse(ctx, err) } } - -func GetAuthenticationData(ctx *web.Context) { - fmt.Println(ctx.Session.User) - err := ctx.WriteJSON(ctx.Session.User) - if err != nil { - ErrorResponse(ctx, err) - } -} diff --git a/backend/internal/presentation/responses.go b/backend/internal/presentation/responses.go index df41af9..6e469ef 100644 --- a/backend/internal/presentation/responses.go +++ b/backend/internal/presentation/responses.go @@ -28,8 +28,12 @@ func ErrorResponse(ctx *web.Context, err error) { StatusResponse(ctx, http.StatusNotFound, err.Error()) case logic.ErrInvalidParameter: StatusResponse(ctx, http.StatusBadRequest, err.Error()) + case logic.ErrInsufficientPrivilege: + StatusResponse(ctx, http.StatusForbidden, err.Error()) case logic.ErrLoginFailed: StatusResponse(ctx, http.StatusForbidden, err.Error()) + case logic.ErrConflict: + StatusResponse(ctx, http.StatusConflict, err.Error()) case logic.ErrInvalidSession: StatusResponse(ctx, http.StatusUnauthorized, err.Error()) case logic.ErrInternalError: diff --git a/backend/internal/presentation/user.go b/backend/internal/presentation/user.go new file mode 100644 index 0000000..072fbbd --- /dev/null +++ b/backend/internal/presentation/user.go @@ -0,0 +1,36 @@ +package presentation + +import ( + "threadule/backend/internal/data/models" + "threadule/backend/internal/web" +) + +func UpdateSelf(ctx *web.Context) { + var user models.User + err := ctx.ReadJSON(&user) + if err != nil { + ErrorResponse(ctx, err) + return + } + + user.ID = ctx.Session.User.ID + + err = ctx.AppCtx.Logic.UpdateUser(&user, ctx.Session.User) + if err != nil { + ErrorResponse(ctx, err) + return + } + + err = ctx.WriteJSON(&user) + if err != nil { + ErrorResponse(ctx, err) + return + } +} + +func GetSelf(ctx *web.Context) { + err := ctx.WriteJSON(ctx.Session.User) + if err != nil { + ErrorResponse(ctx, err) + } +} diff --git a/backend/internal/router/routes.go b/backend/internal/router/routes.go index 4ebe4de..0fce4b3 100644 --- a/backend/internal/router/routes.go +++ b/backend/internal/router/routes.go @@ -21,7 +21,6 @@ func Setup(ctx *app.Context) http.Handler { }) router.POST("/authentication", Login) - router.GET("/authentication", authenticated(GetAuthenticationData)) router.GET("/account/", authenticated(GetAccounts)) router.POST("/account/", authenticated(AddAccount)) @@ -32,5 +31,8 @@ func Setup(ctx *app.Context) http.Handler { router.PUT("/thread/:id", authenticated(UpdateThread)) router.DELETE("/thread/:id", authenticated(DeleteThread)) + router.GET("/self/", authenticated(GetSelf)) + router.PUT("/self/", authenticated(UpdateSelf)) + return router }