mirror of
https://github.com/keepassxreboot/keepassxc.git
synced 2025-01-25 14:07:44 -05:00
CLI: Add group commands
This commit is contained in:
parent
964478e78f
commit
19f87ca057
@ -10,6 +10,7 @@
|
||||
- CLI: Add CSV export to the 'export' command [#3277]
|
||||
- CLI: Add `-y --yubikey` option for YubiKey [#3416](https://github.com/keepassxreboot/keepassxc/issues/3416)
|
||||
- Add 'Monospaced font' option to the Notes field [#3321](https://github.com/keepassxreboot/keepassxc/issues/3321)
|
||||
- CLI: Add group commands (mv, mkdir and rmdir) [#3313].
|
||||
|
||||
### Changed
|
||||
|
||||
|
80
src/cli/AddGroup.cpp
Normal file
80
src/cli/AddGroup.cpp
Normal file
@ -0,0 +1,80 @@
|
||||
/*
|
||||
* Copyright (C) 2019 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 "AddGroup.h"
|
||||
|
||||
#include "cli/TextStream.h"
|
||||
#include "cli/Utils.h"
|
||||
#include "core/Database.h"
|
||||
#include "core/Entry.h"
|
||||
#include "core/Group.h"
|
||||
|
||||
AddGroup::AddGroup()
|
||||
{
|
||||
name = QString("mkdir");
|
||||
description = QObject::tr("Adds a new group to a database.");
|
||||
positionalArguments.append({QString("group"), QObject::tr("Path of the group to add."), QString("")});
|
||||
}
|
||||
|
||||
AddGroup::~AddGroup()
|
||||
{
|
||||
}
|
||||
|
||||
int AddGroup::executeWithDatabase(QSharedPointer<Database> database, QSharedPointer<QCommandLineParser> parser)
|
||||
{
|
||||
TextStream outputTextStream(Utils::STDOUT, QIODevice::WriteOnly);
|
||||
TextStream errorTextStream(Utils::STDERR, QIODevice::WriteOnly);
|
||||
|
||||
const QStringList args = parser->positionalArguments();
|
||||
const QString& databasePath = args.at(0);
|
||||
const QString& groupPath = args.at(1);
|
||||
|
||||
QStringList pathParts = groupPath.split("/");
|
||||
QString groupName = pathParts.takeLast();
|
||||
QString parentGroupPath = pathParts.join("/");
|
||||
|
||||
Group* group = database->rootGroup()->findGroupByPath(groupPath);
|
||||
if (group) {
|
||||
errorTextStream << QObject::tr("Group %1 already exists!").arg(groupPath) << endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
Group* parentGroup = database->rootGroup()->findGroupByPath(parentGroupPath);
|
||||
if (!parentGroup) {
|
||||
errorTextStream << QObject::tr("Group %1 not found.").arg(parentGroupPath) << endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
Group* newGroup = new Group();
|
||||
newGroup->setUuid(QUuid::createUuid());
|
||||
newGroup->setName(groupName);
|
||||
newGroup->setParent(parentGroup);
|
||||
|
||||
QString errorMessage;
|
||||
if (!database->save(databasePath, &errorMessage, true, false)) {
|
||||
errorTextStream << QObject::tr("Writing the database failed %1.").arg(errorMessage) << endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (!parser->isSet(Command::QuietOption)) {
|
||||
outputTextStream << QObject::tr("Successfully added group %1.").arg(groupName) << endl;
|
||||
}
|
||||
return EXIT_SUCCESS;
|
||||
}
|
32
src/cli/AddGroup.h
Normal file
32
src/cli/AddGroup.h
Normal file
@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright (C) 2019 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_ADDGROUP_H
|
||||
#define KEEPASSXC_ADDGROUP_H
|
||||
|
||||
#include "DatabaseCommand.h"
|
||||
|
||||
class AddGroup : public DatabaseCommand
|
||||
{
|
||||
public:
|
||||
AddGroup();
|
||||
~AddGroup();
|
||||
|
||||
int executeWithDatabase(QSharedPointer<Database> db, QSharedPointer<QCommandLineParser> parser);
|
||||
};
|
||||
|
||||
#endif // KEEPASSXC_ADDGROUP_H
|
@ -15,6 +15,7 @@
|
||||
|
||||
set(cli_SOURCES
|
||||
Add.cpp
|
||||
AddGroup.cpp
|
||||
Analyze.cpp
|
||||
Clip.cpp
|
||||
Create.cpp
|
||||
@ -28,7 +29,9 @@ set(cli_SOURCES
|
||||
List.cpp
|
||||
Locate.cpp
|
||||
Merge.cpp
|
||||
Move.cpp
|
||||
Remove.cpp
|
||||
RemoveGroup.cpp
|
||||
Show.cpp)
|
||||
|
||||
add_library(cli STATIC ${cli_SOURCES})
|
||||
|
@ -23,6 +23,7 @@
|
||||
#include "Command.h"
|
||||
|
||||
#include "Add.h"
|
||||
#include "AddGroup.h"
|
||||
#include "Analyze.h"
|
||||
#include "Clip.h"
|
||||
#include "Create.h"
|
||||
@ -34,7 +35,9 @@
|
||||
#include "List.h"
|
||||
#include "Locate.h"
|
||||
#include "Merge.h"
|
||||
#include "Move.h"
|
||||
#include "Remove.h"
|
||||
#include "RemoveGroup.h"
|
||||
#include "Show.h"
|
||||
#include "TextStream.h"
|
||||
#include "Utils.h"
|
||||
@ -125,7 +128,10 @@ void populateCommands()
|
||||
commands.insert(QString("locate"), new Locate());
|
||||
commands.insert(QString("ls"), new List());
|
||||
commands.insert(QString("merge"), new Merge());
|
||||
commands.insert(QString("mkdir"), new AddGroup());
|
||||
commands.insert(QString("mv"), new Move());
|
||||
commands.insert(QString("rm"), new Remove());
|
||||
commands.insert(QString("rmdir"), new RemoveGroup());
|
||||
commands.insert(QString("show"), new Show());
|
||||
}
|
||||
}
|
||||
|
81
src/cli/Move.cpp
Normal file
81
src/cli/Move.cpp
Normal file
@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Copyright (C) 2019 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 "Move.h"
|
||||
|
||||
#include "cli/TextStream.h"
|
||||
#include "cli/Utils.h"
|
||||
#include "core/Database.h"
|
||||
#include "core/Entry.h"
|
||||
#include "core/Group.h"
|
||||
|
||||
Move::Move()
|
||||
{
|
||||
name = QString("mv");
|
||||
description = QObject::tr("Moves an entry to a new group.");
|
||||
positionalArguments.append({QString("entry"), QObject::tr("Path of the entry to move."), QString("")});
|
||||
positionalArguments.append({QString("group"), QObject::tr("Path of the destination group."), QString("")});
|
||||
}
|
||||
|
||||
Move::~Move()
|
||||
{
|
||||
}
|
||||
|
||||
int Move::executeWithDatabase(QSharedPointer<Database> database, QSharedPointer<QCommandLineParser> parser)
|
||||
{
|
||||
TextStream outputTextStream(Utils::STDOUT, QIODevice::WriteOnly);
|
||||
TextStream errorTextStream(Utils::STDERR, QIODevice::WriteOnly);
|
||||
|
||||
const QStringList args = parser->positionalArguments();
|
||||
const QString& databasePath = args.at(0);
|
||||
const QString& entryPath = args.at(1);
|
||||
const QString& destinationPath = args.at(2);
|
||||
|
||||
Entry* entry = database->rootGroup()->findEntryByPath(entryPath);
|
||||
if (!entry) {
|
||||
errorTextStream << QObject::tr("Could not find entry with path %1.").arg(entryPath) << endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
Group* destinationGroup = database->rootGroup()->findGroupByPath(destinationPath);
|
||||
if (!destinationGroup) {
|
||||
errorTextStream << QObject::tr("Could not find group with path %1.").arg(destinationPath) << endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (destinationGroup == entry->parent()) {
|
||||
errorTextStream << QObject::tr("Entry is already in group %1.").arg(destinationPath) << endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
entry->beginUpdate();
|
||||
entry->setGroup(destinationGroup);
|
||||
entry->endUpdate();
|
||||
|
||||
QString errorMessage;
|
||||
if (!database->save(databasePath, &errorMessage, true, false)) {
|
||||
errorTextStream << QObject::tr("Writing the database failed %1.").arg(errorMessage) << endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
outputTextStream << QObject::tr("Successfully moved entry %1 to group %2.").arg(entry->title(), destinationPath)
|
||||
<< endl;
|
||||
return EXIT_SUCCESS;
|
||||
}
|
32
src/cli/Move.h
Normal file
32
src/cli/Move.h
Normal file
@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright (C) 2019 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_MOVE_H
|
||||
#define KEEPASSXC_MOVE_H
|
||||
|
||||
#include "DatabaseCommand.h"
|
||||
|
||||
class Move : public DatabaseCommand
|
||||
{
|
||||
public:
|
||||
Move();
|
||||
~Move();
|
||||
|
||||
int executeWithDatabase(QSharedPointer<Database> db, QSharedPointer<QCommandLineParser> parser);
|
||||
};
|
||||
|
||||
#endif // KEEPASSXC_MOVE_H
|
85
src/cli/RemoveGroup.cpp
Normal file
85
src/cli/RemoveGroup.cpp
Normal file
@ -0,0 +1,85 @@
|
||||
/*
|
||||
* Copyright (C) 2019 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 "RemoveGroup.h"
|
||||
|
||||
#include "cli/TextStream.h"
|
||||
#include "cli/Utils.h"
|
||||
#include "core/Database.h"
|
||||
#include "core/Entry.h"
|
||||
#include "core/Group.h"
|
||||
#include "core/Metadata.h"
|
||||
#include "core/Tools.h"
|
||||
|
||||
RemoveGroup::RemoveGroup()
|
||||
{
|
||||
name = QString("rmdir");
|
||||
description = QString("Removes a group from a database.");
|
||||
positionalArguments.append({QString("group"), QObject::tr("Path of the group to remove."), QString("")});
|
||||
}
|
||||
|
||||
RemoveGroup::~RemoveGroup()
|
||||
{
|
||||
}
|
||||
|
||||
int RemoveGroup::executeWithDatabase(QSharedPointer<Database> database, QSharedPointer<QCommandLineParser> parser)
|
||||
{
|
||||
bool quiet = parser->isSet(Command::QuietOption);
|
||||
QString databasePath = parser->positionalArguments().at(0);
|
||||
QString groupPath = parser->positionalArguments().at(1);
|
||||
|
||||
TextStream outputTextStream(quiet ? Utils::DEVNULL : Utils::STDOUT, QIODevice::WriteOnly);
|
||||
TextStream errorTextStream(Utils::STDERR, QIODevice::WriteOnly);
|
||||
|
||||
// Recursive option means were looking for a group to remove.
|
||||
QPointer<Group> group = database->rootGroup()->findGroupByPath(groupPath);
|
||||
if (!group) {
|
||||
errorTextStream << QObject::tr("Group %1 not found.").arg(groupPath) << endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (group == database->rootGroup()) {
|
||||
errorTextStream << QObject::tr("Cannot remove root group from database.") << endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
bool recycled = true;
|
||||
auto* recycleBin = database->metadata()->recycleBin();
|
||||
if (!database->metadata()->recycleBinEnabled() || (recycleBin && recycleBin->findGroupByUuid(group->uuid()))) {
|
||||
delete group;
|
||||
recycled = false;
|
||||
} else {
|
||||
database->recycleGroup(group);
|
||||
};
|
||||
|
||||
QString errorMessage;
|
||||
if (!database->save(databasePath, &errorMessage, true, false)) {
|
||||
errorTextStream << QObject::tr("Unable to save database to file: %1").arg(errorMessage) << endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (recycled) {
|
||||
outputTextStream << QObject::tr("Successfully recycled group %1.").arg(groupPath) << endl;
|
||||
} else {
|
||||
outputTextStream << QObject::tr("Successfully deleted group %1.").arg(groupPath) << endl;
|
||||
}
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
32
src/cli/RemoveGroup.h
Normal file
32
src/cli/RemoveGroup.h
Normal file
@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright (C) 2017 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_REMOVEGROUP_H
|
||||
#define KEEPASSXC_REMOVEGROUP_H
|
||||
|
||||
#include "DatabaseCommand.h"
|
||||
|
||||
class RemoveGroup : public DatabaseCommand
|
||||
{
|
||||
public:
|
||||
RemoveGroup();
|
||||
~RemoveGroup();
|
||||
|
||||
int executeWithDatabase(QSharedPointer<Database> db, QSharedPointer<QCommandLineParser> parser);
|
||||
};
|
||||
|
||||
#endif // KEEPASSXC_REMOVEGROUP_H
|
@ -51,9 +51,18 @@ Lists the contents of a group in a database. If no group is specified, it will d
|
||||
.IP "merge [options] <database1> <database2>"
|
||||
Merges two databases together. The first database file is going to be replaced by the result of the merge, for that reason it is advisable to keep a backup of the two database files before attempting a merge. In the case that both databases make use of the same credentials, the \fI--same-credentials\fP or \fI-s\fP option can be used.
|
||||
|
||||
.IP "mkdir [options] <database> <group>"
|
||||
Adds a new group to a database.
|
||||
|
||||
.IP "mv [options] <database> <entry> <group>"
|
||||
Moves an entry to a new group.
|
||||
|
||||
.IP "rm [options] <database> <entry>"
|
||||
Removes an entry from a database. If the database has a recycle bin, the entry will be moved there. If the entry is already in the recycle bin, it will be removed permanently.
|
||||
|
||||
.IP "rmdir [options] <database> <group>"
|
||||
Removes a group from a database. If the database has a recycle bin, the group will be moved there. If the group is already in the recycle bin, it will be removed permanently.
|
||||
|
||||
.IP "show [options] <database> <entry>"
|
||||
Shows the title, username, password, URL and notes of a database entry. Can also show the current TOTP. Regarding the occurrence of multiple entries with the same name in different groups, everything stated in the \fIclip\fP command section also applies here.
|
||||
|
||||
|
@ -32,6 +32,7 @@
|
||||
#include "format/KeePass2.h"
|
||||
|
||||
#include "cli/Add.h"
|
||||
#include "cli/AddGroup.h"
|
||||
#include "cli/Analyze.h"
|
||||
#include "cli/Clip.h"
|
||||
#include "cli/Command.h"
|
||||
@ -44,7 +45,9 @@
|
||||
#include "cli/List.h"
|
||||
#include "cli/Locate.h"
|
||||
#include "cli/Merge.h"
|
||||
#include "cli/Move.h"
|
||||
#include "cli/Remove.h"
|
||||
#include "cli/RemoveGroup.h"
|
||||
#include "cli/Show.h"
|
||||
#include "cli/Utils.h"
|
||||
|
||||
@ -172,7 +175,7 @@ QSharedPointer<Database> TestCli::readTestDatabase() const
|
||||
|
||||
void TestCli::testCommand()
|
||||
{
|
||||
QCOMPARE(Command::getCommands().size(), 14);
|
||||
QCOMPARE(Command::getCommands().size(), 17);
|
||||
QVERIFY(Command::getCommand("add"));
|
||||
QVERIFY(Command::getCommand("analyze"));
|
||||
QVERIFY(Command::getCommand("clip"));
|
||||
@ -295,20 +298,73 @@ void TestCli::testAdd()
|
||||
QVERIFY(!defaultPasswordClassesRegex.match(entry->password()).hasMatch());
|
||||
}
|
||||
|
||||
void TestCli::testAnalyze()
|
||||
void TestCli::testAddGroup()
|
||||
{
|
||||
Analyze analyzeCmd;
|
||||
QVERIFY(!analyzeCmd.name.isEmpty());
|
||||
QVERIFY(analyzeCmd.getDescriptionLine().contains(analyzeCmd.name));
|
||||
|
||||
const QString hibpPath = QString(KEEPASSX_TEST_DATA_DIR).append("/hibp.txt");
|
||||
AddGroup addGroupCmd;
|
||||
QVERIFY(!addGroupCmd.name.isEmpty());
|
||||
QVERIFY(addGroupCmd.getDescriptionLine().contains(addGroupCmd.name));
|
||||
|
||||
Utils::Test::setNextPassword("a");
|
||||
analyzeCmd.execute({"analyze", "--hibp", hibpPath, m_dbFile->fileName()});
|
||||
addGroupCmd.execute({"mkdir", m_dbFile->fileName(), "/new_group"});
|
||||
m_stderrFile->reset();
|
||||
m_stdoutFile->reset();
|
||||
m_stdoutFile->readLine(); // skip password prompt
|
||||
auto output = m_stdoutFile->readAll();
|
||||
QVERIFY(output.contains("Sample Entry") && output.contains("123"));
|
||||
QCOMPARE(m_stderrFile->readAll(), QByteArray(""));
|
||||
QCOMPARE(m_stdoutFile->readAll(), QByteArray("Successfully added group new_group.\n"));
|
||||
|
||||
auto db = readTestDatabase();
|
||||
auto* group = db->rootGroup()->findGroupByPath("new_group");
|
||||
QVERIFY(group);
|
||||
QCOMPARE(group->name(), QString("new_group"));
|
||||
|
||||
// Trying to add the same group should fail.
|
||||
qint64 pos = m_stdoutFile->pos();
|
||||
qint64 posErr = m_stderrFile->pos();
|
||||
Utils::Test::setNextPassword("a");
|
||||
addGroupCmd.execute({"mkdir", m_dbFile->fileName(), "/new_group"});
|
||||
m_stdoutFile->seek(pos);
|
||||
m_stdoutFile->readLine(); // skip password prompt
|
||||
m_stderrFile->seek(posErr);
|
||||
QCOMPARE(m_stderrFile->readAll(), QByteArray("Group /new_group already exists!\n"));
|
||||
QCOMPARE(m_stdoutFile->readAll(), QByteArray(""));
|
||||
|
||||
// Should be able to add groups down the tree.
|
||||
pos = m_stdoutFile->pos();
|
||||
posErr = m_stderrFile->pos();
|
||||
Utils::Test::setNextPassword("a");
|
||||
addGroupCmd.execute({"mkdir", m_dbFile->fileName(), "/new_group/newer_group"});
|
||||
m_stdoutFile->seek(pos);
|
||||
m_stdoutFile->readLine(); // skip password prompt
|
||||
m_stderrFile->seek(posErr);
|
||||
QCOMPARE(m_stderrFile->readAll(), QByteArray(""));
|
||||
QCOMPARE(m_stdoutFile->readAll(), QByteArray("Successfully added group newer_group.\n"));
|
||||
|
||||
db = readTestDatabase();
|
||||
group = db->rootGroup()->findGroupByPath("new_group/newer_group");
|
||||
QVERIFY(group);
|
||||
QCOMPARE(group->name(), QString("newer_group"));
|
||||
|
||||
// Should fail if the path is invalid.
|
||||
pos = m_stdoutFile->pos();
|
||||
posErr = m_stderrFile->pos();
|
||||
Utils::Test::setNextPassword("a");
|
||||
addGroupCmd.execute({"mkdir", m_dbFile->fileName(), "/invalid_group/newer_group"});
|
||||
m_stdoutFile->seek(pos);
|
||||
m_stdoutFile->readLine(); // skip password prompt
|
||||
m_stderrFile->seek(posErr);
|
||||
QCOMPARE(m_stderrFile->readAll(), QByteArray("Group /invalid_group not found.\n"));
|
||||
QCOMPARE(m_stdoutFile->readAll(), QByteArray(""));
|
||||
|
||||
// Should fail to add the root group.
|
||||
pos = m_stdoutFile->pos();
|
||||
posErr = m_stderrFile->pos();
|
||||
Utils::Test::setNextPassword("a");
|
||||
addGroupCmd.execute({"mkdir", m_dbFile->fileName(), "/"});
|
||||
m_stdoutFile->seek(pos);
|
||||
m_stdoutFile->readLine(); // skip password prompt
|
||||
m_stderrFile->seek(posErr);
|
||||
QCOMPARE(m_stderrFile->readAll(), QByteArray("Group / already exists!\n"));
|
||||
QCOMPARE(m_stdoutFile->readAll(), QByteArray(""));
|
||||
}
|
||||
|
||||
bool isTOTP(const QString& value)
|
||||
@ -325,6 +381,22 @@ bool isTOTP(const QString& value)
|
||||
return true;
|
||||
}
|
||||
|
||||
void TestCli::testAnalyze()
|
||||
{
|
||||
Analyze analyzeCmd;
|
||||
QVERIFY(!analyzeCmd.name.isEmpty());
|
||||
QVERIFY(analyzeCmd.getDescriptionLine().contains(analyzeCmd.name));
|
||||
|
||||
const QString hibpPath = QString(KEEPASSX_TEST_DATA_DIR).append("/hibp.txt");
|
||||
|
||||
Utils::Test::setNextPassword("a");
|
||||
analyzeCmd.execute({"analyze", "--hibp", hibpPath, m_dbFile->fileName()});
|
||||
m_stdoutFile->reset();
|
||||
m_stdoutFile->readLine(); // skip password prompt
|
||||
auto output = m_stdoutFile->readAll();
|
||||
QVERIFY(output.contains("Sample Entry") && output.contains("123"));
|
||||
}
|
||||
|
||||
void TestCli::testClip()
|
||||
{
|
||||
QClipboard* clipboard = QGuiApplication::clipboard();
|
||||
@ -1245,6 +1317,63 @@ void TestCli::testMerge()
|
||||
QCOMPARE(m_stdoutFile->readAll(), QByteArray(""));
|
||||
}
|
||||
|
||||
void TestCli::testMove()
|
||||
{
|
||||
Move moveCmd;
|
||||
QVERIFY(!moveCmd.name.isEmpty());
|
||||
QVERIFY(moveCmd.getDescriptionLine().contains(moveCmd.name));
|
||||
|
||||
qint64 pos = m_stdoutFile->pos();
|
||||
qint64 posErr = m_stderrFile->pos();
|
||||
Utils::Test::setNextPassword("a");
|
||||
moveCmd.execute({"mv", m_dbFile->fileName(), "invalid_entry_path", "invalid_group_path"});
|
||||
m_stdoutFile->seek(pos);
|
||||
m_stderrFile->seek(posErr);
|
||||
m_stdoutFile->readLine(); // skip prompt line
|
||||
QCOMPARE(m_stdoutFile->readLine(), QByteArray(""));
|
||||
QCOMPARE(m_stderrFile->readLine(), QByteArray("Could not find entry with path invalid_entry_path.\n"));
|
||||
|
||||
pos = m_stdoutFile->pos();
|
||||
posErr = m_stderrFile->pos();
|
||||
Utils::Test::setNextPassword("a");
|
||||
moveCmd.execute({"mv", m_dbFile->fileName(), "Sample Entry", "invalid_group_path"});
|
||||
m_stdoutFile->seek(pos);
|
||||
m_stderrFile->seek(posErr);
|
||||
m_stdoutFile->readLine(); // skip prompt line
|
||||
QCOMPARE(m_stdoutFile->readLine(), QByteArray(""));
|
||||
QCOMPARE(m_stderrFile->readLine(), QByteArray("Could not find group with path invalid_group_path.\n"));
|
||||
|
||||
pos = m_stdoutFile->pos();
|
||||
posErr = m_stderrFile->pos();
|
||||
Utils::Test::setNextPassword("a");
|
||||
moveCmd.execute({"mv", m_dbFile->fileName(), "Sample Entry", "General/"});
|
||||
m_stdoutFile->seek(pos);
|
||||
m_stderrFile->seek(posErr);
|
||||
m_stdoutFile->readLine(); // skip prompt line
|
||||
QCOMPARE(m_stdoutFile->readLine(), QByteArray("Successfully moved entry Sample Entry to group General/.\n"));
|
||||
QCOMPARE(m_stderrFile->readLine(), QByteArray(""));
|
||||
|
||||
auto db = readTestDatabase();
|
||||
auto* entry = db->rootGroup()->findEntryByPath("General/Sample Entry");
|
||||
QVERIFY(entry);
|
||||
|
||||
// Test that not modified if the same group is destination.
|
||||
pos = m_stdoutFile->pos();
|
||||
posErr = m_stderrFile->pos();
|
||||
Utils::Test::setNextPassword("a");
|
||||
moveCmd.execute({"mv", m_dbFile->fileName(), "General/Sample Entry", "General/"});
|
||||
m_stdoutFile->seek(pos);
|
||||
m_stderrFile->seek(posErr);
|
||||
m_stdoutFile->readLine(); // skip prompt line
|
||||
QCOMPARE(m_stdoutFile->readLine(), QByteArray(""));
|
||||
QCOMPARE(m_stderrFile->readLine(), QByteArray("Entry is already in group General/.\n"));
|
||||
|
||||
// sanity check
|
||||
db = readTestDatabase();
|
||||
entry = db->rootGroup()->findEntryByPath("General/Sample Entry");
|
||||
QVERIFY(entry);
|
||||
}
|
||||
|
||||
void TestCli::testRemove()
|
||||
{
|
||||
Remove removeCmd;
|
||||
@ -1264,6 +1393,7 @@ void TestCli::testRemove()
|
||||
fileCopy.close();
|
||||
|
||||
qint64 pos = m_stdoutFile->pos();
|
||||
qint64 posErr = m_stderrFile->pos();
|
||||
|
||||
// delete entry and verify
|
||||
Utils::Test::setNextPassword("a");
|
||||
@ -1271,6 +1401,7 @@ void TestCli::testRemove()
|
||||
m_stdoutFile->seek(pos);
|
||||
m_stdoutFile->readLine(); // skip password prompt
|
||||
QCOMPARE(m_stdoutFile->readAll(), QByteArray("Successfully recycled entry Sample Entry.\n"));
|
||||
QCOMPARE(m_stderrFile->readAll(), QByteArray(""));
|
||||
|
||||
auto key = QSharedPointer<CompositeKey>::create();
|
||||
key->addKey(QSharedPointer<PasswordKey>::create("a"));
|
||||
@ -1283,6 +1414,7 @@ void TestCli::testRemove()
|
||||
QVERIFY(!readBackDb->rootGroup()->findEntryByPath("/Sample Entry"));
|
||||
QVERIFY(readBackDb->rootGroup()->findEntryByPath(QString("/%1/Sample Entry").arg(Group::tr("Recycle Bin"))));
|
||||
|
||||
pos = m_stdoutFile->pos();
|
||||
pos = m_stdoutFile->pos();
|
||||
|
||||
// try again, this time without recycle bin
|
||||
@ -1301,16 +1433,89 @@ void TestCli::testRemove()
|
||||
QVERIFY(!readBackDb->rootGroup()->findEntryByPath("/Sample Entry"));
|
||||
QVERIFY(!readBackDb->rootGroup()->findEntryByPath(QString("/%1/Sample Entry").arg(Group::tr("Recycle Bin"))));
|
||||
|
||||
pos = m_stdoutFile->pos();
|
||||
|
||||
// finally, try deleting a non-existent entry
|
||||
pos = m_stdoutFile->pos();
|
||||
posErr = m_stderrFile->pos();
|
||||
Utils::Test::setNextPassword("a");
|
||||
removeCmd.execute({"rm", fileCopy.fileName(), "/Sample Entry"});
|
||||
m_stdoutFile->seek(pos);
|
||||
m_stdoutFile->readLine(); // skip password prompt
|
||||
m_stderrFile->reset();
|
||||
m_stderrFile->seek(posErr);
|
||||
QCOMPARE(m_stdoutFile->readAll(), QByteArray(""));
|
||||
QCOMPARE(m_stderrFile->readAll(), QByteArray("Entry /Sample Entry not found.\n"));
|
||||
|
||||
// try deleting a directory, should fail
|
||||
pos = m_stdoutFile->pos();
|
||||
posErr = m_stderrFile->pos();
|
||||
Utils::Test::setNextPassword("a");
|
||||
removeCmd.execute({"rm", fileCopy.fileName(), "/General"});
|
||||
m_stdoutFile->seek(pos);
|
||||
m_stdoutFile->readLine(); // skip password prompt
|
||||
m_stderrFile->seek(posErr);
|
||||
QCOMPARE(m_stdoutFile->readAll(), QByteArray(""));
|
||||
QCOMPARE(m_stderrFile->readAll(), QByteArray("Entry /General not found.\n"));
|
||||
}
|
||||
|
||||
void TestCli::testRemoveGroup()
|
||||
{
|
||||
RemoveGroup removeGroupCmd;
|
||||
QVERIFY(!removeGroupCmd.name.isEmpty());
|
||||
QVERIFY(removeGroupCmd.getDescriptionLine().contains(removeGroupCmd.name));
|
||||
|
||||
Kdbx3Reader reader;
|
||||
Kdbx3Writer writer;
|
||||
|
||||
// try deleting a directory, should recycle it first.
|
||||
qint64 pos = m_stdoutFile->pos();
|
||||
qint64 posErr = m_stderrFile->pos();
|
||||
Utils::Test::setNextPassword("a");
|
||||
removeGroupCmd.execute({"rmdir", m_dbFile->fileName(), "/General"});
|
||||
m_stdoutFile->seek(pos);
|
||||
m_stdoutFile->readLine(); // skip password prompt
|
||||
m_stderrFile->seek(posErr);
|
||||
QCOMPARE(m_stderrFile->readAll(), QByteArray(""));
|
||||
QCOMPARE(m_stdoutFile->readAll(), QByteArray("Successfully recycled group /General.\n"));
|
||||
|
||||
auto db = readTestDatabase();
|
||||
auto* group = db->rootGroup()->findGroupByPath("General");
|
||||
QVERIFY(!group);
|
||||
|
||||
// try deleting a directory again, should delete it permanently.
|
||||
pos = m_stdoutFile->pos();
|
||||
posErr = m_stderrFile->pos();
|
||||
Utils::Test::setNextPassword("a");
|
||||
removeGroupCmd.execute({"rmdir", m_dbFile->fileName(), "Recycle Bin/General"});
|
||||
m_stdoutFile->seek(pos);
|
||||
m_stdoutFile->readLine(); // skip password prompt
|
||||
m_stderrFile->seek(posErr);
|
||||
QCOMPARE(m_stdoutFile->readAll(), QByteArray("Successfully deleted group Recycle Bin/General.\n"));
|
||||
QCOMPARE(m_stderrFile->readAll(), QByteArray(""));
|
||||
|
||||
db = readTestDatabase();
|
||||
group = db->rootGroup()->findGroupByPath("Recycle Bin/General");
|
||||
QVERIFY(!group);
|
||||
|
||||
// try deleting an invalid group, should fail.
|
||||
pos = m_stdoutFile->pos();
|
||||
posErr = m_stderrFile->pos();
|
||||
Utils::Test::setNextPassword("a");
|
||||
removeGroupCmd.execute({"rmdir", m_dbFile->fileName(), "invalid"});
|
||||
m_stdoutFile->seek(pos);
|
||||
m_stdoutFile->readLine(); // skip password prompt
|
||||
m_stderrFile->seek(posErr);
|
||||
QCOMPARE(m_stderrFile->readAll(), QByteArray("Group invalid not found.\n"));
|
||||
QCOMPARE(m_stdoutFile->readAll(), QByteArray(""));
|
||||
|
||||
// Should fail to remove the root group.
|
||||
pos = m_stdoutFile->pos();
|
||||
posErr = m_stderrFile->pos();
|
||||
Utils::Test::setNextPassword("a");
|
||||
removeGroupCmd.execute({"rmdir", m_dbFile->fileName(), "/"});
|
||||
m_stdoutFile->seek(pos);
|
||||
m_stdoutFile->readLine(); // skip password prompt
|
||||
m_stderrFile->seek(posErr);
|
||||
QCOMPARE(m_stderrFile->readAll(), QByteArray("Cannot remove root group from database.\n"));
|
||||
QCOMPARE(m_stdoutFile->readAll(), QByteArray(""));
|
||||
}
|
||||
|
||||
void TestCli::testRemoveQuiet()
|
||||
|
@ -45,6 +45,7 @@ private slots:
|
||||
|
||||
void testCommand();
|
||||
void testAdd();
|
||||
void testAddGroup();
|
||||
void testAnalyze();
|
||||
void testClip();
|
||||
void testCreate();
|
||||
@ -60,7 +61,9 @@ private slots:
|
||||
void testList();
|
||||
void testLocate();
|
||||
void testMerge();
|
||||
void testMove();
|
||||
void testRemove();
|
||||
void testRemoveGroup();
|
||||
void testRemoveQuiet();
|
||||
void testShow();
|
||||
void testInvalidDbFiles();
|
||||
|
Loading…
x
Reference in New Issue
Block a user