Skip to content

Commit

Permalink
Delete and expire carves and queries via osctrl-api
Browse files Browse the repository at this point in the history
  • Loading branch information
javuto committed Oct 21, 2024
1 parent 459912b commit d2e765e
Show file tree
Hide file tree
Showing 6 changed files with 279 additions and 2 deletions.
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

0 comments on commit d2e765e

Please sign in to comment.