mirror of
https://github.com/netbirdio/netbird.git
synced 2026-08-24 16:41:30 +02:00
Introduce a namespaced preference store owned by the profile manager, persisted next to the profile config as <id>.prefs.json and deleted with the profile. Sections are opaque JSON, so the profile manager stays free of any feature schema, and writes reuse the state file's atomic path. Migrate the Android SSH known-hosts store and session list onto it. Both previously lived outside the profile lifecycle: known hosts in a per-profile file under filesDir, the session list in Java SharedPreferences, each needing its own sweep against the live profile list to avoid outliving the profile they belonged to. A profile ID that got reused would have inherited the trusted keys of a deleted profile. Both now share the "ssh" and "ssh-sessions" namespaces of the profile's preferences, so deleting a profile takes them along and the Java-side pruning is gone. The known-hosts entries keep the OpenSSH line format, only the container changed, and host key verification keeps rejecting a changed key outright. SetKnownHostsPath becomes SetKnownHostsStore, taking the config dir and profile ID instead of a file path. Existing known-hosts files are not migrated: hosts trusted before this change prompt for confirmation once more, which errs towards safety.
105 lines
2.7 KiB
Go
105 lines
2.7 KiB
Go
//go:build android
|
|
|
|
package android
|
|
|
|
const (
|
|
sshSessionsNamespace = "ssh-sessions"
|
|
maxStoredSSHSessions = 50
|
|
)
|
|
|
|
type sshSessionRecord struct {
|
|
ID string `json:"id"`
|
|
Host string `json:"host"`
|
|
Port int `json:"port"`
|
|
User string `json:"user"`
|
|
}
|
|
|
|
type sshSessionsSection struct {
|
|
Sessions []sshSessionRecord `json:"sessions"`
|
|
}
|
|
|
|
// SSHSessionEntry is one stored SSH session, without any credential.
|
|
type SSHSessionEntry struct {
|
|
ID string
|
|
Host string
|
|
Port int
|
|
User string
|
|
}
|
|
|
|
// SSHSessionArray wraps stored SSH sessions for gomobile compatibility.
|
|
type SSHSessionArray struct {
|
|
items []*SSHSessionEntry
|
|
}
|
|
|
|
// NewSSHSessionArray creates an empty session array to fill via Add.
|
|
func NewSSHSessionArray() *SSHSessionArray {
|
|
return &SSHSessionArray{}
|
|
}
|
|
|
|
// Add appends a session entry, oldest first.
|
|
func (a *SSHSessionArray) Add(id, host string, port int, user string) {
|
|
a.items = append(a.items, &SSHSessionEntry{ID: id, Host: host, Port: port, User: user})
|
|
}
|
|
|
|
// Length returns the number of entries.
|
|
func (a *SSHSessionArray) Length() int {
|
|
return len(a.items)
|
|
}
|
|
|
|
// Get returns the entry at index i, or nil when out of range.
|
|
func (a *SSHSessionArray) Get(i int) *SSHSessionEntry {
|
|
if i < 0 || i >= len(a.items) {
|
|
return nil
|
|
}
|
|
return a.items[i]
|
|
}
|
|
|
|
// SSHSessionStore reads and writes a profile's stored SSH sessions.
|
|
type SSHSessionStore struct {
|
|
prefs prefsStore
|
|
}
|
|
|
|
// NewSSHSessionStore opens the session store of the given profile.
|
|
func NewSSHSessionStore(configDir, profileID string) (*SSHSessionStore, error) {
|
|
prefs, err := newProfilePrefs(configDir, profileID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &SSHSessionStore{prefs: prefs}, nil
|
|
}
|
|
|
|
// Load returns the stored sessions, oldest first.
|
|
func (s *SSHSessionStore) Load() (*SSHSessionArray, error) {
|
|
var section sshSessionsSection
|
|
if _, err := s.prefs.Get(sshSessionsNamespace, §ion); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
out := NewSSHSessionArray()
|
|
for _, record := range section.Sessions {
|
|
if record.ID == "" || record.Host == "" {
|
|
continue
|
|
}
|
|
out.Add(record.ID, record.Host, record.Port, record.User)
|
|
}
|
|
return out, nil
|
|
}
|
|
|
|
// Save replaces the stored sessions, keeping only the newest entries when the
|
|
// list exceeds the storage cap.
|
|
func (s *SSHSessionStore) Save(sessions *SSHSessionArray) error {
|
|
var items []*SSHSessionEntry
|
|
if sessions != nil {
|
|
items = sessions.items
|
|
}
|
|
if len(items) > maxStoredSSHSessions {
|
|
items = items[len(items)-maxStoredSSHSessions:]
|
|
}
|
|
|
|
records := make([]sshSessionRecord, 0, len(items))
|
|
for _, item := range items {
|
|
records = append(records, sshSessionRecord{ID: item.ID, Host: item.Host, Port: item.Port, User: item.User})
|
|
}
|
|
return s.prefs.Put(sshSessionsNamespace, sshSessionsSection{Sessions: records})
|
|
}
|