mirror of
https://github.com/netbirdio/netbird.git
synced 2026-04-19 00:36:38 +00:00
* wip: add posture checks structs * add netbird version check * Refactor posture checks and add version checks * Add posture check activities (#1445) * Integrate Endpoints for Posture Checks (#1432) * wip: add posture checks structs * add netbird version check * Refactor posture checks and add version checks * Implement posture and version checks in API models * Refactor API models and enhance posture check functionality * wip: add posture checks endpoints * go mod tidy * Reference the posture checks by id's in policy * Add posture checks management to server * Add posture checks management mocks * implement posture checks handlers * Add posture checks to account copy and fix tests * Refactor posture checks validation * wip: Add posture checks handler tests * Add JSON encoding support to posture checks * Encode posture checks to correct api response object * Refactored posture checks implementation to align with the new API schema * Refactor structure of `Checks` from slice to map * Cleanup * Add posture check activities (#1445) * Revert map to use list of checks * Add posture check activity events * Refactor posture check initialization in account test * Improve the handling of version range in posture check * Fix tests and linter * Remove max_version from NBVersionCheck * Added unit tests for NBVersionCheck * go mod tidy * Extend policy endpoint with posture checks (#1450) * Implement posture and version checks in API models * go mod tidy * Allow attaching posture checks to policy * Update error message for linked posture check on deleting * Refactor PostureCheck and Checks structures * go mod tidy * Add validation for non-existing posture checks * fix unit tests * use Wt version * Remove the enabled field, as posture check will now automatically be activated by default when attaching to a policy * wip: add posture checks structs * add netbird version check * Refactor posture checks and add version checks * Add posture check activities (#1445) * Integrate Endpoints for Posture Checks (#1432) * wip: add posture checks structs * add netbird version check * Refactor posture checks and add version checks * Implement posture and version checks in API models * Refactor API models and enhance posture check functionality * wip: add posture checks endpoints * go mod tidy * Reference the posture checks by id's in policy * Add posture checks management to server * Add posture checks management mocks * implement posture checks handlers * Add posture checks to account copy and fix tests * Refactor posture checks validation * wip: Add posture checks handler tests * Add JSON encoding support to posture checks * Encode posture checks to correct api response object * Refactored posture checks implementation to align with the new API schema * Refactor structure of `Checks` from slice to map * Cleanup * Add posture check activities (#1445) * Revert map to use list of checks * Add posture check activity events * Refactor posture check initialization in account test * Improve the handling of version range in posture check * Fix tests and linter * Remove max_version from NBVersionCheck * Added unit tests for NBVersionCheck * go mod tidy * Extend policy endpoint with posture checks (#1450) * Implement posture and version checks in API models * go mod tidy * Allow attaching posture checks to policy * Update error message for linked posture check on deleting * Refactor PostureCheck and Checks structures * go mod tidy * Add validation for non-existing posture checks * fix unit tests * use Wt version * Remove the enabled field, as posture check will now automatically be activated by default when attaching to a policy * Extend network map generation with posture checks (#1466) * Apply posture checks to network map generation * run policy posture checks on peers to connect * Refactor and streamline policy posture check process for peers to connect. * Add posture checks testing in a network map * Remove redundant nil check in policy.go * Refactor peer validation check in policy.go * Update 'Check' function signature and use logger for version check * Refactor posture checks run on sources and updated the validation func * Update peer validation * fix tests * improved test coverage for policy posture check * Refactoring * Extend NetBird agent to collect kernel version (#1495) * Add KernelVersion field to LoginRequest * Add KernelVersion to system info retrieval * Fix tests * Remove Core field from system info * Replace Core field with new OSVersion field in system info * Added WMI dependency to info_windows.go * Add OS Version posture checks (#1479) * Initial support of Geolocation service (#1491) * Add Geo Location posture check (#1500) * wip: implement geolocation check * add geo location posture checks to posture api * Merge branch 'feature/posture-checks' into geo-posture-check * Remove CityGeoNameID and update required fields in API * Add geoLocation checks to posture checks handler tests * Implement geo location-based checks for peers * Update test values and embed location struct in peer system * add support for country wide checks * initialize country code regex once * Fix peer meta core compability with older clients (#1515) * Refactor extraction of OSVersion in grpcserver * Ignore lint check * Fix peer meta core compability with older management (#1532) * Revert core field deprecation * fix tests * Extend peer meta with location information (#1517) This PR uses the geolocation service to resolve IP to location. The lookup happens once on the first connection - when a client calls the Sync func. The location is stored as part of the peer: * Add Locations endpoints (#1516) * add locations endpoints * Add sqlite3 check and database generation in geolite script * Add SQLite storage for geolocation data * Refactor file existence check into a separate function * Integrate geolocation services into management application * Refactoring * Refactor city retrieval to include Geonames ID * Add signature verification for GeoLite2 database download * Change to in-memory database for geolocation store * Merge manager to geolocation * Update GetAllCountries to return Country name and iso code * fix tests * Add reload to SqliteStore * Add geoname indexes * move db file check to connectDB * Add concurrency safety to SQL queries and database reloading The commit adds mutex locks to the GetAllCountries and GetCitiesByCountry functions to ensure thread-safety during database queries. Additionally, it introduces a mechanism to safely close the old database connection before a new connection is established upon reloading, which improves the reliability of database operations. Lastly, it moves the checking of database file existence to the connectDB function. * Add sha256 sum check to geolocation store before reload * Use read lock * Check SHA256 twice when reload geonames db --------- Co-authored-by: Yury Gargay <yury.gargay@gmail.com> * Add tests and validation for empty peer location in GeoLocationCheck (#1546) * Disallow Geo check creation/update without configured Geo DB (#1548) * Fix shared access to in memory copy of geonames.db (#1550) * Trim suffix in when evaluate Min Kernel Version in OS check * Add Valid Peer Windows Kernel version test * Add Geolocation handler tests (#1556) * Implement user admin checks in posture checks * Add geolocation handler tests * Mark initGeolocationTestData as helper func * Add error handling to geolocation database closure * Add cleanup function to close geolocation resources * Simplify checks definition serialisation (#1555) * Regenerate network map on posture check update (#1563) * change network state and generate map on posture check update * Refactoring * Make city name optional (#1575) * Do not return empty city name * Validate action param of geo location checks (#1577) We only support allow and deny * Switch realip middleware to upstream (#1578) * Be more silent in download-geolite2.sh script * Fix geonames db reload (#1580) * Ensure posture check name uniqueness when create (#1594) * Enhance the management of posture checks (#1595) * add a correct min version and kernel for os posture check example * handle error when geo or location db is nil * expose all peer location details in api response * Check for nil geolocation manager only * Validate posture check before save * bump open api version * add peer location fields to toPeerListItemResponse * Feautre/extend sys meta (#1536) * Collect network addresses * Add Linux sys product info * Fix peer meta comparison * Collect sys info on mac * Add windows sys info * Fix test * Fix test * Fix grpc client * Ignore test * Fix test * Collect IPv6 addresses * Change the IP to IP + net * fix tests * Use netip on server side * Serialize netip to json * Extend Peer metadata with cloud detection (#1552) * add cloud detection + test binary * test windows exe * Collect IPv6 addresses * Change the IP to IP + net * switch to forked cloud detect lib * new test builds * new GCE build * discontinue using library but local copy instead * fix imports * remove openstack check * add hierarchy to cloud check * merge IBM and SoftLayer * close resp bodies and use os lib for file reading * close more resp bodies * fix error check logic * parallelize IBM checks * fix response value * go mod tidy * include context + change kubernetes detection * add context in info functions * extract platform into separate field * fix imports * add missing wmi import --------- Co-authored-by: Zoltan Papp <zoltan.pmail@gmail.com> --------- Co-authored-by: pascal-fischer <32096965+pascal-fischer@users.noreply.github.com> * generate proto * remove test binaries --------- Co-authored-by: bcmmbaga <bethuelmbaga12@gmail.com> Co-authored-by: Yury Gargay <yury.gargay@gmail.com> Co-authored-by: Zoltan Papp <zoltan.pmail@gmail.com>
495 lines
14 KiB
Go
495 lines
14 KiB
Go
package server
|
|
|
|
import (
|
|
"path/filepath"
|
|
"runtime"
|
|
"strings"
|
|
"sync"
|
|
"time"
|
|
|
|
log "github.com/sirupsen/logrus"
|
|
"gorm.io/driver/sqlite"
|
|
"gorm.io/gorm"
|
|
"gorm.io/gorm/clause"
|
|
"gorm.io/gorm/logger"
|
|
|
|
nbdns "github.com/netbirdio/netbird/dns"
|
|
"github.com/netbirdio/netbird/management/server/account"
|
|
nbpeer "github.com/netbirdio/netbird/management/server/peer"
|
|
"github.com/netbirdio/netbird/management/server/posture"
|
|
"github.com/netbirdio/netbird/management/server/status"
|
|
"github.com/netbirdio/netbird/management/server/telemetry"
|
|
"github.com/netbirdio/netbird/route"
|
|
)
|
|
|
|
// SqliteStore represents an account storage backed by a Sqlite DB persisted to disk
|
|
type SqliteStore struct {
|
|
db *gorm.DB
|
|
storeFile string
|
|
accountLocks sync.Map
|
|
globalAccountLock sync.Mutex
|
|
metrics telemetry.AppMetrics
|
|
installationPK int
|
|
}
|
|
|
|
type installation struct {
|
|
ID uint `gorm:"primaryKey"`
|
|
InstallationIDValue string
|
|
}
|
|
|
|
// NewSqliteStore restores a store from the file located in the datadir
|
|
func NewSqliteStore(dataDir string, metrics telemetry.AppMetrics) (*SqliteStore, error) {
|
|
storeStr := "store.db?cache=shared"
|
|
if runtime.GOOS == "windows" {
|
|
// Vo avoid `The process cannot access the file because it is being used by another process` on Windows
|
|
storeStr = "store.db"
|
|
}
|
|
|
|
file := filepath.Join(dataDir, storeStr)
|
|
db, err := gorm.Open(sqlite.Open(file), &gorm.Config{
|
|
Logger: logger.Default.LogMode(logger.Silent),
|
|
PrepareStmt: true,
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
sql, err := db.DB()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
conns := runtime.NumCPU()
|
|
sql.SetMaxOpenConns(conns) // TODO: make it configurable
|
|
|
|
err = db.AutoMigrate(
|
|
&SetupKey{}, &nbpeer.Peer{}, &User{}, &PersonalAccessToken{}, &Group{}, &Rule{},
|
|
&Account{}, &Policy{}, &PolicyRule{}, &route.Route{}, &nbdns.NameServerGroup{},
|
|
&installation{}, &account.ExtraSettings{}, &posture.Checks{}, &nbpeer.NetworkAddress{},
|
|
)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &SqliteStore{db: db, storeFile: file, metrics: metrics, installationPK: 1}, nil
|
|
}
|
|
|
|
// NewSqliteStoreFromFileStore restores a store from FileStore and stores SQLite DB in the file located in datadir
|
|
func NewSqliteStoreFromFileStore(filestore *FileStore, dataDir string, metrics telemetry.AppMetrics) (*SqliteStore, error) {
|
|
store, err := NewSqliteStore(dataDir, metrics)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
err = store.SaveInstallationID(filestore.InstallationID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
for _, account := range filestore.GetAllAccounts() {
|
|
err := store.SaveAccount(account)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
return store, nil
|
|
}
|
|
|
|
// AcquireGlobalLock acquires global lock across all the accounts and returns a function that releases the lock
|
|
func (s *SqliteStore) AcquireGlobalLock() (unlock func()) {
|
|
log.Debugf("acquiring global lock")
|
|
start := time.Now()
|
|
s.globalAccountLock.Lock()
|
|
|
|
unlock = func() {
|
|
s.globalAccountLock.Unlock()
|
|
log.Debugf("released global lock in %v", time.Since(start))
|
|
}
|
|
|
|
took := time.Since(start)
|
|
log.Debugf("took %v to acquire global lock", took)
|
|
if s.metrics != nil {
|
|
s.metrics.StoreMetrics().CountGlobalLockAcquisitionDuration(took)
|
|
}
|
|
|
|
return unlock
|
|
}
|
|
|
|
func (s *SqliteStore) AcquireAccountLock(accountID string) (unlock func()) {
|
|
log.Debugf("acquiring lock for account %s", accountID)
|
|
|
|
start := time.Now()
|
|
value, _ := s.accountLocks.LoadOrStore(accountID, &sync.Mutex{})
|
|
mtx := value.(*sync.Mutex)
|
|
mtx.Lock()
|
|
|
|
unlock = func() {
|
|
mtx.Unlock()
|
|
log.Debugf("released lock for account %s in %v", accountID, time.Since(start))
|
|
}
|
|
|
|
return unlock
|
|
}
|
|
|
|
func (s *SqliteStore) SaveAccount(account *Account) error {
|
|
start := time.Now()
|
|
|
|
for _, key := range account.SetupKeys {
|
|
account.SetupKeysG = append(account.SetupKeysG, *key)
|
|
}
|
|
|
|
for id, peer := range account.Peers {
|
|
peer.ID = id
|
|
account.PeersG = append(account.PeersG, *peer)
|
|
}
|
|
|
|
for id, user := range account.Users {
|
|
user.Id = id
|
|
for id, pat := range user.PATs {
|
|
pat.ID = id
|
|
user.PATsG = append(user.PATsG, *pat)
|
|
}
|
|
account.UsersG = append(account.UsersG, *user)
|
|
}
|
|
|
|
for id, group := range account.Groups {
|
|
group.ID = id
|
|
account.GroupsG = append(account.GroupsG, *group)
|
|
}
|
|
|
|
for id, route := range account.Routes {
|
|
route.ID = id
|
|
account.RoutesG = append(account.RoutesG, *route)
|
|
}
|
|
|
|
for id, ns := range account.NameServerGroups {
|
|
ns.ID = id
|
|
account.NameServerGroupsG = append(account.NameServerGroupsG, *ns)
|
|
}
|
|
|
|
err := s.db.Transaction(func(tx *gorm.DB) error {
|
|
result := tx.Select(clause.Associations).Delete(account.Policies, "account_id = ?", account.Id)
|
|
if result.Error != nil {
|
|
return result.Error
|
|
}
|
|
|
|
result = tx.Select(clause.Associations).Delete(account.UsersG, "account_id = ?", account.Id)
|
|
if result.Error != nil {
|
|
return result.Error
|
|
}
|
|
|
|
result = tx.Select(clause.Associations).Delete(account)
|
|
if result.Error != nil {
|
|
return result.Error
|
|
}
|
|
|
|
result = tx.
|
|
Session(&gorm.Session{FullSaveAssociations: true}).
|
|
Clauses(clause.OnConflict{UpdateAll: true}).Create(account)
|
|
if result.Error != nil {
|
|
return result.Error
|
|
}
|
|
return nil
|
|
})
|
|
|
|
took := time.Since(start)
|
|
if s.metrics != nil {
|
|
s.metrics.StoreMetrics().CountPersistenceDuration(took)
|
|
}
|
|
log.Debugf("took %d ms to persist an account to the SQLite", took.Milliseconds())
|
|
|
|
return err
|
|
}
|
|
|
|
func (s *SqliteStore) DeleteAccount(account *Account) error {
|
|
start := time.Now()
|
|
|
|
err := s.db.Transaction(func(tx *gorm.DB) error {
|
|
result := tx.Select(clause.Associations).Delete(account.Policies, "account_id = ?", account.Id)
|
|
if result.Error != nil {
|
|
return result.Error
|
|
}
|
|
|
|
result = tx.Select(clause.Associations).Delete(account.UsersG, "account_id = ?", account.Id)
|
|
if result.Error != nil {
|
|
return result.Error
|
|
}
|
|
|
|
result = tx.Select(clause.Associations).Delete(account)
|
|
if result.Error != nil {
|
|
return result.Error
|
|
}
|
|
|
|
return nil
|
|
})
|
|
|
|
took := time.Since(start)
|
|
if s.metrics != nil {
|
|
s.metrics.StoreMetrics().CountPersistenceDuration(took)
|
|
}
|
|
log.Debugf("took %d ms to delete an account to the SQLite", took.Milliseconds())
|
|
|
|
return err
|
|
}
|
|
|
|
func (s *SqliteStore) SaveInstallationID(ID string) error {
|
|
installation := installation{InstallationIDValue: ID}
|
|
installation.ID = uint(s.installationPK)
|
|
|
|
return s.db.Clauses(clause.OnConflict{UpdateAll: true}).Create(&installation).Error
|
|
}
|
|
|
|
func (s *SqliteStore) GetInstallationID() string {
|
|
var installation installation
|
|
|
|
if result := s.db.First(&installation, "id = ?", s.installationPK); result.Error != nil {
|
|
return ""
|
|
}
|
|
|
|
return installation.InstallationIDValue
|
|
}
|
|
|
|
func (s *SqliteStore) SavePeerStatus(accountID, peerID string, peerStatus nbpeer.PeerStatus) error {
|
|
var peer nbpeer.Peer
|
|
|
|
result := s.db.First(&peer, "account_id = ? and id = ?", accountID, peerID)
|
|
if result.Error != nil {
|
|
return status.Errorf(status.NotFound, "peer %s not found", peerID)
|
|
}
|
|
|
|
peer.Status = &peerStatus
|
|
|
|
return s.db.Save(peer).Error
|
|
}
|
|
|
|
func (s *SqliteStore) SavePeerLocation(accountID string, peerWithLocation *nbpeer.Peer) error {
|
|
var peer nbpeer.Peer
|
|
result := s.db.First(&peer, "account_id = ? and id = ?", accountID, peerWithLocation.ID)
|
|
if result.Error != nil {
|
|
return status.Errorf(status.NotFound, "peer %s not found", peer.ID)
|
|
}
|
|
|
|
peer.Location = peerWithLocation.Location
|
|
|
|
return s.db.Save(peer).Error
|
|
}
|
|
|
|
// DeleteHashedPAT2TokenIDIndex is noop in Sqlite
|
|
func (s *SqliteStore) DeleteHashedPAT2TokenIDIndex(hashedToken string) error {
|
|
return nil
|
|
}
|
|
|
|
// DeleteTokenID2UserIDIndex is noop in Sqlite
|
|
func (s *SqliteStore) DeleteTokenID2UserIDIndex(tokenID string) error {
|
|
return nil
|
|
}
|
|
|
|
func (s *SqliteStore) GetAccountByPrivateDomain(domain string) (*Account, error) {
|
|
var account Account
|
|
|
|
result := s.db.First(&account, "domain = ? and is_domain_primary_account = ? and domain_category = ?",
|
|
strings.ToLower(domain), true, PrivateCategory)
|
|
if result.Error != nil {
|
|
return nil, status.Errorf(status.NotFound, "account not found: provided domain is not registered or is not private")
|
|
}
|
|
|
|
// TODO: rework to not call GetAccount
|
|
return s.GetAccount(account.Id)
|
|
}
|
|
|
|
func (s *SqliteStore) GetAccountBySetupKey(setupKey string) (*Account, error) {
|
|
var key SetupKey
|
|
result := s.db.Select("account_id").First(&key, "key = ?", strings.ToUpper(setupKey))
|
|
if result.Error != nil {
|
|
return nil, status.Errorf(status.NotFound, "account not found: index lookup failed")
|
|
}
|
|
|
|
if key.AccountID == "" {
|
|
return nil, status.Errorf(status.NotFound, "account not found: index lookup failed")
|
|
}
|
|
|
|
return s.GetAccount(key.AccountID)
|
|
}
|
|
|
|
func (s *SqliteStore) GetTokenIDByHashedToken(hashedToken string) (string, error) {
|
|
var token PersonalAccessToken
|
|
result := s.db.First(&token, "hashed_token = ?", hashedToken)
|
|
if result.Error != nil {
|
|
return "", status.Errorf(status.NotFound, "account not found: index lookup failed")
|
|
}
|
|
|
|
return token.ID, nil
|
|
}
|
|
|
|
func (s *SqliteStore) GetUserByTokenID(tokenID string) (*User, error) {
|
|
var token PersonalAccessToken
|
|
result := s.db.First(&token, "id = ?", tokenID)
|
|
if result.Error != nil {
|
|
return nil, status.Errorf(status.NotFound, "account not found: index lookup failed")
|
|
}
|
|
|
|
if token.UserID == "" {
|
|
return nil, status.Errorf(status.NotFound, "account not found: index lookup failed")
|
|
}
|
|
|
|
var user User
|
|
result = s.db.Preload("PATsG").First(&user, "id = ?", token.UserID)
|
|
if result.Error != nil {
|
|
return nil, status.Errorf(status.NotFound, "account not found: index lookup failed")
|
|
}
|
|
|
|
user.PATs = make(map[string]*PersonalAccessToken, len(user.PATsG))
|
|
for _, pat := range user.PATsG {
|
|
user.PATs[pat.ID] = pat.Copy()
|
|
}
|
|
|
|
return &user, nil
|
|
}
|
|
|
|
func (s *SqliteStore) GetAllAccounts() (all []*Account) {
|
|
var accounts []Account
|
|
result := s.db.Find(&accounts)
|
|
if result.Error != nil {
|
|
return all
|
|
}
|
|
|
|
for _, account := range accounts {
|
|
if acc, err := s.GetAccount(account.Id); err == nil {
|
|
all = append(all, acc)
|
|
}
|
|
}
|
|
|
|
return all
|
|
}
|
|
|
|
func (s *SqliteStore) GetAccount(accountID string) (*Account, error) {
|
|
var account Account
|
|
result := s.db.Model(&account).
|
|
Preload("UsersG.PATsG"). // have to be specifies as this is nester reference
|
|
Preload(clause.Associations).
|
|
First(&account, "id = ?", accountID)
|
|
if result.Error != nil {
|
|
log.Errorf("when getting account from the store: %s", result.Error)
|
|
return nil, status.Errorf(status.NotFound, "account not found")
|
|
}
|
|
|
|
// we have to manually preload policy rules as it seems that gorm preloading doesn't do it for us
|
|
for i, policy := range account.Policies {
|
|
var rules []*PolicyRule
|
|
err := s.db.Model(&PolicyRule{}).Find(&rules, "policy_id = ?", policy.ID).Error
|
|
if err != nil {
|
|
return nil, status.Errorf(status.NotFound, "rule not found")
|
|
}
|
|
account.Policies[i].Rules = rules
|
|
}
|
|
|
|
account.SetupKeys = make(map[string]*SetupKey, len(account.SetupKeysG))
|
|
for _, key := range account.SetupKeysG {
|
|
account.SetupKeys[key.Key] = key.Copy()
|
|
}
|
|
account.SetupKeysG = nil
|
|
|
|
account.Peers = make(map[string]*nbpeer.Peer, len(account.PeersG))
|
|
for _, peer := range account.PeersG {
|
|
account.Peers[peer.ID] = peer.Copy()
|
|
}
|
|
account.PeersG = nil
|
|
|
|
account.Users = make(map[string]*User, len(account.UsersG))
|
|
for _, user := range account.UsersG {
|
|
user.PATs = make(map[string]*PersonalAccessToken, len(user.PATs))
|
|
for _, pat := range user.PATsG {
|
|
user.PATs[pat.ID] = pat.Copy()
|
|
}
|
|
account.Users[user.Id] = user.Copy()
|
|
}
|
|
account.UsersG = nil
|
|
|
|
account.Groups = make(map[string]*Group, len(account.GroupsG))
|
|
for _, group := range account.GroupsG {
|
|
account.Groups[group.ID] = group.Copy()
|
|
}
|
|
account.GroupsG = nil
|
|
|
|
account.Routes = make(map[string]*route.Route, len(account.RoutesG))
|
|
for _, route := range account.RoutesG {
|
|
account.Routes[route.ID] = route.Copy()
|
|
}
|
|
account.RoutesG = nil
|
|
|
|
account.NameServerGroups = make(map[string]*nbdns.NameServerGroup, len(account.NameServerGroupsG))
|
|
for _, ns := range account.NameServerGroupsG {
|
|
account.NameServerGroups[ns.ID] = ns.Copy()
|
|
}
|
|
account.NameServerGroupsG = nil
|
|
|
|
return &account, nil
|
|
}
|
|
|
|
func (s *SqliteStore) GetAccountByUser(userID string) (*Account, error) {
|
|
var user User
|
|
result := s.db.Select("account_id").First(&user, "id = ?", userID)
|
|
if result.Error != nil {
|
|
return nil, status.Errorf(status.NotFound, "account not found: index lookup failed")
|
|
}
|
|
|
|
if user.AccountID == "" {
|
|
return nil, status.Errorf(status.NotFound, "account not found: index lookup failed")
|
|
}
|
|
|
|
return s.GetAccount(user.AccountID)
|
|
}
|
|
|
|
func (s *SqliteStore) GetAccountByPeerID(peerID string) (*Account, error) {
|
|
var peer nbpeer.Peer
|
|
result := s.db.Select("account_id").First(&peer, "id = ?", peerID)
|
|
if result.Error != nil {
|
|
return nil, status.Errorf(status.NotFound, "account not found: index lookup failed")
|
|
}
|
|
|
|
if peer.AccountID == "" {
|
|
return nil, status.Errorf(status.NotFound, "account not found: index lookup failed")
|
|
}
|
|
|
|
return s.GetAccount(peer.AccountID)
|
|
}
|
|
|
|
func (s *SqliteStore) GetAccountByPeerPubKey(peerKey string) (*Account, error) {
|
|
var peer nbpeer.Peer
|
|
|
|
result := s.db.Select("account_id").First(&peer, "key = ?", peerKey)
|
|
if result.Error != nil {
|
|
return nil, status.Errorf(status.NotFound, "account not found: index lookup failed")
|
|
}
|
|
|
|
if peer.AccountID == "" {
|
|
return nil, status.Errorf(status.NotFound, "account not found: index lookup failed")
|
|
}
|
|
|
|
return s.GetAccount(peer.AccountID)
|
|
}
|
|
|
|
// SaveUserLastLogin stores the last login time for a user in DB.
|
|
func (s *SqliteStore) SaveUserLastLogin(accountID, userID string, lastLogin time.Time) error {
|
|
var user User
|
|
|
|
result := s.db.First(&user, "account_id = ? and id = ?", accountID, userID)
|
|
if result.Error != nil {
|
|
return status.Errorf(status.NotFound, "user %s not found", userID)
|
|
}
|
|
|
|
user.LastLogin = lastLogin
|
|
|
|
return s.db.Save(user).Error
|
|
}
|
|
|
|
// Close is noop in Sqlite
|
|
func (s *SqliteStore) Close() error {
|
|
return nil
|
|
}
|
|
|
|
// GetStoreEngine returns SqliteStoreEngine
|
|
func (s *SqliteStore) GetStoreEngine() StoreEngine {
|
|
return SqliteStoreEngine
|
|
}
|