wp-m365-login/includes/class-m365-login.php
Friederich Loheide 4edf20bc45
Some checks are pending
CI / PHP lint (7.4) (pull_request) Waiting to run
CI / PHP lint (8.0) (pull_request) Waiting to run
CI / PHP lint (8.1) (pull_request) Waiting to run
CI / PHP lint (8.2) (pull_request) Waiting to run
CI / PHP lint (8.3) (pull_request) Waiting to run
CI / PHP lint (8.4) (pull_request) Waiting to run
CI / WordPress Coding Standards (pull_request) Waiting to run
CI / WordPress.org Plugin Check (pull_request) Waiting to run
Add Microsoft 365 user sync with roles, profile fields and deprovisioning
New "User sync" tab that imports Microsoft 365 / Entra ID users as
WordPress accounts and keeps them up to date:

- Scope: whole tenant or the (nested) members of selected groups,
  guests optional, e-mail domain allow-list respected. Existing accounts
  are linked by e-mail address.
- Roles: selectable default role plus a group -> role mapping (in
  addition to or instead of the default role, first match wins).
  Roles of pre-existing accounts are only managed on request.
- Profile: selectable Graph attributes (names, job title, department,
  phones, address, language, ...) and the profile photo as avatar.
- Deprovisioning: accounts disabled or deleted in Microsoft 365 (or
  removed from the sync groups) are deactivated or deleted; accounts
  deactivated by the sync are reactivated automatically. Deactivated
  accounts lose every sign-in path and all sessions.
- Safeguards: dry run, safety stop above 20 % (min. 5) deprovisioning,
  abort on any Graph error, "deleted" only on a 404 for the object ID,
  protected pre-existing administrators and own account, content
  reassignment required for deletion, run lock.
- Runs manually, via WP-Cron or `wp m365-login sync [--dry-run]`.
- Users screen column with deactivate/reactivate row actions and a
  read-only Microsoft 365 section on the profile screen.

The Graph client gains paging, retry on throttling and user, group
member and photo endpoints. The group picker is now reusable.
Version 1.1.0, German translations (du/Sie), docs and audit addendum.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-23 16:34:30 +00:00

141 lines
3.1 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' ) );
}
/**
* 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' );
}
}
}