diff --git a/auth/auth.go b/auth/auth.go index ff3c1dc8..7be57345 100644 --- a/auth/auth.go +++ b/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 diff --git a/auth/register_callback.go b/auth/register_callback.go index 66bb7556..3c1e5abf 100644 --- a/auth/register_callback.go +++ b/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 } diff --git a/cli/cmd/network_user/create.go b/cli/cmd/network_user/create.go deleted file mode 100644 index 433f0a1a..00000000 --- a/cli/cmd/network_user/create.go +++ /dev/null @@ -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) -} diff --git a/cli/cmd/network_user/delete.go b/cli/cmd/network_user/delete.go deleted file mode 100644 index 31a3c7e5..00000000 --- a/cli/cmd/network_user/delete.go +++ /dev/null @@ -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) -} diff --git a/cli/cmd/network_user/flags.go b/cli/cmd/network_user/flags.go deleted file mode 100644 index b328ca2c..00000000 --- a/cli/cmd/network_user/flags.go +++ /dev/null @@ -1,10 +0,0 @@ -package network_user - -var ( - accessLevel int - clientLimit int - nodeLimit int - clients string - nodes string - id string -) diff --git a/cli/cmd/network_user/get.go b/cli/cmd/network_user/get.go deleted file mode 100644 index 46e3025c..00000000 --- a/cli/cmd/network_user/get.go +++ /dev/null @@ -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) -} diff --git a/cli/cmd/network_user/list.go b/cli/cmd/network_user/list.go deleted file mode 100644 index c539cf2a..00000000 --- a/cli/cmd/network_user/list.go +++ /dev/null @@ -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) -} diff --git a/cli/cmd/network_user/root.go b/cli/cmd/network_user/root.go deleted file mode 100644 index b7c7facd..00000000 --- a/cli/cmd/network_user/root.go +++ /dev/null @@ -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) - } -} diff --git a/cli/cmd/network_user/update.go b/cli/cmd/network_user/update.go deleted file mode 100644 index c7ece7fc..00000000 --- a/cli/cmd/network_user/update.go +++ /dev/null @@ -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) -} diff --git a/cli/cmd/root.go b/cli/cmd/root.go index ff6c54f1..7376104c 100644 --- a/cli/cmd/root.go +++ b/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()) } diff --git a/cli/cmd/user/create.go b/cli/cmd/user/create.go index 4c24b545..dc1e2a0b 100644 --- a/cli/cmd/user/create.go +++ b/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)) }, } diff --git a/cli/cmd/user/list.go b/cli/cmd/user/list.go index 3dd9e01c..694697a2 100644 --- a/cli/cmd/user/list.go +++ b/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() } diff --git a/cli/cmd/user/update.go b/cli/cmd/user/update.go index cb95adf5..0a79f26a 100644 --- a/cli/cmd/user/update.go +++ b/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)) }, } diff --git a/cli/cmd/usergroup/create.go b/cli/cmd/usergroup/create.go deleted file mode 100644 index f3139d7c..00000000 --- a/cli/cmd/usergroup/create.go +++ /dev/null @@ -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) -} diff --git a/cli/cmd/usergroup/delete.go b/cli/cmd/usergroup/delete.go deleted file mode 100644 index b4172b74..00000000 --- a/cli/cmd/usergroup/delete.go +++ /dev/null @@ -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) -} diff --git a/cli/cmd/usergroup/get.go b/cli/cmd/usergroup/get.go deleted file mode 100644 index e2f1a0bf..00000000 --- a/cli/cmd/usergroup/get.go +++ /dev/null @@ -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) -} diff --git a/cli/cmd/usergroup/root.go b/cli/cmd/usergroup/root.go deleted file mode 100644 index 3269ebaa..00000000 --- a/cli/cmd/usergroup/root.go +++ /dev/null @@ -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) - } -} diff --git a/cli/functions/network_user.go b/cli/functions/network_user.go deleted file mode 100644 index b7563728..00000000 --- a/cli/functions/network_user.go +++ /dev/null @@ -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) -} diff --git a/cli/functions/usergroups.go b/cli/functions/usergroups.go deleted file mode 100644 index 98b90d69..00000000 --- a/cli/functions/usergroups.go +++ /dev/null @@ -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) -} diff --git a/controllers/dns.go b/controllers/dns.go index eb038fc0..8d7be291 100644 --- a/controllers/dns.go +++ b/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 diff --git a/controllers/enrollmentkeys.go b/controllers/enrollmentkeys.go index 157a6f71..78b0f6b6 100644 --- a/controllers/enrollmentkeys.go +++ b/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")) diff --git a/controllers/ext_client.go b/controllers/ext_client.go index 1e2f29f2..898f5098 100644 --- a/controllers/ext_client.go +++ b/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...) - } - } - } + var err error + 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 diff --git a/controllers/hosts.go b/controllers/hosts.go index 17c13b07..a17a0a55 100644 --- a/controllers/hosts.go +++ b/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) diff --git a/controllers/network.go b/controllers/network.go index b3e799b7..3e30d07c 100644 --- a/controllers/network.go +++ b/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")) diff --git a/controllers/network_test.go b/controllers/network_test.go index ab7a476c..8678f408 100644 --- a/controllers/network_test.go +++ b/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) }) } diff --git a/controllers/node.go b/controllers/node.go index dd3c39f2..3ccd23ae 100644 --- a/controllers/node.go +++ b/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 { diff --git a/controllers/user.go b/controllers/user.go index 0a7229c1..69f05d67 100644 --- a/controllers/user.go +++ b/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,16 +218,72 @@ 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, u.UserName, "was made a super admin") + json.NewEncoder(w).Encode(logic.ToReturnUser(u)) +} - logger.Log(1, admin.UserName, "was made a new admin") - json.NewEncoder(w).Encode(logic.ToReturnUser(admin)) +// swagger:route POST /api/users/adm/transfersuperadmin user transferSuperAdmin +// +// Transfers superadmin role to an admin user. +// +// Schemes: https +// +// Security: +// oauth +// +// Responses: +// 200: userBodyResponse +func transferSuperAdmin(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + caller, err := logic.GetUser(r.Header.Get("user")) + if err != nil { + 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 + } + + u.IsSuperAdmin = true + u.IsAdmin = false + err = logic.UpsertUser(*u) + if err != nil { + slog.Error("error updating user to superadmin: ", "user", u.UserName, "error", err.Error()) + logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal")) + return + } + 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 POST /api/users/{username} user createUser @@ -249,83 +299,41 @@ func createAdmin(w http.ResponseWriter, r *http.Request) { // 200: userBodyResponse func createUser(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") - + caller, err := logic.GetUser(r.Header.Get("user")) + if err != nil { + logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal")) + } var user models.User - err := json.NewDecoder(r.Body).Decode(&user) + err = json.NewDecoder(r.Body).Decode(&user) if err != nil { logger.Log(0, user.UserName, "error decoding request body: ", err.Error()) logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest")) return } + 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 + } + 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, user.UserName, "error creating new user: ", - err.Error()) + slog.Error("error creating new user: ", "user", user.UserName, "error", err.Error()) logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest")) return } - logger.Log(1, user.UserName, "was created") + slog.Info("user was created", "username", user.UserName) json.NewEncoder(w).Encode(logic.ToReturnUser(user)) } -// swagger:route PUT /api/users/networks/{username} user updateUserNetworks -// -// Updates the networks of the given user. -// -// Schemes: https -// -// Security: -// oauth -// -// Responses: -// 200: userBodyResponse -func updateUserNetworks(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 { - 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) - if err != nil { - logger.Log(0, 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")) - return - } - logger.Log(1, username, "status was updated") - // re-read and return the new user struct - returnUser, err := logic.GetReturnUser(username) - if err != nil { - logger.Log(0, username, "failed to fetch user: ", err.Error()) - logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal")) - return - } - json.NewEncoder(w).Encode(returnUser) -} - // swagger:route PUT /api/users/{username} user updateUser // // Update a user. @@ -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,27 +369,70 @@ func updateUser(w http.ResponseWriter, r *http.Request) { logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal")) return } + var userchange models.User + // we decode our body request params + err = json.NewDecoder(r.Body).Decode(&userchange) + if err != nil { + slog.Error("failed to decode body", "error ", err.Error()) + logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest")) + return + } + if user.UserName != userchange.UserName { + logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("user in param and request body not matching"), "badrequest")) + return + } + selfUpdate := false + if !ismaster && caller.UserName == user.UserName { + selfUpdate = true + } + + 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 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")) - return - } - userchange.Networks = nil + user, err = logic.UpdateUser(&userchange, user) if err != nil { logger.Log(0, username, @@ -392,57 +444,6 @@ func updateUser(w http.ResponseWriter, r *http.Request) { 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 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()) - logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest")) - return - } - logger.Log(1, username, "was updated (admin)") - json.NewEncoder(w).Encode(logic.ToReturnUser(*user)) -} - // swagger:route DELETE /api/users/{username} user deleteUser // // Delete a 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 -} diff --git a/controllers/user_test.go b/controllers/user_test.go index f98e341f..c99b8c47 100644 --- a/controllers/user_test.go +++ b/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) } diff --git a/functions/helpers_test.go b/functions/helpers_test.go index 0f78a166..4dc3d529 100644 --- a/functions/helpers_test.go +++ b/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) diff --git a/logic/auth.go b/logic/auth.go index 66e1e02d..89eb27e5 100644 --- a/logic/auth.go +++ b/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(¤tNets[i]) - if pro.IsUserAllowed(¤tNets[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(¤tNets[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 + if err = database.Insert(user.UserName, string(data), database.USERS_TABLE_NAME); err != nil { + return err } - - userChange := models.User{ - UserName: currentUser.UserName, - Networks: currentUser.Networks, - IsAdmin: currentUser.IsAdmin, - Password: "", - Groups: currentUser.Groups, - } - - _, 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 -} diff --git a/logic/enrollmentkey.go b/logic/enrollmentkey.go index 53c43320..a1b2ecbe 100644 --- a/logic/enrollmentkey.go +++ b/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 -} diff --git a/logic/enrollmentkey_test.go b/logic/enrollmentkey_test.go index 5cf36586..ace8ef9a 100644 --- a/logic/enrollmentkey_test.go +++ b/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)) - } -} diff --git a/logic/gateway.go b/logic/gateway.go index e25aaa91..2f723469 100644 --- a/logic/gateway.go +++ b/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 +} diff --git a/logic/jwts.go b/logic/jwts.go index 0f355a87..49e15d45 100644 --- a/logic/jwts.go +++ b/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 diff --git a/logic/networks.go b/logic/networks.go index 61ea17d3..a6203ab4 100644 --- a/logic/networks.go +++ b/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 } diff --git a/logic/nodes.go b/logic/nodes.go index a809bbec..1178ca61 100644 --- a/logic/nodes.go +++ b/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 diff --git a/logic/pro/networks.go b/logic/pro/networks.go deleted file mode 100644 index 480c7c1d..00000000 --- a/logic/pro/networks.go +++ /dev/null @@ -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 -} diff --git a/logic/pro/networks_test.go b/logic/pro/networks_test.go deleted file mode 100644 index 2f674e03..00000000 --- a/logic/pro/networks_test.go +++ /dev/null @@ -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) - }) -} diff --git a/logic/pro/networkuser.go b/logic/pro/networkuser.go deleted file mode 100644 index ed758c23..00000000 --- a/logic/pro/networkuser.go +++ /dev/null @@ -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 ¤tNetUser, 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(¤tUsers) - 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 -} diff --git a/logic/pro/networkuser_test.go b/logic/pro/networkuser_test.go deleted file mode 100644 index bf462f49..00000000 --- a/logic/pro/networkuser_test.go +++ /dev/null @@ -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) - }) -} diff --git a/logic/pro/types.go b/logic/pro/types.go deleted file mode 100644 index d2063116..00000000 --- a/logic/pro/types.go +++ /dev/null @@ -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" -) diff --git a/logic/pro/usergroups.go b/logic/pro/usergroups.go deleted file mode 100644 index e7132b3b..00000000 --- a/logic/pro/usergroups.go +++ /dev/null @@ -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(¤tGroups) - 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 -} diff --git a/logic/pro/usergroups_test.go b/logic/pro/usergroups_test.go deleted file mode 100644 index 3ca32cef..00000000 --- a/logic/pro/usergroups_test.go +++ /dev/null @@ -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"))) - }) -} diff --git a/logic/security.go b/logic/security.go index 36dda5e3..37be1f44 100644 --- a/logic/security.go +++ b/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{ diff --git a/logic/users.go b/logic/users.go index cbbade5e..994a71db 100644 --- a/logic/users.go +++ b/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") +} diff --git a/main.go b/main.go index bd3cfc71..1861ee21 100644 --- a/main.go +++ b/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()) diff --git a/migrate/migrate.go b/migrate/migrate.go index 2f064528..22b70251 100644 --- a/migrate/migrate.go +++ b/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() { diff --git a/models/extclient.go b/models/extclient.go index 2b3704c2..229b3f80 100644 --- a/models/extclient.go +++ b/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"` } diff --git a/models/network.go b/models/network.go index b075a3ef..89fb44d4 100644 --- a/models/network.go +++ b/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 diff --git a/models/promodels/networkuser.go b/models/promodels/networkuser.go deleted file mode 100644 index ac34a1c1..00000000 --- a/models/promodels/networkuser.go +++ /dev/null @@ -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{} - } -} diff --git a/models/promodels/pro.go b/models/promodels/pro.go deleted file mode 100644 index 47ebe4b9..00000000 --- a/models/promodels/pro.go +++ /dev/null @@ -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"` -} diff --git a/models/promodels/usergroups.go b/models/promodels/usergroups.go deleted file mode 100644 index e01e6e9c..00000000 --- a/models/promodels/usergroups.go +++ /dev/null @@ -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 diff --git a/models/structs.go b/models/structs.go index d161e283..eed8f447 100644 --- a/models/structs.go +++ b/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 diff --git a/pro/controllers/networkusers.go b/pro/controllers/networkusers.go deleted file mode 100644 index fe5a0fc5..00000000 --- a/pro/controllers/networkusers.go +++ /dev/null @@ -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) -} diff --git a/pro/controllers/usergroups.go b/pro/controllers/usergroups.go deleted file mode 100644 index 2e64d276..00000000 --- a/pro/controllers/usergroups.go +++ /dev/null @@ -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) -} diff --git a/pro/controllers/users.go b/pro/controllers/users.go new file mode 100644 index 00000000..8c10800a --- /dev/null +++ b/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) +} diff --git a/pro/initialize.go b/pro/initialize.go index bcc42c48..4b8abee3 100644 --- a/pro/initialize.go +++ b/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 == diff --git a/servercfg/serverconf.go b/servercfg/serverconf.go index c5135645..c0b2341c 100644 --- a/servercfg/serverconf.go +++ b/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 := "*" diff --git a/serverctl/serverctl.go b/serverctl/serverctl.go index 242c5031..1d445ddf 100644 --- a/serverctl/serverctl.go +++ b/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