Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Delete and expire carves and queries via osctrl-api #545

Merged
merged 1 commit into from
Oct 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 67 additions & 0 deletions api/handlers/carves.go
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,73 @@ func (h *HandlersApi) CarvesRunHandler(w http.ResponseWriter, r *http.Request) {
h.Inc(metricAPICarvesOK)
}

// CarvesActionHandler - POST Handler to delete/expire a carve
func (h *HandlersApi) CarvesActionHandler(w http.ResponseWriter, r *http.Request) {
h.Inc(metricAPICarvesReq)
utils.DebugHTTPDump(r, h.Settings.DebugHTTP(settings.ServiceAPI, settings.NoEnvironmentID), false)
// Extract environment
envVar := r.PathValue("env")
if envVar == "" {
apiErrorResponse(w, "error with environment", http.StatusBadRequest, nil)
h.Inc(metricAPICarvesErr)
return
}
// Get environment
env, err := h.Envs.GetByUUID(envVar)
if err != nil {
apiErrorResponse(w, "error getting environment", http.StatusInternalServerError, nil)
h.Inc(metricAPICarvesErr)
return
}
// Get context data and check access
ctx := r.Context().Value(contextKey(contextAPI)).(contextValue)
if !h.Users.CheckPermissions(ctx[ctxUser], users.AdminLevel, env.UUID) {
apiErrorResponse(w, "no access", http.StatusForbidden, fmt.Errorf("attempt to use API by user %s", ctx[ctxUser]))
h.Inc(metricAPICarvesErr)
return
}
var msgReturn string
// Carve can not be empty
nameVar := r.PathValue("name")
if nameVar == "" {
apiErrorResponse(w, "name can not be empty", http.StatusBadRequest, nil)
h.Inc(metricAPICarvesErr)
return
}
// Check if carve exists
if !h.Queries.Exists(nameVar, env.ID) {
apiErrorResponse(w, "carve not found", http.StatusNotFound, nil)
h.Inc(metricAPICarvesErr)
return
}
// Extract action
actionVar := r.PathValue("action")
if actionVar == "" {
apiErrorResponse(w, "error getting action", http.StatusBadRequest, nil)
h.Inc(metricAPICarvesErr)
return
}
switch actionVar {
case settings.CarveDelete:
if err := h.Queries.Delete(nameVar, env.ID); err != nil {
apiErrorResponse(w, "error deleting carve", http.StatusInternalServerError, err)
h.Inc(metricAPICarvesErr)
return
}
msgReturn = fmt.Sprintf("carve %s deleted successfully", nameVar)
case settings.CarveExpire:
if err := h.Queries.Expire(nameVar, env.ID); err != nil {
apiErrorResponse(w, "error expiring carve", http.StatusInternalServerError, err)
h.Inc(metricAPICarvesErr)
return
}
msgReturn = fmt.Sprintf("carve %s expired successfully", nameVar)
}
// Return message as serialized response
utils.HTTPResponse(w, utils.JSONApplicationUTF8, http.StatusOK, types.ApiGenericResponse{Message: msgReturn})
h.Inc(metricAPICarvesOK)
}

// GET Handler to return carves in JSON
func (h *HandlersApi) apiCarvesShowHandler(w http.ResponseWriter, r *http.Request) {
h.Inc(metricAPICarvesReq)
Expand Down
67 changes: 67 additions & 0 deletions api/handlers/queries.go
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,73 @@ func (h *HandlersApi) QueriesRunHandler(w http.ResponseWriter, r *http.Request)
h.Inc(metricAPIQueriesOK)
}

// QueriesActionHandler - POST Handler to delete/expire a query
func (h *HandlersApi) QueriesActionHandler(w http.ResponseWriter, r *http.Request) {
h.Inc(metricAPIQueriesReq)
utils.DebugHTTPDump(r, h.Settings.DebugHTTP(settings.ServiceAPI, settings.NoEnvironmentID), false)
// Extract environment
envVar := r.PathValue("env")
if envVar == "" {
apiErrorResponse(w, "error with environment", http.StatusBadRequest, nil)
h.Inc(metricAPIQueriesErr)
return
}
// Get environment
env, err := h.Envs.GetByUUID(envVar)
if err != nil {
apiErrorResponse(w, "error getting environment", http.StatusInternalServerError, nil)
h.Inc(metricAPIQueriesErr)
return
}
// Get context data and check access
ctx := r.Context().Value(contextKey(contextAPI)).(contextValue)
if !h.Users.CheckPermissions(ctx[ctxUser], users.AdminLevel, env.UUID) {
apiErrorResponse(w, "no access", http.StatusForbidden, fmt.Errorf("attempt to use API by user %s", ctx[ctxUser]))
h.Inc(metricAPIQueriesErr)
return
}
var msgReturn string
// Extract action
actionVar := r.PathValue("action")
if actionVar == "" {
apiErrorResponse(w, "error getting action", http.StatusBadRequest, nil)
h.Inc(metricAPIQueriesErr)
return
}
// Query can not be empty
nameVar := r.PathValue("name")
if nameVar == "" {
apiErrorResponse(w, "name can not be empty", http.StatusBadRequest, nil)
h.Inc(metricAPIQueriesErr)
return
}
// Check if query exists
if !h.Queries.Exists(nameVar, env.ID) {
apiErrorResponse(w, "query not found", http.StatusNotFound, nil)
h.Inc(metricAPIQueriesErr)
return
}
switch actionVar {
case settings.QueryDelete:
if err := h.Queries.Delete(nameVar, env.ID); err != nil {
apiErrorResponse(w, "error deleting query", http.StatusInternalServerError, err)
h.Inc(metricAPIQueriesErr)
return
}
msgReturn = fmt.Sprintf("query %s deleted successfully", nameVar)
case settings.QueryExpire:
if err := h.Queries.Expire(nameVar, env.ID); err != nil {
apiErrorResponse(w, "error expiring query", http.StatusInternalServerError, err)
h.Inc(metricAPIQueriesErr)
return
}
msgReturn = fmt.Sprintf("query %s expired successfully", nameVar)
}
// Return message as serialized response
utils.HTTPResponse(w, utils.JSONApplicationUTF8, http.StatusOK, types.ApiGenericResponse{Message: msgReturn})
h.Inc(metricAPIQueriesOK)
}

// AllQueriesShowHandler - GET Handler to return all queries in JSON
func (h *HandlersApi) AllQueriesShowHandler(w http.ResponseWriter, r *http.Request) {
h.Inc(metricAPIQueriesReq)
Expand Down
2 changes: 2 additions & 0 deletions api/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -574,10 +574,12 @@ func osctrlAPIService() {
muxAPI.Handle("GET "+_apiPath(apiQueriesPath)+"/{env}/{name}", handlerAuthCheck(http.HandlerFunc(handlersApi.QueryShowHandler)))
muxAPI.Handle("GET "+_apiPath(apiQueriesPath)+"/{env}/results/{name}", handlerAuthCheck(http.HandlerFunc(handlersApi.QueryResultsHandler)))
muxAPI.Handle("GET "+_apiPath(apiAllQueriesPath+"/{env}"), handlerAuthCheck(http.HandlerFunc(handlersApi.AllQueriesShowHandler)))
muxAPI.Handle("POST "+_apiPath(apiQueriesPath)+"/{env}/{action}/{name}", handlerAuthCheck(http.HandlerFunc(handlersApi.QueriesActionHandler)))
// API: carves by environment
muxAPI.Handle("GET "+_apiPath(apiCarvesPath)+"/{env}", handlerAuthCheck(http.HandlerFunc(handlersApi.CarveShowHandler)))
muxAPI.Handle("POST "+_apiPath(apiCarvesPath)+"/{env}", handlerAuthCheck(http.HandlerFunc(handlersApi.CarvesRunHandler)))
muxAPI.Handle("GET "+_apiPath(apiCarvesPath)+"/{env}/{name}", handlerAuthCheck(http.HandlerFunc(handlersApi.CarveShowHandler)))
muxAPI.Handle("POST "+_apiPath(apiCarvesPath)+"/{env}/{action}/{name}", handlerAuthCheck(http.HandlerFunc(handlersApi.CarvesActionHandler)))
// API: users
muxAPI.Handle("GET "+_apiPath(apiUsersPath)+"/{username}", handlerAuthCheck(http.HandlerFunc(handlersApi.UserHandler)))
muxAPI.Handle("GET "+_apiPath(apiUsersPath), handlerAuthCheck(http.HandlerFunc(handlersApi.UsersHandler)))
Expand Down
130 changes: 128 additions & 2 deletions osctrl-api.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -461,6 +461,12 @@ paths:
description: Returns the requested on-demand query results by name and environment
operationId: QueryResultsHandler
parameters:
- name: env
in: path
description: Name or UUID of the requested osctrl environment
required: true
schema:
type: string
- name: name
in: path
description: Name of the requested on-demand query
Expand Down Expand Up @@ -495,6 +501,66 @@ paths:
security:
- Authorization:
- query
/queries/{env}/{action}/{name}:
post:
tags:
- queries
summary: Execute action on on-demand query
description: Executes an action (delete/expire) in the on-demand query by name and environment
operationId: QueriesActionHandler
parameters:
- name: env
in: path
description: Name or UUID of the requested osctrl environment
required: true
schema:
type: string
- name: action
in: path
description: Action to execute (delete, expire)
required: true
schema:
type: string
- name: name
in: path
description: Name of the requested on-demand query
required: true
schema:
type: string
responses:
200:
description: successful operation
content:
application/json:
schema:
$ref: "#/components/schemas/ApiGenericResponse"
400:
description: bad request
content:
application/json:
schema:
$ref: "#/components/schemas/ApiErrorResponse"
403:
description: no access
content:
application/json:
schema:
$ref: "#/components/schemas/ApiErrorResponse"
404:
description: no queries
content:
application/json:
schema:
$ref: "#/components/schemas/ApiErrorResponse"
500:
description: error getting queries
content:
application/json:
schema:
$ref: "#/components/schemas/ApiErrorResponse"
security:
- Authorization:
- admin
/all-queries/{env}:
get:
tags:
Expand Down Expand Up @@ -542,7 +608,7 @@ paths:
/carves/{env}:
get:
tags:
- queries
- carves
summary: Get file carves
description: Returns all file carves by environment
operationId: CarvesShowHandler
Expand Down Expand Up @@ -625,7 +691,7 @@ paths:
/carves/{env}/{name}:
get:
tags:
- queries
- carves
summary: Get a file carve
description: Returns a file carve by environment and name
operationId: CarveShowHandler
Expand Down Expand Up @@ -672,6 +738,66 @@ paths:
security:
- Authorization:
- carve
/carves/{env}/{action}/{name}:
post:
tags:
- carves
summary: Execute action on file carve
description: Executes an action (delete/expire) in the file carve by name and environment
operationId: CarvesActionHandler
parameters:
- name: env
in: path
description: Name or UUID of the requested osctrl environment
required: true
schema:
type: string
- name: action
in: path
description: Action to execute (delete, expire)
required: true
schema:
type: string
- name: name
in: path
description: Name of the requested file carve
required: true
schema:
type: string
responses:
200:
description: successful operation
content:
application/json:
schema:
$ref: "#/components/schemas/ApiGenericResponse"
400:
description: bad request
content:
application/json:
schema:
$ref: "#/components/schemas/ApiErrorResponse"
403:
description: no access
content:
application/json:
schema:
$ref: "#/components/schemas/ApiErrorResponse"
404:
description: no queries
content:
application/json:
schema:
$ref: "#/components/schemas/ApiErrorResponse"
500:
description: error getting queries
content:
application/json:
schema:
$ref: "#/components/schemas/ApiErrorResponse"
security:
- Authorization:
- admin
/users:
get:
tags:
Expand Down
7 changes: 7 additions & 0 deletions queries/queries.go
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,13 @@ func (q *Queries) Gets(target, qtype string, envid uint) ([]DistributedQuery, er
return queries, nil
}

// Checks if a query exists in an environment, regardless of the status
func (q *Queries) Exists(name string, envid uint) bool {
var count int64
q.DB.Model(&DistributedQuery{}).Where("name = ? AND environment_id = ?", name, envid).Count(&count)
return (count > 0)
}

// GetActive all active queries and carves by target
func (q *Queries) GetActive(envid uint) ([]DistributedQuery, error) {
var queries []DistributedQuery
Expand Down
8 changes: 8 additions & 0 deletions settings/settings.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,14 @@ const (
SetRpmPackage string = "set_rpm"
)

// Types of query/carve actions
const (
QueryDelete string = "delete"
QueryExpire string = "expire"
CarveDelete string = QueryDelete
CarveExpire string = QueryExpire
)

// Types of package
const (
PackageDeb string = "deb"
Expand Down
Loading