all: force permissions for external shares

pull/465/head
Varun Patil 2023-03-09 16:39:26 -08:00
parent b3fa60ce0c
commit af2a095059
7 changed files with 98 additions and 12 deletions

View File

@ -27,6 +27,7 @@ use OCA\Memories\AppInfo\Application;
use OCA\Memories\Db\TimelineQuery;
use OCA\Memories\Db\TimelineRoot;
use OCA\Memories\Exif;
use OCA\Memories\Util;
use OCP\App\IAppManager;
use OCP\AppFramework\Controller;
use OCP\AppFramework\Http;
@ -168,6 +169,9 @@ class ApiBase extends Controller
}
$userFolder = $this->rootFolder->getUserFolder($user->getUID());
// No need to force permissions when reading
// from the user's own folder. This includes shared
// folders and files from other users.
return $this->getOneFileFromFolder($userFolder, $id);
}
@ -189,7 +193,10 @@ class ApiBase extends Controller
$folder = $this->rootFolder->getUserFolder($owner);
return $this->getOneFileFromFolder($folder, $id);
// Album files are always read-only
// Note that albums have lowest priority, so it means the
// user doesn't have access to the file in their own folder.
return $this->getOneFileFromFolder($folder, $id, \OCP\Constants::PERMISSION_READ);
}
/**
@ -214,12 +221,15 @@ class ApiBase extends Controller
$folder = $this->rootFolder->getUserFolder($owner);
return $this->getOneFileFromFolder($folder, $id);
// Public albums are always read-only
return $this->getOneFileFromFolder($folder, $id, \OCP\Constants::PERMISSION_READ);
}
// Folder share
if ($share = $this->getShareNode()) {
return $this->getOneFileFromFolder($share, $id);
// Public shares may allow editing
// Just use the same permissions as the share
return $this->getOneFileFromFolder($share, $id, $share->getPermissions());
}
} catch (\Exception $e) {
}
@ -295,6 +305,9 @@ class ApiBase extends Controller
throw new \Exception('Share not found or invalid');
}
// Force permissions from the share onto the node
Util::forcePermissions($node, $share->getPermissions());
return $node;
}
@ -385,25 +398,35 @@ class ApiBase extends Controller
/**
* Helper to get one file or null from a fiolder.
*
* @param Folder $folder Folder to search in
* @param int $id Id of the file
* @param int $perm Permissions to force on the file
*/
private function getOneFileFromFolder(Folder $folder, int $id): ?File
private function getOneFileFromFolder(Folder $folder, int $id, int $perm = -1): ?File
{
// Check for permissions and get numeric Id
$file = $folder->getById($id);
if (0 === \count($file)) {
return null;
}
$file = $file[0];
// Check if node is a file
if (!$file[0] instanceof File) {
if (!$file instanceof File) {
return null;
}
// Check read permission
if (!($file[0]->getPermissions() & \OCP\Constants::PERMISSION_READ)) {
if (!$file->isReadable()) {
return null;
}
return $file[0];
// Force file permissions if required
if ($perm >= 0) {
Util::forcePermissions($file, $perm);
}
return $file;
}
}

View File

@ -206,6 +206,9 @@ class ImageController extends ApiBase
}
}
// Inject permissions and convert to string
$info['permissions'] = \OCA\Memories\Util::permissionsToStr($file->getPermissions());
return new JSONResponse($info, Http::STATUS_OK);
}

View File

@ -5,6 +5,7 @@ declare(strict_types=1);
namespace OCA\Memories;
use OCP\App\IAppManager;
use OCP\Files\Node;
use OCP\IConfig;
class Util
@ -127,6 +128,57 @@ class Util
return true;
}
/**
* Force a fileinfo value on a node.
* This is a hack to avoid subclassing everything.
*
* @param mixed $node File to patch
* @param mixed $key Key to set
* @param mixed $value Value to set
*/
public static function forceFileInfo(Node &$node, $key, $value)
{
/** @var \OC\Files\Node\Node */
$node = $node;
$node->getFileInfo()[$key] = $value;
}
/**
* Force permissions on a node.
*
* @param mixed $node File to patch
* @param mixed $permissions Permissions to set
*/
public static function forcePermissions(Node &$node, int $permissions)
{
self::forceFileInfo($node, 'permissions', $permissions);
}
/**
* Convert permissions to string.
*/
public static function permissionsToStr(int $permissions): string
{
$str = '';
if ($permissions & \OCP\Constants::PERMISSION_CREATE) {
$str .= 'C';
}
if ($permissions & \OCP\Constants::PERMISSION_READ) {
$str .= 'R';
}
if ($permissions & \OCP\Constants::PERMISSION_UPDATE) {
$str .= 'U';
}
if ($permissions & \OCP\Constants::PERMISSION_DELETE) {
$str .= 'D';
}
if ($permissions & \OCP\Constants::PERMISSION_SHARE) {
$str .= 'S';
}
return $str;
}
/**
* Check if any encryption is enabled that we can not cope with
* such as end-to-end encryption.

View File

@ -172,7 +172,7 @@ export default defineComponent({
},
canEdit(): boolean {
return !this.$route.name?.endsWith("-share");
return this.baseInfo?.permissions?.includes("U");
},
/** Date taken info */
@ -246,7 +246,11 @@ export default defineComponent({
/** Image info */
imageInfo(): string | null {
return this.fileInfo?.basename || (<any>this.fileInfo)?.name;
return (
this.fileInfo?.originalBasename ||
this.fileInfo?.basename ||
(<any>this.fileInfo)?.name
);
},
imageInfoSub(): string[] | null {

View File

@ -1,7 +1,7 @@
<template>
<aside class="app-sidebar" v-if="reducedOpen">
<div class="title">
<h2>{{ filename }}</h2>
<h2>{{ basename }}</h2>
<NcActions :inline="1">
<NcActionButton :aria-label="t('memories', 'Close')" @click="close()">
@ -40,7 +40,7 @@ export default defineComponent({
return {
nativeOpen: false,
reducedOpen: false,
filename: "",
basename: "",
};
},
@ -72,7 +72,7 @@ export default defineComponent({
} else {
this.reducedOpen = true;
await this.$nextTick();
this.filename = file.basename;
this.basename = file.originalBasename || file.basename;
(<any>this.$refs.metadata)?.update(file);
emit("memories:sidebar:opened", null);

View File

@ -285,6 +285,7 @@ export function getAlbumFileInfos(
filename: `${collection}/${basename}`,
originalFilename: `${collection}/${basename}`,
basename: basename,
originalBasename: photo.basename,
mime: photo.mimetype,
hasPreview: true,
etag: photo.etag,

View File

@ -9,6 +9,8 @@ export type IFileInfo = {
originalFilename?: string;
/** Base name of file e.g. Qx0dq7dvEXA.jpg */
basename: string;
/** Original base name, e.g. in albums without the file id */
originalBasename?: string;
/** Etag identifier */
etag: string;
/** File has preview available */
@ -78,6 +80,7 @@ export type IPhoto = {
datetaken: number;
address?: string;
tags: { [id: string]: string };
permissions: string;
exif?: {
Rotation?: number;
Orientation?: number;