summaryrefslogtreecommitdiff
path: root/httemplate
diff options
context:
space:
mode:
authorJonathan Prykop <jonathan@freeside.biz>2015-11-21 01:54:21 -0600
committerJonathan Prykop <jonathan@freeside.biz>2015-11-21 01:54:21 -0600
commit45d0f6c6325fb8ab5fdc478a7dc278872defa479 (patch)
treee0dead35eba1d7af126a06463dfa8fe122e53755 /httemplate
parent8248d1c6ba608044c8f66a53daab254f476d5c6d (diff)
RT#29354: Password Security in Email
Diffstat (limited to 'httemplate')
-rwxr-xr-xhttemplate/edit/svc_acct.cgi7
-rw-r--r--httemplate/elements/change_password.html6
-rw-r--r--httemplate/elements/random_pass.html18
-rw-r--r--httemplate/elements/validate_password.html58
-rw-r--r--httemplate/misc/xmlhttp-validate_password.html50
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>