PluginProbe ʕ •ᴥ•ʔ
Really Simple Security – Simple and Performant Security (formerly Really Simple SSL) / 9.5.0.1
Really Simple Security – Simple and Performant Security (formerly Really Simple SSL) v9.5.0.1
9.5.11 9.5.10.1 9.5.10 trunk 9.4.0 9.4.1 9.4.2 9.4.3 9.5.0 9.5.0.1 9.5.0.2 9.5.1 9.5.2 9.5.2.2 9.5.2.3 9.5.3 9.5.3.1 9.5.3.2 9.5.4 9.5.5 9.5.6 9.5.7 9.5.8 9.5.9
really-simple-ssl / class-multisite.php
really-simple-ssl Last commit date
assets 9 months ago languages 9 months ago lets-encrypt 9 months ago lib 1 year ago mailer 10 months ago modal 9 months ago onboarding 10 months ago placeholders 9 months ago progress 1 year ago security 9 months ago settings 9 months ago testssl 5 years ago upgrade 9 months ago .wp-env.json 10 months ago SECURITY.md 9 months ago class-admin.php 9 months ago class-cache.php 2 years ago class-certificate.php 2 years ago class-front-end.php 1 year ago class-installer.php 10 months ago class-mixed-content-fixer.php 3 years ago class-multisite.php 1 year ago class-server.php 1 year ago class-site-health.php 1 year ago class-wp-cli.php 11 months ago compatibility.php 1 year ago force-deactivate.txt 1 year ago functions.php 10 months ago index.php 2 years ago readme.txt 9 months ago rector.php 1 year ago rlrsssl-really-simple-ssl.php 9 months ago rsssl-auto-loader.php 1 year ago sbom.json.gz 9 months ago ssl-test-page.php 2 years ago system-status.php 9 months ago uninstall.php 9 months ago upgrade.php 9 months ago
class-multisite.php
614 lines
1 <?php defined('ABSPATH') or die();
2
3 if (!class_exists('rsssl_multisite')) {
4 class rsssl_multisite
5 {
6 private static $_this;
7
8 function __construct()
9 {
10
11 if (isset(self::$_this))
12 wp_die();
13
14 self::$_this = $this;
15
16 register_activation_hook( __DIR__ . "/" . rsssl_plugin, array($this, 'activate'));
17 add_action( 'network_admin_menu', array( $this, 'add_multisite_menu' ) );
18 /*filters to make sure WordPress returns the correct protocol */
19 add_filter("admin_url", array($this, "check_admin_protocol"), 20, 3);
20 add_filter('home_url', array($this, 'check_site_protocol'), 20, 4);
21 add_filter('site_url', array($this, 'check_site_protocol'), 20, 4);
22 add_action('plugins_loaded', array($this, 'maybe_redirect_old_settings_url'), 10);
23
24 if ( is_network_admin() ) {
25 add_action('network_admin_notices', array($this, 'show_notices'), 10);
26 }
27
28 //If WP version is 5.1 or higher, use wp_insert_site hook for multisite SSL activation in new blogs
29 if( version_compare(get_bloginfo('version'),'5.1', '>=') ) {
30 add_action('wp_initialize_site', array($this, 'maybe_activate_ssl_in_new_blog'), 20, 1);
31 } else {
32 add_action('wpmu_new_blog', array($this, 'maybe_activate_ssl_in_new_blog_deprecated'), 10, 6);
33 }
34
35 add_filter('rsssl_notices', array($this, 'add_multisite_notices'));
36 }
37
38 static function this()
39 {
40 return self::$_this;
41 }
42
43 /**
44 * Redirect to the new settings page
45 *
46 * @return void
47 */
48 public function maybe_redirect_old_settings_url(){
49 if ( !rsssl_user_can_manage() || !is_network_admin() ) {
50 return;
51 }
52 if ( isset($_GET['page']) && $_GET['page'] === 'rlrsssl_really_simple_ssl' ){
53 wp_redirect(add_query_arg(['page' => 'really-simple-security'], network_admin_url('settings.php') ) );
54 exit;
55 }
56 }
57
58 /**
59 * Add notices to the dashboard
60 * @param array $notices
61 *
62 * @return array
63 */
64 public function add_multisite_notices( array $notices): array {
65 $unset_array = array(
66 'mixed_content_fixer_detected',
67 'elementor',
68 'divi',
69 );
70
71 foreach ( $unset_array as $unset_item ) {
72 unset( $notices[$unset_item] );
73 }
74 $notices['ssl_enabled'] = array(
75 'callback' => 'rsssl_ssl_enabled',
76 'score' => 30,
77 'output' => array(
78 'true' => array(
79 'msg' =>__('SSL is enabled networkwide.', 'really-simple-ssl'),
80 'icon' => 'success'
81 ),
82 'false' => array(
83 'msg' => __('SSL is not enabled on your network', 'really-simple-ssl'),
84 'icon' => 'open',
85 'plusone' => true,
86 ),
87 ),
88 );
89
90 $notices['multisite_server_variable_warning'] = array(
91 'condition' => array('rsssl_ssl_enabled'),
92 'callback' => 'RSSSL()->multisite->multisite_server_variable_warning',
93 'score' => 30,
94 'output' => array(
95 'no-server-variable' => array(
96 'msg' => __('You run a Multisite installation with subfolders, which prevents this plugin from fixing your missing server variable in the wp-config.php.', 'really-simple-ssl') . " "
97 .__('Because the $_SERVER["HTTPS"] variable is not set, your website may experience redirect loops.', 'really-simple-ssl') . " "
98 .__('Activate networkwide to fix this.', 'really-simple-ssl'),
99 'icon' => 'warning',
100 'plusone' => true,
101 ),
102 ),
103 );
104
105 $notices['activation_not_completed'] = array(
106 'callback' => 'RSSSL()->multisite->ssl_activation_started_but_not_completed',
107 'score' => 30,
108 'output' => array(
109 'true' => array(
110 'title' => __("SSL activation in progress", "really-simple-ssl"),
111 'msg' => __('A networkwide SSL activation process has been started, but has not been completed. Please go to the SSL settings page to complete the process.', 'really-simple-ssl').'&nbsp;'.
112 '<a href="'.add_query_arg(['page'=>'really-simple-security'], network_admin_url('settings.php') ).'">'.__('View settings page','really-simple-ssl').'</a>',
113 'icon' => 'warning',
114 'plusone' => true,
115 'admin_notice' => true,
116 ),
117 ),
118 );
119
120 $notices['subdomains_no_wildcard'] = array(
121 'condition' => array('rsssl_ssl_enabled'),
122 'callback' => 'RSSSL()->multisite->subdomains_no_wildcard',
123 'score' => 30,
124 'output' => array(
125 'subdomains-no-wildcard' => array(
126 'msg' => __("You run a Multisite installation with subdomains, but your site doesn't have a wildcard certificate.", 'really-simple-ssl') . " "
127 . __("This leads to issues when activating SSL networkwide since subdomains will be forced over SSL as well while they don't have a valid certificate.", 'really-simple-ssl') . " "
128 . __("Activate SSL per site or install a wildcard certificate to fix this.", 'really-simple-ssl'),
129 'icon' => 'warning',
130 'dismissible' => true,
131 'plusone' => true,
132 ),
133 ),
134 );
135
136 return $notices;
137 }
138
139 /**
140 * Check if site has a server var issue.
141 * @return string
142 */
143
144 public function multisite_server_variable_warning(){
145 if (!function_exists('is_plugin_active_for_network'))
146 require_once(ABSPATH . '/wp-admin/includes/plugin.php');
147
148 if ( is_multisite() && !is_plugin_active_for_network(rsssl_plugin) && $this->is_multisite_subfolder_install() ) {
149 //with no server variables, the website could get into a redirect loop.
150 if (RSSSL()->admin->no_server_variable) {
151 return 'no-server-variable';
152 }
153 }
154 return 'success';
155 }
156
157 /**
158 * Check if we have a subdomains setup, but no wildcard
159 * @return string
160 */
161
162 public function subdomains_no_wildcard(){
163 if ( get_site_option('rsssl_network_activation_status' !== 'completed') && !$this->is_multisite_subfolder_install() && !RSSSL()->certificate->is_wildcard() ) {
164 return 'subdomains-no-wildcard';
165 }
166 return 'success';
167 }
168
169 /**
170 * When a new site is added, maybe activate SSL as well.
171 *
172 * @param int $blog_id
173 * @param bool $user_id
174 * @param bool $domain
175 * @param bool $path
176 * @param bool $site_id
177 * @param bool $meta
178 */
179
180 public function maybe_activate_ssl_in_new_blog_deprecated( int $blog_id, $user_id=false, $domain=false, $path=false, $site_id=false, $meta=false)
181 {
182
183 if ( get_site_option('rsssl_network_activation_status' === 'completed') ) {
184 $site = get_blog_details($blog_id);
185 switch_to_blog($site->blog_id);
186 RSSSL()->admin->activate_ssl(false);
187 restore_current_blog();
188 }
189 }
190
191 /**
192 * Activate SSl in new block
193 * @since 3.1.6
194 * @param $site
195 * @return void
196 */
197
198 public function maybe_activate_ssl_in_new_blog($site)
199 {
200 if ( get_site_option('rsssl_network_activation_status' === 'completed') ) {
201 switch_to_blog($site->blog_id);
202 RSSSL()->admin->activate_ssl(false);
203 restore_current_blog();
204 }
205 }
206
207 /**
208 Add network menu for SSL
209 Only when plugin is network activated.
210 */
211
212 public function add_multisite_menu() {
213 if ( ! is_multisite() || ! rsssl_is_networkwide_active() ) {
214 return;
215 }
216
217 if ( ! rsssl_user_can_manage() ) {
218 return;
219 }
220
221 $count = RSSSL()->admin->count_plusones();
222 $update_count = $count > 0 ? "<span class='update-plugins rsssl-update-count'><span class='update-count'>$count</span></span>" : "";
223
224 $icon_svg = '<?xml version="1.0" encoding="UTF-8"?>
225 <svg id="rss-menu-logo" xmlns="http://www.w3.org/2000/svg" viewBox="0 -15 100 130" width="28" height="28">
226 <defs>
227 <style>.cls-1{fill:#fff;stroke-width:0px;}</style>
228 </defs>
229 <g fill="none" stroke-width="2">
230 <path class="cls-1" d="M72.92,26.6h-13v-9.4c0-7.6-6.1-13.7-13.7-13.7s-13.8,6.1-13.8,13.7v9.4h-13.1v-9.4C19.32,2.4,31.32,-9.6,46.12,-9.6s26.8,12,26.8,26.8v9.4h0Z"/>
231 <rect class="cls-1" x="10.02" y="84.6" width="72.3" height="5.6"/>
232 <path class="cls-1" d="M82.32,82H10.02V31.8c0-2.9,2.3-5.2,5.2-5.2h61.9c2.9,0,5.2,2.3,5.2,5.2V82h0ZM64.62,37.8c-2.2-2.2-5.9-2.2-8.2,0l-15.7,15.3l-4.9-4.9c-2.2-2.2-5.9-2.2-8.2,0l-1.9,1.9c-2.2,2.2-2.2,5.9,0,8.2l8.5,8.5c0.1,0.2,0.3,0.4,0.5,0.6l1.9,1.9l4.2,4l3.5-3.5c0.2-0.1,0.4-0.3,0.6-0.5l1.9-1.9c0.2-0.2,0.4-0.4,0.5-0.6l19.1-18.9c2.2-2.2,2.2-5.9,0-8.2l-1.8-1.9Z"/>
233 </g>
234 </svg>';
235
236 $icon_base64 = 'data:image/svg+xml;base64,' . base64_encode($icon_svg);
237
238 $page_hook_suffix = add_menu_page(
239 __( "Security", "really-simple-ssl" ),
240 __( "Security", "really-simple-ssl" ) . $update_count,
241 'manage_security',
242 'really-simple-security',
243 'rsssl_settings_page',
244 $icon_base64,
245 100 // This will place it near the bottom of the menu
246 );
247
248 add_action( "admin_print_scripts-{$page_hook_suffix}", 'rsssl_plugin_admin_scripts' );
249 // Update the page title to prevent issues with an empty title causing strip_tags deprecation warnings
250 add_action("load-{$page_hook_suffix}", 'rsssl_set_admin_page_title');
251 add_action('admin_head', 'rsssl_override_wordpress_svg_size');
252
253 }
254
255 /**
256 * Check if an SSL process is active
257 * @return bool
258 */
259 public function ssl_process_active(){
260 if ( get_site_option('rsssl_ssl_activation_active') ){
261 return true;
262 }
263 return false;
264 }
265
266 /**
267 * Run SSL upgrade process
268 *
269 * @return void
270 */
271 public function run_ssl_process(){
272 if ( get_site_option('rsssl_ssl_activation_active') ){
273 $this->activate_ssl_networkwide();
274 }
275 update_site_option('rsssl_run', false);
276 }
277
278 /**
279 * @param WP_REST_Request $request
280 *
281 * @return array
282 */
283 public function process_ssl_activation_step(){
284 if ( !$this->ssl_process_active() ) {
285 $this->start_ssl_activation();
286 }
287 $this->run_ssl_process();
288 $progress = $this->get_process_completed_percentage();
289 return [
290 'progress' => $progress,
291 'success' => true
292 ];
293 }
294
295 /**
296 * Get SSL process completed percentage
297 * @return int
298 */
299 public function get_process_completed_percentage(){
300 if ( get_site_option('rsssl_network_activation_status') === 'completed' ) {
301 return 100;
302 }
303 $complete_count = get_site_option('rsssl_siteprocessing_progress');
304 $blog_count = $this->get_total_blog_count();
305 $blog_count = $blog_count !== 0 ? $blog_count : 1; //prevent division by zero
306 $percentage = round(( $complete_count/$blog_count )*100,0);
307 if ( $percentage > 99 ) {
308 $percentage = 100;
309 }
310
311 return (int) $percentage;
312 }
313
314 /**
315 * Check if website has started activation, but didn't completed
316 * @return bool
317 */
318 public function ssl_activation_started_but_not_completed(){
319 if ( !get_option('rsssl_network_activation_status') ) {
320 return false;
321 }
322 return get_option('rsssl_network_activation_status')!=='completed';
323 }
324
325 /**
326 * Start SSL activation
327 *
328 * @return void
329 */
330 public function start_ssl_activation(){
331 if (!rsssl_user_can_manage()) {
332 return;
333 }
334 update_site_option('rsssl_siteprocessing_progress', 0);
335 update_site_option('rsssl_ssl_activation_active', true);
336 }
337
338 /**
339 * End SSL activation
340 *
341 * @return void
342 */
343 public function end_ssl_activation(){
344 if (!rsssl_user_can_manage()) {
345 return;
346 }
347 update_site_option('rsssl_ssl_activation_active', false);
348 }
349
350 /**
351 * Activate SSL network wide
352 */
353
354 public function activate_ssl_networkwide()
355 {
356 if (!rsssl_user_can_manage()) {
357 return;
358 }
359 //run chunked
360 $nr_of_sites = 200;
361 $current_offset = get_site_option('rsssl_siteprocessing_progress');
362 //set batch of sites
363 $args = array(
364 'number' => $nr_of_sites,
365 'offset' => $current_offset,
366 'meta_query' => [
367 'relation' => 'or',
368 [
369 'key' => 'rsssl_ssl_activated',
370 'compare' => 'NOT EXISTS'
371 ],
372 [
373 'key' => 'rsssl_ssl_activated',
374 'value' => false,
375 'compare' => '=',
376 ],
377 ]
378 );
379
380 $sites = get_sites($args);
381 //if no sites are found, we assume we're done.
382 if ( count($sites)==0 ) {
383 $this->end_ssl_activation();
384 update_site_option('rsssl_network_activation_status', 'completed');
385 } else {
386 foreach ($sites as $site) {
387 switch_to_blog($site->blog_id);
388 update_site_meta($site->blog_id, 'rsssl_ssl_activated', true );
389 RSSSL()->admin->activate_ssl(false);
390 restore_current_blog(); //switches back to previous blog, not current, so we have to do it each loop
391 update_site_option('rsssl_siteprocessing_progress', $current_offset+$nr_of_sites);
392 }
393 }
394 }
395
396 /**
397 * Deactivate SSL on all subsites
398 *
399 * @return void
400 */
401
402 public function deactivate()
403 {
404 if (!rsssl_user_can_manage()) {
405 return;
406 }
407 $ssl_was_enabled = rsssl_get_option('ssl_enabled');
408 delete_site_option('rsssl_network_activation_status');
409 rsssl_update_option('ssl_enabled', false);
410 //main site first
411 $site_id = get_main_site_id();
412 switch_to_blog($site_id);
413 RSSSL()->admin->deactivate_site($ssl_was_enabled);
414 restore_current_blog();
415
416 //because the deactivation should be a one click procedure, chunking this would cause difficulties
417 $args = array(
418 'number' => $this->get_total_blog_count(),
419 'offset' => 0,
420 );
421 $sites = get_sites($args);
422 foreach ($sites as $site) {
423 switch_to_blog($site->blog_id);
424 update_site_meta($site->blog_id, 'rsssl_ssl_activated', false );
425 //we already did the main site
426 if ( !is_main_site() ) {
427 RSSSL()->admin->deactivate_site($ssl_was_enabled);
428 }
429 restore_current_blog();
430 }
431 }
432
433 /**
434 * filters the get_admin_url function to correct the false https urls wordpress returns for non SSL websites.
435 *
436 * @since 2.3.10
437 *
438 */
439
440 public function check_admin_protocol($url, $path, $blog_id)
441 {
442 if ( !$blog_id ) $blog_id = get_current_blog_id();
443
444 //if the force_ssl_admin is defined, the admin_url should not be forced back to http: all admin panels should be https.
445 if (defined('FORCE_SSL_ADMIN')) return $url;
446
447 //do not force to http if the request is made for an url of the current blog.
448 //if a site is loaded over https, it should return https links, unless the url is requested for another blog.
449 //In that case, we only return a https link if the site_url is https, and http otherwise.
450 if (get_current_blog_id() == $blog_id) return $url;
451
452 //now check if the blog is http or https, and change the url accordingly
453 if (!function_exists('is_plugin_active_for_network'))
454 require_once(ABSPATH . '/wp-admin/includes/plugin.php');
455
456 if ( !is_plugin_active_for_network(rsssl_plugin) ) {
457 $home_url = get_blog_option($blog_id, 'home');
458 if (strpos($home_url, "https://") === false) {
459 $url = str_replace("https://", "http://", $url);
460 }
461 }
462
463 return $url;
464 }
465
466 /**
467 * filters the home_url and/or site_url function to correct the false https urls wordpress returns for non SSL websites.
468 *
469 * @since 2.3.17
470 *
471 */
472
473 public function check_site_protocol($url, $path, $orig_scheme, $blog_id)
474 {
475 if ( !$blog_id ) {
476 $blog_id = get_current_blog_id();
477 }
478
479 if (get_current_blog_id() == $blog_id) return $url;
480
481 if (!function_exists('is_plugin_active_for_network'))
482 require_once(ABSPATH . '/wp-admin/includes/plugin.php');
483
484 if ( !is_plugin_active_for_network(rsssl_plugin) ) {
485 $home_url = get_blog_option($blog_id, 'home');
486 if (strpos($home_url, "https://") === false) {
487 $url = str_replace("https://", "http://", $url);
488 }
489 }
490 return $url;
491 }
492
493
494 /**
495 * Checks if we are on a subfolder install. (domain.com/site1 )
496 *
497 * @since 2.2
498 *
499 * @access public
500 *
501 **/
502
503 public function is_multisite_subfolder_install()
504 {
505 if ( !is_multisite() ) {
506 return false;
507 }
508 //we check this manually, as the SUBDOMAIN_INSTALL constant of wordpress might return false for domain mapping configs
509 $is_subfolder = false;
510 $args = array(
511 'number' => 5,
512 'offset' => 0,
513 );
514 $sites = get_sites($args);
515 foreach ($sites as $site) {
516 switch_to_blog($site->blog_id);
517 if ($this->is_subfolder(home_url())) {
518 $is_subfolder = true;
519 }
520 restore_current_blog(); //switches back to previous blog, not current, so we have to do it each loop
521 if ($is_subfolder) return true;
522 }
523
524 return false;
525 }
526
527 /**
528 * Test if a domain has a subfolder structure
529 *
530 * @param string $domain
531 *
532 * @access public
533 *
534 * @return bool
535 * @since 2.2
536 *
537 */
538
539 public function is_subfolder(string $domain): bool {
540 //remove slashes of the http(s)
541 $domain = preg_replace("/(http:\/\/|https:\/\/)/", "", $domain);
542 return strpos( $domain, "/" ) !== false;
543 }
544
545 /**
546 * Show notices
547 *
548 * @since 2.0
549 *
550 * @access public
551 *
552 */
553
554 public function show_notices()
555 {
556 if ( !rsssl_user_can_manage() ) {
557 return;
558 }
559
560 //prevent showing the review on edit screen, as gutenberg removes the class which makes it editable.
561 $screen = get_current_screen();
562 if ( $screen && $screen->base === 'post' ) {
563 return;
564 }
565
566 if ( !$this->is_settings_page() ) {
567 $notices = RSSSL()->admin->get_notices_list( array('admin_notices'=>true) );
568 foreach ( $notices as $id => $notice ){
569 $notice = $notice['output'];
570 $class = 'open' === $notice['status'] ? 'warning' : 'error';
571 $more_info = $notice['url'] ?? false;
572 $logo = $notice['logo'] ?? false;
573 $dismiss_id = isset( $notice['dismissible'] ) && $notice['dismissible'] ? $id : false;
574 $dashboard_button = isset( $notice['dashboard_button'] ) && $notice['dashboard_button'] ? $id : false;
575 echo RSSSL()->admin->notice_html( $class . ' ' . $id, $notice['msg'], $more_info, $logo, $dismiss_id, $dashboard_button );
576 }
577 }
578 }
579
580 /**
581 * Check if we are on the settings page
582 * @return bool
583 */
584
585 public function is_settings_page()
586 {
587 if (!rsssl_user_can_manage()) {
588 return false;
589 }
590 return (isset($_GET['page']) && $_GET['page'] === 'really-simple-security');
591 }
592
593 /**
594 * Get blog count for all networks
595 *
596 * @return int
597 */
598 public function get_total_blog_count()
599 {
600 //Get the total blog count from all multisite networks
601 $networks = get_networks();
602 $total_blog_count = 0;
603 foreach($networks as $network){
604 $network_id = ($network->__get('id'));
605 $blog_count = get_blog_count($network_id);
606 $total_blog_count += $blog_count;
607 }
608
609 return $total_blog_count;
610 }
611
612 } //class closure
613 }
614