| 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 * | 7 * |
| 8 * 'settings-password-prompt-dialog' shows a dialog which asks for the user to | 8 * 'settings-password-prompt-dialog' shows a dialog which asks for the user to |
| 9 * enter their password. It validates the password is correct. Once the user has | 9 * enter their password. It validates the password is correct. Once the user has |
| 10 * entered their account password, the page fires an 'authenticated' event and | 10 * entered their account password, the page fires an 'authenticated' event and |
| 11 * updates the setModes binding. | 11 * updates the setModes binding. |
| 12 * | 12 * |
| 13 * The setModes binding is a wrapper around chrome.quickUnlockPrivate.setModes | 13 * The setModes binding is a wrapper around chrome.quickUnlockPrivate.setModes |
| 14 * which has a prebound account password. The account password by itself is not | 14 * which has a prebound account password. The account password by itself is not |
| 15 * available for other elements to access. | 15 * available for other elements to access. |
| 16 * | 16 * |
| 17 * Example: | 17 * Example: |
| 18 * | 18 * |
| 19 * <settings-password-prompt-dialog | 19 * <settings-password-prompt-dialog |
| 20 * id="passwordPrompt" | 20 * id="passwordPrompt" |
| 21 * set-modes="[[setModes]]"> | 21 * set-modes="[[setModes]]"> |
| 22 * </settings-password-prompt-dialog> | 22 * </settings-password-prompt-dialog> |
| 23 * | 23 * |
| 24 * this.$.passwordPrompt.open() | 24 * this.$.passwordPrompt.open() |
| 25 */ | 25 */ |
| 26 | 26 |
| 27 (function() { | 27 (function() { |
| 28 'use strict'; | 28 'use strict'; |
| 29 | 29 |
| 30 /** @const */ var PASSWORD_ACTIVE_DURATION_MS = 10 * 60 * 1000; // Ten minutes. | 30 /** @const */ var PASSWORD_ACTIVE_DURATION_MS = |
| 31 10 * 60 * 1000; // Ten minutes. |
| 31 | 32 |
| 32 Polymer({ | 33 Polymer({ |
| 33 is: 'settings-password-prompt-dialog', | 34 is: 'settings-password-prompt-dialog', |
| 34 | 35 |
| 35 properties: { | 36 properties: { |
| 36 /** | 37 /** |
| 37 * A wrapper around chrome.quickUnlockPrivate.setModes with the account | 38 * A wrapper around chrome.quickUnlockPrivate.setModes with the account |
| 38 * password already supplied. If this is null, the authentication screen | 39 * password already supplied. If this is null, the authentication screen |
| 39 * needs to be redisplayed. This property will be cleared after | 40 * needs to be redisplayed. This property will be cleared after |
| 40 * |this.passwordActiveDurationMs_| milliseconds. | 41 * |this.passwordActiveDurationMs_| milliseconds. |
| 41 */ | 42 */ |
| 42 setModes: { | 43 setModes: {type: Object, notify: true}, |
| 43 type: Object, | 44 |
| 44 notify: true | 45 /** |
| 46 * The actual value of the password field. This is cleared whenever the |
| 47 * authentication screen is not displayed so that the user's password is |
| 48 * not |
| 49 * easily available to an attacker. The actual password is stored as an |
| 50 * captured closure variable inside of setModes. |
| 51 * @private |
| 52 */ |
| 53 password_: {type: String, observer: 'onPasswordChanged_'}, |
| 54 |
| 55 /** |
| 56 * Helper property which marks password as valid/invalid. |
| 57 * @private |
| 58 */ |
| 59 passwordInvalid_: Boolean, |
| 60 |
| 61 /** |
| 62 * Interface for chrome.quickUnlockPrivate calls. May be overriden by |
| 63 * tests. |
| 64 * @private |
| 65 */ |
| 66 quickUnlockPrivate_: {type: Object, value: chrome.quickUnlockPrivate}, |
| 67 |
| 68 /** |
| 69 * writeUma_ is a function that handles writing uma stats. It may be |
| 70 * overridden for tests. |
| 71 * |
| 72 * @type {Function} |
| 73 * @private |
| 74 */ |
| 75 writeUma_: { |
| 76 type: Object, |
| 77 value: function() { |
| 78 return settings.recordLockScreenProgress; |
| 79 } |
| 80 }, |
| 81 |
| 82 /** |
| 83 * PASSWORD_ACTIVE_DURATION_MS value. May be overridden by tests. |
| 84 * @private |
| 85 */ |
| 86 passwordActiveDurationMs_: |
| 87 {type: Number, value: PASSWORD_ACTIVE_DURATION_MS}, |
| 45 }, | 88 }, |
| 46 | 89 |
| 47 /** | 90 /** |
| 48 * The actual value of the password field. This is cleared whenever the | 91 * Open up the dialog. This will wait until the dialog has loaded before |
| 49 * authentication screen is not displayed so that the user's password is not | 92 * opening it. |
| 50 * easily available to an attacker. The actual password is stored as an | |
| 51 * captured closure variable inside of setModes. | |
| 52 * @private | |
| 53 */ | 93 */ |
| 54 password_: { | 94 open: function() { |
| 55 type: String, | 95 // Wait until the dialog is attached to the DOM before trying to open it. |
| 56 observer: 'onPasswordChanged_' | 96 var dialog = /** @type {{isConnected: boolean}} */ (this.$.dialog); |
| 97 if (!dialog.isConnected) { |
| 98 setTimeout(this.open.bind(this)); |
| 99 return; |
| 100 } |
| 101 |
| 102 if (this.$.dialog.open) |
| 103 return; |
| 104 |
| 105 this.writeUma_(LockScreenProgress.START_SCREEN_LOCK); |
| 106 this.$.dialog.showModal(); |
| 107 }, |
| 108 |
| 109 /** @private */ |
| 110 onCancelTap_: function() { |
| 111 if (this.$.dialog.open) |
| 112 this.$.dialog.close(); |
| 57 }, | 113 }, |
| 58 | 114 |
| 59 /** | 115 /** |
| 60 * Helper property which marks password as valid/invalid. | 116 * Called whenever the dialog is closed. |
| 61 * @private | 117 * @private |
| 62 */ | 118 */ |
| 63 passwordInvalid_: Boolean, | 119 onClose_: function() { |
| 120 this.password_ = ''; |
| 121 }, |
| 64 | 122 |
| 65 /** | 123 /** @private */ |
| 66 * Interface for chrome.quickUnlockPrivate calls. May be overriden by tests. | 124 onKeydown_: function(e) { |
| 67 * @private | 125 if (e.key == 'Enter') |
| 68 */ | 126 this.submitPassword_(); |
| 69 quickUnlockPrivate_: { | |
| 70 type: Object, | |
| 71 value: chrome.quickUnlockPrivate | |
| 72 }, | 127 }, |
| 73 | 128 |
| 74 /** | 129 /** |
| 75 * writeUma_ is a function that handles writing uma stats. It may be | 130 * Run the account password check. |
| 76 * overridden for tests. | |
| 77 * | |
| 78 * @type {Function} | |
| 79 * @private | 131 * @private |
| 80 */ | 132 */ |
| 81 writeUma_: { | 133 submitPassword_: function() { |
| 82 type: Object, | 134 clearTimeout(this.clearAccountPasswordTimeout_); |
| 83 value: function() { return settings.recordLockScreenProgress; } | 135 |
| 136 // The user might have started entering a password and then deleted it |
| 137 // all. |
| 138 // Do not submit/show an error in this case. |
| 139 if (!this.password_) { |
| 140 this.passwordInvalid_ = false; |
| 141 return; |
| 142 } |
| 143 |
| 144 function onPasswordChecked(valid) { |
| 145 // The password might have been cleared during the duration of the |
| 146 // getActiveModes call. |
| 147 this.passwordInvalid_ = !valid && !!this.password_; |
| 148 |
| 149 if (valid) { |
| 150 // Create the |this.setModes| closure and automatically clear it after |
| 151 // |this.passwordActiveDurationMs_|. |
| 152 var password = this.password_; |
| 153 this.password_ = ''; |
| 154 |
| 155 this.setModes = function(modes, credentials, onComplete) { |
| 156 this.quickUnlockPrivate_.setModes( |
| 157 password, modes, credentials, onComplete); |
| 158 }.bind(this); |
| 159 |
| 160 function clearSetModes() { |
| 161 // Reset the password so that any cached references to this.setModes |
| 162 // will fail. |
| 163 password = ''; |
| 164 this.setModes = null; |
| 165 } |
| 166 |
| 167 this.clearAccountPasswordTimeout_ = setTimeout( |
| 168 clearSetModes.bind(this), this.passwordActiveDurationMs_); |
| 169 |
| 170 // Clear stored password state and close the dialog. |
| 171 this.password_ = ''; |
| 172 if (this.$.dialog.open) |
| 173 this.$.dialog.close(); |
| 174 |
| 175 this.writeUma_(LockScreenProgress.ENTER_PASSWORD_CORRECTLY); |
| 176 } |
| 177 } |
| 178 |
| 179 this.checkAccountPassword_(onPasswordChecked.bind(this)); |
| 180 }, |
| 181 |
| 182 /** @private */ |
| 183 onPasswordChanged_: function() { |
| 184 this.passwordInvalid_ = false; |
| 185 }, |
| 186 |
| 187 /** @private */ |
| 188 enableConfirm_: function() { |
| 189 return !!this.password_ && !this.passwordInvalid_; |
| 84 }, | 190 }, |
| 85 | 191 |
| 86 /** | 192 /** |
| 87 * PASSWORD_ACTIVE_DURATION_MS value. May be overridden by tests. | 193 * Helper method that checks if the current password is valid. |
| 88 * @private | 194 * @param {function(boolean):void} onCheck |
| 89 */ | 195 */ |
| 90 passwordActiveDurationMs_: { | 196 checkAccountPassword_: function(onCheck) { |
| 91 type: Number, | 197 // We check the account password by trying to update the active set of |
| 92 value: PASSWORD_ACTIVE_DURATION_MS | 198 // quick |
| 93 }, | 199 // unlock modes without changing any credentials. |
| 94 }, | 200 this.quickUnlockPrivate_.getActiveModes(function(modes) { |
| 95 | 201 var credentials = |
| 96 /** | 202 /** @type {!Array<string>} */ (Array(modes.length).fill('')); |
| 97 * Open up the dialog. This will wait until the dialog has loaded before | 203 this.quickUnlockPrivate_.setModes( |
| 98 * opening it. | 204 this.password_, modes, credentials, onCheck); |
| 99 */ | 205 }.bind(this)); |
| 100 open: function() { | |
| 101 // Wait until the dialog is attached to the DOM before trying to open it. | |
| 102 var dialog = /** @type {{isConnected: boolean}} */ (this.$.dialog); | |
| 103 if (!dialog.isConnected) { | |
| 104 setTimeout(this.open.bind(this)); | |
| 105 return; | |
| 106 } | 206 } |
| 107 | 207 }); |
| 108 if (this.$.dialog.open) | |
| 109 return; | |
| 110 | |
| 111 this.writeUma_(LockScreenProgress.START_SCREEN_LOCK); | |
| 112 this.$.dialog.showModal(); | |
| 113 }, | |
| 114 | |
| 115 /** @private */ | |
| 116 onCancelTap_: function() { | |
| 117 if (this.$.dialog.open) | |
| 118 this.$.dialog.close(); | |
| 119 }, | |
| 120 | |
| 121 /** | |
| 122 * Called whenever the dialog is closed. | |
| 123 * @private | |
| 124 */ | |
| 125 onClose_: function() { | |
| 126 this.password_ = ''; | |
| 127 }, | |
| 128 | |
| 129 /** @private */ | |
| 130 onKeydown_: function(e) { | |
| 131 if (e.key == 'Enter') | |
| 132 this.submitPassword_(); | |
| 133 }, | |
| 134 | |
| 135 /** | |
| 136 * Run the account password check. | |
| 137 * @private | |
| 138 */ | |
| 139 submitPassword_: function() { | |
| 140 clearTimeout(this.clearAccountPasswordTimeout_); | |
| 141 | |
| 142 // The user might have started entering a password and then deleted it all. | |
| 143 // Do not submit/show an error in this case. | |
| 144 if (!this.password_) { | |
| 145 this.passwordInvalid_ = false; | |
| 146 return; | |
| 147 } | |
| 148 | |
| 149 function onPasswordChecked(valid) { | |
| 150 // The password might have been cleared during the duration of the | |
| 151 // getActiveModes call. | |
| 152 this.passwordInvalid_ = !valid && !!this.password_; | |
| 153 | |
| 154 if (valid) { | |
| 155 // Create the |this.setModes| closure and automatically clear it after | |
| 156 // |this.passwordActiveDurationMs_|. | |
| 157 var password = this.password_; | |
| 158 this.password_ = ''; | |
| 159 | |
| 160 this.setModes = function(modes, credentials, onComplete) { | |
| 161 this.quickUnlockPrivate_.setModes( | |
| 162 password, modes, credentials, onComplete); | |
| 163 }.bind(this); | |
| 164 | |
| 165 function clearSetModes() { | |
| 166 // Reset the password so that any cached references to this.setModes | |
| 167 // will fail. | |
| 168 password = ''; | |
| 169 this.setModes = null; | |
| 170 } | |
| 171 | |
| 172 this.clearAccountPasswordTimeout_ = setTimeout( | |
| 173 clearSetModes.bind(this), this.passwordActiveDurationMs_); | |
| 174 | |
| 175 // Clear stored password state and close the dialog. | |
| 176 this.password_ = ''; | |
| 177 if (this.$.dialog.open) | |
| 178 this.$.dialog.close(); | |
| 179 | |
| 180 this.writeUma_(LockScreenProgress.ENTER_PASSWORD_CORRECTLY); | |
| 181 } | |
| 182 } | |
| 183 | |
| 184 this.checkAccountPassword_(onPasswordChecked.bind(this)); | |
| 185 }, | |
| 186 | |
| 187 /** @private */ | |
| 188 onPasswordChanged_: function() { | |
| 189 this.passwordInvalid_ = false; | |
| 190 }, | |
| 191 | |
| 192 /** @private */ | |
| 193 enableConfirm_: function() { | |
| 194 return !!this.password_ && !this.passwordInvalid_; | |
| 195 }, | |
| 196 | |
| 197 /** | |
| 198 * Helper method that checks if the current password is valid. | |
| 199 * @param {function(boolean):void} onCheck | |
| 200 */ | |
| 201 checkAccountPassword_: function(onCheck) { | |
| 202 // We check the account password by trying to update the active set of quick | |
| 203 // unlock modes without changing any credentials. | |
| 204 this.quickUnlockPrivate_.getActiveModes(function(modes) { | |
| 205 var credentials = | |
| 206 /** @type {!Array<string>} */ (Array(modes.length).fill('')); | |
| 207 this.quickUnlockPrivate_.setModes( | |
| 208 this.password_, modes, credentials, onCheck); | |
| 209 }.bind(this)); | |
| 210 } | |
| 211 }); | |
| 212 | 208 |
| 213 })(); | 209 })(); |
| OLD | NEW |