From 93d9ae32fc2075a42354257c9dd3228e2040a3ba Mon Sep 17 00:00:00 2001 From: James Houlahan Date: Fri, 28 Oct 2022 13:38:16 +0200 Subject: [PATCH] Other: Only listen on localhost and add send test We should only listen on constants.Host when serving IMAP and SMTP. This change fixes that. It also adds a test that we can send over SMTP and receive over IMAP. --- go.mod | 2 +- go.sum | 4 +- internal/bridge/bridge.go | 4 +- internal/bridge/send_test.go | 98 +++++++++++++++++++++++++++++++++ internal/bridge/sync_test.go | 7 ++- internal/constants/constants.go | 2 +- internal/locations/provider.go | 18 +++++- 7 files changed, 124 insertions(+), 11 deletions(-) create mode 100644 internal/bridge/send_test.go diff --git a/go.mod b/go.mod index b710377e..10af2e83 100644 --- a/go.mod +++ b/go.mod @@ -39,7 +39,7 @@ require ( github.com/stretchr/testify v1.8.0 github.com/urfave/cli/v2 v2.20.3 github.com/vmihailenco/msgpack/v5 v5.3.5 - gitlab.protontech.ch/go/liteapi v0.37.0 + gitlab.protontech.ch/go/liteapi v0.37.1 go.uber.org/goleak v1.2.0 golang.org/x/exp v0.0.0-20221023144134-a1e5550cf13e golang.org/x/net v0.1.0 diff --git a/go.sum b/go.sum index 60586de2..76142874 100644 --- a/go.sum +++ b/go.sum @@ -403,8 +403,8 @@ github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsr github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/zclconf/go-cty v1.11.0 h1:726SxLdi2SDnjY+BStqB9J1hNp4+2WlzyXLuimibIe0= github.com/zclconf/go-cty v1.11.0/go.mod h1:s9IfD1LK5ccNMSWCVFCE2rJfHiZgi7JijgeWIMfhLvA= -gitlab.protontech.ch/go/liteapi v0.37.0 h1:HLK+jixbAhLL/h8ySx4dl7JT5sb2zUAkOzcBIF4IqSw= -gitlab.protontech.ch/go/liteapi v0.37.0/go.mod h1:IM7ADWjgIL2hXopzx0WNamizEuMgM2QZl7QH12FNflk= +gitlab.protontech.ch/go/liteapi v0.37.1 h1:ajYSnz733L//i9SKOSqxGlZh4bNFYkCnfrUYli52CS4= +gitlab.protontech.ch/go/liteapi v0.37.1/go.mod h1:IM7ADWjgIL2hXopzx0WNamizEuMgM2QZl7QH12FNflk= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= diff --git a/internal/bridge/bridge.go b/internal/bridge/bridge.go index 04246bde..5ef1e3be 100644 --- a/internal/bridge/bridge.go +++ b/internal/bridge/bridge.go @@ -487,7 +487,7 @@ func loadTLSConfig(vault *vault.Vault) (*tls.Config, error) { func newListener(port int, useTLS bool, tlsConfig *tls.Config) (net.Listener, error) { if useTLS { - tlsListener, err := tls.Listen("tcp", fmt.Sprintf(":%v", port), tlsConfig) + tlsListener, err := tls.Listen("tcp", fmt.Sprintf("%v:%v", constants.Host, port), tlsConfig) if err != nil { return nil, err } @@ -495,7 +495,7 @@ func newListener(port int, useTLS bool, tlsConfig *tls.Config) (net.Listener, er return tlsListener, nil } - netListener, err := net.Listen("tcp", fmt.Sprintf(":%v", port)) + netListener, err := net.Listen("tcp", fmt.Sprintf("%v:%v", constants.Host, port)) if err != nil { return nil, err } diff --git a/internal/bridge/send_test.go b/internal/bridge/send_test.go new file mode 100644 index 00000000..6b0feb77 --- /dev/null +++ b/internal/bridge/send_test.go @@ -0,0 +1,98 @@ +// Copyright (c) 2022 Proton AG +// +// This file is part of Proton Mail Bridge. +// +// Proton Mail 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. +// +// Proton Mail 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 Proton Mail Bridge. If not, see . + +package bridge_test + +import ( + "context" + "fmt" + "net/smtp" + "testing" + "time" + + "github.com/ProtonMail/proton-bridge/v2/internal/bridge" + "github.com/ProtonMail/proton-bridge/v2/internal/constants" + "github.com/emersion/go-imap" + "github.com/emersion/go-imap/client" + "github.com/stretchr/testify/require" + "gitlab.protontech.ch/go/liteapi" + "gitlab.protontech.ch/go/liteapi/server" +) + +func TestBridge_Send(t *testing.T) { + withEnv(t, func(ctx context.Context, s *server.Server, netCtl *liteapi.NetCtl, locator bridge.Locator, storeKey []byte) { + _, _, err := s.CreateUser("recipient", "recipient@pm.me", password) + require.NoError(t, err) + + withBridge(ctx, t, s.GetHostURL(), netCtl, locator, storeKey, func(bridge *bridge.Bridge, mocks *bridge.Mocks) { + senderUserID, err := bridge.LoginFull(ctx, username, password, nil, nil) + require.NoError(t, err) + + recipientUserID, err := bridge.LoginFull(ctx, "recipient", password, nil, nil) + require.NoError(t, err) + + senderInfo, err := bridge.GetUserInfo(senderUserID) + require.NoError(t, err) + + recipientInfo, err := bridge.GetUserInfo(recipientUserID) + require.NoError(t, err) + + for i := 0; i < 10; i++ { + // Send an email from sender to recipient. + smtpClient, err := smtp.Dial(fmt.Sprintf("%v:%v", constants.Host, bridge.GetSMTPPort())) + require.NoError(t, err) + defer smtpClient.Close() //nolint:errcheck + + require.NoError(t, smtpClient.Auth(smtp.PlainAuth("", senderInfo.Addresses[0], string(senderInfo.BridgePass), constants.Host))) + require.NoError(t, smtpClient.Mail(senderInfo.Addresses[0])) + require.NoError(t, smtpClient.Rcpt("recipient@pm.me")) + + wc, err := smtpClient.Data() + require.NoError(t, err) + + n, err := fmt.Fprintf(wc, "Subject: Test %v\r\n\r\nHello world!", i) + require.NoError(t, err) + require.Greater(t, n, 0) + require.NoError(t, wc.Close()) + + // Sender should see the message in the Sent folder. + senderIMAPClient, err := client.Dial(fmt.Sprintf("%v:%v", constants.Host, bridge.GetIMAPPort())) + require.NoError(t, err) + require.NoError(t, senderIMAPClient.Login(senderInfo.Addresses[0], string(senderInfo.BridgePass))) + defer senderIMAPClient.Logout() //nolint:errcheck + + require.Eventually(t, func() bool { + status, err := senderIMAPClient.Status(`Sent`, []imap.StatusItem{imap.StatusMessages}) + require.NoError(t, err) + return status.Messages == uint32(i+1) + }, 10*time.Second, 100*time.Millisecond) + + // Recipient should see the message in the Inbox. + recipientIMAPClient, err := client.Dial(fmt.Sprintf("%v:%v", constants.Host, bridge.GetIMAPPort())) + require.NoError(t, err) + require.NoError(t, recipientIMAPClient.Login(recipientInfo.Addresses[0], string(recipientInfo.BridgePass))) + defer recipientIMAPClient.Logout() //nolint:errcheck + + require.Eventually(t, func() bool { + status, err := recipientIMAPClient.Status(`Inbox`, []imap.StatusItem{imap.StatusMessages}) + require.NoError(t, err) + return status.Messages == uint32(i+1) + }, 10*time.Second, 100*time.Millisecond) + } + }) + }) +} diff --git a/internal/bridge/sync_test.go b/internal/bridge/sync_test.go index 057106bc..5b06de25 100644 --- a/internal/bridge/sync_test.go +++ b/internal/bridge/sync_test.go @@ -27,6 +27,7 @@ import ( "testing" "github.com/ProtonMail/proton-bridge/v2/internal/bridge" + "github.com/ProtonMail/proton-bridge/v2/internal/constants" "github.com/ProtonMail/proton-bridge/v2/internal/events" "github.com/bradenaw/juniper/iterator" "github.com/bradenaw/juniper/stream" @@ -72,7 +73,7 @@ func TestBridge_Sync(t *testing.T) { require.NoError(t, err) require.True(t, info.Connected) - client, err := client.Dial(fmt.Sprintf(":%v", bridge.GetIMAPPort())) + client, err := client.Dial(fmt.Sprintf("%v:%v", constants.Host, bridge.GetIMAPPort())) require.NoError(t, err) require.NoError(t, client.Login("imap@pm.me", string(info.BridgePass))) defer func() { _ = client.Logout() }() @@ -105,7 +106,7 @@ func TestBridge_Sync(t *testing.T) { require.NoError(t, err) require.True(t, info.Connected) - client, err := client.Dial(fmt.Sprintf(":%v", bridge.GetIMAPPort())) + client, err := client.Dial(fmt.Sprintf("%v:%v", constants.Host, bridge.GetIMAPPort())) require.NoError(t, err) require.NoError(t, client.Login("imap@pm.me", string(info.BridgePass))) defer func() { _ = client.Logout() }() @@ -128,7 +129,7 @@ func TestBridge_Sync(t *testing.T) { require.NoError(t, err) require.True(t, info.Connected) - client, err := client.Dial(fmt.Sprintf(":%v", bridge.GetIMAPPort())) + client, err := client.Dial(fmt.Sprintf("%v:%v", constants.Host, bridge.GetIMAPPort())) require.NoError(t, err) require.NoError(t, client.Login("imap@pm.me", string(info.BridgePass))) defer func() { _ = client.Logout() }() diff --git a/internal/constants/constants.go b/internal/constants/constants.go index 6798da4c..b5ad8a58 100644 --- a/internal/constants/constants.go +++ b/internal/constants/constants.go @@ -34,7 +34,7 @@ var ( FullAppName = "" // Version of the build. - Version = "" + Version = "0.0.0" // Revision is current hash of the build. Revision = "" diff --git a/internal/locations/provider.go b/internal/locations/provider.go index 83d23594..4ff1b9b1 100644 --- a/internal/locations/provider.go +++ b/internal/locations/provider.go @@ -52,11 +52,25 @@ func NewDefaultProvider(name string) (*DefaultProvider, error) { return nil, err } - return &DefaultProvider{ + provider := &DefaultProvider{ config: filepath.Join(config, name), data: filepath.Join(data, name), cache: filepath.Join(cache, name), - }, nil + } + + if err := os.MkdirAll(provider.config, 0o700); err != nil { + return nil, err + } + + if err := os.MkdirAll(provider.data, 0o700); err != nil { + return nil, err + } + + if err := os.MkdirAll(provider.cache, 0o700); err != nil { + return nil, err + } + + return provider, nil } // UserConfig returns a directory that can be used to store user-specific configuration.