| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146 |
- // Copyright (c) Tailscale Inc & AUTHORS
- // SPDX-License-Identifier: BSD-3-Clause
- // Package source defines interfaces for policy stores,
- // facilitates the creation of policy sources, and provides
- // functionality for reading policy settings from these sources.
- package source
- import (
- "cmp"
- "errors"
- "fmt"
- "io"
- "tailscale.com/types/lazy"
- "tailscale.com/util/syspolicy/setting"
- )
- // ErrStoreClosed is an error returned when attempting to use a [Store] after it
- // has been closed.
- var ErrStoreClosed = errors.New("the policy store has been closed")
- // Store provides methods to read system policy settings from OS-specific storage.
- // Implementations must be concurrency-safe, and may also implement
- // [Lockable], [Changeable], [Expirable] and [io.Closer].
- //
- // If a [Store] implementation also implements [io.Closer],
- // it will be called by the package to release the resources
- // when the store is no longer needed.
- type Store interface {
- // ReadString returns the value of a [setting.StringValue] with the specified key,
- // an [setting.ErrNotConfigured] if the policy setting is not configured, or
- // an error on failure.
- ReadString(key setting.Key) (string, error)
- // ReadUInt64 returns the value of a [setting.IntegerValue] with the specified key,
- // an [setting.ErrNotConfigured] if the policy setting is not configured, or
- // an error on failure.
- ReadUInt64(key setting.Key) (uint64, error)
- // ReadBoolean returns the value of a [setting.BooleanValue] with the specified key,
- // an [setting.ErrNotConfigured] if the policy setting is not configured, or
- // an error on failure.
- ReadBoolean(key setting.Key) (bool, error)
- // ReadStringArray returns the value of a [setting.StringListValue] with the specified key,
- // an [setting.ErrNotConfigured] if the policy setting is not configured, or
- // an error on failure.
- ReadStringArray(key setting.Key) ([]string, error)
- }
- // Lockable is an optional interface that [Store] implementations may support.
- // Locking a [Store] is not mandatory as [Store] must be concurrency-safe,
- // but is recommended to avoid issues where consecutive read calls for related
- // policies might return inconsistent results if a policy change occurs between
- // the calls. Implementations may use locking to pre-read policies or for
- // similar performance optimizations.
- type Lockable interface {
- // Lock acquires a read lock on the policy store,
- // ensuring the store's state remains unchanged while locked.
- // Multiple readers can hold the lock simultaneously.
- // It returns an error if the store cannot be locked.
- Lock() error
- // Unlock unlocks the policy store.
- // It is a run-time error if the store is not locked on entry to Unlock.
- Unlock()
- }
- // Changeable is an optional interface that [Store] implementations may support
- // if the policy settings they contain can be externally changed after being initially read.
- type Changeable interface {
- // RegisterChangeCallback adds a function that will be called
- // whenever there's a policy change in the [Store].
- // The returned function can be used to unregister the callback.
- RegisterChangeCallback(callback func()) (unregister func(), err error)
- }
- // Expirable is an optional interface that [Store] implementations may support
- // if they can be externally closed or otherwise become invalid while in use.
- type Expirable interface {
- // Done returns a channel that is closed when the policy [Store] should no longer be used.
- // It should return nil if the store never expires.
- Done() <-chan struct{}
- }
- // Source represents a named source of policy settings for a given [setting.PolicyScope].
- type Source struct {
- name string
- scope setting.PolicyScope
- store Store
- origin *setting.Origin
- lazyReader lazy.SyncValue[*Reader]
- }
- // NewSource returns a new [Source] with the specified name, scope, and store.
- func NewSource(name string, scope setting.PolicyScope, store Store) *Source {
- return &Source{name: name, scope: scope, store: store, origin: setting.NewNamedOrigin(name, scope)}
- }
- // Name reports the name of the policy source.
- func (s *Source) Name() string {
- return s.name
- }
- // Scope reports the management scope of the policy source.
- func (s *Source) Scope() setting.PolicyScope {
- return s.scope
- }
- // Reader returns a [Reader] that reads from this source's [Store].
- func (s *Source) Reader() (*Reader, error) {
- return s.lazyReader.GetErr(func() (*Reader, error) {
- return newReader(s.store, s.origin)
- })
- }
- // Description returns a formatted string with the scope and name of this policy source.
- // It can be used for logging or display purposes.
- func (s *Source) Description() string {
- if s.name != "" {
- return fmt.Sprintf("%s (%v)", s.name, s.Scope())
- }
- return s.Scope().String()
- }
- // Compare returns an integer comparing s and s2
- // by their precedence, following the "last-wins" model.
- // The result will be:
- //
- // -1 if policy settings from s should be processed before policy settings from s2;
- // +1 if policy settings from s should be processed after policy settings from s2, overriding s2;
- // 0 if the relative processing order of policy settings in s and s2 is unspecified.
- func (s *Source) Compare(s2 *Source) int {
- return cmp.Compare(s2.Scope().Kind(), s.Scope().Kind())
- }
- // Close closes the [Source] and the underlying [Store].
- func (s *Source) Close() error {
- // The [Reader], if any, owns the [Store].
- if reader, _ := s.lazyReader.GetErr(func() (*Reader, error) { return nil, ErrStoreClosed }); reader != nil {
- return reader.Close()
- }
- // Otherwise, it is our responsibility to close it.
- if closer, ok := s.store.(io.Closer); ok {
- return closer.Close()
- }
- return nil
- }
|