mirror of
				https://github.com/gravitl/netmaker.git
				synced 2025-10-28 07:00:14 +08:00 
			
		
		
		
	
		
			
				
	
	
		
			738 lines
		
	
	
	
		
			25 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			738 lines
		
	
	
	
		
			25 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package controller
 | |
| 
 | |
| import (
 | |
| 	"encoding/json"
 | |
| 	"errors"
 | |
| 	"fmt"
 | |
| 	"net/http"
 | |
| 	"reflect"
 | |
| 
 | |
| 	"github.com/gorilla/mux"
 | |
| 	"github.com/gorilla/websocket"
 | |
| 	"github.com/gravitl/netmaker/auth"
 | |
| 	"github.com/gravitl/netmaker/logger"
 | |
| 	"github.com/gravitl/netmaker/logic"
 | |
| 	"github.com/gravitl/netmaker/models"
 | |
| 	"github.com/gravitl/netmaker/mq"
 | |
| 	"github.com/gravitl/netmaker/servercfg"
 | |
| 	"golang.org/x/exp/slog"
 | |
| )
 | |
| 
 | |
| 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/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/v1/users/roles", logic.SecurityCheck(true, http.HandlerFunc(ListRoles))).Methods(http.MethodGet)
 | |
| 
 | |
| }
 | |
| 
 | |
| // @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
 | |
| 	// in case of Master, auth is ignored and mac is set to "mastermac"
 | |
| 	var authRequest models.UserAuthParams
 | |
| 	var errorResponse = models.ErrorResponse{
 | |
| 		Code: http.StatusInternalServerError, Message: "W1R3: It's not you it's me.",
 | |
| 	}
 | |
| 
 | |
| 	if !servercfg.IsBasicAuthEnabled() {
 | |
| 		logic.ReturnErrorResponse(
 | |
| 			response,
 | |
| 			request,
 | |
| 			logic.FormatError(fmt.Errorf("basic auth is disabled"), "badrequest"),
 | |
| 		)
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	decoder := json.NewDecoder(request.Body)
 | |
| 	decoderErr := decoder.Decode(&authRequest)
 | |
| 	defer request.Body.Close()
 | |
| 	if decoderErr != nil {
 | |
| 		logger.Log(0, "error decoding request body: ",
 | |
| 			decoderErr.Error())
 | |
| 		logic.ReturnErrorResponse(response, request, errorResponse)
 | |
| 		return
 | |
| 	}
 | |
| 	if val := request.Header.Get("From-Ui"); val == "true" {
 | |
| 		// request came from UI, if normal user block Login
 | |
| 		user, err := logic.GetUser(authRequest.UserName)
 | |
| 		if err != nil {
 | |
| 			logger.Log(0, authRequest.UserName, "user validation failed: ",
 | |
| 				err.Error())
 | |
| 			logic.ReturnErrorResponse(response, request, logic.FormatError(err, "unauthorized"))
 | |
| 			return
 | |
| 		}
 | |
| 		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)
 | |
| 	if err != nil {
 | |
| 		logger.Log(0, username, "user validation failed: ",
 | |
| 			err.Error())
 | |
| 		logic.ReturnErrorResponse(response, request, logic.FormatError(err, "badrequest"))
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	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"),
 | |
| 		)
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	var successResponse = models.SuccessResponse{
 | |
| 		Code:    http.StatusOK,
 | |
| 		Message: "W1R3: Device " + username + " Authorized",
 | |
| 		Response: models.SuccessfulUserLoginResponse{
 | |
| 			AuthToken: jwt,
 | |
| 			UserName:  username,
 | |
| 		},
 | |
| 	}
 | |
| 	// Send back the JWT
 | |
| 	successJSONResponse, jsonError := json.Marshal(successResponse)
 | |
| 	if jsonError != nil {
 | |
| 		logger.Log(0, username,
 | |
| 			"error marshalling resp: ", jsonError.Error())
 | |
| 		logic.ReturnErrorResponse(response, request, errorResponse)
 | |
| 		return
 | |
| 	}
 | |
| 	logger.Log(2, username, "was authenticated")
 | |
| 	response.Header().Set("Content-Type", "application/json")
 | |
| 	response.Write(successJSONResponse)
 | |
| 
 | |
| 	go func() {
 | |
| 		if servercfg.IsPro && servercfg.GetRacAutoDisable() {
 | |
| 			// enable all associeated clients for the user
 | |
| 			clients, err := logic.GetAllExtClients()
 | |
| 			if err != nil {
 | |
| 				slog.Error("error getting clients: ", "error", err)
 | |
| 				return
 | |
| 			}
 | |
| 			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,
 | |
| 						),
 | |
| 					)
 | |
| 					if newClient, err := logic.ToggleExtClientConnectivity(&client, true); err != nil {
 | |
| 						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
 | |
| 						if ingressNode, err := logic.GetNodeByID(newClient.IngressGatewayID); err == nil {
 | |
| 							if err = mq.PublishPeerUpdate(false); err != nil {
 | |
| 								slog.Error("error updating ext clients on", "ingress", ingressNode.ID.String(), "err", err.Error())
 | |
| 							}
 | |
| 						}
 | |
| 					}
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 	}()
 | |
| }
 | |
| 
 | |
| // @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")
 | |
| 
 | |
| 	hasSuperAdmin, err := logic.HasSuperAdmin()
 | |
| 	if err != nil {
 | |
| 		logger.Log(0, "failed to check for admin: ", err.Error())
 | |
| 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	json.NewEncoder(w).Encode(hasSuperAdmin)
 | |
| 
 | |
| }
 | |
| 
 | |
| // @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 with role info.
 | |
| //
 | |
| //			Schemes: https
 | |
| //
 | |
| //			Security:
 | |
| //	  		oauth
 | |
| //
 | |
| //			Responses:
 | |
| //				200: ReturnUserWithRolesAndGroups
 | |
| func getUserV1(w http.ResponseWriter, r *http.Request) {
 | |
| 	// set header.
 | |
| 	w.Header().Set("Content-Type", "application/json")
 | |
| 	usernameFetched := 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)
 | |
| 	logic.ReturnSuccessResponseWithJson(w, r, resp, "fetched user with role info")
 | |
| }
 | |
| 
 | |
| // swagger:route GET /api/users user getUsers
 | |
| //
 | |
| // Get all users.
 | |
| //
 | |
| //			Schemes: https
 | |
| //
 | |
| //			Security:
 | |
| //	  		oauth
 | |
| //
 | |
| //			Responses:
 | |
| //				200: userBodyResponse
 | |
| func getUsers(w http.ResponseWriter, r *http.Request) {
 | |
| 	// set header.
 | |
| 	w.Header().Set("Content-Type", "application/json")
 | |
| 
 | |
| 	users, err := logic.GetUsers()
 | |
| 
 | |
| 	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 users")
 | |
| 	json.NewEncoder(w).Encode(users)
 | |
| }
 | |
| 
 | |
| // @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")
 | |
| 
 | |
| 	var u models.User
 | |
| 
 | |
| 	err := json.NewDecoder(r.Body).Decode(&u)
 | |
| 	if err != nil {
 | |
| 		slog.Error("error decoding request body", "error", err.Error())
 | |
| 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	if !servercfg.IsBasicAuthEnabled() {
 | |
| 		logic.ReturnErrorResponse(
 | |
| 			w,
 | |
| 			r,
 | |
| 			logic.FormatError(fmt.Errorf("basic auth is disabled"), "badrequest"),
 | |
| 		)
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	err = logic.CreateSuperAdmin(&u)
 | |
| 	if err != nil {
 | |
| 		slog.Error("failed to create admin", "error", err.Error())
 | |
| 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
 | |
| 		return
 | |
| 	}
 | |
| 	logger.Log(1, u.UserName, "was made a super admin")
 | |
| 	json.NewEncoder(w).Encode(logic.ToReturnUser(u))
 | |
| }
 | |
| 
 | |
| // @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.PlatformRoleID != models.SuperAdminRole {
 | |
| 		logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("only superadmin can assign the superadmin role to another user"), "forbidden"))
 | |
| 		return
 | |
| 	}
 | |
| 	var params = mux.Vars(r)
 | |
| 	username := params["username"]
 | |
| 	u, err := logic.GetUser(username)
 | |
| 	if err != nil {
 | |
| 		slog.Error("error getting user", "user", u.UserName, "error", err.Error())
 | |
| 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
 | |
| 		return
 | |
| 	}
 | |
| 	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"),
 | |
| 		)
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	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.PlatformRoleID = models.AdminRole
 | |
| 	err = logic.UpsertUser(*caller)
 | |
| 	if err != nil {
 | |
| 		slog.Error("error demoting user to admin: ", "user", caller.UserName, "error", err.Error())
 | |
| 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
 | |
| 		return
 | |
| 	}
 | |
| 	slog.Info("user was made a super admin", "user", u.UserName)
 | |
| 	json.NewEncoder(w).Encode(logic.ToReturnUser(*u))
 | |
| }
 | |
| 
 | |
| // @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, "badrequest"))
 | |
| 		return
 | |
| 	}
 | |
| 	var user models.User
 | |
| 	err = json.NewDecoder(r.Body).Decode(&user)
 | |
| 	if err != nil {
 | |
| 		logger.Log(0, user.UserName, "error decoding request body: ",
 | |
| 			err.Error())
 | |
| 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
 | |
| 		return
 | |
| 	}
 | |
| 	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, "badrequest"))
 | |
| 		return
 | |
| 	}
 | |
| 	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 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
 | |
| 	}
 | |
| 
 | |
| 	err = logic.CreateUser(&user)
 | |
| 	if err != nil {
 | |
| 		slog.Error("error creating new user: ", "user", user.UserName, "error", err.Error())
 | |
| 		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))
 | |
| }
 | |
| 
 | |
| // @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)
 | |
| 	// start here
 | |
| 	var caller *models.User
 | |
| 	var err error
 | |
| 	var ismaster bool
 | |
| 	if r.Header.Get("user") == logic.MasterUser {
 | |
| 		ismaster = true
 | |
| 	} else {
 | |
| 		caller, err = logic.GetUser(r.Header.Get("user"))
 | |
| 		if err != nil {
 | |
| 			logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	username := params["username"]
 | |
| 	user, err := logic.GetUser(username)
 | |
| 	if err != nil {
 | |
| 		logger.Log(0, username,
 | |
| 			"failed to update user info: ", err.Error())
 | |
| 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
 | |
| 		return
 | |
| 	}
 | |
| 	var userchange models.User
 | |
| 	// we decode our body request params
 | |
| 	err = json.NewDecoder(r.Body).Decode(&userchange)
 | |
| 	if err != nil {
 | |
| 		slog.Error("failed to decode body", "error ", err.Error())
 | |
| 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
 | |
| 		return
 | |
| 	}
 | |
| 	if user.UserName != userchange.UserName {
 | |
| 		logic.ReturnErrorResponse(
 | |
| 			w,
 | |
| 			r,
 | |
| 			logic.FormatError(
 | |
| 				errors.New("user in param and request body not matching"),
 | |
| 				"badrequest",
 | |
| 			),
 | |
| 		)
 | |
| 		return
 | |
| 	}
 | |
| 	selfUpdate := false
 | |
| 	if !ismaster && caller.UserName == user.UserName {
 | |
| 		selfUpdate = true
 | |
| 	}
 | |
| 
 | |
| 	if !ismaster && !selfUpdate {
 | |
| 		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.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.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.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,
 | |
| 			)
 | |
| 			logic.ReturnErrorResponse(w, r, logic.FormatError(err, "forbidden"))
 | |
| 			return
 | |
| 		}
 | |
| 
 | |
| 	}
 | |
| 	if !ismaster && selfUpdate {
 | |
| 		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.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 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
 | |
| 	}
 | |
| 
 | |
| 	user, err = logic.UpdateUser(&userchange, user)
 | |
| 	if err != nil {
 | |
| 		logger.Log(0, username,
 | |
| 			"failed to update user info: ", err.Error())
 | |
| 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
 | |
| 		return
 | |
| 	}
 | |
| 	logger.Log(1, username, "was updated")
 | |
| 	json.NewEncoder(w).Encode(logic.ToReturnUser(*user))
 | |
| }
 | |
| 
 | |
| // @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")
 | |
| 
 | |
| 	// get params
 | |
| 	var params = mux.Vars(r)
 | |
| 	caller, err := logic.GetUser(r.Header.Get("user"))
 | |
| 	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 {
 | |
| 		logger.Log(0, username,
 | |
| 			"failed to update user info: ", err.Error())
 | |
| 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
 | |
| 		return
 | |
| 	}
 | |
| 	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"),
 | |
| 		)
 | |
| 		return
 | |
| 	}
 | |
| 	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",
 | |
| 				),
 | |
| 			)
 | |
| 			return
 | |
| 		}
 | |
| 	}
 | |
| 	success, err := logic.DeleteUser(username)
 | |
| 	if err != nil {
 | |
| 		logger.Log(0, username,
 | |
| 			"failed to delete user: ", err.Error())
 | |
| 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
 | |
| 		return
 | |
| 	} else if !success {
 | |
| 		err := errors.New("delete unsuccessful")
 | |
| 		logger.Log(0, username, err.Error())
 | |
| 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
 | |
| 		return
 | |
| 	}
 | |
| 	// check and delete extclient with this ownerID
 | |
| 	go func() {
 | |
| 		extclients, err := logic.GetAllExtClients()
 | |
| 		if err != nil {
 | |
| 			slog.Error("failed to get extclients", "error", err)
 | |
| 			return
 | |
| 		}
 | |
| 		for _, extclient := range extclients {
 | |
| 			if extclient.OwnerID == user.UserName {
 | |
| 				err = logic.DeleteExtClientAndCleanup(extclient)
 | |
| 				if err != nil {
 | |
| 					slog.Error("failed to delete extclient",
 | |
| 						"id", extclient.ClientID, "owner", username, "error", err)
 | |
| 				} else {
 | |
| 					if err := mq.PublishDeletedClientPeerUpdate(&extclient); err != nil {
 | |
| 						slog.Error("error setting ext peers: " + err.Error())
 | |
| 					}
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 		if servercfg.IsDNSMode() {
 | |
| 			logic.SetDNS()
 | |
| 		}
 | |
| 	}()
 | |
| 	logger.Log(1, username, "was deleted")
 | |
| 	json.NewEncoder(w).Encode(params["username"] + " deleted.")
 | |
| }
 | |
| 
 | |
| // Called when vpn client dials in to start the auth flow and first stage is to get register URL itself
 | |
| func socketHandler(w http.ResponseWriter, r *http.Request) {
 | |
| 	// Upgrade our raw HTTP connection to a websocket based one
 | |
| 	conn, err := upgrader.Upgrade(w, r, nil)
 | |
| 	if err != nil {
 | |
| 		logger.Log(0, "error during connection upgrade for node sign-in:", err.Error())
 | |
| 		return
 | |
| 	}
 | |
| 	if conn == nil {
 | |
| 		logger.Log(0, "failed to establish web-socket connection during node sign-in")
 | |
| 		return
 | |
| 	}
 | |
| 	// Start handling the session
 | |
| 	go auth.SessionHandler(conn)
 | |
| }
 | |
| 
 | |
| // @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 {
 | |
| 		logic.ReturnErrorResponse(w, r, models.ErrorResponse{
 | |
| 			Code:    http.StatusInternalServerError,
 | |
| 			Message: err.Error(),
 | |
| 		})
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	logic.ReturnSuccessResponseWithJson(w, r, roles, "successfully fetched user roles permission templates")
 | |
| }
 |