From 18a4dc54e723d3c26d167dc2316559f1e7d497a6 Mon Sep 17 00:00:00 2001 From: Jonas Plum Date: Sun, 13 Mar 2022 13:45:10 +0100 Subject: [PATCH] Add global settings (#40) --- cmd/cmd.go | 60 +----- database/db.go | 8 + database/migrations/migrations.go | 5 + database/settings.go | 80 ++------ database/userdata.go | 85 +++++++++ .../{settings_test.go => userdata_test.go} | 0 definition/artifacts.yaml | 1 + definition/settings.yaml | 72 +++++-- definition/users.yaml | 6 +- generated/api/server.go | 25 ++- generated/api/test_api.go | 17 +- generated/catalyst.json | 145 +++++++++++++- generated/catalyst.yml | 160 +++++++++++++++- generated/community.json | 145 +++++++++++++- generated/community.yml | 160 +++++++++++++++- generated/model/model.go | 19 +- role/role.go | 2 + server.go | 3 +- service/service.go | 7 +- service/settings.go | 62 ++++++ service/uisettings.go | 45 ----- test/test.go | 19 +- ui/src/App.vue | 1 + ui/src/client/api.ts | 152 ++++++++++++--- .../components/snippets/ArtifactSnippet.vue | 21 +++ ui/src/router/index.ts | 8 + ui/src/store/index.ts | 4 +- ui/src/views/ArtifactPopup.vue | 61 ++++++ ui/src/views/Settings.vue | 177 ++++++++++++++++++ ui/src/views/Ticket.vue | 2 +- 30 files changed, 1297 insertions(+), 255 deletions(-) create mode 100644 database/userdata.go rename database/{settings_test.go => userdata_test.go} (100%) create mode 100644 service/settings.go delete mode 100644 service/uisettings.go create mode 100644 ui/src/views/Settings.vue diff --git a/cmd/cmd.go b/cmd/cmd.go index 84091cc..f61dc82 100644 --- a/cmd/cmd.go +++ b/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 diff --git a/database/db.go b/database/db.go index 6c46b2c..8a5bd8c 100644 --- a/database/db.go +++ b/database/db.go @@ -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) } diff --git a/database/migrations/migrations.go b/database/migrations/migrations.go index d3683ff..19516ae 100644 --- a/database/migrations/migrations.go +++ b/database/migrations/migrations.go @@ -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 } diff --git a/database/settings.go b/database/settings.go index ddfff22..e58a7dc 100644 --- a/database/settings.go +++ b/database/settings.go @@ -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 } diff --git a/database/userdata.go b/database/userdata.go new file mode 100644 index 0000000..ddfff22 --- /dev/null +++ b/database/userdata.go @@ -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 +} diff --git a/database/settings_test.go b/database/userdata_test.go similarity index 100% rename from database/settings_test.go rename to database/userdata_test.go diff --git a/definition/artifacts.yaml b/definition/artifacts.yaml index 346f22b..f597890 100644 --- a/definition/artifacts.yaml +++ b/definition/artifacts.yaml @@ -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" } } diff --git a/definition/settings.yaml b/definition/settings.yaml index 7a2c173..69f296c 100644 --- a/definition/settings.yaml +++ b/definition/settings.yaml @@ -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 } } diff --git a/definition/users.yaml b/definition/users.yaml index d2a86dd..3bec567 100644 --- a/definition/users.yaml +++ b/definition/users.yaml @@ -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" ] } ] diff --git a/generated/api/server.go b/generated/api/server.go index 38ec935..8be8c44 100755 --- a/generated/api/server.go +++ b/generated/api/server.go @@ -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) diff --git a/generated/api/test_api.go b/generated/api/test_api.go index 28d809e..932b771 100755 --- a/generated/api/test_api.go +++ b/generated/api/test_api.go @@ -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"}}, }, }, diff --git a/generated/catalyst.json b/generated/catalyst.json index 1d423d9..c70b7a2 100644 --- a/generated/catalyst.json +++ b/generated/catalyst.json @@ -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" : { diff --git a/generated/catalyst.yml b/generated/catalyst.yml index 747d8a4..b92a3fd 100644 --- a/generated/catalyst.yml +++ b/generated/catalyst.yml @@ -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 diff --git a/generated/community.json b/generated/community.json index 735d528..28c28a5 100644 --- a/generated/community.json +++ b/generated/community.json @@ -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" : { diff --git a/generated/community.yml b/generated/community.yml index eb992a4..adffefa 100644 --- a/generated/community.yml +++ b/generated/community.yml @@ -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 diff --git a/generated/model/model.go b/generated/model/model.go index 2730dc5..d4b3c53 100755 --- a/generated/model/model.go +++ b/generated/model/model.go @@ -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" diff --git a/role/role.go b/role/role.go index c7fc85e..6443ff8 100644 --- a/role/role.go +++ b/role/role.go @@ -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, } } diff --git a/server.go b/server.go index ae0b3a9..c7c567e 100644 --- a/server.go +++ b/server.go @@ -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 } diff --git a/service/service.go b/service/service.go index e93ed9c..d5ef87f 100644 --- a/service/service.go +++ b/service/service.go @@ -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) { diff --git a/service/settings.go b/service/settings.go new file mode 100644 index 0000000..ee3d82a --- /dev/null +++ b/service/settings.go @@ -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 +} diff --git a/service/uisettings.go b/service/uisettings.go deleted file mode 100644 index 2ea6fd8..0000000 --- a/service/uisettings.go +++ /dev/null @@ -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 -} diff --git a/test/test.go b/test/test.go index 98492a7..7a4a8fd 100644 --- a/test/test.go +++ b/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) } diff --git a/ui/src/App.vue b/ui/src/App.vue index eb69896..65792f3 100644 --- a/ui/src/App.vue +++ b/ui/src/App.vue @@ -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: "", diff --git a/ui/src/client/api.ts b/ui/src/client/api.ts index 9bc362a..9b57d97 100644 --- a/ui/src/client/api.ts +++ b/ui/src/client/api.ts @@ -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} + * @memberof Settings + */ + 'artifactKinds': Array; /** * * @type {Array} * @memberof Settings */ 'artifactStates': Array; - /** - * - * @type {Array} - * @memberof Settings - */ - 'roles'?: Array; - /** - * - * @type {Array} - * @memberof Settings - */ - 'ticketTypes': Array; - /** - * - * @type {string} - * @memberof Settings - */ - 'tier': SettingsTierEnum; /** * * @type {string} * @memberof Settings */ 'timeformat': string; +} +/** + * + * @export + * @interface SettingsResponse + */ +export interface SettingsResponse { + /** + * + * @type {Array} + * @memberof SettingsResponse + */ + 'artifactKinds': Array; + /** + * + * @type {Array} + * @memberof SettingsResponse + */ + 'artifactStates': Array; + /** + * + * @type {Array} + * @memberof SettingsResponse + */ + 'roles'?: Array; + /** + * + * @type {Array} + * @memberof SettingsResponse + */ + 'ticketTypes': Array; /** * * @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 => { + // 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> { + async getSettings(options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { 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> { + 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 { + getSettings(options?: any): AxiosPromise { 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 { + 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)); + } } diff --git a/ui/src/components/snippets/ArtifactSnippet.vue b/ui/src/components/snippets/ArtifactSnippet.vue index 607776f..0bd3dc8 100644 --- a/ui/src/components/snippets/ArtifactSnippet.vue +++ b/ui/src/components/snippets/ArtifactSnippet.vue @@ -9,6 +9,9 @@ {{ statusIcon }} {{ artifact.status | capitalize }} + {{ kindIcon }} + {{ artifact.kind | capitalize }} + mdi-information {{ artifact.enrichments ? lodash.size(artifact.enrichments) : 0 }} @@ -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: { diff --git a/ui/src/router/index.ts b/ui/src/router/index.ts index 1ed8181..565a1f3 100644 --- a/ui/src/router/index.ts +++ b/ui/src/router/index.ts @@ -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 = [ ] }, + { + path: "/settings", + name: "Settings", + component: Settings, + meta: { title: "Settings" }, + }, + { path: "/apidocs", name: "API", diff --git a/ui/src/store/index.ts b/ui/src/store/index.ts index fd68b3b..dfd66ab 100644 --- a/ui/src/store/index.ts +++ b/ui/src/store/index.ts @@ -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) { - API.getSettings().then((response: AxiosResponse) => { + API.getSettings().then((response: AxiosResponse) => { context.commit("setSettings", response.data); context.dispatch("fetchCount"); }) diff --git a/ui/src/views/ArtifactPopup.vue b/ui/src/views/ArtifactPopup.vue index 6febd28..906b2b6 100644 --- a/ui/src/views/ArtifactPopup.vue +++ b/ui/src/views/ArtifactPopup.vue @@ -22,6 +22,23 @@ · + Kind: + + + + + + Set kind to {{ kindIcon(state.id) }} {{ state.name }} + + + + + · 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); } diff --git a/ui/src/views/Settings.vue b/ui/src/views/Settings.vue new file mode 100644 index 0000000..3902518 --- /dev/null +++ b/ui/src/views/Settings.vue @@ -0,0 +1,177 @@ + + + diff --git a/ui/src/views/Ticket.vue b/ui/src/views/Ticket.vue index 0c5132d..dc131be 100644 --- a/ui/src/views/Ticket.vue +++ b/ui/src/views/Ticket.vue @@ -181,7 +181,7 @@