Chromium Code Reviews| 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); | |
|
stevenjb
2017/01/03 18:35:13
break;
sammiequon
2017/01/03 22:15:07
Done.
| |
| 221 case chrome.quickUnlockPrivate.CredentialProblem.TOO_WEAK: | |
| 222 this.showProblem_(MessageType.TOO_WEAK, ProblemType.ERROR); | |
| 223 break; | |
| 224 default: | |
| 225 assertNotReached(); | |
| 226 break; | |
| 227 } | |
| 228 } | |
| 229 | |
| 230 }, | |
| 231 | |
| 191 /** @private */ | 232 /** @private */ |
| 192 onPinChange_: function() { | 233 onPinChange_: function() { |
| 193 if (!this.isConfirmStep_) { | 234 if (!this.isConfirmStep_) { |
| 194 var isPinLongEnough = this.isPinLongEnough_(this.pinKeyboardValue_); | 235 if (this.pinKeyboardValue_) { |
| 195 var isWeak = isPinLongEnough && this.isPinWeak_(this.pinKeyboardValue_); | 236 this.quickUnlockPrivate_.checkCredential( |
| 237 chrome.quickUnlockPrivate.QuickUnlockMode.PIN, | |
| 238 this.pinKeyboardValue_, | |
| 239 this.processPinProblems_.bind(this)); | |
| 240 } | |
| 241 return; | |
| 242 } | |
| 196 | 243 |
| 197 if (!isPinLongEnough) | 244 if (this.canSubmit_()) { |
| 198 this.showProblem_('configurePinTooShort', 'warning'); | 245 this.hideProblem_(); |
| 199 else if (isWeak) | 246 this.enableSubmit_ = true; |
| 200 this.showProblem_('configurePinWeakPin', 'warning'); | 247 return; |
| 201 else | 248 } |
| 202 this.hideProblem_(); | |
| 203 | 249 |
| 204 this.enableSubmit_ = isPinLongEnough; | 250 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 }, | 251 }, |
| 215 | 252 |
| 216 /** @private */ | 253 /** @private */ |
| 217 onPinSubmit_: function() { | 254 onPinSubmit_: function() { |
| 218 if (!this.isConfirmStep_) { | 255 if (!this.isConfirmStep_) { |
| 219 if (this.isPinLongEnough_(this.pinKeyboardValue_)) { | 256 this.initialPin_ = this.pinKeyboardValue_; |
| 220 this.initialPin_ = this.pinKeyboardValue_; | 257 this.pinKeyboardValue_ = ''; |
| 221 this.pinKeyboardValue_ = ''; | 258 this.isConfirmStep_ = true; |
| 222 this.isConfirmStep_ = true; | 259 this.onPinChange_(); |
| 223 this.onPinChange_(); | 260 this.$.pinKeyboard.focus(); |
| 224 this.$.pinKeyboard.focus(); | 261 this.writeUma_(LockScreenProgress.ENTER_PIN); |
| 225 this.writeUma_(LockScreenProgress.ENTER_PIN); | 262 return; |
| 226 } | 263 } |
| 227 } else { | 264 // 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. | 265 // The PIN is not guaranteed to be valid in that case. |
| 229 // The PIN is not guaranteed to be valid in that case. | 266 if (!this.canSubmit_()) { |
| 230 if (!this.isPinConfirmed_()) { | 267 this.showProblem_(MessageType.MISMATCH, ProblemType.ERROR); |
| 231 this.showProblem_('configurePinMismatched', 'error'); | 268 return; |
| 269 } | |
| 270 | |
| 271 function onSetModesCompleted(didSet) { | |
| 272 if (!didSet) { | |
| 273 console.error('Failed to update pin'); | |
| 232 return; | 274 return; |
| 233 } | 275 } |
| 234 | 276 |
| 235 function onSetModesCompleted(didSet) { | 277 this.resetState_(); |
| 236 if (!didSet) { | 278 this.fire('done'); |
| 237 console.error('Failed to update pin'); | 279 } |
| 238 return; | |
| 239 } | |
| 240 | 280 |
| 241 this.resetState_(); | 281 this.setModes.call( |
| 242 this.fire('done'); | 282 null, |
| 243 } | 283 [chrome.quickUnlockPrivate.QuickUnlockMode.PIN], |
| 244 | 284 [this.pinKeyboardValue_], |
| 245 this.setModes.call( | 285 onSetModesCompleted.bind(this)); |
| 246 null, | 286 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 }, | 287 }, |
| 253 | 288 |
| 254 /** | 289 /** |
| 255 * @private | 290 * @private |
| 256 * @param {string} problemMessage | 291 * @param {string} problemMessage |
| 257 * @return {boolean} | 292 * @return {boolean} |
| 258 */ | 293 */ |
| 259 hasProblem_: function(problemMessage) { | 294 hasProblem_: function(problemMessage) { |
| 260 return !!problemMessage; | 295 return !!problemMessage; |
| 261 }, | 296 }, |
| (...skipping 12 matching lines...) Expand all Loading... | |
| 274 * @private | 309 * @private |
| 275 * @param {boolean} isConfirmStep | 310 * @param {boolean} isConfirmStep |
| 276 * @return {string} | 311 * @return {string} |
| 277 */ | 312 */ |
| 278 getContinueMessage_: function(isConfirmStep) { | 313 getContinueMessage_: function(isConfirmStep) { |
| 279 return this.i18n(isConfirmStep ? 'confirm' : 'configurePinContinueButton'); | 314 return this.i18n(isConfirmStep ? 'confirm' : 'configurePinContinueButton'); |
| 280 }, | 315 }, |
| 281 }); | 316 }); |
| 282 | 317 |
| 283 })(); | 318 })(); |
| OLD | NEW |