PluginProbe ʕ •ᴥ•ʔ
WP 2FA – Two-factor authentication for WordPress / 2.9.0
WP 2FA – Two-factor authentication for WordPress v2.9.0
4.0.0 1.7.1 2.0.0 2.0.1 2.1.0 2.2.0 2.2.1 2.3.0 2.4.0 2.4.1 2.4.2 2.5.0 2.6.0 2.6.1 2.6.2 2.6.3 2.6.4 2.7.0 2.8.0 2.9.0 2.9.1 2.9.2 2.9.3 3.0.0 3.0.1 3.1.0 3.1.1 3.1.1.2 trunk 1.2.0 1.3.0 1.4.0 1.4.1 1.4.2 1.5.0 1.5.1 1.5.2 1.6.0 1.6.1 1.6.2 1.7.0
wp-2fa / includes / classes / Admin / Helpers / class-wp-helper.php
wp-2fa / includes / classes / Admin / Helpers Last commit date
class-ajax-helper.php 11 months ago class-classes-helper.php 11 months ago class-file-writer.php 11 months ago class-methods-helper.php 11 months ago class-php-helper.php 11 months ago class-user-helper.php 11 months ago class-wp-helper.php 11 months ago index.php 11 months ago
class-wp-helper.php
374 lines
1 <?php
2 /**
3 * Responsible for the WP core functionalities.
4 *
5 * @package wp2fa
6 * @subpackage helpers
7 *
8 * @since 2.2.0
9 *
10 * @copyright 2025 Melapress
11 * @license https://www.apache.org/licenses/LICENSE-2.0 Apache License 2.0
12 *
13 * @see https://wordpress.org/plugins/wp-2fa/
14 */
15
16 namespace WP2FA\Admin\Helpers;
17
18 defined( 'ABSPATH' ) || exit; // Exit if accessed directly.
19
20 /*
21 * WP helper class
22 */
23 if ( ! class_exists( '\WP2FA\Admin\Helpers\WP_Helper' ) ) {
24 /**
25 * All the WP functionality must go trough this class.
26 *
27 * @since 2.2.0
28 */
29 class WP_Helper {
30 /**
31 * Hold the user roles as array - Human readable is used for key of the array, and the internal role name is the value.
32 *
33 * @var array
34 *
35 * @since 2.2.0
36 */
37 private static $user_roles = array();
38
39 /**
40 * Hold the user roles as array - Internal role name is used for key of the array, and the human readable format is the value.
41 *
42 * @var array
43 *
44 * @since 2.2.0
45 */
46 private static $user_roles_wp = array();
47
48 /**
49 * Keeps the value of the multisite install of the WP.
50 *
51 * @var bool
52 *
53 * @since 2.2.0
54 */
55 private static $is_multisite = null;
56
57 /**
58 * Holds array with all the sites in multisite WP installation.
59 *
60 * @var array
61 */
62 private static $sites = array();
63
64 /**
65 * Inits the class, and fires all the necessarily methods.
66 *
67 * @return void
68 *
69 * @since 2.2.0
70 */
71 public static function init() {
72 if ( self::is_multisite() ) {
73 \add_action( 'network_admin_notices', array( __CLASS__, 'show_critical_admin_notice' ) );
74 } else {
75 \add_action( 'admin_notices', array( __CLASS__, 'show_critical_admin_notice' ) );
76 }
77 }
78
79 /**
80 * Checks if specific role exists.
81 *
82 * @param string $role - The name of the role to check.
83 *
84 * @since 2.2.0
85 */
86 public static function is_role_exists( string $role ): bool {
87 self::set_roles();
88
89 if ( in_array( $role, self::$user_roles, true ) ) {
90 return true;
91 }
92
93 return false;
94 }
95
96 /**
97 * Returns the currently available WP roles - the Human readable format is the key.
98 *
99 * @return array
100 *
101 * @since 2.2.0
102 */
103 public static function get_roles() {
104 self::set_roles();
105
106 return self::$user_roles;
107 }
108
109 /**
110 * Returns the currently available WP roles.
111 *
112 * @return array
113 *
114 * @since 2.2.0
115 */
116 public static function get_roles_wp() {
117 if ( empty( self::$user_roles_wp ) ) {
118 self::set_roles();
119 self::$user_roles_wp = array_flip( self::$user_roles );
120 }
121
122 return self::$user_roles_wp;
123 }
124
125 /**
126 * Shows critical notices to the admin.
127 *
128 * @return void
129 *
130 * @since 2.2.0
131 */
132 public static function show_critical_admin_notice() {
133 if ( User_Helper::is_admin() ) {
134 /*
135 * Gives the ability to show notices to the admins
136 */
137 \do_action( WP_2FA_PREFIX . 'critical_notice' );
138 }
139 }
140
141 /**
142 * Check is this is a multisite setup.
143 *
144 * @return bool
145 *
146 * @since 2.2.0
147 */
148 public static function is_multisite() {
149 if ( null === self::$is_multisite ) {
150 self::$is_multisite = function_exists( 'is_multisite' ) && is_multisite();
151 }
152
153 return self::$is_multisite;
154 }
155
156 /**
157 * Collects all the sites from multisite WP installation.
158 *
159 * @since 2.5.0
160 */
161 public static function get_multi_sites(): array {
162 if ( self::is_multisite() ) {
163 if ( empty( self::$sites ) ) {
164 self::$sites = self::get_sites();
165 }
166
167 return self::$sites;
168 }
169
170 return array();
171 }
172
173 /**
174 * Query sites from WPDB.
175 *
176 * @since 3.0.0
177 *
178 * @param int|null $limit — Maximum number of sites to return (null = no limit).
179 *
180 * @return object — Object with keys: blog_id, blogname, domain
181 */
182 public static function get_sites( $limit = null ) {
183 if ( self::is_multisite() ) {
184 global $wpdb;
185 // Build query.
186 $sql =
187 'SELECT blog_id, domain FROM ' . $wpdb->blogs . ( ! is_null( $limit ) ? ' LIMIT ' . $limit : '' );
188
189 // Execute query.
190 $res = $wpdb->get_results( $sql ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared
191
192 // Modify result.
193 foreach ( $res as $row ) {
194 $row->blogname = \esc_html( \get_blog_option( $row->blog_id, 'blogname' ) );
195 }
196 } else {
197 $res = new \stdClass();
198 $res->blog_id = \get_current_blog_id();
199 $res->blogname = \esc_html( \get_bloginfo( 'name' ) );
200 $res = array( $res );
201 }
202
203 // Return result.
204 return $res;
205 }
206
207 /**
208 * Calculating the signature.
209 *
210 * @param array $data - Array with data to create a signature for.
211 *
212 * @since 2.2.2
213 */
214 public static function calculate_api_signature( array $data ): string {
215 $now = new \DateTime( 'now', new \DateTimeZone( 'UTC' ) );
216 $nonce = $now->getTimestamp();
217
218 $pk_hash = hash( 'sha512', $data['license_key'] . '|' . $nonce );
219 $authentication_string = base64_encode( $pk_hash . '|' . $nonce );
220
221 return $authentication_string;
222 }
223
224 /**
225 * Checks if that is the WP login page or not.
226 *
227 * @return bool
228 *
229 * @since 2.4.1
230 */
231 public static function is_wp_login() {
232 $abs_path = str_replace( array( '\\', '/' ), DIRECTORY_SEPARATOR, ABSPATH );
233
234 if ( function_exists( 'is_account_page' ) && \is_account_page() ) {
235 // The user is on the WooCommerce login page.
236
237 return true;
238 }
239
240 return ( in_array( $abs_path . 'wp-login.php', get_included_files() ) || in_array( $abs_path . 'wp-register.php', get_included_files() ) ) || ( isset( $GLOBALS['pagenow'] ) && 'wp-login.php' === $GLOBALS['pagenow'] ) || '/wp-login.php' == $_SERVER['PHP_SELF']; // phpcs:ignore Universal.Operators.StrictComparisons.LooseEqual, WordPress.Security.ValidatedSanitizedInput.InputNotValidated
241 }
242
243 /**
244 * Check whether we are on an admin and plugin page.
245 *
246 * @since 2.4.1
247 *
248 * @param array|string $slug ID(s) of a plugin page. Possible values: 'general', 'logs', 'about' or array of them.
249 *
250 * @return bool
251 */
252 public static function is_admin_page( $slug = array() ) { // phpcs:ignore Generic.Metrics.NestingLevel.MaxExceeded
253
254 $cur_page = isset( $_GET['page'] ) ? sanitize_key( $_GET['page'] ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
255 $check = WP_2FA_PREFIX_PAGE;
256
257 return \is_admin() && ( false !== strpos( $cur_page, $check ) );
258 }
259
260 /**
261 * Remove all non-WP Mail SMTP plugin notices from our plugin pages.
262 *
263 * @since 2.4.1
264 */
265 public static function hide_unrelated_notices() {
266 // Bail if we're not on our screen or page.
267 if ( ! self::is_admin_page() ) {
268 return;
269 }
270
271 self::remove_unrelated_actions( 'user_admin_notices' );
272 self::remove_unrelated_actions( 'admin_notices' );
273 self::remove_unrelated_actions( 'all_admin_notices' );
274 self::remove_unrelated_actions( 'network_admin_notices' );
275 }
276
277 /**
278 * Creates a nonce for HTML field by given name.
279 *
280 * @param string $nonce_name -The name of the nonce to create.
281 *
282 * @return string
283 *
284 * @since 2.6.0
285 */
286 public static function create_data_nonce( string $nonce_name ): string {
287 return ' data-nonce="' . \esc_attr( \wp_create_nonce( $nonce_name ) ) . '"';
288 }
289
290 /**
291 * Extracts the domain part of the given string.
292 *
293 * @param string $url_to_check - The URL string to be checked.
294 *
295 * @return string
296 *
297 * @since 2.6.0
298 */
299 public static function extract_domain( string $url_to_check ): string {
300 // get the full domain.
301 // $urlparts = parse_url( \site_url() );.
302
303 if ( false !== strpos( $url_to_check, '@' ) ) {
304 $domain = \explode( '@', $url_to_check )[1];
305
306 return $domain;
307 }
308 $urlparts = parse_url( $url_to_check );
309 $domain = $urlparts ['host'];
310
311 // get the TLD and domain.
312 $domainparts = explode( '.', $domain );
313 $domain = $domainparts[ count( $domainparts ) - 2 ] . '.' . $domainparts[ count( $domainparts ) - 1 ];
314
315 return $domain;
316 }
317
318 /**
319 * Remove all non-WP Mail SMTP notices from the our plugin pages based on the provided action hook.
320 *
321 * @since 2.4.1
322 *
323 * @param string $action The name of the action.
324 */
325 private static function remove_unrelated_actions( $action ) {
326 global $wp_filter;
327
328 if ( empty( $wp_filter[ $action ]->callbacks ) || ! is_array( $wp_filter[ $action ]->callbacks ) ) {
329 return;
330 }
331
332 foreach ( $wp_filter[ $action ]->callbacks as $priority => $hooks ) {
333 foreach ( $hooks as $name => $arr ) {
334 if (
335 ( // Cover object method callback case.
336 is_array( $arr['function'] ) &&
337 isset( $arr['function'][0] ) &&
338 is_object( $arr['function'][0] ) &&
339 false !== strpos( ( get_class( $arr['function'][0] ) ), 'WP2FA' )
340 ) ||
341 ( // Cover class static method callback case.
342 ! empty( $name ) &&
343 false !== strpos( ( $name ), 'WP2FA' )
344 )
345 ) {
346 continue;
347 }
348
349 unset( $wp_filter[ $action ]->callbacks[ $priority ][ $name ] );
350 }
351 }
352 }
353
354 /**
355 * Sets the internal variable with all the existing WP roles.
356 *
357 * @return void
358 *
359 * @since 2.2.0
360 */
361 private static function set_roles() {
362 if ( empty( self::$user_roles ) ) {
363 global $wp_roles;
364
365 if ( null === $wp_roles ) {
366 wp_roles();
367 }
368
369 self::$user_roles = array_flip( $wp_roles->get_names() );
370 }
371 }
372 }
373 }
374