mirror of
https://github.com/tailscale/tailscale.git
synced 2025-04-23 09:21:41 +00:00
163 lines
5.2 KiB
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
|
||
|
}
|