Initial commit: AetherForge Linux (forge-mesh) v0.1.0-dev
Some checks failed
Test / test (push) Has been cancelled
Some checks failed
Test / test (push) Has been cancelled
This commit is contained in:
91
internal/fleet/policy_snapshot.go
Normal file
91
internal/fleet/policy_snapshot.go
Normal file
@@ -0,0 +1,91 @@
|
||||
package fleet
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"database/sql"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
// PolicySnapshot is a shareable frozen policy bundle.
|
||||
type PolicySnapshot struct {
|
||||
Token string `json:"token"`
|
||||
PolicyJSON json.RawMessage `json:"policy_json"`
|
||||
ExpiresAt string `json:"expires_at,omitempty"`
|
||||
CreatedAt string `json:"created_at"`
|
||||
}
|
||||
|
||||
// CreatePolicySnapshot stores a policy JSON blob and returns an opaque token.
|
||||
func (s *Store) CreatePolicySnapshot(policy map[string]any, ttl time.Duration) (*PolicySnapshot, error) {
|
||||
raw, err := json.Marshal(policy)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("marshal policy: %w", err)
|
||||
}
|
||||
|
||||
token, err := randomToken(16)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
now := time.Now().UTC()
|
||||
expires := ""
|
||||
if ttl > 0 {
|
||||
expires = now.Add(ttl).Format(time.RFC3339)
|
||||
}
|
||||
|
||||
_, err = s.db.Exec(`
|
||||
INSERT INTO policy_snapshots (token, policy_json, expires_at, created_at)
|
||||
VALUES (?, ?, ?, ?)
|
||||
`, token, string(raw), nullIfEmpty(expires), now.Format(time.RFC3339))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("insert policy snapshot: %w", err)
|
||||
}
|
||||
|
||||
return &PolicySnapshot{
|
||||
Token: token,
|
||||
PolicyJSON: raw,
|
||||
ExpiresAt: expires,
|
||||
CreatedAt: now.Format(time.RFC3339),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// GetPolicySnapshot loads a snapshot by token (public summon link).
|
||||
func (s *Store) GetPolicySnapshot(token string) (*PolicySnapshot, error) {
|
||||
row := s.db.QueryRow(`
|
||||
SELECT token, policy_json, expires_at, created_at
|
||||
FROM policy_snapshots WHERE token = ?
|
||||
`, token)
|
||||
|
||||
var snap PolicySnapshot
|
||||
var expires sql.NullString
|
||||
var created string
|
||||
if err := row.Scan(&snap.Token, &snap.PolicyJSON, &expires, &created); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if expires.Valid {
|
||||
snap.ExpiresAt = expires.String
|
||||
t, err := time.Parse(time.RFC3339, expires.String)
|
||||
if err == nil && time.Now().After(t) {
|
||||
return nil, sql.ErrNoRows
|
||||
}
|
||||
}
|
||||
snap.CreatedAt = created
|
||||
return &snap, nil
|
||||
}
|
||||
|
||||
func randomToken(n int) (string, error) {
|
||||
b := make([]byte, n)
|
||||
if _, err := rand.Read(b); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return hex.EncodeToString(b), nil
|
||||
}
|
||||
|
||||
func nullIfEmpty(s string) sql.NullString {
|
||||
if s == "" {
|
||||
return sql.NullString{}
|
||||
}
|
||||
return sql.NullString{String: s, Valid: true}
|
||||
}
|
||||
Reference in New Issue
Block a user