- Privileged accounts: the UPN rule also applies when bind_oid is off or the account is not bound; privileges are checked on every site of a multisite user, include code/HTML capabilities (unfiltered_html, plugins, themes, users) and the remembered roles of deactivated accounts. - send_auth_cookies protection also works on WordPress 6.0/6.1. - Run lock via INSERT IGNORE (atomic), refreshed during long runs; a shutdown handler reports fatal errors and frees the lock. - Deprovisioning only for accounts linked in the current tenant (tenant recorded per account; legacy links not found are left alone). - Safety stop based on the accounts linked before the run; new safety stop for removals of administrative roles. - Disable is idempotent; row-action nonces are bound to the state. - Profile photos are re-encoded to 240 px (drops EXIF and appended data), size-limited while downloading, removed on deactivation; index.php guard in the photo folder. - Privacy exporter and eraser for the copied data. - One-time migration hardens accounts deactivated by 1.0 and cleans a stored certificate bundle; the .cer download is always re-exported. - Password fields hidden in button-only mode even when the connection is broken; settings written non-autoloaded; robust user ID queries. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
158 lines
3.6 KiB
PHP
158 lines
3.6 KiB
PHP
<?php
|
|
/**
|
|
* Plugin bootstrap.
|
|
*
|
|
* @package M365_Login
|
|
*/
|
|
|
|
defined( 'ABSPATH' ) || exit;
|
|
|
|
/**
|
|
* Wires the individual components together.
|
|
*/
|
|
final class M365_Login {
|
|
|
|
/**
|
|
* Singleton instance.
|
|
*
|
|
* @var M365_Login|null
|
|
*/
|
|
private static $instance = null;
|
|
|
|
/**
|
|
* Settings component.
|
|
*
|
|
* @var M365_Login_Settings
|
|
*/
|
|
public $settings;
|
|
|
|
/**
|
|
* Authentication component.
|
|
*
|
|
* @var M365_Login_Auth
|
|
*/
|
|
public $auth;
|
|
|
|
/**
|
|
* Microsoft Graph client.
|
|
*
|
|
* @var M365_Login_Graph
|
|
*/
|
|
public $graph;
|
|
|
|
/**
|
|
* User sync component.
|
|
*
|
|
* @var M365_Login_Sync
|
|
*/
|
|
public $sync;
|
|
|
|
/**
|
|
* Login button component.
|
|
*
|
|
* @var M365_Login_Button
|
|
*/
|
|
public $button;
|
|
|
|
/**
|
|
* Admin component.
|
|
*
|
|
* @var M365_Login_Admin|null
|
|
*/
|
|
public $admin = null;
|
|
|
|
/**
|
|
* Returns the singleton.
|
|
*
|
|
* @return M365_Login
|
|
*/
|
|
public static function instance() {
|
|
if ( null === self::$instance ) {
|
|
self::$instance = new self();
|
|
}
|
|
return self::$instance;
|
|
}
|
|
|
|
/**
|
|
* Constructor.
|
|
*/
|
|
private function __construct() {
|
|
add_action( 'init', array( $this, 'load_textdomain' ) );
|
|
|
|
$this->settings = new M365_Login_Settings();
|
|
$this->graph = new M365_Login_Graph( $this->settings );
|
|
$this->auth = new M365_Login_Auth( $this->settings, $this->graph );
|
|
$this->sync = new M365_Login_Sync( $this->settings, $this->graph );
|
|
$this->button = new M365_Login_Button( $this->settings );
|
|
|
|
if ( is_admin() ) {
|
|
$this->admin = new M365_Login_Admin( $this->settings, $this->auth, $this->graph, $this->sync );
|
|
}
|
|
|
|
add_filter( 'plugin_action_links_' . plugin_basename( M365_LOGIN_FILE ), array( $this, 'action_links' ) );
|
|
add_action( 'init', array( $this, 'maybe_upgrade' ), 1 );
|
|
}
|
|
|
|
/**
|
|
* One-time data migrations after an update.
|
|
*/
|
|
public function maybe_upgrade() {
|
|
$stored = (string) get_option( 'm365_login_version', '1.0.0' );
|
|
if ( version_compare( $stored, M365_LOGIN_VERSION, '>=' ) ) {
|
|
return;
|
|
}
|
|
update_option( 'm365_login_version', M365_LOGIN_VERSION );
|
|
|
|
if ( version_compare( $stored, '1.1.0', '<' ) ) {
|
|
M365_Login_Sync::harden_legacy_disabled();
|
|
$this->settings->normalise_stored_certificate();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Loads bundled translations.
|
|
*/
|
|
public function load_textdomain() {
|
|
load_plugin_textdomain( 'm365-login', false, dirname( plugin_basename( M365_LOGIN_FILE ) ) . '/languages' );
|
|
}
|
|
|
|
/**
|
|
* Adds a "Settings" link on the plugins screen.
|
|
*
|
|
* @param string[] $links Existing links.
|
|
* @return string[]
|
|
*/
|
|
public function action_links( $links ) {
|
|
$url = admin_url( 'admin.php?page=m365-login' );
|
|
array_unshift( $links, '<a href="' . esc_url( $url ) . '">' . esc_html__( 'Settings', 'm365-login' ) . '</a>' );
|
|
return $links;
|
|
}
|
|
|
|
/**
|
|
* Activation hook: seed defaults and check requirements.
|
|
*/
|
|
public static function activate() {
|
|
if ( version_compare( PHP_VERSION, '7.4', '<' ) ) {
|
|
deactivate_plugins( plugin_basename( M365_LOGIN_FILE ) );
|
|
wp_die(
|
|
esc_html__( 'M365 Login requires PHP 7.4 or newer.', 'm365-login' ),
|
|
esc_html__( 'Plugin activation failed', 'm365-login' ),
|
|
array( 'back_link' => true )
|
|
);
|
|
}
|
|
|
|
if ( ! function_exists( 'openssl_encrypt' ) ) {
|
|
deactivate_plugins( plugin_basename( M365_LOGIN_FILE ) );
|
|
wp_die(
|
|
esc_html__( 'M365 Login requires the PHP OpenSSL extension (needed to verify Microsoft token signatures and to encrypt the client secret).', 'm365-login' ),
|
|
esc_html__( 'Plugin activation failed', 'm365-login' ),
|
|
array( 'back_link' => true )
|
|
);
|
|
}
|
|
|
|
$settings = new M365_Login_Settings();
|
|
if ( false === get_option( M365_LOGIN_OPTION, false ) ) {
|
|
add_option( M365_LOGIN_OPTION, $settings->defaults(), '', 'no' );
|
|
}
|
|
}
|
|
}
|