Skip to content

Commit

Permalink
feat(security): Add public API to allow validating IP Ranges and chec…
Browse files Browse the repository at this point in the history
…king for "in range"

Signed-off-by: Joas Schilling <coding@schilljs.com>
  • Loading branch information
nickvergessen committed Jul 17, 2024
1 parent d1007db commit 791b066
Show file tree
Hide file tree
Showing 5 changed files with 64 additions and 11 deletions.
1 change: 1 addition & 0 deletions lib/composer/composer/autoload_classmap.php
Original file line number Diff line number Diff line change
Expand Up @@ -641,6 +641,7 @@
'OCP\\Security\\ICrypto' => $baseDir . '/lib/public/Security/ICrypto.php',
'OCP\\Security\\IHasher' => $baseDir . '/lib/public/Security/IHasher.php',
'OCP\\Security\\IRemoteHostValidator' => $baseDir . '/lib/public/Security/IRemoteHostValidator.php',
'OCP\\Security\\IRemoteIpAddress' => $baseDir . '/lib/public/Security/IRemoteIpAddress.php',
'OCP\\Security\\ISecureRandom' => $baseDir . '/lib/public/Security/ISecureRandom.php',
'OCP\\Security\\ITrustedDomainHelper' => $baseDir . '/lib/public/Security/ITrustedDomainHelper.php',
'OCP\\Security\\RateLimiting\\ILimiter' => $baseDir . '/lib/public/Security/RateLimiting/ILimiter.php',
Expand Down
1 change: 1 addition & 0 deletions lib/composer/composer/autoload_static.php
Original file line number Diff line number Diff line change
Expand Up @@ -674,6 +674,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
'OCP\\Security\\ICrypto' => __DIR__ . '/../../..' . '/lib/public/Security/ICrypto.php',
'OCP\\Security\\IHasher' => __DIR__ . '/../../..' . '/lib/public/Security/IHasher.php',
'OCP\\Security\\IRemoteHostValidator' => __DIR__ . '/../../..' . '/lib/public/Security/IRemoteHostValidator.php',
'OCP\\Security\\IRemoteIpAddress' => __DIR__ . '/../../..' . '/lib/public/Security/IRemoteIpAddress.php',
'OCP\\Security\\ISecureRandom' => __DIR__ . '/../../..' . '/lib/public/Security/ISecureRandom.php',
'OCP\\Security\\ITrustedDomainHelper' => __DIR__ . '/../../..' . '/lib/public/Security/ITrustedDomainHelper.php',
'OCP\\Security\\RateLimiting\\ILimiter' => __DIR__ . '/../../..' . '/lib/public/Security/RateLimiting/ILimiter.php',
Expand Down
32 changes: 22 additions & 10 deletions lib/private/Security/RemoteIpAddress.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,10 @@
use IPLib\Factory;
use OCP\IConfig;
use OCP\IRequest;
use OCP\Security\IRemoteIpAddress;
use Psr\Log\LoggerInterface;

class RemoteIpAddress {
class RemoteIpAddress implements IRemoteIpAddress {
public const SETTING_NAME = 'allowed_admin_ranges';

public function __construct(
Expand All @@ -38,22 +39,33 @@ public function allowsAdminActions(): bool {
if (empty($allowedAdminRanges)) {
return true;
}
return $this->isRemoteAddressInAnyRange($this->request->getRemoteAddress(), $allowedAdminRanges);
}

$ipAddress = Factory::parseAddressString($this->request->getRemoteAddress());
if ($ipAddress === null) {
$this->logger->warning(
'Unable to parse remote IP "{ip}"',
['ip' => $ipAddress,]
);
public function isValidRangeString(string $rangeString): bool {
return Factory::parseRangeString($rangeString) !== null;
}

return false;
/**
* @param string[] $rangeStrings
* @throws \InvalidArgumentException When the remote address is not a valid IP, or any of the ranges is invalid
*/
public function isRemoteAddressInAnyRange(string $remoteAddress, array $rangeStrings): bool {
$ipAddress = Factory::parseAddressString($remoteAddress);
if ($ipAddress === null) {
throw new \InvalidArgumentException('Given remote address can not be parsed', 1);
}

foreach ($allowedAdminRanges as $rangeString) {
foreach ($rangeStrings as $rangeString) {
if (!is_string($rangeString)) {
throw new \InvalidArgumentException('One of the rangeStrings is not a string', 2);
}

$range = Factory::parseRangeString($rangeString);
if ($range === null) {
continue;
throw new \InvalidArgumentException('Range "' . $rangeString . '" can not be parsed', 3);
}

if ($range->contains($ipAddress)) {
return true;
}
Expand Down
5 changes: 4 additions & 1 deletion lib/private/Server.php
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,7 @@
use OCP\Security\ICredentialsManager;
use OCP\Security\ICrypto;
use OCP\Security\IHasher;
use OCP\Security\IRemoteIpAddress;
use OCP\Security\ISecureRandom;
use OCP\Security\ITrustedDomainHelper;
use OCP\Security\RateLimiting\ILimiter;
Expand Down Expand Up @@ -466,7 +467,7 @@ public function __construct($webRoot, \OC\Config $config) {
$this->get(IEventDispatcher::class),
$this->get(LoggerInterface::class),
$this->get(ICacheFactory::class),
$this->get(RemoteIpAddress::class),
$this->get(IRemoteIpAddress::class),
);
return $groupManager;
});
Expand Down Expand Up @@ -1405,6 +1406,8 @@ public function __construct($webRoot, \OC\Config $config) {

$this->registerAlias(\OCP\TaskProcessing\IManager::class, \OC\TaskProcessing\Manager::class);

$this->registerAlias(IRemoteIpAddress::class, RemoteIpAddress::class);

$this->connectDispatcher();
}

Expand Down
36 changes: 36 additions & 0 deletions lib/public/Security/IRemoteIpAddress.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?php

declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

namespace OCP\Security;

/**
* @since 30.0.0
*/
interface IRemoteIpAddress {

/**
* Check if the current remote address is allowed to perform admin actions
* @since 30.0.0
*/
public function allowsAdminActions(): bool;

/**
* Check if a given range is valid
* @since 30.0.0
*/
public function isValidRangeString(string $rangeString): bool;

/**
* Check if a given remote address is in a list of ranges
*
* @param string[] $rangeStrings
* @throws \InvalidArgumentException When the remote address is not a valid IP, or any of the ranges is invalid
* @since 30.0.0
*/
public function isRemoteAddressInAnyRange(string $remoteAddress, array $rangeStrings): bool;
}

0 comments on commit 791b066

Please sign in to comment.