mirror of https://github.com/keeweb/keeweb.git
fix #1006: password generator patterns
This commit is contained in:
parent
c4be2e5d00
commit
8021c6bb81
|
@ -47,23 +47,20 @@ const GeneratorPresets = {
|
|||
name: 'Mac',
|
||||
title: Locale.genPresetMac,
|
||||
length: 17,
|
||||
upper: true,
|
||||
digits: true,
|
||||
special: true
|
||||
include: '0123456789ABCDEF',
|
||||
pattern: 'XX-'
|
||||
},
|
||||
{
|
||||
name: 'Hash128',
|
||||
title: Locale.genPresetHash128,
|
||||
length: 32,
|
||||
lower: true,
|
||||
digits: true
|
||||
include: '0123456789abcdef'
|
||||
},
|
||||
{
|
||||
name: 'Hash256',
|
||||
title: Locale.genPresetHash256,
|
||||
length: 64,
|
||||
lower: true,
|
||||
digits: true
|
||||
include: '0123456789abcdef'
|
||||
}
|
||||
];
|
||||
},
|
||||
|
|
|
@ -138,6 +138,10 @@
|
|||
"genPsAmbiguous": "Ambiguous symbols",
|
||||
"genPsInclude": "Additional symbols to include",
|
||||
"genPsExample": "Example of generated password",
|
||||
"genPsPattern": "Pattern",
|
||||
"genPsPatternHelp": "Patterns can be used to specify custom rules for selecting characters. For example, 1-AA will generate passwords starting with a digit, followed by a dash and two letters. You can use these symbols:",
|
||||
"genPsAllRanges": "All symbols",
|
||||
"genPsIncluded": "Additional symbols added above",
|
||||
|
||||
"keyChangeTitleRemote": "Master Key Changed",
|
||||
"keyChangeMessageRemote": "Master key was changed for this database. Please enter a new key",
|
||||
|
|
|
@ -1,80 +1,65 @@
|
|||
import kdbxweb from 'kdbxweb';
|
||||
import { phonetic } from 'util/generators/phonetic';
|
||||
|
||||
const PasswordGenerator = {
|
||||
charRanges: {
|
||||
upper: 'ABCDEFGHJKLMNPQRSTUVWXYZ',
|
||||
lower: 'abcdefghijkmnpqrstuvwxyz',
|
||||
digits: '123456789',
|
||||
special: '!@#$%^&*_+-=,./?;:`"~\'\\',
|
||||
brackets: '(){}[]<>',
|
||||
high:
|
||||
'¡¢£¤¥¦§©ª«¬®¯°±²³´µ¶¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖ×ØÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþ',
|
||||
ambiguous: 'O0oIl'
|
||||
},
|
||||
const CharRanges = {
|
||||
upper: 'ABCDEFGHJKLMNPQRSTUVWXYZ',
|
||||
lower: 'abcdefghijkmnpqrstuvwxyz',
|
||||
digits: '123456789',
|
||||
special: '!@#$%^&*_+-=,./?;:`"~\'\\',
|
||||
brackets: '(){}[]<>',
|
||||
high:
|
||||
'¡¢£¤¥¦§©ª«¬®¯°±²³´µ¶¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖ×ØÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþ',
|
||||
ambiguous: 'O0oIl'
|
||||
};
|
||||
|
||||
const DefaultCharRangesByPattern = {
|
||||
'A': CharRanges.upper,
|
||||
'a': CharRanges.lower,
|
||||
'1': CharRanges.digits,
|
||||
'*': CharRanges.special,
|
||||
'[': CharRanges.brackets,
|
||||
'Ä': CharRanges.high,
|
||||
'0': CharRanges.ambiguous
|
||||
};
|
||||
|
||||
const PasswordGenerator = {
|
||||
generate(opts) {
|
||||
if (!opts || typeof opts.length !== 'number' || opts.length < 0) {
|
||||
return '';
|
||||
}
|
||||
switch (opts.name) {
|
||||
case 'Pronounceable':
|
||||
return this.generatePronounceable(opts);
|
||||
case 'Hash128':
|
||||
return this.generateHash(32);
|
||||
case 'Hash256':
|
||||
return this.generateHash(64);
|
||||
case 'Mac':
|
||||
return this.generateMac();
|
||||
if (opts.name === 'Pronounceable') {
|
||||
return this.generatePronounceable(opts);
|
||||
}
|
||||
const ranges = Object.keys(this.charRanges)
|
||||
const ranges = Object.keys(CharRanges)
|
||||
.filter(r => opts[r])
|
||||
.map(function(r) {
|
||||
return this.charRanges[r];
|
||||
}, this);
|
||||
.map(r => CharRanges[r]);
|
||||
if (opts.include && opts.include.length) {
|
||||
ranges.push(opts.include);
|
||||
}
|
||||
if (!ranges.length) {
|
||||
return '';
|
||||
}
|
||||
const pool = ranges.join('');
|
||||
const rangesByPatternChar = {
|
||||
...DefaultCharRangesByPattern,
|
||||
'X': ranges.join(''),
|
||||
'I': opts.include || ''
|
||||
};
|
||||
const pattern = opts.pattern || 'X';
|
||||
const randomBytes = kdbxweb.Random.getBytes(opts.length);
|
||||
const chars = [];
|
||||
for (let i = 0; i < opts.length; i++) {
|
||||
const rand = Math.round(Math.random() * 1000) + randomBytes[i];
|
||||
chars.push(pool[rand % pool.length]);
|
||||
const patternChar = pattern[i % pattern.length];
|
||||
const range = rangesByPatternChar[patternChar];
|
||||
const char = range ? range[rand % range.length] : patternChar;
|
||||
chars.push(char);
|
||||
}
|
||||
return chars.join('');
|
||||
},
|
||||
|
||||
generateMac() {
|
||||
const segmentsCount = 6;
|
||||
const randomBytes = kdbxweb.Random.getBytes(segmentsCount);
|
||||
let result = '';
|
||||
for (let i = 0; i < segmentsCount; i++) {
|
||||
let segment = randomBytes[i].toString(16).toUpperCase();
|
||||
if (segment.length < 2) {
|
||||
segment = '0' + segment;
|
||||
}
|
||||
result += (result ? '-' : '') + segment;
|
||||
}
|
||||
return result;
|
||||
},
|
||||
|
||||
generateHash(length) {
|
||||
const randomBytes = kdbxweb.Random.getBytes(length);
|
||||
let result = '';
|
||||
for (let i = 0; i < length; i++) {
|
||||
result += randomBytes[i].toString(16)[0];
|
||||
}
|
||||
return result;
|
||||
},
|
||||
|
||||
generatePronounceable(opts) {
|
||||
const pass = phonetic.generate({
|
||||
length: opts.length,
|
||||
seed: this.generateHash(1024)
|
||||
length: opts.length
|
||||
});
|
||||
let result = '';
|
||||
const upper = [];
|
||||
|
@ -98,7 +83,7 @@ const PasswordGenerator = {
|
|||
const opts = {};
|
||||
let length = 0;
|
||||
if (password) {
|
||||
const charRanges = this.charRanges;
|
||||
const charRanges = CharRanges;
|
||||
password.forEachChar(ch => {
|
||||
length++;
|
||||
ch = String.fromCharCode(ch);
|
||||
|
@ -114,4 +99,4 @@ const PasswordGenerator = {
|
|||
}
|
||||
};
|
||||
|
||||
export { PasswordGenerator };
|
||||
export { PasswordGenerator, CharRanges };
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { Events } from 'framework/events';
|
||||
import { View } from 'framework/views/view';
|
||||
import { GeneratorPresets } from 'comp/app/generator-presets';
|
||||
import { PasswordGenerator } from 'util/generators/password-generator';
|
||||
import { PasswordGenerator, CharRanges } from 'util/generators/password-generator';
|
||||
import { Locale } from 'util/locale';
|
||||
import { Scrollable } from 'framework/views/scrollable';
|
||||
import template from 'templates/generator-presets.hbs';
|
||||
|
@ -16,12 +16,14 @@ class GeneratorPresetsView extends View {
|
|||
'change .gen-ps__list': 'changePreset',
|
||||
'click .gen-ps__btn-create': 'createPreset',
|
||||
'click .gen-ps__btn-delete': 'deletePreset',
|
||||
'click .info-btn--pattern': 'togglePatternHelp',
|
||||
'input #gen-ps__field-title': 'changeTitle',
|
||||
'change #gen-ps__check-enabled': 'changeEnabled',
|
||||
'change #gen-ps__check-default': 'changeDefault',
|
||||
'input #gen-ps__field-length': 'changeLength',
|
||||
'change .gen-ps__check-range': 'changeRange',
|
||||
'input #gen-ps__field-include': 'changeInclude'
|
||||
'input #gen-ps__field-include': 'changeInclude',
|
||||
'input #gen-ps__field-pattern': 'changePattern'
|
||||
};
|
||||
|
||||
selected = null;
|
||||
|
@ -65,7 +67,7 @@ class GeneratorPresetsView extends View {
|
|||
name: nameLower,
|
||||
title: Locale['genPs' + name],
|
||||
enabled: sel[nameLower],
|
||||
sample: rangeOverride[nameLower] || PasswordGenerator.charRanges[nameLower]
|
||||
sample: rangeOverride[nameLower] || CharRanges[nameLower]
|
||||
};
|
||||
}
|
||||
);
|
||||
|
@ -119,6 +121,10 @@ class GeneratorPresetsView extends View {
|
|||
this.render();
|
||||
}
|
||||
|
||||
togglePatternHelp() {
|
||||
this.$el.find('.gen-ps__pattern-help').toggleClass('hide');
|
||||
}
|
||||
|
||||
changeTitle(e) {
|
||||
const title = $.trim(e.target.value);
|
||||
if (title && title !== this.getPreset(this.selected).title) {
|
||||
|
@ -175,6 +181,15 @@ class GeneratorPresetsView extends View {
|
|||
this.presets = GeneratorPresets.all;
|
||||
this.renderExample();
|
||||
}
|
||||
|
||||
changePattern(e) {
|
||||
const pattern = e.target.value;
|
||||
if (pattern !== this.getPreset(this.selected).pattern) {
|
||||
GeneratorPresets.setPreset(this.selected, { pattern });
|
||||
}
|
||||
this.presets = GeneratorPresets.all;
|
||||
this.renderExample();
|
||||
}
|
||||
}
|
||||
|
||||
Object.assign(GeneratorPresetsView.prototype, Scrollable);
|
||||
|
|
|
@ -38,4 +38,8 @@
|
|||
&__input {
|
||||
height: 2em;
|
||||
}
|
||||
|
||||
&__pattern-help {
|
||||
margin-bottom: $base-padding-v;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@ $fa-font-path: '~font-awesome/fonts';
|
|||
@import 'utils/common-dropdown';
|
||||
@import 'utils/auto-type-hint';
|
||||
@import 'utils/drag';
|
||||
@import 'utils/help';
|
||||
@import 'utils/selection';
|
||||
|
||||
@import 'common/dates';
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
.info-btn {
|
||||
cursor: pointer;
|
||||
color: var(--muted-color);
|
||||
&:hover {
|
||||
color: var(--text-color);
|
||||
}
|
||||
}
|
|
@ -41,6 +41,25 @@
|
|||
<input type="text" class="input-base" id="gen-ps__field-include" value="{{selected.include}}"
|
||||
{{#if selected.builtIn}}readonly{{/if}} />
|
||||
</div>
|
||||
<div class="gen-ps__field">
|
||||
<label for="gen-ps__field-pattern">{{res 'genPsPattern'}}: <i class="fa fa-info-circle info-btn info-btn--pattern"></i></label>
|
||||
<div class="gen-ps__pattern-help hide">
|
||||
<p>{{res 'genPsPatternHelp'}}</p>
|
||||
<p>
|
||||
<code>X</code> – {{res 'genPsAllRanges'}}<br/>
|
||||
<code>A</code> – {{res 'genPsUpper'}}<br/>
|
||||
<code>a</code> – {{res 'genPsLower'}}<br/>
|
||||
<code>1</code> – {{res 'genPsDigits'}}<br/>
|
||||
<code>*</code> – {{res 'genPsSpecial'}}<br/>
|
||||
<code>[</code> – {{res 'genPsBrackets'}}<br/>
|
||||
<code>Ä</code> – {{res 'genPsHigh'}}<br/>
|
||||
<code>0</code> – {{res 'genPsAmbiguous'}}<br/>
|
||||
<code>I</code> – {{res 'genPsIncluded'}}
|
||||
</p>
|
||||
</div>
|
||||
<input type="text" class="input-base" id="gen-ps__field-pattern" value="{{selected.pattern}}"
|
||||
{{#if selected.builtIn}}readonly{{/if}} />
|
||||
</div>
|
||||
<div class="gen-ps__field">
|
||||
<label>{{res 'genPsExample'}}:</label>
|
||||
<div class="gen-ps__example"></div>
|
||||
|
|
|
@ -5,6 +5,7 @@ Release notes
|
|||
`+` #1065: PORTABLE_EXECUTABLE_DIR environment variable
|
||||
`*` #1397: Segoe UI font on Windows
|
||||
`+` #1393: option to disable saving and exporting (canSaveTo)
|
||||
`+` #1006: password generator patterns
|
||||
`-` fix #1396: fixed hyperlinks in notes
|
||||
`-` fix #1323: version in the About dialog
|
||||
`-` fix #734: OTP secrets with spaces
|
||||
|
|
Loading…
Reference in New Issue