on xenial help sign in
Add support for lightdm guest users
2d4064cMichael Terry
plugins/LightDM/DBusGreeterList.cpp+3−3
@@ -43,10 +43,10 @@ bool DBusGreeterList::entryIsLocked() const
43 return !m_greeter->promptless();
43 return !m_greeter->promptless();
44}
44}
45
45
46−void DBusGreeterList::authenticationUserChangedHandler(const QString &user)
46+void DBusGreeterList::authenticationUserChangedHandler()
47{
47{
48− notifyPropertyChanged(QStringLiteral("ActiveEntry"), user);
48+ notifyPropertyChanged(QStringLiteral("ActiveEntry"), m_greeter->authenticationUser());
49− Q_EMIT EntrySelected(user);
49+ Q_EMIT EntrySelected(m_greeter->authenticationUser());
50}
50}
51
51
52void DBusGreeterList::promptlessChangedHandler()
52void DBusGreeterList::promptlessChangedHandler()
plugins/LightDM/DBusGreeterList.h+1−1
@@ -47,7 +47,7 @@ Q_SIGNALS:
47 void entryIsLockedChanged();
47 void entryIsLockedChanged();
48
48
49private Q_SLOTS:
49private Q_SLOTS:
50− void authenticationUserChangedHandler(const QString &user);
50+ void authenticationUserChangedHandler();
51 void promptlessChangedHandler();
51 void promptlessChangedHandler();
52
52
53private:
53private:
plugins/LightDM/Greeter.cpp+33−3
@@ -19,6 +19,8 @@
19#include "GreeterPrivate.h"
19#include "GreeterPrivate.h"
20#include <libintl.h>
20#include <libintl.h>
21
21
22+static Greeter *singleton = nullptr;
23+
22GreeterPrivate::GreeterPrivate(Greeter* parent)
24GreeterPrivate::GreeterPrivate(Greeter* parent)
23 : m_greeter(new QLightDM::Greeter(parent)),
25 : m_greeter(new QLightDM::Greeter(parent)),
24 m_active(false),
26 m_active(false),
@@ -44,6 +46,19 @@ Greeter::Greeter(QObject* parent)
44 d->m_greeter->connectSync();
46 d->m_greeter->connectSync();
45}
47}
46
48
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+
47bool Greeter::isActive() const
62bool Greeter::isActive() const
48{
63{
49 Q_D(const Greeter);
64 Q_D(const Greeter);
@@ -86,7 +101,17 @@ bool Greeter::promptless() const
86QString Greeter::selectUser() const
101QString Greeter::selectUser() const
87{
102{
88 Q_D(const Greeter);
103 Q_D(const Greeter);
104+ if (hasGuestAccount() && d->m_greeter->selectGuestHint()) {
105+ return QStringLiteral("*guest");
106+ } else {
89− return d->m_greeter->selectUserHint();
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();
90}
115}
91
116
92void Greeter::authenticate(const QString &username)
117void Greeter::authenticate(const QString &username)
@@ -98,9 +123,14 @@ void Greeter::authenticate(const QString &username)
98 Q_EMIT promptlessChanged();
123 Q_EMIT promptlessChanged();
99 }
124 }
100
125
126+ if (username == QStringLiteral("*guest")) {
127+ d->m_greeter->authenticateAsGuest();
128+ } else {
101− d->m_greeter->authenticate(username);
129+ d->m_greeter->authenticate(username);
130+ }
131+
102 Q_EMIT isAuthenticatedChanged();
132 Q_EMIT isAuthenticatedChanged();
103− Q_EMIT authenticationUserChanged(username);
133+ Q_EMIT authenticationUserChanged();
104}
134}
105
135
106void Greeter::respond(const QString &response)
136void Greeter::respond(const QString &response)
plugins/LightDM/Greeter.h+6−2
@@ -42,7 +42,8 @@ class Greeter : public QObject
42 Q_PROPERTY(QObject *mock READ mock CONSTANT) // for testing
42 Q_PROPERTY(QObject *mock READ mock CONSTANT) // for testing
43
43
44public:
44public:
45− explicit Greeter(QObject* parent=0);
45+ static Greeter *instance();
46+ virtual ~Greeter();
46
47
47 bool isActive() const;
48 bool isActive() const;
48 bool isAuthenticated() const;
49 bool isAuthenticated() const;
@@ -50,6 +51,7 @@ public:
50 QString defaultSessionHint() const;
51 QString defaultSessionHint() const;
51 bool promptless() const;
52 bool promptless() const;
52 QString selectUser() const;
53 QString selectUser() const;
54+ bool hasGuestAccount() const;
53
55
54 QObject *mock();
56 QObject *mock();
55
57
@@ -63,7 +65,7 @@ Q_SIGNALS:
63 void showMessage(const QString &text, bool isError);
65 void showMessage(const QString &text, bool isError);
64 void showPrompt(const QString &text, bool isSecret, bool isDefaultPrompt);
66 void showPrompt(const QString &text, bool isSecret, bool isDefaultPrompt);
65 void authenticationComplete();
67 void authenticationComplete();
66− void authenticationUserChanged(const QString &user);
68+ void authenticationUserChanged();
67 void isActiveChanged();
69 void isActiveChanged();
68 void isAuthenticatedChanged();
70 void isAuthenticatedChanged();
69 void promptlessChanged();
71 void promptlessChanged();
@@ -75,6 +77,8 @@ Q_SIGNALS:
75 void requestAuthenticationUser(const QString &user);
77 void requestAuthenticationUser(const QString &user);
76
78
77protected:
79protected:
80+ explicit Greeter(QObject* parent=0);
81+
78 GreeterPrivate * const d_ptr;
82 GreeterPrivate * const d_ptr;
79
83
80 Q_DECLARE_PRIVATE(Greeter)
84 Q_DECLARE_PRIVATE(Greeter)
plugins/LightDM/UsersModel.cpp+41
@@ -16,10 +16,13 @@
16 * Author: Michael Terry <michael.terry@canonical.com>
16 * Author: Michael Terry <michael.terry@canonical.com>
17 */
17 */
18
18
19+#include "Greeter.h"
19#include "UsersModel.h"
20#include "UsersModel.h"
20#include <QLightDM/UsersModel>
21#include <QLightDM/UsersModel>
21#include <QtCore/QSortFilterProxyModel>
22#include <QtCore/QSortFilterProxyModel>
22
23
24+#include <libintl.h>
25+
23// First, we define an internal class that wraps LightDM's UsersModel. This
26// First, we define an internal class that wraps LightDM's UsersModel. This
24// class will modify some of the data coming from LightDM. For example, we
27// class will modify some of the data coming from LightDM. For example, we
25// modify any empty Real Names into just normal Names.
28// modify any empty Real Names into just normal Names.
@@ -68,6 +71,44 @@ UsersModel::UsersModel(QObject* parent)
68 sort(0);
71 sort(0);
69}
72}
70
73
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+
71QObject *UsersModel::mock()
112QObject *UsersModel::mock()
72{
113{
73 // get through MangleModel down to QLightDM::UsersModel
114 // get through MangleModel down to QLightDM::UsersModel
plugins/LightDM/UsersModel.h+7
@@ -32,5 +32,12 @@ class UsersModel : public UnitySortFilterProxyModelQML
32public:
32public:
33 explicit UsersModel(QObject* parent=0);
33 explicit UsersModel(QObject* parent=0);
34
34
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+
35 QObject *mock();
39 QObject *mock();
40+
41+private:
42+ int guestRow() const;
36};
43};
plugins/LightDM/plugin.cpp+3−2
@@ -32,13 +32,14 @@
32
32
33static QObject *greeter_provider(QQmlEngine *engine, QJSEngine *scriptEngine)
33static QObject *greeter_provider(QQmlEngine *engine, QJSEngine *scriptEngine)
34{
34{
35− Q_UNUSED(engine)
36 Q_UNUSED(scriptEngine)
35 Q_UNUSED(scriptEngine)
37
36
38− Greeter *greeter = new Greeter();
37+ Greeter *greeter = Greeter::instance();
39 new DBusGreeter(greeter, QStringLiteral("/"));
38 new DBusGreeter(greeter, QStringLiteral("/"));
40 new DBusGreeterList(greeter, QStringLiteral("/list"));
39 new DBusGreeterList(greeter, QStringLiteral("/list"));
41
40
41+ engine->setObjectOwnership(greeter, QQmlEngine::CppOwnership);
42+
42 return greeter;
43 return greeter;
43}
44}
44
45
tests/mocks/AccountsService/AccountsService.cpp+2−2
@@ -121,8 +121,8 @@ QString AccountsService::backgroundFile() const
121
121
122 // Check if our mock user has a background set in liblightdm
122 // Check if our mock user has a background set in liblightdm
123 for (int i = 0; i < m_usersModel->count(); i++) {
123 for (int i = 0; i < m_usersModel->count(); i++) {
124− if (m_usersModel->data(i, QLightDM::UsersModel::NameRole) == m_user) {
124+ if (m_usersModel->data(m_usersModel->index(i, 0), QLightDM::UsersModel::NameRole) == m_user) {
125− return m_usersModel->data(i, QLightDM::UsersModel::BackgroundPathRole).toString();
125+ return m_usersModel->data(m_usersModel->index(i, 0), QLightDM::UsersModel::BackgroundPathRole).toString();
126 }
126 }
127 }
127 }
128
128
tests/mocks/AccountsService/CMakeLists.txt+1
@@ -6,6 +6,7 @@ include_directories(
6 )
6 )
7
7
8add_library(MockAccountsService-qml MODULE
8add_library(MockAccountsService-qml MODULE
9+ ${CMAKE_SOURCE_DIR}/plugins/LightDM/Greeter.cpp
9 ${CMAKE_SOURCE_DIR}/plugins/LightDM/UsersModel.cpp
10 ${CMAKE_SOURCE_DIR}/plugins/LightDM/UsersModel.cpp
10 ${CMAKE_SOURCE_DIR}/plugins/Utils/unitysortfilterproxymodelqml.cpp
11 ${CMAKE_SOURCE_DIR}/plugins/Utils/unitysortfilterproxymodelqml.cpp
11 AccountsService.cpp
12 AccountsService.cpp
tests/mocks/liblightdm/MockController.cpp+37
@@ -23,6 +23,8 @@ namespace QLightDM
23
23
24MockController::MockController(QObject *parent)
24MockController::MockController(QObject *parent)
25 : QObject(parent)
25 : QObject(parent)
26+ , m_selectGuestHint(false)
27+ , m_hasGuestAccountHint(false)
26 , m_fullSessions(
28 , m_fullSessions(
27 {
29 {
28 {"ubuntu", "Ubuntu"},
30 {"ubuntu", "Ubuntu"},
@@ -59,6 +61,15 @@ MockController *MockController::instance()
59 return m_instance;
61 return m_instance;
60}
62}
61
63
64+void MockController::reset()
65+{
66+ setSelectUserHint("");
67+ setSelectGuestHint(false);
68+ setHasGuestAccountHint(false);
69+ setUserMode("full");
70+ setSessionMode("full");
71+}
72+
62QString MockController::selectUserHint() const
73QString MockController::selectUserHint() const
63{
74{
64 return m_selectUserHint;
75 return m_selectUserHint;
@@ -72,6 +83,32 @@ void MockController::setSelectUserHint(const QString &selectUserHint)
72 }
83 }
73}
84}
74
85
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+
75QString MockController::userMode() const
112QString MockController::userMode() const
76{
113{
77 return m_userMode;
114 return m_userMode;
tests/mocks/liblightdm/MockController.h+14
@@ -27,6 +27,8 @@ class Q_DECL_EXPORT MockController : public QObject
27 Q_OBJECT
27 Q_OBJECT
28
28
29 Q_PROPERTY(QString selectUserHint READ selectUserHint WRITE setSelectUserHint NOTIFY selectUserHintChanged)
29 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)
30
32
31 // single, single-pin, single-passphrase, full
33 // single, single-pin, single-passphrase, full
32 Q_PROPERTY(QString userMode READ userMode WRITE setUserMode NOTIFY userModeChanged)
34 Q_PROPERTY(QString userMode READ userMode WRITE setUserMode NOTIFY userModeChanged)
@@ -41,9 +43,17 @@ public:
41 static MockController *instance();
43 static MockController *instance();
42 virtual ~MockController();
44 virtual ~MockController();
43
45
46+ Q_INVOKABLE void reset();
47+
44 QString selectUserHint() const;
48 QString selectUserHint() const;
45 void setSelectUserHint(const QString &selectUserHint);
49 void setSelectUserHint(const QString &selectUserHint);
46
50
51+ bool selectGuestHint() const;
52+ void setSelectGuestHint(bool selectGuestHint);
53+
54+ bool hasGuestAccountHint() const;
55+ void setHasGuestAccountHint(bool hasGuestAccountHint);
56+
47 QString userMode() const;
57 QString userMode() const;
48 void setUserMode(const QString &userMode);
58 void setUserMode(const QString &userMode);
49
59
@@ -64,6 +74,8 @@ public:
64
74
65Q_SIGNALS:
75Q_SIGNALS:
66 void selectUserHintChanged();
76 void selectUserHintChanged();
77+ void selectGuestHintChanged();
78+ void hasGuestAccountHintChanged();
67 void userModeChanged();
79 void userModeChanged();
68 void sessionModeChanged();
80 void sessionModeChanged();
69 void numSessionsChanged();
81 void numSessionsChanged();
@@ -72,6 +84,8 @@ private:
72 explicit MockController(QObject* parent=0);
84 explicit MockController(QObject* parent=0);
73
85
74 QString m_selectUserHint;
86 QString m_selectUserHint;
87+ bool m_selectGuestHint;
88+ bool m_hasGuestAccountHint;
75 QString m_userMode;
89 QString m_userMode;
76 QString m_sessionMode;
90 QString m_sessionMode;
77 QList<SessionItem> m_fullSessions;
91 QList<SessionItem> m_fullSessions;
tests/mocks/liblightdm/MockGreeter.cpp+10−3
@@ -51,7 +51,7 @@ QString Greeter::authenticationUser() const
51
51
52bool Greeter::hasGuestAccountHint() const
52bool Greeter::hasGuestAccountHint() const
53{
53{
54− return true;
54+ return MockController::instance()->hasGuestAccountHint();
55}
55}
56
56
57QString Greeter::getHint(const QString &name) const
57QString Greeter::getHint(const QString &name) const
@@ -87,7 +87,7 @@ QString Greeter::selectUserHint() const
87
87
88bool Greeter::selectGuestHint() const
88bool Greeter::selectGuestHint() const
89{
89{
90− return false;
90+ return MockController::instance()->selectGuestHint();
91}
91}
92
92
93QString Greeter::autologinUserHint() const
93QString Greeter::autologinUserHint() const
@@ -173,7 +173,14 @@ void Greeter::handleAuthenticate()
173}
173}
174
174
175void Greeter::authenticateAsGuest()
175void 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+}
177
184
178void Greeter::authenticateAutologin()
185void Greeter::authenticateAutologin()
179{}
186{}
tests/plugins/LightDM/IntegratedLightDM/CMakeLists.txt+1
@@ -66,6 +66,7 @@ add_unity8_uitest(GreeterSessions GreeterSessionsModelTestExec
66# UsersModelTest
66# UsersModelTest
67add_executable(GreeterUsersModelTestExec
67add_executable(GreeterUsersModelTestExec
68 usersmodel.cpp
68 usersmodel.cpp
69+ ${CMAKE_SOURCE_DIR}/plugins/LightDM/Greeter.cpp
69 ${CMAKE_SOURCE_DIR}/plugins/LightDM/UsersModel.cpp
70 ${CMAKE_SOURCE_DIR}/plugins/LightDM/UsersModel.cpp
70 ${CMAKE_SOURCE_DIR}/plugins/Utils/unitysortfilterproxymodelqml.cpp
71 ${CMAKE_SOURCE_DIR}/plugins/Utils/unitysortfilterproxymodelqml.cpp
71 )
72 )
tests/plugins/LightDM/IntegratedLightDM/dbus.cpp+1
@@ -81,6 +81,7 @@ private Q_SLOTS:
81 void cleanup()
81 void cleanup()
82 {
82 {
83 delete view;
83 delete view;
84+ delete greeter; // reset singleton
84 }
85 }
85
86
86 void testGetActiveEntry()
87 void testGetActiveEntry()
tests/qmltests/Greeter/tst_Greeter.qml+28−2
@@ -143,7 +143,7 @@ Item {
143
143
144 function init() {
144 function init() {
145 greeterSettings.lockedOutTime = 0;
145 greeterSettings.lockedOutTime = 0;
146− LightDM.Greeter.mock.selectUserHint = "";
146+ LightDM.Greeter.mock.reset();
147 greeter.failedLoginsDelayAttempts = 7;
147 greeter.failedLoginsDelayAttempts = 7;
148 greeter.failedLoginsDelayMinutes = 5;
148 greeter.failedLoginsDelayMinutes = 5;
149 teaseSpy.clear();
149 teaseSpy.clear();
@@ -200,7 +200,10 @@ Item {
200 var i = getIndexOf(name);
200 var i = getIndexOf(name);
201 compare(view.currentIndex, i);
201 compare(view.currentIndex, i);
202 compare(AccountsService.user, name);
202 compare(AccountsService.user, name);
203+ if (name === "*guest") // guest has no authenticationUser set
204+ compare(LightDM.Greeter.authenticationUser, "");
205+ else
203− compare(LightDM.Greeter.authenticationUser, name);
206+ compare(LightDM.Greeter.authenticationUser, name);
204 return i;
207 return i;
205 }
208 }
206
209
@@ -585,6 +588,29 @@ Item {
585 verifySelected(LightDM.Users.data(0, LightDM.UserRoles.NameRole));
588 verifySelected(LightDM.Users.data(0, LightDM.UserRoles.NameRole));
586 }
589 }
587
590
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+
588 function test_fingerprintSuccess() {
614 function test_fingerprintSuccess() {
589 var index = selectUser("has-password");
615 var index = selectUser("has-password");
590 unlockAndShowGreeter(); // turn on lockscreen mode
616 unlockAndShowGreeter(); // turn on lockscreen mode