Skip to content

Commit

Permalink
Refactor ctx user agent interceptor and add desktop client ns
Browse files Browse the repository at this point in the history
  • Loading branch information
ishank011 committed Apr 19, 2022
1 parent 7a7a3b2 commit 7979cad
Show file tree
Hide file tree
Showing 6 changed files with 92 additions and 151 deletions.
30 changes: 17 additions & 13 deletions internal/grpc/services/gateway/storageprovider.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,6 @@ import (
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 @@ -1778,30 +1776,36 @@ 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 {
ua, ok := ctxpkg.ContextGetUserAgent(ctx)
cat, ok := ctxpkg.ContextGetUserAgentCategory(ctx)
if !ok {
return providers
}

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

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

for _, userAgent := range allowedUserAgents {
if userAgent == cat {
return true
}
}
return false
}

func (s *svc) listContainer(ctx context.Context, req *provider.ListContainerRequest) (*provider.ListContainerResponse, error) {
providers, err := s.findProviders(ctx, req.Ref)
if err != nil {
Expand Down
4 changes: 2 additions & 2 deletions internal/http/interceptors/auth/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ func New(m map[string]interface{}, unprotected []string) (global.Middleware, err
isUnprotectedEndpoint = true
}

ctx, err := authenticateUser(w, r, conf, unprotected, tokenStrategy, tokenManager, tokenWriter, credChain, isUnprotectedEndpoint)
ctx, err := authenticateUser(w, r, conf, tokenStrategy, tokenManager, tokenWriter, credChain, isUnprotectedEndpoint)
if err != nil {
if !isUnprotectedEndpoint {
return
Expand All @@ -187,7 +187,7 @@ func New(m map[string]interface{}, unprotected []string) (global.Middleware, err
return chain, nil
}

func authenticateUser(w http.ResponseWriter, r *http.Request, conf *config, unprotected []string, tokenStrategy auth.TokenStrategy, tokenManager token.Manager, tokenWriter auth.TokenWriter, credChain map[string]auth.CredentialStrategy, isUnprotectedEndpoint bool) (context.Context, error) {
func authenticateUser(w http.ResponseWriter, r *http.Request, conf *config, tokenStrategy auth.TokenStrategy, tokenManager token.Manager, tokenWriter auth.TokenWriter, credChain map[string]auth.CredentialStrategy, isUnprotectedEndpoint bool) (context.Context, error) {
ctx := r.Context()
log := appctx.GetLogger(ctx)

Expand Down
6 changes: 6 additions & 0 deletions internal/http/services/owncloud/ocdav/ocdav.go
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,12 @@ func (s *svc) Handler() http.Handler {
head, r.URL.Path = router.ShiftPath(r.URL.Path)
log.Debug().Str("head", head).Str("tail", r.URL.Path).Msg("http routing")
switch head {
case "cernbox":
var origin string
origin, r.URL.Path = router.ShiftPath(r.URL.Path) // desktop or mobile
_, r.URL.Path = router.ShiftPath(r.URL.Path) // remote.php
head, r.URL.Path = router.ShiftPath(r.URL.Path)
base = path.Join(base, "cernbox", origin, "remote.php")
case "status.php":
s.doStatus(w, r)
return
Expand Down
53 changes: 52 additions & 1 deletion pkg/ctx/agentctx.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,21 @@ package ctx

import (
"context"
"strings"

ua "github.com/mileusna/useragent"
"google.golang.org/grpc/metadata"
)

// UserAgentHeader is the header used for the user agent
const UserAgentHeader = "x-user-agent"
const (
UserAgentHeader = "x-user-agent"

WebUserAgent = "web"
GrpcUserAgent = "grpc"
MobileUserAgent = "mobile"
DesktopUserAgent = "desktop"
)

// ContextGetUserAgent returns the user agent if set in the given context.
// see https://github.com/grpc/grpc-go/issues/1100
Expand Down Expand Up @@ -56,3 +64,46 @@ func ContextGetUserAgentString(ctx context.Context) (string, bool) {
}
return userAgentLst[0], true
}

// ContextGetUserAgentCategory returns the category of the user agent
// (i.e. if it is a web, mobile, desktop or grpc user agent)
func ContextGetUserAgentCategory(ctx context.Context) (string, bool) {
agent, ok := ContextGetUserAgent(ctx)
if !ok {
return "", false
}
switch {
case isWeb(agent):
return WebUserAgent, true
case isMobile(agent):
return MobileUserAgent, true
case isDesktop(agent):
return DesktopUserAgent, true
case isGRPC(agent):
return GrpcUserAgent, true
default:
return "", false
}
}

func isWeb(ua *ua.UserAgent) bool {
return ua.IsChrome() || ua.IsEdge() || ua.IsFirefox() || ua.IsSafari() ||
ua.IsInternetExplorer() || ua.IsOpera() || ua.IsOperaMini()
}

// isMobile returns true if the useragent is generated by the mobile
func isMobile(ua *ua.UserAgent) bool {
// workaround as the library does not recognise iOS string inside the user agent
isIOS := ua.IsIOS() || strings.Contains(ua.String, "iOS")
return !isWeb(ua) && (ua.IsAndroid() || isIOS)
}

// isDesktop returns true if the useragent is generated by a desktop application
func isDesktop(ua *ua.UserAgent) bool {
return ua.Desktop && !isWeb(ua)
}

// isGRPC returns true if the useragent is generated by a grpc client
func isGRPC(ua *ua.UserAgent) bool {
return strings.HasPrefix(ua.Name, "grpc")
}
73 changes: 15 additions & 58 deletions pkg/useragent/useragent_test.go → pkg/ctx/agentctx_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,105 +16,62 @@
// granted to it by virtue of its status as an Intergovernmental Organization
// or submit itself to any jurisdiction.

package useragent
package ctx

import (
"context"
"testing"

ua "github.com/mileusna/useragent"
"google.golang.org/grpc/metadata"
)

func TestUserAgentIsAllowed(t *testing.T) {

tests := []struct {
description string
userAgent string
userAgents []string
expected bool
expected string
}{
{
description: "grpc-go",
userAgent: "grpc-go",
userAgents: []string{"grpc"},
expected: true,
},
{
description: "grpc-go",
userAgent: "grpc-go",
userAgents: []string{"desktop", "mobile", "web"},
expected: false,
expected: "grpc",
},
{
description: "web-firefox",
userAgent: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.0 Safari/605.1.15",
userAgents: []string{"web"},
expected: true,
},
{
description: "web-firefox",
userAgent: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.0 Safari/605.1.15",
userAgents: []string{"desktop", "mobile", "grpc"},
expected: false,
},
{
description: "desktop-mirall",
userAgent: "Mozilla/5.0 (Linux) mirall/2.7.1 (build 2596) (cernboxcmd, centos-3.10.0-1160.36.2.el7.x86_64 ClientArchitecture: x86_64 OsArchitecture: x86_64)",
userAgents: []string{"desktop"},
expected: true,
expected: "web",
},
{
description: "desktop-mirall",
userAgent: "Mozilla/5.0 (Linux) mirall/2.7.1 (build 2596) (cernboxcmd, centos-3.10.0-1160.36.2.el7.x86_64 ClientArchitecture: x86_64 OsArchitecture: x86_64)",
userAgents: []string{"web", "mobile", "grpc"},
expected: false,
},
{
description: "mobile-android",
userAgent: "Mozilla/5.0 (Android) ownCloud-android/2.13.1 cernbox/Android",
userAgents: []string{"mobile"},
expected: true,
},
{
description: "mobile-ios",
userAgent: "Mozilla/5.0 (iOS) ownCloud-iOS/3.8.0 cernbox/iOS",
userAgents: []string{"mobile"},
expected: true,
expected: "desktop",
},
{
description: "mobile-android",
userAgent: "Mozilla/5.0 (Android) ownCloud-android/2.13.1 cernbox/Android",
userAgents: []string{"web", "desktop", "grpc"},
expected: false,
expected: "mobile",
},
{
description: "mobile-ios",
userAgent: "Mozilla/5.0 (iOS) ownCloud-iOS/3.8.0 cernbox/iOS",
userAgents: []string{"web", "desktop", "grpc"},
expected: false,
expected: "mobile",
},
{
description: "mobile-web",
userAgent: "Mozilla/5.0 (Android 11; Mobile; rv:86.0) Gecko/86.0 Firefox/86.0",
userAgents: []string{"web"},
expected: true,
},
{
description: "mobile-web",
userAgent: "Mozilla/5.0 (Android 11; Mobile; rv:86.0) Gecko/86.0 Firefox/86.0",
userAgents: []string{"desktop", "grpc", "mobile"},
expected: false,
expected: "web",
},
}

ctx := context.Background()
for _, tt := range tests {
t.Run(tt.description, func(t *testing.T) {
ctx = metadata.NewIncomingContext(ctx, metadata.New(map[string]string{UserAgentHeader: tt.userAgent}))
cat, _ := ContextGetUserAgentCategory(ctx)

ua := ua.Parse(tt.userAgent)

res := IsUserAgentAllowed(&ua, tt.userAgents)

if res != tt.expected {
t.Fatalf("result does not match with expected. got=%+v expected=%+v", res, tt.expected)
if cat != tt.expected {
t.Fatalf("result does not match with expected. got=%+v expected=%+v", cat, tt.expected)
}

})
Expand Down
77 changes: 0 additions & 77 deletions pkg/useragent/useragent.go

This file was deleted.

0 comments on commit 7979cad

Please sign in to comment.