Merge lp:~mterry/unity8/wrong-password-handling into lp:unity8

Proposed by Michael Terry on 2014-08-01
Status: Superseded
Proposed branch: lp:~mterry/unity8/wrong-password-handling
Merge into: lp:unity8
Prerequisite: lp:~mterry/unity8/greeter-misc-cleanups
Diff against target: 1663 lines (+703/-139)
45 files modified
cmake/modules/QmlTest.cmake (+2/-2)
cmake/modules/autopilot.cmake (+2/-2)
debian/control (+2/-1)
debian/unity8-private.install (+0/-2)
plugins/AccountsService/50-com.canonical.unity.AccountsService.pkla (+0/-6)
plugins/AccountsService/AccountsService.cpp (+27/-1)
plugins/AccountsService/AccountsService.h (+9/-0)
plugins/AccountsService/CMakeLists.txt (+0/-10)
plugins/AccountsService/com.canonical.unity.AccountsService.policy (+0/-24)
plugins/AccountsService/com.canonical.unity.AccountsService.xml (+28/-5)
plugins/LightDM/Greeter.cpp (+4/-1)
plugins/LightDM/Greeter.h (+1/-1)
plugins/Ubuntu/CMakeLists.txt (+1/-0)
plugins/Ubuntu/SystemImage/CMakeLists.txt (+10/-0)
plugins/Ubuntu/SystemImage/SystemImage.cpp (+34/-0)
plugins/Ubuntu/SystemImage/SystemImage.h (+38/-0)
plugins/Ubuntu/SystemImage/SystemImage.qmltypes (+17/-0)
plugins/Ubuntu/SystemImage/plugin.cpp (+34/-0)
plugins/Ubuntu/SystemImage/plugin.h (+31/-0)
plugins/Ubuntu/SystemImage/qmldir (+3/-0)
po/unity8.pot (+72/-19)
qml/Components/Lockscreen.qml (+35/-10)
qml/Components/PassphraseLockscreen.qml (+3/-4)
qml/Components/PinLockscreen.qml (+3/-4)
qml/Notifications/NotificationMenuItemFactory.qml (+0/-1)
qml/Shell.qml (+53/-8)
run.sh (+21/-20)
tests/mocks/AccountsService/AccountsService.cpp (+13/-1)
tests/mocks/AccountsService/AccountsService.h (+8/-0)
tests/mocks/LightDM/Greeter.cpp (+11/-0)
tests/mocks/LightDM/Greeter.h (+3/-0)
tests/mocks/LightDM/demo/CMakeLists.txt (+13/-0)
tests/mocks/LightDM/full/GreeterPrivate.cpp (+4/-4)
tests/mocks/LightDM/single-passphrase/GreeterPrivate.cpp (+2/-9)
tests/mocks/LightDM/single-pin/GreeterPrivate.cpp (+3/-2)
tests/mocks/Ubuntu/CMakeLists.txt (+1/-0)
tests/mocks/Ubuntu/SystemImage/CMakeLists.txt (+10/-0)
tests/mocks/Ubuntu/SystemImage/MockSystemImage.cpp (+27/-0)
tests/mocks/Ubuntu/SystemImage/MockSystemImage.h (+36/-0)
tests/mocks/Ubuntu/SystemImage/SystemImage.qmltypes (+18/-0)
tests/mocks/Ubuntu/SystemImage/plugin.cpp (+34/-0)
tests/mocks/Ubuntu/SystemImage/plugin.h (+31/-0)
tests/mocks/Ubuntu/SystemImage/qmldir (+3/-0)
tests/qmltests/Greeter/tst_Lockscreen.qml (+0/-2)
tests/qmltests/tst_ShellWithPin.qml (+56/-0)
To merge this branch: bzr merge lp:~mterry/unity8/wrong-password-handling
Reviewer Review Type Date Requested Status
PS Jenkins bot (community) continuous-integration Needs Fixing on 2014-08-14
Michael Zanetti (community) Approve on 2014-08-13
Michał Sawicz Abstain on 2014-08-12
David Planella (community) Needs Fixing on 2014-08-06
Albert Astals Cid (community) 2014-08-01 Needs Fixing on 2014-08-06
Review via email: mp+229184@code.launchpad.net

This proposal has been superseded by a proposal from 2014-08-14.

Commit message

Make wrong-password handling much nicer by showing a pretty spinner while we wait for PAM, by improving the prompt text to match designs, by forcing the user to wait five seconds after every five failed attemps, and by supporting (but not yet enabling) an opt-in "factory-reset your phone after X failed attemps" feature.

Description of the change

Make wrong-password handling much nicer by showing a pretty spinner while we wait for PAM, by improving the prompt text to match designs, by forcing the user to wait five seconds after every five failed attemps, and by supporting (but not yet enabling) an opt-in "factory-reset your phone after X failed attemps" feature.

For the spec, see the "passcode incorrect" part of:
https://docs.google.com/a/canonical.com/document/d/1VajNkWbBH61iVixXJAmOvNGiG__GWQTMXGNOZijXWJw

It should go without saying, but BE PREPARED TO HAVE YOUR PHONE WIPED while testing the factory-reset bit (which, if you want to test, you have to manually edit the maxFailedLogins field in the qml).

I've added a tiny plugin for calling the system-image daemon's FactoryReset method. Maybe it will be useful for more things in the future.

I also cleaned up the AccountsService field definitions a bit.

== Using AccountsService to store the failedLogins count ==

I thought this might deserve a few words.

My original thought was to take advantage of our PAM infrastructure and use pam_tally2, which can do fancy tallying, login denial, and timeouts. But (A) that does not give any extra security since we'd need to specify a user-writable tally file as long as we don't have a split greeter, (B) it does not give us any warning when we are about to go over our limit, and (C) does not tell us *when* we are delaying due to too many failed logins, so we can't tell the user.

So we needed to store it ourselves. I could have used gsettings or similar, but we want to keep track of this per-user and stuffing a map into a settings backend isn't always convenient.

While AccountsService is mostly designed for data that can be shared between greeters and user sessions, it seemed like not an awful place to store the data and gives us per-user semantics for free. And we can share the data store with the eventually-split greeter (although that is certainly small potatoes).

== Checklist ==

 * Are there any related MPs required for this MP to build/function as expected? Please list.
 - Yes, greeter-misc-cleanups

 * Did you perform an exploratory manual test run of your code change and any related functionality?
 - Yes

 * Did you make sure that your branch does not contain spurious tags?
 - Yes

 * If you changed the packaging (debian), did you subscribe the ubuntu-unity team to this MP?
 - I'm on the team

 * If you changed the UI, has there been a design review?
 - Olga and Esti are fine with this for now, but the visuals are all being updated. I just wanted to get the backend work in.

To post a comment you must log in.
1015. By Michael Terry on 2014-08-01

Switch to 7 attempts, 5 min lockout

1016. By Michael Terry on 2014-08-01

Handle translated PAM prompts

1017. By Michael Terry on 2014-08-04

Merge from trunk

1018. By Michael Terry on 2014-08-04

Fix whitespace

Albert Astals Cid (aacid) wrote :

Seems like the qmluitests failure may be related to this, no?

review: Needs Fixing
David Planella (dpm) wrote :

This merge proposal is introducing a few new translatable strings. Could it provide an updated .pot file as well, so that translators can do their job in time? Thanks!

review: Needs Fixing
Michał Sawicz (saviq) wrote :

Indeed, make pot_file please.

We've a plan for a ~push hook that will warn us if stuff like this happens, but... too many higher priority items...

1019. By Michael Terry on 2014-08-06

Update pot file

1020. By Michael Terry on 2014-08-06

Fix mocks to use same prompt string as PAM

Michael Terry (mterry) wrote :

Updated pot file and fixed the test.

1021. By Michael Terry on 2014-08-07

Merge from trunk

Michał Sawicz (saviq) wrote :

Please delete tag 7.85+14.10.20140428.2-0ubuntu1 in this branch and any local checkouts you might have.

review: Needs Fixing
Michael Terry (mterry) wrote :

Deleted that tag.

Michał Sawicz (saviq) :
review: Abstain
Michael Zanetti (mzanetti) wrote :

The lockscreen features showInfoPopup(). You should use that one for the wipe warning instead of adding your own Dialog component in order to keep design consistent with the same warning for locking the SIM card etc.

review: Needs Fixing
Michael Zanetti (mzanetti) wrote :

On the graphical changes, the Spinner in the lockscreen etc, that doesn't match with the new design... I'll probably have to kill/redo that again when merging the new-lockscreen-design branch.

Michael Zanetti (mzanetti) wrote :

I'm curious if there are any downsides of adding all those plugins with just one method. You think it might make sense to merge the SystemImage plugin with e.g. the Powerd plugin into a single "System" plugin supporting Powerd, SystemImage etc?

Michael Zanetti (mzanetti) wrote :

some inline comments

Michael Terry (mterry) wrote :

I can use the existing dialog support, didn't even notice it.

The spinner can go away. Design didn't like it anyway. I was going to wait until I landed the "nodelay" change so PAM feedback is instant, but it can go away now instead, if it makes the other merge easier.

As for the plugin... It's just a bunch more boilerplate. And maybe some extra binary-size overhead? I worry that a generic "System" plugin would be a bit of a dumping place with little cohesion between the pieces. But I'm not a -1, just a +0 on the idea.

OK, working on the dialog and spinner bits, as well as merging with trunk again.

1022. By Michael Terry on 2014-08-13

Merge from trunk

1023. By Michael Terry on 2014-08-13

Remove spinner; design didn't like it and it complicates other UI changes down the line

1024. By Michael Terry on 2014-08-13

Use the built-in lockscreen dialog rather than a home-brewn one

Michael Terry (mterry) wrote :

OK. No spinner and I'm using the built-in lockscreen dialog. Plus merged from trunk.

I didn't address the bevy-of-tiny-plugins issue. How strong did you feel about it?

Michael Zanetti (mzanetti) wrote :

2 inline comments. You forgot to update some things after the latest changes.

review: Needs Fixing
1025. By Michael Terry on 2014-08-13

Update pot

1026. By Michael Terry on 2014-08-13

update object names

Michael Zanetti (mzanetti) wrote :

there's also some more test failures:

FAIL! : qmltestrunner::ShellWithPin::test_factoryReset() function returned unexpected result
   Actual (): false
   Expected (): true
   Loc: [/home/mzanetti/Development/reviews/wrong-password-handling/tests/qmltests/tst_ShellWithPin.qml(223)]
FAIL! : qmltestrunner::ShellWithPin::test_failedLoginsCount() property showProgress
   Actual (): 1
   Expected (): 0
   Loc: [/home/mzanetti/Development/reviews/wrong-password-handling/tests/qmltests/tst_ShellWithPin.qml(106)]
FAIL! : qmltestrunner::ShellWithPin::test_failedLoginsCount() property failedLogins
   Actual (): 0
   Expected (): 1
   Loc: [/home/mzanetti/Development/reviews/wrong-password-handling/tests/qmltests/tst_ShellWithPin.qml(195)]
FAIL! : qmltestrunner::ShellWithPin::test_login() property showProgress
   Actual (): 1
   Expected (): 0
   Loc: [/home/mzanetti/Development/reviews/wrong-password-handling/tests/qmltests/tst_ShellWithPin.qml(106)]
FAIL! : qmltestrunner::ShellWithPin::test_login() property count
   Actual (): 0
   Expected (): 1
   Loc: [/home/mzanetti/Development/reviews/wrong-password-handling/tests/qmltests/tst_ShellWithPin.qml(128)]
FAIL! : qmltestrunner::ShellWithPin::test_wrongEntries() property showProgress
   Actual (): 1
   Expected (): 0
   Loc: [/home/mzanetti/Development/reviews/wrong-password-handling/tests/qmltests/tst_ShellWithPin.qml(106)]
FAIL! : qmltestrunner::ShellWithPin::test_wrongEntries() property text
   Actual (): Enter your passcode
   Expected (): Incorrect passcode
Please re-enter
   Loc: [/home/mzanetti/Development/reviews/wrong-password-handling/tests/qmltests/tst_ShellWithPin.qml(208)]
PASS : qmltestrunner::ShellWithPin::cleanupTestCase()
Totals: 6 passed, 7 failed, 0 skipped

review: Needs Fixing
Michael Terry (mterry) wrote :

Ugh, you're right. I was hasty. Pot updated again, and test fixed.

Michael Zanetti (mzanetti) wrote :

ok. lookin good now. thanks

 * Did you perform an exploratory manual test run of the code change and any related functionality?

yes

 * Did CI run pass? If not, please explain why.

waiting on the latest run, but there seem to be general problems with tests atm.

review: Approve

Unmerged revisions

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'cmake/modules/QmlTest.cmake'
2--- cmake/modules/QmlTest.cmake 2014-08-06 12:59:59 +0000
3+++ cmake/modules/QmlTest.cmake 2014-08-13 18:12:02 +0000
4@@ -103,7 +103,7 @@
5 endif()
6
7 set(qmltest_command
8- env ${qmltest_ENVIRONMENT}
9+ env ${qmltest_ENVIRONMENT} UNITY_TESTING=1
10 ${qmltestrunner_exe} -input ${CMAKE_CURRENT_SOURCE_DIR}/${qmltest_FILE}.qml
11 ${qmltestrunner_imports}
12 ${ITERATIONS_STRING}
13@@ -117,7 +117,7 @@
14 set(LD_PRELOAD_PATH "LD_PRELOAD=/usr/lib/${ARCH_TRIPLET}/mesa/libGL.so.1")
15 endif()
16 set(qmltest_xvfb_command
17- env ${qmltest_ENVIRONMENT} ${LD_PRELOAD_PATH}
18+ env ${qmltest_ENVIRONMENT} ${LD_PRELOAD_PATH} UNITY_TESTING=1
19 xvfb-run --server-args "-screen 0 1024x768x24" --auto-servernum
20 ${qmltestrunner_exe} -input ${CMAKE_CURRENT_SOURCE_DIR}/${qmltest_FILE}.qml
21 ${qmltestrunner_imports}
22
23=== modified file 'cmake/modules/autopilot.cmake'
24--- cmake/modules/autopilot.cmake 2014-03-17 14:27:05 +0000
25+++ cmake/modules/autopilot.cmake 2014-08-13 18:12:02 +0000
26@@ -2,7 +2,7 @@
27
28 function(declare_autopilot_test TEST_NAME TEST_SUITE WORKING_DIR)
29 add_custom_target(autopilot-${TEST_NAME}
30- COMMAND LANG=C QML2_IMPORT_PATH=${SHELL_INSTALL_QML}/mocks python3 -m autopilot.run run ${TEST_SUITE}
31+ COMMAND UNITY_TESTING=1 LANG=C QML2_IMPORT_PATH=${SHELL_INSTALL_QML}/mocks python3 -m autopilot.run run ${TEST_SUITE}
32 WORKING_DIRECTORY ${WORKING_DIR}
33 DEPENDS fake_install
34 )
35@@ -14,7 +14,7 @@
36 add_dependencies(autopilot autopilot-${TEST_NAME})
37
38 add_custom_target(autopilot2-${TEST_NAME}
39- COMMAND LANG=C QML2_IMPORT_PATH=${SHELL_INSTALL_QML}/mocks python2 -m autopilot.run run ${TEST_SUITE}
40+ COMMAND UNITY_TESTING=1 LANG=C QML2_IMPORT_PATH=${SHELL_INSTALL_QML}/mocks python2 -m autopilot.run run ${TEST_SUITE}
41 WORKING_DIRECTORY ${WORKING_DIR}
42 DEPENDS fake_install
43 )
44
45=== modified file 'debian/control'
46--- debian/control 2014-08-11 19:02:49 +0000
47+++ debian/control 2014-08-13 18:12:02 +0000
48@@ -165,7 +165,8 @@
49 Architecture: any
50 Multi-Arch: same
51 Pre-Depends: ${misc:Pre-Depends},
52-Depends: gsettings-ubuntu-schemas,
53+Depends: accountsservice-ubuntu-schemas,
54+ gsettings-ubuntu-schemas,
55 libhardware2,
56 libunity-core-6.0-9,
57 pay-service,
58
59=== modified file 'debian/unity8-private.install'
60--- debian/unity8-private.install 2014-06-27 20:48:38 +0000
61+++ debian/unity8-private.install 2014-08-13 18:12:02 +0000
62@@ -10,5 +10,3 @@
63 usr/lib/*/unity8/qml/Utils
64 usr/share/accountsservice/interfaces
65 usr/share/dbus-1/interfaces
66-usr/share/polkit-1
67-var/lib/polkit-1
68
69=== removed file 'plugins/AccountsService/50-com.canonical.unity.AccountsService.pkla'
70--- plugins/AccountsService/50-com.canonical.unity.AccountsService.pkla 2013-08-12 18:41:45 +0000
71+++ plugins/AccountsService/50-com.canonical.unity.AccountsService.pkla 1970-01-01 00:00:00 +0000
72@@ -1,6 +0,0 @@
73-[Allow LightDM to set Unity AccountsService fields]
74-Identity=unix-user:lightdm
75-Action=com.canonical.unity.AccountsService.ModifyAnyUser
76-ResultActive=yes
77-ResultInactive=yes
78-ResultAny=yes
79
80=== modified file 'plugins/AccountsService/AccountsService.cpp'
81--- plugins/AccountsService/AccountsService.cpp 2014-07-02 16:17:14 +0000
82+++ plugins/AccountsService/AccountsService.cpp 2014-08-13 18:12:02 +0000
83@@ -27,7 +27,8 @@
84 m_user(qgetenv("USER")),
85 m_demoEdges(false),
86 m_statsWelcomeScreen(false),
87- m_passwordDisplayHint(Keyboard)
88+ m_passwordDisplayHint(Keyboard),
89+ m_failedLogins(0)
90 {
91 connect(m_service, SIGNAL(propertiesChanged(const QString &, const QString &, const QStringList &)),
92 this, SLOT(propertiesChanged(const QString &, const QString &, const QStringList &)));
93@@ -49,6 +50,7 @@
94 updateBackgroundFile();
95 updateStatsWelcomeScreen();
96 updatePasswordDisplayHint();
97+ updateFailedLogins();
98 }
99
100 bool AccountsService::demoEdges() const
101@@ -113,6 +115,26 @@
102 }
103 }
104
105+void AccountsService::updateFailedLogins()
106+{
107+ uint failedLogins = m_service->getUserProperty(m_user, "com.canonical.unity.AccountsService.Private", "FailedLogins").toUInt();
108+ if (m_failedLogins != failedLogins) {
109+ m_failedLogins = failedLogins;
110+ Q_EMIT failedLoginsChanged();
111+ }
112+}
113+
114+uint AccountsService::failedLogins() const
115+{
116+ return m_failedLogins;
117+}
118+
119+void AccountsService::setFailedLogins(uint failedLogins)
120+{
121+ m_failedLogins = failedLogins;
122+ m_service->setUserProperty(m_user, "com.canonical.unity.AccountsService.Private", "FailedLogins", failedLogins);
123+}
124+
125 void AccountsService::propertiesChanged(const QString &user, const QString &interface, const QStringList &changed)
126 {
127 if (m_user != user) {
128@@ -123,6 +145,10 @@
129 if (changed.contains("demo-edges")) {
130 updateDemoEdges();
131 }
132+ } else if (interface == "com.canonical.unity.AccountsService.Private") {
133+ if (changed.contains("FailedLogins")) {
134+ updateFailedLogins();
135+ }
136 } else if (interface == "com.ubuntu.touch.AccountsService.SecurityPrivacy") {
137 if (changed.contains("StatsWelcomeScreen")) {
138 updateStatsWelcomeScreen();
139
140=== modified file 'plugins/AccountsService/AccountsService.h'
141--- plugins/AccountsService/AccountsService.h 2014-07-02 16:17:14 +0000
142+++ plugins/AccountsService/AccountsService.h 2014-08-13 18:12:02 +0000
143@@ -45,6 +45,10 @@
144 Q_PROPERTY (PasswordDisplayHint passwordDisplayHint
145 READ passwordDisplayHint
146 NOTIFY passwordDisplayHintChanged)
147+ Q_PROPERTY (uint failedLogins
148+ READ failedLogins
149+ WRITE setFailedLogins
150+ NOTIFY failedLoginsChanged)
151
152 public:
153 enum PasswordDisplayHint {
154@@ -61,6 +65,8 @@
155 QString backgroundFile() const;
156 bool statsWelcomeScreen() const;
157 PasswordDisplayHint passwordDisplayHint() const;
158+ uint failedLogins() const;
159+ void setFailedLogins(uint failedLogins);
160
161 Q_SIGNALS:
162 void userChanged();
163@@ -68,6 +74,7 @@
164 void backgroundFileChanged();
165 void statsWelcomeScreenChanged();
166 void passwordDisplayHintChanged();
167+ void failedLoginsChanged();
168
169 private Q_SLOTS:
170 void propertiesChanged(const QString &user, const QString &interface, const QStringList &changed);
171@@ -78,6 +85,7 @@
172 void updateBackgroundFile();
173 void updateStatsWelcomeScreen();
174 void updatePasswordDisplayHint();
175+ void updateFailedLogins();
176
177 AccountsServiceDBusAdaptor *m_service;
178 QString m_user;
179@@ -85,6 +93,7 @@
180 QString m_backgroundFile;
181 bool m_statsWelcomeScreen;
182 PasswordDisplayHint m_passwordDisplayHint;
183+ uint m_failedLogins;
184 };
185
186 #endif
187
188=== modified file 'plugins/AccountsService/CMakeLists.txt'
189--- plugins/AccountsService/CMakeLists.txt 2014-05-02 22:57:00 +0000
190+++ plugins/AccountsService/CMakeLists.txt 2014-08-13 18:12:02 +0000
191@@ -10,8 +10,6 @@
192
193 add_unity8_plugin(AccountsService 0.1 AccountsService TARGETS AccountsService-qml)
194
195-set(POLKIT_LIB_DIR "${CMAKE_INSTALL_LOCALSTATEDIR}/lib/polkit-1")
196-set(POLKIT_DATA_DIR "${CMAKE_INSTALL_PREFIX}/share/polkit-1")
197 set(DBUS_IFACE_DIR "${CMAKE_INSTALL_PREFIX}/share/dbus-1/interfaces")
198 set(ACCOUNTS_IFACE_DIR "${CMAKE_INSTALL_PREFIX}/share/accountsservice/interfaces")
199
200@@ -24,11 +22,3 @@
201 execute_process(COMMAND mkdir -p \"\$ENV{DESTDIR}${ACCOUNTS_IFACE_DIR}\")
202 execute_process(COMMAND ln -sf ../../dbus-1/interfaces/com.canonical.unity.AccountsService.xml \"\$ENV{DESTDIR}${ACCOUNTS_IFACE_DIR}\")
203 ")
204-
205-install(FILES com.canonical.unity.AccountsService.policy
206- DESTINATION "${POLKIT_DATA_DIR}/actions"
207- )
208-
209-install(FILES 50-com.canonical.unity.AccountsService.pkla
210- DESTINATION "${POLKIT_LIB_DIR}/localauthority/10-vendor.d"
211- )
212
213=== removed file 'plugins/AccountsService/com.canonical.unity.AccountsService.policy'
214--- plugins/AccountsService/com.canonical.unity.AccountsService.policy 2013-08-12 18:41:45 +0000
215+++ plugins/AccountsService/com.canonical.unity.AccountsService.policy 1970-01-01 00:00:00 +0000
216@@ -1,24 +0,0 @@
217-<?xml version="1.0" encoding="UTF-8"?>
218-
219-<policyconfig>
220- <action id="com.canonical.unity.AccountsService.ModifyOwnUser">
221- <description>Set properties of own user</description>
222- <message>Authentication is required to set one's own unity properties.</message>
223- <defaults>
224- <allow_any>yes</allow_any>
225- <allow_inactive>yes</allow_inactive>
226- <allow_active>yes</allow_active>
227- </defaults>
228- </action>
229-
230- <action id="com.canonical.unity.AccountsService.ModifyAnyUser">
231- <description>Set properties of any user</description>
232- <message>Authentication is required to set another user's unity
233-properties.</message>
234- <defaults>
235- <allow_any>no</allow_any>
236- <allow_inactive>no</allow_inactive>
237- <allow_active>no</allow_active>
238- </defaults>
239- </action>
240-</policyconfig>
241
242=== modified file 'plugins/AccountsService/com.canonical.unity.AccountsService.xml'
243--- plugins/AccountsService/com.canonical.unity.AccountsService.xml 2013-08-23 19:49:18 +0000
244+++ plugins/AccountsService/com.canonical.unity.AccountsService.xml 2014-08-13 18:12:02 +0000
245@@ -3,22 +3,45 @@
246
247 <annotation name="org.freedesktop.Accounts.VendorExtension" value="true"/>
248
249- <annotation name="org.freedesktop.Accounts.Authentication.ChangeOwn"
250- value="com.canonical.unity.AccountsService.ModifyOwnUser"/>
251-
252 <annotation name="org.freedesktop.Accounts.Authentication.ReadAny"
253- value="com.canonical.unity.AccountsService.ModifyAnyUser"/>
254+ value="com.ubuntu.AccountsService.GreeterReadAny"/>
255
256 <annotation name="org.freedesktop.Accounts.Authentication.ChangeAny"
257- value="com.canonical.unity.AccountsService.ModifyAnyUser"/>
258+ value="com.ubuntu.AccountsService.GreeterChangeAny"/>
259
260+ <!-- Should have been named DemoEdges, sorry folks. -mterry -->
261 <property name="demo-edges" type="b" access="readwrite">
262 <annotation name="org.freedesktop.Accounts.DefaultValue" value="true"/>
263 </property>
264
265+ <!-- Should have been named LauncherItems, sorry folks. -mterry -->
266 <property name="launcher-items" type="aa{sv}" access="readwrite">
267 <annotation name="org.freedesktop.Accounts.DefaultValue" value="[{'defaults': <true>}]"/>
268 </property>
269
270 </interface>
271+
272+ <!-- This interface is for bits of data that the greeter wants to track
273+ per-user in a persistent way, but that users shouldn't be able to edit
274+ in an ideal world.
275+
276+ This interface is identical in permissions to the above one for now,
277+ but once we stop running the greeter in user-space, we should
278+ disallow org.freedesktop.Accounts.Authentication.ChangeOwn from the
279+ Private inteface. -->
280+ <interface name="com.canonical.unity.AccountsService.Private">
281+
282+ <annotation name="org.freedesktop.Accounts.VendorExtension" value="true"/>
283+
284+ <annotation name="org.freedesktop.Accounts.Authentication.ReadAny"
285+ value="com.ubuntu.AccountsService.GreeterReadAny"/>
286+
287+ <annotation name="org.freedesktop.Accounts.Authentication.ChangeAny"
288+ value="com.ubuntu.AccountsService.GreeterChangeAny"/>
289+
290+ <property name="FailedLogins" type="u" access="readwrite">
291+ <annotation name="org.freedesktop.Accounts.DefaultValue" value="0"/>
292+ </property>
293+
294+ </interface>
295 </node>
296
297=== modified file 'plugins/LightDM/Greeter.cpp'
298--- plugins/LightDM/Greeter.cpp 2014-06-18 19:49:22 +0000
299+++ plugins/LightDM/Greeter.cpp 2014-08-13 18:12:02 +0000
300@@ -17,6 +17,7 @@
301 */
302
303 #include "Greeter.h"
304+#include <libintl.h>
305 #include <QLightDM/Greeter>
306
307 class GreeterPrivate
308@@ -125,13 +126,15 @@
309 Q_D(Greeter);
310 d->wasPrompted = true;
311
312+ bool isDefaultPrompt = (text == dgettext("Linux-PAM", "Password: "));
313+
314 // Strip prompt of any colons at the end
315 QString trimmedText = text.trimmed();
316 if (trimmedText.endsWith(":") || trimmedText.endsWith(":")) {
317 trimmedText.chop(1);
318 }
319
320- Q_EMIT showPrompt(trimmedText, type == QLightDM::Greeter::PromptTypeSecret);
321+ Q_EMIT showPrompt(trimmedText, type == QLightDM::Greeter::PromptTypeSecret, isDefaultPrompt);
322 }
323
324 void Greeter::showMessageFilter(const QString &text, QLightDM::Greeter::MessageType type)
325
326=== modified file 'plugins/LightDM/Greeter.h'
327--- plugins/LightDM/Greeter.h 2014-06-27 22:08:19 +0000
328+++ plugins/LightDM/Greeter.h 2014-08-13 18:12:02 +0000
329@@ -55,7 +55,7 @@
330
331 Q_SIGNALS:
332 void showMessage(const QString &text, bool isError);
333- void showPrompt(const QString &text, bool isSecret);
334+ void showPrompt(const QString &text, bool isSecret, bool isDefaultPrompt);
335 void authenticationComplete();
336 void authenticationUserChanged(const QString &user);
337 void isActiveChanged();
338
339=== modified file 'plugins/Ubuntu/CMakeLists.txt'
340--- plugins/Ubuntu/CMakeLists.txt 2014-05-29 14:36:21 +0000
341+++ plugins/Ubuntu/CMakeLists.txt 2014-08-13 18:12:02 +0000
342@@ -1,3 +1,4 @@
343 add_subdirectory(Gestures)
344 add_subdirectory(DownloadDaemonListener)
345 add_subdirectory(Payments)
346+add_subdirectory(SystemImage)
347
348=== added directory 'plugins/Ubuntu/SystemImage'
349=== added file 'plugins/Ubuntu/SystemImage/CMakeLists.txt'
350--- plugins/Ubuntu/SystemImage/CMakeLists.txt 1970-01-01 00:00:00 +0000
351+++ plugins/Ubuntu/SystemImage/CMakeLists.txt 2014-08-13 18:12:02 +0000
352@@ -0,0 +1,10 @@
353+set(SYSTEMIMAGE_SOURCES
354+ plugin.cpp
355+ SystemImage.cpp
356+)
357+
358+add_library(SystemImage MODULE ${SYSTEMIMAGE_SOURCES})
359+
360+qt5_use_modules(SystemImage Qml DBus Core)
361+
362+add_unity8_plugin(Ubuntu.SystemImage 0.1 Ubuntu/SystemImage TARGETS SystemImage)
363
364=== added file 'plugins/Ubuntu/SystemImage/SystemImage.cpp'
365--- plugins/Ubuntu/SystemImage/SystemImage.cpp 1970-01-01 00:00:00 +0000
366+++ plugins/Ubuntu/SystemImage/SystemImage.cpp 2014-08-13 18:12:02 +0000
367@@ -0,0 +1,34 @@
368+/*
369+ * Copyright (C) 2014 Canonical, Ltd.
370+ *
371+ * This program is free software; you can redistribute it and/or modify
372+ * it under the terms of the GNU General Public License as published by
373+ * the Free Software Foundation; version 3.
374+ *
375+ * This program is distributed in the hope that it will be useful,
376+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
377+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
378+ * GNU General Public License for more details.
379+ *
380+ * You should have received a copy of the GNU General Public License
381+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
382+ */
383+
384+#include "SystemImage.h"
385+#include <QDBusConnection>
386+#include <QDBusInterface>
387+
388+SystemImage::SystemImage(QObject *parent)
389+ : QObject(parent),
390+ m_interface(new QDBusInterface("com.canonical.SystemImage",
391+ "/Service",
392+ "com.canonical.SystemImage",
393+ QDBusConnection::systemBus(),
394+ this))
395+{
396+}
397+
398+void SystemImage::factoryReset()
399+{
400+ m_interface->call("FactoryReset");
401+}
402
403=== added file 'plugins/Ubuntu/SystemImage/SystemImage.h'
404--- plugins/Ubuntu/SystemImage/SystemImage.h 1970-01-01 00:00:00 +0000
405+++ plugins/Ubuntu/SystemImage/SystemImage.h 2014-08-13 18:12:02 +0000
406@@ -0,0 +1,38 @@
407+/*
408+ * Copyright (C) 2014 Canonical, Ltd.
409+ *
410+ * This program is free software; you can redistribute it and/or modify
411+ * it under the terms of the GNU General Public License as published by
412+ * the Free Software Foundation; version 3.
413+ *
414+ * This program is distributed in the hope that it will be useful,
415+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
416+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
417+ * GNU General Public License for more details.
418+ *
419+ * You should have received a copy of the GNU General Public License
420+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
421+ */
422+
423+#ifndef SYSTEMIMAGE_H
424+#define SYSTEMIMAGE_H
425+
426+#include <QObject>
427+
428+class QDBusInterface;
429+
430+class SystemImage : public QObject
431+{
432+ Q_OBJECT
433+ Q_DISABLE_COPY(SystemImage)
434+
435+public:
436+ explicit SystemImage(QObject *parent = 0);
437+
438+ Q_INVOKABLE void factoryReset();
439+
440+private:
441+ QDBusInterface *m_interface;
442+};
443+
444+#endif // SYSTEMIMAGE_H
445
446=== added file 'plugins/Ubuntu/SystemImage/SystemImage.qmltypes'
447--- plugins/Ubuntu/SystemImage/SystemImage.qmltypes 1970-01-01 00:00:00 +0000
448+++ plugins/Ubuntu/SystemImage/SystemImage.qmltypes 2014-08-13 18:12:02 +0000
449@@ -0,0 +1,17 @@
450+import QtQuick.tooling 1.1
451+
452+// This file describes the plugin-supplied types contained in the library.
453+// It is used for QML tooling purposes only.
454+//
455+// This file was auto-generated by:
456+// 'qmlplugindump -notrelocatable Ubuntu.SystemImage 0.1 plugins'
457+
458+Module {
459+ Component {
460+ name: "SystemImage"
461+ prototype: "QObject"
462+ exports: ["Ubuntu.SystemImage/SystemImage 0.1"]
463+ exportMetaObjectRevisions: [0]
464+ Method { name: "factoryReset" }
465+ }
466+}
467
468=== added file 'plugins/Ubuntu/SystemImage/plugin.cpp'
469--- plugins/Ubuntu/SystemImage/plugin.cpp 1970-01-01 00:00:00 +0000
470+++ plugins/Ubuntu/SystemImage/plugin.cpp 2014-08-13 18:12:02 +0000
471@@ -0,0 +1,34 @@
472+/*
473+ * Copyright (C) 2014 Canonical, Ltd.
474+ *
475+ * This program is free software; you can redistribute it and/or modify
476+ * it under the terms of the GNU General Public License as published by
477+ * the Free Software Foundation; version 3.
478+ *
479+ * This program is distributed in the hope that it will be useful,
480+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
481+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
482+ * GNU General Public License for more details.
483+ *
484+ * You should have received a copy of the GNU General Public License
485+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
486+ */
487+
488+#include "plugin.h"
489+#include "SystemImage.h"
490+
491+#include <QtQml>
492+
493+static QObject *service_provider(QQmlEngine *engine, QJSEngine *scriptEngine)
494+{
495+ Q_UNUSED(engine)
496+ Q_UNUSED(scriptEngine)
497+ return new SystemImage();
498+}
499+
500+void BackendPlugin::registerTypes(const char *uri)
501+{
502+ Q_ASSERT(uri == QLatin1String("Ubuntu.SystemImage"));
503+
504+ qmlRegisterSingletonType<SystemImage>(uri, 0, 1, "SystemImage", service_provider);
505+}
506
507=== added file 'plugins/Ubuntu/SystemImage/plugin.h'
508--- plugins/Ubuntu/SystemImage/plugin.h 1970-01-01 00:00:00 +0000
509+++ plugins/Ubuntu/SystemImage/plugin.h 2014-08-13 18:12:02 +0000
510@@ -0,0 +1,31 @@
511+/*
512+ * Copyright (C) 2014 Canonical, Ltd.
513+ *
514+ * This program is free software; you can redistribute it and/or modify
515+ * it under the terms of the GNU General Public License as published by
516+ * the Free Software Foundation; version 3.
517+ *
518+ * This program is distributed in the hope that it will be useful,
519+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
520+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
521+ * GNU General Public License for more details.
522+ *
523+ * You should have received a copy of the GNU General Public License
524+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
525+ */
526+
527+#ifndef SYSTEMIMAGE_PLUGIN_H
528+#define SYSTEMIMAGE_PLUGIN_H
529+
530+#include <QQmlExtensionPlugin>
531+
532+class BackendPlugin : public QQmlExtensionPlugin
533+{
534+ Q_OBJECT
535+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
536+
537+public:
538+ void registerTypes(const char *uri);
539+};
540+
541+#endif // SYSTEMIMAGE_PLUGIN_H
542
543=== added file 'plugins/Ubuntu/SystemImage/qmldir'
544--- plugins/Ubuntu/SystemImage/qmldir 1970-01-01 00:00:00 +0000
545+++ plugins/Ubuntu/SystemImage/qmldir 2014-08-13 18:12:02 +0000
546@@ -0,0 +1,3 @@
547+module Ubuntu.SystemImage
548+plugin SystemImage
549+typeinfo SystemImage.qmltypes
550
551=== modified file 'po/unity8.pot'
552--- po/unity8.pot 2014-08-08 09:17:29 +0000
553+++ po/unity8.pot 2014-08-13 18:12:02 +0000
554@@ -8,7 +8,7 @@
555 msgstr ""
556 "Project-Id-Version: unity8\n"
557 "Report-Msgid-Bugs-To: \n"
558-"POT-Creation-Date: 2014-08-08 11:17+0200\n"
559+"POT-Creation-Date: 2014-08-13 14:09-0400\n"
560 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
561 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
562 "Language-Team: LANGUAGE <LL@li.org>\n"
563@@ -17,6 +17,10 @@
564 "Content-Type: text/plain; charset=UTF-8\n"
565 "Content-Transfer-Encoding: 8bit\n"
566
567+#: plugins/LightDM/Greeter.cpp:129
568+msgid "Password: "
569+msgstr ""
570+
571 #: plugins/Unity/Launcher/launcheritem.cpp:43
572 #: plugins/Unity/Launcher/launcheritem.cpp:73
573 msgid "Pin shortcut"
574@@ -88,39 +92,51 @@
575 "phone<br><br>Tap on the screen to start"
576 msgstr ""
577
578-#: qml/Components/Lockscreen.qml:220
579+#: qml/Components/Lockscreen.qml:161
580+msgid "Too many incorrect attempts"
581+msgstr ""
582+
583+#: qml/Components/Lockscreen.qml:163
584+msgid "Please wait"
585+msgstr ""
586+
587+#: qml/Components/Lockscreen.qml:245
588 msgid "Emergency Call"
589 msgstr ""
590
591-#: qml/Components/Lockscreen.qml:243
592+#: qml/Components/Lockscreen.qml:268
593 msgid "OK"
594 msgstr ""
595
596-#: qml/Components/PassphraseLockscreen.qml:62
597+#: qml/Components/PassphraseLockscreen.qml:61
598 #, qt-format
599 msgid "Hello %1"
600 msgstr ""
601
602-#: qml/Components/PassphraseLockscreen.qml:62
603+#: qml/Components/PassphraseLockscreen.qml:61
604 msgid "Hello"
605 msgstr ""
606
607-#: qml/Components/PinLockscreen.qml:198
608+#: qml/Components/PinLockscreen.qml:197
609 msgid "CANCEL"
610 msgstr ""
611
612-#: qml/Components/PinLockscreen.qml:216
613+#: qml/Components/PinLockscreen.qml:215
614 msgid "DONE"
615 msgstr ""
616
617-#: qml/Dash/GenericScopeView.qml:358
618+#: qml/Dash/GenericScopeView.qml:360
619 msgid "See less"
620 msgstr ""
621
622-#: qml/Dash/GenericScopeView.qml:358
623+#: qml/Dash/GenericScopeView.qml:360
624 msgid "See all"
625 msgstr ""
626
627+#: qml/Dash/GenericScopeView.qml:423 qml/Panel/SearchIndicator.qml:27
628+msgid "Search"
629+msgstr ""
630+
631 #: qml/Dash/Previews/PreviewActionCombo.qml:34
632 msgid "More..."
633 msgstr ""
634@@ -141,15 +157,15 @@
635 msgid "Send"
636 msgstr ""
637
638-#: qml/Dash/ScopesOverview.qml:200
639+#: qml/Dash/ScopesOverview.qml:201
640 msgid "Manage Dash"
641 msgstr ""
642
643-#: qml/Dash/ScopesOverview.qml:405
644+#: qml/Dash/ScopesOverview.qml:408
645 msgid "Done"
646 msgstr ""
647
648-#: qml/Dash/ScopesOverview.qml:431
649+#: qml/Dash/ScopesOverview.qml:434
650 msgid "Store"
651 msgstr ""
652
653@@ -233,11 +249,48 @@
654 msgid "Roaming"
655 msgstr ""
656
657-#: qml/Panel/SearchIndicator.qml:27
658-msgid "Search"
659-msgstr ""
660-
661-#: qml/Shell.qml:256
662-#, qt-format
663-msgid "Please enter %1"
664+#: qml/Shell.qml:267
665+msgid "passphrase"
666+msgstr ""
667+
668+#: qml/Shell.qml:267
669+msgid "passcode"
670+msgstr ""
671+
672+#: qml/Shell.qml:269
673+#, qt-format
674+msgid "Enter your %1"
675+msgstr ""
676+
677+#: qml/Shell.qml:270
678+#, qt-format
679+msgid "Incorrect %1"
680+msgstr ""
681+
682+#: qml/Shell.qml:272
683+msgid "Please re-enter"
684+msgstr ""
685+
686+#: qml/Shell.qml:306
687+msgid "Sorry, incorrect passphrase."
688+msgstr ""
689+
690+#: qml/Shell.qml:307
691+msgid "Sorry, incorrect passcode."
692+msgstr ""
693+
694+#: qml/Shell.qml:308
695+msgid "This will be your last attempt."
696+msgstr ""
697+
698+#: qml/Shell.qml:310
699+msgid ""
700+"If passphrase is entered incorrectly, your phone will conduct a factory "
701+"reset and all personal data will be deleted."
702+msgstr ""
703+
704+#: qml/Shell.qml:311
705+msgid ""
706+"If passcode is entered incorrectly, your phone will conduct a factory reset "
707+"and all personal data will be deleted."
708 msgstr ""
709
710=== modified file 'qml/Components/Lockscreen.qml'
711--- qml/Components/Lockscreen.qml 2014-07-22 14:46:01 +0000
712+++ qml/Components/Lockscreen.qml 2014-08-13 18:12:02 +0000
713@@ -57,10 +57,15 @@
714
715 onRequiredChanged: {
716 if (required && pinPadLoader.item) {
717- pinPadLoader.item.clear(false);
718+ clear(false)
719 }
720 }
721
722+ function forceDelay(delay) {
723+ forcedDelayTimer.interval = delay
724+ forcedDelayTimer.start()
725+ }
726+
727 function reset() {
728 // This causes the loader below to destry and recreate the source
729 pinPadLoader.resetting = true;
730@@ -68,13 +73,22 @@
731 }
732
733 function clear(showAnimation) {
734- pinPadLoader.item.clear(showAnimation);
735+ if (pinPadLoader.item) {
736+ pinPadLoader.item.clear(showAnimation);
737+ }
738+ pinPadLoader.showWrongText = showAnimation
739+ pinPadLoader.waiting = false
740 }
741
742 function showInfoPopup(title, text) {
743 PopupUtils.open(infoPopupComponent, root, {title: title, text: text})
744 }
745
746+ Timer {
747+ id: forcedDelayTimer
748+ onTriggered: pinPadLoader.showWrongText = false
749+ }
750+
751 Rectangle {
752 // In case background fails to load or is undefined
753 id: backgroundBackup
754@@ -131,8 +145,6 @@
755 }
756 }
757
758-
759-
760 Loader {
761 id: pinPadLoader
762 objectName: "pinPadLoader"
763@@ -143,13 +155,24 @@
764 verticalCenterOffset: root.alphaNumeric ? -units.gu(10) : -units.gu(4)
765 }
766 property bool resetting: false
767+ property bool waiting: false
768+ property bool showWrongText: false
769+
770+ readonly property string forcedDelayText: i18n.tr("Too many incorrect attempts") +
771+ "\n" +
772+ i18n.tr("Please wait")
773
774 source: (!resetting && root.required) ? (root.alphaNumeric ? "PassphraseLockscreen.qml" : "PinLockscreen.qml") : ""
775+ onSourceChanged: {
776+ waiting = false
777+ showWrongText = false
778+ }
779
780 Connections {
781 target: pinPadLoader.item
782
783 onEntered: {
784+ pinPadLoader.waiting = true
785 root.entered(passphrase);
786 }
787
788@@ -171,18 +194,20 @@
789 Binding {
790 target: pinPadLoader.item
791 property: "placeholderText"
792- value: root.placeholderText
793- }
794- Binding {
795- target: pinPadLoader.item
796- property: "wrongPlaceholderText"
797- value: root.wrongPlaceholderText
798+ value: forcedDelayTimer.running ? pinPadLoader.forcedDelayText :
799+ (pinPadLoader.showWrongText ? root.wrongPlaceholderText :
800+ root.placeholderText)
801 }
802 Binding {
803 target: pinPadLoader.item
804 property: "username"
805 value: root.username
806 }
807+ Binding {
808+ target: pinPadLoader.item
809+ property: "entryEnabled"
810+ value: !pinPadLoader.waiting && !forcedDelayTimer.running
811+ }
812 }
813
814 Column {
815
816=== modified file 'qml/Components/PassphraseLockscreen.qml'
817--- qml/Components/PassphraseLockscreen.qml 2014-07-02 18:40:30 +0000
818+++ qml/Components/PassphraseLockscreen.qml 2014-08-13 18:12:02 +0000
819@@ -23,15 +23,14 @@
820 height: highlightItem.height
821
822 property string placeholderText
823- property string wrongPlaceholderText
824 property string username: ""
825+ property bool entryEnabled: true
826
827 signal entered(string passphrase)
828 signal cancel()
829
830 function clear(playAnimation) {
831 pinentryField.text = "";
832- pinentryField.enabled = true
833 if (playAnimation) {
834 wrongPasswordAnimation.start();
835 pinentryField.forceActiveFocus();
836@@ -76,11 +75,11 @@
837 echoMode: TextInput.Password
838 opacity: 0.9
839 hasClearButton: false
840- placeholderText: wrongPasswordAnimation.running ? root.wrongPlaceholderText : root.placeholderText
841+ enabled: entryEnabled
842+ placeholderText: root.placeholderText
843
844 onAccepted: {
845 if (pinentryField.text) {
846- pinentryField.enabled = false;
847 root.entered(pinentryField.text);
848 }
849 }
850
851=== modified file 'qml/Components/PinLockscreen.qml'
852--- qml/Components/PinLockscreen.qml 2014-06-06 11:37:55 +0000
853+++ qml/Components/PinLockscreen.qml 2014-08-13 18:12:02 +0000
854@@ -25,7 +25,6 @@
855 spacing: units.gu(3.5)
856
857 property alias placeholderText: pinentryField.placeholderText
858- property alias wrongPlaceholderText: pinentryField.wrongPlaceholderText
859 property int padWidth: units.gu(34)
860 property int padHeight: units.gu(28)
861 property int minPinLength: -1
862@@ -58,7 +57,6 @@
863 radius: "medium"
864 property string text: ""
865 property string placeholderText: ""
866- property string wrongPlaceholderText: ""
867
868 function appendChar(character) {
869 if (root.maxPinLength == -1 || pinentryField.text.length < root.maxPinLength) {
870@@ -90,9 +88,10 @@
871 id: pinentryFieldPlaceHolder
872 objectName: "pinentryFieldPlaceHolder"
873 anchors.centerIn: parent
874+ horizontalAlignment: Text.AlignHCenter
875 color: "#f3f3e7"
876 opacity: 0.6
877- text: wrongPasswordAnimation.running ? parent.wrongPlaceholderText : parent.placeholderText
878+ text: parent.placeholderText
879 visible: pinentryFieldLabel.text.length == 0
880 }
881
882@@ -107,7 +106,7 @@
883 bottom: parent.bottom
884 bottomMargin: units.gu(1)
885 }
886- visible: !priv.autoConfirm
887+ visible: entryEnabled && !priv.autoConfirm
888 width: height
889 name: "erase"
890 color: "#f3f3e7"
891
892=== modified file 'qml/Notifications/NotificationMenuItemFactory.qml'
893--- qml/Notifications/NotificationMenuItemFactory.qml 2014-07-22 12:13:02 +0000
894+++ qml/Notifications/NotificationMenuItemFactory.qml 2014-08-13 18:12:02 +0000
895@@ -117,7 +117,6 @@
896
897 onEntered: {
898 menuModel.changeState(menuIndex, passphrase);
899- entryEnabled = false;
900 }
901
902 onCancel: {
903
904=== modified file 'qml/Shell.qml'
905--- qml/Shell.qml 2014-08-11 19:02:49 +0000
906+++ qml/Shell.qml 2014-08-13 18:12:02 +0000
907@@ -19,7 +19,9 @@
908 import GSettings 1.0
909 import Unity.Application 0.1
910 import Ubuntu.Components 0.1
911+import Ubuntu.Components.Popups 1.0
912 import Ubuntu.Gestures 0.1
913+import Ubuntu.SystemImage 0.1
914 import Unity.Launcher 0.1
915 import Utils 0.1
916 import LightDM 0.1 as LightDM
917@@ -53,6 +55,10 @@
918 property bool sideStageEnabled: shell.width >= units.gu(100)
919 readonly property string focusedApplicationId: ApplicationManager.focusedApplicationId
920
921+ property int maxFailedLogins: -1 // disabled by default for now, will enable via settings in future
922+ property int failedLoginsDelayAttempts: 7 // number of failed logins
923+ property int failedLoginsDelaySeconds: 5 * 60 // seconds of forced waiting
924+
925 function activateApplication(appId) {
926 if (ApplicationManager.findApplication(appId)) {
927 ApplicationManager.requestFocusApplication(appId);
928@@ -255,13 +261,21 @@
929
930 onShowPrompt: {
931 if (greeter.narrowMode) {
932- lockscreen.placeholderText = i18n.tr("Please enter %1").arg(text.toLowerCase());
933+ var promptText = text.toLowerCase()
934+ if (isDefaultPrompt) {
935+ promptText = lockscreen.alphaNumeric ?
936+ i18n.tr("passphrase") : i18n.tr("passcode")
937+ }
938+ lockscreen.placeholderText = i18n.tr("Enter your %1").arg(promptText)
939+ lockscreen.wrongPlaceholderText = i18n.tr("Incorrect %1").arg(promptText) +
940+ "\n" +
941+ i18n.tr("Please re-enter")
942 lockscreen.show();
943 }
944 }
945
946 onPromptlessChanged: {
947- if (LightDM.Greeter.promptless) {
948+ if (LightDM.Greeter.promptless && LightDM.Greeter.authenticated) {
949 lockscreen.hide()
950 } else {
951 lockscreen.reset();
952@@ -270,13 +284,40 @@
953 }
954
955 onAuthenticationComplete: {
956+ if (LightDM.Greeter.authenticated) {
957+ AccountsService.failedLogins = 0
958+ }
959+ // Else only penalize user for a failed login if they actually were
960+ // prompted for a password. We do this below after the promptless
961+ // early exit.
962+
963 if (LightDM.Greeter.promptless) {
964 return;
965 }
966+
967 if (LightDM.Greeter.authenticated) {
968 lockscreen.hide();
969 greeter.login();
970 } else {
971+ AccountsService.failedLogins++
972+ if (maxFailedLogins >= 2) { // require at least a warning
973+ if (AccountsService.failedLogins === maxFailedLogins - 1) {
974+ var title = lockscreen.alphaNumeric ?
975+ i18n.tr("Sorry, incorrect passphrase.") :
976+ i18n.tr("Sorry, incorrect passcode.")
977+ var text = i18n.tr("This will be your last attempt.") + " " +
978+ (lockscreen.alphaNumeric ?
979+ i18n.tr("If passphrase is entered incorrectly, your phone will conduct a factory reset and all personal data will be deleted.") :
980+ i18n.tr("If passcode is entered incorrectly, your phone will conduct a factory reset and all personal data will be deleted."))
981+ lockscreen.showInfoPopup(title, text)
982+ } else if (AccountsService.failedLogins >= maxFailedLogins) {
983+ SystemImage.factoryReset() // Ouch!
984+ }
985+ }
986+ if (failedLoginsDelayAttempts > 0 && AccountsService.failedLogins % failedLoginsDelayAttempts == 0) {
987+ lockscreen.forceDelay(failedLoginsDelaySeconds * 1000)
988+ }
989+
990 lockscreen.clear(true);
991 if (greeter.narrowMode) {
992 LightDM.Greeter.authenticate(LightDM.Users.data(0, LightDM.UserRoles.NameRole))
993@@ -311,8 +352,16 @@
994 }
995
996 property bool fullyShown: showProgress === 1.0
997+ onFullyShownChanged: {
998+ // Wait until the greeter is completely covering lockscreen before resetting it.
999+ if (fullyShown && !LightDM.Greeter.authenticated) {
1000+ lockscreen.reset();
1001+ lockscreen.show();
1002+ }
1003+ }
1004+
1005 readonly property real showProgress: MathUtils.clamp((1 - x/width) + greeter.showProgress - 1, 0, 1)
1006- onShowProgressChanged: if (LightDM.Greeter.promptless && showProgress === 0) greeter.login()
1007+ onShowProgressChanged: if (LightDM.Greeter.authenticated && showProgress === 0) greeter.login()
1008
1009 Greeter {
1010 id: greeter
1011@@ -350,10 +399,6 @@
1012 if (greeter.narrowMode) {
1013 LightDM.Greeter.authenticate(LightDM.Users.data(0, LightDM.UserRoles.NameRole));
1014 }
1015- if (!LightDM.Greeter.promptless) {
1016- lockscreen.reset();
1017- lockscreen.show();
1018- }
1019 greeter.fakeActiveForApp = "";
1020 greeter.forceActiveFocus();
1021 }
1022@@ -403,7 +448,7 @@
1023 }
1024
1025 if (LightDM.Greeter.active) {
1026- if (!LightDM.Greeter.promptless) {
1027+ if (!LightDM.Greeter.authenticated) {
1028 lockscreen.show()
1029 }
1030 greeter.hide()
1031
1032=== modified file 'run.sh'
1033--- run.sh 2014-07-16 14:37:21 +0000
1034+++ run.sh 2014-08-13 18:12:02 +0000
1035@@ -18,6 +18,7 @@
1036 echo " -f, --fake Force use of fake Qml modules." >&2
1037 echo " -p, --pinlock Use a pin protected user." >&2
1038 echo " -k, --keylock Use a passphrase protected user." >&2
1039+ echo " -l, --lightdm Use the specified lightdm mock." >&2
1040 echo " -g, --gdb Run through gdb." >&2
1041 echo " -h, --help Show this help." >&2
1042 echo " -m, --nomousetouch Run without -mousetouch argument." >&2
1043@@ -25,16 +26,20 @@
1044 exit 1
1045 }
1046
1047-ARGS=`getopt -n$0 -u -a --longoptions="fake,pinlock,keylock,gdb,help,nomousetouch" -o "fpkghm" -- "$@"`
1048+ARGS=`getopt -n$0 -u -a --longoptions="fake,pinlock,keylock,gdb,help,lightdm:,nomousetouch" -o "fpkl:ghm" -- "$@"`
1049 [ $? -ne 0 ] && usage
1050 eval set -- "$ARGS"
1051
1052 while [ $# -gt 0 ]
1053 do
1054 case "$1" in
1055- -f|--fake) FAKE=true; USE_MOCKS=true;;
1056- -p|--pinlock) PINLOCK=true; USE_MOCKS=true;;
1057- -k|--keylock) KEYLOCK=true; USE_MOCKS=true;;
1058+ -f|--fake) USE_MOCKS=true;;
1059+ -p|--pinlock) USE_MOCKS=true; LIGHTDM_MOCK=single-pin;;
1060+ -k|--keylock) USE_MOCKS=true; LIGHTDM_MOCK=single-passphrase;;
1061+ -l|--lightdm) LIGHTDM_MOCK=$2; shift;
1062+ if [ -z "$LIGHTDM_MOCK" ]; then
1063+ echo "Please specify an argument to --lightdm"
1064+ fi;;
1065 -g|--gdb) GDB=true;;
1066 -h|--help) usage;;
1067 -m|--nomousetouch) MOUSE_TOUCH=false;;
1068@@ -43,27 +48,23 @@
1069 shift
1070 done
1071
1072-if $FAKE; then
1073- export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$PWD/builddir/tests/mocks/libusermetrics:$PWD/builddir/tests/mocks/LightDM/single
1074-fi
1075-
1076-if $PINLOCK; then
1077- export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$PWD/builddir/tests/mocks/libusermetrics:$PWD/builddir/tests/mocks/LightDM/single-pin
1078-fi
1079-
1080-if $KEYLOCK; then
1081- export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$PWD/builddir/tests/mocks/libusermetrics:$PWD/builddir/tests/mocks/LightDM/single-passphrase
1082-fi
1083+if [ -z "$LIGHTDM_MOCK" ]; then
1084+ LIGHTDM_MOCK=single
1085+fi
1086+
1087+# Even without USE_MOCKS set, we want to fake our lightdm backend, because it's
1088+# annoying to be prompted for your password when testing. To get the same
1089+# backend used in production, pass '--lightdm=demo'.
1090+export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$PWD/builddir/tests/mocks/LightDM/$LIGHTDM_MOCK
1091
1092 if $USE_MOCKS; then
1093 rm -f $PWD/builddir/nonmirplugins/LightDM # undo symlink (from below) for cleanliness
1094 export QML2_IMPORT_PATH=$QML2_IMPORT_PATH:$PWD/builddir/tests/mocks:$PWD/builddir/plugins:$PWD/builddir/modules
1095+ export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$PWD/builddir/tests/mocks/libusermetrics
1096 else
1097- # Still fake no-login user for convenience (it's annoying to be prompted for your password when testing)
1098- # And in particular, just link our LightDM mock into the nonmirplugins folder. We don't want the rest of
1099- # our plugins to be used.
1100- ln -s $PWD/builddir/tests/mocks/LightDM $PWD/builddir/nonmirplugins/
1101- export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$PWD/builddir/tests/mocks/LightDM/single
1102+ # Just link our LightDM mock into the nonmirplugins folder. We don't want
1103+ # the rest of our plugins to be used.
1104+ ln -sf $PWD/builddir/tests/mocks/LightDM $PWD/builddir/nonmirplugins/
1105 fi
1106
1107 QML_PHONE_SHELL_ARGS=""
1108
1109=== modified file 'tests/mocks/AccountsService/AccountsService.cpp'
1110--- tests/mocks/AccountsService/AccountsService.cpp 2014-07-02 16:17:14 +0000
1111+++ tests/mocks/AccountsService/AccountsService.cpp 2014-08-13 18:12:02 +0000
1112@@ -23,7 +23,8 @@
1113 AccountsService::AccountsService(QObject* parent)
1114 : QObject(parent),
1115 m_backgroundFile(qmlDirectory() + "graphics/phone_background.jpg"),
1116- m_statsWelcomeScreen(true)
1117+ m_statsWelcomeScreen(true),
1118+ m_failedLogins(0)
1119 {
1120 }
1121
1122@@ -78,3 +79,14 @@
1123 else
1124 return PasswordDisplayHint::Keyboard;
1125 }
1126+
1127+uint AccountsService::failedLogins() const
1128+{
1129+ return m_failedLogins;
1130+}
1131+
1132+void AccountsService::setFailedLogins(uint failedLogins)
1133+{
1134+ m_failedLogins = failedLogins;
1135+ failedLoginsChanged();
1136+}
1137
1138=== modified file 'tests/mocks/AccountsService/AccountsService.h'
1139--- tests/mocks/AccountsService/AccountsService.h 2014-07-02 16:17:14 +0000
1140+++ tests/mocks/AccountsService/AccountsService.h 2014-08-13 18:12:02 +0000
1141@@ -47,6 +47,10 @@
1142 Q_PROPERTY (PasswordDisplayHint passwordDisplayHint
1143 READ passwordDisplayHint
1144 NOTIFY passwordDisplayHintChanged)
1145+ Q_PROPERTY (uint failedLogins
1146+ READ failedLogins
1147+ WRITE setFailedLogins
1148+ NOTIFY failedLoginsChanged)
1149
1150 public:
1151 enum PasswordDisplayHint {
1152@@ -65,6 +69,8 @@
1153 bool statsWelcomeScreen() const;
1154 void setStatsWelcomeScreen(bool statsWelcomeScreen);
1155 PasswordDisplayHint passwordDisplayHint() const;
1156+ uint failedLogins() const;
1157+ void setFailedLogins(uint failedLogins);
1158
1159 Q_SIGNALS:
1160 void userChanged();
1161@@ -72,11 +78,13 @@
1162 void backgroundFileChanged();
1163 void statsWelcomeScreenChanged();
1164 void passwordDisplayHintChanged();
1165+ void failedLoginsChanged();
1166
1167 private:
1168 QString m_backgroundFile;
1169 QString m_user;
1170 bool m_statsWelcomeScreen;
1171+ uint m_failedLogins;
1172 };
1173
1174 #endif
1175
1176=== modified file 'tests/mocks/LightDM/Greeter.cpp'
1177--- tests/mocks/LightDM/Greeter.cpp 2014-06-11 15:36:51 +0000
1178+++ tests/mocks/LightDM/Greeter.cpp 2014-08-13 18:12:02 +0000
1179@@ -19,6 +19,7 @@
1180 #include "Greeter.h"
1181 #include "GreeterPrivate.h"
1182 #include <QtCore/QCoreApplication>
1183+#include <QTimer>
1184
1185 namespace QLightDM
1186 {
1187@@ -164,4 +165,14 @@
1188 d->handleRespond(response);
1189 }
1190
1191+void Greeter::sendAuthenticationComplete()
1192+{
1193+ if (qgetenv("UNITY_TESTING").isEmpty()) {
1194+ // simulate PAM's delay
1195+ QTimer::singleShot(1000, this, SIGNAL(authenticationComplete()));
1196+ } else {
1197+ Q_EMIT authenticationComplete();
1198+ }
1199+}
1200+
1201 }
1202
1203=== modified file 'tests/mocks/LightDM/Greeter.h'
1204--- tests/mocks/LightDM/Greeter.h 2014-06-11 15:36:51 +0000
1205+++ tests/mocks/LightDM/Greeter.h 2014-08-13 18:12:02 +0000
1206@@ -96,6 +96,9 @@
1207 void authenticationComplete();
1208 void autologinTimerExpired();
1209
1210+protected:
1211+ void sendAuthenticationComplete();
1212+
1213 private:
1214 GreeterPrivate *d_ptr;
1215 Q_DECLARE_PRIVATE(Greeter)
1216
1217=== modified file 'tests/mocks/LightDM/demo/CMakeLists.txt'
1218--- tests/mocks/LightDM/demo/CMakeLists.txt 2014-07-01 20:21:23 +0000
1219+++ tests/mocks/LightDM/demo/CMakeLists.txt 2014-08-13 18:12:02 +0000
1220@@ -9,6 +9,7 @@
1221 )
1222
1223 add_library(MockLightDM-demo STATIC ${LibLightDM_SOURCES})
1224+add_library(MockLightDM-demo-shared SHARED ${LibLightDM_SOURCES})
1225
1226 include_directories(
1227 ${CMAKE_CURRENT_BINARY_DIR}
1228@@ -19,5 +20,17 @@
1229 ${LIBUSERMETRICSOUTPUT_LDFLAGS}
1230 -lpam
1231 )
1232+target_link_libraries(MockLightDM-demo-shared
1233+ ${LIBUSERMETRICSOUTPUT_LDFLAGS}
1234+ -lpam
1235+)
1236
1237 qt5_use_modules(MockLightDM-demo Concurrent Gui)
1238+qt5_use_modules(MockLightDM-demo-shared Concurrent Gui)
1239+
1240+set_target_properties(MockLightDM-demo-shared PROPERTIES
1241+ OUTPUT_NAME lightdm-qt5-2)
1242+
1243+install(TARGETS MockLightDM-demo-shared
1244+ DESTINATION ${SHELL_INSTALL_QML}/mocks/LightDM/demo
1245+ )
1246
1247=== modified file 'tests/mocks/LightDM/full/GreeterPrivate.cpp'
1248--- tests/mocks/LightDM/full/GreeterPrivate.cpp 2014-07-02 16:17:14 +0000
1249+++ tests/mocks/LightDM/full/GreeterPrivate.cpp 2014-08-13 18:12:02 +0000
1250@@ -54,7 +54,7 @@
1251 authenticated = true;
1252 Q_EMIT q->authenticationComplete();
1253 } else if (authenticationUser == "has-pin"){
1254- Q_EMIT q->showPrompt("Password:", Greeter::PromptTypeSecret);
1255+ Q_EMIT q->showPrompt("Password: ", Greeter::PromptTypeSecret);
1256 } else if (authenticationUser == "auth-error") {
1257 authenticated = false;
1258 Q_EMIT q->authenticationComplete();
1259@@ -78,11 +78,11 @@
1260 Q_EMIT q->showPrompt("otp", Greeter::PromptTypeQuestion);
1261 } else {
1262 authenticated = false;
1263- Q_EMIT q->authenticationComplete();
1264+ q->sendAuthenticationComplete();
1265 }
1266 } else {
1267 authenticated = (response == "otp");
1268- Q_EMIT q->authenticationComplete();
1269+ q->sendAuthenticationComplete();
1270 }
1271 return;
1272 }
1273@@ -92,7 +92,7 @@
1274 } else {
1275 authenticated = (response == "password");
1276 }
1277- Q_EMIT q->authenticationComplete();
1278+ q->sendAuthenticationComplete();
1279 }
1280
1281 }
1282
1283=== modified file 'tests/mocks/LightDM/single-passphrase/GreeterPrivate.cpp'
1284--- tests/mocks/LightDM/single-passphrase/GreeterPrivate.cpp 2013-06-06 12:18:34 +0000
1285+++ tests/mocks/LightDM/single-passphrase/GreeterPrivate.cpp 2014-08-13 18:12:02 +0000
1286@@ -19,8 +19,6 @@
1287 #include "../Greeter.h"
1288 #include "../GreeterPrivate.h"
1289
1290-#include <QDebug>
1291-
1292 namespace QLightDM
1293 {
1294
1295@@ -34,20 +32,15 @@
1296 void GreeterPrivate::handleAuthenticate()
1297 {
1298 Q_Q(Greeter);
1299-
1300- qDebug() << "handleAuthentication called!" << authenticationUser;
1301-
1302- Q_EMIT q->showPrompt("Password:", Greeter::PromptTypeSecret);
1303+ Q_EMIT q->showPrompt("Password: ", Greeter::PromptTypeSecret);
1304 }
1305
1306 void GreeterPrivate::handleRespond(const QString &response)
1307 {
1308 Q_Q(Greeter);
1309
1310-
1311 authenticated = (response == "password");
1312- qDebug() << "responding" << response << authenticated;
1313- Q_EMIT q->authenticationComplete();
1314+ q->sendAuthenticationComplete();
1315 }
1316
1317 }
1318
1319=== modified file 'tests/mocks/LightDM/single-pin/GreeterPrivate.cpp'
1320--- tests/mocks/LightDM/single-pin/GreeterPrivate.cpp 2014-07-02 16:17:14 +0000
1321+++ tests/mocks/LightDM/single-pin/GreeterPrivate.cpp 2014-08-13 18:12:02 +0000
1322@@ -32,14 +32,15 @@
1323 void GreeterPrivate::handleAuthenticate()
1324 {
1325 Q_Q(Greeter);
1326- Q_EMIT q->showPrompt("Password:", Greeter::PromptTypeSecret);
1327+ Q_EMIT q->showPrompt("Password: ", Greeter::PromptTypeSecret);
1328 }
1329
1330 void GreeterPrivate::handleRespond(const QString &response)
1331 {
1332 Q_Q(Greeter);
1333+
1334 authenticated = (response == "1234");
1335- Q_EMIT q->authenticationComplete();
1336+ q->sendAuthenticationComplete();
1337 }
1338
1339 }
1340
1341=== modified file 'tests/mocks/Ubuntu/CMakeLists.txt'
1342--- tests/mocks/Ubuntu/CMakeLists.txt 2014-07-07 08:51:33 +0000
1343+++ tests/mocks/Ubuntu/CMakeLists.txt 2014-08-13 18:12:02 +0000
1344@@ -1,4 +1,5 @@
1345 add_subdirectory(DownloadDaemonListener)
1346 add_subdirectory(Payments)
1347+add_subdirectory(SystemImage)
1348 add_subdirectory(Telephony)
1349 add_subdirectory(Thumbnailer)
1350
1351=== added directory 'tests/mocks/Ubuntu/SystemImage'
1352=== added file 'tests/mocks/Ubuntu/SystemImage/CMakeLists.txt'
1353--- tests/mocks/Ubuntu/SystemImage/CMakeLists.txt 1970-01-01 00:00:00 +0000
1354+++ tests/mocks/Ubuntu/SystemImage/CMakeLists.txt 2014-08-13 18:12:02 +0000
1355@@ -0,0 +1,10 @@
1356+set(MOCK_SYSTEMIMAGE_SOURCES
1357+ plugin.cpp
1358+ MockSystemImage.cpp
1359+)
1360+
1361+add_library(MockSystemImage MODULE ${MOCK_SYSTEMIMAGE_SOURCES})
1362+
1363+qt5_use_modules(MockSystemImage Qml Quick Core)
1364+
1365+add_unity8_mock(Ubuntu.SystemImage 0.1 Ubuntu/SystemImage TARGETS MockSystemImage)
1366
1367=== added file 'tests/mocks/Ubuntu/SystemImage/MockSystemImage.cpp'
1368--- tests/mocks/Ubuntu/SystemImage/MockSystemImage.cpp 1970-01-01 00:00:00 +0000
1369+++ tests/mocks/Ubuntu/SystemImage/MockSystemImage.cpp 2014-08-13 18:12:02 +0000
1370@@ -0,0 +1,27 @@
1371+/*
1372+ * Copyright (C) 2014 Canonical, Ltd.
1373+ *
1374+ * This program is free software; you can redistribute it and/or modify
1375+ * it under the terms of the GNU General Public License as published by
1376+ * the Free Software Foundation; version 3.
1377+ *
1378+ * This program is distributed in the hope that it will be useful,
1379+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1380+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1381+ * GNU General Public License for more details.
1382+ *
1383+ * You should have received a copy of the GNU General Public License
1384+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1385+ */
1386+
1387+#include "MockSystemImage.h"
1388+
1389+MockSystemImage::MockSystemImage(QObject *parent)
1390+ : QObject(parent)
1391+{
1392+}
1393+
1394+void MockSystemImage::factoryReset()
1395+{
1396+ Q_EMIT resettingDevice();
1397+}
1398
1399=== added file 'tests/mocks/Ubuntu/SystemImage/MockSystemImage.h'
1400--- tests/mocks/Ubuntu/SystemImage/MockSystemImage.h 1970-01-01 00:00:00 +0000
1401+++ tests/mocks/Ubuntu/SystemImage/MockSystemImage.h 2014-08-13 18:12:02 +0000
1402@@ -0,0 +1,36 @@
1403+/*
1404+ * Copyright (C) 2014 Canonical, Ltd.
1405+ *
1406+ * This program is free software; you can redistribute it and/or modify
1407+ * it under the terms of the GNU General Public License as published by
1408+ * the Free Software Foundation; version 3.
1409+ *
1410+ * This program is distributed in the hope that it will be useful,
1411+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1412+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1413+ * GNU General Public License for more details.
1414+ *
1415+ * You should have received a copy of the GNU General Public License
1416+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1417+ */
1418+
1419+#ifndef MOCK_SYSTEMIMAGE_H
1420+#define MOCK_SYSTEMIMAGE_H
1421+
1422+#include <QObject>
1423+
1424+class MockSystemImage : public QObject
1425+{
1426+ Q_OBJECT
1427+ Q_DISABLE_COPY(MockSystemImage)
1428+
1429+public:
1430+ explicit MockSystemImage(QObject *parent = 0);
1431+
1432+ Q_INVOKABLE void factoryReset();
1433+
1434+Q_SIGNALS:
1435+ void resettingDevice(); // only for mock
1436+};
1437+
1438+#endif // MOCK_SYSTEMIMAGE_H
1439
1440=== added file 'tests/mocks/Ubuntu/SystemImage/SystemImage.qmltypes'
1441--- tests/mocks/Ubuntu/SystemImage/SystemImage.qmltypes 1970-01-01 00:00:00 +0000
1442+++ tests/mocks/Ubuntu/SystemImage/SystemImage.qmltypes 2014-08-13 18:12:02 +0000
1443@@ -0,0 +1,18 @@
1444+import QtQuick.tooling 1.1
1445+
1446+// This file describes the plugin-supplied types contained in the library.
1447+// It is used for QML tooling purposes only.
1448+//
1449+// This file was auto-generated by:
1450+// 'qmlplugindump -notrelocatable Ubuntu.SystemImage 0.1 plugins'
1451+
1452+Module {
1453+ Component {
1454+ name: "MockSystemImage"
1455+ prototype: "QObject"
1456+ exports: ["Ubuntu.SystemImage/SystemImage 0.1"]
1457+ exportMetaObjectRevisions: [0]
1458+ Signal { name: "resettingDevice" }
1459+ Method { name: "factoryReset" }
1460+ }
1461+}
1462
1463=== added file 'tests/mocks/Ubuntu/SystemImage/plugin.cpp'
1464--- tests/mocks/Ubuntu/SystemImage/plugin.cpp 1970-01-01 00:00:00 +0000
1465+++ tests/mocks/Ubuntu/SystemImage/plugin.cpp 2014-08-13 18:12:02 +0000
1466@@ -0,0 +1,34 @@
1467+/*
1468+ * Copyright (C) 2014 Canonical, Ltd.
1469+ *
1470+ * This program is free software; you can redistribute it and/or modify
1471+ * it under the terms of the GNU General Public License as published by
1472+ * the Free Software Foundation; version 3.
1473+ *
1474+ * This program is distributed in the hope that it will be useful,
1475+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1476+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1477+ * GNU General Public License for more details.
1478+ *
1479+ * You should have received a copy of the GNU General Public License
1480+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1481+ */
1482+
1483+#include "plugin.h"
1484+#include "MockSystemImage.h"
1485+
1486+#include <QtQml>
1487+
1488+static QObject *service_provider(QQmlEngine *engine, QJSEngine *scriptEngine)
1489+{
1490+ Q_UNUSED(engine)
1491+ Q_UNUSED(scriptEngine)
1492+ return new MockSystemImage();
1493+}
1494+
1495+void BackendPlugin::registerTypes(const char *uri)
1496+{
1497+ Q_ASSERT(uri == QLatin1String("Ubuntu.SystemImage"));
1498+
1499+ qmlRegisterSingletonType<MockSystemImage>(uri, 0, 1, "SystemImage", service_provider);
1500+}
1501
1502=== added file 'tests/mocks/Ubuntu/SystemImage/plugin.h'
1503--- tests/mocks/Ubuntu/SystemImage/plugin.h 1970-01-01 00:00:00 +0000
1504+++ tests/mocks/Ubuntu/SystemImage/plugin.h 2014-08-13 18:12:02 +0000
1505@@ -0,0 +1,31 @@
1506+/*
1507+ * Copyright (C) 2014 Canonical, Ltd.
1508+ *
1509+ * This program is free software; you can redistribute it and/or modify
1510+ * it under the terms of the GNU General Public License as published by
1511+ * the Free Software Foundation; version 3.
1512+ *
1513+ * This program is distributed in the hope that it will be useful,
1514+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1515+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1516+ * GNU General Public License for more details.
1517+ *
1518+ * You should have received a copy of the GNU General Public License
1519+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1520+ */
1521+
1522+#ifndef MOCK_SYSTEMIMAGE_PLUGIN_H
1523+#define MOCK_SYSTEMIMAGE_PLUGIN_H
1524+
1525+#include <QQmlExtensionPlugin>
1526+
1527+class BackendPlugin : public QQmlExtensionPlugin
1528+{
1529+ Q_OBJECT
1530+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QQmlExtensionInterface")
1531+
1532+public:
1533+ void registerTypes(const char *uri);
1534+};
1535+
1536+#endif // MOCK_SYSTEMIMAGE_PLUGIN_H
1537
1538=== added file 'tests/mocks/Ubuntu/SystemImage/qmldir'
1539--- tests/mocks/Ubuntu/SystemImage/qmldir 1970-01-01 00:00:00 +0000
1540+++ tests/mocks/Ubuntu/SystemImage/qmldir 2014-08-13 18:12:02 +0000
1541@@ -0,0 +1,3 @@
1542+module Ubuntu.SystemImage
1543+plugin MockSystemImage
1544+typeinfo SystemImage.qmltypes
1545
1546=== modified file 'tests/qmltests/Greeter/tst_Lockscreen.qml'
1547--- tests/qmltests/Greeter/tst_Lockscreen.qml 2014-07-02 16:17:14 +0000
1548+++ tests/qmltests/Greeter/tst_Lockscreen.qml 2014-08-13 18:12:02 +0000
1549@@ -319,11 +319,9 @@
1550 if (data.animation) {
1551 if (data.alphanumeric) {
1552 tryCompare(inputField, "placeholderText", lockscreen.wrongPlaceholderText)
1553- tryCompare(inputField, "placeholderText", lockscreen.placeholderText)
1554 } else {
1555 var label = findChild(lockscreen, "pinentryFieldPlaceHolder");
1556 tryCompare(label, "text", lockscreen.wrongPlaceholderText)
1557- tryCompare(label, "text", lockscreen.placeholderText)
1558 }
1559 }
1560
1561
1562=== modified file 'tests/qmltests/tst_ShellWithPin.qml'
1563--- tests/qmltests/tst_ShellWithPin.qml 2014-08-05 18:18:09 +0000
1564+++ tests/qmltests/tst_ShellWithPin.qml 2014-08-13 18:12:02 +0000
1565@@ -16,8 +16,10 @@
1566
1567 import QtQuick 2.0
1568 import QtTest 1.0
1569+import AccountsService 0.1
1570 import GSettings 1.0
1571 import LightDM 0.1 as LightDM
1572+import Ubuntu.SystemImage 0.1
1573 import Unity.Application 0.1
1574 import Unity.Test 0.1 as UT
1575 import Powerd 0.1
1576@@ -25,6 +27,7 @@
1577 import "../../qml"
1578
1579 Item {
1580+ id: root
1581 width: shell.width
1582 height: shell.height
1583
1584@@ -53,6 +56,12 @@
1585 signalName: "sessionStarted"
1586 }
1587
1588+ SignalSpy {
1589+ id: resetSpy
1590+ target: SystemImage
1591+ signalName: "resettingDevice"
1592+ }
1593+
1594 UT.UnityTestCase {
1595 name: "ShellWithPin"
1596 when: windowShown
1597@@ -64,6 +73,8 @@
1598
1599 function init() {
1600 swipeAwayGreeter()
1601+ shell.failedLoginsDelayAttempts = -1
1602+ shell.maxFailedLogins = -1
1603 }
1604
1605 function cleanup() {
1606@@ -111,6 +122,7 @@
1607 }
1608
1609 function test_login() {
1610+ sessionSpy.clear()
1611 tryCompare(sessionSpy, "count", 0)
1612 enterPin("1234")
1613 tryCompare(sessionSpy, "count", 1)
1614@@ -175,5 +187,49 @@
1615 ApplicationManager.startApplication("gallery-app", ApplicationManager.NoFlag)
1616 tryCompare(lockscreen, "shown", true)
1617 }
1618+
1619+ function test_failedLoginsCount() {
1620+ AccountsService.failedLogins = 0
1621+
1622+ enterPin("1111")
1623+ tryCompare(AccountsService, "failedLogins", 1)
1624+
1625+ enterPin("1234")
1626+ tryCompare(AccountsService, "failedLogins", 0)
1627+ }
1628+
1629+ function test_wrongEntries() {
1630+ shell.failedLoginsDelayAttempts = 3
1631+
1632+ var placeHolder = findChild(shell, "pinentryFieldPlaceHolder")
1633+ tryCompare(placeHolder, "text", "Enter your passcode")
1634+
1635+ enterPin("1111")
1636+ tryCompare(placeHolder, "text", "Incorrect passcode\nPlease re-enter")
1637+
1638+ enterPin("1111")
1639+ tryCompare(placeHolder, "text", "Incorrect passcode\nPlease re-enter")
1640+
1641+ enterPin("1111")
1642+ tryCompare(placeHolder, "text", "Too many incorrect attempts\nPlease wait")
1643+ }
1644+
1645+ function test_factoryReset() {
1646+ shell.maxFailedLogins = 3
1647+ resetSpy.clear()
1648+
1649+ enterPin("1111")
1650+ enterPin("1111")
1651+ tryCompareFunction(function() {return findChild(root, "infoPopup") !== null}, true)
1652+
1653+ var dialog = findChild(root, "infoPopup")
1654+ var button = findChild(dialog, "infoPopupOkButton")
1655+ mouseClick(button, units.gu(1), units.gu(1))
1656+ tryCompareFunction(function() {return findChild(root, "infoPopup")}, null)
1657+
1658+ tryCompare(resetSpy, "count", 0)
1659+ enterPin("1111")
1660+ tryCompare(resetSpy, "count", 1)
1661+ }
1662 }
1663 }

Subscribers

People subscribed via source and target branches