|
@@ -6,7 +6,9 @@ import (
|
|
"fmt"
|
|
"fmt"
|
|
"net/http"
|
|
"net/http"
|
|
"reflect"
|
|
"reflect"
|
|
|
|
+ "time"
|
|
|
|
|
|
|
|
+ "github.com/google/uuid"
|
|
"github.com/gorilla/mux"
|
|
"github.com/gorilla/mux"
|
|
"github.com/gorilla/websocket"
|
|
"github.com/gorilla/websocket"
|
|
"github.com/gravitl/netmaker/auth"
|
|
"github.com/gravitl/netmaker/auth"
|
|
@@ -14,6 +16,7 @@ import (
|
|
"github.com/gravitl/netmaker/logic"
|
|
"github.com/gravitl/netmaker/logic"
|
|
"github.com/gravitl/netmaker/models"
|
|
"github.com/gravitl/netmaker/models"
|
|
"github.com/gravitl/netmaker/mq"
|
|
"github.com/gravitl/netmaker/mq"
|
|
|
|
+ "github.com/gravitl/netmaker/schema"
|
|
"github.com/gravitl/netmaker/servercfg"
|
|
"github.com/gravitl/netmaker/servercfg"
|
|
"golang.org/x/exp/slog"
|
|
"golang.org/x/exp/slog"
|
|
)
|
|
)
|
|
@@ -34,14 +37,19 @@ func userHandlers(r *mux.Router) {
|
|
r.HandleFunc("/api/users/{username}", logic.SecurityCheck(true, checkFreeTierLimits(limitChoiceUsers, http.HandlerFunc(createUser)))).Methods(http.MethodPost)
|
|
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(true, http.HandlerFunc(deleteUser))).Methods(http.MethodDelete)
|
|
r.HandleFunc("/api/users/{username}", logic.SecurityCheck(false, logic.ContinueIfUserMatch(http.HandlerFunc(getUser)))).Methods(http.MethodGet)
|
|
r.HandleFunc("/api/users/{username}", logic.SecurityCheck(false, logic.ContinueIfUserMatch(http.HandlerFunc(getUser)))).Methods(http.MethodGet)
|
|
|
|
+ r.HandleFunc("/api/users/{username}/enable", logic.SecurityCheck(true, http.HandlerFunc(enableUserAccount))).Methods(http.MethodPost)
|
|
|
|
+ r.HandleFunc("/api/users/{username}/disable", logic.SecurityCheck(true, http.HandlerFunc(disableUserAccount))).Methods(http.MethodPost)
|
|
r.HandleFunc("/api/v1/users", logic.SecurityCheck(false, logic.ContinueIfUserMatch(http.HandlerFunc(getUserV1)))).Methods(http.MethodGet)
|
|
r.HandleFunc("/api/v1/users", logic.SecurityCheck(false, logic.ContinueIfUserMatch(http.HandlerFunc(getUserV1)))).Methods(http.MethodGet)
|
|
r.HandleFunc("/api/users", logic.SecurityCheck(true, http.HandlerFunc(getUsers))).Methods(http.MethodGet)
|
|
r.HandleFunc("/api/users", logic.SecurityCheck(true, http.HandlerFunc(getUsers))).Methods(http.MethodGet)
|
|
r.HandleFunc("/api/v1/users/roles", logic.SecurityCheck(true, http.HandlerFunc(ListRoles))).Methods(http.MethodGet)
|
|
r.HandleFunc("/api/v1/users/roles", logic.SecurityCheck(true, http.HandlerFunc(ListRoles))).Methods(http.MethodGet)
|
|
-
|
|
|
|
|
|
+ r.HandleFunc("/api/v1/users/access_token", logic.SecurityCheck(true, http.HandlerFunc(createUserAccessToken))).Methods(http.MethodPost)
|
|
|
|
+ r.HandleFunc("/api/v1/users/access_token", logic.SecurityCheck(true, http.HandlerFunc(getUserAccessTokens))).Methods(http.MethodGet)
|
|
|
|
+ r.HandleFunc("/api/v1/users/access_token", logic.SecurityCheck(true, http.HandlerFunc(deleteUserAccessTokens))).Methods(http.MethodDelete)
|
|
|
|
+ r.HandleFunc("/api/v1/users/logout", logic.SecurityCheck(false, logic.ContinueIfUserMatch(http.HandlerFunc(logout)))).Methods(http.MethodPost)
|
|
}
|
|
}
|
|
|
|
|
|
// @Summary Authenticate a user to retrieve an authorization token
|
|
// @Summary Authenticate a user to retrieve an authorization token
|
|
-// @Router /api/users/adm/authenticate [post]
|
|
|
|
|
|
+// @Router /api/v1/users/{username}/access_token [post]
|
|
// @Tags Auth
|
|
// @Tags Auth
|
|
// @Accept json
|
|
// @Accept json
|
|
// @Param body body models.UserAuthParams true "Authentication parameters"
|
|
// @Param body body models.UserAuthParams true "Authentication parameters"
|
|
@@ -49,24 +57,201 @@ func userHandlers(r *mux.Router) {
|
|
// @Failure 400 {object} models.ErrorResponse
|
|
// @Failure 400 {object} models.ErrorResponse
|
|
// @Failure 401 {object} models.ErrorResponse
|
|
// @Failure 401 {object} models.ErrorResponse
|
|
// @Failure 500 {object} models.ErrorResponse
|
|
// @Failure 500 {object} models.ErrorResponse
|
|
-func authenticateUser(response http.ResponseWriter, request *http.Request) {
|
|
|
|
|
|
+func createUserAccessToken(w http.ResponseWriter, r *http.Request) {
|
|
|
|
|
|
// Auth request consists of Mac Address and Password (from node that is authorizing
|
|
// Auth request consists of Mac Address and Password (from node that is authorizing
|
|
// in case of Master, auth is ignored and mac is set to "mastermac"
|
|
// in case of Master, auth is ignored and mac is set to "mastermac"
|
|
- var authRequest models.UserAuthParams
|
|
|
|
- var errorResponse = models.ErrorResponse{
|
|
|
|
- Code: http.StatusInternalServerError, Message: "W1R3: It's not you it's me.",
|
|
|
|
|
|
+ var req schema.UserAccessToken
|
|
|
|
+
|
|
|
|
+ err := json.NewDecoder(r.Body).Decode(&req)
|
|
|
|
+ if err != nil {
|
|
|
|
+ logger.Log(0, "error decoding request body: ",
|
|
|
|
+ err.Error())
|
|
|
|
+ logic.ReturnErrorResponse(w, r, logic.FormatError(err, logic.BadReq))
|
|
|
|
+ return
|
|
|
|
+ }
|
|
|
|
+ if req.Name == "" {
|
|
|
|
+ logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("name is required"), logic.BadReq))
|
|
|
|
+ return
|
|
|
|
+ }
|
|
|
|
+ if req.UserName == "" {
|
|
|
|
+ logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("username is required"), logic.BadReq))
|
|
|
|
+ return
|
|
|
|
+ }
|
|
|
|
+ caller, err := logic.GetUser(r.Header.Get("user"))
|
|
|
|
+ if err != nil {
|
|
|
|
+ logic.ReturnErrorResponse(w, r, logic.FormatError(err, logic.UnAuthorized))
|
|
|
|
+ return
|
|
|
|
+ }
|
|
|
|
+ user, err := logic.GetUser(req.UserName)
|
|
|
|
+ if err != nil {
|
|
|
|
+ logic.ReturnErrorResponse(w, r, logic.FormatError(err, logic.UnAuthorized))
|
|
|
|
+ return
|
|
|
|
+ }
|
|
|
|
+ if caller.UserName != user.UserName && caller.PlatformRoleID != models.SuperAdminRole {
|
|
|
|
+ if caller.PlatformRoleID == models.AdminRole {
|
|
|
|
+ if user.PlatformRoleID == models.SuperAdminRole || user.PlatformRoleID == models.AdminRole {
|
|
|
|
+ logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("not enough permissions to create token for user "+user.UserName), logic.Forbidden_Msg))
|
|
|
|
+ return
|
|
|
|
+ }
|
|
|
|
+ } else {
|
|
|
|
+ logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("not enough permissions to create token for user "+user.UserName), logic.Forbidden_Msg))
|
|
|
|
+ return
|
|
|
|
+ }
|
|
}
|
|
}
|
|
|
|
|
|
- if !servercfg.IsBasicAuthEnabled() {
|
|
|
|
|
|
+ req.ID = uuid.New().String()
|
|
|
|
+ req.CreatedBy = r.Header.Get("user")
|
|
|
|
+ req.CreatedAt = time.Now()
|
|
|
|
+ jwt, err := logic.CreateUserAccessJwtToken(user.UserName, user.PlatformRoleID, req.ExpiresAt, req.ID)
|
|
|
|
+ if jwt == "" {
|
|
|
|
+ // very unlikely that err is !nil and no jwt returned, but handle it anyways.
|
|
logic.ReturnErrorResponse(
|
|
logic.ReturnErrorResponse(
|
|
- response,
|
|
|
|
- request,
|
|
|
|
- logic.FormatError(fmt.Errorf("basic auth is disabled"), "badrequest"),
|
|
|
|
|
|
+ w,
|
|
|
|
+ r,
|
|
|
|
+ logic.FormatError(errors.New("error creating access token "+err.Error()), logic.Internal),
|
|
|
|
+ )
|
|
|
|
+ return
|
|
|
|
+ }
|
|
|
|
+ err = req.Create(r.Context())
|
|
|
|
+ if err != nil {
|
|
|
|
+ logic.ReturnErrorResponse(
|
|
|
|
+ w,
|
|
|
|
+ r,
|
|
|
|
+ logic.FormatError(errors.New("error creating access token "+err.Error()), logic.Internal),
|
|
|
|
+ )
|
|
|
|
+ return
|
|
|
|
+ }
|
|
|
|
+ logic.LogEvent(&models.Event{
|
|
|
|
+ Action: models.Create,
|
|
|
|
+ Source: models.Subject{
|
|
|
|
+ ID: caller.UserName,
|
|
|
|
+ Name: caller.UserName,
|
|
|
|
+ Type: models.UserSub,
|
|
|
|
+ },
|
|
|
|
+ TriggeredBy: caller.UserName,
|
|
|
|
+ Target: models.Subject{
|
|
|
|
+ ID: req.ID,
|
|
|
|
+ Name: req.Name,
|
|
|
|
+ Type: models.UserAccessTokenSub,
|
|
|
|
+ Info: req,
|
|
|
|
+ },
|
|
|
|
+ Origin: models.Dashboard,
|
|
|
|
+ })
|
|
|
|
+ logic.ReturnSuccessResponseWithJson(w, r, models.SuccessfulUserLoginResponse{
|
|
|
|
+ AuthToken: jwt,
|
|
|
|
+ UserName: req.UserName,
|
|
|
|
+ }, "api access token has generated for user "+req.UserName)
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+// @Summary Authenticate a user to retrieve an authorization token
|
|
|
|
+// @Router /api/v1/users/{username}/access_token [post]
|
|
|
|
+// @Tags Auth
|
|
|
|
+// @Accept json
|
|
|
|
+// @Param body body models.UserAuthParams true "Authentication parameters"
|
|
|
|
+// @Success 200 {object} models.SuccessResponse
|
|
|
|
+// @Failure 400 {object} models.ErrorResponse
|
|
|
|
+// @Failure 401 {object} models.ErrorResponse
|
|
|
|
+// @Failure 500 {object} models.ErrorResponse
|
|
|
|
+func getUserAccessTokens(w http.ResponseWriter, r *http.Request) {
|
|
|
|
+ username := r.URL.Query().Get("username")
|
|
|
|
+ if username == "" {
|
|
|
|
+ logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("username is required"), "badrequest"))
|
|
|
|
+ return
|
|
|
|
+ }
|
|
|
|
+ logic.ReturnSuccessResponseWithJson(w, r, (&schema.UserAccessToken{UserName: username}).ListByUser(r.Context()), "fetched api access tokens for user "+username)
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+// @Summary Authenticate a user to retrieve an authorization token
|
|
|
|
+// @Router /api/v1/users/{username}/access_token [post]
|
|
|
|
+// @Tags Auth
|
|
|
|
+// @Accept json
|
|
|
|
+// @Param body body models.UserAuthParams true "Authentication parameters"
|
|
|
|
+// @Success 200 {object} models.SuccessResponse
|
|
|
|
+// @Failure 400 {object} models.ErrorResponse
|
|
|
|
+// @Failure 401 {object} models.ErrorResponse
|
|
|
|
+// @Failure 500 {object} models.ErrorResponse
|
|
|
|
+func deleteUserAccessTokens(w http.ResponseWriter, r *http.Request) {
|
|
|
|
+ id := r.URL.Query().Get("id")
|
|
|
|
+ if id == "" {
|
|
|
|
+ logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("id is required"), "badrequest"))
|
|
|
|
+ return
|
|
|
|
+ }
|
|
|
|
+ a := schema.UserAccessToken{
|
|
|
|
+ ID: id,
|
|
|
|
+ }
|
|
|
|
+ err := a.Get(r.Context())
|
|
|
|
+ if err != nil {
|
|
|
|
+ logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("token does not exist"), "badrequest"))
|
|
|
|
+ return
|
|
|
|
+ }
|
|
|
|
+ caller, err := logic.GetUser(r.Header.Get("user"))
|
|
|
|
+ if err != nil {
|
|
|
|
+ logic.ReturnErrorResponse(w, r, logic.FormatError(err, "unauthorized"))
|
|
|
|
+ return
|
|
|
|
+ }
|
|
|
|
+ user, err := logic.GetUser(a.UserName)
|
|
|
|
+ if err != nil {
|
|
|
|
+ logic.ReturnErrorResponse(w, r, logic.FormatError(err, "unauthorized"))
|
|
|
|
+ return
|
|
|
|
+ }
|
|
|
|
+ if caller.UserName != user.UserName && caller.PlatformRoleID != models.SuperAdminRole {
|
|
|
|
+ if caller.PlatformRoleID == models.AdminRole {
|
|
|
|
+ if user.PlatformRoleID == models.SuperAdminRole || user.PlatformRoleID == models.AdminRole {
|
|
|
|
+ logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("not enough permissions to delete token of user "+user.UserName), logic.Forbidden_Msg))
|
|
|
|
+ return
|
|
|
|
+ }
|
|
|
|
+ } else {
|
|
|
|
+ logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("not enough permissions to delete token of user "+user.UserName), logic.Forbidden_Msg))
|
|
|
|
+ return
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ err = (&schema.UserAccessToken{ID: id}).Delete(r.Context())
|
|
|
|
+ if err != nil {
|
|
|
|
+ logic.ReturnErrorResponse(
|
|
|
|
+ w,
|
|
|
|
+ r,
|
|
|
|
+ logic.FormatError(errors.New("error deleting access token "+err.Error()), "internal"),
|
|
)
|
|
)
|
|
return
|
|
return
|
|
}
|
|
}
|
|
|
|
+ logic.LogEvent(&models.Event{
|
|
|
|
+ Action: models.Delete,
|
|
|
|
+ Source: models.Subject{
|
|
|
|
+ ID: caller.UserName,
|
|
|
|
+ Name: caller.UserName,
|
|
|
|
+ Type: models.UserSub,
|
|
|
|
+ },
|
|
|
|
+ TriggeredBy: caller.UserName,
|
|
|
|
+ Target: models.Subject{
|
|
|
|
+ ID: a.ID,
|
|
|
|
+ Name: a.Name,
|
|
|
|
+ Type: models.UserAccessTokenSub,
|
|
|
|
+ Info: a,
|
|
|
|
+ },
|
|
|
|
+ Origin: models.Dashboard,
|
|
|
|
+ })
|
|
|
|
+ logic.ReturnSuccessResponseWithJson(w, r, nil, "revoked access token")
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+// @Summary Authenticate a user to retrieve an authorization token
|
|
|
|
+// @Router /api/users/adm/authenticate [post]
|
|
|
|
+// @Tags Auth
|
|
|
|
+// @Accept json
|
|
|
|
+// @Param body body models.UserAuthParams true "Authentication parameters"
|
|
|
|
+// @Success 200 {object} models.SuccessResponse
|
|
|
|
+// @Failure 400 {object} models.ErrorResponse
|
|
|
|
+// @Failure 401 {object} models.ErrorResponse
|
|
|
|
+// @Failure 500 {object} models.ErrorResponse
|
|
|
|
+func authenticateUser(response http.ResponseWriter, request *http.Request) {
|
|
|
|
|
|
|
|
+ // Auth request consists of Mac Address and Password (from node that is authorizing
|
|
|
|
+ // in case of Master, auth is ignored and mac is set to "mastermac"
|
|
|
|
+ var authRequest models.UserAuthParams
|
|
|
|
+ var errorResponse = models.ErrorResponse{
|
|
|
|
+ Code: http.StatusInternalServerError, Message: "W1R3: It's not you it's me.",
|
|
|
|
+ }
|
|
decoder := json.NewDecoder(request.Body)
|
|
decoder := json.NewDecoder(request.Body)
|
|
decoderErr := decoder.Decode(&authRequest)
|
|
decoderErr := decoder.Decode(&authRequest)
|
|
defer request.Body.Close()
|
|
defer request.Body.Close()
|
|
@@ -76,15 +261,36 @@ func authenticateUser(response http.ResponseWriter, request *http.Request) {
|
|
logic.ReturnErrorResponse(response, request, errorResponse)
|
|
logic.ReturnErrorResponse(response, request, errorResponse)
|
|
return
|
|
return
|
|
}
|
|
}
|
|
|
|
+ user, err := logic.GetUser(authRequest.UserName)
|
|
|
|
+ if err != nil {
|
|
|
|
+ logger.Log(0, authRequest.UserName, "user validation failed: ",
|
|
|
|
+ err.Error())
|
|
|
|
+ logic.ReturnErrorResponse(response, request, logic.FormatError(err, "unauthorized"))
|
|
|
|
+ return
|
|
|
|
+ }
|
|
|
|
+ if logic.IsOauthUser(user) == nil {
|
|
|
|
+ logic.ReturnErrorResponse(response, request, logic.FormatError(errors.New("user is registered via SSO"), "badrequest"))
|
|
|
|
+ return
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if user.AccountDisabled {
|
|
|
|
+ err = errors.New("user account disabled")
|
|
|
|
+ logic.ReturnErrorResponse(response, request, logic.FormatError(err, "unauthorized"))
|
|
|
|
+ return
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if !user.IsSuperAdmin && !logic.IsBasicAuthEnabled() {
|
|
|
|
+ logic.ReturnErrorResponse(
|
|
|
|
+ response,
|
|
|
|
+ request,
|
|
|
|
+ logic.FormatError(fmt.Errorf("basic auth is disabled"), "badrequest"),
|
|
|
|
+ )
|
|
|
|
+ return
|
|
|
|
+ }
|
|
|
|
+
|
|
if val := request.Header.Get("From-Ui"); val == "true" {
|
|
if val := request.Header.Get("From-Ui"); val == "true" {
|
|
// request came from UI, if normal user block Login
|
|
// request came from UI, if normal user block Login
|
|
- user, err := logic.GetUser(authRequest.UserName)
|
|
|
|
- if err != nil {
|
|
|
|
- logger.Log(0, authRequest.UserName, "user validation failed: ",
|
|
|
|
- err.Error())
|
|
|
|
- logic.ReturnErrorResponse(response, request, logic.FormatError(err, "unauthorized"))
|
|
|
|
- return
|
|
|
|
- }
|
|
|
|
|
|
+
|
|
role, err := logic.GetRole(user.PlatformRoleID)
|
|
role, err := logic.GetRole(user.PlatformRoleID)
|
|
if err != nil {
|
|
if err != nil {
|
|
logic.ReturnErrorResponse(response, request, logic.FormatError(errors.New("access denied to dashboard"), "unauthorized"))
|
|
logic.ReturnErrorResponse(response, request, logic.FormatError(errors.New("access denied to dashboard"), "unauthorized"))
|
|
@@ -94,16 +300,40 @@ func authenticateUser(response http.ResponseWriter, request *http.Request) {
|
|
logic.ReturnErrorResponse(response, request, logic.FormatError(errors.New("access denied to dashboard"), "unauthorized"))
|
|
logic.ReturnErrorResponse(response, request, logic.FormatError(errors.New("access denied to dashboard"), "unauthorized"))
|
|
return
|
|
return
|
|
}
|
|
}
|
|
|
|
+ // log user activity
|
|
|
|
+ logic.LogEvent(&models.Event{
|
|
|
|
+ Action: models.Login,
|
|
|
|
+ Source: models.Subject{
|
|
|
|
+ ID: user.UserName,
|
|
|
|
+ Name: user.UserName,
|
|
|
|
+ Type: models.UserSub,
|
|
|
|
+ },
|
|
|
|
+ TriggeredBy: user.UserName,
|
|
|
|
+ Target: models.Subject{
|
|
|
|
+ ID: models.DashboardSub.String(),
|
|
|
|
+ Name: models.DashboardSub.String(),
|
|
|
|
+ Type: models.DashboardSub,
|
|
|
|
+ },
|
|
|
|
+ Origin: models.Dashboard,
|
|
|
|
+ })
|
|
|
|
+ } else {
|
|
|
|
+ logic.LogEvent(&models.Event{
|
|
|
|
+ Action: models.Login,
|
|
|
|
+ Source: models.Subject{
|
|
|
|
+ ID: user.UserName,
|
|
|
|
+ Name: user.UserName,
|
|
|
|
+ Type: models.UserSub,
|
|
|
|
+ },
|
|
|
|
+ TriggeredBy: user.UserName,
|
|
|
|
+ Target: models.Subject{
|
|
|
|
+ ID: models.ClientAppSub.String(),
|
|
|
|
+ Name: models.ClientAppSub.String(),
|
|
|
|
+ Type: models.ClientAppSub,
|
|
|
|
+ },
|
|
|
|
+ Origin: models.ClientApp,
|
|
|
|
+ })
|
|
}
|
|
}
|
|
- user, err := logic.GetUser(authRequest.UserName)
|
|
|
|
- if err != nil {
|
|
|
|
- logic.ReturnErrorResponse(response, request, logic.FormatError(err, "unauthorized"))
|
|
|
|
- return
|
|
|
|
- }
|
|
|
|
- if logic.IsOauthUser(user) == nil {
|
|
|
|
- logic.ReturnErrorResponse(response, request, logic.FormatError(errors.New("user is registered via SSO"), "badrequest"))
|
|
|
|
- return
|
|
|
|
- }
|
|
|
|
|
|
+
|
|
username := authRequest.UserName
|
|
username := authRequest.UserName
|
|
jwt, err := logic.VerifyAuthRequest(authRequest)
|
|
jwt, err := logic.VerifyAuthRequest(authRequest)
|
|
if err != nil {
|
|
if err != nil {
|
|
@@ -145,7 +375,7 @@ func authenticateUser(response http.ResponseWriter, request *http.Request) {
|
|
response.Write(successJSONResponse)
|
|
response.Write(successJSONResponse)
|
|
|
|
|
|
go func() {
|
|
go func() {
|
|
- if servercfg.IsPro && servercfg.GetRacAutoDisable() {
|
|
|
|
|
|
+ if servercfg.IsPro && logic.GetRacAutoDisable() {
|
|
// enable all associeated clients for the user
|
|
// enable all associeated clients for the user
|
|
clients, err := logic.GetAllExtClients()
|
|
clients, err := logic.GetAllExtClients()
|
|
if err != nil {
|
|
if err != nil {
|
|
@@ -225,6 +455,65 @@ func getUser(w http.ResponseWriter, r *http.Request) {
|
|
json.NewEncoder(w).Encode(user)
|
|
json.NewEncoder(w).Encode(user)
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+// @Summary Enable a user's account
|
|
|
|
+// @Router /api/users/{username}/enable [post]
|
|
|
|
+// @Tags Users
|
|
|
|
+// @Param username path string true "Username of the user to enable"
|
|
|
|
+// @Success 200 {object} models.SuccessResponse
|
|
|
|
+// @Failure 400 {object} models.ErrorResponse
|
|
|
|
+// @Failure 500 {object} models.ErrorResponse
|
|
|
|
+func enableUserAccount(w http.ResponseWriter, r *http.Request) {
|
|
|
|
+ username := mux.Vars(r)["username"]
|
|
|
|
+ user, err := logic.GetUser(username)
|
|
|
|
+ if err != nil {
|
|
|
|
+ logger.Log(0, "failed to fetch user: ", err.Error())
|
|
|
|
+ logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
|
|
|
|
+ return
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ user.AccountDisabled = false
|
|
|
|
+ err = logic.UpsertUser(*user)
|
|
|
|
+ if err != nil {
|
|
|
|
+ logger.Log(0, "failed to enable user account: ", err.Error())
|
|
|
|
+ logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ logic.ReturnSuccessResponse(w, r, "user account enabled")
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+// @Summary Disable a user's account
|
|
|
|
+// @Router /api/users/{username}/disable [post]
|
|
|
|
+// @Tags Users
|
|
|
|
+// @Param username path string true "Username of the user to disable"
|
|
|
|
+// @Success 200 {object} models.SuccessResponse
|
|
|
|
+// @Failure 400 {object} models.ErrorResponse
|
|
|
|
+// @Failure 500 {object} models.ErrorResponse
|
|
|
|
+func disableUserAccount(w http.ResponseWriter, r *http.Request) {
|
|
|
|
+ username := mux.Vars(r)["username"]
|
|
|
|
+ user, err := logic.GetUser(username)
|
|
|
|
+ if err != nil {
|
|
|
|
+ logger.Log(0, "failed to fetch user: ", err.Error())
|
|
|
|
+ logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
|
|
|
|
+ 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"))
|
|
|
|
+ return
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ user.AccountDisabled = true
|
|
|
|
+ err = logic.UpsertUser(*user)
|
|
|
|
+ if err != nil {
|
|
|
|
+ logger.Log(0, "failed to disable user account: ", err.Error())
|
|
|
|
+ logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ logic.ReturnSuccessResponse(w, r, "user account disabled")
|
|
|
|
+}
|
|
|
|
+
|
|
// swagger:route GET /api/v1/users user getUserV1
|
|
// swagger:route GET /api/v1/users user getUserV1
|
|
//
|
|
//
|
|
// Get an individual user with role info.
|
|
// Get an individual user with role info.
|
|
@@ -319,7 +608,7 @@ func createSuperAdmin(w http.ResponseWriter, r *http.Request) {
|
|
return
|
|
return
|
|
}
|
|
}
|
|
|
|
|
|
- if !servercfg.IsBasicAuthEnabled() {
|
|
|
|
|
|
+ if !logic.IsBasicAuthEnabled() {
|
|
logic.ReturnErrorResponse(
|
|
logic.ReturnErrorResponse(
|
|
w,
|
|
w,
|
|
r,
|
|
r,
|
|
@@ -367,7 +656,7 @@ func transferSuperAdmin(w http.ResponseWriter, r *http.Request) {
|
|
logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("only admins can be promoted to superadmin role"), "forbidden"))
|
|
logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("only admins can be promoted to superadmin role"), "forbidden"))
|
|
return
|
|
return
|
|
}
|
|
}
|
|
- if !servercfg.IsBasicAuthEnabled() {
|
|
|
|
|
|
+ if !logic.IsBasicAuthEnabled() {
|
|
logic.ReturnErrorResponse(
|
|
logic.ReturnErrorResponse(
|
|
w,
|
|
w,
|
|
r,
|
|
r,
|
|
@@ -421,6 +710,10 @@ func createUser(w http.ResponseWriter, r *http.Request) {
|
|
if !servercfg.IsPro {
|
|
if !servercfg.IsPro {
|
|
user.PlatformRoleID = models.AdminRole
|
|
user.PlatformRoleID = models.AdminRole
|
|
}
|
|
}
|
|
|
|
+ if user.UserName == logic.MasterUser {
|
|
|
|
+ logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("username not allowed"), "badrequest"))
|
|
|
|
+ return
|
|
|
|
+ }
|
|
|
|
|
|
if user.PlatformRoleID == "" {
|
|
if user.PlatformRoleID == "" {
|
|
logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("platform role is missing"), "badrequest"))
|
|
logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("platform role is missing"), "badrequest"))
|
|
@@ -458,6 +751,22 @@ func createUser(w http.ResponseWriter, r *http.Request) {
|
|
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
|
|
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
|
|
return
|
|
return
|
|
}
|
|
}
|
|
|
|
+ logic.LogEvent(&models.Event{
|
|
|
|
+ Action: models.Create,
|
|
|
|
+ Source: models.Subject{
|
|
|
|
+ ID: caller.UserName,
|
|
|
|
+ Name: caller.UserName,
|
|
|
|
+ Type: models.UserSub,
|
|
|
|
+ },
|
|
|
|
+ TriggeredBy: caller.UserName,
|
|
|
|
+ Target: models.Subject{
|
|
|
|
+ ID: user.UserName,
|
|
|
|
+ Name: user.UserName,
|
|
|
|
+ Type: models.UserSub,
|
|
|
|
+ Info: user,
|
|
|
|
+ },
|
|
|
|
+ Origin: models.Dashboard,
|
|
|
|
+ })
|
|
logic.DeleteUserInvite(user.UserName)
|
|
logic.DeleteUserInvite(user.UserName)
|
|
logic.DeletePendingUser(user.UserName)
|
|
logic.DeletePendingUser(user.UserName)
|
|
go mq.PublishPeerUpdate(false)
|
|
go mq.PublishPeerUpdate(false)
|
|
@@ -534,12 +843,12 @@ func updateUser(w http.ResponseWriter, r *http.Request) {
|
|
return
|
|
return
|
|
}
|
|
}
|
|
if caller.PlatformRoleID == models.AdminRole && user.PlatformRoleID == models.AdminRole {
|
|
if caller.PlatformRoleID == models.AdminRole && user.PlatformRoleID == models.AdminRole {
|
|
- 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"))
|
|
|
|
|
|
+ slog.Error("an admin user does not have permissions to update another admin user", "caller", caller.UserName, "attempted to update admin user", username)
|
|
|
|
+ logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("an admin user does not have permissions to update another admin user"), "forbidden"))
|
|
return
|
|
return
|
|
}
|
|
}
|
|
if caller.PlatformRoleID == models.AdminRole && userchange.PlatformRoleID == models.AdminRole {
|
|
if caller.PlatformRoleID == models.AdminRole && userchange.PlatformRoleID == models.AdminRole {
|
|
- err = errors.New("admin user cannot update role of an another user to admin")
|
|
|
|
|
|
+ err = errors.New("an admin user does not have permissions to assign the admin role to another user")
|
|
slog.Error(
|
|
slog.Error(
|
|
"failed to update user",
|
|
"failed to update user",
|
|
"caller",
|
|
"caller",
|
|
@@ -592,7 +901,30 @@ func updateUser(w http.ResponseWriter, r *http.Request) {
|
|
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "forbidden"))
|
|
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "forbidden"))
|
|
return
|
|
return
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
+ logic.AddGlobalNetRolesToAdmins(&userchange)
|
|
|
|
+ if userchange.PlatformRoleID != user.PlatformRoleID || !logic.CompareMaps(user.UserGroups, userchange.UserGroups) {
|
|
|
|
+ (&schema.UserAccessToken{UserName: user.UserName}).DeleteAllUserTokens(r.Context())
|
|
|
|
+ }
|
|
|
|
+ oldUser := *user
|
|
|
|
+ e := models.Event{
|
|
|
|
+ Action: models.Update,
|
|
|
|
+ Source: models.Subject{
|
|
|
|
+ ID: caller.UserName,
|
|
|
|
+ Name: caller.UserName,
|
|
|
|
+ Type: models.UserSub,
|
|
|
|
+ },
|
|
|
|
+ TriggeredBy: caller.UserName,
|
|
|
|
+ Target: models.Subject{
|
|
|
|
+ ID: user.UserName,
|
|
|
|
+ Name: user.UserName,
|
|
|
|
+ Type: models.UserSub,
|
|
|
|
+ },
|
|
|
|
+ Diff: models.Diff{
|
|
|
|
+ Old: oldUser,
|
|
|
|
+ New: userchange,
|
|
|
|
+ },
|
|
|
|
+ Origin: models.Dashboard,
|
|
|
|
+ }
|
|
user, err = logic.UpdateUser(&userchange, user)
|
|
user, err = logic.UpdateUser(&userchange, user)
|
|
if err != nil {
|
|
if err != nil {
|
|
logger.Log(0, username,
|
|
logger.Log(0, username,
|
|
@@ -600,6 +932,7 @@ func updateUser(w http.ResponseWriter, r *http.Request) {
|
|
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
|
|
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
|
|
return
|
|
return
|
|
}
|
|
}
|
|
|
|
+ logic.LogEvent(&e)
|
|
go mq.PublishPeerUpdate(false)
|
|
go mq.PublishPeerUpdate(false)
|
|
logger.Log(1, username, "was updated")
|
|
logger.Log(1, username, "was updated")
|
|
json.NewEncoder(w).Encode(logic.ToReturnUser(*user))
|
|
json.NewEncoder(w).Encode(logic.ToReturnUser(*user))
|
|
@@ -671,18 +1004,28 @@ func deleteUser(w http.ResponseWriter, r *http.Request) {
|
|
return
|
|
return
|
|
}
|
|
}
|
|
}
|
|
}
|
|
- success, err := logic.DeleteUser(username)
|
|
|
|
|
|
+ err = logic.DeleteUser(username)
|
|
if err != nil {
|
|
if err != nil {
|
|
logger.Log(0, username,
|
|
logger.Log(0, username,
|
|
"failed to delete user: ", err.Error())
|
|
"failed to delete user: ", err.Error())
|
|
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
|
|
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
|
|
return
|
|
return
|
|
- } else if !success {
|
|
|
|
- err := errors.New("delete unsuccessful")
|
|
|
|
- logger.Log(0, username, err.Error())
|
|
|
|
- logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
|
|
|
|
- return
|
|
|
|
}
|
|
}
|
|
|
|
+ logic.LogEvent(&models.Event{
|
|
|
|
+ Action: models.Delete,
|
|
|
|
+ Source: models.Subject{
|
|
|
|
+ ID: caller.UserName,
|
|
|
|
+ Name: caller.UserName,
|
|
|
|
+ Type: models.UserSub,
|
|
|
|
+ },
|
|
|
|
+ TriggeredBy: caller.UserName,
|
|
|
|
+ Target: models.Subject{
|
|
|
|
+ ID: user.UserName,
|
|
|
|
+ Name: user.UserName,
|
|
|
|
+ Type: models.UserSub,
|
|
|
|
+ },
|
|
|
|
+ Origin: models.Dashboard,
|
|
|
|
+ })
|
|
// check and delete extclient with this ownerID
|
|
// check and delete extclient with this ownerID
|
|
go func() {
|
|
go func() {
|
|
extclients, err := logic.GetAllExtClients()
|
|
extclients, err := logic.GetAllExtClients()
|
|
@@ -748,3 +1091,50 @@ func listRoles(w http.ResponseWriter, r *http.Request) {
|
|
|
|
|
|
logic.ReturnSuccessResponseWithJson(w, r, roles, "successfully fetched user roles permission templates")
|
|
logic.ReturnSuccessResponseWithJson(w, r, roles, "successfully fetched user roles permission templates")
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+// swagger:route POST /api/v1/user/logout user logout
|
|
|
|
+//
|
|
|
|
+// LogOut user.
|
|
|
|
+//
|
|
|
|
+// Schemes: https
|
|
|
|
+//
|
|
|
|
+// Security:
|
|
|
|
+// oauth
|
|
|
|
+//
|
|
|
|
+// Responses:
|
|
|
|
+// 200: userBodyResponse
|
|
|
|
+func logout(w http.ResponseWriter, r *http.Request) {
|
|
|
|
+ // set header.
|
|
|
|
+ w.Header().Set("Content-Type", "application/json")
|
|
|
|
+ userName := r.URL.Query().Get("username")
|
|
|
|
+ user, err := logic.GetUser(userName)
|
|
|
|
+ if err != nil {
|
|
|
|
+ logic.ReturnErrorResponse(w, r, logic.FormatError(err, logic.BadReq))
|
|
|
|
+ return
|
|
|
|
+ }
|
|
|
|
+ var target models.SubjectType
|
|
|
|
+ if val := r.Header.Get("From-Ui"); val == "true" {
|
|
|
|
+ target = models.DashboardSub
|
|
|
|
+ } else {
|
|
|
|
+ target = models.ClientAppSub
|
|
|
|
+ }
|
|
|
|
+ if target != "" {
|
|
|
|
+ logic.LogEvent(&models.Event{
|
|
|
|
+ Action: models.LogOut,
|
|
|
|
+ Source: models.Subject{
|
|
|
|
+ ID: user.UserName,
|
|
|
|
+ Name: user.UserName,
|
|
|
|
+ Type: models.UserSub,
|
|
|
|
+ },
|
|
|
|
+ TriggeredBy: user.UserName,
|
|
|
|
+ Target: models.Subject{
|
|
|
|
+ ID: target.String(),
|
|
|
|
+ Name: target.String(),
|
|
|
|
+ Type: target,
|
|
|
|
+ },
|
|
|
|
+ Origin: models.Origin(target),
|
|
|
|
+ })
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ logic.ReturnSuccessResponse(w, r, "user logged out")
|
|
|
|
+}
|