|
@@ -5,11 +5,12 @@ import (
|
|
|
"errors"
|
|
|
"fmt"
|
|
|
"net/http"
|
|
|
+ "net/url"
|
|
|
+ "reflect"
|
|
|
|
|
|
"github.com/gorilla/mux"
|
|
|
"github.com/gorilla/websocket"
|
|
|
"github.com/gravitl/netmaker/auth"
|
|
|
- "github.com/gravitl/netmaker/database"
|
|
|
"github.com/gravitl/netmaker/logger"
|
|
|
"github.com/gravitl/netmaker/logic"
|
|
|
"github.com/gravitl/netmaker/models"
|
|
@@ -22,34 +23,33 @@ var (
|
|
|
upgrader = websocket.Upgrader{}
|
|
|
)
|
|
|
|
|
|
+var ListRoles = listRoles
|
|
|
+
|
|
|
func userHandlers(r *mux.Router) {
|
|
|
r.HandleFunc("/api/users/adm/hassuperadmin", hasSuperAdmin).Methods(http.MethodGet)
|
|
|
r.HandleFunc("/api/users/adm/createsuperadmin", createSuperAdmin).Methods(http.MethodPost)
|
|
|
- r.HandleFunc("/api/users/adm/transfersuperadmin/{username}", logic.SecurityCheck(true, http.HandlerFunc(transferSuperAdmin))).Methods(http.MethodPost)
|
|
|
+ r.HandleFunc("/api/users/adm/transfersuperadmin/{username}", logic.SecurityCheck(true, http.HandlerFunc(transferSuperAdmin))).
|
|
|
+ Methods(http.MethodPost)
|
|
|
r.HandleFunc("/api/users/adm/authenticate", authenticateUser).Methods(http.MethodPost)
|
|
|
r.HandleFunc("/api/users/{username}", logic.SecurityCheck(true, http.HandlerFunc(updateUser))).Methods(http.MethodPut)
|
|
|
r.HandleFunc("/api/users/{username}", logic.SecurityCheck(true, checkFreeTierLimits(limitChoiceUsers, http.HandlerFunc(createUser)))).Methods(http.MethodPost)
|
|
|
r.HandleFunc("/api/users/{username}", logic.SecurityCheck(true, http.HandlerFunc(deleteUser))).Methods(http.MethodDelete)
|
|
|
r.HandleFunc("/api/users/{username}", logic.SecurityCheck(false, logic.ContinueIfUserMatch(http.HandlerFunc(getUser)))).Methods(http.MethodGet)
|
|
|
+ r.HandleFunc("/api/v1/users", logic.SecurityCheck(false, logic.ContinueIfUserMatch(http.HandlerFunc(getUserV1)))).Methods(http.MethodGet)
|
|
|
r.HandleFunc("/api/users", logic.SecurityCheck(true, http.HandlerFunc(getUsers))).Methods(http.MethodGet)
|
|
|
- r.HandleFunc("/api/users_pending", logic.SecurityCheck(true, http.HandlerFunc(getPendingUsers))).Methods(http.MethodGet)
|
|
|
- r.HandleFunc("/api/users_pending", logic.SecurityCheck(true, http.HandlerFunc(deleteAllPendingUsers))).Methods(http.MethodDelete)
|
|
|
- r.HandleFunc("/api/users_pending/user/{username}", logic.SecurityCheck(true, http.HandlerFunc(deletePendingUser))).Methods(http.MethodDelete)
|
|
|
- r.HandleFunc("/api/users_pending/user/{username}", logic.SecurityCheck(true, http.HandlerFunc(approvePendingUser))).Methods(http.MethodPost)
|
|
|
+ r.HandleFunc("/api/v1/users/roles", logic.SecurityCheck(true, http.HandlerFunc(ListRoles))).Methods(http.MethodGet)
|
|
|
|
|
|
}
|
|
|
|
|
|
-// swagger:route POST /api/users/adm/authenticate authenticate authenticateUser
|
|
|
-//
|
|
|
-// User authenticates using its password and retrieves a JWT for authorization.
|
|
|
-//
|
|
|
-// Schemes: https
|
|
|
-//
|
|
|
-// Security:
|
|
|
-// oauth
|
|
|
-//
|
|
|
-// Responses:
|
|
|
-// 200: successResponse
|
|
|
+// @Summary Authenticate a user to retrieve an authorization token
|
|
|
+// @Router /api/users/adm/authenticate [post]
|
|
|
+// @Tags Auth
|
|
|
+// @Accept json
|
|
|
+// @Param body body models.UserAuthParams true "Authentication parameters"
|
|
|
+// @Success 200 {object} models.SuccessResponse
|
|
|
+// @Failure 400 {object} models.ErrorResponse
|
|
|
+// @Failure 401 {object} models.ErrorResponse
|
|
|
+// @Failure 500 {object} models.ErrorResponse
|
|
|
func authenticateUser(response http.ResponseWriter, request *http.Request) {
|
|
|
|
|
|
// Auth request consists of Mac Address and Password (from node that is authorizing
|
|
@@ -60,7 +60,11 @@ func authenticateUser(response http.ResponseWriter, request *http.Request) {
|
|
|
}
|
|
|
|
|
|
if !servercfg.IsBasicAuthEnabled() {
|
|
|
- logic.ReturnErrorResponse(response, request, logic.FormatError(fmt.Errorf("basic auth is disabled"), "badrequest"))
|
|
|
+ logic.ReturnErrorResponse(
|
|
|
+ response,
|
|
|
+ request,
|
|
|
+ logic.FormatError(fmt.Errorf("basic auth is disabled"), "badrequest"),
|
|
|
+ )
|
|
|
return
|
|
|
}
|
|
|
|
|
@@ -82,10 +86,24 @@ func authenticateUser(response http.ResponseWriter, request *http.Request) {
|
|
|
logic.ReturnErrorResponse(response, request, logic.FormatError(err, "unauthorized"))
|
|
|
return
|
|
|
}
|
|
|
- if !(user.IsAdmin || user.IsSuperAdmin) {
|
|
|
- logic.ReturnErrorResponse(response, request, logic.FormatError(errors.New("only admins can access dashboard"), "unauthorized"))
|
|
|
+ role, err := logic.GetRole(user.PlatformRoleID)
|
|
|
+ if err != nil {
|
|
|
+ logic.ReturnErrorResponse(response, request, logic.FormatError(errors.New("access denied to dashboard"), "unauthorized"))
|
|
|
return
|
|
|
}
|
|
|
+ if role.DenyDashboardAccess {
|
|
|
+ logic.ReturnErrorResponse(response, request, logic.FormatError(errors.New("access denied to dashboard"), "unauthorized"))
|
|
|
+ return
|
|
|
+ }
|
|
|
+ }
|
|
|
+ user, err := logic.GetUser(authRequest.UserName)
|
|
|
+ if err != nil {
|
|
|
+ logic.ReturnErrorResponse(response, request, logic.FormatError(err, "unauthorized"))
|
|
|
+ return
|
|
|
+ }
|
|
|
+ if logic.IsOauthUser(user) == nil {
|
|
|
+ logic.ReturnErrorResponse(response, request, logic.FormatError(errors.New("user is registered via SSO"), "badrequest"))
|
|
|
+ return
|
|
|
}
|
|
|
username := authRequest.UserName
|
|
|
jwt, err := logic.VerifyAuthRequest(authRequest)
|
|
@@ -99,7 +117,11 @@ func authenticateUser(response http.ResponseWriter, request *http.Request) {
|
|
|
if jwt == "" {
|
|
|
// very unlikely that err is !nil and no jwt returned, but handle it anyways.
|
|
|
logger.Log(0, username, "jwt token is empty")
|
|
|
- logic.ReturnErrorResponse(response, request, logic.FormatError(errors.New("no token returned"), "internal"))
|
|
|
+ logic.ReturnErrorResponse(
|
|
|
+ response,
|
|
|
+ request,
|
|
|
+ logic.FormatError(errors.New("no token returned"), "internal"),
|
|
|
+ )
|
|
|
return
|
|
|
}
|
|
|
|
|
@@ -133,9 +155,19 @@ func authenticateUser(response http.ResponseWriter, request *http.Request) {
|
|
|
}
|
|
|
for _, client := range clients {
|
|
|
if client.OwnerID == username && !client.Enabled {
|
|
|
- slog.Info(fmt.Sprintf("enabling ext client %s for user %s due to RAC autodisabling feature", client.ClientID, client.OwnerID))
|
|
|
+ slog.Info(
|
|
|
+ fmt.Sprintf(
|
|
|
+ "enabling ext client %s for user %s due to RAC autodisabling feature",
|
|
|
+ client.ClientID,
|
|
|
+ client.OwnerID,
|
|
|
+ ),
|
|
|
+ )
|
|
|
if newClient, err := logic.ToggleExtClientConnectivity(&client, true); err != nil {
|
|
|
- slog.Error("error enabling ext client in RAC autodisable hook", "error", err)
|
|
|
+ slog.Error(
|
|
|
+ "error enabling ext client in RAC autodisable hook",
|
|
|
+ "error",
|
|
|
+ err,
|
|
|
+ )
|
|
|
continue // dont return but try for other clients
|
|
|
} else {
|
|
|
// publish peer update to ingress gateway
|
|
@@ -151,17 +183,11 @@ func authenticateUser(response http.ResponseWriter, request *http.Request) {
|
|
|
}()
|
|
|
}
|
|
|
|
|
|
-// swagger:route GET /api/users/adm/hassuperadmin user hasSuperAdmin
|
|
|
-//
|
|
|
-// Checks whether the server has an admin.
|
|
|
-//
|
|
|
-// Schemes: https
|
|
|
-//
|
|
|
-// Security:
|
|
|
-// oauth
|
|
|
-//
|
|
|
-// Responses:
|
|
|
-// 200: hasAdmin
|
|
|
+// @Summary Check if the server has a super admin
|
|
|
+// @Router /api/users/adm/hassuperadmin [get]
|
|
|
+// @Tags Users
|
|
|
+// @Success 200 {object} bool
|
|
|
+// @Failure 500 {object} models.ErrorResponse
|
|
|
func hasSuperAdmin(w http.ResponseWriter, r *http.Request) {
|
|
|
|
|
|
w.Header().Set("Content-Type", "application/json")
|
|
@@ -177,9 +203,32 @@ func hasSuperAdmin(w http.ResponseWriter, r *http.Request) {
|
|
|
|
|
|
}
|
|
|
|
|
|
-// swagger:route GET /api/users/{username} user getUser
|
|
|
+// @Summary Get an individual user
|
|
|
+// @Router /api/users/{username} [get]
|
|
|
+// @Tags Users
|
|
|
+// @Param username path string true "Username of the user to fetch"
|
|
|
+// @Success 200 {object} models.User
|
|
|
+// @Failure 500 {object} models.ErrorResponse
|
|
|
+func getUser(w http.ResponseWriter, r *http.Request) {
|
|
|
+ // set header.
|
|
|
+ w.Header().Set("Content-Type", "application/json")
|
|
|
+
|
|
|
+ var params = mux.Vars(r)
|
|
|
+ usernameFetched := params["username"]
|
|
|
+ user, err := logic.GetReturnUser(usernameFetched)
|
|
|
+
|
|
|
+ if err != nil {
|
|
|
+ logger.Log(0, usernameFetched, "failed to fetch user: ", err.Error())
|
|
|
+ logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
|
|
|
+ return
|
|
|
+ }
|
|
|
+ logger.Log(2, r.Header.Get("user"), "fetched user", usernameFetched)
|
|
|
+ json.NewEncoder(w).Encode(user)
|
|
|
+}
|
|
|
+
|
|
|
+// swagger:route GET /api/v1/users user getUserV1
|
|
|
//
|
|
|
-// Get an individual user.
|
|
|
+// Get an individual user with role info.
|
|
|
//
|
|
|
// Schemes: https
|
|
|
//
|
|
@@ -187,22 +236,32 @@ func hasSuperAdmin(w http.ResponseWriter, r *http.Request) {
|
|
|
// oauth
|
|
|
//
|
|
|
// Responses:
|
|
|
-// 200: userBodyResponse
|
|
|
-func getUser(w http.ResponseWriter, r *http.Request) {
|
|
|
+// 200: ReturnUserWithRolesAndGroups
|
|
|
+func getUserV1(w http.ResponseWriter, r *http.Request) {
|
|
|
// set header.
|
|
|
w.Header().Set("Content-Type", "application/json")
|
|
|
-
|
|
|
- var params = mux.Vars(r)
|
|
|
- usernameFetched := params["username"]
|
|
|
+ usernameFetched, _ := url.QueryUnescape(r.URL.Query().Get("username"))
|
|
|
+ if usernameFetched == "" {
|
|
|
+ logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("username is required"), "badrequest"))
|
|
|
+ return
|
|
|
+ }
|
|
|
user, err := logic.GetReturnUser(usernameFetched)
|
|
|
-
|
|
|
if err != nil {
|
|
|
logger.Log(0, usernameFetched, "failed to fetch user: ", err.Error())
|
|
|
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
|
|
|
return
|
|
|
}
|
|
|
+ userRoleTemplate, err := logic.GetRole(user.PlatformRoleID)
|
|
|
+ if err != nil {
|
|
|
+ logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
|
|
|
+ return
|
|
|
+ }
|
|
|
+ resp := models.ReturnUserWithRolesAndGroups{
|
|
|
+ ReturnUser: user,
|
|
|
+ PlatformRole: userRoleTemplate,
|
|
|
+ }
|
|
|
logger.Log(2, r.Header.Get("user"), "fetched user", usernameFetched)
|
|
|
- json.NewEncoder(w).Encode(user)
|
|
|
+ logic.ReturnSuccessResponseWithJson(w, r, resp, "fetched user with role info")
|
|
|
}
|
|
|
|
|
|
// swagger:route GET /api/users user getUsers
|
|
@@ -233,17 +292,13 @@ func getUsers(w http.ResponseWriter, r *http.Request) {
|
|
|
json.NewEncoder(w).Encode(users)
|
|
|
}
|
|
|
|
|
|
-// swagger:route POST /api/users/adm/createsuperadmin user createAdmin
|
|
|
-//
|
|
|
-// Make a user an admin.
|
|
|
-//
|
|
|
-// Schemes: https
|
|
|
-//
|
|
|
-// Security:
|
|
|
-// oauth
|
|
|
-//
|
|
|
-// Responses:
|
|
|
-// 200: userBodyResponse
|
|
|
+// @Summary Create a super admin
|
|
|
+// @Router /api/users/adm/createsuperadmin [post]
|
|
|
+// @Tags Users
|
|
|
+// @Param body body models.User true "User details"
|
|
|
+// @Success 200 {object} models.User
|
|
|
+// @Failure 400 {object} models.ErrorResponse
|
|
|
+// @Failure 500 {object} models.ErrorResponse
|
|
|
func createSuperAdmin(w http.ResponseWriter, r *http.Request) {
|
|
|
w.Header().Set("Content-Type", "application/json")
|
|
|
|
|
@@ -257,7 +312,11 @@ func createSuperAdmin(w http.ResponseWriter, r *http.Request) {
|
|
|
}
|
|
|
|
|
|
if !servercfg.IsBasicAuthEnabled() {
|
|
|
- logic.ReturnErrorResponse(w, r, logic.FormatError(fmt.Errorf("basic auth is disabled"), "badrequest"))
|
|
|
+ logic.ReturnErrorResponse(
|
|
|
+ w,
|
|
|
+ r,
|
|
|
+ logic.FormatError(fmt.Errorf("basic auth is disabled"), "badrequest"),
|
|
|
+ )
|
|
|
return
|
|
|
}
|
|
|
|
|
@@ -271,24 +330,20 @@ func createSuperAdmin(w http.ResponseWriter, r *http.Request) {
|
|
|
json.NewEncoder(w).Encode(logic.ToReturnUser(u))
|
|
|
}
|
|
|
|
|
|
-// swagger:route POST /api/users/adm/transfersuperadmin user transferSuperAdmin
|
|
|
-//
|
|
|
-// Transfers superadmin role to an admin user.
|
|
|
-//
|
|
|
-// Schemes: https
|
|
|
-//
|
|
|
-// Security:
|
|
|
-// oauth
|
|
|
-//
|
|
|
-// Responses:
|
|
|
-// 200: userBodyResponse
|
|
|
+// @Summary Transfer super admin role to another admin user
|
|
|
+// @Router /api/users/adm/transfersuperadmin/{username} [post]
|
|
|
+// @Tags Users
|
|
|
+// @Param username path string true "Username of the user to transfer super admin role"
|
|
|
+// @Success 200 {object} models.User
|
|
|
+// @Failure 403 {object} models.ErrorResponse
|
|
|
+// @Failure 500 {object} models.ErrorResponse
|
|
|
func transferSuperAdmin(w http.ResponseWriter, r *http.Request) {
|
|
|
w.Header().Set("Content-Type", "application/json")
|
|
|
caller, err := logic.GetUser(r.Header.Get("user"))
|
|
|
if err != nil {
|
|
|
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
|
|
|
}
|
|
|
- if !caller.IsSuperAdmin {
|
|
|
+ if caller.PlatformRoleID != models.SuperAdminRole {
|
|
|
logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("only superadmin can assign the superadmin role to another user"), "forbidden"))
|
|
|
return
|
|
|
}
|
|
@@ -300,25 +355,27 @@ func transferSuperAdmin(w http.ResponseWriter, r *http.Request) {
|
|
|
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
|
|
|
return
|
|
|
}
|
|
|
- if !u.IsAdmin {
|
|
|
+ if u.PlatformRoleID != models.AdminRole {
|
|
|
logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("only admins can be promoted to superadmin role"), "forbidden"))
|
|
|
return
|
|
|
}
|
|
|
if !servercfg.IsBasicAuthEnabled() {
|
|
|
- logic.ReturnErrorResponse(w, r, logic.FormatError(fmt.Errorf("basic auth is disabled"), "badrequest"))
|
|
|
+ logic.ReturnErrorResponse(
|
|
|
+ w,
|
|
|
+ r,
|
|
|
+ logic.FormatError(fmt.Errorf("basic auth is disabled"), "badrequest"),
|
|
|
+ )
|
|
|
return
|
|
|
}
|
|
|
|
|
|
- u.IsSuperAdmin = true
|
|
|
- u.IsAdmin = false
|
|
|
+ u.PlatformRoleID = models.SuperAdminRole
|
|
|
err = logic.UpsertUser(*u)
|
|
|
if err != nil {
|
|
|
slog.Error("error updating user to superadmin: ", "user", u.UserName, "error", err.Error())
|
|
|
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
|
|
|
return
|
|
|
}
|
|
|
- caller.IsSuperAdmin = false
|
|
|
- caller.IsAdmin = true
|
|
|
+ caller.PlatformRoleID = models.AdminRole
|
|
|
err = logic.UpsertUser(*caller)
|
|
|
if err != nil {
|
|
|
slog.Error("error demoting user to admin: ", "user", caller.UserName, "error", err.Error())
|
|
@@ -329,22 +386,20 @@ func transferSuperAdmin(w http.ResponseWriter, r *http.Request) {
|
|
|
json.NewEncoder(w).Encode(logic.ToReturnUser(*u))
|
|
|
}
|
|
|
|
|
|
-// swagger:route POST /api/users/{username} user createUser
|
|
|
-//
|
|
|
-// Create a user.
|
|
|
-//
|
|
|
-// Schemes: https
|
|
|
-//
|
|
|
-// Security:
|
|
|
-// oauth
|
|
|
-//
|
|
|
-// Responses:
|
|
|
-// 200: userBodyResponse
|
|
|
+// @Summary Create a user
|
|
|
+// @Router /api/users/{username} [post]
|
|
|
+// @Tags Users
|
|
|
+// @Param username path string true "Username of the user to create"
|
|
|
+// @Param body body models.User true "User details"
|
|
|
+// @Success 200 {object} models.User
|
|
|
+// @Failure 400 {object} models.ErrorResponse
|
|
|
+// @Failure 403 {object} models.ErrorResponse
|
|
|
+// @Failure 500 {object} models.ErrorResponse
|
|
|
func createUser(w http.ResponseWriter, r *http.Request) {
|
|
|
w.Header().Set("Content-Type", "application/json")
|
|
|
caller, err := logic.GetUser(r.Header.Get("user"))
|
|
|
if err != nil {
|
|
|
- logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
|
|
|
+ logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
|
|
|
return
|
|
|
}
|
|
|
var user models.User
|
|
@@ -355,19 +410,36 @@ func createUser(w http.ResponseWriter, r *http.Request) {
|
|
|
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
|
|
|
return
|
|
|
}
|
|
|
- if !caller.IsSuperAdmin && user.IsAdmin {
|
|
|
- err = errors.New("only superadmin can create admin users")
|
|
|
+ if !servercfg.IsPro {
|
|
|
+ user.PlatformRoleID = models.AdminRole
|
|
|
+ }
|
|
|
+
|
|
|
+ if user.PlatformRoleID == "" {
|
|
|
+ logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("platform role is missing"), "badrequest"))
|
|
|
+ return
|
|
|
+ }
|
|
|
+ userRole, err := logic.GetRole(user.PlatformRoleID)
|
|
|
+ if err != nil {
|
|
|
+ err = errors.New("error fetching role " + user.PlatformRoleID.String() + " " + err.Error())
|
|
|
slog.Error("error creating new user: ", "user", user.UserName, "error", err)
|
|
|
- logic.ReturnErrorResponse(w, r, logic.FormatError(err, "forbidden"))
|
|
|
+ logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
|
|
|
return
|
|
|
}
|
|
|
- if user.IsSuperAdmin {
|
|
|
+ if userRole.ID == models.SuperAdminRole {
|
|
|
err = errors.New("additional superadmins cannot be created")
|
|
|
slog.Error("error creating new user: ", "user", user.UserName, "error", err)
|
|
|
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "forbidden"))
|
|
|
return
|
|
|
}
|
|
|
- if !servercfg.IsPro && !user.IsAdmin {
|
|
|
+
|
|
|
+ if caller.PlatformRoleID != models.SuperAdminRole && user.PlatformRoleID == models.AdminRole {
|
|
|
+ err = errors.New("only superadmin can create admin users")
|
|
|
+ slog.Error("error creating new user: ", "user", user.UserName, "error", err)
|
|
|
+ logic.ReturnErrorResponse(w, r, logic.FormatError(err, "forbidden"))
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ if !servercfg.IsPro && user.PlatformRoleID != models.AdminRole {
|
|
|
logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("non-admins users can only be created on Pro version"), "forbidden"))
|
|
|
return
|
|
|
}
|
|
@@ -378,21 +450,21 @@ func createUser(w http.ResponseWriter, r *http.Request) {
|
|
|
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
|
|
|
return
|
|
|
}
|
|
|
+ logic.DeleteUserInvite(user.UserName)
|
|
|
+ logic.DeletePendingUser(user.UserName)
|
|
|
slog.Info("user was created", "username", user.UserName)
|
|
|
json.NewEncoder(w).Encode(logic.ToReturnUser(user))
|
|
|
}
|
|
|
|
|
|
-// swagger:route PUT /api/users/{username} user updateUser
|
|
|
-//
|
|
|
-// Update a user.
|
|
|
-//
|
|
|
-// Schemes: https
|
|
|
-//
|
|
|
-// Security:
|
|
|
-// oauth
|
|
|
-//
|
|
|
-// Responses:
|
|
|
-// 200: userBodyResponse
|
|
|
+// @Summary Update a user
|
|
|
+// @Router /api/users/{username} [put]
|
|
|
+// @Tags Users
|
|
|
+// @Param username path string true "Username of the user to update"
|
|
|
+// @Param body body models.User true "User details"
|
|
|
+// @Success 200 {object} models.User
|
|
|
+// @Failure 400 {object} models.ErrorResponse
|
|
|
+// @Failure 403 {object} models.ErrorResponse
|
|
|
+// @Failure 500 {object} models.ErrorResponse
|
|
|
func updateUser(w http.ResponseWriter, r *http.Request) {
|
|
|
w.Header().Set("Content-Type", "application/json")
|
|
|
var params = mux.Vars(r)
|
|
@@ -426,7 +498,14 @@ func updateUser(w http.ResponseWriter, r *http.Request) {
|
|
|
return
|
|
|
}
|
|
|
if user.UserName != userchange.UserName {
|
|
|
- logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("user in param and request body not matching"), "badrequest"))
|
|
|
+ logic.ReturnErrorResponse(
|
|
|
+ w,
|
|
|
+ r,
|
|
|
+ logic.FormatError(
|
|
|
+ errors.New("user in param and request body not matching"),
|
|
|
+ "badrequest",
|
|
|
+ ),
|
|
|
+ )
|
|
|
return
|
|
|
}
|
|
|
selfUpdate := false
|
|
@@ -435,46 +514,71 @@ func updateUser(w http.ResponseWriter, r *http.Request) {
|
|
|
}
|
|
|
|
|
|
if !ismaster && !selfUpdate {
|
|
|
- if caller.IsAdmin && user.IsSuperAdmin {
|
|
|
+ if caller.PlatformRoleID == models.AdminRole && user.PlatformRoleID == models.SuperAdminRole {
|
|
|
slog.Error("non-superadmin user", "caller", caller.UserName, "attempted to update superadmin user", username)
|
|
|
logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("cannot update superadmin user"), "forbidden"))
|
|
|
return
|
|
|
}
|
|
|
- if !caller.IsAdmin && !caller.IsSuperAdmin {
|
|
|
+ if caller.PlatformRoleID != models.AdminRole && caller.PlatformRoleID != models.SuperAdminRole {
|
|
|
slog.Error("operation not allowed", "caller", caller.UserName, "attempted to update user", username)
|
|
|
logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("cannot update superadmin user"), "forbidden"))
|
|
|
return
|
|
|
}
|
|
|
- if caller.IsAdmin && user.IsAdmin {
|
|
|
+ if caller.PlatformRoleID == models.AdminRole && user.PlatformRoleID == models.AdminRole {
|
|
|
slog.Error("admin user cannot update another admin", "caller", caller.UserName, "attempted to update admin user", username)
|
|
|
logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("admin user cannot update another admin"), "forbidden"))
|
|
|
return
|
|
|
}
|
|
|
- if caller.IsAdmin && userchange.IsAdmin {
|
|
|
+ if caller.PlatformRoleID == models.AdminRole && userchange.PlatformRoleID == models.AdminRole {
|
|
|
err = errors.New("admin user cannot update role of an another user to admin")
|
|
|
- slog.Error("failed to update user", "caller", caller.UserName, "attempted to update user", username, "error", err)
|
|
|
+ slog.Error(
|
|
|
+ "failed to update user",
|
|
|
+ "caller",
|
|
|
+ caller.UserName,
|
|
|
+ "attempted to update user",
|
|
|
+ username,
|
|
|
+ "error",
|
|
|
+ err,
|
|
|
+ )
|
|
|
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "forbidden"))
|
|
|
return
|
|
|
}
|
|
|
|
|
|
}
|
|
|
if !ismaster && selfUpdate {
|
|
|
- if user.IsAdmin != userchange.IsAdmin || user.IsSuperAdmin != userchange.IsSuperAdmin {
|
|
|
+ if user.PlatformRoleID != userchange.PlatformRoleID {
|
|
|
slog.Error("user cannot change his own role", "caller", caller.UserName, "attempted to update user role", username)
|
|
|
logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("user not allowed to self assign role"), "forbidden"))
|
|
|
return
|
|
|
|
|
|
}
|
|
|
+ if servercfg.IsPro {
|
|
|
+ // user cannot update his own roles and groups
|
|
|
+ if len(user.NetworkRoles) != len(userchange.NetworkRoles) || !reflect.DeepEqual(user.NetworkRoles, userchange.NetworkRoles) {
|
|
|
+ err = errors.New("user cannot update self update their network roles")
|
|
|
+ slog.Error("failed to update user", "caller", caller.UserName, "attempted to update user", username, "error", err)
|
|
|
+ logic.ReturnErrorResponse(w, r, logic.FormatError(err, "forbidden"))
|
|
|
+ return
|
|
|
+ }
|
|
|
+ // user cannot update his own roles and groups
|
|
|
+ if len(user.UserGroups) != len(userchange.UserGroups) || !reflect.DeepEqual(user.UserGroups, userchange.UserGroups) {
|
|
|
+ err = errors.New("user cannot update self update their groups")
|
|
|
+ slog.Error("failed to update user", "caller", caller.UserName, "attempted to update user", username, "error", err)
|
|
|
+ logic.ReturnErrorResponse(w, r, logic.FormatError(err, "forbidden"))
|
|
|
+ return
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
}
|
|
|
if ismaster {
|
|
|
- if !user.IsSuperAdmin && userchange.IsSuperAdmin {
|
|
|
+ if user.PlatformRoleID != models.SuperAdminRole && userchange.PlatformRoleID == models.SuperAdminRole {
|
|
|
slog.Error("operation not allowed", "caller", logic.MasterUser, "attempted to update user role to superadmin", username)
|
|
|
logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("attempted to update user role to superadmin"), "forbidden"))
|
|
|
return
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- if auth.IsOauthUser(user) == nil && userchange.Password != "" {
|
|
|
+ if logic.IsOauthUser(user) == nil && userchange.Password != "" {
|
|
|
err := fmt.Errorf("cannot update user's password for an oauth user %s", username)
|
|
|
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "forbidden"))
|
|
|
return
|
|
@@ -491,17 +595,12 @@ func updateUser(w http.ResponseWriter, r *http.Request) {
|
|
|
json.NewEncoder(w).Encode(logic.ToReturnUser(*user))
|
|
|
}
|
|
|
|
|
|
-// swagger:route DELETE /api/users/{username} user deleteUser
|
|
|
-//
|
|
|
-// Delete a user.
|
|
|
-//
|
|
|
-// Schemes: https
|
|
|
-//
|
|
|
-// Security:
|
|
|
-// oauth
|
|
|
-//
|
|
|
-// Responses:
|
|
|
-// 200: userBodyResponse
|
|
|
+// @Summary Delete a user
|
|
|
+// @Router /api/users/{username} [delete]
|
|
|
+// @Tags Users
|
|
|
+// @Param username path string true "Username of the user to delete"
|
|
|
+// @Success 200 {string} string
|
|
|
+// @Failure 500 {object} models.ErrorResponse
|
|
|
func deleteUser(w http.ResponseWriter, r *http.Request) {
|
|
|
// Set header
|
|
|
w.Header().Set("Content-Type", "application/json")
|
|
@@ -512,6 +611,12 @@ func deleteUser(w http.ResponseWriter, r *http.Request) {
|
|
|
if err != nil {
|
|
|
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
|
|
|
}
|
|
|
+ callerUserRole, err := logic.GetRole(caller.PlatformRoleID)
|
|
|
+ if err != nil {
|
|
|
+ slog.Error("failed to get role ", "role", callerUserRole.ID, "error", err)
|
|
|
+ logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
|
|
|
+ return
|
|
|
+ }
|
|
|
username := params["username"]
|
|
|
user, err := logic.GetUser(username)
|
|
|
if err != nil {
|
|
@@ -520,17 +625,39 @@ func deleteUser(w http.ResponseWriter, r *http.Request) {
|
|
|
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
|
|
|
return
|
|
|
}
|
|
|
- if user.IsSuperAdmin {
|
|
|
+ userRole, err := logic.GetRole(user.PlatformRoleID)
|
|
|
+ if err != nil {
|
|
|
+ slog.Error("failed to get role ", "role", userRole.ID, "error", err)
|
|
|
+ logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
|
|
|
+ return
|
|
|
+ }
|
|
|
+ if userRole.ID == models.SuperAdminRole {
|
|
|
slog.Error(
|
|
|
"failed to delete user: ", "user", username, "error", "superadmin cannot be deleted")
|
|
|
- logic.ReturnErrorResponse(w, r, logic.FormatError(fmt.Errorf("superadmin cannot be deleted"), "internal"))
|
|
|
+ logic.ReturnErrorResponse(
|
|
|
+ w,
|
|
|
+ r,
|
|
|
+ logic.FormatError(fmt.Errorf("superadmin cannot be deleted"), "internal"),
|
|
|
+ )
|
|
|
return
|
|
|
}
|
|
|
- if !caller.IsSuperAdmin {
|
|
|
- if caller.IsAdmin && user.IsAdmin {
|
|
|
+ if callerUserRole.ID != models.SuperAdminRole {
|
|
|
+ if callerUserRole.ID == models.AdminRole && userRole.ID == models.AdminRole {
|
|
|
slog.Error(
|
|
|
- "failed to delete user: ", "user", username, "error", "admin cannot delete another admin user, including oneself")
|
|
|
- logic.ReturnErrorResponse(w, r, logic.FormatError(fmt.Errorf("admin cannot delete another admin user, including oneself"), "internal"))
|
|
|
+ "failed to delete user: ",
|
|
|
+ "user",
|
|
|
+ username,
|
|
|
+ "error",
|
|
|
+ "admin cannot delete another admin user, including oneself",
|
|
|
+ )
|
|
|
+ logic.ReturnErrorResponse(
|
|
|
+ w,
|
|
|
+ r,
|
|
|
+ logic.FormatError(
|
|
|
+ fmt.Errorf("admin cannot delete another admin user, including oneself"),
|
|
|
+ "internal",
|
|
|
+ ),
|
|
|
+ )
|
|
|
return
|
|
|
}
|
|
|
}
|
|
@@ -555,10 +682,14 @@ func deleteUser(w http.ResponseWriter, r *http.Request) {
|
|
|
}
|
|
|
for _, extclient := range extclients {
|
|
|
if extclient.OwnerID == user.UserName {
|
|
|
- err = logic.DeleteExtClient(extclient.Network, extclient.ClientID)
|
|
|
+ err = logic.DeleteExtClientAndCleanup(extclient)
|
|
|
if err != nil {
|
|
|
slog.Error("failed to delete extclient",
|
|
|
- "id", extclient.ClientID, "owner", user.UserName, "error", err)
|
|
|
+ "id", extclient.ClientID, "owner", username, "error", err)
|
|
|
+ } else {
|
|
|
+ if err := mq.PublishDeletedClientPeerUpdate(&extclient); err != nil {
|
|
|
+ slog.Error("error setting ext peers: " + err.Error())
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
}
|
|
@@ -586,135 +717,23 @@ func socketHandler(w http.ResponseWriter, r *http.Request) {
|
|
|
go auth.SessionHandler(conn)
|
|
|
}
|
|
|
|
|
|
-// swagger:route GET /api/users_pending user getPendingUsers
|
|
|
-//
|
|
|
-// Get all pending users.
|
|
|
-//
|
|
|
-// Schemes: https
|
|
|
-//
|
|
|
-// Security:
|
|
|
-// oauth
|
|
|
-//
|
|
|
-// Responses:
|
|
|
-// 200: userBodyResponse
|
|
|
-func getPendingUsers(w http.ResponseWriter, r *http.Request) {
|
|
|
- // set header.
|
|
|
- w.Header().Set("Content-Type", "application/json")
|
|
|
-
|
|
|
- users, err := logic.ListPendingUsers()
|
|
|
- if err != nil {
|
|
|
- logger.Log(0, "failed to fetch users: ", err.Error())
|
|
|
- logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
|
|
|
- return
|
|
|
- }
|
|
|
-
|
|
|
- logic.SortUsers(users[:])
|
|
|
- logger.Log(2, r.Header.Get("user"), "fetched pending users")
|
|
|
- json.NewEncoder(w).Encode(users)
|
|
|
-}
|
|
|
-
|
|
|
-// swagger:route POST /api/users_pending/user/{username} user approvePendingUser
|
|
|
-//
|
|
|
-// approve pending user.
|
|
|
-//
|
|
|
-// Schemes: https
|
|
|
-//
|
|
|
-// Security:
|
|
|
-// oauth
|
|
|
-//
|
|
|
-// Responses:
|
|
|
-// 200: userBodyResponse
|
|
|
-func approvePendingUser(w http.ResponseWriter, r *http.Request) {
|
|
|
- // set header.
|
|
|
- w.Header().Set("Content-Type", "application/json")
|
|
|
- var params = mux.Vars(r)
|
|
|
- username := params["username"]
|
|
|
- users, err := logic.ListPendingUsers()
|
|
|
-
|
|
|
- if err != nil {
|
|
|
- logger.Log(0, "failed to fetch users: ", err.Error())
|
|
|
- logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
|
|
|
- return
|
|
|
- }
|
|
|
- for _, user := range users {
|
|
|
- if user.UserName == username {
|
|
|
- var newPass, fetchErr = auth.FetchPassValue("")
|
|
|
- if fetchErr != nil {
|
|
|
- logic.ReturnErrorResponse(w, r, logic.FormatError(fetchErr, "internal"))
|
|
|
- return
|
|
|
- }
|
|
|
- if err = logic.CreateUser(&models.User{
|
|
|
- UserName: user.UserName,
|
|
|
- Password: newPass,
|
|
|
- }); err != nil {
|
|
|
- logic.ReturnErrorResponse(w, r, logic.FormatError(fmt.Errorf("failed to create user: %s", err), "internal"))
|
|
|
- return
|
|
|
- }
|
|
|
- err = logic.DeletePendingUser(username)
|
|
|
- if err != nil {
|
|
|
- logic.ReturnErrorResponse(w, r, logic.FormatError(fmt.Errorf("failed to delete pending user: %s", err), "internal"))
|
|
|
- return
|
|
|
- }
|
|
|
- break
|
|
|
- }
|
|
|
- }
|
|
|
- logic.ReturnSuccessResponse(w, r, "approved "+username)
|
|
|
-}
|
|
|
-
|
|
|
-// swagger:route DELETE /api/users_pending/user/{username} user deletePendingUser
|
|
|
-//
|
|
|
-// delete pending user.
|
|
|
-//
|
|
|
-// Schemes: https
|
|
|
-//
|
|
|
-// Security:
|
|
|
-// oauth
|
|
|
-//
|
|
|
-// Responses:
|
|
|
-// 200: userBodyResponse
|
|
|
-func deletePendingUser(w http.ResponseWriter, r *http.Request) {
|
|
|
- // set header.
|
|
|
- w.Header().Set("Content-Type", "application/json")
|
|
|
- var params = mux.Vars(r)
|
|
|
- username := params["username"]
|
|
|
- users, err := logic.ListPendingUsers()
|
|
|
-
|
|
|
+// @Summary lists all user roles.
|
|
|
+// @Router /api/v1/user/roles [get]
|
|
|
+// @Tags Users
|
|
|
+// @Param role_id param string true "roleid required to get the role details"
|
|
|
+// @Success 200 {object} []models.UserRolePermissionTemplate
|
|
|
+// @Failure 500 {object} models.ErrorResponse
|
|
|
+func listRoles(w http.ResponseWriter, r *http.Request) {
|
|
|
+ var roles []models.UserRolePermissionTemplate
|
|
|
+ var err error
|
|
|
+ roles, err = logic.ListPlatformRoles()
|
|
|
if err != nil {
|
|
|
- logger.Log(0, "failed to fetch users: ", err.Error())
|
|
|
- logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
|
|
|
+ logic.ReturnErrorResponse(w, r, models.ErrorResponse{
|
|
|
+ Code: http.StatusInternalServerError,
|
|
|
+ Message: err.Error(),
|
|
|
+ })
|
|
|
return
|
|
|
}
|
|
|
- for _, user := range users {
|
|
|
- if user.UserName == username {
|
|
|
- err = logic.DeletePendingUser(username)
|
|
|
- if err != nil {
|
|
|
- logic.ReturnErrorResponse(w, r, logic.FormatError(fmt.Errorf("failed to delete pending user: %s", err), "internal"))
|
|
|
- return
|
|
|
- }
|
|
|
- break
|
|
|
- }
|
|
|
- }
|
|
|
- logic.ReturnSuccessResponse(w, r, "deleted pending "+username)
|
|
|
-}
|
|
|
|
|
|
-// swagger:route DELETE /api/users_pending/{username}/pending user deleteAllPendingUsers
|
|
|
-//
|
|
|
-// delete all pending users.
|
|
|
-//
|
|
|
-// Schemes: https
|
|
|
-//
|
|
|
-// Security:
|
|
|
-// oauth
|
|
|
-//
|
|
|
-// Responses:
|
|
|
-// 200: userBodyResponse
|
|
|
-func deleteAllPendingUsers(w http.ResponseWriter, r *http.Request) {
|
|
|
- // set header.
|
|
|
- w.Header().Set("Content-Type", "application/json")
|
|
|
- err := database.DeleteAllRecords(database.PENDING_USERS_TABLE_NAME)
|
|
|
- if err != nil {
|
|
|
- logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("failed to delete all pending users "+err.Error()), "internal"))
|
|
|
- return
|
|
|
- }
|
|
|
- logic.ReturnSuccessResponse(w, r, "cleared all pending users")
|
|
|
+ logic.ReturnSuccessResponseWithJson(w, r, roles, "successfully fetched user roles permission templates")
|
|
|
}
|