Skip to content
Open
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
64 changes: 50 additions & 14 deletions controllers/TestController.php
Original file line number Diff line number Diff line change
@@ -1,23 +1,48 @@
<?php

namespace lewiscowles;

use WP_REST_Server;
use WP_REST_Controller;
use WP_REST_Request;
use WP_REST_Response;

class TestController extends WP_REST_Controller {
use lewiscowles\CreditDataInterface;
use lewiscowles\CreditHandlerInterface;
use lewiscowles\auth\SystemUserInterface;
use lewiscowles\wordpress\auth\WPUser;
use lewiscowles\storage\TTLStorageInterface;
use lewiscowles\HTTP\REST\RestEndpointCreditData;
use lewiscowles\HTTP\REST\RestEndpointTimedRateLimit;
use lewiscowles\wordpress\storage\WPTransientStorage;

class TestController extends \WP_REST_Controller {
const RATE_FIELD = 'cd2_rest_rate_user';
const RATE_TIME = 60; // One Minute
const RATE_LIMIT = 5;

protected $limiter;
protected $auth;

public function __construct() {
$this->namespace = 'test/v1';
$this->auth = new WPUser();
$this->limiter = new RestEndpointTimedRateLimit(
self::RATE_FIELD."_".$this->auth->getPk(),
time(),
self::RATE_LIMIT,
new WPTransientStorage(),
self::RATE_TIME
);
}

public function register_routes() {
register_rest_route(
\register_rest_route(
"{$this->namespace}",
"/date",
[
[
'methods' => WP_REST_Server::READABLE,
'methods' => \WP_REST_Server::READABLE,
'callback' => [$this, 'index'],
'permission_callback' => [$this, 'index_permissions_check'],
'args' => [],
Expand All @@ -26,21 +51,32 @@ public function register_routes() {
);
}

public function index(WP_REST_Request $request) {
$data = ['now'=> date('Y-m-d H:i:s')];
return new WP_REST_Response( $data, 200);
public function index(\WP_REST_Request $request) {
$creditData = $this->limiter->getCreditData();
$refresh = $this->limiter->formatCreditReload($creditData);
if(!$this->limiter->checkCreditData($creditData)) {
$left = $creditData->getBalance();
$limit = self::RATE_LIMIT;
return new \WP_REST_Response(
"API Limit reached {$left}/{$limit} calls refresh in {$refresh}",
429
);
}

$data = [
'now'=> date('Y-m-d H:i:s'),
'time_to_refresh' => $refresh
];

$creditData->deductBalance(1);
$this->limiter->setCreditData($creditData);

return new \WP_REST_Response( $data, 200);
}

public function index_permissions_check(WP_REST_Request $request) {
// Really basic example for permissions checking
// (https://codex.wordpress.org/Roles_and_Capabilities#read)
return $this->user_permission_check(get_current_user_id(), 'read');
}

public function user_permission_check($user_id='', $cap='admin') {
if(!empty($user_id)) {
return user_can($user_id, $cap);
}
return false;
return $this->auth->hasCapability('read');
}
}
22 changes: 20 additions & 2 deletions index.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,31 @@
Author URI: https://www.codesign2.co.uk
*/

add_action('rest_api_init', function(){
namespace lewiscowles;

\add_action('rest_api_init', function() {
error_reporting(E_ALL);
ini_set('display_errors', true);
foreach([
'Test'
] as $endpoint) {
require_once( __DIR__."/controllers/{$endpoint}Controller.php");
$controller_class = "{$endpoint}Controller";
$controller_class = __NAMESPACE__."\\{$endpoint}Controller";
$controller = new $controller_class();
$controller->register_routes();
}
});

\add_action('wp_ajax_apinonce', function() {
die(\wp_create_nonce( 'wp_rest' ));
});

\spl_autoload_register( function($classname) {
$file = sprintf(
'%s/vendor/%s.php',
__DIR__, str_replace('\\', DIRECTORY_SEPARATOR, $classname)
);
if(file_exists($file)) {
require $file;
}
});
10 changes: 7 additions & 3 deletions swagger/description.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,19 @@ paths:
403:
description: "Unauthorised request rejection"
schema:
$ref: "#/definitions/inline_response_403"
$ref: "#/definitions/inline_response_err"
421:
description: "Too many requests rejection"
schema:
$ref: "#/definitions/inline_response_err"
definitions:
inline_response_200:
properties:
now:
type: "string"
format: "datetime"
description: "Curent server datetime value"
inline_response_403:
inline_response_err:
properties:
code:
type: string
Expand All @@ -44,4 +48,4 @@ definitions:
status:
type: integer
description: "HTTP Error Code"

10 changes: 10 additions & 0 deletions vendor/lewiscowles/CreditDataInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php

namespace lewiscowles;

interface CreditDataInterface {
public function getCreated() : int;
public function getBalance() : int;
public function deductBalance(int $deduction);
public function topupBalance(int $deduction);
}
12 changes: 12 additions & 0 deletions vendor/lewiscowles/CreditHandlerInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?php

namespace lewiscowles;

use lewiscowles\CreditDataInterface;

interface CreditHandlerInterface {
public function getCreditData() : CreditDataInterface;
public function checkCreditData(CreditDataInterface $data) : bool;
public function setCreditData(CreditDataInterface $data);
public function formatCreditReload(CreditDataInterface $data) : string;
}
31 changes: 31 additions & 0 deletions vendor/lewiscowles/HTTP/REST/RestEndpointCreditData.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<?php

namespace lewiscowles\HTTP\REST;

use lewiscowles\CreditDataInterface;

class RestEndpointCreditData implements CreditDataInterface {
protected $Created;
protected $Balance;

public function __construct(int $time, int $balance) {
$this->Created = $time;
$this->Balance = $balance;
}

public function getCreated() : int {
return $this->Created;
}

public function getBalance() : int {
return $this->Balance;
}

public function deductBalance(int $value) {
$this->Balance -= abs($value);
}

public function topUpBalance(int $value) {
$this->Balance += abs($value);
}
}
55 changes: 55 additions & 0 deletions vendor/lewiscowles/HTTP/REST/RestEndpointTimedRateLimit.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<?php

namespace lewiscowles\HTTP\REST;

use lewiscowles\CreditHandlerInterface;
use lewiscowles\CreditDataInterface;
use lewiscowles\storage\TTLStorageInterface;
use lewiscowles\HTTP\REST\RestEndpointCreditData;

class RestEndpointTimedRateLimit implements CreditHandlerInterface {
protected $HashId;
protected $Time;
protected $Credits;
protected $Storage;
protected $Ttl;

public function __construct(string $hash_id, int $time, int $credits,
TTLStorageInterface $ttsi, int $ttl) {
$this->HashId = $hash_id;
$this->Time = $time;
$this->Credits = $credits;
$this->Storage = $ttsi;
$this->Ttl = $ttl;
}

public function getCreditData() : CreditDataInterface {
return $this->Storage->get($this->HashId, new RestEndpointCreditData(
$this->Time, $this->Credits
));
}

public function checkCreditData(CreditDataInterface $data) : bool {
return ($data->getBalance() > 0);
}

public function setCreditData(CreditDataInterface $data) {
$this->Storage->set($this->HashId, $data, $this->getRefreshTtl($data));
}

public function formatCreditReload(CreditDataInterface $data) : string {
$refresh = $this->getRefreshTtl($data);
$out = date('s', $refresh).' seconds';
if($refresh > 60) {
$out = date('i', $refresh).' minutes, ' . $out;
}
if($refresh > 3600) {
$out = intval($refresh / 3600).' hours, '.$out;
}
return $out;
}

protected function getRefreshTtl(CreditDataInterface $data) {
return ($this->Ttl - ($this->Time - $data->getCreated()));
}
}
11 changes: 11 additions & 0 deletions vendor/lewiscowles/auth/SystemUserInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?php

namespace lewiscowles\auth;

interface SystemUserInterface {
public function hasCapability(string $capability) : bool;
public function isLoggedIn() : bool;
public function getPK() : string;
public function login(string $username, string $password);
public function logout();
}
8 changes: 8 additions & 0 deletions vendor/lewiscowles/storage/TTLStorageInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?php

namespace lewiscowles\storage;

interface TTLStorageInterface {
public function get(string $hash, $default);
public function set(string $hash, $value, int $ttl);
}
40 changes: 40 additions & 0 deletions vendor/lewiscowles/wordpress/auth/WPUser.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<?php

namespace lewiscowles\wordpress\auth;

use lewiscowles\auth\SystemUserInterface;

class WPUser implements SystemUserInterface {
protected $Pk;

public function __construct() {
if(!function_exists('get_current_user_id') ||
!function_exists('current_user_can') ||
!function_exists('wp_authenticate') ||
!function_exists('wp_logout') ||
!function_exists('is_user_logged_in')) {
throw new \RuntimeException("Required To Use Inside WordPress");
}
$this->Pk = \get_current_user_id();
}

public function hasCapability(string $capability) : bool {
return \current_user_can($capability);
}

public function isLoggedIn() : bool {
return \is_user_logged_in();
}

public function getPK() : string {
return $this->Pk;
}

public function login(string $username, string $password) {
\wp_authenticate($username, $password);
}

public function logout() {
\wp_logout();
}
}
26 changes: 26 additions & 0 deletions vendor/lewiscowles/wordpress/storage/WPTransientStorage.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?php

namespace lewiscowles\wordpress\storage;

use lewiscowles\storage\TTLStorageInterface;

class WPTransientStorage implements TTLStorageInterface {
public function __construct() {
if(!function_exists('set_transient') ||
!function_exists('get_transient')) {
throw new \RuntimeException("Required To Use Inside WordPress");
}
}

public function get(string $hash, $default) {
$out = \get_transient($hash);
if(false !== $out) {
return $out;
}
return $default;
}

public function set(string $hash, $value, int $ttl) {
\set_transient($hash, $value, $ttl);
}
}