diff --git a/appinfo/Migrations/Version20230916.php b/appinfo/Migrations/Version20230916.php
new file mode 100644
index 00000000..66b91b68
--- /dev/null
+++ b/appinfo/Migrations/Version20230916.php
@@ -0,0 +1,191 @@
+
+ */
+
+namespace OCA\ScienceMesh\Migrations;
+
+use Doctrine\DBAL\Schema\Schema;
+use OCP\Migration\ISchemaMigration;
+
+/** Creates initial schema */
+class Version20230916 implements ISchemaMigration
+{
+ public function changeSchema(Schema $schema, array $options)
+ {
+ $prefix = $options["tablePrefix"];
+
+ // ocm_received_shares table.
+ if (!$schema->hasTable("{$prefix}sciencemesh_ocm_received_shares")) {
+ $table = $schema->createTable("{$prefix}sciencemesh_ocm_received_shares");
+
+ $table->addColumn("id", "bigint", [
+ "autoincrement" => true,
+ "unsigned" => true,
+ "notnull" => true,
+ "length" => 11,
+ ]);
+
+ $table->addColumn("share_external_id", "bigint", [
+ "unsigned" => false,
+ "notnull" => true,
+ "length" => 11,
+ ]);
+
+ $table->addColumn("name", "string", [
+ "length" => 255,
+ "notnull" => true,
+ "comment" => "Original name on the remote server"
+ ]);
+
+ $table->addColumn("share_with", "string", [
+ "length" => 255,
+ "notnull" => true,
+ ]);
+
+ $table->addColumn("owner", "string", [
+ "length" => 255,
+ "notnull" => true,
+ ]);
+
+ $table->addColumn("initiator", "string", [
+ "length" => 255,
+ "notnull" => true,
+ ]);
+
+ $table->addColumn("ctime", "bigint", [
+ "unsigned" => false,
+ "notnull" => true,
+ "length" => 11,
+ ]);
+
+ $table->addColumn("mtime", "bigint", [
+ "unsigned" => false,
+ "notnull" => true,
+ "length" => 11,
+ ]);
+
+ $table->addColumn("expiration", "bigint", [
+ "unsigned" => false,
+ "notnull" => false,
+ "default" => null,
+ "length" => 11,
+ ]);
+
+ $table->addColumn("remote_share_id", "string", [
+ "length" => 255,
+ "notnull" => false,
+ "default" => null,
+ "comment" => "share ID at the remote server"
+ ]);
+
+ $table->setPrimaryKey(["id"]);
+
+ $table->addUniqueIndex(
+ ["share_external_id"],
+ "sm_ocm_rx_ex_id_idx"
+ );
+ $table->addUniqueIndex(
+ ["share_with"],
+ "sm_ocm_rx_sh_w_idx"
+ );
+ }
+
+ // ocm_protocol_transfer table.
+ if (!$schema->hasTable("{$prefix}sciencemesh_ocm_received_share_protocol_transfer")) {
+ $table = $schema->createTable("{$prefix}sciencemesh_ocm_received_share_protocol_transfer");
+
+ $table->addColumn("ocm_received_share_id", "bigint", [
+ "unsigned" => true,
+ "notnull" => true,
+ "length" => 11,
+ ]);
+
+ $table->addColumn("source_uri", "string", [
+ "length" => 255,
+ "notnull" => true,
+ ]);
+
+ $table->addColumn("shared_secret", "string", [
+ "length" => 255,
+ "notnull" => true,
+ ]);
+
+ $table->addColumn("size", "bigint", [
+ "unsigned" => false,
+ "notnull" => true,
+ "length" => 11,
+ ]);
+
+ $table->addUniqueIndex(
+ ["ocm_received_share_id"],
+ "sm_ocm_rx_share_id_tx_idx"
+ );
+ }
+
+ // ocm_protocol_webapp table.
+ if (!$schema->hasTable("{$prefix}sciencemesh_ocm_received_share_protocol_webapp")) {
+ $table = $schema->createTable("{$prefix}sciencemesh_ocm_received_share_protocol_webapp");
+
+ $table->addColumn("ocm_received_share_id", "bigint", [
+ "unsigned" => true,
+ "notnull" => true,
+ "length" => 11,
+ ]);
+
+ $table->addColumn("uri_template", "string", [
+ "length" => 255,
+ "notnull" => true,
+ ]);
+
+ $table->addColumn("view_mode", "bigint", [
+ "unsigned" => false,
+ "notnull" => true,
+ "length" => 11,
+ ]);
+
+ $table->addUniqueIndex(
+ ["ocm_received_share_id"],
+ "sm_ocm_rx_share_id_app_idx"
+ );
+ }
+
+ // ocm_protocol_webdav table.
+ if (!$schema->hasTable("{$prefix}sciencemesh_ocm_received_share_protocol_webdav")) {
+ $table = $schema->createTable("{$prefix}sciencemesh_ocm_received_share_protocol_webdav");
+
+ $table->addColumn("ocm_received_share_id", "bigint", [
+ "unsigned" => true,
+ "notnull" => true,
+ "length" => 11,
+ ]);
+
+ $table->addColumn("uri", "string", [
+ "length" => 255,
+ "notnull" => true,
+ ]);
+
+ $table->addColumn("shared_secret", "string", [
+ "length" => 255,
+ "notnull" => true,
+ ]);
+
+ $table->addColumn("permissions", "bigint", [
+ "unsigned" => false,
+ "notnull" => true,
+ "length" => 11,
+ ]);
+
+ $table->addUniqueIndex(
+ ["ocm_received_share_id"],
+ "sm_ocm_rx_share_id_dav_idx"
+ );
+ }
+ }
+}
diff --git a/appinfo/Migrations/Version20230917.php b/appinfo/Migrations/Version20230917.php
new file mode 100644
index 00000000..32810759
--- /dev/null
+++ b/appinfo/Migrations/Version20230917.php
@@ -0,0 +1,184 @@
+
+ */
+
+namespace OCA\ScienceMesh\Migrations;
+
+use Doctrine\DBAL\Schema\Schema;
+use OCP\Migration\ISchemaMigration;
+
+/** Creates initial schema */
+class Version20230917 implements ISchemaMigration
+{
+ public function changeSchema(Schema $schema, array $options)
+ {
+ $prefix = $options["tablePrefix"];
+
+ // ocm_sent_shares table.
+ if (!$schema->hasTable("{$prefix}sciencemesh_ocm_sent_shares")) {
+ $table = $schema->createTable("{$prefix}sciencemesh_ocm_sent_shares");
+
+ $table->addColumn("id", "bigint", [
+ "autoincrement" => true,
+ "unsigned" => true,
+ "notnull" => true,
+ "length" => 11,
+ ]);
+
+ $table->addColumn("share_internal_id", "bigint", [
+ "unsigned" => false,
+ "notnull" => true,
+ "length" => 11,
+ ]);
+
+ $table->addColumn("name", "string", [
+ "length" => 255,
+ "notnull" => true,
+ "comment" => "Original name on the sending server"
+ ]);
+
+ $table->addColumn("share_with", "string", [
+ "length" => 255,
+ "notnull" => true,
+ ]);
+
+ $table->addColumn("owner", "string", [
+ "length" => 255,
+ "notnull" => true,
+ ]);
+
+ $table->addColumn("initiator", "string", [
+ "length" => 255,
+ "notnull" => true,
+ ]);
+
+ $table->addColumn("ctime", "bigint", [
+ "unsigned" => false,
+ "notnull" => true,
+ "length" => 11,
+ ]);
+
+ $table->addColumn("mtime", "bigint", [
+ "unsigned" => false,
+ "notnull" => true,
+ "length" => 11,
+ ]);
+
+ $table->addColumn("expiration", "bigint", [
+ "unsigned" => false,
+ "notnull" => false,
+ "default" => null,
+ "length" => 11,
+ ]);
+
+ $table->setPrimaryKey(["id"]);
+
+ $table->addUniqueIndex(
+ ["share_internal_id"],
+ "sm_ocm_tx_in_id_idx"
+ );
+ $table->addUniqueIndex(
+ ["share_with"],
+ "sm_ocm_rx_sh_w_idx"
+ );
+ }
+
+ // ocm_protocol_transfer table.
+ if (!$schema->hasTable("{$prefix}sciencemesh_ocm_sent_share_protocol_transfer")) {
+ $table = $schema->createTable("{$prefix}sciencemesh_ocm_sent_share_protocol_transfer");
+
+ $table->addColumn("ocm_sent_share_id", "bigint", [
+ "unsigned" => true,
+ "notnull" => true,
+ "length" => 11,
+ ]);
+
+ $table->addColumn("source_uri", "string", [
+ "length" => 255,
+ "notnull" => true,
+ ]);
+
+ $table->addColumn("shared_secret", "string", [
+ "length" => 255,
+ "notnull" => true,
+ ]);
+
+ $table->addColumn("size", "bigint", [
+ "unsigned" => false,
+ "notnull" => true,
+ "length" => 11,
+ ]);
+
+ $table->addUniqueIndex(
+ ["ocm_sent_share_id"],
+ "sm_ocm_tx_share_id_tx_idx"
+ );
+ }
+
+ // ocm_protocol_webapp table.
+ if (!$schema->hasTable("{$prefix}sciencemesh_ocm_sent_share_protocol_webapp")) {
+ $table = $schema->createTable("{$prefix}sciencemesh_ocm_sent_share_protocol_webapp");
+
+ $table->addColumn("ocm_sent_share_id", "bigint", [
+ "unsigned" => true,
+ "notnull" => true,
+ "length" => 11,
+ ]);
+
+ $table->addColumn("uri_template", "string", [
+ "length" => 255,
+ "notnull" => true,
+ ]);
+
+ $table->addColumn("view_mode", "bigint", [
+ "unsigned" => false,
+ "notnull" => true,
+ "length" => 11,
+ ]);
+
+ $table->addUniqueIndex(
+ ["ocm_sent_share_id"],
+ "sm_ocm_tx_share_id_app_idx"
+ );
+ }
+
+ // ocm_protocol_webdav table.
+ if (!$schema->hasTable("{$prefix}sciencemesh_ocm_sent_share_protocol_webdav")) {
+ $table = $schema->createTable("{$prefix}sciencemesh_ocm_sent_share_protocol_webdav");
+
+ $table->addColumn("ocm_sent_share_id", "bigint", [
+ "unsigned" => true,
+ "notnull" => true,
+ "length" => 11,
+ ]);
+
+ $table->addColumn("uri", "string", [
+ "length" => 255,
+ "notnull" => true,
+ ]);
+
+ $table->addColumn("shared_secret", "string", [
+ "length" => 255,
+ "notnull" => true,
+ ]);
+
+ $table->addColumn("permissions", "bigint", [
+ "unsigned" => false,
+ "notnull" => true,
+ "length" => 11,
+ ]);
+
+ $table->addUniqueIndex(
+ ["ocm_sent_share_id"],
+ "sm_ocm_tx_share_id_dav_idx"
+ );
+ }
+ }
+}
diff --git a/appinfo/info.xml b/appinfo/info.xml
index f78cc4ed..9027ae9f 100644
--- a/appinfo/info.xml
+++ b/appinfo/info.xml
@@ -40,6 +40,8 @@
OCA\ScienceMesh\Sections\SciencemeshSettingsAdmin
+ true
+
ScienceMesh
diff --git a/appinfo/routes.php b/appinfo/routes.php
index eb840bad..7fc82375 100644
--- a/appinfo/routes.php
+++ b/appinfo/routes.php
@@ -37,20 +37,24 @@
['name' => 'reva#UpdateShare', 'url' => '/~{userId}/api/ocm/UpdateShare', 'verb' => 'POST'],
// TODO: @Mahdi why do we have alias here? check with @Giuseppe and Reva EFSS code.
+ // check in reva code, and make it to use clear names like ListSentShares and ListRxShares
['name' => 'reva#ListSentShares', 'url' => '/~{userId}/api/ocm/ListSentShares', 'verb' => 'POST'],
- // alias for ListSentShares.
+ // alias for ListSentShares. https://github.com/cs3org/reva/blob/76d29f92b4872df37d7c3ac78f6a1574df1d320d/pkg/ocm/share/repository/nextcloud/nextcloud.go#L267
['name' => 'reva#ListSentShares', 'url' => '/~{userId}/api/ocm/ListShares', 'verb' => 'POST'],
['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'],
+ # TODO: @Mahdi move them to user controller.
+ ['name' => 'reva#GetUser', 'url' => '/~{dummy}/api/user/GetUser', 'verb' => 'POST'],
+ ['name' => 'reva#GetUserByClaim', 'url' => '/~{dummy}/api/user/GetUserByClaim', 'verb' => 'POST'],
+
// storage routes.
['name' => 'reva#AddGrant', 'url' => '/~{userId}/api/storage/AddGrant', 'verb' => 'POST'],
['name' => 'reva#CreateDir', 'url' => '/~{userId}/api/storage/CreateDir', 'verb' => 'POST'],
@@ -77,12 +81,14 @@
['name' => 'reva#Upload', 'url' => '/~{userId}/api/storage/Upload/{path}', 'verb' => 'PUT', 'requirements' => ['path' => '.+']],
// TODO: @Mahdi Are these used anywhere in Reva?
+ /*
// files routes.
['name' => 'storage#handleGet', 'url' => '/~{userId}/files/{path}', 'verb' => 'GET', 'requirements' => ['path' => '.+']],
['name' => 'storage#handlePost', 'url' => '/~{userId}/files/{path}', 'verb' => 'POST', 'requirements' => ['path' => '.+']],
['name' => 'storage#handlePut', 'url' => '/~{userId}/files/{path}', 'verb' => 'PUT', 'requirements' => ['path' => '.+']],
['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'],
diff --git a/flow.md b/flow.md
new file mode 100644
index 00000000..ccf34578
--- /dev/null
+++ b/flow.md
@@ -0,0 +1,191 @@
+### Creating a share
+
+
+
+## Sender
+
+Search for contacts, it goes through the search plugin and finds all the contacts you have made.
+
+example output:
+```json
+{
+ "display_name": "marie",
+ "idp": "revaowncloud2.docker",
+ "user_id": "marie",
+ "mail": ""
+}
+```
+
+click on contact to create a share. this step appends postfix for sm shares to distinguish them
+from regular federated shares.
+
+Call share provider `create`, this is called by share manager, share manager calls share provider factory which has
+been overridden to be sm share provider.
+
+sm share provider will route sm share and regular federated shares based on the appended postfix.
+
+sm share goes to: revaHttpClient->createShare
+
+example request:
+```json
+{
+ "sourcePath": "\\/home\\/test\\/",
+ "targetPath": "\\/test\\/",
+ "type": "dir",
+ "recipientUsername": "marie",
+ "recipientHost": "revaowncloud2.docker",
+ "role": "viewer"
+}
+```
+
+reva received the request and does these calls in order:
+1. "POST /index.php/apps/sciencemesh/~einstein/api/auth/Authenticate HTTP/1.1"
+2. "POST /index.php/apps/sciencemesh/~einstein/api/storage/CreateHome HTTP/1.1"
+3. "POST /index.php/apps/sciencemesh/~einstein/api/storage/GetMD HTTP/1.1" 200
+sm response:
+```php
+array (
+ 'type' => 2,
+ 'id' =>
+ array (
+ 'opaque_id' => 'fileid-/home/test',
+ ),
+ 'checksum' =>
+ array (
+ 'type' => 1,
+ 'sum' => '',
+ ),
+ 'etag' => '65129328493b0',
+ 'mime_type' => 'folder',
+ 'mtime' =>
+ array (
+ 'seconds' => 1695716136,
+ ),
+ 'path' => '/home/test',
+ 'permissions' => 31,
+ 'size' => 0,
+ 'owner' =>
+ array (
+ 'opaque_id' => 'einstein',
+ 'idp' => 'revaowncloud1.docker',
+ ),
+)
+```
+4. "POST /index.php/apps/sciencemesh/~einstein/api/storage/GetMD HTTP/1.1" 200
+sm response:
+```php
+array (
+ 'type' => 2,
+ 'id' =>
+ array (
+ 'opaque_id' => 'fileid-/home/test',
+ ),
+ 'checksum' =>
+ array (
+ 'type' => 1,
+ 'sum' => '',
+ ),
+ 'etag' => '65129328493b0',
+ 'mime_type' => 'folder',
+ 'mtime' =>
+ array (
+ 'seconds' => 1695716136,
+ ),
+ 'path' => '/home/test',
+ 'permissions' => 31,
+ 'size' => 0,
+ 'owner' =>
+ array (
+ 'opaque_id' => 'einstein',
+ 'idp' => 'revaowncloud1.docker',
+ ),
+)
+```
+5. "POST /index.php/apps/sciencemesh/~einstein/api/ocm/addSentShare HTTP/1.1" 201
+reva payload:
+```php
+array (
+ 'userId' => 'einstein',
+ '_route' => 'sciencemesh.reva.addSentShare',
+ 'resourceId' =>
+ array (
+ 'storageId' => 'nextcloud',
+ 'opaqueId' => 'fileid-/home/test',
+ ),
+ 'name' => 'test',
+ 'token' => 'Y7bWUulmHrhfUJ8LRknNpkZQGcRRkMk7',
+ 'grantee' =>
+ array (
+ 'type' => 'GRANTEE_TYPE_USER',
+ 'userId' =>
+ array (
+ 'idp' => 'revaowncloud2.docker',
+ 'opaqueId' => 'marie',
+ ),
+ ),
+ 'owner' =>
+ array (
+ 'idp' => 'revaowncloud1.docker',
+ 'opaqueId' => 'einstein',
+ 'type' => 'USER_TYPE_PRIMARY',
+ ),
+ 'creator' =>
+ array (
+ 'idp' => 'revaowncloud1.docker',
+ 'opaqueId' => 'einstein',
+ ),
+ 'ctime' =>
+ array (
+ 'seconds' => '1695716163',
+ 'nanos' => 943856286,
+ ),
+ 'mtime' =>
+ array (
+ 'seconds' => '1695716163',
+ 'nanos' => 943856286,
+ ),
+ 'shareType' => 'SHARE_TYPE_USER',
+ 'accessMethods' =>
+ array (
+ 0 =>
+ array (
+ 'webdavOptions' =>
+ array (
+ 'permissions' =>
+ array (
+ 'getPath' => true,
+ 'getQuota' => true,
+ 'initiateFileDownload' => true,
+ 'listGrants' => true,
+ 'listContainer' => true,
+ 'listFileVersions' => true,
+ 'listRecycle' => true,
+ 'stat' => true,
+ ),
+ ),
+ ),
+ 1 =>
+ array (
+ 'webappOptions' =>
+ array (
+ 'viewMode' => 'VIEW_MODE_READ_ONLY',
+ ),
+ ),
+ ),
+)
+```
+
+sm app creates share object via sm share provider `createInternal` that inserts share into efss native db.
+
+probably owncloud would inform the recepient by these request (should dig deeper to be sure):
+"POST /ocs/v2.php/apps/files_sharing/api/v1/shares?format=json HTTP/1.1" 200
+"GET /ocs/v2.php/apps/files_sharing/api/v1/shares?format=json&path=%2Ftest&reshares=true
+
+
+## Receiver
+
+Reva will call these in order:
+1. "POST /index.php/apps/sciencemesh/~unauthenticated/api/user/GetUser HTTP/1.1" 200
+2. "POST /index.php/apps/sciencemesh/~marie/api/ocm/addReceivedShare HTTP/1.1" 400
+
+FIX: 400 in addReceivedShare
diff --git a/lib/Controller/RevaController.php b/lib/Controller/RevaController.php
index 3570ce22..c37ce673 100644
--- a/lib/Controller/RevaController.php
+++ b/lib/Controller/RevaController.php
@@ -34,6 +34,8 @@
use OCP\Files\NotFoundException;
use OCP\Files\NotPermittedException;
use OCP\IConfig;
+use OCP\IL10N;
+use OCP\ILogger;
use OCP\IRequest;
use OCP\IURLGenerator;
use OCP\IUserManager;
@@ -45,11 +47,11 @@
use OCP\Share\IShare;
use Symfony\Component\Filesystem\Exception\FileNotFoundException;
-define('RESTRICT_TO_SCIENCEMESH_FOLDER', false);
-define('EFSS_PREFIX', (RESTRICT_TO_SCIENCEMESH_FOLDER ? 'sciencemesh/' : ''));
+define("RESTRICT_TO_SCIENCEMESH_FOLDER", false);
+define("EFSS_PREFIX", (RESTRICT_TO_SCIENCEMESH_FOLDER ? "sciencemesh/" : ""));
// See https://github.com/pondersource/sciencemesh-php/issues/96#issuecomment-1298656896
-define('REVA_PREFIX', '/home/');
+define("REVA_PREFIX", "/home/");
class RevaController extends Controller
{
@@ -80,6 +82,27 @@ class RevaController extends Controller
/** @var Folder */
private Folder $userFolder;
+ /** @var IL10N */
+ private IL10N $l;
+
+ /** @var ILogger */
+ private ILogger $logger;
+
+ /**
+ * Reva Controller.
+ *
+ * @param string $appName
+ * @param IRootFolder $rootFolder
+ * @param IRequest $request
+ * @param IUserManager $userManager
+ * @param IURLGenerator $urlGenerator
+ * @param IConfig $config
+ * @param TrashBinManager $trashManager
+ * @param IManager $shareManager
+ * @param IL10N $l10n
+ * @param ILogger $logger
+ * @param ScienceMeshShareProvider $shareProvider
+ */
public function __construct(
string $appName,
IRootFolder $rootFolder,
@@ -89,11 +112,13 @@ public function __construct(
IConfig $config,
TrashBinManager $trashManager,
IManager $shareManager,
+ IL10N $l10n,
+ ILogger $logger,
ScienceMeshShareProvider $shareProvider
)
{
parent::__construct($appName, $request);
- require_once(__DIR__ . '/../../vendor/autoload.php');
+ require_once(__DIR__ . "/../../vendor/autoload.php");
$this->rootFolder = $rootFolder;
$this->request = $request;
@@ -102,6 +127,8 @@ public function __construct(
$this->config = new ServerConfig($config);
$this->trashManager = $trashManager;
$this->shareManager = $shareManager;
+ $this->l = $l10n;
+ $this->logger = $logger;
$this->shareProvider = $shareProvider;
}
@@ -194,40 +221,43 @@ private function removePrefix($string, $prefix)
* @return JSONResponse
* @throws NotPermittedException
* @throws ShareNotFound|IllegalIDChangeException
+ * @throws Exception
*/
public function Authenticate($userId): JSONResponse
{
- error_log("Authenticate");
+ error_log("Authenticate: " . $userId);
+
if ($this->userManager->userExists($userId)) {
$this->init($userId);
- } else {
- $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");
+ $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
- // 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
+ // see: https://github.com/cs3org/reva/issues/2356
+ if ($password == $this->config->getRevaLoopbackSecret()) {
+ // NOTE: @Mahdi, usually everything goes in this branch.
+ $user = $this->userManager->get($userId);
+ } else {
+ $user = $this->userManager->checkPassword($userId, $password);
+ }
- // see: https://github.com/cs3org/reva/issues/2356
- if ($password == $this->config->getRevaLoopbackSecret()) {
- $user = $this->userManager->get($userId);
} else {
- $user = $this->userManager->checkPassword($userId, $password);
+ $share = $this->shareProvider->getSentShareByToken($userId);
+ $userId = $share->getSharedBy();
+ $user = $this->userManager->get($userId);
}
+
if ($user) {
- // FIXME this hardcoded value represents {"resource_id":{"storage_id":"storage-id","opaque_id":"opaque-id"},"path":"some/file/path.txt"} and is not needed
+ // FIXME: @Mahdi this hardcoded value represents the json below and is not needed.
+ // {
+ // "resource_id": {
+ // "storage_id": "storage-id",
+ // "opaque_id": "opaque-id"
+ // },
+ // "path": "some/file/path.txt"
+ // }
$result = [
"user" => $this->formatUser($user),
"scopes" => [
@@ -240,14 +270,16 @@ public function Authenticate($userId): JSONResponse
],
],
];
+
return new JSONResponse($result, Http::STATUS_OK);
}
- return new JSONResponse("Username / password not recognized", 401);
+ return new JSONResponse("Username / password not recognized", Http::STATUS_UNAUTHORIZED);
}
/**
* GetSentShareByToken gets the information for a share by the given token.
+ *
* @PublicPage
* @NoCSRFRequired
* @param $userId
@@ -263,7 +295,7 @@ public function GetSentShareByToken($userId): JSONResponse
{
error_log("GetSentShareByToken: user is -> $userId");
- // See: https://github.com/cs3org/reva/pull/4115#discussion_r1308371946
+ // see: https://github.com/cs3org/reva/pull/4115#discussion_r1308371946
if ($userId !== "nobody") {
if ($this->userManager->userExists($userId)) {
$this->init($userId);
@@ -275,79 +307,150 @@ public function GetSentShareByToken($userId): JSONResponse
$token = $this->request->getParam("Spec")["Token"];
error_log("GetSentShareByToken: " . var_export($this->request->getParam("Spec"), true));
+ // TODO: @Mahdi handle in try catch block and send back correct responses.
$share = $this->shareProvider->getSentShareByToken($token);
if ($share) {
- $response = $this->shareInfoToCs3Share($share, $token);
+ $response = $this->shareInfoToCs3Share($share, "sent", $token);
return new JSONResponse($response, Http::STATUS_OK);
}
return new JSONResponse(["error" => "GetSentShare failed"], Http::STATUS_BAD_REQUEST);
}
+ // For ListReceivedShares, GetReceivedShare and UpdateReceivedShare we need to include "state:2"
+ // see:
+ // https://github.com/cs3org/cs3apis/blob/cfd1ad29fdf00c79c2a321de7b1a60d0725fe4e8/cs3/sharing/ocm/v1beta1/resources.proto#L160
/**
* @throws NotFoundException
* @throws InvalidPathException
*/
- private function shareInfoToCs3Share(IShare $share, $token = ''): array
+ private function shareInfoToCs3Share(IShare $share, string $direction, $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"];
+ $shareId = $share->getId();
+
+ // TODO @Mahdi use enums!
+ if ($direction === "sent") {
+ $ocmShareData = $this->shareProvider->getSentOcmShareFromSciencemeshTable($shareId);
+ $ocmShareProtocols = $this->shareProvider->getSentOcmShareProtocolsFromSciencemeshTable($ocmShareData["id"]);
+ } elseif ($direction === "received") {
+ $ocmShareData = $this->shareProvider->getReceivedOcmShareFromSciencemeshTable($shareId);
+ $ocmShareProtocols = $this->shareProvider->getReceivedOcmShareProtocolsFromSciencemeshTable($ocmShareData["id"]);
+ }
+
+ // use ocm payload stored in sciencemesh table. if it fails, use native efss share data.
+ // in case of total failure use "unknown".
+
+ // this one is obvious right?
+ if (isset($ocmShareData["share_with"])) {
+ $granteeParts = explode("@", $ocmShareData["share_with"]);
+ } else {
+ $granteeParts = explode("@", $share->getSharedWith());
+ }
+
+ if (count($granteeParts) != 2) {
+ $granteeParts = ["unknown", "unknown"];
+ }
+
+ // the original share owner (who owns the path that is shared)
+ if (isset($ocmShareData["owner"])) {
+ $ownerParts = explode("@", $ocmShareData["owner"]);
+ } else {
+ $ownerParts = explode("@", $share->getShareOwner());
+ }
+
+ if (count($granteeParts) != 2) {
+ $ownerParts = ["unknown", "unknown"];
}
- $ownerParts = [$share->getShareOwner(), $this->getDomainFromURL($this->config->getIopUrl())];
+ // NOTE: @Mahdi initiator/creator/sharedBy etc., whatever other names it has! means the share sharer!
+ // you can be owner and sharer, you can be someone who is re-sharing, in this case you are sharer but not owner
+ if (isset($ocmShareData["initiator"])) {
+ $creatorParts = explode("@", $ocmShareData["initiator"]);
+ } else {
+ $creatorParts = explode("@", $share->getSharedBy());
+ }
- $stime = 0; // $share->getShareTime()->getTimeStamp();
+ if (count($granteeParts) != 2) {
+ $creatorParts = ["unknown", "unknown"];
+ }
try {
$filePath = $share->getNode()->getPath();
+ // @Mahdi why is this hardcoded?
+ // @Giuseppe this should be something that doesn't change when file is moved!
$opaqueId = "fileid-" . $filePath;
} catch (NotFoundException $e) {
+ // @Mahdi why not just return status bad request or status not found?
+ // @Michiel sometimes you want to translate share object even if file doesn't exist.
$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
+ // TODO: @Mahdi update this comment to point at the Reva structure mappings for this json.
+ // produces JSON that maps to reva
$payload = [
+ // use OCM name, if null use efss share native name, if null fall back to "unknown"
+ "name" => $ocmShareData["name"] ?? ($share->getName() ?? "unknown"),
+ "token" => $token ?? "unknown",
+ // TODO: @Mahdi what permissions is the correct one? share permissions has different value than the share->node permissions.
+ // maybe use the ocmData for this one? needs testing for different scenarios to see which is the best/correct one.
+ "permissions" => $share->getNode()->getPermissions() ?? 0,
"id" => [
// https://github.com/cs3org/go-cs3apis/blob/d297419/cs3/sharing/ocm/v1beta1/resources.pb.go#L423
- "opaque_id" => $share->getId()
+ "opaque_id" => $shareId ?? "unknown",
],
"resource_id" => [
- "opaque_id" => $opaqueId
+ "opaque_id" => $opaqueId,
],
- "permissions" => $share->getNode()->getPermissions(),
- // https://github.com/cs3org/go-cs3apis/blob/d29741980082ecd0f70fe10bd2e98cf75764e858/cs3/storage/provider/v1beta1/resources.pb.go#L897
+ // these three have been already handled and don't need "unknown" default values.
"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]
+ "opaque_id" => $granteeParts[0],
+ "idp" => $granteeParts[1],
],
],
"owner" => [
"id" => [
"opaque_id" => $ownerParts[0],
- "idp" => $ownerParts[1]
+ "idp" => $ownerParts[1],
],
],
"creator" => [
"id" => [
- "opaque_id" => $ownerParts[0],
- "idp" => $ownerParts[1]
+ "opaque_id" => $creatorParts[0],
+ "idp" => $creatorParts[1],
],
],
+ // NOTE: make sure seconds type is int, otherwise Reva gives:
+ // error="json: cannot unmarshal string into Go struct field Timestamp.ctime.seconds of type uint64"
"ctime" => [
- "seconds" => $stime
+ "seconds" => isset($ocmShareData["ctime"]) ? (int)$ocmShareData["ctime"] : ($share->getShareTime()->getTimestamp() ?? 0)
],
"mtime" => [
- "seconds" => $stime
+ "seconds" => isset($ocmShareData["mtime"]) ? (int)$ocmShareData["ctime"] : ($share->getShareTime()->getTimestamp() ?? 0)
],
- "token" => $token
+ "access_methods" => [
+ "transfer" => [
+ "source_uri" => $ocmShareProtocols["transfer"]["source_uri"] ?? "unknown",
+ // TODO: @Mahdi this feels redundant, already included in top-level token and webdav shared_secret.
+ "shared_secret" => $ocmShareProtocols["transfer"]["shared_secret"] ?? "unknown",
+ // TODO: @Mahdi should the default value be an integer?
+ "size" => $ocmShareProtocols["transfer"]["size"] ?? "unknown",
+ ],
+ "webapp" => [
+ "uri_template" => $ocmShareProtocols["webapp"]["uri_template"] ?? "unknown",
+ "view_mode" => $ocmShareProtocols["webapp"]["view_mode"] ?? "unknown",
+ ],
+ "webdav" => [
+ // TODO: @Mahdi it is better to have sharedSecret and permissions in this part of code.
+ "uri" => $ocmShareProtocols["webdav"]["uri"] ?? "unknown",
+ // TODO: @Mahdi it is interesting this function accepts token as argument! is token different that the share secret?
+ // why do we have to pass token while the share object already has the information about token?
+ // $share->getToken();
+ "shared_secret" => $ocmShareProtocols["webdav"]["shared_secret"] ?? "unknown",
+ "permissions" => $ocmShareProtocols["webdav"]["permissions"] ?? "unknown",
+ ],
+ ]
];
error_log("shareInfoToCs3Share " . var_export($payload, true));
@@ -355,32 +458,20 @@ private function shareInfoToCs3Share(IShare $share, $token = ''): array
return $payload;
}
+ // TODO: @Mahdi Move to utils.
private function getDomainFromURL($url)
{
// converts https://revaowncloud1.docker/ to revaowncloud1.docker
- // Note: do not use it on anything without http(s) in the start, it would return null.
+ // NOTE: do not use it on anything without http(s) in the start, it would return null.
return str_ireplace("www.", "", parse_url($url, PHP_URL_HOST));
}
- private function formatFederatedUser($username, $remote): array
- {
- 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,
- ];
- }
-
+ // TODO: @Mahdi Move to utils.
private function formatUser($user): array
{
return [
"id" => [
- "idp" => $this->getDomainFromURL($this->config->getIopUrl()),
+ "idp" => $this->config->getIopIdp(),
"opaque_id" => $user->getUID(),
],
"display_name" => $user->getDisplayName(),
@@ -418,9 +509,6 @@ public function CreateDir($userId): JSONResponse
return new JSONResponse("OK", Http::STATUS_OK);
}
- // TODO: @Mahdi What does this even mean? what is state:2 ?
- // For ListReceivedShares, GetReceivedShare and UpdateReceivedShare we need to include "state:2"
-
/**
* @PublicPage
* @NoAdminRequired
@@ -451,9 +539,6 @@ public function CreateHome($userId): JSONResponse
return new JSONResponse("OK", Http::STATUS_OK);
}
- // TODO: @Mahdi what is this?
- // corresponds the permissions we got from Reva to Nextcloud
-
/**
* @PublicPage
* @NoAdminRequired
@@ -474,6 +559,8 @@ public function CreateReference($userId): JSONResponse
return new JSONResponse("Not implemented", Http::STATUS_NOT_IMPLEMENTED);
}
+ // TODO: @Mahdi maybe not used anymore.
+
/**
* @PublicPage
* @NoAdminRequired
@@ -568,7 +655,7 @@ public function Delete($userId): JSONResponse
*/
public function EmptyRecycle($userId): JSONResponse
{
- // DIFFERENT FUNCTION IN NC/OC
+ // TODO: @Mahdi fix this! DIFFERENT FUNCTION IN NC/OC
error_log("EmptyRecycle");
if ($this->userManager->userExists($userId)) {
$this->init($userId);
@@ -602,14 +689,37 @@ public function GetMD($userId): JSONResponse
$ref = $this->request->getParam("ref");
error_log("GetMD " . var_export($ref, true));
+ if (!isset($ref)) {
+ return new JSONResponse("ref not found in the request.", Http::STATUS_BAD_REQUEST);
+ }
+
if (isset($ref["path"])) {
- // e.g. GetMD {"ref": {"path": "/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}
+ } else if (
+ 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!");
+ return new JSONResponse("ref not understood!", Http::STATUS_BAD_REQUEST);
}
// this path is url coded, we need to decode it
@@ -671,7 +781,7 @@ private function nodeToCS3ResourceInfo(Node $node): array
// 3 MD5
// 4 SHA1
- // note: folders do not have checksum, their type should be unset.
+ // NOTE: folders do not have checksum, their type should be unset.
"type" => $isDirectory ? 1 : 4,
"sum" => $this->getChecksum($node, $isDirectory ? 1 : 4),
],
@@ -712,11 +822,11 @@ private function getChecksum(Node $node, $checksumType = 4): string
// checksum is in db table oc_filecache.
// folders do not have checksum
- $checksums = explode(' ', $node->getFileInfo()->getChecksum());
+ $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);
+ // 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 "false", we can't use simpler constructs
// like !strpos($a, 'are').
@@ -725,9 +835,11 @@ private function getChecksum(Node $node, $checksumType = 4): string
}
}
- return '';
+ return "";
}
+ // TODO: @Mahdi remove.
+
/**
* @PublicPage
* @NoAdminRequired
@@ -743,6 +855,7 @@ public function GetPathByID($userId)
return new JSONResponse("User not found", Http::STATUS_FORBIDDEN);
}
+ // TODO: @Mahdi what is "in progress"? what should be done here?
// in progress
$path = "subdir/";
$storageId = $this->request->getParam("storage_id");
@@ -1144,6 +1257,7 @@ public function Upload($userId, $path): JSONResponse
/**
* Get user list.
+ *
* @PublicPage
* @NoCSRFRequired
* @NoSameSiteCookieRequired
@@ -1169,6 +1283,7 @@ public function GetUser($dummy): JSONResponse
/**
* Get user by claim.
+ *
* @PublicPage
* @NoCSRFRequired
* @NoSameSiteCookieRequired
@@ -1201,6 +1316,7 @@ public function GetUserByClaim($dummy): JSONResponse
/**
* Create a new share in fn with the given access control list.
+ *
* @PublicPage
* @NoCSRFRequired
* @return Http\DataResponse|JSONResponse
@@ -1220,56 +1336,214 @@ public function addSentShare($userId)
$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"
+ $name = $params["name"] ?? null;
+ $token = $params["token"] ?? null;
+ $ctime = (int)$params["ctime"]["seconds"] ?? null;
+ $mtime = (int)$params["mtime"]["seconds"] ?? null;
+ $resourceId = $params["resourceId"]["opaqueId"] ?? null;
+ $payloadUserId = $params["userId"] ?? null;
+
+ if (!isset($name)) {
+ return new JSONResponse("name not found in the request.", Http::STATUS_BAD_REQUEST);
+ }
+ if (!isset($token)) {
+ return new JSONResponse("token not found in the request.", Http::STATUS_BAD_REQUEST);
+ }
+ if (!isset($ctime)) {
+ return new JSONResponse("ctime not found in the request.", Http::STATUS_BAD_REQUEST);
+ }
+ if (!isset($mtime)) {
+ return new JSONResponse("mtime not found in the request.", Http::STATUS_BAD_REQUEST);
+ }
+ if (!isset($resourceId)) {
+ return new JSONResponse("resourceId->opaqueId not found in the request.", Http::STATUS_BAD_REQUEST);
+ }
+ if (!isset($payloadUserId)) {
+ return new JSONResponse("userId not found in the request.", Http::STATUS_BAD_REQUEST);
+ }
+
+ // chained path conversions to verify this file exists in our server.
+ // "fileid-/home/test" -> "/home/test" -> "/test"
+ $revaPath = $this->revaPathFromOpaqueId($resourceId);
$efssPath = $this->revaPathToEfssPath($revaPath);
- $revaPermissions = null;
+ try {
+ $node = $this->userFolder->get($efssPath);
+ } catch (NotFoundException $e) {
+ return new JSONResponse("share failed. resource path not found.", Http::STATUS_BAD_REQUEST);
+ }
- foreach ($params['accessMethods'] as $accessMethod) {
- if (isset($accessMethod['webdavOptions'])) {
- $revaPermissions = $accessMethod['webdavOptions']['permissions'];
- break;
- }
+ if (isset($params["grantee"]["userId"])) {
+ $granteeIdp = $params["grantee"]["userId"]["idp"] ?? null;
+ $granteeOpaqueId = $params["grantee"]["userId"]["opaqueId"] ?? null;
+ }
+
+ if (isset($params["owner"])) {
+ $ownerIdp = $params["owner"]["idp"] ?? null;
+ $ownerOpaqueId = $params["owner"]["opaqueId"] ?? null;
+ }
+
+ if (isset($params["creator"])) {
+ $creatorIdp = $params["creator"]["idp"] ?? null;
+ $creatorOpaqueId = $params["creator"]["opaqueId"] ?? null;
+ }
+
+ if (!isset($granteeIdp)) {
+ return new JSONResponse("grantee idp not found in the request.", Http::STATUS_BAD_REQUEST);
+ }
+ if (!isset($granteeOpaqueId)) {
+ return new JSONResponse("grantee opaqueId not found in the request.", Http::STATUS_BAD_REQUEST);
+ }
+ if (!isset($ownerIdp)) {
+ return new JSONResponse("owner idp not found in the request.", Http::STATUS_BAD_REQUEST);
+ }
+ if (!isset($ownerOpaqueId)) {
+ return new JSONResponse("owner opaqueId not found in the request.", Http::STATUS_BAD_REQUEST);
+ }
+ if (!isset($creatorIdp)) {
+ return new JSONResponse("creator idp not found in the request.", Http::STATUS_BAD_REQUEST);
+ }
+ if (!isset($creatorOpaqueId)) {
+ return new JSONResponse("creator opaqueId not found in the request.", Http::STATUS_BAD_REQUEST);
}
- if (!isset($revaPermissions)) {
- throw new Exception('reva permissions not found');
+ // NOTE: @Mahdi this 3 variables should be exactly same as of now.
+ // maybe it would be subject to change in future but for now it is good to check these
+ // instead of blindly assuming they're same.
+ if ($userId !== $payloadUserId || $userId !== $creatorOpaqueId || $payloadUserId !== $creatorOpaqueId) {
+ return new JSONResponse(
+ "creator->opaqueId, userId and userId from the payload are mismatched.",
+ Http::STATUS_UNPROCESSABLE_ENTITY
+ );
}
- $granteeType = $params["grantee"]["type"]; // "GRANTEE_TYPE_USER"
- $granteeHost = $params["grantee"]["userId"]["idp"]; // "revanc2.docker"
- $granteeUser = $params["grantee"]["userId"]["opaqueId"]; // "marie"
+ // don't allow ScienceMesh shares if source and target server are the same.
+ // this means users with the same reva iop cannot share with each other via sciencemesh and
+ // should use their native efss capabilities to do so.
+ // see: https://github.com/sciencemesh/nc-sciencemesh/issues/57
+ if ($ownerIdp === $granteeIdp) {
+ $message = "Not allowed to create a ScienceMesh share for a user on the same server %s as sender %s.";
+ $this->logger->debug(
+ sprintf(
+ $message, $ownerIdp, $granteeIdp
+ ),
+ ["app" => "sciencemesh"]
+ );
+ return new JSONResponse(
+ "Not allowed to create a ScienceMesh share for a user on the same server %s as sender %s.",
+ Http::STATUS_UNPROCESSABLE_ENTITY
+ );
+ }
- $efssPermissions = $this->getPermissionsCode($revaPermissions);
- $shareWith = $granteeUser . "@" . $granteeHost;
- $sharedSecret = $params["token"];
+ if (!isset($params["accessMethods"])) {
+ return new JSONResponse("accessMethods not found in the request.", Http::STATUS_BAD_REQUEST);
+ }
- try {
- $node = $this->userFolder->get($efssPath);
- } catch (NotFoundException $e) {
- return new JSONResponse(["error" => "Share failed. Resource Path not found"], Http::STATUS_BAD_REQUEST);
+ $accessMethods = $params["accessMethods"];
+
+ // TODO: @Mahdi these one has problems, check and debug.
+ foreach ($accessMethods as $method) {
+ if (isset($method["transferOptions"])) {
+ $ocmProtocolTransfer = $method["transferOptions"];
+ }
+ if (isset($method["webappOptions"])) {
+ $ocmProtocolWebapp = $method["webappOptions"];
+ }
+ if (isset($method["webdavOptions"])) {
+ $ocmProtocolWebdav = $method["webdavOptions"];
+ }
}
- error_log("calling newShare");
+ if (!isset($ocmProtocolWebdav)) {
+ return new JSONResponse("webdavOptions not found in the request.", Http::STATUS_BAD_REQUEST);
+ }
+
+ // handle bad cases and eliminate null variables.
+ if (!isset($ocmProtocolWebdav["permissions"])) {
+ return new JSONResponse("webdavOptions permissions not found in the request.", Http::STATUS_BAD_REQUEST);
+ }
+
+ // convert permissions from array to integer.
+ $permissions = $this->getPermissionsCode($ocmProtocolWebdav["permissions"]);
+
+ // prepare data for adding to the native efss table.
$share = $this->shareManager->newShare();
$share->setNode($node);
+ $share->setShareType(ScienceMeshApp::SHARE_TYPE_SCIENCEMESH);
+ $share->setSharedWith($granteeOpaqueId . "@" . $granteeIdp);
+ $share->setShareOwner($ownerOpaqueId);
+ $share->setSharedBy($creatorOpaqueId);
+ $share->setPermissions($permissions);
+ $share->setToken($token);
+ $share->setShareTime(new DateTime("@$ctime"));
+
+ // check if file is not already shared with the remote user
+ $alreadyShared = $this->shareProvider->getSharedWith(
+ $share->getSharedWith(),
+ $share->getShareType(),
+ $share->getNode(),
+ 1,
+ 0
+ );
+
+ if (!empty($alreadyShared)) {
+ $message = "Sharing %s failed, because this item is already shared with %s";
+ $message_t = $this->l->t(
+ "Sharing %s failed, because this item is already shared with %s",
+ [$share->getNode()->getName(), $share->getSharedWith()]
+ );
+ $this->logger->debug(
+ sprintf(
+ $message, $share->getNode()->getName(), $share->getSharedWith()
+ ),
+ ["app" => "sciencemesh"]
+ );
+ return new JSONResponse($message_t, Http::STATUS_CONFLICT);
+ }
+
+ // 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']);
+ return new JSONResponse($message_t, Http::STATUS_UNPROCESSABLE_ENTITY);
+ }
+
$this->lock($share->getNode());
- $share->setShareType(ScienceMeshApp::SHARE_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);
+ // prepare share data for ocm
+ $share = $this->shareProvider->createNativeEfssScienceMeshShare($share);
+ $efssShareInternalId = $share->getId();
+
+ // prepare data for adding to the ScienceMesh OCM table.
+ // see: https://github.com/sciencemesh/nc-sciencemesh/issues/45
+
+ $expiration = $params["expiration"] ?? null;
+ if (isset($expiration)) {
+ $expiration = (int)$expiration;
+ }
+
+ $ocmShareData = [
+ "share_internal_id" => $efssShareInternalId,
+ "name" => $name,
+ "share_with" => $granteeOpaqueId . "@" . $granteeIdp,
+ "owner" => $ownerOpaqueId . "@" . $ownerIdp,
+ "initiator" => $creatorOpaqueId . "@" . $creatorIdp,
+ "ctime" => $ctime,
+ "mtime" => $mtime,
+ "expiration" => $expiration,
+ "transfer" => $ocmProtocolTransfer ?? null,
+ "webapp" => $ocmProtocolWebapp ?? null,
+ "webdav" => $ocmProtocolWebdav,
+ ];
+
+ $this->shareProvider->addSentOcmShareToSciencemeshTable($ocmShareData);
+
+ return new DataResponse($efssShareInternalId, Http::STATUS_CREATED);
}
+ // TODO: @Mahdi Move to utils.
private function getPermissionsCode(array $permissions): int
{
$permissionsCode = 0;
@@ -1291,6 +1565,8 @@ private function getPermissionsCode(array $permissions): int
return $permissionsCode;
}
+ // TODO: @Mahdi Move to utils.
+
/**
* @param Node $node
* @return void
@@ -1300,7 +1576,6 @@ private function getPermissionsCode(array $permissions): int
private function lock(Node $node)
{
$node->lock(ILockingProvider::LOCK_SHARED);
- $this->lockedNode = $node;
}
/**
@@ -1315,53 +1590,171 @@ private function lock(Node $node)
*/
public function addReceivedShare($userId): JSONResponse
{
+ if ($this->userManager->userExists($userId)) {
+ $this->init($userId);
+ } else {
+ return new JSONResponse("User not found", Http::STATUS_FORBIDDEN);
+ }
+
$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;
- }
+
+ $name = $params["name"] ?? null;
+ $ctime = (int)$params["ctime"]["seconds"] ?? null;
+ $mtime = (int)$params["mtime"]["seconds"] ?? null;
+ $remoteShareId = $params["remoteShareId"] ?? null;
+
+ if (!isset($name)) {
+ return new JSONResponse("name not found in the request.", Http::STATUS_BAD_REQUEST);
+ }
+ if (!isset($ctime)) {
+ return new JSONResponse("ctime not found in the request.", Http::STATUS_BAD_REQUEST);
+ }
+ if (!isset($mtime)) {
+ return new JSONResponse("mtime not found in the request.", Http::STATUS_BAD_REQUEST);
+ }
+ if (!isset($remoteShareId)) {
+ return new JSONResponse("remoteShareId not found in the request.", Http::STATUS_BAD_REQUEST);
}
- if (!isset($sharedSecret)) {
- throw new Exception('sharedSecret not found');
+ if (isset($params["grantee"]["userId"])) {
+ $granteeIdp = $params["grantee"]["userId"]["idp"] ?? null;
+ $granteeOpaqueId = $params["grantee"]["userId"]["opaqueId"] ?? null;
}
- if (!isset($remote)) {
- throw new Exception('protocols[[webdavOptions][uri]] not found');
+ if (isset($params["owner"])) {
+ $ownerIdp = $params["owner"]["idp"] ?? null;
+ $ownerOpaqueId = $params["owner"]["opaqueId"] ?? null;
}
- $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 (isset($params["creator"])) {
+ $creatorIdp = $params["creator"]["idp"] ?? null;
+ $creatorOpaqueId = $params["creator"]["opaqueId"] ?? null;
+ }
- if ($this->userManager->userExists($userId)) {
- $this->init($userId);
- } else {
- return new JSONResponse("User not found", Http::STATUS_FORBIDDEN);
+ if (!isset($granteeIdp)) {
+ return new JSONResponse("grantee idp not found in the request.", Http::STATUS_BAD_REQUEST);
+ }
+ if (!isset($granteeOpaqueId)) {
+ return new JSONResponse("grantee opaqueId not found in the request.", Http::STATUS_BAD_REQUEST);
+ }
+ if (!isset($ownerIdp)) {
+ return new JSONResponse("owner idp not found in the request.", Http::STATUS_BAD_REQUEST);
+ }
+ if (!isset($ownerOpaqueId)) {
+ return new JSONResponse("owner opaqueId not found in the request.", Http::STATUS_BAD_REQUEST);
+ }
+ if (!isset($creatorIdp)) {
+ return new JSONResponse("creator idp not found in the request.", Http::STATUS_BAD_REQUEST);
+ }
+ if (!isset($creatorOpaqueId)) {
+ return new JSONResponse("creator opaqueId not found in the request.", Http::STATUS_BAD_REQUEST);
+ }
+
+ if (!isset($params["protocols"])) {
+ return new JSONResponse("protocols not found in the request.", Http::STATUS_BAD_REQUEST);
+ }
+
+ $protocols = $params["protocols"];
+
+ foreach ($protocols as $protocol) {
+ if (isset($protocol["transferOptions"])) {
+ $ocmProtocolTransfer = $protocol["transferOptions"];
+ }
+ if (isset($protocol["webappOptions"])) {
+ $ocmProtocolWebapp = $protocol["webappOptions"];
+ }
+ if (isset($protocol["webdavOptions"])) {
+ $ocmProtocolWebdav = $protocol["webdavOptions"];
+ }
+ }
+
+ if (!isset($ocmProtocolWebdav)) {
+ return new JSONResponse("webdavOptions not found in the request.", Http::STATUS_BAD_REQUEST);
+ }
+
+ // handle bad cases and eliminate null variables.
+ if (!isset($ocmProtocolWebdav["permissions"]["permissions"])) {
+ return new JSONResponse("webdavOptions permissions not found in the request.", Http::STATUS_BAD_REQUEST);
+ }
+ if (!isset($ocmProtocolWebdav["sharedSecret"])) {
+ return new JSONResponse("webdavOptions sharedSecret not found in the request.", Http::STATUS_BAD_REQUEST);
+ }
+ if (!isset($ocmProtocolWebdav["uri"])) {
+ return new JSONResponse("webdavOptions uri not found in the request.", Http::STATUS_BAD_REQUEST);
}
- $scienceMeshData = [
- "is_external" => true,
+ // convert permissions from array to integer.
+ $integerPermissions = $this->getPermissionsCode($ocmProtocolWebdav["permissions"]["permissions"]);
+ $ocmProtocolWebdav["permissions"] = $integerPermissions;
+
+ $sharedSecret = $ocmProtocolWebdav["sharedSecret"];
+ // URI example: https://nc1.docker/remote.php/dav/ocm/vaKE36Wf1lJWCvpDcRQUScraVP5quhzA
+ $uri = $ocmProtocolWebdav["uri"];
+ // remote extracted from URI: https://nc1.docker
+ // below line splits uri by the "/" character, picks first 3 items aka ["https:", "", "nc.docker"]
+ // and joins them again with "/" character in between.
+ $remote = implode("/", array_slice(explode("/", $uri), 0, 3));
+
+ if (!isset($remote)) {
+ return new JSONResponse("Correct WebDAV URI not found in the request. remote is: $remote", Http::STATUS_BAD_REQUEST);
+ }
+
+ // TODO: @Mahdi write checks for webapp and transfer protocols missing properties and return STATUS_BAD_REQUEST.
+
+ // remove trailing "/" from share name.
+ $name = rtrim($name, "/");
+
+ // prepare data for adding to the native efss table.
+ $efssShareData = [
+ // "https://nc1.docker"
+ "remote" => $remote,
+ // the id of the share in the oc_share table of the remote.
+ "remote_id" => $remoteShareId,
+ // "tDPRTrLI4hE3C5T"
+ "share_token" => $sharedSecret,
+ // password is always null. ScienceMesh doesn't have password protected shares.
+ "password" => null,
+ // "TestFolder"
+ "name" => $name,
+ // "einstein"
+ "owner" => $ownerOpaqueId,
+ // receiver "marie"
+ "user" => $userId
+ ];
+
+ $efssShareId = $this->shareProvider->addReceivedOcmShareToEfssTable($efssShareData);
+
+ // prepare data for adding to the ScienceMesh OCM table.
+ // see: https://github.com/sciencemesh/nc-sciencemesh/issues/45
+ $expiration = $params["expiration"] ?? null;
+ if (isset($expiration)) {
+ $expiration = (int)$expiration;
+ }
+
+ $ocmShareData = [
+ "share_external_id" => $efssShareId,
+ "name" => $name,
+ "share_with" => $granteeOpaqueId . "@" . $granteeIdp,
+ "owner" => $ownerOpaqueId . "@" . $ownerIdp,
+ "initiator" => $creatorOpaqueId . "@" . $creatorIdp,
+ "ctime" => $ctime,
+ "mtime" => $mtime,
+ "expiration" => $expiration,
+ "remote_share_id" => $remoteShareId,
+ "transfer" => $ocmProtocolTransfer ?? null,
+ "webapp" => $ocmProtocolWebapp ?? null,
+ "webdav" => $ocmProtocolWebdav,
];
- $id = $this->shareProvider->addScienceMeshShare($scienceMeshData, $shareData);
- return new JSONResponse($id, 201);
+ $this->shareProvider->addReceivedOcmShareToSciencemeshTable($ocmShareData);
+
+ return new JSONResponse($efssShareId, Http::STATUS_CREATED);
}
/**
* Remove Share from share table
+ *
* @PublicPage
* @NoCSRFRequired
* @param $userId
@@ -1421,12 +1814,13 @@ public function UpdateSentShare($userId): JSONResponse
$share->setPermissions($permissionsCode);
$shareUpdated = $this->shareProvider->update($share);
- $response = $this->shareInfoToCs3Share($shareUpdated);
+ $response = $this->shareInfoToCs3Share($shareUpdated, "sent");
return new JSONResponse($response, Http::STATUS_OK);
}
/**
* UpdateReceivedShare updates the received share with share state.
+ *
* @PublicPage
* @NoCSRFRequired
* @param $userId
@@ -1451,7 +1845,7 @@ public function UpdateReceivedShare($userId): JSONResponse
$share = $this->shareProvider->getReceivedShareByToken($resourceId);
$share->setPermissions($permissionsCode);
$shareUpdate = $this->shareProvider->UpdateReceivedShare($share);
- $response = $this->shareInfoToCs3Share($shareUpdate, $resourceId);
+ $response = $this->shareInfoToCs3Share($shareUpdate, "received", $resourceId);
$response["state"] = 2;
return new JSONResponse($response, Http::STATUS_OK);
} catch (Exception $e) {
@@ -1462,6 +1856,7 @@ public function UpdateReceivedShare($userId): 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.
+ *
* @PublicPage
* @NoCSRFRequired
* @param $userId
@@ -1485,7 +1880,7 @@ public function ListSentShares($userId): JSONResponse
if ($shares) {
foreach ($shares as $share) {
- $responses[] = $this->shareInfoToCs3Share($share);
+ $responses[] = $this->shareInfoToCs3Share($share, "sent");
}
}
return new JSONResponse($responses, Http::STATUS_OK);
@@ -1493,6 +1888,7 @@ public function ListSentShares($userId): JSONResponse
/**
* ListReceivedShares returns the list of shares the user has access.
+ *
* @PublicPage
* @NoCSRFRequired
* @param $userId
@@ -1515,7 +1911,7 @@ public function ListReceivedShares($userId): JSONResponse
if ($shares) {
foreach ($shares as $share) {
- $response = $this->shareInfoToCs3Share($share);
+ $response = $this->shareInfoToCs3Share($share, "received");
$responses[] = [
"share" => $response,
"state" => 2
@@ -1528,6 +1924,7 @@ public function ListReceivedShares($userId): JSONResponse
/**
* GetReceivedShare returns the information for a received share the user has access.
+ *
* @PublicPage
* @NoCSRFRequired
* @param $userId
@@ -1548,7 +1945,7 @@ public function GetReceivedShare($userId): JSONResponse
try {
$share = $this->shareProvider->getReceivedShareByToken($opaqueId);
- $response = $this->shareInfoToCs3Share($share, $opaqueId);
+ $response = $this->shareInfoToCs3Share($share, "received", $opaqueId);
$response["state"] = 2;
return new JSONResponse($response, Http::STATUS_OK);
} catch (Exception $e) {
@@ -1558,6 +1955,7 @@ public function GetReceivedShare($userId): JSONResponse
/**
* GetSentShare gets the information for a share by the given ref.
+ *
* @PublicPage
* @NoCSRFRequired
* @param $userId
@@ -1581,7 +1979,7 @@ public function GetSentShare($userId): JSONResponse
$share = $this->shareProvider->getSentShareByName($userId, $name);
if ($share) {
- $response = $this->shareInfoToCs3Share($share);
+ $response = $this->shareInfoToCs3Share($share, "sent");
return new JSONResponse($response, Http::STATUS_OK);
}
diff --git a/lib/Migration/Version0001Date20211201101630.php b/lib/Migration/Version0001Date20211201101630.php
deleted file mode 100644
index 0dcc91e5..00000000
--- a/lib/Migration/Version0001Date20211201101630.php
+++ /dev/null
@@ -1,100 +0,0 @@
-connection = $connection;
- }
-
- public function changeSchema(IOutput $output, Closure $schemaClosure, array $options)
- {
- $schema = $schemaClosure();
- $shares = $schema->createTable('sciencemesh_shares');
- $users = $schema->createTable('sciencemesh_users');
- $shares->addColumn(
- 'opaque_id',
- \OCP\DB\Types::STRING,
- ['notnull' => true]
- );
- $shares->addColumn(
- 'resource_id',
- \OCP\DB\Types::STRING,
- ['notnull' => true]
- );
- $shares->addColumn(
- 'permissions',
- \OCP\DB\Types::INTEGER,
- []
- );
- $shares->addColumn(
- 'grantee',
- \OCP\DB\Types::INTEGER,
- []
- );
- $shares->addColumn(
- 'owner',
- \OCP\DB\Types::INTEGER,
- []
- );
- $shares->addColumn(
- 'creator',
- \OCP\DB\Types::INTEGER,
- []
- );
- $shares->addColumn(
- 'ctime',
- \OCP\DB\Types::INTEGER,
- ['notnull' => true]
- );
- $shares->addColumn(
- 'mtime',
- \OCP\DB\Types::INTEGER,
- ['notnull' => true]
- );
- $shares->addColumn(
- 'is_external',
- \OCP\DB\Types::BOOLEAN,
- ['notnull' => false]
- );
- $shares->addColumn(
- 'foreign_id',
- \OCP\DB\Types::INTEGER,
- []
- );
- $shares->setPrimaryKey(['opaque_id']);
- $users->addColumn(
- 'id',
- \OCP\DB\Types::INTEGER,
- ['notnull' => true, 'autoincrement' => true, 'unsigned' => true]
- );
- $users->addColumn(
- 'idp',
- \OCP\DB\Types::STRING,
- ['notnull' => true]
- );
- $users->addColumn(
- 'opaque_id',
- \OCP\DB\Types::STRING,
- ['notnull' => true]
- );
- $users->addColumn(
- 'type',
- \OCP\DB\Types::INTEGER,
- []
- );
- $users->setPrimaryKey(['id']);
- return $schema;
- }
-}
diff --git a/lib/Migration/Version010000Date20210118175358.php b/lib/Migration/Version010000Date20210118175358.php
deleted file mode 100644
index a2dda8dd..00000000
--- a/lib/Migration/Version010000Date20210118175358.php
+++ /dev/null
@@ -1,87 +0,0 @@
-hasTable('sciencemesh')) {
- $table = $schema->createTable("sciencemesh");
- $table->addColumn('apikey', 'string', [
- 'notnull' => true,
- ]);
- $table->addColumn('sitename', 'string', [
- 'notnull' => true,
- ]);
- $table->addColumn('siteurl', 'string', [
- 'notnull' => true,
- ]);
- $table->addColumn('siteid', 'string', [
- 'notnull' => false,
- ]);
- $table->addColumn('country', 'string', [
- 'notnull' => true,
- 'length' => 3,
- ]);
- $table->addColumn('iopurl', 'string', [
- 'notnull' => true,
- ]);
- $table->addColumn('numusers', Types::BIGINT, [
- 'notnull' => true,
- 'default' => 0,
- 'unsigned' => true,
- ]);
- $table->addColumn('numfiles', Types::BIGINT, [
- 'notnull' => true,
- 'default' => 0,
- 'unsigned' => true,
- ]);
- $table->addColumn('numstorage', Types::BIGINT, [
- 'notnull' => true,
- 'default' => 0,
- 'unsigned' => true,
- ]);
- }
- return $schema;
- }
-
- /**
- * @param IOutput $output
- * @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper`
- * @param array $options
- */
- public function postSchemaChange(IOutput $output, Closure $schemaClosure, array $options)
- {
- }
-}
diff --git a/lib/Plugins/ScienceMeshSearchPlugin.php b/lib/Plugins/ScienceMeshSearchPlugin.php
index 65f0d1ab..11fa6289 100644
--- a/lib/Plugins/ScienceMeshSearchPlugin.php
+++ b/lib/Plugins/ScienceMeshSearchPlugin.php
@@ -12,6 +12,7 @@
namespace OCA\ScienceMesh\Plugins;
+use Exception;
use OC\Share\Constants;
use OCA\ScienceMesh\AppInfo\ScienceMeshApp;
use OCA\ScienceMesh\RevaHttpClient;
@@ -21,6 +22,7 @@
use OCP\Util\UserSearch;
use function explode;
use function is_array;
+use function strtolower;
use function substr_count;
class ScienceMeshSearchPlugin
@@ -44,8 +46,13 @@ class ScienceMeshSearchPlugin
/** @var string */
private string $userId = '';
+
+ /** @var RevaHttpClient */
private RevaHttpClient $revaHttpClient;
+ /**
+ * @throws Exception
+ */
public function __construct(
IManager $contactsManager,
IConfig $config,
@@ -117,11 +124,11 @@ public function search($search): array
$cloudIds = [$cloudIds];
}
- $lowerSearch = \strtolower($search);
+ $lowerSearch = strtolower($search);
foreach ($cloudIds as $cloudId) {
list(, $serverUrl) = $this->splitUserRemote($cloudId);
- if (\strtolower($cloudId) === $lowerSearch) {
+ if (strtolower($cloudId) === $lowerSearch) {
$foundRemoteById = true;
// Save this as an exact match and continue with next CLOUD
$otherResults[] = [
@@ -150,7 +157,7 @@ public function search($search): array
}
foreach ($values as $value) {
// check if we have an exact match
- if (\strtolower($value) === $lowerSearch) {
+ if (strtolower($value) === $lowerSearch) {
$this->result['exact']['remotes'][] = [
'label' => $contact['FN'],
'value' => [
diff --git a/lib/RevaHttpClient.php b/lib/RevaHttpClient.php
index 7e319de5..ad00cc54 100644
--- a/lib/RevaHttpClient.php
+++ b/lib/RevaHttpClient.php
@@ -158,6 +158,7 @@ public function createShare(string $user, array $params)
throw new Exception("Missing type", 400);
}
if (!isset($params['role'])) {
+ // TODO: @Mahdi why is thi hard coded? is it related to share permissions?
$params['role'] = 'viewer';
}
if (!isset($params['recipientUsername'])) {
diff --git a/lib/ServerConfig.php b/lib/ServerConfig.php
index e9a0a597..8a83076d 100644
--- a/lib/ServerConfig.php
+++ b/lib/ServerConfig.php
@@ -21,18 +21,18 @@
*/
function random_str(
int $length = 64,
- string $keyspace = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
+ string $keyspace = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
): string
{
if ($length < 1) {
throw new RangeException("Length must be a positive integer");
}
$pieces = [];
- $max = mb_strlen($keyspace, '8bit') - 1;
+ $max = mb_strlen($keyspace, "8bit") - 1;
for ($i = 0; $i < $length; ++$i) {
$pieces [] = $keyspace[random_int(0, $max)];
}
- return implode('', $pieces);
+ return implode("", $pieces);
}
/**
@@ -54,32 +54,40 @@ public function __construct(IConfig $config)
public function getApiKey()
{
- return $this->config->getAppValue('sciencemesh', 'apiKey');
+ return $this->config->getAppValue("sciencemesh", "apiKey");
}
public function getSiteName()
{
- return $this->config->getAppValue('sciencemesh', 'siteName');
+ return $this->config->getAppValue("sciencemesh", "siteName");
}
public function getSiteUrl()
{
- return $this->config->getAppValue('sciencemesh', 'siteUrl');
+ return $this->config->getAppValue("sciencemesh", "siteUrl");
}
public function getSiteId()
{
- return $this->config->getAppValue('sciencemesh', 'siteId');
+ return $this->config->getAppValue("sciencemesh", "siteId");
}
public function getCountry()
{
- return $this->config->getAppValue('sciencemesh', 'country');
+ return $this->config->getAppValue("sciencemesh", "country");
}
public function getIopUrl(): string
{
- return rtrim($this->config->getAppValue('sciencemesh', 'iopUrl'), '/') . '/';
+ return rtrim($this->config->getAppValue("sciencemesh", "iopUrl"), "/") . "/";
+ }
+
+ public function getIopIdp(): string
+ {
+ // TODO: @Mahdi use function from utils.
+ // converts https://revaowncloud1.docker/ to revaowncloud1.docker
+ // NOTE: do not use it on anything without http(s) in the start, it would return null.
+ return str_ireplace("www.", "", parse_url($this->getIopUrl(), PHP_URL_HOST));
}
/**
@@ -87,10 +95,10 @@ public function getIopUrl(): string
*/
public function getRevaLoopbackSecret()
{
- $ret = $this->config->getAppValue('sciencemesh', 'revaLoopbackSecret');
+ $ret = $this->config->getAppValue("sciencemesh", "revaLoopbackSecret");
if (!$ret) {
$ret = random_str(32);
- $this->config->setAppValue('sciencemesh', 'revaLoopbackSecret', $ret);
+ $this->config->setAppValue("sciencemesh", "revaLoopbackSecret", $ret);
}
return $ret;
}
@@ -100,76 +108,76 @@ public function getRevaLoopbackSecret()
*/
public function getRevaSharedSecret()
{
- $ret = $this->config->getAppValue('sciencemesh', 'revaSharedSecret');
+ $ret = $this->config->getAppValue("sciencemesh", "revaSharedSecret");
if (!$ret) {
$ret = random_str(32);
- $this->config->setAppValue('sciencemesh', 'revaSharedSecret', $ret);
+ $this->config->setAppValue("sciencemesh", "revaSharedSecret", $ret);
}
return $ret;
}
public function setRevaSharedSecret($sharedSecret)
{
- $this->config->setAppValue('sciencemesh', 'revaSharedSecret', $sharedSecret);
+ $this->config->setAppValue("sciencemesh", "revaSharedSecret", $sharedSecret);
}
public function getNumUsers()
{
- return $this->config->getAppValue('sciencemesh', 'numUsers');
+ return $this->config->getAppValue("sciencemesh", "numUsers");
}
public function getNumFiles()
{
- return $this->config->getAppValue('sciencemesh', 'numFiles');
+ return $this->config->getAppValue("sciencemesh", "numFiles");
}
public function getNumStorage()
{
- return $this->config->getAppValue('sciencemesh', 'numStorage');
+ return $this->config->getAppValue("sciencemesh", "numStorage");
}
public function setApiKey($apiKey)
{
- $this->config->setAppValue('sciencemesh', 'apiKey', $apiKey);
+ $this->config->setAppValue("sciencemesh", "apiKey", $apiKey);
}
public function setSiteName($siteName)
{
- $this->config->setAppValue('sciencemesh', 'siteName', $siteName);
+ $this->config->setAppValue("sciencemesh", "siteName", $siteName);
}
public function setSiteUrl($siteUrl)
{
- $this->config->setAppValue('sciencemesh', 'siteUrl', $siteUrl);
+ $this->config->setAppValue("sciencemesh", "siteUrl", $siteUrl);
}
public function setSiteId($siteId)
{
- $this->config->setAppValue('sciencemesh', 'siteId', $siteId);
+ $this->config->setAppValue("sciencemesh", "siteId", $siteId);
}
public function setCountry($country)
{
- $this->config->setAppValue('sciencemesh', 'country', $country);
+ $this->config->setAppValue("sciencemesh", "country", $country);
}
public function setIopUrl($iopUrl)
{
- $this->config->setAppValue('sciencemesh', 'iopUrl', $iopUrl);
+ $this->config->setAppValue("sciencemesh", "iopUrl", $iopUrl);
}
public function setNumUsers($numUsers)
{
- $this->config->setAppValue('sciencemesh', 'numUsers', $numUsers);
+ $this->config->setAppValue("sciencemesh", "numUsers", $numUsers);
}
public function setNumFiles($numFiles)
{
- $this->config->setAppValue('sciencemesh', 'numFiles', $numFiles);
+ $this->config->setAppValue("sciencemesh", "numFiles", $numFiles);
}
public function setNumStorage($numStorage)
{
- $this->config->setAppValue('sciencemesh', 'numStorage', $numStorage);
+ $this->config->setAppValue("sciencemesh", "numStorage", $numStorage);
}
}
diff --git a/lib/ShareProvider/ScienceMeshShareProvider.php b/lib/ShareProvider/ScienceMeshShareProvider.php
index 106a4b59..765b41f4 100644
--- a/lib/ShareProvider/ScienceMeshShareProvider.php
+++ b/lib/ShareProvider/ScienceMeshShareProvider.php
@@ -24,8 +24,9 @@
use OCA\FederatedFileSharing\TokenHandler;
use OCA\ScienceMesh\AppInfo\ScienceMeshApp;
use OCA\ScienceMesh\RevaHttpClient;
-use OCP\Constants;
+use OCA\ScienceMesh\ServerConfig;
use OCP\Files\IRootFolder;
+use OCP\Files\NotFoundException;
use OCP\IConfig;
use OCP\IDBConnection;
use OCP\IL10N;
@@ -44,12 +45,15 @@
class ScienceMeshShareProvider extends FederatedShareProviderCopy
{
- /** @var RevaHttpClient */
- protected RevaHttpClient $revaHttpClient;
+ /** @var ServerConfig */
+ private ServerConfig $serverConfig;
/** @var array */
protected array $supportedShareType;
+ /** @var RevaHttpClient */
+ protected RevaHttpClient $revaHttpClient;
+
/**
* ScienceMeshShareProvider constructor.
*
@@ -91,8 +95,9 @@ public function __construct(
$userManager
);
- $this->supportedShareType[] = ScienceMeshApp::SHARE_TYPE_SCIENCEMESH;
+ $this->serverConfig = new ServerConfig($config);
$this->revaHttpClient = new RevaHttpClient($config);
+ $this->supportedShareType[] = ScienceMeshApp::SHARE_TYPE_SCIENCEMESH;
}
/**
@@ -108,119 +113,10 @@ public function isShareTypeSupported(int $shareType): bool
}
/**
- * Share a path
- *
- * @param IShare $share
- * @return IShare The share object
- * @throws ShareNotFound
- * @throws Exception
- */
- public function createInternal(IShare $share): IShare
- {
- error_log("SMSP: createInternal");
- $shareWith = $share->getSharedWith();
-
- error_log("shareWith $shareWith");
-
- error_log("checking if already shared " . $share->getNode()->getName());
- /*
- * Check if file is not already shared with the remote user
- */
- $alreadyShared = $this->getSharedWith($shareWith, $share->getShareType(), $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
- *
- * @param IShare $share
- * @return int
- * @throws Exception
- */
- protected function createScienceMeshShare(IShare $share): int
- {
- return $this->addSentShareToDB(
- $share->getNodeId(),
- $share->getNodeType(),
- $share->getSharedWith(),
- $share->getSharedBy(),
- $share->getShareOwner(),
- $share->getPermissions(),
- $share->getToken(),
- $share->getShareType()
- );
- }
-
- /**
- * add share to the database and return the ID
+ * Share a path.
*
- * @param int $itemSource
- * @param string $itemType
- * @param string $shareWith
- * @param string $sharedBy
- * @param string $uidOwner
- * @param int $permissions
- * @param string $token
- * @param int $shareType
- * @return int
- */
- protected function addSentShareToDB(
- int $itemSource,
- string $itemType,
- string $shareWith,
- string $sharedBy,
- string $uidOwner,
- int $permissions,
- string $token,
- int $shareType
- ): int
- {
- $qb = $this->dbConnection->getQueryBuilder();
- $qb->insert('share')
- ->setValue('share_type', $qb->createNamedParameter($shareType))
- ->setValue('item_type', $qb->createNamedParameter($itemType))
- ->setValue('item_source', $qb->createNamedParameter($itemSource))
- ->setValue('file_source', $qb->createNamedParameter($itemSource))
- ->setValue('share_with', $qb->createNamedParameter($shareWith))
- ->setValue('uid_owner', $qb->createNamedParameter($uidOwner))
- ->setValue('uid_initiator', $qb->createNamedParameter($sharedBy))
- ->setValue('permissions', $qb->createNamedParameter($permissions))
- ->setValue('token', $qb->createNamedParameter($token))
- ->setValue('stime', $qb->createNamedParameter(time()));
-
- /*
- * Added to fix https://github.com/owncloud/core/issues/22215
- * Can be removed once we get rid of ajax/share.php
- */
- $qb->setValue('file_target', $qb->createNamedParameter(''));
-
- $qb->execute();
- return $qb->getLastInsertId();
- }
-
- /**
- * Share a path
+ * This method is being called by:
+ * https://github.com/owncloud/core/blob/e95e978f452787e55515ac3e43763e4adcf87d14/lib/private/Share20/Manager.php#L832C3-L832C3
*
* @param IShare $share
* @return IShare The share object
@@ -232,17 +128,21 @@ public function create(IShare $share): IShare
$node = $share->getNode();
$shareWith = $share->getSharedWith();
- // This is the routing flag for sending a share.
+ // this is the routing flag for sending a share.
// if the recipient of the share is a sciencemesh contact,
// the search plugin will mark it by a postfix.
$isSciencemeshUser = $this->stringEndsWith($shareWith, ScienceMeshApp::SCIENCEMESH_POSTFIX);
- // Based on the flag, the share will be sent through sciencemesh or regular share provider.
+ // based on the flag, the share will be sent through sciencemesh or regular share provider.
if ($isSciencemeshUser) {
// remove the postfix flag from the string.
$shareWith = str_replace(ScienceMeshApp::SCIENCEMESH_POSTFIX, "", $shareWith);
+ error_log("create: node path: " . $node->getPath());
+ // node path: /einstein/files/test
+ // path parts: ["einstein", "files", "test"]
$pathParts = explode("/", $node->getPath());
+ // sender: einstein
$sender = $pathParts[1];
$sourceOffset = 3;
$targetOffset = 3;
@@ -250,10 +150,13 @@ public function create(IShare $share): IShare
$suffix = ($node->getType() == "dir" ? "/" : "");
// "home" is reva's default work space name, prepending that in the source path:
+ // source path: /test/
+ // target path: /home/test/
$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.
+ // TODO: @Mahdi make a function for below operation. it is used in a lot placed, but incorrectly.
+ // TODO: @Mahdi Move to utils.
// it should split username@host into an array of 2 element
// representing array[0] = username, array[1] = host
// requirement:
@@ -261,21 +164,72 @@ public function create(IShare $share): IShare
// example: MahdiBaghbani@pondersource@sciencemesh.org
// username: MahdiBaghbani@pondersource
// host: sciencemesh.org
- $split_point = '@';
+ $split_point = "@";
$parts = explode($split_point, $shareWith);
$last = array_pop($parts);
$shareWithParts = array(implode($split_point, $parts), $last);
+ // don't allow ScienceMesh shares if source and target server are the same.
+ // this means users with the same reva iop cannot share with each other via sciencemesh and
+ // should use their native efss capabilities to do so.
+ // see: https://github.com/sciencemesh/nc-sciencemesh/issues/57
+ if ($this->serverConfig->getIopIdp() === $shareWithParts[1]) {
+ $message = "Not allowed to create a ScienceMesh share for a user on the same server %s as sender.";
+ $message_t = $this->l->t(
+ "Not allowed to create a ScienceMesh share for a user on the same server %s as sender.",
+ [$shareWithParts[1]]
+ );
+ $this->logger->debug(
+ sprintf(
+ $message, $shareWithParts[1]
+ ),
+ ["app" => "sciencemesh"]
+ );
+ throw new Exception($message_t);
+ }
+
+ // check if file is not already shared with the remote user.
+ $alreadyShared = $this->getSharedWith(
+ $shareWith,
+ $share->getShareType(),
+ $share->getNode(),
+ 1,
+ 0
+ );
+
+ if (!empty($alreadyShared)) {
+ $message = "Sharing %s failed, because this item is already shared with %s";
+ $message_t = $this->l->t(
+ "Sharing %s failed, because this item is already shared with %s",
+ [$share->getNode()->getName(), $share->getSharedWith()]
+ );
+ $this->logger->debug(
+ sprintf(
+ $message, $share->getNode()->getName(), $share->getSharedWith()
+ ),
+ ["app" => "sciencemesh"]
+ );
+ throw new Exception($message_t);
+ }
+
$response = $this->revaHttpClient->createShare($sender, [
- 'sourcePath' => $sourcePath,
- 'targetPath' => $targetPath,
- 'type' => $node->getType(),
- 'recipientUsername' => $shareWithParts[0],
- 'recipientHost' => $shareWithParts[1]
+ "sourcePath" => $sourcePath,
+ "targetPath" => $targetPath,
+ "type" => $node->getType(),
+ "recipientUsername" => $shareWithParts[0],
+ "recipientHost" => $shareWithParts[1]
]);
- if (!isset($response) || !isset($response->share) || !isset($response->share->owner) || !isset($response->share->owner->idp)) {
- throw new Exception("Unexpected response from reva");
+ if (!isset($response->share->owner->idp)) {
+ $message = "Unexpected response from Reva, response share owner idp doesn't exist.";
+ $message_t = $this->l->t(
+ "Unexpected response from Reva, response share owner idp doesn't exist.",
+ );
+ $this->logger->error(
+ $message,
+ ["app" => "sciencemesh"]
+ );
+ throw new Exception($message_t);
}
$share->setId("will-set-this-later");
@@ -288,6 +242,27 @@ public function create(IShare $share): IShare
return $share;
}
+ /**
+ * Share a path via ScienceMesh.
+ *
+ * @param IShare $share
+ * @return IShare The share object
+ * @throws NotFoundException
+ * @throws ShareNotFound
+ * @throws InvalidShare
+ * @throws IllegalIDChangeException
+ */
+ public function createNativeEfssScienceMeshShare(IShare $share): IShare
+ {
+ // this adds share to native efss table in the database.
+ $shareId = $this->addSentOcmShareToEfssTable($share);
+
+ $data = $this->getRawShare($shareId);
+ return $this->createShareObject($data);
+ }
+
+ // TODO: @Mahdi Move to utils.
+
/**
* Check if a given string ends with a substring.
*
@@ -316,9 +291,9 @@ public function updateReceivedShare(IShare $share): IShare
* We allow updating the permissions of sciencemesh shares
*/
$qb = $this->dbConnection->getQueryBuilder();
- $qb->update('share_external')
- ->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId())))
- ->set('owner', $qb->createNamedParameter($share->getShareOwner()))
+ $qb->update("share_external")
+ ->where($qb->expr()->eq("id", $qb->createNamedParameter($share->getId())))
+ ->set("owner", $qb->createNamedParameter($share->getShareOwner()))
->execute();
return $share;
}
@@ -333,14 +308,16 @@ public function updateReceivedShare(IShare $share): IShare
public function getReceivedShareByToken(string $token)
{
$qb = $this->dbConnection->getQueryBuilder();
- $cursor = $qb->select('*')
- ->from('share_external')
- ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(14)))
- ->andWhere($qb->expr()->eq('share_token', $qb->createNamedParameter($token)))
+ $cursor = $qb->select("*")
+ ->from("share_external")
+ ->where($qb->expr()->eq("share_type", $qb->createNamedParameter(14)))
+ ->andWhere($qb->expr()->eq("share_token", $qb->createNamedParameter($token)))
->execute();
$data = $cursor->fetch();
+ $cursor->closeCursor();
+
if ($data === false) {
- throw new ShareNotFound('Share not found', $this->l->t('Could not find share'));
+ throw new ShareNotFound("Share not found", $this->l->t("Could not find share"));
}
return $this->createExternalShareObject($data);
}
@@ -355,12 +332,12 @@ public function getReceivedShareByToken(string $token)
protected function createExternalShareObject(array $data)
{
$share = new Share($this->rootFolder, $this->userManager);
- $share->setId((int)$data['id'])
- ->setShareType((int)$data['share_type'])
- ->setShareOwner($data['owner'])
- ->setSharedBy($data['owner'])
- ->setToken($data['share_token'])
- ->setSharedWith($data['user']);
+ $share->setId((int)$data["id"])
+ ->setShareType((int)$data["share_type"])
+ ->setShareOwner($data["owner"])
+ ->setSharedBy($data["owner"])
+ ->setToken($data["share_token"])
+ ->setSharedWith($data["user"]);
$share->setProviderId($this->identifier());
return $share;
@@ -373,7 +350,7 @@ protected function createExternalShareObject(array $data)
*/
public function identifier(): string
{
- return 'sciencemesh';
+ return "sciencemesh";
}
/**
@@ -386,22 +363,27 @@ public function identifier(): string
public function getSentShareByToken(string $token): IShare
{
error_log("share provider getSentShareByToken '$token'");
+
$qb = $this->dbConnection->getQueryBuilder();
- $cursor = $qb->select('*')
- ->from('share')
- ->where($qb->expr()->eq('token', $qb->createNamedParameter($token)))
+ $cursor = $qb->select("*")
+ ->from("share")
+ ->where($qb->expr()->eq("token", $qb->createNamedParameter($token)))
->execute();
$data = $cursor->fetch();
+ $cursor->closeCursor();
+
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'));
+ 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'));
+ 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;
}
@@ -410,15 +392,15 @@ public function getSentShares(int $userId): iterable
{
$qb = $this->dbConnection->getQueryBuilder();
- $qb->select('*')
- ->from('share')
+ $qb->select("*")
+ ->from("share")
->where(
- $qb->expr()->eq('share_type', $qb->createNamedParameter(ScienceMeshApp::SHARE_TYPE_SCIENCEMESH))
+ $qb->expr()->eq("share_type", $qb->createNamedParameter(ScienceMeshApp::SHARE_TYPE_SCIENCEMESH))
)
->andWhere(
$qb->expr()->orX(
- $qb->expr()->eq('uid_initiator', $qb->createNamedParameter($userId)),
- $qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId))
+ $qb->expr()->eq("uid_initiator", $qb->createNamedParameter($userId)),
+ $qb->expr()->eq("uid_owner", $qb->createNamedParameter($userId))
)
);
@@ -438,10 +420,10 @@ public function getSentShares(int $userId): iterable
public function getReceivedShares($userId): iterable
{
$qb = $this->dbConnection->getQueryBuilder();
- $qb->select('*')
- ->from('share_external')
+ $qb->select("*")
+ ->from("share_external")
->where(
- $qb->expr()->eq('user', $qb->createNamedParameter($userId))
+ $qb->expr()->eq("user", $qb->createNamedParameter($userId))
);
$cursor = $qb->execute();
while ($data = $cursor->fetch()) {
@@ -459,34 +441,38 @@ public function getReceivedShares($userId): iterable
public function deleteSentShareByName($userId, $name): bool
{
$qb = $this->dbConnection->getQueryBuilder();
- $qb->select('fileid')
- ->from('filecache')
+ $qb->select("fileid")
+ ->from("filecache")
->where(
- $qb->expr()->eq('name', $qb->createNamedParameter($name))
+ $qb->expr()->eq("name", $qb->createNamedParameter($name))
);
$cursor = $qb->execute();
$data = $cursor->fetch();
+ $cursor->closeCursor();
+
if (!$data) {
return false;
}
- $id = $data['fileid'];
- $isShare = $qb->select('*')
- ->from('share')
+
+
+ $id = $data["fileid"];
+ $isShare = $qb->select("*")
+ ->from("share")
->where(
- $qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId))
+ $qb->expr()->eq("uid_owner", $qb->createNamedParameter($userId))
)
->andWhere(
- $qb->expr()->eq('item_source', $qb->createNamedParameter($id))
+ $qb->expr()->eq("item_source", $qb->createNamedParameter($id))
)
->execute()
->fetch();
if ($isShare) {
- $qb->delete('share')
+ $qb->delete("share")
->where(
- $qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId))
+ $qb->expr()->eq("uid_owner", $qb->createNamedParameter($userId))
)
->andWhere(
- $qb->expr()->eq('item_source', $qb->createNamedParameter($id))
+ $qb->expr()->eq("item_source", $qb->createNamedParameter($id))
);
$qb->execute();
return true;
@@ -509,25 +495,27 @@ public function delete(IShare $share)
public function deleteReceivedShareByOpaqueId($userId, $opaqueId): bool
{
$qb = $this->dbConnection->getQueryBuilder();
- $qb->select('*')
- ->from('share_external')
+ $qb->select("*")
+ ->from("share_external")
->where(
- $qb->expr()->eq('user', $qb->createNamedParameter($userId))
+ $qb->expr()->eq("user", $qb->createNamedParameter($userId))
)
->andWhere(
- $qb->expr()->eq('share_token', $qb->createNamedParameter($opaqueId))
+ $qb->expr()->eq("share_token", $qb->createNamedParameter($opaqueId))
);
$cursor = $qb->execute();
$data = $cursor->fetch();
+ $cursor->closeCursor();
+
if (!$data) {
return false;
} else {
- $qb->delete('share_external')
+ $qb->delete("share_external")
->where(
- $qb->expr()->eq('user', $qb->createNamedParameter($userId))
+ $qb->expr()->eq("user", $qb->createNamedParameter($userId))
)
->andWhere(
- $qb->expr()->eq('share_token', $qb->createNamedParameter($opaqueId))
+ $qb->expr()->eq("share_token", $qb->createNamedParameter($opaqueId))
);
$qb->execute();
return true;
@@ -542,36 +530,43 @@ public function getSentShareByPath($userId, $path)
{
$qb = $this->dbConnection->getQueryBuilder();
- $qb->select('fileid')
- ->from('filecache')
+ $qb->select("fileid")
+ ->from("filecache")
->where(
- $qb->expr()->eq('path', $qb->createNamedParameter($path))
+ $qb->expr()->eq("path", $qb->createNamedParameter($path))
);
+
$cursor = $qb->execute();
$data = $cursor->fetch();
+ $cursor->closeCursor();
+
if (!$data) {
return false;
}
- $id = $data['fileid'];
- $qb->select('*')
- ->from('share')
+ $id = $data["fileid"];
+ $qb->select("*")
+ ->from("share")
->where(
- $qb->expr()->eq('uid_owner', $qb->createNamedParameter($userId))
+ $qb->expr()->eq("uid_owner", $qb->createNamedParameter($userId))
)
->andWhere(
- $qb->expr()->eq('item_source', $qb->createNamedParameter($id))
+ $qb->expr()->eq("item_source", $qb->createNamedParameter($id))
);
+
$cursor = $qb->execute();
$data = $cursor->fetch();
+ $cursor->closeCursor();
+
if (!$data) {
return false;
}
+
try {
$share = $this->createShareObject($data);
} catch (InvalidShare $e) {
throw new ShareNotFound();
}
- $cursor->closeCursor();
+
return $share;
}
@@ -581,30 +576,35 @@ public function getSentShareByPath($userId, $path)
public function getShareByOpaqueId($opaqueId)
{
$qb = $this->dbConnection->getQueryBuilder();
- $c = $qb->select('is_external')
- ->from('sciencemesh_shares')
+ $cursor = $qb->select("is_external")
+ ->from("sciencemesh_shares")
->where(
- $qb->expr()->eq('opaque_id', $qb->createNamedParameter($opaqueId))
+ $qb->expr()->eq("opaque_id", $qb->createNamedParameter($opaqueId))
)
->execute();
- $data = $c->fetch();
+ $data = $cursor->fetch();
+ $cursor->closeCursor();
+
if (!$data) {
return false;
}
- $external = $data['is_external'];
- $c = $qb->select('*')
- ->from('sciencemesh_shares', 'sms')
- ->innerJoin('sms', $external ? 'share_external' : 'share', 's', $qb->expr()->eq('sms.foreignId', 's.id'))
+
+ $external = $data["is_external"];
+ $cursor = $qb->select("*")
+ ->from("sciencemesh_shares", "sms")
+ ->innerJoin("sms", $external ? "share_external" : "share", "s", $qb->expr()->eq("sms.foreignId", "s.id"))
->where(
- $qb->expr()->eq('sms.opaque_id', $qb->createNamedParameter($opaqueId))
+ $qb->expr()->eq("sms.opaque_id", $qb->createNamedParameter($opaqueId))
)
->execute();
- $data = $c->fetch();
+ $data = $cursor->fetch();
+ $cursor->closeCursor();
+
if (!$data) {
return false;
}
- return $external ? $this->createScienceMeshExternalShare($data) : $this->createScienceMeshShare($data);
+ return $external ? $this->createScienceMeshExternalShare($data) : $this->addSentOcmShareToEfssTable($data);
}
public function addScienceMeshUser($user)
@@ -613,25 +613,27 @@ public function addScienceMeshUser($user)
$opaqueId = $user->getOpaqueId();
$type = $user->getType();
$qb = $this->dbConnection->getQueryBuilder();
- $cursor = $qb->select('*')
- ->from('sciencemesh_users')
+ $cursor = $qb->select("*")
+ ->from("sciencemesh_users")
->where(
- $qb->expr()->eq('idp', $qb->createNamedParameter($idp))
+ $qb->expr()->eq("idp", $qb->createNamedParameter($idp))
)
->andWhere(
- $qb->expr()->eq('opaque_id', $qb->createNamedParameter($opaqueId))
+ $qb->expr()->eq("opaque_id", $qb->createNamedParameter($opaqueId))
)
->execute();
$data = $cursor->fetch();
+ $cursor->closeCursor();
+
if (!$data) {
- $qb->insert('sciencemesh_users')
- ->setValue('idp', $qb->createNamedParameter($idp))
- ->setValue('opaque_id', $qb->createNamedParameter($opaqueId))
- ->setValue('type', $qb->createNamedParameter($type))
+ $qb->insert("sciencemesh_users")
+ ->setValue("idp", $qb->createNamedParameter($idp))
+ ->setValue("opaque_id", $qb->createNamedParameter($opaqueId))
+ ->setValue("type", $qb->createNamedParameter($type))
->execute();
return $qb->getLastInsertId();
} else {
- return $data['id'];
+ return $data["id"];
}
}
@@ -640,55 +642,384 @@ public function addScienceMeshUser($user)
*/
public function addScienceMeshShare($scienceMeshData, $shareData): int
{
- if ($scienceMeshData['is_external']) {
- return $this->addReceivedShareToDB($shareData);
+ if ($scienceMeshData["is_external"]) {
+ return $this->addReceivedOcmShareToEfssTable($shareData);
} else {
- return $this->createScienceMeshShare($shareData);
+ return $this->addSentOcmShareToEfssTable($shareData);
}
}
/**
- * add share to the database and return the ID
+ * add sent ScienceMesh share to the native efss table and return the ID.
+ *
+ * @param IShare $share
+ * @return int
+ * @throws NotFoundException
+ */
+ protected function addSentOcmShareToEfssTable(IShare $share): int
+ {
+ $shareType = $share->getShareType();
+ $itemType = $share->getNodeType();
+ $itemSource = $share->getNodeId();
+ $shareWith = $share->getSharedWith();
+ $owner = $share->getShareOwner();
+ $sharedBy = $share->getSharedBy();
+ $permissions = $share->getPermissions();
+ $token = $share->getToken();
+ $shareTime = $share->getShareTime()->getTimestamp();
+
+ $qb = $this->dbConnection->getQueryBuilder();
+ $qb->insert("share")
+ ->setValue("share_type", $qb->createNamedParameter($shareType))
+ ->setValue("item_type", $qb->createNamedParameter($itemType))
+ ->setValue("item_source", $qb->createNamedParameter($itemSource))
+ ->setValue("file_source", $qb->createNamedParameter($itemSource))
+ ->setValue("share_with", $qb->createNamedParameter($shareWith))
+ ->setValue("uid_owner", $qb->createNamedParameter($owner))
+ ->setValue("uid_initiator", $qb->createNamedParameter($sharedBy))
+ ->setValue("permissions", $qb->createNamedParameter($permissions))
+ ->setValue("token", $qb->createNamedParameter($token))
+ ->setValue("stime", $qb->createNamedParameter($shareTime));
+
+ /*
+ * Added to fix https://github.com/owncloud/core/issues/22215
+ * Can be removed once we get rid of ajax/share.php
+ */
+ $qb->setValue("file_target", $qb->createNamedParameter(""));
+
+ $qb->execute();
+ return $qb->getLastInsertId();
+ }
+
+
+ /**
+ * add sent ScienceMesh share to the ScienceMesh table and return the ID
+ *
+ * @param $shareData
+ * @return int
+ */
+ public function addSentOcmShareToSciencemeshTable($shareData): int
+ {
+ // check if the share already exists in the database.
+ $qbt = $this->dbConnection->getQueryBuilder();
+ $qbt->select("*")
+ ->from("sciencemesh_ocm_sent_shares")
+ ->where($qbt->expr()->eq("share_internal_id", $qbt->createNamedParameter($shareData["share_internal_id"])));
+ $cursor = $qbt->execute();
+ $data = $cursor->fetch();
+ $cursor->closeCursor();
+
+ // return id if share already exists.
+ if ($data) {
+ return $data["id"];
+ }
+
+ // add ocm share to sciencemesh_ocm_sent_shares table.
+ $qb = $this->dbConnection->getQueryBuilder();
+ $qb->insert("sciencemesh_ocm_sent_shares")
+ ->setValue("share_internal_id", $qb->createNamedParameter($shareData["share_internal_id"]))
+ ->setValue("name", $qb->createNamedParameter($shareData["name"]))
+ ->setValue("share_with", $qb->createNamedParameter($shareData["share_with"]))
+ ->setValue("owner", $qb->createNamedParameter($shareData["owner"]))
+ ->setValue("initiator", $qb->createNamedParameter($shareData["initiator"]))
+ ->setValue("ctime", $qb->createNamedParameter($shareData["ctime"]))
+ ->setValue("ctime", $qb->createNamedParameter($shareData["ctime"]))
+ ->setValue("mtime", $qb->createNamedParameter($shareData["mtime"]))
+ ->setValue("expiration", $qb->createNamedParameter($shareData["expiration"]));
+ $qb->execute();
+
+ $id = $qb->getLastInsertId();
+
+ // add protocols to their tables.
+ $transfer = $shareData["transfer"] ?? null;
+
+ if (isset($transfer["sourceUri"]) && $transfer["sharedSecret"] && $transfer["size"]) {
+ $qb = $this->dbConnection->getQueryBuilder();
+ $qb->insert("sciencemesh_ocm_sent_share_protocol_transfer")
+ ->setValue("ocm_sent_share_id", $qb->createNamedParameter($id))
+ ->setValue("source_uri", $qb->createNamedParameter($transfer["sourceUri"]))
+ ->setValue("shared_secret", $qb->createNamedParameter($transfer["sharedSecret"]))
+ ->setValue("size", $qb->createNamedParameter($transfer["size"]));
+ $qb->execute();
+ }
+
+ $webapp = $shareData["webapp"] ?? null;
+
+ if (isset($webapp["uriTemplate"]) && $webapp["viewMode"]) {
+ $qb = $this->dbConnection->getQueryBuilder();
+ $qb->insert("sciencemesh_ocm_sent_share_protocol_webapp")
+ ->setValue("ocm_sent_share_id", $qb->createNamedParameter($id))
+ ->setValue("uri_template", $qb->createNamedParameter($webapp["uriTemplate"]))
+ ->setValue("view_mode", $qb->createNamedParameter($webapp["viewMode"]));
+ $qb->execute();
+ }
+
+ $webdav = $shareData["webdav"] ?? null;
+
+ if (isset($webdav["uri"]) && $webdav["sharedSecret"] && $webdav["permissions"]) {
+ $qb = $this->dbConnection->getQueryBuilder();
+ $qb->insert("sciencemesh_ocm_sent_share_protocol_webdav")
+ ->setValue("ocm_sent_share_id", $qb->createNamedParameter($id))
+ ->setValue("uri", $qb->createNamedParameter($webdav["uri"]))
+ ->setValue("shared_secret", $qb->createNamedParameter($webdav["sharedSecret"]))
+ ->setValue("permissions", $qb->createNamedParameter($webdav["permissions"]));
+ $qb->execute();
+ }
+
+ return $id;
+ }
+
+ /**
+ * add received ScienceMesh share to the native efss table and return the ID.
*
* @param $shareData
* @return int
*/
- public function addReceivedShareToDB($shareData): int
+ public function addReceivedOcmShareToEfssTable($shareData): int
{
- $mountpoint = "{{TemporaryMountPointName#" . $shareData["name"] . "}}";
- $mountpoint_hash = md5($mountpoint);
+ // calculate the mount point has of the share.
+ $mountPoint = "{{TemporaryMountPointName#" . $shareData["name"] . "}}";
+ $mountPointHash = md5($mountPoint);
+
+ // check if the share already exists in the database.
$qbt = $this->dbConnection->getQueryBuilder();
- $qbt->select('*')
- ->from('share_external')
- ->where($qbt->expr()->eq('user', $qbt->createNamedParameter($shareData["user"])))
- ->andWhere($qbt->expr()->eq('mountpoint_hash', $qbt->createNamedParameter($mountpoint_hash)));
+ $qbt->select("*")
+ ->from("share_external")
+ ->where($qbt->expr()->eq("user", $qbt->createNamedParameter($shareData["user"])))
+ ->andWhere($qbt->expr()->eq("mountpoint_hash", $qbt->createNamedParameter($mountPointHash)));
$cursor = $qbt->execute();
- if ($data = $cursor->fetch()) {
- return $data['id'];
+ $data = $cursor->fetch();
+ $cursor->closeCursor();
+
+ // return id if share already exists.
+ if ($data) {
+ return $data["id"];
}
- if (!str_starts_with(strtolower($shareData["remote"]), 'http://') && !str_starts_with(strtolower($shareData["remote"]), 'https://')) {
+ // NOTE: @Mahdi I don't like this approach.
+ // prefix remote with https if it doesn't start with http or https.
+ if (!str_starts_with(strtolower($shareData["remote"]), "http://") && !str_starts_with(strtolower($shareData["remote"]), "https://")) {
$shareData["remote"] = "https://" . $shareData["remote"];
}
+ // TODO: @Mahdi maybe use enums? for better readability.
// 0 => pending, 1 => accepted, 2 => rejected
- $accepted = 0; //pending
+ $accepted = 0;
$qb = $this->dbConnection->getQueryBuilder();
- $qb->insert('share_external')
- ->setValue('remote', $qb->createNamedParameter($shareData["remote"]))
- ->setValue('remote_id', $qb->createNamedParameter(trim($shareData["remote_id"], '"')))
- ->setValue('share_token', $qb->createNamedParameter($shareData["share_token"]))
- ->setValue('password', $qb->createNamedParameter($shareData["password"]))
- ->setValue('name', $qb->createNamedParameter($shareData["name"]))
- ->setValue('owner', $qb->createNamedParameter($shareData["owner"]))
- ->setValue('user', $qb->createNamedParameter($shareData["user"]))
- ->setValue('mountpoint', $qb->createNamedParameter($mountpoint))
- ->setValue('mountpoint_hash', $qb->createNamedParameter($mountpoint_hash))
- ->setValue('accepted', $qb->createNamedParameter($accepted));
+ $qb->insert("share_external")
+ ->setValue("remote", $qb->createNamedParameter($shareData["remote"]))
+ ->setValue("remote_id", $qb->createNamedParameter(trim($shareData["remote_id"], '"')))
+ ->setValue("share_token", $qb->createNamedParameter($shareData["share_token"]))
+ ->setValue("password", $qb->createNamedParameter($shareData["password"]))
+ ->setValue("name", $qb->createNamedParameter($shareData["name"]))
+ ->setValue("owner", $qb->createNamedParameter($shareData["owner"]))
+ ->setValue("user", $qb->createNamedParameter($shareData["user"]))
+ ->setValue("mountpoint", $qb->createNamedParameter($mountPoint))
+ ->setValue("mountpoint_hash", $qb->createNamedParameter($mountPointHash))
+ ->setValue("accepted", $qb->createNamedParameter($accepted));
$qb->execute();
return $qb->getLastInsertId();
}
+ /**
+ * add received ScienceMesh share to the ScienceMesh table and return the ID
+ *
+ * @param $shareData
+ * @return int
+ */
+ public function addReceivedOcmShareToSciencemeshTable($shareData): int
+ {
+ // check if the share already exists in the database.
+ $qbt = $this->dbConnection->getQueryBuilder();
+ $qbt->select("*")
+ ->from("sciencemesh_ocm_received_shares")
+ ->where($qbt->expr()->eq("share_external_id", $qbt->createNamedParameter($shareData["share_external_id"])));
+ $cursor = $qbt->execute();
+ $data = $cursor->fetch();
+ $cursor->closeCursor();
+
+ // return id if share already exists.
+ if ($data) {
+ return $data["id"];
+ }
+
+ // add ocm share to sciencemesh_ocm_received_shares table.
+ $qb = $this->dbConnection->getQueryBuilder();
+ $qb->insert("sciencemesh_ocm_received_shares")
+ ->setValue("share_external_id", $qb->createNamedParameter($shareData["share_external_id"]))
+ ->setValue("name", $qb->createNamedParameter($shareData["name"]))
+ ->setValue("share_with", $qb->createNamedParameter($shareData["share_with"]))
+ ->setValue("owner", $qb->createNamedParameter($shareData["owner"]))
+ ->setValue("initiator", $qb->createNamedParameter($shareData["initiator"]))
+ ->setValue("ctime", $qb->createNamedParameter($shareData["ctime"]))
+ ->setValue("ctime", $qb->createNamedParameter($shareData["ctime"]))
+ ->setValue("mtime", $qb->createNamedParameter($shareData["mtime"]))
+ ->setValue("expiration", $qb->createNamedParameter($shareData["expiration"]))
+ ->setValue("remote_share_id", $qb->createNamedParameter($shareData["remote_share_id"]));
+ $qb->execute();
+
+ $id = $qb->getLastInsertId();
+
+ // add protocols to their tables.
+ $transfer = $shareData["transfer"] ?? null;
+
+ if (isset($transfer["sourceUri"]) && $transfer["sharedSecret"] && $transfer["size"]) {
+ $qb = $this->dbConnection->getQueryBuilder();
+ $qb->insert("sciencemesh_ocm_received_share_protocol_transfer")
+ ->setValue("ocm_received_share_id", $qb->createNamedParameter($id))
+ ->setValue("source_uri", $qb->createNamedParameter($transfer["sourceUri"]))
+ ->setValue("shared_secret", $qb->createNamedParameter($transfer["sharedSecret"]))
+ ->setValue("size", $qb->createNamedParameter($transfer["size"]));
+ $qb->execute();
+ }
+
+ $webapp = $shareData["webapp"] ?? null;
+
+ if (isset($webapp["uriTemplate"]) && isset($webapp["viewMode"])) {
+ $qb = $this->dbConnection->getQueryBuilder();
+ $qb->insert("sciencemesh_ocm_received_share_protocol_webapp")
+ ->setValue("ocm_received_share_id", $qb->createNamedParameter($id))
+ ->setValue("uri_template", $qb->createNamedParameter($webapp["uriTemplate"]))
+ ->setValue("view_mode", $qb->createNamedParameter($webapp["viewMode"]));
+ $qb->execute();
+ }
+
+ $webdav = $shareData["webdav"] ?? null;
+
+ if (isset($webdav["uri"]) && $webdav["sharedSecret"] && $webdav["permissions"]) {
+ $qb = $this->dbConnection->getQueryBuilder();
+ $qb->insert("sciencemesh_ocm_received_share_protocol_webdav")
+ ->setValue("ocm_received_share_id", $qb->createNamedParameter($id))
+ ->setValue("uri", $qb->createNamedParameter($webdav["uri"]))
+ ->setValue("shared_secret", $qb->createNamedParameter($webdav["sharedSecret"]))
+ ->setValue("permissions", $qb->createNamedParameter($webdav["permissions"]));
+ $qb->execute();
+ }
+
+ return $id;
+ }
+
+ /**
+ * get all the data about the sent OCM share from ScienceMesh table.
+ *
+ * @param $shareId
+ * @return array|null
+ */
+ public function getSentOcmShareFromSciencemeshTable($shareId): ?array
+ {
+ $qbt = $this->dbConnection->getQueryBuilder();
+ $qbt->select("*")
+ ->from("sciencemesh_ocm_sent_shares")
+ ->where($qbt->expr()->eq("share_internal_id", $qbt->createNamedParameter($shareId)));
+ $cursor = $qbt->execute();
+ $data = $cursor->fetch();
+ $cursor->closeCursor();
+
+ return $data;
+ }
+
+ /**
+ * get all the protocols of the sent OCM share from ScienceMesh table.
+ *
+ * @param $ocmShareId
+ * @return array|null
+ */
+ public function getSentOcmShareProtocolsFromSciencemeshTable($ocmShareId): ?array
+ {
+ $qbt = $this->dbConnection->getQueryBuilder();
+ $qbt->select("*")
+ ->from("sciencemesh_ocm_sent_share_protocol_transfer")
+ ->where($qbt->expr()->eq("ocm_sent_share_id", $qbt->createNamedParameter($ocmShareId)));
+ $cursor = $qbt->execute();
+
+ $protocolTransfer = $cursor->fetch() ?? null;
+ $cursor->closeCursor();
+
+ $qbt = $this->dbConnection->getQueryBuilder();
+ $qbt->select("*")
+ ->from("sciencemesh_ocm_sent_share_protocol_webapp")
+ ->where($qbt->expr()->eq("ocm_sent_share_id", $qbt->createNamedParameter($ocmShareId)));
+ $cursor = $qbt->execute();
+
+ $protocolWebApp = $cursor->fetch() ?? null;
+ $cursor->closeCursor();
+
+ $qbt = $this->dbConnection->getQueryBuilder();
+ $qbt->select("*")
+ ->from("sciencemesh_ocm_sent_share_protocol_webdav")
+ ->where($qbt->expr()->eq("ocm_sent_share_id", $qbt->createNamedParameter($ocmShareId)));
+ $cursor = $qbt->execute();
+
+ $protocolWebDav = $cursor->fetch() ?? null;
+ $cursor->closeCursor();
+
+ return [
+ "transfer" => $protocolTransfer,
+ "webapp" => $protocolWebApp,
+ "webdav" => $protocolWebDav,
+ ];
+ }
+
+ /**
+ * get all the data about the received OCM share from ScienceMesh table.
+ *
+ * @param $shareId
+ * @return array|null
+ */
+ public function getReceivedOcmShareFromSciencemeshTable($shareId): ?array
+ {
+ $qbt = $this->dbConnection->getQueryBuilder();
+ $qbt->select("*")
+ ->from("sciencemesh_ocm_received_shares")
+ ->where($qbt->expr()->eq("share_external_id", $qbt->createNamedParameter($shareId)));
+ $cursor = $qbt->execute();
+ $data = $cursor->fetch();
+ $cursor->closeCursor();
+
+ return $data;
+ }
+
+ /**
+ * get all the protocols of the received OCM share from ScienceMesh table.
+ *
+ * @param $ocmShareId
+ * @return array|null
+ */
+ public function getReceivedOcmShareProtocolsFromSciencemeshTable($ocmShareId): ?array
+ {
+ $qbt = $this->dbConnection->getQueryBuilder();
+ $qbt->select("*")
+ ->from("sciencemesh_ocm_received_share_protocol_transfer")
+ ->where($qbt->expr()->eq("ocm_received_share_id", $qbt->createNamedParameter($ocmShareId)));
+ $cursor = $qbt->execute();
+
+ $protocolTransfer = $cursor->fetch() ?? null;
+ $cursor->closeCursor();
+
+ $qbt = $this->dbConnection->getQueryBuilder();
+ $qbt->select("*")
+ ->from("sciencemesh_ocm_received_share_protocol_webapp")
+ ->where($qbt->expr()->eq("ocm_received_share_id", $qbt->createNamedParameter($ocmShareId)));
+ $cursor = $qbt->execute();
+
+ $protocolWebApp = $cursor->fetch() ?? null;
+ $cursor->closeCursor();
+
+ $qbt = $this->dbConnection->getQueryBuilder();
+ $qbt->select("*")
+ ->from("sciencemesh_ocm_received_share_protocol_webdav")
+ ->where($qbt->expr()->eq("ocm_received_share_id", $qbt->createNamedParameter($ocmShareId)));
+ $cursor = $qbt->execute();
+
+ $protocolWebDav = $cursor->fetch() ?? null;
+ $cursor->closeCursor();
+
+ return [
+ "transfer" => $protocolTransfer,
+ "webapp" => $protocolWebApp,
+ "webdav" => $protocolWebDav,
+ ];
+ }
protected function revokeShare(IShare $share, bool $isOwner)
{