Skip to content

Commit

Permalink
Merge pull request #209 from ishank011/public-shares-apps
Browse files Browse the repository at this point in the history
Move code for public share scope expansion to grpc interceptor
  • Loading branch information
C0rby authored Oct 15, 2021
2 parents 032a7b9 + 6708574 commit 9af576c
Show file tree
Hide file tree
Showing 14 changed files with 97 additions and 104 deletions.
4 changes: 2 additions & 2 deletions internal/grpc/interceptors/auth/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -224,15 +224,15 @@ func dismantleToken(ctx context.Context, tkn string, req interface{}, mgr token.
}

// Check if access to the resource is in the scope of the token
ok, err := scope.VerifyScope(ctx, tokenScope, req, client, mgr)
ok, err := scope.VerifyScope(ctx, tokenScope, req)
if err != nil {
return nil, errtypes.InternalError("error verifying scope of access token")
}
if ok {
return u, nil
}

if err = expandAndVerifyScope(ctx, req, tokenScope, gatewayAddr); err != nil {
if err = expandAndVerifyScope(ctx, req, tokenScope, gatewayAddr, mgr); err != nil {
return nil, err
}

Expand Down
89 changes: 62 additions & 27 deletions internal/grpc/interceptors/auth/scope.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,20 +23,30 @@ import (
"strings"

authpb "github.com/cs3org/go-cs3apis/cs3/auth/provider/v1beta1"
gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1"
userpb "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1"
collaboration "github.com/cs3org/go-cs3apis/cs3/sharing/collaboration/v1beta1"
link "github.com/cs3org/go-cs3apis/cs3/sharing/link/v1beta1"
provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
registry "github.com/cs3org/go-cs3apis/cs3/storage/registry/v1beta1"
"github.com/cs3org/reva/pkg/appctx"
"github.com/cs3org/reva/pkg/auth/scope"
ctxpkg "github.com/cs3org/reva/pkg/ctx"
"github.com/cs3org/reva/pkg/errtypes"
statuspkg "github.com/cs3org/reva/pkg/rgrpc/status"
"github.com/cs3org/reva/pkg/rgrpc/todo/pool"
"github.com/cs3org/reva/pkg/token"
"github.com/cs3org/reva/pkg/utils"
"google.golang.org/grpc/metadata"
)

func expandAndVerifyScope(ctx context.Context, req interface{}, tokenScope map[string]*authpb.Scope, gatewayAddr string) error {
func expandAndVerifyScope(ctx context.Context, req interface{}, tokenScope map[string]*authpb.Scope, gatewayAddr string, mgr token.Manager) error {
log := appctx.GetLogger(ctx)
client, err := pool.GetGatewayServiceClient(gatewayAddr)
if err != nil {
return err
}

if ref, ok := extractRef(req); ok {
// Check if req is of type *provider.Reference_Path
Expand All @@ -53,7 +63,7 @@ func expandAndVerifyScope(ctx context.Context, req interface{}, tokenScope map[s
if err != nil {
continue
}
if ok, err := checkResourcePath(ctx, ref, share.ResourceId, gatewayAddr); err == nil && ok {
if ok, err := checkIfNestedResource(ctx, ref, share.ResourceId, client, mgr); err == nil && ok {
return nil
}

Expand All @@ -63,37 +73,35 @@ func expandAndVerifyScope(ctx context.Context, req interface{}, tokenScope map[s
if err != nil {
continue
}
if ok, err := checkResourcePath(ctx, ref, share.ResourceId, gatewayAddr); err == nil && ok {
if ok, err := checkIfNestedResource(ctx, ref, share.ResourceId, client, mgr); err == nil && ok {
return nil
}
case strings.HasPrefix(k, "lightweight"):
client, err := pool.GetGatewayServiceClient(gatewayAddr)
if err != nil {
continue
}
shares, err := client.ListReceivedShares(ctx, &collaboration.ListReceivedSharesRequest{})
if err != nil || shares.Status.Code != rpc.Code_CODE_OK {
log.Warn().Err(err).Msg("error listing received shares")
continue
}
for _, share := range shares.Shares {
if ok, err := checkResourcePath(ctx, ref, share.Share.ResourceId, gatewayAddr); err == nil && ok {
if ok, err := checkIfNestedResource(ctx, ref, share.Share.ResourceId, client, mgr); err == nil && ok {
return nil
}
}
}
}
} else {
// ref has ID present
// The request might be coming from a share created for a lightweight account
// after the token was minted.
log.Info().Msgf("resolving ID reference against received shares to verify token scope %+v", ref.GetResourceId())
// The request might be coming from
// - a resource present inside a shared folder, or
// - a share created for a lightweight account after the token was minted.

client, err := pool.GetGatewayServiceClient(gatewayAddr)
if err != nil {
return err
}
for k := range tokenScope {
if strings.HasPrefix(k, "lightweight") {
log.Info().Msgf("resolving ID reference against received shares to verify token scope %+v", ref.GetResourceId())
shares, err := client.ListReceivedShares(ctx, &collaboration.ListReceivedSharesRequest{})
if err != nil || shares.Status.Code != rpc.Code_CODE_OK {
log.Warn().Err(err).Msg("error listing received shares")
Expand All @@ -104,6 +112,15 @@ func expandAndVerifyScope(ctx context.Context, req interface{}, tokenScope map[s
return nil
}
}
} else if strings.HasPrefix(k, "publicshare") {
var share link.PublicShare
err := utils.UnmarshalJSONToProtoV1(tokenScope[k].Resource.Value, &share)
if err != nil {
continue
}
if ok, err := checkIfNestedResource(ctx, ref, share.ResourceId, client, mgr); err == nil && ok {
return nil
}
}
}
}
Expand Down Expand Up @@ -140,32 +157,50 @@ func expandAndVerifyScope(ctx context.Context, req interface{}, tokenScope map[s
return errtypes.PermissionDenied("access to resource not allowed within the assigned scope")
}

func checkResourcePath(ctx context.Context, ref *provider.Reference, r *provider.ResourceId, gatewayAddr string) (bool, error) {
client, err := pool.GetGatewayServiceClient(gatewayAddr)
if err != nil {
return false, err
}

func checkIfNestedResource(ctx context.Context, ref *provider.Reference, parent *provider.ResourceId, client gateway.GatewayAPIClient, mgr token.Manager) (bool, error) {
// Since the resource ID is obtained from the scope, the current token
// has access to it.
statReq := &provider.StatRequest{
Ref: &provider.Reference{ResourceId: r},
}

statResponse, err := client.Stat(ctx, statReq)
statResponse, err := client.Stat(ctx, &provider.StatRequest{Ref: &provider.Reference{ResourceId: parent}})
if err != nil {
return false, err
}
if statResponse.Status.Code != rpc.Code_CODE_OK {
return false, statuspkg.NewErrorFromCode(statResponse.Status.Code, "auth interceptor")
}
parentPath := statResponse.Info.Path

childPath := ref.GetPath()
if childPath == "" {
// We mint a token as the owner of the public share and try to stat the reference
// TODO(ishank011): We need to find a better alternative to this

if strings.HasPrefix(ref.GetPath(), statResponse.Info.Path) {
// The path corresponds to the resource to which the token has access.
// We allow access to it.
return true, nil
userResp, err := client.GetUser(ctx, &userpb.GetUserRequest{UserId: statResponse.Info.Owner})
if err != nil || userResp.Status.Code != rpc.Code_CODE_OK {
return false, err
}

scope, err := scope.AddOwnerScope(map[string]*authpb.Scope{})
if err != nil {
return false, err
}
token, err := mgr.MintToken(ctx, userResp.User, scope)
if err != nil {
return false, err
}
ctx = metadata.AppendToOutgoingContext(context.Background(), ctxpkg.TokenHeader, token)

childStat, err := client.Stat(ctx, &provider.StatRequest{Ref: ref})
if err != nil {
return false, err
}
if childStat.Status.Code != rpc.Code_CODE_OK {
return false, statuspkg.NewErrorFromCode(childStat.Status.Code, "auth interceptor")
}
childPath = statResponse.Info.Path
}
return false, nil

return strings.HasPrefix(childPath, parentPath), nil

}

func extractRef(req interface{}) (*provider.Reference, bool) {
Expand Down
2 changes: 1 addition & 1 deletion internal/http/interceptors/auth/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -265,7 +265,7 @@ func New(m map[string]interface{}, unprotected []string) (global.Middleware, err
}

// ensure access to the resource is allowed
ok, err := scope.VerifyScope(ctx, tokenScope, r.URL.Path, client, tokenManager)
ok, err := scope.VerifyScope(ctx, tokenScope, r.URL.Path)
if err != nil {
log.Error().Err(err).Msg("error verifying scope of access token")
w.WriteHeader(http.StatusInternalServerError)
Expand Down
4 changes: 1 addition & 3 deletions pkg/auth/scope/lightweight.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,16 +22,14 @@ import (
"context"

authpb "github.com/cs3org/go-cs3apis/cs3/auth/provider/v1beta1"
gatewayv1beta1 "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1"
collaboration "github.com/cs3org/go-cs3apis/cs3/sharing/collaboration/v1beta1"
provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
types "github.com/cs3org/go-cs3apis/cs3/types/v1beta1"
"github.com/cs3org/reva/pkg/token"
"github.com/cs3org/reva/pkg/utils"
"github.com/rs/zerolog"
)

func lightweightAccountScope(_ context.Context, scope *authpb.Scope, resource interface{}, _ *zerolog.Logger, _ gatewayv1beta1.GatewayAPIClient, _ token.Manager) (bool, error) {
func lightweightAccountScope(_ context.Context, scope *authpb.Scope, resource interface{}, _ *zerolog.Logger) (bool, error) {
// Lightweight accounts have access to resources shared with them.
// These cannot be resolved from here, but need to be added to the scope from
// where the call to mint tokens is made.
Expand Down
71 changes: 20 additions & 51 deletions pkg/auth/scope/publicshare.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,21 +28,16 @@ import (
authpb "github.com/cs3org/go-cs3apis/cs3/auth/provider/v1beta1"
gatewayv1beta1 "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1"
userv1beta1 "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
rpcv1beta1 "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1"
link "github.com/cs3org/go-cs3apis/cs3/sharing/link/v1beta1"
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"
"github.com/cs3org/reva/pkg/errtypes"
"github.com/cs3org/reva/pkg/token"
"github.com/cs3org/reva/pkg/utils"
"github.com/rs/zerolog"
"google.golang.org/grpc/metadata"

ctxpkg "github.com/cs3org/reva/pkg/ctx"
)

func publicshareScope(ctx context.Context, scope *authpb.Scope, resource interface{}, logger *zerolog.Logger, client gatewayv1beta1.GatewayAPIClient, mgr token.Manager) (bool, error) {
func publicshareScope(ctx context.Context, scope *authpb.Scope, resource interface{}, logger *zerolog.Logger) (bool, error) {
var share link.PublicShare
err := utils.UnmarshalJSONToProtoV1(scope.Resource.Value, &share)
if err != nil {
Expand All @@ -52,37 +47,39 @@ func publicshareScope(ctx context.Context, scope *authpb.Scope, resource interfa
switch v := resource.(type) {
// Viewer role
case *registry.GetStorageProvidersRequest:
return checkStorageRef(ctx, &share, v.GetRef(), client, mgr), nil
return checkStorageRef(ctx, &share, v.GetRef()), nil
case *provider.StatRequest:
return checkStorageRef(ctx, &share, v.GetRef(), client, mgr), nil
return checkStorageRef(ctx, &share, v.GetRef()), nil
case *provider.ListContainerRequest:
return checkStorageRef(ctx, &share, v.GetRef(), client, mgr), nil
return checkStorageRef(ctx, &share, v.GetRef()), nil
case *provider.InitiateFileDownloadRequest:
return checkStorageRef(ctx, &share, v.GetRef(), client, mgr), nil
return checkStorageRef(ctx, &share, v.GetRef()), nil

// Editor role
// TODO(ishank011): Add role checks,
// need to return appropriate status codes in the ocs/ocdav layers.
case *provider.CreateContainerRequest:
return checkStorageRef(ctx, &share, v.GetRef(), client, mgr), nil
return checkStorageRef(ctx, &share, v.GetRef()), nil
case *provider.DeleteRequest:
return checkStorageRef(ctx, &share, v.GetRef(), client, mgr), nil
return checkStorageRef(ctx, &share, v.GetRef()), nil
case *provider.MoveRequest:
return checkStorageRef(ctx, &share, v.GetSource(), client, mgr) && checkStorageRef(ctx, &share, v.GetDestination(), client, mgr), nil
return checkStorageRef(ctx, &share, v.GetSource()) && checkStorageRef(ctx, &share, v.GetDestination()), nil
case *provider.InitiateFileUploadRequest:
return checkStorageRef(ctx, &share, v.GetRef(), client, mgr), nil
return checkStorageRef(ctx, &share, v.GetRef()), nil
case *provider.SetArbitraryMetadataRequest:
return checkStorageRef(ctx, &share, v.GetRef()), nil
case *provider.UnsetArbitraryMetadataRequest:
return checkStorageRef(ctx, &share, v.GetRef()), nil

// App provider requests
case *appregistry.GetAppProvidersRequest:
return checkStorageRef(ctx, &share, &provider.Reference{ResourceId: v.ResourceInfo.Id}, client, mgr), nil
return checkStorageRef(ctx, &share, &provider.Reference{ResourceId: v.ResourceInfo.Id}), nil
case *appregistry.GetDefaultAppProviderForMimeTypeRequest:
return true, nil
case *appprovider.OpenInAppRequest:
return checkStorageRef(ctx, &share, &provider.Reference{ResourceId: v.ResourceInfo.Id}, client, mgr), nil
return checkStorageRef(ctx, &share, &provider.Reference{ResourceId: v.ResourceInfo.Id}), nil
case *gatewayv1beta1.OpenInAppRequest:
return checkStorageRef(ctx, &share, v.GetRef(), client, mgr), nil
case *provider.SetArbitraryMetadataRequest:
return checkStorageRef(ctx, &share, v.GetRef(), client, mgr), nil
case *provider.UnsetArbitraryMetadataRequest:
return checkStorageRef(ctx, &share, v.GetRef(), client, mgr), nil
return checkStorageRef(ctx, &share, v.GetRef()), nil

case *userv1beta1.GetUserByClaimRequest:
return true, nil
Expand All @@ -98,38 +95,10 @@ func publicshareScope(ctx context.Context, scope *authpb.Scope, resource interfa
return false, errtypes.InternalError(msg)
}

func checkStorageRef(ctx context.Context, s *link.PublicShare, r *provider.Reference, client gatewayv1beta1.GatewayAPIClient, mgr token.Manager) bool {
func checkStorageRef(ctx context.Context, s *link.PublicShare, r *provider.Reference) bool {
// r: <resource_id:<storage_id:$storageID opaque_id:$opaqueID> path:$path > >
if r.ResourceId != nil && r.Path == "" { // path must be empty
if utils.ResourceIDEqual(s.ResourceId, r.GetResourceId()) {
return true
}
shareStat, err := client.Stat(ctx, &provider.StatRequest{Ref: &provider.Reference{ResourceId: s.ResourceId}})
if err != nil || shareStat.Status.Code != rpcv1beta1.Code_CODE_OK {
return false
}

userResp, err := client.GetUserByClaim(ctx, &userv1beta1.GetUserByClaimRequest{Claim: "userid", Value: shareStat.Info.Owner.OpaqueId})
if err != nil || userResp.Status.Code != rpcv1beta1.Code_CODE_OK {
return false
}

scope, err := AddOwnerScope(map[string]*authpb.Scope{})
if err != nil {
return false
}
token, err := mgr.MintToken(ctx, userResp.User, scope)
if err != nil {
return false
}

ctx = metadata.AppendToOutgoingContext(context.Background(), ctxpkg.TokenHeader, token)
refStat, err := client.Stat(ctx, &provider.StatRequest{Ref: r})
if err != nil || refStat.Status.Code != rpcv1beta1.Code_CODE_OK {
return false
}

return strings.HasPrefix(refStat.Info.Path, shareStat.Info.Path)
return utils.ResourceIDEqual(s.ResourceId, r.GetResourceId())
}

// r: <path:"/public/$token" >
Expand Down
4 changes: 1 addition & 3 deletions pkg/auth/scope/receivedshare.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,14 @@ import (
"fmt"

authpb "github.com/cs3org/go-cs3apis/cs3/auth/provider/v1beta1"
gatewayv1beta1 "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1"
collaboration "github.com/cs3org/go-cs3apis/cs3/sharing/collaboration/v1beta1"
types "github.com/cs3org/go-cs3apis/cs3/types/v1beta1"
"github.com/cs3org/reva/pkg/errtypes"
"github.com/cs3org/reva/pkg/token"
"github.com/cs3org/reva/pkg/utils"
"github.com/rs/zerolog"
)

func receivedShareScope(_ context.Context, scope *authpb.Scope, resource interface{}, logger *zerolog.Logger, _ gatewayv1beta1.GatewayAPIClient, _ token.Manager) (bool, error) {
func receivedShareScope(_ context.Context, scope *authpb.Scope, resource interface{}, logger *zerolog.Logger) (bool, error) {
var share collaboration.ReceivedShare
err := utils.UnmarshalJSONToProtoV1(scope.Resource.Value, &share)
if err != nil {
Expand Down
7 changes: 1 addition & 6 deletions pkg/auth/scope/resourceinfo.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,20 +23,17 @@ import (
"fmt"
"strings"

appregistry "github.com/cs3org/go-cs3apis/cs3/app/registry/v1beta1"
authpb "github.com/cs3org/go-cs3apis/cs3/auth/provider/v1beta1"
gatewayv1beta1 "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1"
provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
registry "github.com/cs3org/go-cs3apis/cs3/storage/registry/v1beta1"
"github.com/rs/zerolog"

types "github.com/cs3org/go-cs3apis/cs3/types/v1beta1"
"github.com/cs3org/reva/pkg/errtypes"
"github.com/cs3org/reva/pkg/token"
"github.com/cs3org/reva/pkg/utils"
)

func resourceinfoScope(_ context.Context, scope *authpb.Scope, resource interface{}, logger *zerolog.Logger, _ gatewayv1beta1.GatewayAPIClient, _ token.Manager) (bool, error) {
func resourceinfoScope(_ context.Context, scope *authpb.Scope, resource interface{}, logger *zerolog.Logger) (bool, error) {
var r provider.ResourceInfo
err := utils.UnmarshalJSONToProtoV1(scope.Resource.Value, &r)
if err != nil {
Expand All @@ -45,8 +42,6 @@ func resourceinfoScope(_ context.Context, scope *authpb.Scope, resource interfac

switch v := resource.(type) {
// Viewer role
case *appregistry.GetDefaultAppProviderForMimeTypeRequest:
return true, nil
case *registry.GetStorageProvidersRequest:
return checkResourceInfo(&r, v.GetRef()), nil
case *provider.StatRequest:
Expand Down
Loading

0 comments on commit 9af576c

Please sign in to comment.