Skip to content

Commit

Permalink
Merge pull request #98 from jaredhendrickson13/v117
Browse files Browse the repository at this point in the history
v1.1.7 Fixes & Features
  • Loading branch information
jaredhendrickson13 authored Mar 13, 2021
2 parents f188f36 + 8c2642f commit 7b277bb
Show file tree
Hide file tree
Showing 10 changed files with 363 additions and 121 deletions.
34 changes: 32 additions & 2 deletions pfSense-pkg-API/files/etc/inc/api/framework/APIResponse.inc
Original file line number Diff line number Diff line change
Expand Up @@ -1598,7 +1598,7 @@ function get($id, $data=[], $all=false) {
"status" => "bad request",
"code" => 400,
"return" => $id,
"message" => "User privilege must be type array or string"
"message" => "System users cannot be deleted"
],
5006 => [
"status" => "bad request",
Expand Down Expand Up @@ -1726,6 +1726,36 @@ function get($id, $data=[], $all=false) {
"return" => $id,
"message" => "Authentication server name already in use"
],
5036 => [
"status" => "bad request",
"code" => 400,
"return" => $id,
"message" => "Invalid characters in username"
],
5037 => [
"status" => "bad request",
"code" => 400,
"return" => $id,
"message" => "Username is reserved by the system"
],
5038 => [
"status" => "bad request",
"code" => 400,
"return" => $id,
"message" => "Username cannot contain more than 32 characters"
],
5039 => [
"status" => "bad request",
"code" => 400,
"return" => $id,
"message" => "Invalid characters is IPsec PSK"
],
5040 => [
"status" => "bad request",
"code" => 400,
"return" => $id,
"message" => "User expiration date must be in MM/DD/YYYY format"
],
//6000-6999 reserved for /routing API calls
6000 => [
"status" => "bad request",
Expand Down Expand Up @@ -1904,7 +1934,7 @@ function get($id, $data=[], $all=false) {


];
$response = $responses[(!in_array($id, $responses)) ? $id : 1];
$response = $responses[(array_key_exists($id, $responses)) ? $id : 1];
$response["data"] = $data;
if ($all === true) {
$response = $responses;
Expand Down
1 change: 1 addition & 0 deletions pfSense-pkg-API/files/etc/inc/api/framework/APITools.inc
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ require_once("util.inc");
require_once("interfaces.inc");
require_once("interfaces_fast.inc");
require_once("priv.defs.inc");
require_once("priv.inc");
require_once("service-utils.inc");
require_once("filter.inc");
require_once("shaper.inc");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ class APIFirewallVirtualIPCreate extends APIModel {
if ($this->validated_data["mode"] === "carp") {
# Check for our optional 'vhid' payload value. Assume default if none was specified.
if (isset($this->initial_data['vhid'])) {
if ($this->__vhid_exists($this->initial_data['vhid'])) {
if ($this->__vhid_exists($this->initial_data["interface"], $this->initial_data['vhid'])) {
$this->errors[] = APIResponse\get(4027);
} elseif (1 > $this->initial_data['vhid'] or $this->initial_data['vhid'] > 255) {
$this->errors[] = APIResponse\get(4028);
Expand Down Expand Up @@ -153,14 +153,14 @@ class APIFirewallVirtualIPCreate extends APIModel {
$this->validated_data["type"] = "network";
}

private function __vhid_exists($vhid) {
private function __vhid_exists($interface, $vhid) {
# Loop through each virtual IP and ensure it is not using the requested vhid
foreach ($this->config["virtualip"]["vip"] as $vip) {
if (intval($vhid) === intval($vip["vhid"])) {
if ($interface === $vip["interface"] && intval($vhid) === intval($vip["vhid"])) {
return true;
}
}
return false;
}

}
}
134 changes: 114 additions & 20 deletions pfSense-pkg-API/files/etc/inc/api/models/APIUserCreate.inc
Original file line number Diff line number Diff line change
Expand Up @@ -25,57 +25,151 @@ class APIUserCreate extends APIModel {
}

public function action() {
# Increase the system's next UID by one and add our user to the configuration
$this->config["system"]["nextuid"] = strval(intval($this->validated_data["uid"]) + 1);
$this->config['system']['user'][] = $this->validated_data;
$this->config["system"]["nextuid"] = strval(intval($this->validated_data["uid"]) + 1); // Increase our next UID
local_user_set_password($this->validated_data, $this->validated_data["password"]); // Set our new user's password
local_user_set($this->validated_data);

# Write the user to configuration and set the user on the backend. Return response with created user object.
$this->write_config();
$userindex = index_users(); // Update our user index
local_user_set($this->validated_data);
return APIResponse\get(0, $this->validated_data);
}

public function validate_payload() {
$this->validated_data["uid"] = $this->config["system"]["nextuid"]; // Save our next UID
private function __validate_username() {
# Check for our required `username` payload value
if (isset($this->initial_data['username'])) {
// Check that our user already exists
if (array_key_exists($this->initial_data['username'], index_users())) {
$this->errors[] = APIResponse\get(5002);
# Ensure a user with this username does not already exist
if (!array_key_exists($this->initial_data['username'], index_users())) {
# Ensure the username does not contain invalid characters
if (!preg_match("/[^a-zA-Z0-9\.\-_]/", $this->initial_data['username'])) {
# Ensure username is not reserved by the system
if (!$this->is_username_reserved($this->initial_data["username"])) {
# Ensure username is not longer that 32 characters
if (strlen($this->initial_data["username"]) <= 32) {
$this->validated_data["name"] = $this->initial_data['username'];
} else {
$this->errors[] = APIResponse\get(5038);
}
} else {
$this->errors[] = APIResponse\get(5037);
}
} else {
$this->errors[] = APIResponse\get(5036);
}
} else {
$this->validated_data["name"] = trim($this->initial_data['username']);
$this->errors[] = APIResponse\get(5002);
}
} else {
$this->errors[] = APIResponse\get(5000);
}
}

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

private function __validate_priv() {
global $priv_list;
$this->validated_data["priv"] = [];

# Check for our optional `priv` payload value
if ($this->initial_data["priv"]) {
# Ensure value is an array
if (!is_array($this->initial_data["priv"])) {
$this->initial_data["priv"] = array($this->initial_data["priv"]);
}

# Loop through each requested privilege and ensure it exists
foreach ($this->initial_data["priv"] as $priv) {
if (array_key_exists($priv, $priv_list)) {
$this->validated_data["priv"][] = $priv;
$this->validated_data["priv"] = array_unique($this->validated_data["priv"]);
} else {
$this->errors[] = APIResponse\get(5006);
break;
}
}
}
}

private function __validate_disabled() {
# Check for our optional `disabled` payload value
if ($this->initial_data["disabled"] === true) {
$this->validated_data["disabled"] = ""; // Update our user's disabled value if not false
} elseif ($this->initial_data["disabled"] === false) {
unset($this->validated_data["disabled"]); // Unset our disabled value if not requested
$this->validated_data["disabled"] = "";
}
}

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

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

private function __validate_authorizedkeys() {
# Check for our optional `authorizedkeys` payload value
if (isset($this->initial_data['authorizedkeys'])) {
$this->validated_data["authorizedkeys"] = trim($this->initial_data['authorizedkeys']); // Update our user's authorized keys
$this->validated_data["authorizedkeys"] = base64_encode($this->initial_data['authorizedkeys']);
}
}

private function __validate_ipsecpsk() {
# Check for our optional `ipsecpsk` payload value
if (isset($this->initial_data['ipsecpsk'])) {
$this->validated_data["ipsecpsk"] = trim($this->initial_data['ipsecpsk']); // Update our user's IPsec pre-shared key
# Ensure the PSK does not contain invalid characters
if (preg_match('/^[[:ascii:]]*$/', $_POST['ipsecpsk'])) {
$this->validated_data["ipsecpsk"] = $this->initial_data['ipsecpsk'];
} else {
$this->errors[] = APIResponse\get(5039);
}
}
}

$this->validated_data["scope"] = "user"; // Set our new user's system scope
$this->validated_data["priv"] = []; // Default our privs to empty array
public function validate_payload() {
# Set static object values
$this->validated_data["uid"] = $this->config["system"]["nextuid"];
$this->validated_data["scope"] = "user";

# Run each validation method
$this->__validate_username();
$this->__validate_password();
$this->__validate_priv();
$this->__validate_descr();
$this->__validate_disabled();
$this->__validate_expires();
$this->__validate_authorizedkeys();
$this->__validate_ipsecpsk();
}

public function is_username_reserved($user) {
# Open the /etc/passwd file to read all system users
$sys_users = explode(PHP_EOL, file_get_contents("/etc/passwd"));

# Loop through each system user and check if the username is reserved
foreach ($sys_users as $sys_user_ent) {
$sys_username = explode(":", $sys_user_ent)[0];
if ($sys_username == $user) {
return true;
}
}
return false;
}
}
37 changes: 24 additions & 13 deletions pfSense-pkg-API/files/etc/inc/api/models/APIUserDelete.inc
Original file line number Diff line number Diff line change
Expand Up @@ -25,26 +25,37 @@ class APIUserDelete extends APIModel {
}

public function action() {
$index_id = index_users()[$this->validated_data["username"]]; // Save our user's index ID number
$del_user = $this->config["system"]["user"][$index_id];
local_user_del($this->config["system"]["user"][$index_id]); // Delete our user on the backend
unset($this->config['system']['user'][$index_id]); // Unset our user from config
$this->config['system']['user'] = array_values($this->config['system']['user']); // Reindex our users
$this->write_config(); // Write our new config
return APIResponse\get(0, $del_user);
# Remove user from backend and remove from config
local_user_del($this->config["system"]["user"][$this->id]);
unset($this->config["system"]["user"][$this->id]);
$this->write_config();
return APIResponse\get(0, $this->validated_data);
}

public function validate_payload() {
if (isset($this->initial_data["username"])) {
if (!array_key_exists($this->initial_data["username"], index_users())) {
private function __validate_username() {
# Check for our required `username` payload value
if (isset($this->initial_data['username'])) {
# Loop through each configured user and check if this user exists
foreach ($this->config["system"]["user"] as $id=>$user) {
if ($this->initial_data["username"] === $user["name"]) {
$this->validated_data = $user;
$this->id = intval($id);
}
}
# Set an error if no user was found
if (!isset($this->validated_data["uid"])) {
$this->errors[] = APIResponse\get(5001);
} else {
$this->validated_data["username"] = $this->initial_data['username'];
$this->validated_data["username"] = trim($this->validated_data["username"]);
}
# Set an error if this is a system user
if ($this->validated_data["scope"] !== "user") {
$this->errors[] = APIResponse\get(5005);
}
} else {
$this->errors[] = APIResponse\get(5000);
}
}

public function validate_payload() {
$this->__validate_username();
}
}
Loading

0 comments on commit 7b277bb

Please sign in to comment.