/
t3
/
gateway-server
Обзор
Документация
Войти
/
t3
/
gateway-server
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
master
internal/api/iam_proxy_handler.go
223 строки
7 KB
Ivan
end-to-end logging
25 июл 2026, 00:10
25 июл 2026, 00:10
663e207
Код
Авторство
О чём код?
package api import ( "io" "log/slog" "net/http" "net/url" "gitverse.ru/t3/gateway-server/internal/auth" "gitverse.ru/t3/gateway-server/internal/client" ) // IAMProxyHandler proxies admin API calls to IAM Server behind JWTAuthMiddleware. // Each request is first checked against IAM via AuthCheck, then forwarded with // the X-IAM-Service-Token header. type IAMProxyHandler struct { iamClient *client.IAMClient logger *slog.Logger } func NewIAMProxyHandler(iamClient *client.IAMClient, logger *slog.Logger) *IAMProxyHandler { return &IAMProxyHandler{iamClient: iamClient, logger: logger} } // proxy performs AuthCheck and, if allowed, reverse-proxies the request to IAM. // The action is the IAM action for policy evaluation (e.g. "iam:user.read"). func (h *IAMProxyHandler) proxy(w http.ResponseWriter, r *http.Request, action string) { claims := auth.ClaimsFromContext(r.Context()) if claims == nil { writeError(w, http.StatusUnauthorized, "unauthorized", "no session") return } check, err := h.iamClient.AuthCheck(r.Context(), claims.AccessKey, claims.AccessSecret, action, "*") if err != nil || !check.Allowed { writeError(w, http.StatusForbidden, "forbidden", "no permission for "+action) return } // Build the target URL. target, err := url.Parse(h.iamClient.AdminBaseURL() + r.URL.Path) if err != nil { h.logger.Error("iam proxy: bad target url", "error", err) writeError(w, http.StatusInternalServerError, "internal_error", "bad upstream url") return } target.RawQuery = r.URL.RawQuery // Create outgoing request. proxyReq, err := http.NewRequestWithContext(r.Context(), r.Method, target.String(), r.Body) if err != nil { h.logger.Error("iam proxy: cannot create request", "error", err) writeError(w, http.StatusInternalServerError, "internal_error", "failed to proxy") return } proxyReq.Header.Set("Content-Type", "application/json") proxyReq.Header.Set("X-IAM-Service-Token", h.iamClient.ServiceToken()) // Forward X-Request-ID for end-to-end tracing (from context, set by RequestIDMiddleware). if rid := RequestIDFromContext(r.Context()); rid != "" { proxyReq.Header.Set("X-Request-ID", rid) } // Forward If-Match for optimistic locking. if ifMatch := r.Header.Get("If-Match"); ifMatch != "" { proxyReq.Header.Set("If-Match", ifMatch) } resp, err := h.iamClient.AdminHTTPClient().Do(proxyReq) if err != nil { h.logger.Error("iam proxy: upstream error", "error", err, "path", r.URL.Path) writeError(w, http.StatusBadGateway, "bad_gateway", "IAM service error") return } defer resp.Body.Close() // Copy response headers that the client needs. if ct := resp.Header.Get("Content-Type"); ct != "" { w.Header().Set("Content-Type", ct) } if etag := resp.Header.Get("ETag"); etag != "" { w.Header().Set("ETag", etag) } if xrv := resp.Header.Get("X-Resource-Version"); xrv != "" { w.Header().Set("X-Resource-Version", xrv) } if rid := resp.Header.Get("X-Request-ID"); rid != "" { w.Header().Set("X-Request-ID", rid) } w.WriteHeader(resp.StatusCode) io.Copy(w, resp.Body) } // --- Users --- func (h *IAMProxyHandler) ListUsers(w http.ResponseWriter, r *http.Request) { h.proxy(w, r, "iam:user.read") } func (h *IAMProxyHandler) CreateUser(w http.ResponseWriter, r *http.Request) { h.proxy(w, r, "iam:user.create") } func (h *IAMProxyHandler) InspectUser(w http.ResponseWriter, r *http.Request) { h.proxy(w, r, "iam:user.read") } func (h *IAMProxyHandler) UpdateUser(w http.ResponseWriter, r *http.Request) { h.proxy(w, r, "iam:user.update") } func (h *IAMProxyHandler) DeleteUser(w http.ResponseWriter, r *http.Request) { h.proxy(w, r, "iam:user.delete") } // --- Access Keys --- func (h *IAMProxyHandler) CreateAccessKey(w http.ResponseWriter, r *http.Request) { h.proxy(w, r, "iam:access_key.manage") } func (h *IAMProxyHandler) RotateAccessKey(w http.ResponseWriter, r *http.Request) { h.proxy(w, r, "iam:access_key.manage") } func (h *IAMProxyHandler) ListAccessKeys(w http.ResponseWriter, r *http.Request) { h.proxy(w, r, "iam:user.read") } func (h *IAMProxyHandler) UpdateAccessKey(w http.ResponseWriter, r *http.Request) { h.proxy(w, r, "iam:access_key.manage") } func (h *IAMProxyHandler) DeleteAccessKey(w http.ResponseWriter, r *http.Request) { h.proxy(w, r, "iam:access_key.manage") } // --- User Roles --- func (h *IAMProxyHandler) GetUserRoles(w http.ResponseWriter, r *http.Request) { h.proxy(w, r, "iam:user.read") } func (h *IAMProxyHandler) SetUserRoles(w http.ResponseWriter, r *http.Request) { h.proxy(w, r, "iam:user.update") } func (h *IAMProxyHandler) AddUserRoles(w http.ResponseWriter, r *http.Request) { h.proxy(w, r, "iam:user.update") } func (h *IAMProxyHandler) RemoveUserRoles(w http.ResponseWriter, r *http.Request) { h.proxy(w, r, "iam:user.update") } // --- User Permissions --- func (h *IAMProxyHandler) GetUserPermissions(w http.ResponseWriter, r *http.Request) { h.proxy(w, r, "iam:user.read") } func (h *IAMProxyHandler) SetUserPermissions(w http.ResponseWriter, r *http.Request) { h.proxy(w, r, "iam:user.update") } func (h *IAMProxyHandler) AddUserPermissions(w http.ResponseWriter, r *http.Request) { h.proxy(w, r, "iam:user.update") } func (h *IAMProxyHandler) RemoveUserPermissions(w http.ResponseWriter, r *http.Request) { h.proxy(w, r, "iam:user.update") } // --- Roles --- func (h *IAMProxyHandler) ListRoles(w http.ResponseWriter, r *http.Request) { h.proxy(w, r, "iam:role.read") } func (h *IAMProxyHandler) CreateRole(w http.ResponseWriter, r *http.Request) { h.proxy(w, r, "iam:role.create") } func (h *IAMProxyHandler) InspectRole(w http.ResponseWriter, r *http.Request) { h.proxy(w, r, "iam:role.read") } func (h *IAMProxyHandler) UpdateRole(w http.ResponseWriter, r *http.Request) { h.proxy(w, r, "iam:role.update") } func (h *IAMProxyHandler) DeleteRole(w http.ResponseWriter, r *http.Request) { h.proxy(w, r, "iam:role.delete") } func (h *IAMProxyHandler) RestoreRole(w http.ResponseWriter, r *http.Request) { h.proxy(w, r, "iam:role.create") } // --- Role Permissions --- func (h *IAMProxyHandler) GetRolePermissions(w http.ResponseWriter, r *http.Request) { h.proxy(w, r, "iam:role.read") } func (h *IAMProxyHandler) SetRolePermissions(w http.ResponseWriter, r *http.Request) { h.proxy(w, r, "iam:role.update") } func (h *IAMProxyHandler) AddRolePermissions(w http.ResponseWriter, r *http.Request) { h.proxy(w, r, "iam:role.update") } func (h *IAMProxyHandler) RemoveRolePermissions(w http.ResponseWriter, r *http.Request) { h.proxy(w, r, "iam:role.update") } // --- Audit --- func (h *IAMProxyHandler) ListAudit(w http.ResponseWriter, r *http.Request) { h.proxy(w, r, "iam:audit.read") }