PluginProbe ʕ •ᴥ•ʔ
Really Simple Security – Simple and Performant Security (formerly Really Simple SSL) / 9.5.10.1
Really Simple Security – Simple and Performant Security (formerly Really Simple SSL) v9.5.10.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 1 month ago core 1 month ago languages 1 month ago lets-encrypt 1 month ago lib 1 month ago mailer 1 month ago modal 1 month ago placeholders 1 month ago progress 1 month ago security 1 month ago settings 1 month ago testssl 1 month ago upgrade 1 month ago .wp-env.json 1 month ago SECURITY.md 1 month ago class-admin.php 1 month ago class-cache.php 1 month ago class-certificate.php 1 month ago class-front-end.php 1 month ago class-installer.php 1 month ago class-mixed-content-fixer.php 1 month ago class-multisite.php 1 month ago class-server.php 1 month ago class-site-health.php 1 month ago class-wp-cli.php 1 month ago compatibility.php 1 month ago force-deactivate.txt 1 month ago functions.php 1 month ago index.php 1 month ago readme.txt 1 month ago rector.php 1 month ago rlrsssl-really-simple-ssl.php 1 month ago rsssl-auto-loader.php 1 month ago sbom.json.gz 1 month ago ssl-test-page.php 1 month ago system-status.php 1 month ago uninstall.php 1 month ago upgrade.php 1 month ago
class-multisite.php
577 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 * filters the get_admin_url function to correct the false https urls wordpress returns for non SSL websites.
398 *
399 * @since 2.3.10
400 *
401 */
402
403 public function check_admin_protocol($url, $path, $blog_id)
404 {
405 if ( !$blog_id ) $blog_id = get_current_blog_id();
406
407 //if the force_ssl_admin is defined, the admin_url should not be forced back to http: all admin panels should be https.
408 if (defined('FORCE_SSL_ADMIN')) return $url;
409
410 //do not force to http if the request is made for an url of the current blog.
411 //if a site is loaded over https, it should return https links, unless the url is requested for another blog.
412 //In that case, we only return a https link if the site_url is https, and http otherwise.
413 if (get_current_blog_id() == $blog_id) return $url;
414
415 //now check if the blog is http or https, and change the url accordingly
416 if (!function_exists('is_plugin_active_for_network'))
417 require_once(ABSPATH . '/wp-admin/includes/plugin.php');
418
419 if ( !is_plugin_active_for_network(rsssl_plugin) ) {
420 $home_url = get_blog_option($blog_id, 'home');
421 if (strpos($home_url, "https://") === false) {
422 $url = str_replace("https://", "http://", $url);
423 }
424 }
425
426 return $url;
427 }
428
429 /**
430 * filters the home_url and/or site_url function to correct the false https urls wordpress returns for non SSL websites.
431 *
432 * @since 2.3.17
433 *
434 */
435
436 public function check_site_protocol($url, $path, $orig_scheme, $blog_id)
437 {
438 if ( !$blog_id ) {
439 $blog_id = get_current_blog_id();
440 }
441
442 if (get_current_blog_id() == $blog_id) return $url;
443
444 if (!function_exists('is_plugin_active_for_network'))
445 require_once(ABSPATH . '/wp-admin/includes/plugin.php');
446
447 if ( !is_plugin_active_for_network(rsssl_plugin) ) {
448 $home_url = get_blog_option($blog_id, 'home');
449 if (strpos($home_url, "https://") === false) {
450 $url = str_replace("https://", "http://", $url);
451 }
452 }
453 return $url;
454 }
455
456
457 /**
458 * Checks if we are on a subfolder install. (domain.com/site1 )
459 *
460 * @since 2.2
461 *
462 * @access public
463 *
464 **/
465
466 public function is_multisite_subfolder_install()
467 {
468 if ( !is_multisite() ) {
469 return false;
470 }
471 //we check this manually, as the SUBDOMAIN_INSTALL constant of wordpress might return false for domain mapping configs
472 $is_subfolder = false;
473 $args = array(
474 'number' => 5,
475 'offset' => 0,
476 );
477 $sites = get_sites($args);
478 foreach ($sites as $site) {
479 switch_to_blog($site->blog_id);
480 if ($this->is_subfolder(home_url())) {
481 $is_subfolder = true;
482 }
483 restore_current_blog(); //switches back to previous blog, not current, so we have to do it each loop
484 if ($is_subfolder) return true;
485 }
486
487 return false;
488 }
489
490 /**
491 * Test if a domain has a subfolder structure
492 *
493 * @param string $domain
494 *
495 * @access public
496 *
497 * @return bool
498 * @since 2.2
499 *
500 */
501
502 public function is_subfolder(string $domain): bool {
503 //remove slashes of the http(s)
504 $domain = preg_replace("/(http:\/\/|https:\/\/)/", "", $domain);
505 return strpos( $domain, "/" ) !== false;
506 }
507
508 /**
509 * Show notices
510 *
511 * @since 2.0
512 *
513 * @access public
514 *
515 */
516
517 public function show_notices()
518 {
519 if ( !rsssl_user_can_manage() ) {
520 return;
521 }
522
523 //prevent showing the review on edit screen, as gutenberg removes the class which makes it editable.
524 $screen = get_current_screen();
525 if ( $screen && $screen->base === 'post' ) {
526 return;
527 }
528
529 if ( !$this->is_settings_page() ) {
530 $notices = RSSSL()->admin->get_notices_list( array('admin_notices'=>true) );
531 foreach ( $notices as $id => $notice ){
532 $notice = $notice['output'];
533 $class = 'open' === $notice['status'] ? 'warning' : 'error';
534 $more_info = $notice['url'] ?? false;
535 $logo = $notice['logo'] ?? false;
536 $dismiss_id = isset( $notice['dismissible'] ) && $notice['dismissible'] ? $id : false;
537 $dashboard_button = isset( $notice['dashboard_button'] ) && $notice['dashboard_button'] ? $id : false;
538 echo RSSSL()->admin->notice_html( $class . ' ' . $id, $notice['msg'], $more_info, $logo, $dismiss_id, $dashboard_button );
539 }
540 }
541 }
542
543 /**
544 * Check if we are on the settings page
545 * @return bool
546 */
547
548 public function is_settings_page()
549 {
550 if (!rsssl_user_can_manage()) {
551 return false;
552 }
553 return (isset($_GET['page']) && $_GET['page'] === 'really-simple-security');
554 }
555
556 /**
557 * Get blog count for all networks
558 *
559 * @return int
560 */
561 public function get_total_blog_count()
562 {
563 //Get the total blog count from all multisite networks
564 $networks = get_networks();
565 $total_blog_count = 0;
566 foreach($networks as $network){
567 $network_id = ($network->__get('id'));
568 $blog_count = get_blog_count($network_id);
569 $total_blog_count += $blog_count;
570 }
571
572 return $total_blog_count;
573 }
574
575 } //class closure
576 }
577