├── languages ├── wp-saml-auth-fr_FR.mo ├── wp-saml-auth.pot └── wp-saml-auth-fr_FR.po ├── catalog-info.yml ├── inc ├── class-wp-saml-auth-cli.php ├── class-wp-saml-auth-options.php ├── class-wp-saml-auth-settings.php └── class-wp-saml-auth.php ├── wp-saml-auth.php └── readme.txt /languages/wp-saml-auth-fr_FR.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pantheon-systems/wp-saml-auth/HEAD/languages/wp-saml-auth-fr_FR.mo -------------------------------------------------------------------------------- /catalog-info.yml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: backstage.io/v1alpha1 3 | kind: Component 4 | metadata: 5 | name: wp-saml-auth 6 | description: Auto-generated catalog info for pantheon-systems/wp-saml-auth 7 | annotations: 8 | backstage.io/techdocs-ref: dir:docs/ 9 | spec: 10 | type: library 11 | lifecycle: mature 12 | owner: site 13 | -------------------------------------------------------------------------------- /inc/class-wp-saml-auth-cli.php: -------------------------------------------------------------------------------- 1 | ] 19 | * : Path to the SimpleSAMLphp autoloader. Defaults to a subdirectory of 20 | * the plugin's directory. 21 | * 22 | * [--auth_source=] 23 | * : Authentication source to pass to SimpleSAMLphp. This must be one of 24 | * your configured identity providers in SimpleSAMLphp. 25 | * --- 26 | * default: default-sp 27 | * --- 28 | * 29 | * [--auto_provision=] 30 | * : Whether or not to automatically provision new WordPress users. 31 | * 32 | * [--permit_wp_login=] 33 | * : Whether or not to permit logging in with username and password. 34 | * 35 | * [--get_user_by=] 36 | * : Attribute by which to get a WordPress user for a SAML user. 37 | * --- 38 | * default: email 39 | * options: 40 | * - email 41 | * - login 42 | * --- 43 | * 44 | * [--user_login_attribute=] 45 | * : SAML attribute which includes the user_login value for a user. 46 | * --- 47 | * default: uid 48 | * --- 49 | * 50 | * [--user_email_attribute=] 51 | * : SAML attribute which includes the user_email value for a user. 52 | * --- 53 | * default: email 54 | * --- 55 | * 56 | * [--display_name_attribute=] 57 | * : SAML attribute which includes the display_name value for a user. 58 | * --- 59 | * default: display_name 60 | * --- 61 | * 62 | * [--first_name_attribute=] 63 | * : SAML attribute which includes the first_name value for a user. 64 | * --- 65 | * default: first_name 66 | * --- 67 | * 68 | * [--last_name_attribute=] 69 | * : SAML attribute which includes the last_name value for a user. 70 | * --- 71 | * default: last_name 72 | * --- 73 | * 74 | * [--default_role=] 75 | * : Default WordPress role to grant when provisioning new users. 76 | * 77 | * @subcommand scaffold-config 78 | */ 79 | public function scaffold_config( $args, $assoc_args ) { 80 | 81 | $function = self::scaffold_config_function( $assoc_args ); 82 | WP_CLI::log( $function ); 83 | } 84 | 85 | /** 86 | * Generate a string representation of a function to be used for configuring the plugin. 87 | * 88 | * @param array $assoc_args Associative arguments passed to the command. 89 | * @return string 90 | */ 91 | protected static function scaffold_config_function( $assoc_args ) { 92 | $defaults = [ 93 | 'type' => 'internal', 94 | 'simplesamlphp_autoload' => __DIR__ . '/simplesamlphp/lib/_autoload.php', 95 | 'auth_source' => 'default-sp', 96 | 'auto_provision' => true, 97 | 'permit_wp_login' => true, 98 | 'get_user_by' => 'email', 99 | 'user_login_attribute' => 'uid', 100 | 'user_email_attribute' => 'mail', 101 | 'display_name_attribute' => 'display_name', 102 | 'first_name_attribute' => 'first_name', 103 | 'last_name_attribute' => 'last_name', 104 | 'default_role' => get_option( 'default_role' ), 105 | ]; 106 | $assoc_args = array_merge( $defaults, $assoc_args ); 107 | 108 | foreach ( [ 'auto_provision', 'permit_wp_login' ] as $bool ) { 109 | // Support --auto_provision=false passed as an argument. 110 | $assoc_args[ $bool ] = 'false' === $assoc_args[ $bool ] ? false : (bool) $assoc_args[ $bool ]; 111 | } 112 | 113 | $values = var_export( $assoc_args, true ); //phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_var_export 114 | // Formatting fixes. 115 | $search_replace = [ 116 | ' ' => "\t\t", 117 | 'array (' => 'array(', 118 | ]; 119 | $values = str_replace( array_keys( $search_replace ), array_values( $search_replace ), $values ); 120 | $values = rtrim( $values, ')' ) . "\t);"; 121 | $function = << 'internal', 122 | 'internal_config' => [ 123 | 'strict' => true, 124 | 'debug' => defined( 'WP_DEBUG' ) && WP_DEBUG ? true : false, 125 | 'baseurl' => $options['baseurl'], 126 | 'sp' => [ 127 | 'entityId' => $options['sp_entityId'], 128 | 'assertionConsumerService' => [ 129 | 'url' => $options['sp_assertionConsumerService_url'], 130 | 'binding' => 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST', 131 | ], 132 | ], 133 | 'idp' => [ 134 | 'entityId' => $options['idp_entityId'], 135 | 'singleSignOnService' => [ 136 | 'url' => $options['idp_singleSignOnService_url'], 137 | 'binding' => 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect', 138 | ], 139 | 'singleLogoutService' => [ 140 | 'url' => $options['idp_singleLogoutService_url'], 141 | 'binding' => 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect', 142 | ], 143 | 'x509cert' => $x509cert, 144 | 'certFingerprint' => $options['certFingerprint'], 145 | 'certFingerprintAlgorithm' => $options['certFingerprintAlgorithm'], 146 | ], 147 | ], 148 | ]; 149 | 150 | $remaining_settings = [ 151 | 'auto_provision', 152 | 'permit_wp_login', 153 | 'get_user_by', 154 | 'user_login_attribute', 155 | 'user_email_attribute', 156 | 'display_name_attribute', 157 | 'first_name_attribute', 158 | 'last_name_attribute', 159 | ]; 160 | foreach ( $remaining_settings as $setting ) { 161 | $settings[ $setting ] = $options[ $setting ]; 162 | } 163 | $value = isset( $settings[ $option_name ] ) ? $settings[ $option_name ] : $value; 164 | return $value; 165 | } 166 | } 167 | -------------------------------------------------------------------------------- /wp-saml-auth.php: -------------------------------------------------------------------------------- 1 | 'simplesamlphp', 62 | /** 63 | * Path to SimpleSAMLphp autoloader. 64 | * 65 | * Follow the standard implementation by installing SimpleSAMLphp 66 | * alongside the plugin, and provide the path to its autoloader. 67 | * Alternatively, this plugin will work if it can find the 68 | * `SimpleSAML_Auth_Simple` class. 69 | * 70 | * @param string 71 | */ 72 | 'simplesamlphp_autoload' => class_exists( 'WP_SAML_Auth' ) ? WP_SAML_Auth::get_simplesamlphp_autoloader() : '', 73 | /** 74 | * Authentication source to pass to SimpleSAMLphp 75 | * 76 | * This must be one of your configured identity providers in 77 | * SimpleSAMLphp. If the identity provider isn't configured 78 | * properly, the plugin will not work properly. 79 | * 80 | * @param string 81 | */ 82 | 'auth_source' => 'default-sp', 83 | /** 84 | * Configuration options for OneLogin library use. 85 | * 86 | * See comments with "Required:" for values you absolutely need to configure. 87 | * 88 | * @param array 89 | */ 90 | 'internal_config' => [ 91 | // Validation of SAML responses is required. 92 | 'strict' => true, 93 | 'debug' => defined( 'WP_DEBUG' ) && WP_DEBUG ? true : false, 94 | 'baseurl' => home_url(), 95 | 'sp' => [ 96 | 'entityId' => 'urn:' . parse_url( home_url(), PHP_URL_HOST ), 97 | 'assertionConsumerService' => [ 98 | 'url' => home_url(), 99 | 'binding' => 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST', 100 | ], 101 | ], 102 | 'idp' => [ 103 | // Required: Set based on provider's supplied value. 104 | 'entityId' => '', 105 | 'singleSignOnService' => [ 106 | // Required: Set based on provider's supplied value. 107 | 'url' => '', 108 | 'binding' => 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect', 109 | ], 110 | 'singleLogoutService' => [ 111 | // Required: Set based on provider's supplied value. 112 | 'url' => '', 113 | 'binding' => 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect', 114 | ], 115 | // Required: Contents of the IDP's public x509 certificate. 116 | // Use file_get_contents() to load certificate contents into scope. 117 | 'x509cert' => '', 118 | // Optional: Instead of using the x509 cert, you can specify the fingerprint and algorithm. 119 | 'certFingerprint' => '', 120 | 'certFingerprintAlgorithm' => '', 121 | ], 122 | ], 123 | /** 124 | * Whether or not to automatically provision new WordPress users. 125 | * 126 | * When WordPress is presented with a SAML user without a 127 | * corresponding WordPress account, it can either create a new user 128 | * or display an error that the user needs to contact the site 129 | * administrator. 130 | * 131 | * @param bool 132 | */ 133 | 'auto_provision' => true, 134 | /** 135 | * Whether or not to permit logging in with username and password. 136 | * 137 | * If this feature is disabled, all authentication requests will be 138 | * channeled through SimpleSAMLphp. 139 | * 140 | * @param bool 141 | */ 142 | 'permit_wp_login' => true, 143 | /** 144 | * Attribute by which to get a WordPress user for a SAML user. 145 | * 146 | * @param string Supported options are 'email' and 'login'. 147 | */ 148 | 'get_user_by' => 'email', 149 | /** 150 | * SAML attribute which includes the user_login value for a user. 151 | * 152 | * @param string 153 | */ 154 | 'user_login_attribute' => 'uid', 155 | /** 156 | * SAML attribute which includes the user_email value for a user. 157 | * 158 | * @param string 159 | */ 160 | 'user_email_attribute' => 'mail', 161 | /** 162 | * SAML attribute which includes the display_name value for a user. 163 | * 164 | * @param string 165 | */ 166 | 'display_name_attribute' => 'display_name', 167 | /** 168 | * SAML attribute which includes the first_name value for a user. 169 | * 170 | * @param string 171 | */ 172 | 'first_name_attribute' => 'first_name', 173 | /** 174 | * SAML attribute which includes the last_name value for a user. 175 | * 176 | * @param string 177 | */ 178 | 'last_name_attribute' => 'last_name', 179 | /** 180 | * Default WordPress role to grant when provisioning new users. 181 | * 182 | * @param string 183 | */ 184 | 'default_role' => get_option( 'default_role' ), 185 | /** 186 | * Minimum recommended version of SimpleSAMLphp. 187 | * Versions below this will show a warning but still work. 188 | * 189 | * @param string 190 | */ 191 | 'min_simplesamlphp_version' => '2.3.7', 192 | /** 193 | * Critical security version of SimpleSAMLphp. 194 | * Versions below this will show an error and block authentication if `enforce_min_simplesamlphp_version` is true. 195 | * 196 | * @param string 197 | */ 198 | 'critical_simplesamlphp_version' => '2.0.0', 199 | /** 200 | * Whether to enforce the minimum SimpleSAMLphp version requirement. 201 | * If true, authentication will be blocked for versions below critical_simplesamlphp_version. Defaults to false. 202 | * 203 | * @param bool 204 | */ 205 | 'enforce_min_simplesamlphp_version' => false, 206 | ]; 207 | $value = isset( $defaults[ $option_name ] ) ? $defaults[ $option_name ] : $value; 208 | return $value; 209 | } 210 | 211 | // Bootstrap the plugin. 212 | wpsa_boostrap(); 213 | -------------------------------------------------------------------------------- /languages/wp-saml-auth.pot: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2021 Pantheon 2 | # This file is distributed under the same license as the WP SAML Auth plugin. 3 | msgid "" 4 | msgstr "" 5 | "Project-Id-Version: WP SAML Auth 1.2.7\n" 6 | "Report-Msgid-Bugs-To: https://wordpress.org/support/plugin/wp-saml-auth\n" 7 | "Last-Translator: FULL NAME \n" 8 | "Language-Team: LANGUAGE \n" 9 | "MIME-Version: 1.0\n" 10 | "Content-Type: text/plain; charset=UTF-8\n" 11 | "Content-Transfer-Encoding: 8bit\n" 12 | "POT-Creation-Date: 2021-12-09T16:26:03+00:00\n" 13 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" 14 | "X-Generator: WP-CLI 2.5.0\n" 15 | "X-Domain: wp-saml-auth\n" 16 | 17 | #. Plugin Name of the plugin 18 | #: inc/class-wp-saml-auth-settings.php:95 19 | msgid "WP SAML Auth" 20 | msgstr "" 21 | 22 | #. Plugin URI of the plugin 23 | msgid "https://wordpress.org/plugins/wp-saml-auth/" 24 | msgstr "" 25 | 26 | #. Description of the plugin 27 | msgid "SAML authentication for WordPress, using SimpleSAMLphp." 28 | msgstr "" 29 | 30 | #. Author of the plugin 31 | msgid "Pantheon" 32 | msgstr "" 33 | 34 | #. Author URI of the plugin 35 | msgid "https://pantheon.io" 36 | msgstr "" 37 | 38 | #: inc/class-wp-saml-auth-settings.php:94 39 | #: inc/class-wp-saml-auth-settings.php:145 40 | msgid "WP SAML Auth Settings" 41 | msgstr "" 42 | 43 | #. translators: Link to the plugin settings page. 44 | #: inc/class-wp-saml-auth-settings.php:150 45 | msgid "Settings are defined with a filter and unavailable for editing through the backend. Visit the plugin page for more information." 46 | msgstr "" 47 | 48 | #. translators: Link to the plugin settings page. 49 | #: inc/class-wp-saml-auth-settings.php:157 50 | msgid "Use the following settings to configure WP SAML Auth with the 'internal' connection type. Visit the plugin page for more information." 51 | msgstr "" 52 | 53 | #: inc/class-wp-saml-auth-settings.php:161 54 | msgid "Settings are actively applied to WP SAML Auth configuration." 55 | msgstr "" 56 | 57 | #: inc/class-wp-saml-auth-settings.php:163 58 | msgid "Some required settings don't have values, so WP SAML Auth isn't active." 59 | msgstr "" 60 | 61 | #: inc/class-wp-saml-auth-settings.php:184 62 | msgid "Settings" 63 | msgstr "" 64 | 65 | #. translators: Field label. 66 | #: inc/class-wp-saml-auth-settings.php:218 67 | msgid "%s is a required field" 68 | msgstr "" 69 | 70 | #. translators: Field label. 71 | #: inc/class-wp-saml-auth-settings.php:242 72 | msgid "%s is not a valid URL." 73 | msgstr "" 74 | 75 | #. translators: Field label. 76 | #: inc/class-wp-saml-auth-settings.php:256 77 | msgid "%s is not a valid certificate path." 78 | msgstr "" 79 | 80 | #: inc/class-wp-saml-auth-settings.php:295 81 | msgid "Service Provider Settings" 82 | msgstr "" 83 | 84 | #: inc/class-wp-saml-auth-settings.php:296 85 | msgid "Identity Provider Settings" 86 | msgstr "" 87 | 88 | #: inc/class-wp-saml-auth-settings.php:297 89 | msgid "Attribute Mappings" 90 | msgstr "" 91 | 92 | #: inc/class-wp-saml-auth-settings.php:313 93 | msgid "Auto Provision" 94 | msgstr "" 95 | 96 | #: inc/class-wp-saml-auth-settings.php:315 97 | msgid "If checked, create a new WordPress user upon login.
If unchecked, WordPress user will already need to exist in order to log in." 98 | msgstr "" 99 | 100 | #: inc/class-wp-saml-auth-settings.php:321 101 | msgid "Permit WordPress login" 102 | msgstr "" 103 | 104 | #: inc/class-wp-saml-auth-settings.php:323 105 | msgid "If checked, WordPress user can also log in with the standard username and password flow." 106 | msgstr "" 107 | 108 | #: inc/class-wp-saml-auth-settings.php:329 109 | msgid "Get User By" 110 | msgstr "" 111 | 112 | #: inc/class-wp-saml-auth-settings.php:335 113 | msgid "Attribute by which SAML requests are matched to WordPress users." 114 | msgstr "" 115 | 116 | #: inc/class-wp-saml-auth-settings.php:341 117 | msgid "Base URL" 118 | msgstr "" 119 | 120 | #: inc/class-wp-saml-auth-settings.php:343 121 | msgid "The base url to be used when constructing URLs." 122 | msgstr "" 123 | 124 | #: inc/class-wp-saml-auth-settings.php:350 125 | #: inc/class-wp-saml-auth-settings.php:370 126 | msgid "Entity Id (Required)" 127 | msgstr "" 128 | 129 | #: inc/class-wp-saml-auth-settings.php:353 130 | msgid "SP (WordPress) entity identifier." 131 | msgstr "" 132 | 133 | #: inc/class-wp-saml-auth-settings.php:360 134 | msgid "Assertion Consumer Service URL (Required)" 135 | msgstr "" 136 | 137 | #: inc/class-wp-saml-auth-settings.php:362 138 | msgid "URL where the response from the IdP should be returned (usually the login URL)." 139 | msgstr "" 140 | 141 | #: inc/class-wp-saml-auth-settings.php:372 142 | msgid "IdP entity identifier." 143 | msgstr "" 144 | 145 | #: inc/class-wp-saml-auth-settings.php:378 146 | msgid "Single SignOn Service URL (Required)" 147 | msgstr "" 148 | 149 | #: inc/class-wp-saml-auth-settings.php:380 150 | msgid "URL of the IdP where the SP (WordPress) will send the authentication request." 151 | msgstr "" 152 | 153 | #: inc/class-wp-saml-auth-settings.php:386 154 | msgid "Single Logout Service URL" 155 | msgstr "" 156 | 157 | #: inc/class-wp-saml-auth-settings.php:388 158 | msgid "URL of the IdP where the SP (WordPress) will send the signout request." 159 | msgstr "" 160 | 161 | #: inc/class-wp-saml-auth-settings.php:393 162 | msgid "x509 Cerificate Path" 163 | msgstr "" 164 | 165 | #: inc/class-wp-saml-auth-settings.php:395 166 | msgid "Path to the x509 certificate file, used for verifying the request.
Include ABSPATH to set path base to WordPress' ABSPATH constant." 167 | msgstr "" 168 | 169 | #: inc/class-wp-saml-auth-settings.php:400 170 | msgid "Certificate Fingerprint" 171 | msgstr "" 172 | 173 | #: inc/class-wp-saml-auth-settings.php:402 174 | msgid "If not using x509 certificate, paste the certificate fingerprint and specify the fingerprint algorithm below." 175 | msgstr "" 176 | 177 | #: inc/class-wp-saml-auth-settings.php:407 178 | msgid "Certificate Fingerprint Algorithm" 179 | msgstr "" 180 | 181 | #: inc/class-wp-saml-auth-settings.php:410 182 | msgid "N/A" 183 | msgstr "" 184 | 185 | #: inc/class-wp-saml-auth.php:151 186 | msgid "Use one-click authentication:" 187 | msgstr "" 188 | 189 | #: inc/class-wp-saml-auth.php:152 190 | msgid "Sign In" 191 | msgstr "" 192 | 193 | #: inc/class-wp-saml-auth.php:153 194 | msgid "Or, sign in with WordPress:" 195 | msgstr "" 196 | 197 | #. Translators: Includes error reason from OneLogin. 198 | #: inc/class-wp-saml-auth.php:260 199 | msgid "User is not authenticated with SAML IdP. Reason: %s" 200 | msgstr "" 201 | 202 | #: inc/class-wp-saml-auth.php:327 203 | msgid "Invalid provider specified for SAML authentication" 204 | msgstr "" 205 | 206 | #: inc/class-wp-saml-auth.php:352 207 | msgid "No attributes were present in SAML response. Attributes are used to create and fetch users. Please contact your administrator" 208 | msgstr "" 209 | 210 | #. Translators: Communicates how the user is fetched based on the SAML response. 211 | #: inc/class-wp-saml-auth.php:359 212 | msgid "\"%1$s\" attribute is expected, but missing, in SAML response. Attribute is used to fetch existing user by \"%2$s\". Please contact your administrator." 213 | msgstr "" 214 | 215 | #: inc/class-wp-saml-auth.php:374 216 | msgid "No WordPress user exists for your account. Please contact your administrator." 217 | msgstr "" 218 | 219 | #. Translators: Links to the WP SAML Auth plugin. 220 | #: inc/class-wp-saml-auth.php:426 221 | msgid "WP SAML Auth wasn't able to find the OneLogin\\Saml2\\Auth class. Please verify your Composer autoloader, or visit the plugin page for more information." 222 | msgstr "" 223 | 224 | #: inc/class-wp-saml-auth.php:437 225 | msgid "WP SAML Auth wasn't able to find the %1$s class. Please check the simplesamlphp_autoload configuration option, or visit the plugin page for more information." 226 | msgstr "" 227 | -------------------------------------------------------------------------------- /languages/wp-saml-auth-fr_FR.po: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2018 Pantheon 2 | # This file is distributed under the same license as the WP SAML Auth package. 3 | msgid "" 4 | msgstr "" 5 | "Project-Id-Version: WP SAML Auth 0.3.9\n" 6 | "Report-Msgid-Bugs-To: https://wordpress.org/support/plugin/wp-saml-auth\n" 7 | "POT-Creation-Date: 2020-12-01T13:07:52+00:00\n" 8 | "PO-Revision-Date: 2021-02-03 15:33+0100\n" 9 | "Last-Translator: \n" 10 | "Language-Team: \n" 11 | "Language: fr_FR\n" 12 | "MIME-Version: 1.0\n" 13 | "Content-Type: text/plain; charset=UTF-8\n" 14 | "Content-Transfer-Encoding: 8bit\n" 15 | "X-Generator: Poedit 1.5.7\n" 16 | "Plural-Forms: nplurals=2; plural=(n > 1);\n" 17 | "X-Poedit-SourceCharset: UTF-8\n" 18 | 19 | #. Plugin Name of the plugin 20 | #: inc/class-wp-saml-auth-settings.php:95 21 | msgid "WP SAML Auth" 22 | msgstr "WP SAML Auth" 23 | 24 | #. Plugin URI of the plugin 25 | msgid "https://wordpress.org/plugins/wp-saml-auth/" 26 | msgstr "https://fr.wordpress.org/plugins/wp-saml-auth/" 27 | 28 | #. Description of the plugin 29 | msgid "SAML authentication for WordPress, using SimpleSAMLphp." 30 | msgstr "Authentification SAML pour WordPress utilisant SimpleSAMLphp." 31 | 32 | #. Author of the plugin 33 | msgid "Pantheon" 34 | msgstr "Pantheon" 35 | 36 | #. Author URI of the plugin 37 | msgid "https://pantheon.io" 38 | msgstr "https://pantheon.io" 39 | 40 | #: inc/class-wp-saml-auth-settings.php:94 41 | #: inc/class-wp-saml-auth-settings.php:145 42 | msgid "WP SAML Auth Settings" 43 | msgstr "Réglages de WP SAML Auth" 44 | 45 | #. translators: Link to the plugin settings page. 46 | #: inc/class-wp-saml-auth-settings.php:150 47 | msgid "" 48 | "Settings are defined with a filter and unavailable for editing through the " 49 | "backend. Visit the plugin page for more information." 50 | msgstr "" 51 | "Les réglages sont définis à l’aide d’un filtre et ne peuvent pas être " 52 | "modifiés depuis l’administration. Consulter la page de " 53 | "l’extension pour plus d’information." 54 | 55 | #. translators: Link to the plugin settings page. 56 | #: inc/class-wp-saml-auth-settings.php:157 57 | msgid "" 58 | "Use the following settings to configure WP SAML Auth with the 'internal' " 59 | "connection type. Visit the plugin page for more " 60 | "information." 61 | msgstr "" 62 | "Utiliser les réglages suivants pour configurer WP SAML Auth selon le type de " 63 | "connexion « interne ». Consulter la page de l’extension " 64 | "pour plus d’information." 65 | 66 | #: inc/class-wp-saml-auth-settings.php:161 67 | msgid "Settings are actively applied to WP SAML Auth configuration." 68 | msgstr "" 69 | "Les réglages sont effectivement appliqués à la configuration de WP SAML Auth." 70 | 71 | #: inc/class-wp-saml-auth-settings.php:163 72 | msgid "Some required settings don't have values, so WP SAML Auth isn't active." 73 | msgstr "" 74 | "Certains réglages obligatoires sont non renseignés, alors WP SAML Auth n’est " 75 | "pas actif." 76 | 77 | #: inc/class-wp-saml-auth-settings.php:184 78 | msgid "Settings" 79 | msgstr "Réglages" 80 | 81 | #. translators: Field label. 82 | #: inc/class-wp-saml-auth-settings.php:215 83 | msgid "%s is a required field" 84 | msgstr "%s est un champ obligatoire" 85 | 86 | #. translators: Field label. 87 | #: inc/class-wp-saml-auth-settings.php:239 88 | msgid "%s is not a valid URL." 89 | msgstr "%s n’est pas une URL valide." 90 | 91 | #. translators: Field label. 92 | #: inc/class-wp-saml-auth-settings.php:253 93 | msgid "%s is not a valid certificate path." 94 | msgstr "%s n’est pas un chemin valide pour le certificat." 95 | 96 | #: inc/class-wp-saml-auth-settings.php:293 97 | msgid "Service Provider Settings" 98 | msgstr "Réglages du fournisseur de service" 99 | 100 | #: inc/class-wp-saml-auth-settings.php:294 101 | msgid "Identity Provider Settings" 102 | msgstr "Réglages de fournisseur d’identité" 103 | 104 | #: inc/class-wp-saml-auth-settings.php:295 105 | msgid "Attribute Mappings" 106 | msgstr "Correspondance des attributs" 107 | 108 | #: inc/class-wp-saml-auth-settings.php:311 109 | msgid "Auto Provision" 110 | msgstr "Provision automatique" 111 | 112 | #: inc/class-wp-saml-auth-settings.php:313 113 | msgid "" 114 | "If checked, create a new WordPress user upon login.
If unchecked, " 115 | "WordPress user will already need to exist in order to log in." 116 | msgstr "" 117 | "Si coché, tout nouvel utilisateur WordPress se connectant sera créé suite à " 118 | "son authentification.
Si non coché, l’utilisateur WordPress devra " 119 | "exister pour pouvoir s’authentifier." 120 | 121 | #: inc/class-wp-saml-auth-settings.php:319 122 | msgid "Permit WordPress login" 123 | msgstr "Autoriser la connexion via WordPress" 124 | 125 | #: inc/class-wp-saml-auth-settings.php:321 126 | msgid "" 127 | "If checked, WordPress user can also log in with the standard username and " 128 | "password flow." 129 | msgstr "" 130 | "Si coché, l’utilisateur WordPress pourra également s’authentifier à l’aide " 131 | "du processus standard basé sur un nom d’utilisateur et un mot de passe." 132 | 133 | #: inc/class-wp-saml-auth-settings.php:327 134 | msgid "Get User By" 135 | msgstr "Définir l’utilisateur selon" 136 | 137 | #: inc/class-wp-saml-auth-settings.php:333 138 | msgid "Attribute by which SAML requests are matched to WordPress users." 139 | msgstr "" 140 | "Attribut que les requêtes SAML doivent utiliser pour déterminer les " 141 | "utilisateurs WordPress." 142 | 143 | #: inc/class-wp-saml-auth-settings.php:339 144 | msgid "Base URL" 145 | msgstr "URL de base" 146 | 147 | #: inc/class-wp-saml-auth-settings.php:341 148 | msgid "The base url to be used when constructing URLs." 149 | msgstr "L’URL de base à utiliser pour construire les URLs." 150 | 151 | #: inc/class-wp-saml-auth-settings.php:348 152 | #: inc/class-wp-saml-auth-settings.php:368 153 | msgid "Entity Id (Required)" 154 | msgstr "Identifiant d’entité (obligatoire)" 155 | 156 | #: inc/class-wp-saml-auth-settings.php:351 157 | msgid "SP (WordPress) entity identifier." 158 | msgstr "Identifiant d’entité du fournisseur de service SP (WordPress)" 159 | 160 | #: inc/class-wp-saml-auth-settings.php:358 161 | msgid "Assertion Consumer Service URL (Required)" 162 | msgstr "URL du service consommateur des assertions (obligatoire)" 163 | 164 | #: inc/class-wp-saml-auth-settings.php:360 165 | msgid "" 166 | "URL where the response from the IdP should be returned (usually the login " 167 | "URL)." 168 | msgstr "" 169 | "URL à utiliser pour la réception des réponses du fournisseur d’identité IdP " 170 | "(habituellement l’URL de connexion). " 171 | 172 | #: inc/class-wp-saml-auth-settings.php:370 173 | msgid "IdP entity identifier." 174 | msgstr "Identifiant du fournisseur d’identité (IdP)" 175 | 176 | #: inc/class-wp-saml-auth-settings.php:376 177 | msgid "Single SignOn Service URL (Required)" 178 | msgstr "URL du service d’authentification unique (obligatoire)" 179 | 180 | #: inc/class-wp-saml-auth-settings.php:378 181 | msgid "" 182 | "URL of the IdP where the SP (WordPress) will send the authentication request." 183 | msgstr "" 184 | "URL du fournisseur d’identité - IdP auprès de laquelle le fournisseur de " 185 | "service - SP (WordPress) doit envoyer la requête d’authentification" 186 | 187 | #: inc/class-wp-saml-auth-settings.php:384 188 | msgid "Single Logout Service URL" 189 | msgstr "URL du service de déconnexion unique" 190 | 191 | #: inc/class-wp-saml-auth-settings.php:386 192 | msgid "URL of the IdP where the SP (WordPress) will send the signout request." 193 | msgstr "" 194 | "URL du fournisseur d’identité - IdP auprès de laquelle le fournisseur de " 195 | "service - SP (WordPress) doit envoyer la requête de déconnexion." 196 | 197 | #: inc/class-wp-saml-auth-settings.php:391 198 | msgid "x509 Cerificate Path" 199 | msgstr "Chemin du certificat x509" 200 | 201 | #: inc/class-wp-saml-auth-settings.php:393 202 | msgid "" 203 | "Path to the x509 certificate file, used for verifying the request.
Include ABSPATH to set path base to WordPress' ABSPATH " 205 | "constant." 206 | msgstr "" 207 | "Chemin pour accéder au fichier contenant le certificat x509, lequel est " 208 | "utilisé pour vérifier la requête.
Utilisez ABSPATH pour " 209 | "baser votre chemin sur la constante ABSPATH de WordPress." 210 | 211 | #: inc/class-wp-saml-auth-settings.php:398 212 | msgid "Certificate Fingerprint" 213 | msgstr "Emprunte du certificat" 214 | 215 | #: inc/class-wp-saml-auth-settings.php:400 216 | msgid "" 217 | "If not using x509 certificate, paste the certificate fingerprint and specify " 218 | "the fingerprint algorithm below." 219 | msgstr "" 220 | "Si vous n’utilisez pas un certificat x509, copier l’empreinte du certificat " 221 | "en spécifiant son algorythme ci-dessous." 222 | 223 | #: inc/class-wp-saml-auth-settings.php:405 224 | msgid "Certificate Fingerprint Algorithm" 225 | msgstr "Algorythme de l’empreinte du certificat" 226 | 227 | #: inc/class-wp-saml-auth-settings.php:408 228 | msgid "N/A" 229 | msgstr "N/A" 230 | 231 | #: inc/class-wp-saml-auth.php:147 232 | msgid "Use one-click authentication:" 233 | msgstr "Utilisez l’authentification unique:" 234 | 235 | #: inc/class-wp-saml-auth.php:148 236 | msgid "Sign In" 237 | msgstr "Connectez-vous" 238 | 239 | #: inc/class-wp-saml-auth.php:149 240 | msgid "Or, sign in with WordPress:" 241 | msgstr "Ou connectez-vous à votre compte WordPress:" 242 | 243 | #. Translators: Includes error reason from OneLogin. 244 | #: inc/class-wp-saml-auth.php:234 245 | msgid "User is not authenticated with SAML IdP. Reason: %s" 246 | msgstr "" 247 | "L’utilisateur n’est pas authentifié par le fournisseur d’identité (IdP) SAML " 248 | "en raison de : %s" 249 | 250 | #: inc/class-wp-saml-auth.php:287 251 | msgid "Invalid provider specified for SAML authentication" 252 | msgstr "Le fournisseur renseigné pour l’authentification SAML est invalide" 253 | 254 | #: inc/class-wp-saml-auth.php:312 255 | msgid "" 256 | "No attributes were present in SAML response. Attributes are used to create " 257 | "and fetch users. Please contact your administrator" 258 | msgstr "" 259 | "Aucun attribut n’était présent dans la réponse SAML. Les attributs sont " 260 | "utilisés pour créer et rappatrier les utilisateurs. Merci de contacter votre " 261 | "administrateur" 262 | 263 | #. Translators: Communicates how the user is fetched based on the SAML response. 264 | #: inc/class-wp-saml-auth.php:319 265 | msgid "" 266 | "\"%1$s\" attribute is expected, but missing, in SAML response. Attribute is " 267 | "used to fetch existing user by \"%2$s\". Please contact your administrator." 268 | msgstr "" 269 | "L’attribut « %1$s » est requis mais il est absent dans la réponse SAML. " 270 | "L’attribut est utilisé pour trouver l’utilisateur existant par « %2$s ». " 271 | "Merci de contacter l’administrateur du site." 272 | 273 | #: inc/class-wp-saml-auth.php:334 274 | msgid "" 275 | "No WordPress user exists for your account. Please contact your administrator." 276 | msgstr "" 277 | "Utilisateur WordPress introuvable pour votre compte. Merci de contacter " 278 | "l’administrateur du site." 279 | 280 | #. Translators: Links to the WP SAML Auth plugin. 281 | #: inc/class-wp-saml-auth.php:386 282 | msgid "" 283 | "WP SAML Auth wasn't able to find the OneLogin\\Saml2\\Auth " 284 | "class. Please verify your Composer autoloader, or visit the " 285 | "plugin page for more information." 286 | msgstr "" 287 | "WP SAML n’a pas été en mesure de trouver la classe OneLogin" 288 | "\\Saml2\\Auth. Merci de vérifier le chargeur automatique (autoloader) " 289 | "de votre Composer, ou de consulter la page de l’extension " 290 | "pour plus d’information." 291 | 292 | #: inc/class-wp-saml-auth.php:397 293 | msgid "" 294 | "WP SAML Auth wasn't able to find the %1$s class. Please check " 295 | "the simplesamlphp_autoload configuration option, or visit the plugin page for more information." 297 | msgstr "" 298 | "Le plugin WP SAML Auth n’a pas trouvé la classe %1$s. Merci de vérifier l’option de configuration " 300 | "simplesamlphp_autoload, ou allez sur la page du " 301 | "plugin pour plus d’informations." 302 | 303 | #~ msgid "" 304 | #~ "WP SAML Auth wasn't able to find the OneLogin_Saml2_Auth " 305 | #~ "class. Please verify your Composer autoloader, or visit the " 306 | #~ "plugin page for more information." 307 | #~ msgstr "" 308 | #~ "Le plugin WP SAML Auth n’a pas trouvé la class " 309 | #~ "OneLogin_Saml2_Auth. Merci de vérifier l’autoloader dans votre configuration du Composer, ou allez sur la page du plugin pour plus d’informations." 312 | -------------------------------------------------------------------------------- /inc/class-wp-saml-auth-settings.php: -------------------------------------------------------------------------------- 1 | [ self::$instance, 'sanitize_callback' ] ] 84 | ); 85 | self::setup_sections(); 86 | self::setup_fields(); 87 | } 88 | 89 | /** 90 | * Add sub menu page to the Settings menu 91 | */ 92 | public static function admin_menu() { 93 | add_options_page( 94 | __( 'WP SAML Auth Settings', 'wp-saml-auth' ), 95 | __( 'WP SAML Auth', 'wp-saml-auth' ), 96 | self::$capability, 97 | self::$menu_slug, 98 | [ self::$instance, 'render_page_content' ] 99 | ); 100 | } 101 | 102 | /** 103 | * Add each field to the HTML form 104 | * 105 | * @param array $arguments field data passed from add_settings_field(). 106 | */ 107 | public static function field_callback( $arguments ) { 108 | $uid = WP_SAML_Auth_Options::get_option_name() . '[' . $arguments['uid'] . ']'; 109 | $value = $arguments['value']; 110 | switch ( $arguments['type'] ) { 111 | case 'checkbox': 112 | printf( '', esc_attr( $uid ), checked( $value, true, false ) ); 113 | break; 114 | case 'select': 115 | if ( ! empty( $arguments['choices'] ) && is_array( $arguments['choices'] ) ) { 116 | $markup = ''; 117 | foreach ( $arguments['choices'] as $key => $label ) { 118 | $markup .= ''; 120 | } 121 | printf( '', esc_attr( $uid ), $markup ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped 122 | } 123 | break; 124 | case 'html': 125 | if ( ! empty( $arguments['html'] ) ) { 126 | echo wp_kses_post( $arguments['html'] ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped 127 | } 128 | break; 129 | case 'text': 130 | case 'url': 131 | printf( 132 | '', 133 | esc_attr( $uid ), 134 | esc_attr( $value ) 135 | ); 136 | break; 137 | } 138 | 139 | if ( isset( $arguments['description'] ) ) { 140 | printf( '

%s

', wp_kses_post( $arguments['description'] ) ); 141 | } 142 | } 143 | 144 | /** 145 | * Render the settings page 146 | */ 147 | public static function render_page_content() { 148 | $allowed_html = [ 149 | 'a' => [ 150 | 'href' => [], 151 | ], 152 | ]; 153 | ?> 154 |
155 |

156 | 157 |

158 | Visit the plugin page for more information.', 'wp-saml-auth' ), $allowed_html ), 'https://wordpress.org/plugins/wp-saml-auth/' ); 161 | ?> 162 |

163 | 164 |

165 | Visit the plugin page for more information.', 'wp-saml-auth' ), $allowed_html ), 'https://wordpress.org/plugins/wp-saml-auth/' ); 168 | ?> 169 |

170 | 171 |

172 | 173 |

174 | 175 |
176 | 181 |
182 | 183 |
184 | ' . esc_html__( 'Settings', 'wp-saml-auth' ) . ''; 195 | array_push( $links, $a ); 196 | return $links; 197 | } 198 | 199 | /** 200 | * Sanitize user input 201 | * 202 | * @param array $input input fields values. 203 | * @return array 204 | */ 205 | public static function sanitize_callback( $input ) { 206 | if ( empty( $input ) || ! is_array( $input ) ) { 207 | return []; 208 | } 209 | 210 | foreach ( self::$fields as $field ) { 211 | $section = self::$sections[ $field['section'] ]; 212 | $uid = $field['uid']; 213 | $value = $input[ $uid ]; 214 | 215 | // checkboxes. 216 | if ( 'checkbox' === $field['type'] ) { 217 | $input[ $uid ] = isset( $value ) ? true : false; 218 | } 219 | 220 | // required fields. 221 | if ( isset( $field['required'] ) && $field['required'] ) { 222 | if ( empty( $value ) ) { 223 | $input['connection_type'] = null; 224 | add_settings_error( 225 | WP_SAML_Auth_Options::get_option_name(), 226 | $uid, 227 | // translators: Field label. 228 | sprintf( __( '%s is a required field', 'wp-saml-auth' ), trim( $section . ' ' . $field['label'] ) ) 229 | ); 230 | } 231 | } 232 | 233 | // text fields. 234 | if ( 'text' === $field['type'] ) { 235 | if ( ! empty( $value ) ) { 236 | $input[ $uid ] = sanitize_text_field( $value ); 237 | } 238 | } 239 | 240 | // url fields. 241 | if ( 'url' === $field['type'] ) { 242 | if ( ! empty( $value ) ) { 243 | if ( filter_var( $value, FILTER_VALIDATE_URL ) ) { 244 | $input[ $uid ] = esc_url_raw( $value, [ 'http', 'https' ] ); 245 | } else { 246 | $input['connection_type'] = null; 247 | $input[ $uid ] = null; 248 | add_settings_error( 249 | WP_SAML_Auth_Options::get_option_name(), 250 | $uid, 251 | // translators: Field label. 252 | sprintf( __( '%s is not a valid URL.', 'wp-saml-auth' ), trim( $section . ' ' . $field['label'] ) ) 253 | ); 254 | } 255 | } 256 | } 257 | 258 | if ( 'x509cert' === $field['uid'] ) { 259 | if ( ! empty( $value ) ) { 260 | $value = str_replace( 'ABSPATH', ABSPATH, $value ); 261 | if ( ! file_exists( $value ) ) { 262 | add_settings_error( 263 | WP_SAML_Auth_Options::get_option_name(), 264 | $uid, 265 | // translators: Field label. 266 | sprintf( __( '%s is not a valid certificate path.', 'wp-saml-auth' ), trim( $section . ' ' . $field['label'] ) ) 267 | ); 268 | } 269 | } 270 | } 271 | } 272 | 273 | return $input; 274 | } 275 | 276 | /** 277 | * Add all fields to the settings page 278 | */ 279 | public static function setup_fields() { 280 | self::init_fields(); 281 | $options = get_option( WP_SAML_Auth_Options::get_option_name() ); 282 | foreach ( self::$fields as $field ) { 283 | if ( ! empty( $options ) && is_array( $options ) && array_key_exists( $field['uid'], $options ) ) { 284 | $field['value'] = $options[ $field['uid'] ]; 285 | } else { 286 | $field['value'] = isset( $field['default'] ) ? $field['default'] : null; 287 | } 288 | add_settings_field( 289 | $field['uid'], 290 | $field['label'], 291 | [ self::$instance, 'field_callback' ], 292 | WP_SAML_Auth_Options::get_option_name(), 293 | $field['section'], 294 | $field 295 | ); 296 | } 297 | } 298 | 299 | /** 300 | * Initialize the sections array and add settings sections 301 | */ 302 | public static function setup_sections() { 303 | self::$sections = [ 304 | 'general' => '', 305 | 'security' => __( 'Security Settings', 'wp-saml-auth' ), 306 | 'sp' => __( 'Service Provider Settings', 'wp-saml-auth' ), 307 | 'idp' => __( 'Identity Provider Settings', 'wp-saml-auth' ), 308 | 'attributes' => __( 'Attribute Mappings', 'wp-saml-auth' ), 309 | ]; 310 | foreach ( self::$sections as $id => $title ) { 311 | add_settings_section( $id, $title, null, WP_SAML_Auth_Options::get_option_name() ); 312 | } 313 | } 314 | 315 | /** 316 | * Initialize the fields array 317 | */ 318 | public static function init_fields() { 319 | self::$fields = [ 320 | // general section. 321 | [ 322 | 'section' => 'general', 323 | 'uid' => 'auto_provision', 324 | 'label' => __( 'Auto Provision', 'wp-saml-auth' ), 325 | 'type' => 'checkbox', 326 | 'description' => __( 'If checked, create a new WordPress user upon login.
If unchecked, WordPress user will already need to exist in order to log in.', 'wp-saml-auth' ), 327 | 'default' => 'true', 328 | ], 329 | [ 330 | 'section' => 'general', 331 | 'uid' => 'permit_wp_login', 332 | 'label' => __( 'Permit WordPress login', 'wp-saml-auth' ), 333 | 'type' => 'checkbox', 334 | 'description' => __( 'If checked, WordPress user can also log in with the standard username and password flow.', 'wp-saml-auth' ), 335 | 'default' => 'true', 336 | ], 337 | [ 338 | 'section' => 'general', 339 | 'uid' => 'get_user_by', 340 | 'label' => __( 'Get User By', 'wp-saml-auth' ), 341 | 'type' => 'select', 342 | 'choices' => [ 343 | 'email' => 'email', 344 | 'login' => 'login', 345 | ], 346 | 'description' => __( 'Attribute by which SAML requests are matched to WordPress users.', 'wp-saml-auth' ), 347 | 'default' => 'email', 348 | ], 349 | [ 350 | 'section' => 'general', 351 | 'uid' => 'baseurl', 352 | 'label' => __( 'Base URL', 'wp-saml-auth' ), 353 | 'type' => 'url', 354 | 'description' => __( 'The base url to be used when constructing URLs.', 'wp-saml-auth' ), 355 | 'default' => home_url(), 356 | ], 357 | // Security section. 358 | [ 359 | 'section' => 'security', 360 | 'uid' => 'security_info', 361 | 'label' => __( 'Security Information', 'wp-saml-auth' ), 362 | 'type' => 'html', 363 | 'html' => '
' . 364 | '

' . __( 'SimpleSAMLphp Security Requirements:', 'wp-saml-auth' ) . '

' . 365 | '
    ' . 366 | // Translators: %s maps to the critical version of SimpleSAMLphp. 367 | '
  • ' . sprintf( __( 'Critical Security Requirement: Version %s or later is required to fix CVE-2023-26881 (XML signature validation bypass vulnerability).', 'wp-saml-auth' ), WP_SAML_Auth::get_option( 'critical_simplesamlphp_version' ) ) . '
  • ' . 368 | // Translators: %s maps to the minimum version of SimpleSAMLphp. 369 | '
  • ' . sprintf( __( 'Recommended Security Requirement: Version %s or later is recommended for additional security fixes.', 'wp-saml-auth' ), WP_SAML_Auth::get_option( 'min_simplesamlphp_version' ) ) . '
  • ' . 370 | '
' . 371 | '

' . __( 'Authentication will be blocked for versions below the critical security requirement when "Enforce Security Requirements" is enabled.', 'wp-saml-auth' ) . '

' . 372 | '
', 373 | ], 374 | [ 375 | 'section' => 'security', 376 | 'uid' => 'enforce_min_simplesamlphp_version', 377 | 'label' => __( 'Enforce Security Requirements', 'wp-saml-auth' ), 378 | 'type' => 'checkbox', 379 | 'description' => __( 'If checked, authentication will be blocked for SimpleSAMLphp versions with critical security vulnerabilities (below 2.0.0).', 'wp-saml-auth' ), 380 | 'default' => true, 381 | ], 382 | // sp section. 383 | [ 384 | 'section' => 'sp', 385 | 'uid' => 'sp_entityId', 386 | 'label' => __( 'Entity Id (Required)', 'wp-saml-auth' ), 387 | 'type' => 'text', 388 | 'choices' => false, 389 | 'description' => __( 'SP (WordPress) entity identifier.', 'wp-saml-auth' ), 390 | 'default' => 'urn:' . parse_url( home_url(), PHP_URL_HOST ), 391 | 'required' => true, 392 | ], 393 | [ 394 | 'section' => 'sp', 395 | 'uid' => 'sp_assertionConsumerService_url', 396 | 'label' => __( 'Assertion Consumer Service URL (Required)', 'wp-saml-auth' ), 397 | 'type' => 'url', 398 | 'description' => __( 'URL where the response from the IdP should be returned (usually the login URL).', 'wp-saml-auth' ), 399 | 'default' => home_url( '/wp-login.php' ), 400 | 'required' => true, 401 | ], 402 | // idp section. 403 | [ 404 | 'section' => 'idp', 405 | 'uid' => 'idp_entityId', 406 | 'label' => __( 'Entity Id (Required)', 'wp-saml-auth' ), 407 | 'type' => 'text', 408 | 'description' => __( 'IdP entity identifier.', 'wp-saml-auth' ), 409 | 'required' => true, 410 | ], 411 | [ 412 | 'section' => 'idp', 413 | 'uid' => 'idp_singleSignOnService_url', 414 | 'label' => __( 'Single SignOn Service URL (Required)', 'wp-saml-auth' ), 415 | 'type' => 'url', 416 | 'description' => __( 'URL of the IdP where the SP (WordPress) will send the authentication request.', 'wp-saml-auth' ), 417 | 'required' => true, 418 | ], 419 | [ 420 | 'section' => 'idp', 421 | 'uid' => 'idp_singleLogoutService_url', 422 | 'label' => __( 'Single Logout Service URL', 'wp-saml-auth' ), 423 | 'type' => 'url', 424 | 'description' => __( 'URL of the IdP where the SP (WordPress) will send the signout request.', 'wp-saml-auth' ), 425 | ], 426 | [ 427 | 'section' => 'idp', 428 | 'uid' => 'x509cert', 429 | 'label' => __( 'x509 Certificate Path', 'wp-saml-auth' ), 430 | 'type' => 'text', 431 | 'description' => __( 'Path to the x509 certificate file, used for verifying the request.
Include ABSPATH to set path base to WordPress\' ABSPATH constant.', 'wp-saml-auth' ), 432 | ], 433 | [ 434 | 'section' => 'idp', 435 | 'uid' => 'certFingerprint', 436 | 'label' => __( 'Certificate Fingerprint', 'wp-saml-auth' ), 437 | 'type' => 'text', 438 | 'description' => __( 'If not using x509 certificate, paste the certificate fingerprint and specify the fingerprint algorithm below.', 'wp-saml-auth' ), 439 | ], 440 | [ 441 | 'section' => 'idp', 442 | 'uid' => 'certFingerprintAlgorithm', 443 | 'label' => __( 'Certificate Fingerprint Algorithm', 'wp-saml-auth' ), 444 | 'type' => 'select', 445 | 'choices' => [ 446 | '' => __( 'N/A', 'wp-saml-auth' ), 447 | 'sha1' => 'sha1', 448 | 'sha256' => 'sha256', 449 | 'sha384' => 'sha384', 450 | 'sha512' => 'sha512', 451 | ], 452 | ], 453 | // attributes section. 454 | [ 455 | 'section' => 'attributes', 456 | 'uid' => 'user_login_attribute', 457 | 'label' => 'user_login', 458 | 'type' => 'text', 459 | 'default' => 'uid', 460 | ], 461 | [ 462 | 'section' => 'attributes', 463 | 'uid' => 'user_email_attribute', 464 | 'label' => 'user_email', 465 | 'type' => 'text', 466 | 'default' => 'email', 467 | ], 468 | [ 469 | 'section' => 'attributes', 470 | 'uid' => 'display_name_attribute', 471 | 'label' => 'display_name', 472 | 'type' => 'text', 473 | 'default' => 'display_name', 474 | ], 475 | [ 476 | 'section' => 'attributes', 477 | 'uid' => 'first_name_attribute', 478 | 'label' => 'first_name', 479 | 'type' => 'text', 480 | 'default' => 'first_name', 481 | ], 482 | [ 483 | 'section' => 'attributes', 484 | 'uid' => 'last_name_attribute', 485 | 'label' => 'last_name', 486 | 'type' => 'text', 487 | 'default' => 'last_name', 488 | ], 489 | ]; 490 | } 491 | 492 | /** 493 | * Gets all of the fields. 494 | * 495 | * @return array 496 | */ 497 | public static function get_fields() { 498 | self::init_fields(); 499 | return self::$fields; 500 | } 501 | } 502 | -------------------------------------------------------------------------------- /readme.txt: -------------------------------------------------------------------------------- 1 | === WP SAML Auth === 2 | Contributors: getpantheon, danielbachhuber, Outlandish Josh, jspellman, jazzs3quence 3 | Tags: authentication, SAML 4 | Requires at least: 6.4 5 | Tested up to: 6.8.1 6 | Requires PHP: 7.3 7 | Stable tag: 2.2.0 8 | License: GPLv2 or later 9 | License URI: http://www.gnu.org/licenses/gpl-2.0.html 10 | 11 | SAML authentication for WordPress. 12 | 13 | == Description == 14 | 15 | SAML authentication for WordPress, using the bundled OneLogin SAML library or optionally installed [SimpleSAMLphp](https://simplesamlphp.org/). OneLogin provides a SAML authentication bridge; SimpleSAMLphp provides SAML plus a variety of other authentication mechanisms. This plugin acts as a bridge between WordPress and the authentication library. 16 | 17 | If your organization uses Google Apps, [integrating Google Apps with WP SAML Auth](https://pantheon.io/docs/wordpress-google-sso/) takes just a few steps. 18 | 19 | The standard user flow looks like this: 20 | 21 | * User can log in via SAML using a button added to the standard WordPress login view. 22 | * When the button is clicked, the user is handed off to the authentication library. With OneLogin, the user is redirected to the SAML identity provider. With SimpleSAMLphp, the user is redirected to the SimpleSAMLphp install. 23 | * Once the user is authenticated with the identity provider, they're redirected back to WordPress and signed in to their account. A new WordPress user will be created if none exists (although this behavior can be disabled). 24 | * When the user logs out of WordPress, they are also logged out of the identity provider. 25 | 26 | A set of configuration options allow you to change the plugin's default behavior. For instance, `permit_wp_login=>false` will force all authentication to go through the SAML identity provider, bypassing `wp-login.php`. Similiarly, `auto_provision=>false` will disable automatic creation of new WordPress users. 27 | 28 | See installation instructions for full configuration details. 29 | 30 | == Installation == 31 | 32 | Once you've activated the plugin, and have access to a functioning SAML Identity Provider (IdP), there are a couple of ways WP SAML Auth can be configured: 33 | 34 | 1. **Settings page in the WordPress backend.** The settings page offers the most common configuration options, but not all. It's located at "Settings" -> "WP SAML Auth". 35 | 2. **Code snippet applied with a filter.** The code snippet approach, documented below, allows access to all configuration settings. The settings page is disabled entirely when a code snippet is present. 36 | 37 | If you're connecting directly to an existing IdP, you should use the bundled OneLogin SAML library. The necessary and most common settings are available in the WordPress backend. 38 | 39 | If you have more complex authentication needs, then you can also use a SimpleSAMLphp installation running in the same environment. These settings are not configurable through the WordPress backend; they'll need to be defined with a filter. And, if you have a filter in place, the WordPress backend settings will be removed. 40 | 41 | **Note:** A security vulnerability was found in SimpleSAMLphp versions 2.0.0 and below. It is highly recommended if you are using SimpleSAMLphp with WP SAML Auth that you update your SimpleSAMLphp library to 2.4.0 or above. (See [CVE-2025-27773](https://nvd.nist.gov/vuln/detail/CVE-2025-27773) and [The SimpleSAMLphp SAML2 library incorrectly verifies signatures for HTTP-Redirect bindings](https://github.com/advisories/GHSA-46r4-f8gj-xg56) for more information.) 42 | 43 | Additional explanation of each setting can be found in the code snippet below. 44 | 45 | To install SimpleSAMLphp locally for testing purposes, the [Identity Provider QuickStart](https://simplesamlphp.org/docs/stable/simplesamlphp-idp) is a good place to start. On Pantheon, the SimpleSAMLphp web directory needs to be symlinked to `~/code/simplesaml` to be properly handled by Nginx. [Read the docs](https://pantheon.io/docs/shibboleth-sso/) for more details about configuring SimpleSAMLphp on Pantheon. 46 | 47 | Because SAML authentication is handled as a part of the login flow, your SAML identity provider will need to send responses back to `wp-login.php`. For instance, if your domain is `pantheon.io`, then you'd use `http://pantheon.io/wp-login.php` as your `AssertionConsumerService` configuration value. 48 | 49 | To configure the plugin with a filter, or for additional detail on each setting, use this code snippet: 50 | 51 | function wpsax_filter_option( $value, $option_name ) { 52 | $defaults = array( 53 | /** 54 | * Type of SAML connection bridge to use. 55 | * 56 | * 'internal' uses OneLogin bundled library; 'simplesamlphp' uses SimpleSAMLphp. 57 | * 58 | * Defaults to SimpleSAMLphp for backwards compatibility. 59 | * 60 | * @param string 61 | */ 62 | 'connection_type' => 'internal', 63 | /** 64 | * Configuration options for OneLogin library use. 65 | * 66 | * See comments with "Required:" for values you absolutely need to configure. 67 | * 68 | * @param array 69 | */ 70 | 'internal_config' => array( 71 | // Validation of SAML responses is required. 72 | 'strict' => true, 73 | 'debug' => defined( 'WP_DEBUG' ) && WP_DEBUG ? true : false, 74 | 'baseurl' => home_url(), 75 | 'sp' => array( 76 | 'entityId' => 'urn:' . parse_url( home_url(), PHP_URL_HOST ), 77 | 'assertionConsumerService' => array( 78 | 'url' => wp_login_url(), 79 | 'binding' => 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST', 80 | ), 81 | ), 82 | 'idp' => array( 83 | // Required: Set based on provider's supplied value. 84 | 'entityId' => '', 85 | 'singleSignOnService' => array( 86 | // Required: Set based on provider's supplied value. 87 | 'url' => '', 88 | 'binding' => 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect', 89 | ), 90 | 'singleLogoutService' => array( 91 | // Required: Set based on provider's supplied value. 92 | 'url' => '', 93 | 'binding' => 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect', 94 | ), 95 | // Required: Contents of the IDP's public x509 certificate. 96 | // Use file_get_contents() to load certificate contents into scope. 97 | 'x509cert' => '', 98 | // Optional: Instead of using the x509 cert, you can specify the fingerprint and algorithm. 99 | 'certFingerprint' => '', 100 | 'certFingerprintAlgorithm' => '', 101 | ), 102 | ), 103 | /** 104 | * Path to SimpleSAMLphp autoloader. 105 | * 106 | * Follow the standard implementation by installing SimpleSAMLphp 107 | * alongside the plugin, and provide the path to its autoloader. 108 | * Alternatively, this plugin will work if it can find the 109 | * `SimpleSAML_Auth_Simple` class. 110 | * 111 | * @param string 112 | */ 113 | 'simplesamlphp_autoload' => dirname( __FILE__ ) . '/simplesamlphp/lib/_autoload.php', 114 | /** 115 | * Authentication source to pass to SimpleSAMLphp 116 | * 117 | * This must be one of your configured identity providers in 118 | * SimpleSAMLphp. If the identity provider isn't configured 119 | * properly, the plugin will not work properly. 120 | * 121 | * @param string 122 | */ 123 | 'auth_source' => 'default-sp', 124 | /** 125 | * Whether or not to automatically provision new WordPress users. 126 | * 127 | * When WordPress is presented with a SAML user without a 128 | * corresponding WordPress account, it can either create a new user 129 | * or display an error that the user needs to contact the site 130 | * administrator. 131 | * 132 | * @param bool 133 | */ 134 | 'auto_provision' => true, 135 | /** 136 | * Whether or not to permit logging in with username and password. 137 | * 138 | * If this feature is disabled, all authentication requests will be 139 | * channeled through SimpleSAMLphp. 140 | * 141 | * @param bool 142 | */ 143 | 'permit_wp_login' => true, 144 | /** 145 | * Attribute by which to get a WordPress user for a SAML user. 146 | * 147 | * @param string Supported options are 'email' and 'login'. 148 | */ 149 | 'get_user_by' => 'email', 150 | /** 151 | * SAML attribute which includes the user_login value for a user. 152 | * 153 | * @param string 154 | */ 155 | 'user_login_attribute' => 'uid', 156 | /** 157 | * SAML attribute which includes the user_email value for a user. 158 | * 159 | * @param string 160 | */ 161 | 'user_email_attribute' => 'mail', 162 | /** 163 | * SAML attribute which includes the display_name value for a user. 164 | * 165 | * @param string 166 | */ 167 | 'display_name_attribute' => 'display_name', 168 | /** 169 | * SAML attribute which includes the first_name value for a user. 170 | * 171 | * @param string 172 | */ 173 | 'first_name_attribute' => 'first_name', 174 | /** 175 | * SAML attribute which includes the last_name value for a user. 176 | * 177 | * @param string 178 | */ 179 | 'last_name_attribute' => 'last_name', 180 | /** 181 | * Default WordPress role to grant when provisioning new users. 182 | * 183 | * @param string 184 | */ 185 | 'default_role' => get_option( 'default_role' ), 186 | ); 187 | $value = isset( $defaults[ $option_name ] ) ? $defaults[ $option_name ] : $value; 188 | return $value; 189 | } 190 | add_filter( 'wp_saml_auth_option', 'wpsax_filter_option', 10, 2 ); 191 | 192 | If you need to adapt authentication behavior based on the SAML response, you can do so with the `wp_saml_auth_pre_authentication` filter: 193 | 194 | /** 195 | * Reject authentication if $attributes doesn't include the authorized group. 196 | */ 197 | add_filter( 'wp_saml_auth_pre_authentication', function( $ret, $attributes ) { 198 | if ( empty( $attributes['group'] ) || ! in_array( 'administrators', $attributes['group'] ) ) { 199 | return new WP_Error( 'unauthorized-group', "Sorry, you're not a member of an authorized group." ); 200 | } 201 | return $ret; 202 | }, 10, 2 ); 203 | 204 | If you have installed SimpleSAMLphp to a non-default path, you can set that path via the `wp_saml_auth_simplesamlphp_path_array` filter. By default, it is assumed that SimpleSAMLphp is installed into one of the following paths: 205 | * `ABSPATH . 'simplesaml'` 206 | * `ABSPATH . 'private/simplesamlphp'` 207 | * `ABSPATH . 'simplesamlphp'` 208 | 209 | add_filter( 'wp_saml_auth_simplesamlphp_path_array', function( $simplesamlphp_path_array ) { 210 | // Override default paths with a defined path. 211 | return [ ABSPATH . 'path/to/simplesamlphp' ]; 212 | } 213 | 214 | You can also define an explicit path to the SimpleSAMLphp autoloader file (defaults to the `lib/_autoload.php` file under the SimpleSAMLphp path) with the `wp_saml_auth_ssp_autoloader` filter. 215 | 216 | add_filter( 'wp_saml_auth_ssp_autoloader', function( $ssp_autoloader ) { 217 | if ( ! file_exists( $ssp_autoloader ) ) { 218 | return ABSPATH . 'path/to/simplesamlphp/autoload.php'; 219 | } 220 | } 221 | 222 | == WP-CLI Commands == 223 | 224 | This plugin implements a variety of [WP-CLI](https://wp-cli.org) commands. All commands are grouped into the `wp saml-auth` namespace. 225 | 226 | $ wp help saml-auth 227 | 228 | NAME 229 | 230 | wp saml-auth 231 | 232 | DESCRIPTION 233 | 234 | Configure and manage the WP SAML Auth plugin. 235 | 236 | SYNOPSIS 237 | 238 | wp saml-auth 239 | 240 | SUBCOMMANDS 241 | 242 | scaffold-config Scaffold a configuration filter to customize WP SAML Auth usage. 243 | 244 | Use `wp help saml-auth ` to learn more about each command. 245 | 246 | == Contributing == 247 | 248 | See [CONTRIBUTING.md](https://github.com/pantheon-systems/wp-saml-auth/blob/master/CONTRIBUTING.md) for information on contributing. 249 | 250 | == Frequently Asked Questions == 251 | 252 | = Can I update an existing WordPress user's data when they log back in? = 253 | 254 | If you'd like to make sure the user's display name, first name, and last name are updated in WordPress when they log back in, you can use the following code snippet: 255 | 256 | /** 257 | * Update user attributes after a user has logged in via SAML. 258 | */ 259 | add_action( 'wp_saml_auth_existing_user_authenticated', function( $existing_user, $attributes ) { 260 | $user_args = array( 261 | 'ID' => $existing_user->ID, 262 | ); 263 | foreach ( array( 'display_name', 'first_name', 'last_name' ) as $type ) { 264 | $attribute = \WP_SAML_Auth::get_option( "{$type}_attribute" ); 265 | $user_args[ $type ] = ! empty( $attributes[ $attribute ][0] ) ? $attributes[ $attribute ][0] : ''; 266 | } 267 | wp_update_user( $user_args ); 268 | }, 10, 2 ); 269 | 270 | The `wp_saml_auth_existing_user_authenticated` action fires after the user has successfully authenticated with the SAML IdP. The code snippet then uses a pattern similar to WP SAML Auth to fetch display name, first name, and last name from the SAML response. Lastly, the code snippet updates the existing WordPress user object. 271 | 272 | = How do I use SimpleSAMLphp and WP SAML Auth on a multi web node environment? = 273 | 274 | Because SimpleSAMLphp uses PHP sessions to manage user authentication, it will work unreliably or not at all on a server configuration with multiple web nodes. This is because PHP's default session handler uses the filesystem, and each web node has a different filesystem. Fortunately, there's a way around this. 275 | 276 | First, install and activate the [WP Native PHP Sessions plugin](https://wordpress.org/plugins/wp-native-php-sessions/), which registers a database-based PHP session handler for WordPress to use. 277 | 278 | Next, modify SimpleSAMLphp's `www/_include.php` file to require `wp-load.php`. If you installed SimpleSAMLphp within the `wp-saml-auth` directory, you'd edit `wp-saml-auth/simplesamlphp/www/_include.php` to include: 279 | 280 | provider ) ) { 73 | $this->set_provider(); 74 | } 75 | return $this->provider; 76 | } 77 | 78 | /** 79 | * Determines the provider class to use and loads an instance of it, stores it to ->provider 80 | * 81 | * @return void 82 | */ 83 | protected function set_provider() { 84 | $connection_type = self::get_option( 'connection_type' ); 85 | if ( 'internal' === $connection_type ) { 86 | if ( file_exists( WP_SAML_AUTH_AUTOLOADER ) ) { 87 | require_once WP_SAML_AUTH_AUTOLOADER; 88 | } 89 | if ( ! class_exists( 'OneLogin\Saml2\Auth' ) ) { 90 | return; 91 | } 92 | $auth_config = self::get_option( 'internal_config' ); 93 | $this->provider = new OneLogin\Saml2\Auth( $auth_config ); 94 | } else { 95 | $simplesamlphp_autoloader = self::get_simplesamlphp_autoloader(); 96 | 97 | // If the autoloader exists, load it. 98 | if ( ! empty( $simplesamlphp_autoloader ) && file_exists( $simplesamlphp_autoloader ) ) { 99 | require_once $simplesamlphp_autoloader; 100 | } else { 101 | // Autoloader not found. 102 | if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) { 103 | $error_message = sprintf( 104 | // Translators: %s is the path to the SimpleSAMLphp autoloader file (if found). 105 | __( 'WP SAML Auth: SimpleSAMLphp autoloader could not be loaded for set_provider. Path determined: %s', 'wp-saml-auth' ), 106 | empty( $simplesamlphp_autoloader ) ? '[empty]' : esc_html( $simplesamlphp_autoloader ) 107 | ); 108 | error_log( $error_message ); 109 | } 110 | return; 111 | } 112 | 113 | if ( class_exists( 'SimpleSAML\Auth\Simple' ) ) { 114 | $this->simplesamlphp_class = 'SimpleSAML\Auth\Simple'; 115 | } 116 | if ( ! class_exists( $this->simplesamlphp_class ) ) { 117 | return; 118 | } 119 | $this->provider = new $this->simplesamlphp_class( self::get_option( 'auth_source' ) ); 120 | } 121 | } 122 | 123 | /** 124 | * Initialize the controller logic on the 'init' hook 125 | */ 126 | public function action_init() { 127 | add_action( 'login_head', [ $this, 'action_login_head' ] ); 128 | add_action( 'login_message', [ $this, 'action_login_message' ] ); 129 | add_action( 'wp_logout', [ $this, 'action_wp_logout' ] ); 130 | add_filter( 'login_body_class', [ $this, 'filter_login_body_class' ] ); 131 | add_filter( 'authenticate', [ $this, 'filter_authenticate' ], 21, 3 ); // after wp_authenticate_username_password runs. 132 | add_action( 'admin_notices', [ $this, 'action_admin_notices' ] ); 133 | } 134 | 135 | /** 136 | * Render CSS on the login screen 137 | */ 138 | public function action_login_head() { 139 | if ( ! did_action( 'login_form_login' ) ) { 140 | return; 141 | } 142 | 143 | ?> 144 | 158 | __( 'Use one-click authentication:', 'wp-saml-auth' ), 173 | 'button' => __( 'Sign In', 'wp-saml-auth' ), 174 | 'alt_title' => __( 'Or, sign in with WordPress:', 'wp-saml-auth' ), 175 | ]; 176 | 177 | $query_args = [ 178 | 'action' => 'wp-saml-auth', 179 | ]; 180 | $redirect_to = filter_input( INPUT_GET, 'redirect_to', FILTER_SANITIZE_URL ); 181 | if ( $redirect_to ) { 182 | $query_args['redirect_to'] = rawurlencode( $redirect_to ); 183 | } 184 | 185 | /** 186 | * Permit login screen text strings to be easily customized. 187 | * 188 | * @param array $strings Existing text strings. 189 | */ 190 | $strings = apply_filters( 'wp_saml_auth_login_strings', $strings ); 191 | echo '

' . esc_html( $strings['title'] ) . '

'; 192 | echo ''; 193 | echo '

' . esc_html( $strings['alt_title'] ) . '

'; 194 | return $message; 195 | } 196 | 197 | /** 198 | * Log the user out of the SAML instance when they log out of WordPress 199 | */ 200 | public function action_wp_logout() { 201 | /** 202 | * Fires before the user is logged out. 203 | */ 204 | do_action( 'wp_saml_auth_pre_logout' ); 205 | $provider = $this->get_provider(); 206 | if ( 'internal' === self::get_option( 'connection_type' ) ) { 207 | $internal_config = self::get_option( 'internal_config' ); 208 | if ( empty( $internal_config['idp']['singleLogoutService']['url'] ) ) { 209 | return; 210 | } 211 | $args = [ 212 | 'parameters' => [], 213 | 'nameId' => null, 214 | 'sessionIndex' => null, 215 | ]; 216 | /** 217 | * Permit the arguments passed to the logout() method to be customized. 218 | * 219 | * @param array $args Existing arguments to be passed. 220 | */ 221 | $args = apply_filters( 'wp_saml_auth_internal_logout_args', $args ); 222 | $provider->logout( 223 | add_query_arg( 'loggedout', true, wp_login_url() ), 224 | $args['parameters'], 225 | $args['nameId'], 226 | $args['sessionIndex'] 227 | ); 228 | } else { 229 | $provider->logout( add_query_arg( 'loggedout', true, wp_login_url() ) ); 230 | } 231 | } 232 | 233 | /** 234 | * Add body classes for our specific configuration attributes 235 | * 236 | * @param array $classes Body CSS classes. 237 | * @return array 238 | */ 239 | public function filter_login_body_class( $classes ) { 240 | 241 | if ( ! self::get_option( 'permit_wp_login' ) ) { 242 | $classes[] = 'wp-saml-auth-deny-wp-login'; 243 | } 244 | 245 | return $classes; 246 | } 247 | 248 | /** 249 | * Check if the user is authenticated against the SimpleSAMLphp instance 250 | * 251 | * @param mixed $user WordPress user reference. 252 | * @param string $username Username. 253 | * @param string $password Password supplied by the user. 254 | * @return mixed 255 | */ 256 | public function filter_authenticate( $user, $username, $password ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable,Generic.CodeAnalysis.UnusedFunctionParameter.FoundAfterLastUsed 257 | 258 | $permit_wp_login = self::get_option( 'permit_wp_login' ); 259 | if ( is_a( $user, 'WP_User' ) ) { 260 | 261 | if ( ! $permit_wp_login ) { 262 | $user = $this->do_saml_authentication(); 263 | } 264 | 265 | return $user; 266 | } 267 | 268 | if ( ! $permit_wp_login ) { 269 | $should_saml = ! isset( $_GET['loggedout'] ); 270 | } else { 271 | $should_saml = isset( $_POST['SAMLResponse'] ) || ( isset( $_GET['action'] ) && 'wp-saml-auth' === $_GET['action'] ); 272 | } 273 | 274 | if ( $should_saml ) { 275 | return $this->do_saml_authentication(); 276 | } 277 | 278 | return $user; 279 | } 280 | 281 | /** 282 | * Do the SAML authentication dance 283 | */ 284 | public function do_saml_authentication() { 285 | // Check SimpleSAMLphp version if using simplesamlphp connection type. 286 | if ( 'simplesamlphp' === self::get_option( 'connection_type' ) && self::get_option( 'enforce_min_simplesamlphp_version' ) ) { 287 | $version = $this->get_simplesamlphp_version(); 288 | $version_status = $this->check_simplesamlphp_version( $version ); 289 | 290 | if ( 'critical' === $version_status ) { 291 | $critical_version = self::get_option( 'critical_simplesamlphp_version' ); 292 | return new WP_Error( 293 | 'wp_saml_auth_vulnerable_simplesamlphp', 294 | sprintf( 295 | // Translators: 1 is the installed SimpleSAMLphp version. 2 is the critical SImpleSAMLphp version. 296 | __( 'Authentication blocked: Your SimpleSAMLphp version (%1$s) has a critical security vulnerability. Please update to version %2$s or later.', 'wp-saml-auth' ), 297 | esc_html( $version ), 298 | esc_html( $critical_version ) 299 | ) 300 | ); 301 | } 302 | } 303 | 304 | $provider = $this->get_provider(); 305 | if ( is_a( $provider, 'OneLogin\Saml2\Auth' ) ) { 306 | if ( ! empty( $_POST['SAMLResponse'] ) ) { 307 | $provider->processResponse(); 308 | if ( ! $provider->isAuthenticated() ) { 309 | // Translators: Includes error reason from OneLogin. 310 | return new WP_Error( 'wp_saml_auth_unauthenticated', sprintf( __( 'User is not authenticated with SAML IdP. Reason: %s', 'wp-saml-auth' ), $provider->getLastErrorReason() ) ); 311 | } 312 | $attributes = $provider->getAttributes(); 313 | $redirect_to = filter_input( INPUT_POST, 'RelayState', FILTER_SANITIZE_URL ); 314 | $permit_wp_login = self::get_option( 'permit_wp_login' ); 315 | if ( $redirect_to ) { 316 | // When $permit_wp_login=true, we only care about accidentially triggering the redirect 317 | // to the IDP. However, when $permit_wp_login=false, hitting wp-login will always 318 | // trigger the IDP redirect. 319 | if ( ( $permit_wp_login && false === stripos( $redirect_to, 'action=wp-saml-auth' ) ) 320 | || ( ! $permit_wp_login && false === stripos( $redirect_to, parse_url( wp_login_url(), PHP_URL_PATH ) ) ) ) { 321 | add_filter( 322 | 'login_redirect', 323 | function () use ( $redirect_to ) { 324 | return $redirect_to; 325 | }, 326 | 1 327 | ); 328 | } 329 | } 330 | } else { 331 | $redirect_to = filter_input( INPUT_GET, 'redirect_to', FILTER_SANITIZE_URL ); 332 | $redirect_to = $redirect_to ? $redirect_to : ( isset( $_SERVER['REQUEST_URI'] ) ? sanitize_text_field( $_SERVER['REQUEST_URI'] ) : null ); 333 | /** 334 | * Allows forceAuthn="true" to be enabled. 335 | * 336 | * @param boolean $force_auth forceAuthn behavior. 337 | */ 338 | $force_authn = apply_filters( 'wp_saml_auth_force_authn', false ); 339 | 340 | /** 341 | * Allows login parameters to be customized. 342 | * 343 | * @param array $parameters 344 | */ 345 | $parameters = apply_filters( 'wp_saml_auth_login_parameters', [] ); 346 | 347 | $provider->login( $redirect_to, $parameters, $force_authn ); 348 | } 349 | } elseif ( is_a( $provider, $this->simplesamlphp_class ) ) { 350 | $redirect_to = filter_input( INPUT_GET, 'redirect_to', FILTER_SANITIZE_URL ); 351 | if ( $redirect_to ) { 352 | $redirect_to = add_query_arg( 353 | [ 354 | 'redirect_to' => rawurlencode( $redirect_to ), 355 | 'action' => 'wp-saml-auth', 356 | ], 357 | wp_login_url() 358 | ); 359 | } else { 360 | $redirect_to = wp_login_url(); 361 | // Make sure we're only dealing with the URI components and not arguments. 362 | $request = explode( '?', sanitize_text_field( $_SERVER['REQUEST_URI'] ) ); 363 | // Only persist redirect_to when it's not wp-login.php. 364 | if ( false === stripos( $redirect_to, reset( $request ) ) ) { 365 | $redirect_to = add_query_arg( 'redirect_to', sanitize_text_field( $_SERVER['REQUEST_URI'] ), $redirect_to ); 366 | } else { 367 | $redirect_to = add_query_arg( [ 'action' => 'wp-saml-auth' ], $redirect_to ); 368 | } 369 | } 370 | $provider->requireAuth( 371 | [ 372 | 'ReturnTo' => $redirect_to, 373 | ] 374 | ); 375 | $attributes = $provider->getAttributes(); 376 | } else { 377 | return new WP_Error( 'wp_saml_auth_invalid_provider', __( 'Invalid provider specified for SAML authentication', 'wp-saml-auth' ) ); 378 | } 379 | 380 | /** 381 | * Allows to modify attributes before the SAML authentication. 382 | * 383 | * @param array $attributes All attributes received from the SAML response. 384 | * @param object $provider Provider instance currently in use. 385 | */ 386 | $attributes = apply_filters( 'wp_saml_auth_attributes', $attributes, $provider ); 387 | 388 | /** 389 | * Runs before the SAML authentication dance proceeds 390 | * 391 | * Can be used to short-circuit the authentication process. 392 | * 393 | * @param false $short_circuit Return some non-false value to bypass authentication. 394 | * @param array $attributes All attributes received from the SAML response. 395 | */ 396 | $pre_auth = apply_filters( 'wp_saml_auth_pre_authentication', false, $attributes ); 397 | if ( false !== $pre_auth ) { 398 | return $pre_auth; 399 | } 400 | 401 | if ( empty( $attributes ) ) { 402 | return new WP_Error( 'wp_saml_auth_no_attributes', esc_html__( 'No attributes were present in SAML response. Attributes are used to create and fetch users. Please contact your administrator', 'wp-saml-auth' ) ); 403 | } 404 | 405 | // Some SAML providers return oddly shaped responses. 406 | $attributes = apply_filters( 'wp_saml_auth_patch_attributes', $attributes, $provider ); 407 | $get_user_by = self::get_option( 'get_user_by' ); 408 | $attribute = self::get_option( "user_{$get_user_by}_attribute" ); 409 | if ( empty( $attributes[ $attribute ][0] ) ) { 410 | // Translators: Communicates how the user is fetched based on the SAML response. 411 | return new WP_Error( 'wp_saml_auth_missing_attribute', sprintf( esc_html__( '"%1$s" attribute is expected, but missing, in SAML response. Attribute is used to fetch existing user by "%2$s". Please contact your administrator.', 'wp-saml-auth' ), $attribute, $get_user_by ) ); 412 | } 413 | 414 | $existing_user = get_user_by( $get_user_by, $attributes[ $attribute ][0] ); 415 | if ( $existing_user ) { 416 | /** 417 | * Runs after a existing user has been authenticated in WordPress 418 | * 419 | * @param WP_User $existing_user The existing user object. 420 | * @param array $attributes All attributes received from the SAML Response 421 | */ 422 | do_action( 'wp_saml_auth_existing_user_authenticated', $existing_user, $attributes ); 423 | return $existing_user; 424 | } 425 | if ( ! self::get_option( 'auto_provision' ) ) { 426 | return new WP_Error( 'wp_saml_auth_auto_provision_disabled', esc_html__( 'No WordPress user exists for your account. Please contact your administrator.', 'wp-saml-auth' ) ); 427 | } 428 | 429 | $user_args = []; 430 | foreach ( [ 'display_name', 'user_login', 'user_email', 'first_name', 'last_name' ] as $type ) { 431 | $attribute = self::get_option( "{$type}_attribute" ); 432 | $user_args[ $type ] = ! empty( $attributes[ $attribute ][0] ) ? $attributes[ $attribute ][0] : ''; 433 | } 434 | $user_args['role'] = self::get_option( 'default_role' ); 435 | $user_args['user_pass'] = wp_generate_password(); 436 | /** 437 | * Runs before a user is created based off a SAML response. 438 | * 439 | * @param array $user_args Arguments passed to wp_insert_user(). 440 | * @param array $attributes Attributes from the SAML response. 441 | */ 442 | $user_args = apply_filters( 'wp_saml_auth_insert_user', $user_args, $attributes ); 443 | $user_id = wp_insert_user( $user_args ); 444 | if ( is_wp_error( $user_id ) ) { 445 | return $user_id; 446 | } 447 | 448 | $user = get_user_by( 'id', $user_id ); 449 | 450 | /** 451 | * Runs after the user has been authenticated in WordPress 452 | * 453 | * @param WP_User $user The new user object. 454 | * @param array $attributes All attributes received from the SAML Response 455 | */ 456 | do_action( 'wp_saml_auth_new_user_authenticated', $user, $attributes ); 457 | return $user; 458 | } 459 | 460 | /** 461 | * Retrieves the path to the SimpleSAMLphp autoloader file. 462 | * 463 | * This method attempts to determine the correct path to the SimpleSAMLphp autoloader 464 | * by checking the following, in order: 465 | * 1. A valid path resulting from the 'wp_saml_auth_ssp_autoloader' filter. 466 | * 2. The path configured via the 'simplesamlphp_autoload' option, if set and exists. 467 | * 3. A set of default paths, which can be filtered via 'wp_saml_auth_simplesamlphp_path_array'. 468 | * For each path, it checks if the directory exists and contains 'lib/_autoload.php'. 469 | * 470 | * @return string The path to the SimpleSAMLphp autoloader file, or an empty string if not found. 471 | */ 472 | public static function get_simplesamlphp_autoloader() { 473 | /** 474 | * Define a path to SimpleSAMLphp autoloader file. 475 | * 476 | * @param string $ssp_autoloader The path to the SimpleSAMLphp autoloader file. 477 | */ 478 | $simplesamlphp_autoloader = apply_filters( 'wp_saml_auth_ssp_autoloader', '' ); 479 | 480 | if ( ! empty( $simplesamlphp_autoloader ) && file_exists( $simplesamlphp_autoloader ) ) { 481 | return $simplesamlphp_autoloader; 482 | } 483 | 484 | /* 485 | * If self::$is_resolving_autoloader_via_option is true, this call is recursive 486 | * (from wpsa_filter_option for 'simplesamlphp_autoload' default), so skip option check. 487 | */ 488 | if ( ! self::$is_resolving_autoloader_via_option ) { 489 | self::$is_resolving_autoloader_via_option = true; 490 | $simplesamlphp_autoloader = self::get_option( 'simplesamlphp_autoload' ); 491 | self::$is_resolving_autoloader_via_option = false; // Reset recursion guard. 492 | 493 | // Check the configured 'simplesamlphp_autoload' path first. 494 | if ( ! empty( $simplesamlphp_autoloader ) && file_exists( $simplesamlphp_autoloader ) ) { 495 | return $simplesamlphp_autoloader; 496 | } 497 | } 498 | 499 | /** 500 | * Add the default path for simplesaml and allow it to be filtered. 501 | * This is checked regardless of whether an option is set. 502 | * 503 | * @param array $simplesamlphp_path_array An array of paths to check for SimpleSAMLphp. 504 | */ 505 | $base_paths = apply_filters( 'wp_saml_auth_simplesamlphp_path_array', [ 506 | ABSPATH . 'simplesaml', 507 | ABSPATH . 'private/simplesamlphp', 508 | ABSPATH . 'simplesamlphp', 509 | plugin_dir_path( __DIR__ ) . 'simplesamlphp', 510 | ] ); 511 | 512 | foreach ( $base_paths as $base_path ) { 513 | $trimmed_base = rtrim( $base_path, '/\\' ); 514 | 515 | if ( is_dir( $trimmed_base ) ) { 516 | // If an autoloader exists in a guessed path, try to include it. 517 | $simplesamlphp_autoloader_path = $trimmed_base . '/lib/_autoload.php'; 518 | if ( file_exists( $simplesamlphp_autoloader_path ) ) { 519 | return $simplesamlphp_autoloader_path; 520 | } 521 | } 522 | } 523 | 524 | // Fallback for plugin-relative vendor autoloader if filter/option failed or in recursive call for default. 525 | $simplesamlphp_vendor_path = WP_PLUGIN_DIR . '/' . basename( dirname( __DIR__ ) ) . '/simplesamlphp/vendor/autoload.php'; 526 | if ( file_exists( $simplesamlphp_vendor_path ) ) { 527 | return $simplesamlphp_vendor_path; 528 | } 529 | 530 | // If we got here, this should be an empty string. 531 | return $simplesamlphp_autoloader; 532 | } 533 | 534 | /** 535 | * Get the installed SimpleSAMLphp version. 536 | * Attempts to find SimpleSAMLphp first via the configured option, 537 | * then by checking common installation paths. 538 | * 539 | * @return string|false Version string if found, false if not found. 540 | */ 541 | public function get_simplesamlphp_version() { 542 | $simplesamlphp_autoloader = self::get_simplesamlphp_autoloader(); 543 | $base_dir = rtrim( preg_replace( '#/lib/?$#', '', dirname( $simplesamlphp_autoloader ) ), '/\\' ); 544 | 545 | try { 546 | if ( file_exists( $simplesamlphp_autoloader ) ) { 547 | include_once $simplesamlphp_autoloader; 548 | } 549 | } catch ( \Exception $e ) { 550 | // Log an error to the debug log. 551 | if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) { 552 | error_log( sprintf( 553 | // Translators: %s is the error message returned from the exception. 554 | __( 'SimpleSAMLphp autoloader not found. Error: %s', 'wp-saml-auth' ), 555 | $e->getMessage() 556 | ) ); 557 | } 558 | } 559 | 560 | /** 561 | * Try to get version from SimpleSAML\Configuration (SSP 2.0+). 562 | * First, check for the VERSION constant. 563 | */ 564 | if ( class_exists( 'SimpleSAML\Configuration' ) ) { 565 | // Try getting the version from the VERSION constant. 566 | if ( defined( 'SimpleSAML\Configuration::VERSION' ) ) { 567 | $ssp_version = \SimpleSAML\Configuration::VERSION; 568 | if ( ! empty( $ssp_version ) && is_string( $ssp_version ) ) { 569 | return $ssp_version; 570 | } 571 | } 572 | 573 | // Otherwise get the version from getVersion. 574 | try { 575 | $simple_saml_config = \SimpleSAML\Configuration::getInstance(); 576 | if ( method_exists( $simple_saml_config, 'getVersion' ) ) { 577 | $ssp_version = $simple_saml_config->getVersion(); 578 | if ( ! empty( $ssp_version ) && is_string( $ssp_version ) ) { 579 | return $ssp_version; 580 | } 581 | } 582 | } catch ( \Exception $e ) { 583 | // Log an error to the debug log. 584 | if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) { 585 | error_log( sprintf( 586 | // Translators: %s is the error message returned from the exception. 587 | __( 'Error getting SimpleSAMLphp version: %s', 'wp-saml-auth' ), 588 | $e->getMessage() 589 | ) ); 590 | } 591 | } 592 | } 593 | 594 | // Try to get version from legacy SimpleSAML_Configuration class (SSP < 2.0). 595 | if ( class_exists( 'SimpleSAML_Configuration' ) ) { 596 | try { 597 | if ( is_callable( [ 'SimpleSAML_Configuration', 'getConfig' ] ) ) { 598 | $simple_saml_config_obj = \SimpleSAML_Configuration::getConfig(); 599 | if ( is_object( $simple_saml_config_obj ) && method_exists( $simple_saml_config_obj, 'getVersion' ) ) { 600 | $ssp_version = $simple_saml_config_obj->getVersion(); 601 | if ( ! empty( $ssp_version ) && is_string( $ssp_version ) ) { 602 | return $ssp_version; 603 | } 604 | } 605 | } 606 | } catch ( \Exception $e ) { 607 | // Log an error to the debug log. 608 | if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) { 609 | error_log( sprintf( 610 | // Translators: %s is the error message returned from the exception. 611 | __( 'Error getting SimpleSAMLphp version: %s', 'wp-saml-auth' ), 612 | $e->getMessage() 613 | ) ); 614 | } 615 | } 616 | } 617 | 618 | if ( ! is_dir( $base_dir ) ) { 619 | // Log an error to the debug log if the base directory does not exist. 620 | if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) { 621 | error_log( sprintf( 622 | // Translators: %s is the base directory we tried. 623 | __( 'SimpleSAMLphp base directory does not exist: %s', 'wp-saml-auth' ), 624 | $base_dir 625 | ) ); 626 | } 627 | return false; 628 | } 629 | 630 | // Check for a Composer file. 631 | $composer_path = $base_dir . '/composer.json'; 632 | if ( file_exists( $composer_path ) ) { 633 | $composer_data_json = file_get_contents( $composer_path ); 634 | if ( $composer_data_json ) { 635 | $composer_data = json_decode( $composer_data_json, true ); 636 | if ( is_array( $composer_data ) && isset( $composer_data['version'] ) && ! empty( $composer_data['version'] ) && is_string( $composer_data['version'] ) ) { 637 | return $composer_data['version']; 638 | } 639 | } 640 | } 641 | 642 | // Check for a VERSION file. 643 | $version_file_path = $base_dir . '/VERSION'; 644 | if ( file_exists( $version_file_path ) ) { 645 | $version_str = trim( file_get_contents( $version_file_path ) ); 646 | if ( ! empty( $version_str ) && is_string( $version_str ) ) { 647 | return $version_str; 648 | } 649 | } 650 | 651 | // Check for a version.php file. 652 | $version_php_path = $base_dir . '/config/version.php'; 653 | if ( file_exists( $version_php_path ) ) { 654 | $version_data = include $version_php_path; 655 | if ( is_array( $version_data ) && isset( $version_data['version'] ) && ! empty( $version_data['version'] ) && is_string( $version_data['version'] ) ) { 656 | return $version_data['version']; 657 | } 658 | } 659 | 660 | return false; 661 | } 662 | 663 | /** 664 | * Check if the installed SimpleSAMLphp version meets the minimum requirements 665 | * 666 | * @param string $version Version to check against minimum requirements 667 | * @return string 'critical', 'warning', or 'ok' based on version comparison 668 | */ 669 | public function check_simplesamlphp_version( $version ) { 670 | if ( ! $version ) { 671 | return 'unknown'; 672 | } 673 | 674 | $min_version = self::get_option( 'min_simplesamlphp_version' ); 675 | $critical_version = self::get_option( 'critical_simplesamlphp_version' ); 676 | 677 | if ( version_compare( $version, $critical_version, '<' ) ) { 678 | return 'critical'; 679 | } elseif ( version_compare( $version, $min_version, '<' ) ) { 680 | return 'warning'; 681 | } 682 | return 'ok'; 683 | } 684 | 685 | /** 686 | * Displays notices in the admin if certain configuration properties aren't correct. 687 | */ 688 | public function action_admin_notices() { 689 | if ( ! current_user_can( 'manage_options' ) ) { 690 | return; 691 | } 692 | 693 | $connection_type = self::get_option( 'connection_type' ); 694 | $simplesamlphp_version = $this->get_simplesamlphp_version(); 695 | $simplesamlphp_version_status = $this->check_simplesamlphp_version( $simplesamlphp_version ); 696 | $plugin_page = 'https://wordpress.org/plugins/wp-saml-auth'; 697 | 698 | // Using 'internal' (default) connection type. 699 | if ( 'internal' === $connection_type ) { 700 | if ( file_exists( WP_SAML_AUTH_AUTOLOADER ) ) { 701 | require_once WP_SAML_AUTH_AUTOLOADER; 702 | } 703 | // If the OneLogin class does not exist, OneLogin SAML didn't load properly. 704 | if ( ! class_exists( 'OneLogin\Saml2\Auth' ) ) { 705 | wp_admin_notice( 706 | sprintf( 707 | // Translators: Links to the WP SAML Auth plugin. 708 | __( "WP SAML Auth wasn't able to find the OneLogin\Saml2\Auth class. Please verify your Composer autoloader, or visit the plugin page for more information.", 'wp-saml-auth' ), 709 | $plugin_page 710 | ), 711 | [ 712 | 'type' => 'error', 713 | 'dismissible' => true, 714 | 'attributes' => [ 715 | 'data-slug' => 'wp-saml-auth', 716 | 'data-type' => 'onelogin-not-found', 717 | ], 718 | ] 719 | ); 720 | } 721 | } 722 | 723 | // If we have a SimpleSAMLphp version but the connection type is set, we haven't set up SimpleSAMLphp correctly. 724 | if ( ! $simplesamlphp_version && $connection_type === 'simplesaml' ) { 725 | // Only show this notice if we're on the settings page. 726 | if ( ! isset( $_GET['page'] ) || $_GET['page'] !== 'wp-saml-auth-settings' ) { 727 | return; 728 | } 729 | wp_admin_notice( 730 | sprintf( 731 | // Translators: %s is the link to the plugin page. 732 | __( 'SimpleSAMLphp is defined as the SAML connection type, but the SimpleSAMLphp library was not found.Visit the plugin page for more information', 'wp-saml-auth' ), 733 | $plugin_page 734 | ), 735 | [ 736 | 'type' => 'error', 737 | 'dismissible' => true, 738 | 'attributes' => [ 739 | 'data-slug' => 'wp-saml-auth', 740 | 'data-type' => 'simplesamlphp-not-found', 741 | ], 742 | ] 743 | ); 744 | } 745 | 746 | // Check SimpleSAMLphp version. 747 | if ( $simplesamlphp_version !== false ) { 748 | if ( 'critical' === $simplesamlphp_version_status ) { 749 | $min_version = self::get_option( 'critical_simplesamlphp_version' ); 750 | wp_admin_notice( 751 | sprintf( 752 | // Translators: 1 is the installed version of SimpleSAMLphp, 2 is the minimum version and 3 is the most secure version. 753 | __( 'Security Alert: The SimpleSAMLphp version used by the WP SAML Auth plugin (%1$s) has a critical security vulnerability (CVE-2023-26881). Please update to version %2$s or later. Learn more.', 'wp-saml-auth' ), 754 | esc_html( $simplesamlphp_version ), 755 | esc_html( $min_version ), 756 | esc_url( admin_url( 'options-general.php?page=wp-saml-auth-settings' ) ) 757 | ), 758 | [ 759 | 'type' => 'error', 760 | 'dismissible' => false, 761 | 'attributes' => [ 762 | 'data-slug' => 'wp-saml-auth', 763 | 'data-type' => 'simplesamlphp-critical-vulnerability', 764 | ], 765 | ] 766 | ); 767 | } elseif ( 'warning' === $simplesamlphp_version_status ) { 768 | $min_version = self::get_option( 'min_simplesamlphp_version' ); 769 | wp_admin_notice( 770 | sprintf( 771 | // Translators: 1 is the installed version of SimpleSAMLphp, 2 is the minimum version and 3 is the most secure version. 772 | __( 'Security Recommendation: The SimpleSAMLphp version used by the WP SAML Auth plugin (%1$s) is older than the recommended secure version. Please consider updating to version %2$s or later. Learn more.', 'wp-saml-auth' ), 773 | esc_html( $simplesamlphp_version ), 774 | esc_html( $min_version ), 775 | esc_url( admin_url( 'options-general.php?page=wp-saml-auth-settings' ) ) 776 | ), 777 | [ 778 | 'type' => 'warning', 779 | 'dismissible' => true, 780 | 'attributes' => [ 781 | 'data-slug' => 'wp-saml-auth', 782 | 'data-type' => 'simplesamlphp-version-warning', 783 | ], 784 | ] 785 | ); 786 | } 787 | } elseif ( 'unknown' === $simplesamlphp_version_status ) { 788 | // Only show this notice if we're on the settings page. 789 | if ( ! isset( $_GET['page'] ) || $_GET['page'] !== 'wp-saml-auth-settings' ) { 790 | return; 791 | } 792 | wp_admin_notice( 793 | sprintf( 794 | // Translators: 1 is the minimum recommended version of SimpleSAMLphp. 2 is a link to the WP SAML Auth settings page. 795 | __( 'Warning: WP SAML Auth was unable to determine your SimpleSAMLphp version. Please ensure you are using version %1$s or later for security. Learn more.', 'wp-saml-auth' ), 796 | esc_html( self::get_option( 'min_simplesamlphp_version' ) ), 797 | esc_url( admin_url( 'options-general.php?page=wp-saml-auth-settings' ) ) 798 | ), 799 | [ 800 | 'type' => 'warning', 801 | 'dismissible' => true, 802 | 'attributes' => [ 803 | 'data-slug' => 'wp-saml-auth', 804 | 'data-type' => 'simplesamlphp-version-unknown', 805 | ], 806 | ] 807 | ); 808 | } 809 | } 810 | 811 | /** 812 | * Loads Plugin translation files. 813 | * 814 | * @since 1.1.1 815 | */ 816 | public function load_textdomain() { 817 | load_plugin_textdomain( 'wp-saml-auth', false, dirname( plugin_basename( __FILE__ ), 2 ) . '/languages' ); 818 | } 819 | } 820 | --------------------------------------------------------------------------------