Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Filter root path according to the agent that makes the request #2212

Merged
merged 24 commits into from
Nov 17, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions changelog/unreleased/clean-root-path.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Enhancement: Filter root path according to the agent that makes the request

https://github.com/cs3org/reva/pull/2212
25 changes: 10 additions & 15 deletions internal/grpc/interceptors/log/log.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,10 @@ import (
"time"

"github.com/cs3org/reva/pkg/appctx"
ctxpkg "github.com/cs3org/reva/pkg/ctx"
"github.com/rs/zerolog"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/metadata"
"google.golang.org/grpc/peer"
"google.golang.org/grpc/status"
)
Expand All @@ -40,16 +40,13 @@ func NewUnary() grpc.UnaryServerInterceptor {
code := status.Code(err)
end := time.Now()
diff := end.Sub(start).Nanoseconds()
var fromAddress, userAgent string
var fromAddress string
if p, ok := peer.FromContext(ctx); ok {
fromAddress = p.Addr.Network() + "://" + p.Addr.String()
}
if md, ok := metadata.FromIncomingContext(ctx); ok {
if vals, ok := md["user-agent"]; ok {
if len(vals) > 0 && vals[0] != "" {
userAgent = vals[0]
}
}
userAgent, ok := ctxpkg.ContextGetUserAgentString(ctx)
if !ok {
userAgent = ""
}

log := appctx.GetLogger(ctx)
Expand Down Expand Up @@ -77,21 +74,19 @@ func NewUnary() grpc.UnaryServerInterceptor {
// that adds trace information to the request.
func NewStream() grpc.StreamServerInterceptor {
interceptor := func(srv interface{}, ss grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error {
ctx := ss.Context()
start := time.Now()
err := handler(srv, ss)
end := time.Now()
code := status.Code(err)
diff := end.Sub(start).Nanoseconds()
var fromAddress, userAgent string
var fromAddress string
if p, ok := peer.FromContext(ss.Context()); ok {
fromAddress = p.Addr.Network() + "://" + p.Addr.String()
}
if md, ok := metadata.FromIncomingContext(ss.Context()); ok {
if vals, ok := md["user-agent"]; ok {
if len(vals) > 0 && vals[0] != "" {
userAgent = vals[0]
}
}
userAgent, ok := ctxpkg.ContextGetUserAgentString(ctx)
if !ok {
userAgent = ""
}

log := appctx.GetLogger(ss.Context())
Expand Down
70 changes: 70 additions & 0 deletions internal/grpc/interceptors/useragent/useragent.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
// 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 useragent

import (
"context"

ctxpkg "github.com/cs3org/reva/pkg/ctx"
"google.golang.org/grpc"
"google.golang.org/grpc/metadata"
)

// NewUnary returns a new unary interceptor that adds
// the useragent to the context.
func NewUnary() grpc.UnaryServerInterceptor {
interceptor := func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
if md, ok := metadata.FromIncomingContext(ctx); ok {
if lst, ok := md[ctxpkg.UserAgentHeader]; ok && len(lst) != 0 {
ctx = metadata.AppendToOutgoingContext(ctx, ctxpkg.UserAgentHeader, lst[0])
}
}
return handler(ctx, req)
}
return interceptor
}

// NewStream returns a new server stream interceptor
// that adds the user agent to the context.
func NewStream() grpc.StreamServerInterceptor {
interceptor := func(srv interface{}, ss grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error {
ctx := ss.Context()
if md, ok := metadata.FromIncomingContext(ctx); ok {
if lst, ok := md[ctxpkg.UserAgentHeader]; ok && len(lst) != 0 {
ctx = metadata.AppendToOutgoingContext(ctx, ctxpkg.UserAgentHeader, lst[0])
}
}
wrapped := newWrappedServerStream(ctx, ss)
return handler(srv, wrapped)
}
return interceptor
}

func newWrappedServerStream(ctx context.Context, ss grpc.ServerStream) *wrappedServerStream {
return &wrappedServerStream{ServerStream: ss, newCtx: ctx}
}

type wrappedServerStream struct {
grpc.ServerStream
newCtx context.Context
}

func (ss *wrappedServerStream) Context() context.Context {
return ss.newCtx
}
1 change: 1 addition & 0 deletions internal/grpc/services/gateway/gateway.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ type config struct {
HomeMapping string `mapstructure:"home_mapping"`
TokenManagers map[string]map[string]interface{} `mapstructure:"token_managers"`
EtagCacheTTL int `mapstructure:"etag_cache_ttl"`
AllowedUserAgents map[string][]string `mapstructure:"allowed_user_agents"` // map[path][]user-agent
CreateHomeCacheTTL int `mapstructure:"create_home_cache_ttl"`
}

Expand Down
42 changes: 35 additions & 7 deletions internal/grpc/services/gateway/storageprovider.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,10 @@ import (
provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
registry "github.com/cs3org/go-cs3apis/cs3/storage/registry/v1beta1"
types "github.com/cs3org/go-cs3apis/cs3/types/v1beta1"
ctxpkg "github.com/cs3org/reva/pkg/ctx"
rtrace "github.com/cs3org/reva/pkg/trace"
"github.com/cs3org/reva/pkg/useragent"
ua "github.com/mileusna/useragent"

"github.com/cs3org/reva/pkg/appctx"
"github.com/cs3org/reva/pkg/errtypes"
Expand Down Expand Up @@ -1655,6 +1658,30 @@ func (s *svc) listSharesFolder(ctx context.Context) (*provider.ListContainerResp
return lcr, nil
}

func (s *svc) isPathAllowed(ua *ua.UserAgent, path string) bool {
uaLst, ok := s.c.AllowedUserAgents[path]
if !ok {
// if no user agent is defined for a path, all user agents are allowed
return true
}
return useragent.IsUserAgentAllowed(ua, uaLst)
}

func (s *svc) filterProvidersByUserAgent(ctx context.Context, providers []*registry.ProviderInfo) []*registry.ProviderInfo {
gmgigi96 marked this conversation as resolved.
Show resolved Hide resolved
ua, ok := ctxpkg.ContextGetUserAgent(ctx)
if !ok {
return providers
}

filters := []*registry.ProviderInfo{}
for _, p := range providers {
if s.isPathAllowed(ua, p.ProviderPath) {
filters = append(filters, p)
}
}
return filters
}

func (s *svc) listContainer(ctx context.Context, req *provider.ListContainerRequest) (*provider.ListContainerResponse, error) {
providers, err := s.findProviders(ctx, req.Ref)
if err != nil {
Expand All @@ -1665,6 +1692,7 @@ func (s *svc) listContainer(ctx context.Context, req *provider.ListContainerRequ
providers = getUniqueProviders(providers)

resPath := req.Ref.GetPath()

if len(providers) == 1 && (utils.IsRelativeReference(req.Ref) || resPath == "" || strings.HasPrefix(resPath, providers[0].ProviderPath)) {
c, err := s.getStorageProviderClient(ctx, providers[0])
if err != nil {
Expand All @@ -1686,7 +1714,7 @@ func (s *svc) listContainerAcrossProviders(ctx context.Context, req *provider.Li
nestedInfos := make(map[string]*provider.ResourceInfo)
log := appctx.GetLogger(ctx)

for _, p := range providers {
for _, p := range s.filterProvidersByUserAgent(ctx, providers) {
c, err := s.getStorageProviderClient(ctx, p)
if err != nil {
log.Err(err).Msg("error connecting to storage provider=" + p.Address)
Expand Down Expand Up @@ -2180,15 +2208,15 @@ func (s *svc) findProviders(ctx context.Context, ref *provider.Reference) ([]*re
}

func getUniqueProviders(providers []*registry.ProviderInfo) []*registry.ProviderInfo {
unique := make(map[string]bool)
unique := make(map[string]*registry.ProviderInfo)
for _, p := range providers {
unique[p.Address] = true
unique[p.Address] = p
}
p := make([]*registry.ProviderInfo, 0, len(unique))
for addr := range unique {
p = append(p, &registry.ProviderInfo{Address: addr})
res := make([]*registry.ProviderInfo, 0, len(unique))
for _, provider := range unique {
res = append(res, provider)
}
return p
return res
}

type etagWithTS struct {
Expand Down
2 changes: 2 additions & 0 deletions internal/http/interceptors/auth/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,8 @@ func New(m map[string]interface{}, unprotected []string) (global.Middleware, err
ctx = ctxpkg.ContextSetToken(ctx, tkn)
ctx = metadata.AppendToOutgoingContext(ctx, ctxpkg.TokenHeader, tkn) // TODO(jfd): hardcoded metadata key. use PerRPCCredentials?

ctx = metadata.AppendToOutgoingContext(ctx, ctxpkg.UserAgentHeader, r.UserAgent())

r = r.WithContext(ctx)
h.ServeHTTP(w, r)
})
Expand Down
26 changes: 20 additions & 6 deletions pkg/ctx/agentctx.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,20 +25,34 @@ import (
"google.golang.org/grpc/metadata"
)

// UserAgentHeader is the header used for the user agent
const UserAgentHeader = "x-user-agent"
gmgigi96 marked this conversation as resolved.
Show resolved Hide resolved

// 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) {
if userAgentStr, ok := ContextGetUserAgentString(ctx); ok {
userAgent := ua.Parse(userAgentStr)
return &userAgent, true
}
return nil, false
}

// ContextGetUserAgentString returns the user agent string if set in the given context.
func ContextGetUserAgentString(ctx context.Context) (string, bool) {
md, ok := metadata.FromIncomingContext(ctx)
if !ok {
return nil, false
return "", false
}
userAgentLst, ok := md["user-agent"]
userAgentLst, ok := md[UserAgentHeader]
if !ok {
return nil, false
userAgentLst, ok = md["user-agent"]
if !ok {
return "", false
}
}
if len(userAgentLst) == 0 {
return nil, false
return "", false
}
userAgent := ua.Parse(userAgentLst[0])
return &userAgent, true
return userAgentLst[0], true
}
3 changes: 3 additions & 0 deletions pkg/rgrpc/rgrpc.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import (
"github.com/cs3org/reva/internal/grpc/interceptors/log"
"github.com/cs3org/reva/internal/grpc/interceptors/recovery"
"github.com/cs3org/reva/internal/grpc/interceptors/token"
"github.com/cs3org/reva/internal/grpc/interceptors/useragent"
"github.com/cs3org/reva/pkg/sharedconf"
rtrace "github.com/cs3org/reva/pkg/trace"
grpc_middleware "github.com/grpc-ecosystem/go-grpc-middleware"
Expand Down Expand Up @@ -290,6 +291,7 @@ func (s *Server) getInterceptors(unprotected []string) ([]grpc.ServerOption, err
unaryInterceptors = append([]grpc.UnaryServerInterceptor{
appctx.NewUnary(s.log),
token.NewUnary(),
useragent.NewUnary(),
log.NewUnary(),
recovery.NewUnary(),
}, unaryInterceptors...)
Expand Down Expand Up @@ -331,6 +333,7 @@ func (s *Server) getInterceptors(unprotected []string) ([]grpc.ServerOption, err
authStream,
appctx.NewStream(s.log),
token.NewStream(),
useragent.NewStream(),
log.NewStream(),
recovery.NewStream(),
}, streamInterceptors...)
Expand Down
44 changes: 3 additions & 41 deletions pkg/storage/registry/static/static.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ 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"
)
Expand All @@ -44,10 +43,9 @@ func init() {
var bracketRegex = regexp.MustCompile(`\[(.*?)\]`)

type rule struct {
Mapping string `mapstructure:"mapping"`
Address string `mapstructure:"address"`
Aliases map[string]string `mapstructure:"aliases"`
AllowedUserAgents []string `mapstructure:"allowed_user_agents"`
Mapping string `mapstructure:"mapping"`
Address string `mapstructure:"address"`
Aliases map[string]string `mapstructure:"aliases"`
}

type config struct {
Expand Down Expand Up @@ -140,27 +138,6 @@ 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
Expand Down Expand Up @@ -197,21 +174,6 @@ func (b *reg) FindProviders(ctx context.Context, ref *provider.Reference) ([]*re
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 {
Expand Down
Loading