Merge pull request #3021 from gravitl/NET-1227-pro

Net 1227 pro
This commit is contained in:
Abhishek K 2024-07-31 11:31:22 +05:30 committed by GitHub
commit b35554e3cf
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
24 changed files with 1615 additions and 1540 deletions

View file

@ -1,13 +1,8 @@
package auth
import (
"encoding/base64"
"encoding/json"
"github.com/gravitl/netmaker/logger"
"github.com/gravitl/netmaker/logic"
"github.com/gravitl/netmaker/models"
"golang.org/x/crypto/bcrypt"
"golang.org/x/oauth2"
)
@ -20,39 +15,6 @@ var (
auth_provider *oauth2.Config
)
// IsOauthUser - returns
func IsOauthUser(user *models.User) error {
var currentValue, err = FetchPassValue("")
if err != nil {
return err
}
var bCryptErr = bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(currentValue))
return bCryptErr
}
func FetchPassValue(newValue string) (string, error) {
type valueHolder struct {
Value string `json:"value" bson:"value"`
}
newValueHolder := valueHolder{}
var currentValue, err = logic.FetchAuthSecret()
if err != nil {
return "", err
}
var unmarshErr = json.Unmarshal([]byte(currentValue), &newValueHolder)
if unmarshErr != nil {
return "", unmarshErr
}
var b64CurrentValue, b64Err = base64.StdEncoding.DecodeString(newValueHolder.Value)
if b64Err != nil {
logger.Log(0, "could not decode pass")
return "", nil
}
return string(b64CurrentValue), nil
}
func isUserIsAllowed(username, network string) (*models.User, error) {
user, err := logic.GetUser(username)

View file

@ -60,41 +60,7 @@ func getNetworks(w http.ResponseWriter, r *http.Request) {
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
return
}
platformRole, err := logic.GetRole(user.PlatformRoleID)
if err != nil {
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
return
}
if !platformRole.FullAccess {
allNetworkRoles := make(map[models.NetworkID]struct{})
if len(user.NetworkRoles) > 0 {
for netID := range user.NetworkRoles {
allNetworkRoles[netID] = struct{}{}
}
}
if len(user.UserGroups) > 0 {
for userGID := range user.UserGroups {
userG, err := logic.GetUserGroup(userGID)
if err == nil {
if len(userG.NetworkRoles) > 0 {
for netID := range userG.NetworkRoles {
allNetworkRoles[netID] = struct{}{}
}
}
}
}
}
filteredNetworks := []models.Network{}
for _, networkI := range allnetworks {
if _, ok := allNetworkRoles[models.NetworkID(networkI.NetID)]; ok {
filteredNetworks = append(filteredNetworks, networkI)
}
}
allnetworks = filteredNetworks
}
allnetworks = logic.FilterNetworksByRole(allnetworks, *user)
logger.Log(2, r.Header.Get("user"), "fetched networks.")
logic.SortNetworks(allnetworks[:])
w.WriteHeader(http.StatusOK)

View file

@ -650,27 +650,8 @@ func deleteIngressGateway(w http.ResponseWriter, r *http.Request) {
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
return
}
if servercfg.IsPro {
go func() {
users, err := logic.GetUsersDB()
if err == nil {
for _, user := range users {
// delete role from user
if netRoles, ok := user.NetworkRoles[models.NetworkID(node.Network)]; ok {
delete(netRoles, models.GetRAGRoleName(node.Network, host.Name))
user.NetworkRoles[models.NetworkID(node.Network)] = netRoles
err = logic.UpsertUser(user)
if err != nil {
slog.Error("failed to get user", "user", user.UserName, "error", err)
}
}
}
} else {
slog.Error("failed to get users", "error", err)
}
logic.DeleteRole(models.GetRAGRoleName(node.Network, host.Name))
}()
}
go logic.RemoveNetworkRoleFromUsers(*host, node)
apiNode := node.ConvertToAPINode()
logger.Log(1, r.Header.Get("user"), "deleted ingress gateway", nodeid)

View file

@ -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"
@ -36,334 +33,7 @@ func userHandlers(r *mux.Router) {
r.HandleFunc("/api/users/{username}", logic.SecurityCheck(false, logic.ContinueIfUserMatch(http.HandlerFunc(getUser)))).Methods(http.MethodGet)
r.HandleFunc("/api/v1/users", logic.SecurityCheck(false, logic.ContinueIfUserMatch(http.HandlerFunc(getUserV1)))).Methods(http.MethodGet)
r.HandleFunc("/api/users", logic.SecurityCheck(true, http.HandlerFunc(getUsers))).Methods(http.MethodGet)
r.HandleFunc("/api/users_pending", logic.SecurityCheck(true, http.HandlerFunc(getPendingUsers))).Methods(http.MethodGet)
r.HandleFunc("/api/users_pending", logic.SecurityCheck(true, http.HandlerFunc(deleteAllPendingUsers))).Methods(http.MethodDelete)
r.HandleFunc("/api/users_pending/user/{username}", logic.SecurityCheck(true, http.HandlerFunc(deletePendingUser))).Methods(http.MethodDelete)
r.HandleFunc("/api/users_pending/user/{username}", logic.SecurityCheck(true, http.HandlerFunc(approvePendingUser))).Methods(http.MethodPost)
// 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
@ -723,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
}
@ -763,6 +422,7 @@ func createUser(w http.ResponseWriter, r *http.Request) {
logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("non-admins users can only be created on Pro version"), "forbidden"))
return
}
err = logic.CreateUser(&user)
if err != nil {
slog.Error("error creating new user: ", "user", user.UserName, "error", err.Error())
@ -867,7 +527,7 @@ func updateUser(w http.ResponseWriter, r *http.Request) {
}
}
if auth.IsOauthUser(user) == nil && userchange.Password != "" {
if logic.IsOauthUser(user) == nil && userchange.Password != "" {
err := fmt.Errorf("cannot update user's password for an oauth user %s", username)
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "forbidden"))
return
@ -990,367 +650,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")
}

View file

@ -93,18 +93,54 @@ func GetUsers() ([]models.ReturnUser, error) {
return users, err
}
// IsOauthUser - returns
func IsOauthUser(user *models.User) error {
var currentValue, err = FetchPassValue("")
if err != nil {
return err
}
var bCryptErr = bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(currentValue))
return bCryptErr
}
func FetchPassValue(newValue string) (string, error) {
type valueHolder struct {
Value string `json:"value" bson:"value"`
}
newValueHolder := valueHolder{}
var currentValue, err = FetchAuthSecret()
if err != nil {
return "", err
}
var unmarshErr = json.Unmarshal([]byte(currentValue), &newValueHolder)
if unmarshErr != nil {
return "", unmarshErr
}
var b64CurrentValue, b64Err = base64.StdEncoding.DecodeString(newValueHolder.Value)
if b64Err != nil {
logger.Log(0, "could not decode pass")
return "", nil
}
return string(b64CurrentValue), nil
}
// CreateUser - creates a user
func CreateUser(user *models.User) error {
// check if user exists
if _, err := GetUser(user.UserName); err == nil {
return errors.New("user exists")
}
user.AuthType = models.BasicAuth
if IsOauthUser(user) == nil {
user.AuthType = models.OAuth
}
var err = ValidateUser(user)
if err != nil {
logger.Log(0, "failed to validate user", err.Error())
return err
}
// encrypt that password so we never see it again
hash, err := bcrypt.GenerateFromPassword([]byte(user.Password), 5)
if err != nil {

View file

@ -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
}

View file

@ -1,8 +1,6 @@
package logic
import (
"errors"
"fmt"
"net/http"
"net/url"
"strings"
@ -21,189 +19,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 {
@ -224,9 +41,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"))

View file

@ -2,11 +2,8 @@ package logic
import (
"encoding/json"
"errors"
"fmt"
"github.com/gravitl/netmaker/database"
"github.com/gravitl/netmaker/logger"
"github.com/gravitl/netmaker/models"
)
@ -23,172 +20,26 @@ var AdminPermissionTemplate = models.UserRolePermissionTemplate{
FullAccess: true,
}
var ServiceUserPermissionTemplate = models.UserRolePermissionTemplate{
ID: models.ServiceUser,
Default: true,
FullAccess: false,
DenyDashboardAccess: true,
var GetFilteredNodesByUserAccess = func(user models.User, nodes []models.Node) (filteredNodes []models.Node) {
return
}
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")
}
var CreateRole = func(r models.UserRolePermissionTemplate) error {
return nil
}
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())
}
}
}
return nil
var FilterNetworksByRole = func(allnetworks []models.Network, user models.User) []models.Network {
return allnetworks
}
// 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 IsGroupsValid = func(groups map[models.UserGroupID]struct{}) error {
return nil
}
var RemoveNetworkRoleFromUsers = func(host models.Host, node models.Node) {}
var InitialiseRoles = func() {}
var DeleteNetworkRoles = func(netID string) {}
var CreateDefaultNetworkRoles = func(netID string) {}
// GetRole - fetches role template by id
func GetRole(roleID models.UserRole) (models.UserRolePermissionTemplate, error) {
@ -205,302 +56,10 @@ 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")
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 := 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 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
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)
}

View file

@ -356,6 +356,10 @@ func syncUsers() {
if user.PlatformRoleID.String() != "" {
continue
}
user.AuthType = models.BasicAuth
if logic.IsOauthUser(&user) == nil {
user.AuthType = models.OAuth
}
if len(user.NetworkRoles) == 0 {
user.NetworkRoles = make(map[models.NetworkID]map[models.UserRole]struct{})
}

View file

@ -12,6 +12,12 @@ type RsrcType string
type RsrcID string
type UserRole string
type UserGroupID string
type AuthType string
var (
BasicAuth AuthType = "basic_auth"
OAuth AuthType = "oauth"
)
func (r RsrcType) String() string {
return string(r)
@ -128,6 +134,7 @@ type User struct {
IsAdmin bool `json:"isadmin" bson:"isadmin"` // deprecated
IsSuperAdmin bool `json:"issuperadmin"` // deprecated
RemoteGwIDs map[string]struct{} `json:"remote_gw_ids"` // deprecated
AuthType AuthType `json:"auth_type"`
UserGroups map[UserGroupID]struct{} `json:"user_group_ids"`
PlatformRoleID UserRole `json:"platform_role_id"`
NetworkRoles map[NetworkID]map[UserRole]struct{} `json:"network_roles"`
@ -144,6 +151,7 @@ type ReturnUser struct {
UserName string `json:"username"`
IsAdmin bool `json:"isadmin"`
IsSuperAdmin bool `json:"issuperadmin"`
AuthType AuthType `json:"auth_type"`
RemoteGwIDs map[string]struct{} `json:"remote_gw_ids"` // deprecated
UserGroups map[UserGroupID]struct{} `json:"user_group_ids"`
PlatformRoleID UserRole `json:"platform_role_id"`

View file

@ -9,11 +9,11 @@ import (
"net/http"
"strings"
"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"
proLogic "github.com/gravitl/netmaker/pro/logic"
"github.com/gravitl/netmaker/servercfg"
"golang.org/x/oauth2"
"golang.org/x/oauth2/microsoft"
@ -89,7 +89,7 @@ func handleAzureCallback(w http.ResponseWriter, r *http.Request) {
if database.IsEmptyRecord(err) { // user must not exist, so try to make one
if inviteExists {
// create user
var newPass, fetchErr = auth.FetchPassValue("")
var newPass, fetchErr = logic.FetchPassValue("")
if fetchErr != nil {
logic.ReturnErrorResponse(w, r, logic.FormatError(fetchErr, "internal"))
return
@ -99,7 +99,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
@ -146,7 +146,7 @@ func handleAzureCallback(w http.ResponseWriter, r *http.Request) {
handleOauthUserNotAllowed(w)
return
}
var newPass, fetchErr = auth.FetchPassValue("")
var newPass, fetchErr = logic.FetchPassValue("")
if fetchErr != nil {
return
}

View file

@ -9,11 +9,11 @@ import (
"net/http"
"strings"
"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"
proLogic "github.com/gravitl/netmaker/pro/logic"
"github.com/gravitl/netmaker/servercfg"
"golang.org/x/oauth2"
"golang.org/x/oauth2/github"
@ -88,7 +88,7 @@ func handleGithubCallback(w http.ResponseWriter, r *http.Request) {
if database.IsEmptyRecord(err) { // user must not exist, so try to make one
if inviteExists {
// create user
var newPass, fetchErr = auth.FetchPassValue("")
var newPass, fetchErr = logic.FetchPassValue("")
if fetchErr != nil {
logic.ReturnErrorResponse(w, r, logic.FormatError(fetchErr, "internal"))
return
@ -99,7 +99,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
@ -146,7 +146,7 @@ func handleGithubCallback(w http.ResponseWriter, r *http.Request) {
handleOauthUserNotAllowed(w)
return
}
var newPass, fetchErr = auth.FetchPassValue("")
var newPass, fetchErr = logic.FetchPassValue("")
if fetchErr != nil {
return
}

View file

@ -10,11 +10,11 @@ import (
"strings"
"time"
"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"
proLogic "github.com/gravitl/netmaker/pro/logic"
"github.com/gravitl/netmaker/servercfg"
"golang.org/x/oauth2"
"golang.org/x/oauth2/google"
@ -95,7 +95,7 @@ func handleGoogleCallback(w http.ResponseWriter, r *http.Request) {
if inviteExists {
// create user
logger.Log(0, "CALLBACK ----> 4.0")
var newPass, fetchErr = auth.FetchPassValue("")
var newPass, fetchErr = logic.FetchPassValue("")
if fetchErr != nil {
logic.ReturnErrorResponse(w, r, logic.FormatError(fetchErr, "internal"))
return
@ -106,7 +106,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
@ -158,7 +158,7 @@ func handleGoogleCallback(w http.ResponseWriter, r *http.Request) {
handleOauthUserNotAllowed(w)
return
}
var newPass, fetchErr = auth.FetchPassValue("")
var newPass, fetchErr = logic.FetchPassValue("")
if fetchErr != nil {
return
}

View file

@ -5,7 +5,6 @@ import (
"fmt"
"net/http"
"github.com/gravitl/netmaker/auth"
"github.com/gravitl/netmaker/database"
"github.com/gravitl/netmaker/logger"
"github.com/gravitl/netmaker/logic"
@ -78,7 +77,7 @@ func HandleHeadlessSSOCallback(w http.ResponseWriter, r *http.Request) {
return
}
}
newPass, fetchErr := auth.FetchPassValue("")
newPass, fetchErr := logic.FetchPassValue("")
if fetchErr != nil {
return
}

View file

@ -9,11 +9,11 @@ import (
"time"
"github.com/coreos/go-oidc/v3/oidc"
"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"
proLogic "github.com/gravitl/netmaker/pro/logic"
"github.com/gravitl/netmaker/servercfg"
"golang.org/x/oauth2"
)
@ -101,7 +101,7 @@ func handleOIDCCallback(w http.ResponseWriter, r *http.Request) {
if database.IsEmptyRecord(err) { // user must not exist, so try to make one
if inviteExists {
// create user
var newPass, fetchErr = auth.FetchPassValue("")
var newPass, fetchErr = logic.FetchPassValue("")
if fetchErr != nil {
logic.ReturnErrorResponse(w, r, logic.FormatError(fetchErr, "internal"))
return
@ -111,7 +111,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
@ -158,7 +158,7 @@ func handleOIDCCallback(w http.ResponseWriter, r *http.Request) {
handleOauthUserNotAllowed(w)
return
}
var newPass, fetchErr = auth.FetchPassValue("")
var newPass, fetchErr = logic.FetchPassValue("")
if fetchErr != nil {
return
}

View file

@ -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")
}

View file

@ -22,6 +22,7 @@ import (
func InitPro() {
servercfg.IsPro = true
models.SetLogo(retrieveProLogo())
proLogic.UserRolesInit()
controller.HttpMiddlewares = append(
controller.HttpMiddlewares,
proControllers.OnlyServerAPIWhenUnlicensedMiddleware,
@ -119,6 +120,15 @@ 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
logic.CreateDefaultNetworkRoles = proLogic.CreateDefaultNetworkRoles
logic.FilterNetworksByRole = proLogic.FilterNetworksByRole
logic.IsGroupsValid = proLogic.IsGroupsValid
logic.RemoveNetworkRoleFromUsers = proLogic.RemoveNetworkRoleFromUsers
}
func retrieveProLogo() string {

186
pro/logic/security.go Normal file
View file

@ -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")
}

620
pro/logic/user_mgmt.go Normal file
View file

@ -0,0 +1,620 @@
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"
"golang.org/x/exp/slog"
)
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(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
}
func FilterNetworksByRole(allnetworks []models.Network, user models.User) []models.Network {
platformRole, err := logic.GetRole(user.PlatformRoleID)
if err != nil {
return []models.Network{}
}
if !platformRole.FullAccess {
allNetworkRoles := make(map[models.NetworkID]struct{})
if len(user.NetworkRoles) > 0 {
for netID := range user.NetworkRoles {
allNetworkRoles[netID] = struct{}{}
}
}
if len(user.UserGroups) > 0 {
for userGID := range user.UserGroups {
userG, err := GetUserGroup(userGID)
if err == nil {
if len(userG.NetworkRoles) > 0 {
for netID := range userG.NetworkRoles {
allNetworkRoles[netID] = struct{}{}
}
}
}
}
}
filteredNetworks := []models.Network{}
for _, networkI := range allnetworks {
if _, ok := allNetworkRoles[models.NetworkID(networkI.NetID)]; ok {
filteredNetworks = append(filteredNetworks, networkI)
}
}
allnetworks = filteredNetworks
}
return allnetworks
}
func IsGroupsValid(groups map[models.UserGroupID]struct{}) error {
uniqueGroupsPlatformRole := make(map[models.UserRole]struct{})
for groupID := range groups {
userG, err := GetUserGroup(groupID)
if err != nil {
return err
}
uniqueGroupsPlatformRole[userG.PlatformRole] = struct{}{}
}
if len(uniqueGroupsPlatformRole) > 1 {
return errors.New("only groups with same platform role can be assigned to an user")
}
return nil
}
func RemoveNetworkRoleFromUsers(host models.Host, node models.Node) {
users, err := logic.GetUsersDB()
if err == nil {
for _, user := range users {
// delete role from user
if netRoles, ok := user.NetworkRoles[models.NetworkID(node.Network)]; ok {
delete(netRoles, models.GetRAGRoleName(node.Network, host.Name))
user.NetworkRoles[models.NetworkID(node.Network)] = netRoles
err = logic.UpsertUser(user)
if err != nil {
slog.Error("failed to get user", "user", user.UserName, "error", err)
}
}
}
} else {
slog.Error("failed to get users", "error", err)
}
err = DeleteRole(models.GetRAGRoleName(node.Network, host.Name))
if err != nil {
slog.Error("failed to delete role: ", models.GetRAGRoleName(node.Network, host.Name), err)
}
}