Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow full paths targets #1605

Closed
wants to merge 5 commits into from
Closed

Allow full paths targets #1605

wants to merge 5 commits into from

Conversation

labkode
Copy link
Member

@labkode labkode commented Mar 31, 2021

Visually it allows to click on a share item in the "Shared with me" tab (accepted or not) and access it in the owner's path.

Screenshot 2021-03-31 at 16 58 38
Screenshot 2021-03-31 at 16 58 49
Screenshot 2021-03-31 at 16 58 55

@update-docs
Copy link

update-docs bot commented Mar 31, 2021

Thanks for opening this pull request! The maintainers of this repository would appreciate it if you would create a changelog item based on your changes.

@labkode labkode changed the title allow full paths targets Allow full paths targets Mar 31, 2021
Copy link
Contributor

@ishank011 ishank011 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@labkode Path needs to contain the full path, while the base is expected as FileTarget

Also, can you remove this line? https://github.com/cs3org/reva/blob/master/internal/http/services/owncloud/ocs/handlers/apps/sharing/shares/shares.go#L641

@butonic
Copy link
Contributor

butonic commented Apr 9, 2021

The share path is the path of the share relative to the currently logged in users home.
The share file_target is the mount point of the share, relative to the recipients home. Yes, this leaks information about the recipients folder names and how he organized his files.
The paths are used to show in the ui who and why someone has access.

That being said, the current implementation is indeed wrong because it only returns the basename.

Some more details on how the path and file_target are built in oc10:

In oc10 the path is built as

		$result['path'] = $userFolder->getRelativePath($shareNode->getPath());

whereas the file_target is the mount point of the recipient

		$result['file_target'] = $share->getTarget();

getTarget directly comes from the db and returns the recipients mount point.

In the web ui the relevant code is in apps/files_sharing/js/sharedfilelist.js

					if (self._sharedWithUser) {
						// Whether the list shows the files shared with the user (true) or
						file.shareOwner = share.displayname_owner;
						file.shareState = share.state;
						file.name = OC.basename(share.file_target);
						file.path = OC.dirname(share.file_target);
						file.permissions = share.permissions;
						if (file.path) {
							file.extraData = share.file_target;
						}
					}
					else {
						// the files that the user shared with others (false).
						if (share.share_type !== OC.Share.SHARE_TYPE_LINK) {
							file.share.targetDisplayName = share.share_with_displayname;
						}
						file.name = OC.basename(share.path);
						file.path = OC.dirname(share.path);
						file.permissions = OC.PERMISSION_ALL;
						if (file.path) {
							file.extraData = share.path;
						}
					}

self._sharedWithUser is true when:

		/**
		 * Whether the list shows the files shared with the user (true) or
		 * the files that the user shared with others (false).
		 */
		_sharedWithUser: false,

I'll dig deeper after lunch...

@labkode
Copy link
Member Author

labkode commented Apr 9, 2021

@butonic that explains it. We are returning a full path. I think nor the server nor the UI should to any assumptions and only show the basename and when clicking use the file_target wherever it points to.

@phil-davis
Copy link
Contributor

Note about tests: if, after discussion, it is agreed to have some behaviour in reva/OCIS that is "a bit" different to oC10, then we can adjust for that in the tests by:

If the new behaviour is made a config option, then the existing oC10 API tests will pass. And "local" acceptance tests can be added that run with the config option, and describe/test the new behaviour.

@micbar
Copy link
Member

micbar commented Apr 9, 2021

let me throw in my 2 cents.

We have the requirement that ownCloud web and all other clients are 100% compatible with ocis backend and oc10 backend.

If we do introduce a "slightly" different behavior of the APIs, we need to do extensive research to not break anything, or maybe also follow with oc10 to keep compatibility.

@butonic
Copy link
Contributor

butonic commented Apr 9, 2021

In owncloud/web the same code looks like this:

  if (incomingShares) {
    resource.resourceOwner = {
      username: share.uid_file_owner,
      displayName: share.displayname_file_owner
    }
    resource.owner = [
      {
        username: share.uid_owner,
        displayName: share.displayname_owner,
        avatar: await getAvatarSrc(share.uid_file_owner, server, token)
      }
    ]
    resource.status = share.state
    resource.name = path.basename(share.file_target)
    resource.path = share.file_target
    resource.isReceivedShare = () => true
  } else {
    resource.sharedWith = share.sharedWith
    resource.shareOwner = share.uid_owner
    resource.shareOwnerDisplayname = share.displayname_owner
    resource.name = path.basename(share.path)
    resource.path = share.path
    // permissions irrelevant here
    resource.isReceivedShare = () => false
  }

So both oc10 and ocis/web do not need the path to show the shared with me / incomingshares=true (ocis web ui) / _sharedWithUser=true (oc10 web ui) list. The path property is not even read in that case. Same goes for the share.file_target when listing shares with others.

The desktop client only uses the file_target to determine permissions, but the code is already prepended with a large comment about removing it for 2.7: https://github.com/owncloud/client/blob/8f6e8c5358be3ceaed405cc4e10d0ae3f7bddc2f/src/gui/sharemanager.cpp#L305-L309

Next thing is the reva ocs sharePrefix. If not configured it defaults to /Shares as the paths used to be relative to the users home ... if we now make them absolute two things need to be checked:

  1. the sharePrefix needs to be set to / or removed entirely
  2. the info.Path needs to point to a global path 🤔

To conclude:

  • the oc10 ui already uses the basename of either file_target or path as the name
  • the ocis web ui already uses the basename of either file_target or path as the name
  • when listing shares with others the API should not return the file_target. It leaks the full path to the mount point of the recipient. And it is not used in the ui because the path will be used instead.

@butonic
Copy link
Contributor

butonic commented Apr 9, 2021

@mbarz the question is if tho oc10 web ui needs to work with ocis?

@mbarz
Copy link

mbarz commented Apr 9, 2021

@mbarz the question is if tho oc10 web ui needs to work with ocis?

Unfortunately, I can't answer this question. You should better ask @micbar 🤔

@butonic
Copy link
Contributor

butonic commented Apr 9, 2021

Regarding tests, this is an example that we can use to clarify why they fail:

Running apiShareManagementBasicToShares tests tagged ~@skipWhenTestingRemoteSystems&&~@skipOnOcV&&~@skipOnOcV&&~@skipOnOcV&&~@notToImplementOnOCIS&&~@toImplementOnOCIS&&~comments-app-required&&~@federation-app-required&&~@notifications-app-required&&~systemtags-app-required&&~@provisioning_api-app-required&&~@preview-extension-required&&~@local_storage&&~@skipOnOcis-OCIS-Storage&&@api&&~@skip | 13s
-- | --
562 | @api @files_sharing-app-required | 14s
563 | Feature: sharing | 14s
564 |   | 14s
565 | Background:                                                                                       # /drone/src/tmp/testrunner/tests/acceptance/features/apiShareManagementBasicToShares/createShareToSharesFolder.feature:4 | 14s
566 | Given the administrator has set the default folder for received shares to "Shares"              # OccContext::theAdministratorHasSetTheDefaultFolderForReceivedSharesTo() | 14s
567 | And auto-accept shares has been disabled                                                        # FeatureContext::autoAcceptSharesHasBeenDisabled() | 14s
568 | And user "Alice" has been created with default attributes and without skeleton files            # FeatureContext::userHasBeenCreatedWithDefaultAttributesAndWithoutSkeletonFiles() | 14s
569 | And user "Alice" has uploaded file with content "ownCloud test text file 0" to "/textfile0.txt" # FeatureContext::userHasUploadedAFileWithContentTo() | 14s
570 |   | 14s
571 | @smokeTest @skipOnEncryptionType:user-keys @issue-32322 | 15s
572 | Scenario Outline: Creating a share of a file with a user, the default permissions are read(1)+update(2)+can-share(16) # /drone/src/tmp/testrunner/tests/acceptance/features/apiShareManagementBasicToShares/createShareToSharesFolder.feature:12 | 15s
573 | Given using OCS API version "<ocs_api_version>"                                                                     # FeatureContext::usingOcsApiVersion() | 15s
574 | And user "Brian" has been created with default attributes and without skeleton files                                # FeatureContext::userHasBeenCreatedWithDefaultAttributesAndWithoutSkeletonFiles() | 15s
575 | When user "Alice" shares file "textfile0.txt" with user "Brian" using the sharing API                               # FeatureContext::userSharesFileWithUserUsingTheSharingApi() | 15s
576 | Then the OCS status code should be "<ocs_status_code>"                                                              # OCSContext::theOCSStatusCodeShouldBe() | 15s
577 | And the HTTP status code should be "200"                                                                            # FeatureContext::thenTheHTTPStatusCodeShouldBe() | 15s
578 | And the fields of the last response to user "Alice" sharing with user "Brian" should include                        # FeatureContext::checkFieldsOfLastResponseToUser() | 15s
579 | \| share_with             \| %username%            \| | 15s
580 | \| share_with_displayname \| %displayname%         \| | 15s
581 | \| file_target            \| /Shares/textfile0.txt \| | 15s
582 | \| path                   \| /textfile0.txt        \| | 15s
583 | \| permissions            \| share,read,update     \| | 15s
584 | \| uid_owner              \| %username%            \| | 15s
585 | \| displayname_owner      \| %displayname%         \| | 15s
586 | \| item_type              \| file                  \| | 15s
587 | \| mimetype               \| text/plain            \| | 15s
588 | \| storage_id             \| ANY_VALUE             \| | 15s
589 | \| share_type             \| user                  \| | 15s
590 | When user "Brian" accepts share "/textfile0.txt" offered by user "Alice" using the sharing API                      # FeatureContext::userReactsToShareOfferedBy() | 15s
591 | Then the OCS status code should be "<ocs_status_code>"                                                              # OCSContext::theOCSStatusCodeShouldBe() | 15s
592 | And the HTTP status code should be "200"                                                                            # FeatureContext::thenTheHTTPStatusCodeShouldBe() | 15s
593 | And the content of file "/Shares/textfile0.txt" for user "Brian" should be "ownCloud test text file 0"              # FeatureContext::contentOfFileForUserShouldBe() | 15s
594 |   | 15s
595 | Examples: | 15s
596 | \| ocs_api_version \| ocs_status_code \| | 15s
597 | \| 1               \| 100             \| | 15s
598 | │ file_target has unexpected value '/home/textfile0.txt' | 15s
599 | │ | 15s
600 | file_target doesn't have value '/Shares/textfile0.txt' | 15s
601 | Failed asserting that false is true. | 15s
602 | \| 2               \| 200             \| | 15s
603 | │ file_target has unexpected value '/home/textfile0.txt' | 15s
604 | │ | 15s
605 | file_target doesn't have value '/Shares/textfile0.txt' | 15s
606 | Failed asserting that false is true.

file_target has unexpected value '/home/textfile0.txt'
file_target doesn't have value '/Shares/textfile0.txt'

We already determined that the ocs handler implementation for path and file_target are currently wrong. How wrong? Or wrong how?

When listing shares with others (and links) info.Path will be prefixed with / to make it absolute. AFAICT this part is correct for oc10 compatability, as the info.Path should contain the path relative to the currently logged in users home. In order to correctly handle reshares the path must be a global one for ocis. It should point to /users/<username_layout> or whatever the namespace for users is. Optionally it can point to /home if the owner of the file is the currently logged in user. But to be honest that contradicts copyable urls. We should always use the user namespace. So, for this @labkode is right.

When listing listSharesWithMe the path and file_target are built by prefixing the info.Path with the share_prefix (/Shares).

🤔 totally different beast.

This needs to list the mount points of the shared file in the file_target. AKA what path does the currently logged in user have to follow to access the share. If we have a global namespace @labkode is correct here as well:

  • reva ocs should render a global file_target when listing shares with me.

In a global namespace there is no neccessity for a /shares namespace: the listing will point to /users/<user_layout>/path/to/share, leaking the path segments between the owners home and the shared file.

A virtual /shares namespace is only necessary to prevent this leakage and to aggregate the etag changes for the desktop client, so it knows where to sync.

But determining where to sync can also be done by listing the storage spaces a user has access to. Every share creates its own storage space and the storage registry can cache the etag for it ...

For the time being, as well as for older desktop clients, we can provide a /shares namespace. Similar to the /home namespace it can build a listing of received shares that all redirect to the actual /user or /projects storage provider. Similar to the publicstorageprovider.

@labkode
Copy link
Member Author

labkode commented Apr 9, 2021

@butonic @micbar my addition: in current OCIS master the shared with me functionality is half-broken. If I share a file/folder with a user, the recipient can click on it but you get an error message. Only after accepting the share you can list the contents. By having the full path exposed (global namespace) you can still access the share.

Our config already reflects the global namespace:

STORAGE_FRONTEND_OCS_Share_PREFIX="/"

@fschade fschade mentioned this pull request Apr 9, 2021
10 tasks
@micbar
Copy link
Member

micbar commented Apr 14, 2021

@butonic @phil-davis Can we figure out how to make this work?

If I got you right @butonic the change is not conflicting with the Classic APIs.

@butonic
Copy link
Contributor

butonic commented May 6, 2021

This is caused by jailing users into the /home folder:

			// only accepted shares can be accessed when jailing users into their home.
			// in this case we cannot stat shared resources that are outside the users home (/home),
			// the path (/users/u-u-i-d/foo) will not be accessible

			// in a global namespace we can access the share using the full path
			// in a jailed namespace we have to point to the mount point in the users /Shares jail
			// - needed for oc10 hot migration
			// or use the /dav/spaces/<space id> endpoint?

			// list /Shares and match fileids with list of received shares
			// - only works for a /Shares folder jail
			// - does not work for freely mountable shares as in oc10 because we would need to iterate over the whole tree, there is no listing of mountpoints, yet

			// can we return the mountpoint when the gateway resolves the listing of shares?
			// - no, the gateway only sees the same list any has the same options as the ocs service
			// - we would need to have a list of mountpoints for the shares -> owncloudstorageprovider for hot migration migration

			// best we can do for now is stat the /Shares jail if it is set and return those paths

on it

  • We also need to fix accepting two folders with the same name. currently the second will fail, it should create a reference and append a number

@butonic
Copy link
Contributor

butonic commented May 6, 2021

@labkode @ishank011 labkode#183 should fix the tests by using paths from the Share if it is configured.

@butonic
Copy link
Contributor

butonic commented May 7, 2021

@ishank011 continued in #1691?

@ishank011
Copy link
Contributor

ishank011 commented May 7, 2021

@butonic nope, I just tried to see if your commit passes the CI. Refer labkode#183 (comment) and labkode#183 (comment)

@butonic
Copy link
Contributor

butonic commented May 27, 2021

trying to reproduce with:

                    "STORAGE_WEBDAV_NAMESPACE": "/", // http /webav/home/hello.txt -> cs3 /home/hello.txt
                    "STORAGE_DAV_FILES_NAMESPACE": "/", // "/users"
                    "STORAGE_FRONTEND_OCS_SHARE_PREFIX": "/",

but running into panics.

AFAICT we need

diff --git a/internal/grpc/services/gateway/storageprovider.go b/internal/grpc/services/gateway/storageprovider.go
index 90a5a042..6052b926 100644
--- a/internal/grpc/services/gateway/storageprovider.go
+++ b/internal/grpc/services/gateway/storageprovider.go
@@ -1225,6 +1225,23 @@ func (s *svc) statOnProvider(ctx context.Context, req *provider.StatRequest, res
                *e = errors.Wrap(err, "gateway: error calling ListContainer")
                return
        }
+
+       if r.Status.Code != rpc.Code_CODE_OK {
+               switch r.Status.Code {
+               case rpc.Code_CODE_NOT_FOUND:
+                       *e = errtypes.NotFound(newPath)
+               case rpc.Code_CODE_PERMISSION_DENIED:
+                       *e = errtypes.PermissionDenied(newPath)
+               case rpc.Code_CODE_INVALID_ARGUMENT, rpc.Code_CODE_FAILED_PRECONDITION, rpc.Code_CODE_OUT_OF_RANGE:
+                       *e = errtypes.BadRequest(newPath)
+               case rpc.Code_CODE_UNIMPLEMENTED:
+                       *e = errtypes.NotSupported(newPath)
+               default:
+                       *e = errtypes.InternalError("gateway: error stating target reference")
+               }
+               return
+       }
+
        if res == nil {
                res = &provider.ResourceInfo{}
        }
diff --git a/pkg/storage/utils/decomposedfs/lookup.go b/pkg/storage/utils/decomposedfs/lookup.go
index 0de4106b..f4d2b4ae 100644
--- a/pkg/storage/utils/decomposedfs/lookup.go
+++ b/pkg/storage/utils/decomposedfs/lookup.go
@@ -71,6 +71,9 @@ func (lu *Lookup) NodeFromPath(ctx context.Context, fn string) (*node.Node, erro
                if err != nil {
                        return nil, err
                }
+       } else {
+               // allow listing and stating root
+               n.Exists = true
        }
 
        return n, nil
diff --git a/pkg/storage/utils/decomposedfs/node/permissions.go b/pkg/storage/utils/decomposedfs/node/permissions.go
index ea3e5cae..913cc949 100644
--- a/pkg/storage/utils/decomposedfs/node/permissions.go
+++ b/pkg/storage/utils/decomposedfs/node/permissions.go
@@ -37,7 +37,8 @@ var NoPermissions *provider.ResourcePermissions = &provider.ResourcePermissions{
 
 // NoOwnerPermissions defines permissions for nodes that don't have an owner set, eg the root node
 var NoOwnerPermissions *provider.ResourcePermissions = &provider.ResourcePermissions{
-       Stat: true,
+       Stat:          true,
+       ListContainer: true,
 }
 
 // OwnerPermissions defines permissions for nodes owned by the user

@butonic butonic mentioned this pull request May 27, 2021
@butonic
Copy link
Contributor

butonic commented May 28, 2021

deprecated by #1739

@butonic butonic closed this May 28, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants