Pārlūkot izejas kodu

move user roles and groups

abhishek9686 1 gadu atpakaļ
vecāks
revīzija
a7a431b36b

+ 9 - 712
controllers/user.go

@@ -1,7 +1,6 @@
 package controller
 
 import (
-	"context"
 	"encoding/json"
 	"errors"
 	"fmt"
@@ -11,8 +10,6 @@ import (
 	"github.com/gorilla/mux"
 	"github.com/gorilla/websocket"
 	"github.com/gravitl/netmaker/auth"
-	"github.com/gravitl/netmaker/database"
-	"github.com/gravitl/netmaker/email"
 	"github.com/gravitl/netmaker/logger"
 	"github.com/gravitl/netmaker/logic"
 	"github.com/gravitl/netmaker/models"
@@ -34,336 +31,9 @@ func userHandlers(r *mux.Router) {
 	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/{username}", logic.SecurityCheck(false, logic.ContinueIfUserMatch(http.HandlerFunc(getUserV1)))).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)
 
-	// User Role Handlers
-	r.HandleFunc("/api/v1/users/roles", logic.SecurityCheck(true, http.HandlerFunc(listRoles))).Methods(http.MethodGet)
-	r.HandleFunc("/api/v1/users/role", getRole).Methods(http.MethodGet)
-	r.HandleFunc("/api/v1/users/role", logic.SecurityCheck(true, http.HandlerFunc(createRole))).Methods(http.MethodPost)
-	r.HandleFunc("/api/v1/users/role", logic.SecurityCheck(true, http.HandlerFunc(updateRole))).Methods(http.MethodPut)
-	r.HandleFunc("/api/v1/users/role", logic.SecurityCheck(true, http.HandlerFunc(deleteRole))).Methods(http.MethodDelete)
-
-	// User Group Handlers
-	r.HandleFunc("/api/v1/users/groups", logic.SecurityCheck(true, http.HandlerFunc(listUserGroups))).Methods(http.MethodGet)
-	r.HandleFunc("/api/v1/users/group", logic.SecurityCheck(true, http.HandlerFunc(getUserGroup))).Methods(http.MethodGet)
-	r.HandleFunc("/api/v1/users/group", logic.SecurityCheck(true, http.HandlerFunc(createUserGroup))).Methods(http.MethodPost)
-	r.HandleFunc("/api/v1/users/group", logic.SecurityCheck(true, http.HandlerFunc(updateUserGroup))).Methods(http.MethodPut)
-	r.HandleFunc("/api/v1/users/group", logic.SecurityCheck(true, http.HandlerFunc(deleteUserGroup))).Methods(http.MethodDelete)
-
-	// User Invite Handlers
-	r.HandleFunc("/api/v1/users/invite", userInviteVerify).Methods(http.MethodGet)
-	r.HandleFunc("/api/v1/users/invite-signup", userInviteSignUp).Methods(http.MethodPost)
-	r.HandleFunc("/api/v1/users/invite", logic.SecurityCheck(true, http.HandlerFunc(inviteUsers))).Methods(http.MethodPost)
-	r.HandleFunc("/api/v1/users/invites", logic.SecurityCheck(true, http.HandlerFunc(listUserInvites))).Methods(http.MethodGet)
-	r.HandleFunc("/api/v1/users/invite", logic.SecurityCheck(true, http.HandlerFunc(deleteUserInvite))).Methods(http.MethodDelete)
-	r.HandleFunc("/api/v1/users/invites", logic.SecurityCheck(true, http.HandlerFunc(deleteAllUserInvites))).Methods(http.MethodDelete)
-
-}
-
-// swagger:route GET /api/v1/user/groups user listUserGroups
-//
-// Get all user groups.
-//
-//			Schemes: https
-//
-//			Security:
-//	  		oauth
-//
-//			Responses:
-//				200: userBodyResponse
-func listUserGroups(w http.ResponseWriter, r *http.Request) {
-	groups, err := logic.ListUserGroups()
-	if err != nil {
-		logic.ReturnErrorResponse(w, r, models.ErrorResponse{
-			Code:    http.StatusInternalServerError,
-			Message: err.Error(),
-		})
-		return
-	}
-	logic.ReturnSuccessResponseWithJson(w, r, groups, "successfully fetched user groups")
-}
-
-// swagger:route GET /api/v1/user/group user getUserGroup
-//
-// Get user group.
-//
-//			Schemes: https
-//
-//			Security:
-//	  		oauth
-//
-//			Responses:
-//				200: userBodyResponse
-func getUserGroup(w http.ResponseWriter, r *http.Request) {
-
-	gid, _ := url.QueryUnescape(r.URL.Query().Get("group_id"))
-	if gid == "" {
-		logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("group id is required"), "badrequest"))
-		return
-	}
-	group, err := logic.GetUserGroup(models.UserGroupID(gid))
-	if err != nil {
-		logic.ReturnErrorResponse(w, r, models.ErrorResponse{
-			Code:    http.StatusInternalServerError,
-			Message: err.Error(),
-		})
-		return
-	}
-	logic.ReturnSuccessResponseWithJson(w, r, group, "successfully fetched user group")
-}
-
-// swagger:route POST /api/v1/user/group user createUserGroup
-//
-// Create user groups.
-//
-//			Schemes: https
-//
-//			Security:
-//	  		oauth
-//
-//			Responses:
-//				200: userBodyResponse
-func createUserGroup(w http.ResponseWriter, r *http.Request) {
-	var userGroupReq models.CreateGroupReq
-	err := json.NewDecoder(r.Body).Decode(&userGroupReq)
-	if err != nil {
-		slog.Error("error decoding request body", "error",
-			err.Error())
-		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
-		return
-	}
-	err = logic.ValidateCreateGroupReq(userGroupReq.Group)
-	if err != nil {
-		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
-		return
-	}
-	err = logic.CreateUserGroup(userGroupReq.Group)
-	if err != nil {
-		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
-		return
-	}
-	for _, userID := range userGroupReq.Members {
-		user, err := logic.GetUser(userID)
-		if err != nil {
-			continue
-		}
-		if len(user.UserGroups) == 0 {
-			user.UserGroups = make(map[models.UserGroupID]struct{})
-		}
-		user.UserGroups[userGroupReq.Group.ID] = struct{}{}
-		logic.UpsertUser(*user)
-	}
-	logic.ReturnSuccessResponseWithJson(w, r, userGroupReq.Group, "created user group")
-}
-
-// swagger:route PUT /api/v1/user/group user updateUserGroup
-//
-// Update user group.
-//
-//			Schemes: https
-//
-//			Security:
-//	  		oauth
-//
-//			Responses:
-//				200: userBodyResponse
-func updateUserGroup(w http.ResponseWriter, r *http.Request) {
-	var userGroup models.UserGroup
-	err := json.NewDecoder(r.Body).Decode(&userGroup)
-	if err != nil {
-		slog.Error("error decoding request body", "error",
-			err.Error())
-		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
-		return
-	}
-	err = logic.ValidateUpdateGroupReq(userGroup)
-	if err != nil {
-		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
-		return
-	}
-	err = logic.UpdateUserGroup(userGroup)
-	if err != nil {
-		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
-		return
-	}
-	logic.ReturnSuccessResponseWithJson(w, r, userGroup, "updated user group")
-}
-
-// swagger:route DELETE /api/v1/user/group user deleteUserGroup
-//
-// delete user group.
-//
-//			Schemes: https
-//
-//			Security:
-//	  		oauth
-//
-//			Responses:
-//				200: userBodyResponse
-func deleteUserGroup(w http.ResponseWriter, r *http.Request) {
-
-	gid, _ := url.QueryUnescape(r.URL.Query().Get("group_id"))
-	if gid == "" {
-		logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("role is required"), "badrequest"))
-		return
-	}
-	err := logic.DeleteUserGroup(models.UserGroupID(gid))
-	if err != nil {
-		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
-		return
-	}
-	logic.ReturnSuccessResponseWithJson(w, r, nil, "deleted user group")
-}
-
-// swagger:route GET /api/v1/user/roles user listRoles
-//
-// lists all user roles.
-//
-//			Schemes: https
-//
-//			Security:
-//	  		oauth
-//
-//			Responses:
-//				200: userBodyResponse
-func listRoles(w http.ResponseWriter, r *http.Request) {
-	roles, err := logic.ListRoles()
-	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")
-}
-
-// swagger:route GET /api/v1/user/role user getRole
-//
-// Get user role permission templates.
-//
-//			Schemes: https
-//
-//			Security:
-//	  		oauth
-//
-//			Responses:
-//				200: userBodyResponse
-func getRole(w http.ResponseWriter, r *http.Request) {
-	rid, _ := url.QueryUnescape(r.URL.Query().Get("role_id"))
-	if rid == "" {
-		logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("role is required"), "badrequest"))
-		return
-	}
-	role, err := logic.GetRole(models.UserRole(rid))
-	if err != nil {
-		logic.ReturnErrorResponse(w, r, models.ErrorResponse{
-			Code:    http.StatusInternalServerError,
-			Message: err.Error(),
-		})
-		return
-	}
-	logic.ReturnSuccessResponseWithJson(w, r, role, "successfully fetched user role permission templates")
-}
-
-// swagger:route POST /api/v1/user/role user createRole
-//
-// Create user role permission template.
-//
-//			Schemes: https
-//
-//			Security:
-//	  		oauth
-//
-//			Responses:
-//				200: userBodyResponse
-func createRole(w http.ResponseWriter, r *http.Request) {
-	var userRole models.UserRolePermissionTemplate
-	err := json.NewDecoder(r.Body).Decode(&userRole)
-	if err != nil {
-		slog.Error("error decoding request body", "error",
-			err.Error())
-		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
-		return
-	}
-	err = logic.ValidateCreateRoleReq(userRole)
-	if err != nil {
-		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
-		return
-	}
-	userRole.Default = false
-	userRole.GlobalLevelAccess = make(map[models.RsrcType]map[models.RsrcID]models.RsrcPermissionScope)
-	err = logic.CreateRole(userRole)
-	if err != nil {
-		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
-		return
-	}
-	logic.ReturnSuccessResponseWithJson(w, r, userRole, "created user role")
-}
-
-// swagger:route PUT /api/v1/user/role user updateRole
-//
-// Update user role permission template.
-//
-//			Schemes: https
-//
-//			Security:
-//	  		oauth
-//
-//			Responses:
-//				200: userBodyResponse
-func updateRole(w http.ResponseWriter, r *http.Request) {
-	var userRole models.UserRolePermissionTemplate
-	err := json.NewDecoder(r.Body).Decode(&userRole)
-	if err != nil {
-		slog.Error("error decoding request body", "error",
-			err.Error())
-		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
-		return
-	}
-	err = logic.ValidateUpdateRoleReq(userRole)
-	if err != nil {
-		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
-		return
-	}
-	userRole.GlobalLevelAccess = make(map[models.RsrcType]map[models.RsrcID]models.RsrcPermissionScope)
-	err = logic.UpdateRole(userRole)
-	if err != nil {
-		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
-		return
-	}
-	logic.ReturnSuccessResponseWithJson(w, r, userRole, "updated user role")
-}
-
-// swagger:route DELETE /api/v1/user/role user deleteRole
-//
-// Delete user role permission template.
-//
-//			Schemes: https
-//
-//			Security:
-//	  		oauth
-//
-//			Responses:
-//				200: userBodyResponse
-func deleteRole(w http.ResponseWriter, r *http.Request) {
-
-	rid, _ := url.QueryUnescape(r.URL.Query().Get("role_id"))
-	if rid == "" {
-		logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("role is required"), "badrequest"))
-		return
-	}
-	err := logic.DeleteRole(models.UserRole(rid))
-	if err != nil {
-		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
-		return
-	}
-	logic.ReturnSuccessResponseWithJson(w, r, nil, "created user role")
 }
 
 // swagger:route POST /api/users/adm/authenticate authenticate authenticateUser
@@ -537,7 +207,7 @@ func getUser(w http.ResponseWriter, r *http.Request) {
 	json.NewEncoder(w).Encode(user)
 }
 
-// swagger:route GET /api/v1/users/{username} user getUser
+// swagger:route GET /api/v1/users user getUserV1
 //
 // Get an individual user with role info.
 //
@@ -547,13 +217,15 @@ func getUser(w http.ResponseWriter, r *http.Request) {
 //	  		oauth
 //
 //			Responses:
-//				200: userBodyResponse
+//				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())
@@ -721,18 +393,7 @@ func createUser(w http.ResponseWriter, r *http.Request) {
 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
 		return
 	}
-	uniqueGroupsPlatformRole := make(map[models.UserRole]struct{})
-	for groupID := range user.UserGroups {
-		userG, err := logic.GetUserGroup(groupID)
-		if err != nil {
-			logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
-			return
-		}
-		uniqueGroupsPlatformRole[userG.PlatformRole] = struct{}{}
-		user.PlatformRoleID = userG.PlatformRole
-	}
-	if len(uniqueGroupsPlatformRole) > 1 {
-		err = errors.New("only groups with same platform role can be assigned to an user")
+	if err = logic.IsGroupsValid(user.UserGroups); err != nil {
 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
 		return
 	}
@@ -988,367 +649,3 @@ func socketHandler(w http.ResponseWriter, r *http.Request) {
 	// Start handling the session
 	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()
-
-	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 {
-			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.
-	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")
-}
-
-// swagger:route POST /api/v1/users/invite-signup user userInviteSignUp
-//
-// user signup via invite.
-//
-//	Schemes: https
-//
-//	Responses:
-//		200: ReturnSuccessResponse
-func userInviteSignUp(w http.ResponseWriter, r *http.Request) {
-	email, _ := url.QueryUnescape(r.URL.Query().Get("email"))
-	code, _ := url.QueryUnescape(r.URL.Query().Get("invite_code"))
-	in, err := logic.GetUserInvite(email)
-	if err != nil {
-		logger.Log(0, "failed to fetch users: ", err.Error())
-		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
-		return
-	}
-	if code != in.InviteCode {
-		logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("invalid invite code"), "badrequest"))
-		return
-	}
-	// check if user already exists
-	_, err = logic.GetUser(email)
-	if err == nil {
-		logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("user already exists"), "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 user.UserName != email {
-		logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("username not matching with invite"), "badrequest"))
-		return
-	}
-	if user.Password == "" {
-		logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("password cannot be empty"), "badrequest"))
-		return
-	}
-
-	for _, inviteGroupID := range in.Groups {
-		userG, err := logic.GetUserGroup(inviteGroupID)
-		if err != nil {
-			logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("error fetching group id "+inviteGroupID.String()), "badrequest"))
-			return
-		}
-		user.PlatformRoleID = userG.PlatformRole
-		user.UserGroups = make(map[models.UserGroupID]struct{})
-		user.UserGroups[inviteGroupID] = struct{}{}
-	}
-	if user.PlatformRoleID == "" {
-		user.PlatformRoleID = models.ServiceUser
-	}
-	user.NetworkRoles = make(map[models.NetworkID]map[models.UserRole]struct{})
-	err = logic.CreateUser(&user)
-	if err != nil {
-		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
-		return
-	}
-	// delete invite
-	logic.DeleteUserInvite(email)
-	logic.DeletePendingUser(email)
-	w.Header().Set("Access-Control-Allow-Origin", "*")
-	logic.ReturnSuccessResponse(w, r, "created user successfully "+email)
-}
-
-// swagger:route GET /api/v1/users/invite user userInviteVerify
-//
-// verfies user invite.
-//
-//	Schemes: https
-//
-//	Responses:
-//		200: ReturnSuccessResponse
-func userInviteVerify(w http.ResponseWriter, r *http.Request) {
-	email, _ := url.QueryUnescape(r.URL.Query().Get("email"))
-	code, _ := url.QueryUnescape(r.URL.Query().Get("invite_code"))
-	err := logic.ValidateAndApproveUserInvite(email, code)
-	if err != nil {
-		logger.Log(0, "failed to fetch users: ", err.Error())
-		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
-		return
-	}
-	logic.ReturnSuccessResponse(w, r, "invite is valid")
-}
-
-// swagger:route POST /api/v1/users/invite user inviteUsers
-//
-// invite users.
-//
-//			Schemes: https
-//
-//			Security:
-//	  		oauth
-//
-//			Responses:
-//				200: userBodyResponse
-func inviteUsers(w http.ResponseWriter, r *http.Request) {
-	var inviteReq models.InviteUsersReq
-	err := json.NewDecoder(r.Body).Decode(&inviteReq)
-	if err != nil {
-		slog.Error("error decoding request body", "error",
-			err.Error())
-		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
-		return
-	}
-	//validate Req
-	uniqueGroupsPlatformRole := make(map[models.UserRole]struct{})
-	for _, groupID := range inviteReq.Groups {
-		userG, err := logic.GetUserGroup(groupID)
-		if err != nil {
-			logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
-			return
-		}
-		uniqueGroupsPlatformRole[userG.PlatformRole] = struct{}{}
-	}
-	if len(uniqueGroupsPlatformRole) > 1 {
-		err = errors.New("only groups with same platform role can be assigned to an user")
-		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
-		return
-	}
-
-	for _, inviteeEmail := range inviteReq.UserEmails {
-		// check if user with email exists, then ignore
-		_, err := logic.GetUser(inviteeEmail)
-		if err == nil {
-			// user exists already, so ignore
-			continue
-		}
-		invite := models.UserInvite{
-			Email:      inviteeEmail,
-			Groups:     inviteReq.Groups,
-			InviteCode: logic.RandomString(8),
-		}
-		u, err := url.Parse(fmt.Sprintf("%s/invite?email=%s&invite_code=%s",
-			servercfg.GetFrontendURL(), url.QueryEscape(invite.Email), url.QueryEscape(invite.InviteCode)))
-		if err != nil {
-			slog.Error("failed to parse to invite url", "error", err)
-			return
-		}
-		invite.InviteURL = u.String()
-		err = logic.InsertUserInvite(invite)
-		if err != nil {
-			slog.Error("failed to insert invite for user", "email", invite.Email, "error", err)
-		}
-		// notify user with magic link
-		go func(invite models.UserInvite) {
-			// Set E-Mail body. You can set plain text or html with text/html
-
-			e := email.UserInvitedMail{
-				BodyBuilder: &email.EmailBodyBuilderWithH1HeadlineAndImage{},
-				InviteURL:   invite.InviteURL,
-			}
-			n := email.Notification{
-				RecipientMail: invite.Email,
-			}
-			err = email.GetClient().SendEmail(context.Background(), n, e)
-			if err != nil {
-				slog.Error("failed to send email invite", "user", invite.Email, "error", err)
-			}
-		}(invite)
-	}
-
-}
-
-// swagger:route GET /api/v1/users/invites user listUserInvites
-//
-// lists all pending invited users.
-//
-//			Schemes: https
-//
-//			Security:
-//	  		oauth
-//
-//			Responses:
-//				200: ReturnSuccessResponseWithJson
-func listUserInvites(w http.ResponseWriter, r *http.Request) {
-	usersInvites, err := logic.ListUserInvites()
-	if err != nil {
-		logger.Log(0, "failed to fetch users: ", err.Error())
-		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
-		return
-	}
-	logic.ReturnSuccessResponseWithJson(w, r, usersInvites, "fetched pending user invites")
-}
-
-// swagger:route DELETE /api/v1/users/invite user deleteUserInvite
-//
-// delete pending invite.
-//
-//			Schemes: https
-//
-//			Security:
-//	  		oauth
-//
-//			Responses:
-//				200: ReturnSuccessResponse
-func deleteUserInvite(w http.ResponseWriter, r *http.Request) {
-	email, _ := url.QueryUnescape(r.URL.Query().Get("invitee_email"))
-	err := logic.DeleteUserInvite(email)
-	if err != nil {
-		logger.Log(0, "failed to delete user invite: ", email, err.Error())
-		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
-		return
-	}
-	logic.ReturnSuccessResponse(w, r, "deleted user invite")
-}
-
-// swagger:route DELETE /api/v1/users/invites user deleteAllUserInvites
-//
-// deletes all pending invites.
-//
-//			Schemes: https
-//
-//			Security:
-//	  		oauth
-//
-//			Responses:
-//				200: ReturnSuccessResponse
-func deleteAllUserInvites(w http.ResponseWriter, r *http.Request) {
-	err := database.DeleteAllRecords(database.USER_INVITES_TABLE_NAME)
-	if err != nil {
-		logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("failed to delete all pending user invites "+err.Error()), "internal"))
-		return
-	}
-	logic.ReturnSuccessResponse(w, r, "cleared all pending user invites")
-}

+ 0 - 68
logic/nodes.go

@@ -674,71 +674,3 @@ func GetAllFailOvers() ([]models.Node, error) {
 	}
 	return igs, nil
 }
-
-func GetFilteredNodesByUserAccess(user models.User, nodes []models.Node) (filteredNodes []models.Node) {
-
-	nodesMap := make(map[string]struct{})
-	allNetworkRoles := []models.UserRole{}
-	if len(user.NetworkRoles) > 0 {
-		for _, netRoles := range user.NetworkRoles {
-			for netRoleI := range netRoles {
-				allNetworkRoles = append(allNetworkRoles, netRoleI)
-			}
-		}
-	}
-	if len(user.UserGroups) > 0 {
-		for userGID := range user.UserGroups {
-			userG, err := GetUserGroup(userGID)
-			if err == nil {
-				if len(userG.NetworkRoles) > 0 {
-					for _, netRoles := range userG.NetworkRoles {
-						for netRoleI := range netRoles {
-							allNetworkRoles = append(allNetworkRoles, netRoleI)
-						}
-					}
-				}
-			}
-		}
-	}
-	for _, networkRoleID := range allNetworkRoles {
-		userPermTemplate, err := GetRole(networkRoleID)
-		if err != nil {
-			continue
-		}
-		networkNodes := GetNetworkNodesMemory(nodes, userPermTemplate.NetworkID)
-		if userPermTemplate.FullAccess {
-			for _, node := range networkNodes {
-				nodesMap[node.ID.String()] = struct{}{}
-			}
-			filteredNodes = append(filteredNodes, networkNodes...)
-			continue
-		}
-		if rsrcPerms, ok := userPermTemplate.NetworkLevelAccess[models.RemoteAccessGwRsrc]; ok {
-			if _, ok := rsrcPerms[models.AllRemoteAccessGwRsrcID]; ok {
-				for _, node := range networkNodes {
-					if _, ok := nodesMap[node.ID.String()]; ok {
-						continue
-					}
-					if node.IsIngressGateway {
-						nodesMap[node.ID.String()] = struct{}{}
-						filteredNodes = append(filteredNodes, node)
-					}
-				}
-			} else {
-				for gwID, scope := range rsrcPerms {
-					if _, ok := nodesMap[gwID.String()]; ok {
-						continue
-					}
-					if scope.Read {
-						gwNode, err := GetNodeByID(gwID.String())
-						if err == nil && gwNode.IsIngressGateway {
-							filteredNodes = append(filteredNodes, gwNode)
-						}
-					}
-				}
-			}
-		}
-
-	}
-	return
-}

+ 4 - 187
logic/security.go

@@ -1,8 +1,6 @@
 package logic
 
 import (
-	"errors"
-	"fmt"
 	"net/http"
 	"strings"
 
@@ -20,189 +18,8 @@ const (
 	Unauthorized_Err = models.Error(Unauthorized_Msg)
 )
 
-func GetSubjectsFromURL(URL string) (rsrcType models.RsrcType, rsrcID models.RsrcID) {
-	urlSplit := strings.Split(URL, "/")
-	rsrcType = models.RsrcType(urlSplit[1])
-	if len(urlSplit) > 1 {
-		rsrcID = models.RsrcID(urlSplit[2])
-	}
-	return
-}
-
-func networkPermissionsCheck(username string, r *http.Request) error {
-	// at this point global checks should be completed
-	user, err := GetUser(username)
-	if err != nil {
-		return err
-	}
-	logger.Log(0, "NET MIDDL----> 1")
-	userRole, err := GetRole(user.PlatformRoleID)
-	if err != nil {
-		return errors.New("access denied")
-	}
-	if userRole.FullAccess {
-		return nil
-	}
-	logger.Log(0, "NET MIDDL----> 2")
-	// get info from header to determine the target rsrc
-	targetRsrc := r.Header.Get("TARGET_RSRC")
-	targetRsrcID := r.Header.Get("TARGET_RSRC_ID")
-	netID := r.Header.Get("NET_ID")
-	if targetRsrc == "" {
-		return errors.New("target rsrc is missing")
-	}
-	if netID == "" {
-		return errors.New("network id is missing")
-	}
-	if r.Method == "" {
-		r.Method = http.MethodGet
-	}
-	if targetRsrc == models.MetricRsrc.String() {
-		return nil
-	}
-
-	// check if user has scope for target resource
-	// TODO - differentitate between global scope and network scope apis
-	netRoles := user.NetworkRoles[models.NetworkID(netID)]
-	for netRoleID := range netRoles {
-		err = checkNetworkAccessPermissions(netRoleID, username, r.Method, targetRsrc, targetRsrcID)
-		if err == nil {
-			return nil
-		}
-	}
-	for groupID := range user.UserGroups {
-		userG, err := GetUserGroup(groupID)
-		if err == nil {
-			netRoles := userG.NetworkRoles[models.NetworkID(netID)]
-			for netRoleID := range netRoles {
-				err = checkNetworkAccessPermissions(netRoleID, username, r.Method, targetRsrc, targetRsrcID)
-				if err == nil {
-					return nil
-				}
-			}
-		}
-	}
-
-	return errors.New("access denied")
-}
-
-func checkNetworkAccessPermissions(netRoleID models.UserRole, username, reqScope, targetRsrc, targetRsrcID string) error {
-	networkPermissionScope, err := GetRole(netRoleID)
-	if err != nil {
-		return err
-	}
-	logger.Log(0, "NET MIDDL----> 3", string(netRoleID))
-	if networkPermissionScope.FullAccess {
-		return nil
-	}
-	rsrcPermissionScope, ok := networkPermissionScope.NetworkLevelAccess[models.RsrcType(targetRsrc)]
-	if targetRsrc == models.HostRsrc.String() && !ok {
-		rsrcPermissionScope, ok = networkPermissionScope.NetworkLevelAccess[models.RemoteAccessGwRsrc]
-	}
-	if !ok {
-		return errors.New("access denied")
-	}
-	logger.Log(0, "NET MIDDL----> 4", string(netRoleID))
-	if allRsrcsTypePermissionScope, ok := rsrcPermissionScope[models.RsrcID(fmt.Sprintf("all_%s", targetRsrc))]; ok {
-		// handle extclient apis here
-		if models.RsrcType(targetRsrc) == models.ExtClientsRsrc && allRsrcsTypePermissionScope.SelfOnly && targetRsrcID != "" {
-			extclient, err := GetExtClient(targetRsrcID, networkPermissionScope.NetworkID)
-			if err != nil {
-				return err
-			}
-			if !IsUserAllowedAccessToExtClient(username, extclient) {
-				return errors.New("access denied")
-			}
-		}
-		err = checkPermissionScopeWithReqMethod(allRsrcsTypePermissionScope, reqScope)
-		if err == nil {
-			return nil
-		}
-
-	}
-	if targetRsrc == models.HostRsrc.String() {
-		if allRsrcsTypePermissionScope, ok := rsrcPermissionScope[models.RsrcID(fmt.Sprintf("all_%s", models.RemoteAccessGwRsrc))]; ok {
-			err = checkPermissionScopeWithReqMethod(allRsrcsTypePermissionScope, reqScope)
-			if err == nil {
-				return nil
-			}
-		}
-	}
-	logger.Log(0, "NET MIDDL----> 5", string(netRoleID))
-	if targetRsrcID == "" {
-		return errors.New("target rsrc id is empty")
-	}
-	if scope, ok := rsrcPermissionScope[models.RsrcID(targetRsrcID)]; ok {
-		err = checkPermissionScopeWithReqMethod(scope, reqScope)
-		if err == nil {
-			return nil
-		}
-	}
-	logger.Log(0, "NET MIDDL----> 6", string(netRoleID))
-	return errors.New("access denied")
-}
-
-func globalPermissionsCheck(username string, r *http.Request) error {
-	user, err := GetUser(username)
-	if err != nil {
-		return err
-	}
-	userRole, err := GetRole(user.PlatformRoleID)
-	if err != nil {
-		return errors.New("access denied")
-	}
-	if userRole.FullAccess {
-		return nil
-	}
-	targetRsrc := r.Header.Get("TARGET_RSRC")
-	targetRsrcID := r.Header.Get("TARGET_RSRC_ID")
-	if targetRsrc == "" {
-		return errors.New("target rsrc is missing")
-	}
-	if r.Method == "" {
-		r.Method = http.MethodGet
-	}
-	if targetRsrc == models.MetricRsrc.String() {
-		return nil
-	}
-	if (targetRsrc == models.HostRsrc.String() || targetRsrc == models.NetworkRsrc.String()) && r.Method == http.MethodGet && targetRsrcID == "" {
-		return nil
-	}
-	if targetRsrc == models.UserRsrc.String() && username == targetRsrcID && (r.Method != http.MethodDelete) {
-		return nil
-	}
-	rsrcPermissionScope, ok := userRole.GlobalLevelAccess[models.RsrcType(targetRsrc)]
-	if !ok {
-		return fmt.Errorf("access denied to %s rsrc", targetRsrc)
-	}
-	if allRsrcsTypePermissionScope, ok := rsrcPermissionScope[models.RsrcID(fmt.Sprintf("all_%s", targetRsrc))]; ok {
-		return checkPermissionScopeWithReqMethod(allRsrcsTypePermissionScope, r.Method)
-
-	}
-	if targetRsrcID == "" {
-		return errors.New("target rsrc id is missing")
-	}
-	if scope, ok := rsrcPermissionScope[models.RsrcID(targetRsrcID)]; ok {
-		return checkPermissionScopeWithReqMethod(scope, r.Method)
-	}
-	return errors.New("access denied")
-}
-
-func checkPermissionScopeWithReqMethod(scope models.RsrcPermissionScope, reqmethod string) error {
-	if reqmethod == http.MethodGet && scope.Read {
-		return nil
-	}
-	if (reqmethod == http.MethodPatch || reqmethod == http.MethodPut) && scope.Update {
-		return nil
-	}
-	if reqmethod == http.MethodDelete && scope.Delete {
-		return nil
-	}
-	if reqmethod == http.MethodPost && scope.Create {
-		return nil
-	}
-	return errors.New("operation not permitted")
-}
+var NetworkPermissionsCheck = func(username string, r *http.Request) error { return nil }
+var GlobalPermissionsCheck = func(username string, r *http.Request) error { return nil }
 
 // SecurityCheck - Check if user has appropriate permissions
 func SecurityCheck(reqAdmin bool, next http.Handler) http.HandlerFunc {
@@ -223,9 +40,9 @@ func SecurityCheck(reqAdmin bool, next http.Handler) http.HandlerFunc {
 			r.Header.Set("ismaster", "yes")
 		} else {
 			if isGlobalAccesss {
-				err = globalPermissionsCheck(username, r)
+				err = GlobalPermissionsCheck(username, r)
 			} else {
-				err = networkPermissionsCheck(username, r)
+				err = NetworkPermissionsCheck(username, r)
 			}
 		}
 		w.Header().Set("TARGET_RSRC", r.Header.Get("TARGET_RSRC"))

+ 12 - 469
logic/user_mgmt.go

@@ -3,192 +3,19 @@ package logic
 import (
 	"encoding/json"
 	"errors"
-	"fmt"
 
 	"github.com/gravitl/netmaker/database"
-	"github.com/gravitl/netmaker/logger"
 	"github.com/gravitl/netmaker/models"
 )
 
-// Pre-Define Permission Templates for default Roles
-var SuperAdminPermissionTemplate = models.UserRolePermissionTemplate{
-	ID:         models.SuperAdminRole,
-	Default:    true,
-	FullAccess: true,
-}
-
-var AdminPermissionTemplate = models.UserRolePermissionTemplate{
-	ID:         models.AdminRole,
-	Default:    true,
-	FullAccess: true,
-}
-
-var ServiceUserPermissionTemplate = models.UserRolePermissionTemplate{
-	ID:                  models.ServiceUser,
-	Default:             true,
-	FullAccess:          false,
-	DenyDashboardAccess: true,
-}
-
-var PlatformUserUserPermissionTemplate = models.UserRolePermissionTemplate{
-	ID:         models.PlatformUser,
-	Default:    true,
-	FullAccess: false,
-}
-
-func UserRolesInit() {
-	d, _ := json.Marshal(SuperAdminPermissionTemplate)
-	database.Insert(SuperAdminPermissionTemplate.ID.String(), string(d), database.USER_PERMISSIONS_TABLE_NAME)
-	d, _ = json.Marshal(AdminPermissionTemplate)
-	database.Insert(AdminPermissionTemplate.ID.String(), string(d), database.USER_PERMISSIONS_TABLE_NAME)
-	d, _ = json.Marshal(ServiceUserPermissionTemplate)
-	database.Insert(ServiceUserPermissionTemplate.ID.String(), string(d), database.USER_PERMISSIONS_TABLE_NAME)
-	d, _ = json.Marshal(PlatformUserUserPermissionTemplate)
-	database.Insert(PlatformUserUserPermissionTemplate.ID.String(), string(d), database.USER_PERMISSIONS_TABLE_NAME)
-
-}
-
-func CreateDefaultNetworkRoles(netID string) {
-	var NetworkAdminPermissionTemplate = models.UserRolePermissionTemplate{
-		ID:                 models.UserRole(fmt.Sprintf("%s_%s", netID, models.NetworkAdmin)),
-		Default:            false,
-		NetworkID:          netID,
-		FullAccess:         true,
-		NetworkLevelAccess: make(map[models.RsrcType]map[models.RsrcID]models.RsrcPermissionScope),
-	}
-
-	var NetworkUserPermissionTemplate = models.UserRolePermissionTemplate{
-		ID:                  models.UserRole(fmt.Sprintf("%s_%s", netID, models.NetworkUser)),
-		Default:             false,
-		FullAccess:          false,
-		NetworkID:           netID,
-		DenyDashboardAccess: false,
-		NetworkLevelAccess: map[models.RsrcType]map[models.RsrcID]models.RsrcPermissionScope{
-			models.RemoteAccessGwRsrc: {
-				models.AllRemoteAccessGwRsrcID: models.RsrcPermissionScope{
-					Read:      true,
-					VPNaccess: true,
-				},
-			},
-			models.ExtClientsRsrc: {
-				models.AllExtClientsRsrcID: models.RsrcPermissionScope{
-					Read:     true,
-					Create:   true,
-					Update:   true,
-					Delete:   true,
-					SelfOnly: true,
-				},
-			},
-		},
-	}
-	d, _ := json.Marshal(NetworkAdminPermissionTemplate)
-	database.Insert(NetworkAdminPermissionTemplate.ID.String(), string(d), database.USER_PERMISSIONS_TABLE_NAME)
-	d, _ = json.Marshal(NetworkUserPermissionTemplate)
-	database.Insert(NetworkUserPermissionTemplate.ID.String(), string(d), database.USER_PERMISSIONS_TABLE_NAME)
-}
-
-func DeleteNetworkRoles(netID string) {
-	users, err := GetUsersDB()
-	if err != nil {
-		return
-	}
-	for _, user := range users {
-		if _, ok := user.NetworkRoles[models.NetworkID(netID)]; ok {
-			delete(user.NetworkRoles, models.NetworkID(netID))
-			UpsertUser(user)
-		}
-
-	}
-	userGs, _ := ListUserGroups()
-	for _, userGI := range userGs {
-		if _, ok := userGI.NetworkRoles[models.NetworkID(netID)]; ok {
-			delete(userGI.NetworkRoles, models.NetworkID(netID))
-			UpdateUserGroup(userGI)
-		}
-	}
-
-	roles, _ := ListRoles()
-	for _, role := range roles {
-		if role.NetworkID == netID {
-			DeleteRole(role.ID)
-		}
-	}
-}
-
-// ListRoles - lists user roles permission templates
-func ListRoles() ([]models.UserRolePermissionTemplate, error) {
-	data, err := database.FetchRecords(database.USER_PERMISSIONS_TABLE_NAME)
-	if err != nil && !database.IsEmptyRecord(err) {
-		return []models.UserRolePermissionTemplate{}, err
-	}
-	userRoles := []models.UserRolePermissionTemplate{}
-	for _, dataI := range data {
-		userRole := models.UserRolePermissionTemplate{}
-		err := json.Unmarshal([]byte(dataI), &userRole)
-		if err != nil {
-			continue
-		}
-		userRoles = append(userRoles, userRole)
-	}
-	return userRoles, nil
-}
-
-func ValidateCreateRoleReq(userRole models.UserRolePermissionTemplate) error {
-	// check if role exists with this id
-	_, err := GetRole(userRole.ID)
-	if err == nil {
-		return fmt.Errorf("role with id `%s` exists already", userRole.ID.String())
-	}
-	if len(userRole.NetworkLevelAccess) > 0 {
-		for rsrcType := range userRole.NetworkLevelAccess {
-			if _, ok := models.RsrcTypeMap[rsrcType]; !ok {
-				return errors.New("invalid rsrc type " + rsrcType.String())
-			}
-		}
-	}
-	if userRole.NetworkID == "" {
-		return errors.New("only network roles are allowed to be created")
-	}
-	return nil
+var GetFilteredNodesByUserAccess = func(user models.User, nodes []models.Node) (filteredNodes []models.Node) {
+	return
 }
 
-func ValidateUpdateRoleReq(userRole models.UserRolePermissionTemplate) error {
-	roleInDB, err := GetRole(userRole.ID)
-	if err != nil {
-		return err
-	}
-	if roleInDB.NetworkID != userRole.NetworkID {
-		return errors.New("network id mismatch")
-	}
-	if roleInDB.Default {
-		return errors.New("cannot update default role")
-	}
-	if len(userRole.NetworkLevelAccess) > 0 {
-		for rsrcType := range userRole.NetworkLevelAccess {
-			if _, ok := models.RsrcTypeMap[rsrcType]; !ok {
-				return errors.New("invalid rsrc type " + rsrcType.String())
-			}
-		}
-	}
+var CreateRole = func(r models.UserRolePermissionTemplate) error {
 	return nil
 }
-
-// CreateRole - inserts new role into DB
-func CreateRole(r models.UserRolePermissionTemplate) error {
-	// check if role already exists
-	if r.ID.String() == "" {
-		return errors.New("role id cannot be empty")
-	}
-	_, err := database.FetchRecord(database.USER_PERMISSIONS_TABLE_NAME, r.ID.String())
-	if err == nil {
-		return errors.New("role already exists")
-	}
-	d, err := json.Marshal(r)
-	if err != nil {
-		return err
-	}
-	return database.Insert(r.ID.String(), string(d), database.USER_PERMISSIONS_TABLE_NAME)
-}
+var DeleteNetworkRoles = func(netID string) {}
 
 // GetRole - fetches role template by id
 func GetRole(roleID models.UserRole) (models.UserRolePermissionTemplate, error) {
@@ -205,302 +32,18 @@ func GetRole(roleID models.UserRole) (models.UserRolePermissionTemplate, error)
 	return ur, nil
 }
 
-// UpdateRole - updates role template
-func UpdateRole(r models.UserRolePermissionTemplate) error {
-	if r.ID.String() == "" {
-		return errors.New("role id cannot be empty")
-	}
-	_, err := database.FetchRecord(database.USER_PERMISSIONS_TABLE_NAME, r.ID.String())
-	if err != nil {
-		return err
-	}
-	d, err := json.Marshal(r)
-	if err != nil {
-		return err
-	}
-	return database.Insert(r.ID.String(), string(d), database.USER_PERMISSIONS_TABLE_NAME)
-}
-
-// DeleteRole - deletes user role
-func DeleteRole(rid models.UserRole) error {
-	if rid.String() == "" {
-		return errors.New("role id cannot be empty")
-	}
-	users, err := GetUsersDB()
-	if err != nil {
-		return err
-	}
-	role, err := GetRole(rid)
-	if err != nil {
-		return err
-	}
-	if role.Default {
-		return errors.New("cannot delete default role")
-	}
-	for _, user := range users {
-		for userG := range user.UserGroups {
-			ug, err := GetUserGroup(userG)
-			if err == nil {
-				if role.NetworkID != "" {
-					for _, networkRoles := range ug.NetworkRoles {
-						if _, ok := networkRoles[rid]; ok {
-							err = errors.New("role cannot be deleted as active user groups are using this role")
-							return err
-						}
-					}
-				}
-
-			}
-		}
-
-		if user.PlatformRoleID == rid {
-			err = errors.New("active roles cannot be deleted.switch existing users to a new role before deleting")
+func IsGroupsValid(groups map[models.UserGroupID]struct{}) error {
+	uniqueGroupsPlatformRole := make(map[models.UserRole]struct{})
+	for groupID := range groups {
+		userG, err := logic.GetUserGroup(groupID)
+		if err != nil {
 			return err
 		}
-		for _, networkRoles := range user.NetworkRoles {
-			if _, ok := networkRoles[rid]; ok {
-				err = errors.New("active roles cannot be deleted.switch existing users to a new role before deleting")
-				return err
-			}
-
-		}
+		uniqueGroupsPlatformRole[userG.PlatformRole] = struct{}{}
 	}
-	return database.DeleteRecord(database.USER_PERMISSIONS_TABLE_NAME, rid.String())
-}
+	if len(uniqueGroupsPlatformRole) > 1 {
 
-func ValidateCreateGroupReq(g models.UserGroup) error {
-	// check platform role is valid
-	role, err := GetRole(g.PlatformRole)
-	if err != nil {
-		err = fmt.Errorf("invalid platform role")
-		return err
-	}
-	if role.NetworkID != "" {
-		return errors.New("network role cannot be used as platform role")
-	}
-	// check if network roles are valid
-	for _, roleMap := range g.NetworkRoles {
-		for roleID := range roleMap {
-			role, err := GetRole(roleID)
-			if err != nil {
-				return fmt.Errorf("invalid network role %s", roleID)
-			}
-			if role.NetworkID == "" {
-				return errors.New("platform role cannot be used as network role")
-			}
-		}
+		return errors.New("only groups with same platform role can be assigned to an user")
 	}
 	return nil
 }
-func ValidateUpdateGroupReq(g models.UserGroup) error {
-	// check platform role is valid
-	role, err := GetRole(g.PlatformRole)
-	if err != nil {
-		err = fmt.Errorf("invalid platform role")
-		return err
-	}
-	if role.NetworkID != "" {
-		return errors.New("network role cannot be used as platform role")
-	}
-	for networkID := range g.NetworkRoles {
-		userRolesMap := g.NetworkRoles[networkID]
-		for roleID := range userRolesMap {
-			netRole, err := GetRole(roleID)
-			if err != nil {
-				err = fmt.Errorf("invalid network role")
-				return err
-			}
-			if netRole.NetworkID == "" {
-				return errors.New("platform role cannot be used as network role")
-			}
-		}
-	}
-	return nil
-}
-
-// CreateUserGroup - creates new user group
-func CreateUserGroup(g models.UserGroup) error {
-	// check if role already exists
-	if g.ID == "" {
-		return errors.New("group id cannot be empty")
-	}
-	_, err := database.FetchRecord(database.USER_GROUPS_TABLE_NAME, g.ID.String())
-	if err == nil {
-		return errors.New("group already exists")
-	}
-	d, err := json.Marshal(g)
-	if err != nil {
-		return err
-	}
-	return database.Insert(g.ID.String(), string(d), database.USER_GROUPS_TABLE_NAME)
-}
-
-// GetUserGroup - fetches user group
-func GetUserGroup(gid models.UserGroupID) (models.UserGroup, error) {
-	d, err := database.FetchRecord(database.USER_GROUPS_TABLE_NAME, gid.String())
-	if err != nil {
-		return models.UserGroup{}, err
-	}
-	var ug models.UserGroup
-	err = json.Unmarshal([]byte(d), &ug)
-	if err != nil {
-		return ug, err
-	}
-	return ug, nil
-}
-
-// ListUserGroups - lists user groups
-func ListUserGroups() ([]models.UserGroup, error) {
-	data, err := database.FetchRecords(database.USER_GROUPS_TABLE_NAME)
-	if err != nil && !database.IsEmptyRecord(err) {
-		return []models.UserGroup{}, err
-	}
-	userGroups := []models.UserGroup{}
-	for _, dataI := range data {
-		userGroup := models.UserGroup{}
-		err := json.Unmarshal([]byte(dataI), &userGroup)
-		if err != nil {
-			continue
-		}
-		userGroups = append(userGroups, userGroup)
-	}
-	return userGroups, nil
-}
-
-// UpdateUserGroup - updates new user group
-func UpdateUserGroup(g models.UserGroup) error {
-	// check if group exists
-	if g.ID == "" {
-		return errors.New("group id cannot be empty")
-	}
-	_, err := database.FetchRecord(database.USER_GROUPS_TABLE_NAME, g.ID.String())
-	if err != nil {
-		return err
-	}
-	d, err := json.Marshal(g)
-	if err != nil {
-		return err
-	}
-	return database.Insert(g.ID.String(), string(d), database.USER_GROUPS_TABLE_NAME)
-}
-
-// DeleteUserGroup - deletes user group
-func DeleteUserGroup(gid models.UserGroupID) error {
-	users, err := GetUsersDB()
-	if err != nil {
-		return err
-	}
-	for _, user := range users {
-		delete(user.UserGroups, gid)
-		UpsertUser(user)
-	}
-	return database.DeleteRecord(database.USER_GROUPS_TABLE_NAME, gid.String())
-}
-
-func HasNetworkRsrcScope(permissionTemplate models.UserRolePermissionTemplate, netid string, rsrcType models.RsrcType, rsrcID models.RsrcID, op string) bool {
-	if permissionTemplate.FullAccess {
-		return true
-	}
-
-	rsrcScope, ok := permissionTemplate.NetworkLevelAccess[rsrcType]
-	if !ok {
-		return false
-	}
-	_, ok = rsrcScope[rsrcID]
-	return ok
-}
-func GetUserRAGNodes(user models.User) (gws map[string]models.Node) {
-	logger.Log(0, "------------> 7. getUserRemoteAccessGwsV1")
-	gws = make(map[string]models.Node)
-	userGwAccessScope := GetUserNetworkRolesWithRemoteVPNAccess(user)
-	logger.Log(0, fmt.Sprintf("User Gw Access Scope: %+v", userGwAccessScope))
-	_, allNetAccess := userGwAccessScope["*"]
-	nodes, err := GetAllNodes()
-	if err != nil {
-		return
-	}
-	logger.Log(0, "------------> 8. getUserRemoteAccessGwsV1")
-	for _, node := range nodes {
-		if node.IsIngressGateway && !node.PendingDelete {
-			if allNetAccess {
-				gws[node.ID.String()] = node
-			} else {
-				gwRsrcMap := userGwAccessScope[models.NetworkID(node.Network)]
-				scope, ok := gwRsrcMap[models.AllRemoteAccessGwRsrcID]
-				if !ok {
-					if scope, ok = gwRsrcMap[models.RsrcID(node.ID.String())]; !ok {
-						continue
-					}
-				}
-				if scope.VPNaccess {
-					gws[node.ID.String()] = node
-				}
-
-			}
-		}
-	}
-	logger.Log(0, "------------> 9. getUserRemoteAccessGwsV1")
-	return
-}
-
-// GetUserNetworkRoles - get user network roles
-func GetUserNetworkRolesWithRemoteVPNAccess(user models.User) (gwAccess map[models.NetworkID]map[models.RsrcID]models.RsrcPermissionScope) {
-	gwAccess = make(map[models.NetworkID]map[models.RsrcID]models.RsrcPermissionScope)
-	logger.Log(0, "------------> 7.1 getUserRemoteAccessGwsV1")
-	platformRole, err := GetRole(user.PlatformRoleID)
-	if err != nil {
-		return
-	}
-	if platformRole.FullAccess {
-		gwAccess[models.NetworkID("*")] = make(map[models.RsrcID]models.RsrcPermissionScope)
-		return
-	}
-	logger.Log(0, "------------> 7.2 getUserRemoteAccessGwsV1")
-	for netID, roleMap := range user.NetworkRoles {
-		for roleID := range roleMap {
-			role, err := GetRole(roleID)
-			if err == nil {
-				if role.FullAccess {
-					gwAccess[netID] = map[models.RsrcID]models.RsrcPermissionScope{
-						models.AllRemoteAccessGwRsrcID: {
-							Create:    true,
-							Read:      true,
-							Update:    true,
-							VPNaccess: true,
-							Delete:    true,
-						},
-						models.AllExtClientsRsrcID: {
-							Create: true,
-							Read:   true,
-							Update: true,
-							Delete: true,
-						},
-					}
-					break
-				}
-				if rsrcsMap, ok := role.NetworkLevelAccess[models.RemoteAccessGwRsrc]; ok {
-					if permissions, ok := rsrcsMap[models.AllRemoteAccessGwRsrcID]; ok && permissions.VPNaccess {
-						if len(gwAccess[netID]) == 0 {
-							gwAccess[netID] = make(map[models.RsrcID]models.RsrcPermissionScope)
-						}
-						gwAccess[netID][models.AllRemoteAccessGwRsrcID] = permissions
-						break
-					} else {
-						for gwID, scope := range rsrcsMap {
-							if scope.VPNaccess {
-								if len(gwAccess[netID]) == 0 {
-									gwAccess[netID] = make(map[models.RsrcID]models.RsrcPermissionScope)
-								}
-								gwAccess[netID][gwID] = scope
-							}
-						}
-					}
-
-				}
-
-			}
-		}
-	}
-	logger.Log(0, "------------> 7.3 getUserRemoteAccessGwsV1")
-	return
-}

+ 2 - 1
pro/auth/azure-ad.go

@@ -14,6 +14,7 @@ import (
 	"github.com/gravitl/netmaker/logger"
 	"github.com/gravitl/netmaker/logic"
 	"github.com/gravitl/netmaker/models"
+	proLogic "github.com/gravitl/netmaker/pro/logic"
 	"github.com/gravitl/netmaker/servercfg"
 	"golang.org/x/oauth2"
 	"golang.org/x/oauth2/microsoft"
@@ -99,7 +100,7 @@ func handleAzureCallback(w http.ResponseWriter, r *http.Request) {
 					Password: newPass,
 				}
 				for _, inviteGroupID := range in.Groups {
-					userG, err := logic.GetUserGroup(inviteGroupID)
+					userG, err := proLogic.GetUserGroup(inviteGroupID)
 					if err != nil {
 						logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("error fetching group id "+inviteGroupID.String()), "badrequest"))
 						return

+ 2 - 1
pro/auth/github.go

@@ -14,6 +14,7 @@ import (
 	"github.com/gravitl/netmaker/logger"
 	"github.com/gravitl/netmaker/logic"
 	"github.com/gravitl/netmaker/models"
+	proLogic "github.com/gravitl/netmaker/pro/logic"
 	"github.com/gravitl/netmaker/servercfg"
 	"golang.org/x/oauth2"
 	"golang.org/x/oauth2/github"
@@ -99,7 +100,7 @@ func handleGithubCallback(w http.ResponseWriter, r *http.Request) {
 				}
 
 				for _, inviteGroupID := range in.Groups {
-					userG, err := logic.GetUserGroup(inviteGroupID)
+					userG, err := proLogic.GetUserGroup(inviteGroupID)
 					if err != nil {
 						logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("error fetching group id "+inviteGroupID.String()), "badrequest"))
 						return

+ 2 - 1
pro/auth/google.go

@@ -15,6 +15,7 @@ import (
 	"github.com/gravitl/netmaker/logger"
 	"github.com/gravitl/netmaker/logic"
 	"github.com/gravitl/netmaker/models"
+	proLogic "github.com/gravitl/netmaker/pro/logic"
 	"github.com/gravitl/netmaker/servercfg"
 	"golang.org/x/oauth2"
 	"golang.org/x/oauth2/google"
@@ -106,7 +107,7 @@ func handleGoogleCallback(w http.ResponseWriter, r *http.Request) {
 				}
 				logger.Log(0, "CALLBACK ----> 4.1")
 				for _, inviteGroupID := range in.Groups {
-					userG, err := logic.GetUserGroup(inviteGroupID)
+					userG, err := proLogic.GetUserGroup(inviteGroupID)
 					if err != nil {
 						logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("error fetching group id "+inviteGroupID.String()), "badrequest"))
 						return

+ 2 - 1
pro/auth/oidc.go

@@ -14,6 +14,7 @@ import (
 	"github.com/gravitl/netmaker/logger"
 	"github.com/gravitl/netmaker/logic"
 	"github.com/gravitl/netmaker/models"
+	proLogic "github.com/gravitl/netmaker/pro/logic"
 	"github.com/gravitl/netmaker/servercfg"
 	"golang.org/x/oauth2"
 )
@@ -111,7 +112,7 @@ func handleOIDCCallback(w http.ResponseWriter, r *http.Request) {
 					Password: newPass,
 				}
 				for _, inviteGroupID := range in.Groups {
-					userG, err := logic.GetUserGroup(inviteGroupID)
+					userG, err := proLogic.GetUserGroup(inviteGroupID)
 					if err != nil {
 						logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("error fetching group id "+inviteGroupID.String()), "badrequest"))
 						return

+ 705 - 6
pro/controllers/users.go

@@ -1,17 +1,23 @@
 package controllers
 
 import (
+	"context"
 	"encoding/json"
 	"errors"
 	"fmt"
 	"net/http"
+	"net/url"
 
 	"github.com/gorilla/mux"
+	"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"
 	"github.com/gravitl/netmaker/mq"
-	"github.com/gravitl/netmaker/pro/auth"
+	proAuth "github.com/gravitl/netmaker/pro/auth"
+	"github.com/gravitl/netmaker/pro/email"
+	proLogic "github.com/gravitl/netmaker/pro/logic"
 	"github.com/gravitl/netmaker/servercfg"
 	"golang.org/x/exp/slog"
 )
@@ -21,10 +27,571 @@ func UserHandlers(r *mux.Router) {
 	r.HandleFunc("/api/users/{username}/remote_access_gw/{remote_access_gateway_id}", logic.SecurityCheck(true, http.HandlerFunc(removeUserFromRemoteAccessGW))).Methods(http.MethodDelete)
 	r.HandleFunc("/api/users/{username}/remote_access_gw", logic.SecurityCheck(false, logic.ContinueIfUserMatch(http.HandlerFunc(getUserRemoteAccessGwsV1)))).Methods(http.MethodGet)
 	r.HandleFunc("/api/users/ingress/{ingress_id}", logic.SecurityCheck(true, http.HandlerFunc(ingressGatewayUsers))).Methods(http.MethodGet)
-	r.HandleFunc("/api/oauth/login", auth.HandleAuthLogin).Methods(http.MethodGet)
-	r.HandleFunc("/api/oauth/callback", auth.HandleAuthCallback).Methods(http.MethodGet)
-	r.HandleFunc("/api/oauth/headless", auth.HandleHeadlessSSO)
-	r.HandleFunc("/api/oauth/register/{regKey}", auth.RegisterHostSSO).Methods(http.MethodGet)
+	r.HandleFunc("/api/oauth/login", proAuth.HandleAuthLogin).Methods(http.MethodGet)
+	r.HandleFunc("/api/oauth/callback", proAuth.HandleAuthCallback).Methods(http.MethodGet)
+	r.HandleFunc("/api/oauth/headless", proAuth.HandleHeadlessSSO)
+	r.HandleFunc("/api/oauth/register/{regKey}", proAuth.RegisterHostSSO).Methods(http.MethodGet)
+
+	// User Role Handlers
+	r.HandleFunc("/api/v1/users/roles", logic.SecurityCheck(true, http.HandlerFunc(listRoles))).Methods(http.MethodGet)
+	r.HandleFunc("/api/v1/users/role", getRole).Methods(http.MethodGet)
+	r.HandleFunc("/api/v1/users/role", logic.SecurityCheck(true, http.HandlerFunc(createRole))).Methods(http.MethodPost)
+	r.HandleFunc("/api/v1/users/role", logic.SecurityCheck(true, http.HandlerFunc(updateRole))).Methods(http.MethodPut)
+	r.HandleFunc("/api/v1/users/role", logic.SecurityCheck(true, http.HandlerFunc(deleteRole))).Methods(http.MethodDelete)
+
+	// User Group Handlers
+	r.HandleFunc("/api/v1/users/groups", logic.SecurityCheck(true, http.HandlerFunc(listUserGroups))).Methods(http.MethodGet)
+	r.HandleFunc("/api/v1/users/group", logic.SecurityCheck(true, http.HandlerFunc(getUserGroup))).Methods(http.MethodGet)
+	r.HandleFunc("/api/v1/users/group", logic.SecurityCheck(true, http.HandlerFunc(createUserGroup))).Methods(http.MethodPost)
+	r.HandleFunc("/api/v1/users/group", logic.SecurityCheck(true, http.HandlerFunc(updateUserGroup))).Methods(http.MethodPut)
+	r.HandleFunc("/api/v1/users/group", logic.SecurityCheck(true, http.HandlerFunc(deleteUserGroup))).Methods(http.MethodDelete)
+
+	// User Invite Handlers
+	r.HandleFunc("/api/v1/users/invite", userInviteVerify).Methods(http.MethodGet)
+	r.HandleFunc("/api/v1/users/invite-signup", userInviteSignUp).Methods(http.MethodPost)
+	r.HandleFunc("/api/v1/users/invite", logic.SecurityCheck(true, http.HandlerFunc(inviteUsers))).Methods(http.MethodPost)
+	r.HandleFunc("/api/v1/users/invites", logic.SecurityCheck(true, http.HandlerFunc(listUserInvites))).Methods(http.MethodGet)
+	r.HandleFunc("/api/v1/users/invite", logic.SecurityCheck(true, http.HandlerFunc(deleteUserInvite))).Methods(http.MethodDelete)
+	r.HandleFunc("/api/v1/users/invites", logic.SecurityCheck(true, http.HandlerFunc(deleteAllUserInvites))).Methods(http.MethodDelete)
+
+	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)
+
+}
+
+// swagger:route POST /api/v1/users/invite-signup user userInviteSignUp
+//
+// user signup via invite.
+//
+//	Schemes: https
+//
+//	Responses:
+//		200: ReturnSuccessResponse
+func userInviteSignUp(w http.ResponseWriter, r *http.Request) {
+	email, _ := url.QueryUnescape(r.URL.Query().Get("email"))
+	code, _ := url.QueryUnescape(r.URL.Query().Get("invite_code"))
+	in, err := logic.GetUserInvite(email)
+	if err != nil {
+		logger.Log(0, "failed to fetch users: ", err.Error())
+		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
+		return
+	}
+	if code != in.InviteCode {
+		logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("invalid invite code"), "badrequest"))
+		return
+	}
+	// check if user already exists
+	_, err = logic.GetUser(email)
+	if err == nil {
+		logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("user already exists"), "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 user.UserName != email {
+		logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("username not matching with invite"), "badrequest"))
+		return
+	}
+	if user.Password == "" {
+		logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("password cannot be empty"), "badrequest"))
+		return
+	}
+
+	for _, inviteGroupID := range in.Groups {
+		userG, err := proLogic.GetUserGroup(inviteGroupID)
+		if err != nil {
+			logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("error fetching group id "+inviteGroupID.String()), "badrequest"))
+			return
+		}
+		user.PlatformRoleID = userG.PlatformRole
+		user.UserGroups = make(map[models.UserGroupID]struct{})
+		user.UserGroups[inviteGroupID] = struct{}{}
+	}
+	if user.PlatformRoleID == "" {
+		user.PlatformRoleID = models.ServiceUser
+	}
+	user.NetworkRoles = make(map[models.NetworkID]map[models.UserRole]struct{})
+	err = logic.CreateUser(&user)
+	if err != nil {
+		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
+		return
+	}
+	// delete invite
+	logic.DeleteUserInvite(email)
+	logic.DeletePendingUser(email)
+	w.Header().Set("Access-Control-Allow-Origin", "*")
+	logic.ReturnSuccessResponse(w, r, "created user successfully "+email)
+}
+
+// swagger:route GET /api/v1/users/invite user userInviteVerify
+//
+// verfies user invite.
+//
+//	Schemes: https
+//
+//	Responses:
+//		200: ReturnSuccessResponse
+func userInviteVerify(w http.ResponseWriter, r *http.Request) {
+	email, _ := url.QueryUnescape(r.URL.Query().Get("email"))
+	code, _ := url.QueryUnescape(r.URL.Query().Get("invite_code"))
+	err := logic.ValidateAndApproveUserInvite(email, code)
+	if err != nil {
+		logger.Log(0, "failed to fetch users: ", err.Error())
+		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
+		return
+	}
+	logic.ReturnSuccessResponse(w, r, "invite is valid")
+}
+
+// swagger:route POST /api/v1/users/invite user inviteUsers
+//
+// invite users.
+//
+//			Schemes: https
+//
+//			Security:
+//	  		oauth
+//
+//			Responses:
+//				200: userBodyResponse
+func inviteUsers(w http.ResponseWriter, r *http.Request) {
+	var inviteReq models.InviteUsersReq
+	err := json.NewDecoder(r.Body).Decode(&inviteReq)
+	if err != nil {
+		slog.Error("error decoding request body", "error",
+			err.Error())
+		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
+		return
+	}
+	//validate Req
+	uniqueGroupsPlatformRole := make(map[models.UserRole]struct{})
+	for _, groupID := range inviteReq.Groups {
+		userG, err := proLogic.GetUserGroup(groupID)
+		if err != nil {
+			logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
+			return
+		}
+		uniqueGroupsPlatformRole[userG.PlatformRole] = struct{}{}
+	}
+	if len(uniqueGroupsPlatformRole) > 1 {
+		err = errors.New("only groups with same platform role can be assigned to an user")
+		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
+		return
+	}
+
+	for _, inviteeEmail := range inviteReq.UserEmails {
+		// check if user with email exists, then ignore
+		_, err := logic.GetUser(inviteeEmail)
+		if err == nil {
+			// user exists already, so ignore
+			continue
+		}
+		invite := models.UserInvite{
+			Email:      inviteeEmail,
+			Groups:     inviteReq.Groups,
+			InviteCode: logic.RandomString(8),
+		}
+		u, err := url.Parse(fmt.Sprintf("%s/invite?email=%s&invite_code=%s",
+			servercfg.GetFrontendURL(), url.QueryEscape(invite.Email), url.QueryEscape(invite.InviteCode)))
+		if err != nil {
+			slog.Error("failed to parse to invite url", "error", err)
+			return
+		}
+		invite.InviteURL = u.String()
+		err = logic.InsertUserInvite(invite)
+		if err != nil {
+			slog.Error("failed to insert invite for user", "email", invite.Email, "error", err)
+		}
+		// notify user with magic link
+		go func(invite models.UserInvite) {
+			// Set E-Mail body. You can set plain text or html with text/html
+
+			e := email.UserInvitedMail{
+				BodyBuilder: &email.EmailBodyBuilderWithH1HeadlineAndImage{},
+				InviteURL:   invite.InviteURL,
+			}
+			n := email.Notification{
+				RecipientMail: invite.Email,
+			}
+			err = email.GetClient().SendEmail(context.Background(), n, e)
+			if err != nil {
+				slog.Error("failed to send email invite", "user", invite.Email, "error", err)
+			}
+		}(invite)
+	}
+
+}
+
+// swagger:route GET /api/v1/users/invites user listUserInvites
+//
+// lists all pending invited users.
+//
+//			Schemes: https
+//
+//			Security:
+//	  		oauth
+//
+//			Responses:
+//				200: ReturnSuccessResponseWithJson
+func listUserInvites(w http.ResponseWriter, r *http.Request) {
+	usersInvites, err := logic.ListUserInvites()
+	if err != nil {
+		logger.Log(0, "failed to fetch users: ", err.Error())
+		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
+		return
+	}
+	logic.ReturnSuccessResponseWithJson(w, r, usersInvites, "fetched pending user invites")
+}
+
+// swagger:route DELETE /api/v1/users/invite user deleteUserInvite
+//
+// delete pending invite.
+//
+//			Schemes: https
+//
+//			Security:
+//	  		oauth
+//
+//			Responses:
+//				200: ReturnSuccessResponse
+func deleteUserInvite(w http.ResponseWriter, r *http.Request) {
+	email, _ := url.QueryUnescape(r.URL.Query().Get("invitee_email"))
+	err := logic.DeleteUserInvite(email)
+	if err != nil {
+		logger.Log(0, "failed to delete user invite: ", email, err.Error())
+		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
+		return
+	}
+	logic.ReturnSuccessResponse(w, r, "deleted user invite")
+}
+
+// swagger:route DELETE /api/v1/users/invites user deleteAllUserInvites
+//
+// deletes all pending invites.
+//
+//			Schemes: https
+//
+//			Security:
+//	  		oauth
+//
+//			Responses:
+//				200: ReturnSuccessResponse
+func deleteAllUserInvites(w http.ResponseWriter, r *http.Request) {
+	err := database.DeleteAllRecords(database.USER_INVITES_TABLE_NAME)
+	if err != nil {
+		logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("failed to delete all pending user invites "+err.Error()), "internal"))
+		return
+	}
+	logic.ReturnSuccessResponse(w, r, "cleared all pending user invites")
+}
+
+// swagger:route GET /api/v1/user/groups user listUserGroups
+//
+// Get all user groups.
+//
+//			Schemes: https
+//
+//			Security:
+//	  		oauth
+//
+//			Responses:
+//				200: userBodyResponse
+func listUserGroups(w http.ResponseWriter, r *http.Request) {
+	groups, err := proLogic.ListUserGroups()
+	if err != nil {
+		logic.ReturnErrorResponse(w, r, models.ErrorResponse{
+			Code:    http.StatusInternalServerError,
+			Message: err.Error(),
+		})
+		return
+	}
+	logic.ReturnSuccessResponseWithJson(w, r, groups, "successfully fetched user groups")
+}
+
+// swagger:route GET /api/v1/user/group user getUserGroup
+//
+// Get user group.
+//
+//			Schemes: https
+//
+//			Security:
+//	  		oauth
+//
+//			Responses:
+//				200: userBodyResponse
+func getUserGroup(w http.ResponseWriter, r *http.Request) {
+
+	gid, _ := url.QueryUnescape(r.URL.Query().Get("group_id"))
+	if gid == "" {
+		logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("group id is required"), "badrequest"))
+		return
+	}
+	group, err := proLogic.GetUserGroup(models.UserGroupID(gid))
+	if err != nil {
+		logic.ReturnErrorResponse(w, r, models.ErrorResponse{
+			Code:    http.StatusInternalServerError,
+			Message: err.Error(),
+		})
+		return
+	}
+	logic.ReturnSuccessResponseWithJson(w, r, group, "successfully fetched user group")
+}
+
+// swagger:route POST /api/v1/user/group user createUserGroup
+//
+// Create user groups.
+//
+//			Schemes: https
+//
+//			Security:
+//	  		oauth
+//
+//			Responses:
+//				200: userBodyResponse
+func createUserGroup(w http.ResponseWriter, r *http.Request) {
+	var userGroupReq models.CreateGroupReq
+	err := json.NewDecoder(r.Body).Decode(&userGroupReq)
+	if err != nil {
+		slog.Error("error decoding request body", "error",
+			err.Error())
+		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
+		return
+	}
+	err = proLogic.ValidateCreateGroupReq(userGroupReq.Group)
+	if err != nil {
+		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
+		return
+	}
+	err = proLogic.CreateUserGroup(userGroupReq.Group)
+	if err != nil {
+		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
+		return
+	}
+	for _, userID := range userGroupReq.Members {
+		user, err := logic.GetUser(userID)
+		if err != nil {
+			continue
+		}
+		if len(user.UserGroups) == 0 {
+			user.UserGroups = make(map[models.UserGroupID]struct{})
+		}
+		user.UserGroups[userGroupReq.Group.ID] = struct{}{}
+		logic.UpsertUser(*user)
+	}
+	logic.ReturnSuccessResponseWithJson(w, r, userGroupReq.Group, "created user group")
+}
+
+// swagger:route PUT /api/v1/user/group user updateUserGroup
+//
+// Update user group.
+//
+//			Schemes: https
+//
+//			Security:
+//	  		oauth
+//
+//			Responses:
+//				200: userBodyResponse
+func updateUserGroup(w http.ResponseWriter, r *http.Request) {
+	var userGroup models.UserGroup
+	err := json.NewDecoder(r.Body).Decode(&userGroup)
+	if err != nil {
+		slog.Error("error decoding request body", "error",
+			err.Error())
+		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
+		return
+	}
+	err = proLogic.ValidateUpdateGroupReq(userGroup)
+	if err != nil {
+		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
+		return
+	}
+	err = proLogic.UpdateUserGroup(userGroup)
+	if err != nil {
+		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
+		return
+	}
+	logic.ReturnSuccessResponseWithJson(w, r, userGroup, "updated user group")
+}
+
+// swagger:route DELETE /api/v1/user/group user deleteUserGroup
+//
+// delete user group.
+//
+//			Schemes: https
+//
+//			Security:
+//	  		oauth
+//
+//			Responses:
+//				200: userBodyResponse
+func deleteUserGroup(w http.ResponseWriter, r *http.Request) {
+
+	gid, _ := url.QueryUnescape(r.URL.Query().Get("group_id"))
+	if gid == "" {
+		logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("role is required"), "badrequest"))
+		return
+	}
+	err := proLogic.DeleteUserGroup(models.UserGroupID(gid))
+	if err != nil {
+		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
+		return
+	}
+	logic.ReturnSuccessResponseWithJson(w, r, nil, "deleted user group")
+}
+
+// swagger:route GET /api/v1/user/roles user listRoles
+//
+// lists all user roles.
+//
+//			Schemes: https
+//
+//			Security:
+//	  		oauth
+//
+//			Responses:
+//				200: userBodyResponse
+func listRoles(w http.ResponseWriter, r *http.Request) {
+	roles, err := proLogic.ListRoles()
+	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")
+}
+
+// swagger:route GET /api/v1/user/role user getRole
+//
+// Get user role permission templates.
+//
+//			Schemes: https
+//
+//			Security:
+//	  		oauth
+//
+//			Responses:
+//				200: userBodyResponse
+func getRole(w http.ResponseWriter, r *http.Request) {
+	rid, _ := url.QueryUnescape(r.URL.Query().Get("role_id"))
+	if rid == "" {
+		logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("role is required"), "badrequest"))
+		return
+	}
+	role, err := logic.GetRole(models.UserRole(rid))
+	if err != nil {
+		logic.ReturnErrorResponse(w, r, models.ErrorResponse{
+			Code:    http.StatusInternalServerError,
+			Message: err.Error(),
+		})
+		return
+	}
+	logic.ReturnSuccessResponseWithJson(w, r, role, "successfully fetched user role permission templates")
+}
+
+// swagger:route POST /api/v1/user/role user createRole
+//
+// Create user role permission template.
+//
+//			Schemes: https
+//
+//			Security:
+//	  		oauth
+//
+//			Responses:
+//				200: userBodyResponse
+func createRole(w http.ResponseWriter, r *http.Request) {
+	var userRole models.UserRolePermissionTemplate
+	err := json.NewDecoder(r.Body).Decode(&userRole)
+	if err != nil {
+		slog.Error("error decoding request body", "error",
+			err.Error())
+		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
+		return
+	}
+	err = proLogic.ValidateCreateRoleReq(userRole)
+	if err != nil {
+		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
+		return
+	}
+	userRole.Default = false
+	userRole.GlobalLevelAccess = make(map[models.RsrcType]map[models.RsrcID]models.RsrcPermissionScope)
+	err = proLogic.CreateRole(userRole)
+	if err != nil {
+		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
+		return
+	}
+	logic.ReturnSuccessResponseWithJson(w, r, userRole, "created user role")
+}
+
+// swagger:route PUT /api/v1/user/role user updateRole
+//
+// Update user role permission template.
+//
+//			Schemes: https
+//
+//			Security:
+//	  		oauth
+//
+//			Responses:
+//				200: userBodyResponse
+func updateRole(w http.ResponseWriter, r *http.Request) {
+	var userRole models.UserRolePermissionTemplate
+	err := json.NewDecoder(r.Body).Decode(&userRole)
+	if err != nil {
+		slog.Error("error decoding request body", "error",
+			err.Error())
+		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
+		return
+	}
+	err = proLogic.ValidateUpdateRoleReq(userRole)
+	if err != nil {
+		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
+		return
+	}
+	userRole.GlobalLevelAccess = make(map[models.RsrcType]map[models.RsrcID]models.RsrcPermissionScope)
+	err = proLogic.UpdateRole(userRole)
+	if err != nil {
+		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
+		return
+	}
+	logic.ReturnSuccessResponseWithJson(w, r, userRole, "updated user role")
+}
+
+// swagger:route DELETE /api/v1/user/role user deleteRole
+//
+// Delete user role permission template.
+//
+//			Schemes: https
+//
+//			Security:
+//	  		oauth
+//
+//			Responses:
+//				200: userBodyResponse
+func deleteRole(w http.ResponseWriter, r *http.Request) {
+
+	rid, _ := url.QueryUnescape(r.URL.Query().Get("role_id"))
+	if rid == "" {
+		logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("role is required"), "badrequest"))
+		return
+	}
+	err := proLogic.DeleteRole(models.UserRole(rid))
+	if err != nil {
+		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
+		return
+	}
+	logic.ReturnSuccessResponseWithJson(w, r, nil, "created user role")
 }
 
 // swagger:route POST /api/users/{username}/remote_access_gw user attachUserToRemoteAccessGateway
@@ -190,7 +757,7 @@ func getUserRemoteAccessGwsV1(w http.ResponseWriter, r *http.Request) {
 		return
 	}
 	logger.Log(0, "------------> 6. getUserRemoteAccessGwsV1")
-	userGwNodes := logic.GetUserRAGNodes(*user)
+	userGwNodes := proLogic.GetUserRAGNodes(*user)
 	logger.Log(0, fmt.Sprintf("1. User Gw Nodes: %+v", userGwNodes))
 	for _, extClient := range allextClients {
 		node, ok := userGwNodes[extClient.IngressGatewayID]
@@ -528,3 +1095,135 @@ func getAllowedRagEndpoints(ragNode *models.Node, ragHost *models.Host) []string
 	}
 	return endpoints
 }
+
+// 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()
+
+	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 {
+			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.
+	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")
+}

+ 0 - 0
email/email.go → pro/email/email.go


+ 0 - 0
email/invite.go → pro/email/invite.go


+ 0 - 0
email/resend.go → pro/email/resend.go


+ 0 - 0
email/smtp.go → pro/email/smtp.go


+ 0 - 0
email/utils.go → pro/email/utils.go


+ 5 - 0
pro/initialize.go

@@ -119,6 +119,11 @@ func InitPro() {
 	logic.GetAllowedIpForInetNodeClient = proLogic.GetAllowedIpForInetNodeClient
 	mq.UpdateMetrics = proLogic.MQUpdateMetrics
 	mq.UpdateMetricsFallBack = proLogic.MQUpdateMetricsFallBack
+	logic.GetFilteredNodesByUserAccess = proLogic.GetFilteredNodesByUserAccess
+	logic.CreateRole = proLogic.CreateRole
+	logic.NetworkPermissionsCheck = proLogic.NetworkPermissionsCheck
+	logic.GlobalPermissionsCheck = proLogic.GlobalPermissionsCheck
+	logic.DeleteNetworkRoles = proLogic.DeleteNetworkRoles
 }
 
 func retrieveProLogo() string {

+ 186 - 0
pro/logic/security.go

@@ -0,0 +1,186 @@
+package logic
+
+import (
+	"errors"
+	"fmt"
+	"net/http"
+
+	"github.com/gravitl/netmaker/logger"
+	"github.com/gravitl/netmaker/logic"
+	"github.com/gravitl/netmaker/models"
+)
+
+func NetworkPermissionsCheck(username string, r *http.Request) error {
+	// at this point global checks should be completed
+	user, err := logic.GetUser(username)
+	if err != nil {
+		return err
+	}
+	logger.Log(0, "NET MIDDL----> 1")
+	userRole, err := logic.GetRole(user.PlatformRoleID)
+	if err != nil {
+		return errors.New("access denied")
+	}
+	if userRole.FullAccess {
+		return nil
+	}
+	logger.Log(0, "NET MIDDL----> 2")
+	// get info from header to determine the target rsrc
+	targetRsrc := r.Header.Get("TARGET_RSRC")
+	targetRsrcID := r.Header.Get("TARGET_RSRC_ID")
+	netID := r.Header.Get("NET_ID")
+	if targetRsrc == "" {
+		return errors.New("target rsrc is missing")
+	}
+	if netID == "" {
+		return errors.New("network id is missing")
+	}
+	if r.Method == "" {
+		r.Method = http.MethodGet
+	}
+	if targetRsrc == models.MetricRsrc.String() {
+		return nil
+	}
+
+	// check if user has scope for target resource
+	// TODO - differentitate between global scope and network scope apis
+	netRoles := user.NetworkRoles[models.NetworkID(netID)]
+	for netRoleID := range netRoles {
+		err = checkNetworkAccessPermissions(netRoleID, username, r.Method, targetRsrc, targetRsrcID)
+		if err == nil {
+			return nil
+		}
+	}
+	for groupID := range user.UserGroups {
+		userG, err := GetUserGroup(groupID)
+		if err == nil {
+			netRoles := userG.NetworkRoles[models.NetworkID(netID)]
+			for netRoleID := range netRoles {
+				err = checkNetworkAccessPermissions(netRoleID, username, r.Method, targetRsrc, targetRsrcID)
+				if err == nil {
+					return nil
+				}
+			}
+		}
+	}
+
+	return errors.New("access denied")
+}
+
+func checkNetworkAccessPermissions(netRoleID models.UserRole, username, reqScope, targetRsrc, targetRsrcID string) error {
+	networkPermissionScope, err := logic.GetRole(netRoleID)
+	if err != nil {
+		return err
+	}
+	logger.Log(0, "NET MIDDL----> 3", string(netRoleID))
+	if networkPermissionScope.FullAccess {
+		return nil
+	}
+	rsrcPermissionScope, ok := networkPermissionScope.NetworkLevelAccess[models.RsrcType(targetRsrc)]
+	if targetRsrc == models.HostRsrc.String() && !ok {
+		rsrcPermissionScope, ok = networkPermissionScope.NetworkLevelAccess[models.RemoteAccessGwRsrc]
+	}
+	if !ok {
+		return errors.New("access denied")
+	}
+	logger.Log(0, "NET MIDDL----> 4", string(netRoleID))
+	if allRsrcsTypePermissionScope, ok := rsrcPermissionScope[models.RsrcID(fmt.Sprintf("all_%s", targetRsrc))]; ok {
+		// handle extclient apis here
+		if models.RsrcType(targetRsrc) == models.ExtClientsRsrc && allRsrcsTypePermissionScope.SelfOnly && targetRsrcID != "" {
+			extclient, err := logic.GetExtClient(targetRsrcID, networkPermissionScope.NetworkID)
+			if err != nil {
+				return err
+			}
+			if !logic.IsUserAllowedAccessToExtClient(username, extclient) {
+				return errors.New("access denied")
+			}
+		}
+		err = checkPermissionScopeWithReqMethod(allRsrcsTypePermissionScope, reqScope)
+		if err == nil {
+			return nil
+		}
+
+	}
+	if targetRsrc == models.HostRsrc.String() {
+		if allRsrcsTypePermissionScope, ok := rsrcPermissionScope[models.RsrcID(fmt.Sprintf("all_%s", models.RemoteAccessGwRsrc))]; ok {
+			err = checkPermissionScopeWithReqMethod(allRsrcsTypePermissionScope, reqScope)
+			if err == nil {
+				return nil
+			}
+		}
+	}
+	logger.Log(0, "NET MIDDL----> 5", string(netRoleID))
+	if targetRsrcID == "" {
+		return errors.New("target rsrc id is empty")
+	}
+	if scope, ok := rsrcPermissionScope[models.RsrcID(targetRsrcID)]; ok {
+		err = checkPermissionScopeWithReqMethod(scope, reqScope)
+		if err == nil {
+			return nil
+		}
+	}
+	logger.Log(0, "NET MIDDL----> 6", string(netRoleID))
+	return errors.New("access denied")
+}
+
+func GlobalPermissionsCheck(username string, r *http.Request) error {
+	user, err := logic.GetUser(username)
+	if err != nil {
+		return err
+	}
+	userRole, err := logic.GetRole(user.PlatformRoleID)
+	if err != nil {
+		return errors.New("access denied")
+	}
+	if userRole.FullAccess {
+		return nil
+	}
+	targetRsrc := r.Header.Get("TARGET_RSRC")
+	targetRsrcID := r.Header.Get("TARGET_RSRC_ID")
+	if targetRsrc == "" {
+		return errors.New("target rsrc is missing")
+	}
+	if r.Method == "" {
+		r.Method = http.MethodGet
+	}
+	if targetRsrc == models.MetricRsrc.String() {
+		return nil
+	}
+	if (targetRsrc == models.HostRsrc.String() || targetRsrc == models.NetworkRsrc.String()) && r.Method == http.MethodGet && targetRsrcID == "" {
+		return nil
+	}
+	if targetRsrc == models.UserRsrc.String() && username == targetRsrcID && (r.Method != http.MethodDelete) {
+		return nil
+	}
+	rsrcPermissionScope, ok := userRole.GlobalLevelAccess[models.RsrcType(targetRsrc)]
+	if !ok {
+		return fmt.Errorf("access denied to %s rsrc", targetRsrc)
+	}
+	if allRsrcsTypePermissionScope, ok := rsrcPermissionScope[models.RsrcID(fmt.Sprintf("all_%s", targetRsrc))]; ok {
+		return checkPermissionScopeWithReqMethod(allRsrcsTypePermissionScope, r.Method)
+
+	}
+	if targetRsrcID == "" {
+		return errors.New("target rsrc id is missing")
+	}
+	if scope, ok := rsrcPermissionScope[models.RsrcID(targetRsrcID)]; ok {
+		return checkPermissionScopeWithReqMethod(scope, r.Method)
+	}
+	return errors.New("access denied")
+}
+
+func checkPermissionScopeWithReqMethod(scope models.RsrcPermissionScope, reqmethod string) error {
+	if reqmethod == http.MethodGet && scope.Read {
+		return nil
+	}
+	if (reqmethod == http.MethodPatch || reqmethod == http.MethodPut) && scope.Update {
+		return nil
+	}
+	if reqmethod == http.MethodDelete && scope.Delete {
+		return nil
+	}
+	if reqmethod == http.MethodPost && scope.Create {
+		return nil
+	}
+	return errors.New("operation not permitted")
+}

+ 560 - 0
pro/logic/user_mgmt.go

@@ -0,0 +1,560 @@
+package logic
+
+import (
+	"encoding/json"
+	"errors"
+	"fmt"
+
+	"github.com/gravitl/netmaker/database"
+	"github.com/gravitl/netmaker/logger"
+	"github.com/gravitl/netmaker/logic"
+	"github.com/gravitl/netmaker/models"
+)
+
+// Pre-Define Permission Templates for default Roles
+var SuperAdminPermissionTemplate = models.UserRolePermissionTemplate{
+	ID:         models.SuperAdminRole,
+	Default:    true,
+	FullAccess: true,
+}
+
+var AdminPermissionTemplate = models.UserRolePermissionTemplate{
+	ID:         models.AdminRole,
+	Default:    true,
+	FullAccess: true,
+}
+
+var ServiceUserPermissionTemplate = models.UserRolePermissionTemplate{
+	ID:                  models.ServiceUser,
+	Default:             true,
+	FullAccess:          false,
+	DenyDashboardAccess: true,
+}
+
+var PlatformUserUserPermissionTemplate = models.UserRolePermissionTemplate{
+	ID:         models.PlatformUser,
+	Default:    true,
+	FullAccess: false,
+}
+
+func UserRolesInit() {
+	d, _ := json.Marshal(SuperAdminPermissionTemplate)
+	database.Insert(SuperAdminPermissionTemplate.ID.String(), string(d), database.USER_PERMISSIONS_TABLE_NAME)
+	d, _ = json.Marshal(AdminPermissionTemplate)
+	database.Insert(AdminPermissionTemplate.ID.String(), string(d), database.USER_PERMISSIONS_TABLE_NAME)
+	d, _ = json.Marshal(ServiceUserPermissionTemplate)
+	database.Insert(ServiceUserPermissionTemplate.ID.String(), string(d), database.USER_PERMISSIONS_TABLE_NAME)
+	d, _ = json.Marshal(PlatformUserUserPermissionTemplate)
+	database.Insert(PlatformUserUserPermissionTemplate.ID.String(), string(d), database.USER_PERMISSIONS_TABLE_NAME)
+
+}
+
+func CreateDefaultNetworkRoles(netID string) {
+	var NetworkAdminPermissionTemplate = models.UserRolePermissionTemplate{
+		ID:                 models.UserRole(fmt.Sprintf("%s_%s", netID, models.NetworkAdmin)),
+		Default:            false,
+		NetworkID:          netID,
+		FullAccess:         true,
+		NetworkLevelAccess: make(map[models.RsrcType]map[models.RsrcID]models.RsrcPermissionScope),
+	}
+
+	var NetworkUserPermissionTemplate = models.UserRolePermissionTemplate{
+		ID:                  models.UserRole(fmt.Sprintf("%s_%s", netID, models.NetworkUser)),
+		Default:             false,
+		FullAccess:          false,
+		NetworkID:           netID,
+		DenyDashboardAccess: false,
+		NetworkLevelAccess: map[models.RsrcType]map[models.RsrcID]models.RsrcPermissionScope{
+			models.RemoteAccessGwRsrc: {
+				models.AllRemoteAccessGwRsrcID: models.RsrcPermissionScope{
+					Read:      true,
+					VPNaccess: true,
+				},
+			},
+			models.ExtClientsRsrc: {
+				models.AllExtClientsRsrcID: models.RsrcPermissionScope{
+					Read:     true,
+					Create:   true,
+					Update:   true,
+					Delete:   true,
+					SelfOnly: true,
+				},
+			},
+		},
+	}
+	d, _ := json.Marshal(NetworkAdminPermissionTemplate)
+	database.Insert(NetworkAdminPermissionTemplate.ID.String(), string(d), database.USER_PERMISSIONS_TABLE_NAME)
+	d, _ = json.Marshal(NetworkUserPermissionTemplate)
+	database.Insert(NetworkUserPermissionTemplate.ID.String(), string(d), database.USER_PERMISSIONS_TABLE_NAME)
+}
+
+func DeleteNetworkRoles(netID string) {
+	users, err := logic.GetUsersDB()
+	if err != nil {
+		return
+	}
+	for _, user := range users {
+		if _, ok := user.NetworkRoles[models.NetworkID(netID)]; ok {
+			delete(user.NetworkRoles, models.NetworkID(netID))
+			logic.UpsertUser(user)
+		}
+
+	}
+	userGs, _ := ListUserGroups()
+	for _, userGI := range userGs {
+		if _, ok := userGI.NetworkRoles[models.NetworkID(netID)]; ok {
+			delete(userGI.NetworkRoles, models.NetworkID(netID))
+			UpdateUserGroup(userGI)
+		}
+	}
+
+	roles, _ := ListRoles()
+	for _, role := range roles {
+		if role.NetworkID == netID {
+			DeleteRole(role.ID)
+		}
+	}
+}
+
+// ListRoles - lists user roles permission templates
+func ListRoles() ([]models.UserRolePermissionTemplate, error) {
+	data, err := database.FetchRecords(database.USER_PERMISSIONS_TABLE_NAME)
+	if err != nil && !database.IsEmptyRecord(err) {
+		return []models.UserRolePermissionTemplate{}, err
+	}
+	userRoles := []models.UserRolePermissionTemplate{}
+	for _, dataI := range data {
+		userRole := models.UserRolePermissionTemplate{}
+		err := json.Unmarshal([]byte(dataI), &userRole)
+		if err != nil {
+			continue
+		}
+		userRoles = append(userRoles, userRole)
+	}
+	return userRoles, nil
+}
+
+func ValidateCreateRoleReq(userRole models.UserRolePermissionTemplate) error {
+	// check if role exists with this id
+	_, err := logic.GetRole(userRole.ID)
+	if err == nil {
+		return fmt.Errorf("role with id `%s` exists already", userRole.ID.String())
+	}
+	if len(userRole.NetworkLevelAccess) > 0 {
+		for rsrcType := range userRole.NetworkLevelAccess {
+			if _, ok := models.RsrcTypeMap[rsrcType]; !ok {
+				return errors.New("invalid rsrc type " + rsrcType.String())
+			}
+		}
+	}
+	if userRole.NetworkID == "" {
+		return errors.New("only network roles are allowed to be created")
+	}
+	return nil
+}
+
+func ValidateUpdateRoleReq(userRole models.UserRolePermissionTemplate) error {
+	roleInDB, err := logic.GetRole(userRole.ID)
+	if err != nil {
+		return err
+	}
+	if roleInDB.NetworkID != userRole.NetworkID {
+		return errors.New("network id mismatch")
+	}
+	if roleInDB.Default {
+		return errors.New("cannot update default role")
+	}
+	if len(userRole.NetworkLevelAccess) > 0 {
+		for rsrcType := range userRole.NetworkLevelAccess {
+			if _, ok := models.RsrcTypeMap[rsrcType]; !ok {
+				return errors.New("invalid rsrc type " + rsrcType.String())
+			}
+		}
+	}
+	return nil
+}
+
+// CreateRole - inserts new role into DB
+func CreateRole(r models.UserRolePermissionTemplate) error {
+	// check if role already exists
+	if r.ID.String() == "" {
+		return errors.New("role id cannot be empty")
+	}
+	_, err := database.FetchRecord(database.USER_PERMISSIONS_TABLE_NAME, r.ID.String())
+	if err == nil {
+		return errors.New("role already exists")
+	}
+	d, err := json.Marshal(r)
+	if err != nil {
+		return err
+	}
+	return database.Insert(r.ID.String(), string(d), database.USER_PERMISSIONS_TABLE_NAME)
+}
+
+// UpdateRole - updates role template
+func UpdateRole(r models.UserRolePermissionTemplate) error {
+	if r.ID.String() == "" {
+		return errors.New("role id cannot be empty")
+	}
+	_, err := database.FetchRecord(database.USER_PERMISSIONS_TABLE_NAME, r.ID.String())
+	if err != nil {
+		return err
+	}
+	d, err := json.Marshal(r)
+	if err != nil {
+		return err
+	}
+	return database.Insert(r.ID.String(), string(d), database.USER_PERMISSIONS_TABLE_NAME)
+}
+
+// DeleteRole - deletes user role
+func DeleteRole(rid models.UserRole) error {
+	if rid.String() == "" {
+		return errors.New("role id cannot be empty")
+	}
+	users, err := logic.GetUsersDB()
+	if err != nil {
+		return err
+	}
+	role, err := logic.GetRole(rid)
+	if err != nil {
+		return err
+	}
+	if role.Default {
+		return errors.New("cannot delete default role")
+	}
+	for _, user := range users {
+		for userG := range user.UserGroups {
+			ug, err := GetUserGroup(userG)
+			if err == nil {
+				if role.NetworkID != "" {
+					for _, networkRoles := range ug.NetworkRoles {
+						if _, ok := networkRoles[rid]; ok {
+							err = errors.New("role cannot be deleted as active user groups are using this role")
+							return err
+						}
+					}
+				}
+
+			}
+		}
+
+		if user.PlatformRoleID == rid {
+			err = errors.New("active roles cannot be deleted.switch existing users to a new role before deleting")
+			return err
+		}
+		for _, networkRoles := range user.NetworkRoles {
+			if _, ok := networkRoles[rid]; ok {
+				err = errors.New("active roles cannot be deleted.switch existing users to a new role before deleting")
+				return err
+			}
+
+		}
+	}
+	return database.DeleteRecord(database.USER_PERMISSIONS_TABLE_NAME, rid.String())
+}
+
+func ValidateCreateGroupReq(g models.UserGroup) error {
+	// check platform role is valid
+	role, err := logic.GetRole(g.PlatformRole)
+	if err != nil {
+		err = fmt.Errorf("invalid platform role")
+		return err
+	}
+	if role.NetworkID != "" {
+		return errors.New("network role cannot be used as platform role")
+	}
+	// check if network roles are valid
+	for _, roleMap := range g.NetworkRoles {
+		for roleID := range roleMap {
+			role, err := logic.GetRole(roleID)
+			if err != nil {
+				return fmt.Errorf("invalid network role %s", roleID)
+			}
+			if role.NetworkID == "" {
+				return errors.New("platform role cannot be used as network role")
+			}
+		}
+	}
+	return nil
+}
+func ValidateUpdateGroupReq(g models.UserGroup) error {
+	// check platform role is valid
+	role, err := logic.GetRole(g.PlatformRole)
+	if err != nil {
+		err = fmt.Errorf("invalid platform role")
+		return err
+	}
+	if role.NetworkID != "" {
+		return errors.New("network role cannot be used as platform role")
+	}
+	for networkID := range g.NetworkRoles {
+		userRolesMap := g.NetworkRoles[networkID]
+		for roleID := range userRolesMap {
+			netRole, err := logic.GetRole(roleID)
+			if err != nil {
+				err = fmt.Errorf("invalid network role")
+				return err
+			}
+			if netRole.NetworkID == "" {
+				return errors.New("platform role cannot be used as network role")
+			}
+		}
+	}
+	return nil
+}
+
+// CreateUserGroup - creates new user group
+func CreateUserGroup(g models.UserGroup) error {
+	// check if role already exists
+	if g.ID == "" {
+		return errors.New("group id cannot be empty")
+	}
+	_, err := database.FetchRecord(database.USER_GROUPS_TABLE_NAME, g.ID.String())
+	if err == nil {
+		return errors.New("group already exists")
+	}
+	d, err := json.Marshal(g)
+	if err != nil {
+		return err
+	}
+	return database.Insert(g.ID.String(), string(d), database.USER_GROUPS_TABLE_NAME)
+}
+
+// GetUserGroup - fetches user group
+func GetUserGroup(gid models.UserGroupID) (models.UserGroup, error) {
+	d, err := database.FetchRecord(database.USER_GROUPS_TABLE_NAME, gid.String())
+	if err != nil {
+		return models.UserGroup{}, err
+	}
+	var ug models.UserGroup
+	err = json.Unmarshal([]byte(d), &ug)
+	if err != nil {
+		return ug, err
+	}
+	return ug, nil
+}
+
+// ListUserGroups - lists user groups
+func ListUserGroups() ([]models.UserGroup, error) {
+	data, err := database.FetchRecords(database.USER_GROUPS_TABLE_NAME)
+	if err != nil && !database.IsEmptyRecord(err) {
+		return []models.UserGroup{}, err
+	}
+	userGroups := []models.UserGroup{}
+	for _, dataI := range data {
+		userGroup := models.UserGroup{}
+		err := json.Unmarshal([]byte(dataI), &userGroup)
+		if err != nil {
+			continue
+		}
+		userGroups = append(userGroups, userGroup)
+	}
+	return userGroups, nil
+}
+
+// UpdateUserGroup - updates new user group
+func UpdateUserGroup(g models.UserGroup) error {
+	// check if group exists
+	if g.ID == "" {
+		return errors.New("group id cannot be empty")
+	}
+	_, err := database.FetchRecord(database.USER_GROUPS_TABLE_NAME, g.ID.String())
+	if err != nil {
+		return err
+	}
+	d, err := json.Marshal(g)
+	if err != nil {
+		return err
+	}
+	return database.Insert(g.ID.String(), string(d), database.USER_GROUPS_TABLE_NAME)
+}
+
+// DeleteUserGroup - deletes user group
+func DeleteUserGroup(gid models.UserGroupID) error {
+	users, err := logic.GetUsersDB()
+	if err != nil {
+		return err
+	}
+	for _, user := range users {
+		delete(user.UserGroups, gid)
+		logic.UpsertUser(user)
+	}
+	return database.DeleteRecord(database.USER_GROUPS_TABLE_NAME, gid.String())
+}
+
+func HasNetworkRsrcScope(permissionTemplate models.UserRolePermissionTemplate, netid string, rsrcType models.RsrcType, rsrcID models.RsrcID, op string) bool {
+	if permissionTemplate.FullAccess {
+		return true
+	}
+
+	rsrcScope, ok := permissionTemplate.NetworkLevelAccess[rsrcType]
+	if !ok {
+		return false
+	}
+	_, ok = rsrcScope[rsrcID]
+	return ok
+}
+func GetUserRAGNodes(user models.User) (gws map[string]models.Node) {
+	logger.Log(0, "------------> 7. getUserRemoteAccessGwsV1")
+	gws = make(map[string]models.Node)
+	userGwAccessScope := GetUserNetworkRolesWithRemoteVPNAccess(user)
+	logger.Log(0, fmt.Sprintf("User Gw Access Scope: %+v", userGwAccessScope))
+	_, allNetAccess := userGwAccessScope["*"]
+	nodes, err := logic.GetAllNodes()
+	if err != nil {
+		return
+	}
+	logger.Log(0, "------------> 8. getUserRemoteAccessGwsV1")
+	for _, node := range nodes {
+		if node.IsIngressGateway && !node.PendingDelete {
+			if allNetAccess {
+				gws[node.ID.String()] = node
+			} else {
+				gwRsrcMap := userGwAccessScope[models.NetworkID(node.Network)]
+				scope, ok := gwRsrcMap[models.AllRemoteAccessGwRsrcID]
+				if !ok {
+					if scope, ok = gwRsrcMap[models.RsrcID(node.ID.String())]; !ok {
+						continue
+					}
+				}
+				if scope.VPNaccess {
+					gws[node.ID.String()] = node
+				}
+
+			}
+		}
+	}
+	logger.Log(0, "------------> 9. getUserRemoteAccessGwsV1")
+	return
+}
+
+// GetUserNetworkRoles - get user network roles
+func GetUserNetworkRolesWithRemoteVPNAccess(user models.User) (gwAccess map[models.NetworkID]map[models.RsrcID]models.RsrcPermissionScope) {
+	gwAccess = make(map[models.NetworkID]map[models.RsrcID]models.RsrcPermissionScope)
+	logger.Log(0, "------------> 7.1 getUserRemoteAccessGwsV1")
+	platformRole, err := logic.GetRole(user.PlatformRoleID)
+	if err != nil {
+		return
+	}
+	if platformRole.FullAccess {
+		gwAccess[models.NetworkID("*")] = make(map[models.RsrcID]models.RsrcPermissionScope)
+		return
+	}
+	logger.Log(0, "------------> 7.2 getUserRemoteAccessGwsV1")
+	for netID, roleMap := range user.NetworkRoles {
+		for roleID := range roleMap {
+			role, err := logic.GetRole(roleID)
+			if err == nil {
+				if role.FullAccess {
+					gwAccess[netID] = map[models.RsrcID]models.RsrcPermissionScope{
+						models.AllRemoteAccessGwRsrcID: {
+							Create:    true,
+							Read:      true,
+							Update:    true,
+							VPNaccess: true,
+							Delete:    true,
+						},
+						models.AllExtClientsRsrcID: {
+							Create: true,
+							Read:   true,
+							Update: true,
+							Delete: true,
+						},
+					}
+					break
+				}
+				if rsrcsMap, ok := role.NetworkLevelAccess[models.RemoteAccessGwRsrc]; ok {
+					if permissions, ok := rsrcsMap[models.AllRemoteAccessGwRsrcID]; ok && permissions.VPNaccess {
+						if len(gwAccess[netID]) == 0 {
+							gwAccess[netID] = make(map[models.RsrcID]models.RsrcPermissionScope)
+						}
+						gwAccess[netID][models.AllRemoteAccessGwRsrcID] = permissions
+						break
+					} else {
+						for gwID, scope := range rsrcsMap {
+							if scope.VPNaccess {
+								if len(gwAccess[netID]) == 0 {
+									gwAccess[netID] = make(map[models.RsrcID]models.RsrcPermissionScope)
+								}
+								gwAccess[netID][gwID] = scope
+							}
+						}
+					}
+
+				}
+
+			}
+		}
+	}
+	logger.Log(0, "------------> 7.3 getUserRemoteAccessGwsV1")
+	return
+}
+
+func GetFilteredNodesByUserAccess(user models.User, nodes []models.Node) (filteredNodes []models.Node) {
+
+	nodesMap := make(map[string]struct{})
+	allNetworkRoles := []models.UserRole{}
+	if len(user.NetworkRoles) > 0 {
+		for _, netRoles := range user.NetworkRoles {
+			for netRoleI := range netRoles {
+				allNetworkRoles = append(allNetworkRoles, netRoleI)
+			}
+		}
+	}
+	if len(user.UserGroups) > 0 {
+		for userGID := range user.UserGroups {
+			userG, err := GetUserGroup(userGID)
+			if err == nil {
+				if len(userG.NetworkRoles) > 0 {
+					for _, netRoles := range userG.NetworkRoles {
+						for netRoleI := range netRoles {
+							allNetworkRoles = append(allNetworkRoles, netRoleI)
+						}
+					}
+				}
+			}
+		}
+	}
+	for _, networkRoleID := range allNetworkRoles {
+		userPermTemplate, err := logic.GetRole(networkRoleID)
+		if err != nil {
+			continue
+		}
+		networkNodes := logic.GetNetworkNodesMemory(nodes, userPermTemplate.NetworkID)
+		if userPermTemplate.FullAccess {
+			for _, node := range networkNodes {
+				nodesMap[node.ID.String()] = struct{}{}
+			}
+			filteredNodes = append(filteredNodes, networkNodes...)
+			continue
+		}
+		if rsrcPerms, ok := userPermTemplate.NetworkLevelAccess[models.RemoteAccessGwRsrc]; ok {
+			if _, ok := rsrcPerms[models.AllRemoteAccessGwRsrcID]; ok {
+				for _, node := range networkNodes {
+					if _, ok := nodesMap[node.ID.String()]; ok {
+						continue
+					}
+					if node.IsIngressGateway {
+						nodesMap[node.ID.String()] = struct{}{}
+						filteredNodes = append(filteredNodes, node)
+					}
+				}
+			} else {
+				for gwID, scope := range rsrcPerms {
+					if _, ok := nodesMap[gwID.String()]; ok {
+						continue
+					}
+					if scope.Read {
+						gwNode, err := logic.GetNodeByID(gwID.String())
+						if err == nil && gwNode.IsIngressGateway {
+							filteredNodes = append(filteredNodes, gwNode)
+						}
+					}
+				}
+			}
+		}
+
+	}
+	return
+}