mirror of
https://github.com/SecurityBrewery/catalyst.git
synced 2025-12-06 07:12:46 +01:00
Add global settings (#40)
This commit is contained in:
60
cmd/cmd.go
60
cmd/cmd.go
@@ -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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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
85
database/userdata.go
Normal 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
|
||||
}
|
||||
@@ -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" } }
|
||||
|
||||
|
||||
@@ -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 } }
|
||||
|
||||
|
||||
@@ -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" ] } ]
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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"}},
|
||||
},
|
||||
},
|
||||
|
||||
|
||||
@@ -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" : {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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" : {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
62
service/settings.go
Normal 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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
19
test/test.go
19
test/test.go
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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: "",
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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: {
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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");
|
||||
})
|
||||
|
||||
@@ -22,6 +22,23 @@
|
||||
</v-list>
|
||||
</v-menu>
|
||||
·
|
||||
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>
|
||||
·
|
||||
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
177
ui/src/views/Settings.vue
Normal 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>
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user