summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--apps/dav/l10n/sv.js26
-rw-r--r--apps/dav/l10n/sv.json26
-rw-r--r--apps/federatedfilesharing/lib/Controller/MountPublicLinkController.php46
-rw-r--r--apps/theming/lib/Controller/ThemingController.php1
-rw-r--r--apps/theming/tests/Controller/ThemingControllerTest.php4
-rw-r--r--apps/twofactor_backupcodes/appinfo/app.php3
-rw-r--r--apps/twofactor_backupcodes/appinfo/routes.php2
-rw-r--r--apps/twofactor_backupcodes/l10n/de.js2
-rw-r--r--apps/twofactor_backupcodes/l10n/de.json2
-rw-r--r--apps/twofactor_backupcodes/lib/Activity/Provider.php5
-rw-r--r--apps/twofactor_backupcodes/lib/Db/BackupCodeMapper.php1
-rw-r--r--apps/twofactor_backupcodes/settings/personal.php3
-rw-r--r--apps/twofactor_backupcodes/tests/Unit/Activity/GenericProviderTest.php132
-rw-r--r--apps/twofactor_backupcodes/tests/Unit/Activity/ProviderTest.php55
-rw-r--r--config/config.sample.php226
-rw-r--r--core/Controller/LoginController.php12
-rw-r--r--core/Controller/LostController.php1
-rw-r--r--core/Controller/OCSController.php2
-rw-r--r--core/css/apps.scss4
-rw-r--r--core/css/header.scss227
-rw-r--r--lib/private/AppFramework/DependencyInjection/DIContainer.php21
-rw-r--r--lib/private/AppFramework/Middleware/Security/SecurityMiddleware.php14
-rw-r--r--lib/private/AppFramework/Utility/ControllerMethodReflector.php23
-rw-r--r--lib/private/Security/Bruteforce/Throttler.php13
-rw-r--r--lib/private/User/Session.php6
-rw-r--r--tests/lib/AppFramework/Middleware/Security/SecurityMiddlewareTest.php73
-rw-r--r--tests/lib/AppFramework/Utility/ControllerMethodReflectorTest.php13
27 files changed, 701 insertions, 242 deletions
diff --git a/apps/dav/l10n/sv.js b/apps/dav/l10n/sv.js
index 28eea0c5867..25cd0d797c8 100644
--- a/apps/dav/l10n/sv.js
+++ b/apps/dav/l10n/sv.js
@@ -2,7 +2,7 @@ OC.L10N.register(
"dav",
{
"Calendar" : "Kalender",
- "Todos" : "Att göra",
+ "Todos" : "Uppgifter",
"{actor} created calendar {calendar}" : "{actor} skapade kalender {calendar}",
"You created calendar {calendar}" : "Du skapade kalender {calendar}",
"{actor} deleted calendar {calendar}" : "{actor} raderade kalender {calendar}",
@@ -26,20 +26,20 @@ OC.L10N.register(
"You deleted event {event} from calendar {calendar}" : "Du raderade händelse {event} från kalender {calendar}",
"{actor} updated event {event} in calendar {calendar}" : "{actor} uppdaterade händelse {event} i kalender {calendar}",
"You updated event {event} in calendar {calendar}" : "Du uppdaterade händelse {event} i kalender {calendar}",
- "{actor} created todo {todo} in list {calendar}" : "{actor} skapade att-göra {todo} i listan {calendar}",
- "You created todo {todo} in list {calendar}" : "Du skapade att-göra {todo} i listan {calendar}",
- "{actor} deleted todo {todo} from list {calendar}" : "{actor} raderade att-göra {todo} från listan {calendar}",
- "You deleted todo {todo} from list {calendar}" : "Du raderade att-göra {todo} från listan {calendar}",
- "{actor} updated todo {todo} in list {calendar}" : "{actor} uppdaterade att-göra {todo} i listan {calendar}",
- "You updated todo {todo} in list {calendar}" : "Du uppdaterade att-göra {todo} i listan {calendar}",
- "{actor} solved todo {todo} in list {calendar}" : "{actor} löste att-göra {todo} i listan {calendar}",
- "You solved todo {todo} in list {calendar}" : "Du löste att-göra {todo} i listan {calendar}",
- "{actor} reopened todo {todo} in list {calendar}" : "{actor} återupptog att-göra {todo} i listan {calendar}",
- "You reopened todo {todo} in list {calendar}" : "Du återupptog att-göra {todo} i listan {calendar}",
+ "{actor} created todo {todo} in list {calendar}" : "{actor} skapade uppgift {todo} i listan {calendar}",
+ "You created todo {todo} in list {calendar}" : "Du skapade uppgift {todo} i listan {calendar}",
+ "{actor} deleted todo {todo} from list {calendar}" : "{actor} raderade uppgift {todo} från listan {calendar}",
+ "You deleted todo {todo} from list {calendar}" : "Du raderade uppgift {todo} från listan {calendar}",
+ "{actor} updated todo {todo} in list {calendar}" : "{actor} uppdaterade uppgift {todo} i listan {calendar}",
+ "You updated todo {todo} in list {calendar}" : "Du uppdaterade uppgift {todo} i listan {calendar}",
+ "{actor} solved todo {todo} in list {calendar}" : "{actor} löste uppgift {todo} i listan {calendar}",
+ "You solved todo {todo} in list {calendar}" : "Du löste uppgift {todo} i listan {calendar}",
+ "{actor} reopened todo {todo} in list {calendar}" : "{actor} återupptog uppgift {todo} i listan {calendar}",
+ "You reopened todo {todo} in list {calendar}" : "Du återupptog uppgift {todo} i listan {calendar}",
"A <strong>calendar</strong> was modified" : "En <strong>kalender</strong> modifierades",
"A calendar <strong>event</strong> was modified" : "En kalender-<strong>händelse</strong> modifierades",
- "A calendar <strong>todo</strong> was modified" : "En kalender <strong>att-göra</strong> modifierades",
- "Contact birthdays" : "Kontakters födelsedagar",
+ "A calendar <strong>todo</strong> was modified" : "En kalender <strong>uppgift</strong> modifierades",
+ "Contact birthdays" : "Födelsedagar",
"Personal" : "Privat",
"Contacts" : "Kontakter",
"Technical details" : "Tekniska detaljer",
diff --git a/apps/dav/l10n/sv.json b/apps/dav/l10n/sv.json
index 8dfb9e4e6ba..ba76ca55329 100644
--- a/apps/dav/l10n/sv.json
+++ b/apps/dav/l10n/sv.json
@@ -1,6 +1,6 @@
{ "translations": {
"Calendar" : "Kalender",
- "Todos" : "Att göra",
+ "Todos" : "Uppgifter",
"{actor} created calendar {calendar}" : "{actor} skapade kalender {calendar}",
"You created calendar {calendar}" : "Du skapade kalender {calendar}",
"{actor} deleted calendar {calendar}" : "{actor} raderade kalender {calendar}",
@@ -24,20 +24,20 @@
"You deleted event {event} from calendar {calendar}" : "Du raderade händelse {event} från kalender {calendar}",
"{actor} updated event {event} in calendar {calendar}" : "{actor} uppdaterade händelse {event} i kalender {calendar}",
"You updated event {event} in calendar {calendar}" : "Du uppdaterade händelse {event} i kalender {calendar}",
- "{actor} created todo {todo} in list {calendar}" : "{actor} skapade att-göra {todo} i listan {calendar}",
- "You created todo {todo} in list {calendar}" : "Du skapade att-göra {todo} i listan {calendar}",
- "{actor} deleted todo {todo} from list {calendar}" : "{actor} raderade att-göra {todo} från listan {calendar}",
- "You deleted todo {todo} from list {calendar}" : "Du raderade att-göra {todo} från listan {calendar}",
- "{actor} updated todo {todo} in list {calendar}" : "{actor} uppdaterade att-göra {todo} i listan {calendar}",
- "You updated todo {todo} in list {calendar}" : "Du uppdaterade att-göra {todo} i listan {calendar}",
- "{actor} solved todo {todo} in list {calendar}" : "{actor} löste att-göra {todo} i listan {calendar}",
- "You solved todo {todo} in list {calendar}" : "Du löste att-göra {todo} i listan {calendar}",
- "{actor} reopened todo {todo} in list {calendar}" : "{actor} återupptog att-göra {todo} i listan {calendar}",
- "You reopened todo {todo} in list {calendar}" : "Du återupptog att-göra {todo} i listan {calendar}",
+ "{actor} created todo {todo} in list {calendar}" : "{actor} skapade uppgift {todo} i listan {calendar}",
+ "You created todo {todo} in list {calendar}" : "Du skapade uppgift {todo} i listan {calendar}",
+ "{actor} deleted todo {todo} from list {calendar}" : "{actor} raderade uppgift {todo} från listan {calendar}",
+ "You deleted todo {todo} from list {calendar}" : "Du raderade uppgift {todo} från listan {calendar}",
+ "{actor} updated todo {todo} in list {calendar}" : "{actor} uppdaterade uppgift {todo} i listan {calendar}",
+ "You updated todo {todo} in list {calendar}" : "Du uppdaterade uppgift {todo} i listan {calendar}",
+ "{actor} solved todo {todo} in list {calendar}" : "{actor} löste uppgift {todo} i listan {calendar}",
+ "You solved todo {todo} in list {calendar}" : "Du löste uppgift {todo} i listan {calendar}",
+ "{actor} reopened todo {todo} in list {calendar}" : "{actor} återupptog uppgift {todo} i listan {calendar}",
+ "You reopened todo {todo} in list {calendar}" : "Du återupptog uppgift {todo} i listan {calendar}",
"A <strong>calendar</strong> was modified" : "En <strong>kalender</strong> modifierades",
"A calendar <strong>event</strong> was modified" : "En kalender-<strong>händelse</strong> modifierades",
- "A calendar <strong>todo</strong> was modified" : "En kalender <strong>att-göra</strong> modifierades",
- "Contact birthdays" : "Kontakters födelsedagar",
+ "A calendar <strong>todo</strong> was modified" : "En kalender <strong>uppgift</strong> modifierades",
+ "Contact birthdays" : "Födelsedagar",
"Personal" : "Privat",
"Contacts" : "Kontakter",
"Technical details" : "Tekniska detaljer",
diff --git a/apps/federatedfilesharing/lib/Controller/MountPublicLinkController.php b/apps/federatedfilesharing/lib/Controller/MountPublicLinkController.php
index 55329338a92..3c399268124 100644
--- a/apps/federatedfilesharing/lib/Controller/MountPublicLinkController.php
+++ b/apps/federatedfilesharing/lib/Controller/MountPublicLinkController.php
@@ -25,18 +25,24 @@
namespace OCA\FederatedFileSharing\Controller;
+use OC\Files\Filesystem;
use OC\HintException;
+use OC\Share\Helper;
use OCA\FederatedFileSharing\AddressHandler;
+use OCA\FederatedFileSharing\DiscoveryManager;
use OCA\FederatedFileSharing\FederatedShareProvider;
+use OCA\Files_Sharing\External\Manager;
use OCP\AppFramework\Controller;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\JSONResponse;
+use OCP\Files\StorageInvalidException;
use OCP\Http\Client\IClientService;
use OCP\IL10N;
use OCP\IRequest;
use OCP\ISession;
use OCP\IUserSession;
use OCP\Share\IManager;
+use OCP\Util;
/**
* Class MountPublicLinkController
@@ -107,6 +113,7 @@ class MountPublicLinkController extends Controller {
*
* @NoCSRFRequired
* @PublicPage
+ * @BruteForceProtection publicLink2FederatedShare
*
* @param string $shareWith
* @param string $token
@@ -226,22 +233,22 @@ class MountPublicLinkController extends Controller {
private function legacyMountPublicLink($token, $remote, $password, $name, $owner, $ownerDisplayName) {
// Check for invalid name
- if (!\OCP\Util::isValidFileName($name)) {
+ if (!Util::isValidFileName($name)) {
return new JSONResponse(['message' => $this->l->t('The mountpoint name contains invalid characters.')], Http::STATUS_BAD_REQUEST);
}
$currentUser = $this->userSession->getUser()->getUID();
$currentServer = $this->addressHandler->generateRemoteURL();
- if (\OC\Share\Helper::isSameUserOnSameServer($owner, $remote, $currentUser, $currentServer)) {
+ if (Helper::isSameUserOnSameServer($owner, $remote, $currentUser, $currentServer)) {
return new JSONResponse(['message' => $this->l->t('Not allowed to create a federated share with the owner.')], Http::STATUS_BAD_REQUEST);
}
- $discoveryManager = new \OCA\FederatedFileSharing\DiscoveryManager(
+ $discoveryManager = new DiscoveryManager(
\OC::$server->getMemCacheFactory(),
\OC::$server->getHTTPClientService()
);
- $externalManager = new \OCA\Files_Sharing\External\Manager(
+ $externalManager = new Manager(
\OC::$server->getDatabaseConnection(),
- \OC\Files\Filesystem::getMountManager(),
- \OC\Files\Filesystem::getLoader(),
+ Filesystem::getMountManager(),
+ Filesystem::getLoader(),
\OC::$server->getHTTPClientService(),
\OC::$server->getNotificationManager(),
$discoveryManager,
@@ -249,7 +256,8 @@ class MountPublicLinkController extends Controller {
);
// check for ssl cert
- if (substr($remote, 0, 5) === 'https') {
+
+ if (strpos($remote, 'https') === 0) {
try {
$client = $this->clientService->newClient();
$client->get($remote, [
@@ -268,19 +276,19 @@ class MountPublicLinkController extends Controller {
try {
// check if storage exists
$storage->checkStorageAvailability();
- } catch (\OCP\Files\StorageInvalidException $e) {
+ } catch (StorageInvalidException $e) {
// note: checkStorageAvailability will already remove the invalid share
- \OCP\Util::writeLog(
+ Util::writeLog(
'federatedfilesharing',
'Invalid remote storage: ' . get_class($e) . ': ' . $e->getMessage(),
- \OCP\Util::DEBUG
+ Util::DEBUG
);
return new JSONResponse(['message' => $this->l->t('Could not authenticate to remote share, password might be wrong')], Http::STATUS_BAD_REQUEST);
} catch (\Exception $e) {
- \OCP\Util::writeLog(
+ Util::writeLog(
'federatedfilesharing',
'Invalid remote storage: ' . get_class($e) . ': ' . $e->getMessage(),
- \OCP\Util::DEBUG
+ Util::DEBUG
);
$externalManager->removeShare($mount->getMountPoint());
return new JSONResponse(['message' => $this->l->t('Storage not valid')], Http::STATUS_BAD_REQUEST);
@@ -295,27 +303,27 @@ class MountPublicLinkController extends Controller {
'legacyMount' => '1'
]
);
- } catch (\OCP\Files\StorageInvalidException $e) {
- \OCP\Util::writeLog(
+ } catch (StorageInvalidException $e) {
+ Util::writeLog(
'federatedfilesharing',
'Invalid remote storage: ' . get_class($e) . ': ' . $e->getMessage(),
- \OCP\Util::DEBUG
+ Util::DEBUG
);
return new JSONResponse(['message' => $this->l->t('Storage not valid')], Http::STATUS_BAD_REQUEST);
} catch (\Exception $e) {
- \OCP\Util::writeLog(
+ Util::writeLog(
'federatedfilesharing',
'Invalid remote storage: ' . get_class($e) . ': ' . $e->getMessage(),
- \OCP\Util::DEBUG
+ Util::DEBUG
);
return new JSONResponse(['message' => $this->l->t('Couldn\'t add remote share')], Http::STATUS_BAD_REQUEST);
}
} else {
$externalManager->removeShare($mount->getMountPoint());
- \OCP\Util::writeLog(
+ Util::writeLog(
'federatedfilesharing',
'Couldn\'t add remote share',
- \OCP\Util::DEBUG
+ Util::DEBUG
);
return new JSONResponse(['message' => $this->l->t('Couldn\'t add remote share')], Http::STATUS_BAD_REQUEST);
}
diff --git a/apps/theming/lib/Controller/ThemingController.php b/apps/theming/lib/Controller/ThemingController.php
index 15bbfc30c54..24ac1c7d8d5 100644
--- a/apps/theming/lib/Controller/ThemingController.php
+++ b/apps/theming/lib/Controller/ThemingController.php
@@ -374,6 +374,7 @@ class ThemingController extends Controller {
';
$responseCss .= sprintf('.nc-theming-main-background {background-color: %s}' . "\n", $color);
$responseCss .= sprintf('.nc-theming-main-text {color: %s}' . "\n", $color);
+ $responseCss .= sprintf('#app-navigation li:hover > a, #app-navigation li:focus > a, #app-navigation a:focus, #app-navigation .selected, #app-navigation .selected a, #app-navigation .active, #app-navigation .active a {box-shadow: inset 2px 0 %s}' . "\n", $color);
}
$logo = $this->config->getAppValue($this->appName, 'logoMime');
diff --git a/apps/theming/tests/Controller/ThemingControllerTest.php b/apps/theming/tests/Controller/ThemingControllerTest.php
index f7c4a9f120e..97a5e985860 100644
--- a/apps/theming/tests/Controller/ThemingControllerTest.php
+++ b/apps/theming/tests/Controller/ThemingControllerTest.php
@@ -487,6 +487,7 @@ class ThemingControllerTest extends TestCase {
';
$expectedData .= sprintf('.nc-theming-main-background {background-color: %s}' . "\n", $color);
$expectedData .= sprintf('.nc-theming-main-text {color: %s}' . "\n", $color);
+ $expectedData .= sprintf('#app-navigation li:hover > a, #app-navigation li:focus > a, #app-navigation a:focus, #app-navigation .selected, #app-navigation .selected a, #app-navigation .active, #app-navigation .active a {box-shadow: inset 2px 0 %s}' . "\n", $color);
$expectedData .= '.nc-theming-contrast {color: #ffffff}' . "\n";
$expectedData .= '.icon-file,.icon-filetype-text {' .
'background-image: url(\'./img/core/filetypes/text.svg?v=0\');' . "}\n" .
@@ -581,6 +582,7 @@ class ThemingControllerTest extends TestCase {
';
$expectedData .= sprintf('.nc-theming-main-background {background-color: %s}' . "\n", $color);
$expectedData .= sprintf('.nc-theming-main-text {color: %s}' . "\n", $color);
+ $expectedData .= sprintf('#app-navigation li:hover > a, #app-navigation li:focus > a, #app-navigation a:focus, #app-navigation .selected, #app-navigation .selected a, #app-navigation .active, #app-navigation .active a {box-shadow: inset 2px 0 %s}' . "\n", $color);
$expectedData .= '#header .header-appname, #expandDisplayName { color: #000000; }' . "\n";
$expectedData .= '#header .icon-caret { background-image: url(\'' . \OC::$WEBROOT . '/core/img/actions/caret-dark.svg\'); }' . "\n";
$expectedData .= '.searchbox input[type="search"] { background: transparent url(\'' . \OC::$WEBROOT . '/core/img/actions/search.svg\') no-repeat 6px center; color: #000; }' . "\n";
@@ -768,6 +770,7 @@ class ThemingControllerTest extends TestCase {
';
$expectedData .= sprintf('.nc-theming-main-background {background-color: %s}' . "\n", $color);
$expectedData .= sprintf('.nc-theming-main-text {color: %s}' . "\n", $color);
+ $expectedData .= sprintf('#app-navigation li:hover > a, #app-navigation li:focus > a, #app-navigation a:focus, #app-navigation .selected, #app-navigation .selected a, #app-navigation .active, #app-navigation .active a {box-shadow: inset 2px 0 %s}' . "\n", $color);
$expectedData .= sprintf(
'#header .logo {' .
'background-image: url(\'./logo?v=0\');' .
@@ -879,6 +882,7 @@ class ThemingControllerTest extends TestCase {
';
$expectedData .= sprintf('.nc-theming-main-background {background-color: %s}' . "\n", $color);
$expectedData .= sprintf('.nc-theming-main-text {color: %s}' . "\n", $color);
+ $expectedData .= sprintf('#app-navigation li:hover > a, #app-navigation li:focus > a, #app-navigation a:focus, #app-navigation .selected, #app-navigation .selected a, #app-navigation .active, #app-navigation .active a {box-shadow: inset 2px 0 %s}' . "\n", $color);
$expectedData .= sprintf(
'#header .logo {' .
'background-image: url(\'./logo?v=0\');' .
diff --git a/apps/twofactor_backupcodes/appinfo/app.php b/apps/twofactor_backupcodes/appinfo/app.php
index 31f9b6b8eae..0cb10531360 100644
--- a/apps/twofactor_backupcodes/appinfo/app.php
+++ b/apps/twofactor_backupcodes/appinfo/app.php
@@ -19,4 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
+
+// @codeCoverageIgnoreStart
OC_App::registerPersonal('twofactor_backupcodes', 'settings/personal');
+// @codeCoverageIgnoreEnd
diff --git a/apps/twofactor_backupcodes/appinfo/routes.php b/apps/twofactor_backupcodes/appinfo/routes.php
index f2af12e9b45..0119bfd0b08 100644
--- a/apps/twofactor_backupcodes/appinfo/routes.php
+++ b/apps/twofactor_backupcodes/appinfo/routes.php
@@ -19,6 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
+// @codeCoverageIgnoreStart
return [
'routes' => [
[
@@ -33,3 +34,4 @@ return [
],
]
];
+// @codeCoverageIgnoreEnd
diff --git a/apps/twofactor_backupcodes/l10n/de.js b/apps/twofactor_backupcodes/l10n/de.js
index 96e1fca0759..039b8bea68b 100644
--- a/apps/twofactor_backupcodes/l10n/de.js
+++ b/apps/twofactor_backupcodes/l10n/de.js
@@ -13,7 +13,7 @@ OC.L10N.register(
"Two-factor authentication" : "Zwei-Faktor Authentifizierung",
"You successfully logged in using two-factor authentication (%1$s)" : "Erfolgreich mittels Zwei-Faktorauthentifizierung angemeldet (%1$s)",
"A login attempt using two-factor authentication failed (%1$s)" : "Ein Anmeldeversuch mittels Zwei-Faktorauthentifizierung gescheitert (%1$s)",
- "You created two-factor backup codes for your account" : "Du hast Zwei-Faktor Sicherungs-Codes für Ihr Konto erstellt",
+ "You created two-factor backup codes for your account" : "Du hast Zwei-Faktor Sicherungs-Codes für Dein Konto erstellt",
"Backup code" : "Backup-Code",
"Use backup code" : "Backup-Code verwenden",
"Second-factor backup codes" : "Zweitfaktor-Backup-Codes"
diff --git a/apps/twofactor_backupcodes/l10n/de.json b/apps/twofactor_backupcodes/l10n/de.json
index 6f582fc8bf0..6afdfa52ac4 100644
--- a/apps/twofactor_backupcodes/l10n/de.json
+++ b/apps/twofactor_backupcodes/l10n/de.json
@@ -11,7 +11,7 @@
"Two-factor authentication" : "Zwei-Faktor Authentifizierung",
"You successfully logged in using two-factor authentication (%1$s)" : "Erfolgreich mittels Zwei-Faktorauthentifizierung angemeldet (%1$s)",
"A login attempt using two-factor authentication failed (%1$s)" : "Ein Anmeldeversuch mittels Zwei-Faktorauthentifizierung gescheitert (%1$s)",
- "You created two-factor backup codes for your account" : "Du hast Zwei-Faktor Sicherungs-Codes für Ihr Konto erstellt",
+ "You created two-factor backup codes for your account" : "Du hast Zwei-Faktor Sicherungs-Codes für Dein Konto erstellt",
"Backup code" : "Backup-Code",
"Use backup code" : "Backup-Code verwenden",
"Second-factor backup codes" : "Zweitfaktor-Backup-Codes"
diff --git a/apps/twofactor_backupcodes/lib/Activity/Provider.php b/apps/twofactor_backupcodes/lib/Activity/Provider.php
index cfb16c9f8d3..9c7aaeae630 100644
--- a/apps/twofactor_backupcodes/lib/Activity/Provider.php
+++ b/apps/twofactor_backupcodes/lib/Activity/Provider.php
@@ -40,6 +40,11 @@ class Provider implements IProvider {
/** @var ILogger */
private $logger;
+ /**
+ * @param L10nFactory $l10n
+ * @param IURLGenerator $urlGenerator
+ * @param ILogger $logger
+ */
public function __construct(L10nFactory $l10n, IURLGenerator $urlGenerator, ILogger $logger) {
$this->logger = $logger;
$this->urlGenerator = $urlGenerator;
diff --git a/apps/twofactor_backupcodes/lib/Db/BackupCodeMapper.php b/apps/twofactor_backupcodes/lib/Db/BackupCodeMapper.php
index f64e2e9e60b..85cc174fb6a 100644
--- a/apps/twofactor_backupcodes/lib/Db/BackupCodeMapper.php
+++ b/apps/twofactor_backupcodes/lib/Db/BackupCodeMapper.php
@@ -22,7 +22,6 @@
namespace OCA\TwoFactorBackupCodes\Db;
use OCP\AppFramework\Db\Mapper;
-use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\IDBConnection;
use OCP\IUser;
diff --git a/apps/twofactor_backupcodes/settings/personal.php b/apps/twofactor_backupcodes/settings/personal.php
index 037516e39a3..0a018c0ff28 100644
--- a/apps/twofactor_backupcodes/settings/personal.php
+++ b/apps/twofactor_backupcodes/settings/personal.php
@@ -1,5 +1,6 @@
<?php
-
+// @codeCoverageIgnoreStart
$tmpl = new \OCP\Template('twofactor_backupcodes', 'personal');
return $tmpl->fetchPage();
+// @codeCoverageIgnoreEnd
diff --git a/apps/twofactor_backupcodes/tests/Unit/Activity/GenericProviderTest.php b/apps/twofactor_backupcodes/tests/Unit/Activity/GenericProviderTest.php
new file mode 100644
index 00000000000..242c4ab4e8d
--- /dev/null
+++ b/apps/twofactor_backupcodes/tests/Unit/Activity/GenericProviderTest.php
@@ -0,0 +1,132 @@
+<?php
+
+/**
+ * @author Christoph Wurst <christoph@winzerhof-wurst.at>
+ * @copyright Copyright (c) 2016 Christoph Wurst <christoph@winzerhof-wurst.at>
+ *
+ * Two-factor backup codes
+ *
+ * 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 OCA\TwoFactorBackupCodes\Test\Unit\Activity;
+
+use InvalidArgumentException;
+use OCA\TwoFactorBackupCodes\Activity\GenericProvider;
+use OCP\Activity\IEvent;
+use OCP\IL10N;
+use OCP\ILogger;
+use OCP\IURLGenerator;
+use OCP\L10N\IFactory;
+use PHPUnit_Framework_MockObject_MockObject;
+use Test\TestCase;
+
+class GenericProviderTest extends TestCase {
+
+ /** @var IL10N|PHPUnit_Framework_MockObject_MockObject */
+ private $l10n;
+
+ /** @var IURLGenerator|PHPUnit_Framework_MockObject_MockObject */
+ private $urlGenerator;
+
+ /** @var ILogger|PHPUnit_Framework_MockObject_MockObject */
+ private $logger;
+
+ /** @var GenericProvider */
+ private $provider;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->l10n = $this->createMock(IFactory::class);
+ $this->urlGenerator = $this->createMock(IURLGenerator::class);
+ $this->logger = $this->createMock(ILogger::class);
+
+ $this->provider = new GenericProvider($this->l10n, $this->urlGenerator, $this->logger);
+ }
+
+ public function testParseUnrelated() {
+ $lang = 'ru';
+ $event = $this->createMock(IEvent::class);
+ $event->expects($this->once())
+ ->method('getType')
+ ->willReturn('comments');
+ $this->setExpectedException(InvalidArgumentException::class);
+
+ $this->provider->parse($lang, $event);
+ }
+
+ public function subjectData() {
+ return [
+ ['twofactor_success'],
+ ['twofactor_failed'],
+ ];
+ }
+
+ /**
+ * @dataProvider subjectData
+ */
+ public function testParse($subject) {
+ $lang = 'ru';
+ $event = $this->createMock(IEvent::class);
+ $l = $this->createMock(IL10N::class);
+
+ $event->expects($this->once())
+ ->method('getType')
+ ->willReturn('twofactor');
+ $this->l10n->expects($this->once())
+ ->method('get')
+ ->with('core', $lang)
+ ->willReturn($l);
+ $this->urlGenerator->expects($this->once())
+ ->method('imagePath')
+ ->with('core', 'actions/password.svg')
+ ->willReturn('path/to/image');
+ $this->urlGenerator->expects($this->once())
+ ->method('getAbsoluteURL')
+ ->with('path/to/image')
+ ->willReturn('absolute/path/to/image');
+ $event->expects($this->once())
+ ->method('setIcon')
+ ->with('absolute/path/to/image');
+ $event->expects($this->once())
+ ->method('getSubject')
+ ->willReturn($subject);
+ $event->expects($this->once())
+ ->method('setParsedSubject');
+
+ $this->provider->parse($lang, $event);
+ }
+
+ public function testParseInvalidSubject() {
+ $lang = 'ru';
+ $l = $this->createMock(IL10N::class);
+ $event = $this->createMock(IEvent::class);
+
+ $event->expects($this->once())
+ ->method('getType')
+ ->willReturn('twofactor');
+ $this->l10n->expects($this->once())
+ ->method('get')
+ ->with('core', $lang)
+ ->willReturn($l);
+ $event->expects($this->once())
+ ->method('getSubject')
+ ->willReturn('unrelated');
+
+ $this->expectException(InvalidArgumentException::class);
+ $this->provider->parse($lang, $event);
+ }
+
+}
diff --git a/apps/twofactor_backupcodes/tests/Unit/Activity/ProviderTest.php b/apps/twofactor_backupcodes/tests/Unit/Activity/ProviderTest.php
index 36e85ec1872..e1a13c89c10 100644
--- a/apps/twofactor_backupcodes/tests/Unit/Activity/ProviderTest.php
+++ b/apps/twofactor_backupcodes/tests/Unit/Activity/ProviderTest.php
@@ -2,7 +2,7 @@
/**
* @author Christoph Wurst <christoph@winzerhof-wurst.at>
- * @copyright Copyright (c) 2016 Christoph Wurst <christoph@winzerhof-wurst.at>
+ * @copyright Copyright (c) 2017 Christoph Wurst <christoph@winzerhof-wurst.at>
*
* Two-factor backup codes
*
@@ -23,21 +23,27 @@
namespace OCA\TwoFactorBackupCodes\Test\Unit\Activity;
use InvalidArgumentException;
-use OCA\TwoFactorBackupCodes\Activity\GenericProvider;
+use OCA\TwoFactorBackupCodes\Activity\Provider;
use OCP\Activity\IEvent;
use OCP\IL10N;
use OCP\ILogger;
use OCP\IURLGenerator;
use OCP\L10N\IFactory;
+use PHPUnit_Framework_MockObject_MockObject;
use Test\TestCase;
class ProviderTest extends TestCase {
+ /** @var IL10N|PHPUnit_Framework_MockObject_MockObject */
private $l10n;
+
+ /** @var IURLGenerator|PHPUnit_Framework_MockObject_MockObject */
private $urlGenerator;
+
+ /** @var ILogger|PHPUnit_Framework_MockObject_MockObject */
private $logger;
- /** @var GenericProvider */
+ /** @var Provider */
private $provider;
protected function setUp() {
@@ -47,15 +53,15 @@ class ProviderTest extends TestCase {
$this->urlGenerator = $this->createMock(IURLGenerator::class);
$this->logger = $this->createMock(ILogger::class);
- $this->provider = new GenericProvider($this->l10n, $this->urlGenerator, $this->logger);
+ $this->provider = new Provider($this->l10n, $this->urlGenerator, $this->logger);
}
public function testParseUnrelated() {
$lang = 'ru';
$event = $this->createMock(IEvent::class);
$event->expects($this->once())
- ->method('getType')
- ->will($this->returnValue('comments'));
+ ->method('getApp')
+ ->willReturn('comments');
$this->setExpectedException(InvalidArgumentException::class);
$this->provider->parse($lang, $event);
@@ -63,8 +69,7 @@ class ProviderTest extends TestCase {
public function subjectData() {
return [
- ['twofactor_success'],
- ['twofactor_failed'],
+ ['codes_generated'],
];
}
@@ -77,30 +82,50 @@ class ProviderTest extends TestCase {
$l = $this->createMock(IL10N::class);
$event->expects($this->once())
- ->method('getType')
- ->will($this->returnValue('twofactor'));
+ ->method('getApp')
+ ->willReturn('twofactor_backupcodes');
$this->l10n->expects($this->once())
->method('get')
- ->with('core', $lang)
- ->will($this->returnValue($l));
+ ->with('twofactor_backupcodes', $lang)
+ ->willReturn($l);
$this->urlGenerator->expects($this->once())
->method('imagePath')
->with('core', 'actions/password.svg')
- ->will($this->returnValue('path/to/image'));
+ ->willReturn('path/to/image');
$this->urlGenerator->expects($this->once())
->method('getAbsoluteURL')
->with('path/to/image')
- ->will($this->returnValue('absolute/path/to/image'));
+ ->willReturn('absolute/path/to/image');
$event->expects($this->once())
->method('setIcon')
->with('absolute/path/to/image');
$event->expects($this->once())
->method('getSubject')
- ->will($this->returnValue($subject));
+ ->willReturn($subject);
$event->expects($this->once())
->method('setParsedSubject');
$this->provider->parse($lang, $event);
}
+ public function testParseInvalidSubject() {
+ $lang = 'ru';
+ $l = $this->createMock(IL10N::class);
+ $event = $this->createMock(IEvent::class);
+
+ $event->expects($this->once())
+ ->method('getApp')
+ ->willReturn('twofactor_backupcodes');
+ $this->l10n->expects($this->once())
+ ->method('get')
+ ->with('twofactor_backupcodes', $lang)
+ ->willReturn($l);
+ $event->expects($this->once())
+ ->method('getSubject')
+ ->willReturn('unrelated');
+
+ $this->expectException(InvalidArgumentException::class);
+ $this->provider->parse($lang, $event);
+ }
+
}
diff --git a/config/config.sample.php b/config/config.sample.php
index a9bb0067e5b..534af5a3a31 100644
--- a/config/config.sample.php
+++ b/config/config.sample.php
@@ -74,8 +74,10 @@ $CONFIG = array(
/**
- * Where user files are stored; this defaults to ``data/`` in the Nextcloud
- * directory. The SQLite database is also stored here, when you use SQLite.
+ * Where user files are stored. The SQLite database is also stored here, when
+ * you use SQLite.
+ *
+ * Default to ``data/`` in the Nextcloud directory.
*/
'datadirectory' => '/var/www/nextcloud/data',
@@ -93,7 +95,8 @@ $CONFIG = array(
* - sqlite (SQLite3)
* - mysql (MySQL/MariaDB)
* - pgsql (PostgreSQL)
- * - oci (Oracle)
+ *
+ * Defaults to ``sqlite``
*/
'dbtype' => 'sqlite',
@@ -126,6 +129,8 @@ $CONFIG = array(
/**
* Prefix for the Nextcloud tables in the database.
+ *
+ * Default to ``oc_``
*/
'dbtableprefix' => '',
@@ -134,6 +139,8 @@ $CONFIG = array(
* Indicates whether the Nextcloud instance was installed successfully; ``true``
* indicates a successful installation, and ``false`` indicates an unsuccessful
* installation.
+ *
+ * Defaults to ``false``
*/
'installed' => false,
@@ -151,6 +158,8 @@ $CONFIG = array(
* French. It overrides automatic language detection on public pages like login
* or shared items. User's language preferences configured under "personal ->
* language" override this setting after they have logged in.
+ *
+ * Defaults to ``en``
*/
'default_language' => 'en',
@@ -160,6 +169,8 @@ $CONFIG = array(
* gallery. You can use a comma-separated list of app names, so if the first
* app is not enabled for a user then Nextcloud will try the second one, and so
* on. If no enabled apps are found it defaults to the Files app.
+ *
+ * Defaults to ``files``
*/
'defaultapp' => 'files',
@@ -173,6 +184,8 @@ $CONFIG = array(
* ``true`` enables avatars, or user profile photos. These appear on the User
* page, on user's Personal pages and are used by some apps (contacts, mail,
* etc). ``false`` disables them.
+ *
+ * Defaults to ``true``
*/
'enable_avatars' => true,
@@ -183,21 +196,25 @@ $CONFIG = array(
'allow_user_to_change_display_name' => true,
/**
- * Lifetime of the remember login cookie, which is set when the user clicks the
- * ``remember`` checkbox on the login screen. The default is 15 days, expressed
- * in seconds.
+ * Lifetime of the remember login cookie, which is set when the user clicks
+ * the ``remember`` checkbox on the login screen.
+ *
+ * Defaults to ``60*60*24*15`` seconds (15 days)
*/
'remember_login_cookie_lifetime' => 60*60*24*15,
/**
- * The lifetime of a session after inactivity; the default is 24 hours,
- * expressed in seconds.
+ * The lifetime of a session after inactivity.
+ *
+ * Defaults to ``60*60*24`` seconds (24 hours)
*/
'session_lifetime' => 60 * 60 * 24,
/**
* Enable or disable session keep-alive when a user is logged in to the Web UI.
* Enabling this sends a "heartbeat" to the server to keep it from timing out.
+ *
+ * Defaults to ``true``
*/
'session_keepalive' => true,
@@ -205,6 +222,8 @@ $CONFIG = array(
* Enforce token authentication for clients, which blocks requests using the user
* password for enhanced security. Users need to generate tokens in personal settings
* which can be used as passwords on their clients.
+ *
+ * Defaults to ``false``
*/
'token_auth_enforced' => false,
@@ -212,6 +231,8 @@ $CONFIG = array(
* Whether the bruteforce protection shipped with Nextcloud should be enabled or not.
*
* Disabling this is discouraged for security reasons.
+ *
+ * Defaults to ``true``
*/
'auth.bruteforce.protection.enabled' => true,
@@ -219,6 +240,8 @@ $CONFIG = array(
* The directory where the skeleton files are located. These files will be
* copied to the data directory of new users. Leave empty to not copy any
* skeleton files.
+ *
+ * Defaults to ``core/skeleton`` in the Nextcloud directory.
*/
'skeletondirectory' => '/path/to/nextcloud/core/skeleton',
@@ -259,11 +282,15 @@ $CONFIG = array(
/**
* FROM address that overrides the built-in ``sharing-noreply`` and
* ``lostpassword-noreply`` FROM addresses.
+ *
+ * Defaults to different from addresses depending on the feature.
*/
'mail_from_address' => 'nextcloud',
/**
* Enable SMTP class debugging.
+ *
+ * Defaults to ``false``
*/
'mail_smtpdebug' => false,
@@ -282,6 +309,8 @@ $CONFIG = array(
*
* For ``qmail`` the binary is /var/qmail/bin/sendmail, and it must be installed
* on your Unix system.
+ *
+ * Defaults to ``sendmail``
*/
'mail_smtpmode' => 'sendmail',
@@ -290,11 +319,15 @@ $CONFIG = array(
* server host. This may contain multiple hosts separated by a semi-colon. If
* you need to specify the port number append it to the IP address separated by
* a colon, like this: ``127.0.0.1:24``.
+ *
+ * Defaults to ``127.0.0.1``
*/
'mail_smtphost' => '127.0.0.1',
/**
* This depends on ``mail_smtpmode``. Specify the port for sending mail.
+ *
+ * Defaults to ``25``
*/
'mail_smtpport' => 25,
@@ -302,36 +335,48 @@ $CONFIG = array(
* This depends on ``mail_smtpmode``. This sets the SMTP server timeout, in
* seconds. You may need to increase this if you are running an anti-malware or
* spam scanner.
+ *
+ * Defaults to ``10`` seconds
*/
'mail_smtptimeout' => 10,
/**
* This depends on ``mail_smtpmode``. Specify when you are using ``ssl`` or
* ``tls``, or leave empty for no encryption.
+ *
+ * Defaults to ``''`` (empty string)
*/
'mail_smtpsecure' => '',
/**
* This depends on ``mail_smtpmode``. Change this to ``true`` if your mail
* server requires authentication.
+ *
+ * Defaults to ``false``
*/
'mail_smtpauth' => false,
/**
* This depends on ``mail_smtpmode``. If SMTP authentication is required, choose
- * the authentication type as ``LOGIN`` (default) or ``PLAIN``.
+ * the authentication type as ``LOGIN`` or ``PLAIN``.
+ *
+ * Defaults to ``LOGIN``
*/
'mail_smtpauthtype' => 'LOGIN',
/**
* This depends on ``mail_smtpauth``. Specify the username for authenticating to
* the SMTP server.
+ *
+ * Defaults to ``''`` (empty string)
*/
'mail_smtpname' => '',
/**
* This depends on ``mail_smtpauth``. Specify the password for authenticating to
* the SMTP server.
+ *
+ * Default to ``''`` (empty string)
*/
'mail_smtppassword' => '',
@@ -371,6 +416,8 @@ $CONFIG = array(
* expression for the remote IP address. For example, defining a range of IP
* addresses starting with ``10.0.0.`` and ending with 1 to 3:
* ``^10\.0\.0\.[1-3]$``
+ *
+ * Defaults to ``''`` (empty string)
*/
'overwritecondaddr' => '',
@@ -379,6 +426,8 @@ $CONFIG = array(
* are generated within Nextcloud using any kind of command line tools (cron or
* occ). The value should contain the full base URL:
* ``https://www.example.com/nextcloud``
+ *
+ * Defaults to ``''`` (empty string)
*/
'overwrite.cli.url' => '',
@@ -404,6 +453,8 @@ $CONFIG = array(
*
* - `mod_rewrite` is installed
* - `mod_env` is installed
+ *
+ * Defaults to ``''`` (empty string)
*/
'htaccess.RewriteBase' => '/',
@@ -420,12 +471,16 @@ $CONFIG = array(
/**
* The URL of your proxy server, for example ``proxy.example.com:8081``.
+ *
+ * Defaults to ``''`` (empty string)
*/
'proxy' => '',
/**
* The optional authentication for the proxy to use to connect to the internet.
* The format is: ``username:password``.
+ *
+ * Defaults to ``''`` (empty string)
*/
'proxyuserpwd' => '',
@@ -466,6 +521,8 @@ $CONFIG = array(
* delete when exceeds D2 days
* * ``disabled``
* trash bin auto clean disabled, files and folders will be kept forever
+ *
+ * Defaults to ``auto``
*/
'trashbin_retention_obligation' => 'auto',
@@ -505,6 +562,8 @@ $CONFIG = array(
* keep versions for at least D1 days and delete when exceeds D2 days
* * ``disabled``
* versions auto clean disabled, versions will be kept forever
+ *
+ * Defaults to ``auto``
*/
'versions_retention_obligation' => 'auto',
@@ -519,17 +578,23 @@ $CONFIG = array(
* Checks an app before install whether it uses private APIs instead of the
* proper public APIs. If this is set to true it will only allow to install or
* enable apps that pass this check.
+ *
+ * Defaults to ``false``
*/
'appcodechecker' => true,
/**
* Check if Nextcloud is up-to-date and shows a notification if a new version is
* available.
+ *
+ * Defaults to ``true``
*/
'updatechecker' => true,
/**
* URL that Nextcloud should use to look for updates
+ *
+ * Defaults to ``https://updates.nextcloud.com/updater_server/``
*/
'updater.server.url' => 'https://updates.nextcloud.com/updater_server/',
@@ -538,7 +603,7 @@ $CONFIG = array(
*
* Supported values:
* - ``daily``
- * - ``beta`
+ * - ``beta``
* - ``stable``
* - ``production``
*/
@@ -546,6 +611,8 @@ $CONFIG = array(
/**
* Is Nextcloud connected to the Internet or running in a closed network?
+ *
+ * Defaults to ``true``
*/
'has_internet_connection' => true,
@@ -559,6 +626,8 @@ $CONFIG = array(
* Allows Nextcloud to verify a working .well-known URL redirects. This is done
* by attempting to make a request from JS to
* https://your-domain.com/.well-known/caldav/
+ *
+ * Defaults to ``true``
*/
'check_for_working_wellknown_setup' => true,
@@ -568,6 +637,8 @@ $CONFIG = array(
* If it is not, then any options controlled by ``.htaccess``, such as large
* file uploads, will not work. It also runs checks on the ``data/`` directory,
* which verifies that it can't be accessed directly through the Web server.
+ *
+ * Defaults to ``true``
*/
'check_for_working_htaccess' => true,
@@ -578,6 +649,8 @@ $CONFIG = array(
* all options via the Web interface. Furthermore, when updating Nextcloud
* it is required to make the configuration file writable again for the update
* process.
+ *
+ * Defaults to ``false``
*/
'config_is_read_only' => false,
@@ -591,11 +664,14 @@ $CONFIG = array(
* If syslogging is desired, set this parameter to ``syslog``.
* Setting this parameter to ``errorlog`` will use the PHP error_log function
* for logging.
+ *
+ * Defaults to ``file``
*/
'log_type' => 'file',
/**
* Log file path for the Nextcloud logging type.
+ *
* Defaults to ``[datadirectory]/nextcloud.log``
*/
'logfile' => '/var/log/nextcloud.log',
@@ -603,6 +679,8 @@ $CONFIG = array(
/**
* Loglevel to start logging at. Valid values are: 0 = Debug, 1 = Info, 2 =
* Warning, 3 = Error, and 4 = Fatal. The default value is Warning.
+ *
+ * Defaults to ``2``
*/
'loglevel' => 2,
@@ -637,12 +715,16 @@ $CONFIG = array(
/**
* This uses PHP.date formatting; see http://php.net/manual/en/function.date.php
+ *
+ * Defaults to ``Y-m-d\TH:i:sO`` (ISO8601)
*/
'logdateformat' => 'F d, Y H:i:s',
/**
- * The default timezone for logfiles is UTC. You may change this; see
+ * The timezone for logfiles. You may change this; see
* http://php.net/manual/en/timezones.php
+ *
+ * Defaults to ``UTC``
*/
'logtimezone' => 'Europe/Berlin',
@@ -654,6 +736,8 @@ $CONFIG = array(
/**
* Log successful cron runs.
+ *
+ * Defaults to ``true``
*/
'cron_log' => true,
@@ -663,6 +747,8 @@ $CONFIG = array(
* = 100 * 1024 * 1024 bytes). A new logfile is created with a new name when the
* old logfile reaches your limit. If a rotated log file is already present, it
* will be overwritten.
+ *
+ * Defaults to ``0`` (no rotation)
*/
'log_rotate_size' => false,
@@ -676,6 +762,11 @@ $CONFIG = array(
/**
* This section is for configuring the download links for Nextcloud clients, as
* seen in the first-run wizard and on Personal pages.
+ *
+ * Defaults to
+ * * Desktop client: ``https://nextcloud.com/install/#install-clients``
+ * * Android client: ``https://play.google.com/store/apps/details?id=com.nextcloud.client``
+ * * iOS client : ``https://itunes.apple.com/us/app/nextcloud/id1125420102?mt=8``
*/
'customclient_desktop' =>
'https://nextcloud.com/install/#install-clients',
@@ -692,6 +783,8 @@ $CONFIG = array(
/**
* When enabled, admins may install apps from the Nextcloud app store.
+ *
+ * Defaults to ``true``
*/
'appstoreenabled' => true,
@@ -733,16 +826,22 @@ $CONFIG = array(
*
* Valid values are ``true``, to enable previews, or
* ``false``, to disable previews
+ *
+ * Defaults to ``true``
*/
'enable_previews' => true,
/**
* The maximum width, in pixels, of a preview. A value of ``null`` means there
* is no limit.
+ *
+ * Defaults to ``2048``
*/
'preview_max_x' => 2048,
/**
* The maximum height, in pixels, of a preview. A value of ``null`` means there
* is no limit.
+ *
+ * Defaults to ``2048``
*/
'preview_max_y' => 2048,
/**
@@ -750,26 +849,30 @@ $CONFIG = array(
* preview system generates blurry previews, you might want to consider setting
* a maximum scale factor. By default, pictures are upscaled to 10 times the
* original size. A value of ``1`` or ``null`` disables scaling.
+ *
+ * Defaults to ``2``
*/
'preview_max_scale_factor' => 10,
/**
* max file size for generating image previews with imagegd (default behaviour)
- * If the image is bigger, it'll try other preview generators,
- * but will most likely show the default mimetype icon
+ * If the image is bigger, it'll try other preview generators, but will most
+ * likely show the default mimetype icon. Set to -1 for no limit.
*
- * Value represents the maximum filesize in megabytes
- * Default is 50
- * Set to -1 for no limit
+ * Defaults to ``50`` megabytes
*/
'preview_max_filesize_image' => 50,
/**
* custom path for LibreOffice/OpenOffice binary
+ *
+ * Defaults to ``''`` (empty string)
*/
'preview_libreoffice_path' => '/usr/bin/libreoffice',
/**
* Use this if LibreOffice/OpenOffice requires additional arguments.
+ *
+ * Defaults to ``''`` (empty string)
*/
'preview_office_cl_parameters' =>
' --headless --nologo --nofirststartwizard --invisible --norestore '.
@@ -778,17 +881,6 @@ $CONFIG = array(
/**
* Only register providers that have been explicitly enabled
*
- * The following providers are enabled by default:
- *
- * - OC\Preview\PNG
- * - OC\Preview\JPEG
- * - OC\Preview\GIF
- * - OC\Preview\BMP
- * - OC\Preview\XBitmap
- * - OC\Preview\MarkDown
- * - OC\Preview\MP3
- * - OC\Preview\TXT
- *
* The following providers are disabled by default due to performance or privacy
* concerns:
*
@@ -818,6 +910,17 @@ $CONFIG = array(
* - OC\Preview\MSOffice2007
* - OC\Preview\OpenDocument
* - OC\Preview\StarOffice
+ *
+ * Defaults to the following providers:
+ *
+ * - OC\Preview\BMP
+ * - OC\Preview\GIF
+ * - OC\Preview\JPEG
+ * - OC\Preview\MarkDown
+ * - OC\Preview\MP3
+ * - OC\Preview\PNG
+ * - OC\Preview\TXT
+ * - OC\Preview\XBitmap
*/
'enabledPreviewProviders' => array(
'OC\Preview\PNG',
@@ -841,6 +944,8 @@ $CONFIG = array(
* existence and marks them as ready to be cleaned up. The number is always
* minutes. Setting it to 0 disables the feature.
* See command line (occ) methods ``ldap:show-remnants`` and ``user:delete``
+ *
+ * Defaults to ``51`` minutes
*/
'ldapUserCleanupInterval' => 51,
@@ -854,6 +959,8 @@ $CONFIG = array(
* Replaces the default Comments Manager Factory. This can be utilized if an
* own or 3rdParty CommentsManager should be used that – for instance – uses the
* filesystem instead of the database to keep the comments.
+ *
+ * Defaults to ``\OC\Comments\ManagerFactory``
*/
'comments.managerFactory' => '\OC\Comments\ManagerFactory',
@@ -861,6 +968,8 @@ $CONFIG = array(
* Replaces the default System Tags Manager Factory. This can be utilized if an
* own or 3rdParty SystemTagsManager should be used that – for instance – uses the
* filesystem instead of the database to keep the comments.
+ *
+ * Defaults to ``\OC\SystemTag\ManagerFactory``
*/
'systemtags.managerFactory' => '\OC\SystemTag\ManagerFactory',
@@ -878,12 +987,16 @@ $CONFIG = array(
* doing some maintenance work, you need to set the value of the maintenance
* parameter to true. Please keep in mind that users who are already logged-in
* are kicked out of Nextcloud instantly.
+ *
+ * Defaults to ``false``
*/
'maintenance' => false,
/**
* When set to ``true``, the Nextcloud instance will be unavailable for all
* users who are not in the ``admin`` group.
+ *
+ * Defaults to ``false``
*/
'singleuser' => false,
@@ -894,6 +1007,8 @@ $CONFIG = array(
/**
* Extra SSL options to be used for configuration.
+ *
+ * Defaults to an empty array.
*/
'openssl' => array(
'config' => '/absolute/location/of/openssl.cnf',
@@ -927,6 +1042,8 @@ $CONFIG = array(
* Memory caching backend for locally stored data
*
* * Used for host-specific data, e.g. file paths
+ *
+ * Defaults to ``none``
*/
'memcache.local' => '\OC\Memcache\APCu',
@@ -935,6 +1052,8 @@ $CONFIG = array(
*
* * Used for installation-specific data, e.g. database caching
* * If unset, defaults to the value of memcache.local
+ *
+ * Defaults to ``none``
*/
'memcache.distributed' => '\OC\Memcache\Memcached',
@@ -994,6 +1113,8 @@ $CONFIG = array(
* ``$user`` is the current user. When specified, the format will change to
* ``$cache_path/$user`` where ``$cache_path`` is the configured cache directory
* and ``$user`` is the user.
+ *
+ * Defaults to ``''`` (empty string)
*/
'cache_path' => '',
@@ -1002,8 +1123,10 @@ $CONFIG = array(
* garbage collection (in seconds). Increase this value if users have
* issues uploading very large files via the Nextcloud Client as upload isn't
* completed within one day.
+ *
+ * Defaults to ``60*60*24`` (1 day)
*/
-'cache_chunk_gc_ttl' => 86400, // 60*60*24 = 1 day
+'cache_chunk_gc_ttl' => 60*60*24,
/**
* Using Object Store with Nextcloud
@@ -1065,6 +1188,8 @@ $CONFIG = array(
* Replaces the default Share Provider Factory. This can be utilized if
* own or 3rdParty Share Providers be used that – for instance – uses the
* filesystem instead of the database to keep the share information.
+ *
+ * Defaults to ``\OC\Share20\ProviderFactory``
*/
'sharing.managerFactory' => '\OC\Share20\ProviderFactory',
@@ -1125,6 +1250,11 @@ $CONFIG = array(
* - mysql (MySQL)
* - pgsql (PostgreSQL)
* - oci (Oracle)
+ *
+ * Defaults to the following databases:
+ * - sqlite (SQLite3)
+ * - mysql (MySQL)
+ * - pgsql (PostgreSQL)
*/
'supportedDatabases' => array(
'sqlite',
@@ -1153,17 +1283,23 @@ $CONFIG = array(
* Blacklist a specific file or files and disallow the upload of files
* with this name. ``.htaccess`` is blocked by default.
* WARNING: USE THIS ONLY IF YOU KNOW WHAT YOU ARE DOING.
+ *
+ * Defaults to ``array('.htaccess')``
*/
'blacklisted_files' => array('.htaccess'),
/**
* Define a default folder for shared files and folders other than root.
+ *
+ * Defaults to ``/``
*/
'share_folder' => '/',
/**
* If you are applying a theme to Nextcloud, enter the name of the theme here.
* The default location for themes is ``nextcloud/themes/``.
+ *
+ * Defaults to the theming app which is shipped since Nextcloud 9
*/
'theme' => '',
@@ -1182,12 +1318,16 @@ $CONFIG = array(
* When changing this, note that older unsupported versions of the Nextcloud desktop
* client may not function as expected, and could lead to permanent data loss for
* clients or other unexpected results.
+ *
+ * Defaults to ``2.0.0``
*/
'minimum.supported.desktop.version' => '2.0.0',
/**
* EXPERIMENTAL: option whether to include external storage in quota
* calculation, defaults to false.
+ *
+ * Defaults to ``false``
*/
'quota_include_external_storage' => false,
@@ -1202,6 +1342,8 @@ $CONFIG = array(
*
* 1 -> Check each file or folder at most once per request, recommended for
* general use if outside changes might happen.
+ *
+ * Defaults to ``0``
*/
'filesystem_check_changes' => 0,
@@ -1210,18 +1352,24 @@ $CONFIG = array(
* same storage as the upload target. Setting this to false will store the part
* files in the root of the users folder which might be required to work with certain
* external storage setups that have limited rename capabilities.
+ *
+ * Defaults to ``true``
*/
'part_file_in_storage' => true,
/**
* Where ``mount.json`` file should be stored, defaults to ``data/mount.json``
* in the Nextcloud directory.
+ *
+ * Defaults to ``data/mount.json`` in the Nextcloud directory.
*/
'mount_file' => '/var/www/nextcloud/data/mount.json',
/**
* When ``true``, prevent Nextcloud from changing the cache due to changes in
* the filesystem for all storage.
+ *
+ * Defaults to ``false``
*/
'filesystem_cache_readonly' => false,
@@ -1236,6 +1384,7 @@ $CONFIG = array(
*
* If you configure these also consider setting `forwarded_for_headers` which
* otherwise defaults to `HTTP_X_FORWARDED_FOR` (the `X-Forwarded-For` header).
+ * Defaults to an empty array.
*/
'trusted_proxies' => array('203.0.113.45', '198.51.100.128'),
@@ -1247,7 +1396,7 @@ $CONFIG = array(
* If set incorrectly, a client can spoof their IP address as visible to
* Nextcloud, bypassing access controls and making logs useless!
*
- * Defaults to 'HTTP_X_FORWARED_FOR' if unset
+ * Defaults to ``'HTTP_X_FORWARED_FOR'``
*/
'forwarded_for_headers' => array('HTTP_X_FORWARDED', 'HTTP_FORWARDED_FOR'),
@@ -1255,8 +1404,10 @@ $CONFIG = array(
* max file size for animating gifs on public-sharing-site.
* If the gif is bigger, it'll show a static preview
*
- * Value represents the maximum filesize in megabytes. Default is ``10``. Set to
- * ``-1`` for no limit.
+ * Value represents the maximum filesize in megabytes. Set to ``-1`` for
+ * no limit.
+ *
+ * Defaults to ``10`` megabytes
*/
'max_filesize_animated_gifs_public_sharing' => 10,
@@ -1270,6 +1421,8 @@ $CONFIG = array(
* be caused by concurrent operations. Mainly relevant for
* very large installations with many users working with
* shared files.
+ *
+ * Defaults to ``true``
*/
'filelocking.enabled' => true,
@@ -1278,15 +1431,18 @@ $CONFIG = array(
*
* Any lock older than this will be automatically cleaned up.
*
- * If not set this defaults to either 1 hour or the php max_execution_time, whichever is higher.
+ * Defaults to ``60*60`` seconds (1 hour) or the php
+ * max_execution_time, whichever is higher.
*/
-'filelocking.ttl' => 3600,
+'filelocking.ttl' => 60*60,
/**
* Memory caching backend for file locking
*
* Because most memcache backends can clean values without warning using redis
* is highly recommended to *avoid data loss*.
+ *
+ * Defaults to ``none``
*/
'memcache.locking' => '\\OC\\Memcache\\Redis',
@@ -1300,6 +1456,8 @@ $CONFIG = array(
*
* Only enable this for local development and not in production environments
* This will disable the minifier and outputs some additional debug information
+ *
+ * Defaults to ``false``
*/
'debug' => false,
@@ -1313,6 +1471,8 @@ $CONFIG = array(
*
* Updating/Deleting this value can make connected clients stall until
* the user has resolved conflicts.
+ *
+ * Defaults to ``''`` (empty string)
*/
'data-fingerprint' => '',
diff --git a/core/Controller/LoginController.php b/core/Controller/LoginController.php
index 3c81ed5242a..187c818b9e1 100644
--- a/core/Controller/LoginController.php
+++ b/core/Controller/LoginController.php
@@ -205,8 +205,8 @@ class LoginController extends Controller {
* @return RedirectResponse
*/
public function tryLogin($user, $password, $redirect_url, $remember_login = false, $timezone = '', $timezone_offset = '') {
- $currentDelay = $this->throttler->getDelay($this->request->getRemoteAddress());
- $this->throttler->sleepDelay($this->request->getRemoteAddress());
+ $currentDelay = $this->throttler->getDelay($this->request->getRemoteAddress(), 'login');
+ $this->throttler->sleepDelay($this->request->getRemoteAddress(), 'login');
// If the user is already logged in and the CSRF check does not pass then
// simply redirect the user to the correct page as required. This is the
@@ -230,7 +230,7 @@ class LoginController extends Controller {
if ($loginResult === false) {
$this->throttler->registerAttempt('login', $this->request->getRemoteAddress(), ['user' => $originalUser]);
if($currentDelay === 0) {
- $this->throttler->sleepDelay($this->request->getRemoteAddress());
+ $this->throttler->sleepDelay($this->request->getRemoteAddress(), 'login');
}
$this->session->set('loginMessages', [
['invalidpassword'], []
@@ -295,15 +295,15 @@ class LoginController extends Controller {
* @return DataResponse
*/
public function confirmPassword($password) {
- $currentDelay = $this->throttler->getDelay($this->request->getRemoteAddress());
- $this->throttler->sleepDelay($this->request->getRemoteAddress());
+ $currentDelay = $this->throttler->getDelay($this->request->getRemoteAddress(), 'sudo');
+ $this->throttler->sleepDelay($this->request->getRemoteAddress(), 'sudo');
$loginName = $this->userSession->getLoginName();
$loginResult = $this->userManager->checkPassword($loginName, $password);
if ($loginResult === false) {
$this->throttler->registerAttempt('sudo', $this->request->getRemoteAddress(), ['user' => $loginName]);
if ($currentDelay === 0) {
- $this->throttler->sleepDelay($this->request->getRemoteAddress());
+ $this->throttler->sleepDelay($this->request->getRemoteAddress(), 'sudo');
}
return new DataResponse([], Http::STATUS_FORBIDDEN);
diff --git a/core/Controller/LostController.php b/core/Controller/LostController.php
index a0ef87e50d8..8a8a50343ed 100644
--- a/core/Controller/LostController.php
+++ b/core/Controller/LostController.php
@@ -202,6 +202,7 @@ class LostController extends Controller {
/**
* @PublicPage
+ * @BruteForceProtection passwordResetEmail
*
* @param string $user
* @return array
diff --git a/core/Controller/OCSController.php b/core/Controller/OCSController.php
index c59b0d7ad3f..dc9775f2603 100644
--- a/core/Controller/OCSController.php
+++ b/core/Controller/OCSController.php
@@ -128,7 +128,7 @@ class OCSController extends \OCP\AppFramework\OCSController {
*/
public function personCheck($login = '', $password = '') {
if ($login !== '' && $password !== '') {
- $this->throttler->sleepDelay($this->request->getRemoteAddress());
+ $this->throttler->sleepDelay($this->request->getRemoteAddress(), 'login');
if ($this->userManager->checkPassword($login, $password)) {
return new DataResponse([
'person' => [
diff --git a/core/css/apps.scss b/core/css/apps.scss
index 88fd223ba64..93e60fbfbf1 100644
--- a/core/css/apps.scss
+++ b/core/css/apps.scss
@@ -1,4 +1,5 @@
/**
+ * @copyright Copyright (c) 2014, Jan-Christoph Borchardt (http://jancborchardt.net)
* @copyright Copyright (c) 2017, John Molakvoæ (skjnldsv@protonmail.com)
*
* @license GNU AGPL version 3 or any later version
@@ -101,6 +102,7 @@ em {
.active,
.active a {
opacity: 1;
+ box-shadow: inset 2px 0 #0082c9;
}
.collapse {
display: none;
@@ -284,7 +286,7 @@ em {
.app-navigation-entry-utils ul, .app-navigation-entry-menu ul {
list-style-type: none;
}
-
+
/* editing an entry */
.app-navigation-entry-edit {
padding-left: 5px;
diff --git a/core/css/header.scss b/core/css/header.scss
index 8035f7e568a..886c2489a63 100644
--- a/core/css/header.scss
+++ b/core/css/header.scss
@@ -1,13 +1,23 @@
-/* prevent ugly selection effect on accidental selection */
+/**
+ * @copyright Copyright (c) 2017, John Molakvoæ (skjnldsv@protonmail.com)
+ * @copyright Copyright (c) 2016, Lukas Reschke (lukas@statuscode.ch)
+ * @copyright Copyright (c) 2016, Julius Härtl (jus@bitgrid.net)
+ * @copyright Copyright (c) 2016, Jos Poortvliet (jospoortvliet@gmail.com)
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ */
-#header, #navigation, #expanddiv {
+/* prevent ugly selection effect on accidental selection */
+#header,
+#navigation,
+#expanddiv {
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
}
/* removed until content-focusing issue is fixed */
-
#skip-to-content a {
position: absolute;
left: -10000px;
@@ -25,8 +35,9 @@
}
/* HEADERS ------------------------------------------------------------------ */
-
-#body-user #header, #body-settings #header, #body-public #header {
+#body-user #header,
+#body-settings #header,
+#body-public #header {
position: fixed;
top: 0;
left: 0;
@@ -39,7 +50,6 @@
}
/* LOGO and APP NAME -------------------------------------------------------- */
-
#nextcloud {
position: absolute;
top: 0;
@@ -88,6 +98,16 @@
padding-top: 18px;
padding-right: 10px;
}
+ /* show caret indicator next to logo to make clear it is tappable */
+ .icon-caret {
+ display: inline-block;
+ width: 12px;
+ height: 12px;
+ margin: 0;
+ margin-top: -21px;
+ padding: 0;
+ vertical-align: middle;
+ }
}
/* hover effect for app switcher label */
@@ -123,7 +143,6 @@
}
/* show appname next to logo */
-
.header-appname {
display: inline-block;
position: relative;
@@ -136,26 +155,14 @@
vertical-align: middle;
}
-/* show caret indicator next to logo to make clear it is tappable */
-#header .icon-caret {
- display: inline-block;
- width: 12px;
- height: 12px;
- margin: 0;
- margin-top: -21px;
- padding: 0;
- vertical-align: middle;
-}
/* do not show menu toggle on public share links as there is no menu */
-
#body-public #header .icon-caret {
display: none;
}
/* NAVIGATION --------------------------------------------------------------- */
-
#navigation {
position: fixed;
top: 45px;
@@ -170,6 +177,7 @@
border-top-left-radius: 0;
border-top-right-radius: 0;
display: none;
+ box-sizing: border-box;
/*overflow-y: auto;
overflow-x: hidden;*/
z-index: 2000;
@@ -185,37 +193,8 @@
border-bottom-color: rgba(255, 255, 255, 0.97);
border-width: 10px;
margin-left: -10px;
+ left: 47%;
}
-}
-
-/* arrow look */
-
-#expanddiv:after {
- bottom: 100%;
- border: solid transparent;
- content: ' ';
- height: 0;
- width: 0;
- position: absolute;
- pointer-events: none;
- border-color: rgba(0, 0, 0, 0);
- border-bottom-color: rgba(255, 255, 255, 0.97);
- border-width: 10px;
- margin-left: -10px;
-}
-
-/* position of dropdown arrow */
-
-#navigation:after {
- left: 47%;
-}
-
-#expanddiv:after {
- right: 15px;
-}
-
-#navigation {
- box-sizing: border-box;
* {
box-sizing: border-box;
}
@@ -241,11 +220,15 @@
overflow: hidden;
text-overflow: ellipsis;
}
- svg, span {
+ svg,
+ span {
-ms-filter: 'progid:DXImageTransform.Microsoft.Alpha(Opacity=50)';
opacity: .5;
}
- &:hover svg, &:focus svg, &:hover span, &:focus span {
+ &:hover svg,
+ &:focus svg,
+ &:hover span,
+ &:focus span {
-ms-filter: 'progid:DXImageTransform.Microsoft.Alpha(Opacity=100)';
opacity: 1;
}
@@ -256,52 +239,50 @@
}
}
}
-}
-
-/* icon opacity and hover effect */
-
-#apps-management a {
- &:hover svg, &:focus svg, &.active svg, &:hover span, &:focus span, &.active span {
- -ms-filter: 'progid:DXImageTransform.Microsoft.Alpha(Opacity=100)';
- opacity: 1;
+ .app-icon {
+ margin: 0 auto;
+ padding: 0;
+ max-height: 32px;
+ max-width: 32px;
+ }
+ /* loading feedback for apps */
+ .app-loading {
+ .icon-loading-dark {
+ display: inline !important;
+ position: absolute;
+ top: 20px;
+ left: 24px;
+ width: 32px;
+ height: 32px;
+ }
+ .app-icon {
+ -ms-filter: 'progid:DXImageTransform.Microsoft.Alpha(Opacity=0)';
+ opacity: 0;
+ }
}
-}
-
-#navigation .app-icon {
- margin: 0 auto;
- padding: 0;
- max-height: 32px;
- max-width: 32px;
}
/* Apps management */
-
#apps-management {
min-height: initial;
height: initial;
margin: 0;
a {
- svg, span {
+ svg,
+ span {
-ms-filter: 'progid:DXImageTransform.Microsoft.Alpha(Opacity=30)';
opacity: .3;
}
- }
-}
-
-/* loading feedback for apps */
-
-#navigation .app-loading {
- .icon-loading-dark {
- display: inline !important;
- position: absolute;
- top: 20px;
- left: 24px;
- width: 32px;
- height: 32px;
- }
- .app-icon {
- -ms-filter: 'progid:DXImageTransform.Microsoft.Alpha(Opacity=0)';
- opacity: 0;
+ /* icon opacity and hover effect */
+ &:hover svg,
+ &:focus svg,
+ &.active svg,
+ &:hover span,
+ &:focus span,
+ &.active span {
+ -ms-filter: 'progid:DXImageTransform.Microsoft.Alpha(Opacity=100)';
+ opacity: 1;
+ }
}
}
@@ -313,7 +294,6 @@
/* USER MENU -----------------------------------------------------------------*/
/* info part on the right, used e.g. for info on who shared something */
-
.header-right {
position: absolute;
right: 0;
@@ -325,21 +305,6 @@
box-sizing: border-box;
}
-/* Profile picture in header */
-
-#header .avatardiv {
- float: left;
- display: inline-block;
- margin-right: 8px;
- cursor: pointer;
- height: 32px;
- width: 32px;
- img {
- opacity: 1;
- cursor: pointer;
- }
-}
-
#settings {
float: right;
color: #ddd;
@@ -352,6 +317,7 @@
}
}
+/* User menu on the right */
#expand {
display: block;
padding: 7px 30px 6px 10px;
@@ -359,21 +325,41 @@
* {
cursor: pointer;
}
- &:hover, &:focus, &:active {
- color: #fff;
- }
img {
-ms-filter: 'progid:DXImageTransform.Microsoft.Alpha(Opacity=70)';
opacity: .7;
margin-bottom: -2px;
}
- &:hover img, &:focus img, &:active img {
- -ms-filter: 'progid:DXImageTransform.Microsoft.Alpha(Opacity=100)';
- opacity: 1;
+ &:hover,
+ &:focus,
+ &:active {
+ color: #fff;
+ img {
+ -ms-filter: 'progid:DXImageTransform.Microsoft.Alpha(Opacity=100)';
+ opacity: 1;
+ }
}
.icon-caret {
margin-top: 0;
}
+
+ /* Profile picture in header */
+ .avatardiv {
+ float: left;
+ display: inline-block;
+ margin-right: 8px;
+ cursor: pointer;
+ height: 32px;
+ width: 32px;
+ img {
+ opacity: 1;
+ cursor: pointer;
+ }
+ /* do not show display name when profile picture is present */
+ &.avatardiv-shown + #expandDisplayName {
+ display: none;
+ }
+ }
}
#expanddiv {
@@ -389,8 +375,19 @@
border-top-right-radius: 0;
box-sizing: border-box;
&:after {
+ /* position of dropdown arrow */
+ right: 15px;
border-color: rgba(0, 0, 0, 0);
border-bottom-color: rgba(255, 255, 255, 1);
+ bottom: 100%;
+ border: solid transparent;
+ content: ' ';
+ height: 0;
+ width: 0;
+ position: absolute;
+ pointer-events: none;
+ border-width: 10px;
+ margin-left: -10px;
}
a {
display: block;
@@ -404,20 +401,12 @@
margin-bottom: -3px;
margin-right: 6px;
}
- &:hover, &:focus, &:active, &.active {
+ &:hover,
+ &:focus,
+ &:active,
+ &.active {
-ms-filter: 'progid:DXImageTransform.Microsoft.Alpha(Opacity=100)';
opacity: 1;
}
}
}
-
-/* do not show display name when profile picture is present */
-
-#header {
- .avatardiv.avatardiv-shown + #expandDisplayName {
- display: none;
- }
- #expand {
- display: block;
- }
-}
diff --git a/lib/private/AppFramework/DependencyInjection/DIContainer.php b/lib/private/AppFramework/DependencyInjection/DIContainer.php
index ac42960f54d..57499f3ffe8 100644
--- a/lib/private/AppFramework/DependencyInjection/DIContainer.php
+++ b/lib/private/AppFramework/DependencyInjection/DIContainer.php
@@ -43,8 +43,10 @@ use OC\AppFramework\Middleware\OCSMiddleware;
use OC\AppFramework\Middleware\Security\SecurityMiddleware;
use OC\AppFramework\Middleware\SessionMiddleware;
use OC\AppFramework\Utility\SimpleContainer;
+use OC\AppFramework\Utility\TimeFactory;
use OC\Core\Middleware\TwoFactorMiddleware;
use OC\RichObjectStrings\Validator;
+use OC\Security\Bruteforce\Throttler;
use OCP\AppFramework\IApi;
use OCP\AppFramework\IAppContainer;
use OCP\Files\IAppData;
@@ -376,20 +378,25 @@ class DIContainer extends SimpleContainer implements IAppContainer {
*/
$app = $this;
$this->registerService('SecurityMiddleware', function($c) use ($app){
+ /** @var \OC\Server $server */
+ $server = $app->getServer();
+
return new SecurityMiddleware(
$c['Request'],
$c['ControllerMethodReflector'],
- $app->getServer()->getNavigationManager(),
- $app->getServer()->getURLGenerator(),
- $app->getServer()->getLogger(),
- $app->getServer()->getSession(),
+ $server->getNavigationManager(),
+ $server->getURLGenerator(),
+ $server->getLogger(),
+ $server->getSession(),
$c['AppName'],
$app->isLoggedIn(),
$app->isAdminUser(),
- $app->getServer()->getContentSecurityPolicyManager(),
- $app->getServer()->getCsrfTokenManager(),
- $app->getServer()->getContentSecurityPolicyNonceManager()
+ $server->getContentSecurityPolicyManager(),
+ $server->getCsrfTokenManager(),
+ $server->getContentSecurityPolicyNonceManager(),
+ $server->getBruteForceThrottler()
);
+
});
$this->registerService('CORSMiddleware', function($c) {
diff --git a/lib/private/AppFramework/Middleware/Security/SecurityMiddleware.php b/lib/private/AppFramework/Middleware/Security/SecurityMiddleware.php
index d60d5749d57..edba6a3e759 100644
--- a/lib/private/AppFramework/Middleware/Security/SecurityMiddleware.php
+++ b/lib/private/AppFramework/Middleware/Security/SecurityMiddleware.php
@@ -36,6 +36,7 @@ use OC\AppFramework\Middleware\Security\Exceptions\NotConfirmedException;
use OC\AppFramework\Middleware\Security\Exceptions\NotLoggedInException;
use OC\AppFramework\Middleware\Security\Exceptions\StrictCookieMissingException;
use OC\AppFramework\Utility\ControllerMethodReflector;
+use OC\Security\Bruteforce\Throttler;
use OC\Security\CSP\ContentSecurityPolicyManager;
use OC\Security\CSP\ContentSecurityPolicyNonceManager;
use OC\Security\CSRF\CsrfTokenManager;
@@ -87,6 +88,8 @@ class SecurityMiddleware extends Middleware {
private $csrfTokenManager;
/** @var ContentSecurityPolicyNonceManager */
private $cspNonceManager;
+ /** @var Throttler */
+ private $throttler;
/**
* @param IRequest $request
@@ -101,6 +104,7 @@ class SecurityMiddleware extends Middleware {
* @param ContentSecurityPolicyManager $contentSecurityPolicyManager
* @param CSRFTokenManager $csrfTokenManager
* @param ContentSecurityPolicyNonceManager $cspNonceManager
+ * @param Throttler $throttler
*/
public function __construct(IRequest $request,
ControllerMethodReflector $reflector,
@@ -113,7 +117,8 @@ class SecurityMiddleware extends Middleware {
$isAdminUser,
ContentSecurityPolicyManager $contentSecurityPolicyManager,
CsrfTokenManager $csrfTokenManager,
- ContentSecurityPolicyNonceManager $cspNonceManager) {
+ ContentSecurityPolicyNonceManager $cspNonceManager,
+ Throttler $throttler) {
$this->navigationManager = $navigationManager;
$this->request = $request;
$this->reflector = $reflector;
@@ -126,6 +131,7 @@ class SecurityMiddleware extends Middleware {
$this->contentSecurityPolicyManager = $contentSecurityPolicyManager;
$this->csrfTokenManager = $csrfTokenManager;
$this->cspNonceManager = $cspNonceManager;
+ $this->throttler = $throttler;
}
@@ -185,6 +191,12 @@ class SecurityMiddleware extends Middleware {
}
}
+ if($this->reflector->hasAnnotation('BruteForceProtection')) {
+ $action = $this->reflector->getAnnotationParameter('BruteForceProtection');
+ $this->throttler->sleepDelay($this->request->getRemoteAddress(), $action);
+ $this->throttler->registerAttempt($action, $this->request->getRemoteAddress());
+ }
+
/**
* FIXME: Use DI once available
* Checks if app is enabled (also includes a check whether user is allowed to access the resource)
diff --git a/lib/private/AppFramework/Utility/ControllerMethodReflector.php b/lib/private/AppFramework/Utility/ControllerMethodReflector.php
index 33a117d8121..034fc3a1759 100644
--- a/lib/private/AppFramework/Utility/ControllerMethodReflector.php
+++ b/lib/private/AppFramework/Utility/ControllerMethodReflector.php
@@ -55,8 +55,10 @@ class ControllerMethodReflector implements IControllerMethodReflector{
$docs = $reflection->getDocComment();
// extract everything prefixed by @ and first letter uppercase
- preg_match_all('/@([A-Z]\w+)/', $docs, $matches);
- $this->annotations = $matches[1];
+ preg_match_all('/^\h+\*\h+@(?P<annotation>[A-Z]\w+)(\h+(?P<parameter>\w+))?$/m', $docs, $matches);
+ foreach($matches['annotation'] as $key => $annontation) {
+ $this->annotations[$annontation] = $matches['parameter'][$key];
+ }
// extract type parameter information
preg_match_all('/@param\h+(?P<type>\w+)\h+\$(?P<var>\w+)/', $docs, $matches);
@@ -112,7 +114,22 @@ class ControllerMethodReflector implements IControllerMethodReflector{
* @return bool true if the annotation is found
*/
public function hasAnnotation($name){
- return in_array($name, $this->annotations);
+ return array_key_exists($name, $this->annotations);
+ }
+
+
+ /**
+ * Get optional annotation parameter
+ * @param string $name the name of the annotation
+ * @return string
+ */
+ public function getAnnotationParameter($name){
+ $parameter = '';
+ if($this->hasAnnotation($name)) {
+ $parameter = $this->annotations[$name];
+ }
+
+ return $parameter;
}
diff --git a/lib/private/Security/Bruteforce/Throttler.php b/lib/private/Security/Bruteforce/Throttler.php
index 031c5ffd411..765f109fdb3 100644
--- a/lib/private/Security/Bruteforce/Throttler.php
+++ b/lib/private/Security/Bruteforce/Throttler.php
@@ -189,9 +189,10 @@ class Throttler {
* Get the throttling delay (in milliseconds)
*
* @param string $ip
+ * @param string $action optionally filter by action
* @return int
*/
- public function getDelay($ip) {
+ public function getDelay($ip, $action = '') {
$cutoffTime = (new \DateTime())
->sub($this->getCutoff(43200))
->getTimestamp();
@@ -201,6 +202,11 @@ class Throttler {
->from('bruteforce_attempts')
->where($qb->expr()->gt('occurred', $qb->createNamedParameter($cutoffTime)))
->andWhere($qb->expr()->eq('subnet', $qb->createNamedParameter($this->getSubnet($ip))));
+
+ if ($action !== '') {
+ $qb->andWhere($qb->expr()->eq('action', $qb->createNamedParameter($action)));
+ }
+
$attempts = count($qb->execute()->fetchAll());
if ($attempts === 0) {
@@ -225,10 +231,11 @@ class Throttler {
* Will sleep for the defined amount of time
*
* @param string $ip
+ * @param string $action optionally filter by action
* @return int the time spent sleeping
*/
- public function sleepDelay($ip) {
- $delay = $this->getDelay($ip);
+ public function sleepDelay($ip, $action = '') {
+ $delay = $this->getDelay($ip, $action);
usleep($delay * 1000);
return $delay;
}
diff --git a/lib/private/User/Session.php b/lib/private/User/Session.php
index 1834bd025d1..9cc42e671a8 100644
--- a/lib/private/User/Session.php
+++ b/lib/private/User/Session.php
@@ -317,7 +317,7 @@ class Session implements IUserSession, Emitter {
$password,
IRequest $request,
OC\Security\Bruteforce\Throttler $throttler) {
- $currentDelay = $throttler->sleepDelay($request->getRemoteAddress());
+ $currentDelay = $throttler->sleepDelay($request->getRemoteAddress(), 'login');
$isTokenPassword = $this->isTokenPassword($password);
if (!$isTokenPassword && $this->isTokenAuthEnforced()) {
@@ -334,7 +334,7 @@ class Session implements IUserSession, Emitter {
$throttler->registerAttempt('login', $request->getRemoteAddress(), ['uid' => $user]);
if($currentDelay === 0) {
- $throttler->sleepDelay($request->getRemoteAddress());
+ $throttler->sleepDelay($request->getRemoteAddress(), 'login');
}
return false;
}
@@ -768,7 +768,7 @@ class Session implements IUserSession, Emitter {
try {
$this->tokenProvider->invalidateToken($this->session->getId());
} catch (SessionNotAvailableException $ex) {
-
+
}
}
$this->setUser(null);
diff --git a/tests/lib/AppFramework/Middleware/Security/SecurityMiddlewareTest.php b/tests/lib/AppFramework/Middleware/Security/SecurityMiddlewareTest.php
index 5a988751070..164ea48de70 100644
--- a/tests/lib/AppFramework/Middleware/Security/SecurityMiddlewareTest.php
+++ b/tests/lib/AppFramework/Middleware/Security/SecurityMiddlewareTest.php
@@ -34,6 +34,7 @@ use OC\AppFramework\Middleware\Security\Exceptions\SecurityException;
use OC\Appframework\Middleware\Security\Exceptions\StrictCookieMissingException;
use OC\AppFramework\Middleware\Security\SecurityMiddleware;
use OC\AppFramework\Utility\ControllerMethodReflector;
+use OC\Security\Bruteforce\Throttler;
use OC\Security\CSP\ContentSecurityPolicy;
use OC\Security\CSP\ContentSecurityPolicyManager;
use OC\Security\CSP\ContentSecurityPolicyNonceManager;
@@ -82,6 +83,8 @@ class SecurityMiddlewareTest extends \Test\TestCase {
private $csrfTokenManager;
/** @var ContentSecurityPolicyNonceManager|\PHPUnit_Framework_MockObject_MockObject */
private $cspNonceManager;
+ /** @var Throttler|\PHPUnit_Framework_MockObject_MockObject */
+ private $bruteForceThrottler;
protected function setUp() {
parent::setUp();
@@ -96,6 +99,7 @@ class SecurityMiddlewareTest extends \Test\TestCase {
$this->contentSecurityPolicyManager = $this->createMock(ContentSecurityPolicyManager::class);
$this->csrfTokenManager = $this->createMock(CsrfTokenManager::class);
$this->cspNonceManager = $this->createMock(ContentSecurityPolicyNonceManager::class);
+ $this->bruteForceThrottler = $this->getMockBuilder(Throttler::class)->disableOriginalConstructor()->getMock();
$this->middleware = $this->getMiddleware(true, true);
$this->secException = new SecurityException('hey', false);
$this->secAjaxException = new SecurityException('hey', true);
@@ -119,7 +123,8 @@ class SecurityMiddlewareTest extends \Test\TestCase {
$isAdminUser,
$this->contentSecurityPolicyManager,
$this->csrfTokenManager,
- $this->cspNonceManager
+ $this->cspNonceManager,
+ $this->bruteForceThrottler
);
}
@@ -652,4 +657,70 @@ class SecurityMiddlewareTest extends \Test\TestCase {
$this->assertEquals($response, $this->middleware->afterController($this->controller, 'test', $response));
}
+
+ /**
+ * @dataProvider dataTestBeforeControllerBruteForce
+ */
+ public function testBeforeControllerBruteForce($bruteForceProtectionEnabled) {
+ /** @var ControllerMethodReflector|\PHPUnit_Framework_MockObject_MockObject $reader */
+ $reader = $this->getMockBuilder(ControllerMethodReflector::class)->disableOriginalConstructor()->getMock();
+
+ $middleware = new SecurityMiddleware(
+ $this->request,
+ $reader,
+ $this->navigationManager,
+ $this->urlGenerator,
+ $this->logger,
+ $this->session,
+ 'files',
+ false,
+ false,
+ $this->contentSecurityPolicyManager,
+ $this->csrfTokenManager,
+ $this->cspNonceManager,
+ $this->bruteForceThrottler
+ );
+
+ $reader->expects($this->any())->method('hasAnnotation')
+ ->willReturnCallback(
+ function($annotation) use ($bruteForceProtectionEnabled) {
+
+ switch ($annotation) {
+ case 'BruteForceProtection':
+ return $bruteForceProtectionEnabled;
+ case 'PasswordConfirmationRequired':
+ case 'StrictCookieRequired':
+ return false;
+ case 'PublicPage':
+ case 'NoCSRFRequired':
+ return true;
+ }
+
+ return true;
+ }
+ );
+
+ $reader->expects($this->any())->method('getAnnotationParameter')->willReturn('action');
+ $this->request->expects($this->any())->method('getRemoteAddress')->willReturn('remoteAddress');
+
+ if ($bruteForceProtectionEnabled) {
+ $this->bruteForceThrottler->expects($this->once())->method('sleepDelay')
+ ->with('remoteAddress', 'action');
+ $this->bruteForceThrottler->expects($this->once())->method('registerAttempt')
+ ->with('action', 'remoteAddress');
+ } else {
+ $this->bruteForceThrottler->expects($this->never())->method('sleepDelay');
+ $this->bruteForceThrottler->expects($this->never())->method('registerAttempt');
+ }
+
+ $middleware->beforeController($this->controller, 'test');
+
+ }
+
+ public function dataTestBeforeControllerBruteForce() {
+ return [
+ [true],
+ [false]
+ ];
+ }
}
diff --git a/tests/lib/AppFramework/Utility/ControllerMethodReflectorTest.php b/tests/lib/AppFramework/Utility/ControllerMethodReflectorTest.php
index 92d767e9987..644245e1967 100644
--- a/tests/lib/AppFramework/Utility/ControllerMethodReflectorTest.php
+++ b/tests/lib/AppFramework/Utility/ControllerMethodReflectorTest.php
@@ -77,6 +77,19 @@ class ControllerMethodReflectorTest extends \Test\TestCase {
/**
+ * @Annotation parameter
+ */
+ public function testGetAnnotationParameter(){
+ $reader = new ControllerMethodReflector();
+ $reader->reflect(
+ '\Test\AppFramework\Utility\ControllerMethodReflectorTest',
+ 'testGetAnnotationParameter'
+ );
+
+ $this->assertSame('parameter', $reader->getAnnotationParameter('Annotation'));
+ }
+
+ /**
* @Annotation
* @param test
*/