Skip to content

Commit

Permalink
Initial work on password reset tools.
Browse files Browse the repository at this point in the history
See #3406.
  • Loading branch information
boonebgorges committed Jul 16, 2024
1 parent 985a595 commit 20f8360
Show file tree
Hide file tree
Showing 3 changed files with 219 additions and 0 deletions.
22 changes: 22 additions & 0 deletions wp-content/plugins/wds-citytech/assets/js/passwords-admin.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/* global jQuery */
(function($){
$(document).ready(function(){
const $passwordExpiration = $('#password-expiration');

$( '#set-password-expiration' ).click(function(e){
e.preventDefault();

const now = new Date();
const formattedDate = now.toISOString().slice( 0, 16 );

$passwordExpiration.val( formattedDate );
});

$( '#clear-password-expiration' ).click(function(e){
e.preventDefault();

$passwordExpiration.val( '' );
});

});
})(jQuery);
196 changes: 196 additions & 0 deletions wp-content/plugins/wds-citytech/includes/passwords.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
<?php

/**
* Customizations related to user passwords.
*/

namespace OpenLab\Passwords;

/**
* Sets a user's password expiration date.
*
* @param int $user_id The user ID.
* @param int $expiration The expiration timestamp.
*/
function set_password_expiration( $user_id, $expiration ) {
update_user_meta( $user_id, 'password_expiration', $expiration );
}

/**
* Gets a user's password expiration date.
*
* @param int $user_id The user ID.
* @return int|null The expiration timestamp. Null if not set.
*/
function get_password_expiration( $user_id ) {
$saved = get_user_meta( $user_id, 'password_expiration', true );

if ( ! $saved ) {
return null;
}

return (int) $saved;
}

/**
* Adds a 'Password Expiration' section to user-edit.php.
*
* @param WP_User $user The user object.
*/
function user_edit_form( $user ) {
wp_enqueue_script(
'passwords-admin',
plugins_url() . '/wds-citytech/assets/js/passwords-admin.js',
array( 'jquery' ),
null,
true
);

$expiration = get_password_expiration( $user->ID );

?>

<h2>Password Expiration</h2>

<?php if ( ! $expiration ) : ?>
<p>This user's password does not currently have an expiration date.</p>
<?php elseif ( $expiration > time() ) : ?>
<p>This user's password <strong>will expire</strong> on <?php echo date( 'F j, Y', $expiration ); ?> at <?php echo date( 'g:i a', $expiration ); ?> (UTC).</p>
<?php else : ?>
<p>This user's password <strong>expired</strong> on <?php echo date( 'F j, Y', $expiration ); ?> at <?php echo date( 'g:i a', $expiration ); ?> (UTC).</p>
<?php endif; ?>

<table class="form-table">
<tr>
<th><label for="password_expiration">Set Password Expiration (UTC)</label></th>
<td>
<input type="datetime-local" id="password-expiration" name="password_expiration" value="<?php echo $expiration ? date( 'Y-m-d H:i:s', $expiration ) : ''; ?>" />
<button class="button" id="set-password-expiration">Set to Now</button>
<button class="button" id="clear-password-expiration">Clear</button>

<?php wp_nonce_field( 'set_password_expiration', 'set-password-expiration-nonce', false ); ?>
</td>
</tr>
</table>

<?php
}
add_action( 'edit_user_profile', __NAMESPACE__ . '\user_edit_form' );

/**
* Saves the password expiration date when a user is updated.
*
* @param int $user_id The user ID.
*/
function save_user( $user_id ) {
if ( ! current_user_can( 'edit_user', $user_id ) ) {
return;
}

if ( ! isset( $_POST['set-password-expiration-nonce'] ) || ! wp_verify_nonce( $_POST['set-password-expiration-nonce'], 'set_password_expiration' ) ) {
return;
}

$expiration = strtotime( $_POST['password_expiration'] );

if ( ! $expiration ) {
delete_user_meta( $user_id, 'password_expiration' );
} else {
set_password_expiration( $user_id, $expiration );
}
}
add_action( 'edit_user_profile_update', __NAMESPACE__ . '\save_user' );

/**
* Hook into the login process to check for password expiration.
*
* @param WP_User|WP_Error $user The WP_User object or WP_Error.
* @param string $username The username.
* @param string $password The password.
* @return WP_User|WP_Error The WP_User object or WP_Error.
*/
function check_password_expiration( $user, $username, $password ) {
if ( \is_wp_error( $user ) ) {

Check failure on line 113 in wp-content/plugins/wds-citytech/includes/passwords.php

View workflow job for this annotation

GitHub Actions / phpstan

is_wp_error(OpenLab\Passwords\WP_Error|OpenLab\Passwords\WP_User) will always evaluate to false.
return $user;
}

$user_obj = get_user_by( 'login', $username );
if ( ! $user_obj ) {
return $user;
}

$expiration = get_password_expiration( $user_obj->ID );

if ( ! $expiration ) {
return $user;
}

if ( $expiration < time() ) {
return new \WP_Error(
'password_expired',
'Your password has expired. Please reset it.' ,
[
'username' => $username,
]
);
}

return $user;
}
add_filter( 'authenticate', __NAMESPACE__ . '\check_password_expiration', 999, 3 );

/**
* Perform a redirect when a user's password has expired.
*
* We use the 'login_redirect' filter because it occurs after the user has been authenticated.
*
* @param string $redirect_to The redirect URL.
* @param string $requested_redirect_to The requested redirect URL.
* @param \WP_User|\WP_Error $user The WP_User object.
* @return string The redirect URL.
*/
function login_redirect( $redirect_to, $requested_redirect_to, $user ) {
if ( \is_wp_error( $user ) && 'password_expired' === $user->get_error_code() ) {
$redirect_url = add_query_arg(
[
'action' => 'lostpassword',
'password_expired' => 'true',
'user_login' => $user->get_error_data()['username'],
],
wp_login_url()
);

wp_safe_redirect( $redirect_url );
die;
}

return $redirect_to;
}
add_filter( 'login_redirect', __NAMESPACE__ . '\login_redirect', 50, 3 );

/**
* Filters the message to display when a user's password has expired.
*
* @param string $message The message.
* @return string The message.
*/
function password_expired_message( $message ) {
if ( empty( $_GET['action'] ) || 'lostpassword' !== $_GET['action'] ) {
return $message;
}

if ( empty( $_GET['password_expired'] ) || 'true' !== $_GET['password_expired'] ) {
return $message;
}

$message = wp_get_admin_notice(
'Your password has expired. To reset it, please enter your username or email address below. You will receive an email with instructions on how to proceed.',
[
'type' => 'error',
'additional_classes' => [ 'password-expired' ],
]
);

return $message;
}
add_filter( 'login_message', __NAMESPACE__ . '\password_expired_message' );
1 change: 1 addition & 0 deletions wp-content/plugins/wds-citytech/wds-citytech.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
require 'includes/user-moderation.php';
require 'includes/block-widgets.php';
require 'includes/cbox-polyfills/index.php';
require 'includes/passwords.php';

// Conditionally load Easy TOC modifications.
add_action( 'plugins_loaded', function() {
Expand Down

0 comments on commit 20f8360

Please sign in to comment.