diff --git a/CHANGELOG.md b/CHANGELOG.md index bb351c5..a66252f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,4 +4,9 @@ ## 1.1 (19th Dec 2013) - - Bug fixes for LDAP plugin \ No newline at end of file + - Bug fixes for LDAP plugin + +## 1.2 (20th May 2015) + + - Bug fixes for LDAP plugin + - Improved UI \ No newline at end of file diff --git a/builds/passwd-1.2.zip b/builds/passwd-1.2.zip new file mode 100644 index 0000000..1706ae5 Binary files /dev/null and b/builds/passwd-1.2.zip differ diff --git a/config.php b/config.php index 133f679..0dcc16f 100644 --- a/config.php +++ b/config.php @@ -9,7 +9,16 @@ define('PLUGIN_PASSWD_LDAP', false); define('PLUGIN_PASSWD_LDAP_BASEDN', 'dc=example,dc=com'); /** URI to access LDAP server **/ -define('PLUGIN_PASSWD_LDAP_URI', 'localhost'); +define('PLUGIN_PASSWD_LDAP_URI', 'ldap://localhost'); + +/** Use TLS to access LDAP server **/ +define('PLUGIN_PASSWD_LDAP_USE_TLS', true); + +/** Bind DN to access LDAP server (keep empty for anonymous bind) **/ +define('PLUGIN_PASSWD_LDAP_BIND_DN', ""); + +/** Bind password to access LDAP server (keep empty for anonymous bind) **/ +define('PLUGIN_PASSWD_LDAP_BIND_PW', ""); /** Set to true if you login with username@tenantname **/ define('PLUGIN_PASSWD_LOGIN_WITH_TENANT', false); diff --git a/js/external/PasswordMeter.js b/js/external/PasswordMeter.js new file mode 100644 index 0000000..b8ce416 --- /dev/null +++ b/js/external/PasswordMeter.js @@ -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 + * @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); \ No newline at end of file diff --git a/js/settings/PasswdPanel.js b/js/settings/PasswdPanel.js index 6083906..730d695 100644 --- a/js/settings/PasswdPanel.js +++ b/js/settings/PasswdPanel.js @@ -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 diff --git a/js/settings/SettingsPasswdCategory.js b/js/settings/SettingsPasswdCategory.js index 6cd21d0..fd73c68 100644 --- a/js/settings/SettingsPasswdCategory.js +++ b/js/settings/SettingsPasswdCategory.js @@ -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); diff --git a/js/settings/SettingsPasswdWidget.js b/js/settings/SettingsPasswdWidget.js index c8b473b..59a93b6 100644 --- a/js/settings/SettingsPasswdWidget.js +++ b/js/settings/SettingsPasswdWidget.js @@ -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 })); diff --git a/lib/PasswordMeter.js b/lib/PasswordMeter.js deleted file mode 100644 index 7406beb..0000000 --- a/lib/PasswordMeter.js +++ /dev/null @@ -1,158 +0,0 @@ -// Create user extensions namespace (Ext.ux) -Ext.namespace('Ext.ux'); - -/** - * Ext.ux.PasswordMeter Extension Class - * - * @author Eelco Wiersma - * @version 0.2 - * - * Algorithm based on code of Tane - * http://digitalspaghetti.me.uk/index.php?q=jquery-pstrength - * and Steve Moitozo - * http://www.geekwisdom.com/dyn/passwdmeter - * - * @license MIT License: http://www.opensource.org/licenses/mit-license.php - * - * @class Ext.ux.PasswordMeter - * @extends Ext.form.TextField - * @constructor - * Creates new Ext.ux.PasswordMeter - */ -Ext.ux.PasswordMeter = function(config) { - - // call parent constructor - Ext.ux.PasswordMeter.superclass.constructor.call(this, config); - -}; - -Ext.extend(Ext.ux.PasswordMeter, Ext.form.TextField, { - /** - * @cfg {String} inputType The type attribute for input fields -- e.g. text, password (defaults to "password"). - */ - inputType: 'text', - // private - onRender: function(ct, position) { - Ext.ux.PasswordMeter.superclass.onRender.call(this, ct, position); - - var elp = this.el.findParent('.x-form-element', 5, true); - this.objMeter = ct.createChild({tag: "div", 'class': "strengthMeter"}); - - this.objMeter.setWidth(elp.getWidth(true)-17); - this.scoreBar = this.objMeter.createChild({tag: "div", 'class': "scoreBar"}); - - if(Ext.isIE && !Ext.isIE7) { // Fix style for IE6 - this.objMeter.setStyle('margin-left', '3px'); - } - }, - // private - initEvents: function() { - Ext.ux.PasswordMeter.superclass.initEvents.call(this); - - this.el.on('keyup', this.updateMeter, this); - }, - /** - * Sets the width of the meter, based on the score - * @param {Object} e - * Private function - */ - updateMeter: function(e) { - var score = 0 - var p = e.target.value; - - var maxWidth = this.objMeter.getWidth() - 2; - - var nScore = this.calcStrength(p); - - // Set new width - var nRound = Math.round(nScore * 2); - - if (nRound > 100) { - nRound = 100; - } - - var scoreWidth = (maxWidth / 100) * nRound; - this.scoreBar.setWidth(scoreWidth, true); - }, - /** - * 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) { - var intScore = 0; - - // PASSWORD LENGTH - intScore += p.length; - - if(p.length > 0 && p.length <= 4) { // length 4 or less - intScore += p.length; - } - else if (p.length >= 5 && p.length <= 7) { // length between 5 and 7 - intScore += 6; - } - else if (p.length >= 8 && p.length <= 15) { // length between 8 and 15 - intScore += 12; - //alert(intScore); - } - else if (p.length >= 16) { // length 16 or more - intScore += 18; - //alert(intScore); - } - - // 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 - intScore += 1; - } - if (p.match(/[A-Z]/)) { // [verified] at least one upper case letter - intScore += 5; - } - // NUMBERS - if (p.match(/\d/)) { // [verified] at least one number - intScore += 5; - } - if (p.match(/.*\d.*\d.*\d/)) { // [verified] at least three numbers - intScore += 5; - } - - // SPECIAL CHAR - if (p.match(/[!,@,#,$,%,^,&,*,?,_,~]/)) { // [verified] at least one special character - intScore += 5; - } - // [verified] at least two special characters - if (p.match(/.*[!,@,#,$,%,^,&,*,?,_,~].*[!,@,#,$,%,^,&,*,?,_,~]/)) { - intScore += 5; - } - - // COMBOS - if (p.match(/(?=.*[a-z])(?=.*[A-Z])/)) { // [verified] both upper and lower case - intScore += 2; - } - if (p.match(/(?=.*\d)(?=.*[a-z])(?=.*[A-Z])/)) { // [verified] both letters and numbers - intScore += 2; - } - // [verified] letters, numbers, and special characters - if (p.match(/(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[!,@,#,$,%,^,&,*,?,_,~])/)) { - intScore += 2; - } - - return intScore; - - }, - // private - onFocus: function() { - Ext.ux.PasswordMeter.superclass.onFocus.call(this); - - if(!Ext.isOpera) { // don't touch in Opera - this.objMeter.addClass('strengthMeter-focus'); - } - }, - // private - onBlur: function() { - Ext.ux.PasswordMeter.superclass.onBlur.call(this); - - if(!Ext.isOpera) { // don't touch in Opera - this.objMeter.removeClass('strengthMeter-focus'); - } - } -}); \ No newline at end of file diff --git a/manifest.xml b/manifest.xml index 56d418c..c9e6440 100644 --- a/manifest.xml +++ b/manifest.xml @@ -2,7 +2,7 @@ - 1.0 + 1.2 Passwd Password Change Plugin Saket Patel @@ -18,19 +18,6 @@ - @@ -40,13 +27,20 @@ js/passwd.js js/passwd-debug.js + js/PasswdPlugin.js js/settings/SettingsPasswdCategory.js js/settings/SettingsPasswdWidget.js js/settings/PasswdPanel.js js/data/PasswdResponseHandler.js js/ABOUT.js + js/external/PasswordMeter.js + + resources/css/passwd.css + resources/css/passwd.css + resources/css/passwd-main.css + diff --git a/php/class.passwdmodule.php b/php/class.passwdmodule.php index cda26dd..f2f4a02 100644 --- a/php/class.passwdmodule.php +++ b/php/class.passwdmodule.php @@ -97,11 +97,23 @@ class PasswdModule extends Module $uid = $data['username']; } + // check if we should use tls! + if(strrpos(PLUGIN_PASSWD_LDAP_URI, "ldaps://", -strlen(PLUGIN_PASSWD_LDAP_URI)) === FALSE && PLUGIN_PASSWD_LDAP_USE_TLS === true) { + ldap_start_tls($ldapconn); + } + + // set connection parametes + ldap_set_option($ldapconn, LDAP_OPT_PROTOCOL_VERSION, 3); + ldap_set_option($ldapconn, LDAP_OPT_REFERRALS, 0); + + // now bind to the ldap server to search the user dn + ldap_bind($ldapconn, PLUGIN_PASSWD_LDAP_BIND_DN, PLUGIN_PASSWD_LDAP_BIND_PW); + // search for the user dn that will be used to do login into LDAP $userdn = ldap_search ( $ldapconn, // connection-identify PLUGIN_PASSWD_LDAP_BASEDN, // basedn - 'uid=' . $uid, // search filter + 'uid=' . $uid, // search filter array('dn') // needed attributes. we need the dn ); @@ -110,25 +122,34 @@ class PasswdModule extends Module $userdn = $userdn[0]['dn']; // bind to ldap directory - ldap_set_option($ldapconn, LDAP_OPT_PROTOCOL_VERSION, 3); - // login with current password if that fails then current password is wrong - $bind = ldap_bind($ldapconn, $userdn, $data['current_password']); + ldap_bind($ldapconn, $userdn, $data['current_password']); if(ldap_errno($ldapconn) === 0) { - $passwd = $data['new_password']; - $passwdRepeat = $data['new_password_repeat']; - if($this->checkPasswordStrenth($passwd)) { + $passwd = $data['new_password']; + + if ($this->checkPasswordStrenth($passwd)) { $password_hash = $this->sshaEncode($passwd); $entry = array('userPassword' => $password_hash); - $return_mod = ldap_modify($ldapconn, $userdn, $entry); + ldap_modify($ldapconn, $userdn, $entry); if (ldap_errno($ldapconn) === 0) { // password changed successfully // write new password to session because we don't want user to re-authenticate session_start(); - $_SESSION['password'] = $passwd; + // if user has openssl module installed + if(function_exists("openssl_encrypt")) { + // In PHP 5.3.3 the iv parameter was added + if(version_compare(phpversion(), "5.3.3", "<")) { + $_SESSION['password'] = openssl_encrypt($passwd,"des-ede3-cbc",PASSWORD_KEY,0); + } else { + $_SESSION['password'] = openssl_encrypt($passwd,"des-ede3-cbc",PASSWORD_KEY,0,PASSWORD_IV); + } + } + else { + $_SESSION['password'] = $passwd; + } session_write_close(); // send feedback to client @@ -172,32 +193,60 @@ class PasswdModule extends Module { $errorMessage = ''; $passwd = $data['new_password']; - $passwdRepeat = $data['new_password_repeat']; - if($this->checkPasswordStrenth($passwd)) { - // all information correct, change password - $store = $GLOBALS['mapisession']->getDefaultMessageStore(); - $userinfo = mapi_zarafa_getuser_by_name($store, $data['username']); - - if (mapi_zarafa_setuser($store, $userinfo['userid'], $data['username'], $userinfo['fullname'], $userinfo['emailaddress'], $passwd, 0, $userinfo['admin'])) { - // password changed successfully - - // write new password to session because we don't want user to re-authenticate - session_start(); - $_SESSION['password'] = $passwd; - session_write_close(); - - // send feedback to client - $this->sendFeedback(true, array( - 'info' => array( - 'display_message' => dgettext("plugin_passwd", 'Password is changed successfully.') - ) - )); + // get current session password + $sessionPass = $_SESSION['password']; + // if user has openssl module installed + if (function_exists("openssl_decrypt")) { + if (version_compare(phpversion(), "5.3.3", "<")) { + $sessionPass = openssl_decrypt($sessionPass, "des-ede3-cbc", PASSWORD_KEY, 0); } else { - $errorMessage = dgettext("plugin_passwd", 'Password is not changed.'); + $sessionPass = openssl_decrypt($sessionPass, "des-ede3-cbc", PASSWORD_KEY, 0, PASSWORD_IV); + } + + if (!$sessionPass) { + $sessionPass = $_SESSION['password']; + } + } + + if($data['current_password'] === $sessionPass) { + if ($this->checkPasswordStrenth($passwd)) { + // all information correct, change password + $store = $GLOBALS['mapisession']->getDefaultMessageStore(); + $userinfo = mapi_zarafa_getuser_by_name($store, $data['username']); + + if (mapi_zarafa_setuser($store, $userinfo['userid'], $data['username'], $userinfo['fullname'], $userinfo['emailaddress'], $passwd, 0, $userinfo['admin'])) { + // password changed successfully + + // write new password to session because we don't want user to re-authenticate + session_start(); + // if user has openssl module installed + if (function_exists("openssl_encrypt")) { + // In PHP 5.3.3 the iv parameter was added + if (version_compare(phpversion(), "5.3.3", "<")) { + $_SESSION['password'] = openssl_encrypt($passwd, "des-ede3-cbc", PASSWORD_KEY, 0); + } else { + $_SESSION['password'] = openssl_encrypt($passwd, "des-ede3-cbc", PASSWORD_KEY, 0, PASSWORD_IV); + } + } else { + $_SESSION['password'] = $passwd; + } + session_write_close(); + + // send feedback to client + $this->sendFeedback(true, array( + 'info' => array( + 'display_message' => dgettext("plugin_passwd", 'Password is changed successfully.') + ) + )); + } else { + $errorMessage = dgettext("plugin_passwd", 'Password is not changed.'); + } + } else { + $errorMessage = dgettext("plugin_passwd", 'Password is weak. Password should contain capital, non-capital letters and numbers. Password should have 8 to 20 characters.'); } } else { - $errorMessage = dgettext("plugin_passwd", 'Password is weak. Password should contain capital, non-capital letters and numbers. Password should have 8 to 20 characters.'); + $errorMessage = dgettext("plugin_passwd", 'Current password does not match.'); } if(!empty($errorMessage)) { @@ -222,7 +271,6 @@ class PasswdModule extends Module */ public function checkPasswordStrenth($password) { - // @FIXME should be moved to client side if (preg_match("#.*^(?=.{8,20})(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9]).*$#", $password)) { return true; } else { diff --git a/resources/css/passwd-main.css b/resources/css/passwd-main.css new file mode 100644 index 0000000..7091cd7 --- /dev/null +++ b/resources/css/passwd-main.css @@ -0,0 +1,27 @@ +.zarafa-settings-category-passwd { + background-image: url(../images/icon_lock.png) !important; +} + +/* STYLES FOR THE PASSWORD METER */ +.x-form-strengthmeter { + width : 100%; + height: 5px; + /*float: right;*/ + background: #ff4c00; /* Old browsers */ + /* IE9 SVG, needs conditional override of 'filter' to 'none' */ + background: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/Pgo8c3ZnIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgdmlld0JveD0iMCAwIDEgMSIgcHJlc2VydmVBc3BlY3RSYXRpbz0ibm9uZSI+CiAgPGxpbmVhckdyYWRpZW50IGlkPSJncmFkLXVjZ2ctZ2VuZXJhdGVkIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9IjAlIiB5MT0iMCUiIHgyPSIxMDAlIiB5Mj0iMCUiPgogICAgPHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iI2ZmNGMwMCIgc3RvcC1vcGFjaXR5PSIxIi8+CiAgICA8c3RvcCBvZmZzZXQ9IjUwJSIgc3RvcC1jb2xvcj0iI2VkZDcxMiIgc3RvcC1vcGFjaXR5PSIxIi8+CiAgICA8c3RvcCBvZmZzZXQ9IjEwMCUiIHN0b3AtY29sb3I9IiMxYWZmMDAiIHN0b3Atb3BhY2l0eT0iMSIvPgogIDwvbGluZWFyR3JhZGllbnQ+CiAgPHJlY3QgeD0iMCIgeT0iMCIgd2lkdGg9IjEiIGhlaWdodD0iMSIgZmlsbD0idXJsKCNncmFkLXVjZ2ctZ2VuZXJhdGVkKSIgLz4KPC9zdmc+); + background: -moz-linear-gradient(left, #ff4c00 0%, #edd712 50%, #1aff00 100%); /* FF3.6+ */ + background: -webkit-gradient(linear, left top, right top, color-stop(0%,#ff4c00), color-stop(50%,#edd712), color-stop(100%,#1aff00)); /* Chrome,Safari4+ */ + background: -webkit-linear-gradient(left, #ff4c00 0%,#edd712 50%,#1aff00 100%); /* Chrome10+,Safari5.1+ */ + background: -o-linear-gradient(left, #ff4c00 0%,#edd712 50%,#1aff00 100%); /* Opera 11.10+ */ + background: -ms-linear-gradient(left, #ff4c00 0%,#edd712 50%,#1aff00 100%); /* IE10+ */ + background: linear-gradient(left, #ff4c00 0%,#edd712 50%,#1aff00 100%); /* W3C */ + filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#ff4c00', endColorstr='#1aff00',GradientType=1 ); /* IE6-8 */ +} + +.x-form-strengthmeter-scorebar { + background-color: white; + opacity: 0.7; + height: 5px; + width: 100%; +} \ No newline at end of file diff --git a/resources/images/icon_lock.png b/resources/images/icon_lock.png new file mode 100644 index 0000000..be4689e Binary files /dev/null and b/resources/images/icon_lock.png differ diff --git a/resources/images/icon_lock.xcf b/resources/images/icon_lock.xcf new file mode 100644 index 0000000..38f1853 Binary files /dev/null and b/resources/images/icon_lock.xcf differ