Skip to content

Commit 96165bc

Browse files
authored
Add Settings UI (#10)
2 parents 54916bd + bdba836 commit 96165bc

File tree

9 files changed

+239
-67
lines changed

9 files changed

+239
-67
lines changed

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
.DS_Store
22
composer.lock
33
node_modules/
4-
phpcs.log
54
vendor/
5+
phpcs-report.xml

README.md

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -124,11 +124,11 @@ use FrostyMedia\WpRestCop\RestApi\Rules\IpRulesInterface;
124124
/**
125125
* Register routes.
126126
*/
127-
add_action( 'rest_api_init', static function (): void {
127+
add_action('rest_api_init', static function (): void {
128128
register_rest_route( 'myplugin/v1', '/internal/(?P<id>\d+)', [
129129
'methods' => 'GET',
130130
'callback' => 'my_awesome_expensive_func',
131-
'ips' => [
131+
IpRulesInterface::IPS => [
132132
IpRulesInterface::ALLOW => ['192.168.50.4'],
133133
IpRulesInterface::DENY => ['66.249.66.1'],
134134
],
@@ -154,4 +154,3 @@ A few [WP CLI](http://wp-cli.org/) commands are included to configure the plugin
154154
* Additional rate limit strategies.
155155
* More route-level capabilities.
156156
* Advanced access rules.
157-
* Administration UI.

composer.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
},
3838
"require": {
3939
"php": "^8.3",
40+
"dwnload/wp-settings-api": "^3.11",
4041
"pimple/pimple": "~3.0",
4142
"symfony/http-foundation": "^7.1.7",
4243
"thefrosty/wp-utilities": "^3.3"

src/Cli/RestCop.php

Lines changed: 24 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,11 @@
44

55
namespace FrostyMedia\WpRestCop\Cli;
66

7-
use FrostyMedia\WpRestCop\RestApi\Officer;
7+
use Dwnload\WpSettingsApi\Api\Options;
88
use FrostyMedia\WpRestCop\RestApi\Rules\IpRules;
99
use FrostyMedia\WpRestCop\RestApi\Rules\IpRulesInterface;
1010
use FrostyMedia\WpRestCop\ServiceProvider;
11+
use FrostyMedia\WpRestCop\Settings\Settings;
1112
use TheFrosty\WpUtilities\Plugin\ContainerAwareTrait;
1213
use WP_CLI;
1314
use function array_diff;
@@ -98,11 +99,11 @@ public function status(): void
9899
esc_html__('Source', 'wp-rest-cop'),
99100
];
100101

101-
$settings = Officer::getSettings();
102+
$settings = Options::getOption(Settings::SETTINGS);
102103
$action_l10n = esc_html__('ALLOW', 'wp-rest-cop');
103104
foreach ($this->getRules()->getAllowed() as $ip) {
104105
$source = 'code';
105-
if (in_array($ip, $settings['rules'][IpRulesInterface::ALLOW], true)) {
106+
if (in_array($ip, $settings[Settings::SETTING_ALLOW_RULES], true)) {
106107
$source = 'option';
107108
}
108109

@@ -112,7 +113,7 @@ public function status(): void
112113
$action_l10n = esc_html__('DENY', 'wp-rest-cop');
113114
foreach ($this->getRules()->getDenied() as $ip) {
114115
$source = 'code';
115-
if (in_array($ip, $settings['rules'][IpRulesInterface::DENY], true)) {
116+
if (in_array($ip, $settings[Settings::SETTING_DENY_RULES], true)) {
116117
$source = 'option';
117118
}
118119

@@ -139,14 +140,14 @@ public function status(): void
139140
*/
140141
public function set(array $args): void
141142
{
142-
$settings = Officer::getSettings();
143+
$settings = Options::getOption(Settings::SETTINGS);
143144

144-
if (!in_array($args[0], ['interval', 'limit'], true)) {
145+
if (!in_array($args[0], [Settings::SETTING_INTERVAL, Settings::SETTING_LIMIT], true)) {
145146
WP_CLI::error(sprintf(esc_html__('%s is not a valid setting.', 'wp-rest-cop'), $args[0]));
146147
}
147148

148149
$settings[$args[0]] = (int)$args[1];
149-
update_option(Officer::OPTION, $settings);
150+
update_option(Settings::SETTINGS, $settings);
150151
WP_CLI::success(sprintf(esc_html__('Updated %1$s setting to %2$s.', 'wp-rest-cop'), $args[0], $args[1]));
151152
}
152153

@@ -160,12 +161,22 @@ public function set(array $args): void
160161
*/
161162
protected function updateOption(string $key, array $args, bool $delete = false): void
162163
{
163-
$settings = Officer::getSettings();
164-
$ips = $settings['rules'][$key] ?? [];
165-
$ips = $delete ? array_diff($ips, $args) : array_merge($ips, $args);
166-
$settings['rules'][$key] = array_unique(array_filter($ips));
167-
168-
update_option(Officer::OPTION, $settings);
164+
$update = static function (string $key) use ($args, $delete): void {
165+
$settings = Options::getOption(Settings::SETTINGS);
166+
$ips = $settings[$key] ?? [];
167+
$ips = $delete ? array_diff($ips, $args) : array_merge($ips, $args);
168+
$settings[$key] = array_unique(array_filter($ips));
169+
update_option(Settings::SETTINGS, $settings);
170+
};
171+
172+
switch ($key) {
173+
case IpRulesInterface::ALLOW:
174+
$update(Settings::SETTING_ALLOW_RULES);
175+
break;
176+
case IpRulesInterface::DENY:
177+
$update(Settings::SETTING_DENY_RULES);
178+
break;
179+
}
169180
}
170181

171182
/**

src/RestApi/Officer.php

Lines changed: 28 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,10 @@
44

55
namespace FrostyMedia\WpRestCop\RestApi;
66

7-
use FrostyMedia\WpRestCop\RestApi\Rules\IpRules;
7+
use Dwnload\WpSettingsApi\Api\Options;
88
use FrostyMedia\WpRestCop\RestApi\Rules\IpRulesInterface;
99
use FrostyMedia\WpRestCop\ServiceProvider;
10+
use FrostyMedia\WpRestCop\Settings\Settings;
1011
use Psr\Container\ContainerInterface;
1112
use TheFrosty\WpUtilities\Plugin\AbstractContainerProvider;
1213
use TheFrosty\WpUtilities\Plugin\HttpFoundationRequestInterface;
@@ -22,13 +23,11 @@
2223
use function esc_html__;
2324
use function filter_var;
2425
use function get_current_user_id;
25-
use function get_option;
2626
use function is_user_logged_in;
2727
use function rest_convert_error_to_response;
2828
use function sprintf;
2929
use function update_option;
3030
use const FILTER_VALIDATE_BOOLEAN;
31-
use const MINUTE_IN_SECONDS;
3231

3332
/**
3433
* RateLimit class.
@@ -39,10 +38,6 @@ class Officer extends AbstractContainerProvider implements HttpFoundationRequest
3938

4039
use HttpFoundationRequestTrait;
4140

42-
public const string OPTION = 'wp_rest_cop_settings';
43-
protected const int DEFAULT_INTERVAL = MINUTE_IN_SECONDS;
44-
protected const int DEFAULT_LIMIT = MINUTE_IN_SECONDS;
45-
4641
/**
4742
* Officer constructor.
4843
* @param ContainerInterface|null $container
@@ -51,21 +46,12 @@ class Officer extends AbstractContainerProvider implements HttpFoundationRequest
5146
*/
5247
public function __construct(
5348
?ContainerInterface $container = null,
54-
protected int $interval = self::DEFAULT_INTERVAL,
55-
protected int $limit = self::DEFAULT_LIMIT
49+
protected int $interval = Settings::DEFAULT_INTERVAL,
50+
protected int $limit = Settings::DEFAULT_LIMIT
5651
) {
5752
parent::__construct($container);
5853
}
5954

60-
/**
61-
* Get the option array.
62-
* @return array
63-
*/
64-
public static function getSettings(): array
65-
{
66-
return get_option(self::OPTION, []);
67-
}
68-
6955
/**
7056
* Add class hooks.
7157
*/
@@ -123,12 +109,12 @@ public function getInterval(): int
123109

124110
/**
125111
* Set the number of seconds per interval.
126-
* @param int $interval Seconds per interval.
112+
* @param int|string $interval Seconds per interval.
127113
* @return $this
128114
*/
129-
public function setInterval(int $interval): static
115+
public function setInterval(int|string $interval): static
130116
{
131-
$this->interval = $interval;
117+
$this->interval = (int)$interval;
132118
return $this;
133119
}
134120

@@ -143,12 +129,12 @@ public function getLimit(): int
143129

144130
/**
145131
* Set the number of requests allowed per interval.
146-
* @param int $limit Number of requests.
132+
* @param int|string $limit Number of requests.
147133
* @return $this
148134
*/
149-
public function setLimit(int $limit): static
135+
public function setLimit(int|string $limit): static
150136
{
151-
$this->limit = $limit;
137+
$this->limit = (int)$limit;
152138
return $this;
153139
}
154140

@@ -157,24 +143,19 @@ public function setLimit(int $limit): static
157143
*/
158144
protected function initializeSettings(): void
159145
{
160-
$settings = self::getSettings();
146+
$settings = Options::getOptions(Settings::SETTINGS);
161147

162148
if (empty($settings)) {
163149
$settings = [
164-
'interval' => self::DEFAULT_INTERVAL,
165-
'limit' => self::DEFAULT_LIMIT,
166-
'rules' => [
167-
IpRulesInterface::ALLOW => [
168-
'127.0.0.1',
169-
'::1',
170-
],
171-
IpRulesInterface::DENY => [],
172-
],
150+
Settings::SETTING_INTERVAL => Settings::DEFAULT_INTERVAL,
151+
Settings::SETTING_LIMIT => Settings::DEFAULT_LIMIT,
152+
Settings::SETTING_ALLOW_RULES => ['127.0.0.1', '::1'],
153+
Settings::SETTING_DENY_RULES => [],
173154
];
174-
update_option(self::OPTION, $settings);
155+
update_option(Settings::SETTINGS, $settings);
175156
}
176157

177-
$this->setInterval($settings['interval'])->setLimit($settings['limit']);
158+
$this->setInterval($settings[Settings::SETTING_INTERVAL])->setLimit($settings[Settings::SETTING_LIMIT]);
178159
}
179160

180161
/**
@@ -183,12 +164,11 @@ protected function initializeSettings(): void
183164
*/
184165
protected function initializeIpRules(): void
185166
{
186-
$settings = self::getSettings();
187-
/** @var IpRules $rules */
167+
/** @var \FrostyMedia\WpRestCop\RestApi\Rules\IpRules $rules */
188168
$rules = $this->getContainer()->get(ServiceProvider::IP_RULES);
189169
$rules
190-
->allow($settings['rules'][IpRulesInterface::ALLOW] ?? '')
191-
->deny($settings['rules'][IpRulesInterface::DENY] ?? '');
170+
->allow(Options::getOption(Settings::SETTING_ALLOW_RULES, Settings::SETTINGS))
171+
->deny(Options::getOption(Settings::SETTING_DENY_RULES, Settings::SETTINGS));
192172
}
193173

194174
/**
@@ -199,7 +179,7 @@ protected function initializeIpRules(): void
199179
*/
200180
protected function checkIpRules(WP_Error|bool|null $error): WP_Error|bool|null
201181
{
202-
/** @var IpRules $rules */
182+
/** @var \FrostyMedia\WpRestCop\RestApi\Rules\IpRules $rules */
203183
$rules = $this->getContainer()->get(ServiceProvider::IP_RULES);
204184
if (!$rules->check($this->getIpAddress())) {
205185
return $this->getForbiddenError();
@@ -268,11 +248,15 @@ protected function maybeThrottleRequest(
268248
protected function checkRouteIpRules(mixed $response, WP_REST_Request $request): mixed
269249
{
270250
$ips = [];
271-
if (!empty($request->get_attributes()['ips'])) {
272-
$ips = $request->get_attributes()['ips'];
251+
if (!empty($request->get_attributes()[IpRulesInterface::IPS])) {
252+
$ips = $request->get_attributes()[IpRulesInterface::IPS];
273253
}
274254

275-
$rules = $ips instanceof IpRulesInterface ? $ips : new IpRules($ips);
255+
/** @var \FrostyMedia\WpRestCop\RestApi\Rules\IpRules $rules */
256+
$rules = $this->getContainer()->get(ServiceProvider::IP_RULES);
257+
if (!$ips instanceof IpRulesInterface) {
258+
$rules->allow($ips[IpRulesInterface::ALLOW] ?? '')->deny($ips[IpRulesInterface::DENY] ?? '');
259+
}
276260

277261
if (!$rules->check($this->getIpAddress())) {
278262
$response = $this->getForbiddenError();

src/RestApi/Rules/IpRules.php

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
/**
1313
* IP address rules class.
1414
* phpcs:disable Squiz.Commenting.FunctionComment.MissingParamTag
15-
* @package Cedaro\WPRESTCop
15+
* @package FrostyMedia\WpRestCop\RestApi\Rules
1616
*/
1717
class IpRules implements IpRulesInterface
1818
{
@@ -34,12 +34,12 @@ class IpRules implements IpRulesInterface
3434
*/
3535
public function __construct(array $rules = [])
3636
{
37-
if (isset($rules['allow'])) {
38-
$this->allow($rules['allow']);
37+
if (isset($rules[self::ALLOW])) {
38+
$this->allow($rules[self::ALLOW]);
3939
}
4040

41-
if (isset($rules['deny'])) {
42-
$this->deny($rules['deny']);
41+
if (isset($rules[self::DENY])) {
42+
$this->deny($rules[self::DENY]);
4343
}
4444
}
4545

src/RestApi/Rules/IpRulesInterface.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,13 @@
66

77
/**
88
* Access rules interface.
9-
* @package Cedaro\WPRESTCop
9+
* @package FrostyMedia\WpRestCop\RestApi\Rules
1010
*/
1111
interface IpRulesInterface
1212
{
1313
public const string ALLOW = 'allow';
1414
public const string DENY = 'deny';
15+
public const string IPS = 'ips';
1516

1617
/**
1718
* Whether client ID passes allowed and denied checks.

0 commit comments

Comments
 (0)