Skip to content

Commit

Permalink
Adding db-show CLI command.
Browse files Browse the repository at this point in the history
This adds a basic db-show CLI command, to display
the information related to a database.
  • Loading branch information
louib authored and louib committed Jan 25, 2020
1 parent 796b5ce commit a97a807
Show file tree
Hide file tree
Showing 15 changed files with 173 additions and 12 deletions.
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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
Expand Down
5 changes: 4 additions & 1 deletion share/docs/man/keepassxc-cli.1
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,12 @@ Copies the password or the current TOTP (\fI-t\fP option) of a database entry to
.IP "close"
In interactive mode, closes the currently opened database (see \fIopen\fP).

.IP "create [options] <database>"
.IP "db-create [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 "db-info [options] <database>"
Show a database's information.

.IP "diceware [options]"
Generates a random diceware passphrase.

Expand Down
1 change: 1 addition & 0 deletions src/cli/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ set(cli_SOURCES
Generate.cpp
Help.cpp
Import.cpp
Info.cpp
List.cpp
Locate.cpp
Merge.cpp
Expand Down
4 changes: 3 additions & 1 deletion src/cli/Command.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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()));
Expand Down
48 changes: 48 additions & 0 deletions src/cli/Info.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*
* 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 "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;
out << QObject::tr("Cipher: ") << SymmetricCipher::cipherToString(database->cipher()) << 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 changes: 31 additions & 0 deletions src/cli/Info.h
Original file line number Diff line number Diff line change
@@ -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
14 changes: 14 additions & 0 deletions src/crypto/SymmetricCipher.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,20 @@ SymmetricCipher::Algorithm SymmetricCipher::cipherToAlgorithm(const QUuid& ciphe
return InvalidAlgorithm;
}

QString SymmetricCipher::cipherToString(const QUuid& cipher)
{
if (cipher == KeePass2::CIPHER_AES256) {
return QString("AES 256-bit");
} else if (cipher == KeePass2::CIPHER_CHACHA20) {
return QString("Chacha20");
} else if (cipher == KeePass2::CIPHER_TWOFISH) {
return QString("Twofish 256-bit");
}

qWarning("SymmetricCipher::cipherToString: invalid UUID %s", cipher.toString().toLatin1().data());
return QString();
}

QUuid SymmetricCipher::algorithmToCipher(Algorithm algo)
{
switch (algo) {
Expand Down
1 change: 1 addition & 0 deletions src/crypto/SymmetricCipher.h
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ class SymmetricCipher
Algorithm algorithm() const;

static Algorithm cipherToAlgorithm(const QUuid& cipher);
static QString cipherToString(const QUuid& cipher);
static QUuid algorithmToCipher(Algorithm algo);
static int algorithmIvSize(Algorithm algo);
static Mode algorithmMode(Algorithm algo);
Expand Down
5 changes: 5 additions & 0 deletions src/crypto/kdf/AesKdf.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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()));
}
1 change: 1 addition & 0 deletions src/crypto/kdf/AesKdf.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ class AesKdf : public Kdf
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;
Expand Down
5 changes: 5 additions & 0 deletions src/crypto/kdf/Argon2Kdf.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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()));
}
1 change: 1 addition & 0 deletions src/crypto/kdf/Argon2Kdf.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ class Argon2Kdf : public Kdf
bool setMemory(quint64 kibibytes);
quint32 parallelism() const;
bool setParallelism(quint32 threads);
QString toString() const override;

protected:
int benchmarkImpl(int msec) const override;
Expand Down
2 changes: 2 additions & 0 deletions src/crypto/kdf/Kdf.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ class Kdf
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:
Expand Down
58 changes: 48 additions & 10 deletions tests/TestCli.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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"));
Expand All @@ -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()
Expand All @@ -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"));
Expand All @@ -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()
Expand Down Expand Up @@ -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();
Expand All @@ -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.
Expand All @@ -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);

Expand All @@ -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);

Expand All @@ -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;
Expand Down Expand Up @@ -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>(
Expand Down
1 change: 1 addition & 0 deletions tests/TestCli.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ private slots:
void testGenerate_data();
void testGenerate();
void testImport();
void testInfo();
void testKeyFileOption();
void testNoPasswordOption();
void testHelp();
Expand Down

0 comments on commit a97a807

Please sign in to comment.