Files
catalyst/fakedata/default.go
2025-02-02 13:40:33 +01:00

236 lines
6.5 KiB
Go

package fakedata
import (
"encoding/json"
"errors"
"fmt"
"reflect"
"time"
"github.com/pocketbase/pocketbase/core"
"github.com/pocketbase/pocketbase/models"
"github.com/pocketbase/pocketbase/tools/types"
"github.com/SecurityBrewery/catalyst/migrations"
)
func defaultData() map[string]map[string]map[string]any {
var (
ticketCreated = time.Date(2025, 2, 1, 11, 29, 35, 0, time.UTC)
ticketUpdated = ticketCreated.Add(time.Minute * 5)
commentCreated = ticketCreated.Add(time.Minute * 10)
commentUpdated = commentCreated.Add(time.Minute * 5)
timelineCreated = ticketCreated.Add(time.Minute * 15)
timelineUpdated = timelineCreated.Add(time.Minute * 5)
taskCreated = ticketCreated.Add(time.Minute * 20)
taskUpdated = taskCreated.Add(time.Minute * 5)
linkCreated = ticketCreated.Add(time.Minute * 25)
linkUpdated = linkCreated.Add(time.Minute * 5)
reactionCreated = time.Date(2025, 2, 1, 11, 30, 0, 0, time.UTC)
reactionUpdated = reactionCreated.Add(time.Minute * 5)
)
createTicketActionData := `{"requirements":"pocketbase","script":"import sys\nimport json\nimport random\nimport os\n\nfrom pocketbase import PocketBase\n\n# Connect to the PocketBase server\nclient = PocketBase(os.environ[\"CATALYST_APP_URL\"])\nclient.auth_store.save(token=os.environ[\"CATALYST_TOKEN\"])\n\nnewtickets = client.collection(\"tickets\").get_list(1, 200, {\"filter\": 'name = \"New Ticket\"'})\nfor ticket in newtickets.items:\n\tclient.collection(\"tickets\").delete(ticket.id)\n\n# Create a new ticket\nclient.collection(\"tickets\").create({\n\t\"name\": \"New Ticket\",\n\t\"type\": \"alert\",\n\t\"open\": True,\n})"}`
return map[string]map[string]map[string]any{
migrations.TicketCollectionName: {
"t_0": {
"created": dateTime(ticketCreated),
"updated": dateTime(ticketUpdated),
"name": "phishing-123",
"type": "alert",
"description": "Phishing email reported by several employees.",
"open": true,
"schema": types.JsonRaw(`{"type":"object","properties":{"tlp":{"title":"TLP","type":"string"}}}`),
"state": types.JsonRaw(`{"severity":"Medium"}`),
"owner": "u_test",
},
},
migrations.CommentCollectionName: {
"c_0": {
"created": dateTime(commentCreated),
"updated": dateTime(commentUpdated),
"ticket": "t_0",
"author": "u_test",
"message": "This is a test comment.",
},
},
migrations.TimelineCollectionName: {
"tl_0": {
"created": dateTime(timelineCreated),
"updated": dateTime(timelineUpdated),
"ticket": "t_0",
"time": dateTime(timelineCreated),
"message": "This is a test timeline message.",
},
},
migrations.TaskCollectionName: {
"ts_0": {
"created": dateTime(taskCreated),
"updated": dateTime(taskUpdated),
"ticket": "t_0",
"name": "This is a test task.",
"open": true,
"owner": "u_test",
},
},
migrations.LinkCollectionName: {
"l_0": {
"created": dateTime(linkCreated),
"updated": dateTime(linkUpdated),
"ticket": "t_0",
"url": "https://www.example.com",
"name": "This is a test link.",
},
},
migrations.ReactionCollectionName: {
"w_0": {
"created": dateTime(reactionCreated),
"updated": dateTime(reactionUpdated),
"name": "Create New Ticket",
"trigger": "schedule",
"triggerdata": types.JsonRaw(triggerSchedule),
"action": "python",
"actiondata": types.JsonRaw(createTicketActionData),
},
},
}
}
func GenerateDefaultData(app core.App) error {
var records []*models.Record
// users
userRecord, err := testUser(app.Dao())
if err != nil {
return err
}
records = append(records, userRecord)
// records
for collectionName, collectionRecords := range defaultData() {
collection, err := app.Dao().FindCollectionByNameOrId(collectionName)
if err != nil {
return err
}
for id, fields := range collectionRecords {
record := models.NewRecord(collection)
record.SetId(id)
for key, value := range fields {
record.Set(key, value)
}
records = append(records, record)
}
}
for _, record := range records {
if err := app.Dao().SaveRecord(record); err != nil {
return err
}
}
return nil
}
func ValidateDefaultData(app core.App) error { //nolint:cyclop,gocognit
// users
userRecord, err := app.Dao().FindRecordById(migrations.UserCollectionName, "u_test")
if err != nil {
return fmt.Errorf("failed to find user record: %w", err)
}
if userRecord == nil {
return errors.New("user not found")
}
if userRecord.Username() != "u_test" {
return fmt.Errorf(`username does not match: got %q, want "u_test"`, userRecord.Username())
}
if !userRecord.ValidatePassword("1234567890") {
return errors.New("password does not match")
}
if userRecord.Get("name") != "Test User" {
return fmt.Errorf(`name does not match: got %q, want "Test User"`, userRecord.Get("name"))
}
if userRecord.Get("email") != "user@catalyst-soar.com" {
return fmt.Errorf(`email does not match: got %q, want "user@catalyst-soar.com"`, userRecord.Get("email"))
}
if !userRecord.Verified() {
return errors.New("user is not verified")
}
// records
for collectionName, collectionRecords := range defaultData() {
for id, fields := range collectionRecords {
record, err := app.Dao().FindRecordById(collectionName, id)
if err != nil {
return fmt.Errorf("failed to find record %s: %w", id, err)
}
if record == nil {
return errors.New("record not found")
}
for key, value := range fields {
got := record.Get(key)
if wantJSON, ok := value.(types.JsonRaw); ok {
if err := compareJSON(got, wantJSON); err != nil {
return fmt.Errorf("record field %q does not match: %w", key, err)
}
continue
}
if got != value {
return fmt.Errorf("record field %s does not match: got %v (%T), want %v (%T)", key, got, got, value, value)
}
}
}
}
return nil
}
func compareJSON(got any, wantJSON types.JsonRaw) error {
gotJSON, ok := got.(types.JsonRaw)
if !ok {
return fmt.Errorf("got %T, want %T", got, wantJSON)
}
if !jsonEqual(gotJSON.String(), wantJSON.String()) {
return fmt.Errorf("got %v, want %v", gotJSON, wantJSON)
}
return nil
}
func jsonEqual(a, b string) bool {
var objA, objB interface{}
if err := json.Unmarshal([]byte(a), &objA); err != nil {
return false
}
if err := json.Unmarshal([]byte(b), &objB); err != nil {
return false
}
return reflect.DeepEqual(objA, objB)
}
func dateTime(t time.Time) types.DateTime {
dt := types.DateTime{}
_ = dt.Scan(t)
return dt
}