initial /me/drives implementation

Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>
This commit is contained in:
Jörn Friedrich Dreyer
2021-05-03 15:30:57 +00:00
parent 322cb454c4
commit 449edd31c8
4 changed files with 176 additions and 25 deletions

View File

@@ -63,18 +63,23 @@ type Reva struct {
Address string
}
type Spaces struct {
WebDavBase string
}
// Config combines all available configuration parts.
type Config struct {
File string
WebdavNamespace string
Log Log
Debug Debug
HTTP HTTP
Server Server
Tracing Tracing
Ldap Ldap
OpenIDConnect OpenIDConnect
Reva Reva
File string
WebdavNamespace string
Log Log
Debug Debug
HTTP HTTP
Server Server
Tracing Tracing
Ldap Ldap
OpenIDConnect OpenIDConnect
Reva Reva
Spaces Spaces
Context context.Context
Supervised bool

View File

@@ -140,6 +140,15 @@ func ServerWithConfig(cfg *config.Config) []cli.Flag {
EnvVars: []string{"GRAPH_HTTP_NAMESPACE"},
Destination: &cfg.HTTP.Namespace,
},
&cli.StringFlag{
Name: "spaces-webdav-base",
Value: flags.OverrideDefaultString(cfg.Spaces.WebDavBase, "https://localhost:9200/dav/spaces/"),
Usage: "spaces webdav base URL",
EnvVars: []string{"GRAPH_SPACES_WEBDAV_BASE"},
Destination: &cfg.Spaces.WebDavBase,
},
&cli.StringFlag{
Name: "ldap-network",
Value: flags.OverrideDefaultString(cfg.Ldap.Network, "tcp"),
@@ -182,6 +191,7 @@ func ServerWithConfig(cfg *config.Config) []cli.Flag {
EnvVars: []string{"GRAPH_LDAP_BASEDN_GROUPS"},
Destination: &cfg.Ldap.BaseDNGroups,
},
&cli.StringFlag{
Name: "oidc-endpoint",
Value: flags.OverrideDefaultString(cfg.OpenIDConnect.Endpoint, "https://localhost:9200"),
@@ -202,6 +212,7 @@ func ServerWithConfig(cfg *config.Config) []cli.Flag {
EnvVars: []string{"GRAPH_OIDC_REALM"},
Destination: &cfg.OpenIDConnect.Realm,
},
&cli.StringFlag{
Name: "reva-gateway-addr",
Value: flags.OverrideDefaultString(cfg.Reva.Address, "127.0.0.1:9142"),

View File

@@ -1,16 +1,18 @@
package svc
import (
"math"
"net/http"
"net/url"
"strings"
"time"
"github.com/go-chi/render"
"google.golang.org/grpc/metadata"
gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1"
cs3rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1"
storageprovider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
types "github.com/cs3org/go-cs3apis/cs3/types/v1beta1"
"github.com/cs3org/reva/pkg/token"
msgraph "github.com/yaegashi/msgraph.go/v1.0"
)
@@ -33,6 +35,71 @@ func getToken(r *http.Request) string {
return tokens[0]
}
// GetDrives implements the Service interface.
func (g Graph) GetDrives(w http.ResponseWriter, r *http.Request) {
g.logger.Info().Msgf("Calling GetDrives")
accessToken := getToken(r)
if accessToken == "" {
g.logger.Error().Msg("no access token provided in request")
w.WriteHeader(http.StatusForbidden)
return
}
ctx := r.Context()
client, err := g.GetClient()
if err != nil {
g.logger.Err(err).Msg("error getting grpc client")
w.WriteHeader(http.StatusInternalServerError)
return
}
t := r.Header.Get("x-access-token")
ctx = token.ContextSetToken(ctx, t)
ctx = metadata.AppendToOutgoingContext(ctx, "x-access-token", t)
req := &storageprovider.ListStorageSpacesRequest{
Filters: []*storageprovider.ListStorageSpacesRequest_Filter{
{
Type: storageprovider.ListStorageSpacesRequest_Filter_TYPE_ID,
Term: &storageprovider.ListStorageSpacesRequest_Filter_Id{
Id: &storageprovider.StorageSpaceId{
OpaqueId: "1284d238-aa92-42ce-bdc4-0b0000009157", // FIXME dynamically discover home and other storages ... actually the storage registry should provide the list of storage spaces
},
},
},
},
}
res, err := client.ListStorageSpaces(ctx, req)
if err != nil {
g.logger.Error().Err(err).Msg("error sending list storage spaces grpc request")
w.WriteHeader(http.StatusInternalServerError)
return
}
if res.Status.Code != cs3rpc.Code_CODE_OK {
g.logger.Error().Err(err).Msg("error calling grpc list storage spaces")
w.WriteHeader(http.StatusInternalServerError)
return
}
wdu, err := url.Parse(g.config.Spaces.WebDavBase)
if err != nil {
g.logger.Error().Err(err).Msgf("error parsing url", err)
w.WriteHeader(http.StatusInternalServerError)
return
}
files, err := formatDrives(wdu, res.StorageSpaces)
if err != nil {
g.logger.Error().Err(err).Msgf("error encoding response as json %s", err)
w.WriteHeader(http.StatusInternalServerError)
return
}
render.Status(r, http.StatusOK)
render.JSON(w, r, &listResponse{Value: files})
}
// GetRootDriveChildren implements the Service interface.
func (g Graph) GetRootDriveChildren(w http.ResponseWriter, r *http.Request) {
g.logger.Info().Msgf("Calling GetRootDriveChildren")
@@ -53,15 +120,9 @@ func (g Graph) GetRootDriveChildren(w http.ResponseWriter, r *http.Request) {
return
}
// get reva token
authReq := &gateway.AuthenticateRequest{
Type: "bearer",
ClientSecret: accessToken,
}
authRes, _ := client.Authenticate(ctx, authReq);
ctx = token.ContextSetToken(ctx, authRes.Token)
ctx = metadata.AppendToOutgoingContext(ctx, "x-access-token", authRes.Token)
t := r.Header.Get("x-access-token")
ctx = token.ContextSetToken(ctx, t)
ctx = metadata.AppendToOutgoingContext(ctx, "x-access-token", t)
g.logger.Info().Msgf("provides access token %v", ctx)
@@ -95,13 +156,14 @@ func (g Graph) GetRootDriveChildren(w http.ResponseWriter, r *http.Request) {
render.JSON(w, r, &listResponse{Value: files})
}
func cs3TimestampToTime(t *types.Timestamp) time.Time {
return time.Unix(int64(t.Seconds), int64(t.Nanos))
}
func cs3ResourceToDriveItem(res *storageprovider.ResourceInfo) (*msgraph.DriveItem, error) {
size := new(int)
*size = int(res.Size) // uint64 -> int :boom:
name := strings.TrimPrefix(res.Path, "/home/")
lastModified := new(time.Time)
*lastModified = time.Unix(int64(res.Mtime.Seconds), int64(res.Mtime.Nanos))
driveItem := &msgraph.DriveItem{
BaseItem: msgraph.BaseItem{
@@ -110,19 +172,21 @@ func cs3ResourceToDriveItem(res *storageprovider.ResourceInfo) (*msgraph.DriveIt
ID: &res.Id.OpaqueId,
},
Name: &name,
LastModifiedDateTime: lastModified,
ETag: &res.Etag,
},
Size: size,
}
if res.Mtime != nil {
lastModified := cs3TimestampToTime(res.Mtime)
driveItem.BaseItem.LastModifiedDateTime = &lastModified
}
if res.Type == storageprovider.ResourceType_RESOURCE_TYPE_FILE {
driveItem.File = &msgraph.File{
MimeType: &res.MimeType,
}
}
if res.Type == storageprovider.ResourceType_RESOURCE_TYPE_CONTAINER {
driveItem.Folder = &msgraph.Folder{
}
driveItem.Folder = &msgraph.Folder{}
}
return driveItem, nil
}
@@ -139,3 +203,73 @@ func formatDriveItems(mds []*storageprovider.ResourceInfo) ([]*msgraph.DriveItem
return responses, nil
}
func cs3StorageSpaceToDrive(baseUrl *url.URL, space *storageprovider.StorageSpace) (*msgraph.Drive, error) {
rootId := space.Root.StorageId + "!" + space.Root.OpaqueId
drive := &msgraph.Drive{
BaseItem: msgraph.BaseItem{
Entity: msgraph.Entity{
ID: &space.Id.OpaqueId,
},
Name: &space.Name,
//"createdDateTime": "string (timestamp)", // TODO read from StorageSpace ... needs Opaque for now
//"description": "string", // TODO read from StorageSpace ... needs Opaque for now
},
Owner: &msgraph.IdentitySet{
User: &msgraph.Identity{
ID: &space.Owner.Id.OpaqueId,
// DisplayName: , TODO read and cache from users provider
},
},
DriveType: &space.SpaceType,
Root: &msgraph.DriveItem{
BaseItem: msgraph.BaseItem{
Entity: msgraph.Entity{
ID: &rootId,
},
},
},
}
if baseUrl != nil {
// TODO read from StorageSpace ... needs Opaque for now
// TODO how do we build the url?
// for now: read from request
webDavURL := baseUrl.String() + rootId
drive.Root.WebDavURL = &webDavURL
}
if space.Mtime != nil {
lastModified := cs3TimestampToTime(space.Mtime)
drive.BaseItem.LastModifiedDateTime = &lastModified
}
if space.Quota != nil {
// FIXME use https://github.com/owncloud/open-graph-api and return proper int64
var t int
if space.Quota.QuotaMaxBytes > math.MaxInt32 {
t = math.MaxInt32
} else {
t = int(space.Quota.QuotaMaxBytes)
}
drive.Quota = &msgraph.Quota{
Total: &t,
}
}
// FIXME use coowner from https://github.com/owncloud/open-graph-api
return drive, nil
}
func formatDrives(baseUrl *url.URL, mds []*storageprovider.StorageSpace) ([]*msgraph.Drive, error) {
responses := make([]*msgraph.Drive, 0, len(mds))
for i := range mds {
res, err := cs3StorageSpaceToDrive(baseUrl, mds[i])
if err != nil {
return nil, err
}
responses = append(responses, res)
}
return responses, nil
}

View File

@@ -33,6 +33,7 @@ func NewService(opts ...Option) Service {
r.Route("/v1.0", func(r chi.Router) {
r.Route("/me", func(r chi.Router) {
r.Get("/", svc.GetMe)
r.Get("/drives", svc.GetDrives)
r.Get("/drive/root/children", svc.GetRootDriveChildren)
})
r.Route("/users", func(r chi.Router) {