|
@@ -6,13 +6,14 @@ import (
|
|
|
"encoding/json"
|
|
|
"errors"
|
|
|
"fmt"
|
|
|
- "github.com/pquerna/otp"
|
|
|
- "golang.org/x/crypto/bcrypt"
|
|
|
"image/png"
|
|
|
"net/http"
|
|
|
"reflect"
|
|
|
"time"
|
|
|
|
|
|
+ "github.com/pquerna/otp"
|
|
|
+ "golang.org/x/crypto/bcrypt"
|
|
|
+
|
|
|
"github.com/google/uuid"
|
|
|
"github.com/gorilla/mux"
|
|
|
"github.com/gorilla/websocket"
|
|
@@ -776,6 +777,52 @@ func enableUserAccount(w http.ResponseWriter, r *http.Request) {
|
|
|
return
|
|
|
}
|
|
|
|
|
|
+ var caller *models.User
|
|
|
+ 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"))
|
|
|
+ return
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if !isMaster && caller.UserName == user.UserName {
|
|
|
+ // This implies that a user is trying to enable themselves.
|
|
|
+ // This can never happen, since a disabled user cannot be
|
|
|
+ // authenticated.
|
|
|
+ err := fmt.Errorf("cannot enable self")
|
|
|
+ logger.Log(0, err.Error())
|
|
|
+ logic.ReturnErrorResponse(w, r, logic.FormatError(err, "forbidden"))
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ switch user.PlatformRoleID {
|
|
|
+ case models.SuperAdminRole:
|
|
|
+ // This can never happen, since a superadmin user cannot
|
|
|
+ // be disabled.
|
|
|
+ case models.AdminRole:
|
|
|
+ if !isMaster && caller.PlatformRoleID != models.SuperAdminRole {
|
|
|
+ err = fmt.Errorf("%s cannot enable an admin", caller.PlatformRoleID)
|
|
|
+ logic.ReturnErrorResponse(w, r, logic.FormatError(err, "forbidden"))
|
|
|
+ return
|
|
|
+ }
|
|
|
+ case models.PlatformUser:
|
|
|
+ if !isMaster && caller.PlatformRoleID != models.SuperAdminRole && caller.PlatformRoleID != models.AdminRole {
|
|
|
+ err = fmt.Errorf("%s cannot enable a platform-user", caller.PlatformRoleID)
|
|
|
+ logic.ReturnErrorResponse(w, r, logic.FormatError(err, "forbidden"))
|
|
|
+ return
|
|
|
+ }
|
|
|
+ case models.ServiceUser:
|
|
|
+ if !isMaster && caller.PlatformRoleID != models.SuperAdminRole && caller.PlatformRoleID != models.AdminRole {
|
|
|
+ err = fmt.Errorf("%s cannot enable a service-user", caller.PlatformRoleID)
|
|
|
+ logic.ReturnErrorResponse(w, r, logic.FormatError(err, "forbidden"))
|
|
|
+ return
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
user.AccountDisabled = false
|
|
|
err = logic.UpsertUser(*user)
|
|
|
if err != nil {
|
|
@@ -802,13 +849,51 @@ func disableUserAccount(w http.ResponseWriter, r *http.Request) {
|
|
|
return
|
|
|
}
|
|
|
|
|
|
- if user.PlatformRoleID == models.SuperAdminRole {
|
|
|
- err = errors.New("cannot disable super-admin user account")
|
|
|
- logger.Log(0, err.Error())
|
|
|
- logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
|
|
|
+ var caller *models.User
|
|
|
+ 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"))
|
|
|
+ return
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if !isMaster && caller.UserName == user.UserName {
|
|
|
+ // This implies that a user is trying to disable themselves.
|
|
|
+ // This should not be allowed.
|
|
|
+ err = fmt.Errorf("cannot disable self")
|
|
|
+ logic.ReturnErrorResponse(w, r, logic.FormatError(err, "forbidden"))
|
|
|
return
|
|
|
}
|
|
|
|
|
|
+ switch user.PlatformRoleID {
|
|
|
+ case models.SuperAdminRole:
|
|
|
+ err = errors.New("cannot disable a super-admin")
|
|
|
+ logic.ReturnErrorResponse(w, r, logic.FormatError(err, "forbidden"))
|
|
|
+ return
|
|
|
+ case models.AdminRole:
|
|
|
+ if !isMaster && caller.PlatformRoleID != models.SuperAdminRole {
|
|
|
+ err = fmt.Errorf("%s cannot disable an admin", caller.PlatformRoleID)
|
|
|
+ logic.ReturnErrorResponse(w, r, logic.FormatError(err, "forbidden"))
|
|
|
+ return
|
|
|
+ }
|
|
|
+ case models.PlatformUser:
|
|
|
+ if !isMaster && caller.PlatformRoleID != models.SuperAdminRole && caller.PlatformRoleID != models.AdminRole {
|
|
|
+ err = fmt.Errorf("%s cannot disable a platform-user", caller.PlatformRoleID)
|
|
|
+ logic.ReturnErrorResponse(w, r, logic.FormatError(err, "forbidden"))
|
|
|
+ return
|
|
|
+ }
|
|
|
+ case models.ServiceUser:
|
|
|
+ if !isMaster && caller.PlatformRoleID != models.SuperAdminRole && caller.PlatformRoleID != models.AdminRole {
|
|
|
+ err = fmt.Errorf("%s cannot disable a service-user", caller.PlatformRoleID)
|
|
|
+ logic.ReturnErrorResponse(w, r, logic.FormatError(err, "forbidden"))
|
|
|
+ return
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
user.AccountDisabled = true
|
|
|
err = logic.UpsertUser(*user)
|
|
|
if err != nil {
|
|
@@ -816,6 +901,28 @@ func disableUserAccount(w http.ResponseWriter, r *http.Request) {
|
|
|
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
|
|
|
}
|
|
|
|
|
|
+ go func() {
|
|
|
+ extclients, err := logic.GetAllExtClients()
|
|
|
+ if err != nil {
|
|
|
+ logger.Log(0, "failed to get user extclients:", err.Error())
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ for _, extclient := range extclients {
|
|
|
+ if extclient.OwnerID == user.UserName {
|
|
|
+ err = logic.DeleteExtClientAndCleanup(extclient)
|
|
|
+ if err != nil {
|
|
|
+ logger.Log(0, "failed to delete user extclient:", err.Error())
|
|
|
+ } else {
|
|
|
+ err := mq.PublishDeletedClientPeerUpdate(&extclient)
|
|
|
+ if err != nil {
|
|
|
+ logger.Log(0, "failed to publish deleted client peer update:", err.Error())
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }()
|
|
|
+
|
|
|
logic.ReturnSuccessResponse(w, r, "user account disabled")
|
|
|
}
|
|
|
|
|
@@ -925,18 +1032,16 @@ func getUsers(w http.ResponseWriter, r *http.Request) {
|
|
|
w.Header().Set("Content-Type", "application/json")
|
|
|
|
|
|
users, err := logic.GetUsers()
|
|
|
-
|
|
|
- for i := range users {
|
|
|
- users[i].NumAccessTokens, _ = (&schema.UserAccessToken{
|
|
|
- UserName: users[i].UserName,
|
|
|
- }).CountByUser(r.Context())
|
|
|
- }
|
|
|
-
|
|
|
if err != nil {
|
|
|
logger.Log(0, "failed to fetch users: ", err.Error())
|
|
|
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
|
|
|
return
|
|
|
}
|
|
|
+ for i := range users {
|
|
|
+ users[i].NumAccessTokens, _ = (&schema.UserAccessToken{
|
|
|
+ UserName: users[i].UserName,
|
|
|
+ }).CountByUser(r.Context())
|
|
|
+ }
|
|
|
|
|
|
logic.SortUsers(users[:])
|
|
|
logger.Log(2, r.Header.Get("user"), "fetched users")
|
|
@@ -1314,6 +1419,67 @@ func updateUser(w http.ResponseWriter, r *http.Request) {
|
|
|
}
|
|
|
logic.LogEvent(&e)
|
|
|
go mq.PublishPeerUpdate(false)
|
|
|
+ go func() {
|
|
|
+ // Populating all the networks the user has access to by
|
|
|
+ // being a member of groups.
|
|
|
+ userMembershipNetworkAccess := make(map[models.NetworkID]struct{})
|
|
|
+ for groupID := range user.UserGroups {
|
|
|
+ userGroup, _ := logic.GetUserGroup(groupID)
|
|
|
+ for netID := range userGroup.NetworkRoles {
|
|
|
+ userMembershipNetworkAccess[netID] = struct{}{}
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ 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 {
|
|
|
+ continue
|
|
|
+ }
|
|
|
+
|
|
|
+ var shouldDelete bool
|
|
|
+ if user.PlatformRoleID == models.SuperAdminRole || user.PlatformRoleID == models.AdminRole {
|
|
|
+ // Super-admin and Admin's access is not determined by group membership
|
|
|
+ // or network roles. Even if a user is removed from the group, they
|
|
|
+ // continue to have access to the network.
|
|
|
+ // So, no need to delete the extclient.
|
|
|
+ shouldDelete = false
|
|
|
+ } else {
|
|
|
+ _, hasAccess := user.NetworkRoles[models.NetworkID(extclient.Network)]
|
|
|
+ if hasAccess {
|
|
|
+ // The user has access to the network by themselves and not by
|
|
|
+ // virtue of being a member of the group.
|
|
|
+ // So, no need to delete the extclient.
|
|
|
+ shouldDelete = false
|
|
|
+ } else {
|
|
|
+ _, hasAccessThroughGroups := userMembershipNetworkAccess[models.NetworkID(extclient.Network)]
|
|
|
+ if !hasAccessThroughGroups {
|
|
|
+ // The user does not have access to the network by either
|
|
|
+ // being a Super-admin or Admin, by network roles or by virtue
|
|
|
+ // of being a member a group that has access to the network.
|
|
|
+ // So, delete the extclient.
|
|
|
+ shouldDelete = true
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if shouldDelete {
|
|
|
+ err = logic.DeleteExtClientAndCleanup(extclient)
|
|
|
+ if err != nil {
|
|
|
+ slog.Error("failed to delete extclient",
|
|
|
+ "id", extclient.ClientID, "owner", user.UserName, "error", err)
|
|
|
+ } else {
|
|
|
+ if err := mq.PublishDeletedClientPeerUpdate(&extclient); err != nil {
|
|
|
+ slog.Error("error setting ext peers: " + err.Error())
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }()
|
|
|
logger.Log(1, username, "was updated")
|
|
|
json.NewEncoder(w).Encode(logic.ToReturnUser(*user))
|
|
|
}
|