mirror of
https://github.com/jc21/nginx-proxy-manager.git
synced 2024-08-30 18:22:48 +00:00
29990110b1
for Mysql and Postgres in addition to existing Sqlite
246 lines
6.6 KiB
Go
246 lines
6.6 KiB
Go
package handler
|
|
|
|
import (
|
|
"database/sql"
|
|
"encoding/json"
|
|
"net/http"
|
|
|
|
c "npm/internal/api/context"
|
|
h "npm/internal/api/http"
|
|
"npm/internal/api/middleware"
|
|
"npm/internal/config"
|
|
"npm/internal/entity/auth"
|
|
"npm/internal/entity/user"
|
|
"npm/internal/errors"
|
|
"npm/internal/logger"
|
|
|
|
"github.com/go-chi/chi"
|
|
)
|
|
|
|
// GetUsers returns all users
|
|
// Route: GET /users
|
|
func GetUsers() func(http.ResponseWriter, *http.Request) {
|
|
return func(w http.ResponseWriter, r *http.Request) {
|
|
pageInfo, err := getPageInfoFromRequest(r)
|
|
if err != nil {
|
|
h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
|
|
return
|
|
}
|
|
|
|
users, err := user.List(pageInfo, middleware.GetFiltersFromContext(r), getExpandFromContext(r))
|
|
if err != nil {
|
|
h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
|
|
} else {
|
|
h.ResultResponseJSON(w, r, http.StatusOK, users)
|
|
}
|
|
}
|
|
}
|
|
|
|
// GetUser returns a specific user
|
|
// Route: GET /users/{userID}
|
|
func GetUser() func(http.ResponseWriter, *http.Request) {
|
|
return func(w http.ResponseWriter, r *http.Request) {
|
|
userID, _, userIDErr := getUserIDFromRequest(r)
|
|
if userIDErr != nil {
|
|
h.ResultErrorJSON(w, r, http.StatusBadRequest, userIDErr.Error(), nil)
|
|
return
|
|
}
|
|
|
|
item, err := user.GetByID(userID)
|
|
switch err {
|
|
case sql.ErrNoRows:
|
|
h.NotFound(w, r)
|
|
case nil:
|
|
// nolint: errcheck,gosec
|
|
item.Expand(getExpandFromContext(r))
|
|
h.ResultResponseJSON(w, r, http.StatusOK, item)
|
|
default:
|
|
h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
|
|
}
|
|
}
|
|
}
|
|
|
|
// UpdateUser updates a user
|
|
// Route: PUT /users/{userID}
|
|
func UpdateUser() func(http.ResponseWriter, *http.Request) {
|
|
return func(w http.ResponseWriter, r *http.Request) {
|
|
userID, self, userIDErr := getUserIDFromRequest(r)
|
|
if userIDErr != nil {
|
|
h.ResultErrorJSON(w, r, http.StatusBadRequest, userIDErr.Error(), nil)
|
|
return
|
|
}
|
|
|
|
userObject, err := user.GetByID(userID)
|
|
switch err {
|
|
case sql.ErrNoRows:
|
|
h.NotFound(w, r)
|
|
case nil:
|
|
// nolint: errcheck,gosec
|
|
userObject.Expand([]string{"capabilities"})
|
|
bodyBytes, _ := r.Context().Value(c.BodyCtxKey).([]byte)
|
|
err := json.Unmarshal(bodyBytes, &userObject)
|
|
if err != nil {
|
|
h.ResultErrorJSON(w, r, http.StatusBadRequest, h.ErrInvalidPayload.Error(), nil)
|
|
return
|
|
}
|
|
|
|
if userObject.IsDisabled && self {
|
|
h.ResultErrorJSON(w, r, http.StatusBadRequest, "You cannot disable yourself!", nil)
|
|
return
|
|
}
|
|
|
|
if err = userObject.Save(); err != nil {
|
|
if err == errors.ErrDuplicateEmailUser || err == errors.ErrSystemUserReadonly {
|
|
h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
|
|
} else {
|
|
logger.Error("UpdateUserError", err)
|
|
h.ResultErrorJSON(w, r, http.StatusInternalServerError, "Unable to save User", nil)
|
|
}
|
|
return
|
|
}
|
|
|
|
if !self {
|
|
err = userObject.SaveCapabilities()
|
|
if err != nil {
|
|
h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
|
|
return
|
|
}
|
|
}
|
|
|
|
// nolint: errcheck,gosec
|
|
userObject.Expand(getExpandFromContext(r))
|
|
|
|
h.ResultResponseJSON(w, r, http.StatusOK, userObject)
|
|
default:
|
|
h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
|
|
}
|
|
}
|
|
}
|
|
|
|
// DeleteUser removes a user
|
|
// Route: DELETE /users/{userID}
|
|
func DeleteUser() func(http.ResponseWriter, *http.Request) {
|
|
return func(w http.ResponseWriter, r *http.Request) {
|
|
var userID uint
|
|
var err error
|
|
if userID, err = getURLParamInt(r, "userID"); err != nil {
|
|
h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
|
|
return
|
|
}
|
|
|
|
myUserID, _ := r.Context().Value(c.UserIDCtxKey).(uint)
|
|
if myUserID == userID {
|
|
h.ResultErrorJSON(w, r, http.StatusBadRequest, "You cannot delete yourself!", nil)
|
|
return
|
|
}
|
|
|
|
item, err := user.GetByID(userID)
|
|
switch err {
|
|
case sql.ErrNoRows:
|
|
h.NotFound(w, r)
|
|
case nil:
|
|
h.ResultResponseJSON(w, r, http.StatusOK, item.Delete())
|
|
default:
|
|
h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
|
|
}
|
|
}
|
|
}
|
|
|
|
// CreateUser creates a user
|
|
// Route: POST /users
|
|
func CreateUser() func(http.ResponseWriter, *http.Request) {
|
|
return func(w http.ResponseWriter, r *http.Request) {
|
|
bodyBytes, _ := r.Context().Value(c.BodyCtxKey).([]byte)
|
|
|
|
var newUser user.Model
|
|
err := json.Unmarshal(bodyBytes, &newUser)
|
|
if err != nil {
|
|
h.ResultErrorJSON(w, r, http.StatusBadRequest, h.ErrInvalidPayload.Error(), nil)
|
|
return
|
|
}
|
|
|
|
if err = newUser.Save(); err != nil {
|
|
if err == errors.ErrDuplicateEmailUser || err == errors.ErrSystemUserReadonly {
|
|
h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
|
|
} else {
|
|
logger.Error("UpdateUserError", err)
|
|
h.ResultErrorJSON(w, r, http.StatusInternalServerError, "Unable to save User", nil)
|
|
}
|
|
return
|
|
}
|
|
|
|
// Set the permissions to full-admin for this user
|
|
if !config.IsSetup {
|
|
newUser.Capabilities = []string{user.CapabilityFullAdmin}
|
|
}
|
|
|
|
// nolint: errcheck,gosec
|
|
err = newUser.SaveCapabilities()
|
|
if err != nil {
|
|
h.ResultErrorJSON(w, r, http.StatusInternalServerError, err.Error(), nil)
|
|
return
|
|
}
|
|
|
|
// newUser has been saved, now save their auth
|
|
if newUser.Auth.Secret != "" && newUser.Auth.ID == 0 {
|
|
newUser.Auth.UserID = newUser.ID
|
|
if newUser.Auth.Type == auth.TypePassword {
|
|
err = newUser.Auth.SetPassword(newUser.Auth.Secret)
|
|
if err != nil {
|
|
logger.Error("SetPasswordError", err)
|
|
}
|
|
}
|
|
|
|
if err = newUser.Auth.Save(); err != nil {
|
|
h.ResultErrorJSON(w, r, http.StatusInternalServerError, "Unable to save Authentication for User", nil)
|
|
return
|
|
}
|
|
|
|
newUser.Auth.Secret = ""
|
|
}
|
|
|
|
if !config.IsSetup {
|
|
config.IsSetup = true
|
|
logger.Info("A new user was created, leaving Setup Mode")
|
|
}
|
|
|
|
h.ResultResponseJSON(w, r, http.StatusOK, newUser)
|
|
}
|
|
}
|
|
|
|
// DeleteUsers is only available in debug mode for cypress tests
|
|
// Route: DELETE /users
|
|
func DeleteUsers() func(http.ResponseWriter, *http.Request) {
|
|
return func(w http.ResponseWriter, r *http.Request) {
|
|
err := user.DeleteAll()
|
|
if err != nil {
|
|
h.ResultErrorJSON(w, r, http.StatusBadRequest, err.Error(), nil)
|
|
} else {
|
|
// also change setup to true
|
|
config.IsSetup = false
|
|
logger.Info("Users have been wiped, entering Setup Mode")
|
|
h.ResultResponseJSON(w, r, http.StatusOK, true)
|
|
}
|
|
}
|
|
}
|
|
|
|
func getUserIDFromRequest(r *http.Request) (uint, bool, error) {
|
|
userIDstr := chi.URLParam(r, "userID")
|
|
selfUserID, _ := r.Context().Value(c.UserIDCtxKey).(uint)
|
|
|
|
var userID uint
|
|
self := false
|
|
if userIDstr == "me" {
|
|
// Get user id from Token
|
|
userID = selfUserID
|
|
self = true
|
|
} else {
|
|
var userIDerr error
|
|
if userID, userIDerr = getURLParamInt(r, "userID"); userIDerr != nil {
|
|
return 0, false, userIDerr
|
|
}
|
|
self = selfUserID == userID
|
|
}
|
|
return userID, self, nil
|
|
}
|