feature (plg/extension): expand rust plugin footprint

This commit is contained in:
MickaelK
2026-09-09 00:27:33 +10:00
parent c0315003d8
commit fa36a5855d
12 changed files with 82 additions and 18 deletions
+1 -1
View File
@@ -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())
}
},
+8 -4
View File
@@ -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))
+14 -4
View File
@@ -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 {
@@ -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
}
+8
View File
@@ -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())
}
+2
View File
@@ -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]
+29
View File
@@ -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() {}
+6
View File
@@ -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;
+4 -1
View File
@@ -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
@@ -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)
}
}
@@ -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"<!DOCTYPE html><h1>Hello from a Filestash plugin</h1>");
fn index(&self, _ctx: &impl Context, _req: &impl Request, res: &mut impl Response) {
res.header("Content-Type", "text/html");
res.write(b"<h1>Hello from a Filestash plugin</h1>");
}
}
@@ -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": [