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:
parent
b434706684
commit
5064cf5742
7 changed files with 365 additions and 57 deletions
|
|
@ -185,6 +185,7 @@
|
|||
<!-- copy files -->
|
||||
<copy todir="${target-folder}/${plugin-folder}">
|
||||
<fileset dir=".">
|
||||
<include name="lib/**/*.*"/>
|
||||
<include name="resources/**/*.*"/>
|
||||
<include name="php/**/*.php"/>
|
||||
<include name="config.php"/>
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
define('PLUGIN_PASSWD_USER_DEFAULT_ENABLE', true);
|
||||
|
||||
/** Define zarafa installtion uses LDAP **/
|
||||
define('PLUGIN_PASSWD_LDAP', true);
|
||||
define('PLUGIN_PASSWD_LDAP', false);
|
||||
|
||||
/** Base DN to access LDAP users **/
|
||||
define('PLUGIN_PASSWD_LDAP_BASEDN', 'dc=example,dc=com');
|
||||
|
|
|
|||
|
|
@ -10,6 +10,8 @@ Zarafa.plugins.passwd.PasswdPanel = Ext.extend(Ext.form.FormPanel, {
|
|||
/**
|
||||
* @property
|
||||
* @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,
|
||||
|
||||
|
|
@ -63,6 +65,10 @@ Zarafa.plugins.passwd.PasswdPanel = Ext.extend(Ext.form.FormPanel, {
|
|||
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()
|
||||
{
|
||||
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()
|
||||
{
|
||||
// send request
|
||||
|
||||
// show load mask
|
||||
this.saveMask.show();
|
||||
|
||||
// send request
|
||||
container.getRequest().singleRequest('passwdmodule', 'save', {
|
||||
username : this.userNameField.getValue(),
|
||||
current_password : this.oldPasswdField.getValue(),
|
||||
new_password : this.newPasswdField.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()
|
||||
{
|
||||
// close the dialog
|
||||
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();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
|||
54
js/data/PasswdResponseHandler.js
Normal file
54
js/data/PasswdResponseHandler.js
Normal 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
158
lib/PasswordMeter.js
Normal 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');
|
||||
}
|
||||
}
|
||||
});
|
||||
14
manifest.xml
14
manifest.xml
|
|
@ -13,6 +13,19 @@
|
|||
<configfile>config.php</configfile>
|
||||
</config>
|
||||
<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>
|
||||
<files>
|
||||
<server>
|
||||
|
|
@ -25,6 +38,7 @@
|
|||
<clientfile load="source">js/PasswdPlugin.js</clientfile>
|
||||
<clientfile load="source">js/PasswdContentPanel.js</clientfile>
|
||||
<clientfile load="source">js/PasswdPanel.js</clientfile>
|
||||
<clientfile load="source">js/data/PasswdResponseHandler.js</clientfile>
|
||||
<clientfile load="source">js/ABOUT.js</clientfile>
|
||||
</client>
|
||||
<!-- <resources>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<?php
|
||||
/**
|
||||
* Passwd module.
|
||||
*
|
||||
* Module that will be used to change passwords of the user
|
||||
*/
|
||||
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)
|
||||
{
|
||||
$errorMessage = '';
|
||||
|
||||
// some sanity checks
|
||||
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'])) {
|
||||
$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) {
|
||||
$this->saveInLDAP($data);
|
||||
} else {
|
||||
$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)
|
||||
{
|
||||
$errorMessage = '';
|
||||
|
|
@ -79,27 +111,23 @@ class PasswdModule extends Module
|
|||
$passwd = $data['new_password'];
|
||||
$passwdRepeat = $data['new_password_repeat'];
|
||||
|
||||
if($passwd === $passwdRepeat) {
|
||||
if(checkPasswordStrenth($passwd)) {
|
||||
$password_hash = sshaEncode($passwd);
|
||||
if($this->checkPasswordStrenth($passwd)) {
|
||||
$password_hash = $this->sshaEncode($passwd);
|
||||
$entry = array('userPassword' => $password_hash);
|
||||
$return_mod = ldap_modify($ldapconn, $userdn, $entry);
|
||||
if (ldap_errno($ldapconn) === 0) {
|
||||
// password changed successfully
|
||||
$this->sendFeedback(true, {
|
||||
$this->sendFeedback(true, array(
|
||||
'info' => array(
|
||||
'display_message' => _('Password is changed successfully.')
|
||||
)
|
||||
});
|
||||
));
|
||||
} else {
|
||||
$errorMessage = _('Password is not changed.');
|
||||
}
|
||||
} else {
|
||||
$errorMessage = _('Password is weak.');
|
||||
}
|
||||
} else {
|
||||
$errorMessage = _('New passwords does not match.');
|
||||
}
|
||||
} else {
|
||||
$errorMessage = _('Current password does not match.');
|
||||
}
|
||||
|
|
@ -110,55 +138,72 @@ class PasswdModule extends Module
|
|||
}
|
||||
|
||||
if(!empty($errorMessage)) {
|
||||
$this->sendFeedback(false, {
|
||||
'type' => 999, // ERROR_LDAP
|
||||
$this->sendFeedback(false, array(
|
||||
'type' => ERROR_ZARAFA,
|
||||
'info' => array(
|
||||
'ldap_error' => ldap_errno(),
|
||||
'ldap_error_name' => ldap_error(),
|
||||
'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)
|
||||
{
|
||||
$errorMessage = '';
|
||||
$passwd = $data['new_password'];
|
||||
$passwdRepeat = $data['new_password_repeat'];
|
||||
|
||||
$passwd_cmd = "/usr/bin/zarafa-passwd -u %s -o %s -p %s";
|
||||
|
||||
if($passwd === $passwdRepeat) {
|
||||
if(checkPasswordStrenth($passwd)) {
|
||||
if($this->checkPasswordStrenth($passwd)) {
|
||||
// all information correct, change password
|
||||
$cmd = sprintf($passwd_cmd, $data['username'], $data['current_passwd'], $passwd);
|
||||
exec($cmd, $arrayout, $retval);
|
||||
|
||||
if ($retval === 0) {
|
||||
// password changed successfully
|
||||
$this->sendFeedback(true, {
|
||||
$this->sendFeedback(true, array(
|
||||
'info' => array(
|
||||
'display_message' => _('Password is changed successfully.')
|
||||
)
|
||||
});
|
||||
));
|
||||
} else {
|
||||
$errorMessage = _('Password is not changed.');
|
||||
}
|
||||
} else {
|
||||
$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
|
||||
// - contain caps und noncaps characters
|
||||
// - contain numbers
|
||||
// return FALSE if not all criteria are met
|
||||
/**
|
||||
* Function will check strength of the password and if it does not meet minimum requirements then
|
||||
* will return false.
|
||||
* Password should meet the following criteria:
|
||||
* - 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)
|
||||
{
|
||||
return true;
|
||||
// @FIXME should be moved to client side
|
||||
if (preg_match("#.*^(?=.{8,20})(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9]).*$#", $password)) {
|
||||
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)
|
||||
{
|
||||
$salt = '';
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue