mirror of
https://github.com/tailscale/tailscale.git
synced 2025-10-19 06:02:57 +00:00
util/syspolicy: finish plumbing policyclient, add feature/syspolicy, move global impl
This is step 4 of making syspolicy a build-time feature. This adds a policyclient.Get() accessor to return the correct implementation to use: either the real one, or the no-op one. (A third type, a static one for testing, also exists, so in general a policyclient.Client should be plumbed around and not always fetched via policyclient.Get whenever possible, especially if tests need to use alternate syspolicy) Updates #16998 Updates #12614 Change-Id: Iaf19670744a596d5918acfa744f5db4564272978 Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
This commit is contained in:

committed by
Brad Fitzpatrick

parent
9e9bf13063
commit
2b3e533048
@@ -44,8 +44,8 @@ type Client interface {
|
||||
// overrides of users' choices in a way that we do not want tailcontrol to have
|
||||
// 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
|
||||
// present or set to a different value, "user-decides" is the default.
|
||||
GetPreferenceOption(key pkey.Key) (ptype.PreferenceOption, error)
|
||||
// present or set to a different value, defaultValue (and a nil error) is returned.
|
||||
GetPreferenceOption(key pkey.Key, defaultValue ptype.PreferenceOption) (ptype.PreferenceOption, error)
|
||||
|
||||
// GetVisibility returns whether a UI element should be visible based on
|
||||
// the system's configuration.
|
||||
@@ -66,6 +66,21 @@ type Client interface {
|
||||
RegisterChangeCallback(cb func(PolicyChange)) (unregister func(), err error)
|
||||
}
|
||||
|
||||
// Get returns a non-nil [Client] implementation as a function of the
|
||||
// build tags. It returns a no-op implementation if the full syspolicy
|
||||
// package is omitted from the build.
|
||||
func Get() Client {
|
||||
return client
|
||||
}
|
||||
|
||||
// RegisterClientImpl registers a [Client] implementation to be returned by
|
||||
// [Get].
|
||||
func RegisterClientImpl(c Client) {
|
||||
client = c
|
||||
}
|
||||
|
||||
var client Client = NoPolicyClient{}
|
||||
|
||||
// PolicyChange is the interface representing a change in policy settings.
|
||||
type PolicyChange interface {
|
||||
// HasChanged reports whether the policy setting identified by the given key
|
||||
@@ -81,6 +96,8 @@ type PolicyChange interface {
|
||||
// returns default values.
|
||||
type NoPolicyClient struct{}
|
||||
|
||||
var _ Client = NoPolicyClient{}
|
||||
|
||||
func (NoPolicyClient) GetBoolean(key pkey.Key, defaultValue bool) (bool, error) {
|
||||
return defaultValue, nil
|
||||
}
|
||||
@@ -101,8 +118,8 @@ func (NoPolicyClient) GetDuration(name pkey.Key, defaultValue time.Duration) (ti
|
||||
return defaultValue, nil
|
||||
}
|
||||
|
||||
func (NoPolicyClient) GetPreferenceOption(name pkey.Key) (ptype.PreferenceOption, error) {
|
||||
return ptype.ShowChoiceByPolicy, nil
|
||||
func (NoPolicyClient) GetPreferenceOption(name pkey.Key, defaultValue ptype.PreferenceOption) (ptype.PreferenceOption, error) {
|
||||
return defaultValue, nil
|
||||
}
|
||||
|
||||
func (NoPolicyClient) GetVisibility(name pkey.Key) (ptype.Visibility, error) {
|
||||
|
@@ -1,13 +1,9 @@
|
||||
// Copyright (c) Tailscale Inc & AUTHORS
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
// Package syspolicy facilitates retrieval of the current policy settings
|
||||
// applied to the device or user and receiving notifications when the policy
|
||||
// changes.
|
||||
//
|
||||
// It provides functions that return specific policy settings by their unique
|
||||
// [setting.Key]s, such as [GetBoolean], [GetUint64], [GetString],
|
||||
// [GetStringArray], [GetPreferenceOption], [GetVisibility] and [GetDuration].
|
||||
// Package syspolicy contains the implementation of system policy management.
|
||||
// Calling code should use the client interface in
|
||||
// tailscale.com/util/syspolicy/policyclient.
|
||||
package syspolicy
|
||||
|
||||
import (
|
||||
@@ -18,6 +14,7 @@ import (
|
||||
|
||||
"tailscale.com/util/syspolicy/internal/loggerx"
|
||||
"tailscale.com/util/syspolicy/pkey"
|
||||
"tailscale.com/util/syspolicy/policyclient"
|
||||
"tailscale.com/util/syspolicy/ptype"
|
||||
"tailscale.com/util/syspolicy/rsop"
|
||||
"tailscale.com/util/syspolicy/setting"
|
||||
@@ -58,9 +55,9 @@ func MustRegisterStoreForTest(tb testenv.TB, name string, scope setting.PolicySc
|
||||
return reg
|
||||
}
|
||||
|
||||
// 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.
|
||||
func HasAnyOf(keys ...pkey.Key) (bool, error) {
|
||||
func hasAnyOf(keys ...pkey.Key) (bool, error) {
|
||||
if len(keys) == 0 {
|
||||
return false, errors.New("at least one key must be specified")
|
||||
}
|
||||
@@ -82,62 +79,55 @@ func HasAnyOf(keys ...pkey.Key) (bool, error) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// 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.
|
||||
func GetString(key pkey.Key, defaultValue string) (string, error) {
|
||||
func getString(key pkey.Key, defaultValue string) (string, error) {
|
||||
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.
|
||||
func GetUint64(key pkey.Key, defaultValue uint64) (uint64, error) {
|
||||
func getUint64(key pkey.Key, defaultValue uint64) (uint64, error) {
|
||||
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.
|
||||
func GetBoolean(key pkey.Key, defaultValue bool) (bool, error) {
|
||||
func getBoolean(key pkey.Key, defaultValue bool) (bool, error) {
|
||||
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.
|
||||
func GetStringArray(key pkey.Key, defaultValue []string) ([]string, error) {
|
||||
func getStringArray(key pkey.Key, defaultValue []string) ([]string, error) {
|
||||
return getCurrentPolicySettingValue(key, defaultValue)
|
||||
}
|
||||
|
||||
// GetPreferenceOption loads a policy from the registry that can be
|
||||
// getPreferenceOption loads a policy from the registry that can be
|
||||
// managed by an enterprise policy management system and allows administrative
|
||||
// overrides of users' choices in a way that we do not want tailcontrol to have
|
||||
// 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
|
||||
// present or set to a different value, "user-decides" is the default.
|
||||
func GetPreferenceOption(name pkey.Key) (ptype.PreferenceOption, error) {
|
||||
return getCurrentPolicySettingValue(name, ptype.ShowChoiceByPolicy)
|
||||
}
|
||||
|
||||
// GetPreferenceOptionOrDefault is like [GetPreferenceOption], but allows
|
||||
// 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.
|
||||
func GetPreferenceOptionOrDefault(name pkey.Key, defaultValue ptype.PreferenceOption) (ptype.PreferenceOption, error) {
|
||||
// present or set to a different value, defaultValue (and a nil error) is returned.
|
||||
func getPreferenceOption(name pkey.Key, defaultValue ptype.PreferenceOption) (ptype.PreferenceOption, error) {
|
||||
return getCurrentPolicySettingValue(name, defaultValue)
|
||||
}
|
||||
|
||||
// GetVisibility loads a policy from the registry that can be managed
|
||||
// getVisibility loads a policy from the registry that can be managed
|
||||
// by an enterprise policy management system and describes show/hide decisions
|
||||
// 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,
|
||||
// "show" (return false) is the default.
|
||||
func GetVisibility(name pkey.Key) (ptype.Visibility, error) {
|
||||
func getVisibility(name pkey.Key) (ptype.Visibility, error) {
|
||||
return getCurrentPolicySettingValue(name, ptype.VisibleByPolicy)
|
||||
}
|
||||
|
||||
// GetDuration loads a policy from the registry that can be managed
|
||||
// getDuration loads a policy from the registry that can be managed
|
||||
// by an enterprise policy management system and describes a duration for some
|
||||
// action. The registry value should be a string that time.ParseDuration
|
||||
// understands. If the registry value is "" or can not be processed,
|
||||
// defaultValue is returned instead.
|
||||
func GetDuration(name pkey.Key, defaultValue time.Duration) (time.Duration, error) {
|
||||
func getDuration(name pkey.Key, defaultValue time.Duration) (time.Duration, error) {
|
||||
d, err := getCurrentPolicySettingValue(name, defaultValue)
|
||||
if err != nil {
|
||||
return d, err
|
||||
@@ -148,9 +138,9 @@ func GetDuration(name pkey.Key, defaultValue time.Duration) (time.Duration, erro
|
||||
return d, nil
|
||||
}
|
||||
|
||||
// RegisterChangeCallback adds a function that will be called whenever the effective policy
|
||||
// registerChangeCallback adds a function that will be called whenever the effective policy
|
||||
// for the default scope changes. The returned function can be used to unregister the callback.
|
||||
func RegisterChangeCallback(cb rsop.PolicyChangeCallback) (unregister func(), err error) {
|
||||
func registerChangeCallback(cb rsop.PolicyChangeCallback) (unregister func(), err error) {
|
||||
effective, err := rsop.PolicyFor(setting.DefaultScope())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -233,7 +223,53 @@ func SelectControlURL(reg, disk string) string {
|
||||
return def
|
||||
}
|
||||
|
||||
// SetDebugLoggingEnabled controls whether spammy debug logging is enabled.
|
||||
func SetDebugLoggingEnabled(v bool) {
|
||||
loggerx.SetDebugLoggingEnabled(v)
|
||||
func init() {
|
||||
policyclient.RegisterClientImpl(globalSyspolicy{})
|
||||
}
|
||||
|
||||
// globalSyspolicy implements [policyclient.Client] using the syspolicy global
|
||||
// functions and global registrations.
|
||||
//
|
||||
// TODO: de-global-ify. This implementation using the old global functions
|
||||
// is an intermediate stage while changing policyclient to be modular.
|
||||
type globalSyspolicy struct{}
|
||||
|
||||
func (globalSyspolicy) GetBoolean(key pkey.Key, defaultValue bool) (bool, error) {
|
||||
return getBoolean(key, defaultValue)
|
||||
}
|
||||
|
||||
func (globalSyspolicy) GetString(key pkey.Key, defaultValue string) (string, error) {
|
||||
return getString(key, defaultValue)
|
||||
}
|
||||
|
||||
func (globalSyspolicy) GetStringArray(key pkey.Key, defaultValue []string) ([]string, error) {
|
||||
return getStringArray(key, defaultValue)
|
||||
}
|
||||
|
||||
func (globalSyspolicy) SetDebugLoggingEnabled(enabled bool) {
|
||||
loggerx.SetDebugLoggingEnabled(enabled)
|
||||
}
|
||||
|
||||
func (globalSyspolicy) GetUint64(key pkey.Key, defaultValue uint64) (uint64, error) {
|
||||
return getUint64(key, defaultValue)
|
||||
}
|
||||
|
||||
func (globalSyspolicy) GetDuration(name pkey.Key, defaultValue time.Duration) (time.Duration, error) {
|
||||
return getDuration(name, defaultValue)
|
||||
}
|
||||
|
||||
func (globalSyspolicy) GetPreferenceOption(name pkey.Key, defaultValue ptype.PreferenceOption) (ptype.PreferenceOption, error) {
|
||||
return getPreferenceOption(name, defaultValue)
|
||||
}
|
||||
|
||||
func (globalSyspolicy) GetVisibility(name pkey.Key) (ptype.Visibility, error) {
|
||||
return getVisibility(name)
|
||||
}
|
||||
|
||||
func (globalSyspolicy) HasAnyOf(keys ...pkey.Key) (bool, error) {
|
||||
return hasAnyOf(keys...)
|
||||
}
|
||||
|
||||
func (globalSyspolicy) RegisterChangeCallback(cb func(policyclient.PolicyChange)) (unregister func(), err error) {
|
||||
return registerChangeCallback(cb)
|
||||
}
|
||||
|
@@ -82,7 +82,7 @@ func TestGetString(t *testing.T) {
|
||||
}
|
||||
registerSingleSettingStoreForTest(t, s)
|
||||
|
||||
value, err := GetString(tt.key, tt.defaultValue)
|
||||
value, err := getString(tt.key, tt.defaultValue)
|
||||
if !errorsMatchForTest(err, tt.wantError) {
|
||||
t.Errorf("err=%q, want %q", err, tt.wantError)
|
||||
}
|
||||
@@ -157,7 +157,7 @@ func TestGetUint64(t *testing.T) {
|
||||
}
|
||||
registerSingleSettingStoreForTest(t, s)
|
||||
|
||||
value, err := GetUint64(tt.key, tt.defaultValue)
|
||||
value, err := getUint64(tt.key, tt.defaultValue)
|
||||
if !errorsMatchForTest(err, tt.wantError) {
|
||||
t.Errorf("err=%q, want %q", err, tt.wantError)
|
||||
}
|
||||
@@ -224,7 +224,7 @@ func TestGetBoolean(t *testing.T) {
|
||||
}
|
||||
registerSingleSettingStoreForTest(t, s)
|
||||
|
||||
value, err := GetBoolean(tt.key, tt.defaultValue)
|
||||
value, err := getBoolean(tt.key, tt.defaultValue)
|
||||
if !errorsMatchForTest(err, tt.wantError) {
|
||||
t.Errorf("err=%q, want %q", err, tt.wantError)
|
||||
}
|
||||
@@ -317,7 +317,7 @@ func TestGetPreferenceOption(t *testing.T) {
|
||||
}
|
||||
registerSingleSettingStoreForTest(t, s)
|
||||
|
||||
option, err := GetPreferenceOption(tt.key)
|
||||
option, err := getPreferenceOption(tt.key, ptype.ShowChoiceByPolicy)
|
||||
if !errorsMatchForTest(err, tt.wantError) {
|
||||
t.Errorf("err=%q, want %q", err, tt.wantError)
|
||||
}
|
||||
@@ -402,7 +402,7 @@ func TestGetVisibility(t *testing.T) {
|
||||
}
|
||||
registerSingleSettingStoreForTest(t, s)
|
||||
|
||||
visibility, err := GetVisibility(tt.key)
|
||||
visibility, err := getVisibility(tt.key)
|
||||
if !errorsMatchForTest(err, tt.wantError) {
|
||||
t.Errorf("err=%q, want %q", err, tt.wantError)
|
||||
}
|
||||
@@ -498,7 +498,7 @@ func TestGetDuration(t *testing.T) {
|
||||
}
|
||||
registerSingleSettingStoreForTest(t, s)
|
||||
|
||||
duration, err := GetDuration(tt.key, tt.defaultValue)
|
||||
duration, err := getDuration(tt.key, tt.defaultValue)
|
||||
if !errorsMatchForTest(err, tt.wantError) {
|
||||
t.Errorf("err=%q, want %q", err, tt.wantError)
|
||||
}
|
||||
@@ -579,7 +579,7 @@ func TestGetStringArray(t *testing.T) {
|
||||
}
|
||||
registerSingleSettingStoreForTest(t, s)
|
||||
|
||||
value, err := GetStringArray(tt.key, tt.defaultValue)
|
||||
value, err := getStringArray(tt.key, tt.defaultValue)
|
||||
if !errorsMatchForTest(err, tt.wantError) {
|
||||
t.Errorf("err=%q, want %q", err, tt.wantError)
|
||||
}
|
||||
@@ -613,7 +613,7 @@ func BenchmarkGetString(b *testing.B) {
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
gotControlURL, _ := GetString(pkey.ControlURL, "https://controlplane.tailscale.com")
|
||||
gotControlURL, _ := getString(pkey.ControlURL, "https://controlplane.tailscale.com")
|
||||
if gotControlURL != wantControlURL {
|
||||
b.Fatalf("got %v; want %v", gotControlURL, wantControlURL)
|
||||
}
|
||||
|
Reference in New Issue
Block a user