Skip to content
This repository has been archived by the owner on Nov 25, 2020. It is now read-only.

Commit

Permalink
Wire password strength checker on user creation form and share passwo…
Browse files Browse the repository at this point in the history
…rd form
  • Loading branch information
cdujeu committed Feb 27, 2016
1 parent 7db5c77 commit 723cc5f
Show file tree
Hide file tree
Showing 9 changed files with 250 additions and 18 deletions.
2 changes: 1 addition & 1 deletion core/src/plugins/action.share/res/react-share-form.css
Expand Up @@ -123,7 +123,7 @@ div#react_share_form .public-link-container .copy-link-button {
background-color: #ddd;
color: #F1F1F1;
padding: 6px;
border-radius: 0;
border-radius: 4px 4px 4px 0;
}
div#react_share_form .public-link-container .copy-link-button:hover {
background-color: #47b092;
Expand Down
2 changes: 1 addition & 1 deletion core/src/plugins/action.share/res/react-share-form.less
Expand Up @@ -142,7 +142,7 @@ div#react_share_form {
background-color: #ddd;
color: #F1F1F1;
padding: 6px;
border-radius: 0;
border-radius: 4px 4px 4px 0;
}
.copy-link-button:hover{
background-color: @color1;
Expand Down
34 changes: 23 additions & 11 deletions core/src/plugins/action.share/res/react/ShareDialog.js
Expand Up @@ -784,30 +784,42 @@
this.props.shareModel.resetPassword(this.props.linkData.hash);
},

updatePassword: function(event){
var newValue = event.currentTarget.getValue();
updatePassword: function(newValue, oldValue){
//var newValue = event.currentTarget.getValue();
this.props.shareModel.updatePassword(this.props.linkData.hash, newValue);
},

render: function(){
var linkId = this.props.linkData.hash;
var passPlaceHolder, resetPassword;
var passwordField;
if(this.props.shareModel.hasHiddenPassword(linkId)){
passPlaceHolder = '********';
resetPassword = <ReactMUI.FlatButton secondary={true} onClick={this.resetPassword} label="Reset Password"/>
var resetPassword = (
<ReactMUI.FlatButton secondary={true} onClick={this.resetPassword} label="Reset Password"/>
);
passwordField = (
<ReactMUI.TextField
floatingLabelText="Password Protection"
disabled={true}
value={'********'}
onChange={this.updatePassword}
/>
);
}else{
passwordField = (
<PydioForm.ValidPassword
attributes={{label:"Password Protection"}}
value={this.props.shareModel.getPassword(linkId)}
onChange={this.updatePassword}
/>
);
}
return (
<div>
<h3>Secure Access</h3>
<div className="section-legend">Protect the link with a password, or make it expire automatically.</div>
<div className="password-container">
<div style={{width:'50%', display:'inline-block'}}>
<ReactMUI.TextField
floatingLabelText="Password Protection"
disabled={passPlaceHolder ? true : false}
value={passPlaceHolder? passPlaceHolder : this.props.shareModel.getPassword(linkId)}
onChange={this.updatePassword}
/>
{passwordField}
</div>
<div style={{width:'50%', display:'inline-block'}}>
{resetPassword}
Expand Down
1 change: 1 addition & 0 deletions core/src/plugins/gui.ajax/Gruntfile.js
Expand Up @@ -6,6 +6,7 @@ var gui_ajax_core = [
'res/js/core/util/XMLUtils.js',
'res/js/core/util/PathUtils.js',
'res/js/core/util/HasherUtils.js',
'res/js/core/util/PassUtils.js',
'res/js/core/util/CookiesManager.js',
'res/js/core/model/Router.js',
'res/js/core/model/AjxpNode.js',
Expand Down
Expand Up @@ -141,7 +141,7 @@
label: MessageHash['523'],
name: "new_password",
scope: "user",
type: "password",
type: "valid-password",
mandatory: "true"
},{
description: MessageHash['536'],
Expand Down
130 changes: 130 additions & 0 deletions core/src/plugins/gui.ajax/res/js/es6/util/PassUtils.es6
@@ -0,0 +1,130 @@
/*
* Copyright 2007-2016 Charles du Jeu - Abstrium SAS <team (at) pyd.io>
* This file is part of Pydio.
*
* Pydio is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Pydio is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Pydio. If not, see <http://www.gnu.org/licenses/>.
*
* The latest code can be found at <https://pydio.com/>.
*
*/
/**
*
* Utils to compute password strength
*
*/
class PassUtils{

static getOptions(){
if(PassUtils.Options){
return PassUtils.Options;
}
PassUtils.Options = {
pydioMessages:[379,380,381,382,383,384,385],
messages: ["Unsafe password word!", "Too short", "Very weak", "Weak", "Medium", "Strong", "Very strong"],
colors: ["#f00", "#999", "#C70F0F", "#C70F0F", "#FF8432", "#279D00", "#279D00"],
scores: [10, 15, 30, 40],
common: ["password", "123456", "123", "1234", "mypass", "pass", "letmein", "qwerty", "monkey", "asdfgh", "zxcvbn", "pass"],
minchar: 8
};
return PassUtils.Options;
}

static checkPasswordStrength(value, callback) {
// Update with Pydio options
PassUtils.getOptions();
if(!PassUtils.Options.pydioMinChar && window.pydio){
var pydioMin = parseInt(window.pydio.getPluginConfigs("core.auth").get("PASSWORD_MINLENGTH"));
PassUtils.Options.pydioMinChar = true;
if(pydioMin){
PassUtils.Options.minchar = pydioMin;
}
}
var options = PassUtils.Options;
var strength = PassUtils.getPasswordScore(value, options.minchar);
if (strength == -200) {
callback(0, 0);
} else {
if (strength < 0 && strength > -199) {
callback(1, 10);
} else {
if (strength <= options.scores[0]) {
callback(2, 10);
} else {
if (strength > options.scores[0] && strength <= options.scores[1]) {
callback(3, 25);
} else if (strength > options.scores[1] && strength <= options.scores[2]) {
callback(4, 55);
} else if (strength > options.scores[2] && strength <= options.scores[3]) {
callback(5, 80);
} else {
callback(6, 98);
}
}
}
}
}

static getPasswordScore(value, minchar) {

var strength = 0;
if (value.length < minchar) {
strength = (strength - 100);
} else {
if (value.length >= minchar && value.length <= (minchar + 2)) {
strength = (strength + 6);
} else {
if (value.length >= (minchar + 3) && value.length <= (minchar + 4)) {
strength = (strength + 12);
} else {
if (value.length >= (minchar + 5)) {
strength = (strength + 18);
}
}
}
}
if (value.match(/[a-z]/)) {
strength = (strength + 1);
}
if (value.match(/[A-Z]/)) {
strength = (strength + 5);
}
if (value.match(/\d+/)) {
strength = (strength + 5);
}
if (value.match(/(.*[0-9].*[0-9].*[0-9])/)) {
strength = (strength + 7);
}
if (value.match(/.[!,@,#,$,%,^,&,*,?,_,~]/)) {
strength = (strength + 5);
}
if (value.match(/(.*[!,@,#,$,%,^,&,*,?,_,~].*[!,@,#,$,%,^,&,*,?,_,~])/)) {
strength = (strength + 7);
}
if (value.match(/([a-z].*[A-Z])|([A-Z].*[a-z])/)) {
strength = (strength + 2);
}
if (value.match(/([a-zA-Z])/) && value.match(/([0-9])/)) {
strength = (strength + 3);
}
if (value.match(/([a-zA-Z0-9].*[!,@,#,$,%,^,&,*,?,_,~])|([!,@,#,$,%,^,&,*,?,_,~].*[a-zA-Z0-9])/)) {
strength = (strength + 3);
}
var common = ["password", "123456", "123", "1234", "mypass", "pass", "letmein", "qwerty", "monkey", "asdfgh", "zxcvbn", "pass"];
if(common.indexOf(value.toLowerCase()) !== -1){
strength = -200;
}
return strength;
}
}

Expand Up @@ -219,6 +219,78 @@

});

var ValidPassword = React.createClass({

mixins:[FormMixin],

isValid:function(){
return (this.state.value && this.checkMinLength(this.state.value));
},

checkMinLength:function(value){
var minLength = parseInt(global.pydio.getPluginConfigs("core.auth").get("PASSWORD_MINLENGTH"));
return !(value && value.length < minLength);
},

getMessage:function(messageId){
if(this.context && this.context.getMessage){
return this.context.getMessage(messageId, '');
}else if(global.pydio && global.pydio.MessageHash){
return global.pydio.MessageHash[messageId];
}
},

getComplexityString:function(value){
var response;
PassUtils.checkPasswordStrength(value, function(segment, percent){
var responseString;
if(global.pydio && global.pydio.MessageHash){
responseString = this.getMessage(PassUtils.Options.pydioMessages[segment]);
}else{
responseString = PassUtils.Options.messages[segment];
}
response = {
segment:segment,
color:(segment>1) ? PassUtils.Options.colors[segment] : null,
responseString:responseString
};
}.bind(this));
return response;
},

render:function(){
if(this.isDisplayGrid() && !this.state.editMode){
var value = this.state.value;
return <div onClick={this.props.disabled?function(){}:this.toggleEditMode} className={value?'':'paramValue-empty'}>{!value?'Empty':value}</div>;
}else{
var errorText = this.state.errorText;
if(this.state.value){
var response = this.getComplexityString(this.state.value);
errorText = <span style={{color: response.color}}>{response.responseString}</span>;
if(response.segment > 1){
var className = "mui-error-as-hint";
}
}
return(
<span>
<ReactMUI.TextField
floatingLabelText={this.isDisplayForm()?this.props.attributes.label:null}
className={className}
value={this.state.value}
onChange={this.onChange}
onKeyDown={this.enterToToggle}
type='password'
multiLine={false}
disabled={this.props.disabled}
errorText={errorText}
/>
</span>
);
}
}

});

/**
* Checkboxk input
*/
Expand Down Expand Up @@ -1583,17 +1655,20 @@
case 'password':
value = <InputText {...props}/>;
break;
case 'valid-password':
value = <ValidPassword {...props}/>;
break;
case 'integer':
value = <PydioForm.InputInteger {...props}/>;
value = <InputInteger {...props}/>;
break;
case 'button':
value = <PydioForm.InputButton {...props}/>;
value = <InputButton {...props}/>;
break;
case 'image':
value = <PydioForm.InputImage {...props}/>;
value = <InputImage {...props}/>;
break;
case 'select':
value = <PydioForm.InputSelectBox {...props}/>;
value = <InputSelectBox {...props}/>;
break;
case 'legend':
value = null;
Expand Down Expand Up @@ -1752,6 +1827,7 @@
PydioForm.createFormElement = PydioFormManager.createFormelement;
PydioForm.Manager = PydioFormManager;
PydioForm.InputText = InputText;
PydioForm.ValidPassword = ValidPassword;
PydioForm.InputBoolean = InputBoolean;
PydioForm.InputInteger = InputInteger;
PydioForm.InputButton = InputButton;
Expand Down
7 changes: 7 additions & 0 deletions core/src/plugins/gui.ajax/res/mui/custom.less
Expand Up @@ -13,6 +13,13 @@

@primary-color: #009688;

.mui-error-as-hint hr.mui-text-field-focus-underline {
border-color: @primary-color !important;
}
.mui-text-field .mui-text-field-error{
right: 0;
}

/*************/
/* LAYOUTS */
/*************/
Expand Down
6 changes: 6 additions & 0 deletions core/src/plugins/gui.ajax/res/mui/pydio-mui.css
Expand Up @@ -2823,6 +2823,12 @@
.react-mui-context label {
margin: 0;
}
.react-mui-context .mui-error-as-hint hr.mui-text-field-focus-underline {
border-color: #009688 !important;
}
.react-mui-context .mui-text-field .mui-text-field-error {
right: 0;
}
.react-mui-context .main-layout-nav-to-stack,
.react-mui-context .horizontal-layout,
.react-mui-context .vertical-layout,
Expand Down

0 comments on commit 723cc5f

Please sign in to comment.