blob: 33e7e7308ca6725d5d6b543b0dc07e697817d71b [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 userutil provides functions that help with management of users
package userutil
import (
"context"
"time"
"chromiumos/tast/errors"
"chromiumos/tast/local/chrome"
"chromiumos/tast/local/chrome/browser"
"chromiumos/tast/local/chrome/localstate"
"chromiumos/tast/testing"
)
// CreateUser creates a new session with a given name and password, considering extra options. It immediately
// closes the session, so it should be used only for creating new users, not logging in.
func CreateUser(ctx context.Context, username, password string, extraOpts ...chrome.Option) error {
cr, err := login(ctx, username, password, extraOpts...)
if err != nil {
return errors.Wrap(err, "failed to create new user")
}
cr.Close(ctx)
return nil
}
// CreateDeviceOwner creates a user like the CreateUser function, but before closing the session
// it waits until the user becomes device owner.
func CreateDeviceOwner(ctx context.Context, username, password string, extraOpts ...chrome.Option) error {
cr, err := login(ctx, username, password, extraOpts...)
if err != nil {
return errors.Wrap(err, "failed to create new user")
}
defer cr.Close(ctx)
return WaitForOwnership(ctx, cr)
}
// Login creates a new user session using provided credentials. If the user doesn't exist it creates a new one.
// It always keeps the current state, and doesn't support other options.
func Login(ctx context.Context, username, password string) (*chrome.Chrome, error) {
return login(ctx, username, password, chrome.KeepState())
}
func login(ctx context.Context, username, password string, extraOpts ...chrome.Option) (*chrome.Chrome, error) {
creds := chrome.Creds{User: username, Pass: password}
opts := append([]chrome.Option{chrome.FakeLogin(creds)}, extraOpts...)
return chrome.New(ctx, opts...)
}
// GetKnownEmailsFromLocalState returns a map of users that logged in on the device, based on the LoggedInUsers from the LocalState file.
func GetKnownEmailsFromLocalState() (map[string]bool, error) {
// Local State is a json-like structure, from which we will need only LoggedInUsers field.
type LocalState struct {
Emails []string `json:"LoggedInUsers"`
}
var localState LocalState
if err := localstate.Unmarshal(browser.TypeAsh, &localState); err != nil {
return nil, errors.Wrap(err, "failed to extract Local State")
}
knownEmails := make(map[string]bool)
for _, email := range localState.Emails {
knownEmails[email] = true
}
return knownEmails, nil
}
// WaitForOwnership waits for up to 20 seconds for the current user to become device owner.
// Normally this should take less then a few seconds.
func WaitForOwnership(ctx context.Context, cr *chrome.Chrome) error {
tconn, err := cr.TestAPIConn(ctx)
if err != nil {
return errors.Wrap(err, "creating login test API connection failed")
}
testing.ContextLog(ctx, "Waiting for the user to become device owner")
var pollOpts = &testing.PollOptions{Interval: 1 * time.Second, Timeout: 20 * time.Second}
var status struct {
IsOwner bool
}
return testing.Poll(ctx, func(ctx context.Context) error {
if err := tconn.Eval(ctx, `tast.promisify(chrome.autotestPrivate.loginStatus)()`, &status); err != nil {
// This is caused by failure to run javascript to get login status
// quit polling.
return testing.PollBreak(err)
}
testing.ContextLogf(ctx, "User is owner: %t", status.IsOwner)
if !status.IsOwner {
return errors.New("user did not become device owner yet")
}
return nil
}, pollOpts)
}