Skip to content

Commit fa5ae66

Browse files
authored
Merge pull request #62906 from nextcloud/backport/62884/stable32
[stable32] feat: Add setting to bypass password confirmation on a selected ip ranges
2 parents b06673e + d5e3d30 commit fa5ae66

5 files changed

Lines changed: 58 additions & 0 deletions

File tree

config/config.sample.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2477,6 +2477,20 @@
24772477
*/
24782478
'allowed_admin_ranges' => ['192.0.2.42/32', '233.252.0.0/24', '2001:db8::13:37/64'],
24792479

2480+
2481+
/**
2482+
* List of trusted IP ranges that can bypass password confirmation.
2483+
* If non-empty, all endpoints marked with the PasswordConfirmationRequired attribute
2484+
* won't need a password confirmation when originating from IPs within these ranges.
2485+
*
2486+
* Supported formats:
2487+
* - IPv4 addresses or ranges, e.g., ``192.0.2.42/32``, ``233.252.0.0/24``
2488+
* - IPv6 addresses or ranges, e.g., ``2001:db8::13:37/64``
2489+
*
2490+
* Defaults to ``[]`` (empty array)
2491+
*/
2492+
'allowed_no_password_confirmation_ranges' => ['192.0.2.42/32', '233.252.0.0/24', '2001:db8::13:37/64'],
2493+
24802494
/**
24812495
* Maximum file size (in megabytes) for animating GIFs on public sharing pages.
24822496
* 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
@@ -21,6 +21,7 @@
2121
use OCP\IRequest;
2222
use OCP\ISession;
2323
use OCP\IUserSession;
24+
use OCP\Security\Ip\IRemoteAddress;
2425
use OCP\Session\Exceptions\SessionNotAvailableException;
2526
use OCP\User\Backend\IPasswordConfirmationBackend;
2627
use Psr\Log\LoggerInterface;
@@ -38,6 +39,7 @@ public function __construct(
3839
private readonly LoggerInterface $logger,
3940
private readonly IRequest $request,
4041
private readonly Manager $userManager,
42+
private readonly IRemoteAddress $remoteAddress,
4143
) {
4244
}
4345

@@ -71,6 +73,10 @@ public function beforeController(Controller $controller, string $methodName) {
7173
return;
7274
}
7375
} catch (SessionNotAvailableException|InvalidTokenException|WipeTokenException|ExpiredTokenException) {
76+
if ($this->remoteAddress->allowsBypassPasswordConfirmation()) {
77+
return;
78+
}
79+
7480
// No scope to test
7581
}
7682

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

@@ -65,6 +66,32 @@ public function allowsAdminActions(): bool {
6566
return false;
6667
}
6768

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

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,6 +18,8 @@
1818
use OCP\ISession;
1919
use OCP\IUser;
2020
use OCP\IUserSession;
21+
use OCP\Security\Ip\IRemoteAddress;
22+
use PHPUnit\Framework\MockObject\MockObject;
2123
use Psr\Log\LoggerInterface;
2224
use Test\AppFramework\Middleware\Security\Mock\PasswordConfirmationMiddlewareController;
2325
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
protected function setUp(): void {
4851
$this->reflector = new ControllerMethodReflector(\OCP\Server::get(LoggerInterface::class));
@@ -58,6 +61,7 @@ protected function setUp(): void {
5861
'test',
5962
$this->createMock(IRequest::class)
6063
);
64+
$this->remoteAddress = $this->createMock(IRemoteAddress::class);
6165

6266
$this->middleware = new PasswordConfirmationMiddleware(
6367
$this->reflector,
@@ -68,6 +72,7 @@ protected function setUp(): void {
6872
$this->logger,
6973
$this->request,
7074
$this->userManager,
75+
$this->remoteAddress,
7176
);
7277
}
7378

0 commit comments

Comments
 (0)