mirror of
https://github.com/pushbits/server.git
synced 2025-04-30 10:46:55 +02:00
Some plugins check for token length. Since PushBits uses longer tokens by default for better security, these plugins are incompatible. With this patch, users can decide if they want an application to have a short token, so that said plugins can talk to PushBits again.
226 lines
5.4 KiB
Go
226 lines
5.4 KiB
Go
package api
|
|
|
|
import (
|
|
"errors"
|
|
"log"
|
|
"net/http"
|
|
|
|
"github.com/pushbits/server/internal/authentication"
|
|
"github.com/pushbits/server/internal/model"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
)
|
|
|
|
// UserHandler holds information for processing requests about users.
|
|
type UserHandler struct {
|
|
AH *ApplicationHandler
|
|
CM CredentialsManager
|
|
DB Database
|
|
DP Dispatcher
|
|
}
|
|
|
|
func (h *UserHandler) userExists(name string) bool {
|
|
user, _ := h.DB.GetUserByName(name)
|
|
return user != nil
|
|
}
|
|
|
|
func (h *UserHandler) requireMultipleAdmins(ctx *gin.Context) error {
|
|
if count, err := h.DB.AdminUserCount(); err != nil {
|
|
ctx.AbortWithError(http.StatusInternalServerError, err)
|
|
return err
|
|
} else if count == 1 {
|
|
err := errors.New("instance needs at least one privileged user")
|
|
ctx.AbortWithError(http.StatusBadRequest, err)
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (h *UserHandler) deleteApplications(ctx *gin.Context, u *model.User) error {
|
|
applications, err := h.DB.GetApplications(u)
|
|
if success := successOrAbort(ctx, http.StatusInternalServerError, err); !success {
|
|
return err
|
|
}
|
|
|
|
for _, application := range applications {
|
|
if err := h.AH.deleteApplication(ctx, &application, u); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (h *UserHandler) updateChannels(ctx *gin.Context, u *model.User, matrixID string) error {
|
|
applications, err := h.DB.GetApplications(u)
|
|
if success := successOrAbort(ctx, http.StatusInternalServerError, err); !success {
|
|
return err
|
|
}
|
|
|
|
for _, application := range applications {
|
|
err := h.DP.DeregisterApplication(&application, u)
|
|
if success := successOrAbort(ctx, http.StatusInternalServerError, err); !success {
|
|
return err
|
|
}
|
|
}
|
|
|
|
u.MatrixID = matrixID
|
|
|
|
for _, application := range applications {
|
|
err := h.AH.registerApplication(ctx, &application, u)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (h *UserHandler) updateUser(ctx *gin.Context, u *model.User, updateUser model.UpdateUser) error {
|
|
if updateUser.MatrixID != nil && u.MatrixID != *updateUser.MatrixID {
|
|
if err := h.updateChannels(ctx, u, *updateUser.MatrixID); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
log.Printf("Updating user %s.\n", u.Name)
|
|
|
|
if updateUser.Name != nil {
|
|
u.Name = *updateUser.Name
|
|
}
|
|
if updateUser.Password != nil {
|
|
hash, err := h.CM.CreatePasswordHash(*updateUser.Password)
|
|
if success := successOrAbort(ctx, http.StatusBadRequest, err); !success {
|
|
return err
|
|
}
|
|
|
|
u.PasswordHash = hash
|
|
}
|
|
if updateUser.MatrixID != nil {
|
|
u.MatrixID = *updateUser.MatrixID
|
|
}
|
|
if updateUser.IsAdmin != nil {
|
|
u.IsAdmin = *updateUser.IsAdmin
|
|
}
|
|
|
|
err := h.DB.UpdateUser(u)
|
|
if success := successOrAbort(ctx, http.StatusInternalServerError, err); !success {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// CreateUser creates a new user.
|
|
// This method assumes that the requesting user has privileges.
|
|
func (h *UserHandler) CreateUser(ctx *gin.Context) {
|
|
var createUser model.CreateUser
|
|
|
|
if err := ctx.Bind(&createUser); err != nil {
|
|
return
|
|
}
|
|
|
|
if h.userExists(createUser.Name) {
|
|
ctx.AbortWithError(http.StatusBadRequest, errors.New("username already exists"))
|
|
return
|
|
}
|
|
|
|
log.Printf("Creating user %s.\n", createUser.Name)
|
|
|
|
user, err := h.DB.CreateUser(createUser)
|
|
|
|
if success := successOrAbort(ctx, http.StatusInternalServerError, err); !success {
|
|
return
|
|
}
|
|
|
|
ctx.JSON(http.StatusOK, user.IntoExternalUser())
|
|
}
|
|
|
|
// GetUsers returns all users.
|
|
// This method assumes that the requesting user has privileges.
|
|
func (h *UserHandler) GetUsers(ctx *gin.Context) {
|
|
users, err := h.DB.GetUsers()
|
|
if success := successOrAbort(ctx, http.StatusInternalServerError, err); !success {
|
|
return
|
|
}
|
|
|
|
var externalUsers []*model.ExternalUser
|
|
|
|
for _, user := range users {
|
|
externalUsers = append(externalUsers, user.IntoExternalUser())
|
|
}
|
|
|
|
ctx.JSON(http.StatusOK, &externalUsers)
|
|
}
|
|
|
|
// GetUser returns the user with the specified ID.
|
|
// This method assumes that the requesting user has privileges.
|
|
func (h *UserHandler) GetUser(ctx *gin.Context) {
|
|
user, err := getUser(ctx, h.DB)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
ctx.JSON(http.StatusOK, user.IntoExternalUser())
|
|
}
|
|
|
|
// DeleteUser deletes a user with a certain ID.
|
|
//
|
|
// This method assumes that the requesting user has privileges.
|
|
func (h *UserHandler) DeleteUser(ctx *gin.Context) {
|
|
user, err := getUser(ctx, h.DB)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
// Last privileged user must not be deleted.
|
|
if user.IsAdmin {
|
|
if err := h.requireMultipleAdmins(ctx); err != nil {
|
|
return
|
|
}
|
|
}
|
|
|
|
log.Printf("Deleting user %s.\n", user.Name)
|
|
|
|
if err := h.deleteApplications(ctx, user); err != nil {
|
|
return
|
|
}
|
|
|
|
if success := successOrAbort(ctx, http.StatusInternalServerError, h.DB.DeleteUser(user)); !success {
|
|
return
|
|
}
|
|
|
|
ctx.JSON(http.StatusOK, gin.H{})
|
|
}
|
|
|
|
// UpdateUser updates a user with a certain ID.
|
|
//
|
|
// This method assumes that the requesting user has privileges. If users can later update their own user, make sure they
|
|
// cannot give themselves privileges.
|
|
func (h *UserHandler) UpdateUser(ctx *gin.Context) {
|
|
user, err := getUser(ctx, h.DB)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
var updateUser model.UpdateUser
|
|
if err := ctx.Bind(&updateUser); err != nil {
|
|
return
|
|
}
|
|
|
|
requestingUser := authentication.GetUser(ctx)
|
|
|
|
// Last privileged user must not be taken privileges. Assumes that the current user has privileges.
|
|
if user.ID == requestingUser.ID && updateUser.IsAdmin != nil && !(*updateUser.IsAdmin) {
|
|
if err := h.requireMultipleAdmins(ctx); err != nil {
|
|
return
|
|
}
|
|
}
|
|
|
|
if err := h.updateUser(ctx, user, updateUser); err != nil {
|
|
return
|
|
}
|
|
|
|
ctx.JSON(http.StatusOK, gin.H{})
|
|
}
|