PluginProbe
Two Factor Authentication / 1.14.23
Two Factor Authentication v1.14.23
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.14.23, at simba-tfa/includes/tfa.js

356 lines 12.5 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
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"]';
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 // Gravity Forms won't submit if the elements are hidden
166 var form_retain_existing_elements = form_is_gravity_forms ? true : false;
167
168 // name="Submit" is WP-Members. 'submit' is Theme My Login starting from 7.x
169 $submit_button = $(form).find('input[name="wp-submit"], input[name="Submit"], input[name="submit"]');
170 // 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:
171 if (0 == $submit_button.length) {
172 $submit_button = $(form).find('input[type="submit"], button[type="submit"]').first();
173 }
174
175 if (!form_retain_existing_elements) {
176 // Hide all elements in a browser-safe way
177 // .user-pass-wrap is the wrapper used (instead of a paragraph) on wp-login.php from WP 5.3
178 // .um-row : Ultimate Member
179 $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').each(function(i) {
180 $(this).css('visibility', 'hidden').css('position', 'absolute');
181 // On the WooCommerce form, the 'required' asterisk in the child <span> still shows without this
182 $(this).find('span').css('visibility', 'hidden').css('position', 'absolute');
183 });
184
185 // WP-Members
186 $submit_button.parents('#wpmem_login').find('fieldset').css('visibility', 'hidden').css('position', 'absolute');
187
188 }
189
190 // Add new field and controls
191 var html = '';
192
193 if (form_is_ultimate_member) {
194 html += '<div class="um-row">';
195 }
196
197 if (user_already_trusted) {
198
199 html += '<br><span class="simbaotp_is_trusted">'+simba_tfasettings.is_trusted+'</span>';
200
201 } else {
202
203 if (form_is_ultimate_member) { html += '<div class="um-field um-field-text um-field-type_text"><div class="um-field-label">'; }
204
205 html += '<label ';
206
207 if (form_is_gravity_forms) {
208 html += 'class="gfield_label"';
209 }
210
211 html += 'for="simba_two_factor_auth">';
212
213 html += simba_tfasettings.otp + '<br><input type="text" name="two_factor_code" id="simba_two_factor_auth" autocomplete="off" data-lpignore="true"';
214
215 if ($(form).hasClass('woocommerce-form-login')) {
216 // Retain compatibility with previous full-width layout
217 html += ' style="width: 100%;"';
218 }
219
220 html += '></label>';
221
222 if (form_is_ultimate_member) { html += '</div>'; }
223
224 html += '<p class="forgetmenot';
225 if (form_is_gravity_forms) html += ' gfield';
226 html += '" style="font-size:small;';
227 if (!$(form).hasClass('woocommerce-form-login')) {
228 // Retain compatibility with previous full-width layout
229 html += ' max-width: 60%;';
230 }
231 html += '">';
232
233 if (form_is_ultimate_member) { html += '</div>'; }
234
235 // Would need further styling investigations to display this
236 if (!form_is_gravity_forms) {
237 html += '<span class="simba_tfa_otp_login_help">'+simba_tfasettings.otp_login_help+'</span>';
238 }
239
240 if (form_is_ultimate_member) {
241 html += '</div>';
242 }
243
244 if (user_can_trust) {
245
246 html += '<br><input type="checkbox" name="simba_tfa_mark_as_trusted" id="simba_tfa_mark_as_trusted" value="1"><label for="simba_tfa_mark_as_trusted" style="display:inline;">'+ simba_tfasettings.mark_as_trusted+'</label>';
247
248 }
249 }
250
251 html += '</p>';
252
253 var submit_button_text;
254 var submit_button_name;
255
256 // Gravity forms doesn't like its button being disabled
257 if (!form_is_gravity_forms) {
258
259 if ('button' == $submit_button.prop('nodeName').toLowerCase()) {
260 submit_button_text = $submit_button.text().trim();
261 submit_button_name = $submit_button.attr('name');
262 } else {
263 submit_button_text = $submit_button.val();
264 submit_button_name = $submit_button.attr('name');
265 }
266
267 html += '<p class="submit';
268
269 if (form_is_ultimate_member) { html += ' um-center'; }
270
271 html += '"><input id="tfa_login_btn" class="button button-primary button-large';
272
273 if (form_is_ultimate_member) { html += ' um-button'; }
274
275 html += '" type="submit" ';
276 if ('undefined' !== typeof submit_button_name && '' != submit_button_name) { html += 'name="'+submit_button_name+'" '; }
277 html += 'value="' + submit_button_text + '"></p>';
278
279 $submit_button.prop('disabled', true).hide();
280
281 }
282
283 if (form_retain_existing_elements && form_is_gravity_forms) {
284 // $submit_button.parents('form').first().append(html);
285 //$('<div style="clear:both;">'+html+'</div>').insertBefore($submit_button);
286 $(form).find('#gform_fields_login').append(html);
287 } else {
288 $submit_button.parents('form').first().prepend(html);
289 }
290
291 $('#login_error').hide();
292
293 if (user_already_trusted) {
294 if (form_retain_existing_elements) {
295 $submit_button.trigger('click');
296 } else {
297 $('#tfa_login_btn').trigger('click');
298 }
299 } else {
300
301 $('#simba_two_factor_auth').trigger('focus');
302
303 // Hide extra boxes of third party plugins
304 jQuery('.hide-when-displaying-tfa-input').hide();
305 }
306
307 }
308
309 /**
310 * This function gets attached to a form submission handler and decides whether to add an OTP field or not.
311 *
312 * @param Object e - submission event
313 *
314 * @return Boolean - whether to proceed with the submission or not
315 */
316 var form_submit_handler = function(e) {
317
318 console.log('Simba TFA: form submit request');
319
320 var form = e.target;
321
322 var form_is_gravity_forms = ('object' == typeof window['gform_gravityforms'] && 'undefined' !== typeof $(form).attr('id') && 'gform_' === $(form).attr('id').substring(0, 6));
323
324 // Turn off everything
325 $(form).off();
326
327 if (0 == $(form).find('#simba_two_factor_auth').length && check_and_possibly_show_otp_field(form)) {
328
329 if (form_is_gravity_forms) {
330 var form_id = $(form).attr('id').substring(6);
331 // Gravity Forms won't allow the form to submit if this is already true
332 window['gf_submitting_'+form_id] = false;
333 }
334
335 e.preventDefault();
336 return false;
337
338 }
339
340 return true;
341
342 };
343
344 if (simba_tfasettings.login_form_off_selectors) {
345 $(simba_tfasettings.login_form_off_selectors).off('submit');
346 }
347
348 $(simba_tfasettings.login_form_selectors).on('submit', form_submit_handler);
349
350 $(simba_tfasettings.login_form_selectors).find(get_username_identifiers()).on('blur', function() {
351 var $form = $(this).parents('form').first();
352 check_and_possibly_show_otp_field($form, true);
353 });
354
355 });
356