Skip to content

Commit 08cf960

Browse files
committed
- NEW Features
- Api Key command info - Added field in limits table
1 parent f8d2f47 commit 08cf960

File tree

10 files changed

+257
-17
lines changed

10 files changed

+257
-17
lines changed

composer.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,10 @@
4949
}
5050
},
5151
"scripts": {
52-
"test": "vendor/bin/phpunit",
52+
"test": [
53+
"Composer\\Config::disableProcessTimeout",
54+
"vendor/bin/phpunit"
55+
],
5356
"cs-fixer": "vendor/bin/php-cs-fixer fix src",
5457
"cs-fixer-tests": "vendor/bin/php-cs-fixer fix tests"
5558
}

src/Commands/KeyInfo.php

Lines changed: 165 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@
66
use CodeIgniter\CLI\CLI;
77
use CodeIgniter\Config\BaseConfig;
88
use Daycry\RestServer\Exceptions\UnauthorizedException;
9+
use Daycry\RestServer\Entities\AccessEntity;
10+
use Daycry\RestServer\Entities\LimitEntity;
11+
use Daycry\RestServer\Entities\PetitionEntity;
12+
use DateTime;
913

1014
class KeyInfo extends BaseCommand
1115
{
@@ -15,55 +19,205 @@ class KeyInfo extends BaseCommand
1519
protected $usage = 'restserver:apikeyinfo [Options]';
1620
protected $options = [ '-key' => 'Api Key' ];
1721

18-
protected BaseConfig $config;
22+
private $_key = null;
23+
private BaseConfig $config;
24+
private $parser;
1925

2026
public function run(array $params)
2127
{
28+
$this->parser = \Config\Services::parser();
2229
$this->config = config('RestServer');
2330
$this->db = db_connect( $this->config->restDatabaseGroup);
2431

2532
try
2633
{
27-
$key = $params[ 'key' ] ?? CLI::getOption( 'key' );
34+
if( $this->config->restEnableKeys != true )
35+
{
36+
$this->_printError(lang('RestCommand.apiKeyDisabled'));
37+
exit;
38+
}
2839

29-
if( empty( $key ) )
40+
$this->_key = $params[ 'key' ] ?? CLI::getOption( 'key' );
41+
42+
if( empty( $this->_key ) )
3043
{
31-
$key = CLI::prompt( lang( 'RestCommand.insertApiKey' ), null, 'required|max_length['.$this->config->restKeyLength.']' );
44+
$this->_key = CLI::prompt( lang( 'RestCommand.insertApiKey' ), null, 'required|max_length['.$this->config->restKeyLength.']' );
3245
CLI::newLine();
3346
}
3447

3548
$keyModel = new \Daycry\RestServer\Models\KeyModel();
36-
$row = $keyModel->where($this->config->restKeyColumn, $key)->first();
49+
$row = $keyModel->where($this->config->restKeyColumn, $this->_key)->first();
3750

3851
if(!$row)
3952
{
40-
throw UnauthorizedException::forInvalidApiKey($key);
53+
throw UnauthorizedException::forInvalidApiKey($this->_key);
4154
}
4255

4356
$this->_getKeyInfo($row);
57+
$this->_getKeyUser($row);
58+
$this->_getKeyAccess($row);
59+
$this->_getKeyLocks($row);
4460

4561
}catch( \Exception $ex )
4662
{
4763
CLI::newLine();
48-
CLI::error( $ex->getMessage() );
64+
CLI::error( '**** ' . $ex->getMessage() . ' ****' );
4965
CLI::newLine();
5066
}
5167
}
5268

5369
private function _getKeyInfo( \Daycry\RestServer\Entities\KeyEntity $row )
5470
{
55-
CLI::write('****' . lang( 'RestCommand.apiKeyInfo' ) . '****', 'green', 'light_gray');
71+
$this->_printHeader( lang( 'RestCommand.apiKeyInfo' ) );
5672

57-
$thead = $this->_getTableColumns($this->config->restKeysTable);
58-
$tbody = [
73+
$row = [
5974
[$row->id, $row->{$this->config->restKeyColumn}, $row->level, $row->ignore_limits, $row->is_private_key, $row->ip_addresses, $row->created_at, $row->updated_at, $row->deleted_at]
6075
];
6176

62-
CLI::table($tbody, $thead);
77+
$this->_printTable( $this->_getTableColumns($this->config->restKeysTable), $row);
78+
}
79+
80+
private function _getKeyUser( \Daycry\RestServer\Entities\KeyEntity $row )
81+
{
82+
$this->_printHeader( lang( 'RestCommand.apiKeyUser' ) );
83+
84+
$users = $row->{$this->config->restUsersTable};
85+
$body = $columns = [];
86+
87+
if( !$users )
88+
{
89+
$this->_printError(lang( 'RestCommand.apiKeyNoUsers' ));
90+
}else{
91+
foreach( $users as $user )
92+
{
93+
array_push($body, array( $user->id, $user->{$this->config->userNameColumn}, $user->created_at, $user->updated_at, $user->deleted_at ));
94+
}
95+
96+
$columns = array( 'id', $this->config->userNameColumn, 'created_at', 'updated_at', 'deleted_at' );
97+
98+
$this->_printTable( $columns, $body);
99+
}
100+
}
101+
102+
private function _getKeyAccess( \Daycry\RestServer\Entities\KeyEntity $row )
103+
{
104+
$this->_printHeader( lang( 'RestCommand.apiKeyAccess' ) );
105+
106+
if( $this->config->restEnableAccess != true)
107+
{
108+
$this->_printError(lang('RestCommand.apiKeyAccessDisabled'));
109+
}
110+
111+
$body = [];
112+
foreach( $row->{$this->config->restAccessTable} as $access)
113+
{
114+
// @codeCoverageIgnoreStart
115+
if (!$access instanceof AccessEntity) {
116+
$access = new AccessEntity((array)$access);
117+
}
118+
// @codeCoverageIgnoreEnd
119+
120+
$access->namespace_id = $access->{$this->config->restNamespaceTable}->controller;
121+
122+
array_push($body, array($access->id, $access->all_access, $access->namespace_id, $access->method, $access->created_at, $access->updated_at, $access->deleted_at));
123+
}
124+
125+
$columns = $this->_getTableColumns($this->config->restAccessTable);
126+
$columns = \array_filter($columns, static function ($element) {
127+
return $element !== "key_id";
128+
});
129+
130+
$this->_printTable( $columns, $body);
131+
}
132+
133+
private function _getKeyLocks( \Daycry\RestServer\Entities\KeyEntity $row )
134+
{
135+
$this->_printHeader( lang( 'RestCommand.apiKeyLocks' ) );
136+
137+
if( $this->config->restEnableLimits != true || $this->config->restEnableOverridePetition != true)
138+
{
139+
$this->_printError($this->parser->setData(array( 'table' => $this->config->configRestPetitionsTable ))->renderString(lang('RestCommand.apiKeyLimitsDisabled')));
140+
}else{
141+
$limits = $row->{$this->config->restLimitsTable};
142+
$body = [];
143+
144+
if( !$limits )
145+
{
146+
$this->_printError(lang( 'RestCommand.apiKeyNoLocks' ));
147+
}else{
148+
foreach( $limits as $limit )
149+
{
150+
// @codeCoverageIgnoreStart
151+
if (!$limit instanceof LimitEntity) {
152+
$limit = new LimitEntity((array)$limit);
153+
}
154+
// @codeCoverageIgnoreEnd
155+
156+
$r = $limit->{$this->config->configRestPetitionsTable};
157+
// @codeCoverageIgnoreStart
158+
if (!$r instanceof PetitionEntity) {
159+
$r = new PetitionEntity((array)$r);
160+
}
161+
// @codeCoverageIgnoreEnd
162+
163+
$timeLimit = ( isset( $r->time ) ) ? $r->time : 3600;
164+
if( $limit->count >= $r->limit && $limit->hour_started > (time() - $timeLimit) )
165+
{
166+
$timeLeft = $this->_getTimeLeft($limit->hour_started - (time() - $timeLimit));
167+
168+
$limit->hour_started = date('Y-m-d H:i:s', $limit->hour_started);
169+
array_push( $body, array( $timeLeft, $limit->id, $r->{$this->config->restNamespaceTable}->controller, $limit->uri, $limit->count, $limit->hour_started, $limit->created_at, $limit->updated_at, $limit->deleted_at ) );
170+
}
171+
}
172+
173+
$columns = $this->_getTableColumns($this->config->restLimitsTable);
174+
$columns = \array_filter($columns, static function ($element) {
175+
return $element !== "key_id";
176+
});
177+
178+
array_unshift($columns, 'Time Left');
179+
$this->_printTable( $columns, $body);
180+
}
181+
}
182+
}
183+
184+
private function _getTimeLeft( int $timeLeft)
185+
{
186+
$init = new DateTime('now');
187+
$end = new DateTime();
188+
$end->setTimestamp( time() + $timeLeft );
189+
190+
$interval = $init->diff($end);
191+
192+
return $interval->format('%H:%I:%S');
63193
}
64194

65195
private function _getTableColumns(string $table) :array
66196
{
67197
return $this->db->getFieldNames($table);
68198
}
199+
200+
private function _printTable( $header, $rows)
201+
{
202+
$tbody = [];
203+
$thead = $header;
204+
foreach( $rows as $row)
205+
{
206+
array_push($tbody, $row);
207+
}
208+
209+
CLI::table($tbody, $thead);
210+
}
211+
212+
private function _printHeader(string $header)
213+
{
214+
CLI::write('**** ' . $header . ' ****', 'green', 'light_gray');
215+
}
216+
217+
private function _printError(string $error)
218+
{
219+
CLI::newLine();
220+
CLI::error( '**** ' . $error . ' ****' );
221+
CLI::newLine();
222+
}
69223
}

src/Config/RestServer.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -237,9 +237,12 @@ public function validate( $username, $password = true )
237237
/**
238238
* Class that associates keys to users, you can edit the class for whatever you want
239239
* The "$userKeyColumn" column is the foreign key of the keys table
240+
* The "$userNameColumn" column is the name of user and is unique Ex: name, username
241+
* This column is used in apikeyinfo command for extract the info about api key
240242
*/
241243
public $userModelClass = \Daycry\RestServer\Models\UserModel::class;
242244
public $userKeyColumn = 'key_id';
245+
public $userNameColumn = 'name';
243246

244247
/**
245248
*--------------------------------------------------------------------------
@@ -415,7 +418,7 @@ public function validate( $username, $password = true )
415418
| table name to match e.g. my_access
416419
|
417420
*/
418-
public $restAccessTable = 'ws_access';
421+
public $restAccessTable = 'ws_accesses';
419422

420423
/*
421424
|--------------------------------------------------------------------------
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
<?php
2+
3+
namespace Daycry\RestServer\Database\Migrations;
4+
5+
use CodeIgniter\Database\Migration;
6+
use CodeIgniter\Database\Forge;
7+
use CodeIgniter\Config\BaseConfig;
8+
9+
class AddColumnRequestInLimitsTable extends Migration
10+
{
11+
protected $DBGroup = 'default';
12+
private BaseConfig $config;
13+
/**
14+
* Constructor.
15+
*
16+
* @param Forge $forge
17+
*/
18+
public function __construct(?Forge $forge = null)
19+
{
20+
$this->config = $this->_getConfig();
21+
$this->DBGroup = $this->config->restDatabaseGroup;
22+
23+
parent::__construct($forge);
24+
}
25+
26+
public function up()
27+
{
28+
$this->DBGroup = $this->config->restDatabaseGroup;
29+
30+
$cache = \Config\Services::cache();
31+
$cache->delete('database-schema-' . $this->config->restDatabaseGroup);
32+
33+
$this->forge->addColumn($this->config->restLimitsTable, [
34+
'request_id' => [
35+
'type' => 'int',
36+
'constraint' => 11,
37+
'unsigned' => true,
38+
'null' => false,
39+
'after' => 'id'
40+
],
41+
]);
42+
43+
$this->db->query('ALTER TABLE `'.$this->config->restLimitsTable.'` ADD CONSTRAINT `'.$this->config->restLimitsTable.'_request_id_foreign` FOREIGN KEY (`request_id`) REFERENCES `'.$this->config->configRestPetitionsTable.'` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE;');
44+
45+
$cache = \Config\Services::cache();
46+
$cache->delete('database-schema-' . config('RestServer')->restDatabaseGroup);
47+
}
48+
49+
public function down()
50+
{
51+
if ($this->db->DBDriver != 'SQLite3') { // @phpstan-ignore-line
52+
$this->forge->dropForeignKey($this->config->restLimitsTable, $this->config->restLimitsTable . '_request_id_foreign');
53+
}
54+
55+
$this->forge->dropColumn($this->config->restLimitsTable, 'request_id');
56+
57+
$cache = \Config\Services::cache();
58+
$cache->delete('database-schema-' . config('RestServer')->restDatabaseGroup);
59+
}
60+
61+
private function _getConfig()
62+
{
63+
$config = config('RestServer');
64+
65+
if (!$config) {
66+
$config = new \Daycry\RestServer\Config\RestServer();
67+
}
68+
69+
return $config;
70+
}
71+
}

src/Database/Seeds/ExampleSeeder.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,7 @@ public function run()
317317

318318
$limits = [
319319
[
320+
'request_id' => '1',
320321
'uri' => 'api-key:wco8go0csckk8cckgw4kk40g4c4s0ckkcscggocg',
321322
'count' => '10',
322323
'hour_started' => 1459537345,

src/Language/en/RestCommand.php

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,13 @@
22

33
return [
44
'insertApiKey' => 'Insert an api key',
5-
'apiKeyInfo' => 'Api key info'
5+
'apiKeyInfo' => 'Api key info',
6+
'apiKeyUser' => 'Api key User',
7+
'apiKeyLocks' => 'Api key Locks',
8+
'apiKeyLimitsDisabled' => 'Api key Limits disabled, try to enable \'restEnableOverridePetition\' and \'restEnableLimits\' in Restserver config, and configure the limits in \'{ table }\' table',
9+
'apiKeyNoUsers' => 'There are no assigned users',
10+
'apiKeyNoLocks' => 'There are no locks',
11+
'apiKeyAccess' => 'Api key access',
12+
'apiKeyDisabled' => 'Api Key control disabled, try to enable \'restEnableKeys\' in Restserver config',
13+
'apiKeyAccessDisabled' => 'Api Key access disabled, try to enable \'restEnableAccess\' in Restserver config'
614
];

src/Models/LimitModel.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ class LimitModel extends Model
2323

2424
protected $useSoftDeletes = true;
2525

26-
protected $allowedFields = [ 'uri', 'count', 'hour_started', 'key_id' ];
26+
protected $allowedFields = [ 'uri', 'count', 'hour_started', 'key_id', 'request_id' ];
2727

2828
protected $useTimestamps = true;
2929
protected $createdField = 'created_at';

src/Validators/ApiKey.php

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ public static function check(RequestInterface $request, object $petition = null,
2323
$keyModel = new \Daycry\RestServer\Models\KeyModel();
2424

2525
if (!($row = $keyModel->where(config('RestServer')->restKeyColumn, $key)->first())) {
26-
//$authorized = false;
2726
return UnauthorizedException::forInvalidApiKey($key);
2827
}
2928

src/Validators/Limit.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ public static function check(RequestInterface $request, Router $router, ?object
6161
$limit->fill(
6262
[
6363
'uri' => $limited_uri,
64+
'request_id' => $petition->id,
6465
'key_id' => $api_key_id,
6566
'count' => 1,
6667
'hour_started' => time()

src/Validators/Override.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ public static function check(RequestInterface $request, Router $router)
2626
foreach ($requests as $r) {
2727
// @codeCoverageIgnoreStart
2828
if (!$r instanceof PetitionEntity) {
29-
$r = new \Daycry\RestServer\Entities\PetitionEntity((array)$r);
29+
$r = new PetitionEntity((array)$r);
3030
}
3131
// @codeCoverageIgnoreEnd
3232

0 commit comments

Comments
 (0)