mirror of
https://github.com/ProtonMail/proton-bridge.git
synced 2025-12-10 20:56:51 +00:00
GODT-1815: Start with missing gluon files
This commit is contained in:
@ -50,9 +50,8 @@ type Bridge struct {
|
|||||||
imapListener net.Listener
|
imapListener net.Listener
|
||||||
|
|
||||||
// smtpServer is the bridge's SMTP server.
|
// smtpServer is the bridge's SMTP server.
|
||||||
smtpServer *smtp.Server
|
smtpServer *smtp.Server
|
||||||
smtpBackend *smtpBackend
|
smtpBackend *smtpBackend
|
||||||
smtpListener net.Listener
|
|
||||||
|
|
||||||
// updater is the bridge's updater.
|
// updater is the bridge's updater.
|
||||||
updater Updater
|
updater Updater
|
||||||
@ -107,7 +106,13 @@ func New(
|
|||||||
return nil, fmt.Errorf("failed to load TLS config: %w", err)
|
return nil, fmt.Errorf("failed to load TLS config: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
imapServer, err := newIMAPServer(vault.GetGluonDir(), curVersion, tlsConfig)
|
// TODO: Handle case that the gluon directory is missing!
|
||||||
|
gluonDir, err := getGluonDir(vault)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to get Gluon directory: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
imapServer, err := newIMAPServer(gluonDir, curVersion, tlsConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to create IMAP server: %w", err)
|
return nil, fmt.Errorf("failed to create IMAP server: %w", err)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,6 +2,7 @@ package bridge_test
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/Masterminds/semver/v3"
|
"github.com/Masterminds/semver/v3"
|
||||||
@ -282,6 +283,31 @@ func TestBridge_BadVaultKey(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestBridge_MissingGluonDir(t *testing.T) {
|
||||||
|
withEnv(t, func(ctx context.Context, s *server.Server, dialer *bridge.TestDialer, locator bridge.Locator, vaultKey []byte) {
|
||||||
|
var gluonDir string
|
||||||
|
|
||||||
|
withBridge(t, ctx, s.GetHostURL(), dialer, locator, vaultKey, func(bridge *bridge.Bridge, mocks *bridge.Mocks) {
|
||||||
|
_, err := bridge.LoginUser(context.Background(), username, password, nil, nil)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// Move the gluon dir.
|
||||||
|
bridge.SetGluonDir(ctx, t.TempDir())
|
||||||
|
|
||||||
|
// Get the gluon dir.
|
||||||
|
gluonDir = bridge.GetGluonDir()
|
||||||
|
})
|
||||||
|
|
||||||
|
// The user removes the gluon dir while bridge is not running.
|
||||||
|
require.NoError(t, os.RemoveAll(gluonDir))
|
||||||
|
|
||||||
|
// Bridge starts but can't find the gluon dir; there should be no error.
|
||||||
|
withBridge(t, ctx, s.GetHostURL(), dialer, locator, vaultKey, func(bridge *bridge.Bridge, mocks *bridge.Mocks) {
|
||||||
|
// ...
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// withEnv creates the full test environment and runs the tests.
|
// withEnv creates the full test environment and runs the tests.
|
||||||
func withEnv(t *testing.T, tests func(ctx context.Context, server *server.Server, dialer *bridge.TestDialer, locator bridge.Locator, vaultKey []byte)) {
|
func withEnv(t *testing.T, tests func(ctx context.Context, server *server.Server, dialer *bridge.TestDialer, locator bridge.Locator, vaultKey []byte)) {
|
||||||
// Create test API.
|
// Create test API.
|
||||||
@ -305,7 +331,7 @@ func withEnv(t *testing.T, tests func(ctx context.Context, server *server.Server
|
|||||||
ctx,
|
ctx,
|
||||||
server,
|
server,
|
||||||
bridge.NewTestDialer(),
|
bridge.NewTestDialer(),
|
||||||
locations.New(bridge.NewTestLocationsProvider(t), "config-name"),
|
locations.New(bridge.NewTestLocationsProvider(t.TempDir()), "config-name"),
|
||||||
vaultKey,
|
vaultKey,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,12 +3,16 @@ package bridge
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io/fs"
|
||||||
|
"os"
|
||||||
|
|
||||||
"github.com/Masterminds/semver/v3"
|
"github.com/Masterminds/semver/v3"
|
||||||
"github.com/ProtonMail/gluon"
|
"github.com/ProtonMail/gluon"
|
||||||
imapEvents "github.com/ProtonMail/gluon/events"
|
imapEvents "github.com/ProtonMail/gluon/events"
|
||||||
"github.com/ProtonMail/proton-bridge/v2/internal/constants"
|
"github.com/ProtonMail/proton-bridge/v2/internal/constants"
|
||||||
|
"github.com/ProtonMail/proton-bridge/v2/internal/vault"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -17,38 +21,6 @@ const (
|
|||||||
defaultClientVersion = "0.0.1"
|
defaultClientVersion = "0.0.1"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (bridge *Bridge) GetIMAPPort() int {
|
|
||||||
return bridge.vault.GetIMAPPort()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (bridge *Bridge) SetIMAPPort(newPort int) error {
|
|
||||||
if newPort == bridge.vault.GetIMAPPort() {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := bridge.vault.SetIMAPPort(newPort); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return bridge.restartIMAP(context.Background())
|
|
||||||
}
|
|
||||||
|
|
||||||
func (bridge *Bridge) GetIMAPSSL() bool {
|
|
||||||
return bridge.vault.GetIMAPSSL()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (bridge *Bridge) SetIMAPSSL(newSSL bool) error {
|
|
||||||
if newSSL == bridge.vault.GetIMAPSSL() {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := bridge.vault.SetIMAPSSL(newSSL); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return bridge.restartIMAP(context.Background())
|
|
||||||
}
|
|
||||||
|
|
||||||
func (bridge *Bridge) serveIMAP() error {
|
func (bridge *Bridge) serveIMAP() error {
|
||||||
imapListener, err := newListener(bridge.vault.GetIMAPPort(), bridge.vault.GetIMAPSSL(), bridge.tlsConfig)
|
imapListener, err := newListener(bridge.vault.GetIMAPPort(), bridge.vault.GetIMAPSSL(), bridge.tlsConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -83,15 +55,34 @@ func (bridge *Bridge) closeIMAP(ctx context.Context) error {
|
|||||||
func (bridge *Bridge) handleIMAPEvent(event imapEvents.Event) {
|
func (bridge *Bridge) handleIMAPEvent(event imapEvents.Event) {
|
||||||
switch event := event.(type) {
|
switch event := event.(type) {
|
||||||
case imapEvents.SessionAdded:
|
case imapEvents.SessionAdded:
|
||||||
if !bridge.identifier.HasClient() {
|
if bridge.identifier.HasClient() {
|
||||||
bridge.identifier.SetClient(defaultClientName, defaultClientVersion)
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bridge.identifier.SetClient(defaultClientName, defaultClientVersion)
|
||||||
|
|
||||||
case imapEvents.IMAPID:
|
case imapEvents.IMAPID:
|
||||||
bridge.identifier.SetClient(event.IMAPID.Name, event.IMAPID.Version)
|
bridge.identifier.SetClient(event.IMAPID.Name, event.IMAPID.Version)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getGluonDir(encVault *vault.Vault) (string, error) {
|
||||||
|
empty, err := isEmpty(encVault.GetGluonDir())
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("failed to check if gluon dir is empty: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if empty {
|
||||||
|
if err := encVault.ForUser(func(user *vault.User) error {
|
||||||
|
return user.SetSync(false)
|
||||||
|
}); err != nil {
|
||||||
|
return "", fmt.Errorf("failed to reset user sync status: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return encVault.GetGluonDir(), nil
|
||||||
|
}
|
||||||
|
|
||||||
func newIMAPServer(gluonDir string, version *semver.Version, tlsConfig *tls.Config) (*gluon.Server, error) {
|
func newIMAPServer(gluonDir string, version *semver.Version, tlsConfig *tls.Config) (*gluon.Server, error) {
|
||||||
imapServer, err := gluon.New(
|
imapServer, err := gluon.New(
|
||||||
gluon.WithTLS(tlsConfig),
|
gluon.WithTLS(tlsConfig),
|
||||||
@ -115,3 +106,20 @@ func newIMAPServer(gluonDir string, version *semver.Version, tlsConfig *tls.Conf
|
|||||||
|
|
||||||
return imapServer, nil
|
return imapServer, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func isEmpty(dir string) (bool, error) {
|
||||||
|
if _, err := os.Stat(dir); err != nil {
|
||||||
|
if !errors.Is(err, fs.ErrNotExist) {
|
||||||
|
return false, fmt.Errorf("failed to stat %s: %w", dir, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
entries, err := os.ReadDir(dir)
|
||||||
|
if err != nil {
|
||||||
|
return false, fmt.Errorf("failed to read dir %s: %w", dir, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return len(entries) == 0, nil
|
||||||
|
}
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import (
|
|||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"errors"
|
"errors"
|
||||||
"net"
|
"net"
|
||||||
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/Masterminds/semver/v3"
|
"github.com/Masterminds/semver/v3"
|
||||||
@ -75,10 +76,20 @@ type TestLocationsProvider struct {
|
|||||||
config, cache string
|
config, cache string
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewTestLocationsProvider(tb testing.TB) *TestLocationsProvider {
|
func NewTestLocationsProvider(dir string) *TestLocationsProvider {
|
||||||
|
config, err := os.MkdirTemp(dir, "config")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
cache, err := os.MkdirTemp(dir, "cache")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
return &TestLocationsProvider{
|
return &TestLocationsProvider{
|
||||||
config: tb.TempDir(),
|
config: config,
|
||||||
cache: tb.TempDir(),
|
cache: cache,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -26,6 +26,70 @@ func (bridge *Bridge) SetKeychainApp(helper string) error {
|
|||||||
return vault.SetHelper(vaultDir, helper)
|
return vault.SetHelper(vaultDir, helper)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (bridge *Bridge) GetIMAPPort() int {
|
||||||
|
return bridge.vault.GetIMAPPort()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bridge *Bridge) SetIMAPPort(newPort int) error {
|
||||||
|
if newPort == bridge.vault.GetIMAPPort() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := bridge.vault.SetIMAPPort(newPort); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return bridge.restartIMAP(context.Background())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bridge *Bridge) GetIMAPSSL() bool {
|
||||||
|
return bridge.vault.GetIMAPSSL()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bridge *Bridge) SetIMAPSSL(newSSL bool) error {
|
||||||
|
if newSSL == bridge.vault.GetIMAPSSL() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := bridge.vault.SetIMAPSSL(newSSL); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return bridge.restartIMAP(context.Background())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bridge *Bridge) GetSMTPPort() int {
|
||||||
|
return bridge.vault.GetSMTPPort()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bridge *Bridge) SetSMTPPort(newPort int) error {
|
||||||
|
if newPort == bridge.vault.GetSMTPPort() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := bridge.vault.SetSMTPPort(newPort); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return bridge.restartSMTP()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bridge *Bridge) GetSMTPSSL() bool {
|
||||||
|
return bridge.vault.GetSMTPSSL()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bridge *Bridge) SetSMTPSSL(newSSL bool) error {
|
||||||
|
if newSSL == bridge.vault.GetSMTPSSL() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := bridge.vault.SetSMTPSSL(newSSL); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return bridge.restartSMTP()
|
||||||
|
}
|
||||||
|
|
||||||
func (bridge *Bridge) GetGluonDir() string {
|
func (bridge *Bridge) GetGluonDir() string {
|
||||||
return bridge.vault.GetGluonDir()
|
return bridge.vault.GetGluonDir()
|
||||||
}
|
}
|
||||||
|
|||||||
@ -10,48 +10,14 @@ import (
|
|||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (bridge *Bridge) GetSMTPPort() int {
|
|
||||||
return bridge.vault.GetSMTPPort()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (bridge *Bridge) SetSMTPPort(newPort int) error {
|
|
||||||
if newPort == bridge.vault.GetSMTPPort() {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := bridge.vault.SetSMTPPort(newPort); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return bridge.restartSMTP()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (bridge *Bridge) GetSMTPSSL() bool {
|
|
||||||
return bridge.vault.GetSMTPSSL()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (bridge *Bridge) SetSMTPSSL(newSSL bool) error {
|
|
||||||
if newSSL == bridge.vault.GetSMTPSSL() {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := bridge.vault.SetSMTPSSL(newSSL); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return bridge.restartSMTP()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (bridge *Bridge) serveSMTP() error {
|
func (bridge *Bridge) serveSMTP() error {
|
||||||
smtpListener, err := newListener(bridge.vault.GetSMTPPort(), bridge.vault.GetSMTPSSL(), bridge.tlsConfig)
|
smtpListener, err := newListener(bridge.vault.GetSMTPPort(), bridge.vault.GetSMTPSSL(), bridge.tlsConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to create SMTP listener: %w", err)
|
return fmt.Errorf("failed to create SMTP listener: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
bridge.smtpListener = smtpListener
|
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
if err := bridge.smtpServer.Serve(bridge.smtpListener); err != nil {
|
if err := bridge.smtpServer.Serve(smtpListener); err != nil {
|
||||||
logrus.WithError(err).Error("SMTP server stopped")
|
logrus.WithError(err).Error("SMTP server stopped")
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|||||||
@ -308,7 +308,7 @@ func (bridge *Bridge) addNewUser(
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := vaultUser.UpdateGluonData(gluonID, gluonKey); err != nil {
|
if err := vaultUser.SetGluonAuth(gluonID, gluonKey); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -336,11 +336,11 @@ func (bridge *Bridge) addExistingUser(
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := vaultUser.UpdateAuth(authUID, authRef); err != nil {
|
if err := vaultUser.SetAuth(authUID, authRef); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := vaultUser.UpdateKeyPass(saltedKeyPass); err != nil {
|
if err := vaultUser.SetKeyPass(saltedKeyPass); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -34,7 +34,7 @@ func (user *User) sync(ctx context.Context) error {
|
|||||||
UserID: user.ID(),
|
UserID: user.ID(),
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := user.vault.UpdateSync(true); err != nil {
|
if err := user.vault.SetSync(true); err != nil {
|
||||||
return fmt.Errorf("failed to update sync status: %w", err)
|
return fmt.Errorf("failed to update sync status: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -56,7 +56,7 @@ func New(
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := vault.UpdateEventID(eventID); err != nil {
|
if err := vault.SetEventID(eventID); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -85,7 +85,7 @@ func New(
|
|||||||
// When we receive an auth object, we update it in the store.
|
// When we receive an auth object, we update it in the store.
|
||||||
// This will be used to authorize the user on the next run.
|
// This will be used to authorize the user on the next run.
|
||||||
client.AddAuthHandler(func(auth liteapi.Auth) {
|
client.AddAuthHandler(func(auth liteapi.Auth) {
|
||||||
if err := user.vault.UpdateAuth(auth.UID, auth.RefreshToken); err != nil {
|
if err := user.vault.SetAuth(auth.UID, auth.RefreshToken); err != nil {
|
||||||
logrus.WithError(err).Error("Failed to update auth")
|
logrus.WithError(err).Error("Failed to update auth")
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -104,7 +104,7 @@ func New(
|
|||||||
if err := user.handleAPIEvent(event); err != nil {
|
if err := user.handleAPIEvent(event); err != nil {
|
||||||
logrus.WithError(err).Error("Failed to handle event")
|
logrus.WithError(err).Error("Failed to handle event")
|
||||||
} else {
|
} else {
|
||||||
if err := user.vault.UpdateEventID(event.EventID); err != nil {
|
if err := user.vault.SetEventID(event.EventID); err != nil {
|
||||||
logrus.WithError(err).Error("Failed to update event ID")
|
logrus.WithError(err).Error("Failed to update event ID")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -45,37 +45,37 @@ func (user *User) HasSync() bool {
|
|||||||
return user.vault.getUser(user.userID).HasSync
|
return user.vault.getUser(user.userID).HasSync
|
||||||
}
|
}
|
||||||
|
|
||||||
func (user *User) UpdateKeyPass(keyPass []byte) error {
|
func (user *User) SetKeyPass(keyPass []byte) error {
|
||||||
return user.vault.modUser(user.userID, func(data *UserData) {
|
return user.vault.modUser(user.userID, func(data *UserData) {
|
||||||
data.KeyPass = keyPass
|
data.KeyPass = keyPass
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateAuth updates the auth secrets for the given user.
|
// SetAuth updates the auth secrets for the given user.
|
||||||
func (user *User) UpdateAuth(authUID, authRef string) error {
|
func (user *User) SetAuth(authUID, authRef string) error {
|
||||||
return user.vault.modUser(user.userID, func(data *UserData) {
|
return user.vault.modUser(user.userID, func(data *UserData) {
|
||||||
data.AuthUID = authUID
|
data.AuthUID = authUID
|
||||||
data.AuthRef = authRef
|
data.AuthRef = authRef
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateGluonData updates the gluon ID and key for the given user.
|
// SetGluonAuth updates the gluon ID and key for the given user.
|
||||||
func (user *User) UpdateGluonData(gluonID string, gluonKey []byte) error {
|
func (user *User) SetGluonAuth(gluonID string, gluonKey []byte) error {
|
||||||
return user.vault.modUser(user.userID, func(data *UserData) {
|
return user.vault.modUser(user.userID, func(data *UserData) {
|
||||||
data.GluonID = gluonID
|
data.GluonID = gluonID
|
||||||
data.GluonKey = gluonKey
|
data.GluonKey = gluonKey
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateEventID updates the event ID for the given user.
|
// SetEventID updates the event ID for the given user.
|
||||||
func (user *User) UpdateEventID(eventID string) error {
|
func (user *User) SetEventID(eventID string) error {
|
||||||
return user.vault.modUser(user.userID, func(data *UserData) {
|
return user.vault.modUser(user.userID, func(data *UserData) {
|
||||||
data.EventID = eventID
|
data.EventID = eventID
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateSync updates the sync state for the given user.
|
// SetSync updates the sync state for the given user.
|
||||||
func (user *User) UpdateSync(hasSync bool) error {
|
func (user *User) SetSync(hasSync bool) error {
|
||||||
return user.vault.modUser(user.userID, func(data *UserData) {
|
return user.vault.modUser(user.userID, func(data *UserData) {
|
||||||
data.HasSync = hasSync
|
data.HasSync = hasSync
|
||||||
})
|
})
|
||||||
|
|||||||
@ -24,16 +24,16 @@ func TestUser(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
// Set event IDs for user 1 and 2.
|
// Set event IDs for user 1 and 2.
|
||||||
require.NoError(t, user1.UpdateEventID("eventID1"))
|
require.NoError(t, user1.SetEventID("eventID1"))
|
||||||
require.NoError(t, user2.UpdateEventID("eventID2"))
|
require.NoError(t, user2.SetEventID("eventID2"))
|
||||||
|
|
||||||
// Set sync state for user 1 and 2.
|
// Set sync state for user 1 and 2.
|
||||||
require.NoError(t, user1.UpdateSync(true))
|
require.NoError(t, user1.SetSync(true))
|
||||||
require.NoError(t, user2.UpdateSync(false))
|
require.NoError(t, user2.SetSync(false))
|
||||||
|
|
||||||
// Set gluon data for user 1 and 2.
|
// Set gluon data for user 1 and 2.
|
||||||
require.NoError(t, user1.UpdateGluonData("gluonID1", []byte("gluonKey1")))
|
require.NoError(t, user1.SetGluonAuth("gluonID1", []byte("gluonKey1")))
|
||||||
require.NoError(t, user2.UpdateGluonData("gluonID2", []byte("gluonKey2")))
|
require.NoError(t, user2.SetGluonAuth("gluonID2", []byte("gluonKey2")))
|
||||||
|
|
||||||
// List available users.
|
// List available users.
|
||||||
require.ElementsMatch(t, []string{"userID1", "userID2"}, s.GetUserIDs())
|
require.ElementsMatch(t, []string{"userID1", "userID2"}, s.GetUserIDs())
|
||||||
|
|||||||
@ -74,6 +74,22 @@ func (vault *Vault) GetUser(userID string) (*User, error) {
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ForUser executes a callback for each user in the vault.
|
||||||
|
func (vault *Vault) ForUser(fn func(*User) error) error {
|
||||||
|
for _, userID := range vault.GetUserIDs() {
|
||||||
|
user, err := vault.GetUser(userID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := fn(user); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// AddUser creates a new user in the vault with the given ID and username.
|
// AddUser creates a new user in the vault with the given ID and username.
|
||||||
// A bridge password is generated using the package's token generator.
|
// A bridge password is generated using the package's token generator.
|
||||||
func (vault *Vault) AddUser(userID, username, authUID, authRef string, keyPass []byte) (*User, error) {
|
func (vault *Vault) AddUser(userID, username, authUID, authRef string, keyPass []byte) (*User, error) {
|
||||||
@ -86,6 +86,7 @@ func TestFeatures(testingT *testing.T) {
|
|||||||
ctx.Step(`^the user changes the IMAP port to (\d+)$`, s.theUserChangesTheIMAPPortTo)
|
ctx.Step(`^the user changes the IMAP port to (\d+)$`, s.theUserChangesTheIMAPPortTo)
|
||||||
ctx.Step(`^the user changes the SMTP port to (\d+)$`, s.theUserChangesTheSMTPPortTo)
|
ctx.Step(`^the user changes the SMTP port to (\d+)$`, s.theUserChangesTheSMTPPortTo)
|
||||||
ctx.Step(`^the user changes the gluon path$`, s.theUserChangesTheGluonPath)
|
ctx.Step(`^the user changes the gluon path$`, s.theUserChangesTheGluonPath)
|
||||||
|
ctx.Step(`^the user deletes the gluon files$`, s.theUserDeletesTheGluonFiles)
|
||||||
ctx.Step(`^the user reports a bug$`, s.theUserReportsABug)
|
ctx.Step(`^the user reports a bug$`, s.theUserReportsABug)
|
||||||
ctx.Step(`^bridge sends a connection up event$`, s.bridgeSendsAConnectionUpEvent)
|
ctx.Step(`^bridge sends a connection up event$`, s.bridgeSendsAConnectionUpEvent)
|
||||||
ctx.Step(`^bridge sends a connection down event$`, s.bridgeSendsAConnectionDownEvent)
|
ctx.Step(`^bridge sends a connection down event$`, s.bridgeSendsAConnectionDownEvent)
|
||||||
@ -127,6 +128,7 @@ func TestFeatures(testingT *testing.T) {
|
|||||||
ctx.Step(`^IMAP client "([^"]*)" deletes "([^"]*)"$`, s.imapClientDeletesMailbox)
|
ctx.Step(`^IMAP client "([^"]*)" deletes "([^"]*)"$`, s.imapClientDeletesMailbox)
|
||||||
ctx.Step(`^IMAP client "([^"]*)" renames "([^"]*)" to "([^"]*)"$`, s.imapClientRenamesMailboxTo)
|
ctx.Step(`^IMAP client "([^"]*)" renames "([^"]*)" to "([^"]*)"$`, s.imapClientRenamesMailboxTo)
|
||||||
ctx.Step(`^IMAP client "([^"]*)" sees the following mailbox info:$`, s.imapClientSeesTheFollowingMailboxInfo)
|
ctx.Step(`^IMAP client "([^"]*)" sees the following mailbox info:$`, s.imapClientSeesTheFollowingMailboxInfo)
|
||||||
|
ctx.Step(`^IMAP client "([^"]*)" eventually sees the following mailbox info:$`, s.imapClientEventuallySeesTheFollowingMailboxInfo)
|
||||||
ctx.Step(`^IMAP client "([^"]*)" sees the following mailbox info for "([^"]*)":$`, s.imapClientSeesTheFollowingMailboxInfoForMailbox)
|
ctx.Step(`^IMAP client "([^"]*)" sees the following mailbox info for "([^"]*)":$`, s.imapClientSeesTheFollowingMailboxInfoForMailbox)
|
||||||
ctx.Step(`^IMAP client "([^"]*)" sees the following mailboxes:$`, s.imapClientSeesTheFollowingMailboxes)
|
ctx.Step(`^IMAP client "([^"]*)" sees the following mailboxes:$`, s.imapClientSeesTheFollowingMailboxes)
|
||||||
ctx.Step(`^IMAP client "([^"]*)" sees "([^"]*)"$`, s.imapClientSeesMailbox)
|
ctx.Step(`^IMAP client "([^"]*)" sees "([^"]*)"$`, s.imapClientSeesMailbox)
|
||||||
|
|||||||
@ -55,6 +55,15 @@ func (s *scenario) theUserChangesTheGluonPath() error {
|
|||||||
return s.t.bridge.SetGluonDir(context.Background(), gluonDir)
|
return s.t.bridge.SetGluonDir(context.Background(), gluonDir)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *scenario) theUserDeletesTheGluonFiles() error {
|
||||||
|
path, err := s.t.locator.ProvideGluonPath()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return os.RemoveAll(path)
|
||||||
|
}
|
||||||
|
|
||||||
func (s *scenario) theUserHasDisabledAutomaticUpdates() error {
|
func (s *scenario) theUserHasDisabledAutomaticUpdates() error {
|
||||||
var started bool
|
var started bool
|
||||||
|
|
||||||
|
|||||||
@ -70,13 +70,15 @@ type smtpClient struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func newTestCtx(tb testing.TB) *testCtx {
|
func newTestCtx(tb testing.TB) *testCtx {
|
||||||
|
dir := tb.TempDir()
|
||||||
|
|
||||||
dialer := bridge.NewTestDialer()
|
dialer := bridge.NewTestDialer()
|
||||||
|
|
||||||
ctx := &testCtx{
|
ctx := &testCtx{
|
||||||
dir: tb.TempDir(),
|
dir: dir,
|
||||||
api: newFakeAPI(),
|
api: newFakeAPI(),
|
||||||
dialer: dialer,
|
dialer: dialer,
|
||||||
locator: locations.New(bridge.NewTestLocationsProvider(tb), "config-name"),
|
locator: locations.New(bridge.NewTestLocationsProvider(dir), "config-name"),
|
||||||
storeKey: []byte("super-secret-store-key"),
|
storeKey: []byte("super-secret-store-key"),
|
||||||
mocks: bridge.NewMocks(tb, dialer, defaultVersion, defaultVersion),
|
mocks: bridge.NewMocks(tb, dialer, defaultVersion, defaultVersion),
|
||||||
version: defaultVersion,
|
version: defaultVersion,
|
||||||
|
|||||||
@ -22,6 +22,29 @@ Feature: Bridge can fully sync an account
|
|||||||
When bridge restarts
|
When bridge restarts
|
||||||
And user "user@pm.me" connects and authenticates IMAP client "1"
|
And user "user@pm.me" connects and authenticates IMAP client "1"
|
||||||
Then IMAP client "1" sees the following mailbox info:
|
Then IMAP client "1" sees the following mailbox info:
|
||||||
|
| name | total | unread |
|
||||||
|
| INBOX | 0 | 0 |
|
||||||
|
| Drafts | 0 | 0 |
|
||||||
|
| Sent | 0 | 0 |
|
||||||
|
| Starred | 0 | 0 |
|
||||||
|
| Archive | 0 | 0 |
|
||||||
|
| Spam | 0 | 0 |
|
||||||
|
| Trash | 0 | 0 |
|
||||||
|
| All Mail | 4 | 2 |
|
||||||
|
| Folders | 0 | 0 |
|
||||||
|
| Folders/one | 2 | 1 |
|
||||||
|
| Folders/two | 2 | 1 |
|
||||||
|
| Labels | 0 | 0 |
|
||||||
|
| Labels/three | 0 | 0 |
|
||||||
|
|
||||||
|
Scenario: If the gluon files are deleted, the account is synced again
|
||||||
|
Given the user logs in with username "user@pm.me" and password "password"
|
||||||
|
And user "user@pm.me" finishes syncing
|
||||||
|
And bridge stops
|
||||||
|
And the user deletes the gluon files
|
||||||
|
And bridge starts
|
||||||
|
When user "user@pm.me" connects and authenticates IMAP client "1"
|
||||||
|
Then IMAP client "1" eventually sees the following mailbox info:
|
||||||
| name | total | unread |
|
| name | total | unread |
|
||||||
| INBOX | 0 | 0 |
|
| INBOX | 0 | 0 |
|
||||||
| Drafts | 0 | 0 |
|
| Drafts | 0 | 0 |
|
||||||
|
|||||||
@ -124,6 +124,14 @@ func (s *scenario) imapClientSeesTheFollowingMailboxInfo(clientID string, table
|
|||||||
return matchMailboxes(haveMailboxes, table)
|
return matchMailboxes(haveMailboxes, table)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *scenario) imapClientEventuallySeesTheFollowingMailboxInfo(clientID string, table *godog.Table) error {
|
||||||
|
return eventually(
|
||||||
|
func() error { return s.imapClientSeesTheFollowingMailboxInfo(clientID, table) },
|
||||||
|
5*time.Second,
|
||||||
|
100*time.Millisecond,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
func (s *scenario) imapClientSeesTheFollowingMailboxInfoForMailbox(clientID, mailbox string, table *godog.Table) error {
|
func (s *scenario) imapClientSeesTheFollowingMailboxInfoForMailbox(clientID, mailbox string, table *godog.Table) error {
|
||||||
_, client := s.t.getIMAPClient(clientID)
|
_, client := s.t.getIMAPClient(clientID)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user