Skip to content

Commit

Permalink
Merge pull request #9800 from owncloud/collaboration_checkfileid
Browse files Browse the repository at this point in the history
fix: verify file id in URL matches the one inside the access token
  • Loading branch information
butonic authored Aug 21, 2024
2 parents d4db23c + 9e7d570 commit 5d49f41
Show file tree
Hide file tree
Showing 4 changed files with 32 additions and 14 deletions.
8 changes: 2 additions & 6 deletions services/collaboration/pkg/connector/fileconnector.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package connector
import (
"bytes"
"context"
"crypto/sha256"
"encoding/base64"
"encoding/binary"
"encoding/hex"
Expand All @@ -20,11 +19,11 @@ import (
rpcv1beta1 "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1"
providerv1beta1 "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
typesv1beta1 "github.com/cs3org/go-cs3apis/cs3/types/v1beta1"
"github.com/cs3org/reva/v2/pkg/storagespace"
"github.com/cs3org/reva/v2/pkg/utils"
"github.com/google/uuid"
"github.com/owncloud/ocis/v2/services/collaboration/pkg/config"
"github.com/owncloud/ocis/v2/services/collaboration/pkg/connector/fileinfo"
"github.com/owncloud/ocis/v2/services/collaboration/pkg/helpers"
"github.com/owncloud/ocis/v2/services/collaboration/pkg/middleware"
"github.com/rs/zerolog"
)
Expand Down Expand Up @@ -1172,10 +1171,7 @@ func (f *FileConnector) generateWOPISrc(ctx context.Context, wopiContext middlew
}

// get the reference
resourceId := wopiContext.FileReference.GetResourceId()
c := sha256.New()
c.Write([]byte(storagespace.FormatResourceID(resourceId)))
fileRef := hex.EncodeToString(c.Sum(nil))
fileRef := helpers.HashResourceId(wopiContext.FileReference.GetResourceId())

// generate the URL for the WOPI app to access the new created file
wopiSrcURL, err := url.Parse(f.cfg.Wopi.WopiSrc)
Expand Down
13 changes: 13 additions & 0 deletions services/collaboration/pkg/helpers/cs3.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
package helpers

import (
"crypto/sha256"
"encoding/hex"

gatewayv1beta1 "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1"
providerv1beta1 "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
"github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool"
"github.com/cs3org/reva/v2/pkg/storagespace"
"github.com/owncloud/ocis/v2/services/collaboration/pkg/config"
)

Expand All @@ -25,3 +30,11 @@ func GetCS3apiClient(cfg *config.Config, forceNew bool) (gatewayv1beta1.GatewayA
}
return client, err
}

// HashResourceId builds a urlsafe and stable file reference that can be used for proxy routing,
// so that all sessions on one file end on the same office server
func HashResourceId(resourceId *providerv1beta1.ResourceId) string {
c := sha256.New()
c.Write([]byte(storagespace.FormatResourceID(resourceId)))
return hex.EncodeToString(c.Sum(nil))
}
17 changes: 15 additions & 2 deletions services/collaboration/pkg/middleware/wopicontext.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,15 @@ import (
"errors"
"fmt"
"net/http"
"regexp"

appproviderv1beta1 "github.com/cs3org/go-cs3apis/cs3/app/provider/v1beta1"
userv1beta1 "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
providerv1beta1 "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
ctxpkg "github.com/cs3org/reva/v2/pkg/ctx"
"github.com/golang-jwt/jwt/v4"
"github.com/owncloud/ocis/v2/services/collaboration/pkg/config"
"github.com/owncloud/ocis/v2/services/collaboration/pkg/helpers"
"github.com/rs/zerolog"
"google.golang.org/grpc/metadata"
)
Expand Down Expand Up @@ -45,6 +47,8 @@ type WopiContext struct {
// * A contextual zerologger containing information about the request
// and the WopiContext
func WopiContextAuthMiddleware(cfg *config.Config, next http.Handler) http.Handler {
// compile a regexp here to extract the fileid from the URL
fileIDregexp := regexp.MustCompile(`^/wopi/files/([0-9a-f]{64})(/.*)?$`)
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
accessToken := r.URL.Query().Get("access_token")
if accessToken == "" {
Expand Down Expand Up @@ -89,7 +93,7 @@ func WopiContextAuthMiddleware(cfg *config.Config, next http.Handler) http.Handl
// we might need to check https://learn.microsoft.com/en-us/microsoft-365/cloud-storage-partner-program/rest/common-headers
// although some headers might not be sent depending on the client.
logger := zerolog.Ctx(ctx)
ctx = logger.With().
wopiLogger := logger.With().
Str("WopiSessionId", r.Header.Get("X-WOPI-SessionId")).
Str("WopiOverride", r.Header.Get("X-WOPI-Override")).
Str("WopiProof", r.Header.Get("X-WOPI-Proof")).
Expand All @@ -98,7 +102,16 @@ func WopiContextAuthMiddleware(cfg *config.Config, next http.Handler) http.Handl
Str("FileReference", claims.WopiContext.FileReference.String()).
Str("ViewMode", claims.WopiContext.ViewMode.String()).
Str("Requester", claims.WopiContext.User.GetId().String()).
Logger().WithContext(ctx)
Logger()
ctx = wopiLogger.WithContext(ctx)

hashedRef := helpers.HashResourceId(claims.WopiContext.FileReference.GetResourceId())
matches := fileIDregexp.FindStringSubmatch(r.URL.Path)
if len(matches) < 2 || matches[1] != hashedRef {
wopiLogger.Error().Msg("file reference in the URL doesn't match the one inside the access token")
http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
return
}

next.ServeHTTP(w, r.WithContext(ctx))
})
Expand Down
8 changes: 2 additions & 6 deletions services/collaboration/pkg/service/grpc/v0/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@ package service

import (
"context"
"crypto/sha256"
"encoding/hex"
"fmt"
"net/url"
"path"
Expand All @@ -19,6 +17,7 @@ import (

"github.com/owncloud/ocis/v2/ocis-pkg/log"
"github.com/owncloud/ocis/v2/services/collaboration/pkg/config"
"github.com/owncloud/ocis/v2/services/collaboration/pkg/helpers"
"github.com/owncloud/ocis/v2/services/collaboration/pkg/middleware"
)

Expand Down Expand Up @@ -83,10 +82,7 @@ func (s *Service) OpenInApp(

// build a urlsafe and stable file reference that can be used for proxy routing,
// so that all sessions on one file end on the same office server

c := sha256.New()
c.Write([]byte(req.GetResourceInfo().GetId().GetStorageId() + "$" + req.GetResourceInfo().GetId().GetSpaceId() + "!" + req.GetResourceInfo().GetId().GetOpaqueId()))
fileRef := hex.EncodeToString(c.Sum(nil))
fileRef := helpers.HashResourceId(req.GetResourceInfo().GetId())

// get the file extension to use the right wopi app url
fileExt := path.Ext(req.GetResourceInfo().GetPath())
Expand Down

0 comments on commit 5d49f41

Please sign in to comment.