mirror of
https://github.com/ProtonMail/proton-bridge.git
synced 2025-12-16 07:06:45 +00:00
Other(refactor): Remove bridgeWrap from frontend interface
This commit is contained in:
@ -22,7 +22,7 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/ProtonMail/proton-bridge/v2/internal/frontend/types"
|
||||
"github.com/ProtonMail/proton-bridge/v2/internal/users"
|
||||
"github.com/abiosoft/ishell"
|
||||
)
|
||||
|
||||
@ -35,9 +35,13 @@ func (f *frontendCLI) completeUsernames(args []string) (usernames []string) {
|
||||
if len(args) == 1 {
|
||||
arg = args[0]
|
||||
}
|
||||
for _, user := range f.bridge.GetUsers() {
|
||||
if strings.HasPrefix(strings.ToLower(user.Username()), strings.ToLower(arg)) {
|
||||
usernames = append(usernames, user.Username())
|
||||
for _, userID := range f.bridge.GetUserIDs() {
|
||||
user, err := f.bridge.GetUserInfo(userID)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if strings.HasPrefix(strings.ToLower(user.Username), strings.ToLower(arg)) {
|
||||
usernames = append(usernames, user.Username)
|
||||
}
|
||||
}
|
||||
return
|
||||
@ -46,7 +50,7 @@ func (f *frontendCLI) completeUsernames(args []string) (usernames []string) {
|
||||
// noAccountWrapper is a decorator for functions which need any account to be properly functional.
|
||||
func (f *frontendCLI) noAccountWrapper(callback func(*ishell.Context)) func(*ishell.Context) {
|
||||
return func(c *ishell.Context) {
|
||||
users := f.bridge.GetUsers()
|
||||
users := f.bridge.GetUserIDs()
|
||||
if len(users) == 0 {
|
||||
f.Println("No active accounts. Please add account to continue.")
|
||||
} else {
|
||||
@ -55,46 +59,54 @@ func (f *frontendCLI) noAccountWrapper(callback func(*ishell.Context)) func(*ish
|
||||
}
|
||||
}
|
||||
|
||||
func (f *frontendCLI) askUserByIndexOrName(c *ishell.Context) types.User {
|
||||
func (f *frontendCLI) askUserByIndexOrName(c *ishell.Context) users.UserInfo {
|
||||
user := f.getUserByIndexOrName("")
|
||||
if user != nil {
|
||||
if user.ID != "" {
|
||||
return user
|
||||
}
|
||||
|
||||
numberOfAccounts := len(f.bridge.GetUsers())
|
||||
numberOfAccounts := len(f.bridge.GetUserIDs())
|
||||
indexRange := fmt.Sprintf("number between 0 and %d", numberOfAccounts-1)
|
||||
if len(c.Args) == 0 {
|
||||
f.Printf("Please choose %s or username.\n", indexRange)
|
||||
return nil
|
||||
return users.UserInfo{}
|
||||
}
|
||||
arg := c.Args[0]
|
||||
user = f.getUserByIndexOrName(arg)
|
||||
if user == nil {
|
||||
if user.ID == "" {
|
||||
f.Printf("Wrong input '%s'. Choose %s or username.\n", bold(arg), indexRange)
|
||||
return nil
|
||||
return users.UserInfo{}
|
||||
}
|
||||
return user
|
||||
}
|
||||
|
||||
func (f *frontendCLI) getUserByIndexOrName(arg string) types.User {
|
||||
users := f.bridge.GetUsers()
|
||||
numberOfAccounts := len(users)
|
||||
func (f *frontendCLI) getUserByIndexOrName(arg string) users.UserInfo {
|
||||
userIDs := f.bridge.GetUserIDs()
|
||||
numberOfAccounts := len(userIDs)
|
||||
if numberOfAccounts == 0 {
|
||||
return nil
|
||||
return users.UserInfo{}
|
||||
}
|
||||
res := make([]users.UserInfo, len(userIDs))
|
||||
for idx, userID := range userIDs {
|
||||
user, err := f.bridge.GetUserInfo(userID)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
res[idx] = user
|
||||
}
|
||||
if numberOfAccounts == 1 {
|
||||
return users[0]
|
||||
return res[0]
|
||||
}
|
||||
if index, err := strconv.Atoi(arg); err == nil {
|
||||
if index < 0 || index >= numberOfAccounts {
|
||||
return nil
|
||||
return users.UserInfo{}
|
||||
}
|
||||
return users[index]
|
||||
return res[index]
|
||||
}
|
||||
for _, user := range users {
|
||||
if user.Username() == arg {
|
||||
for _, user := range res {
|
||||
if user.Username == arg {
|
||||
return user
|
||||
}
|
||||
}
|
||||
return nil
|
||||
return users.UserInfo{}
|
||||
}
|
||||
|
||||
@ -23,48 +23,52 @@ import (
|
||||
|
||||
"github.com/ProtonMail/proton-bridge/v2/internal/bridge"
|
||||
"github.com/ProtonMail/proton-bridge/v2/internal/config/settings"
|
||||
"github.com/ProtonMail/proton-bridge/v2/internal/frontend/types"
|
||||
"github.com/ProtonMail/proton-bridge/v2/internal/users"
|
||||
"github.com/abiosoft/ishell"
|
||||
)
|
||||
|
||||
func (f *frontendCLI) listAccounts(c *ishell.Context) {
|
||||
spacing := "%-2d: %-20s (%-15s, %-15s)\n"
|
||||
f.Printf(bold(strings.ReplaceAll(spacing, "d", "s")), "#", "account", "status", "address mode")
|
||||
for idx, user := range f.bridge.GetUsers() {
|
||||
for idx, userID := range f.bridge.GetUserIDs() {
|
||||
user, err := f.bridge.GetUserInfo(userID)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
connected := "disconnected"
|
||||
if user.IsConnected() {
|
||||
if user.Connected {
|
||||
connected = "connected"
|
||||
}
|
||||
mode := "split"
|
||||
if user.IsCombinedAddressMode() {
|
||||
if user.Mode == users.CombinedMode {
|
||||
mode = "combined"
|
||||
}
|
||||
f.Printf(spacing, idx, user.Username(), connected, mode)
|
||||
f.Printf(spacing, idx, user.Username, connected, mode)
|
||||
}
|
||||
f.Println()
|
||||
}
|
||||
|
||||
func (f *frontendCLI) showAccountInfo(c *ishell.Context) {
|
||||
user := f.askUserByIndexOrName(c)
|
||||
if user == nil {
|
||||
if user.ID == "" {
|
||||
return
|
||||
}
|
||||
|
||||
if !user.IsConnected() {
|
||||
f.Printf("Please login to %s to get email client configuration.\n", bold(user.Username()))
|
||||
if !user.Connected {
|
||||
f.Printf("Please login to %s to get email client configuration.\n", bold(user.Username))
|
||||
return
|
||||
}
|
||||
|
||||
if user.IsCombinedAddressMode() {
|
||||
f.showAccountAddressInfo(user, user.GetPrimaryAddress())
|
||||
if user.Mode == users.CombinedMode {
|
||||
f.showAccountAddressInfo(user, user.Addresses[user.Primary])
|
||||
} else {
|
||||
for _, address := range user.GetAddresses() {
|
||||
for _, address := range user.Addresses {
|
||||
f.showAccountAddressInfo(user, address)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (f *frontendCLI) showAccountAddressInfo(user types.User, address string) {
|
||||
func (f *frontendCLI) showAccountAddressInfo(user users.UserInfo, address string) {
|
||||
smtpSecurity := "STARTTLS"
|
||||
if f.bridge.GetBool(settings.SMTPSSLKey) {
|
||||
smtpSecurity = "SSL"
|
||||
@ -74,7 +78,7 @@ func (f *frontendCLI) showAccountAddressInfo(user types.User, address string) {
|
||||
bridge.Host,
|
||||
f.bridge.GetInt(settings.IMAPPortKey),
|
||||
address,
|
||||
user.GetBridgePassword(),
|
||||
user.Password,
|
||||
"STARTTLS",
|
||||
)
|
||||
f.Println("")
|
||||
@ -82,7 +86,7 @@ func (f *frontendCLI) showAccountAddressInfo(user types.User, address string) {
|
||||
bridge.Host,
|
||||
f.bridge.GetInt(settings.SMTPPortKey),
|
||||
address,
|
||||
user.GetBridgePassword(),
|
||||
user.Password,
|
||||
smtpSecurity,
|
||||
)
|
||||
f.Println("")
|
||||
@ -95,8 +99,8 @@ func (f *frontendCLI) loginAccount(c *ishell.Context) { //nolint:funlen
|
||||
loginName := ""
|
||||
if len(c.Args) > 0 {
|
||||
user := f.getUserByIndexOrName(c.Args[0])
|
||||
if user != nil {
|
||||
loginName = user.GetPrimaryAddress()
|
||||
if user.ID != "" {
|
||||
loginName = user.Addresses[user.Primary]
|
||||
}
|
||||
}
|
||||
|
||||
@ -143,14 +147,19 @@ func (f *frontendCLI) loginAccount(c *ishell.Context) { //nolint:funlen
|
||||
}
|
||||
|
||||
f.Println("Adding account ...")
|
||||
user, err := f.bridge.FinishLogin(client, auth, []byte(mailboxPassword))
|
||||
userID, err := f.bridge.FinishLogin(client, auth, []byte(mailboxPassword))
|
||||
if err != nil {
|
||||
log.WithField("username", loginName).WithError(err).Error("Login was unsuccessful")
|
||||
f.Println("Adding account was unsuccessful:", err)
|
||||
return
|
||||
}
|
||||
|
||||
f.Printf("Account %s was added successfully.\n", bold(user.Username()))
|
||||
user, err := f.bridge.GetUserInfo(userID)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
f.Printf("Account %s was added successfully.\n", bold(user.Username))
|
||||
}
|
||||
|
||||
func (f *frontendCLI) logoutAccount(c *ishell.Context) {
|
||||
@ -158,11 +167,11 @@ func (f *frontendCLI) logoutAccount(c *ishell.Context) {
|
||||
defer f.ShowPrompt(true)
|
||||
|
||||
user := f.askUserByIndexOrName(c)
|
||||
if user == nil {
|
||||
if user.ID == "" {
|
||||
return
|
||||
}
|
||||
if f.yesNoQuestion("Are you sure you want to logout account " + bold(user.Username())) {
|
||||
if err := user.Logout(); err != nil {
|
||||
if f.yesNoQuestion("Are you sure you want to logout account " + bold(user.Username)) {
|
||||
if err := f.bridge.LogoutUser(user.ID); err != nil {
|
||||
f.printAndLogError("Logging out failed: ", err)
|
||||
}
|
||||
}
|
||||
@ -173,12 +182,12 @@ func (f *frontendCLI) deleteAccount(c *ishell.Context) {
|
||||
defer f.ShowPrompt(true)
|
||||
|
||||
user := f.askUserByIndexOrName(c)
|
||||
if user == nil {
|
||||
if user.ID == "" {
|
||||
return
|
||||
}
|
||||
if f.yesNoQuestion("Are you sure you want to " + bold("remove account "+user.Username())) {
|
||||
if f.yesNoQuestion("Are you sure you want to " + bold("remove account "+user.Username)) {
|
||||
clearCache := f.yesNoQuestion("Do you want to remove cache for this account")
|
||||
if err := f.bridge.DeleteUser(user.ID(), clearCache); err != nil {
|
||||
if err := f.bridge.DeleteUser(user.ID, clearCache); err != nil {
|
||||
f.printAndLogError("Cannot delete account: ", err)
|
||||
return
|
||||
}
|
||||
@ -193,9 +202,13 @@ func (f *frontendCLI) deleteAccounts(c *ishell.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
for _, user := range f.bridge.GetUsers() {
|
||||
if err := f.bridge.DeleteUser(user.ID(), false); err != nil {
|
||||
f.printAndLogError("Cannot delete account ", user.Username(), ": ", err)
|
||||
for _, userID := range f.bridge.GetUserIDs() {
|
||||
user, err := f.bridge.GetUserInfo(userID)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if err := f.bridge.DeleteUser(user.ID, false); err != nil {
|
||||
f.printAndLogError("Cannot delete account ", user.Username, ": ", err)
|
||||
}
|
||||
}
|
||||
|
||||
@ -222,19 +235,25 @@ func (f *frontendCLI) deleteEverything(c *ishell.Context) {
|
||||
|
||||
func (f *frontendCLI) changeMode(c *ishell.Context) {
|
||||
user := f.askUserByIndexOrName(c)
|
||||
if user == nil {
|
||||
if user.ID == "" {
|
||||
return
|
||||
}
|
||||
|
||||
newMode := "combined mode"
|
||||
if user.IsCombinedAddressMode() {
|
||||
newMode = "split mode"
|
||||
var targetMode users.AddressMode
|
||||
|
||||
if user.Mode == users.CombinedMode {
|
||||
targetMode = users.SplitMode
|
||||
} else {
|
||||
targetMode = users.CombinedMode
|
||||
}
|
||||
if !f.yesNoQuestion("Are you sure you want to change the mode for account " + bold(user.Username()) + " to " + bold(newMode)) {
|
||||
|
||||
if !f.yesNoQuestion("Are you sure you want to change the mode for account " + bold(user.Username) + " to " + bold(targetMode)) {
|
||||
return
|
||||
}
|
||||
if err := user.SwitchAddressMode(); err != nil {
|
||||
|
||||
if err := f.bridge.SetAddressMode(user.ID, targetMode); err != nil {
|
||||
f.printAndLogError("Cannot switch address mode:", err)
|
||||
}
|
||||
f.Printf("Address mode for account %s changed to %s\n", user.Username(), newMode)
|
||||
|
||||
f.Printf("Address mode for account %s changed to %s\n", user.Username, targetMode)
|
||||
}
|
||||
|
||||
@ -307,11 +307,11 @@ func (f *frontendCLI) watchEvents() {
|
||||
case address := <-addressChangedLogoutCh:
|
||||
f.notifyLogout(address)
|
||||
case userID := <-logoutCh:
|
||||
user, err := f.bridge.GetUser(userID)
|
||||
user, err := f.bridge.GetUserInfo(userID)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
f.notifyLogout(user.Username())
|
||||
f.notifyLogout(user.Username)
|
||||
case <-certIssue:
|
||||
f.notifyCertIssue()
|
||||
}
|
||||
|
||||
@ -46,7 +46,6 @@ func New(
|
||||
bridge *bridge.Bridge,
|
||||
restarter types.Restarter,
|
||||
) Frontend {
|
||||
bridgeWrap := types.NewBridgeWrap(bridge)
|
||||
switch frontendType {
|
||||
case "grpc":
|
||||
return grpc.NewService(
|
||||
@ -54,7 +53,7 @@ func New(
|
||||
panicHandler,
|
||||
eventListener,
|
||||
updater,
|
||||
bridgeWrap,
|
||||
bridge,
|
||||
restarter,
|
||||
)
|
||||
|
||||
@ -63,7 +62,7 @@ func New(
|
||||
panicHandler,
|
||||
eventListener,
|
||||
updater,
|
||||
bridgeWrap,
|
||||
bridge,
|
||||
restarter,
|
||||
)
|
||||
|
||||
|
||||
@ -8,6 +8,7 @@ package grpc
|
||||
|
||||
import (
|
||||
context "context"
|
||||
|
||||
grpc "google.golang.org/grpc"
|
||||
codes "google.golang.org/grpc/codes"
|
||||
status "google.golang.org/grpc/status"
|
||||
|
||||
@ -229,11 +229,11 @@ func (s *Service) watchEvents() { // nolint:funlen
|
||||
case address := <-addressChangedLogoutCh:
|
||||
_ = s.SendEvent(NewMailAddressChangeLogoutEvent(address))
|
||||
case userID := <-logoutCh:
|
||||
user, err := s.bridge.GetUser(userID)
|
||||
user, err := s.bridge.GetUserInfo(userID)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
_ = s.SendEvent(NewUserDisconnectedEvent(user.Username()))
|
||||
_ = s.SendEvent(NewUserDisconnectedEvent(user.Username))
|
||||
case <-updateApplicationCh:
|
||||
s.updateForce()
|
||||
case userID := <-userChangedCh:
|
||||
@ -275,7 +275,7 @@ func (s *Service) finishLogin() {
|
||||
s.eventListener.Add(events.UserChangeDone, done)
|
||||
defer s.eventListener.Remove(events.UserChangeDone, done)
|
||||
|
||||
user, err := s.bridge.FinishLogin(s.authClient, s.auth, s.password)
|
||||
userID, err := s.bridge.FinishLogin(s.authClient, s.auth, s.password)
|
||||
|
||||
if err != nil && err != users.ErrUserAlreadyConnected {
|
||||
s.log.WithError(err).Errorf("Finish login failed")
|
||||
@ -286,14 +286,14 @@ func (s *Service) finishLogin() {
|
||||
// The user changed should be triggered by FinishLogin, but it is not
|
||||
// guaranteed when this is going to happen. Therefor we should wait
|
||||
// until we receive the signal from userChanged function.
|
||||
s.waitForUserChangeDone(done, user.ID())
|
||||
s.waitForUserChangeDone(done, userID)
|
||||
|
||||
s.log.WithField("userID", user.ID()).Debug("Login finished")
|
||||
_ = s.SendEvent(NewLoginFinishedEvent(user.ID()))
|
||||
s.log.WithField("userID", userID).Debug("Login finished")
|
||||
_ = s.SendEvent(NewLoginFinishedEvent(userID))
|
||||
|
||||
if err == users.ErrUserAlreadyConnected {
|
||||
s.log.WithError(err).Error("User already logged in")
|
||||
_ = s.SendEvent(NewLoginAlreadyLoggedInEvent(user.ID()))
|
||||
_ = s.SendEvent(NewLoginAlreadyLoggedInEvent(userID))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -21,6 +21,8 @@ import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/ProtonMail/proton-bridge/v2/internal/users"
|
||||
"github.com/sirupsen/logrus"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
"google.golang.org/protobuf/types/known/emptypb"
|
||||
@ -30,11 +32,15 @@ import (
|
||||
func (s *Service) GetUserList(context.Context, *emptypb.Empty) (*UserListResponse, error) {
|
||||
s.log.Info("GetUserList")
|
||||
|
||||
users := s.bridge.GetUsers()
|
||||
var userList []*User
|
||||
|
||||
userList := make([]*User, len(users))
|
||||
for i, user := range users {
|
||||
userList[i] = grpcUserFromBridge(user)
|
||||
for idx, userID := range s.bridge.GetUserIDs() {
|
||||
user, err := s.bridge.GetUserInfo(userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
userList[idx] = grpcUserFromInfo(user)
|
||||
}
|
||||
|
||||
// If there are no active accounts.
|
||||
@ -48,18 +54,18 @@ func (s *Service) GetUserList(context.Context, *emptypb.Empty) (*UserListRespons
|
||||
func (s *Service) GetUser(_ context.Context, userID *wrapperspb.StringValue) (*User, error) {
|
||||
s.log.WithField("userID", userID).Info("GetUser")
|
||||
|
||||
user, err := s.bridge.GetUser(userID.Value)
|
||||
user, err := s.bridge.GetUserInfo(userID.Value)
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.NotFound, "user not found %v", userID.Value)
|
||||
}
|
||||
|
||||
return grpcUserFromBridge(user), nil
|
||||
return grpcUserFromInfo(user), nil
|
||||
}
|
||||
|
||||
func (s *Service) SetUserSplitMode(_ context.Context, splitMode *UserSplitModeRequest) (*emptypb.Empty, error) {
|
||||
s.log.WithField("UserID", splitMode.UserID).WithField("Active", splitMode.Active).Info("SetUserSplitMode")
|
||||
|
||||
user, err := s.bridge.GetUser(splitMode.UserID)
|
||||
user, err := s.bridge.GetUserInfo(splitMode.UserID)
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.NotFound, "user not found %v", splitMode.UserID)
|
||||
}
|
||||
@ -67,8 +73,17 @@ func (s *Service) SetUserSplitMode(_ context.Context, splitMode *UserSplitModeRe
|
||||
go func() {
|
||||
defer s.panicHandler.HandlePanic()
|
||||
defer func() { _ = s.SendEvent(NewUserToggleSplitModeFinishedEvent(splitMode.UserID)) }()
|
||||
if splitMode.Active == user.IsCombinedAddressMode() {
|
||||
_ = user.SwitchAddressMode() // check for errors
|
||||
|
||||
var targetMode users.AddressMode
|
||||
|
||||
if splitMode.Active && user.Mode == users.CombinedMode {
|
||||
targetMode = users.SplitMode
|
||||
} else if !splitMode.Active && user.Mode == users.SplitMode {
|
||||
targetMode = users.CombinedMode
|
||||
}
|
||||
|
||||
if err := s.bridge.SetAddressMode(user.ID, targetMode); err != nil {
|
||||
logrus.WithError(err).Error("Failed to set address mode")
|
||||
}
|
||||
}()
|
||||
|
||||
@ -78,14 +93,16 @@ func (s *Service) SetUserSplitMode(_ context.Context, splitMode *UserSplitModeRe
|
||||
func (s *Service) LogoutUser(_ context.Context, userID *wrapperspb.StringValue) (*emptypb.Empty, error) {
|
||||
s.log.WithField("UserID", userID.Value).Info("LogoutUser")
|
||||
|
||||
user, err := s.bridge.GetUser(userID.Value)
|
||||
if err != nil {
|
||||
if _, err := s.bridge.GetUserInfo(userID.Value); err != nil {
|
||||
return nil, status.Errorf(codes.NotFound, "user not found %v", userID.Value)
|
||||
}
|
||||
|
||||
go func() {
|
||||
defer s.panicHandler.HandlePanic()
|
||||
_ = user.Logout()
|
||||
|
||||
if err := s.bridge.LogoutUser(userID.Value); err != nil {
|
||||
logrus.WithError(err).Error("Failed to log user out")
|
||||
}
|
||||
}()
|
||||
|
||||
return &emptypb.Empty{}, nil
|
||||
|
||||
@ -21,7 +21,7 @@ import (
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/ProtonMail/proton-bridge/v2/internal/frontend/types"
|
||||
"github.com/ProtonMail/proton-bridge/v2/internal/users"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
@ -57,19 +57,19 @@ func getInitials(fullName string) string {
|
||||
return strings.ToUpper(initials)
|
||||
}
|
||||
|
||||
// grpcUserFromBridge converts a bridge user to a gRPC user.
|
||||
func grpcUserFromBridge(user types.User) *User {
|
||||
// grpcUserFromInfo converts a bridge user to a gRPC user.
|
||||
func grpcUserFromInfo(user users.UserInfo) *User {
|
||||
return &User{
|
||||
Id: user.ID(),
|
||||
Username: user.Username(),
|
||||
AvatarText: getInitials(user.Username()),
|
||||
LoggedIn: user.IsConnected(),
|
||||
SplitMode: !user.IsCombinedAddressMode(),
|
||||
Id: user.ID,
|
||||
Username: user.Username,
|
||||
AvatarText: getInitials(user.Username),
|
||||
LoggedIn: user.Connected,
|
||||
SplitMode: user.Mode == users.SplitMode,
|
||||
SetupGuideSeen: true, // users listed have already seen the setup guide.
|
||||
UsedBytes: user.UsedBytes(),
|
||||
TotalBytes: user.TotalBytes(),
|
||||
Password: user.GetBridgePassword(),
|
||||
Addresses: user.GetAddresses(),
|
||||
UsedBytes: user.UsedBytes,
|
||||
TotalBytes: user.TotalBytes,
|
||||
Password: user.Password,
|
||||
Addresses: user.Addresses,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -21,9 +21,9 @@ package types
|
||||
import (
|
||||
"crypto/tls"
|
||||
|
||||
"github.com/ProtonMail/proton-bridge/v2/internal/bridge"
|
||||
"github.com/ProtonMail/proton-bridge/v2/internal/config/settings"
|
||||
"github.com/ProtonMail/proton-bridge/v2/internal/updater"
|
||||
"github.com/ProtonMail/proton-bridge/v2/internal/users"
|
||||
"github.com/ProtonMail/proton-bridge/v2/pkg/pmapi"
|
||||
)
|
||||
|
||||
@ -45,39 +45,22 @@ type Updater interface {
|
||||
CanInstall(updater.VersionInfo) bool
|
||||
}
|
||||
|
||||
// UserManager is an interface of users needed by frontend.
|
||||
type UserManager interface {
|
||||
// Bridger is an interface of bridge needed by frontend.
|
||||
type Bridger interface {
|
||||
Login(username string, password []byte) (pmapi.Client, *pmapi.Auth, error)
|
||||
FinishLogin(client pmapi.Client, auth *pmapi.Auth, mailboxPassword []byte) (User, error)
|
||||
GetUsers() []User
|
||||
GetUser(query string) (User, error)
|
||||
FinishLogin(client pmapi.Client, auth *pmapi.Auth, mailboxPassword []byte) (string, error)
|
||||
|
||||
GetUserIDs() []string
|
||||
GetUserInfo(string) (users.UserInfo, error)
|
||||
LogoutUser(userID string) error
|
||||
DeleteUser(userID string, clearCache bool) error
|
||||
SetAddressMode(userID string, split users.AddressMode) error
|
||||
|
||||
ClearData() error
|
||||
ClearUsers() error
|
||||
FactoryReset()
|
||||
}
|
||||
|
||||
// User is an interface of user needed by frontend.
|
||||
type User interface {
|
||||
ID() string
|
||||
UsedBytes() int64
|
||||
TotalBytes() int64
|
||||
Username() string
|
||||
IsConnected() bool
|
||||
IsCombinedAddressMode() bool
|
||||
GetPrimaryAddress() string
|
||||
GetAddresses() []string
|
||||
GetBridgePassword() string
|
||||
SwitchAddressMode() error
|
||||
Logout() error
|
||||
}
|
||||
|
||||
// Bridger is an interface of bridge needed by frontend.
|
||||
type Bridger interface {
|
||||
UserManager
|
||||
|
||||
GetTLSConfig() (*tls.Config, error)
|
||||
|
||||
ProvideLogsPath() (string, error)
|
||||
GetLicenseFilePath() string
|
||||
GetDependencyLicensesLink() string
|
||||
@ -115,29 +98,3 @@ type Bridger interface {
|
||||
IsAllMailVisible() bool
|
||||
SetIsAllMailVisible(bool)
|
||||
}
|
||||
|
||||
type bridgeWrap struct {
|
||||
*bridge.Bridge
|
||||
}
|
||||
|
||||
// NewBridgeWrap wraps bridge struct into local bridgeWrap to implement local interface.
|
||||
// The problem is that Bridge returns the bridge package's User type.
|
||||
// Every method which returns User therefore has to be overridden to fulfill the interface.
|
||||
func NewBridgeWrap(bridge *bridge.Bridge) *bridgeWrap { //nolint:revive
|
||||
return &bridgeWrap{Bridge: bridge}
|
||||
}
|
||||
|
||||
func (b *bridgeWrap) FinishLogin(client pmapi.Client, auth *pmapi.Auth, mailboxPassword []byte) (User, error) {
|
||||
return b.Bridge.FinishLogin(client, auth, mailboxPassword)
|
||||
}
|
||||
|
||||
func (b *bridgeWrap) GetUsers() (users []User) {
|
||||
for _, user := range b.Bridge.GetUsers() {
|
||||
users = append(users, user)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (b *bridgeWrap) GetUser(query string) (User, error) {
|
||||
return b.Bridge.GetUser(query)
|
||||
}
|
||||
|
||||
@ -46,3 +46,38 @@ type StoreMaker interface {
|
||||
New(user store.BridgeUser) (*store.Store, error)
|
||||
Remove(userID string) error
|
||||
}
|
||||
|
||||
type UserInfo struct {
|
||||
ID string
|
||||
Username string
|
||||
Password string
|
||||
|
||||
Addresses []string
|
||||
Primary int
|
||||
|
||||
UsedBytes int64
|
||||
TotalBytes int64
|
||||
|
||||
Connected bool
|
||||
Mode AddressMode
|
||||
}
|
||||
|
||||
type AddressMode int
|
||||
|
||||
const (
|
||||
SplitMode AddressMode = iota
|
||||
CombinedMode
|
||||
)
|
||||
|
||||
func (mode AddressMode) String() string {
|
||||
switch mode {
|
||||
case SplitMode:
|
||||
return "split mode"
|
||||
|
||||
case CombinedMode:
|
||||
return "combined mode"
|
||||
|
||||
default:
|
||||
return "unknown mode"
|
||||
}
|
||||
}
|
||||
|
||||
@ -30,9 +30,11 @@ import (
|
||||
"github.com/ProtonMail/proton-bridge/v2/pkg/keychain"
|
||||
"github.com/ProtonMail/proton-bridge/v2/pkg/listener"
|
||||
"github.com/ProtonMail/proton-bridge/v2/pkg/pmapi"
|
||||
"github.com/bradenaw/juniper/xslices"
|
||||
"github.com/hashicorp/go-multierror"
|
||||
"github.com/pkg/errors"
|
||||
logrus "github.com/sirupsen/logrus"
|
||||
"golang.org/x/exp/slices"
|
||||
)
|
||||
|
||||
var (
|
||||
@ -215,10 +217,10 @@ func (u *Users) Login(username string, password []byte) (authClient pmapi.Client
|
||||
}
|
||||
|
||||
// FinishLogin finishes the login procedure and adds the user into the credentials store.
|
||||
func (u *Users) FinishLogin(client pmapi.Client, auth *pmapi.Auth, password []byte) (user *User, err error) { //nolint:funlen
|
||||
func (u *Users) FinishLogin(client pmapi.Client, auth *pmapi.Auth, password []byte) (userID string, err error) { //nolint:funlen
|
||||
apiUser, passphrase, err := getAPIUser(context.Background(), client, password)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return "", err
|
||||
}
|
||||
|
||||
if user, ok := u.hasUser(apiUser.ID); ok {
|
||||
@ -227,39 +229,39 @@ func (u *Users) FinishLogin(client pmapi.Client, auth *pmapi.Auth, password []by
|
||||
logrus.WithError(err).Warn("Failed to delete new auth session")
|
||||
}
|
||||
|
||||
return user, ErrUserAlreadyConnected
|
||||
return user.ID(), ErrUserAlreadyConnected
|
||||
}
|
||||
|
||||
// Update the user's credentials with the latest auth used to connect this user.
|
||||
if _, err := u.credStorer.UpdateToken(auth.UserID, auth.UID, auth.RefreshToken); err != nil {
|
||||
notifyKeychainRepair(u.events, err)
|
||||
return nil, errors.Wrap(err, "failed to load user credentials")
|
||||
return "", errors.Wrap(err, "failed to load user credentials")
|
||||
}
|
||||
|
||||
// Update the password in case the user changed it.
|
||||
creds, err := u.credStorer.UpdatePassword(apiUser.ID, passphrase)
|
||||
if err != nil {
|
||||
notifyKeychainRepair(u.events, err)
|
||||
return nil, errors.Wrap(err, "failed to update password of user in credentials store")
|
||||
return "", errors.Wrap(err, "failed to update password of user in credentials store")
|
||||
}
|
||||
|
||||
// will go and unlock cache if not already done
|
||||
if err := user.connect(client, creds); err != nil {
|
||||
return nil, errors.Wrap(err, "failed to reconnect existing user")
|
||||
return "", errors.Wrap(err, "failed to reconnect existing user")
|
||||
}
|
||||
|
||||
u.events.Emit(events.UserRefreshEvent, apiUser.ID)
|
||||
|
||||
return user, nil
|
||||
return user.ID(), nil
|
||||
}
|
||||
|
||||
if err := u.addNewUser(client, apiUser, auth, passphrase); err != nil {
|
||||
return nil, errors.Wrap(err, "failed to add new user")
|
||||
return "", errors.Wrap(err, "failed to add new user")
|
||||
}
|
||||
|
||||
u.events.Emit(events.UserRefreshEvent, apiUser.ID)
|
||||
|
||||
return u.GetUser(apiUser.ID)
|
||||
return apiUser.ID, nil
|
||||
}
|
||||
|
||||
// addNewUser adds a new user.
|
||||
@ -322,6 +324,16 @@ func (u *Users) GetUsers() []*User {
|
||||
return u.users
|
||||
}
|
||||
|
||||
// GetUserIDs returns IDs of all added users into keychain (even logged out users).
|
||||
func (u *Users) GetUserIDs() []string {
|
||||
u.lock.RLock()
|
||||
defer u.lock.RUnlock()
|
||||
|
||||
return xslices.Map(u.users, func(user *User) string {
|
||||
return user.ID()
|
||||
})
|
||||
}
|
||||
|
||||
// GetUser returns a user by `query` which is compared to users' ID, username or any attached e-mail address.
|
||||
func (u *Users) GetUser(query string) (*User, error) {
|
||||
u.crashBandicoot(query)
|
||||
@ -343,6 +355,44 @@ func (u *Users) GetUser(query string) (*User, error) {
|
||||
return nil, errors.New("user " + query + " not found")
|
||||
}
|
||||
|
||||
// GetUserInfo returns user about the user with the given ID.
|
||||
func (u *Users) GetUserInfo(userID string) (UserInfo, error) {
|
||||
u.lock.RLock()
|
||||
defer u.lock.RUnlock()
|
||||
|
||||
idx := slices.IndexFunc(u.users, func(user *User) bool {
|
||||
return user.userID == userID
|
||||
})
|
||||
if idx < 0 {
|
||||
return UserInfo{}, errors.New("no such user")
|
||||
}
|
||||
|
||||
user := u.users[idx]
|
||||
|
||||
var mode AddressMode
|
||||
|
||||
if user.IsCombinedAddressMode() {
|
||||
mode = CombinedMode
|
||||
} else {
|
||||
mode = SplitMode
|
||||
}
|
||||
|
||||
return UserInfo{
|
||||
ID: userID,
|
||||
Username: user.Username(),
|
||||
Password: user.GetBridgePassword(),
|
||||
|
||||
Addresses: user.GetAddresses(),
|
||||
Primary: slices.Index(user.GetAddresses(), user.GetPrimaryAddress()),
|
||||
|
||||
UsedBytes: user.UsedBytes(),
|
||||
TotalBytes: user.TotalBytes(),
|
||||
|
||||
Connected: user.IsConnected(),
|
||||
Mode: mode,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// ClearData closes all connections (to release db files and so on) and clears all data.
|
||||
func (u *Users) ClearData() error {
|
||||
var result error
|
||||
@ -364,6 +414,42 @@ func (u *Users) ClearData() error {
|
||||
return result
|
||||
}
|
||||
|
||||
func (u *Users) LogoutUser(userID string) error {
|
||||
u.lock.RLock()
|
||||
defer u.lock.RUnlock()
|
||||
|
||||
idx := slices.IndexFunc(u.users, func(user *User) bool {
|
||||
return user.userID == userID
|
||||
})
|
||||
if idx < 0 {
|
||||
return errors.New("no such user")
|
||||
}
|
||||
|
||||
return u.users[idx].Logout()
|
||||
}
|
||||
|
||||
func (u *Users) SetAddressMode(userID string, mode AddressMode) error {
|
||||
u.lock.RLock()
|
||||
defer u.lock.RUnlock()
|
||||
|
||||
idx := slices.IndexFunc(u.users, func(user *User) bool {
|
||||
return user.userID == userID
|
||||
})
|
||||
if idx < 0 {
|
||||
return errors.New("no such user")
|
||||
}
|
||||
|
||||
if mode == CombinedMode && u.users[idx].IsCombinedAddressMode() {
|
||||
return nil
|
||||
}
|
||||
|
||||
if mode == SplitMode && !u.users[idx].IsCombinedAddressMode() {
|
||||
return nil
|
||||
}
|
||||
|
||||
return u.users[idx].SwitchAddressMode()
|
||||
}
|
||||
|
||||
// DeleteUser deletes user completely; it logs user out from the API, stops any
|
||||
// active connection, deletes from credentials store and removes from the Bridge struct.
|
||||
func (u *Users) DeleteUser(userID string, clearStore bool) error {
|
||||
|
||||
@ -117,16 +117,16 @@ func checkUsersFinishLogin(t *testing.T, m mocks, auth *pmapi.Auth, mailboxPassw
|
||||
users := testNewUsers(t, m)
|
||||
defer cleanUpUsersData(users)
|
||||
|
||||
user, err := users.FinishLogin(m.pmapiClient, auth, mailboxPassword)
|
||||
userID, err := users.FinishLogin(m.pmapiClient, auth, mailboxPassword)
|
||||
|
||||
r.Equal(t, expectedErr, err)
|
||||
|
||||
if expectedUserID != "" {
|
||||
r.Equal(t, expectedUserID, user.ID())
|
||||
r.Equal(t, expectedUserID, userID)
|
||||
r.Equal(t, 1, len(users.users))
|
||||
r.Equal(t, expectedUserID, users.users[0].ID())
|
||||
} else {
|
||||
r.Equal(t, (*User)(nil), user)
|
||||
r.Equal(t, "", userID)
|
||||
r.Equal(t, 0, len(users.users))
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user