diff --git a/appinfo/app.php b/appinfo/app.php
index 19a70055..b629bec4 100644
--- a/appinfo/app.php
+++ b/appinfo/app.php
@@ -3,9 +3,3 @@
use OCP\Util;
$app = \OC::$server->query(\OCA\ScienceMesh\AppInfo\ScienceMeshApp::class);
-
-$eventDispatcher = \OC::$server->getEventDispatcher();
-$eventDispatcher->addListener('OCA\Files::loadAdditionalScripts', function(){
- Util::addScript('sciencemesh', 'open-with');
- Util::addStyle('sciencemesh', 'open-with');
-});
diff --git a/appinfo/info.xml b/appinfo/info.xml
index c3400826..48a0ab0d 100644
--- a/appinfo/info.xml
+++ b/appinfo/info.xml
@@ -23,7 +23,7 @@
integration
https://github.com/pondersource/nc-sciencemesh/issues
-
+
diff --git a/appinfo/routes.php b/appinfo/routes.php
index f1187bc0..88bd982e 100644
--- a/appinfo/routes.php
+++ b/appinfo/routes.php
@@ -38,31 +38,25 @@
['name' => 'reva#UnsetArbitraryMetadata', 'url' => '/~{userId}/api/storage/UnsetArbitraryMetadata', 'verb' => 'POST'],
['name' => 'reva#UpdateGrant', 'url' => '/~{userId}/api/storage/UpdateGrant', 'verb' => 'POST'],
['name' => 'reva#Upload', 'url' => '/~{userId}/api/storage/Upload/{path}', 'verb' => 'PUT', 'requirements' => ['path' => '.+']],
+ ['name' => 'reva#Download', 'url' => '/~{userId}/api/storage/Download/{path}', 'verb' => 'GET', 'requirements' => array('path' => '.+')],
// OCM routes
- ['name' => 'ocm#addSentShare', 'url' => '/~{userId}/api/ocm/addSentShare', 'verb' => 'POST'],
- ['name' => 'ocm#addReceivedShare', 'url' => '/~{userId}/api/ocm/addReceivedShare', 'verb' => 'POST'],
- ['name' => 'ocm#GetSentShare', 'url' => '/~{userId}/api/ocm/GetSentShare', 'verb' => 'POST'],
- ['name' => 'ocm#GetSentShareByToken', 'url' => '/~nobody/api/ocm/GetSentShareByToken', 'verb' => 'POST'],
- ['name' => 'ocm#Unshare', 'url' => '/~{userId}/api/ocm/Unshare', 'verb' => 'POST'],
- ['name' => 'ocm#UpdateShare', 'url' => '/~{userId}/api/ocm/UpdateShare', 'verb' => 'POST'],
- ['name' => 'ocm#ListSentShares', 'url' => '/~{userId}/api/ocm/ListSentShares', 'verb' => 'POST'],
- ['name' => 'ocm#ListSentShares', 'url' => '/~{userId}/api/ocm/ListShares', 'verb' => 'POST'], // alias for ListSentShares
- ['name' => 'ocm#ListReceivedShares', 'url' => '/~{userId}/api/ocm/ListReceivedShares', 'verb' => 'POST'],
- ['name' => 'ocm#GetReceivedShare', 'url' => '/~{userId}/api/ocm/GetReceivedShare', 'verb' => 'POST'],
- ['name' => 'ocm#UpdateSentShare', 'url' => '/~{userId}/api/ocm/UpdateSentShare', 'verb' => 'POST'],
- ['name' => 'ocm#UpdateReceivedShare', 'url' => '/~{userId}/api/ocm/UpdateReceivedShare', 'verb' => 'POST'],
- ['name' => 'ocm#GetUser', 'url' => '/~{dummy}/api/user/GetUser', 'verb' => 'POST'],
- ['name' => 'ocm#GetUserByClaim', 'url' => '/~{dummy}/api/user/GetUserByClaim', 'verb' => 'POST'],
-
- /*
- ['name' => 'storage#createHome', 'url' => '/~{userId}/CreateHome', 'verb' => 'POST'],
- ['name' => 'storage#listFolder', 'url' => '/~{userId}/ListFolder', 'verb' => 'POST'],
- ['name' => 'storage#initiateUpload', 'url' => '/~{userId}/InitiateUpload', 'verb' => 'POST'],
- ['name' => 'storage#upload', 'url' => '/~{userId}/Upload', 'verb' => 'POST'],
- ['name' => 'storage#handleUpload', 'url' => '/~{userId}/Upload/{path}', 'verb' => 'PUT'],
- ['name' => 'storage#getMD', 'url' => '/~{userId}/GetMD', 'verb' => 'POST'],
- */
+ ['name' => 'reva#addSentShare', 'url' => '/~{userId}/api/ocm/addSentShare', 'verb' => 'POST'],
+ ['name' => 'reva#addReceivedShare', 'url' => '/~{userId}/api/ocm/addReceivedShare', 'verb' => 'POST'],
+ ['name' => 'reva#GetSentShare', 'url' => '/~{userId}/api/ocm/GetSentShare', 'verb' => 'POST'],
+ ['name' => 'reva#Unshare', 'url' => '/~{userId}/api/ocm/Unshare', 'verb' => 'POST'],
+ ['name' => 'reva#UpdateShare', 'url' => '/~{userId}/api/ocm/UpdateShare', 'verb' => 'POST'],
+ ['name' => 'reva#ListSentShares', 'url' => '/~{userId}/api/ocm/ListSentShares', 'verb' => 'POST'],
+ ['name' => 'reva#ListSentShares', 'url' => '/~{userId}/api/ocm/ListShares', 'verb' => 'POST'], // alias for ListSentShares
+ ['name' => 'reva#ListReceivedShares', 'url' => '/~{userId}/api/ocm/ListReceivedShares', 'verb' => 'POST'],
+ ['name' => 'reva#GetReceivedShare', 'url' => '/~{userId}/api/ocm/GetReceivedShare', 'verb' => 'POST'],
+ ['name' => 'reva#UpdateSentShare', 'url' => '/~{userId}/api/ocm/UpdateSentShare', 'verb' => 'POST'],
+ ['name' => 'reva#UpdateReceivedShare', 'url' => '/~{userId}/api/ocm/UpdateReceivedShare', 'verb' => 'POST'],
+ ['name' => 'reva#GetUser', 'url' => '/~{dummy}/api/user/GetUser', 'verb' => 'POST'],
+ ['name' => 'reva#GetUserByClaim', 'url' => '/~{dummy}/api/user/GetUserByClaim', 'verb' => 'POST'],
+ // See: https://github.com/cs3org/reva/pull/4115#discussion_r1308371946
+ // we need to handle this route for both nobody and userId.
+ ['name' => 'reva#GetSentShareByToken', 'url' => '/~{userId}/api/ocm/GetSentShareByToken', 'verb' => 'POST'],
// Files routes
['name' => 'storage#handleGet', 'url' => '/~{userId}/files/{path}', 'verb' => 'GET', 'requirements' => ['path' => '.+']],
@@ -71,7 +65,6 @@
['name' => 'storage#handleDelete', 'url' => '/~{userId}/files/{path}', 'verb' => 'DELETE', 'requirements' => ['path' => '.+']],
['name' => 'storage#handleHead', 'url' => '/~{userId}/files/{path}', 'verb' => 'HEAD', 'requirements' => ['path' => '.+']],
-
// Internal app routes
['name' => 'app#contacts', 'url' => '/', 'verb' => 'GET'],
['name' => 'app#generate', 'url' => '/generate', 'verb' => 'GET'],
diff --git a/lib/Controller/AppController.php b/lib/Controller/AppController.php
index 49bbb173..78899612 100644
--- a/lib/Controller/AppController.php
+++ b/lib/Controller/AppController.php
@@ -124,8 +124,10 @@ public function accept() {
public function invitationsGenerate() {
$recipient = $this->request->getParam('email');
$invitationsData = $this->httpClient->generateTokenFromReva($this->userId, $recipient);
- $inviteLinkStr = $invitationsData["invite_link"];
-
+
+ // check if invite_link exist before accessing.
+ $inviteLinkStr = isset($invitationsData["invite_link"]) ? $invitationsData["invite_link"] : false;
+
if (!$inviteLinkStr) {
return new TextPlainResponse("Unexpected response from Reva", Http::STATUS_INTERNAL_SERVER_ERROR);
}
diff --git a/lib/Controller/OcmController.php b/lib/Controller/OcmController.php
deleted file mode 100644
index bd7e974e..00000000
--- a/lib/Controller/OcmController.php
+++ /dev/null
@@ -1,732 +0,0 @@
-rootFolder = $rootFolder;
- $this->request = $request;
- $this->session = $session;
- $this->userManager = $userManager;
- $this->urlGenerator = $urlGenerator;
-
- $this->config = new \OCA\ScienceMesh\ServerConfig($config);
-
- $this->trashManager = $trashManager;
- $this->shareManager = $shareManager;
- $this->groupManager = $groupManager;
- $this->cloudFederationProviderManager = $cloudFederationProviderManager;
- $this->factory = $factory;
- $this->cloudIdManager = $cloudIdManager;
- $this->logger = $logger;
- $this->appManager = $appManager;
- $this->l = $l10n;
- $this->shareProvider = $shareProvider;
- }
- private function init($userId) {
- $this->userId = $userId;
- $this->checkRevadAuth();
- if ($userId and $this->userManager->userExists($userId)) {
- $this->userFolder = $this->rootFolder->getUserFolder($userId);
- }
- }
-
- private function revaPathToNextcloudPath($revaPath) {
- $ret = NEXTCLOUD_PREFIX . substr($revaPath, strlen(REVA_PREFIX));
- return $ret;
- }
-
- private function nextcloudPathToRevaPath($nextcloudPath) {
- // return REVA_PREFIX . substr($nextcloudPath, strlen($this->userFolder->getPath() . NEXTCLOUD_PREFIX));
- return REVA_PREFIX . substr($nextcloudPath, strlen(NEXTCLOUD_PREFIX));
- }
-
- /**
- * @param array $nodeInfo
- *
- * Returns the data of a CS3 provider.ResourceInfo object https://github.com/cs3org/cs3apis/blob/a86e5cb/cs3/storage/provider/v1beta1/resources.proto#L35-L93
- * @return array
- *
- * @throws \OCP\Files\InvalidPathException
- * @throws \OCP\Files\NotFoundException
- */
- private function lock(\OCP\Files\Node $node) {
- $node->lock(ILockingProvider::LOCK_SHARED);
- $this->lockedNode = $node;
- }
-
- /**
- * Make sure that the passed date is valid ISO 8601
- * So YYYY-MM-DD
- * If not throw an exception
- *
- * @param string $expireDate
- *
- * @throws \Exception
- * @return \DateTime
- */
- private function parseDate(string $expireDate): \DateTime {
- try {
- $date = new \DateTime($expireDate);
- } catch (\Exception $e) {
- throw new \Exception('Invalid date. Format must be YYYY-MM-DD');
- }
-
- $date->setTime(0, 0, 0);
-
- return $date;
- }
- private function checkRevadAuth() {
- $authHeader = $this->request->getHeader('X-Reva-Secret');
- if ($authHeader != $this->config->getRevaSharedSecret()) {
- throw new \OCP\Files\NotPermittedException('Please set an http request header "X-Reva-Secret: "!');
- }
- }
- private function getRevaPathFromOpaqueId($opaqueId) {
- return substr($opaqueId, strlen("fileid-"));
- }
-
- private function nodeToCS3ResourceInfo(\OCP\Files\Node $node) : array {
- $isDirectory = ($node->getType() === \OCP\Files\FileInfo::TYPE_FOLDER);
- $nextcloudPath = substr($node->getPath(), strlen($this->userFolder->getPath()) + 1);
- $revaPath = $this->nextcloudPathToRevaPath($nextcloudPath);
- return [
- "opaque" => [
- "map" => null,
- ],
- "type" => ($isDirectory ? 2 : 1),
- "id" => [
- "opaque_id" => "fileid-" . $revaPath,
- ],
- "checksum" => [
- "type" => 0,
- "sum" => "",
- ],
- "etag" => "deadbeef",
- "mime_type" => ($isDirectory ? "folder" : $node->getMimetype()),
- "mtime" => [
- "seconds" => $node->getMTime(),
- ],
- "path" => $revaPath,
- "permission_set" => [
- "add_grant" => false,
- "create_container" => false,
- "delete" => false,
- "get_path" => false,
- "get_quota" => false,
- "initiate_file_download" => false,
- "initiate_file_upload" => false,
- ],
- "size" => $node->getSize(),
- "canonical_metadata" => [
- "target" => null,
- ],
- "arbitrary_metadata" => [
- "metadata" => [
- ".placeholder" => "ignore",
- ],
- ],
- "owner" => [
- "opaque_id" => $this->userId,
- "idp" => $this->config->getIopUrl(),
- ],
- ];
- }
-
- # For ListReceivedShares, GetReceivedShare and UpdateReceivedShare we need to include "state:2"
- private function shareInfoToCs3Share(IShare $share, $token = ''): array {
- $shareeParts = explode("@", $share->getSharedWith());
- if (count($shareeParts) == 1) {
- error_log("warning, could not find sharee user@host from '" . $share->getSharedWith() . "'");
- $shareeParts = [ "unknown", "unknown" ];
- }
- $ownerParts = explode("@", $share->getShareOwner());
- if (count($ownerParts) == 1) {
- error_log("warning, could not find owner user@host from '" . $share->getShareOwner() . "'");
- $ownerParts = [ $ownerParts[0], "unknown" ];
- }
- $stime = 0; // $share->getShareTime()->getTimeStamp();
- try {
- $opaqueId = "fileid-" . $share->getNode()->getPath();
- } catch (\OCP\Files\NotFoundException $e) {
- $opaqueId = "unknown";
- }
-
- // produces JSON that maps to
- // https://github.com/cs3org/reva/blob/v1.18.0/pkg/ocm/share/manager/nextcloud/nextcloud.go#L77
- // and
- // https://github.com/cs3org/go-cs3apis/blob/d297419/cs3/sharing/ocm/v1beta1/resources.pb.go#L100
- return [
- "id" => [
- // https://github.com/cs3org/go-cs3apis/blob/d297419/cs3/sharing/ocm/v1beta1/resources.pb.go#L423
- "opaque_id" => $share->getId()
- ],
- "resource_id" => [
-
- "opaque_id" => $opaqueId,
- ],
- "permissions" => [
- "permissions" => [
- "add_grant" => true,
- "create_container" => true,
- "delete" => true,
- "get_path" => true,
- "get_quota" => true,
- "initiate_file_download" => true,
- "initiate_file_upload" => true,
- ]
- ],
- // https://github.com/cs3org/go-cs3apis/blob/d29741980082ecd0f70fe10bd2e98cf75764e858/cs3/storage/provider/v1beta1/resources.pb.go#L897
- "grantee" => [
- "type" => 1, // https://github.com/cs3org/go-cs3apis/blob/d29741980082ecd0f70fe10bd2e98cf75764e858/cs3/storage/provider/v1beta1/resources.pb.go#L135
- "id" => [
- "opaque_id" => $shareeParts[0],
- "idp" => $shareeParts[1]
- ],
- ],
- "owner" => [
- "id" => [
- "opaque_id" => $ownerParts[0],
- "idp" => $ownerParts[1]
- ],
- ],
- "creator" => [
- "id" => [
- "opaque_id" => $ownerParts[0],
- "idp" => $ownerParts[1]
- ],
- ],
- "ctime" => [
- "seconds" => $stime
- ],
- "mtime" => [
- "seconds" => $stime
- ],
- "token" => $token
- ];
- }
-
- # correspondes the permissions we got from Reva to Nextcloud
- private function getPermissionsCode(array $permissions) : int {
- $permissionsCode = 0;
- if (!empty($permissions["get_path"]) || !empty($permissions["get_quota"]) || !empty($permissions["initiate_file_download"]) || !empty($permissions["initiate_file_upload"]) || !empty($permissions["stat"])) {
- $permissionsCode += \OCP\Constants::PERMISSION_READ;
- }
- if (!empty($permissions["create_container"]) || !empty($permissions["move"]) || !empty($permissions["add_grant"]) || !empty($permissions["restore_file_version"]) || !empty($permissions["restore_recycle_item"])) {
- $permissionsCode += \OCP\Constants::PERMISSION_CREATE;
- }
- if (!empty($permissions["move"]) || !empty($permissions["delete"]) || !empty($permissions["remove_grant"])) {
- $permissionsCode += \OCP\Constants::PERMISSION_DELETE;
- }
- if (!empty($permissions["list_grants"]) || !empty($permissions["list_file_versions"]) || !empty($permissions["list_recycle"])) {
- $permissionsCode += \OCP\Constants::PERMISSION_SHARE;
- }
- if (!empty($permissions["update_grant"])) {
- $permissionsCode += \OCP\Constants::PERMISSION_UPDATE;
- }
- return $permissionsCode;
- }
- /**
- * @param int
- *
- * @return int
- * @throws NotFoundException
- */
- private function getStorageUrl($userId) {
- $storageUrl = $this->urlGenerator->getAbsoluteURL($this->urlGenerator->linkToRoute("sciencemesh.storage.handleHead", ["userId" => $userId, "path" => "foo"]));
- $storageUrl = preg_replace('/foo$/', '', $storageUrl);
- return $storageUrl;
- }
-
- /* Reva handlers */
-
- private function formatUser($user) {
- return [
- "id" => [
- "idp" => $this->config->getIopUrl(),
- "opaque_id" => $user->getUID(),
- ],
- "display_name" => $user->getDisplayName(),
- "username" => $user->getUID(),
- "email" => $user->getEmailAddress(),
- "type" => 1,
- ];
- }
-
-
- /**
- * Write a new file.
- *
- * @param string $path
- * @param string $contents
- * @param Config $config Config object
- *
- * @return bool false on failure, true on success
- *
- * @throws \OCP\Files\InvalidPathException
- */
- private function write($path, $contents, Config $config) {
- try {
- if ($this->userFolder->nodeExists($path)) {
- $node = $this->userFolder->get($path);
- $node->putContent($contents);
- } else {
- }
- } catch (\Exception $e) {
- return false;
- }
- return true;
- }
-
- /**
- * @PublicPage
- * @NoCSRFRequired
- * @NoSameSiteCookieRequired
- *
- * Get user list.
- */
- public function GetUser($dummy) {
- $this->init(false);
-
- $userToCheck = $this->request->getParam('opaque_id');
- if ($this->userManager->userExists($userToCheck)) {
- $user = $this->userManager->get($userToCheck);
- $response = $this->formatUser($user);
- return new JSONResponse($response, Http::STATUS_OK);
- }
- return new JSONResponse(
- ['message' => 'User does not exist'],
- Http::STATUS_NOT_FOUND
- );
- }
-
- /**
- * @PublicPage
- * @NoCSRFRequired
- * @NoSameSiteCookieRequired
- *
- * Get user list.
- */
- public function GetUserByClaim($dummy) {
- $this->init(false);
- $userToCheck = $this->request->getParam('value');
- if ($this->request->getParam('claim') == 'username') {
- error_log("GetUserByClaim, claim = 'username', value = $userToCheck");
- } else {
- return new JSONResponse('Please set the claim to username', Http::STATUS_BAD_REQUEST);
- }
- if ($this->userManager->userExists($userToCheck)) {
- $user = $this->userManager->get($userToCheck);
- $response = $this->formatUser($user);
- return new JSONResponse($response, Http::STATUS_OK);
- }
- return new JSONResponse(
- ['message' => 'User does not exist'],
- Http::STATUS_NOT_FOUND
- );
- }
- /**
- * @PublicPage
- * @NoCSRFRequired
- * @return Http\DataResponse|JSONResponse
- *
- * @throws NotFoundException
- * @throws OCSNotFoundException
- * Create a new share in fn with the given access control list.
- */
- public function addSentShare($userId) {
- if ($this->userManager->userExists($userId)) {
- $this->init($userId);
- } else {
- return new JSONResponse("User not found", Http::STATUS_FORBIDDEN);
- }
- $params = $this->request->getParams();
- $owner = $params["owner"]["opaqueId"]; // . "@" . $params["owner"]["idp"];
- $name = $params["name"]; // "fileid-/other/q/f gr"
- $resourceOpaqueId = $params["resourceId"]["opaqueId"]; // "fileid-/other/q/f gr"
- $revaPath = $this->getRevaPathFromOpaqueId($resourceOpaqueId); // "/other/q/f gr"
- $nextcloudPath = $this->revaPathToNextcloudPath($revaPath);
-
- $revaPermissions = null;
-
- foreach($params['accessMethods'] as $accessMethod) {
- if (isset($accessMethod['webdavOptions'])) {
- $revaPermissions = $accessMethod['webdavOptions']['permissions'];
- break;
- }
- }
-
- if (!isset($revaPermissions)) {
- throw new \Exception('reva permissions not found');
- }
-
- $granteeType = $params["grantee"]["type"]; // "GRANTEE_TYPE_USER"
- $granteeHost = $params["grantee"]["userId"]["idp"]; // "revanc2.docker"
- $granteeUser = $params["grantee"]["userId"]["opaqueId"]; // "marie"
-
- if ($revaPermissions === null) {
- $revaPermissions = [
- "initiate_file_download" => true
- ];
- }
- $nextcloudPermissions = $this->getPermissionsCode($revaPermissions);
- $shareWith = $granteeUser."@".$granteeHost;
- $sharedSecret = $params["token"];
- try {
- $node = $this->userFolder->get($nextcloudPath);
- } catch (NotFoundException $e) {
- return new JSONResponse(["error" => "Share failed. Resource Path not found"], Http::STATUS_BAD_REQUEST);
- }
- $share = $this->shareManager->newShare();
- $share->setNode($node);
- try {
- $this->lock($share->getNode());
- } catch (LockedException $e) {
- throw new OCSNotFoundException($this->l->t('Could not create share'));
- }
- $share->setShareType(IShare::TYPE_REMOTE);//IShare::TYPE_SCIENCEMESH);
- $share->setSharedBy($userId);
- $share->setSharedWith($shareWith);
- $share->setShareOwner($owner);
- $share->setPermissions($nextcloudPermissions);
- $share->setToken($sharedSecret);
- $share = $this->shareProvider->createInternal($share);
- return new DataResponse($share->getId(), Http::STATUS_CREATED);
- }
-
- /**
- * add a received share
- *
- * @NoCSRFRequired
- * @PublicPage
- * @return Http\DataResponse|JSONResponse
- */
- public function addReceivedShare($userId) {
- $params = $this->request->getParams();
- foreach($params['protocols'] as $protocol) {
- if (isset($protocol['webdavOptions'])) {
- $sharedSecret = $protocol['webdavOptions']['sharedSecret'];
- // make sure you have webdav_endpoint = "https://nc1.docker/" under
- // [grpc.services.ocmshareprovider] in the sending Reva's config
- $uri = $protocol['webdavOptions']['uri']; // e.g. https://nc1.docker/remote.php/dav/ocm/vaKE36Wf1lJWCvpDcRQUScraVP5quhzA
- $remote = implode('/', array_slice(explode('/', $uri), 0, 3)); // e.g. https://nc1.docker
- break;
- }
- }
- if (!isset($sharedSecret)) {
- throw new \Exception('sharedSecret not found');
- }
-
- $shareData = [
- "remote" => $remote, //https://nc1.docker
- "remote_id" => $params["remoteShareId"], // the id of the share in the oc_share table of the remote.
- "share_token" => $sharedSecret, // 'tDPRTrLI4hE3C5T'
- "password" => "",
- "name" => rtrim($params["name"], "/"), // '/grfe'
- "owner" => $params["owner"]["opaqueId"], // 'einstein'
- "user" => $userId // 'marie'
- ];
- if ($this->userManager->userExists($userId)) {
- $this->init($userId);
- } else {
- return new JSONResponse("User not found", Http::STATUS_FORBIDDEN);
- }
-
- $scienceMeshData = [
- "is_external" => true,
- ];
-
- $id = $this->shareProvider->addScienceMeshShare($scienceMeshData, $shareData);
- return new JSONResponse($id, 201);
- }
-
- /**
- * @PublicPage
- * @NoCSRFRequired
- * @return Http\DataResponse|JSONResponse
- *
- *
- * Remove Share from share table
- */
- public function Unshare($userId) {
- if ($this->userManager->userExists($userId)) {
- $this->init($userId);
- } else {
- return new JSONResponse("User not found", Http::STATUS_FORBIDDEN);
- }
- $opaqueId = $this->request->getParam("Spec")["Id"]["opaque_id"];
- $name = $this->getNameByOpaqueId($opaqueId);
- if ($this->shareProvider->deleteSentShareByName($userId, $name)) {
- return new JSONResponse("Deleted Sent Share",Http::STATUS_OK);
- } else {
- if ($this->shareProvider->deleteReceivedShareByOpaqueId($userId, $opaqueId)) {
- return new JSONResponse("Deleted Received Share",Http::STATUS_OK);
- } else {
- return new JSONResponse("Could not find share", Http::STATUS_BAD_REQUEST);
- }
- }
- }
-
- /**
- * @PublicPage
- * @NoCSRFRequired
- * @return Http\DataResponse|JSONResponse
- *
- */
- public function UpdateSentShare($userId) {
- if ($this->userManager->userExists($userId)) {
- $this->init($userId);
- } else {
- return new JSONResponse("User not found", Http::STATUS_FORBIDDEN);
- }
- $opaqueId = $this->request->getParam("ref")["Spec"]["Id"]["opaque_id"];
- $permissions = $this->request->getParam("p")["permissions"];
- $permissionsCode = $this->getPermissionsCode($permissions);
- $name = $this->getNameByOpaqueId($opaqueId);
- if (!($share = $this->shareProvider->getSentShareByName($userId,$name))) {
- return new JSONResponse(["error" => "UpdateSentShare failed"], Http::STATUS_INTERNAL_SERVER_ERROR);
- }
- $share->setPermissions($permissionsCode);
- $shareUpdated = $this->shareProvider->update($share);
- $response = $this->shareInfoToCs3Share($shareUpdated);
- return new JSONResponse($response, Http::STATUS_OK);
- }
- /**
- * @PublicPage
- * @NoCSRFRequired
- * @return Http\DataResponse|JSONResponse
- *
- * UpdateReceivedShare updates the received share with share state.
- */
- public function UpdateReceivedShare($userId) {
- if ($this->userManager->userExists($userId)) {
- $this->init($userId);
- } else {
- return new JSONResponse("User not found", Http::STATUS_FORBIDDEN);
- }
- $response = [];
- $resourceId = $this->request->getParam("received_share")["share"]["resource_id"];
- $permissions = $this->request->getParam("received_share")["share"]["permissions"];
- $permissionsCode = $this->getPermissionsCode($permissions);
- try {
- $share = $this->shareProvider->getReceivedShareByToken($resourceId);
- $share->setPermissions($permissionsCode);
- $shareUpdate = $this->shareProvider->UpdateReceivedShare($share);
- $response = $this->shareInfoToCs3Share($shareUpdate);
- $response["state"] = 2;
- return new JSONResponse($response, Http::STATUS_OK);
- } catch (\Exception $e) {
- return new JSONResponse(["error" => $e->getMessage()], Http::STATUS_INTERNAL_SERVER_ERROR);
- }
- }
- /**
- * @PublicPage
- * @NoCSRFRequired
- * @return Http\DataResponse|JSONResponse
- *
- * ListSentShares returns the shares created by the user. If md is provided is not nil,
- * it returns only shares attached to the given resource.
- */
- public function ListSentShares($userId) {
- if ($this->userManager->userExists($userId)) {
- $this->init($userId);
- } else {
- return new JSONResponse("User not found", Http::STATUS_FORBIDDEN);
- }
- $responses = [];
- $shares = $this->shareProvider->getSentShares($userId);
- if ($shares) {
- foreach ($shares as $share) {
- array_push($responses, $this->shareInfoToCs3Share($share));
- }
- }
- return new JSONResponse($responses, Http::STATUS_OK);
- }
- /**
- * @PublicPage
- * @NoCSRFRequired
- * @return Http\DataResponse|JSONResponse
- * ListReceivedShares returns the list of shares the user has access.
- */
- public function ListReceivedShares($userId) {
- if ($this->userManager->userExists($userId)) {
- $this->init($userId);
- } else {
- return new JSONResponse("User not found", Http::STATUS_FORBIDDEN);
- }
- $responses = [];
- $shares = $this->shareProvider->getReceivedShares($userId);
- if ($shares) {
- foreach ($shares as $share) {
- $response = $this->shareInfoToCs3Share($share);
- array_push($responses,[
- "share" => $response,
- "state" => 2
- ]);
- }
- }
- return new JSONResponse($responses, Http::STATUS_OK);
- }
- /**
- * @PublicPage
- * @NoCSRFRequired
- * @return Http\DataResponse|JSONResponse
- *
- * GetReceivedShare returns the information for a received share the user has access.
- */
- public function GetReceivedShare($userId) {
- if ($this->userManager->userExists($userId)) {
- $this->init($userId);
- } else {
- return new JSONResponse("User not found", Http::STATUS_FORBIDDEN);
- }
- $opaqueId = $this->request->getParam("Spec")["Id"]["opaque_id"];
- $name = $this->getNameByOpaqueId($opaqueId);
- try {
- $share = $this->shareProvider->getReceivedShareByToken($opaqueId);
- $response = $this->shareInfoToCs3Share($share);
- $response["state"] = 2;
- return new JSONResponse($response, Http::STATUS_OK);
- } catch (\Exception $e) {
- return new JSONResponse(["error" => $e->getMessage()],Http::STATUS_BAD_REQUEST);
- }
- }
-
- /**
- * @PublicPage
- * @NoCSRFRequired
- * @return Http\DataResponse|JSONResponse
- *
- * GetSentShare gets the information for a share by the given ref.
- */
- public function GetSentShare($userId) {
- if ($this->userManager->userExists($userId)) {
- $this->init($userId);
- } else {
- return new JSONResponse("User not found", Http::STATUS_FORBIDDEN);
- }
- $opaqueId = $this->request->getParam("Spec")["Id"]["opaque_id"];
- $name = $this->getNameByOpaqueId($opaqueId);
- $share = $this->shareProvider->getSentShareByName($userId,$name);
- if ($share) {
- $response = $this->shareInfoToCs3Share($share);
- return new JSONResponse($response, Http::STATUS_OK);
- }
- return new JSONResponse(["error" => "GetSentShare failed"], Http::STATUS_BAD_REQUEST);
- }
-
- /**
- * @PublicPage
- * @NoCSRFRequired
- * @return Http\DataResponse|JSONResponse
- *
- * GetSentShareByToken gets the information for a share by the given token.
- */
- public function GetSentShareByToken() {
- $token = $this->request->getParam("Spec")["Token"];
- $share = $this->shareProvider->getShareByToken($token);
- if ($share) {
- $response = $this->shareInfoToCs3Share($share, $token);
- return new JSONResponse($response, Http::STATUS_OK);
- }
- return new JSONResponse(["error" => "GetSentShare failed"], Http::STATUS_BAD_REQUEST);
- }
-}
diff --git a/lib/Controller/RevaController.php b/lib/Controller/RevaController.php
index dcb97944..193ddf48 100644
--- a/lib/Controller/RevaController.php
+++ b/lib/Controller/RevaController.php
@@ -2,13 +2,6 @@
namespace OCA\ScienceMesh\Controller;
-use OCA\ScienceMesh\NextcloudAdapter;
-use OCA\ScienceMesh\ShareProvider\ScienceMeshShareProvider;
-use OCA\ScienceMesh\Share\ScienceMeshSharePermissions;
-use OCA\ScienceMesh\User\ScienceMeshUserId;
-
-use OCA\Files_Trashbin\Trash\ITrashManager;
-
use OCP\IRequest;
use OCP\IUserManager;
use OCP\IGroupManager;
@@ -16,17 +9,23 @@
use OCP\ISession;
use OCP\IConfig;
+use OCP\IL10N;
+use OCP\App\IAppManager;
+use OCP\Lock\ILockingProvider;
+use OCP\Lock\LockedException;
+
+use OC\Files\View;
use OCP\Files\IRootFolder;
use OCP\Files\NotPermittedException;
use \OCP\Files\NotFoundException;
use OCP\AppFramework\Http;
-use OCP\AppFramework\Http\JSONResponse;
use OCP\AppFramework\Http\DataResponse;
+use OCP\AppFramework\Http\JSONResponse;
+use OCP\AppFramework\Http\StreamResponse;
use OCP\AppFramework\Controller;
use OCP\AppFramework\OCS\OCSNotFoundException;
-use OCA\CloudFederationAPI\Config;
use OCP\Federation\ICloudFederationFactory;
use OCP\Federation\ICloudFederationProviderManager;
use OCP\Federation\ICloudIdManager;
@@ -35,18 +34,20 @@
use OCP\Share\IShare;
use OCP\Share\Exceptions\ShareNotFound;
-use Psr\Log\LoggerInterface;
+use OCA\CloudFederationAPI\Config;
+use OCA\Files_Trashbin\Trash\ITrashManager;
-use OCP\App\IAppManager;
-use OCP\Lock\ILockingProvider;
-use OCP\Lock\LockedException;
-use OCP\IL10N;
+use OCA\ScienceMesh\AppInfo\ScienceMeshApp;
+use OCA\ScienceMesh\ShareProvider\ScienceMeshShareProvider;
+
+use Psr\Log\LoggerInterface;
define('RESTRICT_TO_SCIENCEMESH_FOLDER', false);
-define('NEXTCLOUD_PREFIX', (RESTRICT_TO_SCIENCEMESH_FOLDER ? 'sciencemesh/' : ''));
+define('EFSS_PREFIX', (RESTRICT_TO_SCIENCEMESH_FOLDER ? 'sciencemesh/' : ''));
define('REVA_PREFIX', '/home/'); // See https://github.com/pondersource/sciencemesh-php/issues/96#issuecomment-1298656896
-class RevaController extends Controller {
+class RevaController extends Controller
+{
/* @var ISession */
private $session;
@@ -106,7 +107,7 @@ public function __construct(
ScienceMeshShareProvider $shareProvider
) {
parent::__construct($AppName, $request);
- require_once(__DIR__.'/../../vendor/autoload.php');
+ require_once(__DIR__ . '/../../vendor/autoload.php');
$this->rootFolder = $rootFolder;
$this->request = $request;
@@ -127,26 +128,67 @@ public function __construct(
$this->l = $l10n;
$this->shareProvider = $shareProvider;
}
- private function init($userId) {
- error_log("RevaController init");
+
+ private function init($userId)
+ {
+ error_log("RevaController init for user '$userId'");
$this->userId = $userId;
$this->checkRevadAuth();
if ($userId) {
- error_log("Getting user folder for '$userId'");
+ error_log("root folder absolute path '" . $this->rootFolder->getPath() . "'");
$this->userFolder = $this->rootFolder->getUserFolder($userId);
}
}
- private function revaPathToNextcloudPath($revaPath) {
- $ret = NEXTCLOUD_PREFIX . substr($revaPath, strlen(REVA_PREFIX));
- error_log("Interpreting $revaPath as $ret");
- // return $this->userFolder->getPath() . NEXTCLOUD_PREFIX . substr($revaPath, strlen(REVA_PREFIX));
- return $ret;
+ private function getDomainFromURL($url)
+ {
+ // converts https://revaowncloud1.docker/ to revaowncloud1.docker
+ // Note: DO not use it on anything whithout http(s) in the start, it would return null.
+ return str_ireplace("www.", "", parse_url($url, PHP_URL_HOST));
}
- private function nextcloudPathToRevaPath($nextcloudPath) {
- // return REVA_PREFIX . substr($nextcloudPath, strlen($this->userFolder->getPath() . NEXTCLOUD_PREFIX));
- return REVA_PREFIX . substr($nextcloudPath, strlen(NEXTCLOUD_PREFIX));
+ private function removePrefix($string, $prefix)
+ {
+ // first check if string is actually prefixed or not.
+ $len = strlen($prefix);
+ if (substr($string, 0, $len) === $prefix) {
+ $ret = substr($string, $len);
+ } else {
+ $ret = $string;
+ }
+
+ return $ret;
+ }
+
+ private function revaPathFromOpaqueId($opaqueId)
+ {
+ return $this->removePrefix($opaqueId, "fileid-");
+ }
+
+ private function revaPathToEfssPath($revaPath)
+ {
+ if ("$revaPath/" == REVA_PREFIX) {
+ error_log("revaPathToEfssPath: Interpreting special case $revaPath as ''");
+ return '';
+ }
+ $ret = EFSS_PREFIX . $this->removePrefix($revaPath, REVA_PREFIX);
+ error_log("revaPathToEfssPath: Interpreting $revaPath as $ret");
+ return $ret;
+ }
+
+ private function efssPathToRevaPath($efssPath)
+ {
+ $ret = REVA_PREFIX . $this->removePrefix($efssPath, EFSS_PREFIX);
+ error_log("efssPathToRevaPath: Interpreting $efssPath as $ret");
+ return $ret;
+ }
+
+ private function efssFullPathToRelativePath($efssFullPath)
+ {
+
+ $ret = $this->removePrefix($efssFullPath, $this->userFolder->getPath());
+ error_log("efssFullPathToRelativePath: Interpreting $efssFullPath as $ret");
+ return $ret;
}
/**
@@ -158,7 +200,8 @@ private function nextcloudPathToRevaPath($nextcloudPath) {
* @throws \OCP\Files\InvalidPathException
* @throws \OCP\Files\NotFoundException
*/
- private function lock(\OCP\Files\Node $node) {
+ private function lock(\OCP\Files\Node $node)
+ {
$node->lock(ILockingProvider::LOCK_SHARED);
$this->lockedNode = $node;
}
@@ -173,7 +216,8 @@ private function lock(\OCP\Files\Node $node) {
* @throws \Exception
* @return \DateTime
*/
- private function parseDate(string $expireDate): \DateTime {
+ private function parseDate(string $expireDate): \DateTime
+ {
try {
$date = new \DateTime($expireDate);
} catch (\Exception $e) {
@@ -184,77 +228,101 @@ private function parseDate(string $expireDate): \DateTime {
return $date;
}
- private function checkRevadAuth() {
+
+ private function checkRevadAuth()
+ {
error_log("checkRevadAuth");
$authHeader = $this->request->getHeader('X-Reva-Secret');
- if ($authHeader != $this->config->getRevaSharedSecret()) {
- throw new \OCP\Files\NotPermittedException('Please set an http request header "X-Reva-Secret: "!');
+
+ if ($authHeader != $this->config->getRevaSharedSecret()) {
+ throw new \OCP\Files\NotPermittedException('Please set an http request header "X-Reva-Secret: "!');
}
}
- private function getRevaPathFromOpaqueId($opaqueId) {
- return substr($opaqueId, strlen("fileid-"));
+
+ private function getChecksum(\OCP\Files\Node $node, $checksumType = 4): string
+ {
+ $checksumTypes = array(
+ 1 => "UNSET:",
+ 2 => "ADLER32:",
+ 3 => "MD5:",
+ 4 => "SHA1:",
+ );
+
+ // checksum is in db table oc_filecache.
+ // folders do not have checksum
+ $checksums = explode(' ', $node->getFileInfo()->getChecksum());
+
+ foreach ($checksums as $checksum) {
+
+ // Note that the use of !== false is deliberate (neither != false nor === true will return the desired result);
+ // strpos() returns either the offset at which the needle string begins in the haystack string, or the boolean
+ // false if the needle isn't found. Since 0 is a valid offset and 0 is "falsey", we can't use simpler constructs
+ // like !strpos($a, 'are').
+ if (strpos($checksum, $checksumTypes[$checksumType]) !== false) {
+ return substr($checksum, strlen($checksumTypes[$checksumType]));
+ }
+ }
+
+ return '';
}
- private function nodeToCS3ResourceInfo(\OCP\Files\Node $node) : array {
+ private function nodeToCS3ResourceInfo(\OCP\Files\Node $node, $token = ''): array
+ {
$isDirectory = ($node->getType() === \OCP\Files\FileInfo::TYPE_FOLDER);
- $nextcloudPath = substr($node->getPath(), strlen($this->userFolder->getPath()) + 1);
- $revaPath = $this->nextcloudPathToRevaPath($nextcloudPath);
- return [
- "opaque" => [
- "map" => null,
- ],
+ $efssPath = substr($node->getPath(), strlen($this->userFolder->getPath()) + 1);
+ $revaPath = $this->efssPathToRevaPath($efssPath);
+
+ $payload = [
"type" => ($isDirectory ? 2 : 1),
"id" => [
"opaque_id" => "fileid-" . $revaPath,
],
"checksum" => [
- "type" => 0,
- "sum" => "",
+ // checksum algorithms:
+ // 1 UNSET
+ // 2 ADLER32
+ // 3 MD5
+ // 4 SHA1
+
+ // note: folders do not have checksum, their type should be unset.
+ "type" => $isDirectory ? 1 : 4,
+ "sum" => $this->getChecksum($node, $isDirectory ? 1 : 4),
],
- "etag" => "deadbeef",
+ "etag" => $node->getEtag(),
"mime_type" => ($isDirectory ? "folder" : $node->getMimetype()),
"mtime" => [
"seconds" => $node->getMTime(),
],
"path" => $revaPath,
- "permission_set" => [
- "add_grant" => false,
- "create_container" => false,
- "delete" => false,
- "get_path" => false,
- "get_quota" => false,
- "initiate_file_download" => false,
- "initiate_file_upload" => false,
- ],
+ "permissions" => $node->getPermissions(),
"size" => $node->getSize(),
- "canonical_metadata" => [
- "target" => null,
- ],
- "arbitrary_metadata" => [
- "metadata" => [
- ".placeholder" => "ignore",
- ],
- ],
"owner" => [
"opaque_id" => $this->userId,
- "idp" => $this->config->getIopUrl(),
- ],
+ "idp" => $this->getDomainFromURL($this->config->getIopUrl()),
+ ]
];
+
+ error_log("nodeToCS3ResourceInfo " . var_export($payload, true));
+
+ return $payload;
}
# For ListReceivedShares, GetReceivedShare and UpdateReceivedShare we need to include "state:2"
- private function shareInfoToCs3Share(IShare $share): array {
+ private function shareInfoToCs3Share(IShare $share, $token = ''): array
+ {
$shareeParts = explode("@", $share->getSharedWith());
if (count($shareeParts) == 1) {
- $shareeParts = [ "unknown", "unknown" ];
- }
- $ownerParts = explode("@", $share->getShareOwner());
- if (count($ownerParts) == 1) {
- $ownerParts = [ "unknown", "unknown" ];
+ error_log("warning, could not find sharee user@host from '" . $share->getSharedWith() . "'");
+ $shareeParts = ["unknown", "unknown"];
}
+
+ $ownerParts = [$share->getShareOwner(), $this->getDomainFromURL($this->config->getIopUrl())];
+
$stime = 0; // $share->getShareTime()->getTimeStamp();
+
try {
- $opaqueId = "fileid-" . $share->getNode()->getPath();
+ $filePath = $share->getNode()->getPath();
+ $opaqueId = "fileid-" . $filePath;
} catch (\OCP\Files\NotFoundException $e) {
$opaqueId = "unknown";
}
@@ -263,57 +331,53 @@ private function shareInfoToCs3Share(IShare $share): array {
// https://github.com/cs3org/reva/blob/v1.18.0/pkg/ocm/share/manager/nextcloud/nextcloud.go#L77
// and
// https://github.com/cs3org/go-cs3apis/blob/d297419/cs3/sharing/ocm/v1beta1/resources.pb.go#L100
- return [
+ $payload = [
"id" => [
// https://github.com/cs3org/go-cs3apis/blob/d297419/cs3/sharing/ocm/v1beta1/resources.pb.go#L423
"opaque_id" => $share->getId()
],
"resource_id" => [
- "opaque_id" => $opaqueId,
- ],
- "permissions" => [
- "permissions" => [
- "add_grant" => false,
- "create_container" => false,
- "delete" => false,
- "get_path" => false,
- "get_quota" => false,
- "initiate_file_download" => false,
- "initiate_file_upload" => false,
- ]
+ "opaque_id" => $opaqueId,
],
+ "permissions" => $share->getNode()->getPermissions(),
// https://github.com/cs3org/go-cs3apis/blob/d29741980082ecd0f70fe10bd2e98cf75764e858/cs3/storage/provider/v1beta1/resources.pb.go#L897
"grantee" => [
"type" => 1, // https://github.com/cs3org/go-cs3apis/blob/d29741980082ecd0f70fe10bd2e98cf75764e858/cs3/storage/provider/v1beta1/resources.pb.go#L135
- "id" => [
+ "id" => [
"opaque_id" => $shareeParts[0],
"idp" => $shareeParts[1]
- ],
+ ],
],
"owner" => [
- "id" => [
+ "id" => [
"opaque_id" => $ownerParts[0],
"idp" => $ownerParts[1]
- ],
+ ],
],
"creator" => [
"id" => [
"opaque_id" => $ownerParts[0],
"idp" => $ownerParts[1]
- ],
+ ],
],
"ctime" => [
"seconds" => $stime
],
"mtime" => [
"seconds" => $stime
- ]
+ ],
+ "token" => $token
];
+
+ error_log("shareInfoToCs3Share " . var_export($payload, true));
+
+ return $payload;
}
# correspondes the permissions we got from Reva to Nextcloud
- private function getPermissionsCode(array $permissions) : int {
+ private function getPermissionsCode(array $permissions): int
+ {
$permissionsCode = 0;
if (!empty($permissions["get_path"]) || !empty($permissions["get_quota"]) || !empty($permissions["initiate_file_download"]) || !empty($permissions["initiate_file_upload"]) || !empty($permissions["stat"])) {
$permissionsCode += \OCP\Constants::PERMISSION_READ;
@@ -332,13 +396,15 @@ private function getPermissionsCode(array $permissions) : int {
}
return $permissionsCode;
}
+
/**
* @param int
*
* @return int
* @throws NotFoundException
*/
- private function getStorageUrl($userId) {
+ private function getStorageUrl($userId)
+ {
$storageUrl = $this->urlGenerator->getAbsoluteURL($this->urlGenerator->linkToRoute("sciencemesh.storage.handleHead", ["userId" => $userId, "path" => "foo"]));
$storageUrl = preg_replace('/foo$/', '', $storageUrl);
return $storageUrl;
@@ -352,61 +418,87 @@ private function getStorageUrl($userId) {
* @NoCSRFRequired
* @return Http\DataResponse|JSONResponse
*/
- public function AddGrant($userId) {
+ public function AddGrant($userId)
+ {
error_log("AddGrant");
if ($this->userManager->userExists($userId)) {
$this->init($userId);
} else {
return new JSONResponse("User not found", Http::STATUS_FORBIDDEN);
}
-
- $path = $this->revaPathToNextcloudPath($this->request->getParam("path"));
+
+ $path = $this->revaPathToEfssPath($this->request->getParam("path"));
// FIXME: Expected a param with a grant to add here;
+
return new JSONResponse("Not implemented", Http::STATUS_NOT_IMPLEMENTED);
}
- private function formatUser($user) {
+ private function formatUser($user)
+ {
return [
"id" => [
- "idp" => $this->config->getIopUrl(),
+ "idp" => $this->getDomainFromURL($this->config->getIopUrl()),
"opaque_id" => $user->getUID(),
],
"display_name" => $user->getDisplayName(),
+ "username" => $user->getUID(),
"email" => $user->getEmailAddress(),
"type" => 1,
];
}
+ private function formatFederatedUser($username, $remote)
+ {
+ return [
+ "id" => [
+ "idp" => $remote,
+ "opaque_id" => $username,
+ ],
+ "display_name" => $username, // FIXME: this comes in the OCM share payload
+ "username" => $username,
+ "email" => "unknown@unknown", // FIXME: this comes in the OCM share payload
+ "type" => 6,
+ ];
+ }
+
/**
* @PublicPage
* @NoAdminRequired
* @NoCSRFRequired
* @return Http\DataResponse|JSONResponse
*/
- public function Authenticate($userId) {
+ public function Authenticate($userId)
+ {
error_log("Authenticate");
if ($this->userManager->userExists($userId)) {
$this->init($userId);
} else {
- return new JSONResponse("User not found", Http::STATUS_FORBIDDEN);
+ $share = $this->shareProvider->getSentShareByToken($userId);
+ if ($share) {
+ $sharedWith = explode("@", $share->getSharedWith());
+ $result = [
+ "user" => $this->formatFederatedUser($sharedWith[0], $sharedWith[1]),
+ "scopes" => [],
+ ];
+ return new JSONResponse($result, Http::STATUS_OK);
+ } else {
+ return new JSONResponse("User not found", Http::STATUS_FORBIDDEN);
+ }
}
+
$userId = $this->request->getParam("clientID");
$password = $this->request->getParam("clientSecret");
// Try e.g.:
// curl -v -H 'Content-Type:application/json' -d'{"clientID":"einstein",clientSecret":"relativity"}' http://einstein:relativity@localhost/index.php/apps/sciencemesh/~einstein/api/auth/Authenticate
- // Ref https://github.com/cs3org/reva/issues/2356
+ // Ref https://github.com/cs3org/reva/issues/2356
if ($password == $this->config->getRevaLoopbackSecret()) {
$user = $this->userManager->get($userId);
} else {
- $user = $this->userManager->checkPassword($userId, $password);
+ $user = $this->userManager->checkPassword($userId, $password);
}
-
-
if ($user) {
- /// the `value` is a base64 encoded value of:
- /// {"resource_id":{"storage_id":"storage-id","opaque_id":"opaque-id"},"path":"some/file/path.txt"}
$result = [
"user" => $this->formatUser($user),
"scopes" => [
@@ -421,6 +513,7 @@ public function Authenticate($userId) {
];
return new JSONResponse($result, Http::STATUS_OK);
}
+
return new JSONResponse("Username / password not recognized", 401);
}
@@ -431,14 +524,18 @@ public function Authenticate($userId) {
* @return Http\DataResponse|JSONResponse
* @throws \OCP\Files\NotPermittedException
*/
- public function CreateDir($userId) {
+ public function CreateDir($userId)
+ {
error_log("CreateDir");
if ($this->userManager->userExists($userId)) {
$this->init($userId);
} else {
return new JSONResponse("User not found", Http::STATUS_FORBIDDEN);
}
- $path = $this->revaPathToNextcloudPath($this->request->getParam("path"));
+
+ $urlDecodedPath = urldecode($this->request->getParam("path"));
+ $path = $this->revaPathToEfssPath($urlDecodedPath);
+
try {
$this->userFolder->newFolder($path);
} catch (NotPermittedException $e) {
@@ -454,7 +551,8 @@ public function CreateDir($userId) {
* @return Http\DataResponse|JSONResponse
* @throws \OCP\Files\NotPermittedException
*/
- public function CreateHome($userId) {
+ public function CreateHome($userId)
+ {
error_log("CreateHome");
if (RESTRICT_TO_SCIENCEMESH_FOLDER) {
if ($this->userManager->userExists($userId)) {
@@ -481,7 +579,8 @@ public function CreateHome($userId) {
* @NoCSRFRequired
* @return Http\DataResponse|JSONResponse
*/
- public function CreateReference($userId) {
+ public function CreateReference($userId)
+ {
error_log("CreateReference");
if ($this->userManager->userExists($userId)) {
@@ -489,7 +588,7 @@ public function CreateReference($userId) {
} else {
return new JSONResponse("User not found", Http::STATUS_FORBIDDEN);
}
- $path = $this->revaPathToNextcloudPath($this->request->getParam("path"));
+ $path = $this->revaPathToEfssPath($this->request->getParam("path"));
return new JSONResponse("Not implemented", Http::STATUS_NOT_IMPLEMENTED);
}
@@ -499,7 +598,8 @@ public function CreateReference($userId) {
* @NoCSRFRequired
* @return Http\DataResponse|JSONResponse
*/
- public function CreateStorageSpace($userId) {
+ public function CreateStorageSpace($userId)
+ {
error_log("CreateStorageSpace");
return new JSONResponse([
"status" => [
@@ -551,14 +651,17 @@ public function CreateStorageSpace($userId) {
* @return Http\DataResponse|JSONResponse
* @throws FileNotFoundException
*/
- public function Delete($userId) {
+ public function Delete($userId)
+ {
error_log("Delete");
if ($this->userManager->userExists($userId)) {
$this->init($userId);
} else {
return new JSONResponse("User not found", Http::STATUS_FORBIDDEN);
}
- $path = $this->revaPathToNextcloudPath($this->request->getParam("path"));
+
+ $path = $this->revaPathToEfssPath($this->request->getParam("path"));
+
try {
$node = $this->userFolder->get($path);
$node->delete($path);
@@ -574,13 +677,16 @@ public function Delete($userId) {
* @NoCSRFRequired
* @return Http\DataResponse|JSONResponse
*/
- public function EmptyRecycle($userId) {
+ public function EmptyRecycle($userId)
+ {
+ // DIFFERENT FUNCTION IN NC/OC
error_log("EmptyRecycle");
if ($this->userManager->userExists($userId)) {
$this->init($userId);
} else {
return new JSONResponse("User not found", Http::STATUS_FORBIDDEN);
}
+
$user = $this->userManager->get($userId);
$trashItems = $this->trashManager->listTrashRoot($user);
@@ -600,34 +706,48 @@ public function EmptyRecycle($userId) {
* @NoCSRFRequired
* @return Http\DataResponse|JSONResponse
*/
- public function GetMD($userId) {
+ public function GetMD($userId)
+ {
if ($this->userManager->userExists($userId)) {
$this->init($userId);
} else {
return new JSONResponse("User not found", Http::STATUS_FORBIDDEN);
}
+
$ref = $this->request->getParam("ref");
error_log("GetMD " . var_export($ref, true));
+
if (isset($ref["path"])) {
- $revaPath = $ref["path"]; // e.g. GetMD {"ref":{"path":"/home/asdf"},"mdKeys":null}
- } else if (isset($ref["resource_id"]) && isset($ref["resource_id"]["opaque_id"]) && str_starts_with($ref["resource_id"]["opaque_id"], "fileid-/home/")) {
- $revaPath = substr($ref["resource_id"]["opaque_id"], strlen("fileid-")); // e.g. GetMD {"ref":{"resource_id":{"storage_id":"00000000-0000-0000-0000-000000000000","opaque_id":"fileid-/home/asdf"}},"mdKeys":null}
+ // e.g. GetMD {"ref": {"path": "/home/asdf"}, "mdKeys": null}
+ $revaPath = $ref["path"];
+ } else if (isset($ref["resource_id"]) && isset($ref["resource_id"]["opaque_id"]) && str_starts_with($ref["resource_id"]["opaque_id"], "fileid-")) {
+ // e.g. GetMD {"ref": {"resource_id": {"storage_id": "00000000-0000-0000-0000-000000000000", "opaque_id": "fileid-/asdf"}}, "mdKeys":null}
+ $revaPath = $this->revaPathFromOpaqueId($ref["resource_id"]["opaque_id"]);
} else {
throw new \Exception("ref not understood!");
}
- $path = $this->revaPathToNextcloudPath($revaPath);
- error_log("Looking for nc path '$path' in user folder; reva path '".$ref["path"]."' ");
- $dirContents = $this->userFolder->getDirectoryListing();
- $paths = array_map(function (\OCP\Files\Node $node) {
- return $node->getPath();
- }, $dirContents);
- error_log("User folder ".$this->userFolder->getPath()." has: " . implode(",", $paths));
- $success = $this->userFolder->nodeExists($path);
+
+ // this path is url coded, we need to decode it
+ // for example this converts "we%20have%20space" to "we have space"
+ $revaPathDecoded = urldecode($revaPath);
+
+ $path = $this->revaPathToEfssPath($revaPathDecoded);
+ error_log("Looking for EFSS path '$path' in user folder; reva path '$revaPathDecoded' ");
+
+ // apparently nodeExists requires relative path to the user folder:
+ // see https://github.com/owncloud/core/blob/b7bcbdd9edabf7d639b4bb42c4fb87862ddf4a80/lib/private/Files/Node/Folder.php#L45-L55;
+ // another string manipulation is necessary to extract relative path from full path.
+ $relativePath = $this->efssFullPathToRelativePath($path);
+
+ $success = $this->userFolder->nodeExists($relativePath);
if ($success) {
- $node = $this->userFolder->get($path);
+ error_log("File found");
+ $node = $this->userFolder->get($relativePath);
$resourceInfo = $this->nodeToCS3ResourceInfo($node);
return new JSONResponse($resourceInfo, Http::STATUS_OK);
}
+
+ error_log("File not found");
return new JSONResponse(["error" => "File not found"], 404);
}
@@ -637,7 +757,8 @@ public function GetMD($userId) {
* @NoCSRFRequired
* @return Http\DataResponse|JSONResponse
*/
- public function GetPathByID($userId) {
+ public function GetPathByID($userId)
+ {
if ($this->userManager->userExists($userId)) {
$this->init($userId);
} else {
@@ -647,6 +768,7 @@ public function GetPathByID($userId) {
$path = "subdir/";
$storageId = $this->request->getParam("storage_id");
$opaqueId = $this->request->getParam("opaque_id");
+
return new DataResponse($path, Http::STATUS_OK);
}
@@ -656,9 +778,11 @@ public function GetPathByID($userId) {
* @NoCSRFRequired
* @return Http\DataResponse|JSONResponse
*/
- public function InitiateUpload($userId) {
+ public function InitiateUpload($userId)
+ {
$ref = $this->request->getParam("ref");
- $path = $this->revaPathToNextcloudPath((isset($ref["path"]) ? $ref["path"] : ""));
+ $path = $this->revaPathToEfssPath((isset($ref["path"]) ? $ref["path"] : ""));
+
if ($this->userManager->userExists($userId)) {
$this->init($userId);
} else {
@@ -667,6 +791,7 @@ public function InitiateUpload($userId) {
$response = [
"simple" => $path
];
+
return new JSONResponse($response, Http::STATUS_OK);
}
@@ -676,18 +801,29 @@ public function InitiateUpload($userId) {
* @NoCSRFRequired
* @return Http\DataResponse|JSONResponse
*/
- public function ListFolder($userId) {
+ public function ListFolder($userId)
+ {
if ($this->userManager->userExists($userId)) {
$this->init($userId);
} else {
return new JSONResponse("User not found", Http::STATUS_FORBIDDEN);
}
+
$ref = $this->request->getParam("ref");
- $path = $this->revaPathToNextcloudPath((isset($ref["path"]) ? $ref["path"] : ""));
+
+ // this path is url coded, we need to decode it
+ // for example this converts "we%20have%20space" to "we have space"
+ $pathDecoded = urldecode((isset($ref["path"]) ? $ref["path"] : ""));
+ $path = $this->revaPathToEfssPath($pathDecoded);
$success = $this->userFolder->nodeExists($path);
+ error_log("ListFolder: $path");
+
if (!$success) {
+ error_log("ListFolder: path not found");
return new JSONResponse(["error" => "Folder not found"], 404);
}
+ error_log("ListFolder: path found");
+
$node = $this->userFolder->get($path);
$nodes = $node->getDirectoryListing();
$resourceInfos = array_map(function (\OCP\Files\Node $node) {
@@ -702,10 +838,17 @@ public function ListFolder($userId) {
* @NoCSRFRequired
* @return Http\DataResponse|JSONResponse
*/
- public function ListGrants($userId) {
- $this->init($userId);
- $path = $this->revaPathToNextcloudPath($this->request->getParam("path"));
- return new JSONResponse("Not implemented",Http::STATUS_NOT_IMPLEMENTED);
+ public function ListGrants($userId)
+ {
+ if ($this->userManager->userExists($userId)) {
+ $this->init($userId);
+ } else {
+ return new JSONResponse("User not found", Http::STATUS_FORBIDDEN);
+ }
+
+ $path = $this->revaPathToEfssPath($this->request->getParam("path"));
+
+ return new JSONResponse("Not implemented", Http::STATUS_NOT_IMPLEMENTED);
}
/**
@@ -714,18 +857,21 @@ public function ListGrants($userId) {
* @NoCSRFRequired
* @return Http\DataResponse|JSONResponse
*/
- public function ListRecycle($userId) {
+ public function ListRecycle($userId)
+ {
if ($this->userManager->userExists($userId)) {
$this->init($userId);
} else {
return new JSONResponse("User not found", Http::STATUS_FORBIDDEN);
}
+
$user = $this->userManager->get($userId);
$trashItems = $this->trashManager->listTrashRoot($user);
$result = [];
+
foreach ($trashItems as $node) {
if (preg_match("/^sciencemesh/", $node->getOriginalLocation())) {
- $path = $this->nextcloudPathToRevaPath($node->getOriginalLocation());
+ $path = $this->efssPathToRevaPath($node->getOriginalLocation());
$result = [
[
"opaque" => [
@@ -742,9 +888,11 @@ public function ListRecycle($userId) {
"deletion_time" => [
"seconds" => 1234567890
]
- ]];
+ ]
+ ];
}
}
+
return new JSONResponse($result, Http::STATUS_OK);
}
@@ -754,14 +902,17 @@ public function ListRecycle($userId) {
* @NoCSRFRequired
* @return Http\DataResponse|JSONResponse
*/
- public function ListRevisions($userId) {
+ public function ListRevisions($userId)
+ {
if ($this->userManager->userExists($userId)) {
$this->init($userId);
} else {
return new JSONResponse("User not found", Http::STATUS_FORBIDDEN);
}
- $path = $this->revaPathToNextcloudPath($this->request->getParam("path"));
- return new JSONResponse("Not implemented",Http::STATUS_NOT_IMPLEMENTED);
+
+ $path = $this->revaPathToEfssPath($this->request->getParam("path"));
+
+ return new JSONResponse("Not implemented", Http::STATUS_NOT_IMPLEMENTED);
}
/**
@@ -770,14 +921,17 @@ public function ListRevisions($userId) {
* @NoCSRFRequired
* @return Http\DataResponse|JSONResponse
*/
- public function RemoveGrant($userId) {
+ public function RemoveGrant($userId)
+ {
if ($this->userManager->userExists($userId)) {
$this->init($userId);
} else {
return new JSONResponse("User not found", Http::STATUS_FORBIDDEN);
}
- $path = $this->revaPathToNextcloudPath($this->request->getParam("path"));
+
+ $path = $this->revaPathToEfssPath($this->request->getParam("path"));
// FIXME: Expected a grant to remove here;
+
return new JSONResponse("Not implemented", Http::STATUS_NOT_IMPLEMENTED);
}
@@ -787,12 +941,14 @@ public function RemoveGrant($userId) {
* @NoCSRFRequired
* @return Http\DataResponse|JSONResponse
*/
- public function RestoreRecycleItem($userId) {
+ public function RestoreRecycleItem($userId)
+ {
if ($this->userManager->userExists($userId)) {
$this->init($userId);
} else {
return new JSONResponse("User not found", Http::STATUS_FORBIDDEN);
}
+
$key = $this->request->getParam("key");
$user = $this->userManager->get($userId);
$trashItems = $this->trashManager->listTrashRoot($user);
@@ -803,12 +959,13 @@ public function RestoreRecycleItem($userId) {
// unique key string, see:
// https://github.com/cs3org/cs3apis/blob/6eab4643f5113a54f4ce4cd8cb462685d0cdd2ef/cs3/storage/provider/v1beta1/resources.proto#L318
- if ($this->revaPathToNextcloudPath($key) == $node->getOriginalLocation()) {
+ if ($this->revaPathToEfssPath($key) == $node->getOriginalLocation()) {
$this->trashManager->restoreItem($node);
return new JSONResponse("OK", Http::STATUS_OK);
}
}
}
+
return new JSONResponse('["error" => "Not found."]', 404);
}
@@ -818,14 +975,17 @@ public function RestoreRecycleItem($userId) {
* @NoCSRFRequired
* @return Http\DataResponse|JSONResponse
*/
- public function RestoreRevision($userId) {
+ public function RestoreRevision($userId)
+ {
if ($this->userManager->userExists($userId)) {
$this->init($userId);
} else {
return new JSONResponse("User not found", Http::STATUS_FORBIDDEN);
}
- $path = $this->revaPathToNextcloudPath($this->request->getParam("path"));
+
+ $path = $this->revaPathToEfssPath($this->request->getParam("path"));
// FIXME: Expected a revision param here;
+
return new JSONResponse("Not implemented", Http::STATUS_NOT_IMPLEMENTED);
}
@@ -835,16 +995,20 @@ public function RestoreRevision($userId) {
* @NoCSRFRequired
* @return Http\DataResponse|JSONResponse
*/
- public function SetArbitraryMetadata($userId) {
+ public function SetArbitraryMetadata($userId)
+ {
if ($this->userManager->userExists($userId)) {
$this->init($userId);
} else {
return new JSONResponse("User not found", Http::STATUS_FORBIDDEN);
}
- $path = $this->revaPathToNextcloudPath($this->request->getParam("path"));
+
+ $path = $this->revaPathToEfssPath($this->request->getParam("path"));
$metadata = $this->request->getParam("metadata");
- // FIXME: What do we do with the existing metadata? Just toss it and overwrite with the new value? Or do we merge?
- return new JSONResponse("Not implemented", Http::STATUS_NOT_IMPLEMENTED);
+
+ // FIXME: this needs to be implemented for real, merging the incoming metadata with the existing ones.
+ // For now we return OK to let the uploads go through, see https://github.com/sciencemesh/nc-sciencemesh/issues/43
+ return new JSONResponse("I'm cheating", Http::STATUS_OK);
}
/**
@@ -853,13 +1017,17 @@ public function SetArbitraryMetadata($userId) {
* @NoCSRFRequired
* @return Http\DataResponse|JSONResponse
*/
- public function UnsetArbitraryMetadata($userId) {
+ public function UnsetArbitraryMetadata($userId)
+ {
if ($this->userManager->userExists($userId)) {
$this->init($userId);
} else {
return new JSONResponse("User not found", Http::STATUS_FORBIDDEN);
}
- $path = $this->revaPathToNextcloudPath($this->request->getParam("path"));
+
+ $path = $this->revaPathToEfssPath($this->request->getParam("path"));
+
+ // FIXME: this needs to be implemented for real
return new JSONResponse("Not implemented", Http::STATUS_NOT_IMPLEMENTED);
}
@@ -869,13 +1037,16 @@ public function UnsetArbitraryMetadata($userId) {
* @NoCSRFRequired
* @return Http\DataResponse|JSONResponse
*/
- public function UpdateGrant($userId) {
+ public function UpdateGrant($userId)
+ {
if ($this->userManager->userExists($userId)) {
$this->init($userId);
} else {
return new JSONResponse("User not found", Http::STATUS_FORBIDDEN);
}
- $path = $this->revaPathToNextcloudPath($this->request->getParam("path"));
+
+ $path = $this->revaPathToEfssPath($this->request->getParam("path"));
+
// FIXME: Expected a paramater with the grant(s)
return new JSONResponse("Not implemented", Http::STATUS_NOT_IMPLEMENTED);
}
@@ -891,7 +1062,8 @@ public function UpdateGrant($userId) {
*
* @throws \OCP\Files\InvalidPathException
*/
- private function write($path, $contents, Config $config) {
+ private function write($path, $contents, Config $config)
+ {
try {
if ($this->userFolder->nodeExists($path)) {
$node = $this->userFolder->get($path);
@@ -908,38 +1080,511 @@ private function write($path, $contents, Config $config) {
* @PublicPage
* @NoAdminRequired
* @NoCSRFRequired
- * @return Http\DataResponse|JSONResponse
+ * @return JSONResponse|StreamResponse
+ * @throws NotFoundException
*/
- public function Upload($userId, $path) {
+ public function Download($userId, $path)
+ {
+ error_log("Download");
+ if ($this->userManager->userExists($userId)) {
+ $this->init($userId);
+ } else {
+ return new JSONResponse("User not found", Http::STATUS_FORBIDDEN);
+ }
+
+ error_log("Download path: $path");
+
+ $efssPath = $this->removePrefix($path, "home/");
+ error_log("Download efss path: $efssPath");
+
+ if ($this->userFolder->nodeExists($efssPath)) {
+ error_log("Download: file found");
+ $node = $this->userFolder->get($efssPath);
+ $view = new View();
+ $nodeLocalFilePath = $view->getLocalFile($node->getPath());
+ error_log("Download local file path: $nodeLocalFilePath");
+ return new StreamResponse($nodeLocalFilePath);
+ }
+
+ error_log("Download: file not found");
+ return new JSONResponse(["error" => "File not found"], 404);
+ }
+
+ /**
+ * @PublicPage
+ * @NoAdminRequired
+ * @NoCSRFRequired
+ * @param $userId
+ * @param $path
+ * @return JSONResponse
+ */
+ public function Upload($userId, $path): JSONResponse
+ {
$revaPath = "/$path";
- error_log("RevaController Upload! $userId $revaPath");
+ error_log("RevaController Upload! user: $userId , reva path: $revaPath");
+
try {
if ($this->userManager->userExists($userId)) {
$this->init($userId);
} else {
return new JSONResponse("User not found", Http::STATUS_FORBIDDEN);
}
- $contents = $entityBody = file_get_contents('php://input');
- // error_log("PUT body = " . var_export($contents, true));
- error_log("Uploading! $revaPath");
- $ncPath = $this->revaPathToNextcloudPath($revaPath);
- if ($this->userFolder->nodeExists($ncPath)) {
- $node = $this->userFolder->get($ncPath);
- $node->putContent($contents);
+
+ $contents = file_get_contents('php://input');
+ $efssPath = $this->revaPathToEfssPath($revaPath);
+
+ error_log("Uploading! reva path: $revaPath");
+ error_log("Uploading! efss path $efssPath");
+
+ if ($this->userFolder->nodeExists($efssPath)) {
+ $node = $this->userFolder->get($efssPath);
+ $view = new View();
+ $view->file_put_contents($node->getPath(), $contents);
return new JSONResponse("OK", Http::STATUS_OK);
} else {
- $filename = basename($ncPath);
- $dirname = dirname($ncPath);
+ $dirname = dirname($efssPath);
+ $filename = basename($efssPath);
+
if (!$this->userFolder->nodeExists($dirname)) {
$this->userFolder->newFolder($dirname);
}
+
$node = $this->userFolder->get($dirname);
- $node->newFile($filename, $contents);
- return new JSONResponse("CREATED", Http::STATUS_CREATED);
+ $node->newFile($filename);
+
+ $node = $this->userFolder->get($efssPath);
+ $view = new View();
+ $view->file_put_contents($node->getPath(), $contents);
+
+ return new JSONResponse("CREATED", Http::STATUS_CREATED);
}
} catch (\Exception $e) {
return new JSONResponse(["error" => "Upload failed"], Http::STATUS_INTERNAL_SERVER_ERROR);
}
}
-
+
+ /**
+ * @PublicPage
+ * @NoCSRFRequired
+ * @NoSameSiteCookieRequired
+ *
+ * Get user list.
+ */
+ public function GetUser($dummy)
+ {
+ $this->init(false);
+
+ $userToCheck = $this->request->getParam('opaque_id');
+
+ if ($this->userManager->userExists($userToCheck)) {
+ $user = $this->userManager->get($userToCheck);
+ $response = $this->formatUser($user);
+ return new JSONResponse($response, Http::STATUS_OK);
+ }
+
+ return new JSONResponse(
+ ['message' => 'User does not exist'],
+ Http::STATUS_NOT_FOUND
+ );
+ }
+
+ /**
+ * @PublicPage
+ * @NoCSRFRequired
+ * @NoSameSiteCookieRequired
+ *
+ * Get user by claim.
+ */
+ public function GetUserByClaim($dummy)
+ {
+ $this->init(false);
+
+ $userToCheck = $this->request->getParam('value');
+
+ if ($this->request->getParam('claim') == 'username') {
+ error_log("GetUserByClaim, claim = 'username', value = $userToCheck");
+ } else {
+ return new JSONResponse('Please set the claim to username', Http::STATUS_BAD_REQUEST);
+ }
+
+ if ($this->userManager->userExists($userToCheck)) {
+ $user = $this->userManager->get($userToCheck);
+ $response = $this->formatUser($user);
+ return new JSONResponse($response, Http::STATUS_OK);
+ }
+
+ return new JSONResponse(
+ ['message' => 'User does not exist'],
+ Http::STATUS_NOT_FOUND
+ );
+ }
+
+ /**
+ * @PublicPage
+ * @NoCSRFRequired
+ * @return Http\DataResponse|JSONResponse
+ *
+ * @throws NotFoundException
+ * @throws OCSNotFoundException
+ * Create a new share in fn with the given access control list.
+ */
+
+ public function addSentShare($userId)
+ {
+ if ($this->userManager->userExists($userId)) {
+ $this->init($userId);
+ } else {
+ return new JSONResponse("User not found", Http::STATUS_FORBIDDEN);
+ }
+
+ $params = $this->request->getParams();
+ error_log("addSentShare " . var_export($params, true));
+
+ $owner = $params["owner"]["opaqueId"]; // . "@" . $params["owner"]["idp"];
+ $name = $params["name"]; // "fileid-/other/q/f gr"
+ $resourceOpaqueId = $params["resourceId"]["opaqueId"]; // "fileid-/other/q/f gr"
+ $revaPath = $this->revaPathFromOpaqueId($resourceOpaqueId); // "/other/q/f gr"
+ $efssPath = $this->revaPathToEfssPath($revaPath);
+
+ $revaPermissions = null;
+
+ foreach ($params['accessMethods'] as $accessMethod) {
+ if (isset($accessMethod['webdavOptions'])) {
+ $revaPermissions = $accessMethod['webdavOptions']['permissions'];
+ break;
+ }
+ }
+
+ if (!isset($revaPermissions)) {
+ throw new \Exception('reva permissions not found');
+ }
+
+ $granteeType = $params["grantee"]["type"]; // "GRANTEE_TYPE_USER"
+ $granteeHost = $params["grantee"]["userId"]["idp"]; // "revanc2.docker"
+ $granteeUser = $params["grantee"]["userId"]["opaqueId"]; // "marie"
+
+ if ($revaPermissions === null) {
+ $revaPermissions = [
+ "initiate_file_download" => true
+ ];
+ }
+ $efssPermissions = $this->getPermissionsCode($revaPermissions);
+ $shareWith = $granteeUser . "@" . $granteeHost;
+ $sharedSecret = $params["token"];
+
+ try {
+ $node = $this->userFolder->get($efssPath);
+ } catch (NotFoundException $e) {
+ return new JSONResponse(["error" => "Share failed. Resource Path not found"], Http::STATUS_BAD_REQUEST);
+ }
+
+ error_log("calling newShare");
+ $share = $this->shareManager->newShare();
+ $share->setNode($node);
+
+ try {
+ $this->lock($share->getNode());
+ } catch (LockedException $e) {
+ throw new OCSNotFoundException($this->l->t('Could not create share'));
+ }
+
+ $share->setShareType(IShare::TYPE_SCIENCEMESH);
+ $share->setSharedBy($userId);
+ $share->setSharedWith($shareWith);
+ $share->setShareOwner($owner);
+ $share->setPermissions($efssPermissions);
+ $share->setToken($sharedSecret);
+ $share = $this->shareProvider->createInternal($share);
+
+ return new DataResponse($share->getId(), Http::STATUS_CREATED);
+ }
+
+ /**
+ * add a received share
+ *
+ * @NoCSRFRequired
+ * @PublicPage
+ * @return Http\DataResponse|JSONResponse
+ */
+ public function addReceivedShare($userId)
+ {
+ $params = $this->request->getParams();
+ error_log("addReceivedShare " . var_export($params, true));
+ foreach ($params['protocols'] as $protocol) {
+ if (isset($protocol['webdavOptions'])) {
+ $sharedSecret = $protocol['webdavOptions']['sharedSecret'];
+ // make sure you have webdav_endpoint = "https://nc1.docker/" under
+ // [grpc.services.ocmshareprovider] in the sending Reva's config
+ $uri = $protocol['webdavOptions']['uri']; // e.g. https://nc1.docker/remote.php/dav/ocm/vaKE36Wf1lJWCvpDcRQUScraVP5quhzA
+ $remote = implode('/', array_slice(explode('/', $uri), 0, 3)); // e.g. https://nc1.docker
+ break;
+ }
+ }
+ if (!isset($sharedSecret)) {
+ throw new \Exception('sharedSecret not found');
+ }
+
+ $shareData = [
+ "remote" => $remote, //https://nc1.docker
+ "remote_id" => $params["remoteShareId"], // the id of the share in the oc_share table of the remote.
+ "share_token" => $sharedSecret, // 'tDPRTrLI4hE3C5T'
+ "password" => "",
+ "name" => rtrim($params["name"], "/"), // '/grfe'
+ "owner" => $params["owner"]["opaqueId"], // 'einstein'
+ "user" => $userId // 'marie'
+ ];
+
+ if ($this->userManager->userExists($userId)) {
+ $this->init($userId);
+ } else {
+ return new JSONResponse("User not found", Http::STATUS_FORBIDDEN);
+ }
+
+ $scienceMeshData = [
+ "is_external" => true,
+ ];
+
+ $id = $this->shareProvider->addScienceMeshShare($scienceMeshData, $shareData);
+ return new JSONResponse($id, 201);
+ }
+
+ /**
+ * @PublicPage
+ * @NoCSRFRequired
+ * @return Http\DataResponse|JSONResponse
+ *
+ *
+ * Remove Share from share table
+ */
+ public function Unshare($userId)
+ {
+ error_log("Unshare");
+ if ($this->userManager->userExists($userId)) {
+ $this->init($userId);
+ } else {
+ return new JSONResponse("User not found", Http::STATUS_FORBIDDEN);
+ }
+
+ $opaqueId = $this->request->getParam("Spec")["Id"]["opaque_id"];
+ $name = $this->getNameByOpaqueId($opaqueId);
+
+ if ($this->shareProvider->deleteSentShareByName($userId, $name)) {
+ return new JSONResponse("Deleted Sent Share", Http::STATUS_OK);
+ } else {
+ if ($this->shareProvider->deleteReceivedShareByOpaqueId($userId, $opaqueId)) {
+ return new JSONResponse("Deleted Received Share", Http::STATUS_OK);
+ } else {
+ return new JSONResponse("Could not find share", Http::STATUS_BAD_REQUEST);
+ }
+ }
+ }
+
+ /**
+ * @PublicPage
+ * @NoCSRFRequired
+ * @return Http\DataResponse|JSONResponse
+ *
+ */
+ public function UpdateSentShare($userId)
+ {
+ error_log("UpdateSentShare");
+ if ($this->userManager->userExists($userId)) {
+ $this->init($userId);
+ } else {
+ return new JSONResponse("User not found", Http::STATUS_FORBIDDEN);
+ }
+ $opaqueId = $this->request->getParam("ref")["Spec"]["Id"]["opaque_id"];
+ $permissions = $this->request->getParam("p")["permissions"];
+ $permissionsCode = $this->getPermissionsCode($permissions);
+ $name = $this->getNameByOpaqueId($opaqueId);
+ if (!($share = $this->shareProvider->getSentShareByName($userId, $name))) {
+ return new JSONResponse(["error" => "UpdateSentShare failed"], Http::STATUS_INTERNAL_SERVER_ERROR);
+ }
+ $share->setPermissions($permissionsCode);
+ $shareUpdated = $this->shareProvider->update($share);
+ $response = $this->shareInfoToCs3Share($shareUpdated);
+ return new JSONResponse($response, Http::STATUS_OK);
+ }
+
+ /**
+ * @PublicPage
+ * @NoCSRFRequired
+ * @return Http\DataResponse|JSONResponse
+ *
+ * UpdateReceivedShare updates the received share with share state.
+ */
+ public function UpdateReceivedShare($userId)
+ {
+ error_log("UpdateReceivedShare");
+ if ($this->userManager->userExists($userId)) {
+ $this->init($userId);
+ } else {
+ return new JSONResponse("User not found", Http::STATUS_FORBIDDEN);
+ }
+
+ $response = [];
+ $resourceId = $this->request->getParam("received_share")["share"]["resource_id"];
+ $permissions = $this->request->getParam("received_share")["share"]["permissions"];
+ $permissionsCode = $this->getPermissionsCode($permissions);
+
+ try {
+ $share = $this->shareProvider->getReceivedShareByToken($resourceId);
+ $share->setPermissions($permissionsCode);
+ $shareUpdate = $this->shareProvider->UpdateReceivedShare($share);
+ $response = $this->shareInfoToCs3Share($shareUpdate, $resourceId);
+ $response["state"] = 2;
+ return new JSONResponse($response, Http::STATUS_OK);
+ } catch (\Exception $e) {
+ return new JSONResponse(["error" => $e->getMessage()], Http::STATUS_INTERNAL_SERVER_ERROR);
+ }
+ }
+
+ /**
+ * @PublicPage
+ * @NoCSRFRequired
+ * @return Http\DataResponse|JSONResponse
+ *
+ * ListSentShares returns the shares created by the user. If md is provided is not nil,
+ * it returns only shares attached to the given resource.
+ */
+ public function ListSentShares($userId)
+ {
+ error_log("ListSentShares");
+ if ($this->userManager->userExists($userId)) {
+ $this->init($userId);
+ } else {
+ return new JSONResponse("User not found", Http::STATUS_FORBIDDEN);
+ }
+
+ $responses = [];
+ $shares = $this->shareProvider->getSentShares($userId);
+
+ if ($shares) {
+ foreach ($shares as $share) {
+ array_push($responses, $this->shareInfoToCs3Share($share));
+ }
+ }
+ return new JSONResponse($responses, Http::STATUS_OK);
+ }
+
+ /**
+ * @PublicPage
+ * @NoCSRFRequired
+ * @return Http\DataResponse|JSONResponse
+ * ListReceivedShares returns the list of shares the user has access.
+ */
+ public function ListReceivedShares($userId)
+ {
+ error_log("ListReceivedShares");
+ if ($this->userManager->userExists($userId)) {
+ $this->init($userId);
+ } else {
+ return new JSONResponse("User not found", Http::STATUS_FORBIDDEN);
+ }
+
+ $responses = [];
+ $shares = $this->shareProvider->getReceivedShares($userId);
+
+ if ($shares) {
+ foreach ($shares as $share) {
+ $response = $this->shareInfoToCs3Share($share);
+ array_push($responses, [
+ "share" => $response,
+ "state" => 2
+ ]);
+ }
+ }
+
+ return new JSONResponse($responses, Http::STATUS_OK);
+ }
+
+ /**
+ * @PublicPage
+ * @NoCSRFRequired
+ * @return Http\DataResponse|JSONResponse
+ *
+ * GetReceivedShare returns the information for a received share the user has access.
+ */
+ public function GetReceivedShare($userId)
+ {
+ error_log("GetReceivedShare");
+ if ($this->userManager->userExists($userId)) {
+ $this->init($userId);
+ } else {
+ return new JSONResponse("User not found", Http::STATUS_FORBIDDEN);
+ }
+
+ $opaqueId = $this->request->getParam("Spec")["Id"]["opaque_id"];
+ $name = $this->getNameByOpaqueId($opaqueId);
+
+ try {
+ $share = $this->shareProvider->getReceivedShareByToken($opaqueId);
+ $response = $this->shareInfoToCs3Share($share, $opaqueId);
+ $response["state"] = 2;
+ return new JSONResponse($response, Http::STATUS_OK);
+ } catch (\Exception $e) {
+ return new JSONResponse(["error" => $e->getMessage()], Http::STATUS_BAD_REQUEST);
+ }
+ }
+
+ /**
+ * @PublicPage
+ * @NoCSRFRequired
+ * @return Http\DataResponse|JSONResponse
+ *
+ * GetSentShare gets the information for a share by the given ref.
+ */
+ public function GetSentShare($userId)
+ {
+ error_log("GetSentShare");
+ if ($this->userManager->userExists($userId)) {
+ $this->init($userId);
+ } else {
+ return new JSONResponse("User not found", Http::STATUS_FORBIDDEN);
+ }
+
+ $opaqueId = $this->request->getParam("Spec")["Id"]["opaque_id"];
+ $name = $this->getNameByOpaqueId($opaqueId);
+ $share = $this->shareProvider->getSentShareByName($userId, $name);
+
+ if ($share) {
+ $response = $this->shareInfoToCs3Share($share);
+ return new JSONResponse($response, Http::STATUS_OK);
+ }
+
+ return new JSONResponse(["error" => "GetSentShare failed"], Http::STATUS_NOT_FOUND);
+ }
+
+ /**
+ * @PublicPage
+ * @NoCSRFRequired
+ * @return Http\DataResponse|JSONResponse
+ *
+ * GetSentShareByToken gets the information for a share by the given token.
+ */
+ public function GetSentShareByToken($userId)
+ {
+ error_log("GetSentShareByToken: user is -> $userId");
+
+ // See: https://github.com/cs3org/reva/pull/4115#discussion_r1308371946
+ if ($userId !== "nobody") {
+ if ($this->userManager->userExists($userId)) {
+ $this->init($userId);
+ } else {
+ return new JSONResponse("User not found", Http::STATUS_FORBIDDEN);
+ }
+ }
+
+ $token = $this->request->getParam("Spec")["Token"];
+ error_log("GetSentShareByToken: " . var_export($this->request->getParam("Spec"), true));
+
+ $share = $this->shareProvider->getSentShareByToken($token);
+
+ if ($share) {
+ $response = $this->shareInfoToCs3Share($share, $token);
+ return new JSONResponse($response, Http::STATUS_OK);
+ }
+
+ return new JSONResponse(["error" => "GetSentShare failed"], Http::STATUS_BAD_REQUEST);
+ }
}
diff --git a/lib/ShareProvider/ScienceMeshShareProvider.php b/lib/ShareProvider/ScienceMeshShareProvider.php
index 333318df..50eeafa5 100644
--- a/lib/ShareProvider/ScienceMeshShareProvider.php
+++ b/lib/ShareProvider/ScienceMeshShareProvider.php
@@ -112,6 +112,45 @@ public function isShareTypeSupported($shareType) {
return in_array($shareType, $this->supportedShareType);
}
+ /**
+ * Share a path
+ *
+ * @param IShare $share
+ * @return IShare The share object
+ * @throws ShareNotFound
+ * @throws \Exception
+ */
+ public function createInternal(IShare $share) {
+ error_log("SSP create");
+ $shareWith = $share->getSharedWith();
+
+ /*
+ * Check if file is not already shared with the remote user
+ */
+ $alreadyShared = $this->getSharedWith($shareWith, $this::SHARE_TYPE_REMOTE, $share->getNode(), 1, 0);
+ if (!empty($alreadyShared)) {
+ $message = 'Sharing %1$s failed, because this item is already shared with %2$s';
+ $message_t = $this->l->t('Sharing %1$s failed, because this item is already shared with user %2$s', [$share->getNode()->getName(), $shareWith]);
+ $this->logger->debug(sprintf($message, $share->getNode()->getName(), $shareWith), ['app' => 'ScienceMesh']);
+ throw new \Exception($message_t);
+ }
+
+ // FIXME: don't allow ScienceMesh shares if source and target server are the same
+ // ScienceMesh shares always have read permissions
+ if (($share->getPermissions() & Constants::PERMISSION_READ) === 0) {
+ $message = 'ScienceMesh shares require read permissions';
+ $message_t = $this->l->t('ScienceMesh shares require read permissions');
+ $this->logger->debug($message, ['app' => 'ScienceMesh']);
+ throw new \Exception($message_t);
+ }
+
+ $share->setSharedWith($shareWith);
+ $shareId = $this->createScienceMeshShare($share);
+ $data = $this->getRawShare($shareId);
+
+ return $this->createShareObject($data);
+ }
+
/**
* Share a path
*
@@ -134,6 +173,14 @@ public function create(IShare $share) {
$sourcePath = $prefix . "home/" . implode("/", array_slice($pathParts, $sourceOffset)) . $suffix;
$targetPath = $prefix . implode("/", array_slice($pathParts, $targetOffset)) . $suffix;
+ // TODO: make a function for below operation. it is used in a lot placed, but incorrectly.
+ // it should split username@host into an array of 2 element
+ // representing array[0] = username, array[1] = host
+ // requirement:
+ // handle usernames with multiple @ in them.
+ // example: MahdiBaghbani@pondersource@sciencemesh.org
+ // uername: MahdiBaghbani@pondersource
+ // host: sciencemesh.org
$split_point = '@';
$parts = explode($split_point, $shareWith);
$last = array_pop($parts);
@@ -158,45 +205,6 @@ public function create(IShare $share) {
return $share;
}
- /**
- * Share a path
- *
- * @param IShare $share
- * @return IShare The share object
- * @throws ShareNotFound
- * @throws \Exception
- */
- public function createInternal(IShare $share) {
- error_log("SSP create");
- $shareWith = $share->getSharedWith();
-
- /*
- * Check if file is not already shared with the remote user
- */
- $alreadyShared = $this->getSharedWith($shareWith, $this::SHARE_TYPE_REMOTE, $share->getNode(), 1, 0);
- if (!empty($alreadyShared)) {
- $message = 'Sharing %1$s failed, because this item is already shared with %2$s';
- $message_t = $this->l->t('Sharing %1$s failed, because this item is already shared with user %2$s', [$share->getNode()->getName(), $shareWith]);
- $this->logger->debug(sprintf($message, $share->getNode()->getName(), $shareWith), ['app' => 'ScienceMesh']);
- throw new \Exception($message_t);
- }
-
- // FIXME: don't allow ScienceMesh shares if source and target server are the same
- // ScienceMesh shares always have read permissions
- if (($share->getPermissions() & Constants::PERMISSION_READ) === 0) {
- $message = 'ScienceMesh shares require read permissions';
- $message_t = $this->l->t('ScienceMesh shares require read permissions');
- $this->logger->debug($message, ['app' => 'ScienceMesh']);
- throw new \Exception($message_t);
- }
-
- $share->setSharedWith($shareWith);
- $shareId = $this->createScienceMeshShare($share);
- $data = $this->getRawShare($shareId);
-
- return $this->createShareObject($data);
- }
-
/**
* create sciencemesh share and inform the recipient
*
@@ -340,6 +348,10 @@ public function delete(IShare $share) {
$this->removeShareFromTable($share);
}
+ // DIFFERENT FUNCTION IN NC/OC
+ // function revokeShare exists in OC but in NC.
+ // TODO: why?
+
/**
* Get a share by token
*
@@ -367,6 +379,35 @@ public function getReceivedShareByToken($token) {
return $share;
}
+ /**
+ * Get a share by token
+ *
+ * @param string $token
+ * @return IShare
+ * @throws ShareNotFound
+ */
+ public function getSentShareByToken($token) {
+ error_log("share provider getSentShareByToken '$token'");
+ $qb = $this->dbConnection->getQueryBuilder();
+ $cursor = $qb->select('*')
+ ->from('share')
+ ->where($qb->expr()->eq('token', $qb->createNamedParameter($token)))
+ ->execute();
+ $data = $cursor->fetch();
+ if ($data === false) {
+ error_log("sent share not found by token '$token'");
+ throw new ShareNotFound('Share not found', $this->l->t('Could not find share'));
+ }
+ try {
+ $share = $this->createShareObject($data);
+ } catch (InvalidShare $e) {
+ error_log("sent share found invalid by token '$token'");
+ throw new ShareNotFound('Share not found', $this->l->t('Could not find share'));
+ }
+ error_log("found sent share ". $data["id"] . " by token '$token'");
+ return $share;
+ }
+
/**
* Create a share object from a database row from external shares
*
@@ -419,19 +460,14 @@ public function getSentShares($userId): iterable {
}
public function getReceivedShares($userId): iterable {
- error_log("listing received shares for user id '$userId'!");
$qb = $this->dbConnection->getQueryBuilder();
$qb->select('*')
->from('share_external')
->where(
- // $qb->expr()->eq('share_type', $qb->createNamedParameter($this::SHARE_TYPE_REMOTE))
- // )
- // ->andWhere(
$qb->expr()->eq('user', $qb->createNamedParameter($userId))
);
$cursor = $qb->execute();
while ($data = $cursor->fetch()) {
- error_log("received share!");
try {
$share = $this->createExternalShareObject($data);
} catch (InvalidShare $e) {
@@ -442,7 +478,6 @@ public function getReceivedShares($userId): iterable {
yield $share;
}
- error_log("done listing received shares!");
$cursor->closeCursor();
}
@@ -570,7 +605,7 @@ public function getShareByOpaqueId($opaqueId) {
return false;
}
// FIXME: side effect?
- $res = $external?$this->createScienceMeshExternalShare($data):$this->createScienceMeshShare($data);
+ $res = $external ? $this->createScienceMeshExternalShare($data) : $this->createScienceMeshShare($data);
return $res;
}