migrate therinaldos.com data
Build & Deploy to DigitalOcean Space / build (push) Failing after 2m38s

This commit is contained in:
2024-05-05 15:50:45 -04:00
commit ef1ff240d4
23182 changed files with 3801898 additions and 0 deletions
@@ -0,0 +1,125 @@
<?php
/**
* @copyright Copyright (c) 2016 Joas Schilling <coding@schilljs.com>
*
* @author Arthur Schiwon <blizzz@arthur-schiwon.de>
* @author Christoph Wurst <christoph@winzerhof-wurst.at>
* @author Joas Schilling <coding@schilljs.com>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\WorkflowEngine\Check;
use OCP\IL10N;
use OCP\WorkflowEngine\ICheck;
use OCP\WorkflowEngine\IManager;
abstract class AbstractStringCheck implements ICheck {
/** @var array[] Nested array: [Pattern => [ActualValue => Regex Result]] */
protected $matches;
/** @var IL10N */
protected $l;
/**
* @param IL10N $l
*/
public function __construct(IL10N $l) {
$this->l = $l;
}
/**
* @return string
*/
abstract protected function getActualValue();
/**
* @param string $operator
* @param string $value
* @return bool
*/
public function executeCheck($operator, $value) {
$actualValue = $this->getActualValue();
return $this->executeStringCheck($operator, $value, $actualValue);
}
/**
* @param string $operator
* @param string $checkValue
* @param string $actualValue
* @return bool
*/
protected function executeStringCheck($operator, $checkValue, $actualValue) {
if ($operator === 'is') {
return $checkValue === $actualValue;
} elseif ($operator === '!is') {
return $checkValue !== $actualValue;
} else {
$match = $this->match($checkValue, $actualValue);
if ($operator === 'matches') {
return $match === 1;
} else {
return $match === 0;
}
}
}
/**
* @param string $operator
* @param string $value
* @throws \UnexpectedValueException
*/
public function validateCheck($operator, $value) {
if (!in_array($operator, ['is', '!is', 'matches', '!matches'])) {
throw new \UnexpectedValueException($this->l->t('The given operator is invalid'), 1);
}
if (in_array($operator, ['matches', '!matches']) &&
@preg_match($value, null) === false) {
throw new \UnexpectedValueException($this->l->t('The given regular expression is invalid'), 2);
}
}
public function supportedEntities(): array {
// universal by default
return [];
}
public function isAvailableForScope(int $scope): bool {
// admin only by default
return $scope === IManager::SCOPE_ADMIN;
}
/**
* @param string $pattern
* @param string $subject
* @return int|bool
*/
protected function match($pattern, $subject) {
$patternHash = md5($pattern);
$subjectHash = md5($subject);
if (isset($this->matches[$patternHash][$subjectHash])) {
return $this->matches[$patternHash][$subjectHash];
}
if (!isset($this->matches[$patternHash])) {
$this->matches[$patternHash] = [];
}
$this->matches[$patternHash][$subjectHash] = preg_match($pattern, $subject);
return $this->matches[$patternHash][$subjectHash];
}
}
@@ -0,0 +1,179 @@
<?php
/**
* @copyright Copyright (c) 2016 Joas Schilling <coding@schilljs.com>
*
* @author Arthur Schiwon <blizzz@arthur-schiwon.de>
* @author Joas Schilling <coding@schilljs.com>
* @author Julius Härtl <jus@bitgrid.net>
* @author Morris Jobke <hey@morrisjobke.de>
* @author Roeland Jago Douma <roeland@famdouma.nl>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\WorkflowEngine\Check;
use OC\Files\Storage\Local;
use OCA\WorkflowEngine\Entity\File;
use OCP\Files\IMimeTypeDetector;
use OCP\Files\Storage\IStorage;
use OCP\IL10N;
use OCP\IRequest;
use OCP\WorkflowEngine\IFileCheck;
class FileMimeType extends AbstractStringCheck implements IFileCheck {
use TFileCheck {
setFileInfo as _setFileInfo;
}
/** @var array */
protected $mimeType;
/** @var IRequest */
protected $request;
/** @var IMimeTypeDetector */
protected $mimeTypeDetector;
/**
* @param IL10N $l
* @param IRequest $request
* @param IMimeTypeDetector $mimeTypeDetector
*/
public function __construct(IL10N $l, IRequest $request, IMimeTypeDetector $mimeTypeDetector) {
parent::__construct($l);
$this->request = $request;
$this->mimeTypeDetector = $mimeTypeDetector;
}
/**
* @param IStorage $storage
* @param string $path
* @param bool $isDir
*/
public function setFileInfo(IStorage $storage, string $path, bool $isDir = false): void {
$this->_setFileInfo($storage, $path, $isDir);
if (!isset($this->mimeType[$this->storage->getId()][$this->path])
|| $this->mimeType[$this->storage->getId()][$this->path] === '') {
if ($isDir) {
$this->mimeType[$this->storage->getId()][$this->path] = 'httpd/unix-directory';
} else {
$this->mimeType[$this->storage->getId()][$this->path] = null;
}
}
}
/**
* The mimetype is only cached if the file has a valid mimetype. Otherwise files access
* control will cache "application/octet-stream" for all the target node on:
* rename, move, copy and all other methods which create a new item
*
* To check this:
* 1. Add an automated tagging rule which tags on mimetype NOT "httpd/unix-directory"
* 2. Add an access control rule which checks for any mimetype
* 3. Create a folder and rename it, the folder should not be tagged, but it is
*
* @param string $storageId
* @param string|null $path
* @param string $mimeType
* @return string
*/
protected function cacheAndReturnMimeType(string $storageId, ?string $path, string $mimeType): string {
if ($path !== null && $mimeType !== 'application/octet-stream') {
$this->mimeType[$storageId][$path] = $mimeType;
}
return $mimeType;
}
/**
* Make sure that even though the content based check returns an application/octet-stream can still be checked based on mimetypemappings of their extension
*
* @param string $operator
* @param string $value
* @return bool
*/
public function executeCheck($operator, $value) {
return $this->executeStringCheck($operator, $value, $this->getActualValue());
}
/**
* @return string
*/
protected function getActualValue() {
if ($this->mimeType[$this->storage->getId()][$this->path] !== null) {
return $this->mimeType[$this->storage->getId()][$this->path];
}
$cacheEntry = $this->storage->getCache()->get($this->path);
if ($cacheEntry && $cacheEntry->getMimeType() !== 'application/octet-stream') {
return $this->cacheAndReturnMimeType($this->storage->getId(), $this->path, $cacheEntry->getMimeType());
}
if ($this->storage->file_exists($this->path) &&
$this->storage->filesize($this->path) &&
$this->storage->instanceOfStorage(Local::class)
) {
$path = $this->storage->getLocalFile($this->path);
$mimeType = $this->mimeTypeDetector->detectContent($path);
return $this->cacheAndReturnMimeType($this->storage->getId(), $this->path, $mimeType);
}
if ($this->isWebDAVRequest() || $this->isPublicWebDAVRequest()) {
// Creating a folder
if ($this->request->getMethod() === 'MKCOL') {
return 'httpd/unix-directory';
}
}
// We do not cache this, as the file did not exist yet.
// In case it does in the future, we will check with detectContent()
// again to get the real mimetype of the content, rather than
// guessing it from the path.
return $this->mimeTypeDetector->detectPath($this->path);
}
/**
* @return bool
*/
protected function isWebDAVRequest() {
return substr($this->request->getScriptName(), 0 - strlen('/remote.php')) === '/remote.php' && (
$this->request->getPathInfo() === '/webdav' ||
str_starts_with($this->request->getPathInfo() ?? '', '/webdav/') ||
$this->request->getPathInfo() === '/dav/files' ||
str_starts_with($this->request->getPathInfo() ?? '', '/dav/files/') ||
$this->request->getPathInfo() === '/dav/uploads' ||
str_starts_with($this->request->getPathInfo() ?? '', '/dav/uploads/')
);
}
/**
* @return bool
*/
protected function isPublicWebDAVRequest() {
return substr($this->request->getScriptName(), 0 - strlen('/public.php')) === '/public.php' && (
$this->request->getPathInfo() === '/webdav' ||
str_starts_with($this->request->getPathInfo() ?? '', '/webdav/')
);
}
public function supportedEntities(): array {
return [ File::class ];
}
public function isAvailableForScope(int $scope): bool {
return true;
}
}
@@ -0,0 +1,97 @@
<?php
declare(strict_types=1);
/**
* @copyright Copyright (c) 2018 Daniel Kesselberg <mail@danielkesselberg.de>
*
* @author Arthur Schiwon <blizzz@arthur-schiwon.de>
* @author Daniel Kesselberg <mail@danielkesselberg.de>
* @author szaimen <szaimen@e.mail.de>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\WorkflowEngine\Check;
use OC\Files\Storage\Local;
use OCA\WorkflowEngine\Entity\File;
use OCP\Files\Mount\IMountManager;
use OCP\IL10N;
use OCP\IRequest;
use OCP\WorkflowEngine\IFileCheck;
class FileName extends AbstractStringCheck implements IFileCheck {
use TFileCheck;
/** @var IRequest */
protected $request;
/** @var IMountManager */
private $mountManager;
/**
* @param IL10N $l
* @param IRequest $request
*/
public function __construct(IL10N $l, IRequest $request, IMountManager $mountManager) {
parent::__construct($l);
$this->request = $request;
$this->mountManager = $mountManager;
}
/**
* @return string
*/
protected function getActualValue(): string {
$fileName = $this->path === null ? '' : basename($this->path);
if ($fileName === '' && (!$this->storage->isLocal() || $this->storage->instanceOfStorage(Local::class))) {
// Return the mountpoint name of external storage that are not mounted as user home
$mountPoints = $this->mountManager->findByStorageId($this->storage->getId());
if (empty($mountPoints) || $mountPoints[0]->getMountType() !== 'external') {
return $fileName;
}
$mountPointPath = rtrim($mountPoints[0]->getMountPoint(), '/');
$mountPointPieces = explode('/', $mountPointPath);
$mountPointName = array_pop($mountPointPieces);
if (!empty($mountPointName) && $mountPointName !== 'files' && count($mountPointPieces) !== 2) {
return $mountPointName;
}
}
return $fileName;
}
/**
* @param string $operator
* @param string $checkValue
* @param string $actualValue
* @return bool
*/
protected function executeStringCheck($operator, $checkValue, $actualValue): bool {
if ($operator === 'is' || $operator === '!is') {
$checkValue = mb_strtolower($checkValue);
$actualValue = mb_strtolower($actualValue);
}
return parent::executeStringCheck($operator, $checkValue, $actualValue);
}
public function supportedEntities(): array {
return [ File::class ];
}
public function isAvailableForScope(int $scope): bool {
return true;
}
}
@@ -0,0 +1,122 @@
<?php
/**
* @copyright Copyright (c) 2016 Joas Schilling <coding@schilljs.com>
*
* @author Arthur Schiwon <blizzz@arthur-schiwon.de>
* @author Joas Schilling <coding@schilljs.com>
* @author Morris Jobke <hey@morrisjobke.de>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\WorkflowEngine\Check;
use OCA\WorkflowEngine\Entity\File;
use OCP\IL10N;
use OCP\IRequest;
use OCP\Util;
use OCP\WorkflowEngine\ICheck;
class FileSize implements ICheck {
/** @var int */
protected $size;
/** @var IL10N */
protected $l;
/** @var IRequest */
protected $request;
/**
* @param IL10N $l
* @param IRequest $request
*/
public function __construct(IL10N $l, IRequest $request) {
$this->l = $l;
$this->request = $request;
}
/**
* @param string $operator
* @param string $value
* @return bool
*/
public function executeCheck($operator, $value) {
$size = $this->getFileSizeFromHeader();
$value = Util::computerFileSize($value);
if ($size !== false) {
switch ($operator) {
case 'less':
return $size < $value;
case '!less':
return $size >= $value;
case 'greater':
return $size > $value;
case '!greater':
return $size <= $value;
}
}
return false;
}
/**
* @param string $operator
* @param string $value
* @throws \UnexpectedValueException
*/
public function validateCheck($operator, $value) {
if (!in_array($operator, ['less', '!less', 'greater', '!greater'])) {
throw new \UnexpectedValueException($this->l->t('The given operator is invalid'), 1);
}
if (!preg_match('/^[0-9]+[ ]?[kmgt]?b$/i', $value)) {
throw new \UnexpectedValueException($this->l->t('The given file size is invalid'), 2);
}
}
/**
* @return string
*/
protected function getFileSizeFromHeader() {
if ($this->size !== null) {
return $this->size;
}
$size = $this->request->getHeader('OC-Total-Length');
if ($size === '') {
if (in_array($this->request->getMethod(), ['POST', 'PUT'])) {
$size = $this->request->getHeader('Content-Length');
}
}
if ($size === '') {
$size = false;
}
$this->size = $size;
return $this->size;
}
public function supportedEntities(): array {
return [ File::class ];
}
public function isAvailableForScope(int $scope): bool {
return true;
}
}
@@ -0,0 +1,209 @@
<?php
/**
* @copyright Copyright (c) 2016 Joas Schilling <coding@schilljs.com>
*
* @author Arthur Schiwon <blizzz@arthur-schiwon.de>
* @author Christoph Wurst <christoph@winzerhof-wurst.at>
* @author Joas Schilling <coding@schilljs.com>
* @author Julius Härtl <jus@bitgrid.net>
* @author Richard Steinmetz <richard@steinmetz.cloud>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\WorkflowEngine\Check;
use OC\Files\Storage\Wrapper\Wrapper;
use OCA\Files_Sharing\SharedStorage;
use OCA\WorkflowEngine\Entity\File;
use OCP\Files\Cache\ICache;
use OCP\Files\IHomeStorage;
use OCP\IGroupManager;
use OCP\IL10N;
use OCP\IUser;
use OCP\IUserSession;
use OCP\SystemTag\ISystemTagManager;
use OCP\SystemTag\ISystemTagObjectMapper;
use OCP\SystemTag\TagNotFoundException;
use OCP\WorkflowEngine\ICheck;
use OCP\WorkflowEngine\IFileCheck;
class FileSystemTags implements ICheck, IFileCheck {
use TFileCheck;
/** @var array */
protected $fileIds;
/** @var array */
protected $fileSystemTags;
/** @var IL10N */
protected $l;
/** @var ISystemTagManager */
protected $systemTagManager;
/** @var ISystemTagObjectMapper */
protected $systemTagObjectMapper;
/** @var IUserSession */
protected $userSession;
/** @var IGroupManager */
protected $groupManager;
public function __construct(
IL10N $l,
ISystemTagManager $systemTagManager,
ISystemTagObjectMapper $systemTagObjectMapper,
IUserSession $userSession,
IGroupManager $groupManager
) {
$this->l = $l;
$this->systemTagManager = $systemTagManager;
$this->systemTagObjectMapper = $systemTagObjectMapper;
$this->userSession = $userSession;
$this->groupManager = $groupManager;
}
/**
* @param string $operator
* @param string $value
* @return bool
*/
public function executeCheck($operator, $value) {
$systemTags = $this->getSystemTags();
return ($operator === 'is') === in_array($value, $systemTags);
}
/**
* @param string $operator
* @param string $value
* @throws \UnexpectedValueException
*/
public function validateCheck($operator, $value) {
if (!in_array($operator, ['is', '!is'])) {
throw new \UnexpectedValueException($this->l->t('The given operator is invalid'), 1);
}
try {
$tags = $this->systemTagManager->getTagsByIds($value);
$user = $this->userSession->getUser();
$isAdmin = $user instanceof IUser && $this->groupManager->isAdmin($user->getUID());
if (!$isAdmin) {
foreach ($tags as $tag) {
if (!$tag->isUserVisible()) {
throw new \UnexpectedValueException($this->l->t('The given tag id is invalid'), 4);
}
}
}
} catch (TagNotFoundException $e) {
throw new \UnexpectedValueException($this->l->t('The given tag id is invalid'), 2);
} catch (\InvalidArgumentException $e) {
throw new \UnexpectedValueException($this->l->t('The given tag id is invalid'), 3);
}
}
/**
* Get the ids of the assigned system tags
* @return string[]
*/
protected function getSystemTags() {
$cache = $this->storage->getCache();
$fileIds = $this->getFileIds($cache, $this->path, !$this->storage->instanceOfStorage(IHomeStorage::class) || $this->storage->instanceOfStorage(SharedStorage::class));
$systemTags = [];
foreach ($fileIds as $i => $fileId) {
if (isset($this->fileSystemTags[$fileId])) {
$systemTags[] = $this->fileSystemTags[$fileId];
unset($fileIds[$i]);
}
}
if (!empty($fileIds)) {
$mappedSystemTags = $this->systemTagObjectMapper->getTagIdsForObjects($fileIds, 'files');
foreach ($mappedSystemTags as $fileId => $fileSystemTags) {
$this->fileSystemTags[$fileId] = $fileSystemTags;
$systemTags[] = $fileSystemTags;
}
}
$systemTags = call_user_func_array('array_merge', $systemTags);
$systemTags = array_unique($systemTags);
return $systemTags;
}
/**
* Get the file ids of the given path and its parents
* @param ICache $cache
* @param string $path
* @param bool $isExternalStorage
* @return int[]
*/
protected function getFileIds(ICache $cache, $path, $isExternalStorage) {
/** @psalm-suppress InvalidArgument */
if ($this->storage->instanceOfStorage(\OCA\GroupFolders\Mount\GroupFolderStorage::class)) {
// Special implementation for groupfolder since all groupfolders share the same storage
// id so add the group folder id in the cache key too.
$groupFolderStorage = $this->storage;
if ($this->storage instanceof Wrapper) {
$groupFolderStorage = $this->storage->getInstanceOfStorage(\OCA\GroupFolders\Mount\GroupFolderStorage::class);
}
if ($groupFolderStorage === null) {
throw new \LogicException('Should not happen: Storage is instance of GroupFolderStorage but no group folder storage found while unwrapping.');
}
/**
* @psalm-suppress UndefinedDocblockClass
* @psalm-suppress UndefinedInterfaceMethod
*/
$cacheId = $cache->getNumericStorageId() . '/' . $groupFolderStorage->getFolderId();
} else {
$cacheId = $cache->getNumericStorageId();
}
if (isset($this->fileIds[$cacheId][$path])) {
return $this->fileIds[$cacheId][$path];
}
$parentIds = [];
if ($path !== $this->dirname($path)) {
$parentIds = $this->getFileIds($cache, $this->dirname($path), $isExternalStorage);
} elseif (!$isExternalStorage) {
return [];
}
$fileId = $cache->getId($path);
if ($fileId !== -1) {
$parentIds[] = $fileId;
}
$this->fileIds[$cacheId][$path] = $parentIds;
return $parentIds;
}
protected function dirname($path) {
$dir = dirname($path);
return $dir === '.' ? '' : $dir;
}
public function supportedEntities(): array {
return [ File::class ];
}
public function isAvailableForScope(int $scope): bool {
return true;
}
}
@@ -0,0 +1,177 @@
<?php
/**
* @copyright Copyright (c) 2016 Joas Schilling <coding@schilljs.com>
*
* @author Christoph Wurst <christoph@winzerhof-wurst.at>
* @author J0WI <J0WI@users.noreply.github.com>
* @author Joas Schilling <coding@schilljs.com>
* @author Julius Härtl <jus@bitgrid.net>
* @author Morris Jobke <hey@morrisjobke.de>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\WorkflowEngine\Check;
use OCP\IL10N;
use OCP\IRequest;
use OCP\WorkflowEngine\ICheck;
class RequestRemoteAddress implements ICheck {
/** @var IL10N */
protected $l;
/** @var IRequest */
protected $request;
/**
* @param IL10N $l
* @param IRequest $request
*/
public function __construct(IL10N $l, IRequest $request) {
$this->l = $l;
$this->request = $request;
}
/**
* @param string $operator
* @param string $value
* @return bool
*/
public function executeCheck($operator, $value) {
$actualValue = $this->request->getRemoteAddress();
$decodedValue = explode('/', $value);
if ($operator === 'matchesIPv4') {
return $this->matchIPv4($actualValue, $decodedValue[0], $decodedValue[1]);
} elseif ($operator === '!matchesIPv4') {
return !$this->matchIPv4($actualValue, $decodedValue[0], $decodedValue[1]);
} elseif ($operator === 'matchesIPv6') {
return $this->matchIPv6($actualValue, $decodedValue[0], $decodedValue[1]);
} else {
return !$this->matchIPv6($actualValue, $decodedValue[0], $decodedValue[1]);
}
}
/**
* @param string $operator
* @param string $value
* @throws \UnexpectedValueException
*/
public function validateCheck($operator, $value) {
if (!in_array($operator, ['matchesIPv4', '!matchesIPv4', 'matchesIPv6', '!matchesIPv6'])) {
throw new \UnexpectedValueException($this->l->t('The given operator is invalid'), 1);
}
$decodedValue = explode('/', $value);
if (count($decodedValue) !== 2) {
throw new \UnexpectedValueException($this->l->t('The given IP range is invalid'), 2);
}
if (in_array($operator, ['matchesIPv4', '!matchesIPv4'])) {
if (!filter_var($decodedValue[0], FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
throw new \UnexpectedValueException($this->l->t('The given IP range is not valid for IPv4'), 3);
}
if ($decodedValue[1] > 32 || $decodedValue[1] <= 0) {
throw new \UnexpectedValueException($this->l->t('The given IP range is not valid for IPv4'), 4);
}
} else {
if (!filter_var($decodedValue[0], FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {
throw new \UnexpectedValueException($this->l->t('The given IP range is not valid for IPv6'), 3);
}
if ($decodedValue[1] > 128 || $decodedValue[1] <= 0) {
throw new \UnexpectedValueException($this->l->t('The given IP range is not valid for IPv6'), 4);
}
}
}
/**
* Based on https://stackoverflow.com/a/594134
* @param string $ip
* @param string $rangeIp
* @param int $bits
* @return bool
*/
protected function matchIPv4($ip, $rangeIp, $bits) {
$rangeDecimal = ip2long($rangeIp);
$ipDecimal = ip2long($ip);
$mask = -1 << (32 - $bits);
return ($ipDecimal & $mask) === ($rangeDecimal & $mask);
}
/**
* Based on https://stackoverflow.com/a/7951507
* @param string $ip
* @param string $rangeIp
* @param int $bits
* @return bool
*/
protected function matchIPv6($ip, $rangeIp, $bits) {
$ipNet = inet_pton($ip);
$binaryIp = $this->ipv6ToBits($ipNet);
$ipNetBits = substr($binaryIp, 0, $bits);
$rangeNet = inet_pton($rangeIp);
$binaryRange = $this->ipv6ToBits($rangeNet);
$rangeNetBits = substr($binaryRange, 0, $bits);
return $ipNetBits === $rangeNetBits;
}
/**
* Based on https://stackoverflow.com/a/7951507
* @param string $packedIp
* @return string
*/
protected function ipv6ToBits($packedIp) {
$unpackedIp = unpack('A16', $packedIp);
$unpackedIp = str_split($unpackedIp[1]);
$binaryIp = '';
foreach ($unpackedIp as $char) {
$binaryIp .= str_pad(decbin(ord($char)), 8, '0', STR_PAD_LEFT);
}
return str_pad($binaryIp, 128, '0', STR_PAD_RIGHT);
}
/**
* returns a list of Entities the checker supports. The values must match
* the class name of the entity.
*
* An empty result means the check is universally available.
*
* @since 18.0.0
*/
public function supportedEntities(): array {
return [];
}
/**
* returns whether the operation can be used in the requested scope.
*
* Scope IDs are defined as constants in OCP\WorkflowEngine\IManager. At
* time of writing these are SCOPE_ADMIN and SCOPE_USER.
*
* For possibly unknown future scopes the recommended behaviour is: if
* user scope is permitted, the default behaviour should return `true`,
* otherwise `false`.
*
* @since 18.0.0
*/
public function isAvailableForScope(int $scope): bool {
return true;
}
}
@@ -0,0 +1,138 @@
<?php
/**
* @copyright Copyright (c) 2016 Joas Schilling <coding@schilljs.com>
*
* @author Arthur Schiwon <blizzz@arthur-schiwon.de>
* @author Christoph Wurst <christoph@winzerhof-wurst.at>
* @author Joas Schilling <coding@schilljs.com>
* @author Julius Härtl <jus@bitgrid.net>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\WorkflowEngine\Check;
use OCP\AppFramework\Utility\ITimeFactory;
use OCP\IL10N;
use OCP\WorkflowEngine\ICheck;
class RequestTime implements ICheck {
public const REGEX_TIME = '([0-1][0-9]|2[0-3]):([0-5][0-9])';
public const REGEX_TIMEZONE = '([a-zA-Z]+(?:\\/[a-zA-Z\-\_]+)+)';
/** @var bool[] */
protected $cachedResults;
/** @var IL10N */
protected $l;
/** @var ITimeFactory */
protected $timeFactory;
/**
* @param ITimeFactory $timeFactory
*/
public function __construct(IL10N $l, ITimeFactory $timeFactory) {
$this->l = $l;
$this->timeFactory = $timeFactory;
}
/**
* @param string $operator
* @param string $value
* @return bool
*/
public function executeCheck($operator, $value) {
$valueHash = md5($value);
if (isset($this->cachedResults[$valueHash])) {
return $this->cachedResults[$valueHash];
}
$timestamp = $this->timeFactory->getTime();
$values = json_decode($value, true);
$timestamp1 = $this->getTimestamp($timestamp, $values[0]);
$timestamp2 = $this->getTimestamp($timestamp, $values[1]);
if ($timestamp1 < $timestamp2) {
$in = $timestamp1 <= $timestamp && $timestamp <= $timestamp2;
} else {
$in = $timestamp1 <= $timestamp || $timestamp <= $timestamp2;
}
return ($operator === 'in') ? $in : !$in;
}
/**
* @param int $currentTimestamp
* @param string $value Format: "H:i e"
* @return int
*/
protected function getTimestamp($currentTimestamp, $value) {
[$time1, $timezone1] = explode(' ', $value);
[$hour1, $minute1] = explode(':', $time1);
$date1 = new \DateTime('now', new \DateTimeZone($timezone1));
$date1->setTimestamp($currentTimestamp);
$date1->setTime($hour1, $minute1);
return $date1->getTimestamp();
}
/**
* @param string $operator
* @param string $value
* @throws \UnexpectedValueException
*/
public function validateCheck($operator, $value) {
if (!in_array($operator, ['in', '!in'])) {
throw new \UnexpectedValueException($this->l->t('The given operator is invalid'), 1);
}
$regexValue = '\"' . self::REGEX_TIME . ' ' . self::REGEX_TIMEZONE . '\"';
$result = preg_match('/^\[' . $regexValue . ',' . $regexValue . '\]$/', $value, $matches);
if (!$result) {
throw new \UnexpectedValueException($this->l->t('The given time span is invalid'), 2);
}
$values = json_decode($value, true);
$time1 = \DateTime::createFromFormat('H:i e', (string)$values[0]);
if ($time1 === false) {
throw new \UnexpectedValueException($this->l->t('The given start time is invalid'), 3);
}
$time2 = \DateTime::createFromFormat('H:i e', (string)$values[1]);
if ($time2 === false) {
throw new \UnexpectedValueException($this->l->t('The given end time is invalid'), 4);
}
}
public function isAvailableForScope(int $scope): bool {
return true;
}
/**
* returns a list of Entities the checker supports. The values must match
* the class name of the entity.
*
* An empty result means the check is universally available.
*
* @since 18.0.0
*/
public function supportedEntities(): array {
return [];
}
}
@@ -0,0 +1,98 @@
<?php
/**
* @copyright Copyright (c) 2016 Joas Schilling <coding@schilljs.com>
*
* @author Christoph Wurst <christoph@winzerhof-wurst.at>
* @author Joas Schilling <coding@schilljs.com>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\WorkflowEngine\Check;
use OCP\IL10N;
use OCP\IRequest;
class RequestURL extends AbstractStringCheck {
public const CLI = 'cli';
/** @var ?string */
protected $url;
/** @var IRequest */
protected $request;
/**
* @param IL10N $l
* @param IRequest $request
*/
public function __construct(IL10N $l, IRequest $request) {
parent::__construct($l);
$this->request = $request;
}
/**
* @param string $operator
* @param string $value
* @return bool
*/
public function executeCheck($operator, $value) {
if (\OC::$CLI) {
$actualValue = $this->url = RequestURL::CLI;
} else {
$actualValue = $this->getActualValue();
}
if (in_array($operator, ['is', '!is'])) {
switch ($value) {
case 'webdav':
if ($operator === 'is') {
return $this->isWebDAVRequest();
} else {
return !$this->isWebDAVRequest();
}
}
}
return $this->executeStringCheck($operator, $value, $actualValue);
}
/**
* @return string
*/
protected function getActualValue() {
if ($this->url !== null) {
return $this->url;
}
$this->url = $this->request->getServerProtocol() . '://';// E.g. http(s) + ://
$this->url .= $this->request->getServerHost();// E.g. localhost
$this->url .= $this->request->getScriptName();// E.g. /nextcloud/index.php
$this->url .= $this->request->getPathInfo();// E.g. /apps/files_texteditor/ajax/loadfile
return $this->url; // E.g. https://localhost/nextcloud/index.php/apps/files_texteditor/ajax/loadfile
}
protected function isWebDAVRequest(): bool {
if ($this->url === RequestURL::CLI) {
return false;
}
return substr($this->request->getScriptName(), 0 - strlen('/remote.php')) === '/remote.php' && (
$this->request->getPathInfo() === '/webdav' ||
str_starts_with($this->request->getPathInfo() ?? '', '/webdav/') ||
$this->request->getPathInfo() === '/dav/files' ||
str_starts_with($this->request->getPathInfo() ?? '', '/dav/files/')
);
}
}
@@ -0,0 +1,88 @@
<?php
/**
* @copyright Copyright (c) 2016 Joas Schilling <coding@schilljs.com>
*
* @author Arthur Schiwon <blizzz@arthur-schiwon.de>
* @author Joas Schilling <coding@schilljs.com>
* @author Morris Jobke <hey@morrisjobke.de>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\WorkflowEngine\Check;
use OCP\IL10N;
use OCP\IRequest;
class RequestUserAgent extends AbstractStringCheck {
/** @var IRequest */
protected $request;
/**
* @param IL10N $l
* @param IRequest $request
*/
public function __construct(IL10N $l, IRequest $request) {
parent::__construct($l);
$this->request = $request;
}
/**
* @param string $operator
* @param string $value
* @return bool
*/
public function executeCheck($operator, $value) {
$actualValue = $this->getActualValue();
if (in_array($operator, ['is', '!is'], true)) {
switch ($value) {
case 'android':
$operator = $operator === 'is' ? 'matches' : '!matches';
$value = IRequest::USER_AGENT_CLIENT_ANDROID;
break;
case 'ios':
$operator = $operator === 'is' ? 'matches' : '!matches';
$value = IRequest::USER_AGENT_CLIENT_IOS;
break;
case 'desktop':
$operator = $operator === 'is' ? 'matches' : '!matches';
$value = IRequest::USER_AGENT_CLIENT_DESKTOP;
break;
case 'mail':
if ($operator === 'is') {
return $this->executeStringCheck('matches', IRequest::USER_AGENT_OUTLOOK_ADDON, $actualValue)
|| $this->executeStringCheck('matches', IRequest::USER_AGENT_THUNDERBIRD_ADDON, $actualValue);
}
return $this->executeStringCheck('!matches', IRequest::USER_AGENT_OUTLOOK_ADDON, $actualValue)
&& $this->executeStringCheck('!matches', IRequest::USER_AGENT_THUNDERBIRD_ADDON, $actualValue);
}
}
return $this->executeStringCheck($operator, $value, $actualValue);
}
/**
* @return string
*/
protected function getActualValue() {
return $this->request->getHeader('User-Agent');
}
public function isAvailableForScope(int $scope): bool {
return true;
}
}
@@ -0,0 +1,72 @@
<?php
declare(strict_types=1);
/**
* @copyright Copyright (c) 2019 Arthur Schiwon <blizzz@arthur-schiwon.de>
*
* @author Arthur Schiwon <blizzz@arthur-schiwon.de>
* @author Joas Schilling <coding@schilljs.com>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\WorkflowEngine\Check;
use OCA\WorkflowEngine\AppInfo\Application;
use OCA\WorkflowEngine\Entity\File;
use OCP\Files\Node;
use OCP\Files\Storage\IStorage;
use OCP\WorkflowEngine\IEntity;
trait TFileCheck {
/** @var IStorage */
protected $storage;
/** @var string */
protected $path;
/** @var bool */
protected $isDir;
/**
* @param IStorage $storage
* @param string $path
* @param bool $isDir
* @since 18.0.0
*/
public function setFileInfo(IStorage $storage, string $path, bool $isDir = false): void {
$this->storage = $storage;
$this->path = $path;
$this->isDir = $isDir;
}
/**
* @throws \OCP\Files\NotFoundException
*/
public function setEntitySubject(IEntity $entity, $subject): void {
if ($entity instanceof File) {
if (!$subject instanceof Node) {
throw new \UnexpectedValueException(
'Expected Node subject for File entity, got {class}',
['app' => Application::APP_ID, 'class' => get_class($subject)]
);
}
$this->storage = $subject->getStorage();
$this->path = $subject->getPath();
}
}
}
@@ -0,0 +1,118 @@
<?php
/**
* @copyright Copyright (c) 2016 Morris Jobke <hey@morrisjobke.de>
*
* @author Joas Schilling <coding@schilljs.com>
* @author Julius Härtl <jus@bitgrid.net>
* @author Morris Jobke <hey@morrisjobke.de>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\WorkflowEngine\Check;
use OCP\IGroupManager;
use OCP\IL10N;
use OCP\IUser;
use OCP\IUserSession;
use OCP\WorkflowEngine\ICheck;
use OCP\WorkflowEngine\IManager;
class UserGroupMembership implements ICheck {
/** @var string */
protected $cachedUser;
/** @var string[] */
protected $cachedGroupMemberships;
/** @var IUserSession */
protected $userSession;
/** @var IGroupManager */
protected $groupManager;
/** @var IL10N */
protected $l;
/**
* @param IUserSession $userSession
* @param IGroupManager $groupManager
* @param IL10N $l
*/
public function __construct(IUserSession $userSession, IGroupManager $groupManager, IL10N $l) {
$this->userSession = $userSession;
$this->groupManager = $groupManager;
$this->l = $l;
}
/**
* @param string $operator
* @param string $value
* @return bool
*/
public function executeCheck($operator, $value) {
$user = $this->userSession->getUser();
if ($user instanceof IUser) {
$groupIds = $this->getUserGroups($user);
return ($operator === 'is') === in_array($value, $groupIds);
} else {
return $operator !== 'is';
}
}
/**
* @param string $operator
* @param string $value
* @throws \UnexpectedValueException
*/
public function validateCheck($operator, $value) {
if (!in_array($operator, ['is', '!is'])) {
throw new \UnexpectedValueException($this->l->t('The given operator is invalid'), 1);
}
if (!$this->groupManager->groupExists($value)) {
throw new \UnexpectedValueException($this->l->t('The given group does not exist'), 2);
}
}
/**
* @param IUser $user
* @return string[]
*/
protected function getUserGroups(IUser $user) {
$uid = $user->getUID();
if ($this->cachedUser !== $uid) {
$this->cachedUser = $uid;
$this->cachedGroupMemberships = $this->groupManager->getUserGroupIds($user);
}
return $this->cachedGroupMemberships;
}
public function supportedEntities(): array {
// universal by default
return [];
}
public function isAvailableForScope(int $scope): bool {
// admin only by default
return $scope === IManager::SCOPE_ADMIN;
}
}