Publish Framework tray enhancements and idle fingerprint retry
This commit is contained in:
@@ -10,7 +10,7 @@ endif()
|
||||
set(CMAKE_CXX_STANDARD 23)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
set(CMAKE_AUTOMOC ON)
|
||||
find_package(Qt6 REQUIRED COMPONENTS Core Test)
|
||||
find_package(Qt6 REQUIRED COMPONENTS Core Qml Test)
|
||||
find_package(ECM REQUIRED NO_MODULE)
|
||||
list(APPEND CMAKE_MODULE_PATH "${ECM_MODULE_PATH}")
|
||||
include(ECMQtDeclareLoggingCategory)
|
||||
@@ -25,8 +25,9 @@ ecm_qt_declare_logging_category(sources
|
||||
add_executable(retrytest ${sources})
|
||||
target_include_directories(retrytest PRIVATE
|
||||
${CMAKE_CURRENT_BINARY_DIR} ${KSCREENLOCKER_SOURCE}/greeter)
|
||||
target_compile_definitions(retrytest PRIVATE HAVE_PAM_FAIL_DELAY)
|
||||
target_link_libraries(retrytest PRIVATE Qt6::Core Qt6::Test)
|
||||
target_compile_definitions(retrytest PRIVATE HAVE_PAM_FAIL_DELAY
|
||||
RETRY_QML="${CMAKE_CURRENT_SOURCE_DIR}/../../../plasma-always-show-unlock/FedoraToolsFingerprintRetry.qml")
|
||||
target_link_libraries(retrytest PRIVATE Qt6::Core Qt6::Qml Qt6::Test)
|
||||
enable_testing()
|
||||
add_test(NAME fingerprint-retry COMMAND retrytest)
|
||||
set_tests_properties(fingerprint-retry PROPERTIES TIMEOUT 30)
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
#include <QSignalSpy>
|
||||
#include <QTest>
|
||||
#include <QQmlComponent>
|
||||
#include <QQmlEngine>
|
||||
#include <security/pam_appl.h>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
@@ -38,9 +40,21 @@ int pam_authenticate(pam_handle_t *handle, int)
|
||||
if (handle->service == "transient" && handle->attempts == 1) {
|
||||
return PAM_AUTHINFO_UNAVAIL;
|
||||
}
|
||||
if (handle->service == "timeout" && handle->attempts == 1) {
|
||||
if (handle->service == "attempt-limit" && handle->attempts == 1) {
|
||||
return PAM_MAXTRIES;
|
||||
}
|
||||
if (handle->service == "idle" && handle->attempts == 1) {
|
||||
pam_message message{PAM_TEXT_INFO, "Test fingerprint instruction"};
|
||||
const pam_message *messages = &message;
|
||||
pam_response *response = nullptr;
|
||||
const auto &conversation = handle->conversation;
|
||||
const int result = conversation.conv(1, &messages, &response, conversation.appdata_ptr);
|
||||
if (response) {
|
||||
std::free(response->resp);
|
||||
std::free(response);
|
||||
}
|
||||
return result == PAM_SUCCESS ? PAM_AUTHINFO_UNAVAIL : result;
|
||||
}
|
||||
pam_message message{PAM_PROMPT_ECHO_OFF, "Test credential:"};
|
||||
const pam_message *messages = &message;
|
||||
pam_response *response = nullptr;
|
||||
@@ -61,11 +75,60 @@ class RetryTest : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
private Q_SLOTS:
|
||||
void idleFingerprintRetriesThroughQml_data()
|
||||
{
|
||||
QTest::addColumn<bool>("automaticRetry");
|
||||
QTest::newRow("automatic retry") << true;
|
||||
QTest::newRow("without the new retry") << false;
|
||||
}
|
||||
|
||||
void idleFingerprintRetriesThroughQml()
|
||||
{
|
||||
QFETCH(bool, automaticRetry);
|
||||
qmlRegisterUncreatableType<PamAuthenticator>("org.kde.kscreenlocker", 1, 0, "Authenticator", "Test instance only");
|
||||
auto password = std::make_unique<PamAuthenticator>(QStringLiteral("password"), QStringLiteral("test"));
|
||||
auto fingerprint = std::make_unique<PamAuthenticator>(QStringLiteral("idle"), QStringLiteral("test"), PamAuthenticator::Fingerprint);
|
||||
QSignalSpy passwordPrompts(password.get(), &PamAuthenticator::promptForSecret);
|
||||
QSignalSpy fingerprintPrompts(fingerprint.get(), &PamAuthenticator::promptForSecret);
|
||||
std::vector<std::unique_ptr<PamAuthenticator>> others;
|
||||
others.push_back(std::move(fingerprint));
|
||||
PamAuthenticators authenticators(std::move(password), std::move(others));
|
||||
QSignalSpy successes(&authenticators, &PamAuthenticators::succeeded);
|
||||
|
||||
QQmlEngine engine;
|
||||
QQmlComponent component(&engine, QUrl::fromLocalFile(QStringLiteral(RETRY_QML)));
|
||||
QVERIFY2(component.isReady(), qPrintable(component.errorString()));
|
||||
std::unique_ptr<QObject> retry(component.createWithInitialProperties({
|
||||
{QStringLiteral("auth"), QVariant::fromValue(&authenticators)},
|
||||
{QStringLiteral("fingerprintType"), int(PamAuthenticator::Fingerprint)},
|
||||
{QStringLiteral("interval"), 50},
|
||||
{QStringLiteral("enabled"), automaticRetry},
|
||||
}));
|
||||
QVERIFY2(retry, qPrintable(component.errorString()));
|
||||
|
||||
authenticators.startAuthenticating();
|
||||
QTRY_COMPARE(passwordPrompts.count(), 1);
|
||||
if (!automaticRetry) {
|
||||
QTest::qWait(200);
|
||||
QCOMPARE(fingerprintPrompts.count(), 0);
|
||||
// Reproduce today's recovery via Ctrl without any daemon restart.
|
||||
authenticators.startAuthenticating();
|
||||
}
|
||||
// With the helper enabled, no simulated input or manual start is needed.
|
||||
QTRY_COMPARE_WITH_TIMEOUT(fingerprintPrompts.count(), 1, 2000);
|
||||
QCOMPARE(passwordPrompts.count(), 1);
|
||||
QVERIFY(!authenticators.isUnlocked());
|
||||
QCOMPARE(successes.count(), 0);
|
||||
authenticators.respond("correct");
|
||||
QTRY_COMPARE(successes.count(), 1);
|
||||
QVERIFY(authenticators.isUnlocked());
|
||||
}
|
||||
|
||||
void retryWithActivePassword_data()
|
||||
{
|
||||
QTest::addColumn<QString>("service");
|
||||
QTest::newRow("temporary unavailability") << QStringLiteral("transient");
|
||||
QTest::newRow("attempt timeout") << QStringLiteral("timeout");
|
||||
QTest::newRow("exhausted attempts, manual retry") << QStringLiteral("attempt-limit");
|
||||
}
|
||||
|
||||
void retryWithActivePassword()
|
||||
|
||||
Reference in New Issue
Block a user