-
Notifications
You must be signed in to change notification settings - Fork 96
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
Fix Connection update over-writing Connection settings. #582
Conversation
@@ -30,10 +30,6 @@ public function update_wordpress_connection( $app_token, $connection_id, $passwo | |||
$connection->options->customScripts->login = $login_script; | |||
$connection->options->customScripts->get_user = $get_user_script; | |||
|
|||
unset( $connection->name ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Moved to WP_Auth0_Api_Client::update_connection()
lib/WP_Auth0_Api_Operations.php
Outdated
@@ -107,26 +103,21 @@ public function create_wordpress_connection( $app_token, $migration_enabled, $pa | |||
$connections = WP_Auth0_Api_Client::search_connection( $domain, $app_token, 'auth0' ); | |||
$db_connection = null; | |||
|
|||
$migration_connection_id = $response->id; | |||
$created_connection_id = $response->id; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Confusing var name
@@ -741,10 +741,6 @@ public function migration_ws_validation( $old_options, $input ) { | |||
$connection->options->enabledDatabaseCustomization = false; | |||
$connection->options->import_mode = false; | |||
|
|||
unset( $connection->name ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Moved to WP_Auth0_Api_Client::update_connection()
@@ -427,8 +427,8 @@ public function security_validation( $old_options, $input ) { | |||
|
|||
foreach ( $connections as $connection ) { | |||
if ( in_array( $input['client_id'], $connection->enabled_clients ) ) { | |||
$patch = array( 'options' => array( 'passwordPolicy' => $input['password_policy'] ) ); | |||
$update_resp = WP_Auth0_Api_Client::update_connection( $domain, $app_token, $connection->id, $patch ); | |||
$connection['options']['passwordPolicy'] = $input['password_policy']; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here's the bug ... $patch
did not include existing Connection settings
@@ -128,19 +144,21 @@ public function consent_callback( $name ) { | |||
$connection_exists = false; | |||
$connection_pwd_policy = null; | |||
|
|||
$connections = WP_Auth0_Api_Client::search_connection( $domain, $app_token ); | |||
$connections = WP_Auth0_Api_Client::search_connection( $domain, $app_token, 'auth0' ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added strategy filter here to reduce the number of Connections returned.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If possible, can this be renamed to searchConnections
(plural)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Public static method so no, not currently. WP_Auth0_Api_Client
is getting refactored one method at a time so it will go away soon.
e482fec
to
aff92b2
Compare
@@ -619,6 +630,14 @@ public static function update_connection( $domain, $app_token, $id, $payload ) { | |||
$headers['Authorization'] = "Bearer $app_token"; | |||
$headers['content-type'] = 'application/json'; | |||
|
|||
unset( $payload->name ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These must not be set or the API endpoint returns a payload error.
unset( $payload->strategy ); | ||
unset( $payload->id ); | ||
|
||
if ( ! empty( $payload->enabled_clients ) ) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the array is not consecutive then json_encode
converts this to an object rather than an array.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consecutive as in following an ascending/descending order? 🤔 I don't understand why you're doing this. Can you rephrase please?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If $payload->enabled_clients
is anything other than an array with consecutive keys (like, if one of the keys in the middle is removed) then json_encode()
turns it into an object
which is rejected by the server.
|
||
$migration_connection_id = $response->id; | ||
|
||
foreach ( $connections as $connection ) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This method is only called in one place and the calling method already does this check.
|
||
foreach ( $connections as $connection ) { | ||
if ( $should_create_and_update_connection && ! empty( $connections ) && is_array( $connections ) ) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Logic here is still not great but did not want to completely refactor this in a patch release. Main thing here is checking whether we have an array and avoiding the loop if we're not using the values later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds like the main check (the is $should_create_and_update_connection
) is repeated right below this one. What about wrapping those like this... ?
if (!$should_create_and_update_connection){
return;
//early exit, no need to nest another if in that case 👍
}
if (! empty( $connections ) && is_array( $connections )){
//continue with this logic
return; //return here if you don't want to keep going down the code
}
if ( $connection_exists === false ) {
// the remaining stuff
}
I don't agree that doing this in a patch release is wrong in anyway. On the contrary, you're improving code readability and that's a plus for any PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I understand but looking for the minimum number of changes here. This is just one of many things that should be addressed in this method.
$connection_exists = $connection->id; | ||
$connection_pwd_policy = ( isset( $connection->options ) && isset( $connection->options->passwordPolicy ) ) ? $connection->options->passwordPolicy : null; | ||
$connection_exists = $connection->id; | ||
if ( isset( $connection->options ) && isset( $connection->options->passwordPolicy ) ) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ternary was too long and confusing to read.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In those cases what I recommend is extracting the boolean to a variable.
e.g.
$flag = isset( $connection->options ) && isset( $connection->options->passwordPolicy );
if ($flag){
//do flag thing
}
Or even better, have a private method somewhere on the file that given a connection would return the password policy value, if any.
That a side, couldn't you get this connection id and pwd policy when you created it? Or you want to consider the case where the user would have changed it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Lots of better ways to do this, yes :)
} else { | ||
$enabled_clients = array_diff( $connection->enabled_clients, array( $client_id ) ); | ||
WP_Auth0_Api_Client::update_connection( $domain, $app_token, $connection->id, array( 'enabled_clients' => array_values( $enabled_clients ) ) ); | ||
$connection->enabled_clients = array_diff( $connection->enabled_clients, array( $client_id ) ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Avoiding the same settings override as reported in the original bug.
aff92b2
to
cdb75ce
Compare
cdb75ce
to
f60951e
Compare
Codecov Report
@@ Coverage Diff @@
## master #582 +/- ##
==========================================
+ Coverage 21.29% 27.7% +6.4%
+ Complexity 1308 1307 -1
==========================================
Files 51 51
Lines 4263 4198 -65
==========================================
+ Hits 908 1163 +255
+ Misses 3355 3035 -320
Continue to review full report at Codecov.
|
@@ -423,19 +423,24 @@ public function security_validation( $old_options, $input ) { | |||
__( 'No database connections found for this application. ', 'wp-auth0' ) . | |||
$this->get_dashboard_link( 'connections/database', __( 'See all database connections', 'wp-auth0' ) ) | |||
); | |||
$input['password_policy'] = $old_options['password_policy']; | |||
return $input; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Exit here to skip anything else that might process (foreach
will throw an error if it's given anything other than an array)
@@ -423,19 +423,24 @@ public function security_validation( $old_options, $input ) { | |||
__( 'No database connections found for this application. ', 'wp-auth0' ) . | |||
$this->get_dashboard_link( 'connections/database', __( 'See all database connections', 'wp-auth0' ) ) | |||
); | |||
$input['password_policy'] = $old_options['password_policy']; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nothing changed at Auth0 so we should not change the setting in WordPress either.
|
||
if ( false === $update_resp ) { | ||
$this->add_validation_error( | ||
__( 'There was a problem updating the password policy. ', 'wp-auth0' ) . | ||
__( 'Please manually review and update the policy. ', 'wp-auth0' ) . | ||
$this->get_dashboard_link( 'connections/database', __( 'See all database connections', 'wp-auth0' ) ) | ||
); | ||
$input['password_policy'] = $old_options['password_policy']; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nothing changed at Auth0 so we should not change the setting in WordPress either.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if the user is giving you the API2 token make sure they know which scopes they must request
unset( $payload->strategy ); | ||
unset( $payload->id ); | ||
|
||
if ( ! empty( $payload->enabled_clients ) ) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consecutive as in following an ascending/descending order? 🤔 I don't understand why you're doing this. Can you rephrase please?
* @param string $domain - Auth0 domain for the Application. | ||
* @param string $access_token - Management API access token. | ||
* @param string $type - Installation type, "social" (AKA standard) or "enterprise". | ||
* @param bool $hasInternetConnection - True if the installing site be reached by Auth0, false if not. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what is this last param used for? Seems it's being set as part of a migration step, but depending on when that's read it might be out of context. Shouldn't this be checked in that other place and time instead?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just documenting, not refactoring
|
||
foreach ( $connections as $connection ) { | ||
if ( $should_create_and_update_connection && ! empty( $connections ) && is_array( $connections ) ) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds like the main check (the is $should_create_and_update_connection
) is repeated right below this one. What about wrapping those like this... ?
if (!$should_create_and_update_connection){
return;
//early exit, no need to nest another if in that case 👍
}
if (! empty( $connections ) && is_array( $connections )){
//continue with this logic
return; //return here if you don't want to keep going down the code
}
if ( $connection_exists === false ) {
// the remaining stuff
}
I don't agree that doing this in a patch release is wrong in anyway. On the contrary, you're improving code readability and that's a plus for any PR.
|
||
if ( in_array( $client_id, $connection->enabled_clients ) ) { | ||
if ( $connection->strategy === 'auth0' && $should_create_and_update_connection ) { | ||
if ( in_array( $client_id, $connection->enabled_clients ) ) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would be nice if you could provide in-line comments (in the code) to help anyone follow the logic.
$connection_exists = $connection->id; | ||
$connection_pwd_policy = ( isset( $connection->options ) && isset( $connection->options->passwordPolicy ) ) ? $connection->options->passwordPolicy : null; | ||
$connection_exists = $connection->id; | ||
if ( isset( $connection->options ) && isset( $connection->options->passwordPolicy ) ) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In those cases what I recommend is extracting the boolean to a variable.
e.g.
$flag = isset( $connection->options ) && isset( $connection->options->passwordPolicy );
if ($flag){
//do flag thing
}
Or even better, have a private method somewhere on the file that given a connection would return the password policy value, if any.
That a side, couldn't you get this connection id and pwd policy when you created it? Or you want to consider the case where the user would have changed it?
); | ||
|
||
$this->assertContains( | ||
'form:{username:email, access_token:"TEST_MIGRATION_TOKEN', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is this intentionally not having a }
at the end of the string in test?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, next character is a newline
|
||
$caught_redirect = []; | ||
try { | ||
$setup_consent->callback_with_token( $test_domain, $test_token, 'invalid_state' ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what's the previous context? how can you ensure that invalid_state
is an invalid state? normally, you'd manually set that state before this call.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are only two values that callback_with_token
accept here ... not actually state but that's the name used in the method.
|
||
$caught_http = []; | ||
try { | ||
$setup_consent->callback_with_token( 'test-wp.auth0.com', $test_token, 'social' ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
how is this test logic different from the "test invalid state redirects"? Sounds like whatever caused the above test to fail should do the same here. What's changing? and if nothing is, why would an invalid_state scenario still make the create client request?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"social"
is one of two valid "state" values here so this proceeds to the API call
'success_update_connection', | ||
// Connection created successfully. | ||
'success_create_connection', | ||
// Client grant failed. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why would this happen? Seems like this should belong in a different test case than "create new connection succeeds"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe the token doesn't have the right scopes or HTTP error or any number of things.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In that case, that could be moved to a different test other than the "happy path" of creating the connection when missing
@@ -62,7 +62,7 @@ public function stopHttpHalting() { | |||
* Use this at the top of tests that should test behavior for different HTTP responses. | |||
*/ | |||
public function startHttpMocking() { | |||
add_filter( 'pre_http_request', [ $this, 'httpMock' ], 1 ); | |||
add_filter( 'pre_http_request', [ $this, 'httpMock' ], 1, 3 ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what changed here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Technically nothing but that last arg is the number of params that the hooked function receives.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM 🎉
Changes
This fixes a bug that can cause unintended changes to database connection settings when saving a new password policy. Specific changes:
WP_Auth0_Api_Client::update_connection()
to avoid unnecessary payload errors from the server.WP_Auth0_Api_Operations::update_wordpress_connection()
References
Testing
Scenario 1: Updates to the password policy in wp-admin
Expected behavior:
Password policy is changed, other settings are not affected.
Scenario 2: Connection creation during the setup wizard
Expected behavior:
Install completes without errors in WordPress; New client created associated to new DB connection in Auth0.
Checklist