diff --git a/app/servers.go b/app/servers.go index 940459ecd..9eb44739c 100644 --- a/app/servers.go +++ b/app/servers.go @@ -10,10 +10,13 @@ import ( automationRest "github.com/cortezaproject/corteza-server/automation/rest" composeRest "github.com/cortezaproject/corteza-server/compose/rest" + "github.com/cortezaproject/corteza-server/compose/service" "github.com/cortezaproject/corteza-server/docs" federationRest "github.com/cortezaproject/corteza-server/federation/rest" "github.com/cortezaproject/corteza-server/pkg/actionlog" "github.com/cortezaproject/corteza-server/pkg/api/server" + "github.com/cortezaproject/corteza-server/pkg/apigw" + "github.com/cortezaproject/corteza-server/pkg/eventbus" "github.com/cortezaproject/corteza-server/pkg/logger" "github.com/cortezaproject/corteza-server/pkg/options" "github.com/cortezaproject/corteza-server/pkg/webapp" @@ -104,6 +107,12 @@ func (app *CortezaApp) mountHttpRoutes(r chi.Router) { r.Route("/federation", federationRest.MountRoutes) } + // temp api gateway support + { + apigw.Setup(struct{}{}, app.Log, eventbus.Service(), service.DefaultStore) + r.Route("/gateway", apigw.Service().Router(context.Background())) + } + var fullpathDocs = options.CleanBase(ho.BaseUrl, ho.ApiBaseUrl, "docs") app.Log.Info( "API docs enabled", diff --git a/go.mod b/go.mod index 425244504..079c9a80a 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module github.com/cortezaproject/corteza-server go 1.16 require ( + github.com/0xAX/notificator v0.0.0-20191016112426-3962a5ea8da1 // indirect github.com/766b/chi-prometheus v0.0.0-20180509160047-46ac2b31aa30 github.com/99designs/basicauth-go v0.0.0-20160802081356-2a93ba0f464d github.com/Masterminds/goutils v1.1.0 // indirect @@ -14,6 +15,8 @@ require ( github.com/SentimensRG/ctx v0.0.0-20180729130232-0bfd988c655d github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d github.com/brianvoe/gofakeit/v6 v6.5.0 + github.com/codegangsta/envy v0.0.0-20141216192214-4b78388c8ce4 // indirect + github.com/codegangsta/gin v0.0.0-20171026143024-cafe2ce98974 // indirect github.com/crewjam/saml v0.4.5 github.com/crusttech/go-oidc v0.0.0-20180918092017-982855dad3e1 github.com/davecgh/go-spew v1.1.1 @@ -50,6 +53,7 @@ require ( github.com/lestrrat-go/strftime v1.0.3 github.com/lib/pq v1.1.0 github.com/markbates/goth v1.67.1 + github.com/mattn/go-shellwords v1.0.12 // indirect github.com/mattn/go-sqlite3 v1.14.0 github.com/microcosm-cc/bluemonday v1.0.10 github.com/minio/minio-go/v6 v6.0.39 @@ -80,6 +84,7 @@ require ( gopkg.in/ini.v1 v1.51.0 // indirect gopkg.in/mail.v2 v2.3.1 gopkg.in/square/go-jose.v2 v2.3.1 // indirect + gopkg.in/urfave/cli.v1 v1.20.0 // indirect gopkg.in/yaml.v2 v2.3.0 gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 rsc.io/qr v0.2.0 diff --git a/go.sum b/go.sum index a53075c38..61bb3031c 100644 --- a/go.sum +++ b/go.sum @@ -33,6 +33,8 @@ cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohl cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +github.com/0xAX/notificator v0.0.0-20191016112426-3962a5ea8da1 h1:j9HaafapDbPbGRDku6e/HRs6KBMcKHiWcm1/9Sbxnl4= +github.com/0xAX/notificator v0.0.0-20191016112426-3962a5ea8da1/go.mod h1:NtXa9WwQsukMHZpjNakTTz0LArxvGYdPA9CjIcUSZ6s= github.com/766b/chi-prometheus v0.0.0-20180509160047-46ac2b31aa30 h1:bNHbCMKiQxpRNe4Pk2W09N1aXXc4ICOawQFKIDEicqc= github.com/766b/chi-prometheus v0.0.0-20180509160047-46ac2b31aa30/go.mod h1:X/LhbmoBoRu8TxoGIOIraVNhfz3hhikJoaelrOuhdPY= github.com/99designs/basicauth-go v0.0.0-20160802081356-2a93ba0f464d h1:j6oB/WPCigdOkxtuPl1VSIiLpy7Mdsu6phQffbF19Ng= @@ -90,6 +92,10 @@ github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5P github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/codegangsta/envy v0.0.0-20141216192214-4b78388c8ce4 h1:ihrIKrLQzm6Q6NJHBMemvaIGTFxgxQUEkn2AjN0Aulw= +github.com/codegangsta/envy v0.0.0-20141216192214-4b78388c8ce4/go.mod h1:X7wHz0C25Lga6CnJ4WAQNbUQ9P/8eWSNv8qIO71YkSM= +github.com/codegangsta/gin v0.0.0-20171026143024-cafe2ce98974 h1:ysuVNDVE4LIky6I+6JlgAKG+wBNKMpVv3m3neVpvFVw= +github.com/codegangsta/gin v0.0.0-20171026143024-cafe2ce98974/go.mod h1:UBYuwaH3dMw91EZ7tGVaFF6GDj5j46S7zqB9lZPIe58= github.com/crewjam/httperr v0.0.0-20190612203328-a946449404da h1:WXnT88cFG2davqSFqvaFfzkSMC0lqh/8/rKZ+z7tYvI= github.com/crewjam/httperr v0.0.0-20190612203328-a946449404da/go.mod h1:+rmNIXRvYMqLQeR4DHyTvs6y0MEMymTz4vyFpFkKTPs= github.com/crewjam/saml v0.4.5 h1:H9u+6CZAESUKHxMyxUbVn0IawYvKZn4nt3d4ccV4O/M= @@ -288,6 +294,8 @@ github.com/mattermost/xml-roundtrip-validator v0.0.0-20201213122252-bcd7e1b9601e github.com/mattermost/xml-roundtrip-validator v0.0.0-20201213122252-bcd7e1b9601e/go.mod h1:qccnGMcpgwcNaBnxqpJpWWUiPNr5H3O8eDgGV9gT5To= github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-shellwords v1.0.12 h1:M2zGm7EW6UQJvDeQxo4T51eKPurbeFbe8WtebGE2xrk= +github.com/mattn/go-shellwords v1.0.12/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y= github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/mattn/go-sqlite3 v1.14.0 h1:mLyGNKR8+Vv9CAU7PphKa2hkEqxxhn8i32J6FPj1/QA= github.com/mattn/go-sqlite3 v1.14.0/go.mod h1:JIl7NbARA7phWnGvh0LKTyg7S9BA+6gx71ShQilpsus= @@ -739,6 +747,8 @@ gopkg.in/mail.v2 v2.3.1/go.mod h1:htwXN1Qh09vZJ1NVKxQqHPBaCBbzKhp5GzuJEA4VJWw= gopkg.in/square/go-jose.v2 v2.3.1 h1:SK5KegNXmKmqE342YYN2qPHEnUYeoMiXXl1poUlI+o4= gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/urfave/cli.v1 v1.20.0 h1:NdAVW6RYxDif9DhDHaAortIu956m2c0v+09AZBPTbE0= +gopkg.in/urfave/cli.v1 v1.20.0/go.mod h1:vuBzUtMdQeixQj8LVd+/98pzhxNGQoyuPBlsXHOQNO0= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/pkg/apigw/apigw.go b/pkg/apigw/apigw.go index cbdb02191..a1c2dc6ce 100644 --- a/pkg/apigw/apigw.go +++ b/pkg/apigw/apigw.go @@ -173,3 +173,16 @@ func (s *apigw) Init(ctx context.Context, route ...*route) { } } } + +func (s *apigw) Funcs(kind string) (list functionMetaList) { + list = s.reg.All() + + if kind != "" { + list, _ = list.Filter(func(fm *functionMeta) (bool, error) { + // return fm. + return fm.Kind == kind, nil + }) + } + + return +} diff --git a/pkg/apigw/expediter.go b/pkg/apigw/expediter.go index 2a09081a3..5bdd90ae4 100644 --- a/pkg/apigw/expediter.go +++ b/pkg/apigw/expediter.go @@ -2,51 +2,56 @@ package apigw import ( "context" + "encoding/json" "fmt" "net/http" - "net/http/httptest" - "github.com/cortezaproject/corteza-server/pkg/expr" - "github.com/cortezaproject/corteza-server/pkg/wfexec" + "github.com/cortezaproject/corteza-server/system/types" "github.com/davecgh/go-spew/spew" ) type ( - redirectExpediterArgs struct { - Location string + expediterRedirection struct{} + + errorHandler struct { + name string + args []string + weight int + step int } ) -func redirectExpediter(c context.Context, params *expr.Vars) wfHandler { - var ( - clv = redirectExpediterArgs{} - ) - - return func(c context.Context, er *wfexec.ExecRequest) (r wfexec.ExecResponse, err error) { - params.Decode(&clv) - spew.Dump("redirect expediter fn()", clv) - e := er.Scope.GetValue()["envelope"] - ee := e.Get().(envelope) - - http.Redirect(ee.Writer, ee.Request, clv.Location, http.StatusTemporaryRedirect) - - r = &expr.Vars{} - return +func (h expediterRedirection) Meta(f *types.Function) functionMeta { + return functionMeta{ + Step: 3, + Name: "expediterRedirection", + Label: "Redirection expediter", + Kind: "expediter", + Weight: int(f.Weight), + Params: f.Params, } } -func expediterErrorFn(c context.Context, er *wfexec.ExecRequest) (r wfexec.ExecResponse, err error) { - // spew.Dump("expediter error fn()", er) +func (h expediterRedirection) Handler() handlerFunc { + return func(ctx context.Context, scope *scp, params map[string]interface{}, ff functionHandler) error { + scope.writer.Header().Add(fmt.Sprintf("step_%d", ff.step), ff.name) + http.Redirect(scope.writer, scope.req, params["location"].(string), http.StatusFound) - e := er.Scope.GetValue()["error"] - values := er.Scope.GetValue()["writer"] - - writer := values.Get() - eValue := e.Get() - - fmt.Fprintf(writer.(*httptest.ResponseRecorder), fmt.Sprintf(`{"msg": "%s"}`, eValue)) - writer.(*httptest.ResponseRecorder).Code = http.StatusBadGateway - - r = &expr.Vars{} - return + return nil + } +} + +func (pp errorHandler) Exec(ctx context.Context, scope *scp, err error) { + type ( + responseHelper struct { + Msg string `json:"msg"` + } + ) + + resp := responseHelper{ + Msg: err.Error(), + } + spew.Dump("ERR in expediter", err, resp) + + json.NewEncoder(scope.writer).Encode(resp) } diff --git a/pkg/apigw/function.go b/pkg/apigw/function.go index e064a8fa2..586b7d0be 100644 --- a/pkg/apigw/function.go +++ b/pkg/apigw/function.go @@ -7,6 +7,8 @@ import ( ) type ( + functionMetaList []*functionMeta + Handler interface { Handler() handlerFunc Meta(f *types.Function) functionMeta @@ -15,12 +17,20 @@ type ( handlerFunc func(context.Context, *scp, map[string]interface{}, functionHandler) error functionMeta struct { - step int - weight int - name string - label string - kind string - params map[string]interface{} + Step int `json:"step"` + Weight int `json:"-"` + Name string `json:"name"` + Label string `json:"label"` + Kind string `json:"kind"` + Params map[string]interface{} `json:"-"` + Args []*functionMetaArg `json:"params,omitempty"` + } + + functionMetaArg struct { + Label string `json:"label"` + Type string `json:"type"` + Example string `json:"example"` + Options map[string]interface{} `json:"options"` } functionHandler struct { @@ -43,12 +53,12 @@ func (ff *functionHandler) SetHandler(h handlerFunc) { } func (ff *functionHandler) Merge(ctx context.Context, p functionMeta) { - ff.step = p.step - ff.kind = p.kind - ff.label = p.label - ff.name = p.name - ff.weight = p.weight - ff.params = p.params + ff.step = p.Step + ff.kind = p.Kind + ff.label = p.Label + ff.name = p.Name + ff.weight = p.Weight + ff.params = p.Params } func (ff functionHandler) Weight() int { @@ -56,3 +66,17 @@ func (ff functionHandler) Weight() int { // per step, we're doing something wrong return ff.step*1000 + ff.weight } + +func (fm functionMetaList) Filter(f func(*functionMeta) (bool, error)) (out functionMetaList, err error) { + var ok bool + out = functionMetaList{} + for i := range fm { + if ok, err = f(fm[i]); err != nil { + return + } else if ok { + out = append(out, fm[i]) + } + } + + return +} diff --git a/pkg/apigw/matcher.go b/pkg/apigw/matcher.go deleted file mode 100644 index fe0412020..000000000 --- a/pkg/apigw/matcher.go +++ /dev/null @@ -1,42 +0,0 @@ -package apigw - -import ( - "context" - "errors" - - "github.com/cortezaproject/corteza-server/pkg/expr" - "github.com/cortezaproject/corteza-server/pkg/wfexec" - "github.com/davecgh/go-spew/spew" -) - -type ( - authenticationOriginMatcherArgs struct { - Origin string - } -) - -func authenticationOriginMatcher(c context.Context, params *expr.Vars) wfHandler { - var ( - aomp = authenticationOriginMatcherArgs{} - ) - - return func(c context.Context, er *wfexec.ExecRequest) (r wfexec.ExecResponse, err error) { - - params.Decode(&aomp) - spew.Dump("authentication origin matcher fn()", aomp) - e := er.Scope.GetValue()["envelope"] - ee := e.Get().(envelope) - - origin := ee.Request.Header.Get("Origin") - - spew.Dump("input, real", aomp.Origin, origin) - - if aomp.Origin != origin { - err = errors.New("origin fail") - return - } - - r = &expr.Vars{} - return - } -} diff --git a/pkg/apigw/processer.go b/pkg/apigw/processer.go index ca92bf6bf..9ea0f47fa 100644 --- a/pkg/apigw/processer.go +++ b/pkg/apigw/processer.go @@ -3,53 +3,88 @@ package apigw import ( "context" - "github.com/cortezaproject/corteza-server/pkg/expr" - "github.com/cortezaproject/corteza-server/pkg/wfexec" - "github.com/davecgh/go-spew/spew" + "github.com/cortezaproject/corteza-server/pkg/eventbus" + "github.com/cortezaproject/corteza-server/system/types" ) -func formDataProcesserFn(c context.Context, er *wfexec.ExecRequest) (r wfexec.ExecResponse, err error) { - type ( - formDataProcesserResponse struct { - Name string `json:"name"` - } - ) +type ( + dispatcher interface { + Dispatch(ctx context.Context, ev eventbus.Event) + } - spew.Dump("step processer fn()") + processerWorkflow struct { + d dispatcher + } +) - e := er.Scope.GetValue()["envelope"] - ee := e.Get() - - // ee.(envelope).Writer.WriteHeader(int(id)) - ee.(envelope).Writer.Write([]byte(`{"test":"foobar"}`)) - - e.Assign(ee) - - // req := values.Get() - // ww := wr.Get() - // writer := ww.(http.ResponseWriter) - - // formValue := req.(*http.Request).PostFormValue("name") - - // resp := formDataProcesserResponse{ - // // Name: fmt.Sprintf("AA %s AA", formValue), - // Name: "formValue", - // } - - // encoder := json.NewEncoder(writer) - // encoder.Encode(resp) - - // writer.(*httptest.ResponseRecorder).Header()["Content-Type"] = []string{"application/json"} - // writer.Header().Set("Content-Type", "application/json3") - - // spew.Dump(writer.(*httptest.ResponseRecorder).Header()) - // a, b := expr.NewKV(writer) - // spew.Dump("Aaaaaaaaaaa", a) - - vv := &expr.Vars{} - // vv.Set("writer", writer) - - r = vv - - return +func (h processerWorkflow) Meta(f *types.Function) functionMeta { + return functionMeta{ + Step: 2, + Name: "processerWorkflow", + Label: "Workflow processer", + Kind: "processer", + Weight: int(f.Weight), + Params: f.Params, + Args: []*functionMetaArg{ + { + Type: "workflow", + Label: "workflow", + Options: map[string]interface{}{}, + }, + }, + } } + +func (h processerWorkflow) Handler() handlerFunc { + return func(ctx context.Context, scope *scp, params map[string]interface{}, ff functionHandler) error { + // h.d.Dispatch(c, event.ApiOnProcess(&envlp)) + + return nil + } +} + +// func formDataProcesserFn(c context.Context, er *wfexec.ExecRequest) (r wfexec.ExecResponse, err error) { +// type ( +// formDataProcesserResponse struct { +// Name string `json:"name"` +// } +// ) + +// spew.Dump("step processer fn()") + +// e := er.Scope.GetValue()["envelope"] +// ee := e.Get() + +// // ee.(envelope).Writer.WriteHeader(int(id)) +// ee.(envelope).Writer.Write([]byte(`{"test":"foobar"}`)) + +// e.Assign(ee) + +// // req := values.Get() +// // ww := wr.Get() +// // writer := ww.(http.ResponseWriter) + +// // formValue := req.(*http.Request).PostFormValue("name") + +// // resp := formDataProcesserResponse{ +// // // Name: fmt.Sprintf("AA %s AA", formValue), +// // Name: "formValue", +// // } + +// // encoder := json.NewEncoder(writer) +// // encoder.Encode(resp) + +// // writer.(*httptest.ResponseRecorder).Header()["Content-Type"] = []string{"application/json"} +// // writer.Header().Set("Content-Type", "application/json3") + +// // spew.Dump(writer.(*httptest.ResponseRecorder).Header()) +// // a, b := expr.NewKV(writer) +// // spew.Dump("Aaaaaaaaaaa", a) + +// vv := &expr.Vars{} +// // vv.Set("writer", writer) + +// r = vv + +// return +// } diff --git a/pkg/apigw/registry.go b/pkg/apigw/registry.go new file mode 100644 index 000000000..5a14bfc32 --- /dev/null +++ b/pkg/apigw/registry.go @@ -0,0 +1,52 @@ +package apigw + +import ( + "fmt" + + "github.com/cortezaproject/corteza-server/system/types" +) + +type ( + registry struct { + h map[string]Handler + } +) + +func NewRegistry() *registry { + return ®istry{ + h: map[string]Handler{}, + } +} + +func (r *registry) Add(n string, h Handler) { + r.h[n] = h +} + +func (r *registry) Get(identifier string) (Handler, error) { + var ( + ok bool + f Handler + ) + + if f, ok = r.h[identifier]; !ok { + return nil, fmt.Errorf("could not get element from registry: %s", identifier) + } + + return f, nil +} + +func (r *registry) All() (list functionMetaList) { + for _, handler := range r.h { + m := handler.Meta(&types.Function{}) + list = append(list, &m) + } + + return +} + +func (r *registry) Preload() { + r.Add("verifierQueryParam", verifierQueryParam{}) + r.Add("verifierOrigin", verifierOrigin{}) + r.Add("expediterRedirection", expediterRedirection{}) + r.Add("processerWorkflow", processerWorkflow{}) +} diff --git a/pkg/apigw/route.go b/pkg/apigw/route.go index ddfa6e5c3..0749f1fe6 100644 --- a/pkg/apigw/route.go +++ b/pkg/apigw/route.go @@ -3,62 +3,30 @@ package apigw import ( "context" "net/http" - - "github.com/cortezaproject/corteza-server/pkg/expr" - "github.com/cortezaproject/corteza-server/pkg/logger" - "github.com/cortezaproject/corteza-server/pkg/wfexec" - "github.com/davecgh/go-spew/spew" ) type ( route struct { + ID uint64 endpoint string method string - graph *wfexec.Graph - steps []wfexec.Step - fns wfHandlerList + + pipe *pl } ) -func (r route) validate(req *http.Request) (err error) { - // if req.Method != r.method { - // err = errors.New("http method invalid") - // } - - return -} - func (r route) ServeHTTP(w http.ResponseWriter, req *http.Request) { - if err := r.validate(req); err != nil { - spew.Dump("ERR", err) - return - } + var ( + ctx = context.Background() + scope = scp{ + req: req, + writer: w, + } + ) - sess := wfexec.NewSession(context.Background(), r.graph, wfexec.SetLogger(logger.Default()), wfexec.SetHandler(func(ss wfexec.SessionStatus, s1 *wfexec.State, s2 *wfexec.Session) { - // spew.Dump("event handler here!", ss) - })) - - scope := &expr.Vars{} - - scope.Set("envelope", envelope{ - Request: req, - Writer: w, - }) - - if len(r.steps) == 0 { - // dont serve, do what? return default response? - return - } - - err := sess.Exec(context.Background(), r.steps[0], scope) - - // if err != nil { - // fmt.Fprintf(w, "no go, err on exec: %s", err) - // return - // } - - err = sess.Wait(context.Background()) + err := r.pipe.Exec(ctx, &scope) if err != nil { + // log error } } diff --git a/pkg/apigw/validator.go b/pkg/apigw/validator.go deleted file mode 100644 index d83b2de8e..000000000 --- a/pkg/apigw/validator.go +++ /dev/null @@ -1,40 +0,0 @@ -package apigw - -import ( - "context" - "errors" - - "github.com/cortezaproject/corteza-server/pkg/expr" - "github.com/cortezaproject/corteza-server/pkg/wfexec" - "github.com/davecgh/go-spew/spew" -) - -type ( - contentLengthValidatorArgs struct { - Length int - } -) - -func contentLengthValidator(c context.Context, params *expr.Vars) wfHandler { - var ( - clv = contentLengthValidatorArgs{} - ) - - return func(c context.Context, er *wfexec.ExecRequest) (r wfexec.ExecResponse, err error) { - - params.Decode(&clv) - spew.Dump("body size validator fn()", clv) - e := er.Scope.GetValue()["envelope"] - ee := e.Get().(envelope) - - cl := ee.Request.ContentLength - - if clv.Length < int(cl) { - err = errors.New("content length overriden") - return - } - - r = &expr.Vars{} - return - } -} diff --git a/pkg/apigw/verifier.go b/pkg/apigw/verifier.go new file mode 100644 index 000000000..dd68b3649 --- /dev/null +++ b/pkg/apigw/verifier.go @@ -0,0 +1,157 @@ +package apigw + +import ( + "context" + "fmt" + + "github.com/cortezaproject/corteza-server/pkg/expr" + "github.com/cortezaproject/corteza-server/system/types" + "github.com/davecgh/go-spew/spew" +) + +type ( + verifierQueryParam struct{} + verifierOrigin struct{} +) + +func (h verifierQueryParam) Meta(f *types.Function) functionMeta { + return functionMeta{ + Step: 0, + Name: "verifierQueryParam", + Label: "Query parameters verifier", + Kind: "verifier", + Weight: int(f.Weight), + Params: f.Params, + Args: []*functionMetaArg{ + { + Type: "expr", + Label: "expr", + Options: map[string]interface{}{}, + }, + }, + } +} + +func (h verifierOrigin) Meta(f *types.Function) functionMeta { + return functionMeta{ + Step: 0, + Name: "verifierOrigin", + Label: "Origin verifier", + Kind: "verifier", + Weight: int(f.Weight), + Params: f.Params, + Args: []*functionMetaArg{ + { + Type: "expr", + Label: "expr", + Options: map[string]interface{}{}, + }, + }, + } +} + +func (h verifierQueryParam) Handler() handlerFunc { + return func(ctx context.Context, scope *scp, params map[string]interface{}, ff functionHandler) error { + for k := range ff.params { + + v, ok := params[k] + + if !ok { + spew.Dump("not in params", k) + continue + } + + vv := map[string]interface{}{} + vals := scope.req.URL.Query() + + for k, v := range vals { + vv[k] = v[0] + } + + // get the request data and put it into vars + out, err := expr.NewVars(vv) + + if err != nil { + // spew.Dump("ERR!", err) + return err + } + + pp := expr.NewParser() + tt, err := pp.Parse(v.(string)) + + if err != nil { + // spew.Dump("ERR!", err) + return err + } + + b, err := tt.Test(ctx, out) + + if err != nil { + // spew.Dump("ERR!", err) + return err + } + + spew.Dump("BBBB", b) + + if !b { + return fmt.Errorf("failed on step %d, function %s", ff.step, ff.name) + } + } + + // testing + scope.req.Header.Add(fmt.Sprintf("step_%d", ff.step), ff.name) + + return nil + } +} + +func (h verifierOrigin) Handler() handlerFunc { + return func(ctx context.Context, scope *scp, params map[string]interface{}, ff functionHandler) error { + for k := range ff.params { + v, ok := params[k] + + if !ok { + spew.Dump("not in params", k) + continue + } + + vv := map[string]interface{}{ + "origin": scope.req.Header.Get("Origin"), + } + + // get the request data and put it into vars + out, err := expr.NewVars(vv) + + if err != nil { + spew.Dump("ERR!", err) + return err + } + + pp := expr.NewParser() + tt, err := pp.Parse(v.(string)) + + if err != nil { + spew.Dump("ERR!", err) + return err + } + + b, err := tt.Test(ctx, out) + + if err != nil { + spew.Dump("ERR!", err) + return err + } + + spew.Dump("BBBB", b) + + if !b { + return fmt.Errorf("failed on step %d, function %s", ff.step, ff.name) + } + } + + // testing + scope.req.Header.Add(fmt.Sprintf("step_%d", ff.step), ff.name) + + return nil + } +} diff --git a/system/rest.yaml b/system/rest.yaml index 41e68541b..ee0b94405 100644 --- a/system/rest.yaml +++ b/system/rest.yaml @@ -1510,7 +1510,6 @@ endpoints: entrypoint: route authentication: [] imports: - # - github.com/cortezaproject/corteza-server/system/types - github.com/cortezaproject/corteza-server/pkg/label apis: - name: list @@ -1521,8 +1520,8 @@ endpoints: get: - { name: routeID, type: "[]string", title: "Filter by route ID" } - { name: query, type: "string", title: "Filter routes" } - - { name: deleted, type: "uint64", title: "Exclude (0, default), include (1) or return only (2) deleted routes" } - - { name: disabled, type: "uint64", title: "Exclude (0, default), include (1) or return only (2) disabled routes" } + - { name: deleted, type: "uint64", title: "Exclude (0, default), include (1) or return only (2) deleted routes" } + - { name: disabled, type: "uint64", title: "Exclude (0, default), include (1) or return only (2) disabled routes" } - { name: labels, type: "map[string]string", title: "Labels", parser: "label.ParseStrings" } - { name: limit, type: "uint", title: "Limit" } - { name: pageCursor, type: "string", title: "Page cursor" } @@ -1535,10 +1534,9 @@ endpoints: post: - { name: endpoint, type: string, required: true, title: "Route endpoint" } - { name: method, type: string, title: "Route method" } - # - { name: meta, type: "*types.RouteMeta", title: "Route meta data", parser: "types.ParseRouteMeta" } - { name: debug, type: bool, title: "Debug route" } - { name: enabled, type: bool, title: "Is route enabled" } - - { name: group, type: uint64, title: "Route group" } + - { name: group, type: uint64, title: "Route group" } - name: update method: PUT title: Update route details @@ -1550,7 +1548,7 @@ endpoints: - { name: method, type: string, title: "Route method" } - { name: debug, type: bool, title: "Debug route" } - { name: enabled, type: bool, title: "Is route enabled" } - - { name: group, type: uint64, title: "Route group" } + - { name: group, type: uint64, title: "Route group" } - name: read method: GET title: Read route details @@ -1585,7 +1583,6 @@ endpoints: authentication: [] imports: - github.com/cortezaproject/corteza-server/system/types - # - github.com/cortezaproject/corteza-server/pkg/label apis: - name: list method: GET @@ -1593,26 +1590,25 @@ endpoints: path: "/" parameters: get: - - { name: functionID, type: "[]string", title: "Filter by function ID" } - - { name: routeID, type: "string", title: "Filter by route ID" } - - { name: query, type: "string", title: "Filter functions" } - - { name: deleted, type: "uint64", title: "Exclude (0, default), include (1) or return only (2) deleted functions" } - - { name: disabled, type: "uint64", title: "Exclude (0, default), include (1) or return only (2) disabled functions" } - # - { name: labels, type: "map[string]string", title: "Labels", parser: "label.ParseStrings" } - - { name: limit, type: "uint", title: "Limit" } - - { name: pageCursor, type: "string", title: "Page cursor" } - - { name: sort, type: "string", title: "Sort items" } + - { name: functionID, type: "[]string", title: "Filter by function ID" } + - { name: routeID, type: "string", title: "Filter by route ID" } + - { name: query, type: "string", title: "Filter functions" } + - { name: deleted, type: "uint64", title: "Exclude (0, default), include (1) or return only (2) deleted functions" } + - { name: disabled, type: "uint64", title: "Exclude (0, default), include (1) or return only (2) disabled functions" } + - { name: limit, type: "uint", title: "Limit" } + - { name: pageCursor, type: "string", title: "Page cursor" } + - { name: sort, type: "string", title: "Sort items" } - name: create method: PUT title: Create function path: "" parameters: post: - - { name: routeID, type: uint64, required: true, title: "Route" } - - { name: weight, type: uint64, title: "Function priority" } - - { name: kind, type: "types.ApigwFunctionKind", title: "Function kind" } - - { name: ref, type: string, title: "Function ref" } - - { name: params, type: "types.FuncParams", title: "Function parameters", parser: "types.ParseApigwfFunctionParams" } + - { name: routeID, type: uint64, title: "Route", required: true } + - { name: weight, type: uint64, title: "Function priority" } + - { name: kind, type: "types.ApigwFunctionKind", title: "Function kind" } + - { name: ref, type: string, title: "Function ref" } + - { name: params, type: "types.FuncParams", title: "Function parameters", parser: "types.ParseApigwfFunctionParams" } - name: update method: POST title: Update route details @@ -1620,11 +1616,11 @@ endpoints: parameters: path: [ { name: functionID, type: uint64, required: true, title: "Function ID" } ] post: - - { name: routeID, type: uint64, required: true, title: "Route" } - - { name: weight, type: uint64, title: "Function priority" } - - { name: kind, type: "types.ApigwFunctionKind", title: "Function kind" } - - { name: ref, type: string, title: "Function ref" } - - { name: params, type: "types.FuncParams", title: "Function parameters", parser: "types.ParseApigwfFunctionParams" } + - { name: routeID, type: uint64, title: "Route", required: true } + - { name: weight, type: uint64, title: "Function priority" } + - { name: kind, type: "types.ApigwFunctionKind", title: "Function kind" } + - { name: ref, type: string, title: "Function ref" } + - { name: params, type: "types.FuncParams", title: "Function parameters", parser: "types.ParseApigwfFunctionParams" } - name: read method: GET title: Read function details @@ -1640,3 +1636,10 @@ endpoints: title: Undelete function path: "/{functionID}/undelete" parameters: { path: [ { name: functionID, type: uint64, required: true, title: "Function ID" } ] } + - name: definitions + method: GET + title: Function definitions + path: "/def" + parameters: + get: + - { name: kind, type: "string", title: "Filter functions by kind" } diff --git a/system/rest/function.go b/system/rest/function.go index 221ee7a12..5fd0d24c1 100644 --- a/system/rest/function.go +++ b/system/rest/function.go @@ -32,6 +32,7 @@ type ( Update(ctx context.Context, upd *types.Function) (*types.Function, error) DeleteByID(ctx context.Context, ID uint64) error UndeleteByID(ctx context.Context, ID uint64) error + Definitions(context.Context, string) (interface{}, error) } ) @@ -106,6 +107,10 @@ func (ctrl *Function) Delete(ctx context.Context, r *request.FunctionDelete) (in return api.OK(), ctrl.svc.DeleteByID(ctx, r.FunctionID) } +func (ctrl *Function) Definitions(ctx context.Context, r *request.FunctionDefinitions) (interface{}, error) { + return ctrl.svc.Definitions(ctx, r.Kind) +} + func (ctrl *Function) Undelete(ctx context.Context, r *request.FunctionUndelete) (interface{}, error) { return api.OK(), ctrl.svc.UndeleteByID(ctx, r.FunctionID) } diff --git a/system/rest/handlers/function.go b/system/rest/handlers/function.go index 80d67b14b..464a76e45 100644 --- a/system/rest/handlers/function.go +++ b/system/rest/handlers/function.go @@ -25,16 +25,18 @@ type ( Read(context.Context, *request.FunctionRead) (interface{}, error) Delete(context.Context, *request.FunctionDelete) (interface{}, error) Undelete(context.Context, *request.FunctionUndelete) (interface{}, error) + Definitions(context.Context, *request.FunctionDefinitions) (interface{}, error) } // HTTP API interface Function struct { - List func(http.ResponseWriter, *http.Request) - Create func(http.ResponseWriter, *http.Request) - Update func(http.ResponseWriter, *http.Request) - Read func(http.ResponseWriter, *http.Request) - Delete func(http.ResponseWriter, *http.Request) - Undelete func(http.ResponseWriter, *http.Request) + List func(http.ResponseWriter, *http.Request) + Create func(http.ResponseWriter, *http.Request) + Update func(http.ResponseWriter, *http.Request) + Read func(http.ResponseWriter, *http.Request) + Delete func(http.ResponseWriter, *http.Request) + Undelete func(http.ResponseWriter, *http.Request) + Definitions func(http.ResponseWriter, *http.Request) } ) @@ -134,6 +136,22 @@ func NewFunction(h FunctionAPI) *Function { return } + api.Send(w, r, value) + }, + Definitions: func(w http.ResponseWriter, r *http.Request) { + defer r.Body.Close() + params := request.NewFunctionDefinitions() + if err := params.Fill(r); err != nil { + api.Send(w, r, err) + return + } + + value, err := h.Definitions(r.Context(), params) + if err != nil { + api.Send(w, r, err) + return + } + api.Send(w, r, value) }, } @@ -148,5 +166,6 @@ func (h Function) MountRoutes(r chi.Router, middlewares ...func(http.Handler) ht r.Get("/apigw/function/{functionID}", h.Read) r.Delete("/apigw/function/{functionID}", h.Delete) r.Post("/apigw/function/{functionID}/undelete", h.Undelete) + r.Get("/apigw/function/def", h.Definitions) }) } diff --git a/system/rest/request/function.go b/system/rest/request/function.go index 457bcefc6..20c4a4ea4 100644 --- a/system/rest/request/function.go +++ b/system/rest/request/function.go @@ -155,6 +155,13 @@ type ( // Function ID FunctionID uint64 `json:",string"` } + + FunctionDefinitions struct { + // Kind GET parameter + // + // Filter functions by kind + Kind string + } ) // NewFunctionList request @@ -615,3 +622,38 @@ func (r *FunctionUndelete) Fill(req *http.Request) (err error) { return err } + +// NewFunctionDefinitions request +func NewFunctionDefinitions() *FunctionDefinitions { + return &FunctionDefinitions{} +} + +// Auditable returns all auditable/loggable parameters +func (r FunctionDefinitions) Auditable() map[string]interface{} { + return map[string]interface{}{ + "kind": r.Kind, + } +} + +// Auditable returns all auditable/loggable parameters +func (r FunctionDefinitions) GetKind() string { + return r.Kind +} + +// Fill processes request and fills internal variables +func (r *FunctionDefinitions) Fill(req *http.Request) (err error) { + + { + // GET params + tmp := req.URL.Query() + + if val, ok := tmp["kind"]; ok && len(val) > 0 { + r.Kind, err = val[0], nil + if err != nil { + return err + } + } + } + + return err +} diff --git a/system/service/function.go b/system/service/function.go index 881ae5676..d3a2c408e 100644 --- a/system/service/function.go +++ b/system/service/function.go @@ -4,6 +4,7 @@ import ( "context" "github.com/cortezaproject/corteza-server/pkg/actionlog" + "github.com/cortezaproject/corteza-server/pkg/apigw" a "github.com/cortezaproject/corteza-server/pkg/auth" "github.com/cortezaproject/corteza-server/store" "github.com/cortezaproject/corteza-server/system/types" @@ -221,3 +222,8 @@ func (svc *function) Search(ctx context.Context, filter types.FunctionFilter) (r return r, f, svc.recordAction(ctx, aProps, FunctionActionSearch, err) } + +func (svc *function) Definitions(ctx context.Context, kind string) (interface{}, error) { + // get the definitions from registry + return apigw.Service().Funcs(kind), nil +} diff --git a/vendor/modules.txt b/vendor/modules.txt index 95b38a46e..2c2392236 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -1,5 +1,7 @@ # cloud.google.com/go v0.67.0 cloud.google.com/go/compute/metadata +# github.com/0xAX/notificator v0.0.0-20191016112426-3962a5ea8da1 +## explicit # github.com/766b/chi-prometheus v0.0.0-20180509160047-46ac2b31aa30 ## explicit github.com/766b/chi-prometheus @@ -48,6 +50,10 @@ github.com/brianvoe/gofakeit/v6/data github.com/cespare/xxhash/v2 # github.com/chris-ramon/douceur v0.2.0 ## explicit +# github.com/codegangsta/envy v0.0.0-20141216192214-4b78388c8ce4 +## explicit +# github.com/codegangsta/gin v0.0.0-20171026143024-cafe2ce98974 +## explicit # github.com/crewjam/httperr v0.0.0-20190612203328-a946449404da github.com/crewjam/httperr # github.com/crewjam/saml v0.4.5 @@ -192,6 +198,8 @@ github.com/markbates/goth/providers/linkedin github.com/markbates/goth/providers/openidConnect # github.com/mattermost/xml-roundtrip-validator v0.0.0-20201213122252-bcd7e1b9601e github.com/mattermost/xml-roundtrip-validator +# github.com/mattn/go-shellwords v1.0.12 +## explicit # github.com/mattn/go-sqlite3 v1.14.0 ## explicit github.com/mattn/go-sqlite3 @@ -443,6 +451,8 @@ gopkg.in/mail.v2 gopkg.in/square/go-jose.v2 gopkg.in/square/go-jose.v2/cipher gopkg.in/square/go-jose.v2/json +# gopkg.in/urfave/cli.v1 v1.20.0 +## explicit # gopkg.in/yaml.v2 v2.3.0 ## explicit gopkg.in/yaml.v2