Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 63 additions & 0 deletions services/graph/mocks/base_graph_provider.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion services/graph/pkg/service/v0/api_driveitem_permissions.go
Original file line number Diff line number Diff line change
Expand Up @@ -404,7 +404,7 @@ func (s DriveItemPermissionsService) ListPermissions(ctx context.Context, itemID

driveItems := make(driveItemsByResourceID, 1)
// we can use the statResponse to build the drive item before fetching the shares
item, err := cs3ResourceToDriveItem(s.logger, s.publicBaseURL, statResponse.GetInfo())
item, err := s.cs3ResourceToDriveItem(statResponse.GetInfo())
if err != nil {
return collectionOfPermissions, err
}
Expand Down
22 changes: 13 additions & 9 deletions services/graph/pkg/service/v0/base.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (

"github.com/opencloud-eu/reva/v2/pkg/rgrpc/todo/pool"
"github.com/opencloud-eu/reva/v2/pkg/share"
"github.com/opencloud-eu/reva/v2/pkg/signedurl"
"github.com/opencloud-eu/reva/v2/pkg/storagespace"
"github.com/opencloud-eu/reva/v2/pkg/utils"

Expand Down Expand Up @@ -50,6 +51,16 @@ type BaseGraphService struct {
config *config.Config
availableRoles []*libregraph.UnifiedRoleDefinition
publicBaseURL *url.URL
downloadSigner signedurl.Signer
}

// webURLForResource returns the public web URL pointing at the given resource
// (e.g. https://cloud.example.com/f/<resource-id>), using the pre-parsed
// publicBaseURL held by the service.
func (g BaseGraphService) webURLForResource(rid *storageprovider.ResourceId) *string {
u := *g.publicBaseURL
u.Path = path.Join(u.Path, "f", storagespace.FormatResourceID(rid))
return libregraph.PtrString(u.String())
}

func (g BaseGraphService) getDriveItem(ctx context.Context, ref *storageprovider.Reference) (*libregraph.DriveItem, error) {
Expand All @@ -66,7 +77,7 @@ func (g BaseGraphService) getDriveItem(ctx context.Context, ref *storageprovider
refStr, _ := storagespace.FormatReference(ref)
return nil, fmt.Errorf("could not stat %s: %s", refStr, res.GetStatus().GetMessage())
}
return cs3ResourceToDriveItem(g.logger, g.publicBaseURL, res.GetInfo())
return g.cs3ResourceToDriveItem(res.GetInfo())
}

func (g BaseGraphService) CS3ReceivedSharesToDriveItems(ctx context.Context, receivedShares []*collaboration.ReceivedShare) ([]libregraph.DriveItem, error) {
Expand Down Expand Up @@ -217,14 +228,6 @@ func (g BaseGraphService) cs3SpacePermissionsToLibreGraph(ctx context.Context, s
}

func (g BaseGraphService) libreGraphPermissionFromCS3PublicShare(createdLink *link.PublicShare) (*libregraph.Permission, error) {
webURL, err := url.Parse(g.config.Spaces.WebDavBase)
if err != nil {
g.logger.Error().
Err(err).
Str("url", g.config.Spaces.WebDavBase).
Msg("failed to parse webURL base url")
return nil, err
}
lt, actions := linktype.SharingLinkTypeFromCS3Permissions(createdLink.GetPermissions())
perm := libregraph.NewPermission()
perm.Id = libregraph.PtrString(createdLink.GetId().GetOpaqueId())
Expand All @@ -235,6 +238,7 @@ func (g BaseGraphService) libreGraphPermissionFromCS3PublicShare(createdLink *li
LibreGraphQuickLink: libregraph.PtrBool(createdLink.GetQuicklink()),
}
perm.LibreGraphPermissionsActions = actions
webURL := *g.publicBaseURL
webURL.Path = path.Join(webURL.Path, "s", createdLink.GetToken())
perm.Link.SetWebUrl(webURL.String())

Expand Down
116 changes: 116 additions & 0 deletions services/graph/pkg/service/v0/driveitem_download.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
package svc

import (
"errors"
"net/http"
"path"
"strings"
"time"

cs3rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1"
storageprovider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
libregraph "github.com/opencloud-eu/libre-graph-api-go"
revactx "github.com/opencloud-eu/reva/v2/pkg/ctx"
"github.com/opencloud-eu/reva/v2/pkg/storagespace"

"github.com/opencloud-eu/opencloud/services/graph/pkg/errorcode"
)

func (g Graph) GetDriveItemContent(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()

driveID, err := parseIDParam(r, "driveID")
if err != nil {
errorcode.RenderError(w, r, err)
return
}
itemID, err := parseIDParam(r, "itemID")
if err != nil {
errorcode.RenderError(w, r, err)
return
}
if driveID.GetStorageId() != itemID.GetStorageId() || driveID.GetSpaceId() != itemID.GetSpaceId() {
errorcode.ItemNotFound.Render(w, r, http.StatusNotFound, "Item does not exist")
return
}

user, ok := revactx.ContextGetUser(ctx)
if !ok {
errorcode.GeneralException.Render(w, r, http.StatusInternalServerError, "user not in context")
return
}

gatewayClient, err := g.gatewaySelector.Next()
if err != nil {
errorcode.GeneralException.Render(w, r, http.StatusInternalServerError, err.Error())
return
}
stat, err := gatewayClient.Stat(ctx, &storageprovider.StatRequest{Ref: &storageprovider.Reference{ResourceId: &itemID}})
switch {
case err != nil:
errorcode.GeneralException.Render(w, r, http.StatusInternalServerError, err.Error())
return
case stat.GetStatus().GetCode() == cs3rpc.Code_CODE_OK:
case stat.GetStatus().GetCode() == cs3rpc.Code_CODE_NOT_FOUND:
errorcode.ItemNotFound.Render(w, r, http.StatusNotFound, stat.GetStatus().GetMessage())
return
case stat.GetStatus().GetCode() == cs3rpc.Code_CODE_PERMISSION_DENIED:
errorcode.ItemNotFound.Render(w, r, http.StatusNotFound, stat.GetStatus().GetMessage())
return
case stat.GetStatus().GetCode() == cs3rpc.Code_CODE_UNAUTHENTICATED:
errorcode.Unauthenticated.Render(w, r, http.StatusUnauthorized, stat.GetStatus().GetMessage())
return
default:
errorcode.GeneralException.Render(w, r, http.StatusInternalServerError, stat.GetStatus().GetMessage())
return
}

downloadURL, err := g.signedDownloadURL(&itemID, user.GetId().GetOpaqueId())
if err != nil {
errorcode.GeneralException.Render(w, r, http.StatusInternalServerError, err.Error())
return
}

http.Redirect(w, r, downloadURL, http.StatusFound)
}

func (g BaseGraphService) signedDownloadURL(id *storageprovider.ResourceId, userID string) (string, error) {
if g.downloadSigner == nil {
return "", errors.New("download url signing is not configured")
}
base, err := g.getWebDavBaseURL()
if err != nil {
return "", err
}
base.Path = path.Join(base.Path, storagespace.FormatResourceID(id))
return g.downloadSigner.Sign(base.String(), userID, 30*time.Minute)
}

func shouldSelect(r *http.Request, property string) bool {
for _, v := range strings.Split(r.URL.Query().Get("$select"), ",") {
if strings.TrimSpace(v) == property {
return true
}
}
return false
}

func (g Graph) setDriveItemsDownloadURL(r *http.Request, items []*libregraph.DriveItem, infos []*storageprovider.ResourceInfo) {
if g.downloadSigner == nil || !shouldSelect(r, "@microsoft.graph.downloadUrl") {
return
}
user, ok := revactx.ContextGetUser(r.Context())
if !ok {
return
}
for i := range items {
if items[i].File == nil {
continue
}
u, err := g.signedDownloadURL(infos[i].GetId(), user.GetId().GetOpaqueId())
if err != nil {
continue
}
items[i].MicrosoftGraphDownloadUrl = &u
}
}
32 changes: 16 additions & 16 deletions services/graph/pkg/service/v0/driveitems.go
Original file line number Diff line number Diff line change
Expand Up @@ -204,12 +204,13 @@ func (g Graph) GetRootDriveChildren(w http.ResponseWriter, r *http.Request) {
return
}

files, err := formatDriveItems(g.logger, g.publicBaseURL, lRes.GetInfos())
files, err := g.formatDriveItems(lRes.GetInfos())
if err != nil {
g.logger.Error().Err(err).Msg("error encoding response as json")
errorcode.GeneralException.Render(w, r, http.StatusInternalServerError, err.Error())
return
}
g.setDriveItemsDownloadURL(r, files, lRes.GetInfos())

render.Status(r, http.StatusOK)
render.JSON(w, r, &ListResponse{Value: files})
Expand Down Expand Up @@ -269,11 +270,12 @@ func (g Graph) GetDriveItem(w http.ResponseWriter, r *http.Request) {
errorcode.GeneralException.Render(w, r, http.StatusInternalServerError, res.GetStatus().GetMessage())
return
}
driveItem, err := cs3ResourceToDriveItem(g.logger, g.publicBaseURL, res.GetInfo())
driveItem, err := g.cs3ResourceToDriveItem(res.GetInfo())
if err != nil {
errorcode.GeneralException.Render(w, r, http.StatusInternalServerError, err.Error())
return
}
g.setDriveItemsDownloadURL(r, []*libregraph.DriveItem{driveItem}, []*storageprovider.ResourceInfo{res.GetInfo()})

render.Status(r, http.StatusOK)
render.JSON(w, r, &driveItem)
Expand Down Expand Up @@ -337,11 +339,12 @@ func (g Graph) GetDriveItemChildren(w http.ResponseWriter, r *http.Request) {
return
}

files, err := formatDriveItems(g.logger, g.publicBaseURL, res.GetInfos())
files, err := g.formatDriveItems(res.GetInfos())
if err != nil {
errorcode.GeneralException.Render(w, r, http.StatusInternalServerError, err.Error())
return
}
g.setDriveItemsDownloadURL(r, files, res.GetInfos())

render.Status(r, http.StatusOK)
render.JSON(w, r, &ListResponse{Value: files})
Expand Down Expand Up @@ -385,10 +388,10 @@ func (g Graph) getRemoteItem(ctx context.Context, root *storageprovider.Resource
return item, nil
}

func formatDriveItems(logger *log.Logger, publicBaseURL *url.URL, mds []*storageprovider.ResourceInfo) ([]*libregraph.DriveItem, error) {
func (g BaseGraphService) formatDriveItems(mds []*storageprovider.ResourceInfo) ([]*libregraph.DriveItem, error) {
responses := make([]*libregraph.DriveItem, 0, len(mds))
for i := range mds {
res, err := cs3ResourceToDriveItem(logger, publicBaseURL, mds[i])
res, err := g.cs3ResourceToDriveItem(mds[i])
if err != nil {
return nil, err
}
Expand All @@ -402,19 +405,16 @@ func cs3TimestampToTime(t *types.Timestamp) time.Time {
return time.Unix(int64(t.GetSeconds()), int64(t.GetNanos()))
}

func cs3ResourceToDriveItem(logger *log.Logger, publicBaseURL *url.URL, res *storageprovider.ResourceInfo) (*libregraph.DriveItem, error) {
func (g BaseGraphService) cs3ResourceToDriveItem(res *storageprovider.ResourceInfo) (*libregraph.DriveItem, error) {
size := new(int64)
*size = int64(res.GetSize()) // TODO lurking overflow: make size of libregraph drive item use uint64

driveItem := &libregraph.DriveItem{
Id: libregraph.PtrString(storagespace.FormatResourceID(res.GetId())),
Size: size,
Id: libregraph.PtrString(storagespace.FormatResourceID(res.GetId())),
Size: size,
WebUrl: g.webURLForResource(res.GetId()),
}

webURL := *publicBaseURL
webURL.Path = path.Join(webURL.Path, "f", storagespace.FormatResourceID(res.GetId()))
driveItem.WebUrl = libregraph.PtrString(webURL.String())

if name := path.Base(res.GetPath()); name != "" {
driveItem.Name = &name
}
Expand Down Expand Up @@ -453,10 +453,10 @@ func cs3ResourceToDriveItem(logger *log.Logger, publicBaseURL *url.URL, res *sto
}

if res.GetArbitraryMetadata() != nil {
driveItem.Audio = cs3ResourceToDriveItemAudioFacet(logger, res)
driveItem.Image = cs3ResourceToDriveItemImageFacet(logger, res)
driveItem.Location = cs3ResourceToDriveItemLocationFacet(logger, res)
driveItem.Photo = cs3ResourceToDriveItemPhotoFacet(logger, res)
driveItem.Audio = cs3ResourceToDriveItemAudioFacet(g.logger, res)
driveItem.Image = cs3ResourceToDriveItemImageFacet(g.logger, res)
driveItem.Location = cs3ResourceToDriveItemLocationFacet(g.logger, res)
driveItem.Photo = cs3ResourceToDriveItemPhotoFacet(g.logger, res)
}

return driveItem, nil
Expand Down
Loading