Other(refactor): Remove bridgeWrap from frontend interface
This commit is contained in:
parent
9786deef48
commit
6bbe2d0e00
|
@ -85,6 +85,8 @@ Proton Mail Bridge includes the following 3rd party software:
|
|||
* [grpc](https://google.golang.org/grpc) available under [license](https://github.com/grpc/grpc-go/blob/master/LICENSE)
|
||||
* [protobuf](https://google.golang.org/protobuf) available under [license](https://github.com/protocolbuffers/protobuf/blob/main/LICENSE)
|
||||
* [plist](https://howett.net/plist) available under [license](https://github.com/DHowett/go-plist/blob/main/LICENSE)
|
||||
* [juniper](https://github.com/bradenaw/juniper) available under [license](https://github.com/bradenaw/juniper/blob/master/LICENSE)
|
||||
* [exp](https://golang.org/x/exp) available under [license](https://cs.opensource.google/go/x/exp/+/master:LICENSE)
|
||||
* [go-mime](https://github.com/ProtonMail/go-mime) available under [license](https://github.com/ProtonMail/go-mime/blob/master/LICENSE)
|
||||
* [cascadia](https://github.com/andybalholm/cascadia) available under [license](https://github.com/andybalholm/cascadia/blob/master/LICENSE)
|
||||
* [antlr4](https://github.com/antlr/antlr4) available under [license](https://github.com/antlr/antlr4/blob/master/LICENSE)
|
||||
|
@ -109,7 +111,6 @@ Proton Mail Bridge includes the following 3rd party software:
|
|||
github.com/shurcooL/sanitized_anchor_name
|
||||
* [pflag](https://github.com/spf13/pflag) available under [license](https://github.com/spf13/pflag/blob/master/LICENSE)
|
||||
* [tagparser](https://github.com/vmihailenco/tagparser) available under [license](https://github.com/vmihailenco/tagparser/blob/master/LICENSE)
|
||||
* [xerrors](https://golang.org/x/xerrors) available under [license](https://cs.opensource.google/go/x/xerrors/+/master:LICENSE)
|
||||
* [genproto](https://google.golang.org/genproto)
|
||||
gopkg.in/yaml.v3
|
||||
* [docker-credential-helpers](https://github.com/ProtonMail/docker-credential-helpers) available under [license](https://github.com/ProtonMail/docker-credential-helpers/blob/master/LICENSE)
|
||||
|
|
10
go.mod
10
go.mod
|
@ -44,7 +44,7 @@ require (
|
|||
github.com/go-resty/resty/v2 v2.6.0
|
||||
github.com/godbus/dbus v4.1.0+incompatible
|
||||
github.com/golang/mock v1.4.4
|
||||
github.com/google/go-cmp v0.5.6
|
||||
github.com/google/go-cmp v0.5.8
|
||||
github.com/google/uuid v1.1.2
|
||||
github.com/gopherjs/gopherjs v0.0.0-20190411002643-bd77b112433e // indirect
|
||||
github.com/hashicorp/go-multierror v1.1.0
|
||||
|
@ -67,13 +67,18 @@ require (
|
|||
go.etcd.io/bbolt v1.3.6
|
||||
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d // indirect
|
||||
golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4
|
||||
golang.org/x/sys v0.0.0-20220627191245-f75cf1eec38b
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f
|
||||
golang.org/x/text v0.3.7
|
||||
google.golang.org/grpc v1.46.2
|
||||
google.golang.org/protobuf v1.28.0
|
||||
howett.net/plist v1.0.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/bradenaw/juniper v0.7.0
|
||||
golang.org/x/exp v0.0.0-20220823124025-807a23277127
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/ProtonMail/go-mime v0.0.0-20220302105931-303f85f7fe0f // indirect
|
||||
github.com/andybalholm/cascadia v1.1.0 // indirect
|
||||
|
@ -99,7 +104,6 @@ require (
|
|||
github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/vmihailenco/tagparser v0.1.2 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
|
||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 // indirect
|
||||
)
|
||||
|
|
12
go.sum
12
go.sum
|
@ -75,6 +75,8 @@ github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24
|
|||
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
||||
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
||||
github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=
|
||||
github.com/bradenaw/juniper v0.7.0 h1:8JaJpY2Sm+EheEows6ZsS7s8ZM86Fa3yfaq5xXQH4SI=
|
||||
github.com/bradenaw/juniper v0.7.0/go.mod h1:Z2B7aJlQ7xbfWsnMLROj5t/5FQ94/MkIdKC30J4WvzI=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
||||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
|
@ -221,8 +223,9 @@ github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
|
|||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
|
||||
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
|
||||
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||
|
@ -500,6 +503,8 @@ golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxT
|
|||
golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9tZUw9qufEGTyX1+7lmHxV5q5G4=
|
||||
golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
|
||||
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
|
||||
golang.org/x/exp v0.0.0-20220823124025-807a23277127 h1:S4NrSKDfihhl3+4jSTgwoIevKxX9p7Iv9x++OEIptDo=
|
||||
golang.org/x/exp v0.0.0-20220823124025-807a23277127/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE=
|
||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
|
@ -593,8 +598,8 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc
|
|||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220627191245-f75cf1eec38b h1:2n253B2r0pYSmEV+UNCQoPfU/FiaizQEK5Gu4Bq4JE8=
|
||||
golang.org/x/sys v0.0.0-20220627191245-f75cf1eec38b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f h1:v4INt8xihDGvnrfjMDVXGxw9wrfxYyCjk0KbXjhR55s=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
|
@ -632,7 +637,6 @@ golang.org/x/tools v0.0.0-20200117012304-6edc0a871e69/go.mod h1:TB2adYChydJhpapK
|
|||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
|
||||
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -52,12 +52,14 @@ func (ctx *TestContext) LoginUser(username string, password, mailboxPassword []b
|
|||
}
|
||||
}
|
||||
|
||||
user, err := ctx.users.FinishLogin(client, auth, mailboxPassword)
|
||||
userID, err := ctx.users.FinishLogin(client, auth, mailboxPassword)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to finish login")
|
||||
}
|
||||
|
||||
ctx.addCleanupChecked(user.Logout, "Logging out user")
|
||||
ctx.addCleanupChecked(func() error {
|
||||
return ctx.bridge.LogoutUser(userID)
|
||||
}, "Logging out user")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@ -73,12 +75,14 @@ func (ctx *TestContext) FinishLogin(client pmapi.Client, mailboxPassword []byte)
|
|||
return errors.New("cannot get current auth tokens from client")
|
||||
}
|
||||
|
||||
user, err := ctx.users.FinishLogin(client, c.GetCurrentAuth(), mailboxPassword)
|
||||
userID, err := ctx.users.FinishLogin(client, c.GetCurrentAuth(), mailboxPassword)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to finish login")
|
||||
}
|
||||
|
||||
ctx.addCleanupChecked(user.Logout, "Logging out user")
|
||||
ctx.addCleanupChecked(func() error {
|
||||
return ctx.bridge.LogoutUser(userID)
|
||||
}, "Logging out user")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue