on xenial help sign in
Merge greeter-guest
27fa2c9Michael Terry
plugins/LightDM/Greeter.cpp+5
@@ -17,6 +17,7 @@
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
22static Greeter *singleton = nullptr;
23static Greeter *singleton = nullptr;
@@ -43,6 +44,10 @@ Greeter::Greeter(QObject* parent)
43 connect(d->m_greeter, &QLightDM::Greeter::authenticationComplete,
44 connect(d->m_greeter, &QLightDM::Greeter::authenticationComplete,
44 this, &Greeter::authenticationCompleteFilter);
45 this, &Greeter::authenticationCompleteFilter);
45
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+
46 d->m_greeter->connectSync();
51 d->m_greeter->connectSync();
47}
52}
48
53
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
qml/Greeter/LoginList.qml−2
@@ -36,12 +36,10 @@ StyledItem {
36 readonly property int cellHeight: units.gu(5)
36 readonly property int cellHeight: units.gu(5)
37 readonly property int highlightedHeight: units.gu(15)
37 readonly property int highlightedHeight: units.gu(15)
38 readonly property int moveDuration: UbuntuAnimation.FastDuration
38 readonly property int moveDuration: UbuntuAnimation.FastDuration
39− property string selectedSession
40 property string currentSession
39 property string currentSession
41 readonly property string currentUser: userList.currentItem.username
40 readonly property string currentUser: userList.currentItem.username
42 property bool wasPrompted: false
41 property bool wasPrompted: false
43
42
44− signal loginListSessionChanged(string session)
45 signal responded(string response)
43 signal responded(string response)
46 signal selected(int index)
44 signal selected(int index)
47 signal sessionChooserButtonClicked()
45 signal sessionChooserButtonClicked()
qml/Greeter/WideView.qml+1−4
@@ -125,8 +125,6 @@ FocusScope {
125 id: loginList
125 id: loginList
126 objectName: "loginList"
126 objectName: "loginList"
127
127
128− property int selectedUserIndex: 0
129−
130 width: units.gu(40)
128 width: units.gu(40)
131 anchors {
129 anchors {
132 left: parent.left
130 left: parent.left
@@ -141,11 +139,10 @@ FocusScope {
141 Behavior on boxVerticalOffset { UbuntuNumberAnimation {} }
139 Behavior on boxVerticalOffset { UbuntuNumberAnimation {} }
142
140
143 model: root.userModel
141 model: root.userModel
144− currentSession: LightDMService.users.data(selectedUserIndex, LightDMService.userRoles.SessionRole);
145 onResponded: root.responded(response)
142 onResponded: root.responded(response)
146 onSelected: {
143 onSelected: {
147 root.selected(index)
144 root.selected(index)
148− loginList.selectedUserIndex = index;
145+ currentSession = LightDMService.users.data(index, LightDMService.userRoles.SessionRole);
149 }
146 }
150 onSessionChooserButtonClicked: parent.state = "SessionsList"
147 onSessionChooserButtonClicked: parent.state = "SessionsList"
151
148
tests/mocks/liblightdm/MockController.cpp+24
@@ -152,6 +152,30 @@ void MockController::setUserMode(const QString &userMode)
152 }
152 }
153}
153}
154
154
155+QString MockController::sessionName() const
156+{
157+ return QString("INVALID - Please read directly from the UserModel");
158+}
159+
160+void MockController::setSessionName(const QString &sessionName)
161+{
162+ // Let the user model deal with this
163+ Q_EMIT sessionNameChanged(sessionName, m_currentUsername);
164+}
165+
166+QString MockController::currentUsername() const
167+{
168+ return m_currentUsername;
169+}
170+
171+void MockController::setCurrentUsername(const QString &username)
172+{
173+ if (m_currentUsername != username) {
174+ m_currentUsername = username;
175+ Q_EMIT currentUsernameChanged();
176+ }
177+}
178+
155QString MockController::sessionMode() const
179QString MockController::sessionMode() const
156{
180{
157 return m_sessionMode;
181 return m_sessionMode;
tests/mocks/liblightdm/MockController.h+15
@@ -38,6 +38,11 @@ class Q_DECL_EXPORT MockController : public QObject
38 // single, none, full
38 // single, none, full
39 Q_PROPERTY(QString sessionMode READ sessionMode WRITE setSessionMode NOTIFY sessionModeChanged)
39 Q_PROPERTY(QString sessionMode READ sessionMode WRITE setSessionMode NOTIFY sessionModeChanged)
40
40
41+ // This would be best as a Q_INVOKABLE, but using a property allows for
42+ // keeping the mock cleaner
43+ Q_PROPERTY(QString sessionName READ sessionName WRITE setSessionName NOTIFY sessionNameChanged)
44+ Q_PROPERTY(QString currentUsername READ currentUsername WRITE setCurrentUsername NOTIFY currentUsernameChanged)
45+
41 Q_PROPERTY(int numAvailableSessions READ numFullSessions CONSTANT)
46 Q_PROPERTY(int numAvailableSessions READ numFullSessions CONSTANT)
42 Q_PROPERTY(int numSessions READ numSessions WRITE setNumSessions NOTIFY numSessionsChanged)
47 Q_PROPERTY(int numSessions READ numSessions WRITE setNumSessions NOTIFY numSessionsChanged)
43
48
@@ -68,6 +73,12 @@ public:
68 QString sessionMode() const;
73 QString sessionMode() const;
69 void setSessionMode(const QString &sessionMode);
74 void setSessionMode(const QString &sessionMode);
70
75
76+ QString sessionName() const;
77+ void setSessionName(const QString &userIndex);
78+
79+ QString currentUsername() const;
80+ void setCurrentUsername(const QString &username);
81+
71 class SessionItem
82 class SessionItem
72 {
83 {
73 public:
84 public:
@@ -81,6 +92,7 @@ public:
81 void setNumSessions(int numSessions);
92 void setNumSessions(int numSessions);
82
93
83Q_SIGNALS:
94Q_SIGNALS:
95+ void currentUsernameChanged();
84 void selectUserHintChanged();
96 void selectUserHintChanged();
85 void selectGuestHintChanged();
97 void selectGuestHintChanged();
86 void hasGuestAccountHintChanged();
98 void hasGuestAccountHintChanged();
@@ -89,10 +101,13 @@ Q_SIGNALS:
89 void userModeChanged();
101 void userModeChanged();
90 void sessionModeChanged();
102 void sessionModeChanged();
91 void numSessionsChanged();
103 void numSessionsChanged();
104+ void sessionNameChanged(const QString &sessionName, const QString &username);
92
105
93private:
106private:
94 explicit MockController(QObject* parent=0);
107 explicit MockController(QObject* parent=0);
95
108
109+ QString m_currentUsername;
110+ QString m_sessionName;
96 QString m_selectUserHint;
111 QString m_selectUserHint;
97 bool m_selectGuestHint;
112 bool m_selectGuestHint;
98 bool m_hasGuestAccountHint;
113 bool m_hasGuestAccountHint;
tests/mocks/liblightdm/MockUsersModel.cpp+37−14
@@ -73,6 +73,8 @@ UsersModel::UsersModel(QObject *parent)
73 this, &UsersModel::resetEntries);
73 this, &UsersModel::resetEntries);
74 connect(MockController::instance(), &MockController::userModeChanged,
74 connect(MockController::instance(), &MockController::userModeChanged,
75 this, &UsersModel::resetEntries);
75 this, &UsersModel::resetEntries);
76+ connect(MockController::instance(), &MockController::sessionNameChanged,
77+ this, &UsersModel::setCurrentSessionName);
76 resetEntries();
78 resetEntries();
77}
79}
78
80
@@ -81,6 +83,22 @@ UsersModel::~UsersModel()
81 delete d_ptr;
83 delete d_ptr;
82}
84}
83
85
86+void UsersModel::setCurrentSessionName(const QString &sessionName, const QString &username)
87+{
88+ Q_D(UsersModel);
89+
90+ int usersIndex = 0;
91+ for (auto &entry : d->entries) {
92+ if (username == entry.username) {
93+ entry.session = sessionName;
94+ break;
95+ }
96+ usersIndex++;
97+ }
98+
99+ dataChanged(index(usersIndex, 0), index(usersIndex, 0), QVector<int>(SessionRole));
100+}
101+
84int UsersModel::rowCount(const QModelIndex &parent) const
102int UsersModel::rowCount(const QModelIndex &parent) const
85{
103{
86 Q_D(const UsersModel);
104 Q_D(const UsersModel);
@@ -144,28 +162,33 @@ void UsersModel::resetEntries()
144 } else if (userMode == "single-pin") {
162 } else if (userMode == "single-pin") {
145 d->entries = {{"has-pin", "Has PIN", "", 0, false, false, "ubuntu", 0}};
163 d->entries = {{"has-pin", "Has PIN", "", 0, false, false, "ubuntu", 0}};
146 } else if (userMode == "full") {
164 } else if (userMode == "full") {
165+ /*
166+ * Since the real model sorts these entries alphabetically, the model indices
167+ * don't line up correctly. The easiest way to fix this is to ensure that this
168+ * list of entries REMAINS IN ALPHABETICAL ORDER at all times.
169+ */
147 d->entries = {
170 d->entries = {
171+ { "active", "Active Account", 0, 0, true, false, "ubuntu", 0 },
172+ { "auth-error", "Auth Error", 0, 0, false, false, "ubuntu", 0 },
173+ { "black-background", "Black Background", "#000000", 0, false, false, "ubuntu", 0 },
174+ { "color-background", "Color Background", "#E95420", 0, false, false, "ubuntu", 0 },
175+ { "different-prompt", "Different Prompt", 0, 0, false, false, "ubuntu", 0 },
176+ { "empty-name", "", 0, 0, false, false, "ubuntu", 0 },
148 { "has-password", "Has Password", 0, 0, false, false, "ubuntu", 0 },
177 { "has-password", "Has Password", 0, 0, false, false, "ubuntu", 0 },
149 { "has-pin", "Has PIN", 0, 0, false, false, "ubuntu", 0 },
178 { "has-pin", "Has PIN", 0, 0, false, false, "ubuntu", 0 },
150− { "different-prompt", "Different Prompt", 0, 0, false, false, "ubuntu", 0 },
151− { "no-password", "No Password", 0, 0, false, false, "ubuntu", 0 },
152− { "auth-error", "Auth Error", 0, 0, false, false, "ubuntu", 0 },
153− { "two-factor", "Two Factor", 0, 0, false, false, "ubuntu", 0 },
154− { "info-prompt", "Info Prompt", 0, 0, false, false, "ubuntu", 0 },
155 { "html-info-prompt", "HTML Info Prompt", 0, 0, false, false, "ubuntu", 0 },
179 { "html-info-prompt", "HTML Info Prompt", 0, 0, false, false, "ubuntu", 0 },
180+ { "info-prompt", "Info Prompt", 0, 0, false, false, "ubuntu", 0 },
156 { "long-info-prompt", "Long Info Prompt", 0, 0, false, false, "ubuntu", 0 },
181 { "long-info-prompt", "Long Info Prompt", 0, 0, false, false, "ubuntu", 0 },
157− { "wide-info-prompt", "Wide Info Prompt", 0, 0, false, false, "ubuntu", 0 },
158− { "multi-info-prompt", "Multi Info Prompt", 0, 0, false, false, "ubuntu", 0 },
159 { "long-name", "Long name (far far too long to fit, seriously this would never fit on the screen, you will never see this part of the name)", 0, 0, false, false, "ubuntu", 0 },
182 { "long-name", "Long name (far far too long to fit, seriously this would never fit on the screen, you will never see this part of the name)", 0, 0, false, false, "ubuntu", 0 },
160− { "color-background", "Color Background", "#E95420", 0, false, false, "ubuntu", 0 },
183+ { "multi-info-prompt", "Multi Info Prompt", 0, 0, false, false, "ubuntu", 0 },
161− // white and black are a bit redundant, but useful for manually testing if UI is still readable
162− { "white-background", "White Background", "#ffffff", 0, false, false, "ubuntu", 0 },
163− { "black-background", "Black Background", "#000000", 0, false, false, "ubuntu", 0 },
164 { "no-background", "No Background", "", 0, false, false, "ubuntu", 0 },
184 { "no-background", "No Background", "", 0, false, false, "ubuntu", 0 },
165− { "unicode", "가나다라마", 0, 0, false, false, "ubuntu", 0 },
185+ { "no-password", "No Password", 0, 0, false, false, "ubuntu", 0 },
166 { "no-response", "No Response", 0, 0, false, false, "ubuntu", 0 },
186 { "no-response", "No Response", 0, 0, false, false, "ubuntu", 0 },
167− { "empty-name", "", 0, 0, false, false, "ubuntu", 0 },
187+ { "two-factor", "Two Factor", 0, 0, false, false, "ubuntu", 0 },
168− { "active", "Active Account", 0, 0, true, false, "ubuntu", 0 },
188+ { "unicode", "가나다라마", 0, 0, false, false, "ubuntu", 0 },
189+ // white and black are a bit redundant, but useful for manually testing if UI is still readable
190+ { "white-background", "White Background", "#ffffff", 0, false, false, "ubuntu", 0 },
191+ { "wide-info-prompt", "Wide Info Prompt", 0, 0, false, false, "ubuntu", 0 }
169 };
192 };
170 }
193 }
171
194
tests/mocks/liblightdm/MockUsersModel.h+2
@@ -48,6 +48,8 @@ public:
48 int rowCount(const QModelIndex &parent) const override;
48 int rowCount(const QModelIndex &parent) const override;
49 QVariant data(const QModelIndex &index, int role) const override;
49 QVariant data(const QModelIndex &index, int role) const override;
50
50
51+ void setCurrentSessionName(const QString &sessionName, const QString &username);
52+
51private Q_SLOTS:
53private Q_SLOTS:
52 void resetEntries();
54 void resetEntries();
53
55
tests/qmltests/Greeter/tst_WideView.qml+31
@@ -411,6 +411,37 @@ StyledItem {
411 waitForRendering(view);
411 waitForRendering(view);
412 }
412 }
413
413
414+ function test_changingSessionSticksToUser() {
415+ LightDMController.sessionMode = "full";
416+
417+ var loginList = findChild(view, "loginList");
418+ var fakeSession1 = "ASessionWillNeverBeCalledThis";
419+ var fakeSession2 = "ASessionWillAlsoNeverBeCalledThis";
420+
421+ // For this test to work, a user has to be selected and the
422+ // mock must be made aware of that user
423+ selectUser("has-password")
424+ LightDMController.currentUsername = "has-password"
425+ compare(LightDM.Sessions.count > 1, true);
426+ compare(loginList.currentSession != fakeSession1, true);
427+ LightDMController.sessionName = fakeSession1;
428+
429+ selectUser("has-pin")
430+ LightDMController.currentUsername = "has-pin"
431+ compare(loginList.currentSession != fakeSession2, true);
432+ LightDMController.sessionName = fakeSession2;
433+
434+ selectUser("has-password")
435+ tryCompare(loginList, "currentSession", fakeSession1);
436+
437+ selectUser("has-pin")
438+ tryCompare(loginList, "currentSession", fakeSession2);
439+
440+ // Force a model reset
441+ LightDMController.userMode = "single";
442+ LightDMController.userMode = "full";
443+ }
444+
414 function test_tease_data() {
445 function test_tease_data() {
415 return [
446 return [
416 {tag: "locked", x: 0, offset: 0, count: 0, locked: true},
447 {tag: "locked", x: 0, offset: 0, count: 0, locked: true},