mirror of
				https://github.com/keepassxreboot/keepassxc.git
				synced 2025-11-03 15:18:08 -05:00 
			
		
		
		
	[CLI] Add a db-edit command (#8400)
This commit is contained in:
		
							parent
							
								
									b1e7c34b82
								
							
						
					
					
						commit
						db98f114f9
					
				
					 16 changed files with 529 additions and 52 deletions
				
			
		
							
								
								
									
										6
									
								
								.github/CONTRIBUTING.md
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								.github/CONTRIBUTING.md
									
										
									
									
										vendored
									
									
								
							| 
						 | 
				
			
			@ -85,6 +85,12 @@ All pull requests must comply with the above requirements and with the [stylegui
 | 
			
		|||
Translations are managed on [Transifex](https://www.transifex.com/keepassxc/keepassxc/) which offers a web interface.
 | 
			
		||||
Please join an existing language team or request a new one if there is none.
 | 
			
		||||
 | 
			
		||||
If you open a Pull Request with new strings that require translations, you will need to run the following:
 | 
			
		||||
```
 | 
			
		||||
./release-tool i18n lupdate
 | 
			
		||||
```
 | 
			
		||||
This will make the new strings available for translation in Transifex.
 | 
			
		||||
 | 
			
		||||
## Styleguides
 | 
			
		||||
 | 
			
		||||
### Git branch strategy
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -66,6 +66,11 @@ It provides the ability to query and modify the entries of a KeePass database, d
 | 
			
		|||
  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.
 | 
			
		||||
 | 
			
		||||
*db-edit* [_options_] <__database__>::
 | 
			
		||||
  Edits a database.
 | 
			
		||||
  When setting a key file, the key file will be created if the file that is referred to
 | 
			
		||||
  does not exist.
 | 
			
		||||
 | 
			
		||||
*db-info* [_options_] <__database__>::
 | 
			
		||||
  Show a database's information.
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -154,7 +159,7 @@ It provides the ability to query and modify the entries of a KeePass database, d
 | 
			
		|||
*--no-password*::
 | 
			
		||||
  Deactivates the password key for the database.
 | 
			
		||||
 | 
			
		||||
*-y*, *--yubikey* <__slot__>::
 | 
			
		||||
*-y*, *--yubikey* <__slot[:serial]__>::
 | 
			
		||||
  Specifies a yubikey slot for unlocking the database.
 | 
			
		||||
  In a merge operation this option is used to specify the YubiKey slot for the first database.
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -177,7 +182,7 @@ It provides the ability to query and modify the entries of a KeePass database, d
 | 
			
		|||
*--no-password-from*::
 | 
			
		||||
  Deactivates password key for the database to merge from.
 | 
			
		||||
 | 
			
		||||
*--yubikey-from* <__slot__>::
 | 
			
		||||
*--yubikey-from* <__slot[:serial]__>::
 | 
			
		||||
  YubiKey slot for the second database.
 | 
			
		||||
 | 
			
		||||
*-s*, *--same-credentials*::
 | 
			
		||||
| 
						 | 
				
			
			@ -235,16 +240,24 @@ The same password generation options as documented for the generate command can
 | 
			
		|||
  If a unique matching entry is found it will be copied to the clipboard.
 | 
			
		||||
  If multiple entries are found they will be listed to refine the search. (no clip performed)
 | 
			
		||||
 | 
			
		||||
=== Create and Import options
 | 
			
		||||
*-k*, *--set-key-file* <__path__>::
 | 
			
		||||
=== Db-create, Db-edit and Import options
 | 
			
		||||
*--set-key-file* <__path__>::
 | 
			
		||||
  Set the key file for the database.
 | 
			
		||||
 | 
			
		||||
*-p*, *--set-password*::
 | 
			
		||||
  Set a password for the database.
 | 
			
		||||
 | 
			
		||||
=== Db-create, Import options
 | 
			
		||||
*-t*, *--decryption-time* <__time__>::
 | 
			
		||||
  Target decryption time in MS for the database.
 | 
			
		||||
 | 
			
		||||
=== Db-edit options
 | 
			
		||||
*--unset-password* <__path__>::
 | 
			
		||||
  Removes the password for the database.
 | 
			
		||||
 | 
			
		||||
*--unset-key-file* <__path__>::
 | 
			
		||||
  Removes the key file for the database.
 | 
			
		||||
 | 
			
		||||
=== Show options
 | 
			
		||||
*-a*, *--attributes* <__attribute__>...::
 | 
			
		||||
  Shows the named attributes.
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -7871,6 +7871,59 @@ Kernel: %3 %4</source>
 | 
			
		|||
        <source>Show all the attributes of the entry.</source>
 | 
			
		||||
        <translation type="unfinished"></translation>
 | 
			
		||||
    </message>
 | 
			
		||||
    <message>
 | 
			
		||||
        <source>Edit a database.</source>
 | 
			
		||||
        <translation type="unfinished"></translation>
 | 
			
		||||
    </message>
 | 
			
		||||
    <message>
 | 
			
		||||
        <source>Could not change the database key.</source>
 | 
			
		||||
        <translation type="unfinished"></translation>
 | 
			
		||||
    </message>
 | 
			
		||||
    <message>
 | 
			
		||||
        <source>Database was not modified.</source>
 | 
			
		||||
        <translation type="unfinished"></translation>
 | 
			
		||||
    </message>
 | 
			
		||||
    <message>
 | 
			
		||||
        <source>Successfully edited the database.</source>
 | 
			
		||||
        <translation type="unfinished"></translation>
 | 
			
		||||
    </message>
 | 
			
		||||
    <message>
 | 
			
		||||
        <source>Loading the new key file failed: %1</source>
 | 
			
		||||
        <translation type="unfinished"></translation>
 | 
			
		||||
    </message>
 | 
			
		||||
    <message>
 | 
			
		||||
        <source>Unset the password for the database.</source>
 | 
			
		||||
        <translation type="unfinished"></translation>
 | 
			
		||||
    </message>
 | 
			
		||||
    <message>
 | 
			
		||||
        <source>Unset the key file for the database.</source>
 | 
			
		||||
        <translation type="unfinished"></translation>
 | 
			
		||||
    </message>
 | 
			
		||||
    <message>
 | 
			
		||||
        <source>Cannot use %1 and %2 at the same time.</source>
 | 
			
		||||
        <translation type="unfinished"></translation>
 | 
			
		||||
    </message>
 | 
			
		||||
    <message>
 | 
			
		||||
        <source>Cannot remove all the keys from a database.</source>
 | 
			
		||||
        <translation type="unfinished"></translation>
 | 
			
		||||
    </message>
 | 
			
		||||
    <message>
 | 
			
		||||
        <source>Cannot remove password: The database does not have a password.</source>
 | 
			
		||||
        <translation type="unfinished"></translation>
 | 
			
		||||
    </message>
 | 
			
		||||
    <message>
 | 
			
		||||
        <source>Cannot remove file key: The database does not have a file key.</source>
 | 
			
		||||
        <translation type="unfinished"></translation>
 | 
			
		||||
    </message>
 | 
			
		||||
    <message>
 | 
			
		||||
        <source>Found unexpected Key type %1</source>
 | 
			
		||||
        <translation type="unfinished"></translation>
 | 
			
		||||
    </message>
 | 
			
		||||
    <message>
 | 
			
		||||
        <source>Set the key file for the database.
 | 
			
		||||
This options is deprecated, use --set-key-file instead.</source>
 | 
			
		||||
        <translation type="unfinished"></translation>
 | 
			
		||||
    </message>
 | 
			
		||||
</context>
 | 
			
		||||
<context>
 | 
			
		||||
    <name>QtIOCompressor</name>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -22,9 +22,11 @@ set(cli_SOURCES
 | 
			
		|||
        AttachmentRemove.cpp
 | 
			
		||||
        Clip.cpp
 | 
			
		||||
        Close.cpp
 | 
			
		||||
        Create.cpp
 | 
			
		||||
        Command.cpp
 | 
			
		||||
        DatabaseCommand.cpp
 | 
			
		||||
        DatabaseCreate.cpp
 | 
			
		||||
        DatabaseEdit.cpp
 | 
			
		||||
        DatabaseInfo.cpp
 | 
			
		||||
        Diceware.cpp
 | 
			
		||||
        Edit.cpp
 | 
			
		||||
        Estimate.cpp
 | 
			
		||||
| 
						 | 
				
			
			@ -33,7 +35,6 @@ set(cli_SOURCES
 | 
			
		|||
        Generate.cpp
 | 
			
		||||
        Help.cpp
 | 
			
		||||
        Import.cpp
 | 
			
		||||
        Info.cpp
 | 
			
		||||
        List.cpp
 | 
			
		||||
        Merge.cpp
 | 
			
		||||
        Move.cpp
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -23,7 +23,9 @@
 | 
			
		|||
#include "AttachmentRemove.h"
 | 
			
		||||
#include "Clip.h"
 | 
			
		||||
#include "Close.h"
 | 
			
		||||
#include "Create.h"
 | 
			
		||||
#include "DatabaseCreate.h"
 | 
			
		||||
#include "DatabaseEdit.h"
 | 
			
		||||
#include "DatabaseInfo.h"
 | 
			
		||||
#include "Diceware.h"
 | 
			
		||||
#include "Edit.h"
 | 
			
		||||
#include "Estimate.h"
 | 
			
		||||
| 
						 | 
				
			
			@ -32,7 +34,6 @@
 | 
			
		|||
#include "Generate.h"
 | 
			
		||||
#include "Help.h"
 | 
			
		||||
#include "Import.h"
 | 
			
		||||
#include "Info.h"
 | 
			
		||||
#include "List.h"
 | 
			
		||||
#include "Merge.h"
 | 
			
		||||
#include "Move.h"
 | 
			
		||||
| 
						 | 
				
			
			@ -172,8 +173,9 @@ namespace Commands
 | 
			
		|||
        s_commands.insert(QStringLiteral("attachment-rm"), QSharedPointer<Command>(new AttachmentRemove()));
 | 
			
		||||
        s_commands.insert(QStringLiteral("clip"), QSharedPointer<Command>(new Clip()));
 | 
			
		||||
        s_commands.insert(QStringLiteral("close"), QSharedPointer<Command>(new Close()));
 | 
			
		||||
        s_commands.insert(QStringLiteral("db-create"), QSharedPointer<Command>(new Create()));
 | 
			
		||||
        s_commands.insert(QStringLiteral("db-info"), QSharedPointer<Command>(new Info()));
 | 
			
		||||
        s_commands.insert(QStringLiteral("db-create"), QSharedPointer<Command>(new DatabaseCreate()));
 | 
			
		||||
        s_commands.insert(QStringLiteral("db-edit"), QSharedPointer<Command>(new DatabaseEdit()));
 | 
			
		||||
        s_commands.insert(QStringLiteral("db-info"), QSharedPointer<Command>(new DatabaseInfo()));
 | 
			
		||||
        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()));
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -15,7 +15,7 @@
 | 
			
		|||
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "Create.h"
 | 
			
		||||
#include "DatabaseCreate.h"
 | 
			
		||||
 | 
			
		||||
#include "Utils.h"
 | 
			
		||||
#include "keys/FileKey.h"
 | 
			
		||||
| 
						 | 
				
			
			@ -23,34 +23,39 @@
 | 
			
		|||
#include <QCommandLineParser>
 | 
			
		||||
#include <QFileInfo>
 | 
			
		||||
 | 
			
		||||
const QCommandLineOption Create::DecryptionTimeOption =
 | 
			
		||||
const QCommandLineOption DatabaseCreate::DecryptionTimeOption =
 | 
			
		||||
    QCommandLineOption(QStringList() << "t"
 | 
			
		||||
                                     << "decryption-time",
 | 
			
		||||
                       QObject::tr("Target decryption time in MS for the database."),
 | 
			
		||||
                       QObject::tr("time"));
 | 
			
		||||
 | 
			
		||||
const QCommandLineOption Create::SetKeyFileOption =
 | 
			
		||||
    QCommandLineOption(QStringList() << "k"
 | 
			
		||||
                                     << "set-key-file",
 | 
			
		||||
const QCommandLineOption DatabaseCreate::SetKeyFileShortOption = QCommandLineOption(
 | 
			
		||||
    QStringList() << "k",
 | 
			
		||||
    QObject::tr("Set the key file for the database.\nThis options is deprecated, use --set-key-file instead."),
 | 
			
		||||
    QObject::tr("path"));
 | 
			
		||||
 | 
			
		||||
const QCommandLineOption DatabaseCreate::SetKeyFileOption =
 | 
			
		||||
    QCommandLineOption(QStringList() << "set-key-file",
 | 
			
		||||
                       QObject::tr("Set the key file for the database."),
 | 
			
		||||
                       QObject::tr("path"));
 | 
			
		||||
 | 
			
		||||
const QCommandLineOption Create::SetPasswordOption =
 | 
			
		||||
const QCommandLineOption DatabaseCreate::SetPasswordOption =
 | 
			
		||||
    QCommandLineOption(QStringList() << "p"
 | 
			
		||||
                                     << "set-password",
 | 
			
		||||
                       QObject::tr("Set a password for the database."));
 | 
			
		||||
 | 
			
		||||
Create::Create()
 | 
			
		||||
DatabaseCreate::DatabaseCreate()
 | 
			
		||||
{
 | 
			
		||||
    name = QString("db-create");
 | 
			
		||||
    description = QObject::tr("Create a new database.");
 | 
			
		||||
    positionalArguments.append({QString("database"), QObject::tr("Path of the database."), QString("")});
 | 
			
		||||
    options.append(Create::SetKeyFileOption);
 | 
			
		||||
    options.append(Create::SetPasswordOption);
 | 
			
		||||
    options.append(Create::DecryptionTimeOption);
 | 
			
		||||
    options.append(DatabaseCreate::SetKeyFileOption);
 | 
			
		||||
    options.append(DatabaseCreate::SetKeyFileShortOption);
 | 
			
		||||
    options.append(DatabaseCreate::SetPasswordOption);
 | 
			
		||||
    options.append(DatabaseCreate::DecryptionTimeOption);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
QSharedPointer<Database> Create::initializeDatabaseFromOptions(const QSharedPointer<QCommandLineParser>& parser)
 | 
			
		||||
QSharedPointer<Database> DatabaseCreate::initializeDatabaseFromOptions(const QSharedPointer<QCommandLineParser>& parser)
 | 
			
		||||
{
 | 
			
		||||
    if (parser.isNull()) {
 | 
			
		||||
        return {};
 | 
			
		||||
| 
						 | 
				
			
			@ -60,7 +65,7 @@ QSharedPointer<Database> Create::initializeDatabaseFromOptions(const QSharedPoin
 | 
			
		|||
    auto& err = Utils::STDERR;
 | 
			
		||||
 | 
			
		||||
    // Validate the decryption time before asking for a password.
 | 
			
		||||
    QString decryptionTimeValue = parser->value(Create::DecryptionTimeOption);
 | 
			
		||||
    QString decryptionTimeValue = parser->value(DatabaseCreate::DecryptionTimeOption);
 | 
			
		||||
    int decryptionTime = 0;
 | 
			
		||||
    if (decryptionTimeValue.length() != 0) {
 | 
			
		||||
        decryptionTime = decryptionTimeValue.toInt();
 | 
			
		||||
| 
						 | 
				
			
			@ -78,7 +83,7 @@ QSharedPointer<Database> Create::initializeDatabaseFromOptions(const QSharedPoin
 | 
			
		|||
 | 
			
		||||
    auto key = QSharedPointer<CompositeKey>::create();
 | 
			
		||||
 | 
			
		||||
    if (parser->isSet(Create::SetPasswordOption)) {
 | 
			
		||||
    if (parser->isSet(DatabaseCreate::SetPasswordOption)) {
 | 
			
		||||
        auto passwordKey = Utils::getConfirmedPassword();
 | 
			
		||||
        if (passwordKey.isNull()) {
 | 
			
		||||
            err << QObject::tr("Failed to set database password.") << endl;
 | 
			
		||||
| 
						 | 
				
			
			@ -87,10 +92,18 @@ QSharedPointer<Database> Create::initializeDatabaseFromOptions(const QSharedPoin
 | 
			
		|||
        key->addKey(passwordKey);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (parser->isSet(Create::SetKeyFileOption)) {
 | 
			
		||||
    if (parser->isSet(DatabaseCreate::SetKeyFileOption) || parser->isSet(DatabaseCreate::SetKeyFileShortOption)) {
 | 
			
		||||
        QSharedPointer<FileKey> fileKey;
 | 
			
		||||
 | 
			
		||||
        if (!Utils::loadFileKey(parser->value(Create::SetKeyFileOption), fileKey)) {
 | 
			
		||||
        QString keyFilePath;
 | 
			
		||||
        if (parser->isSet(DatabaseCreate::SetKeyFileShortOption)) {
 | 
			
		||||
            qWarning("The -k option will be deprecated. Please use the --set-key-file option instead.");
 | 
			
		||||
            keyFilePath = parser->value(DatabaseCreate::SetKeyFileShortOption);
 | 
			
		||||
        } else {
 | 
			
		||||
            keyFilePath = parser->value(DatabaseCreate::SetKeyFileOption);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (!Utils::loadFileKey(keyFilePath, fileKey)) {
 | 
			
		||||
            err << QObject::tr("Loading the key file failed") << endl;
 | 
			
		||||
            return {};
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			@ -141,7 +154,7 @@ QSharedPointer<Database> Create::initializeDatabaseFromOptions(const QSharedPoin
 | 
			
		|||
 *
 | 
			
		||||
 * @return EXIT_SUCCESS on success, or EXIT_FAILURE on failure
 | 
			
		||||
 */
 | 
			
		||||
int Create::execute(const QStringList& arguments)
 | 
			
		||||
int DatabaseCreate::execute(const QStringList& arguments)
 | 
			
		||||
{
 | 
			
		||||
    QSharedPointer<QCommandLineParser> parser = getCommandLineParser(arguments);
 | 
			
		||||
    if (parser.isNull()) {
 | 
			
		||||
| 
						 | 
				
			
			@ -159,7 +172,7 @@ int Create::execute(const QStringList& arguments)
 | 
			
		|||
        return EXIT_FAILURE;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    QSharedPointer<Database> db = Create::initializeDatabaseFromOptions(parser);
 | 
			
		||||
    QSharedPointer<Database> db = DatabaseCreate::initializeDatabaseFromOptions(parser);
 | 
			
		||||
    if (!db) {
 | 
			
		||||
        return EXIT_FAILURE;
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -15,22 +15,23 @@
 | 
			
		|||
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifndef KEEPASSXC_CREATE_H
 | 
			
		||||
#define KEEPASSXC_CREATE_H
 | 
			
		||||
#ifndef KEEPASSXC_DATABASECREATE_H
 | 
			
		||||
#define KEEPASSXC_DATABASECREATE_H
 | 
			
		||||
 | 
			
		||||
#include "Command.h"
 | 
			
		||||
 | 
			
		||||
class Create : public Command
 | 
			
		||||
class DatabaseCreate : public Command
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    Create();
 | 
			
		||||
    DatabaseCreate();
 | 
			
		||||
    int execute(const QStringList& arguments) override;
 | 
			
		||||
 | 
			
		||||
    static QSharedPointer<Database> initializeDatabaseFromOptions(const QSharedPointer<QCommandLineParser>& parser);
 | 
			
		||||
 | 
			
		||||
    static const QCommandLineOption SetKeyFileOption;
 | 
			
		||||
    static const QCommandLineOption SetKeyFileShortOption;
 | 
			
		||||
    static const QCommandLineOption SetPasswordOption;
 | 
			
		||||
    static const QCommandLineOption DecryptionTimeOption;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#endif // KEEPASSXC_CREATE_H
 | 
			
		||||
#endif // KEEPASSXC_DATABASECREATE_H
 | 
			
		||||
							
								
								
									
										174
									
								
								src/cli/DatabaseEdit.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										174
									
								
								src/cli/DatabaseEdit.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,174 @@
 | 
			
		|||
/*
 | 
			
		||||
 *  Copyright (C) 2022 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 "DatabaseEdit.h"
 | 
			
		||||
 | 
			
		||||
#include "Utils.h"
 | 
			
		||||
#include "cli/DatabaseCreate.h"
 | 
			
		||||
#include "keys/ChallengeResponseKey.h"
 | 
			
		||||
#include "keys/FileKey.h"
 | 
			
		||||
#include "keys/PasswordKey.h"
 | 
			
		||||
 | 
			
		||||
#include <QCommandLineParser>
 | 
			
		||||
#include <QFileInfo>
 | 
			
		||||
 | 
			
		||||
const QCommandLineOption DatabaseEdit::UnsetPasswordOption =
 | 
			
		||||
    QCommandLineOption(QStringList() << "unset-password", QObject::tr("Unset the password for the database."));
 | 
			
		||||
const QCommandLineOption DatabaseEdit::UnsetKeyFileOption =
 | 
			
		||||
    QCommandLineOption(QStringList() << "unset-key-file", QObject::tr("Unset the key file for the database."));
 | 
			
		||||
 | 
			
		||||
DatabaseEdit::DatabaseEdit()
 | 
			
		||||
{
 | 
			
		||||
    name = QString("db-edit");
 | 
			
		||||
    description = QObject::tr("Edit a database.");
 | 
			
		||||
    options.append(DatabaseCreate::SetKeyFileOption);
 | 
			
		||||
    options.append(DatabaseCreate::SetPasswordOption);
 | 
			
		||||
    options.append(DatabaseEdit::UnsetKeyFileOption);
 | 
			
		||||
    options.append(DatabaseEdit::UnsetPasswordOption);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int DatabaseEdit::executeWithDatabase(QSharedPointer<Database> database, QSharedPointer<QCommandLineParser> parser)
 | 
			
		||||
{
 | 
			
		||||
    auto& out = Utils::STDOUT;
 | 
			
		||||
    auto& err = Utils::STDERR;
 | 
			
		||||
 | 
			
		||||
    const QStringList args = parser->positionalArguments();
 | 
			
		||||
    bool databaseWasChanged = false;
 | 
			
		||||
 | 
			
		||||
    if (parser->isSet(DatabaseCreate::SetPasswordOption) && parser->isSet(DatabaseEdit::UnsetPasswordOption)) {
 | 
			
		||||
        err << QObject::tr("Cannot use %1 and %2 at the same time.")
 | 
			
		||||
                   .arg(DatabaseCreate::SetPasswordOption.names().at(0))
 | 
			
		||||
                   .arg(DatabaseEdit::UnsetPasswordOption.names().at(0))
 | 
			
		||||
            << endl;
 | 
			
		||||
        return EXIT_FAILURE;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (parser->isSet(DatabaseCreate::SetKeyFileOption) && parser->isSet(DatabaseEdit::UnsetKeyFileOption)) {
 | 
			
		||||
        err << QObject::tr("Cannot use %1 and %2 at the same time.")
 | 
			
		||||
                   .arg(DatabaseCreate::SetKeyFileOption.names().at(0))
 | 
			
		||||
                   .arg(DatabaseEdit::UnsetKeyFileOption.names().at(0))
 | 
			
		||||
            << endl;
 | 
			
		||||
        return EXIT_FAILURE;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool hasKeyChange =
 | 
			
		||||
        (parser->isSet(DatabaseCreate::SetPasswordOption) || parser->isSet(DatabaseCreate::SetKeyFileOption)
 | 
			
		||||
         || parser->isSet(DatabaseEdit::UnsetPasswordOption) || parser->isSet(DatabaseEdit::UnsetKeyFileOption));
 | 
			
		||||
 | 
			
		||||
    if (hasKeyChange) {
 | 
			
		||||
        auto newDatabaseKey = getNewDatabaseKey(database,
 | 
			
		||||
                                                parser->isSet(DatabaseCreate::SetPasswordOption),
 | 
			
		||||
                                                parser->isSet(DatabaseEdit::UnsetPasswordOption),
 | 
			
		||||
                                                parser->value(DatabaseCreate::SetKeyFileOption),
 | 
			
		||||
                                                parser->isSet(DatabaseEdit::UnsetKeyFileOption));
 | 
			
		||||
        if (newDatabaseKey.isNull()) {
 | 
			
		||||
            err << QObject::tr("Could not change the database key.") << endl;
 | 
			
		||||
            return EXIT_FAILURE;
 | 
			
		||||
        }
 | 
			
		||||
        database->setKey(newDatabaseKey);
 | 
			
		||||
        databaseWasChanged = true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!databaseWasChanged) {
 | 
			
		||||
        out << QObject::tr("Database was not modified.") << endl;
 | 
			
		||||
        return EXIT_SUCCESS;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    QString errorMessage;
 | 
			
		||||
    if (!database->save(Database::Atomic, {}, &errorMessage)) {
 | 
			
		||||
        err << QObject::tr("Writing the database failed: %1").arg(errorMessage) << endl;
 | 
			
		||||
        return EXIT_FAILURE;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    out << QObject::tr("Successfully edited the database.") << endl;
 | 
			
		||||
    return EXIT_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
QSharedPointer<CompositeKey> DatabaseEdit::getNewDatabaseKey(QSharedPointer<Database> database,
 | 
			
		||||
                                                             bool updatePassword,
 | 
			
		||||
                                                             bool removePassword,
 | 
			
		||||
                                                             QString newFileKeyPath,
 | 
			
		||||
                                                             bool removeKeyFile)
 | 
			
		||||
{
 | 
			
		||||
    auto& err = Utils::STDERR;
 | 
			
		||||
    auto newDatabaseKey = QSharedPointer<CompositeKey>::create();
 | 
			
		||||
    bool updateKeyFile = !newFileKeyPath.isEmpty();
 | 
			
		||||
 | 
			
		||||
    auto currentPasswordKey = database->key()->getKey(PasswordKey::UUID);
 | 
			
		||||
    auto currentFileKey = database->key()->getKey(FileKey::UUID);
 | 
			
		||||
    auto currentChallengeResponseKey = database->key()->getChallengeResponseKey(ChallengeResponseKey::UUID);
 | 
			
		||||
 | 
			
		||||
    if (removePassword && currentPasswordKey.isNull()) {
 | 
			
		||||
        err << QObject::tr("Cannot remove password: The database does not have a password.") << endl;
 | 
			
		||||
        return {};
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (removeKeyFile && currentFileKey.isNull()) {
 | 
			
		||||
        err << QObject::tr("Cannot remove file key: The database does not have a file key.") << endl;
 | 
			
		||||
        return {};
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (updatePassword) {
 | 
			
		||||
        QSharedPointer<PasswordKey> newPasswordKey = Utils::getConfirmedPassword();
 | 
			
		||||
        if (newPasswordKey.isNull()) {
 | 
			
		||||
            err << QObject::tr("Failed to set database password.") << endl;
 | 
			
		||||
            return {};
 | 
			
		||||
        }
 | 
			
		||||
        newDatabaseKey->addKey(newPasswordKey);
 | 
			
		||||
    } else if (!removePassword && !currentPasswordKey.isNull()) {
 | 
			
		||||
        newDatabaseKey->addKey(currentPasswordKey);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (updateKeyFile) {
 | 
			
		||||
        QSharedPointer<FileKey> newFileKey = QSharedPointer<FileKey>::create();
 | 
			
		||||
        QString errorMessage;
 | 
			
		||||
        if (!Utils::loadFileKey(newFileKeyPath, newFileKey)) {
 | 
			
		||||
            err << QObject::tr("Loading the new key file failed: %1").arg(errorMessage) << endl;
 | 
			
		||||
            return {};
 | 
			
		||||
        }
 | 
			
		||||
        newDatabaseKey->addKey(newFileKey);
 | 
			
		||||
    } else if (!removeKeyFile && !currentFileKey.isNull()) {
 | 
			
		||||
        newDatabaseKey->addKey(currentFileKey);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // This is a sanity check to make sure that this function is not used if
 | 
			
		||||
    // new key types are introduced. Otherwise, those key types would be
 | 
			
		||||
    // silently removed from the database.
 | 
			
		||||
    for (const QSharedPointer<Key>& key : database->key()->keys()) {
 | 
			
		||||
        if (key->uuid() != PasswordKey::UUID && key->uuid() != FileKey::UUID) {
 | 
			
		||||
            err << QObject::tr("Found unexpected Key type %1").arg(key->uuid().toString()) << endl;
 | 
			
		||||
            return {};
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    for (const QSharedPointer<ChallengeResponseKey>& key : database->key()->challengeResponseKeys()) {
 | 
			
		||||
        if (key->uuid() != ChallengeResponseKey::UUID) {
 | 
			
		||||
            err << QObject::tr("Found unexpected Key type %1").arg(key->uuid().toString()) << endl;
 | 
			
		||||
            return {};
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!currentChallengeResponseKey.isNull()) {
 | 
			
		||||
        newDatabaseKey->addChallengeResponseKey(currentChallengeResponseKey);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (newDatabaseKey->keys().isEmpty() && newDatabaseKey->challengeResponseKeys().isEmpty()) {
 | 
			
		||||
        err << QObject::tr("Cannot remove all the keys from a database.") << endl;
 | 
			
		||||
        return {};
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return newDatabaseKey;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										41
									
								
								src/cli/DatabaseEdit.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								src/cli/DatabaseEdit.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,41 @@
 | 
			
		|||
/*
 | 
			
		||||
 *  Copyright (C) 2022 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_DATABASEEDIT_H
 | 
			
		||||
#define KEEPASSXC_DATABASEEDIT_H
 | 
			
		||||
 | 
			
		||||
#include "DatabaseCommand.h"
 | 
			
		||||
 | 
			
		||||
class DatabaseEdit : public DatabaseCommand
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    DatabaseEdit();
 | 
			
		||||
 | 
			
		||||
    int executeWithDatabase(QSharedPointer<Database> db, QSharedPointer<QCommandLineParser> parser) override;
 | 
			
		||||
 | 
			
		||||
    static const QCommandLineOption UnsetKeyFileOption;
 | 
			
		||||
    static const QCommandLineOption UnsetPasswordOption;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    QSharedPointer<CompositeKey> getNewDatabaseKey(QSharedPointer<Database> database,
 | 
			
		||||
                                                   bool updatePassword,
 | 
			
		||||
                                                   bool removePassword,
 | 
			
		||||
                                                   QString newFileKeyPath,
 | 
			
		||||
                                                   bool removeKeyFile);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#endif // KEEPASSXC_DATABASEEDIT_H
 | 
			
		||||
| 
						 | 
				
			
			@ -15,7 +15,7 @@
 | 
			
		|||
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "Info.h"
 | 
			
		||||
#include "DatabaseInfo.h"
 | 
			
		||||
 | 
			
		||||
#include "Utils.h"
 | 
			
		||||
#include "core/DatabaseStats.h"
 | 
			
		||||
| 
						 | 
				
			
			@ -25,13 +25,13 @@
 | 
			
		|||
 | 
			
		||||
#include <QCommandLineParser>
 | 
			
		||||
 | 
			
		||||
Info::Info()
 | 
			
		||||
DatabaseInfo::DatabaseInfo()
 | 
			
		||||
{
 | 
			
		||||
    name = QString("db-info");
 | 
			
		||||
    description = QObject::tr("Show a database's information.");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int Info::executeWithDatabase(QSharedPointer<Database> database, QSharedPointer<QCommandLineParser>)
 | 
			
		||||
int DatabaseInfo::executeWithDatabase(QSharedPointer<Database> database, QSharedPointer<QCommandLineParser>)
 | 
			
		||||
{
 | 
			
		||||
    auto& out = Utils::STDOUT;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -15,17 +15,17 @@
 | 
			
		|||
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifndef KEEPASSXC_INFO_H
 | 
			
		||||
#define KEEPASSXC_INFO_H
 | 
			
		||||
#ifndef KEEPASSXC_DATABASEINFO_H
 | 
			
		||||
#define KEEPASSXC_DATABASEINFO_H
 | 
			
		||||
 | 
			
		||||
#include "DatabaseCommand.h"
 | 
			
		||||
 | 
			
		||||
class Info : public DatabaseCommand
 | 
			
		||||
class DatabaseInfo : public DatabaseCommand
 | 
			
		||||
{
 | 
			
		||||
public:
 | 
			
		||||
    Info();
 | 
			
		||||
    DatabaseInfo();
 | 
			
		||||
 | 
			
		||||
    int executeWithDatabase(QSharedPointer<Database> db, QSharedPointer<QCommandLineParser> parser) override;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#endif // KEEPASSXC_INFO_H
 | 
			
		||||
#endif // KEEPASSXC_DATABASEINFO_H
 | 
			
		||||
| 
						 | 
				
			
			@ -17,7 +17,7 @@
 | 
			
		|||
 | 
			
		||||
#include "Import.h"
 | 
			
		||||
 | 
			
		||||
#include "Create.h"
 | 
			
		||||
#include "DatabaseCreate.h"
 | 
			
		||||
#include "Utils.h"
 | 
			
		||||
 | 
			
		||||
#include <QCommandLineParser>
 | 
			
		||||
| 
						 | 
				
			
			@ -40,9 +40,10 @@ Import::Import()
 | 
			
		|||
    description = QObject::tr("Import the contents of an XML database.");
 | 
			
		||||
    positionalArguments.append({QString("xml"), QObject::tr("Path of the XML database export."), QString("")});
 | 
			
		||||
    positionalArguments.append({QString("database"), QObject::tr("Path of the new database."), QString("")});
 | 
			
		||||
    options.append(Create::SetKeyFileOption);
 | 
			
		||||
    options.append(Create::SetPasswordOption);
 | 
			
		||||
    options.append(Create::DecryptionTimeOption);
 | 
			
		||||
    options.append(DatabaseCreate::SetKeyFileOption);
 | 
			
		||||
    options.append(DatabaseCreate::SetKeyFileShortOption);
 | 
			
		||||
    options.append(DatabaseCreate::SetPasswordOption);
 | 
			
		||||
    options.append(DatabaseCreate::DecryptionTimeOption);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int Import::execute(const QStringList& arguments)
 | 
			
		||||
| 
						 | 
				
			
			@ -64,7 +65,7 @@ int Import::execute(const QStringList& arguments)
 | 
			
		|||
        return EXIT_FAILURE;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    QSharedPointer<Database> db = Create::initializeDatabaseFromOptions(parser);
 | 
			
		||||
    QSharedPointer<Database> db = DatabaseCreate::initializeDatabaseFromOptions(parser);
 | 
			
		||||
    if (!db) {
 | 
			
		||||
        return EXIT_FAILURE;
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -169,6 +169,36 @@ void CompositeKey::addKey(const QSharedPointer<Key>& key)
 | 
			
		|||
    m_keys.append(key);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Get the \link Key with the specified ID.
 | 
			
		||||
 *
 | 
			
		||||
 * @param keyId the ID of the key to get.
 | 
			
		||||
 */
 | 
			
		||||
QSharedPointer<Key> CompositeKey::getKey(const QUuid keyId) const
 | 
			
		||||
{
 | 
			
		||||
    for (const QSharedPointer<Key>& key : m_keys) {
 | 
			
		||||
        if (key->uuid() == keyId) {
 | 
			
		||||
            return key;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    return {};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Get the \link ChallengeResponseKey with the specified ID.
 | 
			
		||||
 *
 | 
			
		||||
 * @param keyId the ID of the key to get.
 | 
			
		||||
 */
 | 
			
		||||
QSharedPointer<ChallengeResponseKey> CompositeKey::getChallengeResponseKey(const QUuid keyId) const
 | 
			
		||||
{
 | 
			
		||||
    for (const QSharedPointer<ChallengeResponseKey>& key : m_challengeResponseKeys) {
 | 
			
		||||
        if (key->uuid() == keyId) {
 | 
			
		||||
            return key;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    return {};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @return list of Keys which are part of this CompositeKey
 | 
			
		||||
 */
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -43,6 +43,8 @@ public:
 | 
			
		|||
    bool challenge(const QByteArray& seed, QByteArray& result, QString* error = nullptr) const;
 | 
			
		||||
 | 
			
		||||
    void addKey(const QSharedPointer<Key>& key);
 | 
			
		||||
    QSharedPointer<Key> getKey(const QUuid keyType) const;
 | 
			
		||||
    QSharedPointer<ChallengeResponseKey> getChallengeResponseKey(const QUuid keyType) const;
 | 
			
		||||
    const QList<QSharedPointer<Key>>& keys() const;
 | 
			
		||||
 | 
			
		||||
    void addChallengeResponseKey(const QSharedPointer<ChallengeResponseKey>& key);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -34,7 +34,9 @@
 | 
			
		|||
#include "cli/AttachmentImport.h"
 | 
			
		||||
#include "cli/AttachmentRemove.h"
 | 
			
		||||
#include "cli/Clip.h"
 | 
			
		||||
#include "cli/Create.h"
 | 
			
		||||
#include "cli/DatabaseCreate.h"
 | 
			
		||||
#include "cli/DatabaseEdit.h"
 | 
			
		||||
#include "cli/DatabaseInfo.h"
 | 
			
		||||
#include "cli/Diceware.h"
 | 
			
		||||
#include "cli/Edit.h"
 | 
			
		||||
#include "cli/Estimate.h"
 | 
			
		||||
| 
						 | 
				
			
			@ -42,7 +44,6 @@
 | 
			
		|||
#include "cli/Generate.h"
 | 
			
		||||
#include "cli/Help.h"
 | 
			
		||||
#include "cli/Import.h"
 | 
			
		||||
#include "cli/Info.h"
 | 
			
		||||
#include "cli/List.h"
 | 
			
		||||
#include "cli/Merge.h"
 | 
			
		||||
#include "cli/Move.h"
 | 
			
		||||
| 
						 | 
				
			
			@ -242,7 +243,7 @@ void TestCli::testBatchCommands()
 | 
			
		|||
    QVERIFY(Commands::getCommand("show"));
 | 
			
		||||
    QVERIFY(Commands::getCommand("search"));
 | 
			
		||||
    QVERIFY(!Commands::getCommand("doesnotexist"));
 | 
			
		||||
    QCOMPARE(Commands::getCommands().size(), 25);
 | 
			
		||||
    QCOMPARE(Commands::getCommands().size(), 26);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void TestCli::testInteractiveCommands()
 | 
			
		||||
| 
						 | 
				
			
			@ -274,7 +275,7 @@ void TestCli::testInteractiveCommands()
 | 
			
		|||
    QVERIFY(Commands::getCommand("show"));
 | 
			
		||||
    QVERIFY(Commands::getCommand("search"));
 | 
			
		||||
    QVERIFY(!Commands::getCommand("doesnotexist"));
 | 
			
		||||
    QCOMPARE(Commands::getCommands().size(), 25);
 | 
			
		||||
    QCOMPARE(Commands::getCommands().size(), 26);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void TestCli::testAdd()
 | 
			
		||||
| 
						 | 
				
			
			@ -732,7 +733,7 @@ void TestCli::testClip()
 | 
			
		|||
 | 
			
		||||
void TestCli::testCreate()
 | 
			
		||||
{
 | 
			
		||||
    Create createCmd;
 | 
			
		||||
    DatabaseCreate createCmd;
 | 
			
		||||
    QVERIFY(!createCmd.name.isEmpty());
 | 
			
		||||
    QVERIFY(createCmd.getDescriptionLine().contains(createCmd.name));
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -848,9 +849,147 @@ void TestCli::testCreate()
 | 
			
		|||
    QVERIFY(db);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void TestCli::testDatabaseEdit()
 | 
			
		||||
{
 | 
			
		||||
    TemporaryFile firstKeyFile;
 | 
			
		||||
    firstKeyFile.open();
 | 
			
		||||
    firstKeyFile.write(QString("keyFilePassword").toLatin1());
 | 
			
		||||
    firstKeyFile.close();
 | 
			
		||||
 | 
			
		||||
    TemporaryFile secondKeyFile;
 | 
			
		||||
    secondKeyFile.open();
 | 
			
		||||
    secondKeyFile.write(QString("newKeyFilePassword").toLatin1());
 | 
			
		||||
    secondKeyFile.close();
 | 
			
		||||
 | 
			
		||||
    QScopedPointer<QTemporaryDir> testDir(new QTemporaryDir());
 | 
			
		||||
 | 
			
		||||
    DatabaseCreate createCmd;
 | 
			
		||||
    DatabaseEdit editCmd;
 | 
			
		||||
    QVERIFY(!editCmd.name.isEmpty());
 | 
			
		||||
    QVERIFY(editCmd.getDescriptionLine().contains(editCmd.name));
 | 
			
		||||
 | 
			
		||||
    QString dbFilename;
 | 
			
		||||
    dbFilename = testDir->path() + "/testDatabaseEdit.kdbx";
 | 
			
		||||
 | 
			
		||||
    // Creating a database for testing
 | 
			
		||||
    setInput({"a", "a"});
 | 
			
		||||
    execCmd(createCmd, {"db-create", dbFilename, "-p"});
 | 
			
		||||
    QCOMPARE(m_stdout->readLine(), QByteArray("Successfully created new database.\n"));
 | 
			
		||||
 | 
			
		||||
    // Sanity check.
 | 
			
		||||
    auto db = readDatabase(dbFilename, "a");
 | 
			
		||||
    QVERIFY(!db.isNull());
 | 
			
		||||
 | 
			
		||||
    setInput("a");
 | 
			
		||||
    execCmd(editCmd, {"db-edit", dbFilename, "-p", "--unset-password"});
 | 
			
		||||
    QCOMPARE(m_stdout->readAll(), QByteArray(""));
 | 
			
		||||
    m_stderr->readLine();
 | 
			
		||||
    QCOMPARE(m_stderr->readAll(), QByteArray("Cannot use p and unset-password at the same time.\n"));
 | 
			
		||||
 | 
			
		||||
    setInput("a");
 | 
			
		||||
    execCmd(editCmd, {"db-edit", dbFilename, "--set-key-file", "/key/file/path", "--unset-key-file"});
 | 
			
		||||
    QCOMPARE(m_stdout->readAll(), QByteArray(""));
 | 
			
		||||
    // Skipping the password prompt.
 | 
			
		||||
    m_stderr->readLine();
 | 
			
		||||
    QCOMPARE(m_stderr->readAll(), QByteArray("Cannot use set-key-file and unset-key-file at the same time.\n"));
 | 
			
		||||
 | 
			
		||||
    // Sanity check.
 | 
			
		||||
    db = readDatabase(dbFilename, "a");
 | 
			
		||||
    QVERIFY(!db.isNull());
 | 
			
		||||
 | 
			
		||||
    setInput({"a", "b", "b"});
 | 
			
		||||
    execCmd(editCmd, {"db-edit", dbFilename, "-p"});
 | 
			
		||||
    QCOMPARE(m_stdout->readAll(), QByteArray("Successfully edited the database.\n"));
 | 
			
		||||
 | 
			
		||||
    // Sanity check
 | 
			
		||||
    db = readDatabase(dbFilename, "b");
 | 
			
		||||
    QVERIFY(!db.isNull());
 | 
			
		||||
 | 
			
		||||
    setInput("b");
 | 
			
		||||
    execCmd(editCmd, {"db-edit", dbFilename, "--set-key-file", firstKeyFile.fileName()});
 | 
			
		||||
    // Skipping the password prompt.
 | 
			
		||||
    m_stderr->readLine();
 | 
			
		||||
    QCOMPARE(m_stderr->readAll(), QByteArray(""));
 | 
			
		||||
    QCOMPARE(m_stdout->readAll(), QByteArray("Successfully edited the database.\n"));
 | 
			
		||||
 | 
			
		||||
    // Sanity check
 | 
			
		||||
    db = readDatabase(dbFilename, "b");
 | 
			
		||||
    QVERIFY(db.isNull());
 | 
			
		||||
    db = readDatabase(dbFilename, "b", firstKeyFile.fileName());
 | 
			
		||||
    QVERIFY(!db.isNull());
 | 
			
		||||
 | 
			
		||||
    setInput("b");
 | 
			
		||||
    execCmd(editCmd,
 | 
			
		||||
            {"db-edit", dbFilename, "-k", firstKeyFile.fileName(), "--set-key-file", secondKeyFile.fileName()});
 | 
			
		||||
    QCOMPARE(m_stdout->readAll(), QByteArray("Successfully edited the database.\n"));
 | 
			
		||||
 | 
			
		||||
    // Sanity check
 | 
			
		||||
    db = readDatabase(dbFilename, "b", firstKeyFile.fileName());
 | 
			
		||||
    QVERIFY(db.isNull());
 | 
			
		||||
    db = readDatabase(dbFilename, "b", secondKeyFile.fileName());
 | 
			
		||||
    QVERIFY(!db.isNull());
 | 
			
		||||
 | 
			
		||||
    setInput("b");
 | 
			
		||||
    execCmd(editCmd, {"db-edit", dbFilename, "-k", secondKeyFile.fileName(), "--unset-password"});
 | 
			
		||||
    // Skipping the password prompt.
 | 
			
		||||
    m_stderr->readLine();
 | 
			
		||||
    QCOMPARE(m_stderr->readAll(), QByteArray(""));
 | 
			
		||||
    QCOMPARE(m_stdout->readAll(), QByteArray("Successfully edited the database.\n"));
 | 
			
		||||
 | 
			
		||||
    execCmd(editCmd,
 | 
			
		||||
            {"db-edit",
 | 
			
		||||
             dbFilename,
 | 
			
		||||
             "--no-password",
 | 
			
		||||
             "-k",
 | 
			
		||||
             secondKeyFile.fileName(),
 | 
			
		||||
             "--set-key-file",
 | 
			
		||||
             firstKeyFile.fileName()});
 | 
			
		||||
    // Skipping the password prompt.
 | 
			
		||||
    m_stderr->readLine();
 | 
			
		||||
    QCOMPARE(m_stderr->readAll(), QByteArray(""));
 | 
			
		||||
    QCOMPARE(m_stdout->readAll(), QByteArray("Successfully edited the database.\n"));
 | 
			
		||||
 | 
			
		||||
    setInput({"b", "b"});
 | 
			
		||||
    execCmd(editCmd, {"db-edit", dbFilename, "-k", firstKeyFile.fileName(), "--no-password", "--set-password"});
 | 
			
		||||
    // Skipping over the password setting prompts.
 | 
			
		||||
    m_stderr->readLine();
 | 
			
		||||
    m_stderr->readLine();
 | 
			
		||||
    QCOMPARE(m_stderr->readAll(), QByteArray(""));
 | 
			
		||||
    QCOMPARE(m_stdout->readAll(), QByteArray("Successfully edited the database.\n"));
 | 
			
		||||
 | 
			
		||||
    setInput("b");
 | 
			
		||||
    execCmd(editCmd, {"db-edit", dbFilename, "-k", firstKeyFile.fileName(), "--unset-key-file"});
 | 
			
		||||
    // Skipping the password prompt.
 | 
			
		||||
    m_stderr->readLine();
 | 
			
		||||
    QCOMPARE(m_stderr->readAll(), QByteArray(""));
 | 
			
		||||
    QCOMPARE(m_stdout->readAll(), QByteArray("Successfully edited the database.\n"));
 | 
			
		||||
 | 
			
		||||
    // Sanity check
 | 
			
		||||
    db = readDatabase(dbFilename, "b", firstKeyFile.fileName());
 | 
			
		||||
    QVERIFY(db.isNull());
 | 
			
		||||
    db = readDatabase(dbFilename, "b");
 | 
			
		||||
    QVERIFY(!db.isNull());
 | 
			
		||||
 | 
			
		||||
    // Trying to remove the key file when there is none set should
 | 
			
		||||
    // raise an error.
 | 
			
		||||
    setInput("b");
 | 
			
		||||
    execCmd(editCmd, {"db-edit", dbFilename, "-p", "--unset-key-file"});
 | 
			
		||||
    QCOMPARE(m_stdout->readAll(), QByteArray(""));
 | 
			
		||||
    m_stderr->readLine();
 | 
			
		||||
    QCOMPARE(m_stderr->readLine(), QByteArray("Cannot remove file key: The database does not have a file key.\n"));
 | 
			
		||||
    QCOMPARE(m_stderr->readLine(), QByteArray("Could not change the database key.\n"));
 | 
			
		||||
 | 
			
		||||
    setInput("b");
 | 
			
		||||
    execCmd(editCmd, {"db-edit", dbFilename, "--unset-password"});
 | 
			
		||||
    QCOMPARE(m_stdout->readAll(), QByteArray(""));
 | 
			
		||||
    // Skipping the password prompt.
 | 
			
		||||
    m_stderr->readLine();
 | 
			
		||||
    QCOMPARE(m_stderr->readLine(), QByteArray("Cannot remove all the keys from a database.\n"));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void TestCli::testInfo()
 | 
			
		||||
{
 | 
			
		||||
    Info infoCmd;
 | 
			
		||||
    DatabaseInfo infoCmd;
 | 
			
		||||
    QVERIFY(!infoCmd.name.isEmpty());
 | 
			
		||||
    QVERIFY(infoCmd.getDescriptionLine().contains(infoCmd.name));
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1613,7 +1752,7 @@ void TestCli::testMerge()
 | 
			
		|||
 | 
			
		||||
void TestCli::testMergeWithKeys()
 | 
			
		||||
{
 | 
			
		||||
    Create createCmd;
 | 
			
		||||
    DatabaseCreate createCmd;
 | 
			
		||||
    QVERIFY(!createCmd.name.isEmpty());
 | 
			
		||||
    QVERIFY(createCmd.getDescriptionLine().contains(createCmd.name));
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -54,6 +54,7 @@ private slots:
 | 
			
		|||
    void testCommandParsing_data();
 | 
			
		||||
    void testCommandParsing();
 | 
			
		||||
    void testCreate();
 | 
			
		||||
    void testDatabaseEdit();
 | 
			
		||||
    void testDiceware();
 | 
			
		||||
    void testEdit();
 | 
			
		||||
    void testEstimate_data();
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue