PluginProbe
Two Factor Authentication / 1.16.0
Two Factor Authentication v1.16.0
1.12.2 1.13.0 1.14.10 1.14.11 1.14.14 1.14.15 1.14.16 1.14.17 1.14.23 1.14.24 1.14.26 1.14.27 1.14.3 1.14.4 1.14.5 1.14.7 1.14.8 1.15.5 1.16.0 1.2.10 1.2.12 1.2.13 1.2.14 1.2.15 1.2.16 All 98 releases
two-factor-authentication / simba-tfa / includes / tfa.js

tfa.js in Two Factor Authentication 1.16.0, at simba-tfa/includes/tfa.js

362 lines 12.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 jQuery(function($) {
2
3 var username_requires_otp = [];
4
5 /**
6 * Returns the jQuery identifiers for finding the username field. Abstracted here to avoid maintaining multiple lists.
7 *
8 * @return String
9 */
10 function get_username_identifiers() {
11 // 'username' is used by WooCommerce and RegistrationMagic
12 return '[name="log"], [name="username"], #user_login, #affwp-login-user-login, #affwp-user-login, #gform_fields_login input[type="text"], .um-field-username input[type="text"], [name="edd_user_login"]';
13 }
14
15 /**
16 * Process the results of a check for whether the user has TFA enabled or not
17 *
18 * @param Object form - jQuery form object
19 * @param Object response - the response from the check; must have the property (boolean) "status" and potentially user_(boolean) "can_trust" and (boolean) user_can_trust.
20 */
21 function process_user_tfa_enabled_check_results(form, response) {
22
23 if (true === response.status) {
24 // Don't bother to remove the spinner if the form is being submitted.
25 $('.simbaotp_spinner').remove();
26
27 var user_can_trust = (response.hasOwnProperty('user_can_trust') && response.user_can_trust) ? true : false;
28
29 var user_already_trusted = (response.hasOwnProperty('user_already_trusted') && response.user_can_trust) ? true : false;
30
31 console.log("Simba TFA: User has OTP enabled: showing OTP field (user_can_trust="+user_can_trust+")");
32
33 show_otp_field(form, user_can_trust, user_already_trusted);
34
35 return true;
36
37 } else {
38 console.log("Simba TFA: User does not have OTP enabled: submitting form");
39 // For some reason, .submit() stopped working with TML 7.x. N.B. Used to do this only for form_type == 2 ("TML shortcode or widget, WP Members, bbPress, Ultimate Membership Pro, WooCommerce or Elementor login form")
40 // The un-disabling is for Ultimate Member, which for unknown reasons outputs the login button in a disabled state
41 $(form).find('input[type="submit"], button[type="submit"]').first().prop('disabled', false).trigger('click');
42 // $('#wp-submit').parents('form').first().trigger('submit');
43 }
44 return false;
45 }
46
47 /**
48 * Check if the user requires an OTP field and if so, display it
49 *
50 * @param String form - DOM selector string
51 * @param Boolean only_cache_the_results - if true, then nothing more will be done that caching the results (in the variable username_requires_otp will be updated)
52 *
53 * @uses show_otp_field()
54 *
55 * @return Boolean - true if we got involved
56 */
57 function check_and_possibly_show_otp_field(form, only_cache_the_results) {
58
59 // If this is a "lost password" form, then exit
60 if ($(form).attr('id') === 'lostpasswordform' || $(form).attr('id') === 'resetpasswordform') return false;
61
62 var username = $(form).find(get_username_identifiers()).first().val();
63
64 if (!username.length) return false;
65
66 // Is the result already known?
67 if ('object' === typeof username_requires_otp[username]) {
68 if (!only_cache_the_results) {
69 // Process the already-known result
70 return process_user_tfa_enabled_check_results($(form), username_requires_otp[username]);
71 }
72 // No further processing
73 return true;
74 }
75
76 var $submit_button = $(form).find('input[name="wp-submit"], input[type="submit"], button[type="submit"]').first();
77
78 if (simba_tfasettings.hasOwnProperty('spinnerimg') && $('.simbaotp_spinner').length === 0) {
79 var styling = 'float:right; margin:6px 12px; width: 20px; height: 20px;';
80 if ($('#theme-my-login #wp-submit').length >0) {
81 styling = 'margin-left: 4px; position: relative; top: 4px; width: 20px; height: 20px; border:0px; box-shadow:none;';
82 }
83 $submit_button.after('<img class="simbaotp_spinner" src="'+simba_tfasettings.spinnerimg+'" style="'+styling+'">');
84 }
85
86 $.ajax({
87 url: simba_tfasettings.ajaxurl,
88 type: 'POST',
89 data: {
90 action: 'simbatfa-init-otp',
91 user: username
92 },
93 dataType: 'text',
94 success: function(resp) {
95 try {
96 var json_begins = resp.search('{"jsonstarter":"justhere"');
97 if (json_begins > -1) {
98 if (json_begins > 0) {
99 console.log("Expected JSON marker found at position: "+json_begins);
100 resp = resp.substring(json_begins);
101 }
102 } else {
103 console.log("Expected JSON marker not found");
104 console.log(resp);
105 }
106
107 response = JSON.parse(resp);
108
109 if (response.hasOwnProperty('php_output')) {
110 console.log("PHP output was returned (follows)");
111 console.log(response.php_output);
112 }
113
114 if (response.hasOwnProperty('extra_output')) {
115 console.log("Extra output was returned (follows)");
116 console.log(response.extra_output);
117 }
118
119 if (only_cache_the_results) {
120 // Save the result for later processing
121 username_requires_otp[username] = response;
122 $('.simbaotp_spinner').remove();
123 } else {
124 process_user_tfa_enabled_check_results($(form), response);
125 }
126
127 } catch(err) {
128 $('#login').html(resp);
129 console.log("Simba TFA: Error when processing response");
130 console.log(err);
131 console.log(resp);
132 }
133 },
134 error: function(jq_xhr, text_status, error_thrown) {
135 console.log("Simba TFA: AJAX error: "+error_thrown+": "+text_status);
136 console.log(jq_xhr);
137 if (jq_xhr.hasOwnProperty('responseText')) {
138 console.log(jq_xhr.responseText);
139 $(form).append('<p class="error" style="clear:left;">'+simba_tfasettings.error+'</p>');
140 }
141 }
142 });
143 return true;
144 }
145
146 // Parameters: see check_and_possibly_show_otp_field
147 function show_otp_field(form, user_can_trust, user_already_trusted) {
148
149 var $submit_button;
150
151 user_can_trust = ('undefined' == typeof user_can_trust) ? false : user_can_trust;
152 user_already_trusted = ('undefined' == typeof user_already_trusted) ? false : user_already_trusted;
153
154 if ('https:' != window.location.protocol && 'localhost' !== location.hostname && '127.0.0.1' !== location.hostname && /^\.localdomain$/.test(location.hostname)) {
155 user_can_trust = false;
156 }
157
158 if (!user_can_trust) { user_already_trusted = false; }
159
160 var form_is_gravity_forms = ('object' == typeof window['gform_gravityforms'] && 'undefined' !== typeof $(form).attr('id') && 'gform_' === $(form).attr('id').substring(0, 6));
161
162 // This is used just for applying similar styling (via adding structure/CSS classes)
163 var form_is_ultimate_member = ($(form).find('.um-row').length > 0) ? true : false;
164
165 // This is used just for applying styling if .js-login-form class exists inside form
166 var form_is_login_form = ($(form).find('.js-login-form').length > 0) ? true : false;
167
168 // Gravity Forms won't submit if the elements are hidden
169 var form_retain_existing_elements = form_is_gravity_forms ? true : false;
170
171 // name="Submit" is WP-Members. 'submit' is Theme My Login starting from 7.x
172 $submit_button = $(form).find('input[name="wp-submit"], input[name="Submit"], input[name="submit"]');
173 // This hasn't been needed for anything yet (Jul 2018), but is a decent back-stop that would have prevented some breakage in the past that needed manual attention:
174 if (0 == $submit_button.length) {
175 $submit_button = $(form).find('input[type="submit"], button[type="submit"]').first();
176 }
177
178 if (!form_retain_existing_elements) {
179 // Hide all elements in a browser-safe way
180 // .user-pass-wrap is the wrapper used (instead of a paragraph) on wp-login.php from WP 5.3
181 // .um-row : Ultimate Member
182 // .rmrow : RegistrationMagic
183 $submit_button.parents('form').first().find('p, .impu-form-line-fr, .tml-field-wrap, .user-pass-wrap, .elementor-field-type-text, .elementor-field-type-submit, .elementor-remember-me, .bbp-username, .bbp-password, .bbp-submit-wrapper, .gform_body, .um-row, .um-button, .js-login-form, .rmrow').each(function(i) {
184 $(this).css('visibility', 'hidden').css('position', 'absolute');
185 // On the WooCommerce form, the 'required' asterisk in the child <span> still shows without this
186 $(this).find('span').css('visibility', 'hidden').css('position', 'absolute');
187 });
188
189 // WP-Members
190 $submit_button.parents('#wpmem_login').find('fieldset').css('visibility', 'hidden').css('position', 'absolute');
191
192 }
193
194 // Add new field and controls
195 var html = '';
196
197 if (form_is_ultimate_member) {
198 html += '<div class="um-row">';
199 }
200
201 if (user_already_trusted) {
202
203 html += '<br><span class="simbaotp_is_trusted">'+simba_tfasettings.is_trusted+'</span>';
204
205 } else {
206
207 if (form_is_ultimate_member) { html += '<div class="um-field um-field-text um-field-type_text"><div class="um-field-label">'; }
208
209 html += '<label ';
210
211 if (form_is_gravity_forms) {
212 html += 'class="gfield_label"';
213 }
214
215 html += 'for="simba_two_factor_auth">';
216
217 html += simba_tfasettings.otp + '</label><input type="text" name="two_factor_code" id="simba_two_factor_auth" autocomplete="off" data-lpignore="true"';
218
219 if ($(form).hasClass('woocommerce-form-login')) {
220 // Retain compatibility with previous full-width layout
221 html += ' style="width: 100%;"';
222 }
223
224 html += '>';
225
226 if (form_is_ultimate_member) { html += '</div>'; }
227
228 html += '<p class="forgetmenot';
229 if (form_is_gravity_forms) html += ' gfield';
230 html += '" style="font-size:small;';
231 if (!$(form).hasClass('woocommerce-form-login')) {
232 // Retain compatibility with previous full-width layout
233 html += ' max-width: 60%;';
234 }
235 html += '">';
236
237 if (form_is_ultimate_member) { html += '</div>'; }
238
239 // Would need further styling investigations to display this
240 if (!form_is_gravity_forms) {
241 html += '<span class="simba_tfa_otp_login_help">'+simba_tfasettings.otp_login_help+'</span>';
242 }
243
244 if (form_is_ultimate_member) {
245 html += '</div>';
246 }
247
248 if (user_can_trust) {
249
250 html += '<input type="checkbox" name="simba_tfa_mark_as_trusted" id="simba_tfa_mark_as_trusted" value="1"><label for="simba_tfa_mark_as_trusted">'+ simba_tfasettings.mark_as_trusted+'</label>';
251
252 }
253 }
254
255 html += '</p>';
256
257 var submit_button_text;
258 var submit_button_name;
259
260 // Gravity forms doesn't like its button being disabled
261 if (!form_is_gravity_forms) {
262
263 if ('button' == $submit_button.prop('nodeName').toLowerCase()) {
264 submit_button_text = $submit_button.text().trim();
265 submit_button_name = $submit_button.attr('name');
266 } else {
267 submit_button_text = $submit_button.val();
268 submit_button_name = $submit_button.attr('name');
269 }
270
271 html += '<p class="submit';
272
273 if (form_is_ultimate_member) { html += ' um-center'; }
274
275 html += '"><input id="tfa_login_btn" class="button button-primary button-large';
276
277 if (form_is_ultimate_member) { html += ' um-button'; }
278
279 if (form_is_login_form) { html += ' c-btn-rg hover:bg-main focus:bg-main'; }
280
281 html += '" type="submit" ';
282 if ('undefined' !== typeof submit_button_name && '' != submit_button_name) { html += 'name="'+submit_button_name+'" '; }
283 html += 'value="' + submit_button_text + '"></p>';
284
285 $submit_button.prop('disabled', true).hide();
286
287 }
288
289 if (form_retain_existing_elements && form_is_gravity_forms) {
290 // $submit_button.parents('form').first().append(html);
291 //$('<div style="clear:both;">'+html+'</div>').insertBefore($submit_button);
292 $(form).find('#gform_fields_login').append(html);
293 } else {
294 $submit_button.parents('form').first().prepend(html);
295 }
296
297 $('#login_error').hide();
298
299 if (user_already_trusted) {
300 if (form_retain_existing_elements) {
301 $submit_button.trigger('click');
302 } else {
303 $('#tfa_login_btn').trigger('click');
304 }
305 } else {
306
307 $('#simba_two_factor_auth').trigger('focus');
308
309 // Hide extra boxes of third party plugins
310 jQuery('.hide-when-displaying-tfa-input').hide();
311 }
312
313 }
314
315 /**
316 * This function gets attached to a form submission handler and decides whether to add an OTP field or not.
317 *
318 * @param Object e - submission event
319 *
320 * @return Boolean - whether to proceed with the submission or not
321 */
322 var form_submit_handler = function(e) {
323
324 console.log('Simba TFA: form submit request');
325
326 var form = e.target;
327
328 var form_is_gravity_forms = ('object' == typeof window['gform_gravityforms'] && 'undefined' !== typeof $(form).attr('id') && 'gform_' === $(form).attr('id').substring(0, 6));
329
330 // Turn off everything
331 $(form).off();
332
333 if (0 == $(form).find('#simba_two_factor_auth').length && check_and_possibly_show_otp_field(form)) {
334
335 if (form_is_gravity_forms) {
336 var form_id = $(form).attr('id').substring(6);
337 // Gravity Forms won't allow the form to submit if this is already true
338 window['gf_submitting_'+form_id] = false;
339 }
340
341 e.preventDefault();
342 return false;
343
344 }
345
346 return true;
347
348 };
349
350 if (simba_tfasettings.login_form_off_selectors) {
351 $(simba_tfasettings.login_form_off_selectors).off('submit');
352 }
353
354 $(simba_tfasettings.login_form_selectors).on('submit', form_submit_handler);
355
356 $(simba_tfasettings.login_form_selectors).find(get_username_identifiers()).on('blur', function() {
357 var $form = $(this).parents('form').first();
358 check_and_possibly_show_otp_field($form, true);
359 });
360
361 });
362