Commit 56d3d720 authored by Ryan Hansberry's avatar Ryan Hansberry

[CrOS Multidevice] Authenticate user before enabling some features.

Before Smart Lock or Better Together Suite can be enabled, the user is
prompted to confirm their password. The auth token that is generated
by the process is then passed along to MultiDeviceSetupService. For
other features which don't require authentication, simply call out to
MultiDeviceSetupService with no auth token.

This CL always requires Better Together Suite enabling to be an
authenticated request. However, that's really only necessary in the
case that the Smart Lock user pref is enabled. A follow-up CL will
remove this unnecessary authentication requirement; see
crbug.com/876377.

Bug: 870122
Change-Id: I95056000a3dccf27c4d62329d7f04a737f1e1c4a
Reviewed-on: https://chromium-review.googlesource.com/1184025
Commit-Queue: Ryan Hansberry <hansberry@chromium.org>
Reviewed-by: default avatarKyle Horimoto <khorimoto@chromium.org>
Reviewed-by: default avatarHector Carmona <hcarmona@chromium.org>
Cr-Commit-Position: refs/heads/master@{#584948}
parent 0a4a371a
...@@ -60,6 +60,7 @@ js_library("multidevice_page") { ...@@ -60,6 +60,7 @@ js_library("multidevice_page") {
":multidevice_browser_proxy", ":multidevice_browser_proxy",
":multidevice_constants", ":multidevice_constants",
":multidevice_feature_behavior", ":multidevice_feature_behavior",
"../controls:password_prompt_dialog",
"//ui/webui/resources/js:cr", "//ui/webui/resources/js:cr",
] ]
} }
......
...@@ -14,12 +14,12 @@ cr.define('settings', function() { ...@@ -14,12 +14,12 @@ cr.define('settings', function() {
* @param {!settings.MultiDeviceFeature} feature The feature whose state * @param {!settings.MultiDeviceFeature} feature The feature whose state
* should be set. * should be set.
* @param {boolean} enabled Whether the feature should be turned off or on. * @param {boolean} enabled Whether the feature should be turned off or on.
* @param {String} authToken Proof that the user is authenticated. Needed * @param {string=} opt_authToken Proof that the user is authenticated.
* to enable Smart Lock, and Better Together Suite if the Smart Lock * Needed to enable Smart Lock, and Better Together Suite if the Smart
* user pref is enabled. * Lock user pref is enabled.
* @return {!Promise<boolean>} Whether the operation was successful. * @return {!Promise<boolean>} Whether the operation was successful.
*/ */
setFeatureEnabledState(feature, enabled, authToken) {} setFeatureEnabledState(feature, enabled, opt_authToken) {}
retryPendingHostSetup() {} retryPendingHostSetup() {}
} }
...@@ -39,9 +39,9 @@ cr.define('settings', function() { ...@@ -39,9 +39,9 @@ cr.define('settings', function() {
} }
/** @override */ /** @override */
setFeatureEnabledState(feature, enabled, authToken) { setFeatureEnabledState(feature, enabled, opt_authToken) {
return cr.sendWithPromise( return cr.sendWithPromise(
'setFeatureEnabledState', feature, enabled, authToken); 'setFeatureEnabledState', feature, enabled, opt_authToken);
} }
/** @override */ /** @override */
......
<link rel="import" href="chrome://resources/html/polymer.html"> <link rel="import" href="chrome://resources/html/polymer.html">
<link rel="import" href="chrome://resources/cr_elements/cr_toggle/cr_toggle.html"> <link rel="import" href="chrome://resources/cr_elements/cr_toggle/cr_toggle.html">
<link rel="import" href="multidevice_browser_proxy.html">
<link rel="import" href="multidevice_constants.html"> <link rel="import" href="multidevice_constants.html">
<link rel="import" href="multidevice_feature_behavior.html"> <link rel="import" href="multidevice_feature_behavior.html">
......
...@@ -66,9 +66,11 @@ Polymer({ ...@@ -66,9 +66,11 @@ Polymer({
*/ */
onChange_: function() { onChange_: function() {
this.resetChecked_(); this.resetChecked_();
// TODO(hansberry): Do a password check when this.feature is SMART_LOCK or
// BETTER_TOGETHER_SUITE. // Pass the negation of |this.checked_|: this indicates that if the toggle
// TODO(jordynass): Have this request a change in the feature's status via // is checked, the intent is for it to be unchecked, and vice versa.
// the browserProxy. this.fire(
'feature-toggle-clicked',
{feature: this.feature, enabled: !this.checked_});
}, },
}); });
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
<link rel="import" href="chrome://resources/polymer/v1_0/paper-icon-button/paper-icon-button-light.html"> <link rel="import" href="chrome://resources/polymer/v1_0/paper-icon-button/paper-icon-button-light.html">
<link rel="import" href="../i18n_setup.html"> <link rel="import" href="../i18n_setup.html">
<link rel="import" href="../route.html"> <link rel="import" href="../route.html">
<link rel="import" href="../controls/password_prompt_dialog.html">
<link rel="import" href="../settings_page/settings_animated_pages.html"> <link rel="import" href="../settings_page/settings_animated_pages.html">
<link rel="import" href="../settings_page/settings_subpage.html"> <link rel="import" href="../settings_page/settings_subpage.html">
<link rel="import" href="../settings_shared_css.html"> <link rel="import" href="../settings_shared_css.html">
...@@ -64,6 +65,13 @@ ...@@ -64,6 +65,13 @@
</settings-subpage> </settings-subpage>
</template> </template>
</settings-animated-pages> </settings-animated-pages>
<template is="dom-if" if="[[showPasswordPromptDialog_]]" restamp>
<settings-password-prompt-dialog
id="passwordPrompt" on-close="onPasswordPromptDialogClose_"
auth-token="{{authToken_}}">
</settings-password-prompt-dialog>
</template>
</template> </template>
<script src="multidevice_page.js"></script> <script src="multidevice_page.js"></script>
</dom-module> </dom-module>
...@@ -31,6 +31,31 @@ Polymer({ ...@@ -31,6 +31,31 @@ Polymer({
return map; return map;
}, },
}, },
/**
* Authentication token provided by password-prompt-dialog.
* @private {string}
*/
authToken_: {
type: String,
value: '',
observer: 'authTokenChanged_',
},
/**
* The feature which the user wishes to enable, but which requires
* authentication. Once |authToken_| is valid, this value will be passed
* along to |browserProxy_|, and subsequently cleared.
* @private {?settings.MultiDeviceFeature}
*/
attemptedEnabledFeature_: {type: Number, value: null},
/** @private {boolean} */
showPasswordPromptDialog_: {type: Boolean, value: false},
},
listeners: {
'feature-toggle-clicked': 'onFeatureToggleClicked_',
}, },
/** @private {?settings.MultiDeviceBrowserProxy} */ /** @private {?settings.MultiDeviceBrowserProxy} */
...@@ -130,11 +155,89 @@ Polymer({ ...@@ -130,11 +155,89 @@ Polymer({
this.browserProxy_.showMultiDeviceSetupDialog(); this.browserProxy_.showMultiDeviceSetupDialog();
return; return;
case settings.MultiDeviceSettingsMode.HOST_SET_WAITING_FOR_SERVER: case settings.MultiDeviceSettingsMode.HOST_SET_WAITING_FOR_SERVER:
// Intentional fall-through. // Intentional fall-through.
case settings.MultiDeviceSettingsMode.HOST_SET_WAITING_FOR_VERIFICATION: case settings.MultiDeviceSettingsMode.HOST_SET_WAITING_FOR_VERIFICATION:
// If this device is waiting for action on the server or the host // If this device is waiting for action on the server or the host
// device, clicking the button should trigger this action. // device, clicking the button should trigger this action.
this.browserProxy_.retryPendingHostSetup(); this.browserProxy_.retryPendingHostSetup();
} }
}, },
/**
* Tell |this.browserProxy_| to enable |this.attemptedEnabledFeature_|.
* @private
*/
enableAttemptedFeature_: function() {
if (this.attemptedEnabledFeature_ != null) {
this.browserProxy_.setFeatureEnabledState(
this.attemptedEnabledFeature_, true /* enabled */, this.authToken_);
this.attemptedEnabledFeature_ = null;
}
},
/** @private */
openPasswordPromptDialog_: function() {
this.showPasswordPromptDialog_ = true;
},
/** @private */
onPasswordPromptDialogClose_: function() {
this.showPasswordPromptDialog_ = false;
},
/**
* Attempt to enable the provided feature. If not authenticated (i.e.,
* |authToken_| is invalid), display the password prompt to begin the
* authentication process.
*
* @param {!{detail: !Object}} event
* @private
*/
onFeatureToggleClicked_: function(event) {
let feature = event.detail.feature;
let enabled = event.detail.enabled;
// Authentication is never required if disabling a feature; only consider it
// if enabling.
if (enabled) {
// TODO(crbug.com/876436): Actually determine this, when the API is
// available. It's fine to default to true here for now, because it errs
// on the side of more security.
let isSmartLockPrefEnabled = true;
let isPasswordPromptRequired =
(feature === settings.MultiDeviceFeature.SMART_LOCK) ||
(feature === settings.MultiDeviceFeature.BETTER_TOGETHER_SUITE &&
isSmartLockPrefEnabled);
if (isPasswordPromptRequired) {
this.attemptedEnabledFeature_ = feature;
if (this.authToken_) {
this.enableAttemptedFeature_();
} else {
this.openPasswordPromptDialog_();
}
return;
}
}
// No authentication is required.
this.browserProxy_.setFeatureEnabledState(feature, enabled);
},
/**
* Called when the authToken_ changes. If the authToken is valid, that
* indicates the user authenticated successfully. If not, cancel the pending
* attempt to enable attemptedEnabledFeature_.
* @param {String} authToken
* @private
*/
authTokenChanged_: function(authToken) {
if (this.authToken_) {
this.enableAttemptedFeature_();
} else {
this.attemptedEnabledFeature_ = null;
}
},
}); });
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment