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
)
var ErrFailedToUnlock = errors.New("failed to unlock user keys")
type UserInfo struct {
// UserID is the user's API ID.
UserID string
@ -157,11 +159,15 @@ func (bridge *Bridge) LoginUser(
func() (string, error) {
return bridge.loginUser(ctx, client, auth.UID, auth.RefreshToken, keyPass)
},
func() error {
return client.AuthDelete(ctx)
},
)
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)
}
@ -217,7 +223,16 @@ func (bridge *Bridge) LoginFull(
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.
@ -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 {
return "", fmt.Errorf("failed to unlock user keys: %w", err)
return "", fmt.Errorf("%w: %w", ErrFailedToUnlock, err)
} 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 {

View File

@ -54,8 +54,9 @@ import (
)
const (
serverConfigFileName = "grpcServerConfig.json"
serverTokenMetadataKey = "server-token"
serverConfigFileName = "grpcServerConfig.json"
serverTokenMetadataKey = "server-token"
twoPasswordsMaxAttemptCount = 3 // The number of attempts allowed for the mailbox password.
)
// Service is the RPC service struct.
@ -82,9 +83,10 @@ type Service struct { // nolint:structcheck
target updater.VersionInfo
targetLock safe.RWMutex
authClient *proton.Client
auth proton.Auth
password []byte
authClient *proton.Client
auth proton.Auth
password []byte
twoPasswordAttemptCount int
log *logrus.Entry
initializing sync.WaitGroup
@ -408,7 +410,12 @@ func (s *Service) loginClean() {
}
func (s *Service) finishLogin() {
defer s.loginClean()
performCleanup := true
defer func() {
if performCleanup {
s.loginClean()
}
}()
wasSignedOut := s.bridge.HasUser(s.auth.UserID)
@ -426,10 +433,24 @@ func (s *Service) finishLogin() {
eventCh, done := s.bridge.GetEvents(events.UserLoggedIn{})
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 {
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
}

View File

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