OLD | NEW |
1 // Copyright 2016 The Chromium Authors. All rights reserved. | 1 // Copyright 2016 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 /** | 5 /** |
6 * @fileoverview | 6 * @fileoverview |
7 * 'settings-setup-pin-dialog' is the settings page for choosing a PIN. | 7 * 'settings-setup-pin-dialog' is the settings page for choosing a PIN. |
8 * | 8 * |
9 * Example: | 9 * Example: |
10 * | 10 * |
11 * <settings-setup-pin-dialog set-modes="[[quickUnlockSetModes]]"> | 11 * <settings-setup-pin-dialog set-modes="[[quickUnlockSetModes]]"> |
12 * </settings-setup-pin-dialog> | 12 * </settings-setup-pin-dialog> |
13 */ | 13 */ |
14 | 14 |
15 (function() { | 15 (function() { |
16 'use strict'; | 16 'use strict'; |
17 | 17 |
18 /** | 18 /** |
19 * A list of the top-10 most commmonly used PINs. This list is taken from | 19 * Keep in sync with the string keys provided by settings. |
20 * www.datagenetics.com/blog/september32012/. | 20 * @enum {string} |
21 * @const | |
22 */ | 21 */ |
23 var WEAK_PINS = [ | 22 var MessageType = { |
24 '1234', '1111', '0000', '1212', '7777', '1004', '2000', '4444', '2222', | 23 TOO_SHORT: 'configurePinTooShort', |
25 '6969' | 24 TOO_LONG: 'configurePinTooLong', |
26 ]; | 25 TOO_WEAK: 'configurePinWeakPin', |
| 26 MISMATCH: 'configurePinMismatched' |
| 27 }; |
| 28 |
| 29 /** @enum {string} */ |
| 30 var ProblemType = { |
| 31 WARNING: 'warning', |
| 32 ERROR: 'error' |
| 33 }; |
27 | 34 |
28 Polymer({ | 35 Polymer({ |
29 is: 'settings-setup-pin-dialog', | 36 is: 'settings-setup-pin-dialog', |
30 | 37 |
31 behaviors: [I18nBehavior], | 38 behaviors: [I18nBehavior], |
32 | 39 |
33 properties: { | 40 properties: { |
34 /** | 41 /** |
35 * The current PIN keyboard value. | 42 * The current PIN keyboard value. |
36 * @private | 43 * @private |
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
73 }, | 80 }, |
74 | 81 |
75 /** | 82 /** |
76 * The current step/subpage we are on. | 83 * The current step/subpage we are on. |
77 * @private | 84 * @private |
78 */ | 85 */ |
79 isConfirmStep_: { | 86 isConfirmStep_: { |
80 type: Boolean, | 87 type: Boolean, |
81 value: false | 88 value: false |
82 }, | 89 }, |
| 90 |
| 91 /** |
| 92 * Interface for chrome.quickUnlockPrivate calls. May be overriden by tests. |
| 93 * @private |
| 94 */ |
| 95 quickUnlockPrivate_: { |
| 96 type: Object, |
| 97 value: chrome.quickUnlockPrivate |
| 98 }, |
83 }, | 99 }, |
84 | 100 |
85 /** @override */ | 101 /** @override */ |
86 attached: function() { | 102 attached: function() { |
87 this.resetState_(); | 103 this.resetState_(); |
88 }, | 104 }, |
89 | 105 |
90 open: function() { | 106 open: function() { |
91 this.$.dialog.showModal(); | 107 this.$.dialog.showModal(); |
92 this.$.pinKeyboard.focus(); | 108 this.$.pinKeyboard.focus(); |
93 }, | 109 }, |
94 | 110 |
95 close: function() { | 111 close: function() { |
96 if (this.$.dialog.open) | 112 if (this.$.dialog.open) |
97 this.$.dialog.close(); | 113 this.$.dialog.close(); |
98 | 114 |
99 this.resetState_(); | 115 this.resetState_(); |
100 }, | 116 }, |
101 | 117 |
102 /** | 118 /** |
103 * Resets the element to the initial state. | 119 * Resets the element to the initial state. |
104 * @private | 120 * @private |
105 */ | 121 */ |
106 resetState_: function() { | 122 resetState_: function() { |
107 this.initialPin_ = ''; | 123 this.initialPin_ = ''; |
108 this.pinKeyboardValue_ = ''; | 124 this.pinKeyboardValue_ = ''; |
109 this.enableSubmit_ = false; | 125 this.enableSubmit_ = false; |
110 this.isConfirmStep_ = false; | 126 this.isConfirmStep_ = false; |
| 127 this.hideProblem_(); |
111 this.onPinChange_(); | 128 this.onPinChange_(); |
112 }, | 129 }, |
113 | 130 |
114 /** @private */ | 131 /** @private */ |
115 onCancelTap_: function() { | 132 onCancelTap_: function() { |
116 this.resetState_(); | 133 this.resetState_(); |
117 this.$.dialog.close(); | 134 this.$.dialog.close(); |
118 }, | 135 }, |
119 | 136 |
120 /** | 137 /** |
121 * Returns true if the given PIN is likely easy to guess. | 138 * Returns true if the PIN is ready to be changed to a new value. |
122 * @private | 139 * @private |
123 * @param {string} pin | |
124 * @return {boolean} | 140 * @return {boolean} |
125 */ | 141 */ |
126 isPinWeak_: function(pin) { | 142 canSubmit_: function() { |
127 // Warn if it's a top-10 pin. | 143 return this.initialPin_ == this.pinKeyboardValue_; |
128 if (WEAK_PINS.includes(pin)) | |
129 return true; | |
130 | |
131 // Warn if the PIN is consecutive digits. | |
132 var delta = 0; | |
133 for (var i = 1; i < pin.length; ++i) { | |
134 var prev = Number(pin[i - 1]); | |
135 var num = Number(pin[i]); | |
136 if (Number.isNaN(prev) || Number.isNaN(num)) | |
137 return false; | |
138 delta = Math.max(delta, Math.abs(num - prev)); | |
139 } | |
140 | |
141 return delta <= 1; | |
142 }, | 144 }, |
143 | 145 |
144 /** | 146 /** |
145 * Returns true if the given PIN matches PIN requirements, such as minimum | 147 * Handles writting the appropriate message to |problemMessage_|. |
146 * length. | |
147 * @private | 148 * @private |
148 * @param {string|undefined} pin | 149 * @param {string} messageId |
149 * @return {boolean} | 150 * @param {chrome.quickUnlockPrivate.CredentialRequirements} requirements |
| 151 * The requirements received from getCredentialRequirements. |
150 */ | 152 */ |
151 isPinLongEnough_: function(pin) { | 153 processPinRequirements_: function(messageId, requirements) { |
152 return !!pin && pin.length >= 4; | 154 var additionalInformation = ''; |
| 155 switch (messageId) { |
| 156 case MessageType.TOO_SHORT: |
| 157 additionalInformation = requirements.minLength.toString(); |
| 158 break; |
| 159 case MessageType.TOO_LONG: |
| 160 additionalInformation = requirements.maxLength.toString(); |
| 161 break; |
| 162 case MessageType.TOO_WEAK: |
| 163 case MessageType.MISMATCH: |
| 164 break; |
| 165 default: |
| 166 assertNotReached(); |
| 167 break; |
| 168 } |
| 169 this.problemMessage_ = this.i18n(messageId, additionalInformation); |
153 }, | 170 }, |
154 | 171 |
155 /** | 172 /** |
156 * Returns true if the currently entered PIN is the same as the initially | |
157 * submitted PIN. | |
158 * @private | |
159 * @return {boolean} | |
160 */ | |
161 isPinConfirmed_: function() { | |
162 return this.isPinLongEnough_(this.pinKeyboardValue_) && | |
163 this.initialPin_ == this.pinKeyboardValue_; | |
164 }, | |
165 | |
166 /** | |
167 * Notify the user about a problem. | 173 * Notify the user about a problem. |
168 * @private | 174 * @private |
169 * @param {string} messageId | 175 * @param {string} messageId |
170 * @param {string} problemClass | 176 * @param {string} problemClass |
171 */ | 177 */ |
172 showProblem_: function(messageId, problemClass) { | 178 showProblem_: function(messageId, problemClass) { |
173 var previousMessage = this.problemMessage_; | 179 this.quickUnlockPrivate_.getCredentialRequirements( |
174 | 180 chrome.quickUnlockPrivate.QuickUnlockMode.PIN, |
175 // Update problem info. | 181 this.processPinRequirements_.bind(this, messageId)); |
176 this.problemMessage_ = this.i18n(messageId); | |
177 this.problemClass_ = problemClass; | 182 this.problemClass_ = problemClass; |
178 this.updateStyles(); | 183 this.updateStyles(); |
179 | 184 this.enableSubmit_ = problemClass != ProblemType.ERROR && |
180 // If the problem message has changed, fire an alert. | 185 messageId != MessageType.MISMATCH; |
181 if (previousMessage != this.problemMessage_) | 186 }, |
182 this.$.problemDiv.setAttribute('role', 'alert'); | |
183 }, | |
184 | 187 |
185 /** @private */ | 188 /** @private */ |
186 hideProblem_: function() { | 189 hideProblem_: function() { |
187 this.problemMessage_ = ''; | 190 this.problemMessage_ = ''; |
188 this.problemClass_ = ''; | 191 this.problemClass_ = ''; |
189 }, | 192 }, |
190 | 193 |
| 194 /** |
| 195 * Processes the message received from the quick unlock api and hides/shows |
| 196 * the problem based on the message. |
| 197 * @private |
| 198 * @param {chrome.quickUnlockPrivate.CredentialCheck} message The message |
| 199 * received from checkCredential. |
| 200 */ |
| 201 processPinProblems_: function(message) { |
| 202 if (!message.errors.length && !message.warnings.length) { |
| 203 this.hideProblem_(); |
| 204 this.enableSubmit_ = true; |
| 205 return; |
| 206 } |
| 207 |
| 208 if (message.warnings.length) { |
| 209 assert(message.warnings[0] == |
| 210 chrome.quickUnlockPrivate.CredentialProblem.TOO_WEAK); |
| 211 this.showProblem_(MessageType.TOO_WEAK, ProblemType.WARNING); |
| 212 } |
| 213 |
| 214 if (message.errors.length) { |
| 215 switch (message.errors[0]) { |
| 216 case chrome.quickUnlockPrivate.CredentialProblem.TOO_SHORT: |
| 217 this.showProblem_(MessageType.TOO_SHORT, ProblemType.ERROR); |
| 218 break; |
| 219 case chrome.quickUnlockPrivate.CredentialProblem.TOO_LONG: |
| 220 this.showProblem_(MessageType.TOO_LONG, ProblemType.ERROR); |
| 221 break; |
| 222 case chrome.quickUnlockPrivate.CredentialProblem.TOO_WEAK: |
| 223 this.showProblem_(MessageType.TOO_WEAK, ProblemType.ERROR); |
| 224 break; |
| 225 default: |
| 226 assertNotReached(); |
| 227 break; |
| 228 } |
| 229 } |
| 230 |
| 231 }, |
| 232 |
191 /** @private */ | 233 /** @private */ |
192 onPinChange_: function() { | 234 onPinChange_: function() { |
193 if (!this.isConfirmStep_) { | 235 if (!this.isConfirmStep_) { |
194 var isPinLongEnough = this.isPinLongEnough_(this.pinKeyboardValue_); | 236 if (this.pinKeyboardValue_) { |
195 var isWeak = isPinLongEnough && this.isPinWeak_(this.pinKeyboardValue_); | 237 this.quickUnlockPrivate_.checkCredential( |
| 238 chrome.quickUnlockPrivate.QuickUnlockMode.PIN, |
| 239 this.pinKeyboardValue_, |
| 240 this.processPinProblems_.bind(this)); |
| 241 } |
| 242 return; |
| 243 } |
196 | 244 |
197 if (!isPinLongEnough) | 245 if (this.canSubmit_()) { |
198 this.showProblem_('configurePinTooShort', 'warning'); | 246 this.hideProblem_(); |
199 else if (isWeak) | 247 this.enableSubmit_ = true; |
200 this.showProblem_('configurePinWeakPin', 'warning'); | 248 return; |
201 else | 249 } |
202 this.hideProblem_(); | |
203 | 250 |
204 this.enableSubmit_ = isPinLongEnough; | 251 this.showProblem_(MessageType.MISMATCH, ProblemType.WARNING); |
205 | |
206 } else { | |
207 if (this.isPinConfirmed_()) | |
208 this.hideProblem_(); | |
209 else | |
210 this.showProblem_('configurePinMismatched', 'warning'); | |
211 | |
212 this.enableSubmit_ = true; | |
213 } | |
214 }, | 252 }, |
215 | 253 |
216 /** @private */ | 254 /** @private */ |
217 onPinSubmit_: function() { | 255 onPinSubmit_: function() { |
218 if (!this.isConfirmStep_) { | 256 if (!this.isConfirmStep_) { |
219 if (this.isPinLongEnough_(this.pinKeyboardValue_)) { | 257 this.initialPin_ = this.pinKeyboardValue_; |
220 this.initialPin_ = this.pinKeyboardValue_; | 258 this.pinKeyboardValue_ = ''; |
221 this.pinKeyboardValue_ = ''; | 259 this.isConfirmStep_ = true; |
222 this.isConfirmStep_ = true; | 260 this.onPinChange_(); |
223 this.onPinChange_(); | 261 this.$.pinKeyboard.focus(); |
224 this.$.pinKeyboard.focus(); | 262 this.writeUma_(LockScreenProgress.ENTER_PIN); |
225 this.writeUma_(LockScreenProgress.ENTER_PIN); | 263 return; |
226 } | 264 } |
227 } else { | 265 // onPinSubmit_ gets called if the user hits enter on the PIN keyboard. |
228 // onPinSubmit_ gets called if the user hits enter on the PIN keyboard. | 266 // The PIN is not guaranteed to be valid in that case. |
229 // The PIN is not guaranteed to be valid in that case. | 267 if (!this.canSubmit_()) { |
230 if (!this.isPinConfirmed_()) { | 268 this.showProblem_(MessageType.MISMATCH, ProblemType.ERROR); |
231 this.showProblem_('configurePinMismatched', 'error'); | 269 return; |
| 270 } |
| 271 |
| 272 function onSetModesCompleted(didSet) { |
| 273 if (!didSet) { |
| 274 console.error('Failed to update pin'); |
232 return; | 275 return; |
233 } | 276 } |
234 | 277 |
235 function onSetModesCompleted(didSet) { | 278 this.resetState_(); |
236 if (!didSet) { | 279 this.fire('done'); |
237 console.error('Failed to update pin'); | 280 } |
238 return; | |
239 } | |
240 | 281 |
241 this.resetState_(); | 282 this.setModes.call( |
242 this.fire('done'); | 283 null, |
243 } | 284 [chrome.quickUnlockPrivate.QuickUnlockMode.PIN], |
244 | 285 [this.pinKeyboardValue_], |
245 this.setModes.call( | 286 onSetModesCompleted.bind(this)); |
246 null, | 287 this.writeUma_(LockScreenProgress.CONFIRM_PIN); |
247 [chrome.quickUnlockPrivate.QuickUnlockMode.PIN], | |
248 [this.pinKeyboardValue_], | |
249 onSetModesCompleted.bind(this)); | |
250 this.writeUma_(LockScreenProgress.CONFIRM_PIN); | |
251 } | |
252 }, | 288 }, |
253 | 289 |
254 /** | 290 /** |
255 * @private | 291 * @private |
256 * @param {string} problemMessage | 292 * @param {string} problemMessage |
257 * @return {boolean} | 293 * @return {boolean} |
258 */ | 294 */ |
259 hasProblem_: function(problemMessage) { | 295 hasProblem_: function(problemMessage) { |
260 return !!problemMessage; | 296 return !!problemMessage; |
261 }, | 297 }, |
(...skipping 12 matching lines...) Expand all Loading... |
274 * @private | 310 * @private |
275 * @param {boolean} isConfirmStep | 311 * @param {boolean} isConfirmStep |
276 * @return {string} | 312 * @return {string} |
277 */ | 313 */ |
278 getContinueMessage_: function(isConfirmStep) { | 314 getContinueMessage_: function(isConfirmStep) { |
279 return this.i18n(isConfirmStep ? 'confirm' : 'configurePinContinueButton'); | 315 return this.i18n(isConfirmStep ? 'confirm' : 'configurePinContinueButton'); |
280 }, | 316 }, |
281 }); | 317 }); |
282 | 318 |
283 })(); | 319 })(); |
OLD | NEW |