diff options
author | Jonathan Prykop <jonathan@freeside.biz> | 2015-11-21 01:54:21 -0600 |
---|---|---|
committer | Jonathan Prykop <jonathan@freeside.biz> | 2015-11-21 01:54:21 -0600 |
commit | 45d0f6c6325fb8ab5fdc478a7dc278872defa479 (patch) | |
tree | e0dead35eba1d7af126a06463dfa8fe122e53755 /httemplate | |
parent | 8248d1c6ba608044c8f66a53daab254f476d5c6d (diff) |
RT#29354: Password Security in Email
Diffstat (limited to 'httemplate')
-rwxr-xr-x | httemplate/edit/svc_acct.cgi | 7 | ||||
-rw-r--r-- | httemplate/elements/change_password.html | 6 | ||||
-rw-r--r-- | httemplate/elements/random_pass.html | 18 | ||||
-rw-r--r-- | httemplate/elements/validate_password.html | 58 | ||||
-rw-r--r-- | httemplate/misc/xmlhttp-validate_password.html | 50 |
5 files changed, 134 insertions, 5 deletions
diff --git a/httemplate/edit/svc_acct.cgi b/httemplate/edit/svc_acct.cgi index 31678a991..0cf0c20e1 100755 --- a/httemplate/edit/svc_acct.cgi +++ b/httemplate/edit/svc_acct.cgi @@ -50,7 +50,12 @@ 'required' => $part_svc->part_svc_column('_password')->required ) %> <TD> <INPUT TYPE="text" ID="clear_password" NAME="clear_password" VALUE="<% $password %>" SIZE=<% $pmax2 %> MAXLENGTH=<% $pmax %>> - <& /elements/random_pass.html, 'clear_password' &> + <& /elements/random_pass.html, 'clear_password' &><BR> + <DIV ID="clear_password_result" STYLE="font-size: smaller"></DIV> + <& '/elements/validate_password.html', + 'fieldid' => 'clear_password', + 'svcnum' => $svcnum + &> </TD> </TR> %}else{ diff --git a/httemplate/elements/change_password.html b/httemplate/elements/change_password.html index 625ba1fb5..7d8daaeaf 100644 --- a/httemplate/elements/change_password.html +++ b/httemplate/elements/change_password.html @@ -16,6 +16,12 @@ <& /elements/random_pass.html, $pre.'password', 'randomize' &> <INPUT TYPE="submit" VALUE="change"> <INPUT TYPE="button" VALUE="cancel" onclick="<%$pre%>toggle(false)"> + <DIV ID="<%$pre%>password_result" STYLE="font-size: smaller"></DIV> + <& '/elements/validate_password.html', + 'fieldid' => $pre.'password', + 'svcnum' => $svc_acct->svcnum, + + &> % if ( $error ) { <BR><SPAN STYLE="color: #ff0000"><% $error |h %></SPAN> % } diff --git a/httemplate/elements/random_pass.html b/httemplate/elements/random_pass.html index b215b77d9..14bbb581d 100644 --- a/httemplate/elements/random_pass.html +++ b/httemplate/elements/random_pass.html @@ -1,13 +1,23 @@ <INPUT TYPE="button" VALUE="<% emt($label) %>" onclick="randomPass()"> <SCRIPT TYPE="text/javascript"> function randomPass() { + var lower='<% join('', 'a'..'z') %>'; + var upper='<% join('', 'A'..'Z') %>'; + var number='<% join('', '0'..'9') %>'; + var symbol='`~!@#$%^&*-_=+:;<>,.?'; + var pw_set=lower+upper+number+symbol; + var pass=[]; + pass.push(lower.charAt(Math.floor(Math.random() * lower.length))); + pass.push(upper.charAt(Math.floor(Math.random() * lower.length))); + pass.push(number.charAt(Math.floor(Math.random() * number.length))); + pass.push(symbol.charAt(Math.floor(Math.random() * symbol.length))); var i=0; - var pw_set='<% join('', 'a'..'z', 'A'..'Z', '0'..'9' ) %>'; - var pass=''; - while(i < 8) { + while(i < 4) { i++; - pass += pw_set.charAt(Math.floor(Math.random() * pw_set.length)); + pass.push(pw_set.charAt(Math.floor(Math.random() * pw_set.length))); } + for(var j, x, i = pass.length; i; j = Math.floor(Math.random() * i), x = pass[--i], pass[i] = pass[j], pass[j] = x); + pass = pass.join(''); document.getElementById('<% $id %>').value = pass; } </SCRIPT> diff --git a/httemplate/elements/validate_password.html b/httemplate/elements/validate_password.html new file mode 100644 index 000000000..fd2cb6ca0 --- /dev/null +++ b/httemplate/elements/validate_password.html @@ -0,0 +1,58 @@ +<%doc> + +To validate passwords via javascript/xmlhttp: + + <INPUT ID="password_field" TYPE="text"> + <DIV ID="password_field_result"> + <& '/elements/validate_password.html', + fieldid => 'password_field', + svcnum => $svcnum + &> + +The ID of the input field can be anything; the ID of the DIV in which to display results +should be the input id plus '_result'. + +</%doc> + +<& '/elements/xmlhttp.html', + 'url' => $p.'misc/xmlhttp-validate_password.html', + 'subs' => [ 'validate_password' ], + 'method' => 'POST', # important not to put passwords in url +&> +<SCRIPT> +function add_password_validation (fieldid) { + var inputfield = document.getElementById(fieldid); + inputfield.onchange = function () { + var fieldid = this.id+'_result'; + var resultfield = document.getElementById(fieldid); + if (this.value) { + resultfield.innerHTML = '<SPAN STYLE="color: blue;">Validating password...</SPAN>'; + validate_password('fieldid',fieldid,'svcnum','<% $opt{'svcnum'} %>','password',this.value, + function (result) { + result = JSON.parse(result); + var resultfield = document.getElementById(result.fieldid); + if (resultfield) { + if (result.valid) { + resultfield.innerHTML = '<SPAN STYLE="color: green;">Password valid!</SPAN>'; + } else if (result.error) { + resultfield.innerHTML = '<SPAN STYLE="color: red;">'+result.error+'</SPAN>'; + } else { + result.syserror = result.syserror || 'Server error'; + resultfield.innerHTML = '<SPAN STYLE="color: red;">'+result.syserror+'</SPAN>'; + } + } + } + ); + } else { + resultfield.innerHTML = ''; + } + }; +} +add_password_validation('<% $opt{'fieldid'} %>'); +</SCRIPT> + +<%init> +my %opt = @_; +</%init> + + diff --git a/httemplate/misc/xmlhttp-validate_password.html b/httemplate/misc/xmlhttp-validate_password.html new file mode 100644 index 000000000..28dbf6460 --- /dev/null +++ b/httemplate/misc/xmlhttp-validate_password.html @@ -0,0 +1,50 @@ +<%doc> +Requires cgi params 'password' (plaintext) and 'sub' ('validate_password' is only +acceptable value.) Also accepts 'svcnum' (for svc_acct, will otherwise create an +empty dummy svc_acct) and 'fieldid' (for html post-processing, passed along in +results for convenience.) + +Returns a json-encoded hashref with keys of 'valid' (set to 1 if object is valid), +'error' (error text if password is invalid) or 'syserror' (error text if password +could not be validated.) Only one of these keys will be set. Will also set +'fieldid' if it was passed. +</%doc> + +<% encode_json($result) %> + +<%init> + +my $validate_password = sub { + my %arg = $cgi->param('arg'); + my %result; + + $result{'fieldid'} = $arg{'fieldid'} + if $arg{'fieldid'} =~ /^\w+$/; + + $result{'syserror'} = 'Request is not POST' unless $cgi->request_method eq 'POST'; + return \%result if $result{'syserror'}; + + my $password = $arg{'password'}; + $result{'syserror'} = 'Invoked without password' unless $password; + return \%result if $result{'syserror'}; + + my $svcnum = $arg{'svcnum'}; + $result{'syserror'} = 'Invalid svcnum' unless $svcnum =~ /^\d*$/; + return \%result if $result{'syserror'}; + + my $svc_acct = $svcnum + ? qsearchs('svc_acct',{'svcnum' => $svcnum}) + : (new FS::svc_acct {}); + $result{'syserror'} = 'Could not find service' unless $svc_acct; + return \%result if $result{'syserror'}; + + $result{'error'} = $svc_acct->is_password_allowed($password); + $result{'valid'} = 1 unless $result{'error'}; + return \%result; +}; + +my $result = ($cgi->param('sub') eq 'validate_password') + ? &$validate_password() + : { 'syserror' => 'Invalid sub' }; + +</%init> |