1
0
Fork 0
mirror of https://github.com/silentsakky/zarafa-webapp-passwd synced 2026-07-27 10:30:56 +02:00

added most of the basic functionalities

This commit is contained in:
Saket Patel 2013-07-04 03:22:31 +05:30
commit 5064cf5742
7 changed files with 365 additions and 57 deletions

View file

@ -185,6 +185,7 @@
<!-- copy files --> <!-- copy files -->
<copy todir="${target-folder}/${plugin-folder}"> <copy todir="${target-folder}/${plugin-folder}">
<fileset dir="."> <fileset dir=".">
<include name="lib/**/*.*"/>
<include name="resources/**/*.*"/> <include name="resources/**/*.*"/>
<include name="php/**/*.php"/> <include name="php/**/*.php"/>
<include name="config.php"/> <include name="config.php"/>

View file

@ -3,7 +3,7 @@
define('PLUGIN_PASSWD_USER_DEFAULT_ENABLE', true); define('PLUGIN_PASSWD_USER_DEFAULT_ENABLE', true);
/** Define zarafa installtion uses LDAP **/ /** Define zarafa installtion uses LDAP **/
define('PLUGIN_PASSWD_LDAP', true); define('PLUGIN_PASSWD_LDAP', false);
/** Base DN to access LDAP users **/ /** Base DN to access LDAP users **/
define('PLUGIN_PASSWD_LDAP_BASEDN', 'dc=example,dc=com'); define('PLUGIN_PASSWD_LDAP_BASEDN', 'dc=example,dc=com');

View file

@ -10,6 +10,8 @@ Zarafa.plugins.passwd.PasswdPanel = Ext.extend(Ext.form.FormPanel, {
/** /**
* @property * @property
* @type Ext.LoadMask * @type Ext.LoadMask
* Save mask that will be displayed when request has been sent to server for password change
* and waiting for the response.
*/ */
saveMask : undefined, saveMask : undefined,
@ -63,6 +65,10 @@ Zarafa.plugins.passwd.PasswdPanel = Ext.extend(Ext.form.FormPanel, {
this.on('afterrender', this.initialize, this); this.on('afterrender', this.initialize, this);
}, },
/**
* Function will initialize this dialog with some default values and will
* also create object of {@link #saveMask}.
*/
initialize : function() initialize : function()
{ {
this.userNameField.setValue(container.getUser().getUserName()); this.userNameField.setValue(container.getUser().getUserName());
@ -72,24 +78,51 @@ Zarafa.plugins.passwd.PasswdPanel = Ext.extend(Ext.form.FormPanel, {
}) })
}, },
/**
* Handler function that will be called when user presses ok button
* to change password.
*/
onOk : function() onOk : function()
{ {
// send request // show load mask
this.saveMask.show(); this.saveMask.show();
// send request
container.getRequest().singleRequest('passwdmodule', 'save', { container.getRequest().singleRequest('passwdmodule', 'save', {
username : this.userNameField.getValue(), username : this.userNameField.getValue(),
current_password : this.oldPasswdField.getValue(), current_password : this.oldPasswdField.getValue(),
new_password : this.newPasswdField.getValue(), new_password : this.newPasswdField.getValue(),
new_password_repeat : this.newPasswdRepeatField.getValue() new_password_repeat : this.newPasswdRepeatField.getValue()
//}, new Zarafa.plugins.sugarcrm.data.SugarCRMResponseHandler()); }, new Zarafa.plugins.passwd.data.PasswdResponseHandler({
}); callbackFn : this.callbackFn,
scope : this
}));
}, },
/**
* Handler function that will be called when user presses cancel button
* to close the dialog.
*/
onCancel : function() onCancel : function()
{ {
// close the dialog
this.dialog.close(); this.dialog.close();
},
/**
* Callback function that will be executed after response is received from server.
* @param {Boolean} success boolean to indicate response contains success/failure data.
* @param {Object} response response sent by server.
*/
callbackFn : function(success, response)
{
// hide load mask
this.saveMask.hide();
// close the dialog
if(success) {
this.dialog.close();
}
} }
}); });

View file

@ -0,0 +1,54 @@
Ext.namespace('Zarafa.plugins.passwd.data');
/**
* @class Zarafa.plugins.passwd.data.ResponseHandler
* @extends Zarafa.core.data.AbstractResponseHandler
*
* Passwd plugin specific response handler.
*/
Zarafa.plugins.passwd.data.PasswdResponseHandler = Ext.extend(Zarafa.core.data.AbstractResponseHandler, {
/**
* @cfg {Function} callbackFn The function which will be called after success/failure response.
*/
callbackFn : undefined,
/**
* @cfg {Object} scope The function scope that will be used when calling {@link #callbackFn}.
*/
scope : undefined,
/**
* In case exception happened on server, server will return exception response with the display message.
* @param {Object} response Object contained the response data.
*/
doError : function(response)
{
var displayMessage = _('An unknown error occurred while changing password.');
if(response.info) {
displayMessage = response.info.display_message;
}
Ext.MessageBox.alert(_('Error'), displayMessage);
this.callbackFn.apply(this.scope || this, [ false, response ]);
},
/**
* When password change is successfull server will send a success response including display message.
* @param {Object} response Object contained the response data.
*/
doSuccess : function(response)
{
var displayMessage = _('Password is changed successfully.');
if(response.info) {
displayMessage = response.info.display_message;
}
Ext.MessageBox.alert(_('Success'), displayMessage);
this.callbackFn.apply(this.scope || this, [ true, response ]);
}
});

158
lib/PasswordMeter.js Normal file
View file

@ -0,0 +1,158 @@
// 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');
}
}
});

View file

@ -13,6 +13,19 @@
<configfile>config.php</configfile> <configfile>config.php</configfile>
</config> </config>
<components> <components>
<!-- <component>
<info>
<name>passwordmeter</name>
<title>PasswordMeter</title>
<author>Eelco Wiersma</author>
<description>Ext user extension to check password strength</description>
</info>
<files>
<client>
<clientfile>lib/PasswordMeter.js</clientfile>
</client>
</files>
</component> -->
<component> <component>
<files> <files>
<server> <server>
@ -25,6 +38,7 @@
<clientfile load="source">js/PasswdPlugin.js</clientfile> <clientfile load="source">js/PasswdPlugin.js</clientfile>
<clientfile load="source">js/PasswdContentPanel.js</clientfile> <clientfile load="source">js/PasswdContentPanel.js</clientfile>
<clientfile load="source">js/PasswdPanel.js</clientfile> <clientfile load="source">js/PasswdPanel.js</clientfile>
<clientfile load="source">js/data/PasswdResponseHandler.js</clientfile>
<clientfile load="source">js/ABOUT.js</clientfile> <clientfile load="source">js/ABOUT.js</clientfile>
</client> </client>
<!-- <resources> <!-- <resources>

View file

@ -1,7 +1,7 @@
<?php <?php
/** /**
* Passwd module. * Passwd module.
* * Module that will be used to change passwords of the user
*/ */
class PasswdModule extends Module class PasswdModule extends Module
{ {
@ -30,24 +30,56 @@ class PasswdModule extends Module
} }
} }
/**
* Change the password of user. Do some calidation and call proper methods based on
* zarafa setup.
* @param {Array} $data data sent by client.
*/
public function save($data) public function save($data)
{ {
$errorMessage = '';
// some sanity checks // some sanity checks
if(empty($data)) { if(empty($data)) {
$this->sendFeedback(false); $errorMessage = _('No data received.');
} }
if(empty($data['username']) || empty($data['current_password']) || empty($data['new_password']) || empty($data['new_password_repeat'])) { if(empty($data['username']) || empty($data['current_password']) || empty($data['new_password']) || empty($data['new_password_repeat'])) {
$this->sendFeedback(false); $errorMessage = _('User name is empty.');
} }
if(empty($data['current_password'])) {
$errorMessage = _('Current password is empty.');
}
if(empty($data['new_password']) || empty($data['new_password_repeat'])) {
$errorMessage = _('New password is empty.');
}
if($data['new_password'] !== $data['new_password_repeat']) {
$errorMessage = _('New passwords does not match.');
}
if(empty($errorMessage)) {
if(PLUGIN_PASSWD_LDAP) { if(PLUGIN_PASSWD_LDAP) {
$this->saveInLDAP($data); $this->saveInLDAP($data);
} else { } else {
$this->saveInDB($data); $this->saveInDB($data);
} }
} else {
$this->sendFeedback(false, array(
'type' => ERROR_ZARAFA,
'info' => array(
'display_message' => $errorMessage
)
));
}
} }
/**
* Function will connect to LDAP and will try to modify user's password.
* @param {Array} $data data sent by client.
*/
public function saveInLDAP($data) public function saveInLDAP($data)
{ {
$errorMessage = ''; $errorMessage = '';
@ -79,27 +111,23 @@ class PasswdModule extends Module
$passwd = $data['new_password']; $passwd = $data['new_password'];
$passwdRepeat = $data['new_password_repeat']; $passwdRepeat = $data['new_password_repeat'];
if($passwd === $passwdRepeat) { if($this->checkPasswordStrenth($passwd)) {
if(checkPasswordStrenth($passwd)) { $password_hash = $this->sshaEncode($passwd);
$password_hash = sshaEncode($passwd);
$entry = array('userPassword' => $password_hash); $entry = array('userPassword' => $password_hash);
$return_mod = ldap_modify($ldapconn, $userdn, $entry); $return_mod = ldap_modify($ldapconn, $userdn, $entry);
if (ldap_errno($ldapconn) === 0) { if (ldap_errno($ldapconn) === 0) {
// password changed successfully // password changed successfully
$this->sendFeedback(true, { $this->sendFeedback(true, array(
'info' => array( 'info' => array(
'display_message' => _('Password is changed successfully.') 'display_message' => _('Password is changed successfully.')
) )
}); ));
} else { } else {
$errorMessage = _('Password is not changed.'); $errorMessage = _('Password is not changed.');
} }
} else { } else {
$errorMessage = _('Password is weak.'); $errorMessage = _('Password is weak.');
} }
} else {
$errorMessage = _('New passwords does not match.');
}
} else { } else {
$errorMessage = _('Current password does not match.'); $errorMessage = _('Current password does not match.');
} }
@ -110,55 +138,72 @@ class PasswdModule extends Module
} }
if(!empty($errorMessage)) { if(!empty($errorMessage)) {
$this->sendFeedback(false, { $this->sendFeedback(false, array(
'type' => 999, // ERROR_LDAP 'type' => ERROR_ZARAFA,
'info' => array( 'info' => array(
'ldap_error' => ldap_errno(), 'ldap_error' => ldap_errno(),
'ldap_error_name' => ldap_error(), 'ldap_error_name' => ldap_error(),
'display_message' => $errorMessage 'display_message' => $errorMessage
) )
}); ));
} }
} }
/**
* Function will execute zarafa-passwd command and will try to change user's password,
* this method is unsecure and unreliable.
* @param {Array} $data data sent by client.
*/
public function saveInDB($data) public function saveInDB($data)
{ {
$errorMessage = '';
$passwd = $data['new_password']; $passwd = $data['new_password'];
$passwdRepeat = $data['new_password_repeat']; $passwdRepeat = $data['new_password_repeat'];
$passwd_cmd = "/usr/bin/zarafa-passwd -u %s -o %s -p %s"; $passwd_cmd = "/usr/bin/zarafa-passwd -u %s -o %s -p %s";
if($passwd === $passwdRepeat) { if($this->checkPasswordStrenth($passwd)) {
if(checkPasswordStrenth($passwd)) {
// all information correct, change password // all information correct, change password
$cmd = sprintf($passwd_cmd, $data['username'], $data['current_passwd'], $passwd); $cmd = sprintf($passwd_cmd, $data['username'], $data['current_passwd'], $passwd);
exec($cmd, $arrayout, $retval); exec($cmd, $arrayout, $retval);
if ($retval === 0) { if ($retval === 0) {
// password changed successfully // password changed successfully
$this->sendFeedback(true, { $this->sendFeedback(true, array(
'info' => array( 'info' => array(
'display_message' => _('Password is changed successfully.') 'display_message' => _('Password is changed successfully.')
) )
}); ));
} else { } else {
$errorMessage = _('Password is not changed.'); $errorMessage = _('Password is not changed.');
} }
} else { } else {
$errorMessage = _('Password is weak.'); $errorMessage = _('Password is weak.');
} }
} else {
$errorMessage = _('New passwords does not match.'); if(!empty($errorMessage)) {
$this->sendFeedback(false, array(
'type' => ERROR_ZARAFA,
'info' => array(
'display_message' => $errorMessage
)
));
} }
} }
// check passwords. They should meet the following criteria: /**
// - min. 8 chars, max. 20 * Function will check strength of the password and if it does not meet minimum requirements then
// - contain caps und noncaps characters * will return false.
// - contain numbers * Password should meet the following criteria:
// return FALSE if not all criteria are met * - min. 8 chars, max. 20
* - contain caps und noncaps characters
* - contain numbers
* @param {String} $password password which should be checked.
* @return {Boolean} true if password passes the minimum requirement else false.
*/
public function checkPasswordStrenth($password) public function checkPasswordStrenth($password)
{ {
return true;
// @FIXME should be moved to client side // @FIXME should be moved to client side
if (preg_match("#.*^(?=.{8,20})(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9]).*$#", $password)) { if (preg_match("#.*^(?=.{8,20})(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9]).*$#", $password)) {
return true; return true;
@ -167,7 +212,10 @@ class PasswdModule extends Module
} }
} }
// create a ldap-password-hash from $text /**
* Function will generate SSHA hash to use to store user's password in LDAP.
* @param {String} $text text based on which hash will be generated.
*/
function sshaEncode($text) function sshaEncode($text)
{ {
$salt = ''; $salt = '';