blob: 1dcfd28ef017f1bbea1b6520b13c0af4f479b158 [file] [log] [blame]
// 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 kioskmode provides ways to set policies for local device accounts
// in a Kiosk mode.
package kioskmode
import (
"context"
"strings"
"time"
"chromiumos/tast/common/policy"
"chromiumos/tast/common/policy/fakedms"
"chromiumos/tast/errors"
"chromiumos/tast/local/chrome"
"chromiumos/tast/local/chrome/uiauto/nodewith"
"chromiumos/tast/local/policyutil"
"chromiumos/tast/local/policyutil/fixtures"
"chromiumos/tast/local/syslog"
"chromiumos/tast/testing"
)
const (
kioskStarting = "Starting kiosk mode"
kioskLaunchSucceeded = "Kiosk launch succeeded"
// logScanTimeout is a timeout for log messages indicating Kiosk startup
// and successful launch to be present. It is set to over a minute as Kiosk
// mode launch varies depending on device. crbug.com/1222136
logScanTimeout = 90 * time.Second
)
var (
// WebKioskAccountID identifier of the web Kiosk application.
WebKioskAccountID = "arbitrary_id_web_kiosk_1"
webKioskAccountType = policy.AccountTypeKioskWebApp
webKioskIconURL = "https://www.google.com"
webKioskTitle = "TastKioskModeSetByPolicyGooglePage"
webKioskURL = "https://www.google.com"
// DeviceLocalAccountInfo uses *string instead of string for internal data
// structure. That is needed since fields in json are marked as omitempty.
webKioskPolicy = policy.DeviceLocalAccountInfo{
AccountID: &WebKioskAccountID,
AccountType: &webKioskAccountType,
WebKioskAppInfo: &policy.WebKioskAppInfo{
Url: &webKioskURL,
Title: &webKioskTitle,
IconUrl: &webKioskIconURL,
}}
// KioskAppAccountID identifier of the Kiosk application.
KioskAppAccountID = "arbitrary_id_store_app_2"
kioskAppAccountType = policy.AccountTypeKioskApp
// kioskAppID pointing to the Printtest app - not listed in the WebStore.
kioskAppID = "aajgmlihcokkalfjbangebcffdoanjfo"
// KioskAppBtnNode node representing this application on the Apps menu on
// the Sign-in screen.
KioskAppBtnNode = nodewith.Name("Simple Printest").ClassName("MenuItemView")
kioskAppPolicy = policy.DeviceLocalAccountInfo{
AccountID: &KioskAppAccountID,
AccountType: &kioskAppAccountType,
KioskAppInfo: &policy.KioskAppInfo{
AppId: &kioskAppID,
}}
// defaultLocalAccountsConfiguration holds default Kiosks accounts
// configuration. Each, when setting public account policies can be
// referred by id: KioskAppAccountID and WebKioskAccountID
defaultLocalAccountsConfiguration = policy.DeviceLocalAccounts{
Val: []policy.DeviceLocalAccountInfo{
kioskAppPolicy,
webKioskPolicy,
},
}
)
// SetDefaultAppPolicies serves DeviceLocalAccounts policy with default
// configuration for Kiosk accounts and triggers refresh of policies. If you
// want to see the effect - restart Chrome instance to see Apps button on the
// Sign-in screen.
// Deprecated: Use kioskmode.New(...) instead
func SetDefaultAppPolicies(ctx context.Context, fdms *fakedms.FakeDMS, cr *chrome.Chrome) error {
// ServerAndRefresh is used instead of ServerAndVerify since Verify part
// uses Autotest private api that returns only Enterprise policies values
// but not device policies values.
return policyutil.ServeAndRefresh(ctx, fdms, cr, []policy.Policy{
&defaultLocalAccountsConfiguration,
})
}
// SetAutolaunch sets all default congifurations for Kiosk accounts and sets
// one of them to autolaunch.
// appID is the Kiosk account ID that will be autolaunched.
// Deprecated: Use kioskmode.New(...) instead
func SetAutolaunch(ctx context.Context, fdms *fakedms.FakeDMS, cr *chrome.Chrome, appID string) error {
return policyutil.ServeAndRefresh(ctx, fdms, cr, []policy.Policy{
&defaultLocalAccountsConfiguration,
&policy.DeviceLocalAccountAutoLoginId{
Val: appID,
},
})
}
// ConfirmKioskStarted uses reader for looking for logs that confirm Kiosk mode
// starting and also successful launch of Kiosk.
// reader Reader instance should be processing all logs filtered for Chrome
// only.
func ConfirmKioskStarted(ctx context.Context, reader *syslog.Reader) error {
// Check that Kiosk starts successfully.
if _, err := reader.Wait(ctx, logScanTimeout,
func(e *syslog.Entry) bool {
return strings.Contains(e.Content, kioskStarting)
},
); err != nil {
return errors.Wrap(err, "failed to verify starting of Kiosk mode")
}
if _, err := reader.Wait(ctx, logScanTimeout,
func(e *syslog.Entry) bool {
return strings.Contains(e.Content, kioskLaunchSucceeded)
},
); err != nil {
return errors.Wrap(err, "failed to verify successful launch of Kiosk mode")
}
return nil
}
// New starts Chrome, sets passed Kiosk related options to policies and
// restarts Chrome. When kioskmode.AutoLaunch() is used, then it auto starts
// given Kiosk application. Alternatively kioskMode.LoadSigninProfileExtension()
// should be used. In that case Chrome is started on Signin screen with Kiosk
// accounts loaded.
// If kioskmode.AutoLaunch() option is used you should defer cleaning and
// refreshing policies policyutil.ServeAndRefresh(ctx, fdms, cr,
// []policy.Policy{}).
func New(ctx context.Context, fdms *fakedms.FakeDMS, opts ...Option) (*chrome.Chrome, error) {
cfg, err := NewConfig(opts)
if err != nil {
return nil, errors.Wrap(err, "failed to process options")
}
if cfg.m.DeviceLocalAccounts == nil {
return nil, errors.Wrap(err, "local device accounts were not set")
}
err = func(ctx context.Context) error {
testing.ContextLog(ctx, "kiosk_mode - starting Chrome to set Kiosk policies")
cr, err := chrome.New(
ctx,
chrome.FakeLogin(chrome.Creds{User: fixtures.Username, Pass: fixtures.Password}), // Required as refreshing policies require test API.
chrome.DMSPolicy(fdms.URL),
chrome.KeepEnrollment(),
)
if err != nil {
return errors.Wrap(err, "failed to start Chrome")
}
// Set local accounts policy.
policies := []policy.Policy{
cfg.m.DeviceLocalAccounts,
}
// Handle the AutoLaunch setup.
if cfg.m.AutoLaunch == true {
policies = append(policies, &policy.DeviceLocalAccountAutoLoginId{
Val: *cfg.m.AutoLaunchKioskAppID,
})
}
// Handle setting device policies.
if cfg.m.ExtraPolicies != nil {
policies = append(policies, cfg.m.ExtraPolicies...)
}
// TODO: handle public account policies
// Update policies.
if err := policyutil.ServeAndRefresh(ctx, fdms, cr, policies); err != nil {
return errors.Wrap(err, "failed to serve and refresh policies")
}
// Close the previous Chrome instance.
defer cr.Close(ctx)
return nil
}(ctx)
if err != nil {
return nil, errors.Wrap(err, "failed preparing Chrome to start with given Kiosk configuration")
}
reader, err := syslog.NewReader(ctx, syslog.Program("chrome"))
if err != nil {
return nil, errors.Wrap(err, "failed to start log reader")
}
defer reader.Close()
var cr *chrome.Chrome
if cfg.m.AutoLaunch {
testing.ContextLog(ctx, "kiosk_mode - starting Chrome in Kiosk mode")
// Restart Chrome. After that Kiosk auto starts.
cr, err = chrome.New(ctx,
chrome.NoLogin(),
chrome.DMSPolicy(fdms.URL),
chrome.KeepEnrollment(),
)
if err != nil {
return nil, errors.Wrap(err, "Chrome restart failed")
}
if err := ConfirmKioskStarted(ctx, reader); err != nil {
cr.Close(ctx)
return nil, errors.Wrap(err, "there was a problem while checking chrome logs for Kiosk related entries")
}
} else {
if cfg.m.SigninExtKey == nil {
return nil, errors.Wrap(err, "signin extension key was not provided. Cannot start Chrome")
}
testing.ContextLog(ctx, "kiosk_mode - starting Chrome on Signin screen with set Kiosk apps")
// Restart Chrome. Chrome stays on Sing-in screen
cr, err = chrome.New(ctx,
chrome.DeferLogin(),
chrome.LoadSigninProfileExtension(*cfg.m.SigninExtKey),
chrome.DMSPolicy(fdms.URL),
chrome.KeepEnrollment(),
)
if err != nil {
return nil, errors.Wrap(err, "Chrome restart failed")
}
}
return cr, nil
}