Merge pull request #3567 from wkloucek/proxy-thumbnails
add more proxy routes for thumbnails
This commit is contained in:
@@ -0,0 +1,6 @@
|
|||||||
|
Bugfix: Thumbnails for `/dav/xxx?preview=1` requests
|
||||||
|
|
||||||
|
We've added the thumbnail rendering for `/dav/xxx?preview=1`, `/remote.php/webdav/{relative path}?preview=1` and `/webdav/{relative path}?preview=1` requests, which was previously not supported because of missing routes. It now returns the same thumbnails as for
|
||||||
|
`/remote.php/dav/xxx?preview=1`.
|
||||||
|
|
||||||
|
https://github.com/owncloud/ocis/pull/3567
|
||||||
@@ -45,15 +45,15 @@ type Policy struct {
|
|||||||
|
|
||||||
// Route defines forwarding routes
|
// Route defines forwarding routes
|
||||||
type Route struct {
|
type Route struct {
|
||||||
Type RouteType `yaml:"type"`
|
Type RouteType `yaml:"type,omitempty"`
|
||||||
// Method optionally limits the route to this HTTP method
|
// Method optionally limits the route to this HTTP method
|
||||||
Method string `yaml:"method"`
|
Method string `yaml:"method,omitempty"`
|
||||||
Endpoint string `yaml:"endpoint"`
|
Endpoint string `yaml:"endpoint,omitempty"`
|
||||||
// Backend is a static URL to forward the request to
|
// Backend is a static URL to forward the request to
|
||||||
Backend string `yaml:"backend"`
|
Backend string `yaml:"backend,omitempty"`
|
||||||
// Service name to look up in the registry
|
// Service name to look up in the registry
|
||||||
Service string `yaml:"service"`
|
Service string `yaml:"service,omitempty"`
|
||||||
ApacheVHost bool `yaml:"apache_vhost"`
|
ApacheVHost bool `yaml:"apache_vhost,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// RouteType defines the type of a route
|
// RouteType defines the type of a route
|
||||||
|
|||||||
@@ -106,6 +106,16 @@ func DefaultPolicies() []config.Policy {
|
|||||||
Endpoint: "/remote.php/dav/",
|
Endpoint: "/remote.php/dav/",
|
||||||
Backend: "http://localhost:9115", // TODO use registry?
|
Backend: "http://localhost:9115", // TODO use registry?
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Type: config.QueryRoute,
|
||||||
|
Endpoint: "/dav/?preview=1",
|
||||||
|
Backend: "http://localhost:9115",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Type: config.QueryRoute,
|
||||||
|
Endpoint: "/webdav/?preview=1",
|
||||||
|
Backend: "http://localhost:9115",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
Endpoint: "/remote.php/",
|
Endpoint: "/remote.php/",
|
||||||
Service: "ocdav",
|
Service: "ocdav",
|
||||||
|
|||||||
@@ -115,6 +115,7 @@ func (m basicAuth) isPublicLink(req *http.Request) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
publicPaths := []string{
|
publicPaths := []string{
|
||||||
|
"/dav/public-files/",
|
||||||
"/remote.php/dav/public-files/",
|
"/remote.php/dav/public-files/",
|
||||||
"/remote.php/ocs/apps/files_sharing/api/v1/tokeninfo/unprotected",
|
"/remote.php/ocs/apps/files_sharing/api/v1/tokeninfo/unprotected",
|
||||||
"/ocs/v1.php/cloud/capabilities",
|
"/ocs/v1.php/cloud/capabilities",
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
package constants
|
||||||
|
|
||||||
|
type contextKey int
|
||||||
|
|
||||||
|
const (
|
||||||
|
ContextKeyID contextKey = iota
|
||||||
|
ContextKeyPath
|
||||||
|
)
|
||||||
@@ -7,9 +7,10 @@ import (
|
|||||||
"net/url"
|
"net/url"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/go-chi/chi/v5"
|
"github.com/go-chi/chi/v5"
|
||||||
|
|
||||||
|
"github.com/owncloud/ocis/v2/extensions/webdav/pkg/constants"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -39,12 +40,20 @@ type ThumbnailRequest struct {
|
|||||||
|
|
||||||
// ParseThumbnailRequest extracts all required parameters from a http request.
|
// ParseThumbnailRequest extracts all required parameters from a http request.
|
||||||
func ParseThumbnailRequest(r *http.Request) (*ThumbnailRequest, error) {
|
func ParseThumbnailRequest(r *http.Request) (*ThumbnailRequest, error) {
|
||||||
fp, id, err := extractFilePath(r)
|
ctx := r.Context()
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
q := r.URL.Query()
|
|
||||||
|
|
||||||
|
fp := ctx.Value(constants.ContextKeyPath).(string)
|
||||||
|
if fp == "" {
|
||||||
|
return nil, errors.New("invalid file path")
|
||||||
|
}
|
||||||
|
|
||||||
|
id := ""
|
||||||
|
v := ctx.Value(constants.ContextKeyID)
|
||||||
|
if v != nil {
|
||||||
|
id = v.(string)
|
||||||
|
}
|
||||||
|
|
||||||
|
q := r.URL.Query()
|
||||||
width, height, err := parseDimensions(q)
|
width, height, err := parseDimensions(q)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -61,32 +70,6 @@ func ParseThumbnailRequest(r *http.Request) (*ThumbnailRequest, error) {
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// the url looks as followed
|
|
||||||
//
|
|
||||||
// /remote.php/dav/files/<user>/<filepath>
|
|
||||||
//
|
|
||||||
// User and filepath are dynamic and filepath can contain slashes
|
|
||||||
// So using the URLParam function is not possible.
|
|
||||||
func extractFilePath(r *http.Request) (string, string, error) {
|
|
||||||
id := chi.URLParam(r, "id")
|
|
||||||
id, err := url.QueryUnescape(id)
|
|
||||||
if err != nil {
|
|
||||||
return "", "", errors.New("could not unescape user")
|
|
||||||
}
|
|
||||||
if id != "" {
|
|
||||||
parts := strings.SplitN(r.URL.Path, id, 2)
|
|
||||||
return parts[1], id, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is for public links
|
|
||||||
token := chi.URLParam(r, "token")
|
|
||||||
if token != "" {
|
|
||||||
parts := strings.SplitN(r.URL.Path, token, 2)
|
|
||||||
return parts[1], "", nil
|
|
||||||
}
|
|
||||||
return "", "", errors.New("could not extract file path")
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseDimensions(q url.Values) (int64, int64, error) {
|
func parseDimensions(q url.Values) (int64, int64, error) {
|
||||||
width, err := parseDimension(q.Get("x"), "width", DefaultWidth)
|
width, err := parseDimension(q.Get("x"), "width", DefaultWidth)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
package svc
|
package svc
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"encoding/xml"
|
"encoding/xml"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/url"
|
||||||
"path"
|
"path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -13,16 +15,16 @@ import (
|
|||||||
rpcv1beta1 "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1"
|
rpcv1beta1 "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1"
|
||||||
"github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool"
|
"github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool"
|
||||||
"github.com/cs3org/reva/v2/pkg/storage/utils/templates"
|
"github.com/cs3org/reva/v2/pkg/storage/utils/templates"
|
||||||
|
"github.com/go-chi/chi/v5"
|
||||||
"github.com/go-chi/render"
|
"github.com/go-chi/render"
|
||||||
merrors "go-micro.dev/v4/errors"
|
merrors "go-micro.dev/v4/errors"
|
||||||
"google.golang.org/grpc/metadata"
|
"google.golang.org/grpc/metadata"
|
||||||
|
|
||||||
|
"github.com/owncloud/ocis/v2/extensions/webdav/pkg/config"
|
||||||
|
"github.com/owncloud/ocis/v2/extensions/webdav/pkg/constants"
|
||||||
|
"github.com/owncloud/ocis/v2/extensions/webdav/pkg/dav/requests"
|
||||||
"github.com/owncloud/ocis/v2/ocis-pkg/log"
|
"github.com/owncloud/ocis/v2/ocis-pkg/log"
|
||||||
"github.com/owncloud/ocis/v2/ocis-pkg/service/grpc"
|
"github.com/owncloud/ocis/v2/ocis-pkg/service/grpc"
|
||||||
|
|
||||||
"github.com/go-chi/chi/v5"
|
|
||||||
"github.com/owncloud/ocis/v2/extensions/webdav/pkg/config"
|
|
||||||
"github.com/owncloud/ocis/v2/extensions/webdav/pkg/dav/requests"
|
|
||||||
thumbnailsmsg "github.com/owncloud/ocis/v2/protogen/gen/ocis/messages/thumbnails/v0"
|
thumbnailsmsg "github.com/owncloud/ocis/v2/protogen/gen/ocis/messages/thumbnails/v0"
|
||||||
searchsvc "github.com/owncloud/ocis/v2/protogen/gen/ocis/services/search/v0"
|
searchsvc "github.com/owncloud/ocis/v2/protogen/gen/ocis/services/search/v0"
|
||||||
thumbnailssvc "github.com/owncloud/ocis/v2/protogen/gen/ocis/services/thumbnails/v0"
|
thumbnailssvc "github.com/owncloud/ocis/v2/protogen/gen/ocis/services/thumbnails/v0"
|
||||||
@@ -73,10 +75,32 @@ func NewService(opts ...Option) (Service, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
m.Route(options.Config.HTTP.Root, func(r chi.Router) {
|
m.Route(options.Config.HTTP.Root, func(r chi.Router) {
|
||||||
r.Get("/remote.php/dav/spaces/{id}/*", svc.SpacesThumbnail)
|
|
||||||
r.Get("/remote.php/dav/files/{id}/*", svc.Thumbnail)
|
r.Group(func(r chi.Router) {
|
||||||
r.Get("/remote.php/dav/public-files/{token}/*", svc.PublicThumbnail)
|
r.Use(svc.DavUserContext())
|
||||||
r.Head("/remote.php/dav/public-files/{token}/*", svc.PublicThumbnailHead)
|
|
||||||
|
r.Get("/remote.php/dav/spaces/{id}/*", svc.SpacesThumbnail)
|
||||||
|
r.Get("/dav/spaces/{id}/*", svc.SpacesThumbnail)
|
||||||
|
|
||||||
|
r.Get("/remote.php/dav/files/{id}/*", svc.Thumbnail)
|
||||||
|
r.Get("/dav/files/{id}/*", svc.Thumbnail)
|
||||||
|
})
|
||||||
|
|
||||||
|
r.Group(func(r chi.Router) {
|
||||||
|
r.Use(svc.DavPublicContext())
|
||||||
|
|
||||||
|
r.Head("/remote.php/dav/public-files/{token}/*", svc.PublicThumbnailHead)
|
||||||
|
r.Head("/dav/public-files/{token}/*", svc.PublicThumbnailHead)
|
||||||
|
|
||||||
|
r.Get("/remote.php/dav/public-files/{token}/*", svc.PublicThumbnail)
|
||||||
|
r.Get("/dav/public-files/{token}/*", svc.PublicThumbnail)
|
||||||
|
})
|
||||||
|
|
||||||
|
r.Group(func(r chi.Router) {
|
||||||
|
r.Use(svc.WebDAVContext())
|
||||||
|
r.Get("/remote.php/webdav/*", svc.Thumbnail)
|
||||||
|
r.Get("/webdav/*", svc.Thumbnail)
|
||||||
|
})
|
||||||
|
|
||||||
// r.MethodFunc("REPORT", "/remote.php/dav/files/{id}/*", svc.Search)
|
// r.MethodFunc("REPORT", "/remote.php/dav/files/{id}/*", svc.Search)
|
||||||
|
|
||||||
@@ -114,6 +138,67 @@ func (g Webdav) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||||||
g.mux.ServeHTTP(w, r)
|
g.mux.ServeHTTP(w, r)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (g Webdav) DavUserContext() func(next http.Handler) http.Handler {
|
||||||
|
return func(next http.Handler) http.Handler {
|
||||||
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
ctx := r.Context()
|
||||||
|
filePath := r.URL.Path
|
||||||
|
|
||||||
|
id := chi.URLParam(r, "id")
|
||||||
|
id, err := url.QueryUnescape(id)
|
||||||
|
if err == nil && id != "" {
|
||||||
|
ctx = context.WithValue(ctx, constants.ContextKeyID, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
if id != "" {
|
||||||
|
filePath = strings.TrimPrefix(filePath, path.Join("/remote.php/dav/spaces", id)+"/")
|
||||||
|
filePath = strings.TrimPrefix(filePath, path.Join("/dav/spaces", id)+"/")
|
||||||
|
|
||||||
|
filePath = strings.TrimPrefix(filePath, path.Join("/remote.php/dav/files", id)+"/")
|
||||||
|
filePath = strings.TrimPrefix(filePath, path.Join("/dav/files", id)+"/")
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx = context.WithValue(ctx, constants.ContextKeyPath, filePath)
|
||||||
|
|
||||||
|
next.ServeHTTP(w, r.WithContext(ctx))
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g Webdav) DavPublicContext() func(next http.Handler) http.Handler {
|
||||||
|
return func(next http.Handler) http.Handler {
|
||||||
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
ctx := r.Context()
|
||||||
|
filePath := r.URL.Path
|
||||||
|
|
||||||
|
if token := chi.URLParam(r, "token"); token != "" {
|
||||||
|
filePath = strings.TrimPrefix(filePath, path.Join("/remote.php/dav/public-files", token)+"/")
|
||||||
|
filePath = strings.TrimPrefix(filePath, path.Join("/dav/public-files", token)+"/")
|
||||||
|
}
|
||||||
|
ctx = context.WithValue(ctx, constants.ContextKeyPath, filePath)
|
||||||
|
|
||||||
|
next.ServeHTTP(w, r.WithContext(ctx))
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func (g Webdav) WebDAVContext() func(next http.Handler) http.Handler {
|
||||||
|
return func(next http.Handler) http.Handler {
|
||||||
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
|
filePath := r.URL.Path
|
||||||
|
filePath = strings.TrimPrefix(filePath, "/remote.php")
|
||||||
|
filePath = strings.TrimPrefix(filePath, "/webdav/")
|
||||||
|
|
||||||
|
ctx := context.WithValue(r.Context(), constants.ContextKeyPath, filePath)
|
||||||
|
|
||||||
|
next.ServeHTTP(w, r.WithContext(ctx))
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// SpacesThumbnail is the endpoint for retrieving thumbnails inside of spaces.
|
// SpacesThumbnail is the endpoint for retrieving thumbnails inside of spaces.
|
||||||
func (g Webdav) SpacesThumbnail(w http.ResponseWriter, r *http.Request) {
|
func (g Webdav) SpacesThumbnail(w http.ResponseWriter, r *http.Request) {
|
||||||
tr, err := requests.ParseThumbnailRequest(r)
|
tr, err := requests.ParseThumbnailRequest(r)
|
||||||
@@ -166,18 +251,36 @@ func (g Webdav) Thumbnail(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
t := r.Header.Get(TokenHeader)
|
t := r.Header.Get(TokenHeader)
|
||||||
ctx := metadata.AppendToOutgoingContext(r.Context(), TokenHeader, t)
|
|
||||||
userRes, err := g.revaClient.GetUserByClaim(ctx, &userv1beta1.GetUserByClaimRequest{
|
var user *userv1beta1.User
|
||||||
Claim: "username",
|
|
||||||
Value: tr.Identifier,
|
if tr.Identifier == "" {
|
||||||
})
|
// look up user from token via WhoAmI
|
||||||
if err != nil || userRes.Status.Code != rpcv1beta1.Code_CODE_OK {
|
userRes, err := g.revaClient.WhoAmI(r.Context(), &gatewayv1beta1.WhoAmIRequest{
|
||||||
g.log.Error().Err(err).Msg("could not get user")
|
Token: t,
|
||||||
renderError(w, r, errInternalError("could not get user"))
|
})
|
||||||
return
|
if err != nil || userRes.Status.Code != rpcv1beta1.Code_CODE_OK {
|
||||||
|
g.log.Error().Err(err).Msg("could not get user")
|
||||||
|
renderError(w, r, errInternalError("could not get user"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
user = userRes.GetUser()
|
||||||
|
} else {
|
||||||
|
// look up user from URL via GetUserByClaim
|
||||||
|
ctx := metadata.AppendToOutgoingContext(r.Context(), TokenHeader, t)
|
||||||
|
userRes, err := g.revaClient.GetUserByClaim(ctx, &userv1beta1.GetUserByClaimRequest{
|
||||||
|
Claim: "username",
|
||||||
|
Value: tr.Identifier,
|
||||||
|
})
|
||||||
|
if err != nil || userRes.Status.Code != rpcv1beta1.Code_CODE_OK {
|
||||||
|
g.log.Error().Err(err).Msg("could not get user")
|
||||||
|
renderError(w, r, errInternalError("could not get user"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
user = userRes.GetUser()
|
||||||
}
|
}
|
||||||
|
|
||||||
fullPath := filepath.Join(templates.WithUser(userRes.User, g.config.WebdavNamespace), tr.Filepath)
|
fullPath := filepath.Join(templates.WithUser(user, g.config.WebdavNamespace), tr.Filepath)
|
||||||
rsp, err := g.thumbnailsClient.GetThumbnail(r.Context(), &thumbnailssvc.GetThumbnailRequest{
|
rsp, err := g.thumbnailsClient.GetThumbnail(r.Context(), &thumbnailssvc.GetThumbnailRequest{
|
||||||
Filepath: strings.TrimLeft(tr.Filepath, "/"),
|
Filepath: strings.TrimLeft(tr.Filepath, "/"),
|
||||||
ThumbnailType: extensionToThumbnailType(strings.TrimLeft(tr.Extension, ".")),
|
ThumbnailType: extensionToThumbnailType(strings.TrimLeft(tr.Extension, ".")),
|
||||||
|
|||||||
Reference in New Issue
Block a user