From 77bbd0950c5f69a725077a4cbb92605e011b321c Mon Sep 17 00:00:00 2001 From: Michael D'Silva Date: Mon, 10 Feb 2020 21:08:03 +1100 Subject: [PATCH] Layout fix (bringing back pw) (#493) --- cmd/revad/runtime/loader.go | 1 + examples/separate/storage-home.toml | 5 + .../storageprovider/storageprovider.go | 33 +++++++ pkg/storage/pw/context/context.go | 99 +++++++++++++++++++ pkg/storage/pw/loader/loader.go | 25 +++++ pkg/storage/pw/registry/registry.go | 34 +++++++ 6 files changed, 197 insertions(+) create mode 100644 pkg/storage/pw/context/context.go create mode 100644 pkg/storage/pw/loader/loader.go create mode 100644 pkg/storage/pw/registry/registry.go diff --git a/cmd/revad/runtime/loader.go b/cmd/revad/runtime/loader.go index 856c0ab84d..dfaed8b845 100644 --- a/cmd/revad/runtime/loader.go +++ b/cmd/revad/runtime/loader.go @@ -32,6 +32,7 @@ import ( _ "github.com/cs3org/reva/pkg/publicshare/manager/loader" _ "github.com/cs3org/reva/pkg/share/manager/loader" _ "github.com/cs3org/reva/pkg/storage/fs/loader" + _ "github.com/cs3org/reva/pkg/storage/pw/loader" _ "github.com/cs3org/reva/pkg/storage/registry/loader" _ "github.com/cs3org/reva/pkg/token/manager/loader" _ "github.com/cs3org/reva/pkg/user/manager/loader" diff --git a/examples/separate/storage-home.toml b/examples/separate/storage-home.toml index f32485b28a..3576b89c49 100644 --- a/examples/separate/storage-home.toml +++ b/examples/separate/storage-home.toml @@ -26,6 +26,7 @@ driver = "owncloud" mount_path = "/home" mount_id = "123e4567-e89b-12d3-a456-426655440000" expose_data_server = true +path_wrapper = "context" data_server_url = "http://localhost:12001/data" enable_home_creation = true @@ -33,6 +34,10 @@ enable_home_creation = true datadirectory = "/var/tmp/reva/data" layout = "{{.UsernamePrefixCount.1}}/{{.UsernameLower}}" +[grpc.services.storageprovider.path_wrappers.context] +prefix = "" +layout = "{{.UsernamePrefixCount.1}}/{{.UsernameLower}}" + [http] address = "0.0.0.0:12001" diff --git a/internal/grpc/services/storageprovider/storageprovider.go b/internal/grpc/services/storageprovider/storageprovider.go index d50e6a5177..16bd78b7b5 100644 --- a/internal/grpc/services/storageprovider/storageprovider.go +++ b/internal/grpc/services/storageprovider/storageprovider.go @@ -34,6 +34,7 @@ import ( "github.com/cs3org/reva/pkg/rgrpc/status" "github.com/cs3org/reva/pkg/storage" "github.com/cs3org/reva/pkg/storage/fs/registry" + pwregistry "github.com/cs3org/reva/pkg/storage/pw/registry" "github.com/mitchellh/mapstructure" "github.com/pkg/errors" "go.opencensus.io/trace" @@ -61,6 +62,7 @@ type config struct { type service struct { conf *config storage storage.FS + pathWrapper storage.PathWrapper mountPath, mountID string tmpFolder string dataServerURL *url.URL @@ -132,6 +134,10 @@ func New(m map[string]interface{}, ss *grpc.Server) (rgrpc.Service, error) { if err != nil { return nil, err } + pw, err := getPW(c) + if err != nil { + return nil, err + } // parse data server url u, err := url.Parse(c.DataServerURL) @@ -152,6 +158,7 @@ func New(m map[string]interface{}, ss *grpc.Server) (rgrpc.Service, error) { service := &service{ conf: c, storage: fs, + pathWrapper: pw, tmpFolder: tmpFolder, mountPath: mountPath, mountID: mountID, @@ -782,6 +789,16 @@ func getFS(c *config) (storage.FS, error) { return nil, fmt.Errorf("driver not found: %s", c.Driver) } +func getPW(c *config) (storage.PathWrapper, error) { + if c.PathWrapper == "" { + return nil, nil + } + if f, ok := pwregistry.NewFuncs[c.PathWrapper]; ok { + return f(c.PathWrappers[c.PathWrapper]) + } + return nil, fmt.Errorf("path wrapper not found: %s", c.Driver) +} + func (s *service) unwrap(ctx context.Context, ref *provider.Reference) (*provider.Reference, error) { if ref.GetId() != nil { idRef := &provider.Reference{ @@ -807,6 +824,13 @@ func (s *service) unwrap(ctx context.Context, ref *provider.Reference) (*provide return nil, err } + if s.pathWrapper != nil { + fsfn, err = s.pathWrapper.Unwrap(ctx, fsfn) + if err != nil { + return nil, err + } + } + pathRef := &provider.Reference{ Spec: &provider.Reference_Path{ Path: fsfn, @@ -825,6 +849,15 @@ func (s *service) trimMountPrefix(fn string) (string, error) { func (s *service) wrap(ctx context.Context, ri *provider.ResourceInfo) error { ri.Id.StorageId = s.mountID + + if s.pathWrapper != nil { + var err error + ri.Path, err = s.pathWrapper.Wrap(ctx, ri.Path) + if err != nil { + return err + } + } + ri.Path = path.Join(s.mountPath, ri.Path) return nil } diff --git a/pkg/storage/pw/context/context.go b/pkg/storage/pw/context/context.go new file mode 100644 index 0000000000..0bf2045e92 --- /dev/null +++ b/pkg/storage/pw/context/context.go @@ -0,0 +1,99 @@ +// Copyright 2018-2019 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 context + +import ( + "context" + "fmt" + "path" + "strings" + + "github.com/cs3org/reva/pkg/errtypes" + "github.com/cs3org/reva/pkg/storage" + "github.com/cs3org/reva/pkg/storage/helper" + "github.com/cs3org/reva/pkg/storage/pw/registry" + "github.com/cs3org/reva/pkg/user" + "github.com/mitchellh/mapstructure" + "github.com/pkg/errors" +) + +func init() { + registry.Register("context", New) +} + +type config struct { + Prefix string `mapstructure:"prefix"` + Layout string `mapstructure:"layout"` +} + +func parseConfig(m map[string]interface{}) (*config, error) { + c := &config{Layout: "{{.Username}}"} + if err := mapstructure.Decode(m, c); err != nil { + err = errors.Wrap(err, "error decoding conf") + return nil, err + } + return c, nil +} + +// New returns an implementation to of the storage.PathWrapper interface that +// is used to wrap and unwrap storage paths +func New(m map[string]interface{}) (storage.PathWrapper, error) { + c, err := parseConfig(m) + if err != nil { + return nil, err + } + return &pw{prefix: c.Prefix, layout: c.Layout}, nil +} + +type pw struct { + prefix string + layout string +} + +// Only works when a user is in context +func (pw *pw) Unwrap(ctx context.Context, rp string) (string, error) { + + u, ok := user.ContextGetUser(ctx) + if !ok { + return "", errors.Wrap(errtypes.UserRequired("userrequired"), "error getting user from ctx") + } + if u.Username == "" { + return "", errors.Wrap(errtypes.UserRequired("userrequired"), "user has no username") + } + userhome, err := helper.GetUserHomePath(u, pw.layout) + if err != nil { + return "", errors.Wrap(errtypes.UserRequired("userrequired"), fmt.Sprintf("template error: %s", err.Error())) + } + return path.Join("/", pw.prefix, userhome, rp), nil +} + +func (pw *pw) Wrap(ctx context.Context, rp string) (string, error) { + u, ok := user.ContextGetUser(ctx) + if !ok { + return "", errors.Wrap(errtypes.UserRequired("userrequired"), "error getting user from ctx") + } + if u.Username == "" { + return "", errors.Wrap(errtypes.UserRequired("userrequired"), "user has no username") + } + userhome, err := helper.GetUserHomePath(u, pw.layout) + if err != nil { + return "", errors.Wrap(errtypes.UserRequired("userrequired"), fmt.Sprintf("template error: %s", err.Error())) + } + return strings.TrimPrefix(rp, path.Join("/", userhome)), nil +} diff --git a/pkg/storage/pw/loader/loader.go b/pkg/storage/pw/loader/loader.go new file mode 100644 index 0000000000..91a2d6b458 --- /dev/null +++ b/pkg/storage/pw/loader/loader.go @@ -0,0 +1,25 @@ +// Copyright 2018-2019 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 loader + +import ( + // Load core storage path wrapper backends. + _ "github.com/cs3org/reva/pkg/storage/pw/context" + // Add your own here +) diff --git a/pkg/storage/pw/registry/registry.go b/pkg/storage/pw/registry/registry.go new file mode 100644 index 0000000000..ef181ada2d --- /dev/null +++ b/pkg/storage/pw/registry/registry.go @@ -0,0 +1,34 @@ +// Copyright 2018-2019 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 registry + +import "github.com/cs3org/reva/pkg/storage" + +// NewFunc is the function that storage implementations +// should register at init time. +type NewFunc func(map[string]interface{}) (storage.PathWrapper, error) + +// NewFuncs is a map containing all the registered storage backends. +var NewFuncs = map[string]NewFunc{} + +// Register registers a new storage backend new function. +// Not safe for concurrent use. Safe for use from package init. +func Register(name string, f NewFunc) { + NewFuncs[name] = f +}