array(), '2fa' => array( 'enabled' => false, 'enabled_at' => 0, 'grace_days' => 7, 'allow_optional' => true, 'remember_days' => 0, ), 'login_url' => array( 'enabled' => false, 'slug' => '', 'on_hit' => 'my_account', 'bar_myaccount' => true, 'reg_mode' => 'follow', 'reg_slug' => '', ), 'lockout' => array( 'enabled' => false, 'max_attempts' => 5, 'window_min' => 15, 'lock_min' => 30, 'long_lock_after' => 4, 'long_lock_hours' => 24, 'notify' => true, ), ); } public static function settings() { if ( null !== self::$cache ) { return self::$cache; } $stored = get_option( self::OPTION, array() ); $defaults = self::defaults(); if ( ! is_array( $stored ) ) { $stored = array(); } $out = $defaults; foreach ( $defaults as $key => $default ) { if ( ! isset( $stored[ $key ] ) ) { continue; } $out[ $key ] = is_array( $default ) ? array_merge( $default, (array) $stored[ $key ] ) : $stored[ $key ]; } self::$cache = $out; return $out; } public static function save( array $settings ) { update_option( self::OPTION, $settings, false ); self::$cache = null; if ( class_exists( 'AIEO_Login_URL', false ) ) { delete_transient( AIEO_Login_URL::TEST_TRANSIENT ); } } public static function feature( $feature ) { $s = self::settings(); return isset( $s[ $feature ] ) && is_array( $s[ $feature ] ) ? $s[ $feature ] : array(); } public static function protected_roles() { $s = self::settings(); return array_values( array_filter( array_map( 'strval', (array) $s['roles'] ) ) ); } public static function user_is_protected( $user ) { $user = is_numeric( $user ) ? get_userdata( (int) $user ) : $user; if ( ! $user instanceof WP_User ) { return false; } $protected = self::protected_roles(); if ( empty( $protected ) ) { return false; } return (bool) array_intersect( $protected, (array) $user->roles ); } public static function selectable_roles() { if ( ! function_exists( 'get_editable_roles' ) ) { require_once ABSPATH . 'wp-admin/includes/user.php'; } $out = array(); foreach ( (array) get_editable_roles() as $slug => $role ) { $out[ $slug ] = isset( $role['name'] ) ? translate_user_role( $role['name'] ) : $slug; } return $out; } public static function enforcement_active() { if ( defined( 'AIEO_LOGIN_SECURITY_BYPASS' ) && AIEO_LOGIN_SECURITY_BYPASS ) { return false; } if ( self::is_local_host() ) { return false; } return (bool) apply_filters( 'aieo_login_security_enforcement_active', true ); } public static function is_local_host() { $host = ''; if ( isset( $_SERVER['HTTP_HOST'] ) ) { $host = strtolower( sanitize_text_field( wp_unslash( $_SERVER['HTTP_HOST'] ) ) ); } if ( '' === $host ) { $host = strtolower( (string) wp_parse_url( home_url(), PHP_URL_HOST ) ); } $host = preg_replace( '/:\d+$/', '', $host ); if ( 'localhost' === $host || '127.0.0.1' === $host || '::1' === $host ) { return true; } foreach ( array( '.local', '.test', '.localhost', '.ddev.site' ) as $suffix ) { if ( substr( $host, -strlen( $suffix ) ) === $suffix ) { return true; } } return false; } public static function feature_active( $feature ) { if ( ! self::enforcement_active() ) { return false; } $f = self::feature( $feature ); if ( empty( $f['enabled'] ) ) { return false; } if ( 'login_url' === $feature && '' === (string) $f['slug'] ) { return false; } return true; } public static function client_ip() { $ip = isset( $_SERVER['REMOTE_ADDR'] ) ? sanitize_text_field( wp_unslash( $_SERVER['REMOTE_ADDR'] ) ) : ''; $ip = (string) apply_filters( 'aieo_login_security_client_ip', $ip ); return filter_var( $ip, FILTER_VALIDATE_IP ) ? $ip : ''; } public static function fingerprint( $value ) { $value = (string) $value; if ( '' === $value ) { return ''; } return hash_hmac( 'sha256', strtolower( $value ), wp_salt( 'auth' ) ); } public static function encrypt( $plain ) { $plain = (string) $plain; if ( '' === $plain || ! function_exists( 'openssl_encrypt' ) ) { return $plain; } $iv = openssl_random_pseudo_bytes( 16 ); $cipher = openssl_encrypt( $plain, 'aes-256-cbc', self::crypt_key(), OPENSSL_RAW_DATA, $iv ); if ( false === $cipher ) { return ''; } return base64_encode( $iv . $cipher ); } public static function decrypt( $stored ) { $stored = (string) $stored; if ( '' === $stored || ! function_exists( 'openssl_decrypt' ) ) { return $stored; } $raw = base64_decode( $stored, true ); if ( false === $raw || strlen( $raw ) <= 16 ) { return ''; } $iv = substr( $raw, 0, 16 ); $plain = openssl_decrypt( substr( $raw, 16 ), 'aes-256-cbc', self::crypt_key(), OPENSSL_RAW_DATA, $iv ); return ( false === $plain ) ? '' : $plain; } private static function crypt_key() { return hash( 'sha256', wp_salt( 'auth' ) . '|aieo_login_security', true ); } public static function equals( $a, $b ) { return hash_equals( (string) $a, (string) $b ); } }