From 24448675146a1ff625268e5743b12f15d6fb61f1 Mon Sep 17 00:00:00 2001
From: Brad Kovach
Date: Sun, 14 May 2023 06:21:21 +0000
Subject: [PATCH 01/11] move class AADSSO into its own file add support for
mu-plugins use rename plugin class files to align with WP code style modify
antiforgery_id to use wp nonces instead of a GUID/UUID lots of WordPress code
style fixes rework settings class to use magic getter, and a resolution
hierarchy constant, db, default, null secure all admin actions by using
nonces add GUI to support mu-plugins/constants fix lots of phpdoc lots of new
documentation
---
README.md | 167 ++-
Settings.php | 283 -----
SettingsPage.php | 715 -----------
aad-sso-wordpress.php | 786 +-----------
...p => class-aadsso-authorization-helper.php | 104 +-
...elper.php => class-aadsso-graph-helper.php | 75 +-
class-aadsso-html-helper.php | 49 +
class-aadsso-settings-page.php | 1088 +++++++++++++++++
class-aadsso-settings.php | 253 ++++
class-aadsso.php | 832 +++++++++++++
view/settings.php | 154 ++-
11 files changed, 2631 insertions(+), 1875 deletions(-)
delete mode 100644 Settings.php
delete mode 100644 SettingsPage.php
rename AuthorizationHelper.php => class-aadsso-authorization-helper.php (58%)
rename GraphHelper.php => class-aadsso-graph-helper.php (57%)
create mode 100644 class-aadsso-html-helper.php
create mode 100644 class-aadsso-settings-page.php
create mode 100644 class-aadsso-settings.php
create mode 100644 class-aadsso.php
diff --git a/README.md b/README.md
index afb4fbb..bb1f844 100644
--- a/README.md
+++ b/README.md
@@ -138,72 +138,72 @@ The different fields that can be defined in the settings JSON in **Settings** >
Users are matched by their email address in WordPress, and whichever role they have in WordPress is maintained.
-| Setting | Example value
-| --- | ---
-| Display name | Contoso
-| Client ID | 9054eff5-bfef-4cc5-82fd-8c35534e48f9
-| Client Secret | NTY5MmE5YjMwMGY2MWQ0NjU5MzYxNjdjNzE1OGNiZmY=
-| Reply URL | https://www.example.com/blog/wp-login.php
-| Field to match to UPN | Email Address
+| Setting | Example value |
+| --------------------- | -------------------------------------------- |
+| Display name | Contoso |
+| Client ID | 9054eff5-bfef-4cc5-82fd-8c35534e48f9 |
+| Client Secret | NTY5MmE5YjMwMGY2MWQ0NjU5MzYxNjdjNzE1OGNiZmY= |
+| Redirect URL | https://www.example.com/blog/wp-login.php |
+| Field to match to UPN | Email Address |
### Match on username alias
Users are matched by their login names in WordPress and the alias portion of their Azure AD UserPrincipalName. Whichever role they have in WordPress is maintained.
-| Setting | Example value
-| --- | ---
-| Display name | Contoso
-| Client ID | 9054eff5-bfef-4cc5-82fd-8c35534e48f9
-| Client Secret | NTY5MmE5YjMwMGY2MWQ0NjU5MzYxNjdjNzE1OGNiZmY=
-| Reply URL | https://www.example.com/blog/wp-login.php
-| Field to match to UPN | Login Name
-| Match on alias of the UPN | Yes
+| Setting | Example value |
+| ------------------------- | -------------------------------------------- |
+| Display name | Contoso |
+| Client ID | 9054eff5-bfef-4cc5-82fd-8c35534e48f9 |
+| Client Secret | NTY5MmE5YjMwMGY2MWQ0NjU5MzYxNjdjNzE1OGNiZmY= |
+| Redirect URL | https://www.example.com/blog/wp-login.php |
+| Field to match to UPN | Login Name |
+| Match on alias of the UPN | Yes |
### Group membership-based roles, no default role
Users are matched by their login names in WordPress, and WordPress roles are dictated by membership to a given Azure AD group. Access is denied if they are not members of any of these groups.
-| Setting | Example value
-| --- | ---
-| Display name | Contoso
-| Client ID | 9054eff5-bfef-4cc5-82fd-8c35534e48f9
-| Client Secret | NTY5MmE5YjMwMGY2MWQ0NjU5MzYxNjdjNzE1OGNiZmY=
-| Reply URL | https://www.example.com/blog/wp-login.php
-| Field to match to UPN | Login Name
-| Enable Azure AD group to WP role association | Yes
-| Default WordPress role if not in Azure AD group | (None, deny access)
-| WordPress role to Azure AD group map |
Administrator
5d1915c4-2373-42ba-9796-7c092fa1dfc6
Editor
21c0f87b-4b65-48c1-9231-2f9295ef601c
Author
f5784693-11e5-4812-87db-8c6e51a18ffd
Contributor
780e055f-7e64-4e34-9ff3-012910b7e5ad
Subscriber
f1be9515-0aeb-458a-8c0a-30a03c1afb67
+| Setting | Example value |
+| ----------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| Display name | Contoso |
+| Client ID | 9054eff5-bfef-4cc5-82fd-8c35534e48f9 |
+| Client Secret | NTY5MmE5YjMwMGY2MWQ0NjU5MzYxNjdjNzE1OGNiZmY= |
+| Redirect URL | https://www.example.com/blog/wp-login.php |
+| Field to match to UPN | Login Name |
+| Enable Azure AD group to WP role association | Yes |
+| Default WordPress role if not in Azure AD group | (None, deny access) |
+| WordPress role to Azure AD group map |
Administrator
5d1915c4-2373-42ba-9796-7c092fa1dfc6
Editor
21c0f87b-4b65-48c1-9231-2f9295ef601c
Author
f5784693-11e5-4812-87db-8c6e51a18ffd
Contributor
780e055f-7e64-4e34-9ff3-012910b7e5ad
Subscriber
f1be9515-0aeb-458a-8c0a-30a03c1afb67
|
### Group membership-based roles with default role
Users are matched by their login names in WordPress, and WordPress roles are dictated by membership to a given Azure AD group. If the user is not a part of any of these groups, they are assigned the *Author* role.
-| Setting | Example value
-| --- | ---
-| Display name | Contoso
-| Client ID | 9054eff5-bfef-4cc5-82fd-8c35534e48f9
-| Client Secret | NTY5MmE5YjMwMGY2MWQ0NjU5MzYxNjdjNzE1OGNiZmY=
-| Reply URL | https://www.example.com/blog/wp-login.php
-| Field to match to UPN | Login Name
-| Enable Azure AD group to WordPress role association | Yes
-| Default WordPress role if not in Azure AD group | Author
-| WordPress role to Azure AD group map |
Administrator
5d1915c4-2373-42ba-9796-7c092fa1dfc6
Editor
21c0f87b-4b65-48c1-9231-2f9295ef601c
Author
f5784693-11e5-4812-87db-8c6e51a18ffd
Contributor
780e055f-7e64-4e34-9ff3-012910b7e5ad
Subscriber
f1be9515-0aeb-458a-8c0a-30a03c1afb67
+| Setting | Example value |
+| --------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| Display name | Contoso |
+| Client ID | 9054eff5-bfef-4cc5-82fd-8c35534e48f9 |
+| Client Secret | NTY5MmE5YjMwMGY2MWQ0NjU5MzYxNjdjNzE1OGNiZmY= |
+| Reply URL | https://www.example.com/blog/wp-login.php |
+| Field to match to UPN | Login Name |
+| Enable Azure AD group to WordPress role association | Yes |
+| Default WordPress role if not in Azure AD group | Author |
+| WordPress role to Azure AD group map |
Administrator
5d1915c4-2373-42ba-9796-7c092fa1dfc6
Editor
21c0f87b-4b65-48c1-9231-2f9295ef601c
Author
f5784693-11e5-4812-87db-8c6e51a18ffd
Contributor
780e055f-7e64-4e34-9ff3-012910b7e5ad
Subscriber
f1be9515-0aeb-458a-8c0a-30a03c1afb67
|
### Group membership-based roles, default role, auto-provision
Users are matched by their email in WordPress, and WordPress roles are dictated by membership to a given Azure AD group. If the user doesn't exist in WordPress yet, they will be auto-provisioned. If the user is not a part of any of these groups, they are assigned the *Subscriber* role.
-| Setting | Example value
-| --- | ---
-| Display name | Contoso
-| Client ID | 9054eff5-bfef-4cc5-82fd-8c35534e48f9
-| Client Secret | NTY5MmE5YjMwMGY2MWQ0NjU5MzYxNjdjNzE1OGNiZmY=
-| Reply URL | https://www.example.com/blog/wp-login.php
-| Field to match to UPN | Email Address
-| Enable auto-provisioning | Yes
-| Enable Azure AD group to WP role association | Yes
-| Default WordPress role if not in Azure AD group | Subscriber
-| WordPress role to Azure AD group map |
Administrator
5d1915c4-2373-42ba-9796-7c092fa1dfc6
Editor
21c0f87b-4b65-48c1-9231-2f9295ef601c
Author
f5784693-11e5-4812-87db-8c6e51a18ffd
Contributor
780e055f-7e64-4e34-9ff3-012910b7e5ad
Subscriber
f1be9515-0aeb-458a-8c0a-30a03c1afb67
+| Setting | Example value |
+| ----------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| Display name | Contoso |
+| Client ID | 9054eff5-bfef-4cc5-82fd-8c35534e48f9 |
+| Client Secret | NTY5MmE5YjMwMGY2MWQ0NjU5MzYxNjdjNzE1OGNiZmY= |
+| Reply URL | https://www.example.com/blog/wp-login.php |
+| Field to match to UPN | Email Address |
+| Enable auto-provisioning | Yes |
+| Enable Azure AD group to WP role association | Yes |
+| Default WordPress role if not in Azure AD group | Subscriber |
+| WordPress role to Azure AD group map |
Administrator
5d1915c4-2373-42ba-9796-7c092fa1dfc6
Editor
21c0f87b-4b65-48c1-9231-2f9295ef601c
Author
f5784693-11e5-4812-87db-8c6e51a18ffd
Contributor
780e055f-7e64-4e34-9ff3-012910b7e5ad
Subscriber
f1be9515-0aeb-458a-8c0a-30a03c1afb67
|
## Groups
@@ -228,4 +228,77 @@ Most of the OpenID Connect endpoints and configuration (e.g. signing keys, etc.)
If you've configured this plugin to automatically redirect to Azure AD for sign-in, but something is misconfigured, you may find yourself locked out of your site's admin dashboard.
-To log in to your site *without* automatically redirecting to Azure AD (thus giving you an opportunity to enter a regular username and password), you can append `?aadsso_no_redirect=please` to the login URL. For example, if your login URL is `https://example.com/wp-login.php`, navigating to `https://example.com/wp-login.php?aadsso_no_redirect=please` will prevent any automatic redirects.
\ No newline at end of file
+To log in to your site *without* automatically redirecting to Azure AD (thus giving you an opportunity to enter a regular username and password), you can append `?aadsso_no_redirect=please` to the login URL. For example, if your login URL is `https://example.com/wp-login.php`, navigating to `https://example.com/wp-login.php?aadsso_no_redirect=please` will prevent any automatic redirects.
+
+### Constants Support
+
+*since 0.8.0*
+
+The plugin can be configured with defined constants. This allows for zero-database configuration, suitable for automated deployments, or deployments using environment variables.
+
+Constants must be primitive/scalar values (string, number, bool, int), so if you use `AADSSO_ENABLE_AAD_GROUP_TO_WP_ROLE` and `AADSSO_AAD_GROUP_TO_WP_ROLE_MAP`, the value of `AADSSO_AAD_GROUP_TO_WP_ROLE_MAP` must be a JSON-encoded string.
+
+| Constant Name | Type | Description | Default |
+| -------------------------------------- | ------ | ------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------- |
+| `AADSSO_ORG_DISPLAY_NAME` | string | Human-friendly name to describe your organization. | `get_bloginfo('name')` |
+| `AADSSO_ORG_DOMAIN_HINT` | string | tenant or Azure AD directory id. |
+| `AADSSO_CLIENT_ID` | string | Azure AD-provided GUID identifying your WordPress application. |
+| `AADSSO_CLIENT_SECRET` | string | Azure AD-provided secret. |
+| `AADSSO_REDIRECT_URI` | string | URL to redirect to when a user authenticates successfully. | `wp_login_url()` |
+| `AADSSO_LOGOUT_REDIRECT_URI` | string | URL to redirect to when a user logs out. | `wp_login_url()` |
+| `AADSSO_ENABLE_FULL_LOGOUT` | bool | When enabled, a WordPress logout will also trigger an Azure AD logout. | `false` |
+| `AADSSO_FIELD_TO_MATCH_TO_UPN` | string | WordPress field to map to Azure AD UPN | `'email'` |
+| `AADSSO_MATCH_ON_UPN_ALIAS` | type | Allow `user_login` to be matched on on the user portion of an email address. | `false` |
+| `AADSSO_ENABLE_AUTO_PROVISIONING` | type | `true` to allow WordPress to create new users for Azure AD users automatically. | `false` |
+| `AADSSO_ENABLE_AUTO_FORWARD_TO_AAD` | type | `true` to skip the WordPress `/wp-login.php` screen entirely. | `false` |
+| `AADSSO_ENABLE_AAD_GROUP_TO_WP_ROLE` | type | `true` to map Azure AD groups to WordPress roles automatically. | `false` |
+| `AADSSO_DEFAULT_WP_ROLE` | type | WordPress role slug for auto-provisioned users. When `null`, access is denied. | `null` |
+| `AADSSO_AAD_GROUP_TO_WP_ROLE_MAP` | type | Map of Azure AD Group ID to WordPress role slugs. **Constant value be JSON encoded** | `{}` |
+| `AADSSO_GRAPH_ENDPOINT` | type | Endpoint for fetching information about users and groups. | `https://graph.microsoft.com` |
+| `AADSSO_GRAPH_VERSION` | type | Version of Microsoft Graph to use. | `v1.0` |
+| `AADSSO_OPENID_CONFIGURATION_ENDPOINT` | type | `.well-known` endpoint to retrieve automatic OpenID configuration from. | `https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration` |
+| `AADSSO_AUTHORIZATION_ENDPOINT` | type | By default, this is detected automatically. | `null` |
+| `AADSSO_END_SESSION_ENDPOINT` | type | By default, this is detected automatically. | `null` |
+| `AADSSO_JWKS_URI` | type | By default, this is detected automatically. | `null` |
+| `AADSSO_TOKEN_ENDPOINT` | type | By default, this is detected automatically. | `null` |
+
+#### Example `wp-config.php` with Azure AD Group-to-Role Mapping
+
+```php
+ 'administrator',
+ 'a0b1c2d3-4e5f-4a7b-ac9d-0e1f2a3b4c5d' => 'editor',
+ )
+ )
+);
+```
+
+#### Reset Constant
+
+The plugin's settings can be reset with the `AADSSO_RESET_SETTINGS` constant. When `true`, the plugin will delete all of its options from the database. This has no effect on settings defined with constants.
+
+```php
+
+### Must-Use Plugin Support
+
+*since 0.8.0*
+
+The plugin now supports deployment as a must-use plugin. Coupled with constants for configuration, you can rapidly provision unattended Azure AD SSO WordPress installations. To be activated, the plugin should be placed in `wp-content/mu-plugins` and a `wp-content/mu-plugins/load-aad-sso-wordpress.php` file should be created.
+
+```php
+ get_bloginfo( 'name' ),
- 'field_to_match_to_upn' => 'email',
- 'default_wp_role' => null,
- 'enable_auto_provisioning' => false,
- 'match_on_upn_alias' => false,
- 'enable_auto_forward_to_aad' => false,
- 'enable_aad_group_to_wp_role' => false,
- 'redirect_uri' => wp_login_url(),
- 'logout_redirect_uri' => wp_login_url(),
- 'openid_configuration_endpoint' => 'https://login.microsoftonline.com/common/.well-known/openid-configuration',
- );
-
- if ( null === $key ) {
- return $defaults;
- } else {
- if ( isset( $defaults[ $key ] ) ) {
- return $defaults[ $key ];
- } else {
- return null;
- }
- }
- }
-
- /**
- * Gets the (only) instance of the plugin.
- *
- * @return self The (only) instance of the class.
- */
- public static function get_instance() {
- if ( ! self::$instance ) {
- self::$instance = new self;
- }
- return self::$instance;
- }
-
- /**
- * Initializes values for using stored settings and cached Azure AD configuration.
- *
- * @return \AADSSO_Settings The (only) configured instance of this class.
- */
- public static function init() {
-
- $instance = self::get_instance();
-
- // First, retrieve the settings stored in the WordPress database.
- $instance->load_settings( get_option( 'aadsso_settings' ) );
-
- /*
- * Then, add the settings stored in the OpenID Connect configuration endpoint.
- * We're using transient as a cache, to prevent from making a request on every WP page load.
- * Default transient expiration is one hour (3600 seconds), but in case a forced load is
- * required, adding aadsso_reload_openid_config=1 in the URL will do the trick.
- */
- $openid_configuration = get_transient( 'aadsso_openid_configuration' );
- if( false === $openid_configuration || isset( $_GET['aadsso_reload_openid_config'] ) ) {
- $openid_configuration = json_decode(
- self::get_remote_contents( $instance->openid_configuration_endpoint ),
- true // Return associative array
- );
- set_transient( 'aadsso_openid_configuration', $openid_configuration, 3600 );
- }
- $instance->load_settings( $openid_configuration );
-
- return $instance;
- }
-
- /**
- * Loads contents of a text file (local or remote).
- *
- * @param string $file_path The path to the file. May be local or remote.
- *
- * @return string The contents of the file.
- */
- public static function get_remote_contents( $file_path ) {
-
- $response = wp_remote_get( $file_path );
- $file_contents = wp_remote_retrieve_body( $response );
-
- return $file_contents;
- }
-
- /**
- * Sets provided settings inside the current instance.
- *
- * @param array $settings An associative array of settings to be added to current configuration.
- *
- * @return \AADSSO_Settings The current (only) instance with new configuration.
- */
- function load_settings( $settings ) {
-
- // Expecting $settings to be an associative array. Do nothing if it isn't.
- if ( ! is_array( $settings ) || empty( $settings ) ) {
- return $this;
- }
-
- /*
- * Invert the => map (which is what is stored in the database) to a flat
- * => map is used during the authorization check. If a group appears twice, the first
- * occurence (the first role) will take precedence.
- */
- if( ! empty( $settings['role_map'] ) ) {
- $settings['aad_group_to_wp_role_map'] = array();
- foreach ( $settings['role_map'] as $role_slug => $group_ids_list ) {
- $group_ids = explode( ',', $group_ids_list );
- if ( ! empty( $group_ids ) ) {
- foreach ( $group_ids as $group_id ) {
- if ( ! isset( $settings['aad_group_to_wp_role_map'][ $group_id ] ) ) {
- $settings['aad_group_to_wp_role_map'][ $group_id ] = $role_slug;
- }
- }
- }
- }
- }
-
- // Overwrite any provided setting values.
- foreach ( $settings as $key => $value ) {
- if ( property_exists( $this, $key ) ) {
- $this->{$key} = $value;
- }
- }
- return $this;
- }
-}
diff --git a/SettingsPage.php b/SettingsPage.php
deleted file mode 100644
index 802e515..0000000
--- a/SettingsPage.php
+++ /dev/null
@@ -1,715 +0,0 @@
-settings = get_option( 'aadsso_settings', $default_settings );
- foreach ( $default_settings as $key => $default_value ) {
- if ( ! isset( $this->settings[ $key ] ) ) {
- $this->settings[ $key ] = $default_value;
- }
- }
- }
-
- /**
- * Clears settings if $_GET['aadsso_nonce'] is set and if the nonce is valid.
- */
- public function maybe_reset_settings()
- {
- $should_reset_settings = isset( $_GET['aadsso_nonce'] )
- && wp_verify_nonce( $_GET['aadsso_nonce'], 'aadsso_reset_settings' );
- if ( $should_reset_settings ) {
- delete_option( 'aadsso_settings' );
- wp_redirect( admin_url( 'options-general.php?page=aadsso_settings&aadsso_reset=success' ) );
- }
- }
-
- /**
- * Migrates old settings (Settings.json) to the database and attempts to delete the old settings file.
- */
- public function maybe_migrate_settings() {
- /**
- * Settings should only be migrated if
- * - The request came from a nonced link
- * - The nonce action is 'aadsso_migrate_settings'
- * - There is a legacy settings path defined
- * - There is a file at that legacy settings path
- */
- $should_migrate_settings = isset( $_GET['aadsso_nonce'] )
- && wp_verify_nonce( $_GET['aadsso_nonce'], 'aadsso_migrate_from_json' )
- && defined( 'AADSSO_SETTINGS_PATH' )
- && file_exists( AADSSO_SETTINGS_PATH );
-
- if ( $should_migrate_settings ) {
-
- $legacy_settings = json_decode( file_get_contents( AADSSO_SETTINGS_PATH ), true );
-
- if ( null === $legacy_settings ) {
- wp_redirect( admin_url( 'options-general.php?page=aadsso_settings&aadsso_migrate_from_json_status=invalid_json') );
- }
-
- // If aad_group_to_wp_role_map is set in the legacy settings, build the inverted role_map array,
- // which is what is ultimately saved in the database.
- if ( isset( $legacy_settings['aad_group_to_wp_role_map'] ) ) {
- $legacy_settings['role_map'] = array();
- foreach ($aad_group_to_wp_role_map as $group_id => $role_slug ) {
- if ( ! isset( $legacy_settings['role_map'][$role_slug] ) ) {
- $legacy_settings['role_map'][$role_slug] = $group_id;
- } else {
- $legacy_settings['role_map'][$role_slug] .= ',' . $group_id;
- }
- }
- }
-
- $sanitized_settings = $this->sanitize_settings( $legacy_settings );
-
- update_option( 'aadsso_settings', $sanitized_settings );
-
- if ( is_writable( AADSSO_SETTINGS_PATH ) && is_writable( dirname( AADSSO_SETTINGS_PATH ) ) && unlink( AADSSO_SETTINGS_PATH ) ) {
- wp_redirect( admin_url( 'options-general.php?page=aadsso_settings&aadsso_migrate_from_json_status=success' ) );
- } else {
- wp_redirect( admin_url( 'options-general.php?page=aadsso_settings&aadsso_migrate_from_json_status=manual' ) );
- }
- }
- }
-
- /**
- * Shows messages about the state of the migration operation
- */
- public function notify_json_migrate_status() {
- if( isset( $_GET['aadsso_migrate_from_json_status'] ) ) {
- if( 'success' === $_GET['aadsso_migrate_from_json_status'] ) {
- echo '
'
- . __( 'Legacy settings have been migrated and the old configuration file has been deleted.', 'aad-sso-wordpress' )
- . __('To finish migration, unset AADSSO_SETTINGS_PATH from wp-config.php. ', 'aad-sso-wordpress')
- .'
'
- . sprintf( __('Legacy settings could not be migrated from %s. ', 'aad-sso-wordpress'), AADSSO_SETTINGS_PATH )
- . esc_html( 'File could not be parsed as JSON. ', 'aad-sso-wordpress' )
- . esc_html( 'Delete the file, or check its syntax.', 'aad-sso-wordpress' )
- .'
';
- }
- }
- }
-
- /**
- * Notifies user if settings reset was successful.
- */
- public function notify_if_reset_successful()
- {
- if ( isset( $_GET['aadsso_reset'] ) && 'success' === $_GET['aadsso_reset'] ) {
- echo '
'
- . __( 'Single Sign-on with Azure Active Directory settings have been reset to default.',
- 'aad-sso-wordpress' )
- .'
';
- }
- }
-
- /**
- * Adds the 'Azure AD' options page.
- */
- public function add_options_page() {
- $this->options_page_id = add_options_page(
- __( 'Azure Active Directory Settings', 'aad-sso-wordpress' ), // page_title
- 'Azure AD', // menu_title
- 'manage_options', // capability
- 'aadsso_settings', // menu_slug
- array( $this, 'render_admin_page' ) // function
- );
- }
-
- /**
- * Renders the 'Azure AD' settings page.
- */
- public function render_admin_page() {
- require_once( 'view/settings.php' );
- }
-
- /**
- * Registers settings, sections and fields.
- */
- public function register_settings() {
-
- register_setting(
- 'aadsso_settings', // option_group
- 'aadsso_settings', // option_name
- array( $this, 'sanitize_settings' ) // sanitize_callback
- );
-
- add_settings_section(
- 'aadsso_settings_general', // id
- __( 'General', 'aad-sso-wordpress' ), // title
- array( $this, 'settings_general_info' ), // callback
- 'aadsso_settings_page' // page
- );
-
- add_settings_section(
- 'aadsso_settings_advanced', // id
- __( 'Advanced', 'aad-sso-wordpress' ), // title
- array( $this, 'settings_advanced_info' ), // callback
- 'aadsso_settings_page' // page
- );
-
- add_settings_field(
- 'org_display_name', // id
- __( 'Display name', 'aad-sso-wordpress' ), // title
- array( $this, 'org_display_name_callback' ), // callback
- 'aadsso_settings_page', // page
- 'aadsso_settings_general' // section
- );
-
- add_settings_field(
- 'org_domain_hint', // id
- __( 'Domain hint', 'aad-sso-wordpress' ), // title
- array( $this, 'org_domain_hint_callback' ), // callback
- 'aadsso_settings_page', // page
- 'aadsso_settings_general' // section
- );
-
- add_settings_field(
- 'client_id', // id
- __( 'Client ID', 'aad-sso-wordpress' ), // title
- array( $this, 'client_id_callback' ), // callback
- 'aadsso_settings_page', // page
- 'aadsso_settings_general' // section
- );
-
- add_settings_field(
- 'client_secret', // id
- __( 'Client secret', 'aad-sso-wordpress' ), // title
- array( $this, 'client_secret_callback' ), // callback
- 'aadsso_settings_page', // page
- 'aadsso_settings_general' // section
- );
-
- add_settings_field(
- 'redirect_uri', // id
- __( 'Redirect URL', 'aad-sso-wordpress' ), // title
- array( $this, 'redirect_uri_callback' ), // callback
- 'aadsso_settings_page', // page
- 'aadsso_settings_general' // section
- );
-
- add_settings_field(
- 'logout_redirect_uri', // id
- __( 'Logout redirect URL', 'aad-sso-wordpress' ), // title
- array( $this, 'logout_redirect_uri_callback' ), // callback
- 'aadsso_settings_page', // page
- 'aadsso_settings_general' // section
- );
-
- add_settings_field(
- 'enable_full_logout', // id
- __( 'Enable full logout', 'aad-sso-wordpress' ), // title
- array( $this, 'enable_full_logout_callback' ), // callback
- 'aadsso_settings_page', // page
- 'aadsso_settings_general' // section
- );
-
- add_settings_field(
- 'field_to_match_to_upn', // id
- __( 'Field to match to UPN', 'aad-sso-wordpress' ), // title
- array( $this, 'field_to_match_to_upn_callback' ), // callback
- 'aadsso_settings_page', // page
- 'aadsso_settings_general' // section
- );
-
- add_settings_field(
- 'match_on_upn_alias', // id
- __( 'Match on alias of the UPN', 'aad-sso-wordpress' ), // title
- array( $this, 'match_on_upn_alias_callback' ), // callback
- 'aadsso_settings_page', // page
- 'aadsso_settings_general' // section
- );
-
- add_settings_field(
- 'enable_auto_provisioning', // id
- __( 'Enable auto-provisioning', 'aad-sso-wordpress' ), // title
- array( $this, 'enable_auto_provisioning_callback' ), // callback
- 'aadsso_settings_page', // page
- 'aadsso_settings_general' // section
- );
-
- add_settings_field(
- 'enable_auto_forward_to_aad', // id
- __( 'Enable auto-forward to Azure AD', 'aad-sso-wordpress' ), // title
- array( $this, 'enable_auto_forward_to_aad_callback' ), // callback
- 'aadsso_settings_page', // page
- 'aadsso_settings_general' // section
- );
-
- add_settings_field(
- 'enable_aad_group_to_wp_role', // id
- __( 'Enable Azure AD group to WP role association', 'aad-sso-wordpress' ), // title
- array( $this, 'enable_aad_group_to_wp_role_callback' ), // callback
- 'aadsso_settings_page', // page
- 'aadsso_settings_general' // section
- );
-
- add_settings_field(
- 'default_wp_role', // id
- __( 'Default WordPress role if not in Azure AD group', 'aad-sso-wordpress' ), // title
- array( $this, 'default_wp_role_callback' ), // callback
- 'aadsso_settings_page', // page
- 'aadsso_settings_general' // section
- );
-
- add_settings_field(
- 'role_map', // id
- __( 'WordPress role to Azure AD group map', 'aad-sso-wordpress' ), // title
- array( $this, 'role_map_callback' ), // callback
- 'aadsso_settings_page', // page
- 'aadsso_settings_general' // section
- );
-
- add_settings_field(
- 'openid_configuration_endpoint', // id
- __( 'OpenID Connect configuration endpoint', 'aad-sso-wordpress' ), // title
- array( $this, 'openid_configuration_endpoint_callback' ), // callback
- 'aadsso_settings_page', // page
- 'aadsso_settings_advanced' // section
- );
- }
-
- /**
- * Gets the array of roles determined by other plugins to be "editable".
- */
- function get_editable_roles() {
-
- global $wp_roles;
-
- $all_roles = $wp_roles->roles;
- $editable_roles = apply_filters( 'editable_roles', $all_roles );
-
- return $editable_roles;
- }
-
- /**
- * Cleans and validates form information before saving.
- *
- * @param array $input key-value information to be cleaned before saving.
- *
- * @return array The sanitized and valid data to be stored.
- */
- public function sanitize_settings( $input ) {
-
- $sanitary_values = array();
-
- $text_fields = array(
- 'org_display_name',
- 'org_domain_hint',
- 'client_id',
- 'client_secret',
- 'redirect_uri',
- 'logout_redirect_uri',
- 'openid_configuration_endpoint',
- );
-
- foreach ($text_fields as $text_field) {
- if ( isset( $input[ $text_field ] ) ) {
- $sanitary_values[ $text_field ] = sanitize_text_field( $input[ $text_field ] );
- }
- }
-
- // Default field_to_match_to_upn is 'email'
- $sanitary_values['field_to_match_to_upn'] = 'email';
- if ( isset( $input['field_to_match_to_upn'] )
- && in_array( $input['field_to_match_to_upn'], array( 'email', 'login' ) )
- ) {
- $sanitary_values['field_to_match_to_upn'] = $input['field_to_match_to_upn'];
- }
-
- // Default role for user that is not member of any Azure AD group is null, which denies access.
- $sanitary_values['default_wp_role'] = null;
- if ( isset( $input['default_wp_role'] ) ) {
- $sanitary_values['default_wp_role'] = sanitize_text_field( $input['default_wp_role'] );
- }
-
- // Booleans: when key == value, this is considered true, otherwise false.
- $boolean_settings = array(
- 'enable_auto_provisioning',
- 'enable_auto_forward_to_aad',
- 'enable_aad_group_to_wp_role',
- 'match_on_upn_alias',
- 'enable_full_logout',
- );
- foreach ( $boolean_settings as $boolean_setting )
- {
- if( isset( $input[ $boolean_setting ] ) ) {
- $sanitary_values[ $boolean_setting ] = ( $boolean_setting == $input[ $boolean_setting ] );
- } else {
- $sanitary_values[ $boolean_setting ] = false;
- }
- }
-
- /*
- * Many of the roles in WordPress will not have Azure AD groups associated with them.
- * Go over all roles, removing the mapping for empty ones.
- */
- if ( isset( $input['role_map'] ) ) {
- foreach( $input['role_map'] as $role_slug => $group_object_id ) {
- if( empty( $group_object_id ) ) {
- unset( $input['role_map'][ $role_slug ] );
- }
- }
- $sanitary_values['role_map'] = $input['role_map'];
- }
-
- // If the OpenID Connect configuration endpoint is changed, clear the cached values.
- $stored_oidc_config_endpoint = isset( $this->settings['openid_configuration_endpoint'] )
- ? $this->settings['openid_configuration_endpoint'] : null;
- if ( $stored_oidc_config_endpoint !== $sanitary_values['openid_configuration_endpoint'] ) {
- delete_transient( 'aadsso_openid_configuration' );
- AADSSO::debug_log('Setting \'openid_configuration_endpoint\' changed, cleared cached OpenID Connect values.');
- }
-
- return $sanitary_values;
- }
-
- /**
- * Renders details for the General settings section.
- */
- public function settings_general_info() { }
-
- /**
- * Renders details for the Advanced settings section.
- */
- public function settings_advanced_info() { }
-
- /**
- * Renders the `role_map` picker control.
- */
- function role_map_callback() {
- printf( '
%s
',
- __( 'Map WordPress roles to Azure Active Directory groups.', 'aad-sso-wordpress' )
- );
- echo '
';
- }
-
- /**
- * Renders the `org_display_name` form control.
- */
- public function org_display_name_callback() {
- $this->render_text_field( 'org_display_name' );
- printf(
- '
%s
',
- __( 'Display Name will be shown on the WordPress login screen.', 'aad-sso-wordpress' )
- );
- }
-
- /**
- * Renders the `org_domain_hint` form control.
- */
- public function org_domain_hint_callback() {
- $this->render_text_field( 'org_domain_hint' );
- printf(
- '
%s
',
- __( 'Provides a hint to Azure AD about the domain or tenant they will be logging in to. If '
- . 'the domain is federated, the user will be automatically redirected to federation '
- . 'endpoint.', 'aad-sso-wordpress' )
- );
- }
-
- /**
- * Renders the `client_id` form control
- */
- public function client_id_callback() {
- $this->render_text_field( 'client_id' );
- printf(
- '
%s
',
- __( 'The client ID of the Azure AD application representing this blog.', 'aad-sso-wordpress' )
- );
- }
-
- /**
- * Renders the `client_secret` form control
- **/
- public function client_secret_callback() {
- $this->render_text_field( 'client_secret' );
- printf(
- '
%s
',
- __( 'A secret key for the Azure AD application representing this blog.', 'aad-sso-wordpress' )
- );
- }
-
- /**
- * Renders the `redirect_uri` form control
- **/
- public function redirect_uri_callback() {
- $this->render_text_field( 'redirect_uri' );
- printf(
- ' %s'
- . '
%s
',
- wp_login_url(),
- __( 'Set default', 'aad-sso-wordpress' ),
- __( 'The URL where the user is redirected to after authenticating with Azure AD. '
- . 'This URL must be registered in Azure AD as a valid redirect URL, and it must be a '
- . 'page that invokes the "authenticate" filter. If you don\'t know what to set, leave '
- . 'the default value (which is this blog\'s login page).', 'aad-sso-wordpress' )
- );
- }
-
- /**
- * Renders the `logout_redirect_uri` form control
- **/
- public function logout_redirect_uri_callback() {
- $this->render_text_field( 'logout_redirect_uri' );
- printf(
- ' %s'
- . '
%s
',
- wp_login_url(),
- __( 'Set default', 'aad-sso-wordpress'),
- __( 'The URL where the user is redirected to after signing out of Azure AD. '
- . 'This URL must be registered in Azure AD as a valid redirect URL. (This does not affect '
- . ' logging out of the blog, it is only used when logging out of Azure AD.)', 'aad-sso-wordpress' )
- );
- }
-
- /**
- * Renders the `field_to_match_to_upn` form control.
- */
- public function field_to_match_to_upn_callback() {
- $selected =
- isset( $this->settings['field_to_match_to_upn'] )
- ? $this->settings['field_to_match_to_upn']
- : '';
- ?>
-
- %s
',
- __( 'This specifies the WordPress user field which will be used to match to the Azure AD user\'s '
- . 'UserPrincipalName.', 'aad-sso-wordpress' )
- );
- }
-
- /**
- * Renders the `match_on_upn_alias` checkbox control.
- */
- public function match_on_upn_alias_callback() {
- $this->render_checkbox_field(
- 'match_on_upn_alias',
- __( 'Match WordPress users based on the alias of their Azure AD UserPrincipalName. For example, '
- . 'Azure AD username bob@example.com will match WordPress user bob.',
- 'aad-sso-wordpress' )
- );
- }
-
- /**
- * Renders the `default_wp_role` control.
- */
- public function default_wp_role_callback() {
-
- // Default configuration should be most-benign
- if( ! isset( $this->settings['default_wp_role'] ) ) {
- $this->settings['default_wp_role'] = '';
- }
-
- echo '';
- printf(
- '
%s
',
- __('This is the default role that users will be assigned to if matching Azure AD group to '
- . 'WordPress roles is enabled, but the signed in user isn\'t a member of any of the '
- . 'configured Azure AD groups.', 'aad-sso-wordpress')
- );
- }
-
- /**
- * Renders the `enable_auto_provisioning` checkbox control.
- */
- public function enable_auto_provisioning_callback() {
- $this->render_checkbox_field(
- 'enable_auto_provisioning',
- __( 'Automatically create WordPress users, if needed, for authenticated Azure AD users.',
- 'aad-sso-wordpress' )
- );
- }
-
- /**
- * Renders the `enable_auto_forward_to_aad` checkbox control.
- */
- public function enable_auto_forward_to_aad_callback() {
- $this->render_checkbox_field(
- 'enable_auto_forward_to_aad',
- __( 'Automatically forward users to the Azure AD to sign in, skipping the WordPress login screen.',
- 'aad-sso-wordpress')
- );
- }
-
- /**
- * Renders the `enable_aad_group_to_wp_role` checkbox control.
- */
- public function enable_aad_group_to_wp_role_callback() {
- $this->render_checkbox_field(
- 'enable_aad_group_to_wp_role',
- __( 'Automatically assign WordPress user roles based on Azure AD group membership.',
- 'aad-sso-wordpress' )
- );
- }
-
- /**
- * Renders the `openid_configuration_endpoint` form control
- **/
- public function openid_configuration_endpoint_callback() {
- $this->render_text_field( 'openid_configuration_endpoint' );
- printf(
- ' %s'
- . '
%s
',
- AADSSO_Settings::get_defaults( 'openid_configuration_endpoint' ),
- __( 'Set default', 'aad-sso-wordpress'),
- __( 'The OpenID Connect configuration endpoint to use. To support Microsoft Accounts and external '
- . 'users (users invited in from other Azure AD directories, known sometimes as "B2B users") you '
- . 'must use: https://login.microsoftonline.com/{tenant-id}/.well-known/openid-configuration, '
- . 'where {tenant-id} is the tenant ID or a verified domain name of your directory.',
- 'aad-sso-wordpress' )
- );
- }
-
- /**
- * Renders the `enable_full_logout` checkbox control.
- */
- public function enable_full_logout_callback() {
- $this->render_checkbox_field(
- 'enable_full_logout',
- __( 'Do a full logout of Azure AD when logging out of WordPress.',
- 'aad-sso-wordpress' )
- );
- }
-
- /**
- * Renders a simple text field and populates it with the setting value.
- *
- * @param string $name The setting name for the text input field.
- */
- public function render_text_field( $name ) {
- $value = isset( $this->settings[ $name ] ) ? esc_attr( $this->settings[ $name ] ) : '';
- printf(
- '',
- $name, $value
- );
- }
-
- /**
- * Renders a simple checkbox field and populates it with the setting value.
- *
- * @param string $name The setting name for the checkbox input field.
- * @param string $label The label to use for the checkbox.
- */
- public function render_checkbox_field( $name, $label ) {
- printf(
- ''
- . '',
- $name,
- isset( $this->settings[ $name ] ) && $this->settings[ $name ] ? 'checked' : '',
- $label
- );
- }
-
- /**
- * Indicates if user is currently on this settings page.
- */
- public function is_on_options_page() {
- $screen = get_current_screen();
- return $screen->id === $this->options_page_id;
- }
-
- /**
- * Ensures jQuery is loaded
- */
- public function maybe_include_jquery() {
- if ( $this->is_on_options_page() ) {
- wp_enqueue_script( 'jquery' );
- }
- }
-}
diff --git a/aad-sso-wordpress.php b/aad-sso-wordpress.php
index 201476a..0c6649d 100644
--- a/aad-sso-wordpress.php
+++ b/aad-sso-wordpress.php
@@ -1,725 +1,38 @@
settings = $settings;
-
- // Setup the admin settings page
- $this->setup_admin_settings();
-
- // Some debugging locations
- //add_action( 'admin_notices', array( $this, 'print_debug' ) );
- //add_action( 'login_footer', array( $this, 'print_debug' ) );
-
- // Add a link to the Settings page in the list of plugins
- add_filter(
- 'plugin_action_links_' . plugin_basename( __FILE__ ),
- array( $this, 'add_settings_link' )
- );
-
- // Register activation and deactivation hooks
- register_activation_hook( __FILE__, array( 'AADSSO', 'activate' ) );
- register_deactivation_hook( __FILE__, array( 'AADSSO', 'deactivate' ) );
-
- // If plugin is not configured, we shouldn't proceed.
- if ( ! $this->plugin_is_configured() ) {
- add_action( 'all_admin_notices', array( $this, 'print_plugin_not_configured' ) );
- return;
- }
-
- // Add the hook that starts the SESSION
- add_action( 'login_init', array( $this, 'register_session' ), 10 );
-
- // The authenticate filter
- add_filter( 'authenticate', array( $this, 'authenticate' ), 1, 3 );
-
- // Add the