sharded-gotify/api/user.go

147 lines
3.6 KiB
Go

package api
import (
"errors"
"strconv"
"github.com/gin-gonic/gin"
"github.com/gotify/server/auth"
"github.com/gotify/server/model"
)
// The UserDatabase interface for encapsulating database access.
type UserDatabase interface {
GetUsers() []*model.User
GetUserByID(id uint) *model.User
GetUserByName(name string) *model.User
DeleteUserByID(id uint) error
UpdateUser(user *model.User)
CreateUser(user *model.User) error
}
// The UserAPI provides handlers for managing users.
type UserAPI struct {
DB UserDatabase
}
// GetUsers returns all the users
func (a *UserAPI) GetUsers(ctx *gin.Context) {
users := a.DB.GetUsers()
var resp []*model.UserExternal
for _, user := range users {
resp = append(resp, toExternal(user))
}
ctx.JSON(200, resp)
}
// GetCurrentUser returns the current user
func (a *UserAPI) GetCurrentUser(ctx *gin.Context) {
user := a.DB.GetUserByID(auth.GetUserID(ctx))
ctx.JSON(200, toExternal(user))
}
// CreateUser creates a user
func (a *UserAPI) CreateUser(ctx *gin.Context) {
user := model.UserExternal{}
if err := ctx.Bind(&user); err == nil {
if len(user.Pass) == 0 {
ctx.AbortWithError(400, errors.New("password may not be empty"))
} else {
internal := toInternal(&user, []byte{})
if a.DB.GetUserByName(internal.Name) == nil {
a.DB.CreateUser(internal)
ctx.JSON(200, toExternal(internal))
} else {
ctx.AbortWithError(400, errors.New("username already exists"))
}
}
}
}
// GetUserByID returns the user by id
func (a *UserAPI) GetUserByID(ctx *gin.Context) {
if id, err := toUInt(ctx.Param("id")); err == nil {
if user := a.DB.GetUserByID(uint(id)); user != nil {
ctx.JSON(200, toExternal(user))
} else {
ctx.AbortWithError(404, errors.New("user does not exist"))
}
} else {
ctx.AbortWithError(400, errors.New("invalid id"))
}
}
// DeleteUserByID deletes the user by id
func (a *UserAPI) DeleteUserByID(ctx *gin.Context) {
if id, err := toUInt(ctx.Param("id")); err == nil {
if user := a.DB.GetUserByID(id); user != nil {
a.DB.DeleteUserByID(id)
} else {
ctx.AbortWithError(404, errors.New("user does not exist"))
}
} else {
ctx.AbortWithError(400, errors.New("invalid id"))
}
}
type userPassword struct {
Pass string `binding:"required" json:"pass" form:"pass" query:"pass" `
}
// ChangePassword changes the password from the current user
func (a *UserAPI) ChangePassword(ctx *gin.Context) {
pw := userPassword{}
if err := ctx.Bind(&pw); err == nil {
user := a.DB.GetUserByID(auth.GetUserID(ctx))
user.Pass = auth.CreatePassword(pw.Pass)
a.DB.UpdateUser(user)
}
}
// UpdateUserByID updates and user by id
func (a *UserAPI) UpdateUserByID(ctx *gin.Context) {
if id, err := toUInt(ctx.Param("id")); err == nil {
var user *model.UserExternal
if err := ctx.Bind(&user); err == nil {
if oldUser := a.DB.GetUserByID(id); oldUser != nil {
internal := toInternal(user, oldUser.Pass)
internal.ID = id
a.DB.UpdateUser(internal)
ctx.JSON(200, toExternal(internal))
} else {
ctx.AbortWithError(404, errors.New("user does not exist"))
}
}
} else {
ctx.AbortWithError(400, errors.New("invalid id"))
}
}
func toUInt(id string) (uint, error) {
parsed, err := strconv.ParseUint(id, 10, 32)
return uint(parsed), err
}
func toInternal(response *model.UserExternal, pw []byte) *model.User {
user := &model.User{
Name: response.Name,
Admin: response.Admin,
}
if response.Pass != "" {
user.Pass = auth.CreatePassword(response.Pass)
} else {
user.Pass = pw
}
return user
}
func toExternal(internal *model.User) *model.UserExternal {
return &model.UserExternal{
Name: internal.Name,
Admin: internal.Admin,
ID: internal.ID,
}
}