password-manager-mirror/frontend/delta/js/Clipperz/PM/UI/Components/Cards/PasswordGenerator.js

224 lines
8.1 KiB
JavaScript

/*
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({
displayName: 'Clipperz.PM.UI.Components.Cards.PasswordGenerator',
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': this.props['preferences'].getValue('passwordGenerator.length'),
'options': 'closed',
'chars_AZ': this.props['preferences'].getValue('passwordGenerator.characters.A-Z'),
'chars_az': this.props['preferences'].getValue('passwordGenerator.characters.a-z'),
'chars_09': this.props['preferences'].getValue('passwordGenerator.characters.0-9'),
'chars_space': this.props['preferences'].getValue('passwordGenerator.characters.space'),
'chars_other': this.props['preferences'].getValue('passwordGenerator.characters.!#?'),
// 'charset': this.props['preferences'].getValue('passwordGenerator.charset'),
'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'])
});
},
handleFormSubmit: function (anEvent) {
anEvent.preventDefault();
},
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);
}
},
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({'onSubmit':this.handleFormSubmit}, [
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':'button generatePassword', 'onClick':this.refreshPasswordValue}, "generate password"),
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 setPasswordValue', 'onClick':this.setPasswordValue}, "set password"),
]),
])
])
]);
return result;
},
//=========================================================================
});
Clipperz.PM.UI.Components.Cards.PasswordGenerator = React.createFactory(Clipperz.PM.UI.Components.Cards.PasswordGeneratorClass);