Jelajahi Sumber

Merge pull request #3172 from gravitl/NET-1615

NET-1615: ACLS v2
Abhishek K 10 bulan lalu
induk
melakukan
2d3d5fefd0

+ 230 - 0
controllers/acls.go

@@ -0,0 +1,230 @@
+package controller
+
+import (
+	"encoding/json"
+	"errors"
+	"net/http"
+	"net/url"
+	"time"
+
+	"github.com/google/uuid"
+	"github.com/gorilla/mux"
+	"github.com/gravitl/netmaker/logger"
+	"github.com/gravitl/netmaker/logic"
+	"github.com/gravitl/netmaker/models"
+	"github.com/gravitl/netmaker/mq"
+)
+
+func aclHandlers(r *mux.Router) {
+	r.HandleFunc("/api/v1/acls", logic.SecurityCheck(true, http.HandlerFunc(getAcls))).
+		Methods(http.MethodGet)
+	r.HandleFunc("/api/v1/acls/policy_types", logic.SecurityCheck(true, http.HandlerFunc(aclPolicyTypes))).
+		Methods(http.MethodGet)
+	r.HandleFunc("/api/v1/acls", logic.SecurityCheck(true, http.HandlerFunc(createAcl))).
+		Methods(http.MethodPost)
+	r.HandleFunc("/api/v1/acls", logic.SecurityCheck(true, http.HandlerFunc(updateAcl))).
+		Methods(http.MethodPut)
+	r.HandleFunc("/api/v1/acls", logic.SecurityCheck(true, http.HandlerFunc(deleteAcl))).
+		Methods(http.MethodDelete)
+	r.HandleFunc("/api/v1/acls/debug", logic.SecurityCheck(true, http.HandlerFunc(aclDebug))).
+		Methods(http.MethodGet)
+}
+
+// @Summary     List Acl Policy types
+// @Router      /api/v1/acls/policy_types [get]
+// @Tags        ACL
+// @Accept      json
+// @Success     200 {array} models.SuccessResponse
+// @Failure     500 {object} models.ErrorResponse
+func aclPolicyTypes(w http.ResponseWriter, r *http.Request) {
+	resp := models.AclPolicyTypes{
+		RuleTypes: []models.AclPolicyType{
+			models.DevicePolicy,
+			models.UserPolicy,
+		},
+		SrcGroupTypes: []models.AclGroupType{
+			models.UserAclID,
+			models.UserGroupAclID,
+			models.DeviceAclID,
+		},
+		DstGroupTypes: []models.AclGroupType{
+			models.DeviceAclID,
+			// models.NetmakerIPAclID,
+			// models.NetmakerSubNetRangeAClID,
+		},
+	}
+	logic.ReturnSuccessResponseWithJson(w, r, resp, "fetched acls types")
+}
+
+func aclDebug(w http.ResponseWriter, r *http.Request) {
+	nodeID, _ := url.QueryUnescape(r.URL.Query().Get("node"))
+	peerID, _ := url.QueryUnescape(r.URL.Query().Get("peer"))
+	node, err := logic.GetNodeByID(nodeID)
+	if err != nil {
+		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
+		return
+	}
+	peer, err := logic.GetNodeByID(peerID)
+	if err != nil {
+		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
+		return
+	}
+	allowed := logic.IsNodeAllowedToCommunicate(node, peer)
+	logic.ReturnSuccessResponseWithJson(w, r, allowed, "fetched all acls in the network ")
+}
+
+// @Summary     List Acls in a network
+// @Router      /api/v1/acls [get]
+// @Tags        ACL
+// @Accept      json
+// @Success     200 {array} models.SuccessResponse
+// @Failure     500 {object} models.ErrorResponse
+func getAcls(w http.ResponseWriter, r *http.Request) {
+	netID := r.URL.Query().Get("network")
+	if netID == "" {
+		logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("network id param is missing"), "badrequest"))
+		return
+	}
+	// check if network exists
+	_, err := logic.GetNetwork(netID)
+	if err != nil {
+		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
+		return
+	}
+	acls, err := logic.ListAcls(models.NetworkID(netID))
+	if err != nil {
+		logger.Log(0, r.Header.Get("user"), "failed to get all network acl entries: ", err.Error())
+		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
+		return
+	}
+	logic.SortAclEntrys(acls[:])
+	logic.ReturnSuccessResponseWithJson(w, r, acls, "fetched all acls in the network "+netID)
+}
+
+// @Summary     Create Acl
+// @Router      /api/v1/acls [post]
+// @Tags        ACL
+// @Accept      json
+// @Success     200 {array} models.SuccessResponse
+// @Failure     500 {object} models.ErrorResponse
+func createAcl(w http.ResponseWriter, r *http.Request) {
+	var req models.Acl
+	err := json.NewDecoder(r.Body).Decode(&req)
+	if err != nil {
+		logger.Log(0, "error decoding request body: ",
+			err.Error())
+		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
+		return
+	}
+	user, err := logic.GetUser(r.Header.Get("user"))
+	if err != nil {
+		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
+		return
+	}
+	err = logic.ValidateCreateAclReq(req)
+	if err != nil {
+		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
+		return
+	}
+
+	acl := req
+	acl.ID = uuid.New().String()
+	acl.CreatedBy = user.UserName
+	acl.CreatedAt = time.Now().UTC()
+	acl.Default = false
+	if acl.RuleType == models.DevicePolicy {
+		acl.AllowedDirection = models.TrafficDirectionBi
+	} else {
+		acl.AllowedDirection = models.TrafficDirectionUni
+	}
+	// validate create acl policy
+	if !logic.IsAclPolicyValid(acl) {
+		logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("invalid policy"), "badrequest"))
+		return
+	}
+	err = logic.InsertAcl(acl)
+	if err != nil {
+		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
+		return
+	}
+	acl, err = logic.GetAcl(acl.ID)
+	if err != nil {
+		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
+		return
+	}
+	go mq.PublishPeerUpdate(false)
+	logic.ReturnSuccessResponseWithJson(w, r, acl, "created acl successfully")
+}
+
+// @Summary     Update Acl
+// @Router      /api/v1/acls [put]
+// @Tags        ACL
+// @Accept      json
+// @Success     200 {array} models.SuccessResponse
+// @Failure     500 {object} models.ErrorResponse
+func updateAcl(w http.ResponseWriter, r *http.Request) {
+	var updateAcl models.UpdateAclRequest
+	err := json.NewDecoder(r.Body).Decode(&updateAcl)
+	if err != nil {
+		logger.Log(0, "error decoding request body: ",
+			err.Error())
+		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
+		return
+	}
+
+	acl, err := logic.GetAcl(updateAcl.ID)
+	if err != nil {
+		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
+		return
+	}
+	if !logic.IsAclPolicyValid(updateAcl.Acl) {
+		logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("invalid policy"), "badrequest"))
+		return
+	}
+	if updateAcl.Acl.NetworkID != acl.NetworkID {
+		logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("invalid policy, network id mismatch"), "badrequest"))
+		return
+	}
+	if !acl.Default && updateAcl.NewName != "" {
+		//check if policy exists with same name
+		updateAcl.Acl.Name = updateAcl.NewName
+	}
+	err = logic.UpdateAcl(updateAcl.Acl, acl)
+	if err != nil {
+		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
+		return
+	}
+	go mq.PublishPeerUpdate(false)
+	logic.ReturnSuccessResponse(w, r, "updated acl "+acl.Name)
+}
+
+// @Summary     Delete Acl
+// @Router      /api/v1/acls [delete]
+// @Tags        ACL
+// @Accept      json
+// @Success     200 {array} models.SuccessResponse
+// @Failure     500 {object} models.ErrorResponse
+func deleteAcl(w http.ResponseWriter, r *http.Request) {
+	aclID, _ := url.QueryUnescape(r.URL.Query().Get("acl_id"))
+	if aclID == "" {
+		logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("acl id is required"), "badrequest"))
+		return
+	}
+	acl, err := logic.GetAcl(aclID)
+	if err != nil {
+		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
+		return
+	}
+	if acl.Default {
+		logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("cannot delete default policy"), "badrequest"))
+		return
+	}
+	err = logic.DeleteAcl(acl)
+	if err != nil {
+		logic.ReturnErrorResponse(w, r,
+			logic.FormatError(errors.New("cannot delete default policy"), "internal"))
+		return
+	}
+	go mq.PublishPeerUpdate(false)
+	logic.ReturnSuccessResponse(w, r, "deleted acl "+acl.Name)
+}

+ 1 - 0
controllers/controller.go

@@ -35,6 +35,7 @@ var HttpHandlers = []interface{}{
 	hostHandlers,
 	enrollmentKeyHandlers,
 	tagHandlers,
+	aclHandlers,
 	legacyHandlers,
 }
 

+ 2 - 1
controllers/enrollmentkeys.go

@@ -72,7 +72,7 @@ func getEnrollmentKeys(w http.ResponseWriter, r *http.Request) {
 func deleteEnrollmentKey(w http.ResponseWriter, r *http.Request) {
 	params := mux.Vars(r)
 	keyID := params["keyID"]
-	err := logic.DeleteEnrollmentKey(keyID)
+	err := logic.DeleteEnrollmentKey(keyID, false)
 	if err != nil {
 		logger.Log(0, r.Header.Get("user"), "failed to remove enrollment key: ", err.Error())
 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
@@ -159,6 +159,7 @@ func createEnrollmentKey(w http.ResponseWriter, r *http.Request) {
 		enrollmentKeyBody.Groups,
 		enrollmentKeyBody.Unlimited,
 		relayId,
+		false,
 	)
 	if err != nil {
 		logger.Log(0, r.Header.Get("user"), "failed to create enrollment key:", err.Error())

+ 4 - 3
controllers/ext_client.go

@@ -468,13 +468,14 @@ func createExtClient(w http.ResponseWriter, r *http.Request) {
 	extclient.OwnerID = userName
 	extclient.RemoteAccessClientID = customExtClient.RemoteAccessClientID
 	extclient.IngressGatewayID = nodeid
-
+	extclient.Network = node.Network
+	extclient.Tags = make(map[models.TagID]struct{})
+	extclient.Tags[models.TagID(fmt.Sprintf("%s.%s", extclient.Network,
+		models.RemoteAccessTagName))] = struct{}{}
 	// set extclient dns to ingressdns if extclient dns is not explicitly set
 	if (extclient.DNS == "") && (node.IngressDNS != "") {
 		extclient.DNS = node.IngressDNS
 	}
-
-	extclient.Network = node.Network
 	host, err := logic.GetHost(node.HostID.String())
 	if err != nil {
 		logger.Log(0, r.Header.Get("user"),

+ 0 - 13
controllers/hosts.go

@@ -253,19 +253,6 @@ func updateHost(w http.ResponseWriter, r *http.Request) {
 
 	newHost := newHostData.ConvertAPIHostToNMHost(currHost)
 
-	if newHost.Name != currHost.Name {
-		// update any rag role ids
-		for _, nodeID := range newHost.Nodes {
-			node, err := logic.GetNodeByID(nodeID)
-			if err == nil && node.IsIngressGateway {
-				role, err := logic.GetRole(models.GetRAGRoleID(node.Network, currHost.ID.String()))
-				if err == nil {
-					role.UiName = models.GetRAGRoleName(node.Network, newHost.Name)
-					logic.UpdateRole(role)
-				}
-			}
-		}
-	}
 	logic.UpdateHost(newHost, currHost) // update the in memory struct values
 	if err = logic.UpsertHost(newHost); err != nil {
 		logger.Log(0, r.Header.Get("user"), "failed to update a host:", err.Error())

+ 6 - 1
controllers/middleware.go

@@ -29,6 +29,9 @@ func userMiddleWare(handler http.Handler) http.Handler {
 		r.Header.Set("TARGET_RSRC_ID", "")
 		r.Header.Set("RAC", "")
 		r.Header.Set("NET_ID", params["network"])
+		if r.URL.Query().Get("network") != "" {
+			r.Header.Set("NET_ID", r.URL.Query().Get("network"))
+		}
 		if strings.Contains(route, "hosts") || strings.Contains(route, "nodes") {
 			r.Header.Set("TARGET_RSRC", models.HostRsrc.String())
 		}
@@ -57,6 +60,9 @@ func userMiddleWare(handler http.Handler) http.Handler {
 		if strings.Contains(route, "acls") {
 			r.Header.Set("TARGET_RSRC", models.AclRsrc.String())
 		}
+		if strings.Contains(route, "tags") {
+			r.Header.Set("TARGET_RSRC", models.TagRsrc.String())
+		}
 		if strings.Contains(route, "extclients") {
 			r.Header.Set("TARGET_RSRC", models.ExtClientsRsrc.String())
 		}
@@ -105,7 +111,6 @@ func userMiddleWare(handler http.Handler) http.Handler {
 			r.Header.Get("TARGET_RSRC") == models.UserRsrc.String()) {
 			r.Header.Set("IS_GLOBAL_ACCESS", "yes")
 		}
-
 		r.Header.Set("RSRC_TYPE", r.Header.Get("TARGET_RSRC"))
 		handler.ServeHTTP(w, r)
 	})

+ 2 - 1
controllers/network.go

@@ -456,6 +456,7 @@ func deleteNetwork(w http.ResponseWriter, r *http.Request) {
 		return
 	}
 	go logic.DeleteNetworkRoles(network)
+	go logic.DeleteDefaultNetworkPolicies(models.NetworkID(network))
 	//delete network from allocated ip map
 	go logic.RemoveNetworkFromAllocatedIpMap(network)
 
@@ -530,8 +531,8 @@ func createNetwork(w http.ResponseWriter, r *http.Request) {
 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
 		return
 	}
-
 	logic.CreateDefaultNetworkRolesAndGroups(models.NetworkID(network.NetID))
+	logic.CreateDefaultAclNetworkPolicies(models.NetworkID(network.NetID))
 	logic.CreateDefaultTags(models.NetworkID(network.NetID))
 	//add new network to allocated ip map
 	go logic.AddNetworkToAllocatedIpMap(network.NetID)

+ 3 - 0
controllers/node.go

@@ -590,6 +590,7 @@ func createIngressGateway(w http.ResponseWriter, r *http.Request) {
 		if err := mq.NodeUpdate(&node); err != nil {
 			slog.Error("error publishing node update to node", "node", node.ID, "error", err)
 		}
+		mq.PublishPeerUpdate(false)
 	}()
 }
 
@@ -634,6 +635,7 @@ func deleteIngressGateway(w http.ResponseWriter, r *http.Request) {
 				if err := mq.PublishSingleHostPeerUpdate(host, allNodes, nil, removedClients[:], false, nil); err != nil {
 					slog.Error("publishSingleHostUpdate", "host", host.Name, "error", err)
 				}
+				mq.PublishPeerUpdate(false)
 				if err := mq.NodeUpdate(&node); err != nil {
 					slog.Error(
 						"error publishing node update to node",
@@ -749,6 +751,7 @@ func updateNode(w http.ResponseWriter, r *http.Request) {
 				logger.Log(0, "error during node ACL update for node", newNode.ID.String())
 			}
 		}
+		mq.PublishPeerUpdate(false)
 		if servercfg.IsDNSMode() {
 			logic.SetDNS()
 		}

+ 22 - 3
controllers/tags.go

@@ -13,6 +13,7 @@ import (
 	"github.com/gravitl/netmaker/logger"
 	"github.com/gravitl/netmaker/logic"
 	"github.com/gravitl/netmaker/models"
+	"github.com/gravitl/netmaker/mq"
 )
 
 func tagHandlers(r *mux.Router) {
@@ -129,6 +130,7 @@ func createTag(w http.ResponseWriter, r *http.Request) {
 			logic.UpsertNode(&node)
 		}
 	}()
+	go mq.PublishPeerUpdate(false)
 
 	var res models.TagListRespNodes = models.TagListRespNodes{
 		Tag:         tag,
@@ -178,9 +180,15 @@ func updateTag(w http.ResponseWriter, r *http.Request) {
 			return
 		}
 		// delete old Tag entry
-		logic.DeleteTag(updateTag.ID)
+		logic.DeleteTag(updateTag.ID, false)
 	}
-	go logic.UpdateTag(updateTag, newID)
+	go func() {
+		logic.UpdateTag(updateTag, newID)
+		if updateTag.NewName != "" {
+			logic.UpdateDeviceTag(updateTag.ID, newID, tag.Network)
+		}
+		mq.PublishPeerUpdate(false)
+	}()
 
 	var res models.TagListRespNodes = models.TagListRespNodes{
 		Tag:         tag,
@@ -203,10 +211,21 @@ func deleteTag(w http.ResponseWriter, r *http.Request) {
 		logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("role is required"), "badrequest"))
 		return
 	}
-	err := logic.DeleteTag(models.TagID(tagID))
+	tag, err := logic.GetTag(models.TagID(tagID))
 	if err != nil {
 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
 		return
 	}
+	err = logic.DeleteTag(models.TagID(tagID), true)
+	if err != nil {
+		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
+		return
+	}
+
+	go func() {
+		logic.RemoveDeviceTagFromAclPolicies(tag.ID, tag.Network)
+		logic.RemoveTagFromEnrollmentKeys(tag.ID)
+		mq.PublishPeerUpdate(false)
+	}()
 	logic.ReturnSuccessResponse(w, r, "deleted tag "+tagID)
 }

+ 3 - 0
controllers/user.go

@@ -451,6 +451,7 @@ func createUser(w http.ResponseWriter, r *http.Request) {
 	}
 	logic.DeleteUserInvite(user.UserName)
 	logic.DeletePendingUser(user.UserName)
+	go mq.PublishPeerUpdate(false)
 	slog.Info("user was created", "username", user.UserName)
 	json.NewEncoder(w).Encode(logic.ToReturnUser(user))
 }
@@ -590,6 +591,7 @@ func updateUser(w http.ResponseWriter, r *http.Request) {
 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
 		return
 	}
+	go mq.PublishPeerUpdate(false)
 	logger.Log(1, username, "was updated")
 	json.NewEncoder(w).Encode(logic.ToReturnUser(*user))
 }
@@ -692,6 +694,7 @@ func deleteUser(w http.ResponseWriter, r *http.Request) {
 				}
 			}
 		}
+		mq.PublishPeerUpdate(false)
 		if servercfg.IsDNSMode() {
 			logic.SetDNS()
 		}

+ 3 - 0
database/database.go

@@ -47,6 +47,8 @@ const (
 	GENERATED_TABLE_NAME = "generated"
 	// NODE_ACLS_TABLE_NAME - stores the node ACL rules
 	NODE_ACLS_TABLE_NAME = "nodeacls"
+	// ACLS_TABLE_NAME - table for acls v2
+	ACLS_TABLE_NAME = "acls"
 	// SSO_STATE_CACHE - holds sso session information for OAuth2 sign-ins
 	SSO_STATE_CACHE = "ssostatecache"
 	// METRICS_TABLE_NAME - stores network metrics
@@ -155,6 +157,7 @@ func createTables() {
 	CreateTable(USER_PERMISSIONS_TABLE_NAME)
 	CreateTable(USER_INVITES_TABLE_NAME)
 	CreateTable(TAG_TABLE_NAME)
+	CreateTable(ACLS_TABLE_NAME)
 }
 
 func CreateTable(tableName string) error {

+ 596 - 0
logic/acls.go

@@ -0,0 +1,596 @@
+package logic
+
+import (
+	"encoding/json"
+	"errors"
+	"fmt"
+	"sort"
+	"time"
+
+	"github.com/gravitl/netmaker/database"
+	"github.com/gravitl/netmaker/models"
+)
+
+// CreateDefaultAclNetworkPolicies - create default acl network policies
+func CreateDefaultAclNetworkPolicies(netID models.NetworkID) {
+	if netID.String() == "" {
+		return
+	}
+	if !IsAclExists(fmt.Sprintf("%s.%s", netID, "all-nodes")) {
+		defaultDeviceAcl := models.Acl{
+			ID:        fmt.Sprintf("%s.%s", netID, "all-nodes"),
+			Name:      "All Nodes",
+			MetaData:  "This Policy allows all nodes in the network to communicate with each other",
+			Default:   true,
+			NetworkID: netID,
+			RuleType:  models.DevicePolicy,
+			Src: []models.AclPolicyTag{
+				{
+					ID:    models.DeviceAclID,
+					Value: "*",
+				}},
+			Dst: []models.AclPolicyTag{
+				{
+					ID:    models.DeviceAclID,
+					Value: "*",
+				}},
+			AllowedDirection: models.TrafficDirectionBi,
+			Enabled:          true,
+			CreatedBy:        "auto",
+			CreatedAt:        time.Now().UTC(),
+		}
+		InsertAcl(defaultDeviceAcl)
+	}
+	if !IsAclExists(fmt.Sprintf("%s.%s", netID, "all-users")) {
+		defaultUserAcl := models.Acl{
+			ID:        fmt.Sprintf("%s.%s", netID, "all-users"),
+			Default:   true,
+			Name:      "All Users",
+			MetaData:  "This policy gives access to everything in the network for an user",
+			NetworkID: netID,
+			RuleType:  models.UserPolicy,
+			Src: []models.AclPolicyTag{
+				{
+					ID:    models.UserAclID,
+					Value: "*",
+				},
+			},
+			Dst: []models.AclPolicyTag{{
+				ID:    models.DeviceAclID,
+				Value: "*",
+			}},
+			AllowedDirection: models.TrafficDirectionUni,
+			Enabled:          true,
+			CreatedBy:        "auto",
+			CreatedAt:        time.Now().UTC(),
+		}
+		InsertAcl(defaultUserAcl)
+	}
+
+	if !IsAclExists(fmt.Sprintf("%s.%s", netID, "all-remote-access-gws")) {
+		defaultUserAcl := models.Acl{
+			ID:        fmt.Sprintf("%s.%s", netID, "all-remote-access-gws"),
+			Default:   true,
+			Name:      "All Remote Access Gateways",
+			NetworkID: netID,
+			RuleType:  models.DevicePolicy,
+			Src: []models.AclPolicyTag{
+				{
+					ID:    models.DeviceAclID,
+					Value: fmt.Sprintf("%s.%s", netID, models.RemoteAccessTagName),
+				},
+			},
+			Dst: []models.AclPolicyTag{
+				{
+					ID:    models.DeviceAclID,
+					Value: "*",
+				},
+			},
+			AllowedDirection: models.TrafficDirectionBi,
+			Enabled:          true,
+			CreatedBy:        "auto",
+			CreatedAt:        time.Now().UTC(),
+		}
+		InsertAcl(defaultUserAcl)
+	}
+	CreateDefaultUserPolicies(netID)
+}
+
+// DeleteDefaultNetworkPolicies - deletes all default network acl policies
+func DeleteDefaultNetworkPolicies(netId models.NetworkID) {
+	acls, _ := ListAcls(netId)
+	for _, acl := range acls {
+		if acl.NetworkID == netId && acl.Default {
+			DeleteAcl(acl)
+		}
+	}
+}
+
+// ValidateCreateAclReq - validates create req for acl
+func ValidateCreateAclReq(req models.Acl) error {
+	// check if acl network exists
+	_, err := GetNetwork(req.NetworkID.String())
+	if err != nil {
+		return errors.New("failed to get network details for " + req.NetworkID.String())
+	}
+	// err = CheckIDSyntax(req.Name)
+	// if err != nil {
+	// 	return err
+	// }
+	return nil
+}
+
+// InsertAcl - creates acl policy
+func InsertAcl(a models.Acl) error {
+	d, err := json.Marshal(a)
+	if err != nil {
+		return err
+	}
+	return database.Insert(a.ID, string(d), database.ACLS_TABLE_NAME)
+}
+
+// GetAcl - gets acl info by id
+func GetAcl(aID string) (models.Acl, error) {
+	a := models.Acl{}
+	d, err := database.FetchRecord(database.ACLS_TABLE_NAME, aID)
+	if err != nil {
+		return a, err
+	}
+	err = json.Unmarshal([]byte(d), &a)
+	if err != nil {
+		return a, err
+	}
+	return a, nil
+}
+
+// IsAclExists - checks if acl exists
+func IsAclExists(aclID string) bool {
+	_, err := GetAcl(aclID)
+	return err == nil
+}
+
+// IsAclPolicyValid - validates if acl policy is valid
+func IsAclPolicyValid(acl models.Acl) bool {
+	//check if src and dst are valid
+
+	switch acl.RuleType {
+	case models.UserPolicy:
+		// src list should only contain users
+		for _, srcI := range acl.Src {
+
+			if srcI.ID == "" || srcI.Value == "" {
+				return false
+			}
+			if srcI.Value == "*" {
+				continue
+			}
+			if srcI.ID != models.UserAclID && srcI.ID != models.UserGroupAclID {
+				return false
+			}
+			// check if user group is valid
+			if srcI.ID == models.UserAclID {
+				_, err := GetUser(srcI.Value)
+				if err != nil {
+					return false
+				}
+
+			} else if srcI.ID == models.UserGroupAclID {
+				err := IsGroupValid(models.UserGroupID(srcI.Value))
+				if err != nil {
+					return false
+				}
+				// check if group belongs to this network
+				netGrps := GetUserGroupsInNetwork(acl.NetworkID)
+				if _, ok := netGrps[models.UserGroupID(srcI.Value)]; !ok {
+					return false
+				}
+			}
+
+		}
+		for _, dstI := range acl.Dst {
+
+			if dstI.ID == "" || dstI.Value == "" {
+				return false
+			}
+			if dstI.ID != models.DeviceAclID {
+				return false
+			}
+			if dstI.Value == "*" {
+				continue
+			}
+			// check if tag is valid
+			_, err := GetTag(models.TagID(dstI.Value))
+			if err != nil {
+				return false
+			}
+		}
+	case models.DevicePolicy:
+		for _, srcI := range acl.Src {
+			if srcI.ID == "" || srcI.Value == "" {
+				return false
+			}
+			if srcI.ID != models.DeviceAclID {
+				return false
+			}
+			if srcI.Value == "*" {
+				continue
+			}
+			// check if tag is valid
+			_, err := GetTag(models.TagID(srcI.Value))
+			if err != nil {
+				return false
+			}
+		}
+		for _, dstI := range acl.Dst {
+
+			if dstI.ID == "" || dstI.Value == "" {
+				return false
+			}
+			if dstI.ID != models.DeviceAclID {
+				return false
+			}
+			if dstI.Value == "*" {
+				continue
+			}
+			// check if tag is valid
+			_, err := GetTag(models.TagID(dstI.Value))
+			if err != nil {
+				return false
+			}
+		}
+	}
+	return true
+}
+
+// UpdateAcl - updates allowed fields on acls and commits to DB
+func UpdateAcl(newAcl, acl models.Acl) error {
+	if !acl.Default {
+		acl.Name = newAcl.Name
+		acl.Src = newAcl.Src
+		acl.Dst = newAcl.Dst
+	}
+	acl.Enabled = newAcl.Enabled
+	d, err := json.Marshal(acl)
+	if err != nil {
+		return err
+	}
+	return database.Insert(acl.ID, string(d), database.ACLS_TABLE_NAME)
+}
+
+// UpsertAcl - upserts acl
+func UpsertAcl(acl models.Acl) error {
+	d, err := json.Marshal(acl)
+	if err != nil {
+		return err
+	}
+	return database.Insert(acl.ID, string(d), database.ACLS_TABLE_NAME)
+}
+
+// DeleteAcl - deletes acl policy
+func DeleteAcl(a models.Acl) error {
+	return database.DeleteRecord(database.ACLS_TABLE_NAME, a.ID)
+}
+
+// GetDefaultPolicy - fetches default policy in the network by ruleType
+func GetDefaultPolicy(netID models.NetworkID, ruleType models.AclPolicyType) (models.Acl, error) {
+	aclID := "all-users"
+	if ruleType == models.DevicePolicy {
+		aclID = "all-nodes"
+	}
+	acl, err := GetAcl(fmt.Sprintf("%s.%s", netID, aclID))
+	if err != nil {
+		return models.Acl{}, errors.New("default rule not found")
+	}
+	if acl.Enabled {
+		return acl, nil
+	}
+	// check if there are any custom all policies
+	policies, _ := ListAcls(netID)
+	for _, policy := range policies {
+		if !policy.Enabled {
+			continue
+		}
+		if policy.RuleType == ruleType {
+			dstMap := convAclTagToValueMap(policy.Dst)
+			srcMap := convAclTagToValueMap(policy.Dst)
+			if _, ok := srcMap["*"]; ok {
+				if _, ok := dstMap["*"]; ok {
+					return policy, nil
+				}
+			}
+		}
+
+	}
+
+	return acl, nil
+}
+
+// ListUserPolicies - lists all acl policies enforced on an user
+func ListUserPolicies(u models.User) []models.Acl {
+	data, err := database.FetchRecords(database.ACLS_TABLE_NAME)
+	if err != nil && !database.IsEmptyRecord(err) {
+		return []models.Acl{}
+	}
+	acls := []models.Acl{}
+	for _, dataI := range data {
+		acl := models.Acl{}
+		err := json.Unmarshal([]byte(dataI), &acl)
+		if err != nil {
+			continue
+		}
+
+		if acl.RuleType == models.UserPolicy {
+			srcMap := convAclTagToValueMap(acl.Src)
+			if _, ok := srcMap[u.UserName]; ok {
+				acls = append(acls, acl)
+			} else {
+				// check for user groups
+				for gID := range u.UserGroups {
+					if _, ok := srcMap[gID.String()]; ok {
+						acls = append(acls, acl)
+						break
+					}
+				}
+			}
+
+		}
+	}
+	return acls
+}
+
+// listPoliciesOfUser - lists all user acl policies applied to user in an network
+func listPoliciesOfUser(user models.User, netID models.NetworkID) []models.Acl {
+	data, err := database.FetchRecords(database.ACLS_TABLE_NAME)
+	if err != nil && !database.IsEmptyRecord(err) {
+		return []models.Acl{}
+	}
+	acls := []models.Acl{}
+	for _, dataI := range data {
+		acl := models.Acl{}
+		err := json.Unmarshal([]byte(dataI), &acl)
+		if err != nil {
+			continue
+		}
+		if acl.NetworkID == netID && acl.RuleType == models.UserPolicy {
+			srcMap := convAclTagToValueMap(acl.Src)
+			if _, ok := srcMap[user.UserName]; ok {
+				acls = append(acls, acl)
+				continue
+			}
+			for netRole := range user.NetworkRoles {
+				if _, ok := srcMap[netRole.String()]; ok {
+					acls = append(acls, acl)
+					continue
+				}
+			}
+			for userG := range user.UserGroups {
+				if _, ok := srcMap[userG.String()]; ok {
+					acls = append(acls, acl)
+					continue
+				}
+			}
+
+		}
+	}
+	return acls
+}
+
+// listDevicePolicies - lists all device policies in a network
+func listDevicePolicies(netID models.NetworkID) []models.Acl {
+	data, err := database.FetchRecords(database.ACLS_TABLE_NAME)
+	if err != nil && !database.IsEmptyRecord(err) {
+		return []models.Acl{}
+	}
+	acls := []models.Acl{}
+	for _, dataI := range data {
+		acl := models.Acl{}
+		err := json.Unmarshal([]byte(dataI), &acl)
+		if err != nil {
+			continue
+		}
+		if acl.NetworkID == netID && acl.RuleType == models.DevicePolicy {
+			acls = append(acls, acl)
+		}
+	}
+	return acls
+}
+
+// ListAcls - lists all acl policies
+func ListAcls(netID models.NetworkID) ([]models.Acl, error) {
+	data, err := database.FetchRecords(database.ACLS_TABLE_NAME)
+	if err != nil && !database.IsEmptyRecord(err) {
+		return []models.Acl{}, err
+	}
+	acls := []models.Acl{}
+	for _, dataI := range data {
+		acl := models.Acl{}
+		err := json.Unmarshal([]byte(dataI), &acl)
+		if err != nil {
+			continue
+		}
+		if acl.NetworkID == netID {
+			acls = append(acls, acl)
+		}
+	}
+	return acls, nil
+}
+
+func convAclTagToValueMap(acltags []models.AclPolicyTag) map[string]struct{} {
+	aclValueMap := make(map[string]struct{})
+	for _, aclTagI := range acltags {
+		aclValueMap[aclTagI.Value] = struct{}{}
+	}
+	return aclValueMap
+}
+
+// IsUserAllowedToCommunicate - check if user is allowed to communicate with peer
+func IsUserAllowedToCommunicate(userName string, peer models.Node) bool {
+	if peer.IsStatic {
+		peer = peer.StaticNode.ConvertToStaticNode()
+	}
+	acl, _ := GetDefaultPolicy(models.NetworkID(peer.Network), models.UserPolicy)
+	if acl.Enabled {
+		return true
+	}
+	user, err := GetUser(userName)
+	if err != nil {
+		return false
+	}
+
+	policies := listPoliciesOfUser(*user, models.NetworkID(peer.Network))
+	for _, policy := range policies {
+		if !policy.Enabled {
+			continue
+		}
+		dstMap := convAclTagToValueMap(policy.Dst)
+		if _, ok := dstMap["*"]; ok {
+			return true
+		}
+		for tagID := range peer.Tags {
+			if _, ok := dstMap[tagID.String()]; ok {
+				return true
+			}
+		}
+
+	}
+	return false
+}
+
+// IsNodeAllowedToCommunicate - check node is allowed to communicate with the peer
+func IsNodeAllowedToCommunicate(node, peer models.Node) bool {
+	if node.IsStatic {
+		node = node.StaticNode.ConvertToStaticNode()
+	}
+	if peer.IsStatic {
+		peer = peer.StaticNode.ConvertToStaticNode()
+	}
+	// check default policy if all allowed return true
+	defaultPolicy, err := GetDefaultPolicy(models.NetworkID(node.Network), models.DevicePolicy)
+	if err == nil {
+		if defaultPolicy.Enabled {
+			return true
+		}
+	}
+
+	// list device policies
+	policies := listDevicePolicies(models.NetworkID(peer.Network))
+	for _, policy := range policies {
+		if !policy.Enabled {
+			continue
+		}
+		srcMap := convAclTagToValueMap(policy.Src)
+		dstMap := convAclTagToValueMap(policy.Dst)
+		// fmt.Printf("\n======> SRCMAP: %+v\n", srcMap)
+		// fmt.Printf("\n======> DSTMAP: %+v\n", dstMap)
+		// fmt.Printf("\n======> node Tags: %+v\n", node.Tags)
+		// fmt.Printf("\n======> peer Tags: %+v\n", peer.Tags)
+		for tagID := range node.Tags {
+			if _, ok := dstMap[tagID.String()]; ok {
+				if _, ok := srcMap["*"]; ok {
+					return true
+				}
+				for tagID := range peer.Tags {
+					if _, ok := srcMap[tagID.String()]; ok {
+						return true
+					}
+				}
+			}
+			if _, ok := srcMap[tagID.String()]; ok {
+				if _, ok := dstMap["*"]; ok {
+					return true
+				}
+				for tagID := range peer.Tags {
+					if _, ok := dstMap[tagID.String()]; ok {
+						return true
+					}
+				}
+			}
+		}
+		for tagID := range peer.Tags {
+			if _, ok := dstMap[tagID.String()]; ok {
+				if _, ok := srcMap["*"]; ok {
+					return true
+				}
+				for tagID := range node.Tags {
+
+					if _, ok := srcMap[tagID.String()]; ok {
+						return true
+					}
+				}
+			}
+			if _, ok := srcMap[tagID.String()]; ok {
+				if _, ok := dstMap["*"]; ok {
+					return true
+				}
+				for tagID := range node.Tags {
+					if _, ok := dstMap[tagID.String()]; ok {
+						return true
+					}
+				}
+			}
+		}
+	}
+	return false
+}
+
+// SortTagEntrys - Sorts slice of Tag entries by their id
+func SortAclEntrys(acls []models.Acl) {
+	sort.Slice(acls, func(i, j int) bool {
+		return acls[i].Name < acls[j].Name
+	})
+}
+
+// UpdateDeviceTag - updates device tag on acl policies
+func UpdateDeviceTag(OldID, newID models.TagID, netID models.NetworkID) {
+	acls := listDevicePolicies(netID)
+	update := false
+	for _, acl := range acls {
+		for i, srcTagI := range acl.Src {
+			if srcTagI.ID == models.DeviceAclID {
+				if OldID.String() == srcTagI.Value {
+					acl.Src[i].Value = newID.String()
+					update = true
+				}
+			}
+		}
+		for i, dstTagI := range acl.Dst {
+			if dstTagI.ID == models.DeviceAclID {
+				if OldID.String() == dstTagI.Value {
+					acl.Dst[i].Value = newID.String()
+					update = true
+				}
+			}
+		}
+		if update {
+			UpsertAcl(acl)
+		}
+	}
+}
+
+// RemoveDeviceTagFromAclPolicies - remove device tag from acl policies
+func RemoveDeviceTagFromAclPolicies(tagID models.TagID, netID models.NetworkID) error {
+	acls := listDevicePolicies(netID)
+	update := false
+	for _, acl := range acls {
+		for i, srcTagI := range acl.Src {
+			if srcTagI.ID == models.DeviceAclID {
+				if tagID.String() == srcTagI.Value {
+					acl.Src = append(acl.Src[:i], acl.Src[i+1:]...)
+					update = true
+				}
+			}
+		}
+		for i, dstTagI := range acl.Dst {
+			if dstTagI.ID == models.DeviceAclID {
+				if tagID.String() == dstTagI.Value {
+					acl.Dst = append(acl.Dst[:i], acl.Dst[i+1:]...)
+					update = true
+				}
+			}
+		}
+		if update {
+			UpsertAcl(acl)
+		}
+	}
+	return nil
+}

+ 1 - 1
logic/auth.go

@@ -186,7 +186,7 @@ func CreateUser(user *models.User) error {
 		logger.Log(0, "failed to insert user", err.Error())
 		return err
 	}
-
+	AddGlobalNetRolesToAdmins(*user)
 	return nil
 }
 

+ 1 - 1
logic/clients.go

@@ -32,7 +32,7 @@ var (
 			slog.Error("failed to get network acls", "error", err)
 			return err
 		}
-		networkAcls[acls.AclID(ec.ClientID)] = acls.ACL{}
+		networkAcls[acls.AclID(ec.ClientID)] = make(acls.ACL)
 		for objId := range networkAcls {
 			networkAcls[objId][acls.AclID(ec.ClientID)] = acls.Allowed
 			networkAcls[acls.AclID(ec.ClientID)][objId] = acls.Allowed

+ 26 - 3
logic/enrollmentkey.go

@@ -37,7 +37,7 @@ var (
 )
 
 // CreateEnrollmentKey - creates a new enrollment key in db
-func CreateEnrollmentKey(uses int, expiration time.Time, networks, tags []string, groups []models.TagID, unlimited bool, relay uuid.UUID) (*models.EnrollmentKey, error) {
+func CreateEnrollmentKey(uses int, expiration time.Time, networks, tags []string, groups []models.TagID, unlimited bool, relay uuid.UUID, defaultKey bool) (*models.EnrollmentKey, error) {
 	newKeyID, err := getUniqueEnrollmentID()
 	if err != nil {
 		return nil, err
@@ -152,11 +152,14 @@ func deleteEnrollmentkeyFromCache(key string) {
 }
 
 // DeleteEnrollmentKey - delete's a given enrollment key by value
-func DeleteEnrollmentKey(value string) error {
-	_, err := GetEnrollmentKey(value)
+func DeleteEnrollmentKey(value string, force bool) error {
+	key, err := GetEnrollmentKey(value)
 	if err != nil {
 		return err
 	}
+	if key.Default && !force {
+		return errors.New("cannot delete default network key")
+	}
 	err = database.DeleteRecord(database.ENROLLMENT_KEYS_TABLE_NAME, value)
 	if err == nil {
 		if servercfg.CacheEnabled() {
@@ -311,3 +314,23 @@ func getEnrollmentKeysMap() (map[string]models.EnrollmentKey, error) {
 	}
 	return currentKeys, nil
 }
+
+func RemoveTagFromEnrollmentKeys(deletedTagID models.TagID) {
+	keys, _ := GetAllEnrollmentKeys()
+	for _, key := range keys {
+		newTags := []models.TagID{}
+		update := false
+		for _, tagID := range key.Groups {
+			if tagID == deletedTagID {
+				update = true
+				continue
+			}
+			newTags = append(newTags, tagID)
+		}
+		if update {
+			key.Groups = newTags
+			upsertEnrollmentKey(&key)
+		}
+
+	}
+}

+ 15 - 15
logic/enrollmentkey_test.go

@@ -14,35 +14,35 @@ func TestCreateEnrollmentKey(t *testing.T) {
 	database.InitializeDatabase()
 	defer database.CloseDB()
 	t.Run("Can_Not_Create_Key", func(t *testing.T) {
-		newKey, err := CreateEnrollmentKey(0, time.Time{}, nil, nil, nil, false, uuid.Nil)
+		newKey, err := CreateEnrollmentKey(0, time.Time{}, nil, nil, nil, false, uuid.Nil, false)
 		assert.Nil(t, newKey)
 		assert.NotNil(t, err)
 		assert.ErrorIs(t, err, models.ErrInvalidEnrollmentKey)
 	})
 	t.Run("Can_Create_Key_Uses", func(t *testing.T) {
-		newKey, err := CreateEnrollmentKey(1, time.Time{}, nil, nil, nil, false, uuid.Nil)
+		newKey, err := CreateEnrollmentKey(1, time.Time{}, nil, nil, nil, false, uuid.Nil, false)
 		assert.Nil(t, err)
 		assert.Equal(t, 1, newKey.UsesRemaining)
 		assert.True(t, newKey.IsValid())
 	})
 	t.Run("Can_Create_Key_Time", func(t *testing.T) {
-		newKey, err := CreateEnrollmentKey(0, time.Now().Add(time.Minute), nil, nil, nil, false, uuid.Nil)
+		newKey, err := CreateEnrollmentKey(0, time.Now().Add(time.Minute), nil, nil, nil, false, uuid.Nil, false)
 		assert.Nil(t, err)
 		assert.True(t, newKey.IsValid())
 	})
 	t.Run("Can_Create_Key_Unlimited", func(t *testing.T) {
-		newKey, err := CreateEnrollmentKey(0, time.Time{}, nil, nil, nil, true, uuid.Nil)
+		newKey, err := CreateEnrollmentKey(0, time.Time{}, nil, nil, nil, true, uuid.Nil, false)
 		assert.Nil(t, err)
 		assert.True(t, newKey.IsValid())
 	})
 	t.Run("Can_Create_Key_WithNetworks", func(t *testing.T) {
-		newKey, err := CreateEnrollmentKey(0, time.Time{}, []string{"mynet", "skynet"}, nil, nil, true, uuid.Nil)
+		newKey, err := CreateEnrollmentKey(0, time.Time{}, []string{"mynet", "skynet"}, nil, nil, true, uuid.Nil, false)
 		assert.Nil(t, err)
 		assert.True(t, newKey.IsValid())
 		assert.True(t, len(newKey.Networks) == 2)
 	})
 	t.Run("Can_Create_Key_WithTags", func(t *testing.T) {
-		newKey, err := CreateEnrollmentKey(0, time.Time{}, nil, []string{"tag1", "tag2"}, nil, true, uuid.Nil)
+		newKey, err := CreateEnrollmentKey(0, time.Time{}, nil, []string{"tag1", "tag2"}, nil, true, uuid.Nil, false)
 		assert.Nil(t, err)
 		assert.True(t, newKey.IsValid())
 		assert.True(t, len(newKey.Tags) == 2)
@@ -62,10 +62,10 @@ func TestCreateEnrollmentKey(t *testing.T) {
 func TestDelete_EnrollmentKey(t *testing.T) {
 	database.InitializeDatabase()
 	defer database.CloseDB()
-	newKey, _ := CreateEnrollmentKey(0, time.Time{}, []string{"mynet", "skynet"}, nil, nil, true, uuid.Nil)
+	newKey, _ := CreateEnrollmentKey(0, time.Time{}, []string{"mynet", "skynet"}, nil, nil, true, uuid.Nil, false)
 	t.Run("Can_Delete_Key", func(t *testing.T) {
 		assert.True(t, newKey.IsValid())
-		err := DeleteEnrollmentKey(newKey.Value)
+		err := DeleteEnrollmentKey(newKey.Value, false)
 		assert.Nil(t, err)
 		oldKey, err := GetEnrollmentKey(newKey.Value)
 		assert.Equal(t, oldKey, models.EnrollmentKey{})
@@ -73,7 +73,7 @@ func TestDelete_EnrollmentKey(t *testing.T) {
 		assert.Equal(t, err, EnrollmentErrors.NoKeyFound)
 	})
 	t.Run("Can_Not_Delete_Invalid_Key", func(t *testing.T) {
-		err := DeleteEnrollmentKey("notakey")
+		err := DeleteEnrollmentKey("notakey", false)
 		assert.NotNil(t, err)
 		assert.Equal(t, err, EnrollmentErrors.NoKeyFound)
 	})
@@ -83,7 +83,7 @@ func TestDelete_EnrollmentKey(t *testing.T) {
 func TestDecrement_EnrollmentKey(t *testing.T) {
 	database.InitializeDatabase()
 	defer database.CloseDB()
-	newKey, _ := CreateEnrollmentKey(1, time.Time{}, nil, nil, nil, false, uuid.Nil)
+	newKey, _ := CreateEnrollmentKey(1, time.Time{}, nil, nil, nil, false, uuid.Nil, false)
 	t.Run("Check_initial_uses", func(t *testing.T) {
 		assert.True(t, newKey.IsValid())
 		assert.Equal(t, newKey.UsesRemaining, 1)
@@ -107,9 +107,9 @@ func TestDecrement_EnrollmentKey(t *testing.T) {
 func TestUsability_EnrollmentKey(t *testing.T) {
 	database.InitializeDatabase()
 	defer database.CloseDB()
-	key1, _ := CreateEnrollmentKey(1, time.Time{}, nil, nil, nil, false, uuid.Nil)
-	key2, _ := CreateEnrollmentKey(0, time.Now().Add(time.Minute<<4), nil, nil, nil, false, uuid.Nil)
-	key3, _ := CreateEnrollmentKey(0, time.Time{}, nil, nil, nil, true, uuid.Nil)
+	key1, _ := CreateEnrollmentKey(1, time.Time{}, nil, nil, nil, false, uuid.Nil, false)
+	key2, _ := CreateEnrollmentKey(0, time.Now().Add(time.Minute<<4), nil, nil, nil, false, uuid.Nil, false)
+	key3, _ := CreateEnrollmentKey(0, time.Time{}, nil, nil, nil, true, uuid.Nil, false)
 	t.Run("Check if valid use key can be used", func(t *testing.T) {
 		assert.Equal(t, key1.UsesRemaining, 1)
 		ok := TryToUseEnrollmentKey(key1)
@@ -145,7 +145,7 @@ func removeAllEnrollments() {
 func TestTokenize_EnrollmentKeys(t *testing.T) {
 	database.InitializeDatabase()
 	defer database.CloseDB()
-	newKey, _ := CreateEnrollmentKey(0, time.Time{}, []string{"mynet", "skynet"}, nil, nil, true, uuid.Nil)
+	newKey, _ := CreateEnrollmentKey(0, time.Time{}, []string{"mynet", "skynet"}, nil, nil, true, uuid.Nil, false)
 	const defaultValue = "MwE5MwE5MwE5MwE5MwE5MwE5MwE5MwE5"
 	const b64value = "eyJzZXJ2ZXIiOiJhcGkubXlzZXJ2ZXIuY29tIiwidmFsdWUiOiJNd0U1TXdFNU13RTVNd0U1TXdFNU13RTVNd0U1TXdFNSJ9"
 	const serverAddr = "api.myserver.com"
@@ -178,7 +178,7 @@ func TestTokenize_EnrollmentKeys(t *testing.T) {
 func TestDeTokenize_EnrollmentKeys(t *testing.T) {
 	database.InitializeDatabase()
 	defer database.CloseDB()
-	newKey, _ := CreateEnrollmentKey(0, time.Time{}, []string{"mynet", "skynet"}, nil, nil, true, uuid.Nil)
+	newKey, _ := CreateEnrollmentKey(0, time.Time{}, []string{"mynet", "skynet"}, nil, nil, true, uuid.Nil, false)
 	const b64Value = "eyJzZXJ2ZXIiOiJhcGkubXlzZXJ2ZXIuY29tIiwidmFsdWUiOiJNd0U1TXdFNU13RTVNd0U1TXdFNU13RTVNd0U1TXdFNSJ9"
 	const serverAddr = "api.myserver.com"
 

+ 274 - 4
logic/extpeers.go

@@ -69,7 +69,7 @@ func GetEgressRangesOnNetwork(client *models.ExtClient) ([]string, error) {
 			}
 		}
 	}
-	extclients := GetGwExtclients(client.IngressGatewayID, client.Network)
+	extclients, _ := GetNetworkExtClients(client.Network)
 	for _, extclient := range extclients {
 		if extclient.ClientID == client.ClientID {
 			continue
@@ -136,6 +136,12 @@ func DeleteExtClientAndCleanup(extClient models.ExtClient) error {
 	return nil
 }
 
+//TODO - enforce extclient-to-extclient on ingress gw
+/* 1. fetch all non-user static nodes
+a. check against each user node, if allowed add rule
+
+*/
+
 // GetNetworkExtClients - gets the ext clients of given network
 func GetNetworkExtClients(network string) ([]models.ExtClient, error) {
 	var extclients []models.ExtClient
@@ -396,6 +402,206 @@ func ToggleExtClientConnectivity(client *models.ExtClient, enable bool) (models.
 	return newClient, nil
 }
 
+func GetStaticNodeIps(node models.Node) (ips []net.IP) {
+	defaultUserPolicy, _ := GetDefaultPolicy(models.NetworkID(node.Network), models.UserPolicy)
+	defaultDevicePolicy, _ := GetDefaultPolicy(models.NetworkID(node.Network), models.DevicePolicy)
+
+	extclients := GetStaticNodesByNetwork(models.NetworkID(node.Network), false)
+	for _, extclient := range extclients {
+		if extclient.IsUserNode && defaultUserPolicy.Enabled {
+			continue
+		}
+		if !extclient.IsUserNode && defaultDevicePolicy.Enabled {
+			continue
+		}
+		if extclient.StaticNode.Address != "" {
+			ips = append(ips, extclient.StaticNode.AddressIPNet4().IP)
+		}
+		if extclient.StaticNode.Address6 != "" {
+			ips = append(ips, extclient.StaticNode.AddressIPNet6().IP)
+		}
+	}
+	return
+}
+
+func GetFwRulesOnIngressGateway(node models.Node) (rules []models.FwRule) {
+	// fetch user access to static clients via policies
+	defaultUserPolicy, _ := GetDefaultPolicy(models.NetworkID(node.Network), models.UserPolicy)
+	defaultDevicePolicy, _ := GetDefaultPolicy(models.NetworkID(node.Network), models.DevicePolicy)
+	nodes, _ := GetNetworkNodes(node.Network)
+	nodes = append(nodes, GetStaticNodesByNetwork(models.NetworkID(node.Network), true)...)
+	//fmt.Printf("=====> NODES: %+v \n\n", nodes)
+	userNodes := GetStaticUserNodesByNetwork(models.NetworkID(node.Network))
+	//fmt.Printf("=====> USER NODES %+v \n\n", userNodes)
+	for _, userNodeI := range userNodes {
+		for _, peer := range nodes {
+			if peer.IsUserNode {
+				continue
+			}
+			if IsUserAllowedToCommunicate(userNodeI.StaticNode.OwnerID, peer) {
+				if peer.IsStatic {
+					if userNodeI.StaticNode.Address != "" {
+						if !defaultUserPolicy.Enabled {
+							rules = append(rules, models.FwRule{
+								SrcIP: userNodeI.StaticNode.AddressIPNet4(),
+								DstIP: peer.StaticNode.AddressIPNet4(),
+								Allow: true,
+							})
+						}
+						rules = append(rules, models.FwRule{
+							SrcIP: peer.StaticNode.AddressIPNet4(),
+							DstIP: userNodeI.StaticNode.AddressIPNet4(),
+							Allow: true,
+						})
+					}
+					if userNodeI.StaticNode.Address6 != "" {
+						if !defaultUserPolicy.Enabled {
+							rules = append(rules, models.FwRule{
+								SrcIP: userNodeI.StaticNode.AddressIPNet6(),
+								DstIP: peer.StaticNode.AddressIPNet6(),
+								Allow: true,
+							})
+						}
+
+						rules = append(rules, models.FwRule{
+							SrcIP: peer.StaticNode.AddressIPNet6(),
+							DstIP: userNodeI.StaticNode.AddressIPNet6(),
+							Allow: true,
+						})
+					}
+					if len(peer.StaticNode.ExtraAllowedIPs) > 0 {
+						for _, additionalAllowedIPNet := range peer.StaticNode.ExtraAllowedIPs {
+							_, ipNet, err := net.ParseCIDR(additionalAllowedIPNet)
+							if err != nil {
+								continue
+							}
+							if ipNet.IP.To4() != nil {
+								rules = append(rules, models.FwRule{
+									SrcIP: userNodeI.StaticNode.AddressIPNet4(),
+									DstIP: *ipNet,
+									Allow: true,
+								})
+							} else {
+								rules = append(rules, models.FwRule{
+									SrcIP: userNodeI.StaticNode.AddressIPNet6(),
+									DstIP: *ipNet,
+									Allow: true,
+								})
+							}
+
+						}
+
+					}
+				} else {
+
+					if userNodeI.StaticNode.Address != "" {
+						if !defaultUserPolicy.Enabled {
+							rules = append(rules, models.FwRule{
+								SrcIP: userNodeI.StaticNode.AddressIPNet4(),
+								DstIP: net.IPNet{
+									IP:   peer.Address.IP,
+									Mask: net.CIDRMask(32, 32),
+								},
+								Allow: true,
+							})
+						}
+					}
+
+					if userNodeI.StaticNode.Address6 != "" {
+						rules = append(rules, models.FwRule{
+							SrcIP: userNodeI.StaticNode.AddressIPNet6(),
+							DstIP: net.IPNet{
+								IP:   peer.Address6.IP,
+								Mask: net.CIDRMask(128, 128),
+							},
+							Allow: true,
+						})
+					}
+				}
+
+			}
+		}
+	}
+
+	if defaultDevicePolicy.Enabled {
+		return
+	}
+	for _, nodeI := range nodes {
+		if !nodeI.IsStatic || nodeI.IsUserNode {
+			continue
+		}
+		for _, peer := range nodes {
+			if peer.StaticNode.ClientID == nodeI.StaticNode.ClientID || peer.IsUserNode {
+				continue
+			}
+			if IsNodeAllowedToCommunicate(nodeI, peer) {
+				if peer.IsStatic {
+					if nodeI.StaticNode.Address != "" {
+						rules = append(rules, models.FwRule{
+							SrcIP: nodeI.StaticNode.AddressIPNet4(),
+							DstIP: peer.StaticNode.AddressIPNet4(),
+							Allow: true,
+						})
+					}
+					if nodeI.StaticNode.Address6 != "" {
+						rules = append(rules, models.FwRule{
+							SrcIP: nodeI.StaticNode.AddressIPNet6(),
+							DstIP: peer.StaticNode.AddressIPNet6(),
+							Allow: true,
+						})
+					}
+					if len(peer.StaticNode.ExtraAllowedIPs) > 0 {
+						for _, additionalAllowedIPNet := range peer.StaticNode.ExtraAllowedIPs {
+							_, ipNet, err := net.ParseCIDR(additionalAllowedIPNet)
+							if err != nil {
+								continue
+							}
+							if ipNet.IP.To4() != nil {
+								rules = append(rules, models.FwRule{
+									SrcIP: nodeI.StaticNode.AddressIPNet4(),
+									DstIP: *ipNet,
+									Allow: true,
+								})
+							} else {
+								rules = append(rules, models.FwRule{
+									SrcIP: nodeI.StaticNode.AddressIPNet6(),
+									DstIP: *ipNet,
+									Allow: true,
+								})
+							}
+
+						}
+
+					}
+				} else {
+					if nodeI.StaticNode.Address != "" {
+						rules = append(rules, models.FwRule{
+							SrcIP: nodeI.StaticNode.AddressIPNet4(),
+							DstIP: net.IPNet{
+								IP:   peer.Address.IP,
+								Mask: net.CIDRMask(32, 32),
+							},
+							Allow: true,
+						})
+					}
+					if nodeI.StaticNode.Address6 != "" {
+						rules = append(rules, models.FwRule{
+							SrcIP: nodeI.StaticNode.AddressIPNet6(),
+							DstIP: net.IPNet{
+								IP:   peer.Address6.IP,
+								Mask: net.CIDRMask(128, 128),
+							},
+							Allow: true,
+						})
+					}
+				}
+
+			}
+		}
+	}
+	return
+}
+
 func GetExtPeers(node, peer *models.Node) ([]wgtypes.PeerConfig, []models.IDandAddr, []models.EgressNetworkRoutes, error) {
 	var peers []wgtypes.PeerConfig
 	var idsAndAddr []models.IDandAddr
@@ -413,6 +619,16 @@ func GetExtPeers(node, peer *models.Node) ([]wgtypes.PeerConfig, []models.IDandA
 		if !IsClientNodeAllowed(&extPeer, peer.ID.String()) {
 			continue
 		}
+		if extPeer.RemoteAccessClientID == "" {
+			if !IsNodeAllowedToCommunicate(extPeer.ConvertToStaticNode(), *peer) {
+				continue
+			}
+		} else {
+			if !IsUserAllowedToCommunicate(extPeer.OwnerID, *peer) {
+				continue
+			}
+		}
+
 		pubkey, err := wgtypes.ParseKey(extPeer.PublicKey)
 		if err != nil {
 			logger.Log(1, "error parsing ext pub key:", err.Error())
@@ -484,8 +700,8 @@ func getExtPeerEgressRoute(node models.Node, extPeer models.ExtClient) (egressRo
 	return
 }
 
-func getExtpeersExtraRoutes(node models.Node, network string) (egressRoutes []models.EgressNetworkRoutes) {
-	extPeers, err := GetNetworkExtClients(network)
+func getExtpeerEgressRanges(node models.Node) (ranges, ranges6 []net.IPNet) {
+	extPeers, err := GetNetworkExtClients(node.Network)
 	if err != nil {
 		return
 	}
@@ -493,6 +709,36 @@ func getExtpeersExtraRoutes(node models.Node, network string) (egressRoutes []mo
 		if len(extPeer.ExtraAllowedIPs) == 0 {
 			continue
 		}
+		if !IsNodeAllowedToCommunicate(extPeer.ConvertToStaticNode(), node) {
+			continue
+		}
+		for _, allowedRange := range extPeer.ExtraAllowedIPs {
+			_, ipnet, err := net.ParseCIDR(allowedRange)
+			if err == nil {
+				if ipnet.IP.To4() != nil {
+					ranges = append(ranges, *ipnet)
+				} else {
+					ranges6 = append(ranges6, *ipnet)
+				}
+
+			}
+		}
+	}
+	return
+}
+
+func getExtpeersExtraRoutes(node models.Node) (egressRoutes []models.EgressNetworkRoutes) {
+	extPeers, err := GetNetworkExtClients(node.Network)
+	if err != nil {
+		return
+	}
+	for _, extPeer := range extPeers {
+		if len(extPeer.ExtraAllowedIPs) == 0 {
+			continue
+		}
+		if !IsNodeAllowedToCommunicate(extPeer.ConvertToStaticNode(), node) {
+			continue
+		}
 		egressRoutes = append(egressRoutes, getExtPeerEgressRoute(node, extPeer)...)
 	}
 	return
@@ -530,13 +776,37 @@ func GetExtclientAllowedIPs(client models.ExtClient) (allowedIPs []string) {
 	return
 }
 
-func GetStaticNodesByNetwork(network models.NetworkID) (staticNode []models.Node) {
+func GetStaticUserNodesByNetwork(network models.NetworkID) (staticNode []models.Node) {
+	extClients, err := GetAllExtClients()
+	if err != nil {
+		return
+	}
+	for _, extI := range extClients {
+		if extI.Network == network.String() {
+			if extI.RemoteAccessClientID != "" {
+				n := models.Node{
+					IsStatic:   true,
+					StaticNode: extI,
+					IsUserNode: extI.RemoteAccessClientID != "",
+				}
+				staticNode = append(staticNode, n)
+			}
+		}
+	}
+
+	return
+}
+
+func GetStaticNodesByNetwork(network models.NetworkID, onlyWg bool) (staticNode []models.Node) {
 	extClients, err := GetAllExtClients()
 	if err != nil {
 		return
 	}
 	for _, extI := range extClients {
 		if extI.Network == network.String() {
+			if onlyWg && extI.RemoteAccessClientID != "" {
+				continue
+			}
 			n := models.Node{
 				IsStatic:   true,
 				StaticNode: extI,

+ 1 - 29
logic/gateway.go

@@ -188,30 +188,6 @@ func CreateIngressGateway(netid string, nodeid string, ingress models.IngressReq
 	if err != nil {
 		return models.Node{}, err
 	}
-	// create network role for this gateway
-	CreateRole(models.UserRolePermissionTemplate{
-		ID:        models.GetRAGRoleID(node.Network, host.ID.String()),
-		UiName:    models.GetRAGRoleName(node.Network, host.Name),
-		NetworkID: models.NetworkID(node.Network),
-		Default:   true,
-		NetworkLevelAccess: map[models.RsrcType]map[models.RsrcID]models.RsrcPermissionScope{
-			models.RemoteAccessGwRsrc: {
-				models.RsrcID(node.ID.String()): models.RsrcPermissionScope{
-					Read:      true,
-					VPNaccess: true,
-				},
-			},
-			models.ExtClientsRsrc: {
-				models.AllExtClientsRsrcID: models.RsrcPermissionScope{
-					Read:     true,
-					Create:   true,
-					Update:   true,
-					Delete:   true,
-					SelfOnly: true,
-				},
-			},
-		},
-	})
 	err = SetNetworkNodesLastModified(netid)
 	return node, err
 }
@@ -266,11 +242,7 @@ func DeleteIngressGateway(nodeid string) (models.Node, []models.ExtClient, error
 	if err != nil {
 		return models.Node{}, removedClients, err
 	}
-	host, err := GetHost(node.HostID.String())
-	if err != nil {
-		return models.Node{}, removedClients, err
-	}
-	go DeleteRole(models.GetRAGRoleID(node.Network, host.ID.String()), true)
+
 	err = SetNetworkNodesLastModified(node.Network)
 	return node, removedClients, err
 }

+ 1 - 13
logic/hosts.go

@@ -287,19 +287,7 @@ func UpdateHostFromClient(newHost, currHost *models.Host) (sendPeerUpdate bool)
 	currHost.IsStaticPort = newHost.IsStaticPort
 	currHost.IsStatic = newHost.IsStatic
 	currHost.MTU = newHost.MTU
-	if newHost.Name != currHost.Name {
-		// update any rag role ids
-		for _, nodeID := range newHost.Nodes {
-			node, err := GetNodeByID(nodeID)
-			if err == nil && node.IsIngressGateway {
-				role, err := GetRole(models.GetRAGRoleID(node.Network, currHost.ID.String()))
-				if err == nil {
-					role.UiName = models.GetRAGRoleName(node.Network, newHost.Name)
-					UpdateRole(role)
-				}
-			}
-		}
-	}
+
 	currHost.Name = newHost.Name
 	if len(newHost.NatType) > 0 && newHost.NatType != currHost.NatType {
 		currHost.NatType = newHost.NatType

+ 12 - 0
logic/networks.go

@@ -177,6 +177,17 @@ func DeleteNetwork(network string) error {
 	if err != nil {
 		logger.Log(1, "failed to remove the node acls during network delete for network,", network)
 	}
+	// Delete default network enrollment key
+	keys, _ := GetAllEnrollmentKeys()
+	for _, key := range keys {
+		if key.Tags[0] == network {
+			if key.Default {
+				DeleteEnrollmentKey(key.Value, true)
+				break
+			}
+
+		}
+	}
 	nodeCount, err := GetNetworkNonServerNodeCount(network)
 	if nodeCount == 0 || database.IsEmptyRecord(err) {
 		// delete server nodes first then db records
@@ -243,6 +254,7 @@ func CreateNetwork(network models.Network) (models.Network, error) {
 		[]models.TagID{},
 		true,
 		uuid.Nil,
+		true,
 	)
 
 	return network, nil

+ 16 - 6
logic/nodes.go

@@ -196,10 +196,6 @@ func DeleteNode(node *models.Node, purge bool) error {
 		if err := DeleteGatewayExtClients(node.ID.String(), node.Network); err != nil {
 			slog.Error("failed to delete ext clients", "nodeid", node.ID.String(), "error", err.Error())
 		}
-		host, err := GetHost(node.HostID.String())
-		if err == nil {
-			go DeleteRole(models.GetRAGRoleID(node.Network, host.ID.String()), true)
-		}
 	}
 	if node.IsRelayed {
 		// cleanup node from relayednodes on relay node
@@ -385,7 +381,7 @@ func AddStaticNodestoList(nodes []models.Node) []models.Node {
 			continue
 		}
 		if node.IsIngressGateway {
-			nodes = append(nodes, GetStaticNodesByNetwork(models.NetworkID(node.Network))...)
+			nodes = append(nodes, GetStaticNodesByNetwork(models.NetworkID(node.Network), false)...)
 			netMap[node.Network] = struct{}{}
 		}
 	}
@@ -716,7 +712,21 @@ func GetAllFailOvers() ([]models.Node, error) {
 	return igs, nil
 }
 
-func GetTagMapWithNodes(netID models.NetworkID) (tagNodesMap map[models.TagID][]models.Node) {
+func GetTagMapWithNodes() (tagNodesMap map[models.TagID][]models.Node) {
+	tagNodesMap = make(map[models.TagID][]models.Node)
+	nodes, _ := GetAllNodes()
+	for _, nodeI := range nodes {
+		if nodeI.Tags == nil {
+			continue
+		}
+		for nodeTagID := range nodeI.Tags {
+			tagNodesMap[nodeTagID] = append(tagNodesMap[nodeTagID], nodeI)
+		}
+	}
+	return
+}
+
+func GetTagMapWithNodesByNetwork(netID models.NetworkID) (tagNodesMap map[models.TagID][]models.Node) {
 	tagNodesMap = make(map[models.TagID][]models.Node)
 	nodes, _ := GetNetworkNodes(netID.String())
 	for _, nodeI := range nodes {

+ 20 - 2
logic/peers.go

@@ -74,7 +74,8 @@ func GetPeerUpdateForHost(network string, host *models.Host, allNodes []models.N
 		ServerVersion: servercfg.GetVersion(),
 		ServerAddrs:   []models.ServerAddr{},
 		FwUpdate: models.FwUpdate{
-			EgressInfo: make(map[string]models.EgressInfo),
+			EgressInfo:  make(map[string]models.EgressInfo),
+			IngressInfo: make(map[string]models.IngressInfo),
 		},
 		PeerIDs:           make(models.PeerMap, 0),
 		Peers:             []wgtypes.PeerConfig{},
@@ -182,7 +183,7 @@ func GetPeerUpdateForHost(network string, host *models.Host, allNodes []models.N
 				})
 			}
 			if peer.IsIngressGateway {
-				hostPeerUpdate.EgressRoutes = append(hostPeerUpdate.EgressRoutes, getExtpeersExtraRoutes(node, peer.Network)...)
+				hostPeerUpdate.EgressRoutes = append(hostPeerUpdate.EgressRoutes, getExtpeersExtraRoutes(node)...)
 			}
 			_, isFailOverPeer := node.FailOverPeers[peer.ID.String()]
 			if servercfg.IsPro {
@@ -241,6 +242,7 @@ func GetPeerUpdateForHost(network string, host *models.Host, allNodes []models.N
 				!peer.PendingDelete &&
 				peer.Connected &&
 				nodeacls.AreNodesAllowed(nodeacls.NetworkID(node.Network), nodeacls.NodeID(node.ID.String()), nodeacls.NodeID(peer.ID.String())) &&
+				IsNodeAllowedToCommunicate(node, peer) &&
 				(deletedNode == nil || (deletedNode != nil && peer.ID.String() != deletedNode.ID.String())) {
 				peerConfig.AllowedIPs = allowedips // only append allowed IPs if valid connection
 			}
@@ -287,8 +289,23 @@ func GetPeerUpdateForHost(network string, host *models.Host, allNodes []models.N
 		var extPeerIDAndAddrs []models.IDandAddr
 		var egressRoutes []models.EgressNetworkRoutes
 		if node.IsIngressGateway {
+			hostPeerUpdate.FwUpdate.IsIngressGw = true
 			extPeers, extPeerIDAndAddrs, egressRoutes, err = GetExtPeers(&node, &node)
 			if err == nil {
+				defaultUserPolicy, _ := GetDefaultPolicy(models.NetworkID(node.Network), models.UserPolicy)
+				defaultDevicePolicy, _ := GetDefaultPolicy(models.NetworkID(node.Network), models.DevicePolicy)
+				if !defaultDevicePolicy.Enabled || !defaultUserPolicy.Enabled {
+					ingFwUpdate := models.IngressInfo{
+						IngressID:     node.ID.String(),
+						Network:       node.NetworkRange,
+						Network6:      node.NetworkRange6,
+						AllowAll:      defaultDevicePolicy.Enabled && defaultUserPolicy.Default,
+						StaticNodeIps: GetStaticNodeIps(node),
+						Rules:         GetFwRulesOnIngressGateway(node),
+					}
+					ingFwUpdate.EgressRanges, ingFwUpdate.EgressRanges6 = getExtpeerEgressRanges(node)
+					hostPeerUpdate.FwUpdate.IngressInfo[node.ID.String()] = ingFwUpdate
+				}
 				hostPeerUpdate.EgressRoutes = append(hostPeerUpdate.EgressRoutes, egressRoutes...)
 				hostPeerUpdate.Peers = append(hostPeerUpdate.Peers, extPeers...)
 				for _, extPeerIdAndAddr := range extPeerIDAndAddrs {
@@ -426,6 +443,7 @@ func GetAllowedIPs(node, peer *models.Node, metrics *models.Metrics) []net.IPNet
 			logger.Log(2, "could not retrieve ext peers for ", peer.ID.String(), err.Error())
 		}
 		for _, extPeer := range extPeers {
+
 			allowedips = append(allowedips, extPeer.AllowedIPs...)
 		}
 	}

+ 6 - 3
logic/tags.go

@@ -46,7 +46,7 @@ func InsertTag(tag models.Tag) error {
 }
 
 // DeleteTag - delete tag, will also untag hosts
-func DeleteTag(tagID models.TagID) error {
+func DeleteTag(tagID models.TagID, removeFromPolicy bool) error {
 	tagMutex.Lock()
 	defer tagMutex.Unlock()
 	// cleanUp tags on hosts
@@ -65,7 +65,10 @@ func DeleteTag(tagID models.TagID) error {
 			UpsertNode(&nodeI)
 		}
 	}
-
+	if removeFromPolicy {
+		// remove tag used on acl policy
+		go RemoveDeviceTagFromAclPolicies(tagID, tag.Network)
+	}
 	extclients, _ := GetNetworkExtClients(tag.Network.String())
 	for _, extclient := range extclients {
 		if _, ok := extclient.Tags[tagID]; ok {
@@ -82,7 +85,7 @@ func ListTagsWithNodes(netID models.NetworkID) ([]models.TagListResp, error) {
 	if err != nil {
 		return []models.TagListResp{}, err
 	}
-	tagsNodeMap := GetTagMapWithNodes(netID)
+	tagsNodeMap := GetTagMapWithNodesByNetwork(netID)
 	resp := []models.TagListResp{}
 	for _, tagI := range tags {
 		tagRespI := models.TagListResp{

+ 11 - 0
logic/user_mgmt.go

@@ -39,17 +39,28 @@ var FilterNetworksByRole = func(allnetworks []models.Network, user models.User)
 var IsGroupsValid = func(groups map[models.UserGroupID]struct{}) error {
 	return nil
 }
+var IsGroupValid = func(groupID models.UserGroupID) error {
+	return nil
+}
 var IsNetworkRolesValid = func(networkRoles map[models.NetworkID]map[models.UserRoleID]struct{}) error {
 	return nil
 }
 
+var MigrateUserRoleAndGroups = func(u models.User) {
+
+}
+
 var UpdateUserGwAccess = func(currentUser, changeUser models.User) {}
 
 var UpdateRole = func(r models.UserRolePermissionTemplate) error { return nil }
 
 var InitialiseRoles = userRolesInit
+var IntialiseGroups = func() {}
 var DeleteNetworkRoles = func(netID string) {}
 var CreateDefaultNetworkRolesAndGroups = func(netID models.NetworkID) {}
+var CreateDefaultUserPolicies = func(netID models.NetworkID) {}
+var GetUserGroupsInNetwork = func(netID models.NetworkID) (networkGrps map[models.UserGroupID]models.UserGroup) { return }
+var AddGlobalNetRolesToAdmins = func(u models.User) {}
 
 // GetRole - fetches role template by id
 func GetRole(roleID models.UserRoleID) (models.UserRolePermissionTemplate, error) {

+ 1 - 0
main.go

@@ -103,6 +103,7 @@ func initialize() { // Client Mode Prereq Check
 
 	logic.SetJWTSecret()
 	logic.InitialiseRoles()
+	logic.IntialiseGroups()
 	err = serverctl.SetDefaults()
 	if err != nil {
 		logger.FatalLog("error setting defaults: ", err.Error())

+ 15 - 65
migrate/migrate.go

@@ -22,7 +22,7 @@ import (
 func Run() {
 	updateEnrollmentKeys()
 	assignSuperAdmin()
-	createDefaultTags()
+	createDefaultTagsAndPolicies()
 	removeOldUserGrps()
 	syncUsers()
 	updateHosts()
@@ -149,6 +149,7 @@ func updateEnrollmentKeys() {
 			[]models.TagID{},
 			true,
 			uuid.Nil,
+			true,
 		)
 
 	}
@@ -207,7 +208,10 @@ func updateNodes() {
 				node.Tags[tagID] = struct{}{}
 				logic.UpsertNode(&node)
 			}
-
+			host, err := logic.GetHost(node.HostID.String())
+			if err == nil {
+				go logic.DeleteRole(models.GetRAGRoleID(node.Network, host.ID.String()), true)
+			}
 		}
 		if node.IsEgressGateway {
 			egressRanges, update := removeInterGw(node.EgressGatewayRanges)
@@ -385,42 +389,8 @@ func syncUsers() {
 	// create default network user roles for existing networks
 	if servercfg.IsPro {
 		networks, _ := logic.GetNetworks()
-		nodes, err := logic.GetAllNodes()
-		if err == nil {
-			for _, netI := range networks {
-				logic.CreateDefaultNetworkRolesAndGroups(models.NetworkID(netI.NetID))
-				networkNodes := logic.GetNetworkNodesMemory(nodes, netI.NetID)
-				for _, networkNodeI := range networkNodes {
-					if networkNodeI.IsIngressGateway {
-						h, err := logic.GetHost(networkNodeI.HostID.String())
-						if err == nil {
-							logic.CreateRole(models.UserRolePermissionTemplate{
-								ID:        models.GetRAGRoleID(networkNodeI.Network, h.ID.String()),
-								UiName:    models.GetRAGRoleName(networkNodeI.Network, h.Name),
-								NetworkID: models.NetworkID(netI.NetID),
-								NetworkLevelAccess: map[models.RsrcType]map[models.RsrcID]models.RsrcPermissionScope{
-									models.RemoteAccessGwRsrc: {
-										models.RsrcID(networkNodeI.ID.String()): models.RsrcPermissionScope{
-											Read:      true,
-											VPNaccess: true,
-										},
-									},
-									models.ExtClientsRsrc: {
-										models.AllExtClientsRsrcID: models.RsrcPermissionScope{
-											Read:     true,
-											Create:   true,
-											Update:   true,
-											Delete:   true,
-											SelfOnly: true,
-										},
-									},
-								},
-							})
-						}
-
-					}
-				}
-			}
+		for _, netI := range networks {
+			logic.CreateDefaultNetworkRolesAndGroups(models.NetworkID(netI.NetID))
 		}
 	}
 
@@ -437,6 +407,8 @@ func syncUsers() {
 				logic.UpsertUser(user)
 			}
 			if user.PlatformRoleID.String() != "" {
+				logic.MigrateUserRoleAndGroups(user)
+				logic.AddGlobalNetRolesToAdmins(user)
 				continue
 			}
 			user.AuthType = models.BasicAuth
@@ -458,42 +430,20 @@ func syncUsers() {
 				user.PlatformRoleID = models.ServiceUser
 			}
 			logic.UpsertUser(user)
-			if len(user.RemoteGwIDs) > 0 {
-				// define user roles for network
-				// assign relevant network role to user
-				for remoteGwID := range user.RemoteGwIDs {
-					gwNode, err := logic.GetNodeByID(remoteGwID)
-					if err != nil {
-						continue
-					}
-					h, err := logic.GetHost(gwNode.HostID.String())
-					if err != nil {
-						continue
-					}
-					r, err := logic.GetRole(models.GetRAGRoleID(gwNode.Network, h.ID.String()))
-					if err != nil {
-						continue
-					}
-					if netRoles, ok := user.NetworkRoles[models.NetworkID(gwNode.Network)]; ok {
-						netRoles[r.ID] = struct{}{}
-					} else {
-						user.NetworkRoles[models.NetworkID(gwNode.Network)] = map[models.UserRoleID]struct{}{
-							r.ID: {},
-						}
-					}
-				}
-				logic.UpsertUser(user)
-			}
+			logic.AddGlobalNetRolesToAdmins(user)
+			logic.MigrateUserRoleAndGroups(user)
 		}
 	}
+
 }
 
-func createDefaultTags() {
+func createDefaultTagsAndPolicies() {
 	networks, err := logic.GetNetworks()
 	if err != nil {
 		return
 	}
 	for _, network := range networks {
 		logic.CreateDefaultTags(models.NetworkID(network.NetID))
+		logic.CreateDefaultAclNetworkPolicies(models.NetworkID(network.NetID))
 	}
 }

+ 72 - 0
models/acl.go

@@ -0,0 +1,72 @@
+package models
+
+import (
+	"time"
+)
+
+// AllowedTrafficDirection - allowed direction of traffic
+type AllowedTrafficDirection int
+
+const (
+	// TrafficDirectionUni implies traffic is only allowed in one direction (src --> dst)
+	TrafficDirectionUni AllowedTrafficDirection = iota
+	// TrafficDirectionBi implies traffic is allowed both direction (src <--> dst )
+	TrafficDirectionBi
+)
+
+type AclPolicyType string
+
+const (
+	UserPolicy   AclPolicyType = "user-policy"
+	DevicePolicy AclPolicyType = "device-policy"
+)
+
+type AclPolicyTag struct {
+	ID    AclGroupType `json:"id"`
+	Value string       `json:"value"`
+}
+
+type AclGroupType string
+
+const (
+	UserAclID                AclGroupType = "user"
+	UserGroupAclID           AclGroupType = "user-group"
+	DeviceAclID              AclGroupType = "tag"
+	NetmakerIPAclID          AclGroupType = "ip"
+	NetmakerSubNetRangeAClID AclGroupType = "ipset"
+)
+
+func (g AclGroupType) String() string {
+	return string(g)
+}
+
+type UpdateAclRequest struct {
+	Acl
+	NewName string `json:"new_name"`
+}
+
+type AclPolicy struct {
+	TypeID        AclPolicyType
+	PrefixTagUser AclGroupType
+}
+
+type Acl struct {
+	ID               string                  `json:"id"`
+	Default          bool                    `json:"default"`
+	MetaData         string                  `json:"meta_data"`
+	Name             string                  `json:"name"`
+	NetworkID        NetworkID               `json:"network_id"`
+	RuleType         AclPolicyType           `json:"policy_type"`
+	Src              []AclPolicyTag          `json:"src_type"`
+	Dst              []AclPolicyTag          `json:"dst_type"`
+	AllowedDirection AllowedTrafficDirection `json:"allowed_traffic_direction"`
+	Enabled          bool                    `json:"enabled"`
+	CreatedBy        string                  `json:"created_by"`
+	CreatedAt        time.Time               `json:"created_at"`
+}
+
+type AclPolicyTypes struct {
+	RuleTypes     []AclPolicyType `json:"policy_types"`
+	SrcGroupTypes []AclGroupType  `json:"src_grp_types"`
+	DstGroupTypes []AclGroupType  `json:"dst_grp_types"`
+}

+ 1 - 0
models/enrollment_key.go

@@ -53,6 +53,7 @@ type EnrollmentKey struct {
 	Type          KeyType   `json:"type"`
 	Relay         uuid.UUID `json:"relay"`
 	Groups        []TagID   `json:"groups"`
+	Default       bool      `json:"default"`
 }
 
 // APIEnrollmentKey - used to create enrollment keys via API

+ 12 - 0
models/extclient.go

@@ -36,3 +36,15 @@ type CustomExtClient struct {
 	PostDown             string              `json:"postdown" bson:"postdown" validate:"max=1024"`
 	Tags                 map[TagID]struct{}  `json:"tags"`
 }
+
+func (ext *ExtClient) ConvertToStaticNode() Node {
+
+	return Node{
+		CommonNode: CommonNode{
+			Network: ext.Network,
+		},
+		Tags:       ext.Tags,
+		IsStatic:   true,
+		StaticNode: *ext,
+	}
+}

+ 18 - 4
models/mqtt.go

@@ -27,10 +27,22 @@ type HostPeerUpdate struct {
 	ManageDNS         bool                  `yaml:"manage_dns"`
 }
 
+type FwRule struct {
+	SrcIP net.IPNet
+	DstIP net.IPNet
+	Allow bool
+}
+
 // IngressInfo - struct for ingress info
 type IngressInfo struct {
-	ExtPeers     map[string]ExtClientInfo `json:"ext_peers" yaml:"ext_peers"`
-	EgressRanges []string                 `json:"egress_ranges" yaml:"egress_ranges"`
+	IngressID     string      `json:"ingress_id"`
+	Network       net.IPNet   `json:"network"`
+	Network6      net.IPNet   `json:"network6"`
+	StaticNodeIps []net.IP    `json:"static_node_ips"`
+	Rules         []FwRule    `json:"rules"`
+	AllowAll      bool        `json:"allow_all"`
+	EgressRanges  []net.IPNet `json:"egress_ranges"`
+	EgressRanges6 []net.IPNet `json:"egress_ranges6"`
 }
 
 // EgressInfo - struct for egress info
@@ -78,8 +90,10 @@ type KeyUpdate struct {
 
 // FwUpdate - struct for firewall updates
 type FwUpdate struct {
-	IsEgressGw bool                  `json:"is_egress_gw"`
-	EgressInfo map[string]EgressInfo `json:"egress_info"`
+	IsEgressGw  bool                   `json:"is_egress_gw"`
+	IsIngressGw bool                   `json:"is_ingress_gw"`
+	EgressInfo  map[string]EgressInfo  `json:"egress_info"`
+	IngressInfo map[string]IngressInfo `json:"ingress_info"`
 }
 
 // FailOverMeReq - struct for failover req

+ 5 - 1
models/user_mgmt.go

@@ -62,6 +62,7 @@ const (
 	EnrollmentKeysRsrc RsrcType = "enrollment_key"
 	UserRsrc           RsrcType = "users"
 	AclRsrc            RsrcType = "acl"
+	TagRsrc            RsrcType = "tag"
 	DnsRsrc            RsrcType = "dns"
 	FailOverRsrc       RsrcType = "fail_over"
 	MetricRsrc         RsrcType = "metrics"
@@ -116,8 +117,9 @@ type RsrcPermissionScope struct {
 
 type UserRolePermissionTemplate struct {
 	ID                  UserRoleID                                  `json:"id"`
-	UiName              string                                      `json:"ui_name"`
+	Name                string                                      `json:"name"`
 	Default             bool                                        `json:"default"`
+	MetaData            string                                      `json:"meta_data"`
 	DenyDashboardAccess bool                                        `json:"deny_dashboard_access"`
 	FullAccess          bool                                        `json:"full_access"`
 	NetworkID           NetworkID                                   `json:"network_id"`
@@ -132,6 +134,8 @@ type CreateGroupReq struct {
 
 type UserGroup struct {
 	ID           UserGroupID                           `json:"id"`
+	Default      bool                                  `json:"default"`
+	Name         string                                `json:"name"`
 	NetworkRoles map[NetworkID]map[UserRoleID]struct{} `json:"network_roles"`
 	MetaData     string                                `json:"meta_data"`
 }

+ 7 - 0
pro/controllers/users.go

@@ -496,6 +496,10 @@ func deleteUserGroup(w http.ResponseWriter, r *http.Request) {
 		logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("failed to fetch group details"), "badrequest"))
 		return
 	}
+	if userG.Default {
+		logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("cannot delete default user group"), "badrequest"))
+		return
+	}
 	err = proLogic.DeleteUserGroup(models.UserGroupID(gid))
 	if err != nil {
 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
@@ -996,6 +1000,9 @@ func getRemoteAccessGatewayConf(w http.ResponseWriter, r *http.Request) {
 		if err == nil { // check if parent network default ACL is enabled (yes) or not (no)
 			userConf.Enabled = parentNetwork.DefaultACL == "yes"
 		}
+		userConf.Tags = make(map[models.TagID]struct{})
+		userConf.Tags[models.TagID(fmt.Sprintf("%s.%s", userConf.Network,
+			models.RemoteAccessTagName))] = struct{}{}
 		if err = logic.CreateExtClient(&userConf); err != nil {
 			slog.Error(
 				"failed to create extclient",

+ 6 - 0
pro/initialize.go

@@ -131,9 +131,15 @@ func InitPro() {
 	logic.CreateDefaultNetworkRolesAndGroups = proLogic.CreateDefaultNetworkRolesAndGroups
 	logic.FilterNetworksByRole = proLogic.FilterNetworksByRole
 	logic.IsGroupsValid = proLogic.IsGroupsValid
+	logic.IsGroupValid = proLogic.IsGroupValid
 	logic.IsNetworkRolesValid = proLogic.IsNetworkRolesValid
 	logic.InitialiseRoles = proLogic.UserRolesInit
 	logic.UpdateUserGwAccess = proLogic.UpdateUserGwAccess
+	logic.CreateDefaultUserPolicies = proLogic.CreateDefaultUserPolicies
+	logic.MigrateUserRoleAndGroups = proLogic.MigrateUserRoleAndGroups
+	logic.IntialiseGroups = proLogic.UserGroupsInit
+	logic.AddGlobalNetRolesToAdmins = proLogic.AddGlobalNetRolesToAdmins
+	logic.GetUserGroupsInNetwork = proLogic.GetUserGroupsInNetwork
 }
 
 func retrieveProLogo() string {

+ 1 - 1
pro/logic/ext_acls.go

@@ -62,7 +62,7 @@ func SetClientDefaultACLs(ec *models.ExtClient) error {
 		slog.Error("failed to get network acls", "error", err)
 		return err
 	}
-	networkAcls[acls.AclID(ec.ClientID)] = acls.ACL{}
+	networkAcls[acls.AclID(ec.ClientID)] = make(acls.ACL)
 	for i := range networkNodes {
 		currNode := networkNodes[i]
 		if network.DefaultACL == "no" || currNode.DefaultACL == "no" {

+ 51 - 0
pro/logic/migrate.go

@@ -0,0 +1,51 @@
+package logic
+
+import (
+	"fmt"
+
+	"github.com/gravitl/netmaker/logic"
+	"github.com/gravitl/netmaker/models"
+)
+
+func MigrateUserRoleAndGroups(user models.User) {
+	var err error
+	if len(user.RemoteGwIDs) > 0 {
+		// define user roles for network
+		// assign relevant network role to user
+		for remoteGwID := range user.RemoteGwIDs {
+			gwNode, err := logic.GetNodeByID(remoteGwID)
+			if err != nil {
+				continue
+			}
+			var g models.UserGroup
+			if user.PlatformRoleID == models.ServiceUser {
+				g, err = GetUserGroup(models.UserGroupID(fmt.Sprintf("%s-%s-grp", gwNode.Network, models.NetworkUser)))
+			} else {
+				g, err = GetUserGroup(models.UserGroupID(fmt.Sprintf("%s-%s-grp",
+					gwNode.Network, models.NetworkAdmin)))
+			}
+			if err != nil {
+				continue
+			}
+			user.UserGroups[g.ID] = struct{}{}
+		}
+	}
+	if len(user.NetworkRoles) > 0 {
+		for netID := range user.NetworkRoles {
+			var g models.UserGroup
+			if user.PlatformRoleID == models.ServiceUser {
+				g, err = GetUserGroup(models.UserGroupID(fmt.Sprintf("%s-%s-grp", netID, models.NetworkUser)))
+			} else {
+				g, err = GetUserGroup(models.UserGroupID(fmt.Sprintf("%s-%s-grp",
+					netID, models.NetworkAdmin)))
+			}
+			if err != nil {
+				continue
+			}
+			user.UserGroups[g.ID] = struct{}{}
+			user.NetworkRoles = make(map[models.NetworkID]map[models.UserRoleID]struct{})
+		}
+
+	}
+	logic.UpsertUser(user)
+}

+ 9 - 0
pro/logic/security.go

@@ -82,8 +82,17 @@ func NetworkPermissionsCheck(username string, r *http.Request) error {
 		}
 	}
 	for groupID := range user.UserGroups {
+
 		userG, err := GetUserGroup(groupID)
 		if err == nil {
+			if netRoles, ok := userG.NetworkRoles[models.AllNetworks]; ok {
+				for netRoleID := range netRoles {
+					err = checkNetworkAccessPermissions(netRoleID, username, r.Method, targetRsrc, targetRsrcID, netID)
+					if err == nil {
+						return nil
+					}
+				}
+			}
 			netRoles := userG.NetworkRoles[models.NetworkID(netID)]
 			for netRoleID := range netRoles {
 				err = checkNetworkAccessPermissions(netRoleID, username, r.Method, targetRsrc, targetRsrcID, netID)

+ 187 - 21
pro/logic/user_mgmt.go

@@ -4,6 +4,7 @@ import (
 	"encoding/json"
 	"errors"
 	"fmt"
+	"time"
 
 	"github.com/gravitl/netmaker/database"
 	"github.com/gravitl/netmaker/logger"
@@ -29,6 +30,8 @@ var PlatformUserUserPermissionTemplate = models.UserRolePermissionTemplate{
 
 var NetworkAdminAllPermissionTemplate = models.UserRolePermissionTemplate{
 	ID:         models.UserRoleID(fmt.Sprintf("global-%s", models.NetworkAdmin)),
+	Name:       "Network Admins",
+	MetaData:   "Can manage all your networks configuration including adding and removing devices.",
 	Default:    true,
 	FullAccess: true,
 	NetworkID:  models.AllNetworks,
@@ -36,6 +39,8 @@ var NetworkAdminAllPermissionTemplate = models.UserRolePermissionTemplate{
 
 var NetworkUserAllPermissionTemplate = models.UserRolePermissionTemplate{
 	ID:         models.UserRoleID(fmt.Sprintf("global-%s", models.NetworkUser)),
+	Name:       "Network Users",
+	MetaData:   "Cannot access the admin console, but can connect to nodes in your networks via Remote Access Client.",
 	Default:    true,
 	FullAccess: false,
 	NetworkID:  models.AllNetworks,
@@ -74,12 +79,44 @@ func UserRolesInit() {
 
 }
 
+func UserGroupsInit() {
+	// create default network groups
+	var NetworkGlobalAdminGroup = models.UserGroup{
+		ID:       models.UserGroupID(fmt.Sprintf("global-%s-grp", models.NetworkAdmin)),
+		Default:  true,
+		Name:     "All Networks Admin Group",
+		MetaData: "Can manage all your networks configuration.",
+		NetworkRoles: map[models.NetworkID]map[models.UserRoleID]struct{}{
+			models.AllNetworks: {
+				models.UserRoleID(fmt.Sprintf("global-%s", models.NetworkAdmin)): {},
+			},
+		},
+	}
+	var NetworkGlobalUserGroup = models.UserGroup{
+		ID:      models.UserGroupID(fmt.Sprintf("global-%s-grp", models.NetworkUser)),
+		Name:    "All Networks User Group",
+		Default: true,
+		NetworkRoles: map[models.NetworkID]map[models.UserRoleID]struct{}{
+			models.NetworkID(models.AllNetworks): {
+				models.UserRoleID(fmt.Sprintf("global-%s", models.NetworkUser)): {},
+			},
+		},
+		MetaData: "Cannot access the admin console, but can connect to nodes in your networks via Remote Access Client.",
+	}
+	d, _ := json.Marshal(NetworkGlobalAdminGroup)
+	database.Insert(NetworkGlobalAdminGroup.ID.String(), string(d), database.USER_GROUPS_TABLE_NAME)
+	d, _ = json.Marshal(NetworkGlobalUserGroup)
+	database.Insert(NetworkGlobalUserGroup.ID.String(), string(d), database.USER_GROUPS_TABLE_NAME)
+}
+
 func CreateDefaultNetworkRolesAndGroups(netID models.NetworkID) {
 	if netID.String() == "" {
 		return
 	}
 	var NetworkAdminPermissionTemplate = models.UserRolePermissionTemplate{
 		ID:                 models.UserRoleID(fmt.Sprintf("%s-%s", netID, models.NetworkAdmin)),
+		Name:               fmt.Sprintf("%s Admin", netID),
+		MetaData:           fmt.Sprintf("Can manage your network `%s` configuration.", netID),
 		Default:            true,
 		NetworkID:          netID,
 		FullAccess:         true,
@@ -88,6 +125,8 @@ func CreateDefaultNetworkRolesAndGroups(netID models.NetworkID) {
 
 	var NetworkUserPermissionTemplate = models.UserRolePermissionTemplate{
 		ID:                  models.UserRoleID(fmt.Sprintf("%s-%s", netID, models.NetworkUser)),
+		Name:                fmt.Sprintf("%s User", netID),
+		MetaData:            fmt.Sprintf("Cannot access the admin console, but can connect to nodes in your network `%s` via Remote Access Client.", netID),
 		Default:             true,
 		FullAccess:          false,
 		NetworkID:           netID,
@@ -117,27 +156,30 @@ func CreateDefaultNetworkRolesAndGroups(netID models.NetworkID) {
 
 	// create default network groups
 	var NetworkAdminGroup = models.UserGroup{
-		ID: models.UserGroupID(fmt.Sprintf("%s-%s-grp", netID, models.NetworkAdmin)),
+		ID:   models.UserGroupID(fmt.Sprintf("%s-%s-grp", netID, models.NetworkAdmin)),
+		Name: fmt.Sprintf("%s Admin Group", netID),
 		NetworkRoles: map[models.NetworkID]map[models.UserRoleID]struct{}{
 			netID: {
 				models.UserRoleID(fmt.Sprintf("%s-%s", netID, models.NetworkAdmin)): {},
 			},
 		},
-		MetaData: "The network group was automatically created by Netmaker.",
+		MetaData: fmt.Sprintf("Can manage your network `%s` configuration including adding and removing devices.", netID),
 	}
 	var NetworkUserGroup = models.UserGroup{
-		ID: models.UserGroupID(fmt.Sprintf("%s-%s-grp", netID, models.NetworkUser)),
+		ID:   models.UserGroupID(fmt.Sprintf("%s-%s-grp", netID, models.NetworkUser)),
+		Name: fmt.Sprintf("%s User Group", netID),
 		NetworkRoles: map[models.NetworkID]map[models.UserRoleID]struct{}{
 			netID: {
 				models.UserRoleID(fmt.Sprintf("%s-%s", netID, models.NetworkUser)): {},
 			},
 		},
-		MetaData: "The network group was automatically created by Netmaker.",
+		MetaData: fmt.Sprintf("Cannot access the admin console, but can connect to nodes in your network `%s` via Remote Access Client.", netID),
 	}
 	d, _ = json.Marshal(NetworkAdminGroup)
 	database.Insert(NetworkAdminGroup.ID.String(), string(d), database.USER_GROUPS_TABLE_NAME)
 	d, _ = json.Marshal(NetworkUserGroup)
 	database.Insert(NetworkUserGroup.ID.String(), string(d), database.USER_GROUPS_TABLE_NAME)
+
 }
 
 func DeleteNetworkRoles(netID string) {
@@ -512,28 +554,46 @@ func HasNetworkRsrcScope(permissionTemplate models.UserRolePermissionTemplate, n
 	return ok
 }
 
-func DoesUserHaveAccessToRAGNode(user models.User, node models.Node) bool {
-	userGwAccessScope := GetUserNetworkRolesWithRemoteVPNAccess(user)
-	logger.Log(3, fmt.Sprintf("User Gw Access Scope: %+v", userGwAccessScope))
-	_, allNetAccess := userGwAccessScope["*"]
-	if node.IsIngressGateway && !node.PendingDelete {
-		if allNetAccess {
-			return true
-		} else {
-			gwRsrcMap := userGwAccessScope[models.NetworkID(node.Network)]
-			scope, ok := gwRsrcMap[models.AllRemoteAccessGwRsrcID]
-			if !ok {
-				if scope, ok = gwRsrcMap[models.RsrcID(node.ID.String())]; !ok {
-					return false
-				}
+func GetUserRAGNodesV1(user models.User) (gws map[string]models.Node) {
+	gws = make(map[string]models.Node)
+	nodes, err := logic.GetAllNodes()
+	if err != nil {
+		return
+	}
+	if user.PlatformRoleID == models.AdminRole || user.PlatformRoleID == models.SuperAdminRole {
+		for _, node := range nodes {
+			if node.IsIngressGateway {
+				gws[node.ID.String()] = node
 			}
-			if scope.VPNaccess {
-				return true
+
+		}
+	}
+	tagNodesMap := logic.GetTagMapWithNodes()
+	accessPolices := logic.ListUserPolicies(user)
+	for _, policyI := range accessPolices {
+		if !policyI.Enabled {
+			continue
+		}
+		for _, dstI := range policyI.Dst {
+			if dstI.Value == "*" {
+				networkNodes := logic.GetNetworkNodesMemory(nodes, policyI.NetworkID.String())
+				for _, node := range networkNodes {
+					if node.IsIngressGateway {
+						gws[node.ID.String()] = node
+					}
+				}
 			}
+			if nodes, ok := tagNodesMap[models.TagID(dstI.Value)]; ok {
+				for _, node := range nodes {
+					if node.IsIngressGateway {
+						gws[node.ID.String()] = node
+					}
 
+				}
+			}
 		}
 	}
-	return false
+	return
 }
 
 func GetUserRAGNodes(user models.User) (gws map[string]models.Node) {
@@ -821,6 +881,16 @@ func IsGroupsValid(groups map[models.UserGroupID]struct{}) error {
 	return nil
 }
 
+func IsGroupValid(groupID models.UserGroupID) error {
+
+	_, err := GetUserGroup(groupID)
+	if err != nil {
+		return fmt.Errorf("user group `%s` not found", groupID)
+	}
+
+	return nil
+}
+
 func IsNetworkRolesValid(networkRoles map[models.NetworkID]map[models.UserRoleID]struct{}) error {
 	for netID, netRoles := range networkRoles {
 
@@ -1065,3 +1135,99 @@ func UpdateUserGwAccess(currentUser, changeUser models.User) {
 	}
 
 }
+
+func CreateDefaultUserPolicies(netID models.NetworkID) {
+	if netID.String() == "" {
+		return
+	}
+
+	if !logic.IsAclExists(fmt.Sprintf("%s.%s-grp", netID, models.NetworkAdmin)) {
+		defaultUserAcl := models.Acl{
+			ID:        fmt.Sprintf("%s.%s-grp", netID, models.NetworkAdmin),
+			Name:      "Network Admin",
+			MetaData:  "This Policy allows all network admins to communicate with all remote access gateways",
+			Default:   true,
+			NetworkID: netID,
+			RuleType:  models.UserPolicy,
+			Src: []models.AclPolicyTag{
+				{
+					ID:    models.UserGroupAclID,
+					Value: fmt.Sprintf("%s-%s-grp", netID, models.NetworkAdmin),
+				},
+				{
+					ID:    models.UserGroupAclID,
+					Value: fmt.Sprintf("global-%s-grp", models.NetworkAdmin),
+				},
+			},
+			Dst: []models.AclPolicyTag{
+				{
+					ID:    models.DeviceAclID,
+					Value: fmt.Sprintf("%s.%s", netID, models.RemoteAccessTagName),
+				}},
+			AllowedDirection: models.TrafficDirectionUni,
+			Enabled:          true,
+			CreatedBy:        "auto",
+			CreatedAt:        time.Now().UTC(),
+		}
+		logic.InsertAcl(defaultUserAcl)
+	}
+
+	if !logic.IsAclExists(fmt.Sprintf("%s.%s-grp", netID, models.NetworkUser)) {
+		defaultUserAcl := models.Acl{
+			ID:        fmt.Sprintf("%s.%s-grp", netID, models.NetworkUser),
+			Name:      "Network User",
+			MetaData:  "This Policy allows all network users to communicate with all remote access gateways",
+			Default:   true,
+			NetworkID: netID,
+			RuleType:  models.UserPolicy,
+			Src: []models.AclPolicyTag{
+				{
+					ID:    models.UserGroupAclID,
+					Value: fmt.Sprintf("%s-%s-grp", netID, models.NetworkUser),
+				},
+				{
+					ID:    models.UserGroupAclID,
+					Value: fmt.Sprintf("global-%s-grp", models.NetworkUser),
+				},
+			},
+
+			Dst: []models.AclPolicyTag{
+				{
+					ID:    models.DeviceAclID,
+					Value: fmt.Sprintf("%s.%s", netID, models.RemoteAccessTagName),
+				}},
+			AllowedDirection: models.TrafficDirectionUni,
+			Enabled:          true,
+			CreatedBy:        "auto",
+			CreatedAt:        time.Now().UTC(),
+		}
+		logic.InsertAcl(defaultUserAcl)
+	}
+
+}
+
+func GetUserGroupsInNetwork(netID models.NetworkID) (networkGrps map[models.UserGroupID]models.UserGroup) {
+	groups, _ := ListUserGroups()
+	networkGrps = make(map[models.UserGroupID]models.UserGroup)
+	for _, grp := range groups {
+		if _, ok := grp.NetworkRoles[models.AllNetworks]; ok {
+			networkGrps[grp.ID] = grp
+			continue
+		}
+		if _, ok := grp.NetworkRoles[netID]; ok {
+			networkGrps[grp.ID] = grp
+		}
+	}
+	return
+}
+
+func AddGlobalNetRolesToAdmins(u models.User) {
+	if u.PlatformRoleID != models.SuperAdminRole && u.PlatformRoleID != models.AdminRole {
+		return
+	}
+	if u.UserGroups == nil {
+		u.UserGroups = make(map[models.UserGroupID]struct{})
+	}
+	u.UserGroups[models.UserGroupID(fmt.Sprintf("global-%s-grp", models.NetworkAdmin))] = struct{}{}
+	logic.UpsertUser(u)
+}