Add global settings (#40)

This commit is contained in:
Jonas Plum
2022-03-13 13:45:10 +01:00
committed by GitHub
parent 86daadc73d
commit 18a4dc54e7
30 changed files with 1297 additions and 255 deletions

View File

@@ -1,8 +1,6 @@
package cmd
import (
"fmt"
"github.com/alecthomas/kong"
kongyaml "github.com/alecthomas/kong-yaml"
"github.com/coreos/go-oidc/v3/oidc"
@@ -11,8 +9,6 @@ import (
"github.com/SecurityBrewery/catalyst"
"github.com/SecurityBrewery/catalyst/bus"
"github.com/SecurityBrewery/catalyst/database"
"github.com/SecurityBrewery/catalyst/generated/model"
"github.com/SecurityBrewery/catalyst/generated/pointer"
"github.com/SecurityBrewery/catalyst/role"
"github.com/SecurityBrewery/catalyst/storage"
)
@@ -47,9 +43,7 @@ type CLI struct {
EmitterIOHost string `env:"EMITTER_IO_HOST" default:"tcp://emitter:8080"`
EmitterIORKey string `env:"EMITTER_IO_KEY" required:""`
Timeformat string `env:"TIMEFORMAT" default:"yyyy-MM-dd HH:mm:ss" help:""`
ArtifactStates []map[string]string `env:"ARTIFACT_STATES"`
InitialAPIKey string `env:"INITIAL_API_KEY"`
InitialAPIKey string `env:"INITIAL_API_KEY"`
}
func ParseCatalystConfig() (*catalyst.Config, error) {
@@ -68,19 +62,6 @@ func MapConfig(cli CLI) (*catalyst.Config, error) {
roles = append(roles, role.Explodes(cli.AuthDefaultRoles)...)
roles = role.Explodes(role.Strings(roles))
artifactStates, err := toTypes(cli.ArtifactStates)
if err != nil {
return nil, err
}
if len(artifactStates) == 0 {
artifactStates = []*model.Type{
{Icon: "mdi-help-circle-outline", ID: "unknown", Name: "Unknown", Color: pointer.String(model.TypeColorInfo)},
{Icon: "mdi-skull", ID: "malicious", Name: "Malicious", Color: pointer.String(model.TypeColorError)},
{Icon: "mdi-check", ID: "clean", Name: "Clean", Color: pointer.String(model.TypeColorSuccess)},
}
}
scopes := unique(append([]string{oidc.ScopeOpenID, "profile", "email"}, cli.OIDCScopes...))
config := &catalyst.Config{
IndexPath: cli.IndexPath,
@@ -99,49 +80,12 @@ func MapConfig(cli CLI) (*catalyst.Config, error) {
AuthDefaultRoles: roles,
AuthAdminUsers: cli.AuthAdminUsers,
},
Bus: &bus.Config{Host: cli.EmitterIOHost, Key: cli.EmitterIORKey, APIUrl: cli.CatalystAddress + "/api"},
UISettings: &model.Settings{
ArtifactStates: artifactStates,
Timeformat: cli.Timeformat,
Version: catalyst.GetVersion(),
Tier: model.SettingsTierCommunity,
},
Bus: &bus.Config{Host: cli.EmitterIOHost, Key: cli.EmitterIORKey, APIUrl: cli.CatalystAddress + "/api"},
InitialAPIKey: cli.InitialAPIKey,
}
return config, nil
}
func toTypes(params []map[string]string) ([]*model.Type, error) {
var types []*model.Type
for _, param := range params {
t := &model.Type{}
icon, iconOK := param["icon"]
if iconOK {
t.Icon = icon
}
id, idOK := param["id"]
if idOK {
t.ID = id
}
name, nameOK := param["name"]
if nameOK {
t.Name = name
}
color, ok := param["color"]
if ok {
t.Color = pointer.String(color)
}
if iconOK && idOK && nameOK {
types = append(types, t)
} else {
return nil, fmt.Errorf("incomplete type: icon, id and name need to be provided (%s)", params)
}
}
return types, nil
}
func unique(l []string) []string {
keys := make(map[string]bool)
var list []string

View File

@@ -25,6 +25,7 @@ const (
UserCollectionName = "users"
TicketTypeCollectionName = "tickettypes"
JobCollectionName = "jobs"
SettingsCollectionName = "settings"
TicketArtifactsGraphName = "Graph"
RelatedTicketsCollectionName = "related"
@@ -44,6 +45,7 @@ type Database struct {
userCollection *busdb.Collection
tickettypeCollection *busdb.Collection
jobCollection *busdb.Collection
settingsCollection *busdb.Collection
relatedCollection *busdb.Collection
// containsCollection *busdb.Collection
@@ -122,6 +124,10 @@ func New(ctx context.Context, index *index.Index, bus *bus.Bus, hooks *hooks.Hoo
if err != nil {
return nil, err
}
settingsCollection, err := arangoDB.Collection(ctx, SettingsCollectionName)
if err != nil {
return nil, err
}
hookedDB, err := busdb.NewDatabase(ctx, arangoDB, bus)
if err != nil {
@@ -142,6 +148,7 @@ func New(ctx context.Context, index *index.Index, bus *bus.Bus, hooks *hooks.Hoo
userCollection: busdb.NewCollection(userCollection, hookedDB),
tickettypeCollection: busdb.NewCollection(tickettypeCollection, hookedDB),
jobCollection: busdb.NewCollection(jobCollection, hookedDB),
settingsCollection: busdb.NewCollection(settingsCollection, hookedDB),
}
return db, nil
@@ -189,5 +196,6 @@ func (db *Database) Truncate(ctx context.Context) {
db.tickettypeCollection.Truncate(ctx)
db.jobCollection.Truncate(ctx)
db.relatedCollection.Truncate(ctx)
db.settingsCollection.Truncate(ctx)
// db.containsCollection.Truncate(ctx)
}

View File

@@ -51,6 +51,11 @@ func generateMigrations() ([]Migration, error) {
&createCollection{ID: "create-job-collection", Name: "jobs", DataType: "job", Schema: `{"properties":{"automation":{"type":"string"},"log":{"type":"string"},"payload":{},"origin":{"properties":{"artifact_origin":{"properties":{"artifact":{"type":"string"},"ticket_id":{"format":"int64","type":"integer"}},"required":["artifact","ticket_id"],"type":"object"},"task_origin":{"properties":{"playbook_id":{"type":"string"},"task_id":{"type":"string"},"ticket_id":{"format":"int64","type":"integer"}},"required":["playbook_id","task_id","ticket_id"],"type":"object"}},"type":"object"},"output":{"properties":{},"type":"object"},"running":{"type":"boolean"},"status":{"type":"string"}},"required":["automation","running","status"],"type":"object"}`},
&createDocument{ID: "create-playbook-simple", Collection: "playbooks", Document: &busdb.Keyed{Key: "simple", Doc: model.PlaybookTemplate{Name: "Simple", Yaml: SimplePlaybook}}},
&createCollection{ID: "create-settings-collection", Name: "settings", DataType: "settings", Schema: `{"type":"object","properties":{"artifactStates":{"title":"Artifact States","items":{"type":"object","properties":{"color":{"title":"Color","type":"string","enum":["error","info","success","warning"]},"icon":{"title":"Icon (https://materialdesignicons.com)","type":"string"},"id":{"title":"ID","type":"string"},"name":{"title":"Name","type":"string"}},"required":["id","name","icon"]},"type":"array"},"artifactKinds":{"title":"Artifact Kinds","items":{"type":"object","properties":{"color":{"title":"Color","type":"string","enum":["error","info","success","warning"]},"icon":{"title":"Icon (https://materialdesignicons.com)","type":"string"},"id":{"title":"ID","type":"string"},"name":{"title":"Name","type":"string"}},"required":["id","name","icon"]},"type":"array"},"timeformat":{"title":"Time Format","type":"string"}},"required":["timeformat","artifactKinds","artifactStates"]}`},
&createDocument{ID: "create-settings-global", Collection: "settings", Document: &busdb.Keyed{Key: "global", Doc: model.Settings{ArtifactStates: []*model.Type{{Icon: "mdi-help-circle-outline", ID: "unknown", Name: "Unknown", Color: pointer.String(model.TypeColorInfo)}, {Icon: "mdi-skull", ID: "malicious", Name: "Malicious", Color: pointer.String(model.TypeColorError)}, {Icon: "mdi-check", ID: "clean", Name: "Clean", Color: pointer.String(model.TypeColorSuccess)}}, ArtifactKinds: []*model.Type{{Icon: "mdi-server", ID: "asset", Name: "Asset"}, {Icon: "mdi-bullseye", ID: "ioc", Name: "IOC"}}, Timeformat: "YYYY-MM-DDThh:mm:ss"}}},
&updateSchema{ID: "update-ticket-collection", Name: "tickets", DataType: "ticket", Schema: `{"properties":{"artifacts":{"items":{"properties":{"enrichments":{"additionalProperties":{"properties":{"created":{"format":"date-time","type":"string"},"data":{"example":{"hash":"b7a067a742c20d07a7456646de89bc2d408a1153"},"properties":{},"type":"object"},"name":{"example":"hash.sha1","type":"string"}},"required":["created","data","name"],"type":"object"},"type":"object"},"name":{"example":"2.2.2.2","type":"string"},"status":{"example":"Unknown","type":"string"},"type":{"type":"string"},"kind":{"type":"string"}},"required":["name"],"type":"object"},"type":"array"},"comments":{"items":{"properties":{"created":{"format":"date-time","type":"string"},"creator":{"type":"string"},"message":{"type":"string"}},"required":["created","creator","message"],"type":"object"},"type":"array"},"created":{"format":"date-time","type":"string"},"details":{"example":{"description":"my little incident"},"properties":{},"type":"object"},"files":{"items":{"properties":{"key":{"example":"myfile","type":"string"},"name":{"example":"notes.docx","type":"string"}},"required":["key","name"],"type":"object"},"type":"array"},"modified":{"format":"date-time","type":"string"},"name":{"example":"WannyCry","type":"string"},"owner":{"example":"bob","type":"string"},"playbooks":{"additionalProperties":{"properties":{"name":{"example":"Phishing","type":"string"},"tasks":{"additionalProperties":{"properties":{"automation":{"type":"string"},"closed":{"format":"date-time","type":"string"},"created":{"format":"date-time","type":"string"},"data":{"properties":{},"type":"object"},"done":{"type":"boolean"},"join":{"example":false,"type":"boolean"},"payload":{"additionalProperties":{"type":"string"},"type":"object"},"name":{"example":"Inform user","type":"string"},"next":{"additionalProperties":{"type":"string"},"type":"object"},"owner":{"type":"string"},"schema":{"properties":{},"type":"object"},"type":{"enum":["task","input","automation"],"example":"task","type":"string"}},"required":["created","done","name","type"],"type":"object"},"type":"object"}},"required":["name","tasks"],"type":"object"},"type":"object"},"read":{"example":["bob"],"items":{"type":"string"},"type":"array"},"references":{"items":{"properties":{"href":{"example":"https://cve.mitre.org/cgi-bin/cvename.cgi?name=cve-2017-0144","type":"string"},"name":{"example":"CVE-2017-0144","type":"string"}},"required":["href","name"],"type":"object"},"type":"array"},"schema":{"example":"{}","type":"string"},"status":{"example":"open","type":"string"},"type":{"example":"incident","type":"string"},"write":{"example":["alice"],"items":{"type":"string"},"type":"array"}},"required":["created","modified","name","schema","status","type"],"type":"object"}`},
}, nil
}

View File

@@ -2,84 +2,34 @@ package database
import (
"context"
"errors"
"github.com/arangodb/go-driver"
"github.com/SecurityBrewery/catalyst/database/busdb"
"github.com/SecurityBrewery/catalyst/generated/model"
)
func toUserDataResponse(key string, doc *model.UserData) *model.UserDataResponse {
return &model.UserDataResponse{
Email: doc.Email,
ID: key,
Image: doc.Image,
Name: doc.Name,
Timeformat: doc.Timeformat,
func (db *Database) Settings(ctx context.Context) (*model.Settings, error) {
settings := &model.Settings{}
if _, err := db.settingsCollection.ReadDocument(ctx, "global", settings); err != nil {
return nil, err
}
return settings, nil
}
func (db *Database) UserDataCreate(ctx context.Context, id string, userdata *model.UserData) error {
if userdata == nil {
return errors.New("requires setting")
}
if id == "" {
return errors.New("requires username")
}
_, err := db.userdataCollection.CreateDocument(ctx, ctx, id, userdata)
return err
}
func (db *Database) UserDataGetOrCreate(ctx context.Context, id string, newUserData *model.UserData) (*model.UserDataResponse, error) {
setting, err := db.UserDataGet(ctx, id)
if err != nil {
return toUserDataResponse(id, newUserData), db.UserDataCreate(ctx, id, newUserData)
}
return setting, nil
}
func (db *Database) UserDataGet(ctx context.Context, id string) (*model.UserDataResponse, error) {
var doc model.UserData
meta, err := db.userdataCollection.ReadDocument(ctx, id, &doc)
func (db *Database) SaveSettings(ctx context.Context, settings *model.Settings) (*model.Settings, error) {
exists, err := db.settingsCollection.DocumentExists(ctx, "global")
if err != nil {
return nil, err
}
return toUserDataResponse(meta.Key, &doc), err
}
func (db *Database) UserDataList(ctx context.Context) ([]*model.UserDataResponse, error) {
query := "FOR d IN @@collection SORT d.username ASC RETURN d"
cursor, _, err := db.Query(ctx, query, map[string]interface{}{"@collection": UserDataCollectionName}, busdb.ReadOperation)
if err != nil {
return nil, err
}
defer cursor.Close()
var docs []*model.UserDataResponse
for {
var doc model.UserData
meta, err := cursor.ReadDocument(ctx, &doc)
if driver.IsNoMoreDocuments(err) {
break
} else if err != nil {
if exists {
if _, err := db.settingsCollection.ReplaceDocument(ctx, "global", settings); err != nil {
return nil, err
}
} else {
if _, err := db.settingsCollection.CreateDocument(ctx, ctx, "global", settings); err != nil {
return nil, err
}
docs = append(docs, toUserDataResponse(meta.Key, &doc))
}
return docs, err
}
func (db *Database) UserDataUpdate(ctx context.Context, id string, userdata *model.UserData) (*model.UserDataResponse, error) {
var doc model.UserData
ctx = driver.WithReturnNew(ctx, &doc)
meta, err := db.userdataCollection.ReplaceDocument(ctx, id, userdata)
if err != nil {
return nil, err
}
return toUserDataResponse(meta.Key, &doc), nil
return settings, nil
}

85
database/userdata.go Normal file
View File

@@ -0,0 +1,85 @@
package database
import (
"context"
"errors"
"github.com/arangodb/go-driver"
"github.com/SecurityBrewery/catalyst/database/busdb"
"github.com/SecurityBrewery/catalyst/generated/model"
)
func toUserDataResponse(key string, doc *model.UserData) *model.UserDataResponse {
return &model.UserDataResponse{
Email: doc.Email,
ID: key,
Image: doc.Image,
Name: doc.Name,
Timeformat: doc.Timeformat,
}
}
func (db *Database) UserDataCreate(ctx context.Context, id string, userdata *model.UserData) error {
if userdata == nil {
return errors.New("requires setting")
}
if id == "" {
return errors.New("requires username")
}
_, err := db.userdataCollection.CreateDocument(ctx, ctx, id, userdata)
return err
}
func (db *Database) UserDataGetOrCreate(ctx context.Context, id string, newUserData *model.UserData) (*model.UserDataResponse, error) {
setting, err := db.UserDataGet(ctx, id)
if err != nil {
return toUserDataResponse(id, newUserData), db.UserDataCreate(ctx, id, newUserData)
}
return setting, nil
}
func (db *Database) UserDataGet(ctx context.Context, id string) (*model.UserDataResponse, error) {
var doc model.UserData
meta, err := db.userdataCollection.ReadDocument(ctx, id, &doc)
if err != nil {
return nil, err
}
return toUserDataResponse(meta.Key, &doc), err
}
func (db *Database) UserDataList(ctx context.Context) ([]*model.UserDataResponse, error) {
query := "FOR d IN @@collection SORT d.username ASC RETURN d"
cursor, _, err := db.Query(ctx, query, map[string]interface{}{"@collection": UserDataCollectionName}, busdb.ReadOperation)
if err != nil {
return nil, err
}
defer cursor.Close()
var docs []*model.UserDataResponse
for {
var doc model.UserData
meta, err := cursor.ReadDocument(ctx, &doc)
if driver.IsNoMoreDocuments(err) {
break
} else if err != nil {
return nil, err
}
docs = append(docs, toUserDataResponse(meta.Key, &doc))
}
return docs, err
}
func (db *Database) UserDataUpdate(ctx context.Context, id string, userdata *model.UserData) (*model.UserDataResponse, error) {
var doc model.UserData
ctx = driver.WithReturnNew(ctx, &doc)
meta, err := db.userdataCollection.ReplaceDocument(ctx, id, userdata)
if err != nil {
return nil, err
}
return toUserDataResponse(meta.Key, &doc), nil
}

View File

@@ -10,6 +10,7 @@ definitions:
properties:
name: { type: string, example: "2.2.2.2" }
type: { type: string }
kind: { type: string }
status: { type: string, example: "Unknown" }
enrichments: { type: object, additionalProperties: { $ref: "#/definitions/Enrichment" } }

View File

@@ -10,7 +10,7 @@ paths:
responses:
"200":
description: "successful operation"
schema: { $ref: "#/definitions/Settings" }
schema: { $ref: "#/definitions/SettingsResponse" }
examples:
test:
version: "0.0.0-test"
@@ -21,31 +21,81 @@ paths:
- { icon: "mdi-radioactive", id: "incident", name: "Incidents", default_template: "default", default_playbooks: [ ] }
- { icon: "mdi-fingerprint", id: "investigation", name: "Forensic Investigations", default_template: "default", default_playbooks: [ ] }
- { icon: "mdi-target", id: "hunt", name: "Threat Hunting", default_template: "default", default_playbooks: [ ] }
artifactKinds:
- { icon: "mdi-server", id: "asset", name: "Asset" }
- { icon: "mdi-bullseye", id: "ioc", name: "IOC" }
artifactStates:
- { icon: "mdi-help-circle-outline", id: "unknown", name: "Unknown", color: "info" }
- { icon: "mdi-skull", id: "malicious", name: "Malicious", color: "error" }
- { icon: "mdi-check", id: "clean", name: "Clean", color: "success" }
roles: [
"admin:backup:read", "admin:backup:restore", "admin:group:write", "admin:job:read", "admin:job:write",
"admin:log:read", "admin:ticket:delete", "admin:user:write", "admin:userdata:read",
"admin:userdata:write", "analyst:automation:read",
"analyst:currentsettings:write", "analyst:currentuser:read", "analyst:currentuserdata:read",
"analyst:file", "analyst:group:read", "analyst:playbook:read", "analyst:rule:read",
"analyst:settings:read", "analyst:template:read", "analyst:ticket:read", "analyst:ticket:write",
"analyst:tickettype:read", "analyst:user:read", "engineer:automation:write",
"engineer:playbook:write", "engineer:rule:write", "engineer:template:write",
"engineer:tickettype:write" ]
"admin:backup:read", "admin:backup:restore", "admin:group:write", "admin:job:read", "admin:job:write",
"admin:log:read", "admin:settings:write", "admin:ticket:delete", "admin:user:write", "admin:userdata:read",
"admin:userdata:write", "analyst:automation:read",
"analyst:currentsettings:write", "analyst:currentuser:read", "analyst:currentuserdata:read",
"analyst:file", "analyst:group:read", "analyst:playbook:read", "analyst:rule:read",
"analyst:settings:read", "analyst:template:read", "analyst:ticket:read", "analyst:ticket:write",
"analyst:tickettype:read", "analyst:user:read", "engineer:automation:write",
"engineer:playbook:write", "engineer:rule:write", "engineer:template:write",
"engineer:tickettype:write" ]
security: [ { roles: [ "settings:read" ] } ]
post:
tags: [ "settings" ]
summary: "Save settings"
operationId: "saveSettings"
parameters:
- { name: "settings", in: "body", description: "Save settings", required: true, schema: { $ref: "#/definitions/Settings" }, x-example: { timeformat: "YYYY-MM-DDThh:mm:ss", artifactKinds: [ { icon: "mdi-server", id: "asset", name: "Asset" }, { icon: "mdi-bullseye", id: "ioc", name: "IOC" } ], artifactStates: [ { icon: "mdi-help-circle-outline", id: "unknown", name: "Unknown", color: "info" },{ icon: "mdi-skull", id: "malicious", name: "Malicious", color: "error" },{ icon: "mdi-check", id: "clean", name: "Clean", color: "success" } ] } }
responses:
"200":
description: "successful operation"
schema: { $ref: "#/definitions/SettingsResponse" }
examples:
test:
version: "0.0.0-test"
tier: community
timeformat: "YYYY-MM-DDThh:mm:ss"
ticketTypes:
- { icon: "mdi-alert", id: "alert", name: "Alerts", default_template: "default", default_playbooks: [ ] }
- { icon: "mdi-radioactive", id: "incident", name: "Incidents", default_template: "default", default_playbooks: [ ] }
- { icon: "mdi-fingerprint", id: "investigation", name: "Forensic Investigations", default_template: "default", default_playbooks: [ ] }
- { icon: "mdi-target", id: "hunt", name: "Threat Hunting", default_template: "default", default_playbooks: [ ] }
artifactKinds:
- { icon: "mdi-server", id: "asset", name: "Asset" }
- { icon: "mdi-bullseye", id: "ioc", name: "IOC" }
artifactStates:
- { icon: "mdi-help-circle-outline", id: "unknown", name: "Unknown", color: "info" }
- { icon: "mdi-skull", id: "malicious", name: "Malicious", color: "error" }
- { icon: "mdi-check", id: "clean", name: "Clean", color: "success" }
roles: [
"admin:backup:read", "admin:backup:restore", "admin:group:write", "admin:job:read", "admin:job:write",
"admin:log:read", "admin:settings:write", "admin:ticket:delete", "admin:user:write", "admin:userdata:read",
"admin:userdata:write", "analyst:automation:read",
"analyst:currentsettings:write", "analyst:currentuser:read", "analyst:currentuserdata:read",
"analyst:file", "analyst:group:read", "analyst:playbook:read", "analyst:rule:read",
"analyst:settings:read", "analyst:template:read", "analyst:ticket:read", "analyst:ticket:write",
"analyst:tickettype:read", "analyst:user:read", "engineer:automation:write",
"engineer:playbook:write", "engineer:rule:write", "engineer:template:write",
"engineer:tickettype:write" ]
security: [ { roles: [ "settings:write" ] } ]
definitions:
Settings:
type: object
required: [ version, tier, timeformat, ticketTypes, artifactStates ]
required: [ timeformat, artifactKinds, artifactStates ]
properties:
timeformat: { title: "Time Format", type: string }
artifactKinds: { title: "Artifact Kinds", type: array, items: { $ref: "#/definitions/Type" } }
artifactStates: { title: "Artifact States", type: array, items: { $ref: "#/definitions/Type" } }
SettingsResponse:
type: object
required: [ version, tier, timeformat, ticketTypes, artifactKinds, artifactStates ]
properties:
version: { title: "Version", type: string }
tier: { title: "Tier", type: string, enum: [ "community", "enterprise" ] }
timeformat: { title: "Time Format", type: string }
ticketTypes: { title: "Ticket Types", type: array, items: { $ref: "#/definitions/TicketTypeResponse" } }
artifactKinds: { title: "Artifact Kinds", type: array, items: { $ref: "#/definitions/Type" } }
artifactStates: { title: "Artifact States", type: array, items: { $ref: "#/definitions/Type" } }
roles: { title: "Roles", type: array, items: { type: string } }

View File

@@ -12,7 +12,7 @@ paths:
description: "successful operation"
schema: { $ref: "#/definitions/UserResponse" }
examples:
test: { id: bob, roles: [ "admin:backup:read", "admin:backup:restore", "admin:group:write", "admin:job:read", "admin:job:write", "admin:log:read", "admin:ticket:delete", "admin:user:write", "admin:userdata:read", "admin:userdata:write", "analyst:automation:read", "analyst:currentsettings:write", "analyst:currentuser:read", "analyst:currentuserdata:read", "analyst:file", "analyst:group:read", "analyst:playbook:read", "analyst:rule:read", "analyst:settings:read", "analyst:template:read", "analyst:ticket:read", "analyst:ticket:write", "analyst:tickettype:read", "analyst:user:read", "engineer:automation:write", "engineer:playbook:write", "engineer:rule:write", "engineer:template:write", "engineer:tickettype:write" ], blocked: false, apikey: false }
test: { id: bob, roles: [ "admin:backup:read", "admin:backup:restore", "admin:group:write", "admin:job:read", "admin:job:write", "admin:log:read", "admin:settings:write", "admin:ticket:delete", "admin:user:write", "admin:userdata:read", "admin:userdata:write", "analyst:automation:read", "analyst:currentsettings:write", "analyst:currentuser:read", "analyst:currentuserdata:read", "analyst:file", "analyst:group:read", "analyst:playbook:read", "analyst:rule:read", "analyst:settings:read", "analyst:template:read", "analyst:ticket:read", "analyst:ticket:write", "analyst:tickettype:read", "analyst:user:read", "engineer:automation:write", "engineer:playbook:write", "engineer:rule:write", "engineer:template:write", "engineer:tickettype:write" ], blocked: false, apikey: false }
security: [ { roles: [ "currentuser:read" ] } ]
/users:
@@ -26,7 +26,7 @@ paths:
schema: { type: array, items: { $ref: "#/definitions/UserResponse" } }
examples:
test:
- { id: bob, blocked: false, roles: [ "admin:backup:read", "admin:backup:restore", "admin:group:write", "admin:job:read", "admin:job:write", "admin:log:read", "admin:ticket:delete", "admin:user:write", "admin:userdata:read", "admin:userdata:write", "analyst:automation:read", "analyst:currentsettings:write", "analyst:currentuser:read", "analyst:currentuserdata:read", "analyst:file", "analyst:group:read", "analyst:playbook:read", "analyst:rule:read", "analyst:settings:read", "analyst:template:read", "analyst:ticket:read", "analyst:ticket:write", "analyst:tickettype:read", "analyst:user:read", "engineer:automation:write", "engineer:playbook:write", "engineer:rule:write", "engineer:template:write", "engineer:tickettype:write" ], apikey: false }
- { id: bob, blocked: false, roles: [ "admin:backup:read", "admin:backup:restore", "admin:group:write", "admin:job:read", "admin:job:write", "admin:log:read", "admin:settings:write", "admin:ticket:delete", "admin:user:write", "admin:userdata:read", "admin:userdata:write", "analyst:automation:read", "analyst:currentsettings:write", "analyst:currentuser:read", "analyst:currentuserdata:read", "analyst:file", "analyst:group:read", "analyst:playbook:read", "analyst:rule:read", "analyst:settings:read", "analyst:template:read", "analyst:ticket:read", "analyst:ticket:write", "analyst:tickettype:read", "analyst:user:read", "engineer:automation:write", "engineer:playbook:write", "engineer:rule:write", "engineer:template:write", "engineer:tickettype:write" ], apikey: false }
- { id: script, roles: [ "analyst:automation:read", "analyst:currentsettings:write", "analyst:currentuser:read", "analyst:currentuserdata:read", "analyst:file", "analyst:group:read", "analyst:playbook:read", "analyst:rule:read", "analyst:settings:read", "analyst:template:read", "analyst:ticket:read", "analyst:ticket:write", "analyst:tickettype:read", "analyst:user:read", "engineer:automation:write", "engineer:playbook:write", "engineer:rule:write", "engineer:template:write", "engineer:tickettype:write" ], blocked: false, apikey: true }
security: [ { roles: [ "user:read" ] } ]
post:
@@ -70,7 +70,7 @@ paths:
examples:
test:
id: bob
roles: [ "admin:backup:read", "admin:backup:restore", "admin:group:write", "admin:job:read", "admin:job:write", "admin:log:read", "admin:ticket:delete", "admin:user:write", "admin:userdata:read", "admin:userdata:write", "analyst:automation:read", "analyst:currentsettings:write", "analyst:currentuser:read", "analyst:currentuserdata:read", "analyst:file", "analyst:group:read", "analyst:playbook:read", "analyst:rule:read", "analyst:settings:read", "analyst:template:read", "analyst:ticket:read", "analyst:ticket:write", "analyst:tickettype:read", "analyst:user:read", "engineer:automation:write", "engineer:playbook:write", "engineer:rule:write", "engineer:template:write", "engineer:tickettype:write" ]
roles: [ "admin:backup:read", "admin:backup:restore", "admin:group:write", "admin:job:read", "admin:job:write", "admin:log:read", "admin:settings:write", "admin:ticket:delete", "admin:user:write", "admin:userdata:read", "admin:userdata:write", "analyst:automation:read", "analyst:currentsettings:write", "analyst:currentuser:read", "analyst:currentuserdata:read", "analyst:file", "analyst:group:read", "analyst:playbook:read", "analyst:rule:read", "analyst:settings:read", "analyst:template:read", "analyst:ticket:read", "analyst:ticket:write", "analyst:tickettype:read", "analyst:user:read", "engineer:automation:write", "engineer:playbook:write", "engineer:rule:write", "engineer:template:write", "engineer:tickettype:write" ]
apikey: false
blocked: false
security: [ { roles: [ "user:write" ] } ]

View File

@@ -29,7 +29,8 @@ type Service interface {
GetPlaybook(context.Context, string) (*model.PlaybookTemplateResponse, error)
UpdatePlaybook(context.Context, string, *model.PlaybookTemplateForm) (*model.PlaybookTemplateResponse, error)
DeletePlaybook(context.Context, string) error
GetSettings(context.Context) (*model.Settings, error)
GetSettings(context.Context) (*model.SettingsResponse, error)
SaveSettings(context.Context, *model.Settings) (*model.SettingsResponse, error)
GetStatistics(context.Context) (*model.Statistics, error)
ListTasks(context.Context) ([]*model.TaskWithContext, error)
ListTemplates(context.Context) ([]*model.TicketTemplateResponse, error)
@@ -101,6 +102,7 @@ func NewServer(service Service, roleAuth func([]string) func(http.Handler) http.
r.With(roleAuth([]string{"playbook:write"})).Put("/playbooks/{id}", s.updatePlaybookHandler)
r.With(roleAuth([]string{"playbook:write"})).Delete("/playbooks/{id}", s.deletePlaybookHandler)
r.With(roleAuth([]string{"settings:read"})).Get("/settings", s.getSettingsHandler)
r.With(roleAuth([]string{"settings:write"})).Post("/settings", s.saveSettingsHandler)
r.With(roleAuth([]string{"ticket:read"})).Get("/statistics", s.getStatisticsHandler)
r.With(roleAuth([]string{"ticket:read"})).Get("/tasks", s.listTasksHandler)
r.With(roleAuth([]string{"template:read"})).Get("/templates", s.listTemplatesHandler)
@@ -375,6 +377,27 @@ func (s *server) getSettingsHandler(w http.ResponseWriter, r *http.Request) {
response(w, result, err)
}
func (s *server) saveSettingsHandler(w http.ResponseWriter, r *http.Request) {
body, err := io.ReadAll(r.Body)
if err != nil {
JSONError(w, err)
return
}
if validateSchema(body, model.SettingsSchema, w) {
return
}
var settingsP *model.Settings
if err := parseBody(body, &settingsP); err != nil {
JSONError(w, err)
return
}
result, err := s.service.SaveSettings(r.Context(), settingsP)
response(w, result, err)
}
func (s *server) getStatisticsHandler(w http.ResponseWriter, r *http.Request) {
result, err := s.service.GetStatistics(r.Context())
response(w, result, err)

View File

@@ -68,7 +68,7 @@ var Tests = []struct {
Args: Args{Method: "Get", URL: "/currentuser"},
Want: Want{
Status: 200,
Body: map[string]interface{}{"apikey": false, "blocked": false, "id": "bob", "roles": []interface{}{"admin:backup:read", "admin:backup:restore", "admin:group:write", "admin:job:read", "admin:job:write", "admin:log:read", "admin:ticket:delete", "admin:user:write", "admin:userdata:read", "admin:userdata:write", "analyst:automation:read", "analyst:currentsettings:write", "analyst:currentuser:read", "analyst:currentuserdata:read", "analyst:file", "analyst:group:read", "analyst:playbook:read", "analyst:rule:read", "analyst:settings:read", "analyst:template:read", "analyst:ticket:read", "analyst:ticket:write", "analyst:tickettype:read", "analyst:user:read", "engineer:automation:write", "engineer:playbook:write", "engineer:rule:write", "engineer:template:write", "engineer:tickettype:write"}},
Body: map[string]interface{}{"apikey": false, "blocked": false, "id": "bob", "roles": []interface{}{"admin:backup:read", "admin:backup:restore", "admin:group:write", "admin:job:read", "admin:job:write", "admin:log:read", "admin:settings:write", "admin:ticket:delete", "admin:user:write", "admin:userdata:read", "admin:userdata:write", "analyst:automation:read", "analyst:currentsettings:write", "analyst:currentuser:read", "analyst:currentuserdata:read", "analyst:file", "analyst:group:read", "analyst:playbook:read", "analyst:rule:read", "analyst:settings:read", "analyst:template:read", "analyst:ticket:read", "analyst:ticket:write", "analyst:tickettype:read", "analyst:user:read", "engineer:automation:write", "engineer:playbook:write", "engineer:rule:write", "engineer:template:write", "engineer:tickettype:write"}},
},
},
@@ -185,7 +185,16 @@ var Tests = []struct {
Args: Args{Method: "Get", URL: "/settings"},
Want: Want{
Status: 200,
Body: map[string]interface{}{"artifactStates": []interface{}{map[string]interface{}{"color": "info", "icon": "mdi-help-circle-outline", "id": "unknown", "name": "Unknown"}, map[string]interface{}{"color": "error", "icon": "mdi-skull", "id": "malicious", "name": "Malicious"}, map[string]interface{}{"color": "success", "icon": "mdi-check", "id": "clean", "name": "Clean"}}, "roles": []interface{}{"admin:backup:read", "admin:backup:restore", "admin:group:write", "admin:job:read", "admin:job:write", "admin:log:read", "admin:ticket:delete", "admin:user:write", "admin:userdata:read", "admin:userdata:write", "analyst:automation:read", "analyst:currentsettings:write", "analyst:currentuser:read", "analyst:currentuserdata:read", "analyst:file", "analyst:group:read", "analyst:playbook:read", "analyst:rule:read", "analyst:settings:read", "analyst:template:read", "analyst:ticket:read", "analyst:ticket:write", "analyst:tickettype:read", "analyst:user:read", "engineer:automation:write", "engineer:playbook:write", "engineer:rule:write", "engineer:template:write", "engineer:tickettype:write"}, "ticketTypes": []interface{}{map[string]interface{}{"default_playbooks": []interface{}{}, "default_template": "default", "icon": "mdi-alert", "id": "alert", "name": "Alerts"}, map[string]interface{}{"default_playbooks": []interface{}{}, "default_template": "default", "icon": "mdi-radioactive", "id": "incident", "name": "Incidents"}, map[string]interface{}{"default_playbooks": []interface{}{}, "default_template": "default", "icon": "mdi-fingerprint", "id": "investigation", "name": "Forensic Investigations"}, map[string]interface{}{"default_playbooks": []interface{}{}, "default_template": "default", "icon": "mdi-target", "id": "hunt", "name": "Threat Hunting"}}, "tier": "community", "timeformat": "YYYY-MM-DDThh:mm:ss", "version": "0.0.0-test"},
Body: map[string]interface{}{"artifactKinds": []interface{}{map[string]interface{}{"icon": "mdi-server", "id": "asset", "name": "Asset"}, map[string]interface{}{"icon": "mdi-bullseye", "id": "ioc", "name": "IOC"}}, "artifactStates": []interface{}{map[string]interface{}{"color": "info", "icon": "mdi-help-circle-outline", "id": "unknown", "name": "Unknown"}, map[string]interface{}{"color": "error", "icon": "mdi-skull", "id": "malicious", "name": "Malicious"}, map[string]interface{}{"color": "success", "icon": "mdi-check", "id": "clean", "name": "Clean"}}, "roles": []interface{}{"admin:backup:read", "admin:backup:restore", "admin:group:write", "admin:job:read", "admin:job:write", "admin:log:read", "admin:settings:write", "admin:ticket:delete", "admin:user:write", "admin:userdata:read", "admin:userdata:write", "analyst:automation:read", "analyst:currentsettings:write", "analyst:currentuser:read", "analyst:currentuserdata:read", "analyst:file", "analyst:group:read", "analyst:playbook:read", "analyst:rule:read", "analyst:settings:read", "analyst:template:read", "analyst:ticket:read", "analyst:ticket:write", "analyst:tickettype:read", "analyst:user:read", "engineer:automation:write", "engineer:playbook:write", "engineer:rule:write", "engineer:template:write", "engineer:tickettype:write"}, "ticketTypes": []interface{}{map[string]interface{}{"default_playbooks": []interface{}{}, "default_template": "default", "icon": "mdi-alert", "id": "alert", "name": "Alerts"}, map[string]interface{}{"default_playbooks": []interface{}{}, "default_template": "default", "icon": "mdi-radioactive", "id": "incident", "name": "Incidents"}, map[string]interface{}{"default_playbooks": []interface{}{}, "default_template": "default", "icon": "mdi-fingerprint", "id": "investigation", "name": "Forensic Investigations"}, map[string]interface{}{"default_playbooks": []interface{}{}, "default_template": "default", "icon": "mdi-target", "id": "hunt", "name": "Threat Hunting"}}, "tier": "community", "timeformat": "YYYY-MM-DDThh:mm:ss", "version": "0.0.0-test"},
},
},
{
Name: "SaveSettings",
Args: Args{Method: "Post", URL: "/settings", Data: map[string]interface{}{"artifactKinds": []interface{}{map[string]interface{}{"icon": "mdi-server", "id": "asset", "name": "Asset"}, map[string]interface{}{"icon": "mdi-bullseye", "id": "ioc", "name": "IOC"}}, "artifactStates": []interface{}{map[string]interface{}{"color": "info", "icon": "mdi-help-circle-outline", "id": "unknown", "name": "Unknown"}, map[string]interface{}{"color": "error", "icon": "mdi-skull", "id": "malicious", "name": "Malicious"}, map[string]interface{}{"color": "success", "icon": "mdi-check", "id": "clean", "name": "Clean"}}, "timeformat": "YYYY-MM-DDThh:mm:ss"}},
Want: Want{
Status: 200,
Body: map[string]interface{}{"artifactKinds": []interface{}{map[string]interface{}{"icon": "mdi-server", "id": "asset", "name": "Asset"}, map[string]interface{}{"icon": "mdi-bullseye", "id": "ioc", "name": "IOC"}}, "artifactStates": []interface{}{map[string]interface{}{"color": "info", "icon": "mdi-help-circle-outline", "id": "unknown", "name": "Unknown"}, map[string]interface{}{"color": "error", "icon": "mdi-skull", "id": "malicious", "name": "Malicious"}, map[string]interface{}{"color": "success", "icon": "mdi-check", "id": "clean", "name": "Clean"}}, "roles": []interface{}{"admin:backup:read", "admin:backup:restore", "admin:group:write", "admin:job:read", "admin:job:write", "admin:log:read", "admin:settings:write", "admin:ticket:delete", "admin:user:write", "admin:userdata:read", "admin:userdata:write", "analyst:automation:read", "analyst:currentsettings:write", "analyst:currentuser:read", "analyst:currentuserdata:read", "analyst:file", "analyst:group:read", "analyst:playbook:read", "analyst:rule:read", "analyst:settings:read", "analyst:template:read", "analyst:ticket:read", "analyst:ticket:write", "analyst:tickettype:read", "analyst:user:read", "engineer:automation:write", "engineer:playbook:write", "engineer:rule:write", "engineer:template:write", "engineer:tickettype:write"}, "ticketTypes": []interface{}{map[string]interface{}{"default_playbooks": []interface{}{}, "default_template": "default", "icon": "mdi-alert", "id": "alert", "name": "Alerts"}, map[string]interface{}{"default_playbooks": []interface{}{}, "default_template": "default", "icon": "mdi-radioactive", "id": "incident", "name": "Incidents"}, map[string]interface{}{"default_playbooks": []interface{}{}, "default_template": "default", "icon": "mdi-fingerprint", "id": "investigation", "name": "Forensic Investigations"}, map[string]interface{}{"default_playbooks": []interface{}{}, "default_template": "default", "icon": "mdi-target", "id": "hunt", "name": "Threat Hunting"}}, "tier": "community", "timeformat": "YYYY-MM-DDThh:mm:ss", "version": "0.0.0-test"},
},
},
@@ -545,7 +554,7 @@ var Tests = []struct {
Args: Args{Method: "Get", URL: "/users"},
Want: Want{
Status: 200,
Body: []interface{}{map[string]interface{}{"apikey": false, "blocked": false, "id": "bob", "roles": []interface{}{"admin:backup:read", "admin:backup:restore", "admin:group:write", "admin:job:read", "admin:job:write", "admin:log:read", "admin:ticket:delete", "admin:user:write", "admin:userdata:read", "admin:userdata:write", "analyst:automation:read", "analyst:currentsettings:write", "analyst:currentuser:read", "analyst:currentuserdata:read", "analyst:file", "analyst:group:read", "analyst:playbook:read", "analyst:rule:read", "analyst:settings:read", "analyst:template:read", "analyst:ticket:read", "analyst:ticket:write", "analyst:tickettype:read", "analyst:user:read", "engineer:automation:write", "engineer:playbook:write", "engineer:rule:write", "engineer:template:write", "engineer:tickettype:write"}}, map[string]interface{}{"apikey": true, "blocked": false, "id": "script", "roles": []interface{}{"analyst:automation:read", "analyst:currentsettings:write", "analyst:currentuser:read", "analyst:currentuserdata:read", "analyst:file", "analyst:group:read", "analyst:playbook:read", "analyst:rule:read", "analyst:settings:read", "analyst:template:read", "analyst:ticket:read", "analyst:ticket:write", "analyst:tickettype:read", "analyst:user:read", "engineer:automation:write", "engineer:playbook:write", "engineer:rule:write", "engineer:template:write", "engineer:tickettype:write"}}},
Body: []interface{}{map[string]interface{}{"apikey": false, "blocked": false, "id": "bob", "roles": []interface{}{"admin:backup:read", "admin:backup:restore", "admin:group:write", "admin:job:read", "admin:job:write", "admin:log:read", "admin:settings:write", "admin:ticket:delete", "admin:user:write", "admin:userdata:read", "admin:userdata:write", "analyst:automation:read", "analyst:currentsettings:write", "analyst:currentuser:read", "analyst:currentuserdata:read", "analyst:file", "analyst:group:read", "analyst:playbook:read", "analyst:rule:read", "analyst:settings:read", "analyst:template:read", "analyst:ticket:read", "analyst:ticket:write", "analyst:tickettype:read", "analyst:user:read", "engineer:automation:write", "engineer:playbook:write", "engineer:rule:write", "engineer:template:write", "engineer:tickettype:write"}}, map[string]interface{}{"apikey": true, "blocked": false, "id": "script", "roles": []interface{}{"analyst:automation:read", "analyst:currentsettings:write", "analyst:currentuser:read", "analyst:currentuserdata:read", "analyst:file", "analyst:group:read", "analyst:playbook:read", "analyst:rule:read", "analyst:settings:read", "analyst:template:read", "analyst:ticket:read", "analyst:ticket:write", "analyst:tickettype:read", "analyst:user:read", "engineer:automation:write", "engineer:playbook:write", "engineer:rule:write", "engineer:template:write", "engineer:tickettype:write"}}},
},
},
@@ -572,7 +581,7 @@ var Tests = []struct {
Args: Args{Method: "Put", URL: "/users/bob", Data: map[string]interface{}{"apikey": false, "blocked": false, "id": "syncscript", "roles": []interface{}{"analyst", "admin"}}},
Want: Want{
Status: 200,
Body: map[string]interface{}{"apikey": false, "blocked": false, "id": "bob", "roles": []interface{}{"admin:backup:read", "admin:backup:restore", "admin:group:write", "admin:job:read", "admin:job:write", "admin:log:read", "admin:ticket:delete", "admin:user:write", "admin:userdata:read", "admin:userdata:write", "analyst:automation:read", "analyst:currentsettings:write", "analyst:currentuser:read", "analyst:currentuserdata:read", "analyst:file", "analyst:group:read", "analyst:playbook:read", "analyst:rule:read", "analyst:settings:read", "analyst:template:read", "analyst:ticket:read", "analyst:ticket:write", "analyst:tickettype:read", "analyst:user:read", "engineer:automation:write", "engineer:playbook:write", "engineer:rule:write", "engineer:template:write", "engineer:tickettype:write"}},
Body: map[string]interface{}{"apikey": false, "blocked": false, "id": "bob", "roles": []interface{}{"admin:backup:read", "admin:backup:restore", "admin:group:write", "admin:job:read", "admin:job:write", "admin:log:read", "admin:settings:write", "admin:ticket:delete", "admin:user:write", "admin:userdata:read", "admin:userdata:write", "analyst:automation:read", "analyst:currentsettings:write", "analyst:currentuser:read", "analyst:currentuserdata:read", "analyst:file", "analyst:group:read", "analyst:playbook:read", "analyst:rule:read", "analyst:settings:read", "analyst:template:read", "analyst:ticket:read", "analyst:ticket:write", "analyst:tickettype:read", "analyst:user:read", "engineer:automation:write", "engineer:playbook:write", "engineer:rule:write", "engineer:template:write", "engineer:tickettype:write"}},
},
},

View File

@@ -225,7 +225,7 @@
"apikey" : false,
"blocked" : false,
"id" : "bob",
"roles" : [ "admin:backup:read", "admin:backup:restore", "admin:group:write", "admin:job:read", "admin:job:write", "admin:log:read", "admin:ticket:delete", "admin:user:write", "admin:userdata:read", "admin:userdata:write", "analyst:automation:read", "analyst:currentsettings:write", "analyst:currentuser:read", "analyst:currentuserdata:read", "analyst:file", "analyst:group:read", "analyst:playbook:read", "analyst:rule:read", "analyst:settings:read", "analyst:template:read", "analyst:ticket:read", "analyst:ticket:write", "analyst:tickettype:read", "analyst:user:read", "engineer:automation:write", "engineer:playbook:write", "engineer:rule:write", "engineer:template:write", "engineer:tickettype:write" ]
"roles" : [ "admin:backup:read", "admin:backup:restore", "admin:group:write", "admin:job:read", "admin:job:write", "admin:log:read", "admin:settings:write", "admin:ticket:delete", "admin:user:write", "admin:userdata:read", "admin:userdata:write", "analyst:automation:read", "analyst:currentsettings:write", "analyst:currentuser:read", "analyst:currentuserdata:read", "analyst:file", "analyst:group:read", "analyst:playbook:read", "analyst:rule:read", "analyst:settings:read", "analyst:template:read", "analyst:ticket:read", "analyst:ticket:write", "analyst:tickettype:read", "analyst:user:read", "engineer:automation:write", "engineer:playbook:write", "engineer:rule:write", "engineer:template:write", "engineer:tickettype:write" ]
}
}
},
@@ -1143,11 +1143,20 @@
"content" : {
"application/json" : {
"schema" : {
"$ref" : "#/components/schemas/Settings"
"$ref" : "#/components/schemas/SettingsResponse"
}
},
"test" : {
"example" : {
"artifactKinds" : [ {
"icon" : "mdi-server",
"id" : "asset",
"name" : "Asset"
}, {
"icon" : "mdi-bullseye",
"id" : "ioc",
"name" : "IOC"
} ],
"artifactStates" : [ {
"color" : "info",
"icon" : "mdi-help-circle-outline",
@@ -1164,7 +1173,7 @@
"id" : "clean",
"name" : "Clean"
} ],
"roles" : [ "admin:backup:read", "admin:backup:restore", "admin:group:write", "admin:job:read", "admin:job:write", "admin:log:read", "admin:ticket:delete", "admin:user:write", "admin:userdata:read", "admin:userdata:write", "analyst:automation:read", "analyst:currentsettings:write", "analyst:currentuser:read", "analyst:currentuserdata:read", "analyst:file", "analyst:group:read", "analyst:playbook:read", "analyst:rule:read", "analyst:settings:read", "analyst:template:read", "analyst:ticket:read", "analyst:ticket:write", "analyst:tickettype:read", "analyst:user:read", "engineer:automation:write", "engineer:playbook:write", "engineer:rule:write", "engineer:template:write", "engineer:tickettype:write" ],
"roles" : [ "admin:backup:read", "admin:backup:restore", "admin:group:write", "admin:job:read", "admin:job:write", "admin:log:read", "admin:settings:write", "admin:ticket:delete", "admin:user:write", "admin:userdata:read", "admin:userdata:write", "analyst:automation:read", "analyst:currentsettings:write", "analyst:currentuser:read", "analyst:currentuserdata:read", "analyst:file", "analyst:group:read", "analyst:playbook:read", "analyst:rule:read", "analyst:settings:read", "analyst:template:read", "analyst:ticket:read", "analyst:ticket:write", "analyst:tickettype:read", "analyst:user:read", "engineer:automation:write", "engineer:playbook:write", "engineer:rule:write", "engineer:template:write", "engineer:tickettype:write" ],
"ticketTypes" : [ {
"default_playbooks" : [ ],
"default_template" : "default",
@@ -1204,6 +1213,96 @@
} ],
"summary" : "Get settings",
"tags" : [ "settings" ]
},
"post" : {
"operationId" : "saveSettings",
"requestBody" : {
"content" : {
"application/json" : {
"schema" : {
"$ref" : "#/components/schemas/Settings"
}
}
},
"description" : "Save settings",
"required" : true
},
"responses" : {
"200" : {
"content" : {
"application/json" : {
"schema" : {
"$ref" : "#/components/schemas/SettingsResponse"
}
},
"test" : {
"example" : {
"artifactKinds" : [ {
"icon" : "mdi-server",
"id" : "asset",
"name" : "Asset"
}, {
"icon" : "mdi-bullseye",
"id" : "ioc",
"name" : "IOC"
} ],
"artifactStates" : [ {
"color" : "info",
"icon" : "mdi-help-circle-outline",
"id" : "unknown",
"name" : "Unknown"
}, {
"color" : "error",
"icon" : "mdi-skull",
"id" : "malicious",
"name" : "Malicious"
}, {
"color" : "success",
"icon" : "mdi-check",
"id" : "clean",
"name" : "Clean"
} ],
"roles" : [ "admin:backup:read", "admin:backup:restore", "admin:group:write", "admin:job:read", "admin:job:write", "admin:log:read", "admin:settings:write", "admin:ticket:delete", "admin:user:write", "admin:userdata:read", "admin:userdata:write", "analyst:automation:read", "analyst:currentsettings:write", "analyst:currentuser:read", "analyst:currentuserdata:read", "analyst:file", "analyst:group:read", "analyst:playbook:read", "analyst:rule:read", "analyst:settings:read", "analyst:template:read", "analyst:ticket:read", "analyst:ticket:write", "analyst:tickettype:read", "analyst:user:read", "engineer:automation:write", "engineer:playbook:write", "engineer:rule:write", "engineer:template:write", "engineer:tickettype:write" ],
"ticketTypes" : [ {
"default_playbooks" : [ ],
"default_template" : "default",
"icon" : "mdi-alert",
"id" : "alert",
"name" : "Alerts"
}, {
"default_playbooks" : [ ],
"default_template" : "default",
"icon" : "mdi-radioactive",
"id" : "incident",
"name" : "Incidents"
}, {
"default_playbooks" : [ ],
"default_template" : "default",
"icon" : "mdi-fingerprint",
"id" : "investigation",
"name" : "Forensic Investigations"
}, {
"default_playbooks" : [ ],
"default_template" : "default",
"icon" : "mdi-target",
"id" : "hunt",
"name" : "Threat Hunting"
} ],
"tier" : "community",
"timeformat" : "YYYY-MM-DDThh:mm:ss",
"version" : "0.0.0-test"
}
}
},
"description" : "successful operation"
}
},
"security" : [ {
"roles" : [ "settings:write" ]
} ],
"summary" : "Save settings",
"tags" : [ "settings" ],
"x-codegen-request-body-name" : "settings"
}
},
"/statistics" : {
@@ -4993,7 +5092,7 @@
"apikey" : false,
"blocked" : false,
"id" : "bob",
"roles" : [ "admin:backup:read", "admin:backup:restore", "admin:group:write", "admin:job:read", "admin:job:write", "admin:log:read", "admin:ticket:delete", "admin:user:write", "admin:userdata:read", "admin:userdata:write", "analyst:automation:read", "analyst:currentsettings:write", "analyst:currentuser:read", "analyst:currentuserdata:read", "analyst:file", "analyst:group:read", "analyst:playbook:read", "analyst:rule:read", "analyst:settings:read", "analyst:template:read", "analyst:ticket:read", "analyst:ticket:write", "analyst:tickettype:read", "analyst:user:read", "engineer:automation:write", "engineer:playbook:write", "engineer:rule:write", "engineer:template:write", "engineer:tickettype:write" ]
"roles" : [ "admin:backup:read", "admin:backup:restore", "admin:group:write", "admin:job:read", "admin:job:write", "admin:log:read", "admin:settings:write", "admin:ticket:delete", "admin:user:write", "admin:userdata:read", "admin:userdata:write", "analyst:automation:read", "analyst:currentsettings:write", "analyst:currentuser:read", "analyst:currentuserdata:read", "analyst:file", "analyst:group:read", "analyst:playbook:read", "analyst:rule:read", "analyst:settings:read", "analyst:template:read", "analyst:ticket:read", "analyst:ticket:write", "analyst:tickettype:read", "analyst:user:read", "engineer:automation:write", "engineer:playbook:write", "engineer:rule:write", "engineer:template:write", "engineer:tickettype:write" ]
}, {
"apikey" : true,
"blocked" : false,
@@ -5151,7 +5250,7 @@
"apikey" : false,
"blocked" : false,
"id" : "bob",
"roles" : [ "admin:backup:read", "admin:backup:restore", "admin:group:write", "admin:job:read", "admin:job:write", "admin:log:read", "admin:ticket:delete", "admin:user:write", "admin:userdata:read", "admin:userdata:write", "analyst:automation:read", "analyst:currentsettings:write", "analyst:currentuser:read", "analyst:currentuserdata:read", "analyst:file", "analyst:group:read", "analyst:playbook:read", "analyst:rule:read", "analyst:settings:read", "analyst:template:read", "analyst:ticket:read", "analyst:ticket:write", "analyst:tickettype:read", "analyst:user:read", "engineer:automation:write", "engineer:playbook:write", "engineer:rule:write", "engineer:template:write", "engineer:tickettype:write" ]
"roles" : [ "admin:backup:read", "admin:backup:restore", "admin:group:write", "admin:job:read", "admin:job:write", "admin:log:read", "admin:settings:write", "admin:ticket:delete", "admin:user:write", "admin:userdata:read", "admin:userdata:write", "analyst:automation:read", "analyst:currentsettings:write", "analyst:currentuser:read", "analyst:currentuserdata:read", "analyst:file", "analyst:group:read", "analyst:playbook:read", "analyst:rule:read", "analyst:settings:read", "analyst:template:read", "analyst:ticket:read", "analyst:ticket:write", "analyst:tickettype:read", "analyst:user:read", "engineer:automation:write", "engineer:playbook:write", "engineer:rule:write", "engineer:template:write", "engineer:tickettype:write" ]
}
}
},
@@ -5177,6 +5276,9 @@
},
"type" : "object"
},
"kind" : {
"type" : "string"
},
"name" : {
"example" : "2.2.2.2",
"type" : "string"
@@ -5793,6 +5895,37 @@
},
"Settings" : {
"properties" : {
"artifactKinds" : {
"items" : {
"$ref" : "#/components/schemas/Type"
},
"title" : "Artifact Kinds",
"type" : "array"
},
"artifactStates" : {
"items" : {
"$ref" : "#/components/schemas/Type"
},
"title" : "Artifact States",
"type" : "array"
},
"timeformat" : {
"title" : "Time Format",
"type" : "string"
}
},
"required" : [ "artifactKinds", "artifactStates", "timeformat" ],
"type" : "object"
},
"SettingsResponse" : {
"properties" : {
"artifactKinds" : {
"items" : {
"$ref" : "#/components/schemas/Type"
},
"title" : "Artifact Kinds",
"type" : "array"
},
"artifactStates" : {
"items" : {
"$ref" : "#/components/schemas/Type"
@@ -5828,7 +5961,7 @@
"type" : "string"
}
},
"required" : [ "artifactStates", "ticketTypes", "tier", "timeformat", "version" ],
"required" : [ "artifactKinds", "artifactStates", "ticketTypes", "tier", "timeformat", "version" ],
"type" : "object"
},
"Statistics" : {

View File

@@ -8,6 +8,8 @@ definitions:
additionalProperties:
$ref: '#/definitions/Enrichment'
type: object
kind:
type: string
name:
example: 2.2.2.2
type: string
@@ -501,6 +503,31 @@ definitions:
type: object
Settings:
properties:
artifactKinds:
items:
$ref: '#/definitions/Type'
title: Artifact Kinds
type: array
artifactStates:
items:
$ref: '#/definitions/Type'
title: Artifact States
type: array
timeformat:
title: Time Format
type: string
required:
- timeformat
- artifactKinds
- artifactStates
type: object
SettingsResponse:
properties:
artifactKinds:
items:
$ref: '#/definitions/Type'
title: Artifact Kinds
type: array
artifactStates:
items:
$ref: '#/definitions/Type'
@@ -533,6 +560,7 @@ definitions:
- tier
- timeformat
- ticketTypes
- artifactKinds
- artifactStates
type: object
Statistics:
@@ -1552,6 +1580,7 @@ paths:
- admin:job:read
- admin:job:write
- admin:log:read
- admin:settings:write
- admin:ticket:delete
- admin:user:write
- admin:userdata:read
@@ -2549,6 +2578,13 @@ paths:
description: successful operation
examples:
test:
artifactKinds:
- icon: mdi-server
id: asset
name: Asset
- icon: mdi-bullseye
id: ioc
name: IOC
artifactStates:
- color: info
icon: mdi-help-circle-outline
@@ -2569,6 +2605,7 @@ paths:
- admin:job:read
- admin:job:write
- admin:log:read
- admin:settings:write
- admin:ticket:delete
- admin:user:write
- admin:userdata:read
@@ -2617,13 +2654,132 @@ paths:
timeformat: YYYY-MM-DDThh:mm:ss
version: 0.0.0-test
schema:
$ref: '#/definitions/Settings'
$ref: '#/definitions/SettingsResponse'
security:
- roles:
- settings:read
summary: Get settings
tags:
- settings
post:
operationId: saveSettings
parameters:
- description: Save settings
in: body
name: settings
required: true
schema:
$ref: '#/definitions/Settings'
x-example:
artifactKinds:
- icon: mdi-server
id: asset
name: Asset
- icon: mdi-bullseye
id: ioc
name: IOC
artifactStates:
- color: info
icon: mdi-help-circle-outline
id: unknown
name: Unknown
- color: error
icon: mdi-skull
id: malicious
name: Malicious
- color: success
icon: mdi-check
id: clean
name: Clean
timeformat: YYYY-MM-DDThh:mm:ss
responses:
"200":
description: successful operation
examples:
test:
artifactKinds:
- icon: mdi-server
id: asset
name: Asset
- icon: mdi-bullseye
id: ioc
name: IOC
artifactStates:
- color: info
icon: mdi-help-circle-outline
id: unknown
name: Unknown
- color: error
icon: mdi-skull
id: malicious
name: Malicious
- color: success
icon: mdi-check
id: clean
name: Clean
roles:
- admin:backup:read
- admin:backup:restore
- admin:group:write
- admin:job:read
- admin:job:write
- admin:log:read
- admin:settings:write
- admin:ticket:delete
- admin:user:write
- admin:userdata:read
- admin:userdata:write
- analyst:automation:read
- analyst:currentsettings:write
- analyst:currentuser:read
- analyst:currentuserdata:read
- analyst:file
- analyst:group:read
- analyst:playbook:read
- analyst:rule:read
- analyst:settings:read
- analyst:template:read
- analyst:ticket:read
- analyst:ticket:write
- analyst:tickettype:read
- analyst:user:read
- engineer:automation:write
- engineer:playbook:write
- engineer:rule:write
- engineer:template:write
- engineer:tickettype:write
ticketTypes:
- default_playbooks: []
default_template: default
icon: mdi-alert
id: alert
name: Alerts
- default_playbooks: []
default_template: default
icon: mdi-radioactive
id: incident
name: Incidents
- default_playbooks: []
default_template: default
icon: mdi-fingerprint
id: investigation
name: Forensic Investigations
- default_playbooks: []
default_template: default
icon: mdi-target
id: hunt
name: Threat Hunting
tier: community
timeformat: YYYY-MM-DDThh:mm:ss
version: 0.0.0-test
schema:
$ref: '#/definitions/SettingsResponse'
security:
- roles:
- settings:write
summary: Save settings
tags:
- settings
/statistics:
get:
operationId: getStatistics
@@ -6970,6 +7126,7 @@ paths:
- admin:job:read
- admin:job:write
- admin:log:read
- admin:settings:write
- admin:ticket:delete
- admin:user:write
- admin:userdata:read
@@ -7173,6 +7330,7 @@ paths:
- admin:job:read
- admin:job:write
- admin:log:read
- admin:settings:write
- admin:ticket:delete
- admin:user:write
- admin:userdata:read

View File

@@ -225,7 +225,7 @@
"apikey" : false,
"blocked" : false,
"id" : "bob",
"roles" : [ "admin:backup:read", "admin:backup:restore", "admin:group:write", "admin:job:read", "admin:job:write", "admin:log:read", "admin:ticket:delete", "admin:user:write", "admin:userdata:read", "admin:userdata:write", "analyst:automation:read", "analyst:currentsettings:write", "analyst:currentuser:read", "analyst:currentuserdata:read", "analyst:file", "analyst:group:read", "analyst:playbook:read", "analyst:rule:read", "analyst:settings:read", "analyst:template:read", "analyst:ticket:read", "analyst:ticket:write", "analyst:tickettype:read", "analyst:user:read", "engineer:automation:write", "engineer:playbook:write", "engineer:rule:write", "engineer:template:write", "engineer:tickettype:write" ]
"roles" : [ "admin:backup:read", "admin:backup:restore", "admin:group:write", "admin:job:read", "admin:job:write", "admin:log:read", "admin:settings:write", "admin:ticket:delete", "admin:user:write", "admin:userdata:read", "admin:userdata:write", "analyst:automation:read", "analyst:currentsettings:write", "analyst:currentuser:read", "analyst:currentuserdata:read", "analyst:file", "analyst:group:read", "analyst:playbook:read", "analyst:rule:read", "analyst:settings:read", "analyst:template:read", "analyst:ticket:read", "analyst:ticket:write", "analyst:tickettype:read", "analyst:user:read", "engineer:automation:write", "engineer:playbook:write", "engineer:rule:write", "engineer:template:write", "engineer:tickettype:write" ]
}
}
},
@@ -713,11 +713,20 @@
"content" : {
"application/json" : {
"schema" : {
"$ref" : "#/components/schemas/Settings"
"$ref" : "#/components/schemas/SettingsResponse"
}
},
"test" : {
"example" : {
"artifactKinds" : [ {
"icon" : "mdi-server",
"id" : "asset",
"name" : "Asset"
}, {
"icon" : "mdi-bullseye",
"id" : "ioc",
"name" : "IOC"
} ],
"artifactStates" : [ {
"color" : "info",
"icon" : "mdi-help-circle-outline",
@@ -734,7 +743,7 @@
"id" : "clean",
"name" : "Clean"
} ],
"roles" : [ "admin:backup:read", "admin:backup:restore", "admin:group:write", "admin:job:read", "admin:job:write", "admin:log:read", "admin:ticket:delete", "admin:user:write", "admin:userdata:read", "admin:userdata:write", "analyst:automation:read", "analyst:currentsettings:write", "analyst:currentuser:read", "analyst:currentuserdata:read", "analyst:file", "analyst:group:read", "analyst:playbook:read", "analyst:rule:read", "analyst:settings:read", "analyst:template:read", "analyst:ticket:read", "analyst:ticket:write", "analyst:tickettype:read", "analyst:user:read", "engineer:automation:write", "engineer:playbook:write", "engineer:rule:write", "engineer:template:write", "engineer:tickettype:write" ],
"roles" : [ "admin:backup:read", "admin:backup:restore", "admin:group:write", "admin:job:read", "admin:job:write", "admin:log:read", "admin:settings:write", "admin:ticket:delete", "admin:user:write", "admin:userdata:read", "admin:userdata:write", "analyst:automation:read", "analyst:currentsettings:write", "analyst:currentuser:read", "analyst:currentuserdata:read", "analyst:file", "analyst:group:read", "analyst:playbook:read", "analyst:rule:read", "analyst:settings:read", "analyst:template:read", "analyst:ticket:read", "analyst:ticket:write", "analyst:tickettype:read", "analyst:user:read", "engineer:automation:write", "engineer:playbook:write", "engineer:rule:write", "engineer:template:write", "engineer:tickettype:write" ],
"ticketTypes" : [ {
"default_playbooks" : [ ],
"default_template" : "default",
@@ -774,6 +783,96 @@
} ],
"summary" : "Get settings",
"tags" : [ "settings" ]
},
"post" : {
"operationId" : "saveSettings",
"requestBody" : {
"content" : {
"application/json" : {
"schema" : {
"$ref" : "#/components/schemas/Settings"
}
}
},
"description" : "Save settings",
"required" : true
},
"responses" : {
"200" : {
"content" : {
"application/json" : {
"schema" : {
"$ref" : "#/components/schemas/SettingsResponse"
}
},
"test" : {
"example" : {
"artifactKinds" : [ {
"icon" : "mdi-server",
"id" : "asset",
"name" : "Asset"
}, {
"icon" : "mdi-bullseye",
"id" : "ioc",
"name" : "IOC"
} ],
"artifactStates" : [ {
"color" : "info",
"icon" : "mdi-help-circle-outline",
"id" : "unknown",
"name" : "Unknown"
}, {
"color" : "error",
"icon" : "mdi-skull",
"id" : "malicious",
"name" : "Malicious"
}, {
"color" : "success",
"icon" : "mdi-check",
"id" : "clean",
"name" : "Clean"
} ],
"roles" : [ "admin:backup:read", "admin:backup:restore", "admin:group:write", "admin:job:read", "admin:job:write", "admin:log:read", "admin:settings:write", "admin:ticket:delete", "admin:user:write", "admin:userdata:read", "admin:userdata:write", "analyst:automation:read", "analyst:currentsettings:write", "analyst:currentuser:read", "analyst:currentuserdata:read", "analyst:file", "analyst:group:read", "analyst:playbook:read", "analyst:rule:read", "analyst:settings:read", "analyst:template:read", "analyst:ticket:read", "analyst:ticket:write", "analyst:tickettype:read", "analyst:user:read", "engineer:automation:write", "engineer:playbook:write", "engineer:rule:write", "engineer:template:write", "engineer:tickettype:write" ],
"ticketTypes" : [ {
"default_playbooks" : [ ],
"default_template" : "default",
"icon" : "mdi-alert",
"id" : "alert",
"name" : "Alerts"
}, {
"default_playbooks" : [ ],
"default_template" : "default",
"icon" : "mdi-radioactive",
"id" : "incident",
"name" : "Incidents"
}, {
"default_playbooks" : [ ],
"default_template" : "default",
"icon" : "mdi-fingerprint",
"id" : "investigation",
"name" : "Forensic Investigations"
}, {
"default_playbooks" : [ ],
"default_template" : "default",
"icon" : "mdi-target",
"id" : "hunt",
"name" : "Threat Hunting"
} ],
"tier" : "community",
"timeformat" : "YYYY-MM-DDThh:mm:ss",
"version" : "0.0.0-test"
}
}
},
"description" : "successful operation"
}
},
"security" : [ {
"roles" : [ "settings:write" ]
} ],
"summary" : "Save settings",
"tags" : [ "settings" ],
"x-codegen-request-body-name" : "settings"
}
},
"/statistics" : {
@@ -4563,7 +4662,7 @@
"apikey" : false,
"blocked" : false,
"id" : "bob",
"roles" : [ "admin:backup:read", "admin:backup:restore", "admin:group:write", "admin:job:read", "admin:job:write", "admin:log:read", "admin:ticket:delete", "admin:user:write", "admin:userdata:read", "admin:userdata:write", "analyst:automation:read", "analyst:currentsettings:write", "analyst:currentuser:read", "analyst:currentuserdata:read", "analyst:file", "analyst:group:read", "analyst:playbook:read", "analyst:rule:read", "analyst:settings:read", "analyst:template:read", "analyst:ticket:read", "analyst:ticket:write", "analyst:tickettype:read", "analyst:user:read", "engineer:automation:write", "engineer:playbook:write", "engineer:rule:write", "engineer:template:write", "engineer:tickettype:write" ]
"roles" : [ "admin:backup:read", "admin:backup:restore", "admin:group:write", "admin:job:read", "admin:job:write", "admin:log:read", "admin:settings:write", "admin:ticket:delete", "admin:user:write", "admin:userdata:read", "admin:userdata:write", "analyst:automation:read", "analyst:currentsettings:write", "analyst:currentuser:read", "analyst:currentuserdata:read", "analyst:file", "analyst:group:read", "analyst:playbook:read", "analyst:rule:read", "analyst:settings:read", "analyst:template:read", "analyst:ticket:read", "analyst:ticket:write", "analyst:tickettype:read", "analyst:user:read", "engineer:automation:write", "engineer:playbook:write", "engineer:rule:write", "engineer:template:write", "engineer:tickettype:write" ]
}, {
"apikey" : true,
"blocked" : false,
@@ -4721,7 +4820,7 @@
"apikey" : false,
"blocked" : false,
"id" : "bob",
"roles" : [ "admin:backup:read", "admin:backup:restore", "admin:group:write", "admin:job:read", "admin:job:write", "admin:log:read", "admin:ticket:delete", "admin:user:write", "admin:userdata:read", "admin:userdata:write", "analyst:automation:read", "analyst:currentsettings:write", "analyst:currentuser:read", "analyst:currentuserdata:read", "analyst:file", "analyst:group:read", "analyst:playbook:read", "analyst:rule:read", "analyst:settings:read", "analyst:template:read", "analyst:ticket:read", "analyst:ticket:write", "analyst:tickettype:read", "analyst:user:read", "engineer:automation:write", "engineer:playbook:write", "engineer:rule:write", "engineer:template:write", "engineer:tickettype:write" ]
"roles" : [ "admin:backup:read", "admin:backup:restore", "admin:group:write", "admin:job:read", "admin:job:write", "admin:log:read", "admin:settings:write", "admin:ticket:delete", "admin:user:write", "admin:userdata:read", "admin:userdata:write", "analyst:automation:read", "analyst:currentsettings:write", "analyst:currentuser:read", "analyst:currentuserdata:read", "analyst:file", "analyst:group:read", "analyst:playbook:read", "analyst:rule:read", "analyst:settings:read", "analyst:template:read", "analyst:ticket:read", "analyst:ticket:write", "analyst:tickettype:read", "analyst:user:read", "engineer:automation:write", "engineer:playbook:write", "engineer:rule:write", "engineer:template:write", "engineer:tickettype:write" ]
}
}
},
@@ -4747,6 +4846,9 @@
},
"type" : "object"
},
"kind" : {
"type" : "string"
},
"name" : {
"example" : "2.2.2.2",
"type" : "string"
@@ -5214,6 +5316,37 @@
},
"Settings" : {
"properties" : {
"artifactKinds" : {
"items" : {
"$ref" : "#/components/schemas/Type"
},
"title" : "Artifact Kinds",
"type" : "array"
},
"artifactStates" : {
"items" : {
"$ref" : "#/components/schemas/Type"
},
"title" : "Artifact States",
"type" : "array"
},
"timeformat" : {
"title" : "Time Format",
"type" : "string"
}
},
"required" : [ "artifactKinds", "artifactStates", "timeformat" ],
"type" : "object"
},
"SettingsResponse" : {
"properties" : {
"artifactKinds" : {
"items" : {
"$ref" : "#/components/schemas/Type"
},
"title" : "Artifact Kinds",
"type" : "array"
},
"artifactStates" : {
"items" : {
"$ref" : "#/components/schemas/Type"
@@ -5249,7 +5382,7 @@
"type" : "string"
}
},
"required" : [ "artifactStates", "ticketTypes", "tier", "timeformat", "version" ],
"required" : [ "artifactKinds", "artifactStates", "ticketTypes", "tier", "timeformat", "version" ],
"type" : "object"
},
"Statistics" : {

View File

@@ -8,6 +8,8 @@ definitions:
additionalProperties:
$ref: '#/definitions/Enrichment'
type: object
kind:
type: string
name:
example: 2.2.2.2
type: string
@@ -382,6 +384,31 @@ definitions:
type: array
Settings:
properties:
artifactKinds:
items:
$ref: '#/definitions/Type'
title: Artifact Kinds
type: array
artifactStates:
items:
$ref: '#/definitions/Type'
title: Artifact States
type: array
timeformat:
title: Time Format
type: string
required:
- timeformat
- artifactKinds
- artifactStates
type: object
SettingsResponse:
properties:
artifactKinds:
items:
$ref: '#/definitions/Type'
title: Artifact Kinds
type: array
artifactStates:
items:
$ref: '#/definitions/Type'
@@ -414,6 +441,7 @@ definitions:
- tier
- timeformat
- ticketTypes
- artifactKinds
- artifactStates
type: object
Statistics:
@@ -1433,6 +1461,7 @@ paths:
- admin:job:read
- admin:job:write
- admin:log:read
- admin:settings:write
- admin:ticket:delete
- admin:user:write
- admin:userdata:read
@@ -2137,6 +2166,13 @@ paths:
description: successful operation
examples:
test:
artifactKinds:
- icon: mdi-server
id: asset
name: Asset
- icon: mdi-bullseye
id: ioc
name: IOC
artifactStates:
- color: info
icon: mdi-help-circle-outline
@@ -2157,6 +2193,7 @@ paths:
- admin:job:read
- admin:job:write
- admin:log:read
- admin:settings:write
- admin:ticket:delete
- admin:user:write
- admin:userdata:read
@@ -2205,13 +2242,132 @@ paths:
timeformat: YYYY-MM-DDThh:mm:ss
version: 0.0.0-test
schema:
$ref: '#/definitions/Settings'
$ref: '#/definitions/SettingsResponse'
security:
- roles:
- settings:read
summary: Get settings
tags:
- settings
post:
operationId: saveSettings
parameters:
- description: Save settings
in: body
name: settings
required: true
schema:
$ref: '#/definitions/Settings'
x-example:
artifactKinds:
- icon: mdi-server
id: asset
name: Asset
- icon: mdi-bullseye
id: ioc
name: IOC
artifactStates:
- color: info
icon: mdi-help-circle-outline
id: unknown
name: Unknown
- color: error
icon: mdi-skull
id: malicious
name: Malicious
- color: success
icon: mdi-check
id: clean
name: Clean
timeformat: YYYY-MM-DDThh:mm:ss
responses:
"200":
description: successful operation
examples:
test:
artifactKinds:
- icon: mdi-server
id: asset
name: Asset
- icon: mdi-bullseye
id: ioc
name: IOC
artifactStates:
- color: info
icon: mdi-help-circle-outline
id: unknown
name: Unknown
- color: error
icon: mdi-skull
id: malicious
name: Malicious
- color: success
icon: mdi-check
id: clean
name: Clean
roles:
- admin:backup:read
- admin:backup:restore
- admin:group:write
- admin:job:read
- admin:job:write
- admin:log:read
- admin:settings:write
- admin:ticket:delete
- admin:user:write
- admin:userdata:read
- admin:userdata:write
- analyst:automation:read
- analyst:currentsettings:write
- analyst:currentuser:read
- analyst:currentuserdata:read
- analyst:file
- analyst:group:read
- analyst:playbook:read
- analyst:rule:read
- analyst:settings:read
- analyst:template:read
- analyst:ticket:read
- analyst:ticket:write
- analyst:tickettype:read
- analyst:user:read
- engineer:automation:write
- engineer:playbook:write
- engineer:rule:write
- engineer:template:write
- engineer:tickettype:write
ticketTypes:
- default_playbooks: []
default_template: default
icon: mdi-alert
id: alert
name: Alerts
- default_playbooks: []
default_template: default
icon: mdi-radioactive
id: incident
name: Incidents
- default_playbooks: []
default_template: default
icon: mdi-fingerprint
id: investigation
name: Forensic Investigations
- default_playbooks: []
default_template: default
icon: mdi-target
id: hunt
name: Threat Hunting
tier: community
timeformat: YYYY-MM-DDThh:mm:ss
version: 0.0.0-test
schema:
$ref: '#/definitions/SettingsResponse'
security:
- roles:
- settings:write
summary: Save settings
tags:
- settings
/statistics:
get:
operationId: getStatistics
@@ -6558,6 +6714,7 @@ paths:
- admin:job:read
- admin:job:write
- admin:log:read
- admin:settings:write
- admin:ticket:delete
- admin:user:write
- admin:userdata:read
@@ -6761,6 +6918,7 @@ paths:
- admin:job:read
- admin:job:write
- admin:log:read
- admin:settings:write
- admin:ticket:delete
- admin:user:write
- admin:userdata:read

View File

@@ -35,6 +35,7 @@ var (
ReferenceSchema = new(gojsonschema.Schema)
ReferenceArraySchema = new(gojsonschema.Schema)
SettingsSchema = new(gojsonschema.Schema)
SettingsResponseSchema = new(gojsonschema.Schema)
StatisticsSchema = new(gojsonschema.Schema)
TaskSchema = new(gojsonschema.Schema)
TaskOriginSchema = new(gojsonschema.Schema)
@@ -63,7 +64,7 @@ var (
func init() {
err := schemaLoader.AddSchemas(
gojsonschema.NewStringLoader(`{"type":"object","properties":{"enrichments":{"type":"object","additionalProperties":{"$ref":"#/definitions/Enrichment"}},"name":{"type":"string"},"status":{"type":"string"},"type":{"type":"string"}},"required":["name"],"$id":"#/definitions/Artifact"}`),
gojsonschema.NewStringLoader(`{"type":"object","properties":{"enrichments":{"type":"object","additionalProperties":{"$ref":"#/definitions/Enrichment"}},"kind":{"type":"string"},"name":{"type":"string"},"status":{"type":"string"},"type":{"type":"string"}},"required":["name"],"$id":"#/definitions/Artifact"}`),
gojsonschema.NewStringLoader(`{"type":"object","properties":{"artifact":{"type":"string"},"ticket_id":{"format":"int64","type":"integer"}},"required":["ticket_id","artifact"],"$id":"#/definitions/ArtifactOrigin"}`),
gojsonschema.NewStringLoader(`{"type":"object","properties":{"image":{"type":"string"},"schema":{"type":"string"},"script":{"type":"string"},"type":{"items":{"type":"string","enum":["artifact","playbook","global"]},"type":"array"}},"required":["image","script","type"],"$id":"#/definitions/Automation"}`),
gojsonschema.NewStringLoader(`{"type":"object","properties":{"id":{"type":"string"},"image":{"type":"string"},"schema":{"type":"string"},"script":{"type":"string"},"type":{"items":{"type":"string","enum":["artifact","playbook","global"]},"type":"array"}},"required":["id","image","script","type"],"$id":"#/definitions/AutomationForm"}`),
@@ -89,7 +90,8 @@ func init() {
gojsonschema.NewStringLoader(`{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"yaml":{"type":"string"}},"required":["id","name","yaml"],"$id":"#/definitions/PlaybookTemplateResponse"}`),
gojsonschema.NewStringLoader(`{"type":"object","properties":{"href":{"type":"string"},"name":{"type":"string"}},"required":["name","href"],"$id":"#/definitions/Reference"}`),
gojsonschema.NewStringLoader(`{"items":{"$ref":"#/definitions/Reference"},"type":"array","$id":"#/definitions/ReferenceArray"}`),
gojsonschema.NewStringLoader(`{"type":"object","properties":{"artifactStates":{"title":"Artifact States","items":{"$ref":"#/definitions/Type"},"type":"array"},"roles":{"title":"Roles","items":{"type":"string"},"type":"array"},"ticketTypes":{"title":"Ticket Types","items":{"$ref":"#/definitions/TicketTypeResponse"},"type":"array"},"tier":{"title":"Tier","type":"string","enum":["community","enterprise"]},"timeformat":{"title":"Time Format","type":"string"},"version":{"title":"Version","type":"string"}},"required":["version","tier","timeformat","ticketTypes","artifactStates"],"$id":"#/definitions/Settings"}`),
gojsonschema.NewStringLoader(`{"type":"object","properties":{"artifactKinds":{"title":"Artifact Kinds","items":{"$ref":"#/definitions/Type"},"type":"array"},"artifactStates":{"title":"Artifact States","items":{"$ref":"#/definitions/Type"},"type":"array"},"timeformat":{"title":"Time Format","type":"string"}},"required":["timeformat","artifactKinds","artifactStates"],"$id":"#/definitions/Settings"}`),
gojsonschema.NewStringLoader(`{"type":"object","properties":{"artifactKinds":{"title":"Artifact Kinds","items":{"$ref":"#/definitions/Type"},"type":"array"},"artifactStates":{"title":"Artifact States","items":{"$ref":"#/definitions/Type"},"type":"array"},"roles":{"title":"Roles","items":{"type":"string"},"type":"array"},"ticketTypes":{"title":"Ticket Types","items":{"$ref":"#/definitions/TicketTypeResponse"},"type":"array"},"tier":{"title":"Tier","type":"string","enum":["community","enterprise"]},"timeformat":{"title":"Time Format","type":"string"},"version":{"title":"Version","type":"string"}},"required":["version","tier","timeformat","ticketTypes","artifactKinds","artifactStates"],"$id":"#/definitions/SettingsResponse"}`),
gojsonschema.NewStringLoader(`{"type":"object","properties":{"open_tickets_per_user":{"type":"object","additionalProperties":{"type":"integer"}},"tickets_per_type":{"type":"object","additionalProperties":{"type":"integer"}},"tickets_per_week":{"type":"object","additionalProperties":{"type":"integer"}},"unassigned":{"type":"integer"}},"required":["unassigned","open_tickets_per_user","tickets_per_week","tickets_per_type"],"$id":"#/definitions/Statistics"}`),
gojsonschema.NewStringLoader(`{"type":"object","properties":{"automation":{"type":"string"},"closed":{"format":"date-time","type":"string"},"created":{"format":"date-time","type":"string"},"data":{"type":"object"},"done":{"type":"boolean"},"join":{"type":"boolean"},"name":{"type":"string"},"next":{"type":"object","additionalProperties":{"type":"string"}},"owner":{"type":"string"},"payload":{"type":"object","additionalProperties":{"type":"string"}},"schema":{"type":"object"},"type":{"type":"string","enum":["task","input","automation"]}},"required":["name","type","done","created"],"$id":"#/definitions/Task"}`),
gojsonschema.NewStringLoader(`{"type":"object","properties":{"playbook_id":{"type":"string"},"task_id":{"type":"string"},"ticket_id":{"format":"int64","type":"integer"}},"required":["ticket_id","playbook_id","task_id"],"$id":"#/definitions/TaskOrigin"}`),
@@ -146,6 +148,7 @@ func init() {
ReferenceSchema = mustCompile(`#/definitions/Reference`)
ReferenceArraySchema = mustCompile(`#/definitions/ReferenceArray`)
SettingsSchema = mustCompile(`#/definitions/Settings`)
SettingsResponseSchema = mustCompile(`#/definitions/SettingsResponse`)
StatisticsSchema = mustCompile(`#/definitions/Statistics`)
TaskSchema = mustCompile(`#/definitions/Task`)
TaskOriginSchema = mustCompile(`#/definitions/TaskOrigin`)
@@ -174,6 +177,7 @@ func init() {
type Artifact struct {
Enrichments map[string]*Enrichment `json:"enrichments,omitempty"`
Kind *string `json:"kind,omitempty"`
Name string `json:"name"`
Status *string `json:"status,omitempty"`
Type *string `json:"type,omitempty"`
@@ -338,6 +342,13 @@ type Reference struct {
type ReferenceArray []*Reference
type Settings struct {
ArtifactKinds []*Type `json:"artifactKinds"`
ArtifactStates []*Type `json:"artifactStates"`
Timeformat string `json:"timeformat"`
}
type SettingsResponse struct {
ArtifactKinds []*Type `json:"artifactKinds"`
ArtifactStates []*Type `json:"artifactStates"`
Roles []string `json:"roles,omitempty"`
TicketTypes []*TicketTypeResponse `json:"ticketTypes"`
@@ -598,9 +609,9 @@ func mustCompile(uri string) *gojsonschema.Schema {
}
const (
SettingsTierCommunity = "community"
SettingsResponseTierCommunity = "community"
SettingsTierEnterprise = "enterprise"
SettingsResponseTierEnterprise = "enterprise"
TaskTypeTask = "task"

View File

@@ -44,6 +44,7 @@ const (
LogRead Role = "admin:log:read"
UserdataRead Role = "admin:userdata:read"
UserdataWrite Role = "admin:userdata:write"
SettingsWrite Role = "admin:settings:write"
TicketDelete Role = "admin:ticket:delete"
UserWrite Role = "admin:user:write"
)
@@ -145,6 +146,7 @@ func List() []Role {
TicketWrite, UserRead, AutomationWrite, PlaybookWrite, RuleWrite,
TemplateWrite, TickettypeWrite, BackupRead, BackupRestore, GroupWrite,
LogRead, UserdataWrite, TicketDelete, UserWrite, JobRead, JobWrite,
SettingsWrite,
}
}

View File

@@ -31,7 +31,6 @@ type Config struct {
Storage *storage.Config
Bus *bus.Config
UISettings *model.Settings
Secret []byte
Auth *AuthConfig
ExternalAddress string
@@ -82,7 +81,7 @@ func New(hooks *hooks.Hooks, config *Config) (*Server, error) {
return nil, err
}
catalystService, err := service.New(catalystBus, catalystDatabase, catalystStorage, config.UISettings)
catalystService, err := service.New(catalystBus, catalystDatabase, catalystStorage, GetVersion())
if err != nil {
return nil, err
}

View File

@@ -8,19 +8,18 @@ import (
"github.com/SecurityBrewery/catalyst/bus"
"github.com/SecurityBrewery/catalyst/database"
"github.com/SecurityBrewery/catalyst/database/busdb"
"github.com/SecurityBrewery/catalyst/generated/model"
"github.com/SecurityBrewery/catalyst/storage"
)
type Service struct {
bus *bus.Bus
database *database.Database
settings *model.Settings
storage *storage.Storage
version string
}
func New(bus *bus.Bus, database *database.Database, storage *storage.Storage, settings *model.Settings) (*Service, error) {
return &Service{database: database, bus: bus, settings: settings, storage: storage}, nil
func New(bus *bus.Bus, database *database.Database, storage *storage.Storage, version string) (*Service, error) {
return &Service{database: database, bus: bus, storage: storage, version: version}, nil
}
func (s *Service) publishRequest(ctx context.Context, err error, function string, ids []driver.DocumentID) {

62
service/settings.go Normal file
View File

@@ -0,0 +1,62 @@
package service
import (
"context"
"errors"
"sort"
"github.com/SecurityBrewery/catalyst/database/busdb"
"github.com/SecurityBrewery/catalyst/generated/model"
"github.com/SecurityBrewery/catalyst/role"
)
func (s *Service) GetSettings(ctx context.Context) (*model.SettingsResponse, error) {
globalSettings, err := s.database.Settings(ctx)
if err != nil {
return nil, err
}
return s.settings(ctx, globalSettings)
}
func (s *Service) SaveSettings(ctx context.Context, settings *model.Settings) (*model.SettingsResponse, error) {
globalSettings, err := s.database.SaveSettings(ctx, settings)
if err != nil {
return nil, err
}
return s.settings(ctx, globalSettings)
}
func (s *Service) settings(ctx context.Context, globalSettings *model.Settings) (*model.SettingsResponse, error) {
user, ok := busdb.UserFromContext(ctx)
if !ok {
return nil, errors.New("no user in context")
}
userData, err := s.database.UserDataGet(ctx, user.ID)
if err != nil {
return nil, err
}
ticketTypeList, err := s.database.TicketTypeList(ctx)
if err != nil {
return nil, err
}
if userData.Timeformat != nil {
globalSettings.Timeformat = *userData.Timeformat
}
roles := role.Strings(role.List())
sort.Strings(roles)
return &model.SettingsResponse{
Tier: model.SettingsResponseTierCommunity,
Version: s.version,
Roles: roles,
TicketTypes: ticketTypeList,
ArtifactStates: globalSettings.ArtifactStates,
ArtifactKinds: globalSettings.ArtifactKinds,
Timeformat: globalSettings.Timeformat,
}, nil
}

View File

@@ -1,45 +0,0 @@
package service
import (
"context"
"errors"
"sort"
"github.com/SecurityBrewery/catalyst/database/busdb"
"github.com/SecurityBrewery/catalyst/generated/model"
"github.com/SecurityBrewery/catalyst/role"
)
func (s *Service) GetSettings(ctx context.Context) (*model.Settings, error) {
user, ok := busdb.UserFromContext(ctx)
if !ok {
return nil, errors.New("no user in context")
}
setting, err := s.database.UserDataGet(ctx, user.ID)
if err != nil {
return nil, err
}
settings := mergeSettings(s.settings, setting)
ticketTypeList, err := s.database.TicketTypeList(ctx)
if err != nil {
return nil, err
}
settings.TicketTypes = ticketTypeList
return settings, nil
}
func mergeSettings(globalSettings *model.Settings, user *model.UserDataResponse) *model.Settings {
if user.Timeformat != nil {
globalSettings.Timeformat = *user.Timeformat
}
roles := role.Strings(role.List())
sort.Strings(roles)
globalSettings.Roles = roles
return globalSettings
}

View File

@@ -20,7 +20,6 @@ import (
"github.com/SecurityBrewery/catalyst/database/busdb"
"github.com/SecurityBrewery/catalyst/generated/api"
"github.com/SecurityBrewery/catalyst/generated/model"
"github.com/SecurityBrewery/catalyst/generated/pointer"
"github.com/SecurityBrewery/catalyst/hooks"
"github.com/SecurityBrewery/catalyst/index"
"github.com/SecurityBrewery/catalyst/service"
@@ -51,22 +50,6 @@ func Config(ctx context.Context) (*catalyst.Config, error) {
Key: "A9RysEsPJni8RaHeg_K0FKXQNfBrUyw-",
APIUrl: "http://localhost:8002/api",
},
UISettings: &model.Settings{
ArtifactStates: []*model.Type{
{Icon: "mdi-help-circle-outline", ID: "unknown", Name: "Unknown", Color: pointer.String(model.TypeColorInfo)},
{Icon: "mdi-skull", ID: "malicious", Name: "Malicious", Color: pointer.String(model.TypeColorError)},
{Icon: "mdi-check", ID: "clean", Name: "Clean", Color: pointer.String(model.TypeColorSuccess)},
},
TicketTypes: []*model.TicketTypeResponse{
{ID: "alert", Icon: "mdi-alert", Name: "Alerts"},
{ID: "incident", Icon: "mdi-radioactive", Name: "Incidents"},
{ID: "investigation", Icon: "mdi-fingerprint", Name: "Forensic Investigations"},
{ID: "hunt", Icon: "mdi-target", Name: "Threat Hunting"},
},
Version: "0.0.0-test",
Tier: model.SettingsTierCommunity,
Timeformat: "YYYY-MM-DDThh:mm:ss",
},
Secret: []byte("4ef5b29539b70233dd40c02a1799d25079595565e05a193b09da2c3e60ada1cd"),
Auth: &catalyst.AuthConfig{
OIDCIssuer: "http://localhost:9002/auth/realms/catalyst",
@@ -168,7 +151,7 @@ func Service(t *testing.T) (context.Context, *catalyst.Config, *bus.Bus, *index.
t.Fatal(err)
}
catalystService, err := service.New(rbus, db, catalystStorage, config.UISettings)
catalystService, err := service.New(rbus, db, catalystStorage, "0.0.0-test")
if err != nil {
t.Fatal(err)
}

View File

@@ -164,6 +164,7 @@ export default Vue.extend({
{ icon: "mdi-account-group", name: "Groups", to: "GroupList", role: "admin:group:write", tier: "enterprise" },
{ icon: "mdi-cogs", name: "User Data", to: "UserDataList", role: "admin:userdata:write" },
{ icon: "mdi-format-list-checks", name: "Jobs", to: "JobList", role: "admin:job:write" },
{ icon: "mdi-cog", name: "Settings", to: "Settings", role: "admin:settings:write" },
],
mini: true,
goto: "",

View File

@@ -33,6 +33,12 @@ export interface Artifact {
* @memberof Artifact
*/
'enrichments'?: { [key: string]: Enrichment; };
/**
*
* @type {string}
* @memberof Artifact
*/
'kind'?: string;
/**
*
* @type {string}
@@ -976,50 +982,81 @@ export interface RuleResponse {
* @interface Settings
*/
export interface Settings {
/**
*
* @type {Array<Type>}
* @memberof Settings
*/
'artifactKinds': Array<Type>;
/**
*
* @type {Array<Type>}
* @memberof Settings
*/
'artifactStates': Array<Type>;
/**
*
* @type {Array<string>}
* @memberof Settings
*/
'roles'?: Array<string>;
/**
*
* @type {Array<TicketTypeResponse>}
* @memberof Settings
*/
'ticketTypes': Array<TicketTypeResponse>;
/**
*
* @type {string}
* @memberof Settings
*/
'tier': SettingsTierEnum;
/**
*
* @type {string}
* @memberof Settings
*/
'timeformat': string;
}
/**
*
* @export
* @interface SettingsResponse
*/
export interface SettingsResponse {
/**
*
* @type {Array<Type>}
* @memberof SettingsResponse
*/
'artifactKinds': Array<Type>;
/**
*
* @type {Array<Type>}
* @memberof SettingsResponse
*/
'artifactStates': Array<Type>;
/**
*
* @type {Array<string>}
* @memberof SettingsResponse
*/
'roles'?: Array<string>;
/**
*
* @type {Array<TicketTypeResponse>}
* @memberof SettingsResponse
*/
'ticketTypes': Array<TicketTypeResponse>;
/**
*
* @type {string}
* @memberof Settings
* @memberof SettingsResponse
*/
'tier': SettingsResponseTierEnum;
/**
*
* @type {string}
* @memberof SettingsResponse
*/
'timeformat': string;
/**
*
* @type {string}
* @memberof SettingsResponse
*/
'version': string;
}
export const SettingsTierEnum = {
export const SettingsResponseTierEnum = {
Community: 'community',
Enterprise: 'enterprise'
} as const;
export type SettingsTierEnum = typeof SettingsTierEnum[keyof typeof SettingsTierEnum];
export type SettingsResponseTierEnum = typeof SettingsResponseTierEnum[keyof typeof SettingsResponseTierEnum];
/**
*
@@ -4319,6 +4356,42 @@ export const SettingsApiAxiosParamCreator = function (configuration?: Configurat
let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
return {
url: toPathString(localVarUrlObj),
options: localVarRequestOptions,
};
},
/**
*
* @summary Save settings
* @param {Settings} settings Save settings
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
saveSettings: async (settings: Settings, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
// verify required parameter 'settings' is not null or undefined
assertParamExists('saveSettings', 'settings', settings)
const localVarPath = `/settings`;
// use dummy base URL string because the URL constructor only accepts absolute URLs.
const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL);
let baseOptions;
if (configuration) {
baseOptions = configuration.baseOptions;
}
const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options};
const localVarHeaderParameter = {} as any;
const localVarQueryParameter = {} as any;
localVarHeaderParameter['Content-Type'] = 'application/json';
setSearchParams(localVarUrlObj, localVarQueryParameter);
let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
localVarRequestOptions.data = serializeDataIfNeeded(settings, localVarRequestOptions, configuration)
return {
url: toPathString(localVarUrlObj),
options: localVarRequestOptions,
@@ -4340,10 +4413,21 @@ export const SettingsApiFp = function(configuration?: Configuration) {
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
async getSettings(options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<Settings>> {
async getSettings(options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<SettingsResponse>> {
const localVarAxiosArgs = await localVarAxiosParamCreator.getSettings(options);
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
},
/**
*
* @summary Save settings
* @param {Settings} settings Save settings
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
async saveSettings(settings: Settings, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<SettingsResponse>> {
const localVarAxiosArgs = await localVarAxiosParamCreator.saveSettings(settings, options);
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
},
}
};
@@ -4360,9 +4444,19 @@ export const SettingsApiFactory = function (configuration?: Configuration, baseP
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
getSettings(options?: any): AxiosPromise<Settings> {
getSettings(options?: any): AxiosPromise<SettingsResponse> {
return localVarFp.getSettings(options).then((request) => request(axios, basePath));
},
/**
*
* @summary Save settings
* @param {Settings} settings Save settings
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
saveSettings(settings: Settings, options?: any): AxiosPromise<SettingsResponse> {
return localVarFp.saveSettings(settings, options).then((request) => request(axios, basePath));
},
};
};
@@ -4383,6 +4477,18 @@ export class SettingsApi extends BaseAPI {
public getSettings(options?: AxiosRequestConfig) {
return SettingsApiFp(this.configuration).getSettings(options).then((request) => request(this.axios, this.basePath));
}
/**
*
* @summary Save settings
* @param {Settings} settings Save settings
* @param {*} [options] Override http request option.
* @throws {RequiredError}
* @memberof SettingsApi
*/
public saveSettings(settings: Settings, options?: AxiosRequestConfig) {
return SettingsApiFp(this.configuration).saveSettings(settings, options).then((request) => request(this.axios, this.basePath));
}
}

View File

@@ -9,6 +9,9 @@
<v-icon small class="mr-1" :color="statusColor">{{ statusIcon }}</v-icon>
<span :class="statusColor + '--text'">{{ artifact.status | capitalize }}</span>
<v-icon small class="mx-1" :color="kindColor">{{ kindIcon }}</v-icon>
<span :class="kindColor + '--text'">{{ artifact.kind | capitalize }}</span>
<v-spacer></v-spacer>
<v-icon small class="mr-1">mdi-information</v-icon>
<span class="mr-1">{{ artifact.enrichments ? lodash.size(artifact.enrichments) : 0 }}</span>
@@ -47,6 +50,24 @@ export default Vue.extend({
}
})
return color;
},
kindIcon: function () {
let icon = "mdi-help";
this.lodash.forEach(this.$store.state.settings.artifactKinds, (state: Type) => {
if (this.artifact.kind === state.id) {
icon = state.icon;
}
})
return icon;
},
kindColor: function () {
let color = TypeColorEnum.Info as TypeColorEnum;
this.lodash.forEach(this.$store.state.settings.artifactKinds, (state: Type) => {
if (this.artifact.kind === state.id && state.color) {
color = state.color;
}
})
return color;
}
},
methods: {

View File

@@ -26,6 +26,7 @@ import Group from "@/views/Group.vue";
import TicketType from '../views/TicketType.vue';
import TicketTypeList from "@/views/TicketTypeList.vue";
import TaskList from "@/views/TaskList.vue";
import Settings from "@/views/Settings.vue";
Vue.use(VueRouter);
@@ -226,6 +227,13 @@ const routes: Array<RouteConfig> = [
]
},
{
path: "/settings",
name: "Settings",
component: Settings,
meta: { title: "Settings" },
},
{
path: "/apidocs",
name: "API",

View File

@@ -1,7 +1,7 @@
import Vue from "vue";
import Vuex, {ActionContext} from "vuex";
import {API} from "@/services/api";
import {UserData, TicketList, Settings, UserResponse} from "@/client";
import {UserData, TicketList, Settings, UserResponse, SettingsResponse} from "@/client";
import {AxiosResponse} from "axios";
import {Alert} from "@/types/types";
import {templateStore} from "./modules/templates";
@@ -68,7 +68,7 @@ export default new Vuex.Store({
})
},
getSettings (context: ActionContext<any, any>) {
API.getSettings().then((response: AxiosResponse<Settings>) => {
API.getSettings().then((response: AxiosResponse<SettingsResponse>) => {
context.commit("setSettings", response.data);
context.dispatch("fetchCount");
})

View File

@@ -22,6 +22,23 @@
</v-list>
</v-menu>
&middot;
Kind:
<v-menu offset-y class="mr-2">
<template v-slot:activator="{ on, attrs }">
<span v-bind="attrs" v-on="on">
<v-icon small class="mr-1" :color="kindColor(artifact.kind)">{{ kindIcon(artifact.kind) }}</v-icon>
<span :class="kindColor(artifact.kind) + '--text'">{{ artifact.kind | capitalize }}</span>
</span>
</template>
<v-list>
<v-list-item dense link v-for="state in otherKinds" :key="state.id" @click="setKind(state.id)">
<v-list-item-title>
Set kind to <v-icon small>{{ kindIcon(state.id) }}</v-icon> {{ state.name }}
</v-list-item-title>
</v-list-item>
</v-list>
</v-menu>
&middot;
Type:
<v-menu
:close-on-content-click="false"
@@ -148,6 +165,14 @@ export default Vue.extend({
return state.id !== this.artifact.status;
})
},
otherKinds: function (): Array<Type> {
return this.lodash.filter(this.$store.state.settings.artifactKinds, (state: Type) => {
if (!this.artifact || !this.artifact.status) {
return true;
}
return state.id !== this.artifact.status;
})
},
},
methods: {
setArtifactType() {
@@ -229,6 +254,42 @@ export default Vue.extend({
}
});
},
kindIcon: function (kind: string): string {
let icon = "mdi-help";
this.lodash.forEach(this.$store.state.settings.artifactKinds, (state: Type) => {
if (kind === state.id) {
icon = state.icon;
}
})
return icon;
},
kindColor: function (kind: string) {
let color = TypeColorEnum.Info as TypeColorEnum;
this.lodash.forEach(this.$store.state.settings.artifactKinds, (state: Type) => {
if (kind === state.id && state.color) {
color = state.color
}
})
return color;
},
setKind(kind: string) {
if (!this.artifact || !this.artifact.name || this.ticketID === undefined) {
return;
}
let artifact = this.artifact
artifact.kind = kind
API.setArtifact(this.ticketID, this.artifact.name, artifact).then((response) => {
this.$store.dispatch("alertSuccess", { name: "Artifact kind changed", type: "success" })
if (response.data.artifacts) {
this.lodash.forEach(response.data.artifacts, (artifact) => {
if (artifact.name == this.name) {
this.artifact = artifact;
}
})
}
});
},
load() {
this.loadArtifact(this.ticketID, this.name);
}

177
ui/src/views/Settings.vue Normal file
View File

@@ -0,0 +1,177 @@
<template>
<v-main class="ma-4">
<div v-if="settings !== undefined">
<v-text-field label="Time Format" v-model="settings.timeformat"></v-text-field>
<v-subheader class="mx-0 px-0">Artifact States</v-subheader>
<v-card v-for="state in settings.artifactStates" :key="state.id" class="d-flex mb-2">
<v-row class="px-4 pt-2" dense>
<v-col>
<v-text-field label="ID" v-model="state.id"></v-text-field>
</v-col>
<v-col>
<v-text-field label="Name" v-model="state.name"></v-text-field>
</v-col>
<v-col>
<v-text-field label="Icon" v-model="state.icon"></v-text-field>
</v-col>
<v-col>
<v-select label="Color" v-model="state.color" :items="['info', 'error', 'success', 'warning']" clearable></v-select>
</v-col>
</v-row>
<v-btn icon class="mt-6 mr-4" @click="removeState(state.id)"><v-icon>mdi-close</v-icon></v-btn>
</v-card>
<v-card class="d-flex mb-2">
<v-row class="px-4 pt-2" dense>
<v-col>
<v-text-field label="ID" v-model="newState.id"></v-text-field>
</v-col>
<v-col>
<v-text-field label="Name" v-model="newState.name"></v-text-field>
</v-col>
<v-col>
<v-text-field label="Icon" v-model="newState.icon"></v-text-field>
</v-col>
<v-col>
<v-select label="Color" v-model="newState.color" :items="['info', 'error', 'success', 'warning']" clearable></v-select>
</v-col>
</v-row>
<v-btn icon class="mt-6 mr-4" @click="addState"><v-icon>mdi-plus</v-icon></v-btn>
</v-card>
<v-subheader class="mx-0 px-0">Artifact Types</v-subheader>
<v-card v-for="state in settings.artifactKinds" :key="state.id" class="d-flex mb-2">
<v-row class="px-4 pt-2" dense>
<v-col>
<v-text-field label="ID" v-model="state.id"></v-text-field>
</v-col>
<v-col>
<v-text-field label="Name" v-model="state.name"></v-text-field>
</v-col>
<v-col>
<v-text-field label="Icon" v-model="state.icon"></v-text-field>
</v-col>
<v-col>
<v-select label="Color" v-model="state.color" :items="['info', 'error', 'success', 'warning']" clearable></v-select>
</v-col>
</v-row>
<v-btn icon class="mt-6 mr-4" @click="removeKind(state.id)"><v-icon>mdi-close</v-icon></v-btn>
</v-card>
<v-card class="d-flex mb-2">
<v-row class="px-4 pt-2" dense>
<v-col>
<v-text-field label="ID" v-model="newKind.id"></v-text-field>
</v-col>
<v-col>
<v-text-field label="Name" v-model="newKind.name"></v-text-field>
</v-col>
<v-col>
<v-text-field label="Icon" v-model="newKind.icon"></v-text-field>
</v-col>
<v-col>
<v-select label="Color" v-model="newKind.color" :items="['info', 'error', 'success', 'warning']" clearable></v-select>
</v-col>
</v-row>
<v-btn icon class="mt-6 mr-4" @click="addKind"><v-icon>mdi-plus</v-icon></v-btn>
</v-card>
<v-btn color="success" @click="save" outlined class="mt-2">
<v-icon>mdi-content-save</v-icon>
Save
</v-btn>
</div>
</v-main>
</template>
<script lang="ts">
import {DateTime} from "luxon";
import Vue from "vue";
import {Settings, SettingsResponse, Type} from "@/client";
import {API} from "@/services/api";
import {AxiosResponse} from "axios";
interface State {
valid: boolean;
settings?: Settings;
newState: Type;
newKind: Type;
}
export default Vue.extend({
name: "Settings",
data: (): State => ({
valid: true,
settings: undefined,
newState: {} as Type,
newKind: {} as Type,
}),
methods: {
save: function () {
if (this.settings === undefined) {
return
}
API.saveSettings(this.settings).then((response) => {
this.settings = response.data;
this.$store.dispatch("getSettings");
})
},
addState: function () {
if (this.settings === undefined) {
return
}
this.settings.artifactStates.push(this.newState);
this.newState = {} as Type;
},
removeState: function (id: string) {
if (this.settings === undefined) {
return
}
this.settings.artifactStates = this.lodash.filter(this.settings.artifactStates, function (t) { return t.id !== id });
},
addKind: function () {
if (this.settings === undefined) {
return
}
this.settings.artifactKinds.push(this.newKind);
this.newKind = {} as Type;
},
removeKind: function (id: string) {
if (this.settings === undefined) {
return
}
this.settings.artifactKinds = this.lodash.filter(this.settings.artifactKinds, function (t) { return t.id !== id });
},
timeformat: function (s: string) {
let format = this.$store.state.settings.timeformat;
if (!format) {
return DateTime.fromISO(s).toLocaleString(DateTime.DATETIME_SHORT);
}
return DateTime.fromISO(s).toFormat(format);
},
dateformat: function (s: string) {
let format = this.$store.state.settings.timeformat;
if (!format) {
return DateTime.fromISO(s).toLocaleString(DateTime.DATETIME_SHORT);
}
return DateTime.fromISO(s).toFormat(format);
},
datetimeformat: function (s: string) {
let format = this.$store.state.settings.timeformat;
if (!format) {
return DateTime.fromISO(s).toLocaleString(DateTime.DATETIME_SHORT);
}
return DateTime.fromISO(s).toFormat(format);
},
},
mounted() {
API.getSettings().then((response: AxiosResponse<SettingsResponse>) => {
this.settings = response.data;
})
}
});
</script>

View File

@@ -181,7 +181,7 @@
<v-jsf
v-model="ticket.details"
:schema="schema"
:options="{ initialValidation: 'all', formats: { time: timeformat, date: dateformat, 'date-time': datetimeformat } }"
:options="{ initialValidation: 'all', formats: { time: timeformat, date: dateformat, 'date-time': datetimeformat }, editMode: 'inline' }"
/>
</v-form>
<v-btn small class="float-right mb-2" color="card" @click="saveTicket" outlined>