Bump reva
This commit is contained in:
Generated
Vendored
+3
-2
@@ -33,10 +33,11 @@ type Config struct {
|
||||
ProductVersion string `mapstructure:"product_version"`
|
||||
AllowPropfindDepthInfinitiy bool `mapstructure:"allow_depth_infinity"`
|
||||
|
||||
TransferSharedSecret string `mapstructure:"transfer_shared_secret"`
|
||||
|
||||
NameValidation NameValidation `mapstructure:"validation"`
|
||||
|
||||
// SharedSecret used to sign the 'oc:download' URLs
|
||||
URLSigningSharedSecret string `mapstructure:"url_signing_shared_secret"`
|
||||
|
||||
MachineAuthAPIKey string `mapstructure:"machine_auth_apikey"`
|
||||
}
|
||||
|
||||
|
||||
Generated
Vendored
+13
@@ -20,6 +20,7 @@ package ocdav
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"path"
|
||||
@@ -41,6 +42,7 @@ import (
|
||||
"github.com/opencloud-eu/reva/v2/pkg/rhttp"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/rhttp/global"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/rhttp/router"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/signedurl"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/storage/favorite"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/storage/favorite/registry"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/storage/utils/templates"
|
||||
@@ -69,6 +71,7 @@ type svc struct {
|
||||
LockSystem LockSystem
|
||||
userIdentifierCache *ttlcache.Cache
|
||||
nameValidators []Validator
|
||||
urlSigner signedurl.Signer
|
||||
}
|
||||
|
||||
func (s *svc) Config() *config.Config {
|
||||
@@ -116,6 +119,15 @@ func NewWith(conf *config.Config, fm favorite.Manager, ls LockSystem, _ *zerolog
|
||||
// be safe - init the conf again
|
||||
conf.Init()
|
||||
|
||||
var signer signedurl.Signer
|
||||
if conf.URLSigningSharedSecret != "" {
|
||||
var err error
|
||||
signer, err = signedurl.NewJWTSignedURL(signedurl.WithSecret(conf.URLSigningSharedSecret))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to initialize URL signer: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
s := &svc{
|
||||
c: conf,
|
||||
webDavHandler: new(WebDavHandler),
|
||||
@@ -129,6 +141,7 @@ func NewWith(conf *config.Config, fm favorite.Manager, ls LockSystem, _ *zerolog
|
||||
LockSystem: ls,
|
||||
userIdentifierCache: ttlcache.NewCache(),
|
||||
nameValidators: ValidatorsFromConfig(conf),
|
||||
urlSigner: signer,
|
||||
}
|
||||
_ = s.userIdentifierCache.SetTTL(60 * time.Second)
|
||||
|
||||
|
||||
Generated
Vendored
+50
-20
@@ -52,6 +52,7 @@ import (
|
||||
rstatus "github.com/opencloud-eu/reva/v2/pkg/rgrpc/status"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/rgrpc/todo/pool"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/rhttp/router"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/signedurl"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/storagespace"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/utils"
|
||||
"github.com/rs/zerolog"
|
||||
@@ -214,14 +215,16 @@ type Handler struct {
|
||||
PublicURL string
|
||||
selector pool.Selectable[gateway.GatewayAPIClient]
|
||||
c *config.Config
|
||||
urlSigner signedurl.Signer
|
||||
}
|
||||
|
||||
// NewHandler returns a new PropfindHandler instance
|
||||
func NewHandler(publicURL string, selector pool.Selectable[gateway.GatewayAPIClient], c *config.Config) *Handler {
|
||||
func NewHandler(publicURL string, selector pool.Selectable[gateway.GatewayAPIClient], signer signedurl.Signer, c *config.Config) *Handler {
|
||||
return &Handler{
|
||||
PublicURL: publicURL,
|
||||
selector: selector,
|
||||
c: c,
|
||||
urlSigner: signer,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -494,7 +497,7 @@ func (p *Handler) propfindResponse(ctx context.Context, w http.ResponseWriter, r
|
||||
prefer := net.ParsePrefer(r.Header.Get(net.HeaderPrefer))
|
||||
returnMinimal := prefer[net.HeaderPreferReturn] == "minimal"
|
||||
|
||||
propRes, err := MultistatusResponse(ctx, &pf, resourceInfos, p.PublicURL, namespace, linkshares, returnMinimal)
|
||||
propRes, err := MultistatusResponse(ctx, &pf, resourceInfos, p.PublicURL, namespace, linkshares, returnMinimal, p.urlSigner)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("error formatting propfind")
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
@@ -985,7 +988,7 @@ func ReadPropfind(r io.Reader) (pf XML, status int, err error) {
|
||||
}
|
||||
|
||||
// MultistatusResponse converts a list of resource infos into a multistatus response string
|
||||
func MultistatusResponse(ctx context.Context, pf *XML, mds []*provider.ResourceInfo, publicURL, ns string, linkshares map[string]struct{}, returnMinimal bool) ([]byte, error) {
|
||||
func MultistatusResponse(ctx context.Context, pf *XML, mds []*provider.ResourceInfo, publicURL, ns string, linkshares map[string]struct{}, returnMinimal bool, downloadURLSigner signedurl.Signer) ([]byte, error) {
|
||||
g, ctx := errgroup.WithContext(ctx)
|
||||
|
||||
type work struct {
|
||||
@@ -1020,7 +1023,7 @@ func MultistatusResponse(ctx context.Context, pf *XML, mds []*provider.ResourceI
|
||||
for i := 0; i < numWorkers; i++ {
|
||||
g.Go(func() error {
|
||||
for work := range workChan {
|
||||
res, err := mdToPropResponse(ctx, pf, work.info, publicURL, ns, linkshares, returnMinimal)
|
||||
res, err := mdToPropResponse(ctx, pf, work.info, publicURL, ns, linkshares, returnMinimal, downloadURLSigner)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -1061,7 +1064,7 @@ func MultistatusResponse(ctx context.Context, pf *XML, mds []*provider.ResourceI
|
||||
// mdToPropResponse converts the CS3 metadata into a webdav PropResponse
|
||||
// ns is the CS3 namespace that needs to be removed from the CS3 path before
|
||||
// prefixing it with the baseURI
|
||||
func mdToPropResponse(ctx context.Context, pf *XML, md *provider.ResourceInfo, publicURL, ns string, linkshares map[string]struct{}, returnMinimal bool) (*ResponseXML, error) {
|
||||
func mdToPropResponse(ctx context.Context, pf *XML, md *provider.ResourceInfo, publicURL, ns string, linkshares map[string]struct{}, returnMinimal bool, urlSigner signedurl.Signer) (*ResponseXML, error) {
|
||||
ctx, span := appctx.GetTracerProvider(ctx).Tracer(tracerName).Start(ctx, "md_to_prop_response")
|
||||
span.SetAttributes(attribute.KeyValue{Key: "publicURL", Value: attribute.StringValue(publicURL)})
|
||||
span.SetAttributes(attribute.KeyValue{Key: "ns", Value: attribute.StringValue(ns)})
|
||||
@@ -1516,23 +1519,14 @@ func mdToPropResponse(ctx context.Context, pf *XML, md *provider.ResourceInfo, p
|
||||
appendToNotFound(prop.NotFound("oc:owner-display-name"))
|
||||
}
|
||||
case "downloadURL": // desktop
|
||||
if isPublic && md.Type == provider.ResourceType_RESOURCE_TYPE_FILE {
|
||||
var path string
|
||||
if !ls.PasswordProtected {
|
||||
path = p
|
||||
if md.Type == provider.ResourceType_RESOURCE_TYPE_FILE {
|
||||
url := downloadURL(ctx, sublog, isPublic, p, ls, publicURL, baseURI, urlSigner)
|
||||
if url != "" {
|
||||
appendToOK(prop.Escaped("oc:downloadURL", url))
|
||||
} else {
|
||||
expiration := time.Unix(int64(ls.Signature.SignatureExpiration.Seconds), int64(ls.Signature.SignatureExpiration.Nanos))
|
||||
var sb strings.Builder
|
||||
|
||||
sb.WriteString(p)
|
||||
sb.WriteString("?signature=")
|
||||
sb.WriteString(ls.Signature.Signature)
|
||||
sb.WriteString("&expiration=")
|
||||
sb.WriteString(url.QueryEscape(expiration.Format(time.RFC3339)))
|
||||
|
||||
path = sb.String()
|
||||
appendToNotFound(prop.NotFound("oc:" + pf.Prop[i].Local))
|
||||
}
|
||||
appendToOK(prop.Escaped("oc:downloadURL", publicURL+baseURI+path))
|
||||
|
||||
} else {
|
||||
appendToNotFound(prop.NotFound("oc:" + pf.Prop[i].Local))
|
||||
}
|
||||
@@ -1738,6 +1732,42 @@ func hasPreview(md *provider.ResourceInfo, appendToOK func(p ...prop.PropertyXML
|
||||
}
|
||||
}
|
||||
|
||||
func downloadURL(ctx context.Context, log zerolog.Logger, isPublic bool, path string, ls *link.PublicShare, publicURL string, baseURI string, urlSigner signedurl.Signer) string {
|
||||
switch {
|
||||
case isPublic:
|
||||
var queryString string
|
||||
if !ls.PasswordProtected {
|
||||
queryString = path
|
||||
} else {
|
||||
expiration := time.Unix(int64(ls.Signature.SignatureExpiration.Seconds), int64(ls.Signature.SignatureExpiration.Nanos))
|
||||
var sb strings.Builder
|
||||
|
||||
sb.WriteString(path)
|
||||
sb.WriteString("?signature=")
|
||||
sb.WriteString(ls.Signature.Signature)
|
||||
sb.WriteString("&expiration=")
|
||||
sb.WriteString(url.QueryEscape(expiration.Format(time.RFC3339)))
|
||||
|
||||
queryString = sb.String()
|
||||
}
|
||||
return publicURL + baseURI + queryString
|
||||
case urlSigner != nil:
|
||||
u, ok := ctxpkg.ContextGetUser(ctx)
|
||||
if !ok {
|
||||
log.Error().Msg("could not get user from context for download URL signing")
|
||||
return ""
|
||||
}
|
||||
signedURL, err := urlSigner.Sign(publicURL+baseURI+path, u.Id.OpaqueId, 30*time.Minute)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("failed to sign download URL")
|
||||
return ""
|
||||
} else {
|
||||
return signedURL
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func activeLocks(log *zerolog.Logger, lock *provider.Lock) string {
|
||||
if lock == nil || lock.Type == provider.LockType_LOCK_TYPE_INVALID {
|
||||
return ""
|
||||
|
||||
Generated
Vendored
+1
-1
@@ -147,7 +147,7 @@ func (s *svc) handlePropfindOnToken(w http.ResponseWriter, r *http.Request, ns s
|
||||
prefer := net.ParsePrefer(r.Header.Get("prefer"))
|
||||
returnMinimal := prefer[net.HeaderPreferReturn] == "minimal"
|
||||
|
||||
propRes, err := propfind.MultistatusResponse(ctx, &pf, infos, s.c.PublicURL, ns, nil, returnMinimal)
|
||||
propRes, err := propfind.MultistatusResponse(ctx, &pf, infos, s.c.PublicURL, ns, nil, returnMinimal, nil)
|
||||
if err != nil {
|
||||
sublog.Error().Err(err).Msg("error formatting propfind")
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
|
||||
Generated
Vendored
+1
-1
@@ -117,7 +117,7 @@ func (s *svc) doFilterFiles(w http.ResponseWriter, r *http.Request, ff *reportFi
|
||||
prefer := net.ParsePrefer(r.Header.Get("prefer"))
|
||||
returnMinimal := prefer[net.HeaderPreferReturn] == "minimal"
|
||||
|
||||
responsesXML, err := propfind.MultistatusResponse(ctx, &propfind.XML{Prop: ff.Prop}, infos, s.c.PublicURL, namespace, nil, returnMinimal)
|
||||
responsesXML, err := propfind.MultistatusResponse(ctx, &propfind.XML{Prop: ff.Prop}, infos, s.c.PublicURL, namespace, nil, returnMinimal, nil)
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("error formatting propfind")
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
|
||||
Generated
Vendored
+1
-1
@@ -82,7 +82,7 @@ func (h *SpacesHandler) Handler(s *svc, trashbinHandler *TrashbinHandler) http.H
|
||||
var err error
|
||||
switch r.Method {
|
||||
case MethodPropfind:
|
||||
p := propfind.NewHandler(config.PublicURL, s.gatewaySelector, config)
|
||||
p := propfind.NewHandler(config.PublicURL, s.gatewaySelector, s.urlSigner, config)
|
||||
p.HandleSpacesPropfind(w, r, spaceID)
|
||||
case MethodProppatch:
|
||||
status, err = s.handleSpacesProppatch(w, r, spaceID)
|
||||
|
||||
Generated
Vendored
+1
-1
@@ -200,7 +200,7 @@ func (h *VersionsHandler) doListVersions(w http.ResponseWriter, r *http.Request,
|
||||
prefer := net.ParsePrefer(r.Header.Get("prefer"))
|
||||
returnMinimal := prefer[net.HeaderPreferReturn] == "minimal"
|
||||
|
||||
propRes, err := propfind.MultistatusResponse(ctx, &pf, infos, s.c.PublicURL, "", nil, returnMinimal)
|
||||
propRes, err := propfind.MultistatusResponse(ctx, &pf, infos, s.c.PublicURL, "", nil, returnMinimal, nil)
|
||||
if err != nil {
|
||||
sublog.Error().Err(err).Msg("error formatting propfind")
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
|
||||
Generated
Vendored
+1
-1
@@ -72,7 +72,7 @@ func (h *WebDavHandler) Handler(s *svc) http.Handler {
|
||||
var status int // status 0 means the handler already sent the response
|
||||
switch r.Method {
|
||||
case MethodPropfind:
|
||||
p := propfind.NewHandler(config.PublicURL, s.gatewaySelector, config)
|
||||
p := propfind.NewHandler(config.PublicURL, s.gatewaySelector, s.urlSigner, config)
|
||||
p.HandlePathPropfind(w, r, ns)
|
||||
case MethodLock:
|
||||
status, err = s.handleLock(w, r, ns)
|
||||
|
||||
Reference in New Issue
Block a user