From 7c8eb90505b47d8992bb509797b6808d7287334b Mon Sep 17 00:00:00 2001 From: Gianmaria Del Monte <39946305+gmgigi96@users.noreply.github.com> Date: Thu, 20 Jul 2023 12:57:10 +0200 Subject: [PATCH] Plugins (#4073) --- Makefile | 4 +- changelog/unreleased/plugins.md | 7 + cmd/revad/main.go | 4 +- .../registry.go => cmd/revad/main/main.go | 12 +- docker/Dockerfile.revad | 2 +- docker/Dockerfile.revad-eos | 2 +- docs/content/en/docs/concepts/plugins.md | 126 ----------- examples/plugin/json/json.go | 174 -------------- examples/plugin/plugin.toml | 15 -- examples/plugin/users.demo.json | 103 --------- go.mod | 6 +- go.sum | 8 - .../applicationauth/applicationauth.go | 7 + .../grpc/services/appprovider/appprovider.go | 7 + .../grpc/services/appregistry/appregistry.go | 7 + .../services/authprovider/authprovider.go | 37 +-- .../services/authregistry/authregistry.go | 7 + internal/grpc/services/datatx/datatx.go | 7 + .../services/groupprovider/groupprovider.go | 7 + internal/grpc/services/ocmcore/ocmcore.go | 7 + .../ocminvitemanager/ocminvitemanager.go | 6 + .../ocmproviderauthorizer.go | 7 + .../ocmshareprovider/ocmshareprovider.go | 6 + .../grpc/services/permissions/permissions.go | 7 + .../grpc/services/preferences/preferences.go | 7 + .../publicshareprovider.go | 7 + .../storageprovider/storageprovider.go | 6 + .../storageregistry/storageregistry.go | 7 + .../services/userprovider/userprovider.go | 41 +--- .../usershareprovider/usershareprovider.go | 7 + internal/http/interceptors/loader/loader.go | 1 + internal/http/interceptors/plugins/plugins.go | 33 +++ internal/http/services/loader/loader.go | 1 + internal/http/services/plugins/plugins.go | 33 +++ internal/serverless/services/loader/loader.go | 1 + .../serverless/services/plugins/plugins.go | 33 +++ pkg/auth/auth.go | 2 - pkg/auth/rpc_auth.go | 118 ---------- pkg/plugin/loader.go | 127 ----------- pkg/plugin/plugin.go | 44 +++- pkg/user/rpc_user.go | 214 ------------------ pkg/user/user.go | 2 - pkg/utils/utils.go | 14 ++ plugins.go | 141 ++++++++++++ plugins_test.go | 149 ++++++++++++ 45 files changed, 593 insertions(+), 970 deletions(-) create mode 100644 changelog/unreleased/plugins.md rename pkg/plugin/registry.go => cmd/revad/main/main.go (75%) delete mode 100644 docs/content/en/docs/concepts/plugins.md delete mode 100644 examples/plugin/json/json.go delete mode 100644 examples/plugin/plugin.toml delete mode 100644 examples/plugin/users.demo.json create mode 100644 internal/http/interceptors/plugins/plugins.go create mode 100644 internal/http/services/plugins/plugins.go create mode 100644 internal/serverless/services/plugins/plugins.go delete mode 100644 pkg/auth/rpc_auth.go delete mode 100644 pkg/plugin/loader.go delete mode 100644 pkg/user/rpc_user.go create mode 100644 plugins.go create mode 100644 plugins_test.go diff --git a/Makefile b/Makefile index 234b872e43..c362c97be3 100644 --- a/Makefile +++ b/Makefile @@ -39,11 +39,11 @@ BUILD_FLAGS = "`[[ -z "$(STATIC)" ]] && echo "" || echo "-extldflags=-static"` - .PHONY: revad revad: - go build -ldflags $(BUILD_FLAGS) -o ./cmd/revad/revad ./cmd/revad + go build -ldflags $(BUILD_FLAGS) -o ./cmd/revad/revad ./cmd/revad/main .PHONY: revad-ceph revad-ceph: - go build -ldflags $(BUILD_FLAGS) -tags ceph -o ./cmd/revad/revad ./cmd/revad + go build -ldflags $(BUILD_FLAGS) -tags ceph -o ./cmd/revad/revad ./cmd/revad/main .PHONY: reva reva: diff --git a/changelog/unreleased/plugins.md b/changelog/unreleased/plugins.md new file mode 100644 index 0000000000..41564246ca --- /dev/null +++ b/changelog/unreleased/plugins.md @@ -0,0 +1,7 @@ +Enhancement: Plugins + +Adds a plugin system for allowing the creation of external +plugins for different plugable components in reva, +for example grpc drivers, http services and middlewares. + +https://github.com/cs3org/reva/pull/4073 diff --git a/cmd/revad/main.go b/cmd/revad/main.go index aad02a6baf..8041e19e9b 100644 --- a/cmd/revad/main.go +++ b/cmd/revad/main.go @@ -16,7 +16,7 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -package main +package revadcmd import ( "flag" @@ -55,7 +55,7 @@ var ( revaProcs []*runtime.Reva ) -func main() { +func Main() { flag.Parse() // initialize the global system information diff --git a/pkg/plugin/registry.go b/cmd/revad/main/main.go similarity index 75% rename from pkg/plugin/registry.go rename to cmd/revad/main/main.go index fd96c19d1d..aab4599df4 100644 --- a/pkg/plugin/registry.go +++ b/cmd/revad/main/main.go @@ -16,14 +16,10 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -package plugin +package main -import "github.com/hashicorp/go-plugin" +import revadcmd "github.com/cs3org/reva/cmd/revad" -// PluginMap is a map containing all the plugins. -var PluginMap = map[string]plugin.Plugin{} - -// Register registers the plugin. -func Register(name string, plugin plugin.Plugin) { - PluginMap[name] = plugin +func main() { + revadcmd.Main() } diff --git a/docker/Dockerfile.revad b/docker/Dockerfile.revad index 6550ee9506..37a121799f 100644 --- a/docker/Dockerfile.revad +++ b/docker/Dockerfile.revad @@ -29,7 +29,7 @@ ENV CGO_ENABLED 1 RUN apk add --no-cache gcc musl-dev -RUN go build -ldflags "-X main.gitCommit=$GIT_COMMIT -X main.version=$VERSION -X main.goVersion=`go version | awk '{print $3}'` -X main.buildDate=`date +%FT%T%z`" -o ./cmd/revad/revad ./cmd/revad +RUN go build -ldflags "-X main.gitCommit=$GIT_COMMIT -X main.version=$VERSION -X main.goVersion=`go version | awk '{print $3}'` -X main.buildDate=`date +%FT%T%z`" -o ./cmd/revad/revad ./cmd/revad/main FROM alpine:3.16 diff --git a/docker/Dockerfile.revad-eos b/docker/Dockerfile.revad-eos index 92a6a075b6..bbde3b230c 100644 --- a/docker/Dockerfile.revad-eos +++ b/docker/Dockerfile.revad-eos @@ -29,7 +29,7 @@ ENV CGO_ENABLED 1 RUN apk add --no-cache gcc musl-dev -RUN go build -ldflags "-X main.gitCommit=$GIT_COMMIT -X main.version=$VERSION -X main.goVersion=`go version | awk '{print $3}'` -X main.buildDate=`date +%FT%T%z`" -o ./cmd/revad/revad ./cmd/revad +RUN go build -ldflags "-X main.gitCommit=$GIT_COMMIT -X main.version=$VERSION -X main.goVersion=`go version | awk '{print $3}'` -X main.buildDate=`date +%FT%T%z`" -o ./cmd/revad/revad ./cmd/revad/main FROM gitlab-registry.cern.ch/dss/eos/eos-all:4.8.91 diff --git a/docs/content/en/docs/concepts/plugins.md b/docs/content/en/docs/concepts/plugins.md deleted file mode 100644 index f9638a3544..0000000000 --- a/docs/content/en/docs/concepts/plugins.md +++ /dev/null @@ -1,126 +0,0 @@ ---- -title: "Runtime Plugins" -linkTitle: "Runtime Plugins" -weight: 10 -description: > - Guide for developing runtime plugin drivers for Reva ---- - -## Reva Plugins - -Reva Plugins allow new functionality to be added to Reva without modifying the core source code. Reva Plugins are able to add new custom drivers for various services to Reva and also at the same time enable loading existing plugins at runtime. - -This page serves as a guide for understanding and developing Reva plugins. - -### How Plugins Work - -A Reva plugin represents a *service driver*. - -Reva plugins are completely seperate, standalone applications that the core of Reva starts and communicates with. These plugin applications aren't meant to be run manually. Instead, Reva launches and communicates with them. - -Reva uses hashicorp's [go-plugin](https://github.com/hashicorp/go-plugin) framework to implement the plugin architecture. The plugin processes communicate with the Core using the go-plugin framework, which internally uses RPCs for the communication. The Reva core itself is responsible for launching and cleaning up the plugin processes. - -### Developing Plugins - -Reva plugins must be written in [Go](https://golang.org/), so you should be familiar with the language. - -As mentioned earlier, the components that can be created and used in a Reva Plugin are service drivers. These drivers can belong to any of the Reva service, eg: Userprovider, Storageprovider, Authprovider etc. Each service exposes an [interface](https://golang.org/doc/effective_go#interfaces_and_types) for the plugin to implement. - -All you need to do to create a plugin is: - -1. Create an implementation of the desired interface: A Plugin should implement the interface exposed by the corresponding service. Eg: [Userprovider](https://github.com/cs3org/reva/blob/master/pkg/user/user.go#L67) interface. -2. Serve the plugin using the [go-plugin's](https://github.com/hashicorp/go-plugin) `plugin.Serve` method. - -The core handles all of the communication details and go-plugin implementations inside the server. - -Your plugin must use the packages from the Reva core to implement the interfaces. You're encouraged to use whatever other packages you want in your plugin implementation. Because plugins are their own processes, there is no danger of colliding dependencies. - -- `github.com/cs3org/reva/pkg/`: Contains the interface that you have to implement for any give plugin. -- `github.com/hashicorp/go-plugin`: To serve the plugin over RPC. This handles all the inter-process communication. - -Basic example of serving your component is shown below. This example consists of a simple `JSON` plugin driver for the [Userprovider](https://github.com/cs3org/reva/blob/master/internal/grpc/services/userprovider/userprovider.go) service. You can find the example code [here](https://github.com/cs3org/reva/blob/master/examples/plugin/json/json.go). - -```go - -// main.go - -import ( - "github.com/cs3org/reva/pkg/user" - "github.com/hashicorp/go-plugin" - revaPlugin "github.com/cs3org/reva/pkg/plugin" -) - -// Assume this implements the user.Manager interface -type Manager struct{} - -func main() { - // plugin.Serve serves the implementation over RPC to the core - plugin.Serve(&plugin.ServeConfig{ - HandshakeConfig: revaPlugin.Handshake, - Plugins: map[string]plugin.Plugin{ - "userprovider": &user.ProviderPlugin{Impl: &Manager{}}, - }, - }) -} - -``` -The `plugin.Serve` method handles all the details of communicating with Reva core and serving your component over RPC. As long as your struct implements one of the exposed interfaces, Reva will be able to launch your plugin and use it. - -The `plugin.Serve` method takes in the plugin configuration, which you would have to define in your plugin source code: - -- `HandshakeConfig`: The handshake is defined in `github.com/cs3org/reva/pkg/plugin` - -```go -var Handshake = plugin.HandshakeConfig{ - ProtocolVersion: 1, - MagicCookieKey: "BASIC_PLUGIN", - MagicCookieValue: "reva", -} -``` - -- `Plugins`: Plugins is a map which maps the plugin implementation with the plugin name. Currently Reva supports 2 types of plugins (support for more to be added soon): - - `userprovider` - - `authprovider` - -The implementation should be mapped to the above provided plugin names. - - - -### Configuring and Loading Plugins - -Runtime plugin can be configured using `.toml` files. We have ensured backwards compatibilty, hence configuring Reva to use runtime-plugins should feel as natural as configuring in-memory plugin drivers. - -Reva provides 3 ways of loading plugins: - -1. Providing path to already compiled go binary: If you have already compiled plugin binary, you just need to provide the path to the binary and rest of the work of loading the plugin would be taken care by the reva core. - -``` -# Starting grpc userprovider service with json driver plugin -[grpc.service.userprovider] -driver = "/absolute/path/to/binary/json" - -[grpc.service.userprovider.drivers.json] -user = "user.demo.json" -``` - -2. Provide path to the plugin source code: If you want the reva core to compile the plugins and then load the binary, you need to point to the go package consisting the plugin source code: - -``` -# Starting grpc userprovider service with json driver plugin -[grpc.service.userprovider] -driver = "/absolute/path/to/source/json" - -[grpc.service.userprovider.drivers.json] -user = "user.demo.json" -``` - -3. Provide URL to plugin hosted on Github: If you provide Github URL, Reva would download the source code into a temporary directory, compile it into a binary and load that binary. - -``` -# Starting grpc userprovider service with json driver plugin -[grpc.service.userprovider] -driver = "https://github.com/jimil749/json" - -[grpc.service.userprovider.drivers.json] -user = "user.demo.json" -``` \ No newline at end of file diff --git a/examples/plugin/json/json.go b/examples/plugin/json/json.go deleted file mode 100644 index 7ebcc6f930..0000000000 --- a/examples/plugin/json/json.go +++ /dev/null @@ -1,174 +0,0 @@ -// Copyright 2018-2023 CERN -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// In applying this license, CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -package main - -import ( - "context" - "encoding/json" - "errors" - "os" - "strings" - - userpb "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1" - "github.com/cs3org/reva/pkg/errtypes" - "github.com/cs3org/reva/pkg/user" - "github.com/hashicorp/go-plugin" - "github.com/mitchellh/mapstructure" -) - -// Manager is a real implementation of Manager interface. -type Manager struct { - users []*userpb.User -} - -type config struct { - Users string `mapstructure:"users"` -} - -func (c *config) init() { - if c.Users == "" { - c.Users = "/etc/revad/users.json" - } -} - -func parseConfig(m map[string]interface{}) (*config, error) { - c := &config{} - if err := mapstructure.Decode(m, c); err != nil { - return nil, err - } - c.init() - return c, nil -} - -// Configure initializes the manager struct based on the configurations. -func (m *Manager) Configure(ml map[string]interface{}) error { - c, err := parseConfig(ml) - if err != nil { - return err - } - - f, err := os.ReadFile(c.Users) - if err != nil { - return err - } - - users := []*userpb.User{} - - err = json.Unmarshal(f, &users) - if err != nil { - return err - } - - m.users = users - - return nil -} - -// GetUser returns the user based on the uid. -func (m *Manager) GetUser(ctx context.Context, uid *userpb.UserId, skipFetchingGroups bool) (*userpb.User, error) { - for _, u := range m.users { - if (u.Id.GetOpaqueId() == uid.OpaqueId || u.Username == uid.OpaqueId) && (uid.Idp == "" || uid.Idp == u.Id.GetIdp()) { - user := *u - if skipFetchingGroups { - user.Groups = nil - } - return &user, nil - } - } - return nil, nil -} - -// GetUserByClaim returns user based on the claim -func (m *Manager) GetUserByClaim(ctx context.Context, claim, value string, skipFetchingGroups bool) (*userpb.User, error) { - for _, u := range m.users { - if userClaim, err := extractClaim(u, claim); err == nil && value == userClaim { - user := *u - if skipFetchingGroups { - user.Groups = nil - } - return &user, nil - } - } - return nil, errtypes.NotFound(value) -} - -func extractClaim(u *userpb.User, claim string) (string, error) { - switch claim { - case "mail": - return u.Mail, nil - case "username": - return u.Username, nil - case "uid": - if u.Opaque != nil && u.Opaque.Map != nil { - if uidObj, ok := u.Opaque.Map["uid"]; ok { - if uidObj.Decoder == "plain" { - return string(uidObj.Value), nil - } - } - } - } - return "", errors.New("json: invalid field") -} - -// TODO(jfd) search Opaque? compare sub? -func userContains(u *userpb.User, query string) bool { - query = strings.ToLower(query) - return strings.Contains(strings.ToLower(u.Username), query) || strings.Contains(strings.ToLower(u.DisplayName), query) || - strings.Contains(strings.ToLower(u.Mail), query) || strings.Contains(strings.ToLower(u.Id.OpaqueId), query) -} - -// FindUsers returns the user based on the query -func (m *Manager) FindUsers(ctx context.Context, query string, skipFetchingGroups bool) ([]*userpb.User, error) { - users := []*userpb.User{} - for _, u := range m.users { - if userContains(u, query) { - user := *u - if skipFetchingGroups { - user.Groups = nil - } - users = append(users, &user) - } - } - return users, nil -} - -// GetUserGroups returns the user groups -func (m *Manager) GetUserGroups(ctx context.Context, uid *userpb.UserId) ([]string, error) { - user, err := m.GetUser(ctx, uid, false) - if err != nil { - return nil, err - } - return user.Groups, nil -} - -// Handshake hashicorp go-plugin handshake -var Handshake = plugin.HandshakeConfig{ - ProtocolVersion: 1, - MagicCookieKey: "BASIC_PLUGIN", - MagicCookieValue: "hello", -} - -func main() { - plugin.Serve(&plugin.ServeConfig{ - HandshakeConfig: Handshake, - Plugins: map[string]plugin.Plugin{ - "userprovider": &user.ProviderPlugin{Impl: &Manager{}}, - }, - }) -} diff --git a/examples/plugin/plugin.toml b/examples/plugin/plugin.toml deleted file mode 100644 index 28bc78198c..0000000000 --- a/examples/plugin/plugin.toml +++ /dev/null @@ -1,15 +0,0 @@ -[shared] -gatewaysvc = "localhost:19000" -plugin = true - -[grpc] -address = "0.0.0.0:19000" - -[grpc.services.gateway] -userprovidersvc = "localhost:19000" - -[grpc.services.userprovider] -driver = "./json" - -[grpc.services.userprovider.drivers.json] -users = "users.demo.json" diff --git a/examples/plugin/users.demo.json b/examples/plugin/users.demo.json deleted file mode 100644 index 99a04a2554..0000000000 --- a/examples/plugin/users.demo.json +++ /dev/null @@ -1,103 +0,0 @@ -[ - { - "id": { - "opaque_id": "4c510ada-c86b-4815-8820-42cdf82c3d51", - "idp": "https://cernbox.cern.ch", - "type": 1 - }, - "username": "einstein", - "secret": "relativity", - "mail": "einstein@cern.ch", - "display_name": "Albert Einstein", - "groups": ["sailing-lovers", "violin-haters", "physics-lovers"], - "opaque": { - "map": { - "gid": { - "_comment": "decodes to 987", - "decoder":"plain", - "value":"OTg3" - }, - "uid":{ - "_comment": "decodes to 123", - "decoder":"plain", - "value":"MTIz" - } - } - } - }, - { - "id": { - "opaque_id": "f7fbf8c8-139b-4376-b307-cf0a8c2d0d9c", - "idp": "cesnet.cz" - }, - "username": "marie", - "secret": "radioactivity", - "mail": "marie@cesnet.cz", - "display_name": "Marie Curie", - "groups": ["radium-lovers", "polonium-lovers", "physics-lovers"], - "opaque": { - "map": { - "gid": { - "_comment": "decodes to 987", - "decoder":"plain", - "value":"OTg3" - }, - "uid":{ - "_comment": "decodes to 456", - "decoder":"plain", - "value":"NDU2" - } - } - } - }, - { - "id": { - "opaque_id": "932b4540-8d16-481e-8ef4-588e4b6b151c", - "idp": "example.org" - }, - "username": "richard", - "secret": "superfluidity", - "mail": "richard@example.org", - "display_name": "Richard Feynman", - "groups": ["quantum-lovers", "philosophy-haters", "physics-lovers"], - "opaque": { - "map": { - "gid": { - "_comment": "decodes to 135", - "decoder":"plain", - "value":"MTM1" - }, - "uid":{ - "_comment": "decodes to 246", - "decoder":"plain", - "value":"MjQ2" - } - } - } - }, - { - "id": { - "opaque_id": "932b4522-139b-4815-8ef4-42cdf82c3d51", - "idp": "example.com" - }, - "username": "test", - "secret": "test", - "mail": "test@example.com", - "display_name": "Test Testman", - "groups": ["quantum-lovers", "philosophy-haters", "physics-lovers"], - "opaque": { - "map": { - "gid": { - "_comment": "decodes to 135", - "decoder":"plain", - "value":"MTM1" - }, - "uid":{ - "_comment": "decodes to 468", - "decoder":"plain", - "value":"NDY4" - } - } - } - } -] diff --git a/go.mod b/go.mod index 6e25780e1f..f0fe32ae89 100644 --- a/go.mod +++ b/go.mod @@ -35,8 +35,6 @@ require ( github.com/google/go-cmp v0.5.9 github.com/google/uuid v1.3.0 github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 - github.com/hashicorp/go-hclog v1.5.0 - github.com/hashicorp/go-plugin v1.4.9 github.com/jedib0t/go-pretty v4.3.0+incompatible github.com/juliangruber/go-intersect v1.1.0 github.com/mattn/go-sqlite3 v1.14.10 @@ -110,12 +108,12 @@ require ( github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/google/flatbuffers v2.0.6+incompatible // indirect + github.com/hashicorp/go-hclog v1.5.0 // indirect github.com/hashicorp/go-immutable-radix v1.0.0 // indirect github.com/hashicorp/go-msgpack v1.1.5 // indirect github.com/hashicorp/go-msgpack/v2 v2.1.0 // indirect github.com/hashicorp/golang-lru v0.5.4 // indirect github.com/hashicorp/raft v1.4.0 // indirect - github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb // indirect github.com/huandu/xstrings v1.3.3 // indirect github.com/imdario/mergo v0.3.12 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect @@ -134,7 +132,6 @@ require ( github.com/minio/md5-simd v1.1.2 // indirect github.com/minio/sha256-simd v1.0.0 // indirect github.com/mitchellh/copystructure v1.2.0 // indirect - github.com/mitchellh/go-testing-interface v1.14.1 // indirect github.com/mitchellh/hashstructure v1.1.0 // indirect github.com/mitchellh/reflectwalk v1.0.2 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect @@ -144,7 +141,6 @@ require ( github.com/nats-io/nuid v1.0.1 // indirect github.com/nats-io/stan.go v0.10.4 // indirect github.com/nxadm/tail v1.4.8 // indirect - github.com/oklog/run v1.1.0 // indirect github.com/oklog/ulid v1.3.1 // indirect github.com/oliveagle/jsonpath v0.0.0-20180606110733-2e52cf6e6852 // indirect github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c // indirect diff --git a/go.sum b/go.sum index e3848a5c97..0f60a32e0a 100644 --- a/go.sum +++ b/go.sum @@ -652,8 +652,6 @@ github.com/hashicorp/go-msgpack v1.1.5/go.mod h1:gWVc3sv/wbDmR3rQsj1CAktEZzoz1YN github.com/hashicorp/go-msgpack/v2 v2.1.0 h1:J2g2hMyjSefUPTnkLRU2MnsLLsPRB1n4Z/wJRN07GuA= github.com/hashicorp/go-msgpack/v2 v2.1.0/go.mod h1:Tv81cKI2JmHZDjmzEmc1n+8h1DO5k+3pG6BPlNMQds0= github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= -github.com/hashicorp/go-plugin v1.4.9 h1:ESiK220/qE0aGxWdzKIvRH69iLiuN/PjoLTm69RoWtU= -github.com/hashicorp/go-plugin v1.4.9/go.mod h1:viDMjcLJuDui6pXb8U4HVfb8AamCWhHGUjr2IrTF67s= github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= github.com/hashicorp/go-retryablehttp v0.6.6/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY= github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= @@ -678,8 +676,6 @@ github.com/hashicorp/memberlist v0.3.1/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOn github.com/hashicorp/raft v1.4.0 h1:tn28S/AWv0BtRQgwZv/1NELu8sCvI0FixqL8C8MYKeY= github.com/hashicorp/raft v1.4.0/go.mod h1:nz64BIjXphDLATfKGG5RzHtNUPioLeKFsXEm88yTVew= github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= -github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb h1:b5rjCoWHc7eqmAS4/qyk21ZsHyb6Mxv/jykxvNTkU4M= -github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/huandu/xstrings v1.3.3 h1:/Gcsuc1x8JVbJ9/rlye4xZnVAbEkGauT8lbebqcQws4= github.com/huandu/xstrings v1.3.3/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= @@ -698,7 +694,6 @@ github.com/jedib0t/go-pretty v4.3.0+incompatible h1:CGs8AVhEKg/n9YbUenWmNStRW2PH github.com/jedib0t/go-pretty v4.3.0+incompatible/go.mod h1:XemHduiw8R651AF9Pt4FwCTKeG3oo7hrHJAoznj9nag= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4= -github.com/jhump/protoreflect v1.6.0 h1:h5jfMVslIg6l29nsMs0D8Wj17RDVdNYti0vDN/PZZoE= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= @@ -844,8 +839,6 @@ github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HK github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= -github.com/mitchellh/go-testing-interface v1.14.1 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJJ2JqpQmpLJOu07cU= -github.com/mitchellh/go-testing-interface v1.14.1/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8= github.com/mitchellh/go-vnc v0.0.0-20150629162542-723ed9867aed/go.mod h1:3rdaFaCv4AyBgu5ALFM0+tSuHrBh6v692nyQe3ikrq0= github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= @@ -912,7 +905,6 @@ github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs= github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= -github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA= github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU= github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= diff --git a/internal/grpc/services/applicationauth/applicationauth.go b/internal/grpc/services/applicationauth/applicationauth.go index 387a75904e..2ccea16f5f 100644 --- a/internal/grpc/services/applicationauth/applicationauth.go +++ b/internal/grpc/services/applicationauth/applicationauth.go @@ -25,14 +25,21 @@ import ( "github.com/cs3org/reva/pkg/appauth" "github.com/cs3org/reva/pkg/appauth/manager/registry" "github.com/cs3org/reva/pkg/errtypes" + "github.com/cs3org/reva/pkg/plugin" "github.com/cs3org/reva/pkg/rgrpc" "github.com/cs3org/reva/pkg/rgrpc/status" + "github.com/cs3org/reva/pkg/utils" "github.com/cs3org/reva/pkg/utils/cfg" "google.golang.org/grpc" ) func init() { rgrpc.Register("applicationauth", New) + plugin.RegisterNamespace("grpc.services.applicationauth.drivers", func(name string, newFunc any) { + var f registry.NewFunc + utils.Cast(newFunc, &f) + registry.Register(name, f) + }) } type config struct { diff --git a/internal/grpc/services/appprovider/appprovider.go b/internal/grpc/services/appprovider/appprovider.go index 53f5b89a27..7028c4f4d2 100644 --- a/internal/grpc/services/appprovider/appprovider.go +++ b/internal/grpc/services/appprovider/appprovider.go @@ -36,10 +36,12 @@ import ( "github.com/cs3org/reva/pkg/appctx" "github.com/cs3org/reva/pkg/errtypes" "github.com/cs3org/reva/pkg/mime" + "github.com/cs3org/reva/pkg/plugin" "github.com/cs3org/reva/pkg/rgrpc" "github.com/cs3org/reva/pkg/rgrpc/status" "github.com/cs3org/reva/pkg/rgrpc/todo/pool" "github.com/cs3org/reva/pkg/sharedconf" + "github.com/cs3org/reva/pkg/utils" "github.com/cs3org/reva/pkg/utils/cfg" "github.com/juliangruber/go-intersect" "google.golang.org/grpc" @@ -47,6 +49,11 @@ import ( func init() { rgrpc.Register("appprovider", New) + plugin.RegisterNamespace("grpc.services.appprovider.drivers", func(name string, newFunc any) { + var f registry.NewFunc + utils.Cast(newFunc, &f) + registry.Register(name, f) + }) } type service struct { diff --git a/internal/grpc/services/appregistry/appregistry.go b/internal/grpc/services/appregistry/appregistry.go index 0acea9c12f..68d9f00f1a 100644 --- a/internal/grpc/services/appregistry/appregistry.go +++ b/internal/grpc/services/appregistry/appregistry.go @@ -25,14 +25,21 @@ import ( "github.com/cs3org/reva/pkg/app" "github.com/cs3org/reva/pkg/app/registry/registry" "github.com/cs3org/reva/pkg/errtypes" + "github.com/cs3org/reva/pkg/plugin" "github.com/cs3org/reva/pkg/rgrpc" "github.com/cs3org/reva/pkg/rgrpc/status" + "github.com/cs3org/reva/pkg/utils" "github.com/cs3org/reva/pkg/utils/cfg" "google.golang.org/grpc" ) func init() { rgrpc.Register("appregistry", New) + plugin.RegisterNamespace("grpc.services.appregistry.drivers", func(name string, newFunc any) { + var f registry.NewFunc + utils.Cast(newFunc, &f) + registry.Register(name, f) + }) } type svc struct { diff --git a/internal/grpc/services/authprovider/authprovider.go b/internal/grpc/services/authprovider/authprovider.go index 71427f34cc..fdb6937014 100644 --- a/internal/grpc/services/authprovider/authprovider.go +++ b/internal/grpc/services/authprovider/authprovider.go @@ -21,14 +21,12 @@ package authprovider import ( "context" "fmt" - "path/filepath" provider "github.com/cs3org/go-cs3apis/cs3/auth/provider/v1beta1" "github.com/cs3org/reva/pkg/appctx" "github.com/cs3org/reva/pkg/auth" "github.com/cs3org/reva/pkg/auth/manager/registry" "github.com/cs3org/reva/pkg/errtypes" - "github.com/cs3org/reva/pkg/plugin" "github.com/cs3org/reva/pkg/rgrpc" "github.com/cs3org/reva/pkg/rgrpc/status" "github.com/cs3org/reva/pkg/sharedconf" @@ -58,35 +56,18 @@ func (c *config) ApplyDefaults() { type service struct { authmgr auth.Manager conf *config - plugin *plugin.RevaPlugin blockedUsers user.BlockedUsers } -func getAuthManager(ctx context.Context, manager string, m map[string]map[string]interface{}) (auth.Manager, *plugin.RevaPlugin, error) { +func getAuthManager(ctx context.Context, manager string, m map[string]map[string]interface{}) (auth.Manager, error) { if manager == "" { - return nil, nil, errtypes.InternalError("authsvc: driver not configured for auth manager") + return nil, errtypes.InternalError("authsvc: driver not configured for auth manager") } - p, err := plugin.Load("authprovider", manager) - if err == nil { - authManager, ok := p.Plugin.(auth.Manager) - if !ok { - return nil, nil, fmt.Errorf("could not assert the loaded plugin") - } - pluginConfig := filepath.Base(manager) - err = authManager.Configure(m[pluginConfig]) - if err != nil { - return nil, nil, err - } - return authManager, p, nil - } else if _, ok := err.(errtypes.NotFound); ok { - if f, ok := registry.NewFuncs[manager]; ok { - authmgr, err := f(ctx, m[manager]) - return authmgr, nil, err - } - } else { - return nil, nil, err + if f, ok := registry.NewFuncs[manager]; ok { + authmgr, err := f(ctx, m[manager]) + return authmgr, err } - return nil, nil, errtypes.NotFound(fmt.Sprintf("authsvc: driver %s not found for auth manager", manager)) + return nil, errtypes.NotFound(fmt.Sprintf("authsvc: driver %s not found for auth manager", manager)) } // New returns a new AuthProviderServiceServer. @@ -96,7 +77,7 @@ func New(ctx context.Context, m map[string]interface{}) (rgrpc.Service, error) { return nil, err } - authManager, plug, err := getAuthManager(ctx, c.AuthManager, c.AuthManagers) + authManager, err := getAuthManager(ctx, c.AuthManager, c.AuthManagers) if err != nil { return nil, err } @@ -104,7 +85,6 @@ func New(ctx context.Context, m map[string]interface{}) (rgrpc.Service, error) { svc := &service{ conf: &c, authmgr: authManager, - plugin: plug, blockedUsers: user.NewBlockedUsersSet(c.blockedUsers), } @@ -112,9 +92,6 @@ func New(ctx context.Context, m map[string]interface{}) (rgrpc.Service, error) { } func (s *service) Close() error { - if s.plugin != nil { - s.plugin.Kill() - } return nil } diff --git a/internal/grpc/services/authregistry/authregistry.go b/internal/grpc/services/authregistry/authregistry.go index 1e6969667f..1caaaf4d54 100644 --- a/internal/grpc/services/authregistry/authregistry.go +++ b/internal/grpc/services/authregistry/authregistry.go @@ -25,14 +25,21 @@ import ( "github.com/cs3org/reva/pkg/auth" "github.com/cs3org/reva/pkg/auth/registry/registry" "github.com/cs3org/reva/pkg/errtypes" + "github.com/cs3org/reva/pkg/plugin" "github.com/cs3org/reva/pkg/rgrpc" "github.com/cs3org/reva/pkg/rgrpc/status" + "github.com/cs3org/reva/pkg/utils" "github.com/cs3org/reva/pkg/utils/cfg" "google.golang.org/grpc" ) func init() { rgrpc.Register("authregistry", New) + plugin.RegisterNamespace("grpc.services.authregistry.drivers", func(name string, newFunc any) { + var f registry.NewFunc + utils.Cast(newFunc, &f) + registry.Register(name, f) + }) } type service struct { diff --git a/internal/grpc/services/datatx/datatx.go b/internal/grpc/services/datatx/datatx.go index ef953d744d..b927c89f2e 100644 --- a/internal/grpc/services/datatx/datatx.go +++ b/internal/grpc/services/datatx/datatx.go @@ -28,8 +28,10 @@ import ( txregistry "github.com/cs3org/reva/pkg/datatx/manager/registry" repoRegistry "github.com/cs3org/reva/pkg/datatx/repository/registry" "github.com/cs3org/reva/pkg/errtypes" + "github.com/cs3org/reva/pkg/plugin" "github.com/cs3org/reva/pkg/rgrpc" "github.com/cs3org/reva/pkg/rgrpc/status" + "github.com/cs3org/reva/pkg/utils" "github.com/cs3org/reva/pkg/utils/cfg" "github.com/pkg/errors" "google.golang.org/grpc" @@ -37,6 +39,11 @@ import ( func init() { rgrpc.Register("datatx", New) + plugin.RegisterNamespace("grpc.services.datatx.drivers", func(name string, newFunc any) { + var f txregistry.NewFunc + utils.Cast(newFunc, &f) + txregistry.Register(name, f) + }) } type config struct { diff --git a/internal/grpc/services/groupprovider/groupprovider.go b/internal/grpc/services/groupprovider/groupprovider.go index 7ab63af7bc..6a268a8f16 100644 --- a/internal/grpc/services/groupprovider/groupprovider.go +++ b/internal/grpc/services/groupprovider/groupprovider.go @@ -27,8 +27,10 @@ import ( "github.com/cs3org/reva/pkg/errtypes" "github.com/cs3org/reva/pkg/group" "github.com/cs3org/reva/pkg/group/manager/registry" + "github.com/cs3org/reva/pkg/plugin" "github.com/cs3org/reva/pkg/rgrpc" "github.com/cs3org/reva/pkg/rgrpc/status" + "github.com/cs3org/reva/pkg/utils" "github.com/cs3org/reva/pkg/utils/cfg" "github.com/pkg/errors" "google.golang.org/grpc" @@ -36,6 +38,11 @@ import ( func init() { rgrpc.Register("groupprovider", New) + plugin.RegisterNamespace("grpc.services.groupprovider.drivers", func(name string, newFunc any) { + var f registry.NewFunc + utils.Cast(newFunc, &f) + registry.Register(name, f) + }) } type config struct { diff --git a/internal/grpc/services/ocmcore/ocmcore.go b/internal/grpc/services/ocmcore/ocmcore.go index 54bf50c616..77a607dd57 100644 --- a/internal/grpc/services/ocmcore/ocmcore.go +++ b/internal/grpc/services/ocmcore/ocmcore.go @@ -30,14 +30,21 @@ import ( "github.com/cs3org/reva/pkg/errtypes" "github.com/cs3org/reva/pkg/ocm/share" "github.com/cs3org/reva/pkg/ocm/share/repository/registry" + "github.com/cs3org/reva/pkg/plugin" "github.com/cs3org/reva/pkg/rgrpc" "github.com/cs3org/reva/pkg/rgrpc/status" + "github.com/cs3org/reva/pkg/utils" "github.com/cs3org/reva/pkg/utils/cfg" "google.golang.org/grpc" ) func init() { rgrpc.Register("ocmcore", New) + plugin.RegisterNamespace("grpc.services.ocmcore.drivers", func(name string, newFunc any) { + var f registry.NewFunc + utils.Cast(newFunc, &f) + registry.Register(name, f) + }) } type config struct { diff --git a/internal/grpc/services/ocminvitemanager/ocminvitemanager.go b/internal/grpc/services/ocminvitemanager/ocminvitemanager.go index 7906a38bc3..2acbecc185 100644 --- a/internal/grpc/services/ocminvitemanager/ocminvitemanager.go +++ b/internal/grpc/services/ocminvitemanager/ocminvitemanager.go @@ -31,6 +31,7 @@ import ( "github.com/cs3org/reva/pkg/ocm/client" "github.com/cs3org/reva/pkg/ocm/invite" "github.com/cs3org/reva/pkg/ocm/invite/repository/registry" + "github.com/cs3org/reva/pkg/plugin" "github.com/cs3org/reva/pkg/rgrpc" "github.com/cs3org/reva/pkg/rgrpc/status" "github.com/cs3org/reva/pkg/rgrpc/todo/pool" @@ -43,6 +44,11 @@ import ( func init() { rgrpc.Register("ocminvitemanager", New) + plugin.RegisterNamespace("grpc.services.ocminvitemanager.drivers", func(name string, newFunc any) { + var f registry.NewFunc + utils.Cast(newFunc, &f) + registry.Register(name, f) + }) } type config struct { diff --git a/internal/grpc/services/ocmproviderauthorizer/ocmproviderauthorizer.go b/internal/grpc/services/ocmproviderauthorizer/ocmproviderauthorizer.go index 18e436bee6..71d5d4cd60 100644 --- a/internal/grpc/services/ocmproviderauthorizer/ocmproviderauthorizer.go +++ b/internal/grpc/services/ocmproviderauthorizer/ocmproviderauthorizer.go @@ -26,14 +26,21 @@ import ( "github.com/cs3org/reva/pkg/errtypes" "github.com/cs3org/reva/pkg/ocm/provider" "github.com/cs3org/reva/pkg/ocm/provider/authorizer/registry" + "github.com/cs3org/reva/pkg/plugin" "github.com/cs3org/reva/pkg/rgrpc" "github.com/cs3org/reva/pkg/rgrpc/status" + "github.com/cs3org/reva/pkg/utils" "github.com/cs3org/reva/pkg/utils/cfg" "google.golang.org/grpc" ) func init() { rgrpc.Register("ocmproviderauthorizer", New) + plugin.RegisterNamespace("grpc.services.ocmproviderauthorizer.drivers", func(name string, newFunc any) { + var f registry.NewFunc + utils.Cast(newFunc, &f) + registry.Register(name, f) + }) } type config struct { diff --git a/internal/grpc/services/ocmshareprovider/ocmshareprovider.go b/internal/grpc/services/ocmshareprovider/ocmshareprovider.go index acdc56f9c5..7f45e1dfdf 100644 --- a/internal/grpc/services/ocmshareprovider/ocmshareprovider.go +++ b/internal/grpc/services/ocmshareprovider/ocmshareprovider.go @@ -40,6 +40,7 @@ import ( "github.com/cs3org/reva/pkg/ocm/client" "github.com/cs3org/reva/pkg/ocm/share" "github.com/cs3org/reva/pkg/ocm/share/repository/registry" + "github.com/cs3org/reva/pkg/plugin" "github.com/cs3org/reva/pkg/rgrpc" "github.com/cs3org/reva/pkg/rgrpc/status" "github.com/cs3org/reva/pkg/rgrpc/todo/pool" @@ -53,6 +54,11 @@ import ( func init() { rgrpc.Register("ocmshareprovider", New) + plugin.RegisterNamespace("grpc.services.ocmshareprovider.drivers", func(name string, newFunc any) { + var f registry.NewFunc + utils.Cast(newFunc, &f) + registry.Register(name, f) + }) } type config struct { diff --git a/internal/grpc/services/permissions/permissions.go b/internal/grpc/services/permissions/permissions.go index eec9c7e22f..8969192cc4 100644 --- a/internal/grpc/services/permissions/permissions.go +++ b/internal/grpc/services/permissions/permissions.go @@ -26,13 +26,20 @@ import ( rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1" "github.com/cs3org/reva/pkg/permission" "github.com/cs3org/reva/pkg/permission/manager/registry" + "github.com/cs3org/reva/pkg/plugin" "github.com/cs3org/reva/pkg/rgrpc" + "github.com/cs3org/reva/pkg/utils" "github.com/cs3org/reva/pkg/utils/cfg" "google.golang.org/grpc" ) func init() { rgrpc.Register("permissions", New) + plugin.RegisterNamespace("grpc.services.permissions.drivers", func(name string, newFunc any) { + var f registry.NewFunc + utils.Cast(newFunc, &f) + registry.Register(name, f) + }) } type config struct { diff --git a/internal/grpc/services/preferences/preferences.go b/internal/grpc/services/preferences/preferences.go index b45c6afbca..fc38b8f098 100644 --- a/internal/grpc/services/preferences/preferences.go +++ b/internal/grpc/services/preferences/preferences.go @@ -23,16 +23,23 @@ import ( preferencespb "github.com/cs3org/go-cs3apis/cs3/preferences/v1beta1" "github.com/cs3org/reva/pkg/errtypes" + "github.com/cs3org/reva/pkg/plugin" "github.com/cs3org/reva/pkg/preferences" "github.com/cs3org/reva/pkg/preferences/registry" "github.com/cs3org/reva/pkg/rgrpc" "github.com/cs3org/reva/pkg/rgrpc/status" + "github.com/cs3org/reva/pkg/utils" "github.com/cs3org/reva/pkg/utils/cfg" "google.golang.org/grpc" ) func init() { rgrpc.Register("preferences", New) + plugin.RegisterNamespace("grpc.services.preferences.drivers", func(name string, newFunc any) { + var f registry.NewFunc + utils.Cast(newFunc, &f) + registry.Register(name, f) + }) } type config struct { diff --git a/internal/grpc/services/publicshareprovider/publicshareprovider.go b/internal/grpc/services/publicshareprovider/publicshareprovider.go index 91ce7a0547..09313d2f14 100644 --- a/internal/grpc/services/publicshareprovider/publicshareprovider.go +++ b/internal/grpc/services/publicshareprovider/publicshareprovider.go @@ -27,16 +27,23 @@ import ( "github.com/cs3org/reva/pkg/appctx" ctxpkg "github.com/cs3org/reva/pkg/ctx" "github.com/cs3org/reva/pkg/errtypes" + "github.com/cs3org/reva/pkg/plugin" "github.com/cs3org/reva/pkg/publicshare" "github.com/cs3org/reva/pkg/publicshare/manager/registry" "github.com/cs3org/reva/pkg/rgrpc" "github.com/cs3org/reva/pkg/rgrpc/status" + "github.com/cs3org/reva/pkg/utils" "github.com/cs3org/reva/pkg/utils/cfg" "google.golang.org/grpc" ) func init() { rgrpc.Register("publicshareprovider", New) + plugin.RegisterNamespace("grpc.services.publicshareprovider.drivers", func(name string, newFunc any) { + var f registry.NewFunc + utils.Cast(newFunc, &f) + registry.Register(name, f) + }) } type config struct { diff --git a/internal/grpc/services/storageprovider/storageprovider.go b/internal/grpc/services/storageprovider/storageprovider.go index 0ef056cc53..8a70d81629 100644 --- a/internal/grpc/services/storageprovider/storageprovider.go +++ b/internal/grpc/services/storageprovider/storageprovider.go @@ -35,6 +35,7 @@ import ( "github.com/cs3org/reva/pkg/appctx" "github.com/cs3org/reva/pkg/errtypes" "github.com/cs3org/reva/pkg/mime" + "github.com/cs3org/reva/pkg/plugin" "github.com/cs3org/reva/pkg/rgrpc" "github.com/cs3org/reva/pkg/rgrpc/status" "github.com/cs3org/reva/pkg/rhttp/router" @@ -51,6 +52,11 @@ import ( func init() { rgrpc.Register("storageprovider", New) + plugin.RegisterNamespace("grpc.services.storageprovider.drivers", func(name string, newFunc any) { + var f registry.NewFunc + utils.Cast(newFunc, &f) + registry.Register(name, f) + }) } type config struct { diff --git a/internal/grpc/services/storageregistry/storageregistry.go b/internal/grpc/services/storageregistry/storageregistry.go index e3a4ac3454..53afa2487a 100644 --- a/internal/grpc/services/storageregistry/storageregistry.go +++ b/internal/grpc/services/storageregistry/storageregistry.go @@ -24,16 +24,23 @@ import ( registrypb "github.com/cs3org/go-cs3apis/cs3/storage/registry/v1beta1" "github.com/cs3org/reva/pkg/appctx" "github.com/cs3org/reva/pkg/errtypes" + "github.com/cs3org/reva/pkg/plugin" "github.com/cs3org/reva/pkg/rgrpc" "github.com/cs3org/reva/pkg/rgrpc/status" "github.com/cs3org/reva/pkg/storage" "github.com/cs3org/reva/pkg/storage/registry/registry" + "github.com/cs3org/reva/pkg/utils" "github.com/cs3org/reva/pkg/utils/cfg" "google.golang.org/grpc" ) func init() { rgrpc.Register("storageregistry", New) + plugin.RegisterNamespace("grpc.services.storageregistry.drivers", func(name string, newFunc any) { + var f registry.NewFunc + utils.Cast(newFunc, &f) + registry.Register(name, f) + }) } type service struct { diff --git a/internal/grpc/services/userprovider/userprovider.go b/internal/grpc/services/userprovider/userprovider.go index d34ef48318..b3df5d98f6 100644 --- a/internal/grpc/services/userprovider/userprovider.go +++ b/internal/grpc/services/userprovider/userprovider.go @@ -21,7 +21,6 @@ package userprovider import ( "context" "fmt" - "path/filepath" "sort" userpb "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1" @@ -31,6 +30,7 @@ import ( "github.com/cs3org/reva/pkg/rgrpc/status" "github.com/cs3org/reva/pkg/user" "github.com/cs3org/reva/pkg/user/manager/registry" + "github.com/cs3org/reva/pkg/utils" "github.com/cs3org/reva/pkg/utils/cfg" "github.com/pkg/errors" "google.golang.org/grpc" @@ -38,6 +38,11 @@ import ( func init() { rgrpc.Register("userprovider", New) + plugin.RegisterNamespace("grpc.services.userprovider.drivers", func(name string, newFunc any) { + var f registry.NewFunc + utils.Cast(newFunc, &f) + registry.Register(name, f) + }) } type config struct { @@ -51,29 +56,12 @@ func (c *config) ApplyDefaults() { } } -func getDriver(ctx context.Context, c *config) (user.Manager, *plugin.RevaPlugin, error) { - p, err := plugin.Load("userprovider", c.Driver) - if err == nil { - manager, ok := p.Plugin.(user.Manager) - if !ok { - return nil, nil, fmt.Errorf("could not assert the loaded plugin") - } - pluginConfig := filepath.Base(c.Driver) - err = manager.Configure(c.Drivers[pluginConfig]) - if err != nil { - return nil, nil, err - } - return manager, p, nil - } else if _, ok := err.(errtypes.NotFound); ok { - // plugin not found, fetch the driver from the in-memory registry - if f, ok := registry.NewFuncs[c.Driver]; ok { - mgr, err := f(ctx, c.Drivers[c.Driver]) - return mgr, nil, err - } - } else { - return nil, nil, err +func getDriver(ctx context.Context, c *config) (user.Manager, error) { + if f, ok := registry.NewFuncs[c.Driver]; ok { + mgr, err := f(ctx, c.Drivers[c.Driver]) + return mgr, err } - return nil, nil, errtypes.NotFound(fmt.Sprintf("driver %s not found for user manager", c.Driver)) + return nil, errtypes.NotFound(fmt.Sprintf("driver %s not found for user manager", c.Driver)) } // New returns a new UserProviderServiceServer. @@ -82,13 +70,12 @@ func New(ctx context.Context, m map[string]interface{}) (rgrpc.Service, error) { if err := cfg.Decode(m, &c); err != nil { return nil, err } - userManager, plug, err := getDriver(ctx, &c) + userManager, err := getDriver(ctx, &c) if err != nil { return nil, err } svc := &service{ usermgr: userManager, - plugin: plug, } return svc, nil @@ -96,13 +83,9 @@ func New(ctx context.Context, m map[string]interface{}) (rgrpc.Service, error) { type service struct { usermgr user.Manager - plugin *plugin.RevaPlugin } func (s *service) Close() error { - if s.plugin != nil { - s.plugin.Kill() - } return nil } diff --git a/internal/grpc/services/usershareprovider/usershareprovider.go b/internal/grpc/services/usershareprovider/usershareprovider.go index 8d37af562b..3de8bfa3c8 100644 --- a/internal/grpc/services/usershareprovider/usershareprovider.go +++ b/internal/grpc/services/usershareprovider/usershareprovider.go @@ -28,16 +28,23 @@ import ( "github.com/cs3org/reva/pkg/appctx" ctxpkg "github.com/cs3org/reva/pkg/ctx" "github.com/cs3org/reva/pkg/errtypes" + "github.com/cs3org/reva/pkg/plugin" "github.com/cs3org/reva/pkg/rgrpc" "github.com/cs3org/reva/pkg/rgrpc/status" "github.com/cs3org/reva/pkg/share" "github.com/cs3org/reva/pkg/share/manager/registry" + "github.com/cs3org/reva/pkg/utils" "github.com/cs3org/reva/pkg/utils/cfg" "google.golang.org/grpc" ) func init() { rgrpc.Register("usershareprovider", New) + plugin.RegisterNamespace("grpc.services.usershareprovider.drivers", func(name string, newFunc any) { + var f registry.NewFunc + utils.Cast(newFunc, &f) + registry.Register(name, f) + }) } type config struct { diff --git a/internal/http/interceptors/loader/loader.go b/internal/http/interceptors/loader/loader.go index f063179431..722ac23d94 100644 --- a/internal/http/interceptors/loader/loader.go +++ b/internal/http/interceptors/loader/loader.go @@ -21,5 +21,6 @@ package loader import ( // Load core HTTP middlewares. _ "github.com/cs3org/reva/internal/http/interceptors/cors" + _ "github.com/cs3org/reva/internal/http/interceptors/plugins" // Add your own middleware. ) diff --git a/internal/http/interceptors/plugins/plugins.go b/internal/http/interceptors/plugins/plugins.go new file mode 100644 index 0000000000..16284eb790 --- /dev/null +++ b/internal/http/interceptors/plugins/plugins.go @@ -0,0 +1,33 @@ +// Copyright 2018-2023 CERN +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// In applying this license, CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +package plugins + +import ( + "github.com/cs3org/reva/pkg/plugin" + "github.com/cs3org/reva/pkg/rhttp/global" + "github.com/cs3org/reva/pkg/utils" +) + +func init() { + plugin.RegisterNamespace("http.middlewares", func(name string, newFunc any) { + var f global.NewMiddleware + utils.Cast(newFunc, &f) + global.RegisterMiddleware(name, f) + }) +} diff --git a/internal/http/services/loader/loader.go b/internal/http/services/loader/loader.go index 50ad252d9a..3fc02ce9bb 100644 --- a/internal/http/services/loader/loader.go +++ b/internal/http/services/loader/loader.go @@ -31,6 +31,7 @@ import ( _ "github.com/cs3org/reva/internal/http/services/ocmprovider" _ "github.com/cs3org/reva/internal/http/services/owncloud/ocdav" _ "github.com/cs3org/reva/internal/http/services/owncloud/ocs" + _ "github.com/cs3org/reva/internal/http/services/plugins" _ "github.com/cs3org/reva/internal/http/services/preferences" _ "github.com/cs3org/reva/internal/http/services/prometheus" _ "github.com/cs3org/reva/internal/http/services/reverseproxy" diff --git a/internal/http/services/plugins/plugins.go b/internal/http/services/plugins/plugins.go new file mode 100644 index 0000000000..827216e328 --- /dev/null +++ b/internal/http/services/plugins/plugins.go @@ -0,0 +1,33 @@ +// Copyright 2018-2023 CERN +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// In applying this license, CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +package plugins + +import ( + "github.com/cs3org/reva/pkg/plugin" + "github.com/cs3org/reva/pkg/rhttp/global" + "github.com/cs3org/reva/pkg/utils" +) + +func init() { + plugin.RegisterNamespace("http.services", func(name string, newFunc any) { + var f global.NewService + utils.Cast(newFunc, &f) + global.Register(name, f) + }) +} diff --git a/internal/serverless/services/loader/loader.go b/internal/serverless/services/loader/loader.go index 71ab9afb1d..f72462f600 100644 --- a/internal/serverless/services/loader/loader.go +++ b/internal/serverless/services/loader/loader.go @@ -22,5 +22,6 @@ import ( // Load core serverless services. _ "github.com/cs3org/reva/internal/serverless/services/helloworld" _ "github.com/cs3org/reva/internal/serverless/services/notifications" + _ "github.com/cs3org/reva/internal/serverless/services/plugins" // Add your own service here. ) diff --git a/internal/serverless/services/plugins/plugins.go b/internal/serverless/services/plugins/plugins.go new file mode 100644 index 0000000000..125ea28658 --- /dev/null +++ b/internal/serverless/services/plugins/plugins.go @@ -0,0 +1,33 @@ +// Copyright 2018-2023 CERN +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// In applying this license, CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +package plugins + +import ( + "github.com/cs3org/reva/pkg/plugin" + "github.com/cs3org/reva/pkg/rserverless" + "github.com/cs3org/reva/pkg/utils" +) + +func init() { + plugin.RegisterNamespace("serverless.services", func(name string, newFunc any) { + var f rserverless.NewService + utils.Cast(newFunc, &f) + rserverless.Register(name, f) + }) +} diff --git a/pkg/auth/auth.go b/pkg/auth/auth.go index fd7a26858e..f79e959bd3 100644 --- a/pkg/auth/auth.go +++ b/pkg/auth/auth.go @@ -25,12 +25,10 @@ import ( authpb "github.com/cs3org/go-cs3apis/cs3/auth/provider/v1beta1" registry "github.com/cs3org/go-cs3apis/cs3/auth/registry/v1beta1" user "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1" - "github.com/cs3org/reva/pkg/plugin" ) // Manager is the interface to implement to authenticate users. type Manager interface { - plugin.Plugin Authenticate(ctx context.Context, clientID, clientSecret string) (*user.User, map[string]*authpb.Scope, error) } diff --git a/pkg/auth/rpc_auth.go b/pkg/auth/rpc_auth.go deleted file mode 100644 index 5ffbaf3b48..0000000000 --- a/pkg/auth/rpc_auth.go +++ /dev/null @@ -1,118 +0,0 @@ -// Copyright 2018-2023 CERN -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// In applying this license, CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -package auth - -import ( - "context" - "net/rpc" - - authpb "github.com/cs3org/go-cs3apis/cs3/auth/provider/v1beta1" - user "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1" - "github.com/cs3org/reva/pkg/appctx" - "github.com/cs3org/reva/pkg/plugin" - hcplugin "github.com/hashicorp/go-plugin" -) - -func init() { - plugin.Register("authprovider", &ProviderPlugin{}) -} - -// ProviderPlugin is the implementation of plugin.Plugin so we can serve/consume this. -type ProviderPlugin struct { - Impl Manager -} - -// Server returns the RPC Server which serves the methods that the Client calls over net/rpc. -func (p *ProviderPlugin) Server(*hcplugin.MuxBroker) (interface{}, error) { - return &RPCServer{Impl: p.Impl}, nil -} - -// Client returns interface implementation for the plugin that communicates to the server end of the plugin. -func (p *ProviderPlugin) Client(b *hcplugin.MuxBroker, c *rpc.Client) (interface{}, error) { - return &RPCClient{Client: c}, nil -} - -// RPCClient is an implementation of Manager that talks over RPC. -type RPCClient struct{ Client *rpc.Client } - -// ConfigureArg for RPC. -type ConfigureArg struct { - Ml map[string]interface{} -} - -// ConfigureReply for RPC. -type ConfigureReply struct { - Err error -} - -// Configure RPCClient configure method. -func (m *RPCClient) Configure(ml map[string]interface{}) error { - args := ConfigureArg{Ml: ml} - resp := ConfigureReply{} - err := m.Client.Call("Plugin.Configure", args, &resp) - if err != nil { - return err - } - return resp.Err -} - -// AuthenticateArgs for RPC. -type AuthenticateArgs struct { - Ctx map[interface{}]interface{} - ClientID string - ClientSecret string -} - -// AuthenticateReply for RPC. -type AuthenticateReply struct { - User *user.User - Auth map[string]*authpb.Scope - Error error -} - -// Authenticate RPCClient Authenticate method. -func (m *RPCClient) Authenticate(ctx context.Context, clientID, clientSecret string) (*user.User, map[string]*authpb.Scope, error) { - ctxVal := appctx.GetKeyValuesFromCtx(ctx) - args := AuthenticateArgs{Ctx: ctxVal, ClientID: clientID, ClientSecret: clientSecret} - reply := AuthenticateReply{} - err := m.Client.Call("Plugin.Authenticate", args, &reply) - if err != nil { - return nil, nil, err - } - return reply.User, reply.Auth, reply.Error -} - -// RPCServer is the server that RPCClient talks to, conforming to the requirements of net/rpc. -type RPCServer struct { - // This is the real implementation - Impl Manager -} - -// Configure RPCServer Configure method. -func (m *RPCServer) Configure(args ConfigureArg, resp *ConfigureReply) error { - resp.Err = m.Impl.Configure(args.Ml) - return nil -} - -// Authenticate RPCServer Authenticate method. -func (m *RPCServer) Authenticate(args AuthenticateArgs, resp *AuthenticateReply) error { - ctx := appctx.PutKeyValuesToCtx(args.Ctx) - resp.User, resp.Auth, resp.Error = m.Impl.Authenticate(ctx, args.ClientID, args.ClientSecret) - return nil -} diff --git a/pkg/plugin/loader.go b/pkg/plugin/loader.go deleted file mode 100644 index e4bf5865d0..0000000000 --- a/pkg/plugin/loader.go +++ /dev/null @@ -1,127 +0,0 @@ -// Copyright 2018-2023 CERN -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// In applying this license, CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -package plugin - -import ( - "bytes" - "fmt" - "os" - "os/exec" - "path/filepath" - "regexp" - - "github.com/cs3org/reva/pkg/errtypes" - "github.com/hashicorp/go-hclog" - "github.com/hashicorp/go-plugin" -) - -// RevaPlugin represents the runtime plugin. -type RevaPlugin struct { - Plugin interface{} - Client *plugin.Client -} - -const dirname = "/var/tmp/reva" - -var isAlphaNum = regexp.MustCompile(`^[A-Za-z0-9]+$`).MatchString - -// Kill kills the plugin process. -func (plug *RevaPlugin) Kill() { - plug.Client.Kill() -} - -var handshake = plugin.HandshakeConfig{ - ProtocolVersion: 1, - MagicCookieKey: "BASIC_PLUGIN", - MagicCookieValue: "hello", -} - -func compile(pluginType string, path string) (string, error) { - var errb bytes.Buffer - binaryPath := filepath.Join(dirname, "bin", pluginType, filepath.Base(path)) - command := fmt.Sprintf("go build -o %s %s", binaryPath, path) - cmd := exec.Command("bash", "-c", command) - cmd.Stderr = &errb - err := cmd.Run() - if err != nil { - return "", fmt.Errorf("%v: %w", errb.String(), err) - } - return binaryPath, nil -} - -// checkDir checks and compiles plugin if the configuration points to a directory. -func checkDirAndCompile(pluginType, driver string) (string, error) { - bin := driver - file, err := os.Stat(driver) - if err != nil { - return "", err - } - // compile if we point to a package - if file.IsDir() { - bin, err = compile(pluginType, driver) - if err != nil { - return "", err - } - } - return bin, nil -} - -// Load loads the plugin using the hashicorp go-plugin system. -func Load(pluginType, driver string) (*RevaPlugin, error) { - if isAlphaNum(driver) { - return nil, errtypes.NotFound(driver) - } - bin, err := checkDirAndCompile(pluginType, driver) - if err != nil { - return nil, err - } - - logger := hclog.New(&hclog.LoggerOptions{ - Name: "plugin", - Output: os.Stdout, - Level: hclog.Trace, - }) - - client := plugin.NewClient(&plugin.ClientConfig{ - HandshakeConfig: handshake, - Plugins: PluginMap, - Cmd: exec.Command(bin), - AllowedProtocols: []plugin.Protocol{ - plugin.ProtocolNetRPC, - }, - Logger: logger, - }) - - rpcClient, err := client.Client() - if err != nil { - return nil, err - } - - raw, err := rpcClient.Dispense(pluginType) - if err != nil { - return nil, err - } - - revaPlugin := &RevaPlugin{ - Plugin: raw, - Client: client, - } - - return revaPlugin, nil -} diff --git a/pkg/plugin/plugin.go b/pkg/plugin/plugin.go index 4ee00f24c3..d4bfa9251c 100644 --- a/pkg/plugin/plugin.go +++ b/pkg/plugin/plugin.go @@ -18,7 +18,45 @@ package plugin -// Plugin is the interface used to configure plugins. -type Plugin interface { - Configure(m map[string]interface{}) error +import "reflect" + +// RegistryFunc is the func a component that is pluggable +// must define to register the new func in its own registry. +// It is responsibility of the component to type assert the +// new func with the expected one and panic if not. +type RegistryFunc func(name string, newFunc any) + +var registry = map[string]RegistryFunc{} // key is the namespace + +// RegisterNamespace is the function called by a component +// that is pluggable, to register its namespace and a function +// to register the plugins. +func RegisterNamespace(ns string, f RegistryFunc) { + if ns == "" { + panic("namespace cannot be empty") + } + registry[ns] = f +} + +// RegisterPlugin is called to register a new plugin in the +// given namespace. Its called internally by reva, and should +// not be used by external plugins. +func RegisterPlugin(ns, name string, newFunc any) { + if ns == "" { + panic("namespace cannot be empty") + } + if name == "" { + panic("name cannot be empty") + } + if newFunc == nil { + panic("new func cannot be nil") + } + if reflect.TypeOf(newFunc).Kind() != reflect.Func { + panic("type must be a function") + } + r, ok := registry[ns] + if !ok { + panic("namespace does not exist") + } + r(name, newFunc) } diff --git a/pkg/user/rpc_user.go b/pkg/user/rpc_user.go deleted file mode 100644 index 750718d457..0000000000 --- a/pkg/user/rpc_user.go +++ /dev/null @@ -1,214 +0,0 @@ -// Copyright 2018-2023 CERN -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// In applying this license, CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -package user - -import ( - "context" - "encoding/gob" - "net/rpc" - - userpb "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1" - "github.com/cs3org/reva/pkg/appctx" - "github.com/cs3org/reva/pkg/plugin" - hcplugin "github.com/hashicorp/go-plugin" -) - -func init() { - gob.Register(&userpb.User{}) - plugin.Register("userprovider", &ProviderPlugin{}) -} - -// ProviderPlugin is the implementation of plugin.Plugin so we can serve/consume this. -type ProviderPlugin struct { - Impl Manager -} - -// Server returns the RPC Server which serves the methods that the Client calls over net/rpc. -func (p *ProviderPlugin) Server(*hcplugin.MuxBroker) (interface{}, error) { - return &RPCServer{Impl: p.Impl}, nil -} - -// Client returns interface implementation for the plugin that communicates to the server end of the plugin. -func (p *ProviderPlugin) Client(b *hcplugin.MuxBroker, c *rpc.Client) (interface{}, error) { - return &RPCClient{Client: c}, nil -} - -// RPCClient is an implementation of Manager that talks over RPC. -type RPCClient struct{ Client *rpc.Client } - -// ConfigureArg for RPC. -type ConfigureArg struct { - Ml map[string]interface{} -} - -// ConfigureReply for RPC. -type ConfigureReply struct { - Err error -} - -// Configure RPCClient configure method. -func (m *RPCClient) Configure(ml map[string]interface{}) error { - args := ConfigureArg{Ml: ml} - resp := ConfigureReply{} - err := m.Client.Call("Plugin.Configure", args, &resp) - if err != nil { - return err - } - return resp.Err -} - -// GetUserArg for RPC. -type GetUserArg struct { - Ctx map[interface{}]interface{} - UID *userpb.UserId - SkipFetchingGroups bool -} - -// GetUserReply for RPC. -type GetUserReply struct { - User *userpb.User - Err error -} - -// GetUser RPCClient GetUser method. -func (m *RPCClient) GetUser(ctx context.Context, uid *userpb.UserId, skipFetchingGroups bool) (*userpb.User, error) { - ctxVal := appctx.GetKeyValuesFromCtx(ctx) - args := GetUserArg{Ctx: ctxVal, UID: uid, SkipFetchingGroups: skipFetchingGroups} - resp := GetUserReply{} - err := m.Client.Call("Plugin.GetUser", args, &resp) - if err != nil { - return nil, err - } - return resp.User, resp.Err -} - -// GetUserByClaimArg for RPC. -type GetUserByClaimArg struct { - Ctx map[interface{}]interface{} - Claim string - Value string - SkipFetchingGroups bool -} - -// GetUserByClaimReply for RPC. -type GetUserByClaimReply struct { - User *userpb.User - Err error -} - -// GetUserByClaim RPCClient GetUserByClaim method. -func (m *RPCClient) GetUserByClaim(ctx context.Context, claim, value string, skipFetchingGroups bool) (*userpb.User, error) { - ctxVal := appctx.GetKeyValuesFromCtx(ctx) - args := GetUserByClaimArg{Ctx: ctxVal, Claim: claim, Value: value, SkipFetchingGroups: skipFetchingGroups} - resp := GetUserByClaimReply{} - err := m.Client.Call("Plugin.GetUserByClaim", args, &resp) - if err != nil { - return nil, err - } - return resp.User, resp.Err -} - -// GetUserGroupsArg for RPC. -type GetUserGroupsArg struct { - Ctx map[interface{}]interface{} - User *userpb.UserId -} - -// GetUserGroupsReply for RPC. -type GetUserGroupsReply struct { - Group []string - Err error -} - -// GetUserGroups RPCClient GetUserGroups method. -func (m *RPCClient) GetUserGroups(ctx context.Context, user *userpb.UserId) ([]string, error) { - ctxVal := appctx.GetKeyValuesFromCtx(ctx) - args := GetUserGroupsArg{Ctx: ctxVal, User: user} - resp := GetUserGroupsReply{} - err := m.Client.Call("Plugin.GetUserGroups", args, &resp) - if err != nil { - return nil, err - } - return resp.Group, resp.Err -} - -// FindUsersArg for RPC. -type FindUsersArg struct { - Ctx map[interface{}]interface{} - Query string - SkipFetchingGroups bool -} - -// FindUsersReply for RPC. -type FindUsersReply struct { - User []*userpb.User - Err error -} - -// FindUsers RPCClient FindUsers method. -func (m *RPCClient) FindUsers(ctx context.Context, query string, skipFetchingGroups bool) ([]*userpb.User, error) { - ctxVal := appctx.GetKeyValuesFromCtx(ctx) - args := FindUsersArg{Ctx: ctxVal, Query: query, SkipFetchingGroups: skipFetchingGroups} - resp := FindUsersReply{} - err := m.Client.Call("Plugin.FindUsers", args, &resp) - if err != nil { - return nil, err - } - return resp.User, resp.Err -} - -// RPCServer is the server that RPCClient talks to, conforming to the requirements of net/rpc. -type RPCServer struct { - // This is the real implementation - Impl Manager -} - -// Configure RPCServer Configure method. -func (m *RPCServer) Configure(args ConfigureArg, resp *ConfigureReply) error { - resp.Err = m.Impl.Configure(args.Ml) - return nil -} - -// GetUser RPCServer GetUser method. -func (m *RPCServer) GetUser(args GetUserArg, resp *GetUserReply) error { - ctx := appctx.PutKeyValuesToCtx(args.Ctx) - resp.User, resp.Err = m.Impl.GetUser(ctx, args.UID, args.SkipFetchingGroups) - return nil -} - -// GetUserByClaim RPCServer GetUserByClaim method. -func (m *RPCServer) GetUserByClaim(args GetUserByClaimArg, resp *GetUserByClaimReply) error { - ctx := appctx.PutKeyValuesToCtx(args.Ctx) - resp.User, resp.Err = m.Impl.GetUserByClaim(ctx, args.Claim, args.Value, args.SkipFetchingGroups) - return nil -} - -// GetUserGroups RPCServer GetUserGroups method. -func (m *RPCServer) GetUserGroups(args GetUserGroupsArg, resp *GetUserGroupsReply) error { - ctx := appctx.PutKeyValuesToCtx(args.Ctx) - resp.Group, resp.Err = m.Impl.GetUserGroups(ctx, args.User) - return nil -} - -// FindUsers RPCServer FindUsers method. -func (m *RPCServer) FindUsers(args FindUsersArg, resp *FindUsersReply) error { - ctx := appctx.PutKeyValuesToCtx(args.Ctx) - resp.User, resp.Err = m.Impl.FindUsers(ctx, args.Query, args.SkipFetchingGroups) - return nil -} diff --git a/pkg/user/user.go b/pkg/user/user.go index e011b84567..d53233e104 100644 --- a/pkg/user/user.go +++ b/pkg/user/user.go @@ -22,12 +22,10 @@ import ( "context" userpb "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1" - "github.com/cs3org/reva/pkg/plugin" ) // Manager is the interface to implement to manipulate users. type Manager interface { - plugin.Plugin // GetUser returns the user metadata identified by a uid. // The groups of the user are omitted if specified, as these might not be required for certain operations // and might involve computational overhead. diff --git a/pkg/utils/utils.go b/pkg/utils/utils.go index 41b3af44ad..4c7ce12133 100644 --- a/pkg/utils/utils.go +++ b/pkg/utils/utils.go @@ -418,3 +418,17 @@ func UserIsLightweight(u *userpb.User) bool { return u.Id.Type == userpb.UserType_USER_TYPE_FEDERATED || u.Id.Type == userpb.UserType_USER_TYPE_LIGHTWEIGHT } + +// Cast casts a value `v` to the value `to`. +// `v` is expected to be the underlying type of `to`. +// For example, if the type A is defined as func() +// and v is func(), to is of type A. +// to must be a pointer, otherwise the method panics. +func Cast(v any, to any) { + toVal := reflect.ValueOf(to) + if toVal.Type().Kind() != reflect.Pointer { + panic("cast: destination must be a pointer") + } + toVal = toVal.Elem() + toVal.Set(reflect.ValueOf(v)) +} diff --git a/plugins.go b/plugins.go new file mode 100644 index 0000000000..72c7479350 --- /dev/null +++ b/plugins.go @@ -0,0 +1,141 @@ +// Copyright 2018-2023 CERN +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// In applying this license, CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +package reva + +import ( + "sort" + "strings" + + "github.com/cs3org/reva/pkg/plugin" +) + +// Plugin is a type used as reva plugin. +// The type may implement something useful, depending +// on what is possible to plug in. +type Plugin interface { + // RevaPlugin returns the plugin info, like the ID + // in the form of ., and a New func + // used to create the plugin. + // The namespace can be only one defined by reva, + // depending on the scope of the plugin, while the name + // can be whatever, but unique in the namespace. + RevaPlugin() PluginInfo +} + +// PluginInfo holds the information of a reva plugin. +type PluginInfo struct { + // ID is the full name of the plugin, in the form + // .. It must be unique. + ID PluginID + // New is the constructor of the plugin. We rely + // on the developer to correctly provide a valid + // construct depending on the plugin. + New any +} + +// String return a string representation of the PluginInfo. +func (pi PluginInfo) String() string { return string(pi.ID) } + +// PluginID is the string that uniquely identify a reva plugin. +// It consists of a dot-separated labels. The last label is the +// name of the plugin, while the labels before represents the +// namespace. +// A pluginID is in the form . +// Neither the name nor the namespace can be empty. +// The name cannot contain dots. +// ModuleIDs shuld be lowercase and use underscores (_) instead +// of spaces. +type PluginID string + +var registry = map[string]Plugin{} + +// Name returns the name of the Plugin ID (i.e. the last name). +func (i PluginID) Name() string { + parts := strings.Split(string(i), ".") + if len(parts) <= 1 { + panic("plugin id must be .") + } + return parts[len(parts)-1] +} + +// Namespace returns the namespace of the plugin ID, which is +// all but the last label of the ID. +func (i PluginID) Namespace() string { + idx := strings.LastIndex(string(i), ".") + if idx < 0 { + panic("plugin id must be .") + } + return string(i)[:idx] +} + +// RegisterPlugin registers a reva plugin. For registration +// this method should be called in the init() method. +func RegisterPlugin(p Plugin) { + if p == nil { + panic("plugin cannot be nil") + } + plug := p.RevaPlugin() + if plug.ID == "" { + panic("plugin id cannot be nil") + } + if plug.New == nil { + panic("plugin new func cannot be nil") + } + + name := plug.ID.Name() + ns := plug.ID.Namespace() + registry[string(p.RevaPlugin().ID)] = p + plugin.RegisterPlugin(ns, name, plug.New) +} + +func hasPrefixSlices(s, prefix []string) bool { + if len(prefix) > len(s) { + return false + } + for i := range prefix { + if s[i] != prefix[i] { + return false + } + } + return true +} + +// GetPlugins returns all the plugins in the given namespace, +// and their descendants. +// For example, a namespace "foo" returns modules with id "foo", +// "foo.bar", "foo.bar.foo", but not "bar". +func GetPlugins(ns string) []PluginInfo { + prefix := strings.Split(ns, ".") + if ns == "" { + prefix = []string{} + } + + var plugs []PluginInfo + for ns, p := range registry { + nsParts := strings.Split(ns, ".") + if hasPrefixSlices(nsParts, prefix) { + plugs = append(plugs, p.RevaPlugin()) + } + } + + sort.SliceStable(plugs, func(i, j int) bool { + return plugs[i].ID < plugs[j].ID + }) + return plugs +} diff --git a/plugins_test.go b/plugins_test.go new file mode 100644 index 0000000000..1fcf2e4f80 --- /dev/null +++ b/plugins_test.go @@ -0,0 +1,149 @@ +// Copyright 2018-2023 CERN +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// In applying this license, CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +package reva + +import ( + "reflect" + "testing" + + "github.com/stretchr/testify/assert" +) + +type mockPlugin struct { + id string +} + +func (p mockPlugin) RevaPlugin() PluginInfo { + return PluginInfo{ID: PluginID(p.id)} +} + +func TestGetPlugins(t *testing.T) { + registry = map[string]Plugin{ + "a": mockPlugin{id: "a"}, + "a.b": mockPlugin{id: "a.b"}, + "a.b.c": mockPlugin{id: "a.b.c"}, + "a.b.cd": mockPlugin{id: "a.b.cd"}, + "a.c": mockPlugin{id: "a.c"}, + "a.d": mockPlugin{id: "a.d"}, + "b": mockPlugin{id: "b"}, + "b.a": mockPlugin{id: "b.a"}, + "b.b": mockPlugin{id: "b.b"}, + "b.a.c": mockPlugin{id: "b.a.c"}, + "c": mockPlugin{id: "c"}, + } + + tests := []struct { + scope string + exp []PluginInfo + }{ + { + scope: "", + exp: []PluginInfo{ + {ID: "a"}, + {ID: "a.b"}, + {ID: "a.b.c"}, + {ID: "a.b.cd"}, + {ID: "a.c"}, + {ID: "a.d"}, + {ID: "b"}, + {ID: "b.a"}, + {ID: "b.a.c"}, + {ID: "b.b"}, + {ID: "c"}, + }, + }, + { + scope: "a", + exp: []PluginInfo{ + {ID: "a"}, + {ID: "a.b"}, + {ID: "a.b.c"}, + {ID: "a.b.cd"}, + {ID: "a.c"}, + {ID: "a.d"}, + }, + }, + { + scope: "a.b.c", + exp: []PluginInfo{ + {ID: "a.b.c"}, + }, + }, + { + scope: "b.a", + exp: []PluginInfo{ + {ID: "b.a"}, + {ID: "b.a.c"}, + }, + }, + } + + for i, tt := range tests { + got := GetPlugins(tt.scope) + if !reflect.DeepEqual(got, tt.exp) { + t.Fatalf("test %d: expected %v got %v", i+1, tt.exp, got) + } + } +} + +func TestNameNamespace(t *testing.T) { + tests := []struct { + id PluginID + name string + ns string + valid bool + }{ + { + id: "", + valid: false, + }, + { + id: "a", + valid: false, + }, + { + id: "a.b", + name: "b", + ns: "a", + valid: true, + }, + { + id: "aa.bb.cc", + name: "cc", + ns: "aa.bb", + valid: true, + }, + } + + for i, tt := range tests { + if !tt.valid { + assert.Panics(t, func() { + tt.id.Name() + }, "test %d should have paniced", i) + assert.Panics(t, func() { + tt.id.Namespace() + }, "test %d should have paniced", i) + } else { + name := tt.id.Name() + ns := tt.id.Namespace() + assert.Equal(t, tt.name, name, "test %d", i) + assert.Equal(t, tt.ns, ns, "test %d", i) + } + } +}