Skip to content

Commit a32321a

Browse files
Restructured APIUserCreateModel to fix #95 bug that prevent password from being set
1 parent f188f36 commit a32321a

3 files changed

Lines changed: 121 additions & 20 deletions

File tree

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

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1726,6 +1726,36 @@ function get($id, $data=[], $all=false) {
17261726
"return" => $id,
17271727
"message" => "Authentication server name already in use"
17281728
],
1729+
5036 => [
1730+
"status" => "bad request",
1731+
"code" => 400,
1732+
"return" => $id,
1733+
"message" => "Invalid characters in username"
1734+
],
1735+
5037 => [
1736+
"status" => "bad request",
1737+
"code" => 400,
1738+
"return" => $id,
1739+
"message" => "Username is reserved by the system"
1740+
],
1741+
5038 => [
1742+
"status" => "bad request",
1743+
"code" => 400,
1744+
"return" => $id,
1745+
"message" => "Username cannot contain more than 32 characters"
1746+
],
1747+
5039 => [
1748+
"status" => "bad request",
1749+
"code" => 400,
1750+
"return" => $id,
1751+
"message" => "Invalid characters is IPsec PSK"
1752+
],
1753+
5040 => [
1754+
"status" => "bad request",
1755+
"code" => 400,
1756+
"return" => $id,
1757+
"message" => "User expiration date must be in MM/DD/YYYY format"
1758+
],
17291759
//6000-6999 reserved for /routing API calls
17301760
6000 => [
17311761
"status" => "bad request",

pfSense-pkg-API/files/etc/inc/api/models/APIUserCreate.inc

Lines changed: 91 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -25,57 +25,128 @@ class APIUserCreate extends APIModel {
2525
}
2626

2727
public function action() {
28+
# Increase the system's next UID by one and add our user to the configuration
29+
$this->config["system"]["nextuid"] = strval(intval($this->validated_data["uid"]) + 1);
2830
$this->config['system']['user'][] = $this->validated_data;
29-
$this->config["system"]["nextuid"] = strval(intval($this->validated_data["uid"]) + 1); // Increase our next UID
30-
local_user_set_password($this->validated_data, $this->validated_data["password"]); // Set our new user's password
31-
local_user_set($this->validated_data);
31+
32+
# Write the user to configuration and set the user on the backend. Return response with created user object.
3233
$this->write_config();
33-
$userindex = index_users(); // Update our user index
34+
local_user_set($this->validated_data);
3435
return APIResponse\get(0, $this->validated_data);
3536
}
3637

37-
public function validate_payload() {
38-
$this->validated_data["uid"] = $this->config["system"]["nextuid"]; // Save our next UID
38+
private function __validate_username() {
39+
# Check for our required `username` payload value
3940
if (isset($this->initial_data['username'])) {
40-
// Check that our user already exists
41-
if (array_key_exists($this->initial_data['username'], index_users())) {
42-
$this->errors[] = APIResponse\get(5002);
41+
# Ensure a user with this username does not already exist
42+
if (!array_key_exists($this->initial_data['username'], index_users())) {
43+
# Ensure the username does not contain invalid characters
44+
if (!preg_match("/[^a-zA-Z0-9\.\-_]/", $this->initial_data['username'])) {
45+
# Ensure username is not reserved by the system
46+
if (!$this->is_username_reserved($this->initial_data["username"])) {
47+
# Ensure username is not longer that 32 characters
48+
if (strlen($this->initial_data["username"]) > 32) {
49+
$this->validated_data["name"] = $this->initial_data['username'];
50+
} else {
51+
$this->errors[] = APIResponse\get(5038);
52+
}
53+
} else {
54+
$this->errors[] = APIResponse\get(5037);
55+
}
56+
} else {
57+
$this->errors[] = APIResponse\get(5036);
58+
}
4359
} else {
44-
$this->validated_data["name"] = trim($this->initial_data['username']);
60+
$this->errors[] = APIResponse\get(5002);
4561
}
4662
} else {
4763
$this->errors[] = APIResponse\get(5000);
4864
}
65+
}
4966

67+
private function __validate_password() {
68+
# Check for our required `password` payload value
5069
if (isset($this->initial_data['password'])) {
51-
$this->validated_data["password"] = trim($this->initial_data['password']);
70+
# Generate the password hash and add it to our validated data
71+
local_user_set_password($this->validated_data, $this->initial_data['password']);
5272
} else {
5373
$this->errors[] = APIResponse\get(5003);
5474
}
75+
}
5576

77+
private function __validate_disabled() {
78+
# Check for our optional `disabled` payload value
5679
if ($this->initial_data["disabled"] === true) {
57-
$this->validated_data["disabled"] = ""; // Update our user's disabled value if not false
58-
} elseif ($this->initial_data["disabled"] === false) {
59-
unset($this->validated_data["disabled"]); // Unset our disabled value if not requested
80+
$this->validated_data["disabled"] = "";
6081
}
82+
}
6183

84+
private function __validate_descr() {
85+
# Check for our optional `descr` payload value
6286
if (isset($this->initial_data['descr'])) {
63-
$this->validated_data["descr"] = trim($this->initial_data['descr']); // Update our user's full name
87+
$this->validated_data["descr"] = $this->initial_data['descr'];
6488
}
89+
}
6590

91+
private function __validate_expires() {
92+
# Check for our optional `expires` payload value
6693
if (isset($this->initial_data['expires'])) {
67-
$this->validated_data["expires"] = trim($this->initial_data['expires']); // Update our user's expiration date
94+
# Try to format the date string, return an error if the format is invalid
95+
try {
96+
$this->validated_data["expires"] = (new DateTime($this->initial_data['expires']))->format("m/d/Y");
97+
} catch (Exception $e) {
98+
$this->errors[] = APIResponse\get(5040);
99+
}
68100
}
101+
}
69102

103+
private function __validate_authorizedkeys() {
104+
# Check for our optional `authorizedkeys` payload value
70105
if (isset($this->initial_data['authorizedkeys'])) {
71-
$this->validated_data["authorizedkeys"] = trim($this->initial_data['authorizedkeys']); // Update our user's authorized keys
106+
$this->validated_data["authorizedkeys"] = $this->initial_data['authorizedkeys'];
72107
}
108+
}
73109

110+
private function __validate_ipsecpsk() {
111+
# Check for our optional `ipsecpsk` payload value
74112
if (isset($this->initial_data['ipsecpsk'])) {
75-
$this->validated_data["ipsecpsk"] = trim($this->initial_data['ipsecpsk']); // Update our user's IPsec pre-shared key
113+
# Ensure the PSK does not contain invalid characters
114+
if (preg_match('/^[[:ascii:]]*$/', $_POST['ipsecpsk'])) {
115+
$this->validated_data["ipsecpsk"] = $this->initial_data['ipsecpsk'];
116+
} else {
117+
$this->errors[] = APIResponse\get(5039);
118+
}
76119
}
120+
}
121+
122+
public function validate_payload() {
123+
# Set static object values
124+
$this->validated_data["uid"] = $this->config["system"]["nextuid"];
125+
$this->validated_data["scope"] = "user";
126+
$this->validated_data["priv"] = [];
77127

78-
$this->validated_data["scope"] = "user"; // Set our new user's system scope
79-
$this->validated_data["priv"] = []; // Default our privs to empty array
128+
# Run each validation method
129+
$this->__validate_username();
130+
$this->__validate_password();
131+
$this->__validate_descr();
132+
$this->__validate_disabled();
133+
$this->__validate_expires();
134+
$this->__validate_authorizedkeys();
135+
$this->__validate_ipsecpsk();
80136
}
137+
138+
public function is_username_reserved($user) {
139+
# Open the /etc/passwd file to read all system users
140+
$etc_passwd = explode(PHP_EOL, file_get_contents("/etc/passwd"));
141+
142+
# Loop through each system user and check if the username is reserved
143+
foreach ($etc_passwd as $sys_user_ent) {
144+
$sys_username = explode(":", $sys_user_ent)[0];
145+
if ($sys_username == $user) {
146+
return true;
147+
}
148+
}
149+
return false;
150+
}
151+
81152
}
8.56 KB
Binary file not shown.

0 commit comments

Comments
 (0)