sessionwatch

package
v0.75.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 23, 2026 License: BSD-3-Clause Imports: 7 Imported by: 0

Documentation

Overview

Package sessionwatch tracks the SSO session expiry deadline that the management server publishes via LoginResponse / SyncResponse and fires two warning events at fixed lead times before expiry: an interactive T-WarningLead notification and a dismiss-gated T-FinalWarningLead fallback dialog.

The watcher is idempotent: Update may be called as often as the network map snapshots arrive. Repeating the same deadline is a no-op; a new deadline reschedules the timers and arms a fresh warning cycle.

Warning firing is edge-detected. Each unique deadline value fires each warning callback at most once.

Index

Constants

View Source
const (
	// MetaSessionWarning is set to "true" on both warning events (T-10 and
	// T-2) so the UI can detect a session-warning SystemEvent without
	// matching on the message text. Use MetaSessionFinal to distinguish
	// the two.
	MetaSessionWarning = "session_warning"
	// MetaSessionFinal is set to "true" on the T-FinalWarningLead event
	// only. Consumers that need to auto-open the SessionAboutToExpire
	// dialog gate on this; T-WarningLead events leave the field unset.
	MetaSessionFinal = "session_final_warning"
	// MetaSessionExpiresAt carries the absolute UTC deadline encoded with
	// FormatExpiresAt; consumers must decode with ParseExpiresAt so a
	// future format change stays a single edit.
	MetaSessionExpiresAt = "session_expires_at"
	// MetaSessionLeadMinutes carries the lead in whole minutes (WarningLead
	// for the T-10 event, FinalWarningLead for the T-2 event) so the UI
	// can show "expires in ~N minutes" without hardcoding either constant.
	MetaSessionLeadMinutes = "lead_minutes"
	// MetaSessionDeadlineRejected is attached to the ERROR/AUTHENTICATION
	// SystemEvent the daemon emits when it discards a deadline from the
	// management server (pre-epoch, too far in the future, or past the
	// clock-skew tolerance). The value is the rejection reason string.
	// userMessage is left empty; the UI detects the event via this key
	// and builds a localized notification — same pattern as the session
	// warnings above.
	MetaSessionDeadlineRejected = "session_deadline_rejected"
)

Metadata keys attached by the daemon to session-warning SystemEvents. The UI tray reads these to build a locale-aware notification without relying on the daemon's locale-less UserMessage string, and to disambiguate the T-WarningLead notification from the T-FinalWarningLead fallback that auto-opens the SessionAboutToExpire dialog.

View Source
const (

	// WarningLead is how far before expiry the first (interactive)
	// warning fires. Drives the T-10 OS notification with
	// Extend/Dismiss actions.
	WarningLead = 10 * time.Minute

	// FinalWarningLead is how far before expiry the fallback final
	// warning fires. Drives the auto-opened SessionAboutToExpire dialog,
	// but only when the user has not dismissed the T-WarningLead warning
	// for the same deadline. Must be strictly less than WarningLead.
	FinalWarningLead = 2 * time.Minute
)

Variables

View Source
var (
	// ErrDeadlineBeforeEpoch is returned by Update when the supplied
	// deadline pre-dates 1970-01-01.
	ErrDeadlineBeforeEpoch = errors.New("session deadline before unix epoch")

	// ErrDeadlineTooFarFuture is returned by Update when the supplied
	// deadline is more than maxDeadlineHorizon in the future.
	ErrDeadlineTooFarFuture = errors.New("session deadline too far in the future")

	// ErrDeadlineInPast is returned by Update when the supplied deadline
	// is more than maxPastHorizon in the past.
	ErrDeadlineInPast = errors.New("session deadline in the past")
)

Functions

func FormatExpiresAt

func FormatExpiresAt(t time.Time) string

FormatExpiresAt encodes a deadline for MetaSessionExpiresAt. Always emits UTC so a consumer in another timezone reads the same wall-clock deadline.

func FormatLeadMinutes

func FormatLeadMinutes(d time.Duration) string

FormatLeadMinutes encodes a lead duration for MetaSessionLeadMinutes as the integer count of whole minutes. Sub-minute residuals are truncated — the field is informational ("expires in ~N minutes") and fractional minutes don't change what the UI displays.

func ParseExpiresAt

func ParseExpiresAt(s string) (time.Time, error)

ParseExpiresAt decodes the MetaSessionExpiresAt value back to a UTC time. Returns an error when the field is empty or malformed; the caller decides whether to fall back (zero value) or propagate.

func ParseLeadMinutes

func ParseLeadMinutes(s string) (int, error)

ParseLeadMinutes decodes a MetaSessionLeadMinutes value. Returns 0 and the parse error for malformed input; consumers that prefer a silent fallback can simply ignore the error.

Types

type StatusRecorder

type StatusRecorder interface {
	SetSessionExpiresAt(deadline time.Time)
	PublishEvent(
		severity cProto.SystemEvent_Severity,
		category cProto.SystemEvent_Category,
		message string,
		userMessage string,
		metadata map[string]string,
	)
}

StatusRecorder is the side-effect surface the watcher drives on every state transition. Production wires this to peer.Status (SetSessionExpiresAt for deadline change/clear, PublishEvent for the two warnings); tests pass a fake recorder so the same surface is observable without an engine.

While the watcher runs, it owns the deadline propagated to the recorder: every set, clear and sanity-check rejection routes the value through SetSessionExpiresAt, so the SubscribeStatus snapshot the UI reads can never drift from the watcher's timer state. (SetSessionExpiresAt fans out its own state-change notification, so no separate notify is needed.) The recorder is server-scoped and outlives this engine-scoped watcher; Close deliberately leaves the recorder value in place so transient engine restarts don't blank it — the client run loop clears it on real teardown.

PublishEvent's signature mirrors peer.Status.PublishEvent: the watcher composes the metadata internally so the wire format (MetaSession*) is owned by sessionwatch, not the caller.

type Watcher

type Watcher struct {
	// contains filtered or unexported fields
}

Watcher observes the latest session deadline and fires two warnings before it expires: the interactive T-WarningLead notification, and the fallback T-FinalWarningLead dialog (suppressed when the user dismissed the first one for the same deadline). Safe for concurrent use.

func New

func New(recorder StatusRecorder) *Watcher

New returns a watcher with the package defaults WarningLead and FinalWarningLead. Pass nil for recorder to silence side effects (handy in unit tests that exercise sanity checks without observing the publish path).

func NewWithLeads

func NewWithLeads(lead, final time.Duration, recorder StatusRecorder) *Watcher

NewWithLeads returns a watcher with custom lead times. Useful for tests. final must be strictly less than lead; otherwise both timers fire in the wrong order or simultaneously and the UI flow breaks. A zero final lead disables the final-warning timer entirely (see armTimerLocked) so a millisecond-scale deadline doesn't flush both timers in one tick.

func (*Watcher) Close

func (w *Watcher) Close()

Close stops any pending timer. Update calls after Close are ignored. The recorder keeps its deadline: the watcher is engine-scoped and closes on every engine restart (network change, sleep/wake, stream errors) while the SSO deadline stays valid across those, so clearing here would blank the UI's "expires in" row on every transient reconnect. The client run loop clears the server-scoped recorder when it exits for real (Down, profile switch, permanent login failure).

func (*Watcher) Deadline

func (w *Watcher) Deadline() time.Time

Deadline returns the most recently observed deadline. Zero when no deadline is currently tracked.

func (*Watcher) Dismiss

func (w *Watcher) Dismiss()

Dismiss records the user's "Dismiss" action against the current deadline and suppresses the upcoming final-warning callback for that deadline. Idempotent: repeated calls are no-ops. A subsequent Update with a fresh deadline resets the dismissal so the final-warning cycle re-arms.

No-op when the watcher holds no deadline or has been closed.

func (*Watcher) Update

func (w *Watcher) Update(deadline time.Time) error

Update sets the latest deadline. Pass the zero time to clear (e.g. when a Sync push from the server omits the field because login expiration was disabled).

Same-value updates are no-ops. A different non-zero value cancels any pending timer, resets the "already fired" guards, and — when the deadline lies in the future — arms fresh warning timers. A deadline already in the past (within maxPastHorizon) is recorded as-is with no timers: the session has expired and consumers render it that way.

Returns one of the sentinel Err* values when the deadline fails the sanity checks (pre-epoch, far future, or past beyond maxPastHorizon). In every error case the watcher first clears its state so it stays consistent with what the caller will push into its other sinks (e.g. applySessionDeadline forces a zero deadline into the status recorder after a non-nil error).

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL