use the context as a scopes carrier

This commit is contained in:
Roman Perekhod
2024-12-06 11:17:39 +01:00
parent ebfcf5a164
commit 1d08e9f5ea
5 changed files with 141 additions and 72981 deletions

72955
coverage.out

File diff suppressed because it is too large Load Diff

View File

@@ -1228,12 +1228,16 @@ func (f *FileConnector) CheckFileInfo(ctx context.Context) (*ConnectorResponse,
if !isPublicShare {
parentFolderURL.Path = path.Join(ocisURL.Path, "f", storagespace.FormatResourceID(statRes.GetInfo().GetParentId()))
} else {
publicShare := &link.PublicShare{}
err := wopiContext.GetScopeByKeyPrefix("publicshare:", publicShare)
if err != nil {
logger.Error().Err(err).Msg("CheckFileInfo: error getting public share scope")
if scopes, ok := ctxpkg.ContextGetScopes(ctx); ok {
publicShare := &link.PublicShare{}
if err := helpers.GetScopeByKeyPrefix(scopes, "publicshare:", publicShare); err == nil {
parentFolderURL.Path = path.Join(ocisURL.Path, "s", publicShare.GetToken())
} else {
logger.Error().Err(err).Msg("CheckFileInfo: error getting public share scope")
}
} else {
logger.Error().Err(err).Msg("CheckFileInfo: error getting scopes from the context")
}
parentFolderURL.Path = path.Join(ocisURL.Path, "s", publicShare.GetToken())
}
// fileinfo map
infoMap := map[string]interface{}{

View File

@@ -10,12 +10,15 @@ import (
"strings"
appproviderv1beta1 "github.com/cs3org/go-cs3apis/cs3/app/provider/v1beta1"
auth "github.com/cs3org/go-cs3apis/cs3/auth/provider/v1beta1"
gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1"
userv1beta1 "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
link "github.com/cs3org/go-cs3apis/cs3/sharing/link/v1beta1"
providerv1beta1 "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
typesv1beta1 "github.com/cs3org/go-cs3apis/cs3/types/v1beta1"
ctxpkg "github.com/cs3org/reva/v2/pkg/ctx"
"github.com/cs3org/reva/v2/pkg/rgrpc/status"
"github.com/cs3org/reva/v2/pkg/utils"
cs3mocks "github.com/cs3org/reva/v2/tests/cs3mocks/mocks"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
@@ -1806,6 +1809,109 @@ var _ = Describe("FileConnector", func() {
Expect(response.Body.(*fileinfo.Collabora)).To(Equal(expectedFileInfo))
})
It("Stat success guests", func() {
ResourceId := &providerv1beta1.ResourceId{
StorageId: "storageid",
OpaqueId: "opaqueid",
SpaceId: "spaceid",
}
// add user's opaque to include public-share-role
u := &userv1beta1.User{}
u.Opaque = &typesv1beta1.Opaque{
Map: map[string]*typesv1beta1.OpaqueEntry{
"public-share-role": {
Decoder: "plain",
Value: []byte("viewer"),
},
},
}
// Create a new "scope share" to only expose the required fields `ResourceId` and `Token` to the scope.
scopeShare := &link.PublicShare{ResourceId: ResourceId, Token: "ABC123"}
val, err := utils.MarshalProtoV1ToJSON(scopeShare)
Expect(err).ToNot(HaveOccurred())
scopes := map[string]*auth.Scope{
"publicshare:ABC123": {
Resource: &typesv1beta1.OpaqueEntry{
Decoder: "json",
Value: val,
},
Role: auth.Role(appproviderv1beta1.ViewMode_VIEW_MODE_VIEW_ONLY), //
},
}
// change view mode to view only
wopiCtx.ViewMode = appproviderv1beta1.ViewMode_VIEW_MODE_VIEW_ONLY
ctx := middleware.WopiContextToCtx(context.Background(), wopiCtx)
ctx = ctxpkg.ContextSetUser(ctx, u)
ctx = ctxpkg.ContextSetScopes(ctx, scopes)
gatewayClient.On("Stat", mock.Anything, mock.Anything).Times(1).Return(&providerv1beta1.StatResponse{
Status: status.NewOK(ctx),
Info: &providerv1beta1.ResourceInfo{
Owner: &userv1beta1.UserId{
Idp: "customIdp",
OpaqueId: "aabbcc",
Type: userv1beta1.UserType_USER_TYPE_PRIMARY,
},
Size: uint64(998877),
Mtime: &typesv1beta1.Timestamp{
Seconds: uint64(16273849),
},
Path: "/path/to/test.txt",
Id: ResourceId,
ParentId: &providerv1beta1.ResourceId{
StorageId: "storageid",
OpaqueId: "parentopaqueid",
SpaceId: "spaceid",
},
// Other properties aren't used for now.
},
}, nil)
// change wopi app provider
cfg.App.Name = "OnlyOffice"
cfg.App.Product = "OnlyOffice"
expectedFileInfo := &fileinfo.OnlyOffice{
Version: "v162738490",
BaseFileName: "test.txt",
BreadcrumbDocName: "test.txt",
BreadcrumbFolderName: "/path/to",
BreadcrumbFolderURL: "https://ocis.example.prv/s/ABC123",
DisablePrint: true,
UserCanNotWriteRelative: false,
SupportsLocks: true,
SupportsUpdate: true,
SupportsRename: true,
IsAnonymousUser: true,
UserCanRename: false,
UserCanReview: false,
UserCanWrite: false,
EnableInsertRemoteImage: false,
UserID: "guest-zzz000",
UserFriendlyName: "guest zzz000",
FileSharingURL: "https://ocis.example.prv/f/storageid$spaceid%21opaqueid?details=sharing",
FileVersionURL: "https://ocis.example.prv/f/storageid$spaceid%21opaqueid?details=versions",
HostEditURL: "https://ocis.example.prv/external-onlyoffice/path/to/test.txt?fileId=storageid%24spaceid%21opaqueid&view_mode=write",
PostMessageOrigin: "https://ocis.example.prv",
}
response, err := fc.CheckFileInfo(ctx)
// UserID and UserFriendlyName have random Ids generated which are impossible to guess
// Check both separately
Expect(response.Body.(*fileinfo.OnlyOffice).UserID).To(HavePrefix(hex.EncodeToString([]byte("guest-"))))
Expect(response.Body.(*fileinfo.OnlyOffice).UserFriendlyName).To(HavePrefix("Guest "))
// overwrite UserID and UserFriendlyName here for easier matching
response.Body.(*fileinfo.OnlyOffice).UserID = "guest-zzz000"
response.Body.(*fileinfo.OnlyOffice).UserFriendlyName = "guest zzz000"
Expect(err).ToNot(HaveOccurred())
Expect(response.Status).To(Equal(200))
Expect(response.Body.(*fileinfo.OnlyOffice)).To(Equal(expectedFileInfo))
})
It("Stat success authenticated user", func() {
// change view mode to view only
wopiCtx.ViewMode = appproviderv1beta1.ViewMode_VIEW_MODE_VIEW_ONLY

View File

@@ -0,0 +1,24 @@
package helpers
import (
"fmt"
"strings"
auth "github.com/cs3org/go-cs3apis/cs3/auth/provider/v1beta1"
"github.com/cs3org/reva/v2/pkg/utils"
"github.com/golang/protobuf/proto"
)
// GetScopeByKeyPrefix returns the scope from the AccessToken Scope map by key prefix
func GetScopeByKeyPrefix(scopes map[string]*auth.Scope, keyPrefix string, m proto.Message) error {
for k, v := range scopes {
if strings.HasPrefix(k, keyPrefix) && v.Resource.Decoder == "json" {
err := utils.UnmarshalJSONToProtoV1(v.Resource.Value, m)
if err != nil {
return fmt.Errorf("can't unmarshal public share from scope: %w", err)
}
return nil
}
}
return fmt.Errorf("scope %s not found", keyPrefix)
}

View File

@@ -12,13 +12,10 @@ import (
"time"
appproviderv1beta1 "github.com/cs3org/go-cs3apis/cs3/app/provider/v1beta1"
auth "github.com/cs3org/go-cs3apis/cs3/auth/provider/v1beta1"
providerv1beta1 "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
ctxpkg "github.com/cs3org/reva/v2/pkg/ctx"
rjwt "github.com/cs3org/reva/v2/pkg/token/manager/jwt"
"github.com/cs3org/reva/v2/pkg/utils"
"github.com/golang-jwt/jwt/v5"
"github.com/golang/protobuf/proto"
"github.com/owncloud/ocis/v2/services/collaboration/pkg/config"
"github.com/owncloud/ocis/v2/services/collaboration/pkg/helpers"
"github.com/rs/zerolog"
@@ -39,22 +36,6 @@ type WopiContext struct {
FileReference *providerv1beta1.Reference
TemplateReference *providerv1beta1.Reference
ViewMode appproviderv1beta1.ViewMode
// scope contains decoded Scope map from the AccessToken
scope map[string]*auth.Scope
}
// GetScopeByKeyPrefix returns the scope from the AccessToken Scope map by key prefix
func (w *WopiContext) GetScopeByKeyPrefix(keyPrefix string, m proto.Message) error {
for k, v := range w.scope {
if strings.HasPrefix(k, keyPrefix) && v.Resource.Decoder == "json" {
err := utils.UnmarshalJSONToProtoV1(v.Resource.Value, m)
if err != nil {
return fmt.Errorf("can't unmarshal public share from scope: %w", err)
}
break
}
}
return nil
}
// WopiContextAuthMiddleware will prepare an HTTP handler to be used as
@@ -139,7 +120,7 @@ func WopiContextAuthMiddleware(cfg *config.Config, st microstore.Store, next htt
http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
return
}
user, scope, err := tokenManager.DismantleToken(ctx, wopiContextAccessToken)
user, scopes, err := tokenManager.DismantleToken(ctx, wopiContextAccessToken)
if err != nil {
wopiLogger.Error().Err(err).Msg("failed to dismantle reva token manager")
http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
@@ -147,12 +128,12 @@ func WopiContextAuthMiddleware(cfg *config.Config, st microstore.Store, next htt
}
claims.WopiContext.AccessToken = wopiContextAccessToken
claims.WopiContext.scope = scope
ctx = context.WithValue(ctx, wopiContextKey, claims.WopiContext)
// authentication for the CS3 api
ctx = metadata.AppendToOutgoingContext(ctx, ctxpkg.TokenHeader, claims.WopiContext.AccessToken)
ctx = ctxpkg.ContextSetUser(ctx, user)
ctx = ctxpkg.ContextSetScopes(ctx, scopes)
// include additional info in the context's logger
wopiLogger = wopiLogger.With().