mirror of
https://github.com/keepassxreboot/keepassxc.git
synced 2025-02-23 07:59:54 -05:00
Merge branch 'release/2.4.2' into develop
This commit is contained in:
commit
63aab99b9c
@ -80,7 +80,7 @@ parts:
|
|||||||
- libxtst6
|
- libxtst6
|
||||||
- libqt5x11extras5
|
- libqt5x11extras5
|
||||||
- libqt5svg5
|
- libqt5svg5
|
||||||
- libqrencode3
|
- try: [libqrencode3, libqrencode4]
|
||||||
- libqt5concurrent5
|
- libqt5concurrent5
|
||||||
- libquazip5-1
|
- libquazip5-1
|
||||||
- libusb-1.0-0
|
- libusb-1.0-0
|
||||||
|
@ -47,7 +47,7 @@ AutoType::AutoType(QObject* parent, bool test)
|
|||||||
, m_pluginLoader(new QPluginLoader(this))
|
, m_pluginLoader(new QPluginLoader(this))
|
||||||
, m_plugin(nullptr)
|
, m_plugin(nullptr)
|
||||||
, m_executor(nullptr)
|
, m_executor(nullptr)
|
||||||
, m_windowFromGlobal(0)
|
, m_windowForGlobal(0)
|
||||||
{
|
{
|
||||||
// prevent crash when the plugin has unresolved symbols
|
// prevent crash when the plugin has unresolved symbols
|
||||||
m_pluginLoader->setLoadHints(QLibrary::ResolveAllSymbolsHint);
|
m_pluginLoader->setLoadHints(QLibrary::ResolveAllSymbolsHint);
|
||||||
@ -90,7 +90,7 @@ void AutoType::loadPlugin(const QString& pluginPath)
|
|||||||
if (m_plugin) {
|
if (m_plugin) {
|
||||||
if (m_plugin->isAvailable()) {
|
if (m_plugin->isAvailable()) {
|
||||||
m_executor = m_plugin->createExecutor();
|
m_executor = m_plugin->createExecutor();
|
||||||
connect(pluginInstance, SIGNAL(globalShortcutTriggered()), SIGNAL(globalShortcutTriggered()));
|
connect(pluginInstance, SIGNAL(globalShortcutTriggered()), SLOT(startGlobalAutoType()));
|
||||||
} else {
|
} else {
|
||||||
unloadPlugin();
|
unloadPlugin();
|
||||||
}
|
}
|
||||||
@ -222,6 +222,7 @@ void AutoType::executeAutoTypeActions(const Entry* entry, QWidget* hideWindow, c
|
|||||||
|
|
||||||
Tools::wait(qMax(100, config()->get("AutoTypeStartDelay", 500).toInt()));
|
Tools::wait(qMax(100, config()->get("AutoTypeStartDelay", 500).toInt()));
|
||||||
|
|
||||||
|
// Used only for selected entry auto-type
|
||||||
if (!window) {
|
if (!window) {
|
||||||
window = m_plugin->activeWindow();
|
window = m_plugin->activeWindow();
|
||||||
}
|
}
|
||||||
@ -240,6 +241,9 @@ void AutoType::executeAutoTypeActions(const Entry* entry, QWidget* hideWindow, c
|
|||||||
QCoreApplication::processEvents(QEventLoop::AllEvents, 10);
|
QCoreApplication::processEvents(QEventLoop::AllEvents, 10);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m_windowForGlobal = 0;
|
||||||
|
m_windowTitleForGlobal.clear();
|
||||||
|
|
||||||
// emit signal only if autotype performed correctly
|
// emit signal only if autotype performed correctly
|
||||||
emit autotypePerformed();
|
emit autotypePerformed();
|
||||||
|
|
||||||
@ -264,6 +268,13 @@ void AutoType::performAutoType(const Entry* entry, QWidget* hideWindow)
|
|||||||
executeAutoTypeActions(entry, hideWindow, sequences.first());
|
executeAutoTypeActions(entry, hideWindow, sequences.first());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AutoType::startGlobalAutoType()
|
||||||
|
{
|
||||||
|
m_windowForGlobal = m_plugin->activeWindow();
|
||||||
|
m_windowTitleForGlobal = m_plugin->activeWindowTitle();
|
||||||
|
emit globalAutoTypeTriggered();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Global Autotype entry-point function
|
* Global Autotype entry-point function
|
||||||
* Perform global Auto-Type on the active window
|
* Perform global Auto-Type on the active window
|
||||||
@ -278,9 +289,7 @@ void AutoType::performGlobalAutoType(const QList<QSharedPointer<Database>>& dbLi
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString windowTitle = m_plugin->activeWindowTitle();
|
if (m_windowTitleForGlobal.isEmpty()) {
|
||||||
|
|
||||||
if (windowTitle.isEmpty()) {
|
|
||||||
m_inGlobalAutoTypeDialog.unlock();
|
m_inGlobalAutoTypeDialog.unlock();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -290,7 +299,7 @@ void AutoType::performGlobalAutoType(const QList<QSharedPointer<Database>>& dbLi
|
|||||||
for (const auto& db : dbList) {
|
for (const auto& db : dbList) {
|
||||||
const QList<Entry*> dbEntries = db->rootGroup()->entriesRecursive();
|
const QList<Entry*> dbEntries = db->rootGroup()->entriesRecursive();
|
||||||
for (Entry* entry : dbEntries) {
|
for (Entry* entry : dbEntries) {
|
||||||
const QSet<QString> sequences = autoTypeSequences(entry, windowTitle).toSet();
|
const QSet<QString> sequences = autoTypeSequences(entry, m_windowTitleForGlobal).toSet();
|
||||||
for (const QString& sequence : sequences) {
|
for (const QString& sequence : sequences) {
|
||||||
if (!sequence.isEmpty()) {
|
if (!sequence.isEmpty()) {
|
||||||
matchList << AutoTypeMatch(entry, sequence);
|
matchList << AutoTypeMatch(entry, sequence);
|
||||||
@ -304,8 +313,9 @@ void AutoType::performGlobalAutoType(const QList<QSharedPointer<Database>>& dbLi
|
|||||||
auto* msgBox = new QMessageBox();
|
auto* msgBox = new QMessageBox();
|
||||||
msgBox->setAttribute(Qt::WA_DeleteOnClose);
|
msgBox->setAttribute(Qt::WA_DeleteOnClose);
|
||||||
msgBox->setWindowTitle(tr("Auto-Type - KeePassXC"));
|
msgBox->setWindowTitle(tr("Auto-Type - KeePassXC"));
|
||||||
msgBox->setText(
|
msgBox->setText(tr("Couldn't find an entry that matches the window title:")
|
||||||
tr("Couldn't find an entry that matches the window title:").append("\n\n").append(windowTitle));
|
.append("\n\n")
|
||||||
|
.append(m_windowTitleForGlobal));
|
||||||
msgBox->setIcon(QMessageBox::Information);
|
msgBox->setIcon(QMessageBox::Information);
|
||||||
msgBox->setStandardButtons(QMessageBox::Ok);
|
msgBox->setStandardButtons(QMessageBox::Ok);
|
||||||
msgBox->show();
|
msgBox->show();
|
||||||
@ -316,10 +326,9 @@ void AutoType::performGlobalAutoType(const QList<QSharedPointer<Database>>& dbLi
|
|||||||
m_inGlobalAutoTypeDialog.unlock();
|
m_inGlobalAutoTypeDialog.unlock();
|
||||||
emit autotypeRejected();
|
emit autotypeRejected();
|
||||||
} else if ((matchList.size() == 1) && !config()->get("security/autotypeask").toBool()) {
|
} else if ((matchList.size() == 1) && !config()->get("security/autotypeask").toBool()) {
|
||||||
executeAutoTypeActions(matchList.first().entry, nullptr, matchList.first().sequence);
|
executeAutoTypeActions(matchList.first().entry, nullptr, matchList.first().sequence, m_windowForGlobal);
|
||||||
m_inGlobalAutoTypeDialog.unlock();
|
m_inGlobalAutoTypeDialog.unlock();
|
||||||
} else {
|
} else {
|
||||||
m_windowFromGlobal = m_plugin->activeWindow();
|
|
||||||
auto* selectDialog = new AutoTypeSelectDialog();
|
auto* selectDialog = new AutoTypeSelectDialog();
|
||||||
|
|
||||||
// connect slots, both of which must unlock the m_inGlobalAutoTypeDialog mutex
|
// connect slots, both of which must unlock the m_inGlobalAutoTypeDialog mutex
|
||||||
@ -327,11 +336,12 @@ void AutoType::performGlobalAutoType(const QList<QSharedPointer<Database>>& dbLi
|
|||||||
connect(selectDialog, SIGNAL(rejected()), SLOT(autoTypeRejectedFromGlobal()));
|
connect(selectDialog, SIGNAL(rejected()), SLOT(autoTypeRejectedFromGlobal()));
|
||||||
|
|
||||||
selectDialog->setMatchList(matchList);
|
selectDialog->setMatchList(matchList);
|
||||||
#if defined(Q_OS_MACOS)
|
#ifdef Q_OS_MACOS
|
||||||
m_plugin->raiseOwnWindow();
|
m_plugin->raiseOwnWindow();
|
||||||
Tools::wait(500);
|
Tools::wait(200);
|
||||||
#endif
|
#endif
|
||||||
selectDialog->show();
|
selectDialog->show();
|
||||||
|
selectDialog->raise();
|
||||||
// necessary when the main window is minimized
|
// necessary when the main window is minimized
|
||||||
selectDialog->activateWindow();
|
selectDialog->activateWindow();
|
||||||
}
|
}
|
||||||
@ -339,8 +349,8 @@ void AutoType::performGlobalAutoType(const QList<QSharedPointer<Database>>& dbLi
|
|||||||
|
|
||||||
void AutoType::performAutoTypeFromGlobal(AutoTypeMatch match)
|
void AutoType::performAutoTypeFromGlobal(AutoTypeMatch match)
|
||||||
{
|
{
|
||||||
m_plugin->raiseWindow(m_windowFromGlobal);
|
m_plugin->raiseWindow(m_windowForGlobal);
|
||||||
executeAutoTypeActions(match.entry, nullptr, match.sequence, m_windowFromGlobal);
|
executeAutoTypeActions(match.entry, nullptr, match.sequence, m_windowForGlobal);
|
||||||
|
|
||||||
// make sure the mutex is definitely locked before we unlock it
|
// make sure the mutex is definitely locked before we unlock it
|
||||||
Q_UNUSED(m_inGlobalAutoTypeDialog.tryLock());
|
Q_UNUSED(m_inGlobalAutoTypeDialog.tryLock());
|
||||||
@ -353,6 +363,8 @@ void AutoType::autoTypeRejectedFromGlobal()
|
|||||||
// so make sure the mutex is locked before we try unlocking it
|
// so make sure the mutex is locked before we try unlocking it
|
||||||
Q_UNUSED(m_inGlobalAutoTypeDialog.tryLock());
|
Q_UNUSED(m_inGlobalAutoTypeDialog.tryLock());
|
||||||
m_inGlobalAutoTypeDialog.unlock();
|
m_inGlobalAutoTypeDialog.unlock();
|
||||||
|
m_windowForGlobal = 0;
|
||||||
|
m_windowTitleForGlobal.clear();
|
||||||
|
|
||||||
emit autotypeRejected();
|
emit autotypeRejected();
|
||||||
}
|
}
|
||||||
|
@ -62,18 +62,19 @@ public slots:
|
|||||||
void raiseWindow();
|
void raiseWindow();
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void globalShortcutTriggered();
|
void globalAutoTypeTriggered();
|
||||||
void autotypePerformed();
|
void autotypePerformed();
|
||||||
void autotypeRejected();
|
void autotypeRejected();
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
|
void startGlobalAutoType();
|
||||||
void performAutoTypeFromGlobal(AutoTypeMatch match);
|
void performAutoTypeFromGlobal(AutoTypeMatch match);
|
||||||
void autoTypeRejectedFromGlobal();
|
void autoTypeRejectedFromGlobal();
|
||||||
void unloadPlugin();
|
void unloadPlugin();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
explicit AutoType(QObject* parent = nullptr, bool test = false);
|
explicit AutoType(QObject* parent = nullptr, bool test = false);
|
||||||
~AutoType();
|
~AutoType() override;
|
||||||
void loadPlugin(const QString& pluginPath);
|
void loadPlugin(const QString& pluginPath);
|
||||||
void executeAutoTypeActions(const Entry* entry,
|
void executeAutoTypeActions(const Entry* entry,
|
||||||
QWidget* hideWindow = nullptr,
|
QWidget* hideWindow = nullptr,
|
||||||
@ -94,9 +95,11 @@ private:
|
|||||||
QPluginLoader* m_pluginLoader;
|
QPluginLoader* m_pluginLoader;
|
||||||
AutoTypePlatformInterface* m_plugin;
|
AutoTypePlatformInterface* m_plugin;
|
||||||
AutoTypeExecutor* m_executor;
|
AutoTypeExecutor* m_executor;
|
||||||
WId m_windowFromGlobal;
|
|
||||||
static AutoType* m_instance;
|
static AutoType* m_instance;
|
||||||
|
|
||||||
|
QString m_windowTitleForGlobal;
|
||||||
|
WId m_windowForGlobal;
|
||||||
|
|
||||||
Q_DISABLE_COPY(AutoType)
|
Q_DISABLE_COPY(AutoType)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -68,6 +68,11 @@ AutoTypeExecutor* AutoTypePlatformTest::createExecutor()
|
|||||||
return new AutoTypeExecutorTest(this);
|
return new AutoTypeExecutorTest(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AutoTypePlatformTest::triggerGlobalAutoType()
|
||||||
|
{
|
||||||
|
emit globalShortcutTriggered();
|
||||||
|
}
|
||||||
|
|
||||||
void AutoTypePlatformTest::setActiveWindowTitle(const QString& title)
|
void AutoTypePlatformTest::setActiveWindowTitle(const QString& title)
|
||||||
{
|
{
|
||||||
m_activeWindowTitle = title;
|
m_activeWindowTitle = title;
|
||||||
|
@ -48,6 +48,7 @@ public:
|
|||||||
bool raiseOwnWindow() override;
|
bool raiseOwnWindow() override;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
void triggerGlobalAutoType() override;
|
||||||
void setActiveWindowTitle(const QString& title) override;
|
void setActiveWindowTitle(const QString& title) override;
|
||||||
|
|
||||||
QString actionChars() override;
|
QString actionChars() override;
|
||||||
|
@ -26,6 +26,7 @@ public:
|
|||||||
virtual ~AutoTypeTestInterface()
|
virtual ~AutoTypeTestInterface()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
virtual void triggerGlobalAutoType() = 0;
|
||||||
virtual void setActiveWindowTitle(const QString& title) = 0;
|
virtual void setActiveWindowTitle(const QString& title) = 0;
|
||||||
|
|
||||||
virtual QString actionChars() = 0;
|
virtual QString actionChars() = 0;
|
||||||
|
@ -94,6 +94,8 @@ private:
|
|||||||
QString m_publicKey;
|
QString m_publicKey;
|
||||||
QString m_secretKey;
|
QString m_secretKey;
|
||||||
bool m_associated;
|
bool m_associated;
|
||||||
|
|
||||||
|
friend class TestBrowser;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // BROWSERACTION_H
|
#endif // BROWSERACTION_H
|
||||||
|
@ -120,6 +120,7 @@ void BrowserOptionDialog::loadSettings()
|
|||||||
m_ui->useCustomProxy->setChecked(settings->useCustomProxy());
|
m_ui->useCustomProxy->setChecked(settings->useCustomProxy());
|
||||||
m_ui->customProxyLocation->setText(settings->customProxyLocation());
|
m_ui->customProxyLocation->setText(settings->customProxyLocation());
|
||||||
m_ui->updateBinaryPath->setChecked(settings->updateBinaryPath());
|
m_ui->updateBinaryPath->setChecked(settings->updateBinaryPath());
|
||||||
|
m_ui->allowExpiredCredentials->setChecked(settings->allowExpiredCredentials());
|
||||||
m_ui->chromeSupport->setChecked(settings->chromeSupport());
|
m_ui->chromeSupport->setChecked(settings->chromeSupport());
|
||||||
m_ui->chromiumSupport->setChecked(settings->chromiumSupport());
|
m_ui->chromiumSupport->setChecked(settings->chromiumSupport());
|
||||||
m_ui->firefoxSupport->setChecked(settings->firefoxSupport());
|
m_ui->firefoxSupport->setChecked(settings->firefoxSupport());
|
||||||
@ -176,6 +177,7 @@ void BrowserOptionDialog::saveSettings()
|
|||||||
settings->setCustomProxyLocation(m_ui->customProxyLocation->text());
|
settings->setCustomProxyLocation(m_ui->customProxyLocation->text());
|
||||||
|
|
||||||
settings->setUpdateBinaryPath(m_ui->updateBinaryPath->isChecked());
|
settings->setUpdateBinaryPath(m_ui->updateBinaryPath->isChecked());
|
||||||
|
settings->setAllowExpiredCredentials(m_ui->allowExpiredCredentials->isChecked());
|
||||||
settings->setAlwaysAllowAccess(m_ui->alwaysAllowAccess->isChecked());
|
settings->setAlwaysAllowAccess(m_ui->alwaysAllowAccess->isChecked());
|
||||||
settings->setAlwaysAllowUpdate(m_ui->alwaysAllowUpdate->isChecked());
|
settings->setAlwaysAllowUpdate(m_ui->alwaysAllowUpdate->isChecked());
|
||||||
settings->setHttpAuthPermission(m_ui->httpAuthPermission->isChecked());
|
settings->setHttpAuthPermission(m_ui->httpAuthPermission->isChecked());
|
||||||
|
@ -219,6 +219,16 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QCheckBox" name="allowExpiredCredentials">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Returns expired credentials. String [expired] is added to the title.</string>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>&Allow returning expired credentials.</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QRadioButton" name="sortByTitle">
|
<widget class="QRadioButton" name="sortByTitle">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
|
@ -172,9 +172,9 @@ QJsonArray BrowserService::getChildrenFromGroup(Group* group)
|
|||||||
return groupList;
|
return groupList;
|
||||||
}
|
}
|
||||||
|
|
||||||
QJsonObject BrowserService::getDatabaseGroups()
|
QJsonObject BrowserService::getDatabaseGroups(const QSharedPointer<Database>& selectedDb)
|
||||||
{
|
{
|
||||||
auto db = getDatabase();
|
auto db = selectedDb ? selectedDb : getDatabase();
|
||||||
if (!db) {
|
if (!db) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
@ -453,11 +453,6 @@ void BrowserService::addEntry(const QString& id,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto* addEntryGroup = findCreateAddEntryGroup(db);
|
|
||||||
if (!addEntryGroup) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto* entry = new Entry();
|
auto* entry = new Entry();
|
||||||
entry->setUuid(QUuid::createUuid());
|
entry->setUuid(QUuid::createUuid());
|
||||||
entry->setTitle(QUrl(url).host());
|
entry->setTitle(QUrl(url).host());
|
||||||
@ -465,16 +460,19 @@ void BrowserService::addEntry(const QString& id,
|
|||||||
entry->setIcon(KEEPASSXCBROWSER_DEFAULT_ICON);
|
entry->setIcon(KEEPASSXCBROWSER_DEFAULT_ICON);
|
||||||
entry->setUsername(login);
|
entry->setUsername(login);
|
||||||
entry->setPassword(password);
|
entry->setPassword(password);
|
||||||
entry->setGroup(addEntryGroup);
|
|
||||||
|
|
||||||
// Select a group for the entry
|
// Select a group for the entry
|
||||||
if (!group.isEmpty()) {
|
if (!group.isEmpty()) {
|
||||||
if (db->rootGroup()) {
|
if (db->rootGroup()) {
|
||||||
auto selectedGroup = db->rootGroup()->findGroupByUuid(Tools::hexToUuid(groupUuid));
|
auto selectedGroup = db->rootGroup()->findGroupByUuid(Tools::hexToUuid(groupUuid));
|
||||||
if (selectedGroup && selectedGroup->name() == group) {
|
if (selectedGroup) {
|
||||||
entry->setGroup(selectedGroup);
|
entry->setGroup(selectedGroup);
|
||||||
|
} else {
|
||||||
|
entry->setGroup(getDefaultEntryGroup(db));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
entry->setGroup(getDefaultEntryGroup(db));
|
||||||
}
|
}
|
||||||
|
|
||||||
const QString host = QUrl(url).host();
|
const QString host = QUrl(url).host();
|
||||||
@ -818,6 +816,10 @@ QJsonObject BrowserService::prepareEntry(const Entry* entry)
|
|||||||
res["totp"] = entry->totp();
|
res["totp"] = entry->totp();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (entry->isExpired()) {
|
||||||
|
res["expired"] = "true";
|
||||||
|
}
|
||||||
|
|
||||||
if (browserSettings()->supportKphFields()) {
|
if (browserSettings()->supportKphFields()) {
|
||||||
const EntryAttributes* attr = entry->attributes();
|
const EntryAttributes* attr = entry->attributes();
|
||||||
QJsonArray stringFields;
|
QJsonArray stringFields;
|
||||||
@ -841,7 +843,7 @@ BrowserService::checkAccess(const Entry* entry, const QString& host, const QStri
|
|||||||
return Unknown;
|
return Unknown;
|
||||||
}
|
}
|
||||||
if (entry->isExpired()) {
|
if (entry->isExpired()) {
|
||||||
return Denied;
|
return browserSettings()->allowExpiredCredentials() ? Allowed : Denied;
|
||||||
}
|
}
|
||||||
if ((config.isAllowed(host)) && (submitHost.isEmpty() || config.isAllowed(submitHost))) {
|
if ((config.isAllowed(host)) && (submitHost.isEmpty() || config.isAllowed(submitHost))) {
|
||||||
return Allowed;
|
return Allowed;
|
||||||
@ -855,7 +857,7 @@ BrowserService::checkAccess(const Entry* entry, const QString& host, const QStri
|
|||||||
return Unknown;
|
return Unknown;
|
||||||
}
|
}
|
||||||
|
|
||||||
Group* BrowserService::findCreateAddEntryGroup(const QSharedPointer<Database>& selectedDb)
|
Group* BrowserService::getDefaultEntryGroup(const QSharedPointer<Database>& selectedDb)
|
||||||
{
|
{
|
||||||
auto db = selectedDb ? selectedDb : getDatabase();
|
auto db = selectedDb ? selectedDb : getDatabase();
|
||||||
if (!db) {
|
if (!db) {
|
||||||
@ -868,7 +870,7 @@ Group* BrowserService::findCreateAddEntryGroup(const QSharedPointer<Database>& s
|
|||||||
}
|
}
|
||||||
|
|
||||||
const QString groupName =
|
const QString groupName =
|
||||||
QLatin1String(KEEPASSXCBROWSER_GROUP_NAME); // TODO: setting to decide where new keys are created
|
QLatin1String(KEEPASSXCBROWSER_GROUP_NAME);
|
||||||
|
|
||||||
for (auto* g : rootGroup->groupsRecursive(true)) {
|
for (auto* g : rootGroup->groupsRecursive(true)) {
|
||||||
if (g->name() == groupName && !g->isRecycled()) {
|
if (g->name() == groupName && !g->isRecycled()) {
|
||||||
|
@ -44,7 +44,7 @@ public:
|
|||||||
bool openDatabase(bool triggerUnlock);
|
bool openDatabase(bool triggerUnlock);
|
||||||
QString getDatabaseRootUuid();
|
QString getDatabaseRootUuid();
|
||||||
QString getDatabaseRecycleBinUuid();
|
QString getDatabaseRecycleBinUuid();
|
||||||
QJsonObject getDatabaseGroups();
|
QJsonObject getDatabaseGroups(const QSharedPointer<Database>& selectedDb = {});
|
||||||
QJsonObject createNewGroup(const QString& groupName);
|
QJsonObject createNewGroup(const QString& groupName);
|
||||||
QString getKey(const QString& id);
|
QString getKey(const QString& id);
|
||||||
void addEntry(const QString& id,
|
void addEntry(const QString& id,
|
||||||
@ -114,7 +114,7 @@ private:
|
|||||||
const QString& realm);
|
const QString& realm);
|
||||||
QJsonObject prepareEntry(const Entry* entry);
|
QJsonObject prepareEntry(const Entry* entry);
|
||||||
Access checkAccess(const Entry* entry, const QString& host, const QString& submitHost, const QString& realm);
|
Access checkAccess(const Entry* entry, const QString& host, const QString& submitHost, const QString& realm);
|
||||||
Group* findCreateAddEntryGroup(const QSharedPointer<Database>& selectedDb = {});
|
Group* getDefaultEntryGroup(const QSharedPointer<Database>& selectedDb = {});
|
||||||
int
|
int
|
||||||
sortPriority(const Entry* entry, const QString& host, const QString& submitUrl, const QString& baseSubmitUrl) const;
|
sortPriority(const Entry* entry, const QString& host, const QString& submitUrl, const QString& baseSubmitUrl) const;
|
||||||
bool matchUrlScheme(const QString& url);
|
bool matchUrlScheme(const QString& url);
|
||||||
@ -135,6 +135,8 @@ private:
|
|||||||
bool m_bringToFrontRequested;
|
bool m_bringToFrontRequested;
|
||||||
WindowState m_prevWindowState;
|
WindowState m_prevWindowState;
|
||||||
QUuid m_keepassBrowserUUID;
|
QUuid m_keepassBrowserUUID;
|
||||||
|
|
||||||
|
friend class TestBrowser;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // BROWSERSERVICE_H
|
#endif // BROWSERSERVICE_H
|
||||||
|
@ -194,6 +194,16 @@ void BrowserSettings::setUpdateBinaryPath(bool enabled)
|
|||||||
config()->set("Browser/UpdateBinaryPath", enabled);
|
config()->set("Browser/UpdateBinaryPath", enabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool BrowserSettings::allowExpiredCredentials()
|
||||||
|
{
|
||||||
|
return config()->get("Browser/AllowExpiredCredentials", false).toBool();
|
||||||
|
}
|
||||||
|
|
||||||
|
void BrowserSettings::setAllowExpiredCredentials(bool enabled)
|
||||||
|
{
|
||||||
|
config()->set("Browser/AllowExpiredCredentials", enabled);
|
||||||
|
}
|
||||||
|
|
||||||
bool BrowserSettings::chromeSupport()
|
bool BrowserSettings::chromeSupport()
|
||||||
{
|
{
|
||||||
return m_hostInstaller.checkIfInstalled(HostInstaller::SupportedBrowsers::CHROME);
|
return m_hostInstaller.checkIfInstalled(HostInstaller::SupportedBrowsers::CHROME);
|
||||||
|
@ -64,6 +64,8 @@ public:
|
|||||||
void setCustomProxyLocation(const QString& location);
|
void setCustomProxyLocation(const QString& location);
|
||||||
bool updateBinaryPath();
|
bool updateBinaryPath();
|
||||||
void setUpdateBinaryPath(bool enabled);
|
void setUpdateBinaryPath(bool enabled);
|
||||||
|
bool allowExpiredCredentials();
|
||||||
|
void setAllowExpiredCredentials(bool enabled);
|
||||||
bool chromeSupport();
|
bool chromeSupport();
|
||||||
void setChromeSupport(bool enabled);
|
void setChromeSupport(bool enabled);
|
||||||
bool chromiumSupport();
|
bool chromiumSupport();
|
||||||
|
@ -19,6 +19,8 @@
|
|||||||
#include "NativeMessagingBase.h"
|
#include "NativeMessagingBase.h"
|
||||||
#include <QStandardPaths>
|
#include <QStandardPaths>
|
||||||
|
|
||||||
|
#include "config-keepassx.h"
|
||||||
|
|
||||||
#if defined(Q_OS_UNIX) && !defined(Q_OS_LINUX)
|
#if defined(Q_OS_UNIX) && !defined(Q_OS_LINUX)
|
||||||
#include <sys/event.h>
|
#include <sys/event.h>
|
||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
@ -138,7 +140,7 @@ QString NativeMessagingBase::getLocalServerPath() const
|
|||||||
{
|
{
|
||||||
const QString serverPath = "/kpxc_server";
|
const QString serverPath = "/kpxc_server";
|
||||||
#if defined(KEEPASSXC_DIST_SNAP)
|
#if defined(KEEPASSXC_DIST_SNAP)
|
||||||
return QProcessEnvironment::systemEnvironment().value("SNAP_COMMON") + serverPath;
|
return QProcessEnvironment::systemEnvironment().value("SNAP_USER_COMMON") + serverPath;
|
||||||
#elif defined(Q_OS_UNIX) && !defined(Q_OS_MACOS)
|
#elif defined(Q_OS_UNIX) && !defined(Q_OS_MACOS)
|
||||||
// Use XDG_RUNTIME_DIR instead of /tmp if it's available
|
// Use XDG_RUNTIME_DIR instead of /tmp if it's available
|
||||||
QString path = QStandardPaths::writableLocation(QStandardPaths::RuntimeLocation);
|
QString path = QStandardPaths::writableLocation(QStandardPaths::RuntimeLocation);
|
||||||
|
@ -35,7 +35,7 @@ bool Kdbx4Reader::readDatabaseImpl(QIODevice* device,
|
|||||||
{
|
{
|
||||||
Q_ASSERT(m_kdbxVersion == KeePass2::FILE_VERSION_4);
|
Q_ASSERT(m_kdbxVersion == KeePass2::FILE_VERSION_4);
|
||||||
|
|
||||||
m_binaryPoolInverse.clear();
|
m_binaryPool.clear();
|
||||||
|
|
||||||
if (hasError()) {
|
if (hasError()) {
|
||||||
return false;
|
return false;
|
||||||
@ -273,11 +273,7 @@ bool Kdbx4Reader::readInnerHeaderField(QIODevice* device)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
auto data = fieldData.mid(1);
|
auto data = fieldData.mid(1);
|
||||||
if (m_binaryPoolInverse.contains(data)) {
|
m_binaryPool.insert(QString::number(m_binaryPool.size()), data);
|
||||||
qWarning("Skipping duplicate binary record");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
m_binaryPoolInverse.insert(data, QString::number(m_binaryPoolInverse.size()));
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -422,17 +418,5 @@ QVariantMap Kdbx4Reader::readVariantMap(QIODevice* device)
|
|||||||
*/
|
*/
|
||||||
QHash<QString, QByteArray> Kdbx4Reader::binaryPool() const
|
QHash<QString, QByteArray> Kdbx4Reader::binaryPool() const
|
||||||
{
|
{
|
||||||
QHash<QString, QByteArray> binaryPool;
|
return m_binaryPool;
|
||||||
for (auto it = m_binaryPoolInverse.cbegin(); it != m_binaryPoolInverse.cend(); ++it) {
|
|
||||||
binaryPool.insert(it.value(), it.key());
|
|
||||||
}
|
|
||||||
return binaryPool;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return mapping from binary data to attachment keys
|
|
||||||
*/
|
|
||||||
QHash<QByteArray, QString> Kdbx4Reader::binaryPoolInverse() const
|
|
||||||
{
|
|
||||||
return m_binaryPoolInverse;
|
|
||||||
}
|
}
|
||||||
|
@ -34,7 +34,6 @@ public:
|
|||||||
const QByteArray& headerData,
|
const QByteArray& headerData,
|
||||||
QSharedPointer<const CompositeKey> key,
|
QSharedPointer<const CompositeKey> key,
|
||||||
Database* db) override;
|
Database* db) override;
|
||||||
QHash<QByteArray, QString> binaryPoolInverse() const;
|
|
||||||
QHash<QString, QByteArray> binaryPool() const;
|
QHash<QString, QByteArray> binaryPool() const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
@ -44,7 +43,7 @@ private:
|
|||||||
bool readInnerHeaderField(QIODevice* device);
|
bool readInnerHeaderField(QIODevice* device);
|
||||||
QVariantMap readVariantMap(QIODevice* device);
|
QVariantMap readVariantMap(QIODevice* device);
|
||||||
|
|
||||||
QHash<QByteArray, QString> m_binaryPoolInverse;
|
QHash<QString, QByteArray> m_binaryPool;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // KEEPASSX_KDBX4READER_H
|
#endif // KEEPASSX_KDBX4READER_H
|
||||||
|
@ -59,7 +59,7 @@ DatabaseTabWidget::DatabaseTabWidget(QWidget* parent)
|
|||||||
connect(this, SIGNAL(currentChanged(int)), SLOT(emitActivateDatabaseChanged()));
|
connect(this, SIGNAL(currentChanged(int)), SLOT(emitActivateDatabaseChanged()));
|
||||||
connect(this, SIGNAL(activateDatabaseChanged(DatabaseWidget*)),
|
connect(this, SIGNAL(activateDatabaseChanged(DatabaseWidget*)),
|
||||||
m_dbWidgetStateSync, SLOT(setActive(DatabaseWidget*)));
|
m_dbWidgetStateSync, SLOT(setActive(DatabaseWidget*)));
|
||||||
connect(autoType(), SIGNAL(globalShortcutTriggered()), SLOT(performGlobalAutoType()));
|
connect(autoType(), SIGNAL(globalAutoTypeTriggered()), SLOT(performGlobalAutoType()));
|
||||||
connect(autoType(), SIGNAL(autotypePerformed()), SLOT(relockPendingDatabase()));
|
connect(autoType(), SIGNAL(autotypePerformed()), SLOT(relockPendingDatabase()));
|
||||||
connect(autoType(), SIGNAL(autotypeRejected()), SLOT(relockPendingDatabase()));
|
connect(autoType(), SIGNAL(autotypeRejected()), SLOT(relockPendingDatabase()));
|
||||||
// clang-format on
|
// clang-format on
|
||||||
@ -580,7 +580,7 @@ void DatabaseTabWidget::unlockDatabaseInDialog(DatabaseWidget* dbWidget,
|
|||||||
#ifdef Q_OS_MACOS
|
#ifdef Q_OS_MACOS
|
||||||
if (intent == DatabaseOpenDialog::Intent::AutoType || intent == DatabaseOpenDialog::Intent::Browser) {
|
if (intent == DatabaseOpenDialog::Intent::AutoType || intent == DatabaseOpenDialog::Intent::Browser) {
|
||||||
macUtils()->raiseOwnWindow();
|
macUtils()->raiseOwnWindow();
|
||||||
Tools::wait(500);
|
Tools::wait(200);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -178,7 +178,8 @@ DatabaseWidget::DatabaseWidget(QSharedPointer<Database> db, QWidget* parent)
|
|||||||
connect(m_mainSplitter, SIGNAL(splitterMoved(int,int)), SIGNAL(mainSplitterSizesChanged()));
|
connect(m_mainSplitter, SIGNAL(splitterMoved(int,int)), SIGNAL(mainSplitterSizesChanged()));
|
||||||
connect(m_previewSplitter, SIGNAL(splitterMoved(int,int)), SIGNAL(previewSplitterSizesChanged()));
|
connect(m_previewSplitter, SIGNAL(splitterMoved(int,int)), SIGNAL(previewSplitterSizesChanged()));
|
||||||
connect(this, SIGNAL(currentModeChanged(DatabaseWidget::Mode)), m_previewView, SLOT(setDatabaseMode(DatabaseWidget::Mode)));
|
connect(this, SIGNAL(currentModeChanged(DatabaseWidget::Mode)), m_previewView, SLOT(setDatabaseMode(DatabaseWidget::Mode)));
|
||||||
connect(m_previewView, SIGNAL(errorOccurred(QString)), this, SLOT(showErrorMessage(QString)));
|
connect(m_previewView, SIGNAL(errorOccurred(QString)), SLOT(showErrorMessage(QString)));
|
||||||
|
connect(m_previewView, SIGNAL(entryUrlActivated(Entry*)), SLOT(openUrlForEntry(Entry*)));
|
||||||
connect(m_entryView, SIGNAL(viewStateChanged()), SIGNAL(entryViewStateChanged()));
|
connect(m_entryView, SIGNAL(viewStateChanged()), SIGNAL(entryViewStateChanged()));
|
||||||
connect(m_groupView, SIGNAL(groupSelectionChanged(Group*)), SLOT(onGroupChanged(Group*)));
|
connect(m_groupView, SIGNAL(groupSelectionChanged(Group*)), SLOT(onGroupChanged(Group*)));
|
||||||
connect(m_groupView, SIGNAL(groupSelectionChanged(Group*)), SIGNAL(groupChanged()));
|
connect(m_groupView, SIGNAL(groupSelectionChanged(Group*)), SIGNAL(groupChanged()));
|
||||||
@ -195,7 +196,7 @@ DatabaseWidget::DatabaseWidget(QSharedPointer<Database> db, QWidget* parent)
|
|||||||
connect(m_opVaultOpenWidget, SIGNAL(dialogFinished(bool)), SLOT(loadDatabase(bool)));
|
connect(m_opVaultOpenWidget, SIGNAL(dialogFinished(bool)), SLOT(loadDatabase(bool)));
|
||||||
connect(m_csvImportWizard, SIGNAL(importFinished(bool)), SLOT(csvImportFinished(bool)));
|
connect(m_csvImportWizard, SIGNAL(importFinished(bool)), SLOT(csvImportFinished(bool)));
|
||||||
connect(m_fileWatcher.data(), SIGNAL(fileChanged()), this, SLOT(reloadDatabaseFile()));
|
connect(m_fileWatcher.data(), SIGNAL(fileChanged()), this, SLOT(reloadDatabaseFile()));
|
||||||
connect(this, SIGNAL(currentChanged(int)), this, SLOT(emitCurrentModeChanged()));
|
connect(this, SIGNAL(currentChanged(int)), SLOT(emitCurrentModeChanged()));
|
||||||
// clang-format on
|
// clang-format on
|
||||||
|
|
||||||
connectDatabaseSignals();
|
connectDatabaseSignals();
|
||||||
@ -648,6 +649,10 @@ void DatabaseWidget::openUrl()
|
|||||||
void DatabaseWidget::openUrlForEntry(Entry* entry)
|
void DatabaseWidget::openUrlForEntry(Entry* entry)
|
||||||
{
|
{
|
||||||
Q_ASSERT(entry);
|
Q_ASSERT(entry);
|
||||||
|
if (!entry) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
QString cmdString = entry->resolveMultiplePlaceholders(entry->url());
|
QString cmdString = entry->resolveMultiplePlaceholders(entry->url());
|
||||||
if (cmdString.startsWith("cmd://")) {
|
if (cmdString.startsWith("cmd://")) {
|
||||||
// check if decision to execute command was stored
|
// check if decision to execute command was stored
|
||||||
@ -691,9 +696,9 @@ void DatabaseWidget::openUrlForEntry(Entry* entry)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
QString urlString = entry->webUrl();
|
QUrl url = QUrl(entry->url());
|
||||||
if (!urlString.isEmpty()) {
|
if (!url.isEmpty()) {
|
||||||
QDesktopServices::openUrl(urlString);
|
QDesktopServices::openUrl(url);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -54,11 +54,13 @@ EntryPreviewWidget::EntryPreviewWidget(QWidget* parent)
|
|||||||
m_ui->entryAttachmentsWidget->setReadOnly(true);
|
m_ui->entryAttachmentsWidget->setReadOnly(true);
|
||||||
m_ui->entryAttachmentsWidget->setButtonsVisible(false);
|
m_ui->entryAttachmentsWidget->setButtonsVisible(false);
|
||||||
|
|
||||||
|
connect(m_ui->entryUrlLabel, SIGNAL(linkActivated(QString)), SLOT(openEntryUrl()));
|
||||||
|
|
||||||
connect(m_ui->entryTotpButton, SIGNAL(toggled(bool)), m_ui->entryTotpWidget, SLOT(setVisible(bool)));
|
connect(m_ui->entryTotpButton, SIGNAL(toggled(bool)), m_ui->entryTotpWidget, SLOT(setVisible(bool)));
|
||||||
connect(m_ui->entryCloseButton, SIGNAL(clicked()), SLOT(hide()));
|
connect(m_ui->entryCloseButton, SIGNAL(clicked()), SLOT(hide()));
|
||||||
connect(m_ui->togglePasswordButton, SIGNAL(clicked(bool)), SLOT(setPasswordVisible(bool)));
|
connect(m_ui->togglePasswordButton, SIGNAL(clicked(bool)), SLOT(setPasswordVisible(bool)));
|
||||||
connect(m_ui->entryTabWidget, SIGNAL(tabBarClicked(int)), SLOT(updateTabIndexes()), Qt::QueuedConnection);
|
connect(m_ui->entryTabWidget, SIGNAL(tabBarClicked(int)), SLOT(updateTabIndexes()), Qt::QueuedConnection);
|
||||||
connect(&m_totpTimer, SIGNAL(timeout()), this, SLOT(updateTotpLabel()));
|
connect(&m_totpTimer, SIGNAL(timeout()), SLOT(updateTotpLabel()));
|
||||||
|
|
||||||
// Group
|
// Group
|
||||||
m_ui->groupCloseButton->setIcon(filePath()->icon("actions", "dialog-close"));
|
m_ui->groupCloseButton->setIcon(filePath()->icon("actions", "dialog-close"));
|
||||||
@ -197,11 +199,12 @@ void EntryPreviewWidget::updateEntryGeneralTab()
|
|||||||
}
|
}
|
||||||
|
|
||||||
m_ui->entryUrlLabel->setRawText(m_currentEntry->displayUrl());
|
m_ui->entryUrlLabel->setRawText(m_currentEntry->displayUrl());
|
||||||
const QString url = m_currentEntry->webUrl();
|
const QString url = m_currentEntry->url();
|
||||||
if (!url.isEmpty()) {
|
if (!url.isEmpty()) {
|
||||||
// URL is well formed and can be opened in a browser
|
// URL is well formed and can be opened in a browser
|
||||||
m_ui->entryUrlLabel->setUrl(url);
|
m_ui->entryUrlLabel->setUrl(url);
|
||||||
m_ui->entryUrlLabel->setCursor(Qt::PointingHandCursor);
|
m_ui->entryUrlLabel->setCursor(Qt::PointingHandCursor);
|
||||||
|
m_ui->entryUrlLabel->setOpenExternalLinks(false);
|
||||||
} else {
|
} else {
|
||||||
m_ui->entryUrlLabel->setUrl({});
|
m_ui->entryUrlLabel->setUrl({});
|
||||||
m_ui->entryUrlLabel->setCursor(Qt::ArrowCursor);
|
m_ui->entryUrlLabel->setCursor(Qt::ArrowCursor);
|
||||||
@ -327,6 +330,13 @@ void EntryPreviewWidget::updateTabIndexes()
|
|||||||
m_selectedTabGroup = m_ui->groupTabWidget->currentIndex();
|
m_selectedTabGroup = m_ui->groupTabWidget->currentIndex();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void EntryPreviewWidget::openEntryUrl()
|
||||||
|
{
|
||||||
|
if (m_currentEntry) {
|
||||||
|
emit entryUrlActivated(m_currentEntry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void EntryPreviewWidget::setTabEnabled(QTabWidget* tabWidget, QWidget* widget, bool enabled)
|
void EntryPreviewWidget::setTabEnabled(QTabWidget* tabWidget, QWidget* widget, bool enabled)
|
||||||
{
|
{
|
||||||
const int tabIndex = tabWidget->indexOf(widget);
|
const int tabIndex = tabWidget->indexOf(widget);
|
||||||
|
@ -43,6 +43,7 @@ public slots:
|
|||||||
|
|
||||||
signals:
|
signals:
|
||||||
void errorOccurred(const QString& error);
|
void errorOccurred(const QString& error);
|
||||||
|
void entryUrlActivated(Entry* entry);
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void updateEntryHeaderLine();
|
void updateEntryHeaderLine();
|
||||||
@ -63,6 +64,7 @@ private slots:
|
|||||||
|
|
||||||
void updateTotpLabel();
|
void updateTotpLabel();
|
||||||
void updateTabIndexes();
|
void updateTabIndexes();
|
||||||
|
void openEntryUrl();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void setTabEnabled(QTabWidget* tabWidget, QWidget* widget, bool enabled);
|
void setTabEnabled(QTabWidget* tabWidget, QWidget* widget, bool enabled);
|
||||||
|
@ -7,9 +7,11 @@
|
|||||||
#include <QFile>
|
#include <QFile>
|
||||||
#include <QFileInfo>
|
#include <QFileInfo>
|
||||||
#include <QMimeData>
|
#include <QMimeData>
|
||||||
|
#include <QProcessEnvironment>
|
||||||
#include <QTemporaryFile>
|
#include <QTemporaryFile>
|
||||||
|
|
||||||
#include "EntryAttachmentsModel.h"
|
#include "EntryAttachmentsModel.h"
|
||||||
|
#include "config-keepassx.h"
|
||||||
#include "core/Config.h"
|
#include "core/Config.h"
|
||||||
#include "core/EntryAttachments.h"
|
#include "core/EntryAttachments.h"
|
||||||
#include "core/Tools.h"
|
#include "core/Tools.h"
|
||||||
@ -324,7 +326,12 @@ bool EntryAttachmentsWidget::openAttachment(const QModelIndex& index, QString& e
|
|||||||
const QByteArray attachmentData = m_entryAttachments->value(filename);
|
const QByteArray attachmentData = m_entryAttachments->value(filename);
|
||||||
|
|
||||||
// tmp file will be removed once the database (or the application) has been closed
|
// tmp file will be removed once the database (or the application) has been closed
|
||||||
|
#ifdef KEEPASSXC_DIST_SNAP
|
||||||
|
const QString tmpFileTemplate =
|
||||||
|
QString("%1/XXXXXX.%2").arg(QProcessEnvironment::systemEnvironment().value("SNAP_USER_DATA"), filename);
|
||||||
|
#else
|
||||||
const QString tmpFileTemplate = QDir::temp().absoluteFilePath(QString("XXXXXX.").append(filename));
|
const QString tmpFileTemplate = QDir::temp().absoluteFilePath(QString("XXXXXX.").append(filename));
|
||||||
|
#endif
|
||||||
|
|
||||||
QScopedPointer<QTemporaryFile> tmpFile(new QTemporaryFile(tmpFileTemplate, this));
|
QScopedPointer<QTemporaryFile> tmpFile(new QTemporaryFile(tmpFileTemplate, this));
|
||||||
|
|
||||||
|
@ -231,6 +231,11 @@ if(WITH_XC_FDOSECRETS)
|
|||||||
LIBS testsupport ${TEST_LIBRARIES})
|
LIBS testsupport ${TEST_LIBRARIES})
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if(WITH_XC_BROWSER)
|
||||||
|
add_unit_test(NAME testbrowser SOURCES TestBrowser.cpp
|
||||||
|
LIBS ${TEST_LIBRARIES})
|
||||||
|
endif()
|
||||||
|
|
||||||
|
|
||||||
if(WITH_GUI_TESTS)
|
if(WITH_GUI_TESTS)
|
||||||
# CLI clip tests need X environment on Linux
|
# CLI clip tests need X environment on Linux
|
||||||
|
@ -157,6 +157,7 @@ void TestAutoType::testGlobalAutoTypeWithNoMatch()
|
|||||||
void TestAutoType::testGlobalAutoTypeWithOneMatch()
|
void TestAutoType::testGlobalAutoTypeWithOneMatch()
|
||||||
{
|
{
|
||||||
m_test->setActiveWindowTitle("custom window");
|
m_test->setActiveWindowTitle("custom window");
|
||||||
|
m_test->triggerGlobalAutoType();
|
||||||
m_autoType->performGlobalAutoType(m_dbList);
|
m_autoType->performGlobalAutoType(m_dbList);
|
||||||
|
|
||||||
QCOMPARE(m_test->actionChars(), QString("%1association%2").arg(m_entry1->username()).arg(m_entry1->password()));
|
QCOMPARE(m_test->actionChars(), QString("%1association%2").arg(m_entry1->username()).arg(m_entry1->password()));
|
||||||
@ -167,6 +168,7 @@ void TestAutoType::testGlobalAutoTypeTitleMatch()
|
|||||||
config()->set("AutoTypeEntryTitleMatch", true);
|
config()->set("AutoTypeEntryTitleMatch", true);
|
||||||
|
|
||||||
m_test->setActiveWindowTitle("An Entry Title!");
|
m_test->setActiveWindowTitle("An Entry Title!");
|
||||||
|
m_test->triggerGlobalAutoType();
|
||||||
m_autoType->performGlobalAutoType(m_dbList);
|
m_autoType->performGlobalAutoType(m_dbList);
|
||||||
|
|
||||||
QCOMPARE(m_test->actionChars(), QString("%1%2").arg(m_entry2->password(), m_test->keyToString(Qt::Key_Enter)));
|
QCOMPARE(m_test->actionChars(), QString("%1%2").arg(m_entry2->password(), m_test->keyToString(Qt::Key_Enter)));
|
||||||
@ -177,6 +179,7 @@ void TestAutoType::testGlobalAutoTypeUrlMatch()
|
|||||||
config()->set("AutoTypeEntryTitleMatch", true);
|
config()->set("AutoTypeEntryTitleMatch", true);
|
||||||
|
|
||||||
m_test->setActiveWindowTitle("Dummy - http://example.org/ - <My Browser>");
|
m_test->setActiveWindowTitle("Dummy - http://example.org/ - <My Browser>");
|
||||||
|
m_test->triggerGlobalAutoType();
|
||||||
m_autoType->performGlobalAutoType(m_dbList);
|
m_autoType->performGlobalAutoType(m_dbList);
|
||||||
|
|
||||||
QCOMPARE(m_test->actionChars(), QString("%1%2").arg(m_entry5->password(), m_test->keyToString(Qt::Key_Enter)));
|
QCOMPARE(m_test->actionChars(), QString("%1%2").arg(m_entry5->password(), m_test->keyToString(Qt::Key_Enter)));
|
||||||
@ -187,6 +190,7 @@ void TestAutoType::testGlobalAutoTypeUrlSubdomainMatch()
|
|||||||
config()->set("AutoTypeEntryTitleMatch", true);
|
config()->set("AutoTypeEntryTitleMatch", true);
|
||||||
|
|
||||||
m_test->setActiveWindowTitle("Dummy - http://sub.example.org/ - <My Browser>");
|
m_test->setActiveWindowTitle("Dummy - http://sub.example.org/ - <My Browser>");
|
||||||
|
m_test->triggerGlobalAutoType();
|
||||||
m_autoType->performGlobalAutoType(m_dbList);
|
m_autoType->performGlobalAutoType(m_dbList);
|
||||||
|
|
||||||
QCOMPARE(m_test->actionChars(), QString("%1%2").arg(m_entry5->password(), m_test->keyToString(Qt::Key_Enter)));
|
QCOMPARE(m_test->actionChars(), QString("%1%2").arg(m_entry5->password(), m_test->keyToString(Qt::Key_Enter)));
|
||||||
@ -195,6 +199,7 @@ void TestAutoType::testGlobalAutoTypeUrlSubdomainMatch()
|
|||||||
void TestAutoType::testGlobalAutoTypeTitleMatchDisabled()
|
void TestAutoType::testGlobalAutoTypeTitleMatchDisabled()
|
||||||
{
|
{
|
||||||
m_test->setActiveWindowTitle("An Entry Title!");
|
m_test->setActiveWindowTitle("An Entry Title!");
|
||||||
|
m_test->triggerGlobalAutoType();
|
||||||
MessageBox::setNextAnswer(MessageBox::Ok);
|
MessageBox::setNextAnswer(MessageBox::Ok);
|
||||||
m_autoType->performGlobalAutoType(m_dbList);
|
m_autoType->performGlobalAutoType(m_dbList);
|
||||||
|
|
||||||
@ -205,58 +210,68 @@ void TestAutoType::testGlobalAutoTypeRegExp()
|
|||||||
{
|
{
|
||||||
// substring matches are ok
|
// substring matches are ok
|
||||||
m_test->setActiveWindowTitle("lorem REGEX1 ipsum");
|
m_test->setActiveWindowTitle("lorem REGEX1 ipsum");
|
||||||
|
m_test->triggerGlobalAutoType();
|
||||||
m_autoType->performGlobalAutoType(m_dbList);
|
m_autoType->performGlobalAutoType(m_dbList);
|
||||||
QCOMPARE(m_test->actionChars(), QString("regex1"));
|
QCOMPARE(m_test->actionChars(), QString("regex1"));
|
||||||
m_test->clearActions();
|
m_test->clearActions();
|
||||||
|
|
||||||
// should be case-insensitive
|
// should be case-insensitive
|
||||||
m_test->setActiveWindowTitle("lorem regex1 ipsum");
|
m_test->setActiveWindowTitle("lorem regex1 ipsum");
|
||||||
|
m_test->triggerGlobalAutoType();
|
||||||
m_autoType->performGlobalAutoType(m_dbList);
|
m_autoType->performGlobalAutoType(m_dbList);
|
||||||
QCOMPARE(m_test->actionChars(), QString("regex1"));
|
QCOMPARE(m_test->actionChars(), QString("regex1"));
|
||||||
m_test->clearActions();
|
m_test->clearActions();
|
||||||
|
|
||||||
// exact match
|
// exact match
|
||||||
m_test->setActiveWindowTitle("REGEX2");
|
m_test->setActiveWindowTitle("REGEX2");
|
||||||
|
m_test->triggerGlobalAutoType();
|
||||||
m_autoType->performGlobalAutoType(m_dbList);
|
m_autoType->performGlobalAutoType(m_dbList);
|
||||||
QCOMPARE(m_test->actionChars(), QString("regex2"));
|
QCOMPARE(m_test->actionChars(), QString("regex2"));
|
||||||
m_test->clearActions();
|
m_test->clearActions();
|
||||||
|
|
||||||
// a bit more complicated regex
|
// a bit more complicated regex
|
||||||
m_test->setActiveWindowTitle("REGEX3-R2D2");
|
m_test->setActiveWindowTitle("REGEX3-R2D2");
|
||||||
|
m_test->triggerGlobalAutoType();
|
||||||
m_autoType->performGlobalAutoType(m_dbList);
|
m_autoType->performGlobalAutoType(m_dbList);
|
||||||
QCOMPARE(m_test->actionChars(), QString("regex3"));
|
QCOMPARE(m_test->actionChars(), QString("regex3"));
|
||||||
m_test->clearActions();
|
m_test->clearActions();
|
||||||
|
|
||||||
// with custom attributes
|
// with custom attributes
|
||||||
m_test->setActiveWindowTitle("CustomAttr1");
|
m_test->setActiveWindowTitle("CustomAttr1");
|
||||||
|
m_test->triggerGlobalAutoType();
|
||||||
m_autoType->performGlobalAutoType(m_dbList);
|
m_autoType->performGlobalAutoType(m_dbList);
|
||||||
QCOMPARE(m_test->actionChars(), QString("custom_attr:Attribute"));
|
QCOMPARE(m_test->actionChars(), QString("custom_attr:Attribute"));
|
||||||
m_test->clearActions();
|
m_test->clearActions();
|
||||||
|
|
||||||
// with (non uppercase) undefined custom attributes
|
// with (non uppercase) undefined custom attributes
|
||||||
m_test->setActiveWindowTitle("CustomAttr2");
|
m_test->setActiveWindowTitle("CustomAttr2");
|
||||||
|
m_test->triggerGlobalAutoType();
|
||||||
m_autoType->performGlobalAutoType(m_dbList);
|
m_autoType->performGlobalAutoType(m_dbList);
|
||||||
QCOMPARE(m_test->actionChars(), QString(""));
|
QCOMPARE(m_test->actionChars(), QString(""));
|
||||||
m_test->clearActions();
|
m_test->clearActions();
|
||||||
|
|
||||||
// with mixedcase default attributes
|
// with mixedcase default attributes
|
||||||
m_test->setActiveWindowTitle("CustomAttr3");
|
m_test->setActiveWindowTitle("CustomAttr3");
|
||||||
|
m_test->triggerGlobalAutoType();
|
||||||
m_autoType->performGlobalAutoType(m_dbList);
|
m_autoType->performGlobalAutoType(m_dbList);
|
||||||
QCOMPARE(m_test->actionChars(), QString("custom_attr"));
|
QCOMPARE(m_test->actionChars(), QString("custom_attr"));
|
||||||
m_test->clearActions();
|
m_test->clearActions();
|
||||||
|
|
||||||
// with resolve placeholders in window association title
|
// with resolve placeholders in window association title
|
||||||
m_test->setActiveWindowTitle("AttrValueFirst");
|
m_test->setActiveWindowTitle("AttrValueFirst");
|
||||||
|
m_test->triggerGlobalAutoType();
|
||||||
m_autoType->performGlobalAutoType(m_dbList);
|
m_autoType->performGlobalAutoType(m_dbList);
|
||||||
QCOMPARE(m_test->actionChars(), QString("custom_attr_first"));
|
QCOMPARE(m_test->actionChars(), QString("custom_attr_first"));
|
||||||
m_test->clearActions();
|
m_test->clearActions();
|
||||||
|
|
||||||
m_test->setActiveWindowTitle("lorem AttrValueFirstAndAttrValueSecond ipsum");
|
m_test->setActiveWindowTitle("lorem AttrValueFirstAndAttrValueSecond ipsum");
|
||||||
|
m_test->triggerGlobalAutoType();
|
||||||
m_autoType->performGlobalAutoType(m_dbList);
|
m_autoType->performGlobalAutoType(m_dbList);
|
||||||
QCOMPARE(m_test->actionChars(), QString("custom_attr_first_and_second"));
|
QCOMPARE(m_test->actionChars(), QString("custom_attr_first_and_second"));
|
||||||
m_test->clearActions();
|
m_test->clearActions();
|
||||||
|
|
||||||
m_test->setActiveWindowTitle("lorem AttrValueThird ipsum");
|
m_test->setActiveWindowTitle("lorem AttrValueThird ipsum");
|
||||||
|
m_test->triggerGlobalAutoType();
|
||||||
m_autoType->performGlobalAutoType(m_dbList);
|
m_autoType->performGlobalAutoType(m_dbList);
|
||||||
QCOMPARE(m_test->actionChars(), QString("custom_attr_third"));
|
QCOMPARE(m_test->actionChars(), QString("custom_attr_third"));
|
||||||
m_test->clearActions();
|
m_test->clearActions();
|
||||||
|
344
tests/TestBrowser.cpp
Normal file
344
tests/TestBrowser.cpp
Normal file
@ -0,0 +1,344 @@
|
|||||||
|
/*
|
||||||
|
* 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 "TestBrowser.h"
|
||||||
|
#include "TestGlobal.h"
|
||||||
|
#include "crypto/Crypto.h"
|
||||||
|
#include "sodium/crypto_box.h"
|
||||||
|
#include "browser/BrowserSettings.h"
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
|
QTEST_GUILESS_MAIN(TestBrowser)
|
||||||
|
|
||||||
|
const QString PUBLICKEY = "UIIPObeoya1G8g1M5omgyoPR/j1mR1HlYHu0wHCgMhA=";
|
||||||
|
const QString SECRETKEY = "B8ei4ZjQJkWzZU2SK/tBsrYRwp+6ztEMf5GFQV+i0yI=";
|
||||||
|
const QString SERVERPUBLICKEY = "lKnbLhrVCOqzEjuNoUz1xj9EZlz8xeO4miZBvLrUPVQ=";
|
||||||
|
const QString SERVERSECRETKEY = "tbPQcghxfOgbmsnEqG2qMIj1W2+nh+lOJcNsHncaz1Q=";
|
||||||
|
const QString NONCE = "zBKdvTjL5bgWaKMCTut/8soM/uoMrFoZ";
|
||||||
|
const QString CLIENTID = "testClient";
|
||||||
|
|
||||||
|
void TestBrowser::initTestCase()
|
||||||
|
{
|
||||||
|
QVERIFY(Crypto::init());
|
||||||
|
m_browserService.reset(new BrowserService(nullptr));
|
||||||
|
m_browserAction.reset(new BrowserAction(*m_browserService.data()));
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestBrowser::cleanupTestCase()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for BrowserAction
|
||||||
|
*/
|
||||||
|
|
||||||
|
void TestBrowser::testChangePublicKeys()
|
||||||
|
{
|
||||||
|
QJsonObject json;
|
||||||
|
json["action"] = "change-public-keys";
|
||||||
|
json["publicKey"] = PUBLICKEY;
|
||||||
|
json["nonce"] = NONCE;
|
||||||
|
|
||||||
|
auto response = m_browserAction->handleAction(json);
|
||||||
|
QCOMPARE(response["action"].toString(), QString("change-public-keys"));
|
||||||
|
QCOMPARE(response["publicKey"].toString() == PUBLICKEY, false);
|
||||||
|
QCOMPARE(response["success"].toString(), QString("true"));
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestBrowser::testEncryptMessage()
|
||||||
|
{
|
||||||
|
QJsonObject message;
|
||||||
|
message["action"] = "test-action";
|
||||||
|
|
||||||
|
m_browserAction->m_publicKey = SERVERPUBLICKEY;
|
||||||
|
m_browserAction->m_secretKey = SERVERSECRETKEY;
|
||||||
|
m_browserAction->m_clientPublicKey = PUBLICKEY;
|
||||||
|
auto encrypted = m_browserAction->encryptMessage(message, NONCE);
|
||||||
|
|
||||||
|
QCOMPARE(encrypted, QString("+zjtntnk4rGWSl/Ph7Vqip/swvgeupk4lNgHEm2OO3ujNr0OMz6eQtGwjtsj+/rP"));
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestBrowser::testDecryptMessage()
|
||||||
|
{
|
||||||
|
QString message = "+zjtntnk4rGWSl/Ph7Vqip/swvgeupk4lNgHEm2OO3ujNr0OMz6eQtGwjtsj+/rP";
|
||||||
|
m_browserAction->m_publicKey = SERVERPUBLICKEY;
|
||||||
|
m_browserAction->m_secretKey = SERVERSECRETKEY;
|
||||||
|
m_browserAction->m_clientPublicKey = PUBLICKEY;
|
||||||
|
auto decrypted = m_browserAction->decryptMessage(message, NONCE);
|
||||||
|
|
||||||
|
QCOMPARE(decrypted["action"].toString(), QString("test-action"));
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestBrowser::testGetBase64FromKey()
|
||||||
|
{
|
||||||
|
unsigned char pk[crypto_box_PUBLICKEYBYTES];
|
||||||
|
|
||||||
|
for (unsigned int i = 0; i < crypto_box_PUBLICKEYBYTES; ++i) {
|
||||||
|
pk[i] = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto response = m_browserAction->getBase64FromKey(pk, crypto_box_PUBLICKEYBYTES);
|
||||||
|
QCOMPARE(response, QString("AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8="));
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestBrowser::testIncrementNonce()
|
||||||
|
{
|
||||||
|
auto result = m_browserAction->incrementNonce(NONCE);
|
||||||
|
QCOMPARE(result, QString("zRKdvTjL5bgWaKMCTut/8soM/uoMrFoZ"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for BrowserService
|
||||||
|
*/
|
||||||
|
void TestBrowser::testBaseDomain()
|
||||||
|
{
|
||||||
|
QString url1 = "https://another.example.co.uk";
|
||||||
|
QString url2 = "https://www.example.com";
|
||||||
|
QString url3 = "http://test.net";
|
||||||
|
QString url4 = "http://so.many.subdomains.co.jp";
|
||||||
|
|
||||||
|
QString res1 = m_browserService->baseDomain(url1);
|
||||||
|
QString res2 = m_browserService->baseDomain(url2);
|
||||||
|
QString res3 = m_browserService->baseDomain(url3);
|
||||||
|
QString res4 = m_browserService->baseDomain(url4);
|
||||||
|
|
||||||
|
QCOMPARE(res1, QString("example.co.uk"));
|
||||||
|
QCOMPARE(res2, QString("example.com"));
|
||||||
|
QCOMPARE(res3, QString("test.net"));
|
||||||
|
QCOMPARE(res4, QString("subdomains.co.jp"));
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestBrowser::testSortPriority()
|
||||||
|
{
|
||||||
|
QString host = "github.com";
|
||||||
|
QString submitUrl = "https://github.com/session";
|
||||||
|
QString baseSubmitUrl = "https://github.com";
|
||||||
|
|
||||||
|
QScopedPointer<Entry> entry1(new Entry());
|
||||||
|
QScopedPointer<Entry> entry2(new Entry());
|
||||||
|
QScopedPointer<Entry> entry3(new Entry());
|
||||||
|
QScopedPointer<Entry> entry4(new Entry());
|
||||||
|
QScopedPointer<Entry> entry5(new Entry());
|
||||||
|
QScopedPointer<Entry> entry6(new Entry());
|
||||||
|
QScopedPointer<Entry> entry7(new Entry());
|
||||||
|
QScopedPointer<Entry> entry8(new Entry());
|
||||||
|
QScopedPointer<Entry> entry9(new Entry());
|
||||||
|
QScopedPointer<Entry> entry10(new Entry());
|
||||||
|
|
||||||
|
entry1->setUrl("https://github.com/login");
|
||||||
|
entry2->setUrl("https://github.com/login");
|
||||||
|
entry3->setUrl("https://github.com/");
|
||||||
|
entry4->setUrl("github.com/login");
|
||||||
|
entry5->setUrl("http://github.com");
|
||||||
|
entry6->setUrl("http://github.com/login");
|
||||||
|
entry7->setUrl("github.com");
|
||||||
|
entry8->setUrl("github.com/login");
|
||||||
|
entry9->setUrl("https://github");
|
||||||
|
entry10->setUrl("github.com");
|
||||||
|
|
||||||
|
// The extension uses the submitUrl as default for comparison
|
||||||
|
auto res1 = m_browserService->sortPriority(entry1.data(), host, "https://github.com/login", baseSubmitUrl);
|
||||||
|
auto res2 = m_browserService->sortPriority(entry2.data(), host, submitUrl, baseSubmitUrl);
|
||||||
|
auto res3 = m_browserService->sortPriority(entry3.data(), host, submitUrl, baseSubmitUrl);
|
||||||
|
auto res4 = m_browserService->sortPriority(entry4.data(), host, submitUrl, baseSubmitUrl);
|
||||||
|
auto res5 = m_browserService->sortPriority(entry5.data(), host, submitUrl, baseSubmitUrl);
|
||||||
|
auto res6 = m_browserService->sortPriority(entry6.data(), host, submitUrl, baseSubmitUrl);
|
||||||
|
auto res7 = m_browserService->sortPriority(entry7.data(), host, submitUrl, baseSubmitUrl);
|
||||||
|
auto res8 = m_browserService->sortPriority(entry8.data(), host, submitUrl, baseSubmitUrl);
|
||||||
|
auto res9 = m_browserService->sortPriority(entry9.data(), host, submitUrl, baseSubmitUrl);
|
||||||
|
auto res10 = m_browserService->sortPriority(entry10.data(), host, submitUrl, baseSubmitUrl);
|
||||||
|
|
||||||
|
QCOMPARE(res1, 100);
|
||||||
|
QCOMPARE(res2, 40);
|
||||||
|
QCOMPARE(res3, 90);
|
||||||
|
QCOMPARE(res4, 0);
|
||||||
|
QCOMPARE(res5, 0);
|
||||||
|
QCOMPARE(res6, 0);
|
||||||
|
QCOMPARE(res7, 0);
|
||||||
|
QCOMPARE(res8, 0);
|
||||||
|
QCOMPARE(res9, 90);
|
||||||
|
QCOMPARE(res10, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestBrowser::testSearchEntries()
|
||||||
|
{
|
||||||
|
auto db = QSharedPointer<Database>::create();
|
||||||
|
auto* root = db->rootGroup();
|
||||||
|
|
||||||
|
QList<QString> urls;
|
||||||
|
urls.push_back("https://github.com/login_page");
|
||||||
|
urls.push_back("https://github.com/login");
|
||||||
|
urls.push_back("https://github.com/");
|
||||||
|
urls.push_back("github.com/login");
|
||||||
|
urls.push_back("http://github.com");
|
||||||
|
urls.push_back("http://github.com/login");
|
||||||
|
urls.push_back("github.com");
|
||||||
|
urls.push_back("github.com/login");
|
||||||
|
urls.push_back("https://github");
|
||||||
|
urls.push_back("github.com");
|
||||||
|
|
||||||
|
for (int i = 0; i < urls.length(); ++i) {
|
||||||
|
auto entry = new Entry();
|
||||||
|
entry->setGroup(root);
|
||||||
|
entry->beginUpdate();
|
||||||
|
entry->setUrl(urls[i]);
|
||||||
|
entry->setUsername(QString("User %1").arg(i));
|
||||||
|
entry->endUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
|
browserSettings()->setMatchUrlScheme(false);
|
||||||
|
auto result = m_browserService->searchEntries(db, "github.com", "https://github.com"); // db, hostname, url
|
||||||
|
QCOMPARE(result.length(), 7);
|
||||||
|
QCOMPARE(result[0]->url(), QString("https://github.com/login_page"));
|
||||||
|
QCOMPARE(result[1]->url(), QString("https://github.com/login"));
|
||||||
|
QCOMPARE(result[2]->url(), QString("https://github.com/"));
|
||||||
|
QCOMPARE(result[3]->url(), QString("http://github.com"));
|
||||||
|
QCOMPARE(result[4]->url(), QString("http://github.com/login"));
|
||||||
|
QCOMPARE(result[5]->url(), QString("github.com"));
|
||||||
|
QCOMPARE(result[6]->url(), QString("github.com")) ;
|
||||||
|
|
||||||
|
// With matching there should be only 5 results
|
||||||
|
browserSettings()->setMatchUrlScheme(true);
|
||||||
|
result = m_browserService->searchEntries(db, "github.com", "https://github.com"); // db, hostname, url
|
||||||
|
QCOMPARE(result.length(), 5);
|
||||||
|
QCOMPARE(result[0]->url(), QString("https://github.com/login_page"));
|
||||||
|
QCOMPARE(result[1]->url(), QString("https://github.com/login"));
|
||||||
|
QCOMPARE(result[2]->url(), QString("https://github.com/"));
|
||||||
|
QCOMPARE(result[3]->url(), QString("github.com"));
|
||||||
|
QCOMPARE(result[4]->url(), QString("github.com"));
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestBrowser::testSearchEntriesWithPort()
|
||||||
|
{
|
||||||
|
auto db = QSharedPointer<Database>::create();
|
||||||
|
auto* root = db->rootGroup();
|
||||||
|
|
||||||
|
QList<QString> urls;
|
||||||
|
urls.push_back("http://127.0.0.1:443");
|
||||||
|
urls.push_back("http://127.0.0.1:80");
|
||||||
|
|
||||||
|
for (int i = 0; i < urls.length(); ++i) {
|
||||||
|
auto entry = new Entry();
|
||||||
|
entry->setGroup(root);
|
||||||
|
entry->beginUpdate();
|
||||||
|
entry->setUrl(urls[i]);
|
||||||
|
entry->setUsername(QString("User %1").arg(i));
|
||||||
|
entry->endUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto result = m_browserService->searchEntries(db, "127.0.0.1", "http://127.0.0.1:443"); // db, hostname, url
|
||||||
|
QCOMPARE(result.length(), 1);
|
||||||
|
QCOMPARE(result[0]->url(), QString("http://127.0.0.1:443"));
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestBrowser::testSortEntries()
|
||||||
|
{
|
||||||
|
auto db = QSharedPointer<Database>::create();
|
||||||
|
auto* root = db->rootGroup();
|
||||||
|
|
||||||
|
QList<QString> urls;
|
||||||
|
urls.push_back("https://github.com/login_page");
|
||||||
|
urls.push_back("https://github.com/login");
|
||||||
|
urls.push_back("https://github.com/");
|
||||||
|
urls.push_back("github.com/login");
|
||||||
|
urls.push_back("http://github.com");
|
||||||
|
urls.push_back("http://github.com/login");
|
||||||
|
urls.push_back("github.com");
|
||||||
|
urls.push_back("github.com/login");
|
||||||
|
urls.push_back("https://github");
|
||||||
|
urls.push_back("github.com");
|
||||||
|
|
||||||
|
QList<Entry*> entries;
|
||||||
|
for (int i = 0; i < urls.length(); ++i) {
|
||||||
|
auto entry = new Entry();
|
||||||
|
entry->setGroup(root);
|
||||||
|
entry->beginUpdate();
|
||||||
|
entry->setUrl(urls[i]);
|
||||||
|
entry->setUsername(QString("User %1").arg(i));
|
||||||
|
entry->endUpdate();
|
||||||
|
entries.push_back(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
browserSettings()->setBestMatchOnly(false);
|
||||||
|
auto result = m_browserService->sortEntries(entries, "github.com", "https://github.com/session"); // entries, host, submitUrl
|
||||||
|
QCOMPARE(result.size(), 10);
|
||||||
|
QCOMPARE(result[0]->username(), QString("User 2"));
|
||||||
|
QCOMPARE(result[0]->url(), QString("https://github.com/"));
|
||||||
|
QCOMPARE(result[1]->username(), QString("User 8"));
|
||||||
|
QCOMPARE(result[1]->url(), QString("https://github"));
|
||||||
|
QCOMPARE(result[2]->username(), QString("User 0"));
|
||||||
|
QCOMPARE(result[2]->url(), QString("https://github.com/login_page"));
|
||||||
|
QCOMPARE(result[3]->username(), QString("User 1"));
|
||||||
|
QCOMPARE(result[3]->url(), QString("https://github.com/login"));
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestBrowser::testGetDatabaseGroups()
|
||||||
|
{
|
||||||
|
auto db = QSharedPointer<Database>::create();
|
||||||
|
auto* root = db->rootGroup();
|
||||||
|
|
||||||
|
QScopedPointer<Group> group1(new Group());
|
||||||
|
group1->setParent(root);
|
||||||
|
group1->setName("group1");
|
||||||
|
|
||||||
|
QScopedPointer<Group> group2(new Group());
|
||||||
|
group2->setParent(root);
|
||||||
|
group2->setName("group2");
|
||||||
|
|
||||||
|
QScopedPointer<Group> group3(new Group());
|
||||||
|
group3->setParent(root);
|
||||||
|
group3->setName("group3");
|
||||||
|
|
||||||
|
QScopedPointer<Group> group2_1(new Group());
|
||||||
|
group2_1->setParent(group2.data());
|
||||||
|
group2_1->setName("group2_1");
|
||||||
|
|
||||||
|
QScopedPointer<Group> group2_2(new Group());
|
||||||
|
group2_2->setParent(group2.data());
|
||||||
|
group2_2->setName("group2_2");
|
||||||
|
|
||||||
|
QScopedPointer<Group> group2_1_1(new Group());
|
||||||
|
group2_1_1->setParent(group2_1.data());
|
||||||
|
group2_1_1->setName("group2_1_1");
|
||||||
|
|
||||||
|
auto result = m_browserService->getDatabaseGroups(db);
|
||||||
|
QCOMPARE(result.length(), 1);
|
||||||
|
|
||||||
|
auto groups = result["groups"].toArray();
|
||||||
|
auto first = groups.at(0);
|
||||||
|
auto children = first.toObject()["children"].toArray();
|
||||||
|
QCOMPARE(first.toObject()["name"].toString(), QString("Root"));
|
||||||
|
QCOMPARE(children.size(), 3);
|
||||||
|
|
||||||
|
auto firstChild = children.at(0);
|
||||||
|
auto secondChild = children.at(1);
|
||||||
|
auto thirdChild = children.at(2);
|
||||||
|
QCOMPARE(firstChild.toObject()["name"].toString(), QString("group1"));
|
||||||
|
QCOMPARE(secondChild.toObject()["name"].toString(), QString("group2"));
|
||||||
|
QCOMPARE(thirdChild.toObject()["name"].toString(), QString("group3"));
|
||||||
|
|
||||||
|
auto childrenOfSecond = secondChild.toObject()["children"].toArray();
|
||||||
|
auto firstOfCOS = childrenOfSecond.at(0);
|
||||||
|
auto secondOfCOS = childrenOfSecond.at(1);
|
||||||
|
QCOMPARE(firstOfCOS.toObject()["name"].toString(), QString("group2_1"));
|
||||||
|
QCOMPARE(secondOfCOS.toObject()["name"].toString(), QString("group2_2"));
|
||||||
|
|
||||||
|
auto lastChildren = firstOfCOS.toObject()["children"].toArray();
|
||||||
|
auto lastChild = lastChildren.at(0);
|
||||||
|
QCOMPARE(lastChild.toObject()["name"].toString(), QString("group2_1_1"));
|
||||||
|
}
|
53
tests/TestBrowser.h
Normal file
53
tests/TestBrowser.h
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
/*
|
||||||
|
* 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_TESTBROWSER_H
|
||||||
|
#define KEEPASSXC_TESTBROWSER_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
|
||||||
|
#include "browser/BrowserAction.h"
|
||||||
|
#include "browser/BrowserService.h"
|
||||||
|
#include "core/Group.h"
|
||||||
|
|
||||||
|
class TestBrowser : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void initTestCase();
|
||||||
|
void cleanupTestCase();
|
||||||
|
|
||||||
|
void testChangePublicKeys();
|
||||||
|
void testEncryptMessage();
|
||||||
|
void testDecryptMessage();
|
||||||
|
void testGetBase64FromKey();
|
||||||
|
void testIncrementNonce();
|
||||||
|
|
||||||
|
void testBaseDomain();
|
||||||
|
void testSortPriority();
|
||||||
|
void testSearchEntries();
|
||||||
|
void testSearchEntriesWithPort();
|
||||||
|
void testSortEntries();
|
||||||
|
void testGetDatabaseGroups();
|
||||||
|
|
||||||
|
private:
|
||||||
|
QScopedPointer<BrowserAction> m_browserAction;
|
||||||
|
QScopedPointer<BrowserService> m_browserService;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // KEEPASSXC_TESTBROWSER_H
|
Loading…
x
Reference in New Issue
Block a user