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

Add settings validation to import #777

Merged
merged 4 commits into from
Feb 6, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 3 additions & 4 deletions lib/WP_Auth0_Embed_Widget.php
Original file line number Diff line number Diff line change
Expand Up @@ -72,11 +72,10 @@ public function update( $new_instance, $old_instance ) {
new WP_Auth0_Routes( WP_Auth0_Options::Instance() )
);

$validated_opts = $admin_advanced->loginredirection_validation(
[ 'default_login_redirection' => $old_instance['redirect_to'] ],
[ 'default_login_redirection' => $new_instance['redirect_to'] ]
$new_instance['redirect_to'] = $admin_advanced->validate_login_redirect(
$new_instance['redirect_to'],
$old_instance['redirect_to']
);
$new_instance['redirect_to'] = $validated_opts['default_login_redirection'];
}

return $new_instance;
Expand Down
15 changes: 13 additions & 2 deletions lib/WP_Auth0_Import_Settings.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,19 @@ public function import_settings() {
exit;
}

foreach ( $settings as $key => $value ) {
$this->a0_options->set( $key, $value, false );
// Keep original settings keys so we only save imported values.
$settings_keys = array_keys( $settings );

$admin = new WP_Auth0_Admin( $this->a0_options, new WP_Auth0_Routes( $this->a0_options ) );

// Default setting values will be added to the array.
$settings_validated = $admin->input_validator( $settings );

foreach ( $settings_keys as $settings_key ) {
// Invalid settings keys are removed in WP_Auth0_Admin::input_validator().
if ( isset( $settings_validated[ $settings_key ] ) ) {
$this->a0_options->set( $settings_key, $settings_validated[ $settings_key ], false );
}
}

$this->a0_options->update_all();
Expand Down
32 changes: 21 additions & 11 deletions lib/admin/WP_Auth0_Admin.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,13 @@ class WP_Auth0_Admin {
public function __construct( WP_Auth0_Options $a0_options, WP_Auth0_Routes $router ) {
$this->a0_options = $a0_options;
$this->router = $router;

$this->sections = [
'basic' => new WP_Auth0_Admin_Basic( $this->a0_options ),
'features' => new WP_Auth0_Admin_Features( $this->a0_options ),
'appearance' => new WP_Auth0_Admin_Appearance( $this->a0_options ),
'advanced' => new WP_Auth0_Admin_Advanced( $this->a0_options, $this->router ),
];
}

/**
Expand Down Expand Up @@ -58,22 +65,17 @@ public function admin_enqueue() {
}

public function init_admin() {
$this->sections['basic'] = new WP_Auth0_Admin_Basic( $this->a0_options );
$this->sections['basic']->init();

$this->sections['features'] = new WP_Auth0_Admin_Features( $this->a0_options );
$this->sections['features']->init();

$this->sections['appearance'] = new WP_Auth0_Admin_Appearance( $this->a0_options );
$this->sections['appearance']->init();

$this->sections['advanced'] = new WP_Auth0_Admin_Advanced( $this->a0_options, $this->router );
$this->sections['advanced']->init();
foreach ( $this->sections as $section ) {
$section->init();
}

register_setting(
$this->a0_options->get_options_name() . '_basic',
$this->a0_options->get_options_name(),
[ $this, 'input_validator' ]
[
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Third argument as a callable was a backwards-compatible change. Now accepts an array:

https://developer.wordpress.org/reference/functions/register_setting/

'sanitize_callback' => [ $this, 'input_validator' ],
]
);
}

Expand All @@ -93,6 +95,14 @@ public function input_validator( array $input ) {
$input[ $key ] = $this->a0_options->get_constant_val( $key );
}

// Remove unknown keys.
$option_keys = $this->a0_options->get_defaults( true );
foreach ( $input as $key => $val ) {
if ( ! in_array( $key, $option_keys ) ) {
unset( $input[ $key ] );
}
}

foreach ( $this->sections as $name => $section ) {
$input = $section->input_validator( $input );
}
Expand Down
49 changes: 20 additions & 29 deletions lib/admin/WP_Auth0_Admin_Advanced.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ public function __construct( WP_Auth0_Options $options, WP_Auth0_Routes $router
$this->router = $router;
$this->actions_middlewares[] = 'migration_ws_validation';
$this->actions_middlewares[] = 'migration_ips_validation';
$this->actions_middlewares[] = 'loginredirection_validation';
}

/**
Expand Down Expand Up @@ -347,18 +346,17 @@ public function render_auth0_server_domain( $args = [] ) {
/**
* Validate all settings without a specific validation method.
*
* @param array $old_options - Option values before savings.
* @param array $input - New option values to validate.
*
* @return array
*/
public function basic_validation( array $old_options, array $input ) {
$input['requires_verified_email'] = $this->sanitize_switch_val( $input['requires_verified_email'] ?? null );
$input['skip_strategies'] = $this->sanitize_text_val( $input['skip_strategies'] ?? null );
$input['remember_users_session'] = $this->sanitize_switch_val( $input['remember_users_session'] ?? null );
// `default_login_redirection` is sanitized in $this->loginredirection_validation() below.
$input['force_https_callback'] = $this->sanitize_switch_val( $input['force_https_callback'] ?? null );
$input['auto_provisioning'] = $this->sanitize_switch_val( $input['auto_provisioning'] ?? null );
public function basic_validation( array $input ) {
$input['requires_verified_email'] = $this->sanitize_switch_val( $input['requires_verified_email'] ?? null );
$input['skip_strategies'] = $this->sanitize_text_val( $input['skip_strategies'] ?? null );
$input['remember_users_session'] = $this->sanitize_switch_val( $input['remember_users_session'] ?? null );
$input['default_login_redirection'] = $this->validate_login_redirect( $input['default_login_redirection'] ?? null );
$input['force_https_callback'] = $this->sanitize_switch_val( $input['force_https_callback'] ?? null );
$input['auto_provisioning'] = $this->sanitize_switch_val( $input['auto_provisioning'] ?? null );
// `migration_ws` is sanitized in $this->migration_ws_validation() below.
// `migration_token` is sanitized in $this->migration_ws_validation() below.
$input['migration_ips_filter'] = $this->sanitize_switch_val( $input['migration_ips_filter'] ?? null );
Expand All @@ -372,12 +370,11 @@ public function basic_validation( array $old_options, array $input ) {
* Validation for the migration_ws setting.
* Generates new migration tokens if none is present.
*
* @param array $old_options - Option values before savings.
* @param array $input - New option values to validate.
*
* @return array
*/
public function migration_ws_validation( array $old_options, array $input ) {
public function migration_ws_validation( array $input ) {
$input['migration_ws'] = $this->sanitize_switch_val( $input['migration_ws'] ?? null );
$input['migration_token'] = $this->options->get( 'migration_token' );

Expand Down Expand Up @@ -416,12 +413,11 @@ public function migration_ws_validation( array $old_options, array $input ) {
* Validation for the migration_ips setting.
* Generates new migration tokens if none is present.
*
* @param array $old_options - Option values before savings.
* @param array $input - New option values to validate.
*
* @return array
*/
public function migration_ips_validation( array $old_options, array $input ) {
public function migration_ips_validation( array $input ) {

if ( empty( $input['migration_ips'] ) ) {
$input['migration_ips'] = '';
Expand All @@ -446,45 +442,40 @@ public function migration_ips_validation( array $old_options, array $input ) {
/**
* Validate the URL used to redirect users after a successful login.
*
* @param array $old_options - Previously-saved options.
* @param array $input - Options to save.
* @param string $new_url - Options to save.
* @param string|null $existing_url - Value to fall back on if new value does not validate.
*
* @return array
* @return string
*/
public function loginredirection_validation( $old_options, $input ) {
$new_redirect_url = esc_url_raw( strtolower( $input['default_login_redirection'] ) );
$old_redirect_url = strtolower( $old_options['default_login_redirection'] );
public function validate_login_redirect( $new_url, $existing_url = null ) {
$new_redirect_url = esc_url_raw( strtolower( $new_url ) );
$old_redirect_url = $existing_url ?? $this->options->get( 'default_login_redirection' );

// No change so no validation needed.
if ( $new_redirect_url === $old_redirect_url ) {
return $input;
if ( $new_redirect_url === strtolower( $old_redirect_url ) ) {
return $new_url;
}

$home_url = home_url();

// Set the default redirection URL to be the homepage.
if ( empty( $new_redirect_url ) ) {
$input['default_login_redirection'] = $home_url;
return $input;
return $home_url;
}

// Allow subdomains within the same domain.
$home_domain = $this->get_domain( $home_url );
$redirect_domain = $this->get_domain( $new_redirect_url );
if ( $home_domain === $redirect_domain ) {
return $input;
return $new_url;
}

// If we get here, the redirect URL is a page outside of the WordPress install.
$error = __( 'Advanced > "Login Redirection URL" cannot point to another site.', 'wp-auth0' );
$this->add_validation_error( $error );

// Either revert to the previous (validated) value or set as the homepage.
$input['default_login_redirection'] = ! empty( $old_options['default_login_redirection'] ) ?
$old_options['default_login_redirection'] :
$home_url;

return $input;
return ! empty( $old_redirect_url ) ? $old_redirect_url : $home_url;
}

/**
Expand Down
3 changes: 1 addition & 2 deletions lib/admin/WP_Auth0_Admin_Appearance.php
Original file line number Diff line number Diff line change
Expand Up @@ -284,12 +284,11 @@ public function render_extra_conf( $args = [] ) {
/**
* Validation for Basic settings tab.
*
* @param array $old_options - Options before saving the settings form.
* @param array $input - New options being saved.
*
* @return array
*/
public function basic_validation( $old_options, $input ) {
public function basic_validation( array $input ) {
$input['passwordless_enabled'] = $this->sanitize_switch_val( $input['passwordless_enabled'] ?? null );

$input['icon_url'] = esc_url_raw( $this->sanitize_text_val( $input['icon_url'] ?? null ) );
Expand Down
5 changes: 2 additions & 3 deletions lib/admin/WP_Auth0_Admin_Basic.php
Original file line number Diff line number Diff line change
Expand Up @@ -289,12 +289,11 @@ public function render_allow_signup() {
/**
* Validation for Basic settings tab.
*
* @param array $old_options - Options before saving the settings form.
* @param array $input - New options being saved.
*
* @return array
*/
public function basic_validation( $old_options, $input ) {
public function basic_validation( array $input ) {

if ( wp_cache_get( 'doing_db_update', WPA0_CACHE_GROUP ) ) {
return $input;
Expand All @@ -315,7 +314,7 @@ public function basic_validation( $old_options, $input ) {
$input['client_secret'] = $this->sanitize_text_val( $input['client_secret'] ?? null );
if ( __( '[REDACTED]', 'wp-auth0' ) === $input['client_secret'] ) {
// The field is loaded with "[REDACTED]" so if that value is saved, we keep the existing secret.
$input['client_secret'] = $old_options['client_secret'];
$input['client_secret'] = $this->options->get( 'client_secret' );
}
if ( empty( $input['client_secret'] ) ) {
$this->add_validation_error( __( 'You need to specify a Client Secret', 'wp-auth0' ) );
Expand Down
3 changes: 1 addition & 2 deletions lib/admin/WP_Auth0_Admin_Features.php
Original file line number Diff line number Diff line change
Expand Up @@ -126,12 +126,11 @@ public function render_override_wp_avatars( $args = [] ) {
/**
* Validation for Basic settings tab.
*
* @param array $old_options - Options before saving the settings form.
* @param array $input - New options being saved.
*
* @return array
*/
public function basic_validation( array $old_options, array $input ) {
public function basic_validation( array $input ) {
$input['auto_login'] = $this->sanitize_switch_val( $input['auto_login'] ?? null );
$input['auto_login_method'] = $this->sanitize_text_val( $input['auto_login_method'] ?? null );
$input['singlelogout'] = $this->sanitize_switch_val( $input['singlelogout'] ?? null );
Expand Down
7 changes: 2 additions & 5 deletions lib/admin/WP_Auth0_Admin_Generic.php
Original file line number Diff line number Diff line change
Expand Up @@ -66,13 +66,10 @@ protected function init_option_section( $section_name, $id, $options ) {
}
}

public function input_validator( $input, $old_options = null ) {
if ( empty( $old_options ) ) {
$old_options = $this->options->get_options();
}
public function input_validator( $input ) {

foreach ( $this->actions_middlewares as $action ) {
$input = $this->$action( $old_options, $input );
$input = $this->$action( $input );
}

return $input;
Expand Down
8 changes: 7 additions & 1 deletion templates/import_settings.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
<div class="container-fluid">

<h1><?php _e( 'Import and Export Settings', 'wp-auth0' ); ?></h1>

<p class="a0-step-text top-margin">
<?php _e( 'You can import and export your Auth0 WordPress plugin settings here. ', 'wp-auth0' ); ?>
<?php _e( 'This allows you to either backup the data, or to move your settings to a new WordPress instance.', 'wp-auth0' ); ?>
Expand All @@ -28,7 +29,12 @@
<form action="options.php" method="post" enctype="multipart/form-data">
<input type="hidden" name="action" value="wpauth0_import_settings" />

<p class="a0-step-text top-margin"><?php _e( 'Paste the settings JSON in the field below:', 'wp-auth0' ); ?>
<p class="a0-step-text top-margin">
<?php
_e( 'Paste the settings JSON in the field below. ', 'wp-auth0' );
_e( 'Settings that are not in the imported JSON will use existing values. ', 'wp-auth0' );
_e( 'Setting values will be validated so check the final values once import is complete. ', 'wp-auth0' );
?>
<div class="a0-step-text top-margin"><textarea name="settings-json" class="large-text code" rows="6"></textarea></div>

<div class="a0-buttons">
Expand Down
12 changes: 12 additions & 0 deletions templates/settings.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,18 @@

<?php settings_errors(); ?>

<?php if ( wp_auth0_get_option( 'client_id' ) ) : ?>
<a href="https://manage.auth0.com/#/applications/
<?php
echo esc_attr( wp_auth0_get_option( 'client_id' ) );
?>
" target="_blank">
<?php
_e( 'Manage this application at Auth0', 'wp-auth0' );
?>
</a>
<?php endif; ?>

<p class="nav nav-tabs" role="tablist">
<a id="tab-basic" href="#basic" class="js-a0-settings-tabs">
<?php _e( 'Basic', 'wp-auth0' ); ?>
Expand Down
18 changes: 9 additions & 9 deletions tests/testAdminAppearanceValidation.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@
class TestAdminAppearanceValidation extends WP_Auth0_Test_Case {

/**
* WP_Auth0_Admin_Appearance instance.
* WP_Auth0_Admin instance.
*
* @var WP_Auth0_Admin_Appearance
* @var WP_Auth0_Admin
*/
public static $admin;

Expand All @@ -24,39 +24,39 @@ class TestAdminAppearanceValidation extends WP_Auth0_Test_Case {
*/
public static function setUpBeforeClass() {
parent::setUpBeforeClass();
self::$admin = new WP_Auth0_Admin_Appearance( self::$opts );
self::$admin = new WP_Auth0_Admin( self::$opts, new WP_Auth0_Routes( self::$opts ) );
}

/**
* Test that the form_title setting is skipped if empty and removes HTML.
*/
public function testThatFormTitleIsValidatedProperly() {
$validated = self::$admin->basic_validation( [], [] );
$validated = self::$admin->input_validator( [] );
$this->assertEquals( '', $validated['form_title'] );

$validated = self::$admin->basic_validation( [], [ 'form_title' => '<script>alert("hi")</script>' ] );
$validated = self::$admin->input_validator( [ 'form_title' => '<script>alert("hi")</script>' ] );
$this->assertNotContains( '<script>', $validated['form_title'] );
}

/**
* Test that the icon_url setting is skipped if empty and tries to create a valid URL for display.
*/
public function testThatIconUrlIsValidatedProperly() {
$validated = self::$admin->basic_validation( [], [] );
$validated = self::$admin->input_validator( [] );
$this->assertEquals( '', $validated['icon_url'] );

$validated = self::$admin->basic_validation( [], [ 'icon_url' => 'example.org' ] );
$validated = self::$admin->input_validator( [ 'icon_url' => 'example.org' ] );
$this->assertEquals( 'http://example.org', $validated['icon_url'] );
}

/**
* Test that the primary_color setting is skipped if empty and removes HTML.
*/
public function testThatPrimaryColorIsValidatedProperly() {
$validated = self::$admin->basic_validation( [], [] );
$validated = self::$admin->input_validator( [] );
$this->assertEquals( '', $validated['primary_color'] );

$validated = self::$admin->basic_validation( [], [ 'primary_color' => '<script>alert("hi")</script>' ] );
$validated = self::$admin->input_validator( [ 'primary_color' => '<script>alert("hi")</script>' ] );
$this->assertNotContains( '<script>', $validated['primary_color'] );
}
}
Loading