Adding db-info CLI command. (#4231)

This adds a basic db-show CLI command, to display
the information related to a database.
This commit is contained in:
louib 2020-01-26 23:44:31 -05:00 committed by Jonathan White
parent 04be724614
commit b78ca924fd
14 changed files with 166 additions and 15 deletions

View File

@ -1,5 +1,13 @@
# Changelog
## 2.6 (unreleased)
### Added
- Added CLI db-info command [#4231]
### Changed
- Renamed CLI create command to db-create [#4231]
## 2.5.3 (2020-01-19)
### Fixed

View File

@ -28,9 +28,12 @@ Copies the password or the current TOTP (\fI-t\fP option) of a database entry to
.IP "\fBclose\fP"
In interactive mode, closes the currently opened database (see \fIopen\fP).
.IP "\fBcreate\fP [options] <database>"
.IP "\fBdb-create\fP [options] <database>"
Creates a new database with a key file and/or password. The key file will be created if the file that is referred to does not exist. If both the key file and password are empty, no database will be created.
.IP "\fBdb-info\fP [options] <database>"
Show a database's information.
.IP "\fBdiceware\fP [options]"
Generates a random diceware passphrase.

View File

@ -30,6 +30,7 @@ set(cli_SOURCES
Generate.cpp
Help.cpp
Import.cpp
Info.cpp
List.cpp
Locate.cpp
Merge.cpp

View File

@ -37,6 +37,7 @@
#include "Generate.h"
#include "Help.h"
#include "Import.h"
#include "Info.h"
#include "List.h"
#include "Locate.h"
#include "Merge.h"
@ -160,7 +161,8 @@ namespace Commands
s_commands.insert(QStringLiteral("analyze"), QSharedPointer<Command>(new Analyze()));
s_commands.insert(QStringLiteral("clip"), QSharedPointer<Command>(new Clip()));
s_commands.insert(QStringLiteral("close"), QSharedPointer<Command>(new Close()));
s_commands.insert(QStringLiteral("create"), QSharedPointer<Command>(new Create()));
s_commands.insert(QStringLiteral("db-create"), QSharedPointer<Command>(new Create()));
s_commands.insert(QStringLiteral("db-info"), QSharedPointer<Command>(new Info()));
s_commands.insert(QStringLiteral("diceware"), QSharedPointer<Command>(new Diceware()));
s_commands.insert(QStringLiteral("edit"), QSharedPointer<Command>(new Edit()));
s_commands.insert(QStringLiteral("estimate"), QSharedPointer<Command>(new Estimate()));

53
src/cli/Info.cpp Normal file
View File

@ -0,0 +1,53 @@
/*
* Copyright (C) 2020 KeePassXC Team <team@keepassxc.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 or (at your option)
* version 3 of the License.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <cstdlib>
#include <stdio.h>
#include "Info.h"
#include "core/Database.h"
#include "core/Metadata.h"
#include "format/KeePass2.h"
#include "Utils.h"
Info::Info()
{
name = QString("db-show");
description = QObject::tr("Show a database's information.");
}
int Info::executeWithDatabase(QSharedPointer<Database> database, QSharedPointer<QCommandLineParser>)
{
TextStream out(Utils::STDOUT, QIODevice::WriteOnly);
out << QObject::tr("UUID: ") << database->uuid().toString() << endl;
out << QObject::tr("Name: ") << database->metadata()->name() << endl;
out << QObject::tr("Description: ") << database->metadata()->description() << endl;
for (auto& cipher : asConst(KeePass2::CIPHERS)) {
if (cipher.first == database->cipher()) {
out << QObject::tr("Cipher: ") << cipher.second << endl;
}
}
out << QObject::tr("KDF: ") << database->kdf()->toString() << endl;
if (database->metadata()->recycleBinEnabled()) {
out << QObject::tr("Recycle bin is enabled.") << endl;
} else {
out << QObject::tr("Recycle bin is not enabled.") << endl;
}
return EXIT_SUCCESS;
}

31
src/cli/Info.h Normal file
View File

@ -0,0 +1,31 @@
/*
* Copyright (C) 2020 KeePassXC Team <team@keepassxc.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 or (at your option)
* version 3 of the License.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef KEEPASSXC_INFO_H
#define KEEPASSXC_INFO_H
#include "DatabaseCommand.h"
class Info : public DatabaseCommand
{
public:
Info();
int executeWithDatabase(QSharedPointer<Database> db, QSharedPointer<QCommandLineParser> parser);
};
#endif // KEEPASSXC_INFO_H

View File

@ -125,3 +125,8 @@ int AesKdf::benchmarkImpl(int msec) const
return static_cast<int>(rounds * (static_cast<float>(msec) / timer.elapsed()));
}
QString AesKdf::toString() const
{
return QObject::tr("AES (%1 rounds)").arg(QString::number(rounds()));
}

View File

@ -30,6 +30,7 @@ public:
QVariantMap writeParameters() override;
bool transform(const QByteArray& raw, QByteArray& result) const override;
QSharedPointer<Kdf> clone() const override;
QString toString() const override;
protected:
int benchmarkImpl(int msec) const override;

View File

@ -211,3 +211,8 @@ int Argon2Kdf::benchmarkImpl(int msec) const
return 1;
}
QString Argon2Kdf::toString() const
{
return QObject::tr("Argon2 (%1 rounds, %2 KB)").arg(QString::number(rounds()), QString::number(memory()));
}

View File

@ -36,6 +36,7 @@ public:
bool setMemory(quint64 kibibytes);
quint32 parallelism() const;
bool setParallelism(quint32 threads);
QString toString() const override;
protected:
int benchmarkImpl(int msec) const override;

View File

@ -44,6 +44,8 @@ public:
virtual bool transform(const QByteArray& raw, QByteArray& result) const = 0;
virtual QSharedPointer<Kdf> clone() const = 0;
virtual QString toString() const = 0;
int benchmark(int msec) const;
protected:

View File

@ -48,9 +48,9 @@ const QString KeePass2::KDFPARAM_ARGON2_SECRET("K");
const QString KeePass2::KDFPARAM_ARGON2_ASSOCDATA("A");
const QList<QPair<QUuid, QString>> KeePass2::CIPHERS{
qMakePair(KeePass2::CIPHER_AES256, QObject::tr("AES: 256-bit")),
qMakePair(KeePass2::CIPHER_TWOFISH, QObject::tr("Twofish: 256-bit")),
qMakePair(KeePass2::CIPHER_CHACHA20, QObject::tr("ChaCha20: 256-bit"))};
qMakePair(KeePass2::CIPHER_AES256, QObject::tr("AES 256-bit")),
qMakePair(KeePass2::CIPHER_TWOFISH, QObject::tr("Twofish 256-bit")),
qMakePair(KeePass2::CIPHER_CHACHA20, QObject::tr("ChaCha20 256-bit"))};
const QList<QPair<QUuid, QString>> KeePass2::KDFS{
qMakePair(KeePass2::KDF_ARGON2, QObject::tr("Argon2 (KDBX 4 recommended)")),

View File

@ -44,6 +44,7 @@
#include "cli/Generate.h"
#include "cli/Help.h"
#include "cli/Import.h"
#include "cli/Info.h"
#include "cli/List.h"
#include "cli/Locate.h"
#include "cli/Merge.h"
@ -192,7 +193,8 @@ void TestCli::testBatchCommands()
QVERIFY(Commands::getCommand("analyze"));
QVERIFY(Commands::getCommand("clip"));
QVERIFY(Commands::getCommand("close"));
QVERIFY(Commands::getCommand("create"));
QVERIFY(Commands::getCommand("db-create"));
QVERIFY(Commands::getCommand("db-info"));
QVERIFY(Commands::getCommand("diceware"));
QVERIFY(Commands::getCommand("edit"));
QVERIFY(Commands::getCommand("estimate"));
@ -210,7 +212,7 @@ void TestCli::testBatchCommands()
QVERIFY(Commands::getCommand("rmdir"));
QVERIFY(Commands::getCommand("show"));
QVERIFY(!Commands::getCommand("doesnotexist"));
QCOMPARE(Commands::getCommands().size(), 21);
QCOMPARE(Commands::getCommands().size(), 22);
}
void TestCli::testInteractiveCommands()
@ -220,7 +222,8 @@ void TestCli::testInteractiveCommands()
QVERIFY(Commands::getCommand("analyze"));
QVERIFY(Commands::getCommand("clip"));
QVERIFY(Commands::getCommand("close"));
QVERIFY(Commands::getCommand("create"));
QVERIFY(Commands::getCommand("db-create"));
QVERIFY(Commands::getCommand("db-info"));
QVERIFY(Commands::getCommand("diceware"));
QVERIFY(Commands::getCommand("edit"));
QVERIFY(Commands::getCommand("estimate"));
@ -238,7 +241,7 @@ void TestCli::testInteractiveCommands()
QVERIFY(Commands::getCommand("rmdir"));
QVERIFY(Commands::getCommand("show"));
QVERIFY(!Commands::getCommand("doesnotexist"));
QCOMPARE(Commands::getCommands().size(), 21);
QCOMPARE(Commands::getCommands().size(), 22);
}
void TestCli::testAdd()
@ -548,7 +551,7 @@ void TestCli::testCreate()
QString databaseFilename = testDir->path() + "/testCreate1.kdbx";
// Password
Utils::Test::setNextPassword("a");
createCmd.execute({"create", databaseFilename});
createCmd.execute({"db-create", databaseFilename});
m_stderrFile->reset();
m_stdoutFile->reset();
@ -563,7 +566,7 @@ void TestCli::testCreate()
// Should refuse to create the database if it already exists.
qint64 pos = m_stdoutFile->pos();
qint64 errPos = m_stderrFile->pos();
createCmd.execute({"create", databaseFilename});
createCmd.execute({"db-create", databaseFilename});
m_stdoutFile->seek(pos);
m_stderrFile->seek(errPos);
// Output should be empty when there is an error.
@ -577,7 +580,7 @@ void TestCli::testCreate()
pos = m_stdoutFile->pos();
errPos = m_stderrFile->pos();
Utils::Test::setNextPassword("a");
createCmd.execute({"create", databaseFilename2, "-k", keyfilePath});
createCmd.execute({"db-create", databaseFilename2, "-k", keyfilePath});
m_stdoutFile->seek(pos);
m_stderrFile->seek(errPos);
@ -594,7 +597,7 @@ void TestCli::testCreate()
pos = m_stdoutFile->pos();
errPos = m_stderrFile->pos();
Utils::Test::setNextPassword("a");
createCmd.execute({"create", databaseFilename3, "-k", keyfilePath});
createCmd.execute({"db-create", databaseFilename3, "-k", keyfilePath});
m_stdoutFile->seek(pos);
m_stderrFile->seek(errPos);
@ -607,6 +610,41 @@ void TestCli::testCreate()
QVERIFY(db3);
}
void TestCli::testInfo()
{
Info infoCmd;
QVERIFY(!infoCmd.name.isEmpty());
QVERIFY(infoCmd.getDescriptionLine().contains(infoCmd.name));
Utils::Test::setNextPassword("a");
infoCmd.execute({"db-info", m_dbFile->fileName()});
m_stdoutFile->reset();
m_stderrFile->reset();
m_stdoutFile->readLine(); // skip prompt line
QCOMPARE(m_stderrFile->readAll(), QByteArray(""));
QVERIFY(m_stdoutFile->readLine().contains(QByteArray("UUID: ")));
QCOMPARE(m_stdoutFile->readLine(), QByteArray("Name: \n"));
QCOMPARE(m_stdoutFile->readLine(), QByteArray("Description: \n"));
QCOMPARE(m_stdoutFile->readLine(), QByteArray("Cipher: AES 256-bit\n"));
QCOMPARE(m_stdoutFile->readLine(), QByteArray("KDF: AES (6000 rounds)\n"));
QCOMPARE(m_stdoutFile->readLine(), QByteArray("Recycle bin is enabled.\n"));
// Test with quiet option.
qint64 pos = m_stdoutFile->pos();
qint64 errPos = m_stderrFile->pos();
Utils::Test::setNextPassword("a");
infoCmd.execute({"db-info", "-q", m_dbFile->fileName()});
m_stdoutFile->seek(pos);
m_stderrFile->seek(errPos);
QCOMPARE(m_stderrFile->readAll(), QByteArray(""));
QVERIFY(m_stdoutFile->readLine().contains(QByteArray("UUID: ")));
QCOMPARE(m_stdoutFile->readLine(), QByteArray("Name: \n"));
QCOMPARE(m_stdoutFile->readLine(), QByteArray("Description: \n"));
QCOMPARE(m_stdoutFile->readLine(), QByteArray("Cipher: AES 256-bit\n"));
QCOMPARE(m_stdoutFile->readLine(), QByteArray("KDF: AES (6000 rounds)\n"));
QCOMPARE(m_stdoutFile->readLine(), QByteArray("Recycle bin is enabled.\n"));
}
void TestCli::testDiceware()
{
Diceware dicewareCmd;
@ -1446,10 +1484,10 @@ void TestCli::testMergeWithKeys()
qint64 pos = m_stdoutFile->pos();
Utils::Test::setNextPassword("a");
createCmd.execute({"create", sourceDatabaseFilename, "-k", sourceKeyfilePath});
createCmd.execute({"db-create", sourceDatabaseFilename, "-k", sourceKeyfilePath});
Utils::Test::setNextPassword("b");
createCmd.execute({"create", targetDatabaseFilename, "-k", targetKeyfilePath});
createCmd.execute({"db-create", targetDatabaseFilename, "-k", targetKeyfilePath});
Utils::Test::setNextPassword("a");
auto sourceDatabase = QSharedPointer<Database>(

View File

@ -59,6 +59,7 @@ private slots:
void testGenerate_data();
void testGenerate();
void testImport();
void testInfo();
void testKeyFileOption();
void testNoPasswordOption();
void testHelp();