Skip to content
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
46 changes: 25 additions & 21 deletions admin/handlers/post.go
Original file line number Diff line number Diff line change
Expand Up @@ -1440,56 +1440,60 @@ func (h *HandlersAdmin) TagsPOSTHandler(w http.ResponseWriter, r *http.Request)
return
}
switch t.Action {
case "add":
// FIXME password complexity?
if h.Tags.Exists(t.Name) {
case tags.ActionAdd:
if h.Tags.ExistsByEnv(t.Name, env.ID) {
adminErrorResponse(w, "error adding tag", http.StatusInternalServerError, fmt.Errorf("tag %s already exists", t.Name))
h.Inc(metricAdminErr)
return
}
// Prepare user to create
if err := h.Tags.NewTag(t.Name, t.Description, t.Color, t.Icon, ctx[sessions.CtxUser], env.ID, false, t.TagType); err != nil {
adminErrorResponse(w, "error with new tag", http.StatusInternalServerError, err)
h.Inc(metricAdminErr)
return
}
adminOKResponse(w, "tag added successfully")
case "edit":
if t.Description != "" {
if err := h.Tags.ChangeDescription(t.Name, t.Description, env.ID); err != nil {
case tags.ActionEdit:
tag, err := h.Tags.Get(t.Name, env.ID)
if err != nil {
adminErrorResponse(w, "error getting tag", http.StatusInternalServerError, err)
h.Inc(metricAdminErr)
return
}
if t.Description != "" && t.Description != tag.Description {
if err := h.Tags.ChangeDescription(&tag, t.Description); err != nil {
adminErrorResponse(w, "error changing description", http.StatusInternalServerError, err)
h.Inc(metricAdminErr)
return
}
}
if t.Icon != "" {
if err := h.Tags.ChangeIcon(t.Name, t.Icon, env.ID); err != nil {
if t.Icon != "" && t.Icon != tag.Icon {
if err := h.Tags.ChangeIcon(&tag, t.Icon); err != nil {
adminErrorResponse(w, "error changing icon", http.StatusInternalServerError, err)
h.Inc(metricAdminErr)
return
}
}
if t.Color != "" {
if err := h.Tags.ChangeColor(t.Name, t.Color, env.ID); err != nil {
if t.Color != "" && t.Color != tag.Color {
if err := h.Tags.ChangeColor(&tag, t.Color); err != nil {
adminErrorResponse(w, "error changing color", http.StatusInternalServerError, err)
h.Inc(metricAdminErr)
return
}
}
adminOKResponse(w, "tag updated successfully")
case "remove":
if t.Name == ctx[sessions.CtxUser] {
adminErrorResponse(w, "not a good idea", http.StatusInternalServerError, fmt.Errorf("attempt to remove tag %s", t.Name))
h.Inc(metricAdminErr)
return
}
if h.Tags.Exists(t.Name) {
if err := h.Tags.Delete(t.Name, env.ID); err != nil {
adminErrorResponse(w, "error removing tag", http.StatusInternalServerError, err)
if t.TagType != tag.TagType {
if err := h.Tags.ChangeTagType(&tag, t.TagType); err != nil {
adminErrorResponse(w, "error changing tag type", http.StatusInternalServerError, err)
h.Inc(metricAdminErr)
return
}
}
adminOKResponse(w, "tag updated successfully")
case tags.ActionRemove:
if err := h.Tags.DeleteGet(t.Name, env.ID); err != nil {
adminErrorResponse(w, "error removing tag", http.StatusInternalServerError, err)
h.Inc(metricAdminErr)
return
}
adminOKResponse(w, "tag removed successfully")
}
// Serialize and send response
Expand Down
116 changes: 114 additions & 2 deletions api/handlers/tags.go
Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
package handlers

import (
"encoding/json"
"fmt"
"net/http"

"github.com/jmpsec/osctrl/settings"
"github.com/jmpsec/osctrl/tags"
"github.com/jmpsec/osctrl/types"
"github.com/jmpsec/osctrl/users"
"github.com/jmpsec/osctrl/utils"
"github.com/rs/zerolog/log"
)

// TagsHandler - GET Handler for multiple JSON tags
// AllTagsHandler - GET Handler for all JSON tags
func (h *HandlersApi) AllTagsHandler(w http.ResponseWriter, r *http.Request) {
h.Inc(metricAPITagsReq)
utils.DebugHTTPDump(r, h.Settings.DebugHTTP(settings.ServiceAPI, settings.NoEnvironmentID), false)
Expand Down Expand Up @@ -47,7 +50,7 @@ func (h *HandlersApi) TagsEnvHandler(w http.ResponseWriter, r *http.Request) {
h.Inc(metricAPIEnvsErr)
return
}
// Get environment by name
// Get environment by UUID
env, err := h.Envs.GetByUUID(envVar)
if err != nil {
if err.Error() == "record not found" {
Expand Down Expand Up @@ -79,3 +82,112 @@ func (h *HandlersApi) TagsEnvHandler(w http.ResponseWriter, r *http.Request) {
utils.HTTPResponse(w, utils.JSONApplicationUTF8, http.StatusOK, tags)
h.Inc(metricAPITagsOK)
}

// TagsActionHandler - POST Handler to create, update or delete tags
func (h *HandlersApi) TagsActionHandler(w http.ResponseWriter, r *http.Request) {
h.Inc(metricAPITagsReq)
utils.DebugHTTPDump(r, h.Settings.DebugHTTP(settings.ServiceAPI, settings.NoEnvironmentID), false)
// Extract environment
envVar := r.PathValue("env")
if envVar == "" {
apiErrorResponse(w, "error getting environment", http.StatusBadRequest, nil)
h.Inc(metricAPIEnvsErr)
return
}
// Get environment by UUID
env, err := h.Envs.GetByUUID(envVar)
if err != nil {
if err.Error() == "record not found" {
apiErrorResponse(w, "environment not found", http.StatusNotFound, err)
} else {
apiErrorResponse(w, "error getting environment", http.StatusInternalServerError, err)
}
h.Inc(metricAPIEnvsErr)
return
}
// 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]))
h.Inc(metricAPITagsErr)
return
}
// Extract action
actionVar := r.PathValue("action")
if actionVar == "" {
apiErrorResponse(w, "error getting action", http.StatusBadRequest, nil)
h.Inc(metricAPIEnvsErr)
return
}
var t types.ApiTagsRequest
// Parse request JSON body
if err := json.NewDecoder(r.Body).Decode(&t); err != nil {
apiErrorResponse(w, "error parsing POST body", http.StatusInternalServerError, err)
h.Inc(metricAPITagsErr)
return
}
var returnData string
switch actionVar {
case tags.ActionAdd:
if h.Tags.ExistsByEnv(t.Name, env.ID) {
apiErrorResponse(w, "error adding tag", http.StatusInternalServerError, fmt.Errorf("tag %s already exists", t.Name))
h.Inc(metricAPITagsErr)
return
}
if err := h.Tags.NewTag(t.Name, t.Description, t.Color, t.Icon, ctx[ctxUser], env.ID, false, t.TagType); err != nil {
apiErrorResponse(w, "error with new tag", http.StatusInternalServerError, err)
h.Inc(metricAPITagsErr)
return
}
returnData = "tag added successfully"
case tags.ActionEdit:
tag, err := h.Tags.Get(t.Name, env.ID)
if err != nil {
apiErrorResponse(w, "error getting tag", http.StatusInternalServerError, err)
h.Inc(metricAPITagsErr)
return
}
if t.Description != "" && t.Description != tag.Description {
if err := h.Tags.ChangeDescription(&tag, t.Description); err != nil {
apiErrorResponse(w, "error changing description", http.StatusInternalServerError, err)
h.Inc(metricAPITagsErr)
return
}
}
if t.Color != "" && t.Color != tag.Color {
if err := h.Tags.ChangeColor(&tag, t.Color); err != nil {
apiErrorResponse(w, "error changing color", http.StatusInternalServerError, err)
h.Inc(metricAPITagsErr)
return
}
}
if t.Icon != "" && t.Icon != tag.Icon {
if err := h.Tags.ChangeIcon(&tag, t.Icon); err != nil {
apiErrorResponse(w, "error changing icon", http.StatusInternalServerError, err)
h.Inc(metricAPITagsErr)
return
}
}
if t.TagType != tag.TagType {
if err := h.Tags.ChangeTagType(&tag, t.TagType); err != nil {
apiErrorResponse(w, "error changing tag type", http.StatusInternalServerError, err)
h.Inc(metricAPITagsErr)
return
}
}
returnData = "tag updated successfully"
case tags.ActionRemove:
if err := h.Tags.DeleteGet(t.Name, env.ID); err != nil {
apiErrorResponse(w, "error removing tag", http.StatusInternalServerError, err)
h.Inc(metricAPITagsErr)
return
}
returnData = "tag removed successfully"
}
// Serialize and serve JSON
if h.Settings.DebugService(settings.ServiceAPI) {
log.Debug().Msgf("DebugService: Returned %s", returnData)
}
utils.HTTPResponse(w, utils.JSONApplicationUTF8, http.StatusOK, types.ApiDataResponse{Data: returnData})
h.Inc(metricAPITagsOK)
}
1 change: 1 addition & 0 deletions api/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -604,6 +604,7 @@ func osctrlAPIService() {
// API: tags by environment
muxAPI.Handle("GET "+_apiPath(apiTagsPath), handlerAuthCheck(http.HandlerFunc(handlersApi.AllTagsHandler)))
muxAPI.Handle("GET "+_apiPath(apiTagsPath)+"/{env}", handlerAuthCheck(http.HandlerFunc(handlersApi.TagsEnvHandler)))
muxAPI.Handle("POST "+_apiPath(apiTagsPath)+"/{env}/{action}", handlerAuthCheck(http.HandlerFunc(handlersApi.TagsActionHandler)))
// API: settings by environment
muxAPI.Handle("GET "+_apiPath(apiSettingsPath), handlerAuthCheck(http.HandlerFunc(handlersApi.SettingsHandler)))
muxAPI.Handle("GET "+_apiPath(apiSettingsPath)+"/{service}", handlerAuthCheck(http.HandlerFunc(handlersApi.SettingsServiceHandler)))
Expand Down
82 changes: 77 additions & 5 deletions osctrl-api.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1559,6 +1559,67 @@ paths:
security:
- Authorization:
- admin
/tags/{env}/{action}:
post:
tags:
- tags
summary: Get tags
description: Perform tag related actions in tags by environment
operationId: TagsActionHandler
parameters:
- name: env
in: path
description: Name or UUID of the requested osctrl environment to get platforms
required: true
schema:
type: string
- name: action
in: path
description: Action to execute (add, edit, remove)
required: true
schema:
type: string
requestBody:
content:
application/json:
schema:
$ref: "#/components/schemas/ApiTagsRequest"
responses:
200:
description: successful operation
content:
application/json:
schema:
type: array
items:
type: string
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 tags
content:
application/json:
schema:
$ref: "#/components/schemas/ApiErrorResponse"
500:
description: error getting tags
content:
application/json:
schema:
$ref: "#/components/schemas/ApiErrorResponse"
security:
- Authorization:
- admin
/settings:
get:
tags:
Expand Down Expand Up @@ -1938,11 +1999,6 @@ components:
properties:
query_name:
type: string
ApiErrorResponse:
type: object
properties:
error:
type: string
APIQueryData:
type: object
CarvedFile:
Expand Down Expand Up @@ -2220,6 +2276,22 @@ components:
type: string
DebPkgURL:
type: string
ApiTagsRequest:
type: object
properties:
Name:
type: string
Description:
type: string
Color:
type: string
Icon:
type: string
EnvUUID:
type: string
TagType:
type: integer
format: uint32
securitySchemes:
Authorization:
type: http
Expand Down
Loading
Loading