59034c01 by Jeff Balicki

sso

Signed-off-by: Jeff <jeff@gotenzing.com>
1 parent 4f81f235
Showing 176 changed files with 4861 additions and 0 deletions
1 <?php
2
3 namespace Wpo\Blocks;
4
5 // Prevent public access to this script
6 defined('ABSPATH') or die();
7
8 use \Wpo\Core\Script_Helpers;
9 use \Wpo\Services\Options_Service;
10
11 if (!class_exists('\Wpo\Blocks\Loader')) {
12
13 class Loader
14 {
15
16 public function __construct($app, $edition, $plugins_dir, $plugins_url, $load_front_end = true)
17 {
18
19 add_action('enqueue_block_editor_assets', function () use ($app, $edition, $plugins_dir, $plugins_url) {
20 $this->enqueue_editor_assets($app, $edition, $plugins_dir, $plugins_url);
21 });
22
23 if ($load_front_end) {
24 add_action('enqueue_block_assets', function () use ($app, $edition, $plugins_dir, $plugins_url) {
25 $this->enqueue_assets($app, $edition, $plugins_dir, $plugins_url);
26 });
27 }
28 }
29
30 /**
31 * Enqueues js / css assets that will only be loaded for the back end.
32 *
33 * @since 1.0.0
34 *
35 * @return void
36 */
37 private function enqueue_editor_assets($app, $edition, $plugins_dir, $plugins_url)
38 {
39 $editor_block_path = "/Blocks/dist/$app/editor-$edition.js";
40 $editor_block_asset_file = include($plugins_dir . "/Blocks/dist/$app/editor-$edition.asset.php");
41
42 // Enqueue the bundled block JS file
43 \wp_enqueue_script(
44 "wpo365-$app-$edition-editor",
45 $plugins_url . $editor_block_path,
46 $editor_block_asset_file['dependencies'],
47 $editor_block_asset_file['version']
48 );
49
50 \wp_add_inline_script("wpo365-$app-$edition-editor", 'window.wpo365 = window.wpo365 || {}; window.wpo365.blocks = ' . json_encode(array(
51 'nonce' => \wp_create_nonce('wp_rest'),
52 'apiUrl' => \trailingslashit($GLOBALS['WPO_CONFIG']['url_info']['wp_site_url']) . 'wp-json/wpo365/v1/graph',
53 )), 'before');
54
55 if ($app == 'aud') {
56 $audiences = Options_Service::get_global_list_var('audiences');
57 $auth_scenario = Options_Service::get_global_string_var('auth_scenario');
58 $keys = array();
59
60 foreach ($audiences as $index => $audience) {
61 $keys[$audience['key']] = $audience['title'];
62 }
63
64 \wp_add_inline_script("wpo365-$app-$edition-editor", 'window.wpo365 = window.wpo365 || {}; window.wpo365.blocks = ' . json_encode(array(
65 'nonce' => \wp_create_nonce('wp_rest'),
66 'apiUrl' => \trailingslashit($GLOBALS['WPO_CONFIG']['url_info']['wp_site_url']) . 'wp-json/wpo365/v1/graph',
67 )) . '; window.wpo365.aud = ' . \json_encode($keys) . ' ; window.wpo365.scenario = \'' . $auth_scenario . '\'', 'before');
68 }
69 }
70
71 /**
72 * Enqueues js / css assets that will be loaded for both front and back end.
73 *
74 * @since 1.0.0
75 *
76 * @return void
77 */
78 private function enqueue_assets($app, $edition, $plugins_dir, $plugins_url)
79 {
80 $app_block_path = "/Blocks/dist/$app/app-$edition.js";
81 $app_block_asset_file = include($plugins_dir . "/Blocks/dist/$app/app-$edition.asset.php");
82
83 if (is_singular()) {
84 $id = get_the_ID();
85 $block_type = $edition == 'basic' ? $app . 'Basic' : $app;
86
87 if (has_block('wpo365/' . \strtolower($block_type), $id)) {
88
89 $react_urls = Script_Helpers::get_react_urls();
90
91 \wp_enqueue_script('wpo365-unpkg-react', $react_urls['react_url']);
92 \wp_enqueue_script('wpo365-unpkg-react-dom', $react_urls['react_dom_url']);
93
94 \wp_enqueue_script(
95 "wpo365-$app-$edition-block",
96 $plugins_url . $app_block_path,
97 \array_merge($app_block_asset_file['dependencies'], array('wpo365-unpkg-react', 'wpo365-unpkg-react-dom')),
98 $app_block_asset_file['version'],
99 true
100 ); // Load in footer so the page has rendered and the block with the class can be found
101
102 \wp_add_inline_script("wpo365-$app-$edition-block", 'window.wpo365 = window.wpo365 || {}; window.wpo365.blocks = ' . json_encode(array(
103 'nonce' => \wp_create_nonce('wp_rest'),
104 'apiUrl' => \trailingslashit($GLOBALS['WPO_CONFIG']['url_info']['wp_site_url']) . 'wp-json/wpo365/v1/graph',
105 )), 'before');
106 }
107 }
108 }
109 }
110 }
1 <?php return array('dependencies' => array('wp-polyfill'), 'version' => '72b2bd50b39920906f5b6a272414a645');
...\ No newline at end of file ...\ No newline at end of file
This diff could not be displayed because it is too large.
1 <?php return array('dependencies' => array('wp-block-editor', 'wp-blocks', 'wp-components', 'wp-i18n', 'wp-polyfill'), 'version' => 'f3108bb05994449c9043cbfc999b93c4');
...\ No newline at end of file ...\ No newline at end of file
This diff could not be displayed because it is too large.
1 <?php
2
3 namespace Wpo\Core;
4
5 use \Wpo\Core\Wpmu_Helpers;
6 use \Wpo\Services\Log_Service;
7 use \Wpo\Services\Options_Service;
8
9 // Prevent public access to this script
10 defined('ABSPATH') or die();
11
12 if (!class_exists('\Wpo\Core\Compatibility_Helpers')) {
13
14 class Compatibility_Helpers
15 {
16 /**
17 * Writes the compatibility warning as an error to the log and remembers it for 24 hours
18 * to prevent flooding the log with the same error over and again.
19 *
20 * @since 20.0
21 *
22 * @param string $warning
23 *
24 * @return void
25 */
26 public static function compat_warning($warning)
27 {
28 $compat_warnings = Wpmu_Helpers::mu_get_transient('wpo365_compat_warnings');
29
30 if (empty($compat_warnings) || !is_array($compat_warnings) || !in_array($warning, $compat_warnings)) {
31 Log_Service::write_log('ERROR', $warning);
32
33 if (is_array($compat_warnings)) {
34 $compat_warnings[] = $warning;
35 } else {
36 $compat_warnings = array($warning);
37 }
38
39 // Transient shall block the repetition of this warning for 24 hours.
40 Wpmu_Helpers::mu_set_transient('wpo365_compat_warnings', $compat_warnings, 86400);
41 }
42 }
43
44 /**
45 * Reduces the key of the extra_user_fields array by removing the name part for custom
46 * WordPress usermeta that was introduced with version 20.
47 *
48 * @since 20.0
49 *
50 * @param array $extra_user_fields The array of extra user fields that will be updated
51 *
52 * @return void
53 */
54 public static function update_user_field_key($extra_user_fields)
55 {
56 if (!class_exists('\Wpo\Services\User_Details_Service') || method_exists('\Wpo\Services\User_Details_Service', 'parse_user_field_key')) {
57 return $extra_user_fields;
58 }
59
60 // Iterate over the configured graph fields and identify any supported expandable properties
61 $extra_user_fields = array_map(function ($kv_pair) {
62 $marker_pos = WordPress_Helpers::stripos($kv_pair['key'], ';#');
63
64 if ($marker_pos > 0) {
65 $kv_pair['key'] = substr($kv_pair['key'], 0, $marker_pos);
66 }
67
68 return $kv_pair;
69 }, $extra_user_fields);
70
71 $compat_warning = sprintf(
72 '%s -> The administrator configured <em>Azure AD user attributes to WordPress user meta mappings</em> on the plugin\'s <strong>User sync</strong> page. These mappings have been recently upgraded to allow administrators to specify their own name for the usermeta key. This new feature, however, breaks existing functionality. To remain compatible you should update your premium WPO365 extension and optionally update the existing mappings.',
73 __METHOD__
74 );
75
76 self::compat_warning($compat_warning);
77
78 return $extra_user_fields;
79 }
80 }
81 }
1 <?php
2
3 namespace Wpo\Core;
4
5 // Prevent public access to this script
6 defined( 'ABSPATH' ) or die();
7
8 use \Wpo\Core\Permissions_Helpers;
9 use \Wpo\Core\Config_Endpoints;
10 use \Wpo\Services\Log_Service;
11 use \Wpo\Services\Options_Service;
12
13 if( !class_exists( '\Wpo\Core\Config_Controller' ) ) {
14
15 class Config_Controller extends \WP_REST_Controller {
16
17 /**
18 * Register the routes for the objects of the controller.
19 */
20 public function register_routes() {
21
22 $version = '1';
23 $namespace = 'wpo365/v' . $version;
24
25 register_rest_route( $namespace, '/users/search/unique',
26 array(
27 array(
28 'methods' => \WP_REST_Server::CREATABLE,
29 'callback' => function ( $request ) {
30 return Config_Endpoints::users_search_unique( $request );
31 },
32 'permission_callback' => array( $this, 'check_permissions' ),
33 ),
34 )
35 );
36 }
37
38 /**
39 * Checks if the user can retrieve an access token for the requested scope.
40 *
41 * @param string $scope Scope for which the token must be valid.
42 * @return bool|WP_Error True if user can retrieve an access token for the requested scope otherwise a WP_Error is returned.
43 */
44 public function check_permissions( $request, $allow_application = false ) {
45
46 if ( ! wp_verify_nonce( $request->get_header( 'X-WP-Nonce' ), 'wp_rest' ) ) {
47 return new \WP_Error( 'UnauthorizedException', 'The request cannot be validated.', array( 'status' => 401 ) );
48 }
49
50 $wp_usr = \wp_get_current_user();
51
52 if ( empty( $wp_usr ) ) {
53 return new \WP_Error( 'UnauthorizedException', 'Please sign in first before using this API.', array( 'status' => 401 ) );
54 }
55
56 if ( ! Permissions_Helpers::user_is_admin( $wp_usr ) ) {
57 return new \WP_Error( 'UnauthorizedException', 'Please sign in with administrative credentials before using this API.', array( 'status' => 403 ) );
58 }
59
60 return true;
61 }
62 }
63 }
...\ No newline at end of file ...\ No newline at end of file
1 <?php
2
3 namespace Wpo\Core;
4
5 // Prevent public access to this script
6 defined( 'ABSPATH' ) or die();
7
8 use \Wpo\Services\Log_Service;
9 use \Wpo\Services\Options_Service;
10
11 if( !class_exists( '\Wpo\Core\Config_Endpoints' ) ) {
12
13 class Config_Endpoints {
14
15 /**
16 * Register the routes for the objects of the controller.
17 */
18 public static function users_search_unique( $rest_request ) {
19 $body = $rest_request->get_json_params();
20
21 if ( empty( $body ) || !\is_array( $body ) || empty( $body[ 'keyword' ] ) ) {
22 return new \WP_Error( 'InvalidArgumentException', 'Body is malformed JSON or the request header did not define the Content-type as application/json.', array( 'status' => 400 ) );
23 }
24
25 $users = new \WP_User_Query( array(
26 'count_total' => true,
27 'search' => '*' . esc_attr( $body[ 'keyword' ] ) . '*',
28 'search_columns' => array(
29 'user_login',
30 'user_nicename',
31 'user_email',
32 'user_url',
33 ),
34 ) );
35
36 if ( $users->get_total() != 1 ) {
37 return new \WP_Error( 'AmbigiousResultException', 'The query did not return exactly one unique user. Please update your input and try again.', array( 'status' => 404 ) );
38 }
39
40 $users_found = $users->get_results();
41
42 return array(
43 'ID' => $users_found[0]->ID,
44 'user_login' => $users_found[0]->user_login,
45 );
46 }
47 }
48 }
...\ No newline at end of file ...\ No newline at end of file
1 <?php
2
3 namespace Wpo\Core;
4
5 // Prevent public access to this script
6 defined( 'ABSPATH' ) or die( );
7
8 if ( !class_exists( '\Wpo\Core\Cron_Helpers' ) ) {
9
10 class Cron_Helpers {
11
12 /**
13 * Adds custom named cron schedules
14 *
15 * @since 10.0
16 *
17 * @param $schedules Array of already defined
18 */
19 public static function add_cron_schedules( $schedules ) {
20 $schedules[ 'wpo_five_minutes' ] = array(
21 'interval' => 300,
22 'display' => __( 'Every 5 minutes', 'wpo365-login' )
23 );
24
25 $schedules[ 'wpo_daily' ] = array(
26 'interval' => 86400,
27 'display' => __( 'WPO365 Daily', 'wpo365-login' )
28 );
29
30 $schedules[ 'wpo_weekly' ] = array(
31 'interval' => 604800,
32 'display' => __( 'WPO365 Weekly', 'wpo365-login' )
33 );
34
35 return $schedules;
36 }
37 }
38 }
...\ No newline at end of file ...\ No newline at end of file
1 <?php
2
3 namespace Wpo\Core;
4
5 use \Wpo\Services\Log_Service;
6 use \Wpo\Services\Options_Service;
7
8 // Prevent public access to this script
9 defined('ABSPATH') or die();
10
11 if (!class_exists('\Wpo\Core\Domain_Helpers')) {
12
13 class Domain_Helpers
14 {
15
16 /**
17 * Gets the domain (host) part of an email address.
18 *
19 * @since 3.1
20 *
21 * @param string $email_address email address to analyze
22 * @return string Returns the email address' host part or an empty string if
23 * the email address appears to be invalid
24 */
25 public static function get_smtp_domain_from_email_address($email_address)
26 {
27 $smpt_domain = '';
28
29 if (filter_var(trim($email_address), FILTER_VALIDATE_EMAIL) !== false) {
30 $smpt_domain = strtolower(trim(substr($email_address, strrpos($email_address, '@') + 1)));
31 }
32
33 return $smpt_domain;
34 }
35
36 /**
37 * Checks a user's smtp domain against the configured custom and default domains
38 *
39 * @since 4.0
40 *
41 * @return boolean true if a match is found otherwise false
42 */
43 public static function is_tenant_domain($email_domain)
44 {
45 $custom_domain = array_change_key_case(array_flip(Options_Service::get_global_list_var('custom_domain')));
46 $default_domain = Options_Service::get_global_string_var('default_domain');
47
48 if (array_key_exists($email_domain, $custom_domain) || strtolower(trim($default_domain)) == $email_domain) {
49 return true;
50 }
51
52 return false;
53 }
54 }
55 }
1 <?php
2
3 namespace Wpo\Core;
4
5 use \Wpo\Core\Url_Helpers;
6 use \Wpo\Core\WordPress_Helpers;
7
8 // Prevent public access to this script
9 defined('ABSPATH') or die();
10
11 if (!class_exists('\Wpo\Core\Globals')) {
12
13 class Globals
14 {
15
16 public static function set_global_vars(
17 $plugin_file,
18 $plugin_dir
19 ) {
20
21 if (false === function_exists('get_plugin_data')) {
22 require_once ABSPATH . 'wp-admin/includes/plugin.php';
23 }
24
25 $plugin_data = \get_plugin_data($plugin_file);
26
27 $base_name = plugin_basename($plugin_file);
28
29 $GLOBALS['WPO_CONFIG'] = array(
30 'options' => array(),
31 'plugin' => $base_name,
32 'plugin_dir' => $plugin_dir,
33 'plugin_url' => plugin_dir_url($plugin_file),
34 'plugin_file' => $plugin_file,
35 'extension_file' => '',
36 'extensions' => array(),
37 'slug' => substr($base_name, 0, WordPress_Helpers::stripos($base_name, '/')),
38 'store' => 'https://www.wpo365.com',
39 'store_item' => '',
40 'store_item_id' => '',
41 'request_id' => uniqid('', true),
42 'url_info' => self::get_url_info(),
43 'version' => $plugin_data['Version'],
44 'ina' => (array_key_exists('ina', $_POST) && true === filter_var($_POST['ina'], FILTER_VALIDATE_BOOLEAN)),
45 );
46 }
47
48 /**
49 * Sets a number of URL related globals (all normalized and not ending with a trailing space).
50 * Whether or not to force SSL is determined by the user override (option) use_ssl. If this
51 * option hast been configured, the plugin will assume the same protocol as used for the
52 * redirect url. If the redirect utl hasn't been configured yet, the plugin will assume the
53 * same protocol as used for the home url.
54 *
55 * @since 1.0
56 *
57 * @return array
58 */
59 public static function get_url_info()
60 {
61 $home = get_option('home');
62 $scheme = (isset($_SERVER['HTTPS']) && ($_SERVER['HTTPS'] == 'on' || $_SERVER['HTTPS'] == 1)) ||
63 (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https')
64 ? 'https'
65 : 'http';
66
67 /**
68 * @since 12.10 Deal with reverse proxies
69 */
70 if (WordPress_Helpers::stripos($home, 'http://') === 0 && $scheme == 'https') {
71 $home = preg_replace("/^http:/i", "https:", $home);
72 }
73
74 $request_uri = Url_Helpers::ensure_trailing_slash_path($_SERVER['REQUEST_URI']);
75 $home_path = Url_Helpers::ensure_trailing_slash_path(parse_url($home, PHP_URL_PATH));
76 $host = parse_url($home, PHP_URL_HOST);
77 $current_url = $scheme . '://' . $host . $request_uri;
78
79 return array(
80 'request_uri' => $request_uri,
81 'wp_site_url' => Url_Helpers::ensure_trailing_slash_url($home),
82 'wp_site_path' => $home_path,
83 'current_url' => $current_url,
84 'host' => $host,
85 );
86 }
87 }
88 }
1 <?php
2
3 namespace Wpo\Core;
4
5 use \Wpo\Core\WordPress_Helpers;
6 use \Wpo\Services\Log_Service;
7 use \Wpo\Services\Options_Service;
8 use \Wpo\Services\User_Service;
9
10 // Prevent public access to this script
11 defined('ABSPATH') or die();
12
13 if (!class_exists('\Wpo\Core\Permissions_Helpers')) {
14
15 class Permissions_Helpers
16 {
17
18 /**
19 * @since 7.12
20 */
21 public static function user_is_admin($user)
22 {
23
24 if ($user instanceof \WP_User) {
25 return \in_array('administrator', $user->roles) || is_super_admin($user->ID);
26 }
27
28 return false;
29 }
30
31 /**
32 * Returns true when a user is allowed to change the password
33 *
34 * @since 1.0
35 * @return void
36 *
37 * @return boolean true when a user is allowed to change the password otherwise false
38 */
39 public static function show_password_fields($show, $user)
40 {
41
42 return !self::block_password_update($user->ID);
43 }
44
45 /**
46 * Returns true when a user is allowed to change the password
47 *
48 * @since 1.5
49 *
50 * @param boolean $allow whether allowed or not
51 * @param int $user_id id of the user for which the action is triggered
52 *
53 * @return boolean true when a user is allowed to change the password otherwise false
54 */
55 public static function allow_password_reset($allow, $user_id)
56 {
57 return !self::block_password_update($user_id);
58 }
59
60 /**
61 * Helper method to determin whether a user is allowed to change the password
62 *
63 * @since 1.5
64 *
65 * @param int $user_id id of the user for which the action is triggered
66 *
67 * @return boolean true when a user is not allowed to change the password otherwise false
68 */
69 private static function block_password_update($user_id)
70 {
71 $block_password_change = Options_Service::get_global_boolean_var('block_password_change');
72
73 // Not configured or not blocked
74 if (false === $block_password_change) { // user is not logged on
75 Log_Service::write_log('DEBUG', __METHOD__ . ' -> Not blocking password update');
76 return false;
77 }
78
79 $wp_usr = get_user_by('ID', intval($user_id));
80
81 // Limit the blocking of password update only for O365 users
82 return User_Service::user_is_o365_user($user_id) === User_Service::IS_O365_USER && !self::user_is_admin($wp_usr) ? true : false;
83 }
84
85 /**
86 * Prevents users who cannot create new users to change their email address
87 *
88 * @since 1.0
89 * @param array errors => Existing errors ( from Wordpress )
90 * @param bool update => true when updating an existing user otherwise false
91 * @param WPUser usr_new => Updated user
92 * @return void
93 */
94 public static function prevent_email_change($user_id)
95 {
96
97 // Don't block as per global settings configuration
98 if (
99 false === Options_Service::get_global_boolean_var('block_email_change')
100 || User_Service::user_is_o365_user($user_id) !== User_Service::IS_O365_USER
101 ) {
102 return;
103 }
104
105 $usr_old = get_user_by('ID', intval($user_id));
106
107 if ($usr_old === false) {
108 return;
109 }
110
111 // At this point the user is an O365 user and email change should be blocked as per config
112 if (isset($_POST['email']) && $_POST['email'] != $usr_old->user_email) {
113
114 // Prevent update
115 $_POST['email'] = $usr_old->user_email;
116
117 add_action('user_profile_update_errors', function ($errors) {
118 $errors->add('email_update_error', __('Updating your email address is currently not allowed', 'wpo365-login'));
119 });
120 }
121 }
122
123 /**
124 * Quick check whether the requested scope e.g. api.yammer.com requires delegated access.
125 *
126 * @since 17.0
127 *
128 * @param string $scope The scope the requested access must be valid for.
129 * @return boolean True if delegated access is required for the scope provide.
130 */
131 public static function must_use_delegate_access_for_scope($scope)
132 {
133 return (false !== WordPress_Helpers::stripos($scope, 'api.yammer.com') ||
134 false !== WordPress_Helpers::stripos($scope, '.sharepoint.com') ||
135 (false === WordPress_Helpers::stripos($scope, 'user.read.all') && false !== WordPress_Helpers::stripos($scope, 'user.read'))
136 );
137 }
138 }
139 }
1 <?php
2
3 namespace Wpo\Core;
4
5 use \Wpo\Core\Extensions_Helpers;
6 use \Wpo\Services\Options_Service;
7
8 // Prevent public access to this script
9 defined( 'ABSPATH' ) or die();
10
11 if ( !class_exists( '\Wpo\Core\Plugin_Helpers' ) ) {
12
13 class Plugin_Helpers {
14
15 /**
16 * Helper to check if a premium WPO365 plugin edition is active.
17 */
18 public static function is_premium_edition_active( $slug = null ) {
19
20 if ( empty( $slug ) ) {
21 $extensions = Extensions_Helpers::get_extensions();
22
23 foreach ( $extensions as $slug => $extension ) {
24
25 if ( true === $extension[ 'activated' ] ) {
26 return true;
27 }
28 }
29
30 return false;
31 }
32
33 if ( false === function_exists( 'is_plugin_active' ) ) {
34 require_once ABSPATH . 'wp-admin/includes/plugin.php';
35 }
36
37 return \is_plugin_active( $slug );
38 }
39
40 /**
41 * WPMU aware wp filter extension to show the action link on the plugins page. Will add
42 * the wpo365 configuration action link depending on the WPMU configuration
43 *
44 * @since 7.3
45 *
46 * @param Array $links The current action link collection
47 *
48 * @return Array The new action link collection
49 */
50 public static function get_configuration_action_link( $links ) {
51 // Don't show the configuration link for subsite admin if subsite options shouldn't be used
52 if ( is_multisite() && !is_network_admin() && false === Options_Service::mu_use_subsite_options() )
53 return $links;
54
55 $wizard_link = '<a href="admin.php?page=wpo365-wizard">' . __( 'Configuration', 'wpo365-login' ) . '</a>';
56 array_push( $links, $wizard_link );
57
58 return $links;
59 }
60 }
61 }
1 <?php
2
3 namespace Wpo\Core;
4
5 // Prevent public access to this script
6 defined( 'ABSPATH' ) or die();
7
8 if ( !class_exists( '\Wpo\Core\Request' ) ) {
9
10 class Request {
11
12 private $id;
13
14 private $storage = array();
15
16 public function __construct( $id ) {
17 $this->id = $id;
18 }
19
20 public function current_request_id() {
21 return $this->id;
22 }
23
24 public function set_item( $key, $value ) {
25
26 if ( !is_string( $key ) ) {
27 return false;
28 }
29
30 if ( empty( $key ) ) {
31 return false;
32 }
33
34 $this->storage[ $key ] = $value;
35
36 return true;
37 }
38
39 public function get_item( $key ) {
40
41 if ( !is_string( $key ) || empty( $key ) ) {
42 return false;
43 }
44
45 if ( array_key_exists( $key, $this->storage ) ) {
46 return $this->storage[ $key ];
47 }
48
49 return false;
50 }
51
52 public function remove_item( $key ) {
53
54 if ( !is_string( $key ) || empty( $key ) ) {
55 return false;
56 }
57
58 if ( array_key_exists( $key, $this->storage ) ) {
59 unset( $this->storage[ $key ] );
60 return true;
61 }
62
63 return false;
64 }
65
66 public function clear() {
67 $this->storage = array();
68 }
69 }
70 }
1 <?php
2
3 namespace Wpo\Core;
4
5 use \Wpo\Core\WordPress_Helpers;
6 use \Wpo\Services\Options_Service;
7
8 // Prevent public access to this script
9 defined('ABSPATH') or die();
10
11 if (!class_exists('\Wpo\Core\Script_Helpers')) {
12
13 class Script_Helpers
14 {
15
16 /**
17 * Helper to enqueue the pintra redirect script.
18 *
19 * @since 8.6
20 *
21 * @since 15.4 Added inline script to globally define isWpLogin as true if WP login is detected.
22 *
23 * @return void
24 */
25 public static function enqueue_pintra_redirect()
26 {
27 if (Options_Service::get_global_boolean_var('use_no_teams_sso')) {
28 wp_enqueue_script('pintraredirectjs', trailingslashit($GLOBALS['WPO_CONFIG']['plugin_url']) . 'apps/dist/pintra-redirect-wo-teams.js', array(), $GLOBALS['WPO_CONFIG']['version'], false);
29 } else if (Options_Service::get_global_boolean_var('use_ms_teams_sso_v1')) {
30 wp_enqueue_script('pintraredirectjs', trailingslashit($GLOBALS['WPO_CONFIG']['plugin_url']) . 'apps/dist/pintra-redirect-v1.js', array(), $GLOBALS['WPO_CONFIG']['version'], false);
31 } else {
32 wp_enqueue_script('pintraredirectjs', trailingslashit($GLOBALS['WPO_CONFIG']['plugin_url']) . 'apps/dist/pintra-redirect.js', array(), $GLOBALS['WPO_CONFIG']['version'], false);
33 }
34
35 if (class_exists('\Wpo\Core\Url_Helpers') && \Wpo\Core\Url_Helpers::is_wp_login()) {
36 \wp_add_inline_script('pintraredirectjs', 'window.wpo365 = window.wpo365 || {}; window.wpo365.isWpLogin = true;', 'before');
37 }
38 }
39
40 /**
41 * Helper to enqueue the wizard script.
42 *
43 * @since 8.6
44 *
45 * @return void
46 */
47 public static function enqueue_wizard()
48 {
49
50 if (!(is_admin() || is_network_admin()) || !isset($_REQUEST['page']) || WordPress_Helpers::stripos($_REQUEST['page'], 'wpo365-wizard') === false) {
51 return;
52 }
53
54 global $wp_roles;
55
56 $extensions = array();
57
58 // Bundles
59 if (class_exists('\Wpo\Premium')) $extensions[] = 'wpo365LoginPremium';
60 if (class_exists('\Wpo\Intranet')) $extensions[] = 'wpo365LoginIntranet';
61
62 // Extensions
63 if (class_exists('\Wpo\Apps')) $extensions[] = 'wpo365Apps';
64 if (class_exists('\Wpo\Avatar')) $extensions[] = 'wpo365Avatar';
65 if (class_exists('\Wpo\Custom_Fields')) $extensions[] = 'wpo365CustomFields';
66 if (class_exists('\Wpo\Groups')) $extensions[] = 'wpo365Groups';
67 if (class_exists('\Wpo\Plus')) $extensions[] = 'wpo365LoginPlus';
68 if (class_exists('\Wpo\Professional')) $extensions[] = 'wpo365LoginProfessional';
69 if (class_exists('\Wpo\Mail')) $extensions[] = 'wpo365Mail';
70 if (class_exists('\Wpo\Roles_Access')) $extensions[] = 'wpo365RolesAccess';
71 if (class_exists('\Wpo\Scim')) $extensions[] = 'wpo365Scim';
72
73 // Plugins
74 if (class_exists('\Wpo\Login')) $extensions[] = 'wpo365Login';
75 if (class_exists('\Wpo\MsGraphMailer')) $extensions[] = 'wpo365MsGraphMailer';
76 if (!empty(get_option('mail_integration_365_plugin_ops'))) $extensions[] = 'mailIntegration';
77
78 $itthinx_groups = class_exists('\Wpo\Services\Mapped_Itthinx_Groups_Service') ? \Wpo\Services\Mapped_Itthinx_Groups_Service::get_groups_groups() : array();
79 $post_types = get_post_types();
80
81 $props = array(
82 'adminUrl' => get_site_url(null, '/wp-admin'),
83 'availableGroups' => json_encode($itthinx_groups),
84 'availablePostTypes' => json_encode($post_types),
85 'availableRoles' => json_encode($wp_roles->roles),
86 'extensions' => $extensions,
87 'nonce' => wp_create_nonce('wpo365_fx_nonce'),
88 'restNonce' => wp_create_nonce('wp_rest'),
89 'siteUrl' => get_home_url(),
90 'wpmu' => is_multisite() ? (defined('WPO_MU_USE_SUBSITE_OPTIONS') && true === constant('WPO_MU_USE_SUBSITE_OPTIONS') ? 'wpmuDedicated' : 'wpmuShared') : 'wpmuNone',
91 'ina' => is_network_admin(),
92 );
93
94 wp_enqueue_script('wizardjs', trailingslashit($GLOBALS['WPO_CONFIG']['plugin_url']) . 'apps/dist/wizard.js', array(), $GLOBALS['WPO_CONFIG']['version'], true);
95
96 \wp_add_inline_script('wizardjs', 'window.wpo365 = window.wpo365 || {}; window.wpo365.wizard = ' . json_encode(array(
97 'nonce' => wp_create_nonce('wpo365_fx_nonce'),
98 'wpAjaxAdminUrl' => admin_url() . 'admin-ajax.php',
99 'props' => $props,
100 )) . '; window.wpo365.blocks = ' . json_encode(array(
101 'nonce' => \wp_create_nonce('wp_rest'),
102 'apiUrl' => \trailingslashit($GLOBALS['WPO_CONFIG']['url_info']['wp_site_url']) . 'wp-json/wpo365/v1/graph',
103 )), 'before');
104 }
105
106 /**
107 * Helper to load the pintraredirectjs script asynchronously.
108 *
109 * @since 18.0
110 *
111 * @param mixed $tag
112 * @param mixed $handle
113 * @param mixed $src
114 * @return mixed
115 */
116 public static function enqueue_script_asynchronously($tag, $handle, $src)
117 {
118 if ($handle === 'pintraredirectjs') {
119 $tag = str_replace('></script>', ' async></script>', $tag);
120 }
121
122 return $tag;
123 }
124
125 /**
126 * Helper to allow administrators to switch the CDN where to load the react/react-dom dependencies from.
127 * This option was added after unpkg.com was poorly available on 28 Oct. 2022.
128 *
129 * @since 20.2
130 *
131 * @return array Returns an assoc array with react_url and react_dom_url.
132 */
133 public static function get_react_urls()
134 {
135
136 if (Options_Service::get_global_boolean_var('use_alternative_cdn')) {
137
138 // Administrator can define his / her own URLs e.g. when self-hosting the react files
139 if (defined('WPO_CDN') && is_array(constant('WPO_CDN'))) {
140 $react_url = !empty(constant('WPO_CDN')['react']) ? constant('WPO_CDN')['react'] : '';
141 $react_dom_url = !empty(constant('WPO_CDN')['react_dom']) ? constant('WPO_CDN')['react_dom'] : '';
142 }
143
144 // If not self-hosted then we take the react.js file from cdnjs.cloudflare.com instead
145 if (empty($react_url) || !filter_var($react_url, FILTER_VALIDATE_URL)) {
146 $react_url = 'https://cdnjs.cloudflare.com/ajax/libs/react/16.14.0/umd/react.production.min.js';
147 }
148
149 // If not self-hosted then we take the react-dom.js file from cdnjs.cloudflare.com instead
150 if (empty($react_dom_url) || !filter_var($react_dom_url, FILTER_VALIDATE_URL)) {
151 $react_dom_url = 'https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.14.0/umd/react-dom.production.min.js';
152 }
153 }
154
155 // If the administrator did not configure the use of an alternative CDN we take the react.js file from unpkg.com
156 if (empty($react_url) || !filter_var($react_url, FILTER_VALIDATE_URL)) {
157 $react_url = 'https://unpkg.com/react@16/umd/react.production.min.js';
158 }
159
160 // If the administrator did not configure the use of an alternative CDN we take the react-dom.js file from unpkg.com
161 if (empty($react_dom_url) || !filter_var($react_dom_url, FILTER_VALIDATE_URL)) {
162 $react_dom_url = 'https://unpkg.com/react-dom@16/umd/react-dom.production.min.js';
163 }
164
165 return array(
166 'react_url' => $react_url,
167 'react_dom_url' => $react_dom_url
168 );
169 }
170
171 /**
172 * Helper to add admin styles.
173 *
174 * @since 21.8
175 *
176 * @param string $css
177 * @return void
178 */
179 public static function add_admin_bar_styles($css)
180 {
181 wp_enqueue_style('wpo365-admin-bar-styles', plugins_url('/css/wpo365-admin-bar.css', dirname(__FILE__)));
182 }
183 }
184 }
1 <?php
2
3 namespace Wpo\Core;
4
5 use \Wpo\Core\Extensions_Helpers;
6 use \Wpo\Core\WordPress_Helpers;
7 use \Wpo\Services\Options_Service;
8 use \Wpo\Services\Error_Service;
9
10 // Prevent public access to this script
11 defined('ABSPATH') or die();
12
13 if (!class_exists('\Wpo\Core\Shortcode_Helpers')) {
14
15 class Shortcode_Helpers
16 {
17
18 /**
19 * Helper method to ensure that short codes are initialized
20 *
21 * @since 7.0
22 *
23 * @return void
24 */
25 public static function ensure_pintra_short_code()
26 {
27
28 if (!shortcode_exists('pintra')) {
29 add_shortcode('pintra', '\Wpo\Core\Shortcode_Helpers::add_pintra_shortcode');
30 }
31 }
32
33 /**
34 * Adds a pintra app launcher into the page
35 *
36 * @since 5.0
37 *
38 * @param array short code parameters according to Wordpress codex
39 * @param string content found in between the short code start and end tag
40 * @param string text domain
41 */
42 public static function add_pintra_shortcode($atts = array(), $content = null, $tag = '')
43 {
44 $atts = array_change_key_case((array)$atts, CASE_LOWER);
45 $props = '[]';
46
47 if (
48 isset($atts['props'])
49 && strlen(trim($atts['props'])) > 0
50 ) {
51 $result = array();
52 $props = html_entity_decode($atts['props']);
53 $prop_kv_pairs = explode(';', $props);
54
55 foreach ($prop_kv_pairs as $prop_kv_pair) {
56 $first_separator = WordPress_Helpers::stripos($prop_kv_pair, ',');
57
58 if (false === $first_separator) {
59 continue;
60 }
61
62 $result[\substr($prop_kv_pair, 0, $first_separator)] = \substr($prop_kv_pair, $first_separator + 1);
63 }
64
65 $props = json_encode($result);
66 }
67
68 $script_url = isset($atts['script_url']) ? html_entity_decode($atts['script_url']) : '';
69
70 ob_start();
71 include($GLOBALS['WPO_CONFIG']['plugin_dir'] . '/templates/pintra.php');
72 $content = ob_get_clean();
73 return wp_kses($content, WordPress_Helpers::get_allowed_html());
74 }
75
76 /**
77 * Helper method to ensure that short codes are initialized
78 *
79 * @since 8.0
80 *
81 * @return void
82 */
83 public static function ensure_login_button_short_code_V2()
84 {
85
86 if ((class_exists('\Wpo\Premium') || class_exists('\Wpo\Intranet')) && !shortcode_exists('wpo365-sign-in-with-microsoft-v2-sc')) {
87 add_shortcode('wpo365-sign-in-with-microsoft-v2-sc', '\Wpo\Core\Shortcode_Helpers::add_sign_in_with_microsoft_shortcode_V2');
88 }
89 }
90
91 /**
92 * Adds the Sign in with Microsoft short code V2
93 *
94 * @since 8.0
95 *
96 * @param array short code parameters according to Wordpress codex
97 * @param string content found in between the short code start and end tag
98 * @param string text domain
99 */
100 public static function add_sign_in_with_microsoft_shortcode_V2($params = array(), $content = null, $tag = '')
101 {
102
103 if (empty($content)) {
104 return $content;
105 }
106
107 $site_url = $GLOBALS['WPO_CONFIG']['url_info']['wp_site_url'];
108
109 // Load the js dependency
110 ob_start();
111 include(Extensions_Helpers::get_active_extension_dir(array('wpo365-login-premium/wpo365-login.php', 'wpo365-login-intranet/wpo365-login.php')) . '/templates/openid-ssolink.php');
112 $js_lib = ob_get_clean();
113
114 // Sanitize the HTML template
115 $dom = new \DOMDocument();
116 @$dom->loadHTML($content);
117 $script = $dom->getElementsByTagName('script');
118 $remove = array();
119
120 foreach ($script as $item)
121 $remove[] = $item;
122
123 foreach ($remove as $item)
124 $item->parentNode->removeChild($item);
125
126 // Concatenate the two
127 $output = $js_lib . $dom->saveHTML();
128 return str_replace("__##PLUGIN_BASE_URL##__", $GLOBALS['WPO_CONFIG']['plugin_url'], $output);
129 }
130
131 /**
132 * Helper method to ensure that short code for login button is initialized
133 *
134 * @since 11.0
135 */
136 public static function ensure_login_button_short_code()
137 {
138
139 if (!shortcode_exists('wpo365-login-button')) {
140 add_shortcode('wpo365-login-button', '\Wpo\Core\Shortcode_Helpers::login_button');
141 }
142 }
143
144 /**
145 * Helper to display the Sign in with Microsoft button on a login form.
146 *
147 * @since 10.6
148 *
149 * @param
150 *
151 * @return void
152 */
153 public static function login_button()
154 {
155 // Don't render a login button when sso is disabled
156 if (Options_Service::get_global_boolean_var('no_sso')) {
157 return;
158 }
159
160 // Used by the template that is rendered
161 $hide_login_button = Options_Service::get_global_boolean_var('hide_login_button');
162
163 $sign_in_with_microsoft = Options_Service::get_global_string_var('sign_in_with_microsoft');
164
165 if (empty($sign_in_with_microsoft) || $sign_in_with_microsoft == 'Sign in with Microsoft') {
166 $sign_in_with_microsoft = __('Sign in with Microsoft', 'wpo365-login');
167 }
168
169 ob_start();
170 include($GLOBALS['WPO_CONFIG']['plugin_dir'] . '/templates/login-button.php');
171 $content = ob_get_clean();
172 echo wp_kses($content, WordPress_Helpers::get_allowed_html());
173 }
174
175 /**
176 * Helper method to ensure that short code for displaying errors is initialized
177 *
178 * @since 7.8
179 */
180 public static function ensure_display_error_message_short_code()
181 {
182
183 if ((class_exists('\Wpo\Professional') || class_exists('\Wpo\Premium') || class_exists('\Wpo\Intranet')) && !shortcode_exists('wpo365-display-error-message-sc'))
184 add_shortcode('wpo365-display-error-message-sc', '\Wpo\Core\Shortcode_Helpers::add_display_error_message_shortcode');
185 }
186
187 /**
188 * Adds the error message encapsulated in a div into the page
189 *
190 * @since 7.8
191 *
192 * @param array short code parameters according to Wordpress codex
193 * @param string content found in between the short code start and end tag
194 * @param string text domain
195 */
196 public static function add_display_error_message_shortcode($atts = array(), $content = null, $tag = '')
197 {
198 $error_code = isset($_GET['login_errors'])
199 ? sanitize_text_field($_GET['login_errors'])
200 : '';
201
202 $error_message = Error_Service::get_error_message($error_code);
203
204 if (empty($error_message)) {
205 return;
206 }
207
208 ob_start();
209 include(Extensions_Helpers::get_active_extension_dir(array('wpo365-login-professional/wpo365-login.php', 'wpo365-login-premium/wpo365-login.php', 'wpo365-login-intranet/wpo365-login.php')) . '/templates/error-message.php');
210 $content = ob_get_clean();
211 return wp_kses($content, WordPress_Helpers::get_allowed_html());
212 }
213 }
214 }
1 <?php
2
3 namespace Wpo\Core;
4
5 // prevent public access to this script
6 defined('ABSPATH') or die();
7
8 use \Wpo\Services\Log_Service;
9
10 if (!class_exists('\Wpo\Core\User')) {
11
12 class User
13 {
14
15 /**
16 * Email address of user
17 *
18 * @since 1.0.0
19 *
20 * @var string
21 */
22 public $email = '';
23
24 /**
25 * Unique user's principal name
26 *
27 * @since 1.0.0
28 *
29 * @var string
30 */
31 public $upn = '';
32
33 /**
34 * User's preferred name
35 *
36 * @since 1.0.0
37 *
38 * @var string
39 */
40 public $preferred_username = '';
41
42 /**
43 * Name of user
44 *
45 * @since 1.0.0
46 *
47 * @var string
48 */
49 public $name = '';
50
51 /**
52 * User's first name
53 *
54 * @since 1.0.0
55 *
56 * @var string
57 */
58 public $first_name = '';
59
60 /**
61 * User's last name incl. middle name etc.
62 *
63 * @since 1.0.0
64 *
65 * @var string
66 */
67 public $last_name = '';
68
69 /**
70 * User's full ( or display ) name
71 *
72 * @since 1.0.0
73 *
74 * @var string
75 */
76 public $full_name = '';
77
78 /**
79 * Office 365 and/or Azure AD group ids
80 */
81 public $groups = array();
82
83 /**
84 * User's tenant ID
85 */
86 public $tid = '';
87
88 /**
89 * User's Azure AD object ID
90 */
91 public $oid = '';
92
93 /**
94 * True is the user was created during the current script execution
95 */
96 public $created = false;
97
98 /**
99 * True is the user was created from an ID Token / SAML response
100 */
101 public $from_idp_token = false;
102
103 /**
104 * The Graph Resource for this user
105 */
106 public $graph_resource = null;
107
108 /**
109 * The SAML attributes for this user
110 */
111 public $saml_attributes = array();
112 }
113 }
1 <?php
2
3 namespace Wpo\Core;
4
5 // prevent public access to this script
6 defined('ABSPATH') or die();
7
8 if (!class_exists('\Wpo\Core\Version')) {
9
10 class Version
11 {
12 public static $current = '23.1';
13 }
14 }
1 <?php
2
3 namespace Wpo\Core;
4
5 use Wpo\Services\Options_Service;
6
7 // Prevent public access to this script
8 defined('ABSPATH') or die();
9
10 if (!class_exists('\Wpo\Core\WordPress_Helpers')) {
11
12 class WordPress_Helpers
13 {
14 /**
15 * PHP 8.1 safe version of PHP's trim.
16 *
17 * @param mixed $str
18 * @param string $charlist
19 * @return string
20 */
21 public static function trim($str, $charlist = " \n\r\t\v\x00")
22 {
23 $str = null === $str ? '' : $str;
24 return trim($str, $charlist);
25 }
26
27 /**
28 * PHP 8.1 safe version of PHP's ltrim.
29 *
30 * @param mixed $str
31 * @param string $charlist
32 * @return string
33 */
34 public static function ltrim($str, $charlist = " \n\r\t\v\x00")
35 {
36 $str = null === $str ? '' : $str;
37 return ltrim($str, $charlist);
38 }
39
40 /**
41 * PHP 8.1 safe version of PHP's rtrim.
42 *
43 * @param mixed $str
44 * @param string $charlist
45 * @return string
46 */
47 public static function rtrim($str, $charlist = " \n\r\t\v\x00")
48 {
49 $str = null === $str ? '' : $str;
50 return rtrim($str, $charlist);
51 }
52
53 /**
54 * PHP 8.1 safe version of PHP's stripos.
55 *
56 * @param mixed $haystack
57 * @param mixed $needle
58 * @param mixed $offset
59 * @return int|false
60 */
61 public static function stripos($haystack, $needle, $offset = 0)
62 {
63 $haystack = null === $haystack ? '' : $haystack;
64 $needle = null === $needle ? '' : $needle;
65
66 return stripos($haystack, $needle, $offset);
67 }
68
69 /**
70 * PHP 8.1 safe version of PHP's strpos.
71 *
72 * @param mixed $haystack
73 * @param mixed $needle
74 * @param mixed $offset
75 * @return int|false
76 */
77 public static function strpos($haystack, $needle, $offset = 0)
78 {
79 $haystack = null === $haystack ? '' : $haystack;
80 $needle = null === $needle ? '' : $needle;
81
82 return strpos($haystack, $needle, $offset);
83 }
84
85 /**
86 * Helper to base64 URL decode.
87 *
88 * @param string Input to be decoded.
89 *
90 * @return string Input decoded
91 */
92 public static function base64_url_decode($arg)
93 {
94 $res = $arg;
95 $res = str_replace('-', '+', $res);
96 $res = str_replace('_', '/', $res);
97
98 switch (strlen($res) % 4) {
99 case 0:
100 break;
101 case 2:
102 $res .= "==";
103 break;
104 case 3:
105 $res .= "=";
106 break;
107 default:
108 break;
109 }
110
111 $res = base64_decode($res);
112 return $res;
113 }
114
115
116 /**
117 * Helper to base64 URL encode.
118 *
119 * @param mixed $arg
120 * @return string
121 */
122 public static function base64_url_encode($arg)
123 {
124 return strtr(
125 base64_encode($arg),
126 [
127 '=' => '',
128 '+' => '-',
129 '/' => '_',
130 ]
131 );
132 }
133
134 /**
135 * Helper for wp_kses to define the allowed HTML element names, attribute names, attribute
136 * values, and HTML entities
137 *
138 * @return mixed
139 */
140 public static function get_allowed_html()
141 {
142 global $allowedposttags;
143
144 $allowed_atts = array(
145 'action' => array(),
146 'align' => array(),
147 'alt' => array(),
148 'class' => array(),
149 'data-nonce' => array(),
150 'data-props' => array(),
151 'data-wpajaxadminurl' => array(),
152 'data' => array(),
153 'dir' => array(),
154 'fill' => array(),
155 'for' => array(),
156 'height' => array(),
157 'href' => array(),
158 'html' => array(),
159 'id' => array(),
160 'lang' => array(),
161 'method' => array(),
162 'name' => array(),
163 'novalidate' => array(),
164 'onClick' => array(),
165 'onclick' => array(),
166 'rel' => array(),
167 'rev' => array(),
168 'src' => array(),
169 'style' => array(),
170 'tabindex' => array(),
171 'target' => array(),
172 'title' => array(),
173 'type' => array(),
174 'type' => array(),
175 'value' => array(),
176 'viewBox' => array(),
177 'width' => array(),
178 'x' => array(),
179 'xml:lang' => array(),
180 'xmlns' => array(),
181 'y' => array(),
182 );
183
184 // Add custom tags
185 $allowed_tags = array('script' => $allowed_atts);
186 $allowed_tags['!DOCTYPE'] = $allowed_atts;
187 $allowed_tags['body'] = $allowed_atts;
188 $allowed_tags['head'] = $allowed_atts;
189 $allowed_tags['html'] = $allowed_atts;
190 $allowed_tags['rect'] = $allowed_atts;
191 $allowed_tags['style'] = $allowed_atts;
192 $allowed_tags['svg'] = $allowed_atts;
193 $allowed_tags['title'] = $allowed_atts;
194 $allowed_tags['button'] = $allowed_atts;
195 $allowed_tags['a'] = $allowed_atts;
196
197 // Merge global and custom tags
198 $all_allowed_tags = array_merge($allowedposttags, $allowed_tags);
199
200 // Overwrite global ones with custom atts
201 $all_allowed_tags['div'] = array_merge($allowedposttags['div'], $allowed_atts);
202
203 return $all_allowed_tags;
204 }
205
206 /**
207 * Hides the WordPress Admin Bar for specific roles.
208 *
209 * @since 18.0
210 *
211 * @return void
212 */
213 public static function hide_admin_bar()
214 {
215
216 // Don't hide for admin
217 if (is_admin()) {
218 return;
219 }
220
221 $roles = Options_Service::get_global_list_var('hide_admin_bar_roles');
222
223 if (!empty($roles) && get_current_user_id() > 0) {
224 $wp_usr = wp_get_current_user();
225
226 foreach ($roles as $role) {
227
228 if (in_array($role, $wp_usr->roles)) {
229 show_admin_bar(false);
230 break;
231 }
232 }
233 }
234 }
235
236 /**
237 * Simple helper to add save style (css) attributes.
238 *
239 * @since 18.1
240 *
241 * @param mixed $styles
242 * @return string
243 */
244 public static function safe_css($styles)
245 {
246 $styles[] = 'list-style';
247 $styles[] = 'min-width';
248 $styles[] = 'max-width';
249 $styles[] = 'display';
250 return $styles;
251 }
252 }
253 }
1 <?php
2
3 namespace Wpo\Core;
4
5 use WP_Site_Query;
6 use \Wpo\Core\WordPress_Helpers;
7
8 use \Wpo\Services\Options_Service;
9 use \Wpo\Services\Log_Service;
10
11 // Prevent public access to this script
12 defined('ABSPATH') or die();
13
14 if (!class_exists('\Wpo\Core\Wpmu_Helpers')) {
15
16 class Wpmu_Helpers
17 {
18
19 /**
20 * Helper to get the global or local transient based on the
21 * WPMU configuration.
22 *
23 * @since 9.2
24 *
25 * @return mixed Returns the value of transient or false if not found
26 */
27 public static function mu_get_transient($name)
28 {
29
30 if (!is_multisite() || (Options_Service::mu_use_subsite_options() && !self::mu_is_network_admin())) {
31 return get_transient($name);
32 }
33
34 return get_site_transient($name);
35 }
36
37 /**
38 * Helper to set the global or local transient based on the
39 * WPMU configuration.
40 *
41 * @since 9.2
42 *
43 * @param $name string Name of transient
44 * @param $value mixed Value of transient
45 * @param $duration int Time transient should be cached in seconds
46 *
47 * @return void
48 */
49 public static function mu_set_transient($name, $value, $duration = 0)
50 {
51
52 if (!is_multisite() || (Options_Service::mu_use_subsite_options() && !self::mu_is_network_admin())) {
53 set_transient($name, $value, $duration);
54 } else {
55 set_site_transient($name, $value, $duration);
56 }
57 }
58
59 /**
60 * Helper to delete the global or local transient based on the
61 * WPMU configuration.
62 *
63 * @since 10.9
64 *
65 * @param $name string Name of transient
66 *
67 * @return void
68 */
69 public static function mu_delete_transient($name)
70 {
71
72 if (!is_multisite() || (Options_Service::mu_use_subsite_options() && !self::mu_is_network_admin())) {
73 delete_transient($name);
74 } else {
75 delete_site_transient($name);
76 }
77 }
78
79 /**
80 * Helper to check if the current request is for a network admin page and it includes a simple
81 * check if the request is made from an AJAX call.
82 *
83 * @since 11.18
84 *
85 * @return boolean True if the request is for a network admin page other false.
86 */
87 public static function mu_is_network_admin()
88 {
89 return (is_network_admin() || true === $GLOBALS['WPO_CONFIG']['ina']);
90 }
91
92 /**
93 * Helper to switch the current blog from the main site to a subsite in case
94 * of a multisite installation (shared scenario) when the user is redirected
95 * back to the main site whereas the state URL indicates that the target is
96 * a subsite.
97 *
98 * @since 11.0
99 *
100 * @param $state_url string The (Relay) state URL
101 *
102 * @return void
103 */
104 public static function switch_blog($state_url)
105 {
106 Log_Service::write_log('DEBUG', '##### -> ' . __METHOD__);
107
108 if (is_multisite() && !empty($state_url)) {
109 $redirect_url = Options_Service::get_aad_option('redirect_url');
110 $redirect_host = parse_url($redirect_url, PHP_URL_HOST);
111 $state_host = parse_url($state_url, PHP_URL_HOST);
112 $state_path = '/';
113 $redirect_path = '/';
114
115 if (!is_subdomain_install()) {
116 $redirect_path = parse_url($redirect_url, PHP_URL_PATH);
117 $state_path = parse_url($state_url, PHP_URL_PATH);
118 }
119
120 $state_blog_id = self::get_blog_id_from_host_and_path($state_host, $state_path);
121 $redirect_blog_id = self::get_blog_id_from_host_and_path($redirect_host, $redirect_path);
122
123 Log_Service::write_log('DEBUG', __METHOD__ . " -> Detected WPMU with state context (path: $state_path - ID: $state_blog_id) and AAD redirect context (path: $redirect_path - ID: $redirect_blog_id)");
124
125 if ($state_blog_id !== $redirect_blog_id) {
126 switch_to_blog($state_blog_id);
127 $GLOBALS['WPO_CONFIG']['url_info']['wp_site_url'] = get_option('home');
128 }
129 }
130 }
131
132 /**
133 * Helper to try and search for a matching blog by itteratively removing the last segment from the path.
134 *
135 * @since 16.0
136 *
137 * @param string $host The domain e.g. www.your-site.com
138 * @param string $path The path starting with a slash
139 *
140 * @return int The blog ID or 0 if not found
141 */
142 public static function get_blog_id_from_host_and_path($host, $path)
143 {
144 Log_Service::write_log('DEBUG', '##### -> ' . __METHOD__);
145
146 $blog_id = get_blog_id_from_url($host, $path);
147
148 if (!empty($blog_id)) {
149 return $blog_id;
150 }
151
152 $path = WordPress_Helpers::rtrim($path, '/');
153 $path = WordPress_Helpers::ltrim($path, '/');
154 $segments = explode('/', $path);
155 $segments[] = 'placeholder'; // Add empty string to start with full URL when popping elements from the end
156
157 while (NULL != ($last_element = array_pop($segments))) {
158 $path = '/' . implode('/', $segments);
159
160 if (strlen($path) > 1) {
161 $path = $path . '/';
162 }
163
164 $blog_id = get_blog_id_from_url($host, $path);
165
166 if ($blog_id > 0) {
167 return $blog_id;
168 }
169 }
170
171 return 0;
172 }
173 }
174 }
1 <?php
2 namespace Wpo\Firebase;
3
4 class BeforeValidException extends \UnexpectedValueException
5 {
6
7 }
...\ No newline at end of file ...\ No newline at end of file
1 <?php
2 namespace Wpo\Firebase;
3
4 class ExpiredException extends \UnexpectedValueException
5 {
6
7 }
...\ No newline at end of file ...\ No newline at end of file
1 <?php
2 namespace Wpo\Firebase;
3
4 class SignatureInvalidException extends \UnexpectedValueException
5 {
6
7 }
...\ No newline at end of file ...\ No newline at end of file
1 <?php
2
3 namespace Wpo\Graph;
4
5 // Prevent public access to this script
6 defined('ABSPATH') or die();
7
8 use \Wpo\Services\Log_Service;
9 use \Wpo\Services\Options_Service;
10
11 if (!class_exists('\Wpo\Graph\Current_User')) {
12
13 class Current_User
14 {
15
16 /**
17 * Returns basic information for the current user incl. the user's (Azure AD) UPN and Object ID.
18 *
19 * @since 13.0
20 *
21 * @return array The user's login, email, display name, ID, UPN and AAD Object ID.
22 */
23 public static function get_current_user()
24 {
25 Log_Service::write_log('DEBUG', '##### -> ' . __METHOD__);
26
27 $wp_usr_id = \get_current_user_id();
28
29 $user_info = \wp_get_current_user();
30 $user_login = !empty($user_info->user_login) ? $user_info->user_login : '';
31 $user_email = !empty($user_info->user_email) ? $user_info->user_email : '';
32 $display_name = !empty($user_info->display_name) ? $user_info->display_name : '';
33 $id = $user_info->ID;
34
35 $upn = \get_user_meta($wp_usr_id, 'userPrincipalName', true);
36 $aad_object_id = \get_user_meta($wp_usr_id, 'aadObjectId', true);
37
38 return array(
39 "user_login" => $user_login,
40 "user_email" => $user_email,
41 "display_name" => $display_name,
42 "id" => $id,
43 "upn" => !empty($upn) ? $upn : '',
44 "aad_object_id" => !empty($aad_object_id) ? $aad_object_id : '',
45 );
46 }
47 }
48 }
1 Copyright (c) 2010-2016 OneLogin, Inc.
2
3 Permission is hereby granted, free of charge, to any person
4 obtaining a copy of this software and associated documentation
5 files (the "Software"), to deal in the Software without
6 restriction, including without limitation the rights to use,
7 copy, modify, merge, publish, distribute, sublicense, and/or sell
8 copies of the Software, and to permit persons to whom the
9 Software is furnished to do so, subject to the following
10 conditions:
11
12 The above copyright notice and this permission notice shall be
13 included in all copies or substantial portions of the Software.
14
15 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
17 OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
19 HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20 WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22 OTHER DEALINGS IN THE SOFTWARE.
23
1 <?php
2
3 // Create an __autoload function
4 // (can conflicts other autoloaders)
5 // http://php.net/manual/en/language.oop5.autoload.php
6
7 $libDir = __DIR__ . '/lib/Saml2/';
8 $extlibDir = __DIR__ . '/extlib/';
9
10 // Load composer
11 if (file_exists(__DIR__ .'/vendor/autoload.php')) {
12 require __DIR__ . '/vendor/autoload.php';
13 }
14
15 // Load now external libs
16 require_once $extlibDir . 'xmlseclibs/xmlseclibs.php';
17
18 $folderInfo = scandir($libDir);
19
20 foreach ($folderInfo as $element) {
21 if (is_file($libDir.$element) && (substr($element, -4) === '.php')) {
22 include_once $libDir.$element;
23 //break;
24 }
25 }
1 Take care of this folder that could contain private key. Be sure that this folder never is published.
2
3 Onelogin PHP Toolkit expects certs for the SP stored at:
4
5 * sp.key Private Key
6 * sp.crt Public cert
7 * sp_new.crt Future Public cert
8
9 Also you can use other cert to sign the metadata of the SP using the:
10
11 * metadata.key
12 * metadata.crt
1 {
2 "name": "onelogin/php-saml",
3 "description": "OneLogin PHP SAML Toolkit",
4 "license": "MIT",
5 "homepage": "https://developers.onelogin.com/saml/php",
6 "keywords": [
7 "saml",
8 "saml2",
9 "onelogin"
10 ],
11 "autoload": {
12 "classmap": [
13 "extlib/xmlseclibs",
14 "lib/Saml2"
15 ]
16 },
17 "support": {
18 "email": "sixto.garcia@onelogin.com",
19 "issues": "https://github.com/onelogin/php-saml/issues",
20 "source": "https://github.com/onelogin/php-saml/"
21 },
22 "require": {
23 "php": ">=5.3.2 <7.2",
24 "ext-curl": "*",
25 "ext-openssl": "*",
26 "ext-dom": "*",
27 "ext-mcrypt": "*"
28 },
29 "require-dev": {
30 "phpunit/phpunit": "4.8",
31 "satooshi/php-coveralls": "1.0.1",
32 "sebastian/phpcpd": "*",
33 "phploc/phploc": "*",
34 "pdepend/pdepend": "1.1.0",
35 "squizlabs/php_codesniffer": "2.9.0"
36 },
37 "suggest": {
38 "ext-gettext": "Install gettext and php5-gettext libs to handle translations"
39 }
40 }
1 xmlseclibs.php
2
3 06, Nov 2019, 3.0.4
4 Security Improvements:
5 - Insure only a single SignedInfo element exists within a signature during
6 verification. Refs CVE-2019-3465.
7 Bug Fixes:
8 - Fix variable casing.
9
10 15, Nov 2018, 3.0.3
11 Bug Fixes:
12 - Fix casing of class name. (Willem Stuursma-Ruwen)
13 - Fix Xpath casing. (Tim van Dijen)
14
15 Improvements:
16 - Make PCRE2 compliant. (Stefan Winter)
17 - Add PHP 7.3 support. (Stefan Winter)
18
19 27, Sep 2018, 3.0.2
20 Security Improvements:
21 - OpenSSL is now a requirement rather than suggestion. (Slaven Bacelic)
22 - Filter input to avoid XPath injection. (Jaime Pérez)
23
24 Bug Fixes:
25 - Fix missing parentheses (Tim van Dijen)
26
27 Improvements:
28 - Use strict comparison operator to compare digest values. (Jaime Pérez)
29 - Remove call to file_get_contents that doesn't even work. (Jaime Pérez)
30 - Document potentially dangerous return value behaviour. (Thijs Kinkhorst)
31
32 31, Aug 2017, 3.0.1
33 Bug Fixes:
34 - Fixed missing () in function call. (Dennis Væversted)
35
36 Improvements:
37 - Add OneLogin to supported software.
38 - Add .gitattributes to remove unneeded files. (Filippo Tessarotto)
39 - Fix bug in example code. (Dan Church)
40 - Travis: add PHP 7.1, move hhvm to allowed failures. (Thijs Kinkhorst)
41 - Drop failing extract-win-cert test (Thijs Kinkhorst). (Thijs Kinkhorst)
42 - Add comments to warn about return values of verify(). (Thijs Kinkhorst)
43 - Fix tests to properly check return code of verify(). (Thijs Kinkhorst)
44 - Restore support for PHP >= 5.4. (Jaime Pérez)
45
46 25, May 2017, 3.0.0
47 Improvements:
48 - Remove use of mcrypt (skymeyer)
49
50 08, Sep 2016, 2.0.1
51 Bug Fixes:
52 - Strip whitespace characters when parsing X509Certificate. fixes #84
53 (klemen.bratec)
54 - Certificate 'subject' values can be arrays. fixes #80 (Andreas Stangl)
55 - HHVM signing node with ID attribute w/out namespace regenerates ID value.
56 fixes #88 (Milos Tomic)
57
58 Improvements:
59 - Fix typos and add some PHPDoc Blocks. (gfaust-qb)
60 - Update lightSAML link. (Milos Tomic)
61 - Update copyright dates.
62
63 23, Jun 2015, 1.4.0
64 Features:
65 - Support for PSR-0 standard.
66 - Support for X509SubjectName. (Milos Tomic)
67 - Add HMAC-SHA1 support.
68
69 Improvements:
70 - Add how to install to README. (Bernardo Vieira da Silva)
71 - Code cleanup. (Jaime Pérez)
72 - Normalilze tests. (Hidde Wieringa)
73 - Add basic usage to README. (Hidde Wieringa)
74
75 21, May 2015, 1.3.2
76 Bug Fixes:
77 - Fix Undefined variable notice. (dpieper85)
78 - Fix typo when setting MimeType attribute. (Eugene OZ)
79 - Fix validateReference() with enveloping signatures
80
81 Features:
82 - canonicalizeData performance optimization. (Jaime Pérez)
83 - Add composer support (Maks3w)
84
85 19, Jun 2013, 1.3.1
86 Features:
87 - return encrypted node from XMLSecEnc::encryptNode() when replace is set to
88 false. (Olav)
89 - Add support for RSA SHA384 and RSA_SHA512 and SHA384 digest. (Jaime PŽrez)
90 - Add options parameter to the add cert methods.
91 - Add optional issuerSerial creation with cert
92
93 Bug Fixes:
94 - Fix persisted Id when namespaced. (Koen Thomeer)
95
96 Improvements:
97 - Add LICENSE file
98 - Convert CHANGELOG.txt to UTF-8
99
100 26, Sep 2011, 1.3.0
101 Features:
102 - Add param to append sig to node when signing. Fixes a problem when using
103 inclusive canonicalization to append a signature within a namespaced subtree.
104 ex. $objDSig->sign($objKey, $appendToNode);
105 - Add ability to encrypt by reference
106 - Add support for refences within an encrypted key
107 - Add thumbprint generation capability (XMLSecurityKey->getX509Thumbprint() and
108 XMLSecurityKey::getRawThumbprint($cert))
109 - Return signature element node from XMLSecurityDSig::insertSignature() and
110 XMLSecurityDSig::appendSignature() methods
111 - Support for <ds:RetrievalMethod> with simple URI Id reference.
112 - Add XMLSecurityKey::getSymmetricKeySize() method (Olav)
113 - Add XMLSecEnc::getCipherValue() method (Olav)
114 - Improve XMLSecurityKey:generateSessionKey() logic (Olav)
115
116 Bug Fixes:
117 - Change split() to explode() as split is now depreciated
118 - ds:References using empty or simple URI Id reference should never include
119 comments in canonicalized data.
120 - Make sure that the elements in EncryptedData are emitted in the correct
121 sequence.
122
123 11 Jan 2010, 1.2.2
124 Features:
125 - Add support XPath support when creating signature. Provides support for
126 working with EBXML documents.
127 - Add reference option to force creation of URI attribute. For use
128 when adding a DOM Document where by default no URI attribute is added.
129 - Add support for RSA-SHA256
130
131 Bug Fixes:
132 - fix bug #5: createDOMDocumentFragment() in decryptNode when data is node
133 content (patch by Francois Wang)
134
135
136 08 Jul 2008, 1.2.1
137 Features:
138 - Attempt to use mhash when hash extension is not present. (Alfredo Cubitos).
139 - Add fallback to built-in sha1 if both hash and mhash are not available and
140 throw error for other for other missing hashes. (patch by Olav Morken).
141 - Add getX509Certificate method to retrieve the x509 cert used for Key.
142 (patch by Olav Morken).
143 - Add getValidatedNodes method to retrieve the elements signed by the
144 signature. (patch by Olav Morken).
145 - Add insertSignature method for precision signature insertion. Merge
146 functionality from appendSignature in the process. (Olav Morken, Rob).
147 - Finally add some tests
148
149 Bug Fixes:
150 - Fix canonicalization for Document node when using PHP < 5.2.
151 - Add padding for RSA_SHA1. (patch by Olav Morken).
152
153
154 27 Nov 2007, 1.2.0
155 Features:
156 - New addReference/List option (overwrite). Boolean flag indicating if URI
157 value should be overwritten if already existing within document.
158 Default is TRUE to maintain BC.
159
160 18 Nov 2007, 1.1.2
161 Bug Fixes:
162 - Remove closing PHP tag to fix extra whitespace characters from being output
163
164 11 Nov 2007, 1.1.1
165 Features:
166 - Add getRefNodeID() and getRefIDs() methods missed in previous release.
167 Provide functionality to find URIs of existing reference nodes.
168 Required by simpleSAMLphp project
169
170 Bug Fixes:
171 - Remove erroneous whitespace causing issues under certain circumastances.
172
173 18 Oct 2007, 1.1.0
174 Features:
175 - Enable creation of enveloping signature. This allows the creation of
176 managed information cards.
177 - Add addObject method for enveloping signatures.
178 - Add staticGet509XCerts method. Chained certificates within a PEM file can
179 now be added within the X509Data node.
180 - Add xpath support within transformations
181 - Add InclusiveNamespaces prefix list support within exclusive transformations.
182
183 Bug Fixes:
184 - Initialize random number generator for mcrypt_create_iv. (Joan Cornadó).
185 - Fix an interoperability issue with .NET when encrypting data in CBC mode.
186 (Joan Cornadó).
1 Copyright (c) 2007-2019, Robert Richards <rrichards@cdatazone.org>.
2 All rights reserved.
3
4 Redistribution and use in source and binary forms, with or without
5 modification, are permitted provided that the following conditions
6 are met:
7
8 * Redistributions of source code must retain the above copyright
9 notice, this list of conditions and the following disclaimer.
10
11 * Redistributions in binary form must reproduce the above copyright
12 notice, this list of conditions and the following disclaimer in
13 the documentation and/or other materials provided with the
14 distribution.
15
16 * Neither the name of Robert Richards nor the names of his
17 contributors may be used to endorse or promote products derived
18 from this software without specific prior written permission.
19
20 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23 FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
24 COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
25 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26 BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
27 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
30 ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31 POSSIBILITY OF SUCH DAMAGE.
1 <?php
2
3 /**
4 * SAML 2 Authentication Request
5 *
6 */
7 class OneLogin_Saml2_AuthnRequest
8 {
9
10 /**
11 * Object that represents the setting info
12 * @var OneLogin_Saml2_Settings
13 */
14 protected $_settings;
15
16 /**
17 * SAML AuthNRequest string
18 * @var string
19 */
20 private $_authnRequest;
21
22 /**
23 * SAML AuthNRequest ID.
24 * @var string
25 */
26 private $_id;
27
28 /**
29 * Constructs the AuthnRequest object.
30 *
31 * @param OneLogin_Saml2_Settings $settings Settings
32 * @param bool $forceAuthn When true the AuthNReuqest will set the ForceAuthn='true'
33 * @param bool $isPassive When true the AuthNReuqest will set the Ispassive='true'
34 * @param bool $setNameIdPolicy When true the AuthNReuqest will set a nameIdPolicy
35 * @param string $nameIdValueReq Indicates to the IdP the subject that should be authenticated
36 */
37 public function __construct(OneLogin_Saml2_Settings $settings, $forceAuthn = false, $isPassive = false, $setNameIdPolicy = true, $nameIdValueReq = null)
38 {
39 $this->_settings = $settings;
40
41 $spData = $this->_settings->getSPData();
42 $security = $this->_settings->getSecurityData();
43
44 $id = OneLogin_Saml2_Utils::generateUniqueID();
45 $issueInstant = OneLogin_Saml2_Utils::parseTime2SAML(time());
46
47 $subjectStr = "";
48 if (isset($nameIdValueReq)) {
49 $subjectStr = <<<SUBJECT
50
51 <saml:Subject>
52 <saml:NameID Format="{$spData['NameIDFormat']}">{$nameIdValueReq}</saml:NameID>
53 <saml:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer"></saml:SubjectConfirmation>
54 </saml:Subject>
55 SUBJECT;
56 }
57
58 $nameIdPolicyStr = '';
59 if ($setNameIdPolicy) {
60 $nameIDPolicyFormat = $spData['NameIDFormat'];
61 if (isset($security['wantNameIdEncrypted']) && $security['wantNameIdEncrypted']) {
62 $nameIDPolicyFormat = OneLogin_Saml2_Constants::NAMEID_ENCRYPTED;
63 }
64
65 $nameIdPolicyStr = <<<NAMEIDPOLICY
66
67 <samlp:NameIDPolicy
68 Format="{$nameIDPolicyFormat}"
69 AllowCreate="true" />
70 NAMEIDPOLICY;
71 }
72
73
74 $providerNameStr = '';
75 $organizationData = $settings->getOrganization();
76 if (!empty($organizationData)) {
77 $langs = array_keys($organizationData);
78 if (in_array('en-US', $langs)) {
79 $lang = 'en-US';
80 } else {
81 $lang = $langs[0];
82 }
83 if (isset($organizationData[$lang]['displayname']) && !empty($organizationData[$lang]['displayname'])) {
84 $providerNameStr = <<<PROVIDERNAME
85 ProviderName="{$organizationData[$lang]['displayname']}"
86 PROVIDERNAME;
87 }
88 }
89
90 $forceAuthnStr = '';
91 if ($forceAuthn) {
92 $forceAuthnStr = <<<FORCEAUTHN
93
94 ForceAuthn="true"
95 FORCEAUTHN;
96 }
97
98 $isPassiveStr = '';
99 if ($isPassive) {
100 $isPassiveStr = <<<ISPASSIVE
101
102 IsPassive="true"
103 ISPASSIVE;
104 }
105
106 $requestedAuthnStr = '';
107 if (isset($security['requestedAuthnContext']) && $security['requestedAuthnContext'] !== false) {
108 $authnComparison = 'exact';
109 if (isset($security['requestedAuthnContextComparison'])) {
110 $authnComparison = $security['requestedAuthnContextComparison'];
111 }
112
113 $authnComparisonAttr = '';
114 if (!empty($authnComparison)) {
115 $authnComparisonAttr = sprintf('Comparison="%s"', $authnComparison);
116 }
117
118 if ($security['requestedAuthnContext'] === true) {
119 $requestedAuthnStr = <<<REQUESTEDAUTHN
120
121 <samlp:RequestedAuthnContext $authnComparisonAttr>
122 <saml:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport</saml:AuthnContextClassRef>
123 </samlp:RequestedAuthnContext>
124 REQUESTEDAUTHN;
125 } else {
126 $requestedAuthnStr .= " <samlp:RequestedAuthnContext $authnComparisonAttr>\n";
127 foreach ($security['requestedAuthnContext'] as $contextValue) {
128 $requestedAuthnStr .= " <saml:AuthnContextClassRef>".$contextValue."</saml:AuthnContextClassRef>\n";
129 }
130 $requestedAuthnStr .= ' </samlp:RequestedAuthnContext>';
131 }
132 }
133
134 $spEntityId = htmlspecialchars($spData['entityId'], ENT_QUOTES);
135 $acsUrl = htmlspecialchars($spData['assertionConsumerService']['url'], ENT_QUOTES);
136 $destination = $this->_settings->getIdPSSOUrl();
137 $request = <<<AUTHNREQUEST
138 <samlp:AuthnRequest
139 xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"
140 xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"
141 ID="$id"
142 Version="2.0"
143 {$providerNameStr}{$forceAuthnStr}{$isPassiveStr}
144 IssueInstant="$issueInstant"
145 Destination="{$destination}"
146 ProtocolBinding="{$spData['assertionConsumerService']['binding']}"
147 AssertionConsumerServiceURL="{$acsUrl}">
148 <saml:Issuer>{$spEntityId}</saml:Issuer>{$subjectStr}{$nameIdPolicyStr}{$requestedAuthnStr}
149 </samlp:AuthnRequest>
150 AUTHNREQUEST;
151
152 $this->_id = $id;
153 $this->_authnRequest = $request;
154 }
155
156 /**
157 * Returns deflated, base64 encoded, unsigned AuthnRequest.
158 *
159 * @param bool|null $deflate Whether or not we should 'gzdeflate' the request body before we return it.
160 *
161 * @return string
162 */
163 public function getRequest($deflate = null)
164 {
165 $subject = $this->_authnRequest;
166
167 if (is_null($deflate)) {
168 $deflate = $this->_settings->shouldCompressRequests();
169 }
170
171 if ($deflate) {
172 $subject = gzdeflate($this->_authnRequest);
173 }
174
175 $base64Request = base64_encode($subject);
176 return $base64Request;
177 }
178
179 /**
180 * Returns the AuthNRequest ID.
181 *
182 * @return string
183 */
184 public function getId()
185 {
186 return $this->_id;
187 }
188
189 /**
190 * Returns the XML that will be sent as part of the request
191 *
192 * @return string
193 */
194 public function getXML()
195 {
196 return $this->_authnRequest;
197 }
198 }
1 <?php
2
3 /**
4 * Constants of OneLogin PHP Toolkit
5 *
6 * Defines all required constants
7 */
8 class OneLogin_Saml2_Constants
9 {
10 // Value added to the current time in time condition validations
11 const ALLOWED_CLOCK_DRIFT = 180; // 3 min in seconds
12
13 // NameID Formats
14 const NAMEID_EMAIL_ADDRESS = 'urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress';
15 const NAMEID_X509_SUBJECT_NAME = 'urn:oasis:names:tc:SAML:1.1:nameid-format:X509SubjectName';
16 const NAMEID_WINDOWS_DOMAIN_QUALIFIED_NAME = 'urn:oasis:names:tc:SAML:1.1:nameid-format:WindowsDomainQualifiedName';
17 const NAMEID_UNSPECIFIED = 'urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified';
18 const NAMEID_KERBEROS = 'urn:oasis:names:tc:SAML:2.0:nameid-format:kerberos';
19 const NAMEID_ENTITY = 'urn:oasis:names:tc:SAML:2.0:nameid-format:entity';
20 const NAMEID_TRANSIENT = 'urn:oasis:names:tc:SAML:2.0:nameid-format:transient';
21 const NAMEID_PERSISTENT = 'urn:oasis:names:tc:SAML:2.0:nameid-format:persistent';
22 const NAMEID_ENCRYPTED = 'urn:oasis:names:tc:SAML:2.0:nameid-format:encrypted';
23
24 // Attribute Name Formats
25 const ATTRNAME_FORMAT_UNSPECIFIED = 'urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified';
26 const ATTRNAME_FORMAT_URI = 'urn:oasis:names:tc:SAML:2.0:attrname-format:uri';
27 const ATTRNAME_FORMAT_BASIC = 'urn:oasis:names:tc:SAML:2.0:attrname-format:basic';
28
29 // Namespaces
30 const NS_SAML = 'urn:oasis:names:tc:SAML:2.0:assertion';
31 const NS_SAMLP = 'urn:oasis:names:tc:SAML:2.0:protocol';
32 const NS_SOAP = 'http://schemas.xmlsoap.org/soap/envelope/';
33 const NS_MD = 'urn:oasis:names:tc:SAML:2.0:metadata';
34 const NS_XS = 'http://www.w3.org/2001/XMLSchema';
35 const NS_XSI = 'http://www.w3.org/2001/XMLSchema-instance';
36 const NS_XENC = 'http://www.w3.org/2001/04/xmlenc#';
37 const NS_DS = 'http://www.w3.org/2000/09/xmldsig#';
38
39 // Bindings
40 const BINDING_HTTP_POST = 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST';
41 const BINDING_HTTP_REDIRECT = 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect';
42 const BINDING_HTTP_ARTIFACT = 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact';
43 const BINDING_SOAP = 'urn:oasis:names:tc:SAML:2.0:bindings:SOAP';
44 const BINDING_DEFLATE = 'urn:oasis:names:tc:SAML:2.0:bindings:URL-Encoding:DEFLATE';
45
46 // Auth Context Class
47 const AC_UNSPECIFIED = 'urn:oasis:names:tc:SAML:2.0:ac:classes:unspecified';
48 const AC_PASSWORD = 'urn:oasis:names:tc:SAML:2.0:ac:classes:Password';
49 const AC_PASSWORD_PROTECTED = 'urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport';
50 const AC_X509 = 'urn:oasis:names:tc:SAML:2.0:ac:classes:X509';
51 const AC_SMARTCARD = 'urn:oasis:names:tc:SAML:2.0:ac:classes:Smartcard';
52 const AC_SMARTCARD_PKI = 'urn:oasis:names:tc:SAML:2.0:ac:classes:SmartcardPKI';
53 const AC_KERBEROS = 'urn:oasis:names:tc:SAML:2.0:ac:classes:Kerberos';
54 const AC_WINDOWS = 'urn:federation:authentication:windows';
55 const AC_TLS = 'urn:oasis:names:tc:SAML:2.0:ac:classes:TLSClient';
56 const AC_RSATOKEN = 'urn:oasis:names:tc:SAML:2.0:ac:classes:TimeSyncToken';
57
58 // Subject Confirmation
59 const CM_BEARER = 'urn:oasis:names:tc:SAML:2.0:cm:bearer';
60 const CM_HOLDER_KEY = 'urn:oasis:names:tc:SAML:2.0:cm:holder-of-key';
61 const CM_SENDER_VOUCHES = 'urn:oasis:names:tc:SAML:2.0:cm:sender-vouches';
62
63 // Status Codes
64 const STATUS_SUCCESS = 'urn:oasis:names:tc:SAML:2.0:status:Success';
65 const STATUS_REQUESTER = 'urn:oasis:names:tc:SAML:2.0:status:Requester';
66 const STATUS_RESPONDER = 'urn:oasis:names:tc:SAML:2.0:status:Responder';
67 const STATUS_VERSION_MISMATCH = 'urn:oasis:names:tc:SAML:2.0:status:VersionMismatch';
68 const STATUS_NO_PASSIVE = 'urn:oasis:names:tc:SAML:2.0:status:NoPassive';
69 const STATUS_PARTIAL_LOGOUT = 'urn:oasis:names:tc:SAML:2.0:status:PartialLogout';
70 const STATUS_PROXY_COUNT_EXCEEDED = 'urn:oasis:names:tc:SAML:2.0:status:ProxyCountExceeded';
71 }
1 <?php
2
3 /**
4 * Error class of OneLogin PHP Toolkit
5 *
6 * Defines the Error class
7 */
8 class OneLogin_Saml2_Error extends Exception
9 {
10 // Errors
11 const SETTINGS_FILE_NOT_FOUND = 0;
12 const SETTINGS_INVALID_SYNTAX = 1;
13 const SETTINGS_INVALID = 2;
14 const METADATA_SP_INVALID = 3;
15 const SP_CERTS_NOT_FOUND = 4;
16 // SP_CERTS_NOT_FOUND is deprecated, use CERT_NOT_FOUND instead
17 const CERT_NOT_FOUND = 4;
18 const REDIRECT_INVALID_URL = 5;
19 const PUBLIC_CERT_FILE_NOT_FOUND = 6;
20 const PRIVATE_KEY_FILE_NOT_FOUND = 7;
21 const SAML_RESPONSE_NOT_FOUND = 8;
22 const SAML_LOGOUTMESSAGE_NOT_FOUND = 9;
23 const SAML_LOGOUTREQUEST_INVALID = 10;
24 const SAML_LOGOUTRESPONSE_INVALID = 11;
25 const SAML_SINGLE_LOGOUT_NOT_SUPPORTED = 12;
26 const PRIVATE_KEY_NOT_FOUND = 13;
27 const UNSUPPORTED_SETTINGS_OBJECT = 14;
28
29 /**
30 * Constructor
31 *
32 * @param string $msg Describes the error.
33 * @param int $code The code error (defined in the error class).
34 * @param array|null $args Arguments used in the message that describes the error.
35 */
36 public function __construct($msg, $code = 0, $args = null)
37 {
38 //assert('is_string($msg)');
39 //assert('is_int($code)');
40
41 $message = OneLogin_Saml2_Utils::t($msg, $args);
42
43 parent::__construct($message, $code);
44 }
45 }
46
47 /**
48 * This class implements another custom Exception handler,
49 * related to exceptions that happens during validation process.
50 */
51 class OneLogin_Saml2_ValidationError extends Exception
52 {
53 # Validation Errors
54 const UNSUPPORTED_SAML_VERSION = 0;
55 const MISSING_ID = 1;
56 const WRONG_NUMBER_OF_ASSERTIONS = 2;
57 const MISSING_STATUS = 3;
58 const MISSING_STATUS_CODE = 4;
59 const STATUS_CODE_IS_NOT_SUCCESS = 5;
60 const WRONG_SIGNED_ELEMENT = 6;
61 const ID_NOT_FOUND_IN_SIGNED_ELEMENT = 7;
62 const DUPLICATED_ID_IN_SIGNED_ELEMENTS = 8;
63 const INVALID_SIGNED_ELEMENT = 9;
64 const DUPLICATED_REFERENCE_IN_SIGNED_ELEMENTS = 10;
65 const UNEXPECTED_SIGNED_ELEMENTS = 11;
66 const WRONG_NUMBER_OF_SIGNATURES_IN_RESPONSE = 12;
67 const WRONG_NUMBER_OF_SIGNATURES_IN_ASSERTION = 13;
68 const INVALID_XML_FORMAT = 14;
69 const WRONG_INRESPONSETO = 15;
70 const NO_ENCRYPTED_ASSERTION = 16;
71 const NO_ENCRYPTED_NAMEID = 17;
72 const MISSING_CONDITIONS = 18;
73 const ASSERTION_TOO_EARLY = 19;
74 const ASSERTION_EXPIRED = 20;
75 const WRONG_NUMBER_OF_AUTHSTATEMENTS = 21;
76 const NO_ATTRIBUTESTATEMENT = 22;
77 const ENCRYPTED_ATTRIBUTES = 23;
78 const WRONG_DESTINATION = 24;
79 const EMPTY_DESTINATION = 25;
80 const WRONG_AUDIENCE = 26;
81 const ISSUER_MULTIPLE_IN_RESPONSE = 27;
82 const ISSUER_NOT_FOUND_IN_ASSERTION = 28;
83 const WRONG_ISSUER = 29;
84 const SESSION_EXPIRED = 30;
85 const WRONG_SUBJECTCONFIRMATION = 31;
86 const NO_SIGNED_MESSAGE = 32;
87 const NO_SIGNED_ASSERTION = 33;
88 const NO_SIGNATURE_FOUND = 34;
89 const KEYINFO_NOT_FOUND_IN_ENCRYPTED_DATA = 35;
90 const CHILDREN_NODE_NOT_FOUND_IN_KEYINFO = 36;
91 const UNSUPPORTED_RETRIEVAL_METHOD = 37;
92 const NO_NAMEID = 38;
93 const EMPTY_NAMEID = 39;
94 const SP_NAME_QUALIFIER_NAME_MISMATCH = 40;
95 const DUPLICATED_ATTRIBUTE_NAME_FOUND = 41;
96 const INVALID_SIGNATURE = 42;
97 const WRONG_NUMBER_OF_SIGNATURES = 43;
98 const RESPONSE_EXPIRED = 44;
99 const UNEXPECTED_REFERENCE = 45;
100 const NOT_SUPPORTED = 46;
101 const KEY_ALGORITHM_ERROR = 47;
102 const MISSING_ENCRYPTED_ELEMENT = 48;
103
104
105 /**
106 * Constructor
107 *
108 * @param string $msg Describes the error.
109 * @param int $code The code error (defined in the error class).
110 * @param array|null $args Arguments used in the message that describes the error.
111 */
112 public function __construct($msg, $code = 0, $args = null)
113 {
114 //assert('is_string($msg)');
115 //assert('is_int($code)');
116
117 $message = OneLogin_Saml2_Utils::t($msg, $args);
118
119 parent::__construct($message, $code);
120 }
121 }
1 <?php
2
3 /**
4 * Metadata lib of OneLogin PHP Toolkit
5 *
6 */
7
8 class OneLogin_Saml2_Metadata
9 {
10 const TIME_VALID = 172800; // 2 days
11 const TIME_CACHED = 604800; // 1 week
12
13 /**
14 * Generates the metadata of the SP based on the settings
15 *
16 * @param array $sp The SP data
17 * @param bool|string $authnsign authnRequestsSigned attribute
18 * @param bool|string $wsign wantAssertionsSigned attribute
19 * @param DateTime|null $validUntil Metadata's valid time
20 * @param int|null $cacheDuration Duration of the cache in seconds
21 * @param array $contacts Contacts info
22 * @param array $organization Organization ingo
23 * @param array $attributes
24 *
25 * @return string SAML Metadata XML
26 */
27 public static function builder($sp, $authnsign = false, $wsign = false, $validUntil = null, $cacheDuration = null, $contacts = array(), $organization = array(), $attributes = array())
28 {
29
30 if (!isset($validUntil)) {
31 $validUntil = time() + self::TIME_VALID;
32 }
33 $validUntilTime = gmdate('Y-m-d\TH:i:s\Z', $validUntil);
34
35 if (!isset($cacheDuration)) {
36 $cacheDuration = self::TIME_CACHED;
37 }
38
39 $sls = '';
40
41 if (isset($sp['singleLogoutService'])) {
42 $slsUrl = htmlspecialchars($sp['singleLogoutService']['url'], ENT_QUOTES);
43 $sls = <<<SLS_TEMPLATE
44 <md:SingleLogoutService Binding="{$sp['singleLogoutService']['binding']}"
45 Location="{$slsUrl}" />
46
47 SLS_TEMPLATE;
48 }
49
50 if ($authnsign) {
51 $strAuthnsign = 'true';
52 } else {
53 $strAuthnsign = 'false';
54 }
55
56 if ($wsign) {
57 $strWsign = 'true';
58 } else {
59 $strWsign = 'false';
60 }
61
62 $strOrganization = '';
63
64 if (!empty($organization)) {
65 $organizationInfoNames = array();
66 $organizationInfoDisplaynames = array();
67 $organizationInfoUrls = array();
68 foreach ($organization as $lang => $info) {
69 $organizationInfoNames[] = <<<ORGANIZATION_NAME
70 <md:OrganizationName xml:lang="{$lang}">{$info['name']}</md:OrganizationName>
71 ORGANIZATION_NAME;
72 $organizationInfoDisplaynames[] = <<<ORGANIZATION_DISPLAY
73 <md:OrganizationDisplayName xml:lang="{$lang}">{$info['displayname']}</md:OrganizationDisplayName>
74 ORGANIZATION_DISPLAY;
75 $organizationInfoUrls[] = <<<ORGANIZATION_URL
76 <md:OrganizationURL xml:lang="{$lang}">{$info['url']}</md:OrganizationURL>
77 ORGANIZATION_URL;
78 }
79 $orgData = implode("\n", $organizationInfoNames)."\n".implode("\n", $organizationInfoDisplaynames)."\n".implode("\n", $organizationInfoUrls);
80 $strOrganization = <<<ORGANIZATIONSTR
81
82 <md:Organization>
83 {$orgData}
84 </md:Organization>
85 ORGANIZATIONSTR;
86 }
87
88 $strContacts = '';
89 if (!empty($contacts)) {
90 $contactsInfo = array();
91 foreach ($contacts as $type => $info) {
92 $contactsInfo[] = <<<CONTACT
93 <md:ContactPerson contactType="{$type}">
94 <md:GivenName>{$info['givenName']}</md:GivenName>
95 <md:EmailAddress>{$info['emailAddress']}</md:EmailAddress>
96 </md:ContactPerson>
97 CONTACT;
98 }
99 $strContacts = "\n".implode("\n", $contactsInfo);
100 }
101
102 $strAttributeConsumingService = '';
103 if (isset($sp['attributeConsumingService'])) {
104 $attrCsDesc = '';
105 if (isset($sp['attributeConsumingService']['serviceDescription'])) {
106 $attrCsDesc = sprintf(
107 ' <md:ServiceDescription xml:lang="en">%s</md:ServiceDescription>' . PHP_EOL,
108 $sp['attributeConsumingService']['serviceDescription']
109 );
110 }
111 if (!isset($sp['attributeConsumingService']['serviceName'])) {
112 $sp['attributeConsumingService']['serviceName'] = 'Service';
113 }
114 $requestedAttributeData = array();
115 foreach ($sp['attributeConsumingService']['requestedAttributes'] as $attribute) {
116 $requestedAttributeStr = sprintf(' <md:RequestedAttribute Name="%s"', $attribute['name']);
117 if (isset($attribute['nameFormat'])) {
118 $requestedAttributeStr .= sprintf(' NameFormat="%s"', $attribute['nameFormat']);
119 }
120 if (isset($attribute['friendlyName'])) {
121 $requestedAttributeStr .= sprintf(' FriendlyName="%s"', $attribute['friendlyName']);
122 }
123 if (isset($attribute['isRequired'])) {
124 $requestedAttributeStr .= sprintf(' isRequired="%s"', $attribute['isRequired'] === true ? 'true' : 'false');
125 }
126 $reqAttrAuxStr = " />";
127
128 if (isset($attribute['attributeValue']) && !empty($attribute['attributeValue'])) {
129 $reqAttrAuxStr = '>';
130 if (is_string($attribute['attributeValue'])) {
131 $attribute['attributeValue'] = array($attribute['attributeValue']);
132 }
133 foreach ($attribute['attributeValue'] as $attrValue) {
134 $reqAttrAuxStr .=<<<ATTRIBUTEVALUE
135
136 <saml:AttributeValue xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">{$attrValue}</saml:AttributeValue>
137 ATTRIBUTEVALUE;
138 }
139 $reqAttrAuxStr .= "\n </md:RequestedAttribute>";
140 }
141
142 $requestedAttributeData[] = $requestedAttributeStr . $reqAttrAuxStr;
143 }
144
145 $requestedAttributeStr = implode(PHP_EOL, $requestedAttributeData);
146 $strAttributeConsumingService = <<<METADATA_TEMPLATE
147 <md:AttributeConsumingService index="1">
148 <md:ServiceName xml:lang="en">{$sp['attributeConsumingService']['serviceName']}</md:ServiceName>
149 {$attrCsDesc}{$requestedAttributeStr}
150 </md:AttributeConsumingService>
151 METADATA_TEMPLATE;
152 }
153
154 $spEntityId = htmlspecialchars($sp['entityId'], ENT_QUOTES);
155 $acsUrl = htmlspecialchars($sp['assertionConsumerService']['url'], ENT_QUOTES);
156 $metadata = <<<METADATA_TEMPLATE
157 <?xml version="1.0"?>
158 <md:EntityDescriptor xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata"
159 validUntil="{$validUntilTime}"
160 cacheDuration="PT{$cacheDuration}S"
161 entityID="{$spEntityId}">
162 <md:SPSSODescriptor AuthnRequestsSigned="{$strAuthnsign}" WantAssertionsSigned="{$strWsign}" protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
163 {$sls} <md:NameIDFormat>{$sp['NameIDFormat']}</md:NameIDFormat>
164 <md:AssertionConsumerService Binding="{$sp['assertionConsumerService']['binding']}"
165 Location="{$acsUrl}"
166 index="1" />
167 {$strAttributeConsumingService}
168 </md:SPSSODescriptor>{$strOrganization}{$strContacts}
169 </md:EntityDescriptor>
170 METADATA_TEMPLATE;
171 return $metadata;
172 }
173
174 /**
175 * Signs the metadata with the key/cert provided
176 *
177 * @param string $metadata SAML Metadata XML
178 * @param string $key x509 key
179 * @param string $cert x509 cert
180 * @param string $signAlgorithm Signature algorithm method
181 * @param string $digestAlgorithm Digest algorithm method
182 *
183 * @return string Signed Metadata
184 *
185 * @throws Exception
186 */
187 public static function signMetadata($metadata, $key, $cert, $signAlgorithm = XMLSecurityKey::RSA_SHA1, $digestAlgorithm = XMLSecurityDSig::SHA1)
188 {
189 return OneLogin_Saml2_Utils::addSign($metadata, $key, $cert, $signAlgorithm, $digestAlgorithm);
190 }
191
192 /**
193 * Adds the x509 descriptors (sign/encriptation) to the metadata
194 * The same cert will be used for sign/encrypt
195 *
196 * @param string $metadata SAML Metadata XML
197 * @param string $cert x509 cert
198 * @param bool $wantsEncrypted Whether to include the KeyDescriptor for encryption
199 *
200 * @return string Metadata with KeyDescriptors
201 *
202 * @throws Exception
203 */
204 public static function addX509KeyDescriptors($metadata, $cert, $wantsEncrypted = true)
205 {
206 $xml = new DOMDocument();
207 $xml->preserveWhiteSpace = false;
208 $xml->formatOutput = true;
209 try {
210 $xml = OneLogin_Saml2_Utils::loadXML($xml, $metadata);
211 if (!$xml) {
212 throw new Exception('Error parsing metadata');
213 }
214 } catch (Exception $e) {
215 throw new Exception('Error parsing metadata. '.$e->getMessage());
216 }
217
218 $formatedCert = OneLogin_Saml2_Utils::formatCert($cert, false);
219 $x509Certificate = $xml->createElementNS(OneLogin_Saml2_Constants::NS_DS, 'X509Certificate', $formatedCert);
220
221 $keyData = $xml->createElementNS(OneLogin_Saml2_Constants::NS_DS, 'ds:X509Data');
222 $keyData->appendChild($x509Certificate);
223
224 $keyInfo = $xml->createElementNS(OneLogin_Saml2_Constants::NS_DS, 'ds:KeyInfo');
225 $keyInfo->appendChild($keyData);
226
227 $keyDescriptor = $xml->createElementNS(OneLogin_Saml2_Constants::NS_MD, "md:KeyDescriptor");
228
229 $SPSSODescriptor = $xml->getElementsByTagName('SPSSODescriptor')->item(0);
230 $SPSSODescriptor->insertBefore($keyDescriptor->cloneNode(), $SPSSODescriptor->firstChild);
231 if ($wantsEncrypted === true) {
232 $SPSSODescriptor->insertBefore($keyDescriptor->cloneNode(), $SPSSODescriptor->firstChild);
233 }
234
235 $signing = $xml->getElementsByTagName('KeyDescriptor')->item(0);
236 $signing->setAttribute('use', 'signing');
237 $signing->appendChild($keyInfo);
238
239 if ($wantsEncrypted === true) {
240 $encryption = $xml->getElementsByTagName('KeyDescriptor')->item(1);
241 $encryption->setAttribute('use', 'encryption');
242
243 $encryption->appendChild($keyInfo->cloneNode(true));
244 }
245
246 return $xml->saveXML();
247 }
248 }
1 <?xml version="1.0" encoding="UTF-8"?>
2 <xs:schema
3 targetNamespace="urn:oasis:names:tc:SAML:2.0:ac"
4 xmlns:xs="http://www.w3.org/2001/XMLSchema"
5 xmlns="urn:oasis:names:tc:SAML:2.0:ac"
6 blockDefault="substitution"
7 version="2.0">
8
9 <xs:annotation>
10 <xs:documentation>
11 Document identifier: saml-schema-authn-context-2.0
12 Location: http://docs.oasis-open.org/security/saml/v2.0/
13 Revision history:
14 V2.0 (March, 2005):
15 New core authentication context schema for SAML V2.0.
16 This is just an include of all types from the schema
17 referred to in the include statement below.
18 </xs:documentation>
19 </xs:annotation>
20
21 <xs:include schemaLocation="saml-schema-authn-context-types-2.0.xsd"/>
22
23 </xs:schema>
...\ No newline at end of file ...\ No newline at end of file
1 <?xml version="1.0" encoding="UTF-8"?>
2 <schema
3 targetNamespace="urn:oasis:names:tc:SAML:metadata:attribute"
4 xmlns="http://www.w3.org/2001/XMLSchema"
5 xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"
6 xmlns:mdattr="urn:oasis:names:tc:SAML:metadata:attribute"
7 elementFormDefault="unqualified"
8 attributeFormDefault="unqualified"
9 blockDefault="substitution"
10 version="2.0">
11
12 <annotation>
13 <documentation>
14 Document title: SAML V2.0 Metadata Extention for Entity Attributes Schema
15 Document identifier: sstc-metadata-attr.xsd
16 Location: http://www.oasis-open.org/committees/documents.php?wg_abbrev=security
17 Revision history:
18 V1.0 (November 2008):
19 Initial version.
20 </documentation>
21 </annotation>
22
23 <import namespace="urn:oasis:names:tc:SAML:2.0:assertion"
24 schemaLocation="saml-schema-assertion-2.0.xsd"/>
25
26 <element name="EntityAttributes" type="mdattr:EntityAttributesType"/>
27 <complexType name="EntityAttributesType">
28 <choice maxOccurs="unbounded">
29 <element ref="saml:Attribute"/>
30 <element ref="saml:Assertion"/>
31 </choice>
32 </complexType>
33
34 </schema>
35
1 <?xml version="1.0" encoding="UTF-8"?>
2 <schema
3 targetNamespace="urn:oasis:names:tc:SAML:attribute:ext"
4 xmlns="http://www.w3.org/2001/XMLSchema"
5 elementFormDefault="unqualified"
6 attributeFormDefault="unqualified"
7 blockDefault="substitution"
8 version="2.0">
9
10 <annotation>
11 <documentation>
12 Document title: SAML V2.0 Attribute Extension Schema
13 Document identifier: sstc-saml-attribute-ext.xsd
14 Location: http://www.oasis-open.org/committees/documents.php?wg_abbrev=security
15 Revision history:
16 V1.0 (October 2008):
17 Initial version.
18 </documentation>
19 </annotation>
20
21 <attribute name="OriginalIssuer" type="anyURI"/>
22 <attribute name="LastModified" type="dateTime"/>
23
24 </schema>
25
1 <?xml version="1.0" encoding="UTF-8"?>
2 <schema
3 targetNamespace="urn:oasis:names:tc:SAML:metadata:algsupport"
4 xmlns="http://www.w3.org/2001/XMLSchema"
5 xmlns:alg="urn:oasis:names:tc:SAML:metadata:algsupport"
6 elementFormDefault="unqualified"
7 attributeFormDefault="unqualified"
8 blockDefault="substitution"
9 version="1.0">
10
11 <annotation>
12 <documentation>
13 Document title: Metadata Extension Schema for SAML V2.0 Metadata Profile for Algorithm Support Version 1.0
14 Document identifier: sstc-saml-metadata-algsupport.xsd
15 Location: http://docs.oasis-open.org/security/saml/Post2.0/
16 Revision history:
17 V1.0 (June 2010):
18 Initial version.
19 </documentation>
20 </annotation>
21
22 <element name="DigestMethod" type="alg:DigestMethodType"/>
23 <complexType name="DigestMethodType">
24 <sequence>
25 <any namespace="##any" processContents="lax" minOccurs="0" maxOccurs="unbounded"/>
26 </sequence>
27 <attribute name="Algorithm" type="anyURI" use="required"/>
28 </complexType>
29
30 <element name="SigningMethod" type="alg:SigningMethodType"/>
31 <complexType name="SigningMethodType">
32 <sequence>
33 <any namespace="##any" processContents="lax" minOccurs="0" maxOccurs="unbounded"/>
34 </sequence>
35 <attribute name="Algorithm" type="anyURI" use="required"/>
36 <attribute name="MinKeySize" type="positiveInteger"/>
37 <attribute name="MaxKeySize" type="positiveInteger"/>
38 </complexType>
39
40 </schema>
41
1 <?xml version="1.0" encoding="UTF-8"?>
2 <schema
3 targetNamespace="urn:oasis:names:tc:SAML:metadata:ui"
4 xmlns="http://www.w3.org/2001/XMLSchema"
5 xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata"
6 xmlns:mdui="urn:oasis:names:tc:SAML:metadata:ui"
7 elementFormDefault="unqualified"
8 attributeFormDefault="unqualified"
9 blockDefault="substitution"
10 version="1.0">
11
12 <annotation>
13 <documentation>
14 Document title: Metadata Extension Schema for SAML V2.0 Metadata Extensions for Login and Discovery User Interface Version 1.0
15 Document identifier: sstc-saml-metadata-ui-v1.0.xsd
16 Location: http://docs.oasis-open.org/security/saml/Post2.0/
17 Revision history:
18 16 November 2010:
19 Added Keywords element/type.
20 01 November 2010
21 Changed filename.
22 September 2010:
23 Initial version.
24 </documentation>
25 </annotation>
26
27 <import namespace="urn:oasis:names:tc:SAML:2.0:metadata"
28 schemaLocation="saml-schema-metadata-2.0.xsd"/>
29 <import namespace="http://www.w3.org/XML/1998/namespace"
30 schemaLocation="xml.xsd"/>
31
32 <element name="UIInfo" type="mdui:UIInfoType" />
33 <complexType name="UIInfoType">
34 <choice minOccurs="0" maxOccurs="unbounded">
35 <element ref="mdui:DisplayName"/>
36 <element ref="mdui:Description"/>
37 <element ref="mdui:Keywords"/>
38 <element ref="mdui:Logo"/>
39 <element ref="mdui:InformationURL"/>
40 <element ref="mdui:PrivacyStatementURL"/>
41 <any namespace="##other" processContents="lax"/>
42 </choice>
43 </complexType>
44
45 <element name="DisplayName" type="md:localizedNameType"/>
46 <element name="Description" type="md:localizedNameType"/>
47 <element name="InformationURL" type="md:localizedURIType"/>
48 <element name="PrivacyStatementURL" type="md:localizedURIType"/>
49
50 <element name="Keywords" type="mdui:KeywordsType"/>
51 <complexType name="KeywordsType">
52 <simpleContent>
53 <extension base="mdui:listOfStrings">
54 <attribute ref="xml:lang" use="required"/>
55 </extension>
56 </simpleContent>
57 </complexType>
58
59 <simpleType name="listOfStrings">
60 <list itemType="string"/>
61 </simpleType>
62
63 <element name="Logo" type="mdui:LogoType"/>
64 <complexType name="LogoType">
65 <simpleContent>
66 <extension base="anyURI">
67 <attribute name="height" type="positiveInteger" use="required"/>
68 <attribute name="width" type="positiveInteger" use="required"/>
69 <attribute ref="xml:lang"/>
70 </extension>
71 </simpleContent>
72 </complexType>
73
74 <element name="DiscoHints" type="mdui:DiscoHintsType"/>
75 <complexType name="DiscoHintsType">
76 <choice minOccurs="0" maxOccurs="unbounded">
77 <element ref="mdui:IPHint"/>
78 <element ref="mdui:DomainHint"/>
79 <element ref="mdui:GeolocationHint"/>
80 <any namespace="##other" processContents="lax"/>
81 </choice>
82 </complexType>
83
84 <element name="IPHint" type="string"/>
85 <element name="DomainHint" type="string"/>
86 <element name="GeolocationHint" type="anyURI"/>
87
88 </schema>
89
1 <?xml version="1.0" encoding="utf-8"?>
2
3 <schema xmlns='http://www.w3.org/2001/XMLSchema' version='1.0'
4 xmlns:xenc='http://www.w3.org/2001/04/xmlenc#'
5 xmlns:ds='http://www.w3.org/2000/09/xmldsig#'
6 targetNamespace='http://www.w3.org/2001/04/xmlenc#'
7 elementFormDefault='qualified'>
8
9 <import namespace='http://www.w3.org/2000/09/xmldsig#'
10 schemaLocation='xmldsig-core-schema.xsd'/>
11
12 <complexType name='EncryptedType' abstract='true'>
13 <sequence>
14 <element name='EncryptionMethod' type='xenc:EncryptionMethodType'
15 minOccurs='0'/>
16 <element ref='ds:KeyInfo' minOccurs='0'/>
17 <element ref='xenc:CipherData'/>
18 <element ref='xenc:EncryptionProperties' minOccurs='0'/>
19 </sequence>
20 <attribute name='Id' type='ID' use='optional'/>
21 <attribute name='Type' type='anyURI' use='optional'/>
22 <attribute name='MimeType' type='string' use='optional'/>
23 <attribute name='Encoding' type='anyURI' use='optional'/>
24 </complexType>
25
26 <complexType name='EncryptionMethodType' mixed='true'>
27 <sequence>
28 <element name='KeySize' minOccurs='0' type='xenc:KeySizeType'/>
29 <element name='OAEPparams' minOccurs='0' type='base64Binary'/>
30 <any namespace='##other' minOccurs='0' maxOccurs='unbounded'/>
31 </sequence>
32 <attribute name='Algorithm' type='anyURI' use='required'/>
33 </complexType>
34
35 <simpleType name='KeySizeType'>
36 <restriction base="integer"/>
37 </simpleType>
38
39 <element name='CipherData' type='xenc:CipherDataType'/>
40 <complexType name='CipherDataType'>
41 <choice>
42 <element name='CipherValue' type='base64Binary'/>
43 <element ref='xenc:CipherReference'/>
44 </choice>
45 </complexType>
46
47 <element name='CipherReference' type='xenc:CipherReferenceType'/>
48 <complexType name='CipherReferenceType'>
49 <choice>
50 <element name='Transforms' type='xenc:TransformsType' minOccurs='0'/>
51 </choice>
52 <attribute name='URI' type='anyURI' use='required'/>
53 </complexType>
54
55 <complexType name='TransformsType'>
56 <sequence>
57 <element ref='ds:Transform' maxOccurs='unbounded'/>
58 </sequence>
59 </complexType>
60
61
62 <element name='EncryptedData' type='xenc:EncryptedDataType'/>
63 <complexType name='EncryptedDataType'>
64 <complexContent>
65 <extension base='xenc:EncryptedType'>
66 </extension>
67 </complexContent>
68 </complexType>
69
70 <!-- Children of ds:KeyInfo -->
71
72 <element name='EncryptedKey' type='xenc:EncryptedKeyType'/>
73 <complexType name='EncryptedKeyType'>
74 <complexContent>
75 <extension base='xenc:EncryptedType'>
76 <sequence>
77 <element ref='xenc:ReferenceList' minOccurs='0'/>
78 <element name='CarriedKeyName' type='string' minOccurs='0'/>
79 </sequence>
80 <attribute name='Recipient' type='string'
81 use='optional'/>
82 </extension>
83 </complexContent>
84 </complexType>
85
86 <element name="AgreementMethod" type="xenc:AgreementMethodType"/>
87 <complexType name="AgreementMethodType" mixed="true">
88 <sequence>
89 <element name="KA-Nonce" minOccurs="0" type="base64Binary"/>
90 <!-- <element ref="ds:DigestMethod" minOccurs="0"/> -->
91 <any namespace="##other" minOccurs="0" maxOccurs="unbounded"/>
92 <element name="OriginatorKeyInfo" minOccurs="0" type="ds:KeyInfoType"/>
93 <element name="RecipientKeyInfo" minOccurs="0" type="ds:KeyInfoType"/>
94 </sequence>
95 <attribute name="Algorithm" type="anyURI" use="required"/>
96 </complexType>
97
98 <!-- End Children of ds:KeyInfo -->
99
100 <element name='ReferenceList'>
101 <complexType>
102 <choice minOccurs='1' maxOccurs='unbounded'>
103 <element name='DataReference' type='xenc:ReferenceType'/>
104 <element name='KeyReference' type='xenc:ReferenceType'/>
105 </choice>
106 </complexType>
107 </element>
108
109 <complexType name='ReferenceType'>
110 <sequence>
111 <any namespace='##other' minOccurs='0' maxOccurs='unbounded'/>
112 </sequence>
113 <attribute name='URI' type='anyURI' use='required'/>
114 </complexType>
115
116
117 <element name='EncryptionProperties' type='xenc:EncryptionPropertiesType'/>
118 <complexType name='EncryptionPropertiesType'>
119 <sequence>
120 <element ref='xenc:EncryptionProperty' maxOccurs='unbounded'/>
121 </sequence>
122 <attribute name='Id' type='ID' use='optional'/>
123 </complexType>
124
125 <element name='EncryptionProperty' type='xenc:EncryptionPropertyType'/>
126 <complexType name='EncryptionPropertyType' mixed='true'>
127 <choice maxOccurs='unbounded'>
128 <any namespace='##other' processContents='lax'/>
129 </choice>
130 <attribute name='Target' type='anyURI' use='optional'/>
131 <attribute name='Id' type='ID' use='optional'/>
132 <anyAttribute namespace="http://www.w3.org/XML/1998/namespace"/>
133 </complexType>
134
135 </schema>
136
1 <?xml version='1.0'?>
2 <?xml-stylesheet href="../2008/09/xsd.xsl" type="text/xsl"?>
3 <xs:schema targetNamespace="http://www.w3.org/XML/1998/namespace"
4 xmlns:xs="http://www.w3.org/2001/XMLSchema"
5 xmlns ="http://www.w3.org/1999/xhtml"
6 xml:lang="en">
7
8 <xs:annotation>
9 <xs:documentation>
10 <div>
11 <h1>About the XML namespace</h1>
12
13 <div class="bodytext">
14 <p>
15 This schema document describes the XML namespace, in a form
16 suitable for import by other schema documents.
17 </p>
18 <p>
19 See <a href="http://www.w3.org/XML/1998/namespace.html">
20 http://www.w3.org/XML/1998/namespace.html</a> and
21 <a href="http://www.w3.org/TR/REC-xml">
22 http://www.w3.org/TR/REC-xml</a> for information
23 about this namespace.
24 </p>
25 <p>
26 Note that local names in this namespace are intended to be
27 defined only by the World Wide Web Consortium or its subgroups.
28 The names currently defined in this namespace are listed below.
29 They should not be used with conflicting semantics by any Working
30 Group, specification, or document instance.
31 </p>
32 <p>
33 See further below in this document for more information about <a
34 href="#usage">how to refer to this schema document from your own
35 XSD schema documents</a> and about <a href="#nsversioning">the
36 namespace-versioning policy governing this schema document</a>.
37 </p>
38 </div>
39 </div>
40 </xs:documentation>
41 </xs:annotation>
42
43 <xs:attribute name="lang">
44 <xs:annotation>
45 <xs:documentation>
46 <div>
47
48 <h3>lang (as an attribute name)</h3>
49 <p>
50 denotes an attribute whose value
51 is a language code for the natural language of the content of
52 any element; its value is inherited. This name is reserved
53 by virtue of its definition in the XML specification.</p>
54
55 </div>
56 <div>
57 <h4>Notes</h4>
58 <p>
59 Attempting to install the relevant ISO 2- and 3-letter
60 codes as the enumerated possible values is probably never
61 going to be a realistic possibility.
62 </p>
63 <p>
64 See BCP 47 at <a href="http://www.rfc-editor.org/rfc/bcp/bcp47.txt">
65 http://www.rfc-editor.org/rfc/bcp/bcp47.txt</a>
66 and the IANA language subtag registry at
67 <a href="http://www.iana.org/assignments/language-subtag-registry">
68 http://www.iana.org/assignments/language-subtag-registry</a>
69 for further information.
70 </p>
71 <p>
72 The union allows for the 'un-declaration' of xml:lang with
73 the empty string.
74 </p>
75 </div>
76 </xs:documentation>
77 </xs:annotation>
78 <xs:simpleType>
79 <xs:union memberTypes="xs:language">
80 <xs:simpleType>
81 <xs:restriction base="xs:string">
82 <xs:enumeration value=""/>
83 </xs:restriction>
84 </xs:simpleType>
85 </xs:union>
86 </xs:simpleType>
87 </xs:attribute>
88
89 <xs:attribute name="space">
90 <xs:annotation>
91 <xs:documentation>
92 <div>
93
94 <h3>space (as an attribute name)</h3>
95 <p>
96 denotes an attribute whose
97 value is a keyword indicating what whitespace processing
98 discipline is intended for the content of the element; its
99 value is inherited. This name is reserved by virtue of its
100 definition in the XML specification.</p>
101
102 </div>
103 </xs:documentation>
104 </xs:annotation>
105 <xs:simpleType>
106 <xs:restriction base="xs:NCName">
107 <xs:enumeration value="default"/>
108 <xs:enumeration value="preserve"/>
109 </xs:restriction>
110 </xs:simpleType>
111 </xs:attribute>
112
113 <xs:attribute name="base" type="xs:anyURI"> <xs:annotation>
114 <xs:documentation>
115 <div>
116
117 <h3>base (as an attribute name)</h3>
118 <p>
119 denotes an attribute whose value
120 provides a URI to be used as the base for interpreting any
121 relative URIs in the scope of the element on which it
122 appears; its value is inherited. This name is reserved
123 by virtue of its definition in the XML Base specification.</p>
124
125 <p>
126 See <a
127 href="http://www.w3.org/TR/xmlbase/">http://www.w3.org/TR/xmlbase/</a>
128 for information about this attribute.
129 </p>
130 </div>
131 </xs:documentation>
132 </xs:annotation>
133 </xs:attribute>
134
135 <xs:attribute name="id" type="xs:ID">
136 <xs:annotation>
137 <xs:documentation>
138 <div>
139
140 <h3>id (as an attribute name)</h3>
141 <p>
142 denotes an attribute whose value
143 should be interpreted as if declared to be of type ID.
144 This name is reserved by virtue of its definition in the
145 xml:id specification.</p>
146
147 <p>
148 See <a
149 href="http://www.w3.org/TR/xml-id/">http://www.w3.org/TR/xml-id/</a>
150 for information about this attribute.
151 </p>
152 </div>
153 </xs:documentation>
154 </xs:annotation>
155 </xs:attribute>
156
157 <xs:attributeGroup name="specialAttrs">
158 <xs:attribute ref="xml:base"/>
159 <xs:attribute ref="xml:lang"/>
160 <xs:attribute ref="xml:space"/>
161 <xs:attribute ref="xml:id"/>
162 </xs:attributeGroup>
163
164 <xs:annotation>
165 <xs:documentation>
166 <div>
167
168 <h3>Father (in any context at all)</h3>
169
170 <div class="bodytext">
171 <p>
172 denotes Jon Bosak, the chair of
173 the original XML Working Group. This name is reserved by
174 the following decision of the W3C XML Plenary and
175 XML Coordination groups:
176 </p>
177 <blockquote>
178 <p>
179 In appreciation for his vision, leadership and
180 dedication the W3C XML Plenary on this 10th day of
181 February, 2000, reserves for Jon Bosak in perpetuity
182 the XML name "xml:Father".
183 </p>
184 </blockquote>
185 </div>
186 </div>
187 </xs:documentation>
188 </xs:annotation>
189
190 <xs:annotation>
191 <xs:documentation>
192 <div xml:id="usage" id="usage">
193 <h2><a name="usage">About this schema document</a></h2>
194
195 <div class="bodytext">
196 <p>
197 This schema defines attributes and an attribute group suitable
198 for use by schemas wishing to allow <code>xml:base</code>,
199 <code>xml:lang</code>, <code>xml:space</code> or
200 <code>xml:id</code> attributes on elements they define.
201 </p>
202 <p>
203 To enable this, such a schema must import this schema for
204 the XML namespace, e.g. as follows:
205 </p>
206 <pre>
207 &lt;schema . . .>
208 . . .
209 &lt;import namespace="http://www.w3.org/XML/1998/namespace"
210 schemaLocation="http://www.w3.org/2001/xml.xsd"/>
211 </pre>
212 <p>
213 or
214 </p>
215 <pre>
216 &lt;import namespace="http://www.w3.org/XML/1998/namespace"
217 schemaLocation="http://www.w3.org/2009/01/xml.xsd"/>
218 </pre>
219 <p>
220 Subsequently, qualified reference to any of the attributes or the
221 group defined below will have the desired effect, e.g.
222 </p>
223 <pre>
224 &lt;type . . .>
225 . . .
226 &lt;attributeGroup ref="xml:specialAttrs"/>
227 </pre>
228 <p>
229 will define a type which will schema-validate an instance element
230 with any of those attributes.
231 </p>
232 </div>
233 </div>
234 </xs:documentation>
235 </xs:annotation>
236
237 <xs:annotation>
238 <xs:documentation>
239 <div id="nsversioning" xml:id="nsversioning">
240 <h2><a name="nsversioning">Versioning policy for this schema document</a></h2>
241 <div class="bodytext">
242 <p>
243 In keeping with the XML Schema WG's standard versioning
244 policy, this schema document will persist at
245 <a href="http://www.w3.org/2009/01/xml.xsd">
246 http://www.w3.org/2009/01/xml.xsd</a>.
247 </p>
248 <p>
249 At the date of issue it can also be found at
250 <a href="http://www.w3.org/2001/xml.xsd">
251 http://www.w3.org/2001/xml.xsd</a>.
252 </p>
253 <p>
254 The schema document at that URI may however change in the future,
255 in order to remain compatible with the latest version of XML
256 Schema itself, or with the XML namespace itself. In other words,
257 if the XML Schema or XML namespaces change, the version of this
258 document at <a href="http://www.w3.org/2001/xml.xsd">
259 http://www.w3.org/2001/xml.xsd
260 </a>
261 will change accordingly; the version at
262 <a href="http://www.w3.org/2009/01/xml.xsd">
263 http://www.w3.org/2009/01/xml.xsd
264 </a>
265 will not change.
266 </p>
267 <p>
268 Previous dated (and unchanging) versions of this schema
269 document are at:
270 </p>
271 <ul>
272 <li><a href="http://www.w3.org/2009/01/xml.xsd">
273 http://www.w3.org/2009/01/xml.xsd</a></li>
274 <li><a href="http://www.w3.org/2007/08/xml.xsd">
275 http://www.w3.org/2007/08/xml.xsd</a></li>
276 <li><a href="http://www.w3.org/2004/10/xml.xsd">
277 http://www.w3.org/2004/10/xml.xsd</a></li>
278 <li><a href="http://www.w3.org/2001/03/xml.xsd">
279 http://www.w3.org/2001/03/xml.xsd</a></li>
280 </ul>
281 </div>
282 </div>
283 </xs:documentation>
284 </xs:annotation>
285
286 </xs:schema>
287
1 <?xml version="1.0" encoding="utf-8"?>
2
3 <!-- Schema for XML Signatures
4 http://www.w3.org/2000/09/xmldsig#
5 $Revision: 1.1 $ on $Date: 2002/02/08 20:32:26 $ by $Author: reagle $
6
7 Copyright 2001 The Internet Society and W3C (Massachusetts Institute
8 of Technology, Institut National de Recherche en Informatique et en
9 Automatique, Keio University). All Rights Reserved.
10 http://www.w3.org/Consortium/Legal/
11
12 This document is governed by the W3C Software License [1] as described
13 in the FAQ [2].
14
15 [1] http://www.w3.org/Consortium/Legal/copyright-software-19980720
16 [2] http://www.w3.org/Consortium/Legal/IPR-FAQ-20000620.html#DTD
17 -->
18
19
20 <schema xmlns="http://www.w3.org/2001/XMLSchema"
21 xmlns:ds="http://www.w3.org/2000/09/xmldsig#"
22 targetNamespace="http://www.w3.org/2000/09/xmldsig#"
23 version="0.1" elementFormDefault="qualified">
24
25 <!-- Basic Types Defined for Signatures -->
26
27 <simpleType name="CryptoBinary">
28 <restriction base="base64Binary">
29 </restriction>
30 </simpleType>
31
32 <!-- Start Signature -->
33
34 <element name="Signature" type="ds:SignatureType"/>
35 <complexType name="SignatureType">
36 <sequence>
37 <element ref="ds:SignedInfo"/>
38 <element ref="ds:SignatureValue"/>
39 <element ref="ds:KeyInfo" minOccurs="0"/>
40 <element ref="ds:Object" minOccurs="0" maxOccurs="unbounded"/>
41 </sequence>
42 <attribute name="Id" type="ID" use="optional"/>
43 </complexType>
44
45 <element name="SignatureValue" type="ds:SignatureValueType"/>
46 <complexType name="SignatureValueType">
47 <simpleContent>
48 <extension base="base64Binary">
49 <attribute name="Id" type="ID" use="optional"/>
50 </extension>
51 </simpleContent>
52 </complexType>
53
54 <!-- Start SignedInfo -->
55
56 <element name="SignedInfo" type="ds:SignedInfoType"/>
57 <complexType name="SignedInfoType">
58 <sequence>
59 <element ref="ds:CanonicalizationMethod"/>
60 <element ref="ds:SignatureMethod"/>
61 <element ref="ds:Reference" maxOccurs="unbounded"/>
62 </sequence>
63 <attribute name="Id" type="ID" use="optional"/>
64 </complexType>
65
66 <element name="CanonicalizationMethod" type="ds:CanonicalizationMethodType"/>
67 <complexType name="CanonicalizationMethodType" mixed="true">
68 <sequence>
69 <any namespace="##any" minOccurs="0" maxOccurs="unbounded"/>
70 <!-- (0,unbounded) elements from (1,1) namespace -->
71 </sequence>
72 <attribute name="Algorithm" type="anyURI" use="required"/>
73 </complexType>
74
75 <element name="SignatureMethod" type="ds:SignatureMethodType"/>
76 <complexType name="SignatureMethodType" mixed="true">
77 <sequence>
78 <element name="HMACOutputLength" minOccurs="0" type="ds:HMACOutputLengthType"/>
79 <any namespace="##other" minOccurs="0" maxOccurs="unbounded"/>
80 <!-- (0,unbounded) elements from (1,1) external namespace -->
81 </sequence>
82 <attribute name="Algorithm" type="anyURI" use="required"/>
83 </complexType>
84
85 <!-- Start Reference -->
86
87 <element name="Reference" type="ds:ReferenceType"/>
88 <complexType name="ReferenceType">
89 <sequence>
90 <element ref="ds:Transforms" minOccurs="0"/>
91 <element ref="ds:DigestMethod"/>
92 <element ref="ds:DigestValue"/>
93 </sequence>
94 <attribute name="Id" type="ID" use="optional"/>
95 <attribute name="URI" type="anyURI" use="optional"/>
96 <attribute name="Type" type="anyURI" use="optional"/>
97 </complexType>
98
99 <element name="Transforms" type="ds:TransformsType"/>
100 <complexType name="TransformsType">
101 <sequence>
102 <element ref="ds:Transform" maxOccurs="unbounded"/>
103 </sequence>
104 </complexType>
105
106 <element name="Transform" type="ds:TransformType"/>
107 <complexType name="TransformType" mixed="true">
108 <choice minOccurs="0" maxOccurs="unbounded">
109 <any namespace="##other" processContents="lax"/>
110 <!-- (1,1) elements from (0,unbounded) namespaces -->
111 <element name="XPath" type="string"/>
112 </choice>
113 <attribute name="Algorithm" type="anyURI" use="required"/>
114 </complexType>
115
116 <!-- End Reference -->
117
118 <element name="DigestMethod" type="ds:DigestMethodType"/>
119 <complexType name="DigestMethodType" mixed="true">
120 <sequence>
121 <any namespace="##other" processContents="lax" minOccurs="0" maxOccurs="unbounded"/>
122 </sequence>
123 <attribute name="Algorithm" type="anyURI" use="required"/>
124 </complexType>
125
126 <element name="DigestValue" type="ds:DigestValueType"/>
127 <simpleType name="DigestValueType">
128 <restriction base="base64Binary"/>
129 </simpleType>
130
131 <!-- End SignedInfo -->
132
133 <!-- Start KeyInfo -->
134
135 <element name="KeyInfo" type="ds:KeyInfoType"/>
136 <complexType name="KeyInfoType" mixed="true">
137 <choice maxOccurs="unbounded">
138 <element ref="ds:KeyName"/>
139 <element ref="ds:KeyValue"/>
140 <element ref="ds:RetrievalMethod"/>
141 <element ref="ds:X509Data"/>
142 <element ref="ds:PGPData"/>
143 <element ref="ds:SPKIData"/>
144 <element ref="ds:MgmtData"/>
145 <any processContents="lax" namespace="##other"/>
146 <!-- (1,1) elements from (0,unbounded) namespaces -->
147 </choice>
148 <attribute name="Id" type="ID" use="optional"/>
149 </complexType>
150
151 <element name="KeyName" type="string"/>
152 <element name="MgmtData" type="string"/>
153
154 <element name="KeyValue" type="ds:KeyValueType"/>
155 <complexType name="KeyValueType" mixed="true">
156 <choice>
157 <element ref="ds:DSAKeyValue"/>
158 <element ref="ds:RSAKeyValue"/>
159 <any namespace="##other" processContents="lax"/>
160 </choice>
161 </complexType>
162
163 <element name="RetrievalMethod" type="ds:RetrievalMethodType"/>
164 <complexType name="RetrievalMethodType">
165 <sequence>
166 <element ref="ds:Transforms" minOccurs="0"/>
167 </sequence>
168 <attribute name="URI" type="anyURI"/>
169 <attribute name="Type" type="anyURI" use="optional"/>
170 </complexType>
171
172 <!-- Start X509Data -->
173
174 <element name="X509Data" type="ds:X509DataType"/>
175 <complexType name="X509DataType">
176 <sequence maxOccurs="unbounded">
177 <choice>
178 <element name="X509IssuerSerial" type="ds:X509IssuerSerialType"/>
179 <element name="X509SKI" type="base64Binary"/>
180 <element name="X509SubjectName" type="string"/>
181 <element name="X509Certificate" type="base64Binary"/>
182 <element name="X509CRL" type="base64Binary"/>
183 <any namespace="##other" processContents="lax"/>
184 </choice>
185 </sequence>
186 </complexType>
187
188 <complexType name="X509IssuerSerialType">
189 <sequence>
190 <element name="X509IssuerName" type="string"/>
191 <element name="X509SerialNumber" type="string"/>
192 </sequence>
193 </complexType>
194
195 <!-- End X509Data -->
196
197 <!-- Begin PGPData -->
198
199 <element name="PGPData" type="ds:PGPDataType"/>
200 <complexType name="PGPDataType">
201 <choice>
202 <sequence>
203 <element name="PGPKeyID" type="base64Binary"/>
204 <element name="PGPKeyPacket" type="base64Binary" minOccurs="0"/>
205 <any namespace="##other" processContents="lax" minOccurs="0"
206 maxOccurs="unbounded"/>
207 </sequence>
208 <sequence>
209 <element name="PGPKeyPacket" type="base64Binary"/>
210 <any namespace="##other" processContents="lax" minOccurs="0"
211 maxOccurs="unbounded"/>
212 </sequence>
213 </choice>
214 </complexType>
215
216 <!-- End PGPData -->
217
218 <!-- Begin SPKIData -->
219
220 <element name="SPKIData" type="ds:SPKIDataType"/>
221 <complexType name="SPKIDataType">
222 <sequence maxOccurs="unbounded">
223 <element name="SPKISexp" type="base64Binary"/>
224 <any namespace="##other" processContents="lax" minOccurs="0"/>
225 </sequence>
226 </complexType>
227
228 <!-- End SPKIData -->
229
230 <!-- End KeyInfo -->
231
232 <!-- Start Object (Manifest, SignatureProperty) -->
233
234 <element name="Object" type="ds:ObjectType"/>
235 <complexType name="ObjectType" mixed="true">
236 <sequence minOccurs="0" maxOccurs="unbounded">
237 <any namespace="##any" processContents="lax"/>
238 </sequence>
239 <attribute name="Id" type="ID" use="optional"/>
240 <attribute name="MimeType" type="string" use="optional"/> <!-- add a grep facet -->
241 <attribute name="Encoding" type="anyURI" use="optional"/>
242 </complexType>
243
244 <element name="Manifest" type="ds:ManifestType"/>
245 <complexType name="ManifestType">
246 <sequence>
247 <element ref="ds:Reference" maxOccurs="unbounded"/>
248 </sequence>
249 <attribute name="Id" type="ID" use="optional"/>
250 </complexType>
251
252 <element name="SignatureProperties" type="ds:SignaturePropertiesType"/>
253 <complexType name="SignaturePropertiesType">
254 <sequence>
255 <element ref="ds:SignatureProperty" maxOccurs="unbounded"/>
256 </sequence>
257 <attribute name="Id" type="ID" use="optional"/>
258 </complexType>
259
260 <element name="SignatureProperty" type="ds:SignaturePropertyType"/>
261 <complexType name="SignaturePropertyType" mixed="true">
262 <choice maxOccurs="unbounded">
263 <any namespace="##other" processContents="lax"/>
264 <!-- (1,1) elements from (1,unbounded) namespaces -->
265 </choice>
266 <attribute name="Target" type="anyURI" use="required"/>
267 <attribute name="Id" type="ID" use="optional"/>
268 </complexType>
269
270 <!-- End Object (Manifest, SignatureProperty) -->
271
272 <!-- Start Algorithm Parameters -->
273
274 <simpleType name="HMACOutputLengthType">
275 <restriction base="integer"/>
276 </simpleType>
277
278 <!-- Start KeyValue Element-types -->
279
280 <element name="DSAKeyValue" type="ds:DSAKeyValueType"/>
281 <complexType name="DSAKeyValueType">
282 <sequence>
283 <sequence minOccurs="0">
284 <element name="P" type="ds:CryptoBinary"/>
285 <element name="Q" type="ds:CryptoBinary"/>
286 </sequence>
287 <element name="G" type="ds:CryptoBinary" minOccurs="0"/>
288 <element name="Y" type="ds:CryptoBinary"/>
289 <element name="J" type="ds:CryptoBinary" minOccurs="0"/>
290 <sequence minOccurs="0">
291 <element name="Seed" type="ds:CryptoBinary"/>
292 <element name="PgenCounter" type="ds:CryptoBinary"/>
293 </sequence>
294 </sequence>
295 </complexType>
296
297 <element name="RSAKeyValue" type="ds:RSAKeyValueType"/>
298 <complexType name="RSAKeyValueType">
299 <sequence>
300 <element name="Modulus" type="ds:CryptoBinary"/>
301 <element name="Exponent" type="ds:CryptoBinary"/>
302 </sequence>
303 </complexType>
304
305 <!-- End KeyValue Element-types -->
306
307 <!-- End Signature -->
308
309 </schema>
1 {
2 "php-saml": {
3 "version": "2.19.1",
4 "released": "02/03/2021"
5 }
6 }
1 <phpunit bootstrap="./tests/bootstrap.php" colors="true">
2 <testsuites>
3 <testsuite name="OneLogin PHP-SAML Test Suite">
4 <directory>./tests/src</directory>
5 </testsuite>
6 </testsuites>
7 <filter>
8 <whitelist processUncoveredFilesFromWhitelist="true">
9 <directory>./lib</directory>
10 </whitelist>
11 </filter>
12 <logging>
13 <log type="coverage-html" target="./tests/build/coverage" charset="UTF-8" yui="true" highlight="false" lowUpperBound="35" highLowerBound="70"/>
14 <log type="test-xml" target="./tests/build/logfile.xml" logIncompleteSkipped="false"/>
15 <log type="coverage-clover" target="./tests/build/logs/clover.xml"/>
16 <log type="coverage-php" target="./tests/build/logs/coverage.cov"/>
17 </logging>
18 </phpunit>
1 <?php
2
3 $settings = array (
4 // If 'strict' is True, then the PHP Toolkit will reject unsigned
5 // or unencrypted messages if it expects them signed or encrypted
6 // Also will reject the messages if not strictly follow the SAML
7 // standard: Destination, NameId, Conditions ... are validated too.
8 'strict' => true,
9
10 // Enable debug mode (to print errors)
11 'debug' => false,
12
13 // Set a BaseURL to be used instead of try to guess
14 // the BaseURL of the view that process the SAML Message.
15 // Ex. http://sp.example.com/
16 // http://example.com/sp/
17 'baseurl' => '',
18
19 // Service Provider Data that we are deploying
20 'sp' => array (
21 // Identifier of the SP entity (must be a URI)
22 'entityId' => '',
23 // Specifies info about where and how the <AuthnResponse> message MUST be
24 // returned to the requester, in this case our SP.
25 'assertionConsumerService' => array (
26 // URL Location where the <Response> from the IdP will be returned
27 'url' => '',
28 // SAML protocol binding to be used when returning the <Response>
29 // message. Onelogin Toolkit supports for this endpoint the
30 // HTTP-POST binding only
31 'binding' => 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST',
32 ),
33 // If you need to specify requested attributes, set a
34 // attributeConsumingService. nameFormat, attributeValue and
35 // friendlyName can be omitted. Otherwise remove this section.
36 "attributeConsumingService"=> array(
37 "serviceName" => "SP test",
38 "serviceDescription" => "Test Service",
39 "requestedAttributes" => array(
40 array(
41 "name" => "",
42 "isRequired" => false,
43 "nameFormat" => "",
44 "friendlyName" => "",
45 "attributeValue" => ""
46 )
47 )
48 ),
49 // Specifies info about where and how the <Logout Response> message MUST be
50 // returned to the requester, in this case our SP.
51 'singleLogoutService' => array (
52 // URL Location where the <Response> from the IdP will be returned
53 'url' => '',
54 // SAML protocol binding to be used when returning the <Response>
55 // message. Onelogin Toolkit supports for this endpoint the
56 // HTTP-Redirect binding only
57 'binding' => 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect',
58 ),
59 // Specifies constraints on the name identifier to be used to
60 // represent the requested subject.
61 // Take a look on lib/Saml2/Constants.php to see the NameIdFormat supported
62 'NameIDFormat' => 'urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified',
63
64 // Usually x509cert and privateKey of the SP are provided by files placed at
65 // the certs folder. But we can also provide them with the following parameters
66 'x509cert' => '',
67 'privateKey' => '',
68
69 /*
70 * Key rollover
71 * If you plan to update the SP x509cert and privateKey
72 * you can define here the new x509cert and it will be
73 * published on the SP metadata so Identity Providers can
74 * read them and get ready for rollover.
75 */
76 // 'x509certNew' => '',
77 ),
78
79 // Identity Provider Data that we want connect with our SP
80 'idp' => array (
81 // Identifier of the IdP entity (must be a URI)
82 'entityId' => '',
83 // SSO endpoint info of the IdP. (Authentication Request protocol)
84 'singleSignOnService' => array (
85 // URL Target of the IdP where the SP will send the Authentication Request Message
86 'url' => '',
87 // SAML protocol binding to be used when returning the <Response>
88 // message. Onelogin Toolkit supports for this endpoint the
89 // HTTP-Redirect binding only
90 'binding' => 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect',
91 ),
92 // SLO endpoint info of the IdP.
93 'singleLogoutService' => array (
94 // URL Location of the IdP where the SP will send the SLO Request
95 'url' => '',
96 // URL location of the IdP where the SP will send the SLO Response (ResponseLocation)
97 // if not set, url for the SLO Request will be used
98 'responseUrl' => '',
99 // SAML protocol binding to be used when returning the <Response>
100 // message. Onelogin Toolkit supports for this endpoint the
101 // HTTP-Redirect binding only
102 'binding' => 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect',
103 ),
104 // Public x509 certificate of the IdP
105 'x509cert' => '',
106 /*
107 * Instead of use the whole x509cert you can use a fingerprint in
108 * order to validate the SAMLResponse, but we don't recommend to use
109 * that method on production since is exploitable by a collision
110 * attack.
111 * (openssl x509 -noout -fingerprint -in "idp.crt" to generate it,
112 * or add for example the -sha256 , -sha384 or -sha512 parameter)
113 *
114 * If a fingerprint is provided, then the certFingerprintAlgorithm is required in order to
115 * let the toolkit know which Algorithm was used. Possible values: sha1, sha256, sha384 or sha512
116 * 'sha1' is the default value.
117 */
118 // 'certFingerprint' => '',
119 // 'certFingerprintAlgorithm' => 'sha1',
120
121 /* In some scenarios the IdP uses different certificates for
122 * signing/encryption, or is under key rollover phase and more
123 * than one certificate is published on IdP metadata.
124 * In order to handle that the toolkit offers that parameter.
125 * (when used, 'x509cert' and 'certFingerprint' values are
126 * ignored).
127 */
128 // 'x509certMulti' => array(
129 // 'signing' => array(
130 // 0 => '<cert1-string>',
131 // ),
132 // 'encryption' => array(
133 // 0 => '<cert2-string>',
134 // )
135 // ),
136 ),
137 );
1 <?php
2
3 // autoload.php @generated by Composer
4
5 require_once __DIR__ . '/composer/autoload_real.php';
6
7 return ComposerAutoloaderInit39845345b23b103682945e29721176f1::getLoader();
1
2 Copyright (c) Nils Adermann, Jordi Boggiano
3
4 Permission is hereby granted, free of charge, to any person obtaining a copy
5 of this software and associated documentation files (the "Software"), to deal
6 in the Software without restriction, including without limitation the rights
7 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 copies of the Software, and to permit persons to whom the Software is furnished
9 to do so, subject to the following conditions:
10
11 The above copyright notice and this permission notice shall be included in all
12 copies or substantial portions of the Software.
13
14 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 THE SOFTWARE.
21
1 <?php
2
3 // autoload_classmap.php @generated by Composer
4
5 $vendorDir = dirname(dirname(__FILE__));
6 $baseDir = dirname($vendorDir);
7
8 return array(
9 'OneLogin_Saml2_Auth' => $baseDir . '/lib/Saml2/Auth.php',
10 'OneLogin_Saml2_AuthnRequest' => $baseDir . '/lib/Saml2/AuthnRequest.php',
11 'OneLogin_Saml2_Constants' => $baseDir . '/lib/Saml2/Constants.php',
12 'OneLogin_Saml2_Error' => $baseDir . '/lib/Saml2/Error.php',
13 'OneLogin_Saml2_IdPMetadataParser' => $baseDir . '/lib/Saml2/IdPMetadataParser.php',
14 'OneLogin_Saml2_LogoutRequest' => $baseDir . '/lib/Saml2/LogoutRequest.php',
15 'OneLogin_Saml2_LogoutResponse' => $baseDir . '/lib/Saml2/LogoutResponse.php',
16 'OneLogin_Saml2_Metadata' => $baseDir . '/lib/Saml2/Metadata.php',
17 'OneLogin_Saml2_Response' => $baseDir . '/lib/Saml2/Response.php',
18 'OneLogin_Saml2_Settings' => $baseDir . '/lib/Saml2/Settings.php',
19 'OneLogin_Saml2_Utils' => $baseDir . '/lib/Saml2/Utils.php',
20 'OneLogin_Saml2_ValidationError' => $baseDir . '/lib/Saml2/Error.php',
21 'XMLSecEnc' => $baseDir . '/extlib/xmlseclibs/xmlseclibs.php',
22 'XMLSecurityDSig' => $baseDir . '/extlib/xmlseclibs/xmlseclibs.php',
23 'XMLSecurityKey' => $baseDir . '/extlib/xmlseclibs/xmlseclibs.php',
24 );
1 <?php
2
3 // autoload_namespaces.php @generated by Composer
4
5 $vendorDir = dirname(dirname(__FILE__));
6 $baseDir = dirname($vendorDir);
7
8 return array(
9 );
1 <?php
2
3 // autoload_psr4.php @generated by Composer
4
5 $vendorDir = dirname(dirname(__FILE__));
6 $baseDir = dirname($vendorDir);
7
8 return array(
9 );
1 <?php
2
3 // autoload_real.php @generated by Composer
4
5 class ComposerAutoloaderInit39845345b23b103682945e29721176f1
6 {
7 private static $loader;
8
9 public static function loadClassLoader($class)
10 {
11 if ('Composer\Autoload\ClassLoader' === $class) {
12 require __DIR__ . '/ClassLoader.php';
13 }
14 }
15
16 /**
17 * @return \Composer\Autoload\ClassLoader
18 */
19 public static function getLoader()
20 {
21 if (null !== self::$loader) {
22 return self::$loader;
23 }
24
25 spl_autoload_register(array('ComposerAutoloaderInit39845345b23b103682945e29721176f1', 'loadClassLoader'), true, true);
26 self::$loader = $loader = new \Composer\Autoload\ClassLoader();
27 spl_autoload_unregister(array('ComposerAutoloaderInit39845345b23b103682945e29721176f1', 'loadClassLoader'));
28
29 $useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
30 if ($useStaticLoader) {
31 require_once __DIR__ . '/autoload_static.php';
32
33 call_user_func(\Composer\Autoload\ComposerStaticInit39845345b23b103682945e29721176f1::getInitializer($loader));
34 } else {
35 $map = require __DIR__ . '/autoload_namespaces.php';
36 foreach ($map as $namespace => $path) {
37 $loader->set($namespace, $path);
38 }
39
40 $map = require __DIR__ . '/autoload_psr4.php';
41 foreach ($map as $namespace => $path) {
42 $loader->setPsr4($namespace, $path);
43 }
44
45 $classMap = require __DIR__ . '/autoload_classmap.php';
46 if ($classMap) {
47 $loader->addClassMap($classMap);
48 }
49 }
50
51 $loader->register(true);
52
53 return $loader;
54 }
55 }
1 <?php
2
3 // autoload_static.php @generated by Composer
4
5 namespace Composer\Autoload;
6
7 class ComposerStaticInit39845345b23b103682945e29721176f1
8 {
9 public static $classMap = array (
10 'OneLogin_Saml2_Auth' => __DIR__ . '/../..' . '/lib/Saml2/Auth.php',
11 'OneLogin_Saml2_AuthnRequest' => __DIR__ . '/../..' . '/lib/Saml2/AuthnRequest.php',
12 'OneLogin_Saml2_Constants' => __DIR__ . '/../..' . '/lib/Saml2/Constants.php',
13 'OneLogin_Saml2_Error' => __DIR__ . '/../..' . '/lib/Saml2/Error.php',
14 'OneLogin_Saml2_IdPMetadataParser' => __DIR__ . '/../..' . '/lib/Saml2/IdPMetadataParser.php',
15 'OneLogin_Saml2_LogoutRequest' => __DIR__ . '/../..' . '/lib/Saml2/LogoutRequest.php',
16 'OneLogin_Saml2_LogoutResponse' => __DIR__ . '/../..' . '/lib/Saml2/LogoutResponse.php',
17 'OneLogin_Saml2_Metadata' => __DIR__ . '/../..' . '/lib/Saml2/Metadata.php',
18 'OneLogin_Saml2_Response' => __DIR__ . '/../..' . '/lib/Saml2/Response.php',
19 'OneLogin_Saml2_Settings' => __DIR__ . '/../..' . '/lib/Saml2/Settings.php',
20 'OneLogin_Saml2_Utils' => __DIR__ . '/../..' . '/lib/Saml2/Utils.php',
21 'OneLogin_Saml2_ValidationError' => __DIR__ . '/../..' . '/lib/Saml2/Error.php',
22 'XMLSecEnc' => __DIR__ . '/../..' . '/extlib/xmlseclibs/xmlseclibs.php',
23 'XMLSecurityDSig' => __DIR__ . '/../..' . '/extlib/xmlseclibs/xmlseclibs.php',
24 'XMLSecurityKey' => __DIR__ . '/../..' . '/extlib/xmlseclibs/xmlseclibs.php',
25 );
26
27 public static function getInitializer(ClassLoader $loader)
28 {
29 return \Closure::bind(function () use ($loader) {
30 $loader->classMap = ComposerStaticInit39845345b23b103682945e29721176f1::$classMap;
31
32 }, null, ClassLoader::class);
33 }
34 }
1 <?php
2
3 namespace Wpo\Pages;
4
5 use \Wpo\Core\WordPress_Helpers;
6 use \Wpo\Services\Options_Service;
7
8 // Prevent public access to this script
9 defined('ABSPATH') or die();
10
11 if (!class_exists('\Wpo\Pages\Wizard_Page')) {
12
13 class Wizard_Page
14 {
15
16 /**
17 * Definition of the Options page (following default Wordpress practice).
18 *
19 * @since 2.0
20 *
21 * @return void
22 */
23 public static function add_management_page()
24 {
25 /**
26 * @since 21.9 Administrators can restrict access to the WPO365 configuration
27 */
28
29 if (defined('WPO_ADMINS')) {
30 $admins = constant('WPO_ADMINS');
31
32 if (!is_array($admins)) {
33 return;
34 }
35
36 $admins = array_flip($admins);
37 $admins = array_change_key_case($admins);
38 $current_user = wp_get_current_user();
39
40 if (!($current_user instanceof \WP_User)) {
41 return;
42 }
43
44 $user_login = strtolower($current_user->user_login);
45
46 if (!array_key_exists($user_login, $admins)) {
47 return;
48 }
49 }
50
51 // Don't add the WPO365 wizard in the subsite admin when subsite options has not been configured
52 if (
53 is_multisite()
54 && !is_network_admin()
55 && false === Options_Service::mu_use_subsite_options()
56 ) {
57 return;
58 }
59
60 add_menu_page(
61 'WPO365',
62 'WPO365',
63 'delete_users',
64 'wpo365-wizard',
65 '\Wpo\Pages\Wizard_Page::wpo365_wizard_page'
66 );
67 }
68
69 /**
70 *
71 */
72 public static function wpo365_wizard_page()
73 {
74 ob_start();
75 include($GLOBALS['WPO_CONFIG']['plugin_dir'] . '/templates/wizard.php');
76 $content = ob_get_clean();
77 echo '' . wp_kses($content, WordPress_Helpers::get_allowed_html());
78 }
79 }
80 }
1 <?php
2
3 namespace Wpo\Services;
4
5 // Prevent public access to this script
6 defined( 'ABSPATH' ) or die( );
7
8 if ( !class_exists( '\Wpo\Services\Dependency_Service' ) ) {
9
10 class Dependency_Service {
11
12 private static $instance = null;
13
14 private $dependencies = array();
15
16 private function __construct() {
17 }
18
19 public static function get_instance() {
20
21 if ( empty( self::$instance ) ) {
22 self::$instance = new Dependency_Service();
23 }
24
25 return self::$instance;
26 }
27
28 public function add( $name, $dependency ) {
29 $this->dependencies[ $name ] = $dependency;
30 }
31
32 public function get( $request_id, $name ) {
33
34 if ( array_key_exists( $name, $this->dependencies ) ) {
35 return $this->dependencies[ $name ];
36 }
37
38 return false;
39 }
40
41 public function remove( $request_id, $name ) {
42
43 if ( array_key_exists( $name, $this->dependencies ) ) {
44 unset( $this->dependencies[ $name ] );
45 }
46 }
47 }
48 }
...\ No newline at end of file ...\ No newline at end of file
1 <?php
2
3 namespace Wpo\Services;
4
5 use \Wpo\Core\Extensions_Helpers;
6 use \Wpo\Core\WordPress_Helpers;
7 use \Wpo\Services\Id_Token_Service;
8 use \Wpo\Services\Options_Service;
9
10 // Prevent public access to this script
11 defined('ABSPATH') or die();
12
13 if (!class_exists('\Wpo\Services\Error_Service')) {
14
15 class Error_Service
16 {
17
18 const AADAPPREG_ERROR = 'AADAPPREG_ERROR';
19 const BASIC_VERSION = 'BASIC_VERSION';
20 const CHECK_LOG = 'CHECK_LOG';
21 const DEACTIVATED = 'DEACTIVATED';
22 const DUAL_LOGIN = 'DUAL_LOGIN';
23 const DUAL_LOGIN_V2 = 'DUAL_LOGIN_V2';
24 const ID_TOKEN_ERROR = 'ID_TOKEN_ERROR';
25 const ID_TOKEN_AUD = 'ID_TOKEN_AUD';
26 const LOGGED_OUT = 'LOGGED_OUT';
27 const NOT_CONFIGURED = 'NOT_CONFIGURED';
28 const NOT_FROM_DOMAIN = 'NOT_FROM_DOMAIN';
29 const NOT_IN_GROUP = 'NOT_IN_GROUP';
30 const SAML2_ERROR = 'SAML2_ERROR';
31 const TAMPERED_WITH = 'TAMPERED_WITH';
32 const USER_NOT_FOUND = 'USER_NOT_FOUND';
33
34 /**
35 * Checks for errors in the login messages container and display and unset immediatly after if any
36 *
37 * @since 1.0
38 * @return void
39 */
40 public static function check_for_login_messages($message)
41 {
42
43 if (!isset($_GET['login_errors'])) {
44 return $message;
45 }
46
47 // Using $_GET here since wp_query is not loaded on login page
48 $login_error_codes = sanitize_text_field($_GET['login_errors']);
49
50 $result = '';
51
52 foreach (explode(',', $login_error_codes) as $login_error_code) {
53
54 $error_message = self::get_error_message($login_error_code);
55
56 if (empty($error_message)) {
57 continue;
58 }
59
60 $result .= '<p class="message">' . $error_message . '</p><br />';
61 }
62
63 // Return messages to display to hook
64 return $result;
65 }
66
67 /**
68 * Tries to get an error message for the error code provided either from
69 * the options or else from the hard coded backup dictionary provided.
70 *
71 * @since 0.1
72 *
73 * @param string $error_code Error code
74 * @return string Error message
75 */
76 public static function get_error_message($error_code)
77 {
78
79 $deprecated_error_messages = array(
80 self::CHECK_LOG => 'Please contact your System Administrator and check log file.',
81 self::DEACTIVATED => 'Account deactivated.',
82 self::DUAL_LOGIN => 'Alternatively, you can click the following link to sign into this website with your corporate <a href="__##OAUTH_URL##__">network login (Office 365)</a>',
83 self::DUAL_LOGIN_V2 => 'Alternatively, you can click the following link to sign into this website with your corporate <span class="wpo365-dual-login-notice" style="cursor: pointer; text-decoration: underline; color: #000CD" onclick="window.wpo365.pintraRedirect.toMsOnline()">network login (Office 365)</span>',
84 self::ID_TOKEN_ERROR => 'Your ID token could not be processed. Please contact your System Administrator.',
85 self::ID_TOKEN_AUD => 'The ID token is intended for a different audience. Please contact your System Administrator.',
86 self::LOGGED_OUT => 'You are now logged out.',
87 self::NOT_CONFIGURED => 'Wordpress + Office 365 login not configured yet. Please contact your System Administrator.',
88 self::NOT_FROM_DOMAIN => 'Access Denied. Please contact your System Administrator.',
89 self::NOT_IN_GROUP => 'User not in group. Please contact your System Administrator.',
90 self::SAML2_ERROR => 'SAML authentication error',
91 self::TAMPERED_WITH => 'Your login might be tampered with. Please contact your System Administrator.',
92 self::USER_NOT_FOUND => 'Could not create or retrieve your login. Please contact your System Administrator.',
93 );
94
95 $error_messages = array(
96 self::AADAPPREG_ERROR => __('Could not create or retrieve your login. Most likely the authentication response received from Microsoft does not contain an email address. Consult the <a target="_blank" href="https://www.wpo365.com/troubleshooting-the-wpo365-login-plugin/#PARSING_ERROR">online documentation</a> for details.', 'wpo365-login'),
97 self::BASIC_VERSION => __('The BASIC edition of the WordPress + Office 365 plugin does not automatically create new users. See the following <a href="https://www.wpo365.com/basic-edition/">online documentation</a> for more info.', 'wpo365-login'),
98 self::CHECK_LOG => __('Please contact your System Administrator and check log file.', 'wpo365-login'),
99 self::DEACTIVATED => __('Account deactivated.', 'wpo365-login'),
100 self::DUAL_LOGIN => __('Alternatively, you can click the following link to sign into this website with your corporate <a href="__##OAUTH_URL##__">network login (Office 365)</a>', 'wpo365-login'),
101 self::DUAL_LOGIN_V2 => __('Alternatively, you can click the following link to sign into this website with your corporate <span class="wpo365-dual-login-notice" style="cursor: pointer; text-decoration: underline; color: #000CD" onclick="window.wpo365.pintraRedirect.toMsOnline()">network login (Office 365)</span>', 'wpo365-login'),
102 self::ID_TOKEN_ERROR => __('Your ID token could not be processed. Please contact your System Administrator.', 'wpo365-login'),
103 self::ID_TOKEN_AUD => __('The ID token is intended for a different audience. Please contact your System Administrator.', 'wpo365_login'),
104 self::LOGGED_OUT => __('You are now logged out.', 'wpo365-login'),
105 self::NOT_CONFIGURED => __('Wordpress + Office 365 login not configured yet. Please contact your System Administrator.', 'wpo365-login'),
106 self::NOT_FROM_DOMAIN => __('Access Denied. Please contact your System Administrator.', 'wpo365-login'),
107 self::NOT_IN_GROUP => __('Access Denied. Please contact your System Administrator.', 'wpo365-login'),
108 self::SAML2_ERROR => __('SAML authentication error.', 'wpo365-login'),
109 self::TAMPERED_WITH => __('Your login might be tampered with. Please contact your System Administrator.', 'wpo365-login'),
110 self::USER_NOT_FOUND => __('Could not create or retrieve your login. Please contact your System Administrator.', 'wpo365-login'),
111 );
112
113 if (class_exists('\Wpo\Services\Options_Service')) {
114 $error_message = Options_Service::get_global_string_var('wpo_error_' . strtolower($error_code));
115 }
116
117 // Backward compatible with the now deprecated possibility to update the error message through the configuration instead l18n
118 if (empty($error_message) || empty($deprecated_error_messages[$error_code]) || $deprecated_error_messages[$error_code] == $error_message) {
119 $error_message = !empty($error_messages[$error_code])
120 ? $error_messages[$error_code]
121 : '';
122 }
123
124 // Optionally replace template tokens when error is DUAL_LOGIN or DUAL_LOGINV2
125 if (class_exists('\Wpo\Services\Options_Service') && WordPress_Helpers::stripos($error_code, 'DUAL_LOGIN') === 0) {
126
127 if (Options_Service::get_global_boolean_var('hide_sso_link')) {
128 return '';
129 }
130
131 $site_url = $GLOBALS['WPO_CONFIG']['url_info']['wp_site_url'];
132
133 if (false !== WordPress_Helpers::stripos($error_message, '__##OAUTH_URL##__')) {
134 $redirect_to = !empty($_GET['redirect_to'])
135 ? esc_url_raw(strtolower(trim($_GET['redirect_to'])))
136 : null;
137
138 if (Options_Service::get_global_boolean_var('use_b2c') && \class_exists('\Wpo\Services\Id_Token_Service_B2c')) {
139 $oauth_url = \Wpo\Services\Id_Token_Service_B2c::get_openidconnect_url(null, $redirect_to);
140 } else {
141 $oauth_url = Id_Token_Service::get_openidconnect_url(null, $redirect_to);
142 }
143
144 $error_message = str_replace("__##OAUTH_URL##__", $oauth_url, $error_message);
145 }
146 }
147
148 return $error_message;
149 }
150 }
151 }
1 <?php
2
3 namespace Wpo\Services;
4
5 // Prevent public access to this script
6 defined('ABSPATH') or die();
7
8 use \Wpo\Services\Log_Service;
9
10 include_once(ABSPATH . 'wp-admin/includes/file.php');
11
12 if (!class_exists('\Wpo\Services\Files_Service')) {
13
14 class Files_Service
15 {
16
17 private static $instance = null;
18
19 private $wpo365_profile_images_dir = null;
20
21 private $wpo365_profile_images_url = null;
22
23 public $wpo365_profile_images_configured = false;
24
25 protected function __construct()
26 {
27 }
28
29 public static function get_instance()
30 {
31
32 if (empty(self::$instance)) {
33 self::$instance = new Files_Service();
34 }
35
36 return self::$instance;
37 }
38
39 public function configure_wpo365_profile_images()
40 {
41
42 if ($this->wpo365_profile_images_configured) {
43 return true;
44 }
45
46 global $wp_filesystem;
47
48 if (false === ($credentials = request_filesystem_credentials('')) || !WP_Filesystem($credentials)) {
49 Log_Service::write_log('ERROR', __METHOD__ . ' -> Missing / incorrect credentials to write to the file system');
50 $this->wpo365_profile_images_configured = false;
51 return false;
52 }
53
54 $upload_dir = wp_upload_dir();
55 $this->wpo365_profile_images_dir = trailingslashit($upload_dir['basedir']) . 'wpo365/profile-images/'; // C:\path\to\wordpress\wp-content\uploads\wpo365\profile-images\
56
57 if (!file_exists($this->wpo365_profile_images_dir)) {
58
59 if (!$wp_filesystem->mkdir($this->wpo365_profile_images_dir, FS_CHMOD_DIR) && !wp_mkdir_p($this->wpo365_profile_images_dir)) {
60 Log_Service::write_log('DEBUG', __METHOD__ . ' -> Files service could not create requested target directory ' . $this->wpo365_profile_images_dir);
61 $this->wpo365_profile_images_configured = false;
62 return false;
63 }
64 }
65
66 $this->wpo365_profile_images_url = trailingslashit($upload_dir['baseurl']) . 'wpo365/profile-images/'; // http://example.com/wp-content/uploads/wpo365/profile-images/
67
68 Log_Service::write_log('DEBUG', __METHOD__ . ' -> Files service has been configured successfully');
69 $this->wpo365_profile_images_configured = true;
70 return true;
71 }
72
73 public function get_wpo365_profile_images_dir()
74 {
75 return $this->wpo365_profile_images_dir;
76 }
77
78 public function get_wpo365_profile_images_url()
79 {
80 return $this->wpo365_profile_images_url;
81 }
82
83 public function save_wpo365_profile_image($path, $file_content)
84 {
85
86 global $wp_filesystem;
87
88 if ($this->wpo365_profile_images_configured) {
89 Log_Service::write_log('DEBUG', __METHOD__ . ' -> Trying to write a file to the file system');
90 return $wp_filesystem->put_contents($path, $file_content, FS_CHMOD_FILE);
91 }
92
93 return false;
94 }
95 }
96 }
1 <?php
2
3 namespace Wpo\Services;
4
5 use Wpo\Core\Wpmu_Helpers;
6
7 // Prevent public access to this script
8 defined('ABSPATH') or die();
9
10 if (!class_exists('\Wpo\Services\Nonce_Service')) {
11
12 class Nonce_Service
13 {
14 /**
15 * Creates a nonce to ensure the request for an Azure AD token
16 * originates from the current server.
17 *
18 * @since 21.6
19 *
20 * @return string
21 */
22 public static function create_nonce()
23 {
24 $nonce_stack = Wpmu_Helpers::mu_get_transient('wpo365_nonces');
25
26 if (empty($nonce_stack)) {
27 $nonce_stack = array();
28 }
29
30 $nonce = uniqid();
31 $nonce_stack[] = $nonce;
32
33 // When the stack grows to 200 it's downsized to 150
34 if (sizeof($nonce_stack) > 200) {
35 array_splice($nonce_stack, 0, 50);
36 }
37
38 Wpmu_Helpers::mu_set_transient('wpo365_nonces', $nonce_stack, 300);
39
40 return $nonce;
41 }
42
43 /**
44 * Verifies the nonce that Microsoft returns together with the requested token.
45 *
46 * @param mixed $nonce
47 * @return bool
48 */
49 public static function verify_nonce($nonce)
50 {
51 $nonce_stack = Wpmu_Helpers::mu_get_transient('wpo365_nonces');
52
53 if (empty($nonce_stack)) {
54 return false;
55 }
56
57 $index = array_search($nonce, $nonce_stack);
58
59 if (false === $index) {
60 return false;
61 }
62
63 array_splice($nonce_stack, $index, 1);
64 Wpmu_Helpers::mu_set_transient('wpo365_nonces', $nonce_stack, 300);
65
66 return true;
67 }
68 }
69 }
1 <?php
2
3 namespace Wpo\Services;
4
5 // Prevent public access to this script
6 defined('ABSPATH') or die();
7
8 use \Wpo\Core\Request;
9 use \Wpo\Services\Access_Token_Service;
10 use \Wpo\Services\Options_Service;
11 use \Wpo\Services\User_Service;
12
13 if (!class_exists('\Wpo\Services\Request_Service')) {
14
15 class Request_Service
16 {
17
18 private $requests = array();
19
20 private static $instance = null;
21
22 private function __construct()
23 {
24 }
25
26 public static function get_instance($create_new_request = false)
27 {
28
29 if (empty(self::$instance)) {
30 self::$instance = new Request_Service();
31 }
32
33 if ($create_new_request) {
34 $request = self::$instance->get_request($GLOBALS['WPO_CONFIG']['request_id']);
35 $request->set_item(
36 'request_log',
37 array(
38 'debug_log' => Options_Service::get_global_boolean_var('debug_log', false),
39 'log' => array(),
40 )
41 );
42 }
43
44 return self::$instance;
45 }
46
47 public function get_request($id)
48 {
49
50 if (!array_key_exists($id, $this->requests)) {
51 $request = new Request($id);
52 $this->requests[$id] = $request;
53 }
54
55 return $this->requests[$id];
56 }
57
58 public static function shutdown()
59 {
60
61 $request = self::$instance->get_request($GLOBALS['WPO_CONFIG']['request_id']);
62 $mode = $request->get_item('mode');
63
64 if (!empty($mode)) {
65 Log_Service::flush_log();
66 $request->clear();
67 return;
68 }
69
70 $id_token = $request->get_item('id_token');
71
72 if (!empty($id_token)) {
73 $request->remove_item('id_token');
74 }
75
76 $authorization_code = $request->get_item('code');
77
78 if (!empty($authorization_code)) {
79 Access_Token_Service::save_authorization_code($authorization_code);
80 $request->remove_item('authorization_code');
81 }
82
83 $access_tokens = $request->get_item('access_tokens');
84
85 if (!empty($access_tokens)) {
86 Access_Token_Service::save_access_tokens($access_tokens);
87 $request->remove_item('access_tokens');
88 }
89
90 $refresh_token = $request->get_item('refresh_token');
91
92 if (!empty($refresh_token)) {
93 Access_Token_Service::save_refresh_token($refresh_token);
94 $request->remove_item('refresh_token');
95 }
96
97 $pkce_code_verifier = $request->get_item('pkce_code_verifier');
98
99 if (Options_Service::get_global_boolean_var('use_pkce') && class_exists('\Wpo\Services\Pkce_Service') && !empty($pkce_code_verifier)) {
100 \Wpo\Services\Pkce_Service::save_personal_pkce_code_verifier($pkce_code_verifier);
101 $request->remove_item('pkce_code_verifier');
102 }
103
104 $wpo_usr = $request->get_item('wpo_usr');
105
106 if (!empty($wpo_usr)) {
107 User_Service::save_user_principal_name($wpo_usr->upn);
108 User_Service::save_user_tenant_id($wpo_usr->tid);
109 User_Service::save_user_object_id($wpo_usr->oid);
110 }
111
112 Log_Service::flush_log();
113
114 $request->clear();
115 }
116 }
117 }
1 <?php
2
3 namespace Wpo\Services;
4
5 // Prevent public access to this script
6 defined('ABSPATH') or die();
7
8 use \WP_Error;
9 use \Wpo\Core\WordPress_Helpers;
10 use \Wpo\Services\Options_Service;
11 use \Wpo\Services\Log_Service;
12
13 if (!class_exists('\Wpo\Services\Rest_Authentication_Service_Cookies')) {
14
15 class Rest_Authentication_Service_Cookies
16 {
17 /**
18 * Handles the WordPress rest_authentication_errors hook. It looks for the WP REST NONCE header and if found validates it.
19 *
20 * @param mixed $errors
21 * @return WP_Error|null|true
22 */
23 public static function authenticate_request($errors)
24 {
25 Log_Service::write_log('DEBUG', '##### -> ' . __METHOD__);
26
27 // Check if we have a rule that matches the current request URI
28 $wp_rest_cookies_protected_endpoints = Options_Service::get_global_list_var('wp_rest_cookies_protected_endpoints');
29
30 // Authenticated if no rules are found
31 if (empty($wp_rest_cookies_protected_endpoints)) {
32 Log_Service::write_log('DEBUG', __METHOD__ . ' -> No WordPress REST API cookies protected endpoints found');
33 return null;
34 }
35
36 $headers = array_change_key_case(getallheaders());
37
38 foreach ($wp_rest_cookies_protected_endpoints as $wp_rest_cookies_protected_endpoint) {
39
40 if (
41 empty($wp_rest_cookies_protected_endpoint['key'])
42 || empty($wp_rest_cookies_protected_endpoint['value'])
43 ) {
44 Log_Service::write_log('ERROR', __METHOD__ . '-> The following WordPress REST API cookies endpoint is invalid [' . print_r($wp_rest_cookies_protected_endpoint, true) . ']');
45 continue;
46 }
47
48 // 1. REQUEST TYPE
49 if (empty($_SERVER['REQUEST_METHOD']) || false === WordPress_Helpers::stripos($wp_rest_cookies_protected_endpoint['value'], $_SERVER['REQUEST_METHOD'])) {
50 Log_Service::write_log('DEBUG', __METHOD__ . '-> The type of the current request (' . $_SERVER['REQUEST_METHOD'] . ') does not match with the request type of the current rule (' . $wp_rest_cookies_protected_endpoint['value'] . ')');
51 continue;
52 }
53
54 // 2. PATH
55 if (WordPress_Helpers::stripos($GLOBALS['WPO_CONFIG']['url_info']['request_uri'], $wp_rest_cookies_protected_endpoint['key']) !== false) {
56 Log_Service::write_log('DEBUG', __METHOD__ . '-> The following WordPress REST API cookies endpoint configuration will be applied [' . print_r($wp_rest_cookies_protected_endpoint, true) . ']');
57
58 // Check if X-WP-Nonce header is present
59 if (empty($headers['x-wp-nonce'])) {
60 Log_Service::write_log('WARN', __METHOD__ . ' -> X-WP-NONCE header missing [apache or mod_security may have removed it]');
61
62 return new WP_Error(
63 'wpo365_rest_auth_error',
64 '403 FORBIDDEN: X-WP-NONCE header was not found',
65 array('status' => 403)
66 );
67 }
68
69 if (!wp_verify_nonce($headers['x-wp-nonce'], 'wp_rest')) {
70 Log_Service::write_log('WARN', __METHOD__ . ' Validation of the X-WP-NONCE header failed');
71
72 return new WP_Error(
73 'wpo365_rest_auth_error',
74 '401 UNAUTHORIZED: X-WP-NONCE header appears invalid',
75 array('status' => 401)
76 );
77 }
78
79 $wp_usr = \wp_get_current_user();
80 wp_set_current_user($wp_usr->ID);
81
82 Log_Service::write_log('DEBUG', sprintf('%s -> Impersonated WordPress user with ID %s ', __METHOD__, $wp_usr->ID));
83
84 // Exit loop as soon as the token can be validated
85 return true;
86 }
87 }
88
89 // None of the rules apply -> Another authentication handler should handle this request
90
91 if (Options_Service::get_global_boolean_var('wp_rest_block')) {
92 Log_Service::write_log('WARN', sprintf('%s -> Access to this WordPress REST API is forbidden [%s]', __METHOD__, $GLOBALS['WPO_CONFIG']['url_info']['request_uri']));
93
94 return new WP_Error(
95 'wpo365_rest_auth_error',
96 sprintf('403 FORBIDDEN: Access to this WordPress REST API is forbidden [%s]', $GLOBALS['WPO_CONFIG']['url_info']['request_uri']),
97 array('status' => 403)
98 );
99 }
100
101 return null;
102 }
103 }
104 }
1 <?php
2
3 namespace Wpo\Services;
4
5 // Prevent public access to this script
6 defined('ABSPATH') or die();
7
8 use \Wpo\Core\Url_Helpers;
9 use \Wpo\Core\Wpmu_Helpers;
10 use \Wpo\Services\Log_Service;
11 use \Wpo\Services\Options_Service;
12
13 if (!class_exists('\Wpo\Services\User_Create_Service')) {
14
15 class User_Create_Service
16 {
17
18 /**
19 * @since 11.0
20 */
21 public static function create_user(&$wpo_usr)
22 {
23 Log_Service::write_log('DEBUG', '##### -> ' . __METHOD__);
24
25 $user_login = !empty($wpo_usr->preferred_username)
26 ? $wpo_usr->preferred_username
27 : $wpo_usr->upn;
28
29 /**
30 * @since 12.5
31 *
32 * Don't create a user when that user should not be added to a subsite in case of wpmu shared mode.
33 */
34 if (is_multisite() && !Options_Service::mu_use_subsite_options() && !is_main_site() && Options_Service::get_global_boolean_var('skip_add_user_to_subsite')) {
35 $blog_id = get_current_blog_id();
36
37 // Not using subsite options and administrator has disabled automatic adding of users to subsites
38 Log_Service::write_log('WARN', __METHOD__ . " -> Skipped creating a user with login $user_login for blog with ID $blog_id because administrator has disabled adding a user to a subsite");
39 Authentication_Service::goodbye(Error_Service::USER_NOT_FOUND);
40 exit();
41 }
42
43 if (!Options_Service::get_global_boolean_var('create_and_add_users')) {
44 Log_Service::write_log('ERROR', __METHOD__ . ' -> User not found and settings prevented creating a new user on-demand for user ' . $user_login);
45 Authentication_Service::goodbye(Error_Service::USER_NOT_FOUND);
46 exit();
47 }
48
49 /**
50 * @since 23.0 Added possibility to hook up (custom) actions to pre-defined events for various WPO365 workloads.
51 */
52
53 do_action(
54 'wpo365/user/creating',
55 $wpo_usr->preferred_username,
56 $wpo_usr->email,
57 $wpo_usr->groups
58 );
59
60 $usr_default_role = is_main_site()
61 ? Options_Service::get_global_string_var('new_usr_default_role')
62 : Options_Service::get_global_string_var('mu_new_usr_default_role');
63
64 $password_length = Options_Service::get_global_numeric_var('password_length');
65
66 if (empty($password_length) || $password_length < 16) {
67 $password_length = 16;
68 }
69
70 $userdata = array(
71 'user_login' => $user_login,
72 'user_pass' => wp_generate_password($password_length, true, false),
73 'role' => $usr_default_role,
74 );
75
76 /**
77 * @since 9.4
78 *
79 * Optionally removing any user_register hooks as these more often than
80 * not interfer and cause unexpected behavior.
81 */
82
83 $user_regiser_hooks = null;
84
85 if (Options_Service::get_global_boolean_var('skip_user_register_action') && isset($GLOBALS['wp_filter']) && isset($GLOBALS['wp_filter']['user_register'])) {
86 Log_Service::write_log('DEBUG', __METHOD__ . ' -> Temporarily removing all filters for the user_register action to avoid interference');
87 $user_regiser_hooks = $GLOBALS['wp_filter']['user_register'];
88 unset($GLOBALS['wp_filter']['user_register']);
89 }
90
91 // Insert in Wordpress DB
92 $wp_usr_id = wp_insert_user($userdata);
93
94 if (!empty($GLOBALS['wp_filter']) && !empty($user_regiser_hooks)) {
95 $GLOBALS['wp_filter']['user_register'] = $user_regiser_hooks;
96 }
97
98 if (is_wp_error($wp_usr_id)) {
99 Log_Service::write_log('ERROR', __METHOD__ . ' -> Could not create wp user. See next line for error information.');
100 Log_Service::write_log('ERROR', $wp_usr_id);
101 Authentication_Service::goodbye(Error_Service::CHECK_LOG);
102 exit();
103 }
104
105 /**
106 * @since 15.0
107 */
108
109 do_action('wpo365/user/created', $wp_usr_id);
110
111 $wpo_usr->created = true;
112 Log_Service::write_log('DEBUG', __METHOD__ . ' -> Created new user with ID ' . $wp_usr_id);
113
114 self::wpmu_add_user_to_blog($wp_usr_id, $user_login);
115
116 // Try and send new user email
117 if (\class_exists('\Wpo\Services\Mail_Notifications_Service')) {
118
119 if (Options_Service::get_global_boolean_var('new_usr_send_mail')) {
120 $notify = Options_Service::get_global_boolean_var('new_usr_send_mail_admin_only')
121 ? 'admin'
122 : 'both';
123 \Wpo\Services\Mail_Notifications_Service::new_user_notification($wp_usr_id, null, $notify);
124 Log_Service::write_log('DEBUG', __METHOD__ . ' -> Sent new user notification');
125 }
126 } else {
127 Log_Service::write_log('DEBUG', __METHOD__ . ' -> Did not sent new user notification');
128 }
129
130 Wpmu_Helpers::mu_delete_transient('wpo365_upgrade_dismissed');
131 Wpmu_Helpers::mu_set_transient('wpo365_user_created', date('d'), 1209600);
132
133 return $wp_usr_id;
134 }
135
136 /**
137 * @since 11.0
138 */
139 public static function wpmu_add_user_to_blog($wp_usr_id, $preferred_user_name)
140 {
141 Log_Service::write_log('DEBUG', '##### -> ' . __METHOD__);
142
143 if (!is_multisite()) {
144 return;
145 }
146
147 $blog_id = get_current_blog_id();
148 $is_main_site = is_main_site();
149
150 $usr_default_role = $is_main_site
151 ? Options_Service::get_global_string_var('new_usr_default_role')
152 : Options_Service::get_global_string_var('mu_new_usr_default_role');
153
154 if (!empty($usr_default_role)) {
155
156 if (!is_user_member_of_blog($wp_usr_id, $blog_id)) {
157
158 $use_subsite_options = Options_Service::mu_use_subsite_options();
159 $add_member_to_main_site = Options_Service::get_global_boolean_var('create_and_add_users');
160 $add_member_to_subsite = !Options_Service::get_global_boolean_var('skip_add_user_to_subsite');
161
162 // Settings don't allow adding member to main site [wpmu shared mode]
163 if (!$use_subsite_options && $is_main_site && !$add_member_to_main_site) {
164 Log_Service::write_log('ERROR', __METHOD__ . ' -> [WPMU shared / main site] User not a member of blog with id ' . $blog_id . ' and settings prevented adding user ' . $wp_usr_id);
165 Authentication_Service::goodbye(Error_Service::USER_NOT_FOUND);
166 exit();
167 }
168
169 // Settings don't allow adding member to sub site [wpmu shared mode]
170 if (!$use_subsite_options && !$is_main_site && !$add_member_to_subsite) {
171 Log_Service::write_log('ERROR', __METHOD__ . ' -> [WPMU shared / subsite] User not a member of blog with id ' . $blog_id . ' and settings prevented adding user ' . $wp_usr_id);
172 Authentication_Service::goodbye(Error_Service::USER_NOT_FOUND);
173 exit();
174 }
175
176 // Settings don't allow adding member to dedicated site [wpmu dedicated mode]
177 if ($use_subsite_options && !$add_member_to_main_site) {
178 Log_Service::write_log('ERROR', __METHOD__ . ' -> [WPMU dedicated] User not a member of blog with id ' . $blog_id . ' and settings prevented adding user ' . $wp_usr_id);
179 Authentication_Service::goodbye(Error_Service::USER_NOT_FOUND);
180 exit();
181 }
182
183 add_user_to_blog($blog_id, $wp_usr_id, $usr_default_role);
184
185 /**
186 * @since 15.0
187 */
188
189 do_action('wpo365/wpmu/user_added', $blog_id, $wp_usr_id);
190
191 Log_Service::write_log('DEBUG', __METHOD__ . " -> Added user with ID $wp_usr_id as a member to blog with ID $blog_id");
192 } else {
193 Log_Service::write_log('DEBUG', __METHOD__ . " -> Skipped adding user with ID $wp_usr_id to blog with ID $blog_id because user already added");
194 }
195 } else {
196 Log_Service::write_log('WARN', __METHOD__ . ' -> Could not add user ' . $preferred_user_name . ' to current blog with ID ' . $blog_id . ' because the default role for the subsite is not valid');
197 }
198 }
199 }
200 }
This diff could not be displayed because it is too large.
This diff could not be displayed because it is too large.
This diff could not be displayed because it is too large.
This diff could not be displayed because it is too large.
This diff could not be displayed because it is too large.
This diff could not be displayed because it is too large.
This diff could not be displayed because it is too large.
This diff could not be displayed because it is too large.
This diff could not be displayed because it is too large.
This diff could not be displayed because it is too large.
This diff could not be displayed because it is too large.
This diff could not be displayed because it is too large.
This diff could not be displayed because it is too large.
This diff could not be displayed because it is too large.
This diff could not be displayed because it is too large.
This diff could not be displayed because it is too large.