diff --git a/cmd/reva/gen/gen.go b/cmd/reva/gen/gen.go index ff42ff88887..5703e8e3e27 100644 --- a/cmd/reva/gen/gen.go +++ b/cmd/reva/gen/gen.go @@ -375,12 +375,16 @@ func WriteConfig(p string, cs string, dd string, dp string) { var usersTemplate = `[{{range $i, $e := .}}{{if $i}},{{end}} { + "id": { + "idp": "{{$e.Iss}}", + "opaque_id": "{{$e.Sub}}", + }, "sub": "{{$e.Sub}}", "iss": "{{$e.Iss}}", "username": "{{$e.Username}}", "secret": "{{$e.Secret}}", "mail": "{{$e.Mail}}", - "displayname": "{{$e.Displayname}}" + "display_name": "{{$e.Displayname}}" }{{end}} ] ` diff --git a/cmd/revad/gateway.toml b/cmd/revad/gateway.toml index 9f13feb0fc2..091dd983877 100644 --- a/cmd/revad/gateway.toml +++ b/cmd/revad/gateway.toml @@ -42,5 +42,4 @@ secret = "Pive-Fumkiu4" prefix = "owncloud" chunk_folder = "/var/tmp/revad/chunks" gatewaysvc = "localhost:10000" -enable_cors = true diff --git a/cmd/revad/revad.toml b/cmd/revad/revad.toml index 7ee1d8027b5..3896a678218 100644 --- a/cmd/revad/revad.toml +++ b/cmd/revad/revad.toml @@ -43,7 +43,6 @@ prefix = "ui" prefix = "owncloud" chunk_folder = "/var/tmp/revad/chunks" gatewaysvc = "localhost:9999" -enable_cors = true [http.services.datasvc] driver = "local" diff --git a/cmd/revad/svcs/grpcsvcs/authsvc/authsvc.go b/cmd/revad/svcs/grpcsvcs/authsvc/authsvc.go index ad3e7518d01..3067412ff91 100644 --- a/cmd/revad/svcs/grpcsvcs/authsvc/authsvc.go +++ b/cmd/revad/svcs/grpcsvcs/authsvc/authsvc.go @@ -134,7 +134,7 @@ func (s *service) GenerateAccessToken(ctx context.Context, req *authv0alphapb.Ge ctx, err := s.authmgr.Authenticate(ctx, username, password) if err != nil { - log.Error().Err(err).Msg("error authentication user") + log.Error().Err(err).Msg("error authenticating user") status := &rpcpb.Status{Code: rpcpb.Code_CODE_UNAUTHENTICATED} res := &authv0alphapb.GenerateAccessTokenResponse{Status: status} return res, nil diff --git a/cmd/revad/svcs/grpcsvcs/storageprovidersvc/storageprovidersvc.go b/cmd/revad/svcs/grpcsvcs/storageprovidersvc/storageprovidersvc.go index a9c9a1db453..5b3e75647b9 100644 --- a/cmd/revad/svcs/grpcsvcs/storageprovidersvc/storageprovidersvc.go +++ b/cmd/revad/svcs/grpcsvcs/storageprovidersvc/storageprovidersvc.go @@ -410,7 +410,7 @@ func (s *service) ListContainer(ctx context.Context, req *storageproviderv0alpha return res, nil } - var infos = make([]*storageproviderv0alphapb.ResourceInfo, 0, len(mds)) + infos := []*storageproviderv0alphapb.ResourceInfo{} for _, md := range mds { s.wrap(md) infos = append(infos, md) diff --git a/cmd/revad/svcs/httpsvcs/ocdavsvc/files.go b/cmd/revad/svcs/httpsvcs/ocdavsvc/files.go index 465a3221f19..9abcccf1395 100644 --- a/cmd/revad/svcs/httpsvcs/ocdavsvc/files.go +++ b/cmd/revad/svcs/httpsvcs/ocdavsvc/files.go @@ -45,11 +45,11 @@ func (h *FilesHandler) Handler(s *svc) http.Handler { switch r.Method { case "PROPFIND": s.doPropfind(w, r) - case "OPTIONS": + case http.MethodOptions: s.doOptions(w, r) - case "HEAD": + case http.MethodHead: s.doHead(w, r) - case "GET": + case http.MethodGet: s.doGet(w, r) case "LOCK": s.doLock(w, r) @@ -63,9 +63,9 @@ func (h *FilesHandler) Handler(s *svc) http.Handler { s.doMove(w, r) case "COPY": s.doCopy(w, r) - case "PUT": + case http.MethodPut: s.doPut(w, r) - case "DELETE": + case http.MethodDelete: s.doDelete(w, r) case "REPORT": s.doReport(w, r) diff --git a/cmd/revad/svcs/httpsvcs/ocdavsvc/meta.go b/cmd/revad/svcs/httpsvcs/ocdavsvc/meta.go index 2cd5efc652f..bf4947c7623 100644 --- a/cmd/revad/svcs/httpsvcs/ocdavsvc/meta.go +++ b/cmd/revad/svcs/httpsvcs/ocdavsvc/meta.go @@ -19,6 +19,7 @@ package ocdavsvc import ( + "encoding/base64" "net/http" "github.com/cs3org/reva/cmd/revad/svcs/httpsvcs" @@ -46,12 +47,18 @@ func (h *MetaHandler) Handler(s *svc) http.Handler { return } + decodedID, err := base64.StdEncoding.DecodeString(id) + if err != nil { + http.Error(w, "400 Bad Request", http.StatusBadRequest) + return + } + var head string head, r.URL.Path = httpsvcs.ShiftPath(r.URL.Path) switch head { case "v": - h.VersionsHandler.Handler(s, id).ServeHTTP(w, r) + h.VersionsHandler.Handler(s, string(decodedID)).ServeHTTP(w, r) default: w.WriteHeader(http.StatusNotFound) } diff --git a/cmd/revad/svcs/httpsvcs/ocdavsvc/propfind.go b/cmd/revad/svcs/httpsvcs/ocdavsvc/propfind.go index 91d22a2969c..b2dab5e0429 100644 --- a/cmd/revad/svcs/httpsvcs/ocdavsvc/propfind.go +++ b/cmd/revad/svcs/httpsvcs/ocdavsvc/propfind.go @@ -19,8 +19,8 @@ package ocdavsvc import ( - "bytes" "context" + "encoding/base64" "encoding/xml" "fmt" "io" @@ -216,11 +216,15 @@ func (s *svc) mdToPropResponse(ctx context.Context, md *storageproviderv0alphapb // the fileID must be xml-escaped as there are cases like public links // that contains a path as the file id. This path can contain &, for example, // which if it is not encoded properly, will result in an empty view for the user - var fileIDEscaped bytes.Buffer - if err := xml.EscapeText(&fileIDEscaped, []byte(fmt.Sprintf("%s:%s", md.Id.StorageId, md.Id.OpaqueId))); err != nil { - return nil, err - } - ocID := s.newProp("oc:id", fileIDEscaped.String()) + //var fileIDEscaped bytes.Buffer + //if err := xml.EscapeText(&fileIDEscaped, []byte(fmt.Sprintf("%s:%s", md.Id.StorageId, md.Id.OpaqueId))); err != nil { + // return nil, err + //} + //ocID := s.newProp("oc:fileid", fileIDEscaped.String()) + // TODO(jfd) xmlencoding still contains slashes, but the fileid might be used in the url as well. + // for the versions endpoint we need to either also urlencode ... or just base64 encode ... it should be opaque anyway. + base64id := base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("%s:%s", md.Id.StorageId, md.Id.OpaqueId))) + ocID := s.newProp("oc:fileid", base64id) propList = append(propList, ocID) // PropStat, only HTTP/1.1 200 is sent. diff --git a/cmd/revad/svcs/httpsvcs/ocdavsvc/versions.go b/cmd/revad/svcs/httpsvcs/ocdavsvc/versions.go index fdb3515ba8e..8d75d19dd26 100644 --- a/cmd/revad/svcs/httpsvcs/ocdavsvc/versions.go +++ b/cmd/revad/svcs/httpsvcs/ocdavsvc/versions.go @@ -45,6 +45,10 @@ func (h *VersionsHandler) Handler(s *svc, fileid string) http.Handler { var timestamp string timestamp, r.URL.Path = httpsvcs.ShiftPath(r.URL.Path) + if r.Method == http.MethodOptions { + s.doOptions(w, r) + return + } if timestamp == "" && r.Method == "PROPFIND" { // TODO(jfd) list versions h.doPropfind(w, r, s, fileid) @@ -59,11 +63,11 @@ func (h *VersionsHandler) Handler(s *svc, fileid string) http.Handler { h.doPropfind(w, r, s, fileid) //case "HEAD": // TODO(jfd) since we cant GET ... there is no HEAD // s.doHead(w, r) - case "GET": // TODO(jfd) it seems we cannot directly GET version content with cs3 ... + case http.MethodGet: // TODO(jfd) it seems we cannot directly GET version content with cs3 ... s.doGet(w, r) case "COPY": // TODO(jfd) restore version to Destination, but cs3api has no destination s.doCopy(w, r) - case "DELETE": // TODO(jfd) cs3api has no delete file version call + case http.MethodDelete: // TODO(jfd) cs3api has no delete file version call s.doDelete(w, r) default: http.Error(w, "403 Forbidden", http.StatusForbidden) diff --git a/cmd/revad/svcs/httpsvcs/ocdavsvc/webdav.go b/cmd/revad/svcs/httpsvcs/ocdavsvc/webdav.go index 3aa0e8706e0..c891bf9d907 100644 --- a/cmd/revad/svcs/httpsvcs/ocdavsvc/webdav.go +++ b/cmd/revad/svcs/httpsvcs/ocdavsvc/webdav.go @@ -41,7 +41,7 @@ func (h *WebDavHandler) Handler(s *svc) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { log := appctx.GetLogger(r.Context()) - if r.Method == "OPTIONS" { + if r.Method == http.MethodOptions { // no need for the user, and we need to be able // to answer preflight checks, which have no auth headers s.doOptions(w, r) @@ -87,9 +87,9 @@ func (h *WebDavHandler) Handler(s *svc) http.Handler { switch r.Method { case "PROPFIND": s.doPropfind(w, r) - case "HEAD": + case http.MethodHead: s.doHead(w, r) - case "GET": + case http.MethodGet: s.doGet(w, r) case "LOCK": s.doLock(w, r) @@ -103,9 +103,9 @@ func (h *WebDavHandler) Handler(s *svc) http.Handler { s.doMove(w, r) case "COPY": s.doCopy(w, r) - case "PUT": + case http.MethodPut: s.doPut(w, r) - case "DELETE": + case http.MethodDelete: s.doDelete(w, r) case "REPORT": s.doReport(w, r) diff --git a/cmd/revad/svcs/httpsvcs/ocssvc/shares.go b/cmd/revad/svcs/httpsvcs/ocssvc/shares.go index fff8016ff94..bf549fac02a 100644 --- a/cmd/revad/svcs/httpsvcs/ocssvc/shares.go +++ b/cmd/revad/svcs/httpsvcs/ocssvc/shares.go @@ -19,8 +19,8 @@ package ocssvc import ( - "encoding/json" "context" + "encoding/json" "fmt" "net/http" "path" @@ -68,18 +68,6 @@ func getUserManager(manager string, m map[string]map[string]interface{}) (user.M return nil, fmt.Errorf("driver %s not found for user manager", manager) } -func (h *SharesHandler) getSClient() (storageproviderv0alphapb.StorageProviderServiceClient, error) { - return pool.GetStorageProviderServiceClient(h.gatewaySvc) -} - -func (h *SharesHandler) getUClient() (usershareproviderv0alphapb.UserShareProviderServiceClient, error) { - return pool.GetUserShareProviderClient(h.gatewaySvc) -} - -func (h *SharesHandler) getPClient() (publicshareproviderv0alphapb.PublicShareProviderServiceClient, error) { - return pool.GetPublicShareProviderClient(h.gatewaySvc) -} - func (h *SharesHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { log := appctx.GetLogger(r.Context()) @@ -153,6 +141,8 @@ func (h *SharesHandler) userAsMatch(u *authv0alphapb.User) *MatchData { Label: u.DisplayName, Value: &MatchValueData{ ShareType: int(shareTypeUser), + // TODO(jfd) find more robust userid + // username might be ok as it is uniqe at a given point in time ShareWith: u.Username, }, } @@ -182,6 +172,20 @@ func (h *SharesHandler) createShare(w http.ResponseWriter, r *http.Request) { return } + // find recipient based on username + users, err := h.userManager.FindUsers(ctx, shareWith) + if err != nil { + WriteOCSError(w, r, MetaServerError.StatusCode, "error searching recipient", err) + return + } + var recipient *authv0alphapb.User + for _, user := range users { + if user.Username == shareWith { + recipient = user + break + } + } + // we need to prefix the path with the user id u, ok := user.ContextGetUser(ctx) if !ok { @@ -215,13 +219,13 @@ func (h *SharesHandler) createShare(w http.ResponseWriter, r *http.Request) { // map role to permissions var permissions *storageproviderv0alphapb.ResourcePermissions - permissions, err := h.role2CS3Permissions(role) + permissions, err = h.role2CS3Permissions(role) if err != nil { log.Warn().Err(err).Msg("unknown role, mapping legacy permissions") permissions = asCS3Permissions(pint, nil) } - uClient, err := h.getUClient() + uClient, err := pool.GetUserShareProviderClient(h.gatewaySvc) if err != nil { WriteOCSError(w, r, MetaServerError.StatusCode, "error getting grpc client", err) return @@ -241,7 +245,7 @@ func (h *SharesHandler) createShare(w http.ResponseWriter, r *http.Request) { }, } - sClient, err := h.getSClient() + sClient, err := pool.GetStorageProviderServiceClient(h.gatewaySvc) if err != nil { WriteOCSError(w, r, MetaServerError.StatusCode, "error getting storage grpc client", err) return @@ -275,10 +279,7 @@ func (h *SharesHandler) createShare(w http.ResponseWriter, r *http.Request) { Grant: &usershareproviderv0alphapb.ShareGrant{ Grantee: &storageproviderv0alphapb.Grantee{ Type: storageproviderv0alphapb.GranteeType_GRANTEE_TYPE_USER, - Id: &typespb.UserId{ - // Idp: TODO get from where? - OpaqueId: shareWith, - }, + Id: recipient.Id, }, Permissions: &usershareproviderv0alphapb.SharePermissions{ Permissions: permissions, @@ -469,7 +470,7 @@ func (h *SharesHandler) updateShare(w http.ResponseWriter, r *http.Request) { shareID := strings.TrimLeft(r.URL.Path, "/") // TODO we need to lookup the storage that is responsible for this share - uClient, err := h.getUClient() + uClient, err := pool.GetUserShareProviderClient(h.gatewaySvc) if err != nil { WriteOCSError(w, r, MetaServerError.StatusCode, "error getting grpc client", err) return @@ -562,7 +563,7 @@ func (h *SharesHandler) listShares(w http.ResponseWriter, r *http.Request) { log.Debug().Str("path", p).Str("fn", fn).Interface("user", u).Msg("resolved path for user") // first check if the file exists - sClient, err := h.getSClient() + sClient, err := pool.GetStorageProviderServiceClient(h.gatewaySvc) if err != nil { WriteOCSError(w, r, MetaServerError.StatusCode, "error getting grpc storage provider client", err) return @@ -603,7 +604,7 @@ func (h *SharesHandler) listShares(w http.ResponseWriter, r *http.Request) { // fetch user shares if configured if h.gatewaySvc != "" { - uClient, err := h.getUClient() + uClient, err := pool.GetUserShareProviderClient(h.gatewaySvc) if err != nil { WriteOCSError(w, r, MetaServerError.StatusCode, "error getting grpc user share handler client", err) return @@ -642,53 +643,45 @@ func (h *SharesHandler) listShares(w http.ResponseWriter, r *http.Request) { // TODO fetch federated shares // fetch public link shares if configured - if h.gatewaySvc != "" { - pClient, err := h.getPClient() - if err != nil { - WriteOCSError(w, r, MetaServerError.StatusCode, "error getting grpc public share provider client", err) - return - } - req := &publicshareproviderv0alphapb.ListPublicSharesRequest{} - res, err := pClient.ListPublicShares(ctx, req) - if err != nil { - WriteOCSError(w, r, MetaServerError.StatusCode, "error sending a grpc list shares request", err) - return - } - if res.Status.Code != rpcpb.Code_CODE_OK { - if res.Status.Code == rpcpb.Code_CODE_NOT_FOUND { - WriteOCSError(w, r, MetaNotFound.StatusCode, "not found", nil) + /* + if h.gatewaySvc != "" { + pClient, err := pool.GetPublicShareProviderClient(h.gatewaySvc) + if err != nil { + WriteOCSError(w, r, MetaServerError.StatusCode, "error getting grpc public share provider client", err) return } - WriteOCSError(w, r, MetaServerError.StatusCode, "grpc list shares request failed", err) - return - } - for _, s := range res.Share { - share := h.publicShare2ShareData(s) - err := h.addFileInfo(ctx, share, info) + req := &publicshareproviderv0alphapb.ListPublicSharesRequest{} + res, err := pClient.ListPublicShares(ctx, req) if err != nil { - WriteOCSError(w, r, MetaServerError.StatusCode, "error adding file info", err) + WriteOCSError(w, r, MetaServerError.StatusCode, "error sending a grpc list shares request", err) return } - log.Debug().Interface("share", s).Interface("info", info).Interface("shareData", share).Msg("mapped") - shares = append(shares, share) + if res.Status.Code != rpcpb.Code_CODE_OK { + if res.Status.Code == rpcpb.Code_CODE_NOT_FOUND { + WriteOCSError(w, r, MetaNotFound.StatusCode, "not found", nil) + return + } + WriteOCSError(w, r, MetaServerError.StatusCode, "grpc list shares request failed", err) + return + } + for _, s := range res.Share { + share := h.publicShare2ShareData(s) + err := h.addFileInfo(ctx, share, info) + if err != nil { + WriteOCSError(w, r, MetaServerError.StatusCode, "error adding file info", err) + return + } + log.Debug().Interface("share", s).Interface("info", info).Interface("shareData", share).Msg("mapped") + shares = append(shares, share) + } } - } + */ WriteOCSSuccess(w, r, &SharesData{ Shares: shares, }) } -/* TODO implement caching here? -func (h *SharesHandler) lookupUser(ctx context.Context, uid *typespb.UserId) (*authv0alphapb.User, error) { - user, err := h.userManager.GetUser(ctx, uid) - if err != nil { - return nil, err - } - return user -} -*/ - func (h *SharesHandler) addFileInfo(ctx context.Context, s *ShareData, info *storageproviderv0alphapb.ResourceInfo) error { if info != nil { // TODO The owner is not set in the storage stat metadata ... diff --git a/cmd/revad/users.demo.json b/cmd/revad/users.demo.json index b53626d557a..99bd81dfc1f 100644 --- a/cmd/revad/users.demo.json +++ b/cmd/revad/users.demo.json @@ -1,26 +1,32 @@ [ { - "sub": "37a08ed30093a133b1bb4ae0b8f3601f", - "iss": "localhost:9998", + "id": { + "opaque_id": "37a08ed30093a133b1bb4ae0b8f3601f", + "idp": "localhost" + }, "username": "einstein", "secret": "relativity", "mail": "einstein@example.org", - "displayname": "Albert Einstein" + "display_name": "Albert Einstein" }, { - "sub": "b3725122c9d3bfef5664619e08e31877", - "iss": "localhost:9998", + "id": { + "opaque_id": "b3725122c9d3bfef5664619e08e31877", + "idp": "localhost" + }, "username": "marie", "secret": "radioactivity", "mail": "marie@example.org", - "displayname": "Marie Curie" + "display_name": "Marie Curie" }, { - "sub": "6ae199a93c381bf6d5de27491139d3f9", - "iss": "localhost:9998", + "id": { + "opaque_id": "6ae199a93c381bf6d5de27491139d3f9", + "idp": "localhost" + }, "username": "richard", "secret": "superfluidity", "mail": "richard@example.org", - "displayname": "Richard Feynman" + "display_name": "Richard Feynman" } ] diff --git a/cmd/revad/users.json b/cmd/revad/users.json index 1b05f71a99f..ebed6e99f75 100644 --- a/cmd/revad/users.json +++ b/cmd/revad/users.json @@ -1,10 +1,12 @@ [ { - "sub": "a48cb0ed-3375-4b94-bdcd-3cac6e201b7b", - "iss": "example.com", + "id": { + "opaque_id": "a48cb0ed-3375-4b94-bdcd-3cac6e201b7b", + "idp": "example.com" + }, "username": "admin", "secret": "secret", "mail": "admin@example.com", - "displayname": "Admin" + "display_name": "Admin" } ] diff --git a/cmd/revad/users.oidc.json b/cmd/revad/users.oidc.json index 81ced82a46f..27c1ff0f2bf 100644 --- a/cmd/revad/users.oidc.json +++ b/cmd/revad/users.oidc.json @@ -1,26 +1,32 @@ [ { - "sub": "c6e5995d6c7fa1986b830b78b478e6c2", - "iss": "localhost:9998", + "id": { + "opaque_id": "c6e5995d6c7fa1986b830b78b478e6c2", + "idp": "localhost:9998" + }, "username": "aaliyah_abernathy", "secret": "secret", "mail": "aaliyah_abernathy@owncloudqa.com", - "displayname": "Aaliyah Abernathy" + "display_name": "Aaliyah Abernathy" }, { - "sub": "9fb5f8d212cbf3fc55f1bf67d97ed05d", - "iss": "localhost:9998", + "id": { + "opaque_id": "9fb5f8d212cbf3fc55f1bf67d97ed05d", + "idp": "localhost:9998" + }, "username": "aaliyah_adams", "secret": "secret", "mail": "aaliyah_adams@owncloudqa.com", - "displayname": "Aaliyah Adams" + "display_name": "Aaliyah Adams" }, { - "sub": "a84075b398fe6a0aee1155f8ead13331", - "iss": "localhost:9998", + "id": { + "opaque_id": "a84075b398fe6a0aee1155f8ead13331", + "idp": "localhost:9998" + }, "username": "aaliyah_anderson", "secret": "secret", "mail": "aaliyah_anderson@owncloudqa.com", - "displayname": "Aaliyah Anderson" + "display_name": "Aaliyah Anderson" } ] diff --git a/pkg/user/manager/json/json.go b/pkg/user/manager/json/json.go index 274ebd9a3ab..562fd7862b6 100644 --- a/pkg/user/manager/json/json.go +++ b/pkg/user/manager/json/json.go @@ -82,14 +82,15 @@ func New(m map[string]interface{}) (user.Manager, error) { func (m *manager) GetUser(ctx context.Context, uid *typespb.UserId) (*authv0alphapb.User, error) { for _, u := range m.users { - if u.Username == uid.OpaqueId { + // TODO(jfd) we should also compare idp / iss? + if u.Id.GetOpaqueId() == uid.OpaqueId { return u, nil } } return nil, errtypes.NotFound(uid.OpaqueId) } -// TODO search Opaque? compare sub? +// TODO(jfd) search Opaque? compare sub? func userContains(u *authv0alphapb.User, query string) bool { return strings.Contains(u.Username, query) || strings.Contains(u.DisplayName, query) || strings.Contains(u.Mail, query) } diff --git a/pkg/user/manager/oidc/oidc.go b/pkg/user/manager/oidc/oidc.go index a8c309d76ab..c4fb19fca6f 100644 --- a/pkg/user/manager/oidc/oidc.go +++ b/pkg/user/manager/oidc/oidc.go @@ -50,8 +50,13 @@ func (m *manager) GetUser(ctx context.Context, uid *typespb.UserId) (*authv0alph } user := &authv0alphapb.User{ - Subject: claims.Sub, // a stable non reassignable id - Issuer: claims.Iss, // in the scope of this issuer + // TODO(jfd) clean up idp = iss, sub = opaque ... is redundant + Id: &typespb.UserId{ + OpaqueId: claims.Sub, // a stable non reassignable id + Idp: claims.Iss, // in the scope of this issuer + }, + // Subject: claims.Sub, // TODO(labkode) remove from CS3, is in Id + // Issuer: claims.Iss, // TODO(labkode) remove from CS3, is in Id Username: claims.PreferredUsername, Groups: []string{}, Mail: claims.Email,