diff --git a/apps/dav/lib/Connector/Sabre/Auth.php b/apps/dav/lib/Connector/Sabre/Auth.php
index a35eed8807395c71b6b4066d366fdfaf59aa7d4f..95222dafec915b34a5454f86bb40fec45ecc3eb9 100644
--- a/apps/dav/lib/Connector/Sabre/Auth.php
+++ b/apps/dav/lib/Connector/Sabre/Auth.php
@@ -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");
 		}
 	}
diff --git a/db_structure.xml b/db_structure.xml
index 09dbde710d30fb507b78c74cb791249bd38932e9..c7e1e072a8eff238f5f36e6d62db47b7489ebb52 100644
--- a/db_structure.xml
+++ b/db_structure.xml
@@ -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>
diff --git a/lib/composer/composer/autoload_classmap.php b/lib/composer/composer/autoload_classmap.php
index 42cfb8c45e14cee3f33e11a11febf733e2052741..69e8428fdea133572dd62e54f960a244d336cb95 100644
--- a/lib/composer/composer/autoload_classmap.php
+++ b/lib/composer/composer/autoload_classmap.php
@@ -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',
diff --git a/lib/composer/composer/autoload_static.php b/lib/composer/composer/autoload_static.php
index d7e937577f25475e274c5f8326536d5d966f8614..c960a35d951d701daa553080e6e74a2fcdcf2b8a 100644
--- a/lib/composer/composer/autoload_static.php
+++ b/lib/composer/composer/autoload_static.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',
diff --git a/lib/private/Authentication/Token/DefaultToken.php b/lib/private/Authentication/Token/DefaultToken.php
index faef2f73b33dfd84c93c04fe3dc6a63ab0b6879e..127430ea6cbcd749206495e7ea2a304707fd2647 100644
--- a/lib/private/Authentication/Token/DefaultToken.php
+++ b/lib/private/Authentication/Token/DefaultToken.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);
+		}
+	}
 }
diff --git a/lib/private/Authentication/Token/DefaultTokenMapper.php b/lib/private/Authentication/Token/DefaultTokenMapper.php
index 752974ff24033af274418ff0f314dfd4b2227fba..8848cd3ec5630d295b5b45f412fc4c98f060c8bd 100644
--- a/lib/private/Authentication/Token/DefaultTokenMapper.php
+++ b/lib/private/Authentication/Token/DefaultTokenMapper.php
@@ -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);
diff --git a/lib/private/Authentication/Token/DefaultTokenProvider.php b/lib/private/Authentication/Token/DefaultTokenProvider.php
index 87f434c684c56bd12b9ce02055a8bd933b0d40cc..0fdbc4a51dda486e6290e502a273dd3303c2e2eb 100644
--- a/lib/private/Authentication/Token/DefaultTokenProvider.php
+++ b/lib/private/Authentication/Token/DefaultTokenProvider.php
@@ -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
diff --git a/lib/private/Authentication/Token/IProvider.php b/lib/private/Authentication/Token/IProvider.php
index ce14a5880c55f0d2220aa56fdd94d307c4f6f7d9..9f280263d76050bd4870b2095b7c9e841952e019 100644
--- a/lib/private/Authentication/Token/IProvider.php
+++ b/lib/private/Authentication/Token/IProvider.php
@@ -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
diff --git a/lib/private/Authentication/Token/IToken.php b/lib/private/Authentication/Token/IToken.php
index 14811dd3201a07e22ae5cadb58fe18ac6cbba6b6..49745b266c4645681c08e654d649007d2b613a27 100644
--- a/lib/private/Authentication/Token/IToken.php
+++ b/lib/private/Authentication/Token/IToken.php
@@ -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);
 }
diff --git a/lib/private/Files/Filesystem.php b/lib/private/Files/Filesystem.php
index 55cf38bbdc92c4d66d9148c5288d40f58726edc9..ac0e66973d41d8b8a502dc2fd1e6d1e8cc9af54f 100644
--- a/lib/private/Files/Filesystem.php
+++ b/lib/private/Files/Filesystem.php
@@ -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);
+			self::listenForNewMountProviders($mountConfigManager, $userManager);
+		} else {
+			self::$mounts->addMount(new MountPoint(
+				new NullStorage([]),
+				'/' . $user
+			));
+			self::$mounts->addMount(new MountPoint(
+				new NullStorage([]),
+				'/' . $user . '/files'
+			));
+		}
 		\OC_Hook::emit('OC_Filesystem', 'post_initMountPoints', array('user' => $user));
 	}
 
diff --git a/lib/private/Lockdown/Filesystem/NullCache.php b/lib/private/Lockdown/Filesystem/NullCache.php
new file mode 100644
index 0000000000000000000000000000000000000000..8c6b5258aa8b47b3661b7fc06b3b6d5992aa4310
--- /dev/null
+++ b/lib/private/Lockdown/Filesystem/NullCache.php
@@ -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;
+	}
+
+}
diff --git a/lib/private/Lockdown/Filesystem/NullStorage.php b/lib/private/Lockdown/Filesystem/NullStorage.php
new file mode 100644
index 0000000000000000000000000000000000000000..967b6d2c6e777c5a6cf4e4adcf9db1bdb1c5ded6
--- /dev/null
+++ b/lib/private/Lockdown/Filesystem/NullStorage.php
@@ -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();
+	}
+}
diff --git a/lib/private/Lockdown/LockdownManager.php b/lib/private/Lockdown/LockdownManager.php
new file mode 100644
index 0000000000000000000000000000000000000000..5ce52a03683f8a3c676a412b725f1df4e3b02b6f
--- /dev/null
+++ b/lib/private/Lockdown/LockdownManager.php
@@ -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'];
+	}
+}
diff --git a/lib/private/Server.php b/lib/private/Server.php
index abedf8230ed9b3bfc50c56f32a8e11d33fc760f0..c6755357a1d53d7a9cc2906760671c3ac4361ca5 100644
--- a/lib/private/Server.php
+++ b/lib/private/Server.php
@@ -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');
+	}
 }
diff --git a/lib/private/User/Session.php b/lib/private/User/Session.php
index ef408aa40776bb43e520e907e3906f1888d42abf..6033f0605041f6526e33316ad83ba6de260154c4 100644
--- a/lib/private/User/Session.php
+++ b/lib/private/User/Session.php
@@ -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()) {
diff --git a/lib/public/Lockdown/ILockdownManager.php b/lib/public/Lockdown/ILockdownManager.php
new file mode 100644
index 0000000000000000000000000000000000000000..d4d05b37ff83c69cf4d792c4f2df726687c6eaed
--- /dev/null
+++ b/lib/public/Lockdown/ILockdownManager.php
@@ -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();
+}
diff --git a/settings/Controller/AuthSettingsController.php b/settings/Controller/AuthSettingsController.php
index 58994f0d59c41b943917e6edf53626c58a9570d4..4e3d05a14e84666ed7edf9bc33c550d0c31096c9 100644
--- a/settings/Controller/AuthSettingsController.php
+++ b/settings/Controller/AuthSettingsController.php
@@ -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 [];
+	}
 }
diff --git a/settings/css/settings.css b/settings/css/settings.css
index 37197b9550cdf5ad00f53ce449649e0957140813..9008ba5a985bab9f18cc4c84f8328f52f1fdae10 100644
--- a/settings/css/settings.css
+++ b/settings/css/settings.css
@@ -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;
diff --git a/settings/js/authtoken_view.js b/settings/js/authtoken_view.js
index 6eb04b63f201b4a10a125ed5bf34c132d904106f..20fe5235eb0f4a1392c01609dac509b0fe0b7e7f 100644
--- a/settings/js/authtoken_view.js
+++ b/settings/js/authtoken_view.js
@@ -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>'
+		+ '<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}}'
-		+ '<td><a class="icon-delete has-tooltip" title="' + t('core', 'Disconnect') + '"></a></td>'
-		+ '{{else}}'
-		+ '<td></td>'
+		+ '<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();
diff --git a/tests/Settings/Controller/AuthSettingsControllerTest.php b/tests/Settings/Controller/AuthSettingsControllerTest.php
index 9cb49e4eb3f11d52a8c517bbf3459c382484aeca..782c9f644e0b4ac1b8307773f1fc8d3028350e09 100644
--- a/tests/Settings/Controller/AuthSettingsControllerTest.php
+++ b/tests/Settings/Controller/AuthSettingsControllerTest.php
@@ -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']]));
+	}
+
 }
diff --git a/tests/lib/Authentication/Token/DefaultTokenMapperTest.php b/tests/lib/Authentication/Token/DefaultTokenMapperTest.php
index 418a4d14f624f21ffafd7b460b7a0c327d5400b2..8fe0762daadfd464eb804a16c0571225fa9305cc 100644
--- a/tests/lib/Authentication/Token/DefaultTokenMapperTest.php
+++ b/tests/lib/Authentication/Token/DefaultTokenMapperTest.php
@@ -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())
diff --git a/tests/lib/Authentication/Token/DefaultTokenProviderTest.php b/tests/lib/Authentication/Token/DefaultTokenProviderTest.php
index 5e4d4f9436664b8db8037151d03a48dc2c8ffb3b..8d92ee405a1fdd05b2a35e7c0777aa059b8a05b3 100644
--- a/tests/lib/Authentication/Token/DefaultTokenProviderTest.php
+++ b/tests/lib/Authentication/Token/DefaultTokenProviderTest.php
@@ -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);
+	}
 }
diff --git a/tests/lib/Authentication/Token/DefaultTokenTest.php b/tests/lib/Authentication/Token/DefaultTokenTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..f00c32ccaf5cd78c894e09198163ee30fa7ce6bc
--- /dev/null
+++ b/tests/lib/Authentication/Token/DefaultTokenTest.php
@@ -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());
+	}
+}
diff --git a/tests/lib/Lockdown/Filesystem/NoFSTest.php b/tests/lib/Lockdown/Filesystem/NoFSTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..a0900ad769d39b237658633cb28718b4b8c02c9b
--- /dev/null
+++ b/tests/lib/Lockdown/Filesystem/NoFSTest.php
@@ -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'));
+	}
+}
diff --git a/tests/lib/Lockdown/Filesystem/NullCacheTest.php b/tests/lib/Lockdown/Filesystem/NullCacheTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..3a4e3f3a40238881aae02d0ca516885e2af7362f
--- /dev/null
+++ b/tests/lib/Lockdown/Filesystem/NullCacheTest.php
@@ -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 /'));
+	}
+}
diff --git a/tests/lib/Lockdown/Filesystem/NullStorageTest.php b/tests/lib/Lockdown/Filesystem/NullStorageTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..dc99eb4c03aa8c236fe29dc7c113109ac4a332a4
--- /dev/null
+++ b/tests/lib/Lockdown/Filesystem/NullStorageTest.php
@@ -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'));
+	}
+}
diff --git a/tests/lib/Lockdown/LockdownManagerTest.php b/tests/lib/Lockdown/LockdownManagerTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..4cbd9d71a5c4839da9ec27aa40d83263ef97d3e2
--- /dev/null
+++ b/tests/lib/Lockdown/LockdownManagerTest.php
@@ -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());
+	}
+}
diff --git a/version.php b/version.php
index 42a0e7c9bdbe87f496ea7925e554f021c0c18cad..d556386a848a058782858e554966d1d4d41154a8 100644
--- a/version.php
+++ b/version.php
@@ -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';