mirror of
http://git.whoc.org.uk/git/password-manager.git
synced 2025-10-29 18:37:35 +01:00
Added password generator for password fields
This commit is contained in:
@@ -320,7 +320,6 @@ MochiKit.Base.update(Clipperz.PM.Crypto, {
|
||||
deferredResult.addCallback(MochiKit.Async.wait, 0.1);
|
||||
deferredResult.addCallback(Clipperz.Base.evalJSON);
|
||||
deferredResult.addErrback(function(anError) {
|
||||
console.log("PIPPO_1", anError)
|
||||
Clipperz.logError("Error while decrypting data [4]");
|
||||
throw Clipperz.Crypto.Base.exception.CorruptedMessage;
|
||||
})
|
||||
@@ -403,7 +402,6 @@ console.log("PIPPO_1", anError)
|
||||
try {
|
||||
result = Clipperz.Base.evalJSON(value);
|
||||
} catch (exception) {
|
||||
console.log("PIPPO_2", anError)
|
||||
Clipperz.logError("Error while decrypting data [4]");
|
||||
throw Clipperz.Crypto.Base.exception.CorruptedMessage;
|
||||
}
|
||||
|
||||
@@ -37,6 +37,7 @@ Clipperz.PM.UI.Components.Cards.EditClass = React.createClass({
|
||||
getInitialState: function() {
|
||||
return {
|
||||
'draggedFieldReference': null,
|
||||
'passwordGeneratorFieldReference': null,
|
||||
'fromFieldPosition': -1,
|
||||
'toFieldPosition': -1,
|
||||
'dropPosition': -1,
|
||||
@@ -216,6 +217,19 @@ console.log("DROP"); //, anEvent);
|
||||
|
||||
//============================================================================
|
||||
|
||||
setValueFromPasswordGenerator: function (aField, aTextAreaRef) {
|
||||
var reference = this.props['_reference'];
|
||||
var self = this;
|
||||
|
||||
return function (aValue) {
|
||||
aField.setValue(aValue);
|
||||
React.findDOMNode(self.refs[aTextAreaRef]).value = aValue;
|
||||
|
||||
self.setState({'passwordGeneratorFieldReference':null});
|
||||
MochiKit.Signal.signal(Clipperz.Signal.NotificationCenter, 'refreshCardEditDetail', reference);
|
||||
};
|
||||
},
|
||||
|
||||
handleChange: function (anObject , aMethodName) {
|
||||
var reference = this.props['_reference'];
|
||||
var method = MochiKit.Base.method(anObject, aMethodName);
|
||||
@@ -270,6 +284,23 @@ console.log("DROP"); //, anEvent);
|
||||
MochiKit.Signal.signal(Clipperz.Signal.NotificationCenter, 'refreshCardEditDetail', reference);
|
||||
},
|
||||
|
||||
showPasswordGenerator: function (aField) {
|
||||
var result;
|
||||
|
||||
if (aField['actionType'] == 'PASSWORD') {
|
||||
var reference = this.props['_reference'];
|
||||
var self = this;
|
||||
|
||||
result = function (anEvent) {
|
||||
self.setState({'passwordGeneratorFieldReference':aField['_reference']});
|
||||
};
|
||||
} else {
|
||||
result = null;
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
|
||||
toggleLock: function (aField) {
|
||||
var reference = this.props['_reference'];
|
||||
|
||||
@@ -290,6 +321,10 @@ console.log("DROP"); //, anEvent);
|
||||
};
|
||||
},
|
||||
|
||||
closePasswordGenerator: function () {
|
||||
this.setState({'passwordGeneratorFieldReference': null});
|
||||
},
|
||||
|
||||
//============================================================================
|
||||
|
||||
renderLabel: function (aLabel) {
|
||||
@@ -320,6 +355,7 @@ console.log("DROP"); //, anEvent);
|
||||
var cardFieldClasses = {};
|
||||
var cardFieldValueClasses = {};
|
||||
var field = aField['_field'];
|
||||
var fieldValueRef = ref + '_textarea';
|
||||
|
||||
//console.log("RENDER FIELD", aField);
|
||||
cardFieldClasses['cardField'] = true;
|
||||
@@ -355,12 +391,12 @@ console.log("DROP"); //, anEvent);
|
||||
React.DOM.input({'_className_':'_fieldLabel_', 'onChange':this.handleChange(field, 'setLabel'), 'defaultValue':aField['label'], 'placeholder': "label"}),
|
||||
]),
|
||||
React.DOM.div({'className':'fieldValue'}, [
|
||||
// React.DOM.textarea({'className':Clipperz.PM.UI.Components.classNames(cardFieldValueClasses), 'onChange':this.handleChange(field, 'setValue'), 'defaultValue':aField['value'], 'placeholder': "value"}),
|
||||
Clipperz.PM.UI.Components.Cards.TextArea({'className':Clipperz.PM.UI.Components.classNames(cardFieldValueClasses), 'onChange':this.handleChange(field, 'setValue'), 'onKeyDown':this.handleKeyDown(field), 'defaultValue':aField['value'], 'placeholder': "value"}),
|
||||
(ref == this.state['passwordGeneratorFieldReference']) ? Clipperz.PM.UI.Components.Cards.PasswordGenerator({'field':aField, 'setValueCallback':this.setValueFromPasswordGenerator(field, fieldValueRef), 'closeClallback':this.closePasswordGenerator}) : null,
|
||||
Clipperz.PM.UI.Components.Cards.TextArea({'className':Clipperz.PM.UI.Components.classNames(cardFieldValueClasses), 'onChange':this.handleChange(field, 'setValue'), 'onKeyDown':this.handleKeyDown(field), 'defaultValue':aField['value'], 'placeholder': "value", 'ref':fieldValueRef}),
|
||||
])
|
||||
]),
|
||||
React.DOM.div({'className':'fieldAction'}, [
|
||||
React.DOM.span({'className':'action'}, aField['actionType'].toLowerCase() == 'password' ? 'generate password' : aField['actionType'].toLowerCase()),
|
||||
React.DOM.span({'className':'action ' + aField['actionType'], 'onClick':this.showPasswordGenerator(aField)}, aField['actionType'].toLowerCase() == 'password' ? 'password generator' : aField['actionType'].toLowerCase()),
|
||||
React.DOM.span({'className':'toggleLock', 'onClick':this.toggleLock(field)}, aField['isHidden'] ? "locked" : "unlocked")
|
||||
])
|
||||
]);
|
||||
|
||||
@@ -0,0 +1,220 @@
|
||||
/*
|
||||
|
||||
Copyright 2008-2015 Clipperz Srl
|
||||
|
||||
This file is part of Clipperz, the online password manager.
|
||||
For further information about its features and functionalities please
|
||||
refer to http://www.clipperz.com.
|
||||
|
||||
* Clipperz 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.
|
||||
|
||||
* Clipperz 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 Clipperz. If not, see http://www.gnu.org/licenses/.
|
||||
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
Clipperz.Base.module('Clipperz.PM.UI.Components.Cards');
|
||||
|
||||
Clipperz.PM.UI.Components.Cards.PasswordGeneratorClass = React.createClass({
|
||||
|
||||
charsetBlocks: {
|
||||
'chars_AZ': 'ABCDEFGHIJKLMNOPQRSTUVWXYZ',
|
||||
'chars_az': 'abcdefghijklmnopqrstuvwxyz',
|
||||
'chars_09': '0123456789',
|
||||
'chars_space': ' ',
|
||||
'chars_other': '~`!@#$%^&*()-_=+,.<>/?[]{}\\|:;\'"'
|
||||
},
|
||||
|
||||
gradientColors: ['#ff3236', '#e74030', '#cf4e2a', '#b75c24', '#9f6a1e', '#877818', '#6f8612', '#57940c', '#3fa206', '#25ad00', '#25ad00'],
|
||||
|
||||
getInitialState: function () {
|
||||
return {
|
||||
'length': 32,
|
||||
'options': 'closed',
|
||||
'chars_AZ': true,
|
||||
'chars_az': true,
|
||||
'chars_09': true,
|
||||
'chars_space': false,
|
||||
'chars_other': true,
|
||||
'charset': '',
|
||||
'password': '',
|
||||
'entropy': 0,
|
||||
};
|
||||
},
|
||||
|
||||
setPasswordValue: function () {
|
||||
this.props['setValueCallback'](this.state['password']);
|
||||
},
|
||||
|
||||
toggleOptions: function () {
|
||||
var options;
|
||||
|
||||
options = (this.state['options'] == 'closed') ? 'open' : 'closed';
|
||||
this.setState({'options':options});
|
||||
},
|
||||
|
||||
updateCharset: function () {
|
||||
var charsetKeys = ['chars_AZ', 'chars_az', 'chars_09', 'chars_space', 'chars_other'];
|
||||
var self = this;
|
||||
var charset;
|
||||
|
||||
charset = MochiKit.Iter.reduce(function (acc, key) { return acc + self.charsetBlocks[key]}, MochiKit.Base.filter(function (key) { return self.state[key] == true;}, charsetKeys), '');
|
||||
|
||||
this.setState({'charset': charset});
|
||||
MochiKit.Async.callLater(0.1, this.refreshPasswordValue);
|
||||
},
|
||||
|
||||
refreshPasswordValue: function () {
|
||||
var charsetBitSize;
|
||||
var passwordString;
|
||||
var charset;
|
||||
var passwordLength;
|
||||
var randomBytes;
|
||||
var blockIndex;
|
||||
|
||||
charset = this.state['charset'];
|
||||
passwordLength = this.state['length'];
|
||||
|
||||
charsetBitSize = 0;
|
||||
randomBytes = Clipperz.Crypto.PRNG.defaultRandomGenerator().getRandomBytes(50);
|
||||
blockIndex = 0;
|
||||
passwordString = "";
|
||||
|
||||
while (Math.pow(2, charsetBitSize) < charset.length) {
|
||||
charsetBitSize ++;
|
||||
}
|
||||
|
||||
if (charsetBitSize > 0) {
|
||||
// while (Clipperz.PM.Crypto.passwordEntropy(passwordString) < 128) {
|
||||
while (passwordString.length < passwordLength) {
|
||||
var randomValue;
|
||||
|
||||
if (((blockIndex + 1)*charsetBitSize) > (randomBytes.length() * 8)) {
|
||||
randomBytes = Clipperz.Crypto.PRNG.defaultRandomGenerator().getRandomBytes(50);
|
||||
blockIndex = 0;
|
||||
}
|
||||
randomValue = randomBytes.bitBlockAtIndexWithSize(blockIndex*charsetBitSize, charsetBitSize);
|
||||
if (randomValue < charset.length) {
|
||||
passwordString += charset.charAt(randomValue);
|
||||
}
|
||||
|
||||
blockIndex ++;
|
||||
}
|
||||
} else {
|
||||
passwordString = "";
|
||||
}
|
||||
|
||||
this.setState({
|
||||
'password': passwordString,
|
||||
'entropy': Clipperz.PM.Crypto.passwordEntropy(passwordString)
|
||||
});
|
||||
},
|
||||
|
||||
refreshEntropyValue: function () {
|
||||
this.setState({
|
||||
'entropy': Clipperz.PM.Crypto.passwordEntropy(this.state['password'])
|
||||
});
|
||||
},
|
||||
|
||||
changeStateWithTargetValue: function (aKey, shouldUpdatePasswordValue) {
|
||||
var self = this;
|
||||
return function (anEvent) {
|
||||
var newState = {};
|
||||
|
||||
newState[aKey] = anEvent.target.value;
|
||||
self.setState(newState);
|
||||
|
||||
if (shouldUpdatePasswordValue) {
|
||||
MochiKit.Async.callLater(0.1, self.refreshPasswordValue);
|
||||
} else {
|
||||
MochiKit.Async.callLater(0.1, self.refreshEntropyValue);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
changeStateWithCheckbox: function (aKey) {
|
||||
var self = this;
|
||||
return function (anEvent) {
|
||||
var newState = {};
|
||||
|
||||
newState[aKey] = anEvent.target.checked;
|
||||
self.setState(newState);
|
||||
|
||||
MochiKit.Async.callLater(0.1, self.updateCharset);
|
||||
}
|
||||
},
|
||||
|
||||
// isPasswordField: function () {
|
||||
// return this.props['field']['actionType'] == 'PASSWORD';
|
||||
// },
|
||||
|
||||
componentDidMount: function () {
|
||||
this.updateCharset();
|
||||
this.refreshPasswordValue();
|
||||
},
|
||||
|
||||
render: function () {
|
||||
var goodEntropy = 128;
|
||||
var entropyPercentage;
|
||||
var entropyWidth;
|
||||
var entropyColor;
|
||||
var result;
|
||||
|
||||
entropyPercentage = Math.min(this.state['entropy'] / goodEntropy * 100, 100);
|
||||
entropyWidth = (100 - entropyPercentage)+ '%';
|
||||
entropyColor = this.gradientColors[Math.floor(entropyPercentage / 10)];
|
||||
|
||||
result = React.DOM.div({'className':'passwordGenerator'}, [
|
||||
React.DOM.div({'className':'passwordGeneratorMask', 'onClick':this.props['closeClallback']}),
|
||||
React.DOM.div({'className':'passwordGeneratorBaloon'}, [
|
||||
React.DOM.form({}, [
|
||||
React.DOM.div({'className':'optionsWrapper'}, [
|
||||
React.DOM.header({}, [
|
||||
React.DOM.div({'className':'button', 'onClick':this.toggleOptions}, "options")
|
||||
]),
|
||||
React.DOM.div({'className':'options ' + this.state['options']}, [
|
||||
React.DOM.div({'className':'length'}, [
|
||||
React.DOM.span({}, "length"),
|
||||
React.DOM.input({'type':'number', 'placehoder':"", 'value':this.state['length'], 'min':"1", 'max':"99", 'onChange':this.changeStateWithTargetValue('length', true), 'ref':'length'}),
|
||||
]),
|
||||
React.DOM.div({'className':'charList'}, [
|
||||
React.DOM.span({}, "characters"),
|
||||
React.DOM.div({'className':'charsetSets'}, [
|
||||
React.DOM.label({}, [ React.DOM.input({'type':'checkbox', 'checked':this.state['chars_AZ'], 'onChange':this.changeStateWithCheckbox('chars_AZ')}), "A-Z"]),
|
||||
React.DOM.label({}, [ React.DOM.input({'type':'checkbox', 'checked':this.state['chars_az'], 'onChange':this.changeStateWithCheckbox('chars_az')}), "a-z"]),
|
||||
React.DOM.label({}, [ React.DOM.input({'type':'checkbox', 'checked':this.state['chars_09'], 'onChange':this.changeStateWithCheckbox('chars_09')}), "0-9"]),
|
||||
React.DOM.label({}, [ React.DOM.input({'type':'checkbox', 'checked':this.state['chars_space'], 'onChange':this.changeStateWithCheckbox('chars_space')}), "space"]),
|
||||
React.DOM.label({}, [ React.DOM.input({'type':'checkbox', 'checked':this.state['chars_other'], 'onChange':this.changeStateWithCheckbox('chars_other')}), "!#?"]),
|
||||
]),
|
||||
Clipperz.PM.UI.Components.Cards.TextArea({'rows':'1', 'value':this.state['charset'], 'onChange':this.changeStateWithTargetValue('charset', true)})
|
||||
]),
|
||||
])
|
||||
]),
|
||||
React.DOM.div({'className':'passwordValue'}, [
|
||||
React.DOM.div({'className':'passwordWrapper'}, [
|
||||
Clipperz.PM.UI.Components.Cards.TextArea({'rows':'1', 'value':this.state['password'], 'onChange':this.changeStateWithTargetValue('password', false)}),
|
||||
React.DOM.div({'className':'entropyWrapper', 'style':{'backgroundColor':entropyColor}}, [ React.DOM.div({'className':'entropy', 'style':{'width': entropyWidth}})])
|
||||
]),
|
||||
React.DOM.div({'className':'button', 'onClick':this.refreshPasswordValue}, "generate new password"),
|
||||
]),
|
||||
React.DOM.div({'className':'button setValue', 'onClick':this.setPasswordValue})
|
||||
])
|
||||
])
|
||||
]);
|
||||
|
||||
return result;
|
||||
},
|
||||
|
||||
//=========================================================================
|
||||
});
|
||||
|
||||
Clipperz.PM.UI.Components.Cards.PasswordGenerator = React.createFactory(Clipperz.PM.UI.Components.Cards.PasswordGeneratorClass);
|
||||
Reference in New Issue
Block a user