mirror of
https://github.com/tailscale/tailscale.git
synced 2025-12-23 09:06:24 +00:00
util/syspolicy/*: move syspolicy keys to new const leaf "pkey" package
This is step 1 of ~3, breaking up #14720 into reviewable chunks, with the aim to make syspolicy be a build-time configurable feature. In this first (very noisy) step, all the syspolicy string key constants move to a new constant-only (code-free) package. This will make future steps more reviewable, without this movement noise. There are no code or behavior changes here. The future steps of this series can be seen in #14720: removing global funcs from syspolicy resolution and using an interface that's plumbed around instead. Then adding build tags. Updates #12614 Change-Id: If73bf2c28b9c9b1a408fe868b0b6a25b03eeabd1 Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
This commit is contained in:
committed by
Brad Fitzpatrick
parent
6d45fcfc93
commit
cc532efc20
@@ -174,6 +174,7 @@ tailscale.com/cmd/derper dependencies: (generated by github.com/tailscale/depawa
|
|||||||
tailscale.com/util/syspolicy/internal from tailscale.com/util/syspolicy/setting+
|
tailscale.com/util/syspolicy/internal from tailscale.com/util/syspolicy/setting+
|
||||||
tailscale.com/util/syspolicy/internal/loggerx from tailscale.com/util/syspolicy/internal/metrics+
|
tailscale.com/util/syspolicy/internal/loggerx from tailscale.com/util/syspolicy/internal/metrics+
|
||||||
tailscale.com/util/syspolicy/internal/metrics from tailscale.com/util/syspolicy/source
|
tailscale.com/util/syspolicy/internal/metrics from tailscale.com/util/syspolicy/source
|
||||||
|
tailscale.com/util/syspolicy/pkey from tailscale.com/ipn+
|
||||||
tailscale.com/util/syspolicy/rsop from tailscale.com/util/syspolicy
|
tailscale.com/util/syspolicy/rsop from tailscale.com/util/syspolicy
|
||||||
tailscale.com/util/syspolicy/setting from tailscale.com/util/syspolicy+
|
tailscale.com/util/syspolicy/setting from tailscale.com/util/syspolicy+
|
||||||
tailscale.com/util/syspolicy/source from tailscale.com/util/syspolicy+
|
tailscale.com/util/syspolicy/source from tailscale.com/util/syspolicy+
|
||||||
|
|||||||
@@ -955,6 +955,7 @@ tailscale.com/cmd/k8s-operator dependencies: (generated by github.com/tailscale/
|
|||||||
tailscale.com/util/syspolicy/internal from tailscale.com/util/syspolicy/setting+
|
tailscale.com/util/syspolicy/internal from tailscale.com/util/syspolicy/setting+
|
||||||
tailscale.com/util/syspolicy/internal/loggerx from tailscale.com/util/syspolicy/internal/metrics+
|
tailscale.com/util/syspolicy/internal/loggerx from tailscale.com/util/syspolicy/internal/metrics+
|
||||||
tailscale.com/util/syspolicy/internal/metrics from tailscale.com/util/syspolicy/source
|
tailscale.com/util/syspolicy/internal/metrics from tailscale.com/util/syspolicy/source
|
||||||
|
tailscale.com/util/syspolicy/pkey from tailscale.com/control/controlclient+
|
||||||
tailscale.com/util/syspolicy/rsop from tailscale.com/util/syspolicy+
|
tailscale.com/util/syspolicy/rsop from tailscale.com/util/syspolicy+
|
||||||
tailscale.com/util/syspolicy/setting from tailscale.com/util/syspolicy+
|
tailscale.com/util/syspolicy/setting from tailscale.com/util/syspolicy+
|
||||||
tailscale.com/util/syspolicy/source from tailscale.com/util/syspolicy+
|
tailscale.com/util/syspolicy/source from tailscale.com/util/syspolicy+
|
||||||
|
|||||||
@@ -195,6 +195,7 @@ tailscale.com/cmd/tailscale dependencies: (generated by github.com/tailscale/dep
|
|||||||
tailscale.com/util/syspolicy/internal from tailscale.com/util/syspolicy/setting+
|
tailscale.com/util/syspolicy/internal from tailscale.com/util/syspolicy/setting+
|
||||||
tailscale.com/util/syspolicy/internal/loggerx from tailscale.com/util/syspolicy/internal/metrics+
|
tailscale.com/util/syspolicy/internal/loggerx from tailscale.com/util/syspolicy/internal/metrics+
|
||||||
tailscale.com/util/syspolicy/internal/metrics from tailscale.com/util/syspolicy/source
|
tailscale.com/util/syspolicy/internal/metrics from tailscale.com/util/syspolicy/source
|
||||||
|
tailscale.com/util/syspolicy/pkey from tailscale.com/ipn+
|
||||||
tailscale.com/util/syspolicy/rsop from tailscale.com/util/syspolicy
|
tailscale.com/util/syspolicy/rsop from tailscale.com/util/syspolicy
|
||||||
tailscale.com/util/syspolicy/setting from tailscale.com/util/syspolicy+
|
tailscale.com/util/syspolicy/setting from tailscale.com/util/syspolicy+
|
||||||
tailscale.com/util/syspolicy/source from tailscale.com/util/syspolicy+
|
tailscale.com/util/syspolicy/source from tailscale.com/util/syspolicy+
|
||||||
|
|||||||
@@ -432,6 +432,7 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
|
|||||||
tailscale.com/util/syspolicy/internal from tailscale.com/util/syspolicy/setting+
|
tailscale.com/util/syspolicy/internal from tailscale.com/util/syspolicy/setting+
|
||||||
tailscale.com/util/syspolicy/internal/loggerx from tailscale.com/util/syspolicy/internal/metrics+
|
tailscale.com/util/syspolicy/internal/loggerx from tailscale.com/util/syspolicy/internal/metrics+
|
||||||
tailscale.com/util/syspolicy/internal/metrics from tailscale.com/util/syspolicy/source
|
tailscale.com/util/syspolicy/internal/metrics from tailscale.com/util/syspolicy/source
|
||||||
|
tailscale.com/util/syspolicy/pkey from tailscale.com/cmd/tailscaled+
|
||||||
tailscale.com/util/syspolicy/rsop from tailscale.com/util/syspolicy+
|
tailscale.com/util/syspolicy/rsop from tailscale.com/util/syspolicy+
|
||||||
tailscale.com/util/syspolicy/setting from tailscale.com/util/syspolicy+
|
tailscale.com/util/syspolicy/setting from tailscale.com/util/syspolicy+
|
||||||
tailscale.com/util/syspolicy/source from tailscale.com/util/syspolicy+
|
tailscale.com/util/syspolicy/source from tailscale.com/util/syspolicy+
|
||||||
|
|||||||
@@ -65,6 +65,7 @@ import (
|
|||||||
"tailscale.com/util/multierr"
|
"tailscale.com/util/multierr"
|
||||||
"tailscale.com/util/osshare"
|
"tailscale.com/util/osshare"
|
||||||
"tailscale.com/util/syspolicy"
|
"tailscale.com/util/syspolicy"
|
||||||
|
"tailscale.com/util/syspolicy/pkey"
|
||||||
"tailscale.com/version"
|
"tailscale.com/version"
|
||||||
"tailscale.com/version/distro"
|
"tailscale.com/version/distro"
|
||||||
"tailscale.com/wgengine"
|
"tailscale.com/wgengine"
|
||||||
@@ -1011,6 +1012,6 @@ func defaultEncryptState() bool {
|
|||||||
// (plan9/FreeBSD/etc).
|
// (plan9/FreeBSD/etc).
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
v, _ := syspolicy.GetBoolean(syspolicy.EncryptState, false)
|
v, _ := syspolicy.GetBoolean(pkey.EncryptState, false)
|
||||||
return v
|
return v
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -56,6 +56,7 @@ import (
|
|||||||
"tailscale.com/types/logid"
|
"tailscale.com/types/logid"
|
||||||
"tailscale.com/util/osdiag"
|
"tailscale.com/util/osdiag"
|
||||||
"tailscale.com/util/syspolicy"
|
"tailscale.com/util/syspolicy"
|
||||||
|
"tailscale.com/util/syspolicy/pkey"
|
||||||
"tailscale.com/util/winutil"
|
"tailscale.com/util/winutil"
|
||||||
"tailscale.com/util/winutil/gp"
|
"tailscale.com/util/winutil/gp"
|
||||||
"tailscale.com/version"
|
"tailscale.com/version"
|
||||||
@@ -155,7 +156,7 @@ func runWindowsService(pol *logpolicy.Policy) error {
|
|||||||
|
|
||||||
if syslog, err := eventlog.Open(serviceName); err == nil {
|
if syslog, err := eventlog.Open(serviceName); err == nil {
|
||||||
syslogf = func(format string, args ...any) {
|
syslogf = func(format string, args ...any) {
|
||||||
if logSCMInteractions, _ := syspolicy.GetBoolean(syspolicy.LogSCMInteractions, false); logSCMInteractions {
|
if logSCMInteractions, _ := syspolicy.GetBoolean(pkey.LogSCMInteractions, false); logSCMInteractions {
|
||||||
syslog.Info(0, fmt.Sprintf(format, args...))
|
syslog.Info(0, fmt.Sprintf(format, args...))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -389,8 +390,7 @@ func handleSessionChange(chgRequest svc.ChangeRequest) {
|
|||||||
if chgRequest.Cmd != svc.SessionChange || chgRequest.EventType != windows.WTS_SESSION_UNLOCK {
|
if chgRequest.Cmd != svc.SessionChange || chgRequest.EventType != windows.WTS_SESSION_UNLOCK {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if flushDNSOnSessionUnlock, _ := syspolicy.GetBoolean(pkey.FlushDNSOnSessionUnlock, false); flushDNSOnSessionUnlock {
|
||||||
if flushDNSOnSessionUnlock, _ := syspolicy.GetBoolean(syspolicy.FlushDNSOnSessionUnlock, false); flushDNSOnSessionUnlock {
|
|
||||||
log.Printf("Received WTS_SESSION_UNLOCK event, initiating DNS flush.")
|
log.Printf("Received WTS_SESSION_UNLOCK event, initiating DNS flush.")
|
||||||
go func() {
|
go func() {
|
||||||
err := dns.Flush()
|
err := dns.Flush()
|
||||||
|
|||||||
@@ -384,6 +384,7 @@ tailscale.com/cmd/tsidp dependencies: (generated by github.com/tailscale/depawar
|
|||||||
tailscale.com/util/syspolicy/internal from tailscale.com/util/syspolicy+
|
tailscale.com/util/syspolicy/internal from tailscale.com/util/syspolicy+
|
||||||
tailscale.com/util/syspolicy/internal/loggerx from tailscale.com/util/syspolicy+
|
tailscale.com/util/syspolicy/internal/loggerx from tailscale.com/util/syspolicy+
|
||||||
tailscale.com/util/syspolicy/internal/metrics from tailscale.com/util/syspolicy/source
|
tailscale.com/util/syspolicy/internal/metrics from tailscale.com/util/syspolicy/source
|
||||||
|
tailscale.com/util/syspolicy/pkey from tailscale.com/control/controlclient+
|
||||||
tailscale.com/util/syspolicy/rsop from tailscale.com/ipn/ipnlocal+
|
tailscale.com/util/syspolicy/rsop from tailscale.com/ipn/ipnlocal+
|
||||||
tailscale.com/util/syspolicy/setting from tailscale.com/client/local+
|
tailscale.com/util/syspolicy/setting from tailscale.com/client/local+
|
||||||
tailscale.com/util/syspolicy/source from tailscale.com/util/syspolicy+
|
tailscale.com/util/syspolicy/source from tailscale.com/util/syspolicy+
|
||||||
|
|||||||
@@ -54,6 +54,7 @@ import (
|
|||||||
"tailscale.com/util/multierr"
|
"tailscale.com/util/multierr"
|
||||||
"tailscale.com/util/singleflight"
|
"tailscale.com/util/singleflight"
|
||||||
"tailscale.com/util/syspolicy"
|
"tailscale.com/util/syspolicy"
|
||||||
|
"tailscale.com/util/syspolicy/pkey"
|
||||||
"tailscale.com/util/systemd"
|
"tailscale.com/util/systemd"
|
||||||
"tailscale.com/util/testenv"
|
"tailscale.com/util/testenv"
|
||||||
"tailscale.com/util/zstdframe"
|
"tailscale.com/util/zstdframe"
|
||||||
@@ -616,7 +617,7 @@ func (c *Direct) doLogin(ctx context.Context, opt loginOpt) (mustRegen bool, new
|
|||||||
return regen, opt.URL, nil, err
|
return regen, opt.URL, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
tailnet, err := syspolicy.GetString(syspolicy.Tailnet, "")
|
tailnet, err := syspolicy.GetString(pkey.Tailnet, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.logf("unable to provide Tailnet field in register request. err: %v", err)
|
c.logf("unable to provide Tailnet field in register request. err: %v", err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import (
|
|||||||
"tailscale.com/tailcfg"
|
"tailscale.com/tailcfg"
|
||||||
"tailscale.com/types/key"
|
"tailscale.com/types/key"
|
||||||
"tailscale.com/util/syspolicy"
|
"tailscale.com/util/syspolicy"
|
||||||
|
"tailscale.com/util/syspolicy/pkey"
|
||||||
)
|
)
|
||||||
|
|
||||||
// getMachineCertificateSubject returns the exact name of a Subject that needs
|
// getMachineCertificateSubject returns the exact name of a Subject that needs
|
||||||
@@ -31,7 +32,7 @@ import (
|
|||||||
//
|
//
|
||||||
// Example: "CN=Tailscale Inc Test Root CA,OU=Tailscale Inc Test Certificate Authority,O=Tailscale Inc,ST=ON,C=CA"
|
// Example: "CN=Tailscale Inc Test Root CA,OU=Tailscale Inc Test Certificate Authority,O=Tailscale Inc,ST=ON,C=CA"
|
||||||
func getMachineCertificateSubject() string {
|
func getMachineCertificateSubject() string {
|
||||||
machineCertSubject, _ := syspolicy.GetString(syspolicy.MachineCertificateSubject, "")
|
machineCertSubject, _ := syspolicy.GetString(pkey.MachineCertificateSubject, "")
|
||||||
return machineCertSubject
|
return machineCertSubject
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import (
|
|||||||
"tailscale.com/ipn/ipnext"
|
"tailscale.com/ipn/ipnext"
|
||||||
"tailscale.com/types/logger"
|
"tailscale.com/types/logger"
|
||||||
"tailscale.com/util/syspolicy"
|
"tailscale.com/util/syspolicy"
|
||||||
|
"tailscale.com/util/syspolicy/pkey"
|
||||||
)
|
)
|
||||||
|
|
||||||
// featureName is the name of the feature implemented by this package.
|
// featureName is the name of the feature implemented by this package.
|
||||||
@@ -135,7 +136,7 @@ func (e *desktopSessionsExt) getBackgroundProfile(profiles ipnext.ProfileStore)
|
|||||||
e.mu.Lock()
|
e.mu.Lock()
|
||||||
defer e.mu.Unlock()
|
defer e.mu.Unlock()
|
||||||
|
|
||||||
if alwaysOn, _ := syspolicy.GetBoolean(syspolicy.AlwaysOn, false); !alwaysOn {
|
if alwaysOn, _ := syspolicy.GetBoolean(pkey.AlwaysOn, false); !alwaysOn {
|
||||||
// If the Always-On mode is disabled, there's no background profile
|
// If the Always-On mode is disabled, there's no background profile
|
||||||
// as far as the desktop session extension is concerned.
|
// as far as the desktop session extension is concerned.
|
||||||
return ipn.LoginProfileView{}
|
return ipn.LoginProfileView{}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import (
|
|||||||
"tailscale.com/ipn"
|
"tailscale.com/ipn"
|
||||||
"tailscale.com/tailcfg"
|
"tailscale.com/tailcfg"
|
||||||
"tailscale.com/util/syspolicy"
|
"tailscale.com/util/syspolicy"
|
||||||
|
"tailscale.com/util/syspolicy/pkey"
|
||||||
)
|
)
|
||||||
|
|
||||||
type actorWithPolicyChecks struct{ Actor }
|
type actorWithPolicyChecks struct{ Actor }
|
||||||
@@ -50,10 +51,10 @@ func (a actorWithPolicyChecks) CheckProfileAccess(profile ipn.LoginProfileView,
|
|||||||
// TODO(nickkhyl): unexport it when we move [ipn.Actor] implementations from [ipnserver]
|
// TODO(nickkhyl): unexport it when we move [ipn.Actor] implementations from [ipnserver]
|
||||||
// and corp to this package.
|
// and corp to this package.
|
||||||
func CheckDisconnectPolicy(actor Actor, profile ipn.LoginProfileView, reason string, auditFn AuditLogFunc) error {
|
func CheckDisconnectPolicy(actor Actor, profile ipn.LoginProfileView, reason string, auditFn AuditLogFunc) error {
|
||||||
if alwaysOn, _ := syspolicy.GetBoolean(syspolicy.AlwaysOn, false); !alwaysOn {
|
if alwaysOn, _ := syspolicy.GetBoolean(pkey.AlwaysOn, false); !alwaysOn {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if allowWithReason, _ := syspolicy.GetBoolean(syspolicy.AlwaysOnOverrideWithReason, false); !allowWithReason {
|
if allowWithReason, _ := syspolicy.GetBoolean(pkey.AlwaysOnOverrideWithReason, false); !allowWithReason {
|
||||||
return errors.New("disconnect not allowed: always-on mode is enabled")
|
return errors.New("disconnect not allowed: always-on mode is enabled")
|
||||||
}
|
}
|
||||||
if reason == "" {
|
if reason == "" {
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ import (
|
|||||||
"tailscale.com/util/goroutines"
|
"tailscale.com/util/goroutines"
|
||||||
"tailscale.com/util/set"
|
"tailscale.com/util/set"
|
||||||
"tailscale.com/util/syspolicy"
|
"tailscale.com/util/syspolicy"
|
||||||
|
"tailscale.com/util/syspolicy/pkey"
|
||||||
"tailscale.com/version"
|
"tailscale.com/version"
|
||||||
"tailscale.com/version/distro"
|
"tailscale.com/version/distro"
|
||||||
)
|
)
|
||||||
@@ -342,7 +343,7 @@ func handleC2NPostureIdentityGet(b *LocalBackend, w http.ResponseWriter, r *http
|
|||||||
// this will first check syspolicy, MDM settings like Registry
|
// this will first check syspolicy, MDM settings like Registry
|
||||||
// on Windows or defaults on macOS. If they are not set, it falls
|
// on Windows or defaults on macOS. If they are not set, it falls
|
||||||
// back to the cli-flag, `--posture-checking`.
|
// back to the cli-flag, `--posture-checking`.
|
||||||
choice, err := syspolicy.GetPreferenceOption(syspolicy.PostureChecking)
|
choice, err := syspolicy.GetPreferenceOption(pkey.PostureChecking)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
b.logf(
|
b.logf(
|
||||||
"c2n: failed to read PostureChecking from syspolicy, returning default from CLI: %s; got error: %s",
|
"c2n: failed to read PostureChecking from syspolicy, returning default from CLI: %s; got error: %s",
|
||||||
|
|||||||
@@ -108,6 +108,7 @@ import (
|
|||||||
"tailscale.com/util/set"
|
"tailscale.com/util/set"
|
||||||
"tailscale.com/util/slicesx"
|
"tailscale.com/util/slicesx"
|
||||||
"tailscale.com/util/syspolicy"
|
"tailscale.com/util/syspolicy"
|
||||||
|
"tailscale.com/util/syspolicy/pkey"
|
||||||
"tailscale.com/util/syspolicy/rsop"
|
"tailscale.com/util/syspolicy/rsop"
|
||||||
"tailscale.com/util/systemd"
|
"tailscale.com/util/systemd"
|
||||||
"tailscale.com/util/testenv"
|
"tailscale.com/util/testenv"
|
||||||
@@ -1762,51 +1763,51 @@ func (b *LocalBackend) SetControlClientStatus(c controlclient.Client, st control
|
|||||||
}
|
}
|
||||||
|
|
||||||
type preferencePolicyInfo struct {
|
type preferencePolicyInfo struct {
|
||||||
key syspolicy.Key
|
key pkey.Key
|
||||||
get func(ipn.PrefsView) bool
|
get func(ipn.PrefsView) bool
|
||||||
set func(*ipn.Prefs, bool)
|
set func(*ipn.Prefs, bool)
|
||||||
}
|
}
|
||||||
|
|
||||||
var preferencePolicies = []preferencePolicyInfo{
|
var preferencePolicies = []preferencePolicyInfo{
|
||||||
{
|
{
|
||||||
key: syspolicy.EnableIncomingConnections,
|
key: pkey.EnableIncomingConnections,
|
||||||
// Allow Incoming (used by the UI) is the negation of ShieldsUp (used by the
|
// Allow Incoming (used by the UI) is the negation of ShieldsUp (used by the
|
||||||
// backend), so this has to convert between the two conventions.
|
// backend), so this has to convert between the two conventions.
|
||||||
get: func(p ipn.PrefsView) bool { return !p.ShieldsUp() },
|
get: func(p ipn.PrefsView) bool { return !p.ShieldsUp() },
|
||||||
set: func(p *ipn.Prefs, v bool) { p.ShieldsUp = !v },
|
set: func(p *ipn.Prefs, v bool) { p.ShieldsUp = !v },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: syspolicy.EnableServerMode,
|
key: pkey.EnableServerMode,
|
||||||
get: func(p ipn.PrefsView) bool { return p.ForceDaemon() },
|
get: func(p ipn.PrefsView) bool { return p.ForceDaemon() },
|
||||||
set: func(p *ipn.Prefs, v bool) { p.ForceDaemon = v },
|
set: func(p *ipn.Prefs, v bool) { p.ForceDaemon = v },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: syspolicy.ExitNodeAllowLANAccess,
|
key: pkey.ExitNodeAllowLANAccess,
|
||||||
get: func(p ipn.PrefsView) bool { return p.ExitNodeAllowLANAccess() },
|
get: func(p ipn.PrefsView) bool { return p.ExitNodeAllowLANAccess() },
|
||||||
set: func(p *ipn.Prefs, v bool) { p.ExitNodeAllowLANAccess = v },
|
set: func(p *ipn.Prefs, v bool) { p.ExitNodeAllowLANAccess = v },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: syspolicy.EnableTailscaleDNS,
|
key: pkey.EnableTailscaleDNS,
|
||||||
get: func(p ipn.PrefsView) bool { return p.CorpDNS() },
|
get: func(p ipn.PrefsView) bool { return p.CorpDNS() },
|
||||||
set: func(p *ipn.Prefs, v bool) { p.CorpDNS = v },
|
set: func(p *ipn.Prefs, v bool) { p.CorpDNS = v },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: syspolicy.EnableTailscaleSubnets,
|
key: pkey.EnableTailscaleSubnets,
|
||||||
get: func(p ipn.PrefsView) bool { return p.RouteAll() },
|
get: func(p ipn.PrefsView) bool { return p.RouteAll() },
|
||||||
set: func(p *ipn.Prefs, v bool) { p.RouteAll = v },
|
set: func(p *ipn.Prefs, v bool) { p.RouteAll = v },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: syspolicy.CheckUpdates,
|
key: pkey.CheckUpdates,
|
||||||
get: func(p ipn.PrefsView) bool { return p.AutoUpdate().Check },
|
get: func(p ipn.PrefsView) bool { return p.AutoUpdate().Check },
|
||||||
set: func(p *ipn.Prefs, v bool) { p.AutoUpdate.Check = v },
|
set: func(p *ipn.Prefs, v bool) { p.AutoUpdate.Check = v },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: syspolicy.ApplyUpdates,
|
key: pkey.ApplyUpdates,
|
||||||
get: func(p ipn.PrefsView) bool { v, _ := p.AutoUpdate().Apply.Get(); return v },
|
get: func(p ipn.PrefsView) bool { v, _ := p.AutoUpdate().Apply.Get(); return v },
|
||||||
set: func(p *ipn.Prefs, v bool) { p.AutoUpdate.Apply.Set(v) },
|
set: func(p *ipn.Prefs, v bool) { p.AutoUpdate.Apply.Set(v) },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: syspolicy.EnableRunExitNode,
|
key: pkey.EnableRunExitNode,
|
||||||
get: func(p ipn.PrefsView) bool { return p.AdvertisesExitNode() },
|
get: func(p ipn.PrefsView) bool { return p.AdvertisesExitNode() },
|
||||||
set: func(p *ipn.Prefs, v bool) { p.SetAdvertiseExitNode(v) },
|
set: func(p *ipn.Prefs, v bool) { p.SetAdvertiseExitNode(v) },
|
||||||
},
|
},
|
||||||
@@ -1817,13 +1818,13 @@ var preferencePolicies = []preferencePolicyInfo{
|
|||||||
//
|
//
|
||||||
// b.mu must be held.
|
// b.mu must be held.
|
||||||
func (b *LocalBackend) applySysPolicyLocked(prefs *ipn.Prefs) (anyChange bool) {
|
func (b *LocalBackend) applySysPolicyLocked(prefs *ipn.Prefs) (anyChange bool) {
|
||||||
if controlURL, err := syspolicy.GetString(syspolicy.ControlURL, prefs.ControlURL); err == nil && prefs.ControlURL != controlURL {
|
if controlURL, err := syspolicy.GetString(pkey.ControlURL, prefs.ControlURL); err == nil && prefs.ControlURL != controlURL {
|
||||||
prefs.ControlURL = controlURL
|
prefs.ControlURL = controlURL
|
||||||
anyChange = true
|
anyChange = true
|
||||||
}
|
}
|
||||||
|
|
||||||
const sentinel = "HostnameDefaultValue"
|
const sentinel = "HostnameDefaultValue"
|
||||||
hostnameFromPolicy, _ := syspolicy.GetString(syspolicy.Hostname, sentinel)
|
hostnameFromPolicy, _ := syspolicy.GetString(pkey.Hostname, sentinel)
|
||||||
switch hostnameFromPolicy {
|
switch hostnameFromPolicy {
|
||||||
case sentinel:
|
case sentinel:
|
||||||
// An empty string for this policy value means that the admin wants to delete
|
// An empty string for this policy value means that the admin wants to delete
|
||||||
@@ -1858,7 +1859,7 @@ func (b *LocalBackend) applySysPolicyLocked(prefs *ipn.Prefs) (anyChange bool) {
|
|||||||
anyChange = true
|
anyChange = true
|
||||||
}
|
}
|
||||||
|
|
||||||
if alwaysOn, _ := syspolicy.GetBoolean(syspolicy.AlwaysOn, false); alwaysOn && !b.overrideAlwaysOn && !prefs.WantRunning {
|
if alwaysOn, _ := syspolicy.GetBoolean(pkey.AlwaysOn, false); alwaysOn && !b.overrideAlwaysOn && !prefs.WantRunning {
|
||||||
prefs.WantRunning = true
|
prefs.WantRunning = true
|
||||||
anyChange = true
|
anyChange = true
|
||||||
}
|
}
|
||||||
@@ -1882,7 +1883,7 @@ func (b *LocalBackend) applySysPolicyLocked(prefs *ipn.Prefs) (anyChange bool) {
|
|||||||
//
|
//
|
||||||
// b.mu must be held.
|
// b.mu must be held.
|
||||||
func (b *LocalBackend) applyExitNodeSysPolicyLocked(prefs *ipn.Prefs) (anyChange bool) {
|
func (b *LocalBackend) applyExitNodeSysPolicyLocked(prefs *ipn.Prefs) (anyChange bool) {
|
||||||
if exitNodeIDStr, _ := syspolicy.GetString(syspolicy.ExitNodeID, ""); exitNodeIDStr != "" {
|
if exitNodeIDStr, _ := syspolicy.GetString(pkey.ExitNodeID, ""); exitNodeIDStr != "" {
|
||||||
exitNodeID := tailcfg.StableNodeID(exitNodeIDStr)
|
exitNodeID := tailcfg.StableNodeID(exitNodeIDStr)
|
||||||
|
|
||||||
// Try to parse the policy setting value as an "auto:"-prefixed [ipn.ExitNodeExpression],
|
// Try to parse the policy setting value as an "auto:"-prefixed [ipn.ExitNodeExpression],
|
||||||
@@ -1923,7 +1924,7 @@ func (b *LocalBackend) applyExitNodeSysPolicyLocked(prefs *ipn.Prefs) (anyChange
|
|||||||
prefs.ExitNodeIP = netip.Addr{}
|
prefs.ExitNodeIP = netip.Addr{}
|
||||||
anyChange = true
|
anyChange = true
|
||||||
}
|
}
|
||||||
} else if exitNodeIPStr, _ := syspolicy.GetString(syspolicy.ExitNodeIP, ""); exitNodeIPStr != "" {
|
} else if exitNodeIPStr, _ := syspolicy.GetString(pkey.ExitNodeIP, ""); exitNodeIPStr != "" {
|
||||||
if prefs.AutoExitNode != "" {
|
if prefs.AutoExitNode != "" {
|
||||||
prefs.AutoExitNode = "" // mutually exclusive with ExitNodeIP
|
prefs.AutoExitNode = "" // mutually exclusive with ExitNodeIP
|
||||||
anyChange = true
|
anyChange = true
|
||||||
@@ -1970,7 +1971,7 @@ func (b *LocalBackend) reconcilePrefs() (_ ipn.PrefsView, anyChange bool) {
|
|||||||
// sysPolicyChanged is a callback triggered by syspolicy when it detects
|
// sysPolicyChanged is a callback triggered by syspolicy when it detects
|
||||||
// a change in one or more syspolicy settings.
|
// a change in one or more syspolicy settings.
|
||||||
func (b *LocalBackend) sysPolicyChanged(policy *rsop.PolicyChange) {
|
func (b *LocalBackend) sysPolicyChanged(policy *rsop.PolicyChange) {
|
||||||
if policy.HasChangedAnyOf(syspolicy.AlwaysOn, syspolicy.AlwaysOnOverrideWithReason) {
|
if policy.HasChangedAnyOf(pkey.AlwaysOn, pkey.AlwaysOnOverrideWithReason) {
|
||||||
// If the AlwaysOn or the AlwaysOnOverrideWithReason policy has changed,
|
// If the AlwaysOn or the AlwaysOnOverrideWithReason policy has changed,
|
||||||
// we should reset the overrideAlwaysOn flag, as the override might
|
// we should reset the overrideAlwaysOn flag, as the override might
|
||||||
// no longer be valid.
|
// no longer be valid.
|
||||||
@@ -1979,7 +1980,7 @@ func (b *LocalBackend) sysPolicyChanged(policy *rsop.PolicyChange) {
|
|||||||
b.mu.Unlock()
|
b.mu.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
if policy.HasChangedAnyOf(syspolicy.ExitNodeID, syspolicy.ExitNodeIP, syspolicy.AllowExitNodeOverride) {
|
if policy.HasChangedAnyOf(pkey.ExitNodeID, pkey.ExitNodeIP, pkey.AllowExitNodeOverride) {
|
||||||
// Reset the exit node override if a policy that enforces exit node usage
|
// Reset the exit node override if a policy that enforces exit node usage
|
||||||
// or allows the user to override automatic exit node selection has changed.
|
// or allows the user to override automatic exit node selection has changed.
|
||||||
b.mu.Lock()
|
b.mu.Lock()
|
||||||
@@ -1987,7 +1988,7 @@ func (b *LocalBackend) sysPolicyChanged(policy *rsop.PolicyChange) {
|
|||||||
b.mu.Unlock()
|
b.mu.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
if policy.HasChanged(syspolicy.AllowedSuggestedExitNodes) {
|
if policy.HasChanged(pkey.AllowedSuggestedExitNodes) {
|
||||||
b.refreshAllowedSuggestions()
|
b.refreshAllowedSuggestions()
|
||||||
// Re-evaluate exit node suggestion now that the policy setting has changed.
|
// Re-evaluate exit node suggestion now that the policy setting has changed.
|
||||||
if _, err := b.SuggestExitNode(); err != nil && !errors.Is(err, ErrNoPreferredDERP) {
|
if _, err := b.SuggestExitNode(); err != nil && !errors.Is(err, ErrNoPreferredDERP) {
|
||||||
@@ -2348,7 +2349,7 @@ func (b *LocalBackend) Start(opts ipn.Options) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if b.state != ipn.Running && b.conf == nil && opts.AuthKey == "" {
|
if b.state != ipn.Running && b.conf == nil && opts.AuthKey == "" {
|
||||||
sysak, _ := syspolicy.GetString(syspolicy.AuthKey, "")
|
sysak, _ := syspolicy.GetString(pkey.AuthKey, "")
|
||||||
if sysak != "" {
|
if sysak != "" {
|
||||||
b.logf("Start: setting opts.AuthKey by syspolicy, len=%v", len(sysak))
|
b.logf("Start: setting opts.AuthKey by syspolicy, len=%v", len(sysak))
|
||||||
opts.AuthKey = strings.TrimSpace(sysak)
|
opts.AuthKey = strings.TrimSpace(sysak)
|
||||||
@@ -4407,7 +4408,7 @@ func (b *LocalBackend) checkEditPrefsAccessLocked(actor ipnauth.Actor, prefs ipn
|
|||||||
// Prevent users from changing exit node preferences
|
// Prevent users from changing exit node preferences
|
||||||
// when exit node usage is managed by policy.
|
// when exit node usage is managed by policy.
|
||||||
if mp.ExitNodeIDSet || mp.ExitNodeIPSet || mp.AutoExitNodeSet {
|
if mp.ExitNodeIDSet || mp.ExitNodeIPSet || mp.AutoExitNodeSet {
|
||||||
isManaged, err := syspolicy.HasAnyOf(syspolicy.ExitNodeID, syspolicy.ExitNodeIP)
|
isManaged, err := syspolicy.HasAnyOf(pkey.ExitNodeID, pkey.ExitNodeIP)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = fmt.Errorf("policy check failed: %w", err)
|
err = fmt.Errorf("policy check failed: %w", err)
|
||||||
} else if isManaged {
|
} else if isManaged {
|
||||||
@@ -4415,7 +4416,7 @@ func (b *LocalBackend) checkEditPrefsAccessLocked(actor ipnauth.Actor, prefs ipn
|
|||||||
// if permitted by [syspolicy.AllowExitNodeOverride].
|
// if permitted by [syspolicy.AllowExitNodeOverride].
|
||||||
//
|
//
|
||||||
// Disabling exit node usage entirely is not allowed.
|
// Disabling exit node usage entirely is not allowed.
|
||||||
allowExitNodeOverride, _ := syspolicy.GetBoolean(syspolicy.AllowExitNodeOverride, false)
|
allowExitNodeOverride, _ := syspolicy.GetBoolean(pkey.AllowExitNodeOverride, false)
|
||||||
if !allowExitNodeOverride || b.changeDisablesExitNodeLocked(prefs, mp) {
|
if !allowExitNodeOverride || b.changeDisablesExitNodeLocked(prefs, mp) {
|
||||||
err = errManagedByPolicy
|
err = errManagedByPolicy
|
||||||
}
|
}
|
||||||
@@ -4519,7 +4520,7 @@ func (b *LocalBackend) onEditPrefsLocked(_ ipnauth.Actor, mp *ipn.MaskedPrefs, o
|
|||||||
// mode on them until the policy changes, they switch to a different profile, etc.
|
// mode on them until the policy changes, they switch to a different profile, etc.
|
||||||
b.overrideAlwaysOn = true
|
b.overrideAlwaysOn = true
|
||||||
|
|
||||||
if reconnectAfter, _ := syspolicy.GetDuration(syspolicy.ReconnectAfter, 0); reconnectAfter > 0 {
|
if reconnectAfter, _ := syspolicy.GetDuration(pkey.ReconnectAfter, 0); reconnectAfter > 0 {
|
||||||
b.startReconnectTimerLocked(reconnectAfter)
|
b.startReconnectTimerLocked(reconnectAfter)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4530,7 +4531,7 @@ func (b *LocalBackend) onEditPrefsLocked(_ ipnauth.Actor, mp *ipn.MaskedPrefs, o
|
|||||||
b.overrideExitNodePolicy = false
|
b.overrideExitNodePolicy = false
|
||||||
}
|
}
|
||||||
if mp.AutoExitNodeSet || mp.ExitNodeIDSet || mp.ExitNodeIPSet {
|
if mp.AutoExitNodeSet || mp.ExitNodeIDSet || mp.ExitNodeIPSet {
|
||||||
if allowExitNodeOverride, _ := syspolicy.GetBoolean(syspolicy.AllowExitNodeOverride, false); allowExitNodeOverride {
|
if allowExitNodeOverride, _ := syspolicy.GetBoolean(pkey.AllowExitNodeOverride, false); allowExitNodeOverride {
|
||||||
// If applying exit node policy settings to the new prefs results in no change,
|
// If applying exit node policy settings to the new prefs results in no change,
|
||||||
// the user is not overriding the policy. Otherwise, it is an override.
|
// the user is not overriding the policy. Otherwise, it is an override.
|
||||||
b.overrideExitNodePolicy = b.applyExitNodeSysPolicyLocked(newPrefs.AsStruct())
|
b.overrideExitNodePolicy = b.applyExitNodeSysPolicyLocked(newPrefs.AsStruct())
|
||||||
@@ -7807,9 +7808,9 @@ type selectRegionFunc func(views.Slice[int]) int
|
|||||||
type selectNodeFunc func(nodes views.Slice[tailcfg.NodeView], last tailcfg.StableNodeID) tailcfg.NodeView
|
type selectNodeFunc func(nodes views.Slice[tailcfg.NodeView], last tailcfg.StableNodeID) tailcfg.NodeView
|
||||||
|
|
||||||
func fillAllowedSuggestions() set.Set[tailcfg.StableNodeID] {
|
func fillAllowedSuggestions() set.Set[tailcfg.StableNodeID] {
|
||||||
nodes, err := syspolicy.GetStringArray(syspolicy.AllowedSuggestedExitNodes, nil)
|
nodes, err := syspolicy.GetStringArray(pkey.AllowedSuggestedExitNodes, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("fillAllowedSuggestions: unable to look up %q policy: %v", syspolicy.AllowedSuggestedExitNodes, err)
|
log.Printf("fillAllowedSuggestions: unable to look up %q policy: %v", pkey.AllowedSuggestedExitNodes, err)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if nodes == nil {
|
if nodes == nil {
|
||||||
@@ -8176,7 +8177,7 @@ func isAllowedAutoExitNodeID(exitNodeID tailcfg.StableNodeID) bool {
|
|||||||
if exitNodeID == "" {
|
if exitNodeID == "" {
|
||||||
return false // an exit node is required
|
return false // an exit node is required
|
||||||
}
|
}
|
||||||
if nodes, _ := syspolicy.GetStringArray(syspolicy.AllowedSuggestedExitNodes, nil); nodes != nil {
|
if nodes, _ := syspolicy.GetStringArray(pkey.AllowedSuggestedExitNodes, nil); nodes != nil {
|
||||||
return slices.Contains(nodes, string(exitNodeID))
|
return slices.Contains(nodes, string(exitNodeID))
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -8339,7 +8340,7 @@ func (b *LocalBackend) stateEncrypted() opt.Bool {
|
|||||||
// the Keychain. A future release will clean up the on-disk state
|
// the Keychain. A future release will clean up the on-disk state
|
||||||
// files.
|
// files.
|
||||||
// TODO(#15830): always return true here once MacSys is fully migrated.
|
// TODO(#15830): always return true here once MacSys is fully migrated.
|
||||||
sp, _ := syspolicy.GetBoolean(syspolicy.EncryptState, false)
|
sp, _ := syspolicy.GetBoolean(pkey.EncryptState, false)
|
||||||
return opt.NewBool(sp)
|
return opt.NewBool(sp)
|
||||||
default:
|
default:
|
||||||
// Probably self-compiled tailscaled, we don't use the Keychain
|
// Probably self-compiled tailscaled, we don't use the Keychain
|
||||||
|
|||||||
@@ -62,6 +62,7 @@ import (
|
|||||||
"tailscale.com/util/must"
|
"tailscale.com/util/must"
|
||||||
"tailscale.com/util/set"
|
"tailscale.com/util/set"
|
||||||
"tailscale.com/util/syspolicy"
|
"tailscale.com/util/syspolicy"
|
||||||
|
"tailscale.com/util/syspolicy/pkey"
|
||||||
"tailscale.com/util/syspolicy/setting"
|
"tailscale.com/util/syspolicy/setting"
|
||||||
"tailscale.com/util/syspolicy/source"
|
"tailscale.com/util/syspolicy/source"
|
||||||
"tailscale.com/wgengine"
|
"tailscale.com/wgengine"
|
||||||
@@ -1182,16 +1183,16 @@ func TestConfigureExitNode(t *testing.T) {
|
|||||||
// Configure policy settings, if any.
|
// Configure policy settings, if any.
|
||||||
store := source.NewTestStore(t)
|
store := source.NewTestStore(t)
|
||||||
if tt.exitNodeIDPolicy != nil {
|
if tt.exitNodeIDPolicy != nil {
|
||||||
store.SetStrings(source.TestSettingOf(syspolicy.ExitNodeID, string(*tt.exitNodeIDPolicy)))
|
store.SetStrings(source.TestSettingOf(pkey.ExitNodeID, string(*tt.exitNodeIDPolicy)))
|
||||||
}
|
}
|
||||||
if tt.exitNodeIPPolicy != nil {
|
if tt.exitNodeIPPolicy != nil {
|
||||||
store.SetStrings(source.TestSettingOf(syspolicy.ExitNodeIP, tt.exitNodeIPPolicy.String()))
|
store.SetStrings(source.TestSettingOf(pkey.ExitNodeIP, tt.exitNodeIPPolicy.String()))
|
||||||
}
|
}
|
||||||
if tt.exitNodeAllowedIDs != nil {
|
if tt.exitNodeAllowedIDs != nil {
|
||||||
store.SetStringLists(source.TestSettingOf(syspolicy.AllowedSuggestedExitNodes, toStrings(tt.exitNodeAllowedIDs)))
|
store.SetStringLists(source.TestSettingOf(pkey.AllowedSuggestedExitNodes, toStrings(tt.exitNodeAllowedIDs)))
|
||||||
}
|
}
|
||||||
if tt.exitNodeAllowOverride {
|
if tt.exitNodeAllowOverride {
|
||||||
store.SetBooleans(source.TestSettingOf(syspolicy.AllowExitNodeOverride, true))
|
store.SetBooleans(source.TestSettingOf(pkey.AllowExitNodeOverride, true))
|
||||||
}
|
}
|
||||||
if store.IsEmpty() {
|
if store.IsEmpty() {
|
||||||
// No syspolicy settings, so don't register a store.
|
// No syspolicy settings, so don't register a store.
|
||||||
@@ -2890,10 +2891,10 @@ func TestSetExitNodeIDPolicy(t *testing.T) {
|
|||||||
|
|
||||||
policyStore := source.NewTestStore(t)
|
policyStore := source.NewTestStore(t)
|
||||||
if test.exitNodeIDKey {
|
if test.exitNodeIDKey {
|
||||||
policyStore.SetStrings(source.TestSettingOf(syspolicy.ExitNodeID, test.exitNodeID))
|
policyStore.SetStrings(source.TestSettingOf(pkey.ExitNodeID, test.exitNodeID))
|
||||||
}
|
}
|
||||||
if test.exitNodeIPKey {
|
if test.exitNodeIPKey {
|
||||||
policyStore.SetStrings(source.TestSettingOf(syspolicy.ExitNodeIP, test.exitNodeIP))
|
policyStore.SetStrings(source.TestSettingOf(pkey.ExitNodeIP, test.exitNodeIP))
|
||||||
}
|
}
|
||||||
syspolicy.MustRegisterStoreForTest(t, "TestStore", setting.DeviceScope, policyStore)
|
syspolicy.MustRegisterStoreForTest(t, "TestStore", setting.DeviceScope, policyStore)
|
||||||
|
|
||||||
@@ -3029,7 +3030,7 @@ func TestUpdateNetmapDeltaAutoExitNode(t *testing.T) {
|
|||||||
|
|
||||||
syspolicy.RegisterWellKnownSettingsForTest(t)
|
syspolicy.RegisterWellKnownSettingsForTest(t)
|
||||||
policyStore := source.NewTestStoreOf(t, source.TestSettingOf(
|
policyStore := source.NewTestStoreOf(t, source.TestSettingOf(
|
||||||
syspolicy.ExitNodeID, "auto:any",
|
pkey.ExitNodeID, "auto:any",
|
||||||
))
|
))
|
||||||
syspolicy.MustRegisterStoreForTest(t, "TestStore", setting.DeviceScope, policyStore)
|
syspolicy.MustRegisterStoreForTest(t, "TestStore", setting.DeviceScope, policyStore)
|
||||||
|
|
||||||
@@ -3114,7 +3115,7 @@ func TestAutoExitNodeSetNetInfoCallback(t *testing.T) {
|
|||||||
b.cc = cc
|
b.cc = cc
|
||||||
syspolicy.RegisterWellKnownSettingsForTest(t)
|
syspolicy.RegisterWellKnownSettingsForTest(t)
|
||||||
policyStore := source.NewTestStoreOf(t, source.TestSettingOf(
|
policyStore := source.NewTestStoreOf(t, source.TestSettingOf(
|
||||||
syspolicy.ExitNodeID, "auto:any",
|
pkey.ExitNodeID, "auto:any",
|
||||||
))
|
))
|
||||||
syspolicy.MustRegisterStoreForTest(t, "TestStore", setting.DeviceScope, policyStore)
|
syspolicy.MustRegisterStoreForTest(t, "TestStore", setting.DeviceScope, policyStore)
|
||||||
peer1 := makePeer(1, withCap(26), withDERP(3), withSuggest(), withExitRoutes())
|
peer1 := makePeer(1, withCap(26), withDERP(3), withSuggest(), withExitRoutes())
|
||||||
@@ -3223,7 +3224,7 @@ func TestSetControlClientStatusAutoExitNode(t *testing.T) {
|
|||||||
b := newTestLocalBackend(t)
|
b := newTestLocalBackend(t)
|
||||||
syspolicy.RegisterWellKnownSettingsForTest(t)
|
syspolicy.RegisterWellKnownSettingsForTest(t)
|
||||||
policyStore := source.NewTestStoreOf(t, source.TestSettingOf(
|
policyStore := source.NewTestStoreOf(t, source.TestSettingOf(
|
||||||
syspolicy.ExitNodeID, "auto:any",
|
pkey.ExitNodeID, "auto:any",
|
||||||
))
|
))
|
||||||
syspolicy.MustRegisterStoreForTest(t, "TestStore", setting.DeviceScope, policyStore)
|
syspolicy.MustRegisterStoreForTest(t, "TestStore", setting.DeviceScope, policyStore)
|
||||||
b.currentNode().SetNetMap(nm)
|
b.currentNode().SetNetMap(nm)
|
||||||
@@ -3255,7 +3256,7 @@ func TestApplySysPolicy(t *testing.T) {
|
|||||||
prefs ipn.Prefs
|
prefs ipn.Prefs
|
||||||
wantPrefs ipn.Prefs
|
wantPrefs ipn.Prefs
|
||||||
wantAnyChange bool
|
wantAnyChange bool
|
||||||
stringPolicies map[syspolicy.Key]string
|
stringPolicies map[pkey.Key]string
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "empty prefs without policies",
|
name: "empty prefs without policies",
|
||||||
@@ -3290,13 +3291,13 @@ func TestApplySysPolicy(t *testing.T) {
|
|||||||
RouteAll: true,
|
RouteAll: true,
|
||||||
},
|
},
|
||||||
wantAnyChange: true,
|
wantAnyChange: true,
|
||||||
stringPolicies: map[syspolicy.Key]string{
|
stringPolicies: map[pkey.Key]string{
|
||||||
syspolicy.ControlURL: "1",
|
pkey.ControlURL: "1",
|
||||||
syspolicy.EnableIncomingConnections: "never",
|
pkey.EnableIncomingConnections: "never",
|
||||||
syspolicy.EnableServerMode: "always",
|
pkey.EnableServerMode: "always",
|
||||||
syspolicy.ExitNodeAllowLANAccess: "always",
|
pkey.ExitNodeAllowLANAccess: "always",
|
||||||
syspolicy.EnableTailscaleDNS: "always",
|
pkey.EnableTailscaleDNS: "always",
|
||||||
syspolicy.EnableTailscaleSubnets: "always",
|
pkey.EnableTailscaleSubnets: "always",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -3311,13 +3312,13 @@ func TestApplySysPolicy(t *testing.T) {
|
|||||||
ShieldsUp: true,
|
ShieldsUp: true,
|
||||||
ForceDaemon: true,
|
ForceDaemon: true,
|
||||||
},
|
},
|
||||||
stringPolicies: map[syspolicy.Key]string{
|
stringPolicies: map[pkey.Key]string{
|
||||||
syspolicy.ControlURL: "1",
|
pkey.ControlURL: "1",
|
||||||
syspolicy.EnableIncomingConnections: "never",
|
pkey.EnableIncomingConnections: "never",
|
||||||
syspolicy.EnableServerMode: "always",
|
pkey.EnableServerMode: "always",
|
||||||
syspolicy.ExitNodeAllowLANAccess: "never",
|
pkey.ExitNodeAllowLANAccess: "never",
|
||||||
syspolicy.EnableTailscaleDNS: "never",
|
pkey.EnableTailscaleDNS: "never",
|
||||||
syspolicy.EnableTailscaleSubnets: "never",
|
pkey.EnableTailscaleSubnets: "never",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -3339,13 +3340,13 @@ func TestApplySysPolicy(t *testing.T) {
|
|||||||
RouteAll: true,
|
RouteAll: true,
|
||||||
},
|
},
|
||||||
wantAnyChange: true,
|
wantAnyChange: true,
|
||||||
stringPolicies: map[syspolicy.Key]string{
|
stringPolicies: map[pkey.Key]string{
|
||||||
syspolicy.ControlURL: "2",
|
pkey.ControlURL: "2",
|
||||||
syspolicy.EnableIncomingConnections: "always",
|
pkey.EnableIncomingConnections: "always",
|
||||||
syspolicy.EnableServerMode: "never",
|
pkey.EnableServerMode: "never",
|
||||||
syspolicy.ExitNodeAllowLANAccess: "always",
|
pkey.ExitNodeAllowLANAccess: "always",
|
||||||
syspolicy.EnableTailscaleDNS: "never",
|
pkey.EnableTailscaleDNS: "never",
|
||||||
syspolicy.EnableTailscaleSubnets: "always",
|
pkey.EnableTailscaleSubnets: "always",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -3366,12 +3367,12 @@ func TestApplySysPolicy(t *testing.T) {
|
|||||||
CorpDNS: true,
|
CorpDNS: true,
|
||||||
RouteAll: true,
|
RouteAll: true,
|
||||||
},
|
},
|
||||||
stringPolicies: map[syspolicy.Key]string{
|
stringPolicies: map[pkey.Key]string{
|
||||||
syspolicy.EnableIncomingConnections: "user-decides",
|
pkey.EnableIncomingConnections: "user-decides",
|
||||||
syspolicy.EnableServerMode: "user-decides",
|
pkey.EnableServerMode: "user-decides",
|
||||||
syspolicy.ExitNodeAllowLANAccess: "user-decides",
|
pkey.ExitNodeAllowLANAccess: "user-decides",
|
||||||
syspolicy.EnableTailscaleDNS: "user-decides",
|
pkey.EnableTailscaleDNS: "user-decides",
|
||||||
syspolicy.EnableTailscaleSubnets: "user-decides",
|
pkey.EnableTailscaleSubnets: "user-decides",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -3380,8 +3381,8 @@ func TestApplySysPolicy(t *testing.T) {
|
|||||||
ControlURL: "set",
|
ControlURL: "set",
|
||||||
},
|
},
|
||||||
wantAnyChange: true,
|
wantAnyChange: true,
|
||||||
stringPolicies: map[syspolicy.Key]string{
|
stringPolicies: map[pkey.Key]string{
|
||||||
syspolicy.ControlURL: "set",
|
pkey.ControlURL: "set",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -3399,8 +3400,8 @@ func TestApplySysPolicy(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
wantAnyChange: true,
|
wantAnyChange: true,
|
||||||
stringPolicies: map[syspolicy.Key]string{
|
stringPolicies: map[pkey.Key]string{
|
||||||
syspolicy.ApplyUpdates: "always",
|
pkey.ApplyUpdates: "always",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -3418,8 +3419,8 @@ func TestApplySysPolicy(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
wantAnyChange: true,
|
wantAnyChange: true,
|
||||||
stringPolicies: map[syspolicy.Key]string{
|
stringPolicies: map[pkey.Key]string{
|
||||||
syspolicy.ApplyUpdates: "never",
|
pkey.ApplyUpdates: "never",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -3437,8 +3438,8 @@ func TestApplySysPolicy(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
wantAnyChange: true,
|
wantAnyChange: true,
|
||||||
stringPolicies: map[syspolicy.Key]string{
|
stringPolicies: map[pkey.Key]string{
|
||||||
syspolicy.CheckUpdates: "always",
|
pkey.CheckUpdates: "always",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -3456,8 +3457,8 @@ func TestApplySysPolicy(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
wantAnyChange: true,
|
wantAnyChange: true,
|
||||||
stringPolicies: map[syspolicy.Key]string{
|
stringPolicies: map[pkey.Key]string{
|
||||||
syspolicy.CheckUpdates: "never",
|
pkey.CheckUpdates: "never",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -5574,7 +5575,7 @@ func TestFillAllowedSuggestions(t *testing.T) {
|
|||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
policyStore := source.NewTestStoreOf(t, source.TestSettingOf(
|
policyStore := source.NewTestStoreOf(t, source.TestSettingOf(
|
||||||
syspolicy.AllowedSuggestedExitNodes, tt.allowPolicy,
|
pkey.AllowedSuggestedExitNodes, tt.allowPolicy,
|
||||||
))
|
))
|
||||||
syspolicy.MustRegisterStoreForTest(t, "TestStore", setting.DeviceScope, policyStore)
|
syspolicy.MustRegisterStoreForTest(t, "TestStore", setting.DeviceScope, policyStore)
|
||||||
|
|
||||||
@@ -6480,23 +6481,23 @@ func TestUpdatePrefsOnSysPolicyChange(t *testing.T) {
|
|||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "ShieldsUp/True",
|
name: "ShieldsUp/True",
|
||||||
stringSettings: []source.TestSetting[string]{source.TestSettingOf(syspolicy.EnableIncomingConnections, "never")},
|
stringSettings: []source.TestSetting[string]{source.TestSettingOf(pkey.EnableIncomingConnections, "never")},
|
||||||
want: wantPrefsChanges(fieldChange{"ShieldsUp", true}),
|
want: wantPrefsChanges(fieldChange{"ShieldsUp", true}),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "ShieldsUp/False",
|
name: "ShieldsUp/False",
|
||||||
initialPrefs: &ipn.Prefs{ShieldsUp: true},
|
initialPrefs: &ipn.Prefs{ShieldsUp: true},
|
||||||
stringSettings: []source.TestSetting[string]{source.TestSettingOf(syspolicy.EnableIncomingConnections, "always")},
|
stringSettings: []source.TestSetting[string]{source.TestSettingOf(pkey.EnableIncomingConnections, "always")},
|
||||||
want: wantPrefsChanges(fieldChange{"ShieldsUp", false}),
|
want: wantPrefsChanges(fieldChange{"ShieldsUp", false}),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "ExitNodeID",
|
name: "ExitNodeID",
|
||||||
stringSettings: []source.TestSetting[string]{source.TestSettingOf(syspolicy.ExitNodeID, "foo")},
|
stringSettings: []source.TestSetting[string]{source.TestSettingOf(pkey.ExitNodeID, "foo")},
|
||||||
want: wantPrefsChanges(fieldChange{"ExitNodeID", tailcfg.StableNodeID("foo")}),
|
want: wantPrefsChanges(fieldChange{"ExitNodeID", tailcfg.StableNodeID("foo")}),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "EnableRunExitNode",
|
name: "EnableRunExitNode",
|
||||||
stringSettings: []source.TestSetting[string]{source.TestSettingOf(syspolicy.EnableRunExitNode, "always")},
|
stringSettings: []source.TestSetting[string]{source.TestSettingOf(pkey.EnableRunExitNode, "always")},
|
||||||
want: wantPrefsChanges(fieldChange{"AdvertiseRoutes", []netip.Prefix{tsaddr.AllIPv4(), tsaddr.AllIPv6()}}),
|
want: wantPrefsChanges(fieldChange{"AdvertiseRoutes", []netip.Prefix{tsaddr.AllIPv4(), tsaddr.AllIPv6()}}),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -6505,9 +6506,9 @@ func TestUpdatePrefsOnSysPolicyChange(t *testing.T) {
|
|||||||
ExitNodeAllowLANAccess: true,
|
ExitNodeAllowLANAccess: true,
|
||||||
},
|
},
|
||||||
stringSettings: []source.TestSetting[string]{
|
stringSettings: []source.TestSetting[string]{
|
||||||
source.TestSettingOf(syspolicy.EnableServerMode, "always"),
|
source.TestSettingOf(pkey.EnableServerMode, "always"),
|
||||||
source.TestSettingOf(syspolicy.ExitNodeAllowLANAccess, "never"),
|
source.TestSettingOf(pkey.ExitNodeAllowLANAccess, "never"),
|
||||||
source.TestSettingOf(syspolicy.ExitNodeIP, "127.0.0.1"),
|
source.TestSettingOf(pkey.ExitNodeIP, "127.0.0.1"),
|
||||||
},
|
},
|
||||||
want: wantPrefsChanges(
|
want: wantPrefsChanges(
|
||||||
fieldChange{"ForceDaemon", true},
|
fieldChange{"ForceDaemon", true},
|
||||||
@@ -6523,9 +6524,9 @@ func TestUpdatePrefsOnSysPolicyChange(t *testing.T) {
|
|||||||
AdvertiseRoutes: []netip.Prefix{tsaddr.AllIPv4(), tsaddr.AllIPv6()},
|
AdvertiseRoutes: []netip.Prefix{tsaddr.AllIPv4(), tsaddr.AllIPv6()},
|
||||||
},
|
},
|
||||||
stringSettings: []source.TestSetting[string]{
|
stringSettings: []source.TestSetting[string]{
|
||||||
source.TestSettingOf(syspolicy.EnableTailscaleDNS, "always"),
|
source.TestSettingOf(pkey.EnableTailscaleDNS, "always"),
|
||||||
source.TestSettingOf(syspolicy.ExitNodeID, "foo"),
|
source.TestSettingOf(pkey.ExitNodeID, "foo"),
|
||||||
source.TestSettingOf(syspolicy.EnableRunExitNode, "always"),
|
source.TestSettingOf(pkey.EnableRunExitNode, "always"),
|
||||||
},
|
},
|
||||||
want: nil, // syspolicy settings match the preferences; no change notification is expected.
|
want: nil, // syspolicy settings match the preferences; no change notification is expected.
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ import (
|
|||||||
"tailscale.com/types/views"
|
"tailscale.com/types/views"
|
||||||
"tailscale.com/util/dnsname"
|
"tailscale.com/util/dnsname"
|
||||||
"tailscale.com/util/syspolicy"
|
"tailscale.com/util/syspolicy"
|
||||||
|
"tailscale.com/util/syspolicy/pkey"
|
||||||
"tailscale.com/version"
|
"tailscale.com/version"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -726,7 +727,7 @@ func (p PrefsView) ControlURLOrDefault() string {
|
|||||||
// If not configured, or if the configured value is a legacy name equivalent to
|
// If not configured, or if the configured value is a legacy name equivalent to
|
||||||
// the default, then DefaultControlURL is returned instead.
|
// the default, then DefaultControlURL is returned instead.
|
||||||
func (p *Prefs) ControlURLOrDefault() string {
|
func (p *Prefs) ControlURLOrDefault() string {
|
||||||
controlURL, err := syspolicy.GetString(syspolicy.ControlURL, p.ControlURL)
|
controlURL, err := syspolicy.GetString(pkey.ControlURL, p.ControlURL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
controlURL = p.ControlURL
|
controlURL = p.ControlURL
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -52,6 +52,7 @@ import (
|
|||||||
"tailscale.com/util/must"
|
"tailscale.com/util/must"
|
||||||
"tailscale.com/util/racebuild"
|
"tailscale.com/util/racebuild"
|
||||||
"tailscale.com/util/syspolicy"
|
"tailscale.com/util/syspolicy"
|
||||||
|
"tailscale.com/util/syspolicy/pkey"
|
||||||
"tailscale.com/util/testenv"
|
"tailscale.com/util/testenv"
|
||||||
"tailscale.com/version"
|
"tailscale.com/version"
|
||||||
"tailscale.com/version/distro"
|
"tailscale.com/version/distro"
|
||||||
@@ -65,7 +66,7 @@ var getLogTargetOnce struct {
|
|||||||
func getLogTarget() string {
|
func getLogTarget() string {
|
||||||
getLogTargetOnce.Do(func() {
|
getLogTargetOnce.Do(func() {
|
||||||
envTarget, _ := os.LookupEnv("TS_LOG_TARGET")
|
envTarget, _ := os.LookupEnv("TS_LOG_TARGET")
|
||||||
getLogTargetOnce.v, _ = syspolicy.GetString(syspolicy.LogTarget, envTarget)
|
getLogTargetOnce.v, _ = syspolicy.GetString(pkey.LogTarget, envTarget)
|
||||||
})
|
})
|
||||||
|
|
||||||
return getLogTargetOnce.v
|
return getLogTargetOnce.v
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ import (
|
|||||||
"tailscale.com/types/logger"
|
"tailscale.com/types/logger"
|
||||||
"tailscale.com/util/dnsname"
|
"tailscale.com/util/dnsname"
|
||||||
"tailscale.com/util/syspolicy"
|
"tailscale.com/util/syspolicy"
|
||||||
|
"tailscale.com/util/syspolicy/pkey"
|
||||||
"tailscale.com/util/syspolicy/rsop"
|
"tailscale.com/util/syspolicy/rsop"
|
||||||
"tailscale.com/util/syspolicy/setting"
|
"tailscale.com/util/syspolicy/setting"
|
||||||
"tailscale.com/util/winutil"
|
"tailscale.com/util/winutil"
|
||||||
@@ -508,7 +509,7 @@ func (m *windowsManager) Close() error {
|
|||||||
// sysPolicyChanged is a callback triggered by [syspolicy] when it detects
|
// sysPolicyChanged is a callback triggered by [syspolicy] when it detects
|
||||||
// a change in one or more syspolicy settings.
|
// a change in one or more syspolicy settings.
|
||||||
func (m *windowsManager) sysPolicyChanged(policy *rsop.PolicyChange) {
|
func (m *windowsManager) sysPolicyChanged(policy *rsop.PolicyChange) {
|
||||||
if policy.HasChanged(syspolicy.EnableDNSRegistration) {
|
if policy.HasChanged(pkey.EnableDNSRegistration) {
|
||||||
m.reconfigureDNSRegistration()
|
m.reconfigureDNSRegistration()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -520,7 +521,7 @@ func (m *windowsManager) reconfigureDNSRegistration() {
|
|||||||
// Disable DNS registration by default (if the policy setting is not configured).
|
// Disable DNS registration by default (if the policy setting is not configured).
|
||||||
// This is primarily for historical reasons and to avoid breaking existing
|
// This is primarily for historical reasons and to avoid breaking existing
|
||||||
// setups that rely on this behavior.
|
// setups that rely on this behavior.
|
||||||
enableDNSRegistration, err := syspolicy.GetPreferenceOptionOrDefault(syspolicy.EnableDNSRegistration, setting.NeverByPolicy)
|
enableDNSRegistration, err := syspolicy.GetPreferenceOptionOrDefault(pkey.EnableDNSRegistration, setting.NeverByPolicy)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
m.logf("error getting DNSRegistration policy setting: %v", err) // non-fatal; we'll use the default
|
m.logf("error getting DNSRegistration policy setting: %v", err) // non-fatal; we'll use the default
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,13 +10,14 @@ import (
|
|||||||
|
|
||||||
"tailscale.com/types/logger"
|
"tailscale.com/types/logger"
|
||||||
"tailscale.com/util/syspolicy"
|
"tailscale.com/util/syspolicy"
|
||||||
|
"tailscale.com/util/syspolicy/pkey"
|
||||||
)
|
)
|
||||||
|
|
||||||
// GetSerialNumbers returns the serial number of the device as reported by an
|
// GetSerialNumbers returns the serial number of the device as reported by an
|
||||||
// MDM solution. It requires configuration via the DeviceSerialNumber system policy.
|
// MDM solution. It requires configuration via the DeviceSerialNumber system policy.
|
||||||
// This is the only way to gather serial numbers on iOS, tvOS and Android.
|
// This is the only way to gather serial numbers on iOS, tvOS and Android.
|
||||||
func GetSerialNumbers(_ logger.Logf) ([]string, error) {
|
func GetSerialNumbers(_ logger.Logf) ([]string, error) {
|
||||||
s, err := syspolicy.GetString(syspolicy.DeviceSerialNumber, "")
|
s, err := syspolicy.GetString(pkey.DeviceSerialNumber, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to get serial number from MDM: %v", err)
|
return nil, fmt.Errorf("failed to get serial number from MDM: %v", err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -379,6 +379,7 @@ tailscale.com/tsnet dependencies: (generated by github.com/tailscale/depaware)
|
|||||||
tailscale.com/util/syspolicy/internal from tailscale.com/util/syspolicy+
|
tailscale.com/util/syspolicy/internal from tailscale.com/util/syspolicy+
|
||||||
tailscale.com/util/syspolicy/internal/loggerx from tailscale.com/util/syspolicy+
|
tailscale.com/util/syspolicy/internal/loggerx from tailscale.com/util/syspolicy+
|
||||||
tailscale.com/util/syspolicy/internal/metrics from tailscale.com/util/syspolicy/source
|
tailscale.com/util/syspolicy/internal/metrics from tailscale.com/util/syspolicy/source
|
||||||
|
tailscale.com/util/syspolicy/pkey from tailscale.com/control/controlclient+
|
||||||
tailscale.com/util/syspolicy/rsop from tailscale.com/ipn/ipnlocal+
|
tailscale.com/util/syspolicy/rsop from tailscale.com/ipn/ipnlocal+
|
||||||
tailscale.com/util/syspolicy/setting from tailscale.com/client/local+
|
tailscale.com/util/syspolicy/setting from tailscale.com/client/local+
|
||||||
tailscale.com/util/syspolicy/source from tailscale.com/util/syspolicy+
|
tailscale.com/util/syspolicy/source from tailscale.com/util/syspolicy+
|
||||||
|
|||||||
@@ -52,6 +52,7 @@ import (
|
|||||||
_ "tailscale.com/util/multierr"
|
_ "tailscale.com/util/multierr"
|
||||||
_ "tailscale.com/util/osshare"
|
_ "tailscale.com/util/osshare"
|
||||||
_ "tailscale.com/util/syspolicy"
|
_ "tailscale.com/util/syspolicy"
|
||||||
|
_ "tailscale.com/util/syspolicy/pkey"
|
||||||
_ "tailscale.com/version"
|
_ "tailscale.com/version"
|
||||||
_ "tailscale.com/version/distro"
|
_ "tailscale.com/version/distro"
|
||||||
_ "tailscale.com/wgengine"
|
_ "tailscale.com/wgengine"
|
||||||
|
|||||||
@@ -52,6 +52,7 @@ import (
|
|||||||
_ "tailscale.com/util/multierr"
|
_ "tailscale.com/util/multierr"
|
||||||
_ "tailscale.com/util/osshare"
|
_ "tailscale.com/util/osshare"
|
||||||
_ "tailscale.com/util/syspolicy"
|
_ "tailscale.com/util/syspolicy"
|
||||||
|
_ "tailscale.com/util/syspolicy/pkey"
|
||||||
_ "tailscale.com/version"
|
_ "tailscale.com/version"
|
||||||
_ "tailscale.com/version/distro"
|
_ "tailscale.com/version/distro"
|
||||||
_ "tailscale.com/wgengine"
|
_ "tailscale.com/wgengine"
|
||||||
|
|||||||
@@ -52,6 +52,7 @@ import (
|
|||||||
_ "tailscale.com/util/multierr"
|
_ "tailscale.com/util/multierr"
|
||||||
_ "tailscale.com/util/osshare"
|
_ "tailscale.com/util/osshare"
|
||||||
_ "tailscale.com/util/syspolicy"
|
_ "tailscale.com/util/syspolicy"
|
||||||
|
_ "tailscale.com/util/syspolicy/pkey"
|
||||||
_ "tailscale.com/version"
|
_ "tailscale.com/version"
|
||||||
_ "tailscale.com/version/distro"
|
_ "tailscale.com/version/distro"
|
||||||
_ "tailscale.com/wgengine"
|
_ "tailscale.com/wgengine"
|
||||||
|
|||||||
@@ -52,6 +52,7 @@ import (
|
|||||||
_ "tailscale.com/util/multierr"
|
_ "tailscale.com/util/multierr"
|
||||||
_ "tailscale.com/util/osshare"
|
_ "tailscale.com/util/osshare"
|
||||||
_ "tailscale.com/util/syspolicy"
|
_ "tailscale.com/util/syspolicy"
|
||||||
|
_ "tailscale.com/util/syspolicy/pkey"
|
||||||
_ "tailscale.com/version"
|
_ "tailscale.com/version"
|
||||||
_ "tailscale.com/version/distro"
|
_ "tailscale.com/version/distro"
|
||||||
_ "tailscale.com/wgengine"
|
_ "tailscale.com/wgengine"
|
||||||
|
|||||||
@@ -63,6 +63,7 @@ import (
|
|||||||
_ "tailscale.com/util/osdiag"
|
_ "tailscale.com/util/osdiag"
|
||||||
_ "tailscale.com/util/osshare"
|
_ "tailscale.com/util/osshare"
|
||||||
_ "tailscale.com/util/syspolicy"
|
_ "tailscale.com/util/syspolicy"
|
||||||
|
_ "tailscale.com/util/syspolicy/pkey"
|
||||||
_ "tailscale.com/util/winutil"
|
_ "tailscale.com/util/winutil"
|
||||||
_ "tailscale.com/util/winutil/gp"
|
_ "tailscale.com/util/winutil/gp"
|
||||||
_ "tailscale.com/version"
|
_ "tailscale.com/version"
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
package syspolicy
|
package syspolicy
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"tailscale.com/util/syspolicy/pkey"
|
||||||
"tailscale.com/util/syspolicy/rsop"
|
"tailscale.com/util/syspolicy/rsop"
|
||||||
"tailscale.com/util/syspolicy/setting"
|
"tailscale.com/util/syspolicy/setting"
|
||||||
"tailscale.com/util/syspolicy/source"
|
"tailscale.com/util/syspolicy/source"
|
||||||
@@ -85,22 +86,22 @@ func (s handlerStore) RegisterChangeCallback(callback func()) (unregister func()
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ReadString implements [source.Store].
|
// ReadString implements [source.Store].
|
||||||
func (s handlerStore) ReadString(key setting.Key) (string, error) {
|
func (s handlerStore) ReadString(key pkey.Key) (string, error) {
|
||||||
return s.h.ReadString(string(key))
|
return s.h.ReadString(string(key))
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReadUInt64 implements [source.Store].
|
// ReadUInt64 implements [source.Store].
|
||||||
func (s handlerStore) ReadUInt64(key setting.Key) (uint64, error) {
|
func (s handlerStore) ReadUInt64(key pkey.Key) (uint64, error) {
|
||||||
return s.h.ReadUInt64(string(key))
|
return s.h.ReadUInt64(string(key))
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReadBoolean implements [source.Store].
|
// ReadBoolean implements [source.Store].
|
||||||
func (s handlerStore) ReadBoolean(key setting.Key) (bool, error) {
|
func (s handlerStore) ReadBoolean(key pkey.Key) (bool, error) {
|
||||||
return s.h.ReadBoolean(string(key))
|
return s.h.ReadBoolean(string(key))
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReadStringArray implements [source.Store].
|
// ReadStringArray implements [source.Store].
|
||||||
func (s handlerStore) ReadStringArray(key setting.Key) ([]string, error) {
|
func (s handlerStore) ReadStringArray(key pkey.Key) ([]string, error) {
|
||||||
return s.h.ReadStringArray(string(key))
|
return s.h.ReadStringArray(string(key))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import (
|
|||||||
"tailscale.com/util/slicesx"
|
"tailscale.com/util/slicesx"
|
||||||
"tailscale.com/util/syspolicy/internal"
|
"tailscale.com/util/syspolicy/internal"
|
||||||
"tailscale.com/util/syspolicy/internal/loggerx"
|
"tailscale.com/util/syspolicy/internal/loggerx"
|
||||||
|
"tailscale.com/util/syspolicy/pkey"
|
||||||
"tailscale.com/util/syspolicy/setting"
|
"tailscale.com/util/syspolicy/setting"
|
||||||
"tailscale.com/util/testenv"
|
"tailscale.com/util/testenv"
|
||||||
)
|
)
|
||||||
@@ -209,7 +210,7 @@ func scopeMetrics(origin *setting.Origin) *policyScopeMetrics {
|
|||||||
|
|
||||||
var (
|
var (
|
||||||
settingMetricsMu sync.RWMutex
|
settingMetricsMu sync.RWMutex
|
||||||
settingMetricsMap map[setting.Key]*settingMetrics
|
settingMetricsMap map[pkey.Key]*settingMetrics
|
||||||
)
|
)
|
||||||
|
|
||||||
func settingMetricsFor(setting *setting.Definition) *settingMetrics {
|
func settingMetricsFor(setting *setting.Definition) *settingMetrics {
|
||||||
@@ -283,8 +284,8 @@ func SetHooksForTest(tb testenv.TB, addMetric, setMetric metricFn) {
|
|||||||
lazyUserMetrics.SetForTest(tb, newScopeMetrics(setting.UserSetting), nil)
|
lazyUserMetrics.SetForTest(tb, newScopeMetrics(setting.UserSetting), nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func newSettingMetric(key setting.Key, scope setting.Scope, suffix string, typ clientmetric.Type) metric {
|
func newSettingMetric(key pkey.Key, scope setting.Scope, suffix string, typ clientmetric.Type) metric {
|
||||||
name := strings.ReplaceAll(string(key), string(setting.KeyPathSeparator), "_")
|
name := strings.ReplaceAll(string(key), string(pkey.KeyPathSeparator), "_")
|
||||||
name = strings.ReplaceAll(name, ".", "_") // dots are not allowed in metric names
|
name = strings.ReplaceAll(name, ".", "_") // dots are not allowed in metric names
|
||||||
return newMetric([]string{name, metricScopeName(scope), suffix}, typ)
|
return newMetric([]string{name, metricScopeName(scope), suffix}, typ)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,13 +10,14 @@ import (
|
|||||||
"tailscale.com/types/lazy"
|
"tailscale.com/types/lazy"
|
||||||
"tailscale.com/util/clientmetric"
|
"tailscale.com/util/clientmetric"
|
||||||
"tailscale.com/util/syspolicy/internal"
|
"tailscale.com/util/syspolicy/internal"
|
||||||
|
"tailscale.com/util/syspolicy/pkey"
|
||||||
"tailscale.com/util/syspolicy/setting"
|
"tailscale.com/util/syspolicy/setting"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestSettingMetricNames(t *testing.T) {
|
func TestSettingMetricNames(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
key setting.Key
|
key pkey.Key
|
||||||
scope setting.Scope
|
scope setting.Scope
|
||||||
suffix string
|
suffix string
|
||||||
typ clientmetric.Type
|
typ clientmetric.Type
|
||||||
|
|||||||
177
util/syspolicy/pkey/pkey.go
Normal file
177
util/syspolicy/pkey/pkey.go
Normal file
@@ -0,0 +1,177 @@
|
|||||||
|
// Copyright (c) Tailscale Inc & AUTHORS
|
||||||
|
// SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
|
// Package pkey defines the keys used to store system policies in the registry.
|
||||||
|
//
|
||||||
|
// This is a leaf package meant to only contain string constants, not code.
|
||||||
|
package pkey
|
||||||
|
|
||||||
|
// Key is a string that uniquely identifies a policy and must remain unchanged
|
||||||
|
// once established and documented for a given policy setting. It may contain
|
||||||
|
// alphanumeric characters and zero or more [KeyPathSeparator]s to group
|
||||||
|
// individual policy settings into categories.
|
||||||
|
type Key string
|
||||||
|
|
||||||
|
// KeyPathSeparator allows logical grouping of policy settings into categories.
|
||||||
|
const KeyPathSeparator = '/'
|
||||||
|
|
||||||
|
// The const block below lists known policy keys.
|
||||||
|
// When adding a key to this list, remember to add a corresponding
|
||||||
|
// [setting.Definition] to [implicitDefinitions] in util/syspolicy/policy_keys.go.
|
||||||
|
// Otherwise, the [TestKnownKeysRegistered] test will fail as a reminder.
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Keys with a string value
|
||||||
|
ControlURL Key = "LoginURL" // default ""; if blank, ipn uses ipn.DefaultControlURL.
|
||||||
|
LogTarget Key = "LogTarget" // default ""; if blank logging uses logtail.DefaultHost.
|
||||||
|
Tailnet Key = "Tailnet" // default ""; if blank, no tailnet name is sent to the server.
|
||||||
|
|
||||||
|
// AlwaysOn is a boolean key that controls whether Tailscale
|
||||||
|
// should always remain in a connected state, and the user should
|
||||||
|
// not be able to disconnect at their discretion.
|
||||||
|
//
|
||||||
|
// Warning: This policy setting is experimental and may change or be removed in the future.
|
||||||
|
// It may also not be fully supported by all Tailscale clients until it is out of experimental status.
|
||||||
|
// See tailscale/corp#26247, tailscale/corp#26248 and tailscale/corp#26249 for more information.
|
||||||
|
AlwaysOn Key = "AlwaysOn.Enabled"
|
||||||
|
|
||||||
|
// AlwaysOnOverrideWithReason is a boolean key that alters the behavior
|
||||||
|
// of [AlwaysOn]. When true, the user is allowed to disconnect Tailscale
|
||||||
|
// by providing a reason. The reason is logged and sent to the control
|
||||||
|
// for auditing purposes. It has no effect when [AlwaysOn] is false.
|
||||||
|
AlwaysOnOverrideWithReason Key = "AlwaysOn.OverrideWithReason"
|
||||||
|
|
||||||
|
// ReconnectAfter is a string value formatted for use with time.ParseDuration()
|
||||||
|
// that defines the duration after which the client should automatically reconnect
|
||||||
|
// to the Tailscale network following a user-initiated disconnect.
|
||||||
|
// An empty string or a zero duration disables automatic reconnection.
|
||||||
|
ReconnectAfter Key = "ReconnectAfter"
|
||||||
|
|
||||||
|
// ExitNodeID is the exit node's node id. default ""; if blank, no exit node is forced.
|
||||||
|
// Exit node ID takes precedence over exit node IP.
|
||||||
|
// To find the node ID, go to /api.md#device.
|
||||||
|
ExitNodeID Key = "ExitNodeID"
|
||||||
|
ExitNodeIP Key = "ExitNodeIP" // default ""; if blank, no exit node is forced. Value is exit node IP.
|
||||||
|
|
||||||
|
// AllowExitNodeOverride is a boolean key that allows the user to override exit node policy settings
|
||||||
|
// and manually select an exit node. It does not allow disabling exit node usage entirely.
|
||||||
|
// It is typically used in conjunction with [ExitNodeID] set to "auto:any".
|
||||||
|
//
|
||||||
|
// Warning: This policy setting is experimental and may change, be renamed or removed in the future.
|
||||||
|
// It may also not be fully supported by all Tailscale clients until it is out of experimental status.
|
||||||
|
// See tailscale/corp#29969.
|
||||||
|
AllowExitNodeOverride Key = "ExitNode.AllowOverride"
|
||||||
|
|
||||||
|
// Keys with a string value that specifies an option: "always", "never", "user-decides".
|
||||||
|
// The default is "user-decides" unless otherwise stated. Enforcement of
|
||||||
|
// these policies is typically performed in ipnlocal.applySysPolicy(). GUIs
|
||||||
|
// typically hide menu items related to policies that are enforced.
|
||||||
|
EnableIncomingConnections Key = "AllowIncomingConnections"
|
||||||
|
EnableServerMode Key = "UnattendedMode"
|
||||||
|
ExitNodeAllowLANAccess Key = "ExitNodeAllowLANAccess"
|
||||||
|
EnableTailscaleDNS Key = "UseTailscaleDNSSettings"
|
||||||
|
EnableTailscaleSubnets Key = "UseTailscaleSubnets"
|
||||||
|
|
||||||
|
// EnableDNSRegistration is a string value that can be set to "always", "never"
|
||||||
|
// or "user-decides". It controls whether DNS registration and dynamic DNS
|
||||||
|
// updates are enabled for the Tailscale interface. For historical reasons
|
||||||
|
// and to maintain compatibility with existing setups, the default is "never".
|
||||||
|
// It is only used on Windows.
|
||||||
|
EnableDNSRegistration Key = "EnableDNSRegistration"
|
||||||
|
|
||||||
|
// CheckUpdates is the key to signal if the updater should periodically
|
||||||
|
// check for updates.
|
||||||
|
CheckUpdates Key = "CheckUpdates"
|
||||||
|
// ApplyUpdates is the key to signal if updates should be automatically
|
||||||
|
// installed. Its value is "InstallUpdates" because of an awkwardly-named
|
||||||
|
// visibility option "ApplyUpdates" on MacOS.
|
||||||
|
ApplyUpdates Key = "InstallUpdates"
|
||||||
|
// EnableRunExitNode controls if the device acts as an exit node. Even when
|
||||||
|
// running as an exit node, the device must be approved by a tailnet
|
||||||
|
// administrator. Its name is slightly awkward because RunExitNodeVisibility
|
||||||
|
// predates this option but is preserved for backwards compatibility.
|
||||||
|
EnableRunExitNode Key = "AdvertiseExitNode"
|
||||||
|
|
||||||
|
// Keys with a string value that controls visibility: "show", "hide".
|
||||||
|
// The default is "show" unless otherwise stated. Enforcement of these
|
||||||
|
// policies is typically performed by the UI code for the relevant operating
|
||||||
|
// system.
|
||||||
|
AdminConsoleVisibility Key = "AdminConsole"
|
||||||
|
NetworkDevicesVisibility Key = "NetworkDevices"
|
||||||
|
TestMenuVisibility Key = "TestMenu"
|
||||||
|
UpdateMenuVisibility Key = "UpdateMenu"
|
||||||
|
ResetToDefaultsVisibility Key = "ResetToDefaults"
|
||||||
|
// RunExitNodeVisibility controls if the "run as exit node" menu item is
|
||||||
|
// visible, without controlling the setting itself. This is preserved for
|
||||||
|
// backwards compatibility but prefer EnableRunExitNode in new deployments.
|
||||||
|
RunExitNodeVisibility Key = "RunExitNode"
|
||||||
|
PreferencesMenuVisibility Key = "PreferencesMenu"
|
||||||
|
ExitNodeMenuVisibility Key = "ExitNodesPicker"
|
||||||
|
// AutoUpdateVisibility is the key to signal if the menu item for automatic
|
||||||
|
// installation of updates should be visible. It is only used by macsys
|
||||||
|
// installations and uses the Sparkle naming convention, even though it does
|
||||||
|
// not actually control updates, merely the UI for that setting.
|
||||||
|
AutoUpdateVisibility Key = "ApplyUpdates"
|
||||||
|
// SuggestedExitNodeVisibility controls the visibility of suggested exit nodes in the client GUI.
|
||||||
|
// When this system policy is set to 'hide', an exit node suggestion won't be presented to the user as part of the exit nodes picker.
|
||||||
|
SuggestedExitNodeVisibility Key = "SuggestedExitNode"
|
||||||
|
// OnboardingFlowVisibility controls the visibility of the onboarding flow in the client GUI.
|
||||||
|
// When this system policy is set to 'hide', the onboarding flow is never shown to the user.
|
||||||
|
OnboardingFlowVisibility Key = "OnboardingFlow"
|
||||||
|
|
||||||
|
// Keys with a string value formatted for use with time.ParseDuration().
|
||||||
|
KeyExpirationNoticeTime Key = "KeyExpirationNotice" // default 24 hours
|
||||||
|
|
||||||
|
// Boolean Keys that are only applicable on Windows. Booleans are stored in the registry as
|
||||||
|
// DWORD or QWORD (either is acceptable). 0 means false, and anything else means true.
|
||||||
|
// The default is 0 unless otherwise stated.
|
||||||
|
LogSCMInteractions Key = "LogSCMInteractions"
|
||||||
|
FlushDNSOnSessionUnlock Key = "FlushDNSOnSessionUnlock"
|
||||||
|
|
||||||
|
// EncryptState is a boolean setting that specifies whether to encrypt the
|
||||||
|
// tailscaled state file with a TPM device.
|
||||||
|
EncryptState Key = "EncryptState"
|
||||||
|
|
||||||
|
// PostureChecking indicates if posture checking is enabled and the client shall gather
|
||||||
|
// posture data.
|
||||||
|
// Key is a string value that specifies an option: "always", "never", "user-decides".
|
||||||
|
// The default is "user-decides" unless otherwise stated.
|
||||||
|
PostureChecking Key = "PostureChecking"
|
||||||
|
// DeviceSerialNumber is the serial number of the device that is running Tailscale.
|
||||||
|
// This is used on Android, iOS and tvOS to allow IT administrators to manually give us a serial number via MDM.
|
||||||
|
// We are unable to programmatically get the serial number on mobile due to sandboxing restrictions.
|
||||||
|
DeviceSerialNumber Key = "DeviceSerialNumber"
|
||||||
|
|
||||||
|
// ManagedByOrganizationName indicates the name of the organization managing the Tailscale
|
||||||
|
// install. It is displayed inside the client UI in a prominent location.
|
||||||
|
ManagedByOrganizationName Key = "ManagedByOrganizationName"
|
||||||
|
// ManagedByCaption is an info message displayed inside the client UI as a caption when
|
||||||
|
// ManagedByOrganizationName is set. It can be used to provide a pointer to support resources
|
||||||
|
// for Tailscale within the organization.
|
||||||
|
ManagedByCaption Key = "ManagedByCaption"
|
||||||
|
// ManagedByURL is a valid URL pointing to a support help desk for Tailscale within the
|
||||||
|
// organization. A button in the client UI provides easy access to this URL.
|
||||||
|
ManagedByURL Key = "ManagedByURL"
|
||||||
|
|
||||||
|
// AuthKey is an auth key that will be used to login whenever the backend starts. This can be used to
|
||||||
|
// automatically authenticate managed devices, without requiring user interaction.
|
||||||
|
AuthKey Key = "AuthKey"
|
||||||
|
|
||||||
|
// MachineCertificateSubject is the exact name of a Subject that needs
|
||||||
|
// to be present in an identity's certificate chain to sign a RegisterRequest,
|
||||||
|
// formatted as per pkix.Name.String(). The Subject may be that of the identity
|
||||||
|
// itself, an intermediate CA or the root CA.
|
||||||
|
//
|
||||||
|
// Example: "CN=Tailscale Inc Test Root CA,OU=Tailscale Inc Test Certificate Authority,O=Tailscale Inc,ST=ON,C=CA"
|
||||||
|
MachineCertificateSubject Key = "MachineCertificateSubject"
|
||||||
|
|
||||||
|
// Hostname is the hostname of the device that is running Tailscale.
|
||||||
|
// When this policy is set, it overrides the hostname that the client
|
||||||
|
// would otherwise obtain from the OS, e.g. by calling os.Hostname().
|
||||||
|
Hostname Key = "Hostname"
|
||||||
|
|
||||||
|
// Keys with a string array value.
|
||||||
|
|
||||||
|
// AllowedSuggestedExitNodes's string array value is a list of exit node IDs that restricts which exit nodes are considered when generating suggestions for exit nodes.
|
||||||
|
AllowedSuggestedExitNodes Key = "AllowedSuggestedExitNodes"
|
||||||
|
)
|
||||||
@@ -6,225 +6,60 @@ package syspolicy
|
|||||||
import (
|
import (
|
||||||
"tailscale.com/types/lazy"
|
"tailscale.com/types/lazy"
|
||||||
"tailscale.com/util/syspolicy/internal"
|
"tailscale.com/util/syspolicy/internal"
|
||||||
|
"tailscale.com/util/syspolicy/pkey"
|
||||||
"tailscale.com/util/syspolicy/setting"
|
"tailscale.com/util/syspolicy/setting"
|
||||||
"tailscale.com/util/testenv"
|
"tailscale.com/util/testenv"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Key is a string that uniquely identifies a policy and must remain unchanged
|
|
||||||
// once established and documented for a given policy setting. It may contain
|
|
||||||
// alphanumeric characters and zero or more [KeyPathSeparator]s to group
|
|
||||||
// individual policy settings into categories.
|
|
||||||
type Key = setting.Key
|
|
||||||
|
|
||||||
// The const block below lists known policy keys.
|
|
||||||
// When adding a key to this list, remember to add a corresponding
|
|
||||||
// [setting.Definition] to [implicitDefinitions] below.
|
|
||||||
// Otherwise, the [TestKnownKeysRegistered] test will fail as a reminder.
|
|
||||||
|
|
||||||
const (
|
|
||||||
// Keys with a string value
|
|
||||||
ControlURL Key = "LoginURL" // default ""; if blank, ipn uses ipn.DefaultControlURL.
|
|
||||||
LogTarget Key = "LogTarget" // default ""; if blank logging uses logtail.DefaultHost.
|
|
||||||
Tailnet Key = "Tailnet" // default ""; if blank, no tailnet name is sent to the server.
|
|
||||||
|
|
||||||
// AlwaysOn is a boolean key that controls whether Tailscale
|
|
||||||
// should always remain in a connected state, and the user should
|
|
||||||
// not be able to disconnect at their discretion.
|
|
||||||
//
|
|
||||||
// Warning: This policy setting is experimental and may change or be removed in the future.
|
|
||||||
// It may also not be fully supported by all Tailscale clients until it is out of experimental status.
|
|
||||||
// See tailscale/corp#26247, tailscale/corp#26248 and tailscale/corp#26249 for more information.
|
|
||||||
AlwaysOn Key = "AlwaysOn.Enabled"
|
|
||||||
|
|
||||||
// AlwaysOnOverrideWithReason is a boolean key that alters the behavior
|
|
||||||
// of [AlwaysOn]. When true, the user is allowed to disconnect Tailscale
|
|
||||||
// by providing a reason. The reason is logged and sent to the control
|
|
||||||
// for auditing purposes. It has no effect when [AlwaysOn] is false.
|
|
||||||
AlwaysOnOverrideWithReason Key = "AlwaysOn.OverrideWithReason"
|
|
||||||
|
|
||||||
// ReconnectAfter is a string value formatted for use with time.ParseDuration()
|
|
||||||
// that defines the duration after which the client should automatically reconnect
|
|
||||||
// to the Tailscale network following a user-initiated disconnect.
|
|
||||||
// An empty string or a zero duration disables automatic reconnection.
|
|
||||||
ReconnectAfter Key = "ReconnectAfter"
|
|
||||||
|
|
||||||
// ExitNodeID is the exit node's node id. default ""; if blank, no exit node is forced.
|
|
||||||
// Exit node ID takes precedence over exit node IP.
|
|
||||||
// To find the node ID, go to /api.md#device.
|
|
||||||
ExitNodeID Key = "ExitNodeID"
|
|
||||||
ExitNodeIP Key = "ExitNodeIP" // default ""; if blank, no exit node is forced. Value is exit node IP.
|
|
||||||
|
|
||||||
// AllowExitNodeOverride is a boolean key that allows the user to override exit node policy settings
|
|
||||||
// and manually select an exit node. It does not allow disabling exit node usage entirely.
|
|
||||||
// It is typically used in conjunction with [ExitNodeID] set to "auto:any".
|
|
||||||
//
|
|
||||||
// Warning: This policy setting is experimental and may change, be renamed or removed in the future.
|
|
||||||
// It may also not be fully supported by all Tailscale clients until it is out of experimental status.
|
|
||||||
// See tailscale/corp#29969.
|
|
||||||
AllowExitNodeOverride Key = "ExitNode.AllowOverride"
|
|
||||||
|
|
||||||
// Keys with a string value that specifies an option: "always", "never", "user-decides".
|
|
||||||
// The default is "user-decides" unless otherwise stated. Enforcement of
|
|
||||||
// these policies is typically performed in ipnlocal.applySysPolicy(). GUIs
|
|
||||||
// typically hide menu items related to policies that are enforced.
|
|
||||||
EnableIncomingConnections Key = "AllowIncomingConnections"
|
|
||||||
EnableServerMode Key = "UnattendedMode"
|
|
||||||
ExitNodeAllowLANAccess Key = "ExitNodeAllowLANAccess"
|
|
||||||
EnableTailscaleDNS Key = "UseTailscaleDNSSettings"
|
|
||||||
EnableTailscaleSubnets Key = "UseTailscaleSubnets"
|
|
||||||
|
|
||||||
// EnableDNSRegistration is a string value that can be set to "always", "never"
|
|
||||||
// or "user-decides". It controls whether DNS registration and dynamic DNS
|
|
||||||
// updates are enabled for the Tailscale interface. For historical reasons
|
|
||||||
// and to maintain compatibility with existing setups, the default is "never".
|
|
||||||
// It is only used on Windows.
|
|
||||||
EnableDNSRegistration Key = "EnableDNSRegistration"
|
|
||||||
|
|
||||||
// CheckUpdates is the key to signal if the updater should periodically
|
|
||||||
// check for updates.
|
|
||||||
CheckUpdates Key = "CheckUpdates"
|
|
||||||
// ApplyUpdates is the key to signal if updates should be automatically
|
|
||||||
// installed. Its value is "InstallUpdates" because of an awkwardly-named
|
|
||||||
// visibility option "ApplyUpdates" on MacOS.
|
|
||||||
ApplyUpdates Key = "InstallUpdates"
|
|
||||||
// EnableRunExitNode controls if the device acts as an exit node. Even when
|
|
||||||
// running as an exit node, the device must be approved by a tailnet
|
|
||||||
// administrator. Its name is slightly awkward because RunExitNodeVisibility
|
|
||||||
// predates this option but is preserved for backwards compatibility.
|
|
||||||
EnableRunExitNode Key = "AdvertiseExitNode"
|
|
||||||
|
|
||||||
// Keys with a string value that controls visibility: "show", "hide".
|
|
||||||
// The default is "show" unless otherwise stated. Enforcement of these
|
|
||||||
// policies is typically performed by the UI code for the relevant operating
|
|
||||||
// system.
|
|
||||||
AdminConsoleVisibility Key = "AdminConsole"
|
|
||||||
NetworkDevicesVisibility Key = "NetworkDevices"
|
|
||||||
TestMenuVisibility Key = "TestMenu"
|
|
||||||
UpdateMenuVisibility Key = "UpdateMenu"
|
|
||||||
ResetToDefaultsVisibility Key = "ResetToDefaults"
|
|
||||||
// RunExitNodeVisibility controls if the "run as exit node" menu item is
|
|
||||||
// visible, without controlling the setting itself. This is preserved for
|
|
||||||
// backwards compatibility but prefer EnableRunExitNode in new deployments.
|
|
||||||
RunExitNodeVisibility Key = "RunExitNode"
|
|
||||||
PreferencesMenuVisibility Key = "PreferencesMenu"
|
|
||||||
ExitNodeMenuVisibility Key = "ExitNodesPicker"
|
|
||||||
// AutoUpdateVisibility is the key to signal if the menu item for automatic
|
|
||||||
// installation of updates should be visible. It is only used by macsys
|
|
||||||
// installations and uses the Sparkle naming convention, even though it does
|
|
||||||
// not actually control updates, merely the UI for that setting.
|
|
||||||
AutoUpdateVisibility Key = "ApplyUpdates"
|
|
||||||
// SuggestedExitNodeVisibility controls the visibility of suggested exit nodes in the client GUI.
|
|
||||||
// When this system policy is set to 'hide', an exit node suggestion won't be presented to the user as part of the exit nodes picker.
|
|
||||||
SuggestedExitNodeVisibility Key = "SuggestedExitNode"
|
|
||||||
// OnboardingFlowVisibility controls the visibility of the onboarding flow in the client GUI.
|
|
||||||
// When this system policy is set to 'hide', the onboarding flow is never shown to the user.
|
|
||||||
OnboardingFlowVisibility Key = "OnboardingFlow"
|
|
||||||
|
|
||||||
// Keys with a string value formatted for use with time.ParseDuration().
|
|
||||||
KeyExpirationNoticeTime Key = "KeyExpirationNotice" // default 24 hours
|
|
||||||
|
|
||||||
// Boolean Keys that are only applicable on Windows. Booleans are stored in the registry as
|
|
||||||
// DWORD or QWORD (either is acceptable). 0 means false, and anything else means true.
|
|
||||||
// The default is 0 unless otherwise stated.
|
|
||||||
LogSCMInteractions Key = "LogSCMInteractions"
|
|
||||||
FlushDNSOnSessionUnlock Key = "FlushDNSOnSessionUnlock"
|
|
||||||
|
|
||||||
// EncryptState is a boolean setting that specifies whether to encrypt the
|
|
||||||
// tailscaled state file with a TPM device.
|
|
||||||
EncryptState Key = "EncryptState"
|
|
||||||
|
|
||||||
// PostureChecking indicates if posture checking is enabled and the client shall gather
|
|
||||||
// posture data.
|
|
||||||
// Key is a string value that specifies an option: "always", "never", "user-decides".
|
|
||||||
// The default is "user-decides" unless otherwise stated.
|
|
||||||
PostureChecking Key = "PostureChecking"
|
|
||||||
// DeviceSerialNumber is the serial number of the device that is running Tailscale.
|
|
||||||
// This is used on Android, iOS and tvOS to allow IT administrators to manually give us a serial number via MDM.
|
|
||||||
// We are unable to programmatically get the serial number on mobile due to sandboxing restrictions.
|
|
||||||
DeviceSerialNumber Key = "DeviceSerialNumber"
|
|
||||||
|
|
||||||
// ManagedByOrganizationName indicates the name of the organization managing the Tailscale
|
|
||||||
// install. It is displayed inside the client UI in a prominent location.
|
|
||||||
ManagedByOrganizationName Key = "ManagedByOrganizationName"
|
|
||||||
// ManagedByCaption is an info message displayed inside the client UI as a caption when
|
|
||||||
// ManagedByOrganizationName is set. It can be used to provide a pointer to support resources
|
|
||||||
// for Tailscale within the organization.
|
|
||||||
ManagedByCaption Key = "ManagedByCaption"
|
|
||||||
// ManagedByURL is a valid URL pointing to a support help desk for Tailscale within the
|
|
||||||
// organization. A button in the client UI provides easy access to this URL.
|
|
||||||
ManagedByURL Key = "ManagedByURL"
|
|
||||||
|
|
||||||
// AuthKey is an auth key that will be used to login whenever the backend starts. This can be used to
|
|
||||||
// automatically authenticate managed devices, without requiring user interaction.
|
|
||||||
AuthKey Key = "AuthKey"
|
|
||||||
|
|
||||||
// MachineCertificateSubject is the exact name of a Subject that needs
|
|
||||||
// to be present in an identity's certificate chain to sign a RegisterRequest,
|
|
||||||
// formatted as per pkix.Name.String(). The Subject may be that of the identity
|
|
||||||
// itself, an intermediate CA or the root CA.
|
|
||||||
//
|
|
||||||
// Example: "CN=Tailscale Inc Test Root CA,OU=Tailscale Inc Test Certificate Authority,O=Tailscale Inc,ST=ON,C=CA"
|
|
||||||
MachineCertificateSubject Key = "MachineCertificateSubject"
|
|
||||||
|
|
||||||
// Hostname is the hostname of the device that is running Tailscale.
|
|
||||||
// When this policy is set, it overrides the hostname that the client
|
|
||||||
// would otherwise obtain from the OS, e.g. by calling os.Hostname().
|
|
||||||
Hostname Key = "Hostname"
|
|
||||||
|
|
||||||
// Keys with a string array value.
|
|
||||||
// AllowedSuggestedExitNodes's string array value is a list of exit node IDs that restricts which exit nodes are considered when generating suggestions for exit nodes.
|
|
||||||
AllowedSuggestedExitNodes Key = "AllowedSuggestedExitNodes"
|
|
||||||
)
|
|
||||||
|
|
||||||
// implicitDefinitions is a list of [setting.Definition] that will be registered
|
// implicitDefinitions is a list of [setting.Definition] that will be registered
|
||||||
// automatically when the policy setting definitions are first used by the syspolicy package hierarchy.
|
// automatically when the policy setting definitions are first used by the syspolicy package hierarchy.
|
||||||
// This includes the first time a policy needs to be read from any source.
|
// This includes the first time a policy needs to be read from any source.
|
||||||
var implicitDefinitions = []*setting.Definition{
|
var implicitDefinitions = []*setting.Definition{
|
||||||
// Device policy settings (can only be configured on a per-device basis):
|
// Device policy settings (can only be configured on a per-device basis):
|
||||||
setting.NewDefinition(AllowedSuggestedExitNodes, setting.DeviceSetting, setting.StringListValue),
|
setting.NewDefinition(pkey.AllowedSuggestedExitNodes, setting.DeviceSetting, setting.StringListValue),
|
||||||
setting.NewDefinition(AllowExitNodeOverride, setting.DeviceSetting, setting.BooleanValue),
|
setting.NewDefinition(pkey.AllowExitNodeOverride, setting.DeviceSetting, setting.BooleanValue),
|
||||||
setting.NewDefinition(AlwaysOn, setting.DeviceSetting, setting.BooleanValue),
|
setting.NewDefinition(pkey.AlwaysOn, setting.DeviceSetting, setting.BooleanValue),
|
||||||
setting.NewDefinition(AlwaysOnOverrideWithReason, setting.DeviceSetting, setting.BooleanValue),
|
setting.NewDefinition(pkey.AlwaysOnOverrideWithReason, setting.DeviceSetting, setting.BooleanValue),
|
||||||
setting.NewDefinition(ApplyUpdates, setting.DeviceSetting, setting.PreferenceOptionValue),
|
setting.NewDefinition(pkey.ApplyUpdates, setting.DeviceSetting, setting.PreferenceOptionValue),
|
||||||
setting.NewDefinition(AuthKey, setting.DeviceSetting, setting.StringValue),
|
setting.NewDefinition(pkey.AuthKey, setting.DeviceSetting, setting.StringValue),
|
||||||
setting.NewDefinition(CheckUpdates, setting.DeviceSetting, setting.PreferenceOptionValue),
|
setting.NewDefinition(pkey.CheckUpdates, setting.DeviceSetting, setting.PreferenceOptionValue),
|
||||||
setting.NewDefinition(ControlURL, setting.DeviceSetting, setting.StringValue),
|
setting.NewDefinition(pkey.ControlURL, setting.DeviceSetting, setting.StringValue),
|
||||||
setting.NewDefinition(DeviceSerialNumber, setting.DeviceSetting, setting.StringValue),
|
setting.NewDefinition(pkey.DeviceSerialNumber, setting.DeviceSetting, setting.StringValue),
|
||||||
setting.NewDefinition(EnableDNSRegistration, setting.DeviceSetting, setting.PreferenceOptionValue),
|
setting.NewDefinition(pkey.EnableDNSRegistration, setting.DeviceSetting, setting.PreferenceOptionValue),
|
||||||
setting.NewDefinition(EnableIncomingConnections, setting.DeviceSetting, setting.PreferenceOptionValue),
|
setting.NewDefinition(pkey.EnableIncomingConnections, setting.DeviceSetting, setting.PreferenceOptionValue),
|
||||||
setting.NewDefinition(EnableRunExitNode, setting.DeviceSetting, setting.PreferenceOptionValue),
|
setting.NewDefinition(pkey.EnableRunExitNode, setting.DeviceSetting, setting.PreferenceOptionValue),
|
||||||
setting.NewDefinition(EnableServerMode, setting.DeviceSetting, setting.PreferenceOptionValue),
|
setting.NewDefinition(pkey.EnableServerMode, setting.DeviceSetting, setting.PreferenceOptionValue),
|
||||||
setting.NewDefinition(EnableTailscaleDNS, setting.DeviceSetting, setting.PreferenceOptionValue),
|
setting.NewDefinition(pkey.EnableTailscaleDNS, setting.DeviceSetting, setting.PreferenceOptionValue),
|
||||||
setting.NewDefinition(EnableTailscaleSubnets, setting.DeviceSetting, setting.PreferenceOptionValue),
|
setting.NewDefinition(pkey.EnableTailscaleSubnets, setting.DeviceSetting, setting.PreferenceOptionValue),
|
||||||
setting.NewDefinition(ExitNodeAllowLANAccess, setting.DeviceSetting, setting.PreferenceOptionValue),
|
setting.NewDefinition(pkey.ExitNodeAllowLANAccess, setting.DeviceSetting, setting.PreferenceOptionValue),
|
||||||
setting.NewDefinition(ExitNodeID, setting.DeviceSetting, setting.StringValue),
|
setting.NewDefinition(pkey.ExitNodeID, setting.DeviceSetting, setting.StringValue),
|
||||||
setting.NewDefinition(ExitNodeIP, setting.DeviceSetting, setting.StringValue),
|
setting.NewDefinition(pkey.ExitNodeIP, setting.DeviceSetting, setting.StringValue),
|
||||||
setting.NewDefinition(FlushDNSOnSessionUnlock, setting.DeviceSetting, setting.BooleanValue),
|
setting.NewDefinition(pkey.FlushDNSOnSessionUnlock, setting.DeviceSetting, setting.BooleanValue),
|
||||||
setting.NewDefinition(EncryptState, setting.DeviceSetting, setting.BooleanValue),
|
setting.NewDefinition(pkey.EncryptState, setting.DeviceSetting, setting.BooleanValue),
|
||||||
setting.NewDefinition(Hostname, setting.DeviceSetting, setting.StringValue),
|
setting.NewDefinition(pkey.Hostname, setting.DeviceSetting, setting.StringValue),
|
||||||
setting.NewDefinition(LogSCMInteractions, setting.DeviceSetting, setting.BooleanValue),
|
setting.NewDefinition(pkey.LogSCMInteractions, setting.DeviceSetting, setting.BooleanValue),
|
||||||
setting.NewDefinition(LogTarget, setting.DeviceSetting, setting.StringValue),
|
setting.NewDefinition(pkey.LogTarget, setting.DeviceSetting, setting.StringValue),
|
||||||
setting.NewDefinition(MachineCertificateSubject, setting.DeviceSetting, setting.StringValue),
|
setting.NewDefinition(pkey.MachineCertificateSubject, setting.DeviceSetting, setting.StringValue),
|
||||||
setting.NewDefinition(PostureChecking, setting.DeviceSetting, setting.PreferenceOptionValue),
|
setting.NewDefinition(pkey.PostureChecking, setting.DeviceSetting, setting.PreferenceOptionValue),
|
||||||
setting.NewDefinition(ReconnectAfter, setting.DeviceSetting, setting.DurationValue),
|
setting.NewDefinition(pkey.ReconnectAfter, setting.DeviceSetting, setting.DurationValue),
|
||||||
setting.NewDefinition(Tailnet, setting.DeviceSetting, setting.StringValue),
|
setting.NewDefinition(pkey.Tailnet, setting.DeviceSetting, setting.StringValue),
|
||||||
|
|
||||||
// User policy settings (can be configured on a user- or device-basis):
|
// User policy settings (can be configured on a user- or device-basis):
|
||||||
setting.NewDefinition(AdminConsoleVisibility, setting.UserSetting, setting.VisibilityValue),
|
setting.NewDefinition(pkey.AdminConsoleVisibility, setting.UserSetting, setting.VisibilityValue),
|
||||||
setting.NewDefinition(AutoUpdateVisibility, setting.UserSetting, setting.VisibilityValue),
|
setting.NewDefinition(pkey.AutoUpdateVisibility, setting.UserSetting, setting.VisibilityValue),
|
||||||
setting.NewDefinition(ExitNodeMenuVisibility, setting.UserSetting, setting.VisibilityValue),
|
setting.NewDefinition(pkey.ExitNodeMenuVisibility, setting.UserSetting, setting.VisibilityValue),
|
||||||
setting.NewDefinition(KeyExpirationNoticeTime, setting.UserSetting, setting.DurationValue),
|
setting.NewDefinition(pkey.KeyExpirationNoticeTime, setting.UserSetting, setting.DurationValue),
|
||||||
setting.NewDefinition(ManagedByCaption, setting.UserSetting, setting.StringValue),
|
setting.NewDefinition(pkey.ManagedByCaption, setting.UserSetting, setting.StringValue),
|
||||||
setting.NewDefinition(ManagedByOrganizationName, setting.UserSetting, setting.StringValue),
|
setting.NewDefinition(pkey.ManagedByOrganizationName, setting.UserSetting, setting.StringValue),
|
||||||
setting.NewDefinition(ManagedByURL, setting.UserSetting, setting.StringValue),
|
setting.NewDefinition(pkey.ManagedByURL, setting.UserSetting, setting.StringValue),
|
||||||
setting.NewDefinition(NetworkDevicesVisibility, setting.UserSetting, setting.VisibilityValue),
|
setting.NewDefinition(pkey.NetworkDevicesVisibility, setting.UserSetting, setting.VisibilityValue),
|
||||||
setting.NewDefinition(PreferencesMenuVisibility, setting.UserSetting, setting.VisibilityValue),
|
setting.NewDefinition(pkey.PreferencesMenuVisibility, setting.UserSetting, setting.VisibilityValue),
|
||||||
setting.NewDefinition(ResetToDefaultsVisibility, setting.UserSetting, setting.VisibilityValue),
|
setting.NewDefinition(pkey.ResetToDefaultsVisibility, setting.UserSetting, setting.VisibilityValue),
|
||||||
setting.NewDefinition(RunExitNodeVisibility, setting.UserSetting, setting.VisibilityValue),
|
setting.NewDefinition(pkey.RunExitNodeVisibility, setting.UserSetting, setting.VisibilityValue),
|
||||||
setting.NewDefinition(SuggestedExitNodeVisibility, setting.UserSetting, setting.VisibilityValue),
|
setting.NewDefinition(pkey.SuggestedExitNodeVisibility, setting.UserSetting, setting.VisibilityValue),
|
||||||
setting.NewDefinition(TestMenuVisibility, setting.UserSetting, setting.VisibilityValue),
|
setting.NewDefinition(pkey.TestMenuVisibility, setting.UserSetting, setting.VisibilityValue),
|
||||||
setting.NewDefinition(UpdateMenuVisibility, setting.UserSetting, setting.VisibilityValue),
|
setting.NewDefinition(pkey.UpdateMenuVisibility, setting.UserSetting, setting.VisibilityValue),
|
||||||
setting.NewDefinition(OnboardingFlowVisibility, setting.UserSetting, setting.VisibilityValue),
|
setting.NewDefinition(pkey.OnboardingFlowVisibility, setting.UserSetting, setting.VisibilityValue),
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@@ -248,7 +83,7 @@ var implicitDefinitionMap lazy.SyncValue[setting.DefinitionMap]
|
|||||||
// WellKnownSettingDefinition returns a well-known, implicit setting definition by its key,
|
// WellKnownSettingDefinition returns a well-known, implicit setting definition by its key,
|
||||||
// or an [ErrNoSuchKey] if a policy setting with the specified key does not exist
|
// or an [ErrNoSuchKey] if a policy setting with the specified key does not exist
|
||||||
// among implicit policy definitions.
|
// among implicit policy definitions.
|
||||||
func WellKnownSettingDefinition(k Key) (*setting.Definition, error) {
|
func WellKnownSettingDefinition(k pkey.Key) (*setting.Definition, error) {
|
||||||
m, err := implicitDefinitionMap.GetErr(func() (setting.DefinitionMap, error) {
|
m, err := implicitDefinitionMap.GetErr(func() (setting.DefinitionMap, error) {
|
||||||
return setting.DefinitionMapOf(implicitDefinitions)
|
return setting.DefinitionMapOf(implicitDefinitions)
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -14,14 +14,19 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"tailscale.com/util/syspolicy/pkey"
|
||||||
"tailscale.com/util/syspolicy/setting"
|
"tailscale.com/util/syspolicy/setting"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestKnownKeysRegistered(t *testing.T) {
|
func TestKnownKeysRegistered(t *testing.T) {
|
||||||
keyConsts, err := listStringConsts[Key]("policy_keys.go")
|
const file = "pkey/pkey.go"
|
||||||
|
keyConsts, err := listStringConsts[pkey.Key](file)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("listStringConsts failed: %v", err)
|
t.Fatalf("listStringConsts failed: %v", err)
|
||||||
}
|
}
|
||||||
|
if len(keyConsts) == 0 {
|
||||||
|
t.Fatalf("no key constants found in %s", file)
|
||||||
|
}
|
||||||
|
|
||||||
m, err := setting.DefinitionMapOf(implicitDefinitions)
|
m, err := setting.DefinitionMapOf(implicitDefinitions)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import (
|
|||||||
|
|
||||||
"tailscale.com/util/set"
|
"tailscale.com/util/set"
|
||||||
"tailscale.com/util/syspolicy/internal/loggerx"
|
"tailscale.com/util/syspolicy/internal/loggerx"
|
||||||
|
"tailscale.com/util/syspolicy/pkey"
|
||||||
"tailscale.com/util/syspolicy/setting"
|
"tailscale.com/util/syspolicy/setting"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -37,8 +38,8 @@ func (c PolicyChange) Old() *setting.Snapshot {
|
|||||||
return c.snapshots.Old
|
return c.snapshots.Old
|
||||||
}
|
}
|
||||||
|
|
||||||
// HasChanged reports whether a policy setting with the specified [setting.Key], has changed.
|
// HasChanged reports whether a policy setting with the specified [pkey.Key], has changed.
|
||||||
func (c PolicyChange) HasChanged(key setting.Key) bool {
|
func (c PolicyChange) HasChanged(key pkey.Key) bool {
|
||||||
new, newErr := c.snapshots.New.GetErr(key)
|
new, newErr := c.snapshots.New.GetErr(key)
|
||||||
old, oldErr := c.snapshots.Old.GetErr(key)
|
old, oldErr := c.snapshots.Old.GetErr(key)
|
||||||
if newErr != nil && oldErr != nil {
|
if newErr != nil && oldErr != nil {
|
||||||
@@ -60,7 +61,7 @@ func (c PolicyChange) HasChanged(key setting.Key) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// HasChangedAnyOf reports whether any of the specified policy settings has changed.
|
// HasChangedAnyOf reports whether any of the specified policy settings has changed.
|
||||||
func (c PolicyChange) HasChangedAnyOf(keys ...setting.Key) bool {
|
func (c PolicyChange) HasChangedAnyOf(keys ...pkey.Key) bool {
|
||||||
return slices.ContainsFunc(keys, c.HasChanged)
|
return slices.ContainsFunc(keys, c.HasChanged)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import (
|
|||||||
"github.com/google/go-cmp/cmp"
|
"github.com/google/go-cmp/cmp"
|
||||||
"github.com/google/go-cmp/cmp/cmpopts"
|
"github.com/google/go-cmp/cmp/cmpopts"
|
||||||
"tailscale.com/tstest"
|
"tailscale.com/tstest"
|
||||||
|
"tailscale.com/util/syspolicy/pkey"
|
||||||
"tailscale.com/util/syspolicy/setting"
|
"tailscale.com/util/syspolicy/setting"
|
||||||
|
|
||||||
"tailscale.com/util/syspolicy/source"
|
"tailscale.com/util/syspolicy/source"
|
||||||
@@ -80,7 +81,7 @@ func TestRegisterSourceAndGetEffectivePolicy(t *testing.T) {
|
|||||||
type sourceConfig struct {
|
type sourceConfig struct {
|
||||||
name string
|
name string
|
||||||
scope setting.PolicyScope
|
scope setting.PolicyScope
|
||||||
settingKey setting.Key
|
settingKey pkey.Key
|
||||||
settingValue string
|
settingValue string
|
||||||
wantEffective bool
|
wantEffective bool
|
||||||
}
|
}
|
||||||
@@ -113,7 +114,7 @@ func TestRegisterSourceAndGetEffectivePolicy(t *testing.T) {
|
|||||||
wantEffective: true,
|
wantEffective: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
wantSnapshot: setting.NewSnapshot(map[setting.Key]setting.RawItem{
|
wantSnapshot: setting.NewSnapshot(map[pkey.Key]setting.RawItem{
|
||||||
"TestKeyA": setting.RawItemWith("TestValueA", nil, setting.NewNamedOrigin("TestSourceA", setting.DeviceScope)),
|
"TestKeyA": setting.RawItemWith("TestValueA", nil, setting.NewNamedOrigin("TestSourceA", setting.DeviceScope)),
|
||||||
}, setting.NewNamedOrigin("TestSourceA", setting.DeviceScope)),
|
}, setting.NewNamedOrigin("TestSourceA", setting.DeviceScope)),
|
||||||
},
|
},
|
||||||
@@ -129,7 +130,7 @@ func TestRegisterSourceAndGetEffectivePolicy(t *testing.T) {
|
|||||||
wantEffective: true,
|
wantEffective: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
wantSnapshot: setting.NewSnapshot(map[setting.Key]setting.RawItem{
|
wantSnapshot: setting.NewSnapshot(map[pkey.Key]setting.RawItem{
|
||||||
"TestKeyA": setting.RawItemWith("TestValueA", nil, setting.NewNamedOrigin("TestSourceA", setting.DeviceScope)),
|
"TestKeyA": setting.RawItemWith("TestValueA", nil, setting.NewNamedOrigin("TestSourceA", setting.DeviceScope)),
|
||||||
}, setting.NewNamedOrigin("TestSourceA", setting.DeviceScope)),
|
}, setting.NewNamedOrigin("TestSourceA", setting.DeviceScope)),
|
||||||
},
|
},
|
||||||
@@ -159,7 +160,7 @@ func TestRegisterSourceAndGetEffectivePolicy(t *testing.T) {
|
|||||||
wantEffective: true,
|
wantEffective: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
wantSnapshot: setting.NewSnapshot(map[setting.Key]setting.RawItem{
|
wantSnapshot: setting.NewSnapshot(map[pkey.Key]setting.RawItem{
|
||||||
"TestKeyA": setting.RawItemWith("TestValueA", nil, setting.NewNamedOrigin("TestSourceA", setting.DeviceScope)),
|
"TestKeyA": setting.RawItemWith("TestValueA", nil, setting.NewNamedOrigin("TestSourceA", setting.DeviceScope)),
|
||||||
"TestKeyB": setting.RawItemWith("TestValueB", nil, setting.NewNamedOrigin("TestSourceB", setting.DeviceScope)),
|
"TestKeyB": setting.RawItemWith("TestValueB", nil, setting.NewNamedOrigin("TestSourceB", setting.DeviceScope)),
|
||||||
"TestKeyC": setting.RawItemWith("TestValueC", nil, setting.NewNamedOrigin("TestSourceC", setting.DeviceScope)),
|
"TestKeyC": setting.RawItemWith("TestValueC", nil, setting.NewNamedOrigin("TestSourceC", setting.DeviceScope)),
|
||||||
@@ -191,7 +192,7 @@ func TestRegisterSourceAndGetEffectivePolicy(t *testing.T) {
|
|||||||
wantEffective: true,
|
wantEffective: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
wantSnapshot: setting.NewSnapshot(map[setting.Key]setting.RawItem{
|
wantSnapshot: setting.NewSnapshot(map[pkey.Key]setting.RawItem{
|
||||||
"TestKeyA": setting.RawItemWith("TestValueC", nil, setting.NewNamedOrigin("TestSourceC", setting.DeviceScope)),
|
"TestKeyA": setting.RawItemWith("TestValueC", nil, setting.NewNamedOrigin("TestSourceC", setting.DeviceScope)),
|
||||||
"TestKeyB": setting.RawItemWith("TestValueB", nil, setting.NewNamedOrigin("TestSourceB", setting.DeviceScope)),
|
"TestKeyB": setting.RawItemWith("TestValueB", nil, setting.NewNamedOrigin("TestSourceB", setting.DeviceScope)),
|
||||||
}, setting.DeviceScope),
|
}, setting.DeviceScope),
|
||||||
@@ -245,7 +246,7 @@ func TestRegisterSourceAndGetEffectivePolicy(t *testing.T) {
|
|||||||
wantEffective: true,
|
wantEffective: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
wantSnapshot: setting.NewSnapshot(map[setting.Key]setting.RawItem{
|
wantSnapshot: setting.NewSnapshot(map[pkey.Key]setting.RawItem{
|
||||||
"TestKeyA": setting.RawItemWith("TestValueF", nil, setting.NewNamedOrigin("TestSourceF", setting.DeviceScope)),
|
"TestKeyA": setting.RawItemWith("TestValueF", nil, setting.NewNamedOrigin("TestSourceF", setting.DeviceScope)),
|
||||||
"TestKeyB": setting.RawItemWith("TestValueB", nil, setting.NewNamedOrigin("TestSourceB", setting.DeviceScope)),
|
"TestKeyB": setting.RawItemWith("TestValueB", nil, setting.NewNamedOrigin("TestSourceB", setting.DeviceScope)),
|
||||||
"TestKeyC": setting.RawItemWith("TestValueE", nil, setting.NewNamedOrigin("TestSourceE", setting.DeviceScope)),
|
"TestKeyC": setting.RawItemWith("TestValueE", nil, setting.NewNamedOrigin("TestSourceE", setting.DeviceScope)),
|
||||||
@@ -263,7 +264,7 @@ func TestRegisterSourceAndGetEffectivePolicy(t *testing.T) {
|
|||||||
wantEffective: true,
|
wantEffective: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
wantSnapshot: setting.NewSnapshot(map[setting.Key]setting.RawItem{
|
wantSnapshot: setting.NewSnapshot(map[pkey.Key]setting.RawItem{
|
||||||
"TestKeyA": setting.RawItemWith("DeviceValue", nil, setting.NewNamedOrigin("TestSourceDevice", setting.DeviceScope)),
|
"TestKeyA": setting.RawItemWith("DeviceValue", nil, setting.NewNamedOrigin("TestSourceDevice", setting.DeviceScope)),
|
||||||
}, setting.CurrentUserScope, setting.NewNamedOrigin("TestSourceDevice", setting.DeviceScope)),
|
}, setting.CurrentUserScope, setting.NewNamedOrigin("TestSourceDevice", setting.DeviceScope)),
|
||||||
},
|
},
|
||||||
@@ -288,7 +289,7 @@ func TestRegisterSourceAndGetEffectivePolicy(t *testing.T) {
|
|||||||
wantEffective: true,
|
wantEffective: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
wantSnapshot: setting.NewSnapshot(map[setting.Key]setting.RawItem{
|
wantSnapshot: setting.NewSnapshot(map[pkey.Key]setting.RawItem{
|
||||||
"TestKeyA": setting.RawItemWith("DeviceValue", nil, setting.NewNamedOrigin("TestSourceDevice", setting.DeviceScope)),
|
"TestKeyA": setting.RawItemWith("DeviceValue", nil, setting.NewNamedOrigin("TestSourceDevice", setting.DeviceScope)),
|
||||||
"TestKeyB": setting.RawItemWith("UserValue", nil, setting.NewNamedOrigin("TestSourceUser", setting.CurrentUserScope)),
|
"TestKeyB": setting.RawItemWith("UserValue", nil, setting.NewNamedOrigin("TestSourceUser", setting.CurrentUserScope)),
|
||||||
}, setting.CurrentUserScope),
|
}, setting.CurrentUserScope),
|
||||||
@@ -321,7 +322,7 @@ func TestRegisterSourceAndGetEffectivePolicy(t *testing.T) {
|
|||||||
wantEffective: true,
|
wantEffective: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
wantSnapshot: setting.NewSnapshot(map[setting.Key]setting.RawItem{
|
wantSnapshot: setting.NewSnapshot(map[pkey.Key]setting.RawItem{
|
||||||
"TestKeyA": setting.RawItemWith("DeviceValue", nil, setting.NewNamedOrigin("TestSourceDevice", setting.DeviceScope)),
|
"TestKeyA": setting.RawItemWith("DeviceValue", nil, setting.NewNamedOrigin("TestSourceDevice", setting.DeviceScope)),
|
||||||
"TestKeyB": setting.RawItemWith("ProfileValue", nil, setting.NewNamedOrigin("TestSourceProfile", setting.CurrentProfileScope)),
|
"TestKeyB": setting.RawItemWith("ProfileValue", nil, setting.NewNamedOrigin("TestSourceProfile", setting.CurrentProfileScope)),
|
||||||
}, setting.CurrentUserScope),
|
}, setting.CurrentUserScope),
|
||||||
@@ -347,7 +348,7 @@ func TestRegisterSourceAndGetEffectivePolicy(t *testing.T) {
|
|||||||
wantEffective: false, // Registering a user source should have no impact on the device policy.
|
wantEffective: false, // Registering a user source should have no impact on the device policy.
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
wantSnapshot: setting.NewSnapshot(map[setting.Key]setting.RawItem{
|
wantSnapshot: setting.NewSnapshot(map[pkey.Key]setting.RawItem{
|
||||||
"TestKeyA": setting.RawItemWith("DeviceValue", nil, setting.NewNamedOrigin("TestSourceDevice", setting.DeviceScope)),
|
"TestKeyA": setting.RawItemWith("DeviceValue", nil, setting.NewNamedOrigin("TestSourceDevice", setting.DeviceScope)),
|
||||||
}, setting.NewNamedOrigin("TestSourceDevice", setting.DeviceScope)),
|
}, setting.NewNamedOrigin("TestSourceDevice", setting.DeviceScope)),
|
||||||
},
|
},
|
||||||
@@ -497,61 +498,61 @@ func TestPolicyFor(t *testing.T) {
|
|||||||
func TestPolicyChangeHasChanged(t *testing.T) {
|
func TestPolicyChangeHasChanged(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
old, new map[setting.Key]setting.RawItem
|
old, new map[pkey.Key]setting.RawItem
|
||||||
wantChanged []setting.Key
|
wantChanged []pkey.Key
|
||||||
wantUnchanged []setting.Key
|
wantUnchanged []pkey.Key
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "String-Settings",
|
name: "String-Settings",
|
||||||
old: map[setting.Key]setting.RawItem{
|
old: map[pkey.Key]setting.RawItem{
|
||||||
"ChangedSetting": setting.RawItemOf("Old"),
|
"ChangedSetting": setting.RawItemOf("Old"),
|
||||||
"UnchangedSetting": setting.RawItemOf("Value"),
|
"UnchangedSetting": setting.RawItemOf("Value"),
|
||||||
},
|
},
|
||||||
new: map[setting.Key]setting.RawItem{
|
new: map[pkey.Key]setting.RawItem{
|
||||||
"ChangedSetting": setting.RawItemOf("New"),
|
"ChangedSetting": setting.RawItemOf("New"),
|
||||||
"UnchangedSetting": setting.RawItemOf("Value"),
|
"UnchangedSetting": setting.RawItemOf("Value"),
|
||||||
},
|
},
|
||||||
wantChanged: []setting.Key{"ChangedSetting"},
|
wantChanged: []pkey.Key{"ChangedSetting"},
|
||||||
wantUnchanged: []setting.Key{"UnchangedSetting"},
|
wantUnchanged: []pkey.Key{"UnchangedSetting"},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "UInt64-Settings",
|
name: "UInt64-Settings",
|
||||||
old: map[setting.Key]setting.RawItem{
|
old: map[pkey.Key]setting.RawItem{
|
||||||
"ChangedSetting": setting.RawItemOf(uint64(0)),
|
"ChangedSetting": setting.RawItemOf(uint64(0)),
|
||||||
"UnchangedSetting": setting.RawItemOf(uint64(42)),
|
"UnchangedSetting": setting.RawItemOf(uint64(42)),
|
||||||
},
|
},
|
||||||
new: map[setting.Key]setting.RawItem{
|
new: map[pkey.Key]setting.RawItem{
|
||||||
"ChangedSetting": setting.RawItemOf(uint64(1)),
|
"ChangedSetting": setting.RawItemOf(uint64(1)),
|
||||||
"UnchangedSetting": setting.RawItemOf(uint64(42)),
|
"UnchangedSetting": setting.RawItemOf(uint64(42)),
|
||||||
},
|
},
|
||||||
wantChanged: []setting.Key{"ChangedSetting"},
|
wantChanged: []pkey.Key{"ChangedSetting"},
|
||||||
wantUnchanged: []setting.Key{"UnchangedSetting"},
|
wantUnchanged: []pkey.Key{"UnchangedSetting"},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "StringSlice-Settings",
|
name: "StringSlice-Settings",
|
||||||
old: map[setting.Key]setting.RawItem{
|
old: map[pkey.Key]setting.RawItem{
|
||||||
"ChangedSetting": setting.RawItemOf([]string{"Chicago"}),
|
"ChangedSetting": setting.RawItemOf([]string{"Chicago"}),
|
||||||
"UnchangedSetting": setting.RawItemOf([]string{"String1", "String2"}),
|
"UnchangedSetting": setting.RawItemOf([]string{"String1", "String2"}),
|
||||||
},
|
},
|
||||||
new: map[setting.Key]setting.RawItem{
|
new: map[pkey.Key]setting.RawItem{
|
||||||
"ChangedSetting": setting.RawItemOf([]string{"New York"}),
|
"ChangedSetting": setting.RawItemOf([]string{"New York"}),
|
||||||
"UnchangedSetting": setting.RawItemOf([]string{"String1", "String2"}),
|
"UnchangedSetting": setting.RawItemOf([]string{"String1", "String2"}),
|
||||||
},
|
},
|
||||||
wantChanged: []setting.Key{"ChangedSetting"},
|
wantChanged: []pkey.Key{"ChangedSetting"},
|
||||||
wantUnchanged: []setting.Key{"UnchangedSetting"},
|
wantUnchanged: []pkey.Key{"UnchangedSetting"},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Int8-Settings", // We don't have actual int8 settings, but this should still work.
|
name: "Int8-Settings", // We don't have actual int8 settings, but this should still work.
|
||||||
old: map[setting.Key]setting.RawItem{
|
old: map[pkey.Key]setting.RawItem{
|
||||||
"ChangedSetting": setting.RawItemOf(int8(0)),
|
"ChangedSetting": setting.RawItemOf(int8(0)),
|
||||||
"UnchangedSetting": setting.RawItemOf(int8(42)),
|
"UnchangedSetting": setting.RawItemOf(int8(42)),
|
||||||
},
|
},
|
||||||
new: map[setting.Key]setting.RawItem{
|
new: map[pkey.Key]setting.RawItem{
|
||||||
"ChangedSetting": setting.RawItemOf(int8(1)),
|
"ChangedSetting": setting.RawItemOf(int8(1)),
|
||||||
"UnchangedSetting": setting.RawItemOf(int8(42)),
|
"UnchangedSetting": setting.RawItemOf(int8(42)),
|
||||||
},
|
},
|
||||||
wantChanged: []setting.Key{"ChangedSetting"},
|
wantChanged: []pkey.Key{"ChangedSetting"},
|
||||||
wantUnchanged: []setting.Key{"UnchangedSetting"},
|
wantUnchanged: []pkey.Key{"UnchangedSetting"},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
|
|||||||
@@ -1,13 +0,0 @@
|
|||||||
// Copyright (c) Tailscale Inc & AUTHORS
|
|
||||||
// SPDX-License-Identifier: BSD-3-Clause
|
|
||||||
|
|
||||||
package setting
|
|
||||||
|
|
||||||
// Key is a string that uniquely identifies a policy and must remain unchanged
|
|
||||||
// once established and documented for a given policy setting. It may contain
|
|
||||||
// alphanumeric characters and zero or more [KeyPathSeparator]s to group
|
|
||||||
// individual policy settings into categories.
|
|
||||||
type Key string
|
|
||||||
|
|
||||||
// KeyPathSeparator allows logical grouping of policy settings into categories.
|
|
||||||
const KeyPathSeparator = '/'
|
|
||||||
@@ -11,6 +11,7 @@ import (
|
|||||||
"github.com/go-json-experiment/json/jsontext"
|
"github.com/go-json-experiment/json/jsontext"
|
||||||
"tailscale.com/types/opt"
|
"tailscale.com/types/opt"
|
||||||
"tailscale.com/types/structs"
|
"tailscale.com/types/structs"
|
||||||
|
"tailscale.com/util/syspolicy/pkey"
|
||||||
)
|
)
|
||||||
|
|
||||||
// RawItem contains a raw policy setting value as read from a policy store, or an
|
// RawItem contains a raw policy setting value as read from a policy store, or an
|
||||||
@@ -169,4 +170,4 @@ func (v *RawValue) UnmarshalJSON(b []byte) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// RawValues is a map of keyed setting values that can be read from a JSON.
|
// RawValues is a map of keyed setting values that can be read from a JSON.
|
||||||
type RawValues map[Key]RawValue
|
type RawValues map[pkey.Key]RawValue
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import (
|
|||||||
|
|
||||||
"tailscale.com/types/lazy"
|
"tailscale.com/types/lazy"
|
||||||
"tailscale.com/util/syspolicy/internal"
|
"tailscale.com/util/syspolicy/internal"
|
||||||
|
"tailscale.com/util/syspolicy/pkey"
|
||||||
"tailscale.com/util/testenv"
|
"tailscale.com/util/testenv"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -134,7 +135,7 @@ type ValueType interface {
|
|||||||
|
|
||||||
// Definition defines policy key, scope and value type.
|
// Definition defines policy key, scope and value type.
|
||||||
type Definition struct {
|
type Definition struct {
|
||||||
key Key
|
key pkey.Key
|
||||||
scope Scope
|
scope Scope
|
||||||
typ Type
|
typ Type
|
||||||
platforms PlatformList
|
platforms PlatformList
|
||||||
@@ -142,12 +143,12 @@ type Definition struct {
|
|||||||
|
|
||||||
// NewDefinition returns a new [Definition] with the specified
|
// NewDefinition returns a new [Definition] with the specified
|
||||||
// key, scope, type and supported platforms (see [PlatformList]).
|
// key, scope, type and supported platforms (see [PlatformList]).
|
||||||
func NewDefinition(k Key, s Scope, t Type, platforms ...string) *Definition {
|
func NewDefinition(k pkey.Key, s Scope, t Type, platforms ...string) *Definition {
|
||||||
return &Definition{key: k, scope: s, typ: t, platforms: platforms}
|
return &Definition{key: k, scope: s, typ: t, platforms: platforms}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Key returns a policy setting's identifier.
|
// Key returns a policy setting's identifier.
|
||||||
func (d *Definition) Key() Key {
|
func (d *Definition) Key() pkey.Key {
|
||||||
if d == nil {
|
if d == nil {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
@@ -208,7 +209,7 @@ func (d *Definition) Equal(d2 *Definition) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// DefinitionMap is a map of setting [Definition] by [Key].
|
// DefinitionMap is a map of setting [Definition] by [Key].
|
||||||
type DefinitionMap map[Key]*Definition
|
type DefinitionMap map[pkey.Key]*Definition
|
||||||
|
|
||||||
var (
|
var (
|
||||||
definitions lazy.SyncValue[DefinitionMap]
|
definitions lazy.SyncValue[DefinitionMap]
|
||||||
@@ -224,7 +225,7 @@ var (
|
|||||||
// invoking any functions that use the registered policy definitions. This
|
// invoking any functions that use the registered policy definitions. This
|
||||||
// includes calling [Definitions] or [DefinitionOf] directly, or reading any
|
// includes calling [Definitions] or [DefinitionOf] directly, or reading any
|
||||||
// policy settings via syspolicy.
|
// policy settings via syspolicy.
|
||||||
func Register(k Key, s Scope, t Type, platforms ...string) {
|
func Register(k pkey.Key, s Scope, t Type, platforms ...string) {
|
||||||
RegisterDefinition(NewDefinition(k, s, t, platforms...))
|
RegisterDefinition(NewDefinition(k, s, t, platforms...))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -290,7 +291,7 @@ func SetDefinitionsForTest(tb testenv.TB, ds ...*Definition) error {
|
|||||||
// DefinitionOf returns a setting definition by key,
|
// DefinitionOf returns a setting definition by key,
|
||||||
// or [ErrNoSuchKey] if the specified key does not exist,
|
// or [ErrNoSuchKey] if the specified key does not exist,
|
||||||
// or an error if there are conflicting policy definitions.
|
// or an error if there are conflicting policy definitions.
|
||||||
func DefinitionOf(k Key) (*Definition, error) {
|
func DefinitionOf(k pkey.Key) (*Definition, error) {
|
||||||
ds, err := settingDefinitions()
|
ds, err := settingDefinitions()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import (
|
|||||||
"tailscale.com/types/lazy"
|
"tailscale.com/types/lazy"
|
||||||
"tailscale.com/types/ptr"
|
"tailscale.com/types/ptr"
|
||||||
"tailscale.com/util/syspolicy/internal"
|
"tailscale.com/util/syspolicy/internal"
|
||||||
|
"tailscale.com/util/syspolicy/pkey"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestSettingDefinition(t *testing.T) {
|
func TestSettingDefinition(t *testing.T) {
|
||||||
@@ -18,7 +19,7 @@ func TestSettingDefinition(t *testing.T) {
|
|||||||
name string
|
name string
|
||||||
setting *Definition
|
setting *Definition
|
||||||
osOverride string
|
osOverride string
|
||||||
wantKey Key
|
wantKey pkey.Key
|
||||||
wantScope Scope
|
wantScope Scope
|
||||||
wantType Type
|
wantType Type
|
||||||
wantIsSupported bool
|
wantIsSupported bool
|
||||||
@@ -163,10 +164,10 @@ func TestSettingDefinition(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestRegisterSettingDefinition(t *testing.T) {
|
func TestRegisterSettingDefinition(t *testing.T) {
|
||||||
const testPolicySettingKey Key = "TestPolicySetting"
|
const testPolicySettingKey pkey.Key = "TestPolicySetting"
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
key Key
|
key pkey.Key
|
||||||
wantEq *Definition
|
wantEq *Definition
|
||||||
wantErr error
|
wantErr error
|
||||||
}{
|
}{
|
||||||
|
|||||||
@@ -15,34 +15,35 @@ import (
|
|||||||
"github.com/go-json-experiment/json/jsontext"
|
"github.com/go-json-experiment/json/jsontext"
|
||||||
xmaps "golang.org/x/exp/maps"
|
xmaps "golang.org/x/exp/maps"
|
||||||
"tailscale.com/util/deephash"
|
"tailscale.com/util/deephash"
|
||||||
|
"tailscale.com/util/syspolicy/pkey"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Snapshot is an immutable collection of ([Key], [RawItem]) pairs, representing
|
// Snapshot is an immutable collection of ([Key], [RawItem]) pairs, representing
|
||||||
// a set of policy settings applied at a specific moment in time.
|
// a set of policy settings applied at a specific moment in time.
|
||||||
// A nil pointer to [Snapshot] is valid.
|
// A nil pointer to [Snapshot] is valid.
|
||||||
type Snapshot struct {
|
type Snapshot struct {
|
||||||
m map[Key]RawItem
|
m map[pkey.Key]RawItem
|
||||||
sig deephash.Sum // of m
|
sig deephash.Sum // of m
|
||||||
summary Summary
|
summary Summary
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewSnapshot returns a new [Snapshot] with the specified items and options.
|
// NewSnapshot returns a new [Snapshot] with the specified items and options.
|
||||||
func NewSnapshot(items map[Key]RawItem, opts ...SummaryOption) *Snapshot {
|
func NewSnapshot(items map[pkey.Key]RawItem, opts ...SummaryOption) *Snapshot {
|
||||||
return &Snapshot{m: xmaps.Clone(items), sig: deephash.Hash(&items), summary: SummaryWith(opts...)}
|
return &Snapshot{m: xmaps.Clone(items), sig: deephash.Hash(&items), summary: SummaryWith(opts...)}
|
||||||
}
|
}
|
||||||
|
|
||||||
// All returns an iterator over policy settings in s. The iteration order is not
|
// All returns an iterator over policy settings in s. The iteration order is not
|
||||||
// specified and is not guaranteed to be the same from one call to the next.
|
// specified and is not guaranteed to be the same from one call to the next.
|
||||||
func (s *Snapshot) All() iter.Seq2[Key, RawItem] {
|
func (s *Snapshot) All() iter.Seq2[pkey.Key, RawItem] {
|
||||||
if s == nil {
|
if s == nil {
|
||||||
return func(yield func(Key, RawItem) bool) {}
|
return func(yield func(pkey.Key, RawItem) bool) {}
|
||||||
}
|
}
|
||||||
return maps.All(s.m)
|
return maps.All(s.m)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get returns the value of the policy setting with the specified key
|
// Get returns the value of the policy setting with the specified key
|
||||||
// or nil if it is not configured or has an error.
|
// or nil if it is not configured or has an error.
|
||||||
func (s *Snapshot) Get(k Key) any {
|
func (s *Snapshot) Get(k pkey.Key) any {
|
||||||
v, _ := s.GetErr(k)
|
v, _ := s.GetErr(k)
|
||||||
return v
|
return v
|
||||||
}
|
}
|
||||||
@@ -50,7 +51,7 @@ func (s *Snapshot) Get(k Key) any {
|
|||||||
// GetErr returns the value of the policy setting with the specified key,
|
// GetErr returns the value of the policy setting with the specified key,
|
||||||
// [ErrNotConfigured] if it is not configured, or an error returned by
|
// [ErrNotConfigured] if it is not configured, or an error returned by
|
||||||
// the policy Store if the policy setting could not be read.
|
// the policy Store if the policy setting could not be read.
|
||||||
func (s *Snapshot) GetErr(k Key) (any, error) {
|
func (s *Snapshot) GetErr(k pkey.Key) (any, error) {
|
||||||
if s != nil {
|
if s != nil {
|
||||||
if s, ok := s.m[k]; ok {
|
if s, ok := s.m[k]; ok {
|
||||||
return s.Value(), s.Error()
|
return s.Value(), s.Error()
|
||||||
@@ -62,7 +63,7 @@ func (s *Snapshot) GetErr(k Key) (any, error) {
|
|||||||
// GetSetting returns the untyped policy setting with the specified key and true
|
// GetSetting returns the untyped policy setting with the specified key and true
|
||||||
// if a policy setting with such key has been configured;
|
// if a policy setting with such key has been configured;
|
||||||
// otherwise, it returns zero, false.
|
// otherwise, it returns zero, false.
|
||||||
func (s *Snapshot) GetSetting(k Key) (setting RawItem, ok bool) {
|
func (s *Snapshot) GetSetting(k pkey.Key) (setting RawItem, ok bool) {
|
||||||
setting, ok = s.m[k]
|
setting, ok = s.m[k]
|
||||||
return setting, ok
|
return setting, ok
|
||||||
}
|
}
|
||||||
@@ -94,9 +95,9 @@ func (s *Snapshot) EqualItems(s2 *Snapshot) bool {
|
|||||||
|
|
||||||
// Keys return an iterator over keys in s. The iteration order is not specified
|
// Keys return an iterator over keys in s. The iteration order is not specified
|
||||||
// and is not guaranteed to be the same from one call to the next.
|
// and is not guaranteed to be the same from one call to the next.
|
||||||
func (s *Snapshot) Keys() iter.Seq[Key] {
|
func (s *Snapshot) Keys() iter.Seq[pkey.Key] {
|
||||||
if s.m == nil {
|
if s.m == nil {
|
||||||
return func(yield func(Key) bool) {}
|
return func(yield func(pkey.Key) bool) {}
|
||||||
}
|
}
|
||||||
return maps.Keys(s.m)
|
return maps.Keys(s.m)
|
||||||
}
|
}
|
||||||
@@ -144,8 +145,8 @@ func (s *Snapshot) String() string {
|
|||||||
|
|
||||||
// snapshotJSON holds JSON-marshallable data for [Snapshot].
|
// snapshotJSON holds JSON-marshallable data for [Snapshot].
|
||||||
type snapshotJSON struct {
|
type snapshotJSON struct {
|
||||||
Summary Summary `json:",omitzero"`
|
Summary Summary `json:",omitzero"`
|
||||||
Settings map[Key]RawItem `json:",omitempty"`
|
Settings map[pkey.Key]RawItem `json:",omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -232,7 +233,7 @@ func MergeSnapshots(snapshot1, snapshot2 *Snapshot) *Snapshot {
|
|||||||
}
|
}
|
||||||
return &Snapshot{snapshot2.m, snapshot2.sig, SummaryWith(summaryOpts...)}
|
return &Snapshot{snapshot2.m, snapshot2.sig, SummaryWith(summaryOpts...)}
|
||||||
}
|
}
|
||||||
m := make(map[Key]RawItem, snapshot1.Len()+snapshot2.Len())
|
m := make(map[pkey.Key]RawItem, snapshot1.Len()+snapshot2.Len())
|
||||||
xmaps.Copy(m, snapshot1.m)
|
xmaps.Copy(m, snapshot1.m)
|
||||||
xmaps.Copy(m, snapshot2.m) // snapshot2 has higher precedence
|
xmaps.Copy(m, snapshot2.m) // snapshot2 has higher precedence
|
||||||
return &Snapshot{m, deephash.Hash(&m), SummaryWith(summaryOpts...)}
|
return &Snapshot{m, deephash.Hash(&m), SummaryWith(summaryOpts...)}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import (
|
|||||||
|
|
||||||
jsonv2 "github.com/go-json-experiment/json"
|
jsonv2 "github.com/go-json-experiment/json"
|
||||||
"tailscale.com/util/syspolicy/internal"
|
"tailscale.com/util/syspolicy/internal"
|
||||||
|
"tailscale.com/util/syspolicy/pkey"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestMergeSnapshots(t *testing.T) {
|
func TestMergeSnapshots(t *testing.T) {
|
||||||
@@ -23,23 +24,23 @@ func TestMergeSnapshots(t *testing.T) {
|
|||||||
name: "both-nil",
|
name: "both-nil",
|
||||||
s1: nil,
|
s1: nil,
|
||||||
s2: nil,
|
s2: nil,
|
||||||
want: NewSnapshot(map[Key]RawItem{}),
|
want: NewSnapshot(map[pkey.Key]RawItem{}),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "both-empty",
|
name: "both-empty",
|
||||||
s1: NewSnapshot(map[Key]RawItem{}),
|
s1: NewSnapshot(map[pkey.Key]RawItem{}),
|
||||||
s2: NewSnapshot(map[Key]RawItem{}),
|
s2: NewSnapshot(map[pkey.Key]RawItem{}),
|
||||||
want: NewSnapshot(map[Key]RawItem{}),
|
want: NewSnapshot(map[pkey.Key]RawItem{}),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "first-nil",
|
name: "first-nil",
|
||||||
s1: nil,
|
s1: nil,
|
||||||
s2: NewSnapshot(map[Key]RawItem{
|
s2: NewSnapshot(map[pkey.Key]RawItem{
|
||||||
"Setting1": RawItemOf(123),
|
"Setting1": RawItemOf(123),
|
||||||
"Setting2": RawItemOf("String"),
|
"Setting2": RawItemOf("String"),
|
||||||
"Setting3": RawItemOf(true),
|
"Setting3": RawItemOf(true),
|
||||||
}),
|
}),
|
||||||
want: NewSnapshot(map[Key]RawItem{
|
want: NewSnapshot(map[pkey.Key]RawItem{
|
||||||
"Setting1": RawItemOf(123),
|
"Setting1": RawItemOf(123),
|
||||||
"Setting2": RawItemOf("String"),
|
"Setting2": RawItemOf("String"),
|
||||||
"Setting3": RawItemOf(true),
|
"Setting3": RawItemOf(true),
|
||||||
@@ -47,13 +48,13 @@ func TestMergeSnapshots(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "first-empty",
|
name: "first-empty",
|
||||||
s1: NewSnapshot(map[Key]RawItem{}),
|
s1: NewSnapshot(map[pkey.Key]RawItem{}),
|
||||||
s2: NewSnapshot(map[Key]RawItem{
|
s2: NewSnapshot(map[pkey.Key]RawItem{
|
||||||
"Setting1": RawItemOf(123),
|
"Setting1": RawItemOf(123),
|
||||||
"Setting2": RawItemOf("String"),
|
"Setting2": RawItemOf("String"),
|
||||||
"Setting3": RawItemOf(false),
|
"Setting3": RawItemOf(false),
|
||||||
}),
|
}),
|
||||||
want: NewSnapshot(map[Key]RawItem{
|
want: NewSnapshot(map[pkey.Key]RawItem{
|
||||||
"Setting1": RawItemOf(123),
|
"Setting1": RawItemOf(123),
|
||||||
"Setting2": RawItemOf("String"),
|
"Setting2": RawItemOf("String"),
|
||||||
"Setting3": RawItemOf(false),
|
"Setting3": RawItemOf(false),
|
||||||
@@ -61,13 +62,13 @@ func TestMergeSnapshots(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "second-nil",
|
name: "second-nil",
|
||||||
s1: NewSnapshot(map[Key]RawItem{
|
s1: NewSnapshot(map[pkey.Key]RawItem{
|
||||||
"Setting1": RawItemOf(123),
|
"Setting1": RawItemOf(123),
|
||||||
"Setting2": RawItemOf("String"),
|
"Setting2": RawItemOf("String"),
|
||||||
"Setting3": RawItemOf(true),
|
"Setting3": RawItemOf(true),
|
||||||
}),
|
}),
|
||||||
s2: nil,
|
s2: nil,
|
||||||
want: NewSnapshot(map[Key]RawItem{
|
want: NewSnapshot(map[pkey.Key]RawItem{
|
||||||
"Setting1": RawItemOf(123),
|
"Setting1": RawItemOf(123),
|
||||||
"Setting2": RawItemOf("String"),
|
"Setting2": RawItemOf("String"),
|
||||||
"Setting3": RawItemOf(true),
|
"Setting3": RawItemOf(true),
|
||||||
@@ -75,13 +76,13 @@ func TestMergeSnapshots(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "second-empty",
|
name: "second-empty",
|
||||||
s1: NewSnapshot(map[Key]RawItem{
|
s1: NewSnapshot(map[pkey.Key]RawItem{
|
||||||
"Setting1": RawItemOf(123),
|
"Setting1": RawItemOf(123),
|
||||||
"Setting2": RawItemOf("String"),
|
"Setting2": RawItemOf("String"),
|
||||||
"Setting3": RawItemOf(false),
|
"Setting3": RawItemOf(false),
|
||||||
}),
|
}),
|
||||||
s2: NewSnapshot(map[Key]RawItem{}),
|
s2: NewSnapshot(map[pkey.Key]RawItem{}),
|
||||||
want: NewSnapshot(map[Key]RawItem{
|
want: NewSnapshot(map[pkey.Key]RawItem{
|
||||||
"Setting1": RawItemOf(123),
|
"Setting1": RawItemOf(123),
|
||||||
"Setting2": RawItemOf("String"),
|
"Setting2": RawItemOf("String"),
|
||||||
"Setting3": RawItemOf(false),
|
"Setting3": RawItemOf(false),
|
||||||
@@ -89,17 +90,17 @@ func TestMergeSnapshots(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "no-conflicts",
|
name: "no-conflicts",
|
||||||
s1: NewSnapshot(map[Key]RawItem{
|
s1: NewSnapshot(map[pkey.Key]RawItem{
|
||||||
"Setting1": RawItemOf(123),
|
"Setting1": RawItemOf(123),
|
||||||
"Setting2": RawItemOf("String"),
|
"Setting2": RawItemOf("String"),
|
||||||
"Setting3": RawItemOf(false),
|
"Setting3": RawItemOf(false),
|
||||||
}),
|
}),
|
||||||
s2: NewSnapshot(map[Key]RawItem{
|
s2: NewSnapshot(map[pkey.Key]RawItem{
|
||||||
"Setting4": RawItemOf(2 * time.Hour),
|
"Setting4": RawItemOf(2 * time.Hour),
|
||||||
"Setting5": RawItemOf(VisibleByPolicy),
|
"Setting5": RawItemOf(VisibleByPolicy),
|
||||||
"Setting6": RawItemOf(ShowChoiceByPolicy),
|
"Setting6": RawItemOf(ShowChoiceByPolicy),
|
||||||
}),
|
}),
|
||||||
want: NewSnapshot(map[Key]RawItem{
|
want: NewSnapshot(map[pkey.Key]RawItem{
|
||||||
"Setting1": RawItemOf(123),
|
"Setting1": RawItemOf(123),
|
||||||
"Setting2": RawItemOf("String"),
|
"Setting2": RawItemOf("String"),
|
||||||
"Setting3": RawItemOf(false),
|
"Setting3": RawItemOf(false),
|
||||||
@@ -110,17 +111,17 @@ func TestMergeSnapshots(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "with-conflicts",
|
name: "with-conflicts",
|
||||||
s1: NewSnapshot(map[Key]RawItem{
|
s1: NewSnapshot(map[pkey.Key]RawItem{
|
||||||
"Setting1": RawItemOf(123),
|
"Setting1": RawItemOf(123),
|
||||||
"Setting2": RawItemOf("String"),
|
"Setting2": RawItemOf("String"),
|
||||||
"Setting3": RawItemOf(true),
|
"Setting3": RawItemOf(true),
|
||||||
}),
|
}),
|
||||||
s2: NewSnapshot(map[Key]RawItem{
|
s2: NewSnapshot(map[pkey.Key]RawItem{
|
||||||
"Setting1": RawItemOf(456),
|
"Setting1": RawItemOf(456),
|
||||||
"Setting3": RawItemOf(false),
|
"Setting3": RawItemOf(false),
|
||||||
"Setting4": RawItemOf(2 * time.Hour),
|
"Setting4": RawItemOf(2 * time.Hour),
|
||||||
}),
|
}),
|
||||||
want: NewSnapshot(map[Key]RawItem{
|
want: NewSnapshot(map[pkey.Key]RawItem{
|
||||||
"Setting1": RawItemOf(456),
|
"Setting1": RawItemOf(456),
|
||||||
"Setting2": RawItemOf("String"),
|
"Setting2": RawItemOf("String"),
|
||||||
"Setting3": RawItemOf(false),
|
"Setting3": RawItemOf(false),
|
||||||
@@ -129,17 +130,17 @@ func TestMergeSnapshots(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "with-scope-first-wins",
|
name: "with-scope-first-wins",
|
||||||
s1: NewSnapshot(map[Key]RawItem{
|
s1: NewSnapshot(map[pkey.Key]RawItem{
|
||||||
"Setting1": RawItemOf(123),
|
"Setting1": RawItemOf(123),
|
||||||
"Setting2": RawItemOf("String"),
|
"Setting2": RawItemOf("String"),
|
||||||
"Setting3": RawItemOf(true),
|
"Setting3": RawItemOf(true),
|
||||||
}, DeviceScope),
|
}, DeviceScope),
|
||||||
s2: NewSnapshot(map[Key]RawItem{
|
s2: NewSnapshot(map[pkey.Key]RawItem{
|
||||||
"Setting1": RawItemOf(456),
|
"Setting1": RawItemOf(456),
|
||||||
"Setting3": RawItemOf(false),
|
"Setting3": RawItemOf(false),
|
||||||
"Setting4": RawItemOf(2 * time.Hour),
|
"Setting4": RawItemOf(2 * time.Hour),
|
||||||
}, CurrentUserScope),
|
}, CurrentUserScope),
|
||||||
want: NewSnapshot(map[Key]RawItem{
|
want: NewSnapshot(map[pkey.Key]RawItem{
|
||||||
"Setting1": RawItemOf(123),
|
"Setting1": RawItemOf(123),
|
||||||
"Setting2": RawItemOf("String"),
|
"Setting2": RawItemOf("String"),
|
||||||
"Setting3": RawItemOf(true),
|
"Setting3": RawItemOf(true),
|
||||||
@@ -148,17 +149,17 @@ func TestMergeSnapshots(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "with-scope-second-wins",
|
name: "with-scope-second-wins",
|
||||||
s1: NewSnapshot(map[Key]RawItem{
|
s1: NewSnapshot(map[pkey.Key]RawItem{
|
||||||
"Setting1": RawItemOf(123),
|
"Setting1": RawItemOf(123),
|
||||||
"Setting2": RawItemOf("String"),
|
"Setting2": RawItemOf("String"),
|
||||||
"Setting3": RawItemOf(true),
|
"Setting3": RawItemOf(true),
|
||||||
}, CurrentUserScope),
|
}, CurrentUserScope),
|
||||||
s2: NewSnapshot(map[Key]RawItem{
|
s2: NewSnapshot(map[pkey.Key]RawItem{
|
||||||
"Setting1": RawItemOf(456),
|
"Setting1": RawItemOf(456),
|
||||||
"Setting3": RawItemOf(false),
|
"Setting3": RawItemOf(false),
|
||||||
"Setting4": RawItemOf(2 * time.Hour),
|
"Setting4": RawItemOf(2 * time.Hour),
|
||||||
}, DeviceScope),
|
}, DeviceScope),
|
||||||
want: NewSnapshot(map[Key]RawItem{
|
want: NewSnapshot(map[pkey.Key]RawItem{
|
||||||
"Setting1": RawItemOf(456),
|
"Setting1": RawItemOf(456),
|
||||||
"Setting2": RawItemOf("String"),
|
"Setting2": RawItemOf("String"),
|
||||||
"Setting3": RawItemOf(false),
|
"Setting3": RawItemOf(false),
|
||||||
@@ -167,18 +168,18 @@ func TestMergeSnapshots(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "with-scope-both-empty",
|
name: "with-scope-both-empty",
|
||||||
s1: NewSnapshot(map[Key]RawItem{}, CurrentUserScope),
|
s1: NewSnapshot(map[pkey.Key]RawItem{}, CurrentUserScope),
|
||||||
s2: NewSnapshot(map[Key]RawItem{}, DeviceScope),
|
s2: NewSnapshot(map[pkey.Key]RawItem{}, DeviceScope),
|
||||||
want: NewSnapshot(map[Key]RawItem{}, CurrentUserScope),
|
want: NewSnapshot(map[pkey.Key]RawItem{}, CurrentUserScope),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "with-scope-first-empty",
|
name: "with-scope-first-empty",
|
||||||
s1: NewSnapshot(map[Key]RawItem{}, CurrentUserScope),
|
s1: NewSnapshot(map[pkey.Key]RawItem{}, CurrentUserScope),
|
||||||
s2: NewSnapshot(map[Key]RawItem{
|
s2: NewSnapshot(map[pkey.Key]RawItem{
|
||||||
"Setting1": RawItemOf(123),
|
"Setting1": RawItemOf(123),
|
||||||
"Setting2": RawItemOf("String"),
|
"Setting2": RawItemOf("String"),
|
||||||
"Setting3": RawItemOf(true)}, DeviceScope, NewNamedOrigin("TestPolicy", DeviceScope)),
|
"Setting3": RawItemOf(true)}, DeviceScope, NewNamedOrigin("TestPolicy", DeviceScope)),
|
||||||
want: NewSnapshot(map[Key]RawItem{
|
want: NewSnapshot(map[pkey.Key]RawItem{
|
||||||
"Setting1": RawItemOf(123),
|
"Setting1": RawItemOf(123),
|
||||||
"Setting2": RawItemOf("String"),
|
"Setting2": RawItemOf("String"),
|
||||||
"Setting3": RawItemOf(true),
|
"Setting3": RawItemOf(true),
|
||||||
@@ -186,13 +187,13 @@ func TestMergeSnapshots(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "with-scope-second-empty",
|
name: "with-scope-second-empty",
|
||||||
s1: NewSnapshot(map[Key]RawItem{
|
s1: NewSnapshot(map[pkey.Key]RawItem{
|
||||||
"Setting1": RawItemOf(123),
|
"Setting1": RawItemOf(123),
|
||||||
"Setting2": RawItemOf("String"),
|
"Setting2": RawItemOf("String"),
|
||||||
"Setting3": RawItemOf(true),
|
"Setting3": RawItemOf(true),
|
||||||
}, CurrentUserScope),
|
}, CurrentUserScope),
|
||||||
s2: NewSnapshot(map[Key]RawItem{}),
|
s2: NewSnapshot(map[pkey.Key]RawItem{}),
|
||||||
want: NewSnapshot(map[Key]RawItem{
|
want: NewSnapshot(map[pkey.Key]RawItem{
|
||||||
"Setting1": RawItemOf(123),
|
"Setting1": RawItemOf(123),
|
||||||
"Setting2": RawItemOf("String"),
|
"Setting2": RawItemOf("String"),
|
||||||
"Setting3": RawItemOf(true),
|
"Setting3": RawItemOf(true),
|
||||||
@@ -226,28 +227,28 @@ func TestSnapshotEqual(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "nil-empty",
|
name: "nil-empty",
|
||||||
s1: nil,
|
s1: nil,
|
||||||
s2: NewSnapshot(map[Key]RawItem{}),
|
s2: NewSnapshot(map[pkey.Key]RawItem{}),
|
||||||
wantEqual: true,
|
wantEqual: true,
|
||||||
wantEqualItems: true,
|
wantEqualItems: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "empty-nil",
|
name: "empty-nil",
|
||||||
s1: NewSnapshot(map[Key]RawItem{}),
|
s1: NewSnapshot(map[pkey.Key]RawItem{}),
|
||||||
s2: nil,
|
s2: nil,
|
||||||
wantEqual: true,
|
wantEqual: true,
|
||||||
wantEqualItems: true,
|
wantEqualItems: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "empty-empty",
|
name: "empty-empty",
|
||||||
s1: NewSnapshot(map[Key]RawItem{}),
|
s1: NewSnapshot(map[pkey.Key]RawItem{}),
|
||||||
s2: NewSnapshot(map[Key]RawItem{}),
|
s2: NewSnapshot(map[pkey.Key]RawItem{}),
|
||||||
wantEqual: true,
|
wantEqual: true,
|
||||||
wantEqualItems: true,
|
wantEqualItems: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "first-nil",
|
name: "first-nil",
|
||||||
s1: nil,
|
s1: nil,
|
||||||
s2: NewSnapshot(map[Key]RawItem{
|
s2: NewSnapshot(map[pkey.Key]RawItem{
|
||||||
"Setting1": RawItemOf(123),
|
"Setting1": RawItemOf(123),
|
||||||
"Setting2": RawItemOf("String"),
|
"Setting2": RawItemOf("String"),
|
||||||
"Setting3": RawItemOf(false),
|
"Setting3": RawItemOf(false),
|
||||||
@@ -257,8 +258,8 @@ func TestSnapshotEqual(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "first-empty",
|
name: "first-empty",
|
||||||
s1: NewSnapshot(map[Key]RawItem{}),
|
s1: NewSnapshot(map[pkey.Key]RawItem{}),
|
||||||
s2: NewSnapshot(map[Key]RawItem{
|
s2: NewSnapshot(map[pkey.Key]RawItem{
|
||||||
"Setting1": RawItemOf(123),
|
"Setting1": RawItemOf(123),
|
||||||
"Setting2": RawItemOf("String"),
|
"Setting2": RawItemOf("String"),
|
||||||
"Setting3": RawItemOf(false),
|
"Setting3": RawItemOf(false),
|
||||||
@@ -268,7 +269,7 @@ func TestSnapshotEqual(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "second-nil",
|
name: "second-nil",
|
||||||
s1: NewSnapshot(map[Key]RawItem{
|
s1: NewSnapshot(map[pkey.Key]RawItem{
|
||||||
"Setting1": RawItemOf(123),
|
"Setting1": RawItemOf(123),
|
||||||
"Setting2": RawItemOf("String"),
|
"Setting2": RawItemOf("String"),
|
||||||
"Setting3": RawItemOf(true),
|
"Setting3": RawItemOf(true),
|
||||||
@@ -279,23 +280,23 @@ func TestSnapshotEqual(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "second-empty",
|
name: "second-empty",
|
||||||
s1: NewSnapshot(map[Key]RawItem{
|
s1: NewSnapshot(map[pkey.Key]RawItem{
|
||||||
"Setting1": RawItemOf(123),
|
"Setting1": RawItemOf(123),
|
||||||
"Setting2": RawItemOf("String"),
|
"Setting2": RawItemOf("String"),
|
||||||
"Setting3": RawItemOf(false),
|
"Setting3": RawItemOf(false),
|
||||||
}),
|
}),
|
||||||
s2: NewSnapshot(map[Key]RawItem{}),
|
s2: NewSnapshot(map[pkey.Key]RawItem{}),
|
||||||
wantEqual: false,
|
wantEqual: false,
|
||||||
wantEqualItems: false,
|
wantEqualItems: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "same-items-same-order-no-scope",
|
name: "same-items-same-order-no-scope",
|
||||||
s1: NewSnapshot(map[Key]RawItem{
|
s1: NewSnapshot(map[pkey.Key]RawItem{
|
||||||
"Setting1": RawItemOf(123),
|
"Setting1": RawItemOf(123),
|
||||||
"Setting2": RawItemOf("String"),
|
"Setting2": RawItemOf("String"),
|
||||||
"Setting3": RawItemOf(false),
|
"Setting3": RawItemOf(false),
|
||||||
}),
|
}),
|
||||||
s2: NewSnapshot(map[Key]RawItem{
|
s2: NewSnapshot(map[pkey.Key]RawItem{
|
||||||
"Setting1": RawItemOf(123),
|
"Setting1": RawItemOf(123),
|
||||||
"Setting2": RawItemOf("String"),
|
"Setting2": RawItemOf("String"),
|
||||||
"Setting3": RawItemOf(false),
|
"Setting3": RawItemOf(false),
|
||||||
@@ -305,12 +306,12 @@ func TestSnapshotEqual(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "same-items-same-order-same-scope",
|
name: "same-items-same-order-same-scope",
|
||||||
s1: NewSnapshot(map[Key]RawItem{
|
s1: NewSnapshot(map[pkey.Key]RawItem{
|
||||||
"Setting1": RawItemOf(123),
|
"Setting1": RawItemOf(123),
|
||||||
"Setting2": RawItemOf("String"),
|
"Setting2": RawItemOf("String"),
|
||||||
"Setting3": RawItemOf(false),
|
"Setting3": RawItemOf(false),
|
||||||
}, DeviceScope),
|
}, DeviceScope),
|
||||||
s2: NewSnapshot(map[Key]RawItem{
|
s2: NewSnapshot(map[pkey.Key]RawItem{
|
||||||
"Setting1": RawItemOf(123),
|
"Setting1": RawItemOf(123),
|
||||||
"Setting2": RawItemOf("String"),
|
"Setting2": RawItemOf("String"),
|
||||||
"Setting3": RawItemOf(false),
|
"Setting3": RawItemOf(false),
|
||||||
@@ -320,12 +321,12 @@ func TestSnapshotEqual(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "same-items-different-order-same-scope",
|
name: "same-items-different-order-same-scope",
|
||||||
s1: NewSnapshot(map[Key]RawItem{
|
s1: NewSnapshot(map[pkey.Key]RawItem{
|
||||||
"Setting1": RawItemOf(123),
|
"Setting1": RawItemOf(123),
|
||||||
"Setting2": RawItemOf("String"),
|
"Setting2": RawItemOf("String"),
|
||||||
"Setting3": RawItemOf(false),
|
"Setting3": RawItemOf(false),
|
||||||
}, DeviceScope),
|
}, DeviceScope),
|
||||||
s2: NewSnapshot(map[Key]RawItem{
|
s2: NewSnapshot(map[pkey.Key]RawItem{
|
||||||
"Setting3": RawItemOf(false),
|
"Setting3": RawItemOf(false),
|
||||||
"Setting1": RawItemOf(123),
|
"Setting1": RawItemOf(123),
|
||||||
"Setting2": RawItemOf("String"),
|
"Setting2": RawItemOf("String"),
|
||||||
@@ -335,12 +336,12 @@ func TestSnapshotEqual(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "same-items-same-order-different-scope",
|
name: "same-items-same-order-different-scope",
|
||||||
s1: NewSnapshot(map[Key]RawItem{
|
s1: NewSnapshot(map[pkey.Key]RawItem{
|
||||||
"Setting1": RawItemOf(123),
|
"Setting1": RawItemOf(123),
|
||||||
"Setting2": RawItemOf("String"),
|
"Setting2": RawItemOf("String"),
|
||||||
"Setting3": RawItemOf(false),
|
"Setting3": RawItemOf(false),
|
||||||
}, DeviceScope),
|
}, DeviceScope),
|
||||||
s2: NewSnapshot(map[Key]RawItem{
|
s2: NewSnapshot(map[pkey.Key]RawItem{
|
||||||
"Setting1": RawItemOf(123),
|
"Setting1": RawItemOf(123),
|
||||||
"Setting2": RawItemOf("String"),
|
"Setting2": RawItemOf("String"),
|
||||||
"Setting3": RawItemOf(false),
|
"Setting3": RawItemOf(false),
|
||||||
@@ -350,12 +351,12 @@ func TestSnapshotEqual(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "different-items-same-scope",
|
name: "different-items-same-scope",
|
||||||
s1: NewSnapshot(map[Key]RawItem{
|
s1: NewSnapshot(map[pkey.Key]RawItem{
|
||||||
"Setting1": RawItemOf(123),
|
"Setting1": RawItemOf(123),
|
||||||
"Setting2": RawItemOf("String"),
|
"Setting2": RawItemOf("String"),
|
||||||
"Setting3": RawItemOf(false),
|
"Setting3": RawItemOf(false),
|
||||||
}, DeviceScope),
|
}, DeviceScope),
|
||||||
s2: NewSnapshot(map[Key]RawItem{
|
s2: NewSnapshot(map[pkey.Key]RawItem{
|
||||||
"Setting4": RawItemOf(2 * time.Hour),
|
"Setting4": RawItemOf(2 * time.Hour),
|
||||||
"Setting5": RawItemOf(VisibleByPolicy),
|
"Setting5": RawItemOf(VisibleByPolicy),
|
||||||
"Setting6": RawItemOf(ShowChoiceByPolicy),
|
"Setting6": RawItemOf(ShowChoiceByPolicy),
|
||||||
@@ -404,7 +405,7 @@ func TestSnapshotString(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "non-empty",
|
name: "non-empty",
|
||||||
snapshot: NewSnapshot(map[Key]RawItem{
|
snapshot: NewSnapshot(map[pkey.Key]RawItem{
|
||||||
"Setting1": RawItemOf(2 * time.Hour),
|
"Setting1": RawItemOf(2 * time.Hour),
|
||||||
"Setting2": RawItemOf(VisibleByPolicy),
|
"Setting2": RawItemOf(VisibleByPolicy),
|
||||||
"Setting3": RawItemOf(ShowChoiceByPolicy),
|
"Setting3": RawItemOf(ShowChoiceByPolicy),
|
||||||
@@ -416,14 +417,14 @@ Setting3 = user-decides`,
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "non-empty-with-item-origin",
|
name: "non-empty-with-item-origin",
|
||||||
snapshot: NewSnapshot(map[Key]RawItem{
|
snapshot: NewSnapshot(map[pkey.Key]RawItem{
|
||||||
"Setting1": RawItemWith(42, nil, NewNamedOrigin("Test Policy", DeviceScope)),
|
"Setting1": RawItemWith(42, nil, NewNamedOrigin("Test Policy", DeviceScope)),
|
||||||
}),
|
}),
|
||||||
wantString: `Setting1 = 42 - {Test Policy (Device)}`,
|
wantString: `Setting1 = 42 - {Test Policy (Device)}`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "non-empty-with-item-error",
|
name: "non-empty-with-item-error",
|
||||||
snapshot: NewSnapshot(map[Key]RawItem{
|
snapshot: NewSnapshot(map[pkey.Key]RawItem{
|
||||||
"Setting1": RawItemWith(nil, NewErrorText("bang!"), nil),
|
"Setting1": RawItemWith(nil, NewErrorText("bang!"), nil),
|
||||||
}),
|
}),
|
||||||
wantString: `Setting1 = Error{"bang!"}`,
|
wantString: `Setting1 = Error{"bang!"}`,
|
||||||
@@ -458,55 +459,55 @@ func TestMarshalUnmarshalSnapshot(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Bool/True",
|
name: "Bool/True",
|
||||||
snapshot: NewSnapshot(map[Key]RawItem{"BoolPolicy": RawItemOf(true)}),
|
snapshot: NewSnapshot(map[pkey.Key]RawItem{"BoolPolicy": RawItemOf(true)}),
|
||||||
wantJSON: `{"Settings": {"BoolPolicy": {"Value": true}}}`,
|
wantJSON: `{"Settings": {"BoolPolicy": {"Value": true}}}`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Bool/False",
|
name: "Bool/False",
|
||||||
snapshot: NewSnapshot(map[Key]RawItem{"BoolPolicy": RawItemOf(false)}),
|
snapshot: NewSnapshot(map[pkey.Key]RawItem{"BoolPolicy": RawItemOf(false)}),
|
||||||
wantJSON: `{"Settings": {"BoolPolicy": {"Value": false}}}`,
|
wantJSON: `{"Settings": {"BoolPolicy": {"Value": false}}}`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "String/Non-Empty",
|
name: "String/Non-Empty",
|
||||||
snapshot: NewSnapshot(map[Key]RawItem{"StringPolicy": RawItemOf("StringValue")}),
|
snapshot: NewSnapshot(map[pkey.Key]RawItem{"StringPolicy": RawItemOf("StringValue")}),
|
||||||
wantJSON: `{"Settings": {"StringPolicy": {"Value": "StringValue"}}}`,
|
wantJSON: `{"Settings": {"StringPolicy": {"Value": "StringValue"}}}`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "String/Empty",
|
name: "String/Empty",
|
||||||
snapshot: NewSnapshot(map[Key]RawItem{"StringPolicy": RawItemOf("")}),
|
snapshot: NewSnapshot(map[pkey.Key]RawItem{"StringPolicy": RawItemOf("")}),
|
||||||
wantJSON: `{"Settings": {"StringPolicy": {"Value": ""}}}`,
|
wantJSON: `{"Settings": {"StringPolicy": {"Value": ""}}}`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Integer/NonZero",
|
name: "Integer/NonZero",
|
||||||
snapshot: NewSnapshot(map[Key]RawItem{"IntPolicy": RawItemOf(uint64(42))}),
|
snapshot: NewSnapshot(map[pkey.Key]RawItem{"IntPolicy": RawItemOf(uint64(42))}),
|
||||||
wantJSON: `{"Settings": {"IntPolicy": {"Value": 42}}}`,
|
wantJSON: `{"Settings": {"IntPolicy": {"Value": 42}}}`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Integer/Zero",
|
name: "Integer/Zero",
|
||||||
snapshot: NewSnapshot(map[Key]RawItem{"IntPolicy": RawItemOf(uint64(0))}),
|
snapshot: NewSnapshot(map[pkey.Key]RawItem{"IntPolicy": RawItemOf(uint64(0))}),
|
||||||
wantJSON: `{"Settings": {"IntPolicy": {"Value": 0}}}`,
|
wantJSON: `{"Settings": {"IntPolicy": {"Value": 0}}}`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "String-List",
|
name: "String-List",
|
||||||
snapshot: NewSnapshot(map[Key]RawItem{"ListPolicy": RawItemOf([]string{"Value1", "Value2"})}),
|
snapshot: NewSnapshot(map[pkey.Key]RawItem{"ListPolicy": RawItemOf([]string{"Value1", "Value2"})}),
|
||||||
wantJSON: `{"Settings": {"ListPolicy": {"Value": ["Value1", "Value2"]}}}`,
|
wantJSON: `{"Settings": {"ListPolicy": {"Value": ["Value1", "Value2"]}}}`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Duration/Zero",
|
name: "Duration/Zero",
|
||||||
snapshot: NewSnapshot(map[Key]RawItem{"DurationPolicy": RawItemOf(time.Duration(0))}),
|
snapshot: NewSnapshot(map[pkey.Key]RawItem{"DurationPolicy": RawItemOf(time.Duration(0))}),
|
||||||
wantJSON: `{"Settings": {"DurationPolicy": {"Value": "0s"}}}`,
|
wantJSON: `{"Settings": {"DurationPolicy": {"Value": "0s"}}}`,
|
||||||
wantBack: NewSnapshot(map[Key]RawItem{"DurationPolicy": RawItemOf("0s")}),
|
wantBack: NewSnapshot(map[pkey.Key]RawItem{"DurationPolicy": RawItemOf("0s")}),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Duration/NonZero",
|
name: "Duration/NonZero",
|
||||||
snapshot: NewSnapshot(map[Key]RawItem{"DurationPolicy": RawItemOf(2 * time.Hour)}),
|
snapshot: NewSnapshot(map[pkey.Key]RawItem{"DurationPolicy": RawItemOf(2 * time.Hour)}),
|
||||||
wantJSON: `{"Settings": {"DurationPolicy": {"Value": "2h0m0s"}}}`,
|
wantJSON: `{"Settings": {"DurationPolicy": {"Value": "2h0m0s"}}}`,
|
||||||
wantBack: NewSnapshot(map[Key]RawItem{"DurationPolicy": RawItemOf("2h0m0s")}),
|
wantBack: NewSnapshot(map[pkey.Key]RawItem{"DurationPolicy": RawItemOf("2h0m0s")}),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Empty/With-Summary",
|
name: "Empty/With-Summary",
|
||||||
snapshot: NewSnapshot(
|
snapshot: NewSnapshot(
|
||||||
map[Key]RawItem{},
|
map[pkey.Key]RawItem{},
|
||||||
SummaryWith(CurrentUserScope, NewNamedOrigin("TestSource", DeviceScope)),
|
SummaryWith(CurrentUserScope, NewNamedOrigin("TestSource", DeviceScope)),
|
||||||
),
|
),
|
||||||
wantJSON: `{"Summary": {"Origin": {"Name": "TestSource", "Scope": "Device"}, "Scope": "User"}}`,
|
wantJSON: `{"Summary": {"Origin": {"Name": "TestSource", "Scope": "Device"}, "Scope": "User"}}`,
|
||||||
@@ -514,7 +515,7 @@ func TestMarshalUnmarshalSnapshot(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "Setting/With-Summary",
|
name: "Setting/With-Summary",
|
||||||
snapshot: NewSnapshot(
|
snapshot: NewSnapshot(
|
||||||
map[Key]RawItem{"PolicySetting": RawItemOf(uint64(42))},
|
map[pkey.Key]RawItem{"PolicySetting": RawItemOf(uint64(42))},
|
||||||
SummaryWith(CurrentUserScope, NewNamedOrigin("TestSource", DeviceScope)),
|
SummaryWith(CurrentUserScope, NewNamedOrigin("TestSource", DeviceScope)),
|
||||||
),
|
),
|
||||||
wantJSON: `{
|
wantJSON: `{
|
||||||
@@ -525,7 +526,7 @@ func TestMarshalUnmarshalSnapshot(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "Settings/With-Origins",
|
name: "Settings/With-Origins",
|
||||||
snapshot: NewSnapshot(
|
snapshot: NewSnapshot(
|
||||||
map[Key]RawItem{
|
map[pkey.Key]RawItem{
|
||||||
"SettingA": RawItemWith(uint64(42), nil, NewNamedOrigin("SourceA", DeviceScope)),
|
"SettingA": RawItemWith(uint64(42), nil, NewNamedOrigin("SourceA", DeviceScope)),
|
||||||
"SettingB": RawItemWith("B", nil, NewNamedOrigin("SourceB", CurrentProfileScope)),
|
"SettingB": RawItemWith("B", nil, NewNamedOrigin("SourceB", CurrentProfileScope)),
|
||||||
"SettingC": RawItemWith(true, nil, NewNamedOrigin("SourceC", CurrentUserScope)),
|
"SettingC": RawItemWith(true, nil, NewNamedOrigin("SourceC", CurrentUserScope)),
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"unicode/utf8"
|
"unicode/utf8"
|
||||||
|
|
||||||
|
"tailscale.com/util/syspolicy/pkey"
|
||||||
"tailscale.com/util/syspolicy/setting"
|
"tailscale.com/util/syspolicy/setting"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -22,7 +23,7 @@ var _ Store = (*EnvPolicyStore)(nil)
|
|||||||
type EnvPolicyStore struct{}
|
type EnvPolicyStore struct{}
|
||||||
|
|
||||||
// ReadString implements [Store].
|
// ReadString implements [Store].
|
||||||
func (s *EnvPolicyStore) ReadString(key setting.Key) (string, error) {
|
func (s *EnvPolicyStore) ReadString(key pkey.Key) (string, error) {
|
||||||
_, str, err := s.lookupSettingVariable(key)
|
_, str, err := s.lookupSettingVariable(key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
@@ -31,7 +32,7 @@ func (s *EnvPolicyStore) ReadString(key setting.Key) (string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ReadUInt64 implements [Store].
|
// ReadUInt64 implements [Store].
|
||||||
func (s *EnvPolicyStore) ReadUInt64(key setting.Key) (uint64, error) {
|
func (s *EnvPolicyStore) ReadUInt64(key pkey.Key) (uint64, error) {
|
||||||
name, str, err := s.lookupSettingVariable(key)
|
name, str, err := s.lookupSettingVariable(key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
@@ -47,7 +48,7 @@ func (s *EnvPolicyStore) ReadUInt64(key setting.Key) (uint64, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ReadBoolean implements [Store].
|
// ReadBoolean implements [Store].
|
||||||
func (s *EnvPolicyStore) ReadBoolean(key setting.Key) (bool, error) {
|
func (s *EnvPolicyStore) ReadBoolean(key pkey.Key) (bool, error) {
|
||||||
name, str, err := s.lookupSettingVariable(key)
|
name, str, err := s.lookupSettingVariable(key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
@@ -63,7 +64,7 @@ func (s *EnvPolicyStore) ReadBoolean(key setting.Key) (bool, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ReadStringArray implements [Store].
|
// ReadStringArray implements [Store].
|
||||||
func (s *EnvPolicyStore) ReadStringArray(key setting.Key) ([]string, error) {
|
func (s *EnvPolicyStore) ReadStringArray(key pkey.Key) ([]string, error) {
|
||||||
_, str, err := s.lookupSettingVariable(key)
|
_, str, err := s.lookupSettingVariable(key)
|
||||||
if err != nil || str == "" {
|
if err != nil || str == "" {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -79,7 +80,7 @@ func (s *EnvPolicyStore) ReadStringArray(key setting.Key) ([]string, error) {
|
|||||||
return res[0:dst], nil
|
return res[0:dst], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *EnvPolicyStore) lookupSettingVariable(key setting.Key) (name, value string, err error) {
|
func (s *EnvPolicyStore) lookupSettingVariable(key pkey.Key) (name, value string, err error) {
|
||||||
name, err = keyToEnvVarName(key)
|
name, err = keyToEnvVarName(key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", "", err
|
return "", "", err
|
||||||
@@ -103,7 +104,7 @@ var (
|
|||||||
//
|
//
|
||||||
// It's fine to use this in [EnvPolicyStore] without caching variable names since it's not a hot path.
|
// It's fine to use this in [EnvPolicyStore] without caching variable names since it's not a hot path.
|
||||||
// [EnvPolicyStore] is not a [Changeable] policy store, so the conversion will only happen once.
|
// [EnvPolicyStore] is not a [Changeable] policy store, so the conversion will only happen once.
|
||||||
func keyToEnvVarName(key setting.Key) (string, error) {
|
func keyToEnvVarName(key pkey.Key) (string, error) {
|
||||||
if len(key) == 0 {
|
if len(key) == 0 {
|
||||||
return "", errEmptyKey
|
return "", errEmptyKey
|
||||||
}
|
}
|
||||||
@@ -135,7 +136,7 @@ func keyToEnvVarName(key setting.Key) (string, error) {
|
|||||||
}
|
}
|
||||||
case isDigit(c):
|
case isDigit(c):
|
||||||
split = currentWord.Len() > 0 && !isDigit(key[i-1])
|
split = currentWord.Len() > 0 && !isDigit(key[i-1])
|
||||||
case c == setting.KeyPathSeparator:
|
case c == pkey.KeyPathSeparator:
|
||||||
words = append(words, currentWord.String())
|
words = append(words, currentWord.String())
|
||||||
currentWord.Reset()
|
currentWord.Reset()
|
||||||
continue
|
continue
|
||||||
|
|||||||
@@ -11,13 +11,14 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"tailscale.com/util/syspolicy/pkey"
|
||||||
"tailscale.com/util/syspolicy/setting"
|
"tailscale.com/util/syspolicy/setting"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestKeyToEnvVarName(t *testing.T) {
|
func TestKeyToEnvVarName(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
key setting.Key
|
key pkey.Key
|
||||||
want string // suffix after "TS_DEBUGSYSPOLICY_"
|
want string // suffix after "TS_DEBUGSYSPOLICY_"
|
||||||
wantErr error
|
wantErr error
|
||||||
}{
|
}{
|
||||||
@@ -166,7 +167,7 @@ func TestEnvPolicyStore(t *testing.T) {
|
|||||||
}
|
}
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
key setting.Key
|
key pkey.Key
|
||||||
lookup func(string) (string, bool)
|
lookup func(string) (string, bool)
|
||||||
want any
|
want any
|
||||||
wantErr error
|
wantErr error
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import (
|
|||||||
"tailscale.com/util/set"
|
"tailscale.com/util/set"
|
||||||
"tailscale.com/util/syspolicy/internal/loggerx"
|
"tailscale.com/util/syspolicy/internal/loggerx"
|
||||||
"tailscale.com/util/syspolicy/internal/metrics"
|
"tailscale.com/util/syspolicy/internal/metrics"
|
||||||
|
"tailscale.com/util/syspolicy/pkey"
|
||||||
"tailscale.com/util/syspolicy/setting"
|
"tailscale.com/util/syspolicy/setting"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -138,9 +139,9 @@ func (r *Reader) reload(force bool) (*setting.Snapshot, error) {
|
|||||||
|
|
||||||
metrics.Reset(r.origin)
|
metrics.Reset(r.origin)
|
||||||
|
|
||||||
var m map[setting.Key]setting.RawItem
|
var m map[pkey.Key]setting.RawItem
|
||||||
if lastPolicyCount := r.lastPolicy.Len(); lastPolicyCount > 0 {
|
if lastPolicyCount := r.lastPolicy.Len(); lastPolicyCount > 0 {
|
||||||
m = make(map[setting.Key]setting.RawItem, lastPolicyCount)
|
m = make(map[pkey.Key]setting.RawItem, lastPolicyCount)
|
||||||
}
|
}
|
||||||
for _, s := range r.settings {
|
for _, s := range r.settings {
|
||||||
if !r.origin.Scope().IsConfigurableSetting(s) {
|
if !r.origin.Scope().IsConfigurableSetting(s) {
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"tailscale.com/util/must"
|
"tailscale.com/util/must"
|
||||||
|
"tailscale.com/util/syspolicy/pkey"
|
||||||
"tailscale.com/util/syspolicy/setting"
|
"tailscale.com/util/syspolicy/setting"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -72,7 +73,7 @@ func TestReaderLifecycle(t *testing.T) {
|
|||||||
initWant: setting.NewSnapshot(nil, setting.NewNamedOrigin("Test", setting.DeviceScope)),
|
initWant: setting.NewSnapshot(nil, setting.NewNamedOrigin("Test", setting.DeviceScope)),
|
||||||
addStrings: []TestSetting[string]{TestSettingOf("StringValue", "S1")},
|
addStrings: []TestSetting[string]{TestSettingOf("StringValue", "S1")},
|
||||||
addStringLists: []TestSetting[[]string]{TestSettingOf("StringListValue", []string{"S1", "S2", "S3"})},
|
addStringLists: []TestSetting[[]string]{TestSettingOf("StringListValue", []string{"S1", "S2", "S3"})},
|
||||||
newWant: setting.NewSnapshot(map[setting.Key]setting.RawItem{
|
newWant: setting.NewSnapshot(map[pkey.Key]setting.RawItem{
|
||||||
"StringValue": setting.RawItemWith("S1", nil, setting.NewNamedOrigin("Test", setting.DeviceScope)),
|
"StringValue": setting.RawItemWith("S1", nil, setting.NewNamedOrigin("Test", setting.DeviceScope)),
|
||||||
"StringListValue": setting.RawItemWith([]string{"S1", "S2", "S3"}, nil, setting.NewNamedOrigin("Test", setting.DeviceScope)),
|
"StringListValue": setting.RawItemWith([]string{"S1", "S2", "S3"}, nil, setting.NewNamedOrigin("Test", setting.DeviceScope)),
|
||||||
}, setting.NewNamedOrigin("Test", setting.DeviceScope)),
|
}, setting.NewNamedOrigin("Test", setting.DeviceScope)),
|
||||||
@@ -136,7 +137,7 @@ func TestReaderLifecycle(t *testing.T) {
|
|||||||
TestSettingOf("PreferenceOptionValue", "always"),
|
TestSettingOf("PreferenceOptionValue", "always"),
|
||||||
TestSettingOf("VisibilityValue", "show"),
|
TestSettingOf("VisibilityValue", "show"),
|
||||||
},
|
},
|
||||||
initWant: setting.NewSnapshot(map[setting.Key]setting.RawItem{
|
initWant: setting.NewSnapshot(map[pkey.Key]setting.RawItem{
|
||||||
"DurationValue": setting.RawItemWith(must.Get(time.ParseDuration("2h30m")), nil, setting.NewNamedOrigin("Test", setting.DeviceScope)),
|
"DurationValue": setting.RawItemWith(must.Get(time.ParseDuration("2h30m")), nil, setting.NewNamedOrigin("Test", setting.DeviceScope)),
|
||||||
"PreferenceOptionValue": setting.RawItemWith(setting.AlwaysByPolicy, nil, setting.NewNamedOrigin("Test", setting.DeviceScope)),
|
"PreferenceOptionValue": setting.RawItemWith(setting.AlwaysByPolicy, nil, setting.NewNamedOrigin("Test", setting.DeviceScope)),
|
||||||
"VisibilityValue": setting.RawItemWith(setting.VisibleByPolicy, nil, setting.NewNamedOrigin("Test", setting.DeviceScope)),
|
"VisibilityValue": setting.RawItemWith(setting.VisibleByPolicy, nil, setting.NewNamedOrigin("Test", setting.DeviceScope)),
|
||||||
@@ -165,7 +166,7 @@ func TestReaderLifecycle(t *testing.T) {
|
|||||||
initUInt64s: []TestSetting[uint64]{
|
initUInt64s: []TestSetting[uint64]{
|
||||||
TestSettingOf[uint64]("VisibilityValue", 42), // type mismatch
|
TestSettingOf[uint64]("VisibilityValue", 42), // type mismatch
|
||||||
},
|
},
|
||||||
initWant: setting.NewSnapshot(map[setting.Key]setting.RawItem{
|
initWant: setting.NewSnapshot(map[pkey.Key]setting.RawItem{
|
||||||
"DurationValue1": setting.RawItemWith(nil, setting.NewErrorText("time: invalid duration \"soon\""), setting.NewNamedOrigin("Test", setting.CurrentUserScope)),
|
"DurationValue1": setting.RawItemWith(nil, setting.NewErrorText("time: invalid duration \"soon\""), setting.NewNamedOrigin("Test", setting.CurrentUserScope)),
|
||||||
"DurationValue2": setting.RawItemWith(nil, setting.NewErrorText("bang!"), setting.NewNamedOrigin("Test", setting.CurrentUserScope)),
|
"DurationValue2": setting.RawItemWith(nil, setting.NewErrorText("bang!"), setting.NewNamedOrigin("Test", setting.CurrentUserScope)),
|
||||||
"PreferenceOptionValue": setting.RawItemWith(setting.ShowChoiceByPolicy, nil, setting.NewNamedOrigin("Test", setting.CurrentUserScope)),
|
"PreferenceOptionValue": setting.RawItemWith(setting.ShowChoiceByPolicy, nil, setting.NewNamedOrigin("Test", setting.CurrentUserScope)),
|
||||||
@@ -277,7 +278,7 @@ func TestReadingSession(t *testing.T) {
|
|||||||
t.Fatalf("the session was closed prematurely")
|
t.Fatalf("the session was closed prematurely")
|
||||||
}
|
}
|
||||||
|
|
||||||
want := setting.NewSnapshot(map[setting.Key]setting.RawItem{
|
want := setting.NewSnapshot(map[pkey.Key]setting.RawItem{
|
||||||
"StringValue": setting.RawItemWith("S1", nil, origin),
|
"StringValue": setting.RawItemWith("S1", nil, origin),
|
||||||
}, origin)
|
}, origin)
|
||||||
if got := session.GetSettings(); !got.Equal(want) {
|
if got := session.GetSettings(); !got.Equal(want) {
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
|
|
||||||
"tailscale.com/types/lazy"
|
"tailscale.com/types/lazy"
|
||||||
|
"tailscale.com/util/syspolicy/pkey"
|
||||||
"tailscale.com/util/syspolicy/setting"
|
"tailscale.com/util/syspolicy/setting"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -31,19 +32,19 @@ type Store interface {
|
|||||||
// ReadString returns the value of a [setting.StringValue] with the specified key,
|
// ReadString returns the value of a [setting.StringValue] with the specified key,
|
||||||
// an [setting.ErrNotConfigured] if the policy setting is not configured, or
|
// an [setting.ErrNotConfigured] if the policy setting is not configured, or
|
||||||
// an error on failure.
|
// an error on failure.
|
||||||
ReadString(key setting.Key) (string, error)
|
ReadString(key pkey.Key) (string, error)
|
||||||
// ReadUInt64 returns the value of a [setting.IntegerValue] with the specified key,
|
// ReadUInt64 returns the value of a [setting.IntegerValue] with the specified key,
|
||||||
// an [setting.ErrNotConfigured] if the policy setting is not configured, or
|
// an [setting.ErrNotConfigured] if the policy setting is not configured, or
|
||||||
// an error on failure.
|
// an error on failure.
|
||||||
ReadUInt64(key setting.Key) (uint64, error)
|
ReadUInt64(key pkey.Key) (uint64, error)
|
||||||
// ReadBoolean returns the value of a [setting.BooleanValue] with the specified key,
|
// ReadBoolean returns the value of a [setting.BooleanValue] with the specified key,
|
||||||
// an [setting.ErrNotConfigured] if the policy setting is not configured, or
|
// an [setting.ErrNotConfigured] if the policy setting is not configured, or
|
||||||
// an error on failure.
|
// an error on failure.
|
||||||
ReadBoolean(key setting.Key) (bool, error)
|
ReadBoolean(key pkey.Key) (bool, error)
|
||||||
// ReadStringArray returns the value of a [setting.StringListValue] with the specified key,
|
// ReadStringArray returns the value of a [setting.StringListValue] with the specified key,
|
||||||
// an [setting.ErrNotConfigured] if the policy setting is not configured, or
|
// an [setting.ErrNotConfigured] if the policy setting is not configured, or
|
||||||
// an error on failure.
|
// an error on failure.
|
||||||
ReadStringArray(key setting.Key) ([]string, error)
|
ReadStringArray(key pkey.Key) ([]string, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Lockable is an optional interface that [Store] implementations may support.
|
// Lockable is an optional interface that [Store] implementations may support.
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import (
|
|||||||
"golang.org/x/sys/windows/registry"
|
"golang.org/x/sys/windows/registry"
|
||||||
"tailscale.com/util/set"
|
"tailscale.com/util/set"
|
||||||
"tailscale.com/util/syspolicy/internal/loggerx"
|
"tailscale.com/util/syspolicy/internal/loggerx"
|
||||||
|
"tailscale.com/util/syspolicy/pkey"
|
||||||
"tailscale.com/util/syspolicy/setting"
|
"tailscale.com/util/syspolicy/setting"
|
||||||
"tailscale.com/util/winutil/gp"
|
"tailscale.com/util/winutil/gp"
|
||||||
)
|
)
|
||||||
@@ -251,7 +252,7 @@ func (ps *PlatformPolicyStore) onChange() {
|
|||||||
|
|
||||||
// ReadString retrieves a string policy with the specified key.
|
// ReadString retrieves a string policy with the specified key.
|
||||||
// It returns [setting.ErrNotConfigured] if the policy setting does not exist.
|
// It returns [setting.ErrNotConfigured] if the policy setting does not exist.
|
||||||
func (ps *PlatformPolicyStore) ReadString(key setting.Key) (val string, err error) {
|
func (ps *PlatformPolicyStore) ReadString(key pkey.Key) (val string, err error) {
|
||||||
return getPolicyValue(ps, key,
|
return getPolicyValue(ps, key,
|
||||||
func(key registry.Key, valueName string) (string, error) {
|
func(key registry.Key, valueName string) (string, error) {
|
||||||
val, _, err := key.GetStringValue(valueName)
|
val, _, err := key.GetStringValue(valueName)
|
||||||
@@ -261,7 +262,7 @@ func (ps *PlatformPolicyStore) ReadString(key setting.Key) (val string, err erro
|
|||||||
|
|
||||||
// ReadUInt64 retrieves an integer policy with the specified key.
|
// ReadUInt64 retrieves an integer policy with the specified key.
|
||||||
// It returns [setting.ErrNotConfigured] if the policy setting does not exist.
|
// It returns [setting.ErrNotConfigured] if the policy setting does not exist.
|
||||||
func (ps *PlatformPolicyStore) ReadUInt64(key setting.Key) (uint64, error) {
|
func (ps *PlatformPolicyStore) ReadUInt64(key pkey.Key) (uint64, error) {
|
||||||
return getPolicyValue(ps, key,
|
return getPolicyValue(ps, key,
|
||||||
func(key registry.Key, valueName string) (uint64, error) {
|
func(key registry.Key, valueName string) (uint64, error) {
|
||||||
val, _, err := key.GetIntegerValue(valueName)
|
val, _, err := key.GetIntegerValue(valueName)
|
||||||
@@ -271,7 +272,7 @@ func (ps *PlatformPolicyStore) ReadUInt64(key setting.Key) (uint64, error) {
|
|||||||
|
|
||||||
// ReadBoolean retrieves a boolean policy with the specified key.
|
// ReadBoolean retrieves a boolean policy with the specified key.
|
||||||
// It returns [setting.ErrNotConfigured] if the policy setting does not exist.
|
// It returns [setting.ErrNotConfigured] if the policy setting does not exist.
|
||||||
func (ps *PlatformPolicyStore) ReadBoolean(key setting.Key) (bool, error) {
|
func (ps *PlatformPolicyStore) ReadBoolean(key pkey.Key) (bool, error) {
|
||||||
return getPolicyValue(ps, key,
|
return getPolicyValue(ps, key,
|
||||||
func(key registry.Key, valueName string) (bool, error) {
|
func(key registry.Key, valueName string) (bool, error) {
|
||||||
val, _, err := key.GetIntegerValue(valueName)
|
val, _, err := key.GetIntegerValue(valueName)
|
||||||
@@ -283,8 +284,8 @@ func (ps *PlatformPolicyStore) ReadBoolean(key setting.Key) (bool, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ReadString retrieves a multi-string policy with the specified key.
|
// ReadString retrieves a multi-string policy with the specified key.
|
||||||
// It returns [setting.ErrNotConfigured] if the policy setting does not exist.
|
// It returns [pkey.ErrNotConfigured] if the policy setting does not exist.
|
||||||
func (ps *PlatformPolicyStore) ReadStringArray(key setting.Key) ([]string, error) {
|
func (ps *PlatformPolicyStore) ReadStringArray(key pkey.Key) ([]string, error) {
|
||||||
return getPolicyValue(ps, key,
|
return getPolicyValue(ps, key,
|
||||||
func(key registry.Key, valueName string) ([]string, error) {
|
func(key registry.Key, valueName string) ([]string, error) {
|
||||||
val, _, err := key.GetStringsValue(valueName)
|
val, _, err := key.GetStringsValue(valueName)
|
||||||
@@ -322,25 +323,25 @@ func (ps *PlatformPolicyStore) ReadStringArray(key setting.Key) ([]string, error
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// splitSettingKey extracts the registry key name and value name from a [setting.Key].
|
// splitSettingKey extracts the registry key name and value name from a [pkey.Key].
|
||||||
// The [setting.Key] format allows grouping settings into nested categories using one
|
// The [pkey.Key] format allows grouping settings into nested categories using one
|
||||||
// or more [setting.KeyPathSeparator]s in the path. How individual policy settings are
|
// or more [pkey.KeyPathSeparator]s in the path. How individual policy settings are
|
||||||
// stored is an implementation detail of each [Store]. In the [PlatformPolicyStore]
|
// stored is an implementation detail of each [Store]. In the [PlatformPolicyStore]
|
||||||
// for Windows, we map nested policy categories onto the Registry key hierarchy.
|
// for Windows, we map nested policy categories onto the Registry key hierarchy.
|
||||||
// The last component after a [setting.KeyPathSeparator] is treated as the value name,
|
// The last component after a [pkey.KeyPathSeparator] is treated as the value name,
|
||||||
// while everything preceding it is considered a subpath (relative to the {HKLM,HKCU}\Software\Policies\Tailscale key).
|
// while everything preceding it is considered a subpath (relative to the {HKLM,HKCU}\Software\Policies\Tailscale key).
|
||||||
// If there are no [setting.KeyPathSeparator]s in the key, the policy setting value
|
// If there are no [pkey.KeyPathSeparator]s in the key, the policy setting value
|
||||||
// is meant to be stored directly under {HKLM,HKCU}\Software\Policies\Tailscale.
|
// is meant to be stored directly under {HKLM,HKCU}\Software\Policies\Tailscale.
|
||||||
func splitSettingKey(key setting.Key) (path, valueName string) {
|
func splitSettingKey(key pkey.Key) (path, valueName string) {
|
||||||
if idx := strings.LastIndexByte(string(key), setting.KeyPathSeparator); idx != -1 {
|
if idx := strings.LastIndexByte(string(key), pkey.KeyPathSeparator); idx != -1 {
|
||||||
path = strings.ReplaceAll(string(key[:idx]), string(setting.KeyPathSeparator), `\`)
|
path = strings.ReplaceAll(string(key[:idx]), string(pkey.KeyPathSeparator), `\`)
|
||||||
valueName = string(key[idx+1:])
|
valueName = string(key[idx+1:])
|
||||||
return path, valueName
|
return path, valueName
|
||||||
}
|
}
|
||||||
return "", string(key)
|
return "", string(key)
|
||||||
}
|
}
|
||||||
|
|
||||||
func getPolicyValue[T any](ps *PlatformPolicyStore, key setting.Key, getter registryValueGetter[T]) (T, error) {
|
func getPolicyValue[T any](ps *PlatformPolicyStore, key pkey.Key, getter registryValueGetter[T]) (T, error) {
|
||||||
var zero T
|
var zero T
|
||||||
|
|
||||||
ps.mu.Lock()
|
ps.mu.Lock()
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import (
|
|||||||
"tailscale.com/tstest"
|
"tailscale.com/tstest"
|
||||||
"tailscale.com/util/cibuild"
|
"tailscale.com/util/cibuild"
|
||||||
"tailscale.com/util/mak"
|
"tailscale.com/util/mak"
|
||||||
|
"tailscale.com/util/syspolicy/pkey"
|
||||||
"tailscale.com/util/syspolicy/setting"
|
"tailscale.com/util/syspolicy/setting"
|
||||||
"tailscale.com/util/winutil"
|
"tailscale.com/util/winutil"
|
||||||
"tailscale.com/util/winutil/gp"
|
"tailscale.com/util/winutil/gp"
|
||||||
@@ -31,7 +32,7 @@ import (
|
|||||||
type subkeyStrings []string
|
type subkeyStrings []string
|
||||||
|
|
||||||
type testPolicyValue struct {
|
type testPolicyValue struct {
|
||||||
name setting.Key
|
name pkey.Key
|
||||||
value any
|
value any
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -100,7 +101,7 @@ func TestReadPolicyStore(t *testing.T) {
|
|||||||
t.Skipf("test requires running as elevated user")
|
t.Skipf("test requires running as elevated user")
|
||||||
}
|
}
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name setting.Key
|
name pkey.Key
|
||||||
newValue any
|
newValue any
|
||||||
legacyValue any
|
legacyValue any
|
||||||
want any
|
want any
|
||||||
@@ -269,7 +270,7 @@ func TestPolicyStoreChangeNotifications(t *testing.T) {
|
|||||||
func TestSplitSettingKey(t *testing.T) {
|
func TestSplitSettingKey(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
key setting.Key
|
key pkey.Key
|
||||||
wantPath string
|
wantPath string
|
||||||
wantValue string
|
wantValue string
|
||||||
}{
|
}{
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import (
|
|||||||
"tailscale.com/util/mak"
|
"tailscale.com/util/mak"
|
||||||
"tailscale.com/util/set"
|
"tailscale.com/util/set"
|
||||||
"tailscale.com/util/slicesx"
|
"tailscale.com/util/slicesx"
|
||||||
|
"tailscale.com/util/syspolicy/pkey"
|
||||||
"tailscale.com/util/syspolicy/setting"
|
"tailscale.com/util/syspolicy/setting"
|
||||||
"tailscale.com/util/testenv"
|
"tailscale.com/util/testenv"
|
||||||
)
|
)
|
||||||
@@ -31,7 +32,7 @@ type TestValueType interface {
|
|||||||
// TestSetting is a policy setting in a [TestStore].
|
// TestSetting is a policy setting in a [TestStore].
|
||||||
type TestSetting[T TestValueType] struct {
|
type TestSetting[T TestValueType] struct {
|
||||||
// Key is the setting's unique identifier.
|
// Key is the setting's unique identifier.
|
||||||
Key setting.Key
|
Key pkey.Key
|
||||||
// Error is the error to be returned by the [TestStore] when reading
|
// Error is the error to be returned by the [TestStore] when reading
|
||||||
// a policy setting with the specified key.
|
// a policy setting with the specified key.
|
||||||
Error error
|
Error error
|
||||||
@@ -43,20 +44,20 @@ type TestSetting[T TestValueType] struct {
|
|||||||
|
|
||||||
// TestSettingOf returns a [TestSetting] representing a policy setting
|
// TestSettingOf returns a [TestSetting] representing a policy setting
|
||||||
// configured with the specified key and value.
|
// configured with the specified key and value.
|
||||||
func TestSettingOf[T TestValueType](key setting.Key, value T) TestSetting[T] {
|
func TestSettingOf[T TestValueType](key pkey.Key, value T) TestSetting[T] {
|
||||||
return TestSetting[T]{Key: key, Value: value}
|
return TestSetting[T]{Key: key, Value: value}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestSettingWithError returns a [TestSetting] representing a policy setting
|
// TestSettingWithError returns a [TestSetting] representing a policy setting
|
||||||
// with the specified key and error.
|
// with the specified key and error.
|
||||||
func TestSettingWithError[T TestValueType](key setting.Key, err error) TestSetting[T] {
|
func TestSettingWithError[T TestValueType](key pkey.Key, err error) TestSetting[T] {
|
||||||
return TestSetting[T]{Key: key, Error: err}
|
return TestSetting[T]{Key: key, Error: err}
|
||||||
}
|
}
|
||||||
|
|
||||||
// testReadOperation describes a single policy setting read operation.
|
// testReadOperation describes a single policy setting read operation.
|
||||||
type testReadOperation struct {
|
type testReadOperation struct {
|
||||||
// Key is the setting's unique identifier.
|
// Key is the setting's unique identifier.
|
||||||
Key setting.Key
|
Key pkey.Key
|
||||||
// Type is a value type of a read operation.
|
// Type is a value type of a read operation.
|
||||||
// [setting.BooleanValue], [setting.IntegerValue], [setting.StringValue] or [setting.StringListValue]
|
// [setting.BooleanValue], [setting.IntegerValue], [setting.StringValue] or [setting.StringListValue]
|
||||||
Type setting.Type
|
Type setting.Type
|
||||||
@@ -65,7 +66,7 @@ type testReadOperation struct {
|
|||||||
// TestExpectedReads is the number of read operations with the specified details.
|
// TestExpectedReads is the number of read operations with the specified details.
|
||||||
type TestExpectedReads struct {
|
type TestExpectedReads struct {
|
||||||
// Key is the setting's unique identifier.
|
// Key is the setting's unique identifier.
|
||||||
Key setting.Key
|
Key pkey.Key
|
||||||
// Type is a value type of a read operation.
|
// Type is a value type of a read operation.
|
||||||
// [setting.BooleanValue], [setting.IntegerValue], [setting.StringValue] or [setting.StringListValue]
|
// [setting.BooleanValue], [setting.IntegerValue], [setting.StringValue] or [setting.StringListValue]
|
||||||
Type setting.Type
|
Type setting.Type
|
||||||
@@ -87,8 +88,8 @@ type TestStore struct {
|
|||||||
storeLockCount atomic.Int32
|
storeLockCount atomic.Int32
|
||||||
|
|
||||||
mu sync.RWMutex
|
mu sync.RWMutex
|
||||||
suspendCount int // change callback are suspended if > 0
|
suspendCount int // change callback are suspended if > 0
|
||||||
mr, mw map[setting.Key]any // maps for reading and writing; they're the same unless the store is suspended.
|
mr, mw map[pkey.Key]any // maps for reading and writing; they're the same unless the store is suspended.
|
||||||
cbs set.HandleSet[func()]
|
cbs set.HandleSet[func()]
|
||||||
closed bool
|
closed bool
|
||||||
|
|
||||||
@@ -99,7 +100,7 @@ type TestStore struct {
|
|||||||
// NewTestStore returns a new [TestStore].
|
// NewTestStore returns a new [TestStore].
|
||||||
// The tb will be used to report coding errors detected by the [TestStore].
|
// The tb will be used to report coding errors detected by the [TestStore].
|
||||||
func NewTestStore(tb testenv.TB) *TestStore {
|
func NewTestStore(tb testenv.TB) *TestStore {
|
||||||
m := make(map[setting.Key]any)
|
m := make(map[pkey.Key]any)
|
||||||
store := &TestStore{
|
store := &TestStore{
|
||||||
tb: tb,
|
tb: tb,
|
||||||
done: make(chan struct{}),
|
done: make(chan struct{}),
|
||||||
@@ -162,7 +163,7 @@ func (s *TestStore) IsEmpty() bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ReadString implements [Store].
|
// ReadString implements [Store].
|
||||||
func (s *TestStore) ReadString(key setting.Key) (string, error) {
|
func (s *TestStore) ReadString(key pkey.Key) (string, error) {
|
||||||
defer s.recordRead(key, setting.StringValue)
|
defer s.recordRead(key, setting.StringValue)
|
||||||
s.mu.RLock()
|
s.mu.RLock()
|
||||||
defer s.mu.RUnlock()
|
defer s.mu.RUnlock()
|
||||||
@@ -181,7 +182,7 @@ func (s *TestStore) ReadString(key setting.Key) (string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ReadUInt64 implements [Store].
|
// ReadUInt64 implements [Store].
|
||||||
func (s *TestStore) ReadUInt64(key setting.Key) (uint64, error) {
|
func (s *TestStore) ReadUInt64(key pkey.Key) (uint64, error) {
|
||||||
defer s.recordRead(key, setting.IntegerValue)
|
defer s.recordRead(key, setting.IntegerValue)
|
||||||
s.mu.RLock()
|
s.mu.RLock()
|
||||||
defer s.mu.RUnlock()
|
defer s.mu.RUnlock()
|
||||||
@@ -200,7 +201,7 @@ func (s *TestStore) ReadUInt64(key setting.Key) (uint64, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ReadBoolean implements [Store].
|
// ReadBoolean implements [Store].
|
||||||
func (s *TestStore) ReadBoolean(key setting.Key) (bool, error) {
|
func (s *TestStore) ReadBoolean(key pkey.Key) (bool, error) {
|
||||||
defer s.recordRead(key, setting.BooleanValue)
|
defer s.recordRead(key, setting.BooleanValue)
|
||||||
s.mu.RLock()
|
s.mu.RLock()
|
||||||
defer s.mu.RUnlock()
|
defer s.mu.RUnlock()
|
||||||
@@ -219,7 +220,7 @@ func (s *TestStore) ReadBoolean(key setting.Key) (bool, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ReadStringArray implements [Store].
|
// ReadStringArray implements [Store].
|
||||||
func (s *TestStore) ReadStringArray(key setting.Key) ([]string, error) {
|
func (s *TestStore) ReadStringArray(key pkey.Key) ([]string, error) {
|
||||||
defer s.recordRead(key, setting.StringListValue)
|
defer s.recordRead(key, setting.StringListValue)
|
||||||
s.mu.RLock()
|
s.mu.RLock()
|
||||||
defer s.mu.RUnlock()
|
defer s.mu.RUnlock()
|
||||||
@@ -237,7 +238,7 @@ func (s *TestStore) ReadStringArray(key setting.Key) ([]string, error) {
|
|||||||
return slice, nil
|
return slice, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *TestStore) recordRead(key setting.Key, typ setting.Type) {
|
func (s *TestStore) recordRead(key pkey.Key, typ setting.Type) {
|
||||||
s.readsMu.Lock()
|
s.readsMu.Lock()
|
||||||
op := testReadOperation{key, typ}
|
op := testReadOperation{key, typ}
|
||||||
num := s.reads[op]
|
num := s.reads[op]
|
||||||
@@ -399,7 +400,7 @@ func (s *TestStore) SetStringLists(settings ...TestSetting[[]string]) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Delete deletes the specified settings from s.
|
// Delete deletes the specified settings from s.
|
||||||
func (s *TestStore) Delete(keys ...setting.Key) {
|
func (s *TestStore) Delete(keys ...pkey.Key) {
|
||||||
s.storeLock.Lock()
|
s.storeLock.Lock()
|
||||||
for _, key := range keys {
|
for _, key := range keys {
|
||||||
s.mu.Lock()
|
s.mu.Lock()
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"tailscale.com/util/syspolicy/internal/loggerx"
|
"tailscale.com/util/syspolicy/internal/loggerx"
|
||||||
|
"tailscale.com/util/syspolicy/pkey"
|
||||||
"tailscale.com/util/syspolicy/rsop"
|
"tailscale.com/util/syspolicy/rsop"
|
||||||
"tailscale.com/util/syspolicy/setting"
|
"tailscale.com/util/syspolicy/setting"
|
||||||
"tailscale.com/util/syspolicy/source"
|
"tailscale.com/util/syspolicy/source"
|
||||||
@@ -58,7 +59,7 @@ func MustRegisterStoreForTest(tb testenv.TB, name string, scope setting.PolicySc
|
|||||||
|
|
||||||
// HasAnyOf returns whether at least one of the specified policy settings is configured,
|
// HasAnyOf returns whether at least one of the specified policy settings is configured,
|
||||||
// or an error if no keys are provided or the check fails.
|
// or an error if no keys are provided or the check fails.
|
||||||
func HasAnyOf(keys ...Key) (bool, error) {
|
func HasAnyOf(keys ...pkey.Key) (bool, error) {
|
||||||
if len(keys) == 0 {
|
if len(keys) == 0 {
|
||||||
return false, errors.New("at least one key must be specified")
|
return false, errors.New("at least one key must be specified")
|
||||||
}
|
}
|
||||||
@@ -82,25 +83,25 @@ func HasAnyOf(keys ...Key) (bool, error) {
|
|||||||
|
|
||||||
// GetString returns a string policy setting with the specified key,
|
// GetString returns a string policy setting with the specified key,
|
||||||
// or defaultValue if it does not exist.
|
// or defaultValue if it does not exist.
|
||||||
func GetString(key Key, defaultValue string) (string, error) {
|
func GetString(key pkey.Key, defaultValue string) (string, error) {
|
||||||
return getCurrentPolicySettingValue(key, defaultValue)
|
return getCurrentPolicySettingValue(key, defaultValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetUint64 returns a numeric policy setting with the specified key,
|
// GetUint64 returns a numeric policy setting with the specified key,
|
||||||
// or defaultValue if it does not exist.
|
// or defaultValue if it does not exist.
|
||||||
func GetUint64(key Key, defaultValue uint64) (uint64, error) {
|
func GetUint64(key pkey.Key, defaultValue uint64) (uint64, error) {
|
||||||
return getCurrentPolicySettingValue(key, defaultValue)
|
return getCurrentPolicySettingValue(key, defaultValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetBoolean returns a boolean policy setting with the specified key,
|
// GetBoolean returns a boolean policy setting with the specified key,
|
||||||
// or defaultValue if it does not exist.
|
// or defaultValue if it does not exist.
|
||||||
func GetBoolean(key Key, defaultValue bool) (bool, error) {
|
func GetBoolean(key pkey.Key, defaultValue bool) (bool, error) {
|
||||||
return getCurrentPolicySettingValue(key, defaultValue)
|
return getCurrentPolicySettingValue(key, defaultValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetStringArray returns a multi-string policy setting with the specified key,
|
// GetStringArray returns a multi-string policy setting with the specified key,
|
||||||
// or defaultValue if it does not exist.
|
// or defaultValue if it does not exist.
|
||||||
func GetStringArray(key Key, defaultValue []string) ([]string, error) {
|
func GetStringArray(key pkey.Key, defaultValue []string) ([]string, error) {
|
||||||
return getCurrentPolicySettingValue(key, defaultValue)
|
return getCurrentPolicySettingValue(key, defaultValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -110,14 +111,14 @@ func GetStringArray(key Key, defaultValue []string) ([]string, error) {
|
|||||||
// the authority to set. It describes user-decides/always/never options, where
|
// the authority to set. It describes user-decides/always/never options, where
|
||||||
// "always" and "never" remove the user's ability to make a selection. If not
|
// "always" and "never" remove the user's ability to make a selection. If not
|
||||||
// present or set to a different value, "user-decides" is the default.
|
// present or set to a different value, "user-decides" is the default.
|
||||||
func GetPreferenceOption(name Key) (setting.PreferenceOption, error) {
|
func GetPreferenceOption(name pkey.Key) (setting.PreferenceOption, error) {
|
||||||
return getCurrentPolicySettingValue(name, setting.ShowChoiceByPolicy)
|
return getCurrentPolicySettingValue(name, setting.ShowChoiceByPolicy)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetPreferenceOptionOrDefault is like [GetPreferenceOption], but allows
|
// GetPreferenceOptionOrDefault is like [GetPreferenceOption], but allows
|
||||||
// specifying a default value to return if the policy setting is not configured.
|
// specifying a default value to return if the policy setting is not configured.
|
||||||
// It can be used in situations where "user-decides" is not the default.
|
// It can be used in situations where "user-decides" is not the default.
|
||||||
func GetPreferenceOptionOrDefault(name Key, defaultValue setting.PreferenceOption) (setting.PreferenceOption, error) {
|
func GetPreferenceOptionOrDefault(name pkey.Key, defaultValue setting.PreferenceOption) (setting.PreferenceOption, error) {
|
||||||
return getCurrentPolicySettingValue(name, defaultValue)
|
return getCurrentPolicySettingValue(name, defaultValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -126,7 +127,7 @@ func GetPreferenceOptionOrDefault(name Key, defaultValue setting.PreferenceOptio
|
|||||||
// for UI elements. The registry value should be a string set to "show" (return
|
// for UI elements. The registry value should be a string set to "show" (return
|
||||||
// true) or "hide" (return true). If not present or set to a different value,
|
// true) or "hide" (return true). If not present or set to a different value,
|
||||||
// "show" (return false) is the default.
|
// "show" (return false) is the default.
|
||||||
func GetVisibility(name Key) (setting.Visibility, error) {
|
func GetVisibility(name pkey.Key) (setting.Visibility, error) {
|
||||||
return getCurrentPolicySettingValue(name, setting.VisibleByPolicy)
|
return getCurrentPolicySettingValue(name, setting.VisibleByPolicy)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -135,7 +136,7 @@ func GetVisibility(name Key) (setting.Visibility, error) {
|
|||||||
// action. The registry value should be a string that time.ParseDuration
|
// action. The registry value should be a string that time.ParseDuration
|
||||||
// understands. If the registry value is "" or can not be processed,
|
// understands. If the registry value is "" or can not be processed,
|
||||||
// defaultValue is returned instead.
|
// defaultValue is returned instead.
|
||||||
func GetDuration(name Key, defaultValue time.Duration) (time.Duration, error) {
|
func GetDuration(name pkey.Key, defaultValue time.Duration) (time.Duration, error) {
|
||||||
d, err := getCurrentPolicySettingValue(name, defaultValue)
|
d, err := getCurrentPolicySettingValue(name, defaultValue)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return d, err
|
return d, err
|
||||||
@@ -160,7 +161,7 @@ func RegisterChangeCallback(cb rsop.PolicyChangeCallback) (unregister func(), er
|
|||||||
// specified by its key from the [rsop.Policy] of the [setting.DefaultScope]. It
|
// specified by its key from the [rsop.Policy] of the [setting.DefaultScope]. It
|
||||||
// returns def if the policy setting is not configured, or an error if it has
|
// returns def if the policy setting is not configured, or an error if it has
|
||||||
// an error or could not be converted to the specified type T.
|
// an error or could not be converted to the specified type T.
|
||||||
func getCurrentPolicySettingValue[T setting.ValueType](key Key, def T) (T, error) {
|
func getCurrentPolicySettingValue[T setting.ValueType](key pkey.Key, def T) (T, error) {
|
||||||
effective, err := rsop.PolicyFor(setting.DefaultScope())
|
effective, err := rsop.PolicyFor(setting.DefaultScope())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return def, err
|
return def, err
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import (
|
|||||||
"tailscale.com/types/logger"
|
"tailscale.com/types/logger"
|
||||||
"tailscale.com/util/syspolicy/internal/loggerx"
|
"tailscale.com/util/syspolicy/internal/loggerx"
|
||||||
"tailscale.com/util/syspolicy/internal/metrics"
|
"tailscale.com/util/syspolicy/internal/metrics"
|
||||||
|
"tailscale.com/util/syspolicy/pkey"
|
||||||
"tailscale.com/util/syspolicy/setting"
|
"tailscale.com/util/syspolicy/setting"
|
||||||
"tailscale.com/util/syspolicy/source"
|
"tailscale.com/util/syspolicy/source"
|
||||||
"tailscale.com/util/testenv"
|
"tailscale.com/util/testenv"
|
||||||
@@ -22,7 +23,7 @@ var someOtherError = errors.New("error other than not found")
|
|||||||
func TestGetString(t *testing.T) {
|
func TestGetString(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
key Key
|
key pkey.Key
|
||||||
handlerValue string
|
handlerValue string
|
||||||
handlerError error
|
handlerError error
|
||||||
defaultValue string
|
defaultValue string
|
||||||
@@ -32,7 +33,7 @@ func TestGetString(t *testing.T) {
|
|||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "read existing value",
|
name: "read existing value",
|
||||||
key: AdminConsoleVisibility,
|
key: pkey.AdminConsoleVisibility,
|
||||||
handlerValue: "hide",
|
handlerValue: "hide",
|
||||||
wantValue: "hide",
|
wantValue: "hide",
|
||||||
wantMetrics: []metrics.TestState{
|
wantMetrics: []metrics.TestState{
|
||||||
@@ -42,13 +43,13 @@ func TestGetString(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "read non-existing value",
|
name: "read non-existing value",
|
||||||
key: EnableServerMode,
|
key: pkey.EnableServerMode,
|
||||||
handlerError: ErrNotConfigured,
|
handlerError: ErrNotConfigured,
|
||||||
wantError: nil,
|
wantError: nil,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "read non-existing value, non-blank default",
|
name: "read non-existing value, non-blank default",
|
||||||
key: EnableServerMode,
|
key: pkey.EnableServerMode,
|
||||||
handlerError: ErrNotConfigured,
|
handlerError: ErrNotConfigured,
|
||||||
defaultValue: "test",
|
defaultValue: "test",
|
||||||
wantValue: "test",
|
wantValue: "test",
|
||||||
@@ -56,7 +57,7 @@ func TestGetString(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "reading value returns other error",
|
name: "reading value returns other error",
|
||||||
key: NetworkDevicesVisibility,
|
key: pkey.NetworkDevicesVisibility,
|
||||||
handlerError: someOtherError,
|
handlerError: someOtherError,
|
||||||
wantError: someOtherError,
|
wantError: someOtherError,
|
||||||
wantMetrics: []metrics.TestState{
|
wantMetrics: []metrics.TestState{
|
||||||
@@ -103,7 +104,7 @@ func TestGetString(t *testing.T) {
|
|||||||
func TestGetUint64(t *testing.T) {
|
func TestGetUint64(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
key Key
|
key pkey.Key
|
||||||
handlerValue uint64
|
handlerValue uint64
|
||||||
handlerError error
|
handlerError error
|
||||||
defaultValue uint64
|
defaultValue uint64
|
||||||
@@ -112,27 +113,27 @@ func TestGetUint64(t *testing.T) {
|
|||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "read existing value",
|
name: "read existing value",
|
||||||
key: LogSCMInteractions,
|
key: pkey.LogSCMInteractions,
|
||||||
handlerValue: 1,
|
handlerValue: 1,
|
||||||
wantValue: 1,
|
wantValue: 1,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "read non-existing value",
|
name: "read non-existing value",
|
||||||
key: LogSCMInteractions,
|
key: pkey.LogSCMInteractions,
|
||||||
handlerValue: 0,
|
handlerValue: 0,
|
||||||
handlerError: ErrNotConfigured,
|
handlerError: ErrNotConfigured,
|
||||||
wantValue: 0,
|
wantValue: 0,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "read non-existing value, non-zero default",
|
name: "read non-existing value, non-zero default",
|
||||||
key: LogSCMInteractions,
|
key: pkey.LogSCMInteractions,
|
||||||
defaultValue: 2,
|
defaultValue: 2,
|
||||||
handlerError: ErrNotConfigured,
|
handlerError: ErrNotConfigured,
|
||||||
wantValue: 2,
|
wantValue: 2,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "reading value returns other error",
|
name: "reading value returns other error",
|
||||||
key: FlushDNSOnSessionUnlock,
|
key: pkey.FlushDNSOnSessionUnlock,
|
||||||
handlerError: someOtherError,
|
handlerError: someOtherError,
|
||||||
wantError: someOtherError,
|
wantError: someOtherError,
|
||||||
},
|
},
|
||||||
@@ -169,7 +170,7 @@ func TestGetUint64(t *testing.T) {
|
|||||||
func TestGetBoolean(t *testing.T) {
|
func TestGetBoolean(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
key Key
|
key pkey.Key
|
||||||
handlerValue bool
|
handlerValue bool
|
||||||
handlerError error
|
handlerError error
|
||||||
defaultValue bool
|
defaultValue bool
|
||||||
@@ -179,7 +180,7 @@ func TestGetBoolean(t *testing.T) {
|
|||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "read existing value",
|
name: "read existing value",
|
||||||
key: FlushDNSOnSessionUnlock,
|
key: pkey.FlushDNSOnSessionUnlock,
|
||||||
handlerValue: true,
|
handlerValue: true,
|
||||||
wantValue: true,
|
wantValue: true,
|
||||||
wantMetrics: []metrics.TestState{
|
wantMetrics: []metrics.TestState{
|
||||||
@@ -189,14 +190,14 @@ func TestGetBoolean(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "read non-existing value",
|
name: "read non-existing value",
|
||||||
key: LogSCMInteractions,
|
key: pkey.LogSCMInteractions,
|
||||||
handlerValue: false,
|
handlerValue: false,
|
||||||
handlerError: ErrNotConfigured,
|
handlerError: ErrNotConfigured,
|
||||||
wantValue: false,
|
wantValue: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "reading value returns other error",
|
name: "reading value returns other error",
|
||||||
key: FlushDNSOnSessionUnlock,
|
key: pkey.FlushDNSOnSessionUnlock,
|
||||||
handlerError: someOtherError,
|
handlerError: someOtherError,
|
||||||
wantError: someOtherError, // expect error...
|
wantError: someOtherError, // expect error...
|
||||||
defaultValue: true,
|
defaultValue: true,
|
||||||
@@ -245,7 +246,7 @@ func TestGetBoolean(t *testing.T) {
|
|||||||
func TestGetPreferenceOption(t *testing.T) {
|
func TestGetPreferenceOption(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
key Key
|
key pkey.Key
|
||||||
handlerValue string
|
handlerValue string
|
||||||
handlerError error
|
handlerError error
|
||||||
wantValue setting.PreferenceOption
|
wantValue setting.PreferenceOption
|
||||||
@@ -254,7 +255,7 @@ func TestGetPreferenceOption(t *testing.T) {
|
|||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "always by policy",
|
name: "always by policy",
|
||||||
key: EnableIncomingConnections,
|
key: pkey.EnableIncomingConnections,
|
||||||
handlerValue: "always",
|
handlerValue: "always",
|
||||||
wantValue: setting.AlwaysByPolicy,
|
wantValue: setting.AlwaysByPolicy,
|
||||||
wantMetrics: []metrics.TestState{
|
wantMetrics: []metrics.TestState{
|
||||||
@@ -264,7 +265,7 @@ func TestGetPreferenceOption(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "never by policy",
|
name: "never by policy",
|
||||||
key: EnableIncomingConnections,
|
key: pkey.EnableIncomingConnections,
|
||||||
handlerValue: "never",
|
handlerValue: "never",
|
||||||
wantValue: setting.NeverByPolicy,
|
wantValue: setting.NeverByPolicy,
|
||||||
wantMetrics: []metrics.TestState{
|
wantMetrics: []metrics.TestState{
|
||||||
@@ -274,7 +275,7 @@ func TestGetPreferenceOption(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "use default",
|
name: "use default",
|
||||||
key: EnableIncomingConnections,
|
key: pkey.EnableIncomingConnections,
|
||||||
handlerValue: "",
|
handlerValue: "",
|
||||||
wantValue: setting.ShowChoiceByPolicy,
|
wantValue: setting.ShowChoiceByPolicy,
|
||||||
wantMetrics: []metrics.TestState{
|
wantMetrics: []metrics.TestState{
|
||||||
@@ -284,13 +285,13 @@ func TestGetPreferenceOption(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "read non-existing value",
|
name: "read non-existing value",
|
||||||
key: EnableIncomingConnections,
|
key: pkey.EnableIncomingConnections,
|
||||||
handlerError: ErrNotConfigured,
|
handlerError: ErrNotConfigured,
|
||||||
wantValue: setting.ShowChoiceByPolicy,
|
wantValue: setting.ShowChoiceByPolicy,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "other error is returned",
|
name: "other error is returned",
|
||||||
key: EnableIncomingConnections,
|
key: pkey.EnableIncomingConnections,
|
||||||
handlerError: someOtherError,
|
handlerError: someOtherError,
|
||||||
wantValue: setting.ShowChoiceByPolicy,
|
wantValue: setting.ShowChoiceByPolicy,
|
||||||
wantError: someOtherError,
|
wantError: someOtherError,
|
||||||
@@ -338,7 +339,7 @@ func TestGetPreferenceOption(t *testing.T) {
|
|||||||
func TestGetVisibility(t *testing.T) {
|
func TestGetVisibility(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
key Key
|
key pkey.Key
|
||||||
handlerValue string
|
handlerValue string
|
||||||
handlerError error
|
handlerError error
|
||||||
wantValue setting.Visibility
|
wantValue setting.Visibility
|
||||||
@@ -347,7 +348,7 @@ func TestGetVisibility(t *testing.T) {
|
|||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "hidden by policy",
|
name: "hidden by policy",
|
||||||
key: AdminConsoleVisibility,
|
key: pkey.AdminConsoleVisibility,
|
||||||
handlerValue: "hide",
|
handlerValue: "hide",
|
||||||
wantValue: setting.HiddenByPolicy,
|
wantValue: setting.HiddenByPolicy,
|
||||||
wantMetrics: []metrics.TestState{
|
wantMetrics: []metrics.TestState{
|
||||||
@@ -357,7 +358,7 @@ func TestGetVisibility(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "visibility default",
|
name: "visibility default",
|
||||||
key: AdminConsoleVisibility,
|
key: pkey.AdminConsoleVisibility,
|
||||||
handlerValue: "show",
|
handlerValue: "show",
|
||||||
wantValue: setting.VisibleByPolicy,
|
wantValue: setting.VisibleByPolicy,
|
||||||
wantMetrics: []metrics.TestState{
|
wantMetrics: []metrics.TestState{
|
||||||
@@ -367,14 +368,14 @@ func TestGetVisibility(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "read non-existing value",
|
name: "read non-existing value",
|
||||||
key: AdminConsoleVisibility,
|
key: pkey.AdminConsoleVisibility,
|
||||||
handlerValue: "show",
|
handlerValue: "show",
|
||||||
handlerError: ErrNotConfigured,
|
handlerError: ErrNotConfigured,
|
||||||
wantValue: setting.VisibleByPolicy,
|
wantValue: setting.VisibleByPolicy,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "other error is returned",
|
name: "other error is returned",
|
||||||
key: AdminConsoleVisibility,
|
key: pkey.AdminConsoleVisibility,
|
||||||
handlerValue: "show",
|
handlerValue: "show",
|
||||||
handlerError: someOtherError,
|
handlerError: someOtherError,
|
||||||
wantValue: setting.VisibleByPolicy,
|
wantValue: setting.VisibleByPolicy,
|
||||||
@@ -423,7 +424,7 @@ func TestGetVisibility(t *testing.T) {
|
|||||||
func TestGetDuration(t *testing.T) {
|
func TestGetDuration(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
key Key
|
key pkey.Key
|
||||||
handlerValue string
|
handlerValue string
|
||||||
handlerError error
|
handlerError error
|
||||||
defaultValue time.Duration
|
defaultValue time.Duration
|
||||||
@@ -433,7 +434,7 @@ func TestGetDuration(t *testing.T) {
|
|||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "read existing value",
|
name: "read existing value",
|
||||||
key: KeyExpirationNoticeTime,
|
key: pkey.KeyExpirationNoticeTime,
|
||||||
handlerValue: "2h",
|
handlerValue: "2h",
|
||||||
wantValue: 2 * time.Hour,
|
wantValue: 2 * time.Hour,
|
||||||
defaultValue: 24 * time.Hour,
|
defaultValue: 24 * time.Hour,
|
||||||
@@ -444,7 +445,7 @@ func TestGetDuration(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "invalid duration value",
|
name: "invalid duration value",
|
||||||
key: KeyExpirationNoticeTime,
|
key: pkey.KeyExpirationNoticeTime,
|
||||||
handlerValue: "-20",
|
handlerValue: "-20",
|
||||||
wantValue: 24 * time.Hour,
|
wantValue: 24 * time.Hour,
|
||||||
wantError: errors.New(`time: missing unit in duration "-20"`),
|
wantError: errors.New(`time: missing unit in duration "-20"`),
|
||||||
@@ -456,21 +457,21 @@ func TestGetDuration(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "read non-existing value",
|
name: "read non-existing value",
|
||||||
key: KeyExpirationNoticeTime,
|
key: pkey.KeyExpirationNoticeTime,
|
||||||
handlerError: ErrNotConfigured,
|
handlerError: ErrNotConfigured,
|
||||||
wantValue: 24 * time.Hour,
|
wantValue: 24 * time.Hour,
|
||||||
defaultValue: 24 * time.Hour,
|
defaultValue: 24 * time.Hour,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "read non-existing value different default",
|
name: "read non-existing value different default",
|
||||||
key: KeyExpirationNoticeTime,
|
key: pkey.KeyExpirationNoticeTime,
|
||||||
handlerError: ErrNotConfigured,
|
handlerError: ErrNotConfigured,
|
||||||
wantValue: 0 * time.Second,
|
wantValue: 0 * time.Second,
|
||||||
defaultValue: 0 * time.Second,
|
defaultValue: 0 * time.Second,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "other error is returned",
|
name: "other error is returned",
|
||||||
key: KeyExpirationNoticeTime,
|
key: pkey.KeyExpirationNoticeTime,
|
||||||
handlerError: someOtherError,
|
handlerError: someOtherError,
|
||||||
wantValue: 24 * time.Hour,
|
wantValue: 24 * time.Hour,
|
||||||
wantError: someOtherError,
|
wantError: someOtherError,
|
||||||
@@ -519,7 +520,7 @@ func TestGetDuration(t *testing.T) {
|
|||||||
func TestGetStringArray(t *testing.T) {
|
func TestGetStringArray(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
key Key
|
key pkey.Key
|
||||||
handlerValue []string
|
handlerValue []string
|
||||||
handlerError error
|
handlerError error
|
||||||
defaultValue []string
|
defaultValue []string
|
||||||
@@ -529,7 +530,7 @@ func TestGetStringArray(t *testing.T) {
|
|||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "read existing value",
|
name: "read existing value",
|
||||||
key: AllowedSuggestedExitNodes,
|
key: pkey.AllowedSuggestedExitNodes,
|
||||||
handlerValue: []string{"foo", "bar"},
|
handlerValue: []string{"foo", "bar"},
|
||||||
wantValue: []string{"foo", "bar"},
|
wantValue: []string{"foo", "bar"},
|
||||||
wantMetrics: []metrics.TestState{
|
wantMetrics: []metrics.TestState{
|
||||||
@@ -539,13 +540,13 @@ func TestGetStringArray(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "read non-existing value",
|
name: "read non-existing value",
|
||||||
key: AllowedSuggestedExitNodes,
|
key: pkey.AllowedSuggestedExitNodes,
|
||||||
handlerError: ErrNotConfigured,
|
handlerError: ErrNotConfigured,
|
||||||
wantError: nil,
|
wantError: nil,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "read non-existing value, non nil default",
|
name: "read non-existing value, non nil default",
|
||||||
key: AllowedSuggestedExitNodes,
|
key: pkey.AllowedSuggestedExitNodes,
|
||||||
handlerError: ErrNotConfigured,
|
handlerError: ErrNotConfigured,
|
||||||
defaultValue: []string{"foo", "bar"},
|
defaultValue: []string{"foo", "bar"},
|
||||||
wantValue: []string{"foo", "bar"},
|
wantValue: []string{"foo", "bar"},
|
||||||
@@ -553,7 +554,7 @@ func TestGetStringArray(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "reading value returns other error",
|
name: "reading value returns other error",
|
||||||
key: AllowedSuggestedExitNodes,
|
key: pkey.AllowedSuggestedExitNodes,
|
||||||
handlerError: someOtherError,
|
handlerError: someOtherError,
|
||||||
wantError: someOtherError,
|
wantError: someOtherError,
|
||||||
wantMetrics: []metrics.TestState{
|
wantMetrics: []metrics.TestState{
|
||||||
@@ -607,11 +608,11 @@ func BenchmarkGetString(b *testing.B) {
|
|||||||
RegisterWellKnownSettingsForTest(b)
|
RegisterWellKnownSettingsForTest(b)
|
||||||
|
|
||||||
wantControlURL := "https://login.tailscale.com"
|
wantControlURL := "https://login.tailscale.com"
|
||||||
registerSingleSettingStoreForTest(b, source.TestSettingOf(ControlURL, wantControlURL))
|
registerSingleSettingStoreForTest(b, source.TestSettingOf(pkey.ControlURL, wantControlURL))
|
||||||
|
|
||||||
b.ResetTimer()
|
b.ResetTimer()
|
||||||
for i := 0; i < b.N; i++ {
|
for i := 0; i < b.N; i++ {
|
||||||
gotControlURL, _ := GetString(ControlURL, "https://controlplane.tailscale.com")
|
gotControlURL, _ := GetString(pkey.ControlURL, "https://controlplane.tailscale.com")
|
||||||
if gotControlURL != wantControlURL {
|
if gotControlURL != wantControlURL {
|
||||||
b.Fatalf("got %v; want %v", gotControlURL, wantControlURL)
|
b.Fatalf("got %v; want %v", gotControlURL, wantControlURL)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user