Skip to content
7 changes: 6 additions & 1 deletion src/Firewall.php
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,12 @@ public function getLastMatchedRule(): ?Rule
}

/**
* Evaluate the registered rules and return true when the request should be allowed.
* Evaluate registered rules in order against populated attributes.
*
* Sets the matched rule via getLastMatchedRule() when a rule's conditions
* match. Returns whether that rule's action allows the request to continue
* (bypass/rateLimit) or should be blocked (deny/challenge/redirect).
* Returns false when no rule matches.
*/
public function verify(): bool
{
Expand Down
14 changes: 14 additions & 0 deletions src/Rule.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ abstract class Rule
*/
protected array $conditions = [];

private ?string $id = null;

/**
* @param array<Condition|array<string, mixed>> $conditions
*/
Expand All @@ -34,6 +36,18 @@ static function (Condition|array $condition): Condition {

abstract public function getAction(): string;

public function setId(string $id): self
{
$this->id = $id;

return $this;
}

public function getId(): ?string
{
return $this->id;
}

/**
* @return array<Condition>
*/
Expand Down
43 changes: 43 additions & 0 deletions tests/FirewallTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,35 @@

class FirewallTest extends TestCase
{
public function testVerifyUsesPopulatedRequestAttributesAndExposesMatchedRule(): void
{
$firewall = new Firewall();
$firewall->setAttributes([
'requestIP' => '127.0.0.1',
'requestPath' => '/v1/locale',
'requestCountry' => 'IL',
]);

$deny = new Deny([
Condition::equal('ip', ['127.0.0.1']),
Condition::contains('path', ['/v1']),
Condition::equal('country', ['IL']),
]);

$firewall->addRule($deny);

$this->assertFalse($firewall->verify());
$this->assertSame($deny, $firewall->getLastMatchedRule());

$firewall->clearRules();
$firewall->addRule(new Deny([
Condition::equal('country', ['US']),
]));

$this->assertFalse($firewall->verify());
$this->assertNull($firewall->getLastMatchedRule());
}

public function testRuleOrder(): void
{
$firewall = new Firewall();
Expand Down Expand Up @@ -63,4 +92,18 @@ public function testRateLimitMetadata(): void
$this->assertSame(2, $matched->getLimit());
$this->assertSame(60, $matched->getInterval());
}

public function testRuleIdentifierRoundTrip(): void
{
$rule = (new Deny([
Condition::equal('ip', ['127.0.0.1']),
]))->setId('rule_abc');

$firewall = new Firewall();
$firewall->setAttribute('requestIP', '127.0.0.1');
$firewall->addRule($rule);

$this->assertFalse($firewall->verify());
$this->assertSame('rule_abc', $firewall->getLastMatchedRule()?->getId());
}
}
Loading