mirror of
https://github.com/ProtonMail/proton-bridge.git
synced 2025-12-19 16:46:55 +00:00
GODT-1166: Reduce the number of auth for live test
- Changed: Do not reauth controller clients. - Changed: Verbosisty is set only once before run - Changed: AddUser takes TestAccount as argument - Added: Setup/clean up before/after test run - Added: Access to the current refresh token from pmapi.Client interface. - Added: Context function to add test a user to bridge without login, just call users.FinishLogin. - Added: PMAPIController.GetAuthClient returns authenticated client for username. - Added: Persistent clients does not loggout after every scenario. - Changed: Disabled no-internet tests.
This commit is contained in:
@ -21,45 +21,36 @@ import (
|
||||
"net/http"
|
||||
"sync"
|
||||
|
||||
"github.com/ProtonMail/proton-bridge/internal/constants"
|
||||
"github.com/ProtonMail/proton-bridge/pkg/pmapi"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// Controller implements PMAPIController interface for specified endpoint.
|
||||
type Controller struct {
|
||||
log *logrus.Entry
|
||||
// Internal states.
|
||||
lock *sync.RWMutex
|
||||
calls []*fakeCall
|
||||
pmapiByUsername map[string]pmapi.Client
|
||||
messageIDsByUsername map[string][]string
|
||||
clientManager pmapi.Manager
|
||||
|
||||
// State controlled by test.
|
||||
noInternetConnection bool
|
||||
}
|
||||
|
||||
func NewController(app string) (*Controller, pmapi.Manager) {
|
||||
cm := pmapi.New(pmapi.NewConfig(getAppVersionName(app), constants.Version))
|
||||
func NewController(_ string) (*Controller, pmapi.Manager) {
|
||||
controller := &Controller{
|
||||
log: logrus.WithField("pkg", "live-controller"),
|
||||
lock: &sync.RWMutex{},
|
||||
calls: []*fakeCall{},
|
||||
pmapiByUsername: map[string]pmapi.Client{},
|
||||
messageIDsByUsername: map[string][]string{},
|
||||
clientManager: cm,
|
||||
|
||||
noInternetConnection: false,
|
||||
}
|
||||
|
||||
cm.SetTransport(&fakeTransport{
|
||||
persistentClients.manager.SetTransport(&fakeTransport{
|
||||
ctl: controller,
|
||||
transport: http.DefaultTransport,
|
||||
})
|
||||
|
||||
return controller, cm
|
||||
}
|
||||
|
||||
func getAppVersionName(app string) string {
|
||||
if app == "ie" {
|
||||
return "importExport"
|
||||
}
|
||||
return app
|
||||
return controller, persistentClients.manager
|
||||
}
|
||||
|
||||
@ -37,9 +37,9 @@ var systemLabelNameToID = map[string]string{ //nolint[gochecknoglobals]
|
||||
}
|
||||
|
||||
func (ctl *Controller) AddUserLabel(username string, label *pmapi.Label) error {
|
||||
client, ok := ctl.pmapiByUsername[username]
|
||||
if !ok {
|
||||
return fmt.Errorf("user %s does not exist", username)
|
||||
client, err := getPersistentClient(username)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
label.Exclusive = getLabelExclusive(label.Name)
|
||||
@ -68,9 +68,9 @@ func (ctl *Controller) getLabelID(username, labelName string) (string, error) {
|
||||
return labelID, nil
|
||||
}
|
||||
|
||||
client, ok := ctl.pmapiByUsername[username]
|
||||
if !ok {
|
||||
return "", fmt.Errorf("user %s does not exist", username)
|
||||
client, err := getPersistentClient(username)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
labels, err := client.ListLabels(context.Background())
|
||||
|
||||
@ -19,7 +19,6 @@ package liveapi
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
messageUtils "github.com/ProtonMail/proton-bridge/pkg/message"
|
||||
"github.com/ProtonMail/proton-bridge/pkg/pmapi"
|
||||
@ -31,9 +30,9 @@ func (ctl *Controller) AddUserMessage(username string, message *pmapi.Message) (
|
||||
return "", errors.New("add user messages with attachments is not implemented for live")
|
||||
}
|
||||
|
||||
client, ok := ctl.pmapiByUsername[username]
|
||||
if !ok {
|
||||
return "", fmt.Errorf("user %s does not exist", username)
|
||||
client, err := getPersistentClient(username)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if message.Flags == 0 {
|
||||
@ -75,9 +74,9 @@ func (ctl *Controller) AddUserMessage(username string, message *pmapi.Message) (
|
||||
}
|
||||
|
||||
func (ctl *Controller) GetMessages(username, labelID string) ([]*pmapi.Message, error) {
|
||||
client, ok := ctl.pmapiByUsername[username]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("user %s does not exist", username)
|
||||
client, err := getPersistentClient(username)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
page := 0
|
||||
|
||||
131
test/liveapi/persistent_clients.go
Normal file
131
test/liveapi/persistent_clients.go
Normal file
@ -0,0 +1,131 @@
|
||||
// Copyright (c) 2021 Proton Technologies AG
|
||||
//
|
||||
// This file is part of ProtonMail Bridge.Bridge.
|
||||
//
|
||||
// ProtonMail Bridge is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// ProtonMail Bridge is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with ProtonMail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package liveapi
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"os"
|
||||
|
||||
"github.com/ProtonMail/proton-bridge/internal/constants"
|
||||
"github.com/ProtonMail/proton-bridge/pkg/pmapi"
|
||||
"github.com/ProtonMail/proton-bridge/pkg/srp"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// persistentClients keeps authenticated clients for tests.
|
||||
//
|
||||
// We need to reduce the number of authentication done by live tests.
|
||||
// Before every *scenario* we are creating and authenticating new client.
|
||||
// This is not necessary for controller purposes. We can reuse the same clients
|
||||
// for all tests.
|
||||
//
|
||||
//nolint[gochecknoglobals]
|
||||
var persistentClients = struct {
|
||||
manager pmapi.Manager
|
||||
byName map[string]pmapi.Client
|
||||
saltByName map[string]string
|
||||
}{}
|
||||
|
||||
type persistentClient struct {
|
||||
pmapi.Client
|
||||
username string
|
||||
}
|
||||
|
||||
// AuthDelete is noop. All sessions will be closed in CleanupPersistentClients.
|
||||
func (pc *persistentClient) AuthDelete(_ context.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// AuthSalt returns cached string. Otherwise after some time there is an error:
|
||||
//
|
||||
// Access token does not have sufficient scope
|
||||
//
|
||||
// while all other routes works normally. Need to confirm with Aron that this
|
||||
// is expected behaviour.
|
||||
func (pc *persistentClient) AuthSalt(_ context.Context) (string, error) {
|
||||
return persistentClients.saltByName[pc.username], nil
|
||||
}
|
||||
|
||||
func SetupPersistentClients() {
|
||||
app := os.Getenv("TEST_APP")
|
||||
|
||||
persistentClients.manager = pmapi.New(pmapi.NewConfig(getAppVersionName(app), constants.Version))
|
||||
persistentClients.manager.SetLogging(logrus.WithField("pkg", "liveapi"), logrus.GetLevel() == logrus.TraceLevel)
|
||||
|
||||
persistentClients.byName = map[string]pmapi.Client{}
|
||||
persistentClients.saltByName = map[string]string{}
|
||||
}
|
||||
|
||||
func getAppVersionName(app string) string {
|
||||
if app == "ie" {
|
||||
return "importExport"
|
||||
}
|
||||
return app
|
||||
}
|
||||
|
||||
func CleanupPersistentClients() {
|
||||
for username, client := range persistentClients.byName {
|
||||
if err := client.AuthDelete(context.Background()); err != nil {
|
||||
logrus.WithError(err).
|
||||
WithField("username", username).
|
||||
Error("Failed to logout persistent client")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func addPersistentClient(username string, password, mailboxPassword []byte) (pmapi.Client, error) {
|
||||
if cl, ok := persistentClients.byName[username]; ok {
|
||||
return cl, nil
|
||||
}
|
||||
|
||||
srp.RandReader = rand.New(rand.NewSource(42)) //nolint[gosec] It is OK to use weaker random number generator here
|
||||
|
||||
client, _, err := persistentClients.manager.NewClientWithLogin(context.Background(), username, password)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to create new persistent client")
|
||||
}
|
||||
|
||||
salt, err := client.AuthSalt(context.Background())
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "persistent client: failed to get salt")
|
||||
}
|
||||
|
||||
hashedMboxPass, err := pmapi.HashMailboxPassword(mailboxPassword, salt)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "persistent client: failed to hash mailbox password")
|
||||
}
|
||||
|
||||
if err := client.Unlock(context.Background(), hashedMboxPass); err != nil {
|
||||
return nil, errors.Wrap(err, "persistent client: failed to unlock user")
|
||||
}
|
||||
|
||||
persistentClients.byName[username] = client
|
||||
persistentClients.saltByName[username] = salt
|
||||
return client, nil
|
||||
}
|
||||
|
||||
func getPersistentClient(username string) (pmapi.Client, error) {
|
||||
v, ok := persistentClients.byName[username]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("user %s does not exist", username)
|
||||
}
|
||||
return &persistentClient{v, username}, nil
|
||||
}
|
||||
@ -21,43 +21,42 @@ import (
|
||||
"context"
|
||||
|
||||
"github.com/ProtonMail/proton-bridge/pkg/pmapi"
|
||||
"github.com/ProtonMail/proton-bridge/test/accounts"
|
||||
"github.com/cucumber/godog"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func (ctl *Controller) AddUser(user *pmapi.User, addresses *pmapi.AddressList, password []byte, twoFAEnabled bool) error {
|
||||
if twoFAEnabled {
|
||||
func (ctl *Controller) AddUser(account *accounts.TestAccount) error {
|
||||
if account.IsTwoFAEnabled() {
|
||||
return godog.ErrPending
|
||||
}
|
||||
|
||||
client, _, err := ctl.clientManager.NewClientWithLogin(context.Background(), user.Name, password)
|
||||
client, err := addPersistentClient(account.User().Name, account.Password(), account.MailboxPassword())
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to create new client")
|
||||
return errors.Wrap(err, "failed to add persistent client")
|
||||
}
|
||||
|
||||
salt, err := client.AuthSalt(context.Background())
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to get salt")
|
||||
}
|
||||
|
||||
mailboxPassword, err := pmapi.HashMailboxPassword(password, salt)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to hash mailbox password")
|
||||
}
|
||||
|
||||
if err := client.Unlock(context.Background(), mailboxPassword); err != nil {
|
||||
return errors.Wrap(err, "failed to unlock user")
|
||||
}
|
||||
|
||||
if err := cleanup(client, addresses); err != nil {
|
||||
if err := cleanup(client, account.Addresses()); err != nil {
|
||||
return errors.Wrap(err, "failed to clean user")
|
||||
}
|
||||
|
||||
ctl.pmapiByUsername[user.Name] = client
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ctl *Controller) ReorderAddresses(user *pmapi.User, addressIDs []string) error {
|
||||
return ctl.pmapiByUsername[user.Name].ReorderAddresses(context.Background(), addressIDs)
|
||||
client, err := getPersistentClient(user.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return client.ReorderAddresses(context.Background(), addressIDs)
|
||||
}
|
||||
|
||||
func (ctl *Controller) GetAuthClient(username string) pmapi.Client {
|
||||
client, err := getPersistentClient(username)
|
||||
if err != nil {
|
||||
ctl.log.WithError(err).
|
||||
WithField("username", username).
|
||||
Fatal("Cannot get authenticated client")
|
||||
}
|
||||
return client
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user