Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

v2.2.1 Fixes & Features #586

Merged
merged 7 commits into from
Oct 24, 2024
Merged
6 changes: 3 additions & 3 deletions docs/COMMON_CONTROL_PARAMETERS.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,9 +81,9 @@ parameters you can use:
you want to remove from. Then, make another request for the fields you want to replace.

!!! Notes
- This parameter is only available for `PATCH` requests.
- This parameter is only applicable to array fields.
- If the submitted array values match the existing array values exactly, the API will not make any changes to that field to avoid removing all values unintentionally.
- This parameter is only available for `PATCH` requests.
- This parameter is only applicable to array fields.
- If the submitted array values match the existing array values exactly, the API will not make any changes to that field to avoid removing all values unintentionally.

## reverse

Expand Down
45 changes: 38 additions & 7 deletions pfSense-pkg-RESTAPI/files/usr/local/pkg/RESTAPI/Core/Model.inc
Original file line number Diff line number Diff line change
Expand Up @@ -936,13 +936,8 @@ class Model {

# Loop through each of this Model's Fields and add its value to a serializable array
foreach ($this->get_fields() as $field) {
# Skip adding this field if it's a write only field
if ($this->$field->write_only) {
continue;
}

# Skip adding this field if it is sensitive and sensitive fields are not exposed
if ($this->$field->sensitive and !$expose_sensitive_fields) {
# Skip adding this field if it's hidden
if ($this->is_field_hidden($field)) {
continue;
}

Expand Down Expand Up @@ -1063,6 +1058,42 @@ class Model {
return null;
}

/**
* Determines if a given Model field should be hidden in responses.
* @param string $field The field name to check for sensitivity.
* @return bool Returns true if the field is sensitive, false if it is not.
*/
private function is_field_hidden(string $field): bool {
# Variable
$pkg_config = RESTAPI\Models\RESTAPISettings::get_pkg_config();
$expose_sensitive_fields = $pkg_config['expose_sensitive_fields'] === 'enabled';
$overridden_sensitive_fields = explode(',', $pkg_config['override_sensitive_fields']);
$model_field_mapping = "{$this->get_class_shortname()}:$field";

# Field is hidden if it is 'write_only'
if ($this->$field->write_only) {
return true;
}

# Field is not hidden if the sensitive property is not set
if (!$this->$field->sensitive) {
return false;
}

# Field is not hidden if the 'expose_sensitive_fields' setting is enabled
if ($expose_sensitive_fields) {
return false;
}

# Field is not hidden if the field is in the 'override_sensitive_fields' setting
if (in_array($model_field_mapping, $overridden_sensitive_fields)) {
return false;
}

# Field is hidden if none of the above conditions are met
return true;
}

/**
* Determines if this Model applies changes immediately or if they must applied manually after changes are made.
* This is intended to be used to more accurately determine the 'Applies immediately' value in the API documentation
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use RESTAPI\Models\RESTAPISettingsSync;
*/
class RESTAPISettingsSyncDispatcher extends Dispatcher {
public int $timeout = 60;
public int $max_queue = 1024;

/**
* Starts the REST API settings sync to HA peers.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ class SystemRESTAPISettingsForm extends Form {
'login_protection',
'log_successful_auth',
'expose_sensitive_fields',
'override_sensitive_fields',
],
],
'Advanced Settings' => [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ class DHCPServerStaticMapping extends Model {
$this->config_path = 'staticmap';
$this->subsystem = 'dhcpd';
$this->many = true;
$this->sort_by = ['ipaddr'];

# Define model Fields
$this->mac = new StringField(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ class IPsecPhase1 extends Model {
);
$this->myid_type = new StringField(
required: true,
choices: ['myaddress', 'address', 'fqdn', 'user_fqdn', 'asn1dn', 'keyid tag', 'dyn_dns'],
choices: ['myaddress', 'address', 'fqdn', 'user_fqdn', 'asn1dn', 'keyid tag', 'dyn_dns', 'auto'],
help_text: 'The identifier type used by the local end of the tunnel.',
);
$this->myid_data = new StringField(
Expand All @@ -121,7 +121,7 @@ class IPsecPhase1 extends Model {
);
$this->peerid_type = new StringField(
required: true,
choices: ['any', 'peeraddress', 'address', 'fqdn', 'user_fqdn', 'asn1dn', 'keyid tag', 'dyn_dns'],
choices: ['any', 'peeraddress', 'address', 'fqdn', 'user_fqdn', 'asn1dn', 'keyid tag', 'dyn_dns', 'auto'],
help_text: 'The identifier type used by the remote end of the tunnel.',
);
$this->peerid_data = new StringField(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ class RESTAPISettings extends Model {
public BooleanField $allow_pre_releases;
public BooleanField $hateoas;
public BooleanField $expose_sensitive_fields;
public StringField $override_sensitive_fields;
public InterfaceField $allowed_interfaces;
public StringField $represent_interfaces_as;
public StringField $auth_methods;
Expand Down Expand Up @@ -119,6 +120,15 @@ class RESTAPISettings extends Model {
help_text: 'Enables or disables exposing sensitive fields in API responses. When enabled, sensitive fields ' .
'such as passwords, private keys, and other sensitive data will be included in API responses.',
);
$this->override_sensitive_fields = new StringField(
default: [],
choices_callable: 'get_override_sensitive_fields_choices',
many: true,
conditions: ['expose_sensitive_fields' => false],
help_text: 'Specifies a list of fields (formatted as ModelName:FieldName) that should have their sensitive ' .
'attribute overridden. Fields selected here will not be considered sensitive and will be included in ' .
'API responses regardless of the `expose_sensitive_fields` setting.',
);
$this->allowed_interfaces = new InterfaceField(
default: [],
allow_localhost_interface: true,
Expand Down Expand Up @@ -215,6 +225,28 @@ class RESTAPISettings extends Model {
return $choices;
}

/**
* Obtains the choices for the `override_sensitive_fields` field. This will dynamically populate a choice for each
* Model and Field in the \RESTAPI\Models namespace.
*/
public function get_override_sensitive_fields_choices(): array {
# Variables
$choices = [];

# Format choices so they include the class's verbose name as the choice's verbose name
foreach (get_classes_from_namespace(namespace: '\\RESTAPI\\Models\\') as $model_class) {
$model = new $model_class();
foreach ($model->get_fields() as $field) {
if ($model->$field->sensitive) {
$choices[$model->get_class_shortname() . ':' . $field] =
$model->get_class_shortname() . ':' . $field;
}
}
}

return $choices;
}

/**
* Creates a backup of the current API configuration at /usr/local/share/pfSense-pkg-RESTAPI/backup.json.
* @returns integer Returns 0 when backup was successful, or -1 when the API's `keep_backup` field is disabled.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -567,4 +567,26 @@ class APIModelsAPISettingsTestCase extends TestCase {
$user = new User(id: 0);
$this->assert_is_empty($user->to_representation()['password']);
}

/**
* Checks that the `override_sensitive_fields` field successfully overrides specified sensitive fields in the Model's
* representation.
*/
public function test_override_sensitive_fields(): void {
# Ignore the sensitive flag for the User model's password field
$api_settings = new RESTAPISettings(override_sensitive_fields: ['User:password']);
$api_settings->update();

# Ensure we can now read the password field in the User model's representation
$user = new User(id: 0);
$this->assert_is_not_empty($user->to_representation()['password']);

# Reset the override_sensitive_fields setting
$api_settings = new RESTAPISettings(override_sensitive_fields: []);
$api_settings->update();

# Ensure we can no longer read the password
$user = new User(id: 0);
$this->assert_is_empty($user->to_representation()['password']);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
<log_successful_auth>disabled</log_successful_auth>
<hateoas>disabled</hateoas>
<expose_sensitive_fields>disabled</expose_sensitive_fields>
<override_sensitive_fields></override_sensitive_fields>
<allow_pre_releases>disabled</allow_pre_releases>
<allowed_interfaces></allowed_interfaces>
<represent_interfaces_as>id</represent_interfaces_as>
Expand Down
3 changes: 3 additions & 0 deletions tools/make_package.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@ def __init__(self):
self.port_version = self.args.tag.split("_")[0]
self.port_revision = self.args.tag.split("_", maxsplit=1)[1]

# Allow package to build on systems that no longer have upstream ports support
os.environ["ALLOW_UNSUPPORTED_SYSTEM"] = "yes"

# Run tasks for build mode
if self.args.host:
self.build_on_remote_host()
Expand Down