diff options
-rw-r--r-- | apps/files/js/filelist.js | 7 | ||||
-rw-r--r-- | apps/files_external/lib/Lib/Storage/AmazonS3.php | 2 | ||||
-rw-r--r-- | core/templates/layout.user.php | 5 | ||||
-rw-r--r-- | lib/composer/composer/autoload_classmap.php | 1 | ||||
-rw-r--r-- | lib/composer/composer/autoload_static.php | 1 | ||||
-rw-r--r-- | lib/private/Security/Bruteforce/Throttler.php | 8 | ||||
-rw-r--r-- | lib/private/Server.php | 2 | ||||
-rw-r--r-- | lib/public/Security/Bruteforce/IThrottler.php | 126 |
8 files changed, 141 insertions, 11 deletions
diff --git a/apps/files/js/filelist.js b/apps/files/js/filelist.js index b02520a0768..4acefa6902b 100644 --- a/apps/files/js/filelist.js +++ b/apps/files/js/filelist.js @@ -1793,8 +1793,11 @@ td.append(linkElem); tr.append(td); - var enabledThemes = window.OCA?.Theming?.enabledThemes || [] - var isDarkTheme = enabledThemes.join('').indexOf('dark') !== -1 + const enabledThemes = window.OCA?.Theming?.enabledThemes || [] + // Check enabled themes, if system default is selected check the browser + const isDarkTheme = (enabledThemes.length === 0 || enabledThemes[0] === 'default') + ? window.matchMedia('(prefers-color-scheme: dark)').matches + : enabledThemes.join('').indexOf('dark') !== -1 try { var maxContrastHex = window.getComputedStyle(document.documentElement) diff --git a/apps/files_external/lib/Lib/Storage/AmazonS3.php b/apps/files_external/lib/Lib/Storage/AmazonS3.php index 6ebbed33a87..d5b8eff6fe5 100644 --- a/apps/files_external/lib/Lib/Storage/AmazonS3.php +++ b/apps/files_external/lib/Lib/Storage/AmazonS3.php @@ -744,7 +744,7 @@ class AmazonS3 extends \OC\Files\Storage\Common { return $result->get('Status') === 'Enabled'; } catch (S3Exception $s3Exception) { // This is needed for compatibility with Storj gateway which does not support versioning yet - if ($s3Exception->getAwsErrorCode() === 'NotImplemented') { + if ($s3Exception->getAwsErrorCode() === 'NotImplemented' || $s3Exception->getAwsErrorCode() === 'AccessDenied') { return false; } throw $s3Exception; diff --git a/core/templates/layout.user.php b/core/templates/layout.user.php index 6475845321d..c7eb2fde5ad 100644 --- a/core/templates/layout.user.php +++ b/core/templates/layout.user.php @@ -144,7 +144,7 @@ $getUserAvatar = static function (int $size) use ($_): string { </div> <div id="settings"> <div id="expand" tabindex="0" role="button" class="menutoggle" - aria-label="<?php p($l->t('Settings'));?>" + aria-label="<?php p($l->t('Open settings menu'));?>" aria-haspopup="true" aria-controls="expanddiv" aria-expanded="false"> <div id="avatardiv-menu" class="avatardiv<?php if ($_['userAvatarSet']) { print_unescaped(' avatardiv-shown'); @@ -167,8 +167,7 @@ $getUserAvatar = static function (int $size) use ($_): string { <?php } ?> </div> </div> - <nav class="settings-menu" id="expanddiv" style="display:none;" - aria-label="<?php p($l->t('Settings menu'));?>"> + <nav class="settings-menu" id="expanddiv" style="display:none;"> <ul> <?php foreach ($_['settingsnavigation'] as $entry):?> <li data-id="<?php p($entry['id']); ?>"> diff --git a/lib/composer/composer/autoload_classmap.php b/lib/composer/composer/autoload_classmap.php index 420bc89c735..8853c1f17f4 100644 --- a/lib/composer/composer/autoload_classmap.php +++ b/lib/composer/composer/autoload_classmap.php @@ -510,6 +510,7 @@ return array( 'OCP\\Search\\Result' => $baseDir . '/lib/public/Search/Result.php', 'OCP\\Search\\SearchResult' => $baseDir . '/lib/public/Search/SearchResult.php', 'OCP\\Search\\SearchResultEntry' => $baseDir . '/lib/public/Search/SearchResultEntry.php', + 'OCP\\Security\\Bruteforce\\IThrottler' => $baseDir . '/lib/public/Security/Bruteforce/IThrottler.php', 'OCP\\Security\\Bruteforce\\MaxDelayReached' => $baseDir . '/lib/public/Security/Bruteforce/MaxDelayReached.php', 'OCP\\Security\\CSP\\AddContentSecurityPolicyEvent' => $baseDir . '/lib/public/Security/CSP/AddContentSecurityPolicyEvent.php', 'OCP\\Security\\Events\\GenerateSecurePasswordEvent' => $baseDir . '/lib/public/Security/Events/GenerateSecurePasswordEvent.php', diff --git a/lib/composer/composer/autoload_static.php b/lib/composer/composer/autoload_static.php index 9a371b45743..5617430958d 100644 --- a/lib/composer/composer/autoload_static.php +++ b/lib/composer/composer/autoload_static.php @@ -543,6 +543,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2 'OCP\\Search\\Result' => __DIR__ . '/../../..' . '/lib/public/Search/Result.php', 'OCP\\Search\\SearchResult' => __DIR__ . '/../../..' . '/lib/public/Search/SearchResult.php', 'OCP\\Search\\SearchResultEntry' => __DIR__ . '/../../..' . '/lib/public/Search/SearchResultEntry.php', + 'OCP\\Security\\Bruteforce\\IThrottler' => __DIR__ . '/../../..' . '/lib/public/Security/Bruteforce/IThrottler.php', 'OCP\\Security\\Bruteforce\\MaxDelayReached' => __DIR__ . '/../../..' . '/lib/public/Security/Bruteforce/MaxDelayReached.php', 'OCP\\Security\\CSP\\AddContentSecurityPolicyEvent' => __DIR__ . '/../../..' . '/lib/public/Security/CSP/AddContentSecurityPolicyEvent.php', 'OCP\\Security\\Events\\GenerateSecurePasswordEvent' => __DIR__ . '/../../..' . '/lib/public/Security/Events/GenerateSecurePasswordEvent.php', diff --git a/lib/private/Security/Bruteforce/Throttler.php b/lib/private/Security/Bruteforce/Throttler.php index e37746eb6a2..299cab93eb3 100644 --- a/lib/private/Security/Bruteforce/Throttler.php +++ b/lib/private/Security/Bruteforce/Throttler.php @@ -36,6 +36,7 @@ use OC\Security\Normalizer\IpAddress; use OCP\AppFramework\Utility\ITimeFactory; use OCP\IConfig; use OCP\IDBConnection; +use OCP\Security\Bruteforce\IThrottler; use OCP\Security\Bruteforce\MaxDelayReached; use Psr\Log\LoggerInterface; @@ -52,11 +53,8 @@ use Psr\Log\LoggerInterface; * * @package OC\Security\Bruteforce */ -class Throttler { +class Throttler implements IThrottler { public const LOGIN_ACTION = 'login'; - public const MAX_DELAY = 25; - public const MAX_DELAY_MS = 25000; // in milliseconds - public const MAX_ATTEMPTS = 10; /** @var IDBConnection */ private $db; @@ -311,7 +309,7 @@ class Throttler { * * @param string $ip */ - public function resetDelayForIP($ip) { + public function resetDelayForIP(string $ip): void { $cutoffTime = $this->getCutoffTimestamp(); $qb = $this->db->getQueryBuilder(); diff --git a/lib/private/Server.php b/lib/private/Server.php index bcbb6ef6b00..842f72fa1d0 100644 --- a/lib/private/Server.php +++ b/lib/private/Server.php @@ -231,6 +231,7 @@ use OCP\Remote\Api\IApiFactory; use OCP\Remote\IInstanceFactory; use OCP\RichObjectStrings\IValidator; use OCP\Route\IRouter; +use OCP\Security\Bruteforce\IThrottler; use OCP\Security\IContentSecurityPolicyManager; use OCP\Security\ICredentialsManager; use OCP\Security\ICrypto; @@ -1002,6 +1003,7 @@ class Server extends ServerContainer implements IServerContainer { $this->registerAlias(ITrustedDomainHelper::class, TrustedDomainHelper::class); /** @deprecated 19.0.0 */ $this->registerDeprecatedAlias('Throttler', Throttler::class); + $this->registerAlias(IThrottler::class, Throttler::class); $this->registerService('IntegrityCodeChecker', function (ContainerInterface $c) { // IConfig and IAppManager requires a working database. This code // might however be called when ownCloud is not yet setup. diff --git a/lib/public/Security/Bruteforce/IThrottler.php b/lib/public/Security/Bruteforce/IThrottler.php new file mode 100644 index 00000000000..6f492d6c59d --- /dev/null +++ b/lib/public/Security/Bruteforce/IThrottler.php @@ -0,0 +1,126 @@ +<?php + +declare(strict_types=1); +/** + * @copyright Copyright (c) 2022 Joas Schilling <coding@schilljs.com> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +namespace OCP\Security\Bruteforce; + +/** + * Class Throttler implements the bruteforce protection for security actions in + * Nextcloud. + * + * It is working by logging invalid login attempts to the database and slowing + * down all login attempts from the same subnet. The max delay is 30 seconds and + * the starting delay are 200 milliseconds. (after the first failed login) + * + * This is based on Paragonie's AirBrake for Airship CMS. You can find the original + * code at https://github.com/paragonie/airship/blob/7e5bad7e3c0fbbf324c11f963fd1f80e59762606/src/Engine/Security/AirBrake.php + * + * @package OC\Security\Bruteforce + * @since 25.0.0 + */ +interface IThrottler { + /** + * @since 25.0.0 + */ + public const MAX_DELAY = 25; + + /** + * @since 25.0.0 + */ + public const MAX_DELAY_MS = 25000; // in milliseconds + + /** + * @since 25.0.0 + */ + public const MAX_ATTEMPTS = 10; + + /** + * Register a failed attempt to bruteforce a security control + * + * @param string $action + * @param string $ip + * @param array $metadata Optional metadata logged to the database + * @since 25.0.0 + */ + public function registerAttempt(string $action, string $ip, array $metadata = []): void; + + /** + * Get the throttling delay (in milliseconds) + * + * @param string $ip + * @param string $action optionally filter by action + * @param float $maxAgeHours + * @return int + * @since 25.0.0 + */ + public function getAttempts(string $ip, string $action = '', float $maxAgeHours = 12): int; + + /** + * Get the throttling delay (in milliseconds) + * + * @param string $ip + * @param string $action optionally filter by action + * @return int + * @since 25.0.0 + */ + public function getDelay(string $ip, string $action = ''): int; + + /** + * Reset the throttling delay for an IP address, action and metadata + * + * @param string $ip + * @param string $action + * @param array $metadata + * @since 25.0.0 + */ + public function resetDelay(string $ip, string $action, array $metadata): void; + + /** + * Reset the throttling delay for an IP address + * + * @param string $ip + * @since 25.0.0 + */ + public function resetDelayForIP(string $ip): void; + + /** + * Will sleep for the defined amount of time + * + * @param string $ip + * @param string $action optionally filter by action + * @return int the time spent sleeping + * @since 25.0.0 + */ + public function sleepDelay(string $ip, string $action = ''): int; + + /** + * Will sleep for the defined amount of time unless maximum was reached in the last 30 minutes + * In this case a "429 Too Many Request" exception is thrown + * + * @param string $ip + * @param string $action optionally filter by action + * @return int the time spent sleeping + * @throws MaxDelayReached when reached the maximum + * @since 25.0.0 + */ + public function sleepDelayOrThrowOnMax(string $ip, string $action = ''): int; +} |