Skip to content

Commit 7a98f4d

Browse files
authored
Merge pull request #62884 from nextcloud/carl/bypass-password-confirmation
feat: Add setting to bypass password confirmation on a selected ip ranges
2 parents 380ccf3 + 779b45f commit 7a98f4d

5 files changed

Lines changed: 57 additions & 0 deletions

File tree

config/config.sample.php

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2702,6 +2702,19 @@
27022702
*/
27032703
'allowed_admin_ranges' => ['192.0.2.42/32', '233.252.0.0/24', '2001:db8::13:37/64'],
27042704

2705+
/**
2706+
* List of trusted IP ranges that can bypass password confirmation.
2707+
* If non-empty, all endpoints marked with the PasswordConfirmationRequired attribute
2708+
* won't need a password confirmation when originating from IPs within these ranges.
2709+
*
2710+
* Supported formats:
2711+
* - IPv4 addresses or ranges, e.g., ``192.0.2.42/32``, ``233.252.0.0/24``
2712+
* - IPv6 addresses or ranges, e.g., ``2001:db8::13:37/64``
2713+
*
2714+
* Defaults to ``[]`` (empty array)
2715+
*/
2716+
'allowed_no_password_confirmation_ranges' => ['192.0.2.42/32', '233.252.0.0/24', '2001:db8::13:37/64'],
2717+
27052718
/**
27062719
* Maximum file size (in megabytes) for animating GIFs on public sharing pages.
27072720
* If a GIF exceeds this size, a static preview is shown.

lib/private/AppFramework/Middleware/Security/PasswordConfirmationMiddleware.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
use OCP\IRequest;
2323
use OCP\ISession;
2424
use OCP\IUserSession;
25+
use OCP\Security\Ip\IRemoteAddress;
2526
use OCP\Session\Exceptions\SessionNotAvailableException;
2627
use OCP\User\Backend\IPasswordConfirmationBackend;
2728
use ReflectionAttribute;
@@ -38,6 +39,7 @@ public function __construct(
3839
private IProvider $tokenProvider,
3940
private readonly IRequest $request,
4041
private readonly Manager $userManager,
42+
private readonly IRemoteAddress $remoteAddress,
4143
) {
4244
}
4345

@@ -72,6 +74,10 @@ public function beforeController(Controller $controller, string $methodName): vo
7274
return;
7375
}
7476
} catch (SessionNotAvailableException|InvalidTokenException|WipeTokenException|ExpiredTokenException) {
77+
if ($this->remoteAddress->allowsBypassPasswordConfirmation()) {
78+
return;
79+
}
80+
7581
// No scope to test
7682
}
7783

lib/private/Security/Ip/RemoteAddress.php

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
class RemoteAddress implements IRemoteAddress, IAddress {
1919
public const SETTING_NAME = 'allowed_admin_ranges';
20+
public const SETTING_PASSWORD_CONFIRMATION_NAME = 'allowed_no_password_confirmation_ranges';
2021

2122
private readonly ?IAddress $ip;
2223

@@ -68,6 +69,32 @@ public function allowsAdminActions(): bool {
6869
return false;
6970
}
7071

72+
#[\Override]
73+
public function allowsBypassPasswordConfirmation(): bool {
74+
if ($this->ip === null) {
75+
return false;
76+
}
77+
78+
$allowedAdminRanges = $this->config->getSystemValue(self::SETTING_PASSWORD_CONFIRMATION_NAME, false);
79+
80+
// Apply restrictions on empty or invalid configuration
81+
if (
82+
$allowedAdminRanges === false
83+
|| !is_array($allowedAdminRanges)
84+
|| empty($allowedAdminRanges)
85+
) {
86+
return false;
87+
}
88+
89+
foreach ($allowedAdminRanges as $allowedAdminRange) {
90+
if ((new Range($allowedAdminRange))->contains($this->ip)) {
91+
return true;
92+
}
93+
}
94+
95+
return false;
96+
}
97+
7198
public function __toString(): string {
7299
return (string)$this->ip;
73100
}

lib/public/Security/Ip/IRemoteAddress.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,10 @@ interface IRemoteAddress {
1919
* @since 30.0.0
2020
*/
2121
public function allowsAdminActions(): bool;
22+
23+
/**
24+
* Check if the current remote address is allowed to bypass the password confirmation.
25+
* @since 35.0.0
26+
*/
27+
public function allowsBypassPasswordConfirmation(): bool;
2228
}

tests/lib/AppFramework/Middleware/Security/PasswordConfirmationMiddlewareTest.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,9 @@
1818
use OCP\ISession;
1919
use OCP\IUser;
2020
use OCP\IUserSession;
21+
use OCP\Security\Ip\IRemoteAddress;
2122
use OCP\Server;
23+
use PHPUnit\Framework\MockObject\MockObject;
2224
use Psr\Log\LoggerInterface;
2325
use Test\AppFramework\Middleware\Security\Mock\PasswordConfirmationMiddlewareController;
2426
use Test\TestCase;
@@ -43,6 +45,7 @@ class PasswordConfirmationMiddlewareTest extends TestCase {
4345
private IRequest $request;
4446
/** @var Manager&\PHPUnit\Framework\MockObject\MockObject */
4547
private Manager $userManager;
48+
private IRemoteAddress&MockObject $remoteAddress;
4649

4750
#[\Override]
4851
protected function setUp(): void {
@@ -60,6 +63,7 @@ protected function setUp(): void {
6063
'test',
6164
$this->createMock(IRequest::class)
6265
);
66+
$this->remoteAddress = $this->createMock(IRemoteAddress::class);
6367

6468
$this->middleware = new PasswordConfirmationMiddleware(
6569
$this->reflector,
@@ -69,6 +73,7 @@ protected function setUp(): void {
6973
$this->tokenProvider,
7074
$this->request,
7175
$this->userManager,
76+
$this->remoteAddress,
7277
);
7378
}
7479

0 commit comments

Comments
 (0)