feat(GODT-2940): allow 3 attempts for mailbox password.

This commit is contained in:
Xavier Michelon
2023-10-02 15:15:30 +02:00
parent 0c212fbef4
commit 3bf4282037
3 changed files with 51 additions and 14 deletions

View File

@ -46,6 +46,8 @@ const (
Connected Connected
) )
var ErrFailedToUnlock = errors.New("failed to unlock user keys")
type UserInfo struct { type UserInfo struct {
// UserID is the user's API ID. // UserID is the user's API ID.
UserID string UserID string
@ -157,11 +159,15 @@ func (bridge *Bridge) LoginUser(
func() (string, error) { func() (string, error) {
return bridge.loginUser(ctx, client, auth.UID, auth.RefreshToken, keyPass) return bridge.loginUser(ctx, client, auth.UID, auth.RefreshToken, keyPass)
}, },
func() error {
return client.AuthDelete(ctx)
},
) )
if err != nil { if err != nil {
// Failure to unlock will allow retries, so we do not delete auth.
if !errors.Is(err, ErrFailedToUnlock) {
if deleteErr := client.AuthDelete(ctx); deleteErr != nil {
logrus.WithError(deleteErr).Error("Failed to delete auth")
}
}
return "", fmt.Errorf("failed to login user: %w", err) return "", fmt.Errorf("failed to login user: %w", err)
} }
@ -217,7 +223,16 @@ func (bridge *Bridge) LoginFull(
keyPass = password keyPass = password
} }
return bridge.LoginUser(ctx, client, auth, keyPass) userID, err := bridge.LoginUser(ctx, client, auth, keyPass)
if err != nil {
if deleteErr := client.AuthDelete(ctx); deleteErr != nil {
logrus.WithError(err).Error("Failed to delete auth")
}
return "", err
}
return userID, nil
} }
// LogoutUser logs out the given user. // LogoutUser logs out the given user.
@ -374,9 +389,9 @@ func (bridge *Bridge) loginUser(ctx context.Context, client *proton.Client, auth
} }
if userKR, err := apiUser.Keys.Unlock(saltedKeyPass, nil); err != nil { if userKR, err := apiUser.Keys.Unlock(saltedKeyPass, nil); err != nil {
return "", fmt.Errorf("failed to unlock user keys: %w", err) return "", fmt.Errorf("%w: %w", ErrFailedToUnlock, err)
} else if userKR.CountDecryptionEntities() == 0 { } else if userKR.CountDecryptionEntities() == 0 {
return "", fmt.Errorf("failed to unlock user keys") return "", ErrFailedToUnlock
} }
if err := bridge.addUser(ctx, client, apiUser, authUID, authRef, saltedKeyPass, true); err != nil { if err := bridge.addUser(ctx, client, apiUser, authUID, authRef, saltedKeyPass, true); err != nil {

View File

@ -56,6 +56,7 @@ import (
const ( const (
serverConfigFileName = "grpcServerConfig.json" serverConfigFileName = "grpcServerConfig.json"
serverTokenMetadataKey = "server-token" serverTokenMetadataKey = "server-token"
twoPasswordsMaxAttemptCount = 3 // The number of attempts allowed for the mailbox password.
) )
// Service is the RPC service struct. // Service is the RPC service struct.
@ -85,6 +86,7 @@ type Service struct { // nolint:structcheck
authClient *proton.Client authClient *proton.Client
auth proton.Auth auth proton.Auth
password []byte password []byte
twoPasswordAttemptCount int
log *logrus.Entry log *logrus.Entry
initializing sync.WaitGroup initializing sync.WaitGroup
@ -408,7 +410,12 @@ func (s *Service) loginClean() {
} }
func (s *Service) finishLogin() { func (s *Service) finishLogin() {
defer s.loginClean() performCleanup := true
defer func() {
if performCleanup {
s.loginClean()
}
}()
wasSignedOut := s.bridge.HasUser(s.auth.UserID) wasSignedOut := s.bridge.HasUser(s.auth.UserID)
@ -426,10 +433,24 @@ func (s *Service) finishLogin() {
eventCh, done := s.bridge.GetEvents(events.UserLoggedIn{}) eventCh, done := s.bridge.GetEvents(events.UserLoggedIn{})
defer done() defer done()
userID, err := s.bridge.LoginUser(context.Background(), s.authClient, s.auth, s.password) ctx := context.Background()
userID, err := s.bridge.LoginUser(ctx, s.authClient, s.auth, s.password)
if err != nil { if err != nil {
s.log.WithError(err).Errorf("Finish login failed") s.log.WithError(err).Errorf("Finish login failed")
_ = s.SendEvent(NewLoginError(LoginErrorType_TWO_PASSWORDS_ABORT, err.Error())) s.twoPasswordAttemptCount++
errType := LoginErrorType_TWO_PASSWORDS_ABORT
if errors.Is(err, bridge.ErrFailedToUnlock) {
if s.twoPasswordAttemptCount < twoPasswordsMaxAttemptCount {
performCleanup = false
errType = LoginErrorType_TWO_PASSWORDS_ERROR
} else {
if deleteErr := s.authClient.AuthDelete(ctx); deleteErr != nil {
s.log.WithError(deleteErr).Error("Failed to delete auth")
}
}
}
_ = s.SendEvent(NewLoginError(errType, err.Error()))
return return
} }

View File

@ -384,6 +384,7 @@ func (s *Service) Login(_ context.Context, login *LoginRequest) (*emptypb.Empty,
go func() { go func() {
defer async.HandlePanic(s.panicHandler) defer async.HandlePanic(s.panicHandler)
s.twoPasswordAttemptCount = 0
password, err := base64Decode(login.Password) password, err := base64Decode(login.Password)
if err != nil { if err != nil {
s.log.WithError(err).Error("Cannot decode password") s.log.WithError(err).Error("Cannot decode password")