From c217a6721bf42849b8f09c13215133ab937307fe Mon Sep 17 00:00:00 2001 From: Phil Davis Date: Wed, 17 Feb 2021 11:10:05 +0300 Subject: [PATCH 01/12] Bump core commit id (#1482) --- .drone.yml | 28 +++++++++---------- .../expected-failures-on-OCIS-storage.md | 11 +++++++- .../expected-failures-on-OWNCLOUD-storage.md | 7 +++++ 3 files changed, 31 insertions(+), 15 deletions(-) diff --git a/.drone.yml b/.drone.yml index 823e58c247..34b126a4bc 100644 --- a/.drone.yml +++ b/.drone.yml @@ -488,7 +488,7 @@ steps: - git clone -b master --depth=1 https://github.com/owncloud/testing.git /drone/src/tmp/testing - git clone -b master --single-branch --no-tags https://github.com/owncloud/core.git /drone/src/tmp/testrunner - cd /drone/src/tmp/testrunner - - git checkout 62c3d362651fc785952ec7bd3046cd9f0d108cf3 + - git checkout 2056240d53091ea69336d25ad57e3f4afc0c37d3 - name: localAPIAcceptanceTestsOwncloudStorage image: registry.cern.ch/docker.io/owncloudci/php:7.4 @@ -562,7 +562,7 @@ steps: - git clone -b master --depth=1 https://github.com/owncloud/testing.git /drone/src/tmp/testing - git clone -b master --single-branch --no-tags https://github.com/owncloud/core.git /drone/src/tmp/testrunner - cd /drone/src/tmp/testrunner - - git checkout 62c3d362651fc785952ec7bd3046cd9f0d108cf3 + - git checkout 2056240d53091ea69336d25ad57e3f4afc0c37d3 - name: localAPIAcceptanceTestsOcisStorage image: registry.cern.ch/docker.io/owncloudci/php:7.4 @@ -631,7 +631,7 @@ steps: - git clone -b master --depth=1 https://github.com/owncloud/testing.git /drone/src/tmp/testing - git clone -b master --single-branch --no-tags https://github.com/owncloud/core.git /drone/src/tmp/testrunner - cd /drone/src/tmp/testrunner - - git checkout 62c3d362651fc785952ec7bd3046cd9f0d108cf3 + - git checkout 2056240d53091ea69336d25ad57e3f4afc0c37d3 - name: oC10APIAcceptanceTestsOcisStorage image: registry.cern.ch/docker.io/owncloudci/php:7.4 @@ -703,7 +703,7 @@ steps: - git clone -b master --depth=1 https://github.com/owncloud/testing.git /drone/src/tmp/testing - git clone -b master --single-branch --no-tags https://github.com/owncloud/core.git /drone/src/tmp/testrunner - cd /drone/src/tmp/testrunner - - git checkout 62c3d362651fc785952ec7bd3046cd9f0d108cf3 + - git checkout 2056240d53091ea69336d25ad57e3f4afc0c37d3 - name: oC10APIAcceptanceTestsOcisStorage image: registry.cern.ch/docker.io/owncloudci/php:7.4 @@ -775,7 +775,7 @@ steps: - git clone -b master --depth=1 https://github.com/owncloud/testing.git /drone/src/tmp/testing - git clone -b master --single-branch --no-tags https://github.com/owncloud/core.git /drone/src/tmp/testrunner - cd /drone/src/tmp/testrunner - - git checkout 62c3d362651fc785952ec7bd3046cd9f0d108cf3 + - git checkout 2056240d53091ea69336d25ad57e3f4afc0c37d3 - name: oC10APIAcceptanceTestsOcisStorage image: registry.cern.ch/docker.io/owncloudci/php:7.4 @@ -847,7 +847,7 @@ steps: - git clone -b master --depth=1 https://github.com/owncloud/testing.git /drone/src/tmp/testing - git clone -b master --single-branch --no-tags https://github.com/owncloud/core.git /drone/src/tmp/testrunner - cd /drone/src/tmp/testrunner - - git checkout 62c3d362651fc785952ec7bd3046cd9f0d108cf3 + - git checkout 2056240d53091ea69336d25ad57e3f4afc0c37d3 - name: oC10APIAcceptanceTestsOcisStorage image: registry.cern.ch/docker.io/owncloudci/php:7.4 @@ -919,7 +919,7 @@ steps: - git clone -b master --depth=1 https://github.com/owncloud/testing.git /drone/src/tmp/testing - git clone -b master --single-branch --no-tags https://github.com/owncloud/core.git /drone/src/tmp/testrunner - cd /drone/src/tmp/testrunner - - git checkout 62c3d362651fc785952ec7bd3046cd9f0d108cf3 + - git checkout 2056240d53091ea69336d25ad57e3f4afc0c37d3 - name: oC10APIAcceptanceTestsOcisStorage image: registry.cern.ch/docker.io/owncloudci/php:7.4 @@ -991,7 +991,7 @@ steps: - git clone -b master --depth=1 https://github.com/owncloud/testing.git /drone/src/tmp/testing - git clone -b master --single-branch --no-tags https://github.com/owncloud/core.git /drone/src/tmp/testrunner - cd /drone/src/tmp/testrunner - - git checkout 62c3d362651fc785952ec7bd3046cd9f0d108cf3 + - git checkout 2056240d53091ea69336d25ad57e3f4afc0c37d3 - name: oC10APIAcceptanceTestsOcisStorage image: registry.cern.ch/docker.io/owncloudci/php:7.4 @@ -1063,7 +1063,7 @@ steps: - git clone -b master --depth=1 https://github.com/owncloud/testing.git /drone/src/tmp/testing - git clone -b master --single-branch --no-tags https://github.com/owncloud/core.git /drone/src/tmp/testrunner - cd /drone/src/tmp/testrunner - - git checkout 62c3d362651fc785952ec7bd3046cd9f0d108cf3 + - git checkout 2056240d53091ea69336d25ad57e3f4afc0c37d3 - name: oC10APIAcceptanceTestsOwncloudStorage image: registry.cern.ch/docker.io/owncloudci/php:7.4 @@ -1140,7 +1140,7 @@ steps: - git clone -b master --depth=1 https://github.com/owncloud/testing.git /drone/src/tmp/testing - git clone -b master --single-branch --no-tags https://github.com/owncloud/core.git /drone/src/tmp/testrunner - cd /drone/src/tmp/testrunner - - git checkout 62c3d362651fc785952ec7bd3046cd9f0d108cf3 + - git checkout 2056240d53091ea69336d25ad57e3f4afc0c37d3 - name: oC10APIAcceptanceTestsOwncloudStorage image: registry.cern.ch/docker.io/owncloudci/php:7.4 @@ -1217,7 +1217,7 @@ steps: - git clone -b master --depth=1 https://github.com/owncloud/testing.git /drone/src/tmp/testing - git clone -b master --single-branch --no-tags https://github.com/owncloud/core.git /drone/src/tmp/testrunner - cd /drone/src/tmp/testrunner - - git checkout 62c3d362651fc785952ec7bd3046cd9f0d108cf3 + - git checkout 2056240d53091ea69336d25ad57e3f4afc0c37d3 - name: oC10APIAcceptanceTestsOwncloudStorage image: registry.cern.ch/docker.io/owncloudci/php:7.4 @@ -1294,7 +1294,7 @@ steps: - git clone -b master --depth=1 https://github.com/owncloud/testing.git /drone/src/tmp/testing - git clone -b master --single-branch --no-tags https://github.com/owncloud/core.git /drone/src/tmp/testrunner - cd /drone/src/tmp/testrunner - - git checkout 62c3d362651fc785952ec7bd3046cd9f0d108cf3 + - git checkout 2056240d53091ea69336d25ad57e3f4afc0c37d3 - name: oC10APIAcceptanceTestsOwncloudStorage image: registry.cern.ch/docker.io/owncloudci/php:7.4 @@ -1371,7 +1371,7 @@ steps: - git clone -b master --depth=1 https://github.com/owncloud/testing.git /drone/src/tmp/testing - git clone -b master --single-branch --no-tags https://github.com/owncloud/core.git /drone/src/tmp/testrunner - cd /drone/src/tmp/testrunner - - git checkout 62c3d362651fc785952ec7bd3046cd9f0d108cf3 + - git checkout 2056240d53091ea69336d25ad57e3f4afc0c37d3 - name: oC10APIAcceptanceTestsOwncloudStorage image: registry.cern.ch/docker.io/owncloudci/php:7.4 @@ -1448,7 +1448,7 @@ steps: - git clone -b master --depth=1 https://github.com/owncloud/testing.git /drone/src/tmp/testing - git clone -b master --single-branch --no-tags https://github.com/owncloud/core.git /drone/src/tmp/testrunner - cd /drone/src/tmp/testrunner - - git checkout 62c3d362651fc785952ec7bd3046cd9f0d108cf3 + - git checkout 2056240d53091ea69336d25ad57e3f4afc0c37d3 - name: oC10APIAcceptanceTestsOwncloudStorage image: registry.cern.ch/docker.io/owncloudci/php:7.4 diff --git a/tests/acceptance/expected-failures-on-OCIS-storage.md b/tests/acceptance/expected-failures-on-OCIS-storage.md index 99fbab3ca1..d5c23866a4 100644 --- a/tests/acceptance/expected-failures-on-OCIS-storage.md +++ b/tests/acceptance/expected-failures-on-OCIS-storage.md @@ -108,7 +108,6 @@ Basic file management like up and download, move, copy, properties, trash, versi - [apiVersions/fileVersions.feature:93](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiVersions/fileVersions.feature#L93) - [apiVersions/fileVersions.feature:288](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiVersions/fileVersions.feature#L288) - [apiVersions/fileVersions.feature:362](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiVersions/fileVersions.feature#L362) -- [apiVersions/fileVersions.feature:373](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiVersions/fileVersions.feature#L373) - [apiWebdavUpload1/uploadFileAsyncUsingNewChunking.feature:14](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiWebdavUpload1/uploadFileAsyncUsingNewChunking.feature#L14) - [apiWebdavUpload1/uploadFileAsyncUsingNewChunking.feature:31](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiWebdavUpload1/uploadFileAsyncUsingNewChunking.feature#L31) - [apiWebdavUpload1/uploadFileAsyncUsingNewChunking.feature:48](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiWebdavUpload1/uploadFileAsyncUsingNewChunking.feature#L48) @@ -165,6 +164,11 @@ Basic file management like up and download, move, copy, properties, trash, versi - [apiWebdavUpload2/uploadFileUsingNewChunking.feature:168](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiWebdavUpload2/uploadFileUsingNewChunking.feature#L168) - [apiWebdavUpload2/uploadFileUsingNewChunking.feature:169](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiWebdavUpload2/uploadFileUsingNewChunking.feature#L169) +#### [Version count is 1 more than on oC10](https://github.com/owncloud/ocis/issues/1633) +- [apiVersions/fileVersions.feature:373](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiVersions/fileVersions.feature#L373) +- [apiVersions/fileVersions.feature:408](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiVersions/fileVersions.feature#L408) +- [apiVersions/fileVersions.feature:419](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiVersions/fileVersions.feature#L419) + #### [PUT request with missing parent must return status code 409](https://github.com/owncloud/ocis/issues/824) - [apiWebdavUpload1/uploadFile.feature:112](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiWebdavUpload1/uploadFile.feature#L112) - [apiWebdavUpload1/uploadFile.feature:113](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiWebdavUpload1/uploadFile.feature#L113) @@ -2055,6 +2059,11 @@ _ocdav: api compatibility, return correct status code_ _ocdav: api compatibility, return correct status code_ - [apiAuthWebDav/webDavPUTAuth.feature:38](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiAuthWebDav/webDavPUTAuth.feature#L38) Scenario: send PUT requests to another user's webDav endpoints as normal user +#### [Using double slash in URL to access a folder gives 501 and other status codes](https://github.com/owncloud/ocis/issues/1667) +- [apiAuthWebDav/webDavSpecialURLs.feature:24](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiAuthWebDav/webDavSpecialURLs.feature#L24) +- [apiAuthWebDav/webDavSpecialURLs.feature:69](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiAuthWebDav/webDavSpecialURLs.feature#L69) +- [apiAuthWebDav/webDavSpecialURLs.feature:91](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiAuthWebDav/webDavSpecialURLs.feature#L91) + #### [Default capabilities for normal user not same as in oC-core](https://github.com/owncloud/ocis/issues/1285) #### [Difference in response content of status.php and default capabilities](https://github.com/owncloud/ocis/issues/1286) - [apiCapabilities/capabilitiesWithNormalUser.feature:11](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiCapabilities/capabilitiesWithNormalUser.feature#L11) Scenario: getting default capabilities with normal user diff --git a/tests/acceptance/expected-failures-on-OWNCLOUD-storage.md b/tests/acceptance/expected-failures-on-OWNCLOUD-storage.md index 1b46ef480f..23653bd494 100644 --- a/tests/acceptance/expected-failures-on-OWNCLOUD-storage.md +++ b/tests/acceptance/expected-failures-on-OWNCLOUD-storage.md @@ -182,6 +182,8 @@ The following scenarios fail on OWNCLOUD storage but not on OCIS storage: #### [Version count is 1 more than on oC10](https://github.com/owncloud/ocis/issues/1633) - [apiVersions/fileVersions.feature:373](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiVersions/fileVersions.feature#L373) +- [apiVersions/fileVersions.feature:408](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiVersions/fileVersions.feature#L408) +- [apiVersions/fileVersions.feature:419](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiVersions/fileVersions.feature#L419) #### [Version cannot be restored when file has been renamed](https://github.com/owncloud/ocis/issues/1633) - [apiVersions/fileVersions.feature:399](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiVersions/fileVersions.feature#L399) @@ -2183,6 +2185,11 @@ _ocdav: api compatibility, return correct status code_ _ocdav: api compatibility, return correct status code_ - [apiAuthWebDav/webDavPUTAuth.feature:38](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiAuthWebDav/webDavPUTAuth.feature#L38) Scenario: send PUT requests to another user's webDav endpoints as normal user +#### [Using double slash in URL to access a folder gives 501 and other status codes](https://github.com/owncloud/ocis/issues/1667) +- [apiAuthWebDav/webDavSpecialURLs.feature:24](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiAuthWebDav/webDavSpecialURLs.feature#L24) +- [apiAuthWebDav/webDavSpecialURLs.feature:69](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiAuthWebDav/webDavSpecialURLs.feature#L69) +- [apiAuthWebDav/webDavSpecialURLs.feature:91](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiAuthWebDav/webDavSpecialURLs.feature#L91) + #### [Default capabilities for normal user not same as in oC-core](https://github.com/owncloud/ocis/issues/1285) #### [Difference in response content of status.php and default capabilities](https://github.com/owncloud/ocis/issues/1286) - [apiCapabilities/capabilitiesWithNormalUser.feature:11](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiCapabilities/capabilitiesWithNormalUser.feature#L11) Scenario: getting default capabilities with normal user From 8a29177ea9d0c82b0ae6c31e18d89004a5616c4d Mon Sep 17 00:00:00 2001 From: Ishank Arora Date: Wed, 17 Feb 2021 18:55:21 +0100 Subject: [PATCH 02/12] [tests-only] Minor fixes: eosfs, grappa (#1481) --- pkg/cbox/group/rest/rest.go | 4 ++-- pkg/cbox/user/rest/rest.go | 4 ++-- pkg/group/manager/json/json.go | 4 +++- pkg/storage/utils/eosfs/eosfs.go | 2 +- pkg/user/manager/json/json.go | 4 +++- tests/oc-integration-tests/drone/gateway.toml | 1 + .../drone/ldap-users.toml | 21 +++++++++++++++++++ tests/oc-integration-tests/local/gateway.toml | 1 + .../local/ldap-users.toml | 21 +++++++++++++++++++ 9 files changed, 55 insertions(+), 7 deletions(-) diff --git a/pkg/cbox/group/rest/rest.go b/pkg/cbox/group/rest/rest.go index 7ff26e1857..5957768bfd 100644 --- a/pkg/cbox/group/rest/rest.go +++ b/pkg/cbox/group/rest/rest.go @@ -315,7 +315,6 @@ func (m *manager) parseAndCacheGroup(ctx context.Context, groupData map[string]i } func (m *manager) GetGroup(ctx context.Context, gid *grouppb.GroupId) (*grouppb.Group, error) { - g, err := m.fetchCachedGroupDetails(gid) if err != nil { groupData, err := m.getGroupByParam(ctx, "groupIdentifier", gid.OpaqueId) @@ -335,6 +334,7 @@ func (m *manager) GetGroup(ctx context.Context, gid *grouppb.GroupId) (*grouppb. } func (m *manager) GetGroupByClaim(ctx context.Context, claim, value string) (*grouppb.Group, error) { + value = url.QueryEscape(value) opaqueID, err := m.fetchCachedParam(claim, value) if err == nil { return m.GetGroup(ctx, &grouppb.GroupId{OpaqueId: opaqueID}) @@ -420,7 +420,7 @@ func (m *manager) FindGroups(ctx context.Context, query string) ([]*grouppb.Grou for _, f := range filters { url := fmt.Sprintf("%s/Group/?filter=%s:contains:%s&field=groupIdentifier&field=displayName&field=gid", - m.conf.APIBaseURL, f, query) + m.conf.APIBaseURL, f, url.QueryEscape(query)) err := m.findGroupsByFilter(ctx, url, groups) if err != nil { return nil, err diff --git a/pkg/cbox/user/rest/rest.go b/pkg/cbox/user/rest/rest.go index 31bbc8d60a..f40a7a0175 100644 --- a/pkg/cbox/user/rest/rest.go +++ b/pkg/cbox/user/rest/rest.go @@ -327,7 +327,6 @@ func (m *manager) parseAndCacheUser(ctx context.Context, userData map[string]int } func (m *manager) GetUser(ctx context.Context, uid *userpb.UserId) (*userpb.User, error) { - u, err := m.fetchCachedUserDetails(uid) if err != nil { userData, err := m.getUserByParam(ctx, "upn", uid.OpaqueId) @@ -347,6 +346,7 @@ func (m *manager) GetUser(ctx context.Context, uid *userpb.UserId) (*userpb.User } func (m *manager) GetUserByClaim(ctx context.Context, claim, value string) (*userpb.User, error) { + value = url.QueryEscape(value) opaqueID, err := m.fetchCachedParam(claim, value) if err == nil { return m.GetUser(ctx, &userpb.UserId{OpaqueId: opaqueID}) @@ -439,7 +439,7 @@ func (m *manager) FindUsers(ctx context.Context, query string) ([]*userpb.User, for _, f := range filters { url := fmt.Sprintf("%s/Identity/?filter=%s:contains:%s&field=id&field=upn&field=primaryAccountEmail&field=displayName&field=uid&field=gid&field=type", - m.conf.APIBaseURL, f, query) + m.conf.APIBaseURL, f, url.QueryEscape(query)) err := m.findUsersByFilter(ctx, url, users) if err != nil { return nil, err diff --git a/pkg/group/manager/json/json.go b/pkg/group/manager/json/json.go index 7b91a853a4..ad4a425dd6 100644 --- a/pkg/group/manager/json/json.go +++ b/pkg/group/manager/json/json.go @@ -130,7 +130,9 @@ func (m *manager) FindGroups(ctx context.Context, query string) ([]*grouppb.Grou } func groupContains(g *grouppb.Group, query string) bool { - return strings.Contains(g.GroupName, query) || strings.Contains(g.DisplayName, query) || strings.Contains(g.Mail, query) || strings.Contains(g.Id.OpaqueId, query) + query = strings.ToLower(query) + return strings.Contains(strings.ToLower(g.GroupName), query) || strings.Contains(strings.ToLower(g.DisplayName), query) || + strings.Contains(strings.ToLower(g.Mail), query) || strings.Contains(strings.ToLower(g.Id.OpaqueId), query) } func (m *manager) GetMembers(ctx context.Context, gid *grouppb.GroupId) ([]*userpb.UserId, error) { diff --git a/pkg/storage/utils/eosfs/eosfs.go b/pkg/storage/utils/eosfs/eosfs.go index f33ec4de6f..68f9a67786 100644 --- a/pkg/storage/utils/eosfs/eosfs.go +++ b/pkg/storage/utils/eosfs/eosfs.go @@ -767,7 +767,7 @@ func (fs *eosfs) GetQuota(ctx context.Context) (int, int, error) { return 0, 0, err } - qi, err := fs.c.GetQuota(ctx, u.Username, rootUID, rootGID, fs.conf.Namespace) + qi, err := fs.c.GetQuota(ctx, u.Username, rootUID, rootGID, fs.conf.QuotaNode) if err != nil { err := errors.Wrap(err, "eosfs: error getting quota") return 0, 0, err diff --git a/pkg/user/manager/json/json.go b/pkg/user/manager/json/json.go index 489553ea64..6a02e6c09e 100644 --- a/pkg/user/manager/json/json.go +++ b/pkg/user/manager/json/json.go @@ -124,7 +124,9 @@ func extractClaim(u *userpb.User, claim string) (string, error) { // TODO(jfd) search Opaque? compare sub? func userContains(u *userpb.User, query string) bool { - return strings.Contains(u.Username, query) || strings.Contains(u.DisplayName, query) || strings.Contains(u.Mail, query) || strings.Contains(u.Id.OpaqueId, query) + query = strings.ToLower(query) + return strings.Contains(strings.ToLower(u.Username), query) || strings.Contains(strings.ToLower(u.DisplayName), query) || + strings.Contains(strings.ToLower(u.Mail), query) || strings.Contains(strings.ToLower(u.Id.OpaqueId), query) } func (m *manager) FindUsers(ctx context.Context, query string) ([]*userpb.User, error) { diff --git a/tests/oc-integration-tests/drone/gateway.toml b/tests/oc-integration-tests/drone/gateway.toml index 8a0547a928..cd698955a6 100644 --- a/tests/oc-integration-tests/drone/gateway.toml +++ b/tests/oc-integration-tests/drone/gateway.toml @@ -19,6 +19,7 @@ storageregistrysvc = "localhost:19000" # user metadata preferencessvc = "localhost:18000" userprovidersvc = "localhost:18000" +groupprovidersvc = "localhost:18000" # an approvider lives on "localhost:18000" as well, see users.toml # sharing usershareprovidersvc = "localhost:17000" diff --git a/tests/oc-integration-tests/drone/ldap-users.toml b/tests/oc-integration-tests/drone/ldap-users.toml index 28e5e7ad91..d5e897db0b 100644 --- a/tests/oc-integration-tests/drone/ldap-users.toml +++ b/tests/oc-integration-tests/drone/ldap-users.toml @@ -44,3 +44,24 @@ uid="uid" displayName="displayName" dn="dn" cn="cn" + +[grpc.services.groupprovider] +driver = "ldap" + +[grpc.services.groupprovider.drivers.ldap] +hostname="ldap" +port=636 +base_dn="dc=owncloud,dc=com" +groupfilter="(&(objectclass=posixGroup)(|(gid={{.OpaqueId}})(cn={{.OpaqueId}})))" +findfilter="(&(objectclass=posixGroup)(|(cn={{query}}*)(displayname={{query}}*)(mail={{query}}*)))" +attributefilter="(&(objectclass=posixGroup)({{attr}}={{value}}))" +memberfilter="(&(objectclass=posixAccount)(cn={{.OpaqueId}}))" +bind_username="cn=admin,dc=owncloud,dc=com" +bind_password="admin" +idp="http://localhost:18000" + +[grpc.services.groupprovider.drivers.ldap.schema] +gid="gid" +displayName="displayName" +dn="dn" +cn="cn" diff --git a/tests/oc-integration-tests/local/gateway.toml b/tests/oc-integration-tests/local/gateway.toml index 8adea69f59..3805824a2b 100644 --- a/tests/oc-integration-tests/local/gateway.toml +++ b/tests/oc-integration-tests/local/gateway.toml @@ -19,6 +19,7 @@ storageregistrysvc = "localhost:19000" # user metadata preferencessvc = "localhost:18000" userprovidersvc = "localhost:18000" +groupprovidersvc = "localhost:18000" # an approvider lives on "localhost:18000" as well, see users.toml # sharing usershareprovidersvc = "localhost:17000" diff --git a/tests/oc-integration-tests/local/ldap-users.toml b/tests/oc-integration-tests/local/ldap-users.toml index 3bc0897276..afc638a989 100644 --- a/tests/oc-integration-tests/local/ldap-users.toml +++ b/tests/oc-integration-tests/local/ldap-users.toml @@ -44,3 +44,24 @@ uid="uid" displayName="displayName" dn="dn" cn="cn" + +[grpc.services.groupprovider] +driver = "ldap" + +[grpc.services.groupprovider.drivers.ldap] +hostname="localhost" +port=636 +base_dn="dc=owncloud,dc=com" +groupfilter="(&(objectclass=posixGroup)(|(gid={{.OpaqueId}})(cn={{.OpaqueId}})))" +findfilter="(&(objectclass=posixGroup)(|(cn={{query}}*)(displayname={{query}}*)(mail={{query}}*)))" +attributefilter="(&(objectclass=posixGroup)({{attr}}={{value}}))" +memberfilter="(&(objectclass=posixAccount)(cn={{.OpaqueId}}))" +bind_username="cn=admin,dc=owncloud,dc=com" +bind_password="admin" +idp="http://localhost:18000" + +[grpc.services.groupprovider.drivers.ldap.schema] +gid="gid" +displayName="displayName" +dn="dn" +cn="cn" From a954e7242e7a7068e6951601d3826d93b0e043c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Friedrich=20Dreyer?= Date: Thu, 18 Feb 2021 14:08:54 +0100 Subject: [PATCH 03/12] quota querying and tree accounting (#1405) --- changelog/unreleased/ocis-quota.md | 5 + .../grpc/services/gateway/storageprovider.go | 17 ++- .../storageprovider/storageprovider.go | 4 +- .../http/services/owncloud/ocdav/propfind.go | 104 ++++++++++---- .../owncloud/ocs/handlers/cloud/cloud.go | 5 +- .../ocs/handlers/cloud/users/users.go | 89 ++++++++++-- internal/http/services/owncloud/ocs/v1.go | 7 +- pkg/eosclient/eosbinary/eosbinary.go | 16 +-- pkg/eosclient/eosclient.go | 5 +- pkg/storage/fs/ocis/node.go | 128 ++++++++++++++++++ pkg/storage/fs/ocis/ocis.go | 60 +++++++- pkg/storage/fs/ocis/tree.go | 73 +++++++--- pkg/storage/fs/owncloud/owncloud.go | 2 +- pkg/storage/fs/s3/s3.go | 2 +- pkg/storage/fs/s3ng/s3ng.go | 2 +- pkg/storage/storage.go | 2 +- pkg/storage/utils/eosfs/eosfs.go | 2 +- pkg/storage/utils/localfs/localfs.go | 15 +- .../expected-failures-on-OCIS-storage.md | 11 +- .../expected-failures-on-OWNCLOUD-storage.md | 8 +- 20 files changed, 458 insertions(+), 99 deletions(-) create mode 100644 changelog/unreleased/ocis-quota.md diff --git a/changelog/unreleased/ocis-quota.md b/changelog/unreleased/ocis-quota.md new file mode 100644 index 0000000000..0d58ad70c8 --- /dev/null +++ b/changelog/unreleased/ocis-quota.md @@ -0,0 +1,5 @@ +Enhancement: quota querying and tree accounting + +The ocs api now returns the user quota for the users home storage. Furthermore, the ocis storage driver now reads the quota from the extended attributes of the user home or root node and implements tree size accounting. Finally, ocdav PROPFINDS now handle the `DAV:quota-used-bytes` and `DAV:quote-available-bytes` properties. + +https://github.com/cs3org/reva/pull/1405 \ No newline at end of file diff --git a/internal/grpc/services/gateway/storageprovider.go b/internal/grpc/services/gateway/storageprovider.go index bb52f48c6b..dc565ecfed 100644 --- a/internal/grpc/services/gateway/storageprovider.go +++ b/internal/grpc/services/gateway/storageprovider.go @@ -1762,9 +1762,20 @@ func (s *svc) PurgeRecycle(ctx context.Context, req *gateway.PurgeRecycleRequest return res, nil } -func (s *svc) GetQuota(ctx context.Context, _ *gateway.GetQuotaRequest) (*provider.GetQuotaResponse, error) { - res := &provider.GetQuotaResponse{ - Status: status.NewUnimplemented(ctx, nil, "GetQuota not yet implemented"), +func (s *svc) GetQuota(ctx context.Context, req *gateway.GetQuotaRequest) (*provider.GetQuotaResponse, error) { + c, err := s.find(ctx, req.Ref) + if err != nil { + return &provider.GetQuotaResponse{ + Status: status.NewStatusFromErrType(ctx, "GetQuota ref="+req.Ref.String(), err), + }, nil + } + + res, err := c.GetQuota(ctx, &provider.GetQuotaRequest{ + Opaque: req.GetOpaque(), + //Ref: req.GetRef(), // TODO send which storage space ... or root + }) + if err != nil { + return nil, errors.Wrap(err, "gateway: error calling GetQuota") } return res, nil } diff --git a/internal/grpc/services/storageprovider/storageprovider.go b/internal/grpc/services/storageprovider/storageprovider.go index ae950d49c4..4e64793047 100644 --- a/internal/grpc/services/storageprovider/storageprovider.go +++ b/internal/grpc/services/storageprovider/storageprovider.go @@ -1062,8 +1062,8 @@ func (s *service) GetQuota(ctx context.Context, req *provider.GetQuotaRequest) ( res := &provider.GetQuotaResponse{ Status: status.NewOK(ctx), - TotalBytes: uint64(total), - UsedBytes: uint64(used), + TotalBytes: total, + UsedBytes: used, } return res, nil } diff --git a/internal/http/services/owncloud/ocdav/propfind.go b/internal/http/services/owncloud/ocdav/propfind.go index cfc9f3293d..98b815c7a2 100644 --- a/internal/http/services/owncloud/ocdav/propfind.go +++ b/internal/http/services/owncloud/ocdav/propfind.go @@ -52,6 +52,10 @@ const ( // RFC1123 time that mimics oc10. time.RFC1123 would end in "UTC", see https://github.com/golang/go/issues/13781 RFC1123 = "Mon, 02 Jan 2006 15:04:05 GMT" + + //_propQuotaUncalculated = "-1" + _propQuotaUnknown = "-2" + //_propQuotaUnlimited = "-3" ) // ns is the namespace that is prefixed to the path in the cs3 namespace @@ -89,22 +93,6 @@ func (s *svc) handlePropfind(w http.ResponseWriter, r *http.Request, ns string) return } - ref := &provider.Reference{ - Spec: &provider.Reference_Path{Path: fn}, - } - req := &provider.StatRequest{Ref: ref} - res, err := client.Stat(ctx, req) - if err != nil { - sublog.Error().Err(err).Msgf("error sending a grpc stat request to ref: %v", ref) - w.WriteHeader(http.StatusInternalServerError) - return - } - - if res.Status.Code != rpc.Code_CODE_OK { - HandleErrorStatus(&sublog, w, res.Status) - return - } - metadataKeys := []string{} if pf.Allprop != nil { // TODO this changes the behavior and returns all properties if allprops has been set, @@ -120,6 +108,24 @@ func (s *svc) handlePropfind(w http.ResponseWriter, r *http.Request, ns string) } } } + ref := &provider.Reference{ + Spec: &provider.Reference_Path{Path: fn}, + } + req := &provider.StatRequest{ + Ref: ref, + ArbitraryMetadataKeys: metadataKeys, + } + res, err := client.Stat(ctx, req) + if err != nil { + sublog.Error().Err(err).Interface("req", req).Msg("error sending a grpc stat request") + w.WriteHeader(http.StatusInternalServerError) + return + } + + if res.Status.Code != rpc.Code_CODE_OK { + HandleErrorStatus(&sublog, w, res.Status) + return + } info := res.Info infos := []*provider.ResourceInfo{info} @@ -217,10 +223,17 @@ func (s *svc) handlePropfind(w http.ResponseWriter, r *http.Request, ns string) func requiresExplicitFetching(n *xml.Name) bool { switch n.Space { case _nsDav: - return false + switch n.Local { + case "quota-available-bytes", "quota-used-bytes": + // A PROPFIND request SHOULD NOT return DAV:quota-available-bytes and DAV:quota-used-bytes + // from https://www.rfc-editor.org/rfc/rfc4331.html#section-2 + return true + default: + return false + } case _nsOwncloud: switch n.Local { - case "favorite", "share-types", "checksums": + case "favorite", "share-types", "checksums", "size": return true default: return false @@ -334,11 +347,24 @@ func (s *svc) mdToPropResponse(ctx context.Context, pf *propfindXML, md *provide } var ls *link.PublicShare - if md.Opaque != nil && md.Opaque.Map != nil && md.Opaque.Map["link-share"] != nil && md.Opaque.Map["link-share"].Decoder == "json" { - ls = &link.PublicShare{} - err := json.Unmarshal(md.Opaque.Map["link-share"].Value, ls) - if err != nil { - sublog.Error().Err(err).Msg("could not unmarshal link json") + + // -1 indicates uncalculated + // -2 indicates unknown (default) + // -3 indicates unlimited + quota := _propQuotaUnknown + size := fmt.Sprintf("%d", md.Size) + // TODO refactor helper functions: GetOpaqueJSONEncoded(opaque, key string, *struct) err, GetOpaquePlainEncoded(opaque, key) value, err + // or use ok like pattern and return bool? + if md.Opaque != nil && md.Opaque.Map != nil { + if md.Opaque.Map["link-share"] != nil && md.Opaque.Map["link-share"].Decoder == "json" { + ls = &link.PublicShare{} + err := json.Unmarshal(md.Opaque.Map["link-share"].Value, ls) + if err != nil { + sublog.Error().Err(err).Msg("could not unmarshal link json") + } + } + if md.Opaque.Map["quota"] != nil && md.Opaque.Map["quota"].Decoder == "plain" { + quota = string(md.Opaque.Map["quota"].Value) } } @@ -389,12 +415,15 @@ func (s *svc) mdToPropResponse(ctx context.Context, pf *propfindXML, md *provide } // always return size, well nearly always ... public link shares are a little weird - size := fmt.Sprintf("%d", md.Size) if md.Type == provider.ResourceType_RESOURCE_TYPE_CONTAINER { propstatOK.Prop = append(propstatOK.Prop, s.newPropRaw("d:resourcetype", "")) if ls == nil { propstatOK.Prop = append(propstatOK.Prop, s.newProp("oc:size", size)) } + // A PROPFIND request SHOULD NOT return DAV:quota-available-bytes and DAV:quota-used-bytes + // from https://www.rfc-editor.org/rfc/rfc4331.html#section-2 + //propstatOK.Prop = append(propstatOK.Prop, s.newProp("d:quota-used-bytes", size)) + //propstatOK.Prop = append(propstatOK.Prop, s.newProp("d:quota-available-bytes", quota)) } else { propstatOK.Prop = append(propstatOK.Prop, s.newProp("d:resourcetype", ""), @@ -458,7 +487,6 @@ func (s *svc) mdToPropResponse(ctx context.Context, pf *propfindXML, md *provide // TODO return other properties ... but how do we put them in a namespace? } else { // otherwise return only the requested properties - size := fmt.Sprintf("%d", md.Size) for i := range pf.Prop { switch pf.Prop[i].Space { case _nsOwncloud: @@ -539,7 +567,8 @@ func (s *svc) mdToPropResponse(ctx context.Context, pf *propfindXML, md *provide propstatNotFound.Prop = append(propstatNotFound.Prop, s.newProp("oc:public-link-expiration", "")) case "size": // phoenix only // TODO we cannot find out if md.Size is set or not because ints in go default to 0 - // oc:size is also available on folders + // TODO what is the difference to d:quota-used-bytes (which only exists for collections)? + // oc:size is available on files and folders and behaves like d:getcontentlength or d:quota-used-bytes respectively if ls == nil { propstatOK.Prop = append(propstatOK.Prop, s.newProp("oc:size", size)) } else { @@ -690,6 +719,22 @@ func (s *svc) mdToPropResponse(ctx context.Context, pf *propfindXML, md *provide } else { propstatNotFound.Prop = append(propstatNotFound.Prop, s.newProp("d:getlastmodified", "")) } + case "quota-used-bytes": // RFC 4331 + if md.Type == provider.ResourceType_RESOURCE_TYPE_CONTAINER { + // always returns the current usage, + // in oc10 there seems to be a bug that makes the size in webdav differ from the one in the user properties, not taking shares into account + // in ocis we plan to always mak the quota a property of the storage space + propstatOK.Prop = append(propstatOK.Prop, s.newProp("d:quota-used-bytes", size)) + } else { + propstatNotFound.Prop = append(propstatNotFound.Prop, s.newProp("d:quota-used-bytes", "")) + } + case "quota-available-bytes": // RFC 4331 + if md.Type == provider.ResourceType_RESOURCE_TYPE_CONTAINER { + // oc10 returns -3 for unlimited, -2 for unknown, -1 for uncalculated + propstatOK.Prop = append(propstatOK.Prop, s.newProp("d:quota-available-bytes", quota)) + } else { + propstatNotFound.Prop = append(propstatNotFound.Prop, s.newProp("d:quota-available-bytes", "")) + } default: propstatNotFound.Prop = append(propstatNotFound.Prop, s.newProp("d:"+pf.Prop[i].Local, "")) } @@ -763,7 +808,12 @@ func (c *countingReader) Read(p []byte) (int, error) { } func metadataKeyOf(n *xml.Name) string { - return fmt.Sprintf("%s/%s", n.Space, n.Local) + switch { + case n.Space == _nsDav && n.Local == "quota-available-bytes": + return "quota" + default: + return fmt.Sprintf("%s/%s", n.Space, n.Local) + } } // http://www.webdav.org/specs/rfc4918.html#ELEMENT_prop (for propfind) diff --git a/internal/http/services/owncloud/ocs/handlers/cloud/cloud.go b/internal/http/services/owncloud/ocs/handlers/cloud/cloud.go index bb2364f1e3..8b099b7b0c 100644 --- a/internal/http/services/owncloud/ocs/handlers/cloud/cloud.go +++ b/internal/http/services/owncloud/ocs/handlers/cloud/cloud.go @@ -37,11 +37,12 @@ type Handler struct { } // Init initializes this and any contained handlers -func (h *Handler) Init(c *config.Config) { +func (h *Handler) Init(c *config.Config) error { h.UserHandler = new(user.Handler) - h.UsersHandler = new(users.Handler) h.CapabilitiesHandler = new(capabilities.Handler) h.CapabilitiesHandler.Init(c) + h.UsersHandler = new(users.Handler) + return h.UsersHandler.Init(c) } // Handler routes the cloud endpoints diff --git a/internal/http/services/owncloud/ocs/handlers/cloud/users/users.go b/internal/http/services/owncloud/ocs/handlers/cloud/users/users.go index fcf4b638c7..9922110415 100644 --- a/internal/http/services/owncloud/ocs/handlers/cloud/users/users.go +++ b/internal/http/services/owncloud/ocs/handlers/cloud/users/users.go @@ -22,13 +22,28 @@ import ( "fmt" "net/http" + gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1" + userpb "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1" + rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1" + provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" + "github.com/cs3org/reva/internal/http/services/owncloud/ocdav" + "github.com/cs3org/reva/internal/http/services/owncloud/ocs/config" "github.com/cs3org/reva/internal/http/services/owncloud/ocs/response" + "github.com/cs3org/reva/pkg/appctx" + "github.com/cs3org/reva/pkg/rgrpc/todo/pool" "github.com/cs3org/reva/pkg/rhttp/router" ctxuser "github.com/cs3org/reva/pkg/user" ) -// The UsersHandler renders user data for the user id given in the url path +// Handler renders user data for the user id given in the url path type Handler struct { + gatewayAddr string +} + +// Init initializes this and any contained handlers +func (h *Handler) Init(c *config.Config) error { + h.gatewayAddr = c.GatewaySvc + return nil } func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { @@ -44,7 +59,7 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { return } if user != u.Username { - // FIXME allow fetching other users info? + // FIXME allow fetching other users info? only for admins response.WriteOCSError(w, r, http.StatusForbidden, "user id mismatch", fmt.Errorf("%s tried to access %s user info endpoint", u.Id.OpaqueId, user)) return } @@ -53,19 +68,7 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { head, r.URL.Path = router.ShiftPath(r.URL.Path) switch head { case "": - response.WriteOCSSuccess(w, r, &Users{ - // FIXME query storages? cache a summary? - // TODO use list of storages to allow clients to resolve quota status - Quota: &Quota{ - Free: 2840756224000, - Used: 5059416668, - Total: 2845815640668, - Relative: 0.18, - Definition: "default", - }, - DisplayName: u.DisplayName, - Email: u.Mail, - }) + h.handleUsers(w, r, u) return case "groups": response.WriteOCSSuccess(w, r, &Groups{}) @@ -100,3 +103,59 @@ type Users struct { type Groups struct { Groups []string `json:"groups" xml:"groups>element"` } + +func (h *Handler) handleUsers(w http.ResponseWriter, r *http.Request, u *userpb.User) { + ctx := r.Context() + sublog := appctx.GetLogger(r.Context()) + + gc, err := pool.GetGatewayServiceClient(h.gatewayAddr) + if err != nil { + sublog.Error().Err(err).Msg("error getting gateway client") + w.WriteHeader(http.StatusInternalServerError) + return + } + + getHomeRes, err := gc.GetHome(ctx, &provider.GetHomeRequest{}) + if err != nil { + sublog.Error().Err(err).Msg("error calling GetHome") + w.WriteHeader(http.StatusInternalServerError) + return + } + if getHomeRes.Status.Code != rpc.Code_CODE_OK { + ocdav.HandleErrorStatus(sublog, w, getHomeRes.Status) + return + } + + getQuotaRes, err := gc.GetQuota(ctx, &gateway.GetQuotaRequest{ + Ref: &provider.Reference{ + Spec: &provider.Reference_Path{ + Path: getHomeRes.Path, + }, + }, + }) + if err != nil { + sublog.Error().Err(err).Msg("error calling GetQuota") + w.WriteHeader(http.StatusInternalServerError) + return + } + + if getQuotaRes.Status.Code != rpc.Code_CODE_OK { + ocdav.HandleErrorStatus(sublog, w, getQuotaRes.Status) + return + } + + response.WriteOCSSuccess(w, r, &Users{ + // ocs can only return the home storage quota + Quota: &Quota{ + Free: int64(getQuotaRes.TotalBytes - getQuotaRes.UsedBytes), + Used: int64(getQuotaRes.UsedBytes), + // TODO support negative values or flags for the quota to carry special meaning: -1 = uncalculated, -2 = unknown, -3 = unlimited + // for now we can only report total and used + Total: int64(getQuotaRes.TotalBytes), + Relative: float32(float64(getQuotaRes.UsedBytes) / float64(getQuotaRes.TotalBytes)), + Definition: "default", + }, + DisplayName: u.DisplayName, + Email: u.Mail, + }) +} diff --git a/internal/http/services/owncloud/ocs/v1.go b/internal/http/services/owncloud/ocs/v1.go index 635ab74de1..f22f4d5392 100644 --- a/internal/http/services/owncloud/ocs/v1.go +++ b/internal/http/services/owncloud/ocs/v1.go @@ -37,15 +37,14 @@ type V1Handler struct { } func (h *V1Handler) init(c *config.Config) error { + h.ConfigHandler = new(configHandler.Handler) + h.ConfigHandler.Init(c) h.AppsHandler = new(apps.Handler) if err := h.AppsHandler.Init(c); err != nil { return err } h.CloudHandler = new(cloud.Handler) - h.CloudHandler.Init(c) - h.ConfigHandler = new(configHandler.Handler) - h.ConfigHandler.Init(c) - return nil + return h.CloudHandler.Init(c) } // Handler handles requests diff --git a/pkg/eosclient/eosbinary/eosbinary.go b/pkg/eosclient/eosbinary/eosbinary.go index b49ce42606..17679a21f9 100644 --- a/pkg/eosclient/eosbinary/eosbinary.go +++ b/pkg/eosclient/eosbinary/eosbinary.go @@ -794,19 +794,19 @@ func (c *Client) parseQuota(path, raw string) (*eosclient.QuotaInfo, error) { if strings.HasPrefix(path, space) { maxBytesString := m["maxlogicalbytes"] usedBytesString := m["usedlogicalbytes"] - maxBytes, _ := strconv.ParseInt(maxBytesString, 10, 64) - usedBytes, _ := strconv.ParseInt(usedBytesString, 10, 64) + maxBytes, _ := strconv.ParseUint(maxBytesString, 10, 64) + usedBytes, _ := strconv.ParseUint(usedBytesString, 10, 64) maxInodesString := m["maxfiles"] usedInodesString := m["usedfiles"] - maxInodes, _ := strconv.ParseInt(maxInodesString, 10, 64) - usedInodes, _ := strconv.ParseInt(usedInodesString, 10, 64) + maxInodes, _ := strconv.ParseUint(maxInodesString, 10, 64) + usedInodes, _ := strconv.ParseUint(usedInodesString, 10, 64) qi := &eosclient.QuotaInfo{ - AvailableBytes: int(maxBytes), - UsedBytes: int(usedBytes), - AvailableInodes: int(maxInodes), - UsedInodes: int(usedInodes), + AvailableBytes: maxBytes, + UsedBytes: usedBytes, + AvailableInodes: maxInodes, + UsedInodes: usedInodes, } return qi, nil } diff --git a/pkg/eosclient/eosclient.go b/pkg/eosclient/eosclient.go index 502630260d..1425ed5ac7 100644 --- a/pkg/eosclient/eosclient.go +++ b/pkg/eosclient/eosclient.go @@ -96,9 +96,10 @@ type DeletedEntry struct { } // QuotaInfo reports the available bytes and inodes for a particular user. +// eos reports all quota values are unsigned long, see https://github.com/cern-eos/eos/blob/93515df8c0d5a858982853d960bec98f983c1285/mgm/Quota.hh#L135 type QuotaInfo struct { - AvailableBytes, UsedBytes int - AvailableInodes, UsedInodes int + AvailableBytes, UsedBytes uint64 + AvailableInodes, UsedInodes uint64 } // SetQuotaInfo encapsulates the information needed to diff --git a/pkg/storage/fs/ocis/node.go b/pkg/storage/fs/ocis/node.go index f79a07e0ed..6e7d2854bd 100644 --- a/pkg/storage/fs/ocis/node.go +++ b/pkg/storage/fs/ocis/node.go @@ -27,6 +27,7 @@ import ( "io" "os" "path/filepath" + "strconv" "strings" "time" @@ -50,6 +51,11 @@ const ( _favoriteKey = "http://owncloud.org/ns/favorite" _checksumsKey = "http://owncloud.org/ns/checksums" + _quotaKey = "quota" + + _quotaUncalculated = "-1" + _quotaUnknown = "-2" + _quotaUnlimited = "-3" ) // Node represents a node in the tree and provides methods to get a Parent or Child instance @@ -470,6 +476,15 @@ func (n *Node) AsResourceInfo(ctx context.Context, rp *provider.ResourcePermissi Target: string(target), PermissionSet: rp, } + if nodeType == provider.ResourceType_RESOURCE_TYPE_CONTAINER { + ts, err := n.GetTreeSize() + if err == nil { + ri.Size = ts + } else { + ri.Size = 0 // make dirs always return 0 if it is unknown + sublog.Debug().Err(err).Msg("could not read treesize") + } + } if ri.Owner, err = n.Owner(); err != nil { sublog.Debug().Err(err).Msg("could not determine owner") @@ -547,6 +562,26 @@ func (n *Node) AsResourceInfo(ctx context.Context, rp *provider.ResourcePermissi readChecksumIntoOpaque(ctx, nodePath, storageprovider.XSAdler32, ri) } + // quota + if _, ok := mdKeysMap[_quotaKey]; (nodeType == provider.ResourceType_RESOURCE_TYPE_CONTAINER) && returnAllKeys || ok { + var quotaPath string + if n.lu.Options.EnableHome { + if r, err := n.lu.HomeNode(ctx); err == nil { + quotaPath = n.lu.toInternalPath(r.ID) + readQuotaIntoOpaque(ctx, quotaPath, ri) + } else { + sublog.Error().Err(err).Msg("error determining home node for quota") + } + } else { + if r, err := n.lu.RootNode(ctx); err == nil { + quotaPath = n.lu.toInternalPath(r.ID) + readQuotaIntoOpaque(ctx, quotaPath, ri) + } else { + sublog.Error().Err(err).Msg("error determining root node for quota") + } + } + } + // only read the requested metadata attributes attrs, err := xattr.List(nodePath) if err != nil { @@ -620,6 +655,85 @@ func readChecksumIntoOpaque(ctx context.Context, nodePath, algo string, ri *prov } } +// quota is always stored on the root node +func readQuotaIntoOpaque(ctx context.Context, nodePath string, ri *provider.ResourceInfo) { + v, err := xattr.Get(nodePath, quotaAttr) + switch { + case err == nil: + // make sure we have a proper signed int + // we use the same magic numbers to indicate: + // -1 = uncalculated + // -2 = unknown + // -3 = unlimited + if _, err := strconv.ParseInt(string(v), 10, 64); err == nil { + if ri.Opaque == nil { + ri.Opaque = &types.Opaque{ + Map: map[string]*types.OpaqueEntry{}, + } + } + ri.Opaque.Map[_quotaKey] = &types.OpaqueEntry{ + Decoder: "plain", + Value: v, + } + } else { + appctx.GetLogger(ctx).Error().Err(err).Str("nodepath", nodePath).Str("quota", string(v)).Msg("malformed quota") + } + case isNoData(err): + appctx.GetLogger(ctx).Debug().Err(err).Str("nodepath", nodePath).Msg("quota not set") + case isNotFound(err): + appctx.GetLogger(ctx).Error().Err(err).Str("nodepath", nodePath).Msg("file not found when reading quota") + default: + appctx.GetLogger(ctx).Error().Err(err).Str("nodepath", nodePath).Msg("could not read quota") + } +} + +// CalculateTreeSize will sum up the size of all children of a node +func (n *Node) CalculateTreeSize(ctx context.Context) (uint64, error) { + var size uint64 + // TODO check if this is a dir? + nodePath := n.lu.toInternalPath(n.ID) + + f, err := os.Open(nodePath) + if err != nil { + appctx.GetLogger(ctx).Error().Err(err).Str("nodepath", nodePath).Msg("could not open dir") + return 0, err + } + defer f.Close() + + names, err := f.Readdirnames(0) + if err != nil { + appctx.GetLogger(ctx).Error().Err(err).Str("nodepath", nodePath).Msg("could not read dirnames") + return 0, err + } + for i := range names { + cPath := filepath.Join(nodePath, names[i]) + info, err := os.Stat(cPath) + if err != nil { + appctx.GetLogger(ctx).Error().Err(err).Str("childpath", cPath).Msg("could not stat child entry") + continue // continue after an error + } + if !info.IsDir() { + size += uint64(info.Size()) + } else { + // read from attr + var b []byte + // xattr.Get will follow the symlink + if b, err = xattr.Get(cPath, treesizeAttr); err != nil { + // TODO recursively descend and recalculate treesize + continue // continue after an error + } + csize, err := strconv.ParseUint(string(b), 10, 64) + if err != nil { + // TODO recursively descend and recalculate treesize + continue // continue after an error + } + size += csize + } + } + return size, err + +} + // HasPropagation checks if the propagation attribute exists and is set to "1" func (n *Node) HasPropagation() (propagation bool) { if b, err := xattr.Get(n.lu.toInternalPath(n.ID), propagationAttr); err == nil { @@ -642,6 +756,20 @@ func (n *Node) SetTMTime(t time.Time) (err error) { return xattr.Set(n.lu.toInternalPath(n.ID), treeMTimeAttr, []byte(t.UTC().Format(time.RFC3339Nano))) } +// GetTreeSize reads the treesize from the extended attributes +func (n *Node) GetTreeSize() (treesize uint64, err error) { + var b []byte + if b, err = xattr.Get(n.lu.toInternalPath(n.ID), treesizeAttr); err != nil { + return + } + return strconv.ParseUint(string(b), 10, 64) +} + +// SetTreeSize writes the treesize to the extended attributes +func (n *Node) SetTreeSize(ts uint64) (err error) { + return xattr.Set(n.lu.toInternalPath(n.ID), treesizeAttr, []byte(strconv.FormatUint(ts, 10))) +} + // SetChecksum writes the checksum with the given checksum type to the extended attributes func (n *Node) SetChecksum(csType string, h hash.Hash) (err error) { return xattr.Set(n.lu.toInternalPath(n.ID), checksumPrefix+csType, h.Sum(nil)) diff --git a/pkg/storage/fs/ocis/ocis.go b/pkg/storage/fs/ocis/ocis.go index b3024b51ca..ff643cf94b 100644 --- a/pkg/storage/fs/ocis/ocis.go +++ b/pkg/storage/fs/ocis/ocis.go @@ -24,7 +24,9 @@ import ( "net/url" "os" "path/filepath" + "strconv" "strings" + "syscall" userpb "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1" userv1beta1 "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1" @@ -84,7 +86,11 @@ const ( // the size of the tree below this node, // propagated when treesize_accounting is true and // user.ocis.propagation=1 is set - //treesizeAttr string = ocisPrefix + "treesize" + // stored as uint64, little endian + treesizeAttr string = ocisPrefix + "treesize" + + // the quota for the storage space / tree, regardless who accesses it + quotaAttr string = ocisPrefix + "quota" ) func init() { @@ -183,8 +189,56 @@ func (fs *ocisfs) Shutdown(ctx context.Context) error { return nil } -func (fs *ocisfs) GetQuota(ctx context.Context) (int, int, error) { - return 0, 0, nil +// TODO Document in the cs3 should we return quota or free space? +func (fs *ocisfs) GetQuota(ctx context.Context) (uint64, uint64, error) { + var node *Node + var err error + if node, err = fs.lu.HomeOrRootNode(ctx); err != nil { + return 0, 0, err + } + + if !node.Exists { + err = errtypes.NotFound(filepath.Join(node.ParentID, node.Name)) + return 0, 0, err + } + + rp, err := fs.p.AssemblePermissions(ctx, node) + switch { + case err != nil: + return 0, 0, errtypes.InternalError(err.Error()) + case !rp.GetQuota: + return 0, 0, errtypes.PermissionDenied(node.ID) + } + + ri, err := node.AsResourceInfo(ctx, rp, []string{"treesize", "quota"}) + if err != nil { + return 0, 0, err + } + + quotaStr := _quotaUnknown + if ri.Opaque != nil && ri.Opaque.Map != nil && ri.Opaque.Map["quota"] != nil && ri.Opaque.Map["quota"].Decoder == "plain" { + quotaStr = string(ri.Opaque.Map["quota"].Value) + } + stat := syscall.Statfs_t{} + err = syscall.Statfs(fs.lu.toInternalPath(node.ID), &stat) + if err != nil { + return 0, 0, err + } + + total := ri.Size + (stat.Bavail * uint64(stat.Bsize)) // used treesize + available space + + switch { + case quotaStr == _quotaUncalculated, quotaStr == _quotaUnknown, quotaStr == _quotaUnlimited: + // best we can do is return current total + // TODO indicate unlimited total? -> in opaque data? + default: + if quota, err := strconv.ParseUint(quotaStr, 10, 64); err == nil { + if total > quota { + total = quota + } + } + } + return total, ri.Size, nil } // CreateHome creates a new root node that has no parent id diff --git a/pkg/storage/fs/ocis/tree.go b/pkg/storage/fs/ocis/tree.go index 742304dcba..0fed417286 100644 --- a/pkg/storage/fs/ocis/tree.go +++ b/pkg/storage/fs/ocis/tree.go @@ -31,7 +31,6 @@ import ( "github.com/google/uuid" "github.com/pkg/errors" "github.com/pkg/xattr" - "github.com/rs/zerolog/log" ) // Tree manages a hierarchical tree @@ -301,12 +300,12 @@ func (t *Tree) Delete(ctx context.Context, n *Node) (err error) { // Propagate propagates changes to the root of the tree func (t *Tree) Propagate(ctx context.Context, n *Node) (err error) { + sublog := appctx.GetLogger(ctx).With().Interface("node", n).Logger() if !t.lu.Options.TreeTimeAccounting && !t.lu.Options.TreeSizeAccounting { // no propagation enabled - log.Debug().Msg("propagation disabled") + sublog.Debug().Msg("propagation disabled") return } - log := appctx.GetLogger(ctx) // is propagation enabled for the parent node? @@ -318,16 +317,20 @@ func (t *Tree) Propagate(ctx context.Context, n *Node) (err error) { // use a sync time and don't rely on the mtime of the current node, as the stat might not change when a rename happened too quickly sTime := time.Now().UTC() + // we loop until we reach the root for err == nil && n.ID != root.ID { - log.Debug().Interface("node", n).Msg("propagating") + sublog.Debug().Msg("propagating") + // make n the parent or break the loop if n, err = n.Parent(); err != nil { break } + sublog = sublog.With().Interface("node", n).Logger() + // TODO none, sync and async? if !n.HasPropagation() { - log.Debug().Interface("node", n).Str("attr", propagationAttr).Msg("propagation attribute not set or unreadable, not propagating") + sublog.Debug().Str("attr", propagationAttr).Msg("propagation attribute not set or unreadable, not propagating") // if the attribute is not set treat it as false / none / no propagation return nil } @@ -341,20 +344,16 @@ func (t *Tree) Propagate(ctx context.Context, n *Node) (err error) { switch { case err != nil: // missing attribute, or invalid format, overwrite - log.Debug().Err(err). - Interface("node", n). - Msg("could not read tmtime attribute, overwriting") + sublog.Debug().Err(err).Msg("could not read tmtime attribute, overwriting") updateSyncTime = true case tmTime.Before(sTime): - log.Debug(). - Interface("node", n). + sublog.Debug(). Time("tmtime", tmTime). Time("stime", sTime). Msg("parent tmtime is older than node mtime, updating") updateSyncTime = true default: - log.Debug(). - Interface("node", n). + sublog.Debug(). Time("tmtime", tmTime). Time("stime", sTime). Dur("delta", sTime.Sub(tmTime)). @@ -364,24 +363,60 @@ func (t *Tree) Propagate(ctx context.Context, n *Node) (err error) { if updateSyncTime { // update the tree time of the parent node if err = n.SetTMTime(sTime); err != nil { - log.Error().Err(err).Interface("node", n).Time("tmtime", sTime).Msg("could not update tmtime of parent node") - return + sublog.Error().Err(err).Time("tmtime", sTime).Msg("could not update tmtime of parent node") + } else { + sublog.Debug().Time("tmtime", sTime).Msg("updated tmtime of parent node") } - log.Debug().Interface("node", n).Time("tmtime", sTime).Msg("updated tmtime of parent node") } if err := n.UnsetTempEtag(); err != nil { - log.Error().Err(err).Interface("node", n).Msg("could not remove temporary etag attribute") + sublog.Error().Err(err).Msg("could not remove temporary etag attribute") } } - // TODO size accounting + // size accounting + if t.lu.Options.TreeSizeAccounting { + // update the treesize if it differs from the current size + updateTreeSize := false + + var treeSize, calculatedTreeSize uint64 + calculatedTreeSize, err = n.CalculateTreeSize(ctx) + if err != nil { + continue + } + + treeSize, err = n.GetTreeSize() + switch { + case err != nil: + // missing attribute, or invalid format, overwrite + sublog.Debug().Err(err).Msg("could not read treesize attribute, overwriting") + updateTreeSize = true + case treeSize != calculatedTreeSize: + sublog.Debug(). + Uint64("treesize", treeSize). + Uint64("calculatedTreeSize", calculatedTreeSize). + Msg("parent treesize is different then calculated treesize, updating") + updateTreeSize = true + default: + sublog.Debug(). + Uint64("treesize", treeSize). + Uint64("calculatedTreeSize", calculatedTreeSize). + Msg("parent size matches calculated size, not updating") + } + if updateTreeSize { + // update the tree time of the parent node + if err = n.SetTreeSize(calculatedTreeSize); err != nil { + sublog.Error().Err(err).Uint64("calculatedTreeSize", calculatedTreeSize).Msg("could not update treesize of parent node") + } else { + sublog.Debug().Uint64("calculatedTreeSize", calculatedTreeSize).Msg("updated treesize of parent node") + } + } + } } if err != nil { - log.Error().Err(err).Interface("node", n).Msg("error propagating") - return + sublog.Error().Err(err).Msg("error propagating") } return } diff --git a/pkg/storage/fs/owncloud/owncloud.go b/pkg/storage/fs/owncloud/owncloud.go index 7625b0ca77..19efc2f8a5 100644 --- a/pkg/storage/fs/owncloud/owncloud.go +++ b/pkg/storage/fs/owncloud/owncloud.go @@ -1102,7 +1102,7 @@ func (fs *ocfs) UpdateGrant(ctx context.Context, ref *provider.Reference, g *pro return fs.propagate(ctx, ip) } -func (fs *ocfs) GetQuota(ctx context.Context) (int, int, error) { +func (fs *ocfs) GetQuota(ctx context.Context) (uint64, uint64, error) { return 0, 0, nil } diff --git a/pkg/storage/fs/s3/s3.go b/pkg/storage/fs/s3/s3.go index 356aba8447..7e4f101175 100644 --- a/pkg/storage/fs/s3/s3.go +++ b/pkg/storage/fs/s3/s3.go @@ -262,7 +262,7 @@ func (fs *s3FS) UpdateGrant(ctx context.Context, ref *provider.Reference, g *pro return errtypes.NotSupported("s3: operation not supported") } -func (fs *s3FS) GetQuota(ctx context.Context) (int, int, error) { +func (fs *s3FS) GetQuota(ctx context.Context) (uint64, uint64, error) { return 0, 0, nil } diff --git a/pkg/storage/fs/s3ng/s3ng.go b/pkg/storage/fs/s3ng/s3ng.go index e42bb197fa..108d6a6404 100644 --- a/pkg/storage/fs/s3ng/s3ng.go +++ b/pkg/storage/fs/s3ng/s3ng.go @@ -139,7 +139,7 @@ func (fs *s3ngfs) Shutdown(ctx context.Context) error { return nil } -func (fs *s3ngfs) GetQuota(ctx context.Context) (int, int, error) { +func (fs *s3ngfs) GetQuota(ctx context.Context) (uint64, uint64, error) { return 0, 0, nil } diff --git a/pkg/storage/storage.go b/pkg/storage/storage.go index 0ed8cf8f53..a986eb4b18 100644 --- a/pkg/storage/storage.go +++ b/pkg/storage/storage.go @@ -51,7 +51,7 @@ type FS interface { RemoveGrant(ctx context.Context, ref *provider.Reference, g *provider.Grant) error UpdateGrant(ctx context.Context, ref *provider.Reference, g *provider.Grant) error ListGrants(ctx context.Context, ref *provider.Reference) ([]*provider.Grant, error) - GetQuota(ctx context.Context) (int, int, error) + GetQuota(ctx context.Context) (uint64, uint64, error) CreateReference(ctx context.Context, path string, targetURI *url.URL) error Shutdown(ctx context.Context) error SetArbitraryMetadata(ctx context.Context, ref *provider.Reference, md *provider.ArbitraryMetadata) error diff --git a/pkg/storage/utils/eosfs/eosfs.go b/pkg/storage/utils/eosfs/eosfs.go index 68f9a67786..08f38bd17c 100644 --- a/pkg/storage/utils/eosfs/eosfs.go +++ b/pkg/storage/utils/eosfs/eosfs.go @@ -756,7 +756,7 @@ func (fs *eosfs) listShareFolderRoot(ctx context.Context, p string) (finfos []*p return finfos, nil } -func (fs *eosfs) GetQuota(ctx context.Context) (int, int, error) { +func (fs *eosfs) GetQuota(ctx context.Context) (uint64, uint64, error) { u, err := getUser(ctx) if err != nil { return 0, 0, errors.Wrap(err, "eos: no user in ctx") diff --git a/pkg/storage/utils/localfs/localfs.go b/pkg/storage/utils/localfs/localfs.go index 6ee2b6a0c6..3b61580788 100644 --- a/pkg/storage/utils/localfs/localfs.go +++ b/pkg/storage/utils/localfs/localfs.go @@ -29,6 +29,7 @@ import ( "path" "strconv" "strings" + "syscall" "time" grouppb "github.com/cs3org/go-cs3apis/cs3/identity/group/v1beta1" @@ -517,8 +518,18 @@ func (fs *localfs) UpdateGrant(ctx context.Context, ref *provider.Reference, g * return fs.AddGrant(ctx, ref, g) } -func (fs *localfs) GetQuota(ctx context.Context) (int, int, error) { - return 0, 0, nil +func (fs *localfs) GetQuota(ctx context.Context) (uint64, uint64, error) { + // TODO quota of which storage space? + // we could use the logged in user, but when a user has access to multiple storages this falls short + // for now return quota of root + stat := syscall.Statfs_t{} + err := syscall.Statfs(fs.conf.Root, &stat) + if err != nil { + return 0, 0, err + } + total := stat.Blocks * uint64(stat.Bsize) // Total data blocks in filesystem + used := (stat.Blocks - stat.Bavail) * uint64(stat.Bsize) // Free blocks available to unprivileged user + return total, used, nil } func (fs *localfs) CreateReference(ctx context.Context, path string, targetURI *url.URL) error { diff --git a/tests/acceptance/expected-failures-on-OCIS-storage.md b/tests/acceptance/expected-failures-on-OCIS-storage.md index d5c23866a4..b068aae637 100644 --- a/tests/acceptance/expected-failures-on-OCIS-storage.md +++ b/tests/acceptance/expected-failures-on-OCIS-storage.md @@ -1,7 +1,7 @@ ## Scenarios from ownCloud10 core API tests that are expected to fail with OCIS storage ### File -Basic file management like up and download, move, copy, properties, trash, versions and chunking. +Basic file management like up and download, move, copy, properties, quota, trash, versions and chunking. #### [Implement Trashbin Feature for ocis storage](https://github.com/owncloud/product/issues/209) @@ -326,9 +326,6 @@ Scenario Outline: try to create a folder with a name of an existing file ### [Different webdav properties from core](https://github.com/owncloud/ocis/issues/1302) - [apiWebdavProperties2/getFileProperties.feature:327](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiWebdavProperties2/getFileProperties.feature#L327) - [apiWebdavProperties2/getFileProperties.feature:328](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiWebdavProperties2/getFileProperties.feature#L328) -Scenario Outline: Propfind the size of a folder using webdav api `Property "oc:size" found with value "10", expected "#^0$#" or "#^0$#"` -- [apiWebdavProperties2/getFileProperties.feature:376](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiWebdavProperties2/getFileProperties.feature#L376) -- [apiWebdavProperties2/getFileProperties.feature:377](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiWebdavProperties2/getFileProperties.feature#L377) Scenario Outline: Propfind the permissions on a file using webdav api `Property "oc:permissions" found with value "DNVWR", expected "/RM{0,1}DNVW/"` - [apiWebdavProperties2/getFileProperties.feature:441](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiWebdavProperties2/getFileProperties.feature#L441) - [apiWebdavProperties2/getFileProperties.feature:442](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiWebdavProperties2/getFileProperties.feature#L442) @@ -1186,6 +1183,7 @@ File and sync features in a shared scenario - [apiSharePublicLink2/uploadToPublicLinkShare.feature:66](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiSharePublicLink2/uploadToPublicLinkShare.feature#L66) #### [Set quota over settings](https://github.com/owncloud/ocis/issues/1290) +_requires a [CS3 user provisioning api that can update the quota for a user](https://github.com/cs3org/cs3apis/pull/95#issuecomment-772780683)_ - [apiSharePublicLink2/uploadToPublicLinkShare.feature:148](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiSharePublicLink2/uploadToPublicLinkShare.feature#L148) - [apiSharePublicLink2/uploadToPublicLinkShare.feature:158](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiSharePublicLink2/uploadToPublicLinkShare.feature#L158) - [apiSharePublicLink2/uploadToPublicLinkShare.feature:167](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiSharePublicLink2/uploadToPublicLinkShare.feature#L167) @@ -1393,6 +1391,7 @@ Scenario Outline: delete a folder when there is a default folder for received sh - [apiWebdavProperties1/copyFile.feature:475](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiWebdavProperties1/copyFile.feature#L475) #### [quota query](https://github.com/owncloud/ocis/issues/1313) +_requires a [CS3 user provisioning api that can update the quota for a user](https://github.com/cs3org/cs3apis/pull/95#issuecomment-772780683)_ - [apiMain/quota.feature:41](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiMain/quota.feature#L41) Scenario: Uploading a file in received folder having enough quota - [apiMain/quota.feature:54](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiMain/quota.feature#L54) Scenario: Uploading a file in received folder having insufficient quota - [apiMain/quota.feature:68](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiMain/quota.feature#L68) Scenario: Overwriting a file in received folder having enough quota @@ -1425,6 +1424,7 @@ Scenario Outline: Retrieving folder quota when quota is set and a file was recei - [apiWebdavProperties2/getFileProperties.feature:233](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiWebdavProperties2/getFileProperties.feature#L233) #### [changing user quota gives ocs status 103 / Cannot set quota](https://github.com/owncloud/product/issues/247) +_requires a [CS3 user provisioning api that can update the quota for a user](https://github.com/cs3org/cs3apis/pull/95#issuecomment-772780683)_ - [apiShareOperationsToShares/uploadToShare.feature:162](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiShareOperationsToShares/uploadToShare.feature#L162) - [apiShareOperationsToShares/uploadToShare.feature:163](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiShareOperationsToShares/uploadToShare.feature#L163) - [apiShareOperationsToShares/uploadToShare.feature:181](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiShareOperationsToShares/uploadToShare.feature#L181) @@ -1837,7 +1837,7 @@ User and group management features - [apiProvisioning-v2/editUser.feature:47](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/editUser.feature#L47) #### [quota query](https://github.com/owncloud/ocis/issues/1313) -_getting and setting quota_ +_requires a [CS3 user provisioning api that can update the quota for a user](https://github.com/cs3org/cs3apis/pull/95#issuecomment-772780683)_ - [apiMain/quota.feature:9](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiMain/quota.feature#L9) Scenario: Uploading a file as owner having enough quota - [apiMain/quota.feature:16](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiMain/quota.feature#L16) Scenario: Uploading a file as owner having insufficient quota - [apiMain/quota.feature:23](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiMain/quota.feature#L23) Scenario: Overwriting a file as owner having enough quota @@ -1850,6 +1850,7 @@ Scenario Outline: Retrieving folder quota when quota is set - [apiWebdavProperties1/getQuota.feature:28](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiWebdavProperties1/getQuota.feature#L28) #### [changing user quota gives ocs status 103 / Cannot set quota](https://github.com/owncloud/product/issues/247) +_requires a [CS3 user provisioning api that can update the quota for a user](https://github.com/cs3org/cs3apis/pull/95#issuecomment-772780683)_ - [apiProvisioning-v1/editUser.feature:56](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v1/editUser.feature#L56) - [apiProvisioning-v1/editUser.feature:122](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v1/editUser.feature#L122) - [apiProvisioning-v2/editUser.feature:56](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/editUser.feature#L56) diff --git a/tests/acceptance/expected-failures-on-OWNCLOUD-storage.md b/tests/acceptance/expected-failures-on-OWNCLOUD-storage.md index 23653bd494..a29ec9188f 100644 --- a/tests/acceptance/expected-failures-on-OWNCLOUD-storage.md +++ b/tests/acceptance/expected-failures-on-OWNCLOUD-storage.md @@ -1,7 +1,7 @@ ## Scenarios from ownCloud10 core API tests that are expected to fail with owncloud storage ### File -Basic file management like up and download, move, copy, properties, trash, versions and chunking. +Basic file management like up and download, move, copy, properties, quota, trash, versions and chunking. #### [Implement Trashbin Feature for ocis storage](https://github.com/owncloud/product/issues/209) - [apiWebdavEtagPropagation2/restoreFromTrash.feature:48](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiWebdavEtagPropagation2/restoreFromTrash.feature#L48) @@ -1204,6 +1204,7 @@ The following scenarios fail on OWNCLOUD storage but not on OCIS storage: - [apiSharePublicLink2/uploadToPublicLinkShare.feature:66](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiSharePublicLink2/uploadToPublicLinkShare.feature#L66) #### [Set quota over settings](https://github.com/owncloud/ocis/issues/1290) +_requires a [CS3 user provisioning api that can update the quota for a user](https://github.com/cs3org/cs3apis/pull/95#issuecomment-772780683)_ - [apiSharePublicLink2/uploadToPublicLinkShare.feature:148](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiSharePublicLink2/uploadToPublicLinkShare.feature#L148) - [apiSharePublicLink2/uploadToPublicLinkShare.feature:158](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiSharePublicLink2/uploadToPublicLinkShare.feature#L158) - [apiSharePublicLink2/uploadToPublicLinkShare.feature:167](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiSharePublicLink2/uploadToPublicLinkShare.feature#L167) @@ -1505,6 +1506,7 @@ Scenario Outline: delete a folder when there is a default folder for received sh - [apiWebdavProperties1/copyFile.feature:475](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiWebdavProperties1/copyFile.feature#L475) #### [quota query](https://github.com/owncloud/ocis/issues/1313) +_requires a [CS3 user provisioning api that can update the quota for a user](https://github.com/cs3org/cs3apis/pull/95#issuecomment-772780683)_ - [apiMain/quota.feature:41](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiMain/quota.feature#L41) Scenario: Uploading a file in received folder having enough quota - [apiMain/quota.feature:54](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiMain/quota.feature#L54) Scenario: Uploading a file in received folder having insufficient quota - [apiMain/quota.feature:68](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiMain/quota.feature#L68) Scenario: Overwriting a file in received folder having enough quota @@ -1542,6 +1544,7 @@ The following scenarios fail on OWNCLOUD storage but not on OCIS storage: - [apiWebdavProperties2/getFileProperties.feature:233](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiWebdavProperties2/getFileProperties.feature#L233) #### [changing user quota gives ocs status 103 / Cannot set quota](https://github.com/owncloud/product/issues/247) +_requires a [CS3 user provisioning api that can update the quota for a user](https://github.com/cs3org/cs3apis/pull/95#issuecomment-772780683)_ - [apiShareOperationsToShares/uploadToShare.feature:162](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiShareOperationsToShares/uploadToShare.feature#L162) - [apiShareOperationsToShares/uploadToShare.feature:163](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiShareOperationsToShares/uploadToShare.feature#L163) - [apiShareOperationsToShares/uploadToShare.feature:181](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiShareOperationsToShares/uploadToShare.feature#L181) @@ -1963,7 +1966,7 @@ special character username not valid - [apiProvisioning-v2/editUser.feature:47](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/editUser.feature#L47) #### [quota query](https://github.com/owncloud/ocis/issues/1313) -_getting and setting quota_ +_requires a [CS3 user provisioning api that can update the quota for a user](https://github.com/cs3org/cs3apis/pull/95#issuecomment-772780683)_ - [apiMain/quota.feature:9](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiMain/quota.feature#L9) Scenario: Uploading a file as owner having enough quota - [apiMain/quota.feature:16](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiMain/quota.feature#L16) Scenario: Uploading a file as owner having insufficient quota - [apiMain/quota.feature:23](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiMain/quota.feature#L23) Scenario: Overwriting a file as owner having enough quota @@ -1976,6 +1979,7 @@ _getting and setting quota_ - [apiWebdavProperties1/getQuota.feature:28](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiWebdavProperties1/getQuota.feature#L28) #### [changing user quota gives ocs status 103 / Cannot set quota](https://github.com/owncloud/product/issues/247) +_requires a [CS3 user provisioning api that can update the quota for a user](https://github.com/cs3org/cs3apis/pull/95#issuecomment-772780683)_ - [apiProvisioning-v1/editUser.feature:56](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v1/editUser.feature#L56) - [apiProvisioning-v1/editUser.feature:122](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v1/editUser.feature#L122) - [apiProvisioning-v2/editUser.feature:56](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/editUser.feature#L56) From bfacca64c62886442c8011b24a8dc16061ccf1ec Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 22 Feb 2021 09:31:59 +0100 Subject: [PATCH 04/12] [Build-deps]: Bump github.com/gomodule/redigo from 1.8.3 to 1.8.4 (#1488) --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 4aeb2b9e7f..d8dc08b628 100644 --- a/go.mod +++ b/go.mod @@ -21,7 +21,7 @@ require ( github.com/go-openapi/strfmt v0.19.2 // indirect github.com/go-sql-driver/mysql v1.5.0 github.com/golang/protobuf v1.4.3 - github.com/gomodule/redigo v1.8.3 + github.com/gomodule/redigo v1.8.4 github.com/google/go-github v17.0.0+incompatible github.com/google/go-querystring v1.0.0 // indirect github.com/google/uuid v1.2.0 diff --git a/go.sum b/go.sum index b0ff931923..2ddf9ab822 100644 --- a/go.sum +++ b/go.sum @@ -463,8 +463,8 @@ github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/gomodule/redigo v1.8.3 h1:HR0kYDX2RJZvAup8CsiJwxB4dTCSC0AaUq6S4SiLwUc= -github.com/gomodule/redigo v1.8.3/go.mod h1:P9dn9mFrCBvWhGE1wpxx6fgq7BAeLBk+UUUzlpkBYO0= +github.com/gomodule/redigo v1.8.4 h1:Z5JUg94HMTR1XpwBaSH4vq3+PNSIykBLxMdglbw10gg= +github.com/gomodule/redigo v1.8.4/go.mod h1:P9dn9mFrCBvWhGE1wpxx6fgq7BAeLBk+UUUzlpkBYO0= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= From e861f8d76ccc87d4e47d2d2d1c2fc4c8381db185 Mon Sep 17 00:00:00 2001 From: Phil Davis Date: Mon, 22 Feb 2021 12:06:49 +0300 Subject: [PATCH 05/12] Bump core commit id for tests (#1489) --- .drone.yml | 28 +++++++++---------- .../expected-failures-on-OCIS-storage.md | 10 +++++-- .../expected-failures-on-OWNCLOUD-storage.md | 10 +++++-- 3 files changed, 30 insertions(+), 18 deletions(-) diff --git a/.drone.yml b/.drone.yml index 34b126a4bc..3b7f1bb1f1 100644 --- a/.drone.yml +++ b/.drone.yml @@ -488,7 +488,7 @@ steps: - git clone -b master --depth=1 https://github.com/owncloud/testing.git /drone/src/tmp/testing - git clone -b master --single-branch --no-tags https://github.com/owncloud/core.git /drone/src/tmp/testrunner - cd /drone/src/tmp/testrunner - - git checkout 2056240d53091ea69336d25ad57e3f4afc0c37d3 + - git checkout df5e095c7e5e94cb44f842a4b51a3480cf8878a0 - name: localAPIAcceptanceTestsOwncloudStorage image: registry.cern.ch/docker.io/owncloudci/php:7.4 @@ -562,7 +562,7 @@ steps: - git clone -b master --depth=1 https://github.com/owncloud/testing.git /drone/src/tmp/testing - git clone -b master --single-branch --no-tags https://github.com/owncloud/core.git /drone/src/tmp/testrunner - cd /drone/src/tmp/testrunner - - git checkout 2056240d53091ea69336d25ad57e3f4afc0c37d3 + - git checkout df5e095c7e5e94cb44f842a4b51a3480cf8878a0 - name: localAPIAcceptanceTestsOcisStorage image: registry.cern.ch/docker.io/owncloudci/php:7.4 @@ -631,7 +631,7 @@ steps: - git clone -b master --depth=1 https://github.com/owncloud/testing.git /drone/src/tmp/testing - git clone -b master --single-branch --no-tags https://github.com/owncloud/core.git /drone/src/tmp/testrunner - cd /drone/src/tmp/testrunner - - git checkout 2056240d53091ea69336d25ad57e3f4afc0c37d3 + - git checkout df5e095c7e5e94cb44f842a4b51a3480cf8878a0 - name: oC10APIAcceptanceTestsOcisStorage image: registry.cern.ch/docker.io/owncloudci/php:7.4 @@ -703,7 +703,7 @@ steps: - git clone -b master --depth=1 https://github.com/owncloud/testing.git /drone/src/tmp/testing - git clone -b master --single-branch --no-tags https://github.com/owncloud/core.git /drone/src/tmp/testrunner - cd /drone/src/tmp/testrunner - - git checkout 2056240d53091ea69336d25ad57e3f4afc0c37d3 + - git checkout df5e095c7e5e94cb44f842a4b51a3480cf8878a0 - name: oC10APIAcceptanceTestsOcisStorage image: registry.cern.ch/docker.io/owncloudci/php:7.4 @@ -775,7 +775,7 @@ steps: - git clone -b master --depth=1 https://github.com/owncloud/testing.git /drone/src/tmp/testing - git clone -b master --single-branch --no-tags https://github.com/owncloud/core.git /drone/src/tmp/testrunner - cd /drone/src/tmp/testrunner - - git checkout 2056240d53091ea69336d25ad57e3f4afc0c37d3 + - git checkout df5e095c7e5e94cb44f842a4b51a3480cf8878a0 - name: oC10APIAcceptanceTestsOcisStorage image: registry.cern.ch/docker.io/owncloudci/php:7.4 @@ -847,7 +847,7 @@ steps: - git clone -b master --depth=1 https://github.com/owncloud/testing.git /drone/src/tmp/testing - git clone -b master --single-branch --no-tags https://github.com/owncloud/core.git /drone/src/tmp/testrunner - cd /drone/src/tmp/testrunner - - git checkout 2056240d53091ea69336d25ad57e3f4afc0c37d3 + - git checkout df5e095c7e5e94cb44f842a4b51a3480cf8878a0 - name: oC10APIAcceptanceTestsOcisStorage image: registry.cern.ch/docker.io/owncloudci/php:7.4 @@ -919,7 +919,7 @@ steps: - git clone -b master --depth=1 https://github.com/owncloud/testing.git /drone/src/tmp/testing - git clone -b master --single-branch --no-tags https://github.com/owncloud/core.git /drone/src/tmp/testrunner - cd /drone/src/tmp/testrunner - - git checkout 2056240d53091ea69336d25ad57e3f4afc0c37d3 + - git checkout df5e095c7e5e94cb44f842a4b51a3480cf8878a0 - name: oC10APIAcceptanceTestsOcisStorage image: registry.cern.ch/docker.io/owncloudci/php:7.4 @@ -991,7 +991,7 @@ steps: - git clone -b master --depth=1 https://github.com/owncloud/testing.git /drone/src/tmp/testing - git clone -b master --single-branch --no-tags https://github.com/owncloud/core.git /drone/src/tmp/testrunner - cd /drone/src/tmp/testrunner - - git checkout 2056240d53091ea69336d25ad57e3f4afc0c37d3 + - git checkout df5e095c7e5e94cb44f842a4b51a3480cf8878a0 - name: oC10APIAcceptanceTestsOcisStorage image: registry.cern.ch/docker.io/owncloudci/php:7.4 @@ -1063,7 +1063,7 @@ steps: - git clone -b master --depth=1 https://github.com/owncloud/testing.git /drone/src/tmp/testing - git clone -b master --single-branch --no-tags https://github.com/owncloud/core.git /drone/src/tmp/testrunner - cd /drone/src/tmp/testrunner - - git checkout 2056240d53091ea69336d25ad57e3f4afc0c37d3 + - git checkout df5e095c7e5e94cb44f842a4b51a3480cf8878a0 - name: oC10APIAcceptanceTestsOwncloudStorage image: registry.cern.ch/docker.io/owncloudci/php:7.4 @@ -1140,7 +1140,7 @@ steps: - git clone -b master --depth=1 https://github.com/owncloud/testing.git /drone/src/tmp/testing - git clone -b master --single-branch --no-tags https://github.com/owncloud/core.git /drone/src/tmp/testrunner - cd /drone/src/tmp/testrunner - - git checkout 2056240d53091ea69336d25ad57e3f4afc0c37d3 + - git checkout df5e095c7e5e94cb44f842a4b51a3480cf8878a0 - name: oC10APIAcceptanceTestsOwncloudStorage image: registry.cern.ch/docker.io/owncloudci/php:7.4 @@ -1217,7 +1217,7 @@ steps: - git clone -b master --depth=1 https://github.com/owncloud/testing.git /drone/src/tmp/testing - git clone -b master --single-branch --no-tags https://github.com/owncloud/core.git /drone/src/tmp/testrunner - cd /drone/src/tmp/testrunner - - git checkout 2056240d53091ea69336d25ad57e3f4afc0c37d3 + - git checkout df5e095c7e5e94cb44f842a4b51a3480cf8878a0 - name: oC10APIAcceptanceTestsOwncloudStorage image: registry.cern.ch/docker.io/owncloudci/php:7.4 @@ -1294,7 +1294,7 @@ steps: - git clone -b master --depth=1 https://github.com/owncloud/testing.git /drone/src/tmp/testing - git clone -b master --single-branch --no-tags https://github.com/owncloud/core.git /drone/src/tmp/testrunner - cd /drone/src/tmp/testrunner - - git checkout 2056240d53091ea69336d25ad57e3f4afc0c37d3 + - git checkout df5e095c7e5e94cb44f842a4b51a3480cf8878a0 - name: oC10APIAcceptanceTestsOwncloudStorage image: registry.cern.ch/docker.io/owncloudci/php:7.4 @@ -1371,7 +1371,7 @@ steps: - git clone -b master --depth=1 https://github.com/owncloud/testing.git /drone/src/tmp/testing - git clone -b master --single-branch --no-tags https://github.com/owncloud/core.git /drone/src/tmp/testrunner - cd /drone/src/tmp/testrunner - - git checkout 2056240d53091ea69336d25ad57e3f4afc0c37d3 + - git checkout df5e095c7e5e94cb44f842a4b51a3480cf8878a0 - name: oC10APIAcceptanceTestsOwncloudStorage image: registry.cern.ch/docker.io/owncloudci/php:7.4 @@ -1448,7 +1448,7 @@ steps: - git clone -b master --depth=1 https://github.com/owncloud/testing.git /drone/src/tmp/testing - git clone -b master --single-branch --no-tags https://github.com/owncloud/core.git /drone/src/tmp/testrunner - cd /drone/src/tmp/testrunner - - git checkout 2056240d53091ea69336d25ad57e3f4afc0c37d3 + - git checkout df5e095c7e5e94cb44f842a4b51a3480cf8878a0 - name: oC10APIAcceptanceTestsOwncloudStorage image: registry.cern.ch/docker.io/owncloudci/php:7.4 diff --git a/tests/acceptance/expected-failures-on-OCIS-storage.md b/tests/acceptance/expected-failures-on-OCIS-storage.md index b068aae637..c826ed2691 100644 --- a/tests/acceptance/expected-failures-on-OCIS-storage.md +++ b/tests/acceptance/expected-failures-on-OCIS-storage.md @@ -2319,5 +2319,11 @@ Scenario Outline: Do a PROPFIND to a non-existing URL - [apiMain/carddav.feature:23](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiMain/carddav.feature#L23) - [apiMain/carddav.feature:31](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiMain/carddav.feature#L31) -- [apiTranslation/translation.feature:29](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiTranslation/translation.feature#L29) -- [apiTranslation/translation.feature:30](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiTranslation/translation.feature#L30) +- [apiTranslation/translation.feature:20](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiTranslation/translation.feature#L20) +- [apiTranslation/translation.feature:21](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiTranslation/translation.feature#L21) +- [apiTranslation/translation.feature:22](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiTranslation/translation.feature#L22) +- [apiTranslation/translation.feature:23](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiTranslation/translation.feature#L23) +- [apiTranslation/translation.feature:24](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiTranslation/translation.feature#L24) +- [apiTranslation/translation.feature:25](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiTranslation/translation.feature#L25) +- [apiTranslation/translation.feature:26](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiTranslation/translation.feature#L26) +- [apiTranslation/translation.feature:27](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiTranslation/translation.feature#L27) diff --git a/tests/acceptance/expected-failures-on-OWNCLOUD-storage.md b/tests/acceptance/expected-failures-on-OWNCLOUD-storage.md index a29ec9188f..3276ea8070 100644 --- a/tests/acceptance/expected-failures-on-OWNCLOUD-storage.md +++ b/tests/acceptance/expected-failures-on-OWNCLOUD-storage.md @@ -2453,5 +2453,11 @@ Scenario Outline: Do a PROPFIND to a non-existing URL - [apiMain/carddav.feature:23](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiMain/carddav.feature#L23) - [apiMain/carddav.feature:31](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiMain/carddav.feature#L31) -- [apiTranslation/translation.feature:29](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiTranslation/translation.feature#L29) -- [apiTranslation/translation.feature:30](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiTranslation/translation.feature#L30) +- [apiTranslation/translation.feature:20](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiTranslation/translation.feature#L20) +- [apiTranslation/translation.feature:21](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiTranslation/translation.feature#L21) +- [apiTranslation/translation.feature:22](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiTranslation/translation.feature#L22) +- [apiTranslation/translation.feature:23](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiTranslation/translation.feature#L23) +- [apiTranslation/translation.feature:24](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiTranslation/translation.feature#L24) +- [apiTranslation/translation.feature:25](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiTranslation/translation.feature#L25) +- [apiTranslation/translation.feature:26](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiTranslation/translation.feature#L26) +- [apiTranslation/translation.feature:27](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiTranslation/translation.feature#L27) From 53f39499762eacb89aa9e23a0c61e219d9a089a0 Mon Sep 17 00:00:00 2001 From: David Christofas Date: Tue, 23 Feb 2021 07:50:28 +0100 Subject: [PATCH 06/12] fix quota retrieval in windows systems (#1491) --- changelog/unreleased/ocis-quota.md | 3 +- go.mod | 1 + go.sum | 2 ++ pkg/storage/fs/ocis/ocis.go | 8 ++--- pkg/storage/fs/ocis/ocis_unix.go | 32 +++++++++++++++++ pkg/storage/fs/ocis/ocis_windows.go | 36 ++++++++++++++++++++ pkg/storage/utils/localfs/localfs.go | 15 -------- pkg/storage/utils/localfs/localfs_unix.go | 14 ++++++++ pkg/storage/utils/localfs/localfs_windows.go | 20 +++++++++++ 9 files changed, 110 insertions(+), 21 deletions(-) create mode 100644 pkg/storage/fs/ocis/ocis_unix.go create mode 100644 pkg/storage/fs/ocis/ocis_windows.go diff --git a/changelog/unreleased/ocis-quota.md b/changelog/unreleased/ocis-quota.md index 0d58ad70c8..5388c5c856 100644 --- a/changelog/unreleased/ocis-quota.md +++ b/changelog/unreleased/ocis-quota.md @@ -2,4 +2,5 @@ Enhancement: quota querying and tree accounting The ocs api now returns the user quota for the users home storage. Furthermore, the ocis storage driver now reads the quota from the extended attributes of the user home or root node and implements tree size accounting. Finally, ocdav PROPFINDS now handle the `DAV:quota-used-bytes` and `DAV:quote-available-bytes` properties. -https://github.com/cs3org/reva/pull/1405 \ No newline at end of file +https://github.com/cs3org/reva/pull/1405 +https://github.com/cs3org/reva/pull/1491 diff --git a/go.mod b/go.mod index d8dc08b628..cbbafd422e 100644 --- a/go.mod +++ b/go.mod @@ -47,6 +47,7 @@ require ( golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d + golang.org/x/sys v0.0.0-20210218155724-8ebf48af031b golang.org/x/term v0.0.0-20201117132131-f5c789dd3221 google.golang.org/grpc v1.35.0 ) diff --git a/go.sum b/go.sum index 2ddf9ab822..c94803a7bc 100644 --- a/go.sum +++ b/go.sum @@ -1194,6 +1194,8 @@ golang.org/x/sys v0.0.0-20201101102859-da207088b7d1 h1:a/mKvvZr9Jcc8oKfcmgzyp7Ow golang.org/x/sys v0.0.0-20201101102859-da207088b7d1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210112080510-489259a85091 h1:DMyOG0U+gKfu8JZzg2UQe9MeaC1X+xQWlAKcRnjxjCw= golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210218155724-8ebf48af031b h1:lAZ0/chPUDWwjqosYR0X4M490zQhMsiJ4K3DbA7o+3g= +golang.org/x/sys v0.0.0-20210218155724-8ebf48af031b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221 h1:/ZHdbVpdR/jk3g30/d4yUL0JU9kksj8+F/bnQUVLGDM= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= diff --git a/pkg/storage/fs/ocis/ocis.go b/pkg/storage/fs/ocis/ocis.go index ff643cf94b..fe51235e81 100644 --- a/pkg/storage/fs/ocis/ocis.go +++ b/pkg/storage/fs/ocis/ocis.go @@ -26,7 +26,6 @@ import ( "path/filepath" "strconv" "strings" - "syscall" userpb "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1" userv1beta1 "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1" @@ -219,13 +218,12 @@ func (fs *ocisfs) GetQuota(ctx context.Context) (uint64, uint64, error) { if ri.Opaque != nil && ri.Opaque.Map != nil && ri.Opaque.Map["quota"] != nil && ri.Opaque.Map["quota"].Decoder == "plain" { quotaStr = string(ri.Opaque.Map["quota"].Value) } - stat := syscall.Statfs_t{} - err = syscall.Statfs(fs.lu.toInternalPath(node.ID), &stat) + + avail, err := fs.getAvailableSize(fs.lu.toInternalPath(node.ID)) if err != nil { return 0, 0, err } - - total := ri.Size + (stat.Bavail * uint64(stat.Bsize)) // used treesize + available space + total := avail + ri.Size switch { case quotaStr == _quotaUncalculated, quotaStr == _quotaUnknown, quotaStr == _quotaUnlimited: diff --git a/pkg/storage/fs/ocis/ocis_unix.go b/pkg/storage/fs/ocis/ocis_unix.go new file mode 100644 index 0000000000..d291514dbb --- /dev/null +++ b/pkg/storage/fs/ocis/ocis_unix.go @@ -0,0 +1,32 @@ +// Copyright 2018-2021 CERN +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// In applying this license, CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +// +build !windows + +package ocis + +import "syscall" + +func (fs *ocisfs) getAvailableSize(path string) (uint64, error) { + stat := syscall.Statfs_t{} + err := syscall.Statfs(path, &stat) + if err != nil { + return 0, err + } + return stat.Bavail * uint64(stat.Bsize), nil +} diff --git a/pkg/storage/fs/ocis/ocis_windows.go b/pkg/storage/fs/ocis/ocis_windows.go new file mode 100644 index 0000000000..f1663b491e --- /dev/null +++ b/pkg/storage/fs/ocis/ocis_windows.go @@ -0,0 +1,36 @@ +// Copyright 2018-2021 CERN +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// In applying this license, CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +// +build windows + +package ocis + +import "golang.org/x/sys/windows" + +func (fs *ocisfs) getAvailableSize(path string) (uint64, error) { + var free, total, avail uint64 + pathPtr, err := windows.UTF16PtrFromString(path) + if err != nil { + return 0, err + } + err = windows.GetDiskFreeSpaceEx(pathPtr, &avail, &total, &free) + if err != nil { + return 0, err + } + return avail, nil +} diff --git a/pkg/storage/utils/localfs/localfs.go b/pkg/storage/utils/localfs/localfs.go index 3b61580788..153baf0878 100644 --- a/pkg/storage/utils/localfs/localfs.go +++ b/pkg/storage/utils/localfs/localfs.go @@ -29,7 +29,6 @@ import ( "path" "strconv" "strings" - "syscall" "time" grouppb "github.com/cs3org/go-cs3apis/cs3/identity/group/v1beta1" @@ -518,20 +517,6 @@ func (fs *localfs) UpdateGrant(ctx context.Context, ref *provider.Reference, g * return fs.AddGrant(ctx, ref, g) } -func (fs *localfs) GetQuota(ctx context.Context) (uint64, uint64, error) { - // TODO quota of which storage space? - // we could use the logged in user, but when a user has access to multiple storages this falls short - // for now return quota of root - stat := syscall.Statfs_t{} - err := syscall.Statfs(fs.conf.Root, &stat) - if err != nil { - return 0, 0, err - } - total := stat.Blocks * uint64(stat.Bsize) // Total data blocks in filesystem - used := (stat.Blocks - stat.Bavail) * uint64(stat.Bsize) // Free blocks available to unprivileged user - return total, used, nil -} - func (fs *localfs) CreateReference(ctx context.Context, path string, targetURI *url.URL) error { if !fs.isShareFolder(ctx, path) { return errtypes.PermissionDenied("localfs: cannot create references outside the share folder") diff --git a/pkg/storage/utils/localfs/localfs_unix.go b/pkg/storage/utils/localfs/localfs_unix.go index 5cac1b7e59..abcf56553f 100644 --- a/pkg/storage/utils/localfs/localfs_unix.go +++ b/pkg/storage/utils/localfs/localfs_unix.go @@ -64,3 +64,17 @@ func calcEtag(ctx context.Context, fi os.FileInfo) string { etag := fmt.Sprintf(`"%x"`, h.Sum(nil)) return fmt.Sprintf("\"%s\"", strings.Trim(etag, "\"")) } + +func (fs *localfs) GetQuota(ctx context.Context) (uint64, uint64, error) { + // TODO quota of which storage space? + // we could use the logged in user, but when a user has access to multiple storages this falls short + // for now return quota of root + stat := syscall.Statfs_t{} + err := syscall.Statfs(fs.wrap(ctx, "/"), &stat) + if err != nil { + return 0, 0, err + } + total := stat.Blocks * uint64(stat.Bsize) // Total data blocks in filesystem + used := (stat.Blocks - stat.Bavail) * uint64(stat.Bsize) // Free blocks available to unprivileged user + return total, used, nil +} diff --git a/pkg/storage/utils/localfs/localfs_windows.go b/pkg/storage/utils/localfs/localfs_windows.go index 31a7590199..7dcab15cc4 100644 --- a/pkg/storage/utils/localfs/localfs_windows.go +++ b/pkg/storage/utils/localfs/localfs_windows.go @@ -29,6 +29,7 @@ import ( "strings" "github.com/cs3org/reva/pkg/appctx" + "golang.org/x/sys/windows" ) // calcEtag will create an etag based on the md5 of @@ -52,3 +53,22 @@ func calcEtag(ctx context.Context, fi os.FileInfo) string { etag := fmt.Sprintf(`"%x"`, h.Sum(nil)) return fmt.Sprintf("\"%s\"", strings.Trim(etag, "\"")) } + +func (fs *localfs) GetQuota(ctx context.Context) (uint64, uint64, error) { + // TODO quota of which storage space? + // we could use the logged in user, but when a user has access to multiple storages this falls short + // for now return quota of root + var free, total, avail uint64 + + pathPtr, err := windows.UTF16PtrFromString(fs.wrap(ctx, "/")) + if err != nil { + return 0, 0, err + } + err = windows.GetDiskFreeSpaceEx(pathPtr, &avail, &total, &free) + if err != nil { + return 0, 0, err + } + + used := total - free + return total, used, nil +} From 644d54f5d5e5cc940edbf786b49bce22d28c5f63 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 24 Feb 2021 17:33:41 +0100 Subject: [PATCH 07/12] [Build-deps]: Bump github.com/ory/fosite from 0.37.0 to 0.38.0 (#1494) --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index cbbafd422e..2684c94ae2 100644 --- a/go.mod +++ b/go.mod @@ -34,7 +34,7 @@ require ( github.com/mitchellh/mapstructure v1.4.1 github.com/onsi/ginkgo v1.15.0 github.com/onsi/gomega v1.10.5 - github.com/ory/fosite v0.37.0 + github.com/ory/fosite v0.38.0 github.com/pkg/errors v0.9.1 github.com/pkg/xattr v0.4.3 github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35 // indirect diff --git a/go.sum b/go.sum index c94803a7bc..10dc0d6c95 100644 --- a/go.sum +++ b/go.sum @@ -746,8 +746,8 @@ github.com/ory/analytics-go/v4 v4.0.0/go.mod h1:FMx9cLRD9xN+XevPvZ5FDMfignpmcqPP github.com/ory/dockertest v3.3.5+incompatible/go.mod h1:1vX4m9wsvi00u5bseYwXaSnhNrne+V0E6LAcBILJdPs= github.com/ory/dockertest/v3 v3.5.4/go.mod h1:J8ZUbNB2FOhm1cFZW9xBpDsODqsSWcyYgtJYVPcnF70= github.com/ory/fosite v0.29.0/go.mod h1:0atSZmXO7CAcs6NPMI/Qtot8tmZYj04Nddoold4S2h0= -github.com/ory/fosite v0.37.0 h1:NaKYm3hhZW1c812uetiNfHlvXmTokIRrOOiTYz+Yhro= -github.com/ory/fosite v0.37.0/go.mod h1:37r59qkOSPueYKmaA7EHiXrDMF1B+XPN+MgkZgTRg3Y= +github.com/ory/fosite v0.38.0 h1:4y+IurqBAu/Gf0NlW47gabRJZyYIqda+OFHMx5fsy6Q= +github.com/ory/fosite v0.38.0/go.mod h1:37r59qkOSPueYKmaA7EHiXrDMF1B+XPN+MgkZgTRg3Y= github.com/ory/go-acc v0.0.0-20181118080137-ddc355013f90/go.mod h1:sxnvPCxChFuSmTJGj8FdMupeq1BezCiEpDjTUXQ4hf4= github.com/ory/go-acc v0.2.5 h1:31irXHzG2vnKQSE4weJm7AdfrnpaVjVCq3nD7viXCJE= github.com/ory/go-acc v0.2.5/go.mod h1:4Kb/UnPcT8qRAk3IAxta+hvVapdxTLWtrr7bFLlEgpw= From d82e050cf59475298d96233baf43a282c91cffbb Mon Sep 17 00:00:00 2001 From: Phil Davis Date: Fri, 26 Feb 2021 14:39:00 +0300 Subject: [PATCH 08/12] [tests-only] Bump commit id for API tests (#1500) --- .drone.yml | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/.drone.yml b/.drone.yml index 3b7f1bb1f1..bab7168663 100644 --- a/.drone.yml +++ b/.drone.yml @@ -488,7 +488,7 @@ steps: - git clone -b master --depth=1 https://github.com/owncloud/testing.git /drone/src/tmp/testing - git clone -b master --single-branch --no-tags https://github.com/owncloud/core.git /drone/src/tmp/testrunner - cd /drone/src/tmp/testrunner - - git checkout df5e095c7e5e94cb44f842a4b51a3480cf8878a0 + - git checkout 76af1195cb662e4cebc5f3fab4e5383bf0498702 - name: localAPIAcceptanceTestsOwncloudStorage image: registry.cern.ch/docker.io/owncloudci/php:7.4 @@ -562,7 +562,7 @@ steps: - git clone -b master --depth=1 https://github.com/owncloud/testing.git /drone/src/tmp/testing - git clone -b master --single-branch --no-tags https://github.com/owncloud/core.git /drone/src/tmp/testrunner - cd /drone/src/tmp/testrunner - - git checkout df5e095c7e5e94cb44f842a4b51a3480cf8878a0 + - git checkout 76af1195cb662e4cebc5f3fab4e5383bf0498702 - name: localAPIAcceptanceTestsOcisStorage image: registry.cern.ch/docker.io/owncloudci/php:7.4 @@ -631,7 +631,7 @@ steps: - git clone -b master --depth=1 https://github.com/owncloud/testing.git /drone/src/tmp/testing - git clone -b master --single-branch --no-tags https://github.com/owncloud/core.git /drone/src/tmp/testrunner - cd /drone/src/tmp/testrunner - - git checkout df5e095c7e5e94cb44f842a4b51a3480cf8878a0 + - git checkout 76af1195cb662e4cebc5f3fab4e5383bf0498702 - name: oC10APIAcceptanceTestsOcisStorage image: registry.cern.ch/docker.io/owncloudci/php:7.4 @@ -703,7 +703,7 @@ steps: - git clone -b master --depth=1 https://github.com/owncloud/testing.git /drone/src/tmp/testing - git clone -b master --single-branch --no-tags https://github.com/owncloud/core.git /drone/src/tmp/testrunner - cd /drone/src/tmp/testrunner - - git checkout df5e095c7e5e94cb44f842a4b51a3480cf8878a0 + - git checkout 76af1195cb662e4cebc5f3fab4e5383bf0498702 - name: oC10APIAcceptanceTestsOcisStorage image: registry.cern.ch/docker.io/owncloudci/php:7.4 @@ -775,7 +775,7 @@ steps: - git clone -b master --depth=1 https://github.com/owncloud/testing.git /drone/src/tmp/testing - git clone -b master --single-branch --no-tags https://github.com/owncloud/core.git /drone/src/tmp/testrunner - cd /drone/src/tmp/testrunner - - git checkout df5e095c7e5e94cb44f842a4b51a3480cf8878a0 + - git checkout 76af1195cb662e4cebc5f3fab4e5383bf0498702 - name: oC10APIAcceptanceTestsOcisStorage image: registry.cern.ch/docker.io/owncloudci/php:7.4 @@ -847,7 +847,7 @@ steps: - git clone -b master --depth=1 https://github.com/owncloud/testing.git /drone/src/tmp/testing - git clone -b master --single-branch --no-tags https://github.com/owncloud/core.git /drone/src/tmp/testrunner - cd /drone/src/tmp/testrunner - - git checkout df5e095c7e5e94cb44f842a4b51a3480cf8878a0 + - git checkout 76af1195cb662e4cebc5f3fab4e5383bf0498702 - name: oC10APIAcceptanceTestsOcisStorage image: registry.cern.ch/docker.io/owncloudci/php:7.4 @@ -919,7 +919,7 @@ steps: - git clone -b master --depth=1 https://github.com/owncloud/testing.git /drone/src/tmp/testing - git clone -b master --single-branch --no-tags https://github.com/owncloud/core.git /drone/src/tmp/testrunner - cd /drone/src/tmp/testrunner - - git checkout df5e095c7e5e94cb44f842a4b51a3480cf8878a0 + - git checkout 76af1195cb662e4cebc5f3fab4e5383bf0498702 - name: oC10APIAcceptanceTestsOcisStorage image: registry.cern.ch/docker.io/owncloudci/php:7.4 @@ -991,7 +991,7 @@ steps: - git clone -b master --depth=1 https://github.com/owncloud/testing.git /drone/src/tmp/testing - git clone -b master --single-branch --no-tags https://github.com/owncloud/core.git /drone/src/tmp/testrunner - cd /drone/src/tmp/testrunner - - git checkout df5e095c7e5e94cb44f842a4b51a3480cf8878a0 + - git checkout 76af1195cb662e4cebc5f3fab4e5383bf0498702 - name: oC10APIAcceptanceTestsOcisStorage image: registry.cern.ch/docker.io/owncloudci/php:7.4 @@ -1063,7 +1063,7 @@ steps: - git clone -b master --depth=1 https://github.com/owncloud/testing.git /drone/src/tmp/testing - git clone -b master --single-branch --no-tags https://github.com/owncloud/core.git /drone/src/tmp/testrunner - cd /drone/src/tmp/testrunner - - git checkout df5e095c7e5e94cb44f842a4b51a3480cf8878a0 + - git checkout 76af1195cb662e4cebc5f3fab4e5383bf0498702 - name: oC10APIAcceptanceTestsOwncloudStorage image: registry.cern.ch/docker.io/owncloudci/php:7.4 @@ -1140,7 +1140,7 @@ steps: - git clone -b master --depth=1 https://github.com/owncloud/testing.git /drone/src/tmp/testing - git clone -b master --single-branch --no-tags https://github.com/owncloud/core.git /drone/src/tmp/testrunner - cd /drone/src/tmp/testrunner - - git checkout df5e095c7e5e94cb44f842a4b51a3480cf8878a0 + - git checkout 76af1195cb662e4cebc5f3fab4e5383bf0498702 - name: oC10APIAcceptanceTestsOwncloudStorage image: registry.cern.ch/docker.io/owncloudci/php:7.4 @@ -1217,7 +1217,7 @@ steps: - git clone -b master --depth=1 https://github.com/owncloud/testing.git /drone/src/tmp/testing - git clone -b master --single-branch --no-tags https://github.com/owncloud/core.git /drone/src/tmp/testrunner - cd /drone/src/tmp/testrunner - - git checkout df5e095c7e5e94cb44f842a4b51a3480cf8878a0 + - git checkout 76af1195cb662e4cebc5f3fab4e5383bf0498702 - name: oC10APIAcceptanceTestsOwncloudStorage image: registry.cern.ch/docker.io/owncloudci/php:7.4 @@ -1294,7 +1294,7 @@ steps: - git clone -b master --depth=1 https://github.com/owncloud/testing.git /drone/src/tmp/testing - git clone -b master --single-branch --no-tags https://github.com/owncloud/core.git /drone/src/tmp/testrunner - cd /drone/src/tmp/testrunner - - git checkout df5e095c7e5e94cb44f842a4b51a3480cf8878a0 + - git checkout 76af1195cb662e4cebc5f3fab4e5383bf0498702 - name: oC10APIAcceptanceTestsOwncloudStorage image: registry.cern.ch/docker.io/owncloudci/php:7.4 @@ -1371,7 +1371,7 @@ steps: - git clone -b master --depth=1 https://github.com/owncloud/testing.git /drone/src/tmp/testing - git clone -b master --single-branch --no-tags https://github.com/owncloud/core.git /drone/src/tmp/testrunner - cd /drone/src/tmp/testrunner - - git checkout df5e095c7e5e94cb44f842a4b51a3480cf8878a0 + - git checkout 76af1195cb662e4cebc5f3fab4e5383bf0498702 - name: oC10APIAcceptanceTestsOwncloudStorage image: registry.cern.ch/docker.io/owncloudci/php:7.4 @@ -1448,7 +1448,7 @@ steps: - git clone -b master --depth=1 https://github.com/owncloud/testing.git /drone/src/tmp/testing - git clone -b master --single-branch --no-tags https://github.com/owncloud/core.git /drone/src/tmp/testrunner - cd /drone/src/tmp/testrunner - - git checkout df5e095c7e5e94cb44f842a4b51a3480cf8878a0 + - git checkout 76af1195cb662e4cebc5f3fab4e5383bf0498702 - name: oC10APIAcceptanceTestsOwncloudStorage image: registry.cern.ch/docker.io/owncloudci/php:7.4 From fd433b66ccdd5940fa4f7c3d18795fc99924e4ec Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 26 Feb 2021 12:40:33 +0100 Subject: [PATCH 09/12] [Build-deps]: Bump go.opencensus.io from 0.22.6 to 0.23.0 (#1496) --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 2684c94ae2..ed11f40e5a 100644 --- a/go.mod +++ b/go.mod @@ -43,7 +43,7 @@ require ( github.com/stretchr/testify v1.7.0 github.com/studio-b12/gowebdav v0.0.0-20200303150724-9380631c29a1 github.com/tus/tusd v1.1.1-0.20200416115059-9deabf9d80c2 - go.opencensus.io v0.22.6 + go.opencensus.io v0.23.0 golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d diff --git a/go.sum b/go.sum index 10dc0d6c95..0b71f2fb43 100644 --- a/go.sum +++ b/go.sum @@ -978,8 +978,8 @@ go.opencensus.io v0.22.1/go.mod h1:Ap50jQcDJrx6rB6VgeeFPtuPIf3wMRvRfrfYDO6+BmA= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.6 h1:BdkrbWrzDlV9dnbzoP7sfN+dHheJ4J9JOaYxcUDL+ok= -go.opencensus.io v0.22.6/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= +go.opencensus.io v0.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M= +go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.13.0/go.mod h1:TwTkyRaTam1pOIb2wxcAiC2hkMVbokXkt6DEt5nDkD8= go.opentelemetry.io/otel v0.13.0/go.mod h1:dlSNewoRYikTkotEnxdmuBHgzT+k/idJSfDv/FxEnOY= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= From 08c17e7477a65a5605b4e4e3976d115857d98bd2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 26 Feb 2021 12:40:51 +0100 Subject: [PATCH 10/12] [Build-deps]: Bump google.golang.org/grpc from 1.35.0 to 1.36.0 (#1498) Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.35.0 to 1.36.0. - [Release notes](https://github.com/grpc/grpc-go/releases) - [Commits](https://github.com/grpc/grpc-go/compare/v1.35.0...v1.36.0) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go.mod b/go.mod index ed11f40e5a..fb627220c7 100644 --- a/go.mod +++ b/go.mod @@ -49,7 +49,7 @@ require ( golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d golang.org/x/sys v0.0.0-20210218155724-8ebf48af031b golang.org/x/term v0.0.0-20201117132131-f5c789dd3221 - google.golang.org/grpc v1.35.0 + google.golang.org/grpc v1.36.0 ) go 1.13 From 0e57a9d0a5cf7e8a83420a1eab792c96dfe3ec52 Mon Sep 17 00:00:00 2001 From: Phil Davis Date: Fri, 26 Feb 2021 16:17:53 +0300 Subject: [PATCH 11/12] [tests-only] Remove apiProvisioning from expected-failures (#1503) --- .../expected-failures-on-EOS-storage.md | 98 -------- .../expected-failures-on-OCIS-storage.md | 230 +----------------- .../expected-failures-on-OWNCLOUD-storage.md | 230 +----------------- 3 files changed, 2 insertions(+), 556 deletions(-) diff --git a/tests/acceptance/expected-failures-on-EOS-storage.md b/tests/acceptance/expected-failures-on-EOS-storage.md index 0a55f1e91f..8dc92aeb5c 100644 --- a/tests/acceptance/expected-failures-on-EOS-storage.md +++ b/tests/acceptance/expected-failures-on-EOS-storage.md @@ -144,104 +144,6 @@ - [apiWebdavOperations/search.feature:213](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiWebdavOperations/search.feature#L213) - [apiWebdavOperations/search.feature:229](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiWebdavOperations/search.feature#L229) -### OCS Provisioning API scenarios that fail - -to be investigated and issues raised or comments added here - -- [apiProvisioning-v1/addUser.feature:29](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v1/addUser.feature#L29) -- [apiProvisioning-v1/addUser.feature:30](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v1/addUser.feature#L30) -- [apiProvisioning-v1/addUser.feature:32](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v1/addUser.feature#L32) -- [apiProvisioning-v1/addUser.feature:39](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v1/addUser.feature#L39) -- [apiProvisioning-v1/addUser.feature:69](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v1/addUser.feature#L69) -- [apiProvisioning-v1/addUser.feature:96](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v1/addUser.feature#L96) -- [apiProvisioning-v1/addUser.feature:97](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v1/addUser.feature#L97) -- [apiProvisioning-v1/addUser.feature:98](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v1/addUser.feature#L98) -- [apiProvisioning-v1/addUser.feature:99](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v1/addUser.feature#L99) -- [apiProvisioning-v1/addUser.feature:100](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v1/addUser.feature#L100) -- [apiProvisioning-v1/addUser.feature:102](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v1/addUser.feature#L102) -- [apiProvisioning-v1/addUser.feature:118](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v1/addUser.feature#L118) -- [apiProvisioning-v1/addUser.feature:121](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v1/addUser.feature#L121) -- [apiProvisioning-v1/addUser.feature:122](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v1/addUser.feature#L122) -- [apiProvisioning-v1/addUser.feature:123](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v1/addUser.feature#L123) -- [apiProvisioning-v1/apiProvisioningUsingAppPassword.feature:39](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v1/apiProvisioningUsingAppPassword.feature#L39) -- [apiProvisioning-v1/apiProvisioningUsingAppPassword.feature:67](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v1/apiProvisioningUsingAppPassword.feature#L67) -- [apiProvisioning-v1/deleteUser.feature:29](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v1/deleteUser.feature#L29) -- [apiProvisioning-v1/deleteUser.feature:30](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v1/deleteUser.feature#L30) -- [apiProvisioning-v1/deleteUser.feature:32](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v1/deleteUser.feature#L32) -- [apiProvisioning-v1/deleteUser.feature:53](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v1/deleteUser.feature#L53) -- [apiProvisioning-v1/disableUser.feature:11](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v1/disableUser.feature#L11) -- [apiProvisioning-v1/disableUser.feature:29](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v1/disableUser.feature#L29) -- [apiProvisioning-v1/disableUser.feature:30](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v1/disableUser.feature#L30) -- [apiProvisioning-v1/disableUser.feature:79](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v1/disableUser.feature#L79) -- [apiProvisioning-v1/disableUser.feature:99](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v1/disableUser.feature#L99) -- [apiProvisioning-v1/disableUser.feature:107](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v1/disableUser.feature#L107) -- [apiProvisioning-v1/disableUser.feature:129](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v1/disableUser.feature#L129) -- [apiProvisioning-v1/editUser.feature:29](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v1/editUser.feature#L29) -- [apiProvisioning-v1/editUser.feature:30](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v1/editUser.feature#L30) -- [apiProvisioning-v1/editUser.feature:47](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v1/editUser.feature#L47) -- [apiProvisioning-v1/editUser.feature:56](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v1/editUser.feature#L56) -- [apiProvisioning-v1/editUser.feature:122](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v1/editUser.feature#L122) -- [apiProvisioning-v1/enableUser.feature:11](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v1/enableUser.feature#L11) -- [apiProvisioning-v1/enableUser.feature:31](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v1/enableUser.feature#L31) -- [apiProvisioning-v1/enableUser.feature:32](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v1/enableUser.feature#L32) -- [apiProvisioning-v1/enableUser.feature:34](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v1/enableUser.feature#L34) -- [apiProvisioning-v1/enableUser.feature:56](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v1/enableUser.feature#L56) -- [apiProvisioning-v1/enableUser.feature:63](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v1/enableUser.feature#L63) -- [apiProvisioning-v1/enableUser.feature:86](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v1/enableUser.feature#L86) -- [apiProvisioning-v1/getUser.feature:34](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v1/getUser.feature#L34) -- [apiProvisioning-v1/getUser.feature:35](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v1/getUser.feature#L35) -- [apiProvisioning-v1/getUser.feature:37](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v1/getUser.feature#L37) -- [apiProvisioning-v1/getUser.feature:81](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v1/getUser.feature#L81) -- [apiProvisioning-v1/getUsers.feature:11](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v1/getUsers.feature#L11) -- [apiProvisioning-v1/getUsers.feature:35](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v1/getUsers.feature#L35) -- [apiProvisioning-v1/resetUserPassword.feature:21](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v1/resetUserPassword.feature#L21) -- [apiProvisioning-v1/resetUserPassword.feature:55](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v1/resetUserPassword.feature#L55) -- [apiProvisioning-v2/addUser.feature:29](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/addUser.feature#L29) -- [apiProvisioning-v2/addUser.feature:30](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/addUser.feature#L30) -- [apiProvisioning-v2/addUser.feature:32](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/addUser.feature#L32) -- [apiProvisioning-v2/addUser.feature:39](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/addUser.feature#L39) -- [apiProvisioning-v2/addUser.feature:69](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/addUser.feature#L69) -- [apiProvisioning-v2/addUser.feature:96](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/addUser.feature#L96) -- [apiProvisioning-v2/addUser.feature:97](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/addUser.feature#L97) -- [apiProvisioning-v2/addUser.feature:98](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/addUser.feature#L98) -- [apiProvisioning-v2/addUser.feature:99](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/addUser.feature#L99) -- [apiProvisioning-v2/addUser.feature:100](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/addUser.feature#L100) -- [apiProvisioning-v2/addUser.feature:102](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/addUser.feature#L102) -- [apiProvisioning-v2/apiProvisioningUsingAppPassword.feature:39](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/apiProvisioningUsingAppPassword.feature#L39) -- [apiProvisioning-v2/apiProvisioningUsingAppPassword.feature:67](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/apiProvisioningUsingAppPassword.feature#L67) -- [apiProvisioning-v2/deleteUser.feature:29](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/deleteUser.feature#L29) -- [apiProvisioning-v2/deleteUser.feature:30](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/deleteUser.feature#L30) -- [apiProvisioning-v2/deleteUser.feature:32](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/deleteUser.feature#L32) -- [apiProvisioning-v2/deleteUser.feature:54](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/deleteUser.feature#L54) -- [apiProvisioning-v2/disableUser.feature:11](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/disableUser.feature#L11) -- [apiProvisioning-v2/disableUser.feature:29](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/disableUser.feature#L29) -- [apiProvisioning-v2/disableUser.feature:30](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/disableUser.feature#L30) -- [apiProvisioning-v2/disableUser.feature:81](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/disableUser.feature#L81) -- [apiProvisioning-v2/disableUser.feature:101](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/disableUser.feature#L101) -- [apiProvisioning-v2/disableUser.feature:110](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/disableUser.feature#L110) -- [apiProvisioning-v2/disableUser.feature:133](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/disableUser.feature#L133) -- [apiProvisioning-v2/editUser.feature:29](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/editUser.feature#L29) -- [apiProvisioning-v2/editUser.feature:30](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/editUser.feature#L30) -- [apiProvisioning-v2/editUser.feature:47](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/editUser.feature#L47) -- [apiProvisioning-v2/editUser.feature:56](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/editUser.feature#L56) -- [apiProvisioning-v2/editUser.feature:122](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/editUser.feature#L122) -- [apiProvisioning-v2/enableUser.feature:11](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/enableUser.feature#L11) -- [apiProvisioning-v2/enableUser.feature:31](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/enableUser.feature#L31) -- [apiProvisioning-v2/enableUser.feature:32](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/enableUser.feature#L32) -- [apiProvisioning-v2/enableUser.feature:34](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/enableUser.feature#L34) -- [apiProvisioning-v2/enableUser.feature:56](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/enableUser.feature#L56) -- [apiProvisioning-v2/enableUser.feature:64](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/enableUser.feature#L64) -- [apiProvisioning-v2/enableUser.feature:88](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/enableUser.feature#L88) -- [apiProvisioning-v2/getUser.feature:34](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/getUser.feature#L34) -- [apiProvisioning-v2/getUser.feature:35](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/getUser.feature#L35) -- [apiProvisioning-v2/getUser.feature:37](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/getUser.feature#L37) -- [apiProvisioning-v2/getUser.feature:47](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/getUser.feature#L47) -- [apiProvisioning-v2/getUser.feature:82](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/getUser.feature#L82) -- [apiProvisioning-v2/getUsers.feature:11](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/getUsers.feature#L11) -- [apiProvisioning-v2/getUsers.feature:36](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/getUsers.feature#L36) -- [apiProvisioning-v2/resetUserPassword.feature:21](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/resetUserPassword.feature#L21) -- [apiProvisioning-v2/resetUserPassword.feature:55](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/resetUserPassword.feature#L55) - ### [Cannot set mtime on upload](https://github.com/owncloud/product/issues/271) - [apiSharePublicLink1/createPublicLinkShare.feature:776](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiSharePublicLink1/createPublicLinkShare.feature#L776) - [apiSharePublicLink1/createPublicLinkShare.feature:789](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiSharePublicLink1/createPublicLinkShare.feature#L789) diff --git a/tests/acceptance/expected-failures-on-OCIS-storage.md b/tests/acceptance/expected-failures-on-OCIS-storage.md index c826ed2691..51a66dd4e7 100644 --- a/tests/acceptance/expected-failures-on-OCIS-storage.md +++ b/tests/acceptance/expected-failures-on-OCIS-storage.md @@ -168,6 +168,7 @@ Basic file management like up and download, move, copy, properties, quota, trash - [apiVersions/fileVersions.feature:373](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiVersions/fileVersions.feature#L373) - [apiVersions/fileVersions.feature:408](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiVersions/fileVersions.feature#L408) - [apiVersions/fileVersions.feature:419](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiVersions/fileVersions.feature#L419) +- [apiVersions/fileVersions.feature:425](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiVersions/fileVersions.feature#L425) #### [PUT request with missing parent must return status code 409](https://github.com/owncloud/ocis/issues/824) - [apiWebdavUpload1/uploadFile.feature:112](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiWebdavUpload1/uploadFile.feature#L112) @@ -1761,81 +1762,6 @@ Scenario Outline: Renaming a file to a path with extension .part should not be p ### User Management User and group management features -#### [Cannot create user with different username and emails](https://github.com/owncloud/product/issues/187) - special character username not valid -- [apiProvisioning-v1/addUser.feature:29](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v1/addUser.feature#L29) -- [apiProvisioning-v1/addUser.feature:30](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v1/addUser.feature#L30) -- [apiProvisioning-v1/addUser.feature:121](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v1/addUser.feature#L121) -- [apiProvisioning-v1/addUser.feature:122](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v1/addUser.feature#L122) -- [apiProvisioning-v1/addUser.feature:123](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v1/addUser.feature#L123) -- [apiProvisioning-v1/deleteUser.feature:29](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v1/deleteUser.feature#L29) -- [apiProvisioning-v1/deleteUser.feature:30](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v1/deleteUser.feature#L30) -- [apiProvisioning-v1/disableUser.feature:29](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v1/disableUser.feature#L29) -- [apiProvisioning-v1/disableUser.feature:30](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v1/disableUser.feature#L30) -- [apiProvisioning-v1/editUser.feature:29](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v1/editUser.feature#L29) -- [apiProvisioning-v1/editUser.feature:30](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v1/editUser.feature#L30) -- [apiProvisioning-v1/enableUser.feature:31](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v1/enableUser.feature#L31) -- [apiProvisioning-v1/enableUser.feature:32](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v1/enableUser.feature#L32) -- [apiProvisioning-v1/getUser.feature:34](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v1/getUser.feature#L34) -- [apiProvisioning-v1/getUser.feature:35](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v1/getUser.feature#L35) -- [apiProvisioning-v2/addUser.feature:29](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/addUser.feature#L29) -- [apiProvisioning-v2/addUser.feature:30](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/addUser.feature#L30) -- [apiProvisioning-v2/deleteUser.feature:29](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/deleteUser.feature#L29) -- [apiProvisioning-v2/deleteUser.feature:30](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/deleteUser.feature#L30) -- [apiProvisioning-v2/disableUser.feature:29](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/disableUser.feature#L29) -- [apiProvisioning-v2/disableUser.feature:30](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/disableUser.feature#L30) -- [apiProvisioning-v2/editUser.feature:29](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/editUser.feature#L29) -- [apiProvisioning-v2/editUser.feature:30](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/editUser.feature#L30) -- [apiProvisioning-v2/enableUser.feature:31](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/enableUser.feature#L31) -- [apiProvisioning-v2/enableUser.feature:32](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/enableUser.feature#L32) -- [apiProvisioning-v2/getUser.feature:34](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/getUser.feature#L34) -- [apiProvisioning-v2/getUser.feature:35](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/getUser.feature#L35) - -#### [Creating an already existing user works](https://github.com/owncloud/ocis-accounts/issues/80) -- [apiProvisioning-v1/addUser.feature:32](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v1/addUser.feature#L32) -- [apiProvisioning-v2/addUser.feature:32](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/addUser.feature#L32) -- [apiProvisioning-v1/addUser.feature:39](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v1/addUser.feature#L39) -- [apiProvisioning-v2/addUser.feature:39](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/addUser.feature#L39) - -#### [Password can be set to empty](https://github.com/owncloud/product/issues/197) -- [apiProvisioning-v1/addUser.feature:69](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v1/addUser.feature#L69) -- [apiProvisioning-v2/addUser.feature:69](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/addUser.feature#L69) - -#### [Username is case sensitive](https://github.com/owncloud/ocis-accounts/issues/128) -- [apiProvisioning-v1/addUser.feature:102](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v1/addUser.feature#L102) -- [apiProvisioning-v2/addUser.feature:102](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/addUser.feature#L102) - -#### [Client token generation not implemented](https://github.com/owncloud/ocis/issues/197) -- [apiProvisioning-v1/apiProvisioningUsingAppPassword.feature:39](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v1/apiProvisioningUsingAppPassword.feature#L39) -- [apiProvisioning-v1/apiProvisioningUsingAppPassword.feature:67](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v1/apiProvisioningUsingAppPassword.feature#L67) -- [apiProvisioning-v2/apiProvisioningUsingAppPassword.feature:39](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/apiProvisioningUsingAppPassword.feature#L39) -- [apiProvisioning-v2/apiProvisioningUsingAppPassword.feature:67](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/apiProvisioningUsingAppPassword.feature#L67) - -#### [disable users /cloud/users/disable|enable not available](https://github.com/owncloud/ocis/issues/1420) -- [apiProvisioning-v1/enableUser.feature:92](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/enableUser.feature#L92) -- [apiProvisioning-v1/enableUser.feature:102](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v1/enableUser.feature#L102) -- [apiProvisioning-v1/enableUser.feature:129](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v1/enableUser.feature#L129) -- [apiProvisioning-v1/disableUser.feature:99](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v1/disableUser.feature#L99) -- [apiProvisioning-v1/disableUser.feature:129](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v1/disableUser.feature#L129) -- [apiProvisioning-v1/disableUser.feature:165](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v1/disableUser.feature#L165) -- [apiProvisioning-v1/disableUser.feature:172](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v1/disableUser.feature#L172) -- [apiProvisioning-v1/disableUser.feature:190](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v1/disableUser.feature#L190) -- [apiProvisioning-v1/disableUser.feature:203](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v1/disableUser.feature#L203) -- [apiProvisioning-v1/disableUser.feature:219](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v1/disableUser.feature#L219) -- [apiProvisioning-v2/disableUser.feature:79](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/disableUser.feature#L79) -- [apiProvisioning-v2/disableUser.feature:99](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/disableUser.feature#L99) -- [apiProvisioning-v2/disableUser.feature:108](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/disableUser.feature#L108) -- [apiProvisioning-v2/disableUser.feature:130](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/disableUser.feature#L130) -- [apiProvisioning-v2/disableUser.feature:166](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/disableUser.feature#L166) -- [apiProvisioning-v2/disableUser.feature:173](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/disableUser.feature#L173) -- [apiProvisioning-v2/disableUser.feature:191](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/disableUser.feature#L191) -- [apiProvisioning-v2/disableUser.feature:204](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/disableUser.feature#L204) -- [apiProvisioning-v2/disableUser.feature:220](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/disableUser.feature#L220) - -#### [displayname of user can be changed to empty](https://github.com/owncloud/ocis-ocs/issues/51) -- [apiProvisioning-v1/editUser.feature:47](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v1/editUser.feature#L47) -- [apiProvisioning-v2/editUser.feature:47](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/editUser.feature#L47) - #### [quota query](https://github.com/owncloud/ocis/issues/1313) _requires a [CS3 user provisioning api that can update the quota for a user](https://github.com/cs3org/cs3apis/pull/95#issuecomment-772780683)_ - [apiMain/quota.feature:9](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiMain/quota.feature#L9) Scenario: Uploading a file as owner having enough quota @@ -1849,145 +1775,6 @@ Scenario Outline: Retrieving folder quota when quota is set - [apiWebdavProperties1/getQuota.feature:27](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiWebdavProperties1/getQuota.feature#L27) - [apiWebdavProperties1/getQuota.feature:28](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiWebdavProperties1/getQuota.feature#L28) -#### [changing user quota gives ocs status 103 / Cannot set quota](https://github.com/owncloud/product/issues/247) -_requires a [CS3 user provisioning api that can update the quota for a user](https://github.com/cs3org/cs3apis/pull/95#issuecomment-772780683)_ -- [apiProvisioning-v1/editUser.feature:56](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v1/editUser.feature#L56) -- [apiProvisioning-v1/editUser.feature:122](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v1/editUser.feature#L122) -- [apiProvisioning-v2/editUser.feature:56](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/editUser.feature#L56) -- [apiProvisioning-v2/editUser.feature:122](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/editUser.feature#L122) -- [apiProvisioning-v2/enableUser.feature:34](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/enableUser.feature#L34) -- [apiProvisioning-v2/enableUser.feature:56](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/enableUser.feature#L56) -- [apiProvisioning-v2/enableUser.feature:64](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/enableUser.feature#L64) - -#### [user can get info of other users/ cloud/users endpoints not authenticated](https://github.com/owncloud/product/issues/248) -- [apiProvisioning-v2/deleteUser.feature:54](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/deleteUser.feature#L54) -- [apiProvisioning-v1/getUser.feature:81](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v1/getUser.feature#L81) -- [apiProvisioning-v2/getUser.feature:82](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/getUser.feature#L82) -- [apiProvisioning-v2/getUsers.feature:44](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/getUsers.feature#L44) - -#### [incorrect ocs(v2) status value when getting info of user that does not exist should be 404, gives 998](https://github.com/owncloud/product/issues/250) -_ocs: api compatibility, return correct status code_ -- [apiProvisioning-v2/getUser.feature:47](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/getUser.feature#L47) - -#### [subadmin endpoints not implemented for users](https://github.com/owncloud/product/issues/289) -- [apiProvisioning-v1/addUser.feature:125](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v1/addUser.feature#L125) -- [apiProvisioning-v1/createSubAdmin.feature:48](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v1/createSubAdmin.feature#L48) -- [apiProvisioning-v1/deleteUser.feature:73](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v1/deleteUser.feature#L73) -- [apiProvisioning-v1/disableUser.feature:222](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v1/disableUser.feature#L222) -- [apiProvisioning-v1/editUser.feature:131](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v1/editUser.feature#L131) -- [apiProvisioning-v1/editUser.feature:143](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v1/editUser.feature#L143) -- [apiProvisioning-v1/getUser.feature:170](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v1/getUser.feature#L170) -- [apiProvisioning-v1/resetUserPassword.feature:113](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v1/resetUserPassword.feature#L113) - -- [apiProvisioning-v2/addUser.feature:125](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/addUser.feature#L125) -- [apiProvisioning-v2/createSubAdmin.feature:48](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/createSubAdmin.feature#L48) -- [apiProvisioning-v2/deleteUser.feature:64](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/deleteUser.feature#L64) -- [apiProvisioning-v2/deleteUser.feature:74](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/deleteUser.feature#L74) -- [apiProvisioning-v2/deleteUser.feature:88](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/deleteUser.feature#L88) -- [apiProvisioning-v2/deleteUser.feature:101](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/deleteUser.feature#L101) -- [apiProvisioning-v2/deleteUser.feature:115](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/deleteUser.feature#L115) -- [apiProvisioning-v2/disableUser.feature:48](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/disableUser.feature#L48) -- [apiProvisioning-v2/disableUser.feature:64](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/disableUser.feature#L64) -- [apiProvisioning-v2/disableUser.feature:223](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/disableUser.feature#L223) -- [apiProvisioning-v2/disableUser.feature:237](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/disableUser.feature#L237) -- [apiProvisioning-v2/editUser.feature:131](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/editUser.feature#L131) -- [apiProvisioning-v2/editUser.feature:143](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/editUser.feature#L143) -- [apiProvisioning-v2/editUser.feature:159](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/editUser.feature#L159) -- [apiProvisioning-v2/enableUser.feature:104](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/enableUser.feature#L104) -- [apiProvisioning-v2/enableUser.feature:118](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/enableUser.feature#L118) -- [apiProvisioning-v2/enableUser.feature:131](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/enableUser.feature#L131) -- [apiProvisioning-v2/enableUser.feature:146](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/enableUser.feature#L146) -- [apiProvisioning-v2/getUser.feature:145](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/getUser.feature#L145) -- [apiProvisioning-v2/getUser.feature:156](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/getUser.feature#L156) -- [apiProvisioning-v2/getUser.feature:171](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/getUser.feature#L171) -- [apiProvisioning-v2/resetUserPassword.feature:101](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/resetUserPassword.feature#L101) -- [apiProvisioning-v2/resetUserPassword.feature:113](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/resetUserPassword.feature#L113) -- [apiProvisioning-v2/resetUserPassword.feature:129](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/resetUserPassword.feature#L129) - -#### [subadmin endpoints not implemented for groups](https://github.com/owncloud/product/issues/289) -- [apiProvisioningGroups-v1/addGroup.feature:87](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioningGroups-v1/addGroup.feature#L87) -- [apiProvisioningGroups-v1/addToGroup.feature:133](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioningGroups-v1/addToGroup.feature#L133) -- [apiProvisioningGroups-v1/addToGroup.feature:146](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioningGroups-v1/addToGroup.feature#L146) -- [apiProvisioningGroups-v1/deleteGroup.feature:99](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioningGroups-v1/deleteGroup.feature#L99) -- [apiProvisioningGroups-v1/getGroup.feature:72](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioningGroups-v1/getGroup.feature#L72) -- [apiProvisioningGroups-v1/getSubAdminGroups.feature:11](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioningGroups-v1/getSubAdminGroups.feature#L11) -- [apiProvisioningGroups-v1/getSubAdminGroups.feature:24](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioningGroups-v1/getSubAdminGroups.feature#L24) -- [apiProvisioningGroups-v1/getUserGroups.feature:56](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioningGroups-v1/getUserGroups.feature#L56) -- [apiProvisioningGroups-v1/removeFromGroup.feature:110](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioningGroups-v1/removeFromGroup.feature#L110) -- [apiProvisioningGroups-v1/removeFromGroup.feature:123](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioningGroups-v1/removeFromGroup.feature#L123) - -- [apiProvisioningGroups-v2/addGroup.feature:87](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioningGroups-v2/addGroup.feature#L87) -- [apiProvisioningGroups-v2/addGroup.feature:109](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioningGroups-v2/addGroup.feature#L109) -- [apiProvisioningGroups-v2/addToGroup.feature:128](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioningGroups-v2/addToGroup.feature#L128) -- [apiProvisioningGroups-v2/addToGroup.feature:141](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioningGroups-v2/addToGroup.feature#L141) -- [apiProvisioningGroups-v2/deleteGroup.feature:101](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioningGroups-v2/deleteGroup.feature#L101) -- [apiProvisioningGroups-v2/getGroup.feature:55](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioningGroups-v2/getGroup.feature#L55) -- [apiProvisioningGroups-v2/getGroup.feature:73](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioningGroups-v2/getGroup.feature#L73) -- [apiProvisioningGroups-v2/getSubAdminGroups.feature:11](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioningGroups-v2/getSubAdminGroups.feature#L11) -- [apiProvisioningGroups-v2/getSubAdminGroups.feature:24](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioningGroups-v2/getSubAdminGroups.feature#L24) -- [apiProvisioningGroups-v2/getUserGroups.feature:56](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioningGroups-v2/getUserGroups.feature#L56) -- [apiProvisioningGroups-v2/removeFromGroup.feature:110](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioningGroups-v2/removeFromGroup.feature#L110) -- [apiProvisioningGroups-v2/removeFromGroup.feature:123](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioningGroups-v2/removeFromGroup.feature#L123) - -#### [creating existing group doesn't gives error](https://github.com/owncloud/product/issues/282) -- [apiProvisioningGroups-v1/addGroup.feature:94](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioningGroups-v1/addGroup.feature#L94) -- [apiProvisioningGroups-v2/addGroup.feature:94](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioningGroups-v2/addGroup.feature#L94) - -#### [cannot create group with '/'](https://github.com/owncloud/product/issues/285) -- [apiProvisioningGroups-v1/addToGroup.feature:75](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioningGroups-v1/addToGroup.feature#L75) -- [apiProvisioningGroups-v1/addToGroup.feature:76](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioningGroups-v1/addToGroup.feature#L76) -- [apiProvisioningGroups-v1/addToGroup.feature:77](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioningGroups-v1/addToGroup.feature#L77) -- [apiProvisioningGroups-v1/deleteGroup.feature:85](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioningGroups-v1/deleteGroup.feature#L85) -- [apiProvisioningGroups-v1/deleteGroup.feature:86](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioningGroups-v1/deleteGroup.feature#L86) -- [apiProvisioningGroups-v1/deleteGroup.feature:87](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioningGroups-v1/deleteGroup.feature#L87) -- [apiProvisioningGroups-v1/getUserGroups.feature:38](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioningGroups-v1/getUserGroups.feature#L38) -- [apiProvisioningGroups-v1/removeFromGroup.feature:76](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioningGroups-v1/removeFromGroup.feature#L76) -- [apiProvisioningGroups-v1/removeFromGroup.feature:77](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioningGroups-v1/removeFromGroup.feature#L77) -- [apiProvisioningGroups-v1/removeFromGroup.feature:78](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioningGroups-v1/removeFromGroup.feature#L78) - -- [apiProvisioningGroups-v2/addToGroup.feature:70](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioningGroups-v2/addToGroup.feature#L70) -- [apiProvisioningGroups-v2/addToGroup.feature:71](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioningGroups-v2/addToGroup.feature#L71) -- [apiProvisioningGroups-v2/addToGroup.feature:72](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioningGroups-v2/addToGroup.feature#L72) -- [apiProvisioningGroups-v2/deleteGroup.feature:85](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioningGroups-v2/deleteGroup.feature#L85) -- [apiProvisioningGroups-v2/deleteGroup.feature:86](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioningGroups-v2/deleteGroup.feature#L86) -- [apiProvisioningGroups-v2/deleteGroup.feature:87](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioningGroups-v2/deleteGroup.feature#L87) -- [apiProvisioningGroups-v2/getUserGroups.feature:38](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioningGroups-v2/getUserGroups.feature#L38) -- [apiProvisioningGroups-v2/removeFromGroup.feature:76](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioningGroups-v2/removeFromGroup.feature#L76) -- [apiProvisioningGroups-v2/removeFromGroup.feature:77](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioningGroups-v2/removeFromGroup.feature#L77) -- [apiProvisioningGroups-v2/removeFromGroup.feature:78](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioningGroups-v2/removeFromGroup.feature#L78) - -#### [adding user to non-existent group gives wrong statuscode](https://github.com/owncloud/product/issues/286) -- [apiProvisioningGroups-v1/addToGroup.feature:107](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioningGroups-v1/addToGroup.feature#L107) -- [apiProvisioningGroups-v1/removeFromGroup.feature:101](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioningGroups-v1/removeFromGroup.feature#L101) -- [apiProvisioningGroups-v2/removeFromGroup.feature:101](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioningGroups-v2/removeFromGroup.feature#L101) - -#### [adding user to empty group gives wrong statuscode](https://github.com/owncloud/product/issues/287) -- [apiProvisioningGroups-v1/addToGroup.feature:116](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioningGroups-v1/addToGroup.feature#L116) - -#### [adding non-existent user to a group gives wrong status code](https://github.com/owncloud/product/issues/288) -- [apiProvisioningGroups-v1/addToGroup.feature:124](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioningGroups-v1/addToGroup.feature#L124) - - -#### [Cannot create user with different username and emails](https://github.com/owncloud/product/issues/187) -_special character username not valid_ -- [apiProvisioningGroups-v1/getGroup.feature:11](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioningGroups-v1/getGroup.feature#L11) -- [apiProvisioningGroups-v2/getGroup.feature:11](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioningGroups-v2/getGroup.feature#L11) - -#### [normal users can list the members of the group](https://github.com/owncloud/product/issues/290) -- [apiProvisioningGroups-v1/getGroup.feature:81](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioningGroups-v1/getGroup.feature#L81) -- [apiProvisioningGroups-v1/deleteGroup.feature:90](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioningGroups-v1/deleteGroup.feature#L90) -- [apiProvisioningGroups-v2/getGroup.feature:83](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioningGroups-v2/getGroup.feature#L83) - -#### [ocs v2 invalid status code for group endpoints](https://github.com/owncloud/product/issues/291) -- [apiProvisioningGroups-v2/addGroup.feature:102](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioningGroups-v2/addGroup.feature#L102) -- [apiProvisioningGroups-v2/addToGroup.feature:94](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioningGroups-v2/addToGroup.feature#L94) -- [apiProvisioningGroups-v2/addToGroup.feature:102](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioningGroups-v2/addToGroup.feature#L102) -- [apiProvisioningGroups-v2/addToGroup.feature:119](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioningGroups-v2/addToGroup.feature#L119) -- [apiProvisioningGroups-v2/deleteGroup.feature:91](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioningGroups-v2/deleteGroup.feature#L91) -- [apiProvisioningGroups-v2/getGroup.feature:33](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioningGroups-v2/getGroup.feature#L33) -- [apiProvisioningGroups-v2/getUserGroups.feature:73](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioningGroups-v2/getUserGroups.feature#L73) -- [apiProvisioningGroups-v2/removeFromGroup.feature:138](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioningGroups-v2/removeFromGroup.feature#L138) - #### [user-sync endpoint does not exist](https://github.com/owncloud/ocis/issues/1241) - [apiMain/userSync.feature:18](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiMain/userSync.feature#L18) - [apiMain/userSync.feature:19](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiMain/userSync.feature#L19) @@ -2000,21 +1787,6 @@ _special character username not valid_ - [apiMain/userSync.feature:63](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiMain/userSync.feature#L63) - [apiMain/userSync.feature:64](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiMain/userSync.feature#L64) -- [apiProvisioning-v1/createSubAdmin.feature:11](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v1/createSubAdmin.feature#L11) -- [apiProvisioning-v1/createSubAdmin.feature:19](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v1/createSubAdmin.feature#L19) -- [apiProvisioning-v1/createSubAdmin.feature:27](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v1/createSubAdmin.feature#L27) -- [apiProvisioning-v1/createSubAdmin.feature:35](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v1/createSubAdmin.feature#L35) -- [apiProvisioning-v1/getSubAdmins.feature:11](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v1/getSubAdmins.feature#L11) -- [apiProvisioning-v1/getSubAdmins.feature:21](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v1/getSubAdmins.feature#L21) -- [apiProvisioning-v2/createSubAdmin.feature:11](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/createSubAdmin.feature#L11) -- [apiProvisioning-v2/createSubAdmin.feature:19](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/createSubAdmin.feature#L19) -- [apiProvisioning-v2/createSubAdmin.feature:27](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/createSubAdmin.feature#L27) -- [apiProvisioning-v2/createSubAdmin.feature:35](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/createSubAdmin.feature#L35) -- [apiProvisioning-v2/getSubAdmins.feature:11](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/getSubAdmins.feature#L11) -- [apiProvisioning-v2/getSubAdmins.feature:21](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/getSubAdmins.feature#L21) -- [apiProvisioning-v2/getSubAdmins.feature:30](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/getSubAdmins.feature#L30) -- [apiProvisioning-v2/getSubAdmins.feature:44](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/getSubAdmins.feature#L44) - ### Other API, search, favorites, config, capabilities, not existing endpoints, CORS and others diff --git a/tests/acceptance/expected-failures-on-OWNCLOUD-storage.md b/tests/acceptance/expected-failures-on-OWNCLOUD-storage.md index 3276ea8070..8e84a02818 100644 --- a/tests/acceptance/expected-failures-on-OWNCLOUD-storage.md +++ b/tests/acceptance/expected-failures-on-OWNCLOUD-storage.md @@ -184,6 +184,7 @@ The following scenarios fail on OWNCLOUD storage but not on OCIS storage: - [apiVersions/fileVersions.feature:373](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiVersions/fileVersions.feature#L373) - [apiVersions/fileVersions.feature:408](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiVersions/fileVersions.feature#L408) - [apiVersions/fileVersions.feature:419](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiVersions/fileVersions.feature#L419) +- [apiVersions/fileVersions.feature:425](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiVersions/fileVersions.feature#L425) #### [Version cannot be restored when file has been renamed](https://github.com/owncloud/ocis/issues/1633) - [apiVersions/fileVersions.feature:399](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiVersions/fileVersions.feature#L399) @@ -1890,81 +1891,6 @@ The following scenarios fail on OWNCLOUD storage but not on OCIS storage: ### User Management User and group management features -#### [Cannot create user with different username and emails](https://github.com/owncloud/product/issues/187) -special character username not valid -- [apiProvisioning-v1/addUser.feature:29](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v1/addUser.feature#L29) -- [apiProvisioning-v1/addUser.feature:30](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v1/addUser.feature#L30) -- [apiProvisioning-v1/addUser.feature:121](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v1/addUser.feature#L121) -- [apiProvisioning-v1/addUser.feature:122](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v1/addUser.feature#L122) -- [apiProvisioning-v1/addUser.feature:123](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v1/addUser.feature#L123) -- [apiProvisioning-v1/deleteUser.feature:29](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v1/deleteUser.feature#L29) -- [apiProvisioning-v1/deleteUser.feature:30](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v1/deleteUser.feature#L30) -- [apiProvisioning-v1/disableUser.feature:29](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v1/disableUser.feature#L29) -- [apiProvisioning-v1/disableUser.feature:30](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v1/disableUser.feature#L30) -- [apiProvisioning-v1/editUser.feature:29](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v1/editUser.feature#L29) -- [apiProvisioning-v1/editUser.feature:30](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v1/editUser.feature#L30) -- [apiProvisioning-v1/enableUser.feature:31](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v1/enableUser.feature#L31) -- [apiProvisioning-v1/enableUser.feature:32](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v1/enableUser.feature#L32) -- [apiProvisioning-v1/getUser.feature:34](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v1/getUser.feature#L34) -- [apiProvisioning-v1/getUser.feature:35](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v1/getUser.feature#L35) -- [apiProvisioning-v2/addUser.feature:29](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/addUser.feature#L29) -- [apiProvisioning-v2/addUser.feature:30](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/addUser.feature#L30) -- [apiProvisioning-v2/deleteUser.feature:29](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/deleteUser.feature#L29) -- [apiProvisioning-v2/deleteUser.feature:30](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/deleteUser.feature#L30) -- [apiProvisioning-v2/disableUser.feature:29](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/disableUser.feature#L29) -- [apiProvisioning-v2/disableUser.feature:30](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/disableUser.feature#L30) -- [apiProvisioning-v2/editUser.feature:29](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/editUser.feature#L29) -- [apiProvisioning-v2/editUser.feature:30](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/editUser.feature#L30) -- [apiProvisioning-v2/enableUser.feature:31](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/enableUser.feature#L31) -- [apiProvisioning-v2/enableUser.feature:32](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/enableUser.feature#L32) -- [apiProvisioning-v2/getUser.feature:34](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/getUser.feature#L34) -- [apiProvisioning-v2/getUser.feature:35](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/getUser.feature#L35) - -#### [Creating an already existing user works](https://github.com/owncloud/ocis-accounts/issues/80) -- [apiProvisioning-v1/addUser.feature:32](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v1/addUser.feature#L32) -- [apiProvisioning-v2/addUser.feature:32](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/addUser.feature#L32) -- [apiProvisioning-v1/addUser.feature:39](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v1/addUser.feature#L39) -- [apiProvisioning-v2/addUser.feature:39](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/addUser.feature#L39) - -#### [Password can be set to empty](https://github.com/owncloud/product/issues/197) -- [apiProvisioning-v1/addUser.feature:69](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v1/addUser.feature#L69) -- [apiProvisioning-v2/addUser.feature:69](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/addUser.feature#L69) - -#### [Username is case sensitive](https://github.com/owncloud/ocis-accounts/issues/128) -- [apiProvisioning-v1/addUser.feature:102](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v1/addUser.feature#L102) -- [apiProvisioning-v2/addUser.feature:102](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/addUser.feature#L102) - -#### [Client token generation not implemented](https://github.com/owncloud/ocis/issues/197) -- [apiProvisioning-v1/apiProvisioningUsingAppPassword.feature:39](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v1/apiProvisioningUsingAppPassword.feature#L39) -- [apiProvisioning-v1/apiProvisioningUsingAppPassword.feature:67](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v1/apiProvisioningUsingAppPassword.feature#L67) -- [apiProvisioning-v2/apiProvisioningUsingAppPassword.feature:39](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/apiProvisioningUsingAppPassword.feature#L39) -- [apiProvisioning-v2/apiProvisioningUsingAppPassword.feature:67](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/apiProvisioningUsingAppPassword.feature#L67) - -#### [disable users /cloud/users/disable|enable not available](https://github.com/owncloud/ocis/issues/1420) -- [apiProvisioning-v1/enableUser.feature:92](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/enableUser.feature#L92) -- [apiProvisioning-v1/enableUser.feature:102](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v1/enableUser.feature#L102) -- [apiProvisioning-v1/enableUser.feature:129](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v1/enableUser.feature#L129) -- [apiProvisioning-v1/disableUser.feature:99](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v1/disableUser.feature#L99) -- [apiProvisioning-v1/disableUser.feature:129](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v1/disableUser.feature#L129) -- [apiProvisioning-v1/disableUser.feature:165](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v1/disableUser.feature#L165) -- [apiProvisioning-v1/disableUser.feature:172](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v1/disableUser.feature#L172) -- [apiProvisioning-v1/disableUser.feature:190](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v1/disableUser.feature#L190) -- [apiProvisioning-v1/disableUser.feature:203](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v1/disableUser.feature#L203) -- [apiProvisioning-v1/disableUser.feature:219](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v1/disableUser.feature#L219) -- [apiProvisioning-v2/disableUser.feature:79](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/disableUser.feature#L79) -- [apiProvisioning-v2/disableUser.feature:99](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/disableUser.feature#L99) -- [apiProvisioning-v2/disableUser.feature:108](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/disableUser.feature#L108) -- [apiProvisioning-v2/disableUser.feature:130](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/disableUser.feature#L130) -- [apiProvisioning-v2/disableUser.feature:166](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/disableUser.feature#L166) -- [apiProvisioning-v2/disableUser.feature:173](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/disableUser.feature#L173) -- [apiProvisioning-v2/disableUser.feature:191](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/disableUser.feature#L191) -- [apiProvisioning-v2/disableUser.feature:204](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/disableUser.feature#L204) -- [apiProvisioning-v2/disableUser.feature:220](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/disableUser.feature#L220) - -#### [displayname of user can be changed to empty](https://github.com/owncloud/ocis-ocs/issues/51) -- [apiProvisioning-v1/editUser.feature:47](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v1/editUser.feature#L47) -- [apiProvisioning-v2/editUser.feature:47](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/editUser.feature#L47) - #### [quota query](https://github.com/owncloud/ocis/issues/1313) _requires a [CS3 user provisioning api that can update the quota for a user](https://github.com/cs3org/cs3apis/pull/95#issuecomment-772780683)_ - [apiMain/quota.feature:9](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiMain/quota.feature#L9) Scenario: Uploading a file as owner having enough quota @@ -1978,145 +1904,6 @@ _requires a [CS3 user provisioning api that can update the quota for a user](htt - [apiWebdavProperties1/getQuota.feature:27](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiWebdavProperties1/getQuota.feature#L27) - [apiWebdavProperties1/getQuota.feature:28](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiWebdavProperties1/getQuota.feature#L28) -#### [changing user quota gives ocs status 103 / Cannot set quota](https://github.com/owncloud/product/issues/247) -_requires a [CS3 user provisioning api that can update the quota for a user](https://github.com/cs3org/cs3apis/pull/95#issuecomment-772780683)_ -- [apiProvisioning-v1/editUser.feature:56](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v1/editUser.feature#L56) -- [apiProvisioning-v1/editUser.feature:122](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v1/editUser.feature#L122) -- [apiProvisioning-v2/editUser.feature:56](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/editUser.feature#L56) -- [apiProvisioning-v2/editUser.feature:122](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/editUser.feature#L122) -- [apiProvisioning-v2/enableUser.feature:34](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/enableUser.feature#L34) -- [apiProvisioning-v2/enableUser.feature:56](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/enableUser.feature#L56) -- [apiProvisioning-v2/enableUser.feature:64](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/enableUser.feature#L64) - -#### [user can get info of other users/ cloud/users endpoints not authenticated](https://github.com/owncloud/product/issues/248) -- [apiProvisioning-v2/deleteUser.feature:54](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/deleteUser.feature#L54) -- [apiProvisioning-v1/getUser.feature:81](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v1/getUser.feature#L81) -- [apiProvisioning-v2/getUser.feature:82](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/getUser.feature#L82) -- [apiProvisioning-v2/getUsers.feature:44](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/getUsers.feature#L44) - -#### [incorrect ocs(v2) status value when getting info of user that does not exist should be 404, gives 998](https://github.com/owncloud/product/issues/250) -_ocs: api compatibility, return correct status code_ -- [apiProvisioning-v2/getUser.feature:47](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/getUser.feature#L47) - -#### [subadmin endpoints not implemented for users](https://github.com/owncloud/product/issues/289) -- [apiProvisioning-v1/addUser.feature:125](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v1/addUser.feature#L125) -- [apiProvisioning-v1/createSubAdmin.feature:48](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v1/createSubAdmin.feature#L48) -- [apiProvisioning-v1/deleteUser.feature:73](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v1/deleteUser.feature#L73) -- [apiProvisioning-v1/disableUser.feature:222](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v1/disableUser.feature#L222) -- [apiProvisioning-v1/editUser.feature:131](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v1/editUser.feature#L131) -- [apiProvisioning-v1/editUser.feature:143](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v1/editUser.feature#L143) -- [apiProvisioning-v1/getUser.feature:170](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v1/getUser.feature#L170) -- [apiProvisioning-v1/resetUserPassword.feature:113](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v1/resetUserPassword.feature#L113) - -- [apiProvisioning-v2/addUser.feature:125](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/addUser.feature#L125) -- [apiProvisioning-v2/createSubAdmin.feature:48](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/createSubAdmin.feature#L48) -- [apiProvisioning-v2/deleteUser.feature:64](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/deleteUser.feature#L64) -- [apiProvisioning-v2/deleteUser.feature:74](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/deleteUser.feature#L74) -- [apiProvisioning-v2/deleteUser.feature:88](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/deleteUser.feature#L88) -- [apiProvisioning-v2/deleteUser.feature:101](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/deleteUser.feature#L101) -- [apiProvisioning-v2/deleteUser.feature:115](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/deleteUser.feature#L115) -- [apiProvisioning-v2/disableUser.feature:48](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/disableUser.feature#L48) -- [apiProvisioning-v2/disableUser.feature:64](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/disableUser.feature#L64) -- [apiProvisioning-v2/disableUser.feature:223](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/disableUser.feature#L223) -- [apiProvisioning-v2/disableUser.feature:237](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/disableUser.feature#L237) -- [apiProvisioning-v2/editUser.feature:131](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/editUser.feature#L131) -- [apiProvisioning-v2/editUser.feature:143](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/editUser.feature#L143) -- [apiProvisioning-v2/editUser.feature:159](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/editUser.feature#L159) -- [apiProvisioning-v2/enableUser.feature:104](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/enableUser.feature#L104) -- [apiProvisioning-v2/enableUser.feature:118](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/enableUser.feature#L118) -- [apiProvisioning-v2/enableUser.feature:131](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/enableUser.feature#L131) -- [apiProvisioning-v2/enableUser.feature:146](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/enableUser.feature#L146) -- [apiProvisioning-v2/getUser.feature:145](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/getUser.feature#L145) -- [apiProvisioning-v2/getUser.feature:156](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/getUser.feature#L156) -- [apiProvisioning-v2/getUser.feature:171](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/getUser.feature#L171) -- [apiProvisioning-v2/resetUserPassword.feature:101](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/resetUserPassword.feature#L101) -- [apiProvisioning-v2/resetUserPassword.feature:113](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/resetUserPassword.feature#L113) -- [apiProvisioning-v2/resetUserPassword.feature:129](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/resetUserPassword.feature#L129) - -#### [subadmin endpoints not implemented for groups](https://github.com/owncloud/product/issues/289) -- [apiProvisioningGroups-v1/addGroup.feature:87](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioningGroups-v1/addGroup.feature#L87) -- [apiProvisioningGroups-v1/addToGroup.feature:133](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioningGroups-v1/addToGroup.feature#L133) -- [apiProvisioningGroups-v1/addToGroup.feature:146](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioningGroups-v1/addToGroup.feature#L146) -- [apiProvisioningGroups-v1/deleteGroup.feature:99](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioningGroups-v1/deleteGroup.feature#L99) -- [apiProvisioningGroups-v1/getGroup.feature:72](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioningGroups-v1/getGroup.feature#L72) -- [apiProvisioningGroups-v1/getSubAdminGroups.feature:11](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioningGroups-v1/getSubAdminGroups.feature#L11) -- [apiProvisioningGroups-v1/getSubAdminGroups.feature:24](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioningGroups-v1/getSubAdminGroups.feature#L24) -- [apiProvisioningGroups-v1/getUserGroups.feature:56](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioningGroups-v1/getUserGroups.feature#L56) -- [apiProvisioningGroups-v1/removeFromGroup.feature:110](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioningGroups-v1/removeFromGroup.feature#L110) -- [apiProvisioningGroups-v1/removeFromGroup.feature:123](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioningGroups-v1/removeFromGroup.feature#L123) - -- [apiProvisioningGroups-v2/addGroup.feature:87](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioningGroups-v2/addGroup.feature#L87) -- [apiProvisioningGroups-v2/addGroup.feature:109](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioningGroups-v2/addGroup.feature#L109) -- [apiProvisioningGroups-v2/addToGroup.feature:128](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioningGroups-v2/addToGroup.feature#L128) -- [apiProvisioningGroups-v2/addToGroup.feature:141](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioningGroups-v2/addToGroup.feature#L141) -- [apiProvisioningGroups-v2/deleteGroup.feature:101](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioningGroups-v2/deleteGroup.feature#L101) -- [apiProvisioningGroups-v2/getGroup.feature:55](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioningGroups-v2/getGroup.feature#L55) -- [apiProvisioningGroups-v2/getGroup.feature:73](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioningGroups-v2/getGroup.feature#L73) -- [apiProvisioningGroups-v2/getSubAdminGroups.feature:11](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioningGroups-v2/getSubAdminGroups.feature#L11) -- [apiProvisioningGroups-v2/getSubAdminGroups.feature:24](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioningGroups-v2/getSubAdminGroups.feature#L24) -- [apiProvisioningGroups-v2/getUserGroups.feature:56](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioningGroups-v2/getUserGroups.feature#L56) -- [apiProvisioningGroups-v2/removeFromGroup.feature:110](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioningGroups-v2/removeFromGroup.feature#L110) -- [apiProvisioningGroups-v2/removeFromGroup.feature:123](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioningGroups-v2/removeFromGroup.feature#L123) - -#### [creating existing group doesn't gives error](https://github.com/owncloud/product/issues/282) -- [apiProvisioningGroups-v1/addGroup.feature:94](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioningGroups-v1/addGroup.feature#L94) -- [apiProvisioningGroups-v2/addGroup.feature:94](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioningGroups-v2/addGroup.feature#L94) - -#### [cannot create group with '/'](https://github.com/owncloud/product/issues/285) -- [apiProvisioningGroups-v1/addToGroup.feature:75](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioningGroups-v1/addToGroup.feature#L75) -- [apiProvisioningGroups-v1/addToGroup.feature:76](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioningGroups-v1/addToGroup.feature#L76) -- [apiProvisioningGroups-v1/addToGroup.feature:77](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioningGroups-v1/addToGroup.feature#L77) -- [apiProvisioningGroups-v1/deleteGroup.feature:85](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioningGroups-v1/deleteGroup.feature#L85) -- [apiProvisioningGroups-v1/deleteGroup.feature:86](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioningGroups-v1/deleteGroup.feature#L86) -- [apiProvisioningGroups-v1/deleteGroup.feature:87](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioningGroups-v1/deleteGroup.feature#L87) -- [apiProvisioningGroups-v1/getUserGroups.feature:38](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioningGroups-v1/getUserGroups.feature#L38) -- [apiProvisioningGroups-v1/removeFromGroup.feature:76](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioningGroups-v1/removeFromGroup.feature#L76) -- [apiProvisioningGroups-v1/removeFromGroup.feature:77](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioningGroups-v1/removeFromGroup.feature#L77) -- [apiProvisioningGroups-v1/removeFromGroup.feature:78](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioningGroups-v1/removeFromGroup.feature#L78) - -- [apiProvisioningGroups-v2/addToGroup.feature:70](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioningGroups-v2/addToGroup.feature#L70) -- [apiProvisioningGroups-v2/addToGroup.feature:71](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioningGroups-v2/addToGroup.feature#L71) -- [apiProvisioningGroups-v2/addToGroup.feature:72](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioningGroups-v2/addToGroup.feature#L72) -- [apiProvisioningGroups-v2/deleteGroup.feature:85](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioningGroups-v2/deleteGroup.feature#L85) -- [apiProvisioningGroups-v2/deleteGroup.feature:86](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioningGroups-v2/deleteGroup.feature#L86) -- [apiProvisioningGroups-v2/deleteGroup.feature:87](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioningGroups-v2/deleteGroup.feature#L87) -- [apiProvisioningGroups-v2/getUserGroups.feature:38](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioningGroups-v2/getUserGroups.feature#L38) -- [apiProvisioningGroups-v2/removeFromGroup.feature:76](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioningGroups-v2/removeFromGroup.feature#L76) -- [apiProvisioningGroups-v2/removeFromGroup.feature:77](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioningGroups-v2/removeFromGroup.feature#L77) -- [apiProvisioningGroups-v2/removeFromGroup.feature:78](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioningGroups-v2/removeFromGroup.feature#L78) - -#### [adding user to non-existent group gives wrong statuscode](https://github.com/owncloud/product/issues/286) -- [apiProvisioningGroups-v1/addToGroup.feature:107](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioningGroups-v1/addToGroup.feature#L107) -- [apiProvisioningGroups-v1/removeFromGroup.feature:101](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioningGroups-v1/removeFromGroup.feature#L101) -- [apiProvisioningGroups-v2/removeFromGroup.feature:101](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioningGroups-v2/removeFromGroup.feature#L101) - -#### [adding user to empty group gives wrong statuscode](https://github.com/owncloud/product/issues/287) -- [apiProvisioningGroups-v1/addToGroup.feature:116](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioningGroups-v1/addToGroup.feature#L116) - -#### [adding non-existent user to a group gives wrong status code](https://github.com/owncloud/product/issues/288) -- [apiProvisioningGroups-v1/addToGroup.feature:124](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioningGroups-v1/addToGroup.feature#L124) - - -#### [Cannot create user with different username and emails](https://github.com/owncloud/product/issues/187) -_special character username not valid_ -- [apiProvisioningGroups-v1/getGroup.feature:11](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioningGroups-v1/getGroup.feature#L11) -- [apiProvisioningGroups-v2/getGroup.feature:11](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioningGroups-v2/getGroup.feature#L11) - -#### [normal users can list the members of the group](https://github.com/owncloud/product/issues/290) -- [apiProvisioningGroups-v1/getGroup.feature:81](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioningGroups-v1/getGroup.feature#L81) -- [apiProvisioningGroups-v1/deleteGroup.feature:90](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioningGroups-v1/deleteGroup.feature#L90) -- [apiProvisioningGroups-v2/getGroup.feature:83](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioningGroups-v2/getGroup.feature#L83) - -#### [ocs v2 invalid status code for group endpoints](https://github.com/owncloud/product/issues/291) -- [apiProvisioningGroups-v2/addGroup.feature:102](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioningGroups-v2/addGroup.feature#L102) -- [apiProvisioningGroups-v2/addToGroup.feature:94](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioningGroups-v2/addToGroup.feature#L94) -- [apiProvisioningGroups-v2/addToGroup.feature:102](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioningGroups-v2/addToGroup.feature#L102) -- [apiProvisioningGroups-v2/addToGroup.feature:119](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioningGroups-v2/addToGroup.feature#L119) -- [apiProvisioningGroups-v2/deleteGroup.feature:91](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioningGroups-v2/deleteGroup.feature#L91) -- [apiProvisioningGroups-v2/getGroup.feature:33](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioningGroups-v2/getGroup.feature#L33) -- [apiProvisioningGroups-v2/getUserGroups.feature:73](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioningGroups-v2/getUserGroups.feature#L73) -- [apiProvisioningGroups-v2/removeFromGroup.feature:138](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioningGroups-v2/removeFromGroup.feature#L138) - #### [user-sync endpoint does not exist](https://github.com/owncloud/ocis/issues/1241) - [apiMain/userSync.feature:18](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiMain/userSync.feature#L18) - [apiMain/userSync.feature:19](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiMain/userSync.feature#L19) @@ -2129,21 +1916,6 @@ _special character username not valid_ - [apiMain/userSync.feature:63](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiMain/userSync.feature#L63) - [apiMain/userSync.feature:64](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiMain/userSync.feature#L64) -- [apiProvisioning-v1/createSubAdmin.feature:11](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v1/createSubAdmin.feature#L11) -- [apiProvisioning-v1/createSubAdmin.feature:19](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v1/createSubAdmin.feature#L19) -- [apiProvisioning-v1/createSubAdmin.feature:27](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v1/createSubAdmin.feature#L27) -- [apiProvisioning-v1/createSubAdmin.feature:35](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v1/createSubAdmin.feature#L35) -- [apiProvisioning-v1/getSubAdmins.feature:11](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v1/getSubAdmins.feature#L11) -- [apiProvisioning-v1/getSubAdmins.feature:21](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v1/getSubAdmins.feature#L21) -- [apiProvisioning-v2/createSubAdmin.feature:11](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/createSubAdmin.feature#L11) -- [apiProvisioning-v2/createSubAdmin.feature:19](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/createSubAdmin.feature#L19) -- [apiProvisioning-v2/createSubAdmin.feature:27](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/createSubAdmin.feature#L27) -- [apiProvisioning-v2/createSubAdmin.feature:35](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/createSubAdmin.feature#L35) -- [apiProvisioning-v2/getSubAdmins.feature:11](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/getSubAdmins.feature#L11) -- [apiProvisioning-v2/getSubAdmins.feature:21](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/getSubAdmins.feature#L21) -- [apiProvisioning-v2/getSubAdmins.feature:30](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/getSubAdmins.feature#L30) -- [apiProvisioning-v2/getSubAdmins.feature:44](https://github.com/owncloud/core/blob/master/tests/acceptance/features/apiProvisioning-v2/getSubAdmins.feature#L44) - ### Other API, search, favorites, config, capabilities, not existing endpoints, CORS and others From 06aa61fcd366379d1f7ff4f73e71d42d33d932fd Mon Sep 17 00:00:00 2001 From: Ishank Arora Date: Fri, 26 Feb 2021 14:59:58 +0100 Subject: [PATCH 12/12] SQL driver for the publicshare service (#1495) --- .../unreleased/publicshare-sql-driver.md | 3 + pkg/cbox/loader/loader.go | 1 + pkg/cbox/publicshare/sql/sql.go | 448 ++++++++++++++++++ pkg/cbox/share/sql/sql.go | 153 +++--- pkg/cbox/{share/sql => utils}/conversions.go | 117 ++++- pkg/publicshare/manager/json/json.go | 89 ++-- pkg/storage/utils/eosfs/eosfs.go | 2 +- pkg/utils/utils.go | 11 + 8 files changed, 658 insertions(+), 166 deletions(-) create mode 100644 changelog/unreleased/publicshare-sql-driver.md create mode 100644 pkg/cbox/publicshare/sql/sql.go rename pkg/cbox/{share/sql => utils}/conversions.go (54%) diff --git a/changelog/unreleased/publicshare-sql-driver.md b/changelog/unreleased/publicshare-sql-driver.md new file mode 100644 index 0000000000..dcfeee32e1 --- /dev/null +++ b/changelog/unreleased/publicshare-sql-driver.md @@ -0,0 +1,3 @@ +Enhancement: SQL driver for the publicshare service + +https://github.com/cs3org/reva/pull/1495 diff --git a/pkg/cbox/loader/loader.go b/pkg/cbox/loader/loader.go index 8cfab6636e..a46b40c90a 100644 --- a/pkg/cbox/loader/loader.go +++ b/pkg/cbox/loader/loader.go @@ -21,6 +21,7 @@ package loader import ( // Load cbox specific drivers. _ "github.com/cs3org/reva/pkg/cbox/group/rest" + _ "github.com/cs3org/reva/pkg/cbox/publicshare/sql" _ "github.com/cs3org/reva/pkg/cbox/share/sql" _ "github.com/cs3org/reva/pkg/cbox/user/rest" ) diff --git a/pkg/cbox/publicshare/sql/sql.go b/pkg/cbox/publicshare/sql/sql.go new file mode 100644 index 0000000000..86e24b0a70 --- /dev/null +++ b/pkg/cbox/publicshare/sql/sql.go @@ -0,0 +1,448 @@ +// Copyright 2018-2021 CERN +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// In applying this license, CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +package sql + +import ( + "context" + "database/sql" + "fmt" + "os" + "os/signal" + "strconv" + "strings" + "syscall" + "time" + + "golang.org/x/crypto/bcrypt" + + user "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1" + link "github.com/cs3org/go-cs3apis/cs3/sharing/link/v1beta1" + provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" + typespb "github.com/cs3org/go-cs3apis/cs3/types/v1beta1" + conversions "github.com/cs3org/reva/pkg/cbox/utils" + "github.com/cs3org/reva/pkg/errtypes" + "github.com/cs3org/reva/pkg/publicshare" + "github.com/cs3org/reva/pkg/publicshare/manager/registry" + "github.com/cs3org/reva/pkg/utils" + "github.com/mitchellh/mapstructure" + "github.com/pkg/errors" +) + +const publicShareType = 3 + +func init() { + registry.Register("sql", New) +} + +type config struct { + SharePasswordHashCost int `mapstructure:"password_hash_cost"` + JanitorRunInterval int `mapstructure:"janitor_run_interval"` + DbUsername string `mapstructure:"db_username"` + DbPassword string `mapstructure:"db_password"` + DbHost string `mapstructure:"db_host"` + DbPort int `mapstructure:"db_port"` + DbName string `mapstructure:"db_name"` +} + +type manager struct { + c *config + db *sql.DB +} + +func (c *config) init() { + if c.SharePasswordHashCost == 0 { + c.SharePasswordHashCost = 11 + } + if c.JanitorRunInterval == 0 { + c.JanitorRunInterval = 3600 + } +} + +func (m *manager) startJanitorRun() { + ticker := time.NewTicker(time.Duration(m.c.JanitorRunInterval) * time.Second) + work := make(chan os.Signal, 1) + signal.Notify(work, syscall.SIGHUP, syscall.SIGINT, syscall.SIGQUIT) + + for { + select { + case <-work: + return + case <-ticker.C: + _ = m.cleanupExpiredShares() + } + } +} + +// New returns a new public share manager. +func New(m map[string]interface{}) (publicshare.Manager, error) { + c := &config{} + if err := mapstructure.Decode(m, c); err != nil { + return nil, err + } + c.init() + + db, err := sql.Open("mysql", fmt.Sprintf("%s:%s@tcp(%s:%d)/%s", c.DbUsername, c.DbPassword, c.DbHost, c.DbPort, c.DbName)) + if err != nil { + return nil, err + } + + mgr := manager{ + c: c, + db: db, + } + go mgr.startJanitorRun() + + return &mgr, nil +} + +func (m *manager) CreatePublicShare(ctx context.Context, u *user.User, rInfo *provider.ResourceInfo, g *link.Grant) (*link.PublicShare, error) { + + tkn := utils.RandString(15) + now := time.Now().Unix() + + displayName, ok := rInfo.ArbitraryMetadata.Metadata["name"] + if !ok { + displayName = tkn + } + createdAt := &typespb.Timestamp{ + Seconds: uint64(now), + } + + creator := conversions.FormatUserID(u.Id) + owner := conversions.FormatUserID(rInfo.Owner) + permissions := conversions.SharePermToInt(g.Permissions.Permissions) + itemType := conversions.ResourceTypeToItem(rInfo.Type) + prefix := rInfo.Id.StorageId + itemSource := rInfo.Id.OpaqueId + fileSource, err := strconv.ParseUint(itemSource, 10, 64) + if err != nil { + // it can be the case that the item source may be a character string + // we leave fileSource blank in that case + fileSource = 0 + } + + query := "insert into oc_share set share_type=?,uid_owner=?,uid_initiator=?,item_type=?,fileid_prefix=?,item_source=?,file_source=?,permissions=?,stime=?,token=?,share_name=?" + params := []interface{}{publicShareType, owner, creator, itemType, prefix, itemSource, fileSource, permissions, now, tkn, displayName} + + var passwordProtected bool + password := g.Password + if password != "" { + password, err = hashPassword(password, m.c.SharePasswordHashCost) + if err != nil { + return nil, errors.Wrap(err, "could not hash share password") + } + passwordProtected = true + + query += ",share_with=?" + params = append(params, password) + } + + if g.Expiration != nil && g.Expiration.Seconds != 0 { + t := time.Unix(int64(g.Expiration.Seconds), 0) + query += ",expiration=?" + params = append(params, t) + } + + stmt, err := m.db.Prepare(query) + if err != nil { + return nil, err + } + result, err := stmt.Exec(params...) + if err != nil { + return nil, err + } + lastID, err := result.LastInsertId() + if err != nil { + return nil, err + } + + return &link.PublicShare{ + Id: &link.PublicShareId{ + OpaqueId: strconv.FormatInt(lastID, 10), + }, + Owner: rInfo.GetOwner(), + Creator: u.Id, + ResourceId: rInfo.Id, + Token: tkn, + Permissions: g.Permissions, + Ctime: createdAt, + Mtime: createdAt, + PasswordProtected: passwordProtected, + Expiration: g.Expiration, + DisplayName: displayName, + }, nil +} + +func (m *manager) UpdatePublicShare(ctx context.Context, u *user.User, req *link.UpdatePublicShareRequest, g *link.Grant) (*link.PublicShare, error) { + query := "update oc_share set " + paramsMap := map[string]interface{}{} + params := []interface{}{} + + now := time.Now().Unix() + uid := conversions.FormatUserID(u.Id) + + switch req.GetUpdate().GetType() { + case link.UpdatePublicShareRequest_Update_TYPE_DISPLAYNAME: + paramsMap["share_name"] = req.Update.GetDisplayName() + case link.UpdatePublicShareRequest_Update_TYPE_PERMISSIONS: + paramsMap["permissions"] = conversions.SharePermToInt(req.Update.GetGrant().GetPermissions().Permissions) + case link.UpdatePublicShareRequest_Update_TYPE_EXPIRATION: + paramsMap["expiration"] = time.Unix(int64(req.Update.GetGrant().Expiration.Seconds), 0) + case link.UpdatePublicShareRequest_Update_TYPE_PASSWORD: + if req.Update.GetGrant().Password == "" { + paramsMap["share_with"] = "" + } else { + h, err := hashPassword(req.Update.GetGrant().Password, m.c.SharePasswordHashCost) + if err != nil { + return nil, errors.Wrap(err, "could not hash share password") + } + paramsMap["share_with"] = h + } + default: + return nil, fmt.Errorf("invalid update type: %v", req.GetUpdate().GetType()) + } + + for k, v := range paramsMap { + query += k + "=?" + params = append(params, v) + } + + switch { + case req.Ref.GetId() != nil: + query += ",stime=? where id=? AND (uid_owner=? or uid_initiator=?)" + params = append(params, now, req.Ref.GetId().OpaqueId, uid, uid) + case req.Ref.GetToken() != "": + query += ",stime=? where token=? AND (uid_owner=? or uid_initiator=?)" + params = append(params, now, req.Ref.GetToken(), uid, uid) + default: + return nil, errtypes.NotFound(req.Ref.String()) + } + + stmt, err := m.db.Prepare(query) + if err != nil { + return nil, err + } + if _, err = stmt.Exec(params...); err != nil { + return nil, err + } + + return m.GetPublicShare(ctx, u, req.Ref) +} + +func (m *manager) getByToken(ctx context.Context, token string, u *user.User) (*link.PublicShare, error) { + s := conversions.DBShare{Token: token} + query := "select coalesce(uid_owner, '') as uid_owner, coalesce(uid_initiator, '') as uid_initiator, coalesce(share_with, '') as share_with, coalesce(fileid_prefix, '') as fileid_prefix, coalesce(item_source, '') as item_source, coalesce(expiration, '') as expiration, coalesce(share_name, '') as share_name, id, stime, permissions FROM oc_share WHERE share_type=? AND token=?" + if err := m.db.QueryRow(query, publicShareType, token).Scan(&s.UIDOwner, &s.UIDInitiator, &s.ShareWith, &s.Prefix, &s.ItemSource, &s.Expiration, &s.ShareName, &s.ID, &s.STime, &s.Permissions); err != nil { + if err == sql.ErrNoRows { + return nil, errtypes.NotFound(token) + } + return nil, err + } + return conversions.ConvertToCS3PublicShare(s), nil +} + +func (m *manager) getByID(ctx context.Context, id *link.PublicShareId, u *user.User) (*link.PublicShare, error) { + uid := conversions.FormatUserID(u.Id) + s := conversions.DBShare{ID: id.OpaqueId} + query := "select coalesce(uid_owner, '') as uid_owner, coalesce(uid_initiator, '') as uid_initiator, coalesce(share_with, '') as share_with, coalesce(fileid_prefix, '') as fileid_prefix, coalesce(item_source, '') as item_source, coalesce(token,'') as token, coalesce(expiration, '') as expiration, coalesce(share_name, '') as share_name, stime, permissions FROM oc_share WHERE share_type=? AND id=? AND (uid_owner=? OR uid_initiator=?)" + if err := m.db.QueryRow(query, publicShareType, id.OpaqueId, uid, uid).Scan(&s.UIDOwner, &s.UIDInitiator, &s.ShareWith, &s.Prefix, &s.ItemSource, &s.Token, &s.Expiration, &s.ShareName, &s.STime, &s.Permissions); err != nil { + if err == sql.ErrNoRows { + return nil, errtypes.NotFound(id.OpaqueId) + } + return nil, err + } + return conversions.ConvertToCS3PublicShare(s), nil +} + +func (m *manager) GetPublicShare(ctx context.Context, u *user.User, ref *link.PublicShareReference) (*link.PublicShare, error) { + var s *link.PublicShare + var err error + switch { + case ref.GetId() != nil: + s, err = m.getByID(ctx, ref.GetId(), u) + case ref.GetToken() != "": + s, err = m.getByToken(ctx, ref.GetToken(), u) + default: + err = errtypes.NotFound(ref.String()) + } + if err != nil { + return nil, err + } + + if expired(s) { + if err := m.cleanupExpiredShares(); err != nil { + return nil, err + } + return nil, errtypes.NotFound(ref.String()) + } + + return s, nil +} + +func (m *manager) ListPublicShares(ctx context.Context, u *user.User, filters []*link.ListPublicSharesRequest_Filter, md *provider.ResourceInfo) ([]*link.PublicShare, error) { + uid := conversions.FormatUserID(u.Id) + query := "select coalesce(uid_owner, '') as uid_owner, coalesce(uid_initiator, '') as uid_initiator, coalesce(share_with, '') as share_with, coalesce(fileid_prefix, '') as fileid_prefix, coalesce(item_source, '') as item_source, coalesce(token,'') as token, coalesce(expiration, '') as expiration, coalesce(share_name, '') as share_name, id, stime, permissions FROM oc_share WHERE (uid_owner=? or uid_initiator=?) AND (share_type=?)" + var filterQuery string + params := []interface{}{uid, uid, publicShareType} + + for i, f := range filters { + switch f.Type { + case link.ListPublicSharesRequest_Filter_TYPE_RESOURCE_ID: + filterQuery += "(fileid_prefix=? AND item_source=?)" + if i != len(filters)-1 { + filterQuery += " AND " + } + params = append(params, f.GetResourceId().StorageId, f.GetResourceId().OpaqueId) + case link.ListPublicSharesRequest_Filter_TYPE_OWNER: + filterQuery += "(uid_owner=?)" + if i != len(filters)-1 { + filterQuery += " AND " + } + params = append(params, conversions.FormatUserID(f.GetOwner())) + case link.ListPublicSharesRequest_Filter_TYPE_CREATOR: + filterQuery += "(uid_initiator=?)" + if i != len(filters)-1 { + filterQuery += " AND " + } + params = append(params, conversions.FormatUserID(f.GetCreator())) + } + } + if filterQuery != "" { + query = fmt.Sprintf("%s AND (%s)", query, filterQuery) + } + + rows, err := m.db.Query(query, params...) + if err != nil { + return nil, err + } + defer rows.Close() + + var s conversions.DBShare + shares := []*link.PublicShare{} + for rows.Next() { + if err := rows.Scan(&s.UIDOwner, &s.UIDInitiator, &s.ShareWith, &s.Prefix, &s.ItemSource, &s.Token, &s.Expiration, &s.ShareName, &s.ID, &s.STime, &s.Permissions); err != nil { + continue + } + cs3Share := conversions.ConvertToCS3PublicShare(s) + if expired(cs3Share) { + _ = m.cleanupExpiredShares() + } else { + shares = append(shares, cs3Share) + } + } + if err = rows.Err(); err != nil { + return nil, err + } + + return shares, nil +} + +func (m *manager) RevokePublicShare(ctx context.Context, u *user.User, ref *link.PublicShareReference) error { + uid := conversions.FormatUserID(u.Id) + query := "delete from oc_share where " + params := []interface{}{} + + switch { + case ref.GetId() != nil && ref.GetId().OpaqueId != "": + query += "id=? AND (uid_owner=? or uid_initiator=?)" + params = append(params, ref.GetId().OpaqueId, uid, uid) + case ref.GetToken() != "": + query += "token=? AND (uid_owner=? or uid_initiator=?)" + params = append(params, ref.GetToken(), uid, uid) + default: + return errtypes.NotFound(ref.String()) + } + + stmt, err := m.db.Prepare(query) + if err != nil { + return err + } + res, err := stmt.Exec(params...) + if err != nil { + return err + } + + rowCnt, err := res.RowsAffected() + if err != nil { + return err + } + if rowCnt == 0 { + return errtypes.NotFound(ref.String()) + } + return nil +} + +func (m *manager) GetPublicShareByToken(ctx context.Context, token, password string) (*link.PublicShare, error) { + s := conversions.DBShare{Token: token} + query := "select coalesce(uid_owner, '') as uid_owner, coalesce(uid_initiator, '') as uid_initiator, coalesce(share_with, '') as share_with, coalesce(fileid_prefix, '') as fileid_prefix, coalesce(item_source, '') as item_source, coalesce(expiration, '') as expiration, coalesce(share_name, '') as share_name, id, stime, permissions FROM oc_share WHERE share_type=? AND token=?" + if err := m.db.QueryRow(query, publicShareType, token).Scan(&s.UIDOwner, &s.UIDInitiator, &s.ShareWith, &s.Prefix, &s.ItemSource, &s.Expiration, &s.ShareName, &s.ID, &s.STime, &s.Permissions); err != nil { + if err == sql.ErrNoRows { + return nil, errtypes.NotFound(token) + } + return nil, err + } + if s.ShareWith != "" { + if check := checkPasswordHash(password, s.ShareWith); !check { + return nil, errtypes.InvalidCredentials(token) + } + } + + cs3Share := conversions.ConvertToCS3PublicShare(s) + if expired(cs3Share) { + if err := m.cleanupExpiredShares(); err != nil { + return nil, err + } + return nil, errtypes.NotFound(token) + } + + return cs3Share, nil +} + +func (m *manager) cleanupExpiredShares() error { + query := "delete from oc_share where expiration IS NOT NULL AND expiration < ?" + params := []interface{}{time.Now().Format("2006-01-02 03:04:05")} + + stmt, err := m.db.Prepare(query) + if err != nil { + return err + } + if _, err = stmt.Exec(params...); err != nil { + return err + } + return nil +} + +func expired(s *link.PublicShare) bool { + if s.Expiration != nil { + if t := time.Unix(int64(s.Expiration.GetSeconds()), int64(s.Expiration.GetNanos())); t.Before(time.Now()) { + return true + } + } + return false +} + +func hashPassword(password string, cost int) (string, error) { + bytes, err := bcrypt.GenerateFromPassword([]byte(password), cost) + return "1|" + string(bytes), err +} + +func checkPasswordHash(password, hash string) bool { + err := bcrypt.CompareHashAndPassword([]byte(strings.TrimPrefix(hash, "1|")), []byte(password)) + return err == nil +} diff --git a/pkg/cbox/share/sql/sql.go b/pkg/cbox/share/sql/sql.go index 62b2fbf2c1..db86988f21 100644 --- a/pkg/cbox/share/sql/sql.go +++ b/pkg/cbox/share/sql/sql.go @@ -30,6 +30,7 @@ import ( collaboration "github.com/cs3org/go-cs3apis/cs3/sharing/collaboration/v1beta1" provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" typespb "github.com/cs3org/go-cs3apis/cs3/types/v1beta1" + conversions "github.com/cs3org/reva/pkg/cbox/utils" "github.com/cs3org/reva/pkg/errtypes" "github.com/cs3org/reva/pkg/share" "github.com/cs3org/reva/pkg/share/manager/registry" @@ -59,21 +60,7 @@ type mgr struct { db *sql.DB } -type dbShare struct { - ID string - UIDOwner string - UIDInitiator string - Prefix string - ItemSource string - ShareWith string - Permissions int - ShareType int - STime int - FileTarget string - State int -} - -// New returns a new mgr. +// New returns a new share manager. func New(m map[string]interface{}) (share.Manager, error) { c, err := parseConfig(m) if err != nil { @@ -128,10 +115,10 @@ func (m *mgr) Share(ctx context.Context, md *provider.ResourceInfo, g *collabora Seconds: uint64(now), } - shareType, shareWith := formatGrantee(g.Grantee) - itemType := resourceTypeToItem(md.Type) + shareType, shareWith := conversions.FormatGrantee(g.Grantee) + itemType := conversions.ResourceTypeToItem(md.Type) targetPath := path.Join("/", path.Base(md.Path)) - permissions := sharePermToInt(g.Permissions.Permissions) + permissions := conversions.SharePermToInt(g.Permissions.Permissions) prefix := md.Id.StorageId itemSource := md.Id.OpaqueId fileSource, err := strconv.ParseUint(itemSource, 10, 64) @@ -142,7 +129,7 @@ func (m *mgr) Share(ctx context.Context, md *provider.ResourceInfo, g *collabora } stmtString := "insert into oc_share set share_type=?,uid_owner=?,uid_initiator=?,item_type=?,fileid_prefix=?,item_source=?,file_source=?,permissions=?,stime=?,share_with=?,file_target=?" - stmtValues := []interface{}{shareType, formatUserID(md.Owner), formatUserID(user.Id), itemType, prefix, itemSource, fileSource, permissions, now, shareWith, targetPath} + stmtValues := []interface{}{shareType, conversions.FormatUserID(md.Owner), conversions.FormatUserID(user.Id), itemType, prefix, itemSource, fileSource, permissions, now, shareWith, targetPath} stmt, err := m.db.Prepare(stmtString) if err != nil { @@ -172,28 +159,32 @@ func (m *mgr) Share(ctx context.Context, md *provider.ResourceInfo, g *collabora } func (m *mgr) getByID(ctx context.Context, id *collaboration.ShareId) (*collaboration.Share, error) { - s := dbShare{ID: id.OpaqueId} - query := "select coalesce(uid_owner, '') as uid_owner, coalesce(uid_initiator, '') as uid_initiator, coalesce(share_with, '') as share_with, coalesce(fileid_prefix, '') as fileid_prefix, coalesce(item_source, '') as item_source, stime, permissions, share_type FROM oc_share WHERE (orphan = 0 or orphan IS NULL) AND id=?" - if err := m.db.QueryRow(query, id.OpaqueId).Scan(&s.UIDOwner, &s.UIDInitiator, &s.ShareWith, &s.Prefix, &s.ItemSource, &s.STime, &s.Permissions, &s.ShareType); err != nil { + uid := conversions.FormatUserID(user.ContextMustGetUser(ctx).Id) + s := conversions.DBShare{ID: id.OpaqueId} + query := "select coalesce(uid_owner, '') as uid_owner, coalesce(uid_initiator, '') as uid_initiator, coalesce(share_with, '') as share_with, coalesce(fileid_prefix, '') as fileid_prefix, coalesce(item_source, '') as item_source, stime, permissions, share_type FROM oc_share WHERE (orphan = 0 or orphan IS NULL) AND id=? AND (uid_owner=? or uid_initiator=?)" + if err := m.db.QueryRow(query, id.OpaqueId, uid, uid).Scan(&s.UIDOwner, &s.UIDInitiator, &s.ShareWith, &s.Prefix, &s.ItemSource, &s.STime, &s.Permissions, &s.ShareType); err != nil { if err == sql.ErrNoRows { return nil, errtypes.NotFound(id.OpaqueId) } return nil, err } - return convertToCS3Share(s), nil + return conversions.ConvertToCS3Share(s), nil } func (m *mgr) getByKey(ctx context.Context, key *collaboration.ShareKey) (*collaboration.Share, error) { - s := dbShare{} - shareType, shareWith := formatGrantee(key.Grantee) - query := "select coalesce(uid_owner, '') as uid_owner, coalesce(uid_initiator, '') as uid_initiator, coalesce(share_with, '') as share_with, coalesce(fileid_prefix, '') as fileid_prefix, coalesce(item_source, '') as item_source, id, stime, permissions, share_type FROM oc_share WHERE (orphan = 0 or orphan IS NULL) AND (uid_owner=? or uid_initiator=?) AND fileid_prefix=? AND item_source=? AND share_type=? AND share_with=?" - if err := m.db.QueryRow(query, formatUserID(key.Owner), formatUserID(key.Owner), key.ResourceId.StorageId, key.ResourceId.OpaqueId, shareType, shareWith).Scan(&s.UIDOwner, &s.UIDInitiator, &s.ShareWith, &s.Prefix, &s.ItemSource, &s.ID, &s.STime, &s.Permissions, &s.ShareType); err != nil { + owner := conversions.FormatUserID(key.Owner) + uid := conversions.FormatUserID(user.ContextMustGetUser(ctx).Id) + + s := conversions.DBShare{} + shareType, shareWith := conversions.FormatGrantee(key.Grantee) + query := "select coalesce(uid_owner, '') as uid_owner, coalesce(uid_initiator, '') as uid_initiator, coalesce(share_with, '') as share_with, coalesce(fileid_prefix, '') as fileid_prefix, coalesce(item_source, '') as item_source, id, stime, permissions, share_type FROM oc_share WHERE (orphan = 0 or orphan IS NULL) AND uid_owner=? AND fileid_prefix=? AND item_source=? AND share_type=? AND share_with=? AND (uid_owner=? or uid_initiator=?)" + if err := m.db.QueryRow(query, owner, key.ResourceId.StorageId, key.ResourceId.OpaqueId, shareType, shareWith, uid, uid).Scan(&s.UIDOwner, &s.UIDInitiator, &s.ShareWith, &s.Prefix, &s.ItemSource, &s.ID, &s.STime, &s.Permissions, &s.ShareType); err != nil { if err == sql.ErrNoRows { return nil, errtypes.NotFound(key.String()) } return nil, err } - return convertToCS3Share(s), nil + return conversions.ConvertToCS3Share(s), nil } func (m *mgr) GetShare(ctx context.Context, ref *collaboration.ShareReference) (*collaboration.Share, error) { @@ -212,33 +203,23 @@ func (m *mgr) GetShare(ctx context.Context, ref *collaboration.ShareReference) ( return nil, err } - // check if we are the owner - user := user.ContextMustGetUser(ctx) - if utils.UserEqual(user.Id, s.Owner) || utils.UserEqual(user.Id, s.Creator) { - return s, nil - } - - // we return not found to not disclose information - return nil, errtypes.NotFound(ref.String()) + return s, nil } func (m *mgr) Unshare(ctx context.Context, ref *collaboration.ShareReference) error { - user := user.ContextMustGetUser(ctx) - + uid := conversions.FormatUserID(user.ContextMustGetUser(ctx).Id) var query string params := []interface{}{} switch { case ref.GetId() != nil: query = "delete from oc_share where id=? AND (uid_owner=? or uid_initiator=?)" - params = append(params, ref.GetId().OpaqueId, formatUserID(user.Id), formatUserID(user.Id)) + params = append(params, ref.GetId().OpaqueId, uid, uid) case ref.GetKey() != nil: key := ref.GetKey() - if key.Owner != user.Id { - return errtypes.NotFound(ref.String()) - } - shareType, shareWith := formatGrantee(key.Grantee) - query = "delete from oc_share where (uid_owner=? or uid_initiator=?) AND fileid_prefix=? AND item_source=? AND share_type=? AND share_with=?" - params = append(params, formatUserID(key.Owner), formatUserID(key.Owner), key.ResourceId.StorageId, key.ResourceId.OpaqueId, shareType, shareWith) + shareType, shareWith := conversions.FormatGrantee(key.Grantee) + owner := conversions.FormatUserID(key.Owner) + query = "delete from oc_share where uid_owner=? AND fileid_prefix=? AND item_source=? AND share_type=? AND share_with=? AND (uid_owner=? or uid_initiator=?)" + params = append(params, owner, key.ResourceId.StorageId, key.ResourceId.OpaqueId, shareType, shareWith, uid, uid) default: return errtypes.NotFound(ref.String()) } @@ -263,23 +244,21 @@ func (m *mgr) Unshare(ctx context.Context, ref *collaboration.ShareReference) er } func (m *mgr) UpdateShare(ctx context.Context, ref *collaboration.ShareReference, p *collaboration.SharePermissions) (*collaboration.Share, error) { - user := user.ContextMustGetUser(ctx) - permissions := sharePermToInt(p.Permissions) + permissions := conversions.SharePermToInt(p.Permissions) + uid := conversions.FormatUserID(user.ContextMustGetUser(ctx).Id) var query string params := []interface{}{} switch { case ref.GetId() != nil: query = "update oc_share set permissions=?,stime=? where id=? AND (uid_owner=? or uid_initiator=?)" - params = append(params, permissions, time.Now().Unix(), ref.GetId().OpaqueId, formatUserID(user.Id), formatUserID(user.Id)) + params = append(params, permissions, time.Now().Unix(), ref.GetId().OpaqueId, uid, uid) case ref.GetKey() != nil: key := ref.GetKey() - if key.Owner != user.Id { - return nil, errtypes.NotFound(ref.String()) - } - shareType, shareWith := formatGrantee(key.Grantee) - query = "update oc_share set permissions=?,stime=? where (uid_owner=? or uid_initiator=?) AND fileid_prefix=? AND item_source=? AND share_type=? AND share_with=?" - params = append(params, permissions, time.Now().Unix(), formatUserID(key.Owner), formatUserID(key.Owner), key.ResourceId.StorageId, key.ResourceId.OpaqueId, shareType, shareWith) + shareType, shareWith := conversions.FormatGrantee(key.Grantee) + owner := conversions.FormatUserID(key.Owner) + query = "update oc_share set permissions=?,stime=? where (uid_owner=? or uid_initiator=?) AND fileid_prefix=? AND item_source=? AND share_type=? AND share_with=? AND (uid_owner=? or uid_initiator=?)" + params = append(params, permissions, time.Now().Unix(), owner, owner, key.ResourceId.StorageId, key.ResourceId.OpaqueId, shareType, shareWith, uid, uid) default: return nil, errtypes.NotFound(ref.String()) } @@ -296,14 +275,15 @@ func (m *mgr) UpdateShare(ctx context.Context, ref *collaboration.ShareReference } func (m *mgr) ListShares(ctx context.Context, filters []*collaboration.ListSharesRequest_Filter) ([]*collaboration.Share, error) { + uid := conversions.FormatUserID(user.ContextMustGetUser(ctx).Id) query := "select coalesce(uid_owner, '') as uid_owner, coalesce(uid_initiator, '') as uid_initiator, coalesce(share_with, '') as share_with, coalesce(fileid_prefix, '') as fileid_prefix, coalesce(item_source, '') as item_source, id, stime, permissions, share_type FROM oc_share WHERE (orphan = 0 or orphan IS NULL) AND (uid_owner=? or uid_initiator=?) AND (share_type=? OR share_type=?)" var filterQuery string - params := []interface{}{formatUserID(user.ContextMustGetUser(ctx).Id), formatUserID(user.ContextMustGetUser(ctx).Id), 0, 1} + params := []interface{}{uid, uid, 0, 1} for i, f := range filters { if f.Type == collaboration.ListSharesRequest_Filter_TYPE_RESOURCE_ID { filterQuery += "(fileid_prefix=? AND item_source=?)" if i != len(filters)-1 { - filterQuery += " OR " + filterQuery += " AND " } params = append(params, f.GetResourceId().StorageId, f.GetResourceId().OpaqueId) } @@ -318,13 +298,13 @@ func (m *mgr) ListShares(ctx context.Context, filters []*collaboration.ListShare } defer rows.Close() - var s dbShare + var s conversions.DBShare shares := []*collaboration.Share{} for rows.Next() { if err := rows.Scan(&s.UIDOwner, &s.UIDInitiator, &s.ShareWith, &s.Prefix, &s.ItemSource, &s.ID, &s.STime, &s.Permissions, &s.ShareType); err != nil { continue } - shares = append(shares, convertToCS3Share(s)) + shares = append(shares, conversions.ConvertToCS3Share(s)) } if err = rows.Err(); err != nil { return nil, err @@ -336,8 +316,9 @@ func (m *mgr) ListShares(ctx context.Context, filters []*collaboration.ListShare // we list the shares that are targeted to the user in context or to the user groups. func (m *mgr) ListReceivedShares(ctx context.Context) ([]*collaboration.ReceivedShare, error) { user := user.ContextMustGetUser(ctx) + uid := conversions.FormatUserID(user.Id) - params := []interface{}{formatUserID(user.Id), formatUserID(user.Id)} + params := []interface{}{uid, uid} for _, v := range user.Groups { params = append(params, v) } @@ -355,13 +336,13 @@ func (m *mgr) ListReceivedShares(ctx context.Context) ([]*collaboration.Received } defer rows.Close() - var s dbShare + var s conversions.DBShare shares := []*collaboration.ReceivedShare{} for rows.Next() { if err := rows.Scan(&s.UIDOwner, &s.UIDInitiator, &s.ShareWith, &s.Prefix, &s.ItemSource, &s.ID, &s.STime, &s.Permissions, &s.ShareType, &s.State); err != nil { continue } - shares = append(shares, convertToCS3ReceivedShare(s)) + shares = append(shares, conversions.ConvertToCS3ReceivedShare(s)) } if err = rows.Err(); err != nil { return nil, err @@ -372,13 +353,14 @@ func (m *mgr) ListReceivedShares(ctx context.Context) ([]*collaboration.Received func (m *mgr) getReceivedByID(ctx context.Context, id *collaboration.ShareId) (*collaboration.ReceivedShare, error) { user := user.ContextMustGetUser(ctx) + uid := conversions.FormatUserID(user.Id) - params := []interface{}{id.OpaqueId, formatUserID(user.Id), formatUserID(user.Id)} + params := []interface{}{id.OpaqueId, uid, uid} for _, v := range user.Groups { params = append(params, v) } - s := dbShare{ID: id.OpaqueId} + s := conversions.DBShare{ID: id.OpaqueId} query := "select coalesce(uid_owner, '') as uid_owner, coalesce(uid_initiator, '') as uid_initiator, coalesce(share_with, '') as share_with, coalesce(fileid_prefix, '') as fileid_prefix, coalesce(item_source, '') as item_source, stime, permissions, share_type, accepted FROM oc_share WHERE (orphan = 0 or orphan IS NULL) AND id=? AND id not in (SELECT distinct(id) FROM oc_share_acl WHERE rejected_by=?)" if len(user.Groups) > 0 { query += "AND (share_with=? OR share_with in (?" + strings.Repeat(",?", len(user.Groups)-1) + "))" @@ -391,21 +373,34 @@ func (m *mgr) getReceivedByID(ctx context.Context, id *collaboration.ShareId) (* } return nil, err } - return convertToCS3ReceivedShare(s), nil + return conversions.ConvertToCS3ReceivedShare(s), nil } func (m *mgr) getReceivedByKey(ctx context.Context, key *collaboration.ShareKey) (*collaboration.ReceivedShare, error) { - s := dbShare{} - query := "select coalesce(uid_owner, '') as uid_owner, coalesce(uid_initiator, '') as uid_initiator, coalesce(share_with, '') as share_with, coalesce(fileid_prefix, '') as fileid_prefix, coalesce(item_source, '') as item_source, id, stime, permissions, share_type, accepted FROM oc_share WHERE (orphan = 0 or orphan IS NULL) AND (uid_owner=? or uid_initiator=?) AND fileid_prefix=? AND item_source=? AND share_type=? AND share_with=? AND id not in (SELECT distinct(id) FROM oc_share_acl WHERE rejected_by=?)" - shareType, shareWith := formatGrantee(key.Grantee) - if err := m.db.QueryRow(query, formatUserID(key.Owner), formatUserID(key.Owner), key.ResourceId.StorageId, key.ResourceId.OpaqueId, shareType, shareWith, shareWith).Scan(&s.UIDOwner, &s.UIDInitiator, &s.ShareWith, &s.Prefix, &s.ItemSource, &s.ID, &s.STime, &s.Permissions, &s.ShareType, &s.State); err != nil { + user := user.ContextMustGetUser(ctx) + uid := conversions.FormatUserID(user.Id) + + shareType, shareWith := conversions.FormatGrantee(key.Grantee) + params := []interface{}{conversions.FormatUserID(key.Owner), key.ResourceId.StorageId, key.ResourceId.OpaqueId, shareType, shareWith, shareWith, uid} + for _, v := range user.Groups { + params = append(params, v) + } + + s := conversions.DBShare{} + query := "select coalesce(uid_owner, '') as uid_owner, coalesce(uid_initiator, '') as uid_initiator, coalesce(share_with, '') as share_with, coalesce(fileid_prefix, '') as fileid_prefix, coalesce(item_source, '') as item_source, id, stime, permissions, share_type, accepted FROM oc_share WHERE (orphan = 0 or orphan IS NULL) AND uid_owner=? AND fileid_prefix=? AND item_source=? AND share_type=? AND share_with=? AND id not in (SELECT distinct(id) FROM oc_share_acl WHERE rejected_by=?)" + if len(user.Groups) > 0 { + query += "AND (share_with=? OR share_with in (?" + strings.Repeat(",?", len(user.Groups)-1) + "))" + } else { + query += "AND (share_with=?)" + } + + if err := m.db.QueryRow(query, params...).Scan(&s.UIDOwner, &s.UIDInitiator, &s.ShareWith, &s.Prefix, &s.ItemSource, &s.ID, &s.STime, &s.Permissions, &s.ShareType, &s.State); err != nil { if err == sql.ErrNoRows { return nil, errtypes.NotFound(key.String()) } return nil, err } - - return convertToCS3ReceivedShare(s), nil + return conversions.ConvertToCS3ReceivedShare(s), nil } func (m *mgr) GetReceivedShare(ctx context.Context, ref *collaboration.ShareReference) (*collaboration.ReceivedShare, error) { @@ -424,21 +419,7 @@ func (m *mgr) GetReceivedShare(ctx context.Context, ref *collaboration.ShareRefe return nil, err } - user := user.ContextMustGetUser(ctx) - if s.Share.Grantee.Type == provider.GranteeType_GRANTEE_TYPE_USER && utils.UserEqual(user.Id, s.Share.Grantee.GetUserId()) { - return s, nil - } - - if s.Share.Grantee.Type == provider.GranteeType_GRANTEE_TYPE_GROUP { - for _, v := range user.Groups { - if s.Share.Grantee.GetGroupId().OpaqueId == v { - return s, nil - } - } - } - - // we return not found to not disclose information - return nil, errtypes.NotFound(ref.String()) + return s, nil } @@ -455,7 +436,7 @@ func (m *mgr) UpdateReceivedShare(ctx context.Context, ref *collaboration.ShareR switch f.GetState() { case collaboration.ShareState_SHARE_STATE_REJECTED: query = "insert into oc_share_acl(id, rejected_by) values(?, ?)" - params = append(params, formatUserID(user.Id)) + params = append(params, conversions.FormatUserID(user.Id)) case collaboration.ShareState_SHARE_STATE_ACCEPTED: query = "update oc_share set accepted=1 where id=?" } diff --git a/pkg/cbox/share/sql/conversions.go b/pkg/cbox/utils/conversions.go similarity index 54% rename from pkg/cbox/share/sql/conversions.go rename to pkg/cbox/utils/conversions.go index 5e24fdea75..38d5424911 100644 --- a/pkg/cbox/share/sql/conversions.go +++ b/pkg/cbox/utils/conversions.go @@ -16,51 +16,74 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -package sql +package utils import ( "fmt" "strings" + "time" grouppb "github.com/cs3org/go-cs3apis/cs3/identity/group/v1beta1" userpb "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1" collaboration "github.com/cs3org/go-cs3apis/cs3/sharing/collaboration/v1beta1" + link "github.com/cs3org/go-cs3apis/cs3/sharing/link/v1beta1" provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1" typespb "github.com/cs3org/go-cs3apis/cs3/types/v1beta1" ) -func formatGrantee(g *provider.Grantee) (int, string) { +// DBShare stores information about user and public shares. +type DBShare struct { + ID string + UIDOwner string + UIDInitiator string + Prefix string + ItemSource string + ShareWith string + Token string + Expiration string + Permissions int + ShareType int + ShareName string + STime int + FileTarget string + State int +} + +// FormatGrantee formats a CS3API grantee to a string +func FormatGrantee(g *provider.Grantee) (int, string) { var granteeType int var formattedID string switch g.Type { case provider.GranteeType_GRANTEE_TYPE_USER: granteeType = 0 - formattedID = formatUserID(g.GetUserId()) + formattedID = FormatUserID(g.GetUserId()) case provider.GranteeType_GRANTEE_TYPE_GROUP: granteeType = 1 - formattedID = formatGroupID(g.GetGroupId()) + formattedID = FormatGroupID(g.GetGroupId()) default: granteeType = -1 } return granteeType, formattedID } -func extractGrantee(t int, g string) *provider.Grantee { - var grantee *provider.Grantee +// ExtractGrantee retrieves the CS3API grantee from a formatted string +func ExtractGrantee(t int, g string) *provider.Grantee { + var grantee provider.Grantee switch t { case 0: grantee.Type = provider.GranteeType_GRANTEE_TYPE_USER - grantee.Id = &provider.Grantee_UserId{UserId: extractUserID(g)} + grantee.Id = &provider.Grantee_UserId{UserId: ExtractUserID(g)} case 1: grantee.Type = provider.GranteeType_GRANTEE_TYPE_GROUP - grantee.Id = &provider.Grantee_GroupId{GroupId: extractGroupID(g)} + grantee.Id = &provider.Grantee_GroupId{GroupId: ExtractGroupID(g)} default: grantee.Type = provider.GranteeType_GRANTEE_TYPE_INVALID } - return grantee + return &grantee } -func resourceTypeToItem(r provider.ResourceType) string { +// ResourceTypeToItem maps a resource type to an integer +func ResourceTypeToItem(r provider.ResourceType) string { switch r { case provider.ResourceType_RESOURCE_TYPE_FILE: return "file" @@ -75,7 +98,8 @@ func resourceTypeToItem(r provider.ResourceType) string { } } -func sharePermToInt(p *provider.ResourcePermissions) int { +// SharePermToInt maps read/write permissions to an integer +func SharePermToInt(p *provider.ResourcePermissions) int { var perm int if p.CreateContainer { perm = 15 @@ -85,7 +109,8 @@ func sharePermToInt(p *provider.ResourcePermissions) int { return perm } -func intTosharePerm(p int) *provider.ResourcePermissions { +// IntTosharePerm retrieves read/write permissions from an integer +func IntTosharePerm(p int) *provider.ResourcePermissions { switch p { case 1: return &provider.ResourcePermissions{ @@ -122,7 +147,8 @@ func intTosharePerm(p int) *provider.ResourcePermissions { } } -func intToShareState(g int) collaboration.ShareState { +// IntToShareState retrieves the received share state from an integer +func IntToShareState(g int) collaboration.ShareState { switch g { case 0: return collaboration.ShareState_SHARE_STATE_PENDING @@ -133,14 +159,16 @@ func intToShareState(g int) collaboration.ShareState { } } -func formatUserID(u *userpb.UserId) string { +// FormatUserID formats a CS3API user ID to a string +func FormatUserID(u *userpb.UserId) string { if u.Idp != "" { return fmt.Sprintf("%s:%s", u.OpaqueId, u.Idp) } return u.OpaqueId } -func extractUserID(u string) *userpb.UserId { +// ExtractUserID retrieves a CS3API user ID from a string +func ExtractUserID(u string) *userpb.UserId { parts := strings.Split(u, ":") if len(parts) > 1 { return &userpb.UserId{OpaqueId: parts[0], Idp: parts[1]} @@ -148,14 +176,16 @@ func extractUserID(u string) *userpb.UserId { return &userpb.UserId{OpaqueId: parts[0]} } -func formatGroupID(u *grouppb.GroupId) string { +// FormatGroupID formats a CS3API group ID to a string +func FormatGroupID(u *grouppb.GroupId) string { if u.Idp != "" { return fmt.Sprintf("%s:%s", u.OpaqueId, u.Idp) } return u.OpaqueId } -func extractGroupID(u string) *grouppb.GroupId { +// ExtractGroupID retrieves a CS3API group ID from a string +func ExtractGroupID(u string) *grouppb.GroupId { parts := strings.Split(u, ":") if len(parts) > 1 { return &grouppb.GroupId{OpaqueId: parts[0], Idp: parts[1]} @@ -163,7 +193,8 @@ func extractGroupID(u string) *grouppb.GroupId { return &grouppb.GroupId{OpaqueId: parts[0]} } -func convertToCS3Share(s dbShare) *collaboration.Share { +// ConvertToCS3Share converts a DBShare to a CS3API collaboration share +func ConvertToCS3Share(s DBShare) *collaboration.Share { ts := &typespb.Timestamp{ Seconds: uint64(s.STime), } @@ -172,19 +203,55 @@ func convertToCS3Share(s dbShare) *collaboration.Share { OpaqueId: s.ID, }, ResourceId: &provider.ResourceId{OpaqueId: s.ItemSource, StorageId: s.Prefix}, - Permissions: &collaboration.SharePermissions{Permissions: intTosharePerm(s.Permissions)}, - Grantee: extractGrantee(s.ShareType, s.ShareWith), - Owner: extractUserID(s.UIDOwner), - Creator: extractUserID(s.UIDInitiator), + Permissions: &collaboration.SharePermissions{Permissions: IntTosharePerm(s.Permissions)}, + Grantee: ExtractGrantee(s.ShareType, s.ShareWith), + Owner: ExtractUserID(s.UIDOwner), + Creator: ExtractUserID(s.UIDInitiator), Ctime: ts, Mtime: ts, } } -func convertToCS3ReceivedShare(s dbShare) *collaboration.ReceivedShare { - share := convertToCS3Share(s) +// ConvertToCS3ReceivedShare converts a DBShare to a CS3API collaboration received share +func ConvertToCS3ReceivedShare(s DBShare) *collaboration.ReceivedShare { + share := ConvertToCS3Share(s) return &collaboration.ReceivedShare{ Share: share, - State: intToShareState(s.State), + State: IntToShareState(s.State), + } +} + +// ConvertToCS3PublicShare converts a DBShare to a CS3API public share +func ConvertToCS3PublicShare(s DBShare) *link.PublicShare { + ts := &typespb.Timestamp{ + Seconds: uint64(s.STime), + } + pwd := false + if s.ShareWith != "" { + pwd = true + } + var expires *typespb.Timestamp + if s.Expiration != "" { + t, err := time.Parse("2006-01-02 03:04:05", s.Expiration) + if err == nil { + expires = &typespb.Timestamp{ + Seconds: uint64(t.Unix()), + } + } + } + return &link.PublicShare{ + Id: &link.PublicShareId{ + OpaqueId: s.ID, + }, + ResourceId: &provider.ResourceId{OpaqueId: s.ItemSource, StorageId: s.Prefix}, + Permissions: &link.PublicSharePermissions{Permissions: IntTosharePerm(s.Permissions)}, + Owner: ExtractUserID(s.UIDOwner), + Creator: ExtractUserID(s.UIDInitiator), + Token: s.Token, + DisplayName: s.ShareName, + PasswordProtected: pwd, + Expiration: expires, + Ctime: ts, + Mtime: ts, } } diff --git a/pkg/publicshare/manager/json/json.go b/pkg/publicshare/manager/json/json.go index 052840ac34..daff07bad3 100644 --- a/pkg/publicshare/manager/json/json.go +++ b/pkg/publicshare/manager/json/json.go @@ -16,7 +16,7 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -package filesystem +package json import ( "bytes" @@ -24,7 +24,6 @@ import ( "encoding/json" "fmt" "io/ioutil" - "math/rand" "os" "os/signal" "path/filepath" @@ -43,36 +42,13 @@ import ( "github.com/cs3org/reva/pkg/errtypes" "github.com/cs3org/reva/pkg/publicshare" "github.com/cs3org/reva/pkg/publicshare/manager/registry" + "github.com/cs3org/reva/pkg/utils" "github.com/golang/protobuf/jsonpb" "github.com/mitchellh/mapstructure" "github.com/pkg/errors" "go.opencensus.io/trace" ) -type janitor struct { - m *manager - interval time.Duration -} - -func (j *janitor) run() { - ticker := time.NewTicker(j.interval) - work := make(chan os.Signal, 1) - signal.Notify(work, syscall.SIGHUP, syscall.SIGINT, syscall.SIGQUIT) - - for { - select { - case <-work: - return - case <-ticker.C: - j.m.cleanupExpiredShares() - } - } -} - -var j = janitor{ - interval: time.Minute, // TODO we want this interval configurable -} - func init() { registry.Register("json", New) } @@ -87,11 +63,12 @@ func New(c map[string]interface{}) (publicshare.Manager, error) { conf.init() m := manager{ - mutex: &sync.Mutex{}, - marshaler: jsonpb.Marshaler{}, - unmarshaler: jsonpb.Unmarshaler{}, - file: conf.File, - passwordHashCost: conf.SharePasswordHashCost, + mutex: &sync.Mutex{}, + marshaler: jsonpb.Marshaler{}, + unmarshaler: jsonpb.Unmarshaler{}, + file: conf.File, + passwordHashCost: conf.SharePasswordHashCost, + janitorRunInterval: conf.JanitorRunInterval, } // attempt to create the db file @@ -114,8 +91,7 @@ func New(c map[string]interface{}) (publicshare.Manager, error) { } } - j.m = &m - go j.run() + go m.startJanitorRun() return &m, nil } @@ -123,6 +99,7 @@ func New(c map[string]interface{}) (publicshare.Manager, error) { type config struct { File string `mapstructure:"file"` SharePasswordHashCost int `mapstructure:"password_hash_cost"` + JanitorRunInterval int `mapstructure:"janitor_run_interval"` } func (c *config) init() { @@ -132,24 +109,43 @@ func (c *config) init() { if c.SharePasswordHashCost == 0 { c.SharePasswordHashCost = 11 } + if c.JanitorRunInterval == 0 { + c.JanitorRunInterval = 60 + } } type manager struct { mutex *sync.Mutex file string - marshaler jsonpb.Marshaler - unmarshaler jsonpb.Unmarshaler - passwordHashCost int + marshaler jsonpb.Marshaler + unmarshaler jsonpb.Unmarshaler + passwordHashCost int + janitorRunInterval int +} + +func (m *manager) startJanitorRun() { + ticker := time.NewTicker(time.Duration(m.janitorRunInterval) * time.Second) + work := make(chan os.Signal, 1) + signal.Notify(work, syscall.SIGHUP, syscall.SIGINT, syscall.SIGQUIT) + + for { + select { + case <-work: + return + case <-ticker.C: + m.cleanupExpiredShares() + } + } } // CreatePublicShare adds a new entry to manager.shares func (m *manager) CreatePublicShare(ctx context.Context, u *user.User, rInfo *provider.ResourceInfo, g *link.Grant) (*link.PublicShare, error) { id := &link.PublicShareId{ - OpaqueId: randString(15), + OpaqueId: utils.RandString(15), } - tkn := randString(15) + tkn := utils.RandString(15) now := time.Now().UnixNano() displayName, ok := rInfo.ArbitraryMetadata.Metadata["name"] @@ -173,11 +169,6 @@ func (m *manager) CreatePublicShare(ctx context.Context, u *user.User, rInfo *pr Nanos: uint32(now % 1000000000), } - modifiedAt := &typespb.Timestamp{ - Seconds: uint64(now / 1000000000), - Nanos: uint32(now % 1000000000), - } - s := link.PublicShare{ Id: id, Owner: rInfo.GetOwner(), @@ -186,7 +177,7 @@ func (m *manager) CreatePublicShare(ctx context.Context, u *user.User, rInfo *pr Token: tkn, Permissions: g.Permissions, Ctime: createdAt, - Mtime: modifiedAt, + Mtime: createdAt, PasswordProtected: passwordProtected, Expiration: g.Expiration, DisplayName: displayName, @@ -543,16 +534,6 @@ func (m *manager) GetPublicShareByToken(ctx context.Context, token, password str return nil, errtypes.NotFound(fmt.Sprintf("share with token: `%v` not found", token)) } -// randString is a helper to create tokens. It could be a token manager instead. -func randString(n int) string { - var l = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") - b := make([]rune, n) - for i := range b { - b[i] = l[rand.Intn(len(l))] - } - return string(b) -} - func (m *manager) readDb() (map[string]interface{}, error) { db := map[string]interface{}{} readBytes, err := ioutil.ReadFile(m.file) diff --git a/pkg/storage/utils/eosfs/eosfs.go b/pkg/storage/utils/eosfs/eosfs.go index 08f38bd17c..a064a653d0 100644 --- a/pkg/storage/utils/eosfs/eosfs.go +++ b/pkg/storage/utils/eosfs/eosfs.go @@ -1306,7 +1306,7 @@ func (fs *eosfs) convertToRecycleItem(ctx context.Context, eosDeletedItem *eoscl Path: path, Key: eosDeletedItem.RestoreKey, Size: eosDeletedItem.Size, - DeletionTime: &types.Timestamp{Seconds: eosDeletedItem.DeletionMTime / 1000}, // TODO(labkode): check if eos time is millis or nanos + DeletionTime: &types.Timestamp{Seconds: eosDeletedItem.DeletionMTime}, } if eosDeletedItem.IsDir { recycleItem.Type = provider.ResourceType_RESOURCE_TYPE_CONTAINER diff --git a/pkg/utils/utils.go b/pkg/utils/utils.go index ad2a9cbcd8..a321559039 100644 --- a/pkg/utils/utils.go +++ b/pkg/utils/utils.go @@ -19,6 +19,7 @@ package utils import ( + "math/rand" "net" "net/http" "os/user" @@ -99,6 +100,16 @@ func ResolvePath(path string) (string, error) { return path, nil } +// RandString is a helper to create tokens. +func RandString(n int) string { + var l = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") + b := make([]rune, n) + for i := range b { + b[i] = l[rand.Intn(len(l))] + } + return string(b) +} + // TSToUnixNano converts a protobuf Timestamp to uint64 // with nanoseconds resolution. func TSToUnixNano(ts *types.Timestamp) uint64 {