mirror of
https://github.com/ProtonMail/proton-bridge.git
synced 2025-12-19 16:46:55 +00:00
Other: Put back old 2FA/two-pass flow in GUI
This commit is contained in:
2
go.mod
2
go.mod
@ -38,7 +38,7 @@ require (
|
|||||||
github.com/sirupsen/logrus v1.9.0
|
github.com/sirupsen/logrus v1.9.0
|
||||||
github.com/stretchr/testify v1.8.0
|
github.com/stretchr/testify v1.8.0
|
||||||
github.com/urfave/cli/v2 v2.16.3
|
github.com/urfave/cli/v2 v2.16.3
|
||||||
gitlab.protontech.ch/go/liteapi v0.33.2-0.20221011193656-705963f7a7d9
|
gitlab.protontech.ch/go/liteapi v0.33.2-0.20221012095146-bd94443eeb8e
|
||||||
golang.org/x/exp v0.0.0-20220921164117-439092de6870
|
golang.org/x/exp v0.0.0-20220921164117-439092de6870
|
||||||
golang.org/x/net v0.1.0
|
golang.org/x/net v0.1.0
|
||||||
golang.org/x/sys v0.1.0
|
golang.org/x/sys v0.1.0
|
||||||
|
|||||||
4
go.sum
4
go.sum
@ -397,8 +397,8 @@ github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsr
|
|||||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||||
github.com/zclconf/go-cty v1.11.0 h1:726SxLdi2SDnjY+BStqB9J1hNp4+2WlzyXLuimibIe0=
|
github.com/zclconf/go-cty v1.11.0 h1:726SxLdi2SDnjY+BStqB9J1hNp4+2WlzyXLuimibIe0=
|
||||||
github.com/zclconf/go-cty v1.11.0/go.mod h1:s9IfD1LK5ccNMSWCVFCE2rJfHiZgi7JijgeWIMfhLvA=
|
github.com/zclconf/go-cty v1.11.0/go.mod h1:s9IfD1LK5ccNMSWCVFCE2rJfHiZgi7JijgeWIMfhLvA=
|
||||||
gitlab.protontech.ch/go/liteapi v0.33.2-0.20221011193656-705963f7a7d9 h1:WErqL7DdcsQFNNy2Zkj8MT83HbSUbc17qptrEuVcbGA=
|
gitlab.protontech.ch/go/liteapi v0.33.2-0.20221012095146-bd94443eeb8e h1:UBgcmAYZ45ylLlfmc8/0evP40LwVthBHRoMgGqt4YV8=
|
||||||
gitlab.protontech.ch/go/liteapi v0.33.2-0.20221011193656-705963f7a7d9/go.mod h1:NfsxXn1T81sz0gHnxuAfyCI4Agzm5UWVRyEtdQSch/4=
|
gitlab.protontech.ch/go/liteapi v0.33.2-0.20221012095146-bd94443eeb8e/go.mod h1:NfsxXn1T81sz0gHnxuAfyCI4Agzm5UWVRyEtdQSch/4=
|
||||||
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
||||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||||
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
||||||
|
|||||||
@ -12,7 +12,7 @@ var (
|
|||||||
|
|
||||||
ErrNoSuchUser = errors.New("no such user")
|
ErrNoSuchUser = errors.New("no such user")
|
||||||
ErrUserAlreadyExists = errors.New("user already exists")
|
ErrUserAlreadyExists = errors.New("user already exists")
|
||||||
ErrUserAlreadyLoggedIn = errors.New("user already logged in")
|
ErrUserAlreadyLoggedIn = errors.New("the user is already logged in")
|
||||||
ErrNotImplemented = errors.New("not implemented")
|
ErrNotImplemented = errors.New("not implemented")
|
||||||
|
|
||||||
ErrSizeTooLarge = errors.New("file is too big")
|
ErrSizeTooLarge = errors.New("file is too big")
|
||||||
|
|||||||
@ -37,6 +37,7 @@ import (
|
|||||||
"github.com/ProtonMail/proton-bridge/v2/pkg/restarter"
|
"github.com/ProtonMail/proton-bridge/v2/pkg/restarter"
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
|
"gitlab.protontech.ch/go/liteapi"
|
||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
codes "google.golang.org/grpc/codes"
|
codes "google.golang.org/grpc/codes"
|
||||||
"google.golang.org/grpc/credentials"
|
"google.golang.org/grpc/credentials"
|
||||||
@ -65,6 +66,10 @@ type Service struct { // nolint:structcheck
|
|||||||
bridge *bridge.Bridge
|
bridge *bridge.Bridge
|
||||||
newVersionInfo updater.VersionInfo
|
newVersionInfo updater.VersionInfo
|
||||||
|
|
||||||
|
authClient *liteapi.Client
|
||||||
|
auth liteapi.Auth
|
||||||
|
password []byte
|
||||||
|
|
||||||
log *logrus.Entry
|
log *logrus.Entry
|
||||||
initializing sync.WaitGroup
|
initializing sync.WaitGroup
|
||||||
initializationDone sync.Once
|
initializationDone sync.Once
|
||||||
@ -263,6 +268,45 @@ func (s *Service) watchEvents() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Service) loginAbort() {
|
||||||
|
s.loginClean()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) loginClean() {
|
||||||
|
s.auth = liteapi.Auth{}
|
||||||
|
s.authClient = nil
|
||||||
|
for i := range s.password {
|
||||||
|
s.password[i] = '\x00'
|
||||||
|
}
|
||||||
|
s.password = s.password[0:0]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) finishLogin() {
|
||||||
|
defer s.loginClean()
|
||||||
|
|
||||||
|
if len(s.password) == 0 || s.auth.UID == "" || s.authClient == nil {
|
||||||
|
s.log.
|
||||||
|
WithField("hasPass", len(s.password) != 0).
|
||||||
|
WithField("hasAuth", s.auth.UID != "").
|
||||||
|
WithField("hasClient", s.authClient != nil).
|
||||||
|
Error("Finish login: authentication incomplete")
|
||||||
|
|
||||||
|
_ = s.SendEvent(NewLoginError(LoginErrorType_TWO_PASSWORDS_ABORT, "Missing authentication, try again."))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
userID, err := s.bridge.LoginUser(context.Background(), 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()))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
s.log.WithField("userID", userID).Debug("Login finished")
|
||||||
|
|
||||||
|
_ = s.SendEvent(NewLoginFinishedEvent(userID))
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Service) triggerReset() {
|
func (s *Service) triggerReset() {
|
||||||
defer func() {
|
defer func() {
|
||||||
_ = s.SendEvent(NewResetFinishedEvent())
|
_ = s.SendEvent(NewResetFinishedEvent())
|
||||||
|
|||||||
@ -20,15 +20,18 @@ package grpc
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
|
"errors"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
|
||||||
"github.com/Masterminds/semver/v3"
|
"github.com/Masterminds/semver/v3"
|
||||||
|
"github.com/ProtonMail/proton-bridge/v2/internal/bridge"
|
||||||
"github.com/ProtonMail/proton-bridge/v2/internal/constants"
|
"github.com/ProtonMail/proton-bridge/v2/internal/constants"
|
||||||
"github.com/ProtonMail/proton-bridge/v2/internal/frontend/theme"
|
"github.com/ProtonMail/proton-bridge/v2/internal/frontend/theme"
|
||||||
"github.com/ProtonMail/proton-bridge/v2/internal/updater"
|
"github.com/ProtonMail/proton-bridge/v2/internal/updater"
|
||||||
"github.com/ProtonMail/proton-bridge/v2/pkg/keychain"
|
"github.com/ProtonMail/proton-bridge/v2/pkg/keychain"
|
||||||
"github.com/ProtonMail/proton-bridge/v2/pkg/ports"
|
"github.com/ProtonMail/proton-bridge/v2/pkg/ports"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
|
"gitlab.protontech.ch/go/liteapi"
|
||||||
"golang.org/x/exp/maps"
|
"golang.org/x/exp/maps"
|
||||||
"google.golang.org/grpc/codes"
|
"google.golang.org/grpc/codes"
|
||||||
"google.golang.org/grpc/status"
|
"google.golang.org/grpc/status"
|
||||||
@ -364,33 +367,125 @@ func (s *Service) Login(ctx context.Context, login *LoginRequest) (*emptypb.Empt
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Handle different error types!
|
client, auth, err := s.bridge.LoginAuth(context.Background(), login.Username, password)
|
||||||
// - bad credentials
|
|
||||||
// - bad proton plan
|
|
||||||
// - user already exists
|
|
||||||
userID, err := s.bridge.LoginFull(context.Background(), login.Username, password, nil, nil)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.log.WithError(err).Error("Cannot login user")
|
defer s.loginClean()
|
||||||
_ = s.SendEvent(NewLoginError(LoginErrorType_USERNAME_PASSWORD_ERROR, "Cannot login user"))
|
|
||||||
|
switch {
|
||||||
|
case errors.Is(err, bridge.ErrUserAlreadyLoggedIn):
|
||||||
|
_ = s.SendEvent(NewLoginAlreadyLoggedInEvent(auth.UserID))
|
||||||
|
|
||||||
|
case errors.Is(err, liteapi.ErrIncorrectLoginCredentials):
|
||||||
|
_ = s.SendEvent(NewLoginError(LoginErrorType_USERNAME_PASSWORD_ERROR, ""))
|
||||||
|
|
||||||
|
case errors.Is(err, liteapi.ErrPaidPlanRequired):
|
||||||
|
_ = s.SendEvent(NewLoginError(LoginErrorType_FREE_USER, ""))
|
||||||
|
|
||||||
|
default:
|
||||||
|
_ = s.SendEvent(NewLoginError(LoginErrorType_USERNAME_PASSWORD_ERROR, err.Error()))
|
||||||
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
_ = s.SendEvent(NewLoginFinishedEvent(userID))
|
s.password = password
|
||||||
|
s.authClient = client
|
||||||
|
s.auth = auth
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case auth.TwoFA.Enabled == liteapi.TOTPEnabled:
|
||||||
|
_ = s.SendEvent(NewLoginTfaRequestedEvent(login.Username))
|
||||||
|
|
||||||
|
case auth.PasswordMode == liteapi.TwoPasswordMode:
|
||||||
|
_ = s.SendEvent(NewLoginTwoPasswordsRequestedEvent())
|
||||||
|
|
||||||
|
default:
|
||||||
|
s.finishLogin()
|
||||||
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
return &emptypb.Empty{}, nil
|
return &emptypb.Empty{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Service) Login2FA(_ context.Context, login *LoginRequest) (*emptypb.Empty, error) {
|
func (s *Service) Login2FA(ctx context.Context, login *LoginRequest) (*emptypb.Empty, error) {
|
||||||
panic("TODO")
|
s.log.WithField("username", login.Username).Debug("Login2FA")
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
defer s.panicHandler.HandlePanic()
|
||||||
|
|
||||||
|
if s.auth.UID == "" || s.authClient == nil {
|
||||||
|
s.log.Errorf("Login 2FA: authethication incomplete %p %p", s.auth.UID, s.authClient)
|
||||||
|
_ = s.SendEvent(NewLoginError(LoginErrorType_TFA_ABORT, "Missing authentication, try again."))
|
||||||
|
s.loginClean()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
twoFA, err := base64Decode(login.Password)
|
||||||
|
if err != nil {
|
||||||
|
s.log.WithError(err).Error("Cannot decode 2fa code")
|
||||||
|
_ = s.SendEvent(NewLoginError(LoginErrorType_USERNAME_PASSWORD_ERROR, "Cannot decode 2fa code"))
|
||||||
|
s.loginClean()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := s.authClient.Auth2FA(context.Background(), liteapi.Auth2FAReq{TwoFactorCode: string(twoFA)}); err != nil {
|
||||||
|
switch {
|
||||||
|
case errors.Is(err, liteapi.ErrBad2FACode):
|
||||||
|
s.log.Warn("Login 2FA: retry 2fa")
|
||||||
|
_ = s.SendEvent(NewLoginError(LoginErrorType_TFA_ERROR, ""))
|
||||||
|
|
||||||
|
default:
|
||||||
|
s.log.WithError(err).Warn("Login 2FA: failed")
|
||||||
|
_ = s.SendEvent(NewLoginError(LoginErrorType_TFA_ABORT, err.Error()))
|
||||||
|
s.loginClean()
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.auth.PasswordMode == liteapi.TwoPasswordMode {
|
||||||
|
_ = s.SendEvent(NewLoginTwoPasswordsRequestedEvent())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
s.finishLogin()
|
||||||
|
}()
|
||||||
|
|
||||||
|
return &emptypb.Empty{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Service) Login2Passwords(_ context.Context, login *LoginRequest) (*emptypb.Empty, error) {
|
func (s *Service) Login2Passwords(ctx context.Context, login *LoginRequest) (*emptypb.Empty, error) {
|
||||||
panic("TODO")
|
s.log.WithField("username", login.Username).Debug("Login2Passwords")
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
defer s.panicHandler.HandlePanic()
|
||||||
|
|
||||||
|
password, err := base64Decode(login.Password)
|
||||||
|
if err != nil {
|
||||||
|
s.log.WithError(err).Error("Cannot decode mbox password")
|
||||||
|
_ = s.SendEvent(NewLoginError(LoginErrorType_USERNAME_PASSWORD_ERROR, "Cannot decode mbox password"))
|
||||||
|
s.loginClean()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
s.password = password
|
||||||
|
|
||||||
|
s.finishLogin()
|
||||||
|
}()
|
||||||
|
|
||||||
|
return &emptypb.Empty{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Service) LoginAbort(_ context.Context, loginAbort *LoginAbortRequest) (*emptypb.Empty, error) {
|
func (s *Service) LoginAbort(ctx context.Context, loginAbort *LoginAbortRequest) (*emptypb.Empty, error) {
|
||||||
panic("TODO")
|
s.log.WithField("username", loginAbort.Username).Debug("LoginAbort")
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
defer s.panicHandler.HandlePanic()
|
||||||
|
|
||||||
|
s.loginAbort()
|
||||||
|
}()
|
||||||
|
|
||||||
|
return &emptypb.Empty{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
Reference in New Issue
Block a user