mirror of
https://github.com/gravitl/netmaker.git
synced 2026-04-23 00:17:10 +08:00
9b4f9a4a50
* NM-240: update api swagger docs * NM-240: remove api doc for debug api * NM-240: generate swagger * NM-240: remove deprecated and unused apis, update jit models * NM-240: deprecate ununsed apis from api docs
631 lines
19 KiB
Go
631 lines
19 KiB
Go
package controllers
|
|
|
|
import (
|
|
"encoding/json"
|
|
"errors"
|
|
"net/http"
|
|
"strconv"
|
|
"time"
|
|
|
|
"github.com/gorilla/mux"
|
|
"github.com/gravitl/netmaker/db"
|
|
"github.com/gravitl/netmaker/logger"
|
|
"github.com/gravitl/netmaker/logic"
|
|
"github.com/gravitl/netmaker/models"
|
|
"github.com/gravitl/netmaker/pro/email"
|
|
proLogic "github.com/gravitl/netmaker/pro/logic"
|
|
"github.com/gravitl/netmaker/schema"
|
|
"golang.org/x/exp/slog"
|
|
)
|
|
|
|
func JITHandlers(r *mux.Router) {
|
|
r.HandleFunc("/api/v1/jit", logic.SecurityCheck(true,
|
|
http.HandlerFunc(handleJIT))).Methods(http.MethodPost, http.MethodGet)
|
|
|
|
r.HandleFunc("/api/v1/jit", logic.SecurityCheck(true,
|
|
http.HandlerFunc(deleteJITGrant))).Methods(http.MethodDelete)
|
|
|
|
r.HandleFunc("/api/v1/jit_user/networks", logic.SecurityCheck(false,
|
|
http.HandlerFunc(getUserJITNetworks))).Methods(http.MethodGet)
|
|
|
|
r.HandleFunc("/api/v1/jit_user/request", logic.SecurityCheck(false,
|
|
http.HandlerFunc(requestJITAccess))).Methods(http.MethodPost)
|
|
}
|
|
|
|
|
|
// @Summary List JIT requests for a network
|
|
// @Router /api/v1/jit [get]
|
|
// @Tags JIT
|
|
// @Security oauth
|
|
// @Produce json
|
|
// @Param network query string true "Network ID"
|
|
// @Param status query string false "Filter by status (pending, approved, denied, expired)"
|
|
// @Param page query int false "Page number"
|
|
// @Param per_page query int false "Items per page"
|
|
// @Success 200 {array} schema.JITRequest
|
|
// @Failure 400 {object} models.ErrorResponse
|
|
// @Failure 500 {object} models.ErrorResponse
|
|
//
|
|
// @Summary Handle JIT operations (enable, disable, approve, deny)
|
|
// @Router /api/v1/jit [post]
|
|
// @Tags JIT
|
|
// @Security oauth
|
|
// @Accept json
|
|
// @Produce json
|
|
// @Param network query string true "Network ID"
|
|
// @Param body body models.JITOperationRequest true "JIT operation request"
|
|
// @Success 200 {object} models.SuccessResponse
|
|
// @Failure 400 {object} models.ErrorResponse
|
|
// @Failure 500 {object} models.ErrorResponse
|
|
func handleJIT(w http.ResponseWriter, r *http.Request) {
|
|
// Check if JIT feature is enabled
|
|
featureFlags := logic.GetFeatureFlags()
|
|
if !featureFlags.EnableJIT {
|
|
logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("JIT feature is not enabled"), "forbidden"))
|
|
return
|
|
}
|
|
|
|
networkID := r.URL.Query().Get("network")
|
|
if networkID == "" {
|
|
logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("network is required"), "badrequest"))
|
|
return
|
|
}
|
|
|
|
username := r.Header.Get("user")
|
|
if username == "" {
|
|
logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("user not found in request"), "unauthorized"))
|
|
return
|
|
}
|
|
|
|
user, err := logic.GetUser(username)
|
|
if err != nil {
|
|
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "unauthorized"))
|
|
return
|
|
}
|
|
|
|
switch r.Method {
|
|
case http.MethodGet:
|
|
handleJITGet(w, r, networkID, user)
|
|
case http.MethodPost:
|
|
handleJITPost(w, r, networkID, user)
|
|
default:
|
|
logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("method not allowed"), "badrequest"))
|
|
}
|
|
}
|
|
|
|
// handleJITGet - handles GET requests for JIT status/requests
|
|
func handleJITGet(w http.ResponseWriter, r *http.Request, networkID string, user *models.User) {
|
|
statusFilter := r.URL.Query().Get("status") // "pending", "approved", "denied", "expired", or empty for all
|
|
|
|
// Parse pagination parameters (default to 0, db.SetPagination will apply defaults)
|
|
page, _ := strconv.Atoi(r.URL.Query().Get("page"))
|
|
pageSize, _ := strconv.Atoi(r.URL.Query().Get("per_page"))
|
|
|
|
// Apply defaults if not provided (matching db.SetPagination logic)
|
|
if page < 1 {
|
|
page = 1
|
|
}
|
|
if pageSize < 1 || pageSize > 100 {
|
|
pageSize = 10
|
|
}
|
|
|
|
ctx := db.WithContext(r.Context())
|
|
requests, total, err := proLogic.GetNetworkJITRequestsPaginated(ctx, networkID, statusFilter, page, pageSize)
|
|
if err != nil {
|
|
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
|
|
return
|
|
}
|
|
|
|
// Calculate pagination metadata
|
|
totalPages := (int(total) + pageSize - 1) / pageSize
|
|
if totalPages == 0 {
|
|
totalPages = 1
|
|
}
|
|
|
|
response := map[string]interface{}{
|
|
"data": requests,
|
|
"page": page,
|
|
"per_page": pageSize,
|
|
"total": total,
|
|
"total_pages": totalPages,
|
|
}
|
|
|
|
logic.ReturnSuccessResponseWithJson(w, r, response, "fetched JIT requests")
|
|
}
|
|
|
|
// handleJITPost - handles POST requests for JIT operations
|
|
func handleJITPost(w http.ResponseWriter, r *http.Request, networkID string, user *models.User) {
|
|
var req models.JITOperationRequest
|
|
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, "badrequest"))
|
|
return
|
|
}
|
|
|
|
switch req.Action {
|
|
case "enable":
|
|
handleEnableJIT(w, r, networkID, user)
|
|
case "disable":
|
|
handleDisableJIT(w, r, networkID, user)
|
|
case "approve":
|
|
handleApproveRequest(w, r, networkID, user, req.RequestID, req.ExpiresAt)
|
|
case "deny":
|
|
handleDenyRequest(w, r, networkID, user, req.RequestID)
|
|
default:
|
|
logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("invalid action"), "badrequest"))
|
|
}
|
|
}
|
|
|
|
// handleEnableJIT - enables JIT on a network
|
|
func handleEnableJIT(w http.ResponseWriter, r *http.Request, networkID string, user *models.User) {
|
|
// Check if user is admin
|
|
if !isNetworkAdmin(user, networkID) {
|
|
logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("only network admins can enable JIT"), "forbidden"))
|
|
return
|
|
}
|
|
|
|
if err := proLogic.EnableJITOnNetwork(networkID); err != nil {
|
|
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
|
|
return
|
|
}
|
|
|
|
logic.LogEvent(&models.Event{
|
|
Action: models.Update,
|
|
Source: models.Subject{
|
|
ID: user.UserName,
|
|
Name: user.UserName,
|
|
Type: models.UserSub,
|
|
},
|
|
TriggeredBy: user.UserName,
|
|
Target: models.Subject{
|
|
ID: networkID,
|
|
Name: networkID,
|
|
Type: models.NetworkSub,
|
|
},
|
|
NetworkID: models.NetworkID(networkID),
|
|
Origin: models.Dashboard,
|
|
})
|
|
|
|
logic.ReturnSuccessResponse(w, r, "JIT enabled on network")
|
|
}
|
|
|
|
// handleDisableJIT - disables JIT on a network
|
|
func handleDisableJIT(w http.ResponseWriter, r *http.Request, networkID string, user *models.User) {
|
|
// Check if user is admin
|
|
if !isNetworkAdmin(user, networkID) {
|
|
logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("only network admins can disable JIT"), "forbidden"))
|
|
return
|
|
}
|
|
|
|
if err := proLogic.DisableJITOnNetwork(networkID); err != nil {
|
|
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
|
|
return
|
|
}
|
|
|
|
logic.LogEvent(&models.Event{
|
|
Action: models.Update,
|
|
Source: models.Subject{
|
|
ID: user.UserName,
|
|
Name: user.UserName,
|
|
Type: models.UserSub,
|
|
},
|
|
TriggeredBy: user.UserName,
|
|
Target: models.Subject{
|
|
ID: networkID,
|
|
Name: networkID,
|
|
Type: models.NetworkSub,
|
|
},
|
|
NetworkID: models.NetworkID(networkID),
|
|
Origin: models.Dashboard,
|
|
})
|
|
|
|
logic.ReturnSuccessResponse(w, r, "JIT disabled on network")
|
|
}
|
|
|
|
// handleApproveRequest - approves a JIT request
|
|
func handleApproveRequest(w http.ResponseWriter, r *http.Request, networkID string, user *models.User, requestID string, expiresAtEpoch int64) {
|
|
// Check if user is admin
|
|
if !isNetworkAdmin(user, networkID) {
|
|
logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("only network admins can approve requests"), "forbidden"))
|
|
return
|
|
}
|
|
|
|
if requestID == "" {
|
|
logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("request_id is required"), "badrequest"))
|
|
return
|
|
}
|
|
|
|
if expiresAtEpoch <= 0 {
|
|
logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("expires_at is required and must be a valid Unix epoch timestamp"), "badrequest"))
|
|
return
|
|
}
|
|
|
|
// Convert epoch to time.Time
|
|
expiresAt := time.Unix(expiresAtEpoch, 0).UTC()
|
|
now := time.Now().UTC()
|
|
|
|
// Validate that expires_at is in the future
|
|
if expiresAt.Before(now) || expiresAt.Equal(now) {
|
|
logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("expires_at must be in the future"), "badrequest"))
|
|
return
|
|
}
|
|
|
|
grant, req, err := proLogic.ApproveJITRequest(requestID, expiresAt, user.UserName)
|
|
if err != nil {
|
|
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
|
|
return
|
|
}
|
|
// Send approval email to user
|
|
go func() {
|
|
network, _ := logic.GetNetwork(networkID)
|
|
if err := email.SendJITApprovalEmail(grant, req, network); err != nil {
|
|
slog.Error("failed to send approval notification", "error", err)
|
|
}
|
|
}()
|
|
logic.LogEvent(&models.Event{
|
|
Action: models.Update,
|
|
Source: models.Subject{
|
|
ID: user.UserName,
|
|
Name: user.UserName,
|
|
Type: models.UserSub,
|
|
},
|
|
TriggeredBy: user.UserName,
|
|
Target: models.Subject{
|
|
ID: requestID,
|
|
Name: networkID,
|
|
Type: models.NetworkSub,
|
|
},
|
|
NetworkID: models.NetworkID(networkID),
|
|
Origin: models.Dashboard,
|
|
})
|
|
|
|
logic.ReturnSuccessResponseWithJson(w, r, grant, "JIT request approved")
|
|
}
|
|
|
|
// handleDenyRequest - denies a JIT request
|
|
func handleDenyRequest(w http.ResponseWriter, r *http.Request, networkID string, user *models.User, requestID string) {
|
|
// Check if user is admin
|
|
if !isNetworkAdmin(user, networkID) {
|
|
logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("only network admins can deny requests"), "forbidden"))
|
|
return
|
|
}
|
|
|
|
if requestID == "" {
|
|
logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("request_id is required"), "badrequest"))
|
|
return
|
|
}
|
|
|
|
if err := proLogic.DenyJITRequest(requestID, user.UserName); err != nil {
|
|
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
|
|
return
|
|
}
|
|
|
|
logic.LogEvent(&models.Event{
|
|
Action: models.Update,
|
|
Source: models.Subject{
|
|
ID: user.UserName,
|
|
Name: user.UserName,
|
|
Type: models.UserSub,
|
|
},
|
|
TriggeredBy: user.UserName,
|
|
Target: models.Subject{
|
|
ID: requestID,
|
|
Name: networkID,
|
|
Type: models.NetworkSub,
|
|
},
|
|
NetworkID: models.NetworkID(networkID),
|
|
Origin: models.Dashboard,
|
|
})
|
|
|
|
logic.ReturnSuccessResponse(w, r, "JIT request denied")
|
|
}
|
|
|
|
// @Summary Delete/revoke a JIT grant
|
|
// @Router /api/v1/jit [delete]
|
|
// @Tags JIT
|
|
// @Security oauth
|
|
// @Produce json
|
|
// @Param network query string true "Network ID"
|
|
// @Param grant_id query string true "Grant ID to revoke"
|
|
// @Success 200 {object} models.SuccessResponse
|
|
// @Failure 400 {object} models.ErrorResponse
|
|
// @Failure 500 {object} models.ErrorResponse
|
|
func deleteJITGrant(w http.ResponseWriter, r *http.Request) {
|
|
// Check if JIT feature is enabled
|
|
featureFlags := logic.GetFeatureFlags()
|
|
if !featureFlags.EnableJIT {
|
|
logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("JIT feature is not enabled"), "forbidden"))
|
|
return
|
|
}
|
|
|
|
networkID := r.URL.Query().Get("network")
|
|
grantID := r.URL.Query().Get("grant_id")
|
|
|
|
if networkID == "" || grantID == "" {
|
|
logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("network and grant_id are required"), "badrequest"))
|
|
return
|
|
}
|
|
|
|
username := r.Header.Get("user")
|
|
if username == "" {
|
|
logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("user not found in request"), "unauthorized"))
|
|
return
|
|
}
|
|
|
|
user, err := logic.GetUser(username)
|
|
if err != nil {
|
|
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "unauthorized"))
|
|
return
|
|
}
|
|
|
|
// Check if user is admin
|
|
if !isNetworkAdmin(user, networkID) {
|
|
logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("only network admins can revoke grants"), "forbidden"))
|
|
return
|
|
}
|
|
|
|
ctx := db.WithContext(r.Context())
|
|
grant := schema.JITGrant{ID: grantID}
|
|
if err := grant.Get(ctx); err != nil {
|
|
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
|
|
return
|
|
}
|
|
|
|
if grant.NetworkID != networkID {
|
|
logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("grant does not belong to this network"), "badrequest"))
|
|
return
|
|
}
|
|
|
|
// Delete all grants for this user on this network (in case there are multiple)
|
|
if err := proLogic.DeactivateUserGrantsOnNetwork(networkID, grant.UserID); err != nil {
|
|
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
|
|
return
|
|
}
|
|
|
|
// Update associated request status to "expired" for all approved requests from this user
|
|
request := schema.JITRequest{
|
|
NetworkID: networkID,
|
|
UserID: grant.UserID,
|
|
}
|
|
allRequests, err := request.ListByNetwork(ctx)
|
|
var revokedRequest *schema.JITRequest
|
|
if err == nil {
|
|
for _, req := range allRequests {
|
|
if req.UserID == grant.UserID && req.Status == "approved" {
|
|
req.Status = "expired"
|
|
req.RevokedAt = time.Now().UTC()
|
|
if err := req.Update(ctx); err != nil {
|
|
logger.Log(0, "failed to update request status when revoking grant:", err.Error())
|
|
// Don't fail the operation, just log
|
|
} else {
|
|
// Use the first approved request for email notification
|
|
if revokedRequest == nil {
|
|
revokedRequest = &req
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Send email notification to user
|
|
if revokedRequest != nil {
|
|
network, err := logic.GetNetwork(networkID)
|
|
if err == nil {
|
|
if err := email.SendJITExpirationEmail(&grant, revokedRequest, network, true); err != nil {
|
|
slog.Warn("failed to send revocation email", "grant_id", grantID, "user", revokedRequest.UserName, "error", err)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Disconnect user's ext clients from the network
|
|
if err := proLogic.DisconnectUserExtClientsFromNetwork(networkID, grant.UserID); err != nil {
|
|
logger.Log(0, "failed to disconnect ext clients when revoking grant:", err.Error())
|
|
}
|
|
|
|
logic.LogEvent(&models.Event{
|
|
Action: models.Delete,
|
|
Source: models.Subject{
|
|
ID: user.UserName,
|
|
Name: user.UserName,
|
|
Type: models.UserSub,
|
|
},
|
|
TriggeredBy: user.UserName,
|
|
Target: models.Subject{
|
|
ID: grantID,
|
|
Name: networkID,
|
|
Type: models.NetworkSub,
|
|
},
|
|
NetworkID: models.NetworkID(networkID),
|
|
Origin: models.Dashboard,
|
|
})
|
|
|
|
logic.ReturnSuccessResponse(w, r, "JIT grant revoked")
|
|
}
|
|
|
|
// isNetworkAdmin - checks if user is a network admin
|
|
func isNetworkAdmin(user *models.User, networkID string) bool {
|
|
networkIDModel := models.NetworkID(networkID)
|
|
allNetworksID := models.AllNetworks
|
|
|
|
// Check platform role
|
|
if user.PlatformRoleID == models.SuperAdminRole || user.PlatformRoleID == models.AdminRole {
|
|
return true
|
|
}
|
|
|
|
// Check network-specific roles
|
|
if roles, ok := user.NetworkRoles[networkIDModel]; ok {
|
|
for roleID := range roles {
|
|
if roleID == models.NetworkAdmin {
|
|
return true
|
|
}
|
|
}
|
|
}
|
|
|
|
// Check all-networks role
|
|
if roles, ok := user.NetworkRoles[allNetworksID]; ok {
|
|
for roleID := range roles {
|
|
if roleID == models.NetworkAdmin {
|
|
return true
|
|
}
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
// @Summary Get user JIT networks status
|
|
// @Router /api/v1/jit_user/networks [get]
|
|
// @Tags JIT
|
|
// @Security oauth
|
|
// @Produce json
|
|
// @Success 200 {array} models.UserJITNetworkStatus
|
|
// @Failure 400 {object} models.ErrorResponse
|
|
// @Failure 500 {object} models.ErrorResponse
|
|
func getUserJITNetworks(w http.ResponseWriter, r *http.Request) {
|
|
// Check if JIT feature is enabled
|
|
featureFlags := logic.GetFeatureFlags()
|
|
if !featureFlags.EnableJIT {
|
|
logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("JIT feature is not enabled"), "forbidden"))
|
|
return
|
|
}
|
|
|
|
username := r.Header.Get("user")
|
|
if username == "" {
|
|
logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("user not found in request"), "unauthorized"))
|
|
return
|
|
}
|
|
|
|
user, err := logic.GetUser(username)
|
|
if err != nil {
|
|
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "unauthorized"))
|
|
return
|
|
}
|
|
|
|
// Get all networks user has access to
|
|
allNetworks, err := logic.GetNetworks()
|
|
if err != nil {
|
|
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
|
|
return
|
|
}
|
|
|
|
// Filter networks by user role
|
|
userNetworks := logic.FilterNetworksByRole(allNetworks, *user)
|
|
|
|
// Build response with JIT status for each network
|
|
networksWithJITStatus, err := proLogic.GetUserJITNetworksStatus(userNetworks, user.UserName)
|
|
if err != nil {
|
|
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
|
|
return
|
|
}
|
|
|
|
logic.ReturnSuccessResponseWithJson(w, r, networksWithJITStatus, "fetched user JIT network status")
|
|
}
|
|
|
|
// @Summary Request JIT access to a network
|
|
// @Router /api/v1/jit_user/request [post]
|
|
// @Tags JIT
|
|
// @Security oauth
|
|
// @Accept json
|
|
// @Produce json
|
|
// @Param network query string true "Network ID"
|
|
// @Param body body models.JITAccessRequest true "JIT access request"
|
|
// @Success 200 {object} schema.JITRequest
|
|
// @Failure 400 {object} models.ErrorResponse
|
|
// @Failure 500 {object} models.ErrorResponse
|
|
func requestJITAccess(w http.ResponseWriter, r *http.Request) {
|
|
// Check if JIT feature is enabled
|
|
featureFlags := logic.GetFeatureFlags()
|
|
if !featureFlags.EnableJIT {
|
|
logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("JIT feature is not enabled"), "forbidden"))
|
|
return
|
|
}
|
|
|
|
username := r.Header.Get("user")
|
|
if username == "" {
|
|
logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("user not found in request"), "unauthorized"))
|
|
return
|
|
}
|
|
network := r.URL.Query().Get("network")
|
|
|
|
user, err := logic.GetUser(username)
|
|
if err != nil {
|
|
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "unauthorized"))
|
|
return
|
|
}
|
|
|
|
var req models.JITAccessRequest
|
|
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, "badrequest"))
|
|
return
|
|
}
|
|
req.NetworkID = network
|
|
// Validate required fields
|
|
if req.NetworkID == "" {
|
|
logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("network_id is required"), "badrequest"))
|
|
return
|
|
}
|
|
|
|
if req.Reason == "" {
|
|
logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("reason is required"), "badrequest"))
|
|
return
|
|
}
|
|
// Check if user has access to the network by role
|
|
allNetworks, err := logic.GetNetworks()
|
|
if err != nil {
|
|
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "internal"))
|
|
return
|
|
}
|
|
|
|
// Filter networks by user role
|
|
userNetworks := logic.FilterNetworksByRole(allNetworks, *user)
|
|
hasAccess := false
|
|
for _, network := range userNetworks {
|
|
if network.NetID == req.NetworkID {
|
|
hasAccess = true
|
|
break
|
|
}
|
|
}
|
|
|
|
if !hasAccess {
|
|
logic.ReturnErrorResponse(w, r, logic.FormatError(errors.New("user does not have access to this network"), "forbidden"))
|
|
return
|
|
}
|
|
|
|
// Create the JIT request
|
|
request, err := proLogic.CreateJITRequest(req.NetworkID, user.UserName, req.Reason)
|
|
if err != nil {
|
|
logic.ReturnErrorResponse(w, r, logic.FormatError(err, "badrequest"))
|
|
return
|
|
}
|
|
|
|
// Send email notifications to network admins
|
|
go func() {
|
|
network, _ := logic.GetNetwork(req.NetworkID)
|
|
if err := email.SendJITRequestEmails(request, network); err != nil {
|
|
slog.Error("failed to send JIT request notifications", "error", err)
|
|
}
|
|
}()
|
|
|
|
logic.LogEvent(&models.Event{
|
|
Action: models.Create,
|
|
Source: models.Subject{
|
|
ID: user.UserName,
|
|
Name: user.UserName,
|
|
Type: models.UserSub,
|
|
},
|
|
TriggeredBy: user.UserName,
|
|
Target: models.Subject{
|
|
ID: request.ID,
|
|
Name: req.NetworkID,
|
|
Type: models.NetworkSub,
|
|
},
|
|
NetworkID: models.NetworkID(req.NetworkID),
|
|
Origin: models.ClientApp,
|
|
})
|
|
|
|
logic.ReturnSuccessResponseWithJson(w, r, request, "JIT access request created")
|
|
}
|