plugins/yubikey/yubikey.js
changeset 39 6212d849ab08
parent 38 d109af008343
equal deleted inserted replaced
38:d109af008343 39:6212d849ab08
     1 // sample OTP:
       
     2 // ttttvvvvvvcurikvhjcvnlnbecbkubjvuittbifhndhn
       
     3 // charset: cbdefghijklnrtuv
       
     4 
       
     5 var yk_interval = false;
       
     6 
       
     7 var YK_SEC_NORMAL_USERNAME = 1;
       
     8 var YK_SEC_NORMAL_PASSWORD = 2;
       
     9 var YK_SEC_ELEV_USERNAME = 4;
       
    10 var YK_SEC_ELEV_PASSWORD = 8;
       
    11 
       
    12 var yubikey_otp_current = false;
       
    13 
       
    14 function yk_mb_init(fieldid, statid)
       
    15 {
       
    16   load_component(['messagebox', 'fadefilter', 'flyin', 'jquery', 'jquery-ui', 'l10n']);
       
    17   var mp = miniPrompt(yk_mb_construct);
       
    18   if ( typeof(fieldid) == 'function' )
       
    19   {
       
    20     var input = mp.getElementsByTagName('input')[0];
       
    21     input.submit_func = fieldid;
       
    22   }
       
    23   else if ( fieldid && statid )
       
    24   {
       
    25     var input = mp.getElementsByTagName('input')[0];
       
    26     input.yk_field_id = fieldid;
       
    27     input.yk_status_id = statid;
       
    28   }
       
    29 }
       
    30 
       
    31 function yk_mb_construct(mp)
       
    32 {
       
    33   mp.innerHTML = '';
       
    34   mp.style.textAlign = 'center';
       
    35   mp.innerHTML = '<h3>' + $lang.get('yubiauth_msg_please_touch_key') + '</h3>';
       
    36   var progress = document.createElement('div');
       
    37   $(progress).addClass('yubikey_bar').css('text-align', 'left');
       
    38   var progimg = document.createElement('img');
       
    39   progimg.src = cdnPath + '/images/spacer.gif';
       
    40   progress.appendChild(progimg);
       
    41   mp.appendChild(progress);
       
    42   var ta = document.createElement('input');
       
    43   ta.submitted = false;
       
    44   $(ta)
       
    45     .css('background-color', 'transparent')
       
    46     .css('border-width', '0px')
       
    47     .css('color', '#fff')
       
    48     .css('font-size', '1px')
       
    49     .css('padding', '0')
       
    50     .css('opacity', '0')
       
    51     .attr('size', '1')
       
    52     .keyup(function(e)
       
    53       {
       
    54         if ( e.keyCode == 27 )
       
    55         {
       
    56           window.clearInterval(yk_interval);
       
    57           miniPromptDestroy(this);
       
    58         }
       
    59         // 0.3: submit only upon a keycode 13
       
    60         else if ( e.keyCode == 13 )
       
    61         {
       
    62           this.submitted = true;
       
    63           yk_handle_submit(this);
       
    64         }
       
    65         else
       
    66         {
       
    67           $('div.yubikey_bar > img', this.parentNode)
       
    68             .css('width', String(this.value.length * 2) + 'px')
       
    69             //.css('background-position', String((this.value.length > 44 ? 44 : this.value.length) - 44) + 'px -88px');
       
    70         }
       
    71         e.preventDefault();
       
    72         e.stopPropagation();
       
    73       });
       
    74   mp.appendChild(ta);
       
    75   setTimeout(function()
       
    76     {
       
    77       window.yk_interval = setInterval(function()
       
    78         {
       
    79           ta.focus();
       
    80         }, 50);
       
    81     }, 750);
       
    82   var info = document.createElement('p');
       
    83   $(info)
       
    84     .append('<span style="color: #ffffff; font-size: smaller;">' + $lang.get('yubiauth_msg_close_instructions') + '</span>&nbsp;&nbsp;')
       
    85     .append('<a class="abutton abutton_green" href="#" onclick="miniPromptDestroy(this); return false;">' + $lang.get('etc_cancel') + '</a>&nbsp;&nbsp;')
       
    86     //.append('<br />')
       
    87     .append('<span style="color: #909090; font-size: smaller;">' + $lang.get('yubiauth_msg_close_instructions') + '</span>')
       
    88     .css('margin-top', '0');
       
    89   mp.appendChild(info);
       
    90 }
       
    91 
       
    92 function yk_handle_submit(ta)
       
    93 {
       
    94   if ( ta.value.length > 44 || !ta.value.match(/^[cbdefghijklnrtuv]+$/) )
       
    95   {
       
    96     // report "invalid characters"
       
    97     setTimeout(function()
       
    98       {
       
    99         var parent = ta.parentNode;
       
   100         var tabackup = {
       
   101           field_id: ta.yk_field_id,
       
   102           status_id: ta.yk_status_id,
       
   103           submit_func: ta.submit_func
       
   104         };
       
   105         yk_mb_construct(parent);
       
   106         var input = parent.getElementsByTagName('input')[0];
       
   107         if ( tabackup.field_id )
       
   108           input.yk_field_id = tabackup.field_id;
       
   109         if ( tabackup.status_id )
       
   110           input.yk_status_id = tabackup.status_id;
       
   111         if ( tabackup.submit_func )
       
   112           input.submit_func = tabackup.submit_func;
       
   113       }, 1000);
       
   114     $('h3', ta.parentNode).text($lang.get(ta.value.length > 44 ? 'yubiauth_msg_too_long' : 'yubiauth_msg_invalid_chars'));
       
   115     $('div.yubikey_bar > img', this.parentNode).addClass('yubikey_bar_error');
       
   116     return false;
       
   117   }
       
   118   
       
   119   window.clearInterval(yk_interval);
       
   120   
       
   121   if ( ta.yk_field_id && ta.yk_status_id )
       
   122   {
       
   123     var field = document.getElementById(ta.yk_field_id);
       
   124     var status = document.getElementById(ta.yk_status_id);
       
   125     if ( $(status).hasClass('empty') || $(status).hasClass('rmpending') )
       
   126     {
       
   127       $(status)
       
   128       .next('span.yubikey_pubkey')
       
   129         .text(ta.value.substr(0, 12))
       
   130       .next('a.yubikey_enroll')
       
   131         .text($lang.get('yubiauth_ctl_btn_change_key'))
       
   132         .addClass('abutton_green')
       
   133         .after(' <a class="abutton abutton_red yubikey_enroll" href="#yk_clear" onclick="yk_clear(\'' + ta.yk_field_id + '\', \'' + ta.yk_status_id + '\'); return false;">'
       
   134                + $lang.get('yubiauth_ctl_btn_clear') +
       
   135                '</a>');
       
   136     }
       
   137     $(status).removeClass('empty').removeClass('enrolled').removeClass('rmpending').addClass('savepending').html($lang.get('yubiauth_ctl_status_enrolled_pending'));
       
   138     $(status).next('span.yubikey_pubkey').text(ta.value.substr(0, 12));
       
   139     field.value = ta.value;
       
   140     miniPromptDestroy(ta);
       
   141     return true;
       
   142   }
       
   143   else if ( ta.submit_func )
       
   144   {
       
   145     ta.submit_func(ta);
       
   146   }
       
   147   else
       
   148   {
       
   149     miniPromptDestroy(ta);
       
   150   }
       
   151 }
       
   152 
       
   153 function yk_login_validate_reqs(ta)
       
   154 {
       
   155   $(ta.parentNode).remove('p');
       
   156   yubikey_otp_current = ta.value;
       
   157   
       
   158   miniPromptDestroy(ta, true);
       
   159   
       
   160   if ( logindata )
       
   161   {
       
   162     if ( logindata.mb_object )
       
   163     {
       
   164       // login window is open
       
   165       if ( user_level == USER_LEVEL_GUEST )
       
   166       {
       
   167         // for guests, get the user's yubikey auth flags
       
   168         // we're still ok to submit, so make sure twofactor isn't enabled
       
   169         // as we are a guest, we have to get the flags for the user from the server
       
   170         var ajax = ajaxMakeXHR();
       
   171         var uri = makeUrlNS('Special', 'Yubikey', 'get_flags=' + ta.value.substr(0, 12));
       
   172         var flags = 0;
       
   173         try
       
   174         {
       
   175           ajax.open('GET', uri, false);
       
   176           ajax.send(null);
       
   177           
       
   178           if ( ajax.readyState == 4 && ajax.status == 200 )
       
   179           {
       
   180             // we got it
       
   181             var response = String(ajax.responseText + '');
       
   182             if ( check_json_response(response) )
       
   183             {
       
   184               response = parseJSON(response);
       
   185               flags = response.flags || 0;
       
   186             }
       
   187           }
       
   188         }
       
   189         catch ( e )
       
   190         {
       
   191           ajaxLoginSetStatus(AJAX_STATUS_ERROR);
       
   192           return false;
       
   193         }
       
   194         var show_username = flags & YK_SEC_NORMAL_USERNAME;
       
   195         var show_password = flags & YK_SEC_NORMAL_PASSWORD;
       
   196       }
       
   197       else
       
   198       {
       
   199         var show_username = window.yk_user_flags & YK_SEC_ELEV_USERNAME;
       
   200         var show_password = window.yk_user_flags & YK_SEC_ELEV_PASSWORD;
       
   201       }
       
   202       if ( !show_username )
       
   203         $('#ajax_login_field_username').parent('td').hide().prev().hide();
       
   204       if ( !show_password )
       
   205         $('#ajax_login_field_password').parent('td').hide().prev().hide();
       
   206       
       
   207       var can_submit = true;
       
   208       if ( show_username && !$('#ajax_login_field_username').attr('value') )
       
   209       {
       
   210         $('#ajax_login_field_username').focus();
       
   211         
       
   212         if ( !show_password )
       
   213           $('#ajax_login_field_username').keyup(function(e)
       
   214             {
       
   215               // assign press of Enter in username field to submit
       
   216               if ( e.keyCode == 13 )
       
   217               {
       
   218                 $('#messageBoxButtons input:button:first').click();
       
   219               }
       
   220             });
       
   221         
       
   222         can_submit = false;
       
   223       }
       
   224       if ( show_password && !$('#ajax_login_field_password').attr('value') )
       
   225       {
       
   226         if ( can_submit )
       
   227         {
       
   228           // can_submit only true if show_username false
       
   229           $('#ajax_login_field_password').focus();
       
   230         }
       
   231         can_submit = false;
       
   232       }
       
   233       
       
   234       if ( can_submit )
       
   235       {
       
   236         $('#messageBoxButtons input:button:first').click();
       
   237       }
       
   238     }
       
   239   }
       
   240 }
       
   241 
       
   242 function yk_clear(field_id, status_id)
       
   243 {
       
   244   var field = document.getElementById(field_id);
       
   245   var status = document.getElementById(status_id);
       
   246   
       
   247   var was_pending = $(field).hasClass('wasempty');
       
   248   
       
   249   $(field).attr('value', '');
       
   250   $(status)
       
   251     .removeClass('savepending')
       
   252     .removeClass('enrolled')
       
   253     .addClass( was_pending ? 'empty' : 'rmpending' )
       
   254     .text( was_pending ? $lang.get('yubiauth_ctl_status_empty') : $lang.get('yubiauth_ctl_status_remove_pending') )
       
   255     .next('span.yubikey_pubkey')
       
   256       .text('')
       
   257     .next('a')
       
   258       .text($lang.get('yubiauth_ctl_btn_enroll'))
       
   259       .removeClass('abutton_green')
       
   260     .next('a')
       
   261       .remove();
       
   262 }
       
   263 
       
   264 addOnloadHook(function()
       
   265   {
       
   266     if ( is_iPhone )
       
   267       // kinda can't plug a yubikey into an iPhone
       
   268       // ... yet?
       
   269       return;
       
   270     
       
   271     attachHook('login_build_form', 'yk_login_dlg_hook(table, data);');
       
   272     attachHook('login_build_userinfo', 'if ( window.yubikey_otp_current ) userinfo.yubikey_otp = window.yubikey_otp_current;');
       
   273     if ( title == namespace_list.Special + 'Preferences/Yubikey' )
       
   274     {
       
   275       load_component(['jquery', 'jquery-ui', 'expander']);
       
   276     }
       
   277   });
       
   278 
       
   279 function yk_login_dlg_hook(table, data)
       
   280 {
       
   281   window.yubikey_otp_current = false;
       
   282   var tr = document.createElement('tr');
       
   283   var td = document.createElement('td');
       
   284   $(td)
       
   285     .attr('colspan', '2')
       
   286     .css('text-align', 'center')
       
   287     .css('font-size', 'smaller')
       
   288     .css('font-weight', 'bold')
       
   289     .html('<a href="#" onclick="yk_mb_init(yk_login_validate_reqs); return false;" style="color: #6fa202">' + $lang.get('yubiauth_btn_enter_otp') + '</a>');
       
   290   $('a', td).blur(function(e)
       
   291     {
       
   292       $('#messageBoxButtons input:button:first').focus();
       
   293       $('#ajax_login_field_captcha').focus();
       
   294     });
       
   295   if ( ( window.yk_reg_require_otp || window.yk_user_enabled ) && !data.locked_out.locked_out )
       
   296   {
       
   297     setTimeout(function()
       
   298       {
       
   299         yk_mb_init(yk_login_validate_reqs);
       
   300       }, 750);
       
   301   }
       
   302   tr.appendChild(td);
       
   303   table.appendChild(tr);
       
   304 }
       
   305