Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions config/config.sample.php
Original file line number Diff line number Diff line change
Expand Up @@ -2697,6 +2697,19 @@
*/
'allowed_admin_ranges' => ['192.0.2.42/32', '233.252.0.0/24', '2001:db8::13:37/64'],

/**
* List of trusted IP ranges that can bypass password confirmation.
* If non-empty, all endpoints marked with the PasswordConfirmationRequired attribute
* won't need a password confirmation when originating from IPs within these ranges.
*
* Supported formats:
* - IPv4 addresses or ranges, e.g., ``192.0.2.42/32``, ``233.252.0.0/24``
* - IPv6 addresses or ranges, e.g., ``2001:db8::13:37/64``
*
* Defaults to ``[]`` (empty array)
*/
'allowed_no_password_confirmation_ranges' => ['192.0.2.42/32', '233.252.0.0/24', '2001:db8::13:37/64'],

/**
* Maximum file size (in megabytes) for animating GIFs on public sharing pages.
* If a GIF exceeds this size, a static preview is shown.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
use OCP\IRequest;
use OCP\ISession;
use OCP\IUserSession;
use OCP\Security\Ip\IRemoteAddress;
use OCP\Session\Exceptions\SessionNotAvailableException;
use OCP\User\Backend\IPasswordConfirmationBackend;
use Psr\Log\LoggerInterface;
Expand All @@ -39,6 +40,7 @@ public function __construct(
private readonly LoggerInterface $logger,
private readonly IRequest $request,
private readonly Manager $userManager,
private readonly IRemoteAddress $remoteAddress,
) {
}

Expand Down Expand Up @@ -73,6 +75,10 @@ public function beforeController(Controller $controller, string $methodName) {
return;
}
} catch (SessionNotAvailableException|InvalidTokenException|WipeTokenException|ExpiredTokenException) {
if ($this->remoteAddress->allowsBypassPasswordConfirmation()) {
return;
}

// No scope to test
}

Expand Down
27 changes: 27 additions & 0 deletions lib/private/Security/Ip/RemoteAddress.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

class RemoteAddress implements IRemoteAddress, IAddress {
public const SETTING_NAME = 'allowed_admin_ranges';
public const SETTING_PASSWORD_CONFIRMATION_NAME = 'allowed_no_password_confirmation_ranges';

private readonly ?IAddress $ip;

Expand Down Expand Up @@ -68,6 +69,32 @@ public function allowsAdminActions(): bool {
return false;
}

#[\Override]
public function allowsBypassPasswordConfirmation(): bool {
if ($this->ip === null) {
return false;
}

$allowedAdminRanges = $this->config->getSystemValue(self::SETTING_PASSWORD_CONFIRMATION_NAME, false);

// Apply restrictions on empty or invalid configuration
if (
$allowedAdminRanges === false
|| !is_array($allowedAdminRanges)
|| empty($allowedAdminRanges)
) {
return false;
}

foreach ($allowedAdminRanges as $allowedAdminRange) {
if ((new Range($allowedAdminRange))->contains($this->ip)) {
return true;
}
}

return false;
}

public function __toString(): string {
return (string)$this->ip;
}
Expand Down
6 changes: 6 additions & 0 deletions lib/public/Security/Ip/IRemoteAddress.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,10 @@ interface IRemoteAddress {
* @since 30.0.0
*/
public function allowsAdminActions(): bool;

/**
* Check if the current remote address is allowed to bypass the password confirmation.
* @since 35.0.0
*/
public function allowsBypassPasswordConfirmation(): bool;
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@
use OCP\ISession;
use OCP\IUser;
use OCP\IUserSession;
use OCP\Security\Ip\IRemoteAddress;
use OCP\Server;
use PHPUnit\Framework\MockObject\MockObject;
use Psr\Log\LoggerInterface;
use Test\AppFramework\Middleware\Security\Mock\PasswordConfirmationMiddlewareController;
use Test\TestCase;
Expand All @@ -44,6 +46,7 @@ class PasswordConfirmationMiddlewareTest extends TestCase {
private IRequest $request;
/** @var Manager&\PHPUnit\Framework\MockObject\MockObject */
private Manager $userManager;
private IRemoteAddress&MockObject $remoteAddress;

#[\Override]
protected function setUp(): void {
Expand All @@ -60,6 +63,7 @@ protected function setUp(): void {
'test',
$this->createMock(IRequest::class)
);
$this->remoteAddress = $this->createMock(IRemoteAddress::class);

$this->middleware = new PasswordConfirmationMiddleware(
$this->reflector,
Expand All @@ -70,6 +74,7 @@ protected function setUp(): void {
$this->logger,
$this->request,
$this->userManager,
$this->remoteAddress,
);
}

Expand Down
Loading