Files
ssh-access-tui/internal/model/hash.go
T
2026-07-22 17:13:35 +03:00

63 lines
2.0 KiB
Go

package model
import (
"crypto/sha256"
"encoding/hex"
"fmt"
"sort"
"strings"
)
// HashForwards returns a stable, order-independent digest of a Forward set,
// used to detect whether a user's already-delivered client config still
// matches their current access. Sorted before hashing so that reordering
// the underlying groups/hosts/services (without actually changing what's
// granted) never produces a spurious "stale" result.
func HashForwards(forwards []Forward) string {
lines := make([]string, len(forwards))
for i, f := range forwards {
lines[i] = fmt.Sprintf("%s|%s|%d|%s|%s", f.HostName, f.Address, f.Port, f.ServiceName, f.Description)
}
sort.Strings(lines)
sum := sha256.Sum256([]byte(strings.Join(lines, "\n")))
return hex.EncodeToString(sum[:])
}
// IsConfigStale reports whether u's live AllowedForwards no longer matches
// the set they were handed in their last config (full export or
// config-only update). Users who never had a key issued are not "stale" —
// that's a distinct, more basic state (see State.NeverExportedUsernames).
func (s *State) IsConfigStale(u *User) bool {
if u.PublicKey == "" {
return false
}
return u.ForwardsHash != HashForwards(s.AllowedForwards(u))
}
// NeverExportedUsernames returns active users who have never had a key
// issued at all (empty PublicKey) — created but forgotten before "Экспорт
// архивов".
func (s *State) NeverExportedUsernames() []string {
var out []string
for _, u := range s.Users {
if u.Status == StatusActive && u.PublicKey == "" {
out = append(out, u.Username)
}
}
return out
}
// StaleConfigUsernames returns active, already-issued users whose local
// config no longer matches their real access (see IsConfigStale) — they
// need "Обновить конфиги", not a full re-export.
func (s *State) StaleConfigUsernames() []string {
var out []string
for i := range s.Users {
u := &s.Users[i]
if u.Status == StatusActive && u.PublicKey != "" && s.IsConfigStale(u) {
out = append(out, u.Username)
}
}
return out
}