| 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 |