bump to reva 8fb71adbe500

To get: https://github.com/cs3org/reva/pull/4685

Fixes: #9128
Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>
This commit is contained in:
Ralf Haferkamp
2024-05-16 11:50:38 +02:00
parent 58bd67be73
commit f931da5f29
12 changed files with 187 additions and 21 deletions

View File

@@ -27,16 +27,21 @@ import (
providerpb "github.com/cs3org/go-cs3apis/cs3/app/provider/v1beta1"
registry "github.com/cs3org/go-cs3apis/cs3/app/registry/v1beta1"
providerv1beta1 "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"
ocmprovider "github.com/cs3org/go-cs3apis/cs3/ocm/provider/v1beta1"
rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1"
storageprovider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
typespb "github.com/cs3org/go-cs3apis/cs3/types/v1beta1"
"github.com/cs3org/reva/v2/pkg/appctx"
"github.com/cs3org/reva/v2/pkg/auth/scope"
ctxpkg "github.com/cs3org/reva/v2/pkg/ctx"
"github.com/cs3org/reva/v2/pkg/errtypes"
"github.com/cs3org/reva/v2/pkg/rgrpc/status"
"github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool"
"github.com/cs3org/reva/v2/pkg/token"
"github.com/cs3org/reva/v2/pkg/utils"
"github.com/pkg/errors"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
@@ -169,11 +174,9 @@ func (s *svc) openLocalResources(ctx context.Context, ri *storageprovider.Resour
return nil, errors.Wrap(err, "gateway: error calling GetAppProviderClient")
}
appProviderReq := &providerpb.OpenInAppRequest{
ResourceInfo: ri,
ViewMode: providerpb.ViewMode(vm),
AccessToken: accessToken,
Opaque: opaque,
appProviderReq, err := buildOpenInAppRequest(ctx, ri, vm, s.tokenmgr, accessToken, opaque)
if err != nil {
return nil, errors.Wrap(err, "gateway: error building OpenInApp request")
}
res, err := appProviderClient.OpenInApp(ctx, appProviderReq)
@@ -184,6 +187,41 @@ func (s *svc) openLocalResources(ctx context.Context, ri *storageprovider.Resour
return res, nil
}
func buildOpenInAppRequest(ctx context.Context, ri *storageprovider.ResourceInfo, vm gateway.OpenInAppRequest_ViewMode, tokenmgr token.Manager, accessToken string, opaque *typespb.Opaque) (*providerpb.OpenInAppRequest, error) {
// in case of a view only mode and a stat permission we need to create a view only token
if vm == gateway.OpenInAppRequest_VIEW_MODE_VIEW_ONLY && ri.GetPermissionSet().GetStat() {
// Limit scope to the resource
scope, err := scope.AddResourceInfoScope(ri, providerv1beta1.Role_ROLE_VIEWER, nil)
if err != nil {
return nil, err
}
// build a fake user object for the token
currentuser := ctxpkg.ContextMustGetUser(ctx)
scopedUser := &userpb.User{
Id: ri.GetOwner(), // the owner of the resource is always set, right?
DisplayName: "View Only user for " + currentuser.GetUsername(),
}
// mint a view only token
viewOnlyToken, err := tokenmgr.MintToken(ctx, scopedUser, scope)
if err != nil {
return nil, err
}
// TODO we should not append the token to the opaque, we should have a dedicated field in the request
opaque = utils.AppendPlainToOpaque(opaque, "viewOnlyToken", viewOnlyToken)
}
return &providerpb.OpenInAppRequest{
ResourceInfo: ri,
ViewMode: providerpb.ViewMode(vm),
AccessToken: accessToken,
// ViewOnlyToken: viewOnlyToken // scoped to the shared resource if the stat response hase a ViewOnly permission
Opaque: opaque,
}, nil
}
func (s *svc) findAppProvider(ctx context.Context, ri *storageprovider.ResourceInfo, app string) (*registry.ProviderInfo, error) {
c, err := pool.GetAppRegistryClient(s.c.AppRegistryEndpoint)
if err != nil {

View File

@@ -20,6 +20,7 @@ package ocdav
import (
"context"
"fmt"
"net/http"
"path"
"path/filepath"
@@ -28,6 +29,7 @@ import (
gatewayv1beta1 "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1"
userv1beta1 "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
rpc "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"
"github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/config"
"github.com/cs3org/reva/v2/internal/http/services/owncloud/ocdav/errors"
@@ -36,12 +38,18 @@ import (
ctxpkg "github.com/cs3org/reva/v2/pkg/ctx"
"github.com/cs3org/reva/v2/pkg/rgrpc/todo/pool"
"github.com/cs3org/reva/v2/pkg/rhttp/router"
"github.com/cs3org/reva/v2/pkg/storage/utils/grants"
"github.com/cs3org/reva/v2/pkg/storagespace"
"github.com/cs3org/reva/v2/pkg/utils"
"go.opentelemetry.io/otel/trace"
"google.golang.org/grpc/metadata"
)
const (
_trashbinPath = "trash-bin"
// WwwAuthenticate captures the Www-Authenticate header string.
WwwAuthenticate = "Www-Authenticate"
)
// DavHandler routes to the different sub handlers
@@ -248,6 +256,39 @@ func (h *DavHandler) Handler(s *svc) http.Handler {
var hasValidBasicAuthHeader bool
var pass string
var err error
// If user is authenticated
_, userExists := ctxpkg.ContextGetUser(ctx)
if userExists {
client, err := s.gatewaySelector.Next()
if err != nil {
log.Error().Err(err).Msg("error sending grpc stat request")
w.WriteHeader(http.StatusInternalServerError)
return
}
psRes, err := client.GetPublicShare(ctx, &link.GetPublicShareRequest{
Ref: &link.PublicShareReference{
Spec: &link.PublicShareReference_Token{
Token: token,
},
}})
if err != nil && !strings.Contains(err.Error(), "core access token not found") {
log.Error().Err(err).Msg("error sending grpc stat request")
w.WriteHeader(http.StatusInternalServerError)
return
}
// If the link is internal then 307 redirect
if psRes.Status.Code == rpc.Code_CODE_OK && grants.PermissionsEqual(psRes.Share.Permissions.GetPermissions(), &provider.ResourcePermissions{}) {
if psRes.GetShare().GetResourceId() != nil {
rUrl := path.Join("/dav/spaces", storagespace.FormatResourceID(*psRes.GetShare().GetResourceId()))
http.Redirect(w, r, rUrl, http.StatusTemporaryRedirect)
return
}
log.Debug().Str("token", token).Interface("status", res.Status).Msg("resource id not found")
w.WriteHeader(http.StatusNotFound)
return
}
}
if _, pass, hasValidBasicAuthHeader = r.BasicAuth(); hasValidBasicAuthHeader {
res, err = handleBasicAuth(r.Context(), s.gatewaySelector, token, pass)
} else {
@@ -286,6 +327,17 @@ func (h *DavHandler) Handler(s *svc) http.Handler {
return
}
if userExists {
// Build new context without an authenticated user.
// the public link should be resolved by the 'publicshares' authenticated user
baseURI := ctx.Value(net.CtxKeyBaseURI).(string)
logger := appctx.GetLogger(ctx)
span := trace.SpanFromContext(ctx)
span.End()
ctx = trace.ContextWithSpan(context.Background(), span)
ctx = appctx.WithLogger(ctx, logger)
ctx = context.WithValue(ctx, net.CtxKeyBaseURI, baseURI)
}
ctx = ctxpkg.ContextSetToken(ctx, res.Token)
ctx = ctxpkg.ContextSetUser(ctx, res.User)
ctx = metadata.AppendToOutgoingContext(ctx, ctxpkg.TokenHeader, res.Token)
@@ -301,6 +353,16 @@ func (h *DavHandler) Handler(s *svc) http.Handler {
return
case sRes.Status.Code == rpc.Code_CODE_PERMISSION_DENIED:
fallthrough
case sRes.Status.Code == rpc.Code_CODE_OK && grants.PermissionsEqual(sRes.GetInfo().GetPermissionSet(), &provider.ResourcePermissions{}):
// If the link is internal
if !userExists {
w.Header().Add(WwwAuthenticate, fmt.Sprintf("Bearer realm=\"%s\", charset=\"UTF-8\"", r.Host))
w.WriteHeader(http.StatusUnauthorized)
b, err := errors.Marshal(http.StatusUnauthorized, "No 'Authorization: Bearer' header found", "")
errors.HandleWebdavError(log, w, b, err)
return
}
fallthrough
case sRes.Status.Code == rpc.Code_CODE_NOT_FOUND:
log.Debug().Str("token", token).Interface("status", res.Status).Msg("resource not found")
w.WriteHeader(http.StatusNotFound) // log the difference

View File

@@ -89,6 +89,21 @@ var (
"latitude",
"longitude",
}
imageKeys = []string{
"width",
"height",
}
photoKeys = []string{
"cameraMake",
"cameraModel",
"exposureDenominator",
"exposureNumerator",
"fNumber",
"focalLength",
"iso",
"orientation",
"takenDateTime",
}
)
type countingReader struct {
@@ -853,6 +868,10 @@ func metadataKeys(pf XML) ([]string, []string) {
metadataKeys = append(metadataKeys, metadataKeysWithPrefix("libre.graph.audio", audioKeys)...)
case "http://owncloud.org/ns/location":
metadataKeys = append(metadataKeys, metadataKeysWithPrefix("libre.graph.location", locationKeys)...)
case "http://owncloud.org/ns/image":
metadataKeys = append(metadataKeys, metadataKeysWithPrefix("libre.graph.image", imageKeys)...)
case "http://owncloud.org/ns/photo":
metadataKeys = append(metadataKeys, metadataKeysWithPrefix("libre.graph.photo", photoKeys)...)
default:
metadataKeys = append(metadataKeys, key)
}
@@ -910,7 +929,7 @@ func requiresExplicitFetching(n *xml.Name) bool {
}
case net.NsOwncloud:
switch n.Local {
case "favorite", "share-types", "checksums", "size", "tags", "audio", "location":
case "favorite", "share-types", "checksums", "size", "tags", "audio", "location", "image", "photo":
return true
default:
return false
@@ -1253,6 +1272,8 @@ func mdToPropResponse(ctx context.Context, pf *XML, md *provider.ResourceInfo, p
propstatOK.Prop = append(propstatOK.Prop, prop.Raw("oc:tags", k["tags"]))
appendMetadataProp(k, "oc", "audio", "libre.graph.audio", audioKeys)
appendMetadataProp(k, "oc", "location", "libre.graph.location", locationKeys)
appendMetadataProp(k, "oc", "image", "libre.graph.image", imageKeys)
appendMetadataProp(k, "oc", "photo", "libre.graph.photo", photoKeys)
}
// ls do not report any properties as missing by default
@@ -1534,6 +1555,14 @@ func mdToPropResponse(ctx context.Context, pf *XML, md *provider.ResourceInfo, p
if k := md.GetArbitraryMetadata().GetMetadata(); k != nil {
appendMetadataProp(k, "oc", "location", "libre.graph.location", locationKeys)
}
case "image":
if k := md.GetArbitraryMetadata().GetMetadata(); k != nil {
appendMetadataProp(k, "oc", "image", "libre.graph.image", imageKeys)
}
case "photo":
if k := md.GetArbitraryMetadata().GetMetadata(); k != nil {
appendMetadataProp(k, "oc", "photo", "libre.graph.photo", photoKeys)
}
case "name":
appendToOK(prop.Escaped("oc:name", md.Name))
case "shareid":

View File

@@ -48,7 +48,9 @@ const (
RoleFileEditor = "file-editor"
// RoleCoowner grants co-owner permissions on a resource.
RoleCoowner = "coowner"
// RoleUploader grants uploader permission to upload onto a resource.
// RoleEditorLite grants permission to upload and download to a resource.
RoleEditorLite = "editor-lite"
// RoleUploader grants uploader permission to upload onto a resource (no download).
RoleUploader = "uploader"
// RoleManager grants manager permissions on a resource. Semantically equivalent to co-owner.
RoleManager = "manager"
@@ -313,7 +315,24 @@ func NewCoownerRole() *Role {
}
}
// NewUploaderRole creates an uploader role
// NewEditorLiteRole creates an editor-lite role
func NewEditorLiteRole() *Role {
return &Role{
Name: RoleEditorLite,
cS3ResourcePermissions: &provider.ResourcePermissions{
Stat: true,
GetPath: true,
CreateContainer: true,
InitiateFileUpload: true,
InitiateFileDownload: true,
ListContainer: true,
Move: true,
},
ocsPermissions: PermissionCreate,
}
}
// NewUploaderRole creates an uploader role with no download permissions
func NewUploaderRole() *Role {
return &Role{
Name: RoleUploader,
@@ -524,6 +543,10 @@ func RoleFromResourcePermissions(rp *provider.ResourcePermissions, islink bool)
}
}
if r.ocsPermissions == PermissionCreate {
if rp.GetPath && rp.InitiateFileDownload && rp.ListContainer && rp.Move {
r.Name = RoleEditorLite
return r
}
r.Name = RoleUploader
return r
}

View File

@@ -794,6 +794,7 @@ var mimeTypes = map[string]string{
"s3m": "audio/s3m",
"saf": "application/vnd.yamaha.smaf-audio",
"sass": "text/x-sass",
"sb3": "application/x.scratch.sb3",
"sbml": "application/sbml+xml",
"sc": "application/vnd.ibm.secure-container",
"scd": "application/x-msschedule",

View File

@@ -53,7 +53,7 @@ const serverStateMetadata = "METADATA"
var serverState = serverStateEmpty
var responses = map[string]Response{
`POST /apps/sciencemesh/~f7fbf8c8-139b-4376-b307-cf0a8c2d0d9c/api/storage/AddGrant {"ref":{"path":"/subdir"},"g":{"grantee":{"type":1,"Id":{"UserId":{"opaque_id":"4c510ada-c86b-4815-8820-42cdf82c3d51"}}},"permissions":{"move":true,"stat":true}}} EMPTY`: {200, ``, serverStateGrantAdded},
`POST /apps/sciencemesh/~f7fbf8c8-139b-4376-b307-cf0a8c2d0d9c/api/storage/AddGrant {"ref":{"path":"/subdir"},"g":{"grantee":{"type":1,"Id":{"UserId":{"opaque_id":"4c510ada-c86b-4815-8820-42cdf82c3d51"}}},"permissions":{"initiate_file_download":true,"move":true,"stat":true}}} EMPTY`: {200, ``, serverStateGrantAdded},
`POST /apps/sciencemesh/~f7fbf8c8-139b-4376-b307-cf0a8c2d0d9c/api/storage/CreateDir {"path":"/subdir"} EMPTY`: {200, ``, serverStateSubdir},
`POST /apps/sciencemesh/~f7fbf8c8-139b-4376-b307-cf0a8c2d0d9c/api/storage/CreateDir {"path":"/subdir"} HOME`: {200, ``, serverStateSubdir},
@@ -149,7 +149,7 @@ var responses = map[string]Response{
`POST /apps/sciencemesh/~f7fbf8c8-139b-4376-b307-cf0a8c2d0d9c/api/storage/UnsetArbitraryMetadata {"ref":{"path":"/subdir"},"keys":["foo"]}`: {200, ``, serverStateSubdir},
`POST /apps/sciencemesh/~f7fbf8c8-139b-4376-b307-cf0a8c2d0d9c/api/storage/UpdateGrant {"ref":{"path":"/subdir"},"g":{"grantee":{"type":1,"Id":{"UserId":{"opaque_id":"4c510ada-c86b-4815-8820-42cdf82c3d51"}}},"permissions":{"delete":true,"move":true,"stat":true}}}`: {200, ``, serverStateGrantUpdated},
`POST /apps/sciencemesh/~f7fbf8c8-139b-4376-b307-cf0a8c2d0d9c/api/storage/UpdateGrant {"ref":{"path":"/subdir"},"g":{"grantee":{"type":1,"Id":{"UserId":{"opaque_id":"4c510ada-c86b-4815-8820-42cdf82c3d51"}}},"permissions":{"delete":true,"initiate_file_download":true,"move":true,"stat":true}}}`: {200, ``, serverStateGrantUpdated},
`POST /apps/sciencemesh/~tester/api/storage/GetHome `: {200, `yes we are`, serverStateHome},
`POST /apps/sciencemesh/~tester/api/storage/CreateHome `: {201, ``, serverStateEmpty},

View File

@@ -316,10 +316,15 @@ func (e *ACE) granteeType() provider.GranteeType {
// grantPermissionSet returns the set of CS3 resource permissions representing the ACE
func (e *ACE) grantPermissionSet() *provider.ResourcePermissions {
p := &provider.ResourcePermissions{}
// r
if strings.Contains(e.permissions, "r") {
// t
if strings.Contains(e.permissions, "t") {
p.Stat = true
p.GetPath = true
}
// r
if strings.Contains(e.permissions, "r") {
p.Stat = true // currently assumed
p.GetPath = true // currently assumed
p.InitiateFileDownload = true
p.ListContainer = true
}
@@ -336,10 +341,9 @@ func (e *ACE) grantPermissionSet() *provider.ResourcePermissions {
p.CreateContainer = true
}
// x
// if strings.Contains(e.Permissions, "x") {
// TODO execute file permission?
// TODO change directory permission?
// }
if strings.Contains(e.permissions, "x") {
p.ListContainer = true
}
// d
if strings.Contains(e.permissions, "d") {
p.Delete = true
@@ -436,10 +440,17 @@ func unmarshalKV(s string) (*ACE, error) {
return e, nil
}
// getACEPerm produces an NFSv4.x inspired permission string from a CS3 resource permissions set
func getACEPerm(set *provider.ResourcePermissions) string {
var b strings.Builder
if set.Stat || set.InitiateFileDownload || set.ListContainer || set.GetPath {
if set.Stat || set.GetPath {
b.WriteString("t")
}
if set.ListContainer { // we have no dedicated traversal permission, but to listing a container allows traversing it
b.WriteString("x")
}
if set.InitiateFileDownload {
b.WriteString("r")
}
if set.InitiateFileUpload || set.Move {

View File

@@ -100,6 +100,7 @@ func ServiceAccountPermissions() provider.ResourcePermissions {
RestoreRecycleItem: true, // for cli restore command
Delete: true, // for cli restore command with replace option
CreateContainer: true, // for space provisioning
AddGrant: true, // for initial project space member assignment
}
}