Skip to content

Commit 340ab54

Browse files
Create API model APIUserAuthServerRADIUSCreate.inc to create RADIUS authentication servers, updated documentation to include instructions
1 parent 4afe54f commit 340ab54

5 files changed

Lines changed: 319 additions & 4 deletions

File tree

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
.idea
2-
*/__pycache__/
2+
tests/unit_test_framework/__pycache__/
33
*.DS_Store
44
.phplint-cache
55

docs/documentation.json

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -291,6 +291,87 @@
291291
},
292292
"response": []
293293
},
294+
{
295+
"name": "Create RADIUS Auth Servers",
296+
"request": {
297+
"method": "POST",
298+
"header": [
299+
{
300+
"key": "Content-Type",
301+
"name": "Content-Type",
302+
"type": "text",
303+
"value": "application/json"
304+
}
305+
],
306+
"body": {
307+
"mode": "raw",
308+
"raw": "{\n \"name\": \"TEST_RADIUS\",\n \"host\": \"123\",\n \"radius_secret\": \"testsecret\",\n \"radius_auth_port\": 1812,\n \"radius_acct_port\": 1813,\n \"radius_protocol\": \"MSCHAPv2\",\n \"radius_timeout\": 5,\n \"radius_nasip_attribute\": \"wan\"\n}"
309+
},
310+
"url": {
311+
"raw": "https://{{$hostname}}/api/v1/user/auth_server/radius?name=string&host=string&radius_secret=string&radius_auth_port=integer&radius_acct_port=integer&radius_timeout=integer&radis_nasip_attribute=string&active=boolean&radius_protocol=string",
312+
"protocol": "https",
313+
"host": [
314+
"{{$hostname}}"
315+
],
316+
"path": [
317+
"api",
318+
"v1",
319+
"user",
320+
"auth_server",
321+
"radius"
322+
],
323+
"query": [
324+
{
325+
"key": "name",
326+
"value": "string",
327+
"description": "Specify a descriptive name for the RADIUS authentication server to create. This name must be unique from all other authentication servers on the system."
328+
},
329+
{
330+
"key": "host",
331+
"value": "string",
332+
"description": "Specify the IP or hostname of the remote RADIUS authentication server."
333+
},
334+
{
335+
"key": "radius_secret",
336+
"value": "string",
337+
"description": "Specify the shared secret of the remote RADIUS authentication server. "
338+
},
339+
{
340+
"key": "radius_auth_port",
341+
"value": "integer",
342+
"description": "Specify the remote RADIUS authentication server's authentication port. If no value is specified, the authentication service on the remote RADIUS server will not be used. This field is optional if a `radius_acct_port` value is specified. (optional)"
343+
},
344+
{
345+
"key": "radius_acct_port",
346+
"value": "integer",
347+
"description": "Specify the remote RADIUS authentication server's accounting port. If no value is specified, the accounting service on the remote RADIUS server will not be used. This field is optional if a `radius_auth_port` value is specified.(optional)"
348+
},
349+
{
350+
"key": "radius_timeout",
351+
"value": "integer",
352+
"description": "Specify the amount of time (in seconds) to wait for the remote RADIUS server to respond before timing out. This value must be `1` or greater. Defaults to `5`. (optional)"
353+
},
354+
{
355+
"key": "radis_nasip_attribute",
356+
"value": "string",
357+
"description": "Specify which Interface's IP address to send in the NAS IP RADIUS attribute. You may specify either the physical Interface ID, the pfSense Interface ID or the descriptive Interface name. Defaults to `wan`. (optional)"
358+
},
359+
{
360+
"key": "active",
361+
"value": "boolean",
362+
"description": "Specify whether pfSense should use this authentication server by default after creation. (optional)"
363+
},
364+
{
365+
"key": "radius_protocol",
366+
"value": "string",
367+
"description": "Specify the RADIUS authentication protocol to use when communicating with the remote RADIUS server. Options are `PAP`, `CHAP_MD5`, `MSCHAPv1` or `MSCHAPv2`. Defaults to `MSCHAPv2`. (optional)"
368+
}
369+
]
370+
},
371+
"description": "Delete an existing RADIUS authentication server.<br><br>\n\n_Requires at least one of the following privileges:_ [`page-all`, `page-system-authservers`]"
372+
},
373+
"response": []
374+
},
294375
{
295376
"name": "Delete Auth Servers",
296377
"request": {

pfSense-pkg-API/files/etc/inc/api/endpoints/APIUserAuthServerRADIUS.inc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,10 @@ class APIUserAuthServerRADIUS extends APIEndpoint {
2424
return (new APIUserAuthServerRADIUSRead())->call();
2525
}
2626

27+
protected function post() {
28+
return (new APIUserAuthServerRADIUSCreate())->call();
29+
}
30+
2731
protected function delete() {
2832
return (new APIUserAuthServerRADIUSDelete())->call();
2933
}

pfSense-pkg-API/files/etc/inc/api/framework/APIResponse.inc

Lines changed: 57 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1712,13 +1712,13 @@ function get($id, $data=[], $all=false) {
17121712
"status" => "bad request",
17131713
"code" => 400,
17141714
"return" => $id,
1715-
"message" => "LDAP authentication server hostname or IP required"
1715+
"message" => "Authentication server hostname or IP required"
17161716
],
17171717
5012 => [
17181718
"status" => "bad request",
17191719
"code" => 400,
17201720
"return" => $id,
1721-
"message" => "Invalid LDAP authentication server hostname or IP"
1721+
"message" => "Invalid authentication server hostname or IP"
17221722
],
17231723
5013 => [
17241724
"status" => "bad request",
@@ -1804,6 +1804,60 @@ function get($id, $data=[], $all=false) {
18041804
"return" => $id,
18051805
"message" => "Authentication server name already in use"
18061806
],
1807+
5027 => [
1808+
"status" => "bad request",
1809+
"code" => 400,
1810+
"return" => $id,
1811+
"message" => "Unsupported RADIUS protocol"
1812+
],
1813+
5028 => [
1814+
"status" => "bad request",
1815+
"code" => 400,
1816+
"return" => $id,
1817+
"message" => "RADIUS secret required"
1818+
],
1819+
5029 => [
1820+
"status" => "bad request",
1821+
"code" => 400,
1822+
"return" => $id,
1823+
"message" => "RADIUS secret must be string type"
1824+
],
1825+
5030 => [
1826+
"status" => "bad request",
1827+
"code" => 400,
1828+
"return" => $id,
1829+
"message" => "Invalid RADIUS authentication port"
1830+
],
1831+
5031 => [
1832+
"status" => "bad request",
1833+
"code" => 400,
1834+
"return" => $id,
1835+
"message" => "Invalid RADIUS accounting port"
1836+
],
1837+
5032 => [
1838+
"status" => "bad request",
1839+
"code" => 400,
1840+
"return" => $id,
1841+
"message" => "RADIUS authentication and/or accounting port required"
1842+
],
1843+
5033 => [
1844+
"status" => "bad request",
1845+
"code" => 400,
1846+
"return" => $id,
1847+
"message" => "RADIUS timeout must be an integer of 1 or greater"
1848+
],
1849+
5034 => [
1850+
"status" => "bad request",
1851+
"code" => 400,
1852+
"return" => $id,
1853+
"message" => "Unknown RADIUS NAS-IP attribute"
1854+
],
1855+
5035 => [
1856+
"status" => "bad request",
1857+
"code" => 400,
1858+
"return" => $id,
1859+
"message" => "Authentication server name is required"
1860+
],
18071861

18081862
//6000-6999 reserved for /routing API calls
18091863
6000 => [
@@ -1983,7 +2037,7 @@ function get($id, $data=[], $all=false) {
19832037

19842038

19852039
];
1986-
$response = $responses[(!in_array($id, $responses)) ? $id : 1];
2040+
$response = $responses[(array_key_exists($id, $responses)) ? $id : 1];
19872041
$response["data"] = $data;
19882042
if ($all === true) {
19892043
$response = $responses;
Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
<?php
2+
// Copyright 2021 Jared Hendrickson
3+
//
4+
// Licensed under the Apache License, Version 2.0 (the "License");
5+
// you may not use this file except in compliance with the License.
6+
// You may obtain a copy of the License at
7+
//
8+
// http://www.apache.org/licenses/LICENSE-2.0
9+
//
10+
// Unless required by applicable law or agreed to in writing, software
11+
// distributed under the License is distributed on an "AS IS" BASIS,
12+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
// See the License for the specific language governing permissions and
14+
// limitations under the License.
15+
16+
require_once("api/framework/APIModel.inc");
17+
require_once("api/framework/APIResponse.inc");
18+
19+
class APIUserAuthServerRADIUSCreate extends APIModel {
20+
# Create our method constructor
21+
public function __construct() {
22+
parent::__construct();
23+
$this->privileges = ["page-all", "page-system-authserver"];
24+
$this->change_note = "Added RADIUS authentication server via API";
25+
}
26+
27+
public function action() {
28+
# Add our new authentication server to the configuration
29+
$this->config["system"]["authserver"][] = $this->validated_data;
30+
31+
# Check if clients wants to set this as default auth server
32+
if ($this->initial_data["active"] === true) {
33+
$this->config['system']['webgui']['authmode'] = $this->validated_data["name"];
34+
}
35+
36+
$this->write_config();
37+
return APIResponse\get(0, $this->validated_data);
38+
}
39+
40+
public function validate_payload() {
41+
# Set static configuration attributes
42+
$this->validated_data["type"] = "radius";
43+
$this->validated_data["refid"] = uniqid();
44+
45+
# Validate input fields
46+
$this->__validate_name();
47+
$this->__validate_host();
48+
$this->__validate_radius_secret();
49+
$this->__validate_radius_auth_port();
50+
$this->__validate_radius_acct_port();
51+
$this->__validate_radius_protocol();
52+
$this->__validate_radius_timeout();
53+
$this->__validate_radius_nasip_attribute();
54+
}
55+
56+
private function __validate_name() {
57+
# Validate our required `name` payload value
58+
if (isset($this->initial_data["name"])) {
59+
# Ensure the name is not already in use
60+
if (!APITools\is_authentication_server($this->initial_data["name"])) {
61+
$this->validated_data["name"] = $this->initial_data["name"];
62+
} else {
63+
$this->errors[] = APIResponse\get(5026);
64+
}
65+
} else {
66+
$this->errors[] = APIResponse\get(5035);
67+
}
68+
}
69+
70+
private function __validate_host() {
71+
# Validate our required `host` payload value
72+
if (isset($this->initial_data["host"])) {
73+
# Ensure the `host` value is a valid IP or hostname
74+
if (is_ipaddr($this->initial_data["host"]) or is_hostname($this->initial_data["host"])) {
75+
$this->validated_data["host"] = $this->initial_data["host"];
76+
} else {
77+
$this->errors[] = APIResponse\get(5012);
78+
}
79+
} else {
80+
$this->errors[] = APIResponse\get(5011);
81+
}
82+
}
83+
84+
private function __validate_radius_secret() {
85+
# Validate our required `radius_secret` payload value
86+
if (isset($this->initial_data["radius_secret"])) {
87+
# Ensure the `radius_secret` is a string
88+
if (is_string($this->initial_data["radius_secret"])) {
89+
$this->validated_data["radius_secret"] = $this->initial_data["radius_secret"];
90+
} else {
91+
$this->errors[] = APIResponse\get(5029);
92+
}
93+
} else {
94+
$this->errors[] = APIResponse\get(5028);
95+
}
96+
}
97+
98+
private function __validate_radius_auth_port() {
99+
# Validate our required `radius_auth_port` payload value
100+
if (isset($this->initial_data["radius_auth_port"])) {
101+
# Ensure the `radius_auth_port` is a port
102+
if (is_port(strval($this->initial_data["radius_auth_port"]))) {
103+
$this->validated_data["radius_auth_port"] = $this->initial_data["radius_auth_port"];
104+
} else {
105+
$this->errors[] = APIResponse\get(5030);
106+
}
107+
}
108+
}
109+
110+
private function __validate_radius_acct_port() {
111+
# Validate our required `radius_acct_port` payload value
112+
if (isset($this->initial_data["radius_acct_port"])) {
113+
# Ensure the `radius_acct_port` is a port
114+
if (is_port(strval($this->initial_data["radius_acct_port"]))) {
115+
$this->validated_data["radius_acct_port"] = $this->initial_data["radius_acct_port"];
116+
} else {
117+
$this->errors[] = APIResponse\get(5031);
118+
}
119+
}
120+
# Throw an error if neither an authentication nor accounting port was provided
121+
elseif (!isset($this->validated_data["radius_auth_port"])) {
122+
$this->errors[] = APIResponse\get(5032);
123+
}
124+
}
125+
126+
private function __validate_radius_protocol() {
127+
# Validate our optional `radius_protocol` payload value
128+
if (isset($this->initial_data["radius_protocol"])) {
129+
# Ensure the value is a valid RADIUS protocol option
130+
if (in_array($this->initial_data["radius_protocol"], ["PAP", "CHAP_MD5", "MSCHAPv1", "MSCHAPv2"])) {
131+
$this->validated_data["radius_protocol"] = $this->initial_data["radius_protocol"];
132+
} else {
133+
$this->errors[] = APIResponse\get(5027);
134+
}
135+
}
136+
# Assume default if none was specified
137+
else {
138+
$this->validated_data["radius_protocol"] = "MSCHAPv2";
139+
}
140+
}
141+
142+
private function __validate_radius_timeout() {
143+
# Validate our required `radius_timeout` payload value
144+
if (isset($this->initial_data["radius_timeout"])) {
145+
# Ensure the `radius_timeout` is an integer of 1 or greater
146+
if (is_integer($this->initial_data["radius_timeout"]) and $this->initial_data["radius_timeout"] >= 1) {
147+
$this->validated_data["radius_timeout"] = $this->initial_data["radius_timeout"];
148+
} else {
149+
$this->errors[] = APIResponse\get(5033);
150+
}
151+
}
152+
# Otherwise assume default value
153+
else {
154+
$this->validated_data["radius_timeout"] = 5;
155+
}
156+
}
157+
158+
private function __validate_radius_nasip_attribute() {
159+
# Validate our required `radius_nasip_attribute` payload value
160+
if (isset($this->initial_data["radius_nasip_attribute"])) {
161+
# Try to locate the interface associated with the client's input
162+
$if = APITools\get_pfsense_if_id($this->initial_data["radius_nasip_attribute"]);
163+
164+
# Ensure the client's input matched an existing interface
165+
if ($if) {
166+
$this->validated_data["radius_nasip_attribute"] = $if;
167+
} else {
168+
$this->errors[] = APIResponse\get(5034);
169+
}
170+
}
171+
# Otherwise assume default value
172+
else {
173+
$this->validated_data["radius_nasip_attribute"] = "wan";
174+
}
175+
}
176+
}

0 commit comments

Comments
 (0)