diff --git a/server/pkg/extension/adapter/http.go b/server/pkg/extension/adapter/http.go index 134f6bd9..40a08713 100644 --- a/server/pkg/extension/adapter/http.go +++ b/server/pkg/extension/adapter/http.go @@ -70,7 +70,7 @@ func (in *Instance) Http() func(*mux.Router) error { } router.HandleFunc(rt.path, middleware.NewMiddlewareChain( func(app *App, w http.ResponseWriter, r *http.Request) { - if err := in.rt.Call(r.Context(), "http", httpKey{}, &httpData{r: r, w: w, ctx: app}); err != nil { + if err := in.rt.Call(withHttp(r.Context(), app, w, r), "http", nil, nil); err != nil { utils.Log.Error("extension::adapter http path=%s err=%s", r.URL.Path, err.Error()) } }, diff --git a/server/pkg/extension/adapter/index.go b/server/pkg/extension/adapter/index.go index f3216332..2da3d715 100644 --- a/server/pkg/extension/adapter/index.go +++ b/server/pkg/extension/adapter/index.go @@ -21,6 +21,7 @@ func NewInstance(wasm []byte, permissions ...string) (*Instance, error) { } rt, err := runtime.New(wasm, append(opts, runtime.WithExports(func(b *runtime.HostModuleBuilder) { exportShared(b) + exportAuthentication(b) exportAuthorisation(b) exportMiddleware(b) exportHttp(b) @@ -43,10 +44,9 @@ type appKey struct{} type httpKey struct{} type httpData struct { - ctx *App - r *http.Request - w http.ResponseWriter - next bool + ctx *App + r *http.Request + w http.ResponseWriter } func stateHttp(ctx context.Context) *httpData { @@ -54,6 +54,10 @@ func stateHttp(ctx context.Context) *httpData { return d } +func withHttp(ctx context.Context, app *App, w http.ResponseWriter, r *http.Request) context.Context { + return context.WithValue(ctx, httpKey{}, &httpData{ctx: app, r: r, w: w}) +} + func exportShared(b *runtime.HostModuleBuilder) { b.Export("log", func(ctx context.Context, mem runtime.IMemory, level, ptr, length uint32) { msg := string(mem.Read(ptr, length)) diff --git a/server/pkg/extension/adapter/middleware.go b/server/pkg/extension/adapter/middleware.go index a2022c99..c4e2c47d 100644 --- a/server/pkg/extension/adapter/middleware.go +++ b/server/pkg/extension/adapter/middleware.go @@ -10,18 +10,28 @@ import ( "github.com/mickael-kerjean/filestash/server/pkg/utils" ) +type middlewareKey struct{} +type middlewareData struct { + next bool +} + +func stateMiddleware(ctx context.Context) *middlewareData { + d, _ := ctx.Value(middlewareKey{}).(*middlewareData) + return d +} + func exportMiddleware(b *runtime.HostModuleBuilder) { b.Export("ffi_middleware_push_next", func(ctx context.Context) { - stateHttp(ctx).next = true + stateMiddleware(ctx).next = true }) } func (in *Instance) Middleware() func(HandlerFunc) HandlerFunc { return func(next HandlerFunc) HandlerFunc { return func(app *App, w http.ResponseWriter, r *http.Request) { - hd := &httpData{r: r, w: w} - err := in.rt.Call(r.Context(), "middleware", httpKey{}, hd) - if errors.Is(err, runtime.ErrNoExport) || hd.next { + md := middlewareData{next: false} + err := in.rt.Call(withHttp(r.Context(), app, w, r), "middleware", middlewareKey{}, &md) + if errors.Is(err, runtime.ErrNoExport) || md.next { next(app, w, r) return } else if err != nil { diff --git a/server/pkg/extension/adapter/runtime/runtime.go b/server/pkg/extension/adapter/runtime/runtime.go index 076ce3a2..cc10a1bb 100644 --- a/server/pkg/extension/adapter/runtime/runtime.go +++ b/server/pkg/extension/adapter/runtime/runtime.go @@ -70,8 +70,10 @@ func (r *Runtime) Call(ctx context.Context, fnName string, key, val any) error { } if fn == nil { return fmt.Errorf("%w: %s", ErrNoExport, fnName) + } else if key != nil { + ctx = context.WithValue(ctx, key, val) } - _, err := fn.Call(context.WithValue(ctx, key, val)) + _, err := fn.Call(ctx) return err } diff --git a/server/pkg/extension/discovery.go b/server/pkg/extension/discovery.go index 76bc6232..51dda0ee 100644 --- a/server/pkg/extension/discovery.go +++ b/server/pkg/extension/discovery.go @@ -62,6 +62,14 @@ func Discovery() error { if err != nil { return err } + if in.Provides("authentication") { + impl := in.Authentication() + for _, elmnt := range impl.Setup().Elmnts { + if elmnt.Name == "type" { + Hooks.Register.AuthenticationMiddleware(fmt.Sprintf("%s", elmnt.Value), impl) + } + } + } if in.Provides("authorisation") { Hooks.Register.AuthorisationMiddleware(in.Authorisation()) } diff --git a/server/pkg/extension/sdk/rust/Cargo.toml b/server/pkg/extension/sdk/rust/Cargo.toml index d3ad91ba..a648b999 100644 --- a/server/pkg/extension/sdk/rust/Cargo.toml +++ b/server/pkg/extension/sdk/rust/Cargo.toml @@ -5,6 +5,8 @@ edition = "2021" [dependencies] log = "0.4" +serde = { version = "1", features = ["derive"] } +serde_json = "1" mockall = { version = "0.13", optional = true } [features] diff --git a/server/pkg/extension/sdk/rust/src/app.rs b/server/pkg/extension/sdk/rust/src/app.rs index e299f9be..03c2dde3 100644 --- a/server/pkg/extension/sdk/rust/src/app.rs +++ b/server/pkg/extension/sdk/rust/src/app.rs @@ -61,6 +61,35 @@ macro_rules! register { } }; + (@capability $anchor:ident, $app:ident, Authentication) => { + #[no_mangle] + pub extern "C" fn capability_authentication() {} + #[no_mangle] + pub extern "C" fn authentication_setup() { + $crate::authentication::authentication_push_setup( + &<$app as $crate::Authentication>::setup(), + ); + } + #[no_mangle] + pub extern "C" fn authentication_entrypoint() { + let idp = $crate::authentication::authentication_pull_idp(); + let mut res = $crate::ResponseImpl; + if let Err(err) = <$app as $crate::Authentication>::entrypoint(idp, &$crate::RequestImpl, &mut res) { + $crate::authentication::authentication_push_error(&err); + } + } + #[no_mangle] + pub extern "C" fn authentication_callback() { + let form = $crate::authentication::authentication_pull_form(); + let idp = $crate::authentication::authentication_pull_idp(); + let mut res = $crate::ResponseImpl; + match <$app as $crate::Authentication>::callback(form, idp, &mut res) { + Ok(session) => $crate::authentication::authentication_push_session(&session), + Err(err) => $crate::authentication::authentication_push_error(&err), + } + } + }; + (@capability $anchor:ident, $app:ident, Http) => { #[no_mangle] pub extern "C" fn capability_http() {} diff --git a/server/pkg/extension/sdk/rust/src/lib.rs b/server/pkg/extension/sdk/rust/src/lib.rs index 628ecc0e..3a434eb1 100644 --- a/server/pkg/extension/sdk/rust/src/lib.rs +++ b/server/pkg/extension/sdk/rust/src/lib.rs @@ -1,5 +1,6 @@ mod app; pub mod authorisation; +pub mod authentication; mod context; mod ffi; mod http; @@ -8,14 +9,19 @@ pub mod logger; mod middleware; mod request; mod response; +mod form; +mod error; pub use authorisation::{Authorisation, Decision}; +pub use authentication::{Authentication}; pub use http::{Handler, Http, Router}; pub use lifecycle::{Lifecycle, OnChanges, OnDestroy, OnInit}; pub use middleware::{Middleware, Next}; pub use request::{Request, RequestImpl}; pub use response::{Response, ResponseImpl}; pub use context::{Context, ContextImpl}; +pub use form::{Form}; +pub use error::{Error}; pub use log; diff --git a/server/plugin/plg_runtime_cookbook/Makefile b/server/plugin/plg_runtime_cookbook/Makefile index 16a30afd..736466d6 100644 --- a/server/plugin/plg_runtime_cookbook/Makefile +++ b/server/plugin/plg_runtime_cookbook/Makefile @@ -8,6 +8,7 @@ all: $(MAKE) -C permission_sock $(MAKE) -C permission_fs $(MAKE) -C authorisation + $(MAKE) -C authentication zip -j cookbook.zip manifest.json \ http/$(RELEASE)/endpoint.wasm \ onInit/$(RELEASE)/oninit.wasm \ @@ -15,7 +16,8 @@ all: onQuit/$(RELEASE)/onquit.wasm \ permission_sock/$(RELEASE)/socket.wasm \ permission_fs/$(RELEASE)/filesystem.wasm \ - authorisation/$(RELEASE)/authorisation.wasm + authorisation/$(RELEASE)/authorisation.wasm \ + authentication/$(RELEASE)/authentication.wasm mv cookbook.zip ../../../dist/data/state/plugins/ init: @@ -30,3 +32,4 @@ clean: $(MAKE) -C permission_sock clean $(MAKE) -C permission_fs clean $(MAKE) -C authorisation clean + $(MAKE) -C authentication clean diff --git a/server/plugin/plg_runtime_cookbook/authorisation/lib.rs b/server/plugin/plg_runtime_cookbook/authorisation/lib.rs index 4eb55fb9..048a2bb2 100644 --- a/server/plugin/plg_runtime_cookbook/authorisation/lib.rs +++ b/server/plugin/plg_runtime_cookbook/authorisation/lib.rs @@ -4,13 +4,13 @@ use filestash::*; pub struct Plugin; impl Authorisation for Plugin { - fn ls(&self, _ctx: &Context, path: &str) -> Decision { + fn ls(&self, _ctx: &impl Context, path: &str) -> Decision { self.check(path) } - fn cat(&self, _ctx: &Context, path: &str) -> Decision { + fn cat(&self, _ctx: &impl Context, path: &str) -> Decision { self.check(path) } - fn stat(&self, _ctx: &Context, path: &str) -> Decision { + fn stat(&self, _ctx: &impl Context, path: &str) -> Decision { self.check(path) } } diff --git a/server/plugin/plg_runtime_cookbook/http/lib.rs b/server/plugin/plg_runtime_cookbook/http/lib.rs index 09e8c663..15b47713 100644 --- a/server/plugin/plg_runtime_cookbook/http/lib.rs +++ b/server/plugin/plg_runtime_cookbook/http/lib.rs @@ -10,10 +10,9 @@ impl Http for Page { } impl Page { - fn index(&self, _req: &Request, res: &mut Response) { - res.header("Content-Type", "text/html; charset=utf-8") - .status(200) - .write(b"

Hello from a Filestash plugin

"); + fn index(&self, _ctx: &impl Context, _req: &impl Request, res: &mut impl Response) { + res.header("Content-Type", "text/html"); + res.write(b"

Hello from a Filestash plugin

"); } } diff --git a/server/plugin/plg_runtime_cookbook/manifest.json b/server/plugin/plg_runtime_cookbook/manifest.json index 0467d843..66eeff1c 100644 --- a/server/plugin/plg_runtime_cookbook/manifest.json +++ b/server/plugin/plg_runtime_cookbook/manifest.json @@ -8,6 +8,7 @@ { "type": "abi", "entrypoint": "onquit.wasm" }, { "type": "abi", "entrypoint": "socket.wasm" }, { "type": "abi", "entrypoint": "filesystem.wasm" }, + { "type": "abi", "entrypoint": "authentication.wasm" }, { "type": "abi", "entrypoint": "authorisation.wasm" } ], "permissions": [