Commit 6bacd60e authored by Danan S's avatar Danan S Committed by Chromium LUCI CQ

Add PostMessage bridge method: getTimeDeltaSinceSigninSeconds()

This method will be called by the EDU Coexistence guest flow to
help determine whether or not it should prompt the child's parent for
permission before allowing them to add a secondary EDU account.

The time delta itself is derived from the kOobeOnboardingTime user pref
and the current time.

Also added a test suite for EduCoexistenceController to test this
functionality that can be extended to support more tests for
EduCoexistenceController.

Lastly, added several standard JS primitive return types to the
closure signature for PostMessageAPI method implementations,
which is needed to make this CL pass closure compile type checks.

Bug: 1160024
Change-Id: Ie05f4bccc2db0e3127472937eed51551e864e575
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2596255Reviewed-by: default avatarMichael Giuffrida <michaelpg@chromium.org>
Reviewed-by: default avatarAga Wronska <agawronska@chromium.org>
Commit-Queue: Dan S <danan@chromium.org>
Cr-Commit-Position: refs/heads/master@{#839136}
parent 5b73d37f
......@@ -48,7 +48,7 @@ const MAX_INITIALIZATION_ATTEMPTS = 8;
/**
* Map that stores references to the methods implemented by the API.
* @private {!Map<string, function(!Array): (Promise|Object|undefined)>}
* @private {!Map<string, function(!Array):?>}
*/
this.apiFns_ = new Map();
......@@ -91,8 +91,8 @@ const MAX_INITIALIZATION_ATTEMPTS = 8;
* function.
*
* @param {!string} methodName name of the method to register.
* @param {!function(!Array): (Promise|Object|undefined)} method The function
* to associate with the name.
* @param {!function(!Array):?} method The function to associate with the
* name.
*/
registerMethod(methodName, method) {
this.apiFns_.set(methodName, method);
......
......@@ -10,10 +10,16 @@ import {EduCoexistenceBrowserProxyImpl} from './edu_coexistence_browser_proxy.js
* The methods to expose to the hosted content via the PostMessageAPI.
*/
const METHOD_LIST = [
'consentValid', 'consentLogged', 'requestClose', 'saveGuestFlowState',
'fetchGuestFlowState', 'error'
'consentValid',
'consentLogged',
'requestClose',
'saveGuestFlowState',
'fetchGuestFlowState',
'error',
'getTimeDeltaSinceSigninSeconds',
];
const MILLISECONDS_PER_SECOND = 1000;
/**
* @typedef {{
......@@ -29,6 +35,7 @@ const METHOD_LIST = [
* deviceId: (string),
* email: (string|undefined),
* readOnlyEmail: (string|undefined),
* signinTime: (number),
* }}
*/
export let EduCoexistenceParams;
......@@ -82,6 +89,7 @@ export class EduCoexistenceController extends PostMessageAPIServer {
this.authCompletedReceived_ = false;
this.browserProxy_ = EduCoexistenceBrowserProxyImpl.getInstance();
this.eduCoexistenceAccessToken_ = params.eduCoexistenceAccessToken;
this.signinTime_ = new Date(params.signinTime);
this.webview_.request.onBeforeSendHeaders.addListener(
(details) => {
......@@ -155,6 +163,9 @@ export class EduCoexistenceController extends PostMessageAPIServer {
'fetchGuestFlowState', this.fetchGuestFlowState_.bind(this));
this.registerMethod(
'getEduAccountEmail', this.getEduAccountEmail_.bind(this));
this.registerMethod(
'getTimeDeltaSinceSigninSeconds',
this.getTimeDeltaSinceSigninSeconds_.bind(this));
// Add listeners for Authenticator.
this.addAuthExtHostListeners_();
......@@ -290,4 +301,13 @@ export class EduCoexistenceController extends PostMessageAPIServer {
// Send the error strings to C++ handler so they are logged.
this.browserProxy_.onError(error);
}
/**
* @private
* @return {number} Returns the number of seconds that have elapsed since
* the user's initial signin.
*/
getTimeDeltaSinceSigninSeconds_() {
return (Date.now() - this.signinTime_) / MILLISECONDS_PER_SECOND;
}
}
......@@ -15,10 +15,12 @@
#include "base/guid.h"
#include "base/metrics/histogram_functions.h"
#include "base/system/sys_info.h"
#include "base/time/time.h"
#include "base/values.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/browser_process_platform_part.h"
#include "chrome/browser/chromeos/child_accounts/edu_coexistence_tos_store_utils.h"
#include "chrome/browser/chromeos/login/login_pref_names.h"
#include "chrome/browser/chromeos/policy/user_cloud_policy_manager_chromeos.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/profiles/profile_manager.h"
......@@ -83,14 +85,22 @@ std::string GetSourceUI() {
std::string GetOrCreateEduCoexistenceUserId() {
Profile* profile = ProfileManager::GetActiveUserProfile();
PrefService* pref_service = profile->GetPrefs();
std::string id = pref_service->GetString(chromeos::prefs::kEduCoexistenceId);
std::string id = pref_service->GetString(prefs::kEduCoexistenceId);
if (id.empty()) {
id = base::GenerateGUID();
pref_service->SetString(chromeos::prefs::kEduCoexistenceId, id);
pref_service->SetString(prefs::kEduCoexistenceId, id);
}
return id;
}
base::Time GetSigninTime() {
const Profile* profile = ProfileManager::GetActiveUserProfile();
const PrefService* pref_service = profile->GetPrefs();
base::Time signin_time = pref_service->GetTime(prefs::kOobeOnboardingTime);
DCHECK(!signin_time.is_min());
return signin_time;
}
// Tries to get the policy device id for the family link user profile if
// available. The policy device id is used to identify the device login
// timestamp to skip parental reauth for secondary edu account login during
......@@ -277,6 +287,8 @@ void EduCoexistenceLoginHandler::SendInitializeEduArgs() {
params.SetStringKey("releaseChannel", chrome::GetChannelName());
params.SetStringKey("deviceId", GetDeviceIdForActiveUserProfile());
params.SetDoubleKey("signinTime", GetSigninTime().ToJsTimeIgnoringNull());
// If the secondary edu account is being reauthenticated, the email address
// will be provided via the url of the webcontent. Example
// chrome://chrome-signin/edu-coexistence?email=testuser1%40gmail.com
......
......@@ -61,3 +61,21 @@ TEST_F('EduCoexistenceAppTest', 'DontSwitchViewIfDisplayingError', function() {
this.runMochaTest(
edu_coexistence_app_tests.TestNames.DontSwitchViewIfDisplayingError);
});
var EduCoexistenceControllerTest = class extends EduCoexistenceTest {
/** @override */
get browsePreload() {
return 'chrome://chrome-signin/test_loader.html?module=' +
'chromeos/edu_coexistence/edu_coexistence_controller_test.js';
}
/** @override */
get suiteName() {
return edu_coexistence_controller_tests.suiteName;
}
};
TEST_F('EduCoexistenceControllerTest', 'GetSigninTimeDelta', function() {
this.runMochaTest(
edu_coexistence_controller_tests.TestNames.GetSigninTimeDelta);
});
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import {EduCoexistenceBrowserProxyImpl} from 'chrome://chrome-signin/edu_coexistence_browser_proxy.js';
import {EduCoexistenceController} from 'chrome://chrome-signin/edu_coexistence_controller.js';
import {assert} from 'chrome://resources/js/assert.m.js';
import {flush} from 'chrome://resources/polymer/v3_0/polymer/polymer_bundled.min.js';
import {TestEduCoexistenceBrowserProxy} from './edu_coexistence_test_util.js';
window.edu_coexistence_controller_tests = {};
edu_coexistence_controller_tests.suiteName = 'EduCoexistenceControllerTest';
const FAKE_NOW_MILLISECONDS = 100000;
const FAKE_SIGNIN_TIME_MILLISECONDS = 50000;
/** @enum {string} */
edu_coexistence_controller_tests.TestNames = {
GetSigninTimeDelta: 'Get the correct time delta',
};
suite(edu_coexistence_controller_tests.suiteName, function() {
let appComponent;
let testBrowserProxy;
let eduCoexistenceController;
setup(function() {
testBrowserProxy = new TestEduCoexistenceBrowserProxy();
EduCoexistenceBrowserProxyImpl.instance_ = testBrowserProxy;
testBrowserProxy.setInitializeEduArgsResponse(async function() {
return {
url: 'https://foo.example.com/supervision/coexistence/intro',
hl: 'en-US',
sourceUi: 'oobe',
clientId: 'test-client-id',
clientVersion: ' test-client-version',
eduCoexistenceId: ' test-edu-coexistence-id',
platformVersion: ' test-platform-version',
releaseChannel: 'test-release-channel',
deviceId: 'test-device-id',
signinTime: FAKE_SIGNIN_TIME_MILLISECONDS,
};
});
PolymerTest.clearBody();
// The controller wants an edu-coexistence-ui Polymer component
// as a parameter.
appComponent = document.createElement('edu-coexistence-ui');
document.body.appendChild(appComponent);
flush();
eduCoexistenceController = new EduCoexistenceController(
appComponent, document.createElement('webview'),
getEduCoexistenceParams());
});
test(
assert(edu_coexistence_controller_tests.TestNames.GetSigninTimeDelta),
function() {
// Fake Date.now()
var realDateNow = Date.now;
Date.now = () => {
return FAKE_NOW_MILLISECONDS;
};
const expectedDeltaSeconds =
(FAKE_NOW_MILLISECONDS - FAKE_SIGNIN_TIME_MILLISECONDS) / 1000;
assertEquals(
eduCoexistenceController.getTimeDeltaSinceSigninSeconds_(),
expectedDeltaSeconds);
// Restore original Date.now()
Date.now = realDateNow;
});
});
function getEduCoexistenceParams() {
return {
url: 'https://foo.example.com/supervision/coexistence/intro',
hl: 'en-US',
sourceUi: 'oobe',
clientId: 'test-client-id',
clientVersion: ' test-client-version',
eduCoexistenceId: ' test-edu-coexistence-id',
platformVersion: ' test-platform-version',
releaseChannel: 'test-release-channel',
deviceId: 'test-device-id',
signinTime: FAKE_SIGNIN_TIME_MILLISECONDS,
};
}
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