From d353268cf2fc4b74cb2e5f03ee542e3bb0056887 Mon Sep 17 00:00:00 2001 From: Jonas Plum Date: Sat, 12 Mar 2022 21:09:10 +0100 Subject: [PATCH] Update generator (#37) --- auth.go | 131 +- busservice/busservice.go | 2 +- cmd/catalyst-dev/main.go | 18 +- cmd/cmd.go | 2 +- cookie.go | 49 + database/artifact.go | 2 +- database/busdb/log.go | 2 +- database/migrations/migrations.go | 2 +- database/playbook.go | 2 +- database/settings_test.go | 2 +- database/ticket.go | 2 +- database/ticket_field.go | 4 +- database/ticket_task.go | 2 +- database/user.go | 4 +- generate.sh | 1 + generated/api/api.go | 1501 +-------------------- generated/api/server.go | 1129 ++++++++++++++++ generated/api/static.go | 25 + {pointer => generated/pointer}/pointer.go | 0 {time => generated/time}/time.go | 0 restore.go | 2 +- server.go | 7 +- static.go | 22 - storage/storage.go | 2 +- test/backup_test.go | 2 +- test/data.go | 2 +- test/server_test.go | 2 +- test/test.go | 2 +- 28 files changed, 1303 insertions(+), 1618 deletions(-) create mode 100644 cookie.go create mode 100755 generated/api/server.go create mode 100755 generated/api/static.go rename {pointer => generated/pointer}/pointer.go (100%) mode change 100644 => 100755 rename {time => generated/time}/time.go (100%) mode change 100644 => 100755 delete mode 100644 static.go diff --git a/auth.go b/auth.go index 545e1bf..19a8fdb 100644 --- a/auth.go +++ b/auth.go @@ -4,7 +4,6 @@ import ( "context" "crypto/sha256" "encoding/base64" - "encoding/json" "errors" "fmt" "math/rand" @@ -58,11 +57,6 @@ func (c *AuthConfig) Load(ctx context.Context) error { return nil } -const ( - stateSessionCookie = "state" - userSessionCookie = "user" -) - func Authenticate(db *database.Database, config *AuthConfig) func(next http.Handler) http.Handler { return func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { @@ -82,36 +76,6 @@ func Authenticate(db *database.Database, config *AuthConfig) func(next http.Hand } } -/* -func oidcCtx(w http.ResponseWriter, r *http.Request) (context.Context, context.CancelFunc) { - if config.TLSCertFile != "" && config.TLSKeyFile != "" { - cert, err := tls.LoadX509KeyPair(config.TLSCertFile, config.TLSKeyFile) - if err != nil { - return nil, err - } - - rootCAs, _ := x509.SystemCertPool() - if rootCAs == nil { - rootCAs = x509.NewCertPool() - } - for _, c := range cert.Certificate { - rootCAs.AppendCertsFromPEM(c) - } - - return oidc.ClientContext(ctx, &http.Client{ - Transport: &http.Transport{ - TLSClientConfig: &tls.Config{ - RootCAs: rootCAs, - InsecureSkipVerify: true, - }, - }, - }), nil - } - cctx, cancel := context.WithTimeout(ctx, time.Minute) - return cctx, cancel -} -*/ - func bearerAuth(db *database.Database, authHeader string, iss string, config *AuthConfig) func(next http.Handler) http.Handler { return func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { @@ -120,23 +84,9 @@ func bearerAuth(db *database.Database, authHeader string, iss string, config *Au return } - // oidcCtx, cancel := oidcCtx(ctx) - // defer cancel() - - verifier, err := config.Verifier(r.Context()) - if err != nil { - api.JSONErrorStatus(w, http.StatusUnauthorized, fmt.Errorf("could not verify: %w", err)) - return - } - authToken, err := verifier.Verify(r.Context(), authHeader[7:]) - if err != nil { - api.JSONErrorStatus(w, http.StatusInternalServerError, fmt.Errorf("could not verify bearer token: %w", err)) - return - } - - var claims map[string]interface{} - if err := authToken.Claims(&claims); err != nil { - api.JSONErrorStatus(w, http.StatusInternalServerError, fmt.Errorf("failed to parse claims: %w", err)) + claims, apiError := verifyClaims(r, config, authHeader[7:]) + if apiError != nil { + api.JSONErrorStatus(w, apiError.Status, apiError.Internal) return } @@ -145,10 +95,9 @@ func bearerAuth(db *database.Database, authHeader string, iss string, config *Au // return // } - b, _ := json.Marshal(claims) - http.SetCookie(w, &http.Cookie{Name: userSessionCookie, Value: base64.StdEncoding.EncodeToString(b)}) + setClaimsCookie(w, claims) - r, err = setContextClaims(r, db, claims, config) + r, err := setContextClaims(r, db, claims, config) if err != nil { api.JSONErrorStatus(w, http.StatusInternalServerError, fmt.Errorf("could not load user: %w", err)) return @@ -180,22 +129,13 @@ func keyAuth(db *database.Database, keyHeader string) func(next http.Handler) ht func sessionAuth(db *database.Database, config *AuthConfig) func(next http.Handler) http.Handler { return func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - userCookie, err := r.Cookie(userSessionCookie) + claims, noCookie, err := claimsCookie(r) if err != nil { + api.JSONError(w, err) + return + } + if noCookie { redirectToLogin(w, r, config.OAuth2) - - return - } - - b, err := base64.StdEncoding.DecodeString(userCookie.Value) - if err != nil { - api.JSONErrorStatus(w, http.StatusInternalServerError, fmt.Errorf("could not decode cookie: %w", err)) - return - } - - var claims map[string]interface{} - if err := json.Unmarshal(b, &claims); err != nil { - api.JSONErrorStatus(w, http.StatusInternalServerError, errors.New("claims not in session")) return } @@ -310,7 +250,7 @@ func redirectToLogin(w http.ResponseWriter, r *http.Request, oauth2Config *oauth return } - http.SetCookie(w, &http.Cookie{Name: stateSessionCookie, Value: state}) + setStateCookie(w, state) http.Redirect(w, r, oauth2Config.AuthCodeURL(state), http.StatusFound) return @@ -356,13 +296,13 @@ func AuthorizeRole(roles []string) func(http.Handler) http.Handler { func callback(config *AuthConfig) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { - stateCookie, err := r.Cookie(stateSessionCookie) - if err != nil || stateCookie.Value == "" { + state, err := stateCookie(r) + if err != nil || state == "" { api.JSONErrorStatus(w, http.StatusInternalServerError, errors.New("state missing")) return } - if stateCookie.Value != r.URL.Query().Get("state") { + if state != r.URL.Query().Get("state") { api.JSONErrorStatus(w, http.StatusInternalServerError, errors.New("state mismatch")) return } @@ -380,31 +320,13 @@ func callback(config *AuthConfig) http.HandlerFunc { return } - // oidcCtx, cancel := oidcCtx(ctx) - // defer cancel() - - verifier, err := config.Verifier(r.Context()) - if err != nil { - api.JSONErrorStatus(w, http.StatusUnauthorized, fmt.Errorf("could not verify: %w", err)) + claims, apiError := verifyClaims(r, config, rawIDToken) + if apiError != nil { + api.JSONErrorStatus(w, apiError.Status, apiError.Internal) return } - // Parse and verify ID Token payload. - idToken, err := verifier.Verify(r.Context(), rawIDToken) - if err != nil { - api.JSONErrorStatus(w, http.StatusInternalServerError, fmt.Errorf("token verification failed: %w", err)) - return - } - - // Extract custom claims - var claims map[string]interface{} - if err := idToken.Claims(&claims); err != nil { - api.JSONErrorStatus(w, http.StatusInternalServerError, errors.New("claim extraction failed")) - return - } - - b, _ := json.Marshal(claims) - http.SetCookie(w, &http.Cookie{Name: userSessionCookie, Value: base64.StdEncoding.EncodeToString(b)}) + setClaimsCookie(w, claims) http.Redirect(w, r, "/", http.StatusFound) } @@ -417,3 +339,20 @@ func state() (string, error) { } return base64.URLEncoding.EncodeToString(rnd), nil } + +func verifyClaims(r *http.Request, config *AuthConfig, rawIDToken string) (map[string]interface{}, *api.HTTPError) { + verifier, err := config.Verifier(r.Context()) + if err != nil { + return nil, &api.HTTPError{Status: http.StatusUnauthorized, Internal: fmt.Errorf("could not verify: %w", err)} + } + authToken, err := verifier.Verify(r.Context(), rawIDToken) + if err != nil { + return nil, &api.HTTPError{Status: http.StatusInternalServerError, Internal: fmt.Errorf("could not verify bearer token: %w", err)} + } + + var claims map[string]interface{} + if err := authToken.Claims(&claims); err != nil { + return nil, &api.HTTPError{Status: http.StatusInternalServerError, Internal: fmt.Errorf("failed to parse claims: %w", err)} + } + return claims, nil +} diff --git a/busservice/busservice.go b/busservice/busservice.go index d676d7e..1129779 100644 --- a/busservice/busservice.go +++ b/busservice/busservice.go @@ -8,8 +8,8 @@ import ( "github.com/SecurityBrewery/catalyst/database" "github.com/SecurityBrewery/catalyst/database/busdb" "github.com/SecurityBrewery/catalyst/generated/model" + "github.com/SecurityBrewery/catalyst/generated/time" "github.com/SecurityBrewery/catalyst/role" - "github.com/SecurityBrewery/catalyst/time" ) type busService struct { diff --git a/cmd/catalyst-dev/main.go b/cmd/catalyst-dev/main.go index cd0f313..484e0d6 100644 --- a/cmd/catalyst-dev/main.go +++ b/cmd/catalyst-dev/main.go @@ -4,14 +4,13 @@ import ( "context" "log" "net/http" - "net/http/httputil" - "net/url" "github.com/arangodb/go-driver" "github.com/SecurityBrewery/catalyst" "github.com/SecurityBrewery/catalyst/cmd" "github.com/SecurityBrewery/catalyst/database/busdb" + "github.com/SecurityBrewery/catalyst/generated/api" "github.com/SecurityBrewery/catalyst/generated/model" "github.com/SecurityBrewery/catalyst/hooks" "github.com/SecurityBrewery/catalyst/role" @@ -41,18 +40,13 @@ func main() { } // proxy static requests - theCatalyst.Server.With(catalyst.Authenticate(theCatalyst.DB, config.Auth), catalyst.AuthorizeBlockedUser()).NotFound(proxy) + middlewares := []func(next http.Handler) http.Handler{ + catalyst.Authenticate(theCatalyst.DB, config.Auth), + catalyst.AuthorizeBlockedUser(), + } + theCatalyst.Server.With(middlewares...).NotFound(api.Proxy("http://localhost:8080")) if err := http.ListenAndServe(":8000", theCatalyst.Server); err != nil { log.Fatal(err) } } - -func proxy(w http.ResponseWriter, r *http.Request) { - u, _ := url.Parse("http://localhost:8080") - proxy := httputil.NewSingleHostReverseProxy(u) - - r.Host = r.URL.Host - - proxy.ServeHTTP(w, r) -} diff --git a/cmd/cmd.go b/cmd/cmd.go index e523e7f..84091cc 100644 --- a/cmd/cmd.go +++ b/cmd/cmd.go @@ -12,7 +12,7 @@ import ( "github.com/SecurityBrewery/catalyst/bus" "github.com/SecurityBrewery/catalyst/database" "github.com/SecurityBrewery/catalyst/generated/model" - "github.com/SecurityBrewery/catalyst/pointer" + "github.com/SecurityBrewery/catalyst/generated/pointer" "github.com/SecurityBrewery/catalyst/role" "github.com/SecurityBrewery/catalyst/storage" ) diff --git a/cookie.go b/cookie.go new file mode 100644 index 0000000..2e1bb56 --- /dev/null +++ b/cookie.go @@ -0,0 +1,49 @@ +package catalyst + +import ( + "encoding/base64" + "encoding/json" + "errors" + "fmt" + "net/http" +) + +const ( + stateSessionCookie = "state" + userSessionCookie = "user" +) + +func setStateCookie(w http.ResponseWriter, state string) { + http.SetCookie(w, &http.Cookie{Name: stateSessionCookie, Value: state}) +} + +func stateCookie(r *http.Request) (string, error) { + stateCookie, err := r.Cookie(stateSessionCookie) + if err != nil { + return "", err + } + return stateCookie.Value, nil +} + +func setClaimsCookie(w http.ResponseWriter, claims map[string]interface{}) { + b, _ := json.Marshal(claims) + http.SetCookie(w, &http.Cookie{Name: userSessionCookie, Value: base64.StdEncoding.EncodeToString(b)}) +} + +func claimsCookie(r *http.Request) (map[string]interface{}, bool, error) { + userCookie, err := r.Cookie(userSessionCookie) + if err != nil { + return nil, true, nil + } + + b, err := base64.StdEncoding.DecodeString(userCookie.Value) + if err != nil { + return nil, false, fmt.Errorf("could not decode cookie: %w", err) + } + + var claims map[string]interface{} + if err := json.Unmarshal(b, &claims); err != nil { + return nil, false, errors.New("claims not in session") + } + return claims, false, err +} diff --git a/database/artifact.go b/database/artifact.go index cd06003..c27c249 100644 --- a/database/artifact.go +++ b/database/artifact.go @@ -9,7 +9,7 @@ import ( "github.com/SecurityBrewery/catalyst/bus" "github.com/SecurityBrewery/catalyst/database/busdb" "github.com/SecurityBrewery/catalyst/generated/model" - "github.com/SecurityBrewery/catalyst/time" + "github.com/SecurityBrewery/catalyst/generated/time" ) func (db *Database) ArtifactGet(ctx context.Context, id int64, name string) (*model.Artifact, error) { diff --git a/database/busdb/log.go b/database/busdb/log.go index 1b33036..62e5d45 100644 --- a/database/busdb/log.go +++ b/database/busdb/log.go @@ -9,7 +9,7 @@ import ( "github.com/SecurityBrewery/catalyst/bus" "github.com/SecurityBrewery/catalyst/generated/model" - "github.com/SecurityBrewery/catalyst/time" + "github.com/SecurityBrewery/catalyst/generated/time" ) const LogCollectionName = "logs" diff --git a/database/migrations/migrations.go b/database/migrations/migrations.go index 97b09b7..3eae5c6 100644 --- a/database/migrations/migrations.go +++ b/database/migrations/migrations.go @@ -8,7 +8,7 @@ import ( "github.com/SecurityBrewery/catalyst/database/busdb" "github.com/SecurityBrewery/catalyst/generated/model" - "github.com/SecurityBrewery/catalyst/pointer" + "github.com/SecurityBrewery/catalyst/generated/pointer" ) const MigrationCollection string = "migrations" diff --git a/database/playbook.go b/database/playbook.go index ac08f79..8d99cb7 100644 --- a/database/playbook.go +++ b/database/playbook.go @@ -11,7 +11,7 @@ import ( "github.com/SecurityBrewery/catalyst/database/busdb" "github.com/SecurityBrewery/catalyst/generated/model" - "github.com/SecurityBrewery/catalyst/time" + "github.com/SecurityBrewery/catalyst/generated/time" ) type PlaybookYAML struct { diff --git a/database/settings_test.go b/database/settings_test.go index f52ab68..e807ab7 100644 --- a/database/settings_test.go +++ b/database/settings_test.go @@ -6,7 +6,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/SecurityBrewery/catalyst/generated/model" - "github.com/SecurityBrewery/catalyst/pointer" + "github.com/SecurityBrewery/catalyst/generated/pointer" "github.com/SecurityBrewery/catalyst/test" ) diff --git a/database/ticket.go b/database/ticket.go index 39519d3..70b5091 100644 --- a/database/ticket.go +++ b/database/ticket.go @@ -17,8 +17,8 @@ import ( "github.com/SecurityBrewery/catalyst/caql" "github.com/SecurityBrewery/catalyst/database/busdb" "github.com/SecurityBrewery/catalyst/generated/model" + "github.com/SecurityBrewery/catalyst/generated/time" "github.com/SecurityBrewery/catalyst/index" - "github.com/SecurityBrewery/catalyst/time" ) func toTicket(ticketForm *model.TicketForm) (interface{}, error) { diff --git a/database/ticket_field.go b/database/ticket_field.go index c641028..475698f 100644 --- a/database/ticket_field.go +++ b/database/ticket_field.go @@ -12,8 +12,8 @@ import ( "github.com/SecurityBrewery/catalyst/bus" "github.com/SecurityBrewery/catalyst/database/busdb" "github.com/SecurityBrewery/catalyst/generated/model" - "github.com/SecurityBrewery/catalyst/pointer" - "github.com/SecurityBrewery/catalyst/time" + "github.com/SecurityBrewery/catalyst/generated/pointer" + "github.com/SecurityBrewery/catalyst/generated/time" ) func (db *Database) AddArtifact(ctx context.Context, id int64, artifact *model.Artifact) (*model.TicketWithTickets, error) { diff --git a/database/ticket_task.go b/database/ticket_task.go index 7f0616a..8e26c5d 100644 --- a/database/ticket_task.go +++ b/database/ticket_task.go @@ -12,7 +12,7 @@ import ( "github.com/SecurityBrewery/catalyst/bus" "github.com/SecurityBrewery/catalyst/database/busdb" "github.com/SecurityBrewery/catalyst/generated/model" - "github.com/SecurityBrewery/catalyst/time" + "github.com/SecurityBrewery/catalyst/generated/time" ) func (db *Database) TaskGet(ctx context.Context, id int64, playbookID string, taskID string) (*model.TicketWithTickets, *model.PlaybookResponse, *model.TaskWithContext, error) { diff --git a/database/user.go b/database/user.go index ea752c7..4c8ca80 100644 --- a/database/user.go +++ b/database/user.go @@ -12,9 +12,9 @@ import ( "github.com/SecurityBrewery/catalyst/database/busdb" "github.com/SecurityBrewery/catalyst/generated/model" - "github.com/SecurityBrewery/catalyst/pointer" + "github.com/SecurityBrewery/catalyst/generated/pointer" + "github.com/SecurityBrewery/catalyst/generated/time" "github.com/SecurityBrewery/catalyst/role" - "github.com/SecurityBrewery/catalyst/time" ) var letters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_") diff --git a/generate.sh b/generate.sh index f0b5048..8f7dd8c 100644 --- a/generate.sh +++ b/generate.sh @@ -20,6 +20,7 @@ mv generated/openapi.json generated/catalyst.json echo generate server and tests swagger-go-chi generated/community.yml generated +rm -rf generated/auth generated/cli echo generate typescript client openapi-generator generate -i generated/catalyst.yml -o ui/src/client -g typescript-axios --artifact-version 1.0.0-SNAPSHOT diff --git a/generated/api/api.go b/generated/api/api.go index c6ba70b..d6f5917 100755 --- a/generated/api/api.go +++ b/generated/api/api.go @@ -2,18 +2,14 @@ package api import ( "bytes" - "context" "encoding/json" "errors" "fmt" - "io" "net/http" "strconv" "github.com/go-chi/chi" "github.com/xeipuuv/gojsonschema" - - "github.com/SecurityBrewery/catalyst/generated/model" ) type HTTPError struct { @@ -29,1469 +25,6 @@ func (e *HTTPError) Unwrap() error { return e.Internal } -type Service interface { - ListAutomations(context.Context) ([]*model.AutomationResponse, error) - CreateAutomation(context.Context, *model.AutomationForm) (*model.AutomationResponse, error) - GetAutomation(context.Context, string) (*model.AutomationResponse, error) - UpdateAutomation(context.Context, string, *model.AutomationForm) (*model.AutomationResponse, error) - DeleteAutomation(context.Context, string) error - CurrentUser(context.Context) (*model.UserResponse, error) - CurrentUserData(context.Context) (*model.UserDataResponse, error) - UpdateCurrentUserData(context.Context, *model.UserData) (*model.UserDataResponse, error) - ListJobs(context.Context) ([]*model.JobResponse, error) - RunJob(context.Context, *model.JobForm) (*model.JobResponse, error) - GetJob(context.Context, string) (*model.JobResponse, error) - UpdateJob(context.Context, string, *model.JobUpdate) (*model.JobResponse, error) - GetLogs(context.Context, string) ([]*model.LogEntry, error) - ListPlaybooks(context.Context) ([]*model.PlaybookTemplateResponse, error) - CreatePlaybook(context.Context, *model.PlaybookTemplateForm) (*model.PlaybookTemplateResponse, error) - 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) - GetStatistics(context.Context) (*model.Statistics, error) - ListTasks(context.Context) ([]*model.TaskWithContext, error) - ListTemplates(context.Context) ([]*model.TicketTemplateResponse, error) - CreateTemplate(context.Context, *model.TicketTemplateForm) (*model.TicketTemplateResponse, error) - GetTemplate(context.Context, string) (*model.TicketTemplateResponse, error) - UpdateTemplate(context.Context, string, *model.TicketTemplateForm) (*model.TicketTemplateResponse, error) - DeleteTemplate(context.Context, string) error - ListTickets(context.Context, *string, *int, *int, []string, []bool, *string) (*model.TicketList, error) - CreateTicket(context.Context, *model.TicketForm) (*model.TicketResponse, error) - CreateTicketBatch(context.Context, *model.TicketFormArray) error - GetTicket(context.Context, int64) (*model.TicketWithTickets, error) - UpdateTicket(context.Context, int64, *model.Ticket) (*model.TicketWithTickets, error) - DeleteTicket(context.Context, int64) error - AddArtifact(context.Context, int64, *model.Artifact) (*model.TicketWithTickets, error) - GetArtifact(context.Context, int64, string) (*model.Artifact, error) - SetArtifact(context.Context, int64, string, *model.Artifact) (*model.TicketWithTickets, error) - RemoveArtifact(context.Context, int64, string) (*model.TicketWithTickets, error) - EnrichArtifact(context.Context, int64, string, *model.EnrichmentForm) (*model.TicketWithTickets, error) - RunArtifact(context.Context, int64, string, string) error - AddComment(context.Context, int64, *model.CommentForm) (*model.TicketWithTickets, error) - RemoveComment(context.Context, int64, int) (*model.TicketWithTickets, error) - AddTicketPlaybook(context.Context, int64, *model.PlaybookTemplateForm) (*model.TicketWithTickets, error) - RemoveTicketPlaybook(context.Context, int64, string) (*model.TicketWithTickets, error) - SetTaskData(context.Context, int64, string, string, map[string]interface{}) (*model.TicketWithTickets, error) - CompleteTask(context.Context, int64, string, string, map[string]interface{}) (*model.TicketWithTickets, error) - SetTaskOwner(context.Context, int64, string, string, string) (*model.TicketWithTickets, error) - RunTask(context.Context, int64, string, string) error - SetReferences(context.Context, int64, *model.ReferenceArray) (*model.TicketWithTickets, error) - SetSchema(context.Context, int64, string) (*model.TicketWithTickets, error) - LinkTicket(context.Context, int64, int64) (*model.TicketWithTickets, error) - UnlinkTicket(context.Context, int64, int64) (*model.TicketWithTickets, error) - ListTicketTypes(context.Context) ([]*model.TicketTypeResponse, error) - CreateTicketType(context.Context, *model.TicketTypeForm) (*model.TicketTypeResponse, error) - GetTicketType(context.Context, string) (*model.TicketTypeResponse, error) - UpdateTicketType(context.Context, string, *model.TicketTypeForm) (*model.TicketTypeResponse, error) - DeleteTicketType(context.Context, string) error - ListUserData(context.Context) ([]*model.UserDataResponse, error) - GetUserData(context.Context, string) (*model.UserDataResponse, error) - UpdateUserData(context.Context, string, *model.UserData) (*model.UserDataResponse, error) - ListUsers(context.Context) ([]*model.UserResponse, error) - CreateUser(context.Context, *model.UserForm) (*model.NewUserResponse, error) - GetUser(context.Context, string) (*model.UserResponse, error) - UpdateUser(context.Context, string, *model.UserForm) (*model.UserResponse, error) - DeleteUser(context.Context, string) error -} - -func NewServer(service Service, roleAuth func([]string) func(http.Handler) http.Handler, middlewares ...func(http.Handler) http.Handler) chi.Router { - r := chi.NewRouter() - r.Use(middlewares...) - - s := &server{service} - - r.With(roleAuth([]string{"automation:read"})).Get("/automations", s.listAutomationsHandler) - r.With(roleAuth([]string{"automation:write"})).Post("/automations", s.createAutomationHandler) - r.With(roleAuth([]string{"automation:read"})).Get("/automations/{id}", s.getAutomationHandler) - r.With(roleAuth([]string{"automation:write"})).Put("/automations/{id}", s.updateAutomationHandler) - r.With(roleAuth([]string{"automation:write"})).Delete("/automations/{id}", s.deleteAutomationHandler) - r.With(roleAuth([]string{"currentuser:read"})).Get("/currentuser", s.currentUserHandler) - r.With(roleAuth([]string{"currentuserdata:read"})).Get("/currentuserdata", s.currentUserDataHandler) - r.With(roleAuth([]string{"currentuserdata:write"})).Put("/currentuserdata", s.updateCurrentUserDataHandler) - r.With(roleAuth([]string{"job:read"})).Get("/jobs", s.listJobsHandler) - r.With(roleAuth([]string{"job:write"})).Post("/jobs", s.runJobHandler) - r.With(roleAuth([]string{"job:read"})).Get("/jobs/{id}", s.getJobHandler) - r.With(roleAuth([]string{"job:write"})).Put("/jobs/{id}", s.updateJobHandler) - r.With(roleAuth([]string{"log:read"})).Get("/logs/{reference}", s.getLogsHandler) - r.With(roleAuth([]string{"playbook:read"})).Get("/playbooks", s.listPlaybooksHandler) - r.With(roleAuth([]string{"playbook:write"})).Post("/playbooks", s.createPlaybookHandler) - r.With(roleAuth([]string{"playbook:read"})).Get("/playbooks/{id}", s.getPlaybookHandler) - 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{"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) - r.With(roleAuth([]string{"template:write"})).Post("/templates", s.createTemplateHandler) - r.With(roleAuth([]string{"template:read"})).Get("/templates/{id}", s.getTemplateHandler) - r.With(roleAuth([]string{"template:write"})).Put("/templates/{id}", s.updateTemplateHandler) - r.With(roleAuth([]string{"template:write"})).Delete("/templates/{id}", s.deleteTemplateHandler) - r.With(roleAuth([]string{"ticket:read"})).Get("/tickets", s.listTicketsHandler) - r.With(roleAuth([]string{"ticket:write"})).Post("/tickets", s.createTicketHandler) - r.With(roleAuth([]string{"ticket:write"})).Post("/tickets/batch", s.createTicketBatchHandler) - r.With(roleAuth([]string{"ticket:read"})).Get("/tickets/{id}", s.getTicketHandler) - r.With(roleAuth([]string{"ticket:write"})).Put("/tickets/{id}", s.updateTicketHandler) - r.With(roleAuth([]string{"ticket:delete"})).Delete("/tickets/{id}", s.deleteTicketHandler) - r.With(roleAuth([]string{"ticket:write"})).Post("/tickets/{id}/artifacts", s.addArtifactHandler) - r.With(roleAuth([]string{"ticket:write"})).Get("/tickets/{id}/artifacts/{name}", s.getArtifactHandler) - r.With(roleAuth([]string{"ticket:write"})).Put("/tickets/{id}/artifacts/{name}", s.setArtifactHandler) - r.With(roleAuth([]string{"ticket:write"})).Delete("/tickets/{id}/artifacts/{name}", s.removeArtifactHandler) - r.With(roleAuth([]string{"ticket:write"})).Post("/tickets/{id}/artifacts/{name}/enrich", s.enrichArtifactHandler) - r.With(roleAuth([]string{"ticket:write"})).Post("/tickets/{id}/artifacts/{name}/run/{automation}", s.runArtifactHandler) - r.With(roleAuth([]string{"ticket:write"})).Post("/tickets/{id}/comments", s.addCommentHandler) - r.With(roleAuth([]string{"ticket:write"})).Delete("/tickets/{id}/comments/{commentID}", s.removeCommentHandler) - r.With(roleAuth([]string{"ticket:write"})).Post("/tickets/{id}/playbooks", s.addTicketPlaybookHandler) - r.With(roleAuth([]string{"ticket:write"})).Delete("/tickets/{id}/playbooks/{playbookID}", s.removeTicketPlaybookHandler) - r.With(roleAuth([]string{"ticket:write"})).Put("/tickets/{id}/playbooks/{playbookID}/task/{taskID}", s.setTaskDataHandler) - r.With(roleAuth([]string{"ticket:write"})).Put("/tickets/{id}/playbooks/{playbookID}/task/{taskID}/complete", s.completeTaskHandler) - r.With(roleAuth([]string{"ticket:write"})).Put("/tickets/{id}/playbooks/{playbookID}/task/{taskID}/owner", s.setTaskOwnerHandler) - r.With(roleAuth([]string{"ticket:write"})).Post("/tickets/{id}/playbooks/{playbookID}/task/{taskID}/run", s.runTaskHandler) - r.With(roleAuth([]string{"ticket:write"})).Put("/tickets/{id}/references", s.setReferencesHandler) - r.With(roleAuth([]string{"ticket:write"})).Put("/tickets/{id}/schema", s.setSchemaHandler) - r.With(roleAuth([]string{"ticket:write"})).Patch("/tickets/{id}/tickets", s.linkTicketHandler) - r.With(roleAuth([]string{"ticket:write"})).Delete("/tickets/{id}/tickets", s.unlinkTicketHandler) - r.With(roleAuth([]string{"tickettype:read"})).Get("/tickettypes", s.listTicketTypesHandler) - r.With(roleAuth([]string{"tickettype:write"})).Post("/tickettypes", s.createTicketTypeHandler) - r.With(roleAuth([]string{"tickettype:read"})).Get("/tickettypes/{id}", s.getTicketTypeHandler) - r.With(roleAuth([]string{"tickettype:write"})).Put("/tickettypes/{id}", s.updateTicketTypeHandler) - r.With(roleAuth([]string{"tickettype:write"})).Delete("/tickettypes/{id}", s.deleteTicketTypeHandler) - r.With(roleAuth([]string{"userdata:read"})).Get("/userdata", s.listUserDataHandler) - r.With(roleAuth([]string{"userdata:read"})).Get("/userdata/{id}", s.getUserDataHandler) - r.With(roleAuth([]string{"userdata:write"})).Put("/userdata/{id}", s.updateUserDataHandler) - r.With(roleAuth([]string{"user:read"})).Get("/users", s.listUsersHandler) - r.With(roleAuth([]string{"user:write"})).Post("/users", s.createUserHandler) - r.With(roleAuth([]string{"user:read"})).Get("/users/{id}", s.getUserHandler) - r.With(roleAuth([]string{"user:write"})).Put("/users/{id}", s.updateUserHandler) - r.With(roleAuth([]string{"user:write"})).Delete("/users/{id}", s.deleteUserHandler) - return r -} - -type server struct { - service Service -} - -func (s *server) listAutomationsHandler(w http.ResponseWriter, r *http.Request) { - result, err := s.service.ListAutomations(r.Context()) - response(w, result, err) -} - -func (s *server) createAutomationHandler(w http.ResponseWriter, r *http.Request) { - body, err := io.ReadAll(r.Body) - if err != nil { - JSONError(w, err) - return - } - - jl := gojsonschema.NewBytesLoader(body) - validationResult, err := model.AutomationFormSchema.Validate(jl) - if err != nil { - JSONError(w, err) - return - } - if !validationResult.Valid() { - w.WriteHeader(http.StatusUnprocessableEntity) - - var valdiationErrors []string - for _, valdiationError := range validationResult.Errors() { - valdiationErrors = append(valdiationErrors, valdiationError.String()) - } - - b, _ := json.Marshal(map[string]interface{}{"error": "wrong input", "errors": valdiationErrors}) - w.Write(b) - return - } - - var automationP *model.AutomationForm - if err := parseBody(body, &automationP); err != nil { - JSONError(w, err) - return - } - - result, err := s.service.CreateAutomation(r.Context(), automationP) - response(w, result, err) -} - -func (s *server) getAutomationHandler(w http.ResponseWriter, r *http.Request) { - idP := chi.URLParam(r, "id") - - result, err := s.service.GetAutomation(r.Context(), idP) - response(w, result, err) -} - -func (s *server) updateAutomationHandler(w http.ResponseWriter, r *http.Request) { - idP := chi.URLParam(r, "id") - - body, err := io.ReadAll(r.Body) - if err != nil { - JSONError(w, err) - return - } - - jl := gojsonschema.NewBytesLoader(body) - validationResult, err := model.AutomationFormSchema.Validate(jl) - if err != nil { - JSONError(w, err) - return - } - if !validationResult.Valid() { - w.WriteHeader(http.StatusUnprocessableEntity) - - var valdiationErrors []string - for _, valdiationError := range validationResult.Errors() { - valdiationErrors = append(valdiationErrors, valdiationError.String()) - } - - b, _ := json.Marshal(map[string]interface{}{"error": "wrong input", "errors": valdiationErrors}) - w.Write(b) - return - } - - var automationP *model.AutomationForm - if err := parseBody(body, &automationP); err != nil { - JSONError(w, err) - return - } - - result, err := s.service.UpdateAutomation(r.Context(), idP, automationP) - response(w, result, err) -} - -func (s *server) deleteAutomationHandler(w http.ResponseWriter, r *http.Request) { - idP := chi.URLParam(r, "id") - - response(w, nil, s.service.DeleteAutomation(r.Context(), idP)) -} - -func (s *server) currentUserHandler(w http.ResponseWriter, r *http.Request) { - result, err := s.service.CurrentUser(r.Context()) - response(w, result, err) -} - -func (s *server) currentUserDataHandler(w http.ResponseWriter, r *http.Request) { - result, err := s.service.CurrentUserData(r.Context()) - response(w, result, err) -} - -func (s *server) updateCurrentUserDataHandler(w http.ResponseWriter, r *http.Request) { - body, err := io.ReadAll(r.Body) - if err != nil { - JSONError(w, err) - return - } - - jl := gojsonschema.NewBytesLoader(body) - validationResult, err := model.UserDataSchema.Validate(jl) - if err != nil { - JSONError(w, err) - return - } - if !validationResult.Valid() { - w.WriteHeader(http.StatusUnprocessableEntity) - - var valdiationErrors []string - for _, valdiationError := range validationResult.Errors() { - valdiationErrors = append(valdiationErrors, valdiationError.String()) - } - - b, _ := json.Marshal(map[string]interface{}{"error": "wrong input", "errors": valdiationErrors}) - w.Write(b) - return - } - - var userdataP *model.UserData - if err := parseBody(body, &userdataP); err != nil { - JSONError(w, err) - return - } - - result, err := s.service.UpdateCurrentUserData(r.Context(), userdataP) - response(w, result, err) -} - -func (s *server) listJobsHandler(w http.ResponseWriter, r *http.Request) { - result, err := s.service.ListJobs(r.Context()) - response(w, result, err) -} - -func (s *server) runJobHandler(w http.ResponseWriter, r *http.Request) { - body, err := io.ReadAll(r.Body) - if err != nil { - JSONError(w, err) - return - } - - jl := gojsonschema.NewBytesLoader(body) - validationResult, err := model.JobFormSchema.Validate(jl) - if err != nil { - JSONError(w, err) - return - } - if !validationResult.Valid() { - w.WriteHeader(http.StatusUnprocessableEntity) - - var valdiationErrors []string - for _, valdiationError := range validationResult.Errors() { - valdiationErrors = append(valdiationErrors, valdiationError.String()) - } - - b, _ := json.Marshal(map[string]interface{}{"error": "wrong input", "errors": valdiationErrors}) - w.Write(b) - return - } - - var jobP *model.JobForm - if err := parseBody(body, &jobP); err != nil { - JSONError(w, err) - return - } - - result, err := s.service.RunJob(r.Context(), jobP) - response(w, result, err) -} - -func (s *server) getJobHandler(w http.ResponseWriter, r *http.Request) { - idP := chi.URLParam(r, "id") - - result, err := s.service.GetJob(r.Context(), idP) - response(w, result, err) -} - -func (s *server) updateJobHandler(w http.ResponseWriter, r *http.Request) { - idP := chi.URLParam(r, "id") - - body, err := io.ReadAll(r.Body) - if err != nil { - JSONError(w, err) - return - } - - jl := gojsonschema.NewBytesLoader(body) - validationResult, err := model.JobUpdateSchema.Validate(jl) - if err != nil { - JSONError(w, err) - return - } - if !validationResult.Valid() { - w.WriteHeader(http.StatusUnprocessableEntity) - - var valdiationErrors []string - for _, valdiationError := range validationResult.Errors() { - valdiationErrors = append(valdiationErrors, valdiationError.String()) - } - - b, _ := json.Marshal(map[string]interface{}{"error": "wrong input", "errors": valdiationErrors}) - w.Write(b) - return - } - - var jobP *model.JobUpdate - if err := parseBody(body, &jobP); err != nil { - JSONError(w, err) - return - } - - result, err := s.service.UpdateJob(r.Context(), idP, jobP) - response(w, result, err) -} - -func (s *server) getLogsHandler(w http.ResponseWriter, r *http.Request) { - referenceP := chi.URLParam(r, "reference") - - result, err := s.service.GetLogs(r.Context(), referenceP) - response(w, result, err) -} - -func (s *server) listPlaybooksHandler(w http.ResponseWriter, r *http.Request) { - result, err := s.service.ListPlaybooks(r.Context()) - response(w, result, err) -} - -func (s *server) createPlaybookHandler(w http.ResponseWriter, r *http.Request) { - body, err := io.ReadAll(r.Body) - if err != nil { - JSONError(w, err) - return - } - - jl := gojsonschema.NewBytesLoader(body) - validationResult, err := model.PlaybookTemplateFormSchema.Validate(jl) - if err != nil { - JSONError(w, err) - return - } - if !validationResult.Valid() { - w.WriteHeader(http.StatusUnprocessableEntity) - - var valdiationErrors []string - for _, valdiationError := range validationResult.Errors() { - valdiationErrors = append(valdiationErrors, valdiationError.String()) - } - - b, _ := json.Marshal(map[string]interface{}{"error": "wrong input", "errors": valdiationErrors}) - w.Write(b) - return - } - - var playbookP *model.PlaybookTemplateForm - if err := parseBody(body, &playbookP); err != nil { - JSONError(w, err) - return - } - - result, err := s.service.CreatePlaybook(r.Context(), playbookP) - response(w, result, err) -} - -func (s *server) getPlaybookHandler(w http.ResponseWriter, r *http.Request) { - idP := chi.URLParam(r, "id") - - result, err := s.service.GetPlaybook(r.Context(), idP) - response(w, result, err) -} - -func (s *server) updatePlaybookHandler(w http.ResponseWriter, r *http.Request) { - idP := chi.URLParam(r, "id") - - body, err := io.ReadAll(r.Body) - if err != nil { - JSONError(w, err) - return - } - - jl := gojsonschema.NewBytesLoader(body) - validationResult, err := model.PlaybookTemplateFormSchema.Validate(jl) - if err != nil { - JSONError(w, err) - return - } - if !validationResult.Valid() { - w.WriteHeader(http.StatusUnprocessableEntity) - - var valdiationErrors []string - for _, valdiationError := range validationResult.Errors() { - valdiationErrors = append(valdiationErrors, valdiationError.String()) - } - - b, _ := json.Marshal(map[string]interface{}{"error": "wrong input", "errors": valdiationErrors}) - w.Write(b) - return - } - - var playbookP *model.PlaybookTemplateForm - if err := parseBody(body, &playbookP); err != nil { - JSONError(w, err) - return - } - - result, err := s.service.UpdatePlaybook(r.Context(), idP, playbookP) - response(w, result, err) -} - -func (s *server) deletePlaybookHandler(w http.ResponseWriter, r *http.Request) { - idP := chi.URLParam(r, "id") - - response(w, nil, s.service.DeletePlaybook(r.Context(), idP)) -} - -func (s *server) getSettingsHandler(w http.ResponseWriter, r *http.Request) { - result, err := s.service.GetSettings(r.Context()) - 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) -} - -func (s *server) listTasksHandler(w http.ResponseWriter, r *http.Request) { - result, err := s.service.ListTasks(r.Context()) - response(w, result, err) -} - -func (s *server) listTemplatesHandler(w http.ResponseWriter, r *http.Request) { - result, err := s.service.ListTemplates(r.Context()) - response(w, result, err) -} - -func (s *server) createTemplateHandler(w http.ResponseWriter, r *http.Request) { - body, err := io.ReadAll(r.Body) - if err != nil { - JSONError(w, err) - return - } - - jl := gojsonschema.NewBytesLoader(body) - validationResult, err := model.TicketTemplateFormSchema.Validate(jl) - if err != nil { - JSONError(w, err) - return - } - if !validationResult.Valid() { - w.WriteHeader(http.StatusUnprocessableEntity) - - var valdiationErrors []string - for _, valdiationError := range validationResult.Errors() { - valdiationErrors = append(valdiationErrors, valdiationError.String()) - } - - b, _ := json.Marshal(map[string]interface{}{"error": "wrong input", "errors": valdiationErrors}) - w.Write(b) - return - } - - var templateP *model.TicketTemplateForm - if err := parseBody(body, &templateP); err != nil { - JSONError(w, err) - return - } - - result, err := s.service.CreateTemplate(r.Context(), templateP) - response(w, result, err) -} - -func (s *server) getTemplateHandler(w http.ResponseWriter, r *http.Request) { - idP := chi.URLParam(r, "id") - - result, err := s.service.GetTemplate(r.Context(), idP) - response(w, result, err) -} - -func (s *server) updateTemplateHandler(w http.ResponseWriter, r *http.Request) { - idP := chi.URLParam(r, "id") - - body, err := io.ReadAll(r.Body) - if err != nil { - JSONError(w, err) - return - } - - jl := gojsonschema.NewBytesLoader(body) - validationResult, err := model.TicketTemplateFormSchema.Validate(jl) - if err != nil { - JSONError(w, err) - return - } - if !validationResult.Valid() { - w.WriteHeader(http.StatusUnprocessableEntity) - - var valdiationErrors []string - for _, valdiationError := range validationResult.Errors() { - valdiationErrors = append(valdiationErrors, valdiationError.String()) - } - - b, _ := json.Marshal(map[string]interface{}{"error": "wrong input", "errors": valdiationErrors}) - w.Write(b) - return - } - - var templateP *model.TicketTemplateForm - if err := parseBody(body, &templateP); err != nil { - JSONError(w, err) - return - } - - result, err := s.service.UpdateTemplate(r.Context(), idP, templateP) - response(w, result, err) -} - -func (s *server) deleteTemplateHandler(w http.ResponseWriter, r *http.Request) { - idP := chi.URLParam(r, "id") - - response(w, nil, s.service.DeleteTemplate(r.Context(), idP)) -} - -func (s *server) listTicketsHandler(w http.ResponseWriter, r *http.Request) { - typeP := r.URL.Query().Get("type") - - offsetP, err := parseQueryOptionalInt(r, "offset") - if err != nil { - JSONError(w, err) - return - } - - countP, err := parseQueryOptionalInt(r, "count") - if err != nil { - JSONError(w, err) - return - } - - sortP, err := parseQueryOptionalStringArray(r, "sort") - if err != nil { - JSONError(w, err) - return - } - - descP, err := parseQueryOptionalBoolArray(r, "desc") - if err != nil { - JSONError(w, err) - return - } - - queryP := r.URL.Query().Get("query") - - result, err := s.service.ListTickets(r.Context(), &typeP, offsetP, countP, sortP, descP, &queryP) - response(w, result, err) -} - -func (s *server) createTicketHandler(w http.ResponseWriter, r *http.Request) { - body, err := io.ReadAll(r.Body) - if err != nil { - JSONError(w, err) - return - } - - jl := gojsonschema.NewBytesLoader(body) - validationResult, err := model.TicketFormSchema.Validate(jl) - if err != nil { - JSONError(w, err) - return - } - if !validationResult.Valid() { - w.WriteHeader(http.StatusUnprocessableEntity) - - var valdiationErrors []string - for _, valdiationError := range validationResult.Errors() { - valdiationErrors = append(valdiationErrors, valdiationError.String()) - } - - b, _ := json.Marshal(map[string]interface{}{"error": "wrong input", "errors": valdiationErrors}) - w.Write(b) - return - } - - var ticketP *model.TicketForm - if err := parseBody(body, &ticketP); err != nil { - JSONError(w, err) - return - } - - result, err := s.service.CreateTicket(r.Context(), ticketP) - response(w, result, err) -} - -func (s *server) createTicketBatchHandler(w http.ResponseWriter, r *http.Request) { - body, err := io.ReadAll(r.Body) - if err != nil { - JSONError(w, err) - return - } - - jl := gojsonschema.NewBytesLoader(body) - validationResult, err := model.TicketFormArraySchema.Validate(jl) - if err != nil { - JSONError(w, err) - return - } - if !validationResult.Valid() { - w.WriteHeader(http.StatusUnprocessableEntity) - - var valdiationErrors []string - for _, valdiationError := range validationResult.Errors() { - valdiationErrors = append(valdiationErrors, valdiationError.String()) - } - - b, _ := json.Marshal(map[string]interface{}{"error": "wrong input", "errors": valdiationErrors}) - w.Write(b) - return - } - - var ticketP *model.TicketFormArray - if err := parseBody(body, &ticketP); err != nil { - JSONError(w, err) - return - } - - response(w, nil, s.service.CreateTicketBatch(r.Context(), ticketP)) -} - -func (s *server) getTicketHandler(w http.ResponseWriter, r *http.Request) { - idP, err := parseURLInt64(r, "id") - if err != nil { - JSONError(w, err) - return - } - - result, err := s.service.GetTicket(r.Context(), idP) - response(w, result, err) -} - -func (s *server) updateTicketHandler(w http.ResponseWriter, r *http.Request) { - idP, err := parseURLInt64(r, "id") - if err != nil { - JSONError(w, err) - return - } - - body, err := io.ReadAll(r.Body) - if err != nil { - JSONError(w, err) - return - } - - jl := gojsonschema.NewBytesLoader(body) - validationResult, err := model.TicketSchema.Validate(jl) - if err != nil { - JSONError(w, err) - return - } - if !validationResult.Valid() { - w.WriteHeader(http.StatusUnprocessableEntity) - - var valdiationErrors []string - for _, valdiationError := range validationResult.Errors() { - valdiationErrors = append(valdiationErrors, valdiationError.String()) - } - - b, _ := json.Marshal(map[string]interface{}{"error": "wrong input", "errors": valdiationErrors}) - w.Write(b) - return - } - - var ticketP *model.Ticket - if err := parseBody(body, &ticketP); err != nil { - JSONError(w, err) - return - } - - result, err := s.service.UpdateTicket(r.Context(), idP, ticketP) - response(w, result, err) -} - -func (s *server) deleteTicketHandler(w http.ResponseWriter, r *http.Request) { - idP, err := parseURLInt64(r, "id") - if err != nil { - JSONError(w, err) - return - } - - response(w, nil, s.service.DeleteTicket(r.Context(), idP)) -} - -func (s *server) addArtifactHandler(w http.ResponseWriter, r *http.Request) { - idP, err := parseURLInt64(r, "id") - if err != nil { - JSONError(w, err) - return - } - - body, err := io.ReadAll(r.Body) - if err != nil { - JSONError(w, err) - return - } - - jl := gojsonschema.NewBytesLoader(body) - validationResult, err := model.ArtifactSchema.Validate(jl) - if err != nil { - JSONError(w, err) - return - } - if !validationResult.Valid() { - w.WriteHeader(http.StatusUnprocessableEntity) - - var valdiationErrors []string - for _, valdiationError := range validationResult.Errors() { - valdiationErrors = append(valdiationErrors, valdiationError.String()) - } - - b, _ := json.Marshal(map[string]interface{}{"error": "wrong input", "errors": valdiationErrors}) - w.Write(b) - return - } - - var artifactP *model.Artifact - if err := parseBody(body, &artifactP); err != nil { - JSONError(w, err) - return - } - - result, err := s.service.AddArtifact(r.Context(), idP, artifactP) - response(w, result, err) -} - -func (s *server) getArtifactHandler(w http.ResponseWriter, r *http.Request) { - idP, err := parseURLInt64(r, "id") - if err != nil { - JSONError(w, err) - return - } - - nameP := chi.URLParam(r, "name") - - result, err := s.service.GetArtifact(r.Context(), idP, nameP) - response(w, result, err) -} - -func (s *server) setArtifactHandler(w http.ResponseWriter, r *http.Request) { - idP, err := parseURLInt64(r, "id") - if err != nil { - JSONError(w, err) - return - } - - nameP := chi.URLParam(r, "name") - - body, err := io.ReadAll(r.Body) - if err != nil { - JSONError(w, err) - return - } - - jl := gojsonschema.NewBytesLoader(body) - validationResult, err := model.ArtifactSchema.Validate(jl) - if err != nil { - JSONError(w, err) - return - } - if !validationResult.Valid() { - w.WriteHeader(http.StatusUnprocessableEntity) - - var valdiationErrors []string - for _, valdiationError := range validationResult.Errors() { - valdiationErrors = append(valdiationErrors, valdiationError.String()) - } - - b, _ := json.Marshal(map[string]interface{}{"error": "wrong input", "errors": valdiationErrors}) - w.Write(b) - return - } - - var artifactP *model.Artifact - if err := parseBody(body, &artifactP); err != nil { - JSONError(w, err) - return - } - - result, err := s.service.SetArtifact(r.Context(), idP, nameP, artifactP) - response(w, result, err) -} - -func (s *server) removeArtifactHandler(w http.ResponseWriter, r *http.Request) { - idP, err := parseURLInt64(r, "id") - if err != nil { - JSONError(w, err) - return - } - - nameP := chi.URLParam(r, "name") - - result, err := s.service.RemoveArtifact(r.Context(), idP, nameP) - response(w, result, err) -} - -func (s *server) enrichArtifactHandler(w http.ResponseWriter, r *http.Request) { - idP, err := parseURLInt64(r, "id") - if err != nil { - JSONError(w, err) - return - } - - nameP := chi.URLParam(r, "name") - - body, err := io.ReadAll(r.Body) - if err != nil { - JSONError(w, err) - return - } - - jl := gojsonschema.NewBytesLoader(body) - validationResult, err := model.EnrichmentFormSchema.Validate(jl) - if err != nil { - JSONError(w, err) - return - } - if !validationResult.Valid() { - w.WriteHeader(http.StatusUnprocessableEntity) - - var valdiationErrors []string - for _, valdiationError := range validationResult.Errors() { - valdiationErrors = append(valdiationErrors, valdiationError.String()) - } - - b, _ := json.Marshal(map[string]interface{}{"error": "wrong input", "errors": valdiationErrors}) - w.Write(b) - return - } - - var dataP *model.EnrichmentForm - if err := parseBody(body, &dataP); err != nil { - JSONError(w, err) - return - } - - result, err := s.service.EnrichArtifact(r.Context(), idP, nameP, dataP) - response(w, result, err) -} - -func (s *server) runArtifactHandler(w http.ResponseWriter, r *http.Request) { - idP, err := parseURLInt64(r, "id") - if err != nil { - JSONError(w, err) - return - } - - nameP := chi.URLParam(r, "name") - - automationP := chi.URLParam(r, "automation") - - response(w, nil, s.service.RunArtifact(r.Context(), idP, nameP, automationP)) -} - -func (s *server) addCommentHandler(w http.ResponseWriter, r *http.Request) { - idP, err := parseURLInt64(r, "id") - if err != nil { - JSONError(w, err) - return - } - - body, err := io.ReadAll(r.Body) - if err != nil { - JSONError(w, err) - return - } - - jl := gojsonschema.NewBytesLoader(body) - validationResult, err := model.CommentFormSchema.Validate(jl) - if err != nil { - JSONError(w, err) - return - } - if !validationResult.Valid() { - w.WriteHeader(http.StatusUnprocessableEntity) - - var valdiationErrors []string - for _, valdiationError := range validationResult.Errors() { - valdiationErrors = append(valdiationErrors, valdiationError.String()) - } - - b, _ := json.Marshal(map[string]interface{}{"error": "wrong input", "errors": valdiationErrors}) - w.Write(b) - return - } - - var commentP *model.CommentForm - if err := parseBody(body, &commentP); err != nil { - JSONError(w, err) - return - } - - result, err := s.service.AddComment(r.Context(), idP, commentP) - response(w, result, err) -} - -func (s *server) removeCommentHandler(w http.ResponseWriter, r *http.Request) { - idP, err := parseURLInt64(r, "id") - if err != nil { - JSONError(w, err) - return - } - - commentIDP, err := parseURLInt(r, "commentID") - if err != nil { - JSONError(w, err) - return - } - - result, err := s.service.RemoveComment(r.Context(), idP, commentIDP) - response(w, result, err) -} - -func (s *server) addTicketPlaybookHandler(w http.ResponseWriter, r *http.Request) { - idP, err := parseURLInt64(r, "id") - if err != nil { - JSONError(w, err) - return - } - - body, err := io.ReadAll(r.Body) - if err != nil { - JSONError(w, err) - return - } - - jl := gojsonschema.NewBytesLoader(body) - validationResult, err := model.PlaybookTemplateFormSchema.Validate(jl) - if err != nil { - JSONError(w, err) - return - } - if !validationResult.Valid() { - w.WriteHeader(http.StatusUnprocessableEntity) - - var valdiationErrors []string - for _, valdiationError := range validationResult.Errors() { - valdiationErrors = append(valdiationErrors, valdiationError.String()) - } - - b, _ := json.Marshal(map[string]interface{}{"error": "wrong input", "errors": valdiationErrors}) - w.Write(b) - return - } - - var playbookP *model.PlaybookTemplateForm - if err := parseBody(body, &playbookP); err != nil { - JSONError(w, err) - return - } - - result, err := s.service.AddTicketPlaybook(r.Context(), idP, playbookP) - response(w, result, err) -} - -func (s *server) removeTicketPlaybookHandler(w http.ResponseWriter, r *http.Request) { - idP, err := parseURLInt64(r, "id") - if err != nil { - JSONError(w, err) - return - } - - playbookIDP := chi.URLParam(r, "playbookID") - - result, err := s.service.RemoveTicketPlaybook(r.Context(), idP, playbookIDP) - response(w, result, err) -} - -func (s *server) setTaskDataHandler(w http.ResponseWriter, r *http.Request) { - idP, err := parseURLInt64(r, "id") - if err != nil { - JSONError(w, err) - return - } - - playbookIDP := chi.URLParam(r, "playbookID") - - taskIDP := chi.URLParam(r, "taskID") - - body, err := io.ReadAll(r.Body) - if err != nil { - JSONError(w, err) - return - } - - var dataP map[string]interface{} - if err := parseBody(body, &dataP); err != nil { - JSONError(w, err) - return - } - - result, err := s.service.SetTaskData(r.Context(), idP, playbookIDP, taskIDP, dataP) - response(w, result, err) -} - -func (s *server) completeTaskHandler(w http.ResponseWriter, r *http.Request) { - idP, err := parseURLInt64(r, "id") - if err != nil { - JSONError(w, err) - return - } - - playbookIDP := chi.URLParam(r, "playbookID") - - taskIDP := chi.URLParam(r, "taskID") - - body, err := io.ReadAll(r.Body) - if err != nil { - JSONError(w, err) - return - } - - var dataP map[string]interface{} - if err := parseBody(body, &dataP); err != nil { - JSONError(w, err) - return - } - - result, err := s.service.CompleteTask(r.Context(), idP, playbookIDP, taskIDP, dataP) - response(w, result, err) -} - -func (s *server) setTaskOwnerHandler(w http.ResponseWriter, r *http.Request) { - idP, err := parseURLInt64(r, "id") - if err != nil { - JSONError(w, err) - return - } - - playbookIDP := chi.URLParam(r, "playbookID") - - taskIDP := chi.URLParam(r, "taskID") - - body, err := io.ReadAll(r.Body) - if err != nil { - JSONError(w, err) - return - } - - var ownerP string - if err := parseBody(body, &ownerP); err != nil { - JSONError(w, err) - return - } - - result, err := s.service.SetTaskOwner(r.Context(), idP, playbookIDP, taskIDP, ownerP) - response(w, result, err) -} - -func (s *server) runTaskHandler(w http.ResponseWriter, r *http.Request) { - idP, err := parseURLInt64(r, "id") - if err != nil { - JSONError(w, err) - return - } - - playbookIDP := chi.URLParam(r, "playbookID") - - taskIDP := chi.URLParam(r, "taskID") - - response(w, nil, s.service.RunTask(r.Context(), idP, playbookIDP, taskIDP)) -} - -func (s *server) setReferencesHandler(w http.ResponseWriter, r *http.Request) { - idP, err := parseURLInt64(r, "id") - if err != nil { - JSONError(w, err) - return - } - - body, err := io.ReadAll(r.Body) - if err != nil { - JSONError(w, err) - return - } - - jl := gojsonschema.NewBytesLoader(body) - validationResult, err := model.ReferenceArraySchema.Validate(jl) - if err != nil { - JSONError(w, err) - return - } - if !validationResult.Valid() { - w.WriteHeader(http.StatusUnprocessableEntity) - - var valdiationErrors []string - for _, valdiationError := range validationResult.Errors() { - valdiationErrors = append(valdiationErrors, valdiationError.String()) - } - - b, _ := json.Marshal(map[string]interface{}{"error": "wrong input", "errors": valdiationErrors}) - w.Write(b) - return - } - - var referencesP *model.ReferenceArray - if err := parseBody(body, &referencesP); err != nil { - JSONError(w, err) - return - } - - result, err := s.service.SetReferences(r.Context(), idP, referencesP) - response(w, result, err) -} - -func (s *server) setSchemaHandler(w http.ResponseWriter, r *http.Request) { - idP, err := parseURLInt64(r, "id") - if err != nil { - JSONError(w, err) - return - } - - body, err := io.ReadAll(r.Body) - if err != nil { - JSONError(w, err) - return - } - - var schemaP string - if err := parseBody(body, &schemaP); err != nil { - JSONError(w, err) - return - } - - result, err := s.service.SetSchema(r.Context(), idP, schemaP) - response(w, result, err) -} - -func (s *server) linkTicketHandler(w http.ResponseWriter, r *http.Request) { - idP, err := parseURLInt64(r, "id") - if err != nil { - JSONError(w, err) - return - } - - body, err := io.ReadAll(r.Body) - if err != nil { - JSONError(w, err) - return - } - - var linkedIDP int64 - if err := parseBody(body, &linkedIDP); err != nil { - JSONError(w, err) - return - } - - result, err := s.service.LinkTicket(r.Context(), idP, linkedIDP) - response(w, result, err) -} - -func (s *server) unlinkTicketHandler(w http.ResponseWriter, r *http.Request) { - idP, err := parseURLInt64(r, "id") - if err != nil { - JSONError(w, err) - return - } - - body, err := io.ReadAll(r.Body) - if err != nil { - JSONError(w, err) - return - } - - var linkedIDP int64 - if err := parseBody(body, &linkedIDP); err != nil { - JSONError(w, err) - return - } - - result, err := s.service.UnlinkTicket(r.Context(), idP, linkedIDP) - response(w, result, err) -} - -func (s *server) listTicketTypesHandler(w http.ResponseWriter, r *http.Request) { - result, err := s.service.ListTicketTypes(r.Context()) - response(w, result, err) -} - -func (s *server) createTicketTypeHandler(w http.ResponseWriter, r *http.Request) { - body, err := io.ReadAll(r.Body) - if err != nil { - JSONError(w, err) - return - } - - jl := gojsonschema.NewBytesLoader(body) - validationResult, err := model.TicketTypeFormSchema.Validate(jl) - if err != nil { - JSONError(w, err) - return - } - if !validationResult.Valid() { - w.WriteHeader(http.StatusUnprocessableEntity) - - var valdiationErrors []string - for _, valdiationError := range validationResult.Errors() { - valdiationErrors = append(valdiationErrors, valdiationError.String()) - } - - b, _ := json.Marshal(map[string]interface{}{"error": "wrong input", "errors": valdiationErrors}) - w.Write(b) - return - } - - var tickettypeP *model.TicketTypeForm - if err := parseBody(body, &tickettypeP); err != nil { - JSONError(w, err) - return - } - - result, err := s.service.CreateTicketType(r.Context(), tickettypeP) - response(w, result, err) -} - -func (s *server) getTicketTypeHandler(w http.ResponseWriter, r *http.Request) { - idP := chi.URLParam(r, "id") - - result, err := s.service.GetTicketType(r.Context(), idP) - response(w, result, err) -} - -func (s *server) updateTicketTypeHandler(w http.ResponseWriter, r *http.Request) { - idP := chi.URLParam(r, "id") - - body, err := io.ReadAll(r.Body) - if err != nil { - JSONError(w, err) - return - } - - jl := gojsonschema.NewBytesLoader(body) - validationResult, err := model.TicketTypeFormSchema.Validate(jl) - if err != nil { - JSONError(w, err) - return - } - if !validationResult.Valid() { - w.WriteHeader(http.StatusUnprocessableEntity) - - var valdiationErrors []string - for _, valdiationError := range validationResult.Errors() { - valdiationErrors = append(valdiationErrors, valdiationError.String()) - } - - b, _ := json.Marshal(map[string]interface{}{"error": "wrong input", "errors": valdiationErrors}) - w.Write(b) - return - } - - var tickettypeP *model.TicketTypeForm - if err := parseBody(body, &tickettypeP); err != nil { - JSONError(w, err) - return - } - - result, err := s.service.UpdateTicketType(r.Context(), idP, tickettypeP) - response(w, result, err) -} - -func (s *server) deleteTicketTypeHandler(w http.ResponseWriter, r *http.Request) { - idP := chi.URLParam(r, "id") - - response(w, nil, s.service.DeleteTicketType(r.Context(), idP)) -} - -func (s *server) listUserDataHandler(w http.ResponseWriter, r *http.Request) { - result, err := s.service.ListUserData(r.Context()) - response(w, result, err) -} - -func (s *server) getUserDataHandler(w http.ResponseWriter, r *http.Request) { - idP := chi.URLParam(r, "id") - - result, err := s.service.GetUserData(r.Context(), idP) - response(w, result, err) -} - -func (s *server) updateUserDataHandler(w http.ResponseWriter, r *http.Request) { - idP := chi.URLParam(r, "id") - - body, err := io.ReadAll(r.Body) - if err != nil { - JSONError(w, err) - return - } - - jl := gojsonschema.NewBytesLoader(body) - validationResult, err := model.UserDataSchema.Validate(jl) - if err != nil { - JSONError(w, err) - return - } - if !validationResult.Valid() { - w.WriteHeader(http.StatusUnprocessableEntity) - - var valdiationErrors []string - for _, valdiationError := range validationResult.Errors() { - valdiationErrors = append(valdiationErrors, valdiationError.String()) - } - - b, _ := json.Marshal(map[string]interface{}{"error": "wrong input", "errors": valdiationErrors}) - w.Write(b) - return - } - - var userdataP *model.UserData - if err := parseBody(body, &userdataP); err != nil { - JSONError(w, err) - return - } - - result, err := s.service.UpdateUserData(r.Context(), idP, userdataP) - response(w, result, err) -} - -func (s *server) listUsersHandler(w http.ResponseWriter, r *http.Request) { - result, err := s.service.ListUsers(r.Context()) - response(w, result, err) -} - -func (s *server) createUserHandler(w http.ResponseWriter, r *http.Request) { - body, err := io.ReadAll(r.Body) - if err != nil { - JSONError(w, err) - return - } - - jl := gojsonschema.NewBytesLoader(body) - validationResult, err := model.UserFormSchema.Validate(jl) - if err != nil { - JSONError(w, err) - return - } - if !validationResult.Valid() { - w.WriteHeader(http.StatusUnprocessableEntity) - - var valdiationErrors []string - for _, valdiationError := range validationResult.Errors() { - valdiationErrors = append(valdiationErrors, valdiationError.String()) - } - - b, _ := json.Marshal(map[string]interface{}{"error": "wrong input", "errors": valdiationErrors}) - w.Write(b) - return - } - - var userP *model.UserForm - if err := parseBody(body, &userP); err != nil { - JSONError(w, err) - return - } - - result, err := s.service.CreateUser(r.Context(), userP) - response(w, result, err) -} - -func (s *server) getUserHandler(w http.ResponseWriter, r *http.Request) { - idP := chi.URLParam(r, "id") - - result, err := s.service.GetUser(r.Context(), idP) - response(w, result, err) -} - -func (s *server) updateUserHandler(w http.ResponseWriter, r *http.Request) { - idP := chi.URLParam(r, "id") - - body, err := io.ReadAll(r.Body) - if err != nil { - JSONError(w, err) - return - } - - jl := gojsonschema.NewBytesLoader(body) - validationResult, err := model.UserFormSchema.Validate(jl) - if err != nil { - JSONError(w, err) - return - } - if !validationResult.Valid() { - w.WriteHeader(http.StatusUnprocessableEntity) - - var valdiationErrors []string - for _, valdiationError := range validationResult.Errors() { - valdiationErrors = append(valdiationErrors, valdiationError.String()) - } - - b, _ := json.Marshal(map[string]interface{}{"error": "wrong input", "errors": valdiationErrors}) - w.Write(b) - return - } - - var userP *model.UserForm - if err := parseBody(body, &userP); err != nil { - JSONError(w, err) - return - } - - result, err := s.service.UpdateUser(r.Context(), idP, userP) - response(w, result, err) -} - -func (s *server) deleteUserHandler(w http.ResponseWriter, r *http.Request) { - idP := chi.URLParam(r, "id") - - response(w, nil, s.service.DeleteUser(r.Context(), idP)) -} - func parseURLInt64(r *http.Request, s string) (int64, error) { i, err := strconv.ParseInt(chi.URLParam(r, s), 10, 64) if err != nil { @@ -1623,3 +156,37 @@ func response(w http.ResponseWriter, v interface{}, err error) { b, _ := json.Marshal(v) w.Write(b) } + +func validateSchema(body []byte, schema *gojsonschema.Schema, w http.ResponseWriter) bool { + jl := gojsonschema.NewBytesLoader(body) + validationResult, err := schema.Validate(jl) + if err != nil { + JSONError(w, err) + return true + } + if !validationResult.Valid() { + w.WriteHeader(http.StatusUnprocessableEntity) + + var validationErrors []string + for _, valdiationError := range validationResult.Errors() { + validationErrors = append(validationErrors, valdiationError.String()) + } + + b, _ := json.Marshal(map[string]interface{}{"error": "wrong input", "errors": validationErrors}) + w.Write(b) + return true + } + return false +} + +func NilMiddleware() func(next http.Handler) http.Handler { + return func(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + next.ServeHTTP(w, r) + }) + } +} + +func IgnoreRoles(_ []string) func(next http.Handler) http.Handler { + return NilMiddleware() +} diff --git a/generated/api/server.go b/generated/api/server.go new file mode 100755 index 0000000..38ec935 --- /dev/null +++ b/generated/api/server.go @@ -0,0 +1,1129 @@ +package api + +import ( + "context" + "io" + "net/http" + + "github.com/go-chi/chi" + + "github.com/SecurityBrewery/catalyst/generated/model" +) + +type Service interface { + ListAutomations(context.Context) ([]*model.AutomationResponse, error) + CreateAutomation(context.Context, *model.AutomationForm) (*model.AutomationResponse, error) + GetAutomation(context.Context, string) (*model.AutomationResponse, error) + UpdateAutomation(context.Context, string, *model.AutomationForm) (*model.AutomationResponse, error) + DeleteAutomation(context.Context, string) error + CurrentUser(context.Context) (*model.UserResponse, error) + CurrentUserData(context.Context) (*model.UserDataResponse, error) + UpdateCurrentUserData(context.Context, *model.UserData) (*model.UserDataResponse, error) + ListJobs(context.Context) ([]*model.JobResponse, error) + RunJob(context.Context, *model.JobForm) (*model.JobResponse, error) + GetJob(context.Context, string) (*model.JobResponse, error) + UpdateJob(context.Context, string, *model.JobUpdate) (*model.JobResponse, error) + GetLogs(context.Context, string) ([]*model.LogEntry, error) + ListPlaybooks(context.Context) ([]*model.PlaybookTemplateResponse, error) + CreatePlaybook(context.Context, *model.PlaybookTemplateForm) (*model.PlaybookTemplateResponse, error) + 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) + GetStatistics(context.Context) (*model.Statistics, error) + ListTasks(context.Context) ([]*model.TaskWithContext, error) + ListTemplates(context.Context) ([]*model.TicketTemplateResponse, error) + CreateTemplate(context.Context, *model.TicketTemplateForm) (*model.TicketTemplateResponse, error) + GetTemplate(context.Context, string) (*model.TicketTemplateResponse, error) + UpdateTemplate(context.Context, string, *model.TicketTemplateForm) (*model.TicketTemplateResponse, error) + DeleteTemplate(context.Context, string) error + ListTickets(context.Context, *string, *int, *int, []string, []bool, *string) (*model.TicketList, error) + CreateTicket(context.Context, *model.TicketForm) (*model.TicketResponse, error) + CreateTicketBatch(context.Context, *model.TicketFormArray) error + GetTicket(context.Context, int64) (*model.TicketWithTickets, error) + UpdateTicket(context.Context, int64, *model.Ticket) (*model.TicketWithTickets, error) + DeleteTicket(context.Context, int64) error + AddArtifact(context.Context, int64, *model.Artifact) (*model.TicketWithTickets, error) + GetArtifact(context.Context, int64, string) (*model.Artifact, error) + SetArtifact(context.Context, int64, string, *model.Artifact) (*model.TicketWithTickets, error) + RemoveArtifact(context.Context, int64, string) (*model.TicketWithTickets, error) + EnrichArtifact(context.Context, int64, string, *model.EnrichmentForm) (*model.TicketWithTickets, error) + RunArtifact(context.Context, int64, string, string) error + AddComment(context.Context, int64, *model.CommentForm) (*model.TicketWithTickets, error) + RemoveComment(context.Context, int64, int) (*model.TicketWithTickets, error) + AddTicketPlaybook(context.Context, int64, *model.PlaybookTemplateForm) (*model.TicketWithTickets, error) + RemoveTicketPlaybook(context.Context, int64, string) (*model.TicketWithTickets, error) + SetTaskData(context.Context, int64, string, string, map[string]interface{}) (*model.TicketWithTickets, error) + CompleteTask(context.Context, int64, string, string, map[string]interface{}) (*model.TicketWithTickets, error) + SetTaskOwner(context.Context, int64, string, string, string) (*model.TicketWithTickets, error) + RunTask(context.Context, int64, string, string) error + SetReferences(context.Context, int64, *model.ReferenceArray) (*model.TicketWithTickets, error) + SetSchema(context.Context, int64, string) (*model.TicketWithTickets, error) + LinkTicket(context.Context, int64, int64) (*model.TicketWithTickets, error) + UnlinkTicket(context.Context, int64, int64) (*model.TicketWithTickets, error) + ListTicketTypes(context.Context) ([]*model.TicketTypeResponse, error) + CreateTicketType(context.Context, *model.TicketTypeForm) (*model.TicketTypeResponse, error) + GetTicketType(context.Context, string) (*model.TicketTypeResponse, error) + UpdateTicketType(context.Context, string, *model.TicketTypeForm) (*model.TicketTypeResponse, error) + DeleteTicketType(context.Context, string) error + ListUserData(context.Context) ([]*model.UserDataResponse, error) + GetUserData(context.Context, string) (*model.UserDataResponse, error) + UpdateUserData(context.Context, string, *model.UserData) (*model.UserDataResponse, error) + ListUsers(context.Context) ([]*model.UserResponse, error) + CreateUser(context.Context, *model.UserForm) (*model.NewUserResponse, error) + GetUser(context.Context, string) (*model.UserResponse, error) + UpdateUser(context.Context, string, *model.UserForm) (*model.UserResponse, error) + DeleteUser(context.Context, string) error +} + +func NewServer(service Service, roleAuth func([]string) func(http.Handler) http.Handler, middlewares ...func(http.Handler) http.Handler) chi.Router { + r := chi.NewRouter() + r.Use(middlewares...) + + s := &server{service} + + r.With(roleAuth([]string{"automation:read"})).Get("/automations", s.listAutomationsHandler) + r.With(roleAuth([]string{"automation:write"})).Post("/automations", s.createAutomationHandler) + r.With(roleAuth([]string{"automation:read"})).Get("/automations/{id}", s.getAutomationHandler) + r.With(roleAuth([]string{"automation:write"})).Put("/automations/{id}", s.updateAutomationHandler) + r.With(roleAuth([]string{"automation:write"})).Delete("/automations/{id}", s.deleteAutomationHandler) + r.With(roleAuth([]string{"currentuser:read"})).Get("/currentuser", s.currentUserHandler) + r.With(roleAuth([]string{"currentuserdata:read"})).Get("/currentuserdata", s.currentUserDataHandler) + r.With(roleAuth([]string{"currentuserdata:write"})).Put("/currentuserdata", s.updateCurrentUserDataHandler) + r.With(roleAuth([]string{"job:read"})).Get("/jobs", s.listJobsHandler) + r.With(roleAuth([]string{"job:write"})).Post("/jobs", s.runJobHandler) + r.With(roleAuth([]string{"job:read"})).Get("/jobs/{id}", s.getJobHandler) + r.With(roleAuth([]string{"job:write"})).Put("/jobs/{id}", s.updateJobHandler) + r.With(roleAuth([]string{"log:read"})).Get("/logs/{reference}", s.getLogsHandler) + r.With(roleAuth([]string{"playbook:read"})).Get("/playbooks", s.listPlaybooksHandler) + r.With(roleAuth([]string{"playbook:write"})).Post("/playbooks", s.createPlaybookHandler) + r.With(roleAuth([]string{"playbook:read"})).Get("/playbooks/{id}", s.getPlaybookHandler) + 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{"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) + r.With(roleAuth([]string{"template:write"})).Post("/templates", s.createTemplateHandler) + r.With(roleAuth([]string{"template:read"})).Get("/templates/{id}", s.getTemplateHandler) + r.With(roleAuth([]string{"template:write"})).Put("/templates/{id}", s.updateTemplateHandler) + r.With(roleAuth([]string{"template:write"})).Delete("/templates/{id}", s.deleteTemplateHandler) + r.With(roleAuth([]string{"ticket:read"})).Get("/tickets", s.listTicketsHandler) + r.With(roleAuth([]string{"ticket:write"})).Post("/tickets", s.createTicketHandler) + r.With(roleAuth([]string{"ticket:write"})).Post("/tickets/batch", s.createTicketBatchHandler) + r.With(roleAuth([]string{"ticket:read"})).Get("/tickets/{id}", s.getTicketHandler) + r.With(roleAuth([]string{"ticket:write"})).Put("/tickets/{id}", s.updateTicketHandler) + r.With(roleAuth([]string{"ticket:delete"})).Delete("/tickets/{id}", s.deleteTicketHandler) + r.With(roleAuth([]string{"ticket:write"})).Post("/tickets/{id}/artifacts", s.addArtifactHandler) + r.With(roleAuth([]string{"ticket:write"})).Get("/tickets/{id}/artifacts/{name}", s.getArtifactHandler) + r.With(roleAuth([]string{"ticket:write"})).Put("/tickets/{id}/artifacts/{name}", s.setArtifactHandler) + r.With(roleAuth([]string{"ticket:write"})).Delete("/tickets/{id}/artifacts/{name}", s.removeArtifactHandler) + r.With(roleAuth([]string{"ticket:write"})).Post("/tickets/{id}/artifacts/{name}/enrich", s.enrichArtifactHandler) + r.With(roleAuth([]string{"ticket:write"})).Post("/tickets/{id}/artifacts/{name}/run/{automation}", s.runArtifactHandler) + r.With(roleAuth([]string{"ticket:write"})).Post("/tickets/{id}/comments", s.addCommentHandler) + r.With(roleAuth([]string{"ticket:write"})).Delete("/tickets/{id}/comments/{commentID}", s.removeCommentHandler) + r.With(roleAuth([]string{"ticket:write"})).Post("/tickets/{id}/playbooks", s.addTicketPlaybookHandler) + r.With(roleAuth([]string{"ticket:write"})).Delete("/tickets/{id}/playbooks/{playbookID}", s.removeTicketPlaybookHandler) + r.With(roleAuth([]string{"ticket:write"})).Put("/tickets/{id}/playbooks/{playbookID}/task/{taskID}", s.setTaskDataHandler) + r.With(roleAuth([]string{"ticket:write"})).Put("/tickets/{id}/playbooks/{playbookID}/task/{taskID}/complete", s.completeTaskHandler) + r.With(roleAuth([]string{"ticket:write"})).Put("/tickets/{id}/playbooks/{playbookID}/task/{taskID}/owner", s.setTaskOwnerHandler) + r.With(roleAuth([]string{"ticket:write"})).Post("/tickets/{id}/playbooks/{playbookID}/task/{taskID}/run", s.runTaskHandler) + r.With(roleAuth([]string{"ticket:write"})).Put("/tickets/{id}/references", s.setReferencesHandler) + r.With(roleAuth([]string{"ticket:write"})).Put("/tickets/{id}/schema", s.setSchemaHandler) + r.With(roleAuth([]string{"ticket:write"})).Patch("/tickets/{id}/tickets", s.linkTicketHandler) + r.With(roleAuth([]string{"ticket:write"})).Delete("/tickets/{id}/tickets", s.unlinkTicketHandler) + r.With(roleAuth([]string{"tickettype:read"})).Get("/tickettypes", s.listTicketTypesHandler) + r.With(roleAuth([]string{"tickettype:write"})).Post("/tickettypes", s.createTicketTypeHandler) + r.With(roleAuth([]string{"tickettype:read"})).Get("/tickettypes/{id}", s.getTicketTypeHandler) + r.With(roleAuth([]string{"tickettype:write"})).Put("/tickettypes/{id}", s.updateTicketTypeHandler) + r.With(roleAuth([]string{"tickettype:write"})).Delete("/tickettypes/{id}", s.deleteTicketTypeHandler) + r.With(roleAuth([]string{"userdata:read"})).Get("/userdata", s.listUserDataHandler) + r.With(roleAuth([]string{"userdata:read"})).Get("/userdata/{id}", s.getUserDataHandler) + r.With(roleAuth([]string{"userdata:write"})).Put("/userdata/{id}", s.updateUserDataHandler) + r.With(roleAuth([]string{"user:read"})).Get("/users", s.listUsersHandler) + r.With(roleAuth([]string{"user:write"})).Post("/users", s.createUserHandler) + r.With(roleAuth([]string{"user:read"})).Get("/users/{id}", s.getUserHandler) + r.With(roleAuth([]string{"user:write"})).Put("/users/{id}", s.updateUserHandler) + r.With(roleAuth([]string{"user:write"})).Delete("/users/{id}", s.deleteUserHandler) + return r +} + +type server struct { + service Service +} + +func (s *server) listAutomationsHandler(w http.ResponseWriter, r *http.Request) { + result, err := s.service.ListAutomations(r.Context()) + response(w, result, err) +} + +func (s *server) createAutomationHandler(w http.ResponseWriter, r *http.Request) { + body, err := io.ReadAll(r.Body) + if err != nil { + JSONError(w, err) + return + } + + if validateSchema(body, model.AutomationFormSchema, w) { + return + } + + var automationP *model.AutomationForm + if err := parseBody(body, &automationP); err != nil { + JSONError(w, err) + return + } + + result, err := s.service.CreateAutomation(r.Context(), automationP) + response(w, result, err) +} + +func (s *server) getAutomationHandler(w http.ResponseWriter, r *http.Request) { + idP := chi.URLParam(r, "id") + + result, err := s.service.GetAutomation(r.Context(), idP) + response(w, result, err) +} + +func (s *server) updateAutomationHandler(w http.ResponseWriter, r *http.Request) { + idP := chi.URLParam(r, "id") + + body, err := io.ReadAll(r.Body) + if err != nil { + JSONError(w, err) + return + } + + if validateSchema(body, model.AutomationFormSchema, w) { + return + } + + var automationP *model.AutomationForm + if err := parseBody(body, &automationP); err != nil { + JSONError(w, err) + return + } + + result, err := s.service.UpdateAutomation(r.Context(), idP, automationP) + response(w, result, err) +} + +func (s *server) deleteAutomationHandler(w http.ResponseWriter, r *http.Request) { + idP := chi.URLParam(r, "id") + + response(w, nil, s.service.DeleteAutomation(r.Context(), idP)) +} + +func (s *server) currentUserHandler(w http.ResponseWriter, r *http.Request) { + result, err := s.service.CurrentUser(r.Context()) + response(w, result, err) +} + +func (s *server) currentUserDataHandler(w http.ResponseWriter, r *http.Request) { + result, err := s.service.CurrentUserData(r.Context()) + response(w, result, err) +} + +func (s *server) updateCurrentUserDataHandler(w http.ResponseWriter, r *http.Request) { + body, err := io.ReadAll(r.Body) + if err != nil { + JSONError(w, err) + return + } + + if validateSchema(body, model.UserDataSchema, w) { + return + } + + var userdataP *model.UserData + if err := parseBody(body, &userdataP); err != nil { + JSONError(w, err) + return + } + + result, err := s.service.UpdateCurrentUserData(r.Context(), userdataP) + response(w, result, err) +} + +func (s *server) listJobsHandler(w http.ResponseWriter, r *http.Request) { + result, err := s.service.ListJobs(r.Context()) + response(w, result, err) +} + +func (s *server) runJobHandler(w http.ResponseWriter, r *http.Request) { + body, err := io.ReadAll(r.Body) + if err != nil { + JSONError(w, err) + return + } + + if validateSchema(body, model.JobFormSchema, w) { + return + } + + var jobP *model.JobForm + if err := parseBody(body, &jobP); err != nil { + JSONError(w, err) + return + } + + result, err := s.service.RunJob(r.Context(), jobP) + response(w, result, err) +} + +func (s *server) getJobHandler(w http.ResponseWriter, r *http.Request) { + idP := chi.URLParam(r, "id") + + result, err := s.service.GetJob(r.Context(), idP) + response(w, result, err) +} + +func (s *server) updateJobHandler(w http.ResponseWriter, r *http.Request) { + idP := chi.URLParam(r, "id") + + body, err := io.ReadAll(r.Body) + if err != nil { + JSONError(w, err) + return + } + + if validateSchema(body, model.JobUpdateSchema, w) { + return + } + + var jobP *model.JobUpdate + if err := parseBody(body, &jobP); err != nil { + JSONError(w, err) + return + } + + result, err := s.service.UpdateJob(r.Context(), idP, jobP) + response(w, result, err) +} + +func (s *server) getLogsHandler(w http.ResponseWriter, r *http.Request) { + referenceP := chi.URLParam(r, "reference") + + result, err := s.service.GetLogs(r.Context(), referenceP) + response(w, result, err) +} + +func (s *server) listPlaybooksHandler(w http.ResponseWriter, r *http.Request) { + result, err := s.service.ListPlaybooks(r.Context()) + response(w, result, err) +} + +func (s *server) createPlaybookHandler(w http.ResponseWriter, r *http.Request) { + body, err := io.ReadAll(r.Body) + if err != nil { + JSONError(w, err) + return + } + + if validateSchema(body, model.PlaybookTemplateFormSchema, w) { + return + } + + var playbookP *model.PlaybookTemplateForm + if err := parseBody(body, &playbookP); err != nil { + JSONError(w, err) + return + } + + result, err := s.service.CreatePlaybook(r.Context(), playbookP) + response(w, result, err) +} + +func (s *server) getPlaybookHandler(w http.ResponseWriter, r *http.Request) { + idP := chi.URLParam(r, "id") + + result, err := s.service.GetPlaybook(r.Context(), idP) + response(w, result, err) +} + +func (s *server) updatePlaybookHandler(w http.ResponseWriter, r *http.Request) { + idP := chi.URLParam(r, "id") + + body, err := io.ReadAll(r.Body) + if err != nil { + JSONError(w, err) + return + } + + if validateSchema(body, model.PlaybookTemplateFormSchema, w) { + return + } + + var playbookP *model.PlaybookTemplateForm + if err := parseBody(body, &playbookP); err != nil { + JSONError(w, err) + return + } + + result, err := s.service.UpdatePlaybook(r.Context(), idP, playbookP) + response(w, result, err) +} + +func (s *server) deletePlaybookHandler(w http.ResponseWriter, r *http.Request) { + idP := chi.URLParam(r, "id") + + response(w, nil, s.service.DeletePlaybook(r.Context(), idP)) +} + +func (s *server) getSettingsHandler(w http.ResponseWriter, r *http.Request) { + result, err := s.service.GetSettings(r.Context()) + 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) +} + +func (s *server) listTasksHandler(w http.ResponseWriter, r *http.Request) { + result, err := s.service.ListTasks(r.Context()) + response(w, result, err) +} + +func (s *server) listTemplatesHandler(w http.ResponseWriter, r *http.Request) { + result, err := s.service.ListTemplates(r.Context()) + response(w, result, err) +} + +func (s *server) createTemplateHandler(w http.ResponseWriter, r *http.Request) { + body, err := io.ReadAll(r.Body) + if err != nil { + JSONError(w, err) + return + } + + if validateSchema(body, model.TicketTemplateFormSchema, w) { + return + } + + var templateP *model.TicketTemplateForm + if err := parseBody(body, &templateP); err != nil { + JSONError(w, err) + return + } + + result, err := s.service.CreateTemplate(r.Context(), templateP) + response(w, result, err) +} + +func (s *server) getTemplateHandler(w http.ResponseWriter, r *http.Request) { + idP := chi.URLParam(r, "id") + + result, err := s.service.GetTemplate(r.Context(), idP) + response(w, result, err) +} + +func (s *server) updateTemplateHandler(w http.ResponseWriter, r *http.Request) { + idP := chi.URLParam(r, "id") + + body, err := io.ReadAll(r.Body) + if err != nil { + JSONError(w, err) + return + } + + if validateSchema(body, model.TicketTemplateFormSchema, w) { + return + } + + var templateP *model.TicketTemplateForm + if err := parseBody(body, &templateP); err != nil { + JSONError(w, err) + return + } + + result, err := s.service.UpdateTemplate(r.Context(), idP, templateP) + response(w, result, err) +} + +func (s *server) deleteTemplateHandler(w http.ResponseWriter, r *http.Request) { + idP := chi.URLParam(r, "id") + + response(w, nil, s.service.DeleteTemplate(r.Context(), idP)) +} + +func (s *server) listTicketsHandler(w http.ResponseWriter, r *http.Request) { + typeP := r.URL.Query().Get("type") + + offsetP, err := parseQueryOptionalInt(r, "offset") + if err != nil { + JSONError(w, err) + return + } + + countP, err := parseQueryOptionalInt(r, "count") + if err != nil { + JSONError(w, err) + return + } + + sortP, err := parseQueryOptionalStringArray(r, "sort") + if err != nil { + JSONError(w, err) + return + } + + descP, err := parseQueryOptionalBoolArray(r, "desc") + if err != nil { + JSONError(w, err) + return + } + + queryP := r.URL.Query().Get("query") + + result, err := s.service.ListTickets(r.Context(), &typeP, offsetP, countP, sortP, descP, &queryP) + response(w, result, err) +} + +func (s *server) createTicketHandler(w http.ResponseWriter, r *http.Request) { + body, err := io.ReadAll(r.Body) + if err != nil { + JSONError(w, err) + return + } + + if validateSchema(body, model.TicketFormSchema, w) { + return + } + + var ticketP *model.TicketForm + if err := parseBody(body, &ticketP); err != nil { + JSONError(w, err) + return + } + + result, err := s.service.CreateTicket(r.Context(), ticketP) + response(w, result, err) +} + +func (s *server) createTicketBatchHandler(w http.ResponseWriter, r *http.Request) { + body, err := io.ReadAll(r.Body) + if err != nil { + JSONError(w, err) + return + } + + if validateSchema(body, model.TicketFormArraySchema, w) { + return + } + + var ticketP *model.TicketFormArray + if err := parseBody(body, &ticketP); err != nil { + JSONError(w, err) + return + } + + response(w, nil, s.service.CreateTicketBatch(r.Context(), ticketP)) +} + +func (s *server) getTicketHandler(w http.ResponseWriter, r *http.Request) { + idP, err := parseURLInt64(r, "id") + if err != nil { + JSONError(w, err) + return + } + + result, err := s.service.GetTicket(r.Context(), idP) + response(w, result, err) +} + +func (s *server) updateTicketHandler(w http.ResponseWriter, r *http.Request) { + idP, err := parseURLInt64(r, "id") + if err != nil { + JSONError(w, err) + return + } + + body, err := io.ReadAll(r.Body) + if err != nil { + JSONError(w, err) + return + } + + if validateSchema(body, model.TicketSchema, w) { + return + } + + var ticketP *model.Ticket + if err := parseBody(body, &ticketP); err != nil { + JSONError(w, err) + return + } + + result, err := s.service.UpdateTicket(r.Context(), idP, ticketP) + response(w, result, err) +} + +func (s *server) deleteTicketHandler(w http.ResponseWriter, r *http.Request) { + idP, err := parseURLInt64(r, "id") + if err != nil { + JSONError(w, err) + return + } + + response(w, nil, s.service.DeleteTicket(r.Context(), idP)) +} + +func (s *server) addArtifactHandler(w http.ResponseWriter, r *http.Request) { + idP, err := parseURLInt64(r, "id") + if err != nil { + JSONError(w, err) + return + } + + body, err := io.ReadAll(r.Body) + if err != nil { + JSONError(w, err) + return + } + + if validateSchema(body, model.ArtifactSchema, w) { + return + } + + var artifactP *model.Artifact + if err := parseBody(body, &artifactP); err != nil { + JSONError(w, err) + return + } + + result, err := s.service.AddArtifact(r.Context(), idP, artifactP) + response(w, result, err) +} + +func (s *server) getArtifactHandler(w http.ResponseWriter, r *http.Request) { + idP, err := parseURLInt64(r, "id") + if err != nil { + JSONError(w, err) + return + } + + nameP := chi.URLParam(r, "name") + + result, err := s.service.GetArtifact(r.Context(), idP, nameP) + response(w, result, err) +} + +func (s *server) setArtifactHandler(w http.ResponseWriter, r *http.Request) { + idP, err := parseURLInt64(r, "id") + if err != nil { + JSONError(w, err) + return + } + + nameP := chi.URLParam(r, "name") + + body, err := io.ReadAll(r.Body) + if err != nil { + JSONError(w, err) + return + } + + if validateSchema(body, model.ArtifactSchema, w) { + return + } + + var artifactP *model.Artifact + if err := parseBody(body, &artifactP); err != nil { + JSONError(w, err) + return + } + + result, err := s.service.SetArtifact(r.Context(), idP, nameP, artifactP) + response(w, result, err) +} + +func (s *server) removeArtifactHandler(w http.ResponseWriter, r *http.Request) { + idP, err := parseURLInt64(r, "id") + if err != nil { + JSONError(w, err) + return + } + + nameP := chi.URLParam(r, "name") + + result, err := s.service.RemoveArtifact(r.Context(), idP, nameP) + response(w, result, err) +} + +func (s *server) enrichArtifactHandler(w http.ResponseWriter, r *http.Request) { + idP, err := parseURLInt64(r, "id") + if err != nil { + JSONError(w, err) + return + } + + nameP := chi.URLParam(r, "name") + + body, err := io.ReadAll(r.Body) + if err != nil { + JSONError(w, err) + return + } + + if validateSchema(body, model.EnrichmentFormSchema, w) { + return + } + + var dataP *model.EnrichmentForm + if err := parseBody(body, &dataP); err != nil { + JSONError(w, err) + return + } + + result, err := s.service.EnrichArtifact(r.Context(), idP, nameP, dataP) + response(w, result, err) +} + +func (s *server) runArtifactHandler(w http.ResponseWriter, r *http.Request) { + idP, err := parseURLInt64(r, "id") + if err != nil { + JSONError(w, err) + return + } + + nameP := chi.URLParam(r, "name") + + automationP := chi.URLParam(r, "automation") + + response(w, nil, s.service.RunArtifact(r.Context(), idP, nameP, automationP)) +} + +func (s *server) addCommentHandler(w http.ResponseWriter, r *http.Request) { + idP, err := parseURLInt64(r, "id") + if err != nil { + JSONError(w, err) + return + } + + body, err := io.ReadAll(r.Body) + if err != nil { + JSONError(w, err) + return + } + + if validateSchema(body, model.CommentFormSchema, w) { + return + } + + var commentP *model.CommentForm + if err := parseBody(body, &commentP); err != nil { + JSONError(w, err) + return + } + + result, err := s.service.AddComment(r.Context(), idP, commentP) + response(w, result, err) +} + +func (s *server) removeCommentHandler(w http.ResponseWriter, r *http.Request) { + idP, err := parseURLInt64(r, "id") + if err != nil { + JSONError(w, err) + return + } + + commentIDP, err := parseURLInt(r, "commentID") + if err != nil { + JSONError(w, err) + return + } + + result, err := s.service.RemoveComment(r.Context(), idP, commentIDP) + response(w, result, err) +} + +func (s *server) addTicketPlaybookHandler(w http.ResponseWriter, r *http.Request) { + idP, err := parseURLInt64(r, "id") + if err != nil { + JSONError(w, err) + return + } + + body, err := io.ReadAll(r.Body) + if err != nil { + JSONError(w, err) + return + } + + if validateSchema(body, model.PlaybookTemplateFormSchema, w) { + return + } + + var playbookP *model.PlaybookTemplateForm + if err := parseBody(body, &playbookP); err != nil { + JSONError(w, err) + return + } + + result, err := s.service.AddTicketPlaybook(r.Context(), idP, playbookP) + response(w, result, err) +} + +func (s *server) removeTicketPlaybookHandler(w http.ResponseWriter, r *http.Request) { + idP, err := parseURLInt64(r, "id") + if err != nil { + JSONError(w, err) + return + } + + playbookIDP := chi.URLParam(r, "playbookID") + + result, err := s.service.RemoveTicketPlaybook(r.Context(), idP, playbookIDP) + response(w, result, err) +} + +func (s *server) setTaskDataHandler(w http.ResponseWriter, r *http.Request) { + idP, err := parseURLInt64(r, "id") + if err != nil { + JSONError(w, err) + return + } + + playbookIDP := chi.URLParam(r, "playbookID") + + taskIDP := chi.URLParam(r, "taskID") + + body, err := io.ReadAll(r.Body) + if err != nil { + JSONError(w, err) + return + } + + var dataP map[string]interface{} + if err := parseBody(body, &dataP); err != nil { + JSONError(w, err) + return + } + + result, err := s.service.SetTaskData(r.Context(), idP, playbookIDP, taskIDP, dataP) + response(w, result, err) +} + +func (s *server) completeTaskHandler(w http.ResponseWriter, r *http.Request) { + idP, err := parseURLInt64(r, "id") + if err != nil { + JSONError(w, err) + return + } + + playbookIDP := chi.URLParam(r, "playbookID") + + taskIDP := chi.URLParam(r, "taskID") + + body, err := io.ReadAll(r.Body) + if err != nil { + JSONError(w, err) + return + } + + var dataP map[string]interface{} + if err := parseBody(body, &dataP); err != nil { + JSONError(w, err) + return + } + + result, err := s.service.CompleteTask(r.Context(), idP, playbookIDP, taskIDP, dataP) + response(w, result, err) +} + +func (s *server) setTaskOwnerHandler(w http.ResponseWriter, r *http.Request) { + idP, err := parseURLInt64(r, "id") + if err != nil { + JSONError(w, err) + return + } + + playbookIDP := chi.URLParam(r, "playbookID") + + taskIDP := chi.URLParam(r, "taskID") + + body, err := io.ReadAll(r.Body) + if err != nil { + JSONError(w, err) + return + } + + var ownerP string + if err := parseBody(body, &ownerP); err != nil { + JSONError(w, err) + return + } + + result, err := s.service.SetTaskOwner(r.Context(), idP, playbookIDP, taskIDP, ownerP) + response(w, result, err) +} + +func (s *server) runTaskHandler(w http.ResponseWriter, r *http.Request) { + idP, err := parseURLInt64(r, "id") + if err != nil { + JSONError(w, err) + return + } + + playbookIDP := chi.URLParam(r, "playbookID") + + taskIDP := chi.URLParam(r, "taskID") + + response(w, nil, s.service.RunTask(r.Context(), idP, playbookIDP, taskIDP)) +} + +func (s *server) setReferencesHandler(w http.ResponseWriter, r *http.Request) { + idP, err := parseURLInt64(r, "id") + if err != nil { + JSONError(w, err) + return + } + + body, err := io.ReadAll(r.Body) + if err != nil { + JSONError(w, err) + return + } + + if validateSchema(body, model.ReferenceArraySchema, w) { + return + } + + var referencesP *model.ReferenceArray + if err := parseBody(body, &referencesP); err != nil { + JSONError(w, err) + return + } + + result, err := s.service.SetReferences(r.Context(), idP, referencesP) + response(w, result, err) +} + +func (s *server) setSchemaHandler(w http.ResponseWriter, r *http.Request) { + idP, err := parseURLInt64(r, "id") + if err != nil { + JSONError(w, err) + return + } + + body, err := io.ReadAll(r.Body) + if err != nil { + JSONError(w, err) + return + } + + var schemaP string + if err := parseBody(body, &schemaP); err != nil { + JSONError(w, err) + return + } + + result, err := s.service.SetSchema(r.Context(), idP, schemaP) + response(w, result, err) +} + +func (s *server) linkTicketHandler(w http.ResponseWriter, r *http.Request) { + idP, err := parseURLInt64(r, "id") + if err != nil { + JSONError(w, err) + return + } + + body, err := io.ReadAll(r.Body) + if err != nil { + JSONError(w, err) + return + } + + var linkedIDP int64 + if err := parseBody(body, &linkedIDP); err != nil { + JSONError(w, err) + return + } + + result, err := s.service.LinkTicket(r.Context(), idP, linkedIDP) + response(w, result, err) +} + +func (s *server) unlinkTicketHandler(w http.ResponseWriter, r *http.Request) { + idP, err := parseURLInt64(r, "id") + if err != nil { + JSONError(w, err) + return + } + + body, err := io.ReadAll(r.Body) + if err != nil { + JSONError(w, err) + return + } + + var linkedIDP int64 + if err := parseBody(body, &linkedIDP); err != nil { + JSONError(w, err) + return + } + + result, err := s.service.UnlinkTicket(r.Context(), idP, linkedIDP) + response(w, result, err) +} + +func (s *server) listTicketTypesHandler(w http.ResponseWriter, r *http.Request) { + result, err := s.service.ListTicketTypes(r.Context()) + response(w, result, err) +} + +func (s *server) createTicketTypeHandler(w http.ResponseWriter, r *http.Request) { + body, err := io.ReadAll(r.Body) + if err != nil { + JSONError(w, err) + return + } + + if validateSchema(body, model.TicketTypeFormSchema, w) { + return + } + + var tickettypeP *model.TicketTypeForm + if err := parseBody(body, &tickettypeP); err != nil { + JSONError(w, err) + return + } + + result, err := s.service.CreateTicketType(r.Context(), tickettypeP) + response(w, result, err) +} + +func (s *server) getTicketTypeHandler(w http.ResponseWriter, r *http.Request) { + idP := chi.URLParam(r, "id") + + result, err := s.service.GetTicketType(r.Context(), idP) + response(w, result, err) +} + +func (s *server) updateTicketTypeHandler(w http.ResponseWriter, r *http.Request) { + idP := chi.URLParam(r, "id") + + body, err := io.ReadAll(r.Body) + if err != nil { + JSONError(w, err) + return + } + + if validateSchema(body, model.TicketTypeFormSchema, w) { + return + } + + var tickettypeP *model.TicketTypeForm + if err := parseBody(body, &tickettypeP); err != nil { + JSONError(w, err) + return + } + + result, err := s.service.UpdateTicketType(r.Context(), idP, tickettypeP) + response(w, result, err) +} + +func (s *server) deleteTicketTypeHandler(w http.ResponseWriter, r *http.Request) { + idP := chi.URLParam(r, "id") + + response(w, nil, s.service.DeleteTicketType(r.Context(), idP)) +} + +func (s *server) listUserDataHandler(w http.ResponseWriter, r *http.Request) { + result, err := s.service.ListUserData(r.Context()) + response(w, result, err) +} + +func (s *server) getUserDataHandler(w http.ResponseWriter, r *http.Request) { + idP := chi.URLParam(r, "id") + + result, err := s.service.GetUserData(r.Context(), idP) + response(w, result, err) +} + +func (s *server) updateUserDataHandler(w http.ResponseWriter, r *http.Request) { + idP := chi.URLParam(r, "id") + + body, err := io.ReadAll(r.Body) + if err != nil { + JSONError(w, err) + return + } + + if validateSchema(body, model.UserDataSchema, w) { + return + } + + var userdataP *model.UserData + if err := parseBody(body, &userdataP); err != nil { + JSONError(w, err) + return + } + + result, err := s.service.UpdateUserData(r.Context(), idP, userdataP) + response(w, result, err) +} + +func (s *server) listUsersHandler(w http.ResponseWriter, r *http.Request) { + result, err := s.service.ListUsers(r.Context()) + response(w, result, err) +} + +func (s *server) createUserHandler(w http.ResponseWriter, r *http.Request) { + body, err := io.ReadAll(r.Body) + if err != nil { + JSONError(w, err) + return + } + + if validateSchema(body, model.UserFormSchema, w) { + return + } + + var userP *model.UserForm + if err := parseBody(body, &userP); err != nil { + JSONError(w, err) + return + } + + result, err := s.service.CreateUser(r.Context(), userP) + response(w, result, err) +} + +func (s *server) getUserHandler(w http.ResponseWriter, r *http.Request) { + idP := chi.URLParam(r, "id") + + result, err := s.service.GetUser(r.Context(), idP) + response(w, result, err) +} + +func (s *server) updateUserHandler(w http.ResponseWriter, r *http.Request) { + idP := chi.URLParam(r, "id") + + body, err := io.ReadAll(r.Body) + if err != nil { + JSONError(w, err) + return + } + + if validateSchema(body, model.UserFormSchema, w) { + return + } + + var userP *model.UserForm + if err := parseBody(body, &userP); err != nil { + JSONError(w, err) + return + } + + result, err := s.service.UpdateUser(r.Context(), idP, userP) + response(w, result, err) +} + +func (s *server) deleteUserHandler(w http.ResponseWriter, r *http.Request) { + idP := chi.URLParam(r, "id") + + response(w, nil, s.service.DeleteUser(r.Context(), idP)) +} diff --git a/generated/api/static.go b/generated/api/static.go new file mode 100755 index 0000000..9e035c6 --- /dev/null +++ b/generated/api/static.go @@ -0,0 +1,25 @@ +package api + +import ( + "io/fs" + "net/http" + "net/http/httputil" + "net/url" +) + +func Static(fsys fs.FS) func(w http.ResponseWriter, r *http.Request) { + return func(w http.ResponseWriter, r *http.Request) { + http.FileServer(http.FS(fsys)).ServeHTTP(w, r) + } +} + +func Proxy(dest string) func(w http.ResponseWriter, r *http.Request) { + return func(w http.ResponseWriter, r *http.Request) { + u, _ := url.Parse(dest) + proxy := httputil.NewSingleHostReverseProxy(u) + + r.Host = r.URL.Host + + proxy.ServeHTTP(w, r) + } +} diff --git a/pointer/pointer.go b/generated/pointer/pointer.go old mode 100644 new mode 100755 similarity index 100% rename from pointer/pointer.go rename to generated/pointer/pointer.go diff --git a/time/time.go b/generated/time/time.go old mode 100644 new mode 100755 similarity index 100% rename from time/time.go rename to generated/time/time.go diff --git a/restore.go b/restore.go index 179d0f3..efa0a96 100644 --- a/restore.go +++ b/restore.go @@ -20,7 +20,7 @@ import ( "github.com/SecurityBrewery/catalyst/database" "github.com/SecurityBrewery/catalyst/generated/api" - "github.com/SecurityBrewery/catalyst/pointer" + "github.com/SecurityBrewery/catalyst/generated/pointer" "github.com/SecurityBrewery/catalyst/storage" ) diff --git a/server.go b/server.go index 0736b99..ae0b3a9 100644 --- a/server.go +++ b/server.go @@ -2,6 +2,7 @@ package catalyst import ( "context" + "io/fs" "log" "net/http" "time" @@ -21,6 +22,7 @@ import ( "github.com/SecurityBrewery/catalyst/role" "github.com/SecurityBrewery/catalyst/service" "github.com/SecurityBrewery/catalyst/storage" + "github.com/SecurityBrewery/catalyst/ui" ) type Config struct { @@ -138,10 +140,11 @@ func setupAPI(catalystService *service.Service, catalystStorage *storage.Storage server.Get("/callback", callback(config.Auth)) server.With(Authenticate(catalystDatabase, config.Auth), AuthorizeBlockedUser()).Handle("/wss", handleWebSocket(bus)) - // server.With(Authenticate(catalystDatabase, config.Auth), AuthorizeBlockedUser()).NotFound(static) + + fsys, _ := fs.Sub(ui.UI, "dist") server.NotFound(func(w http.ResponseWriter, r *http.Request) { log.Println("not found", r.URL.RawPath) - Authenticate(catalystDatabase, config.Auth)(AuthorizeBlockedUser()(http.HandlerFunc(static))).ServeHTTP(w, r) + Authenticate(catalystDatabase, config.Auth)(AuthorizeBlockedUser()(http.HandlerFunc(api.Static(fsys)))).ServeHTTP(w, r) }) return server, nil diff --git a/static.go b/static.go deleted file mode 100644 index d5eb45c..0000000 --- a/static.go +++ /dev/null @@ -1,22 +0,0 @@ -package catalyst - -import ( - "io/fs" - "net/http" - "strings" - - "github.com/SecurityBrewery/catalyst/ui" -) - -func static(w http.ResponseWriter, r *http.Request) { - fsys, _ := fs.Sub(ui.UI, "dist") - - upath := strings.TrimPrefix(r.URL.Path, "/") - - if _, err := fs.Stat(fsys, upath); err != nil { - r.URL.Path = "/" - r.URL.RawPath = "/" - } - - http.FileServer(http.FS(fsys)).ServeHTTP(w, r) -} diff --git a/storage/storage.go b/storage/storage.go index 25472d4..affb11e 100644 --- a/storage/storage.go +++ b/storage/storage.go @@ -8,7 +8,7 @@ import ( "github.com/aws/aws-sdk-go/service/s3" "github.com/aws/aws-sdk-go/service/s3/s3manager" - "github.com/SecurityBrewery/catalyst/pointer" + "github.com/SecurityBrewery/catalyst/generated/pointer" ) type Storage struct { diff --git a/test/backup_test.go b/test/backup_test.go index 6ced3ee..2348de4 100644 --- a/test/backup_test.go +++ b/test/backup_test.go @@ -21,7 +21,7 @@ import ( "github.com/SecurityBrewery/catalyst" "github.com/SecurityBrewery/catalyst/generated/model" - "github.com/SecurityBrewery/catalyst/pointer" + "github.com/SecurityBrewery/catalyst/generated/pointer" ) func TestBackupAndRestore(t *testing.T) { diff --git a/test/data.go b/test/data.go index 4f2a01b..560d34b 100644 --- a/test/data.go +++ b/test/data.go @@ -7,7 +7,7 @@ import ( "github.com/SecurityBrewery/catalyst/database" "github.com/SecurityBrewery/catalyst/database/migrations" "github.com/SecurityBrewery/catalyst/generated/model" - "github.com/SecurityBrewery/catalyst/pointer" + "github.com/SecurityBrewery/catalyst/generated/pointer" ) var bobSetting = &model.UserData{Email: pointer.String("bob@example.org"), Name: pointer.String("Bob Bad")} diff --git a/test/server_test.go b/test/server_test.go index 10d5abc..f7d2e3e 100644 --- a/test/server_test.go +++ b/test/server_test.go @@ -15,7 +15,7 @@ import ( "github.com/tidwall/sjson" "github.com/SecurityBrewery/catalyst/generated/api" - ctime "github.com/SecurityBrewery/catalyst/time" + ctime "github.com/SecurityBrewery/catalyst/generated/time" ) type testClock struct{} diff --git a/test/test.go b/test/test.go index 8d6f7a4..98492a7 100644 --- a/test/test.go +++ b/test/test.go @@ -20,9 +20,9 @@ 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/pointer" "github.com/SecurityBrewery/catalyst/service" "github.com/SecurityBrewery/catalyst/storage" )