diff --git a/management/server/account.go b/management/server/account.go index f5a5a2339..c19ea90cc 100644 --- a/management/server/account.go +++ b/management/server/account.go @@ -95,7 +95,6 @@ type AccountManager interface { GetDNSSettings(accountID string, userID string) (*DNSSettings, error) SaveDNSSettings(accountID string, userID string, dnsSettingsToSave *DNSSettings) error GetPeer(accountID, peerID, userID string) (*Peer, error) - UpdatePeerLastLogin(peerID string) error UpdateAccountSettings(accountID, userID string, newSettings *Settings) (*Account, error) LoginPeer(login PeerLogin) (*Peer, error) } diff --git a/management/server/grpcserver.go b/management/server/grpcserver.go index 9bf6f6012..1645dbd6d 100644 --- a/management/server/grpcserver.go +++ b/management/server/grpcserver.go @@ -218,7 +218,7 @@ func (s *GRPCServer) validateToken(jwtToken string) (string, error) { token, err := s.jwtMiddleware.ValidateAndParse(jwtToken) if err != nil { - return "", status.Errorf(codes.Internal, "invalid jwt token, err: %v", err) + return "", status.Errorf(codes.InvalidArgument, "invalid jwt token, err: %v", err) } claims := s.jwtClaimsExtractor.FromToken(token) // we need to call this method because if user is new, we will automatically add it to existing or create a new account @@ -313,6 +313,7 @@ func (s *GRPCServer) Login(ctx context.Context, req *proto.EncryptedMessage) (*p // todo what about the case when JWT provided expired? userID, err = s.validateToken(loginReq.GetJwtToken()) if err != nil { + log.Warnf("failed validating JWT token sent from peer %s", peerKey) return nil, mapError(err) } } @@ -329,11 +330,13 @@ func (s *GRPCServer) Login(ctx context.Context, req *proto.EncryptedMessage) (*p SetupKey: loginReq.GetSetupKey(), }) if err != nil { + log.Warnf("failed logging in peer %s", peerKey) return nil, mapError(err) } network, err := s.accountManager.GetPeerNetwork(peer.ID) if err != nil { + log.Warnf("failed getting peer %s network on login", peer.ID) return nil, status.Errorf(codes.Internal, "failed getting peer network on login") } @@ -344,6 +347,7 @@ func (s *GRPCServer) Login(ctx context.Context, req *proto.EncryptedMessage) (*p } encryptedResp, err := encryption.EncryptMessage(peerKey, s.wgKey, loginResp) if err != nil { + log.Warnf("failed encrypting peer %s message", peer.ID) return nil, status.Errorf(codes.Internal, "failed logging in peer") } diff --git a/management/server/mock_server/account_mock.go b/management/server/mock_server/account_mock.go index 48c5cb2c7..0af18f988 100644 --- a/management/server/mock_server/account_mock.go +++ b/management/server/mock_server/account_mock.go @@ -71,7 +71,6 @@ type MockAccountManager struct { SaveDNSSettingsFunc func(accountID, userID string, dnsSettingsToSave *server.DNSSettings) error GetPeerFunc func(accountID, peerID, userID string) (*server.Peer, error) GetAccountByPeerIDFunc func(peerID string) (*server.Account, error) - UpdatePeerLastLoginFunc func(peerID string) error UpdateAccountSettingsFunc func(accountID, userID string, newSettings *server.Settings) (*server.Account, error) LoginPeerFunc func(login server.PeerLogin) (*server.Peer, error) } @@ -550,14 +549,6 @@ func (am *MockAccountManager) GetAccountByPeerID(peerID string) (*server.Account return nil, status.Errorf(codes.Unimplemented, "method GetAccountByPeerID is not implemented") } -// UpdatePeerLastLogin mocks UpdatePeerLastLogin of the AccountManager interface -func (am *MockAccountManager) UpdatePeerLastLogin(peerID string) error { - if am.UpdatePeerLastLoginFunc != nil { - return am.UpdatePeerLastLoginFunc(peerID) - } - return status.Errorf(codes.Unimplemented, "method UpdatePeerLastLogin is not implemented") -} - // UpdateAccountSettings mocks UpdateAccountSettings of the AccountManager interface func (am *MockAccountManager) UpdateAccountSettings(accountID, userID string, newSettings *server.Settings) (*server.Account, error) { if am.UpdateAccountSettingsFunc != nil { diff --git a/management/server/peer.go b/management/server/peer.go index 33e8e2ff5..8ae0a2d10 100644 --- a/management/server/peer.go +++ b/management/server/peer.go @@ -643,6 +643,34 @@ func (am *DefaultAccountManager) AddPeer(setupKey, userID string, peer *Peer) (* return newPeer, nil } +func (am *DefaultAccountManager) checkPeerLoginExpiration(login PeerLogin, peer *Peer, account *Account) error { + if peer.AddedWithSSOLogin() { + expired, expiresIn := peer.LoginExpired(account.Settings.PeerLoginExpiration) + expired = account.Settings.PeerLoginExpirationEnabled && expired + if expired || peer.Status.LoginExpired { + log.Debugf("peer %s login expired", peer.ID) + if login.UserID == "" { + // absence of a user ID indicates that JWT wasn't provided. + _, err := am.markPeerLoginExpired(peer, account, true) + if err != nil { + return err + } + return status.Errorf(status.PermissionDenied, + "peer login has expired %v ago. Please log in once more", expiresIn) + } else { + // user ID is there meaning that JWT validation passed successfully in the API layer. + if peer.UserID != login.UserID { + log.Warnf("user mismatch when loggin in peer %s: peer user %s, login user %s ", peer.ID, peer.UserID, login.UserID) + return status.Errorf(status.Unauthenticated, "can't login") + } + peer = am.updatePeerLastLogin(peer, account) + } + } + } + + return nil +} + // LoginPeer logs in or registers a peer. // If peer doesn't exist the function checks whether a setup key or a user is present and registers a new peer if so. func (am *DefaultAccountManager) LoginPeer(login PeerLogin) (*Peer, error) { @@ -677,25 +705,9 @@ func (am *DefaultAccountManager) LoginPeer(login PeerLogin) (*Peer, error) { return nil, err } - expired, expiresIn := peer.LoginExpired(account.Settings.PeerLoginExpiration) - expired = account.Settings.PeerLoginExpirationEnabled && expired - if peer.UserID != "" && (expired || peer.Status.LoginExpired) { - if login.UserID == "" { - // absence of a user ID indicates that JWT wasn't provided. - _, err = am.markPeerLoginExpired(peer, account, true) - if err != nil { - return nil, err - } - return nil, status.Errorf(status.PermissionDenied, - "peer login has expired %v ago. Please log in once more", expiresIn) - } else { - // user ID is there meaning that JWT validation passed successfully in the API layer. - if peer.UserID != login.UserID { - log.Warnf("user mismatch when loggin in peer %s: peer user %s, login user %s ", peer.ID, peer.UserID, login.UserID) - return nil, status.Errorf(status.Unauthenticated, "can't login") - } - peer = am.updatePeerLastLogin(peer, account) - } + err = am.checkPeerLoginExpiration(login, peer, account) + if err != nil { + return nil, err } peer = am.updatePeerMeta(peer, peer.Meta, account) @@ -723,41 +735,6 @@ func (am *DefaultAccountManager) updatePeerLastLogin(peer *Peer, account *Accoun return peer } -// UpdatePeerLastLogin sets Peer.LastLogin to the current timestamp. -func (am *DefaultAccountManager) UpdatePeerLastLogin(peerID string) error { - account, err := am.Store.GetAccountByPeerID(peerID) - if err != nil { - return err - } - - unlock := am.Store.AcquireAccountLock(account.Id) - defer unlock() - - // ensure that we consider modification happened meanwhile (because we were outside the account lock when we fetched the account) - account, err = am.Store.GetAccount(account.Id) - if err != nil { - return err - } - - peer := account.GetPeer(peerID) - if peer == nil { - return status.Errorf(status.NotFound, "peer with ID %s not found", peerID) - } - - peer.LastLogin = time.Now() - newStatus := peer.Status.Copy() - newStatus.LoginExpired = false - peer.Status = newStatus - account.UpdatePeer(peer) - - err = am.Store.SaveAccount(account) - if err != nil { - return err - } - - return nil -} - func (am *DefaultAccountManager) checkAndUpdatePeerSSHKey(peer *Peer, account *Account, newSshKey string) (*Peer, error) { if len(newSshKey) == 0 { log.Debugf("no new SSH key provided for peer %s, skipping update", peer.ID)