on xenial help sign in
Add support for guest sessions in unity8-greeter.
f869068Michael 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+38−3
@@ -17,8 +17,11 @@
17
17
18#include "Greeter.h"
18#include "Greeter.h"
19#include "GreeterPrivate.h"
19#include "GreeterPrivate.h"
20+#include <QCoreApplication>
20#include <libintl.h>
21#include <libintl.h>
21
22
23+static Greeter *singleton = nullptr;
24+
22GreeterPrivate::GreeterPrivate(Greeter* parent)
25GreeterPrivate::GreeterPrivate(Greeter* parent)
23 : m_greeter(new QLightDM::Greeter(parent)),
26 : m_greeter(new QLightDM::Greeter(parent)),
24 m_active(false),
27 m_active(false),
@@ -41,9 +44,26 @@ Greeter::Greeter(QObject* parent)
41 connect(d->m_greeter, &QLightDM::Greeter::authenticationComplete,
44 connect(d->m_greeter, &QLightDM::Greeter::authenticationComplete,
42 this, &Greeter::authenticationCompleteFilter);
45 this, &Greeter::authenticationCompleteFilter);
43
46
47+ // Don't get stuck waiting for PAM as we shut down.
48+ connect(QCoreApplication::instance(), &QCoreApplication::aboutToQuit,
49+ d->m_greeter, &QLightDM::Greeter::cancelAuthentication);
50+
44 d->m_greeter->connectSync();
51 d->m_greeter->connectSync();
45}
52}
46
53
54+Greeter::~Greeter()
55+{
56+ singleton = nullptr;
57+}
58+
59+Greeter *Greeter::instance()
60+{
61+ if (!singleton) {
62+ singleton = new Greeter;
63+ }
64+ return singleton;
65+}
66+
47bool Greeter::isActive() const
67bool Greeter::isActive() const
48{
68{
49 Q_D(const Greeter);
69 Q_D(const Greeter);
@@ -86,7 +106,17 @@ bool Greeter::promptless() const
86QString Greeter::selectUser() const
106QString Greeter::selectUser() const
87{
107{
88 Q_D(const Greeter);
108 Q_D(const Greeter);
109+ if (hasGuestAccount() && d->m_greeter->selectGuestHint()) {
110+ return QStringLiteral("*guest");
111+ } else {
89− return d->m_greeter->selectUserHint();
112+ return d->m_greeter->selectUserHint();
113+ }
114+}
115+
116+bool Greeter::hasGuestAccount() const
117+{
118+ Q_D(const Greeter);
119+ return d->m_greeter->hasGuestAccountHint();
90}
120}
91
121
92void Greeter::authenticate(const QString &username)
122void Greeter::authenticate(const QString &username)
@@ -98,9 +128,14 @@ void Greeter::authenticate(const QString &username)
98 Q_EMIT promptlessChanged();
128 Q_EMIT promptlessChanged();
99 }
129 }
100
130
131+ if (username == QStringLiteral("*guest")) {
132+ d->m_greeter->authenticateAsGuest();
133+ } else {
101− d->m_greeter->authenticate(username);
134+ d->m_greeter->authenticate(username);
135+ }
136+
102 Q_EMIT isAuthenticatedChanged();
137 Q_EMIT isAuthenticatedChanged();
103− Q_EMIT authenticationUserChanged(username);
138+ Q_EMIT authenticationUserChanged();
104}
139}
105
140
106void Greeter::respond(const QString &response)
141void Greeter::respond(const QString &response)
plugins/LightDM/Greeter.h+6−2
@@ -40,7 +40,8 @@ class Greeter : public QObject
40 Q_PROPERTY(QString selectUser READ selectUser CONSTANT)
40 Q_PROPERTY(QString selectUser READ selectUser CONSTANT)
41
41
42public:
42public:
43− explicit Greeter(QObject* parent=0);
43+ static Greeter *instance();
44+ virtual ~Greeter();
44
45
45 bool isActive() const;
46 bool isActive() const;
46 bool isAuthenticated() const;
47 bool isAuthenticated() const;
@@ -48,6 +49,7 @@ public:
48 QString defaultSessionHint() const;
49 QString defaultSessionHint() const;
49 bool promptless() const;
50 bool promptless() const;
50 QString selectUser() const;
51 QString selectUser() const;
52+ bool hasGuestAccount() const;
51
53
52public Q_SLOTS:
54public Q_SLOTS:
53 void authenticate(const QString &username=QString());
55 void authenticate(const QString &username=QString());
@@ -59,7 +61,7 @@ Q_SIGNALS:
59 void showMessage(const QString &text, bool isError);
61 void showMessage(const QString &text, bool isError);
60 void showPrompt(const QString &text, bool isSecret, bool isDefaultPrompt);
62 void showPrompt(const QString &text, bool isSecret, bool isDefaultPrompt);
61 void authenticationComplete();
63 void authenticationComplete();
62− void authenticationUserChanged(const QString &user);
64+ void authenticationUserChanged();
63 void isActiveChanged();
65 void isActiveChanged();
64 void isAuthenticatedChanged();
66 void isAuthenticatedChanged();
65 void promptlessChanged();
67 void promptlessChanged();
@@ -71,6 +73,8 @@ Q_SIGNALS:
71 void requestAuthenticationUser(const QString &user);
73 void requestAuthenticationUser(const QString &user);
72
74
73protected:
75protected:
76+ explicit Greeter(QObject* parent=0);
77+
74 GreeterPrivate * const d_ptr;
78 GreeterPrivate * const d_ptr;
75
79
76 Q_DECLARE_PRIVATE(Greeter)
80 Q_DECLARE_PRIVATE(Greeter)
plugins/LightDM/IntegratedLightDM/liblightdm/Greeter.cpp+7−4
@@ -49,7 +49,7 @@ QString Greeter::authenticationUser() const
49
49
50bool Greeter::hasGuestAccountHint() const
50bool Greeter::hasGuestAccountHint() const
51{
51{
52− return true;
52+ return false;
53}
53}
54
54
55QString Greeter::getHint(const QString &name) const
55QString Greeter::getHint(const QString &name) const
@@ -70,12 +70,12 @@ bool Greeter::hideUsersHint() const
70
70
71bool Greeter::showManualLoginHint() const
71bool Greeter::showManualLoginHint() const
72{
72{
73− return true;
73+ return false;
74}
74}
75
75
76bool Greeter::showRemoteLoginHint() const
76bool Greeter::showRemoteLoginHint() const
77{
77{
78− return true;
78+ return false;
79}
79}
80
80
81QString Greeter::selectUserHint() const
81QString Greeter::selectUserHint() const
@@ -146,7 +146,10 @@ void Greeter::authenticateRemote(const QString &session, const QString &username
146}
146}
147
147
148void Greeter::cancelAuthentication()
148void Greeter::cancelAuthentication()
149−{}
149+{
150+ Q_D(Greeter);
151+ d->cancelAuthentication();
152+}
150
153
151void Greeter::setLanguage (const QString &language)
154void Greeter::setLanguage (const QString &language)
152{
155{
plugins/LightDM/IntegratedLightDM/liblightdm/GreeterPrivate.cpp+27−22
@@ -206,6 +206,28 @@ public Q_SLOTS:
206 }
206 }
207 }
207 }
208
208
209+ void cancelPam()
210+ {
211+ if (pamHandle != nullptr) {
212+ QFuture<int> pamFuture = futureWatcher.future();
213+ pam_handle *handle = pamHandle;
214+ pamHandle = nullptr; // to disable normal finishPam() handling
215+ pamFuture.cancel();
216+
217+ // Note the empty loop, we just want to clear the futures queue.
218+ // Any further prompts from the pam thread will be immediately
219+ // responded to/dismissed in handlePrompt().
220+ while (respond(QString()));
221+
222+ // Now let signal/slot handling happen so the thread can finish
223+ while (!pamFuture.isFinished()) {
224+ QCoreApplication::processEvents();
225+ }
226+
227+ pam_end(handle, PAM_CONV_ERR);
228+ }
229+ }
230+
209Q_SIGNALS:
231Q_SIGNALS:
210 void showMessage(pam_handle *handle, QString text, QLightDM::Greeter::MessageType type);
232 void showMessage(pam_handle *handle, QString text, QLightDM::Greeter::MessageType type);
211 void showPrompt(pam_handle *handle, QString text, QLightDM::Greeter::PromptType type, QLightDM::GreeterImpl::ResponseFuture response);
233 void showPrompt(pam_handle *handle, QString text, QLightDM::Greeter::PromptType type, QLightDM::GreeterImpl::ResponseFuture response);
@@ -247,28 +269,6 @@ private Q_SLOTS:
247 }
269 }
248
270
249private:
271private:
250− void cancelPam()
251− {
252− if (pamHandle != nullptr) {
253− QFuture<int> pamFuture = futureWatcher.future();
254− pam_handle *handle = pamHandle;
255− pamHandle = nullptr; // to disable normal finishPam() handling
256− pamFuture.cancel();
257−
258− // Note the empty loop, we just want to clear the futures queue.
259− // Any further prompts from the pam thread will be immediately
260− // responded to/dismissed in handlePrompt().
261− while (respond(QString()));
262−
263− // Now let signal/slot handling happen so the thread can finish
264− while (!pamFuture.isFinished()) {
265− QCoreApplication::processEvents();
266− }
267−
268− pam_end(handle, PAM_CONV_ERR);
269− }
270− }
271−
272 Greeter *greeter;
272 Greeter *greeter;
273 GreeterPrivate *greeterPrivate;
273 GreeterPrivate *greeterPrivate;
274 pam_handle* pamHandle;
274 pam_handle* pamHandle;
@@ -299,6 +299,11 @@ void GreeterPrivate::handleRespond(const QString &response)
299 m_impl->respond(response);
299 m_impl->respond(response);
300}
300}
301
301
302+void GreeterPrivate::cancelAuthentication()
303+{
304+ m_impl->cancelPam();
305+}
306+
302}
307}
303
308
304#include "GreeterPrivate.moc"
309#include "GreeterPrivate.moc"
plugins/LightDM/IntegratedLightDM/liblightdm/GreeterPrivate.h+1
@@ -38,6 +38,7 @@ public:
38
38
39 void handleAuthenticate();
39 void handleAuthenticate();
40 void handleRespond(const QString &response);
40 void handleRespond(const QString &response);
41+ void cancelAuthentication();
41
42
42protected:
43protected:
43 GreeterImpl *m_impl; // if the backend needs more private data
44 GreeterImpl *m_impl; // if the backend needs more private data
plugins/LightDM/UsersModel.cpp+68−8
@@ -12,20 +12,22 @@
12 *
12 *
13 * You should have received a copy of the GNU General Public License
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15− *
16− * Author: Michael Terry <michael.terry@canonical.com>
17 */
15 */
18
16
17+#include "Greeter.h"
19#include "UsersModel.h"
18#include "UsersModel.h"
19+#include <QIdentityProxyModel>
20#include <QLightDM/UsersModel>
20#include <QLightDM/UsersModel>
21−#include <QtCore/QSortFilterProxyModel>
21+
22+#include <libintl.h>
22
23
23// First, we define an internal class that wraps LightDM's UsersModel. This
24// 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
25// class will modify some of the data coming from LightDM. For example, we
25−// modify any empty Real Names into just normal Names.
26+// modify any empty Real Names into just normal Names. We also add optional
27+// rows, depending on configuration.
26// (We can't modify the data directly in UsersModel below because it won't sort
28// (We can't modify the data directly in UsersModel below because it won't sort
27// using the modified data.)
29// using the modified data.)
28−class MangleModel : public QSortFilterProxyModel
30+class MangleModel : public QIdentityProxyModel
29{
31{
30 Q_OBJECT
32 Q_OBJECT
31
33
@@ -33,21 +35,41 @@ public:
33 explicit MangleModel(QObject* parent=0);
35 explicit MangleModel(QObject* parent=0);
34
36
35 QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
37 QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
38+ int rowCount(const QModelIndex &parent = QModelIndex()) const override;
39+ QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override;
40+
41+private:
42+ int guestRow() const;
36};
43};
37
44
38MangleModel::MangleModel(QObject* parent)
45MangleModel::MangleModel(QObject* parent)
39− : QSortFilterProxyModel(parent)
46+ : QIdentityProxyModel(parent)
40{
47{
41 setSourceModel(new QLightDM::UsersModel(this));
48 setSourceModel(new QLightDM::UsersModel(this));
42}
49}
43
50
44QVariant MangleModel::data(const QModelIndex &index, int role) const
51QVariant MangleModel::data(const QModelIndex &index, int role) const
45{
52{
46− QVariant variantData = QSortFilterProxyModel::data(index, role);
53+ QVariant variantData;
54+
55+ if (index.row() == guestRow() && index.column() == 0) {
56+ switch (role) {
57+ case QLightDM::UsersModel::NameRole:
58+ variantData = QStringLiteral("*guest"); break;
59+ case QLightDM::UsersModel::RealNameRole:
60+ variantData = gettext("Guest Session"); break;
61+ case QLightDM::UsersModel::LoggedInRole:
62+ variantData = false; break;
63+ case QLightDM::UsersModel::SessionRole:
64+ variantData = Greeter::instance()->defaultSessionHint(); break;
65+ }
66+ } else {
67+ variantData = QIdentityProxyModel::data(index, role);
68+ }
47
69
48 // If user's real name is empty, switch to unix name
70 // If user's real name is empty, switch to unix name
49 if (role == QLightDM::UsersModel::RealNameRole && variantData.toString().isEmpty()) {
71 if (role == QLightDM::UsersModel::RealNameRole && variantData.toString().isEmpty()) {
50− variantData = QSortFilterProxyModel::data(index, QLightDM::UsersModel::NameRole);
72+ variantData = QIdentityProxyModel::data(index, QLightDM::UsersModel::NameRole);
51 } else if (role == QLightDM::UsersModel::BackgroundPathRole && variantData.toString().startsWith('#')) {
73 } else if (role == QLightDM::UsersModel::BackgroundPathRole && variantData.toString().startsWith('#')) {
52 const QString stringData = "data:image/svg+xml,<svg><rect width='100%' height='100%' fill='" + variantData.toString() + "'/></svg>";
74 const QString stringData = "data:image/svg+xml,<svg><rect width='100%' height='100%' fill='" + variantData.toString() + "'/></svg>";
53 variantData = stringData;
75 variantData = stringData;
@@ -56,6 +78,31 @@ QVariant MangleModel::data(const QModelIndex &index, int role) const
56 return variantData;
78 return variantData;
57}
79}
58
80
81+int MangleModel::guestRow() const
82+{
83+ return Greeter::instance()->hasGuestAccount() ?
84+ QIdentityProxyModel::rowCount() : -1;
85+}
86+
87+int MangleModel::rowCount(const QModelIndex &parent) const
88+{
89+ auto count = QIdentityProxyModel::rowCount(parent);
90+
91+ if (Greeter::instance()->hasGuestAccount() && !parent.isValid())
92+ count++;
93+
94+ return count;
95+}
96+
97+QModelIndex MangleModel::index(int row, int column, const QModelIndex &parent) const
98+{
99+ if (row == guestRow() && !parent.isValid()) {
100+ return createIndex(row, column);
101+ } else {
102+ return QIdentityProxyModel::index(row, column, parent);
103+ }
104+}
105+
59// **** Now we continue with actual UsersModel class ****
106// **** Now we continue with actual UsersModel class ****
60
107
61UsersModel::UsersModel(QObject* parent)
108UsersModel::UsersModel(QObject* parent)
@@ -68,4 +115,17 @@ UsersModel::UsersModel(QObject* parent)
68 sort(0);
115 sort(0);
69}
116}
70
117
118+bool UsersModel::lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const
119+{
120+ auto leftName = source_left.data(QLightDM::UsersModel::NameRole);
121+ auto rightName = source_right.data(QLightDM::UsersModel::NameRole);
122+
123+ if (leftName == QStringLiteral("*guest"))
124+ return false;
125+ if (rightName == QStringLiteral("*guest"))
126+ return true;
127+
128+ return UnitySortFilterProxyModelQML::lessThan(source_left, source_right);
129+}
130+
71#include "UsersModel.moc"
131#include "UsersModel.moc"
plugins/LightDM/UsersModel.h+4−1
@@ -21,7 +21,7 @@
21#pragma once
21#pragma once
22
22
23#include <unitysortfilterproxymodelqml.h>
23#include <unitysortfilterproxymodelqml.h>
24−#include <QtCore/QObject>
24+#include <QObject>
25
25
26class UsersModel : public UnitySortFilterProxyModelQML
26class UsersModel : public UnitySortFilterProxyModelQML
27{
27{
@@ -29,4 +29,7 @@ class UsersModel : public UnitySortFilterProxyModelQML
29
29
30public:
30public:
31 explicit UsersModel(QObject* parent=0);
31 explicit UsersModel(QObject* parent=0);
32+
33+protected:
34+ bool lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const override;
32};
35};
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/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+45−6
@@ -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"},
@@ -37,13 +39,9 @@ MockController::MockController(QObject *parent)
37 {"xterm", "Recovery Console"},
39 {"xterm", "Recovery Console"},
38 {"", "Unknown?"}
40 {"", "Unknown?"}
39 })
41 })
42+ , m_numSessions(0)
40{
43{
41− m_userMode = qgetenv("LIBLIGHTDM_MOCK_MODE");
44+ reset();
42− if (m_userMode.isEmpty()) {
43− m_userMode = "full";
44− }
45− m_sessionMode = "full";
46− m_numSessions = numFullSessions();
47}
45}
48
46
49MockController::~MockController()
47MockController::~MockController()
@@ -59,6 +57,21 @@ MockController *MockController::instance()
59 return m_instance;
57 return m_instance;
60}
58}
61
59
60+void MockController::reset()
61+{
62+ auto userMode = qgetenv("LIBLIGHTDM_MOCK_MODE");
63+ if (userMode.isEmpty()) {
64+ userMode = "full";
65+ }
66+
67+ setSelectUserHint("");
68+ setSelectGuestHint(false);
69+ setHasGuestAccountHint(false);
70+ setUserMode(userMode);
71+ setSessionMode("full");
72+ setNumSessions(numFullSessions());
73+}
74+
62QString MockController::selectUserHint() const
75QString MockController::selectUserHint() const
63{
76{
64 return m_selectUserHint;
77 return m_selectUserHint;
@@ -72,6 +85,32 @@ void MockController::setSelectUserHint(const QString &selectUserHint)
72 }
85 }
73}
86}
74
87
88+bool MockController::selectGuestHint() const
89+{
90+ return m_selectGuestHint;
91+}
92+
93+void MockController::setSelectGuestHint(bool selectGuestHint)
94+{
95+ if (m_selectGuestHint != selectGuestHint) {
96+ m_selectGuestHint = selectGuestHint;
97+ Q_EMIT selectGuestHintChanged();
98+ }
99+}
100+
101+bool MockController::hasGuestAccountHint() const
102+{
103+ return m_hasGuestAccountHint;
104+}
105+
106+void MockController::setHasGuestAccountHint(bool hasGuestAccountHint)
107+{
108+ if (m_hasGuestAccountHint != hasGuestAccountHint) {
109+ m_hasGuestAccountHint = hasGuestAccountHint;
110+ Q_EMIT hasGuestAccountHintChanged();
111+ }
112+}
113+
75QString MockController::userMode() const
114QString MockController::userMode() const
76{
115{
77 return m_userMode;
116 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)
@@ -46,9 +48,17 @@ public:
46 static MockController *instance();
48 static MockController *instance();
47 virtual ~MockController();
49 virtual ~MockController();
48
50
51+ Q_INVOKABLE void reset();
52+
49 QString selectUserHint() const;
53 QString selectUserHint() const;
50 void setSelectUserHint(const QString &selectUserHint);
54 void setSelectUserHint(const QString &selectUserHint);
51
55
56+ bool selectGuestHint() const;
57+ void setSelectGuestHint(bool selectGuestHint);
58+
59+ bool hasGuestAccountHint() const;
60+ void setHasGuestAccountHint(bool hasGuestAccountHint);
61+
52 QString userMode() const;
62 QString userMode() const;
53 void setUserMode(const QString &userMode);
63 void setUserMode(const QString &userMode);
54
64
@@ -76,6 +86,8 @@ public:
76Q_SIGNALS:
86Q_SIGNALS:
77 void currentUsernameChanged();
87 void currentUsernameChanged();
78 void selectUserHintChanged();
88 void selectUserHintChanged();
89+ void selectGuestHintChanged();
90+ void hasGuestAccountHintChanged();
79 void userModeChanged();
91 void userModeChanged();
80 void sessionModeChanged();
92 void sessionModeChanged();
81 void numSessionsChanged();
93 void numSessionsChanged();
@@ -87,6 +99,8 @@ private:
87 QString m_currentUsername;
99 QString m_currentUsername;
88 QString m_sessionName;
100 QString m_sessionName;
89 QString m_selectUserHint;
101 QString m_selectUserHint;
102+ bool m_selectGuestHint;
103+ bool m_hasGuestAccountHint;
90 QString m_userMode;
104 QString m_userMode;
91 QString m_sessionMode;
105 QString m_sessionMode;
92 QList<SessionItem> m_fullSessions;
106 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/mocks/liblightdm/MockUsersModel.cpp+2
@@ -65,6 +65,8 @@ UsersModel::UsersModel(QObject *parent)
65 roles[UidRole] = "uid";
65 roles[UidRole] = "uid";
66 setRoleNames(roles);
66 setRoleNames(roles);
67
67
68+ connect(MockController::instance(), &MockController::hasGuestAccountHintChanged,
69+ this, &UsersModel::resetEntries);
68 connect(MockController::instance(), &MockController::userModeChanged,
70 connect(MockController::instance(), &MockController::userModeChanged,
69 this, &UsersModel::resetEntries);
71 this, &UsersModel::resetEntries);
70 connect(MockController::instance(), &MockController::sessionNameChanged,
72 connect(MockController::instance(), &MockController::sessionNameChanged,
tests/plugins/LightDM/IntegratedLightDM/CMakeLists.txt+2−3
@@ -17,7 +17,6 @@ target_link_libraries(GreeterDBusTestExec
17 )
17 )
18target_include_directories(GreeterDBusTestExec PUBLIC
18target_include_directories(GreeterDBusTestExec PUBLIC
19 ${CMAKE_SOURCE_DIR}/plugins/LightDM
19 ${CMAKE_SOURCE_DIR}/plugins/LightDM
20− ${CMAKE_SOURCE_DIR}/tests/mocks/LightDM/IntegratedLightDM
21 )
20 )
22set_target_properties(GreeterDBusTestExec PROPERTIES INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${SHELL_INSTALL_QML}/mocks/IntegratedLightDM/liblightdm")
21set_target_properties(GreeterDBusTestExec PROPERTIES INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${SHELL_INSTALL_QML}/mocks/IntegratedLightDM/liblightdm")
23install(TARGETS GreeterDBusTestExec
22install(TARGETS GreeterDBusTestExec
@@ -64,7 +63,6 @@ target_link_libraries(GreeterSessionsModelTestExec
64target_include_directories(GreeterSessionsModelTestExec PUBLIC
63target_include_directories(GreeterSessionsModelTestExec PUBLIC
65 ${CMAKE_SOURCE_DIR}/plugins/LightDM
64 ${CMAKE_SOURCE_DIR}/plugins/LightDM
66 ${CMAKE_SOURCE_DIR}/plugins/Utils
65 ${CMAKE_SOURCE_DIR}/plugins/Utils
67− ${CMAKE_SOURCE_DIR}/tests/mocks/LightDM/IntegratedLightDM
68 ${CMAKE_SOURCE_DIR}/tests/mocks/liblightdm/
66 ${CMAKE_SOURCE_DIR}/tests/mocks/liblightdm/
69 )
67 )
70install(TARGETS GreeterSessionsModelTestExec
68install(TARGETS GreeterSessionsModelTestExec
@@ -77,6 +75,7 @@ add_unity8_uitest(GreeterSessions GreeterSessionsModelTestExec
77# UsersModelTest
75# UsersModelTest
78add_executable(GreeterUsersModelTestExec
76add_executable(GreeterUsersModelTestExec
79 usersmodel.cpp
77 usersmodel.cpp
78+ ${CMAKE_SOURCE_DIR}/plugins/LightDM/Greeter.cpp
80 ${CMAKE_SOURCE_DIR}/plugins/LightDM/UsersModel.cpp
79 ${CMAKE_SOURCE_DIR}/plugins/LightDM/UsersModel.cpp
81 ${CMAKE_SOURCE_DIR}/plugins/Utils/unitysortfilterproxymodelqml.cpp
80 ${CMAKE_SOURCE_DIR}/plugins/Utils/unitysortfilterproxymodelqml.cpp
82 )
81 )
@@ -90,7 +89,7 @@ target_link_libraries(GreeterUsersModelTestExec
90target_include_directories(GreeterUsersModelTestExec PUBLIC
89target_include_directories(GreeterUsersModelTestExec PUBLIC
91 ${CMAKE_SOURCE_DIR}/plugins/LightDM
90 ${CMAKE_SOURCE_DIR}/plugins/LightDM
92 ${CMAKE_SOURCE_DIR}/plugins/Utils
91 ${CMAKE_SOURCE_DIR}/plugins/Utils
93− ${CMAKE_SOURCE_DIR}/tests/mocks/LightDM/IntegratedLightDM
92+ ${CMAKE_SOURCE_DIR}/tests/mocks/liblightdm
94 )
93 )
95set_target_properties(GreeterUsersModelTestExec PROPERTIES INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${SHELL_INSTALL_QML}/mocks/IntegratedLightDM/liblightdm")
94set_target_properties(GreeterUsersModelTestExec PROPERTIES INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${SHELL_INSTALL_QML}/mocks/IntegratedLightDM/liblightdm")
96install(TARGETS GreeterUsersModelTestExec
95install(TARGETS GreeterUsersModelTestExec
tests/plugins/LightDM/IntegratedLightDM/dbus.cpp+1
@@ -82,6 +82,7 @@ private Q_SLOTS:
82 void cleanup()
82 void cleanup()
83 {
83 {
84 delete view;
84 delete view;
85+ delete greeter; // reset singleton
85 }
86 }
86
87
87 void testGetActiveEntry()
88 void testGetActiveEntry()
tests/plugins/LightDM/IntegratedLightDM/usersmodel.cpp+34−4
@@ -14,6 +14,8 @@
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
15 */
16
16
17+#include "Greeter.h"
18+#include "MockController.h"
17#include "UsersModel.h"
19#include "UsersModel.h"
18
20
19#include <QLightDM/UsersModel>
21#include <QLightDM/UsersModel>
@@ -40,17 +42,23 @@ private:
40 return model->data(model->index(i, 0), role).toString();
42 return model->data(model->index(i, 0), role).toString();
41 }
43 }
42
44
45+ void recreateModel()
46+ {
47+ delete model;
48+ model = new UsersModel();
49+ QVERIFY(model);
50+ }
51+
43private Q_SLOTS:
52private Q_SLOTS:
44
53
45 void init()
54 void init()
46 {
55 {
47− model = new UsersModel();
56+ recreateModel();
48− QVERIFY(model);
49 }
57 }
50
58
51 void cleanup()
59 void cleanup()
52 {
60 {
53− delete model;
61+ QLightDM::MockController::instance()->reset();
54 }
62 }
55
63
56 void testMangleColor()
64 void testMangleColor()
@@ -65,8 +73,30 @@ private Q_SLOTS:
65 QVERIFY(name == "empty-name");
73 QVERIFY(name == "empty-name");
66 }
74 }
67
75
76+ void testHasGuest()
77+ {
78+ // sanity check that it doesn't start already present
79+ QCOMPARE(findName(model, QStringLiteral("*guest")), -1);
80+
81+ QLightDM::MockController::instance()->setHasGuestAccountHint(true);
82+ recreateModel();
83+
84+ int i = findName(model, QStringLiteral("*guest"));
85+ QVERIFY(i >= 0);
86+ QCOMPARE(i, model->count() - 1);
87+
88+ auto realName = model->data(i, QLightDM::UsersModel::RealNameRole).toString();
89+ QCOMPARE(realName, QStringLiteral("Guest Session"));
90+
91+ auto loggedIn = model->data(i, QLightDM::UsersModel::LoggedInRole).toBool();
92+ QVERIFY(!loggedIn);
93+
94+ auto session = model->data(i, QLightDM::UsersModel::SessionRole).toString();
95+ QCOMPARE(session, Greeter::instance()->defaultSessionHint());
96+ }
97+
68private:
98private:
69− UsersModel *model;
99+ UsersModel *model = nullptr;
70};
100};
71
101
72QTEST_MAIN(GreeterUsersModelTest)
102QTEST_MAIN(GreeterUsersModelTest)
tests/qmltests/Greeter/tst_Greeter.qml+28−2
@@ -144,7 +144,7 @@ Item {
144
144
145 function init() {
145 function init() {
146 greeterSettings.lockedOutTime = 0;
146 greeterSettings.lockedOutTime = 0;
147− LightDMController.selectUserHint = "";
147+ LightDMController.reset();
148 greeter.failedLoginsDelayAttempts = 7;
148 greeter.failedLoginsDelayAttempts = 7;
149 greeter.failedLoginsDelayMinutes = 5;
149 greeter.failedLoginsDelayMinutes = 5;
150 teaseSpy.clear();
150 teaseSpy.clear();
@@ -201,7 +201,10 @@ Item {
201 var i = getIndexOf(name);
201 var i = getIndexOf(name);
202 compare(view.currentIndex, i);
202 compare(view.currentIndex, i);
203 compare(AccountsService.user, name);
203 compare(AccountsService.user, name);
204+ if (name === "*guest") // guest has no authenticationUser set
205+ compare(LightDM.Greeter.authenticationUser, "");
206+ else
204− compare(LightDM.Greeter.authenticationUser, name);
207+ compare(LightDM.Greeter.authenticationUser, name);
205 return i;
208 return i;
206 }
209 }
207
210
@@ -586,6 +589,29 @@ Item {
586 verifySelected(LightDM.Users.data(0, LightDM.UserRoles.NameRole));
589 verifySelected(LightDM.Users.data(0, LightDM.UserRoles.NameRole));
587 }
590 }
588
591
592+ function test_selectUserHintGuest() {
593+ LightDMController.hasGuestAccountHint = true;
594+ LightDMController.selectGuestHint = true;
595+ LightDMController.selectUserHint = "info-prompt";
596+ resetLoader();
597+ verifySelected("*guest");
598+ }
599+
600+ function test_selectUserHintGuestWithNoGuest() {
601+ LightDMController.selectGuestHint = true;
602+ LightDMController.selectUserHint = "info-prompt";
603+ resetLoader();
604+ verifySelected("info-prompt");
605+ }
606+
607+ function test_hasGuestAccountHint() {
608+ LightDMController.hasGuestAccountHint = true;
609+ resetLoader();
610+ var i = selectUser("*guest");
611+ compare(i, LightDM.Users.count - 1); // guest should be last
612+ verify(!view.locked);
613+ }
614+
589 function test_fingerprintSuccess() {
615 function test_fingerprintSuccess() {
590 var index = selectUser("has-password");
616 var index = selectUser("has-password");
591 unlockAndShowGreeter(); // turn on lockscreen mode
617 unlockAndShowGreeter(); // turn on lockscreen mode