Skip to content

Commit 8556c83

Browse files
Merge pull request #710 from vitspec99/FreeRADIUS
FreeRADIUS Interface(s) and Client(s)
2 parents 4b40141 + 6b19ba7 commit 8556c83

File tree

6 files changed

+416
-0
lines changed

6 files changed

+416
-0
lines changed
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
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 a single Client Model object at
11+
* /api/v2/services/freeradius/client
12+
*/
13+
class ServicesFreeRADIUSClientEndpoint extends Endpoint {
14+
public function __construct() {
15+
/**
16+
* Set Endpoint attributes
17+
*/
18+
$this->url = '/api/v2/services/freeradius/client';
19+
$this->model_name = 'FreeRADIUSClient';
20+
$this->many = false;
21+
$this->request_method_options = ['GET', 'POST', 'DELETE'];
22+
23+
# Construct the parent Endpoint object
24+
parent::__construct();
25+
}
26+
}
27+
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
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 a many Client Model object at
11+
* /api/v2/services/freeradius/clients
12+
*/
13+
class ServicesFreeRADIUSClientsEndpoint extends Endpoint {
14+
public function __construct() {
15+
/**
16+
* Set Endpoint attributes
17+
*/
18+
$this->url = '/api/v2/services/freeradius/clients';
19+
$this->model_name = 'FreeRADIUSClient';
20+
$this->many = true;
21+
$this->request_method_options = ['GET'];
22+
23+
# Construct the parent Endpoint object
24+
parent::__construct();
25+
}
26+
}
27+
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
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 a single interface Model object at
11+
* /api/v2/services/freeradius/interface
12+
*/
13+
class ServicesFreeRADIUSInterfaceEndpoint extends Endpoint {
14+
public function __construct() {
15+
/**
16+
* Set Endpoint attributes
17+
*/
18+
$this->url = '/api/v2/services/freeradius/interface';
19+
$this->model_name = 'FreeRADIUSInterface';
20+
$this->many = false;
21+
$this->request_method_options = ['GET', 'POST', 'DELETE'];
22+
23+
# Construct the parent Endpoint object
24+
parent::__construct();
25+
}
26+
}
27+
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
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 a many interface Model object at
11+
* /api/v2/services/freeradius/interfaces
12+
*/
13+
class ServicesFreeRADIUSInterfacesEndpoint extends Endpoint {
14+
public function __construct() {
15+
/**
16+
* Set Endpoint attributes
17+
*/
18+
$this->url = '/api/v2/services/freeradius/interfaces';
19+
$this->model_name = 'FreeRADIUSInterface';
20+
$this->many = true;
21+
$this->request_method_options = ['GET'];
22+
23+
# Construct the parent Endpoint object
24+
parent::__construct();
25+
}
26+
}
27+
Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
<?php
2+
3+
namespace RESTAPI\Models;
4+
5+
require_once 'RESTAPI/autoloader.inc';
6+
7+
use RESTAPI\Core\Model;
8+
use RESTAPI\Fields\Base64Field;
9+
use RESTAPI\Fields\BooleanField;
10+
use RESTAPI\Fields\ForeignModelField;
11+
use RESTAPI\Fields\IntegerField;
12+
use RESTAPI\Fields\PortField;
13+
use RESTAPI\Fields\ObjectField;
14+
use RESTAPI\Fields\StringField;
15+
use RESTAPI\Responses\ConflictError;
16+
use RESTAPI\Responses\ValidationError;
17+
use RESTAPI\Responses\ServerError;
18+
use RESTAPI\Validators\HostnameValidator;
19+
use RESTAPI\Validators\IPAddressValidator;
20+
use RESTAPI\Validators\RegexValidator;
21+
22+
/**
23+
* Defines a Model that represents FreeRADIUS Interfaces
24+
*/
25+
class FreeRADIUSClient extends Model {
26+
27+
public StringField $addr;
28+
public PortField $port;
29+
public StringField $type;
30+
public StringField $ipv;
31+
public StringField $description;
32+
33+
/**
34+
*
35+
*/
36+
public function __construct(mixed $id = null, mixed $parent_id = null, mixed $data = [], mixed ...$options) {
37+
#
38+
# Set model attributes
39+
#
40+
$this->packages = ['pfSense-pkg-freeradius3'];
41+
$this->package_includes = ['freeradius.inc'];
42+
$this->config_path = 'installedpackages/freeradiusclients/config';
43+
$this->many = true;
44+
$this->always_apply = true;
45+
46+
#
47+
# Set model fields
48+
#
49+
$this->addr = new StringField(
50+
internal_name: 'varclientip',
51+
required: true,
52+
unique: true,
53+
validators: [new IPAddressValidator(allow_ipv4: true, allow_ipv6: true)],
54+
help_text: 'The IP address or network of the RADIUS client(s) in CIDR notation. This is the IP of the NAS (switch, access point, firewall, router, etc.)'
55+
);
56+
$this->ipv = new StringField(
57+
internal_name: 'varclientipversion',
58+
choices: [ 'ipaddr', 'ipv6addr' ],
59+
allow_empty: true,
60+
default: 'ipaddr',
61+
help_text: 'The IP version of the this Client.'
62+
);
63+
$this->shortname = new StringField(
64+
internal_name: 'varclientshortname',
65+
required: true,
66+
allow_null: false,
67+
help_text: 'A short name for the client. This is generally the hostname of the NAS.'
68+
);
69+
$this->secret = new StringField(
70+
internal_name: 'varclientsharedsecret',
71+
required: true,
72+
sensitive: true,
73+
allow_empty: false,
74+
help_text: 'This is the shared secret (password) which the NAS (switch, accesspoint, etc.) needs to communicate with the RADIUS server.'
75+
);
76+
77+
$this->proto = new StringField(
78+
internal_name: 'varclientproto',
79+
choices: ['udp', 'tcp'],
80+
allow_empty: true,
81+
default: 'udp',
82+
help_text: 'The protocol the client uses. (Default: udp)'
83+
);
84+
$this->nastype = new StringField(
85+
internal_name: 'varclientnastype',
86+
choices: ['cisco', 'cvx', 'computone', 'digitro', 'livingston', 'juniper', 'max40xx', 'mikrotik', 'mikrotik_snmp', 'dot1x', 'other'],
87+
allow_empty: true,
88+
default: 'other',
89+
help_text: 'The NAS type of the client. This is used by checkrad.pl for simultaneous use checks. (Default: other)'
90+
);
91+
$this->msgauth = new StringField(
92+
internal_name: 'varrequiremessageauthenticator',
93+
choices: ['yes', 'no'],
94+
default: 'no',
95+
help_text: 'RFC5080 requires Message-Authenticator in Access-Request. But older NAS (switches or accesspoints) do not include that. (Default: no)'
96+
);
97+
$this->maxconn = new IntegerField(
98+
internal_name: 'varclientmaxconnections',
99+
minimum: 1,
100+
maximum: 32,
101+
default: 16,
102+
help_text: 'Takes only effect if you use TCP as protocol. Limits the number of simultaneous TCP connections from a client. (max=32)'
103+
);
104+
$this->naslogin = new StringField(
105+
internal_name: 'varclientlogininput',
106+
allow_empty: true,
107+
default: '',
108+
help_text: 'If supported by your NAS, you can use SNMP or finger for simultaneous-use checks instead of (s)radutmp file and accounting. Leave empty to choose (s)radutmp. (Default: empty) '
109+
);
110+
$this->naspassword= new StringField(
111+
internal_name: 'varclientpasswordinput',
112+
allow_empty: true,
113+
default: '',
114+
sensitive: true,
115+
help_text: 'If supported by your NAS, you can use SNMP or finger for simultaneous-use checks instead of (s)radutmp file and accounting. Leave empty to choose
116+
(s)radutmp. (Default: empty) '
117+
);
118+
119+
$this->description = new StringField(
120+
required: false,
121+
allow_empty: true,
122+
default: "",
123+
validators: [
124+
new RegexValidator(pattern: "/^[a-zA-Z0-9 _,.;:+=()-]*$/", error_msg: 'Value contains invalid characters.'),
125+
],
126+
help_text: 'The description for this interface.'
127+
);
128+
129+
parent::__construct($id, $parent_id, $data, ...$options);
130+
}
131+
132+
133+
/**
134+
* Perform additional validation on the Model's fields and data.
135+
*/
136+
public function validate_extra(): void {
137+
$input_errors = [];
138+
139+
/*
140+
*/
141+
$iface_addr = $this->addr->value;
142+
if ( $iface_addr != '*' ) {
143+
if ( is_ipaddrv4($iface_addr) ) {
144+
$this->ipv->value = 'ipaddr';
145+
} elseif ( is_ipaddrv6($iface_addr) ) {
146+
$this->ipv->value = 'ipv6addr';
147+
} else {
148+
// we don't must be here because Model validator for $this->addr
149+
$input_errors[] = "Cann't recognize IP-address={$iface_addr}";
150+
}
151+
}
152+
153+
# Run service level validations
154+
$client = $this->to_internal();
155+
freeradius_validate_clients($iface, $input_errors);
156+
157+
# If there were validation errors that were not caught by the model fields, throw a ValidationError.
158+
# Ideally the Model should catch all validation errors itself so prompt the user to report this error
159+
if (!empty($input_errors)) {
160+
throw new ValidationError(
161+
message: "An unexpected validation error has occurred: $input_errors[0]. Please report this issue at " .
162+
'https://github.com/jaredhendrickson13/pfsense-api/issues/new',
163+
response_id: 'FREERADIUS_USER_UNEXPECTED_VALIDATION_ERROR',
164+
);
165+
}
166+
}
167+
168+
169+
/**
170+
* Apply specific action on Client(s)
171+
*/
172+
public function apply() {
173+
freeradius_clients_resync();
174+
}
175+
}

0 commit comments

Comments
 (0)