diff --git a/db_test.go b/db_test.go index 6a9b7c2..3dc3739 100644 --- a/db_test.go +++ b/db_test.go @@ -62,7 +62,7 @@ func TestDatabase(t *testing.T) { Name: "guest", Password: "passwordd1", - Role: "admin", + Role: &models.Role{Role: "guest"}, Email: "guest@gmail.com", }, ignoreError: true}, request{Log: "Get all users", Name: "get all users", Method: "GET", Path: "/users"}, @@ -72,12 +72,12 @@ func TestDatabase(t *testing.T) { }, ignoreError: true}, request{Log: "Get all roles", Name: "get all roles", Method: "GET", Path: "/roles"}, request{Log: "Get all role id 1", Name: "get role id 1", Method: "GET", Path: "/roles?id=1"}, + request{Log: "Get user id 2", Name: "get user id 2", Method: "GET", Path: "/users?id=2"}, ) for _, request := range requests { if request.Log != "" { t.Log(request.Log) - } var bodyReader io.Reader if request.Payload != nil { @@ -98,64 +98,6 @@ func TestDatabase(t *testing.T) { } } - // t.Log("get user id 1") - // result, err := accessHandler.GetUserByKey("user_name", "admin", false) - // if err != nil { - // t.Fatal(err) - // } - // t.Log(result) - - // t.Log("get user by key") - // result, err = accessHandler.GetUserByKey("password", "passwordd", false) - // if err != nil { - // t.Fatal(err) - // } - // t.Log(result) - - // t.Log("get user by key and like") - // result, err = accessHandler.GetUserByKey("user_name", "a*", true) - // if err != nil { - // t.Fatal(err) - // } - // t.Log(result) - - // var user_name string = "admin1" - // if len(result) > 0 { - // if result[0].Name == user_name { - // user_name = "admin" - // } - - // t.Log("update user to ", user_name) - - // accessHandler.UpdateUserByKey(models.User{ - // Name: user_name, - // }, "user_name", result[0].Name) - // } - // t.Log("read user again") - // result, err = accessHandler.GetUserByKey("user_name", "a*", true) - // if err != nil { - // t.Fatal(err) - // } - // t.Log(result) - - // t.Log("delete user id 1") - // err = accessHandler.DeleteUserByKey("user_name", user_name, false) - // if err != nil { - // t.Fatal(err) - // } - // t.Log("read user again") - // result, err = accessHandler.GetUserById(0) - // if err != nil { - // t.Fatal(err) - // } - // t.Log(result) - - // t.Log("read admin permissions") - // result1, err := accessHandler.GetRoleByKey("role", "admin", false) - // if err != nil { - // t.Fatal(err) - // } - // t.Log(result1) } func TestLoginAndAuthorization(t *testing.T) { @@ -177,6 +119,7 @@ func TestLoginAndAuthorization(t *testing.T) { r.POST("/login", aH.Login) r.POST("/login/refresh", aH.Refresh) + r.POST("/roles/update", aH.UpdateRole) r.GET("/login/me", aH.Me) r.GET("/logout", aH.Logout) middleware := r.Group("", aH.AuthMiddleware()) @@ -239,6 +182,9 @@ func TestLoginAndAuthorization(t *testing.T) { requests = append(requests, request{Name: "Refresh", Method: "POST", Path: "/login/refresh", Cookie: refreshCookie}, request{Name: "Me", Method: "GET", Path: "/login/me", Cookie: accessCookie}, + request{Name: "add new role", Method: "POST", Path: "/roles/update", Payload: models.Role{ + Role: "guest", Permissions: models.Permissions{{Name: "members", Permission: 31}}, + }, ignoreError: true}, request{Name: "Authorization", Method: "GET", Path: "/members", Cookie: accessCookie}, request{Name: "Change Password", Method: "POST", Path: "/login/change/password", Cookie: accessCookie, Payload: user}, request{Name: "Logout", Method: "GET", Path: "/logout", Cookie: refreshCookie}, diff --git a/go.mod b/go.mod index 243412a..ee9b7a8 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module gitea.tecamino.com/paadi/access-handler go 1.24.5 require ( - gitea.tecamino.com/paadi/dbHandler v1.0.8 + gitea.tecamino.com/paadi/dbHandler v1.1.4 gitea.tecamino.com/paadi/tecamino-logger v0.2.1 github.com/gin-gonic/gin v1.11.0 github.com/go-playground/assert/v2 v2.2.0 diff --git a/go.sum b/go.sum index 2b0c0f3..b361ac7 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -gitea.tecamino.com/paadi/dbHandler v1.0.8 h1:ZWSBM/KFtLwTv2cBqwK1mOxWAxAfL0BcWEC3kJ9JALU= -gitea.tecamino.com/paadi/dbHandler v1.0.8/go.mod h1:y/xn/POJg1DO++67uKvnO23lJQgh+XFQq7HZCS9Getw= +gitea.tecamino.com/paadi/dbHandler v1.1.4 h1:/yBwhRzBp823mF7Y0z68uxLEWCoIFeFd4aGdYYPk+Sc= +gitea.tecamino.com/paadi/dbHandler v1.1.4/go.mod h1:y/xn/POJg1DO++67uKvnO23lJQgh+XFQq7HZCS9Getw= gitea.tecamino.com/paadi/tecamino-logger v0.2.1 h1:sQTBKYPdzn9mmWX2JXZBtGBvNQH7cuXIwsl4TD0aMgE= gitea.tecamino.com/paadi/tecamino-logger v0.2.1/go.mod h1:FkzRTldUBBOd/iy2upycArDftSZ5trbsX5Ira5OzJgM= github.com/bytedance/sonic v1.14.0 h1:/OfKt8HFw0kh2rj8N0F6C/qPGRESq0BbaNZgcNXXzQQ= diff --git a/handlers/access.go b/handlers/access.go index da64dc7..ab36ed6 100644 --- a/handlers/access.go +++ b/handlers/access.go @@ -73,22 +73,6 @@ func NewAccessHandler(path string, logger *logging.Logger) (aH *AccessHandler, e return } - logger.Debug("NewAccessHandler", "add user table") - // Add the user table to the database - err = aH.AddUserTable() - if err != nil { - aH.logger.Error("NewAccessHandler", err) - return - } - - logger.Debug("NewAccessHandler", "add default user") - // Add default users to the system - err = aH.AddDefaultUser() - if err != nil { - aH.logger.Error("NewAccessHandler", err) - return - } - logger.Debug("NewAccessHandler", "add role table") // Add the role table to the database err = aH.AddRoleTable() @@ -104,6 +88,21 @@ func NewAccessHandler(path string, logger *logging.Logger) (aH *AccessHandler, e aH.logger.Error("NewAccessHandler", err) } + logger.Debug("NewAccessHandler", "add user table") + // Add the user table to the database + err = aH.AddUserTable() + if err != nil { + aH.logger.Error("NewAccessHandler", err) + return + } + + logger.Debug("NewAccessHandler", "add default user") + // Add default users to the system + err = aH.AddDefaultUser() + if err != nil { + aH.logger.Error("NewAccessHandler", err) + return + } return } diff --git a/handlers/login.go b/handlers/login.go index 03270cd..e3d13e9 100644 --- a/handlers/login.go +++ b/handlers/login.go @@ -78,7 +78,7 @@ func (aH *AccessHandler) Login(c *gin.Context) { accessToken := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{ "id": user.Id, "username": user.Name, - "role": user.Role, + "role": user.Role.Role, "type": "access", "exp": accessTokenExp.Unix(), }) @@ -87,7 +87,7 @@ func (aH *AccessHandler) Login(c *gin.Context) { refreshToken := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{ "id": user.Id, "username": user.Name, - "role": user.Role, + "role": user.Role.Role, "type": "refresh", "exp": refreshTokenExp.Unix(), }) @@ -125,7 +125,7 @@ func (aH *AccessHandler) Login(c *gin.Context) { "message": "login successful", "id": user.Id, "user": user.Name, - "role": user.Role, + "role": user.Role.Role, "settings": user.Settings, }) } @@ -177,7 +177,7 @@ func (aH *AccessHandler) Refresh(c *gin.Context) { newAccess := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{ "id": id, "username": username, - "role": user.Role, + "role": user.Role.Role, "exp": accessExp.Unix(), }) accessString, _ := newAccess.SignedString(ACCESS_SECRET) @@ -241,7 +241,7 @@ func (aH *AccessHandler) getUserFromDB(c *gin.Context, userName string) (user mo hasError = true // Fetch user record from DB var dbRecord []models.User - err := aH.dbHandler.GetByKey(&dbRecord, "user_name", userName, false) + err := aH.dbHandler.GetByKey(&dbRecord, "Role", "user_name", userName, false) if err != nil { aH.logger.Error("Login", err) c.JSON(http.StatusBadRequest, models.NewJsonErrorResponse(err)) diff --git a/handlers/middleware.go b/handlers/middleware.go index 0994913..a3d24f4 100644 --- a/handlers/middleware.go +++ b/handlers/middleware.go @@ -132,7 +132,7 @@ func (aH *AccessHandler) AuthorizeRole(suffix string, exeptions ...string) gin.H // Fetch roles and associated permissions from the database or store var roles []models.Role - err := aH.dbHandler.GetByKey(&roles, "role", role, false) + err := aH.dbHandler.GetByKey(&roles, "", "role", role, false) if err != nil { aH.logger.Error("AuthorizeRole", err) c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{"message": http.StatusInternalServerError}) diff --git a/handlers/role.go b/handlers/role.go index 81e9e3b..972d22a 100644 --- a/handlers/role.go +++ b/handlers/role.go @@ -55,6 +55,7 @@ func (aH *AccessHandler) AddRole(c *gin.Context) { if aH.dbHandler.Exists(&models.Role{}, "role", role.Role, false) { aH.logger.Error("AddRole", fmt.Sprintf("role with name %s already exists", role.Role)) c.JSON(http.StatusBadRequest, models.NewJsonMessageResponse(fmt.Sprintf("role with name %s already exists", role.Role))) + return } // Insert new role with provided permissions @@ -62,6 +63,7 @@ func (aH *AccessHandler) AddRole(c *gin.Context) { Role: role.Role, Permissions: role.Permissions, }) + c.JSON(http.StatusOK, gin.H{ "message": fmt.Sprintf("role '%s' successfully added", role.Role), }) @@ -76,7 +78,7 @@ func (aH *AccessHandler) GetRole(c *gin.Context) { id := c.Query("id") if role != "" { - err = aH.dbHandler.GetByKey(&roles, "role", role, false) + err = aH.dbHandler.GetByKey(&roles, "", "role", role, false) } else if id != "" { i, err = strconv.Atoi(id) if err != nil { @@ -85,9 +87,9 @@ func (aH *AccessHandler) GetRole(c *gin.Context) { }) return } - err = aH.dbHandler.GetById(&roles, uint(i)) + err = aH.dbHandler.GetById(&roles, "", uint(i)) } else { - err = aH.dbHandler.GetById(&roles, 0) + err = aH.dbHandler.GetById(&roles, "", 0) } @@ -106,12 +108,23 @@ func (aH *AccessHandler) UpdateRole(c *gin.Context) { c.JSON(http.StatusInternalServerError, nil) return } - err := aH.dbHandler.UpdateValuesById(&role, role.Id) - if err != nil { - aH.logger.Error("UpdateRole", err) - c.JSON(http.StatusInternalServerError, nil) - return + + if role.Id != 0 { + err := aH.dbHandler.UpdateValuesById(&role, "", role.Id) + if err != nil { + aH.logger.Error("UpdateRole", err) + c.JSON(http.StatusInternalServerError, nil) + return + } + } else { + err := aH.dbHandler.UpdateValuesByKey(&role, "", "role", role.Role) + if err != nil { + aH.logger.Error("UpdateRole", err) + c.JSON(http.StatusInternalServerError, nil) + return + } } + c.JSON(http.StatusOK, models.NewJsonMessageResponse("successfully updated role '"+role.Role+"'")) } diff --git a/handlers/user.go b/handlers/user.go index d660235..06093e2 100644 --- a/handlers/user.go +++ b/handlers/user.go @@ -16,31 +16,36 @@ func (aH *AccessHandler) AddUserTable() error { } func (aH *AccessHandler) AddDefaultUser() (err error) { - name := "admin" - role := "admin" - email := "zuercher@tecamino.ch" - - // Check if a user with this email already exists - if aH.dbHandler.Exists(&models.User{}, "email", email, false) { - aH.logger.Debug("AddDefaultUser", "user email "+email+" exists already") - // Found a user → skip create - return nil - } - // Create default settings for the new user settings := models.Settings{} aH.logger.Debug("AddDefaultUser", "set default quasar settings") settings.DefaultQuasarSettings() - // Insert default admin user into the database - aH.dbHandler.AddNewColum(&models.User{ - Name: name, - Role: role, - Email: email, + user := &models.User{ + Name: "admin", + Email: "zuercher@tecamino.ch", Password: "$2a$10$sZZOWBP8DSFLrLFQNoXw8OsEEr0tez1B8lPzKCHofaHg6PMNxx1pG", Settings: settings, - }) - return + } + + // Check if a user with this email already exists + if aH.dbHandler.Exists(&models.User{}, "email", user.Email, false) { + aH.logger.Debug("AddDefaultUser", "user email "+user.Email+" exists already") + // Found a user → skip create + return nil + } + + // Insert default admin user into the database + + if err := aH.dbHandler.AddNewColum(user); err != nil { + return err + } + + role := &models.Role{} + if err := aH.dbHandler.GetByKey(role, "", "role", "admin", false); err != nil { + return err + } + return aH.dbHandler.AddRelation(user, role, "Role") } func (aH *AccessHandler) AddUser(c *gin.Context) { @@ -73,7 +78,7 @@ func (aH *AccessHandler) AddUser(c *gin.Context) { } // Hash the provided password before saving - hash, err := utils.HashPassword(user.Password) + user.Password, err = utils.HashPassword(user.Password) if err != nil { aH.logger.Error("AddUser", err) c.JSON(http.StatusInternalServerError, nil) @@ -83,16 +88,29 @@ func (aH *AccessHandler) AddUser(c *gin.Context) { aH.logger.Debug("AddUser", "add default quasar user setting ") user.Settings.DefaultQuasarSettings() - aH.logger.Debug("AddUser", "add new user "+user.Name+" with role "+user.Role) + aH.logger.Debug("AddUser", "add new user "+user.Name+" with role "+user.Role.Role) + + if user.Role.Id != 0 { + if err := aH.dbHandler.GetById(&user.Role, "", user.Role.Id); err != nil { + aH.logger.Error("AddUser", err) + c.JSON(http.StatusInternalServerError, nil) + return + } + } else { + if err := aH.dbHandler.GetByKey(&user.Role, "", "role", user.Role.Role, false); err != nil { + aH.logger.Error("AddUser", err) + c.JSON(http.StatusInternalServerError, nil) + return + } + } + user.RoleID = &user.Role.Id // Insert the new user record - aH.dbHandler.AddNewColum(&models.User{ - Name: user.Name, - Role: user.Role, - Email: user.Email, - Password: hash, - Settings: user.Settings, - }) + if err := aH.dbHandler.AddNewColum(&user); err != nil { + aH.logger.Error("AddUser", err) + c.JSON(http.StatusInternalServerError, nil) + return + } c.JSON(http.StatusOK, gin.H{ "message": fmt.Sprintf("user '%s' successfully added", user.Name), @@ -110,7 +128,7 @@ func (aH *AccessHandler) ChangePassword(c *gin.Context) { // get user to check ChangePassword var dbRecord models.User - err = aH.dbHandler.GetById(&dbRecord, user.Id) + err = aH.dbHandler.GetById(&dbRecord, "Role", user.Id) if err != nil { aH.logger.Error("ChangePassword", err) c.JSON(http.StatusInternalServerError, nil) @@ -119,13 +137,11 @@ func (aH *AccessHandler) ChangePassword(c *gin.Context) { // Check if old password is correct if !utils.CheckPassword(user.Password, dbRecord.Password) { - fmt.Println(123, dbRecord.Password, user.Password) // Found a user → skip create aH.logger.Error("ChangePassword", "wrong password entered for user: "+user.Name) c.JSON(http.StatusBadRequest, models.NewJsonMessageResponse("invalid credentials")) return } - fmt.Println(3) // Hash the provided password before saving user.Password, err = utils.HashPassword(user.NewPassword) @@ -134,13 +150,11 @@ func (aH *AccessHandler) ChangePassword(c *gin.Context) { c.JSON(http.StatusInternalServerError, nil) return } - fmt.Println(4) aH.logger.Debug("ChangePassword", "change user "+user.Name+" password") // Update user - aH.dbHandler.UpdateValuesById(&user, user.Id) - fmt.Println(5) + aH.dbHandler.UpdateValuesById(&user, "Role", user.Id) c.JSON(http.StatusOK, gin.H{ "message": fmt.Sprintf("password of user '%s' changed", user.Name), @@ -164,7 +178,7 @@ func (aH *AccessHandler) GetUser(c *gin.Context) { } var users []models.User - err = aH.dbHandler.GetById(&users, uint(i)) + err = aH.dbHandler.GetById(&users, "Role", uint(i)) if err != nil { aH.logger.Error("GetUser", err) c.JSON(http.StatusInternalServerError, nil) @@ -180,7 +194,7 @@ func (aH *AccessHandler) UpdateUser(c *gin.Context) { c.JSON(http.StatusInternalServerError, nil) return } - err := aH.dbHandler.UpdateValuesById(&user, user.Id) + err := aH.dbHandler.UpdateValuesById(&user, "Role", user.Id) if err != nil { aH.logger.Error("UpdateUser", err) c.JSON(http.StatusInternalServerError, nil) diff --git a/models/user.go b/models/user.go index 91a5816..39f8c96 100644 --- a/models/user.go +++ b/models/user.go @@ -8,9 +8,10 @@ type User struct { Id uint `gorm:"primaryKey" json:"id"` Name string `gorm:"column:user_name" json:"user"` Email string `gorm:"column:email" json:"email"` - Role string `gorm:"column:role" json:"role,omitempty"` + RoleID *uint `gorm:"column:roleId" json:"roleId,omitempty"` + Role *Role `gorm:"foreignKey:RoleID" json:"role,omitempty"` Password string `gorm:"column:password" json:"password"` - NewPassword string `gorm:"-" json:"newPassword"` + NewPassword string `gorm:"-" json:"newPassword,omitempty"` Expiration string `gorm:"column:expiration" json:"expiration,omitempty"` Settings Settings `gorm:"type:json" json:"settings"` }