Skip to content

Commit e81f1d1

Browse files
feat: add models, endpoints and tests for system tables
1 parent f976673 commit e81f1d1

File tree

4 files changed

+200
-0
lines changed

4 files changed

+200
-0
lines changed
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<?php
2+
3+
namespace RESTAPI\Endpoints;
4+
5+
require_once 'RESTAPI/autoloader.inc';
6+
7+
use RESTAPI\Core\Endpoint;
8+
9+
/**
10+
* Defines an Endpoint for interacting with singular Table Model objects at /api/v2/diagnostics/table.
11+
*/
12+
class DiagnosticsTableEndpoint extends Endpoint {
13+
public function __construct() {
14+
# Set Endpoint attributes
15+
$this->url = '/api/v2/diagnostics/table';
16+
$this->model_name = 'Table';
17+
$this->request_method_options = ['GET', 'DELETE'];
18+
19+
# Set help texts
20+
$this->get_help_text = 'Retrieves the entries in a specified table.';
21+
$this->delete_help_text =
22+
'Flushes all entries in a specified table. Please note this does not ' .
23+
'delete the table itself, only its entries.';
24+
25+
# Construct the parent Endpoint object
26+
parent::__construct();
27+
}
28+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<?php
2+
3+
namespace RESTAPI\Endpoints;
4+
5+
require_once 'RESTAPI/autoloader.inc';
6+
7+
use RESTAPI\Core\Endpoint;
8+
9+
/**
10+
* Defines an Endpoint for interacting with multiple Table Model objects at /api/v2/diagnostics/tables.
11+
*/
12+
class DiagnosticsTablesEndpoint extends Endpoint {
13+
public function __construct() {
14+
# Set Endpoint attributes
15+
$this->url = '/api/v2/diagnostics/tables';
16+
$this->model_name = 'Table';
17+
$this->request_method_options = ['GET'];
18+
$this->many = true;
19+
20+
# Set help texts
21+
$this->get_help_text =
22+
'Retrieves the entries from all tables. For systems with a large number of tables or ' .
23+
'entries, it is recommended to use pagination and/or querying to limit the amount of data returned.';
24+
25+
# Construct the parent Endpoint object
26+
parent::__construct();
27+
}
28+
}
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
<?php
2+
3+
namespace RESTAPI\Models;
4+
5+
use RESTAPI\Core\Command;
6+
use RESTAPI\Core\Model;
7+
use RESTAPI\Fields\StringField;
8+
use RESTAPI\Responses\ServerError;
9+
10+
/**
11+
* Defines a Model for interacting with pfSense tables.
12+
*/
13+
class Table extends Model {
14+
public StringField $entries;
15+
16+
public function __construct(mixed $id = null, mixed $parent_id = null, mixed $data = [], ...$options) {
17+
# Set model attributes
18+
$this->internal_callable = 'get_tables';
19+
$this->id_type = 'string';
20+
$this->many = true;
21+
22+
$this->entries = new StringField(many: true, delimiter: ' ', help_text: 'The entries currently in the table.');
23+
24+
parent::__construct($id, $parent_id, $data, ...$options);
25+
}
26+
27+
/**
28+
* Retrieves the list of available tables from the pfctl command.
29+
* @return array The list of available table names.
30+
*/
31+
public function get_available_table_names(): array {
32+
$table_names_ouptput = new Command('/sbin/pfctl -sT');
33+
return explode("\n", $table_names_ouptput->output);
34+
}
35+
36+
/**
37+
* Obtains the auth log as an array. This method is the internal callable for this Model.
38+
* @return array The auth log as an array of objects.
39+
*/
40+
protected function get_tables(): array {
41+
$tables = [];
42+
43+
# Loop through each table and expand its entries
44+
foreach ($this->get_available_table_names() as $table_name) {
45+
# Get the entries for the table
46+
$table_entries_output = new Command(
47+
'/sbin/pfctl -t ' . escapeshellarg($table_name) . ' -T show',
48+
trim_whitespace: true,
49+
);
50+
$tables[$table_name] = ['entries' => trim($table_entries_output->output)];
51+
}
52+
53+
return $tables;
54+
}
55+
56+
/**
57+
* Overrides the default _delete method to flush the table entries instead.
58+
*/
59+
protected function _delete(): void {
60+
# Flush the table entries using pfctl
61+
$flush_command = new Command('/sbin/pfctl -t ' . escapeshellarg($this->id) . ' -T flush');
62+
if ($flush_command->result_code !== 0) {
63+
throw new ServerError(
64+
message: 'Failed to flush table entries for ' . $this->id . ': ' . $flush_command->output,
65+
response_id: 'TABLE_FLUSH_FAILED',
66+
);
67+
}
68+
}
69+
}
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
<?php
2+
3+
namespace RESTAPI\Tests;
4+
5+
use RESTAPI\Core\Command;
6+
use RESTAPI\Core\TestCase;
7+
use RESTAPI\Models\CARP;
8+
use RESTAPI\Models\Table;
9+
use RESTAPI\Models\VirtualIP;
10+
11+
class APIModelsTableTestCase extends TestCase {
12+
/**
13+
* Checks that we can successful retrieve the list of available table names.
14+
*/
15+
public function test_get_available_table_names(): void {
16+
# Create a new pf table to test with
17+
new Command('/sbin/pfctl -t pfrest_test_table -T add 1.2.3.4');
18+
19+
# Ensure get_available_table_names returns the test table
20+
$table = new Table();
21+
$this->assert_is_true(
22+
in_array('pfrest_test_table', $table->get_available_table_names()),
23+
message: 'The test table should be in the list of available tables.',
24+
);
25+
26+
# Delete the test table after the test
27+
new Command('/sbin/pfctl -t pfrest_test_table -T kill');
28+
29+
# Ensure the test table is no longer available
30+
$this->assert_is_false(
31+
in_array('pfrest_test_table', $table->get_available_table_names()),
32+
message: 'The test table should no longer be in the list of available tables.',
33+
);
34+
}
35+
36+
/**
37+
* Checks that we can successfully read entries from a table
38+
*/
39+
public function test_read(): void {
40+
# Create a new pf table to test with
41+
new Command('/sbin/pfctl -t pfrest_test_table -T add 1.2.3.4 4.3.2.1');
42+
43+
# Load the Table model
44+
$table = new Table(id: 'pfrest_test_table');
45+
46+
# Ensure the table has the expected entries
47+
$this->assert_equals($table->entries->value, ['1.2.3.4', '4.3.2.1']);
48+
49+
# Delete the test table after the test
50+
new Command('/sbin/pfctl -t pfrest_test_table -T kill');
51+
}
52+
53+
/**
54+
* Checks that we can successfully delete (flush) entrries from a table
55+
*/
56+
public function test_delete(): void {
57+
# Create a new pf table to test with
58+
new Command('/sbin/pfctl -t pfrest_test_table -T add 1.2.3.4 4.3.2.1');
59+
60+
# Load the Table model
61+
$table = new Table(id: 'pfrest_test_table');
62+
63+
# Ensure the table entries are present (so we know delete actually flushes them)
64+
$this->assert_equals($table->entries->value, ['1.2.3.4', '4.3.2.1']);
65+
66+
# Delete (flush) the test table and ensure the entries are actually flushed
67+
$table->delete();
68+
$table_show = new Command('/sbin/pfctl -t pfrest_test_table -T show');
69+
$this->assert_str_does_not_contain($table_show->output, '1.2.3.4');
70+
$this->assert_str_does_not_contain($table_show->output, '4.3.2.1');
71+
72+
# Delete the test table after the test
73+
new Command('/sbin/pfctl -t pfrest_test_table -T kill');
74+
}
75+
}

0 commit comments

Comments
 (0)