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

Expire Passwords #89

Draft
wants to merge 16 commits into
base: develop
Choose a base branch
from
Draft
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
17 changes: 17 additions & 0 deletions 10up-experience.php
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,13 @@ function( $plugin_info, $http_response = null ) {
SupportMonitor\Monitor::instance()->setup();
SupportMonitor\Debug::instance()->setup();
Notifications\Welcome::instance()->setup();
Notifications\Welcome::instance()->setup();

if( ( ! defined( 'TENUP_DISABLE_PASSWORD_POLICY' ) || ! TENUP_DISABLE_PASSWORD_POLICY ) ){
if ( AdminCustomizations\PasswordPolicy::instance()->is_enabled() ) {
Authentication\PastPasswords::instance()->setup();
}
}

/**
* We load this later to make sure there are no conflicts with other plugins.
Expand All @@ -92,6 +99,16 @@ function() {
}
);

/**
* Clean up when plugin is deactivated
*/
register_deactivation_hook(
__FILE__,
function() {
Authentication\PastPasswords::instance()->deactivate();
}
);

/**
* Disable plugin/theme editor
*/
Expand Down
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,14 @@

*Configured in `Settings > General` or `Settings > Network Settings` if network activated.*

* __Password Policy__

Enable site wide Password Policy for administrator, editors and authors. Control how long passwords are good for, prevent users from repeating passwords and configure when to send reminder emails to users whose password will be expiring soon. The reminder email can be fully customized and dynamic information can be added via a set of replacement tags. Once a password is expired the user will be prompted to reset their password before they can login.

*Configured in `Users > Password Policy`

*Note:* Password Policy can be disabled by defining the constant `TENUP_DISABLE_PASSWORD_POLICY` as `true`.

* __Headers__

`X-Frame-Origins` is set to `sameorigin` to prevent click jacking.
Expand Down
213 changes: 213 additions & 0 deletions includes/classes/AdminCustomizations/PasswordPolicy.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,213 @@
<?php
/**
* Admin password Policy
*
* @package 10up-experience
*/

namespace TenUpExperience\AdminCustomizations;

use TenUpExperience\Singleton;

/**
* Admin PasswordPolicy class
*/
class PasswordPolicy extends Singleton {

/**
* Password policy option name
*/
const PASSWORD_POLICY_OPTION_NAME = 'tenup_password_policy_settings';

/**
* Setup module
*
* @since 1.7
*/
public function setup() {
add_action( 'admin_menu', [ $this, 'register_admin_pages' ] );
add_filter( 'admin_init', [ $this, 'register_password_policy_settings' ], 10, 2 );
}

/**
* Register admin pages with output callbacks
*/
public function register_admin_pages() {
if ( apply_filters( 'tenup_experience_enable_password_policy', true ) ) {
add_users_page( esc_html__( 'Password Policy', 'tenup' ), esc_html__( 'Password Policy', 'tenup' ), 'manage_options', '10up-password-policy', [ $this, 'password_policy_screen' ] );
}
}

/**
* Register password policy settings
*
* @return void
*/
public function register_password_policy_settings() {
register_setting(
self::PASSWORD_POLICY_OPTION_NAME,
self::PASSWORD_POLICY_OPTION_NAME,
[
'sanitize_callback' => [ $this, 'sanitize_settings' ],
]
);

add_settings_section(
self::PASSWORD_POLICY_OPTION_NAME,
'',
'__return_empty_string',
self::PASSWORD_POLICY_OPTION_NAME
);

$settings = [
'enabled' => [
'label' => __( 'Enable Password Policy', 'tenup' ),
'type' => 'checkbox',
],
'expires' => [
'label' => __( 'Password Expires', 'tenup' ),
'type' => 'number',
'description' => __( 'The number of days a passwords is good for before it needs to be changed.', 'tenup' ),
],
'reminder' => [
'label' => __( 'Send Password Reminder', 'tenup' ),
'type' => 'number',
'description' => __( 'The number of days before a password expires to send an email reminder.', 'tenup' ),
],
'past_passwords' => [
'label' => __( 'Past Passwords', 'tenup' ),
'type' => 'number',
'description' => __( 'The number of past passwords a user can\'t repeat.', 'tenup' ),
],
'reminder_email' => [
'label' => __( 'Reminder Email', 'tenup' ),
'description' => __( '###USERNAME###, ###ADMIN_EMAIL###, ###EMAIL###, ###SITENAME###, ###SITEURL###, ###DAYSLEFT###, and ###EXPIRATIONDATE### are replacement tags that can be used to populate the reminder email with dynamic information.', 'tenup' ),
'type' => 'tinymce',
],
];

foreach ( $settings as $setting_id => $setting ) {
$options = [
'name' => self::PASSWORD_POLICY_OPTION_NAME . "[$setting_id]",
'id' => $setting_id,
'type' => $setting['type'] ?? 'text',
'description' => $setting['description'] ?? '',
];

add_settings_field(
$setting_id,
$setting['label'],
[ $this, 'field' ],
self::PASSWORD_POLICY_OPTION_NAME,
self::PASSWORD_POLICY_OPTION_NAME,
$options
);
}
}

/**
* Output setting fields
*
* @param array $args field options
*/
public function field( $args ) {
$settings = get_option( self::PASSWORD_POLICY_OPTION_NAME, [] );
$value = $settings[ $args['id'] ] ?? '';

if ( 'checkbox' === $args['type'] ) {
printf( '<input type="%s" id="%s" name="%s" value="on" %s/>', esc_attr( $args['type'] ), esc_attr( $args['id'] ), esc_attr( $args['name'] ), esc_attr( checked( 'on', $value, false ) ) );
if ( ! empty( $args['description'] ) ) {
printf( '<label for="%s">%s</label>', esc_attr( $args['id'] ), esc_html( $args['description'] ) );
}
} elseif ( 'tinymce' === $args['type'] ) {
wp_editor(
$value,
$args['id'],
[
'media_buttons' => false,
'textarea_name' => $args['name'],
]
);
if ( ! empty( $args['description'] ) ) {
printf( '<p class="description">%s</p>', wp_kses_post( $args['description'] ) );
}
} else {
printf( '<input type="%s" id="%s" name="%s" value="%s" class="regular-text"/>', esc_attr( $args['type'] ), esc_attr( $args['id'] ), esc_attr( $args['name'] ), esc_attr( $value ) );

if ( ! empty( $args['description'] ) ) {
printf( '<p class="description">%s</p>', esc_html( $args['description'] ) );
}
}
}

/**
* Sanitize settings fields
*
* @param array $settings setting being saved
*
* @return array
*/
public function sanitize_settings( $settings ) {
$clean_settings = array();
foreach ( $settings as $key => $setting ) {
if ( in_array( $key, [ 'reminder_email', 'token_email' ], true ) ) {
$clean_settings[ $key ] = wp_kses_post( $setting );
} else {
$clean_settings[ $key ] = sanitize_text_field( $setting );
}
}

return $clean_settings;
}

/**
* Password policy screen
*
* @return void
*/
public function password_policy_screen() {
?>
<div class="wrap">
<h2><?php esc_html_e( 'Password Policy', 'tenup' ); ?></h2>

<?php settings_errors(); ?>

<form action="options.php" method="POST">
<?php
settings_fields( self::PASSWORD_POLICY_OPTION_NAME );
do_settings_sections( self::PASSWORD_POLICY_OPTION_NAME );
submit_button( __( 'Save Settings', 'tenup' ) );
?>
</form>
</div>
<?php

}

/**
* Return password policy settings
*
* @param string $name Setting key
*
* @return string
*/
public function get_setting( $name = '' ) {
$settings = get_option( self::PASSWORD_POLICY_OPTION_NAME, [] );

if ( empty( $name ) ) {
return $settings;
}

return $settings[ $name ] ?? '';
}

/**
* Is the password policy feature enabled
*
* @return bool
*/
public function is_enabled() {
return ! empty( $this->get_setting( 'enabled' ) ) && apply_filters( 'tenup_experience_enable_password_policy', true );
}

}
Loading