Browse Source

NET-551: User Mgmt Re-Design (#2547)

* add superadmin role, apis to create superadmin user

* apis to attach and remove user from remote access gateways

* add api to list user's remote client has gateway clients

* remove code related user groups

* remove networks and groups from user model

* refactor user CRUD operations

* fix network permission test

* add superadmin to authorize func

* remove user network and groups from cli

* api to transfer superadmin role

* add api to list users on a ingress gw

* restrict user access to resources on server

* deny request from remote access client if extclient is already created

* fix user tests

* fix static checks

* fix static checks

* add limits to extclient create handler

* set username to superadmin on if masterkey is used

* allow creation of extclients using masterkey

* add migration func to assign superadmin role for existing admin user

* check for superadmin on migration if users are present

* allowe masterkey to extcleint apis

* check ownerid

* format error, on jwt token verification failure return unauthorized rather than forbidden

* user update fix

* move user remote functionality to ee

* fix update user api

* security patch

* initalise ee user handlers

* allow user to use master key to update any user

* use slog

* fix auth user test

* table headers

* remove user role, it's covered in middleware

* setuser defaults fix
Abhishek K 1 year ago
parent
commit
719e0c254d
59 changed files with 809 additions and 2677 deletions
  1. 7 6
      auth/auth.go
  2. 0 22
      auth/register_callback.go
  3. 0 43
      cli/cmd/network_user/create.go
  4. 0 23
      cli/cmd/network_user/delete.go
  5. 0 10
      cli/cmd/network_user/flags.go
  6. 0 27
      cli/cmd/network_user/get.go
  7. 0 27
      cli/cmd/network_user/list.go
  8. 0 28
      cli/cmd/network_user/root.go
  9. 0 43
      cli/cmd/network_user/update.go
  10. 0 4
      cli/cmd/root.go
  11. 0 8
      cli/cmd/user/create.go
  12. 2 3
      cli/cmd/user/list.go
  13. 0 10
      cli/cmd/user/update.go
  14. 0 23
      cli/cmd/usergroup/create.go
  15. 0 23
      cli/cmd/usergroup/delete.go
  16. 0 20
      cli/cmd/usergroup/get.go
  17. 0 28
      cli/cmd/usergroup/root.go
  18. 0 44
      cli/functions/network_user.go
  19. 0 22
      cli/functions/usergroups.go
  20. 6 6
      controllers/dns.go
  21. 3 13
      controllers/enrollmentkeys.go
  22. 87 145
      controllers/ext_client.go
  23. 1 1
      controllers/hosts.go
  24. 10 26
      controllers/network.go
  25. 6 19
      controllers/network_test.go
  26. 9 66
      controllers/node.go
  27. 163 150
      controllers/user.go
  28. 29 89
      controllers/user_test.go
  29. 4 6
      functions/helpers_test.go
  30. 25 181
      logic/auth.go
  31. 0 14
      logic/enrollmentkey.go
  32. 0 74
      logic/enrollmentkey_test.go
  33. 36 0
      logic/gateway.go
  34. 11 35
      logic/jwts.go
  35. 5 37
      logic/networks.go
  36. 0 7
      logic/nodes.go
  37. 0 68
      logic/pro/networks.go
  38. 0 64
      logic/pro/networks_test.go
  39. 0 251
      logic/pro/networkuser.go
  40. 0 110
      logic/pro/networkuser_test.go
  41. 0 20
      logic/pro/types.go
  42. 0 80
      logic/pro/usergroups.go
  43. 0 41
      logic/pro/usergroups_test.go
  44. 13 143
      logic/security.go
  45. 21 56
      logic/users.go
  46. 0 5
      main.go
  47. 39 0
      migrate/migrate.go
  48. 8 6
      models/extclient.go
  49. 16 19
      models/network.go
  50. 0 37
      models/promodels/networkuser.go
  51. 0 10
      models/promodels/pro.go
  52. 0 9
      models/promodels/usergroups.go
  53. 33 13
      models/structs.go
  54. 0 365
      pro/controllers/networkusers.go
  55. 0 73
      pro/controllers/usergroups.go
  56. 268 0
      pro/controllers/users.go
  57. 1 2
      pro/initialize.go
  58. 0 11
      servercfg/serverconf.go
  59. 6 11
      serverctl/serverctl.go

+ 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/models/promodels"
-	proControllers "github.com/gravitl/netmaker/pro/controllers"
-)
-
-// 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) *proControllers.NetworkUserDataMap {
-	return request[proControllers.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)
-}

+ 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

+ 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"))

+ 87 - 145
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,26 +413,6 @@ 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() {
@@ -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

+ 1 - 1
controllers/hosts.go

@@ -17,7 +17,7 @@ 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)

+ 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)
 	})
 }
 

+ 9 - 66
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.
@@ -727,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)
@@ -780,27 +744,6 @@ func deleteNode(w http.ResponseWriter, r *http.Request) {
 	}()
 }
 
-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 {

+ 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)
 		}

+ 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)

+ 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.IsPro {
-				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
-}

+ 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))
-	}
-}

+ 36 - 0
logic/gateway.go

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

+ 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

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

+ 0 - 7
logic/nodes.go

@@ -16,7 +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/models"
 	"github.com/gravitl/netmaker/servercfg"
 	"github.com/gravitl/netmaker/validation"
@@ -235,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

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

+ 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 servercfg.IsPro && 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{

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

+ 0 - 5
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"
@@ -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())

+ 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() {

+ 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"`
 }

+ 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

+ 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

+ 33 - 13
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

+ 0 - 365
pro/controllers/networkusers.go

@@ -1,365 +0,0 @@
-package 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
pro/controllers/usergroups.go

@@ -1,73 +0,0 @@
-package 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)
-}

+ 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)
+}

+ 1 - 2
pro/initialize.go

@@ -25,9 +25,8 @@ func InitPro() {
 	controller.HttpHandlers = append(
 		controller.HttpHandlers,
 		proControllers.MetricHandlers,
-		proControllers.NetworkUsersHandlers,
-		proControllers.UserGroupsHandlers,
 		proControllers.RelayHandlers,
+		proControllers.UserHandlers,
 	)
 	logic.EnterpriseCheckFuncs = append(logic.EnterpriseCheckFuncs, func() {
 		// == License Handling ==

+ 0 - 11
servercfg/serverconf.go

@@ -320,17 +320,6 @@ func GetMasterKey() string {
 	return key
 }
 
-// GetDNSKey - gets the configured dns key of server
-func GetDNSKey() string {
-	key := ""
-	if os.Getenv("DNS_KEY") != "" {
-		key = os.Getenv("DNS_KEY")
-	} else if config.Config.Server.DNSKey != "" {
-		key = config.Config.Server.DNSKey
-	}
-	return key
-}
-
 // GetAllowedOrigin - get the allowed origin
 func GetAllowedOrigin() string {
 	allowedorigin := "*"

+ 6 - 11
serverctl/serverctl.go

@@ -8,7 +8,7 @@ import (
 	"github.com/gravitl/netmaker/logic"
 	"github.com/gravitl/netmaker/logic/acls"
 	"github.com/gravitl/netmaker/logic/acls/nodeacls"
-	"github.com/gravitl/netmaker/logic/pro"
+	"golang.org/x/exp/slog"
 )
 
 const (
@@ -59,10 +59,6 @@ func setNetworkDefaults() error {
 		return err
 	}
 	for _, network := range networks {
-		if err = pro.InitializeNetworkUsers(network.NetID); err != nil {
-			logger.Log(0, "could not initialize NetworkUsers on network", network.NetID)
-		}
-		pro.AddProNetDefaults(&network)
 		update := false
 		newNet := network
 		if strings.Contains(network.NetID, ".") {
@@ -85,7 +81,7 @@ func setNetworkDefaults() error {
 			}
 		} else {
 			network.SetDefaults()
-			_, _, _, _, _, err = logic.UpdateNetwork(&network, &network)
+			_, _, _, err = logic.UpdateNetwork(&network, &network)
 			if err != nil {
 				logger.Log(0, "could not set defaults on network", network.NetID)
 			}
@@ -102,13 +98,12 @@ func setUserDefaults() error {
 	for _, user := range users {
 		updateUser, err := logic.GetUser(user.UserName)
 		if err != nil {
-			logger.Log(0, "could not update user", updateUser.UserName)
+			slog.Error("could not get user", "user", updateUser.UserName, "error", err.Error())
 		}
 		logic.SetUserDefaults(updateUser)
-		copyUser := updateUser
-		copyUser.Password = ""
-		if _, err = logic.UpdateUser(copyUser, updateUser); err != nil {
-			logger.Log(0, "could not update user", updateUser.UserName)
+		err = logic.UpsertUser(*updateUser)
+		if err != nil {
+			slog.Error("could not update user", "user", updateUser.UserName, "error", err.Error())
 		}
 	}
 	return nil