Skip to content
Open
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
131 changes: 127 additions & 4 deletions cmd/api/handlers/environments.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"github.com/jmpsec/osctrl/pkg/auditlog"
"github.com/jmpsec/osctrl/pkg/environments"
"github.com/jmpsec/osctrl/pkg/settings"
"github.com/jmpsec/osctrl/pkg/tags"
"github.com/jmpsec/osctrl/pkg/types"
"github.com/jmpsec/osctrl/pkg/users"
"github.com/jmpsec/osctrl/pkg/utils"
Expand Down Expand Up @@ -391,24 +392,24 @@ func (h *HandlersApi) EnvRemoveActionsHandler(w http.ResponseWriter, r *http.Req
var msgReturn string
switch actionVar {
case settings.ActionExtend:
if err := h.Envs.ExtendEnroll(env.UUID); err != nil {
if err := h.Envs.ExtendRemove(env.UUID); err != nil {
apiErrorResponse(w, "error extending remove", http.StatusInternalServerError, err)
return
}
msgReturn = "remove extended successfully"
case settings.ActionExpire:
if err := h.Envs.ExpireEnroll(env.UUID); err != nil {
if err := h.Envs.ExpireRemove(env.UUID); err != nil {
apiErrorResponse(w, "error expiring remove", http.StatusInternalServerError, err)
return
}
case settings.ActionRotate:
if err := h.Envs.RotateEnroll(env.UUID); err != nil {
if err := h.Envs.RotateRemove(env.UUID); err != nil {
apiErrorResponse(w, "error rotating remove", http.StatusInternalServerError, err)
return
}
msgReturn = "remove rotated successfully"
case settings.ActionNotexpire:
if err := h.Envs.NotExpireEnroll(env.UUID); err != nil {
if err := h.Envs.NotExpireRemove(env.UUID); err != nil {
apiErrorResponse(w, "error setting no remove", http.StatusInternalServerError, err)
return
}
Expand All @@ -422,3 +423,125 @@ func (h *HandlersApi) EnvRemoveActionsHandler(w http.ResponseWriter, r *http.Req
h.AuditLog.EnvAction(ctx[ctxUser], actionVar+" removal for environment "+env.Name, strings.Split(r.RemoteAddr, ":")[0], env.ID)
utils.HTTPResponse(w, utils.JSONApplicationUTF8, http.StatusOK, types.ApiGenericResponse{Message: msgReturn})
}

// EnvActionsHandler - POST Handler to perform actions (create, delete, edit) on environments
func (h *HandlersApi) EnvActionsHandler(w http.ResponseWriter, r *http.Request) {
// Debug HTTP if enabled
if h.DebugHTTPConfig.EnableHTTP {
utils.DebugHTTPDump(h.DebugHTTP, r, h.DebugHTTPConfig.ShowBody)
}
// Get context data and check access
ctx := r.Context().Value(ContextKey(contextAPI)).(ContextValue)
if !h.Users.CheckPermissions(ctx[ctxUser], users.AdminLevel, users.NoEnvironment) {
apiErrorResponse(w, "no access", http.StatusForbidden, fmt.Errorf("attempt to use API by user %s", ctx[ctxUser]))
return
}
var e types.ApiEnvRequest
// Parse request JSON body
if err := json.NewDecoder(r.Body).Decode(&e); err != nil {
apiErrorResponse(w, "error parsing POST body", http.StatusInternalServerError, err)
return
}
var msgReturn string
switch e.Action {
case "create":
// Verify request fields
if !environments.VerifyEnvFilters(e.Name, e.Icon, e.Type, e.Hostname) {
apiErrorResponse(w, "invalid data", http.StatusBadRequest, nil)
return
}
// Check if environment already exists
if !h.Envs.Exists(e.Name) {
env := h.Envs.Empty(e.Name, e.Hostname)
env.Icon = e.Icon
env.Type = e.Type
if e.UUID != "" {
env.UUID = e.UUID
}
// Empty configuration
env.Configuration = h.Envs.GenEmptyConfiguration(true)
// Generate flags
flags, err := h.Envs.GenerateFlags(env, "", "", h.OsqueryValues)
if err != nil {
apiErrorResponse(w, "error generating flags", http.StatusInternalServerError, err)
return
}
env.Flags = flags
// Create environment
if err := h.Envs.Create(&env); err != nil {
apiErrorResponse(w, "error creating environment", http.StatusInternalServerError, err)
return
}
// Generate full permissions for the user creating the environment
access := h.Users.GenEnvUserAccess([]string{env.UUID}, true, true, true, true)
perms := h.Users.GenPermissions(ctx[ctxUser], "osctrl-api", access)
if err := h.Users.CreatePermissions(perms); err != nil {
apiErrorResponse(w, "error generating permissions", http.StatusInternalServerError, err)
return
}
// Create a tag for this new environment
if !h.Tags.Exists(env.Name) {
if err := h.Tags.NewTag(
env.Name,
"Tag for environment "+env.Name,
"",
env.Icon,
ctx[ctxUser],
env.ID,
false,
tags.TagTypeEnv,
""); err != nil {
msgReturn = fmt.Sprintf("error generating tag %s ", err.Error())
return
}
}
msgReturn = "environment created successfully"
} else {
apiErrorResponse(w, "environment already exists", http.StatusBadRequest, fmt.Errorf("environment %s already exists", e.Name))
return
}
case "delete":
// Verify request fields
if !environments.EnvNameFilter(e.Name) {
apiErrorResponse(w, "invalid environment name", http.StatusBadRequest, nil)
return
}
if h.Envs.Exists(e.UUID) {
if err := h.Envs.Delete(e.Name); err != nil {
apiErrorResponse(w, "error deleting environment", http.StatusInternalServerError, err)
return
}
msgReturn = "environment deleted successfully"
} else {
apiErrorResponse(w, "environment not found", http.StatusNotFound, fmt.Errorf("environment %s not found", e.Name))
return
}
case "edit":
// Verify request fields
if !environments.EnvUUIDFilter(e.UUID) {
apiErrorResponse(w, "invalid environment UUID", http.StatusBadRequest, nil)
return
}
if !environments.HostnameFilter(e.Hostname) {
apiErrorResponse(w, "invalid hostname", http.StatusBadRequest, nil)
return
}
if h.Envs.Exists(e.UUID) {
if err := h.Envs.UpdateHostname(e.UUID, e.Hostname); err != nil {
apiErrorResponse(w, "error updating hostname", http.StatusInternalServerError, err)
return
}
msgReturn = "environment updated successfully"
} else {
apiErrorResponse(w, "environment not found", http.StatusNotFound, fmt.Errorf("environment %s not found", e.UUID))
return
}
default:
apiErrorResponse(w, "invalid action", http.StatusBadRequest, fmt.Errorf("invalid action %s", e.Action))
return
}
// Return serialized response
log.Debug().Msgf("Environment action %s completed: %s", e.Action, msgReturn)
h.AuditLog.EnvAction(ctx[ctxUser], e.Action+" - "+e.Name, strings.Split(r.RemoteAddr, ":")[0], auditlog.NoEnvironment)
utils.HTTPResponse(w, utils.JSONApplicationUTF8, http.StatusOK, types.ApiGenericResponse{Message: msgReturn})
}
6 changes: 6 additions & 0 deletions cmd/api/handlers/handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ type HandlersApi struct {
ApiConfig *config.APIConfiguration
DebugHTTP *zerolog.Logger
DebugHTTPConfig *config.YAMLConfigurationDebug
OsqueryValues config.YAMLConfigurationOsquery
}

type HandlersOption func(*HandlersApi)
Expand Down Expand Up @@ -111,6 +112,11 @@ func WithAuditLog(auditLog *auditlog.AuditLogManager) HandlersOption {
h.AuditLog = auditLog
}
}
func WithOsqueryValues(values config.YAMLConfigurationOsquery) HandlersOption {
return func(h *HandlersApi) {
h.OsqueryValues = values
}
}

func WithDebugHTTP(cfg *config.YAMLConfigurationDebug) HandlersOption {
return func(h *HandlersApi) {
Expand Down
6 changes: 6 additions & 0 deletions cmd/api/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,8 @@ func osctrlAPIService() {
handlers.WithName(serviceName),
handlers.WithAuditLog(auditLog),
handlers.WithDebugHTTP(flagParams.Debug),
handlers.WithOsqueryValues(*flagParams.Osquery),

)

// ///////////////////////// API
Expand Down Expand Up @@ -356,6 +358,10 @@ func osctrlAPIService() {
muxAPI.Handle(
"GET "+_apiPath(apiEnvironmentsPath),
handlerAuthCheck(http.HandlerFunc(handlersApi.EnvironmentsHandler), flagParams.Service.Auth, flagParams.JWT.JWTSecret))
muxAPI.Handle(
"POST "+_apiPath(apiEnvironmentsPath),
handlerAuthCheck(http.HandlerFunc(handlersApi.EnvActionsHandler), flagParams.Service.Auth, flagParams.JWT.JWTSecret))

muxAPI.Handle(
"GET "+_apiPath(apiEnvironmentsPath)+"/{env}",
handlerAuthCheck(http.HandlerFunc(handlersApi.EnvironmentHandler), flagParams.Service.Auth, flagParams.JWT.JWTSecret))
Expand Down
10 changes: 10 additions & 0 deletions pkg/types/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,16 @@ type ApiActionsRequest struct {
DebPkgURL string `json:"url_deb_pkg"`
}

// ApiEnvRequest to receive environment action requests
type ApiEnvRequest struct {
Action string `json:"action"`
Name string `json:"name"`
UUID string `json:"uuid"`
Hostname string `json:"hostname"`
Icon string `json:"icon"`
Type string `json:"type"`
}

// ApiTagsRequest to receive tag requests
type ApiTagsRequest struct {
Name string `json:"name"`
Expand Down