From 11655dfdd65f21bea89a95e662676b05fdbd21a4 Mon Sep 17 00:00:00 2001 From: Abhishek Kondur Date: Wed, 16 Aug 2023 16:34:10 +0530 Subject: [PATCH 01/38] add superadmin role, apis to create superadmin user --- auth/auth.go | 10 ++++---- controllers/network_test.go | 2 +- controllers/user.go | 33 +++++++++++++------------- controllers/user_test.go | 47 +++++++++++++++++++------------------ functions/helpers_test.go | 12 +++++----- logic/auth.go | 20 ++++++++-------- models/structs.go | 11 +++++---- 7 files changed, 68 insertions(+), 67 deletions(-) diff --git a/auth/auth.go b/auth/auth.go index ff3c1dc89..39ee0768b 100644 --- a/auth/auth.go +++ b/auth/auth.go @@ -238,7 +238,7 @@ 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") return err @@ -251,11 +251,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 { + logger.Log(1, "error creating super admin from user,", email, "; user not added") } else { - logger.Log(1, "admin created from user,", email, "; was first user added") + logger.Log(1, "superadmin created from user,", email, "; was first user added") } } else { // otherwise add to db as admin..? // TODO: add ability to add users with preemptive permissions diff --git a/controllers/network_test.go b/controllers/network_test.go index ab7a476c6..63914d31f 100644 --- a/controllers/network_test.go +++ b/controllers/network_test.go @@ -25,7 +25,7 @@ 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, diff --git a/controllers/user.go b/controllers/user.go index 0a7229c17..285c30f00 100644 --- a/controllers/user.go +++ b/controllers/user.go @@ -23,9 +23,8 @@ var ( 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/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) @@ -112,7 +111,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 +122,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 +193,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,15 +204,15 @@ 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: ", + logger.Log(0, u.UserName, "error decoding request body: ", err.Error()) logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest")) return @@ -224,16 +223,16 @@ 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: ", + logger.Log(0, u.UserName, "failed to create admin: ", err.Error()) logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest")) return } - logger.Log(1, admin.UserName, "was made a new admin") - json.NewEncoder(w).Encode(logic.ToReturnUser(admin)) + logger.Log(1, u.UserName, "was made a super admin") + json.NewEncoder(w).Encode(logic.ToReturnUser(u)) } // swagger:route POST /api/users/{username} user createUser @@ -428,7 +427,7 @@ func updateUserAdm(w http.ResponseWriter, r *http.Request) { logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest")) return } - if !user.IsAdmin { + if !user.IsAdmin && !user.IsSuperAdmin { logger.Log(0, username, "not an admin user") logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("not a admin user"), "badrequest")) } diff --git a/controllers/user_test.go b/controllers/user_test.go index f98e341f6..ad98ee5ff 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,7 +48,7 @@ func TestCreateAdminNoHashedPassword(t *testing.T) { rec, req := prepareUserRequest(t, user, "") // test response - createAdmin(rec, req) + createSuperAdmin(rec, req) assertUserNameButNoPassword(t, rec.Body, user.UserName) } @@ -101,7 +102,7 @@ func TestUpdateUserNoHashedPassword(t *testing.T) { func TestUpdateUserAdmNoHashedPassword(t *testing.T) { // prepare existing user base - user1 := models.User{UserName: "dio", Password: "brando", IsAdmin: true} + user1 := models.User{UserName: "dio", Password: "brando", IsSuperAdmin: true} haveOnlyOneUser(t, user1) // prepare request @@ -126,8 +127,8 @@ func prepareUserRequest(t *testing.T, userForBody models.User, userNameForParam 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 +143,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 +152,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 +196,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") }) } diff --git a/functions/helpers_test.go b/functions/helpers_test.go index 0f78a1667..7d5531ea2 100644 --- a/functions/helpers_test.go +++ b/functions/helpers_test.go @@ -25,12 +25,12 @@ 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, + Networks: []string{}, + Groups: []string{}, }) peerUpdate := make(chan *models.Node) go logic.ManageZombies(context.Background(), peerUpdate) diff --git a/logic/auth.go b/logic/auth.go index 6c2b210b8..6d690b266 100644 --- a/logic/auth.go +++ b/logic/auth.go @@ -17,8 +17,8 @@ import ( "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 +34,7 @@ func HasAdmin() (bool, error) { if err != nil { continue } - if user.IsAdmin { + if user.IsSuperAdmin { return true, nil } } @@ -142,17 +142,17 @@ func CreateUser(user *models.User) error { 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 diff --git a/models/structs.go b/models/structs.go index bff547c33..3a39989d8 100644 --- a/models/structs.go +++ b/models/structs.go @@ -24,11 +24,12 @@ 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"` + Networks []string `json:"networks" bson:"networks"` + IsAdmin bool `json:"isadmin" bson:"isadmin"` + IsSuperAdmin bool `json:"super_admin"` + Groups []string `json:"groups" bson:"groups" yaml:"groups"` } // ReturnUser - return user struct From f66cd3d4a9b4da25c96ad754f4cf2da304d88244 Mon Sep 17 00:00:00 2001 From: Abhishek Kondur Date: Thu, 17 Aug 2023 12:47:13 +0530 Subject: [PATCH 02/38] apis to attach and remove user from remote access gateways --- controllers/user.go | 147 ++++++++++++++++++++++++++++++++++++++++++++ logic/auth.go | 11 ++++ models/structs.go | 32 +++++----- 3 files changed, 176 insertions(+), 14 deletions(-) diff --git a/controllers/user.go b/controllers/user.go index 285c30f00..d09abc9e7 100644 --- a/controllers/user.go +++ b/controllers/user.go @@ -13,6 +13,7 @@ import ( "github.com/gravitl/netmaker/logic" "github.com/gravitl/netmaker/models" "github.com/gravitl/netmaker/servercfg" + "golang.org/x/exp/slog" ) var ( @@ -26,6 +27,9 @@ func userHandlers(r *mux.Router) { 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/authenticate", authenticateUser).Methods(http.MethodPost) + r.HandleFunc("/api/users/{username}/remote_access_gw", attachUserToRemoteAccessGw).Methods(http.MethodPost) + r.HandleFunc("/api/users/{username}/remote_access_gw", removeUserFromRemoteAccessGW).Methods(http.MethodDelete) + r.HandleFunc("/api/nodes/user/remote_access_gws", logic.SecurityCheck(false, http.HandlerFunc(getUserRemoteAccessGws))).Methods(http.MethodGet) 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) @@ -165,6 +169,149 @@ func getUser(w http.ResponseWriter, r *http.Request) { json.NewEncoder(w).Encode(user) } +// 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 { + logger.Log(0, username, "failed to fetch user: ", err.Error()) + logic.ReturnErrorResponse(w, r, logic.FormatError(fmt.Errorf("failed to fetch user", username, err), "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 gaetway node", 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.UpdateUserV1(*user) + if err != nil { + slog.Error("failed to update user gateways", "user", username, "error", err) + logic.ReturnErrorResponse(w, r, logic.FormatError(fmt.Errorf("failed to fetch remote access gaetway node", err), "badrequest")) + return + } + json.NewEncoder(w).Encode(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", username, err), "badrequest")) + return + } + delete(user.RemoteGwIDs, remoteGwID) + err = logic.UpdateUserV1(*user) + if err != nil { + slog.Error("failed to update user gateways", "user", username, "error", err) + logic.ReturnErrorResponse(w, r, logic.FormatError(fmt.Errorf("failed to fetch remote access gaetway node", err), "badrequest")) + return + } + logic.ReturnSuccessResponse(w, r, fmt.Sprintf("removed user %s from remote access gateway %s", username, remoteGwID)) +} + +type UserRemoteGws struct { + GwID string `json:"remote_access_gw_id"` + Network string `json:"network"` + Connected bool `json:"connected"` +} + +// swagger:route GET "/api/nodes/{username}/remote_access_gws" 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 param username"), "badrequest")) + } + userGws := []UserRemoteGws{} + allNodes, err := logic.GetAllNodes() + if err != nil { + logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal")) + 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 + } + if user.RemoteGwIDs != nil { + for _, node := range allNodes { + if _, ok := user.RemoteGwIDs[node.ID.String()]; ok { + userGws = append(userGws, UserRemoteGws{ + GwID: node.ID.String(), + Network: node.Network, + }) + // check if on the gw user has ext client created and set the connected flag + } + } + } + + w.WriteHeader(http.StatusOK) + json.NewEncoder(w).Encode(userGws) +} + // swagger:route GET /api/users user getUsers // // Get all users. diff --git a/logic/auth.go b/logic/auth.go index 6d690b266..16c7b39d2 100644 --- a/logic/auth.go +++ b/logic/auth.go @@ -241,6 +241,17 @@ func UpdateUserNetworks(newNetworks, newGroups []string, isadmin bool, currentUs return err } +func UpdateUserV1(user models.User) error { + data, err := json.Marshal(&user) + if err != nil { + return err + } + if err = database.Insert(user.UserName, string(data), database.USERS_TABLE_NAME); err != nil { + return err + } + return nil +} + // UpdateUser - updates a given user func UpdateUser(userchange, user *models.User) (*models.User, error) { // check if user exists diff --git a/models/structs.go b/models/structs.go index 3a39989d8..9340bb798 100644 --- a/models/structs.go +++ b/models/structs.go @@ -24,20 +24,22 @@ 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"` - IsSuperAdmin bool `json:"super_admin"` - 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"` + Networks []string `json:"networks" bson:"networks"` + IsAdmin bool `json:"isadmin" bson:"isadmin"` + IsSuperAdmin bool `json:"super_admin"` + RemoteGwIDs map[string]struct{} `json:"remote_gw_ids"` + Groups []string `json:"groups" bson:"groups" yaml:"groups"` } // 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"` + Networks []string `json:"networks"` + IsAdmin bool `json:"isadmin"` + IsSuperAdmin bool `json:"is_superadmin"` + Groups []string `json:"groups"` } // UserAuthParams - user auth params struct @@ -48,10 +50,12 @@ type UserAuthParams struct { // UserClaims - user claims struct type UserClaims struct { - IsAdmin bool - UserName string - Networks []string - Groups []string + IsAdmin bool + IsSuperAdmin bool + UserName string + Networks []string + Groups []string + GateWays map[string]struct{} jwt.RegisteredClaims } From 6cd7939f6bbcdd3ab9bf0656071804c045ce5fbb Mon Sep 17 00:00:00 2001 From: Abhishek Kondur Date: Thu, 17 Aug 2023 13:59:40 +0530 Subject: [PATCH 03/38] add api to list user's remote client has gateway clients --- controllers/user.go | 80 ++++++++++++++++++++++++++++++++++----------- models/extclient.go | 14 ++++---- 2 files changed, 69 insertions(+), 25 deletions(-) diff --git a/controllers/user.go b/controllers/user.go index d09abc9e7..878158eb2 100644 --- a/controllers/user.go +++ b/controllers/user.go @@ -245,23 +245,26 @@ func removeUserFromRemoteAccessGW(w http.ResponseWriter, r *http.Request) { 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", username, err), "badrequest")) + logic.ReturnErrorResponse(w, r, logic.FormatError(fmt.Errorf("failed to fetch user %s, error: %v", username, err), "badrequest")) return } delete(user.RemoteGwIDs, remoteGwID) + //TODO: remove all related ext client configs of the user err = logic.UpdateUserV1(*user) if err != nil { slog.Error("failed to update user gateways", "user", username, "error", err) - logic.ReturnErrorResponse(w, r, logic.FormatError(fmt.Errorf("failed to fetch remote access gaetway node", err), "badrequest")) + logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("failed to fetch remote access gaetway node "+err.Error()), "badrequest")) return } logic.ReturnSuccessResponse(w, r, fmt.Sprintf("removed user %s from remote access gateway %s", username, remoteGwID)) } type UserRemoteGws struct { - GwID string `json:"remote_access_gw_id"` - Network string `json:"network"` - Connected bool `json:"connected"` + GwID string `json:"remote_access_gw_id"` + GWName string `json:"gw_name"` + Network string `json:"network"` + Connected bool `json:"connected"` + GwClient models.ExtClient `json:"gw_client"` } // swagger:route GET "/api/nodes/{username}/remote_access_gws" nodes getUserRemoteAccessGws @@ -281,31 +284,70 @@ func getUserRemoteAccessGws(w http.ResponseWriter, r *http.Request) { var params = mux.Vars(r) username := params["username"] - if username == "" { - logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("required param username"), "badrequest")) - } - userGws := []UserRemoteGws{} - allNodes, err := logic.GetAllNodes() - if err != nil { - logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal")) + remoteClientID := params["remote_client_id"] + if username == "" || remoteClientID == "" { + logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("required params username and remote_client_id"), "badrequest")) return } + + userGws := []UserRemoteGws{} user, err := logic.GetUser(username) if err != nil { logger.Log(0, username, "failed to fetch user: ", err.Error()) logic.ReturnErrorResponse(w, r, logic.FormatError(fmt.Errorf("failed to fetch user %s, error: %v", username, err), "badrequest")) return } + allextClients, err := logic.GetAllExtClients() + if err != nil { + logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal")) + return + } if user.RemoteGwIDs != nil { - for _, node := range allNodes { - if _, ok := user.RemoteGwIDs[node.ID.String()]; ok { - userGws = append(userGws, UserRemoteGws{ - GwID: node.ID.String(), - Network: node.Network, - }) - // check if on the gw user has ext client created and set the connected flag + for _, extClient := range allextClients { + if extClient.RemoteAccessClientID == remoteClientID && 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 { + userGws = append(userGws, UserRemoteGws{ + GwID: node.ID.String(), + GWName: host.Name, + Network: node.Network, + GwClient: extClient, + Connected: true, + }) + 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 } + userGws = append(userGws, UserRemoteGws{ + GwID: node.ID.String(), + GWName: host.Name, + Network: node.Network, + }) } w.WriteHeader(http.StatusOK) diff --git a/models/extclient.go b/models/extclient.go index 2b3704c20..229b3f807 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"` } From d4b76fbe986abc888de39766d92a62aaa425db76 Mon Sep 17 00:00:00 2001 From: Abhishek Kondur Date: Mon, 21 Aug 2023 10:12:58 +0530 Subject: [PATCH 04/38] remove code related user groups --- controllers/ext_client.go | 128 +---------- controllers/network.go | 3 +- controllers/node.go | 45 +--- ee/ee_controllers/networkusers.go | 365 ------------------------------ ee/ee_controllers/usergroups.go | 73 ------ logic/auth.go | 147 +----------- logic/networks.go | 42 +--- logic/nodes.go | 7 - logic/pro/networks.go | 68 ------ logic/pro/networks_test.go | 64 ------ logic/pro/networkuser.go | 251 -------------------- logic/pro/networkuser_test.go | 110 --------- logic/pro/types.go | 20 -- logic/pro/usergroups.go | 80 ------- logic/pro/usergroups_test.go | 41 ---- logic/security.go | 74 +----- logic/users.go | 39 +--- main.go | 5 - models/network.go | 35 ++- models/promodels/networkuser.go | 37 --- models/promodels/pro.go | 10 - models/promodels/usergroups.go | 9 - serverctl/serverctl.go | 7 +- 23 files changed, 50 insertions(+), 1610 deletions(-) delete mode 100644 ee/ee_controllers/networkusers.go delete mode 100644 ee/ee_controllers/usergroups.go delete mode 100644 logic/pro/networks.go delete mode 100644 logic/pro/networks_test.go delete mode 100644 logic/pro/networkuser.go delete mode 100644 logic/pro/networkuser_test.go delete mode 100644 logic/pro/types.go delete mode 100644 logic/pro/usergroups.go delete mode 100644 logic/pro/usergroups_test.go delete mode 100644 models/promodels/networkuser.go delete mode 100644 models/promodels/pro.go delete mode 100644 models/promodels/usergroups.go diff --git a/controllers/ext_client.go b/controllers/ext_client.go index 35d6358ae..ffbd31cd8 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" @@ -26,10 +26,10 @@ 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/{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}", getExtClientConf).Methods(http.MethodGet) + r.HandleFunc("/api/extclients/{network}/{clientid}", updateExtClient).Methods(http.MethodPut) + r.HandleFunc("/api/extclients/{network}/{clientid}", deleteExtClient).Methods(http.MethodDelete) + r.HandleFunc("/api/extclients/{network}/{nodeid}", createExtClient).Methods(http.MethodPost) } func checkIngressExists(nodeID string) bool { @@ -372,26 +372,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() { @@ -448,31 +428,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 @@ -543,24 +504,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 +527,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/network.go b/controllers/network.go index b3e799b71..74faff1da 100644 --- a/controllers/network.go +++ b/controllers/network.go @@ -326,8 +326,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/node.go b/controllers/node.go index 28de485d1..70dd972c8 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" @@ -198,7 +196,7 @@ func Authorize(hostAllowed, networkCheck bool, authNetwork string, next http.Han var isAuthorized = false var nodeID = "" - username, networks, isadmin, errN := logic.VerifyUserToken(authToken) + username, _, isadmin, errN := logic.VerifyUserToken(authToken) if errN != nil { logic.ReturnErrorResponse(w, r, errorResponse) return @@ -210,11 +208,11 @@ func Authorize(hostAllowed, networkCheck bool, authNetwork string, next http.Han isAuthorized = true r.Header.Set("ismasterkey", "yes") } - if !isadmin && params["network"] != "" { - if logic.StringSliceContains(networks, params["network"]) && pro.IsUserNetAdmin(params["network"], username) { - isnetadmin = true - } - } + // 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 @@ -744,11 +742,11 @@ 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 - } + // 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 @@ -805,27 +803,6 @@ func runUpdates(node *models.Node, ifaceDelta bool) { }() } -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/ee/ee_controllers/networkusers.go b/ee/ee_controllers/networkusers.go deleted file mode 100644 index 8ae41b168..000000000 --- a/ee/ee_controllers/networkusers.go +++ /dev/null @@ -1,365 +0,0 @@ -package ee_controllers - -import ( - "encoding/json" - "errors" - "net/http" - - "github.com/gorilla/mux" - "github.com/gravitl/netmaker/database" - "github.com/gravitl/netmaker/logger" - "github.com/gravitl/netmaker/logic" - "github.com/gravitl/netmaker/logic/pro" - "github.com/gravitl/netmaker/models" - "github.com/gravitl/netmaker/models/promodels" -) - -func NetworkUsersHandlers(r *mux.Router) { - r.HandleFunc("/api/networkusers", logic.SecurityCheck(true, http.HandlerFunc(getAllNetworkUsers))).Methods(http.MethodGet) - r.HandleFunc("/api/networkusers/{network}", logic.SecurityCheck(true, http.HandlerFunc(getNetworkUsers))).Methods(http.MethodGet) - r.HandleFunc("/api/networkusers/{network}/{networkuser}", logic.SecurityCheck(true, http.HandlerFunc(getNetworkUser))).Methods(http.MethodGet) - r.HandleFunc("/api/networkusers/{network}", logic.SecurityCheck(true, http.HandlerFunc(createNetworkUser))).Methods(http.MethodPost) - r.HandleFunc("/api/networkusers/{network}", logic.SecurityCheck(true, http.HandlerFunc(updateNetworkUser))).Methods(http.MethodPut) - r.HandleFunc("/api/networkusers/data/{networkuser}/me", logic.NetUserSecurityCheck(false, false, http.HandlerFunc(getNetworkUserData))).Methods(http.MethodGet) - r.HandleFunc("/api/networkusers/{network}/{networkuser}", logic.SecurityCheck(true, http.HandlerFunc(deleteNetworkUser))).Methods(http.MethodDelete) -} - -// == RETURN TYPES == - -// NetworkName - represents a network name/ID -type NetworkName string - -// NetworkUserDataMap - map of all data per network for a user -type NetworkUserDataMap map[NetworkName]NetworkUserData - -// NetworkUserData - data struct for network users -type NetworkUserData struct { - Nodes []models.Node `json:"nodes" bson:"nodes" yaml:"nodes"` - Clients []models.ExtClient `json:"clients" bson:"clients" yaml:"clients"` - Vpn []models.Node `json:"vpns" bson:"vpns" yaml:"vpns"` - Networks []models.Network `json:"networks" bson:"networks" yaml:"networks"` - User promodels.NetworkUser `json:"user" bson:"user" yaml:"user"` -} - -// == END RETURN TYPES == - -// returns a map of a network user's data across all networks -func getNetworkUserData(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Content-Type", "application/json") - - var params = mux.Vars(r) - networkUserName := params["networkuser"] - logger.Log(1, r.Header.Get("user"), "requested fetching network user data for user", networkUserName) - - networks, err := logic.GetNetworks() - if err != nil { - logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal")) - return - } - - if networkUserName == "" { - logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("netuserToGet"), "badrequest")) - return - } - - u, err := logic.GetUser(networkUserName) - if err != nil { - logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("could not find user"), "badrequest")) - return - } - - // initialize the return data of network users - returnData := make(NetworkUserDataMap) - - // go through each network and get that user's data - // if user has no access, give no data - // if user is a net admin, give all nodes - // if user has node access, give user's nodes if any - // if user has client access, git user's clients if any - for i := range networks { - - netID := networks[i].NetID - newData := NetworkUserData{ - Nodes: []models.Node{}, - Clients: []models.ExtClient{}, - Vpn: []models.Node{}, - Networks: []models.Network{}, - } - netUser, err := pro.GetNetworkUser(netID, promodels.NetworkUserID(networkUserName)) - // check if user has access - if err == nil && netUser.AccessLevel != pro.NO_ACCESS { - newData.User = promodels.NetworkUser{ - AccessLevel: netUser.AccessLevel, - ClientLimit: netUser.ClientLimit, - NodeLimit: netUser.NodeLimit, - Nodes: netUser.Nodes, - Clients: netUser.Clients, - } - newData.User.SetDefaults() - // check network level permissions - if doesNetworkAllow := pro.IsUserAllowed(&networks[i], networkUserName, u.Groups); doesNetworkAllow || netUser.AccessLevel == pro.NET_ADMIN { - netNodes, err := logic.GetNetworkNodes(netID) - if err != nil { - if database.IsEmptyRecord(err) && netUser.AccessLevel == pro.NET_ADMIN { - newData.Networks = append(newData.Networks, networks[i]) - } else { - logger.Log(0, "failed to retrieve nodes on network", netID, "for user", string(netUser.ID)) - } - } else { - if netUser.AccessLevel <= pro.NODE_ACCESS { // handle nodes - // if access level is NODE_ACCESS, filter nodes - if netUser.AccessLevel == pro.NODE_ACCESS { - for i := range netNodes { - if logic.StringSliceContains(netUser.Nodes, netNodes[i].ID.String()) { - newData.Nodes = append(newData.Nodes, netNodes[i]) - } - } - } else { // net admin so, get all nodes and ext clients on network... - newData.Nodes = netNodes - for i := range netNodes { - if netNodes[i].IsIngressGateway { - newData.Vpn = append(newData.Vpn, netNodes[i]) - if clients, err := logic.GetExtClientsByID(netNodes[i].ID.String(), netID); err == nil { - newData.Clients = append(newData.Clients, clients...) - } - } - } - newData.Networks = append(newData.Networks, networks[i]) - } - } - if netUser.AccessLevel <= pro.CLIENT_ACCESS && netUser.AccessLevel != pro.NET_ADMIN { - for _, c := range netUser.Clients { - if client, err := logic.GetExtClient(c, netID); err == nil { - newData.Clients = append(newData.Clients, client) - } - } - for i := range netNodes { - if netNodes[i].IsIngressGateway { - newData.Vpn = append(newData.Vpn, netNodes[i]) - } - } - } - } - } - returnData[NetworkName(netID)] = newData - } - } - - w.WriteHeader(http.StatusOK) - json.NewEncoder(w).Encode(returnData) -} - -// returns a map of all network users mapped to each network -func getAllNetworkUsers(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Content-Type", "application/json") - logger.Log(1, r.Header.Get("user"), "requested fetching all network users") - type allNetworkUsers = map[string][]promodels.NetworkUser - - networks, err := logic.GetNetworks() - if err != nil { - logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal")) - return - } - - var allNetUsers = make(allNetworkUsers, len(networks)) - - for i := range networks { - netusers, err := pro.GetNetworkUsers(networks[i].NetID) - if err != nil { - logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal")) - return - } - for _, v := range netusers { - allNetUsers[networks[i].NetID] = append(allNetUsers[networks[i].NetID], v) - } - } - - w.WriteHeader(http.StatusOK) - json.NewEncoder(w).Encode(allNetUsers) -} - -func getNetworkUsers(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Content-Type", "application/json") - - var params = mux.Vars(r) - netname := params["network"] - logger.Log(1, r.Header.Get("user"), "requested fetching network users for network", netname) - - _, err := logic.GetNetwork(netname) - if err != nil { - logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal")) - return - } - - netusers, err := pro.GetNetworkUsers(netname) - if err != nil { - logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal")) - return - } - w.WriteHeader(http.StatusOK) - json.NewEncoder(w).Encode(netusers) -} - -func getNetworkUser(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Content-Type", "application/json") - - var params = mux.Vars(r) - netname := params["network"] - logger.Log(1, r.Header.Get("user"), "requested fetching network user", params["networkuser"], "on network", netname) - - _, err := logic.GetNetwork(netname) - if err != nil { - logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal")) - return - } - - netuserToGet := params["networkuser"] - if netuserToGet == "" { - logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("netuserToGet"), "badrequest")) - return - } - - netuser, err := pro.GetNetworkUser(netname, promodels.NetworkUserID(netuserToGet)) - if err != nil { - logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal")) - return - } - w.WriteHeader(http.StatusOK) - json.NewEncoder(w).Encode(netuser) -} - -func createNetworkUser(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Content-Type", "application/json") - var params = mux.Vars(r) - netname := params["network"] - logger.Log(1, r.Header.Get("user"), "requested creating a network user on network", netname) - - network, err := logic.GetNetwork(netname) - if err != nil { - logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal")) - return - } - var networkuser promodels.NetworkUser - - // we decode our body request params - err = json.NewDecoder(r.Body).Decode(&networkuser) - if err != nil { - logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal")) - return - } - - err = pro.CreateNetworkUser(&network, &networkuser) - if err != nil { - logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest")) - return - } - - w.WriteHeader(http.StatusOK) -} - -func updateNetworkUser(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Content-Type", "application/json") - - var params = mux.Vars(r) - netname := params["network"] - logger.Log(1, r.Header.Get("user"), "requested updating a network user on network", netname) - - network, err := logic.GetNetwork(netname) - if err != nil { - logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal")) - return - } - var networkuser promodels.NetworkUser - - // we decode our body request params - err = json.NewDecoder(r.Body).Decode(&networkuser) - if err != nil { - logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal")) - return - } - if networkuser.ID == "" || !pro.DoesNetworkUserExist(netname, networkuser.ID) { - logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("invalid user "+string(networkuser.ID)), "badrequest")) - return - } - if networkuser.AccessLevel < pro.NET_ADMIN || networkuser.AccessLevel > pro.NO_ACCESS { - logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("invalid user access level provided"), "badrequest")) - return - } - - if networkuser.ClientLimit < 0 || networkuser.NodeLimit < 0 { - logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("negative user limit provided"), "badrequest")) - return - } - - u, err := logic.GetUser(string(networkuser.ID)) - if err != nil { - logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("invalid user "+string(networkuser.ID)), "badrequest")) - return - } - - if !pro.IsUserAllowed(&network, u.UserName, u.Groups) { - logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("user must be in allowed groups or users"), "badrequest")) - return - } - - if networkuser.AccessLevel == pro.NET_ADMIN { - currentUser, err := logic.GetUser(string(networkuser.ID)) - if err != nil { - logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("user model not found for "+string(networkuser.ID)), "badrequest")) - return - } - - if !logic.StringSliceContains(currentUser.Networks, netname) { - // append network name to user model to conform to old model - if err = logic.UpdateUserNetworks( - append(currentUser.Networks, netname), - currentUser.Groups, - currentUser.IsAdmin, - &models.ReturnUser{ - Groups: currentUser.Groups, - IsAdmin: currentUser.IsAdmin, - Networks: currentUser.Networks, - UserName: currentUser.UserName, - }, - ); err != nil { - logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("user model failed net admin update "+string(networkuser.ID)+" (are they an admin?"), "badrequest")) - return - } - } - } - - err = pro.UpdateNetworkUser(netname, &networkuser) - if err != nil { - logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest")) - return - } - - w.WriteHeader(http.StatusOK) -} - -func deleteNetworkUser(w http.ResponseWriter, r *http.Request) { - - var params = mux.Vars(r) - netname := params["network"] - - logger.Log(1, r.Header.Get("user"), "requested deleting network user", params["networkuser"], "on network", netname) - - _, err := logic.GetNetwork(netname) - if err != nil { - logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal")) - return - } - - netuserToDelete := params["networkuser"] - if netuserToDelete == "" { - logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("no group name provided"), "badrequest")) - return - } - - if err := pro.DeleteNetworkUser(netname, netuserToDelete); err != nil { - logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal")) - return - } - - w.WriteHeader(http.StatusOK) -} diff --git a/ee/ee_controllers/usergroups.go b/ee/ee_controllers/usergroups.go deleted file mode 100644 index 787d52687..000000000 --- a/ee/ee_controllers/usergroups.go +++ /dev/null @@ -1,73 +0,0 @@ -package ee_controllers - -import ( - "encoding/json" - "errors" - "net/http" - - "github.com/gravitl/netmaker/logger" - "github.com/gravitl/netmaker/logic" - - "github.com/gorilla/mux" - "github.com/gravitl/netmaker/logic/pro" - "github.com/gravitl/netmaker/models/promodels" -) - -func UserGroupsHandlers(r *mux.Router) { - r.HandleFunc("/api/usergroups", logic.SecurityCheck(true, http.HandlerFunc(getUserGroups))).Methods(http.MethodGet) - r.HandleFunc("/api/usergroups/{usergroup}", logic.SecurityCheck(true, http.HandlerFunc(createUserGroup))).Methods(http.MethodPost) - r.HandleFunc("/api/usergroups/{usergroup}", logic.SecurityCheck(true, http.HandlerFunc(deleteUserGroup))).Methods(http.MethodDelete) -} - -func getUserGroups(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Content-Type", "application/json") - logger.Log(1, r.Header.Get("user"), "requested fetching user groups") - - userGroups, err := pro.GetUserGroups() - if err != nil { - logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal")) - return - } - // Returns all the groups in JSON format - w.WriteHeader(http.StatusOK) - json.NewEncoder(w).Encode(userGroups) -} - -func createUserGroup(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Content-Type", "application/json") - var params = mux.Vars(r) - newGroup := params["usergroup"] - - logger.Log(1, r.Header.Get("user"), "requested creating user group", newGroup) - - if newGroup == "" { - logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("no group name provided"), "badrequest")) - return - } - - err := pro.InsertUserGroup(promodels.UserGroupName(newGroup)) - if err != nil { - logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest")) - return - } - - w.WriteHeader(http.StatusOK) -} - -func deleteUserGroup(w http.ResponseWriter, r *http.Request) { - var params = mux.Vars(r) - groupToDelete := params["usergroup"] - logger.Log(1, r.Header.Get("user"), "requested deleting user group", groupToDelete) - - if groupToDelete == "" { - logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("no group name provided"), "badrequest")) - return - } - - if err := pro.DeleteUserGroup(promodels.UserGroupName(groupToDelete)); err != nil { - logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal")) - return - } - - w.WriteHeader(http.StatusOK) -} diff --git a/logic/auth.go b/logic/auth.go index 16c7b39d2..59e0efac7 100644 --- a/logic/auth.go +++ b/logic/auth.go @@ -11,10 +11,7 @@ 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" ) // HasSuperAdmin - checks if server has an superadmin/owner @@ -85,11 +82,11 @@ 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) - if tokenString == "" { - // logic.ReturnErrorResponse(w, r, errorResponse) - return err - } + // tokenString, _ := CreateUserJWT(user.UserName, user.Networks, user.IsAdmin) + // if tokenString == "" { + // // logic.ReturnErrorResponse(w, r, errorResponse) + // return err + // } SetUserDefaults(user) @@ -103,42 +100,6 @@ 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.Is_EE { - 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 } @@ -180,7 +141,7 @@ 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.Networks, result.IsAdmin) return tokenString, nil } @@ -196,38 +157,7 @@ func UpdateUserNetworks(newNetworks, newGroups []string, isadmin bool, currentUs if isadmin { currentUser.IsAdmin = true currentUser.Networks = nil - } else { - // == PRO == - currentUser.Groups = newGroups - for _, n := range newNetworks { - if !StringSliceContains(currentUser.Networks, n) { - // make net admin of any network not previously assigned - pro.MakeNetAdmin(n, currentUser.UserName) - } - } - // Compare networks, find networks not in previous - for _, n := range currentUser.Networks { - if !StringSliceContains(newNetworks, n) { - // if user was removed from a network, re-assign access to net default level - if network, err := GetNetwork(n); err == nil { - if network.ProSettings != nil { - ok := pro.AssignAccessLvl(n, currentUser.UserName, network.ProSettings.DefaultAccessLevel) - if ok { - logger.Log(0, "changed", currentUser.UserName, "access level on network", network.NetID, "to", fmt.Sprintf("%d", network.ProSettings.DefaultAccessLevel)) - } - } - } - } - } - - if err := AdjustGroupPermissions(currentUser); err != nil { - logger.Log(0, "failed to update user", currentUser.UserName, "after group update", err.Error()) - } - // == END PRO == - - currentUser.Networks = newNetworks } - userChange := models.User{ UserName: currentUser.UserName, Networks: currentUser.Networks, @@ -337,23 +267,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 } @@ -425,51 +338,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/networks.go b/logic/networks.go index 61ea17d3a..a6203ab47 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 7c85b9982..ea77f1af0 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/logic/pro/proacls" "github.com/gravitl/netmaker/models" "github.com/gravitl/netmaker/servercfg" @@ -236,12 +235,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 480c7c1d5..000000000 --- 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 2f674e039..000000000 --- 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 ed758c232..000000000 --- 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 bf462f490..000000000 --- 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 d2063116b..000000000 --- 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 e7132b3b9..000000000 --- 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 3ca32cefe..000000000 --- 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 f13743244..8d8bf02e7 100644 --- a/logic/security.go +++ b/logic/security.go @@ -7,9 +7,7 @@ import ( "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" ) @@ -69,76 +67,6 @@ func SecurityCheck(reqAdmin bool, next http.Handler) http.HandlerFunc { } } -// 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) { var tokenSplit = strings.Split(token, " ") @@ -170,7 +98,7 @@ func UserPermissions(reqAdmin bool, netname string, token string) ([]string, str if len(netname) > 0 && (len(userNetworks) == 0 || !authenticateNetworkUser(netname, userNetworks)) { return nil, username, Forbidden_Err } - if isEE && len(netname) > 0 && !pro.IsUserNetAdmin(netname, username) { + if isEE && len(netname) > 0 { return nil, "", Forbidden_Err } return userNetworks, username, nil diff --git a/logic/users.go b/logic/users.go index cbbade5ec..051452188 100644 --- a/logic/users.go +++ b/logic/users.go @@ -5,10 +5,7 @@ import ( "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 @@ -65,42 +62,10 @@ func GetGroupUsers(group string) ([]models.ReturnUser, error) { 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{}) } } diff --git a/main.go b/main.go index a0fe2824f..277742614 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/models/network.go b/models/network.go index b075a3ef8..89fb44d4d 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 ac34a1c1c..000000000 --- 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 47ebe4b94..000000000 --- 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 e01e6e9ca..000000000 --- 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/serverctl/serverctl.go b/serverctl/serverctl.go index 242c5031d..645dfc679 100644 --- a/serverctl/serverctl.go +++ b/serverctl/serverctl.go @@ -8,7 +8,6 @@ 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" ) const ( @@ -59,10 +58,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 +80,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) } From 33c07076447742bc45cc5f8572083eb7fef03588 Mon Sep 17 00:00:00 2001 From: Abhishek Kondur Date: Mon, 21 Aug 2023 10:49:59 +0530 Subject: [PATCH 05/38] remove networks and groups from user model --- auth/register_callback.go | 22 ----------- controllers/enrollmentkeys.go | 2 +- controllers/network_test.go | 2 - controllers/node.go | 21 ---------- controllers/user.go | 15 +------ controllers/user_test.go | 12 +++--- functions/helpers_test.go | 2 - logic/auth.go | 18 +++------ logic/enrollmentkey.go | 14 ------- logic/enrollmentkey_test.go | 74 ----------------------------------- logic/jwts.go | 46 ++++++---------------- logic/security.go | 14 +------ logic/users.go | 7 ++-- models/structs.go | 5 --- 14 files changed, 30 insertions(+), 224 deletions(-) diff --git a/auth/register_callback.go b/auth/register_callback.go index 66bb75567..3c1e5abfa 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/controllers/enrollmentkeys.go b/controllers/enrollmentkeys.go index 157a6f711..5a1a8fa2f 100644 --- a/controllers/enrollmentkeys.go +++ b/controllers/enrollmentkeys.go @@ -51,7 +51,7 @@ func getEnrollmentKeys(w http.ResponseWriter, r *http.Request) { // TODO drop double pointer ret := []*models.EnrollmentKey{} for _, key := range keys { - if !isMasterAdmin && !logic.UserHasNetworksAccess(key.Networks, user) { + if !isMasterAdmin && (!user.IsAdmin || !user.IsSuperAdmin) { continue } if err = logic.Tokenize(key, servercfg.GetAPIHost()); err != nil { diff --git a/controllers/network_test.go b/controllers/network_test.go index 63914d31f..bc169516b 100644 --- a/controllers/network_test.go +++ b/controllers/network_test.go @@ -29,8 +29,6 @@ func TestMain(m *testing.M) { UserName: "admin", Password: "password", IsAdmin: true, - Networks: []string{}, - Groups: []string{}, }) peerUpdate := make(chan *models.Node) go logic.ManageZombies(context.Background(), peerUpdate) diff --git a/controllers/node.go b/controllers/node.go index 70dd972c8..f0c9bc016 100644 --- a/controllers/node.go +++ b/controllers/node.go @@ -324,14 +324,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[:]) @@ -341,19 +333,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. diff --git a/controllers/user.go b/controllers/user.go index 878158eb2..1fc31101b 100644 --- a/controllers/user.go +++ b/controllers/user.go @@ -474,7 +474,7 @@ func updateUserNetworks(w http.ResponseWriter, r *http.Request) { var params = mux.Vars(r) // start here username := params["username"] - user, err := logic.GetUser(username) + _, err := logic.GetUser(username) if err != nil { logger.Log(0, username, "failed to update user networks: ", err.Error()) @@ -490,19 +490,7 @@ func updateUserNetworks(w http.ResponseWriter, r *http.Request) { 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) @@ -568,7 +556,6 @@ func updateUser(w http.ResponseWriter, r *http.Request) { 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, diff --git a/controllers/user_test.go b/controllers/user_test.go index ad98ee5ff..3b7cc2302 100644 --- a/controllers/user_test.go +++ b/controllers/user_test.go @@ -90,8 +90,8 @@ func TestUpdateUserNoHashedPassword(t *testing.T) { // 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 + verifyJWT = func(bearerToken string) (username string, issuperadmin, isadmin bool, err error) { + return user1.UserName, user1.IsSuperAdmin, user1.IsAdmin, nil } defer func() { verifyJWT = oldVerify }() @@ -281,7 +281,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") @@ -339,7 +339,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") @@ -382,7 +382,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 = "" @@ -418,7 +418,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 7d5531ea2..4dc3d5297 100644 --- a/functions/helpers_test.go +++ b/functions/helpers_test.go @@ -29,8 +29,6 @@ func TestMain(m *testing.M) { UserName: "superadmin", Password: "password", IsSuperAdmin: true, - Networks: []string{}, - Groups: []string{}, }) peerUpdate := make(chan *models.Node) go logic.ManageZombies(context.Background(), peerUpdate) diff --git a/logic/auth.go b/logic/auth.go index 59e0efac7..5dbc05684 100644 --- a/logic/auth.go +++ b/logic/auth.go @@ -141,7 +141,7 @@ func VerifyAuthRequest(authRequest models.UserAuthParams) (string, error) { } // Create a new JWT for the node - tokenString, _ := CreateUserJWT(authRequest.UserName, result.Networks, result.IsAdmin) + tokenString, _ := CreateUserJWT(authRequest.UserName, result.IsSuperAdmin, result.IsAdmin) return tokenString, nil } @@ -159,11 +159,10 @@ func UpdateUserNetworks(newNetworks, newGroups []string, isadmin bool, currentUs currentUser.Networks = nil } userChange := models.User{ - UserName: currentUser.UserName, - Networks: currentUser.Networks, - IsAdmin: currentUser.IsAdmin, - Password: "", - Groups: currentUser.Groups, + UserName: currentUser.UserName, + IsSuperAdmin: currentUser.IsSuperAdmin, + IsAdmin: currentUser.IsAdmin, + Password: "", } _, err = UpdateUser(&userChange, returnedUser) @@ -194,12 +193,7 @@ func UpdateUser(userchange, user *models.User) (*models.User, error) { if userchange.UserName != "" { user.UserName = userchange.UserName } - if len(userchange.Networks) > 0 { - user.Networks = userchange.Networks - } - if len(userchange.Groups) > 0 { - user.Groups = userchange.Groups - } + if userchange.Password != "" { // encrypt that password so we never see it again hash, err := bcrypt.GenerateFromPassword([]byte(userchange.Password), 5) diff --git a/logic/enrollmentkey.go b/logic/enrollmentkey.go index 53c433206..a1b2ecbea 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 5cf36586a..ace8ef9a8 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/jwts.go b/logic/jwts.go index 0f355a87f..9b53a6e85 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 "masteradministrator", 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/security.go b/logic/security.go index 8d8bf02e7..3f9f547f1 100644 --- a/logic/security.go +++ b/logic/security.go @@ -83,24 +83,14 @@ func UserPermissions(reqAdmin bool, netname string, token string) ([]string, str // TODO log in as an actual admin user return []string{ALL_NETWORK_ACCESS}, master_uname, nil } - username, networks, isadmin, err := VerifyUserToken(authToken) + username, _, isadmin, err := VerifyUserToken(authToken) if err != nil { return nil, username, Unauthorized_Err } if !isadmin && reqAdmin { return nil, username, Forbidden_Err } - userNetworks = networks - if isadmin { - return []string{ALL_NETWORK_ACCESS}, username, nil - } - // check network admin access - if len(netname) > 0 && (len(userNetworks) == 0 || !authenticateNetworkUser(netname, userNetworks)) { - return nil, username, Forbidden_Err - } - if isEE && len(netname) > 0 { - return nil, "", Forbidden_Err - } + return userNetworks, username, nil } diff --git a/logic/users.go b/logic/users.go index 051452188..f18459e84 100644 --- a/logic/users.go +++ b/logic/users.go @@ -40,10 +40,9 @@ 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, } } diff --git a/models/structs.go b/models/structs.go index 9340bb798..d3f3df496 100644 --- a/models/structs.go +++ b/models/structs.go @@ -26,11 +26,9 @@ type AuthParams struct { type User struct { UserName string `json:"username" bson:"username" validate:"min=3,max=40,in_charset|email"` Password string `json:"password" bson:"password" validate:"required,min=5"` - Networks []string `json:"networks" bson:"networks"` IsAdmin bool `json:"isadmin" bson:"isadmin"` IsSuperAdmin bool `json:"super_admin"` RemoteGwIDs map[string]struct{} `json:"remote_gw_ids"` - Groups []string `json:"groups" bson:"groups" yaml:"groups"` } // ReturnUser - return user struct @@ -53,9 +51,6 @@ type UserClaims struct { IsAdmin bool IsSuperAdmin bool UserName string - Networks []string - Groups []string - GateWays map[string]struct{} jwt.RegisteredClaims } From abdd2e4bbe851be722fbb6cbbf83634979eb93b6 Mon Sep 17 00:00:00 2001 From: Abhishek Kondur Date: Mon, 21 Aug 2023 16:58:35 +0530 Subject: [PATCH 06/38] refactor user CRUD operations --- controllers/network_test.go | 16 ++-- controllers/user.go | 164 ++++++++++++++++++++---------------- controllers/user_test.go | 14 --- logic/auth.go | 5 +- logic/security.go | 62 +++----------- servercfg/serverconf.go | 11 --- 6 files changed, 111 insertions(+), 161 deletions(-) diff --git a/controllers/network_test.go b/controllers/network_test.go index bc169516b..2611655b4 100644 --- a/controllers/network_test.go +++ b/controllers/network_test.go @@ -89,27 +89,27 @@ 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.Log(username) }) t.Run("WithNetwork", func(t *testing.T) { - networks, username, err := logic.UserPermissions(false, "skynet", "Bearer secretkey") + username, err := logic.UserPermissions(false, "skynet", "Bearer secretkey") assert.Nil(t, err) - t.Log(networks, username) + t.Log(username) }) t.Run("BadNet", func(t *testing.T) { t.Skip() - networks, username, err := logic.UserPermissions(false, "badnet", "Bearer secretkey") + 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, "skynet", "Bearer badkey") assert.NotNil(t, err) t.Log(err) - t.Log(networks, username) + t.Log(username) }) } diff --git a/controllers/user.go b/controllers/user.go index 1fc31101b..5af2700e8 100644 --- a/controllers/user.go +++ b/controllers/user.go @@ -29,10 +29,8 @@ func userHandlers(r *mux.Router) { r.HandleFunc("/api/users/adm/authenticate", authenticateUser).Methods(http.MethodPost) r.HandleFunc("/api/users/{username}/remote_access_gw", attachUserToRemoteAccessGw).Methods(http.MethodPost) r.HandleFunc("/api/users/{username}/remote_access_gw", removeUserFromRemoteAccessGW).Methods(http.MethodDelete) - r.HandleFunc("/api/nodes/user/remote_access_gws", logic.SecurityCheck(false, http.HandlerFunc(getUserRemoteAccessGws))).Methods(http.MethodGet) - 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}/remote_access_gw", logic.SecurityCheck(false, http.HandlerFunc(getUserRemoteAccessGws))).Methods(http.MethodGet) + 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) @@ -290,7 +288,7 @@ func getUserRemoteAccessGws(w http.ResponseWriter, r *http.Request) { return } - userGws := []UserRemoteGws{} + userGws := make(map[string][]UserRemoteGws) user, err := logic.GetUser(username) if err != nil { logger.Log(0, username, "failed to fetch user: ", err.Error()) @@ -316,14 +314,18 @@ func getUserRemoteAccessGws(w http.ResponseWriter, r *http.Request) { if err != nil { continue } + if _, ok := user.RemoteGwIDs[node.ID.String()]; ok { - userGws = append(userGws, UserRemoteGws{ + gws := userGws[node.Network] + + gws = append(gws, 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()) } } @@ -343,11 +345,14 @@ func getUserRemoteAccessGws(w http.ResponseWriter, r *http.Request) { if err != nil { continue } - userGws = append(userGws, UserRemoteGws{ + gws := userGws[node.Network] + + gws = append(gws, UserRemoteGws{ GwID: node.ID.String(), GWName: host.Name, Network: node.Network, }) + userGws[node.Network] = gws } w.WriteHeader(http.StatusOK) @@ -437,69 +442,40 @@ func createSuperAdmin(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 } - - err = logic.CreateUser(&user) - if err != nil { - logger.Log(0, user.UserName, "error creating new user: ", - err.Error()) + if !caller.IsSuperAdmin && user.IsAdmin { + slog.Error("error creating new user: ", "user", user.UserName, "error", + errors.New("only superadmin can create admin users")) logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest")) return } - logger.Log(1, user.UserName, "was created") - 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"] - _, 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()) + if user.IsSuperAdmin { + slog.Error("error creating new user: ", "user", user.UserName, "error", + errors.New("additional superadmins cannot be created")) 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) + err = logic.CreateUser(&user) if err != nil { - logger.Log(0, username, "failed to fetch user: ", err.Error()) - logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal")) + logger.Log(0, user.UserName, "error creating new user: ", + err.Error()) + logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest")) return } - json.NewEncoder(w).Encode(returnUser) + logger.Log(1, user.UserName, "was created") + json.NewEncoder(w).Encode(logic.ToReturnUser(user)) } // swagger:route PUT /api/users/{username} user updateUser @@ -517,18 +493,12 @@ 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")) + + caller, err := logic.GetUser(r.Header.Get("user")) if err != nil { - logger.Log(0, "verifyJWT error", err.Error()) logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal")) - return } 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, @@ -536,12 +506,6 @@ func updateUser(w http.ResponseWriter, r *http.Request) { logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal")) return } - if auth.IsOauthUser(user) == nil { - err := fmt.Errorf("cannot update user info for oauth user %s", username) - logger.Log(0, err.Error()) - logic.ReturnErrorResponse(w, r, logic.FormatError(err, "forbidden")) - return - } var userchange models.User // we decode our body request params err = json.NewDecoder(r.Body).Decode(&userchange) @@ -551,11 +515,44 @@ func updateUser(w http.ResponseWriter, r *http.Request) { 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")) + selfUpdate := false + if caller.UserName == user.UserName { + selfUpdate = true + } + + if !caller.IsSuperAdmin { + if 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 !selfUpdate && !(caller.IsAdmin) { + slog.Error("non-admin user", "caller", caller.UserName, "attempted to update user", username) + logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("not authorized"), "forbidden")) + return + } + + if userchange.IsAdmin != user.IsAdmin || userchange.IsSuperAdmin != user.IsSuperAdmin { + if selfUpdate { + 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 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 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 } + user, err = logic.UpdateUser(&userchange, user) if err != nil { logger.Log(0, username, @@ -635,9 +632,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, diff --git a/controllers/user_test.go b/controllers/user_test.go index 3b7cc2302..bc23b268c 100644 --- a/controllers/user_test.go +++ b/controllers/user_test.go @@ -65,20 +65,6 @@ func TestCreateUserNoHashedPassword(t *testing.T) { 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"} diff --git a/logic/auth.go b/logic/auth.go index 5dbc05684..c14fd9d86 100644 --- a/logic/auth.go +++ b/logic/auth.go @@ -206,10 +206,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 { diff --git a/logic/security.go b/logic/security.go index 3f9f547f1..777a83387 100644 --- a/logic/security.go +++ b/logic/security.go @@ -1,12 +1,10 @@ package logic import ( - "encoding/json" "net/http" "strings" "github.com/gorilla/mux" - "github.com/gravitl/netmaker/database" "github.com/gravitl/netmaker/models" "github.com/gravitl/netmaker/servercfg" ) @@ -30,68 +28,45 @@ func SecurityCheck(reqAdmin bool, next http.Handler) http.HandlerFunc { 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) return } // detect masteradmin - if len(networks) > 0 && networks[0] == ALL_NETWORK_ACCESS { + if username == master_uname { 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) } } // 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 master_uname, nil } - username, _, 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 } - return userNetworks, username, nil + return username, nil } // Consider a more secure way of setting master key @@ -99,23 +74,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/servercfg/serverconf.go b/servercfg/serverconf.go index 4a535c188..7e2857489 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 := "*" From f53bbd0e10bdcb9cab6d991aa06e3060069cf7b5 Mon Sep 17 00:00:00 2001 From: Abhishek Kondur Date: Mon, 21 Aug 2023 17:21:15 +0530 Subject: [PATCH 07/38] fix network permission test --- controllers/network_test.go | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) diff --git a/controllers/network_test.go b/controllers/network_test.go index 2611655b4..8678f4089 100644 --- a/controllers/network_test.go +++ b/controllers/network_test.go @@ -89,24 +89,13 @@ func TestSecurityCheck(t *testing.T) { os.Setenv("MASTER_KEY", "secretkey") t.Run("NoNetwork", func(t *testing.T) { - username, err := logic.UserPermissions(false, "", "Bearer secretkey") + username, err := logic.UserPermissions(false, "Bearer secretkey") assert.Nil(t, err) t.Log(username) }) - t.Run("WithNetwork", func(t *testing.T) { - username, err := logic.UserPermissions(false, "skynet", "Bearer secretkey") - assert.Nil(t, err) - t.Log(username) - }) - t.Run("BadNet", func(t *testing.T) { - t.Skip() - username, err := logic.UserPermissions(false, "badnet", "Bearer secretkey") - assert.NotNil(t, err) - t.Log(err) - t.Log(username) - }) + t.Run("BadToken", func(t *testing.T) { - username, err := logic.UserPermissions(false, "skynet", "Bearer badkey") + username, err := logic.UserPermissions(false, "Bearer badkey") assert.NotNil(t, err) t.Log(err) t.Log(username) From 7526ee23a8f4876b45b12b98cf3ca28ec414e644 Mon Sep 17 00:00:00 2001 From: Abhishek Kondur Date: Mon, 21 Aug 2023 21:02:17 +0530 Subject: [PATCH 08/38] add superadmin to authorize func --- controllers/node.go | 11 +++-------- controllers/user.go | 8 ++++---- logic/auth.go | 28 ++-------------------------- 3 files changed, 9 insertions(+), 38 deletions(-) diff --git a/controllers/node.go b/controllers/node.go index f0c9bc016..fe38d1edb 100644 --- a/controllers/node.go +++ b/controllers/node.go @@ -196,23 +196,18 @@ func Authorize(hostAllowed, networkCheck bool, authNetwork string, next http.Han var isAuthorized = false var nodeID = "" - username, _, isadmin, errN := logic.VerifyUserToken(authToken) + username, issuperadmin, isadmin, errN := logic.VerifyUserToken(authToken) if errN != nil { logic.ReturnErrorResponse(w, r, errorResponse) 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 diff --git a/controllers/user.go b/controllers/user.go index 5af2700e8..e9b028c33 100644 --- a/controllers/user.go +++ b/controllers/user.go @@ -27,8 +27,8 @@ func userHandlers(r *mux.Router) { 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/authenticate", authenticateUser).Methods(http.MethodPost) - r.HandleFunc("/api/users/{username}/remote_access_gw", attachUserToRemoteAccessGw).Methods(http.MethodPost) - r.HandleFunc("/api/users/{username}/remote_access_gw", removeUserFromRemoteAccessGW).Methods(http.MethodDelete) + r.HandleFunc("/api/users/{username}/remote_access_gw", logic.SecurityCheck(true, http.HandlerFunc(attachUserToRemoteAccessGw))).Methods(http.MethodPost) + r.HandleFunc("/api/users/{username}/remote_access_gw", logic.SecurityCheck(true, http.HandlerFunc(removeUserFromRemoteAccessGW))).Methods(http.MethodDelete) r.HandleFunc("/api/users/{username}/remote_access_gw", logic.SecurityCheck(false, http.HandlerFunc(getUserRemoteAccessGws))).Methods(http.MethodGet) 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) @@ -209,7 +209,7 @@ func attachUserToRemoteAccessGw(w http.ResponseWriter, r *http.Request) { user.RemoteGwIDs = make(map[string]struct{}) } user.RemoteGwIDs[node.ID.String()] = struct{}{} - err = logic.UpdateUserV1(*user) + err = logic.UpsertUser(*user) if err != nil { slog.Error("failed to update user gateways", "user", username, "error", err) logic.ReturnErrorResponse(w, r, logic.FormatError(fmt.Errorf("failed to fetch remote access gaetway node", err), "badrequest")) @@ -248,7 +248,7 @@ func removeUserFromRemoteAccessGW(w http.ResponseWriter, r *http.Request) { } delete(user.RemoteGwIDs, remoteGwID) //TODO: remove all related ext client configs of the user - err = logic.UpdateUserV1(*user) + 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")) diff --git a/logic/auth.go b/logic/auth.go index c14fd9d86..07a427ef7 100644 --- a/logic/auth.go +++ b/logic/auth.go @@ -145,32 +145,8 @@ func VerifyAuthRequest(authRequest models.UserAuthParams) (string, error) { 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) - 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 - } - userChange := models.User{ - UserName: currentUser.UserName, - IsSuperAdmin: currentUser.IsSuperAdmin, - IsAdmin: currentUser.IsAdmin, - Password: "", - } - - _, err = UpdateUser(&userChange, returnedUser) - - return err -} - -func UpdateUserV1(user models.User) error { +// UpsertUser - updates user in the db +func UpsertUser(user models.User) error { data, err := json.Marshal(&user) if err != nil { return err From 89fb719d95e51195e3be11a804c1f0addb385994 Mon Sep 17 00:00:00 2001 From: Abhishek Kondur Date: Mon, 21 Aug 2023 23:46:23 +0530 Subject: [PATCH 09/38] remove user network and groups from cli --- cli/cmd/network_user/create.go | 43 ----------- cli/cmd/network_user/delete.go | 23 ------ cli/cmd/network_user/flags.go | 10 --- cli/cmd/network_user/get.go | 27 ------- cli/cmd/network_user/list.go | 27 ------- cli/cmd/network_user/root.go | 28 ------- cli/cmd/network_user/update.go | 43 ----------- cli/cmd/root.go | 4 - cli/cmd/user/create.go | 8 -- cli/cmd/user/update.go | 10 --- cli/cmd/usergroup/create.go | 23 ------ cli/cmd/usergroup/delete.go | 23 ------ cli/cmd/usergroup/get.go | 20 ----- cli/cmd/usergroup/root.go | 28 ------- cli/functions/network_user.go | 44 ----------- cli/functions/usergroups.go | 22 ------ controllers/user.go | 129 ++++++++++++++++++++------------- ee/initialize.go | 2 - logic/auth.go | 9 +-- 19 files changed, 84 insertions(+), 439 deletions(-) delete mode 100644 cli/cmd/network_user/create.go delete mode 100644 cli/cmd/network_user/delete.go delete mode 100644 cli/cmd/network_user/flags.go delete mode 100644 cli/cmd/network_user/get.go delete mode 100644 cli/cmd/network_user/list.go delete mode 100644 cli/cmd/network_user/root.go delete mode 100644 cli/cmd/network_user/update.go delete mode 100644 cli/cmd/usergroup/create.go delete mode 100644 cli/cmd/usergroup/delete.go delete mode 100644 cli/cmd/usergroup/get.go delete mode 100644 cli/cmd/usergroup/root.go delete mode 100644 cli/functions/network_user.go delete mode 100644 cli/functions/usergroups.go diff --git a/cli/cmd/network_user/create.go b/cli/cmd/network_user/create.go deleted file mode 100644 index 433f0a1ad..000000000 --- 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 31a3c7e51..000000000 --- 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 b328ca2c9..000000000 --- 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 46e3025c2..000000000 --- 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 c539cf2a6..000000000 --- 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 b7c7facd2..000000000 --- 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 c7ece7fc7..000000000 --- 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 ff6c54f15..7376104c1 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 4c24b5453..dc1e2a0b5 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/update.go b/cli/cmd/user/update.go index cb95adf52..0a79f26a3 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 f3139d7c9..000000000 --- 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 b4172b743..000000000 --- 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 e2f1a0bf7..000000000 --- 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 3269ebaad..000000000 --- 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 f412fdeeb..000000000 --- a/cli/functions/network_user.go +++ /dev/null @@ -1,44 +0,0 @@ -package functions - -import ( - "fmt" - "net/http" - - "github.com/gravitl/netmaker/ee/ee_controllers" - "github.com/gravitl/netmaker/models/promodels" -) - -// GetAllNetworkUsers - fetch all network users -func GetAllNetworkUsers() *map[string][]promodels.NetworkUser { - return request[map[string][]promodels.NetworkUser](http.MethodGet, "/api/networkusers", nil) -} - -// GetNetworkUsers - fetch network users belonging to a particular network -func GetNetworkUsers(networkName string) *promodels.NetworkUserMap { - return request[promodels.NetworkUserMap](http.MethodGet, "/api/networkusers/"+networkName, nil) -} - -// GetNetworkUser - fetch a single network user -func GetNetworkUser(networkName, networkUserName string) *promodels.NetworkUser { - return request[promodels.NetworkUser](http.MethodGet, fmt.Sprintf("/api/networkusers/%s/%s", networkName, networkUserName), nil) -} - -// CreateNetworkUser - create a network user -func CreateNetworkUser(networkName string, payload *promodels.NetworkUser) { - request[any](http.MethodPost, "/api/networkusers/"+networkName, payload) -} - -// UpdateNetworkUser - update a network user -func UpdateNetworkUser(networkName string, payload *promodels.NetworkUser) { - request[any](http.MethodPut, "/api/networkusers/"+networkName, payload) -} - -// GetNetworkUserData - fetch a network user's complete data -func GetNetworkUserData(networkUserName string) *ee_controllers.NetworkUserDataMap { - return request[ee_controllers.NetworkUserDataMap](http.MethodGet, fmt.Sprintf("/api/networkusers/data/%s/me", networkUserName), nil) -} - -// DeleteNetworkUser - delete a network user -func DeleteNetworkUser(networkName, networkUserName string) { - request[any](http.MethodDelete, fmt.Sprintf("/api/networkusers/%s/%s", networkName, networkUserName), nil) -} diff --git a/cli/functions/usergroups.go b/cli/functions/usergroups.go deleted file mode 100644 index 98b90d69a..000000000 --- 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/user.go b/controllers/user.go index e9b028c33..99a078581 100644 --- a/controllers/user.go +++ b/controllers/user.go @@ -191,14 +191,18 @@ func attachUserToRemoteAccessGw(w http.ResponseWriter, r *http.Request) { } 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", username, err), "badrequest")) + 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 gaetway node", err), "badrequest")) + logic.ReturnErrorResponse(w, r, logic.FormatError(fmt.Errorf("failed to fetch remote access gateway node, error: %v", err), "badrequest")) return } if !node.IsIngressGateway { @@ -265,7 +269,7 @@ type UserRemoteGws struct { GwClient models.ExtClient `json:"gw_client"` } -// swagger:route GET "/api/nodes/{username}/remote_access_gws" nodes getUserRemoteAccessGws +// swagger:route GET "/api/users/{username}/remote_access_gw" nodes getUserRemoteAccessGws // // Get an individual node. // @@ -300,59 +304,86 @@ func getUserRemoteAccessGws(w http.ResponseWriter, r *http.Request) { logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal")) return } - if user.RemoteGwIDs != nil { - for _, extClient := range allextClients { - if extClient.RemoteAccessClientID == remoteClientID && 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, UserRemoteGws{ - GwID: node.ID.String(), - GWName: host.Name, - Network: node.Network, - GwClient: extClient, - Connected: true, - }) - userGws[node.Network] = gws + for _, extClient := range allextClients { + if extClient.RemoteAccessClientID == remoteClientID && 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 || user.IsSuperAdmin || user.IsAdmin { + gws := userGws[node.Network] + + gws = append(gws, UserRemoteGws{ + GwID: node.ID.String(), + GWName: host.Name, + Network: node.Network, + GwClient: extClient, + Connected: true, + }) + userGws[node.Network] = gws + if user.IsAdmin || user.IsSuperAdmin { + user.RemoteGwIDs[node.ID.String()] = struct{}{} + } else { 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 + if user.IsAdmin || user.IsSuperAdmin { + nodes, err := logic.GetAllNodes() + if err == nil { + for _, node := range nodes { + if node.IsIngressGateway { + if _, ok := user.RemoteGwIDs[node.ID.String()]; !ok { + gws := userGws[node.Network] + host, err := logic.GetHost(node.HostID.String()) + if err != nil { + continue + } + gws = append(gws, UserRemoteGws{ + GwID: node.ID.String(), + GWName: host.Name, + Network: node.Network, + }) + userGws[node.Network] = gws + } + } + } } - host, err := logic.GetHost(node.HostID.String()) - if err != nil { - continue + } else { + // 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, UserRemoteGws{ + GwID: node.ID.String(), + GWName: host.Name, + Network: node.Network, + }) + userGws[node.Network] = gws } - gws := userGws[node.Network] - - gws = append(gws, UserRemoteGws{ - GwID: node.ID.String(), - GWName: host.Name, - Network: node.Network, - }) - userGws[node.Network] = gws } w.WriteHeader(http.StatusOK) diff --git a/ee/initialize.go b/ee/initialize.go index e779147bf..fe06c8112 100644 --- a/ee/initialize.go +++ b/ee/initialize.go @@ -25,8 +25,6 @@ func InitEE() { controller.HttpHandlers = append( controller.HttpHandlers, ee_controllers.MetricHandlers, - ee_controllers.NetworkUsersHandlers, - ee_controllers.UserGroupsHandlers, ee_controllers.RelayHandlers, ) logic.EnterpriseCheckFuncs = append(logic.EnterpriseCheckFuncs, func() { diff --git a/logic/auth.go b/logic/auth.go index 07a427ef7..43068af06 100644 --- a/logic/auth.go +++ b/logic/auth.go @@ -82,11 +82,10 @@ func CreateUser(user *models.User) error { // set password to encrypted password user.Password = string(hash) - // tokenString, _ := CreateUserJWT(user.UserName, user.Networks, user.IsAdmin) - // if tokenString == "" { - // // logic.ReturnErrorResponse(w, r, errorResponse) - // return err - // } + tokenString, _ := CreateUserJWT(user.UserName, user.IsSuperAdmin, user.IsAdmin) + if tokenString == "" { + return err + } SetUserDefaults(user) From 76ec51e8525a2782c2f1e0692949df06e54bb6c0 Mon Sep 17 00:00:00 2001 From: Abhishek Kondur Date: Tue, 22 Aug 2023 12:44:03 +0530 Subject: [PATCH 10/38] api to transfer superadmin role --- controllers/node.go | 61 ++++++++++++++++++++++++++++++++++++++++++ controllers/user.go | 64 ++++++++++++++++++++++++++++++++++++++++++--- logic/auth.go | 4 +++ 3 files changed, 126 insertions(+), 3 deletions(-) diff --git a/controllers/node.go b/controllers/node.go index fe38d1edb..91b0b8d8b 100644 --- a/controllers/node.go +++ b/controllers/node.go @@ -30,6 +30,7 @@ func nodeHandlers(r *mux.Router) { 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}/ingress/users", 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/adm/{network}/authenticate", authenticate).Methods(http.MethodPost) r.HandleFunc("/api/v1/nodes/migrate", migrate).Methods(http.MethodPost) @@ -595,6 +596,66 @@ func deleteIngressGateway(w http.ResponseWriter, r *http.Request) { runUpdates(&node, true) } +// swagger:route GET /api/nodes/{network}/{nodeid}/ingress/users nodes 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) + nodeid := params["nodeid"] + netid := params["network"] + node, err := validateParams(nodeid, netid) + if err != nil { + logic.ReturnErrorResponse(w, r, logic.FormatError(err, "bad request")) + return + } + node, wasFailover, removedClients, err := logic.DeleteIngressGateway(nodeid) + if err != nil { + logger.Log(0, r.Header.Get("user"), + fmt.Sprintf("failed to delete ingress gateway on node [%s] on network [%s]: %v", + nodeid, netid, err)) + logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal")) + return + } + + if servercfg.Is_EE && wasFailover { + if err = logic.EnterpriseResetFailoverFunc(node.Network); err != nil { + logger.Log(1, "failed to reset failover list during failover create", node.ID.String(), node.Network) + } + } + + apiNode := node.ConvertToAPINode() + logger.Log(1, r.Header.Get("user"), "deleted ingress gateway", nodeid) + w.WriteHeader(http.StatusOK) + json.NewEncoder(w).Encode(apiNode) + + if len(removedClients) > 0 { + host, err := logic.GetHost(node.HostID.String()) + if err == nil { + allNodes, err := logic.GetAllNodes() + if err != nil { + return + } + go mq.PublishSingleHostPeerUpdate( + host, + allNodes, + nil, + removedClients[:], + ) + } + } + + runUpdates(&node, true) +} + // swagger:route PUT /api/nodes/{network}/{nodeid} nodes updateNode // // Update an individual node. diff --git a/controllers/user.go b/controllers/user.go index 99a078581..5b9e9fe20 100644 --- a/controllers/user.go +++ b/controllers/user.go @@ -26,6 +26,7 @@ var verifyJWT = logic.VerifyJWT func userHandlers(r *mux.Router) { 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", logic.SecurityCheck(true, http.HandlerFunc(transferSuperAdmin))).Methods(http.MethodPost) r.HandleFunc("/api/users/adm/authenticate", authenticateUser).Methods(http.MethodPost) r.HandleFunc("/api/users/{username}/remote_access_gw", logic.SecurityCheck(true, http.HandlerFunc(attachUserToRemoteAccessGw))).Methods(http.MethodPost) r.HandleFunc("/api/users/{username}/remote_access_gw", logic.SecurityCheck(true, http.HandlerFunc(removeUserFromRemoteAccessGW))).Methods(http.MethodDelete) @@ -460,6 +461,63 @@ func createSuperAdmin(w http.ResponseWriter, r *http.Request) { json.NewEncoder(w).Encode(logic.ToReturnUser(u)) } +// swagger:route POST /api/users/adm/transfersuperadmin user transferSuperAdmin +// +// Transfers suoeradmin 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 u models.User + err = json.NewDecoder(r.Body).Decode(&u) + if err != nil { + slog.Error("error decoding request body: ", "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 // // Create a user. @@ -488,13 +546,13 @@ func createUser(w http.ResponseWriter, r *http.Request) { if !caller.IsSuperAdmin && user.IsAdmin { slog.Error("error creating new user: ", "user", user.UserName, "error", errors.New("only superadmin can create admin users")) - logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest")) + logic.ReturnErrorResponse(w, r, logic.FormatError(err, "forbidden")) return } if user.IsSuperAdmin { slog.Error("error creating new user: ", "user", user.UserName, "error", errors.New("additional superadmins cannot be created")) - logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest")) + logic.ReturnErrorResponse(w, r, logic.FormatError(err, "forbidden")) return } @@ -570,7 +628,7 @@ func updateUser(w http.ResponseWriter, r *http.Request) { return } } - if caller.IsAdmin && user.IsAdmin { + if caller.IsAdmin && userchange.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 diff --git a/logic/auth.go b/logic/auth.go index 43068af06..b469f1052 100644 --- a/logic/auth.go +++ b/logic/auth.go @@ -166,6 +166,10 @@ func UpdateUser(userchange, user *models.User) (*models.User, error) { queryUser := user.UserName if 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 } From e544feeb86d61dfc4be069ecb03532a41d25687a Mon Sep 17 00:00:00 2001 From: Abhishek Kondur Date: Tue, 22 Aug 2023 13:26:04 +0530 Subject: [PATCH 11/38] add api to list users on a ingress gw --- controllers/node.go | 38 +++++--------------------------------- logic/gateway.go | 19 +++++++++++++++++++ logic/users.go | 15 --------------- models/structs.go | 17 +++++++++++------ 4 files changed, 35 insertions(+), 54 deletions(-) diff --git a/controllers/node.go b/controllers/node.go index 91b0b8d8b..3be3e4d81 100644 --- a/controllers/node.go +++ b/controllers/node.go @@ -30,7 +30,7 @@ func nodeHandlers(r *mux.Router) { 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}/ingress/users", logic.SecurityCheck(false, http.HandlerFunc(deleteIngressGateway))).Methods(http.MethodDelete) + r.HandleFunc("/api/nodes/{network}/{nodeid}/ingress/users", logic.SecurityCheck(false, http.HandlerFunc(IngressGatewayUsers))).Methods(http.MethodGet) r.HandleFunc("/api/nodes/{network}/{nodeid}", Authorize(true, true, "node", http.HandlerFunc(updateNode))).Methods(http.MethodPost) r.HandleFunc("/api/nodes/adm/{network}/authenticate", authenticate).Methods(http.MethodPost) r.HandleFunc("/api/v1/nodes/migrate", migrate).Methods(http.MethodPost) @@ -617,43 +617,15 @@ func IngressGatewayUsers(w http.ResponseWriter, r *http.Request) { logic.ReturnErrorResponse(w, r, logic.FormatError(err, "bad request")) return } - node, wasFailover, removedClients, err := logic.DeleteIngressGateway(nodeid) + gwUsers, err := logic.GetIngressGwUsers(node) if err != nil { - logger.Log(0, r.Header.Get("user"), - fmt.Sprintf("failed to delete ingress gateway on node [%s] on network [%s]: %v", - nodeid, netid, err)) + slog.Error("failed to get users on ingress gateway", "nodeid", nodeid, "network", netid, "user", r.Header.Get("user"), + "error", err) logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal")) return } - - if servercfg.Is_EE && wasFailover { - if err = logic.EnterpriseResetFailoverFunc(node.Network); err != nil { - logger.Log(1, "failed to reset failover list during failover create", node.ID.String(), node.Network) - } - } - - apiNode := node.ConvertToAPINode() - logger.Log(1, r.Header.Get("user"), "deleted ingress gateway", nodeid) w.WriteHeader(http.StatusOK) - json.NewEncoder(w).Encode(apiNode) - - if len(removedClients) > 0 { - host, err := logic.GetHost(node.HostID.String()) - if err == nil { - allNodes, err := logic.GetAllNodes() - if err != nil { - return - } - go mq.PublishSingleHostPeerUpdate( - host, - allNodes, - nil, - removedClients[:], - ) - } - } - - runUpdates(&node, true) + json.NewEncoder(w).Encode(gwUsers) } // swagger:route PUT /api/nodes/{network}/{nodeid} nodes updateNode diff --git a/logic/gateway.go b/logic/gateway.go index 094ffa800..28c998310 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 _, ok := user.RemoteGwIDs[node.ID.String()]; ok { + 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{} diff --git a/logic/users.go b/logic/users.go index f18459e84..e73957478 100644 --- a/logic/users.go +++ b/logic/users.go @@ -46,21 +46,6 @@ func ToReturnUser(user models.User) models.ReturnUser { } } -// 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 -} - // SetUserDefaults - sets the defaults of a user to avoid empty fields func SetUserDefaults(user *models.User) { if user.RemoteGwIDs == nil { diff --git a/models/structs.go b/models/structs.go index d3f3df496..2f40db9f9 100644 --- a/models/structs.go +++ b/models/structs.go @@ -27,17 +27,16 @@ 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"` IsAdmin bool `json:"isadmin" bson:"isadmin"` - IsSuperAdmin bool `json:"super_admin"` + IsSuperAdmin bool `json:"issuperadmin"` RemoteGwIDs map[string]struct{} `json:"remote_gw_ids"` } // ReturnUser - return user struct type ReturnUser struct { - UserName string `json:"username"` - Networks []string `json:"networks"` - IsAdmin bool `json:"isadmin"` - IsSuperAdmin bool `json:"is_superadmin"` - Groups []string `json:"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 @@ -54,6 +53,12 @@ type UserClaims struct { jwt.RegisteredClaims } +type IngressGwUsers struct { + NodeID string `json:"node_id"` + Network string `json:"network"` + Users []ReturnUser `json:"users"` +} + // SuccessfulUserLoginResponse - successlogin struct type SuccessfulUserLoginResponse struct { UserName string From 595911bccb70bfcc76f7e71fd8dc597650a0c4f8 Mon Sep 17 00:00:00 2001 From: Abhishek Kondur Date: Tue, 22 Aug 2023 22:43:05 +0530 Subject: [PATCH 12/38] restrict user access to resources on server --- controllers/dns.go | 12 +++--- controllers/enrollmentkeys.go | 2 +- controllers/ext_client.go | 74 ++++++++++++++++++++++++----------- controllers/hosts.go | 2 +- controllers/network.go | 33 +++++----------- controllers/node.go | 6 +-- controllers/user.go | 11 +++--- logic/gateway.go | 14 +++++++ logic/security.go | 3 -- 9 files changed, 91 insertions(+), 66 deletions(-) diff --git a/controllers/dns.go b/controllers/dns.go index eb038fc0a..8d7be2918 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 5a1a8fa2f..dfe579152 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) } diff --git a/controllers/ext_client.go b/controllers/ext_client.go index ffbd31cd8..32d4eab7b 100644 --- a/controllers/ext_client.go +++ b/controllers/ext_client.go @@ -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}", getExtClientConf).Methods(http.MethodGet) - r.HandleFunc("/api/extclients/{network}/{clientid}", updateExtClient).Methods(http.MethodPut) - r.HandleFunc("/api/extclients/{network}/{clientid}", deleteExtClient).Methods(http.MethodDelete) - r.HandleFunc("/api/extclients/{network}/{nodeid}", 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, http.HandlerFunc(createExtClient))).Methods(http.MethodPost) } func checkIngressExists(nodeID string) bool { @@ -99,24 +99,14 @@ func getAllExtClients(w http.ResponseWriter, r *http.Request) { logic.ReturnErrorResponse(w, r, logic.FormatError(marshalErr, "internal")) return } - clients := []models.ExtClient{} + var err error - if len(networksSlice) > 0 && networksSlice[0] == logic.ALL_NETWORK_ACCESS { - clients, err = logic.GetAllExtClients() - if err != nil && !database.IsEmptyRecord(err) { - logger.Log(0, "failed to get all extclients: ", err.Error()) - logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal")) - return - } - } else { - for _, network := range networksSlice { - extclients, err := logic.GetNetworkExtClients(network) - if err == nil { - clients = append(clients, extclients...) - } - } + clients, err := logic.GetAllExtClients() + if err != nil && !database.IsEmptyRecord(err) { + logger.Log(0, "failed to get all extclients: ", err.Error()) + logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal")) + return } - //Return all the extclients in JSON format logic.SortExtClient(clients[:]) w.WriteHeader(http.StatusOK) @@ -149,6 +139,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 +177,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,6 +327,17 @@ func createExtClient(w http.ResponseWriter, r *http.Request) { logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest")) return } + caller, err := logic.GetUser(r.Header.Get("user")) + if err != nil { + logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal")) + } + if !caller.IsAdmin || !caller.IsSuperAdmin { + if _, ok := caller.RemoteGwIDs[nodeid]; !ok { + slog.Error("failed to create extclient", "error", errors.New("no permission")) + logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("no permission"), "forbidden")) + return + } + } var customExtClient models.CustomExtClient @@ -411,12 +426,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")) @@ -496,6 +520,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"), diff --git a/controllers/hosts.go b/controllers/hosts.go index 1cff4aea8..5a0fab352 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 74faff1da..80c74ac01 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) { + logger.Log(0, r.Header.Get("user"), "failed to fetch networks: ", err.Error()) + logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal")) + return } logger.Log(2, r.Header.Get("user"), "fetched networks.") diff --git a/controllers/node.go b/controllers/node.go index 3be3e4d81..189403835 100644 --- a/controllers/node.go +++ b/controllers/node.go @@ -28,9 +28,9 @@ func nodeHandlers(r *mux.Router) { 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}/ingress/users", logic.SecurityCheck(false, http.HandlerFunc(IngressGatewayUsers))).Methods(http.MethodGet) + 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/{network}/{nodeid}/ingress/users", logic.SecurityCheck(true, http.HandlerFunc(IngressGatewayUsers))).Methods(http.MethodGet) r.HandleFunc("/api/nodes/{network}/{nodeid}", Authorize(true, true, "node", http.HandlerFunc(updateNode))).Methods(http.MethodPost) r.HandleFunc("/api/nodes/adm/{network}/authenticate", authenticate).Methods(http.MethodPost) r.HandleFunc("/api/v1/nodes/migrate", migrate).Methods(http.MethodPost) diff --git a/controllers/user.go b/controllers/user.go index 5b9e9fe20..51633dc21 100644 --- a/controllers/user.go +++ b/controllers/user.go @@ -544,22 +544,21 @@ func createUser(w http.ResponseWriter, r *http.Request) { return } if !caller.IsSuperAdmin && user.IsAdmin { - slog.Error("error creating new user: ", "user", user.UserName, "error", - errors.New("only superadmin can create admin users")) + 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 { - slog.Error("error creating new user: ", "user", user.UserName, "error", - errors.New("additional superadmins cannot be created")) + 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()) + logger.Log(0, user.UserName, "error creating new user: ", err.Error()) logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest")) return } diff --git a/logic/gateway.go b/logic/gateway.go index 28c998310..cf61ad225 100644 --- a/logic/gateway.go +++ b/logic/gateway.go @@ -229,3 +229,17 @@ 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 { + user, err := GetUser(username) + if err != nil { + return false + } + if !user.IsAdmin && !user.IsSuperAdmin { + if user.UserName != client.ClientID { + return false + } + } + return true +} diff --git a/logic/security.go b/logic/security.go index 777a83387..bf0ec7624 100644 --- a/logic/security.go +++ b/logic/security.go @@ -10,9 +10,6 @@ import ( ) const ( - // ALL_NETWORK_ACCESS - represents all networks - ALL_NETWORK_ACCESS = "THIS_USER_HAS_ALL" - master_uname = "masteradministrator" Forbidden_Msg = "forbidden" Forbidden_Err = models.Error(Forbidden_Msg) From b301a1e29dbc8dd1fc5c85f2e56cd1cffd3a207f Mon Sep 17 00:00:00 2001 From: Abhishek Kondur Date: Wed, 23 Aug 2023 11:44:38 +0530 Subject: [PATCH 13/38] deny request from remote access client if extclient is already created --- cli/cmd/user/list.go | 3 +- controllers/ext_client.go | 51 ++++++++++----- controllers/user.go | 134 ++++++++++++++++++-------------------- logic/users.go | 1 + models/structs.go | 15 +++++ 5 files changed, 116 insertions(+), 88 deletions(-) diff --git a/cli/cmd/user/list.go b/cli/cmd/user/list.go index 3dd9e01ca..f13ed97c6 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" @@ -25,7 +24,7 @@ var userListCmd = &cobra.Command{ table := tablewriter.NewWriter(os.Stdout) table.SetHeader([]string{"Name", "Admin", "Networks", "Groups"}) 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/controllers/ext_client.go b/controllers/ext_client.go index 32d4eab7b..cdf2f5c40 100644 --- a/controllers/ext_client.go +++ b/controllers/ext_client.go @@ -327,18 +327,6 @@ func createExtClient(w http.ResponseWriter, r *http.Request) { logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest")) return } - caller, err := logic.GetUser(r.Header.Get("user")) - if err != nil { - logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal")) - } - if !caller.IsAdmin || !caller.IsSuperAdmin { - if _, ok := caller.RemoteGwIDs[nodeid]; !ok { - slog.Error("failed to create extclient", "error", errors.New("no permission")) - logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("no permission"), "forbidden")) - return - } - } - var customExtClient models.CustomExtClient if err := json.NewDecoder(r.Body).Decode(&customExtClient); err != nil { @@ -349,9 +337,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"), @@ -359,6 +344,42 @@ func createExtClient(w http.ResponseWriter, r *http.Request) { logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal")) return } + caller, err := logic.GetUser(r.Header.Get("user")) + if err != nil { + logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal")) + return + } + 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 = caller.UserName + extclient.RemoteAccessClientID = customExtClient.RemoteAccessClientID + extclient.IngressGatewayID = nodeid + extclient.Network = node.Network host, err := logic.GetHost(node.HostID.String()) if err != nil { diff --git a/controllers/user.go b/controllers/user.go index 51633dc21..837b74898 100644 --- a/controllers/user.go +++ b/controllers/user.go @@ -28,9 +28,9 @@ func userHandlers(r *mux.Router) { r.HandleFunc("/api/users/adm/createsuperadmin", createSuperAdmin).Methods(http.MethodPost) r.HandleFunc("/api/users/adm/transfersuperadmin", logic.SecurityCheck(true, http.HandlerFunc(transferSuperAdmin))).Methods(http.MethodPost) r.HandleFunc("/api/users/adm/authenticate", authenticateUser).Methods(http.MethodPost) - r.HandleFunc("/api/users/{username}/remote_access_gw", logic.SecurityCheck(true, http.HandlerFunc(attachUserToRemoteAccessGw))).Methods(http.MethodPost) - r.HandleFunc("/api/users/{username}/remote_access_gw", logic.SecurityCheck(true, http.HandlerFunc(removeUserFromRemoteAccessGW))).Methods(http.MethodDelete) - r.HandleFunc("/api/users/{username}/remote_access_gw", logic.SecurityCheck(false, http.HandlerFunc(getUserRemoteAccessGws))).Methods(http.MethodGet) + 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/{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) @@ -216,11 +216,12 @@ func attachUserToRemoteAccessGw(w http.ResponseWriter, r *http.Request) { user.RemoteGwIDs[node.ID.String()] = struct{}{} err = logic.UpsertUser(*user) if err != nil { - slog.Error("failed to update user gateways", "user", username, "error", err) - logic.ReturnErrorResponse(w, r, logic.FormatError(fmt.Errorf("failed to fetch remote access gaetway node", err), "badrequest")) + 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(user) + + json.NewEncoder(w).Encode(logic.ToReturnUser(*user)) } // swagger:route DELETE /api/users/{username}/remote_access_gw user removeUserFromRemoteAccessGW @@ -252,22 +253,26 @@ func removeUserFromRemoteAccessGW(w http.ResponseWriter, r *http.Request) { return } delete(user.RemoteGwIDs, remoteGwID) - //TODO: remove all related ext client configs of the user + 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 } - logic.ReturnSuccessResponse(w, r, fmt.Sprintf("removed user %s from remote access gateway %s", username, remoteGwID)) -} - -type UserRemoteGws struct { - GwID string `json:"remote_access_gw_id"` - GWName string `json:"gw_name"` - Network string `json:"network"` - Connected bool `json:"connected"` - GwClient models.ExtClient `json:"gw_client"` + json.NewEncoder(w).Encode(logic.ToReturnUser(*user)) } // swagger:route GET "/api/users/{username}/remote_access_gw" nodes getUserRemoteAccessGws @@ -287,19 +292,32 @@ func getUserRemoteAccessGws(w http.ResponseWriter, r *http.Request) { var params = mux.Vars(r) username := params["username"] - remoteClientID := params["remote_client_id"] - if username == "" || remoteClientID == "" { - logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("required params username and remote_client_id"), "badrequest")) + if username == "" { + logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("required params username"), "badrequest")) return } - - userGws := make(map[string][]UserRemoteGws) + 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")) @@ -307,7 +325,7 @@ func getUserRemoteAccessGws(w http.ResponseWriter, r *http.Request) { } for _, extClient := range allextClients { - if extClient.RemoteAccessClientID == remoteClientID && extClient.OwnerID == username { + if extClient.RemoteAccessClientID == req.RemoteAccessClientID && extClient.OwnerID == username { node, err := logic.GetNodeByID(extClient.IngressGatewayID) if err != nil { continue @@ -320,10 +338,10 @@ func getUserRemoteAccessGws(w http.ResponseWriter, r *http.Request) { continue } - if _, ok := user.RemoteGwIDs[node.ID.String()]; ok || user.IsSuperAdmin || user.IsAdmin { + if _, ok := user.RemoteGwIDs[node.ID.String()]; ok { gws := userGws[node.Network] - gws = append(gws, UserRemoteGws{ + gws = append(gws, models.UserRemoteGws{ GwID: node.ID.String(), GWName: host.Name, Network: node.Network, @@ -331,60 +349,34 @@ func getUserRemoteAccessGws(w http.ResponseWriter, r *http.Request) { Connected: true, }) userGws[node.Network] = gws - if user.IsAdmin || user.IsSuperAdmin { - user.RemoteGwIDs[node.ID.String()] = struct{}{} - } else { - delete(user.RemoteGwIDs, node.ID.String()) - } + delete(user.RemoteGwIDs, node.ID.String()) } } } - if user.IsAdmin || user.IsSuperAdmin { - nodes, err := logic.GetAllNodes() - if err == nil { - for _, node := range nodes { - if node.IsIngressGateway { - if _, ok := user.RemoteGwIDs[node.ID.String()]; !ok { - gws := userGws[node.Network] - host, err := logic.GetHost(node.HostID.String()) - if err != nil { - continue - } - gws = append(gws, UserRemoteGws{ - GwID: node.ID.String(), - GWName: host.Name, - Network: node.Network, - }) - userGws[node.Network] = gws - } - } - } + + // add remaining gw nodes to resp + for gwID := range user.RemoteGwIDs { + node, err := logic.GetNodeByID(gwID) + if err != nil { + continue } - } else { - // 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, UserRemoteGws{ - GwID: node.ID.String(), - GWName: host.Name, - Network: node.Network, - }) - userGws[node.Network] = gws + 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) diff --git a/logic/users.go b/logic/users.go index e73957478..104eccca5 100644 --- a/logic/users.go +++ b/logic/users.go @@ -43,6 +43,7 @@ func ToReturnUser(user models.User) models.ReturnUser { UserName: user.UserName, IsSuperAdmin: user.IsSuperAdmin, IsAdmin: user.IsAdmin, + RemoteGwIDs: user.RemoteGwIDs, } } diff --git a/models/structs.go b/models/structs.go index 2f40db9f9..a9c91ce1f 100644 --- a/models/structs.go +++ b/models/structs.go @@ -53,12 +53,27 @@ type UserClaims struct { 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 From 4cbfb3ec8d165e90cad133f8e5dd62e77d91293a Mon Sep 17 00:00:00 2001 From: Abhishek Kondur Date: Wed, 23 Aug 2023 13:46:57 +0530 Subject: [PATCH 14/38] fix user tests --- controllers/user_test.go | 49 +--------------------------------------- 1 file changed, 1 insertion(+), 48 deletions(-) diff --git a/controllers/user_test.go b/controllers/user_test.go index bc23b268c..2f045682f 100644 --- a/controllers/user_test.go +++ b/controllers/user_test.go @@ -52,54 +52,6 @@ func TestCreateAdminNoHashedPassword(t *testing.T) { 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 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, issuperadmin, isadmin bool, err error) { - return user1.UserName, user1.IsSuperAdmin, 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", IsSuperAdmin: 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) @@ -107,6 +59,7 @@ 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 } From ad32f063ce8d2eee61b00885150782a53b6461c3 Mon Sep 17 00:00:00 2001 From: Abhishek Kondur Date: Wed, 23 Aug 2023 13:50:38 +0530 Subject: [PATCH 15/38] fix static checks --- controllers/user.go | 65 --------------------------------------------- 1 file changed, 65 deletions(-) diff --git a/controllers/user.go b/controllers/user.go index 837b74898..25b0ebcbc 100644 --- a/controllers/user.go +++ b/controllers/user.go @@ -20,9 +20,6 @@ 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/hassuperadmin", hasSuperAdmin).Methods(http.MethodGet) r.HandleFunc("/api/users/adm/createsuperadmin", createSuperAdmin).Methods(http.MethodPost) @@ -644,57 +641,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 && !user.IsSuperAdmin { - 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. @@ -770,14 +716,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 -} From f3f2cfd8ab998839e362a50b5991f900147628a8 Mon Sep 17 00:00:00 2001 From: Abhishek Kondur Date: Wed, 23 Aug 2023 14:06:30 +0530 Subject: [PATCH 16/38] fix static checks --- controllers/node.go | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/controllers/node.go b/controllers/node.go index 189403835..09a67aaa8 100644 --- a/controllers/node.go +++ b/controllers/node.go @@ -24,14 +24,13 @@ 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}/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/{network}/{nodeid}/ingress/users", logic.SecurityCheck(true, http.HandlerFunc(IngressGatewayUsers))).Methods(http.MethodGet) - r.HandleFunc("/api/nodes/{network}/{nodeid}", Authorize(true, true, "node", http.HandlerFunc(updateNode))).Methods(http.MethodPost) r.HandleFunc("/api/nodes/adm/{network}/authenticate", authenticate).Methods(http.MethodPost) r.HandleFunc("/api/v1/nodes/migrate", migrate).Methods(http.MethodPost) } @@ -748,13 +747,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) From 98e7a98acb65fd31ce054b00c6f11600511bffe5 Mon Sep 17 00:00:00 2001 From: Abhishek Kondur Date: Wed, 23 Aug 2023 14:36:13 +0530 Subject: [PATCH 17/38] add limits to extclient create handler --- controllers/ext_client.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/controllers/ext_client.go b/controllers/ext_client.go index cdf2f5c40..b783ae057 100644 --- a/controllers/ext_client.go +++ b/controllers/ext_client.go @@ -29,7 +29,7 @@ func extClientHandlers(r *mux.Router) { 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, http.HandlerFunc(createExtClient))).Methods(http.MethodPost) + r.HandleFunc("/api/extclients/{network}/{nodeid}", logic.SecurityCheck(false, checkFreeTierLimits(limitChoiceMachines, http.HandlerFunc(createExtClient)))).Methods(http.MethodPost) } func checkIngressExists(nodeID string) bool { From 6ffac8ce5deacc99a5233b596bd6e37d82f52682 Mon Sep 17 00:00:00 2001 From: Abhishek Kondur Date: Wed, 23 Aug 2023 22:14:39 +0530 Subject: [PATCH 18/38] set username to superadmin on if masterkey is used --- logic/security.go | 5 +++++ logic/users.go | 15 +++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/logic/security.go b/logic/security.go index bf0ec7624..1aacddfde 100644 --- a/logic/security.go +++ b/logic/security.go @@ -34,6 +34,11 @@ func SecurityCheck(reqAdmin bool, next http.Handler) http.HandlerFunc { // detect masteradmin if username == master_uname { r.Header.Set("ismaster", "yes") + // set user as superadmin + user, err := GetSuperAdmin() + if err == nil { + username = user.UserName + } } r.Header.Set("user", username) next.ServeHTTP(w, r) diff --git a/logic/users.go b/logic/users.go index 104eccca5..994a71dba 100644 --- a/logic/users.go +++ b/logic/users.go @@ -2,6 +2,7 @@ package logic import ( "encoding/json" + "errors" "sort" "github.com/gravitl/netmaker/database" @@ -60,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") +} From 6e7f8c7af3e820210164ed23220939eed82e7585 Mon Sep 17 00:00:00 2001 From: Abhishek Kondur Date: Wed, 23 Aug 2023 22:42:51 +0530 Subject: [PATCH 19/38] allow creation of extclients using masterkey --- controllers/ext_client.go | 49 +++++++++++++++++++++------------------ logic/jwts.go | 2 +- logic/security.go | 11 +++------ 3 files changed, 31 insertions(+), 31 deletions(-) diff --git a/controllers/ext_client.go b/controllers/ext_client.go index b783ae057..18550dbae 100644 --- a/controllers/ext_client.go +++ b/controllers/ext_client.go @@ -344,39 +344,44 @@ func createExtClient(w http.ResponseWriter, r *http.Request) { logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal")) return } - caller, err := logic.GetUser(r.Header.Get("user")) - if err != nil { - logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal")) - return - } - 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) + var userName string + if r.Header.Get("ismaster") == "yes" { + userName = logic.Master_uname + } else { + caller, err := logic.GetUser(r.Header.Get("user")) 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") + 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, "badrequest")) + 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 = caller.UserName + extclient.OwnerID = userName extclient.RemoteAccessClientID = customExtClient.RemoteAccessClientID extclient.IngressGatewayID = nodeid diff --git a/logic/jwts.go b/logic/jwts.go index 9b53a6e85..3d2374eee 100644 --- a/logic/jwts.go +++ b/logic/jwts.go @@ -92,7 +92,7 @@ func VerifyUserToken(tokenString string) (username string, issuperadmin, isadmin claims := &models.UserClaims{} if tokenString == servercfg.GetMasterKey() && servercfg.GetMasterKey() != "" { - return "masteradministrator", true, true, nil + return Master_uname, true, true, nil } token, err := jwt.ParseWithClaims(tokenString, claims, func(token *jwt.Token) (interface{}, error) { diff --git a/logic/security.go b/logic/security.go index 1aacddfde..b53c348e9 100644 --- a/logic/security.go +++ b/logic/security.go @@ -10,7 +10,7 @@ import ( ) const ( - master_uname = "masteradministrator" + Master_uname = "masteradministrator" Forbidden_Msg = "forbidden" Forbidden_Err = models.Error(Forbidden_Msg) Unauthorized_Msg = "unauthorized" @@ -32,13 +32,8 @@ func SecurityCheck(reqAdmin bool, next http.Handler) http.HandlerFunc { return } // detect masteradmin - if username == master_uname { + if username == Master_uname { r.Header.Set("ismaster", "yes") - // set user as superadmin - user, err := GetSuperAdmin() - if err == nil { - username = user.UserName - } } r.Header.Set("user", username) next.ServeHTTP(w, r) @@ -58,7 +53,7 @@ func UserPermissions(reqAdmin bool, token string) (string, error) { //all endpoints here require master so not as complicated if authenticateMaster(authToken) { // TODO log in as an actual admin user - return master_uname, nil + return Master_uname, nil } username, issuperadmin, isadmin, err := VerifyUserToken(authToken) if err != nil { From ed5d022af0210b27f8b8424f0f6cebb0600ecb79 Mon Sep 17 00:00:00 2001 From: Abhishek Kondur Date: Thu, 24 Aug 2023 11:44:06 +0530 Subject: [PATCH 20/38] add migration func to assign superadmin role for existing admin user --- controllers/user.go | 13 +++++++------ logic/gateway.go | 2 +- migrate/migrate.go | 35 +++++++++++++++++++++++++++++++++++ 3 files changed, 43 insertions(+), 7 deletions(-) diff --git a/controllers/user.go b/controllers/user.go index 25b0ebcbc..f62009cb4 100644 --- a/controllers/user.go +++ b/controllers/user.go @@ -23,7 +23,7 @@ var ( func userHandlers(r *mux.Router) { 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", logic.SecurityCheck(true, http.HandlerFunc(transferSuperAdmin))).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}/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) @@ -471,10 +471,11 @@ func transferSuperAdmin(w http.ResponseWriter, r *http.Request) { logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("only superadmin can assign the superadmin role to another user"), "forbidden")) return } - var u models.User - err = json.NewDecoder(r.Body).Decode(&u) + var params = mux.Vars(r) + username := params["username"] + u, err := logic.GetUser(username) if err != nil { - slog.Error("error decoding request body: ", "error", err.Error()) + slog.Error("error getting user", "user", u.UserName, "error", err.Error()) logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest")) return } @@ -489,7 +490,7 @@ func transferSuperAdmin(w http.ResponseWriter, r *http.Request) { u.IsSuperAdmin = true u.IsAdmin = false - err = logic.UpsertUser(u) + 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")) @@ -504,7 +505,7 @@ func transferSuperAdmin(w http.ResponseWriter, r *http.Request) { return } slog.Info("user was made a super admin", "user", u.UserName) - json.NewEncoder(w).Encode(logic.ToReturnUser(u)) + json.NewEncoder(w).Encode(logic.ToReturnUser(*u)) } // swagger:route POST /api/users/{username} user createUser diff --git a/logic/gateway.go b/logic/gateway.go index cf61ad225..02c639c9f 100644 --- a/logic/gateway.go +++ b/logic/gateway.go @@ -171,7 +171,7 @@ func GetIngressGwUsers(node models.Node) (models.IngressGwUsers, error) { return gwUsers, err } for _, user := range users { - if _, ok := user.RemoteGwIDs[node.ID.String()]; ok { + if !user.IsAdmin && !user.IsSuperAdmin { gwUsers.Users = append(gwUsers.Users, user) } } diff --git a/migrate/migrate.go b/migrate/migrate.go index 2f0645283..b8380e91e 100644 --- a/migrate/migrate.go +++ b/migrate/migrate.go @@ -5,12 +5,47 @@ 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() { + ok, _ := logic.HasSuperAdmin() + if !ok { + createdSuperAdmin := false + users, err := logic.GetUsers() + if err == nil { + 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 { + logger.FatalLog0("failed to create superadmin!!") + } + } } func updateEnrollmentKeys() { From 484ce65059d8080614c2521c51b8156338b96a2a Mon Sep 17 00:00:00 2001 From: Abhishek Kondur Date: Thu, 24 Aug 2023 17:13:27 +0530 Subject: [PATCH 21/38] check for superadmin on migration if users are present --- controllers/ext_client.go | 1 + migrate/migrate.go | 56 +++++++++++++++++++++------------------ 2 files changed, 31 insertions(+), 26 deletions(-) diff --git a/controllers/ext_client.go b/controllers/ext_client.go index 18550dbae..cc9d6d916 100644 --- a/controllers/ext_client.go +++ b/controllers/ext_client.go @@ -353,6 +353,7 @@ func createExtClient(w http.ResponseWriter, r *http.Request) { 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") diff --git a/migrate/migrate.go b/migrate/migrate.go index b8380e91e..22b70251d 100644 --- a/migrate/migrate.go +++ b/migrate/migrate.go @@ -17,35 +17,39 @@ func Run() { } func assignSuperAdmin() { - ok, _ := logic.HasSuperAdmin() - if !ok { - createdSuperAdmin := false - users, err := logic.GetUsers() - if err == nil { - 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 - } + 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 } - } - if !createdSuperAdmin { - logger.FatalLog0("failed to create superadmin!!") + 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() { From 72ba473df3eae6cf3f07ef17499e64da22a8c47d Mon Sep 17 00:00:00 2001 From: Abhishek Kondur Date: Thu, 24 Aug 2023 19:43:05 +0530 Subject: [PATCH 22/38] allowe masterkey to extcleint apis --- logic/gateway.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/logic/gateway.go b/logic/gateway.go index 02c639c9f..f0e300f21 100644 --- a/logic/gateway.go +++ b/logic/gateway.go @@ -232,6 +232,9 @@ func DeleteGatewayExtClients(gatewayID string, networkName string) error { // IsUserAllowedAccessToExtClient - checks if user has permission to access extclient func IsUserAllowedAccessToExtClient(username string, client models.ExtClient) bool { + if username == Master_uname { + return true + } user, err := GetUser(username) if err != nil { return false From 41d6ca9d5c799f7f11671d3ca67dec393e78953f Mon Sep 17 00:00:00 2001 From: Abhishek Kondur Date: Thu, 24 Aug 2023 21:17:00 +0530 Subject: [PATCH 23/38] check ownerid --- logic/gateway.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/logic/gateway.go b/logic/gateway.go index f0e300f21..671139eb5 100644 --- a/logic/gateway.go +++ b/logic/gateway.go @@ -240,7 +240,7 @@ func IsUserAllowedAccessToExtClient(username string, client models.ExtClient) bo return false } if !user.IsAdmin && !user.IsSuperAdmin { - if user.UserName != client.ClientID { + if user.UserName != client.OwnerID { return false } } From 299d086e006ba55c5c3646daac446f29a89a3046 Mon Sep 17 00:00:00 2001 From: Abhishek Kondur Date: Fri, 25 Aug 2023 07:32:35 +0530 Subject: [PATCH 24/38] format error, on jwt token verification failure return unauthorized rather than forbidden --- logic/security.go | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/logic/security.go b/logic/security.go index b53c348e9..373576422 100644 --- a/logic/security.go +++ b/logic/security.go @@ -21,14 +21,11 @@ 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") bearerToken := r.Header.Get("Authorization") username, err := UserPermissions(reqAdmin, bearerToken) if err != nil { - ReturnErrorResponse(w, r, errorResponse) + ReturnErrorResponse(w, r, FormatError(err, err.Error())) return } // detect masteradmin From 8136d03e0a0a5f1118a40b16690f4dd3b2671c5a Mon Sep 17 00:00:00 2001 From: Abhishek Kondur Date: Fri, 25 Aug 2023 08:28:03 +0530 Subject: [PATCH 25/38] user update fix --- controllers/node.go | 2 +- controllers/user.go | 2 +- logic/auth.go | 4 +--- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/controllers/node.go b/controllers/node.go index 09a67aaa8..910c3d1fa 100644 --- a/controllers/node.go +++ b/controllers/node.go @@ -198,7 +198,7 @@ func Authorize(hostAllowed, networkCheck bool, authNetwork string, next http.Han var nodeID = "" 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 } diff --git a/controllers/user.go b/controllers/user.go index f62009cb4..9c6b0a5f8 100644 --- a/controllers/user.go +++ b/controllers/user.go @@ -617,7 +617,7 @@ func updateUser(w http.ResponseWriter, r *http.Request) { return } } - if caller.IsAdmin && userchange.IsAdmin { + if !selfUpdate && caller.IsAdmin && userchange.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 diff --git a/logic/auth.go b/logic/auth.go index b469f1052..f8d1489a1 100644 --- a/logic/auth.go +++ b/logic/auth.go @@ -164,15 +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 userchange.Password != "" { // encrypt that password so we never see it again hash, err := bcrypt.GenerateFromPassword([]byte(userchange.Password), 5) From be71536c2ae03045d14e3294a899b880b9da1e8a Mon Sep 17 00:00:00 2001 From: Abhishek Kondur Date: Fri, 25 Aug 2023 15:59:01 +0530 Subject: [PATCH 26/38] move user remote functionality to ee --- controllers/node.go | 33 ----- controllers/user.go | 218 ------------------------------ ee/ee_controllers/users.go | 268 +++++++++++++++++++++++++++++++++++++ 3 files changed, 268 insertions(+), 251 deletions(-) create mode 100644 ee/ee_controllers/users.go diff --git a/controllers/node.go b/controllers/node.go index 910c3d1fa..07fda7bb3 100644 --- a/controllers/node.go +++ b/controllers/node.go @@ -30,7 +30,6 @@ func nodeHandlers(r *mux.Router) { 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/{network}/{nodeid}/ingress/users", logic.SecurityCheck(true, http.HandlerFunc(IngressGatewayUsers))).Methods(http.MethodGet) r.HandleFunc("/api/nodes/adm/{network}/authenticate", authenticate).Methods(http.MethodPost) r.HandleFunc("/api/v1/nodes/migrate", migrate).Methods(http.MethodPost) } @@ -595,38 +594,6 @@ func deleteIngressGateway(w http.ResponseWriter, r *http.Request) { runUpdates(&node, true) } -// swagger:route GET /api/nodes/{network}/{nodeid}/ingress/users nodes 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) - nodeid := params["nodeid"] - netid := params["network"] - node, err := validateParams(nodeid, netid) - if err != nil { - logic.ReturnErrorResponse(w, r, logic.FormatError(err, "bad request")) - return - } - gwUsers, err := logic.GetIngressGwUsers(node) - if err != nil { - slog.Error("failed to get users on ingress gateway", "nodeid", nodeid, "network", netid, "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) -} - // swagger:route PUT /api/nodes/{network}/{nodeid} nodes updateNode // // Update an individual node. diff --git a/controllers/user.go b/controllers/user.go index 9c6b0a5f8..0261ea325 100644 --- a/controllers/user.go +++ b/controllers/user.go @@ -25,9 +25,6 @@ func userHandlers(r *mux.Router) { 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}/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/{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) @@ -165,221 +162,6 @@ func getUser(w http.ResponseWriter, r *http.Request) { json.NewEncoder(w).Encode(user) } -// 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/users user getUsers // // Get all users. diff --git a/ee/ee_controllers/users.go b/ee/ee_controllers/users.go new file mode 100644 index 000000000..615c0e656 --- /dev/null +++ b/ee/ee_controllers/users.go @@ -0,0 +1,268 @@ +package ee_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) +} From 2f78fc895d95ead2541366e735bbcb03f0da4548 Mon Sep 17 00:00:00 2001 From: Abhishek Kondur Date: Mon, 28 Aug 2023 16:40:16 +0530 Subject: [PATCH 27/38] fix update user api --- controllers/user.go | 42 ++++++++++++++++++++++++++---------------- 1 file changed, 26 insertions(+), 16 deletions(-) diff --git a/controllers/user.go b/controllers/user.go index 0261ea325..cbcdfbf3e 100644 --- a/controllers/user.go +++ b/controllers/user.go @@ -370,40 +370,50 @@ func updateUser(w http.ResponseWriter, r *http.Request) { // we decode our body request params err = json.NewDecoder(r.Body).Decode(&userchange) if err != nil { - logger.Log(0, username, "error decoding request body: ", - err.Error()) + slog.Error("failed to decode body", "error ", err.Error()) logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest")) return } + if 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 caller.UserName == user.UserName { selfUpdate = true } - if !caller.IsSuperAdmin { - if user.IsSuperAdmin { + if !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 !selfUpdate && !(caller.IsAdmin) { - slog.Error("non-admin user", "caller", caller.UserName, "attempted to update user", username) - logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("not authorized"), "forbidden")) + 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 userchange.IsAdmin != user.IsAdmin || userchange.IsSuperAdmin != user.IsSuperAdmin { - if selfUpdate { - 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 !selfUpdate && caller.IsAdmin && userchange.IsAdmin { + 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 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 auth.IsOauthUser(user) == nil { From 06088b9a7eb5f5aa551c07a3fafbf82bdafd6449 Mon Sep 17 00:00:00 2001 From: Abhishek Kondur Date: Mon, 28 Aug 2023 16:49:44 +0530 Subject: [PATCH 28/38] security patch --- logic/auth.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/logic/auth.go b/logic/auth.go index f8d1489a1..89eb27e51 100644 --- a/logic/auth.go +++ b/logic/auth.go @@ -126,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()) From 95009fc2e3a93289e78f0ad0cd1cf0d352438597 Mon Sep 17 00:00:00 2001 From: Abhishek Kondur Date: Mon, 28 Aug 2023 17:05:54 +0530 Subject: [PATCH 29/38] initalise ee user handlers --- ee/initialize.go | 1 + 1 file changed, 1 insertion(+) diff --git a/ee/initialize.go b/ee/initialize.go index fe06c8112..e7a496293 100644 --- a/ee/initialize.go +++ b/ee/initialize.go @@ -26,6 +26,7 @@ func InitEE() { controller.HttpHandlers, ee_controllers.MetricHandlers, ee_controllers.RelayHandlers, + ee_controllers.UserHandlers, ) logic.EnterpriseCheckFuncs = append(logic.EnterpriseCheckFuncs, func() { // == License Handling == From 64370f269613ba52c29648acf9ccb193b29bffd3 Mon Sep 17 00:00:00 2001 From: Abhishek Kondur Date: Tue, 29 Aug 2023 12:41:02 +0530 Subject: [PATCH 30/38] allow user to use master key to update any user --- controllers/ext_client.go | 2 +- controllers/user.go | 28 +++++++++++++++++++++------- logic/gateway.go | 2 +- logic/jwts.go | 2 +- logic/security.go | 6 +++--- 5 files changed, 27 insertions(+), 13 deletions(-) diff --git a/controllers/ext_client.go b/controllers/ext_client.go index cc9d6d916..d95b7a4a3 100644 --- a/controllers/ext_client.go +++ b/controllers/ext_client.go @@ -346,7 +346,7 @@ func createExtClient(w http.ResponseWriter, r *http.Request) { } var userName string if r.Header.Get("ismaster") == "yes" { - userName = logic.Master_uname + userName = logic.MasterUser } else { caller, err := logic.GetUser(r.Header.Get("user")) if err != nil { diff --git a/controllers/user.go b/controllers/user.go index cbcdfbf3e..4375604eb 100644 --- a/controllers/user.go +++ b/controllers/user.go @@ -353,11 +353,18 @@ func updateUser(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") var params = mux.Vars(r) // start here - - caller, err := logic.GetUser(r.Header.Get("user")) - if err != nil { - logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal")) + 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"] user, err := logic.GetUser(username) if err != nil { @@ -379,11 +386,11 @@ func updateUser(w http.ResponseWriter, r *http.Request) { return } selfUpdate := false - if caller.UserName == user.UserName { + if !ismaster && caller.UserName == user.UserName { selfUpdate = true } - if !selfUpdate { + 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")) @@ -407,7 +414,7 @@ func updateUser(w http.ResponseWriter, r *http.Request) { } } - if selfUpdate { + 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")) @@ -415,6 +422,13 @@ func updateUser(w http.ResponseWriter, r *http.Request) { } } + 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) diff --git a/logic/gateway.go b/logic/gateway.go index 671139eb5..6a10f4a4d 100644 --- a/logic/gateway.go +++ b/logic/gateway.go @@ -232,7 +232,7 @@ func DeleteGatewayExtClients(gatewayID string, networkName string) error { // IsUserAllowedAccessToExtClient - checks if user has permission to access extclient func IsUserAllowedAccessToExtClient(username string, client models.ExtClient) bool { - if username == Master_uname { + if username == MasterUser { return true } user, err := GetUser(username) diff --git a/logic/jwts.go b/logic/jwts.go index 3d2374eee..49e15d456 100644 --- a/logic/jwts.go +++ b/logic/jwts.go @@ -92,7 +92,7 @@ func VerifyUserToken(tokenString string) (username string, issuperadmin, isadmin claims := &models.UserClaims{} if tokenString == servercfg.GetMasterKey() && servercfg.GetMasterKey() != "" { - return Master_uname, true, true, nil + return MasterUser, true, true, nil } token, err := jwt.ParseWithClaims(tokenString, claims, func(token *jwt.Token) (interface{}, error) { diff --git a/logic/security.go b/logic/security.go index 373576422..37be1f441 100644 --- a/logic/security.go +++ b/logic/security.go @@ -10,7 +10,7 @@ import ( ) const ( - Master_uname = "masteradministrator" + MasterUser = "masteradministrator" Forbidden_Msg = "forbidden" Forbidden_Err = models.Error(Forbidden_Msg) Unauthorized_Msg = "unauthorized" @@ -29,7 +29,7 @@ func SecurityCheck(reqAdmin bool, next http.Handler) http.HandlerFunc { return } // detect masteradmin - if username == Master_uname { + if username == MasterUser { r.Header.Set("ismaster", "yes") } r.Header.Set("user", username) @@ -50,7 +50,7 @@ func UserPermissions(reqAdmin bool, token string) (string, error) { //all endpoints here require master so not as complicated if authenticateMaster(authToken) { // TODO log in as an actual admin user - return Master_uname, nil + return MasterUser, nil } username, issuperadmin, isadmin, err := VerifyUserToken(authToken) if err != nil { From a986db6775636fe272cef72f6ae2d5cf3bb82649 Mon Sep 17 00:00:00 2001 From: Abhishek Kondur Date: Tue, 29 Aug 2023 16:24:13 +0530 Subject: [PATCH 31/38] use slog --- auth/auth.go | 7 ++++--- controllers/ext_client.go | 3 +-- controllers/network.go | 2 +- controllers/user.go | 14 +++++--------- 4 files changed, 11 insertions(+), 15 deletions(-) diff --git a/auth/auth.go b/auth/auth.go index 39ee0768b..7be57345a 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" @@ -240,7 +241,7 @@ func HandleHeadlessSSO(w http.ResponseWriter, r *http.Request) { func addUser(email string) error { 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("") @@ -253,9 +254,9 @@ func addUser(email string) error { } if !hasSuperAdmin { // must be first attempt, create a superadmin if err = logic.CreateSuperAdmin(&newUser); err != nil { - logger.Log(1, "error creating super admin from user,", email, "; user not added") + slog.Error("error creating super admin from user", "email", email, "error", err) } else { - logger.Log(1, "superadmin 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/controllers/ext_client.go b/controllers/ext_client.go index d95b7a4a3..b5bd7eb24 100644 --- a/controllers/ext_client.go +++ b/controllers/ext_client.go @@ -94,8 +94,7 @@ 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 } diff --git a/controllers/network.go b/controllers/network.go index 80c74ac01..3e30d07c6 100644 --- a/controllers/network.go +++ b/controllers/network.go @@ -47,7 +47,7 @@ func getNetworks(w http.ResponseWriter, r *http.Request) { allnetworks, err := logic.GetNetworks() if err != nil && !database.IsEmptyRecord(err) { - logger.Log(0, r.Header.Get("user"), "failed to fetch networks: ", err.Error()) + slog.Error("failed to fetch networks", "error", err.Error()) logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal")) return } diff --git a/controllers/user.go b/controllers/user.go index 4375604eb..69f05d677 100644 --- a/controllers/user.go +++ b/controllers/user.go @@ -208,9 +208,7 @@ func createSuperAdmin(w http.ResponseWriter, r *http.Request) { err := json.NewDecoder(r.Body).Decode(&u) if err != nil { - - logger.Log(0, u.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 } @@ -222,19 +220,17 @@ func createSuperAdmin(w http.ResponseWriter, r *http.Request) { err = logic.CreateSuperAdmin(&u) if err != nil { - logger.Log(0, u.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)) } // swagger:route POST /api/users/adm/transfersuperadmin user transferSuperAdmin // -// Transfers suoeradmin role to an admin user. +// Transfers superadmin role to an admin user. // // Schemes: https // @@ -330,11 +326,11 @@ func createUser(w http.ResponseWriter, r *http.Request) { 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)) } From 7b1aebb981f0f8db5ea43c972cc087e4d892aa02 Mon Sep 17 00:00:00 2001 From: Abhishek Kondur Date: Tue, 29 Aug 2023 16:29:52 +0530 Subject: [PATCH 32/38] fix auth user test --- controllers/user_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/controllers/user_test.go b/controllers/user_test.go index 2f045682f..c99b8c475 100644 --- a/controllers/user_test.go +++ b/controllers/user_test.go @@ -342,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 From 57c967b156735afca75fbe8aac46432946999c62 Mon Sep 17 00:00:00 2001 From: Abhishek Kondur Date: Tue, 29 Aug 2023 21:38:35 +0530 Subject: [PATCH 33/38] table headers --- cli/cmd/user/list.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/cmd/user/list.go b/cli/cmd/user/list.go index f13ed97c6..694697a28 100644 --- a/cli/cmd/user/list.go +++ b/cli/cmd/user/list.go @@ -22,7 +22,7 @@ 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.IsSuperAdmin), strconv.FormatBool(d.IsAdmin)}) } From c5aa242e10274d01cda5386b2e0ba0e8a3b32efe Mon Sep 17 00:00:00 2001 From: Abhishek Kondur Date: Thu, 31 Aug 2023 21:07:55 +0530 Subject: [PATCH 34/38] remove user role, it's covered in middleware --- controllers/enrollmentkeys.go | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/controllers/enrollmentkeys.go b/controllers/enrollmentkeys.go index dfe579152..78b0f6b63 100644 --- a/controllers/enrollmentkeys.go +++ b/controllers/enrollmentkeys.go @@ -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 && (!user.IsAdmin || !user.IsSuperAdmin) { - 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")) From 27831321804d50cdcde46b464b2763413c14ac69 Mon Sep 17 00:00:00 2001 From: Abhishek Kondur Date: Fri, 1 Sep 2023 12:35:35 +0530 Subject: [PATCH 35/38] setuser defaults fix --- serverctl/serverctl.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/serverctl/serverctl.go b/serverctl/serverctl.go index 645dfc679..1d445ddf3 100644 --- a/serverctl/serverctl.go +++ b/serverctl/serverctl.go @@ -8,6 +8,7 @@ import ( "github.com/gravitl/netmaker/logic" "github.com/gravitl/netmaker/logic/acls" "github.com/gravitl/netmaker/logic/acls/nodeacls" + "golang.org/x/exp/slog" ) const ( @@ -97,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 From 7f6e92c0b05367ee3013032023cf108ff7cc0abf Mon Sep 17 00:00:00 2001 From: Abhishek Kondur Date: Mon, 4 Sep 2023 13:08:09 +0530 Subject: [PATCH 36/38] if ingress node is deleted, cleanup gateway clients --- controllers/hosts.go | 8 ++++++++ controllers/node.go | 9 +++++++++ 2 files changed, 17 insertions(+) diff --git a/controllers/hosts.go b/controllers/hosts.go index a17a0a557..37800403c 100644 --- a/controllers/hosts.go +++ b/controllers/hosts.go @@ -328,6 +328,14 @@ func deleteHostFromNetwork(w http.ResponseWriter, r *http.Request) { // unset all the relayed nodes logic.SetRelayedNodes(false, node.ID.String(), node.RelayedNodes) } + if node.IsIngressGateway { + // delete ext clients belonging to ingress gateway + if err = logic.DeleteGatewayExtClients(node.ID.String(), node.Network); err != nil { + slog.Error("failed to delete extclients", "gatewayid", node.ID.String(), "network", node.Network, "error", err.Error()) + logic.ReturnErrorResponse(w, r, logic.FormatError(err, "bad request")) + return + } + } logger.Log(1, "deleting node", node.ID.String(), "from host", currHost.Name) if err := logic.DeleteNode(node, forceDelete); err != nil { logic.ReturnErrorResponse(w, r, logic.FormatError(fmt.Errorf("failed to delete node"), "internal")) diff --git a/controllers/node.go b/controllers/node.go index 3ccd23aef..a8f6d218e 100644 --- a/controllers/node.go +++ b/controllers/node.go @@ -717,6 +717,15 @@ func deleteNode(w http.ResponseWriter, r *http.Request) { // unset all the relayed nodes logic.SetRelayedNodes(false, node.ID.String(), node.RelayedNodes) } + if node.IsIngressGateway { + // delete ext clients belonging to ingress gateway + if err = logic.DeleteGatewayExtClients(node.ID.String(), node.Network); err != nil { + slog.Error("failed to delete extclients", "gatewayid", node.ID.String(), "network", node.Network, "error", err.Error()) + logic.ReturnErrorResponse(w, r, logic.FormatError(err, "bad request")) + return + } + } + purge := forceDelete || fromNode if err := logic.DeleteNode(&node, purge); err != nil { logic.ReturnErrorResponse(w, r, logic.FormatError(fmt.Errorf("failed to delete node"), "internal")) From 1ad96c5e4f980e9661e79f5e2df9c33ca9d00d1c Mon Sep 17 00:00:00 2001 From: Abhishek Kondur Date: Mon, 4 Sep 2023 18:25:02 +0530 Subject: [PATCH 37/38] delete ext clients in a go routine --- controllers/hosts.go | 12 +++++++----- controllers/node.go | 15 +++++++++------ 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/controllers/hosts.go b/controllers/hosts.go index 37800403c..d57e2a621 100644 --- a/controllers/hosts.go +++ b/controllers/hosts.go @@ -330,11 +330,13 @@ func deleteHostFromNetwork(w http.ResponseWriter, r *http.Request) { } if node.IsIngressGateway { // delete ext clients belonging to ingress gateway - if err = logic.DeleteGatewayExtClients(node.ID.String(), node.Network); err != nil { - slog.Error("failed to delete extclients", "gatewayid", node.ID.String(), "network", node.Network, "error", err.Error()) - logic.ReturnErrorResponse(w, r, logic.FormatError(err, "bad request")) - return - } + go func(node models.Node) { + if err = logic.DeleteGatewayExtClients(node.ID.String(), node.Network); err != nil { + slog.Error("failed to delete extclients", "gatewayid", node.ID.String(), "network", node.Network, "error", err.Error()) + logic.ReturnErrorResponse(w, r, logic.FormatError(err, "bad request")) + return + } + }(*node) } logger.Log(1, "deleting node", node.ID.String(), "from host", currHost.Name) if err := logic.DeleteNode(node, forceDelete); err != nil { diff --git a/controllers/node.go b/controllers/node.go index a8f6d218e..903606593 100644 --- a/controllers/node.go +++ b/controllers/node.go @@ -718,12 +718,15 @@ func deleteNode(w http.ResponseWriter, r *http.Request) { logic.SetRelayedNodes(false, node.ID.String(), node.RelayedNodes) } if node.IsIngressGateway { - // delete ext clients belonging to ingress gateway - if err = logic.DeleteGatewayExtClients(node.ID.String(), node.Network); err != nil { - slog.Error("failed to delete extclients", "gatewayid", node.ID.String(), "network", node.Network, "error", err.Error()) - logic.ReturnErrorResponse(w, r, logic.FormatError(err, "bad request")) - return - } + // delete ext clients belonging to ingress gatewa + go func(node models.Node) { + if err = logic.DeleteGatewayExtClients(node.ID.String(), node.Network); err != nil { + slog.Error("failed to delete extclients", "gatewayid", node.ID.String(), "network", node.Network, "error", err.Error()) + logic.ReturnErrorResponse(w, r, logic.FormatError(err, "bad request")) + return + } + }(node) + } purge := forceDelete || fromNode From cdf0443424f4c1c925ca7c5f59fc79e43ffc6cb4 Mon Sep 17 00:00:00 2001 From: Abhishek Kondur Date: Tue, 5 Sep 2023 16:18:24 +0530 Subject: [PATCH 38/38] remove response writer from go routine --- controllers/hosts.go | 2 -- controllers/node.go | 2 -- 2 files changed, 4 deletions(-) diff --git a/controllers/hosts.go b/controllers/hosts.go index d57e2a621..0c6e9fd65 100644 --- a/controllers/hosts.go +++ b/controllers/hosts.go @@ -333,8 +333,6 @@ func deleteHostFromNetwork(w http.ResponseWriter, r *http.Request) { go func(node models.Node) { if err = logic.DeleteGatewayExtClients(node.ID.String(), node.Network); err != nil { slog.Error("failed to delete extclients", "gatewayid", node.ID.String(), "network", node.Network, "error", err.Error()) - logic.ReturnErrorResponse(w, r, logic.FormatError(err, "bad request")) - return } }(*node) } diff --git a/controllers/node.go b/controllers/node.go index 903606593..81693a03b 100644 --- a/controllers/node.go +++ b/controllers/node.go @@ -722,8 +722,6 @@ func deleteNode(w http.ResponseWriter, r *http.Request) { go func(node models.Node) { if err = logic.DeleteGatewayExtClients(node.ID.String(), node.Network); err != nil { slog.Error("failed to delete extclients", "gatewayid", node.ID.String(), "network", node.Network, "error", err.Error()) - logic.ReturnErrorResponse(w, r, logic.FormatError(err, "bad request")) - return } }(node)