feat(BRIDGE-205): add support for the IMAP AUTHENTICATE command.

This commit is contained in:
Xavier Michelon
2024-11-11 08:21:44 +01:00
parent 7d9753e2da
commit b3e2a91f56
5 changed files with 144 additions and 12 deletions

View File

@ -13,26 +13,46 @@ Feature: A user can authenticate an IMAP client
When user "[user:user]" connects IMAP client "1"
Then IMAP client "1" can authenticate
Scenario: IMAP client can authenticate successfully using IMAP AUTHENTICATE
When user "[user:user]" connects IMAP client "1"
Then IMAP client "1" can authenticate using IMAP AUTHENTICATE
Scenario: IMAP client can authenticate successfully with different case
When user "[user:user]" connects IMAP client "1"
Then IMAP client "1" can authenticate with address "{toUpper:[user:user]@[domain]}"
Scenario: IMAP client can authenticate successfully with different case using IMAP AUTHENTICATE
When user "[user:user]" connects IMAP client "1"
Then IMAP client "1" can authenticate with address "{toUpper:[user:user]@[domain]}" using IMAP AUTHENTICATE
Scenario: IMAP client can authenticate successfully with secondary address
Given user "[user:user]" connects and authenticates IMAP client "1" with address "[alias:alias]@[domain]"
Scenario: IMAP client can authenticate successfully
When user "[user:user]" connects IMAP client "1"
Then IMAP client "1" can authenticate
Scenario: IMAP client can authenticate successfully with secondary address using IMAP AUTHENTICATE
Given user "[user:user]" connects and authenticates IMAP client "1" with address "[alias:alias]@[domain]" using IMAP AUTHENTICATE
Scenario: IMAP client cannot authenticate with bad username
When user "[user:user]" connects IMAP client "1"
Then IMAP client "1" cannot authenticate with incorrect username
Scenario: IMAP client cannot authenticate with bad username using IMAP AUTHENTICATE
When user "[user:user]" connects IMAP client "1"
Then IMAP client "1" cannot authenticate with incorrect username using IMAP AUTHENTICATE
Scenario: IMAP client cannot authenticate with bad password
When user "[user:user]" connects IMAP client "1"
Then IMAP client "1" cannot authenticate with incorrect password
Scenario: IMAP client cannot authenticate with bad password using IMAP AUTHENTICATE
When user "[user:user]" connects IMAP client "1"
Then IMAP client "1" cannot authenticate with incorrect password using IMAP AUTHENTICATE
Scenario: IMAP client cannot authenticate for disconnected user
When user "[user:user]" logs out
And user "[user:user]" connects IMAP client "1"
Then IMAP client "1" cannot authenticate
Scenario: IMAP client cannot authenticate using IMAP AUTHENTICATE for disconnected user
When user "[user:user]" logs out
And user "[user:user]" connects IMAP client "1"
Then IMAP client "1" cannot authenticate using IMAP AUTHENTICATE

View File

@ -36,10 +36,38 @@ import (
"github.com/emersion/go-imap"
id "github.com/emersion/go-imap-id"
"github.com/emersion/go-imap/client"
"github.com/emersion/go-sasl"
"github.com/sirupsen/logrus"
"golang.org/x/exp/slices"
)
type imapAuthMethod int
const (
imapLogin imapAuthMethod = iota
imapAuthenticate
)
func (s *scenario) loginWithAuthMethod(client *client.Client, username, password string, authMethod imapAuthMethod) error {
switch authMethod {
case imapLogin:
return client.Login(username, password)
case imapAuthenticate:
supported, err := client.SupportAuth(sasl.Plain)
if err != nil {
return err
}
if !supported {
return errors.New("server does not support AUTHENTICATE PLAIN")
}
return client.Authenticate(sasl.NewPlainClient("", username, password))
default:
return errors.New("unknown IMAP auth method")
}
}
func (s *scenario) userConnectsIMAPClient(username, clientID string) error {
return s.t.newIMAPClient(s.t.getUserByName(username).getUserID(), clientID)
}
@ -59,7 +87,17 @@ func (s *scenario) userConnectsAndAuthenticatesIMAPClientWithAddress(username, c
userID, client := s.t.getIMAPClient(clientID)
return client.Login(address, s.t.getUserByID(userID).getBridgePass())
return s.loginWithAuthMethod(client, address, s.t.getUserByID(userID).getBridgePass(), imapLogin)
}
func (s *scenario) userConnectsAndAuthenticatesIMAPClientWithAddressUsingIMAPAuthenticate(username, clientID, address string) error {
if err := s.t.newIMAPClient(s.t.getUserByName(username).getUserID(), clientID); err != nil {
return err
}
userID, client := s.t.getIMAPClient(clientID)
return s.loginWithAuthMethod(client, address, s.t.getUserByID(userID).getBridgePass(), imapAuthenticate)
}
func (s *scenario) userConnectsAndCanNotAuthenticateIMAPClientWithAddress(username, clientID, address string) error {
@ -69,7 +107,7 @@ func (s *scenario) userConnectsAndCanNotAuthenticateIMAPClientWithAddress(userna
userID, client := s.t.getIMAPClient(clientID)
if err := client.Login(address, s.t.getUserByID(userID).getBridgePass()); err == nil {
if err := s.loginWithAuthMethod(client, address, s.t.getUserByID(userID).getBridgePass(), imapLogin); err == nil {
return fmt.Errorf("expected error, got nil")
}
@ -79,19 +117,51 @@ func (s *scenario) userConnectsAndCanNotAuthenticateIMAPClientWithAddress(userna
func (s *scenario) imapClientCanAuthenticate(clientID string) error {
userID, client := s.t.getIMAPClient(clientID)
return client.Login(s.t.getUserByID(userID).getEmails()[0], s.t.getUserByID(userID).getBridgePass())
return s.loginWithAuthMethod(client, s.t.getUserByID(userID).getEmails()[0], s.t.getUserByID(userID).getBridgePass(), imapLogin)
}
func (s *scenario) imapClientCanAuthenticateUsingIMAPAuthenticate(clientID string) error {
userID, client := s.t.getIMAPClient(clientID)
return s.loginWithAuthMethod(client, s.t.getUserByID(userID).getEmails()[0], s.t.getUserByID(userID).getBridgePass(), imapAuthenticate)
}
func (s *scenario) imapClientCanAuthenticateWithAddress(clientID string, address string) error {
userID, client := s.t.getIMAPClient(clientID)
return client.Login(address, s.t.getUserByID(userID).getBridgePass())
return s.loginWithAuthMethod(client, address, s.t.getUserByID(userID).getBridgePass(), imapLogin)
}
func (s *scenario) imapClientCanAuthenticateWithAddressUsingIMAPAuthenticate(clientID string, address string) error {
userID, client := s.t.getIMAPClient(clientID)
return s.loginWithAuthMethod(client, address, s.t.getUserByID(userID).getBridgePass(), imapAuthenticate)
}
func (s *scenario) imapClientCannotAuthenticate(clientID string) error {
userID, client := s.t.getIMAPClient(clientID)
if err := client.Login(s.t.getUserByID(userID).getEmails()[0], s.t.getUserByID(userID).getBridgePass()); err == nil {
if err := s.loginWithAuthMethod(
client,
s.t.getUserByID(userID).getEmails()[0],
s.t.getUserByID(userID).getBridgePass(),
imapLogin,
); err == nil {
return fmt.Errorf("expected error, got nil")
}
return nil
}
func (s *scenario) imapClientCannotAuthenticateUsingIMAPAuthenticate(clientID string) error {
userID, client := s.t.getIMAPClient(clientID)
if err := s.loginWithAuthMethod(
client,
s.t.getUserByID(userID).getEmails()[0],
s.t.getUserByID(userID).getBridgePass(),
imapAuthenticate,
); err == nil {
return fmt.Errorf("expected error, got nil")
}
@ -101,7 +171,7 @@ func (s *scenario) imapClientCannotAuthenticate(clientID string) error {
func (s *scenario) imapClientCannotAuthenticateWithAddress(clientID, address string) error {
userID, client := s.t.getIMAPClient(clientID)
if err := client.Login(address, s.t.getUserByID(userID).getBridgePass()); err == nil {
if err := s.loginWithAuthMethod(client, address, s.t.getUserByID(userID).getBridgePass(), imapLogin); err == nil {
return fmt.Errorf("expected error, got nil")
}
@ -111,7 +181,27 @@ func (s *scenario) imapClientCannotAuthenticateWithAddress(clientID, address str
func (s *scenario) imapClientCannotAuthenticateWithIncorrectUsername(clientID string) error {
userID, client := s.t.getIMAPClient(clientID)
if err := client.Login(s.t.getUserByID(userID).getEmails()[0]+"bad", s.t.getUserByID(userID).getBridgePass()); err == nil {
if err := s.loginWithAuthMethod(
client,
s.t.getUserByID(userID).getEmails()[0]+"bad",
s.t.getUserByID(userID).getBridgePass(),
imapLogin,
); err == nil {
return fmt.Errorf("expected error, got nil")
}
return nil
}
func (s *scenario) imapClientCannotAuthenticateWithIncorrectUsernameUsingIMAPAuthenticate(clientID string) error {
userID, client := s.t.getIMAPClient(clientID)
if err := s.loginWithAuthMethod(
client,
s.t.getUserByID(userID).getEmails()[0]+"bad",
s.t.getUserByID(userID).getBridgePass(),
imapAuthenticate,
); err == nil {
return fmt.Errorf("expected error, got nil")
}
@ -121,7 +211,17 @@ func (s *scenario) imapClientCannotAuthenticateWithIncorrectUsername(clientID st
func (s *scenario) imapClientCannotAuthenticateWithIncorrectPassword(clientID string) error {
userID, client := s.t.getIMAPClient(clientID)
badPass := base64.StdEncoding.EncodeToString([]byte("bad_password"))
if err := client.Login(s.t.getUserByID(userID).getEmails()[0], badPass); err == nil {
if err := s.loginWithAuthMethod(client, s.t.getUserByID(userID).getEmails()[0], badPass, imapLogin); err == nil {
return fmt.Errorf("expected error, got nil")
}
return nil
}
func (s *scenario) imapClientCannotAuthenticateWithIncorrectPasswordUsingIMAPAuthenticate(clientID string) error {
userID, client := s.t.getIMAPClient(clientID)
badPass := base64.StdEncoding.EncodeToString([]byte("bad_password"))
if err := s.loginWithAuthMethod(client, s.t.getUserByID(userID).getEmails()[0], badPass, imapAuthenticate); err == nil {
return fmt.Errorf("expected error, got nil")
}

View File

@ -129,13 +129,19 @@ func (s *scenario) steps(ctx *godog.ScenarioContext) {
ctx.Step(`^user "([^"]*)" connects IMAP client "([^"]*)" on port (\d+)$`, s.userConnectsIMAPClientOnPort)
ctx.Step(`^user "([^"]*)" connects and authenticates IMAP client "([^"]*)"$`, s.userConnectsAndAuthenticatesIMAPClient)
ctx.Step(`^user "([^"]*)" connects and authenticates IMAP client "([^"]*)" with address "([^"]*)"$`, s.userConnectsAndAuthenticatesIMAPClientWithAddress)
ctx.Step(`^user "([^"]*)" connects and authenticates IMAP client "([^"]*)" with address "([^"]*)" using IMAP AUTHENTICATE$`, s.userConnectsAndAuthenticatesIMAPClientWithAddressUsingIMAPAuthenticate)
ctx.Step(`^user "([^"]*)" connects and can not authenticate IMAP client "([^"]*)" with address "([^"]*)"$`, s.userConnectsAndCanNotAuthenticateIMAPClientWithAddress)
ctx.Step(`^IMAP client "([^"]*)" can authenticate$`, s.imapClientCanAuthenticate)
ctx.Step(`^IMAP client "([^"]*)" can authenticate using IMAP AUTHENTICATE$`, s.imapClientCanAuthenticateUsingIMAPAuthenticate)
ctx.Step(`^IMAP client "([^"]*)" can authenticate with address "([^"]*)"$`, s.imapClientCanAuthenticateWithAddress)
ctx.Step(`^IMAP client "([^"]*)" can authenticate with address "([^"]*)" using IMAP AUTHENTICATE$`, s.imapClientCanAuthenticateWithAddressUsingIMAPAuthenticate)
ctx.Step(`^IMAP client "([^"]*)" cannot authenticate$`, s.imapClientCannotAuthenticate)
ctx.Step(`^IMAP client "([^"]*)" cannot authenticate using IMAP AUTHENTICATE$`, s.imapClientCannotAuthenticateUsingIMAPAuthenticate)
ctx.Step(`^IMAP client "([^"]*)" cannot authenticate with address "([^"]*)"$`, s.imapClientCannotAuthenticateWithAddress)
ctx.Step(`^IMAP client "([^"]*)" cannot authenticate with incorrect username$`, s.imapClientCannotAuthenticateWithIncorrectUsername)
ctx.Step(`^IMAP client "([^"]*)" cannot authenticate with incorrect username using IMAP AUTHENTICATE$`, s.imapClientCannotAuthenticateWithIncorrectUsernameUsingIMAPAuthenticate)
ctx.Step(`^IMAP client "([^"]*)" cannot authenticate with incorrect password$`, s.imapClientCannotAuthenticateWithIncorrectPassword)
ctx.Step(`^IMAP client "([^"]*)" cannot authenticate with incorrect password using IMAP AUTHENTICATE$`, s.imapClientCannotAuthenticateWithIncorrectPasswordUsingIMAPAuthenticate)
ctx.Step(`^IMAP client "([^"]*)" closes$`, s.imapClientCloses)
ctx.Step(`^IMAP client "([^"]*)" announces its ID with name "([^"]*)" and version "([^"]*)"$`, s.imapClientAnnouncesItsIDWithNameAndVersion)
ctx.Step(`^IMAP client "([^"]*)" creates "([^"]*)"$`, s.imapClientCreatesMailbox)