Use time pointer instead of sql.NullTime

Signed-off-by: bcmmbaga <bethuelmbaga12@gmail.com>
This commit is contained in:
bcmmbaga 2025-01-02 15:48:50 +03:00
parent a3fe7bea38
commit 525019b5ed
No known key found for this signature in database
GPG Key ID: 511EED5C928AD547
11 changed files with 64 additions and 21 deletions

View File

@ -789,7 +789,7 @@ func (am *DefaultAccountManager) lookupUserInCache(ctx context.Context, userID s
if user.Issued == types.UserIssuedIntegration { if user.Issued == types.UserIssuedIntegration {
continue continue
} }
users[user.Id] = userLoggedInOnce(!user.LastLogin.Time.IsZero()) users[user.Id] = userLoggedInOnce(!user.LastLoginTime().IsZero())
} }
log.WithContext(ctx).Debugf("looking up user %s of account %s in cache", userID, account.Id) log.WithContext(ctx).Debugf("looking up user %s of account %s in cache", userID, account.Id)
userData, err := am.lookupCache(ctx, users, account.Id) userData, err := am.lookupCache(ctx, users, account.Id)

View File

@ -349,7 +349,7 @@ func toSinglePeerResponse(peer *nbpeer.Peer, groupsInfo []api.GroupMinimum, dnsD
UiVersion: peer.Meta.UIVersion, UiVersion: peer.Meta.UIVersion,
DnsLabel: fqdn(peer, dnsDomain), DnsLabel: fqdn(peer, dnsDomain),
LoginExpirationEnabled: peer.LoginExpirationEnabled, LoginExpirationEnabled: peer.LoginExpirationEnabled,
LastLogin: peer.LastLogin, LastLogin: peer.LastLoginTime(),
LoginExpired: peer.Status.LoginExpired, LoginExpired: peer.Status.LoginExpired,
ApprovalRequired: !approved, ApprovalRequired: !approved,
CountryCode: peer.Location.CountryCode, CountryCode: peer.Location.CountryCode,
@ -383,7 +383,7 @@ func toPeerListItemResponse(peer *nbpeer.Peer, groupsInfo []api.GroupMinimum, dn
UiVersion: peer.Meta.UIVersion, UiVersion: peer.Meta.UIVersion,
DnsLabel: fqdn(peer, dnsDomain), DnsLabel: fqdn(peer, dnsDomain),
LoginExpirationEnabled: peer.LoginExpirationEnabled, LoginExpirationEnabled: peer.LoginExpirationEnabled,
LastLogin: peer.LastLogin, LastLogin: peer.LastLoginTime(),
LoginExpired: peer.Status.LoginExpired, LoginExpired: peer.Status.LoginExpired,
AccessiblePeersCount: accessiblePeersCount, AccessiblePeersCount: accessiblePeersCount,
CountryCode: peer.Location.CountryCode, CountryCode: peer.Location.CountryCode,

View File

@ -245,7 +245,7 @@ func toResponseBody(key *types.SetupKey) *api.SetupKey {
Valid: key.IsValid(), Valid: key.IsValid(),
Revoked: key.Revoked, Revoked: key.Revoked,
UsedTimes: key.UsedTimes, UsedTimes: key.UsedTimes,
LastUsed: key.LastUsed, LastUsed: key.LastUsedTime(),
State: state, State: state,
AutoGroups: key.AutoGroups, AutoGroups: key.AutoGroups,
UpdatedAt: key.UpdatedAt, UpdatedAt: key.UpdatedAt,

View File

@ -11,6 +11,7 @@ import (
"sync" "sync"
"time" "time"
"github.com/netbirdio/netbird/management/server/util"
"github.com/rs/xid" "github.com/rs/xid"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
@ -511,7 +512,7 @@ func (am *DefaultAccountManager) AddPeer(ctx context.Context, setupKey, userID s
Status: &nbpeer.PeerStatus{Connected: false, LastSeen: registrationTime}, Status: &nbpeer.PeerStatus{Connected: false, LastSeen: registrationTime},
SSHEnabled: false, SSHEnabled: false,
SSHKey: peer.SSHKey, SSHKey: peer.SSHKey,
LastLogin: registrationTime, LastLogin: util.ToPtr(registrationTime),
CreatedAt: registrationTime, CreatedAt: registrationTime,
LoginExpirationEnabled: addedByUser, LoginExpirationEnabled: addedByUser,
Ephemeral: ephemeral, Ephemeral: ephemeral,
@ -566,7 +567,7 @@ func (am *DefaultAccountManager) AddPeer(ctx context.Context, setupKey, userID s
} }
if addedByUser { if addedByUser {
err := transaction.SaveUserLastLogin(ctx, accountID, userID, newPeer.LastLogin) err := transaction.SaveUserLastLogin(ctx, accountID, userID, *newPeer.LastLogin)
if err != nil { if err != nil {
return fmt.Errorf("failed to update user last login: %w", err) return fmt.Errorf("failed to update user last login: %w", err)
} }
@ -911,7 +912,7 @@ func (am *DefaultAccountManager) handleExpiredPeer(ctx context.Context, user *ty
return err return err
} }
err = am.Store.SaveUserLastLogin(ctx, user.AccountID, user.Id, peer.LastLogin) err = am.Store.SaveUserLastLogin(ctx, user.AccountID, user.Id, peer.LastLoginTime())
if err != nil { if err != nil {
return err return err
} }

View File

@ -6,6 +6,8 @@ import (
"slices" "slices"
"sort" "sort"
"time" "time"
"github.com/netbirdio/netbird/management/server/util"
) )
// Peer represents a machine connected to the network. // Peer represents a machine connected to the network.
@ -40,7 +42,7 @@ type Peer struct {
InactivityExpirationEnabled bool InactivityExpirationEnabled bool
// LastLogin the time when peer performed last login operation // LastLogin the time when peer performed last login operation
LastLogin time.Time LastLogin *time.Time
// CreatedAt records the time the peer was created // CreatedAt records the time the peer was created
CreatedAt time.Time CreatedAt time.Time
// Indicate ephemeral peer attribute // Indicate ephemeral peer attribute
@ -222,6 +224,15 @@ func (p *Peer) UpdateMetaIfNew(meta PeerSystemMeta) bool {
return true return true
} }
// LastLoginTime returns the last login time of the peer.
func (p *Peer) LastLoginTime() time.Time {
if p.LastLogin != nil {
return *p.LastLogin
}
return time.Time{}
}
// MarkLoginExpired marks peer's status expired or not // MarkLoginExpired marks peer's status expired or not
func (p *Peer) MarkLoginExpired(expired bool) { func (p *Peer) MarkLoginExpired(expired bool) {
newStatus := p.Status.Copy() newStatus := p.Status.Copy()
@ -291,7 +302,7 @@ func (p *PeerStatus) Copy() *PeerStatus {
// UpdateLastLogin and set login expired false // UpdateLastLogin and set login expired false
func (p *Peer) UpdateLastLogin() *Peer { func (p *Peer) UpdateLastLogin() *Peer {
p.LastLogin = time.Now().UTC() p.LastLogin = util.ToPtr(time.Now().UTC())
newStatus := p.Status.Copy() newStatus := p.Status.Copy()
newStatus.LoginExpired = false newStatus.LoginExpired = false
p.Status = newStatus p.Status = newStatus

View File

@ -14,6 +14,7 @@ import (
nbpeer "github.com/netbirdio/netbird/management/server/peer" nbpeer "github.com/netbirdio/netbird/management/server/peer"
"github.com/netbirdio/netbird/management/server/telemetry" "github.com/netbirdio/netbird/management/server/telemetry"
"github.com/netbirdio/netbird/management/server/types" "github.com/netbirdio/netbird/management/server/types"
nbutil "github.com/netbirdio/netbird/management/server/util"
"github.com/netbirdio/netbird/util" "github.com/netbirdio/netbird/util"
) )
@ -176,7 +177,7 @@ func restore(ctx context.Context, file string) (*FileStore, error) {
for key, peer := range account.Peers { for key, peer := range account.Peers {
// set LastLogin for the peers that were onboarded before the peer login expiration feature // set LastLogin for the peers that were onboarded before the peer login expiration feature
if peer.LastLogin.IsZero() { if peer.LastLogin.IsZero() {
peer.LastLogin = time.Now().UTC() peer.LastLogin = nbutil.ToPtr(time.Now().UTC())
} }
if peer.ID != "" { if peer.ID != "" {
continue continue

View File

@ -2,7 +2,6 @@ package store
import ( import (
"context" "context"
"database/sql"
"encoding/json" "encoding/json"
"errors" "errors"
"fmt" "fmt"
@ -895,7 +894,7 @@ func (s *SqlStore) SaveUserLastLogin(ctx context.Context, accountID, userID stri
} }
return status.NewGetUserFromStoreError() return status.NewGetUserFromStoreError()
} }
user.LastLogin = sql.NullTime{Time: lastLogin, Valid: !lastLogin.IsZero()} user.LastLogin = &lastLogin
return s.db.Save(&user).Error return s.db.Save(&user).Error
} }

View File

@ -10,6 +10,7 @@ import (
"unicode/utf8" "unicode/utf8"
"github.com/google/uuid" "github.com/google/uuid"
"github.com/netbirdio/netbird/management/server/util"
) )
const ( const (
@ -45,7 +46,7 @@ type SetupKey struct {
// UsedTimes indicates how many times the key was used // UsedTimes indicates how many times the key was used
UsedTimes int UsedTimes int
// LastUsed last time the key was used for peer registration // LastUsed last time the key was used for peer registration
LastUsed time.Time LastUsed *time.Time
// AutoGroups is a list of Group IDs that are auto assigned to a Peer when it uses this key to register // AutoGroups is a list of Group IDs that are auto assigned to a Peer when it uses this key to register
AutoGroups []string `gorm:"serializer:json"` AutoGroups []string `gorm:"serializer:json"`
// UsageLimit indicates the number of times this key can be used to enroll a machine. // UsageLimit indicates the number of times this key can be used to enroll a machine.
@ -86,6 +87,14 @@ func (key *SetupKey) EventMeta() map[string]any {
return map[string]any{"name": key.Name, "type": key.Type, "key": key.KeySecret} return map[string]any{"name": key.Name, "type": key.Type, "key": key.KeySecret}
} }
// LastUsedTime returns the last used time of the setup key.
func (key *SetupKey) LastUsedTime() time.Time {
if key.LastUsed != nil {
return *key.LastUsed
}
return time.Time{}
}
// HiddenKey returns the Key value hidden with "*" and a 5 character prefix. // HiddenKey returns the Key value hidden with "*" and a 5 character prefix.
// E.g., "831F6*******************************" // E.g., "831F6*******************************"
func HiddenKey(key string, length int) string { func HiddenKey(key string, length int) string {
@ -100,7 +109,7 @@ func HiddenKey(key string, length int) string {
func (key *SetupKey) IncrementUsage() *SetupKey { func (key *SetupKey) IncrementUsage() *SetupKey {
c := key.Copy() c := key.Copy()
c.UsedTimes++ c.UsedTimes++
c.LastUsed = time.Now().UTC() c.LastUsed = util.ToPtr(time.Now().UTC())
return c return c
} }

View File

@ -1,7 +1,6 @@
package types package types
import ( import (
"database/sql"
"fmt" "fmt"
"strings" "strings"
"time" "time"
@ -85,7 +84,7 @@ type User struct {
// Blocked indicates whether the user is blocked. Blocked users can't use the system. // Blocked indicates whether the user is blocked. Blocked users can't use the system.
Blocked bool Blocked bool
// LastLogin is the last time the user logged in to IdP // LastLogin is the last time the user logged in to IdP
LastLogin sql.NullTime LastLogin *time.Time
// CreatedAt records the time the user was created // CreatedAt records the time the user was created
CreatedAt time.Time CreatedAt time.Time
@ -100,8 +99,16 @@ func (u *User) IsBlocked() bool {
return u.Blocked return u.Blocked
} }
func (u *User) LastDashboardLoginChanged(LastLogin time.Time) bool { func (u *User) LastDashboardLoginChanged(lastLogin time.Time) bool {
return LastLogin.After(u.LastLogin.Time) && !u.LastLogin.Time.IsZero() return lastLogin.After(u.LastLoginTime()) && !u.LastLoginTime().IsZero()
}
// LastLoginTime returns the last login time of the user.
func (u *User) LastLoginTime() time.Time {
if u.LastLogin != nil {
return *u.LastLogin
}
return time.Time{}
} }
// HasAdminPower returns true if the user has admin or owner roles, false otherwise // HasAdminPower returns true if the user has admin or owner roles, false otherwise
@ -135,6 +142,11 @@ func (u *User) ToUserInfo(userData *idp.UserData, settings *Settings) (*UserInfo
} }
if userData == nil { if userData == nil {
var lastLogin time.Time
if u.LastLogin != nil {
lastLogin = *u.LastLogin
}
return &UserInfo{ return &UserInfo{
ID: u.Id, ID: u.Id,
Email: "", Email: "",
@ -144,7 +156,7 @@ func (u *User) ToUserInfo(userData *idp.UserData, settings *Settings) (*UserInfo
Status: string(UserStatusActive), Status: string(UserStatusActive),
IsServiceUser: u.IsServiceUser, IsServiceUser: u.IsServiceUser,
IsBlocked: u.Blocked, IsBlocked: u.Blocked,
LastLogin: u.LastLogin.Time, LastLogin: lastLogin,
Issued: u.Issued, Issued: u.Issued,
Permissions: UserPermissions{ Permissions: UserPermissions{
DashboardView: dashboardViewPermissions, DashboardView: dashboardViewPermissions,
@ -160,6 +172,11 @@ func (u *User) ToUserInfo(userData *idp.UserData, settings *Settings) (*UserInfo
userStatus = UserStatusInvited userStatus = UserStatusInvited
} }
lastLogin := time.Time{}
if u.LastLogin != nil {
lastLogin = *u.LastLogin
}
return &UserInfo{ return &UserInfo{
ID: u.Id, ID: u.Id,
Email: userData.Email, Email: userData.Email,
@ -169,7 +186,7 @@ func (u *User) ToUserInfo(userData *idp.UserData, settings *Settings) (*UserInfo
Status: string(userStatus), Status: string(userStatus),
IsServiceUser: u.IsServiceUser, IsServiceUser: u.IsServiceUser,
IsBlocked: u.Blocked, IsBlocked: u.Blocked,
LastLogin: u.LastLogin.Time, LastLogin: lastLogin,
Issued: u.Issued, Issued: u.Issued,
Permissions: UserPermissions{ Permissions: UserPermissions{
DashboardView: dashboardViewPermissions, DashboardView: dashboardViewPermissions,

View File

@ -880,7 +880,7 @@ func (am *DefaultAccountManager) GetUsersFromAccount(ctx context.Context, accoun
continue continue
} }
if !user.IsServiceUser { if !user.IsServiceUser {
users[user.Id] = userLoggedInOnce(!user.LastLogin.Time.IsZero()) users[user.Id] = userLoggedInOnce(!user.LastLoginTime().IsZero())
} }
} }
queriedUsers, err = am.lookupCache(ctx, users, accountID) queriedUsers, err = am.lookupCache(ctx, users, accountID)

View File

@ -14,3 +14,8 @@ func Difference(a, b []string) []string {
} }
return diff return diff
} }
// ToPtr returns a pointer to the given value.
func ToPtr[T any](value T) *T {
return &value
}