Skip to content

feat(metrics): Add reset metrics feature #137

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Apr 28, 2025
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
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,18 @@ As far as possible, we try to adhere to [Symfony guidelines](https://symfony.com

---

## [4.3.0](https://github.com/crowdsecurity/php-cs-bouncer/releases/tag/v4.3.0) - 2025-05-??
[_Compare with previous release_](https://github.com/crowdsecurity/php-cs-bouncer/compare/v4.2.0...HEAD)

__This release is not published yet__

### Added

- Add `hasBlaasUri` to detect if the bouncer is connected to a Block As A Service Lapi
- Add `resetUsageMetrics` to reset the usage metrics cache item

---

## [4.2.0](https://github.com/crowdsecurity/php-cs-bouncer/releases/tag/v4.2.0) - 2025-01-31
[_Compare with previous release_](https://github.com/crowdsecurity/php-cs-bouncer/compare/v4.1.0...v4.2.0)

Expand Down
2 changes: 1 addition & 1 deletion docs/USER_GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -299,7 +299,7 @@ Below is the list of available settings:


- `captcha_cache_duration`: Set the duration we keep in cache the captcha flow variables for an IP. In seconds.
Defaults to 86400.. In seconds. Defaults to 20.
Defaults to 86400.


### Geolocation
Expand Down
24 changes: 24 additions & 0 deletions src/AbstractBouncer.php
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,16 @@ abstract public function getRequestUri(): string;
*/
abstract public function getRequestUserAgent(): string;

/**
* Check if the bouncer is connected to a "Blocklist as a service" Lapi.
*/
public function hasBlaasUri(): bool
{
$url = $this->getRemediationEngine()->getClient()->getConfig('api_url');

return 0 === strpos($url, Constants::BLAAS_URL);
}

/**
* This method prune the cache: it removes all the expired cache items.
*
Expand Down Expand Up @@ -276,6 +286,20 @@ public function refreshBlocklistCache(): array
}
}

/**
* @throws InvalidArgumentException
*/
public function resetUsageMetrics(): void
{
// Retrieve metrics cache item
$metricsItem = $this->getRemediationEngine()->getCacheStorage()->getItem(AbstractCache::ORIGINS_COUNT);
if ($metricsItem->isHit()) {
// Reset the metrics
$metricsItem->set([]);
$this->getRemediationEngine()->getCacheStorage()->getAdapter()->save($metricsItem);
}
}

/**
* Handle a bounce for current IP.
*
Expand Down
2 changes: 2 additions & 0 deletions src/Constants.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
*/
class Constants extends RemConstants
{
/** @var string The URL prefix for Blocklist as a service LAPI */
public const BLAAS_URL = 'https://admin.api.crowdsec.net';
/** @var int The duration we keep a captcha flow in cache */
public const CACHE_EXPIRATION_FOR_CAPTCHA = 86400;
/** @var string The "MEMCACHED" cache system */
Expand Down
84 changes: 84 additions & 0 deletions tests/Integration/AbstractBouncerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@
* @covers \CrowdSecBouncer\AbstractBouncer::shouldBounceCurrentIp
* @covers \CrowdSecBouncer\AbstractBouncer::handleBounceExclusion
* @covers \CrowdSecBouncer\AbstractBouncer::pushUsageMetrics
* @covers \CrowdSecBouncer\AbstractBouncer::resetUsageMetrics
*/
final class AbstractBouncerTest extends TestCase
{
Expand Down Expand Up @@ -1053,6 +1054,89 @@ public function testRun()
);
}

public function testResetMetrics()
{
$client = new BouncerClient($this->configs, null, $this->logger);
$cache = new PhpFiles($this->configs, $this->logger);
$originCountItem = $cache->getItem(AbstractCache::ORIGINS_COUNT)->get();
$this->assertEquals(
null,
$originCountItem,
'The origin count for clean should be empty'
);

// bouncing URI
$client = new BouncerClient($this->configs, null, $this->logger);
$cache = new PhpFiles($this->configs, $this->logger);
$lapiRemediation = new LapiRemediation($this->configs, $client, $cache, $this->logger);
// Mock sendResponse and redirectResponse to avoid PHP UNIT header already sent or exit error
$bouncer = $this->getMockForAbstractClass(AbstractBouncer::class, [$this->configs, $lapiRemediation, $this->logger],
'', true,
true, true, [
'sendResponse',
'redirectResponse',
'getHttpMethod',
'getPostedVariable',
'getHttpRequestHeader',
'getRemoteIp',
'getRequestUri',
]);

$bouncer->method('getRequestUri')->willReturnOnConsecutiveCalls('/home');
$bouncer->method('getRemoteIp')->willReturnOnConsecutiveCalls('127.0.0.3');
$this->assertEquals(true, $bouncer->run(), 'Should bounce uri');
$originCountItem = $cache->getItem(AbstractCache::ORIGINS_COUNT)->get();
$this->assertEquals(
['clean' => ['bypass' => 1]],
$originCountItem,
'The origin count for clean should be 1'
);

// Test no-forward
$bouncerConfigs = array_merge(
$this->configs,
[
'forced_test_forwarded_ip' => Constants::X_FORWARDED_DISABLED,
]
);
$client = new BouncerClient($bouncerConfigs, null, $this->logger);
$cache = new PhpFiles($bouncerConfigs, $this->logger);
$lapiRemediation = new LapiRemediation($bouncerConfigs, $client, $cache, $this->logger);
// Mock sendResponse and redirectResponse to avoid PHP UNIT header already sent or exit error
$bouncer = $this->getMockForAbstractClass(AbstractBouncer::class, [$bouncerConfigs, $lapiRemediation, $this->logger],
'', true,
true, true, [
'sendResponse',
'redirectResponse',
'getHttpMethod',
'getPostedVariable',
'getHttpRequestHeader',
'getRemoteIp',
'getRequestUri',
]);

$bouncer->method('getRequestUri')->willReturnOnConsecutiveCalls('/home');
$bouncer->method('getRemoteIp')->willReturnOnConsecutiveCalls('127.0.0.7');

$bouncer->run();

$originCountItem = $cache->getItem(AbstractCache::ORIGINS_COUNT)->get();
$this->assertEquals(
['clean' => ['bypass' => 2]],
$originCountItem,
'The origin count for clean should be 2'
);

// Test: reset metrics
$result = $bouncer->resetUsageMetrics();
$originCountItem = $cache->getItem(AbstractCache::ORIGINS_COUNT)->get();
$this->assertEquals(
[],
$originCountItem,
'The origin count item should be reset'
);
}

public function testPrivateAndProtectedMethods()
{
// handleCache
Expand Down
25 changes: 25 additions & 0 deletions tests/Unit/AbstractBouncerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@
* @uses \CrowdSecBouncer\AbstractBouncer::handleBounceExclusion
*
* @covers \CrowdSecBouncer\AbstractBouncer::pushUsageMetrics
* @covers \CrowdSecBouncer\AbstractBouncer::hasBlaasUri
*/
final class AbstractBouncerTest extends TestCase
{
Expand Down Expand Up @@ -774,6 +775,30 @@ public function testPushUsageMetricsException()
$this->assertEquals('Error in unit test', $errorMessage);
}

public function testHasBlockAsAServiceUri()
{
$configs = $this->configs;
$client = new BouncerClient($configs);
$cache = new PhpFiles($configs);
$lapiRemediation = new LapiRemediation($configs, $client, $cache);
$bouncer = $this->getMockForAbstractClass(AbstractBouncer::class, [$configs, $lapiRemediation]);

$result = $bouncer->hasBlaasUri();
$this->assertEquals(false, $result);

$configs = array_merge($this->configs, [
'api_url' => 'https://admin.api.crowdsec.net',
]);

$client = new BouncerClient($configs);
$cache = new PhpFiles($configs);
$lapiRemediation = new LapiRemediation($configs, $client, $cache);
$bouncer = $this->getMockForAbstractClass(AbstractBouncer::class, [$configs, $lapiRemediation]);

$result = $bouncer->hasBlaasUri();
$this->assertEquals(true, $result);
}

public function testShouldBounceCurrentIp()
{
$configs = $this->configs;
Expand Down
Loading