mirror of
https://github.com/opencloud-eu/opencloud.git
synced 2026-04-14 04:17:36 -04:00
do not automatically expand drive root permissions
Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>
This commit is contained in:
committed by
Ralf Haferkamp
parent
7e1a21994b
commit
981e8fe5a3
@@ -13,6 +13,7 @@ import (
|
||||
"github.com/opencloud-eu/opencloud/pkg/log"
|
||||
"github.com/opencloud-eu/opencloud/pkg/shared"
|
||||
"github.com/opencloud-eu/opencloud/services/graph/pkg/errorcode"
|
||||
"github.com/opencloud-eu/opencloud/services/graph/pkg/odata"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/rgrpc/todo/pool"
|
||||
libregraph "github.com/opencloud-eu/libre-graph-api-go"
|
||||
)
|
||||
@@ -81,7 +82,7 @@ func (i *CS3) GetUsers(ctx context.Context, oreq *godata.GoDataRequest) ([]*libr
|
||||
return nil, errorcode.New(errorcode.ServiceNotAvailable, err.Error())
|
||||
}
|
||||
|
||||
search, err := GetSearchValues(oreq.Query)
|
||||
search, err := odata.GetSearchValues(oreq.Query)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -132,7 +133,7 @@ func (i *CS3) GetGroups(ctx context.Context, oreq *godata.GoDataRequest) ([]*lib
|
||||
return nil, errorcode.New(errorcode.ServiceNotAvailable, err.Error())
|
||||
}
|
||||
|
||||
search, err := GetSearchValues(oreq.Query)
|
||||
search, err := odata.GetSearchValues(oreq.Query)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ import (
|
||||
"github.com/opencloud-eu/opencloud/pkg/log"
|
||||
"github.com/opencloud-eu/opencloud/services/graph/pkg/config"
|
||||
"github.com/opencloud-eu/opencloud/services/graph/pkg/errorcode"
|
||||
"github.com/opencloud-eu/opencloud/services/graph/pkg/odata"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -563,7 +564,7 @@ func (i *LDAP) GetUser(ctx context.Context, nameOrID string, oreq *godata.GoData
|
||||
}
|
||||
}
|
||||
|
||||
exp, err := GetExpandValues(oreq.Query)
|
||||
exp, err := odata.GetExpandValues(oreq.Query)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -593,12 +594,12 @@ func (i *LDAP) FilterUsers(ctx context.Context, oreq *godata.GoDataRequest, filt
|
||||
return nil, err
|
||||
}
|
||||
|
||||
search, err := GetSearchValues(oreq.Query)
|
||||
search, err := odata.GetSearchValues(oreq.Query)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
exp, err := GetExpandValues(oreq.Query)
|
||||
exp, err := odata.GetExpandValues(oreq.Query)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ import (
|
||||
libregraph "github.com/opencloud-eu/libre-graph-api-go"
|
||||
|
||||
"github.com/opencloud-eu/opencloud/services/graph/pkg/errorcode"
|
||||
"github.com/opencloud-eu/opencloud/services/graph/pkg/odata"
|
||||
)
|
||||
|
||||
type groupAttributeMap struct {
|
||||
@@ -59,17 +60,17 @@ func (i *LDAP) GetGroups(ctx context.Context, oreq *godata.GoDataRequest) ([]*li
|
||||
logger := i.logger.SubloggerWithRequestID(ctx)
|
||||
logger.Debug().Str("backend", "ldap").Msg("GetGroups")
|
||||
|
||||
search, err := GetSearchValues(oreq.Query)
|
||||
search, err := odata.GetSearchValues(oreq.Query)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var expandMembers bool
|
||||
exp, err := GetExpandValues(oreq.Query)
|
||||
exp, err := odata.GetExpandValues(oreq.Query)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sel, err := GetSelectValues(oreq.Query)
|
||||
sel, err := odata.GetSelectValues(oreq.Query)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -146,7 +147,7 @@ func (i *LDAP) GetGroupMembers(ctx context.Context, groupID string, req *godata.
|
||||
logger := i.logger.SubloggerWithRequestID(ctx)
|
||||
logger.Debug().Str("backend", "ldap").Msg("GetGroupMembers")
|
||||
|
||||
exp, err := GetExpandValues(req.Query)
|
||||
exp, err := odata.GetExpandValues(req.Query)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -156,7 +157,7 @@ func (i *LDAP) GetGroupMembers(ctx context.Context, groupID string, req *godata.
|
||||
return nil, err
|
||||
}
|
||||
|
||||
searchTerm, err := GetSearchValues(req.Query)
|
||||
searchTerm, err := odata.GetSearchValues(req.Query)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -1,63 +0,0 @@
|
||||
package identity
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/CiscoM31/godata"
|
||||
)
|
||||
|
||||
// GetExpandValues extracts the values of the $expand query parameter and
|
||||
// returns them in a []string, rejects any $expand value that consists of more
|
||||
// than just a single path segment
|
||||
func GetExpandValues(req *godata.GoDataQuery) ([]string, error) {
|
||||
if req == nil || req.Expand == nil {
|
||||
return []string{}, nil
|
||||
}
|
||||
expand := make([]string, 0, len(req.Expand.ExpandItems))
|
||||
for _, item := range req.Expand.ExpandItems {
|
||||
if item.Filter != nil || item.At != nil || item.Search != nil ||
|
||||
item.OrderBy != nil || item.Skip != nil || item.Top != nil ||
|
||||
item.Select != nil || item.Compute != nil || item.Expand != nil ||
|
||||
item.Levels != 0 {
|
||||
return []string{}, godata.NotImplementedError("options for $expand not supported")
|
||||
}
|
||||
if len(item.Path) > 1 {
|
||||
return []string{}, godata.NotImplementedError("multiple segments in $expand not supported")
|
||||
}
|
||||
expand = append(expand, item.Path[0].Value)
|
||||
}
|
||||
return expand, nil
|
||||
}
|
||||
|
||||
// GetSelectValues extracts the values of the $select query parameter and
|
||||
// returns them in a []string, rejects any $select value that consists of more
|
||||
// than just a single path segment
|
||||
func GetSelectValues(req *godata.GoDataQuery) ([]string, error) {
|
||||
if req == nil || req.Select == nil {
|
||||
return []string{}, nil
|
||||
}
|
||||
sel := make([]string, 0, len(req.Select.SelectItems))
|
||||
for _, item := range req.Select.SelectItems {
|
||||
if len(item.Segments) > 1 {
|
||||
return []string{}, godata.NotImplementedError("multiple segments in $select not supported")
|
||||
}
|
||||
sel = append(sel, item.Segments[0].Value)
|
||||
}
|
||||
return sel, nil
|
||||
}
|
||||
|
||||
// GetSearchValues extracts the value of the $search query parameter and returns
|
||||
// it as a string. Rejects any search query that is more than just a simple string
|
||||
func GetSearchValues(req *godata.GoDataQuery) (string, error) {
|
||||
if req == nil || req.Search == nil {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
// Only allow simple search queries for now
|
||||
if len(req.Search.Tree.Children) != 0 {
|
||||
return "", godata.NotImplementedError("complex search queries are not supported")
|
||||
}
|
||||
|
||||
searchValue := strings.Trim(req.Search.Tree.Token.Value, "\"")
|
||||
return searchValue, nil
|
||||
}
|
||||
104
services/graph/pkg/odata/odata.go
Normal file
104
services/graph/pkg/odata/odata.go
Normal file
@@ -0,0 +1,104 @@
|
||||
package odata
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/CiscoM31/godata"
|
||||
)
|
||||
|
||||
// GetExpandValues extracts the values of the $expand query parameter and
|
||||
// returns them in a []string, rejecting any $expand value that consists of more
|
||||
// than just a single path segment.
|
||||
func GetExpandValues(req *godata.GoDataQuery) ([]string, error) {
|
||||
if req == nil || req.Expand == nil {
|
||||
return []string{}, nil
|
||||
}
|
||||
|
||||
var expand []string
|
||||
for _, item := range req.Expand.ExpandItems {
|
||||
paths, err := collectExpandPaths(item, "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
expand = append(expand, paths...)
|
||||
}
|
||||
|
||||
return expand, nil
|
||||
}
|
||||
|
||||
// collectExpandPaths recursively collects all valid expand paths from the given item.
|
||||
func collectExpandPaths(item *godata.ExpandItem, prefix string) ([]string, error) {
|
||||
if err := validateExpandItem(item); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Build the current path
|
||||
currentPath := prefix
|
||||
if len(item.Path) > 0 {
|
||||
if len(item.Path) > 1 {
|
||||
return nil, godata.NotImplementedError("multiple segments in $expand not supported")
|
||||
}
|
||||
if currentPath == "" {
|
||||
currentPath = item.Path[0].Value
|
||||
} else {
|
||||
currentPath += "." + item.Path[0].Value
|
||||
}
|
||||
}
|
||||
|
||||
// Collect all paths, including nested ones
|
||||
paths := []string{currentPath}
|
||||
if item.Expand != nil {
|
||||
for _, subItem := range item.Expand.ExpandItems {
|
||||
subPaths, err := collectExpandPaths(subItem, currentPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
paths = append(paths, subPaths...)
|
||||
}
|
||||
}
|
||||
|
||||
return paths, nil
|
||||
}
|
||||
|
||||
// validateExpandItem checks if an expand item contains unsupported options.
|
||||
func validateExpandItem(item *godata.ExpandItem) error {
|
||||
if item.Filter != nil || item.At != nil || item.Search != nil ||
|
||||
item.OrderBy != nil || item.Skip != nil || item.Top != nil ||
|
||||
item.Select != nil || item.Compute != nil || item.Levels != 0 {
|
||||
return godata.NotImplementedError("options for $expand not supported")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetSelectValues extracts the values of the $select query parameter and
|
||||
// returns them in a []string, rejects any $select value that consists of more
|
||||
// than just a single path segment
|
||||
func GetSelectValues(req *godata.GoDataQuery) ([]string, error) {
|
||||
if req == nil || req.Select == nil {
|
||||
return []string{}, nil
|
||||
}
|
||||
sel := make([]string, 0, len(req.Select.SelectItems))
|
||||
for _, item := range req.Select.SelectItems {
|
||||
if len(item.Segments) > 1 {
|
||||
return []string{}, godata.NotImplementedError("multiple segments in $select not supported")
|
||||
}
|
||||
sel = append(sel, item.Segments[0].Value)
|
||||
}
|
||||
return sel, nil
|
||||
}
|
||||
|
||||
// GetSearchValues extracts the value of the $search query parameter and returns
|
||||
// it as a string. Rejects any search query that is more than just a simple string
|
||||
func GetSearchValues(req *godata.GoDataQuery) (string, error) {
|
||||
if req == nil || req.Search == nil {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
// Only allow simple search queries for now
|
||||
if len(req.Search.Tree.Children) != 0 {
|
||||
return "", godata.NotImplementedError("complex search queries are not supported")
|
||||
}
|
||||
|
||||
searchValue := strings.Trim(req.Search.Tree.Token.Value, "\"")
|
||||
return searchValue, nil
|
||||
}
|
||||
160
services/graph/pkg/odata/odata_test.go
Normal file
160
services/graph/pkg/odata/odata_test.go
Normal file
@@ -0,0 +1,160 @@
|
||||
package odata
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/CiscoM31/godata"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestGetExpandValues(t *testing.T) {
|
||||
t.Run("NilRequest", func(t *testing.T) {
|
||||
result, err := GetExpandValues(nil)
|
||||
assert.NoError(t, err)
|
||||
assert.Empty(t, result)
|
||||
})
|
||||
|
||||
t.Run("EmptyExpand", func(t *testing.T) {
|
||||
req := &godata.GoDataQuery{}
|
||||
result, err := GetExpandValues(req)
|
||||
assert.NoError(t, err)
|
||||
assert.Empty(t, result)
|
||||
})
|
||||
|
||||
t.Run("SinglePathSegment", func(t *testing.T) {
|
||||
req := &godata.GoDataQuery{
|
||||
Expand: &godata.GoDataExpandQuery{
|
||||
ExpandItems: []*godata.ExpandItem{
|
||||
{Path: []*godata.Token{{Value: "orders"}}},
|
||||
},
|
||||
},
|
||||
}
|
||||
result, err := GetExpandValues(req)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, []string{"orders"}, result)
|
||||
})
|
||||
|
||||
t.Run("MultiplePathSegments", func(t *testing.T) {
|
||||
req := &godata.GoDataQuery{
|
||||
Expand: &godata.GoDataExpandQuery{
|
||||
ExpandItems: []*godata.ExpandItem{
|
||||
{Path: []*godata.Token{{Value: "orders"}, {Value: "details"}}},
|
||||
},
|
||||
},
|
||||
}
|
||||
result, err := GetExpandValues(req)
|
||||
assert.Error(t, err)
|
||||
assert.Empty(t, result)
|
||||
})
|
||||
|
||||
t.Run("NestedExpand", func(t *testing.T) {
|
||||
req := &godata.GoDataQuery{
|
||||
Expand: &godata.GoDataExpandQuery{
|
||||
ExpandItems: []*godata.ExpandItem{
|
||||
{
|
||||
Path: []*godata.Token{{Value: "items"}},
|
||||
Expand: &godata.GoDataExpandQuery{
|
||||
ExpandItems: []*godata.ExpandItem{
|
||||
{
|
||||
Path: []*godata.Token{{Value: "subitem"}},
|
||||
Expand: &godata.GoDataExpandQuery{
|
||||
ExpandItems: []*godata.ExpandItem{
|
||||
{Path: []*godata.Token{{Value: "subsubitems"}}},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
result, err := GetExpandValues(req)
|
||||
assert.NoError(t, err)
|
||||
assert.Subset(t, result, []string{"items", "items.subitem", "items.subitem.subsubitems"}, "must contain all levels of expansion")
|
||||
})
|
||||
}
|
||||
|
||||
func TestGetSelectValues(t *testing.T) {
|
||||
t.Run("NilRequest", func(t *testing.T) {
|
||||
result, err := GetSelectValues(nil)
|
||||
assert.NoError(t, err)
|
||||
assert.Empty(t, result)
|
||||
})
|
||||
|
||||
t.Run("EmptySelect", func(t *testing.T) {
|
||||
req := &godata.GoDataQuery{}
|
||||
result, err := GetSelectValues(req)
|
||||
assert.NoError(t, err)
|
||||
assert.Empty(t, result)
|
||||
})
|
||||
|
||||
t.Run("SinglePathSegment", func(t *testing.T) {
|
||||
req := &godata.GoDataQuery{
|
||||
Select: &godata.GoDataSelectQuery{
|
||||
SelectItems: []*godata.SelectItem{
|
||||
{Segments: []*godata.Token{{Value: "name"}}},
|
||||
},
|
||||
},
|
||||
}
|
||||
result, err := GetSelectValues(req)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, []string{"name"}, result)
|
||||
})
|
||||
|
||||
t.Run("MultiplePathSegments", func(t *testing.T) {
|
||||
req := &godata.GoDataQuery{
|
||||
Select: &godata.GoDataSelectQuery{
|
||||
SelectItems: []*godata.SelectItem{
|
||||
{Segments: []*godata.Token{{Value: "name"}, {Value: "first"}}},
|
||||
},
|
||||
},
|
||||
}
|
||||
result, err := GetSelectValues(req)
|
||||
assert.Error(t, err)
|
||||
assert.Empty(t, result)
|
||||
})
|
||||
}
|
||||
|
||||
func TestGetSearchValues(t *testing.T) {
|
||||
t.Run("NilRequest", func(t *testing.T) {
|
||||
result, err := GetSearchValues(nil)
|
||||
assert.NoError(t, err)
|
||||
assert.Empty(t, result)
|
||||
})
|
||||
|
||||
t.Run("EmptySearch", func(t *testing.T) {
|
||||
req := &godata.GoDataQuery{}
|
||||
result, err := GetSearchValues(req)
|
||||
assert.NoError(t, err)
|
||||
assert.Empty(t, result)
|
||||
})
|
||||
|
||||
t.Run("SimpleSearch", func(t *testing.T) {
|
||||
req := &godata.GoDataQuery{
|
||||
Search: &godata.GoDataSearchQuery{
|
||||
Tree: &godata.ParseNode{
|
||||
Token: &godata.Token{Value: "test"},
|
||||
},
|
||||
},
|
||||
}
|
||||
result, err := GetSearchValues(req)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "test", result)
|
||||
})
|
||||
|
||||
t.Run("ComplexSearch", func(t *testing.T) {
|
||||
req := &godata.GoDataQuery{
|
||||
Search: &godata.GoDataSearchQuery{
|
||||
Tree: &godata.ParseNode{
|
||||
Children: []*godata.ParseNode{
|
||||
{Token: &godata.Token{Value: "test"}},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
result, err := GetSearchValues(req)
|
||||
assert.Error(t, err)
|
||||
assert.Empty(t, result)
|
||||
})
|
||||
}
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
"path"
|
||||
"slices"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -32,6 +33,7 @@ import (
|
||||
v0 "github.com/opencloud-eu/opencloud/protogen/gen/opencloud/messages/settings/v0"
|
||||
settingssvc "github.com/opencloud-eu/opencloud/protogen/gen/opencloud/services/settings/v0"
|
||||
"github.com/opencloud-eu/opencloud/services/graph/pkg/errorcode"
|
||||
"github.com/opencloud-eu/opencloud/services/graph/pkg/odata"
|
||||
settingsServiceExt "github.com/opencloud-eu/opencloud/services/settings/pkg/store/defaults"
|
||||
)
|
||||
|
||||
@@ -168,31 +170,63 @@ func (g Graph) GetAllDrivesV1Beta1(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
}
|
||||
|
||||
func sanitizePath(path string, apiVersion APIVersion) string {
|
||||
switch apiVersion {
|
||||
case APIVersion_1:
|
||||
return strings.TrimPrefix(path, "/graph/v1.0/")
|
||||
case APIVersion_1_Beta_1:
|
||||
return strings.TrimPrefix(path, "/graph/v1beta1.0/")
|
||||
default:
|
||||
return path
|
||||
}
|
||||
}
|
||||
|
||||
// parseDriveRequest parses the odata request and returns the parsed request and a boolean indicating if the request should expand root driveItems.
|
||||
func parseDriveRequest(r *http.Request) (*godata.GoDataRequest, bool, error) {
|
||||
odataReq, err := godata.ParseRequest(r.Context(), sanitizePath(r.URL.Path, APIVersion_1), r.URL.Query())
|
||||
if err != nil {
|
||||
return nil, false, errorcode.New(errorcode.InvalidRequest, err.Error())
|
||||
}
|
||||
exp, err := odata.GetExpandValues(odataReq.Query)
|
||||
if err != nil {
|
||||
return nil, false, errorcode.New(errorcode.InvalidRequest, err.Error())
|
||||
}
|
||||
expandPermissions := false
|
||||
if slices.Contains(exp, "root.permissions") {
|
||||
expandPermissions = true
|
||||
}
|
||||
return odataReq, expandPermissions, nil
|
||||
}
|
||||
|
||||
// getDrives implements the Service interface.
|
||||
func (g Graph) getDrives(r *http.Request, unrestricted bool, apiVersion APIVersion) ([]*libregraph.Drive, error) {
|
||||
logger := g.logger.SubloggerWithRequestID(r.Context())
|
||||
logger.Info().
|
||||
Interface("query", r.URL.Query()).
|
||||
Bool("unrestricted", unrestricted).
|
||||
Msg("calling get drives")
|
||||
sanitizedPath := strings.TrimPrefix(r.URL.Path, "/graph/v1.0/")
|
||||
// Parse the request with odata parser
|
||||
odataReq, err := godata.ParseRequest(r.Context(), sanitizedPath, r.URL.Query())
|
||||
if err != nil {
|
||||
logger.Debug().Err(err).Interface("query", r.URL.Query()).Msg("could not get drives: query error")
|
||||
return nil, errorcode.New(errorcode.InvalidRequest, err.Error())
|
||||
}
|
||||
ctx := r.Context()
|
||||
log := g.logger.SubloggerWithRequestID(ctx).With().Interface("query", r.URL.Query()).Bool("unrestricted", unrestricted).Logger()
|
||||
log.Debug().Msg("calling get drives")
|
||||
|
||||
webDavBaseURL, err := g.getWebDavBaseURL()
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("could not get drives: error parsing url")
|
||||
return nil, errorcode.New(errorcode.GeneralException, err.Error())
|
||||
}
|
||||
|
||||
log = log.With().Str("url", webDavBaseURL.String()).Logger()
|
||||
|
||||
odataReq, expandPermissions, err := parseDriveRequest(r)
|
||||
if err != nil {
|
||||
log.Debug().Err(err).Msg("could not get drives: error parsing odata request")
|
||||
return nil, err
|
||||
}
|
||||
|
||||
filters, err := generateCs3Filters(odataReq)
|
||||
if err != nil {
|
||||
logger.Debug().Err(err).Interface("query", r.URL.Query()).Msg("could not get drives: error parsing filters")
|
||||
log.Debug().Err(err).Msg("could not get drives: error parsing filters")
|
||||
return nil, errorcode.New(errorcode.NotSupported, err.Error())
|
||||
}
|
||||
if !unrestricted {
|
||||
user, ok := revactx.ContextGetUser(r.Context())
|
||||
if !ok {
|
||||
logger.Debug().Msg("could not create drive: invalid user")
|
||||
log.Debug().Msg("could not create drive: invalid user")
|
||||
return nil, errorcode.New(errorcode.AccessDenied, "invalid user")
|
||||
}
|
||||
filters = append(filters, &storageprovider.ListStorageSpacesRequest_Filter{
|
||||
@@ -203,39 +237,32 @@ func (g Graph) getDrives(r *http.Request, unrestricted bool, apiVersion APIVersi
|
||||
})
|
||||
}
|
||||
|
||||
logger.Debug().
|
||||
log.Debug().
|
||||
Interface("filters", filters).
|
||||
Bool("unrestricted", unrestricted).
|
||||
Msg("calling list storage spaces on backend")
|
||||
res, err := g.ListStorageSpacesWithFilters(ctx, filters, unrestricted)
|
||||
switch {
|
||||
case err != nil:
|
||||
logger.Error().Err(err).Msg("could not get drives: transport error")
|
||||
log.Error().Err(err).Msg("could not get drives: transport error")
|
||||
return nil, errorcode.New(errorcode.GeneralException, err.Error())
|
||||
case res.Status.Code != cs3rpc.Code_CODE_OK:
|
||||
if res.Status.Code == cs3rpc.Code_CODE_NOT_FOUND {
|
||||
// ok, empty return
|
||||
return nil, nil
|
||||
}
|
||||
logger.Debug().Str("message", res.GetStatus().GetMessage()).Msg("could not get drives: grpc error")
|
||||
log.Debug().Str("message", res.GetStatus().GetMessage()).Msg("could not get drives: grpc error")
|
||||
return nil, errorcode.New(errorcode.GeneralException, res.Status.Message)
|
||||
}
|
||||
|
||||
webDavBaseURL, err := g.getWebDavBaseURL()
|
||||
spaces, err := g.formatDrives(ctx, webDavBaseURL, res.StorageSpaces, apiVersion, expandPermissions)
|
||||
if err != nil {
|
||||
logger.Error().Err(err).Str("url", webDavBaseURL.String()).Msg("could not get drives: error parsing url")
|
||||
return nil, errorcode.New(errorcode.GeneralException, err.Error())
|
||||
}
|
||||
|
||||
spaces, err := g.formatDrives(ctx, webDavBaseURL, res.StorageSpaces, apiVersion)
|
||||
if err != nil {
|
||||
logger.Debug().Err(err).Msg("could not get drives: error parsing grpc response")
|
||||
log.Debug().Err(err).Msg("could not get drives: error parsing grpc response")
|
||||
return nil, errorcode.New(errorcode.GeneralException, err.Error())
|
||||
}
|
||||
|
||||
spaces, err = sortSpaces(odataReq, spaces)
|
||||
if err != nil {
|
||||
logger.Debug().Err(err).Msg("could not get drives: error sorting the spaces list according to query")
|
||||
log.Debug().Err(err).Msg("could not get drives: error sorting the spaces list according to query")
|
||||
return nil, errorcode.New(errorcode.InvalidRequest, err.Error())
|
||||
}
|
||||
|
||||
@@ -245,15 +272,32 @@ func (g Graph) getDrives(r *http.Request, unrestricted bool, apiVersion APIVersi
|
||||
// GetSingleDrive does a lookup of a single space by spaceId
|
||||
func (g Graph) GetSingleDrive(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
logger := g.logger.SubloggerWithRequestID(ctx)
|
||||
logger.Info().Interface("query", r.URL.Query()).Msg("calling get drive")
|
||||
log := g.logger.SubloggerWithRequestID(ctx).With().Interface("query", r.URL.Query()).Logger()
|
||||
log.Debug().Msg("calling get drive")
|
||||
|
||||
rid, err := parseIDParam(r, "driveID")
|
||||
if err != nil {
|
||||
errorcode.RenderError(w, r, err)
|
||||
return
|
||||
}
|
||||
log := logger.With().Str("storage", rid.StorageId).Str("space", rid.SpaceId).Str("node", rid.OpaqueId).Logger()
|
||||
|
||||
log = log.With().Str("storage", rid.StorageId).Str("space", rid.SpaceId).Str("node", rid.OpaqueId).Logger()
|
||||
|
||||
webDavBaseURL, err := g.getWebDavBaseURL()
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("could not get drive: error parsing webdav base url")
|
||||
errorcode.RenderError(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
log = log.With().Str("url", webDavBaseURL.String()).Logger()
|
||||
|
||||
_, expandPermissions, err := parseDriveRequest(r)
|
||||
if err != nil {
|
||||
log.Debug().Err(err).Msg("could not get drives: error parsing odata request")
|
||||
errorcode.RenderError(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
log.Debug().Msg("calling list storage spaces with id filter")
|
||||
|
||||
@@ -281,13 +325,7 @@ func (g Graph) GetSingleDrive(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
webDavBaseURL, err := g.getWebDavBaseURL()
|
||||
if err != nil {
|
||||
log.Error().Err(err).Str("url", webDavBaseURL.String()).Msg("could not get drive: error parsing webdav base url")
|
||||
errorcode.GeneralException.Render(w, r, http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
spaces, err := g.formatDrives(ctx, webDavBaseURL, res.StorageSpaces, APIVersion_1)
|
||||
spaces, err := g.formatDrives(ctx, webDavBaseURL, res.StorageSpaces, APIVersion_1, expandPermissions)
|
||||
if err != nil {
|
||||
log.Debug().Err(err).Msg("could not get drive: error parsing grpc response")
|
||||
errorcode.GeneralException.Render(w, r, http.StatusInternalServerError, err.Error())
|
||||
@@ -324,14 +362,28 @@ func (g Graph) canCreateSpace(ctx context.Context, ownPersonalHome bool) bool {
|
||||
|
||||
// CreateDrive creates a storage drive (space).
|
||||
func (g Graph) CreateDrive(w http.ResponseWriter, r *http.Request) {
|
||||
logger := g.logger.SubloggerWithRequestID(r.Context())
|
||||
logger.Info().Msg("calling create drive")
|
||||
|
||||
ctx := r.Context()
|
||||
log := g.logger.SubloggerWithRequestID(ctx).With().Interface("query", r.URL.Query()).Logger()
|
||||
log.Debug().Msg("calling create drive")
|
||||
|
||||
webDavBaseURL, err := g.getWebDavBaseURL()
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("could not create drive: error parsing webdav base url")
|
||||
errorcode.GeneralException.Render(w, r, http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
log = log.With().Str("url", webDavBaseURL.String()).Logger()
|
||||
|
||||
_, expandPermissions, err := parseDriveRequest(r)
|
||||
if err != nil {
|
||||
log.Debug().Err(err).Msg("could not create drive: error parsing odata request")
|
||||
errorcode.RenderError(w, r, err)
|
||||
}
|
||||
|
||||
us, ok := revactx.ContextGetUser(ctx)
|
||||
if !ok {
|
||||
logger.Debug().Msg("could not create drive: invalid user")
|
||||
log.Debug().Msg("could not create drive: invalid user")
|
||||
errorcode.NotAllowed.Render(w, r, http.StatusUnauthorized, "invalid user")
|
||||
return
|
||||
}
|
||||
@@ -339,7 +391,7 @@ func (g Graph) CreateDrive(w http.ResponseWriter, r *http.Request) {
|
||||
// TODO determine if the user tries to create his own personal space and pass that as a boolean
|
||||
canCreateSpace := g.canCreateSpace(ctx, false)
|
||||
if !canCreateSpace {
|
||||
logger.Debug().Bool("cancreatespace", canCreateSpace).Msg("could not create drive: insufficient permissions")
|
||||
log.Debug().Bool("cancreatespace", canCreateSpace).Msg("could not create drive: insufficient permissions")
|
||||
// if the permission is not existing for the user in context we can assume we don't have it. Return 401.
|
||||
errorcode.NotAllowed.Render(w, r, http.StatusForbidden, "insufficient permissions to create a space.")
|
||||
return
|
||||
@@ -347,20 +399,20 @@ func (g Graph) CreateDrive(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
gatewayClient, err := g.gatewaySelector.Next()
|
||||
if err != nil {
|
||||
logger.Error().Err(err).Msg("could not select next gateway client")
|
||||
log.Error().Err(err).Msg("could not select next gateway client")
|
||||
errorcode.ServiceNotAvailable.Render(w, r, http.StatusInternalServerError, "could not select next gateway client, aborting")
|
||||
return
|
||||
}
|
||||
|
||||
drive := libregraph.Drive{}
|
||||
if err := StrictJSONUnmarshal(r.Body, &drive); err != nil {
|
||||
logger.Debug().Err(err).Interface("body", r.Body).Msg("could not create drive: invalid body schema definition")
|
||||
log.Debug().Err(err).Interface("body", r.Body).Msg("could not create drive: invalid body schema definition")
|
||||
errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, "invalid body schema definition")
|
||||
return
|
||||
}
|
||||
spaceName := strings.TrimSpace(drive.Name)
|
||||
if err := validateSpaceName(spaceName); err != nil {
|
||||
logger.Debug().Str("name", spaceName).Err(err).Msg("could not create drive: name validation failed")
|
||||
log.Debug().Str("name", spaceName).Err(err).Msg("could not create drive: name validation failed")
|
||||
errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, fmt.Sprintf("invalid spacename: %s", err.Error()))
|
||||
return
|
||||
}
|
||||
@@ -373,7 +425,7 @@ func (g Graph) CreateDrive(w http.ResponseWriter, r *http.Request) {
|
||||
case "", _spaceTypeProject:
|
||||
driveType = _spaceTypeProject
|
||||
default:
|
||||
logger.Debug().Str("type", driveType).Msg("could not create drive: drives of this type cannot be created via this api")
|
||||
log.Debug().Str("type", driveType).Msg("could not create drive: drives of this type cannot be created via this api")
|
||||
errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, "drives of this type cannot be created via this api")
|
||||
return
|
||||
}
|
||||
@@ -398,39 +450,32 @@ func (g Graph) CreateDrive(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
resp, err := gatewayClient.CreateStorageSpace(ctx, &csr)
|
||||
if err != nil {
|
||||
logger.Error().Err(err).Msg("could not create drive: transport error")
|
||||
log.Error().Err(err).Msg("could not create drive: transport error")
|
||||
errorcode.GeneralException.Render(w, r, http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
if resp.GetStatus().GetCode() != cs3rpc.Code_CODE_OK {
|
||||
if resp.GetStatus().GetCode() == cs3rpc.Code_CODE_PERMISSION_DENIED {
|
||||
logger.Debug().Str("grpcmessage", resp.GetStatus().GetMessage()).Msg("could not create drive: permission denied")
|
||||
log.Debug().Str("grpcmessage", resp.GetStatus().GetMessage()).Msg("could not create drive: permission denied")
|
||||
errorcode.NotAllowed.Render(w, r, http.StatusForbidden, "permission denied")
|
||||
return
|
||||
}
|
||||
if resp.GetStatus().GetCode() == cs3rpc.Code_CODE_INVALID_ARGUMENT {
|
||||
logger.Debug().Str("grpcmessage", resp.GetStatus().GetMessage()).Msg("could not create drive: bad request")
|
||||
log.Debug().Str("grpcmessage", resp.GetStatus().GetMessage()).Msg("could not create drive: bad request")
|
||||
errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, resp.GetStatus().GetMessage())
|
||||
return
|
||||
}
|
||||
logger.Debug().Interface("grpcmessage", csr).Str("grpc", resp.GetStatus().GetMessage()).Msg("could not create drive: grpc error")
|
||||
log.Debug().Interface("grpcmessage", csr).Str("grpc", resp.GetStatus().GetMessage()).Msg("could not create drive: grpc error")
|
||||
errorcode.GeneralException.Render(w, r, http.StatusInternalServerError, resp.GetStatus().GetMessage())
|
||||
return
|
||||
}
|
||||
|
||||
webDavBaseURL, err := g.getWebDavBaseURL()
|
||||
if err != nil {
|
||||
logger.Error().Str("url", webDavBaseURL.String()).Err(err).Msg("could not create drive: error parsing webdav base url")
|
||||
errorcode.GeneralException.Render(w, r, http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
space := resp.GetStorageSpace()
|
||||
if t := r.URL.Query().Get(TemplateParameter); t != "" && driveType == _spaceTypeProject {
|
||||
loc := l10n.MustGetUserLocale(ctx, us.GetId().GetOpaqueId(), r.Header.Get(HeaderAcceptLanguage), g.valueService)
|
||||
if err := g.applySpaceTemplate(ctx, gatewayClient, space.GetRoot(), t, loc); err != nil {
|
||||
logger.Error().Err(err).Msg("could not apply template to space")
|
||||
log.Error().Err(err).Msg("could not apply template to space")
|
||||
errorcode.GeneralException.Render(w, r, http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
@@ -438,20 +483,20 @@ func (g Graph) CreateDrive(w http.ResponseWriter, r *http.Request) {
|
||||
// refetch the drive to get quota information - should we calculate this ourselves to avoid the extra call?
|
||||
space, err = utils.GetSpace(ctx, space.GetId().GetOpaqueId(), gatewayClient)
|
||||
if err != nil {
|
||||
logger.Error().Err(err).Msg("could not refetch space")
|
||||
log.Error().Err(err).Msg("could not refetch space")
|
||||
errorcode.GeneralException.Render(w, r, http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
spaces, err := g.formatDrives(ctx, webDavBaseURL, []*storageprovider.StorageSpace{space}, APIVersion_1)
|
||||
spaces, err := g.formatDrives(ctx, webDavBaseURL, []*storageprovider.StorageSpace{space}, APIVersion_1, expandPermissions)
|
||||
if err != nil {
|
||||
logger.Debug().Err(err).Msg("could not get drive: error parsing grpc response")
|
||||
log.Debug().Err(err).Msg("could not get drive: error parsing grpc response")
|
||||
errorcode.GeneralException.Render(w, r, http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
if len(spaces) == 0 {
|
||||
logger.Error().Msg("could not convert space")
|
||||
log.Error().Msg("could not convert space")
|
||||
errorcode.GeneralException.Render(w, r, http.StatusInternalServerError, "could not convert space")
|
||||
return
|
||||
}
|
||||
@@ -462,8 +507,8 @@ func (g Graph) CreateDrive(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
// UpdateDrive updates the properties of a storage drive (space).
|
||||
func (g Graph) UpdateDrive(w http.ResponseWriter, r *http.Request) {
|
||||
logger := g.logger.SubloggerWithRequestID(r.Context())
|
||||
logger.Info().Msg("calling update drive")
|
||||
log := g.logger.SubloggerWithRequestID(r.Context()).With().Interface("query", r.URL.Query()).Logger()
|
||||
log.Debug().Msg("calling update drive")
|
||||
|
||||
rid, err := parseIDParam(r, "driveID")
|
||||
if err != nil {
|
||||
@@ -471,9 +516,26 @@ func (g Graph) UpdateDrive(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
log = log.With().Str("storage", rid.StorageId).Str("space", rid.SpaceId).Str("node", rid.OpaqueId).Logger()
|
||||
|
||||
webDavBaseURL, err := g.getWebDavBaseURL()
|
||||
if err != nil {
|
||||
log.Error().Err(err).Interface("url", webDavBaseURL.String()).Msg("could not update drive: error parsing url")
|
||||
errorcode.GeneralException.Render(w, r, http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
log = log.With().Str("url", webDavBaseURL.String()).Logger()
|
||||
|
||||
_, expandPermissions, err := parseDriveRequest(r)
|
||||
if err != nil {
|
||||
log.Debug().Err(err).Msg("could not create drive: error parsing odata request")
|
||||
errorcode.RenderError(w, r, err)
|
||||
}
|
||||
|
||||
drive := libregraph.DriveUpdate{}
|
||||
if err = StrictJSONUnmarshal(r.Body, &drive); err != nil {
|
||||
logger.Debug().Err(err).Interface("body", r.Body).Msg("could not update drive, invalid request body")
|
||||
log.Debug().Err(err).Interface("body", r.Body).Msg("could not update drive, invalid request body")
|
||||
errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, fmt.Sprintf("invalid request body: error: %v", err.Error()))
|
||||
return
|
||||
}
|
||||
@@ -526,7 +588,7 @@ func (g Graph) UpdateDrive(w http.ResponseWriter, r *http.Request) {
|
||||
if drive.GetName() != "" {
|
||||
spacename := strings.TrimSpace(drive.GetName())
|
||||
if err := validateSpaceName(spacename); err != nil {
|
||||
logger.Info().Err(err).Msg("could not update drive: spacename invalid")
|
||||
log.Info().Err(err).Msg("could not update drive: spacename invalid")
|
||||
errorcode.GeneralException.Render(w, r, http.StatusBadRequest, err.Error())
|
||||
return
|
||||
}
|
||||
@@ -552,12 +614,12 @@ func (g Graph) UpdateDrive(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
canSetSpaceQuota, err := g.canSetSpaceQuota(r.Context(), user, dt)
|
||||
if err != nil {
|
||||
logger.Error().Err(err).Msg("could not update drive: failed to check if the user can set space quota")
|
||||
log.Error().Err(err).Msg("could not update drive: failed to check if the user can set space quota")
|
||||
errorcode.GeneralException.Render(w, r, http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
if !canSetSpaceQuota {
|
||||
logger.Debug().
|
||||
log.Debug().
|
||||
Bool("cansetspacequota", canSetSpaceQuota).
|
||||
Msg("could not update drive: user is not allowed to set the space quota")
|
||||
errorcode.NotAllowed.Render(w, r, http.StatusForbidden, "user is not allowed to set the space quota")
|
||||
@@ -568,10 +630,10 @@ func (g Graph) UpdateDrive(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
}
|
||||
|
||||
logger.Debug().Interface("payload", updateSpaceRequest).Msg("calling update space on backend")
|
||||
log.Debug().Interface("payload", updateSpaceRequest).Msg("calling update space on backend")
|
||||
resp, err := gatewayClient.UpdateStorageSpace(r.Context(), updateSpaceRequest)
|
||||
if err != nil {
|
||||
logger.Error().Err(err).Msg("could not update drive: transport error")
|
||||
log.Error().Err(err).Msg("could not update drive: transport error")
|
||||
errorcode.GeneralException.Render(w, r, http.StatusInternalServerError, "transport error")
|
||||
return
|
||||
}
|
||||
@@ -579,38 +641,31 @@ func (g Graph) UpdateDrive(w http.ResponseWriter, r *http.Request) {
|
||||
if resp.GetStatus().GetCode() != cs3rpc.Code_CODE_OK {
|
||||
switch resp.Status.GetCode() {
|
||||
case cs3rpc.Code_CODE_NOT_FOUND:
|
||||
logger.Debug().Interface("id", rid).Msg("could not update drive: drive not found")
|
||||
log.Debug().Msg("could not update drive: drive not found")
|
||||
errorcode.ItemNotFound.Render(w, r, http.StatusNotFound, "drive not found")
|
||||
return
|
||||
case cs3rpc.Code_CODE_PERMISSION_DENIED:
|
||||
logger.Debug().Interface("id", rid).Msg("could not update drive, permission denied")
|
||||
log.Debug().Msg("could not update drive, permission denied")
|
||||
errorcode.ItemNotFound.Render(w, r, http.StatusNotFound, "drive not found")
|
||||
return
|
||||
case cs3rpc.Code_CODE_INVALID_ARGUMENT:
|
||||
logger.Debug().Interface("id", rid).Msg("could not update drive, invalid argument")
|
||||
log.Debug().Msg("could not update drive, invalid argument")
|
||||
errorcode.NotAllowed.Render(w, r, http.StatusBadRequest, resp.GetStatus().GetMessage())
|
||||
return
|
||||
case cs3rpc.Code_CODE_UNIMPLEMENTED:
|
||||
logger.Debug().Interface("id", rid).Msg("could not delete drive: delete not implemented for this type of drive")
|
||||
log.Debug().Msg("could not delete drive: delete not implemented for this type of drive")
|
||||
errorcode.NotAllowed.Render(w, r, http.StatusMethodNotAllowed, "drive cannot be updated")
|
||||
return
|
||||
default:
|
||||
logger.Debug().Interface("id", rid).Str("grpc", resp.GetStatus().GetMessage()).Msg("could not update drive: grpc error")
|
||||
log.Debug().Str("grpc", resp.GetStatus().GetMessage()).Msg("could not update drive: grpc error")
|
||||
errorcode.GeneralException.Render(w, r, http.StatusInternalServerError, "grpc error")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
webDavBaseURL, err := g.getWebDavBaseURL()
|
||||
spaces, err := g.formatDrives(r.Context(), webDavBaseURL, []*storageprovider.StorageSpace{resp.StorageSpace}, APIVersion_1, expandPermissions)
|
||||
if err != nil {
|
||||
logger.Error().Err(err).Interface("url", webDavBaseURL.String()).Msg("could not update drive: error parsing url")
|
||||
errorcode.GeneralException.Render(w, r, http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
spaces, err := g.formatDrives(r.Context(), webDavBaseURL, []*storageprovider.StorageSpace{resp.StorageSpace}, APIVersion_1)
|
||||
if err != nil {
|
||||
logger.Debug().Err(err).Msg("could not update drive: error parsing grpc response")
|
||||
log.Debug().Err(err).Msg("could not update drive: error parsing grpc response")
|
||||
errorcode.GeneralException.Render(w, r, http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
@@ -619,7 +674,7 @@ func (g Graph) UpdateDrive(w http.ResponseWriter, r *http.Request) {
|
||||
render.JSON(w, r, spaces[0])
|
||||
}
|
||||
|
||||
func (g Graph) formatDrives(ctx context.Context, baseURL *url.URL, storageSpaces []*storageprovider.StorageSpace, apiVersion APIVersion) ([]*libregraph.Drive, error) {
|
||||
func (g Graph) formatDrives(ctx context.Context, baseURL *url.URL, storageSpaces []*storageprovider.StorageSpace, apiVersion APIVersion, expandPermissions bool) ([]*libregraph.Drive, error) {
|
||||
errg, ctx := errgroup.WithContext(ctx)
|
||||
work := make(chan *storageprovider.StorageSpace, len(storageSpaces))
|
||||
results := make(chan *libregraph.Drive, len(storageSpaces))
|
||||
@@ -649,7 +704,7 @@ func (g Graph) formatDrives(ctx context.Context, baseURL *url.URL, storageSpaces
|
||||
// skip OCM shares they are no supposed to show up in the drives list
|
||||
continue
|
||||
}
|
||||
res, err := g.cs3StorageSpaceToDrive(ctx, baseURL, storageSpace, apiVersion)
|
||||
res, err := g.cs3StorageSpaceToDrive(ctx, baseURL, storageSpace, apiVersion, expandPermissions)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -733,7 +788,7 @@ func (g Graph) ListStorageSpacesWithFilters(ctx context.Context, filters []*stor
|
||||
return res, err
|
||||
}
|
||||
|
||||
func (g Graph) cs3StorageSpaceToDrive(ctx context.Context, baseURL *url.URL, space *storageprovider.StorageSpace, apiVersion APIVersion) (*libregraph.Drive, error) {
|
||||
func (g Graph) cs3StorageSpaceToDrive(ctx context.Context, baseURL *url.URL, space *storageprovider.StorageSpace, apiVersion APIVersion, expandPermissions bool) (*libregraph.Drive, error) {
|
||||
logger := g.logger.SubloggerWithRequestID(ctx)
|
||||
if space.Root == nil {
|
||||
logger.Error().Msg("unable to parse space: space has no root")
|
||||
@@ -745,18 +800,20 @@ func (g Graph) cs3StorageSpaceToDrive(ctx context.Context, baseURL *url.URL, spa
|
||||
}
|
||||
spaceID := storagespace.FormatResourceID(spaceRid)
|
||||
|
||||
permissions := g.cs3SpacePermissionsToLibreGraph(ctx, space, apiVersion)
|
||||
|
||||
drive := &libregraph.Drive{
|
||||
Id: libregraph.PtrString(spaceID),
|
||||
Name: space.Name,
|
||||
//"createdDateTime": "string (timestamp)", // TODO read from StorageSpace ... needs Opaque for now
|
||||
DriveType: &space.SpaceType,
|
||||
// we currently always expandt the root because it carries the deleted property that indiccates if a space is trashed
|
||||
Root: &libregraph.DriveItem{
|
||||
Id: libregraph.PtrString(storagespace.FormatResourceID(spaceRid)),
|
||||
Permissions: permissions,
|
||||
Id: libregraph.PtrString(storagespace.FormatResourceID(spaceRid)),
|
||||
},
|
||||
}
|
||||
if expandPermissions {
|
||||
drive.Root.Permissions = g.cs3SpacePermissionsToLibreGraph(ctx, space, apiVersion)
|
||||
}
|
||||
|
||||
if space.SpaceType == _spaceTypeMountpoint {
|
||||
var remoteItem *libregraph.RemoteItem
|
||||
grantID := storageprovider.ResourceId{
|
||||
|
||||
@@ -489,8 +489,42 @@ var _ = Describe("Graph", func() {
|
||||
Expect(libreError.Error.Message).To(Equal("internal quota error"))
|
||||
Expect(libreError.Error.Code).To(Equal(errorcode.GeneralException.String()))
|
||||
})
|
||||
It("omit permissions by default", func() {
|
||||
gatewayClient.On("ListStorageSpaces", mock.Anything, mock.Anything).Times(1).Return(&provider.ListStorageSpacesResponse{
|
||||
Status: status.NewOK(ctx),
|
||||
StorageSpaces: []*provider.StorageSpace{
|
||||
{
|
||||
Opaque: utils.AppendJSONToOpaque(nil, "grants", map[string]provider.ResourcePermissions{
|
||||
"1": *conversions.NewManagerRole().CS3ResourcePermissions(),
|
||||
}),
|
||||
Root: &provider.ResourceId{},
|
||||
},
|
||||
},
|
||||
}, nil)
|
||||
gatewayClient.On("InitiateFileDownload", mock.Anything, mock.Anything).Return(&gateway.InitiateFileDownloadResponse{
|
||||
Status: status.NewNotFound(ctx, "not found"),
|
||||
}, nil)
|
||||
gatewayClient.On("GetQuota", mock.Anything, mock.Anything).Return(&provider.GetQuotaResponse{
|
||||
Status: status.NewUnimplemented(ctx, fmt.Errorf("not supported"), "not supported"),
|
||||
}, nil)
|
||||
gatewayClient.On("GetUser", mock.Anything, mock.Anything).Return(&userprovider.GetUserResponse{
|
||||
Status: status.NewUnimplemented(ctx, fmt.Errorf("not supported"), "not supported"),
|
||||
}, nil)
|
||||
|
||||
r := httptest.NewRequest(http.MethodGet, "/graph/v1.0/me/drives", nil)
|
||||
r = r.WithContext(ctx)
|
||||
rr := httptest.NewRecorder()
|
||||
svc.GetDrivesV1(rr, r)
|
||||
|
||||
Expect(rr.Code).To(Equal(http.StatusOK))
|
||||
|
||||
jsonData := gjson.Get(rr.Body.String(), "value")
|
||||
|
||||
Expect(jsonData.Get("#").Num).To(Equal(float64(1)))
|
||||
Expect(jsonData.Get("0.root.permissions").Exists()).To(BeFalse())
|
||||
})
|
||||
})
|
||||
DescribeTable("GetDrivesV1Beta1 and GetAllDrivesV1Beta1",
|
||||
DescribeTable("GetDrivesV1Beta1 and GetAllDrivesV1Beta1 expands root permissions",
|
||||
func(check func(gjson.Result), resourcePermissions provider.ResourcePermissions) {
|
||||
permissionService.On("GetPermissionByID", mock.Anything, mock.Anything).Return(&settingssvc.GetPermissionByIDResponse{
|
||||
Permission: &v0.Permission{
|
||||
@@ -518,7 +552,7 @@ var _ = Describe("Graph", func() {
|
||||
Status: status.NewUnimplemented(ctx, fmt.Errorf("not supported"), "not supported"),
|
||||
}, nil)
|
||||
|
||||
r := httptest.NewRequest(http.MethodGet, "/graph/v1.0/me/drives", nil)
|
||||
r := httptest.NewRequest(http.MethodGet, "/graph/v1beta1.0/me/drives?$expand=root($expand=permissions)", nil)
|
||||
r = r.WithContext(ctx)
|
||||
rr := httptest.NewRecorder()
|
||||
svc.GetDrivesV1Beta1(rr, r)
|
||||
|
||||
@@ -24,6 +24,7 @@ import (
|
||||
settingssvc "github.com/opencloud-eu/opencloud/protogen/gen/opencloud/services/settings/v0"
|
||||
"github.com/opencloud-eu/opencloud/services/graph/pkg/errorcode"
|
||||
"github.com/opencloud-eu/opencloud/services/graph/pkg/identity"
|
||||
"github.com/opencloud-eu/opencloud/services/graph/pkg/odata"
|
||||
ocsettingssvc "github.com/opencloud-eu/opencloud/services/settings/pkg/service/v0"
|
||||
"github.com/opencloud-eu/opencloud/services/settings/pkg/store/defaults"
|
||||
revactx "github.com/opencloud-eu/reva/v2/pkg/ctx"
|
||||
@@ -53,7 +54,7 @@ func (g Graph) GetMe(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
exp, err := identity.GetExpandValues(odataReq.Query)
|
||||
exp, err := odata.GetExpandValues(odataReq.Query)
|
||||
if err != nil {
|
||||
logger.Debug().Err(err).Interface("query", r.URL.Query()).Msg("could not get users: $expand error")
|
||||
errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, err.Error())
|
||||
@@ -119,74 +120,85 @@ func (g Graph) fetchAppRoleAssignments(ctx context.Context, accountuuid string)
|
||||
|
||||
// GetUserDrive implements the Service interface.
|
||||
func (g Graph) GetUserDrive(w http.ResponseWriter, r *http.Request) {
|
||||
logger := g.logger.SubloggerWithRequestID(r.Context())
|
||||
logger.Debug().Interface("query", r.URL.Query()).Msg("calling get user drive")
|
||||
ctx := r.Context()
|
||||
log := g.logger.SubloggerWithRequestID(ctx).With().Interface("query", r.URL.Query()).Logger()
|
||||
log.Debug().Msg("calling get user drive")
|
||||
|
||||
webDavBaseURL, err := g.getWebDavBaseURL()
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("could not get personal drive: error parsing webdav base url")
|
||||
errorcode.GeneralException.Render(w, r, http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
log = log.With().Str("url", webDavBaseURL.String()).Logger()
|
||||
|
||||
userID, err := url.PathUnescape(chi.URLParam(r, "userID"))
|
||||
if err != nil {
|
||||
logger.Debug().Err(err).Str("userID", chi.URLParam(r, "userID")).Msg("could not get drive: unescaping drive id failed")
|
||||
log.Debug().Err(err).Str("userID", chi.URLParam(r, "userID")).Msg("could not get drive: unescaping drive id failed")
|
||||
errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, "unescaping user id failed")
|
||||
return
|
||||
}
|
||||
|
||||
log = log.With().Str("userID", userID).Logger()
|
||||
|
||||
_, expandPermissions, err := parseDriveRequest(r)
|
||||
if err != nil {
|
||||
log.Debug().Err(err).Msg("could not get drives: error parsing odata request")
|
||||
errorcode.RenderError(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
if userID == "" {
|
||||
u, ok := revactx.ContextGetUser(r.Context())
|
||||
u, ok := revactx.ContextGetUser(ctx)
|
||||
if !ok {
|
||||
logger.Debug().Msg("could not get user: user not in context")
|
||||
log.Debug().Msg("could not get user: user not in context")
|
||||
errorcode.GeneralException.Render(w, r, http.StatusInternalServerError, "user not in context")
|
||||
return
|
||||
}
|
||||
userID = u.GetId().GetOpaqueId()
|
||||
}
|
||||
|
||||
logger.Debug().Str("userID", userID).Msg("calling list storage spaces with user and personal filter")
|
||||
ctx := r.Context()
|
||||
log.Debug().Msg("calling list storage spaces with user and personal filter")
|
||||
|
||||
filters := []*storageprovider.ListStorageSpacesRequest_Filter{listStorageSpacesTypeFilter("personal"), listStorageSpacesUserFilter(userID)}
|
||||
res, err := g.ListStorageSpacesWithFilters(ctx, filters, true)
|
||||
switch {
|
||||
case err != nil:
|
||||
logger.Error().Err(err).Msg("could not get drive: transport error")
|
||||
log.Error().Err(err).Msg("could not get drive: transport error")
|
||||
errorcode.GeneralException.Render(w, r, http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
case res.Status.Code != cs3rpc.Code_CODE_OK:
|
||||
if res.Status.Code == cs3rpc.Code_CODE_NOT_FOUND {
|
||||
// the client is doing a lookup for a specific space, therefore we need to return
|
||||
// not found to the caller
|
||||
logger.Debug().Str("userID", userID).Msg("could not get personal drive for user: not found")
|
||||
log.Debug().Msg("could not get personal drive for user: not found")
|
||||
errorcode.ItemNotFound.Render(w, r, http.StatusNotFound, "drive not found")
|
||||
return
|
||||
}
|
||||
logger.Debug().
|
||||
Str("userID", userID).
|
||||
log.Debug().
|
||||
Str("grpcmessage", res.GetStatus().GetMessage()).
|
||||
Msg("could not get personal drive for user: grpc error")
|
||||
errorcode.GeneralException.Render(w, r, http.StatusInternalServerError, res.Status.Message)
|
||||
return
|
||||
}
|
||||
|
||||
webDavBaseURL, err := g.getWebDavBaseURL()
|
||||
spaces, err := g.formatDrives(ctx, webDavBaseURL, res.StorageSpaces, APIVersion_1, expandPermissions)
|
||||
if err != nil {
|
||||
logger.Error().Err(err).Str("url", webDavBaseURL.String()).Msg("could not get personal drive: error parsing webdav base url")
|
||||
errorcode.GeneralException.Render(w, r, http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
spaces, err := g.formatDrives(ctx, webDavBaseURL, res.StorageSpaces, APIVersion_1)
|
||||
if err != nil {
|
||||
logger.Debug().Err(err).Msg("could not get personal drive: error parsing grpc response")
|
||||
log.Debug().Err(err).Msg("could not get personal drive: error parsing grpc response")
|
||||
errorcode.GeneralException.Render(w, r, http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
switch num := len(spaces); {
|
||||
case num == 0:
|
||||
logger.Debug().Str("userID", userID).Msg("could not get personal drive: no drive returned from storage")
|
||||
log.Debug().Msg("could not get personal drive: no drive returned from storage")
|
||||
errorcode.ItemNotFound.Render(w, r, http.StatusNotFound, "no drive returned from storage")
|
||||
return
|
||||
case num == 1:
|
||||
render.Status(r, http.StatusOK)
|
||||
render.JSON(w, r, spaces[0])
|
||||
default:
|
||||
logger.Debug().Int("number", num).Msg("could not get personal drive: expected to find a single drive but fetched more")
|
||||
log.Debug().Int("number", num).Msg("could not get personal drive: expected to find a single drive but fetched more")
|
||||
errorcode.GeneralException.Render(w, r, http.StatusInternalServerError, "could not get personal drive: expected to find a single drive but fetched more")
|
||||
return
|
||||
}
|
||||
@@ -301,7 +313,7 @@ func (g Graph) GetUsers(w http.ResponseWriter, r *http.Request) {
|
||||
users = finalUsers
|
||||
}
|
||||
|
||||
exp, err := identity.GetExpandValues(odataReq.Query)
|
||||
exp, err := odata.GetExpandValues(odataReq.Query)
|
||||
if err != nil {
|
||||
logger.Debug().Err(err).Interface("query", r.URL.Query()).Msg("could not get users: $expand error")
|
||||
errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, err.Error())
|
||||
@@ -449,7 +461,7 @@ func (g Graph) GetUser(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
exp, err := identity.GetExpandValues(odataReq.Query)
|
||||
exp, err := odata.GetExpandValues(odataReq.Query)
|
||||
if err != nil {
|
||||
logger.Debug().Err(err).Interface("query", r.URL.Query()).Msg("could not get users: $expand error")
|
||||
errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, err.Error())
|
||||
@@ -466,6 +478,8 @@ func (g Graph) GetUser(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
listDrives := slices.Contains(exp, "drives")
|
||||
listDrive := slices.Contains(exp, "drive")
|
||||
expandDrivePermissions := slices.Contains(exp, "drive.root.permissions")
|
||||
expandDrivesPermissions := slices.Contains(exp, "drives.root.permissions")
|
||||
|
||||
// do we need to list all or only the personal drive
|
||||
filters := []*storageprovider.ListStorageSpacesRequest_Filter{}
|
||||
@@ -526,7 +540,13 @@ func (g Graph) GetUser(w http.ResponseWriter, r *http.Request) {
|
||||
user.Drive = &libregraph.Drive{}
|
||||
}
|
||||
for _, sp := range lspr.GetStorageSpaces() {
|
||||
d, err := g.cs3StorageSpaceToDrive(r.Context(), wdu, sp, APIVersion_1)
|
||||
expandPermissions := false
|
||||
if sp.GetSpaceType() == "personal" && sp.GetOwner().GetId().GetOpaqueId() != user.GetId() {
|
||||
expandPermissions = expandDrivePermissions
|
||||
} else {
|
||||
expandPermissions = expandDrivesPermissions
|
||||
}
|
||||
d, err := g.cs3StorageSpaceToDrive(r.Context(), wdu, sp, APIVersion_1, expandPermissions)
|
||||
if err != nil {
|
||||
logger.Debug().Err(err).Interface("id", sp.Id).Msg("error converting space to drive")
|
||||
continue
|
||||
@@ -1023,7 +1043,7 @@ func (g Graph) searchOCMAcceptedUsers(ctx context.Context, odataReq *godata.GoDa
|
||||
if err != nil {
|
||||
return nil, errorcode.New(errorcode.GeneralException, err.Error())
|
||||
}
|
||||
term, err := identity.GetSearchValues(odataReq.Query)
|
||||
term, err := odata.GetSearchValues(odataReq.Query)
|
||||
if err != nil {
|
||||
return nil, errorcode.New(errorcode.InvalidRequest, err.Error())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user