From 7c66eee1506681f3f6baaae9fd7bc0f598df2353 Mon Sep 17 00:00:00 2001 From: Javier Marcos <1271349+javuto@users.noreply.github.com> Date: Wed, 27 May 2026 08:27:40 +0200 Subject: [PATCH] Annotations in osctrl-api to keep API documented --- .gitignore | 4 + cmd/api/handlers/audit.go | 21 ++++ cmd/api/handlers/auth_logout.go | 15 +++ cmd/api/handlers/auth_methods.go | 14 +++ cmd/api/handlers/auth_oidc.go | 32 ++++++ cmd/api/handlers/auth_saml.go | 47 +++++++++ cmd/api/handlers/carves.go | 108 +++++++++++++++++++ cmd/api/handlers/environments.go | 136 ++++++++++++++++++++++++ cmd/api/handlers/environments_crud.go | 121 +++++++++++++++++++++ cmd/api/handlers/get.go | 83 +++++++++++++++ cmd/api/handlers/login.go | 18 ++++ cmd/api/handlers/login_envs.go | 14 +++ cmd/api/handlers/logs.go | 21 ++++ cmd/api/handlers/nodes.go | 142 +++++++++++++++++++++++++ cmd/api/handlers/platforms.go | 16 +++ cmd/api/handlers/queries.go | 145 ++++++++++++++++++++++++++ cmd/api/handlers/samples.go | 30 ++++++ cmd/api/handlers/saved_queries.go | 73 +++++++++++++ cmd/api/handlers/settings.go | 81 ++++++++++++++ cmd/api/handlers/settings_patch.go | 19 ++++ cmd/api/handlers/stats.go | 84 +++++++++++++++ cmd/api/handlers/tags.go | 67 ++++++++++++ cmd/api/handlers/users.go | 50 +++++++++ cmd/api/handlers/users_profile.go | 134 ++++++++++++++++++++++++ cmd/api/main.go | 13 +++ 25 files changed, 1488 insertions(+) diff --git a/.gitignore b/.gitignore index 0c32f90c..2fab56ec 100644 --- a/.gitignore +++ b/.gitignore @@ -85,3 +85,7 @@ tools/bruno/collection.bru !CHANGELOG.md !SECURITY.md !frontend/**/*.md + +# Generate swagger files +cmd/api/docs/swagger.json +cmd/api/docs/swagger.yaml diff --git a/cmd/api/handlers/audit.go b/cmd/api/handlers/audit.go index a1ccbe9d..b418a159 100644 --- a/cmd/api/handlers/audit.go +++ b/cmd/api/handlers/audit.go @@ -27,6 +27,27 @@ import ( // // Returns the SPA-canonical paginated envelope. The handler audit-logs the // visit on success. +// @Summary List audit logs +// @Description Returns paginated API audit log entries. +// @Tags audit +// @Produce json +// @Param page query int false "Page number" +// @Param page_size query int false "Page size" +// @Param q query string false "Search query" +// @Param service query string false "Service filter" +// @Param username query string false "Username filter" +// @Param env query string false "Environment filter" +// @Success 200 {object} types.AuditLogsPagedResponse +// @Failure 400 {object} types.ApiErrorResponse "Bad request" +// @Failure 401 {object} types.ApiErrorResponse "Unauthorized" +// @Failure 403 {object} types.ApiErrorResponse "Forbidden" +// @Failure 404 {object} types.ApiErrorResponse "Not found" +// @Failure 409 {object} types.ApiErrorResponse "Conflict" +// @Failure 429 {object} types.ApiErrorResponse "Too many requests" +// @Failure 500 {object} types.ApiErrorResponse "Internal server error" +// @Failure 503 {object} types.ApiErrorResponse "Service unavailable" +// @Security ApiKeyAuth +// @Router /api/v1/audit-logs [get] func (h *HandlersApi) AuditLogsHandler(w http.ResponseWriter, r *http.Request) { if h.DebugHTTPConfig.EnableHTTP { utils.DebugHTTPDump(h.DebugHTTP, r, h.DebugHTTPConfig.ShowBody) diff --git a/cmd/api/handlers/auth_logout.go b/cmd/api/handlers/auth_logout.go index f987abd7..5ce77b0a 100644 --- a/cmd/api/handlers/auth_logout.go +++ b/cmd/api/handlers/auth_logout.go @@ -76,6 +76,21 @@ type LogoutResponse struct { // "someone forged a logout request" — the only consequence is // invalidating the legitimate user's token, which is exactly what // logout is supposed to do. No privilege gain. +// @Summary Log out +// @Description Clears API session cookies and revokes the active token when present. +// @Tags auth +// @Accept json +// @Produce json +// @Success 200 {object} LogoutResponse +// @Failure 400 {object} types.ApiErrorResponse "Bad request" +// @Failure 401 {object} types.ApiErrorResponse "Unauthorized" +// @Failure 403 {object} types.ApiErrorResponse "Forbidden" +// @Failure 404 {object} types.ApiErrorResponse "Not found" +// @Failure 409 {object} types.ApiErrorResponse "Conflict" +// @Failure 429 {object} types.ApiErrorResponse "Too many requests" +// @Failure 500 {object} types.ApiErrorResponse "Internal server error" +// @Failure 503 {object} types.ApiErrorResponse "Service unavailable" +// @Router /api/v1/logout [post] func (h *HandlersApi) LogoutHandler(w http.ResponseWriter, r *http.Request) { if h.DebugHTTPConfig != nil && h.DebugHTTPConfig.EnableHTTP { utils.DebugHTTPDump(h.DebugHTTP, r, false) diff --git a/cmd/api/handlers/auth_methods.go b/cmd/api/handlers/auth_methods.go index 9738e79f..2bfe2fee 100644 --- a/cmd/api/handlers/auth_methods.go +++ b/cmd/api/handlers/auth_methods.go @@ -45,6 +45,20 @@ type AuthMethodsResponse struct { // Rate-limited at the route layer (same preAuthRateLimit as the // env/sample endpoints) to keep this from being a free metadata // scrape vector. +// @Summary List authentication methods +// @Description Returns the authentication methods enabled for the API login UI. +// @Tags auth +// @Produce json +// @Success 200 {object} AuthMethodsResponse +// @Failure 400 {object} types.ApiErrorResponse "Bad request" +// @Failure 401 {object} types.ApiErrorResponse "Unauthorized" +// @Failure 403 {object} types.ApiErrorResponse "Forbidden" +// @Failure 404 {object} types.ApiErrorResponse "Not found" +// @Failure 409 {object} types.ApiErrorResponse "Conflict" +// @Failure 429 {object} types.ApiErrorResponse "Too many requests" +// @Failure 500 {object} types.ApiErrorResponse "Internal server error" +// @Failure 503 {object} types.ApiErrorResponse "Service unavailable" +// @Router /api/v1/auth/methods [get] func (h *HandlersApi) AuthMethodsHandler(w http.ResponseWriter, r *http.Request) { if h.DebugHTTPConfig != nil && h.DebugHTTPConfig.EnableHTTP { utils.DebugHTTPDump(h.DebugHTTP, r, false) diff --git a/cmd/api/handlers/auth_oidc.go b/cmd/api/handlers/auth_oidc.go index 591d3946..785bac86 100644 --- a/cmd/api/handlers/auth_oidc.go +++ b/cmd/api/handlers/auth_oidc.go @@ -93,6 +93,21 @@ func InitOIDC(ctx context.Context, cfg config.YAMLConfigurationOIDC) error { // EnvUUID on the State is a fixed sentinel ("api") because osctrl-api // has no per-env IdP concept — see auth-providers spec § "OIDC is // global." The legacy admin uses "admin" for the same reason. +// @Summary Start OIDC login +// @Description Redirects the browser to the configured OIDC identity provider. +// @Tags auth +// @Produce json +// @Success 200 {string} string +// @Failure 302 {string} string +// @Failure 400 {object} types.ApiErrorResponse "Bad request" +// @Failure 401 {object} types.ApiErrorResponse "Unauthorized" +// @Failure 403 {object} types.ApiErrorResponse "Forbidden" +// @Failure 404 {object} types.ApiErrorResponse "Not found" +// @Failure 409 {object} types.ApiErrorResponse "Conflict" +// @Failure 429 {object} types.ApiErrorResponse "Too many requests" +// @Failure 500 {object} types.ApiErrorResponse "Internal server error" +// @Failure 503 {object} types.ApiErrorResponse "Service unavailable" +// @Router /api/v1/auth/oidc/login [get] func (h *HandlersApi) OIDCLoginHandler(w http.ResponseWriter, r *http.Request) { if h.DebugHTTPConfig != nil && h.DebugHTTPConfig.EnableHTTP { utils.DebugHTTPDump(h.DebugHTTP, r, false) @@ -163,6 +178,23 @@ func (h *HandlersApi) OIDCLoginHandler(w http.ResponseWriter, r *http.Request) { // server-side log records WHY; the client gets a generic outcome. // This is the timing-oracle defense (threat T31): every failure mode // produces an indistinguishable client-visible response. +// @Summary Complete OIDC login +// @Description Handles the OIDC authorization callback and creates an API session. +// @Tags auth +// @Produce json +// @Param code query string false "OIDC authorization code" +// @Param state query string false "OIDC state" +// @Success 200 {string} string +// @Failure 302 {string} string +// @Failure 400 {object} types.ApiErrorResponse "Bad request" +// @Failure 401 {object} types.ApiErrorResponse "Unauthorized" +// @Failure 403 {object} types.ApiErrorResponse "Forbidden" +// @Failure 404 {object} types.ApiErrorResponse "Not found" +// @Failure 409 {object} types.ApiErrorResponse "Conflict" +// @Failure 429 {object} types.ApiErrorResponse "Too many requests" +// @Failure 500 {object} types.ApiErrorResponse "Internal server error" +// @Failure 503 {object} types.ApiErrorResponse "Service unavailable" +// @Router /api/v1/auth/oidc/callback [get] func (h *HandlersApi) OIDCCallbackHandler(w http.ResponseWriter, r *http.Request) { if h.DebugHTTPConfig != nil && h.DebugHTTPConfig.EnableHTTP { utils.DebugHTTPDump(h.DebugHTTP, r, false) diff --git a/cmd/api/handlers/auth_saml.go b/cmd/api/handlers/auth_saml.go index 41ef2a0b..f3905eea 100644 --- a/cmd/api/handlers/auth_saml.go +++ b/cmd/api/handlers/auth_saml.go @@ -86,6 +86,21 @@ func InitSAML(ctx context.Context, cfg config.YAMLConfigurationSAML, entityID, a // EnvUUID on the State is a fixed sentinel ("api") because osctrl-api // is single-tenant for federated login. Matches the OIDC handler's // posture. +// @Summary Start SAML login +// @Description Redirects the browser to the configured SAML identity provider. +// @Tags auth +// @Produce json +// @Success 200 {string} string +// @Failure 302 {string} string +// @Failure 400 {object} types.ApiErrorResponse "Bad request" +// @Failure 401 {object} types.ApiErrorResponse "Unauthorized" +// @Failure 403 {object} types.ApiErrorResponse "Forbidden" +// @Failure 404 {object} types.ApiErrorResponse "Not found" +// @Failure 409 {object} types.ApiErrorResponse "Conflict" +// @Failure 429 {object} types.ApiErrorResponse "Too many requests" +// @Failure 500 {object} types.ApiErrorResponse "Internal server error" +// @Failure 503 {object} types.ApiErrorResponse "Service unavailable" +// @Router /api/v1/auth/saml/login [get] func (h *HandlersApi) SAMLLoginHandler(w http.ResponseWriter, r *http.Request) { if h.DebugHTTPConfig != nil && h.DebugHTTPConfig.EnableHTTP { utils.DebugHTTPDump(h.DebugHTTP, r, false) @@ -147,6 +162,24 @@ func (h *HandlersApi) SAMLLoginHandler(w http.ResponseWriter, r *http.Request) { // Failure paths redirect to "/" too (no error param leak). Server-side // log records WHY; client sees a generic outcome. Timing-oracle defense // matches the OIDC handler. +// @Summary Complete SAML login +// @Description Handles the SAML assertion consumer service callback and creates an API session. +// @Tags auth +// @Accept json +// @Produce json +// @Param SAMLResponse formData string false "SAML response assertion" +// @Param RelayState formData string false "SAML relay state" +// @Success 200 {string} string +// @Failure 302 {string} string +// @Failure 400 {object} types.ApiErrorResponse "Bad request" +// @Failure 401 {object} types.ApiErrorResponse "Unauthorized" +// @Failure 403 {object} types.ApiErrorResponse "Forbidden" +// @Failure 404 {object} types.ApiErrorResponse "Not found" +// @Failure 409 {object} types.ApiErrorResponse "Conflict" +// @Failure 429 {object} types.ApiErrorResponse "Too many requests" +// @Failure 500 {object} types.ApiErrorResponse "Internal server error" +// @Failure 503 {object} types.ApiErrorResponse "Service unavailable" +// @Router /api/v1/auth/saml/acs [post] func (h *HandlersApi) SAMLACSHandler(w http.ResponseWriter, r *http.Request) { if h.DebugHTTPConfig != nil && h.DebugHTTPConfig.EnableHTTP { utils.DebugHTTPDump(h.DebugHTTP, r, false) @@ -208,6 +241,20 @@ func (h *HandlersApi) SAMLACSHandler(w http.ResponseWriter, r *http.Request) { // during the first login attempt. // // Rate-limited at the route layer like the other unauth endpoints. +// @Summary Get SAML metadata +// @Description Returns service provider metadata for SAML identity provider registration. +// @Tags auth +// @Produce application/xml +// @Success 200 {string} string +// @Failure 400 {object} types.ApiErrorResponse "Bad request" +// @Failure 401 {object} types.ApiErrorResponse "Unauthorized" +// @Failure 403 {object} types.ApiErrorResponse "Forbidden" +// @Failure 404 {object} types.ApiErrorResponse "Not found" +// @Failure 409 {object} types.ApiErrorResponse "Conflict" +// @Failure 429 {object} types.ApiErrorResponse "Too many requests" +// @Failure 500 {object} types.ApiErrorResponse "Internal server error" +// @Failure 503 {object} types.ApiErrorResponse "Service unavailable" +// @Router /api/v1/auth/saml/metadata [get] func (h *HandlersApi) SAMLMetadataHandler(w http.ResponseWriter, r *http.Request) { if h.DebugHTTPConfig != nil && h.DebugHTTPConfig.EnableHTTP { utils.DebugHTTPDump(h.DebugHTTP, r, false) diff --git a/cmd/api/handlers/carves.go b/cmd/api/handlers/carves.go index 3ed3a822..4f911c3c 100644 --- a/cmd/api/handlers/carves.go +++ b/cmd/api/handlers/carves.go @@ -47,6 +47,23 @@ func carveFileView(c carves.CarvedFile) types.CarveFileView { // Returns the carve query metadata plus the array of per-node CarvedFile rows // produced by the carve. Returns 404 when the carve query name does not exist // in the environment. +// @Summary Get file carve +// @Description Returns a file carve and the files produced by it. +// @Tags carves +// @Produce json +// @Param env path string true "Environment name or UUID" +// @Param name path string true "Carve query name" +// @Success 200 {object} types.CarveDetailResponse +// @Failure 400 {object} types.ApiErrorResponse "Bad request" +// @Failure 401 {object} types.ApiErrorResponse "Unauthorized" +// @Failure 403 {object} types.ApiErrorResponse "Forbidden" +// @Failure 404 {object} types.ApiErrorResponse "Not found" +// @Failure 409 {object} types.ApiErrorResponse "Conflict" +// @Failure 429 {object} types.ApiErrorResponse "Too many requests" +// @Failure 500 {object} types.ApiErrorResponse "Internal server error" +// @Failure 503 {object} types.ApiErrorResponse "Service unavailable" +// @Security ApiKeyAuth +// @Router /api/v1/carves/{env}/{name} [get] func (h *HandlersApi) CarveShowHandler(w http.ResponseWriter, r *http.Request) { if h.DebugHTTPConfig.EnableHTTP { utils.DebugHTTPDump(h.DebugHTTP, r, h.DebugHTTPConfig.ShowBody) @@ -108,6 +125,23 @@ func (h *HandlersApi) CarveShowHandler(w http.ResponseWriter, r *http.Request) { // // Returns carve queries by target. Retained from the legacy contract; the // canonical list endpoint is now CarveListHandler at /api/v1/carves/{env}. +// @Summary List carve queries +// @Description Returns file carve queries by target and environment. +// @Tags carves +// @Produce json +// @Param env path string true "Environment name or UUID" +// @Param target path string true "Carve target filter" +// @Success 200 {array} queries.DistributedQuery +// @Failure 400 {object} types.ApiErrorResponse "Bad request" +// @Failure 401 {object} types.ApiErrorResponse "Unauthorized" +// @Failure 403 {object} types.ApiErrorResponse "Forbidden" +// @Failure 404 {object} types.ApiErrorResponse "Not found" +// @Failure 409 {object} types.ApiErrorResponse "Conflict" +// @Failure 429 {object} types.ApiErrorResponse "Too many requests" +// @Failure 500 {object} types.ApiErrorResponse "Internal server error" +// @Failure 503 {object} types.ApiErrorResponse "Service unavailable" +// @Security ApiKeyAuth +// @Router /api/v1/carves/{env}/queries/{target} [get] func (h *HandlersApi) CarveQueriesHandler(w http.ResponseWriter, r *http.Request) { if h.DebugHTTPConfig.EnableHTTP { utils.DebugHTTPDump(h.DebugHTTP, r, h.DebugHTTPConfig.ShowBody) @@ -151,6 +185,26 @@ func (h *HandlersApi) CarveQueriesHandler(w http.ResponseWriter, r *http.Request // Paginated, sorted, searchable list of carve queries (DistributedQuery rows // with type=carve). Query params: page, page_size, q, sort, dir, target. // Empty result → HTTP 200 with items: []. +// @Summary List file carves +// @Description Returns paginated file carves for an environment. +// @Tags carves +// @Produce json +// @Param env path string true "Environment name or UUID" +// @Param page query int false "Page number" +// @Param page_size query int false "Page size" +// @Param q query string false "Search query" +// @Success 200 {object} types.CarvesPagedResponse +// @Failure 400 {object} types.ApiErrorResponse "Bad request" +// @Failure 401 {object} types.ApiErrorResponse "Unauthorized" +// @Failure 403 {object} types.ApiErrorResponse "Forbidden" +// @Failure 404 {object} types.ApiErrorResponse "Not found" +// @Failure 409 {object} types.ApiErrorResponse "Conflict" +// @Failure 429 {object} types.ApiErrorResponse "Too many requests" +// @Failure 500 {object} types.ApiErrorResponse "Internal server error" +// @Failure 503 {object} types.ApiErrorResponse "Service unavailable" +// @Security ApiKeyAuth +// @Router /api/v1/carves/{env} [get] +// @Router /api/v1/carves/{env}/list [get] func (h *HandlersApi) CarveListHandler(w http.ResponseWriter, r *http.Request) { if h.DebugHTTPConfig.EnableHTTP { utils.DebugHTTPDump(h.DebugHTTP, r, h.DebugHTTPConfig.ShowBody) @@ -222,6 +276,24 @@ func (h *HandlersApi) CarveListHandler(w http.ResponseWriter, r *http.Request) { } // CarvesRunHandler - POST /api/v1/carves/{env} +// @Summary Run file carve +// @Description Starts a new file carve. +// @Tags carves +// @Accept json +// @Produce json +// @Param env path string true "Environment name or UUID" +// @Param request body types.ApiDistributedQueryRequest true "Request body" +// @Success 200 {object} types.ApiQueriesResponse +// @Failure 400 {object} types.ApiErrorResponse "Bad request" +// @Failure 401 {object} types.ApiErrorResponse "Unauthorized" +// @Failure 403 {object} types.ApiErrorResponse "Forbidden" +// @Failure 404 {object} types.ApiErrorResponse "Not found" +// @Failure 409 {object} types.ApiErrorResponse "Conflict" +// @Failure 429 {object} types.ApiErrorResponse "Too many requests" +// @Failure 500 {object} types.ApiErrorResponse "Internal server error" +// @Failure 503 {object} types.ApiErrorResponse "Service unavailable" +// @Security ApiKeyAuth +// @Router /api/v1/carves/{env} [post] func (h *HandlersApi) CarvesRunHandler(w http.ResponseWriter, r *http.Request) { if h.DebugHTTPConfig.EnableHTTP { utils.DebugHTTPDump(h.DebugHTTP, r, h.DebugHTTPConfig.ShowBody) @@ -314,6 +386,25 @@ func (h *HandlersApi) CarvesRunHandler(w http.ResponseWriter, r *http.Request) { } // CarvesActionHandler - POST /api/v1/carves/{env}/{action}/{name} +// @Summary Execute carve action +// @Description Deletes, expires, or otherwise acts on a file carve. +// @Tags carves +// @Accept json +// @Produce json +// @Param env path string true "Environment name or UUID" +// @Param action path string true "Carve action" +// @Param name path string true "Carve query name" +// @Success 200 {object} types.ApiGenericResponse +// @Failure 400 {object} types.ApiErrorResponse "Bad request" +// @Failure 401 {object} types.ApiErrorResponse "Unauthorized" +// @Failure 403 {object} types.ApiErrorResponse "Forbidden" +// @Failure 404 {object} types.ApiErrorResponse "Not found" +// @Failure 409 {object} types.ApiErrorResponse "Conflict" +// @Failure 429 {object} types.ApiErrorResponse "Too many requests" +// @Failure 500 {object} types.ApiErrorResponse "Internal server error" +// @Failure 503 {object} types.ApiErrorResponse "Service unavailable" +// @Security ApiKeyAuth +// @Router /api/v1/carves/{env}/{action}/{name} [post] func (h *HandlersApi) CarvesActionHandler(w http.ResponseWriter, r *http.Request) { if h.DebugHTTPConfig.EnableHTTP { utils.DebugHTTPDump(h.DebugHTTP, r, h.DebugHTTPConfig.ShowBody) @@ -394,6 +485,23 @@ func (h *HandlersApi) CarvesActionHandler(w http.ResponseWriter, r *http.Request // download URL is returned via 302 redirect). // // Content-Disposition is set to attachment with the carve archive filename. +// @Summary Download carve archive +// @Description Downloads the archive for a completed file carve. +// @Tags carves +// @Produce application/octet-stream +// @Param env path string true "Environment name or UUID" +// @Param name path string true "Carve query name" +// @Success 200 {file} file +// @Failure 400 {object} types.ApiErrorResponse "Bad request" +// @Failure 401 {object} types.ApiErrorResponse "Unauthorized" +// @Failure 403 {object} types.ApiErrorResponse "Forbidden" +// @Failure 404 {object} types.ApiErrorResponse "Not found" +// @Failure 409 {object} types.ApiErrorResponse "Conflict" +// @Failure 429 {object} types.ApiErrorResponse "Too many requests" +// @Failure 500 {object} types.ApiErrorResponse "Internal server error" +// @Failure 503 {object} types.ApiErrorResponse "Service unavailable" +// @Security ApiKeyAuth +// @Router /api/v1/carves/{env}/archive/{name} [get] func (h *HandlersApi) CarveArchiveHandler(w http.ResponseWriter, r *http.Request) { if h.DebugHTTPConfig.EnableHTTP { utils.DebugHTTPDump(h.DebugHTTP, r, h.DebugHTTPConfig.ShowBody) diff --git a/cmd/api/handlers/environments.go b/cmd/api/handlers/environments.go index 3f4eee0a..93f83c39 100644 --- a/cmd/api/handlers/environments.go +++ b/cmd/api/handlers/environments.go @@ -64,6 +64,22 @@ func projectEnvironmentView(env environments.TLSEnvironment) types.TLSEnvironmen } // EnvironmentHandler - GET Handler to return one environment by UUID as JSON +// @Summary Get environment +// @Description Returns one environment by name or UUID. +// @Tags environments +// @Produce json +// @Param env path string true "Environment name or UUID" +// @Success 200 {object} types.TLSEnvironmentView +// @Failure 400 {object} types.ApiErrorResponse "Bad request" +// @Failure 401 {object} types.ApiErrorResponse "Unauthorized" +// @Failure 403 {object} types.ApiErrorResponse "Forbidden" +// @Failure 404 {object} types.ApiErrorResponse "Not found" +// @Failure 409 {object} types.ApiErrorResponse "Conflict" +// @Failure 429 {object} types.ApiErrorResponse "Too many requests" +// @Failure 500 {object} types.ApiErrorResponse "Internal server error" +// @Failure 503 {object} types.ApiErrorResponse "Service unavailable" +// @Security ApiKeyAuth +// @Router /api/v1/environments/{env} [get] func (h *HandlersApi) EnvironmentHandler(w http.ResponseWriter, r *http.Request) { // Debug HTTP if enabled if h.DebugHTTPConfig.EnableHTTP { @@ -106,6 +122,22 @@ func (h *HandlersApi) EnvironmentHandler(w http.ResponseWriter, r *http.Request) } // EnvironmentMapHandler - GET Handler to return one environment as JSON +// @Summary Map environments +// @Description Returns an environment lookup map by target key. +// @Tags environments +// @Produce json +// @Param target path string true "Map target: id, name, or uuid" +// @Success 200 {object} map[string]environments.NameUUID +// @Failure 400 {object} types.ApiErrorResponse "Bad request" +// @Failure 401 {object} types.ApiErrorResponse "Unauthorized" +// @Failure 403 {object} types.ApiErrorResponse "Forbidden" +// @Failure 404 {object} types.ApiErrorResponse "Not found" +// @Failure 409 {object} types.ApiErrorResponse "Conflict" +// @Failure 429 {object} types.ApiErrorResponse "Too many requests" +// @Failure 500 {object} types.ApiErrorResponse "Internal server error" +// @Failure 503 {object} types.ApiErrorResponse "Service unavailable" +// @Security ApiKeyAuth +// @Router /api/v1/environments/map/{target} [get] func (h *HandlersApi) EnvironmentMapHandler(w http.ResponseWriter, r *http.Request) { // Debug HTTP if enabled if h.DebugHTTPConfig.EnableHTTP { @@ -157,6 +189,21 @@ func (h *HandlersApi) EnvironmentMapHandler(w http.ResponseWriter, r *http.Reque // meant a non-super-admin user with valid env permissions couldn't // even populate the SPA's env switcher — their nav read "No // environments configured" even though they had access to envs. +// @Summary List environments +// @Description Returns environments visible to the authenticated user. +// @Tags environments +// @Produce json +// @Success 200 {array} types.TLSEnvironmentView +// @Failure 400 {object} types.ApiErrorResponse "Bad request" +// @Failure 401 {object} types.ApiErrorResponse "Unauthorized" +// @Failure 403 {object} types.ApiErrorResponse "Forbidden" +// @Failure 404 {object} types.ApiErrorResponse "Not found" +// @Failure 409 {object} types.ApiErrorResponse "Conflict" +// @Failure 429 {object} types.ApiErrorResponse "Too many requests" +// @Failure 500 {object} types.ApiErrorResponse "Internal server error" +// @Failure 503 {object} types.ApiErrorResponse "Service unavailable" +// @Security ApiKeyAuth +// @Router /api/v1/environments [get] func (h *HandlersApi) EnvironmentsHandler(w http.ResponseWriter, r *http.Request) { // Debug HTTP if enabled if h.DebugHTTPConfig.EnableHTTP { @@ -197,6 +244,23 @@ func (h *HandlersApi) EnvironmentsHandler(w http.ResponseWriter, r *http.Request } // EnvEnrollHandler - GET Handler to return node enrollment values (secret, certificate, one-liner) for an environment as JSON +// @Summary Get enrollment values +// @Description Returns enrollment helper values for an environment. +// @Tags environments +// @Produce json +// @Param env path string true "Environment name or UUID" +// @Param target path string true "Enrollment target" +// @Success 200 {object} types.ApiDataResponse +// @Failure 400 {object} types.ApiErrorResponse "Bad request" +// @Failure 401 {object} types.ApiErrorResponse "Unauthorized" +// @Failure 403 {object} types.ApiErrorResponse "Forbidden" +// @Failure 404 {object} types.ApiErrorResponse "Not found" +// @Failure 409 {object} types.ApiErrorResponse "Conflict" +// @Failure 429 {object} types.ApiErrorResponse "Too many requests" +// @Failure 500 {object} types.ApiErrorResponse "Internal server error" +// @Failure 503 {object} types.ApiErrorResponse "Service unavailable" +// @Security ApiKeyAuth +// @Router /api/v1/environments/{env}/enroll/{target} [get] func (h *HandlersApi) EnvEnrollHandler(w http.ResponseWriter, r *http.Request) { // Debug HTTP if enabled if h.DebugHTTPConfig.EnableHTTP { @@ -267,6 +331,23 @@ func (h *HandlersApi) EnvEnrollHandler(w http.ResponseWriter, r *http.Request) { } // EnvRemoveHandler - GET Handler to return node removal values for an environment as JSON +// @Summary Get removal values +// @Description Returns removal helper values for an environment. +// @Tags environments +// @Produce json +// @Param env path string true "Environment name or UUID" +// @Param target path string true "Removal target" +// @Success 200 {object} types.ApiDataResponse +// @Failure 400 {object} types.ApiErrorResponse "Bad request" +// @Failure 401 {object} types.ApiErrorResponse "Unauthorized" +// @Failure 403 {object} types.ApiErrorResponse "Forbidden" +// @Failure 404 {object} types.ApiErrorResponse "Not found" +// @Failure 409 {object} types.ApiErrorResponse "Conflict" +// @Failure 429 {object} types.ApiErrorResponse "Too many requests" +// @Failure 500 {object} types.ApiErrorResponse "Internal server error" +// @Failure 503 {object} types.ApiErrorResponse "Service unavailable" +// @Security ApiKeyAuth +// @Router /api/v1/environments/{env}/remove/{target} [get] func (h *HandlersApi) EnvRemoveHandler(w http.ResponseWriter, r *http.Request) { // Debug HTTP if enabled if h.DebugHTTPConfig.EnableHTTP { @@ -328,6 +409,25 @@ func (h *HandlersApi) EnvRemoveHandler(w http.ResponseWriter, r *http.Request) { } // EnvEnrollActionsHandler - POST Handler to perform actions (extend, expire) in enroll values +// @Summary Execute enrollment action +// @Description Extends, expires, rotates, or updates enrollment values for an environment. +// @Tags environments +// @Accept json +// @Produce json +// @Param env path string true "Environment name or UUID" +// @Param action path string true "Enrollment action" +// @Param request body types.ApiActionsRequest true "Request body" +// @Success 200 {object} types.ApiGenericResponse +// @Failure 400 {object} types.ApiErrorResponse "Bad request" +// @Failure 401 {object} types.ApiErrorResponse "Unauthorized" +// @Failure 403 {object} types.ApiErrorResponse "Forbidden" +// @Failure 404 {object} types.ApiErrorResponse "Not found" +// @Failure 409 {object} types.ApiErrorResponse "Conflict" +// @Failure 429 {object} types.ApiErrorResponse "Too many requests" +// @Failure 500 {object} types.ApiErrorResponse "Internal server error" +// @Failure 503 {object} types.ApiErrorResponse "Service unavailable" +// @Security ApiKeyAuth +// @Router /api/v1/environments/{env}/enroll/{action} [post] func (h *HandlersApi) EnvEnrollActionsHandler(w http.ResponseWriter, r *http.Request) { // Debug HTTP if enabled if h.DebugHTTPConfig.EnableHTTP { @@ -428,6 +528,25 @@ func (h *HandlersApi) EnvEnrollActionsHandler(w http.ResponseWriter, r *http.Req } // EnvRemoveActionsHandler - POST Handler to perform actions (extend, expire) in remove values +// @Summary Execute removal action +// @Description Extends, expires, rotates, or updates removal values for an environment. +// @Tags environments +// @Accept json +// @Produce json +// @Param env path string true "Environment name or UUID" +// @Param action path string true "Removal action" +// @Param request body types.ApiActionsRequest true "Request body" +// @Success 200 {object} types.ApiGenericResponse +// @Failure 400 {object} types.ApiErrorResponse "Bad request" +// @Failure 401 {object} types.ApiErrorResponse "Unauthorized" +// @Failure 403 {object} types.ApiErrorResponse "Forbidden" +// @Failure 404 {object} types.ApiErrorResponse "Not found" +// @Failure 409 {object} types.ApiErrorResponse "Conflict" +// @Failure 429 {object} types.ApiErrorResponse "Too many requests" +// @Failure 500 {object} types.ApiErrorResponse "Internal server error" +// @Failure 503 {object} types.ApiErrorResponse "Service unavailable" +// @Security ApiKeyAuth +// @Router /api/v1/environments/{env}/remove/{action} [post] func (h *HandlersApi) EnvRemoveActionsHandler(w http.ResponseWriter, r *http.Request) { // Debug HTTP if enabled if h.DebugHTTPConfig.EnableHTTP { @@ -503,6 +622,23 @@ func (h *HandlersApi) EnvRemoveActionsHandler(w http.ResponseWriter, r *http.Req } // EnvActionsHandler - POST Handler to perform actions (create, delete, edit) on environments +// @Summary Execute environment action +// @Description Creates or modifies an environment using the legacy action endpoint. +// @Tags environments +// @Accept json +// @Produce json +// @Param request body types.ApiEnvRequest true "Request body" +// @Success 200 {object} types.ApiGenericResponse +// @Failure 400 {object} types.ApiErrorResponse "Bad request" +// @Failure 401 {object} types.ApiErrorResponse "Unauthorized" +// @Failure 403 {object} types.ApiErrorResponse "Forbidden" +// @Failure 404 {object} types.ApiErrorResponse "Not found" +// @Failure 409 {object} types.ApiErrorResponse "Conflict" +// @Failure 429 {object} types.ApiErrorResponse "Too many requests" +// @Failure 500 {object} types.ApiErrorResponse "Internal server error" +// @Failure 503 {object} types.ApiErrorResponse "Service unavailable" +// @Security ApiKeyAuth +// @Router /api/v1/environments/actions [post] func (h *HandlersApi) EnvActionsHandler(w http.ResponseWriter, r *http.Request) { // Debug HTTP if enabled if h.DebugHTTPConfig.EnableHTTP { diff --git a/cmd/api/handlers/environments_crud.go b/cmd/api/handlers/environments_crud.go index ee6bab0b..ac2d08ab 100644 --- a/cmd/api/handlers/environments_crud.go +++ b/cmd/api/handlers/environments_crud.go @@ -21,6 +21,23 @@ import ( // Body: { name, hostname, type? }. Generates a UUID, defaults config / // schedule / packs / decorators / ATC to "{}", and persists the env. // Returns 201 with the created TLSEnvironment. Super-admin only. +// @Summary Create environment +// @Description Creates an environment. +// @Tags environments +// @Accept json +// @Produce json +// @Param request body types.EnvCreateRequest true "Request body" +// @Success 200 {object} environments.TLSEnvironment +// @Failure 400 {object} types.ApiErrorResponse "Bad request" +// @Failure 401 {object} types.ApiErrorResponse "Unauthorized" +// @Failure 403 {object} types.ApiErrorResponse "Forbidden" +// @Failure 404 {object} types.ApiErrorResponse "Not found" +// @Failure 409 {object} types.ApiErrorResponse "Conflict" +// @Failure 429 {object} types.ApiErrorResponse "Too many requests" +// @Failure 500 {object} types.ApiErrorResponse "Internal server error" +// @Failure 503 {object} types.ApiErrorResponse "Service unavailable" +// @Security ApiKeyAuth +// @Router /api/v1/environments [post] func (h *HandlersApi) EnvironmentCreateHandler(w http.ResponseWriter, r *http.Request) { if h.DebugHTTPConfig.EnableHTTP { utils.DebugHTTPDump(h.DebugHTTP, r, h.DebugHTTPConfig.ShowBody) @@ -108,6 +125,24 @@ func (h *HandlersApi) EnvironmentCreateHandler(w http.ResponseWriter, r *http.Re // // Updates name / hostname / type / icon / debug_http / accept_enrolls. // Other env fields go through the per-section endpoints. Super-admin only. +// @Summary Update environment +// @Description Updates an environment. +// @Tags environments +// @Accept json +// @Produce json +// @Param env path string true "Environment name or UUID" +// @Param request body types.EnvUpdateRequest true "Request body" +// @Success 200 {object} environments.TLSEnvironment +// @Failure 400 {object} types.ApiErrorResponse "Bad request" +// @Failure 401 {object} types.ApiErrorResponse "Unauthorized" +// @Failure 403 {object} types.ApiErrorResponse "Forbidden" +// @Failure 404 {object} types.ApiErrorResponse "Not found" +// @Failure 409 {object} types.ApiErrorResponse "Conflict" +// @Failure 429 {object} types.ApiErrorResponse "Too many requests" +// @Failure 500 {object} types.ApiErrorResponse "Internal server error" +// @Failure 503 {object} types.ApiErrorResponse "Service unavailable" +// @Security ApiKeyAuth +// @Router /api/v1/environments/{env} [patch] func (h *HandlersApi) EnvironmentUpdateHandler(w http.ResponseWriter, r *http.Request) { if h.DebugHTTPConfig.EnableHTTP { utils.DebugHTTPDump(h.DebugHTTP, r, h.DebugHTTPConfig.ShowBody) @@ -225,6 +260,22 @@ func (h *HandlersApi) EnvironmentUpdateHandler(w http.ResponseWriter, r *http.Re // EnvironmentDeleteHandler - DELETE /api/v1/environments/{env} // // Removes the environment. Super-admin only. Returns 200 with a message. +// @Summary Delete environment +// @Description Deletes an environment. +// @Tags environments +// @Produce json +// @Param env path string true "Environment name or UUID" +// @Success 200 {object} types.ApiGenericResponse +// @Failure 400 {object} types.ApiErrorResponse "Bad request" +// @Failure 401 {object} types.ApiErrorResponse "Unauthorized" +// @Failure 403 {object} types.ApiErrorResponse "Forbidden" +// @Failure 404 {object} types.ApiErrorResponse "Not found" +// @Failure 409 {object} types.ApiErrorResponse "Conflict" +// @Failure 429 {object} types.ApiErrorResponse "Too many requests" +// @Failure 500 {object} types.ApiErrorResponse "Internal server error" +// @Failure 503 {object} types.ApiErrorResponse "Service unavailable" +// @Security ApiKeyAuth +// @Router /api/v1/environments/{env} [delete] func (h *HandlersApi) EnvironmentDeleteHandler(w http.ResponseWriter, r *http.Request) { if h.DebugHTTPConfig.EnableHTTP { utils.DebugHTTPDump(h.DebugHTTP, r, h.DebugHTTPConfig.ShowBody) @@ -261,6 +312,22 @@ func (h *HandlersApi) EnvironmentDeleteHandler(w http.ResponseWriter, r *http.Re // // Returns the env's JSON-shaped config sections (options/schedule/packs/ // decorators/atc/flags) so the SPA's Monaco editor can render each section. +// @Summary Get environment config +// @Description Returns raw osquery config sections for an environment. +// @Tags environments +// @Produce json +// @Param env path string true "Environment name or UUID" +// @Success 200 {object} types.EnvConfigResponse +// @Failure 400 {object} types.ApiErrorResponse "Bad request" +// @Failure 401 {object} types.ApiErrorResponse "Unauthorized" +// @Failure 403 {object} types.ApiErrorResponse "Forbidden" +// @Failure 404 {object} types.ApiErrorResponse "Not found" +// @Failure 409 {object} types.ApiErrorResponse "Conflict" +// @Failure 429 {object} types.ApiErrorResponse "Too many requests" +// @Failure 500 {object} types.ApiErrorResponse "Internal server error" +// @Failure 503 {object} types.ApiErrorResponse "Service unavailable" +// @Security ApiKeyAuth +// @Router /api/v1/environments/config/{env} [get] func (h *HandlersApi) EnvironmentConfigHandler(w http.ResponseWriter, r *http.Request) { if h.DebugHTTPConfig.EnableHTTP { utils.DebugHTTPDump(h.DebugHTTP, r, h.DebugHTTPConfig.ShowBody) @@ -301,6 +368,24 @@ func (h *HandlersApi) EnvironmentConfigHandler(w http.ResponseWriter, r *http.Re // Body: optional options/schedule/packs/decorators/atc/flags string fields. // Each non-nil field is validated as JSON before persisting; an invalid // payload is rejected with 400 (no partial writes). +// @Summary Update environment config +// @Description Updates raw osquery config sections for an environment. +// @Tags environments +// @Accept json +// @Produce json +// @Param env path string true "Environment name or UUID" +// @Param request body types.EnvConfigPatchRequest true "Request body" +// @Success 200 {object} types.EnvConfigResponse +// @Failure 400 {object} types.ApiErrorResponse "Bad request" +// @Failure 401 {object} types.ApiErrorResponse "Unauthorized" +// @Failure 403 {object} types.ApiErrorResponse "Forbidden" +// @Failure 404 {object} types.ApiErrorResponse "Not found" +// @Failure 409 {object} types.ApiErrorResponse "Conflict" +// @Failure 429 {object} types.ApiErrorResponse "Too many requests" +// @Failure 500 {object} types.ApiErrorResponse "Internal server error" +// @Failure 503 {object} types.ApiErrorResponse "Service unavailable" +// @Security ApiKeyAuth +// @Router /api/v1/environments/config/{env} [patch] func (h *HandlersApi) EnvironmentConfigPatchHandler(w http.ResponseWriter, r *http.Request) { if h.DebugHTTPConfig.EnableHTTP { utils.DebugHTTPDump(h.DebugHTTP, r, h.DebugHTTPConfig.ShowBody) @@ -414,6 +499,24 @@ func (h *HandlersApi) EnvironmentConfigPatchHandler(w http.ResponseWriter, r *ht // // Body: { config_interval?, log_interval?, query_interval? }. Updates the // three node-pull intervals atomically. Unsupplied fields are kept. +// @Summary Update environment intervals +// @Description Updates osquery interval settings for an environment. +// @Tags environments +// @Accept json +// @Produce json +// @Param env path string true "Environment name or UUID" +// @Param request body types.EnvIntervalsPatchRequest true "Request body" +// @Success 200 {object} environments.TLSEnvironment +// @Failure 400 {object} types.ApiErrorResponse "Bad request" +// @Failure 401 {object} types.ApiErrorResponse "Unauthorized" +// @Failure 403 {object} types.ApiErrorResponse "Forbidden" +// @Failure 404 {object} types.ApiErrorResponse "Not found" +// @Failure 409 {object} types.ApiErrorResponse "Conflict" +// @Failure 429 {object} types.ApiErrorResponse "Too many requests" +// @Failure 500 {object} types.ApiErrorResponse "Internal server error" +// @Failure 503 {object} types.ApiErrorResponse "Service unavailable" +// @Security ApiKeyAuth +// @Router /api/v1/environments/intervals/{env} [patch] func (h *HandlersApi) EnvironmentIntervalsPatchHandler(w http.ResponseWriter, r *http.Request) { if h.DebugHTTPConfig.EnableHTTP { utils.DebugHTTPDump(h.DebugHTTP, r, h.DebugHTTPConfig.ShowBody) @@ -483,6 +586,24 @@ func (h *HandlersApi) EnvironmentIntervalsPatchHandler(w http.ResponseWriter, r // (extend / expire / rotate / not-expire), accepting one of those actions // via JSON body instead of as a path segment. Mirrors the legacy // EnvEnrollActionsHandler semantics for both enroll and remove paths. +// @Summary Update environment expiration +// @Description Updates enrollment expiration state for an environment. +// @Tags environments +// @Accept json +// @Produce json +// @Param env path string true "Environment name or UUID" +// @Param request body types.EnvExpirationPatchRequest true "Request body" +// @Success 200 {object} environments.TLSEnvironment +// @Failure 400 {object} types.ApiErrorResponse "Bad request" +// @Failure 401 {object} types.ApiErrorResponse "Unauthorized" +// @Failure 403 {object} types.ApiErrorResponse "Forbidden" +// @Failure 404 {object} types.ApiErrorResponse "Not found" +// @Failure 409 {object} types.ApiErrorResponse "Conflict" +// @Failure 429 {object} types.ApiErrorResponse "Too many requests" +// @Failure 500 {object} types.ApiErrorResponse "Internal server error" +// @Failure 503 {object} types.ApiErrorResponse "Service unavailable" +// @Security ApiKeyAuth +// @Router /api/v1/environments/expiration/{env} [patch] func (h *HandlersApi) EnvironmentExpirationPatchHandler(w http.ResponseWriter, r *http.Request) { if h.DebugHTTPConfig.EnableHTTP { utils.DebugHTTPDump(h.DebugHTTP, r, h.DebugHTTPConfig.ShowBody) diff --git a/cmd/api/handlers/get.go b/cmd/api/handlers/get.go index 5f0acf26..3272f96c 100644 --- a/cmd/api/handlers/get.go +++ b/cmd/api/handlers/get.go @@ -9,6 +9,20 @@ import ( ) // HealthHandler - Handle health requests +// @Summary API health check +// @Description Returns the API health response. +// @Tags system +// @Produce json +// @Success 200 {string} string +// @Failure 400 {object} types.ApiErrorResponse "Bad request" +// @Failure 401 {object} types.ApiErrorResponse "Unauthorized" +// @Failure 403 {object} types.ApiErrorResponse "Forbidden" +// @Failure 404 {object} types.ApiErrorResponse "Not found" +// @Failure 409 {object} types.ApiErrorResponse "Conflict" +// @Failure 429 {object} types.ApiErrorResponse "Too many requests" +// @Failure 500 {object} types.ApiErrorResponse "Internal server error" +// @Failure 503 {object} types.ApiErrorResponse "Service unavailable" +// @Router /health [get] func (h *HandlersApi) HealthHandler(w http.ResponseWriter, r *http.Request) { // Debug HTTP if enabled if h.DebugHTTPConfig.EnableHTTP { @@ -19,6 +33,20 @@ func (h *HandlersApi) HealthHandler(w http.ResponseWriter, r *http.Request) { } // CheckHandlerNoAuth - Handle unauthenticated check requests +// @Summary Unauthenticated API check +// @Description Returns API availability without requiring authentication. +// @Tags checks +// @Produce json +// @Success 200 {string} string +// @Failure 400 {object} types.ApiErrorResponse "Bad request" +// @Failure 401 {object} types.ApiErrorResponse "Unauthorized" +// @Failure 403 {object} types.ApiErrorResponse "Forbidden" +// @Failure 404 {object} types.ApiErrorResponse "Not found" +// @Failure 409 {object} types.ApiErrorResponse "Conflict" +// @Failure 429 {object} types.ApiErrorResponse "Too many requests" +// @Failure 500 {object} types.ApiErrorResponse "Internal server error" +// @Failure 503 {object} types.ApiErrorResponse "Service unavailable" +// @Router /api/v1/checks-no-auth [get] func (h *HandlersApi) CheckHandlerNoAuth(w http.ResponseWriter, r *http.Request) { // Debug HTTP if enabled if h.DebugHTTPConfig.EnableHTTP { @@ -29,6 +57,21 @@ func (h *HandlersApi) CheckHandlerNoAuth(w http.ResponseWriter, r *http.Request) } // CheckHandlerAuth - Handle authenticated check requests +// @Summary Authenticated API check +// @Description Returns API availability for an authenticated user. +// @Tags checks +// @Produce json +// @Success 200 {string} string +// @Failure 400 {object} types.ApiErrorResponse "Bad request" +// @Failure 401 {object} types.ApiErrorResponse "Unauthorized" +// @Failure 403 {object} types.ApiErrorResponse "Forbidden" +// @Failure 404 {object} types.ApiErrorResponse "Not found" +// @Failure 409 {object} types.ApiErrorResponse "Conflict" +// @Failure 429 {object} types.ApiErrorResponse "Too many requests" +// @Failure 500 {object} types.ApiErrorResponse "Internal server error" +// @Failure 503 {object} types.ApiErrorResponse "Service unavailable" +// @Security ApiKeyAuth +// @Router /api/v1/checks-auth [get] func (h *HandlersApi) CheckHandlerAuth(w http.ResponseWriter, r *http.Request) { // Debug HTTP if enabled if h.DebugHTTPConfig.EnableHTTP { @@ -58,6 +101,20 @@ func (h *HandlersApi) CheckHandlerAuth(w http.ResponseWriter, r *http.Request) { // Returning 404 here doesn't leak endpoint structure beyond what's // already in the public OpenAPI; it just stops the api from silently // claiming success on misrouted requests. +// @Summary API root liveness check +// @Description Returns the API root liveness response. +// @Tags system +// @Produce json +// @Success 200 {string} string +// @Failure 400 {object} types.ApiErrorResponse "Bad request" +// @Failure 401 {object} types.ApiErrorResponse "Unauthorized" +// @Failure 403 {object} types.ApiErrorResponse "Forbidden" +// @Failure 404 {object} types.ApiErrorResponse "Not found" +// @Failure 409 {object} types.ApiErrorResponse "Conflict" +// @Failure 429 {object} types.ApiErrorResponse "Too many requests" +// @Failure 500 {object} types.ApiErrorResponse "Internal server error" +// @Failure 503 {object} types.ApiErrorResponse "Service unavailable" +// @Router / [get] func (h *HandlersApi) RootHandler(w http.ResponseWriter, r *http.Request) { // Debug HTTP if enabled if h.DebugHTTPConfig.EnableHTTP { @@ -71,6 +128,19 @@ func (h *HandlersApi) RootHandler(w http.ResponseWriter, r *http.Request) { } // ErrorHandler - Handle error requests +// @Summary API error response +// @Description Returns a generic API error response. +// @Tags system +// @Produce json +// @Failure 400 {object} types.ApiErrorResponse "Bad request" +// @Failure 401 {object} types.ApiErrorResponse "Unauthorized" +// @Failure 403 {object} types.ApiErrorResponse "Forbidden" +// @Failure 404 {object} types.ApiErrorResponse "Not found" +// @Failure 409 {object} types.ApiErrorResponse "Conflict" +// @Failure 429 {object} types.ApiErrorResponse "Too many requests" +// @Failure 500 {string} string "Internal server error" +// @Failure 503 {object} types.ApiErrorResponse "Service unavailable" +// @Router /error [get] func (h *HandlersApi) ErrorHandler(w http.ResponseWriter, r *http.Request) { // Debug HTTP if enabled if h.DebugHTTPConfig.EnableHTTP { @@ -81,6 +151,19 @@ func (h *HandlersApi) ErrorHandler(w http.ResponseWriter, r *http.Request) { } // ForbiddenHandler - Handle forbidden error requests +// @Summary API forbidden response +// @Description Returns a generic forbidden response. +// @Tags system +// @Produce json +// @Failure 400 {object} types.ApiErrorResponse "Bad request" +// @Failure 401 {object} types.ApiErrorResponse "Unauthorized" +// @Failure 403 {string} string "Forbidden" +// @Failure 404 {object} types.ApiErrorResponse "Not found" +// @Failure 409 {object} types.ApiErrorResponse "Conflict" +// @Failure 429 {object} types.ApiErrorResponse "Too many requests" +// @Failure 500 {object} types.ApiErrorResponse "Internal server error" +// @Failure 503 {object} types.ApiErrorResponse "Service unavailable" +// @Router /forbidden [get] func (h *HandlersApi) ForbiddenHandler(w http.ResponseWriter, r *http.Request) { // Debug HTTP if enabled if h.DebugHTTPConfig.EnableHTTP { diff --git a/cmd/api/handlers/login.go b/cmd/api/handlers/login.go index 93bcc21f..d05464dd 100644 --- a/cmd/api/handlers/login.go +++ b/cmd/api/handlers/login.go @@ -22,6 +22,24 @@ import ( // handler authenticates the user and issues a token without an // env-scoped permission check — per-request authorization on // every subsequent endpoint enforces env access anyway. +// @Summary Log in +// @Description Authenticates an API user and returns a JWT token. +// @Tags auth +// @Accept json +// @Produce json +// @Param env path string false "Environment name or UUID" +// @Param request body types.ApiLoginRequest true "Request body" +// @Success 200 {object} types.ApiLoginResponse +// @Failure 400 {object} types.ApiErrorResponse "Bad request" +// @Failure 401 {object} types.ApiErrorResponse "Unauthorized" +// @Failure 403 {object} types.ApiErrorResponse "Forbidden" +// @Failure 404 {object} types.ApiErrorResponse "Not found" +// @Failure 409 {object} types.ApiErrorResponse "Conflict" +// @Failure 429 {object} types.ApiErrorResponse "Too many requests" +// @Failure 500 {object} types.ApiErrorResponse "Internal server error" +// @Failure 503 {object} types.ApiErrorResponse "Service unavailable" +// @Router /api/v1/login [post] +// @Router /api/v1/login/{env} [post] func (h *HandlersApi) LoginHandler(w http.ResponseWriter, r *http.Request) { // Debug HTTP if enabled, never log the body for login if h.DebugHTTPConfig.EnableHTTP { diff --git a/cmd/api/handlers/login_envs.go b/cmd/api/handlers/login_envs.go index b1b5c729..af1c874c 100644 --- a/cmd/api/handlers/login_envs.go +++ b/cmd/api/handlers/login_envs.go @@ -23,6 +23,20 @@ import ( // Like POST /login/{env}, this lives behind the per-IP rate limit registered // in main.go so the endpoint can't be turned into an env-enumeration oracle // for brute-force prep beyond the limit. +// @Summary List login environments +// @Description Returns pre-auth environment choices for the login UI. +// @Tags auth +// @Produce json +// @Success 200 {array} types.LoginEnvironment +// @Failure 400 {object} types.ApiErrorResponse "Bad request" +// @Failure 401 {object} types.ApiErrorResponse "Unauthorized" +// @Failure 403 {object} types.ApiErrorResponse "Forbidden" +// @Failure 404 {object} types.ApiErrorResponse "Not found" +// @Failure 409 {object} types.ApiErrorResponse "Conflict" +// @Failure 429 {object} types.ApiErrorResponse "Too many requests" +// @Failure 500 {object} types.ApiErrorResponse "Internal server error" +// @Failure 503 {object} types.ApiErrorResponse "Service unavailable" +// @Router /api/v1/login/environments [get] func (h *HandlersApi) LoginEnvironmentsHandler(w http.ResponseWriter, r *http.Request) { if h.DebugHTTPConfig.EnableHTTP { utils.DebugHTTPDump(h.DebugHTTP, r, h.DebugHTTPConfig.ShowBody) diff --git a/cmd/api/handlers/logs.go b/cmd/api/handlers/logs.go index 8f71250b..ff6291ab 100644 --- a/cmd/api/handlers/logs.go +++ b/cmd/api/handlers/logs.go @@ -35,6 +35,27 @@ type NodeLogsResponse struct { // // since: RFC3339 timestamp; entries strictly after this point only // limit: 1..1000 (default 100) +// +// @Summary Get node logs +// @Description Returns recent status or result logs for a node. +// @Tags logs +// @Produce json +// @Param env path string true "Environment name or UUID" +// @Param type path string true "Log type: status or result" +// @Param uuid path string true "Node UUID" +// @Param limit query int false "Maximum number of log rows" +// @Param since query string false "RFC3339 lower bound" +// @Success 200 {object} NodeLogsResponse +// @Failure 400 {object} types.ApiErrorResponse "Bad request" +// @Failure 401 {object} types.ApiErrorResponse "Unauthorized" +// @Failure 403 {object} types.ApiErrorResponse "Forbidden" +// @Failure 404 {object} types.ApiErrorResponse "Not found" +// @Failure 409 {object} types.ApiErrorResponse "Conflict" +// @Failure 429 {object} types.ApiErrorResponse "Too many requests" +// @Failure 500 {object} types.ApiErrorResponse "Internal server error" +// @Failure 503 {object} types.ApiErrorResponse "Service unavailable" +// @Security ApiKeyAuth +// @Router /api/v1/logs/{type}/{env}/{uuid} [get] func (h *HandlersApi) NodeLogsHandler(w http.ResponseWriter, r *http.Request) { // Debug HTTP if enabled if h.DebugHTTPConfig.EnableHTTP { diff --git a/cmd/api/handlers/nodes.go b/cmd/api/handlers/nodes.go index c37df832..5cd36c1b 100644 --- a/cmd/api/handlers/nodes.go +++ b/cmd/api/handlers/nodes.go @@ -16,6 +16,23 @@ import ( ) // NodeHandler - GET Handler for single JSON nodes +// @Summary Get node +// @Description Returns a single enrolled node in an environment. +// @Tags nodes +// @Produce json +// @Param env path string true "Environment name or UUID" +// @Param node path string true "Node UUID, hostname, or local name" +// @Success 200 {object} types.NodeView +// @Failure 400 {object} types.ApiErrorResponse "Bad request" +// @Failure 401 {object} types.ApiErrorResponse "Unauthorized" +// @Failure 403 {object} types.ApiErrorResponse "Forbidden" +// @Failure 404 {object} types.ApiErrorResponse "Not found" +// @Failure 409 {object} types.ApiErrorResponse "Conflict" +// @Failure 429 {object} types.ApiErrorResponse "Too many requests" +// @Failure 500 {object} types.ApiErrorResponse "Internal server error" +// @Failure 503 {object} types.ApiErrorResponse "Service unavailable" +// @Security ApiKeyAuth +// @Router /api/v1/nodes/{env}/node/{node} [get] func (h *HandlersApi) NodeHandler(w http.ResponseWriter, r *http.Request) { // Debug HTTP if enabled if h.DebugHTTPConfig.EnableHTTP { @@ -65,6 +82,22 @@ func (h *HandlersApi) NodeHandler(w http.ResponseWriter, r *http.Request) { } // ActiveNodesHandler - GET Handler for active JSON nodes +// @Summary List active nodes +// @Description Returns active enrolled nodes for an environment. +// @Tags nodes +// @Produce json +// @Param env path string true "Environment name or UUID" +// @Success 200 {array} nodes.OsqueryNode +// @Failure 400 {object} types.ApiErrorResponse "Bad request" +// @Failure 401 {object} types.ApiErrorResponse "Unauthorized" +// @Failure 403 {object} types.ApiErrorResponse "Forbidden" +// @Failure 404 {object} types.ApiErrorResponse "Not found" +// @Failure 409 {object} types.ApiErrorResponse "Conflict" +// @Failure 429 {object} types.ApiErrorResponse "Too many requests" +// @Failure 500 {object} types.ApiErrorResponse "Internal server error" +// @Failure 503 {object} types.ApiErrorResponse "Service unavailable" +// @Security ApiKeyAuth +// @Router /api/v1/nodes/{env}/active [get] func (h *HandlersApi) ActiveNodesHandler(w http.ResponseWriter, r *http.Request) { // Debug HTTP if enabled if h.DebugHTTPConfig.EnableHTTP { @@ -106,6 +139,22 @@ func (h *HandlersApi) ActiveNodesHandler(w http.ResponseWriter, r *http.Request) } // InactiveNodesHandler - GET Handler for inactive JSON nodes +// @Summary List inactive nodes +// @Description Returns inactive enrolled nodes for an environment. +// @Tags nodes +// @Produce json +// @Param env path string true "Environment name or UUID" +// @Success 200 {array} nodes.OsqueryNode +// @Failure 400 {object} types.ApiErrorResponse "Bad request" +// @Failure 401 {object} types.ApiErrorResponse "Unauthorized" +// @Failure 403 {object} types.ApiErrorResponse "Forbidden" +// @Failure 404 {object} types.ApiErrorResponse "Not found" +// @Failure 409 {object} types.ApiErrorResponse "Conflict" +// @Failure 429 {object} types.ApiErrorResponse "Too many requests" +// @Failure 500 {object} types.ApiErrorResponse "Internal server error" +// @Failure 503 {object} types.ApiErrorResponse "Service unavailable" +// @Security ApiKeyAuth +// @Router /api/v1/nodes/{env}/inactive [get] func (h *HandlersApi) InactiveNodesHandler(w http.ResponseWriter, r *http.Request) { // Debug HTTP if enabled if h.DebugHTTPConfig.EnableHTTP { @@ -147,6 +196,22 @@ func (h *HandlersApi) InactiveNodesHandler(w http.ResponseWriter, r *http.Reques } // AllNodesHandler - GET Handler for all JSON nodes +// @Summary List all nodes +// @Description Returns all enrolled nodes for an environment. +// @Tags nodes +// @Produce json +// @Param env path string true "Environment name or UUID" +// @Success 200 {array} nodes.OsqueryNode +// @Failure 400 {object} types.ApiErrorResponse "Bad request" +// @Failure 401 {object} types.ApiErrorResponse "Unauthorized" +// @Failure 403 {object} types.ApiErrorResponse "Forbidden" +// @Failure 404 {object} types.ApiErrorResponse "Not found" +// @Failure 409 {object} types.ApiErrorResponse "Conflict" +// @Failure 429 {object} types.ApiErrorResponse "Too many requests" +// @Failure 500 {object} types.ApiErrorResponse "Internal server error" +// @Failure 503 {object} types.ApiErrorResponse "Service unavailable" +// @Security ApiKeyAuth +// @Router /api/v1/nodes/{env}/all [get] func (h *HandlersApi) AllNodesHandler(w http.ResponseWriter, r *http.Request) { // Debug HTTP if enabled if h.DebugHTTPConfig.EnableHTTP { @@ -187,6 +252,24 @@ func (h *HandlersApi) AllNodesHandler(w http.ResponseWriter, r *http.Request) { } // DeleteNodeHandler - POST Handler to delete single node +// @Summary Delete node +// @Description Deletes a node from an environment. +// @Tags nodes +// @Accept json +// @Produce json +// @Param env path string true "Environment name or UUID" +// @Param request body types.ApiNodeGenericRequest true "Request body" +// @Success 200 {object} types.ApiGenericResponse +// @Failure 400 {object} types.ApiErrorResponse "Bad request" +// @Failure 401 {object} types.ApiErrorResponse "Unauthorized" +// @Failure 403 {object} types.ApiErrorResponse "Forbidden" +// @Failure 404 {object} types.ApiErrorResponse "Not found" +// @Failure 409 {object} types.ApiErrorResponse "Conflict" +// @Failure 429 {object} types.ApiErrorResponse "Too many requests" +// @Failure 500 {object} types.ApiErrorResponse "Internal server error" +// @Failure 503 {object} types.ApiErrorResponse "Service unavailable" +// @Security ApiKeyAuth +// @Router /api/v1/nodes/{env}/delete [post] func (h *HandlersApi) DeleteNodeHandler(w http.ResponseWriter, r *http.Request) { // Debug HTTP if enabled if h.DebugHTTPConfig.EnableHTTP { @@ -235,6 +318,24 @@ func (h *HandlersApi) DeleteNodeHandler(w http.ResponseWriter, r *http.Request) } // TagNodeHandler - POST Handler to tag a node +// @Summary Tag node +// @Description Adds or updates a tag on a node. +// @Tags nodes +// @Accept json +// @Produce json +// @Param env path string true "Environment name or UUID" +// @Param request body types.ApiNodeTagRequest true "Request body" +// @Success 200 {object} types.ApiGenericResponse +// @Failure 400 {object} types.ApiErrorResponse "Bad request" +// @Failure 401 {object} types.ApiErrorResponse "Unauthorized" +// @Failure 403 {object} types.ApiErrorResponse "Forbidden" +// @Failure 404 {object} types.ApiErrorResponse "Not found" +// @Failure 409 {object} types.ApiErrorResponse "Conflict" +// @Failure 429 {object} types.ApiErrorResponse "Too many requests" +// @Failure 500 {object} types.ApiErrorResponse "Internal server error" +// @Failure 503 {object} types.ApiErrorResponse "Service unavailable" +// @Security ApiKeyAuth +// @Router /api/v1/nodes/{env}/tag [post] func (h *HandlersApi) TagNodeHandler(w http.ResponseWriter, r *http.Request) { // Debug HTTP if enabled if h.DebugHTTPConfig.EnableHTTP { @@ -289,6 +390,23 @@ func (h *HandlersApi) TagNodeHandler(w http.ResponseWriter, r *http.Request) { } // LookupNodeHandler - POST Handler to lookup a node by identifier +// @Summary Lookup node +// @Description Looks up a node by UUID, hostname, or local name. +// @Tags nodes +// @Accept json +// @Produce json +// @Param request body types.ApiLookupRequest true "Request body" +// @Success 200 {object} nodes.OsqueryNode +// @Failure 400 {object} types.ApiErrorResponse "Bad request" +// @Failure 401 {object} types.ApiErrorResponse "Unauthorized" +// @Failure 403 {object} types.ApiErrorResponse "Forbidden" +// @Failure 404 {object} types.ApiErrorResponse "Not found" +// @Failure 409 {object} types.ApiErrorResponse "Conflict" +// @Failure 429 {object} types.ApiErrorResponse "Too many requests" +// @Failure 500 {object} types.ApiErrorResponse "Internal server error" +// @Failure 503 {object} types.ApiErrorResponse "Service unavailable" +// @Security ApiKeyAuth +// @Router /api/v1/nodes/lookup [post] func (h *HandlersApi) LookupNodeHandler(w http.ResponseWriter, r *http.Request) { // Debug HTTP if enabled if h.DebugHTTPConfig.EnableHTTP { @@ -337,6 +455,30 @@ func (h *HandlersApi) LookupNodeHandler(w http.ResponseWriter, r *http.Request) // dir: "asc" | "desc" (default "desc" for lastseen, "asc" otherwise) // page: 1-indexed page number (default 1) // page_size: 1..500 (default 50) +// +// @Summary List paginated nodes +// @Description Returns paginated, filtered, and sorted nodes for an environment. +// @Tags nodes +// @Produce json +// @Param env path string true "Environment name or UUID" +// @Param page query int false "Page number" +// @Param page_size query int false "Page size" +// @Param q query string false "Search query" +// @Param status query string false "Node status filter" +// @Param platform query string false "Platform filter" +// @Param sort query string false "Sort field" +// @Param order query string false "Sort order" +// @Success 200 {object} types.NodesPagedResponse +// @Failure 400 {object} types.ApiErrorResponse "Bad request" +// @Failure 401 {object} types.ApiErrorResponse "Unauthorized" +// @Failure 403 {object} types.ApiErrorResponse "Forbidden" +// @Failure 404 {object} types.ApiErrorResponse "Not found" +// @Failure 409 {object} types.ApiErrorResponse "Conflict" +// @Failure 429 {object} types.ApiErrorResponse "Too many requests" +// @Failure 500 {object} types.ApiErrorResponse "Internal server error" +// @Failure 503 {object} types.ApiErrorResponse "Service unavailable" +// @Security ApiKeyAuth +// @Router /api/v1/nodes/{env} [get] func (h *HandlersApi) NodesPagedHandler(w http.ResponseWriter, r *http.Request) { // Debug HTTP if enabled if h.DebugHTTPConfig.EnableHTTP { diff --git a/cmd/api/handlers/platforms.go b/cmd/api/handlers/platforms.go index b28dd7a0..a60b75d3 100644 --- a/cmd/api/handlers/platforms.go +++ b/cmd/api/handlers/platforms.go @@ -36,6 +36,22 @@ func (h *HandlersApi) PlatformsHandler(w http.ResponseWriter, r *http.Request) { } // PlatformsEnvHandler - GET Handler to return platforms for one environment as JSON +// @Summary List platforms +// @Description Returns platform counts for an environment. +// @Tags platforms +// @Produce json +// @Param env path string true "Environment name or UUID" +// @Success 200 {array} nodes.PlatformCounts +// @Failure 400 {object} types.ApiErrorResponse "Bad request" +// @Failure 401 {object} types.ApiErrorResponse "Unauthorized" +// @Failure 403 {object} types.ApiErrorResponse "Forbidden" +// @Failure 404 {object} types.ApiErrorResponse "Not found" +// @Failure 409 {object} types.ApiErrorResponse "Conflict" +// @Failure 429 {object} types.ApiErrorResponse "Too many requests" +// @Failure 500 {object} types.ApiErrorResponse "Internal server error" +// @Failure 503 {object} types.ApiErrorResponse "Service unavailable" +// @Security ApiKeyAuth +// @Router /api/v1/platforms/{env} [get] func (h *HandlersApi) PlatformsEnvHandler(w http.ResponseWriter, r *http.Request) { // Debug HTTP if enabled if h.DebugHTTPConfig.EnableHTTP { diff --git a/cmd/api/handlers/queries.go b/cmd/api/handlers/queries.go index bf52bda2..a98b9b7e 100644 --- a/cmd/api/handlers/queries.go +++ b/cmd/api/handlers/queries.go @@ -36,6 +36,23 @@ var QueryTargets = map[string]bool{ } // QueryShowHandler - GET Handler to return a single query in JSON +// @Summary Get query +// @Description Returns a single on-demand query. +// @Tags queries +// @Produce json +// @Param env path string true "Environment name or UUID" +// @Param name path string true "Query name" +// @Success 200 {object} queries.DistributedQuery +// @Failure 400 {object} types.ApiErrorResponse "Bad request" +// @Failure 401 {object} types.ApiErrorResponse "Unauthorized" +// @Failure 403 {object} types.ApiErrorResponse "Forbidden" +// @Failure 404 {object} types.ApiErrorResponse "Not found" +// @Failure 409 {object} types.ApiErrorResponse "Conflict" +// @Failure 429 {object} types.ApiErrorResponse "Too many requests" +// @Failure 500 {object} types.ApiErrorResponse "Internal server error" +// @Failure 503 {object} types.ApiErrorResponse "Service unavailable" +// @Security ApiKeyAuth +// @Router /api/v1/queries/{env}/{name} [get] func (h *HandlersApi) QueryShowHandler(w http.ResponseWriter, r *http.Request) { // Debug HTTP if enabled if h.DebugHTTPConfig.EnableHTTP { @@ -82,6 +99,24 @@ func (h *HandlersApi) QueryShowHandler(w http.ResponseWriter, r *http.Request) { } // QueriesRunHandler - POST Handler to run a query +// @Summary Run query +// @Description Starts a new distributed query. +// @Tags queries +// @Accept json +// @Produce json +// @Param env path string true "Environment name or UUID" +// @Param request body types.ApiDistributedQueryRequest true "Request body" +// @Success 200 {object} types.ApiQueriesResponse +// @Failure 400 {object} types.ApiErrorResponse "Bad request" +// @Failure 401 {object} types.ApiErrorResponse "Unauthorized" +// @Failure 403 {object} types.ApiErrorResponse "Forbidden" +// @Failure 404 {object} types.ApiErrorResponse "Not found" +// @Failure 409 {object} types.ApiErrorResponse "Conflict" +// @Failure 429 {object} types.ApiErrorResponse "Too many requests" +// @Failure 500 {object} types.ApiErrorResponse "Internal server error" +// @Failure 503 {object} types.ApiErrorResponse "Service unavailable" +// @Security ApiKeyAuth +// @Router /api/v1/queries/{env} [post] func (h *HandlersApi) QueriesRunHandler(w http.ResponseWriter, r *http.Request) { // Debug HTTP if enabled if h.DebugHTTPConfig.EnableHTTP { @@ -190,6 +225,25 @@ func (h *HandlersApi) QueriesRunHandler(w http.ResponseWriter, r *http.Request) } // QueriesActionHandler - POST Handler to delete/expire a query +// @Summary Execute query action +// @Description Deletes, expires, or otherwise acts on an on-demand query. +// @Tags queries +// @Accept json +// @Produce json +// @Param env path string true "Environment name or UUID" +// @Param action path string true "Query action" +// @Param name path string true "Query name" +// @Success 200 {object} types.ApiGenericResponse +// @Failure 400 {object} types.ApiErrorResponse "Bad request" +// @Failure 401 {object} types.ApiErrorResponse "Unauthorized" +// @Failure 403 {object} types.ApiErrorResponse "Forbidden" +// @Failure 404 {object} types.ApiErrorResponse "Not found" +// @Failure 409 {object} types.ApiErrorResponse "Conflict" +// @Failure 429 {object} types.ApiErrorResponse "Too many requests" +// @Failure 500 {object} types.ApiErrorResponse "Internal server error" +// @Failure 503 {object} types.ApiErrorResponse "Service unavailable" +// @Security ApiKeyAuth +// @Router /api/v1/queries/{env}/{action}/{name} [post] func (h *HandlersApi) QueriesActionHandler(w http.ResponseWriter, r *http.Request) { // Debug HTTP if enabled if h.DebugHTTPConfig.EnableHTTP { @@ -258,6 +312,23 @@ func (h *HandlersApi) QueriesActionHandler(w http.ResponseWriter, r *http.Reques } // AllQueriesShowHandler - GET Handler to return all queries in JSON +// @Summary List queries +// @Description Returns on-demand queries for an environment. +// @Tags queries +// @Produce json +// @Param env path string true "Environment name or UUID" +// @Success 200 {array} queries.DistributedQuery +// @Failure 400 {object} types.ApiErrorResponse "Bad request" +// @Failure 401 {object} types.ApiErrorResponse "Unauthorized" +// @Failure 403 {object} types.ApiErrorResponse "Forbidden" +// @Failure 404 {object} types.ApiErrorResponse "Not found" +// @Failure 409 {object} types.ApiErrorResponse "Conflict" +// @Failure 429 {object} types.ApiErrorResponse "Too many requests" +// @Failure 500 {object} types.ApiErrorResponse "Internal server error" +// @Failure 503 {object} types.ApiErrorResponse "Service unavailable" +// @Security ApiKeyAuth +// @Router /api/v1/queries/{env} [get] +// @Router /api/v1/all-queries/{env} [get] func (h *HandlersApi) AllQueriesShowHandler(w http.ResponseWriter, r *http.Request) { // Debug HTTP if enabled if h.DebugHTTPConfig.EnableHTTP { @@ -300,6 +371,28 @@ func (h *HandlersApi) AllQueriesShowHandler(w http.ResponseWriter, r *http.Reque // QueryListHandler - GET Handler to return queries in JSON by target and environment (paginated) // // Query params: page, page_size, q (free-text search), sort (column key), dir (asc|desc) +// @Summary List paginated queries +// @Description Returns paginated on-demand queries by target and environment. +// @Tags queries +// @Produce json +// @Param env path string true "Environment name or UUID" +// @Param target path string true "Query target filter" +// @Param page query int false "Page number" +// @Param page_size query int false "Page size" +// @Param q query string false "Search query" +// @Param sort query string false "Sort field" +// @Param order query string false "Sort order" +// @Success 200 {object} types.QueriesPagedResponse +// @Failure 400 {object} types.ApiErrorResponse "Bad request" +// @Failure 401 {object} types.ApiErrorResponse "Unauthorized" +// @Failure 403 {object} types.ApiErrorResponse "Forbidden" +// @Failure 404 {object} types.ApiErrorResponse "Not found" +// @Failure 409 {object} types.ApiErrorResponse "Conflict" +// @Failure 429 {object} types.ApiErrorResponse "Too many requests" +// @Failure 500 {object} types.ApiErrorResponse "Internal server error" +// @Failure 503 {object} types.ApiErrorResponse "Service unavailable" +// @Security ApiKeyAuth +// @Router /api/v1/queries/{env}/list/{target} [get] func (h *HandlersApi) QueryListHandler(w http.ResponseWriter, r *http.Request) { // Debug HTTP if enabled if h.DebugHTTPConfig.EnableHTTP { @@ -390,6 +483,26 @@ func (h *HandlersApi) QueryListHandler(w http.ResponseWriter, r *http.Request) { // Params: page, page_size, since (RFC3339 timestamp; unparseable → ignored) // // Empty results are a valid state and return HTTP 200 with items: []. +// @Summary Get query results +// @Description Returns paginated results for an on-demand query. +// @Tags queries +// @Produce json +// @Param env path string true "Environment name or UUID" +// @Param name path string true "Query name" +// @Param page query int false "Page number" +// @Param page_size query int false "Page size" +// @Param since query string false "RFC3339 lower bound" +// @Success 200 {object} types.QueryResultsResponse +// @Failure 400 {object} types.ApiErrorResponse "Bad request" +// @Failure 401 {object} types.ApiErrorResponse "Unauthorized" +// @Failure 403 {object} types.ApiErrorResponse "Forbidden" +// @Failure 404 {object} types.ApiErrorResponse "Not found" +// @Failure 409 {object} types.ApiErrorResponse "Conflict" +// @Failure 429 {object} types.ApiErrorResponse "Too many requests" +// @Failure 500 {object} types.ApiErrorResponse "Internal server error" +// @Failure 503 {object} types.ApiErrorResponse "Service unavailable" +// @Security ApiKeyAuth +// @Router /api/v1/queries/{env}/results/{name} [get] func (h *HandlersApi) QueryResultsHandler(w http.ResponseWriter, r *http.Request) { // Debug HTTP if enabled if h.DebugHTTPConfig.EnableHTTP { @@ -482,6 +595,23 @@ func (h *HandlersApi) QueryResultsHandler(w http.ResponseWriter, r *http.Request // (The `.csv` lives as a literal path segment before `{name}` because Go's // ServeMux grammar requires wildcards to end at `/` or end-of-pattern, so // `{name}.csv` is a parse error at registration time.) +// @Summary Export query results CSV +// @Description Streams query results as CSV. +// @Tags queries +// @Produce text/csv +// @Param env path string true "Environment name or UUID" +// @Param name path string true "Query name" +// @Success 200 {string} string +// @Failure 400 {object} types.ApiErrorResponse "Bad request" +// @Failure 401 {object} types.ApiErrorResponse "Unauthorized" +// @Failure 403 {object} types.ApiErrorResponse "Forbidden" +// @Failure 404 {object} types.ApiErrorResponse "Not found" +// @Failure 409 {object} types.ApiErrorResponse "Conflict" +// @Failure 429 {object} types.ApiErrorResponse "Too many requests" +// @Failure 500 {object} types.ApiErrorResponse "Internal server error" +// @Failure 503 {object} types.ApiErrorResponse "Service unavailable" +// @Security ApiKeyAuth +// @Router /api/v1/queries/{env}/results/csv/{name} [get] func (h *HandlersApi) QueryResultsCSVHandler(w http.ResponseWriter, r *http.Request) { // Debug HTTP if enabled if h.DebugHTTPConfig.EnableHTTP { @@ -591,6 +721,21 @@ func (h *HandlersApi) QueryResultsCSVHandler(w http.ResponseWriter, r *http.Requ // Path: /api/v1/osquery/tables // The schema is global (not env-scoped). Requires any authenticated user. // Responses are cache-able for one hour since the schema rarely changes. +// @Summary List osquery tables +// @Description Returns the osquery schema table metadata known to the API. +// @Tags osquery +// @Produce json +// @Success 200 {array} types.OsqueryTable +// @Failure 400 {object} types.ApiErrorResponse "Bad request" +// @Failure 401 {object} types.ApiErrorResponse "Unauthorized" +// @Failure 403 {object} types.ApiErrorResponse "Forbidden" +// @Failure 404 {object} types.ApiErrorResponse "Not found" +// @Failure 409 {object} types.ApiErrorResponse "Conflict" +// @Failure 429 {object} types.ApiErrorResponse "Too many requests" +// @Failure 500 {object} types.ApiErrorResponse "Internal server error" +// @Failure 503 {object} types.ApiErrorResponse "Service unavailable" +// @Security ApiKeyAuth +// @Router /api/v1/osquery/tables [get] func (h *HandlersApi) OsqueryTablesHandler(w http.ResponseWriter, r *http.Request) { // Debug HTTP if enabled if h.DebugHTTPConfig.EnableHTTP { diff --git a/cmd/api/handlers/samples.go b/cmd/api/handlers/samples.go index 88ec0d42..9605eab0 100644 --- a/cmd/api/handlers/samples.go +++ b/cmd/api/handlers/samples.go @@ -19,6 +19,21 @@ import ( // starter pack to anonymous callers — neither of which the only // consumer (the post-login queries/new form) requires at pre-auth time. // Moved behind handlerAuthCheck in cmd/api/main.go. +// @Summary List query samples +// @Description Returns sample query templates. +// @Tags queries +// @Produce json +// @Success 200 {array} queries.QuerySample +// @Failure 400 {object} types.ApiErrorResponse "Bad request" +// @Failure 401 {object} types.ApiErrorResponse "Unauthorized" +// @Failure 403 {object} types.ApiErrorResponse "Forbidden" +// @Failure 404 {object} types.ApiErrorResponse "Not found" +// @Failure 409 {object} types.ApiErrorResponse "Conflict" +// @Failure 429 {object} types.ApiErrorResponse "Too many requests" +// @Failure 500 {object} types.ApiErrorResponse "Internal server error" +// @Failure 503 {object} types.ApiErrorResponse "Service unavailable" +// @Security ApiKeyAuth +// @Router /api/v1/queries/samples [get] func (h *HandlersApi) QuerySamplesHandler(w http.ResponseWriter, r *http.Request) { if h.DebugHTTPConfig.EnableHTTP { utils.DebugHTTPDump(h.DebugHTTP, r, h.DebugHTTPConfig.ShowBody) @@ -33,6 +48,21 @@ func (h *HandlersApi) QuerySamplesHandler(w http.ResponseWriter, r *http.Request // as QuerySamplesHandler. The path list is the set of high-value // exfiltration locations osctrl is provisioned to carve; surfacing it // to anonymous callers was a free recon gift to attackers. +// @Summary List carve samples +// @Description Returns sample carve path templates. +// @Tags carves +// @Produce json +// @Success 200 {array} carves.CarveSample +// @Failure 400 {object} types.ApiErrorResponse "Bad request" +// @Failure 401 {object} types.ApiErrorResponse "Unauthorized" +// @Failure 403 {object} types.ApiErrorResponse "Forbidden" +// @Failure 404 {object} types.ApiErrorResponse "Not found" +// @Failure 409 {object} types.ApiErrorResponse "Conflict" +// @Failure 429 {object} types.ApiErrorResponse "Too many requests" +// @Failure 500 {object} types.ApiErrorResponse "Internal server error" +// @Failure 503 {object} types.ApiErrorResponse "Service unavailable" +// @Security ApiKeyAuth +// @Router /api/v1/carves/samples [get] func (h *HandlersApi) CarveSamplesHandler(w http.ResponseWriter, r *http.Request) { if h.DebugHTTPConfig.EnableHTTP { utils.DebugHTTPDump(h.DebugHTTP, r, h.DebugHTTPConfig.ShowBody) diff --git a/cmd/api/handlers/saved_queries.go b/cmd/api/handlers/saved_queries.go index bdd6c72a..d0f22159 100644 --- a/cmd/api/handlers/saved_queries.go +++ b/cmd/api/handlers/saved_queries.go @@ -36,6 +36,25 @@ func savedQueryView(s queries.SavedQuery) types.SavedQueryView { // // Paginated, sorted, searchable list of saved queries for an environment. // Query params: page, page_size, q (free-text), sort (column key), dir (asc|desc). +// @Summary List saved queries +// @Description Returns paginated saved queries for an environment. +// @Tags saved-queries +// @Produce json +// @Param env path string true "Environment name or UUID" +// @Param page query int false "Page number" +// @Param page_size query int false "Page size" +// @Param q query string false "Search query" +// @Success 200 {object} types.SavedQueriesPagedResponse +// @Failure 400 {object} types.ApiErrorResponse "Bad request" +// @Failure 401 {object} types.ApiErrorResponse "Unauthorized" +// @Failure 403 {object} types.ApiErrorResponse "Forbidden" +// @Failure 404 {object} types.ApiErrorResponse "Not found" +// @Failure 409 {object} types.ApiErrorResponse "Conflict" +// @Failure 429 {object} types.ApiErrorResponse "Too many requests" +// @Failure 500 {object} types.ApiErrorResponse "Internal server error" +// @Failure 503 {object} types.ApiErrorResponse "Service unavailable" +// @Security ApiKeyAuth +// @Router /api/v1/saved-queries/{env} [get] func (h *HandlersApi) SavedQueriesListHandler(w http.ResponseWriter, r *http.Request) { if h.DebugHTTPConfig.EnableHTTP { utils.DebugHTTPDump(h.DebugHTTP, r, h.DebugHTTPConfig.ShowBody) @@ -101,6 +120,24 @@ func (h *HandlersApi) SavedQueriesListHandler(w http.ResponseWriter, r *http.Req // // Body: { "name": string, "query": string }. Returns 201 with the created view, // 409 if a saved query with that name already exists in the environment. +// @Summary Create saved query +// @Description Creates a saved query in an environment. +// @Tags saved-queries +// @Accept json +// @Produce json +// @Param env path string true "Environment name or UUID" +// @Param request body types.SavedQueryCreateRequest true "Request body" +// @Success 200 {object} types.SavedQueryView +// @Failure 400 {object} types.ApiErrorResponse "Bad request" +// @Failure 401 {object} types.ApiErrorResponse "Unauthorized" +// @Failure 403 {object} types.ApiErrorResponse "Forbidden" +// @Failure 404 {object} types.ApiErrorResponse "Not found" +// @Failure 409 {object} types.ApiErrorResponse "Conflict" +// @Failure 429 {object} types.ApiErrorResponse "Too many requests" +// @Failure 500 {object} types.ApiErrorResponse "Internal server error" +// @Failure 503 {object} types.ApiErrorResponse "Service unavailable" +// @Security ApiKeyAuth +// @Router /api/v1/saved-queries/{env} [post] func (h *HandlersApi) SavedQueryCreateHandler(w http.ResponseWriter, r *http.Request) { if h.DebugHTTPConfig.EnableHTTP { utils.DebugHTTPDump(h.DebugHTTP, r, h.DebugHTTPConfig.ShowBody) @@ -171,6 +208,25 @@ func (h *HandlersApi) SavedQueryCreateHandler(w http.ResponseWriter, r *http.Req // // Body: { "query": string }. Updates the SQL body only; the original creator // is preserved. Returns the updated view. +// @Summary Update saved query +// @Description Updates a saved query in an environment. +// @Tags saved-queries +// @Accept json +// @Produce json +// @Param env path string true "Environment name or UUID" +// @Param name path string true "Saved query name" +// @Param request body types.SavedQueryUpdateRequest true "Request body" +// @Success 200 {object} types.SavedQueryView +// @Failure 400 {object} types.ApiErrorResponse "Bad request" +// @Failure 401 {object} types.ApiErrorResponse "Unauthorized" +// @Failure 403 {object} types.ApiErrorResponse "Forbidden" +// @Failure 404 {object} types.ApiErrorResponse "Not found" +// @Failure 409 {object} types.ApiErrorResponse "Conflict" +// @Failure 429 {object} types.ApiErrorResponse "Too many requests" +// @Failure 500 {object} types.ApiErrorResponse "Internal server error" +// @Failure 503 {object} types.ApiErrorResponse "Service unavailable" +// @Security ApiKeyAuth +// @Router /api/v1/saved-queries/{env}/{name} [patch] func (h *HandlersApi) SavedQueryUpdateHandler(w http.ResponseWriter, r *http.Request) { if h.DebugHTTPConfig.EnableHTTP { utils.DebugHTTPDump(h.DebugHTTP, r, h.DebugHTTPConfig.ShowBody) @@ -222,6 +278,23 @@ func (h *HandlersApi) SavedQueryUpdateHandler(w http.ResponseWriter, r *http.Req } // SavedQueryDeleteHandler - DELETE /api/v1/saved-queries/{env}/{name} +// @Summary Delete saved query +// @Description Deletes a saved query in an environment. +// @Tags saved-queries +// @Produce json +// @Param env path string true "Environment name or UUID" +// @Param name path string true "Saved query name" +// @Success 200 {object} types.ApiGenericResponse +// @Failure 400 {object} types.ApiErrorResponse "Bad request" +// @Failure 401 {object} types.ApiErrorResponse "Unauthorized" +// @Failure 403 {object} types.ApiErrorResponse "Forbidden" +// @Failure 404 {object} types.ApiErrorResponse "Not found" +// @Failure 409 {object} types.ApiErrorResponse "Conflict" +// @Failure 429 {object} types.ApiErrorResponse "Too many requests" +// @Failure 500 {object} types.ApiErrorResponse "Internal server error" +// @Failure 503 {object} types.ApiErrorResponse "Service unavailable" +// @Security ApiKeyAuth +// @Router /api/v1/saved-queries/{env}/{name} [delete] func (h *HandlersApi) SavedQueryDeleteHandler(w http.ResponseWriter, r *http.Request) { if h.DebugHTTPConfig.EnableHTTP { utils.DebugHTTPDump(h.DebugHTTP, r, h.DebugHTTPConfig.ShowBody) diff --git a/cmd/api/handlers/settings.go b/cmd/api/handlers/settings.go index f2baa8f0..c070d7e7 100644 --- a/cmd/api/handlers/settings.go +++ b/cmd/api/handlers/settings.go @@ -13,6 +13,21 @@ import ( ) // SettingsHandler - GET Handler for all settings including JSON +// @Summary List settings +// @Description Returns settings for all services. +// @Tags settings +// @Produce json +// @Success 200 {array} settings.SettingValue +// @Failure 400 {object} types.ApiErrorResponse "Bad request" +// @Failure 401 {object} types.ApiErrorResponse "Unauthorized" +// @Failure 403 {object} types.ApiErrorResponse "Forbidden" +// @Failure 404 {object} types.ApiErrorResponse "Not found" +// @Failure 409 {object} types.ApiErrorResponse "Conflict" +// @Failure 429 {object} types.ApiErrorResponse "Too many requests" +// @Failure 500 {object} types.ApiErrorResponse "Internal server error" +// @Failure 503 {object} types.ApiErrorResponse "Service unavailable" +// @Security ApiKeyAuth +// @Router /api/v1/settings [get] func (h *HandlersApi) SettingsHandler(w http.ResponseWriter, r *http.Request) { // Debug HTTP if enabled if h.DebugHTTPConfig.EnableHTTP { @@ -37,6 +52,22 @@ func (h *HandlersApi) SettingsHandler(w http.ResponseWriter, r *http.Request) { } // SettingsServiceHandler - GET Handler for service specific settings excluding JSON +// @Summary List service settings +// @Description Returns settings for a service. +// @Tags settings +// @Produce json +// @Param service path string true "Service name" +// @Success 200 {array} settings.SettingValue +// @Failure 400 {object} types.ApiErrorResponse "Bad request" +// @Failure 401 {object} types.ApiErrorResponse "Unauthorized" +// @Failure 403 {object} types.ApiErrorResponse "Forbidden" +// @Failure 404 {object} types.ApiErrorResponse "Not found" +// @Failure 409 {object} types.ApiErrorResponse "Conflict" +// @Failure 429 {object} types.ApiErrorResponse "Too many requests" +// @Failure 500 {object} types.ApiErrorResponse "Internal server error" +// @Failure 503 {object} types.ApiErrorResponse "Service unavailable" +// @Security ApiKeyAuth +// @Router /api/v1/settings/{service} [get] func (h *HandlersApi) SettingsServiceHandler(w http.ResponseWriter, r *http.Request) { // Debug HTTP if enabled if h.DebugHTTPConfig.EnableHTTP { @@ -72,6 +103,23 @@ func (h *HandlersApi) SettingsServiceHandler(w http.ResponseWriter, r *http.Requ } // SettingsServiceEnvHandler - GET Handler for service and environment specific settings excluding JSON +// @Summary List service environment settings +// @Description Returns settings for a service and environment. +// @Tags settings +// @Produce json +// @Param service path string true "Service name" +// @Param env path string true "Environment name or UUID" +// @Success 200 {array} settings.SettingValue +// @Failure 400 {object} types.ApiErrorResponse "Bad request" +// @Failure 401 {object} types.ApiErrorResponse "Unauthorized" +// @Failure 403 {object} types.ApiErrorResponse "Forbidden" +// @Failure 404 {object} types.ApiErrorResponse "Not found" +// @Failure 409 {object} types.ApiErrorResponse "Conflict" +// @Failure 429 {object} types.ApiErrorResponse "Too many requests" +// @Failure 500 {object} types.ApiErrorResponse "Internal server error" +// @Failure 503 {object} types.ApiErrorResponse "Service unavailable" +// @Security ApiKeyAuth +// @Router /api/v1/settings/{service}/{env} [get] func (h *HandlersApi) SettingsServiceEnvHandler(w http.ResponseWriter, r *http.Request) { // Debug HTTP if enabled if h.DebugHTTPConfig.EnableHTTP { @@ -126,6 +174,22 @@ func (h *HandlersApi) SettingsServiceEnvHandler(w http.ResponseWriter, r *http.R } // SettingsServiceJSONHandler - GET Handler for service specific settings including JSON +// @Summary List service JSON settings +// @Description Returns JSON settings for a service. +// @Tags settings +// @Produce json +// @Param service path string true "Service name" +// @Success 200 {array} settings.SettingValue +// @Failure 400 {object} types.ApiErrorResponse "Bad request" +// @Failure 401 {object} types.ApiErrorResponse "Unauthorized" +// @Failure 403 {object} types.ApiErrorResponse "Forbidden" +// @Failure 404 {object} types.ApiErrorResponse "Not found" +// @Failure 409 {object} types.ApiErrorResponse "Conflict" +// @Failure 429 {object} types.ApiErrorResponse "Too many requests" +// @Failure 500 {object} types.ApiErrorResponse "Internal server error" +// @Failure 503 {object} types.ApiErrorResponse "Service unavailable" +// @Security ApiKeyAuth +// @Router /api/v1/settings/{service}/json [get] func (h *HandlersApi) SettingsServiceJSONHandler(w http.ResponseWriter, r *http.Request) { // Debug HTTP if enabled if h.DebugHTTPConfig.EnableHTTP { @@ -161,6 +225,23 @@ func (h *HandlersApi) SettingsServiceJSONHandler(w http.ResponseWriter, r *http. } // GET Handler for service and environment specific settings including JSON +// @Summary List service environment JSON settings +// @Description Returns JSON settings for a service and environment. +// @Tags settings +// @Produce json +// @Param service path string true "Service name" +// @Param env path string true "Environment name or UUID" +// @Success 200 {array} settings.SettingValue +// @Failure 400 {object} types.ApiErrorResponse "Bad request" +// @Failure 401 {object} types.ApiErrorResponse "Unauthorized" +// @Failure 403 {object} types.ApiErrorResponse "Forbidden" +// @Failure 404 {object} types.ApiErrorResponse "Not found" +// @Failure 409 {object} types.ApiErrorResponse "Conflict" +// @Failure 429 {object} types.ApiErrorResponse "Too many requests" +// @Failure 500 {object} types.ApiErrorResponse "Internal server error" +// @Failure 503 {object} types.ApiErrorResponse "Service unavailable" +// @Security ApiKeyAuth +// @Router /api/v1/settings/{service}/json/{env} [get] func (h *HandlersApi) SettingsServiceEnvJSONHandler(w http.ResponseWriter, r *http.Request) { // Debug HTTP if enabled if h.DebugHTTPConfig.EnableHTTP { diff --git a/cmd/api/handlers/settings_patch.go b/cmd/api/handlers/settings_patch.go index 69336813..9586370b 100644 --- a/cmd/api/handlers/settings_patch.go +++ b/cmd/api/handlers/settings_patch.go @@ -27,6 +27,25 @@ import ( // applies the matching typed setter. Mismatched payloads return 400. The // setting must already exist (creation is the legacy admin's job); a missing // setting → 404. Audit-log on success only. +// @Summary Update setting +// @Description Updates a mutable setting value. +// @Tags settings +// @Accept json +// @Produce json +// @Param service path string true "Service name" +// @Param name path string true "Setting name" +// @Param request body types.SettingPatchRequest true "Request body" +// @Success 200 {object} settings.SettingValue +// @Failure 400 {object} types.ApiErrorResponse "Bad request" +// @Failure 401 {object} types.ApiErrorResponse "Unauthorized" +// @Failure 403 {object} types.ApiErrorResponse "Forbidden" +// @Failure 404 {object} types.ApiErrorResponse "Not found" +// @Failure 409 {object} types.ApiErrorResponse "Conflict" +// @Failure 429 {object} types.ApiErrorResponse "Too many requests" +// @Failure 500 {object} types.ApiErrorResponse "Internal server error" +// @Failure 503 {object} types.ApiErrorResponse "Service unavailable" +// @Security ApiKeyAuth +// @Router /api/v1/settings/{service}/{name} [patch] func (h *HandlersApi) SettingPatchHandler(w http.ResponseWriter, r *http.Request) { if h.DebugHTTPConfig.EnableHTTP { utils.DebugHTTPDump(h.DebugHTTP, r, h.DebugHTTPConfig.ShowBody) diff --git a/cmd/api/handlers/stats.go b/cmd/api/handlers/stats.go index 800b0447..f3815413 100644 --- a/cmd/api/handlers/stats.go +++ b/cmd/api/handlers/stats.go @@ -66,6 +66,22 @@ type StatsResponse struct { // functions are exercised by existing tests in pkg/queries; a full // integration test would require DB fixture setup that is out of scope // for Track 2. +// +// @Summary Get dashboard stats +// @Description Returns cross-environment dashboard statistics. +// @Tags stats +// @Produce json +// @Success 200 {object} StatsResponse +// @Failure 400 {object} types.ApiErrorResponse "Bad request" +// @Failure 401 {object} types.ApiErrorResponse "Unauthorized" +// @Failure 403 {object} types.ApiErrorResponse "Forbidden" +// @Failure 404 {object} types.ApiErrorResponse "Not found" +// @Failure 409 {object} types.ApiErrorResponse "Conflict" +// @Failure 429 {object} types.ApiErrorResponse "Too many requests" +// @Failure 500 {object} types.ApiErrorResponse "Internal server error" +// @Failure 503 {object} types.ApiErrorResponse "Service unavailable" +// @Security ApiKeyAuth +// @Router /api/v1/stats [get] func (h *HandlersApi) StatsHandler(w http.ResponseWriter, r *http.Request) { ctxVal := r.Context().Value(ContextKey(contextAPI)) if ctxVal == nil { @@ -204,6 +220,23 @@ var activityIntervalHours = map[string]int{ // Buckets are emitted contiguously — empty windows return zero rows for // that bucket — so the SPA can render the grid without densifying // client-side. +// @Summary Get environment activity +// @Description Returns activity buckets for an environment. +// @Tags stats +// @Produce json +// @Param env path string true "Environment name or UUID" +// @Param hours query int false "Number of hours to include" +// @Success 200 {object} ActivityBucket +// @Failure 400 {object} types.ApiErrorResponse "Bad request" +// @Failure 401 {object} types.ApiErrorResponse "Unauthorized" +// @Failure 403 {object} types.ApiErrorResponse "Forbidden" +// @Failure 404 {object} types.ApiErrorResponse "Not found" +// @Failure 409 {object} types.ApiErrorResponse "Conflict" +// @Failure 429 {object} types.ApiErrorResponse "Too many requests" +// @Failure 500 {object} types.ApiErrorResponse "Internal server error" +// @Failure 503 {object} types.ApiErrorResponse "Service unavailable" +// @Security ApiKeyAuth +// @Router /api/v1/stats/activity/{env} [get] func (h *HandlersApi) EnvActivityHandler(w http.ResponseWriter, r *http.Request) { ctxVal := r.Context().Value(ContextKey(contextAPI)) if ctxVal == nil { @@ -306,6 +339,24 @@ type NodeActivityBucket struct { // tables (see NodeActivityBucket) keyed by the node's UUID — except // node_queries which keys by numeric NodeID, looked up once from the // resolved node. +// @Summary Get node activity +// @Description Returns activity buckets for a node. +// @Tags stats +// @Produce json +// @Param env path string true "Environment name or UUID" +// @Param uuid path string true "Node UUID" +// @Param hours query int false "Number of hours to include" +// @Success 200 {object} NodeActivityBucket +// @Failure 400 {object} types.ApiErrorResponse "Bad request" +// @Failure 401 {object} types.ApiErrorResponse "Unauthorized" +// @Failure 403 {object} types.ApiErrorResponse "Forbidden" +// @Failure 404 {object} types.ApiErrorResponse "Not found" +// @Failure 409 {object} types.ApiErrorResponse "Conflict" +// @Failure 429 {object} types.ApiErrorResponse "Too many requests" +// @Failure 500 {object} types.ApiErrorResponse "Internal server error" +// @Failure 503 {object} types.ApiErrorResponse "Service unavailable" +// @Security ApiKeyAuth +// @Router /api/v1/stats/activity/node/{env}/{uuid} [get] func (h *HandlersApi) NodeActivityHandler(w http.ResponseWriter, r *http.Request) { ctxVal := r.Context().Value(ContextKey(contextAPI)) if ctxVal == nil { @@ -427,6 +478,24 @@ func (h *HandlersApi) computeNodeActivityForNode( // Unknown / unauthorized UUIDs are silently omitted from the response // (they're treated as "no data"), not 404'd — that lets a single bad UUID // in the list not break the whole page render. +// @Summary Get node activity batch +// @Description Returns activity buckets for multiple nodes in an environment. +// @Tags stats +// @Produce json +// @Param env path string true "Environment name or UUID" +// @Param uuids query string false "Comma-separated node UUIDs" +// @Param hours query int false "Number of hours to include" +// @Success 200 {object} map[string][]NodeActivityBucket +// @Failure 400 {object} types.ApiErrorResponse "Bad request" +// @Failure 401 {object} types.ApiErrorResponse "Unauthorized" +// @Failure 403 {object} types.ApiErrorResponse "Forbidden" +// @Failure 404 {object} types.ApiErrorResponse "Not found" +// @Failure 409 {object} types.ApiErrorResponse "Conflict" +// @Failure 429 {object} types.ApiErrorResponse "Too many requests" +// @Failure 500 {object} types.ApiErrorResponse "Internal server error" +// @Failure 503 {object} types.ApiErrorResponse "Service unavailable" +// @Security ApiKeyAuth +// @Router /api/v1/stats/activity/node-batch/{env} [get] func (h *HandlersApi) NodeActivityBatchHandler(w http.ResponseWriter, r *http.Request) { ctxVal := r.Context().Value(ContextKey(contextAPI)) if ctxVal == nil { @@ -523,6 +592,21 @@ func (h *HandlersApi) NodeActivityBatchHandler(w http.ResponseWriter, r *http.Re // Counts include both active and inactive nodes — a node sitting at an old // osquery version is still "stale" even if it's offline today, because once // it comes back online it'll come back stale. +// @Summary Get osquery version stats +// @Description Returns fleet-wide osquery version counts. +// @Tags stats +// @Produce json +// @Success 200 {object} nodes.OsqueryVersionCount +// @Failure 400 {object} types.ApiErrorResponse "Bad request" +// @Failure 401 {object} types.ApiErrorResponse "Unauthorized" +// @Failure 403 {object} types.ApiErrorResponse "Forbidden" +// @Failure 404 {object} types.ApiErrorResponse "Not found" +// @Failure 409 {object} types.ApiErrorResponse "Conflict" +// @Failure 429 {object} types.ApiErrorResponse "Too many requests" +// @Failure 500 {object} types.ApiErrorResponse "Internal server error" +// @Failure 503 {object} types.ApiErrorResponse "Service unavailable" +// @Security ApiKeyAuth +// @Router /api/v1/stats/osquery-versions [get] func (h *HandlersApi) OsqueryVersionsHandler(w http.ResponseWriter, r *http.Request) { ctxVal := r.Context().Value(ContextKey(contextAPI)) if ctxVal == nil { diff --git a/cmd/api/handlers/tags.go b/cmd/api/handlers/tags.go index 801aaba2..357282c1 100644 --- a/cmd/api/handlers/tags.go +++ b/cmd/api/handlers/tags.go @@ -15,6 +15,21 @@ import ( ) // AllTagsHandler - GET Handler for all JSON tags +// @Summary List tags +// @Description Returns tags across environments. +// @Tags tags +// @Produce json +// @Success 200 {array} tags.AdminTag +// @Failure 400 {object} types.ApiErrorResponse "Bad request" +// @Failure 401 {object} types.ApiErrorResponse "Unauthorized" +// @Failure 403 {object} types.ApiErrorResponse "Forbidden" +// @Failure 404 {object} types.ApiErrorResponse "Not found" +// @Failure 409 {object} types.ApiErrorResponse "Conflict" +// @Failure 429 {object} types.ApiErrorResponse "Too many requests" +// @Failure 500 {object} types.ApiErrorResponse "Internal server error" +// @Failure 503 {object} types.ApiErrorResponse "Service unavailable" +// @Security ApiKeyAuth +// @Router /api/v1/tags [get] func (h *HandlersApi) AllTagsHandler(w http.ResponseWriter, r *http.Request) { // Debug HTTP if enabled if h.DebugHTTPConfig.EnableHTTP { @@ -41,6 +56,23 @@ func (h *HandlersApi) AllTagsHandler(w http.ResponseWriter, r *http.Request) { // TagEnvHandler - GET Handler to return one tag for one environment as JSON. // Permission is scoped to env.UUID admin so non-super operators with admin // rights on this specific environment can view its tags. +// @Summary Get environment tag +// @Description Returns one tag by name for an environment. +// @Tags tags +// @Produce json +// @Param env path string true "Environment name or UUID" +// @Param name path string true "Tag name" +// @Success 200 {object} tags.AdminTag +// @Failure 400 {object} types.ApiErrorResponse "Bad request" +// @Failure 401 {object} types.ApiErrorResponse "Unauthorized" +// @Failure 403 {object} types.ApiErrorResponse "Forbidden" +// @Failure 404 {object} types.ApiErrorResponse "Not found" +// @Failure 409 {object} types.ApiErrorResponse "Conflict" +// @Failure 429 {object} types.ApiErrorResponse "Too many requests" +// @Failure 500 {object} types.ApiErrorResponse "Internal server error" +// @Failure 503 {object} types.ApiErrorResponse "Service unavailable" +// @Security ApiKeyAuth +// @Router /api/v1/tags/{env}/{name} [get] func (h *HandlersApi) TagEnvHandler(w http.ResponseWriter, r *http.Request) { // Debug HTTP if enabled if h.DebugHTTPConfig.EnableHTTP { @@ -82,6 +114,22 @@ func (h *HandlersApi) TagEnvHandler(w http.ResponseWriter, r *http.Request) { // TagsEnvHandler - GET Handler to return tags for one environment as JSON. // Permission is scoped to env.UUID admin (see TagEnvHandler note). +// @Summary List environment tags +// @Description Returns tags for an environment. +// @Tags tags +// @Produce json +// @Param env path string true "Environment name or UUID" +// @Success 200 {array} tags.AdminTag +// @Failure 400 {object} types.ApiErrorResponse "Bad request" +// @Failure 401 {object} types.ApiErrorResponse "Unauthorized" +// @Failure 403 {object} types.ApiErrorResponse "Forbidden" +// @Failure 404 {object} types.ApiErrorResponse "Not found" +// @Failure 409 {object} types.ApiErrorResponse "Conflict" +// @Failure 429 {object} types.ApiErrorResponse "Too many requests" +// @Failure 500 {object} types.ApiErrorResponse "Internal server error" +// @Failure 503 {object} types.ApiErrorResponse "Service unavailable" +// @Security ApiKeyAuth +// @Router /api/v1/tags/{env} [get] func (h *HandlersApi) TagsEnvHandler(w http.ResponseWriter, r *http.Request) { if h.DebugHTTPConfig.EnableHTTP { utils.DebugHTTPDump(h.DebugHTTP, r, h.DebugHTTPConfig.ShowBody) @@ -123,6 +171,25 @@ func (h *HandlersApi) TagsEnvHandler(w http.ResponseWriter, r *http.Request) { // action arrives as a URL path segment (legacy contract retained because // Track 6 doesn't introduce new tag routes); body validation surfaces 400 // on parse error and 409 on duplicate-name conflicts. +// @Summary Execute tag action +// @Description Creates, updates, deletes, or applies tags in an environment. +// @Tags tags +// @Accept json +// @Produce json +// @Param env path string true "Environment name or UUID" +// @Param action path string true "Tag action" +// @Param request body types.ApiTagsRequest true "Request body" +// @Success 200 {object} types.ApiDataResponse +// @Failure 400 {object} types.ApiErrorResponse "Bad request" +// @Failure 401 {object} types.ApiErrorResponse "Unauthorized" +// @Failure 403 {object} types.ApiErrorResponse "Forbidden" +// @Failure 404 {object} types.ApiErrorResponse "Not found" +// @Failure 409 {object} types.ApiErrorResponse "Conflict" +// @Failure 429 {object} types.ApiErrorResponse "Too many requests" +// @Failure 500 {object} types.ApiErrorResponse "Internal server error" +// @Failure 503 {object} types.ApiErrorResponse "Service unavailable" +// @Security ApiKeyAuth +// @Router /api/v1/tags/{env}/{action} [post] func (h *HandlersApi) TagsActionHandler(w http.ResponseWriter, r *http.Request) { if h.DebugHTTPConfig.EnableHTTP { utils.DebugHTTPDump(h.DebugHTTP, r, h.DebugHTTPConfig.ShowBody) diff --git a/cmd/api/handlers/users.go b/cmd/api/handlers/users.go index b7a56599..00102e75 100644 --- a/cmd/api/handlers/users.go +++ b/cmd/api/handlers/users.go @@ -35,6 +35,22 @@ func projectAdminUserView(u users.AdminUser) types.AdminUserView { } // UserHandler - GET Handler for environment users +// @Summary Get user +// @Description Returns an API user by username. +// @Tags users +// @Produce json +// @Param username path string true "Username" +// @Success 200 {object} types.AdminUserView +// @Failure 400 {object} types.ApiErrorResponse "Bad request" +// @Failure 401 {object} types.ApiErrorResponse "Unauthorized" +// @Failure 403 {object} types.ApiErrorResponse "Forbidden" +// @Failure 404 {object} types.ApiErrorResponse "Not found" +// @Failure 409 {object} types.ApiErrorResponse "Conflict" +// @Failure 429 {object} types.ApiErrorResponse "Too many requests" +// @Failure 500 {object} types.ApiErrorResponse "Internal server error" +// @Failure 503 {object} types.ApiErrorResponse "Service unavailable" +// @Security ApiKeyAuth +// @Router /api/v1/users/{username} [get] func (h *HandlersApi) UserHandler(w http.ResponseWriter, r *http.Request) { // Debug HTTP if enabled if h.DebugHTTPConfig.EnableHTTP { @@ -67,6 +83,21 @@ func (h *HandlersApi) UserHandler(w http.ResponseWriter, r *http.Request) { } // UsersHandler - GET Handler for multiple JSON nodes +// @Summary List users +// @Description Returns API users. +// @Tags users +// @Produce json +// @Success 200 {array} types.AdminUserView +// @Failure 400 {object} types.ApiErrorResponse "Bad request" +// @Failure 401 {object} types.ApiErrorResponse "Unauthorized" +// @Failure 403 {object} types.ApiErrorResponse "Forbidden" +// @Failure 404 {object} types.ApiErrorResponse "Not found" +// @Failure 409 {object} types.ApiErrorResponse "Conflict" +// @Failure 429 {object} types.ApiErrorResponse "Too many requests" +// @Failure 500 {object} types.ApiErrorResponse "Internal server error" +// @Failure 503 {object} types.ApiErrorResponse "Service unavailable" +// @Security ApiKeyAuth +// @Router /api/v1/users [get] func (h *HandlersApi) UsersHandler(w http.ResponseWriter, r *http.Request) { // Debug HTTP if enabled if h.DebugHTTPConfig.EnableHTTP { @@ -100,6 +131,25 @@ func (h *HandlersApi) UsersHandler(w http.ResponseWriter, r *http.Request) { } // UserActionHandler - POST Handler to take actions on a user by username and environment +// @Summary Execute user action +// @Description Creates, updates, deletes, or changes flags on a user. +// @Tags users +// @Accept json +// @Produce json +// @Param username path string true "Username" +// @Param action path string true "User action" +// @Param request body types.ApiUserRequest true "Request body" +// @Success 200 {object} types.ApiDataResponse +// @Failure 400 {object} types.ApiErrorResponse "Bad request" +// @Failure 401 {object} types.ApiErrorResponse "Unauthorized" +// @Failure 403 {object} types.ApiErrorResponse "Forbidden" +// @Failure 404 {object} types.ApiErrorResponse "Not found" +// @Failure 409 {object} types.ApiErrorResponse "Conflict" +// @Failure 429 {object} types.ApiErrorResponse "Too many requests" +// @Failure 500 {object} types.ApiErrorResponse "Internal server error" +// @Failure 503 {object} types.ApiErrorResponse "Service unavailable" +// @Security ApiKeyAuth +// @Router /api/v1/users/{username}/{action} [post] func (h *HandlersApi) UserActionHandler(w http.ResponseWriter, r *http.Request) { // Debug HTTP if enabled if h.DebugHTTPConfig.EnableHTTP { diff --git a/cmd/api/handlers/users_profile.go b/cmd/api/handlers/users_profile.go index bae2ee27..3658d117 100644 --- a/cmd/api/handlers/users_profile.go +++ b/cmd/api/handlers/users_profile.go @@ -28,6 +28,22 @@ const tokenRefreshDefaultHours = 24 // sees current state before making changes — no more accidentally // overwriting (user:true, query:true) by re-saving the modal's // default of (user:true, query:false). +// @Summary Get user permissions +// @Description Returns per-environment permissions for a user. +// @Tags users +// @Produce json +// @Param username path string true "Username" +// @Success 200 {object} types.GetPermissionsResponse +// @Failure 400 {object} types.ApiErrorResponse "Bad request" +// @Failure 401 {object} types.ApiErrorResponse "Unauthorized" +// @Failure 403 {object} types.ApiErrorResponse "Forbidden" +// @Failure 404 {object} types.ApiErrorResponse "Not found" +// @Failure 409 {object} types.ApiErrorResponse "Conflict" +// @Failure 429 {object} types.ApiErrorResponse "Too many requests" +// @Failure 500 {object} types.ApiErrorResponse "Internal server error" +// @Failure 503 {object} types.ApiErrorResponse "Service unavailable" +// @Security ApiKeyAuth +// @Router /api/v1/users/{username}/permissions [get] func (h *HandlersApi) GetUserPermissionsHandler(w http.ResponseWriter, r *http.Request) { if h.DebugHTTPConfig.EnableHTTP { utils.DebugHTTPDump(h.DebugHTTP, r, h.DebugHTTPConfig.ShowBody) @@ -72,6 +88,24 @@ func (h *HandlersApi) GetUserPermissionsHandler(w http.ResponseWriter, r *http.R // target user's per-env access rows. Returns 200 with the new EnvAccess. // Requires super-admin (AdminLevel, NoEnvironment) — env-scoped admins can // not grant permissions for their environment from this endpoint. +// @Summary Set user permissions +// @Description Sets per-environment permissions for a user. +// @Tags users +// @Accept json +// @Produce json +// @Param username path string true "Username" +// @Param request body types.SetPermissionsRequest true "Request body" +// @Success 200 {object} types.EnvAccessView +// @Failure 400 {object} types.ApiErrorResponse "Bad request" +// @Failure 401 {object} types.ApiErrorResponse "Unauthorized" +// @Failure 403 {object} types.ApiErrorResponse "Forbidden" +// @Failure 404 {object} types.ApiErrorResponse "Not found" +// @Failure 409 {object} types.ApiErrorResponse "Conflict" +// @Failure 429 {object} types.ApiErrorResponse "Too many requests" +// @Failure 500 {object} types.ApiErrorResponse "Internal server error" +// @Failure 503 {object} types.ApiErrorResponse "Service unavailable" +// @Security ApiKeyAuth +// @Router /api/v1/users/{username}/permissions [post] func (h *HandlersApi) SetUserPermissionsHandler(w http.ResponseWriter, r *http.Request) { if h.DebugHTTPConfig.EnableHTTP { utils.DebugHTTPDump(h.DebugHTTP, r, h.DebugHTTPConfig.ShowBody) @@ -171,6 +205,24 @@ func (h *HandlersApi) SetUserPermissionsHandler(w http.ResponseWriter, r *http.R // "All current envs" semantics: enumeration happens server-side at // request time. Envs created LATER do not inherit; the operator // re-applies as needed. See SetPermissionsAllRequest godoc. +// @Summary Set all user permissions +// @Description Sets permissions across all environments for a user. +// @Tags users +// @Accept json +// @Produce json +// @Param username path string true "Username" +// @Param request body types.SetPermissionsAllRequest true "Request body" +// @Success 200 {object} types.SetPermissionsAllResponse +// @Failure 400 {object} types.ApiErrorResponse "Bad request" +// @Failure 401 {object} types.ApiErrorResponse "Unauthorized" +// @Failure 403 {object} types.ApiErrorResponse "Forbidden" +// @Failure 404 {object} types.ApiErrorResponse "Not found" +// @Failure 409 {object} types.ApiErrorResponse "Conflict" +// @Failure 429 {object} types.ApiErrorResponse "Too many requests" +// @Failure 500 {object} types.ApiErrorResponse "Internal server error" +// @Failure 503 {object} types.ApiErrorResponse "Service unavailable" +// @Security ApiKeyAuth +// @Router /api/v1/users/{username}/permissions/all [post] func (h *HandlersApi) SetUserPermissionsAllHandler(w http.ResponseWriter, r *http.Request) { if h.DebugHTTPConfig.EnableHTTP { utils.DebugHTTPDump(h.DebugHTTP, r, h.DebugHTTPConfig.ShowBody) @@ -264,6 +316,23 @@ func (h *HandlersApi) SetUserPermissionsAllHandler(w http.ResponseWriter, r *htt // APIToken (invalidating the previous token), and returns the new token + // expiry. Requires super-admin OR the request author asking for their own // token. Audit-logged on success. +// @Summary Refresh user token +// @Description Refreshes an API token for a user. +// @Tags users +// @Accept json +// @Produce json +// @Param username path string true "Username" +// @Success 200 {object} types.TokenResponse +// @Failure 400 {object} types.ApiErrorResponse "Bad request" +// @Failure 401 {object} types.ApiErrorResponse "Unauthorized" +// @Failure 403 {object} types.ApiErrorResponse "Forbidden" +// @Failure 404 {object} types.ApiErrorResponse "Not found" +// @Failure 409 {object} types.ApiErrorResponse "Conflict" +// @Failure 429 {object} types.ApiErrorResponse "Too many requests" +// @Failure 500 {object} types.ApiErrorResponse "Internal server error" +// @Failure 503 {object} types.ApiErrorResponse "Service unavailable" +// @Security ApiKeyAuth +// @Router /api/v1/users/{username}/token/refresh [post] func (h *HandlersApi) RefreshUserTokenHandler(w http.ResponseWriter, r *http.Request) { if h.DebugHTTPConfig.EnableHTTP { utils.DebugHTTPDump(h.DebugHTTP, r, h.DebugHTTPConfig.ShowBody) @@ -303,6 +372,22 @@ func (h *HandlersApi) RefreshUserTokenHandler(w http.ResponseWriter, r *http.Req // // Clears the user's APIToken so any existing JWT for them stops working. // Requires super-admin OR the user themselves. +// @Summary Delete user token +// @Description Deletes an API token for a user. +// @Tags users +// @Produce json +// @Param username path string true "Username" +// @Success 200 {object} types.ApiGenericResponse +// @Failure 400 {object} types.ApiErrorResponse "Bad request" +// @Failure 401 {object} types.ApiErrorResponse "Unauthorized" +// @Failure 403 {object} types.ApiErrorResponse "Forbidden" +// @Failure 404 {object} types.ApiErrorResponse "Not found" +// @Failure 409 {object} types.ApiErrorResponse "Conflict" +// @Failure 429 {object} types.ApiErrorResponse "Too many requests" +// @Failure 500 {object} types.ApiErrorResponse "Internal server error" +// @Failure 503 {object} types.ApiErrorResponse "Service unavailable" +// @Security ApiKeyAuth +// @Router /api/v1/users/{username}/token [delete] func (h *HandlersApi) DeleteUserTokenHandler(w http.ResponseWriter, r *http.Request) { if h.DebugHTTPConfig.EnableHTTP { utils.DebugHTTPDump(h.DebugHTTP, r, h.DebugHTTPConfig.ShowBody) @@ -336,6 +421,21 @@ func (h *HandlersApi) DeleteUserTokenHandler(w http.ResponseWriter, r *http.Requ // // Returns the currently authenticated user's profile (sans password hash // and API token). Useful for the SPA's Profile page. +// @Summary Get current user +// @Description Returns the currently authenticated user profile. +// @Tags users +// @Produce json +// @Success 200 {object} types.UserMeResponse +// @Failure 400 {object} types.ApiErrorResponse "Bad request" +// @Failure 401 {object} types.ApiErrorResponse "Unauthorized" +// @Failure 403 {object} types.ApiErrorResponse "Forbidden" +// @Failure 404 {object} types.ApiErrorResponse "Not found" +// @Failure 409 {object} types.ApiErrorResponse "Conflict" +// @Failure 429 {object} types.ApiErrorResponse "Too many requests" +// @Failure 500 {object} types.ApiErrorResponse "Internal server error" +// @Failure 503 {object} types.ApiErrorResponse "Service unavailable" +// @Security ApiKeyAuth +// @Router /api/v1/users/me [get] func (h *HandlersApi) MeHandler(w http.ResponseWriter, r *http.Request) { if h.DebugHTTPConfig.EnableHTTP { utils.DebugHTTPDump(h.DebugHTTP, r, h.DebugHTTPConfig.ShowBody) @@ -381,6 +481,23 @@ func (h *HandlersApi) MeHandler(w http.ResponseWriter, r *http.Request) { // // Updates email and/or fullname for the currently authenticated user. Sends // each empty field through unchanged. Returns the updated profile. +// @Summary Update current user +// @Description Updates the current user's profile fields. +// @Tags users +// @Accept json +// @Produce json +// @Param request body types.UserMePatchRequest true "Request body" +// @Success 200 {object} types.UserMeResponse +// @Failure 400 {object} types.ApiErrorResponse "Bad request" +// @Failure 401 {object} types.ApiErrorResponse "Unauthorized" +// @Failure 403 {object} types.ApiErrorResponse "Forbidden" +// @Failure 404 {object} types.ApiErrorResponse "Not found" +// @Failure 409 {object} types.ApiErrorResponse "Conflict" +// @Failure 429 {object} types.ApiErrorResponse "Too many requests" +// @Failure 500 {object} types.ApiErrorResponse "Internal server error" +// @Failure 503 {object} types.ApiErrorResponse "Service unavailable" +// @Security ApiKeyAuth +// @Router /api/v1/users/me [patch] func (h *HandlersApi) MePatchHandler(w http.ResponseWriter, r *http.Request) { if h.DebugHTTPConfig.EnableHTTP { utils.DebugHTTPDump(h.DebugHTTP, r, h.DebugHTTPConfig.ShowBody) @@ -430,6 +547,23 @@ func (h *HandlersApi) MePatchHandler(w http.ResponseWriter, r *http.Request) { // // Changes the currently authenticated user's password. Verifies the // current password (bcrypt) before persisting the new hash. +// @Summary Change current user password +// @Description Changes the current user's password. +// @Tags users +// @Accept json +// @Produce json +// @Param request body types.PasswordChangeRequest true "Request body" +// @Success 200 {object} types.ApiGenericResponse +// @Failure 400 {object} types.ApiErrorResponse "Bad request" +// @Failure 401 {object} types.ApiErrorResponse "Unauthorized" +// @Failure 403 {object} types.ApiErrorResponse "Forbidden" +// @Failure 404 {object} types.ApiErrorResponse "Not found" +// @Failure 409 {object} types.ApiErrorResponse "Conflict" +// @Failure 429 {object} types.ApiErrorResponse "Too many requests" +// @Failure 500 {object} types.ApiErrorResponse "Internal server error" +// @Failure 503 {object} types.ApiErrorResponse "Service unavailable" +// @Security ApiKeyAuth +// @Router /api/v1/users/me/password [post] func (h *HandlersApi) MePasswordHandler(w http.ResponseWriter, r *http.Request) { if h.DebugHTTPConfig.EnableHTTP { utils.DebugHTTPDump(h.DebugHTTP, r, h.DebugHTTPConfig.ShowBody) diff --git a/cmd/api/main.go b/cmd/api/main.go index 421295ae..c7dce7f4 100644 --- a/cmd/api/main.go +++ b/cmd/api/main.go @@ -787,6 +787,19 @@ func initializeLoggers(cfg config.YAMLConfigurationService) { } } +// @title osctrl API +// @version 0.5.2 +// @description API service for osctrl, a fast and efficient osquery management solution. +// @termsOfService https://github.com/jmpsec/osctrl +// @contact.name osctrl +// @contact.url https://github.com/jmpsec/osctrl +// @license.name MIT +// @license.url https://github.com/jmpsec/osctrl/blob/master/LICENSE +// @BasePath / +// @schemes http https +// @securityDefinitions.apikey ApiKeyAuth +// @in header +// @name Authorization func main() { // Initiate CLI and parse arguments app = &cli.Command{