PluginProbe
Magic Login – Magic Link & Passwordless Authentication for WordPress – Login Without Password / 2.1
Magic Login – Magic Link & Passwordless Authentication for WordPress – Login Without Password v2.1
2.8.1 2.1 2.1.1 2.1.2 2.1.3 2.2 2.3 2.3.1 2.3.2 2.3.3 2.3.4 2.3.5 2.4 2.4.1 2.4.2 2.5 2.5.1 2.6 2.6.1 2.6.2 2.6.3 2.7 2.7.1 2.8 trunk All 48 releases
magic-login / includes / utils.php

utils.php in Magic Login – Magic Link & Passwordless Authentication for WordPress – Login Without Password 2.1, at includes/utils.php

372 lines 8.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Common utilities and functions
4 *
5 * @package MagicLogin
6 */
7
8 namespace MagicLogin\Utils;
9
10 use const MagicLogin\Constants\CRON_HOOK_NAME;
11 use const MagicLogin\Constants\SETTING_OPTION;
12 use const MagicLogin\Constants\TOKEN_USER_META;
13
14 /**
15 * Create token
16 *
17 * @param object $user \WP_User object
18 *
19 * @return string
20 */
21 function create_user_token( $user ) {
22 $settings = get_settings(); // phpcs:ignore
23 $tokens = get_user_meta( $user->ID, TOKEN_USER_META, true );
24 $tokens = is_string( $tokens ) ? array( $tokens ) : $tokens;
25 $new_token = sha1( wp_generate_password() );
26 $hashed_token = hash_hmac( 'sha256', $new_token, wp_salt() );
27
28 $ip = sha1( get_client_ip() );
29 if ( defined( 'WP_CLI' ) && WP_CLI ) {
30 $ip = 'cli';
31 }
32
33 $tokens[] = [
34 'token' => $hashed_token,
35 'time' => time(),
36 'ip_hash' => $ip,
37 ];
38
39 update_user_meta( $user->ID, TOKEN_USER_META, $tokens );
40
41 if ( absint( $settings['token_ttl'] ) > 0 ) { // eternal token
42 wp_schedule_single_event( time() + ( $settings['token_ttl'] * MINUTE_IN_SECONDS ), CRON_HOOK_NAME, array( $user->ID ) );
43 }
44
45 return $new_token;
46 }
47
48
49 /**
50 * Create login link for given user
51 *
52 * @param object $user WP_User object
53 *
54 * @return mixed|string
55 */
56 function create_login_link( $user ) {
57 $token = create_user_token( $user );
58
59 $query_args = array(
60 'user_id' => $user->ID,
61 'token' => $token,
62 'magic-login' => 1,
63 );
64
65 if ( ! empty( $_POST['redirect_to'] ) ) {
66 $query_args['redirect_to'] = urlencode( wp_unslash( $_POST['redirect_to'] ) ); // phpcs:ignore
67 }
68
69 $login_url = esc_url_raw( add_query_arg( $query_args, wp_login_url() ) );
70
71 return $login_url;
72 }
73
74 /**
75 * Get client raw ip
76 * this should be hashed
77 *
78 * @return mixed
79 */
80 function get_client_ip() {
81 /**
82 * `HTTP_X_FORWARDED_FOR` removed in 1.5
83 * Filters the ip address
84 *
85 * @hook magic_login_client_ip
86 *
87 * @param {string} REMOTE_ADDR
88 *
89 * @return {string} New value.
90 * @since 1.5
91 */
92 return apply_filters( 'magic_login_client_ip', $_SERVER['REMOTE_ADDR'] ); // phpcs:ignore
93 }
94 /**
95 * Get settings with defaults
96 *
97 * @return array
98 * @since 1.0
99 */
100 function get_settings() {
101 $defaults = [
102 'is_default' => false,
103 'add_login_button' => true,
104 'token_ttl' => 5,
105 'token_validity' => 1,
106 'token_interval' => 'MINUTE',
107 'enable_brute_force_protection' => false,
108 'brute_force_bantime' => 60, // in minutes
109 'brute_force_login_attempt' => 10,
110 'brute_force_login_time' => 5, // in minutes
111 'enable_login_throttling' => false,
112 'login_throttling_limit' => 10,
113 'login_throttling_time' => 15, // in minutes
114 'enable_ip_check' => false,
115 'enable_domain_restriction' => false,
116 'allowed_domains' => '',
117 'login_email' => get_default_login_email_text(),
118 'enable_login_redirection' => false,
119 'default_redirection_url' => '',
120 'enforce_redirection_rules' => true,
121 'enable_wp_login_redirection' => false,
122 'enable_role_based_redirection' => false,
123 'role_based_redirection_rules' => [],
124 'email_subject' => __( 'Log in to {{SITENAME}}', 'magic-login' ),
125 'auto_login_links' => false,
126 'enable_ajax' => false,
127 'enable_woo_integration' => false,
128 'woo_position' => 'before',
129 ];
130
131 if ( MAGIC_LOGIN_IS_NETWORK ) {
132 $settings = get_site_option( SETTING_OPTION, [] );
133 } else {
134 $settings = get_option( SETTING_OPTION, [] );
135 }
136
137 $settings = wp_parse_args( $settings, $defaults );
138
139 return $settings;
140 }
141
142 /**
143 * Default login email message
144 *
145 * @return mixed|string|void
146 */
147 function get_default_login_email_text() {
148 /* translators: Do not translate USERNAME, SITENAME,EXPIRES, MAGIC_LINK, SITENAME, SITEUR, EXPIRES_WITH_INTERVAL: those are placeholders. */
149 $email_text = __(
150 'Hi {{USERNAME}},
151
152 Click and confirm that you want to log in to {{SITENAME}}. This link will expire in {{EXPIRES_WITH_INTERVAL}} and can only be used once:
153
154 <a href="{{MAGIC_LINK}}" target="_blank" rel="noreferrer noopener">Log In</a>
155
156 Need the link? {{MAGIC_LINK}}
157
158
159 You can safely ignore and delete this email if you do not want to log in.
160
161 Regards,
162 All at {{SITENAME}}
163 {{SITEURL}}',
164 'magic-login'
165 );
166
167 return $email_text;
168 }
169
170 /**
171 * Is plugin activated network wide?
172 *
173 * @param string $plugin_file file path
174 *
175 * @return bool
176 * @since 1.0
177 */
178 function is_network_wide( $plugin_file ) {
179 if ( ! is_multisite() ) {
180 return false;
181 }
182
183 if ( ! function_exists( 'is_plugin_active_for_network' ) ) {
184 require_once ABSPATH . '/wp-admin/includes/plugin.php';
185 }
186
187 return is_plugin_active_for_network( plugin_basename( $plugin_file ) );
188 }
189
190 /**
191 * Get login link
192 *
193 * @return mixed|string
194 */
195 function get_magic_login_url() {
196 return esc_url_raw( site_url( 'wp-login.php?action=magic_login', 'login_post' ) );
197 }
198
199 /**
200 * Get user tokens
201 *
202 * @param int $user_id User ID
203 * @param bool $clear_expired flag for clean-up expired tokens
204 *
205 * @return array|mixed
206 */
207 function get_user_tokens( $user_id, $clear_expired = false ) {
208 $tokens = get_user_meta( $user_id, TOKEN_USER_META, true );
209 $tokens = is_array( $tokens ) ? $tokens : [];
210
211 /**
212 * Filter user tokens
213 *
214 * @hook magic_login_user_tokens
215 *
216 * @param {array} $tokens User tokens.
217 * @param {int} $user_id User ID.
218 * @param {boolean} $clear_expired Whether to clear expired tokens or not.
219 *
220 * @return {array} New value
221 * @since 2.1
222 */
223 $tokens = (array) apply_filters( 'magic_login_user_tokens', $tokens, $user_id, $clear_expired );
224
225 if ( $clear_expired ) {
226 $settings = get_settings(); //phpcs:ignore
227 $ttl = absint( $settings['token_ttl'] );
228
229 if ( 0 === $ttl ) { // means token lives forever till used
230 return $tokens;
231 }
232
233 foreach ( $tokens as $index => $token_data ) {
234 if ( empty( $token_data ) ) {
235 unset( $tokens[ $index ] );
236 continue;
237 }
238
239 if ( time() > absint( $token_data['time'] ) + ( $ttl * MINUTE_IN_SECONDS ) ) {
240 unset( $tokens[ $index ] );
241 }
242 }
243 update_user_meta( $user_id, TOKEN_USER_META, $tokens );
244 }
245
246 return $tokens;
247 }
248
249 /**
250 * Get default redirect url for given user
251 *
252 * @param \WP_User $user User object
253 *
254 * @return string|void
255 */
256 function get_user_default_redirect( $user ) {
257 if ( is_multisite() && ! get_active_blog_for_user( $user->ID ) && ! is_super_admin( $user->ID ) ) {
258 $redirect_to = user_admin_url();
259 } elseif ( is_multisite() && ! $user->has_cap( 'read' ) ) {
260 $redirect_to = get_dashboard_url( $user->ID );
261 } elseif ( ! $user->has_cap( 'edit_posts' ) ) {
262 $redirect_to = $user->has_cap( 'read' ) ? admin_url( 'profile.php' ) : home_url();
263 } else {
264 $redirect_to = admin_url();
265 }
266
267 return $redirect_to;
268 }
269
270 /**
271 * Delete all token meta
272 */
273 function delete_all_tokens() {
274 global $wpdb;
275
276 return $wpdb->delete( // phpcs:ignore WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.DirectDatabaseQuery.DirectQuery
277 $wpdb->usermeta,
278 [
279 'meta_key' => TOKEN_USER_META, // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key
280 ]
281 );
282 }
283
284
285 /**
286 * Allowed intervals for TTL.
287 *
288 * @return array
289 * @since 1.2
290 */
291 function get_allowed_intervals() {
292 return [
293 'MINUTE' => esc_html__( 'Minute(s)', 'magic-login' ),
294 'HOUR' => esc_html__( 'Hour(s)', 'magic-login' ),
295 'DAY' => esc_html__( 'Day(s)', 'magic-login' ),
296 ];
297 }
298
299 /**
300 * Convert minutes to possible time format
301 *
302 * @param int $timeout_in_minutes TTL in minutes
303 *
304 * @return array
305 * @since 1.2
306 */
307 function get_ttl_with_interval( $timeout_in_minutes ) {
308 $ttl = $timeout_in_minutes;
309 $interval = 'MINUTE';
310
311 if ( $ttl > 0 ) {
312 if ( 0 === (int) ( $ttl % 1440 ) ) {
313 $ttl = $ttl / 1440;
314 $interval = 'DAY';
315 } elseif ( 0 === (int) ( $ttl % 60 ) ) {
316 $ttl = $ttl / 60;
317 $interval = 'HOUR';
318 }
319 }
320
321 return array(
322 $ttl,
323 $interval,
324 );
325 }
326
327
328 /**
329 * Get the documentation url
330 *
331 * @param string $path The path of documentation
332 * @param string $fragment URL Fragment
333 *
334 * @return string final URL
335 */
336 function get_doc_url( $path = null, $fragment = '' ) {
337 $doc_base = 'https://handyplugins.co/magic-login-pro/docs/';
338 $utm_parameters = '?utm_source=wp_admin&utm_medium=plugin&utm_campaign=settings_page';
339
340 if ( ! empty( $path ) ) {
341 $doc_base .= ltrim( $path, '/' );
342 }
343
344 $doc_url = trailingslashit( $doc_base ) . $utm_parameters;
345
346 if ( ! empty( $fragment ) ) {
347 $doc_url .= '#' . $fragment;
348 }
349
350 return $doc_url;
351 }
352
353 /**
354 * Check weather current screen is magic login settings page or not
355 *
356 * @return bool
357 * @since 1.2.1
358 */
359 function is_magic_login_settings_screen() {
360 $current_screen = get_current_screen();
361
362 if ( ! is_a( $current_screen, '\WP_Screen' ) ) {
363 return false;
364 }
365
366 if ( false !== strpos( $current_screen->base, 'magic-login' ) ) {
367 return true;
368 }
369
370 return false;
371 }
372