diff --git a/go.mod b/go.mod index c9bb4a7a..7f7da8c2 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,7 @@ require ( github.com/Masterminds/semver/v3 v3.1.1 github.com/ProtonMail/gluon v0.14.2-0.20230106095250-7e99ea4da61e github.com/ProtonMail/go-autostart v0.0.0-20210130080809-00ed301c8e9a - github.com/ProtonMail/go-proton-api v0.2.4-0.20230103140323-680d85d1c3f0 + github.com/ProtonMail/go-proton-api v0.2.4-0.20230109130302-16194531370b github.com/ProtonMail/go-rfc5322 v0.11.0 github.com/ProtonMail/gopenpgp/v2 v2.4.10 github.com/PuerkitoBio/goquery v1.8.0 diff --git a/go.sum b/go.sum index ee4362ef..a79c6e01 100644 --- a/go.sum +++ b/go.sum @@ -28,8 +28,6 @@ github.com/ProtonMail/bcrypt v0.0.0-20211005172633-e235017c1baf h1:yc9daCCYUefEs github.com/ProtonMail/bcrypt v0.0.0-20211005172633-e235017c1baf/go.mod h1:o0ESU9p83twszAU8LBeJKFAAMX14tISa0yk4Oo5TOqo= github.com/ProtonMail/docker-credential-helpers v1.1.0 h1:+kvUIpwWcbtP3WFv5sSvkFn/XLzSqPOB5AAthuk9xPk= github.com/ProtonMail/docker-credential-helpers v1.1.0/go.mod h1:mK0aBveCxhnQ756AmaTfXMZDeULvheYVhF/MWMErN5g= -github.com/ProtonMail/gluon v0.14.2-0.20230105101243-675bf5daf3e6 h1:pGom2w5ncRNZf8+Z3IL4DQyTCI2UK8jhb6zkQVbVrLg= -github.com/ProtonMail/gluon v0.14.2-0.20230105101243-675bf5daf3e6/go.mod h1:z2AxLIiBCT1K+0OBHyaDI7AEaO5qI6/BEC2TE42vs4Q= github.com/ProtonMail/gluon v0.14.2-0.20230106095250-7e99ea4da61e h1://xRNjGTAMXw2U91MtqPc4krUtxQmt2+4z1oYrBaOWU= github.com/ProtonMail/gluon v0.14.2-0.20230106095250-7e99ea4da61e/go.mod h1:z2AxLIiBCT1K+0OBHyaDI7AEaO5qI6/BEC2TE42vs4Q= github.com/ProtonMail/go-autostart v0.0.0-20210130080809-00ed301c8e9a h1:D+aZah+k14Gn6kmL7eKxoo/4Dr/lK3ChBcwce2+SQP4= @@ -43,8 +41,8 @@ github.com/ProtonMail/go-message v0.0.0-20210611055058-fabeff2ec753/go.mod h1:NB github.com/ProtonMail/go-mime v0.0.0-20220302105931-303f85f7fe0f/go.mod h1:NYt+V3/4rEeDuaev/zw1zCq8uqVEuPHzDPo3OZrlGJ4= github.com/ProtonMail/go-mime v0.0.0-20220429130430-2192574d760f h1:4IWzKjHzZxdrW9k4zl/qCwenOVHDbVDADPPHFLjs0Oc= github.com/ProtonMail/go-mime v0.0.0-20220429130430-2192574d760f/go.mod h1:qRZgbeASl2a9OwmsV85aWwRqic0NHPh+9ewGAzb4cgM= -github.com/ProtonMail/go-proton-api v0.2.4-0.20230103140323-680d85d1c3f0 h1:0QzaGMTJsW+LT1+pCG93eYadIwZlgX2aNfwP3O83Mo0= -github.com/ProtonMail/go-proton-api v0.2.4-0.20230103140323-680d85d1c3f0/go.mod h1:JUo5IQG0hNuPRuDpOUsCOvtee6UjTEHHF1QN2i8RSos= +github.com/ProtonMail/go-proton-api v0.2.4-0.20230109130302-16194531370b h1:0uoTz1ZBTKpVazsBSt+i2MLupp6xIcv7Ay4KtROQUKU= +github.com/ProtonMail/go-proton-api v0.2.4-0.20230109130302-16194531370b/go.mod h1:JUo5IQG0hNuPRuDpOUsCOvtee6UjTEHHF1QN2i8RSos= github.com/ProtonMail/go-rfc5322 v0.11.0 h1:o5Obrm4DpmQEffvgsVqG6S4BKwC1Wat+hYwjIp2YcCY= github.com/ProtonMail/go-rfc5322 v0.11.0/go.mod h1:6oOKr0jXvpoE6pwTx/HukigQpX2J9WUf6h0auplrFTw= github.com/ProtonMail/go-srp v0.0.5 h1:xhUioxZgDbCnpo9JehyFhwwsn9JLWkUGfB0oiKXgiGg= diff --git a/tests/_features/smtp/auth.feature b/tests/_features/smtp/auth.feature deleted file mode 100644 index 7c4188d3..00000000 --- a/tests/_features/smtp/auth.feature +++ /dev/null @@ -1,65 +0,0 @@ -Feature: SMTP auth - Scenario: Ask EHLO - Given there is connected user "user" - When SMTP client sends "EHLO example.com" - Then SMTP response is "OK" - - Scenario: Authenticates successfully and EHLO successfully - Given there is connected user "user" - When SMTP client authenticates "user" - Then SMTP response is "OK" - When SMTP client sends "EHLO example.com" - Then SMTP response is "OK" - - Scenario: Authenticates with bad password - Given there is connected user "user" - When SMTP client authenticates "user" with bad password - Then SMTP response is "SMTP error: 454 4.7.0 backend/credentials: incorrect password" - - Scenario: Authenticates with disconnected user - Given there is disconnected user "user" - When SMTP client authenticates "user" - Then SMTP response is "SMTP error: 454 4.7.0 account is logged out, use the app to login again" - - Scenario: Authenticates with no user - When SMTP client authenticates with username "user@pm.me" and password "bridgepassword" - Then SMTP response is "SMTP error: 454 4.7.0 user user@pm.me not found" - - Scenario: Authenticates with capital letter - Given there is connected user "userAddressWithCapitalLetter" - When SMTP client authenticates "userAddressWithCapitalLetter" - Then SMTP response is "OK" - - Scenario: Authenticates with more addresses - primary one - Given there is connected user "userMoreAddresses" - When SMTP client authenticates "userMoreAddresses" with address "primary" - Then SMTP response is "OK" - - Scenario: Authenticates with more addresses - secondary one - Given there is connected user "userMoreAddresses" - When SMTP client authenticates "userMoreAddresses" with address "secondary" - Then SMTP response is "OK" - - Scenario: Authenticates with more addresses - disabled address - Given there is connected user "userMoreAddresses" - When SMTP client authenticates "userMoreAddresses" with address "disabled" - Then SMTP response is "SMTP error: 454 4.7.0 user .* not found" - - @ignore-live - Scenario: Authenticates with secondary address of account with disabled primary address - Given there is connected user "userDisabledPrimaryAddress" - When SMTP client authenticates "userDisabledPrimaryAddress" with address "secondary" - Then SMTP response is "OK" - - Scenario: Authenticates two users - Given there is connected user "user" - And there is connected user "userMoreAddresses" - When SMTP client "smtp1" authenticates "user" - Then SMTP response to "smtp1" is "OK" - When SMTP client "smtp2" authenticates "userMoreAddresses" with address "primary" - Then SMTP response to "smtp2" is "OK" - - Scenario: Logs out user - Given there is connected user "user" - When SMTP client logs out - Then SMTP response is "OK" diff --git a/tests/_features/smtp/init.feature b/tests/_features/smtp/init.feature deleted file mode 100644 index 056a504a..00000000 --- a/tests/_features/smtp/init.feature +++ /dev/null @@ -1,90 +0,0 @@ -Feature: SMTP initiation - Scenario: Empty FROM - Given there is connected user "user" - When SMTP client authenticates "user" - Then SMTP response is "OK" - When SMTP client sends "MAIL FROM:<>" - Then SMTP response is "OK" - - Scenario: Send without FROM and TO - Given there is connected user "user" - When SMTP client authenticates "user" - Then SMTP response is "OK" - When SMTP client sends "DATA" - Then SMTP response is "SMTP error: 502 5.5.1 Missing RCPT TO command." - - Scenario: Reset is the same as without FROM and TO - Given there is connected user "user" - When SMTP client authenticates "user" - Then SMTP response is "OK" - When SMTP client sends "MAIL FROM:<[userAddress]>" - Then SMTP response is "OK" - When SMTP client sends "RCPT TO:" - Then SMTP response is "OK" - When SMTP client sends "RSET" - Then SMTP response is "OK" - When SMTP client sends "DATA" - Then SMTP response is "SMTP error: 502 5.5.1 Missing RCPT TO command" - - Scenario: Send without FROM - Given there is connected user "user" - When SMTP client authenticates "user" - Then SMTP response is "OK" - When SMTP client sends "RCPT TO:" - Then SMTP response is "SMTP error: 502 5.5.1 Missing MAIL FROM command." - - Scenario: Send without TO - Given there is connected user "user" - When SMTP client authenticates "user" - Then SMTP response is "OK" - When SMTP client sends "MAIL FROM:<[userAddress]>" - Then SMTP response is "OK" - When SMTP client sends "DATA" - Then SMTP response is "SMTP error: 502 5.5.1 Missing RCPT TO command." - - Scenario: Send with empty FROM - Given there is connected user "user" - When SMTP client authenticates "user" - Then SMTP response is "OK" - When SMTP client sends "MAIL FROM:<>" - Then SMTP response is "OK" - When SMTP client sends "RCPT TO:" - Then SMTP response is "OK" - When SMTP client sends "DATA" - Then SMTP response is "OK" - When SMTP client sends "hello\r\n." - Then SMTP response is "SMTP error: 554 5.0.0 Error: transaction failed, blame it on the weather: missing return path" - - Scenario: Send with empty TO - Given there is connected user "user" - When SMTP client authenticates "user" - Then SMTP response is "OK" - When SMTP client sends "MAIL FROM:<[userAddress]>" - Then SMTP response is "OK" - When SMTP client sends "RCPT TO:<>" - Then SMTP response is "OK" - When SMTP client sends "DATA" - Then SMTP response is "OK" - When SMTP client sends "hello\r\n." - Then SMTP response is "SMTP error: 554 5.0.0 Error: transaction failed, blame it on the weather: missing recipient" - - Scenario: Allow BODY parameter of MAIL FROM command - Given there is connected user "user" - When SMTP client authenticates "user" - Then SMTP response is "OK" - When SMTP client sends "MAIL FROM:<[userAddress]> BODY=7BIT" - Then SMTP response is "OK" - - Scenario: Allow AUTH parameter of MAIL FROM command - Given there is connected user "user" - When SMTP client authenticates "user" - Then SMTP response is "OK" - When SMTP client sends "MAIL FROM:<[userAddress]> AUTH=<>" - Then SMTP response is "OK" - - Scenario: FROM not owned by user - Given there is connected user "user" - When SMTP client authenticates "user" - Then SMTP response is "OK" - When SMTP client sends "MAIL FROM:" - Then SMTP response is "SMTP error: 451 4.0.0 backend: invalid return path: not owned by user" diff --git a/tests/bdd_test.go b/tests/bdd_test.go index 5072f7f8..8612369e 100644 --- a/tests/bdd_test.go +++ b/tests/bdd_test.go @@ -110,7 +110,9 @@ func TestFeatures(testingT *testing.T) { // ==== SETUP ==== ctx.Step(`^there exists an account with username "([^"]*)" and password "([^"]*)"$`, s.thereExistsAnAccountWithUsernameAndPassword) + ctx.Step(`^there exists a disabled account with username "([^"]*)" and password "([^"]*)"$`, s.thereExistsAnAccountWithUsernameAndPasswordWithDisablePrimary) ctx.Step(`^the account "([^"]*)" has additional address "([^"]*)"$`, s.theAccountHasAdditionalAddress) + ctx.Step(`^the account "([^"]*)" has additional disabled address "([^"]*)"$`, s.theAccountHasAdditionalDisabledAddress) ctx.Step(`^the account "([^"]*)" has additional address "([^"]*)" without keys$`, s.theAccountHasAdditionalAddressWithoutKeys) ctx.Step(`^the account "([^"]*)" no longer has additional address "([^"]*)"$`, s.theAccountNoLongerHasAdditionalAddress) ctx.Step(`^the account "([^"]*)" has (\d+) custom folders$`, s.theAccountHasCustomFolders) @@ -220,6 +222,7 @@ func TestFeatures(testingT *testing.T) { ctx.Step(`^SMTP client "([^"]*)" sends DATA:$`, s.smtpClientSendsData) ctx.Step(`^SMTP client "([^"]*)" sends RSET$`, s.smtpClientSendsReset) ctx.Step(`^SMTP client "([^"]*)" sends the following message from "([^"]*)" to "([^"]*)":$`, s.smtpClientSendsTheFollowingMessageFromTo) + ctx.Step(`^SMTP client "([^"]*)" logs out$`, s.smtpClientLogsOut) }, Options: &godog.Options{ Format: "pretty", diff --git a/tests/features/smtp/auth.feature b/tests/features/smtp/auth.feature index 131cf69e..ea7afd5b 100644 --- a/tests/features/smtp/auth.feature +++ b/tests/features/smtp/auth.feature @@ -1,8 +1,15 @@ Feature: A user can authenticate an SMTP client Background: Given there exists an account with username "[user:user]" and password "password" + And there exists an account with username "[user2:user2]" and password "password2" + And there exists a disabled account with username "[user3:user3]" and password "password3" + And the account "[user:user]" has additional address "[alias:alias]@[domain]" + And the account "[user2:user2]" has additional disabled address "[alias2:alias2]@[domain]" + And the account "[user3:user3]" has additional address "[alias3:alias3]@[domain]" And bridge starts And the user logs in with username "[user:user]" and password "password" + And the user logs in with username "[user2:user2]" and password "password2" + And the user logs in with username "[user3:user3]" and password "password3" Scenario: SMTP client can authenticate successfully When user "[user:user]" connects SMTP client "1" @@ -19,4 +26,28 @@ Feature: A user can authenticate an SMTP client Scenario: SMTP client cannot authenticate for disconnected user When user "[user:user]" logs out And user "[user:user]" connects SMTP client "1" - Then SMTP client "1" cannot authenticate \ No newline at end of file + Then SMTP client "1" cannot authenticate + + Scenario: SMTP client can authenticate successfully with alias + When user "[user:user]" connects and authenticates SMTP client "1" with address "[alias:alias]@[domain]" + Then it succeeds + + Scenario: SMTP client can not authenticate with disabled address + When user "[user2:user2]" connects and authenticates SMTP client "1" with address "[alias2:alias2]@[domain]" + Then it fails + + Scenario: SMTP Logs out user + Given user "[user:user]" connects SMTP client "1" + When SMTP client "1" logs out + Then it succeeds + + Scenario: SMTP client can authenticate two users + When user "[user:user]" connects SMTP client "1" + Then SMTP client "1" can authenticate + When user "[user2:user2]" connects SMTP client "2" + Then SMTP client "2" can authenticate + + @ignore-live + Scenario: SMTP Authenticates with secondary address of account with disabled primary address + When user "[user3:user3]" connects and authenticates SMTP client "1" with address "[alias3:alias3]@[domain]" + Then it succeeds diff --git a/tests/smtp_test.go b/tests/smtp_test.go index 2f36f031..26c1fe29 100644 --- a/tests/smtp_test.go +++ b/tests/smtp_test.go @@ -157,3 +157,11 @@ func (s *scenario) smtpClientSendsTheFollowingMessageFromTo(clientID, from, to s return nil } + +func (s *scenario) smtpClientLogsOut(clientID string) error { + _, client := s.t.getSMTPClient(clientID) + + s.t.pushError(client.Quit()) + + return nil +} diff --git a/tests/user_test.go b/tests/user_test.go index 4bce22a9..5cd0b1d7 100644 --- a/tests/user_test.go +++ b/tests/user_test.go @@ -35,89 +35,19 @@ import ( ) func (s *scenario) thereExistsAnAccountWithUsernameAndPassword(username, password string) error { - // Create the user and generate its default address (with keys). - if _, err := s.t.runQuarkCmd( - context.Background(), - "user:create", - "--name", username, - "--password", password, - "--gen-keys", "RSA2048", - ); err != nil { - return err - } + return s.createUserAccount(username, password, false) +} - return s.t.withClientPass(context.Background(), username, password, func(ctx context.Context, c *proton.Client) error { - user, err := c.GetUser(ctx) - if err != nil { - return err - } - - // Decrypt the user's encrypted ID for use with quark. - userDecID, err := s.t.runQuarkCmd(context.Background(), "encryption:id", "--decrypt", user.ID) - if err != nil { - return err - } - - // Upgrade the user to a paid account. - if _, err := s.t.runQuarkCmd( - context.Background(), - "user:create:subscription", - "--planID", "plus", - string(userDecID), - ); err != nil { - return err - } - - addr, err := c.GetAddresses(ctx) - if err != nil { - return err - } - - // Set the ID of the user. - s.t.setUserID(username, user.ID) - - // Set the password of the user. - s.t.setUserPass(user.ID, password) - - // Set the address of the user. - s.t.setUserAddr(user.ID, addr[0].ID, addr[0].Email) - - return nil - }) +func (s *scenario) thereExistsAnAccountWithUsernameAndPasswordWithDisablePrimary(username, password string) error { + return s.createUserAccount(username, password, true) } func (s *scenario) theAccountHasAdditionalAddress(username, address string) error { - userID := s.t.getUserID(username) + return s.addAdditionalAddressToAccount(username, address, false) +} - // Decrypt the user's encrypted ID for use with quark. - userDecID, err := s.t.runQuarkCmd(context.Background(), "encryption:id", "--decrypt", userID) - if err != nil { - return err - } - - // Create the user's additional address. - if _, err := s.t.runQuarkCmd( - context.Background(), - "user:create:address", - "--gen-keys", "RSA2048", - string(userDecID), - s.t.getUserPass(userID), - address, - ); err != nil { - return err - } - - return s.t.withClient(context.Background(), username, func(ctx context.Context, c *proton.Client) error { - addr, err := c.GetAddresses(ctx) - if err != nil { - return err - } - - // Set the new address of the user. - s.t.setUserAddr(userID, addr[len(addr)-1].ID, address) - - return nil - }) +func (s *scenario) theAccountHasAdditionalDisabledAddress(username, address string) error { + return s.addAdditionalAddressToAccount(username, address, true) } func (s *scenario) theAccountHasAdditionalAddressWithoutKeys(username, address string) error { @@ -452,3 +382,109 @@ func (s *scenario) userIsNotListed(username string) error { func (s *scenario) userFinishesSyncing(username string) error { return s.bridgeSendsSyncStartedAndFinishedEventsForUser(username) } + +func (s *scenario) addAdditionalAddressToAccount(username, address string, disabled bool) error { + userID := s.t.getUserID(username) + + // Decrypt the user's encrypted ID for use with quark. + userDecID, err := s.t.runQuarkCmd(context.Background(), "encryption:id", "--decrypt", userID) + if err != nil { + return err + } + + args := []string{ + "--gen-keys", "RSA2048", + } + + if disabled { + args = append(args, "--status", "1") + } + + args = append(args, + string(userDecID), + s.t.getUserPass(userID), + address, + ) + + // Create the user's additional address. + if _, err := s.t.runQuarkCmd( + context.Background(), + "user:create:address", + args..., + ); err != nil { + return err + } + + return s.t.withClient(context.Background(), username, func(ctx context.Context, c *proton.Client) error { + addr, err := c.GetAddresses(ctx) + if err != nil { + return err + } + + // Set the new address of the user. + s.t.setUserAddr(userID, addr[len(addr)-1].ID, address) + + return nil + }) +} + +func (s *scenario) createUserAccount(username, password string, disabled bool) error { + // Create the user and generate its default address (with keys). + + args := []string{ + "--name", username, + "--password", password, + "--gen-keys", "RSA2048", + } + + if disabled { + args = append(args, "--status", "1") + } + + if _, err := s.t.runQuarkCmd( + context.Background(), + "user:create", + args..., + ); err != nil { + return err + } + + return s.t.withClientPass(context.Background(), username, password, func(ctx context.Context, c *proton.Client) error { + user, err := c.GetUser(ctx) + if err != nil { + return err + } + + // Decrypt the user's encrypted ID for use with quark. + userDecID, err := s.t.runQuarkCmd(context.Background(), "encryption:id", "--decrypt", user.ID) + if err != nil { + return err + } + + // Upgrade the user to a paid account. + if _, err := s.t.runQuarkCmd( + context.Background(), + "user:create:subscription", + "--planID", "plus", + string(userDecID), + ); err != nil { + return err + } + + addr, err := c.GetAddresses(ctx) + if err != nil { + return err + } + + // Set the ID of the user. + s.t.setUserID(username, user.ID) + + // Set the password of the user. + s.t.setUserPass(user.ID, password) + + // Set the address of the user. + s.t.setUserAddr(user.ID, addr[0].ID, addr[0].Email) + + return nil + }) +}