Brad Fitzpatrick d05e6dc09e util/syspolicy/policyclient: add policyclient.Client interface, start plumbing
This is step 2 of ~4, breaking up #14720 into reviewable chunks, with
the aim to make syspolicy be a build-time configurable feature.

Step 1 was #16984.

In this second step, the util/syspolicy/policyclient package is added
with the policyclient.Client interface.  This is the interface that's
always present (regardless of build tags), and is what code around the
tree uses to ask syspolicy/MDM questions.

There are two implementations of policyclient.Client for now:

1) NoPolicyClient, which only returns default values.
2) the unexported, temporary 'globalSyspolicy', which is implemented
   in terms of the global functions we wish to later eliminate.

This then starts to plumb around the policyclient.Client to most callers.

Future changes will plumb it more. When the last of the global func
callers are gone, then we can unexport the global functions and make a
proper policyclient.Client type and constructor in the syspolicy
package, removing the globalSyspolicy impl out of tsd.

The final change will sprinkle build tags in a few more places and
lock it in with dependency tests to make sure the dependencies don't
later creep back in.

Updates #16998
Updates #12614

Change-Id: Ib2c93d15c15c1f2b981464099177cd492d50391c
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
2025-09-01 09:34:29 -07:00

67 lines
2.5 KiB
Go

// Copyright (c) Tailscale Inc & AUTHORS
// SPDX-License-Identifier: BSD-3-Clause
// Package policyclient contains the minimal syspolicy interface as needed by
// client code using syspolicy. It's the part that's always linked in, even if the rest
// of syspolicy is omitted from the build.
package policyclient
import "tailscale.com/util/syspolicy/pkey"
// Client is the interface between code making questions about the system policy
// and the actual implementation.
type Client interface {
// GetString returns a string policy setting with the specified key,
// or defaultValue (and a nil error) if it does not exist.
GetString(key pkey.Key, defaultValue string) (string, error)
// GetStringArray returns a string array policy setting with the specified key,
// or defaultValue (and a nil error) if it does not exist.
GetStringArray(key pkey.Key, defaultValue []string) ([]string, error)
// GetBoolean returns a boolean policy setting with the specified key,
// or defaultValue (and a nil error) if it does not exist.
GetBoolean(key pkey.Key, defaultValue bool) (bool, error)
// SetDebugLoggingEnabled enables or disables debug logging for the policy client.
SetDebugLoggingEnabled(enabled bool)
// RegisterChangeCallback registers a callback function that will be called
// whenever a policy change is detected. It returns a function to unregister
// the callback and an error if the registration fails.
RegisterChangeCallback(cb func(PolicyChange)) (unregister func(), err error)
}
// PolicyChange is the interface representing a change in policy settings.
type PolicyChange interface {
// HasChanged reports whether the policy setting identified by the given key
// has changed.
HasChanged(pkey.Key) bool
// HasChangedAnyOf reports whether any of the provided policy settings
// changed in this change.
HasChangedAnyOf(keys ...pkey.Key) bool
}
// NoPolicyClient is a no-op implementation of [Client] that only
// returns default values.
type NoPolicyClient struct{}
func (NoPolicyClient) GetBoolean(key pkey.Key, defaultValue bool) (bool, error) {
return defaultValue, nil
}
func (NoPolicyClient) GetString(key pkey.Key, defaultValue string) (string, error) {
return defaultValue, nil
}
func (NoPolicyClient) GetStringArray(key pkey.Key, defaultValue []string) ([]string, error) {
return defaultValue, nil
}
func (NoPolicyClient) SetDebugLoggingEnabled(enabled bool) {}
func (NoPolicyClient) RegisterChangeCallback(cb func(PolicyChange)) (unregister func(), err error) {
return func() {}, nil
}