Skip to content

Commit

Permalink
Add -i/--include option to "generate" CLI command. (keepassxreboot#7112)
Browse files Browse the repository at this point in the history
  • Loading branch information
libklein authored Dec 8, 2021
1 parent b3896f2 commit a0a063b
Show file tree
Hide file tree
Showing 9 changed files with 357 additions and 211 deletions.
4 changes: 4 additions & 0 deletions share/translations/keepassxc_en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7557,6 +7557,10 @@ Please consider generating a new key file.</source>
<source>AES-KDF (KDBX 3)</source>
<translation type="unfinished">AES-KDF (KDBX 3.1) {3)?}</translation>
</message>
<message>
<source>Use custom character set</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>QtIOCompressor</name>
Expand Down
19 changes: 16 additions & 3 deletions src/cli/Generate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,12 @@ const QCommandLineOption Generate::ExcludeCharsOption = QCommandLineOption(QStri
QObject::tr("Exclude character set"),
QObject::tr("chars"));

const QCommandLineOption Generate::CustomCharacterSetOption =
QCommandLineOption(QStringList() << "c"
<< "custom",
QObject::tr("Use custom character set"),
QObject::tr("chars"));

const QCommandLineOption Generate::ExcludeSimilarCharsOption =
QCommandLineOption(QStringList() << "exclude-similar", QObject::tr("Exclude similar looking characters"));

Expand All @@ -71,6 +77,7 @@ Generate::Generate()
options.append(Generate::ExcludeCharsOption);
options.append(Generate::ExcludeSimilarCharsOption);
options.append(Generate::IncludeEveryGroupOption);
options.append(Generate::CustomCharacterSetOption);
}

/**
Expand Down Expand Up @@ -120,9 +127,15 @@ QSharedPointer<PasswordGenerator> Generate::createGenerator(QSharedPointer<QComm

// The default charset will be used if no explicit class
// option was set.
passwordGenerator->setCharClasses(classes);
passwordGenerator->setFlags(flags);
passwordGenerator->setExcludedChars(parser->value(Generate::ExcludeCharsOption));
if (flags != 0x0) {
passwordGenerator->setFlags(flags);
}
QString customCharacterSet = parser->value(Generate::CustomCharacterSetOption);
if (classes != 0x0 || !customCharacterSet.isNull()) {
passwordGenerator->setCharClasses(classes);
}
passwordGenerator->setCustomCharacterSet(customCharacterSet);
passwordGenerator->setExcludedCharacterSet(parser->value(Generate::ExcludeCharsOption));

if (!passwordGenerator->isValid()) {
err << QObject::tr("Invalid password generator after applying all options") << endl;
Expand Down
1 change: 1 addition & 0 deletions src/cli/Generate.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ class Generate : public Command
static const QCommandLineOption ExcludeCharsOption;
static const QCommandLineOption ExcludeSimilarCharsOption;
static const QCommandLineOption IncludeEveryGroupOption;
static const QCommandLineOption CustomCharacterSetOption;
};

#endif // KEEPASSXC_GENERATE_H
111 changes: 54 additions & 57 deletions src/core/PasswordGenerator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,49 +20,41 @@

#include "crypto/Random.h"

const char* PasswordGenerator::DefaultAdditionalChars = "";
const int PasswordGenerator::DefaultLength = 32;
const char* PasswordGenerator::DefaultCustomCharacterSet = "";
const char* PasswordGenerator::DefaultExcludedChars = "";

PasswordGenerator::PasswordGenerator()
: m_length(0)
, m_classes(nullptr)
, m_flags(nullptr)
, m_additional(PasswordGenerator::DefaultAdditionalChars)
: m_length(PasswordGenerator::DefaultLength)
, m_classes(PasswordGenerator::CharClass::DefaultCharset)
, m_flags(PasswordGenerator::GeneratorFlag::DefaultFlags)
, m_custom(PasswordGenerator::DefaultCustomCharacterSet)
, m_excluded(PasswordGenerator::DefaultExcludedChars)
{
}

void PasswordGenerator::setLength(int length)
{
if (length <= 0) {
m_length = DefaultLength;
return;
}
m_length = length;
}

void PasswordGenerator::setCharClasses(const CharClasses& classes)
void PasswordGenerator::setCharClasses(const PasswordGenerator::CharClasses& classes)
{
if (classes == 0) {
m_classes = DefaultCharset;
return;
}
m_classes = classes;
}

void PasswordGenerator::setFlags(const GeneratorFlags& flags)
void PasswordGenerator::setCustomCharacterSet(const QString& customCharacterSet)
{
m_flags = flags;
m_custom = customCharacterSet;
}

void PasswordGenerator::setAdditionalChars(const QString& chars)
void PasswordGenerator::setExcludedCharacterSet(const QString& excludedCharacterSet)
{
m_additional = chars;
m_excluded = excludedCharacterSet;
}

void PasswordGenerator::setExcludedChars(const QString& chars)
void PasswordGenerator::setFlags(const GeneratorFlags& flags)
{
m_excluded = chars;
m_flags = flags;
}

QString PasswordGenerator::generatePassword() const
Expand Down Expand Up @@ -114,9 +106,9 @@ QString PasswordGenerator::generatePassword() const

bool PasswordGenerator::isValid() const
{
if (m_classes == 0 && m_additional.isEmpty()) {
if (m_classes == CharClass::NoClass && m_custom.isEmpty()) {
return false;
} else if (m_length == 0) {
} else if (m_length <= 0) {
return false;
}

Expand Down Expand Up @@ -266,10 +258,10 @@ QVector<PasswordGroup> PasswordGenerator::passwordGroups() const

passwordGroups.append(group);
}
if (!m_additional.isEmpty()) {
if (!m_custom.isEmpty()) {
PasswordGroup group;

for (auto ch : m_additional) {
for (auto ch : m_custom) {
group.append(ch);
}

Expand Down Expand Up @@ -302,38 +294,43 @@ QVector<PasswordGroup> PasswordGenerator::passwordGroups() const

int PasswordGenerator::numCharClasses() const
{
int numClasses = 0;
// Actually compute the non empty password groups
auto non_empty_groups = passwordGroups();
return non_empty_groups.size();
}

if (m_classes & LowerLetters) {
numClasses++;
}
if (m_classes & UpperLetters) {
numClasses++;
}
if (m_classes & Numbers) {
numClasses++;
}
if (m_classes & Braces) {
numClasses++;
}
if (m_classes & Punctuation) {
numClasses++;
}
if (m_classes & Quotes) {
numClasses++;
}
if (m_classes & Dashes) {
numClasses++;
}
if (m_classes & Math) {
numClasses++;
}
if (m_classes & Logograms) {
numClasses++;
}
if (m_classes & EASCII) {
numClasses++;
int PasswordGenerator::getMinLength() const
{
if ((m_flags & CharFromEveryGroup)) {
return numCharClasses();
}

return numClasses;
return 1;
}
void PasswordGenerator::reset()
{
m_classes = CharClass::DefaultCharset;
m_flags = GeneratorFlag::DefaultFlags;
m_custom = DefaultCustomCharacterSet;
m_excluded = DefaultExcludedChars;
m_length = DefaultLength;
}
int PasswordGenerator::getLength() const
{
return m_length;
}
const PasswordGenerator::GeneratorFlags& PasswordGenerator::getFlags() const
{
return m_flags;
}
const PasswordGenerator::CharClasses& PasswordGenerator::getActiveClasses() const
{
return m_classes;
}
const QString& PasswordGenerator::getCustomCharacterSet() const
{
return m_custom;
}
const QString& PasswordGenerator::getExcludedCharacterSet() const
{
return m_excluded;
}
27 changes: 18 additions & 9 deletions src/core/PasswordGenerator.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#ifndef KEEPASSX_PASSWORDGENERATOR_H
#define KEEPASSX_PASSWORDGENERATOR_H

#include <QObject>
#include <QVector>

typedef QVector<QChar> PasswordGroup;
Expand All @@ -28,6 +29,7 @@ class PasswordGenerator
public:
enum CharClass
{
NoClass = 0,
LowerLetters = (1 << 0),
UpperLetters = (1 << 1),
Numbers = (1 << 2),
Expand All @@ -41,10 +43,11 @@ class PasswordGenerator
EASCII = (1 << 9),
DefaultCharset = LowerLetters | UpperLetters | Numbers
};
Q_DECLARE_FLAGS(CharClasses, CharClass)
Q_DECLARE_FLAGS(CharClasses, CharClass);

enum GeneratorFlag
{
NoFlags = 0,
ExcludeLookAlike = (1 << 0),
CharFromEveryGroup = (1 << 1),
AdvancedMode = (1 << 2),
Expand All @@ -56,17 +59,25 @@ class PasswordGenerator
PasswordGenerator();

void setLength(int length);
void setCharClasses(const CharClasses& classes);
void setFlags(const GeneratorFlags& flags);
void setAdditionalChars(const QString& chars);
void setExcludedChars(const QString& chars);
void setCharClasses(const CharClasses& classes);
void setCustomCharacterSet(const QString& customCharacterSet);
void setExcludedCharacterSet(const QString& excludedCharacterSet);
void reset();

bool isValid() const;
int getMinLength() const;

int getLength() const;
const GeneratorFlags& getFlags() const;
const CharClasses& getActiveClasses() const;
const QString& getCustomCharacterSet() const;
const QString& getExcludedCharacterSet() const;

QString generatePassword() const;

static const int DefaultLength = 32;
static const char* DefaultAdditionalChars;
static const int DefaultLength;
static const char* DefaultCustomCharacterSet;
static const char* DefaultExcludedChars;

private:
Expand All @@ -76,10 +87,8 @@ class PasswordGenerator
int m_length;
CharClasses m_classes;
GeneratorFlags m_flags;
QString m_additional;
QString m_custom;
QString m_excluded;

Q_DISABLE_COPY(PasswordGenerator)
};

Q_DECLARE_OPERATORS_FOR_FLAGS(PasswordGenerator::CharClasses)
Expand Down
48 changes: 6 additions & 42 deletions src/gui/PasswordGeneratorWidget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -576,51 +576,15 @@ void PasswordGeneratorWidget::updateGenerator()
auto classes = charClasses();
auto flags = generatorFlags();

int length = 0;
if (flags.testFlag(PasswordGenerator::CharFromEveryGroup)) {
if (classes.testFlag(PasswordGenerator::LowerLetters)) {
++length;
}
if (classes.testFlag(PasswordGenerator::UpperLetters)) {
++length;
}
if (classes.testFlag(PasswordGenerator::Numbers)) {
++length;
}
if (classes.testFlag(PasswordGenerator::Braces)) {
++length;
}
if (classes.testFlag(PasswordGenerator::Punctuation)) {
++length;
}
if (classes.testFlag(PasswordGenerator::Quotes)) {
++length;
}
if (classes.testFlag(PasswordGenerator::Dashes)) {
++length;
}
if (classes.testFlag(PasswordGenerator::Math)) {
++length;
}
if (classes.testFlag(PasswordGenerator::Logograms)) {
++length;
}
if (classes.testFlag(PasswordGenerator::EASCII)) {
++length;
}
}

length = qMax(length, m_ui->spinBoxLength->value());
m_passwordGenerator->setLength(length);
m_passwordGenerator->setCharClasses(classes);
m_passwordGenerator->setFlags(flags);
m_passwordGenerator->setLength(m_ui->spinBoxLength->value());
if (m_ui->buttonAdvancedMode->isChecked()) {
m_passwordGenerator->setAdditionalChars(m_ui->editAdditionalChars->text());
m_passwordGenerator->setExcludedChars(m_ui->editExcludedChars->text());
m_passwordGenerator->setCharClasses(classes);
m_passwordGenerator->setCustomCharacterSet(m_ui->editAdditionalChars->text());
m_passwordGenerator->setCustomCharacterSet(m_ui->editExcludedChars->text());
} else {
m_passwordGenerator->setAdditionalChars("");
m_passwordGenerator->setExcludedChars("");
m_passwordGenerator->setCharClasses(classes);
}
m_passwordGenerator->setFlags(flags);

if (m_passwordGenerator->isValid()) {
m_ui->buttonGenerate->setEnabled(true);
Expand Down
4 changes: 4 additions & 0 deletions tests/TestCli.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1195,6 +1195,10 @@ void TestCli::testGenerate_data()
<< QStringList{"generate", "-L", "2", "--upper", "-l", "--every-group"} << "^[a-z][A-Z]|[A-Z][a-z]$";
QTest::newRow("numbers + lowercase (every)")
<< QStringList{"generate", "-L", "2", "-n", "-l", "--every-group"} << "^[a-z][0-9]|[0-9][a-z]$";
QTest::newRow("custom character set")
<< QStringList{"generate", "-L", "200", "-n", "-c", "abc"} << "^[abc0-9]{200}$";
QTest::newRow("custom character set without extra options uses only custom chars")
<< QStringList{"generate", "-L", "200", "-c", "a"} << "^a{200}$";
}

void TestCli::testGenerate()
Expand Down
Loading

0 comments on commit a0a063b

Please sign in to comment.