on xenial help sign in
When we are locking the user out from too many login failures, notice when time passes even if the device is suspended. (LP: #1396817) Fixes: #1396817
e4df71eMichael Terry
qml/Greeter/Greeter.qml+28−12
@@ -50,7 +50,7 @@ Showable {
50
50
51 property int maxFailedLogins: -1 // disabled by default for now, will enable via settings in future
51 property int maxFailedLogins: -1 // disabled by default for now, will enable via settings in future
52 property int failedLoginsDelayAttempts: 7 // number of failed logins
52 property int failedLoginsDelayAttempts: 7 // number of failed logins
53− property int failedLoginsDelayMinutes: 5 // minutes of forced waiting
53+ property real failedLoginsDelayMinutes: 5 // minutes of forced waiting
54
54
55 signal tease()
55 signal tease()
56 signal sessionStarted()
56 signal sessionStarted()
@@ -109,7 +109,6 @@ Showable {
109
109
110 readonly property bool multiUser: LightDM.Users.count > 1
110 readonly property bool multiUser: LightDM.Users.count > 1
111 property int currentIndex
111 property int currentIndex
112− property int delayMinutes
113 property bool waiting
112 property bool waiting
114
113
115 // We want 'launcherOffset' to animate down to zero. But not to animate
114 // We want 'launcherOffset' to animate down to zero. But not to animate
@@ -180,13 +179,29 @@ Showable {
180
179
181 Timer {
180 Timer {
182 id: forcedDelayTimer
181 id: forcedDelayTimer
182+
183+ // We use a short interval and check against the system wall clock
184+ // because we have to consider the case that the system is suspended
185+ // for a few minutes. When we wake up, we want to quickly be correct.
183− interval: 1000 * 60
186+ interval: 500
187+
188+ property var delayTarget;
189+ property int delayMinutes;
190+
191+ function forceDelay(delay /* in minutes */) {
192+ delayTarget = new Date();
193+ delayTarget.setTime(delayTarget.getTime() + delay * 60000);
194+ delayMinutes = Math.ceil(delay);
195+ start();
196+ }
197+
184 onTriggered: {
198 onTriggered: {
185− if (d.delayMinutes > 0) {
199+ var diff = delayTarget - new Date();
200+ if (diff > 0) {
186− d.delayMinutes -= 1;
201+ delayMinutes = Math.ceil(diff / 60000);
187− if (d.delayMinutes > 0) {
188− start(); // go again
202+ start(); // go again
189− }
203+ } else {
204+ delayMinutes = 0;
190 }
205 }
191 }
206 }
192 }
207 }
@@ -257,7 +272,7 @@ Showable {
257 Binding {
272 Binding {
258 target: loader.item
273 target: loader.item
259 property: "delayMinutes"
274 property: "delayMinutes"
260− value: d.delayMinutes
275+ value: forcedDelayTimer.delayMinutes
261 }
276 }
262
277
263 Binding {
278 Binding {
@@ -358,9 +373,10 @@ Showable {
358 }
373 }
359
374
360 // Check if we should initiate a forced login delay
375 // Check if we should initiate a forced login delay
376+ if (failedLoginsDelayAttempts > 0
377+ && AccountsService.failedLogins > 0
361− if (failedLoginsDelayAttempts > 0 && AccountsService.failedLogins % failedLoginsDelayAttempts == 0) {
378+ && AccountsService.failedLogins % failedLoginsDelayAttempts == 0) {
362− d.delayMinutes = failedLoginsDelayMinutes;
379+ forcedDelayTimer.forceDelay(failedLoginsDelayMinutes);
363− forcedDelayTimer.start();
364 }
380 }
365
381
366 loader.item.notifyAuthenticationFailed();
382 loader.item.notifyAuthenticationFailed();
tests/qmltests/Greeter/tst_Greeter.qml+15
@@ -146,6 +146,8 @@ Item {
146 tryCompare(greeter, "waiting", false);
146 tryCompare(greeter, "waiting", false);
147 view = findChild(greeter, "testView");
147 view = findChild(greeter, "testView");
148 verifySelected(LightDM.Users.data(0, LightDM.UserRoles.NameRole));
148 verifySelected(LightDM.Users.data(0, LightDM.UserRoles.NameRole));
149+ greeter.failedLoginsDelayAttempts = 7;
150+ greeter.failedLoginsDelayMinutes = 5;
149 }
151 }
150
152
151 function cleanup() {
153 function cleanup() {
@@ -429,6 +431,19 @@ Item {
429 compare(LightDM.Infographic.username, "has-password");
431 compare(LightDM.Infographic.username, "has-password");
430 }
432 }
431
433
434+ function test_forcedDelay() {
435+ greeter.failedLoginsDelayAttempts = 1;
436+ greeter.failedLoginsDelayMinutes = 0.001; // make delay very short
437+
438+ selectUser("has-password");
439+ tryCompare(viewShowPromptSpy, "count", 1);
440+
441+ compare(view.delayMinutes, 0);
442+ view.responded("wr0ng p4ssw0rd");
443+ tryCompare(view, "delayMinutes", 1);
444+ tryCompare(view, "delayMinutes", 0);
445+ }
446+
432 function test_dbusRequestAuthenticationUser() {
447 function test_dbusRequestAuthenticationUser() {
433 selectUser("no-password");
448 selectUser("no-password");
434 LightDM.Greeter.requestAuthenticationUser("has-password");
449 LightDM.Greeter.requestAuthenticationUser("has-password");