Browse Source

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

v0.21.0
Abhishek K 1 year ago
parent
commit
f158cafb01
100 changed files with 1377 additions and 3380 deletions
  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
       description: What version are you running?
       options:
+        - v0.21.0
         - v0.20.6
         - v0.20.5
         - v0.20.4

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

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

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

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

+ 1 - 1
README.md

@@ -16,7 +16,7 @@
 
 <p align="center">
   <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 href="https://hub.docker.com/r/gravitl/netmaker/tags">
     <img src="https://img.shields.io/docker/pulls/gravitl/netmaker?label=downloads" />

+ 7 - 6
auth/auth.go

@@ -10,6 +10,7 @@ import (
 	"time"
 
 	"golang.org/x/crypto/bcrypt"
+	"golang.org/x/exp/slog"
 	"golang.org/x/oauth2"
 
 	"github.com/gorilla/websocket"
@@ -238,9 +239,9 @@ func HandleHeadlessSSO(w http.ResponseWriter, r *http.Request) {
 // == private methods ==
 
 func addUser(email string) error {
-	var hasAdmin, err = logic.HasAdmin()
+	var hasSuperAdmin, err = logic.HasSuperAdmin()
 	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
 	} // generate random password to adapt to current model
 	var newPass, fetchErr = fetchPassValue("")
@@ -251,11 +252,11 @@ func addUser(email string) error {
 		UserName: email,
 		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 {
-			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..?
 		// 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/gravitl/netmaker/logger"
 	"github.com/gravitl/netmaker/logic"
-	"github.com/gravitl/netmaker/logic/pro"
 	"github.com/gravitl/netmaker/logic/pro/netcache"
 	"github.com/gravitl/netmaker/models"
-	"github.com/gravitl/netmaker/models/promodels"
 )
 
 var (
@@ -165,25 +163,5 @@ func isUserIsAllowed(username, network string, shouldAddUser bool) (*models.User
 		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
 }

+ 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/metrics"
 	"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/server"
 	"github.com/gravitl/netmaker/cli/cmd/user"
-	"github.com/gravitl/netmaker/cli/cmd/usergroup"
 	"github.com/spf13/cobra"
 )
 
@@ -52,9 +50,7 @@ func init() {
 	rootCmd.AddCommand(server.GetRoot())
 	rootCmd.AddCommand(ext_client.GetRoot())
 	rootCmd.AddCommand(user.GetRoot())
-	rootCmd.AddCommand(usergroup.GetRoot())
 	rootCmd.AddCommand(metrics.GetRoot())
-	rootCmd.AddCommand(network_user.GetRoot())
 	rootCmd.AddCommand(host.GetRoot())
 	rootCmd.AddCommand(enrollment_key.GetRoot())
 }

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

@@ -1,8 +1,6 @@
 package user
 
 import (
-	"strings"
-
 	"github.com/gravitl/netmaker/cli/functions"
 	"github.com/gravitl/netmaker/models"
 	"github.com/spf13/cobra"
@@ -15,12 +13,6 @@ var userCreateCmd = &cobra.Command{
 	Long:  `Create a new user`,
 	Run: func(cmd *cobra.Command, args []string) {
 		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))
 	},
 }

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

@@ -3,7 +3,6 @@ package user
 import (
 	"os"
 	"strconv"
-	"strings"
 
 	"github.com/gravitl/netmaker/cli/cmd/commons"
 	"github.com/gravitl/netmaker/cli/functions"
@@ -23,9 +22,9 @@ var userListCmd = &cobra.Command{
 			functions.PrettyPrint(data)
 		default:
 			table := tablewriter.NewWriter(os.Stdout)
-			table.SetHeader([]string{"Name", "Admin", "Networks", "Groups"})
+			table.SetHeader([]string{"Name", "SuperAdmin", "Admin"})
 			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()
 		}

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

@@ -1,8 +1,6 @@
 package user
 
 import (
-	"strings"
-
 	"github.com/gravitl/netmaker/cli/functions"
 	"github.com/gravitl/netmaker/models"
 	"github.com/spf13/cobra"
@@ -15,14 +13,6 @@ var userUpdateCmd = &cobra.Command{
 	Long:  `Update a user`,
 	Run: func(cmd *cobra.Command, args []string) {
 		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))
 	},
 }

+ 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:
   netclient:
     container_name: netclient
-    image: 'gravitl/netclient:v0.20.6'
+    image: 'gravitl/netclient:v0.21.0'
     hostname: netmaker-1
     network_mode: host
     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"`
 	LicenseValue               string `yaml:"license_value"`
 	NetmakerTenantID           string `yaml:"netmaker_tenant_id"`
-	IsEE                       string `yaml:"is_ee"`
+	IsPro                      string `yaml:"is_ee" json:"IsEE"`
 	StunPort                   int    `yaml:"stun_port"`
 	StunList                   string `yaml:"stun_list"`
 	TurnServer                 string `yaml:"turn_server"`

+ 6 - 6
controllers/dns.go

@@ -17,12 +17,12 @@ import (
 func dnsHandlers(r *mux.Router) {
 
 	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

+ 29 - 6
controllers/dns_test.go

@@ -7,8 +7,10 @@ import (
 
 	"github.com/google/uuid"
 	"github.com/stretchr/testify/assert"
+	"github.com/txn2/txeh"
 	"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
 
+	"github.com/gravitl/netmaker/functions"
 	"github.com/gravitl/netmaker/logic"
 	"github.com/gravitl/netmaker/models"
 )
@@ -50,6 +52,8 @@ func TestGetNodeDNS(t *testing.T) {
 	deleteAllNetworks()
 	createNet()
 	createHost()
+	err := functions.SetDNSDir()
+	assert.Nil(t, err)
 	t.Run("NoNodes", func(t *testing.T) {
 		dns, _ := logic.GetNodeDNS("skynet")
 		assert.Equal(t, []models.DNSEntry(nil), dns)
@@ -202,22 +206,28 @@ func TestCreateDNS(t *testing.T) {
 func TestSetDNS(t *testing.T) {
 	deleteAllDNS(t)
 	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) {
 		err := logic.SetDNS()
 		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.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) {
 		createNet()
 		err := logic.SetDNS()
 		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.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) {
 		createTestNode()
@@ -400,6 +410,19 @@ func TestValidateDNSCreate(t *testing.T) {
 		assert.NotNil(t, err)
 		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() {

+ 1 - 1
controllers/docs.go

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

+ 3 - 13
controllers/enrollmentkeys.go

@@ -17,7 +17,7 @@ import (
 
 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(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/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"))
 		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{}
 	for _, key := range keys {
-		if !isMasterAdmin && !logic.UserHasNetworksAccess(key.Networks, user) {
-			continue
-		}
+		key := key
 		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())
 			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/logger"
 	"github.com/gravitl/netmaker/logic"
-	"github.com/gravitl/netmaker/logic/pro"
+
 	"github.com/gravitl/netmaker/models"
-	"github.com/gravitl/netmaker/models/promodels"
+
 	"github.com/gravitl/netmaker/mq"
 	"github.com/skip2/go-qrcode"
 	"golang.org/x/exp/slog"
@@ -23,13 +23,13 @@ import (
 
 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}/{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 {
@@ -94,29 +94,18 @@ func getAllExtClients(w http.ResponseWriter, r *http.Request) {
 	networksSlice := []string{}
 	marshalErr := json.Unmarshal([]byte(headerNetworks), &networksSlice)
 	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"))
 		return
 	}
-	clients := []models.ExtClient{}
+
 	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
 	logic.SortExtClient(clients[:])
 	w.WriteHeader(http.StatusOK)
@@ -149,6 +138,14 @@ func getExtClient(w http.ResponseWriter, r *http.Request) {
 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
 		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)
 	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"))
 		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)
 	if err != nil {
@@ -323,7 +326,6 @@ func createExtClient(w http.ResponseWriter, r *http.Request) {
 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
 		return
 	}
-
 	var customExtClient models.CustomExtClient
 
 	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"))
 		return
 	}
-	extclient := logic.UpdateExtClient(&models.ExtClient{}, &customExtClient)
-
-	extclient.IngressGatewayID = nodeid
 	node, err := logic.GetNodeByID(nodeid)
 	if err != nil {
 		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"))
 		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
 	host, err := logic.GetHost(node.HostID.String())
 	if err != nil {
@@ -372,33 +413,13 @@ func createExtClient(w http.ResponseWriter, r *http.Request) {
 		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)
 	w.WriteHeader(http.StatusOK)
 	go func() {
 		if err := mq.PublishPeerUpdate(); err != nil {
 			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())
 		}
 	}()
@@ -431,12 +452,21 @@ func updateExtClient(w http.ResponseWriter, r *http.Request) {
 		return
 	}
 	clientid := params["clientid"]
+	network := params["network"]
 	oldExtClient, err := logic.GetExtClientByName(clientid)
 	if err != nil {
 		slog.Error("failed to retrieve extclient", "user", r.Header.Get("user"), "id", clientid, "error", err)
 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
 		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 err := validateCustomExtClient(&update, false); err != nil {
 			logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
@@ -448,31 +478,12 @@ func updateExtClient(w http.ResponseWriter, r *http.Request) {
 			return
 		}
 	}
-
-	// == PRO ==
-	//networkName := params["network"]
 	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) {
 		sendPeerUpdate = true
 		logic.SetClientACLs(&oldExtClient, update.DeniedACLs)
 	}
-	// == END PRO ==
 
 	if update.Enabled != oldExtClient.Enabled {
 		sendPeerUpdate = true
@@ -535,6 +546,12 @@ func deleteExtClient(w http.ResponseWriter, r *http.Request) {
 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
 		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)
 	if err != nil {
 		logger.Log(0, r.Header.Get("user"),
@@ -543,24 +560,6 @@ func deleteExtClient(w http.ResponseWriter, r *http.Request) {
 		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"])
 	if err != nil {
 		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.")
 }
 
-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
 func validateCustomExtClient(customExtClient *models.CustomExtClient, checkID bool) error {
 	//validate clientid

+ 11 - 3
controllers/hosts.go

@@ -17,12 +17,12 @@ import (
 )
 
 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/{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}", 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(deleteHostFromNetwork))).Methods(http.MethodDelete)
 	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
 		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)
 	if err := logic.DeleteNode(node, forceDelete); err != nil {
 		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.PendingDelete = true
 	// notify node change
-	runUpdates(node, false)
+	mq.RunUpdates(node, false)
 	go func() { // notify of peer change
 		err = mq.PublishDeletedNodePeerUpdate(node)
 		if err != nil {

+ 1 - 1
controllers/migrate.go

@@ -123,7 +123,7 @@ func migrate(w http.ResponseWriter, r *http.Request) {
 			if err != nil {
 				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) {
-	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/{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(updateNetwork))).Methods(http.MethodPut)
 	// ACLs
@@ -42,29 +42,14 @@ func networkHandlers(r *mux.Router) {
 //			Responses:
 //				200: getNetworksSliceResponse
 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
-	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.")
@@ -326,8 +311,7 @@ func updateNetwork(w http.ResponseWriter, r *http.Request) {
 	}
 	// partial update
 	netOld2 := netOld1
-	netOld2.ProSettings = payload.ProSettings
-	_, _, _, _, _, err = logic.UpdateNetwork(&netOld1, &netOld2)
+	_, _, _, err = logic.UpdateNetwork(&netOld1, &netOld2)
 	if err != nil {
 		slog.Info("failed to update network", "user", r.Header.Get("user"), "err", err)
 		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) {
 	database.InitializeDatabase()
 	defer database.CloseDB()
-	logic.CreateAdmin(&models.User{
+	logic.CreateSuperAdmin(&models.User{
 		UserName: "admin",
 		Password: "password",
 		IsAdmin:  true,
-		Networks: []string{},
-		Groups:   []string{},
 	})
 	peerUpdate := make(chan *models.Node)
 	go logic.ManageZombies(context.Background(), peerUpdate)
@@ -91,27 +89,16 @@ func TestSecurityCheck(t *testing.T) {
 
 	os.Setenv("MASTER_KEY", "secretkey")
 	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)
-		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) {
-		networks, username, err := logic.UserPermissions(false, "skynet", "Bearer badkey")
+		username, err := logic.UserPermissions(false, "Bearer badkey")
 		assert.NotNil(t, 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/logger"
 	"github.com/gravitl/netmaker/logic"
-	"github.com/gravitl/netmaker/logic/pro"
 	"github.com/gravitl/netmaker/models"
-	"github.com/gravitl/netmaker/models/promodels"
 	"github.com/gravitl/netmaker/mq"
 	"github.com/gravitl/netmaker/servercfg"
 	"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/{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(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}/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/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 nodeID = ""
-			username, networks, isadmin, errN := logic.VerifyUserToken(authToken)
+			username, issuperadmin, isadmin, errN := logic.VerifyUserToken(authToken)
 			if errN != nil {
-				logic.ReturnErrorResponse(w, r, errorResponse)
+				logic.ReturnErrorResponse(w, r, logic.FormatError(errN, logic.Unauthorized_Msg))
 				return
 			}
 
-			isnetadmin := isadmin
-			if errN == nil && isadmin {
+			isnetadmin := issuperadmin || isadmin
+			if errN == nil && (issuperadmin || isadmin) {
 				nodeID = "mastermac"
 				isAuthorized = true
 				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.
 			if nodeID == "mastermac" {
 				isAuthorized = true
@@ -326,14 +318,6 @@ func getAllNodes(w http.ResponseWriter, r *http.Request) {
 			logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
 			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
 	apiNodes := logic.GetAllNodesAPI(nodes[:])
@@ -343,19 +327,6 @@ func getAllNodes(w http.ResponseWriter, r *http.Request) {
 	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
 //
 // Get an individual node.
@@ -415,7 +386,7 @@ func getNode(w http.ResponseWriter, r *http.Request) {
 		PeerIDs:      hostPeerUpdate.PeerIDs,
 	}
 
-	if servercfg.Is_EE && nodeRequest {
+	if servercfg.IsPro && nodeRequest {
 		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)
 		}
@@ -471,7 +442,7 @@ func createEgressGateway(w http.ResponseWriter, r *http.Request) {
 	go func() {
 		mq.PublishPeerUpdate()
 	}()
-	runUpdates(&node, true)
+	mq.RunUpdates(&node, true)
 }
 
 // swagger:route DELETE /api/nodes/{network}/{nodeid}/deletegateway nodes deleteEgressGateway
@@ -512,7 +483,7 @@ func deleteEgressGateway(w http.ResponseWriter, r *http.Request) {
 	go func() {
 		mq.PublishPeerUpdate()
 	}()
-	runUpdates(&node, true)
+	mq.RunUpdates(&node, true)
 }
 
 // == INGRESS ==
@@ -549,7 +520,7 @@ func createIngressGateway(w http.ResponseWriter, r *http.Request) {
 		return
 	}
 
-	if servercfg.Is_EE && request.Failover {
+	if servercfg.IsPro && request.Failover {
 		if err = logic.EnterpriseResetFailoverFunc(node.Network); err != nil {
 			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)
 	json.NewEncoder(w).Encode(apiNode)
 
-	runUpdates(&node, true)
+	mq.RunUpdates(&node, true)
 }
 
 // swagger:route DELETE /api/nodes/{network}/{nodeid}/deleteingress nodes deleteIngressGateway
@@ -593,7 +564,7 @@ func deleteIngressGateway(w http.ResponseWriter, r *http.Request) {
 		return
 	}
 
-	if servercfg.Is_EE && wasFailover {
+	if servercfg.IsPro && wasFailover {
 		if err = logic.EnterpriseResetFailoverFunc(node.Network); err != nil {
 			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
@@ -655,18 +626,7 @@ func updateNode(w http.ResponseWriter, r *http.Request) {
 		return
 	}
 	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())
 	if err != nil {
 		logger.Log(0, r.Header.Get("user"),
@@ -676,7 +636,7 @@ func updateNode(w http.ResponseWriter, r *http.Request) {
 	}
 	ifaceDelta := logic.IfaceDelta(&currentNode, newNode)
 	aclUpdate := currentNode.DefaultACL != newNode.DefaultACL
-	if ifaceDelta && servercfg.Is_EE {
+	if ifaceDelta && servercfg.IsPro {
 		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)
 		}
@@ -689,13 +649,8 @@ func updateNode(w http.ResponseWriter, r *http.Request) {
 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
 		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() {
 		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)
 	w.WriteHeader(http.StatusOK)
 	json.NewEncoder(w).Encode(apiNode)
-	runUpdates(newNode, ifaceDelta)
+	mq.RunUpdates(newNode, ifaceDelta)
 	go func(aclUpdate, relayupdate bool, newNode *models.Node) {
 		if aclUpdate || relayupdate {
 			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 {
 			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
@@ -743,13 +698,6 @@ func deleteNode(w http.ResponseWriter, r *http.Request) {
 	}
 	forceDelete := r.URL.Query().Get("force") == "true"
 	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 {
 		// cleanup node from relayednodes on relay node
 		relayNode, err := logic.GetNodeByID(node.RelayedBy)
@@ -769,6 +717,16 @@ func deleteNode(w http.ResponseWriter, r *http.Request) {
 		// unset all the relayed nodes
 		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
 	if err := logic.DeleteNode(&node, purge); err != nil {
 		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.")
 	logger.Log(1, r.Header.Get("user"), "Deleted node", nodeid, "from network", params["network"])
 	if !fromNode { // notify node change
-		runUpdates(&node, false)
+		mq.RunUpdates(&node, false)
 	}
 	go func() { // notify of peer change
 		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) {
 	node, err := logic.GetNodeByID(nodeid)
 	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)
 }
 
-// TODO move to EE package? there is a function and a type there for that already
 func getUsage(w http.ResponseWriter, r *http.Request) {
 	type usage struct {
 		Hosts     int `json:"hosts"`
@@ -164,9 +163,9 @@ func getConfig(w http.ResponseWriter, r *http.Request) {
 	// get params
 
 	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)
 	//w.WriteHeader(http.StatusOK)

+ 163 - 150
controllers/user.go

@@ -13,23 +13,19 @@ import (
 	"github.com/gravitl/netmaker/logic"
 	"github.com/gravitl/netmaker/models"
 	"github.com/gravitl/netmaker/servercfg"
+	"golang.org/x/exp/slog"
 )
 
 var (
 	upgrader = websocket.Upgrader{}
 )
 
-// verifyJWT makes logic.VerifyJWT fakeable/mockable in tests
-var verifyJWT = logic.VerifyJWT
-
 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/{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, http.HandlerFunc(deleteUser))).Methods(http.MethodDelete)
 	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)
 }
 
-// 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.
 //
@@ -123,18 +119,18 @@ func authenticateUser(response http.ResponseWriter, request *http.Request) {
 //
 //			Responses:
 //				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")
 
-	hasadmin, err := logic.HasAdmin()
+	hasSuperAdmin, err := logic.HasSuperAdmin()
 	if err != nil {
 		logger.Log(0, "failed to check for admin: ", err.Error())
 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
 		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)
 }
 
-// swagger:route POST /api/users/adm/createadmin user createAdmin
+// swagger:route POST /api/users/adm/createsuperadmin user createAdmin
 //
 // Make a user an admin.
 //
@@ -205,16 +201,14 @@ func getUsers(w http.ResponseWriter, r *http.Request) {
 //
 //			Responses:
 //				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")
 
-	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 {
-
-		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"))
 		return
 	}
@@ -224,21 +218,19 @@ func createAdmin(w http.ResponseWriter, r *http.Request) {
 		return
 	}
 
-	err = logic.CreateAdmin(&admin)
+	err = logic.CreateSuperAdmin(&u)
 	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"))
 		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
 //
@@ -247,32 +239,56 @@ func createAdmin(w http.ResponseWriter, r *http.Request) {
 //
 //			Responses:
 //				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")
-
-	var user models.User
-	err := json.NewDecoder(r.Body).Decode(&user)
+	caller, err := logic.GetUser(r.Header.Get("user"))
 	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"))
 		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 {
-		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
 	}
-	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
 //
@@ -281,49 +297,41 @@ func createUser(w http.ResponseWriter, r *http.Request) {
 //
 //			Responses:
 //				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")
-	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 {
-		logger.Log(0, username,
-			"failed to update user networks: ", err.Error())
 		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 {
-		logger.Log(0, username, "error decoding request body: ",
+		logger.Log(0, user.UserName, "error decoding request body: ",
 			err.Error())
 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
 		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
 	}
-	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 {
-		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
 	}
-	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
@@ -341,18 +349,19 @@ func updateUser(w http.ResponseWriter, r *http.Request) {
 	w.Header().Set("Content-Type", "application/json")
 	var params = mux.Vars(r)
 	// 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"]
-	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)
 	if err != nil {
 		logger.Log(0, username,
@@ -360,86 +369,78 @@ func updateUser(w http.ResponseWriter, r *http.Request) {
 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
 		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
 	// 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())
+		slog.Error("failed to decode body", "error ", err.Error())
 		logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
 		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
 	}
-	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 {
 		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
-	// 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)
 	if err != nil {
 		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"))
 		return
 	}
-	logger.Log(1, username, "was updated (admin)")
+	logger.Log(1, username, "was updated")
 	json.NewEncoder(w).Encode(logic.ToReturnUser(*user))
 }
 
@@ -460,9 +461,32 @@ func deleteUser(w http.ResponseWriter, r *http.Request) {
 
 	// get params
 	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"]
-
+	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)
 	if err != nil {
 		logger.Log(0, username,
@@ -495,14 +519,3 @@ func socketHandler(w http.ResponseWriter, r *http.Request) {
 	// Start handling the session
 	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 (
 	"bytes"
-	"github.com/go-jose/go-jose/v3/json"
-	"github.com/gorilla/mux"
 	"io"
 	"net/http"
 	"net/http/httptest"
 	"testing"
 
+	"github.com/go-jose/go-jose/v3/json"
+	"github.com/gorilla/mux"
+
 	"github.com/stretchr/testify/assert"
 
 	"github.com/gravitl/netmaker/logic"
@@ -47,72 +48,10 @@ func TestCreateAdminNoHashedPassword(t *testing.T) {
 	rec, req := prepareUserRequest(t, user, "")
 
 	// test response
-	createAdmin(rec, req)
+	createSuperAdmin(rec, req)
 	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) {
 	bits, err := json.Marshal(userForBody)
 	assert.Nil(t, err)
@@ -120,14 +59,15 @@ func prepareUserRequest(t *testing.T, userForBody models.User, userNameForParam
 	rec := httptest.NewRecorder()
 	req := httptest.NewRequest("ANY", "https://example.com", body) // only the body matters here
 	req = mux.SetURLVars(req, map[string]string{"username": userNameForParam})
+	req.Header.Set("user", userForBody.UserName)
 	return rec, req
 }
 
 func haveOnlyOneUser(t *testing.T, user models.User) {
 	deleteAllUsers(t)
 	var err error
-	if user.IsAdmin {
-		err = logic.CreateAdmin(&user)
+	if user.IsSuperAdmin {
+		err = logic.CreateSuperAdmin(&user)
 	} else {
 		err = logic.CreateUser(&user)
 	}
@@ -142,7 +82,7 @@ func assertUserNameButNoPassword(t *testing.T, r io.Reader, userName string) {
 	assert.Empty(t, resp.Password)
 }
 
-func TestHasAdmin(t *testing.T) {
+func TestHasSuperAdmin(t *testing.T) {
 	// delete all current users
 	users, _ := logic.GetUsers()
 	for _, user := range users {
@@ -151,31 +91,31 @@ func TestHasAdmin(t *testing.T) {
 		assert.True(t, success)
 	}
 	t.Run("NoUser", func(t *testing.T) {
-		found, err := logic.HasAdmin()
+		found, err := logic.HasSuperAdmin()
 		assert.Nil(t, err)
 		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)
 		assert.Nil(t, err)
-		found, err := logic.HasAdmin()
+		found, err := logic.HasSuperAdmin()
 		assert.Nil(t, err)
 		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)
 		assert.Nil(t, err)
-		found, err := logic.HasAdmin()
+		found, err := logic.HasSuperAdmin()
 		assert.Nil(t, err)
 		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)
 		assert.Nil(t, err)
-		found, err := logic.HasAdmin()
+		found, err := logic.HasSuperAdmin()
 		assert.Nil(t, err)
 		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)
 	var user models.User
-	t.Run("NoAdmin", func(t *testing.T) {
+	t.Run("NoSuperAdmin", func(t *testing.T) {
 		user.UserName = "admin"
 		user.Password = "password"
-		err := logic.CreateAdmin(&user)
+		err := logic.CreateSuperAdmin(&user)
 		assert.Nil(t, err)
 	})
-	t.Run("AdminExists", func(t *testing.T) {
+	t.Run("SuperAdminExists", func(t *testing.T) {
 		user.UserName = "admin2"
 		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) {
 	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) {
 		admin, err := logic.GetUser("admin")
@@ -338,7 +278,7 @@ func TestGetUsers(t *testing.T) {
 func TestUpdateUser(t *testing.T) {
 	deleteAllUsers(t)
 	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) {
 		admin, err := logic.UpdateUser(&newuser, &user)
 		assert.EqualError(t, err, "could not find any records")
@@ -381,7 +321,7 @@ func TestUpdateUser(t *testing.T) {
 
 func TestVerifyAuthRequest(t *testing.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
 	t.Run("EmptyUserName", func(t *testing.T) {
 		authRequest.UserName = ""
@@ -402,7 +342,7 @@ func TestVerifyAuthRequest(t *testing.T) {
 		authRequest.Password = "password"
 		jwt, err := logic.VerifyAuthRequest(authRequest)
 		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) {
 		user.IsAdmin = false
@@ -417,7 +357,7 @@ func TestVerifyAuthRequest(t *testing.T) {
 		assert.Nil(t, err)
 	})
 	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 {
 			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) {
 	database.InitializeDatabase()
 	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)
 	go logic.ManageZombies(context.Background(), peerUpdate)

+ 5 - 5
go.mod

@@ -4,9 +4,9 @@ go 1.19
 
 require (
 	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/google/uuid v1.3.0
+	github.com/google/uuid v1.3.1
 	github.com/gorilla/handlers v1.5.1
 	github.com/gorilla/mux v1.8.0
 	github.com/lib/pq v1.10.9
@@ -14,20 +14,20 @@ require (
 	github.com/rqlite/gorqlite v0.0.0-20210514125552-08ff1e76b22f
 	github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e
 	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/net v0.14.0 // indirect
 	golang.org/x/oauth2 v0.11.0
 	golang.org/x/sys v0.11.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
 	gopkg.in/yaml.v3 v3.0.1
 )
 
 require (
 	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
 )
 

+ 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/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns=
 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/go.mod h1:ZpHUsHBucTUj6WOkrP4E20UPynbLZzhTQ1XKCXkxyPc=
 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/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/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/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
 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/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.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/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/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q=
 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/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
 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/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4=
 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-sqlite3 v1.14.17 h1:mCRHCLDUBXgpKAqIKsaAaAsrAlbkeomtRFKXh2L6YIM=
 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/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
 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.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
 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/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=
 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-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/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/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w=
 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-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/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI=
 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/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/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-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/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.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/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
 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-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/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
 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
       containers:
       - name: netclient
-        image: gravitl/netclient:v0.20.6
+        image: gravitl/netclient:v0.21.0
         env:
         - name: TOKEN
           value: "TOKEN_VALUE"

+ 1 - 1
k8s/client/netclient.yaml

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

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

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

+ 25 - 181
logic/auth.go

@@ -11,14 +11,11 @@ import (
 
 	"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/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)
 	if err != nil {
@@ -34,7 +31,7 @@ func HasAdmin() (bool, error) {
 		if err != nil {
 			continue
 		}
-		if user.IsAdmin {
+		if user.IsSuperAdmin {
 			return true, nil
 		}
 	}
@@ -85,9 +82,8 @@ func CreateUser(user *models.User) error {
 	// set password to encrypted password
 	user.Password = string(hash)
 
-	tokenString, _ := CreateProUserJWT(user.UserName, user.Networks, user.Groups, user.IsAdmin)
+	tokenString, _ := CreateUserJWT(user.UserName, user.IsSuperAdmin, user.IsAdmin)
 	if tokenString == "" {
-		// logic.ReturnErrorResponse(w, r, errorResponse)
 		return err
 	}
 
@@ -103,56 +99,20 @@ func CreateUser(user *models.User) error {
 		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
 }
 
-// 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 {
 		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
@@ -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).
 	record, err := database.FetchRecord(database.USERS_TABLE_NAME, authRequest.UserName)
 	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 {
 		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
-	tokenString, _ := CreateProUserJWT(authRequest.UserName, result.Networks, result.Groups, result.IsAdmin)
+	tokenString, _ := CreateUserJWT(authRequest.UserName, result.IsSuperAdmin, result.IsAdmin)
 	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 {
 		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
@@ -249,16 +164,13 @@ func UpdateUser(userchange, user *models.User) (*models.User, error) {
 	}
 
 	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
 	}
-	if len(userchange.Networks) > 0 {
-		user.Networks = userchange.Networks
-	}
-	if len(userchange.Groups) > 0 {
-		user.Groups = userchange.Groups
-	}
 	if userchange.Password != "" {
 		// encrypt that password so we never see it again
 		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
 	}
-
-	if (userchange.IsAdmin != user.IsAdmin) && !user.IsAdmin {
-		user.IsAdmin = userchange.IsAdmin
-	}
+	user.IsAdmin = userchange.IsAdmin
 
 	err := ValidateUser(user)
 	if err != nil {
@@ -326,23 +235,6 @@ func DeleteUser(user string) (bool, error) {
 		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
 }
 
@@ -414,51 +306,3 @@ func IsStateValid(state string) (string, bool) {
 func delState(state string) error {
 	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 {
 		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
 	}
-	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
 func SortExtClient(unsortedExtClient []models.ExtClient) {

+ 15 - 1
logic/dns.go

@@ -3,6 +3,7 @@ package logic
 import (
 	"encoding/json"
 	"os"
+	"regexp"
 	"sort"
 
 	validator "github.com/go-playground/validator/v10"
@@ -14,7 +15,10 @@ import (
 
 // SetDNS - sets the dns on file
 func SetDNS() error {
-	hostfile := txeh.Hosts{}
+	hostfile, err := txeh.NewHosts(&txeh.HostsConfig{})
+	if err != nil {
+		return err
+	}
 	var corefilestring string
 	networks, err := GetNetworks()
 	if err != nil && !database.IsEmptyRecord(err) {
@@ -203,6 +207,11 @@ func ValidateDNSCreate(entry models.DNSEntry) error {
 
 	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 {
 		num, err := GetDNSEntryNum(entry.Name, entry.Network)
 		return err == nil && num == 0
@@ -227,6 +236,11 @@ func ValidateDNSUpdate(change models.DNSEntry, entry models.DNSEntry) error {
 
 	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 {
 		//if name & net not changing name we are good
 		if change.Name == entry.Name && change.Network == entry.Network {

+ 0 - 14
logic/enrollmentkey.go

@@ -5,7 +5,6 @@ import (
 	"encoding/json"
 	"errors"
 	"fmt"
-	"golang.org/x/exp/slices"
 	"time"
 
 	"github.com/gravitl/netmaker/database"
@@ -224,16 +223,3 @@ func getEnrollmentKeysMap() (map[string]*models.EnrollmentKey, error) {
 	}
 	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()
 }
-
-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.IngressDNS = ingress.ExtclientDNS
 	node.SetLastModified()
-	if ingress.Failover && servercfg.Is_EE {
+	if ingress.Failover && servercfg.IsPro {
 		node.Failover = true
 	}
 	err = UpsertNode(&node)
@@ -159,6 +159,25 @@ func CreateIngressGateway(netid string, nodeid string, ingress models.IngressReq
 	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
 func DeleteIngressGateway(nodeid string) (models.Node, bool, []models.ExtClient, error) {
 	removedClients := []models.ExtClient{}
@@ -210,3 +229,20 @@ func DeleteGatewayExtClients(gatewayID string, networkName string) error {
 	}
 	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)
 	}
 	go func() {
-		if servercfg.Is_EE {
+		if servercfg.IsPro {
 			if clients, err := GetNetworkExtClients(n.Network); err != nil {
 				for i := range clients {
 					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
 }
 
-// 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
-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)
 	claims := &models.UserClaims{
-		UserName: username,
-		Networks: networks,
-		IsAdmin:  isadmin,
+		UserName:     username,
+		IsSuperAdmin: issuperadmin,
+		IsAdmin:      isadmin,
 		RegisteredClaims: jwt.RegisteredClaims{
 			Issuer:    "Netmaker",
 			Subject:   fmt.Sprintf("user|%s", username),
@@ -100,23 +76,23 @@ func CreateUserJWT(username string, networks []string, isadmin bool) (response s
 }
 
 // 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 := ""
 	tokenSplit := strings.Split(bearerToken, " ")
 	if len(tokenSplit) > 1 {
 		token = tokenSplit[1]
 	} else {
-		return "", nil, false, errors.New("invalid auth header")
+		return "", false, false, errors.New("invalid auth header")
 	}
 	return VerifyUserToken(token)
 }
 
 // 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{}
 
 	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) {
@@ -128,15 +104,15 @@ func VerifyUserToken(tokenString string) (username string, networks []string, is
 		// check that user exists
 		user, err = GetUser(claims.UserName)
 		if err != nil {
-			return "", nil, false, err
+			return "", false, false, err
 		}
 
 		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")
 	}
-	return "", nil, false, err
+	return "", false, false, err
 }
 
 // VerifyHostToken - [hosts] Only

+ 7 - 28
logic/metrics.go

@@ -1,39 +1,18 @@
 package logic
 
 import (
-	"encoding/json"
-
-	"github.com/gravitl/netmaker/database"
 	"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/logger"
 	"github.com/gravitl/netmaker/logic/acls/nodeacls"
-	"github.com/gravitl/netmaker/logic/pro"
 	"github.com/gravitl/netmaker/models"
 	"github.com/gravitl/netmaker/validation"
 )
@@ -51,9 +50,6 @@ func DeleteNetwork(network string) error {
 	nodeCount, err := GetNetworkNonServerNodeCount(network)
 	if nodeCount == 0 || database.IsEmptyRecord(err) {
 		// 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 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.SetNetworkLastModified()
 
-	pro.AddProNetDefaults(&network)
-
-	if len(network.ProSettings.AllowedGroups) == 0 {
-		network.ProSettings.AllowedGroups = []string{pro.DEFAULT_ALLOWED_GROUPS}
-	}
-
 	err := ValidateNetwork(&network, false)
 	if err != nil {
 		//logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
 		return models.Network{}, err
 	}
 
-	if err = pro.InitializeNetworkUsers(network.NetID); err != nil {
-		return models.Network{}, err
-	}
-
 	data, err := json.Marshal(&network)
 	if err != nil {
 		return models.Network{}, err
@@ -106,11 +92,6 @@ func CreateNetwork(network models.Network) (models.Network, error) {
 		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
 }
 
@@ -302,28 +283,24 @@ func IsNetworkNameUnique(network *models.Network) (bool, error) {
 }
 
 // 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 {
-		return false, false, false, nil, nil, err
+		return false, false, false, err
 	}
 	if newNetwork.NetID == currentNetwork.NetID {
 		hasrangeupdate4 := newNetwork.AddressRange != currentNetwork.AddressRange
 		hasrangeupdate6 := newNetwork.AddressRange6 != currentNetwork.AddressRange6
 		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)
 		if err != nil {
-			return false, false, false, nil, nil, err
+			return false, false, false, err
 		}
 		newNetwork.SetNetworkLastModified()
 		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
-	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
@@ -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
 }
 

+ 4 - 43
logic/nodes.go

@@ -16,8 +16,6 @@ import (
 	"github.com/gravitl/netmaker/logger"
 	"github.com/gravitl/netmaker/logic/acls"
 	"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/servercfg"
 	"github.com/gravitl/netmaker/validation"
@@ -150,7 +148,7 @@ func UpdateNode(currentNode *models.Node, newNode *models.Node) error {
 		}
 	}
 	nodeACLDelta := currentNode.DefaultACL != newNode.DefaultACL
-	newNode.Fill(currentNode, servercfg.Is_EE)
+	newNode.Fill(currentNode, servercfg.IsPro)
 
 	// check for un-settable server values
 	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 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())
 				return err
 			}
@@ -208,7 +206,7 @@ func DeleteNode(node *models.Node, purge bool) error {
 	if err := DissasociateNodeFromHost(node, host); err != nil {
 		return err
 	}
-	if servercfg.Is_EE {
+	if servercfg.IsPro {
 		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)
 		}
@@ -236,12 +234,6 @@ func deleteNodeByID(node *models.Node) error {
 	if servercfg.IsDNSMode() {
 		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()))
 	if err != nil {
 		// 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
 }
 
-// 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
 func GetAllNodesAPI(nodes []models.Node) []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
 func createNode(node *models.Node) error {
 	// 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
 	}
 
-	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())
 		return err
 	}
@@ -600,5 +563,3 @@ func SortApiNodes(unsortedNodes []models.ApiNode) {
 		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{
 					Interfaces: peerHost.Interfaces,
 					ListenPort: peerPort,
+					IsStatic:   peerHost.IsStatic,
 				}
 				nodePeer = peerConfig
 			} else {
@@ -191,6 +192,7 @@ func GetPeerUpdateForHost(network string, host *models.Host, allNodes []models.N
 				hostPeerUpdate.HostNetworkInfo[peerHost.PublicKey.String()] = models.HostNetworkInfo{
 					Interfaces: peerHost.Interfaces,
 					ListenPort: peerPort,
+					IsStatic:   peerHost.IsStatic,
 				}
 				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 {
 			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() {
-		allowedips = append(allowedips, getAllowedIpsForRelayed(node, peer)...)
-
+		allowedips = append(allowedips, GetAllowedIpsForRelayed(node, peer)...)
 	}
 	return allowedips
 }
 
-func getEgressIPs(peer *models.Node) []net.IPNet {
+func GetEgressIPs(peer *models.Node) []net.IPNet {
 
 	peerHost, err := GetHost(peer.HostID.String())
 	if err != nil {
@@ -461,50 +441,15 @@ func getNodeAllowedIPs(peer, node *models.Node) []net.IPNet {
 	// handle egress gateway peers
 	if peer.IsEgressGateway {
 		//hasGateway = true
-		egressIPs := getEgressIPs(peer)
+		egressIPs := GetEgressIPs(peer)
 		allowedips = append(allowedips, egressIPs...)
 	}
 	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
 }
 
-// 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 {
 	cidr := net.CIDRMask(32, 32)
 	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
 
 import (
-	"errors"
-	"fmt"
-	"net"
-
-	"github.com/gravitl/netmaker/logger"
 	"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
 
 import (
-	"encoding/json"
 	"net/http"
 	"strings"
 
 	"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/promodels"
 	"github.com/gravitl/netmaker/servercfg"
 )
 
 const (
-	// ALL_NETWORK_ACCESS - represents all networks
-	ALL_NETWORK_ACCESS = "THIS_USER_HAS_ALL"
-
-	master_uname     = "masteradministrator"
+	MasterUser       = "masteradministrator"
 	Forbidden_Msg    = "forbidden"
 	Forbidden_Err    = models.Error(Forbidden_Msg)
 	Unauthorized_Msg = "unauthorized"
@@ -28,152 +21,46 @@ const (
 func SecurityCheck(reqAdmin 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)
 		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 {
-			ReturnErrorResponse(w, r, errorResponse)
+			ReturnErrorResponse(w, r, FormatError(err, err.Error()))
 			return
 		}
 		// detect masteradmin
-		if len(networks) > 0 && networks[0] == ALL_NETWORK_ACCESS {
+		if username == MasterUser {
 			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("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)
 	}
 }
 
 // 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 authToken = ""
-	userNetworks := []string{}
 
 	if len(tokenSplit) < 2 {
-		return userNetworks, "", Unauthorized_Err
+		return "", Unauthorized_Err
 	} else {
 		authToken = tokenSplit[1]
 	}
 	//all endpoints here require master so not as complicated
 	if authenticateMaster(authToken) {
 		// 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 {
-		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
@@ -181,23 +68,6 @@ func authenticateMaster(tokenString string) bool {
 	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 {
 	return func(w http.ResponseWriter, r *http.Request) {
 		var errorResponse = models.ErrorResponse{

+ 4 - 10
logic/telemetry.go

@@ -13,7 +13,6 @@ import (
 
 // flags to keep for telemetry
 var isFreeTier bool
-var isEE bool
 
 // posthog_pub_key - Key for sending data to PostHog
 const posthog_pub_key = "phc_1vEXhPOA1P7HP5jP2dVU9xDTUqXHAelmtravyZ1vvES"
@@ -21,14 +20,8 @@ const posthog_pub_key = "phc_1vEXhPOA1P7HP5jP2dVU9xDTUqXHAelmtravyZ1vvES"
 // posthog_endpoint - Endpoint of PostHog server
 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
-// (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) {
 	isFreeTier = freeTierFlag
 }
@@ -73,7 +66,7 @@ func sendTelemetry() error {
 			Set("docker", d.Count.Docker).
 			Set("k8s", d.Count.K8S).
 			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),
 	})
 }
@@ -82,6 +75,7 @@ func sendTelemetry() error {
 func fetchTelemetryData() (telemetryData, error) {
 	var data telemetryData
 
+	data.IsPro = servercfg.IsPro
 	data.ExtClients = getDBLength(database.EXT_CLIENT_TABLE_NAME)
 	data.Users = getDBLength(database.USERS_TABLE_NAME)
 	data.Networks = getDBLength(database.NETWORKS_TABLE_NAME)
@@ -176,7 +170,7 @@ type telemetryData struct {
 	Networks   int
 	Servers    int
 	Version    string
-	IsEE       bool
+	IsPro      bool
 	IsFreeTier bool
 }
 

+ 21 - 56
logic/users.go

@@ -2,13 +2,11 @@ package logic
 
 import (
 	"encoding/json"
+	"errors"
 	"sort"
 
 	"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/promodels"
 )
 
 // GetUser - gets a user
@@ -43,64 +41,17 @@ func GetReturnUser(username string) (models.ReturnUser, error) {
 // ToReturnUser - gets a user as a return user
 func ToReturnUser(user models.User) 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
 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
 	})
 }
+
+// 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/logger"
 	"github.com/gravitl/netmaker/logic"
-	"github.com/gravitl/netmaker/logic/pro"
 	"github.com/gravitl/netmaker/migrate"
 	"github.com/gravitl/netmaker/models"
 	"github.com/gravitl/netmaker/mq"
@@ -29,7 +28,7 @@ import (
 	"golang.org/x/exp/slog"
 )
 
-var version = "v0.20.6"
+var version = "v0.21.0"
 
 // Start DB Connection and start API Request Handler
 func main() {
@@ -41,7 +40,7 @@ func main() {
 	initialize()                       // initial db and acls
 	setGarbageCollection()
 	setVerbosity()
-	if servercfg.DeployedByOperator() && !servercfg.Is_EE {
+	if servercfg.DeployedByOperator() && !servercfg.IsPro {
 		logic.SetFreeTierLimits()
 	}
 	defer database.CloseDB()
@@ -83,10 +82,6 @@ func initialize() { // Client Mode Prereq Check
 
 	logic.SetJWTSecret()
 
-	if err = pro.InitializeGroups(); err != nil {
-		logger.Log(0, "could not initialize default user group, \"*\"")
-	}
-
 	err = logic.TimerCheckpoint()
 	if err != nil {
 		logger.Log(1, "Timer error occurred: ", err.Error())

+ 2 - 4
main_ee.go

@@ -3,10 +3,8 @@
 
 package main
 
-import (
-	"github.com/gravitl/netmaker/ee"
-)
+import "github.com/gravitl/netmaker/pro"
 
 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/logger"
+	"github.com/gravitl/netmaker/logic"
 	"github.com/gravitl/netmaker/models"
+	"golang.org/x/exp/slog"
 )
 
 // Run - runs all migrations
 func Run() {
 	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() {

+ 4 - 4
models/dnsEntry.go

@@ -42,8 +42,8 @@ type DNSUpdate struct {
 
 // DNSEntry - a DNS entry represented as 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"`
 	OwnerID                string              `json:"ownerid" bson:"ownerid"`
 	DeniedACLs             map[string]struct{} `json:"deniednodeacls" bson:"acls,omitempty"`
+	RemoteAccessClientID   string              `json:"remote_access_client_id"`
 }
 
 // CustomExtClient - struct for CustomExtClient params
 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 {
 	Interfaces []Iface `json:"interfaces" yaml:"interfaces"`
 	ListenPort int     `json:"listen_port" yaml:"listen_port"`
+	IsStatic   bool    `json:"is_static"`
 }
 
 // PeerMap - peer map for ids and addresses in metrics

+ 16 - 19
models/network.go

@@ -2,30 +2,27 @@ package models
 
 import (
 	"time"
-
-	"github.com/gravitl/netmaker/models/promodels"
 )
 
 // Network Struct - contains info for a given unique network
 // At  some point, need to replace all instances of Name with something else like  Identifier
 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

+ 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)
-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.HostID = currentNode.HostID
 	// 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 {
 		newNode.RelayedNodes = currentNode.RelayedNodes
 	}
-	if newNode.IsRelay != currentNode.IsRelay && isEE {
+	if newNode.IsRelay != currentNode.IsRelay && isPro {
 		newNode.IsRelay = currentNode.IsRelay
 	}
-	if newNode.IsRelayed == currentNode.IsRelayed && isEE {
+	if newNode.IsRelayed == currentNode.IsRelayed && isPro {
 		newNode.IsRelayed = currentNode.IsRelayed
 	}
 	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
 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
 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
@@ -47,13 +47,33 @@ type UserAuthParams struct {
 
 // UserClaims - user claims struct
 type UserClaims struct {
-	IsAdmin  bool
-	UserName string
-	Networks []string
-	Groups   []string
+	IsAdmin      bool
+	IsSuperAdmin bool
+	UserName     string
 	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
 type SuccessfulUserLoginResponse struct {
 	UserName  string
@@ -238,7 +258,7 @@ type ServerConfig struct {
 	MQPassword  string       `yaml:"mq_password"`
 	Server      string       `yaml:"server"`
 	Broker      string       `yaml:"broker"`
-	Is_EE       bool         `yaml:"isee"`
+	IsPro       bool         `yaml:"isee" json:"Is_EE"`
 	StunPort    int          `yaml:"stun_port"`
 	StunList    []StunServer `yaml:"stun_list"`
 	TrafficKey  []byte       `yaml:"traffickey"`

+ 20 - 174
mq/handlers.go

@@ -3,12 +3,10 @@ package mq
 import (
 	"encoding/json"
 	"fmt"
-	"math"
-	"time"
-
 	mqtt "github.com/eclipse/paho.mqtt.golang"
 	"github.com/google/uuid"
 	"github.com/gravitl/netmaker/database"
+	"github.com/gravitl/netmaker/logger"
 	"github.com/gravitl/netmaker/logic"
 	"github.com/gravitl/netmaker/logic/hostactions"
 	"github.com/gravitl/netmaker/models"
@@ -18,6 +16,19 @@ import (
 	"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
 func DefaultHandler(client mqtt.Client, msg mqtt.Message) {
 	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
 func UpdateNode(client mqtt.Client, msg mqtt.Message) {
-	id, err := getID(msg.Topic())
+	id, err := GetID(msg.Topic())
 	if err != nil {
 		slog.Error("error getting node.ID ", "topic", msg.Topic(), "error", err)
 		return
@@ -35,7 +46,7 @@ func UpdateNode(client mqtt.Client, msg mqtt.Message) {
 		slog.Error("error getting node", "id", id, "error", err)
 		return
 	}
-	decrypted, decryptErr := decryptMsg(&currentNode, msg.Payload())
+	decrypted, decryptErr := DecryptMsg(&currentNode, msg.Payload())
 	if decryptErr != nil {
 		slog.Error("failed to decrypt message for node", "id", id, "error", decryptErr)
 		return
@@ -47,7 +58,7 @@ func UpdateNode(client mqtt.Client, msg mqtt.Message) {
 	}
 
 	ifaceDelta := logic.IfaceDelta(&currentNode, &newNode)
-	if servercfg.Is_EE && ifaceDelta {
+	if servercfg.IsPro && ifaceDelta {
 		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)
 		}
@@ -68,7 +79,7 @@ func UpdateNode(client mqtt.Client, msg mqtt.Message) {
 
 // UpdateHost  message Handler -- handles host updates from clients
 func UpdateHost(client mqtt.Client, msg mqtt.Message) {
-	id, err := getID(msg.Topic())
+	id, err := GetID(msg.Topic())
 	if err != nil {
 		slog.Error("error getting host.ID sent on ", "topic", msg.Topic(), "error", err)
 		return
@@ -183,77 +194,11 @@ func UpdateHost(client mqtt.Client, msg mqtt.Message) {
 			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
 func ClientPeerUpdate(client mqtt.Client, msg mqtt.Message) {
-	id, err := getID(msg.Topic())
+	id, err := GetID(msg.Topic())
 	if err != nil {
 		slog.Error("error getting node.ID sent on ", "topic", msg.Topic(), "error", err)
 		return
@@ -263,7 +208,7 @@ func ClientPeerUpdate(client mqtt.Client, msg mqtt.Message) {
 		slog.Error("error getting node", "id", id, "error", err)
 		return
 	}
-	decrypted, decryptErr := decryptMsg(&currentNode, msg.Payload())
+	decrypted, decryptErr := DecryptMsg(&currentNode, msg.Payload())
 	if decryptErr != nil {
 		slog.Error("failed to decrypt message for node", "id", id, "error", decryptErr)
 		return
@@ -281,105 +226,6 @@ func ClientPeerUpdate(client mqtt.Client, msg mqtt.Message) {
 	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 {
 	dns := models.DNSUpdate{
 		Action: models.DNSInsert,

+ 6 - 4
mq/publishers.go

@@ -79,8 +79,10 @@ func PublishDeletedClientPeerUpdate(delClient *models.ExtClient) error {
 	}
 	for _, host := range hosts {
 		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
@@ -258,7 +260,7 @@ func PublishReplaceDNS(oldNode, newNode *models.Node, host *models.Host) error {
 }
 
 // PublishExtClientDNS publish dns update for new extclient
-func PublishExtCLientDNS(client *models.ExtClient) error {
+func PublishExtClientDNS(client *models.ExtClient) error {
 	errMsgs := models.DNSError{}
 	dns := models.DNSUpdate{
 		Action:  models.DNSInsert,
@@ -342,7 +344,7 @@ func PublishHostDNSUpdate(old, new *models.Host, networks []string) error {
 	return nil
 }
 
-func pushMetricsToExporter(metrics models.Metrics) error {
+func PushMetricsToExporter(metrics models.Metrics) error {
 	logger.Log(2, "----> Pushing metrics to exporter")
 	data, err := json.Marshal(metrics)
 	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)
 }
 
-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
 		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
-func getID(topic string) (string, error) {
+func GetID(topic string) (string, error) {
 	parts := strings.Split(topic, "/")
 	count := len(parts)
 	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 (
 	"encoding/json"
+	proLogic "github.com/gravitl/netmaker/pro/logic"
 	"net/http"
 
 	"github.com/gorilla/mux"
@@ -11,7 +12,7 @@ import (
 	"github.com/gravitl/netmaker/models"
 )
 
-// MetricHandlers - How we handle EE Metrics
+// MetricHandlers - How we handle Pro Metrics
 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}", 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 {
 		id := networkNodes[i].ID
-		metrics, err := logic.GetMetrics(id.String())
+		metrics, err := proLogic.GetMetrics(id.String())
 		if err != nil {
 			logger.Log(1, r.Header.Get("user"), "failed to append metrics of node", id.String(), "during network metrics fetch", err.Error())
 			continue
@@ -83,7 +84,7 @@ func getNetworkExtMetrics(w http.ResponseWriter, r *http.Request) {
 	network := params["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 {
 		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"))
@@ -108,7 +109,7 @@ func getNetworkExtMetrics(w http.ResponseWriter, r *http.Request) {
 
 	for i := range ingresses {
 		id := ingresses[i].ID
-		ingressMetrics, err := logic.GetMetrics(id.String())
+		ingressMetrics, err := proLogic.GetMetrics(id.String())
 		if err != nil {
 			logger.Log(1, r.Header.Get("user"), "failed to append external client metrics from ingress node", id.String(), err.Error())
 			continue
@@ -149,7 +150,7 @@ func getAllMetrics(w http.ResponseWriter, r *http.Request) {
 
 	for i := range allNodes {
 		id := allNodes[i].ID
-		metrics, err := logic.GetMetrics(id.String())
+		metrics, err := proLogic.GetMetrics(id.String())
 		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())
 			continue

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

@@ -1,4 +1,4 @@
-package ee_controllers
+package controllers
 
 import (
 	"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 (
 	"encoding/json"
 	"fmt"
+	proLogic "github.com/gravitl/netmaker/pro/logic"
 	"net/http"
 
 	"github.com/gorilla/mux"
@@ -13,7 +14,7 @@ import (
 	"github.com/gravitl/netmaker/mq"
 )
 
-// RelayHandlers - handle EE Relays
+// RelayHandlers - handle Pro Relays
 func RelayHandlers(r *mux.Router) {
 
 	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.NodeID = params["nodeid"]
-	_, relayNode, err := logic.CreateRelay(relayRequest)
+	_, relayNode, err := proLogic.CreateRelay(relayRequest)
 	if err != nil {
 		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))
@@ -73,7 +74,7 @@ func deleteRelay(w http.ResponseWriter, r *http.Request) {
 	var params = mux.Vars(r)
 	nodeid := params["nodeid"]
 	netid := params["network"]
-	updateNodes, node, err := logic.DeleteRelay(netid, nodeid)
+	updateNodes, node, err := proLogic.DeleteRelay(netid, nodeid)
 	if err != nil {
 		logger.Log(0, r.Header.Get("user"), "error decoding request body: ", err.Error())
 		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
 // +build ee
 
-package ee
+package pro
 
 import (
 	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/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"
 	"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,
-		ee_controllers.OnlyServerAPIWhenUnlicensedMiddleware,
+		proControllers.OnlyServerAPIWhenUnlicensedMiddleware,
 	)
 	controller.HttpHandlers = append(
 		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() {
 		// == License Handling ==
@@ -41,19 +40,31 @@ func InitEE() {
 		AddLicenseHooks()
 		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() {
 	nets, err := logic.GetNetworks()
 	if err == nil {
 		for _, net := range nets {
-			err = eelogic.ResetFailover(net.NetID)
+			err = proLogic.ResetFailover(net.NetID)
 			if err != nil {
 				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 `              
  __   __     ______     ______   __    __     ______     __  __     ______     ______    
 /\ "-.\ \   /\  ___\   /\__  _\ /\ "-./  \   /\  __ \   /\ \/ /    /\  ___\   /\  == \   

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

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

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

@@ -1,7 +1,7 @@
 //go:build ee
 // +build ee
 
-package ee
+package pro
 
 import (
 	"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
 	}
 
-	currentMetrics, err := logic.GetMetrics(nodeToBeRelayed.ID.String())
+	currentMetrics, err := GetMetrics(nodeToBeRelayed.ID.String())
 	if err != nil || currentMetrics == nil || currentMetrics.Connectivity == nil {
 		return nil
 	}
@@ -84,7 +84,7 @@ func setFailoverNode(failoverNode, node *models.Node) error {
 
 // WipeFailover - removes the failover peers of given node (ID)
 func WipeFailover(nodeid string) error {
-	metrics, err := logic.GetMetrics(nodeid)
+	metrics, err := GetMetrics(nodeid)
 	if err != nil {
 		return err
 	}
@@ -109,7 +109,7 @@ func WipeAffectedFailoversOnly(nodeid uuid.UUID, network string) error {
 		if currNodeID == nodeid {
 			continue
 		}
-		currMetrics, err := logic.GetMetrics(currNodeID.String())
+		currMetrics, err := GetMetrics(currNodeID.String())
 		if err != nil || currMetrics == nil {
 			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
+}

Some files were not shown because too many files changed in this diff