on xenial help sign in
Add support for lightdm guest users
commit2d4064cdb2fa285eedcbeb596ef9bab56d5995ff authorMichael Terry <mike@mterry.name> dateMon 19 Dec 2016, 15:28 (10 years ago) parentddb5511
15 files changed+188−18
plugins/LightDM/DBusGreeterList.cpp+3−3 plugins/LightDM/DBusGreeterList.h+1−1 plugins/LightDM/Greeter.cpp+33−3 plugins/LightDM/Greeter.h+6−2 plugins/LightDM/UsersModel.cpp+41 plugins/LightDM/UsersModel.h+7 plugins/LightDM/plugin.cpp+3−2 tests/mocks/AccountsService/AccountsService.cpp+2−2 tests/mocks/AccountsService/CMakeLists.txt+1 tests/mocks/liblightdm/MockController.cpp+37 tests/mocks/liblightdm/MockController.h+14 tests/mocks/liblightdm/MockGreeter.cpp+10−3 tests/plugins/LightDM/IntegratedLightDM/CMakeLists.txt+1 tests/plugins/LightDM/IntegratedLightDM/dbus.cpp+1 tests/qmltests/Greeter/tst_Greeter.qml+28−2
plugins/LightDM/DBusGreeterList.cpp+3−3
@@ -43,10 +43,10 @@ bool DBusGreeterList::entryIsLocked() const
4343 return !m_greeter->promptless();
4444}
4545
46−void DBusGreeterList::authenticationUserChangedHandler(const QString &user)
46+void DBusGreeterList::authenticationUserChangedHandler()
4747{
48− notifyPropertyChanged(QStringLiteral("ActiveEntry"), user);
49− Q_EMIT EntrySelected(user);
48+ notifyPropertyChanged(QStringLiteral("ActiveEntry"), m_greeter->authenticationUser());
49+ Q_EMIT EntrySelected(m_greeter->authenticationUser());
5050}
5151
5252void DBusGreeterList::promptlessChangedHandler()
plugins/LightDM/DBusGreeterList.h+1−1
@@ -47,7 +47,7 @@ Q_SIGNALS:
4747 void entryIsLockedChanged();
4848
4949private Q_SLOTS:
50− void authenticationUserChangedHandler(const QString &user);
50+ void authenticationUserChangedHandler();
5151 void promptlessChangedHandler();
5252
5353private:
plugins/LightDM/Greeter.cpp+33−3
@@ -19,6 +19,8 @@
1919#include "GreeterPrivate.h"
2020#include <libintl.h>
2121
22+static Greeter *singleton = nullptr;
23+
2224GreeterPrivate::GreeterPrivate(Greeter* parent)
2325 : m_greeter(new QLightDM::Greeter(parent)),
2426 m_active(false),
@@ -44,6 +46,19 @@ Greeter::Greeter(QObject* parent)
4446 d->m_greeter->connectSync();
4547}
4648
49+Greeter::~Greeter()
50+{
51+ singleton = nullptr;
52+}
53+
54+Greeter *Greeter::instance()
55+{
56+ if (!singleton) {
57+ singleton = new Greeter;
58+ }
59+ return singleton;
60+}
61+
4762bool Greeter::isActive() const
4863{
4964 Q_D(const Greeter);
@@ -86,7 +101,17 @@ bool Greeter::promptless() const
86101QString Greeter::selectUser() const
87102{
88103 Q_D(const Greeter);
89− return d->m_greeter->selectUserHint();
104+ if (hasGuestAccount() && d->m_greeter->selectGuestHint()) {
105+ return QStringLiteral("*guest");
106+ } else {
107+ return d->m_greeter->selectUserHint();
108+ }
109+}
110+
111+bool Greeter::hasGuestAccount() const
112+{
113+ Q_D(const Greeter);
114+ return d->m_greeter->hasGuestAccountHint();
90115}
91116
92117void Greeter::authenticate(const QString &username)
@@ -98,9 +123,14 @@ void Greeter::authenticate(const QString &username)
98123 Q_EMIT promptlessChanged();
99124 }
100125
101− d->m_greeter->authenticate(username);
126+ if (username == QStringLiteral("*guest")) {
127+ d->m_greeter->authenticateAsGuest();
128+ } else {
129+ d->m_greeter->authenticate(username);
130+ }
131+
102132 Q_EMIT isAuthenticatedChanged();
103− Q_EMIT authenticationUserChanged(username);
133+ Q_EMIT authenticationUserChanged();
104134}
105135
106136void Greeter::respond(const QString &response)
plugins/LightDM/Greeter.h+6−2
@@ -42,7 +42,8 @@ class Greeter : public QObject
4242 Q_PROPERTY(QObject *mock READ mock CONSTANT) // for testing
4343
4444public:
45− explicit Greeter(QObject* parent=0);
45+ static Greeter *instance();
46+ virtual ~Greeter();
4647
4748 bool isActive() const;
4849 bool isAuthenticated() const;
@@ -50,6 +51,7 @@ public:
5051 QString defaultSessionHint() const;
5152 bool promptless() const;
5253 QString selectUser() const;
54+ bool hasGuestAccount() const;
5355
5456 QObject *mock();
5557
@@ -63,7 +65,7 @@ Q_SIGNALS:
6365 void showMessage(const QString &text, bool isError);
6466 void showPrompt(const QString &text, bool isSecret, bool isDefaultPrompt);
6567 void authenticationComplete();
66− void authenticationUserChanged(const QString &user);
68+ void authenticationUserChanged();
6769 void isActiveChanged();
6870 void isAuthenticatedChanged();
6971 void promptlessChanged();
@@ -75,6 +77,8 @@ Q_SIGNALS:
7577 void requestAuthenticationUser(const QString &user);
7678
7779protected:
80+ explicit Greeter(QObject* parent=0);
81+
7882 GreeterPrivate * const d_ptr;
7983
8084 Q_DECLARE_PRIVATE(Greeter)
plugins/LightDM/UsersModel.cpp+41
@@ -16,10 +16,13 @@
1616 * Author: Michael Terry <michael.terry@canonical.com>
1717 */
1818
19+#include "Greeter.h"
1920#include "UsersModel.h"
2021#include <QLightDM/UsersModel>
2122#include <QtCore/QSortFilterProxyModel>
2223
24+#include <libintl.h>
25+
2326// First, we define an internal class that wraps LightDM's UsersModel. This
2427// class will modify some of the data coming from LightDM. For example, we
2528// modify any empty Real Names into just normal Names.
@@ -68,6 +71,44 @@ UsersModel::UsersModel(QObject* parent)
6871 sort(0);
6972}
7073
74+int UsersModel::guestRow() const
75+{
76+ return Greeter::instance()->hasGuestAccount() ?
77+ UnitySortFilterProxyModelQML::rowCount() : -1;
78+}
79+
80+int UsersModel::rowCount(const QModelIndex &parent) const
81+{
82+ auto count = UnitySortFilterProxyModelQML::rowCount(parent);
83+
84+ if (Greeter::instance()->hasGuestAccount() && !parent.isValid())
85+ count++;
86+
87+ return count;
88+}
89+
90+QModelIndex UsersModel::index(int row, int column, const QModelIndex &parent) const
91+{
92+ if (row == guestRow() && !parent.isValid()) {
93+ return createIndex(row, column);
94+ } else {
95+ return UnitySortFilterProxyModelQML::index(row, column, parent);
96+ }
97+}
98+
99+QVariant UsersModel::data(const QModelIndex &index, int role) const
100+{
101+ if (index.row() == guestRow() && index.column() == 0) {
102+ switch (role) {
103+ case QLightDM::UsersModel::NameRole: return QStringLiteral("*guest");
104+ case QLightDM::UsersModel::RealNameRole: return gettext("Guest Session");
105+ default: return QVariant();
106+ }
107+ }
108+
109+ return QSortFilterProxyModel::data(index, role);
110+}
111+
71112QObject *UsersModel::mock()
72113{
73114 // get through MangleModel down to QLightDM::UsersModel
plugins/LightDM/UsersModel.h+7
@@ -32,5 +32,12 @@ class UsersModel : public UnitySortFilterProxyModelQML
3232public:
3333 explicit UsersModel(QObject* parent=0);
3434
35+ int rowCount(const QModelIndex &parent = QModelIndex()) const override;
36+ QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override;
37+ QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
38+
3539 QObject *mock();
40+
41+private:
42+ int guestRow() const;
3643};
plugins/LightDM/plugin.cpp+3−2
@@ -32,13 +32,14 @@
3232
3333static QObject *greeter_provider(QQmlEngine *engine, QJSEngine *scriptEngine)
3434{
35− Q_UNUSED(engine)
3635 Q_UNUSED(scriptEngine)
3736
38− Greeter *greeter = new Greeter();
37+ Greeter *greeter = Greeter::instance();
3938 new DBusGreeter(greeter, QStringLiteral("/"));
4039 new DBusGreeterList(greeter, QStringLiteral("/list"));
4140
41+ engine->setObjectOwnership(greeter, QQmlEngine::CppOwnership);
42+
4243 return greeter;
4344}
4445
tests/mocks/AccountsService/AccountsService.cpp+2−2
@@ -121,8 +121,8 @@ QString AccountsService::backgroundFile() const
121121
122122 // Check if our mock user has a background set in liblightdm
123123 for (int i = 0; i < m_usersModel->count(); i++) {
124− if (m_usersModel->data(i, QLightDM::UsersModel::NameRole) == m_user) {
125− return m_usersModel->data(i, QLightDM::UsersModel::BackgroundPathRole).toString();
124+ if (m_usersModel->data(m_usersModel->index(i, 0), QLightDM::UsersModel::NameRole) == m_user) {
125+ return m_usersModel->data(m_usersModel->index(i, 0), QLightDM::UsersModel::BackgroundPathRole).toString();
126126 }
127127 }
128128
tests/mocks/AccountsService/CMakeLists.txt+1
@@ -6,6 +6,7 @@ include_directories(
66 )
77
88add_library(MockAccountsService-qml MODULE
9+ ${CMAKE_SOURCE_DIR}/plugins/LightDM/Greeter.cpp
910 ${CMAKE_SOURCE_DIR}/plugins/LightDM/UsersModel.cpp
1011 ${CMAKE_SOURCE_DIR}/plugins/Utils/unitysortfilterproxymodelqml.cpp
1112 AccountsService.cpp
tests/mocks/liblightdm/MockController.cpp+37
@@ -23,6 +23,8 @@ namespace QLightDM
2323
2424MockController::MockController(QObject *parent)
2525 : QObject(parent)
26+ , m_selectGuestHint(false)
27+ , m_hasGuestAccountHint(false)
2628 , m_fullSessions(
2729 {
2830 {"ubuntu", "Ubuntu"},
@@ -59,6 +61,15 @@ MockController *MockController::instance()
5961 return m_instance;
6062}
6163
64+void MockController::reset()
65+{
66+ setSelectUserHint("");
67+ setSelectGuestHint(false);
68+ setHasGuestAccountHint(false);
69+ setUserMode("full");
70+ setSessionMode("full");
71+}
72+
6273QString MockController::selectUserHint() const
6374{
6475 return m_selectUserHint;
@@ -72,6 +83,32 @@ void MockController::setSelectUserHint(const QString &selectUserHint)
7283 }
7384}
7485
86+bool MockController::selectGuestHint() const
87+{
88+ return m_selectGuestHint;
89+}
90+
91+void MockController::setSelectGuestHint(bool selectGuestHint)
92+{
93+ if (m_selectGuestHint != selectGuestHint) {
94+ m_selectGuestHint = selectGuestHint;
95+ Q_EMIT selectGuestHintChanged();
96+ }
97+}
98+
99+bool MockController::hasGuestAccountHint() const
100+{
101+ return m_hasGuestAccountHint;
102+}
103+
104+void MockController::setHasGuestAccountHint(bool hasGuestAccountHint)
105+{
106+ if (m_hasGuestAccountHint != hasGuestAccountHint) {
107+ m_hasGuestAccountHint = hasGuestAccountHint;
108+ Q_EMIT hasGuestAccountHintChanged();
109+ }
110+}
111+
75112QString MockController::userMode() const
76113{
77114 return m_userMode;
tests/mocks/liblightdm/MockController.h+14
@@ -27,6 +27,8 @@ class Q_DECL_EXPORT MockController : public QObject
2727 Q_OBJECT
2828
2929 Q_PROPERTY(QString selectUserHint READ selectUserHint WRITE setSelectUserHint NOTIFY selectUserHintChanged)
30+ Q_PROPERTY(bool selectGuestHint READ selectGuestHint WRITE setSelectGuestHint NOTIFY selectGuestHintChanged)
31+ Q_PROPERTY(bool hasGuestAccountHint READ hasGuestAccountHint WRITE setHasGuestAccountHint NOTIFY hasGuestAccountHintChanged)
3032
3133 // single, single-pin, single-passphrase, full
3234 Q_PROPERTY(QString userMode READ userMode WRITE setUserMode NOTIFY userModeChanged)
@@ -41,9 +43,17 @@ public:
4143 static MockController *instance();
4244 virtual ~MockController();
4345
46+ Q_INVOKABLE void reset();
47+
4448 QString selectUserHint() const;
4549 void setSelectUserHint(const QString &selectUserHint);
4650
51+ bool selectGuestHint() const;
52+ void setSelectGuestHint(bool selectGuestHint);
53+
54+ bool hasGuestAccountHint() const;
55+ void setHasGuestAccountHint(bool hasGuestAccountHint);
56+
4757 QString userMode() const;
4858 void setUserMode(const QString &userMode);
4959
@@ -64,6 +74,8 @@ public:
6474
6575Q_SIGNALS:
6676 void selectUserHintChanged();
77+ void selectGuestHintChanged();
78+ void hasGuestAccountHintChanged();
6779 void userModeChanged();
6880 void sessionModeChanged();
6981 void numSessionsChanged();
@@ -72,6 +84,8 @@ private:
7284 explicit MockController(QObject* parent=0);
7385
7486 QString m_selectUserHint;
87+ bool m_selectGuestHint;
88+ bool m_hasGuestAccountHint;
7589 QString m_userMode;
7690 QString m_sessionMode;
7791 QList<SessionItem> m_fullSessions;
tests/mocks/liblightdm/MockGreeter.cpp+10−3
@@ -51,7 +51,7 @@ QString Greeter::authenticationUser() const
5151
5252bool Greeter::hasGuestAccountHint() const
5353{
54− return true;
54+ return MockController::instance()->hasGuestAccountHint();
5555}
5656
5757QString Greeter::getHint(const QString &name) const
@@ -87,7 +87,7 @@ QString Greeter::selectUserHint() const
8787
8888bool Greeter::selectGuestHint() const
8989{
90− return false;
90+ return MockController::instance()->selectGuestHint();
9191}
9292
9393QString Greeter::autologinUserHint() const
@@ -173,7 +173,14 @@ void Greeter::handleAuthenticate()
173173}
174174
175175void Greeter::authenticateAsGuest()
176−{}
176+{
177+ Q_D(Greeter);
178+
179+ d->authenticated = true;
180+ d->authenticationUser = QString(); // this is what the real liblightdm does
181+ d->twoFactorDone = false;
182+ QTimer::singleShot(0, this, &Greeter::authenticationComplete);
183+}
177184
178185void Greeter::authenticateAutologin()
179186{}
tests/plugins/LightDM/IntegratedLightDM/CMakeLists.txt+1
@@ -66,6 +66,7 @@ add_unity8_uitest(GreeterSessions GreeterSessionsModelTestExec
6666# UsersModelTest
6767add_executable(GreeterUsersModelTestExec
6868 usersmodel.cpp
69+ ${CMAKE_SOURCE_DIR}/plugins/LightDM/Greeter.cpp
6970 ${CMAKE_SOURCE_DIR}/plugins/LightDM/UsersModel.cpp
7071 ${CMAKE_SOURCE_DIR}/plugins/Utils/unitysortfilterproxymodelqml.cpp
7172 )
tests/plugins/LightDM/IntegratedLightDM/dbus.cpp+1
@@ -81,6 +81,7 @@ private Q_SLOTS:
8181 void cleanup()
8282 {
8383 delete view;
84+ delete greeter; // reset singleton
8485 }
8586
8687 void testGetActiveEntry()
tests/qmltests/Greeter/tst_Greeter.qml+28−2
@@ -143,7 +143,7 @@ Item {
143143
144144 function init() {
145145 greeterSettings.lockedOutTime = 0;
146− LightDM.Greeter.mock.selectUserHint = "";
146+ LightDM.Greeter.mock.reset();
147147 greeter.failedLoginsDelayAttempts = 7;
148148 greeter.failedLoginsDelayMinutes = 5;
149149 teaseSpy.clear();
@@ -200,7 +200,10 @@ Item {
200200 var i = getIndexOf(name);
201201 compare(view.currentIndex, i);
202202 compare(AccountsService.user, name);
203− compare(LightDM.Greeter.authenticationUser, name);
203+ if (name === "*guest") // guest has no authenticationUser set
204+ compare(LightDM.Greeter.authenticationUser, "");
205+ else
206+ compare(LightDM.Greeter.authenticationUser, name);
204207 return i;
205208 }
206209
@@ -585,6 +588,29 @@ Item {
585588 verifySelected(LightDM.Users.data(0, LightDM.UserRoles.NameRole));
586589 }
587590
591+ function test_selectUserHintGuest() {
592+ LightDM.Greeter.mock.hasGuestAccountHint = true;
593+ LightDM.Greeter.mock.selectGuestHint = true;
594+ LightDM.Greeter.mock.selectUserHint = "info-prompt";
595+ resetLoader();
596+ verifySelected("*guest");
597+ }
598+
599+ function test_selectUserHintGuestWithNoGuest() {
600+ LightDM.Greeter.mock.selectGuestHint = true;
601+ LightDM.Greeter.mock.selectUserHint = "info-prompt";
602+ resetLoader();
603+ verifySelected("info-prompt");
604+ }
605+
606+ function test_hasGuestAccountHint() {
607+ LightDM.Greeter.mock.hasGuestAccountHint = true;
608+ resetLoader();
609+ var i = selectUser("*guest");
610+ compare(i, LightDM.Users.count - 1); // guest should be last
611+ verify(!view.locked);
612+ }
613+
588614 function test_fingerprintSuccess() {
589615 var index = selectUser("has-password");
590616 unlockAndShowGreeter(); // turn on lockscreen mode