blob: 8eea98f54c2d912c06022b99a41998be1b7ee085 [file]
// Copyright 2021 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package loginapi
import (
"context"
"time"
"chromiumos/tast/common/fixture"
"chromiumos/tast/common/policy"
"chromiumos/tast/common/policy/fakedms"
"chromiumos/tast/ctxutil"
"chromiumos/tast/local/chrome"
"chromiumos/tast/local/mgs"
"chromiumos/tast/local/policyutil"
"chromiumos/tast/local/policyutil/fixtures"
"chromiumos/tast/local/session"
"chromiumos/tast/testing"
)
func init() {
testing.AddTest(&testing.Test{
Func: LaunchSharedManagedGuestSession,
LacrosStatus: testing.LacrosVariantUnknown,
Desc: "Test chrome.login.launchSharedManagedGuestSession Extension API",
Contacts: []string{
"jityao@google.com", // Test author
"chromeos-commercial-identity@google.com",
},
Attr: []string{"group:mainline", "informational"},
SoftwareDeps: []string{"chrome"},
Fixture: fixture.FakeDMSEnrolled,
})
}
// LaunchSharedManagedGuestSession shares a lot of code with
// LaunchManagedGuestSession in launch_managed_guest_session.go, but
// b/204177106 is in progress, which would simplify the set up of the test.
// TODO(jityao): Refactor tests after b/204177106 is submitted.
func LaunchSharedManagedGuestSession(ctx context.Context, s *testing.State) {
fdms := s.FixtValue().(*fakedms.FakeDMS)
// Start a Chrome instance that will fetch policies from the FakeDMS.
cr, err := chrome.New(ctx,
chrome.FakeLogin(chrome.Creds{User: fixtures.Username, Pass: fixtures.Password}),
chrome.DMSPolicy(fdms.URL),
chrome.KeepState())
if err != nil {
s.Fatal("Chrome login failed: ", err)
}
defer func(ctx context.Context) {
// Use cr as a reference to close the last started Chrome instance.
if err := cr.Close(ctx); err != nil {
s.Error("Failed to close Chrome connection: ", err)
}
}(ctx)
// Use a shortened context for test operations to reserve time for cleanup.
ctx, cancel := ctxutil.Shorten(ctx, 30*time.Second)
defer cancel()
accountID := "foo@bar.com"
accountType := policy.AccountTypePublicSession
policies := []policy.Policy{
&policy.DeviceLocalAccounts{
Val: []policy.DeviceLocalAccountInfo{
{
AccountID: &accountID,
AccountType: &accountType,
},
},
},
&policy.DeviceLoginScreenExtensions{
Val: []string{mgs.LoginScreenExtensionID},
},
&policy.DeviceRestrictedManagedGuestSessionEnabled{
Val: true,
},
}
pb := policy.NewBlob()
pb.AddPolicies(policies)
pb.AddPublicAccountPolicy(accountID, &policy.ExtensionInstallForcelist{
Val: []string{mgs.InSessionExtensionID},
})
if err := policyutil.ServeBlobAndRefresh(ctx, fdms, cr, pb); err != nil {
s.Fatal("Failed to serve policies: ", err)
}
// Close the previous Chrome instance.
if err := cr.Close(ctx); err != nil {
s.Fatal("Failed to close Chrome connection: ", err)
}
// Restart Chrome, forcing Devtools to be available on the login screen.
cr, err = chrome.New(ctx,
chrome.NoLogin(),
chrome.DMSPolicy(fdms.URL),
chrome.KeepState(),
chrome.ExtraArgs("--force-devtools-available"))
if err != nil {
s.Fatal("Chrome restart failed: ", err)
}
sm, err := session.NewSessionManager(ctx)
if err != nil {
s.Fatal("Failed to connect to session manager: ", err)
}
sw, err := sm.WatchSessionStateChanged(ctx, "started")
if err != nil {
s.Fatal("Failed to watch for D-Bus signals: ", err)
}
defer sw.Close(ctx)
loginScreenBGURL := chrome.ExtensionBackgroundPageURL(mgs.LoginScreenExtensionID)
conn, err := cr.NewConnForTarget(ctx, chrome.MatchTargetURL(loginScreenBGURL))
if err != nil {
s.Fatal("Failed to connect to login screen background page: ", err)
}
defer conn.Close()
// Launch a shared managed guest session.
password := "password"
if err := conn.Call(ctx, nil, `(password) => new Promise((resolve, reject) => {
chrome.login.launchSharedManagedGuestSession(password, () => {
if (chrome.runtime.lastError) {
reject(new Error(chrome.runtime.lastError.message));
return;
}
resolve();
});
})`, password); err != nil {
s.Fatal("Failed to launch shared MGS: ", err)
}
select {
case <-sw.Signals:
// Pass
case <-ctx.Done():
s.Fatal("Timeout before getting SessionStateChanged signal: ", err)
}
inSessionBGURL := chrome.ExtensionBackgroundPageURL(mgs.InSessionExtensionID)
inSessionConn, err := cr.NewConnForTarget(ctx, chrome.MatchTargetURL(inSessionBGURL))
if err != nil {
s.Fatal("Failed to connect to in-session background page: ", err)
}
defer inSessionConn.Close()
// Note that this uses lockManagedGuestSession() since locking an MGS is
// equivalent to locking the shared session.
if err := inSessionConn.Eval(ctx, `new Promise((resolve, reject) => {
chrome.login.lockManagedGuestSession(() => {
if (chrome.runtime.lastError) {
reject(new Error(chrome.runtime.lastError.message));
return;
}
resolve();
});
})`, nil); err != nil {
s.Fatal("Failed to lock session: ", err)
}
// Previous conn is closed since it is a login screen extension which
// closes when the session starts.
conn2, err := cr.NewConnForTarget(ctx, chrome.MatchTargetURL(loginScreenBGURL))
if err != nil {
s.Fatal("Failed to connect to login screen background page on lock screen: ", err)
}
defer conn2.Close()
unlockSessionFunc := `(password) => new Promise((resolve, reject) => {
chrome.login.unlockSharedSession(password, () => {
if (chrome.runtime.lastError) {
reject(new Error(chrome.runtime.lastError.message));
return;
}
resolve();
});
})`
// Attempt unlock with wrong password.
wrongPassword := "wrong password"
if err := conn2.Call(ctx, nil, unlockSessionFunc, wrongPassword); err == nil {
s.Fatal("Unlock unexpectedly succeeded with wrong password")
}
swUnlocked, err := sm.WatchScreenIsUnlocked(ctx)
if err != nil {
s.Fatal("Failed to watch for D-Bus signals: ", err)
}
defer swUnlocked.Close(ctx)
// Unlock with correct password.
if err := conn2.Call(ctx, nil, unlockSessionFunc, password); err != nil {
s.Fatal("Failed to unlock session: ", err)
}
select {
case <-swUnlocked.Signals:
// Pass
case <-ctx.Done():
s.Fatal("Timeout before getting session unlocked signal: ", err)
}
}