diff --git a/changelog/unreleased/storage-ua-filtering.md b/changelog/unreleased/storage-ua-filtering.md new file mode 100644 index 00000000000..61e7731a7ae --- /dev/null +++ b/changelog/unreleased/storage-ua-filtering.md @@ -0,0 +1,7 @@ +Enhancement: Filter root paths according to user agent + +Adds a new rule setting in the storage registry ("allowed_user_agents"), +that allows a user to specify which storage provider shows according +to the user agent that made the request. + +https://github.com/cs3org/reva/pull/2193 diff --git a/pkg/ctx/agentctx.go b/pkg/ctx/agentctx.go new file mode 100644 index 00000000000..6b361676e18 --- /dev/null +++ b/pkg/ctx/agentctx.go @@ -0,0 +1,44 @@ +// Copyright 2018-2021 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 ctx + +import ( + "context" + + ua "github.com/mileusna/useragent" + "google.golang.org/grpc/metadata" +) + +// ContextGetUserAgent returns the user agent if set in the given context. +// see https://github.com/grpc/grpc-go/issues/1100 +func ContextGetUserAgent(ctx context.Context) (*ua.UserAgent, bool) { + md, ok := metadata.FromIncomingContext(ctx) + if !ok { + return nil, false + } + userAgentLst, ok := md["user-agent"] + if !ok { + return nil, false + } + if len(userAgentLst) == 0 { + return nil, false + } + userAgent := ua.Parse(userAgentLst[0]) + return &userAgent, true +} diff --git a/pkg/storage/registry/static/static.go b/pkg/storage/registry/static/static.go index 58d3845013e..d5d84403e40 100644 --- a/pkg/storage/registry/static/static.go +++ b/pkg/storage/registry/static/static.go @@ -32,6 +32,7 @@ import ( "github.com/cs3org/reva/pkg/storage" "github.com/cs3org/reva/pkg/storage/registry/registry" "github.com/cs3org/reva/pkg/storage/utils/templates" + ua "github.com/mileusna/useragent" "github.com/mitchellh/mapstructure" "github.com/pkg/errors" ) @@ -43,9 +44,10 @@ func init() { var bracketRegex = regexp.MustCompile(`\[(.*?)\]`) type rule struct { - Mapping string `mapstructure:"mapping"` - Address string `mapstructure:"address"` - Aliases map[string]string `mapstructure:"aliases"` + Mapping string `mapstructure:"mapping"` + Address string `mapstructure:"address"` + Aliases map[string]string `mapstructure:"aliases"` + AllowedUserAgents []string `mapstructure:"allowed_user_agents"` } type config struct { @@ -138,6 +140,27 @@ func (b *reg) GetHome(ctx context.Context) (*registrypb.ProviderInfo, error) { return nil, errors.New("static: home not found") } +func userAgentIsAllowed(ua *ua.UserAgent, userAgents []string) bool { + for _, userAgent := range userAgents { + switch userAgent { + case "web": + if ua.IsChrome() || ua.IsEdge() || ua.IsFirefox() || ua.IsSafari() || + ua.IsInternetExplorer() || ua.IsOpera() || ua.IsOperaMini() { + return true + } + case "desktop": + if ua.Desktop { + return true + } + case "grpc": + if strings.HasPrefix(ua.Name, "grpc") { + return true + } + } + } + return false +} + func (b *reg) FindProviders(ctx context.Context, ref *provider.Reference) ([]*registrypb.ProviderInfo, error) { // find longest match var match *registrypb.ProviderInfo @@ -173,6 +196,22 @@ func (b *reg) FindProviders(ctx context.Context, ref *provider.Reference) ([]*re fn := path.Clean(ref.GetPath()) if fn != "" { for prefix, rule := range b.c.Rules { + + // check if the provider is allowed to be shown according to the + // user agent that made the request + // if the list of AllowedUserAgents is empty, this means that + // every agents that made the request could see the provider + + if len(rule.AllowedUserAgents) != 0 { + ua, ok := ctxpkg.ContextGetUserAgent(ctx) + if !ok { + continue + } + if !userAgentIsAllowed(ua, rule.AllowedUserAgents) { + continue // skip this provider + } + } + addr := getProviderAddr(ctx, rule) r, err := regexp.Compile("^" + prefix) if err != nil {