tailscale/ipn/ipnauth/testutils.go

163 lines
5.2 KiB
Go

// Copyright (c) Tailscale Inc & AUTHORS
// SPDX-License-Identifier: BSD-3-Clause
package ipnauth
import (
"errors"
"runtime"
"tailscale.com/ipn"
"tailscale.com/types/ptr"
)
// TestIdentity is an identity with a predefined UID, Name and access rights.
// It should only be used for testing purposes, and allows external packages
// to test against a specific set of access rights.
type TestIdentity struct {
UID string // UID is an OS-specific user id of the test user.
Name string // Name is the login name of the test user.
DeviceAccess DeviceAccess // DeviceAccess is the test user's access rights on the device.
ProfileAccess ProfileAccess // ProfileAccess is the test user's access rights to Tailscale profiles.
AccessOthersProfiles bool // AccessOthersProfiles indicates whether the test user can access all profiles, regardless of their ownership.
}
var (
// TestAdmin is a test identity that has unrestricted access to the device
// and all Tailscale profiles on it. It should only be used for testing purposes.
TestAdmin = &TestIdentity{
Name: "admin",
DeviceAccess: UnrestrictedDeviceAccess,
ProfileAccess: UnrestrictedProfileAccess,
AccessOthersProfiles: true,
}
)
// NewTestIdentityWithGOOS returns a new test identity for the given GOOS,
// with the specified user name and the isAdmin flag indicating
// whether the user has administrative access on the local machine.
//
// When goos is windows, it returns an identity representing an elevated admin
// or a regular user account on a non-managed non-server environment. Callers
// that require fine-grained control over user's privileges or environment
// should use NewWindowsIdentity instead.
func NewTestIdentityWithGOOS(goos, name string, isAdmin bool) Identity {
if goos == "windows" {
token := &testToken{
SID: ipn.WindowsUserID(name),
Name: name,
Admin: isAdmin,
Elevated: isAdmin,
}
return newWindowsIdentity(token, WindowsEnvironment{})
}
identity := &unixIdentity{goos: goos}
identity.forceForTest.username = ptr.To(name)
identity.forceForTest.isAdmin = ptr.To(isAdmin)
if isAdmin {
identity.forceForTest.uid = ptr.To("0")
} else {
identity.forceForTest.uid = ptr.To("1000")
}
return identity
}
// NewTestIdentity is like NewTestIdentityWithGOOS, but returns a test identity
// for the current platform.
func NewTestIdentity(name string, isAdmin bool) Identity {
return NewTestIdentityWithGOOS(runtime.GOOS, name, isAdmin)
}
// UserID returns t.ID.
func (t *TestIdentity) UserID() ipn.WindowsUserID {
return ipn.WindowsUserID(t.UID)
}
// Username returns t.Name.
func (t *TestIdentity) Username() (string, error) {
return t.Name, nil
}
// CheckAccess reports whether the requested access is allowed or denied
// based on t.DeviceAccess.
func (t *TestIdentity) CheckAccess(requested DeviceAccess) AccessCheckResult {
if requested&t.DeviceAccess == requested {
return AllowAccess()
}
return DenyAccess(errors.New("access denied"))
}
// CheckProfileAccess reports whether the requested profile access is allowed or denied
// based on t.ProfileAccess.
func (t *TestIdentity) CheckProfileAccess(profile ipn.LoginProfileView, prefs ipn.PrefsGetter, requested ProfileAccess) AccessCheckResult {
if !t.AccessOthersProfiles && profile.LocalUserID() != t.UserID() && profile.LocalUserID() != "" {
return DenyAccess(errors.New("the requested profile is owned by another user"))
}
if t.ProfileAccess&requested == requested {
return AllowAccess()
}
return DenyAccess(errors.New("access denied"))
}
// testToken implements WindowsToken and should only be used for testing purposes.
type testToken struct {
SID ipn.WindowsUserID
Name string
Admin, Elevated bool
LocalSystem bool
}
// UID returns t's Security Identifier (SID).
func (t *testToken) UID() (ipn.WindowsUserID, error) {
return t.SID, nil
}
// Username returns t's username.
func (t *testToken) Username() (string, error) {
return t.Name, nil
}
// IsAdministrator reports whether t represents an admin's,
// but not necessarily elevated, security context.
func (t *testToken) IsAdministrator() (bool, error) {
return t.Admin, nil
}
// IsElevated reports whether t represents an elevated security context,
// such as of LocalSystem or "Run as administrator".
func (t *testToken) IsElevated() bool {
return t.Elevated || t.IsLocalSystem()
}
// IsLocalSystem reports whether t represents a LocalSystem's security context.
func (t *testToken) IsLocalSystem() bool {
return t.LocalSystem
}
// UserDir is not implemented.
func (t *testToken) UserDir(folderID string) (string, error) {
return "", errors.New("Not implemented")
}
// Close is a no-op.
func (t *testToken) Close() error {
return nil
}
// EqualUIDs reports whether two WindowsTokens have the same UIDs.
func (t *testToken) EqualUIDs(other WindowsToken) bool {
if t != nil && other == nil || t == nil && other != nil {
return false
}
ot, ok := other.(*testToken)
if !ok {
return false
}
return t == ot || t.SID == ot.SID
}
// IsUID reports whether t has the specified UID.
func (t *testToken) IsUID(uid ipn.WindowsUserID) bool {
return t.SID == uid
}