v1.0.0
This commit is contained in:
parent
3291a5f7d2
commit
212d64abfa
27 changed files with 8654 additions and 2 deletions
433
includes/class-network-settings.php
Normal file
433
includes/class-network-settings.php
Normal file
|
|
@ -0,0 +1,433 @@
|
|||
<?php
|
||||
/**
|
||||
* Network settings class for Multisite
|
||||
*
|
||||
* @package TwoFactorExtended
|
||||
* @since 0.1.0
|
||||
*/
|
||||
|
||||
// Prevent direct access.
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Class Two_Factor_Extended_Network_Settings
|
||||
*
|
||||
* Handles network-wide settings for Multisite installations.
|
||||
*
|
||||
* @since 0.1.0
|
||||
*/
|
||||
class Two_Factor_Extended_Network_Settings {
|
||||
|
||||
/**
|
||||
* Network settings page slug.
|
||||
*
|
||||
* @since 0.1.0
|
||||
* @var string
|
||||
*/
|
||||
const PAGE_SLUG = 'two-factor-extended-network';
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @since 0.1.0
|
||||
*/
|
||||
public function __construct() {
|
||||
// Only initialize for Multisite.
|
||||
if ( ! is_multisite() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->init_hooks();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize WordPress hooks.
|
||||
*
|
||||
* @since 0.1.0
|
||||
*/
|
||||
private function init_hooks(): void {
|
||||
add_action( 'network_admin_menu', array( $this, 'register_network_menu' ) );
|
||||
add_action( 'network_admin_edit_two_factor_extended_network', array( $this, 'save_network_settings' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Register network admin menu.
|
||||
*
|
||||
* @since 0.1.0
|
||||
*/
|
||||
public function register_network_menu(): void {
|
||||
if ( ! current_user_can( 'manage_network_options' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
add_submenu_page(
|
||||
'settings.php',
|
||||
__( 'Two Factor Extended', 'two-factor-extended' ),
|
||||
__( 'Two Factor Extended', 'two-factor-extended' ),
|
||||
'manage_network_options',
|
||||
self::PAGE_SLUG,
|
||||
array( $this, 'render_network_page' )
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render network settings page.
|
||||
*
|
||||
* @since 0.1.0
|
||||
*/
|
||||
public function render_network_page(): void {
|
||||
// Check capability.
|
||||
if ( ! current_user_can( 'manage_network_options' ) ) {
|
||||
wp_die( esc_html__( 'You do not have sufficient permissions to access this page.', 'two-factor-extended' ) );
|
||||
}
|
||||
|
||||
$settings = $this->get_network_settings();
|
||||
|
||||
?>
|
||||
<div class="wrap">
|
||||
<h1><?php echo esc_html( get_admin_page_title() ); ?></h1>
|
||||
|
||||
<?php
|
||||
// Show success message.
|
||||
// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only check
|
||||
if ( isset( $_GET['updated'] ) && 'true' === $_GET['updated'] ) :
|
||||
?>
|
||||
<div class="notice notice-success is-dismissible">
|
||||
<p><?php esc_html_e( 'Network settings saved successfully.', 'two-factor-extended' ); ?></p>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<form method="post" action="edit.php?action=two_factor_extended_network">
|
||||
<?php wp_nonce_field( 'two_factor_extended_network_settings', 'two_factor_extended_network_nonce' ); ?>
|
||||
|
||||
<h2><?php esc_html_e( 'Network-Wide Settings', 'two-factor-extended' ); ?></h2>
|
||||
|
||||
<table class="form-table">
|
||||
<tr>
|
||||
<th scope="row">
|
||||
<label for="network_enforce">
|
||||
<?php esc_html_e( 'Network-Wide Enforcement', 'two-factor-extended' ); ?>
|
||||
</label>
|
||||
</th>
|
||||
<td>
|
||||
<fieldset>
|
||||
<label>
|
||||
<input
|
||||
type="checkbox"
|
||||
id="network_enforce"
|
||||
name="network_settings[enforce_network_wide]"
|
||||
value="1"
|
||||
<?php checked( $settings['enforce_network_wide'] ?? false ); ?>
|
||||
/>
|
||||
<?php esc_html_e( 'Apply these settings to all sites in the network', 'two-factor-extended' ); ?>
|
||||
</label>
|
||||
<p class="description">
|
||||
<?php esc_html_e( 'When enabled, network settings override individual site settings.', 'two-factor-extended' ); ?>
|
||||
</p>
|
||||
</fieldset>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<th scope="row">
|
||||
<label for="allow_site_override">
|
||||
<?php esc_html_e( 'Allow Site Override', 'two-factor-extended' ); ?>
|
||||
</label>
|
||||
</th>
|
||||
<td>
|
||||
<fieldset>
|
||||
<label>
|
||||
<input
|
||||
type="checkbox"
|
||||
id="allow_site_override"
|
||||
name="network_settings[allow_site_override]"
|
||||
value="1"
|
||||
<?php checked( $settings['allow_site_override'] ?? false ); ?>
|
||||
/>
|
||||
<?php esc_html_e( 'Allow individual sites to override network settings', 'two-factor-extended' ); ?>
|
||||
</label>
|
||||
<p class="description">
|
||||
<?php esc_html_e( 'Site administrators can configure their own 2FA requirements.', 'two-factor-extended' ); ?>
|
||||
</p>
|
||||
</fieldset>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<th scope="row">
|
||||
<?php esc_html_e( 'Super Admin Requirements', 'two-factor-extended' ); ?>
|
||||
</th>
|
||||
<td>
|
||||
<?php $this->render_super_admin_requirements( $settings ); ?>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<th scope="row">
|
||||
<?php esc_html_e( 'Network Grace Period', 'two-factor-extended' ); ?>
|
||||
</th>
|
||||
<td>
|
||||
<input
|
||||
type="number"
|
||||
id="network_grace_period"
|
||||
name="network_settings[grace_period_days]"
|
||||
value="<?php echo esc_attr( $settings['grace_period_days'] ?? 7 ); ?>"
|
||||
min="0"
|
||||
max="365"
|
||||
class="small-text"
|
||||
/>
|
||||
<?php esc_html_e( 'days', 'two-factor-extended' ); ?>
|
||||
<p class="description">
|
||||
<?php esc_html_e( 'Grace period for all sites in the network. Set to 0 for immediate enforcement.', 'two-factor-extended' ); ?>
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<h2><?php esc_html_e( 'Network Role Requirements', 'two-factor-extended' ); ?></h2>
|
||||
<p class="description">
|
||||
<?php esc_html_e( 'Configure 2FA requirements that apply across all sites in the network.', 'two-factor-extended' ); ?>
|
||||
</p>
|
||||
|
||||
<?php $this->render_network_role_requirements( $settings ); ?>
|
||||
|
||||
<?php submit_button( __( 'Save Network Settings', 'two-factor-extended' ) ); ?>
|
||||
</form>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Render super admin requirements field.
|
||||
*
|
||||
* @since 0.1.0
|
||||
*
|
||||
* @param array $settings Current network settings.
|
||||
*/
|
||||
private function render_super_admin_requirements( array $settings ): void {
|
||||
$providers = Two_Factor_Extended_Provider_Detector::get_provider_names();
|
||||
$super_admin_reqs = $settings['super_admin_requirements'] ?? array();
|
||||
|
||||
if ( empty( $providers ) ) {
|
||||
?>
|
||||
<p class="description">
|
||||
<?php esc_html_e( 'No 2FA providers detected.', 'two-factor-extended' ); ?>
|
||||
</p>
|
||||
<?php
|
||||
return;
|
||||
}
|
||||
|
||||
?>
|
||||
<fieldset>
|
||||
<?php foreach ( $providers as $class => $name ) : ?>
|
||||
<label style="display: block; margin-bottom: 5px;">
|
||||
<input
|
||||
type="checkbox"
|
||||
name="network_settings[super_admin_requirements][]"
|
||||
value="<?php echo esc_attr( $class ); ?>"
|
||||
<?php checked( in_array( $class, $super_admin_reqs, true ) ); ?>
|
||||
/>
|
||||
<?php echo esc_html( $name ); ?>
|
||||
</label>
|
||||
<?php endforeach; ?>
|
||||
<p class="description">
|
||||
<?php esc_html_e( 'Required 2FA methods for super administrators.', 'two-factor-extended' ); ?>
|
||||
</p>
|
||||
</fieldset>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Render network role requirements field.
|
||||
*
|
||||
* @since 0.1.0
|
||||
*
|
||||
* @param array $settings Current network settings.
|
||||
*/
|
||||
private function render_network_role_requirements( array $settings ): void {
|
||||
$roles = Two_Factor_Extended_Role_Manager::get_all_roles();
|
||||
$providers = Two_Factor_Extended_Provider_Detector::get_provider_names();
|
||||
$network_reqs = $settings['role_requirements'] ?? array();
|
||||
|
||||
if ( empty( $providers ) ) {
|
||||
?>
|
||||
<p class="description">
|
||||
<?php esc_html_e( 'No 2FA providers detected.', 'two-factor-extended' ); ?>
|
||||
</p>
|
||||
<?php
|
||||
return;
|
||||
}
|
||||
|
||||
?>
|
||||
<table class="widefat fixed striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="column-title"><?php esc_html_e( 'Role', 'two-factor-extended' ); ?></th>
|
||||
<th><?php esc_html_e( 'Required 2FA Methods', 'two-factor-extended' ); ?></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ( $roles as $role_slug => $role_name ) : ?>
|
||||
<tr>
|
||||
<td><strong><?php echo esc_html( $role_name ); ?></strong></td>
|
||||
<td>
|
||||
<?php
|
||||
$role_reqs = $network_reqs[ $role_slug ] ?? array();
|
||||
|
||||
foreach ( $providers as $class => $name ) :
|
||||
?>
|
||||
<label style="display: block; margin-bottom: 5px;">
|
||||
<input
|
||||
type="checkbox"
|
||||
name="network_settings[role_requirements][<?php echo esc_attr( $role_slug ); ?>][]"
|
||||
value="<?php echo esc_attr( $class ); ?>"
|
||||
<?php checked( in_array( $class, $role_reqs, true ) ); ?>
|
||||
/>
|
||||
<?php echo esc_html( $name ); ?>
|
||||
</label>
|
||||
<?php endforeach; ?>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Save network settings.
|
||||
*
|
||||
* @since 0.1.0
|
||||
*/
|
||||
public function save_network_settings(): void {
|
||||
// Check capability.
|
||||
if ( ! current_user_can( 'manage_network_options' ) ) {
|
||||
wp_die( esc_html__( 'You do not have sufficient permissions to perform this action.', 'two-factor-extended' ) );
|
||||
}
|
||||
|
||||
// Verify nonce.
|
||||
$nonce = isset( $_POST['two_factor_extended_network_nonce'] )
|
||||
? sanitize_text_field( wp_unslash( $_POST['two_factor_extended_network_nonce'] ) )
|
||||
: '';
|
||||
|
||||
if ( ! wp_verify_nonce( $nonce, 'two_factor_extended_network_settings' ) ) {
|
||||
wp_die( esc_html__( 'Security check failed.', 'two-factor-extended' ) );
|
||||
}
|
||||
|
||||
// Get and sanitize input.
|
||||
// phpcs:disable WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Data sanitized by sanitize_network_settings() method before use
|
||||
$input = isset( $_POST['network_settings'] ) && is_array( $_POST['network_settings'] )
|
||||
? wp_unslash( $_POST['network_settings'] )
|
||||
: array();
|
||||
// phpcs:enable WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
|
||||
|
||||
$sanitized = $this->sanitize_network_settings( $input );
|
||||
|
||||
// Save settings.
|
||||
update_site_option( TWO_FACTOR_EXTENDED_NETWORK_OPTION_SETTINGS, $sanitized );
|
||||
|
||||
// Redirect with success message.
|
||||
wp_safe_redirect(
|
||||
add_query_arg(
|
||||
'updated',
|
||||
'true',
|
||||
network_admin_url( 'settings.php?page=' . self::PAGE_SLUG )
|
||||
)
|
||||
);
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get network settings.
|
||||
*
|
||||
* @since 0.1.0
|
||||
*
|
||||
* @return array Network settings.
|
||||
*/
|
||||
public function get_network_settings(): array {
|
||||
$defaults = array(
|
||||
'enforce_network_wide' => false,
|
||||
'allow_site_override' => true,
|
||||
'grace_period_days' => 7,
|
||||
'super_admin_requirements' => array(),
|
||||
'role_requirements' => array(),
|
||||
);
|
||||
|
||||
$settings = get_site_option( TWO_FACTOR_EXTENDED_NETWORK_OPTION_SETTINGS, array() );
|
||||
|
||||
return wp_parse_args( $settings, $defaults );
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitize network settings.
|
||||
*
|
||||
* @since 0.1.0
|
||||
*
|
||||
* @param array $input Raw input.
|
||||
*
|
||||
* @return array Sanitized settings.
|
||||
*/
|
||||
private function sanitize_network_settings( array $input ): array {
|
||||
$sanitized = array();
|
||||
|
||||
// Enforce network wide.
|
||||
$sanitized['enforce_network_wide'] = ! empty( $input['enforce_network_wide'] );
|
||||
|
||||
// Allow site override.
|
||||
$sanitized['allow_site_override'] = ! empty( $input['allow_site_override'] );
|
||||
|
||||
// Grace period days.
|
||||
if ( isset( $input['grace_period_days'] ) ) {
|
||||
$days = (int) $input['grace_period_days'];
|
||||
$sanitized['grace_period_days'] = max( 0, min( 365, $days ) );
|
||||
}
|
||||
|
||||
// Super admin requirements.
|
||||
if ( isset( $input['super_admin_requirements'] ) && is_array( $input['super_admin_requirements'] ) ) {
|
||||
$sanitized['super_admin_requirements'] = array_map( 'sanitize_text_field', $input['super_admin_requirements'] );
|
||||
}
|
||||
|
||||
// Role requirements.
|
||||
if ( isset( $input['role_requirements'] ) && is_array( $input['role_requirements'] ) ) {
|
||||
$sanitized['role_requirements'] = array();
|
||||
|
||||
foreach ( $input['role_requirements'] as $role => $providers ) {
|
||||
$role_slug = sanitize_key( $role );
|
||||
|
||||
if ( Two_Factor_Extended_Role_Manager::role_exists( $role_slug ) && is_array( $providers ) ) {
|
||||
$sanitized['role_requirements'][ $role_slug ] = array_map( 'sanitize_text_field', $providers );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $sanitized;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if network enforcement is enabled.
|
||||
*
|
||||
* @since 0.1.0
|
||||
*
|
||||
* @return bool True if network enforcement is enabled.
|
||||
*/
|
||||
public function is_network_enforcement_enabled(): bool {
|
||||
$settings = $this->get_network_settings();
|
||||
|
||||
return ! empty( $settings['enforce_network_wide'] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if site override is allowed.
|
||||
*
|
||||
* @since 0.1.0
|
||||
*
|
||||
* @return bool True if site override is allowed.
|
||||
*/
|
||||
public function is_site_override_allowed(): bool {
|
||||
$settings = $this->get_network_settings();
|
||||
|
||||
return ! empty( $settings['allow_site_override'] );
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue