Merge pull request #719 from nextcloud/lockdown
Allow restricting of app password permissions
This commit is contained in:
commit
61453f5fd5
28 changed files with 1337 additions and 42 deletions
|
@ -159,6 +159,7 @@ class Auth extends AbstractBasic {
|
|||
} catch (Exception $e) {
|
||||
$class = get_class($e);
|
||||
$msg = $e->getMessage();
|
||||
\OC::$server->getLogger()->logException($e);
|
||||
throw new ServiceUnavailable("$class: $msg");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1152,6 +1152,13 @@
|
|||
<length>4</length>
|
||||
</field>
|
||||
|
||||
<field>
|
||||
<name>scope</name>
|
||||
<type>clob</type>
|
||||
<default></default>
|
||||
<notnull>false</notnull>
|
||||
</field>
|
||||
|
||||
<index>
|
||||
<name>authtoken_token_index</name>
|
||||
<unique>true</unique>
|
||||
|
|
|
@ -189,6 +189,7 @@ return array(
|
|||
'OCP\\LDAP\\ILDAPProviderFactory' => $baseDir . '/lib/public/LDAP/ILDAPProviderFactory.php',
|
||||
'OCP\\Lock\\ILockingProvider' => $baseDir . '/lib/public/Lock/ILockingProvider.php',
|
||||
'OCP\\Lock\\LockedException' => $baseDir . '/lib/public/Lock/LockedException.php',
|
||||
'OCP\\Lockdown\\ILockdownManager' => $baseDir . '/lib/public/Lockdown/ILockdownManager.php',
|
||||
'OCP\\Mail\\IMailer' => $baseDir . '/lib/public/Mail/IMailer.php',
|
||||
'OCP\\Migration\\IOutput' => $baseDir . '/lib/public/Migration/IOutput.php',
|
||||
'OCP\\Migration\\IRepairStep' => $baseDir . '/lib/public/Migration/IRepairStep.php',
|
||||
|
@ -580,6 +581,9 @@ return array(
|
|||
'OC\\Lock\\DBLockingProvider' => $baseDir . '/lib/private/Lock/DBLockingProvider.php',
|
||||
'OC\\Lock\\MemcacheLockingProvider' => $baseDir . '/lib/private/Lock/MemcacheLockingProvider.php',
|
||||
'OC\\Lock\\NoopLockingProvider' => $baseDir . '/lib/private/Lock/NoopLockingProvider.php',
|
||||
'OC\\Lockdown\\Filesystem\\NullCache' => $baseDir . '/lib/private/Lockdown/Filesystem/NullCache.php',
|
||||
'OC\\Lockdown\\Filesystem\\NullStorage' => $baseDir . '/lib/private/Lockdown/Filesystem/NullStorage.php',
|
||||
'OC\\Lockdown\\LockdownManager' => $baseDir . '/lib/private/Lockdown/LockdownManager.php',
|
||||
'OC\\Log' => $baseDir . '/lib/private/Log.php',
|
||||
'OC\\Log\\ErrorHandler' => $baseDir . '/lib/private/Log/ErrorHandler.php',
|
||||
'OC\\Log\\Errorlog' => $baseDir . '/lib/private/Log/Errorlog.php',
|
||||
|
|
|
@ -219,6 +219,7 @@ class ComposerStaticInit53792487c5a8370acc0b06b1a864ff4c
|
|||
'OCP\\LDAP\\ILDAPProviderFactory' => __DIR__ . '/../../..' . '/lib/public/LDAP/ILDAPProviderFactory.php',
|
||||
'OCP\\Lock\\ILockingProvider' => __DIR__ . '/../../..' . '/lib/public/Lock/ILockingProvider.php',
|
||||
'OCP\\Lock\\LockedException' => __DIR__ . '/../../..' . '/lib/public/Lock/LockedException.php',
|
||||
'OCP\\Lockdown\\ILockdownManager' => __DIR__ . '/../../..' . '/lib/public/Lockdown/ILockdownManager.php',
|
||||
'OCP\\Mail\\IMailer' => __DIR__ . '/../../..' . '/lib/public/Mail/IMailer.php',
|
||||
'OCP\\Migration\\IOutput' => __DIR__ . '/../../..' . '/lib/public/Migration/IOutput.php',
|
||||
'OCP\\Migration\\IRepairStep' => __DIR__ . '/../../..' . '/lib/public/Migration/IRepairStep.php',
|
||||
|
@ -610,6 +611,9 @@ class ComposerStaticInit53792487c5a8370acc0b06b1a864ff4c
|
|||
'OC\\Lock\\DBLockingProvider' => __DIR__ . '/../../..' . '/lib/private/Lock/DBLockingProvider.php',
|
||||
'OC\\Lock\\MemcacheLockingProvider' => __DIR__ . '/../../..' . '/lib/private/Lock/MemcacheLockingProvider.php',
|
||||
'OC\\Lock\\NoopLockingProvider' => __DIR__ . '/../../..' . '/lib/private/Lock/NoopLockingProvider.php',
|
||||
'OC\\Lockdown\\Filesystem\\NullCache' => __DIR__ . '/../../..' . '/lib/private/Lockdown/Filesystem/NullCache.php',
|
||||
'OC\\Lockdown\\Filesystem\\NullStorage' => __DIR__ . '/../../..' . '/lib/private/Lockdown/Filesystem/NullStorage.php',
|
||||
'OC\\Lockdown\\LockdownManager' => __DIR__ . '/../../..' . '/lib/private/Lockdown/LockdownManager.php',
|
||||
'OC\\Log' => __DIR__ . '/../../..' . '/lib/private/Log.php',
|
||||
'OC\\Log\\ErrorHandler' => __DIR__ . '/../../..' . '/lib/private/Log/ErrorHandler.php',
|
||||
'OC\\Log\\Errorlog' => __DIR__ . '/../../..' . '/lib/private/Log/Errorlog.php',
|
||||
|
|
|
@ -87,6 +87,17 @@ class DefaultToken extends Entity implements IToken {
|
|||
*/
|
||||
protected $lastCheck;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $scope;
|
||||
|
||||
public function __construct() {
|
||||
$this->addType('type', 'int');
|
||||
$this->addType('lastActivity', 'int');
|
||||
$this->addType('lastCheck', 'int');
|
||||
}
|
||||
|
||||
public function getId() {
|
||||
return $this->id;
|
||||
}
|
||||
|
@ -119,6 +130,7 @@ class DefaultToken extends Entity implements IToken {
|
|||
'name' => $this->name,
|
||||
'lastActivity' => $this->lastActivity,
|
||||
'type' => $this->type,
|
||||
'scope' => $this->getScopeAsArray()
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -140,4 +152,25 @@ class DefaultToken extends Entity implements IToken {
|
|||
return parent::setLastCheck($time);
|
||||
}
|
||||
|
||||
public function getScope() {
|
||||
return parent::getScope();
|
||||
}
|
||||
|
||||
public function getScopeAsArray() {
|
||||
$scope = json_decode($this->getScope(), true);
|
||||
if (!$scope) {
|
||||
return [
|
||||
'filesystem'=> true
|
||||
];
|
||||
}
|
||||
return $scope;
|
||||
}
|
||||
|
||||
public function setScope($scope) {
|
||||
if (is_array($scope)) {
|
||||
parent::setScope(json_encode($scope));
|
||||
} else {
|
||||
parent::setScope((string)$scope);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -72,10 +72,9 @@ class DefaultTokenMapper extends Mapper {
|
|||
public function getToken($token) {
|
||||
/* @var $qb IQueryBuilder */
|
||||
$qb = $this->db->getQueryBuilder();
|
||||
$result = $qb->select('id', 'uid', 'login_name', 'password', 'name', 'type', 'remember', 'token', 'last_activity', 'last_check')
|
||||
$result = $qb->select('id', 'uid', 'login_name', 'password', 'name', 'type', 'remember', 'token', 'last_activity', 'last_check', 'scope')
|
||||
->from('authtoken')
|
||||
->where($qb->expr()->eq('token', $qb->createParameter('token')))
|
||||
->setParameter('token', $token)
|
||||
->where($qb->expr()->eq('token', $qb->createNamedParameter($token)))
|
||||
->execute();
|
||||
|
||||
$data = $result->fetch();
|
||||
|
@ -83,6 +82,30 @@ class DefaultTokenMapper extends Mapper {
|
|||
if ($data === false) {
|
||||
throw new DoesNotExistException('token does not exist');
|
||||
}
|
||||
;
|
||||
return DefaultToken::fromRow($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the token for $id
|
||||
*
|
||||
* @param string $id
|
||||
* @throws DoesNotExistException
|
||||
* @return DefaultToken
|
||||
*/
|
||||
public function getTokenById($id) {
|
||||
/* @var $qb IQueryBuilder */
|
||||
$qb = $this->db->getQueryBuilder();
|
||||
$result = $qb->select('id', 'uid', 'login_name', 'password', 'name', 'type', 'token', 'last_activity', 'last_check', 'scope')
|
||||
->from('authtoken')
|
||||
->where($qb->expr()->eq('id', $qb->createNamedParameter($id)))
|
||||
->execute();
|
||||
|
||||
$data = $result->fetch();
|
||||
$result->closeCursor();
|
||||
if ($data === false) {
|
||||
throw new DoesNotExistException('token does not exist');
|
||||
};
|
||||
return DefaultToken::fromRow($data);
|
||||
}
|
||||
|
||||
|
@ -98,7 +121,7 @@ class DefaultTokenMapper extends Mapper {
|
|||
public function getTokenByUser(IUser $user) {
|
||||
/* @var $qb IQueryBuilder */
|
||||
$qb = $this->db->getQueryBuilder();
|
||||
$qb->select('id', 'uid', 'login_name', 'password', 'name', 'type', 'remember', 'token', 'last_activity', 'last_check')
|
||||
$qb->select('id', 'uid', 'login_name', 'password', 'name', 'type', 'remember', 'token', 'last_activity', 'last_check', 'scope')
|
||||
->from('authtoken')
|
||||
->where($qb->expr()->eq('uid', $qb->createNamedParameter($user->getUID())))
|
||||
->setMaxResults(1000);
|
||||
|
|
|
@ -145,7 +145,7 @@ class DefaultTokenProvider implements IProvider {
|
|||
}
|
||||
|
||||
/**
|
||||
* Get a token by token id
|
||||
* Get a token by token
|
||||
*
|
||||
* @param string $tokenId
|
||||
* @throws InvalidTokenException
|
||||
|
@ -159,6 +159,21 @@ class DefaultTokenProvider implements IProvider {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a token by token id
|
||||
*
|
||||
* @param string $tokenId
|
||||
* @throws InvalidTokenException
|
||||
* @return DefaultToken
|
||||
*/
|
||||
public function getTokenById($tokenId) {
|
||||
try {
|
||||
return $this->mapper->getTokenById($tokenId);
|
||||
} catch (DoesNotExistException $ex) {
|
||||
throw new InvalidTokenException();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $oldSessionId
|
||||
* @param string $sessionId
|
||||
|
|
|
@ -50,7 +50,16 @@ interface IProvider {
|
|||
* @throws InvalidTokenException
|
||||
* @return IToken
|
||||
*/
|
||||
public function getToken($tokenId) ;
|
||||
public function getToken($tokenId);
|
||||
|
||||
/**
|
||||
* Get a token by token id
|
||||
*
|
||||
* @param string $tokenId
|
||||
* @throws InvalidTokenException
|
||||
* @return DefaultToken
|
||||
*/
|
||||
public function getTokenById($tokenId);
|
||||
|
||||
/**
|
||||
* Duplicate an existing session token
|
||||
|
|
|
@ -67,9 +67,30 @@ interface IToken extends JsonSerializable {
|
|||
public function getLastCheck();
|
||||
|
||||
/**
|
||||
* Get the timestamp of the last password check
|
||||
* Set the timestamp of the last password check
|
||||
*
|
||||
* @param int $time
|
||||
*/
|
||||
public function setLastCheck($time);
|
||||
|
||||
/**
|
||||
* Get the authentication scope for this token
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getScope();
|
||||
|
||||
/**
|
||||
* Get the authentication scope for this token
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getScopeAsArray();
|
||||
|
||||
/**
|
||||
* Set the authentication scope for this token
|
||||
*
|
||||
* @param array $scope
|
||||
*/
|
||||
public function setScope($scope);
|
||||
}
|
||||
|
|
|
@ -62,6 +62,7 @@ use OC\Cache\CappedMemoryCache;
|
|||
use OC\Files\Config\MountProviderCollection;
|
||||
use OC\Files\Mount\MountPoint;
|
||||
use OC\Files\Storage\StorageFactory;
|
||||
use OC\Lockdown\Filesystem\NullStorage;
|
||||
use OCP\Files\Config\IMountProvider;
|
||||
use OCP\Files\Mount\IMountPoint;
|
||||
use OCP\Files\NotFoundException;
|
||||
|
@ -216,7 +217,7 @@ class Filesystem {
|
|||
* @internal
|
||||
*/
|
||||
public static function logWarningWhenAddingStorageWrapper($shouldLog) {
|
||||
self::$logWarningWhenAddingStorageWrapper = (bool) $shouldLog;
|
||||
self::$logWarningWhenAddingStorageWrapper = (bool)$shouldLog;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -426,25 +427,36 @@ class Filesystem {
|
|||
self::$usersSetup[$user] = true;
|
||||
}
|
||||
|
||||
/** @var \OC\Files\Config\MountProviderCollection $mountConfigManager */
|
||||
$mountConfigManager = \OC::$server->getMountProviderCollection();
|
||||
if (\OC::$server->getLockdownManager()->canAccessFilesystem()) {
|
||||
/** @var \OC\Files\Config\MountProviderCollection $mountConfigManager */
|
||||
$mountConfigManager = \OC::$server->getMountProviderCollection();
|
||||
|
||||
// home mounts are handled seperate since we need to ensure this is mounted before we call the other mount providers
|
||||
$homeMount = $mountConfigManager->getHomeMountForUser($userObject);
|
||||
// home mounts are handled seperate since we need to ensure this is mounted before we call the other mount providers
|
||||
$homeMount = $mountConfigManager->getHomeMountForUser($userObject);
|
||||
|
||||
self::getMountManager()->addMount($homeMount);
|
||||
self::getMountManager()->addMount($homeMount);
|
||||
|
||||
\OC\Files\Filesystem::getStorage($user);
|
||||
\OC\Files\Filesystem::getStorage($user);
|
||||
|
||||
// Chance to mount for other storages
|
||||
if ($userObject) {
|
||||
$mounts = $mountConfigManager->getMountsForUser($userObject);
|
||||
array_walk($mounts, array(self::$mounts, 'addMount'));
|
||||
$mounts[] = $homeMount;
|
||||
$mountConfigManager->registerMounts($userObject, $mounts);
|
||||
// Chance to mount for other storages
|
||||
if ($userObject) {
|
||||
$mounts = $mountConfigManager->getMountsForUser($userObject);
|
||||
array_walk($mounts, array(self::$mounts, 'addMount'));
|
||||
$mounts[] = $homeMount;
|
||||
$mountConfigManager->registerMounts($userObject, $mounts);
|
||||
}
|
||||
|
||||
self::listenForNewMountProviders($mountConfigManager, $userManager);
|
||||
} else {
|
||||
self::$mounts->addMount(new MountPoint(
|
||||
new NullStorage([]),
|
||||
'/' . $user
|
||||
));
|
||||
self::$mounts->addMount(new MountPoint(
|
||||
new NullStorage([]),
|
||||
'/' . $user . '/files'
|
||||
));
|
||||
}
|
||||
|
||||
self::listenForNewMountProviders($mountConfigManager, $userManager);
|
||||
\OC_Hook::emit('OC_Filesystem', 'post_initMountPoints', array('user' => $user));
|
||||
}
|
||||
|
||||
|
|
122
lib/private/Lockdown/Filesystem/NullCache.php
Normal file
122
lib/private/Lockdown/Filesystem/NullCache.php
Normal file
|
@ -0,0 +1,122 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @copyright Copyright (c) 2016, Robin Appelman <robin@icewind.nl>
|
||||
*
|
||||
* This code is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License, version 3,
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* 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, version 3,
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
*
|
||||
*/
|
||||
|
||||
namespace OC\Lockdown\Filesystem;
|
||||
|
||||
use OC\Files\Cache\CacheEntry;
|
||||
use OCP\Constants;
|
||||
use OCP\Files\Cache\ICache;
|
||||
use OCP\Files\Cache\ICacheEntry;
|
||||
use OCP\Files\FileInfo;
|
||||
|
||||
class NullCache implements ICache {
|
||||
public function getNumericStorageId() {
|
||||
return -1;
|
||||
}
|
||||
|
||||
public function get($file) {
|
||||
return $file !== '' ? null :
|
||||
new CacheEntry([
|
||||
'fileid' => -1,
|
||||
'parent' => -1,
|
||||
'name' => '',
|
||||
'path' => '',
|
||||
'size' => '0',
|
||||
'mtime' => time(),
|
||||
'storage_mtime' => time(),
|
||||
'etag' => '',
|
||||
'mimetype' => FileInfo::MIMETYPE_FOLDER,
|
||||
'mimepart' => 'httpd',
|
||||
'permissions' => Constants::PERMISSION_READ
|
||||
]);
|
||||
}
|
||||
|
||||
public function getFolderContents($folder) {
|
||||
return [];
|
||||
}
|
||||
|
||||
public function getFolderContentsById($fileId) {
|
||||
return [];
|
||||
}
|
||||
|
||||
public function put($file, array $data) {
|
||||
throw new \OC\ForbiddenException('This request is not allowed to access the filesystem');
|
||||
}
|
||||
|
||||
public function insert($file, array $data) {
|
||||
throw new \OC\ForbiddenException('This request is not allowed to access the filesystem');
|
||||
}
|
||||
|
||||
public function update($id, array $data) {
|
||||
throw new \OC\ForbiddenException('This request is not allowed to access the filesystem');
|
||||
}
|
||||
|
||||
public function getId($file) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
public function getParentId($file) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
public function inCache($file) {
|
||||
return $file === '';
|
||||
}
|
||||
|
||||
public function remove($file) {
|
||||
throw new \OC\ForbiddenException('This request is not allowed to access the filesystem');
|
||||
}
|
||||
|
||||
public function move($source, $target) {
|
||||
throw new \OC\ForbiddenException('This request is not allowed to access the filesystem');
|
||||
}
|
||||
|
||||
public function moveFromCache(ICache $sourceCache, $sourcePath, $targetPath) {
|
||||
throw new \OC\ForbiddenException('This request is not allowed to access the filesystem');
|
||||
}
|
||||
|
||||
public function getStatus($file) {
|
||||
return ICache::COMPLETE;
|
||||
}
|
||||
|
||||
public function search($pattern) {
|
||||
return [];
|
||||
}
|
||||
|
||||
public function searchByMime($mimetype) {
|
||||
return [];
|
||||
}
|
||||
|
||||
public function searchByTag($tag, $userId) {
|
||||
return [];
|
||||
}
|
||||
|
||||
public function getIncomplete() {
|
||||
return [];
|
||||
}
|
||||
|
||||
public function getPathById($id) {
|
||||
return '';
|
||||
}
|
||||
|
||||
public function normalize($path) {
|
||||
return $path;
|
||||
}
|
||||
|
||||
}
|
177
lib/private/Lockdown/Filesystem/NullStorage.php
Normal file
177
lib/private/Lockdown/Filesystem/NullStorage.php
Normal file
|
@ -0,0 +1,177 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @copyright Copyright (c) 2016, Robin Appelman <robin@icewind.nl>
|
||||
*
|
||||
* This code is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License, version 3,
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* 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, version 3,
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
*
|
||||
*/
|
||||
|
||||
namespace OC\Lockdown\Filesystem;
|
||||
|
||||
use Icewind\Streams\IteratorDirectory;
|
||||
use OC\Files\Storage\Common;
|
||||
|
||||
class NullStorage extends Common {
|
||||
public function __construct($parameters) {
|
||||
parent::__construct($parameters);
|
||||
}
|
||||
|
||||
public function getId() {
|
||||
return 'null';
|
||||
}
|
||||
|
||||
public function mkdir($path) {
|
||||
throw new \OC\ForbiddenException('This request is not allowed to access the filesystem');
|
||||
}
|
||||
|
||||
public function rmdir($path) {
|
||||
throw new \OC\ForbiddenException('This request is not allowed to access the filesystem');
|
||||
}
|
||||
|
||||
public function opendir($path) {
|
||||
return new IteratorDirectory([]);
|
||||
}
|
||||
|
||||
public function is_dir($path) {
|
||||
return $path === '';
|
||||
}
|
||||
|
||||
public function is_file($path) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public function stat($path) {
|
||||
throw new \OC\ForbiddenException('This request is not allowed to access the filesystem');
|
||||
}
|
||||
|
||||
public function filetype($path) {
|
||||
return ($path === '') ? 'dir' : false;
|
||||
}
|
||||
|
||||
public function filesize($path) {
|
||||
throw new \OC\ForbiddenException('This request is not allowed to access the filesystem');
|
||||
}
|
||||
|
||||
public function isCreatable($path) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public function isReadable($path) {
|
||||
return $path === '';
|
||||
}
|
||||
|
||||
public function isUpdatable($path) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public function isDeletable($path) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public function isSharable($path) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public function getPermissions($path) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public function file_exists($path) {
|
||||
return $path === '';
|
||||
}
|
||||
|
||||
public function filemtime($path) {
|
||||
return ($path === '') ? time() : false;
|
||||
}
|
||||
|
||||
public function file_get_contents($path) {
|
||||
throw new \OC\ForbiddenException('This request is not allowed to access the filesystem');
|
||||
}
|
||||
|
||||
public function file_put_contents($path, $data) {
|
||||
throw new \OC\ForbiddenException('This request is not allowed to access the filesystem');
|
||||
}
|
||||
|
||||
public function unlink($path) {
|
||||
throw new \OC\ForbiddenException('This request is not allowed to access the filesystem');
|
||||
}
|
||||
|
||||
public function rename($path1, $path2) {
|
||||
throw new \OC\ForbiddenException('This request is not allowed to access the filesystem');
|
||||
}
|
||||
|
||||
public function copy($path1, $path2) {
|
||||
throw new \OC\ForbiddenException('This request is not allowed to access the filesystem');
|
||||
}
|
||||
|
||||
public function fopen($path, $mode) {
|
||||
throw new \OC\ForbiddenException('This request is not allowed to access the filesystem');
|
||||
}
|
||||
|
||||
public function getMimeType($path) {
|
||||
throw new \OC\ForbiddenException('This request is not allowed to access the filesystem');
|
||||
}
|
||||
|
||||
public function hash($type, $path, $raw = false) {
|
||||
throw new \OC\ForbiddenException('This request is not allowed to access the filesystem');
|
||||
}
|
||||
|
||||
public function free_space($path) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public function touch($path, $mtime = null) {
|
||||
throw new \OC\ForbiddenException('This request is not allowed to access the filesystem');
|
||||
}
|
||||
|
||||
public function getLocalFile($path) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public function hasUpdated($path, $time) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public function getETag($path) {
|
||||
return '';
|
||||
}
|
||||
|
||||
public function isLocal() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public function getDirectDownload($path) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public function copyFromStorage(\OCP\Files\Storage $sourceStorage, $sourceInternalPath, $targetInternalPath) {
|
||||
throw new \OC\ForbiddenException('This request is not allowed to access the filesystem');
|
||||
}
|
||||
|
||||
public function moveFromStorage(\OCP\Files\Storage $sourceStorage, $sourceInternalPath, $targetInternalPath) {
|
||||
throw new \OC\ForbiddenException('This request is not allowed to access the filesystem');
|
||||
}
|
||||
|
||||
public function test() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public function getOwner($path) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public function getCache($path = '', $storage = null) {
|
||||
return new NullCache();
|
||||
}
|
||||
}
|
46
lib/private/Lockdown/LockdownManager.php
Normal file
46
lib/private/Lockdown/LockdownManager.php
Normal file
|
@ -0,0 +1,46 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @copyright Copyright (c) 2016, Robin Appelman <robin@icewind.nl>
|
||||
*
|
||||
* This code is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License, version 3,
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* 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, version 3,
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
*
|
||||
*/
|
||||
|
||||
namespace OC\Lockdown;
|
||||
|
||||
use OC\Authentication\Token\IToken;
|
||||
use OCP\Lockdown\ILockdownManager;
|
||||
|
||||
class LockdownManager implements ILockdownManager {
|
||||
private $enabled = false;
|
||||
|
||||
/** @var array|null */
|
||||
private $scope;
|
||||
|
||||
public function enable() {
|
||||
$this->enabled = true;
|
||||
}
|
||||
|
||||
public function setToken(IToken $token) {
|
||||
$this->scope = $token->getScopeAsArray();
|
||||
$this->enable();
|
||||
}
|
||||
|
||||
public function canAccessFilesystem() {
|
||||
if (!$this->enabled) {
|
||||
return true;
|
||||
}
|
||||
return !$this->scope || $this->scope['filesystem'];
|
||||
}
|
||||
}
|
|
@ -69,6 +69,7 @@ use OC\IntegrityCheck\Helpers\FileAccessHelper;
|
|||
use OC\Lock\DBLockingProvider;
|
||||
use OC\Lock\MemcacheLockingProvider;
|
||||
use OC\Lock\NoopLockingProvider;
|
||||
use OC\Lockdown\LockdownManager;
|
||||
use OC\Mail\Mailer;
|
||||
use OC\Memcache\ArrayCache;
|
||||
use OC\Notification\Manager;
|
||||
|
@ -795,6 +796,9 @@ class Server extends ServerContainer implements IServerContainer {
|
|||
$c->getSystemConfig()
|
||||
);
|
||||
});
|
||||
$this->registerService('LockdownManager', function (Server $c) {
|
||||
return new LockdownManager();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1534,4 +1538,11 @@ class Server extends ServerContainer implements IServerContainer {
|
|||
$factory = $this->query(\OC\Files\AppData\Factory::class);
|
||||
return $factory->get($app);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \OCP\Lockdown\ILockdownManager
|
||||
*/
|
||||
public function getLockdownManager() {
|
||||
return $this->query('LockdownManager');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -525,6 +525,7 @@ class Session implements IUserSession, Emitter {
|
|||
//login
|
||||
$this->setUser($user);
|
||||
$this->setLoginName($dbToken->getLoginName());
|
||||
\OC::$server->getLockdownManager()->setToken($dbToken);
|
||||
$this->manager->emit('\OC\User', 'postLogin', array($user, $password));
|
||||
|
||||
if ($this->isLoggedIn()) {
|
||||
|
|
50
lib/public/Lockdown/ILockdownManager.php
Normal file
50
lib/public/Lockdown/ILockdownManager.php
Normal file
|
@ -0,0 +1,50 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @copyright Copyright (c) 2016, Robin Appelman <robin@icewind.nl>
|
||||
*
|
||||
* This code is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License, version 3,
|
||||
* as published by the Free Software Foundation.
|
||||
*
|
||||
* 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, version 3,
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
*
|
||||
*/
|
||||
|
||||
namespace OCP\Lockdown;
|
||||
|
||||
use OC\Authentication\Token\IToken;
|
||||
|
||||
/**
|
||||
* @since 9.2
|
||||
*/
|
||||
interface ILockdownManager {
|
||||
/**
|
||||
* Enable the lockdown restrictions
|
||||
*
|
||||
* @since 9.2
|
||||
*/
|
||||
public function enable();
|
||||
|
||||
/**
|
||||
* Set the active token to get the restrictions from and enable the lockdown
|
||||
*
|
||||
* @param IToken $token
|
||||
* @since 9.2
|
||||
*/
|
||||
public function setToken(IToken $token);
|
||||
|
||||
/**
|
||||
* Check whether or not filesystem access is allowed
|
||||
*
|
||||
* @return bool
|
||||
* @since 9.2
|
||||
*/
|
||||
public function canAccessFilesystem();
|
||||
}
|
|
@ -135,11 +135,13 @@ class AuthSettingsController extends Controller {
|
|||
|
||||
$token = $this->generateRandomDeviceToken();
|
||||
$deviceToken = $this->tokenProvider->generateToken($token, $this->uid, $loginName, $password, $name, IToken::PERMANENT_TOKEN);
|
||||
$tokenData = $deviceToken->jsonSerialize();
|
||||
$tokenData['canDelete'] = true;
|
||||
|
||||
return [
|
||||
'token' => $token,
|
||||
'loginName' => $loginName,
|
||||
'deviceToken' => $deviceToken
|
||||
'deviceToken' => $tokenData
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -180,4 +182,20 @@ class AuthSettingsController extends Controller {
|
|||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @NoAdminRequired
|
||||
* @NoSubadminRequired
|
||||
*
|
||||
* @param int $id
|
||||
* @param array $scope
|
||||
*/
|
||||
public function update($id, array $scope) {
|
||||
$token = $this->tokenProvider->getTokenById($id);
|
||||
$token->setScope([
|
||||
'filesystem' => $scope['filesystem'],
|
||||
'app' => array_values($scope['apps'])
|
||||
]);
|
||||
$this->tokenProvider->updateToken($token);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -149,6 +149,13 @@ table.nostyle td { padding: 0.2em 0; }
|
|||
padding: 10px 10px 10px 0;
|
||||
}
|
||||
|
||||
#sessions .token-list td.more,
|
||||
#apppasswords .token-list td.more {
|
||||
overflow: visible;
|
||||
position: relative;
|
||||
width: 16px;
|
||||
}
|
||||
|
||||
#sessions .token-list td,
|
||||
#apppasswords .token-list td {
|
||||
border-top: 1px solid #DDD;
|
||||
|
@ -156,18 +163,60 @@ table.nostyle td { padding: 0.2em 0; }
|
|||
max-width: 200px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
vertical-align: top;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
#sessions tr *:nth-child(2),
|
||||
#apppasswords tr *:nth-child(2) {
|
||||
#sessions tr>*:nth-child(2),
|
||||
#apppasswords tr>*:nth-child(2) {
|
||||
text-align: right;
|
||||
}
|
||||
#sessions .token-list td a.icon-delete,
|
||||
#apppasswords .token-list td a.icon-delete {
|
||||
#sessions .token-list td > a.icon,
|
||||
#apppasswords .token-list td > a.icon {
|
||||
opacity: 0;
|
||||
transition: opacity 0.5s;
|
||||
}
|
||||
|
||||
#sessions .token-list a.icon,
|
||||
#apppasswords .token-list a.icon {
|
||||
margin-top: 4px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
#sessions .token-list tr:hover td > a.icon,
|
||||
#apppasswords .token-list tr:hover td > a.icon,
|
||||
#sessions .token-list tr.active td > a.icon,
|
||||
#apppasswords .token-list tr.active td > a.icon{
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
#sessions .token-list td div.configure,
|
||||
#apppasswords .token-list td div.configure {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#sessions .token-list tr.active div.configure,
|
||||
#apppasswords .token-list tr.active div.configure {
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: 45px;
|
||||
right: -5px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
#sessions .token-list tr.active div.configure > *,
|
||||
#apppasswords .token-list tr.active div.configure > *{
|
||||
margin-top: 5px;
|
||||
margin-bottom: 5px;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
#sessions .token-list tr.active a.icon-delete,
|
||||
#apppasswords .token-list tr.active a.icon-delete {
|
||||
background-position: left;
|
||||
padding-left: 20px;
|
||||
}
|
||||
|
||||
#new-app-login-name,
|
||||
#new-app-password {
|
||||
width: 186px;
|
||||
|
|
|
@ -27,13 +27,22 @@
|
|||
|
||||
var TEMPLATE_TOKEN =
|
||||
'<tr data-id="{{id}}">'
|
||||
+ '<td class="has-tooltip" title="{{title}}"><span class="token-name">{{name}}</span></td>'
|
||||
+ '<td class="has-tooltip" title="{{title}}">'
|
||||
+ '<span class="token-name">{{name}}</span>'
|
||||
+ '</td>'
|
||||
+ '<td><span class="last-activity has-tooltip" title="{{lastActivityTime}}">{{lastActivity}}</span></td>'
|
||||
+ '{{#if canDelete}}'
|
||||
+ '<td><a class="icon-delete has-tooltip" title="' + t('core', 'Disconnect') + '"></a></td>'
|
||||
+ '{{else}}'
|
||||
+ '<td></td>'
|
||||
+ '<td class="more">'
|
||||
+ '{{#if showMore}}<a class="icon icon-more"/>{{/if}}'
|
||||
+ '<div class="popovermenu bubble open menu configure">'
|
||||
+ '{{#if canScope}}'
|
||||
+ '<input class="filesystem checkbox" type="checkbox" id="{{id}}_filesystem" {{#if scope.filesystem}}checked{{/if}}/>'
|
||||
+ '<label for="{{id}}_filesystem">' + t('core', 'Allow filesystem access') + '</label><br/>'
|
||||
+ '{{/if}}'
|
||||
+ '{{#if canDelete}}'
|
||||
+ '<a class="icon icon-delete has-tooltip" title="' + t('core', 'Disconnect') + '">' + t('core', 'Revoke') +'</a>'
|
||||
+ '{{/if}}'
|
||||
+ '</div>'
|
||||
+ '</td>'
|
||||
+ '<tr>';
|
||||
|
||||
var SubView = OC.Backbone.View.extend({
|
||||
|
@ -70,7 +79,7 @@
|
|||
|
||||
var list = this.$('.token-list');
|
||||
var tokens = this.collection.filter(function (token) {
|
||||
return parseInt(token.get('type'), 10) === _this.type;
|
||||
return token.get('type') === _this.type;
|
||||
});
|
||||
list.html('');
|
||||
|
||||
|
@ -78,7 +87,7 @@
|
|||
this._toggleHeader(tokens.length > 0);
|
||||
|
||||
tokens.forEach(function (token) {
|
||||
var viewData = this._formatViewData(token.toJSON());
|
||||
var viewData = this._formatViewData(token);
|
||||
var html = _this.template(viewData);
|
||||
var $html = $(html);
|
||||
$html.find('.has-tooltip').tooltip({container: 'body'});
|
||||
|
@ -94,10 +103,13 @@
|
|||
this.$('.hidden-when-empty').toggleClass('hidden', !show);
|
||||
},
|
||||
|
||||
_formatViewData: function (viewData) {
|
||||
_formatViewData: function (token) {
|
||||
var viewData = token.toJSON();
|
||||
var ts = viewData.lastActivity * 1000;
|
||||
viewData.lastActivity = OC.Util.relativeModifiedDate(ts);
|
||||
viewData.lastActivityTime = OC.Util.formatDate(ts, 'LLL');
|
||||
viewData.canScope = token.get('type') === 1;
|
||||
viewData.showMore = viewData.canScope || viewData.canDelete;
|
||||
|
||||
// preserve title for cases where we format it further
|
||||
viewData.title = viewData.name;
|
||||
|
@ -204,6 +216,8 @@
|
|||
|
||||
var $el = $(el);
|
||||
$el.on('click', 'a.icon-delete', _.bind(_this._onDeleteToken, _this));
|
||||
$el.on('click', '.icon-more', _.bind(_this._onConfigureToken, _this));
|
||||
$el.on('change', 'input.filesystem', _.bind(_this._onSetTokenScope, _this));
|
||||
});
|
||||
|
||||
this._form = $('#app-password-form');
|
||||
|
@ -325,6 +339,13 @@
|
|||
this._addAppPasswordBtn.toggleClass('icon-loading-small', state);
|
||||
},
|
||||
|
||||
_onConfigureToken: function (event) {
|
||||
var $target = $(event.target);
|
||||
var $row = $target.closest('tr');
|
||||
$row.toggleClass('active');
|
||||
var id = $row.data('id');
|
||||
},
|
||||
|
||||
_onDeleteToken: function (event) {
|
||||
var $target = $(event.target);
|
||||
var $row = $target.closest('tr');
|
||||
|
@ -353,6 +374,24 @@
|
|||
});
|
||||
},
|
||||
|
||||
_onSetTokenScope: function (event) {
|
||||
var $target = $(event.target);
|
||||
var $row = $target.closest('tr');
|
||||
var id = $row.data('id');
|
||||
|
||||
var token = this.collection.get(id);
|
||||
if (_.isUndefined(token)) {
|
||||
// Ignore event
|
||||
return;
|
||||
}
|
||||
|
||||
var scope = token.get('scope');
|
||||
scope.filesystem = $target.is(":checked");
|
||||
|
||||
token.set('scope', scope);
|
||||
token.save();
|
||||
},
|
||||
|
||||
_toggleFormResult: function (showForm) {
|
||||
if (showForm) {
|
||||
this._result.slideUp();
|
||||
|
|
|
@ -42,6 +42,7 @@ class AuthSettingsControllerTest extends TestCase {
|
|||
/** @var AuthSettingsController */
|
||||
private $controller;
|
||||
private $request;
|
||||
/** @var IProvider|\PHPUnit_Framework_MockObject_MockObject */
|
||||
private $tokenProvider;
|
||||
private $userManager;
|
||||
private $session;
|
||||
|
@ -94,17 +95,19 @@ class AuthSettingsControllerTest extends TestCase {
|
|||
[
|
||||
'id' => 100,
|
||||
'name' => null,
|
||||
'lastActivity' => null,
|
||||
'type' => null,
|
||||
'lastActivity' => 0,
|
||||
'type' => 0,
|
||||
'canDelete' => false,
|
||||
'current' => true,
|
||||
'scope' => ['filesystem' => true]
|
||||
],
|
||||
[
|
||||
'id' => 200,
|
||||
'name' => null,
|
||||
'lastActivity' => null,
|
||||
'type' => null,
|
||||
'lastActivity' => 0,
|
||||
'type' => 0,
|
||||
'canDelete' => true,
|
||||
'scope' => ['filesystem' => true]
|
||||
]
|
||||
], $this->controller->index());
|
||||
}
|
||||
|
@ -141,9 +144,13 @@ class AuthSettingsControllerTest extends TestCase {
|
|||
->with($newToken, $this->uid, 'User13', $password, $name, IToken::PERMANENT_TOKEN)
|
||||
->will($this->returnValue($deviceToken));
|
||||
|
||||
$deviceToken->expects($this->once())
|
||||
->method('jsonSerialize')
|
||||
->will($this->returnValue(['dummy' => 'dummy', 'canDelete' => true]));
|
||||
|
||||
$expected = [
|
||||
'token' => $newToken,
|
||||
'deviceToken' => $deviceToken,
|
||||
'deviceToken' => ['dummy' => 'dummy', 'canDelete' => true],
|
||||
'loginName' => 'User13',
|
||||
];
|
||||
$this->assertEquals($expected, $this->controller->create($name));
|
||||
|
@ -194,4 +201,26 @@ class AuthSettingsControllerTest extends TestCase {
|
|||
$this->assertEquals([], $this->controller->destroy($id));
|
||||
}
|
||||
|
||||
public function testUpdateToken() {
|
||||
$token = $this->createMock(DefaultToken::class);
|
||||
|
||||
$this->tokenProvider->expects($this->once())
|
||||
->method('getTokenById')
|
||||
->with($this->equalTo(42))
|
||||
->willReturn($token);
|
||||
|
||||
$token->expects($this->once())
|
||||
->method('setScope')
|
||||
->with($this->equalTo([
|
||||
'filesystem' => true,
|
||||
'app' => ['dav', 'myapp']
|
||||
]));
|
||||
|
||||
$this->tokenProvider->expects($this->once())
|
||||
->method('updateToken')
|
||||
->with($this->equalTo($token));
|
||||
|
||||
$this->assertSame([], $this->controller->update(42, ['filesystem' => true, 'apps' => ['dav', 'myapp']]));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -27,6 +27,7 @@ use OC\Authentication\Token\DefaultToken;
|
|||
use OC\Authentication\Token\DefaultTokenMapper;
|
||||
use OC\Authentication\Token\IToken;
|
||||
use OCP\DB\QueryBuilder\IQueryBuilder;
|
||||
use OCP\IDBConnection;
|
||||
use OCP\IUser;
|
||||
use Test\TestCase;
|
||||
|
||||
|
@ -40,6 +41,8 @@ class DefaultTokenMapperTest extends TestCase {
|
|||
|
||||
/** @var DefaultTokenMapper */
|
||||
private $mapper;
|
||||
|
||||
/** @var IDBConnection */
|
||||
private $dbConnection;
|
||||
private $time;
|
||||
|
||||
|
@ -122,7 +125,6 @@ class DefaultTokenMapperTest extends TestCase {
|
|||
}
|
||||
|
||||
public function testGetToken() {
|
||||
$token = '1504445f1524fc801035448a95681a9378ba2e83930c814546c56e5d6ebde221198792fd900c88ed5ead0555780dad1ebce3370d7e154941cd5de87eb419899b';
|
||||
$token = new DefaultToken();
|
||||
$token->setUid('user2');
|
||||
$token->setLoginName('User2');
|
||||
|
@ -151,6 +153,42 @@ class DefaultTokenMapperTest extends TestCase {
|
|||
$this->mapper->getToken($token);
|
||||
}
|
||||
|
||||
public function testGetTokenById() {
|
||||
$token = new DefaultToken();
|
||||
$token->setUid('user2');
|
||||
$token->setLoginName('User2');
|
||||
$token->setPassword('971a337057853344700bbeccf836519f|UwOQwyb34sJHtqPV|036d4890f8c21d17bbc7b88072d8ef049a5c832a38e97f3e3d5f9186e896c2593aee16883f617322fa242728d0236ff32d163caeb4bd45e14ca002c57a88665f');
|
||||
$token->setName('Firefox on Android');
|
||||
$token->setToken('1504445f1524fc801035448a95681a9378ba2e83930c814546c56e5d6ebde221198792fd900c88ed5ead0555780dad1ebce3370d7e154941cd5de87eb419899b');
|
||||
$token->setType(IToken::TEMPORARY_TOKEN);
|
||||
$token->setRemember(IToken::DO_NOT_REMEMBER);
|
||||
$token->setLastActivity($this->time - 60 * 60 * 24 * 3);
|
||||
$token->setLastCheck($this->time - 10);
|
||||
|
||||
$dbToken = $this->mapper->getToken($token->getToken());
|
||||
$token->setId($dbToken->getId()); // We don't know the ID
|
||||
$token->resetUpdatedFields();
|
||||
|
||||
$dbToken = $this->mapper->getTokenById($token->getId());
|
||||
$this->assertEquals($token, $dbToken);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \OCP\AppFramework\Db\DoesNotExistException
|
||||
*/
|
||||
public function testGetTokenByIdNotFound() {
|
||||
$this->mapper->getTokenById(-1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \OCP\AppFramework\Db\DoesNotExistException
|
||||
*/
|
||||
public function testGetInvalidTokenById() {
|
||||
$id = 42;
|
||||
|
||||
$this->mapper->getToken($id);
|
||||
}
|
||||
|
||||
public function testGetTokenByUser() {
|
||||
$user = $this->createMock(IUser::class);
|
||||
$user->expects($this->once())
|
||||
|
|
|
@ -22,9 +22,11 @@
|
|||
|
||||
namespace Test\Authentication\Token;
|
||||
|
||||
use OC\Authentication\Exceptions\InvalidTokenException;
|
||||
use OC\Authentication\Token\DefaultToken;
|
||||
use OC\Authentication\Token\DefaultTokenProvider;
|
||||
use OC\Authentication\Token\IToken;
|
||||
use OCP\AppFramework\Db\DoesNotExistException;
|
||||
use OCP\AppFramework\Db\Mapper;
|
||||
use OCP\AppFramework\Utility\ITimeFactory;
|
||||
use OCP\IConfig;
|
||||
|
@ -376,4 +378,25 @@ class DefaultTokenProviderTest extends TestCase {
|
|||
$this->tokenProvider->renewSessionToken('oldId', 'newId');
|
||||
}
|
||||
|
||||
public function testGetTokenById() {
|
||||
$token = $this->createMock(DefaultToken::class);
|
||||
|
||||
$this->mapper->expects($this->once())
|
||||
->method('getTokenById')
|
||||
->with($this->equalTo(42))
|
||||
->willReturn($token);
|
||||
|
||||
$this->assertSame($token, $this->tokenProvider->getTokenById(42));
|
||||
}
|
||||
|
||||
public function testGetInvalidTokenById() {
|
||||
$this->expectException(InvalidTokenException::class);
|
||||
|
||||
$this->mapper->expects($this->once())
|
||||
->method('getTokenById')
|
||||
->with($this->equalTo(42))
|
||||
->willThrowException(new DoesNotExistException('nope'));
|
||||
|
||||
$this->tokenProvider->getTokenById(42);
|
||||
}
|
||||
}
|
||||
|
|
49
tests/lib/Authentication/Token/DefaultTokenTest.php
Normal file
49
tests/lib/Authentication/Token/DefaultTokenTest.php
Normal file
|
@ -0,0 +1,49 @@
|
|||
<?php
|
||||
/**
|
||||
* @copyright Copyright (c) 2016 Robin Appelman <robin@icewind.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 Test\Authentication\Token;
|
||||
|
||||
use OC\Authentication\Token\DefaultToken;
|
||||
use Test\TestCase;
|
||||
|
||||
class DefaultTokenTest extends TestCase {
|
||||
public function testSetScopeAsArray() {
|
||||
$scope = ['filesystem' => false];
|
||||
$token = new DefaultToken();
|
||||
$token->setScope($scope);
|
||||
$this->assertEquals(json_encode($scope), $token->getScope());
|
||||
$this->assertEquals($scope, $token->getScopeAsArray());
|
||||
}
|
||||
|
||||
public function testSetScopeAsString() {
|
||||
$scope = ['filesystem' => false];
|
||||
$token = new DefaultToken();
|
||||
$token->setScope(json_encode($scope));
|
||||
$this->assertEquals(json_encode($scope), $token->getScope());
|
||||
$this->assertEquals($scope, $token->getScopeAsArray());
|
||||
}
|
||||
|
||||
public function testDefaultScope() {
|
||||
$scope = ['filesystem' => true];
|
||||
$token = new DefaultToken();
|
||||
$this->assertEquals($scope, $token->getScopeAsArray());
|
||||
}
|
||||
}
|
63
tests/lib/Lockdown/Filesystem/NoFSTest.php
Normal file
63
tests/lib/Lockdown/Filesystem/NoFSTest.php
Normal file
|
@ -0,0 +1,63 @@
|
|||
<?php
|
||||
/**
|
||||
* @copyright 2016, Robin Appelman <robin@icewind.nl>
|
||||
*
|
||||
* @author Robin Appelman <robin@icewind.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 Test\Lockdown\Filesystem;
|
||||
|
||||
use OC\Authentication\Token\DefaultToken;
|
||||
use OC\Files\Filesystem;
|
||||
use OC\Lockdown\Filesystem\NullStorage;
|
||||
use Test\Traits\UserTrait;
|
||||
|
||||
/**
|
||||
* @group DB
|
||||
*/
|
||||
class NoFSTest extends \Test\TestCase {
|
||||
use UserTrait;
|
||||
|
||||
public function tearDown() {
|
||||
$token = new DefaultToken();
|
||||
$token->setScope([
|
||||
'filesystem' => true
|
||||
]);
|
||||
\OC::$server->getLockdownManager()->setToken($token);
|
||||
return parent::tearDown();
|
||||
}
|
||||
|
||||
public function setUp() {
|
||||
parent::setUp();
|
||||
$token = new DefaultToken();
|
||||
$token->setScope([
|
||||
'filesystem' => false
|
||||
]);
|
||||
|
||||
\OC::$server->getLockdownManager()->setToken($token);
|
||||
$this->createUser('foo', 'var');
|
||||
}
|
||||
|
||||
public function testSetupFS() {
|
||||
\OC_Util::tearDownFS();
|
||||
\OC_Util::setupFS('foo');
|
||||
|
||||
$this->assertInstanceOf(NullStorage::class, Filesystem::getStorage('/foo/files'));
|
||||
}
|
||||
}
|
157
tests/lib/Lockdown/Filesystem/NullCacheTest.php
Normal file
157
tests/lib/Lockdown/Filesystem/NullCacheTest.php
Normal file
|
@ -0,0 +1,157 @@
|
|||
<?php
|
||||
/**
|
||||
* @copyright 2016, Roeland Jago Douma <roeland@famdouma.nl>
|
||||
*
|
||||
* @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 Test\Lockdown\Filesystem;
|
||||
|
||||
use OC\ForbiddenException;
|
||||
use OC\Lockdown\Filesystem\NullCache;
|
||||
use OCP\Constants;
|
||||
use OCP\Files\Cache\ICache;
|
||||
use OCP\Files\FileInfo;
|
||||
|
||||
class NulLCacheTest extends \Test\TestCase {
|
||||
|
||||
/** @var NullCache */
|
||||
private $cache;
|
||||
|
||||
public function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->cache = new NullCache();
|
||||
}
|
||||
|
||||
public function testGetNumericStorageId() {
|
||||
$this->assertSame(-1, $this->cache->getNumericStorageId());
|
||||
}
|
||||
|
||||
public function testGetEmpty() {
|
||||
$this->assertNull($this->cache->get('foo'));
|
||||
}
|
||||
|
||||
public function testGet() {
|
||||
$data = $this->cache->get('');
|
||||
|
||||
$this->assertEquals(-1, $data['fileid']);
|
||||
$this->assertEquals(-1, $data['parent']);
|
||||
$this->assertEquals('', $data['name']);
|
||||
$this->assertEquals('', $data['path']);
|
||||
$this->assertEquals('0', $data['size']);
|
||||
$this->assertEquals('', $data['etag']);
|
||||
$this->assertEquals(FileInfo::MIMETYPE_FOLDER, $data['mimetype']);
|
||||
$this->assertEquals('httpd', $data['mimepart']);
|
||||
$this->assertEquals(Constants::PERMISSION_READ, $data['permissions']);
|
||||
}
|
||||
|
||||
public function testGetFolderContents() {
|
||||
$this->assertSame([], $this->cache->getFolderContents('foo'));
|
||||
}
|
||||
|
||||
public function testGetFolderContentsById() {
|
||||
$this->assertSame([], $this->cache->getFolderContentsById(42));
|
||||
}
|
||||
|
||||
public function testPut() {
|
||||
$this->expectException(ForbiddenException::class);
|
||||
$this->expectExceptionMessage('This request is not allowed to access the filesystem');
|
||||
|
||||
$this->cache->put('foo', ['size' => 100]);
|
||||
}
|
||||
|
||||
public function testInsert() {
|
||||
$this->expectException(ForbiddenException::class);
|
||||
$this->expectExceptionMessage('This request is not allowed to access the filesystem');
|
||||
|
||||
$this->cache->insert('foo', ['size' => 100]);
|
||||
}
|
||||
|
||||
public function testUpdate() {
|
||||
$this->expectException(ForbiddenException::class);
|
||||
$this->expectExceptionMessage('This request is not allowed to access the filesystem');
|
||||
|
||||
$this->cache->update('foo', ['size' => 100]);
|
||||
}
|
||||
|
||||
public function testGetId() {
|
||||
$this->assertSame(-1, $this->cache->getId('foo'));
|
||||
}
|
||||
|
||||
public function testGetParentId() {
|
||||
$this->assertSame(-1, $this->cache->getParentId('foo'));
|
||||
}
|
||||
|
||||
public function testInCache() {
|
||||
$this->assertTrue($this->cache->inCache(''));
|
||||
$this->assertFalse($this->cache->inCache('foo'));
|
||||
}
|
||||
|
||||
public function testRemove() {
|
||||
$this->expectException(ForbiddenException::class);
|
||||
$this->expectExceptionMessage('This request is not allowed to access the filesystem');
|
||||
|
||||
$this->cache->remove('foo');
|
||||
}
|
||||
|
||||
public function testMove() {
|
||||
$this->expectException(ForbiddenException::class);
|
||||
$this->expectExceptionMessage('This request is not allowed to access the filesystem');
|
||||
|
||||
$this->cache->move('foo', 'bar');
|
||||
}
|
||||
|
||||
public function testMoveFromCache() {
|
||||
$sourceCache = $this->createMock(ICache::class);
|
||||
|
||||
$this->expectException(ForbiddenException::class);
|
||||
$this->expectExceptionMessage('This request is not allowed to access the filesystem');
|
||||
|
||||
$this->cache->moveFromCache($sourceCache, 'foo', 'bar');
|
||||
}
|
||||
|
||||
public function testGetStatus() {
|
||||
$this->assertSame(ICache::COMPLETE, $this->cache->getStatus('foo'));
|
||||
}
|
||||
|
||||
public function testSearch() {
|
||||
$this->assertSame([], $this->cache->search('foo'));
|
||||
}
|
||||
|
||||
public function testSearchByMime() {
|
||||
$this->assertSame([], $this->cache->searchByMime('foo'));
|
||||
}
|
||||
|
||||
public function testSearchByTag() {
|
||||
$this->assertSame([], $this->cache->searchByTag('foo', 'user'));
|
||||
}
|
||||
|
||||
public function testGetIncomplete() {
|
||||
$this->assertSame([], $this->cache->getIncomplete());
|
||||
}
|
||||
|
||||
public function testGetPathById() {
|
||||
$this->assertSame('', $this->cache->getPathById(42));
|
||||
}
|
||||
|
||||
public function testNormalize() {
|
||||
$this->assertSame('foo/ bar /', $this->cache->normalize('foo/ bar /'));
|
||||
}
|
||||
}
|
245
tests/lib/Lockdown/Filesystem/NullStorageTest.php
Normal file
245
tests/lib/Lockdown/Filesystem/NullStorageTest.php
Normal file
|
@ -0,0 +1,245 @@
|
|||
<?php
|
||||
/**
|
||||
* @copyright 2016, Roeland Jago Douma <roeland@famdouma.nl>
|
||||
*
|
||||
* @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 Test\Lockdown\Filesystem;
|
||||
|
||||
use Icewind\Streams\IteratorDirectory;
|
||||
use OC\ForbiddenException;
|
||||
use OC\Lockdown\Filesystem\NullCache;
|
||||
use OC\Lockdown\Filesystem\NullStorage;
|
||||
use OCP\Files\Storage;
|
||||
use Test\TestCase;
|
||||
|
||||
class NullStorageTest extends TestCase {
|
||||
|
||||
/** @var NullStorage */
|
||||
private $storage;
|
||||
|
||||
public function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->storage = new NullStorage([]);
|
||||
}
|
||||
|
||||
public function testGetId() {
|
||||
$this->assertSame('null', $this->storage->getId());
|
||||
}
|
||||
|
||||
public function testMkdir() {
|
||||
$this->expectException(ForbiddenException::class);
|
||||
$this->expectExceptionMessage('This request is not allowed to access the filesystem');
|
||||
|
||||
$this->storage->mkdir('foo');
|
||||
}
|
||||
|
||||
public function testRmdir() {
|
||||
$this->expectException(ForbiddenException::class);
|
||||
$this->expectExceptionMessage('This request is not allowed to access the filesystem');
|
||||
|
||||
$this->storage->rmdir('foo');
|
||||
}
|
||||
|
||||
public function testOpendir() {
|
||||
$this->assertInstanceOf(IteratorDirectory::class, $this->storage->opendir('foo'));
|
||||
}
|
||||
|
||||
public function testIs_dir() {
|
||||
$this->assertTrue($this->storage->is_dir(''));
|
||||
$this->assertFalse($this->storage->is_dir('foo'));
|
||||
}
|
||||
|
||||
public function testIs_file() {
|
||||
$this->assertFalse($this->storage->is_file('foo'));
|
||||
}
|
||||
|
||||
public function testStat() {
|
||||
$this->expectException(ForbiddenException::class);
|
||||
$this->expectExceptionMessage('This request is not allowed to access the filesystem');
|
||||
|
||||
$this->storage->stat('foo');
|
||||
}
|
||||
|
||||
public function testFiletype() {
|
||||
$this->assertSame('dir', $this->storage->filetype(''));
|
||||
$this->assertFalse($this->storage->filetype('foo'));
|
||||
}
|
||||
|
||||
public function testFilesize() {
|
||||
$this->expectException(ForbiddenException::class);
|
||||
$this->expectExceptionMessage('This request is not allowed to access the filesystem');
|
||||
|
||||
$this->storage->filesize('foo');
|
||||
}
|
||||
|
||||
public function testIsCreatable() {
|
||||
$this->assertFalse($this->storage->isCreatable('foo'));
|
||||
}
|
||||
|
||||
public function testIsReadable() {
|
||||
$this->assertTrue($this->storage->isReadable(''));
|
||||
$this->assertFalse($this->storage->isReadable('foo'));
|
||||
}
|
||||
|
||||
public function testIsUpdatable() {
|
||||
$this->assertFalse($this->storage->isUpdatable('foo'));
|
||||
}
|
||||
|
||||
public function testIsDeletable() {
|
||||
$this->assertFalse($this->storage->isDeletable('foo'));
|
||||
}
|
||||
|
||||
public function testIsSharable() {
|
||||
$this->assertFalse($this->storage->isSharable('foo'));
|
||||
}
|
||||
|
||||
public function testGetPermissions() {
|
||||
$this->assertNull($this->storage->getPermissions('foo'));
|
||||
}
|
||||
|
||||
public function testFile_exists() {
|
||||
$this->assertTrue($this->storage->file_exists(''));
|
||||
$this->assertFalse($this->storage->file_exists('foo'));
|
||||
}
|
||||
|
||||
public function testFilemtime() {
|
||||
$this->assertFalse($this->storage->filemtime('foo'));
|
||||
}
|
||||
|
||||
public function testFile_get_contents() {
|
||||
$this->expectException(ForbiddenException::class);
|
||||
$this->expectExceptionMessage('This request is not allowed to access the filesystem');
|
||||
|
||||
$this->storage->file_get_contents('foo');
|
||||
}
|
||||
|
||||
public function testFile_put_contents() {
|
||||
$this->expectException(ForbiddenException::class);
|
||||
$this->expectExceptionMessage('This request is not allowed to access the filesystem');
|
||||
|
||||
$this->storage->file_put_contents('foo', 'bar');
|
||||
}
|
||||
|
||||
public function testUnlink() {
|
||||
$this->expectException(ForbiddenException::class);
|
||||
$this->expectExceptionMessage('This request is not allowed to access the filesystem');
|
||||
|
||||
$this->storage->unlink('foo');
|
||||
}
|
||||
|
||||
public function testRename() {
|
||||
$this->expectException(ForbiddenException::class);
|
||||
$this->expectExceptionMessage('This request is not allowed to access the filesystem');
|
||||
|
||||
$this->storage->rename('foo', 'bar');
|
||||
}
|
||||
|
||||
public function testCopy() {
|
||||
$this->expectException(ForbiddenException::class);
|
||||
$this->expectExceptionMessage('This request is not allowed to access the filesystem');
|
||||
|
||||
$this->storage->copy('foo', 'bar');
|
||||
}
|
||||
|
||||
public function testFopen() {
|
||||
$this->expectException(ForbiddenException::class);
|
||||
$this->expectExceptionMessage('This request is not allowed to access the filesystem');
|
||||
|
||||
$this->storage->fopen('foo', 'R');
|
||||
}
|
||||
|
||||
public function testGetMimeType() {
|
||||
$this->expectException(ForbiddenException::class);
|
||||
$this->expectExceptionMessage('This request is not allowed to access the filesystem');
|
||||
|
||||
$this->storage->getMimeType('foo');
|
||||
}
|
||||
|
||||
public function testHash() {
|
||||
$this->expectException(ForbiddenException::class);
|
||||
$this->expectExceptionMessage('This request is not allowed to access the filesystem');
|
||||
|
||||
$this->storage->hash('md5', 'foo', true);
|
||||
}
|
||||
|
||||
public function testFree_space() {
|
||||
$this->assertSame(0, $this->storage->free_space('foo'));
|
||||
}
|
||||
|
||||
public function testTouch() {
|
||||
$this->expectException(ForbiddenException::class);
|
||||
$this->expectExceptionMessage('This request is not allowed to access the filesystem');
|
||||
|
||||
$this->storage->touch('foo');
|
||||
}
|
||||
|
||||
public function testGetLocalFile() {
|
||||
$this->assertFalse($this->storage->getLocalFile('foo'));
|
||||
}
|
||||
|
||||
public function testHasUpdated() {
|
||||
$this->assertFalse($this->storage->hasUpdated('foo', 42));
|
||||
}
|
||||
|
||||
public function testGetETag() {
|
||||
$this->assertSame('', $this->storage->getETag('foo'));
|
||||
}
|
||||
|
||||
public function testIsLocal() {
|
||||
$this->assertFalse($this->storage->isLocal());
|
||||
}
|
||||
|
||||
public function testGetDirectDownload() {
|
||||
$this->assertFalse($this->storage->getDirectDownload('foo'));
|
||||
}
|
||||
|
||||
public function testCopyFromStorage() {
|
||||
$sourceStorage = $this->createMock(Storage::class);
|
||||
|
||||
$this->expectException(ForbiddenException::class);
|
||||
$this->expectExceptionMessage('This request is not allowed to access the filesystem');
|
||||
|
||||
$this->storage->copyFromStorage($sourceStorage, 'foo', 'bar');
|
||||
}
|
||||
|
||||
public function testMoveFromStorage() {
|
||||
$sourceStorage = $this->createMock(Storage::class);
|
||||
|
||||
$this->expectException(ForbiddenException::class);
|
||||
$this->expectExceptionMessage('This request is not allowed to access the filesystem');
|
||||
|
||||
$this->storage->moveFromStorage($sourceStorage, 'foo', 'bar');
|
||||
}
|
||||
|
||||
public function testTest() {
|
||||
$this->assertTrue($this->storage->test());
|
||||
return true;
|
||||
}
|
||||
|
||||
public function testGetOwner() {
|
||||
$this->assertNull($this->storage->getOwner('foo'));
|
||||
}
|
||||
|
||||
public function testGetCache() {
|
||||
$this->assertInstanceOf(NullCache::class, $this->storage->getCache('foo'));
|
||||
}
|
||||
}
|
49
tests/lib/Lockdown/LockdownManagerTest.php
Normal file
49
tests/lib/Lockdown/LockdownManagerTest.php
Normal file
|
@ -0,0 +1,49 @@
|
|||
<?php
|
||||
/**
|
||||
* @copyright Copyright (c) 2016 Robin Appelman <robin@icewind.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 Test\Lockdown;
|
||||
|
||||
use OC\Authentication\Token\DefaultToken;
|
||||
use OC\Lockdown\LockdownManager;
|
||||
use Test\TestCase;
|
||||
|
||||
class LockdownManagerTest extends TestCase {
|
||||
public function testCanAccessFilesystemDisabled() {
|
||||
$manager = new LockdownManager();
|
||||
$this->assertTrue($manager->canAccessFilesystem());
|
||||
}
|
||||
|
||||
public function testCanAccessFilesystemAllowed() {
|
||||
$token = new DefaultToken();
|
||||
$token->setScope(['filesystem' => true]);
|
||||
$manager = new LockdownManager();
|
||||
$manager->setToken($token);
|
||||
$this->assertTrue($manager->canAccessFilesystem());
|
||||
}
|
||||
|
||||
public function testCanAccessFilesystemNotAllowed() {
|
||||
$token = new DefaultToken();
|
||||
$token->setScope(['filesystem' => false]);
|
||||
$manager = new LockdownManager();
|
||||
$manager->setToken($token);
|
||||
$this->assertFalse($manager->canAccessFilesystem());
|
||||
}
|
||||
}
|
|
@ -25,7 +25,7 @@
|
|||
// We only can count up. The 4. digit is only for the internal patchlevel to trigger DB upgrades
|
||||
// between betas, final and RCs. This is _not_ the public version number. Reset minor/patchlevel
|
||||
// when updating major/minor version number.
|
||||
$OC_Version = array(11, 0, 0, 0);
|
||||
$OC_Version = array(11, 0, 0, 1);
|
||||
|
||||
// The human readable string
|
||||
$OC_VersionString = '11.0 alpha';
|
||||
|
|
Loading…
Reference in a new issue