Skip to content

Commit

Permalink
feat: Move credential checks to Connector interface (#3)
Browse files Browse the repository at this point in the history
  • Loading branch information
jameshoulahan committed Jun 3, 2022
1 parent 2ec9f35 commit 2ec2361
Show file tree
Hide file tree
Showing 10 changed files with 56 additions and 38 deletions.
3 changes: 3 additions & 0 deletions connector/connector.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ import (

// Connector connects the gluon server to a remote mail store.
type Connector interface {
// Authorize returns whether the given username/password combination are valid for this connector.
Authorize(username, password string) bool

// GetUpdates returns a stream of updates that the gluon server should apply.
GetUpdates() <-chan imap.Update

Expand Down
11 changes: 10 additions & 1 deletion connector/dummy.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ type Dummy struct {
// state holds the fake connector state.
state *dummyState

// username and password are the credentials for this connector.
username, password string

// These hold the default flags/attributes given to mailboxes.
flags, permFlags, attrs imap.FlagSet

Expand All @@ -38,9 +41,11 @@ type Dummy struct {
queueLock sync.Mutex
}

func NewDummy(period time.Duration, flags, permFlags, attrs imap.FlagSet) *Dummy {
func NewDummy(username, password string, period time.Duration, flags, permFlags, attrs imap.FlagSet) *Dummy {
conn := &Dummy{
state: newDummyState(flags, permFlags, attrs),
username: username,
password: password,
flags: flags,
permFlags: permFlags,
attrs: attrs,
Expand All @@ -62,6 +67,10 @@ func NewDummy(period time.Duration, flags, permFlags, attrs imap.FlagSet) *Dummy
return conn
}

func (conn *Dummy) Authorize(username, password string) bool {
return username == conn.username && password == conn.password
}

func (conn *Dummy) GetUpdates() <-chan imap.Update {
return conn.updateCh
}
Expand Down
6 changes: 6 additions & 0 deletions connector/dummy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ var (

func TestDummyConnector_ValidateCreate(t *testing.T) {
conn := NewDummy(
"username",
"password",
defaultPeriod,
defaultFlags,
defaultPermanentFlags,
Expand Down Expand Up @@ -67,6 +69,8 @@ func TestDummyConnector_ValidateCreate(t *testing.T) {

func TestDummyConnector_ValidateUpdate(t *testing.T) {
conn := NewDummy(
"username",
"password",
defaultPeriod,
defaultFlags,
defaultPermanentFlags,
Expand Down Expand Up @@ -97,6 +101,8 @@ func TestDummyConnector_ValidateUpdate(t *testing.T) {

func TestDummyConnector_ValidateDelete(t *testing.T) {
conn := NewDummy(
"username",
"password",
defaultPeriod,
defaultFlags,
defaultPermanentFlags,
Expand Down
12 changes: 7 additions & 5 deletions demo/demo.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ func main() {
server := gluon.New(filepath.Join(dir, "server"))

connector := connector.NewDummy(
"[email protected]",
"password",
time.Second,
imap.NewFlagSet(),
imap.NewFlagSet(),
Expand All @@ -37,18 +39,18 @@ func main() {
logrus.WithError(err).Fatal("Failed to create store")
}

if err := server.AddUser(
"userID",
"username",
"password",
userID, err := server.AddUser(
connector,
store,
dialect.SQLite,
fmt.Sprintf("file:%v?cache=shared&_fk=1", filepath.Join(dir, fmt.Sprintf("%v.db", "userID"))),
); err != nil {
)
if err != nil {
logrus.WithError(err).Fatal("Failed to add user")
}

logrus.WithField("userID", userID).Info("User added to server")

listener, err := net.Listen("tcp", ":1143")
if err != nil {
logrus.WithError(err).Fatal("Failed to listen")
Expand Down
3 changes: 1 addition & 2 deletions events/user.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
package events

type EventUserAdded struct {
UserID string
Username string
UserID string
}

func (EventUserAdded) _isEvent() {}
Expand Down
13 changes: 8 additions & 5 deletions internal/backend/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"github.com/ProtonMail/gluon/internal/backend/ent"
"github.com/ProtonMail/gluon/internal/remote"
"github.com/ProtonMail/gluon/store"
"github.com/google/uuid"
"github.com/sirupsen/logrus"
)

Expand Down Expand Up @@ -36,23 +37,25 @@ func (b *Backend) SetDelimiter(delim string) {
b.delim = delim
}

func (b *Backend) AddUser(userID, username, password string, conn connector.Connector, store store.Store, client *ent.Client) error {
func (b *Backend) AddUser(conn connector.Connector, store store.Store, client *ent.Client) (string, error) {
b.usersLock.Lock()
defer b.usersLock.Unlock()

remote, err := b.remote.AddUser(userID, username, password, conn)
userID := uuid.NewString()

remote, err := b.remote.AddUser(userID, conn)
if err != nil {
return err
return "", err
}

user, err := newUser(userID, client, remote, store, b.delim)
if err != nil {
return err
return "", err
}

b.users[userID] = user

return nil
return userID, nil
}

func (b *Backend) GetState(username, password string) (*State, error) {
Expand Down
6 changes: 3 additions & 3 deletions internal/remote/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ func New(dir string) *Manager {

// AddUser adds the remote user with the given (IMAP) credentials to the remote manager.
// The user interacts with the remote via the given connector.
func (m *Manager) AddUser(userID, username, password string, conn connector.Connector) (*User, error) {
func (m *Manager) AddUser(userID string, conn connector.Connector) (*User, error) {
m.usersLock.Lock()
defer m.usersLock.Unlock()

Expand All @@ -41,7 +41,7 @@ func (m *Manager) AddUser(userID, username, password string, conn connector.Conn
return nil, err
}

user, err := newUser(userID, username, password, path, conn)
user, err := newUser(userID, path, conn)
if err != nil {
return nil, err
}
Expand All @@ -57,7 +57,7 @@ func (m *Manager) GetUserID(username, password string) (string, error) {
defer m.usersLock.Unlock()

for _, user := range m.users {
if user.username == username && user.password == password {
if user.conn.Authorize(username, password) {
return user.userID, nil
}
}
Expand Down
8 changes: 2 additions & 6 deletions internal/remote/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,7 @@ import (

// User performs operations against a remote server using a connector.
type User struct {
userID string
username string
password string
userID string

// path is the path at which the operation queue will be saved to disk.
path string
Expand All @@ -40,11 +38,9 @@ type User struct {
// newUser constructs a new user with the given (IMAP) credentials.
// It serializes its operation queue to a file at the given filepath,
// and performs remote operations using the given connector.
func newUser(userID, username, password, path string, conn connector.Connector) (*User, error) {
func newUser(userID, path string, conn connector.Connector) (*User, error) {
user := &User{
userID: userID,
username: username,
password: password,
path: path,
conn: conn,
updatesCh: make(chan imap.Update),
Expand Down
14 changes: 7 additions & 7 deletions server.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,22 +65,22 @@ func New(dir string, withOpt ...Option) *Server {
}

// AddUser makes a user available to the mailserver.
func (s *Server) AddUser(userID, username, password string, conn connector.Connector, store store.Store, driver, source string) error {
func (s *Server) AddUser(conn connector.Connector, store store.Store, driver, source string) (string, error) {
client, err := ent.Open(driver, source)
if err != nil {
return err
return "", err
}

if err := s.backend.AddUser(userID, username, password, conn, store, client); err != nil {
return err
userID, err := s.backend.AddUser(conn, store, client)
if err != nil {
return "", err
}

s.publish(events.EventUserAdded{
UserID: userID,
Username: username,
UserID: userID,
})

return nil
return userID, nil
}

// AddWatcher adds a new watcher.
Expand Down
18 changes: 9 additions & 9 deletions tests/server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import (
"github.com/ProtonMail/gluon"
"github.com/ProtonMail/gluon/connector"
"github.com/ProtonMail/gluon/imap"
"github.com/ProtonMail/gluon/internal/utils"
"github.com/ProtonMail/gluon/store"
"github.com/emersion/go-imap/client"
"github.com/google/uuid"
Expand Down Expand Up @@ -50,24 +49,25 @@ func runServer(tb testing.TB, credentials map[string]string, delim string, tests
userIDs := make(map[string]string)
conns := make(map[string]Connector)

for user, pass := range credentials {
userID := utils.NewRandomUserID()

for username, password := range credentials {
conn := connector.NewDummy(
username,
password,
defaultPeriod,
defaultFlags,
defaultPermanentFlags,
defaultAttributes,
)

store, err := store.NewOnDiskStore(tb.TempDir(), []byte("passphrase"))
store, err := store.NewOnDiskStore(tb.TempDir(), []byte(password))
require.NoError(tb, err)

require.NoError(tb, server.AddUser(userID, user, pass, conn, store, dialect.SQLite, getEntPath(tb.TempDir(), userID)))
userID, err := server.AddUser(conn, store, dialect.SQLite, getEntPath(tb.TempDir()))
require.NoError(tb, err)

require.NoError(tb, conn.Sync(ctx))

userIDs[user] = userID
userIDs[username] = userID
conns[userID] = conn
}

Expand Down Expand Up @@ -125,6 +125,6 @@ func withData(s *testSession, username string, tests func(string, string)) {
tests(mbox, mboxID)
}

func getEntPath(dir, userID string) string {
return fmt.Sprintf("file:%v?cache=shared&_fk=1", filepath.Join(dir, fmt.Sprintf("%v.db", userID)))
func getEntPath(dir string) string {
return fmt.Sprintf("file:%v?cache=shared&_fk=1", filepath.Join(dir, fmt.Sprintf("%v.db", uuid.NewString())))
}

0 comments on commit 2ec2361

Please sign in to comment.