2
0
Эх сурвалжийг харах

Merge pull request #2563 from gravitl/release-v0.21.0

v0.21.0
Abhishek K 1 жил өмнө
parent
commit
f158cafb01
100 өөрчлөгдсөн 1377 нэмэгдсэн , 3380 устгасан
  1. 1 0
      .github/ISSUE_TEMPLATE/bug-report.yml
  2. 1 1
      .github/workflows/publish-docker.yml
  3. 1 1
      .github/workflows/test.yml
  4. 1 1
      README.md
  5. 7 6
      auth/auth.go
  6. 0 22
      auth/register_callback.go
  7. 0 43
      cli/cmd/network_user/create.go
  8. 0 23
      cli/cmd/network_user/delete.go
  9. 0 10
      cli/cmd/network_user/flags.go
  10. 0 27
      cli/cmd/network_user/get.go
  11. 0 27
      cli/cmd/network_user/list.go
  12. 0 28
      cli/cmd/network_user/root.go
  13. 0 43
      cli/cmd/network_user/update.go
  14. 0 4
      cli/cmd/root.go
  15. 0 8
      cli/cmd/user/create.go
  16. 2 3
      cli/cmd/user/list.go
  17. 0 10
      cli/cmd/user/update.go
  18. 0 23
      cli/cmd/usergroup/create.go
  19. 0 23
      cli/cmd/usergroup/delete.go
  20. 0 20
      cli/cmd/usergroup/get.go
  21. 0 28
      cli/cmd/usergroup/root.go
  22. 0 44
      cli/functions/network_user.go
  23. 0 22
      cli/functions/usergroups.go
  24. 1 1
      compose/docker-compose.netclient.yml
  25. 0 0
      compose/docker-compose.pro.yml
  26. 1 1
      config/config.go
  27. 6 6
      controllers/dns.go
  28. 29 6
      controllers/dns_test.go
  29. 1 1
      controllers/docs.go
  30. 3 13
      controllers/enrollmentkeys.go
  31. 88 146
      controllers/ext_client.go
  32. 11 3
      controllers/hosts.go
  33. 1 1
      controllers/migrate.go
  34. 10 26
      controllers/network.go
  35. 6 19
      controllers/network_test.go
  36. 33 105
      controllers/node.go
  37. 3 4
      controllers/server.go
  38. 163 150
      controllers/user.go
  39. 29 89
      controllers/user_test.go
  40. 0 0
      docker/Caddyfile-pro
  41. 0 365
      ee/ee_controllers/networkusers.go
  42. 0 73
      ee/ee_controllers/usergroups.go
  43. 0 41
      ee/logic/ext_acls.go
  44. 4 6
      functions/helpers_test.go
  45. 5 5
      go.mod
  46. 10 43
      go.sum
  47. 1 1
      k8s/client/netclient-daemonset.yaml
  48. 1 1
      k8s/client/netclient.yaml
  49. 1 1
      k8s/server/netmaker-ui.yaml
  50. 25 181
      logic/auth.go
  51. 5 38
      logic/clients.go
  52. 15 1
      logic/dns.go
  53. 0 14
      logic/enrollmentkey.go
  54. 0 74
      logic/enrollmentkey_test.go
  55. 37 1
      logic/gateway.go
  56. 1 1
      logic/hosts.go
  57. 11 35
      logic/jwts.go
  58. 7 28
      logic/metrics.go
  59. 5 37
      logic/networks.go
  60. 4 43
      logic/nodes.go
  61. 6 61
      logic/peers.go
  62. 0 68
      logic/pro/networks.go
  63. 0 64
      logic/pro/networks_test.go
  64. 0 251
      logic/pro/networkuser.go
  65. 0 110
      logic/pro/networkuser_test.go
  66. 0 35
      logic/pro/proacls/nodes.go
  67. 0 20
      logic/pro/types.go
  68. 0 80
      logic/pro/usergroups.go
  69. 0 41
      logic/pro/usergroups_test.go
  70. 10 134
      logic/relay.go
  71. 13 143
      logic/security.go
  72. 4 10
      logic/telemetry.go
  73. 21 56
      logic/users.go
  74. 2 7
      main.go
  75. 2 4
      main_ee.go
  76. 39 0
      migrate/migrate.go
  77. 4 4
      models/dnsEntry.go
  78. 8 6
      models/extclient.go
  79. 1 0
      models/metrics.go
  80. 16 19
      models/network.go
  81. 3 3
      models/node.go
  82. 0 37
      models/promodels/networkuser.go
  83. 0 10
      models/promodels/pro.go
  84. 0 9
      models/promodels/usergroups.go
  85. 34 14
      models/structs.go
  86. 20 174
      mq/handlers.go
  87. 6 4
      mq/publishers.go
  88. 2 2
      mq/util.go
  89. 0 0
      pro/LICENSE
  90. 7 6
      pro/controllers/metrics.go
  91. 1 1
      pro/controllers/middleware.go
  92. 5 4
      pro/controllers/relay.go
  93. 268 0
      pro/controllers/users.go
  94. 32 21
      pro/initialize.go
  95. 1 1
      pro/license.go
  96. 1 1
      pro/license_test.go
  97. 114 0
      pro/logic/ext_acls.go
  98. 3 3
      pro/logic/failover.go
  99. 203 0
      pro/logic/metrics.go
  100. 21 0
      pro/logic/nodes.go

+ 1 - 0
.github/ISSUE_TEMPLATE/bug-report.yml

@@ -31,6 +31,7 @@ body:
       label: Version
       label: Version
       description: What version are you running?
       description: What version are you running?
       options:
       options:
+        - v0.21.0
         - v0.20.6
         - v0.20.6
         - v0.20.5
         - v0.20.5
         - v0.20.4
         - v0.20.4

+ 1 - 1
.github/workflows/publish-docker.yml

@@ -53,7 +53,7 @@ jobs:
           build-args: | 
           build-args: | 
             tags=ce
             tags=ce
 
 
-  docker-ee:
+  docker-pro:
     runs-on: ubuntu-latest
     runs-on: ubuntu-latest
     steps:
     steps:
       -
       -

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

@@ -51,7 +51,7 @@ jobs:
         run: |
         run: |
           go vet ./...
           go vet ./...
           go test -p 1 ./... -v
           go test -p 1 ./... -v
-          go test -p 1 ./ee -v --tags ee
+          go test -p 1 ./pro -v --tags ee
         env:
         env:
           DATABASE: sqlite
           DATABASE: sqlite
           CLIENT_MODE: "off"
           CLIENT_MODE: "off"

+ 1 - 1
README.md

@@ -16,7 +16,7 @@
 
 
 <p align="center">
 <p align="center">
   <a href="https://github.com/gravitl/netmaker/releases">
   <a href="https://github.com/gravitl/netmaker/releases">
-    <img src="https://img.shields.io/badge/Version-0.20.6-informational?style=flat-square" />
+    <img src="https://img.shields.io/badge/Version-0.21.0-informational?style=flat-square" />
   </a>
   </a>
   <a href="https://hub.docker.com/r/gravitl/netmaker/tags">
   <a href="https://hub.docker.com/r/gravitl/netmaker/tags">
     <img src="https://img.shields.io/docker/pulls/gravitl/netmaker?label=downloads" />
     <img src="https://img.shields.io/docker/pulls/gravitl/netmaker?label=downloads" />

+ 7 - 6
auth/auth.go

@@ -10,6 +10,7 @@ import (
 	"time"
 	"time"
 
 
 	"golang.org/x/crypto/bcrypt"
 	"golang.org/x/crypto/bcrypt"
+	"golang.org/x/exp/slog"
 	"golang.org/x/oauth2"
 	"golang.org/x/oauth2"
 
 
 	"github.com/gorilla/websocket"
 	"github.com/gorilla/websocket"
@@ -238,9 +239,9 @@ func HandleHeadlessSSO(w http.ResponseWriter, r *http.Request) {
 // == private methods ==
 // == private methods ==
 
 
 func addUser(email string) error {
 func addUser(email string) error {
-	var hasAdmin, err = logic.HasAdmin()
+	var hasSuperAdmin, err = logic.HasSuperAdmin()
 	if err != nil {
 	if err != nil {
-		logger.Log(1, "error checking for existence of admin user during OAuth login for", email, "; user not added")
+		slog.Error("error checking for existence of admin user during OAuth login for", "email", email, "error", err)
 		return err
 		return err
 	} // generate random password to adapt to current model
 	} // generate random password to adapt to current model
 	var newPass, fetchErr = fetchPassValue("")
 	var newPass, fetchErr = fetchPassValue("")
@@ -251,11 +252,11 @@ func addUser(email string) error {
 		UserName: email,
 		UserName: email,
 		Password: newPass,
 		Password: newPass,
 	}
 	}
-	if !hasAdmin { // must be first attempt, create an admin
-		if err = logic.CreateAdmin(&newUser); err != nil {
-			logger.Log(1, "error creating admin from user,", email, "; user not added")
+	if !hasSuperAdmin { // must be first attempt, create a superadmin
+		if err = logic.CreateSuperAdmin(&newUser); err != nil {
+			slog.Error("error creating super admin from user", "email", email, "error", err)
 		} else {
 		} else {
-			logger.Log(1, "admin created from user,", email, "; was first user added")
+			slog.Info("superadmin created from user", "email", email)
 		}
 		}
 	} else { // otherwise add to db as admin..?
 	} else { // otherwise add to db as admin..?
 		// TODO: add ability to add users with preemptive permissions
 		// TODO: add ability to add users with preemptive permissions

+ 0 - 22
auth/register_callback.go

@@ -9,10 +9,8 @@ import (
 	"github.com/gorilla/mux"
 	"github.com/gorilla/mux"
 	"github.com/gravitl/netmaker/logger"
 	"github.com/gravitl/netmaker/logger"
 	"github.com/gravitl/netmaker/logic"
 	"github.com/gravitl/netmaker/logic"
-	"github.com/gravitl/netmaker/logic/pro"
 	"github.com/gravitl/netmaker/logic/pro/netcache"
 	"github.com/gravitl/netmaker/logic/pro/netcache"
 	"github.com/gravitl/netmaker/models"
 	"github.com/gravitl/netmaker/models"
-	"github.com/gravitl/netmaker/models/promodels"
 )
 )
 
 
 var (
 var (
@@ -165,25 +163,5 @@ func isUserIsAllowed(username, network string, shouldAddUser bool) (*models.User
 		user, _ = logic.GetUser(username)
 		user, _ = logic.GetUser(username)
 	}
 	}
 
 
-	if !user.IsAdmin { // perform check to see if user is allowed to join a node to network
-		netUser, err := pro.GetNetworkUser(network, promodels.NetworkUserID(user.UserName))
-		if err != nil {
-			logger.Log(0, "failed to get net user details for user", user.UserName, "during node SSO")
-			return nil, fmt.Errorf("failed to verify network user")
-		}
-		if netUser.AccessLevel != pro.NET_ADMIN { // if user is a net admin on network, good to go
-			// otherwise, check if they have node access + haven't reached node limit on network
-			if netUser.AccessLevel == pro.NODE_ACCESS {
-				if len(netUser.Nodes) >= netUser.NodeLimit {
-					logger.Log(0, "user", user.UserName, "has reached their node limit on network", network)
-					return nil, fmt.Errorf("user node limit exceeded")
-				}
-			} else {
-				logger.Log(0, "user", user.UserName, "attempted to access network", network, "via node SSO")
-				return nil, fmt.Errorf("network user not allowed")
-			}
-		}
-	}
-
 	return user, nil
 	return user, nil
 }
 }

+ 0 - 43
cli/cmd/network_user/create.go

@@ -1,43 +0,0 @@
-package network_user
-
-import (
-	"fmt"
-	"strings"
-
-	"github.com/gravitl/netmaker/cli/functions"
-	"github.com/gravitl/netmaker/models/promodels"
-	"github.com/spf13/cobra"
-)
-
-var networkuserCreateCmd = &cobra.Command{
-	Use:   "create [NETWORK NAME]",
-	Args:  cobra.ExactArgs(1),
-	Short: "Create a network user",
-	Long:  `Create a network user`,
-	Run: func(cmd *cobra.Command, args []string) {
-		user := &promodels.NetworkUser{
-			AccessLevel: accessLevel,
-			ClientLimit: clientLimit,
-			NodeLimit:   nodeLimit, ID: promodels.NetworkUserID(id),
-		}
-		if clients != "" {
-			user.Clients = strings.Split(clients, ",")
-		}
-		if nodes != "" {
-			user.Nodes = strings.Split(nodes, ",")
-		}
-		functions.CreateNetworkUser(args[0], user)
-		fmt.Println("Success")
-	},
-}
-
-func init() {
-	networkuserCreateCmd.Flags().IntVar(&accessLevel, "access_level", 0, "Custom access level")
-	networkuserCreateCmd.Flags().IntVar(&clientLimit, "client_limit", 0, "Maximum number of external clients that can be created")
-	networkuserCreateCmd.Flags().IntVar(&nodeLimit, "node_limit", 999999999, "Maximum number of nodes that can be attached to a network")
-	networkuserCreateCmd.Flags().StringVar(&clients, "clients", "", "Access to list of external clients (comma separated)")
-	networkuserCreateCmd.Flags().StringVar(&nodes, "nodes", "", "Access to list of nodes (comma separated)")
-	networkuserCreateCmd.Flags().StringVar(&id, "id", "", "ID of the network user")
-	networkuserCreateCmd.MarkFlagRequired("id")
-	rootCmd.AddCommand(networkuserCreateCmd)
-}

+ 0 - 23
cli/cmd/network_user/delete.go

@@ -1,23 +0,0 @@
-package network_user
-
-import (
-	"fmt"
-
-	"github.com/gravitl/netmaker/cli/functions"
-	"github.com/spf13/cobra"
-)
-
-var networkuserDeleteCmd = &cobra.Command{
-	Use:   "delete [NETWORK NAME] [NETWORK USER NAME]",
-	Args:  cobra.ExactArgs(2),
-	Short: "Delete a network user",
-	Long:  `Delete a network user`,
-	Run: func(cmd *cobra.Command, args []string) {
-		functions.DeleteNetworkUser(args[0], args[1])
-		fmt.Println("Success")
-	},
-}
-
-func init() {
-	rootCmd.AddCommand(networkuserDeleteCmd)
-}

+ 0 - 10
cli/cmd/network_user/flags.go

@@ -1,10 +0,0 @@
-package network_user
-
-var (
-	accessLevel int
-	clientLimit int
-	nodeLimit   int
-	clients     string
-	nodes       string
-	id          string
-)

+ 0 - 27
cli/cmd/network_user/get.go

@@ -1,27 +0,0 @@
-package network_user
-
-import (
-	"github.com/gravitl/netmaker/cli/functions"
-	"github.com/spf13/cobra"
-)
-
-var data bool
-
-var networkuserGetCmd = &cobra.Command{
-	Use:   "get [NETWORK NAME] [NETWORK USER NAME]",
-	Args:  cobra.ExactArgs(2),
-	Short: "Fetch a network user",
-	Long:  `Fetch a network user`,
-	Run: func(cmd *cobra.Command, args []string) {
-		if data {
-			functions.PrettyPrint(functions.GetNetworkUserData(args[1]))
-		} else {
-			functions.PrettyPrint(functions.GetNetworkUser(args[0], args[1]))
-		}
-	},
-}
-
-func init() {
-	networkuserGetCmd.Flags().BoolVar(&data, "data", false, "Fetch entire data of a network user")
-	rootCmd.AddCommand(networkuserGetCmd)
-}

+ 0 - 27
cli/cmd/network_user/list.go

@@ -1,27 +0,0 @@
-package network_user
-
-import (
-	"github.com/gravitl/netmaker/cli/functions"
-	"github.com/spf13/cobra"
-)
-
-var networkName string
-
-var networkuserListCmd = &cobra.Command{
-	Use:   "list",
-	Args:  cobra.NoArgs,
-	Short: "List network users",
-	Long:  `List network users`,
-	Run: func(cmd *cobra.Command, args []string) {
-		if networkName != "" {
-			functions.PrettyPrint(functions.GetNetworkUsers(networkName))
-		} else {
-			functions.PrettyPrint(functions.GetAllNetworkUsers())
-		}
-	},
-}
-
-func init() {
-	networkuserListCmd.Flags().StringVar(&networkName, "network", "", "Name of the network")
-	rootCmd.AddCommand(networkuserListCmd)
-}

+ 0 - 28
cli/cmd/network_user/root.go

@@ -1,28 +0,0 @@
-package network_user
-
-import (
-	"os"
-
-	"github.com/spf13/cobra"
-)
-
-// rootCmd represents the base command when called without any subcommands
-var rootCmd = &cobra.Command{
-	Use:   "network_user",
-	Short: "Manage Network Users",
-	Long:  `Manage Network Users`,
-}
-
-// GetRoot returns the root subcommand
-func GetRoot() *cobra.Command {
-	return rootCmd
-}
-
-// Execute adds all child commands to the root command and sets flags appropriately.
-// This is called by main.main(). It only needs to happen once to the rootCmd.
-func Execute() {
-	err := rootCmd.Execute()
-	if err != nil {
-		os.Exit(1)
-	}
-}

+ 0 - 43
cli/cmd/network_user/update.go

@@ -1,43 +0,0 @@
-package network_user
-
-import (
-	"fmt"
-	"strings"
-
-	"github.com/gravitl/netmaker/cli/functions"
-	"github.com/gravitl/netmaker/models/promodels"
-	"github.com/spf13/cobra"
-)
-
-var networkuserUpdateCmd = &cobra.Command{
-	Use:   "update [NETWORK NAME]",
-	Args:  cobra.ExactArgs(1),
-	Short: "Update a network user",
-	Long:  `Update a network user`,
-	Run: func(cmd *cobra.Command, args []string) {
-		user := &promodels.NetworkUser{
-			AccessLevel: accessLevel,
-			ClientLimit: clientLimit,
-			NodeLimit:   nodeLimit, ID: promodels.NetworkUserID(id),
-		}
-		if clients != "" {
-			user.Clients = strings.Split(clients, ",")
-		}
-		if nodes != "" {
-			user.Nodes = strings.Split(nodes, ",")
-		}
-		functions.UpdateNetworkUser(args[0], user)
-		fmt.Println("Success")
-	},
-}
-
-func init() {
-	networkuserUpdateCmd.Flags().IntVar(&accessLevel, "access_level", 0, "Custom access level")
-	networkuserUpdateCmd.Flags().IntVar(&clientLimit, "client_limit", 0, "Maximum number of external clients that can be created")
-	networkuserUpdateCmd.Flags().IntVar(&nodeLimit, "node_limit", 999999999, "Maximum number of nodes that can be attached to a network")
-	networkuserUpdateCmd.Flags().StringVar(&clients, "clients", "", "Access to list of external clients (comma separated)")
-	networkuserUpdateCmd.Flags().StringVar(&nodes, "nodes", "", "Access to list of nodes (comma separated)")
-	networkuserUpdateCmd.Flags().StringVar(&id, "id", "", "ID of the network user")
-	networkuserUpdateCmd.MarkFlagRequired("id")
-	rootCmd.AddCommand(networkuserUpdateCmd)
-}

+ 0 - 4
cli/cmd/root.go

@@ -12,11 +12,9 @@ import (
 	"github.com/gravitl/netmaker/cli/cmd/host"
 	"github.com/gravitl/netmaker/cli/cmd/host"
 	"github.com/gravitl/netmaker/cli/cmd/metrics"
 	"github.com/gravitl/netmaker/cli/cmd/metrics"
 	"github.com/gravitl/netmaker/cli/cmd/network"
 	"github.com/gravitl/netmaker/cli/cmd/network"
-	"github.com/gravitl/netmaker/cli/cmd/network_user"
 	"github.com/gravitl/netmaker/cli/cmd/node"
 	"github.com/gravitl/netmaker/cli/cmd/node"
 	"github.com/gravitl/netmaker/cli/cmd/server"
 	"github.com/gravitl/netmaker/cli/cmd/server"
 	"github.com/gravitl/netmaker/cli/cmd/user"
 	"github.com/gravitl/netmaker/cli/cmd/user"
-	"github.com/gravitl/netmaker/cli/cmd/usergroup"
 	"github.com/spf13/cobra"
 	"github.com/spf13/cobra"
 )
 )
 
 
@@ -52,9 +50,7 @@ func init() {
 	rootCmd.AddCommand(server.GetRoot())
 	rootCmd.AddCommand(server.GetRoot())
 	rootCmd.AddCommand(ext_client.GetRoot())
 	rootCmd.AddCommand(ext_client.GetRoot())
 	rootCmd.AddCommand(user.GetRoot())
 	rootCmd.AddCommand(user.GetRoot())
-	rootCmd.AddCommand(usergroup.GetRoot())
 	rootCmd.AddCommand(metrics.GetRoot())
 	rootCmd.AddCommand(metrics.GetRoot())
-	rootCmd.AddCommand(network_user.GetRoot())
 	rootCmd.AddCommand(host.GetRoot())
 	rootCmd.AddCommand(host.GetRoot())
 	rootCmd.AddCommand(enrollment_key.GetRoot())
 	rootCmd.AddCommand(enrollment_key.GetRoot())
 }
 }

+ 0 - 8
cli/cmd/user/create.go

@@ -1,8 +1,6 @@
 package user
 package user
 
 
 import (
 import (
-	"strings"
-
 	"github.com/gravitl/netmaker/cli/functions"
 	"github.com/gravitl/netmaker/cli/functions"
 	"github.com/gravitl/netmaker/models"
 	"github.com/gravitl/netmaker/models"
 	"github.com/spf13/cobra"
 	"github.com/spf13/cobra"
@@ -15,12 +13,6 @@ var userCreateCmd = &cobra.Command{
 	Long:  `Create a new user`,
 	Long:  `Create a new user`,
 	Run: func(cmd *cobra.Command, args []string) {
 	Run: func(cmd *cobra.Command, args []string) {
 		user := &models.User{UserName: username, Password: password, IsAdmin: admin}
 		user := &models.User{UserName: username, Password: password, IsAdmin: admin}
-		if networks != "" {
-			user.Networks = strings.Split(networks, ",")
-		}
-		if groups != "" {
-			user.Groups = strings.Split(groups, ",")
-		}
 		functions.PrettyPrint(functions.CreateUser(user))
 		functions.PrettyPrint(functions.CreateUser(user))
 	},
 	},
 }
 }

+ 2 - 3
cli/cmd/user/list.go

@@ -3,7 +3,6 @@ package user
 import (
 import (
 	"os"
 	"os"
 	"strconv"
 	"strconv"
-	"strings"
 
 
 	"github.com/gravitl/netmaker/cli/cmd/commons"
 	"github.com/gravitl/netmaker/cli/cmd/commons"
 	"github.com/gravitl/netmaker/cli/functions"
 	"github.com/gravitl/netmaker/cli/functions"
@@ -23,9 +22,9 @@ var userListCmd = &cobra.Command{
 			functions.PrettyPrint(data)
 			functions.PrettyPrint(data)
 		default:
 		default:
 			table := tablewriter.NewWriter(os.Stdout)
 			table := tablewriter.NewWriter(os.Stdout)
-			table.SetHeader([]string{"Name", "Admin", "Networks", "Groups"})
+			table.SetHeader([]string{"Name", "SuperAdmin", "Admin"})
 			for _, d := range *data {
 			for _, d := range *data {
-				table.Append([]string{d.UserName, strconv.FormatBool(d.IsAdmin), strings.Join(d.Networks, ", "), strings.Join(d.Groups, ", ")})
+				table.Append([]string{d.UserName, strconv.FormatBool(d.IsSuperAdmin), strconv.FormatBool(d.IsAdmin)})
 			}
 			}
 			table.Render()
 			table.Render()
 		}
 		}

+ 0 - 10
cli/cmd/user/update.go

@@ -1,8 +1,6 @@
 package user
 package user
 
 
 import (
 import (
-	"strings"
-
 	"github.com/gravitl/netmaker/cli/functions"
 	"github.com/gravitl/netmaker/cli/functions"
 	"github.com/gravitl/netmaker/models"
 	"github.com/gravitl/netmaker/models"
 	"github.com/spf13/cobra"
 	"github.com/spf13/cobra"
@@ -15,14 +13,6 @@ var userUpdateCmd = &cobra.Command{
 	Long:  `Update a user`,
 	Long:  `Update a user`,
 	Run: func(cmd *cobra.Command, args []string) {
 	Run: func(cmd *cobra.Command, args []string) {
 		user := &models.User{UserName: args[0], IsAdmin: admin}
 		user := &models.User{UserName: args[0], IsAdmin: admin}
-		if networks != "" {
-			user.Networks = strings.Split(networks, ",")
-		}
-		if groups != "" {
-			user.Groups = strings.Split(groups, ",")
-		} else {
-			user.Groups = []string{"*"}
-		}
 		functions.PrettyPrint(functions.UpdateUser(user))
 		functions.PrettyPrint(functions.UpdateUser(user))
 	},
 	},
 }
 }

+ 0 - 23
cli/cmd/usergroup/create.go

@@ -1,23 +0,0 @@
-package usergroup
-
-import (
-	"fmt"
-
-	"github.com/gravitl/netmaker/cli/functions"
-	"github.com/spf13/cobra"
-)
-
-var usergroupCreateCmd = &cobra.Command{
-	Use:   "create [GROUP NAME]",
-	Args:  cobra.ExactArgs(1),
-	Short: "Create a usergroup",
-	Long:  `Create a usergroup`,
-	Run: func(cmd *cobra.Command, args []string) {
-		functions.CreateUsergroup(args[0])
-		fmt.Println("Success")
-	},
-}
-
-func init() {
-	rootCmd.AddCommand(usergroupCreateCmd)
-}

+ 0 - 23
cli/cmd/usergroup/delete.go

@@ -1,23 +0,0 @@
-package usergroup
-
-import (
-	"fmt"
-
-	"github.com/gravitl/netmaker/cli/functions"
-	"github.com/spf13/cobra"
-)
-
-var usergroupDeleteCmd = &cobra.Command{
-	Use:   "delete [GROUP NAME]",
-	Args:  cobra.ExactArgs(1),
-	Short: "Delete a usergroup",
-	Long:  `Delete a usergroup`,
-	Run: func(cmd *cobra.Command, args []string) {
-		functions.DeleteUsergroup(args[0])
-		fmt.Println("Success")
-	},
-}
-
-func init() {
-	rootCmd.AddCommand(usergroupDeleteCmd)
-}

+ 0 - 20
cli/cmd/usergroup/get.go

@@ -1,20 +0,0 @@
-package usergroup
-
-import (
-	"github.com/gravitl/netmaker/cli/functions"
-	"github.com/spf13/cobra"
-)
-
-var usergroupGetCmd = &cobra.Command{
-	Use:   "get",
-	Args:  cobra.NoArgs,
-	Short: "Fetch all usergroups",
-	Long:  `Fetch all usergroups`,
-	Run: func(cmd *cobra.Command, args []string) {
-		functions.PrettyPrint(functions.GetUsergroups())
-	},
-}
-
-func init() {
-	rootCmd.AddCommand(usergroupGetCmd)
-}

+ 0 - 28
cli/cmd/usergroup/root.go

@@ -1,28 +0,0 @@
-package usergroup
-
-import (
-	"os"
-
-	"github.com/spf13/cobra"
-)
-
-// rootCmd represents the base command when called without any subcommands
-var rootCmd = &cobra.Command{
-	Use:   "usergroup",
-	Short: "Manage User Groups",
-	Long:  `Manage User Groups`,
-}
-
-// GetRoot returns the root subcommand
-func GetRoot() *cobra.Command {
-	return rootCmd
-}
-
-// Execute adds all child commands to the root command and sets flags appropriately.
-// This is called by main.main(). It only needs to happen once to the rootCmd.
-func Execute() {
-	err := rootCmd.Execute()
-	if err != nil {
-		os.Exit(1)
-	}
-}

+ 0 - 44
cli/functions/network_user.go

@@ -1,44 +0,0 @@
-package functions
-
-import (
-	"fmt"
-	"net/http"
-
-	"github.com/gravitl/netmaker/ee/ee_controllers"
-	"github.com/gravitl/netmaker/models/promodels"
-)
-
-// GetAllNetworkUsers - fetch all network users
-func GetAllNetworkUsers() *map[string][]promodels.NetworkUser {
-	return request[map[string][]promodels.NetworkUser](http.MethodGet, "/api/networkusers", nil)
-}
-
-// GetNetworkUsers - fetch network users belonging to a particular network
-func GetNetworkUsers(networkName string) *promodels.NetworkUserMap {
-	return request[promodels.NetworkUserMap](http.MethodGet, "/api/networkusers/"+networkName, nil)
-}
-
-// GetNetworkUser - fetch a single network user
-func GetNetworkUser(networkName, networkUserName string) *promodels.NetworkUser {
-	return request[promodels.NetworkUser](http.MethodGet, fmt.Sprintf("/api/networkusers/%s/%s", networkName, networkUserName), nil)
-}
-
-// CreateNetworkUser - create a network user
-func CreateNetworkUser(networkName string, payload *promodels.NetworkUser) {
-	request[any](http.MethodPost, "/api/networkusers/"+networkName, payload)
-}
-
-// UpdateNetworkUser - update a network user
-func UpdateNetworkUser(networkName string, payload *promodels.NetworkUser) {
-	request[any](http.MethodPut, "/api/networkusers/"+networkName, payload)
-}
-
-// GetNetworkUserData - fetch a network user's complete data
-func GetNetworkUserData(networkUserName string) *ee_controllers.NetworkUserDataMap {
-	return request[ee_controllers.NetworkUserDataMap](http.MethodGet, fmt.Sprintf("/api/networkusers/data/%s/me", networkUserName), nil)
-}
-
-// DeleteNetworkUser - delete a network user
-func DeleteNetworkUser(networkName, networkUserName string) {
-	request[any](http.MethodDelete, fmt.Sprintf("/api/networkusers/%s/%s", networkName, networkUserName), nil)
-}

+ 0 - 22
cli/functions/usergroups.go

@@ -1,22 +0,0 @@
-package functions
-
-import (
-	"net/http"
-
-	"github.com/gravitl/netmaker/models/promodels"
-)
-
-// GetUsergroups - fetch all usergroups
-func GetUsergroups() *promodels.UserGroups {
-	return request[promodels.UserGroups](http.MethodGet, "/api/usergroups", nil)
-}
-
-// CreateUsergroup - create a usergroup
-func CreateUsergroup(usergroupName string) {
-	request[any](http.MethodPost, "/api/usergroups/"+usergroupName, nil)
-}
-
-// DeleteUsergroup - delete a usergroup
-func DeleteUsergroup(usergroupName string) {
-	request[any](http.MethodDelete, "/api/usergroups/"+usergroupName, nil)
-}

+ 1 - 1
compose/docker-compose.netclient.yml

@@ -3,7 +3,7 @@ version: "3.4"
 services:
 services:
   netclient:
   netclient:
     container_name: netclient
     container_name: netclient
-    image: 'gravitl/netclient:v0.20.6'
+    image: 'gravitl/netclient:v0.21.0'
     hostname: netmaker-1
     hostname: netmaker-1
     network_mode: host
     network_mode: host
     restart: on-failure
     restart: on-failure

+ 0 - 0
compose/docker-compose.ee.yml → compose/docker-compose.pro.yml


+ 1 - 1
config/config.go

@@ -72,7 +72,7 @@ type ServerConfig struct {
 	BasicAuth                  string `yaml:"basic_auth"`
 	BasicAuth                  string `yaml:"basic_auth"`
 	LicenseValue               string `yaml:"license_value"`
 	LicenseValue               string `yaml:"license_value"`
 	NetmakerTenantID           string `yaml:"netmaker_tenant_id"`
 	NetmakerTenantID           string `yaml:"netmaker_tenant_id"`
-	IsEE                       string `yaml:"is_ee"`
+	IsPro                      string `yaml:"is_ee" json:"IsEE"`
 	StunPort                   int    `yaml:"stun_port"`
 	StunPort                   int    `yaml:"stun_port"`
 	StunList                   string `yaml:"stun_list"`
 	StunList                   string `yaml:"stun_list"`
 	TurnServer                 string `yaml:"turn_server"`
 	TurnServer                 string `yaml:"turn_server"`

+ 6 - 6
controllers/dns.go

@@ -17,12 +17,12 @@ import (
 func dnsHandlers(r *mux.Router) {
 func dnsHandlers(r *mux.Router) {
 
 
 	r.HandleFunc("/api/dns", logic.SecurityCheck(true, http.HandlerFunc(getAllDNS))).Methods(http.MethodGet)
 	r.HandleFunc("/api/dns", logic.SecurityCheck(true, http.HandlerFunc(getAllDNS))).Methods(http.MethodGet)
-	r.HandleFunc("/api/dns/adm/{network}/nodes", logic.SecurityCheck(false, http.HandlerFunc(getNodeDNS))).Methods(http.MethodGet)
-	r.HandleFunc("/api/dns/adm/{network}/custom", logic.SecurityCheck(false, http.HandlerFunc(getCustomDNS))).Methods(http.MethodGet)
-	r.HandleFunc("/api/dns/adm/{network}", logic.SecurityCheck(false, http.HandlerFunc(getDNS))).Methods(http.MethodGet)
-	r.HandleFunc("/api/dns/{network}", logic.SecurityCheck(false, http.HandlerFunc(createDNS))).Methods(http.MethodPost)
-	r.HandleFunc("/api/dns/adm/pushdns", logic.SecurityCheck(false, http.HandlerFunc(pushDNS))).Methods(http.MethodPost)
-	r.HandleFunc("/api/dns/{network}/{domain}", logic.SecurityCheck(false, http.HandlerFunc(deleteDNS))).Methods(http.MethodDelete)
+	r.HandleFunc("/api/dns/adm/{network}/nodes", logic.SecurityCheck(true, http.HandlerFunc(getNodeDNS))).Methods(http.MethodGet)
+	r.HandleFunc("/api/dns/adm/{network}/custom", logic.SecurityCheck(true, http.HandlerFunc(getCustomDNS))).Methods(http.MethodGet)
+	r.HandleFunc("/api/dns/adm/{network}", logic.SecurityCheck(true, http.HandlerFunc(getDNS))).Methods(http.MethodGet)
+	r.HandleFunc("/api/dns/{network}", logic.SecurityCheck(true, http.HandlerFunc(createDNS))).Methods(http.MethodPost)
+	r.HandleFunc("/api/dns/adm/pushdns", logic.SecurityCheck(true, http.HandlerFunc(pushDNS))).Methods(http.MethodPost)
+	r.HandleFunc("/api/dns/{network}/{domain}", logic.SecurityCheck(true, http.HandlerFunc(deleteDNS))).Methods(http.MethodDelete)
 }
 }
 
 
 // swagger:route GET /api/dns/adm/{network}/nodes dns getNodeDNS
 // swagger:route GET /api/dns/adm/{network}/nodes dns getNodeDNS

+ 29 - 6
controllers/dns_test.go

@@ -7,8 +7,10 @@ import (
 
 
 	"github.com/google/uuid"
 	"github.com/google/uuid"
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/assert"
+	"github.com/txn2/txeh"
 	"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
 	"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
 
 
+	"github.com/gravitl/netmaker/functions"
 	"github.com/gravitl/netmaker/logic"
 	"github.com/gravitl/netmaker/logic"
 	"github.com/gravitl/netmaker/models"
 	"github.com/gravitl/netmaker/models"
 )
 )
@@ -50,6 +52,8 @@ func TestGetNodeDNS(t *testing.T) {
 	deleteAllNetworks()
 	deleteAllNetworks()
 	createNet()
 	createNet()
 	createHost()
 	createHost()
+	err := functions.SetDNSDir()
+	assert.Nil(t, err)
 	t.Run("NoNodes", func(t *testing.T) {
 	t.Run("NoNodes", func(t *testing.T) {
 		dns, _ := logic.GetNodeDNS("skynet")
 		dns, _ := logic.GetNodeDNS("skynet")
 		assert.Equal(t, []models.DNSEntry(nil), dns)
 		assert.Equal(t, []models.DNSEntry(nil), dns)
@@ -202,22 +206,28 @@ func TestCreateDNS(t *testing.T) {
 func TestSetDNS(t *testing.T) {
 func TestSetDNS(t *testing.T) {
 	deleteAllDNS(t)
 	deleteAllDNS(t)
 	deleteAllNetworks()
 	deleteAllNetworks()
+	etc, err := txeh.NewHosts(&txeh.HostsConfig{})
+	assert.Nil(t, err)
+	err = functions.SetDNSDir()
+	assert.Nil(t, err)
 	t.Run("NoNetworks", func(t *testing.T) {
 	t.Run("NoNetworks", func(t *testing.T) {
 		err := logic.SetDNS()
 		err := logic.SetDNS()
 		assert.Nil(t, err)
 		assert.Nil(t, err)
-		info, err := os.Stat("./config/dnsconfig/netmaker.hosts")
+		info, err := txeh.NewHosts(&txeh.HostsConfig{
+			ReadFilePath: "./config/dnsconfig/netmaker.hosts",
+		})
 		assert.Nil(t, err)
 		assert.Nil(t, err)
-		assert.False(t, info.IsDir())
-		assert.Equal(t, int64(0), info.Size())
+		assert.Equal(t, etc.RenderHostsFile(), info.RenderHostsFile())
 	})
 	})
 	t.Run("NoEntries", func(t *testing.T) {
 	t.Run("NoEntries", func(t *testing.T) {
 		createNet()
 		createNet()
 		err := logic.SetDNS()
 		err := logic.SetDNS()
 		assert.Nil(t, err)
 		assert.Nil(t, err)
-		info, err := os.Stat("./config/dnsconfig/netmaker.hosts")
+		info, err := txeh.NewHosts(&txeh.HostsConfig{
+			ReadFilePath: "./config/dnsconfig/netmaker.hosts",
+		})
 		assert.Nil(t, err)
 		assert.Nil(t, err)
-		assert.False(t, info.IsDir())
-		assert.Equal(t, int64(0), info.Size())
+		assert.Equal(t, etc.RenderHostsFile(), info.RenderHostsFile())
 	})
 	})
 	t.Run("NodeExists", func(t *testing.T) {
 	t.Run("NodeExists", func(t *testing.T) {
 		createTestNode()
 		createTestNode()
@@ -400,6 +410,19 @@ func TestValidateDNSCreate(t *testing.T) {
 		assert.NotNil(t, err)
 		assert.NotNil(t, err)
 		assert.Contains(t, err.Error(), "Field validation for 'Name' failed on the 'name_unique' tag")
 		assert.Contains(t, err.Error(), "Field validation for 'Name' failed on the 'name_unique' tag")
 	})
 	})
+	t.Run("WhiteSpace", func(t *testing.T) {
+		entry := models.DNSEntry{Address: "10.10.10.5", Name: "white space", Network: "skynet"}
+		err := logic.ValidateDNSCreate(entry)
+		assert.NotNil(t, err)
+		assert.Contains(t, err.Error(), "Field validation for 'Name' failed on the 'whitespace' tag")
+	})
+	t.Run("AllSpaces", func(t *testing.T) {
+		entry := models.DNSEntry{Address: "10.10.10.5", Name: "     ", Network: "skynet"}
+		err := logic.ValidateDNSCreate(entry)
+		assert.NotNil(t, err)
+		assert.Contains(t, err.Error(), "Field validation for 'Name' failed on the 'whitespace' tag")
+	})
+
 }
 }
 
 
 func createHost() {
 func createHost() {

+ 1 - 1
controllers/docs.go

@@ -10,7 +10,7 @@
 //
 //
 //	Schemes: https
 //	Schemes: https
 //	BasePath: /
 //	BasePath: /
-//	Version: 0.20.6
+//	Version: 0.21.0
 //	Host: netmaker.io
 //	Host: netmaker.io
 //
 //
 //	Consumes:
 //	Consumes:

+ 3 - 13
controllers/enrollmentkeys.go

@@ -17,7 +17,7 @@ import (
 
 
 func enrollmentKeyHandlers(r *mux.Router) {
 func enrollmentKeyHandlers(r *mux.Router) {
 	r.HandleFunc("/api/v1/enrollment-keys", logic.SecurityCheck(true, http.HandlerFunc(createEnrollmentKey))).Methods(http.MethodPost)
 	r.HandleFunc("/api/v1/enrollment-keys", logic.SecurityCheck(true, http.HandlerFunc(createEnrollmentKey))).Methods(http.MethodPost)
-	r.HandleFunc("/api/v1/enrollment-keys", logic.SecurityCheck(false, http.HandlerFunc(getEnrollmentKeys))).Methods(http.MethodGet)
+	r.HandleFunc("/api/v1/enrollment-keys", logic.SecurityCheck(true, http.HandlerFunc(getEnrollmentKeys))).Methods(http.MethodGet)
 	r.HandleFunc("/api/v1/enrollment-keys/{keyID}", logic.SecurityCheck(true, http.HandlerFunc(deleteEnrollmentKey))).Methods(http.MethodDelete)
 	r.HandleFunc("/api/v1/enrollment-keys/{keyID}", logic.SecurityCheck(true, http.HandlerFunc(deleteEnrollmentKey))).Methods(http.MethodDelete)
 	r.HandleFunc("/api/v1/host/register/{token}", http.HandlerFunc(handleHostRegister)).Methods(http.MethodPost)
 	r.HandleFunc("/api/v1/host/register/{token}", http.HandlerFunc(handleHostRegister)).Methods(http.MethodPost)
 }
 }
@@ -40,20 +40,10 @@ func getEnrollmentKeys(w http.ResponseWriter, r *http.Request) {
 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
 		return
 		return
 	}
 	}
-	isMasterAdmin := r.Header.Get("ismaster") == "yes"
-	// regular user flow
-	user, err := logic.GetUser(r.Header.Get("user"))
-	if err != nil && !isMasterAdmin {
-		logger.Log(0, r.Header.Get("user"), "failed to fetch user: ", err.Error())
-		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
-		return
-	}
-	// TODO drop double pointer
+
 	ret := []*models.EnrollmentKey{}
 	ret := []*models.EnrollmentKey{}
 	for _, key := range keys {
 	for _, key := range keys {
-		if !isMasterAdmin && !logic.UserHasNetworksAccess(key.Networks, user) {
-			continue
-		}
+		key := key
 		if err = logic.Tokenize(key, servercfg.GetAPIHost()); err != nil {
 		if err = logic.Tokenize(key, servercfg.GetAPIHost()); err != nil {
 			logger.Log(0, r.Header.Get("user"), "failed to get token values for keys:", err.Error())
 			logger.Log(0, r.Header.Get("user"), "failed to get token values for keys:", err.Error())
 			logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
 			logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))

+ 88 - 146
controllers/ext_client.go

@@ -12,9 +12,9 @@ import (
 	"github.com/gravitl/netmaker/database"
 	"github.com/gravitl/netmaker/database"
 	"github.com/gravitl/netmaker/logger"
 	"github.com/gravitl/netmaker/logger"
 	"github.com/gravitl/netmaker/logic"
 	"github.com/gravitl/netmaker/logic"
-	"github.com/gravitl/netmaker/logic/pro"
+
 	"github.com/gravitl/netmaker/models"
 	"github.com/gravitl/netmaker/models"
-	"github.com/gravitl/netmaker/models/promodels"
+
 	"github.com/gravitl/netmaker/mq"
 	"github.com/gravitl/netmaker/mq"
 	"github.com/skip2/go-qrcode"
 	"github.com/skip2/go-qrcode"
 	"golang.org/x/exp/slog"
 	"golang.org/x/exp/slog"
@@ -23,13 +23,13 @@ import (
 
 
 func extClientHandlers(r *mux.Router) {
 func extClientHandlers(r *mux.Router) {
 
 
-	r.HandleFunc("/api/extclients", logic.SecurityCheck(false, http.HandlerFunc(getAllExtClients))).Methods(http.MethodGet)
-	r.HandleFunc("/api/extclients/{network}", logic.SecurityCheck(false, http.HandlerFunc(getNetworkExtClients))).Methods(http.MethodGet)
+	r.HandleFunc("/api/extclients", logic.SecurityCheck(true, http.HandlerFunc(getAllExtClients))).Methods(http.MethodGet)
+	r.HandleFunc("/api/extclients/{network}", logic.SecurityCheck(true, http.HandlerFunc(getNetworkExtClients))).Methods(http.MethodGet)
 	r.HandleFunc("/api/extclients/{network}/{clientid}", logic.SecurityCheck(false, http.HandlerFunc(getExtClient))).Methods(http.MethodGet)
 	r.HandleFunc("/api/extclients/{network}/{clientid}", logic.SecurityCheck(false, http.HandlerFunc(getExtClient))).Methods(http.MethodGet)
-	r.HandleFunc("/api/extclients/{network}/{clientid}/{type}", logic.NetUserSecurityCheck(false, true, http.HandlerFunc(getExtClientConf))).Methods(http.MethodGet)
-	r.HandleFunc("/api/extclients/{network}/{clientid}", logic.NetUserSecurityCheck(false, true, http.HandlerFunc(updateExtClient))).Methods(http.MethodPut)
-	r.HandleFunc("/api/extclients/{network}/{clientid}", logic.NetUserSecurityCheck(false, true, http.HandlerFunc(deleteExtClient))).Methods(http.MethodDelete)
-	r.HandleFunc("/api/extclients/{network}/{nodeid}", logic.NetUserSecurityCheck(false, true, checkFreeTierLimits(limitChoiceMachines, http.HandlerFunc(createExtClient)))).Methods(http.MethodPost)
+	r.HandleFunc("/api/extclients/{network}/{clientid}/{type}", logic.SecurityCheck(false, http.HandlerFunc(getExtClientConf))).Methods(http.MethodGet)
+	r.HandleFunc("/api/extclients/{network}/{clientid}", logic.SecurityCheck(false, http.HandlerFunc(updateExtClient))).Methods(http.MethodPut)
+	r.HandleFunc("/api/extclients/{network}/{clientid}", logic.SecurityCheck(false, http.HandlerFunc(deleteExtClient))).Methods(http.MethodDelete)
+	r.HandleFunc("/api/extclients/{network}/{nodeid}", logic.SecurityCheck(false, checkFreeTierLimits(limitChoiceMachines, http.HandlerFunc(createExtClient)))).Methods(http.MethodPost)
 }
 }
 
 
 func checkIngressExists(nodeID string) bool {
 func checkIngressExists(nodeID string) bool {
@@ -94,29 +94,18 @@ func getAllExtClients(w http.ResponseWriter, r *http.Request) {
 	networksSlice := []string{}
 	networksSlice := []string{}
 	marshalErr := json.Unmarshal([]byte(headerNetworks), &networksSlice)
 	marshalErr := json.Unmarshal([]byte(headerNetworks), &networksSlice)
 	if marshalErr != nil {
 	if marshalErr != nil {
-		logger.Log(0, "error unmarshalling networks: ",
-			marshalErr.Error())
+		slog.Error("error unmarshalling networks", "error", marshalErr.Error())
 		logic.ReturnErrorResponse(w, r, logic.FormatError(marshalErr, "internal"))
 		logic.ReturnErrorResponse(w, r, logic.FormatError(marshalErr, "internal"))
 		return
 		return
 	}
 	}
-	clients := []models.ExtClient{}
+
 	var err error
 	var err error
-	if len(networksSlice) > 0 && networksSlice[0] == logic.ALL_NETWORK_ACCESS {
-		clients, err = logic.GetAllExtClients()
-		if err != nil && !database.IsEmptyRecord(err) {
-			logger.Log(0, "failed to get all extclients: ", err.Error())
-			logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
-			return
-		}
-	} else {
-		for _, network := range networksSlice {
-			extclients, err := logic.GetNetworkExtClients(network)
-			if err == nil {
-				clients = append(clients, extclients...)
-			}
-		}
+	clients, err := logic.GetAllExtClients()
+	if err != nil && !database.IsEmptyRecord(err) {
+		logger.Log(0, "failed to get all extclients: ", err.Error())
+		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
+		return
 	}
 	}
-
 	//Return all the extclients in JSON format
 	//Return all the extclients in JSON format
 	logic.SortExtClient(clients[:])
 	logic.SortExtClient(clients[:])
 	w.WriteHeader(http.StatusOK)
 	w.WriteHeader(http.StatusOK)
@@ -149,6 +138,14 @@ func getExtClient(w http.ResponseWriter, r *http.Request) {
 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
 		return
 		return
 	}
 	}
+	if !logic.IsUserAllowedAccessToExtClient(r.Header.Get("user"), client) {
+		// check if user has access to extclient
+		slog.Error("failed to get extclient", "network", network, "clientID",
+			clientid, "error", errors.New("access is denied"))
+		logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("access is denied"), "forbidden"))
+		return
+
+	}
 
 
 	w.WriteHeader(http.StatusOK)
 	w.WriteHeader(http.StatusOK)
 	json.NewEncoder(w).Encode(client)
 	json.NewEncoder(w).Encode(client)
@@ -179,6 +176,12 @@ func getExtClientConf(w http.ResponseWriter, r *http.Request) {
 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
 		return
 		return
 	}
 	}
+	if !logic.IsUserAllowedAccessToExtClient(r.Header.Get("user"), client) {
+		slog.Error("failed to get extclient", "network", networkid, "clientID",
+			clientid, "error", errors.New("access is denied"))
+		logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("access is denied"), "forbidden"))
+		return
+	}
 
 
 	gwnode, err := logic.GetNodeByID(client.IngressGatewayID)
 	gwnode, err := logic.GetNodeByID(client.IngressGatewayID)
 	if err != nil {
 	if err != nil {
@@ -323,7 +326,6 @@ func createExtClient(w http.ResponseWriter, r *http.Request) {
 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
 		return
 		return
 	}
 	}
-
 	var customExtClient models.CustomExtClient
 	var customExtClient models.CustomExtClient
 
 
 	if err := json.NewDecoder(r.Body).Decode(&customExtClient); err != nil {
 	if err := json.NewDecoder(r.Body).Decode(&customExtClient); err != nil {
@@ -334,9 +336,6 @@ func createExtClient(w http.ResponseWriter, r *http.Request) {
 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
 		return
 		return
 	}
 	}
-	extclient := logic.UpdateExtClient(&models.ExtClient{}, &customExtClient)
-
-	extclient.IngressGatewayID = nodeid
 	node, err := logic.GetNodeByID(nodeid)
 	node, err := logic.GetNodeByID(nodeid)
 	if err != nil {
 	if err != nil {
 		logger.Log(0, r.Header.Get("user"),
 		logger.Log(0, r.Header.Get("user"),
@@ -344,6 +343,48 @@ func createExtClient(w http.ResponseWriter, r *http.Request) {
 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
 		return
 		return
 	}
 	}
+	var userName string
+	if r.Header.Get("ismaster") == "yes" {
+		userName = logic.MasterUser
+	} else {
+		caller, err := logic.GetUser(r.Header.Get("user"))
+		if err != nil {
+			logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
+			return
+		}
+		userName = caller.UserName
+		if !caller.IsAdmin && !caller.IsSuperAdmin {
+			if _, ok := caller.RemoteGwIDs[nodeid]; !ok {
+				err = errors.New("permission denied")
+				slog.Error("failed to create extclient", "error", err)
+				logic.ReturnErrorResponse(w, r, logic.FormatError(err, "forbidden"))
+				return
+			}
+			// check if user has a config already for remote access client
+			extclients, err := logic.GetNetworkExtClients(node.Network)
+			if err != nil {
+				slog.Error("failed to get extclients", "error", err)
+				logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
+				return
+			}
+			for _, extclient := range extclients {
+				if extclient.RemoteAccessClientID != "" &&
+					extclient.RemoteAccessClientID == customExtClient.RemoteAccessClientID && nodeid == extclient.IngressGatewayID {
+					// extclient on the gw already exists for the remote access client
+					err = errors.New("remote client config already exists on the gateway")
+					slog.Error("failed to get extclients", "error", err)
+					logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
+					return
+				}
+			}
+		}
+	}
+
+	extclient := logic.UpdateExtClient(&models.ExtClient{}, &customExtClient)
+	extclient.OwnerID = userName
+	extclient.RemoteAccessClientID = customExtClient.RemoteAccessClientID
+	extclient.IngressGatewayID = nodeid
+
 	extclient.Network = node.Network
 	extclient.Network = node.Network
 	host, err := logic.GetHost(node.HostID.String())
 	host, err := logic.GetHost(node.HostID.String())
 	if err != nil {
 	if err != nil {
@@ -372,33 +413,13 @@ func createExtClient(w http.ResponseWriter, r *http.Request) {
 		return
 		return
 	}
 	}
 
 
-	var isAdmin bool
-	if r.Header.Get("ismaster") != "yes" {
-		userID := r.Header.Get("user")
-		if isAdmin, err = checkProClientAccess(userID, extclient.ClientID, &parentNetwork); err != nil {
-			slog.Error("pro client access check failed", "user", userID, "network", node.Network, "error", err)
-			logic.DeleteExtClient(node.Network, extclient.ClientID)
-			logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
-			return
-		}
-		if !isAdmin {
-			if err = pro.AssociateNetworkUserClient(userID, node.Network, extclient.ClientID); err != nil {
-				logger.Log(0, "failed to associate client", extclient.ClientID, "to user", userID)
-			}
-			extclient.OwnerID = userID
-			if err := logic.SaveExtClient(&extclient); err != nil {
-				logger.Log(0, "failed to add owner id", userID, "to client", extclient.ClientID)
-			}
-		}
-	}
-
 	slog.Info("created extclient", "user", r.Header.Get("user"), "network", node.Network, "clientid", extclient.ClientID)
 	slog.Info("created extclient", "user", r.Header.Get("user"), "network", node.Network, "clientid", extclient.ClientID)
 	w.WriteHeader(http.StatusOK)
 	w.WriteHeader(http.StatusOK)
 	go func() {
 	go func() {
 		if err := mq.PublishPeerUpdate(); err != nil {
 		if err := mq.PublishPeerUpdate(); err != nil {
 			logger.Log(1, "error setting ext peers on "+nodeid+": "+err.Error())
 			logger.Log(1, "error setting ext peers on "+nodeid+": "+err.Error())
 		}
 		}
-		if err := mq.PublishExtCLientDNS(&extclient); err != nil {
+		if err := mq.PublishExtClientDNS(&extclient); err != nil {
 			logger.Log(1, "error publishing extclient dns", err.Error())
 			logger.Log(1, "error publishing extclient dns", err.Error())
 		}
 		}
 	}()
 	}()
@@ -431,12 +452,21 @@ func updateExtClient(w http.ResponseWriter, r *http.Request) {
 		return
 		return
 	}
 	}
 	clientid := params["clientid"]
 	clientid := params["clientid"]
+	network := params["network"]
 	oldExtClient, err := logic.GetExtClientByName(clientid)
 	oldExtClient, err := logic.GetExtClientByName(clientid)
 	if err != nil {
 	if err != nil {
 		slog.Error("failed to retrieve extclient", "user", r.Header.Get("user"), "id", clientid, "error", err)
 		slog.Error("failed to retrieve extclient", "user", r.Header.Get("user"), "id", clientid, "error", err)
 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
 		return
 		return
 	}
 	}
+	if !logic.IsUserAllowedAccessToExtClient(r.Header.Get("user"), oldExtClient) {
+		// check if user has access to extclient
+		slog.Error("failed to get extclient", "network", network, "clientID",
+			clientid, "error", errors.New("access is denied"))
+		logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("access is denied"), "forbidden"))
+		return
+
+	}
 	if oldExtClient.ClientID == update.ClientID {
 	if oldExtClient.ClientID == update.ClientID {
 		if err := validateCustomExtClient(&update, false); err != nil {
 		if err := validateCustomExtClient(&update, false); err != nil {
 			logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
 			logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
@@ -448,31 +478,12 @@ func updateExtClient(w http.ResponseWriter, r *http.Request) {
 			return
 			return
 		}
 		}
 	}
 	}
-
-	// == PRO ==
-	//networkName := params["network"]
 	var changedID = update.ClientID != oldExtClient.ClientID
 	var changedID = update.ClientID != oldExtClient.ClientID
-	if r.Header.Get("ismaster") != "yes" {
-		userID := r.Header.Get("user")
-		_, doesOwn := doesUserOwnClient(userID, params["clientid"], oldExtClient.Network)
-		if !doesOwn {
-			logic.ReturnErrorResponse(w, r, logic.FormatError(fmt.Errorf("user not permitted"), "internal"))
-			return
-		}
-	}
-	if changedID && oldExtClient.OwnerID != "" {
-		if err := pro.DissociateNetworkUserClient(oldExtClient.OwnerID, oldExtClient.Network, oldExtClient.ClientID); err != nil {
-			logger.Log(0, "failed to dissociate client", oldExtClient.ClientID, "from user", oldExtClient.OwnerID)
-		}
-		if err := pro.AssociateNetworkUserClient(oldExtClient.OwnerID, oldExtClient.Network, update.ClientID); err != nil {
-			logger.Log(0, "failed to associate client", update.ClientID, "to user", oldExtClient.OwnerID)
-		}
-	}
+
 	if len(update.DeniedACLs) != len(oldExtClient.DeniedACLs) {
 	if len(update.DeniedACLs) != len(oldExtClient.DeniedACLs) {
 		sendPeerUpdate = true
 		sendPeerUpdate = true
 		logic.SetClientACLs(&oldExtClient, update.DeniedACLs)
 		logic.SetClientACLs(&oldExtClient, update.DeniedACLs)
 	}
 	}
-	// == END PRO ==
 
 
 	if update.Enabled != oldExtClient.Enabled {
 	if update.Enabled != oldExtClient.Enabled {
 		sendPeerUpdate = true
 		sendPeerUpdate = true
@@ -535,6 +546,12 @@ func deleteExtClient(w http.ResponseWriter, r *http.Request) {
 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
 		return
 		return
 	}
 	}
+	if !logic.IsUserAllowedAccessToExtClient(r.Header.Get("user"), extclient) {
+		slog.Error("failed to get extclient", "network", network, "clientID",
+			clientid, "error", errors.New("access is denied"))
+		logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("access is denied"), "forbidden"))
+		return
+	}
 	ingressnode, err := logic.GetNodeByID(extclient.IngressGatewayID)
 	ingressnode, err := logic.GetNodeByID(extclient.IngressGatewayID)
 	if err != nil {
 	if err != nil {
 		logger.Log(0, r.Header.Get("user"),
 		logger.Log(0, r.Header.Get("user"),
@@ -543,24 +560,6 @@ func deleteExtClient(w http.ResponseWriter, r *http.Request) {
 		return
 		return
 	}
 	}
 
 
-	// == PRO ==
-	if r.Header.Get("ismaster") != "yes" {
-		userID, clientID, networkName := r.Header.Get("user"), params["clientid"], params["network"]
-		_, doesOwn := doesUserOwnClient(userID, clientID, networkName)
-		if !doesOwn {
-			logic.ReturnErrorResponse(w, r, logic.FormatError(fmt.Errorf("user not permitted"), "internal"))
-			return
-		}
-	}
-
-	if extclient.OwnerID != "" {
-		if err = pro.DissociateNetworkUserClient(extclient.OwnerID, extclient.Network, extclient.ClientID); err != nil {
-			logger.Log(0, "failed to dissociate client", extclient.ClientID, "from user", extclient.OwnerID)
-		}
-	}
-
-	// == END PRO ==
-
 	err = logic.DeleteExtClient(params["network"], params["clientid"])
 	err = logic.DeleteExtClient(params["network"], params["clientid"])
 	if err != nil {
 	if err != nil {
 		logger.Log(0, r.Header.Get("user"),
 		logger.Log(0, r.Header.Get("user"),
@@ -584,63 +583,6 @@ func deleteExtClient(w http.ResponseWriter, r *http.Request) {
 	logic.ReturnSuccessResponse(w, r, params["clientid"]+" deleted.")
 	logic.ReturnSuccessResponse(w, r, params["clientid"]+" deleted.")
 }
 }
 
 
-func checkProClientAccess(username, clientID string, network *models.Network) (bool, error) {
-	u, err := logic.GetUser(username)
-	if err != nil {
-		return false, err
-	}
-	if u.IsAdmin {
-		return true, nil
-	}
-
-	netUser, err := pro.GetNetworkUser(network.NetID, promodels.NetworkUserID(u.UserName))
-	if err != nil {
-		return false, err
-	}
-
-	if netUser.AccessLevel == pro.NET_ADMIN {
-		return false, nil
-	}
-
-	if netUser.AccessLevel == pro.NO_ACCESS {
-		return false, fmt.Errorf("user does not have access")
-	}
-
-	if !(len(netUser.Clients) < netUser.ClientLimit) {
-		return false, fmt.Errorf("user can not create more clients")
-	}
-
-	if netUser.AccessLevel < pro.NO_ACCESS {
-		netUser.Clients = append(netUser.Clients, clientID)
-		if err = pro.UpdateNetworkUser(network.NetID, netUser); err != nil {
-			return false, err
-		}
-	}
-	return false, nil
-}
-
-// checks if net user owns an ext client or is an admin
-func doesUserOwnClient(username, clientID, network string) (bool, bool) {
-	u, err := logic.GetUser(username)
-	if err != nil {
-		return false, false
-	}
-	if u.IsAdmin {
-		return true, true
-	}
-
-	netUser, err := pro.GetNetworkUser(network, promodels.NetworkUserID(u.UserName))
-	if err != nil {
-		return false, false
-	}
-
-	if netUser.AccessLevel == pro.NET_ADMIN {
-		return false, true
-	}
-
-	return false, logic.StringSliceContains(netUser.Clients, clientID)
-}
-
 // validateCustomExtClient	Validates the extclient object
 // validateCustomExtClient	Validates the extclient object
 func validateCustomExtClient(customExtClient *models.CustomExtClient, checkID bool) error {
 func validateCustomExtClient(customExtClient *models.CustomExtClient, checkID bool) error {
 	//validate clientid
 	//validate clientid

+ 11 - 3
controllers/hosts.go

@@ -17,12 +17,12 @@ import (
 )
 )
 
 
 func hostHandlers(r *mux.Router) {
 func hostHandlers(r *mux.Router) {
-	r.HandleFunc("/api/hosts", logic.SecurityCheck(false, http.HandlerFunc(getHosts))).Methods(http.MethodGet)
+	r.HandleFunc("/api/hosts", logic.SecurityCheck(true, http.HandlerFunc(getHosts))).Methods(http.MethodGet)
 	r.HandleFunc("/api/hosts/keys", logic.SecurityCheck(true, http.HandlerFunc(updateAllKeys))).Methods(http.MethodPut)
 	r.HandleFunc("/api/hosts/keys", logic.SecurityCheck(true, http.HandlerFunc(updateAllKeys))).Methods(http.MethodPut)
 	r.HandleFunc("/api/hosts/{hostid}/keys", logic.SecurityCheck(true, http.HandlerFunc(updateKeys))).Methods(http.MethodPut)
 	r.HandleFunc("/api/hosts/{hostid}/keys", logic.SecurityCheck(true, http.HandlerFunc(updateKeys))).Methods(http.MethodPut)
 	r.HandleFunc("/api/hosts/{hostid}/sync", logic.SecurityCheck(true, http.HandlerFunc(syncHost))).Methods(http.MethodPost)
 	r.HandleFunc("/api/hosts/{hostid}/sync", logic.SecurityCheck(true, http.HandlerFunc(syncHost))).Methods(http.MethodPost)
 	r.HandleFunc("/api/hosts/{hostid}", logic.SecurityCheck(true, http.HandlerFunc(updateHost))).Methods(http.MethodPut)
 	r.HandleFunc("/api/hosts/{hostid}", logic.SecurityCheck(true, http.HandlerFunc(updateHost))).Methods(http.MethodPut)
-	r.HandleFunc("/api/hosts/{hostid}", logic.SecurityCheck(true, http.HandlerFunc(deleteHost))).Methods(http.MethodDelete)
+	r.HandleFunc("/api/hosts/{hostid}", Authorize(true, false, "all", http.HandlerFunc(deleteHost))).Methods(http.MethodDelete)
 	r.HandleFunc("/api/hosts/{hostid}/networks/{network}", logic.SecurityCheck(true, http.HandlerFunc(addHostToNetwork))).Methods(http.MethodPost)
 	r.HandleFunc("/api/hosts/{hostid}/networks/{network}", logic.SecurityCheck(true, http.HandlerFunc(addHostToNetwork))).Methods(http.MethodPost)
 	r.HandleFunc("/api/hosts/{hostid}/networks/{network}", logic.SecurityCheck(true, http.HandlerFunc(deleteHostFromNetwork))).Methods(http.MethodDelete)
 	r.HandleFunc("/api/hosts/{hostid}/networks/{network}", logic.SecurityCheck(true, http.HandlerFunc(deleteHostFromNetwork))).Methods(http.MethodDelete)
 	r.HandleFunc("/api/hosts/adm/authenticate", authenticateHost).Methods(http.MethodPost)
 	r.HandleFunc("/api/hosts/adm/authenticate", authenticateHost).Methods(http.MethodPost)
@@ -328,6 +328,14 @@ func deleteHostFromNetwork(w http.ResponseWriter, r *http.Request) {
 		// unset all the relayed nodes
 		// unset all the relayed nodes
 		logic.SetRelayedNodes(false, node.ID.String(), node.RelayedNodes)
 		logic.SetRelayedNodes(false, node.ID.String(), node.RelayedNodes)
 	}
 	}
+	if node.IsIngressGateway {
+		// delete ext clients belonging to ingress gateway
+		go func(node models.Node) {
+			if err = logic.DeleteGatewayExtClients(node.ID.String(), node.Network); err != nil {
+				slog.Error("failed to delete extclients", "gatewayid", node.ID.String(), "network", node.Network, "error", err.Error())
+			}
+		}(*node)
+	}
 	logger.Log(1, "deleting node", node.ID.String(), "from host", currHost.Name)
 	logger.Log(1, "deleting node", node.ID.String(), "from host", currHost.Name)
 	if err := logic.DeleteNode(node, forceDelete); err != nil {
 	if err := logic.DeleteNode(node, forceDelete); err != nil {
 		logic.ReturnErrorResponse(w, r, logic.FormatError(fmt.Errorf("failed to delete node"), "internal"))
 		logic.ReturnErrorResponse(w, r, logic.FormatError(fmt.Errorf("failed to delete node"), "internal"))
@@ -336,7 +344,7 @@ func deleteHostFromNetwork(w http.ResponseWriter, r *http.Request) {
 	node.Action = models.NODE_DELETE
 	node.Action = models.NODE_DELETE
 	node.PendingDelete = true
 	node.PendingDelete = true
 	// notify node change
 	// notify node change
-	runUpdates(node, false)
+	mq.RunUpdates(node, false)
 	go func() { // notify of peer change
 	go func() { // notify of peer change
 		err = mq.PublishDeletedNodePeerUpdate(node)
 		err = mq.PublishDeletedNodePeerUpdate(node)
 		if err != nil {
 		if err != nil {

+ 1 - 1
controllers/migrate.go

@@ -123,7 +123,7 @@ func migrate(w http.ResponseWriter, r *http.Request) {
 			if err != nil {
 			if err != nil {
 				logger.Log(0, "error creating ingress gateway for node", node.ID, err.Error())
 				logger.Log(0, "error creating ingress gateway for node", node.ID, err.Error())
 			}
 			}
-			runUpdates(&ingressNode, true)
+			mq.RunUpdates(&ingressNode, true)
 		}
 		}
 	}
 	}
 }
 }

+ 10 - 26
controllers/network.go

@@ -20,9 +20,9 @@ import (
 )
 )
 
 
 func networkHandlers(r *mux.Router) {
 func networkHandlers(r *mux.Router) {
-	r.HandleFunc("/api/networks", logic.SecurityCheck(false, http.HandlerFunc(getNetworks))).Methods(http.MethodGet)
+	r.HandleFunc("/api/networks", logic.SecurityCheck(true, http.HandlerFunc(getNetworks))).Methods(http.MethodGet)
 	r.HandleFunc("/api/networks", logic.SecurityCheck(true, checkFreeTierLimits(limitChoiceNetworks, http.HandlerFunc(createNetwork)))).Methods(http.MethodPost)
 	r.HandleFunc("/api/networks", logic.SecurityCheck(true, checkFreeTierLimits(limitChoiceNetworks, http.HandlerFunc(createNetwork)))).Methods(http.MethodPost)
-	r.HandleFunc("/api/networks/{networkname}", logic.SecurityCheck(false, http.HandlerFunc(getNetwork))).Methods(http.MethodGet)
+	r.HandleFunc("/api/networks/{networkname}", logic.SecurityCheck(true, http.HandlerFunc(getNetwork))).Methods(http.MethodGet)
 	r.HandleFunc("/api/networks/{networkname}", logic.SecurityCheck(true, http.HandlerFunc(deleteNetwork))).Methods(http.MethodDelete)
 	r.HandleFunc("/api/networks/{networkname}", logic.SecurityCheck(true, http.HandlerFunc(deleteNetwork))).Methods(http.MethodDelete)
 	r.HandleFunc("/api/networks/{networkname}", logic.SecurityCheck(true, http.HandlerFunc(updateNetwork))).Methods(http.MethodPut)
 	r.HandleFunc("/api/networks/{networkname}", logic.SecurityCheck(true, http.HandlerFunc(updateNetwork))).Methods(http.MethodPut)
 	// ACLs
 	// ACLs
@@ -42,29 +42,14 @@ func networkHandlers(r *mux.Router) {
 //			Responses:
 //			Responses:
 //				200: getNetworksSliceResponse
 //				200: getNetworksSliceResponse
 func getNetworks(w http.ResponseWriter, r *http.Request) {
 func getNetworks(w http.ResponseWriter, r *http.Request) {
-	networksSlice, marshalErr := getHeaderNetworks(r)
-	if marshalErr != nil {
-		logger.Log(0, r.Header.Get("user"), "error unmarshalling networks: ",
-			marshalErr.Error())
-		logic.ReturnErrorResponse(w, r, logic.FormatError(marshalErr, "badrequest"))
-		return
-	}
-	allnetworks := []models.Network{}
+
 	var err error
 	var err error
-	if len(networksSlice) > 0 && networksSlice[0] == logic.ALL_NETWORK_ACCESS {
-		allnetworks, err = logic.GetNetworks()
-		if err != nil && !database.IsEmptyRecord(err) {
-			logger.Log(0, r.Header.Get("user"), "failed to fetch networks: ", err.Error())
-			logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
-			return
-		}
-	} else {
-		for _, network := range networksSlice {
-			netObject, parentErr := logic.GetParentNetwork(network)
-			if parentErr == nil {
-				allnetworks = append(allnetworks, netObject)
-			}
-		}
+
+	allnetworks, err := logic.GetNetworks()
+	if err != nil && !database.IsEmptyRecord(err) {
+		slog.Error("failed to fetch networks", "error", err.Error())
+		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
+		return
 	}
 	}
 
 
 	logger.Log(2, r.Header.Get("user"), "fetched networks.")
 	logger.Log(2, r.Header.Get("user"), "fetched networks.")
@@ -326,8 +311,7 @@ func updateNetwork(w http.ResponseWriter, r *http.Request) {
 	}
 	}
 	// partial update
 	// partial update
 	netOld2 := netOld1
 	netOld2 := netOld1
-	netOld2.ProSettings = payload.ProSettings
-	_, _, _, _, _, err = logic.UpdateNetwork(&netOld1, &netOld2)
+	_, _, _, err = logic.UpdateNetwork(&netOld1, &netOld2)
 	if err != nil {
 	if err != nil {
 		slog.Info("failed to update network", "user", r.Header.Get("user"), "err", err)
 		slog.Info("failed to update network", "user", r.Header.Get("user"), "err", err)
 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))

+ 6 - 19
controllers/network_test.go

@@ -25,12 +25,10 @@ var netHost models.Host
 func TestMain(m *testing.M) {
 func TestMain(m *testing.M) {
 	database.InitializeDatabase()
 	database.InitializeDatabase()
 	defer database.CloseDB()
 	defer database.CloseDB()
-	logic.CreateAdmin(&models.User{
+	logic.CreateSuperAdmin(&models.User{
 		UserName: "admin",
 		UserName: "admin",
 		Password: "password",
 		Password: "password",
 		IsAdmin:  true,
 		IsAdmin:  true,
-		Networks: []string{},
-		Groups:   []string{},
 	})
 	})
 	peerUpdate := make(chan *models.Node)
 	peerUpdate := make(chan *models.Node)
 	go logic.ManageZombies(context.Background(), peerUpdate)
 	go logic.ManageZombies(context.Background(), peerUpdate)
@@ -91,27 +89,16 @@ func TestSecurityCheck(t *testing.T) {
 
 
 	os.Setenv("MASTER_KEY", "secretkey")
 	os.Setenv("MASTER_KEY", "secretkey")
 	t.Run("NoNetwork", func(t *testing.T) {
 	t.Run("NoNetwork", func(t *testing.T) {
-		networks, username, err := logic.UserPermissions(false, "", "Bearer secretkey")
+		username, err := logic.UserPermissions(false, "Bearer secretkey")
 		assert.Nil(t, err)
 		assert.Nil(t, err)
-		t.Log(networks, username)
-	})
-	t.Run("WithNetwork", func(t *testing.T) {
-		networks, username, err := logic.UserPermissions(false, "skynet", "Bearer secretkey")
-		assert.Nil(t, err)
-		t.Log(networks, username)
-	})
-	t.Run("BadNet", func(t *testing.T) {
-		t.Skip()
-		networks, username, err := logic.UserPermissions(false, "badnet", "Bearer secretkey")
-		assert.NotNil(t, err)
-		t.Log(err)
-		t.Log(networks, username)
+		t.Log(username)
 	})
 	})
+
 	t.Run("BadToken", func(t *testing.T) {
 	t.Run("BadToken", func(t *testing.T) {
-		networks, username, err := logic.UserPermissions(false, "skynet", "Bearer badkey")
+		username, err := logic.UserPermissions(false, "Bearer badkey")
 		assert.NotNil(t, err)
 		assert.NotNil(t, err)
 		t.Log(err)
 		t.Log(err)
-		t.Log(networks, username)
+		t.Log(username)
 	})
 	})
 }
 }
 
 

+ 33 - 105
controllers/node.go

@@ -10,9 +10,7 @@ import (
 	"github.com/gravitl/netmaker/database"
 	"github.com/gravitl/netmaker/database"
 	"github.com/gravitl/netmaker/logger"
 	"github.com/gravitl/netmaker/logger"
 	"github.com/gravitl/netmaker/logic"
 	"github.com/gravitl/netmaker/logic"
-	"github.com/gravitl/netmaker/logic/pro"
 	"github.com/gravitl/netmaker/models"
 	"github.com/gravitl/netmaker/models"
-	"github.com/gravitl/netmaker/models/promodels"
 	"github.com/gravitl/netmaker/mq"
 	"github.com/gravitl/netmaker/mq"
 	"github.com/gravitl/netmaker/servercfg"
 	"github.com/gravitl/netmaker/servercfg"
 	"golang.org/x/crypto/bcrypt"
 	"golang.org/x/crypto/bcrypt"
@@ -26,13 +24,12 @@ func nodeHandlers(r *mux.Router) {
 	r.HandleFunc("/api/nodes", Authorize(false, false, "user", http.HandlerFunc(getAllNodes))).Methods(http.MethodGet)
 	r.HandleFunc("/api/nodes", Authorize(false, false, "user", http.HandlerFunc(getAllNodes))).Methods(http.MethodGet)
 	r.HandleFunc("/api/nodes/{network}", Authorize(false, true, "network", http.HandlerFunc(getNetworkNodes))).Methods(http.MethodGet)
 	r.HandleFunc("/api/nodes/{network}", Authorize(false, true, "network", http.HandlerFunc(getNetworkNodes))).Methods(http.MethodGet)
 	r.HandleFunc("/api/nodes/{network}/{nodeid}", Authorize(true, true, "node", http.HandlerFunc(getNode))).Methods(http.MethodGet)
 	r.HandleFunc("/api/nodes/{network}/{nodeid}", Authorize(true, true, "node", http.HandlerFunc(getNode))).Methods(http.MethodGet)
-	r.HandleFunc("/api/nodes/{network}/{nodeid}", Authorize(false, true, "node", http.HandlerFunc(updateNode))).Methods(http.MethodPut)
+	r.HandleFunc("/api/nodes/{network}/{nodeid}", logic.SecurityCheck(true, http.HandlerFunc(updateNode))).Methods(http.MethodPut)
 	r.HandleFunc("/api/nodes/{network}/{nodeid}", Authorize(true, true, "node", http.HandlerFunc(deleteNode))).Methods(http.MethodDelete)
 	r.HandleFunc("/api/nodes/{network}/{nodeid}", Authorize(true, true, "node", http.HandlerFunc(deleteNode))).Methods(http.MethodDelete)
-	r.HandleFunc("/api/nodes/{network}/{nodeid}/creategateway", Authorize(false, true, "user", checkFreeTierLimits(limitChoiceEgress, http.HandlerFunc(createEgressGateway)))).Methods(http.MethodPost)
-	r.HandleFunc("/api/nodes/{network}/{nodeid}/deletegateway", Authorize(false, true, "user", http.HandlerFunc(deleteEgressGateway))).Methods(http.MethodDelete)
-	r.HandleFunc("/api/nodes/{network}/{nodeid}/createingress", logic.SecurityCheck(false, checkFreeTierLimits(limitChoiceIngress, http.HandlerFunc(createIngressGateway)))).Methods(http.MethodPost)
-	r.HandleFunc("/api/nodes/{network}/{nodeid}/deleteingress", logic.SecurityCheck(false, http.HandlerFunc(deleteIngressGateway))).Methods(http.MethodDelete)
-	r.HandleFunc("/api/nodes/{network}/{nodeid}", Authorize(true, true, "node", http.HandlerFunc(updateNode))).Methods(http.MethodPost)
+	r.HandleFunc("/api/nodes/{network}/{nodeid}/creategateway", logic.SecurityCheck(true, checkFreeTierLimits(limitChoiceEgress, http.HandlerFunc(createEgressGateway)))).Methods(http.MethodPost)
+	r.HandleFunc("/api/nodes/{network}/{nodeid}/deletegateway", logic.SecurityCheck(true, http.HandlerFunc(deleteEgressGateway))).Methods(http.MethodDelete)
+	r.HandleFunc("/api/nodes/{network}/{nodeid}/createingress", logic.SecurityCheck(true, checkFreeTierLimits(limitChoiceIngress, http.HandlerFunc(createIngressGateway)))).Methods(http.MethodPost)
+	r.HandleFunc("/api/nodes/{network}/{nodeid}/deleteingress", logic.SecurityCheck(true, http.HandlerFunc(deleteIngressGateway))).Methods(http.MethodDelete)
 	r.HandleFunc("/api/nodes/adm/{network}/authenticate", authenticate).Methods(http.MethodPost)
 	r.HandleFunc("/api/nodes/adm/{network}/authenticate", authenticate).Methods(http.MethodPost)
 	r.HandleFunc("/api/v1/nodes/migrate", migrate).Methods(http.MethodPost)
 	r.HandleFunc("/api/v1/nodes/migrate", migrate).Methods(http.MethodPost)
 }
 }
@@ -198,23 +195,18 @@ func Authorize(hostAllowed, networkCheck bool, authNetwork string, next http.Han
 
 
 			var isAuthorized = false
 			var isAuthorized = false
 			var nodeID = ""
 			var nodeID = ""
-			username, networks, isadmin, errN := logic.VerifyUserToken(authToken)
+			username, issuperadmin, isadmin, errN := logic.VerifyUserToken(authToken)
 			if errN != nil {
 			if errN != nil {
-				logic.ReturnErrorResponse(w, r, errorResponse)
+				logic.ReturnErrorResponse(w, r, logic.FormatError(errN, logic.Unauthorized_Msg))
 				return
 				return
 			}
 			}
 
 
-			isnetadmin := isadmin
-			if errN == nil && isadmin {
+			isnetadmin := issuperadmin || isadmin
+			if errN == nil && (issuperadmin || isadmin) {
 				nodeID = "mastermac"
 				nodeID = "mastermac"
 				isAuthorized = true
 				isAuthorized = true
 				r.Header.Set("ismasterkey", "yes")
 				r.Header.Set("ismasterkey", "yes")
 			}
 			}
-			if !isadmin && params["network"] != "" {
-				if logic.StringSliceContains(networks, params["network"]) && pro.IsUserNetAdmin(params["network"], username) {
-					isnetadmin = true
-				}
-			}
 			//The mastermac (login with masterkey from config) can do everything!! May be dangerous.
 			//The mastermac (login with masterkey from config) can do everything!! May be dangerous.
 			if nodeID == "mastermac" {
 			if nodeID == "mastermac" {
 				isAuthorized = true
 				isAuthorized = true
@@ -326,14 +318,6 @@ func getAllNodes(w http.ResponseWriter, r *http.Request) {
 			logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
 			logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
 			return
 			return
 		}
 		}
-	} else {
-		nodes, err = getUsersNodes(*user)
-		if err != nil {
-			logger.Log(0, r.Header.Get("user"),
-				"error fetching nodes: ", err.Error())
-			logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
-			return
-		}
 	}
 	}
 	// return all the nodes in JSON/API format
 	// return all the nodes in JSON/API format
 	apiNodes := logic.GetAllNodesAPI(nodes[:])
 	apiNodes := logic.GetAllNodesAPI(nodes[:])
@@ -343,19 +327,6 @@ func getAllNodes(w http.ResponseWriter, r *http.Request) {
 	json.NewEncoder(w).Encode(apiNodes)
 	json.NewEncoder(w).Encode(apiNodes)
 }
 }
 
 
-func getUsersNodes(user models.User) ([]models.Node, error) {
-	var nodes []models.Node
-	var err error
-	for _, networkName := range user.Networks {
-		tmpNodes, err := logic.GetNetworkNodes(networkName)
-		if err != nil {
-			continue
-		}
-		nodes = append(nodes, tmpNodes...)
-	}
-	return nodes, err
-}
-
 // swagger:route GET /api/nodes/{network}/{nodeid} nodes getNode
 // swagger:route GET /api/nodes/{network}/{nodeid} nodes getNode
 //
 //
 // Get an individual node.
 // Get an individual node.
@@ -415,7 +386,7 @@ func getNode(w http.ResponseWriter, r *http.Request) {
 		PeerIDs:      hostPeerUpdate.PeerIDs,
 		PeerIDs:      hostPeerUpdate.PeerIDs,
 	}
 	}
 
 
-	if servercfg.Is_EE && nodeRequest {
+	if servercfg.IsPro && nodeRequest {
 		if err = logic.EnterpriseResetAllPeersFailovers(node.ID, node.Network); err != nil {
 		if err = logic.EnterpriseResetAllPeersFailovers(node.ID, node.Network); err != nil {
 			logger.Log(1, "failed to reset failover list during node config pull", node.ID.String(), node.Network)
 			logger.Log(1, "failed to reset failover list during node config pull", node.ID.String(), node.Network)
 		}
 		}
@@ -471,7 +442,7 @@ func createEgressGateway(w http.ResponseWriter, r *http.Request) {
 	go func() {
 	go func() {
 		mq.PublishPeerUpdate()
 		mq.PublishPeerUpdate()
 	}()
 	}()
-	runUpdates(&node, true)
+	mq.RunUpdates(&node, true)
 }
 }
 
 
 // swagger:route DELETE /api/nodes/{network}/{nodeid}/deletegateway nodes deleteEgressGateway
 // swagger:route DELETE /api/nodes/{network}/{nodeid}/deletegateway nodes deleteEgressGateway
@@ -512,7 +483,7 @@ func deleteEgressGateway(w http.ResponseWriter, r *http.Request) {
 	go func() {
 	go func() {
 		mq.PublishPeerUpdate()
 		mq.PublishPeerUpdate()
 	}()
 	}()
-	runUpdates(&node, true)
+	mq.RunUpdates(&node, true)
 }
 }
 
 
 // == INGRESS ==
 // == INGRESS ==
@@ -549,7 +520,7 @@ func createIngressGateway(w http.ResponseWriter, r *http.Request) {
 		return
 		return
 	}
 	}
 
 
-	if servercfg.Is_EE && request.Failover {
+	if servercfg.IsPro && request.Failover {
 		if err = logic.EnterpriseResetFailoverFunc(node.Network); err != nil {
 		if err = logic.EnterpriseResetFailoverFunc(node.Network); err != nil {
 			logger.Log(1, "failed to reset failover list during failover create", node.ID.String(), node.Network)
 			logger.Log(1, "failed to reset failover list during failover create", node.ID.String(), node.Network)
 		}
 		}
@@ -560,7 +531,7 @@ func createIngressGateway(w http.ResponseWriter, r *http.Request) {
 	w.WriteHeader(http.StatusOK)
 	w.WriteHeader(http.StatusOK)
 	json.NewEncoder(w).Encode(apiNode)
 	json.NewEncoder(w).Encode(apiNode)
 
 
-	runUpdates(&node, true)
+	mq.RunUpdates(&node, true)
 }
 }
 
 
 // swagger:route DELETE /api/nodes/{network}/{nodeid}/deleteingress nodes deleteIngressGateway
 // swagger:route DELETE /api/nodes/{network}/{nodeid}/deleteingress nodes deleteIngressGateway
@@ -593,7 +564,7 @@ func deleteIngressGateway(w http.ResponseWriter, r *http.Request) {
 		return
 		return
 	}
 	}
 
 
-	if servercfg.Is_EE && wasFailover {
+	if servercfg.IsPro && wasFailover {
 		if err = logic.EnterpriseResetFailoverFunc(node.Network); err != nil {
 		if err = logic.EnterpriseResetFailoverFunc(node.Network); err != nil {
 			logger.Log(1, "failed to reset failover list during failover create", node.ID.String(), node.Network)
 			logger.Log(1, "failed to reset failover list during failover create", node.ID.String(), node.Network)
 		}
 		}
@@ -620,7 +591,7 @@ func deleteIngressGateway(w http.ResponseWriter, r *http.Request) {
 		}
 		}
 	}
 	}
 
 
-	runUpdates(&node, true)
+	mq.RunUpdates(&node, true)
 }
 }
 
 
 // swagger:route PUT /api/nodes/{network}/{nodeid} nodes updateNode
 // swagger:route PUT /api/nodes/{network}/{nodeid} nodes updateNode
@@ -655,18 +626,7 @@ func updateNode(w http.ResponseWriter, r *http.Request) {
 		return
 		return
 	}
 	}
 	newNode := newData.ConvertToServerNode(&currentNode)
 	newNode := newData.ConvertToServerNode(&currentNode)
-	relayupdate := false
-	if servercfg.Is_EE && newNode.IsRelay && len(newNode.RelayedNodes) > 0 {
-		if len(newNode.RelayedNodes) != len(currentNode.RelayedNodes) {
-			relayupdate = true
-		} else {
-			for i, node := range newNode.RelayedNodes {
-				if node != currentNode.RelayedNodes[i] {
-					relayupdate = true
-				}
-			}
-		}
-	}
+	relayUpdate := logic.RelayUpdates(&currentNode, newNode)
 	host, err := logic.GetHost(newNode.HostID.String())
 	host, err := logic.GetHost(newNode.HostID.String())
 	if err != nil {
 	if err != nil {
 		logger.Log(0, r.Header.Get("user"),
 		logger.Log(0, r.Header.Get("user"),
@@ -676,7 +636,7 @@ func updateNode(w http.ResponseWriter, r *http.Request) {
 	}
 	}
 	ifaceDelta := logic.IfaceDelta(&currentNode, newNode)
 	ifaceDelta := logic.IfaceDelta(&currentNode, newNode)
 	aclUpdate := currentNode.DefaultACL != newNode.DefaultACL
 	aclUpdate := currentNode.DefaultACL != newNode.DefaultACL
-	if ifaceDelta && servercfg.Is_EE {
+	if ifaceDelta && servercfg.IsPro {
 		if err = logic.EnterpriseResetAllPeersFailovers(currentNode.ID, currentNode.Network); err != nil {
 		if err = logic.EnterpriseResetAllPeersFailovers(currentNode.ID, currentNode.Network); err != nil {
 			logger.Log(0, "failed to reset failover lists during node update for node", currentNode.ID.String(), currentNode.Network)
 			logger.Log(0, "failed to reset failover lists during node update for node", currentNode.ID.String(), currentNode.Network)
 		}
 		}
@@ -689,13 +649,8 @@ func updateNode(w http.ResponseWriter, r *http.Request) {
 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
 		return
 		return
 	}
 	}
-	if relayupdate {
-		updatenodes := logic.UpdateRelayed(currentNode.ID.String(), currentNode.RelayedNodes, newNode.RelayedNodes)
-		if len(updatenodes) > 0 {
-			for _, relayedNode := range updatenodes {
-				runUpdates(&relayedNode, false)
-			}
-		}
+	if relayUpdate {
+		logic.UpdateRelayed(&currentNode, newNode)
 	}
 	}
 	if servercfg.IsDNSMode() {
 	if servercfg.IsDNSMode() {
 		logic.SetDNS()
 		logic.SetDNS()
@@ -705,7 +660,7 @@ func updateNode(w http.ResponseWriter, r *http.Request) {
 	logger.Log(1, r.Header.Get("user"), "updated node", currentNode.ID.String(), "on network", currentNode.Network)
 	logger.Log(1, r.Header.Get("user"), "updated node", currentNode.ID.String(), "on network", currentNode.Network)
 	w.WriteHeader(http.StatusOK)
 	w.WriteHeader(http.StatusOK)
 	json.NewEncoder(w).Encode(apiNode)
 	json.NewEncoder(w).Encode(apiNode)
-	runUpdates(newNode, ifaceDelta)
+	mq.RunUpdates(newNode, ifaceDelta)
 	go func(aclUpdate, relayupdate bool, newNode *models.Node) {
 	go func(aclUpdate, relayupdate bool, newNode *models.Node) {
 		if aclUpdate || relayupdate {
 		if aclUpdate || relayupdate {
 			if err := mq.PublishPeerUpdate(); err != nil {
 			if err := mq.PublishPeerUpdate(); err != nil {
@@ -715,7 +670,7 @@ func updateNode(w http.ResponseWriter, r *http.Request) {
 		if err := mq.PublishReplaceDNS(&currentNode, newNode, host); err != nil {
 		if err := mq.PublishReplaceDNS(&currentNode, newNode, host); err != nil {
 			logger.Log(1, "failed to publish dns update", err.Error())
 			logger.Log(1, "failed to publish dns update", err.Error())
 		}
 		}
-	}(aclUpdate, relayupdate, newNode)
+	}(aclUpdate, relayUpdate, newNode)
 }
 }
 
 
 // swagger:route DELETE /api/nodes/{network}/{nodeid} nodes deleteNode
 // swagger:route DELETE /api/nodes/{network}/{nodeid} nodes deleteNode
@@ -743,13 +698,6 @@ func deleteNode(w http.ResponseWriter, r *http.Request) {
 	}
 	}
 	forceDelete := r.URL.Query().Get("force") == "true"
 	forceDelete := r.URL.Query().Get("force") == "true"
 	fromNode := r.Header.Get("requestfrom") == "node"
 	fromNode := r.Header.Get("requestfrom") == "node"
-	if r.Header.Get("ismaster") != "yes" {
-		username := r.Header.Get("user")
-		if username != "" && !doesUserOwnNode(username, params["network"], nodeid) {
-			logic.ReturnErrorResponse(w, r, logic.FormatError(fmt.Errorf("user not permitted"), "badrequest"))
-			return
-		}
-	}
 	if node.IsRelayed {
 	if node.IsRelayed {
 		// cleanup node from relayednodes on relay node
 		// cleanup node from relayednodes on relay node
 		relayNode, err := logic.GetNodeByID(node.RelayedBy)
 		relayNode, err := logic.GetNodeByID(node.RelayedBy)
@@ -769,6 +717,16 @@ func deleteNode(w http.ResponseWriter, r *http.Request) {
 		// unset all the relayed nodes
 		// unset all the relayed nodes
 		logic.SetRelayedNodes(false, node.ID.String(), node.RelayedNodes)
 		logic.SetRelayedNodes(false, node.ID.String(), node.RelayedNodes)
 	}
 	}
+	if node.IsIngressGateway {
+		// delete ext clients belonging to ingress gatewa
+		go func(node models.Node) {
+			if err = logic.DeleteGatewayExtClients(node.ID.String(), node.Network); err != nil {
+				slog.Error("failed to delete extclients", "gatewayid", node.ID.String(), "network", node.Network, "error", err.Error())
+			}
+		}(node)
+
+	}
+
 	purge := forceDelete || fromNode
 	purge := forceDelete || fromNode
 	if err := logic.DeleteNode(&node, purge); err != nil {
 	if err := logic.DeleteNode(&node, purge); err != nil {
 		logic.ReturnErrorResponse(w, r, logic.FormatError(fmt.Errorf("failed to delete node"), "internal"))
 		logic.ReturnErrorResponse(w, r, logic.FormatError(fmt.Errorf("failed to delete node"), "internal"))
@@ -778,7 +736,7 @@ func deleteNode(w http.ResponseWriter, r *http.Request) {
 	logic.ReturnSuccessResponse(w, r, nodeid+" deleted.")
 	logic.ReturnSuccessResponse(w, r, nodeid+" deleted.")
 	logger.Log(1, r.Header.Get("user"), "Deleted node", nodeid, "from network", params["network"])
 	logger.Log(1, r.Header.Get("user"), "Deleted node", nodeid, "from network", params["network"])
 	if !fromNode { // notify node change
 	if !fromNode { // notify node change
-		runUpdates(&node, false)
+		mq.RunUpdates(&node, false)
 	}
 	}
 	go func() { // notify of peer change
 	go func() { // notify of peer change
 		var err error
 		var err error
@@ -796,36 +754,6 @@ func deleteNode(w http.ResponseWriter, r *http.Request) {
 	}()
 	}()
 }
 }
 
 
-func runUpdates(node *models.Node, ifaceDelta bool) {
-	go func() { // don't block http response
-		// publish node update if not server
-		if err := mq.NodeUpdate(node); err != nil {
-			logger.Log(1, "error publishing node update to node", node.ID.String(), err.Error())
-		}
-	}()
-}
-
-func doesUserOwnNode(username, network, nodeID string) bool {
-	u, err := logic.GetUser(username)
-	if err != nil {
-		return false
-	}
-	if u.IsAdmin {
-		return true
-	}
-
-	netUser, err := pro.GetNetworkUser(network, promodels.NetworkUserID(u.UserName))
-	if err != nil {
-		return false
-	}
-
-	if netUser.AccessLevel == pro.NET_ADMIN {
-		return true
-	}
-
-	return logic.StringSliceContains(netUser.Nodes, nodeID)
-}
-
 func validateParams(nodeid, netid string) (models.Node, error) {
 func validateParams(nodeid, netid string) (models.Node, error) {
 	node, err := logic.GetNodeByID(nodeid)
 	node, err := logic.GetNodeByID(nodeid)
 	if err != nil {
 	if err != nil {

+ 3 - 4
controllers/server.go

@@ -25,7 +25,6 @@ func serverHandlers(r *mux.Router) {
 	r.HandleFunc("/api/server/usage", Authorize(true, false, "user", http.HandlerFunc(getUsage))).Methods(http.MethodGet)
 	r.HandleFunc("/api/server/usage", Authorize(true, false, "user", http.HandlerFunc(getUsage))).Methods(http.MethodGet)
 }
 }
 
 
-// TODO move to EE package? there is a function and a type there for that already
 func getUsage(w http.ResponseWriter, r *http.Request) {
 func getUsage(w http.ResponseWriter, r *http.Request) {
 	type usage struct {
 	type usage struct {
 		Hosts     int `json:"hosts"`
 		Hosts     int `json:"hosts"`
@@ -164,9 +163,9 @@ func getConfig(w http.ResponseWriter, r *http.Request) {
 	// get params
 	// get params
 
 
 	scfg := servercfg.GetServerConfig()
 	scfg := servercfg.GetServerConfig()
-	scfg.IsEE = "no"
-	if servercfg.Is_EE {
-		scfg.IsEE = "yes"
+	scfg.IsPro = "no"
+	if servercfg.IsPro {
+		scfg.IsPro = "yes"
 	}
 	}
 	json.NewEncoder(w).Encode(scfg)
 	json.NewEncoder(w).Encode(scfg)
 	//w.WriteHeader(http.StatusOK)
 	//w.WriteHeader(http.StatusOK)

+ 163 - 150
controllers/user.go

@@ -13,23 +13,19 @@ import (
 	"github.com/gravitl/netmaker/logic"
 	"github.com/gravitl/netmaker/logic"
 	"github.com/gravitl/netmaker/models"
 	"github.com/gravitl/netmaker/models"
 	"github.com/gravitl/netmaker/servercfg"
 	"github.com/gravitl/netmaker/servercfg"
+	"golang.org/x/exp/slog"
 )
 )
 
 
 var (
 var (
 	upgrader = websocket.Upgrader{}
 	upgrader = websocket.Upgrader{}
 )
 )
 
 
-// verifyJWT makes logic.VerifyJWT fakeable/mockable in tests
-var verifyJWT = logic.VerifyJWT
-
 func userHandlers(r *mux.Router) {
 func userHandlers(r *mux.Router) {
-
-	r.HandleFunc("/api/users/adm/hasadmin", hasAdmin).Methods(http.MethodGet)
-	r.HandleFunc("/api/users/adm/createadmin", createAdmin).Methods(http.MethodPost)
+	r.HandleFunc("/api/users/adm/hassuperadmin", hasSuperAdmin).Methods(http.MethodGet)
+	r.HandleFunc("/api/users/adm/createsuperadmin", createSuperAdmin).Methods(http.MethodPost)
+	r.HandleFunc("/api/users/adm/transfersuperadmin/{username}", logic.SecurityCheck(true, http.HandlerFunc(transferSuperAdmin))).Methods(http.MethodPost)
 	r.HandleFunc("/api/users/adm/authenticate", authenticateUser).Methods(http.MethodPost)
 	r.HandleFunc("/api/users/adm/authenticate", authenticateUser).Methods(http.MethodPost)
-	r.HandleFunc("/api/users/{username}", logic.SecurityCheck(false, logic.ContinueIfUserMatch(http.HandlerFunc(updateUser)))).Methods(http.MethodPut)
-	r.HandleFunc("/api/users/networks/{username}", logic.SecurityCheck(true, http.HandlerFunc(updateUserNetworks))).Methods(http.MethodPut)
-	r.HandleFunc("/api/users/{username}/adm", logic.SecurityCheck(true, http.HandlerFunc(updateUserAdm))).Methods(http.MethodPut)
+	r.HandleFunc("/api/users/{username}", logic.SecurityCheck(true, http.HandlerFunc(updateUser))).Methods(http.MethodPut)
 	r.HandleFunc("/api/users/{username}", logic.SecurityCheck(true, checkFreeTierLimits(limitChoiceUsers, http.HandlerFunc(createUser)))).Methods(http.MethodPost)
 	r.HandleFunc("/api/users/{username}", logic.SecurityCheck(true, checkFreeTierLimits(limitChoiceUsers, http.HandlerFunc(createUser)))).Methods(http.MethodPost)
 	r.HandleFunc("/api/users/{username}", logic.SecurityCheck(true, http.HandlerFunc(deleteUser))).Methods(http.MethodDelete)
 	r.HandleFunc("/api/users/{username}", logic.SecurityCheck(true, http.HandlerFunc(deleteUser))).Methods(http.MethodDelete)
 	r.HandleFunc("/api/users/{username}", logic.SecurityCheck(false, logic.ContinueIfUserMatch(http.HandlerFunc(getUser)))).Methods(http.MethodGet)
 	r.HandleFunc("/api/users/{username}", logic.SecurityCheck(false, logic.ContinueIfUserMatch(http.HandlerFunc(getUser)))).Methods(http.MethodGet)
@@ -112,7 +108,7 @@ func authenticateUser(response http.ResponseWriter, request *http.Request) {
 	response.Write(successJSONResponse)
 	response.Write(successJSONResponse)
 }
 }
 
 
-// swagger:route GET /api/users/adm/hasadmin user hasAdmin
+// swagger:route GET /api/users/adm/hassuperadmin user hasSuperAdmin
 //
 //
 // Checks whether the server has an admin.
 // Checks whether the server has an admin.
 //
 //
@@ -123,18 +119,18 @@ func authenticateUser(response http.ResponseWriter, request *http.Request) {
 //
 //
 //			Responses:
 //			Responses:
 //				200: successResponse
 //				200: successResponse
-func hasAdmin(w http.ResponseWriter, r *http.Request) {
+func hasSuperAdmin(w http.ResponseWriter, r *http.Request) {
 
 
 	w.Header().Set("Content-Type", "application/json")
 	w.Header().Set("Content-Type", "application/json")
 
 
-	hasadmin, err := logic.HasAdmin()
+	hasSuperAdmin, err := logic.HasSuperAdmin()
 	if err != nil {
 	if err != nil {
 		logger.Log(0, "failed to check for admin: ", err.Error())
 		logger.Log(0, "failed to check for admin: ", err.Error())
 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
 		return
 		return
 	}
 	}
 
 
-	json.NewEncoder(w).Encode(hasadmin)
+	json.NewEncoder(w).Encode(hasSuperAdmin)
 
 
 }
 }
 
 
@@ -194,7 +190,7 @@ func getUsers(w http.ResponseWriter, r *http.Request) {
 	json.NewEncoder(w).Encode(users)
 	json.NewEncoder(w).Encode(users)
 }
 }
 
 
-// swagger:route POST /api/users/adm/createadmin user createAdmin
+// swagger:route POST /api/users/adm/createsuperadmin user createAdmin
 //
 //
 // Make a user an admin.
 // Make a user an admin.
 //
 //
@@ -205,16 +201,14 @@ func getUsers(w http.ResponseWriter, r *http.Request) {
 //
 //
 //			Responses:
 //			Responses:
 //				200: userBodyResponse
 //				200: userBodyResponse
-func createAdmin(w http.ResponseWriter, r *http.Request) {
+func createSuperAdmin(w http.ResponseWriter, r *http.Request) {
 	w.Header().Set("Content-Type", "application/json")
 	w.Header().Set("Content-Type", "application/json")
 
 
-	var admin models.User
+	var u models.User
 
 
-	err := json.NewDecoder(r.Body).Decode(&admin)
+	err := json.NewDecoder(r.Body).Decode(&u)
 	if err != nil {
 	if err != nil {
-
-		logger.Log(0, admin.UserName, "error decoding request body: ",
-			err.Error())
+		slog.Error("error decoding request body", "error", err.Error())
 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
 		return
 		return
 	}
 	}
@@ -224,21 +218,19 @@ func createAdmin(w http.ResponseWriter, r *http.Request) {
 		return
 		return
 	}
 	}
 
 
-	err = logic.CreateAdmin(&admin)
+	err = logic.CreateSuperAdmin(&u)
 	if err != nil {
 	if err != nil {
-		logger.Log(0, admin.UserName, "failed to create admin: ",
-			err.Error())
+		slog.Error("failed to create admin", "error", err.Error())
 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
 		return
 		return
 	}
 	}
-
-	logger.Log(1, admin.UserName, "was made a new admin")
-	json.NewEncoder(w).Encode(logic.ToReturnUser(admin))
+	logger.Log(1, u.UserName, "was made a super admin")
+	json.NewEncoder(w).Encode(logic.ToReturnUser(u))
 }
 }
 
 
-// swagger:route POST /api/users/{username} user createUser
+// swagger:route POST /api/users/adm/transfersuperadmin user transferSuperAdmin
 //
 //
-// Create a user.
+// Transfers superadmin role to an admin user.
 //
 //
 //			Schemes: https
 //			Schemes: https
 //
 //
@@ -247,32 +239,56 @@ func createAdmin(w http.ResponseWriter, r *http.Request) {
 //
 //
 //			Responses:
 //			Responses:
 //				200: userBodyResponse
 //				200: userBodyResponse
-func createUser(w http.ResponseWriter, r *http.Request) {
+func transferSuperAdmin(w http.ResponseWriter, r *http.Request) {
 	w.Header().Set("Content-Type", "application/json")
 	w.Header().Set("Content-Type", "application/json")
-
-	var user models.User
-	err := json.NewDecoder(r.Body).Decode(&user)
+	caller, err := logic.GetUser(r.Header.Get("user"))
 	if err != nil {
 	if err != nil {
-		logger.Log(0, user.UserName, "error decoding request body: ",
-			err.Error())
+		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
+	}
+	if !caller.IsSuperAdmin {
+		logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("only superadmin can assign the superadmin role to another user"), "forbidden"))
+		return
+	}
+	var params = mux.Vars(r)
+	username := params["username"]
+	u, err := logic.GetUser(username)
+	if err != nil {
+		slog.Error("error getting user", "user", u.UserName, "error", err.Error())
 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
 		return
 		return
 	}
 	}
+	if !u.IsAdmin {
+		logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("only admins can be promoted to superadmin role"), "forbidden"))
+		return
+	}
+	if !servercfg.IsBasicAuthEnabled() {
+		logic.ReturnErrorResponse(w, r, logic.FormatError(fmt.Errorf("basic auth is disabled"), "badrequest"))
+		return
+	}
 
 
-	err = logic.CreateUser(&user)
+	u.IsSuperAdmin = true
+	u.IsAdmin = false
+	err = logic.UpsertUser(*u)
 	if err != nil {
 	if err != nil {
-		logger.Log(0, user.UserName, "error creating new user: ",
-			err.Error())
-		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
+		slog.Error("error updating user to superadmin: ", "user", u.UserName, "error", err.Error())
+		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
 		return
 		return
 	}
 	}
-	logger.Log(1, user.UserName, "was created")
-	json.NewEncoder(w).Encode(logic.ToReturnUser(user))
+	caller.IsSuperAdmin = false
+	caller.IsAdmin = true
+	err = logic.UpsertUser(*caller)
+	if err != nil {
+		slog.Error("error demoting user to admin: ", "user", caller.UserName, "error", err.Error())
+		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
+		return
+	}
+	slog.Info("user was made a super admin", "user", u.UserName)
+	json.NewEncoder(w).Encode(logic.ToReturnUser(*u))
 }
 }
 
 
-// swagger:route PUT /api/users/networks/{username} user updateUserNetworks
+// swagger:route POST /api/users/{username} user createUser
 //
 //
-// Updates the networks of the given user.
+// Create a user.
 //
 //
 //			Schemes: https
 //			Schemes: https
 //
 //
@@ -281,49 +297,41 @@ func createUser(w http.ResponseWriter, r *http.Request) {
 //
 //
 //			Responses:
 //			Responses:
 //				200: userBodyResponse
 //				200: userBodyResponse
-func updateUserNetworks(w http.ResponseWriter, r *http.Request) {
+func createUser(w http.ResponseWriter, r *http.Request) {
 	w.Header().Set("Content-Type", "application/json")
 	w.Header().Set("Content-Type", "application/json")
-	var params = mux.Vars(r)
-	// start here
-	username := params["username"]
-	user, err := logic.GetUser(username)
+	caller, err := logic.GetUser(r.Header.Get("user"))
 	if err != nil {
 	if err != nil {
-		logger.Log(0, username,
-			"failed to update user networks: ", err.Error())
 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
-		return
 	}
 	}
-	userChange := &models.User{}
-	// we decode our body request params
-	err = json.NewDecoder(r.Body).Decode(userChange)
+	var user models.User
+	err = json.NewDecoder(r.Body).Decode(&user)
 	if err != nil {
 	if err != nil {
-		logger.Log(0, username, "error decoding request body: ",
+		logger.Log(0, user.UserName, "error decoding request body: ",
 			err.Error())
 			err.Error())
 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
 		return
 		return
 	}
 	}
-	err = logic.UpdateUserNetworks(userChange.Networks, userChange.Groups, userChange.IsAdmin, &models.ReturnUser{
-		Groups:   user.Groups,
-		IsAdmin:  user.IsAdmin,
-		Networks: user.Networks,
-		UserName: user.UserName,
-	})
-
-	if err != nil {
-		logger.Log(0, username,
-			"failed to update user networks: ", err.Error())
-		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
+	if !caller.IsSuperAdmin && user.IsAdmin {
+		err = errors.New("only superadmin can create admin users")
+		slog.Error("error creating new user: ", "user", user.UserName, "error", err)
+		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "forbidden"))
 		return
 		return
 	}
 	}
-	logger.Log(1, username, "status was updated")
-	// re-read and return the new user struct
-	returnUser, err := logic.GetReturnUser(username)
+	if user.IsSuperAdmin {
+		err = errors.New("additional superadmins cannot be created")
+		slog.Error("error creating new user: ", "user", user.UserName, "error", err)
+		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "forbidden"))
+		return
+	}
+
+	err = logic.CreateUser(&user)
 	if err != nil {
 	if err != nil {
-		logger.Log(0, username, "failed to fetch user: ", err.Error())
-		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
+		slog.Error("error creating new user: ", "user", user.UserName, "error", err.Error())
+		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
 		return
 		return
 	}
 	}
-	json.NewEncoder(w).Encode(returnUser)
+	slog.Info("user was created", "username", user.UserName)
+	json.NewEncoder(w).Encode(logic.ToReturnUser(user))
 }
 }
 
 
 // swagger:route PUT /api/users/{username} user updateUser
 // swagger:route PUT /api/users/{username} user updateUser
@@ -341,18 +349,19 @@ func updateUser(w http.ResponseWriter, r *http.Request) {
 	w.Header().Set("Content-Type", "application/json")
 	w.Header().Set("Content-Type", "application/json")
 	var params = mux.Vars(r)
 	var params = mux.Vars(r)
 	// start here
 	// start here
-	jwtUser, _, isadmin, err := verifyJWT(r.Header.Get("Authorization"))
-	if err != nil {
-		logger.Log(0, "verifyJWT error", err.Error())
-		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
-		return
+	var caller *models.User
+	var err error
+	var ismaster bool
+	if r.Header.Get("user") == logic.MasterUser {
+		ismaster = true
+	} else {
+		caller, err = logic.GetUser(r.Header.Get("user"))
+		if err != nil {
+			logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
+		}
 	}
 	}
+
 	username := params["username"]
 	username := params["username"]
-	if username != jwtUser && !isadmin {
-		logger.Log(0, "non-admin user", jwtUser, "attempted to update user", username)
-		logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("not authorizied"), "unauthorized"))
-		return
-	}
 	user, err := logic.GetUser(username)
 	user, err := logic.GetUser(username)
 	if err != nil {
 	if err != nil {
 		logger.Log(0, username,
 		logger.Log(0, username,
@@ -360,86 +369,78 @@ func updateUser(w http.ResponseWriter, r *http.Request) {
 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
 		return
 		return
 	}
 	}
-	if auth.IsOauthUser(user) == nil {
-		err := fmt.Errorf("cannot update user info for oauth user %s", username)
-		logger.Log(0, err.Error())
-		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "forbidden"))
-		return
-	}
 	var userchange models.User
 	var userchange models.User
 	// we decode our body request params
 	// we decode our body request params
 	err = json.NewDecoder(r.Body).Decode(&userchange)
 	err = json.NewDecoder(r.Body).Decode(&userchange)
 	if err != nil {
 	if err != nil {
-		logger.Log(0, username, "error decoding request body: ",
-			err.Error())
+		slog.Error("failed to decode body", "error ", err.Error())
 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
 		return
 		return
 	}
 	}
-	if userchange.IsAdmin && !isadmin {
-		logger.Log(0, "non-admin user", jwtUser, "attempted get admin privilages")
-		logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("not authorizied"), "unauthorized"))
+	if user.UserName != userchange.UserName {
+		logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("user in param and request body not matching"), "badrequest"))
 		return
 		return
 	}
 	}
-	userchange.Networks = nil
-	user, err = logic.UpdateUser(&userchange, user)
-	if err != nil {
-		logger.Log(0, username,
-			"failed to update user info: ", err.Error())
-		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
-		return
+	selfUpdate := false
+	if !ismaster && caller.UserName == user.UserName {
+		selfUpdate = true
 	}
 	}
-	logger.Log(1, username, "was updated")
-	json.NewEncoder(w).Encode(logic.ToReturnUser(*user))
-}
 
 
-// swagger:route PUT /api/users/{username}/adm user updateUserAdm
-//
-// Updates the given admin user's info (as long as the user is an admin).
-//
-//			Schemes: https
-//
-//			Security:
-//	  		oauth
-//
-//			Responses:
-//				200: userBodyResponse
-func updateUserAdm(w http.ResponseWriter, r *http.Request) {
-	w.Header().Set("Content-Type", "application/json")
-	var params = mux.Vars(r)
-	// start here
-	username := params["username"]
-	user, err := logic.GetUser(username)
-	if err != nil {
-		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
-		return
+	if !ismaster && !selfUpdate {
+		if caller.IsAdmin && user.IsSuperAdmin {
+			slog.Error("non-superadmin user", "caller", caller.UserName, "attempted to update superadmin user", username)
+			logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("cannot update superadmin user"), "forbidden"))
+			return
+		}
+		if !caller.IsAdmin && !caller.IsSuperAdmin {
+			slog.Error("operation not allowed", "caller", caller.UserName, "attempted to update user", username)
+			logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("cannot update superadmin user"), "forbidden"))
+			return
+		}
+		if caller.IsAdmin && user.IsAdmin {
+			slog.Error("admin user cannot update another admin", "caller", caller.UserName, "attempted to update admin user", username)
+			logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("admin user cannot update another admin"), "forbidden"))
+			return
+		}
+		if caller.IsAdmin && userchange.IsAdmin {
+			err = errors.New("admin user cannot update role of an another user to admin")
+			slog.Error("failed to update user", "caller", caller.UserName, "attempted to update user", username, "error", err)
+			logic.ReturnErrorResponse(w, r, logic.FormatError(err, "forbidden"))
+			return
+		}
+
+	}
+	if !ismaster && selfUpdate {
+		if user.IsAdmin != userchange.IsAdmin || user.IsSuperAdmin != userchange.IsSuperAdmin {
+			slog.Error("user cannot change his own role", "caller", caller.UserName, "attempted to update user role", username)
+			logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("user not allowed to self assign role"), "forbidden"))
+			return
+
+		}
 	}
 	}
+	if ismaster {
+		if !user.IsSuperAdmin && userchange.IsSuperAdmin {
+			slog.Error("operation not allowed", "caller", logic.MasterUser, "attempted to update user role to superadmin", username)
+			logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("attempted to update user role to superadmin"), "forbidden"))
+			return
+		}
+	}
+
 	if auth.IsOauthUser(user) == nil {
 	if auth.IsOauthUser(user) == nil {
 		err := fmt.Errorf("cannot update user info for oauth user %s", username)
 		err := fmt.Errorf("cannot update user info for oauth user %s", username)
 		logger.Log(0, err.Error())
 		logger.Log(0, err.Error())
 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "forbidden"))
 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "forbidden"))
 		return
 		return
 	}
 	}
-	var userchange models.User
-	// we decode our body request params
-	err = json.NewDecoder(r.Body).Decode(&userchange)
-	if err != nil {
-		logger.Log(0, username, "error decoding request body: ",
-			err.Error())
-		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
-		return
-	}
-	if !user.IsAdmin {
-		logger.Log(0, username, "not an admin user")
-		logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("not a admin user"), "badrequest"))
-	}
+
 	user, err = logic.UpdateUser(&userchange, user)
 	user, err = logic.UpdateUser(&userchange, user)
 	if err != nil {
 	if err != nil {
 		logger.Log(0, username,
 		logger.Log(0, username,
-			"failed to update user (admin) info: ", err.Error())
+			"failed to update user info: ", err.Error())
 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
 		return
 		return
 	}
 	}
-	logger.Log(1, username, "was updated (admin)")
+	logger.Log(1, username, "was updated")
 	json.NewEncoder(w).Encode(logic.ToReturnUser(*user))
 	json.NewEncoder(w).Encode(logic.ToReturnUser(*user))
 }
 }
 
 
@@ -460,9 +461,32 @@ func deleteUser(w http.ResponseWriter, r *http.Request) {
 
 
 	// get params
 	// get params
 	var params = mux.Vars(r)
 	var params = mux.Vars(r)
-
+	caller, err := logic.GetUser(r.Header.Get("user"))
+	if err != nil {
+		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
+	}
 	username := params["username"]
 	username := params["username"]
-
+	user, err := logic.GetUser(username)
+	if err != nil {
+		logger.Log(0, username,
+			"failed to update user info: ", err.Error())
+		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
+		return
+	}
+	if user.IsSuperAdmin {
+		slog.Error(
+			"failed to delete user: ", "user", username, "error", "superadmin cannot be deleted")
+		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
+		return
+	}
+	if !caller.IsSuperAdmin {
+		if caller.IsAdmin && user.IsAdmin {
+			slog.Error(
+				"failed to delete user: ", "user", username, "error", "admin cannot delete another admin user")
+			logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
+			return
+		}
+	}
 	success, err := logic.DeleteUser(username)
 	success, err := logic.DeleteUser(username)
 	if err != nil {
 	if err != nil {
 		logger.Log(0, username,
 		logger.Log(0, username,
@@ -495,14 +519,3 @@ func socketHandler(w http.ResponseWriter, r *http.Request) {
 	// Start handling the session
 	// Start handling the session
 	go auth.SessionHandler(conn)
 	go auth.SessionHandler(conn)
 }
 }
-
-// getHeaderNetworks returns a slice of networks parsed form the request header.
-func getHeaderNetworks(r *http.Request) ([]string, error) {
-	headerNetworks := r.Header.Get("networks")
-	networksSlice := []string{}
-	err := json.Unmarshal([]byte(headerNetworks), &networksSlice)
-	if err != nil {
-		return nil, err
-	}
-	return networksSlice, nil
-}

+ 29 - 89
controllers/user_test.go

@@ -2,13 +2,14 @@ package controller
 
 
 import (
 import (
 	"bytes"
 	"bytes"
-	"github.com/go-jose/go-jose/v3/json"
-	"github.com/gorilla/mux"
 	"io"
 	"io"
 	"net/http"
 	"net/http"
 	"net/http/httptest"
 	"net/http/httptest"
 	"testing"
 	"testing"
 
 
+	"github.com/go-jose/go-jose/v3/json"
+	"github.com/gorilla/mux"
+
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/assert"
 
 
 	"github.com/gravitl/netmaker/logic"
 	"github.com/gravitl/netmaker/logic"
@@ -47,72 +48,10 @@ func TestCreateAdminNoHashedPassword(t *testing.T) {
 	rec, req := prepareUserRequest(t, user, "")
 	rec, req := prepareUserRequest(t, user, "")
 
 
 	// test response
 	// test response
-	createAdmin(rec, req)
+	createSuperAdmin(rec, req)
 	assertUserNameButNoPassword(t, rec.Body, user.UserName)
 	assertUserNameButNoPassword(t, rec.Body, user.UserName)
 }
 }
 
 
-func TestCreateUserNoHashedPassword(t *testing.T) {
-	// prepare existing user base
-	deleteAllUsers(t)
-
-	// prepare request
-	user := models.User{UserName: "jonathan", Password: "password"}
-	rec, req := prepareUserRequest(t, user, "")
-
-	// test response
-	createUser(rec, req)
-	assertUserNameButNoPassword(t, rec.Body, user.UserName)
-}
-
-func TestUpdateUserNetworksNoHashedPassword(t *testing.T) {
-	// prepare existing user base
-	user1 := models.User{UserName: "joestar", Password: "jonathan"}
-	haveOnlyOneUser(t, user1)
-
-	// prepare request
-	user2 := models.User{UserName: "joestar", Password: "joseph"}
-	rec, req := prepareUserRequest(t, user2, user1.UserName)
-
-	// test response
-	updateUserNetworks(rec, req)
-	assertUserNameButNoPassword(t, rec.Body, user1.UserName)
-}
-
-func TestUpdateUserNoHashedPassword(t *testing.T) {
-	// prepare existing user base
-	user1 := models.User{UserName: "dio", Password: "brando"}
-	haveOnlyOneUser(t, user1)
-
-	// prepare request
-	user2 := models.User{UserName: "giorno", Password: "giovanna"}
-	rec, req := prepareUserRequest(t, user2, user1.UserName)
-
-	// mock the jwt verification
-	oldVerify := verifyJWT
-	verifyJWT = func(bearerToken string) (username string, networks []string, isadmin bool, err error) {
-		return user1.UserName, user1.Networks, user1.IsAdmin, nil
-	}
-	defer func() { verifyJWT = oldVerify }()
-
-	// test response
-	updateUser(rec, req)
-	assertUserNameButNoPassword(t, rec.Body, user2.UserName)
-}
-
-func TestUpdateUserAdmNoHashedPassword(t *testing.T) {
-	// prepare existing user base
-	user1 := models.User{UserName: "dio", Password: "brando", IsAdmin: true}
-	haveOnlyOneUser(t, user1)
-
-	// prepare request
-	user2 := models.User{UserName: "giorno", Password: "giovanna"}
-	rec, req := prepareUserRequest(t, user2, user1.UserName)
-
-	// test response
-	updateUserAdm(rec, req)
-	assertUserNameButNoPassword(t, rec.Body, user2.UserName)
-}
-
 func prepareUserRequest(t *testing.T, userForBody models.User, userNameForParam string) (*httptest.ResponseRecorder, *http.Request) {
 func prepareUserRequest(t *testing.T, userForBody models.User, userNameForParam string) (*httptest.ResponseRecorder, *http.Request) {
 	bits, err := json.Marshal(userForBody)
 	bits, err := json.Marshal(userForBody)
 	assert.Nil(t, err)
 	assert.Nil(t, err)
@@ -120,14 +59,15 @@ func prepareUserRequest(t *testing.T, userForBody models.User, userNameForParam
 	rec := httptest.NewRecorder()
 	rec := httptest.NewRecorder()
 	req := httptest.NewRequest("ANY", "https://example.com", body) // only the body matters here
 	req := httptest.NewRequest("ANY", "https://example.com", body) // only the body matters here
 	req = mux.SetURLVars(req, map[string]string{"username": userNameForParam})
 	req = mux.SetURLVars(req, map[string]string{"username": userNameForParam})
+	req.Header.Set("user", userForBody.UserName)
 	return rec, req
 	return rec, req
 }
 }
 
 
 func haveOnlyOneUser(t *testing.T, user models.User) {
 func haveOnlyOneUser(t *testing.T, user models.User) {
 	deleteAllUsers(t)
 	deleteAllUsers(t)
 	var err error
 	var err error
-	if user.IsAdmin {
-		err = logic.CreateAdmin(&user)
+	if user.IsSuperAdmin {
+		err = logic.CreateSuperAdmin(&user)
 	} else {
 	} else {
 		err = logic.CreateUser(&user)
 		err = logic.CreateUser(&user)
 	}
 	}
@@ -142,7 +82,7 @@ func assertUserNameButNoPassword(t *testing.T, r io.Reader, userName string) {
 	assert.Empty(t, resp.Password)
 	assert.Empty(t, resp.Password)
 }
 }
 
 
-func TestHasAdmin(t *testing.T) {
+func TestHasSuperAdmin(t *testing.T) {
 	// delete all current users
 	// delete all current users
 	users, _ := logic.GetUsers()
 	users, _ := logic.GetUsers()
 	for _, user := range users {
 	for _, user := range users {
@@ -151,31 +91,31 @@ func TestHasAdmin(t *testing.T) {
 		assert.True(t, success)
 		assert.True(t, success)
 	}
 	}
 	t.Run("NoUser", func(t *testing.T) {
 	t.Run("NoUser", func(t *testing.T) {
-		found, err := logic.HasAdmin()
+		found, err := logic.HasSuperAdmin()
 		assert.Nil(t, err)
 		assert.Nil(t, err)
 		assert.False(t, found)
 		assert.False(t, found)
 	})
 	})
-	t.Run("No admin user", func(t *testing.T) {
-		var user = models.User{UserName: "noadmin", Password: "password"}
+	t.Run("No superadmin user", func(t *testing.T) {
+		var user = models.User{UserName: "nosuperadmin", Password: "password"}
 		err := logic.CreateUser(&user)
 		err := logic.CreateUser(&user)
 		assert.Nil(t, err)
 		assert.Nil(t, err)
-		found, err := logic.HasAdmin()
+		found, err := logic.HasSuperAdmin()
 		assert.Nil(t, err)
 		assert.Nil(t, err)
 		assert.False(t, found)
 		assert.False(t, found)
 	})
 	})
-	t.Run("admin user", func(t *testing.T) {
-		var user = models.User{UserName: "admin", Password: "password", IsAdmin: true}
+	t.Run("superadmin user", func(t *testing.T) {
+		var user = models.User{UserName: "superadmin", Password: "password", IsSuperAdmin: true}
 		err := logic.CreateUser(&user)
 		err := logic.CreateUser(&user)
 		assert.Nil(t, err)
 		assert.Nil(t, err)
-		found, err := logic.HasAdmin()
+		found, err := logic.HasSuperAdmin()
 		assert.Nil(t, err)
 		assert.Nil(t, err)
 		assert.True(t, found)
 		assert.True(t, found)
 	})
 	})
-	t.Run("multiple admins", func(t *testing.T) {
-		var user = models.User{UserName: "admin1", Password: "password", IsAdmin: true}
+	t.Run("multiple superadmins", func(t *testing.T) {
+		var user = models.User{UserName: "superadmin1", Password: "password", IsSuperAdmin: true}
 		err := logic.CreateUser(&user)
 		err := logic.CreateUser(&user)
 		assert.Nil(t, err)
 		assert.Nil(t, err)
-		found, err := logic.HasAdmin()
+		found, err := logic.HasSuperAdmin()
 		assert.Nil(t, err)
 		assert.Nil(t, err)
 		assert.True(t, found)
 		assert.True(t, found)
 	})
 	})
@@ -195,20 +135,20 @@ func TestCreateUser(t *testing.T) {
 	})
 	})
 }
 }
 
 
-func TestCreateAdmin(t *testing.T) {
+func TestCreateSuperAdmin(t *testing.T) {
 	deleteAllUsers(t)
 	deleteAllUsers(t)
 	var user models.User
 	var user models.User
-	t.Run("NoAdmin", func(t *testing.T) {
+	t.Run("NoSuperAdmin", func(t *testing.T) {
 		user.UserName = "admin"
 		user.UserName = "admin"
 		user.Password = "password"
 		user.Password = "password"
-		err := logic.CreateAdmin(&user)
+		err := logic.CreateSuperAdmin(&user)
 		assert.Nil(t, err)
 		assert.Nil(t, err)
 	})
 	})
-	t.Run("AdminExists", func(t *testing.T) {
+	t.Run("SuperAdminExists", func(t *testing.T) {
 		user.UserName = "admin2"
 		user.UserName = "admin2"
 		user.Password = "password1"
 		user.Password = "password1"
-		err := logic.CreateAdmin(&user)
-		assert.EqualError(t, err, "admin user already exists")
+		err := logic.CreateSuperAdmin(&user)
+		assert.EqualError(t, err, "superadmin user already exists")
 	})
 	})
 }
 }
 
 
@@ -280,7 +220,7 @@ func TestValidateUser(t *testing.T) {
 func TestGetUser(t *testing.T) {
 func TestGetUser(t *testing.T) {
 	deleteAllUsers(t)
 	deleteAllUsers(t)
 
 
-	user := models.User{UserName: "admin", Password: "password", Networks: nil, IsAdmin: true, Groups: nil}
+	user := models.User{UserName: "admin", Password: "password", IsAdmin: true}
 
 
 	t.Run("NonExistantUser", func(t *testing.T) {
 	t.Run("NonExistantUser", func(t *testing.T) {
 		admin, err := logic.GetUser("admin")
 		admin, err := logic.GetUser("admin")
@@ -338,7 +278,7 @@ func TestGetUsers(t *testing.T) {
 func TestUpdateUser(t *testing.T) {
 func TestUpdateUser(t *testing.T) {
 	deleteAllUsers(t)
 	deleteAllUsers(t)
 	user := models.User{UserName: "admin", Password: "password", IsAdmin: true}
 	user := models.User{UserName: "admin", Password: "password", IsAdmin: true}
-	newuser := models.User{UserName: "hello", Password: "world", Networks: []string{"wirecat, netmaker"}, IsAdmin: true, Groups: []string{}}
+	newuser := models.User{UserName: "hello", Password: "world", IsAdmin: true}
 	t.Run("NonExistantUser", func(t *testing.T) {
 	t.Run("NonExistantUser", func(t *testing.T) {
 		admin, err := logic.UpdateUser(&newuser, &user)
 		admin, err := logic.UpdateUser(&newuser, &user)
 		assert.EqualError(t, err, "could not find any records")
 		assert.EqualError(t, err, "could not find any records")
@@ -381,7 +321,7 @@ func TestUpdateUser(t *testing.T) {
 
 
 func TestVerifyAuthRequest(t *testing.T) {
 func TestVerifyAuthRequest(t *testing.T) {
 	deleteAllUsers(t)
 	deleteAllUsers(t)
-	user := models.User{UserName: "admin", Password: "password", Networks: nil, IsAdmin: true, Groups: nil}
+	user := models.User{UserName: "admin", Password: "password", IsSuperAdmin: false, IsAdmin: true}
 	var authRequest models.UserAuthParams
 	var authRequest models.UserAuthParams
 	t.Run("EmptyUserName", func(t *testing.T) {
 	t.Run("EmptyUserName", func(t *testing.T) {
 		authRequest.UserName = ""
 		authRequest.UserName = ""
@@ -402,7 +342,7 @@ func TestVerifyAuthRequest(t *testing.T) {
 		authRequest.Password = "password"
 		authRequest.Password = "password"
 		jwt, err := logic.VerifyAuthRequest(authRequest)
 		jwt, err := logic.VerifyAuthRequest(authRequest)
 		assert.Equal(t, "", jwt)
 		assert.Equal(t, "", jwt)
-		assert.EqualError(t, err, "error retrieving user from db: could not find any records")
+		assert.EqualError(t, err, "incorrect credentials")
 	})
 	})
 	t.Run("Non-Admin", func(t *testing.T) {
 	t.Run("Non-Admin", func(t *testing.T) {
 		user.IsAdmin = false
 		user.IsAdmin = false
@@ -417,7 +357,7 @@ func TestVerifyAuthRequest(t *testing.T) {
 		assert.Nil(t, err)
 		assert.Nil(t, err)
 	})
 	})
 	t.Run("WrongPassword", func(t *testing.T) {
 	t.Run("WrongPassword", func(t *testing.T) {
-		user := models.User{UserName: "admin", Password: "password", Groups: []string{}}
+		user := models.User{UserName: "admin", Password: "password"}
 		if err := logic.CreateUser(&user); err != nil {
 		if err := logic.CreateUser(&user); err != nil {
 			t.Error(err)
 			t.Error(err)
 		}
 		}

+ 0 - 0
docker/Caddyfile-EE → docker/Caddyfile-pro


+ 0 - 365
ee/ee_controllers/networkusers.go

@@ -1,365 +0,0 @@
-package ee_controllers
-
-import (
-	"encoding/json"
-	"errors"
-	"net/http"
-
-	"github.com/gorilla/mux"
-	"github.com/gravitl/netmaker/database"
-	"github.com/gravitl/netmaker/logger"
-	"github.com/gravitl/netmaker/logic"
-	"github.com/gravitl/netmaker/logic/pro"
-	"github.com/gravitl/netmaker/models"
-	"github.com/gravitl/netmaker/models/promodels"
-)
-
-func NetworkUsersHandlers(r *mux.Router) {
-	r.HandleFunc("/api/networkusers", logic.SecurityCheck(true, http.HandlerFunc(getAllNetworkUsers))).Methods(http.MethodGet)
-	r.HandleFunc("/api/networkusers/{network}", logic.SecurityCheck(true, http.HandlerFunc(getNetworkUsers))).Methods(http.MethodGet)
-	r.HandleFunc("/api/networkusers/{network}/{networkuser}", logic.SecurityCheck(true, http.HandlerFunc(getNetworkUser))).Methods(http.MethodGet)
-	r.HandleFunc("/api/networkusers/{network}", logic.SecurityCheck(true, http.HandlerFunc(createNetworkUser))).Methods(http.MethodPost)
-	r.HandleFunc("/api/networkusers/{network}", logic.SecurityCheck(true, http.HandlerFunc(updateNetworkUser))).Methods(http.MethodPut)
-	r.HandleFunc("/api/networkusers/data/{networkuser}/me", logic.NetUserSecurityCheck(false, false, http.HandlerFunc(getNetworkUserData))).Methods(http.MethodGet)
-	r.HandleFunc("/api/networkusers/{network}/{networkuser}", logic.SecurityCheck(true, http.HandlerFunc(deleteNetworkUser))).Methods(http.MethodDelete)
-}
-
-// == RETURN TYPES ==
-
-// NetworkName - represents a network name/ID
-type NetworkName string
-
-// NetworkUserDataMap - map of all data per network for a user
-type NetworkUserDataMap map[NetworkName]NetworkUserData
-
-// NetworkUserData - data struct for network users
-type NetworkUserData struct {
-	Nodes    []models.Node         `json:"nodes" bson:"nodes" yaml:"nodes"`
-	Clients  []models.ExtClient    `json:"clients" bson:"clients" yaml:"clients"`
-	Vpn      []models.Node         `json:"vpns" bson:"vpns" yaml:"vpns"`
-	Networks []models.Network      `json:"networks" bson:"networks" yaml:"networks"`
-	User     promodels.NetworkUser `json:"user" bson:"user" yaml:"user"`
-}
-
-// == END RETURN TYPES ==
-
-// returns a map of a network user's data across all networks
-func getNetworkUserData(w http.ResponseWriter, r *http.Request) {
-	w.Header().Set("Content-Type", "application/json")
-
-	var params = mux.Vars(r)
-	networkUserName := params["networkuser"]
-	logger.Log(1, r.Header.Get("user"), "requested fetching network user data for user", networkUserName)
-
-	networks, err := logic.GetNetworks()
-	if err != nil {
-		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
-		return
-	}
-
-	if networkUserName == "" {
-		logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("netuserToGet"), "badrequest"))
-		return
-	}
-
-	u, err := logic.GetUser(networkUserName)
-	if err != nil {
-		logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("could not find user"), "badrequest"))
-		return
-	}
-
-	// initialize the return data of network users
-	returnData := make(NetworkUserDataMap)
-
-	// go through each network and get that user's data
-	// if user has no access, give no data
-	// if user is a net admin, give all nodes
-	// if user has node access, give user's nodes if any
-	// if user has client access, git user's clients if any
-	for i := range networks {
-
-		netID := networks[i].NetID
-		newData := NetworkUserData{
-			Nodes:    []models.Node{},
-			Clients:  []models.ExtClient{},
-			Vpn:      []models.Node{},
-			Networks: []models.Network{},
-		}
-		netUser, err := pro.GetNetworkUser(netID, promodels.NetworkUserID(networkUserName))
-		// check if user has access
-		if err == nil && netUser.AccessLevel != pro.NO_ACCESS {
-			newData.User = promodels.NetworkUser{
-				AccessLevel: netUser.AccessLevel,
-				ClientLimit: netUser.ClientLimit,
-				NodeLimit:   netUser.NodeLimit,
-				Nodes:       netUser.Nodes,
-				Clients:     netUser.Clients,
-			}
-			newData.User.SetDefaults()
-			// check network level permissions
-			if doesNetworkAllow := pro.IsUserAllowed(&networks[i], networkUserName, u.Groups); doesNetworkAllow || netUser.AccessLevel == pro.NET_ADMIN {
-				netNodes, err := logic.GetNetworkNodes(netID)
-				if err != nil {
-					if database.IsEmptyRecord(err) && netUser.AccessLevel == pro.NET_ADMIN {
-						newData.Networks = append(newData.Networks, networks[i])
-					} else {
-						logger.Log(0, "failed to retrieve nodes on network", netID, "for user", string(netUser.ID))
-					}
-				} else {
-					if netUser.AccessLevel <= pro.NODE_ACCESS { // handle nodes
-						// if access level is NODE_ACCESS, filter nodes
-						if netUser.AccessLevel == pro.NODE_ACCESS {
-							for i := range netNodes {
-								if logic.StringSliceContains(netUser.Nodes, netNodes[i].ID.String()) {
-									newData.Nodes = append(newData.Nodes, netNodes[i])
-								}
-							}
-						} else { // net admin so, get all nodes and ext clients on network...
-							newData.Nodes = netNodes
-							for i := range netNodes {
-								if netNodes[i].IsIngressGateway {
-									newData.Vpn = append(newData.Vpn, netNodes[i])
-									if clients, err := logic.GetExtClientsByID(netNodes[i].ID.String(), netID); err == nil {
-										newData.Clients = append(newData.Clients, clients...)
-									}
-								}
-							}
-							newData.Networks = append(newData.Networks, networks[i])
-						}
-					}
-					if netUser.AccessLevel <= pro.CLIENT_ACCESS && netUser.AccessLevel != pro.NET_ADMIN {
-						for _, c := range netUser.Clients {
-							if client, err := logic.GetExtClient(c, netID); err == nil {
-								newData.Clients = append(newData.Clients, client)
-							}
-						}
-						for i := range netNodes {
-							if netNodes[i].IsIngressGateway {
-								newData.Vpn = append(newData.Vpn, netNodes[i])
-							}
-						}
-					}
-				}
-			}
-			returnData[NetworkName(netID)] = newData
-		}
-	}
-
-	w.WriteHeader(http.StatusOK)
-	json.NewEncoder(w).Encode(returnData)
-}
-
-// returns a map of all network users mapped to each network
-func getAllNetworkUsers(w http.ResponseWriter, r *http.Request) {
-	w.Header().Set("Content-Type", "application/json")
-	logger.Log(1, r.Header.Get("user"), "requested fetching all network users")
-	type allNetworkUsers = map[string][]promodels.NetworkUser
-
-	networks, err := logic.GetNetworks()
-	if err != nil {
-		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
-		return
-	}
-
-	var allNetUsers = make(allNetworkUsers, len(networks))
-
-	for i := range networks {
-		netusers, err := pro.GetNetworkUsers(networks[i].NetID)
-		if err != nil {
-			logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
-			return
-		}
-		for _, v := range netusers {
-			allNetUsers[networks[i].NetID] = append(allNetUsers[networks[i].NetID], v)
-		}
-	}
-
-	w.WriteHeader(http.StatusOK)
-	json.NewEncoder(w).Encode(allNetUsers)
-}
-
-func getNetworkUsers(w http.ResponseWriter, r *http.Request) {
-	w.Header().Set("Content-Type", "application/json")
-
-	var params = mux.Vars(r)
-	netname := params["network"]
-	logger.Log(1, r.Header.Get("user"), "requested fetching network users for network", netname)
-
-	_, err := logic.GetNetwork(netname)
-	if err != nil {
-		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
-		return
-	}
-
-	netusers, err := pro.GetNetworkUsers(netname)
-	if err != nil {
-		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
-		return
-	}
-	w.WriteHeader(http.StatusOK)
-	json.NewEncoder(w).Encode(netusers)
-}
-
-func getNetworkUser(w http.ResponseWriter, r *http.Request) {
-	w.Header().Set("Content-Type", "application/json")
-
-	var params = mux.Vars(r)
-	netname := params["network"]
-	logger.Log(1, r.Header.Get("user"), "requested fetching network user", params["networkuser"], "on network", netname)
-
-	_, err := logic.GetNetwork(netname)
-	if err != nil {
-		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
-		return
-	}
-
-	netuserToGet := params["networkuser"]
-	if netuserToGet == "" {
-		logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("netuserToGet"), "badrequest"))
-		return
-	}
-
-	netuser, err := pro.GetNetworkUser(netname, promodels.NetworkUserID(netuserToGet))
-	if err != nil {
-		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
-		return
-	}
-	w.WriteHeader(http.StatusOK)
-	json.NewEncoder(w).Encode(netuser)
-}
-
-func createNetworkUser(w http.ResponseWriter, r *http.Request) {
-	w.Header().Set("Content-Type", "application/json")
-	var params = mux.Vars(r)
-	netname := params["network"]
-	logger.Log(1, r.Header.Get("user"), "requested creating a network user on network", netname)
-
-	network, err := logic.GetNetwork(netname)
-	if err != nil {
-		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
-		return
-	}
-	var networkuser promodels.NetworkUser
-
-	// we decode our body request params
-	err = json.NewDecoder(r.Body).Decode(&networkuser)
-	if err != nil {
-		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
-		return
-	}
-
-	err = pro.CreateNetworkUser(&network, &networkuser)
-	if err != nil {
-		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
-		return
-	}
-
-	w.WriteHeader(http.StatusOK)
-}
-
-func updateNetworkUser(w http.ResponseWriter, r *http.Request) {
-	w.Header().Set("Content-Type", "application/json")
-
-	var params = mux.Vars(r)
-	netname := params["network"]
-	logger.Log(1, r.Header.Get("user"), "requested updating a network user on network", netname)
-
-	network, err := logic.GetNetwork(netname)
-	if err != nil {
-		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
-		return
-	}
-	var networkuser promodels.NetworkUser
-
-	// we decode our body request params
-	err = json.NewDecoder(r.Body).Decode(&networkuser)
-	if err != nil {
-		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
-		return
-	}
-	if networkuser.ID == "" || !pro.DoesNetworkUserExist(netname, networkuser.ID) {
-		logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("invalid user "+string(networkuser.ID)), "badrequest"))
-		return
-	}
-	if networkuser.AccessLevel < pro.NET_ADMIN || networkuser.AccessLevel > pro.NO_ACCESS {
-		logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("invalid user access level provided"), "badrequest"))
-		return
-	}
-
-	if networkuser.ClientLimit < 0 || networkuser.NodeLimit < 0 {
-		logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("negative user limit provided"), "badrequest"))
-		return
-	}
-
-	u, err := logic.GetUser(string(networkuser.ID))
-	if err != nil {
-		logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("invalid user "+string(networkuser.ID)), "badrequest"))
-		return
-	}
-
-	if !pro.IsUserAllowed(&network, u.UserName, u.Groups) {
-		logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("user must be in allowed groups or users"), "badrequest"))
-		return
-	}
-
-	if networkuser.AccessLevel == pro.NET_ADMIN {
-		currentUser, err := logic.GetUser(string(networkuser.ID))
-		if err != nil {
-			logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("user model not found for "+string(networkuser.ID)), "badrequest"))
-			return
-		}
-
-		if !logic.StringSliceContains(currentUser.Networks, netname) {
-			// append network name to user model to conform to old model
-			if err = logic.UpdateUserNetworks(
-				append(currentUser.Networks, netname),
-				currentUser.Groups,
-				currentUser.IsAdmin,
-				&models.ReturnUser{
-					Groups:   currentUser.Groups,
-					IsAdmin:  currentUser.IsAdmin,
-					Networks: currentUser.Networks,
-					UserName: currentUser.UserName,
-				},
-			); err != nil {
-				logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("user model failed net admin update "+string(networkuser.ID)+" (are they an admin?"), "badrequest"))
-				return
-			}
-		}
-	}
-
-	err = pro.UpdateNetworkUser(netname, &networkuser)
-	if err != nil {
-		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
-		return
-	}
-
-	w.WriteHeader(http.StatusOK)
-}
-
-func deleteNetworkUser(w http.ResponseWriter, r *http.Request) {
-
-	var params = mux.Vars(r)
-	netname := params["network"]
-
-	logger.Log(1, r.Header.Get("user"), "requested deleting network user", params["networkuser"], "on network", netname)
-
-	_, err := logic.GetNetwork(netname)
-	if err != nil {
-		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
-		return
-	}
-
-	netuserToDelete := params["networkuser"]
-	if netuserToDelete == "" {
-		logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("no group name provided"), "badrequest"))
-		return
-	}
-
-	if err := pro.DeleteNetworkUser(netname, netuserToDelete); err != nil {
-		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
-		return
-	}
-
-	w.WriteHeader(http.StatusOK)
-}

+ 0 - 73
ee/ee_controllers/usergroups.go

@@ -1,73 +0,0 @@
-package ee_controllers
-
-import (
-	"encoding/json"
-	"errors"
-	"net/http"
-
-	"github.com/gravitl/netmaker/logger"
-	"github.com/gravitl/netmaker/logic"
-
-	"github.com/gorilla/mux"
-	"github.com/gravitl/netmaker/logic/pro"
-	"github.com/gravitl/netmaker/models/promodels"
-)
-
-func UserGroupsHandlers(r *mux.Router) {
-	r.HandleFunc("/api/usergroups", logic.SecurityCheck(true, http.HandlerFunc(getUserGroups))).Methods(http.MethodGet)
-	r.HandleFunc("/api/usergroups/{usergroup}", logic.SecurityCheck(true, http.HandlerFunc(createUserGroup))).Methods(http.MethodPost)
-	r.HandleFunc("/api/usergroups/{usergroup}", logic.SecurityCheck(true, http.HandlerFunc(deleteUserGroup))).Methods(http.MethodDelete)
-}
-
-func getUserGroups(w http.ResponseWriter, r *http.Request) {
-	w.Header().Set("Content-Type", "application/json")
-	logger.Log(1, r.Header.Get("user"), "requested fetching user groups")
-
-	userGroups, err := pro.GetUserGroups()
-	if err != nil {
-		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
-		return
-	}
-	// Returns all the groups in JSON format
-	w.WriteHeader(http.StatusOK)
-	json.NewEncoder(w).Encode(userGroups)
-}
-
-func createUserGroup(w http.ResponseWriter, r *http.Request) {
-	w.Header().Set("Content-Type", "application/json")
-	var params = mux.Vars(r)
-	newGroup := params["usergroup"]
-
-	logger.Log(1, r.Header.Get("user"), "requested creating user group", newGroup)
-
-	if newGroup == "" {
-		logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("no group name provided"), "badrequest"))
-		return
-	}
-
-	err := pro.InsertUserGroup(promodels.UserGroupName(newGroup))
-	if err != nil {
-		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
-		return
-	}
-
-	w.WriteHeader(http.StatusOK)
-}
-
-func deleteUserGroup(w http.ResponseWriter, r *http.Request) {
-	var params = mux.Vars(r)
-	groupToDelete := params["usergroup"]
-	logger.Log(1, r.Header.Get("user"), "requested deleting user group", groupToDelete)
-
-	if groupToDelete == "" {
-		logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("no group name provided"), "badrequest"))
-		return
-	}
-
-	if err := pro.DeleteUserGroup(promodels.UserGroupName(groupToDelete)); err != nil {
-		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
-		return
-	}
-
-	w.WriteHeader(http.StatusOK)
-}

+ 0 - 41
ee/logic/ext_acls.go

@@ -1,41 +0,0 @@
-package logic
-
-import "github.com/gravitl/netmaker/models"
-
-// DenyClientNode - add a denied node to an ext client's list
-func DenyClientNode(ec *models.ExtClient, clientOrNodeID string) (ok bool) {
-	if ec == nil || len(clientOrNodeID) == 0 {
-		return
-	}
-	if ec.DeniedACLs == nil {
-		ec.DeniedACLs = map[string]struct{}{}
-	}
-	ok = true
-	ec.DeniedACLs[clientOrNodeID] = struct{}{}
-	return
-}
-
-// IsClientNodeAllowed - checks if given ext client and node are allowed to communicate
-func IsClientNodeAllowed(ec *models.ExtClient, clientOrNodeID string) bool {
-	if ec == nil || len(clientOrNodeID) == 0 {
-		return false
-	}
-	if ec.DeniedACLs == nil {
-		return true
-	}
-	_, ok := ec.DeniedACLs[clientOrNodeID]
-	return !ok
-}
-
-// RemoveDeniedNodeFromClient - removes a node id from set of denied nodes
-func RemoveDeniedNodeFromClient(ec *models.ExtClient, clientOrNodeID string) bool {
-	if ec.DeniedACLs == nil {
-		return true
-	}
-	_, ok := ec.DeniedACLs[clientOrNodeID]
-	if !ok {
-		return false
-	}
-	delete(ec.DeniedACLs, clientOrNodeID)
-	return true
-}

+ 4 - 6
functions/helpers_test.go

@@ -25,12 +25,10 @@ var (
 func TestMain(m *testing.M) {
 func TestMain(m *testing.M) {
 	database.InitializeDatabase()
 	database.InitializeDatabase()
 	defer database.CloseDB()
 	defer database.CloseDB()
-	logic.CreateAdmin(&models.User{
-		UserName: "admin",
-		Password: "password",
-		IsAdmin:  true,
-		Networks: []string{},
-		Groups:   []string{},
+	logic.CreateSuperAdmin(&models.User{
+		UserName:     "superadmin",
+		Password:     "password",
+		IsSuperAdmin: true,
 	})
 	})
 	peerUpdate := make(chan *models.Node)
 	peerUpdate := make(chan *models.Node)
 	go logic.ManageZombies(context.Background(), peerUpdate)
 	go logic.ManageZombies(context.Background(), peerUpdate)

+ 5 - 5
go.mod

@@ -4,9 +4,9 @@ go 1.19
 
 
 require (
 require (
 	github.com/eclipse/paho.mqtt.golang v1.4.3
 	github.com/eclipse/paho.mqtt.golang v1.4.3
-	github.com/go-playground/validator/v10 v10.15.0
+	github.com/go-playground/validator/v10 v10.15.1
 	github.com/golang-jwt/jwt/v4 v4.5.0
 	github.com/golang-jwt/jwt/v4 v4.5.0
-	github.com/google/uuid v1.3.0
+	github.com/google/uuid v1.3.1
 	github.com/gorilla/handlers v1.5.1
 	github.com/gorilla/handlers v1.5.1
 	github.com/gorilla/mux v1.8.0
 	github.com/gorilla/mux v1.8.0
 	github.com/lib/pq v1.10.9
 	github.com/lib/pq v1.10.9
@@ -14,20 +14,20 @@ require (
 	github.com/rqlite/gorqlite v0.0.0-20210514125552-08ff1e76b22f
 	github.com/rqlite/gorqlite v0.0.0-20210514125552-08ff1e76b22f
 	github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e
 	github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e
 	github.com/stretchr/testify v1.8.4
 	github.com/stretchr/testify v1.8.4
-	github.com/txn2/txeh v1.4.0
+	github.com/txn2/txeh v1.5.3
 	golang.org/x/crypto v0.12.0
 	golang.org/x/crypto v0.12.0
 	golang.org/x/net v0.14.0 // indirect
 	golang.org/x/net v0.14.0 // indirect
 	golang.org/x/oauth2 v0.11.0
 	golang.org/x/oauth2 v0.11.0
 	golang.org/x/sys v0.11.0 // indirect
 	golang.org/x/sys v0.11.0 // indirect
 	golang.org/x/text v0.12.0 // indirect
 	golang.org/x/text v0.12.0 // indirect
-	golang.zx2c4.com/wireguard/wgctrl v0.0.0-20220324164955-056925b7df31
+	golang.zx2c4.com/wireguard/wgctrl v0.0.0-20221104135756-97bc4ad4a1cb
 	google.golang.org/protobuf v1.31.0 // indirect
 	google.golang.org/protobuf v1.31.0 // indirect
 	gopkg.in/yaml.v3 v3.0.1
 	gopkg.in/yaml.v3 v3.0.1
 )
 )
 
 
 require (
 require (
 	filippo.io/edwards25519 v1.0.0
 	filippo.io/edwards25519 v1.0.0
-	github.com/c-robinson/iplib v1.0.6
+	github.com/c-robinson/iplib v1.0.7
 	github.com/posthog/posthog-go v0.0.0-20211028072449-93c17c49e2b0
 	github.com/posthog/posthog-go v0.0.0-20211028072449-93c17c49e2b0
 )
 )
 
 

+ 10 - 43
go.sum

@@ -5,8 +5,8 @@ cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2Aawl
 filippo.io/edwards25519 v1.0.0 h1:0wAIcmJUqRdI8IJ/3eGi5/HwXZWPujYXXlkrQogz0Ek=
 filippo.io/edwards25519 v1.0.0 h1:0wAIcmJUqRdI8IJ/3eGi5/HwXZWPujYXXlkrQogz0Ek=
 filippo.io/edwards25519 v1.0.0/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns=
 filippo.io/edwards25519 v1.0.0/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns=
 github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
 github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
-github.com/c-robinson/iplib v1.0.6 h1:FfZV9BWNrah3BgLCFl5/nDXe4RbOi/C9n+DeXFOv5CQ=
-github.com/c-robinson/iplib v1.0.6/go.mod h1:i3LuuFL1hRT5gFpBRnEydzw8R6yhGkF4szNDIbF8pgo=
+github.com/c-robinson/iplib v1.0.7 h1:Dh9AINAlkc+NsNzZuFiVs+pi3AjN+0B7mu01KHdJKHU=
+github.com/c-robinson/iplib v1.0.7/go.mod h1:i3LuuFL1hRT5gFpBRnEydzw8R6yhGkF4szNDIbF8pgo=
 github.com/coreos/go-oidc/v3 v3.6.0 h1:AKVxfYw1Gmkn/w96z0DbT/B/xFnzTd3MkZvWLjF4n/o=
 github.com/coreos/go-oidc/v3 v3.6.0 h1:AKVxfYw1Gmkn/w96z0DbT/B/xFnzTd3MkZvWLjF4n/o=
 github.com/coreos/go-oidc/v3 v3.6.0/go.mod h1:ZpHUsHBucTUj6WOkrP4E20UPynbLZzhTQ1XKCXkxyPc=
 github.com/coreos/go-oidc/v3 v3.6.0/go.mod h1:ZpHUsHBucTUj6WOkrP4E20UPynbLZzhTQ1XKCXkxyPc=
 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.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
@@ -30,8 +30,8 @@ github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/o
 github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
 github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
 github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
 github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
 github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
 github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
-github.com/go-playground/validator/v10 v10.15.0 h1:nDU5XeOKtB3GEa+uB7GNYwhVKsgjAR7VgKoNB6ryXfw=
-github.com/go-playground/validator/v10 v10.15.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
+github.com/go-playground/validator/v10 v10.15.1 h1:BSe8uhN+xQ4r5guV/ywQI4gO59C2raYcGffYWZEjZzM=
+github.com/go-playground/validator/v10 v10.15.1/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
 github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg=
 github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg=
 github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
 github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
 github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
 github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
@@ -40,11 +40,9 @@ github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg
 github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
 github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
 github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
 github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
 github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
-github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
-github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4=
+github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4=
 github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4=
 github.com/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q=
 github.com/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q=
 github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
 github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
@@ -57,7 +55,6 @@ github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mO
 github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
 github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
 github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
 github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
 github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
 github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
-github.com/josharian/native v1.0.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
 github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q=
 github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q=
 github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4=
 github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4=
 github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
 github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
@@ -70,10 +67,6 @@ github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4
 github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
 github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
 github.com/mattn/go-sqlite3 v1.14.17 h1:mCRHCLDUBXgpKAqIKsaAaAsrAlbkeomtRFKXh2L6YIM=
 github.com/mattn/go-sqlite3 v1.14.17 h1:mCRHCLDUBXgpKAqIKsaAaAsrAlbkeomtRFKXh2L6YIM=
 github.com/mattn/go-sqlite3 v1.14.17/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
 github.com/mattn/go-sqlite3 v1.14.17/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
-github.com/mdlayher/genetlink v1.2.0/go.mod h1:ra5LDov2KrUCZJiAtEvXXZBxGMInICMXIwshlJ+qRxQ=
-github.com/mdlayher/netlink v1.6.0/go.mod h1:0o3PlBmGst1xve7wQ7j/hwpNaFaH4qCRyWCdcZk8/vA=
-github.com/mdlayher/socket v0.1.1/go.mod h1:mYV5YIZAfHh4dzDVzI8x8tWLWCliuX8Mon5Awbj+qDs=
-github.com/mikioh/ipaddr v0.0.0-20190404000644-d465c8ab6721/go.mod h1:Ickgr2WtCLZ2MDGd4Gr0geeCH5HybhRJbonOgQpvSxc=
 github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
 github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
 github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
 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 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
@@ -103,63 +96,37 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO
 github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
 github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
 github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
 github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
 github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
 github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
-github.com/txn2/txeh v1.4.0 h1:0tdvpA4HGJrj8X3kmrU6o/JFStI009nKxwDpMK5CnRU=
-github.com/txn2/txeh v1.4.0/go.mod h1:Mgq0hY184zCrDBLgvkIp+9NYGHoYbJcu4xKqUcx1shc=
+github.com/txn2/txeh v1.5.3 h1:ZMgc3r+5/AFtE/ayCoICpvxj7xl/CYsZjnIGhozV/Kc=
+github.com/txn2/txeh v1.5.3/go.mod h1:qYzGG9kCzeVEI12geK4IlanHWY8X4uy/I3NcW7mk8g4=
 github.com/urfave/cli v1.22.5/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
 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 h1:3lbZUMbMiGUW/LMkfsEABsc5zNT9+b1CvsJx47JzJ8g=
 github.com/xtgo/uuid v0.0.0-20140804021211-a0b114877d4c/go.mod h1:UrdRz5enIKZ63MEE3IF9l2/ebyx59GyGgPi+tICQdmM=
 github.com/xtgo/uuid v0.0.0-20140804021211-a0b114877d4c/go.mod h1:UrdRz5enIKZ63MEE3IF9l2/ebyx59GyGgPi+tICQdmM=
 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
 golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
-golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
-golang.org/x/crypto v0.0.0-20220208050332-20e1d8d225ab/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
 golang.org/x/crypto v0.12.0 h1:tFM/ta59kqch6LlvYnPa0yx5a83cL2nHflFhYKvv9Yk=
 golang.org/x/crypto v0.12.0 h1:tFM/ta59kqch6LlvYnPa0yx5a83cL2nHflFhYKvv9Yk=
 golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw=
 golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw=
 golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 h1:k/i9J1pBpvlfR+9QsetwPyERsqu1GIbi967PQMq3Ivc=
 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/exp v0.0.0-20230522175609-2e198f4a06a1/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w=
 golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
 golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
-golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
-golang.org/x/net v0.0.0-20210928044308-7d9f5e0b762b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
-golang.org/x/net v0.0.0-20211111083644-e5c967477495/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
-golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
-golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
 golang.org/x/net v0.14.0 h1:BONx9s002vGdD9umnlX1Po8vOZmrgH34qlHcD1MfK14=
 golang.org/x/net v0.14.0 h1:BONx9s002vGdD9umnlX1Po8vOZmrgH34qlHcD1MfK14=
 golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI=
 golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI=
 golang.org/x/oauth2 v0.11.0 h1:vPL4xzxBM4niKCW6g9whtaWVXTJf1U5e4aZxxFx/gbU=
 golang.org/x/oauth2 v0.11.0 h1:vPL4xzxBM4niKCW6g9whtaWVXTJf1U5e4aZxxFx/gbU=
 golang.org/x/oauth2 v0.11.0/go.mod h1:LdF7O/8bLR/qWK9DrpXmbHLTouvRHK0SgJl0GmDBchk=
 golang.org/x/oauth2 v0.11.0/go.mod h1:LdF7O/8bLR/qWK9DrpXmbHLTouvRHK0SgJl0GmDBchk=
-golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
 golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
 golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20211110154304-99a53858aa08/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220207234003-57398862261d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM=
 golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM=
 golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-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/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
 golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
-golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
-golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
-golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
 golang.org/x/text v0.12.0 h1:k+n5B8goJNdU7hSvEtMUz3d1Q6D/XW4COJSJR6fN0mc=
 golang.org/x/text v0.12.0 h1:k+n5B8goJNdU7hSvEtMUz3d1Q6D/XW4COJSJR6fN0mc=
 golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
 golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
 golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-golang.zx2c4.com/go118/netip v0.0.0-20211111135330-a4a02eeacf9d/go.mod h1:5yyfuiqVIJ7t+3MqrpTQ+QqRkMWiESiyDvPNvKYCecg=
-golang.zx2c4.com/wintun v0.0.0-20211104114900-415007cec224/go.mod h1:deeaetjYA+DHMHg+sMSMI58GrEteJUUzzw7en6TJQcI=
-golang.zx2c4.com/wireguard v0.0.0-20220202223031-3b95c81cc178/go.mod h1:TjUWrnD5ATh7bFvmm/ALEJZQ4ivKbETb6pmyj1vUoNI=
-golang.zx2c4.com/wireguard/wgctrl v0.0.0-20220324164955-056925b7df31 h1:AgW3hljgTzuRbCB0j+q9tXT0uy6ij7vMjEzSCeMlQY0=
-golang.zx2c4.com/wireguard/wgctrl v0.0.0-20220324164955-056925b7df31/go.mod h1:8P32Ilp1kCpwB4ItaHyvSk4xAtnpQ+8gQVfg5WaO1TU=
+golang.zx2c4.com/wireguard/wgctrl v0.0.0-20221104135756-97bc4ad4a1cb h1:9aqVcYEDHmSNb0uOWukxV5lHV09WqiSiCuhEgWNETLY=
+golang.zx2c4.com/wireguard/wgctrl v0.0.0-20221104135756-97bc4ad4a1cb/go.mod h1:mQqgjkW8GQQcJQsbBvK890TKqUK1DfKWkuBGbOkuMHQ=
 google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
 google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
 google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
 google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
 google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
 google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=

+ 1 - 1
k8s/client/netclient-daemonset.yaml

@@ -16,7 +16,7 @@ spec:
       hostNetwork: true
       hostNetwork: true
       containers:
       containers:
       - name: netclient
       - name: netclient
-        image: gravitl/netclient:v0.20.6
+        image: gravitl/netclient:v0.21.0
         env:
         env:
         - name: TOKEN
         - name: TOKEN
           value: "TOKEN_VALUE"
           value: "TOKEN_VALUE"

+ 1 - 1
k8s/client/netclient.yaml

@@ -28,7 +28,7 @@ spec:
       #           - "<node label value>"
       #           - "<node label value>"
       containers:
       containers:
       - name: netclient
       - name: netclient
-        image: gravitl/netclient:v0.20.6
+        image: gravitl/netclient:v0.21.0
         env:
         env:
         - name: TOKEN
         - name: TOKEN
           value: "TOKEN_VALUE"
           value: "TOKEN_VALUE"

+ 1 - 1
k8s/server/netmaker-ui.yaml

@@ -15,7 +15,7 @@ spec:
     spec:
     spec:
       containers:
       containers:
       - name: netmaker-ui
       - name: netmaker-ui
-        image: gravitl/netmaker-ui:v0.20.6
+        image: gravitl/netmaker-ui:v0.21.0
         ports:
         ports:
         - containerPort: 443
         - containerPort: 443
         env:
         env:

+ 25 - 181
logic/auth.go

@@ -11,14 +11,11 @@ import (
 
 
 	"github.com/gravitl/netmaker/database"
 	"github.com/gravitl/netmaker/database"
 	"github.com/gravitl/netmaker/logger"
 	"github.com/gravitl/netmaker/logger"
-	"github.com/gravitl/netmaker/logic/pro"
 	"github.com/gravitl/netmaker/models"
 	"github.com/gravitl/netmaker/models"
-	"github.com/gravitl/netmaker/models/promodels"
-	"github.com/gravitl/netmaker/servercfg"
 )
 )
 
 
-// HasAdmin - checks if server has an admin
-func HasAdmin() (bool, error) {
+// HasSuperAdmin - checks if server has an superadmin/owner
+func HasSuperAdmin() (bool, error) {
 
 
 	collection, err := database.FetchRecords(database.USERS_TABLE_NAME)
 	collection, err := database.FetchRecords(database.USERS_TABLE_NAME)
 	if err != nil {
 	if err != nil {
@@ -34,7 +31,7 @@ func HasAdmin() (bool, error) {
 		if err != nil {
 		if err != nil {
 			continue
 			continue
 		}
 		}
-		if user.IsAdmin {
+		if user.IsSuperAdmin {
 			return true, nil
 			return true, nil
 		}
 		}
 	}
 	}
@@ -85,9 +82,8 @@ func CreateUser(user *models.User) error {
 	// set password to encrypted password
 	// set password to encrypted password
 	user.Password = string(hash)
 	user.Password = string(hash)
 
 
-	tokenString, _ := CreateProUserJWT(user.UserName, user.Networks, user.Groups, user.IsAdmin)
+	tokenString, _ := CreateUserJWT(user.UserName, user.IsSuperAdmin, user.IsAdmin)
 	if tokenString == "" {
 	if tokenString == "" {
-		// logic.ReturnErrorResponse(w, r, errorResponse)
 		return err
 		return err
 	}
 	}
 
 
@@ -103,56 +99,20 @@ func CreateUser(user *models.User) error {
 		return err
 		return err
 	}
 	}
 
 
-	// == PRO == Add user to every network as network user ==
-	currentNets, err := GetNetworks()
-	if err != nil {
-		currentNets = []models.Network{}
-	}
-	for i := range currentNets {
-		newUser := promodels.NetworkUser{
-			ID:      promodels.NetworkUserID(user.UserName),
-			Clients: []string{},
-			Nodes:   []string{},
-		}
-
-		pro.AddProNetDefaults(&currentNets[i])
-		if pro.IsUserAllowed(&currentNets[i], user.UserName, user.Groups) {
-			newUser.AccessLevel = currentNets[i].ProSettings.DefaultAccessLevel
-			newUser.ClientLimit = currentNets[i].ProSettings.DefaultUserClientLimit
-			newUser.NodeLimit = currentNets[i].ProSettings.DefaultUserNodeLimit
-		} else {
-			newUser.AccessLevel = pro.NO_ACCESS
-			newUser.ClientLimit = 0
-			newUser.NodeLimit = 0
-		}
-
-		// legacy
-		if StringSliceContains(user.Networks, currentNets[i].NetID) {
-			if !servercfg.Is_EE {
-				newUser.AccessLevel = pro.NET_ADMIN
-			}
-		}
-		userErr := pro.CreateNetworkUser(&currentNets[i], &newUser)
-		if userErr != nil {
-			logger.Log(0, "failed to add network user data on network", currentNets[i].NetID, "for user", user.UserName)
-		}
-	}
-	// == END PRO ==
-
 	return nil
 	return nil
 }
 }
 
 
-// CreateAdmin - creates an admin user
-func CreateAdmin(admin *models.User) error {
-	hasadmin, err := HasAdmin()
+// CreateSuperAdmin - creates an super admin user
+func CreateSuperAdmin(u *models.User) error {
+	hassuperadmin, err := HasSuperAdmin()
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
-	if hasadmin {
-		return errors.New("admin user already exists")
+	if hassuperadmin {
+		return errors.New("superadmin user already exists")
 	}
 	}
-	admin.IsAdmin = true
-	return CreateUser(admin)
+	u.IsSuperAdmin = true
+	return CreateUser(u)
 }
 }
 
 
 // VerifyAuthRequest - verifies an auth request
 // VerifyAuthRequest - verifies an auth request
@@ -166,7 +126,7 @@ func VerifyAuthRequest(authRequest models.UserAuthParams) (string, error) {
 	// Search DB for node with Mac Address. Ignore pending nodes (they should not be able to authenticate with API until approved).
 	// Search DB for node with Mac Address. Ignore pending nodes (they should not be able to authenticate with API until approved).
 	record, err := database.FetchRecord(database.USERS_TABLE_NAME, authRequest.UserName)
 	record, err := database.FetchRecord(database.USERS_TABLE_NAME, authRequest.UserName)
 	if err != nil {
 	if err != nil {
-		return "", errors.New("error retrieving user from db: " + err.Error())
+		return "", errors.New("incorrect credentials")
 	}
 	}
 	if err = json.Unmarshal([]byte(record), &result); err != nil {
 	if err = json.Unmarshal([]byte(record), &result); err != nil {
 		return "", errors.New("error unmarshalling user json: " + err.Error())
 		return "", errors.New("error unmarshalling user json: " + err.Error())
@@ -180,65 +140,20 @@ func VerifyAuthRequest(authRequest models.UserAuthParams) (string, error) {
 	}
 	}
 
 
 	// Create a new JWT for the node
 	// Create a new JWT for the node
-	tokenString, _ := CreateProUserJWT(authRequest.UserName, result.Networks, result.Groups, result.IsAdmin)
+	tokenString, _ := CreateUserJWT(authRequest.UserName, result.IsSuperAdmin, result.IsAdmin)
 	return tokenString, nil
 	return tokenString, nil
 }
 }
 
 
-// UpdateUserNetworks - updates the networks of a given user
-func UpdateUserNetworks(newNetworks, newGroups []string, isadmin bool, currentUser *models.ReturnUser) error {
-	// check if user exists
-	returnedUser, err := GetUser(currentUser.UserName)
+// UpsertUser - updates user in the db
+func UpsertUser(user models.User) error {
+	data, err := json.Marshal(&user)
 	if err != nil {
 	if err != nil {
 		return err
 		return err
-	} else if returnedUser.IsAdmin {
-		return fmt.Errorf("can not make changes to an admin user, attempted to change %s", returnedUser.UserName)
-	}
-	if isadmin {
-		currentUser.IsAdmin = true
-		currentUser.Networks = nil
-	} else {
-		// == PRO ==
-		currentUser.Groups = newGroups
-		for _, n := range newNetworks {
-			if !StringSliceContains(currentUser.Networks, n) {
-				// make net admin of any network not previously assigned
-				pro.MakeNetAdmin(n, currentUser.UserName)
-			}
-		}
-		// Compare networks, find networks not in previous
-		for _, n := range currentUser.Networks {
-			if !StringSliceContains(newNetworks, n) {
-				// if user was removed from a network, re-assign access to net default level
-				if network, err := GetNetwork(n); err == nil {
-					if network.ProSettings != nil {
-						ok := pro.AssignAccessLvl(n, currentUser.UserName, network.ProSettings.DefaultAccessLevel)
-						if ok {
-							logger.Log(0, "changed", currentUser.UserName, "access level on network", network.NetID, "to", fmt.Sprintf("%d", network.ProSettings.DefaultAccessLevel))
-						}
-					}
-				}
-			}
-		}
-
-		if err := AdjustGroupPermissions(currentUser); err != nil {
-			logger.Log(0, "failed to update user", currentUser.UserName, "after group update", err.Error())
-		}
-		// == END PRO ==
-
-		currentUser.Networks = newNetworks
 	}
 	}
-
-	userChange := models.User{
-		UserName: currentUser.UserName,
-		Networks: currentUser.Networks,
-		IsAdmin:  currentUser.IsAdmin,
-		Password: "",
-		Groups:   currentUser.Groups,
+	if err = database.Insert(user.UserName, string(data), database.USERS_TABLE_NAME); err != nil {
+		return err
 	}
 	}
-
-	_, err = UpdateUser(&userChange, returnedUser)
-
-	return err
+	return nil
 }
 }
 
 
 // UpdateUser - updates a given user
 // UpdateUser - updates a given user
@@ -249,16 +164,13 @@ func UpdateUser(userchange, user *models.User) (*models.User, error) {
 	}
 	}
 
 
 	queryUser := user.UserName
 	queryUser := user.UserName
-
-	if userchange.UserName != "" {
+	if userchange.UserName != "" && user.UserName != userchange.UserName {
+		// check if username is available
+		if _, err := GetUser(userchange.UserName); err == nil {
+			return &models.User{}, errors.New("username exists already")
+		}
 		user.UserName = userchange.UserName
 		user.UserName = userchange.UserName
 	}
 	}
-	if len(userchange.Networks) > 0 {
-		user.Networks = userchange.Networks
-	}
-	if len(userchange.Groups) > 0 {
-		user.Groups = userchange.Groups
-	}
 	if userchange.Password != "" {
 	if userchange.Password != "" {
 		// encrypt that password so we never see it again
 		// encrypt that password so we never see it again
 		hash, err := bcrypt.GenerateFromPassword([]byte(userchange.Password), 5)
 		hash, err := bcrypt.GenerateFromPassword([]byte(userchange.Password), 5)
@@ -271,10 +183,7 @@ func UpdateUser(userchange, user *models.User) (*models.User, error) {
 
 
 		user.Password = userchange.Password
 		user.Password = userchange.Password
 	}
 	}
-
-	if (userchange.IsAdmin != user.IsAdmin) && !user.IsAdmin {
-		user.IsAdmin = userchange.IsAdmin
-	}
+	user.IsAdmin = userchange.IsAdmin
 
 
 	err := ValidateUser(user)
 	err := ValidateUser(user)
 	if err != nil {
 	if err != nil {
@@ -326,23 +235,6 @@ func DeleteUser(user string) (bool, error) {
 		return false, err
 		return false, err
 	}
 	}
 
 
-	// == pro - remove user from all network user instances ==
-	currentNets, err := GetNetworks()
-	if err != nil {
-		if database.IsEmptyRecord(err) {
-			currentNets = []models.Network{}
-		} else {
-			return true, err
-		}
-	}
-
-	for i := range currentNets {
-		netID := currentNets[i].NetID
-		if err = pro.DeleteNetworkUser(netID, user); err != nil {
-			logger.Log(0, "failed to remove", user, "as network user from network", netID, err.Error())
-		}
-	}
-
 	return true, nil
 	return true, nil
 }
 }
 
 
@@ -414,51 +306,3 @@ func IsStateValid(state string) (string, bool) {
 func delState(state string) error {
 func delState(state string) error {
 	return database.DeleteRecord(database.SSO_STATE_CACHE, state)
 	return database.DeleteRecord(database.SSO_STATE_CACHE, state)
 }
 }
-
-// PRO
-
-// AdjustGroupPermissions - adjusts a given user's network access based on group changes
-func AdjustGroupPermissions(user *models.ReturnUser) error {
-	networks, err := GetNetworks()
-	if err != nil {
-		return err
-	}
-	// UPDATE
-	// go through all networks and see if new group is in
-	// if access level of current user is greater (value) than network's default
-	// assign network's default
-	// DELETE
-	// if user not allowed on network a
-	for i := range networks {
-		AdjustNetworkUserPermissions(user, &networks[i])
-	}
-
-	return nil
-}
-
-// AdjustNetworkUserPermissions - adjusts a given user's network access based on group changes
-func AdjustNetworkUserPermissions(user *models.ReturnUser, network *models.Network) error {
-	networkUser, err := pro.GetNetworkUser(
-		network.NetID,
-		promodels.NetworkUserID(user.UserName),
-	)
-	if err == nil && network.ProSettings != nil {
-		if pro.IsUserAllowed(network, user.UserName, user.Groups) {
-			if networkUser.AccessLevel > network.ProSettings.DefaultAccessLevel {
-				networkUser.AccessLevel = network.ProSettings.DefaultAccessLevel
-			}
-			if networkUser.NodeLimit < network.ProSettings.DefaultUserNodeLimit {
-				networkUser.NodeLimit = network.ProSettings.DefaultUserNodeLimit
-			}
-			if networkUser.ClientLimit < network.ProSettings.DefaultUserClientLimit {
-				networkUser.ClientLimit = network.ProSettings.DefaultUserClientLimit
-			}
-		} else {
-			networkUser.AccessLevel = pro.NO_ACCESS
-			networkUser.NodeLimit = 0
-			networkUser.ClientLimit = 0
-		}
-		pro.UpdateNetworkUser(network.NetID, networkUser)
-	}
-	return err
-}

+ 5 - 38
logic/clients.go

@@ -22,48 +22,15 @@ var (
 	AllowClientNodeAccess = func(ec *models.ExtClient, clientOrNodeID string) bool {
 	AllowClientNodeAccess = func(ec *models.ExtClient, clientOrNodeID string) bool {
 		return true
 		return true
 	}
 	}
-)
-
-// SetClientDefaultACLs - set's a client's default ACLs based on network and nodes in network
-func SetClientDefaultACLs(ec *models.ExtClient) error {
-	if !isEE {
+	SetClientDefaultACLs = func(ec *models.ExtClient) error {
 		return nil
 		return nil
 	}
 	}
-	networkNodes, err := GetNetworkNodes(ec.Network)
-	if err != nil {
-		return err
-	}
-	network, err := GetNetwork(ec.Network)
-	if err != nil {
-		return err
-	}
-	for i := range networkNodes {
-		currNode := networkNodes[i]
-		if network.DefaultACL == "no" || currNode.DefaultACL == "no" {
-			DenyClientNodeAccess(ec, currNode.ID.String())
-		} else {
-			AllowClientNodeAccess(ec, currNode.ID.String())
-		}
-	}
-	return nil
-}
-
-// SetClientACLs - overwrites an ext client's ACL
-func SetClientACLs(ec *models.ExtClient, newACLs map[string]struct{}) {
-	if ec == nil || newACLs == nil || !isEE {
-		return
+	SetClientACLs = func(ec *models.ExtClient, newACLs map[string]struct{}) {
 	}
 	}
-	ec.DeniedACLs = newACLs
-}
-
-// IsClientNodeAllowedByID - checks if a given ext client ID + nodeID are allowed
-func IsClientNodeAllowedByID(clientID, networkName, clientOrNodeID string) bool {
-	client, err := GetExtClient(clientID, networkName)
-	if err != nil {
-		return false
+	UpdateProNodeACLs = func(node *models.Node) error {
+		return nil
 	}
 	}
-	return IsClientNodeAllowed(&client, clientOrNodeID)
-}
+)
 
 
 // SortExtClient - Sorts slice of ExtClients by their ClientID alphabetically with numbers first
 // SortExtClient - Sorts slice of ExtClients by their ClientID alphabetically with numbers first
 func SortExtClient(unsortedExtClient []models.ExtClient) {
 func SortExtClient(unsortedExtClient []models.ExtClient) {

+ 15 - 1
logic/dns.go

@@ -3,6 +3,7 @@ package logic
 import (
 import (
 	"encoding/json"
 	"encoding/json"
 	"os"
 	"os"
+	"regexp"
 	"sort"
 	"sort"
 
 
 	validator "github.com/go-playground/validator/v10"
 	validator "github.com/go-playground/validator/v10"
@@ -14,7 +15,10 @@ import (
 
 
 // SetDNS - sets the dns on file
 // SetDNS - sets the dns on file
 func SetDNS() error {
 func SetDNS() error {
-	hostfile := txeh.Hosts{}
+	hostfile, err := txeh.NewHosts(&txeh.HostsConfig{})
+	if err != nil {
+		return err
+	}
 	var corefilestring string
 	var corefilestring string
 	networks, err := GetNetworks()
 	networks, err := GetNetworks()
 	if err != nil && !database.IsEmptyRecord(err) {
 	if err != nil && !database.IsEmptyRecord(err) {
@@ -203,6 +207,11 @@ func ValidateDNSCreate(entry models.DNSEntry) error {
 
 
 	v := validator.New()
 	v := validator.New()
 
 
+	_ = v.RegisterValidation("whitespace", func(f1 validator.FieldLevel) bool {
+		match, _ := regexp.MatchString(`\s`, entry.Name)
+		return !match
+	})
+
 	_ = v.RegisterValidation("name_unique", func(fl validator.FieldLevel) bool {
 	_ = v.RegisterValidation("name_unique", func(fl validator.FieldLevel) bool {
 		num, err := GetDNSEntryNum(entry.Name, entry.Network)
 		num, err := GetDNSEntryNum(entry.Name, entry.Network)
 		return err == nil && num == 0
 		return err == nil && num == 0
@@ -227,6 +236,11 @@ func ValidateDNSUpdate(change models.DNSEntry, entry models.DNSEntry) error {
 
 
 	v := validator.New()
 	v := validator.New()
 
 
+	_ = v.RegisterValidation("whitespace", func(f1 validator.FieldLevel) bool {
+		match, _ := regexp.MatchString(`\s`, entry.Name)
+		return !match
+	})
+
 	_ = v.RegisterValidation("name_unique", func(fl validator.FieldLevel) bool {
 	_ = v.RegisterValidation("name_unique", func(fl validator.FieldLevel) bool {
 		//if name & net not changing name we are good
 		//if name & net not changing name we are good
 		if change.Name == entry.Name && change.Network == entry.Network {
 		if change.Name == entry.Name && change.Network == entry.Network {

+ 0 - 14
logic/enrollmentkey.go

@@ -5,7 +5,6 @@ import (
 	"encoding/json"
 	"encoding/json"
 	"errors"
 	"errors"
 	"fmt"
 	"fmt"
-	"golang.org/x/exp/slices"
 	"time"
 	"time"
 
 
 	"github.com/gravitl/netmaker/database"
 	"github.com/gravitl/netmaker/database"
@@ -224,16 +223,3 @@ func getEnrollmentKeysMap() (map[string]*models.EnrollmentKey, error) {
 	}
 	}
 	return currentKeys, nil
 	return currentKeys, nil
 }
 }
-
-// UserHasNetworksAccess - checks if a user `u` has access to all `networks`
-func UserHasNetworksAccess(networks []string, u *models.User) bool {
-	if u.IsAdmin {
-		return true
-	}
-	for _, n := range networks {
-		if !slices.Contains(u.Networks, n) {
-			return false
-		}
-	}
-	return true
-}

+ 0 - 74
logic/enrollmentkey_test.go

@@ -204,77 +204,3 @@ func TestDeTokenize_EnrollmentKeys(t *testing.T) {
 
 
 	removeAllEnrollments()
 	removeAllEnrollments()
 }
 }
-
-func TestHasNetworksAccess(t *testing.T) {
-	type Case struct {
-		// network names
-		n []string
-		u models.User
-	}
-	pass := []Case{
-		{
-			n: []string{"n1", "n2"},
-			u: models.User{
-				Networks: []string{"n1", "n2"},
-				IsAdmin:  false,
-			},
-		},
-		{
-			n: []string{"n1", "n2"},
-			u: models.User{
-				Networks: []string{},
-				IsAdmin:  true,
-			},
-		},
-		{
-			n: []string{"n1", "n2"},
-			u: models.User{
-				Networks: []string{"n1", "n2", "n3"},
-				IsAdmin:  false,
-			},
-		},
-		{
-			n: []string{"n2"},
-			u: models.User{
-				Networks: []string{"n2"},
-				IsAdmin:  false,
-			},
-		},
-	}
-	deny := []Case{
-		{
-			n: []string{"n1", "n2"},
-			u: models.User{
-				Networks: []string{"n2"},
-				IsAdmin:  false,
-			},
-		},
-		{
-			n: []string{"n1", "n2"},
-			u: models.User{
-				Networks: []string{},
-				IsAdmin:  false,
-			},
-		},
-		{
-			n: []string{"n1", "n2"},
-			u: models.User{
-				Networks: []string{"n3"},
-				IsAdmin:  false,
-			},
-		},
-		{
-			n: []string{"n2"},
-			u: models.User{
-				Networks: []string{"n1"},
-				IsAdmin:  false,
-			},
-		},
-	}
-	for _, tc := range pass {
-		assert.True(t, UserHasNetworksAccess(tc.n, &tc.u))
-	}
-	for _, tc := range deny {
-		assert.False(t, UserHasNetworksAccess(tc.n, &tc.u))
-	}
-}

+ 37 - 1
logic/gateway.go

@@ -148,7 +148,7 @@ func CreateIngressGateway(netid string, nodeid string, ingress models.IngressReq
 	node.IngressGatewayRange6 = network.AddressRange6
 	node.IngressGatewayRange6 = network.AddressRange6
 	node.IngressDNS = ingress.ExtclientDNS
 	node.IngressDNS = ingress.ExtclientDNS
 	node.SetLastModified()
 	node.SetLastModified()
-	if ingress.Failover && servercfg.Is_EE {
+	if ingress.Failover && servercfg.IsPro {
 		node.Failover = true
 		node.Failover = true
 	}
 	}
 	err = UpsertNode(&node)
 	err = UpsertNode(&node)
@@ -159,6 +159,25 @@ func CreateIngressGateway(netid string, nodeid string, ingress models.IngressReq
 	return node, err
 	return node, err
 }
 }
 
 
+// GetIngressGwUsers - lists the users having to access to ingressGW
+func GetIngressGwUsers(node models.Node) (models.IngressGwUsers, error) {
+
+	gwUsers := models.IngressGwUsers{
+		NodeID:  node.ID.String(),
+		Network: node.Network,
+	}
+	users, err := GetUsers()
+	if err != nil {
+		return gwUsers, err
+	}
+	for _, user := range users {
+		if !user.IsAdmin && !user.IsSuperAdmin {
+			gwUsers.Users = append(gwUsers.Users, user)
+		}
+	}
+	return gwUsers, nil
+}
+
 // DeleteIngressGateway - deletes an ingress gateway
 // DeleteIngressGateway - deletes an ingress gateway
 func DeleteIngressGateway(nodeid string) (models.Node, bool, []models.ExtClient, error) {
 func DeleteIngressGateway(nodeid string) (models.Node, bool, []models.ExtClient, error) {
 	removedClients := []models.ExtClient{}
 	removedClients := []models.ExtClient{}
@@ -210,3 +229,20 @@ func DeleteGatewayExtClients(gatewayID string, networkName string) error {
 	}
 	}
 	return nil
 	return nil
 }
 }
+
+// IsUserAllowedAccessToExtClient - checks if user has permission to access extclient
+func IsUserAllowedAccessToExtClient(username string, client models.ExtClient) bool {
+	if username == MasterUser {
+		return true
+	}
+	user, err := GetUser(username)
+	if err != nil {
+		return false
+	}
+	if !user.IsAdmin && !user.IsSuperAdmin {
+		if user.UserName != client.OwnerID {
+			return false
+		}
+	}
+	return true
+}

+ 1 - 1
logic/hosts.go

@@ -382,7 +382,7 @@ func DissasociateNodeFromHost(n *models.Node, h *models.Host) error {
 		h.Nodes = RemoveStringSlice(h.Nodes, index)
 		h.Nodes = RemoveStringSlice(h.Nodes, index)
 	}
 	}
 	go func() {
 	go func() {
-		if servercfg.Is_EE {
+		if servercfg.IsPro {
 			if clients, err := GetNetworkExtClients(n.Network); err != nil {
 			if clients, err := GetNetworkExtClients(n.Network); err != nil {
 				for i := range clients {
 				for i := range clients {
 					AllowClientNodeAccess(&clients[i], n.ID.String())
 					AllowClientNodeAccess(&clients[i], n.ID.String())

+ 11 - 35
logic/jwts.go

@@ -52,37 +52,13 @@ func CreateJWT(uuid string, macAddress string, network string) (response string,
 	return "", err
 	return "", err
 }
 }
 
 
-// CreateProUserJWT - creates a user jwt token
-func CreateProUserJWT(username string, networks, groups []string, isadmin bool) (response string, err error) {
-	expirationTime := time.Now().Add(60 * 12 * time.Minute)
-	claims := &models.UserClaims{
-		UserName: username,
-		Networks: networks,
-		IsAdmin:  isadmin,
-		Groups:   groups,
-		RegisteredClaims: jwt.RegisteredClaims{
-			Issuer:    "Netmaker",
-			Subject:   fmt.Sprintf("user|%s", username),
-			IssuedAt:  jwt.NewNumericDate(time.Now()),
-			ExpiresAt: jwt.NewNumericDate(expirationTime),
-		},
-	}
-
-	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
-	tokenString, err := token.SignedString(jwtSecretKey)
-	if err == nil {
-		return tokenString, nil
-	}
-	return "", err
-}
-
 // CreateUserJWT - creates a user jwt token
 // CreateUserJWT - creates a user jwt token
-func CreateUserJWT(username string, networks []string, isadmin bool) (response string, err error) {
+func CreateUserJWT(username string, issuperadmin, isadmin bool) (response string, err error) {
 	expirationTime := time.Now().Add(60 * 12 * time.Minute)
 	expirationTime := time.Now().Add(60 * 12 * time.Minute)
 	claims := &models.UserClaims{
 	claims := &models.UserClaims{
-		UserName: username,
-		Networks: networks,
-		IsAdmin:  isadmin,
+		UserName:     username,
+		IsSuperAdmin: issuperadmin,
+		IsAdmin:      isadmin,
 		RegisteredClaims: jwt.RegisteredClaims{
 		RegisteredClaims: jwt.RegisteredClaims{
 			Issuer:    "Netmaker",
 			Issuer:    "Netmaker",
 			Subject:   fmt.Sprintf("user|%s", username),
 			Subject:   fmt.Sprintf("user|%s", username),
@@ -100,23 +76,23 @@ func CreateUserJWT(username string, networks []string, isadmin bool) (response s
 }
 }
 
 
 // VerifyJWT verifies Auth Header
 // VerifyJWT verifies Auth Header
-func VerifyJWT(bearerToken string) (username string, networks []string, isadmin bool, err error) {
+func VerifyJWT(bearerToken string) (username string, issuperadmin, isadmin bool, err error) {
 	token := ""
 	token := ""
 	tokenSplit := strings.Split(bearerToken, " ")
 	tokenSplit := strings.Split(bearerToken, " ")
 	if len(tokenSplit) > 1 {
 	if len(tokenSplit) > 1 {
 		token = tokenSplit[1]
 		token = tokenSplit[1]
 	} else {
 	} else {
-		return "", nil, false, errors.New("invalid auth header")
+		return "", false, false, errors.New("invalid auth header")
 	}
 	}
 	return VerifyUserToken(token)
 	return VerifyUserToken(token)
 }
 }
 
 
 // VerifyUserToken func will used to Verify the JWT Token while using APIS
 // VerifyUserToken func will used to Verify the JWT Token while using APIS
-func VerifyUserToken(tokenString string) (username string, networks []string, isadmin bool, err error) {
+func VerifyUserToken(tokenString string) (username string, issuperadmin, isadmin bool, err error) {
 	claims := &models.UserClaims{}
 	claims := &models.UserClaims{}
 
 
 	if tokenString == servercfg.GetMasterKey() && servercfg.GetMasterKey() != "" {
 	if tokenString == servercfg.GetMasterKey() && servercfg.GetMasterKey() != "" {
-		return "masteradministrator", nil, true, nil
+		return MasterUser, true, true, nil
 	}
 	}
 
 
 	token, err := jwt.ParseWithClaims(tokenString, claims, func(token *jwt.Token) (interface{}, error) {
 	token, err := jwt.ParseWithClaims(tokenString, claims, func(token *jwt.Token) (interface{}, error) {
@@ -128,15 +104,15 @@ func VerifyUserToken(tokenString string) (username string, networks []string, is
 		// check that user exists
 		// check that user exists
 		user, err = GetUser(claims.UserName)
 		user, err = GetUser(claims.UserName)
 		if err != nil {
 		if err != nil {
-			return "", nil, false, err
+			return "", false, false, err
 		}
 		}
 
 
 		if user.UserName != "" {
 		if user.UserName != "" {
-			return claims.UserName, claims.Networks, claims.IsAdmin, nil
+			return claims.UserName, claims.IsSuperAdmin, claims.IsAdmin, nil
 		}
 		}
 		err = errors.New("user does not exist")
 		err = errors.New("user does not exist")
 	}
 	}
-	return "", nil, false, err
+	return "", false, false, err
 }
 }
 
 
 // VerifyHostToken - [hosts] Only
 // VerifyHostToken - [hosts] Only

+ 7 - 28
logic/metrics.go

@@ -1,39 +1,18 @@
 package logic
 package logic
 
 
 import (
 import (
-	"encoding/json"
-
-	"github.com/gravitl/netmaker/database"
 	"github.com/gravitl/netmaker/models"
 	"github.com/gravitl/netmaker/models"
 )
 )
 
 
-// GetMetrics - gets the metrics
-func GetMetrics(nodeid string) (*models.Metrics, error) {
-	var metrics models.Metrics
-	record, err := database.FetchRecord(database.METRICS_TABLE_NAME, nodeid)
-	if err != nil {
-		if database.IsEmptyRecord(err) {
-			return &metrics, nil
-		}
-		return &metrics, err
-	}
-	err = json.Unmarshal([]byte(record), &metrics)
-	if err != nil {
-		return &metrics, err
-	}
-	return &metrics, nil
+var DeleteMetrics = func(string) error {
+	return nil
 }
 }
 
 
-// UpdateMetrics - updates the metrics of a given client
-func UpdateMetrics(nodeid string, metrics *models.Metrics) error {
-	data, err := json.Marshal(metrics)
-	if err != nil {
-		return err
-	}
-	return database.Insert(nodeid, string(data), database.METRICS_TABLE_NAME)
+var UpdateMetrics = func(string, *models.Metrics) error {
+	return nil
 }
 }
 
 
-// DeleteMetrics - deletes metrics of a given node
-func DeleteMetrics(nodeid string) error {
-	return database.DeleteRecord(database.METRICS_TABLE_NAME, nodeid)
+var GetMetrics = func(string) (*models.Metrics, error) {
+	var metrics models.Metrics
+	return &metrics, nil
 }
 }

+ 5 - 37
logic/networks.go

@@ -14,7 +14,6 @@ import (
 	"github.com/gravitl/netmaker/database"
 	"github.com/gravitl/netmaker/database"
 	"github.com/gravitl/netmaker/logger"
 	"github.com/gravitl/netmaker/logger"
 	"github.com/gravitl/netmaker/logic/acls/nodeacls"
 	"github.com/gravitl/netmaker/logic/acls/nodeacls"
-	"github.com/gravitl/netmaker/logic/pro"
 	"github.com/gravitl/netmaker/models"
 	"github.com/gravitl/netmaker/models"
 	"github.com/gravitl/netmaker/validation"
 	"github.com/gravitl/netmaker/validation"
 )
 )
@@ -51,9 +50,6 @@ func DeleteNetwork(network string) error {
 	nodeCount, err := GetNetworkNonServerNodeCount(network)
 	nodeCount, err := GetNetworkNonServerNodeCount(network)
 	if nodeCount == 0 || database.IsEmptyRecord(err) {
 	if nodeCount == 0 || database.IsEmptyRecord(err) {
 		// delete server nodes first then db records
 		// delete server nodes first then db records
-		if err = pro.RemoveAllNetworkUsers(network); err != nil {
-			logger.Log(0, "failed to remove network users on network delete for network", network, err.Error())
-		}
 		return database.DeleteRecord(database.NETWORKS_TABLE_NAME, network)
 		return database.DeleteRecord(database.NETWORKS_TABLE_NAME, network)
 	}
 	}
 	return errors.New("node check failed. All nodes must be deleted before deleting network")
 	return errors.New("node check failed. All nodes must be deleted before deleting network")
@@ -81,22 +77,12 @@ func CreateNetwork(network models.Network) (models.Network, error) {
 	network.SetNodesLastModified()
 	network.SetNodesLastModified()
 	network.SetNetworkLastModified()
 	network.SetNetworkLastModified()
 
 
-	pro.AddProNetDefaults(&network)
-
-	if len(network.ProSettings.AllowedGroups) == 0 {
-		network.ProSettings.AllowedGroups = []string{pro.DEFAULT_ALLOWED_GROUPS}
-	}
-
 	err := ValidateNetwork(&network, false)
 	err := ValidateNetwork(&network, false)
 	if err != nil {
 	if err != nil {
 		//logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
 		//logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
 		return models.Network{}, err
 		return models.Network{}, err
 	}
 	}
 
 
-	if err = pro.InitializeNetworkUsers(network.NetID); err != nil {
-		return models.Network{}, err
-	}
-
 	data, err := json.Marshal(&network)
 	data, err := json.Marshal(&network)
 	if err != nil {
 	if err != nil {
 		return models.Network{}, err
 		return models.Network{}, err
@@ -106,11 +92,6 @@ func CreateNetwork(network models.Network) (models.Network, error) {
 		return models.Network{}, err
 		return models.Network{}, err
 	}
 	}
 
 
-	// == add all current users to network as network users ==
-	if err = InitializeNetUsers(&network); err != nil {
-		return network, err
-	}
-
 	return network, nil
 	return network, nil
 }
 }
 
 
@@ -302,28 +283,24 @@ func IsNetworkNameUnique(network *models.Network) (bool, error) {
 }
 }
 
 
 // UpdateNetwork - updates a network with another network's fields
 // UpdateNetwork - updates a network with another network's fields
-func UpdateNetwork(currentNetwork *models.Network, newNetwork *models.Network) (bool, bool, bool, []string, []string, error) {
+func UpdateNetwork(currentNetwork *models.Network, newNetwork *models.Network) (bool, bool, bool, error) {
 	if err := ValidateNetwork(newNetwork, true); err != nil {
 	if err := ValidateNetwork(newNetwork, true); err != nil {
-		return false, false, false, nil, nil, err
+		return false, false, false, err
 	}
 	}
 	if newNetwork.NetID == currentNetwork.NetID {
 	if newNetwork.NetID == currentNetwork.NetID {
 		hasrangeupdate4 := newNetwork.AddressRange != currentNetwork.AddressRange
 		hasrangeupdate4 := newNetwork.AddressRange != currentNetwork.AddressRange
 		hasrangeupdate6 := newNetwork.AddressRange6 != currentNetwork.AddressRange6
 		hasrangeupdate6 := newNetwork.AddressRange6 != currentNetwork.AddressRange6
 		hasholepunchupdate := newNetwork.DefaultUDPHolePunch != currentNetwork.DefaultUDPHolePunch
 		hasholepunchupdate := newNetwork.DefaultUDPHolePunch != currentNetwork.DefaultUDPHolePunch
-		groupDelta := append(StringDifference(newNetwork.ProSettings.AllowedGroups, currentNetwork.ProSettings.AllowedGroups),
-			StringDifference(currentNetwork.ProSettings.AllowedGroups, newNetwork.ProSettings.AllowedGroups)...)
-		userDelta := append(StringDifference(newNetwork.ProSettings.AllowedUsers, currentNetwork.ProSettings.AllowedUsers),
-			StringDifference(currentNetwork.ProSettings.AllowedUsers, newNetwork.ProSettings.AllowedUsers)...)
 		data, err := json.Marshal(newNetwork)
 		data, err := json.Marshal(newNetwork)
 		if err != nil {
 		if err != nil {
-			return false, false, false, nil, nil, err
+			return false, false, false, err
 		}
 		}
 		newNetwork.SetNetworkLastModified()
 		newNetwork.SetNetworkLastModified()
 		err = database.Insert(newNetwork.NetID, string(data), database.NETWORKS_TABLE_NAME)
 		err = database.Insert(newNetwork.NetID, string(data), database.NETWORKS_TABLE_NAME)
-		return hasrangeupdate4, hasrangeupdate6, hasholepunchupdate, groupDelta, userDelta, err
+		return hasrangeupdate4, hasrangeupdate6, hasholepunchupdate, err
 	}
 	}
 	// copy values
 	// copy values
-	return false, false, false, nil, nil, errors.New("failed to update network " + newNetwork.NetID + ", cannot change netid.")
+	return false, false, false, errors.New("failed to update network " + newNetwork.NetID + ", cannot change netid.")
 }
 }
 
 
 // GetNetwork - gets a network from database
 // GetNetwork - gets a network from database
@@ -375,15 +352,6 @@ func ValidateNetwork(network *models.Network, isUpdate bool) error {
 		}
 		}
 	}
 	}
 
 
-	if network.ProSettings != nil {
-		if network.ProSettings.DefaultAccessLevel < pro.NET_ADMIN || network.ProSettings.DefaultAccessLevel > pro.NO_ACCESS {
-			return fmt.Errorf("invalid access level")
-		}
-		if network.ProSettings.DefaultUserClientLimit < 0 || network.ProSettings.DefaultUserNodeLimit < 0 {
-			return fmt.Errorf("invalid node/client limit provided")
-		}
-	}
-
 	return err
 	return err
 }
 }
 
 

+ 4 - 43
logic/nodes.go

@@ -16,8 +16,6 @@ import (
 	"github.com/gravitl/netmaker/logger"
 	"github.com/gravitl/netmaker/logger"
 	"github.com/gravitl/netmaker/logic/acls"
 	"github.com/gravitl/netmaker/logic/acls"
 	"github.com/gravitl/netmaker/logic/acls/nodeacls"
 	"github.com/gravitl/netmaker/logic/acls/nodeacls"
-	"github.com/gravitl/netmaker/logic/pro"
-	"github.com/gravitl/netmaker/logic/pro/proacls"
 	"github.com/gravitl/netmaker/models"
 	"github.com/gravitl/netmaker/models"
 	"github.com/gravitl/netmaker/servercfg"
 	"github.com/gravitl/netmaker/servercfg"
 	"github.com/gravitl/netmaker/validation"
 	"github.com/gravitl/netmaker/validation"
@@ -150,7 +148,7 @@ func UpdateNode(currentNode *models.Node, newNode *models.Node) error {
 		}
 		}
 	}
 	}
 	nodeACLDelta := currentNode.DefaultACL != newNode.DefaultACL
 	nodeACLDelta := currentNode.DefaultACL != newNode.DefaultACL
-	newNode.Fill(currentNode, servercfg.Is_EE)
+	newNode.Fill(currentNode, servercfg.IsPro)
 
 
 	// check for un-settable server values
 	// check for un-settable server values
 	if err := ValidateNode(newNode, true); err != nil {
 	if err := ValidateNode(newNode, true); err != nil {
@@ -159,7 +157,7 @@ func UpdateNode(currentNode *models.Node, newNode *models.Node) error {
 
 
 	if newNode.ID == currentNode.ID {
 	if newNode.ID == currentNode.ID {
 		if nodeACLDelta {
 		if nodeACLDelta {
-			if err := updateProNodeACLS(newNode); err != nil {
+			if err := UpdateProNodeACLs(newNode); err != nil {
 				logger.Log(1, "failed to apply node level ACLs during creation of node", newNode.ID.String(), "-", err.Error())
 				logger.Log(1, "failed to apply node level ACLs during creation of node", newNode.ID.String(), "-", err.Error())
 				return err
 				return err
 			}
 			}
@@ -208,7 +206,7 @@ func DeleteNode(node *models.Node, purge bool) error {
 	if err := DissasociateNodeFromHost(node, host); err != nil {
 	if err := DissasociateNodeFromHost(node, host); err != nil {
 		return err
 		return err
 	}
 	}
-	if servercfg.Is_EE {
+	if servercfg.IsPro {
 		if err := EnterpriseResetAllPeersFailovers(node.ID, node.Network); err != nil {
 		if err := EnterpriseResetAllPeersFailovers(node.ID, node.Network); err != nil {
 			logger.Log(0, "failed to reset failover lists during node delete for node", host.Name, node.Network)
 			logger.Log(0, "failed to reset failover lists during node delete for node", host.Name, node.Network)
 		}
 		}
@@ -236,12 +234,6 @@ func deleteNodeByID(node *models.Node) error {
 	if servercfg.IsDNSMode() {
 	if servercfg.IsDNSMode() {
 		SetDNS()
 		SetDNS()
 	}
 	}
-	if node.OwnerID != "" {
-		err = pro.DissociateNetworkUserNode(node.OwnerID, node.Network, node.ID.String())
-		if err != nil {
-			logger.Log(0, "failed to dissasociate", node.OwnerID, "from node", node.ID.String(), ":", err.Error())
-		}
-	}
 	_, err = nodeacls.RemoveNodeACL(nodeacls.NetworkID(node.Network), nodeacls.NodeID(node.ID.String()))
 	_, err = nodeacls.RemoveNodeACL(nodeacls.NetworkID(node.Network), nodeacls.NodeID(node.ID.String()))
 	if err != nil {
 	if err != nil {
 		// ignoring for now, could hit a nil pointer if delete called twice
 		// ignoring for now, could hit a nil pointer if delete called twice
@@ -421,21 +413,6 @@ func FindRelay(node *models.Node) *models.Node {
 	return &relay
 	return &relay
 }
 }
 
 
-// GetNetworkIngresses - gets the gateways of a network
-func GetNetworkIngresses(network string) ([]models.Node, error) {
-	var ingresses []models.Node
-	netNodes, err := GetNetworkNodes(network)
-	if err != nil {
-		return []models.Node{}, err
-	}
-	for i := range netNodes {
-		if netNodes[i].IsIngressGateway {
-			ingresses = append(ingresses, netNodes[i])
-		}
-	}
-	return ingresses, nil
-}
-
 // GetAllNodesAPI - get all nodes for api usage
 // GetAllNodesAPI - get all nodes for api usage
 func GetAllNodesAPI(nodes []models.Node) []models.ApiNode {
 func GetAllNodesAPI(nodes []models.Node) []models.ApiNode {
 	apiNodes := []models.ApiNode{}
 	apiNodes := []models.ApiNode{}
@@ -475,20 +452,6 @@ func DeleteExpiredNodes(ctx context.Context, peerUpdate chan *models.Node) {
 	}
 	}
 }
 }
 
 
-// == PRO ==
-
-func updateProNodeACLS(node *models.Node) error {
-	// == PRO node ACLs ==
-	networkNodes, err := GetNetworkNodes(node.Network)
-	if err != nil {
-		return err
-	}
-	if err = proacls.AdjustNodeAcls(node, networkNodes[:]); err != nil {
-		return err
-	}
-	return nil
-}
-
 // createNode - creates a node in database
 // createNode - creates a node in database
 func createNode(node *models.Node) error {
 func createNode(node *models.Node) error {
 	// lock because we need unique IPs and having it concurrent makes parallel calls result in same "unique" IPs
 	// lock because we need unique IPs and having it concurrent makes parallel calls result in same "unique" IPs
@@ -578,7 +541,7 @@ func createNode(node *models.Node) error {
 		return err
 		return err
 	}
 	}
 
 
-	if err = updateProNodeACLS(node); err != nil {
+	if err = UpdateProNodeACLs(node); err != nil {
 		logger.Log(1, "failed to apply node level ACLs during creation of node", node.ID.String(), "-", err.Error())
 		logger.Log(1, "failed to apply node level ACLs during creation of node", node.ID.String(), "-", err.Error())
 		return err
 		return err
 	}
 	}
@@ -600,5 +563,3 @@ func SortApiNodes(unsortedNodes []models.ApiNode) {
 		return unsortedNodes[i].ID < unsortedNodes[j].ID
 		return unsortedNodes[i].ID < unsortedNodes[j].ID
 	})
 	})
 }
 }
-
-// == END PRO ==

+ 6 - 61
logic/peers.go

@@ -181,6 +181,7 @@ func GetPeerUpdateForHost(network string, host *models.Host, allNodes []models.N
 				hostPeerUpdate.HostNetworkInfo[peerHost.PublicKey.String()] = models.HostNetworkInfo{
 				hostPeerUpdate.HostNetworkInfo[peerHost.PublicKey.String()] = models.HostNetworkInfo{
 					Interfaces: peerHost.Interfaces,
 					Interfaces: peerHost.Interfaces,
 					ListenPort: peerPort,
 					ListenPort: peerPort,
+					IsStatic:   peerHost.IsStatic,
 				}
 				}
 				nodePeer = peerConfig
 				nodePeer = peerConfig
 			} else {
 			} else {
@@ -191,6 +192,7 @@ func GetPeerUpdateForHost(network string, host *models.Host, allNodes []models.N
 				hostPeerUpdate.HostNetworkInfo[peerHost.PublicKey.String()] = models.HostNetworkInfo{
 				hostPeerUpdate.HostNetworkInfo[peerHost.PublicKey.String()] = models.HostNetworkInfo{
 					Interfaces: peerHost.Interfaces,
 					Interfaces: peerHost.Interfaces,
 					ListenPort: peerPort,
 					ListenPort: peerPort,
+					IsStatic:   peerHost.IsStatic,
 				}
 				}
 				nodePeer = hostPeerUpdate.Peers[peerIndexMap[peerHost.PublicKey.String()]]
 				nodePeer = hostPeerUpdate.Peers[peerIndexMap[peerHost.PublicKey.String()]]
 			}
 			}
@@ -375,36 +377,14 @@ func GetAllowedIPs(node, peer *models.Node, metrics *models.Metrics) []net.IPNet
 		for _, extPeer := range extPeers {
 		for _, extPeer := range extPeers {
 			allowedips = append(allowedips, extPeer.AllowedIPs...)
 			allowedips = append(allowedips, extPeer.AllowedIPs...)
 		}
 		}
-		// if node is a failover node, add allowed ips from nodes it is handling
-		if metrics != nil && peer.Failover && metrics.FailoverPeers != nil {
-			// traverse through nodes that need handling
-			logger.Log(3, "peer", peer.ID.String(), "was found to be failover for", node.ID.String(), "checking failover peers...")
-			for k := range metrics.FailoverPeers {
-				// if FailoverNode is me for this node, add allowedips
-				if metrics.FailoverPeers[k] == peer.ID.String() {
-					// get original node so we can traverse the allowed ips
-					nodeToFailover, err := GetNodeByID(k)
-					if err == nil {
-						failoverNodeMetrics, err := GetMetrics(nodeToFailover.ID.String())
-						if err == nil && failoverNodeMetrics != nil {
-							if len(failoverNodeMetrics.NodeName) > 0 {
-								allowedips = append(allowedips, getNodeAllowedIPs(&nodeToFailover, peer)...)
-								logger.Log(0, "failing over node", nodeToFailover.ID.String(), nodeToFailover.PrimaryAddress(), "to failover node", peer.ID.String())
-							}
-						}
-					}
-				}
-			}
-		}
 	}
 	}
 	if node.IsRelayed && node.RelayedBy == peer.ID.String() {
 	if node.IsRelayed && node.RelayedBy == peer.ID.String() {
-		allowedips = append(allowedips, getAllowedIpsForRelayed(node, peer)...)
-
+		allowedips = append(allowedips, GetAllowedIpsForRelayed(node, peer)...)
 	}
 	}
 	return allowedips
 	return allowedips
 }
 }
 
 
-func getEgressIPs(peer *models.Node) []net.IPNet {
+func GetEgressIPs(peer *models.Node) []net.IPNet {
 
 
 	peerHost, err := GetHost(peer.HostID.String())
 	peerHost, err := GetHost(peer.HostID.String())
 	if err != nil {
 	if err != nil {
@@ -461,50 +441,15 @@ func getNodeAllowedIPs(peer, node *models.Node) []net.IPNet {
 	// handle egress gateway peers
 	// handle egress gateway peers
 	if peer.IsEgressGateway {
 	if peer.IsEgressGateway {
 		//hasGateway = true
 		//hasGateway = true
-		egressIPs := getEgressIPs(peer)
+		egressIPs := GetEgressIPs(peer)
 		allowedips = append(allowedips, egressIPs...)
 		allowedips = append(allowedips, egressIPs...)
 	}
 	}
 	if peer.IsRelay {
 	if peer.IsRelay {
-		for _, relayedNodeID := range peer.RelayedNodes {
-			if node.ID.String() == relayedNodeID {
-				continue
-			}
-			relayedNode, err := GetNodeByID(relayedNodeID)
-			if err != nil {
-				continue
-			}
-			allowed := getRelayedAddresses(relayedNodeID)
-			if relayedNode.IsEgressGateway {
-				allowed = append(allowed, getEgressIPs(&relayedNode)...)
-			}
-			allowedips = append(allowedips, allowed...)
-		}
+		allowedips = append(allowedips, RelayedAllowedIPs(peer, node)...)
 	}
 	}
 	return allowedips
 	return allowedips
 }
 }
 
 
-// getAllowedIpsForRelayed - returns the peerConfig for a node relayed by relay
-func getAllowedIpsForRelayed(relayed, relay *models.Node) (allowedIPs []net.IPNet) {
-	if relayed.RelayedBy != relay.ID.String() {
-		logger.Log(0, "RelayedByRelay called with invalid parameters")
-		return
-	}
-	peers, err := GetNetworkNodes(relay.Network)
-	if err != nil {
-		logger.Log(0, "error getting network clients", err.Error())
-		return
-	}
-	for _, peer := range peers {
-		if peer.ID == relayed.ID || peer.ID == relay.ID {
-			continue
-		}
-		if nodeacls.AreNodesAllowed(nodeacls.NetworkID(relayed.Network), nodeacls.NodeID(relayed.ID.String()), nodeacls.NodeID(peer.ID.String())) {
-			allowedIPs = append(allowedIPs, GetAllowedIPs(relayed, &peer, nil)...)
-		}
-	}
-	return
-}
-
 func getCIDRMaskFromAddr(addr string) net.IPMask {
 func getCIDRMaskFromAddr(addr string) net.IPMask {
 	cidr := net.CIDRMask(32, 32)
 	cidr := net.CIDRMask(32, 32)
 	ipAddr, err := netip.ParseAddr(addr)
 	ipAddr, err := netip.ParseAddr(addr)

+ 0 - 68
logic/pro/networks.go

@@ -1,68 +0,0 @@
-package pro
-
-import (
-	"github.com/gravitl/netmaker/models"
-	"github.com/gravitl/netmaker/models/promodels"
-)
-
-// AddProNetDefaults - adds default values to a network model
-func AddProNetDefaults(network *models.Network) {
-	if network.ProSettings == nil {
-		newProSettings := promodels.ProNetwork{
-			DefaultAccessLevel:     NO_ACCESS,
-			DefaultUserNodeLimit:   0,
-			DefaultUserClientLimit: 0,
-			AllowedUsers:           []string{},
-			AllowedGroups:          []string{DEFAULT_ALLOWED_GROUPS},
-		}
-		network.ProSettings = &newProSettings
-	}
-	if network.ProSettings.AllowedUsers == nil {
-		network.ProSettings.AllowedUsers = []string{}
-	}
-	if network.ProSettings.AllowedGroups == nil {
-		network.ProSettings.AllowedGroups = []string{DEFAULT_ALLOWED_GROUPS}
-	}
-}
-
-// isUserGroupAllowed - checks if a user group is allowed on a network
-func isUserGroupAllowed(network *models.Network, groupName string) bool {
-	if network.ProSettings != nil {
-		if len(network.ProSettings.AllowedGroups) > 0 {
-			for i := range network.ProSettings.AllowedGroups {
-				currentGroup := network.ProSettings.AllowedGroups[i]
-				if currentGroup == DEFAULT_ALLOWED_GROUPS || currentGroup == groupName {
-					return true
-				}
-			}
-		}
-	}
-	return false
-}
-
-func isUserInAllowedUsers(network *models.Network, userName string) bool {
-	if network.ProSettings != nil {
-		if len(network.ProSettings.AllowedUsers) > 0 {
-			for i := range network.ProSettings.AllowedUsers {
-				currentUser := network.ProSettings.AllowedUsers[i]
-				if currentUser == DEFAULT_ALLOWED_USERS || currentUser == userName {
-					return true
-				}
-			}
-		}
-	}
-	return false
-}
-
-// IsUserAllowed - checks if given username + groups if a user is allowed on network
-func IsUserAllowed(network *models.Network, userName string, groups []string) bool {
-	isGroupAllowed := false
-	for _, g := range groups {
-		if isUserGroupAllowed(network, g) {
-			isGroupAllowed = true
-			break
-		}
-	}
-
-	return isUserInAllowedUsers(network, userName) || isGroupAllowed
-}

+ 0 - 64
logic/pro/networks_test.go

@@ -1,64 +0,0 @@
-package pro
-
-import (
-	"testing"
-
-	"github.com/gravitl/netmaker/models"
-	"github.com/gravitl/netmaker/models/promodels"
-	"github.com/stretchr/testify/assert"
-)
-
-func TestNetworkProSettings(t *testing.T) {
-	t.Run("Uninitialized with pro", func(t *testing.T) {
-		network := models.Network{
-			NetID: "helloworld",
-		}
-		assert.Nil(t, network.ProSettings)
-	})
-	t.Run("Initialized with pro", func(t *testing.T) {
-		network := models.Network{
-			NetID: "helloworld",
-		}
-		AddProNetDefaults(&network)
-		assert.NotNil(t, network.ProSettings)
-	})
-	t.Run("Net Zero Defaults set correctly with Pro", func(t *testing.T) {
-		network := models.Network{
-			NetID: "helloworld",
-		}
-		AddProNetDefaults(&network)
-		assert.NotNil(t, network.ProSettings)
-		assert.Equal(t, NO_ACCESS, network.ProSettings.DefaultAccessLevel)
-		assert.Equal(t, 0, network.ProSettings.DefaultUserClientLimit)
-		assert.Equal(t, 0, network.ProSettings.DefaultUserNodeLimit)
-	})
-	t.Run("Net Defaults set correctly with Pro", func(t *testing.T) {
-		network := models.Network{
-			NetID: "helloworld",
-			ProSettings: &promodels.ProNetwork{
-				DefaultAccessLevel:     NET_ADMIN,
-				DefaultUserNodeLimit:   10,
-				DefaultUserClientLimit: 25,
-			},
-		}
-		AddProNetDefaults(&network)
-		assert.NotNil(t, network.ProSettings)
-		assert.Equal(t, NET_ADMIN, network.ProSettings.DefaultAccessLevel)
-		assert.Equal(t, 25, network.ProSettings.DefaultUserClientLimit)
-		assert.Equal(t, 10, network.ProSettings.DefaultUserNodeLimit)
-	})
-	t.Run("Net Defaults set to allow all groups/users", func(t *testing.T) {
-		network := models.Network{
-			NetID: "helloworld",
-			ProSettings: &promodels.ProNetwork{
-				DefaultAccessLevel:     NET_ADMIN,
-				DefaultUserNodeLimit:   10,
-				DefaultUserClientLimit: 25,
-			},
-		}
-		AddProNetDefaults(&network)
-		assert.NotNil(t, network.ProSettings)
-		assert.Equal(t, len(network.ProSettings.AllowedGroups), 1)
-		assert.Equal(t, len(network.ProSettings.AllowedUsers), 0)
-	})
-}

+ 0 - 251
logic/pro/networkuser.go

@@ -1,251 +0,0 @@
-package pro
-
-import (
-	"encoding/json"
-	"fmt"
-
-	"github.com/gravitl/netmaker/database"
-	"github.com/gravitl/netmaker/models"
-	"github.com/gravitl/netmaker/models/promodels"
-)
-
-// InitializeNetworkUsers - intializes network users for a given network
-func InitializeNetworkUsers(network string) error {
-
-	_, err := database.FetchRecord(database.NETWORK_USER_TABLE_NAME, network)
-	if err != nil && database.IsEmptyRecord(err) {
-		newNetUserMap := make(promodels.NetworkUserMap)
-		netUserData, err := json.Marshal(newNetUserMap)
-		if err != nil {
-			return err
-		}
-
-		return database.Insert(network, string(netUserData), database.NETWORK_USER_TABLE_NAME)
-	}
-	return err
-}
-
-// GetNetworkUsers - gets the network users table
-func GetNetworkUsers(network string) (promodels.NetworkUserMap, error) {
-	currentUsers, err := database.FetchRecord(database.NETWORK_USER_TABLE_NAME, network)
-	if err != nil {
-		return nil, err
-	}
-	var userMap promodels.NetworkUserMap
-	if err = json.Unmarshal([]byte(currentUsers), &userMap); err != nil {
-		return nil, err
-	}
-	return userMap, nil
-}
-
-// CreateNetworkUser - adds a network user to db
-func CreateNetworkUser(network *models.Network, user *promodels.NetworkUser) error {
-
-	if DoesNetworkUserExist(network.NetID, user.ID) {
-		return nil
-	}
-
-	currentUsers, err := GetNetworkUsers(network.NetID)
-	if err != nil {
-		return err
-	}
-	user.SetDefaults()
-	currentUsers.Add(user)
-	data, err := json.Marshal(currentUsers)
-	if err != nil {
-		return err
-	}
-
-	return database.Insert(network.NetID, string(data), database.NETWORK_USER_TABLE_NAME)
-}
-
-// DeleteNetworkUser - deletes a network user and removes from all networks
-func DeleteNetworkUser(network, userid string) error {
-	currentUsers, err := GetNetworkUsers(network)
-	if err != nil {
-		return err
-	}
-
-	currentUsers.Delete(promodels.NetworkUserID(userid))
-	data, err := json.Marshal(currentUsers)
-	if err != nil {
-		return err
-	}
-
-	return database.Insert(network, string(data), database.NETWORK_USER_TABLE_NAME)
-}
-
-// DissociateNetworkUserNode - removes a node from a given user's node list
-func DissociateNetworkUserNode(userid, networkid, nodeid string) error {
-	nuser, err := GetNetworkUser(networkid, promodels.NetworkUserID(userid))
-	if err != nil {
-		return err
-	}
-	for i, n := range nuser.Nodes {
-		if n == nodeid {
-			nuser.Nodes = removeStringIndex(nuser.Nodes, i)
-			break
-		}
-	}
-	return UpdateNetworkUser(networkid, nuser)
-}
-
-// DissociateNetworkUserClient - removes a client from a given user's client list
-func DissociateNetworkUserClient(userid, networkid, clientid string) error {
-	nuser, err := GetNetworkUser(networkid, promodels.NetworkUserID(userid))
-	if err != nil {
-		return err
-	}
-	for i, n := range nuser.Clients {
-		if n == clientid {
-			nuser.Clients = removeStringIndex(nuser.Clients, i)
-			break
-		}
-	}
-	return UpdateNetworkUser(networkid, nuser)
-}
-
-// AssociateNetworkUserClient - removes a client from a given user's client list
-func AssociateNetworkUserClient(userid, networkid, clientid string) error {
-	nuser, err := GetNetworkUser(networkid, promodels.NetworkUserID(userid))
-	if err != nil {
-		return err
-	}
-	var found bool
-	for _, n := range nuser.Clients {
-		if n == clientid {
-			found = true
-			break
-		}
-	}
-	if found {
-		return nil
-	} else {
-		nuser.Clients = append(nuser.Clients, clientid)
-	}
-
-	return UpdateNetworkUser(networkid, nuser)
-}
-
-func removeStringIndex(s []string, index int) []string {
-	ret := make([]string, 0)
-	ret = append(ret, s[:index]...)
-	return append(ret, s[index+1:]...)
-}
-
-// GetNetworkUser - fetches a network user from a given network
-func GetNetworkUser(network string, userID promodels.NetworkUserID) (*promodels.NetworkUser, error) {
-	currentUsers, err := GetNetworkUsers(network)
-	if err != nil {
-		return nil, err
-	}
-	if currentUsers[userID].ID == "" {
-		return nil, fmt.Errorf("user %s does not exist", userID)
-	}
-	currentNetUser := currentUsers[userID]
-	return &currentNetUser, nil
-}
-
-// DoesNetworkUserExist - check if networkuser exists
-func DoesNetworkUserExist(network string, userID promodels.NetworkUserID) bool {
-	_, err := GetNetworkUser(network, userID)
-	return err == nil
-}
-
-// UpdateNetworkUser - gets a network user from given network
-func UpdateNetworkUser(network string, newUser *promodels.NetworkUser) error {
-	currentUsers, err := GetNetworkUsers(network)
-	if err != nil {
-		return err
-	}
-
-	currentUsers[newUser.ID] = *newUser
-	newUsersData, err := json.Marshal(&currentUsers)
-	if err != nil {
-		return err
-	}
-
-	return database.Insert(network, string(newUsersData), database.NETWORK_USER_TABLE_NAME)
-}
-
-// RemoveAllNetworkUsers - removes all network users from given network
-func RemoveAllNetworkUsers(network string) error {
-	return database.DeleteRecord(database.NETWORK_USER_TABLE_NAME, network)
-}
-
-// IsUserNodeAllowed - given a list of nodes, determine if the user's node is allowed based on ID
-// Checks if node is in given nodes list as well as being in user's list
-func IsUserNodeAllowed(nodes []models.Node, network, userID, nodeID string) bool {
-
-	netUser, err := GetNetworkUser(network, promodels.NetworkUserID(userID))
-	if err != nil {
-		return false
-	}
-
-	for i := range nodes {
-		if nodes[i].ID.String() == nodeID {
-			for j := range netUser.Nodes {
-				if netUser.Nodes[j] == nodeID {
-					return true
-				}
-			}
-		}
-	}
-	return false
-}
-
-// IsUserClientAllowed - given a list of clients, determine if the user's client is allowed based on ID
-// Checks if client is in given ext client list as well as being in user's list
-func IsUserClientAllowed(clients []models.ExtClient, network, userID, clientID string) bool {
-
-	netUser, err := GetNetworkUser(network, promodels.NetworkUserID(userID))
-	if err != nil {
-		return false
-	}
-
-	for i := range clients {
-		if clients[i].ClientID == clientID {
-			for j := range netUser.Clients {
-				if netUser.Clients[j] == clientID {
-					return true
-				}
-			}
-		}
-	}
-	return false
-}
-
-// IsUserNetAdmin - checks if a user is a net admin or not
-func IsUserNetAdmin(network, userID string) bool {
-	user, err := GetNetworkUser(network, promodels.NetworkUserID(userID))
-	if err != nil {
-		return false
-	}
-	return user.AccessLevel == NET_ADMIN
-}
-
-// MakeNetAdmin - makes a given user a network admin on given network
-func MakeNetAdmin(network, userID string) (ok bool) {
-	user, err := GetNetworkUser(network, promodels.NetworkUserID(userID))
-	if err != nil {
-		return ok
-	}
-	user.AccessLevel = NET_ADMIN
-	if err = UpdateNetworkUser(network, user); err != nil {
-		return ok
-	}
-	return true
-}
-
-// AssignAccessLvl - gives a user a specified access level
-func AssignAccessLvl(network, userID string, accesslvl int) (ok bool) {
-	user, err := GetNetworkUser(network, promodels.NetworkUserID(userID))
-	if err != nil {
-		return ok
-	}
-	user.AccessLevel = accesslvl
-	if err = UpdateNetworkUser(network, user); err != nil {
-		return ok
-	}
-	return true
-}

+ 0 - 110
logic/pro/networkuser_test.go

@@ -1,110 +0,0 @@
-package pro
-
-import (
-	"os"
-	"testing"
-
-	"github.com/google/uuid"
-	"github.com/gravitl/netmaker/database"
-	"github.com/gravitl/netmaker/models"
-	"github.com/gravitl/netmaker/models/promodels"
-	"github.com/stretchr/testify/assert"
-)
-
-func TestMain(m *testing.M) {
-	database.InitializeDatabase()
-	defer database.CloseDB()
-	os.Exit(m.Run())
-}
-
-func TestNetworkUserLogic(t *testing.T) {
-	networkUser := promodels.NetworkUser{
-		ID: "helloworld",
-	}
-	network := models.Network{
-		NetID:        "skynet",
-		AddressRange: "192.168.0.0/24",
-	}
-	tmpCNode := models.CommonNode{
-		ID: uuid.New(),
-	}
-	tempNode := models.Node{}
-	tempNode.CommonNode = tmpCNode
-	nodes := []models.Node{
-		tempNode,
-	}
-
-	clients := []models.ExtClient{
-		{
-			ClientID: "coolclient",
-		},
-	}
-	AddProNetDefaults(&network)
-	t.Run("Net Users initialized successfully", func(t *testing.T) {
-		err := InitializeNetworkUsers(network.NetID)
-		assert.Nil(t, err)
-	})
-
-	t.Run("Error when no network users", func(t *testing.T) {
-		user, err := GetNetworkUser(network.NetID, networkUser.ID)
-		assert.Nil(t, user)
-		assert.NotNil(t, err)
-	})
-
-	t.Run("Successful net user create", func(t *testing.T) {
-		DeleteNetworkUser(network.NetID, string(networkUser.ID))
-		err := CreateNetworkUser(&network, &networkUser)
-		assert.Nil(t, err)
-		user, err := GetNetworkUser(network.NetID, networkUser.ID)
-		assert.NotNil(t, user)
-		assert.Nil(t, err)
-		assert.Equal(t, 0, user.AccessLevel)
-		assert.Equal(t, 0, user.ClientLimit)
-	})
-
-	t.Run("Successful net user update", func(t *testing.T) {
-		networkUser.AccessLevel = 0
-		networkUser.ClientLimit = 1
-		err := UpdateNetworkUser(network.NetID, &networkUser)
-		assert.Nil(t, err)
-		user, err := GetNetworkUser(network.NetID, networkUser.ID)
-		assert.NotNil(t, user)
-		assert.Nil(t, err)
-		assert.Equal(t, 0, user.AccessLevel)
-		assert.Equal(t, 1, user.ClientLimit)
-	})
-
-	t.Run("Successful net user node isallowed", func(t *testing.T) {
-		networkUser.Nodes = append(networkUser.Nodes, nodes[0].ID.String())
-		err := UpdateNetworkUser(network.NetID, &networkUser)
-		assert.Nil(t, err)
-		isUserNodeAllowed := IsUserNodeAllowed(nodes[:], network.NetID, string(networkUser.ID), nodes[0].ID.String())
-		assert.True(t, isUserNodeAllowed)
-	})
-
-	t.Run("Successful net user node not allowed", func(t *testing.T) {
-		isUserNodeAllowed := IsUserNodeAllowed(nodes[:], network.NetID, string(networkUser.ID), "notanode")
-		assert.False(t, isUserNodeAllowed)
-	})
-
-	t.Run("Successful net user client isallowed", func(t *testing.T) {
-		networkUser.Clients = append(networkUser.Clients, "coolclient")
-		err := UpdateNetworkUser(network.NetID, &networkUser)
-		assert.Nil(t, err)
-		isUserClientAllowed := IsUserClientAllowed(clients[:], network.NetID, string(networkUser.ID), "coolclient")
-		assert.True(t, isUserClientAllowed)
-	})
-
-	t.Run("Successful net user client not allowed", func(t *testing.T) {
-		isUserClientAllowed := IsUserClientAllowed(clients[:], network.NetID, string(networkUser.ID), "notaclient")
-		assert.False(t, isUserClientAllowed)
-	})
-
-	t.Run("Successful net user delete", func(t *testing.T) {
-		err := DeleteNetworkUser(network.NetID, string(networkUser.ID))
-		assert.Nil(t, err)
-		user, err := GetNetworkUser(network.NetID, networkUser.ID)
-		assert.Nil(t, user)
-		assert.NotNil(t, err)
-	})
-}

+ 0 - 35
logic/pro/proacls/nodes.go

@@ -1,35 +0,0 @@
-package proacls
-
-import (
-	"github.com/gravitl/netmaker/logic/acls"
-	"github.com/gravitl/netmaker/logic/acls/nodeacls"
-	"github.com/gravitl/netmaker/models"
-)
-
-// AdjustNodeAcls - adjusts ACLs based on a node's default value
-func AdjustNodeAcls(node *models.Node, networkNodes []models.Node) error {
-	networkID := nodeacls.NetworkID(node.Network)
-	nodeID := nodeacls.NodeID(node.ID.String())
-	currentACLs, err := nodeacls.FetchAllACLs(networkID)
-	if err != nil {
-		return err
-	}
-
-	for i := range networkNodes {
-		currentNodeID := nodeacls.NodeID(networkNodes[i].ID.String())
-		if currentNodeID == nodeID {
-			continue
-		}
-		// 2 cases
-		// both allow - allow
-		// either 1 denies - deny
-		if node.DoesACLDeny() || networkNodes[i].DoesACLDeny() {
-			currentACLs.ChangeAccess(acls.AclID(nodeID), acls.AclID(currentNodeID), acls.NotAllowed)
-		} else if node.DoesACLAllow() || networkNodes[i].DoesACLAllow() {
-			currentACLs.ChangeAccess(acls.AclID(nodeID), acls.AclID(currentNodeID), acls.Allowed)
-		}
-	}
-
-	_, err = currentACLs.Save(acls.ContainerID(node.Network))
-	return err
-}

+ 0 - 20
logic/pro/types.go

@@ -1,20 +0,0 @@
-package pro
-
-const (
-	// == NET ACCESS END == indicates access for system admin (control of netmaker)
-	// NET_ADMIN - indicates access for network admin (control of network)
-	NET_ADMIN = 0
-	// NODE_ACCESS - indicates access for
-	NODE_ACCESS = 1
-	// CLIENT_ACCESS - indicates access for network user (limited to nodes + ext clients)
-	CLIENT_ACCESS = 2
-	// NO_ACCESS - indicates user has no access to network
-	NO_ACCESS = 3
-	// == NET ACCESS END ==
-	// DEFAULT_ALLOWED_GROUPS - default user group for all networks
-	DEFAULT_ALLOWED_GROUPS = "*"
-	// DEFAULT_ALLOWED_USERS - default allowed users for a network
-	DEFAULT_ALLOWED_USERS = "*"
-	// DB_GROUPS_KEY - represents db groups
-	DB_GROUPS_KEY = "netmaker-groups"
-)

+ 0 - 80
logic/pro/usergroups.go

@@ -1,80 +0,0 @@
-package pro
-
-import (
-	"encoding/json"
-
-	"github.com/gravitl/netmaker/database"
-	"github.com/gravitl/netmaker/models/promodels"
-)
-
-// InitializeGroups - initialize groups data structure if not present in the DB
-func InitializeGroups() error {
-	if !DoesUserGroupExist(DEFAULT_ALLOWED_GROUPS) {
-		return InsertUserGroup(DEFAULT_ALLOWED_GROUPS)
-	}
-	return nil
-}
-
-// InsertUserGroup - inserts a group into the
-func InsertUserGroup(groupName promodels.UserGroupName) error {
-	currentGroups, err := GetUserGroups()
-	if err != nil {
-		return err
-	}
-	currentGroups[groupName] = promodels.Void{}
-	newData, err := json.Marshal(&currentGroups)
-	if err != nil {
-		return err
-	}
-	return database.Insert(DB_GROUPS_KEY, string(newData), database.USER_GROUPS_TABLE_NAME)
-}
-
-// DeleteUserGroup - deletes a group from database
-func DeleteUserGroup(groupName promodels.UserGroupName) error {
-	var newGroups promodels.UserGroups
-	currentGroupRecords, err := database.FetchRecord(database.USER_GROUPS_TABLE_NAME, DB_GROUPS_KEY)
-	if err != nil && !database.IsEmptyRecord(err) {
-		return err
-	}
-	if err = json.Unmarshal([]byte(currentGroupRecords), &newGroups); err != nil {
-		return err
-	}
-	delete(newGroups, groupName)
-	newData, err := json.Marshal(&newGroups)
-	if err != nil {
-		return err
-	}
-	return database.Insert(DB_GROUPS_KEY, string(newData), database.USER_GROUPS_TABLE_NAME)
-}
-
-// GetUserGroups - get groups of users
-func GetUserGroups() (promodels.UserGroups, error) {
-	var returnGroups promodels.UserGroups
-	groupsRecord, err := database.FetchRecord(database.USER_GROUPS_TABLE_NAME, DB_GROUPS_KEY)
-	if err != nil {
-		if database.IsEmptyRecord(err) {
-			return make(promodels.UserGroups, 1), nil
-		}
-		return returnGroups, err
-	}
-
-	if err = json.Unmarshal([]byte(groupsRecord), &returnGroups); err != nil {
-		return returnGroups, err
-	}
-
-	return returnGroups, nil
-}
-
-// DoesUserGroupExist - checks if a user group exists
-func DoesUserGroupExist(group promodels.UserGroupName) bool {
-	currentGroups, err := GetUserGroups()
-	if err != nil {
-		return true
-	}
-	for k := range currentGroups {
-		if k == group {
-			return true
-		}
-	}
-	return false
-}

+ 0 - 41
logic/pro/usergroups_test.go

@@ -1,41 +0,0 @@
-package pro
-
-import (
-	"testing"
-
-	"github.com/gravitl/netmaker/models/promodels"
-	"github.com/stretchr/testify/assert"
-)
-
-func TestUserGroupLogic(t *testing.T) {
-
-	t.Run("User Groups initialized successfully", func(t *testing.T) {
-		err := InitializeGroups()
-		assert.Nil(t, err)
-	})
-
-	t.Run("Check for default group", func(t *testing.T) {
-		groups, err := GetUserGroups()
-		assert.Nil(t, err)
-		var hasdefault bool
-		for k := range groups {
-			if string(k) == DEFAULT_ALLOWED_GROUPS {
-				hasdefault = true
-			}
-		}
-		assert.True(t, hasdefault)
-	})
-
-	t.Run("User Groups created successfully", func(t *testing.T) {
-		err := InsertUserGroup(promodels.UserGroupName("group1"))
-		assert.Nil(t, err)
-		err = InsertUserGroup(promodels.UserGroupName("group2"))
-		assert.Nil(t, err)
-	})
-
-	t.Run("User Groups deleted successfully", func(t *testing.T) {
-		err := DeleteUserGroup(promodels.UserGroupName("group1"))
-		assert.Nil(t, err)
-		assert.False(t, DoesUserGroupExist(promodels.UserGroupName("group1")))
-	})
-}

+ 10 - 134
logic/relay.go

@@ -1,149 +1,25 @@
 package logic
 package logic
 
 
 import (
 import (
-	"errors"
-	"fmt"
-	"net"
-
-	"github.com/gravitl/netmaker/logger"
 	"github.com/gravitl/netmaker/models"
 	"github.com/gravitl/netmaker/models"
+	"net"
 )
 )
 
 
-// CreateRelay - creates a relay
-func CreateRelay(relay models.RelayRequest) ([]models.Node, models.Node, error) {
-	var returnnodes []models.Node
-
-	node, err := GetNodeByID(relay.NodeID)
-	if err != nil {
-		return returnnodes, models.Node{}, err
-	}
-	host, err := GetHost(node.HostID.String())
-	if err != nil {
-		return returnnodes, models.Node{}, err
-	}
-	if host.OS != "linux" {
-		return returnnodes, models.Node{}, fmt.Errorf("only linux machines can be relay nodes")
-	}
-	err = ValidateRelay(relay)
-	if err != nil {
-		return returnnodes, models.Node{}, err
-	}
-	node.IsRelay = true
-	node.RelayedNodes = relay.RelayedNodes
-	node.SetLastModified()
-	err = UpsertNode(&node)
-	if err != nil {
-		return returnnodes, node, err
-	}
-	returnnodes = SetRelayedNodes(true, relay.NodeID, relay.RelayedNodes)
-	return returnnodes, node, nil
+var RelayedAllowedIPs = func(peer, node *models.Node) []net.IPNet {
+	return []net.IPNet{}
 }
 }
 
 
-// SetRelayedNodes- sets and saves node as relayed
-func SetRelayedNodes(setRelayed bool, relay string, relayed []string) []models.Node {
-	var returnnodes []models.Node
-	for _, id := range relayed {
-		node, err := GetNodeByID(id)
-		if err != nil {
-			logger.Log(0, "setRelayedNodes.GetNodebyID", err.Error())
-			continue
-		}
-		node.IsRelayed = setRelayed
-		if node.IsRelayed {
-			node.RelayedBy = relay
-		} else {
-			node.RelayedBy = ""
-		}
-		node.SetLastModified()
-		if err := UpsertNode(&node); err != nil {
-			logger.Log(0, "setRelayedNodes.Insert", err.Error())
-			continue
-		}
-		returnnodes = append(returnnodes, node)
-	}
-	return returnnodes
-}
-
-//func GetRelayedNodes(relayNode *models.Node) (models.Node, error) {
-//	var returnnodes []models.Node
-//	networkNodes, err := GetNetworkNodes(relayNode.Network)
-//	if err != nil {
-//		return returnnodes, err
-//	}
-//	for _, node := range networkNodes {
-//		for _, addr := range relayNode.RelayAddrs {
-//			if addr == node.Address.IP.String() || addr == node.Address6.IP.String() {
-//				returnnodes = append(returnnodes, node)
-//			}
-//		}
-//	}
-//	return returnnodes, nil
-//}
-
-// ValidateRelay - checks if relay is valid
-func ValidateRelay(relay models.RelayRequest) error {
-	var err error
-	//isIp := functions.IsIpCIDR(gateway.RangeString)
-	empty := len(relay.RelayedNodes) == 0
-	if empty {
-		return errors.New("IP Ranges Cannot Be Empty")
-	}
-	node, err := GetNodeByID(relay.NodeID)
-	if err != nil {
-		return err
-	}
-	if node.IsRelay {
-		return errors.New("node is already acting as a relay")
-	}
-	for _, relayedNodeID := range relay.RelayedNodes {
-		relayedNode, err := GetNodeByID(relayedNodeID)
-		if err != nil {
-			return err
-		}
-		if relayedNode.IsIngressGateway {
-			return errors.New("cannot relay an ingress gateway (" + relayedNodeID + ")")
-		}
-	}
-	return err
+var GetAllowedIpsForRelayed = func(relayed, relay *models.Node) []net.IPNet {
+	return []net.IPNet{}
 }
 }
 
 
-// UpdateRelayed - updates relay nodes
-func UpdateRelayed(relay string, oldNodes []string, newNodes []string) []models.Node {
-	_ = SetRelayedNodes(false, relay, oldNodes)
-	return SetRelayedNodes(true, relay, newNodes)
+var UpdateRelayed = func(currentNode, newNode *models.Node) {
 }
 }
 
 
-// DeleteRelay - deletes a relay
-func DeleteRelay(network, nodeid string) ([]models.Node, models.Node, error) {
-	var returnnodes []models.Node
-	node, err := GetNodeByID(nodeid)
-	if err != nil {
-		return returnnodes, models.Node{}, err
-	}
-	returnnodes = SetRelayedNodes(false, nodeid, node.RelayedNodes)
-	node.IsRelay = false
-	node.RelayedNodes = []string{}
-	node.SetLastModified()
-	if err = UpsertNode(&node); err != nil {
-		return returnnodes, models.Node{}, err
-	}
-	return returnnodes, node, nil
+var SetRelayedNodes = func(setRelayed bool, relay string, relayed []string) []models.Node {
+	return []models.Node{}
 }
 }
 
 
-func getRelayedAddresses(id string) []net.IPNet {
-	addrs := []net.IPNet{}
-	node, err := GetNodeByID(id)
-	if err != nil {
-		logger.Log(0, "getRelayedAddresses: "+err.Error())
-		return addrs
-	}
-	if node.Address.IP != nil {
-		node.Address.Mask = net.CIDRMask(32, 32)
-		addrs = append(addrs, node.Address)
-	}
-	if node.Address6.IP != nil {
-		node.Address.Mask = net.CIDRMask(128, 128)
-		addrs = append(addrs, node.Address6)
-	}
-	return addrs
+var RelayUpdates = func(currentNode, newNode *models.Node) bool {
+	return false
 }
 }

+ 13 - 143
logic/security.go

@@ -1,23 +1,16 @@
 package logic
 package logic
 
 
 import (
 import (
-	"encoding/json"
 	"net/http"
 	"net/http"
 	"strings"
 	"strings"
 
 
 	"github.com/gorilla/mux"
 	"github.com/gorilla/mux"
-	"github.com/gravitl/netmaker/database"
-	"github.com/gravitl/netmaker/logic/pro"
 	"github.com/gravitl/netmaker/models"
 	"github.com/gravitl/netmaker/models"
-	"github.com/gravitl/netmaker/models/promodels"
 	"github.com/gravitl/netmaker/servercfg"
 	"github.com/gravitl/netmaker/servercfg"
 )
 )
 
 
 const (
 const (
-	// ALL_NETWORK_ACCESS - represents all networks
-	ALL_NETWORK_ACCESS = "THIS_USER_HAS_ALL"
-
-	master_uname     = "masteradministrator"
+	MasterUser       = "masteradministrator"
 	Forbidden_Msg    = "forbidden"
 	Forbidden_Msg    = "forbidden"
 	Forbidden_Err    = models.Error(Forbidden_Msg)
 	Forbidden_Err    = models.Error(Forbidden_Msg)
 	Unauthorized_Msg = "unauthorized"
 	Unauthorized_Msg = "unauthorized"
@@ -28,152 +21,46 @@ const (
 func SecurityCheck(reqAdmin bool, next http.Handler) http.HandlerFunc {
 func SecurityCheck(reqAdmin bool, next http.Handler) http.HandlerFunc {
 
 
 	return func(w http.ResponseWriter, r *http.Request) {
 	return func(w http.ResponseWriter, r *http.Request) {
-		var errorResponse = models.ErrorResponse{
-			Code: http.StatusForbidden, Message: Forbidden_Msg,
-		}
 		r.Header.Set("ismaster", "no")
 		r.Header.Set("ismaster", "no")
-
-		var params = mux.Vars(r)
 		bearerToken := r.Header.Get("Authorization")
 		bearerToken := r.Header.Get("Authorization")
-		// to have a custom DNS service adding entries
-		// we should refactor this, but is for the special case of an external service to query the DNS api
-		if strings.Contains(r.RequestURI, "/dns") && strings.ToUpper(r.Method) == "GET" && authenticateDNSToken(bearerToken) {
-			// do dns stuff
-			r.Header.Set("user", "nameserver")
-			networks, _ := json.Marshal([]string{ALL_NETWORK_ACCESS})
-			r.Header.Set("networks", string(networks))
-			next.ServeHTTP(w, r)
-			return
-		}
-		var networkName = params["networkname"]
-		if len(networkName) == 0 {
-			networkName = params["network"]
-		}
-		networks, username, err := UserPermissions(reqAdmin, networkName, bearerToken)
+		username, err := UserPermissions(reqAdmin, bearerToken)
 		if err != nil {
 		if err != nil {
-			ReturnErrorResponse(w, r, errorResponse)
+			ReturnErrorResponse(w, r, FormatError(err, err.Error()))
 			return
 			return
 		}
 		}
 		// detect masteradmin
 		// detect masteradmin
-		if len(networks) > 0 && networks[0] == ALL_NETWORK_ACCESS {
+		if username == MasterUser {
 			r.Header.Set("ismaster", "yes")
 			r.Header.Set("ismaster", "yes")
 		}
 		}
-		networksJson, err := json.Marshal(&networks)
-		if err != nil {
-			ReturnErrorResponse(w, r, errorResponse)
-			return
-		}
 		r.Header.Set("user", username)
 		r.Header.Set("user", username)
-		r.Header.Set("networks", string(networksJson))
-		next.ServeHTTP(w, r)
-	}
-}
-
-// NetUserSecurityCheck - Check if network user has appropriate permissions
-func NetUserSecurityCheck(isNodes, isClients bool, next http.Handler) http.HandlerFunc {
-	return func(w http.ResponseWriter, r *http.Request) {
-		var errorResponse = models.ErrorResponse{
-			Code: http.StatusForbidden, Message: Forbidden_Msg,
-		}
-		r.Header.Set("ismaster", "no")
-
-		var params = mux.Vars(r)
-		var netUserName = params["networkuser"]
-		var network = params["network"]
-
-		bearerToken := r.Header.Get("Authorization")
-
-		var tokenSplit = strings.Split(bearerToken, " ")
-		var authToken = ""
-
-		if len(tokenSplit) < 2 {
-			ReturnErrorResponse(w, r, errorResponse)
-			return
-		} else {
-			authToken = tokenSplit[1]
-		}
-
-		isMasterAuthenticated := authenticateMaster(authToken)
-		if isMasterAuthenticated {
-			r.Header.Set("user", "master token user")
-			r.Header.Set("ismaster", "yes")
-			next.ServeHTTP(w, r)
-			return
-		}
-
-		userName, _, isadmin, err := VerifyUserToken(authToken)
-		if err != nil {
-			ReturnErrorResponse(w, r, errorResponse)
-			return
-		}
-		r.Header.Set("user", userName)
-
-		if isadmin {
-			next.ServeHTTP(w, r)
-			return
-		}
-
-		if isNodes || isClients {
-			necessaryAccess := pro.NET_ADMIN
-			if isClients {
-				necessaryAccess = pro.CLIENT_ACCESS
-			}
-			if isNodes {
-				necessaryAccess = pro.NODE_ACCESS
-			}
-			u, err := pro.GetNetworkUser(network, promodels.NetworkUserID(userName))
-			if err != nil {
-				ReturnErrorResponse(w, r, errorResponse)
-				return
-			}
-			if u.AccessLevel > necessaryAccess {
-				ReturnErrorResponse(w, r, errorResponse)
-				return
-			}
-		} else if netUserName != userName {
-			ReturnErrorResponse(w, r, errorResponse)
-			return
-		}
-
 		next.ServeHTTP(w, r)
 		next.ServeHTTP(w, r)
 	}
 	}
 }
 }
 
 
 // UserPermissions - checks token stuff
 // UserPermissions - checks token stuff
-func UserPermissions(reqAdmin bool, netname string, token string) ([]string, string, error) {
+func UserPermissions(reqAdmin bool, token string) (string, error) {
 	var tokenSplit = strings.Split(token, " ")
 	var tokenSplit = strings.Split(token, " ")
 	var authToken = ""
 	var authToken = ""
-	userNetworks := []string{}
 
 
 	if len(tokenSplit) < 2 {
 	if len(tokenSplit) < 2 {
-		return userNetworks, "", Unauthorized_Err
+		return "", Unauthorized_Err
 	} else {
 	} else {
 		authToken = tokenSplit[1]
 		authToken = tokenSplit[1]
 	}
 	}
 	//all endpoints here require master so not as complicated
 	//all endpoints here require master so not as complicated
 	if authenticateMaster(authToken) {
 	if authenticateMaster(authToken) {
 		// TODO log in as an actual admin user
 		// TODO log in as an actual admin user
-		return []string{ALL_NETWORK_ACCESS}, master_uname, nil
+		return MasterUser, nil
 	}
 	}
-	username, networks, isadmin, err := VerifyUserToken(authToken)
+	username, issuperadmin, isadmin, err := VerifyUserToken(authToken)
 	if err != nil {
 	if err != nil {
-		return nil, username, Unauthorized_Err
+		return username, Unauthorized_Err
 	}
 	}
-	if !isadmin && reqAdmin {
-		return nil, username, Forbidden_Err
+	if reqAdmin && !(issuperadmin || isadmin) {
+		return username, Forbidden_Err
 	}
 	}
-	userNetworks = networks
-	if isadmin {
-		return []string{ALL_NETWORK_ACCESS}, username, nil
-	}
-	// check network admin access
-	if len(netname) > 0 && (len(userNetworks) == 0 || !authenticateNetworkUser(netname, userNetworks)) {
-		return nil, username, Forbidden_Err
-	}
-	if isEE && len(netname) > 0 && !pro.IsUserNetAdmin(netname, username) {
-		return nil, "", Forbidden_Err
-	}
-	return userNetworks, username, nil
+
+	return username, nil
 }
 }
 
 
 // Consider a more secure way of setting master key
 // Consider a more secure way of setting master key
@@ -181,23 +68,6 @@ func authenticateMaster(tokenString string) bool {
 	return tokenString == servercfg.GetMasterKey() && servercfg.GetMasterKey() != ""
 	return tokenString == servercfg.GetMasterKey() && servercfg.GetMasterKey() != ""
 }
 }
 
 
-func authenticateNetworkUser(network string, userNetworks []string) bool {
-	networkexists, err := NetworkExists(network)
-	if (err != nil && !database.IsEmptyRecord(err)) || !networkexists {
-		return false
-	}
-	return StringSliceContains(userNetworks, network)
-}
-
-// Consider a more secure way of setting master key
-func authenticateDNSToken(tokenString string) bool {
-	tokens := strings.Split(tokenString, " ")
-	if len(tokens) < 2 {
-		return false
-	}
-	return len(servercfg.GetDNSKey()) > 0 && tokens[1] == servercfg.GetDNSKey()
-}
-
 func ContinueIfUserMatch(next http.Handler) http.HandlerFunc {
 func ContinueIfUserMatch(next http.Handler) http.HandlerFunc {
 	return func(w http.ResponseWriter, r *http.Request) {
 	return func(w http.ResponseWriter, r *http.Request) {
 		var errorResponse = models.ErrorResponse{
 		var errorResponse = models.ErrorResponse{

+ 4 - 10
logic/telemetry.go

@@ -13,7 +13,6 @@ import (
 
 
 // flags to keep for telemetry
 // flags to keep for telemetry
 var isFreeTier bool
 var isFreeTier bool
-var isEE bool
 
 
 // posthog_pub_key - Key for sending data to PostHog
 // posthog_pub_key - Key for sending data to PostHog
 const posthog_pub_key = "phc_1vEXhPOA1P7HP5jP2dVU9xDTUqXHAelmtravyZ1vvES"
 const posthog_pub_key = "phc_1vEXhPOA1P7HP5jP2dVU9xDTUqXHAelmtravyZ1vvES"
@@ -21,14 +20,8 @@ const posthog_pub_key = "phc_1vEXhPOA1P7HP5jP2dVU9xDTUqXHAelmtravyZ1vvES"
 // posthog_endpoint - Endpoint of PostHog server
 // posthog_endpoint - Endpoint of PostHog server
 const posthog_endpoint = "https://app.posthog.com"
 const posthog_endpoint = "https://app.posthog.com"
 
 
-// setEEForTelemetry - store EE flag without having an import cycle when used for telemetry
-// (as the ee package needs the logic package as currently written).
-func SetEEForTelemetry(eeFlag bool) {
-	isEE = eeFlag
-}
-
 // setFreeTierForTelemetry - store free tier flag without having an import cycle when used for telemetry
 // setFreeTierForTelemetry - store free tier flag without having an import cycle when used for telemetry
-// (as the ee package needs the logic package as currently written).
+// (as the pro package needs the logic package as currently written).
 func SetFreeTierForTelemetry(freeTierFlag bool) {
 func SetFreeTierForTelemetry(freeTierFlag bool) {
 	isFreeTier = freeTierFlag
 	isFreeTier = freeTierFlag
 }
 }
@@ -73,7 +66,7 @@ func sendTelemetry() error {
 			Set("docker", d.Count.Docker).
 			Set("docker", d.Count.Docker).
 			Set("k8s", d.Count.K8S).
 			Set("k8s", d.Count.K8S).
 			Set("version", d.Version).
 			Set("version", d.Version).
-			Set("is_ee", isEE).
+			Set("is_ee", d.IsPro). // TODO change is_ee to is_pro for consistency, but probably needs changes in posthog
 			Set("is_free_tier", isFreeTier),
 			Set("is_free_tier", isFreeTier),
 	})
 	})
 }
 }
@@ -82,6 +75,7 @@ func sendTelemetry() error {
 func fetchTelemetryData() (telemetryData, error) {
 func fetchTelemetryData() (telemetryData, error) {
 	var data telemetryData
 	var data telemetryData
 
 
+	data.IsPro = servercfg.IsPro
 	data.ExtClients = getDBLength(database.EXT_CLIENT_TABLE_NAME)
 	data.ExtClients = getDBLength(database.EXT_CLIENT_TABLE_NAME)
 	data.Users = getDBLength(database.USERS_TABLE_NAME)
 	data.Users = getDBLength(database.USERS_TABLE_NAME)
 	data.Networks = getDBLength(database.NETWORKS_TABLE_NAME)
 	data.Networks = getDBLength(database.NETWORKS_TABLE_NAME)
@@ -176,7 +170,7 @@ type telemetryData struct {
 	Networks   int
 	Networks   int
 	Servers    int
 	Servers    int
 	Version    string
 	Version    string
-	IsEE       bool
+	IsPro      bool
 	IsFreeTier bool
 	IsFreeTier bool
 }
 }
 
 

+ 21 - 56
logic/users.go

@@ -2,13 +2,11 @@ package logic
 
 
 import (
 import (
 	"encoding/json"
 	"encoding/json"
+	"errors"
 	"sort"
 	"sort"
 
 
 	"github.com/gravitl/netmaker/database"
 	"github.com/gravitl/netmaker/database"
-	"github.com/gravitl/netmaker/logger"
-	"github.com/gravitl/netmaker/logic/pro"
 	"github.com/gravitl/netmaker/models"
 	"github.com/gravitl/netmaker/models"
-	"github.com/gravitl/netmaker/models/promodels"
 )
 )
 
 
 // GetUser - gets a user
 // GetUser - gets a user
@@ -43,64 +41,17 @@ func GetReturnUser(username string) (models.ReturnUser, error) {
 // ToReturnUser - gets a user as a return user
 // ToReturnUser - gets a user as a return user
 func ToReturnUser(user models.User) models.ReturnUser {
 func ToReturnUser(user models.User) models.ReturnUser {
 	return models.ReturnUser{
 	return models.ReturnUser{
-		UserName: user.UserName,
-		Networks: user.Networks,
-		IsAdmin:  user.IsAdmin,
-		Groups:   user.Groups,
+		UserName:     user.UserName,
+		IsSuperAdmin: user.IsSuperAdmin,
+		IsAdmin:      user.IsAdmin,
+		RemoteGwIDs:  user.RemoteGwIDs,
 	}
 	}
 }
 }
 
 
-// GetGroupUsers - gets users in a group
-func GetGroupUsers(group string) ([]models.ReturnUser, error) {
-	var returnUsers []models.ReturnUser
-	users, err := GetUsers()
-	if err != nil {
-		return returnUsers, err
-	}
-	for _, user := range users {
-		if StringSliceContains(user.Groups, group) {
-			users = append(users, user)
-		}
-	}
-	return users, err
-}
-
-// == PRO ==
-
-// InitializeNetUsers - intializes network users for all users/networks
-func InitializeNetUsers(network *models.Network) error {
-	// == add all current users to network as network users ==
-	currentUsers, err := GetUsers()
-	if err != nil {
-		return err
-	}
-
-	for i := range currentUsers { // add all users to given network
-		newUser := promodels.NetworkUser{
-			ID:          promodels.NetworkUserID(currentUsers[i].UserName),
-			Clients:     []string{},
-			Nodes:       []string{},
-			AccessLevel: pro.NO_ACCESS,
-			ClientLimit: 0,
-			NodeLimit:   0,
-		}
-		if pro.IsUserAllowed(network, currentUsers[i].UserName, currentUsers[i].Groups) {
-			newUser.AccessLevel = network.ProSettings.DefaultAccessLevel
-			newUser.ClientLimit = network.ProSettings.DefaultUserClientLimit
-			newUser.NodeLimit = network.ProSettings.DefaultUserNodeLimit
-		}
-
-		if err = pro.CreateNetworkUser(network, &newUser); err != nil {
-			logger.Log(0, "failed to add network user settings to user", string(newUser.ID), "on network", network.NetID)
-		}
-	}
-	return nil
-}
-
 // SetUserDefaults - sets the defaults of a user to avoid empty fields
 // SetUserDefaults - sets the defaults of a user to avoid empty fields
 func SetUserDefaults(user *models.User) {
 func SetUserDefaults(user *models.User) {
-	if user.Groups == nil {
-		user.Groups = []string{pro.DEFAULT_ALLOWED_GROUPS}
+	if user.RemoteGwIDs == nil {
+		user.RemoteGwIDs = make(map[string]struct{})
 	}
 	}
 }
 }
 
 
@@ -110,3 +61,17 @@ func SortUsers(unsortedUsers []models.ReturnUser) {
 		return unsortedUsers[i].UserName < unsortedUsers[j].UserName
 		return unsortedUsers[i].UserName < unsortedUsers[j].UserName
 	})
 	})
 }
 }
+
+// GetSuperAdmin - fetches superadmin user
+func GetSuperAdmin() (models.ReturnUser, error) {
+	users, err := GetUsers()
+	if err != nil {
+		return models.ReturnUser{}, err
+	}
+	for _, user := range users {
+		if user.IsSuperAdmin {
+			return user, nil
+		}
+	}
+	return models.ReturnUser{}, errors.New("superadmin not found")
+}

+ 2 - 7
main.go

@@ -19,7 +19,6 @@ import (
 	"github.com/gravitl/netmaker/functions"
 	"github.com/gravitl/netmaker/functions"
 	"github.com/gravitl/netmaker/logger"
 	"github.com/gravitl/netmaker/logger"
 	"github.com/gravitl/netmaker/logic"
 	"github.com/gravitl/netmaker/logic"
-	"github.com/gravitl/netmaker/logic/pro"
 	"github.com/gravitl/netmaker/migrate"
 	"github.com/gravitl/netmaker/migrate"
 	"github.com/gravitl/netmaker/models"
 	"github.com/gravitl/netmaker/models"
 	"github.com/gravitl/netmaker/mq"
 	"github.com/gravitl/netmaker/mq"
@@ -29,7 +28,7 @@ import (
 	"golang.org/x/exp/slog"
 	"golang.org/x/exp/slog"
 )
 )
 
 
-var version = "v0.20.6"
+var version = "v0.21.0"
 
 
 // Start DB Connection and start API Request Handler
 // Start DB Connection and start API Request Handler
 func main() {
 func main() {
@@ -41,7 +40,7 @@ func main() {
 	initialize()                       // initial db and acls
 	initialize()                       // initial db and acls
 	setGarbageCollection()
 	setGarbageCollection()
 	setVerbosity()
 	setVerbosity()
-	if servercfg.DeployedByOperator() && !servercfg.Is_EE {
+	if servercfg.DeployedByOperator() && !servercfg.IsPro {
 		logic.SetFreeTierLimits()
 		logic.SetFreeTierLimits()
 	}
 	}
 	defer database.CloseDB()
 	defer database.CloseDB()
@@ -83,10 +82,6 @@ func initialize() { // Client Mode Prereq Check
 
 
 	logic.SetJWTSecret()
 	logic.SetJWTSecret()
 
 
-	if err = pro.InitializeGroups(); err != nil {
-		logger.Log(0, "could not initialize default user group, \"*\"")
-	}
-
 	err = logic.TimerCheckpoint()
 	err = logic.TimerCheckpoint()
 	if err != nil {
 	if err != nil {
 		logger.Log(1, "Timer error occurred: ", err.Error())
 		logger.Log(1, "Timer error occurred: ", err.Error())

+ 2 - 4
main_ee.go

@@ -3,10 +3,8 @@
 
 
 package main
 package main
 
 
-import (
-	"github.com/gravitl/netmaker/ee"
-)
+import "github.com/gravitl/netmaker/pro"
 
 
 func init() {
 func init() {
-	ee.InitEE()
+	pro.InitPro()
 }
 }

+ 39 - 0
migrate/migrate.go

@@ -5,12 +5,51 @@ import (
 
 
 	"github.com/gravitl/netmaker/database"
 	"github.com/gravitl/netmaker/database"
 	"github.com/gravitl/netmaker/logger"
 	"github.com/gravitl/netmaker/logger"
+	"github.com/gravitl/netmaker/logic"
 	"github.com/gravitl/netmaker/models"
 	"github.com/gravitl/netmaker/models"
+	"golang.org/x/exp/slog"
 )
 )
 
 
 // Run - runs all migrations
 // Run - runs all migrations
 func Run() {
 func Run() {
 	updateEnrollmentKeys()
 	updateEnrollmentKeys()
+	assignSuperAdmin()
+}
+
+func assignSuperAdmin() {
+	users, err := logic.GetUsers()
+	if err != nil || len(users) == 0 {
+		return
+	}
+
+	if ok, _ := logic.HasSuperAdmin(); ok {
+		return
+	}
+	createdSuperAdmin := false
+	for _, u := range users {
+		if u.IsAdmin {
+			user, err := logic.GetUser(u.UserName)
+			if err != nil {
+				slog.Error("error getting user", "user", u.UserName, "error", err.Error())
+				continue
+			}
+			user.IsSuperAdmin = true
+			user.IsAdmin = false
+			err = logic.UpsertUser(*user)
+			if err != nil {
+				slog.Error("error updating user to superadmin", "user", user.UserName, "error", err.Error())
+				continue
+			} else {
+				createdSuperAdmin = true
+			}
+			break
+		}
+	}
+
+	if !createdSuperAdmin {
+		slog.Error("failed to create superadmin!!")
+	}
+
 }
 }
 
 
 func updateEnrollmentKeys() {
 func updateEnrollmentKeys() {

+ 4 - 4
models/dnsEntry.go

@@ -42,8 +42,8 @@ type DNSUpdate struct {
 
 
 // DNSEntry - a DNS entry represented as struct
 // DNSEntry - a DNS entry represented as struct
 type DNSEntry struct {
 type DNSEntry struct {
-	Address  string `json:"address" bson:"address" validate:"ip"`
-	Address6 string `json:"address6" bson:"address6"`
-	Name     string `json:"name" bson:"name" validate:"required,name_unique,min=1,max=192"`
-	Network  string `json:"network" bson:"network" validate:"network_exists"`
+	Address  string `json:"address" validate:"ip"`
+	Address6 string `json:"address6"`
+	Name     string `json:"name" validate:"required,name_unique,min=1,max=192,whitespace"`
+	Network  string `json:"network" validate:"network_exists"`
 }
 }

+ 8 - 6
models/extclient.go

@@ -16,14 +16,16 @@ type ExtClient struct {
 	Enabled                bool                `json:"enabled" bson:"enabled"`
 	Enabled                bool                `json:"enabled" bson:"enabled"`
 	OwnerID                string              `json:"ownerid" bson:"ownerid"`
 	OwnerID                string              `json:"ownerid" bson:"ownerid"`
 	DeniedACLs             map[string]struct{} `json:"deniednodeacls" bson:"acls,omitempty"`
 	DeniedACLs             map[string]struct{} `json:"deniednodeacls" bson:"acls,omitempty"`
+	RemoteAccessClientID   string              `json:"remote_access_client_id"`
 }
 }
 
 
 // CustomExtClient - struct for CustomExtClient params
 // CustomExtClient - struct for CustomExtClient params
 type CustomExtClient struct {
 type CustomExtClient struct {
-	ClientID        string              `json:"clientid,omitempty"`
-	PublicKey       string              `json:"publickey,omitempty"`
-	DNS             string              `json:"dns,omitempty"`
-	ExtraAllowedIPs []string            `json:"extraallowedips,omitempty"`
-	Enabled         bool                `json:"enabled,omitempty"`
-	DeniedACLs      map[string]struct{} `json:"deniednodeacls" bson:"acls,omitempty"`
+	ClientID             string              `json:"clientid,omitempty"`
+	PublicKey            string              `json:"publickey,omitempty"`
+	DNS                  string              `json:"dns,omitempty"`
+	ExtraAllowedIPs      []string            `json:"extraallowedips,omitempty"`
+	Enabled              bool                `json:"enabled,omitempty"`
+	DeniedACLs           map[string]struct{} `json:"deniednodeacls" bson:"acls,omitempty"`
+	RemoteAccessClientID string              `json:"remote_access_client_id"`
 }
 }

+ 1 - 0
models/metrics.go

@@ -44,6 +44,7 @@ type HostInfoMap map[string]HostNetworkInfo
 type HostNetworkInfo struct {
 type HostNetworkInfo struct {
 	Interfaces []Iface `json:"interfaces" yaml:"interfaces"`
 	Interfaces []Iface `json:"interfaces" yaml:"interfaces"`
 	ListenPort int     `json:"listen_port" yaml:"listen_port"`
 	ListenPort int     `json:"listen_port" yaml:"listen_port"`
+	IsStatic   bool    `json:"is_static"`
 }
 }
 
 
 // PeerMap - peer map for ids and addresses in metrics
 // PeerMap - peer map for ids and addresses in metrics

+ 16 - 19
models/network.go

@@ -2,30 +2,27 @@ package models
 
 
 import (
 import (
 	"time"
 	"time"
-
-	"github.com/gravitl/netmaker/models/promodels"
 )
 )
 
 
 // Network Struct - contains info for a given unique network
 // Network Struct - contains info for a given unique network
 // At  some point, need to replace all instances of Name with something else like  Identifier
 // At  some point, need to replace all instances of Name with something else like  Identifier
 type Network struct {
 type Network struct {
-	AddressRange        string                `json:"addressrange" bson:"addressrange" validate:"omitempty,cidrv4"`
-	AddressRange6       string                `json:"addressrange6" bson:"addressrange6" validate:"omitempty,cidrv6"`
-	NetID               string                `json:"netid" bson:"netid" validate:"required,min=1,max=32,netid_valid"`
-	NodesLastModified   int64                 `json:"nodeslastmodified" bson:"nodeslastmodified"`
-	NetworkLastModified int64                 `json:"networklastmodified" bson:"networklastmodified"`
-	DefaultInterface    string                `json:"defaultinterface" bson:"defaultinterface" validate:"min=1,max=35"`
-	DefaultListenPort   int32                 `json:"defaultlistenport,omitempty" bson:"defaultlistenport,omitempty" validate:"omitempty,min=1024,max=65535"`
-	NodeLimit           int32                 `json:"nodelimit" bson:"nodelimit"`
-	DefaultPostDown     string                `json:"defaultpostdown" bson:"defaultpostdown"`
-	DefaultKeepalive    int32                 `json:"defaultkeepalive" bson:"defaultkeepalive" validate:"omitempty,max=1000"`
-	AllowManualSignUp   string                `json:"allowmanualsignup" bson:"allowmanualsignup" validate:"checkyesorno"`
-	IsIPv4              string                `json:"isipv4" bson:"isipv4" validate:"checkyesorno"`
-	IsIPv6              string                `json:"isipv6" bson:"isipv6" validate:"checkyesorno"`
-	DefaultUDPHolePunch string                `json:"defaultudpholepunch" bson:"defaultudpholepunch" validate:"checkyesorno"`
-	DefaultMTU          int32                 `json:"defaultmtu" bson:"defaultmtu"`
-	DefaultACL          string                `json:"defaultacl" bson:"defaultacl" yaml:"defaultacl" validate:"checkyesorno"`
-	ProSettings         *promodels.ProNetwork `json:"prosettings,omitempty" bson:"prosettings,omitempty" yaml:"prosettings,omitempty"`
+	AddressRange        string `json:"addressrange" bson:"addressrange" validate:"omitempty,cidrv4"`
+	AddressRange6       string `json:"addressrange6" bson:"addressrange6" validate:"omitempty,cidrv6"`
+	NetID               string `json:"netid" bson:"netid" validate:"required,min=1,max=32,netid_valid"`
+	NodesLastModified   int64  `json:"nodeslastmodified" bson:"nodeslastmodified"`
+	NetworkLastModified int64  `json:"networklastmodified" bson:"networklastmodified"`
+	DefaultInterface    string `json:"defaultinterface" bson:"defaultinterface" validate:"min=1,max=35"`
+	DefaultListenPort   int32  `json:"defaultlistenport,omitempty" bson:"defaultlistenport,omitempty" validate:"omitempty,min=1024,max=65535"`
+	NodeLimit           int32  `json:"nodelimit" bson:"nodelimit"`
+	DefaultPostDown     string `json:"defaultpostdown" bson:"defaultpostdown"`
+	DefaultKeepalive    int32  `json:"defaultkeepalive" bson:"defaultkeepalive" validate:"omitempty,max=1000"`
+	AllowManualSignUp   string `json:"allowmanualsignup" bson:"allowmanualsignup" validate:"checkyesorno"`
+	IsIPv4              string `json:"isipv4" bson:"isipv4" validate:"checkyesorno"`
+	IsIPv6              string `json:"isipv6" bson:"isipv6" validate:"checkyesorno"`
+	DefaultUDPHolePunch string `json:"defaultudpholepunch" bson:"defaultudpholepunch" validate:"checkyesorno"`
+	DefaultMTU          int32  `json:"defaultmtu" bson:"defaultmtu"`
+	DefaultACL          string `json:"defaultacl" bson:"defaultacl" yaml:"defaultacl" validate:"checkyesorno"`
 }
 }
 
 
 // SaveData - sensitive fields of a network that should be kept the same
 // SaveData - sensitive fields of a network that should be kept the same

+ 3 - 3
models/node.go

@@ -357,7 +357,7 @@ func (node *LegacyNode) SetDefaultFailover() {
 }
 }
 
 
 // Node.Fill - fills other node data into calling node data if not set on calling node (skips DNSOn)
 // Node.Fill - fills other node data into calling node data if not set on calling node (skips DNSOn)
-func (newNode *Node) Fill(currentNode *Node, isEE bool) { // TODO add new field for nftables present
+func (newNode *Node) Fill(currentNode *Node, isPro bool) { // TODO add new field for nftables present
 	newNode.ID = currentNode.ID
 	newNode.ID = currentNode.ID
 	newNode.HostID = currentNode.HostID
 	newNode.HostID = currentNode.HostID
 	// Revisit the logic for boolean values
 	// Revisit the logic for boolean values
@@ -408,10 +408,10 @@ func (newNode *Node) Fill(currentNode *Node, isEE bool) { // TODO add new field
 	if newNode.RelayedNodes == nil {
 	if newNode.RelayedNodes == nil {
 		newNode.RelayedNodes = currentNode.RelayedNodes
 		newNode.RelayedNodes = currentNode.RelayedNodes
 	}
 	}
-	if newNode.IsRelay != currentNode.IsRelay && isEE {
+	if newNode.IsRelay != currentNode.IsRelay && isPro {
 		newNode.IsRelay = currentNode.IsRelay
 		newNode.IsRelay = currentNode.IsRelay
 	}
 	}
-	if newNode.IsRelayed == currentNode.IsRelayed && isEE {
+	if newNode.IsRelayed == currentNode.IsRelayed && isPro {
 		newNode.IsRelayed = currentNode.IsRelayed
 		newNode.IsRelayed = currentNode.IsRelayed
 	}
 	}
 	if newNode.Server == "" {
 	if newNode.Server == "" {

+ 0 - 37
models/promodels/networkuser.go

@@ -1,37 +0,0 @@
-package promodels
-
-// NetworkUserID - ID field for a network user
-type NetworkUserID string
-
-// NetworkUser - holds fields for a network user
-type NetworkUser struct {
-	AccessLevel int           `json:"accesslevel" bson:"accesslevel" yaml:"accesslevel"`
-	ClientLimit int           `json:"clientlimit" bson:"clientlimit" yaml:"clientlimit"`
-	NodeLimit   int           `json:"nodelimit" bson:"nodelimit" yaml:"nodelimit"`
-	ID          NetworkUserID `json:"id" bson:"id" yaml:"id"`
-	Clients     []string      `json:"clients" bson:"clients" yaml:"clients"`
-	Nodes       []string      `json:"nodes" bson:"nodes" yaml:"nodes"`
-}
-
-// NetworkUserMap - map of network users
-type NetworkUserMap map[NetworkUserID]NetworkUser
-
-// NetworkUserMap.Delete - deletes a network user struct from a given map in memory
-func (N NetworkUserMap) Delete(ID NetworkUserID) {
-	delete(N, ID)
-}
-
-// NetworkUserMap.Add - adds a network user struct to given network user map in memory
-func (N NetworkUserMap) Add(User *NetworkUser) {
-	N[User.ID] = *User
-}
-
-// SetDefaults - adds the defaults to network user
-func (U *NetworkUser) SetDefaults() {
-	if U.Clients == nil {
-		U.Clients = []string{}
-	}
-	if U.Nodes == nil {
-		U.Nodes = []string{}
-	}
-}

+ 0 - 10
models/promodels/pro.go

@@ -1,10 +0,0 @@
-package promodels
-
-// ProNetwork - struct for all pro Network related fields
-type ProNetwork struct {
-	DefaultAccessLevel     int      `json:"defaultaccesslevel" bson:"defaultaccesslevel" yaml:"defaultaccesslevel"`
-	DefaultUserNodeLimit   int      `json:"defaultusernodelimit" bson:"defaultusernodelimit" yaml:"defaultusernodelimit"`
-	DefaultUserClientLimit int      `json:"defaultuserclientlimit" bson:"defaultuserclientlimit" yaml:"defaultuserclientlimit"`
-	AllowedUsers           []string `json:"allowedusers" bson:"allowedusers" yaml:"allowedusers"`
-	AllowedGroups          []string `json:"allowedgroups" bson:"allowedgroups" yaml:"allowedgroups"`
-}

+ 0 - 9
models/promodels/usergroups.go

@@ -1,9 +0,0 @@
-package promodels
-
-type Void struct{}
-
-// UserGroupName - string representing a group name
-type UserGroupName string
-
-// UserGroups - groups type, holds group names
-type UserGroups map[UserGroupName]Void

+ 34 - 14
models/structs.go

@@ -24,19 +24,19 @@ type AuthParams struct {
 
 
 // User struct - struct for Users
 // User struct - struct for Users
 type User struct {
 type User struct {
-	UserName string   `json:"username" bson:"username" validate:"min=3,max=40,in_charset|email"`
-	Password string   `json:"password" bson:"password" validate:"required,min=5"`
-	Networks []string `json:"networks" bson:"networks"`
-	IsAdmin  bool     `json:"isadmin" bson:"isadmin"`
-	Groups   []string `json:"groups" bson:"groups" yaml:"groups"`
+	UserName     string              `json:"username" bson:"username" validate:"min=3,max=40,in_charset|email"`
+	Password     string              `json:"password" bson:"password" validate:"required,min=5"`
+	IsAdmin      bool                `json:"isadmin" bson:"isadmin"`
+	IsSuperAdmin bool                `json:"issuperadmin"`
+	RemoteGwIDs  map[string]struct{} `json:"remote_gw_ids"`
 }
 }
 
 
 // ReturnUser - return user struct
 // ReturnUser - return user struct
 type ReturnUser struct {
 type ReturnUser struct {
-	UserName string   `json:"username" bson:"username"`
-	Networks []string `json:"networks" bson:"networks"`
-	IsAdmin  bool     `json:"isadmin" bson:"isadmin"`
-	Groups   []string `json:"groups" bson:"groups"`
+	UserName     string              `json:"username"`
+	IsAdmin      bool                `json:"isadmin"`
+	IsSuperAdmin bool                `json:"issuperadmin"`
+	RemoteGwIDs  map[string]struct{} `json:"remote_gw_ids"`
 }
 }
 
 
 // UserAuthParams - user auth params struct
 // UserAuthParams - user auth params struct
@@ -47,13 +47,33 @@ type UserAuthParams struct {
 
 
 // UserClaims - user claims struct
 // UserClaims - user claims struct
 type UserClaims struct {
 type UserClaims struct {
-	IsAdmin  bool
-	UserName string
-	Networks []string
-	Groups   []string
+	IsAdmin      bool
+	IsSuperAdmin bool
+	UserName     string
 	jwt.RegisteredClaims
 	jwt.RegisteredClaims
 }
 }
 
 
+// IngressGwUsers - struct to hold users on a ingress gw
+type IngressGwUsers struct {
+	NodeID  string       `json:"node_id"`
+	Network string       `json:"network"`
+	Users   []ReturnUser `json:"users"`
+}
+
+// UserRemoteGws - struct to hold user's remote gws
+type UserRemoteGws struct {
+	GwID      string    `json:"remote_access_gw_id"`
+	GWName    string    `json:"gw_name"`
+	Network   string    `json:"network"`
+	Connected bool      `json:"connected"`
+	GwClient  ExtClient `json:"gw_client"`
+}
+
+// UserRemoteGwsReq - struct to hold user remote acccess gws req
+type UserRemoteGwsReq struct {
+	RemoteAccessClientID string `json:"remote_access_clientid"`
+}
+
 // SuccessfulUserLoginResponse - successlogin struct
 // SuccessfulUserLoginResponse - successlogin struct
 type SuccessfulUserLoginResponse struct {
 type SuccessfulUserLoginResponse struct {
 	UserName  string
 	UserName  string
@@ -238,7 +258,7 @@ type ServerConfig struct {
 	MQPassword  string       `yaml:"mq_password"`
 	MQPassword  string       `yaml:"mq_password"`
 	Server      string       `yaml:"server"`
 	Server      string       `yaml:"server"`
 	Broker      string       `yaml:"broker"`
 	Broker      string       `yaml:"broker"`
-	Is_EE       bool         `yaml:"isee"`
+	IsPro       bool         `yaml:"isee" json:"Is_EE"`
 	StunPort    int          `yaml:"stun_port"`
 	StunPort    int          `yaml:"stun_port"`
 	StunList    []StunServer `yaml:"stun_list"`
 	StunList    []StunServer `yaml:"stun_list"`
 	TrafficKey  []byte       `yaml:"traffickey"`
 	TrafficKey  []byte       `yaml:"traffickey"`

+ 20 - 174
mq/handlers.go

@@ -3,12 +3,10 @@ package mq
 import (
 import (
 	"encoding/json"
 	"encoding/json"
 	"fmt"
 	"fmt"
-	"math"
-	"time"
-
 	mqtt "github.com/eclipse/paho.mqtt.golang"
 	mqtt "github.com/eclipse/paho.mqtt.golang"
 	"github.com/google/uuid"
 	"github.com/google/uuid"
 	"github.com/gravitl/netmaker/database"
 	"github.com/gravitl/netmaker/database"
+	"github.com/gravitl/netmaker/logger"
 	"github.com/gravitl/netmaker/logic"
 	"github.com/gravitl/netmaker/logic"
 	"github.com/gravitl/netmaker/logic/hostactions"
 	"github.com/gravitl/netmaker/logic/hostactions"
 	"github.com/gravitl/netmaker/models"
 	"github.com/gravitl/netmaker/models"
@@ -18,6 +16,19 @@ import (
 	"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
 	"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
 )
 )
 
 
+// UpdateMetrics  message Handler -- handles updates from client nodes for metrics
+var UpdateMetrics = func(client mqtt.Client, msg mqtt.Message) {
+}
+
+func RunUpdates(node *models.Node, ifaceDelta bool) {
+	go func() { // don't block http response
+		// publish node update if not server
+		if err := NodeUpdate(node); err != nil {
+			logger.Log(1, "error publishing node update to node", node.ID.String(), err.Error())
+		}
+	}()
+}
+
 // DefaultHandler default message queue handler  -- NOT USED
 // DefaultHandler default message queue handler  -- NOT USED
 func DefaultHandler(client mqtt.Client, msg mqtt.Message) {
 func DefaultHandler(client mqtt.Client, msg mqtt.Message) {
 	slog.Info("mqtt default handler", "topic", msg.Topic(), "message", msg.Payload())
 	slog.Info("mqtt default handler", "topic", msg.Topic(), "message", msg.Payload())
@@ -25,7 +36,7 @@ func DefaultHandler(client mqtt.Client, msg mqtt.Message) {
 
 
 // UpdateNode  message Handler -- handles updates from client nodes
 // UpdateNode  message Handler -- handles updates from client nodes
 func UpdateNode(client mqtt.Client, msg mqtt.Message) {
 func UpdateNode(client mqtt.Client, msg mqtt.Message) {
-	id, err := getID(msg.Topic())
+	id, err := GetID(msg.Topic())
 	if err != nil {
 	if err != nil {
 		slog.Error("error getting node.ID ", "topic", msg.Topic(), "error", err)
 		slog.Error("error getting node.ID ", "topic", msg.Topic(), "error", err)
 		return
 		return
@@ -35,7 +46,7 @@ func UpdateNode(client mqtt.Client, msg mqtt.Message) {
 		slog.Error("error getting node", "id", id, "error", err)
 		slog.Error("error getting node", "id", id, "error", err)
 		return
 		return
 	}
 	}
-	decrypted, decryptErr := decryptMsg(&currentNode, msg.Payload())
+	decrypted, decryptErr := DecryptMsg(&currentNode, msg.Payload())
 	if decryptErr != nil {
 	if decryptErr != nil {
 		slog.Error("failed to decrypt message for node", "id", id, "error", decryptErr)
 		slog.Error("failed to decrypt message for node", "id", id, "error", decryptErr)
 		return
 		return
@@ -47,7 +58,7 @@ func UpdateNode(client mqtt.Client, msg mqtt.Message) {
 	}
 	}
 
 
 	ifaceDelta := logic.IfaceDelta(&currentNode, &newNode)
 	ifaceDelta := logic.IfaceDelta(&currentNode, &newNode)
-	if servercfg.Is_EE && ifaceDelta {
+	if servercfg.IsPro && ifaceDelta {
 		if err = logic.EnterpriseResetAllPeersFailovers(currentNode.ID, currentNode.Network); err != nil {
 		if err = logic.EnterpriseResetAllPeersFailovers(currentNode.ID, currentNode.Network); err != nil {
 			slog.Warn("failed to reset failover list during node update", "nodeid", currentNode.ID, "network", currentNode.Network)
 			slog.Warn("failed to reset failover list during node update", "nodeid", currentNode.ID, "network", currentNode.Network)
 		}
 		}
@@ -68,7 +79,7 @@ func UpdateNode(client mqtt.Client, msg mqtt.Message) {
 
 
 // UpdateHost  message Handler -- handles host updates from clients
 // UpdateHost  message Handler -- handles host updates from clients
 func UpdateHost(client mqtt.Client, msg mqtt.Message) {
 func UpdateHost(client mqtt.Client, msg mqtt.Message) {
-	id, err := getID(msg.Topic())
+	id, err := GetID(msg.Topic())
 	if err != nil {
 	if err != nil {
 		slog.Error("error getting host.ID sent on ", "topic", msg.Topic(), "error", err)
 		slog.Error("error getting host.ID sent on ", "topic", msg.Topic(), "error", err)
 		return
 		return
@@ -183,77 +194,11 @@ func UpdateHost(client mqtt.Client, msg mqtt.Message) {
 			slog.Error("failed to publish peer update", "error", err)
 			slog.Error("failed to publish peer update", "error", err)
 		}
 		}
 	}
 	}
-	// if servercfg.Is_EE && ifaceDelta {
-	// 	if err = logic.EnterpriseResetAllPeersFailovers(currentHost.ID.String(), currentHost.Network); err != nil {
-	// 		logger.Log(1, "failed to reset failover list during node update", currentHost.ID.String(), currentHost.Network)
-	// 	}
-	// }
-}
-
-// UpdateMetrics  message Handler -- handles updates from client nodes for metrics
-func UpdateMetrics(client mqtt.Client, msg mqtt.Message) {
-	if servercfg.Is_EE {
-		id, err := getID(msg.Topic())
-		if err != nil {
-			slog.Error("error getting ID sent on ", "topic", msg.Topic(), "error", err)
-			return
-		}
-		currentNode, err := logic.GetNodeByID(id)
-		if err != nil {
-			slog.Error("error getting node", "id", id, "error", err)
-			return
-		}
-		decrypted, decryptErr := decryptMsg(&currentNode, msg.Payload())
-		if decryptErr != nil {
-			slog.Error("failed to decrypt message for node", "id", id, "error", decryptErr)
-			return
-		}
-
-		var newMetrics models.Metrics
-		if err := json.Unmarshal(decrypted, &newMetrics); err != nil {
-			slog.Error("error unmarshaling payload", "error", err)
-			return
-		}
-
-		shouldUpdate := updateNodeMetrics(&currentNode, &newMetrics)
-
-		if err = logic.UpdateMetrics(id, &newMetrics); err != nil {
-			slog.Error("failed to update node metrics", "id", id, "error", err)
-			return
-		}
-		if servercfg.IsMetricsExporter() {
-			if err := pushMetricsToExporter(newMetrics); err != nil {
-				slog.Error("failed to push node metrics to exporter", "id", currentNode.ID, "error", err)
-			}
-		}
-
-		if newMetrics.Connectivity != nil {
-			err := logic.EnterpriseFailoverFunc(&currentNode)
-			if err != nil {
-				slog.Error("failed to failover for node", "id", currentNode.ID, "network", currentNode.Network, "error", err)
-			}
-		}
-
-		if shouldUpdate {
-			slog.Info("updating peers after node detected connectivity issues", "id", currentNode.ID, "network", currentNode.Network)
-			host, err := logic.GetHost(currentNode.HostID.String())
-			if err == nil {
-				nodes, err := logic.GetAllNodes()
-				if err != nil {
-					return
-				}
-				if err = PublishSingleHostPeerUpdate(host, nodes, nil, nil); err != nil {
-					slog.Warn("failed to publish update after failover peer change for node", "id", currentNode.ID, "network", currentNode.Network, "error", err)
-				}
-			}
-		}
-		slog.Debug("updated node metrics", "id", id)
-	}
 }
 }
 
 
 // ClientPeerUpdate  message handler -- handles updating peers after signal from client nodes
 // ClientPeerUpdate  message handler -- handles updating peers after signal from client nodes
 func ClientPeerUpdate(client mqtt.Client, msg mqtt.Message) {
 func ClientPeerUpdate(client mqtt.Client, msg mqtt.Message) {
-	id, err := getID(msg.Topic())
+	id, err := GetID(msg.Topic())
 	if err != nil {
 	if err != nil {
 		slog.Error("error getting node.ID sent on ", "topic", msg.Topic(), "error", err)
 		slog.Error("error getting node.ID sent on ", "topic", msg.Topic(), "error", err)
 		return
 		return
@@ -263,7 +208,7 @@ func ClientPeerUpdate(client mqtt.Client, msg mqtt.Message) {
 		slog.Error("error getting node", "id", id, "error", err)
 		slog.Error("error getting node", "id", id, "error", err)
 		return
 		return
 	}
 	}
-	decrypted, decryptErr := decryptMsg(&currentNode, msg.Payload())
+	decrypted, decryptErr := DecryptMsg(&currentNode, msg.Payload())
 	if decryptErr != nil {
 	if decryptErr != nil {
 		slog.Error("failed to decrypt message for node", "id", id, "error", decryptErr)
 		slog.Error("failed to decrypt message for node", "id", id, "error", decryptErr)
 		return
 		return
@@ -281,105 +226,6 @@ func ClientPeerUpdate(client mqtt.Client, msg mqtt.Message) {
 	slog.Info("sent peer updates after signal received from", "id", id)
 	slog.Info("sent peer updates after signal received from", "id", id)
 }
 }
 
 
-func updateNodeMetrics(currentNode *models.Node, newMetrics *models.Metrics) bool {
-	if newMetrics.FailoverPeers == nil {
-		newMetrics.FailoverPeers = make(map[string]string)
-	}
-	oldMetrics, err := logic.GetMetrics(currentNode.ID.String())
-	if err != nil {
-		slog.Error("error finding old metrics for node", "id", currentNode.ID, "error", err)
-		return false
-	}
-	if oldMetrics.FailoverPeers == nil {
-		oldMetrics.FailoverPeers = make(map[string]string)
-	}
-
-	var attachedClients []models.ExtClient
-	if currentNode.IsIngressGateway {
-		clients, err := logic.GetExtClientsByID(currentNode.ID.String(), currentNode.Network)
-		if err == nil {
-			attachedClients = clients
-		}
-	}
-	if len(attachedClients) > 0 {
-		// associate ext clients with IDs
-		for i := range attachedClients {
-			extMetric := newMetrics.Connectivity[attachedClients[i].PublicKey]
-			if len(extMetric.NodeName) == 0 &&
-				len(newMetrics.Connectivity[attachedClients[i].ClientID].NodeName) > 0 { // cover server clients
-				extMetric = newMetrics.Connectivity[attachedClients[i].ClientID]
-				if extMetric.TotalReceived > 0 && extMetric.TotalSent > 0 {
-					extMetric.Connected = true
-				}
-			}
-			extMetric.NodeName = attachedClients[i].ClientID
-			delete(newMetrics.Connectivity, attachedClients[i].PublicKey)
-			newMetrics.Connectivity[attachedClients[i].ClientID] = extMetric
-		}
-	}
-
-	// run through metrics for each peer
-	for k := range newMetrics.Connectivity {
-		currMetric := newMetrics.Connectivity[k]
-		oldMetric := oldMetrics.Connectivity[k]
-		currMetric.TotalTime += oldMetric.TotalTime
-		currMetric.Uptime += oldMetric.Uptime // get the total uptime for this connection
-
-		if currMetric.TotalReceived < oldMetric.TotalReceived {
-			currMetric.TotalReceived += oldMetric.TotalReceived
-		} else {
-			currMetric.TotalReceived += int64(math.Abs(float64(currMetric.TotalReceived) - float64(oldMetric.TotalReceived)))
-		}
-		if currMetric.TotalSent < oldMetric.TotalSent {
-			currMetric.TotalSent += oldMetric.TotalSent
-		} else {
-			currMetric.TotalSent += int64(math.Abs(float64(currMetric.TotalSent) - float64(oldMetric.TotalSent)))
-		}
-
-		if currMetric.Uptime == 0 || currMetric.TotalTime == 0 {
-			currMetric.PercentUp = 0
-		} else {
-			currMetric.PercentUp = 100.0 * (float64(currMetric.Uptime) / float64(currMetric.TotalTime))
-		}
-		totalUpMinutes := currMetric.Uptime * ncutils.CheckInInterval
-		currMetric.ActualUptime = time.Duration(totalUpMinutes) * time.Minute
-		delete(oldMetrics.Connectivity, k) // remove from old data
-		newMetrics.Connectivity[k] = currMetric
-
-	}
-
-	// add nodes that need failover
-	nodes, err := logic.GetNetworkNodes(currentNode.Network)
-	if err != nil {
-		slog.Error("failed to retrieve nodes while updating metrics", "error", err)
-		return false
-	}
-	for _, node := range nodes {
-		if !newMetrics.Connectivity[node.ID.String()].Connected &&
-			len(newMetrics.Connectivity[node.ID.String()].NodeName) > 0 &&
-			node.Connected &&
-			len(node.FailoverNode) > 0 &&
-			!node.Failover {
-			newMetrics.FailoverPeers[node.ID.String()] = node.FailoverNode.String()
-		}
-	}
-	shouldUpdate := len(oldMetrics.FailoverPeers) == 0 && len(newMetrics.FailoverPeers) > 0
-	for k, v := range oldMetrics.FailoverPeers {
-		if len(newMetrics.FailoverPeers[k]) > 0 && len(v) == 0 {
-			shouldUpdate = true
-		}
-
-		if len(v) > 0 && len(newMetrics.FailoverPeers[k]) == 0 {
-			newMetrics.FailoverPeers[k] = v
-		}
-	}
-
-	for k := range oldMetrics.Connectivity { // cleanup any left over data, self healing
-		delete(newMetrics.Connectivity, k)
-	}
-	return shouldUpdate
-}
-
 func handleNewNodeDNS(host *models.Host, node *models.Node) error {
 func handleNewNodeDNS(host *models.Host, node *models.Node) error {
 	dns := models.DNSUpdate{
 	dns := models.DNSUpdate{
 		Action: models.DNSInsert,
 		Action: models.DNSInsert,

+ 6 - 4
mq/publishers.go

@@ -79,8 +79,10 @@ func PublishDeletedClientPeerUpdate(delClient *models.ExtClient) error {
 	}
 	}
 	for _, host := range hosts {
 	for _, host := range hosts {
 		host := host
 		host := host
-		if err = PublishSingleHostPeerUpdate(&host, nodes, nil, []models.ExtClient{*delClient}); err != nil {
-			logger.Log(1, "failed to publish peer update to host", host.ID.String(), ": ", err.Error())
+		if host.OS != models.OS_Types.IoT {
+			if err = PublishSingleHostPeerUpdate(&host, nodes, nil, []models.ExtClient{*delClient}); err != nil {
+				logger.Log(1, "failed to publish peer update to host", host.ID.String(), ": ", err.Error())
+			}
 		}
 		}
 	}
 	}
 	return err
 	return err
@@ -258,7 +260,7 @@ func PublishReplaceDNS(oldNode, newNode *models.Node, host *models.Host) error {
 }
 }
 
 
 // PublishExtClientDNS publish dns update for new extclient
 // PublishExtClientDNS publish dns update for new extclient
-func PublishExtCLientDNS(client *models.ExtClient) error {
+func PublishExtClientDNS(client *models.ExtClient) error {
 	errMsgs := models.DNSError{}
 	errMsgs := models.DNSError{}
 	dns := models.DNSUpdate{
 	dns := models.DNSUpdate{
 		Action:  models.DNSInsert,
 		Action:  models.DNSInsert,
@@ -342,7 +344,7 @@ func PublishHostDNSUpdate(old, new *models.Host, networks []string) error {
 	return nil
 	return nil
 }
 }
 
 
-func pushMetricsToExporter(metrics models.Metrics) error {
+func PushMetricsToExporter(metrics models.Metrics) error {
 	logger.Log(2, "----> Pushing metrics to exporter")
 	logger.Log(2, "----> Pushing metrics to exporter")
 	data, err := json.Marshal(metrics)
 	data, err := json.Marshal(metrics)
 	if err != nil {
 	if err != nil {

+ 2 - 2
mq/util.go

@@ -32,7 +32,7 @@ func decryptMsgWithHost(host *models.Host, msg []byte) ([]byte, error) {
 	return ncutils.DeChunk(msg, nodePubTKey, serverPrivTKey)
 	return ncutils.DeChunk(msg, nodePubTKey, serverPrivTKey)
 }
 }
 
 
-func decryptMsg(node *models.Node, msg []byte) ([]byte, error) {
+func DecryptMsg(node *models.Node, msg []byte) ([]byte, error) {
 	if len(msg) <= 24 { // make sure message is of appropriate length
 	if len(msg) <= 24 { // make sure message is of appropriate length
 		return nil, fmt.Errorf("recieved invalid message from broker %v", msg)
 		return nil, fmt.Errorf("recieved invalid message from broker %v", msg)
 	}
 	}
@@ -93,7 +93,7 @@ func publish(host *models.Host, dest string, msg []byte) error {
 }
 }
 
 
 // decodes a message queue topic and returns the embedded node.ID
 // decodes a message queue topic and returns the embedded node.ID
-func getID(topic string) (string, error) {
+func GetID(topic string) (string, error) {
 	parts := strings.Split(topic, "/")
 	parts := strings.Split(topic, "/")
 	count := len(parts)
 	count := len(parts)
 	if count == 1 {
 	if count == 1 {

+ 0 - 0
ee/LICENSE → pro/LICENSE


+ 7 - 6
ee/ee_controllers/metrics.go → pro/controllers/metrics.go

@@ -1,7 +1,8 @@
-package ee_controllers
+package controllers
 
 
 import (
 import (
 	"encoding/json"
 	"encoding/json"
+	proLogic "github.com/gravitl/netmaker/pro/logic"
 	"net/http"
 	"net/http"
 
 
 	"github.com/gorilla/mux"
 	"github.com/gorilla/mux"
@@ -11,7 +12,7 @@ import (
 	"github.com/gravitl/netmaker/models"
 	"github.com/gravitl/netmaker/models"
 )
 )
 
 
-// MetricHandlers - How we handle EE Metrics
+// MetricHandlers - How we handle Pro Metrics
 func MetricHandlers(r *mux.Router) {
 func MetricHandlers(r *mux.Router) {
 	r.HandleFunc("/api/metrics/{network}/{nodeid}", logic.SecurityCheck(true, http.HandlerFunc(getNodeMetrics))).Methods(http.MethodGet)
 	r.HandleFunc("/api/metrics/{network}/{nodeid}", logic.SecurityCheck(true, http.HandlerFunc(getNodeMetrics))).Methods(http.MethodGet)
 	r.HandleFunc("/api/metrics/{network}", logic.SecurityCheck(true, http.HandlerFunc(getNetworkNodesMetrics))).Methods(http.MethodGet)
 	r.HandleFunc("/api/metrics/{network}", logic.SecurityCheck(true, http.HandlerFunc(getNetworkNodesMetrics))).Methods(http.MethodGet)
@@ -61,7 +62,7 @@ func getNetworkNodesMetrics(w http.ResponseWriter, r *http.Request) {
 
 
 	for i := range networkNodes {
 	for i := range networkNodes {
 		id := networkNodes[i].ID
 		id := networkNodes[i].ID
-		metrics, err := logic.GetMetrics(id.String())
+		metrics, err := proLogic.GetMetrics(id.String())
 		if err != nil {
 		if err != nil {
 			logger.Log(1, r.Header.Get("user"), "failed to append metrics of node", id.String(), "during network metrics fetch", err.Error())
 			logger.Log(1, r.Header.Get("user"), "failed to append metrics of node", id.String(), "during network metrics fetch", err.Error())
 			continue
 			continue
@@ -83,7 +84,7 @@ func getNetworkExtMetrics(w http.ResponseWriter, r *http.Request) {
 	network := params["network"]
 	network := params["network"]
 
 
 	logger.Log(1, r.Header.Get("user"), "requested fetching external client metrics on network", network)
 	logger.Log(1, r.Header.Get("user"), "requested fetching external client metrics on network", network)
-	ingresses, err := logic.GetNetworkIngresses(network) // grab all the ingress gateways
+	ingresses, err := proLogic.GetNetworkIngresses(network) // grab all the ingress gateways
 	if err != nil {
 	if err != nil {
 		logger.Log(1, r.Header.Get("user"), "failed to fetch metrics of ext clients in network", network, err.Error())
 		logger.Log(1, r.Header.Get("user"), "failed to fetch metrics of ext clients in network", network, err.Error())
 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
@@ -108,7 +109,7 @@ func getNetworkExtMetrics(w http.ResponseWriter, r *http.Request) {
 
 
 	for i := range ingresses {
 	for i := range ingresses {
 		id := ingresses[i].ID
 		id := ingresses[i].ID
-		ingressMetrics, err := logic.GetMetrics(id.String())
+		ingressMetrics, err := proLogic.GetMetrics(id.String())
 		if err != nil {
 		if err != nil {
 			logger.Log(1, r.Header.Get("user"), "failed to append external client metrics from ingress node", id.String(), err.Error())
 			logger.Log(1, r.Header.Get("user"), "failed to append external client metrics from ingress node", id.String(), err.Error())
 			continue
 			continue
@@ -149,7 +150,7 @@ func getAllMetrics(w http.ResponseWriter, r *http.Request) {
 
 
 	for i := range allNodes {
 	for i := range allNodes {
 		id := allNodes[i].ID
 		id := allNodes[i].ID
-		metrics, err := logic.GetMetrics(id.String())
+		metrics, err := proLogic.GetMetrics(id.String())
 		if err != nil {
 		if err != nil {
 			logger.Log(1, r.Header.Get("user"), "failed to append metrics of node", id.String(), "during all nodes metrics fetch", err.Error())
 			logger.Log(1, r.Header.Get("user"), "failed to append metrics of node", id.String(), "during all nodes metrics fetch", err.Error())
 			continue
 			continue

+ 1 - 1
ee/ee_controllers/middleware.go → pro/controllers/middleware.go

@@ -1,4 +1,4 @@
-package ee_controllers
+package controllers
 
 
 import (
 import (
 	"github.com/gravitl/netmaker/logic"
 	"github.com/gravitl/netmaker/logic"

+ 5 - 4
ee/ee_controllers/relay.go → pro/controllers/relay.go

@@ -1,8 +1,9 @@
-package ee_controllers
+package controllers
 
 
 import (
 import (
 	"encoding/json"
 	"encoding/json"
 	"fmt"
 	"fmt"
+	proLogic "github.com/gravitl/netmaker/pro/logic"
 	"net/http"
 	"net/http"
 
 
 	"github.com/gorilla/mux"
 	"github.com/gorilla/mux"
@@ -13,7 +14,7 @@ import (
 	"github.com/gravitl/netmaker/mq"
 	"github.com/gravitl/netmaker/mq"
 )
 )
 
 
-// RelayHandlers - handle EE Relays
+// RelayHandlers - handle Pro Relays
 func RelayHandlers(r *mux.Router) {
 func RelayHandlers(r *mux.Router) {
 
 
 	r.HandleFunc("/api/nodes/{network}/{nodeid}/createrelay", controller.Authorize(false, true, "user", http.HandlerFunc(createRelay))).Methods(http.MethodPost)
 	r.HandleFunc("/api/nodes/{network}/{nodeid}/createrelay", controller.Authorize(false, true, "user", http.HandlerFunc(createRelay))).Methods(http.MethodPost)
@@ -43,7 +44,7 @@ func createRelay(w http.ResponseWriter, r *http.Request) {
 	}
 	}
 	relayRequest.NetID = params["network"]
 	relayRequest.NetID = params["network"]
 	relayRequest.NodeID = params["nodeid"]
 	relayRequest.NodeID = params["nodeid"]
-	_, relayNode, err := logic.CreateRelay(relayRequest)
+	_, relayNode, err := proLogic.CreateRelay(relayRequest)
 	if err != nil {
 	if err != nil {
 		logger.Log(0, r.Header.Get("user"),
 		logger.Log(0, r.Header.Get("user"),
 			fmt.Sprintf("failed to create relay on node [%s] on network [%s]: %v", relayRequest.NodeID, relayRequest.NetID, err))
 			fmt.Sprintf("failed to create relay on node [%s] on network [%s]: %v", relayRequest.NodeID, relayRequest.NetID, err))
@@ -73,7 +74,7 @@ func deleteRelay(w http.ResponseWriter, r *http.Request) {
 	var params = mux.Vars(r)
 	var params = mux.Vars(r)
 	nodeid := params["nodeid"]
 	nodeid := params["nodeid"]
 	netid := params["network"]
 	netid := params["network"]
-	updateNodes, node, err := logic.DeleteRelay(netid, nodeid)
+	updateNodes, node, err := proLogic.DeleteRelay(netid, nodeid)
 	if err != nil {
 	if err != nil {
 		logger.Log(0, r.Header.Get("user"), "error decoding request body: ", err.Error())
 		logger.Log(0, r.Header.Get("user"), "error decoding request body: ", err.Error())
 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))

+ 268 - 0
pro/controllers/users.go

@@ -0,0 +1,268 @@
+package controllers
+
+import (
+	"encoding/json"
+	"errors"
+	"fmt"
+	"net/http"
+
+	"github.com/gorilla/mux"
+	"github.com/gravitl/netmaker/logger"
+	"github.com/gravitl/netmaker/logic"
+	"github.com/gravitl/netmaker/models"
+	"golang.org/x/exp/slog"
+)
+
+func UserHandlers(r *mux.Router) {
+	r.HandleFunc("/api/users/{username}/remote_access_gw/{remote_access_gateway_id}", logic.SecurityCheck(true, http.HandlerFunc(attachUserToRemoteAccessGw))).Methods(http.MethodPost)
+	r.HandleFunc("/api/users/{username}/remote_access_gw/{remote_access_gateway_id}", logic.SecurityCheck(true, http.HandlerFunc(removeUserFromRemoteAccessGW))).Methods(http.MethodDelete)
+	r.HandleFunc("/api/users/{username}/remote_access_gw", logic.SecurityCheck(false, logic.ContinueIfUserMatch(http.HandlerFunc(getUserRemoteAccessGws)))).Methods(http.MethodGet)
+	r.HandleFunc("/api/users/ingress/{ingress_id}", logic.SecurityCheck(true, http.HandlerFunc(ingressGatewayUsers))).Methods(http.MethodGet)
+}
+
+// swagger:route POST /api/users/{username}/remote_access_gw user attachUserToRemoteAccessGateway
+//
+// Attach User to a remote access gateway.
+//
+//			Schemes: https
+//
+//			Security:
+//	  		oauth
+//
+//			Responses:
+//				200: userBodyResponse
+func attachUserToRemoteAccessGw(w http.ResponseWriter, r *http.Request) {
+	// set header.
+	w.Header().Set("Content-Type", "application/json")
+
+	var params = mux.Vars(r)
+	username := params["username"]
+	remoteGwID := params["remote_access_gateway_id"]
+	if username == "" || remoteGwID == "" {
+		logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("required params `username` and `remote_access_gateway_id`"), "badrequest"))
+		return
+	}
+	user, err := logic.GetUser(username)
+	if err != nil {
+		slog.Error("failed to fetch user: ", "username", username, "error", err.Error())
+		logic.ReturnErrorResponse(w, r, logic.FormatError(fmt.Errorf("failed to fetch user %s, error: %v", username, err), "badrequest"))
+		return
+	}
+	if user.IsAdmin || user.IsSuperAdmin {
+		logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("superadmins/admins have access to all gateways"), "badrequest"))
+		return
+	}
+	node, err := logic.GetNodeByID(remoteGwID)
+	if err != nil {
+		slog.Error("failed to fetch gateway node", "nodeID", remoteGwID, "error", err)
+		logic.ReturnErrorResponse(w, r, logic.FormatError(fmt.Errorf("failed to fetch remote access gateway node, error: %v", err), "badrequest"))
+		return
+	}
+	if !node.IsIngressGateway {
+		logic.ReturnErrorResponse(w, r, logic.FormatError(fmt.Errorf("node is not a remote access gateway"), "badrequest"))
+		return
+	}
+	if user.RemoteGwIDs == nil {
+		user.RemoteGwIDs = make(map[string]struct{})
+	}
+	user.RemoteGwIDs[node.ID.String()] = struct{}{}
+	err = logic.UpsertUser(*user)
+	if err != nil {
+		slog.Error("failed to update user's gateways", "user", username, "error", err)
+		logic.ReturnErrorResponse(w, r, logic.FormatError(fmt.Errorf("failed to fetch remote access gateway node,error: %v", err), "badrequest"))
+		return
+	}
+
+	json.NewEncoder(w).Encode(logic.ToReturnUser(*user))
+}
+
+// swagger:route DELETE /api/users/{username}/remote_access_gw user removeUserFromRemoteAccessGW
+//
+// Attach User to a remote access gateway.
+//
+//			Schemes: https
+//
+//			Security:
+//	  		oauth
+//
+//			Responses:
+//				200: userBodyResponse
+func removeUserFromRemoteAccessGW(w http.ResponseWriter, r *http.Request) {
+	// set header.
+	w.Header().Set("Content-Type", "application/json")
+
+	var params = mux.Vars(r)
+	username := params["username"]
+	remoteGwID := params["remote_access_gateway_id"]
+	if username == "" || remoteGwID == "" {
+		logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("required params `username` and `remote_access_gateway_id`"), "badrequest"))
+		return
+	}
+	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
+	}
+	delete(user.RemoteGwIDs, remoteGwID)
+	go func(user models.User, remoteGwID string) {
+		extclients, err := logic.GetAllExtClients()
+		if err != nil {
+			slog.Error("failed to fetch extclients", "error", err)
+			return
+		}
+		for _, extclient := range extclients {
+			if extclient.OwnerID == user.UserName && remoteGwID == extclient.IngressGatewayID {
+				logic.DeleteExtClient(extclient.Network, extclient.ClientID)
+			}
+		}
+	}(*user, remoteGwID)
+
+	err = logic.UpsertUser(*user)
+	if err != nil {
+		slog.Error("failed to update user gateways", "user", username, "error", err)
+		logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("failed to fetch remote access gaetway node "+err.Error()), "badrequest"))
+		return
+	}
+	json.NewEncoder(w).Encode(logic.ToReturnUser(*user))
+}
+
+// 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
+	}
+	var req models.UserRemoteGwsReq
+	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
+	}
+	if req.RemoteAccessClientID == "" {
+		logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("remote access client id cannot be empty"), "badrequest"))
+		return
+	}
+	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
+	}
+	if user.IsAdmin || user.IsSuperAdmin {
+		logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("admins can visit dashboard to create remote clients"), "badrequest"))
+		return
+	}
+	allextClients, err := logic.GetAllExtClients()
+	if err != nil {
+		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
+		return
+	}
+
+	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
+			}
+			host, err := logic.GetHost(node.HostID.String())
+			if err != nil {
+				continue
+			}
+
+			if _, ok := user.RemoteGwIDs[node.ID.String()]; ok {
+				gws := userGws[node.Network]
+
+				gws = append(gws, models.UserRemoteGws{
+					GwID:      node.ID.String(),
+					GWName:    host.Name,
+					Network:   node.Network,
+					GwClient:  extClient,
+					Connected: true,
+				})
+				userGws[node.Network] = gws
+				delete(user.RemoteGwIDs, node.ID.String())
+
+			}
+		}
+
+	}
+
+	// add remaining gw nodes to resp
+	for gwID := range user.RemoteGwIDs {
+		node, err := logic.GetNodeByID(gwID)
+		if err != nil {
+			continue
+		}
+		if node.PendingDelete {
+			continue
+		}
+		host, err := logic.GetHost(node.HostID.String())
+		if err != nil {
+			continue
+		}
+		gws := userGws[node.Network]
+
+		gws = append(gws, models.UserRemoteGws{
+			GwID:    node.ID.String(),
+			GWName:  host.Name,
+			Network: node.Network,
+		})
+		userGws[node.Network] = gws
+	}
+
+	w.WriteHeader(http.StatusOK)
+	json.NewEncoder(w).Encode(userGws)
+}
+
+// swagger:route GET /api/nodes/{network}/{nodeid}/ingress/users users ingressGatewayUsers
+//
+// Lists all the users attached to an ingress gateway.
+//
+//			Schemes: https
+//
+//			Security:
+//	  		oauth
+//
+//			Responses:
+//				200: nodeResponse
+func ingressGatewayUsers(w http.ResponseWriter, r *http.Request) {
+	w.Header().Set("Content-Type", "application/json")
+	var params = mux.Vars(r)
+	ingressID := params["ingress_id"]
+	node, err := logic.GetNodeByID(ingressID)
+	if err != nil {
+		slog.Error("failed to get ingress node", "error", err)
+		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
+		return
+	}
+	gwUsers, err := logic.GetIngressGwUsers(node)
+	if err != nil {
+		slog.Error("failed to get users on ingress gateway", "nodeid", ingressID, "network", node.Network, "user", r.Header.Get("user"),
+			"error", err)
+		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
+		return
+	}
+	w.WriteHeader(http.StatusOK)
+	json.NewEncoder(w).Encode(gwUsers)
+}

+ 32 - 21
ee/initialize.go → pro/initialize.go

@@ -1,33 +1,32 @@
 //go:build ee
 //go:build ee
 // +build ee
 // +build ee
 
 
-package ee
+package pro
 
 
 import (
 import (
 	controller "github.com/gravitl/netmaker/controllers"
 	controller "github.com/gravitl/netmaker/controllers"
-	"github.com/gravitl/netmaker/ee/ee_controllers"
-	eelogic "github.com/gravitl/netmaker/ee/logic"
 	"github.com/gravitl/netmaker/logic"
 	"github.com/gravitl/netmaker/logic"
 	"github.com/gravitl/netmaker/models"
 	"github.com/gravitl/netmaker/models"
+	"github.com/gravitl/netmaker/mq"
+	proControllers "github.com/gravitl/netmaker/pro/controllers"
+	proLogic "github.com/gravitl/netmaker/pro/logic"
 	"github.com/gravitl/netmaker/servercfg"
 	"github.com/gravitl/netmaker/servercfg"
 	"golang.org/x/exp/slog"
 	"golang.org/x/exp/slog"
 )
 )
 
 
-// InitEE - Initialize EE Logic
-func InitEE() {
-	setIsEnterprise()
-	servercfg.Is_EE = true
-	models.SetLogo(retrieveEELogo())
+// InitPro - Initialize Pro Logic
+func InitPro() {
+	servercfg.IsPro = true
+	models.SetLogo(retrieveProLogo())
 	controller.HttpMiddlewares = append(
 	controller.HttpMiddlewares = append(
 		controller.HttpMiddlewares,
 		controller.HttpMiddlewares,
-		ee_controllers.OnlyServerAPIWhenUnlicensedMiddleware,
+		proControllers.OnlyServerAPIWhenUnlicensedMiddleware,
 	)
 	)
 	controller.HttpHandlers = append(
 	controller.HttpHandlers = append(
 		controller.HttpHandlers,
 		controller.HttpHandlers,
-		ee_controllers.MetricHandlers,
-		ee_controllers.NetworkUsersHandlers,
-		ee_controllers.UserGroupsHandlers,
-		ee_controllers.RelayHandlers,
+		proControllers.MetricHandlers,
+		proControllers.RelayHandlers,
+		proControllers.UserHandlers,
 	)
 	)
 	logic.EnterpriseCheckFuncs = append(logic.EnterpriseCheckFuncs, func() {
 	logic.EnterpriseCheckFuncs = append(logic.EnterpriseCheckFuncs, func() {
 		// == License Handling ==
 		// == License Handling ==
@@ -41,19 +40,31 @@ func InitEE() {
 		AddLicenseHooks()
 		AddLicenseHooks()
 		resetFailover()
 		resetFailover()
 	})
 	})
-	logic.EnterpriseFailoverFunc = eelogic.SetFailover
-	logic.EnterpriseResetFailoverFunc = eelogic.ResetFailover
-	logic.EnterpriseResetAllPeersFailovers = eelogic.WipeAffectedFailoversOnly
-	logic.DenyClientNodeAccess = eelogic.DenyClientNode
-	logic.IsClientNodeAllowed = eelogic.IsClientNodeAllowed
-	logic.AllowClientNodeAccess = eelogic.RemoveDeniedNodeFromClient
+	logic.EnterpriseFailoverFunc = proLogic.SetFailover
+	logic.EnterpriseResetFailoverFunc = proLogic.ResetFailover
+	logic.EnterpriseResetAllPeersFailovers = proLogic.WipeAffectedFailoversOnly
+	logic.DenyClientNodeAccess = proLogic.DenyClientNode
+	logic.IsClientNodeAllowed = proLogic.IsClientNodeAllowed
+	logic.AllowClientNodeAccess = proLogic.RemoveDeniedNodeFromClient
+	logic.SetClientDefaultACLs = proLogic.SetClientDefaultACLs
+	logic.SetClientACLs = proLogic.SetClientACLs
+	logic.UpdateProNodeACLs = proLogic.UpdateProNodeACLs
+	logic.GetMetrics = proLogic.GetMetrics
+	logic.UpdateMetrics = proLogic.UpdateMetrics
+	logic.DeleteMetrics = proLogic.DeleteMetrics
+	logic.GetAllowedIpsForRelayed = proLogic.GetAllowedIpsForRelayed
+	logic.RelayedAllowedIPs = proLogic.RelayedAllowedIPs
+	logic.UpdateRelayed = proLogic.UpdateRelayed
+	logic.SetRelayedNodes = proLogic.SetRelayedNodes
+	logic.RelayUpdates = proLogic.RelayUpdates
+	mq.UpdateMetrics = proLogic.MQUpdateMetrics
 }
 }
 
 
 func resetFailover() {
 func resetFailover() {
 	nets, err := logic.GetNetworks()
 	nets, err := logic.GetNetworks()
 	if err == nil {
 	if err == nil {
 		for _, net := range nets {
 		for _, net := range nets {
-			err = eelogic.ResetFailover(net.NetID)
+			err = proLogic.ResetFailover(net.NetID)
 			if err != nil {
 			if err != nil {
 				slog.Error("failed to reset failover", "network", net.NetID, "error", err.Error())
 				slog.Error("failed to reset failover", "network", net.NetID, "error", err.Error())
 			}
 			}
@@ -61,7 +72,7 @@ func resetFailover() {
 	}
 	}
 }
 }
 
 
-func retrieveEELogo() string {
+func retrieveProLogo() string {
 	return `              
 	return `              
  __   __     ______     ______   __    __     ______     __  __     ______     ______    
  __   __     ______     ______   __    __     ______     __  __     ______     ______    
 /\ "-.\ \   /\  ___\   /\__  _\ /\ "-./  \   /\  __ \   /\ \/ /    /\  ___\   /\  == \   
 /\ "-.\ \   /\  ___\   /\__  _\ /\ "-./  \   /\  __ \   /\ \/ /    /\  ___\   /\  == \   

+ 1 - 1
ee/license.go → pro/license.go

@@ -1,7 +1,7 @@
 //go:build ee
 //go:build ee
 // +build ee
 // +build ee
 
 
-package ee
+package pro
 
 
 import (
 import (
 	"bytes"
 	"bytes"

+ 1 - 1
ee/license_test.go → pro/license_test.go

@@ -1,7 +1,7 @@
 //go:build ee
 //go:build ee
 // +build ee
 // +build ee
 
 
-package ee
+package pro
 
 
 import (
 import (
 	"github.com/gravitl/netmaker/config"
 	"github.com/gravitl/netmaker/config"

+ 114 - 0
pro/logic/ext_acls.go

@@ -0,0 +1,114 @@
+package logic
+
+import (
+	"github.com/gravitl/netmaker/logic"
+	"github.com/gravitl/netmaker/logic/acls"
+	"github.com/gravitl/netmaker/logic/acls/nodeacls"
+	"github.com/gravitl/netmaker/models"
+)
+
+// DenyClientNode - add a denied node to an ext client's list
+func DenyClientNode(ec *models.ExtClient, clientOrNodeID string) (ok bool) {
+	if ec == nil || len(clientOrNodeID) == 0 {
+		return
+	}
+	if ec.DeniedACLs == nil {
+		ec.DeniedACLs = map[string]struct{}{}
+	}
+	ok = true
+	ec.DeniedACLs[clientOrNodeID] = struct{}{}
+	return
+}
+
+// IsClientNodeAllowed - checks if given ext client and node are allowed to communicate
+func IsClientNodeAllowed(ec *models.ExtClient, clientOrNodeID string) bool {
+	if ec == nil || len(clientOrNodeID) == 0 {
+		return false
+	}
+	if ec.DeniedACLs == nil {
+		return true
+	}
+	_, ok := ec.DeniedACLs[clientOrNodeID]
+	return !ok
+}
+
+// RemoveDeniedNodeFromClient - removes a node id from set of denied nodes
+func RemoveDeniedNodeFromClient(ec *models.ExtClient, clientOrNodeID string) bool {
+	if ec.DeniedACLs == nil {
+		return true
+	}
+	_, ok := ec.DeniedACLs[clientOrNodeID]
+	if !ok {
+		return false
+	}
+	delete(ec.DeniedACLs, clientOrNodeID)
+	return true
+}
+
+// SetClientDefaultACLs - set's a client's default ACLs based on network and nodes in network
+func SetClientDefaultACLs(ec *models.ExtClient) error {
+	networkNodes, err := logic.GetNetworkNodes(ec.Network)
+	if err != nil {
+		return err
+	}
+	network, err := logic.GetNetwork(ec.Network)
+	if err != nil {
+		return err
+	}
+	for i := range networkNodes {
+		currNode := networkNodes[i]
+		if network.DefaultACL == "no" || currNode.DefaultACL == "no" {
+			DenyClientNode(ec, currNode.ID.String())
+		} else {
+			RemoveDeniedNodeFromClient(ec, currNode.ID.String())
+		}
+	}
+	return nil
+}
+
+// SetClientACLs - overwrites an ext client's ACL
+func SetClientACLs(ec *models.ExtClient, newACLs map[string]struct{}) {
+	if ec == nil || newACLs == nil {
+		return
+	}
+	ec.DeniedACLs = newACLs
+}
+
+func UpdateProNodeACLs(node *models.Node) error {
+	networkNodes, err := logic.GetNetworkNodes(node.Network)
+	if err != nil {
+		return err
+	}
+	if err = adjustNodeAcls(node, networkNodes[:]); err != nil {
+		return err
+	}
+	return nil
+}
+
+// adjustNodeAcls - adjusts ACLs based on a node's default value
+func adjustNodeAcls(node *models.Node, networkNodes []models.Node) error {
+	networkID := nodeacls.NetworkID(node.Network)
+	nodeID := nodeacls.NodeID(node.ID.String())
+	currentACLs, err := nodeacls.FetchAllACLs(networkID)
+	if err != nil {
+		return err
+	}
+
+	for i := range networkNodes {
+		currentNodeID := nodeacls.NodeID(networkNodes[i].ID.String())
+		if currentNodeID == nodeID {
+			continue
+		}
+		// 2 cases
+		// both allow - allow
+		// either 1 denies - deny
+		if node.DoesACLDeny() || networkNodes[i].DoesACLDeny() {
+			currentACLs.ChangeAccess(acls.AclID(nodeID), acls.AclID(currentNodeID), acls.NotAllowed)
+		} else if node.DoesACLAllow() || networkNodes[i].DoesACLAllow() {
+			currentACLs.ChangeAccess(acls.AclID(nodeID), acls.AclID(currentNodeID), acls.Allowed)
+		}
+	}
+
+	_, err = currentACLs.Save(acls.ContainerID(node.Network))
+	return err
+}

+ 3 - 3
ee/logic/failover.go → pro/logic/failover.go

@@ -45,7 +45,7 @@ func determineFailoverCandidate(nodeToBeRelayed *models.Node) *models.Node {
 		return nil
 		return nil
 	}
 	}
 
 
-	currentMetrics, err := logic.GetMetrics(nodeToBeRelayed.ID.String())
+	currentMetrics, err := GetMetrics(nodeToBeRelayed.ID.String())
 	if err != nil || currentMetrics == nil || currentMetrics.Connectivity == nil {
 	if err != nil || currentMetrics == nil || currentMetrics.Connectivity == nil {
 		return nil
 		return nil
 	}
 	}
@@ -84,7 +84,7 @@ func setFailoverNode(failoverNode, node *models.Node) error {
 
 
 // WipeFailover - removes the failover peers of given node (ID)
 // WipeFailover - removes the failover peers of given node (ID)
 func WipeFailover(nodeid string) error {
 func WipeFailover(nodeid string) error {
-	metrics, err := logic.GetMetrics(nodeid)
+	metrics, err := GetMetrics(nodeid)
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
@@ -109,7 +109,7 @@ func WipeAffectedFailoversOnly(nodeid uuid.UUID, network string) error {
 		if currNodeID == nodeid {
 		if currNodeID == nodeid {
 			continue
 			continue
 		}
 		}
-		currMetrics, err := logic.GetMetrics(currNodeID.String())
+		currMetrics, err := GetMetrics(currNodeID.String())
 		if err != nil || currMetrics == nil {
 		if err != nil || currMetrics == nil {
 			continue
 			continue
 		}
 		}

+ 203 - 0
pro/logic/metrics.go

@@ -0,0 +1,203 @@
+package logic
+
+import (
+	"encoding/json"
+	mqtt "github.com/eclipse/paho.mqtt.golang"
+	"github.com/gravitl/netmaker/database"
+	"github.com/gravitl/netmaker/logic"
+	"github.com/gravitl/netmaker/models"
+	"github.com/gravitl/netmaker/mq"
+	"github.com/gravitl/netmaker/netclient/ncutils"
+	"github.com/gravitl/netmaker/servercfg"
+	"golang.org/x/exp/slog"
+	"math"
+	"time"
+)
+
+// GetMetrics - gets the metrics
+func GetMetrics(nodeid string) (*models.Metrics, error) {
+	var metrics models.Metrics
+	record, err := database.FetchRecord(database.METRICS_TABLE_NAME, nodeid)
+	if err != nil {
+		if database.IsEmptyRecord(err) {
+			return &metrics, nil
+		}
+		return &metrics, err
+	}
+	err = json.Unmarshal([]byte(record), &metrics)
+	if err != nil {
+		return &metrics, err
+	}
+	return &metrics, nil
+}
+
+// UpdateMetrics - updates the metrics of a given client
+func UpdateMetrics(nodeid string, metrics *models.Metrics) error {
+	data, err := json.Marshal(metrics)
+	if err != nil {
+		return err
+	}
+	return database.Insert(nodeid, string(data), database.METRICS_TABLE_NAME)
+}
+
+// DeleteMetrics - deletes metrics of a given node
+func DeleteMetrics(nodeid string) error {
+	return database.DeleteRecord(database.METRICS_TABLE_NAME, nodeid)
+}
+
+func MQUpdateMetrics(client mqtt.Client, msg mqtt.Message) {
+	id, err := mq.GetID(msg.Topic())
+	if err != nil {
+		slog.Error("error getting ID sent on ", "topic", msg.Topic(), "error", err)
+		return
+	}
+	currentNode, err := logic.GetNodeByID(id)
+	if err != nil {
+		slog.Error("error getting node", "id", id, "error", err)
+		return
+	}
+	decrypted, decryptErr := mq.DecryptMsg(&currentNode, msg.Payload())
+	if decryptErr != nil {
+		slog.Error("failed to decrypt message for node", "id", id, "error", decryptErr)
+		return
+	}
+
+	var newMetrics models.Metrics
+	if err := json.Unmarshal(decrypted, &newMetrics); err != nil {
+		slog.Error("error unmarshaling payload", "error", err)
+		return
+	}
+
+	shouldUpdate := updateNodeMetrics(&currentNode, &newMetrics)
+
+	if err = logic.UpdateMetrics(id, &newMetrics); err != nil {
+		slog.Error("failed to update node metrics", "id", id, "error", err)
+		return
+	}
+	if servercfg.IsMetricsExporter() {
+		if err := mq.PushMetricsToExporter(newMetrics); err != nil {
+			slog.Error("failed to push node metrics to exporter", "id", currentNode.ID, "error", err)
+		}
+	}
+
+	if newMetrics.Connectivity != nil {
+		err := logic.EnterpriseFailoverFunc(&currentNode)
+		if err != nil {
+			slog.Error("failed to failover for node", "id", currentNode.ID, "network", currentNode.Network, "error", err)
+		}
+	}
+
+	if shouldUpdate {
+		slog.Info("updating peers after node detected connectivity issues", "id", currentNode.ID, "network", currentNode.Network)
+		host, err := logic.GetHost(currentNode.HostID.String())
+		if err == nil {
+			nodes, err := logic.GetAllNodes()
+			if err != nil {
+				return
+			}
+			if err = mq.PublishSingleHostPeerUpdate(host, nodes, nil, nil); err != nil {
+				slog.Warn("failed to publish update after failover peer change for node", "id", currentNode.ID, "network", currentNode.Network, "error", err)
+			}
+		}
+	}
+	slog.Debug("updated node metrics", "id", id)
+}
+
+func updateNodeMetrics(currentNode *models.Node, newMetrics *models.Metrics) bool {
+	if newMetrics.FailoverPeers == nil {
+		newMetrics.FailoverPeers = make(map[string]string)
+	}
+	oldMetrics, err := logic.GetMetrics(currentNode.ID.String())
+	if err != nil {
+		slog.Error("error finding old metrics for node", "id", currentNode.ID, "error", err)
+		return false
+	}
+	if oldMetrics.FailoverPeers == nil {
+		oldMetrics.FailoverPeers = make(map[string]string)
+	}
+
+	var attachedClients []models.ExtClient
+	if currentNode.IsIngressGateway {
+		clients, err := logic.GetExtClientsByID(currentNode.ID.String(), currentNode.Network)
+		if err == nil {
+			attachedClients = clients
+		}
+	}
+	if len(attachedClients) > 0 {
+		// associate ext clients with IDs
+		for i := range attachedClients {
+			extMetric := newMetrics.Connectivity[attachedClients[i].PublicKey]
+			if len(extMetric.NodeName) == 0 &&
+				len(newMetrics.Connectivity[attachedClients[i].ClientID].NodeName) > 0 { // cover server clients
+				extMetric = newMetrics.Connectivity[attachedClients[i].ClientID]
+				if extMetric.TotalReceived > 0 && extMetric.TotalSent > 0 {
+					extMetric.Connected = true
+				}
+			}
+			extMetric.NodeName = attachedClients[i].ClientID
+			delete(newMetrics.Connectivity, attachedClients[i].PublicKey)
+			newMetrics.Connectivity[attachedClients[i].ClientID] = extMetric
+		}
+	}
+
+	// run through metrics for each peer
+	for k := range newMetrics.Connectivity {
+		currMetric := newMetrics.Connectivity[k]
+		oldMetric := oldMetrics.Connectivity[k]
+		currMetric.TotalTime += oldMetric.TotalTime
+		currMetric.Uptime += oldMetric.Uptime // get the total uptime for this connection
+
+		if currMetric.TotalReceived < oldMetric.TotalReceived {
+			currMetric.TotalReceived += oldMetric.TotalReceived
+		} else {
+			currMetric.TotalReceived += int64(math.Abs(float64(currMetric.TotalReceived) - float64(oldMetric.TotalReceived)))
+		}
+		if currMetric.TotalSent < oldMetric.TotalSent {
+			currMetric.TotalSent += oldMetric.TotalSent
+		} else {
+			currMetric.TotalSent += int64(math.Abs(float64(currMetric.TotalSent) - float64(oldMetric.TotalSent)))
+		}
+
+		if currMetric.Uptime == 0 || currMetric.TotalTime == 0 {
+			currMetric.PercentUp = 0
+		} else {
+			currMetric.PercentUp = 100.0 * (float64(currMetric.Uptime) / float64(currMetric.TotalTime))
+		}
+		totalUpMinutes := currMetric.Uptime * ncutils.CheckInInterval
+		currMetric.ActualUptime = time.Duration(totalUpMinutes) * time.Minute
+		delete(oldMetrics.Connectivity, k) // remove from old data
+		newMetrics.Connectivity[k] = currMetric
+
+	}
+
+	// add nodes that need failover
+	nodes, err := logic.GetNetworkNodes(currentNode.Network)
+	if err != nil {
+		slog.Error("failed to retrieve nodes while updating metrics", "error", err)
+		return false
+	}
+	for _, node := range nodes {
+		if !newMetrics.Connectivity[node.ID.String()].Connected &&
+			len(newMetrics.Connectivity[node.ID.String()].NodeName) > 0 &&
+			node.Connected &&
+			len(node.FailoverNode) > 0 &&
+			!node.Failover {
+			newMetrics.FailoverPeers[node.ID.String()] = node.FailoverNode.String()
+		}
+	}
+	shouldUpdate := len(oldMetrics.FailoverPeers) == 0 && len(newMetrics.FailoverPeers) > 0
+	for k, v := range oldMetrics.FailoverPeers {
+		if len(newMetrics.FailoverPeers[k]) > 0 && len(v) == 0 {
+			shouldUpdate = true
+		}
+
+		if len(v) > 0 && len(newMetrics.FailoverPeers[k]) == 0 {
+			newMetrics.FailoverPeers[k] = v
+		}
+	}
+
+	for k := range oldMetrics.Connectivity { // cleanup any left over data, self healing
+		delete(newMetrics.Connectivity, k)
+	}
+	return shouldUpdate
+}

+ 21 - 0
pro/logic/nodes.go

@@ -0,0 +1,21 @@
+package logic
+
+import (
+	celogic "github.com/gravitl/netmaker/logic"
+	"github.com/gravitl/netmaker/models"
+)
+
+// GetNetworkIngresses - gets the gateways of a network
+func GetNetworkIngresses(network string) ([]models.Node, error) {
+	var ingresses []models.Node
+	netNodes, err := celogic.GetNetworkNodes(network)
+	if err != nil {
+		return []models.Node{}, err
+	}
+	for i := range netNodes {
+		if netNodes[i].IsIngressGateway {
+			ingresses = append(ingresses, netNodes[i])
+		}
+	}
+	return ingresses, nil
+}

Энэ ялгаанд хэт олон файл өөрчлөгдсөн тул зарим файлыг харуулаагүй болно