Browse Source

resolve merge conflicts

abhishek9686 1 year ago
parent
commit
27ceaed739

+ 1 - 1
.github/workflows/docs.yml

@@ -36,7 +36,7 @@ jobs:
         run: echo "timestamp=$(date +'%Y-%m-%d %H:%M:%S')" >> $GITHUB_OUTPUT
 
       - name: Create Pull Request
-        uses: peter-evans/create-pull-request@v5
+        uses: peter-evans/create-pull-request@v6
         with:
           token: ${{ secrets.GITHUB_TOKEN }}
           commit-message: "Update documentation ${{ steps.timestamp.outputs.timestamp }}"

+ 1 - 1
.github/workflows/test.yml

@@ -70,5 +70,5 @@ jobs:
       - name: run static checks
         run: |
           sudo apt update
-          go install honnef.co/go/tools/cmd/staticcheck@latest
+          go install honnef.co/go/tools/cmd/staticcheck@v0.4.7
           { ~/go/bin/staticcheck  -tags=ee ./... ; }

+ 1 - 1
Dockerfile

@@ -6,7 +6,7 @@ COPY . .
 
 RUN GOOS=linux CGO_ENABLED=1 go build -ldflags="-s -w " -tags ${tags} .
 # RUN go build -tags=ee . -o netmaker main.go
-FROM alpine:3.20.0
+FROM alpine:3.20.2
 
 # add a c lib
 # set the working directory

+ 1 - 1
Dockerfile-quick

@@ -1,5 +1,5 @@
 #first stage - builder
-FROM alpine:3.20.0
+FROM alpine:3.20.2
 ARG version 
 WORKDIR /app
 COPY ./netmaker /root/netmaker

+ 1 - 7
controllers/ext_client.go

@@ -657,13 +657,7 @@ func updateExtClient(w http.ResponseWriter, r *http.Request) {
 					slog.Error("Failed to get nodes", "error", err)
 					return
 				}
-				go mq.PublishSingleHostPeerUpdate(
-					ingressHost,
-					nodes,
-					nil,
-					[]models.ExtClient{oldExtClient},
-					false,
-				)
+				go mq.PublishSingleHostPeerUpdate(ingressHost, nodes, nil, []models.ExtClient{oldExtClient}, false, nil)
 			}
 		}
 

+ 6 - 3
controllers/hosts.go

@@ -79,7 +79,7 @@ func upgradeHost(w http.ResponseWriter, r *http.Request) {
 // @Success     200 {array} models.ApiHost
 // @Failure     500 {object} models.ErrorResponse
 func getHosts(w http.ResponseWriter, r *http.Request) {
-
+	w.Header().Set("Content-Type", "application/json")
 	currentHosts := []models.Host{}
 	username := r.Header.Get("user")
 	user, err := logic.GetUser(username)
@@ -90,7 +90,7 @@ func getHosts(w http.ResponseWriter, r *http.Request) {
 	if err != nil {
 		return
 	}
-
+	respHostsMap := make(map[string]struct{})
 	if !userPlatformRole.FullAccess {
 		nodes, err := logic.GetAllNodes()
 		if err != nil {
@@ -107,9 +107,12 @@ func getHosts(w http.ResponseWriter, r *http.Request) {
 				return
 			}
 			for _, node := range filteredNodes {
+				if _, ok := respHostsMap[node.HostID.String()]; ok {
+					continue
+				}
 				if host, ok := currentHostsMap[node.HostID.String()]; ok {
 					currentHosts = append(currentHosts, host)
-					delete(currentHostsMap, host.ID.String())
+					respHostsMap[host.ID.String()] = struct{}{}
 				}
 			}
 

+ 2 - 8
controllers/network.go

@@ -340,14 +340,8 @@ func updateNetworkACLv2(w http.ResponseWriter, r *http.Request) {
 		}
 		for hostId, clients := range assocClientsToDisconnectPerHost {
 			if host, ok := hostsMap[hostId]; ok {
-				if err = mq.PublishSingleHostPeerUpdate(&host, allNodes, nil, clients, false); err != nil {
-					slog.Error(
-						"failed to publish peer update to ingress after ACL update on network",
-						"network",
-						netname,
-						"host",
-						hostId,
-					)
+				if err = mq.PublishSingleHostPeerUpdate(&host, allNodes, nil, clients, false, nil); err != nil {
+					slog.Error("failed to publish peer update to ingress after ACL update on network", "network", netname, "host", hostId)
 				}
 			}
 		}

+ 1 - 1
controllers/node.go

@@ -624,7 +624,7 @@ func deleteIngressGateway(w http.ResponseWriter, r *http.Request) {
 				return
 			}
 			go func() {
-				if err := mq.PublishSingleHostPeerUpdate(host, allNodes, nil, removedClients[:], false); err != nil {
+				if err := mq.PublishSingleHostPeerUpdate(host, allNodes, nil, removedClients[:], false, nil); err != nil {
 					slog.Error("publishSingleHostUpdate", "host", host.Name, "error", err)
 				}
 				if err := mq.NodeUpdate(&node); err != nil {

+ 6 - 2
controllers/user.go

@@ -676,10 +676,14 @@ func deleteUser(w http.ResponseWriter, r *http.Request) {
 		}
 		for _, extclient := range extclients {
 			if extclient.OwnerID == user.UserName {
-				err = logic.DeleteExtClient(extclient.Network, extclient.ClientID)
+				err = logic.DeleteExtClientAndCleanup(extclient)
 				if err != nil {
 					slog.Error("failed to delete extclient",
-						"id", extclient.ClientID, "owner", user.UserName, "error", err)
+						"id", extclient.ClientID, "owner", username, "error", err)
+				} else {
+					if err := mq.PublishDeletedClientPeerUpdate(&extclient); err != nil {
+						slog.Error("error setting ext peers: " + err.Error())
+					}
 				}
 			}
 		}

+ 5 - 6
go.mod

@@ -3,7 +3,7 @@ module github.com/gravitl/netmaker
 go 1.19
 
 require (
-	github.com/eclipse/paho.mqtt.golang v1.4.3
+	github.com/eclipse/paho.mqtt.golang v1.5.0
 	github.com/go-playground/validator/v10 v10.22.0
 	github.com/golang-jwt/jwt/v4 v4.5.0
 	github.com/google/uuid v1.6.0
@@ -16,10 +16,10 @@ require (
 	github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e
 	github.com/stretchr/testify v1.9.0
 	github.com/txn2/txeh v1.5.5
-	golang.org/x/crypto v0.23.0
-	golang.org/x/net v0.22.0 // indirect
+	golang.org/x/crypto v0.25.0
+	golang.org/x/net v0.27.0 // indirect
 	golang.org/x/oauth2 v0.21.0
-	golang.org/x/sys v0.21.0 // indirect
+	golang.org/x/sys v0.22.0 // indirect
 	golang.org/x/text v0.16.0 // indirect
 	golang.zx2c4.com/wireguard/wgctrl v0.0.0-20221104135756-97bc4ad4a1cb
 	gopkg.in/yaml.v3 v3.0.1
@@ -28,7 +28,7 @@ require (
 require (
 	filippo.io/edwards25519 v1.1.0
 	github.com/c-robinson/iplib v1.0.8
-	github.com/posthog/posthog-go v0.0.0-20211028072449-93c17c49e2b0
+	github.com/posthog/posthog-go v1.2.18
 )
 
 require (
@@ -66,6 +66,5 @@ require (
 	github.com/leodido/go-urn v1.4.0 // indirect
 	github.com/mattn/go-runewidth v0.0.13 // indirect
 	github.com/pmezard/go-difflib v1.0.0 // indirect
-	github.com/xtgo/uuid v0.0.0-20140804021211-a0b114877d4c // indirect
 	golang.org/x/sync v0.7.0 // indirect
 )

+ 10 - 18
go.sum

@@ -2,18 +2,16 @@ cloud.google.com/go/compute/metadata v0.3.0 h1:Tz+eQXMEqDIKRsmY3cHTL6FVaynIjX2Qx
 cloud.google.com/go/compute/metadata v0.3.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k=
 filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
 filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
-github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
 github.com/c-robinson/iplib v1.0.8 h1:exDRViDyL9UBLcfmlxxkY5odWX5092nPsQIykHXhIn4=
 github.com/c-robinson/iplib v1.0.8/go.mod h1:i3LuuFL1hRT5gFpBRnEydzw8R6yhGkF4szNDIbF8pgo=
 github.com/coreos/go-oidc/v3 v3.9.0 h1:0J/ogVOd4y8P0f0xUh8l9t07xRP/d8tccvjHl2dcsSo=
 github.com/coreos/go-oidc/v3 v3.9.0/go.mod h1:rTKz2PYwftcrtoCzV5g5kvfJoWcm0Mk8AF8y1iAQro4=
-github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
 github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
-github.com/eclipse/paho.mqtt.golang v1.4.3 h1:2kwcUGn8seMUfWndX0hGbvH8r7crgcJguQNCyp70xik=
-github.com/eclipse/paho.mqtt.golang v1.4.3/go.mod h1:CSYvoAlsMkhYOXh/oKyxa8EcBci6dVkLCbo5tTC1RIE=
+github.com/eclipse/paho.mqtt.golang v1.5.0 h1:EH+bUVJNgttidWFkLLVKaQPGmkTUfQQqjOsyvMGvD6o=
+github.com/eclipse/paho.mqtt.golang v1.5.0/go.mod h1:du/2qNQVqJf/Sqs4MEL77kR8QTqANF7XU7Fk0aOTAgk=
 github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk=
 github.com/felixge/httpsnoop v1.0.3/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
 github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
@@ -61,8 +59,8 @@ github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N
 github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
 github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
-github.com/posthog/posthog-go v0.0.0-20211028072449-93c17c49e2b0 h1:Y2hUrkfuM0on62KZOci/VLijlkdF/yeWU262BQgvcjE=
-github.com/posthog/posthog-go v0.0.0-20211028072449-93c17c49e2b0/go.mod h1:oa2sAs9tGai3VldabTV0eWejt/O4/OOD7azP8GaikqU=
+github.com/posthog/posthog-go v1.2.18 h1:2CBA0LOB0up+gon+xpeXuhFw69gZpjAYxQoBBGwiDWw=
+github.com/posthog/posthog-go v1.2.18/go.mod h1:QjlpryJtfYLrZF2GUkAhejH4E7WlDbdKkvOi5hLmkdg=
 github.com/resendlabs/resend-go v1.7.0 h1:DycOqSXtw2q7aB+Nt9DDJUDtaYcrNPGn1t5RFposas0=
 github.com/resendlabs/resend-go v1.7.0/go.mod h1:yip1STH7Bqfm4fD0So5HgyNbt5taG5Cplc4xXxETyLI=
 github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
@@ -70,13 +68,11 @@ github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
 github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
 github.com/rqlite/gorqlite v0.0.0-20240122221808-a8a425b1a6aa h1:hxMLFbj+F444JAS5nUQxTDZwUxwCRqg3WkNqhiDzXrM=
 github.com/rqlite/gorqlite v0.0.0-20240122221808-a8a425b1a6aa/go.mod h1:xF/KoXmrRyahPfo5L7Szb5cAAUl53dMWBh9cMruGEZg=
-github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
 github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
 github.com/seancfoley/bintree v1.3.1 h1:cqmmQK7Jm4aw8gna0bP+huu5leVOgHGSJBEpUx3EXGI=
 github.com/seancfoley/bintree v1.3.1/go.mod h1:hIUabL8OFYyFVTQ6azeajbopogQc2l5C/hiXMcemWNU=
 github.com/seancfoley/ipaddress-go v1.6.0 h1:9z7yGmOnV4P2ML/dlR/kCJiv5tp8iHOOetJvxJh/R5w=
 github.com/seancfoley/ipaddress-go v1.6.0/go.mod h1:TQRZgv+9jdvzHmKoPGBMxyiaVmoI0rYpfEk8Q/sL/Iw=
-github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
 github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e h1:MRM5ITcdelLK2j1vwZ3Je0FKVCfqOLp5zO6trqMLYs0=
 github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e/go.mod h1:XV66xRDqSt+GTGFMVlhk3ULuV0y9ZmzeVGR4mloJI3M=
 github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM=
@@ -89,15 +85,12 @@ github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsT
 github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
 github.com/txn2/txeh v1.5.5 h1:UN4e/lCK5HGw/gGAi2GCVrNKg0GTCUWs7gs5riaZlz4=
 github.com/txn2/txeh v1.5.5/go.mod h1:qYzGG9kCzeVEI12geK4IlanHWY8X4uy/I3NcW7mk8g4=
-github.com/urfave/cli v1.22.5/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
-github.com/xtgo/uuid v0.0.0-20140804021211-a0b114877d4c h1:3lbZUMbMiGUW/LMkfsEABsc5zNT9+b1CvsJx47JzJ8g=
-github.com/xtgo/uuid v0.0.0-20140804021211-a0b114877d4c/go.mod h1:UrdRz5enIKZ63MEE3IF9l2/ebyx59GyGgPi+tICQdmM=
 github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
 golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
 golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
-golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI=
-golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
+golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30=
+golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M=
 golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 h1:k/i9J1pBpvlfR+9QsetwPyERsqu1GIbi967PQMq3Ivc=
 golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w=
 golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
@@ -107,8 +100,8 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v
 golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
 golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
 golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
-golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc=
-golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
+golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys=
+golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE=
 golang.org/x/oauth2 v0.21.0 h1:tsimM75w1tF/uws5rbeHzIWxEqElMehnc+iW793zsZs=
 golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
 golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -124,8 +117,8 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBc
 golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
-golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws=
-golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=
+golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
 golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
@@ -152,7 +145,6 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/mail.v2 v2.3.1 h1:WYFn/oANrAGP2C0dcV6/pbkPzv8yGzqTjPmTeO7qoXk=
 gopkg.in/mail.v2 v2.3.1/go.mod h1:htwXN1Qh09vZJ1NVKxQqHPBaCBbzKhp5GzuJEA4VJWw=
-gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
 gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

+ 2 - 0
logic/auth.go

@@ -295,6 +295,8 @@ func UpdateUser(userchange, user *models.User) (*models.User, error) {
 	if err := IsNetworkRolesValid(userchange.NetworkRoles); err != nil {
 		return userchange, errors.New("invalid network roles: " + err.Error())
 	}
+	// Reset Gw Access for service users
+	go UpdateUserGwAccess(*user, *userchange)
 	user.PlatformRoleID = userchange.PlatformRoleID
 	user.UserGroups = userchange.UserGroups
 	user.NetworkRoles = userchange.NetworkRoles

+ 2 - 0
logic/user_mgmt.go

@@ -43,6 +43,8 @@ var IsNetworkRolesValid = func(networkRoles map[models.NetworkID]map[models.User
 	return nil
 }
 
+var UpdateUserGwAccess = func(currentUser, changeUser models.User) {}
+
 var UpdateRole = func(r models.UserRolePermissionTemplate) error { return nil }
 
 var InitialiseRoles = userRolesInit

+ 16 - 0
logic/users.go

@@ -129,6 +129,22 @@ func ListPendingUsers() ([]models.ReturnUser, error) {
 	return pendingUsers, nil
 }
 
+func GetUserMap() (map[string]models.User, error) {
+	userMap := make(map[string]models.User)
+	records, err := database.FetchRecords(database.USERS_TABLE_NAME)
+	if err != nil && !database.IsEmptyRecord(err) {
+		return userMap, err
+	}
+	for _, record := range records {
+		u := models.User{}
+		err = json.Unmarshal([]byte(record), &u)
+		if err == nil {
+			userMap[u.UserName] = u
+		}
+	}
+	return userMap, nil
+}
+
 func InsertUserInvite(invite models.UserInvite) error {
 	data, err := json.Marshal(invite)
 	if err != nil {

+ 2 - 2
mq/handlers.go

@@ -65,7 +65,7 @@ func UpdateNode(client mqtt.Client, msg mqtt.Message) {
 			}
 			allNodes, err := logic.GetAllNodes()
 			if err == nil {
-				PublishSingleHostPeerUpdate(host, allNodes, nil, nil, false)
+				PublishSingleHostPeerUpdate(host, allNodes, nil, nil, false, nil)
 			}
 		} else {
 			err = PublishPeerUpdate(false)
@@ -117,7 +117,7 @@ func UpdateHost(client mqtt.Client, msg mqtt.Message) {
 				if err != nil {
 					return
 				}
-				if err = PublishSingleHostPeerUpdate(currentHost, nodes, nil, nil, false); err != nil {
+				if err = PublishSingleHostPeerUpdate(currentHost, nodes, nil, nil, false, nil); err != nil {
 					slog.Error("failed peers publish after join acknowledged", "name", hostUpdate.Host.Name, "id", currentHost.ID, "error", err)
 					return
 				}

+ 40 - 11
mq/publishers.go

@@ -4,6 +4,7 @@ import (
 	"encoding/json"
 	"errors"
 	"fmt"
+	"sync"
 	"time"
 
 	"github.com/gravitl/netmaker/logger"
@@ -13,6 +14,9 @@ import (
 	"golang.org/x/exp/slog"
 )
 
+var batchSize = servercfg.GetPeerUpdateBatchSize()
+var batchUpdate = servercfg.GetBatchPeerUpdate()
+
 // PublishPeerUpdate --- determines and publishes a peer update to all the hosts
 func PublishPeerUpdate(replacePeers bool) error {
 	if !servercfg.IsMessageQueueBackend() {
@@ -28,15 +32,37 @@ func PublishPeerUpdate(replacePeers bool) error {
 	if err != nil {
 		return err
 	}
-	for _, host := range hosts {
-		host := host
-		go func(host models.Host) {
-			if err = PublishSingleHostPeerUpdate(&host, allNodes, nil, nil, replacePeers); err != nil {
-				logger.Log(1, "failed to publish peer update to host", host.ID.String(), ": ", err.Error())
-			}
-		}(host)
+
+	//if batch peer update disabled
+	if !batchUpdate {
+		for _, host := range hosts {
+			host := host
+			go func(host models.Host) {
+				if err = PublishSingleHostPeerUpdate(&host, allNodes, nil, nil, replacePeers, nil); err != nil {
+					logger.Log(1, "failed to publish peer update to host", host.ID.String(), ": ", err.Error())
+				}
+			}(host)
+		}
+		return nil
 	}
-	return err
+
+	//if batch peer update enabled
+	batchHost := BatchItems(hosts, batchSize)
+	var wg sync.WaitGroup
+	for _, v := range batchHost {
+		hostLen := len(v)
+		wg.Add(hostLen)
+		for i := 0; i < hostLen; i++ {
+			host := hosts[i]
+			go func(host models.Host) {
+				if err = PublishSingleHostPeerUpdate(&host, allNodes, nil, nil, replacePeers, &wg); err != nil {
+					logger.Log(1, "failed to publish peer update to host", host.ID.String(), ": ", err.Error())
+				}
+			}(host)
+		}
+		wg.Wait()
+	}
+	return nil
 }
 
 // PublishDeletedNodePeerUpdate --- determines and publishes a peer update
@@ -57,7 +83,7 @@ func PublishDeletedNodePeerUpdate(delNode *models.Node) error {
 	}
 	for _, host := range hosts {
 		host := host
-		if err = PublishSingleHostPeerUpdate(&host, allNodes, delNode, nil, false); err != nil {
+		if err = PublishSingleHostPeerUpdate(&host, allNodes, delNode, nil, false, nil); err != nil {
 			logger.Log(1, "failed to publish peer update to host", host.ID.String(), ": ", err.Error())
 		}
 	}
@@ -83,7 +109,7 @@ func PublishDeletedClientPeerUpdate(delClient *models.ExtClient) error {
 	for _, host := range hosts {
 		host := host
 		if host.OS != models.OS_Types.IoT {
-			if err = PublishSingleHostPeerUpdate(&host, nodes, nil, []models.ExtClient{*delClient}, false); err != nil {
+			if err = PublishSingleHostPeerUpdate(&host, nodes, nil, []models.ExtClient{*delClient}, false, nil); err != nil {
 				logger.Log(1, "failed to publish peer update to host", host.ID.String(), ": ", err.Error())
 			}
 		}
@@ -92,7 +118,10 @@ func PublishDeletedClientPeerUpdate(delClient *models.ExtClient) error {
 }
 
 // PublishSingleHostPeerUpdate --- determines and publishes a peer update to one host
-func PublishSingleHostPeerUpdate(host *models.Host, allNodes []models.Node, deletedNode *models.Node, deletedClients []models.ExtClient, replacePeers bool) error {
+func PublishSingleHostPeerUpdate(host *models.Host, allNodes []models.Node, deletedNode *models.Node, deletedClients []models.ExtClient, replacePeers bool, wg *sync.WaitGroup) error {
+	if wg != nil {
+		defer wg.Done()
+	}
 	peerUpdate, err := logic.GetPeerUpdateForHost("", host, allNodes, deletedNode, deletedClients)
 	if err != nil {
 		return err

+ 21 - 0
mq/util.go

@@ -3,6 +3,7 @@ package mq
 import (
 	"errors"
 	"fmt"
+	"math"
 	"strings"
 	"time"
 
@@ -45,6 +46,26 @@ func DecryptMsg(node *models.Node, msg []byte) ([]byte, error) {
 	return decryptMsgWithHost(host, msg)
 }
 
+func BatchItems[T any](items []T, batchSize int) [][]T {
+	if batchSize <= 0 {
+		return nil
+	}
+	remainderBatchSize := len(items) % batchSize
+	nBatches := int(math.Ceil(float64(len(items)) / float64(batchSize)))
+	batches := make([][]T, nBatches)
+	for i := range batches {
+		if i == nBatches-1 && remainderBatchSize > 0 {
+			batches[i] = make([]T, remainderBatchSize)
+		} else {
+			batches[i] = make([]T, batchSize)
+		}
+		for j := range batches[i] {
+			batches[i][j] = items[i*batchSize+j]
+		}
+	}
+	return batches
+}
+
 func encryptMsg(host *models.Host, msg []byte) ([]byte, error) {
 	if host.OS == models.OS_Types.IoT {
 		return msg, nil

+ 2 - 8
pro/controllers/relay.go

@@ -130,14 +130,8 @@ func deleteRelay(w http.ResponseWriter, r *http.Request) {
 						return
 					}
 					node.IsRelay = true // for iot update to recognise that it has to delete relay peer
-					if err = mq.PublishSingleHostPeerUpdate(h, nodes, &node, nil, false); err != nil {
-						logger.Log(
-							1,
-							"failed to publish peer update to host",
-							h.ID.String(),
-							": ",
-							err.Error(),
-						)
+					if err = mq.PublishSingleHostPeerUpdate(h, nodes, &node, nil, false, nil); err != nil {
+						logger.Log(1, "failed to publish peer update to host", h.ID.String(), ": ", err.Error())
 					}
 				}
 			}

+ 77 - 274
pro/controllers/users.go

@@ -174,6 +174,10 @@ func inviteUsers(w http.ResponseWriter, r *http.Request) {
 		logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("super admin cannot be invited"), "badrequest"))
 		return
 	}
+	if inviteReq.PlatformRoleID == "" {
+		logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("platform role id cannot be empty"), "badrequest"))
+		return
+	}
 	if (inviteReq.PlatformRoleID == models.AdminRole.String() ||
 		inviteReq.PlatformRoleID == models.SuperAdminRole.String()) && caller.PlatformRoleID != models.SuperAdminRole {
 		logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("only superadmin can invite admin users"), "forbidden"))
@@ -429,6 +433,12 @@ func updateUserGroup(w http.ResponseWriter, r *http.Request) {
 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
 		return
 	}
+	// fetch curr group
+	currUserG, err := proLogic.GetUserGroup(userGroup.ID)
+	if err != nil {
+		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
+		return
+	}
 	err = proLogic.ValidateUpdateGroupReq(userGroup)
 	if err != nil {
 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
@@ -439,6 +449,8 @@ func updateUserGroup(w http.ResponseWriter, r *http.Request) {
 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
 		return
 	}
+	// reset configs for service user
+	go proLogic.UpdatesUserGwAccessOnGrpUpdates(currUserG.NetworkRoles, userGroup.NetworkRoles)
 	logic.ReturnSuccessResponseWithJson(w, r, userGroup, "updated user group")
 }
 
@@ -453,6 +465,13 @@ func updateUserGroup(w http.ResponseWriter, r *http.Request) {
 //
 //			Responses:
 //				200: userBodyResponse
+//
+// @Summary     Delete user group.
+// @Router      /api/v1/user/group [delete]
+// @Tags        Users
+// @Param       group_id param string true "group id required to delete the role"
+// @Success     200 {string} string
+// @Failure     500 {object} models.ErrorResponse
 func deleteUserGroup(w http.ResponseWriter, r *http.Request) {
 
 	gid, _ := url.QueryUnescape(r.URL.Query().Get("group_id"))
@@ -460,25 +479,26 @@ func deleteUserGroup(w http.ResponseWriter, r *http.Request) {
 		logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("role is required"), "badrequest"))
 		return
 	}
-	err := proLogic.DeleteUserGroup(models.UserGroupID(gid))
+	userG, err := proLogic.GetUserGroup(models.UserGroupID(gid))
+	if err != nil {
+		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
 	}
+	go proLogic.UpdatesUserGwAccessOnGrpUpdates(userG.NetworkRoles, make(map[models.NetworkID]map[models.UserRoleID]struct{}))
 	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
+// @Summary     lists all user roles.
+// @Router      /api/v1/user/roles [get]
+// @Tags        Users
+// @Param       role_id param string true "roleid required to get the role details"
+// @Success     200 {object}  []models.UserRolePermissionTemplate
+// @Failure     500 {object} models.ErrorResponse
 func listRoles(w http.ResponseWriter, r *http.Request) {
 	platform, _ := url.QueryUnescape(r.URL.Query().Get("platform"))
 	var roles []models.UserRolePermissionTemplate
@@ -499,17 +519,12 @@ func listRoles(w http.ResponseWriter, r *http.Request) {
 	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
+// @Summary     Get user role permission template.
+// @Router      /api/v1/user/role [get]
+// @Tags        Users
+// @Param       role_id param string true "roleid required to get the role details"
+// @Success     200 {object} models.UserRolePermissionTemplate
+// @Failure     500 {object} models.ErrorResponse
 func getRole(w http.ResponseWriter, r *http.Request) {
 	rid, _ := url.QueryUnescape(r.URL.Query().Get("role_id"))
 	if rid == "" {
@@ -527,17 +542,12 @@ func getRole(w http.ResponseWriter, r *http.Request) {
 	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
+// @Summary     Create user role permission template.
+// @Router      /api/v1/user/role [post]
+// @Tags        Users
+// @Param       body models.UserRolePermissionTemplate true "user role template"
+// @Success     200 {object}  models.UserRolePermissionTemplate
+// @Failure     500 {object} models.ErrorResponse
 func createRole(w http.ResponseWriter, r *http.Request) {
 	var userRole models.UserRolePermissionTemplate
 	err := json.NewDecoder(r.Body).Decode(&userRole)
@@ -562,17 +572,12 @@ func createRole(w http.ResponseWriter, r *http.Request) {
 	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
+// @Summary     Update user role permission template.
+// @Router      /api/v1/user/role [put]
+// @Tags        Users
+// @Param       body models.UserRolePermissionTemplate true "user role template"
+// @Success     200 {object} userBodyResponse
+// @Failure     500 {object} models.ErrorResponse
 func updateRole(w http.ResponseWriter, r *http.Request) {
 	var userRole models.UserRolePermissionTemplate
 	err := json.NewDecoder(r.Body).Decode(&userRole)
@@ -582,6 +587,11 @@ func updateRole(w http.ResponseWriter, r *http.Request) {
 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
 		return
 	}
+	currRole, err := logic.GetRole(userRole.ID)
+	if err != nil {
+		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
+		return
+	}
 	err = proLogic.ValidateUpdateRoleReq(&userRole)
 	if err != nil {
 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
@@ -593,20 +603,17 @@ func updateRole(w http.ResponseWriter, r *http.Request) {
 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
 		return
 	}
+	// reset configs for service user
+	go proLogic.UpdatesUserGwAccessOnRoleUpdates(currRole.NetworkLevelAccess, userRole.NetworkLevelAccess, string(userRole.NetworkID))
 	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
+// @Summary     Delete user role permission template.
+// @Router      /api/v1/user/role [delete]
+// @Tags        Users
+// @Param       role_id param string true "roleid required to delete the role"
+// @Success     200 {string} string
+// @Failure     500 {object} models.ErrorResponse
 func deleteRole(w http.ResponseWriter, r *http.Request) {
 
 	rid, _ := url.QueryUnescape(r.URL.Query().Get("role_id"))
@@ -614,12 +621,18 @@ func deleteRole(w http.ResponseWriter, r *http.Request) {
 		logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("role is required"), "badrequest"))
 		return
 	}
-	err := proLogic.DeleteRole(models.UserRoleID(rid), false)
+	role, err := logic.GetRole(models.UserRoleID(rid))
+	if err != nil {
+		logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("role is required"), "badrequest"))
+		return
+	}
+	err = proLogic.DeleteRole(models.UserRoleID(rid), false)
 	if err != nil {
 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
 		return
 	}
-	logic.ReturnSuccessResponseWithJson(w, r, nil, "created user role")
+	go proLogic.UpdatesUserGwAccessOnRoleUpdates(role.NetworkLevelAccess, make(map[models.RsrcType]map[models.RsrcID]models.RsrcPermissionScope), role.NetworkID.String())
+	logic.ReturnSuccessResponseWithJson(w, r, nil, "deleted user role")
 }
 
 // @Summary     Attach user to a remote access gateway
@@ -791,6 +804,12 @@ func removeUserFromRemoteAccessGW(w http.ResponseWriter, r *http.Request) {
 	json.NewEncoder(w).Encode(logic.ToReturnUser(*user))
 }
 
+// @Summary     Get Users Remote Access Gw.
+// @Router      /api/users/{username}/remote_access_gw [get]
+// @Tags        Users
+// @Param       username path string true "Username to fetch all the gateways with access"
+// @Success     200 {object} map[string][]models.UserRemoteGws
+// @Failure     500 {object} models.ErrorResponse
 func getUserRemoteAccessGwsV1(w http.ResponseWriter, r *http.Request) {
 	// set header.
 	w.Header().Set("Content-Type", "application/json")
@@ -927,222 +946,6 @@ func getUserRemoteAccessGwsV1(w http.ResponseWriter, r *http.Request) {
 	json.NewEncoder(w).Encode(userGws)
 }
 
-// swagger:route GET "/api/users/{username}/remote_access_gw" nodes getUserRemoteAccessGws
-//
-// Get an individual node.
-//
-//			Schemes: https
-//
-//			Security:
-//	  		oauth
-//
-//			Responses:
-//				200: nodeResponse
-func getUserRemoteAccessGws(w http.ResponseWriter, r *http.Request) {
-	// set header.
-	w.Header().Set("Content-Type", "application/json")
-
-	var params = mux.Vars(r)
-	username := params["username"]
-	if username == "" {
-		logic.ReturnErrorResponse(
-			w,
-			r,
-			logic.FormatError(errors.New("required params username"), "badrequest"),
-		)
-		return
-	}
-	remoteAccessClientID := r.URL.Query().Get("remote_access_clientid")
-	var req models.UserRemoteGwsReq
-	if remoteAccessClientID == "" {
-		err := json.NewDecoder(r.Body).Decode(&req)
-		if err != nil {
-			slog.Error("error decoding request body: ", "error", err)
-			logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
-			return
-		}
-	}
-	reqFromMobile := r.URL.Query().Get("from_mobile") == "true"
-	if req.RemoteAccessClientID == "" && remoteAccessClientID == "" {
-		logic.ReturnErrorResponse(
-			w,
-			r,
-			logic.FormatError(errors.New("remote access client id cannot be empty"), "badrequest"),
-		)
-		return
-	}
-	if req.RemoteAccessClientID == "" {
-		req.RemoteAccessClientID = remoteAccessClientID
-	}
-	userGws := make(map[string][]models.UserRemoteGws)
-	user, err := logic.GetUser(username)
-	if err != nil {
-		logger.Log(0, username, "failed to fetch user: ", err.Error())
-		logic.ReturnErrorResponse(
-			w,
-			r,
-			logic.FormatError(
-				fmt.Errorf("failed to fetch user %s, error: %v", username, err),
-				"badrequest",
-			),
-		)
-		return
-	}
-	allextClients, err := logic.GetAllExtClients()
-	if err != nil {
-		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
-		return
-	}
-
-	processedAdminNodeIds := make(map[string]struct{})
-	for _, extClient := range allextClients {
-		if extClient.RemoteAccessClientID == req.RemoteAccessClientID &&
-			extClient.OwnerID == username {
-			node, err := logic.GetNodeByID(extClient.IngressGatewayID)
-			if err != nil {
-				continue
-			}
-			if node.PendingDelete {
-				continue
-			}
-			if !node.IsIngressGateway {
-				continue
-			}
-			host, err := logic.GetHost(node.HostID.String())
-			if err != nil {
-				continue
-			}
-			network, err := logic.GetNetwork(node.Network)
-			if err != nil {
-				slog.Error("failed to get node network", "error", err)
-			}
-
-			if _, ok := user.RemoteGwIDs[node.ID.String()]; (user.PlatformRoleID != models.AdminRole && user.PlatformRoleID != models.SuperAdminRole) && ok {
-				gws := userGws[node.Network]
-				extClient.AllowedIPs = logic.GetExtclientAllowedIPs(extClient)
-				gws = append(gws, models.UserRemoteGws{
-					GwID:              node.ID.String(),
-					GWName:            host.Name,
-					Network:           node.Network,
-					GwClient:          extClient,
-					Connected:         true,
-					IsInternetGateway: node.IsInternetGateway,
-					GwPeerPublicKey:   host.PublicKey.String(),
-					GwListenPort:      logic.GetPeerListenPort(host),
-					Metadata:          node.Metadata,
-					AllowedEndpoints:  getAllowedRagEndpoints(&node, host),
-					NetworkAddresses:  []string{network.AddressRange, network.AddressRange6},
-				})
-				userGws[node.Network] = gws
-				delete(user.RemoteGwIDs, node.ID.String())
-			} else {
-				gws := userGws[node.Network]
-				extClient.AllowedIPs = logic.GetExtclientAllowedIPs(extClient)
-				gws = append(gws, models.UserRemoteGws{
-					GwID:              node.ID.String(),
-					GWName:            host.Name,
-					Network:           node.Network,
-					GwClient:          extClient,
-					Connected:         true,
-					IsInternetGateway: node.IsInternetGateway,
-					GwPeerPublicKey:   host.PublicKey.String(),
-					GwListenPort:      logic.GetPeerListenPort(host),
-					Metadata:          node.Metadata,
-					AllowedEndpoints:  getAllowedRagEndpoints(&node, host),
-					NetworkAddresses:  []string{network.AddressRange, network.AddressRange6},
-				})
-				userGws[node.Network] = gws
-				processedAdminNodeIds[node.ID.String()] = struct{}{}
-			}
-		}
-	}
-
-	// add remaining gw nodes to resp
-	if user.PlatformRoleID != models.AdminRole && user.PlatformRoleID != models.SuperAdminRole {
-		for gwID := range user.RemoteGwIDs {
-			node, err := logic.GetNodeByID(gwID)
-			if err != nil {
-				continue
-			}
-			if !node.IsIngressGateway {
-				continue
-			}
-			if node.PendingDelete {
-				continue
-			}
-			host, err := logic.GetHost(node.HostID.String())
-			if err != nil {
-				continue
-			}
-			network, err := logic.GetNetwork(node.Network)
-			if err != nil {
-				slog.Error("failed to get node network", "error", err)
-			}
-			gws := userGws[node.Network]
-
-			gws = append(gws, models.UserRemoteGws{
-				GwID:              node.ID.String(),
-				GWName:            host.Name,
-				Network:           node.Network,
-				IsInternetGateway: node.IsInternetGateway,
-				GwPeerPublicKey:   host.PublicKey.String(),
-				GwListenPort:      logic.GetPeerListenPort(host),
-				Metadata:          node.Metadata,
-				AllowedEndpoints:  getAllowedRagEndpoints(&node, host),
-				NetworkAddresses:  []string{network.AddressRange, network.AddressRange6},
-			})
-			userGws[node.Network] = gws
-		}
-	} else {
-		allNodes, err := logic.GetAllNodes()
-		if err != nil {
-			slog.Error("failed to fetch all nodes", "error", err)
-			logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
-			return
-		}
-		for _, node := range allNodes {
-			_, ok := processedAdminNodeIds[node.ID.String()]
-			if node.IsIngressGateway && !node.PendingDelete && !ok {
-				host, err := logic.GetHost(node.HostID.String())
-				if err != nil {
-					slog.Error("failed to fetch host", "error", err)
-					continue
-				}
-				network, err := logic.GetNetwork(node.Network)
-				if err != nil {
-					slog.Error("failed to get node network", "error", err)
-				}
-				gws := userGws[node.Network]
-
-				gws = append(gws, models.UserRemoteGws{
-					GwID:              node.ID.String(),
-					GWName:            host.Name,
-					Network:           node.Network,
-					IsInternetGateway: node.IsInternetGateway,
-					GwPeerPublicKey:   host.PublicKey.String(),
-					GwListenPort:      logic.GetPeerListenPort(host),
-					Metadata:          node.Metadata,
-					AllowedEndpoints:  getAllowedRagEndpoints(&node, host),
-					NetworkAddresses:  []string{network.AddressRange, network.AddressRange6},
-				})
-				userGws[node.Network] = gws
-			}
-		}
-	}
-	if reqFromMobile {
-		// send resp in array format
-		userGwsArr := []models.UserRemoteGws{}
-		for _, userGwI := range userGws {
-			userGwsArr = append(userGwsArr, userGwI...)
-		}
-		logic.ReturnSuccessResponseWithJson(w, r, userGwsArr, "fetched gateways for user"+username)
-		return
-	}
-	slog.Debug("returned user gws", "user", username, "gws", userGws)
-	w.WriteHeader(http.StatusOK)
-	json.NewEncoder(w).Encode(userGws)
-}
-
 // @Summary     List users attached to an remote access gateway
 // @Router      /api/nodes/{network}/{nodeid}/ingress/users [get]
 // @Tags        PRO

+ 1 - 0
pro/initialize.go

@@ -131,6 +131,7 @@ func InitPro() {
 	logic.IsGroupsValid = proLogic.IsGroupsValid
 	logic.IsNetworkRolesValid = proLogic.IsNetworkRolesValid
 	logic.InitialiseRoles = proLogic.UserRolesInit
+	logic.UpdateUserGwAccess = proLogic.UpdateUserGwAccess
 }
 
 func retrieveProLogo() string {

+ 226 - 12
pro/logic/user_mgmt.go

@@ -9,6 +9,9 @@ import (
 	"github.com/gravitl/netmaker/logger"
 	"github.com/gravitl/netmaker/logic"
 	"github.com/gravitl/netmaker/models"
+	"github.com/gravitl/netmaker/mq"
+	"github.com/gravitl/netmaker/servercfg"
+	"golang.org/x/exp/slog"
 )
 
 var ServiceUserPermissionTemplate = models.UserRolePermissionTemplate{
@@ -351,8 +354,19 @@ func DeleteRole(rid models.UserRoleID, force bool) error {
 	if err != nil {
 		return err
 	}
-	if !force && role.Default {
-		return errors.New("cannot delete default role")
+	if role.NetworkID == "" {
+		return errors.New("cannot delete platform role")
+	}
+	// allow deletion of default network roles if network doesn't exist
+	if role.NetworkID == models.AllNetworks {
+		return errors.New("cannot delete default network role")
+	}
+	// check if network exists
+	exists, _ := logic.NetworkExists(role.NetworkID.String())
+	if role.Default {
+		if exists && !force {
+			return errors.New("cannot delete default role")
+		}
 	}
 	for _, user := range users {
 		for userG := range user.UserGroups {
@@ -516,7 +530,6 @@ func HasNetworkRsrcScope(permissionTemplate models.UserRolePermissionTemplate, n
 	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))
@@ -525,7 +538,6 @@ func GetUserRAGNodes(user models.User) (gws map[string]models.Node) {
 	if err != nil {
 		return
 	}
-	logger.Log(0, fmt.Sprintf("------------> 8. getUserRemoteAccessGwsV1 %+v", allNetAccess))
 	for _, node := range nodes {
 		if node.IsIngressGateway && !node.PendingDelete {
 			if allNetAccess {
@@ -545,14 +557,12 @@ func GetUserRAGNodes(user models.User) (gws map[string]models.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
@@ -564,7 +574,6 @@ func GetUserNetworkRolesWithRemoteVPNAccess(user models.User) (gwAccess map[mode
 	if _, ok := user.NetworkRoles[models.AllNetworks]; ok {
 		gwAccess[models.NetworkID("*")] = make(map[models.RsrcID]models.RsrcPermissionScope)
 	}
-	logger.Log(0, "------------> 7.2 getUserRemoteAccessGwsV1")
 	if len(user.UserGroups) > 0 {
 		for gID := range user.UserGroups {
 			userG, err := GetUserGroup(gID)
@@ -664,18 +673,18 @@ func GetUserNetworkRolesWithRemoteVPNAccess(user models.User) (gwAccess map[mode
 		}
 	}
 
-	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.UserRoleID{}
+	allNetworkRoles := make(map[models.UserRoleID]struct{})
+
 	if len(user.NetworkRoles) > 0 {
 		for _, netRoles := range user.NetworkRoles {
 			for netRoleI := range netRoles {
-				allNetworkRoles = append(allNetworkRoles, netRoleI)
+				allNetworkRoles[netRoleI] = struct{}{}
 			}
 		}
 	}
@@ -692,14 +701,14 @@ func GetFilteredNodesByUserAccess(user models.User, nodes []models.Node) (filter
 					}
 					for _, netRoles := range userG.NetworkRoles {
 						for netRoleI := range netRoles {
-							allNetworkRoles = append(allNetworkRoles, netRoleI)
+							allNetworkRoles[netRoleI] = struct{}{}
 						}
 					}
 				}
 			}
 		}
 	}
-	for _, networkRoleID := range allNetworkRoles {
+	for networkRoleID := range allNetworkRoles {
 		userPermTemplate, err := logic.GetRole(networkRoleID)
 		if err != nil {
 			continue
@@ -735,6 +744,7 @@ func GetFilteredNodesByUserAccess(user models.User, nodes []models.Node) (filter
 					if scope.Read {
 						gwNode, err := logic.GetNodeByID(gwID.String())
 						if err == nil && gwNode.IsIngressGateway {
+							nodesMap[gwNode.ID.String()] = struct{}{}
 							filteredNodes = append(filteredNodes, gwNode)
 						}
 					}
@@ -840,3 +850,207 @@ func PrepareOauthUserFromInvite(in models.UserInvite) (models.User, error) {
 	}
 	return user, nil
 }
+
+func UpdatesUserGwAccessOnRoleUpdates(currNetworkAccess,
+	changeNetworkAccess map[models.RsrcType]map[models.RsrcID]models.RsrcPermissionScope, netID string) {
+	networkChangeMap := make(map[models.RsrcID]models.RsrcPermissionScope)
+	for rsrcType, RsrcPermsMap := range currNetworkAccess {
+		if rsrcType != models.RemoteAccessGwRsrc {
+			continue
+		}
+		if _, ok := changeNetworkAccess[rsrcType]; !ok {
+			for rsrcID, scope := range RsrcPermsMap {
+				networkChangeMap[rsrcID] = scope
+			}
+		} else {
+			for rsrcID, scope := range RsrcPermsMap {
+				if _, ok := changeNetworkAccess[rsrcType][rsrcID]; !ok {
+					networkChangeMap[rsrcID] = scope
+				}
+			}
+		}
+	}
+
+	extclients, err := logic.GetAllExtClients()
+	if err != nil {
+		slog.Error("failed to fetch extclients", "error", err)
+		return
+	}
+	userMap, err := logic.GetUserMap()
+	if err != nil {
+		return
+	}
+	for _, extclient := range extclients {
+		if extclient.Network != netID {
+			continue
+		}
+		if _, ok := networkChangeMap[models.AllRemoteAccessGwRsrcID]; ok {
+			if user, ok := userMap[extclient.OwnerID]; ok {
+				if user.PlatformRoleID != models.ServiceUser {
+					continue
+				}
+				err = logic.DeleteExtClientAndCleanup(extclient)
+				if err != nil {
+					slog.Error("failed to delete extclient",
+						"id", extclient.ClientID, "owner", user.UserName, "error", err)
+				} else {
+					if err := mq.PublishDeletedClientPeerUpdate(&extclient); err != nil {
+						slog.Error("error setting ext peers: " + err.Error())
+					}
+				}
+			}
+			continue
+		}
+		if _, ok := networkChangeMap[models.RsrcID(extclient.IngressGatewayID)]; ok {
+			if user, ok := userMap[extclient.OwnerID]; ok {
+				if user.PlatformRoleID != models.ServiceUser {
+					continue
+				}
+				err = logic.DeleteExtClientAndCleanup(extclient)
+				if err != nil {
+					slog.Error("failed to delete extclient",
+						"id", extclient.ClientID, "owner", user.UserName, "error", err)
+				} else {
+					if err := mq.PublishDeletedClientPeerUpdate(&extclient); err != nil {
+						slog.Error("error setting ext peers: " + err.Error())
+					}
+				}
+			}
+
+		}
+
+	}
+	if servercfg.IsDNSMode() {
+		logic.SetDNS()
+	}
+}
+
+func UpdatesUserGwAccessOnGrpUpdates(currNetworkRoles, changeNetworkRoles map[models.NetworkID]map[models.UserRoleID]struct{}) {
+	networkChangeMap := make(map[models.NetworkID]map[models.UserRoleID]struct{})
+	for netID, networkUserRoles := range currNetworkRoles {
+		if _, ok := changeNetworkRoles[netID]; !ok {
+			for netRoleID := range networkUserRoles {
+				if _, ok := networkChangeMap[netID]; !ok {
+					networkChangeMap[netID] = make(map[models.UserRoleID]struct{})
+				}
+				networkChangeMap[netID][netRoleID] = struct{}{}
+			}
+		} else {
+			for netRoleID := range networkUserRoles {
+				if _, ok := changeNetworkRoles[netID][netRoleID]; !ok {
+					if _, ok := networkChangeMap[netID]; !ok {
+						networkChangeMap[netID] = make(map[models.UserRoleID]struct{})
+					}
+					networkChangeMap[netID][netRoleID] = struct{}{}
+				}
+			}
+		}
+	}
+	extclients, err := logic.GetAllExtClients()
+	if err != nil {
+		slog.Error("failed to fetch extclients", "error", err)
+		return
+	}
+	userMap, err := logic.GetUserMap()
+	if err != nil {
+		return
+	}
+	for _, extclient := range extclients {
+
+		if _, ok := networkChangeMap[models.NetworkID(extclient.Network)]; ok {
+			if user, ok := userMap[extclient.OwnerID]; ok {
+				if user.PlatformRoleID != models.ServiceUser {
+					continue
+				}
+				err = logic.DeleteExtClientAndCleanup(extclient)
+				if err != nil {
+					slog.Error("failed to delete extclient",
+						"id", extclient.ClientID, "owner", user.UserName, "error", err)
+				} else {
+					if err := mq.PublishDeletedClientPeerUpdate(&extclient); err != nil {
+						slog.Error("error setting ext peers: " + err.Error())
+					}
+				}
+			}
+
+		}
+
+	}
+	if servercfg.IsDNSMode() {
+		logic.SetDNS()
+	}
+
+}
+
+func UpdateUserGwAccess(currentUser, changeUser models.User) {
+	if changeUser.PlatformRoleID != models.ServiceUser {
+		return
+	}
+
+	networkChangeMap := make(map[models.NetworkID]map[models.UserRoleID]struct{})
+	for netID, networkUserRoles := range currentUser.NetworkRoles {
+		if _, ok := changeUser.NetworkRoles[netID]; !ok {
+			for netRoleID := range networkUserRoles {
+				if _, ok := networkChangeMap[netID]; !ok {
+					networkChangeMap[netID] = make(map[models.UserRoleID]struct{})
+				}
+				networkChangeMap[netID][netRoleID] = struct{}{}
+			}
+		} else {
+			for netRoleID := range networkUserRoles {
+				if _, ok := changeUser.NetworkRoles[netID][netRoleID]; !ok {
+					if _, ok := networkChangeMap[netID]; !ok {
+						networkChangeMap[netID] = make(map[models.UserRoleID]struct{})
+					}
+					networkChangeMap[netID][netRoleID] = struct{}{}
+				}
+			}
+		}
+	}
+	for gID := range currentUser.UserGroups {
+		if _, ok := changeUser.UserGroups[gID]; ok {
+			continue
+		}
+		userG, err := GetUserGroup(gID)
+		if err == nil {
+			for netID, networkUserRoles := range userG.NetworkRoles {
+				for netRoleID := range networkUserRoles {
+					if _, ok := networkChangeMap[netID]; !ok {
+						networkChangeMap[netID] = make(map[models.UserRoleID]struct{})
+					}
+					networkChangeMap[netID][netRoleID] = struct{}{}
+				}
+			}
+		}
+	}
+	if len(networkChangeMap) == 0 {
+		return
+	}
+	// TODO - cleanup gw access when role and groups are updated
+	//removedGwAccess
+	extclients, err := logic.GetAllExtClients()
+	if err != nil {
+		slog.Error("failed to fetch extclients", "error", err)
+		return
+	}
+	for _, extclient := range extclients {
+		if extclient.OwnerID == currentUser.UserName {
+			if _, ok := networkChangeMap[models.NetworkID(extclient.Network)]; ok {
+				err = logic.DeleteExtClientAndCleanup(extclient)
+				if err != nil {
+					slog.Error("failed to delete extclient",
+						"id", extclient.ClientID, "owner", changeUser.UserName, "error", err)
+				} else {
+					if err := mq.PublishDeletedClientPeerUpdate(&extclient); err != nil {
+						slog.Error("error setting ext peers: " + err.Error())
+					}
+				}
+			}
+
+		}
+	}
+	if servercfg.IsDNSMode() {
+		logic.SetDNS()
+	}
+
+}

+ 1 - 1
pro/remote_access_client.go

@@ -78,7 +78,7 @@ func disableExtClient(client *models.ExtClient) error {
 			if err != nil {
 				return err
 			}
-			go mq.PublishSingleHostPeerUpdate(ingressHost, nodes, nil, []models.ExtClient{*client}, false)
+			go mq.PublishSingleHostPeerUpdate(ingressHost, nodes, nil, []models.ExtClient{*client}, false, nil)
 		} else {
 			return err
 		}

+ 1 - 0
scripts/nm-quick.sh

@@ -231,6 +231,7 @@ save_config() { (
 	fi
 	if [ -n "$NETMAKER_BASE_DOMAIN" ]; then
 		save_config_item NM_DOMAIN "$NETMAKER_BASE_DOMAIN"
+		save_config_item FRONTEND_URL "https://dashboard.$NETMAKER_BASE_DOMAIN"
 	fi
 	save_config_item UI_IMAGE_TAG "$IMAGE_TAG"
 	# version-specific entries

+ 22 - 0
servercfg/serverconf.go

@@ -650,6 +650,28 @@ func GetMetricInterval() string {
 	return mi
 }
 
+// GetBatchPeerUpdate - if batch peer update
+func GetBatchPeerUpdate() bool {
+	enabled := true
+	if os.Getenv("PEER_UPDATE_BATCH") != "" {
+		enabled = os.Getenv("PEER_UPDATE_BATCH") == "true"
+	}
+	return enabled
+}
+
+// GetPeerUpdateBatchSize - get the batch size for peer update
+func GetPeerUpdateBatchSize() int {
+	//default 50
+	batchSize := 50
+	if os.Getenv("PEER_UPDATE_BATCH_SIZE") != "" {
+		b, e := strconv.Atoi(os.Getenv("PEER_UPDATE_BATCH_SIZE"))
+		if e == nil && b > 0 && b < 1000 {
+			batchSize = b
+		}
+	}
+	return batchSize
+}
+
 // GetEmqxRestEndpoint - returns the REST API Endpoint of EMQX
 func GetEmqxRestEndpoint() string {
 	return os.Getenv("EMQX_REST_ENDPOINT")