From 31ca5d6c297ec1569f01d72a9389a5590e3486b9 Mon Sep 17 00:00:00 2001 From: Peter Grlica Date: Mon, 5 Dec 2022 08:30:49 +0100 Subject: [PATCH] Added Response postfilter - removed json response - added response postfilter - unit tests --- server/pkg/apigw/filter/postfilter.go | 73 +++++++++++-------- server/pkg/apigw/filter/postfilter_test.go | 42 +++++++++-- server/pkg/apigw/filter/processer.go | 10 ++- server/pkg/apigw/registry/registry.go | 2 +- server/pkg/provision/apigw.go | 39 ++++++++++ server/pkg/provision/auth.go | 61 +++++++++++++++- server/pkg/provision/provision.go | 58 +-------------- server/store/adapters/rdbms/filter.go | 20 ++++- .../system/service/apigw_filter_actions.yaml | 2 +- server/system/types/apigw_filter.go | 6 +- server/tests/apigw/main_test.go | 28 ++++++- .../apigw/postfilter_string_json_test.go | 20 +++++ .../tests/apigw/postfilter_string_kv_test.go | 20 +++++ .../testdata/postfilter_string_json/def.yaml | 38 ++++++++++ .../testdata/postfilter_string_kv/def.yaml | 38 ++++++++++ server/tests/helpers/rbac.go | 6 ++ 16 files changed, 355 insertions(+), 108 deletions(-) create mode 100644 server/pkg/provision/apigw.go create mode 100644 server/tests/apigw/postfilter_string_json_test.go create mode 100644 server/tests/apigw/postfilter_string_kv_test.go create mode 100644 server/tests/apigw/testdata/postfilter_string_json/def.yaml create mode 100644 server/tests/apigw/testdata/postfilter_string_kv/def.yaml diff --git a/server/pkg/apigw/filter/postfilter.go b/server/pkg/apigw/filter/postfilter.go index 172239f84..8d5767b35 100644 --- a/server/pkg/apigw/filter/postfilter.go +++ b/server/pkg/apigw/filter/postfilter.go @@ -6,6 +6,8 @@ import ( "fmt" "net/http" "net/url" + "reflect" + "strings" atypes "github.com/cortezaproject/corteza/server/automation/types" agctx "github.com/cortezaproject/corteza/server/pkg/apigw/ctx" @@ -31,22 +33,16 @@ type ( } } - // support for arbitrary response - // obfuscation - customResponse struct { - types.FilterMeta - params struct { - Source string `json:"source"` - } - } - - jsonResponse struct { + response struct { types.FilterMeta reg typesRegistry params struct { - Exp *atypes.Expr + Header http.Header `json:"header"` + + Exp *atypes.Expr `json:"input"` + Evaluable expr.Evaluable } } @@ -181,11 +177,11 @@ func checkStatus(typ string, status int) bool { } } -func NewJsonResponse(opts options.ApigwOpt, reg typesRegistry) (e *jsonResponse) { - e = &jsonResponse{} +func NewResponse(opts options.ApigwOpt, reg typesRegistry) (e *response) { + e = &response{} - e.Name = "jsonResponse" - e.Label = "JSON response" + e.Name = "response" + e.Label = "Response" e.Kind = types.PostFilter e.Args = []*types.FilterMetaArg{ @@ -194,6 +190,11 @@ func NewJsonResponse(opts options.ApigwOpt, reg typesRegistry) (e *jsonResponse) Label: "input", Options: map[string]interface{}{}, }, + { + Type: "header", + Label: "header", + Options: map[string]interface{}{}, + }, } e.reg = reg @@ -201,28 +202,28 @@ func NewJsonResponse(opts options.ApigwOpt, reg typesRegistry) (e *jsonResponse) return } -func (j jsonResponse) New(opts options.ApigwOpt) types.Handler { - return NewJsonResponse(opts, j.reg) +func (j response) New(opts options.ApigwOpt) types.Handler { + return NewResponse(opts, j.reg) } -func (j jsonResponse) Enabled() bool { +func (j response) Enabled() bool { return true } -func (j jsonResponse) String() string { +func (j response) String() string { return fmt.Sprintf("apigw filter %s (%s)", j.Name, j.Label) } -func (j jsonResponse) Meta() types.FilterMeta { +func (j response) Meta() types.FilterMeta { return j.FilterMeta } -func (j *jsonResponse) Merge(params []byte) (h types.Handler, err error) { +func (j *response) Merge(params []byte) (h types.Handler, err error) { var ( parser = expr.NewParser() ) - err = json.NewDecoder(bytes.NewBuffer(params)).Decode(&j.params.Exp) + err = json.NewDecoder(bytes.NewBuffer(params)).Decode(&j.params) if err != nil { return j, err @@ -239,11 +240,12 @@ func (j *jsonResponse) Merge(params []byte) (h types.Handler, err error) { return j, err } -func (j jsonResponse) Handler() types.HandlerFunc { +func (j response) Handler() types.HandlerFunc { return func(rw http.ResponseWriter, r *http.Request) (err error) { var ( - ctx = r.Context() - scope = agctx.ScopeFromContext(ctx) + ctx = r.Context() + scope = agctx.ScopeFromContext(ctx) + hasJsonHeader = false evald interface{} ) @@ -274,16 +276,23 @@ func (j jsonResponse) Handler() types.HandlerFunc { return } - rw.Header().Add("Content-Type", "application/json") + for h, v := range j.params.Header { + for _, vv := range v { + rw.Header().Add(h, vv) - switch v := evald.(type) { - case string: - rw.Write([]byte(v)) - default: - e := json.NewEncoder(rw) - err = e.Encode(v) + if strings.ToLower(h) == "content-type" { + hasJsonHeader = vv == "application/json" + } + } } + if hasJsonHeader && reflect.ValueOf(evald).Kind() != reflect.String { + err = (json.NewEncoder(rw)).Encode(expr.UntypedValue(evald)) + return + } + + fmt.Fprintf(rw, "%v", evald) + return } } diff --git a/server/pkg/apigw/filter/postfilter_test.go b/server/pkg/apigw/filter/postfilter_test.go index 93cbd233c..d2a32ba39 100644 --- a/server/pkg/apigw/filter/postfilter_test.go +++ b/server/pkg/apigw/filter/postfilter_test.go @@ -114,23 +114,53 @@ func Test_jsonResponse(t *testing.T) { var ( tcc = []tf{ { - name: "Any response as JSON", - expr: `{"expr": "records", "type": "KV"}`, + name: "String response as JSON", + expr: `{"header":{"content-type":["application/json"]},"input":{"expr": "records", "type": "String"}}`, + scope: expr.Must(expr.Any{}.Cast("foobar")), + exp: `foobar`, + }, + { + name: "Array response as JSON", + expr: `{"header":{"content-type":["application/json"]},"input":{"expr": "records", "type": "Array"}}`, scope: expr.Must(expr.Any{}.Cast([]float64{3.14, 42.690})), exp: `[3.14,42.69]`, }, { - name: "KV response as JSON", - expr: `{"expr": "records", "type": "KV"}`, + name: "Array response as text", + expr: `{"input":{"expr": "records", "type": "Array"}}`, + scope: expr.Must(expr.Any{}.Cast([]float64{3.14, 42.690})), + exp: `[3.14 42.69]`, + }, + { + name: "Any response as JSON", + expr: `{"header":{"content-type":["application/json"]},"input": {"expr": "records", "type": "Any"}}`, scope: expr.Must(expr.Any{}.Cast(map[string]string{"foo": "bar", "baz": "bzz"})), exp: `{"baz":"bzz","foo":"bar"}`, }, + { + name: "Any response as text", + expr: `{"input": {"expr": "records", "type": "Any"}}`, + scope: expr.Must(expr.Any{}.Cast(map[string]string{"foo": "bar", "baz": "bzz"})), + exp: `map[baz:bzz foo:bar]`, + }, { name: "struct array response as JSON", - expr: `{"expr": "toJSON(records)", "type": "String"}`, + expr: `{"input":{"expr": "toJSON(records)", "type": "String"}}`, scope: []aux{{"First", "Last"}, {"Foo", "bar"}}, exp: `[{"name":"First","surname":"Last"},{"name":"Foo","surname":"bar"}]`, }, + { + name: "struct array response as text", + expr: `{"input":{"expr": "records", "type": "String"}}`, + scope: []aux{{"First", "Last"}, {"Foo", "bar"}}, + exp: `[{First Last} {Foo bar}]`, + }, + { + name: "string csv response as text", + expr: `{"header":{"content-type":["application/octet-stream"],"Content-Disposition":["attachment; filename=foo.txt"],"Content-Transfer-Encoding":["binary"]},"input":{"expr": "records", "type": "String"}}`, + scope: "\"header 1\",\"header 2\"\nvalue 1,value 2\nvalue 3, value 4", + exp: "\"header 1\",\"header 2\"\nvalue 1,value 2\nvalue 3, value 4", + }, } ) @@ -145,7 +175,7 @@ func Test_jsonResponse(t *testing.T) { r = r.WithContext(agctx.ScopeToContext(context.Background(), scope)) - h := getHandler(NewJsonResponse(options.ApigwOpt{}, &mockHandlerRegistry{})) + h := getHandler(NewResponse(options.ApigwOpt{}, &mockHandlerRegistry{})) h, err := h.Merge([]byte(tc.expr)) req.NoError(err) diff --git a/server/pkg/apigw/filter/processer.go b/server/pkg/apigw/filter/processer.go index 166bd1392..645d90684 100644 --- a/server/pkg/apigw/filter/processer.go +++ b/server/pkg/apigw/filter/processer.go @@ -12,6 +12,7 @@ import ( atypes "github.com/cortezaproject/corteza/server/automation/types" agctx "github.com/cortezaproject/corteza/server/pkg/apigw/ctx" "github.com/cortezaproject/corteza/server/pkg/apigw/types" + "github.com/cortezaproject/corteza/server/pkg/auth" pe "github.com/cortezaproject/corteza/server/pkg/errors" "github.com/cortezaproject/corteza/server/pkg/expr" "github.com/cortezaproject/corteza/server/pkg/jsenv" @@ -90,6 +91,9 @@ func (h *workflow) Merge(params []byte) (types.Handler, error) { t = struct { Workflow string `json:"workflow"` }{} + + // ctx = context.Background() + ctx = auth.SetIdentityToContext(context.Background(), auth.ServiceUser()) ) err := json.NewDecoder(bytes.NewBuffer(params)).Decode(&t) @@ -101,17 +105,17 @@ func (h *workflow) Merge(params []byte) (types.Handler, error) { if err == nil { h.params.Workflow = uint64(i) - return h, h.d.Load(context.Background()) + return h, h.d.Load(ctx) } - if wf, _, err := h.d.Search(context.Background(), atypes.WorkflowFilter{Query: fmt.Sprintf("handle='%s'", t.Workflow)}); err != nil { + if wf, _, err := h.d.Search(ctx, atypes.WorkflowFilter{Query: t.Workflow}); err != nil { return h, err } else { h.params.Workflow = wf[0].ID } // preload workflow cache - return h, h.d.Load(context.Background()) + return h, h.d.Load(ctx) } func (h workflow) Handler() types.HandlerFunc { diff --git a/server/pkg/apigw/registry/registry.go b/server/pkg/apigw/registry/registry.go index 7261f4713..e2bf13a36 100644 --- a/server/pkg/apigw/registry/registry.go +++ b/server/pkg/apigw/registry/registry.go @@ -75,7 +75,7 @@ func (r *Registry) Preload() { // postfilters r.Add("redirection", filter.NewRedirection(r.opts)) - r.Add("jsonResponse", filter.NewJsonResponse(r.opts, service.Registry())) + r.Add("response", filter.NewResponse(r.opts, service.Registry())) r.Add("defaultJsonResponse", filter.NewDefaultJsonResponse(r.opts)) } diff --git a/server/pkg/provision/apigw.go b/server/pkg/provision/apigw.go new file mode 100644 index 000000000..550f51fad --- /dev/null +++ b/server/pkg/provision/apigw.go @@ -0,0 +1,39 @@ +package provision + +import ( + "context" + "fmt" + "net/http" + + atypes "github.com/cortezaproject/corteza/server/automation/types" + "github.com/cortezaproject/corteza/server/store" + "github.com/cortezaproject/corteza/server/system/types" + "go.uber.org/zap" +) + +func apigwFilters(ctx context.Context, log *zap.Logger, s store.Storer) (err error) { + var ( + filters types.ApigwFilterSet + ) + + if filters, _, err = store.SearchApigwFilters(ctx, s, types.ApigwFilterFilter{Ref: "jsonResponse"}); err != nil { + return + } + + for _, f := range filters { + h := http.Header{} + h.Add("Content-Type", "application/json") + + f.Ref = "response" + f.Params = map[string]interface{}{ + "header": h, + "input": &atypes.Expr{Expr: f.Params["input"].(string), Type: "String"}, + } + + if err = store.UpdateApigwFilter(ctx, s, f); err != nil { + log.Warn(fmt.Sprintf("could not migrate jsonResponse to response: %s", err)) + } + } + + return +} diff --git a/server/pkg/provision/auth.go b/server/pkg/provision/auth.go index b7ad80b7c..d96e187c5 100644 --- a/server/pkg/provision/auth.go +++ b/server/pkg/provision/auth.go @@ -3,6 +3,10 @@ package provision import ( "context" "fmt" + "os" + "regexp" + "strings" + internalAuth "github.com/cortezaproject/corteza/server/pkg/auth" "github.com/cortezaproject/corteza/server/pkg/handle" "github.com/cortezaproject/corteza/server/pkg/id" @@ -10,11 +14,9 @@ import ( "github.com/cortezaproject/corteza/server/pkg/options" "github.com/cortezaproject/corteza/server/system/service" "go.uber.org/zap" - "os" - "regexp" - "strings" "github.com/cortezaproject/corteza/server/pkg/errors" + "github.com/cortezaproject/corteza/server/pkg/rand" "github.com/cortezaproject/corteza/server/store" "github.com/cortezaproject/corteza/server/system/types" ) @@ -166,3 +168,56 @@ func createUserHandle(u *types.User) (hdl string) { return hdl } + +// defaultAuthClient checks if default client exists (handle = AUTH_DEFAULT_CLIENT) and adds it +func defaultAuthClient(ctx context.Context, log *zap.Logger, s store.AuthClients, authOpt options.AuthOpt) error { + if authOpt.DefaultClient == "" { + // Default client not set + return nil + } + + c := &types.AuthClient{ + ID: id.Next(), + Handle: authOpt.DefaultClient, + Meta: &types.AuthClientMeta{ + Name: "Corteza Web Applications", + }, + ValidGrant: "authorization_code", + RedirectURI: func() string { + // Disabling protection by redirection URL for now, it caused too much confusion on simple setups + //baseURL, _ := url.Parse(authOpt.BaseURL) + //return fmt.Sprintf("%s://%s", baseURL.Scheme, baseURL.Hostname()) + return "" + }(), + + Secret: string(rand.Bytes(64)), + Scope: "profile api", + Enabled: true, + Trusted: true, + Security: &types.AuthClientSecurity{}, + Labels: nil, + CreatedAt: *now(), + } + + _, err := store.LookupAuthClientByHandle(ctx, s, c.Handle) + if err == nil { + return nil + } + + if !errors.IsNotFound(err) { + return err + } + + if err = store.CreateAuthClient(ctx, s, c); err != nil { + return err + } + + log.Info( + "Added OAuth2 client", + zap.String("name", c.Meta.Name), + zap.String("redirectURI", c.RedirectURI), + zap.Uint64("clientId", c.ID), + ) + + return nil +} diff --git a/server/pkg/provision/provision.go b/server/pkg/provision/provision.go index a24b2060d..d7d71bc7d 100644 --- a/server/pkg/provision/provision.go +++ b/server/pkg/provision/provision.go @@ -4,12 +4,8 @@ import ( "context" "time" - "github.com/cortezaproject/corteza/server/pkg/errors" - "github.com/cortezaproject/corteza/server/pkg/id" "github.com/cortezaproject/corteza/server/pkg/options" - "github.com/cortezaproject/corteza/server/pkg/rand" "github.com/cortezaproject/corteza/server/store" - "github.com/cortezaproject/corteza/server/system/types" "go.uber.org/zap" ) @@ -39,6 +35,7 @@ func Run(ctx context.Context, log *zap.Logger, s store.Storer, provisionOpt opti // Auto-discoveries and other parts that cannot be imported from static files func() error { return emailSettings(ctx, s) }, + func() error { return apigwFilters(ctx, log.Named("apigw.filters"), s) }, func() error { return authAddExternals(ctx, log.Named("auth.externals"), s) }, func() error { return oidcAutoDiscovery(ctx, log.Named("auth.oidc-auto-discovery"), s, authOpt) }, func() error { return defaultAuthClient(ctx, log.Named("auth.clients"), s, authOpt) }, @@ -53,56 +50,3 @@ func Run(ctx context.Context, log *zap.Logger, s store.Storer, provisionOpt opti return nil } - -// defaultAuthClient checks if default client exists (handle = AUTH_DEFAULT_CLIENT) and adds it -func defaultAuthClient(ctx context.Context, log *zap.Logger, s store.AuthClients, authOpt options.AuthOpt) error { - if authOpt.DefaultClient == "" { - // Default client not set - return nil - } - - c := &types.AuthClient{ - ID: id.Next(), - Handle: authOpt.DefaultClient, - Meta: &types.AuthClientMeta{ - Name: "Corteza Web Applications", - }, - ValidGrant: "authorization_code", - RedirectURI: func() string { - // Disabling protection by redirection URL for now, it caused too much confusion on simple setups - //baseURL, _ := url.Parse(authOpt.BaseURL) - //return fmt.Sprintf("%s://%s", baseURL.Scheme, baseURL.Hostname()) - return "" - }(), - - Secret: string(rand.Bytes(64)), - Scope: "profile api", - Enabled: true, - Trusted: true, - Security: &types.AuthClientSecurity{}, - Labels: nil, - CreatedAt: *now(), - } - - _, err := store.LookupAuthClientByHandle(ctx, s, c.Handle) - if err == nil { - return nil - } - - if !errors.IsNotFound(err) { - return err - } - - if err = store.CreateAuthClient(ctx, s, c); err != nil { - return err - } - - log.Info( - "Added OAuth2 client", - zap.String("name", c.Meta.Name), - zap.String("redirectURI", c.RedirectURI), - zap.Uint64("clientId", c.ID), - ) - - return nil -} diff --git a/server/store/adapters/rdbms/filter.go b/server/store/adapters/rdbms/filter.go index aaaae6223..88aa1be05 100644 --- a/server/store/adapters/rdbms/filter.go +++ b/server/store/adapters/rdbms/filter.go @@ -343,6 +343,22 @@ func DefaultFilters() (f *extendedFilters) { return ee, f, nil } + f.ApigwFilter = func(s *Store, f systemType.ApigwFilterFilter) (ee []goqu.Expression, _ systemType.ApigwFilterFilter, err error) { + if ee, f, err = ApigwFilterFilter(s.Dialect, f); err != nil { + return + } + + if len(f.Ref) > 0 { + ee = append(ee, goqu.C("ref").Eq(f.Ref)) + } + + if len(f.Kind) > 0 { + ee = append(ee, goqu.C("kind").Eq(f.Kind)) + } + + return ee, f, nil + } + return } @@ -405,7 +421,9 @@ func stateFalseComparison(d drivers.Dialect, lit string, fs filter.State) goqu.E } // @todo: Currently we have for support for MsSQL, MySql, PSQL, SQLite drivers, -// this changes is supported by all DB but we need to move to store.driver +// +// this changes is supported by all DB but we need to move to store.driver +// // generateSorting verify and converts given sorting to literal if required func generateSorting(sortables map[string]string, s *filter.SortExpr) (out goqu.Expression, err error) { const COALESCE string = "coalesce" diff --git a/server/system/service/apigw_filter_actions.yaml b/server/system/service/apigw_filter_actions.yaml index db1af29f7..d62912ba6 100644 --- a/server/system/service/apigw_filter_actions.yaml +++ b/server/system/service/apigw_filter_actions.yaml @@ -18,7 +18,7 @@ props: fields: [ ID, ref ] - name: search type: "*types.ApigwFilterFilter" - fields: [] + fields: [ kind ] actions: - action: search diff --git a/server/system/types/apigw_filter.go b/server/system/types/apigw_filter.go index 22abf8aef..9c4bdcd54 100644 --- a/server/system/types/apigw_filter.go +++ b/server/system/types/apigw_filter.go @@ -3,9 +3,10 @@ package types import ( "database/sql/driver" "encoding/json" - "github.com/cortezaproject/corteza/server/pkg/sql" "time" + "github.com/cortezaproject/corteza/server/pkg/sql" + "github.com/cortezaproject/corteza/server/pkg/filter" ) @@ -35,6 +36,9 @@ type ( Deleted filter.State `json:"deleted"` Disabled filter.State `json:"disabled"` + Kind string `json:"kind"` + Ref string `json:"ref"` + // Check fn is called by store backend for each resource found function can // modify the resource and return false if store should not return it // diff --git a/server/tests/apigw/main_test.go b/server/tests/apigw/main_test.go index 00b1e3cdf..387fa4583 100644 --- a/server/tests/apigw/main_test.go +++ b/server/tests/apigw/main_test.go @@ -3,13 +3,16 @@ package apigw import ( "context" "errors" - "github.com/cortezaproject/corteza/server/pkg/dal" - "github.com/cortezaproject/corteza/server/store/adapters/rdbms/drivers/sqlite" + "fmt" "os" "path" "testing" + "github.com/cortezaproject/corteza/server/pkg/dal" + "github.com/cortezaproject/corteza/server/store/adapters/rdbms/drivers/sqlite" + "github.com/cortezaproject/corteza/server/app" + as "github.com/cortezaproject/corteza/server/automation/service" "github.com/cortezaproject/corteza/server/pkg/api/server" "github.com/cortezaproject/corteza/server/pkg/apigw" "github.com/cortezaproject/corteza/server/pkg/auth" @@ -61,8 +64,8 @@ func init() { func InitTestApp() { ctx := cli.Context() - if testApp == nil { + if testApp == nil { testApp = helpers.NewIntegrationTestApp(ctx, func(app *app.CortezaApp) (err error) { service.DefaultStore, err = sqlite.ConnectInMemory(ctx) if err != nil { @@ -74,6 +77,10 @@ func InitTestApp() { }) } + if err := testApp.Activate(ctx); err != nil { + panic(fmt.Errorf("could not activate corteza: %v", err)) + } + if r == nil { r = chi.NewRouter() r.Use(server.BaseMiddleware(false, logger.Default())...) @@ -144,11 +151,16 @@ func (h helper) apiInit() *apitest.APITest { func setupScenario(t *testing.T) (context.Context, helper, store.Storer) { ctx, h, s := setup(t) loadScenario(ctx, s, t, h) + loadRbacRules(ctx, s, t, h) _ = apigw.Service().Reload(ctx) return ctx, h, s } +func loadRbacRules(ctx context.Context, s store.Storer, t *testing.T, h helper) { + helpers.AllowMeWorkflowSearch(h) +} + func setup(t *testing.T) (context.Context, helper, store.Storer) { h := newHelper(t) s := service.DefaultStore @@ -156,6 +168,7 @@ func setup(t *testing.T) (context.Context, helper, store.Storer) { u := &sysTypes.User{ ID: id.Next(), } + u.SetRoles(auth.BypassRoles().IDs()...) ctx := auth.SetIdentityToContext(context.Background(), u) @@ -187,11 +200,20 @@ func loadScenario(ctx context.Context, s store.Storer, t *testing.T, h helper) { func loadScenarioWithName(ctx context.Context, s store.Storer, t *testing.T, h helper, scenario string) { cleanup(ctx, h, s) parseEnvoy(ctx, s, h, path.Join("testdata", scenario)) + loadWorkflows(ctx, h) +} + +func loadWorkflows(ctx context.Context, h helper) { + err := as.DefaultWorkflow.Load(ctx) + h.a.NoError(err) } func cleanup(ctx context.Context, h helper, s store.Storer) { h.noError(s.TruncateApigwFilters(ctx)) h.noError(s.TruncateApigwRoutes(ctx)) + h.noError(s.TruncateRbacRules(ctx)) + h.noError(s.TruncateAutomationTriggers(ctx)) + h.noError(s.TruncateAutomationWorkflows(ctx)) } func parseEnvoy(ctx context.Context, s store.Storer, h helper, path string) { diff --git a/server/tests/apigw/postfilter_string_json_test.go b/server/tests/apigw/postfilter_string_json_test.go new file mode 100644 index 000000000..5a9fc61aa --- /dev/null +++ b/server/tests/apigw/postfilter_string_json_test.go @@ -0,0 +1,20 @@ +package apigw + +import ( + "net/http" + "testing" +) + +func Test_postfilter_string_json(t *testing.T) { + var ( + _, h, _ = setupScenario(t) + ) + + h.apiInit(). + Get("/json/string"). + Header("Accept", "application/json"). + Expect(t). + Status(http.StatusOK). + Body("{\"baz\":{\"@value\":{\"1\":{\"@value\":1,\"@type\":\"Float\"},\"2\":{\"@value\":2,\"@type\":\"Float\"}},\"@type\":\"Vars\"},\"foo\":{\"@value\":\"bar\",\"@type\":\"String\"}}"). + End() +} diff --git a/server/tests/apigw/postfilter_string_kv_test.go b/server/tests/apigw/postfilter_string_kv_test.go new file mode 100644 index 000000000..c63ecb40b --- /dev/null +++ b/server/tests/apigw/postfilter_string_kv_test.go @@ -0,0 +1,20 @@ +package apigw + +import ( + "net/http" + "testing" +) + +func Test_postfilter_string_kv(t *testing.T) { + var ( + _, h, _ = setupScenario(t) + ) + + h.apiInit(). + Get("/json/kv"). + Header("Accept", "application/json"). + Expect(t). + Status(http.StatusOK). + Body("{\"baz\":\"123\",\"foo\":\"bar\"}"). + End() +} diff --git a/server/tests/apigw/testdata/postfilter_string_json/def.yaml b/server/tests/apigw/testdata/postfilter_string_json/def.yaml new file mode 100644 index 000000000..eca927d86 --- /dev/null +++ b/server/tests/apigw/testdata/postfilter_string_json/def.yaml @@ -0,0 +1,38 @@ +apigateway: + - endpoint: /json/string + method: GET + enabled: true + filters: + - ref: response + kind: postfilter + enabled: true + params: + input: + expr: toJSON(test) + type: String + header: + Content-Type: [ application/json ] + - ref: workflow + kind: processer + enabled: true + params: + workflow: 'test_variable_json' + +workflows: + - id: 1 + enabled: true + trace: false + handle: test_variable_json + meta: + name: Test variable + triggers: + - enabled: true + stepID: 1 + steps: + - stepID: 1 + kind: expressions + arguments: + - target: test + type: Any + expr: | + {"foo":"bar","baz":{"1":1, "2":2}} diff --git a/server/tests/apigw/testdata/postfilter_string_kv/def.yaml b/server/tests/apigw/testdata/postfilter_string_kv/def.yaml new file mode 100644 index 000000000..a65acae93 --- /dev/null +++ b/server/tests/apigw/testdata/postfilter_string_kv/def.yaml @@ -0,0 +1,38 @@ +apigateway: + - endpoint: /json/kv + method: GET + enabled: true + filters: + - ref: response + kind: postfilter + enabled: true + params: + input: + expr: test + type: KV + header: + Content-Type: [ application/json ] + - ref: workflow + kind: processer + enabled: true + params: + workflow: 'test_variable_kv' + +workflows: + - id: 1 + enabled: true + trace: false + handle: test_variable_kv + meta: + name: Test variable + triggers: + - enabled: true + stepID: 1 + steps: + - stepID: 1 + kind: expressions + arguments: + - target: test + type: KV + expr: | + {"foo":"bar","baz":123} diff --git a/server/tests/helpers/rbac.go b/server/tests/helpers/rbac.go index 08299c6c5..1b51212db 100644 --- a/server/tests/helpers/rbac.go +++ b/server/tests/helpers/rbac.go @@ -3,6 +3,7 @@ package helpers import ( "context" + automationTypes "github.com/cortezaproject/corteza/server/automation/types" composeTypes "github.com/cortezaproject/corteza/server/compose/types" "github.com/cortezaproject/corteza/server/pkg/auth" "github.com/cortezaproject/corteza/server/pkg/cli" @@ -71,6 +72,11 @@ func AllowMeModuleCreate(mrg myRoleGetter) { AllowMe(mrg, composeTypes.NamespaceRbacResource(0), "module.create") } +func AllowMeWorkflowSearch(mrg myRoleGetter) { + AllowMe(mrg, automationTypes.WorkflowRbacResource(0), "workflows.search") + AllowMe(mrg, automationTypes.WorkflowRbacResource(0), "triggers.search") +} + func AllowMeModuleSearch(mrg myRoleGetter) { AllowMe(mrg, composeTypes.NamespaceRbacResource(0), "modules.search") AllowMe(mrg, composeTypes.ModuleRbacResource(0, 0), "read")