mirror of
https://github.com/fleetdm/fleet
synced 2026-05-06 14:58:33 +00:00
This implements what's described in detail here https://github.com/fleetdm/fleet/blob/main/proposals/fleet-desktop-token-rotation.md
84 lines
1.8 KiB
Go
84 lines
1.8 KiB
Go
package token
|
|
|
|
import (
|
|
"os"
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
// Reader is used to read the token value from a file
|
|
type Reader struct {
|
|
Path string // a path to a file containing a token
|
|
|
|
mu sync.Mutex // ensures atomic reads/writes; protects the following fields
|
|
cached string // the value of the token, a.k.a. the contents of the file
|
|
mtime time.Time // the mtime of the file when the token was last read
|
|
}
|
|
|
|
// Read returns the token value from the file only if the file is
|
|
// expired or the cached value is empty
|
|
func (r *Reader) Read() (string, error) {
|
|
changed, err := r.HasChanged()
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
if changed || r.GetCached() == "" {
|
|
if err := r.readFile(); err != nil {
|
|
return "", err
|
|
}
|
|
}
|
|
|
|
return r.GetCached(), nil
|
|
}
|
|
|
|
// HasChanged checks if the in-memory `value` has changed by comparing
|
|
// the chached `r.mtime` value with the `mtime` of the file at
|
|
// `r.path`
|
|
func (r *Reader) HasChanged() (bool, error) {
|
|
info, err := os.Stat(r.Path)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
mtime := info.ModTime()
|
|
r.mu.Lock()
|
|
defer r.mu.Unlock()
|
|
return !mtime.Equal(r.mtime), nil
|
|
}
|
|
|
|
// HasExpired checks if 1 hour has passed since the last recorded `mtime` of
|
|
// the token file
|
|
func (r *Reader) HasExpired() bool {
|
|
r.mu.Lock()
|
|
defer r.mu.Unlock()
|
|
return time.Now().After(r.mtime.Add(1 * time.Hour))
|
|
}
|
|
|
|
func (r *Reader) GetMtime() time.Time {
|
|
r.mu.Lock()
|
|
defer r.mu.Unlock()
|
|
return r.mtime
|
|
}
|
|
|
|
// GetCached returns the cached token value
|
|
func (r *Reader) GetCached() string {
|
|
r.mu.Lock()
|
|
defer r.mu.Unlock()
|
|
return r.cached
|
|
}
|
|
|
|
func (r *Reader) readFile() error {
|
|
f, err := os.ReadFile(r.Path)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
info, err := os.Stat(r.Path)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
r.mu.Lock()
|
|
r.mtime = info.ModTime()
|
|
r.cached = string(f)
|
|
r.mu.Unlock()
|
|
return nil
|
|
}
|