mirror of
https://github.com/dducret/kopano-webapp-passwd
synced 2026-08-04 14:33:31 +02:00
Improved UI, added password strength meter, frontend checks and an password change icon.
This commit is contained in:
parent
ceebc4174e
commit
33d35759a3
11 changed files with 281 additions and 185 deletions
191
js/external/PasswordMeter.js
vendored
Normal file
191
js/external/PasswordMeter.js
vendored
Normal file
|
|
@ -0,0 +1,191 @@
|
|||
Ext.namespace('Ext.ux.form.field');
|
||||
|
||||
/**
|
||||
* @class Ext.ux.form.field.PasswordMeter
|
||||
* @extends Ext.form.TextField
|
||||
* @xtype ux.passwordmeterfield
|
||||
*
|
||||
* @author Christoph Haas <christoph.h@sprinternet.at>
|
||||
* @version 0.1
|
||||
* @license MIT License: http://www.opensource.org/licenses/mit-license.php
|
||||
*
|
||||
* This implementation of the password fields shows a nice graph of how
|
||||
* secure the password is.
|
||||
* Original implementation for ExtJS 1.1 from http://testcases.pagebakers.com/PasswordMeter/.
|
||||
*/
|
||||
Ext.ux.form.field.PasswordMeter = Ext.extend(Ext.form.TextField, {
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
* @param {Object} config configuration object
|
||||
*/
|
||||
constructor : function(config)
|
||||
{
|
||||
config = config || {};
|
||||
|
||||
Ext.applyIf(config, {
|
||||
xtype : 'ux.passwordmeterfield',
|
||||
inputType: 'password',
|
||||
enableKeyEvents: true
|
||||
});
|
||||
|
||||
Ext.ux.form.field.PasswordMeter.superclass.constructor.call(this, config);
|
||||
},
|
||||
|
||||
// private
|
||||
initComponent:function()
|
||||
{
|
||||
Ext.ux.form.field.PasswordMeter.superclass.initComponent.apply(this, arguments);
|
||||
},
|
||||
|
||||
// private
|
||||
reset: function()
|
||||
{
|
||||
Ext.ux.form.field.PasswordMeter.superclass.reset.call(this);
|
||||
this.updateMeter();
|
||||
},
|
||||
|
||||
// private
|
||||
onKeyUp : function(event)
|
||||
{
|
||||
Ext.ux.form.field.PasswordMeter.superclass.onKeyUp.call(this);
|
||||
|
||||
this.updateMeter(this.getValue());
|
||||
|
||||
this.fireEvent('keyup', this, event);
|
||||
},
|
||||
|
||||
// private
|
||||
afterRender: function()
|
||||
{
|
||||
Ext.ux.form.field.PasswordMeter.superclass.afterRender.call(this);
|
||||
|
||||
|
||||
var width = this.getEl().getWidth();
|
||||
this.strengthMeterID = newID = Ext.id();
|
||||
this.scoreBarID = Ext.id();
|
||||
var objMeter = Ext.DomHelper.insertAfter(this.getEl(), {
|
||||
tag: "div",
|
||||
'class': "x-form-strengthmeter",
|
||||
'id': this.strengthMeterID,
|
||||
'style' : {
|
||||
width: width + 'px'
|
||||
}
|
||||
});
|
||||
Ext.DomHelper.append(objMeter, {
|
||||
tag: "div",
|
||||
'class': "x-form-strengthmeter-scorebar",
|
||||
'id': this.scoreBarID
|
||||
});
|
||||
|
||||
this.fireEvent('afterrender', this);
|
||||
},
|
||||
|
||||
/**
|
||||
* Return the score of the entered password.
|
||||
* It is a number between 0 and 100 where 100 is a very safe password.
|
||||
*
|
||||
* @returns {Number}
|
||||
*/
|
||||
getScore : function()
|
||||
{
|
||||
return this.calcStrength(this.getValue());
|
||||
},
|
||||
|
||||
/**
|
||||
* Sets the width of the meter, based on the score
|
||||
*
|
||||
* @param {String} val The current password
|
||||
*/
|
||||
updateMeter : function(val)
|
||||
{
|
||||
var maxWidth, score, scoreWidth, objMeter, scoreBar;
|
||||
|
||||
objMeter = Ext.get(this.strengthMeterID);
|
||||
scoreBar = Ext.get(this.scoreBarID);
|
||||
|
||||
maxWidth = objMeter.getWidth();
|
||||
if (val){
|
||||
score = this.calcStrength(val);
|
||||
scoreWidth = maxWidth - (maxWidth / 100) * score;
|
||||
scoreBar.applyStyles({margin: "0 0 0 " + (maxWidth - scoreWidth) + "px"}); // move the overlay to the right
|
||||
scoreBar.setWidth(scoreWidth, false); // downsize the overlay
|
||||
} else {
|
||||
scoreBar.applyStyles({margin: "0"});
|
||||
scoreBar.setWidth(maxWidth, false);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Calculates the strength of a password
|
||||
*
|
||||
* @param {Object} p The password that needs to be calculated
|
||||
* @return {int} intScore The strength score of the password
|
||||
*/
|
||||
calcStrength: function(p)
|
||||
{
|
||||
// PASSWORD LENGTH
|
||||
var len = p.length, score = len;
|
||||
|
||||
if (len > 0 && len <= 4) { // length 4 or
|
||||
// less
|
||||
score += len
|
||||
} else if (len >= 5 && len <= 7) {
|
||||
// length between 5 and 7
|
||||
score += 6;
|
||||
} else if (len >= 8 && len <= 15) {
|
||||
// length between 8 and 15
|
||||
score += 12;
|
||||
} else if (len >= 16) { // length 16 or more
|
||||
score += 18;
|
||||
}
|
||||
|
||||
// LETTERS (Not exactly implemented as dictacted above
|
||||
// because of my limited understanding of Regex)
|
||||
if (p.match(/[a-z]/)) {
|
||||
// [verified] at least one lower case letter
|
||||
score += 1;
|
||||
}
|
||||
if (p.match(/[A-Z]/)) { // [verified] at least one upper
|
||||
// case letter
|
||||
score += 5;
|
||||
}
|
||||
// NUMBERS
|
||||
if (p.match(/\d/)) { // [verified] at least one
|
||||
// number
|
||||
score += 5;
|
||||
}
|
||||
if (p.match(/(?:.*?\d){3}/)) {
|
||||
// [verified] at least three numbers
|
||||
score += 5;
|
||||
}
|
||||
|
||||
// SPECIAL CHAR
|
||||
if (p.match(/[\!,@,#,$,%,\^,&,\*,\?,_,~]/)) {
|
||||
// [verified] at least one special character
|
||||
score += 5;
|
||||
}
|
||||
// [verified] at least two special characters
|
||||
if (p.match(/(?:.*?[\!,@,#,$,%,\^,&,\*,\?,_,~]){2}/)) {
|
||||
score += 5;
|
||||
}
|
||||
|
||||
// COMBOS
|
||||
if (p.match(/(?=.*[a-z])(?=.*[A-Z])/)) {
|
||||
// [verified] both upper and lower case
|
||||
score += 2;
|
||||
}
|
||||
if (p.match(/(?=.*\d)(?=.*[a-z])(?=.*[A-Z])/)) {
|
||||
// [verified] both letters and numbers
|
||||
score += 2;
|
||||
}
|
||||
// [verified] letters, numbers, and special characters
|
||||
if (p.match(/(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[\!,@,#,$,%,\^,&,\*,\?,_,~])/)) {
|
||||
score += 2;
|
||||
}
|
||||
|
||||
return Math.min(Math.round(score * 2), 100);
|
||||
}
|
||||
});
|
||||
|
||||
Ext.reg('ux.passwordmeterfield', Ext.ux.form.field.PasswordMeter);
|
||||
|
|
@ -30,26 +30,30 @@ Zarafa.plugins.passwd.settings.PasswdPanel = Ext.extend(Ext.form.FormPanel, {
|
|||
}, {
|
||||
xtype : 'textfield',
|
||||
name : 'current_password',
|
||||
ref : 'current_password',
|
||||
fieldLabel : dgettext("plugin_passwd", 'Current password'),
|
||||
inputType : 'password',
|
||||
allowBlank : false,
|
||||
listeners : {
|
||||
change : this.onFieldChange,
|
||||
scope : this
|
||||
}
|
||||
}, {
|
||||
xtype : 'textfield',
|
||||
xtype : 'ux.passwordmeterfield',
|
||||
name : 'new_password',
|
||||
ref : 'new_password',
|
||||
allowBlank : false,
|
||||
fieldLabel : dgettext("plugin_passwd", 'New password'),
|
||||
inputType : 'password',
|
||||
listeners : {
|
||||
change : this.onFieldChange,
|
||||
scope : this
|
||||
}
|
||||
}, {
|
||||
xtype : 'textfield',
|
||||
xtype : 'ux.passwordmeterfield',
|
||||
name : 'new_password_repeat',
|
||||
allowBlank : false,
|
||||
ref : 'new_password_repeat',
|
||||
fieldLabel : dgettext("plugin_passwd", 'Retype new password'),
|
||||
inputType : 'password',
|
||||
listeners : {
|
||||
change : this.onFieldChange,
|
||||
scope : this
|
||||
|
|
|
|||
|
|
@ -19,11 +19,14 @@ Zarafa.plugins.passwd.settings.SettingsPasswdCategory = Ext.extend(Zarafa.settin
|
|||
Ext.applyIf(config, {
|
||||
title : dgettext("plugin_passwd", 'Change Password'),
|
||||
categoryIndex : 9997,
|
||||
iconCls : 'zarafa-settings-category-passwd',
|
||||
xtype : 'zarafa.settingspasswdcategory',
|
||||
items : [{
|
||||
xtype : 'zarafa.settingspasswdwidget',
|
||||
settingsContext : config.settingsContext
|
||||
}]
|
||||
},
|
||||
container.populateInsertionPoint('context.settings.category.passwd', this)
|
||||
]
|
||||
});
|
||||
|
||||
Zarafa.plugins.passwd.settings.SettingsPasswdCategory.superclass.constructor.call(this, config);
|
||||
|
|
|
|||
|
|
@ -19,14 +19,9 @@ Zarafa.plugins.passwd.settings.SettingsPasswdWidget = Ext.extend(Zarafa.settings
|
|||
config = config || {};
|
||||
|
||||
Ext.applyIf(config, {
|
||||
height : 175,
|
||||
width : 400,
|
||||
title : dgettext("plugin_passwd", 'Change Password'),
|
||||
xtype : 'zarafa.settingspasswdwidget',
|
||||
layout : {
|
||||
// override from SettingsWidget
|
||||
type : 'fit'
|
||||
},
|
||||
layout: 'form',
|
||||
items : [{
|
||||
xtype : 'zarafa.passwdpanel',
|
||||
ref : 'passwdPanel',
|
||||
|
|
@ -51,10 +46,42 @@ Zarafa.plugins.passwd.settings.SettingsPasswdWidget = Ext.extend(Zarafa.settings
|
|||
// listen to savesettings and discardsettings to save/discard delegation data
|
||||
var contextModel = this.settingsContext.getModel();
|
||||
|
||||
this.mon(contextModel, 'beforesavesettings', this.onBeforeSaveSettings, this);
|
||||
this.mon(contextModel, 'savesettings', this.onSaveSettings, this);
|
||||
this.mon(contextModel, 'discardsettings', this.onDiscardSettings, this);
|
||||
},
|
||||
|
||||
/**
|
||||
* Event handler will be called when {@link Zarafa.settings.SettingsContextModel#beforesavesettings} event is fired.
|
||||
* This function will validate the formdata.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
onBeforeSaveSettings : function()
|
||||
{
|
||||
// do some quick checks before submitting
|
||||
if(this.passwdPanel.new_password.getValue() != this.passwdPanel.new_password_repeat.getValue()) {
|
||||
Ext.MessageBox.alert(dgettext("plugin_passwd", 'Error'), dgettext("plugin_passwd", 'New passwords do not match.'));
|
||||
return false;
|
||||
} else if(Ext.isEmpty(this.passwdPanel.current_password.getValue())) {
|
||||
Ext.MessageBox.alert(dgettext("plugin_passwd", 'Error'), dgettext("plugin_passwd", 'Current password is empty.'));
|
||||
return false;
|
||||
} else if(Ext.isEmpty(this.passwdPanel.new_password.getValue()) || Ext.isEmpty(this.passwdPanel.new_password_repeat.getValue())) {
|
||||
Ext.MessageBox.alert(dgettext("plugin_passwd", 'Error'), dgettext("plugin_passwd", 'New password is empty.'));
|
||||
return false;
|
||||
} else if(!this.passwdPanel.getForm().isValid()) {
|
||||
Ext.MessageBox.alert(dgettext("plugin_passwd", 'Error'), dgettext("plugin_passwd", 'One or more fields does contain errors.'));
|
||||
return false;
|
||||
} else {
|
||||
// do a quick score check:
|
||||
if(this.passwdPanel.new_password.getScore() < 70) {
|
||||
Ext.MessageBox.alert(dgettext("plugin_passwd", 'Error'), dgettext("plugin_passwd", 'Password is weak. Password should contain capital, non-capital letters and numbers. Password should have 8 to 20 characters.'));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Event handler will be called when {@link Zarafa.settings.SettingsContextModel#savesettings} event is fired.
|
||||
* This will relay this event to {@link Zarafa.plugins.passwd.settings.PasswdPanel PasswdPanel} so it can
|
||||
|
|
@ -71,8 +98,11 @@ Zarafa.plugins.passwd.settings.SettingsPasswdWidget = Ext.extend(Zarafa.settings
|
|||
|
||||
// send request
|
||||
container.getRequest().singleRequest('passwdmodule', 'save', data, new Zarafa.plugins.passwd.data.PasswdResponseHandler({
|
||||
callbackFn : function(success, response) {
|
||||
callbackFn: function (success, response) {
|
||||
this.ownerCt.hideSavingMask(success);
|
||||
if(success) {
|
||||
this.passwdPanel.getForm().reset();
|
||||
}
|
||||
},
|
||||
scope : this
|
||||
}));
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue