Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/107 no auth connect #126

Open
wants to merge 4 commits into
base: master
Choose a base branch
from

Conversation

sbeitzel
Copy link

Fix issue #107 by only trying to authenticate if the server advertises the AUTH extended capability.

Description

During socket initialization, we will check to see if the server even supports AUTH before we attempt to perform a login.

Motivation and Context

Not every SMTP server requires authentication. If you try to use this library to connect to one such, you will have no luck. A trivial real-world use case for this would be a docker container that's running an open SMTP relay but which is only accessible to an application server running in a different container. It seems strange for an SMTP client library to enforce a server's authentication policy, and to limit itself to connecting only to servers which support an optional command.

Fixes issue #107

How Has This Been Tested?

Running an open, "dummy" SMTP server locally, I tried to connect an SMTPSocket to it. Initially, since the server did not advertise an AUTH capability, this threw an exception. After the change, the connection succeeded.

Checklist:

  • I have submitted a CLA form
  • If applicable, I have added tests to cover my changes.

Copy link
Member

@mbarnach mbarnach left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for that!
I think a new init could make it more obvious of what's happening.

@@ -49,7 +49,9 @@ struct SMTPSocket {
}
}
let authMethod = try getAuthMethod(authMethods: authMethods, serverOptions: serverOptions, hostname: hostname)
try login(authMethod: authMethod, email: email, password: password)
if authMethod != .none {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would advocate for a dedicated init, since in that case, the password is useless and the email also. We do not have to specify the authMethods neither if I get it right in such case (must be none).

The functions getAuthMethod and login are useless in the init in the none case.
Maybe a minor refactoring of the connection part (socket and server options) into a dedicated (private) function could keep it cleared and avoid duplication.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see your point, and it makes sense.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here's a difficulty: consumers of this library won't (normally) be calling SMTPSocket(...) directly, they'll be instantiating an SMTP struct. That struct is what constructs the socket -- so, yes, having two different init methods on the socket definitely makes clear what's going on, but it means that upstream, in the SMTP struct, we'll need to gather that choice. Now, since the struct needs to support both ways of working, what do we do?

One approach would be to add a boolean such as useAuthentication and have it default to true. This would allow existing users of the library to keep their code unmodified and working correctly.

Another would be to add a second initializer to the struct, to match the two-init pattern in the socket, and then in the struct's send method we check to see which socket initializer to use.

Have you got a preference?

let hostname = "mail.kitura.dev"
let myEmail: String? = nil
let myEmail: String? = "tester@local"
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need to revert this, sorry

@@ -49,7 +49,9 @@ struct SMTPSocket {
}
}
let authMethod = try getAuthMethod(authMethods: authMethods, serverOptions: serverOptions, hostname: hostname)
try login(authMethod: authMethod, email: email, password: password)
if authMethod != .none {
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see your point, and it makes sense.

Copy link
Member

@mbarnach mbarnach left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The API looks much nicer in my opinion like that! 🎉
👍

@dannys42
Copy link
Contributor

Thanks for the submission @sbeitzel and for your review @mbarnach. Cursory glance looks okay to me, I'll aim to look more closely this weekend if that's okay for you. Please don't worry if I close/open the PR, which I'll probably have to do to ensure travis is integrated correctly. It might take a little extra time since I just realized something happened to the server I was using as a docker registry and will have to rebuild it.

@dannys42 dannys42 closed this Feb 6, 2022
@dannys42 dannys42 reopened this Feb 6, 2022
@dannys42
Copy link
Contributor

dannys42 commented Feb 7, 2022

@sbeitzel I had to make some changes to CI, would you mind rebasing?

@sonarcloud
Copy link

sonarcloud bot commented Feb 12, 2022

Kudos, SonarCloud Quality Gate passed!    Quality Gate passed

Bug A 0 Bugs
Vulnerability A 0 Vulnerabilities
Security Hotspot A 0 Security Hotspots
Code Smell A 0 Code Smells

No Coverage information No Coverage information
0.0% 0.0% Duplication

@Juice805
Copy link

In some cases a server can support authenticated and unauthenticated connections. Does this change support unauthenticated connections to servers which support both?

Not a contribution

@sbeitzel
Copy link
Author

Without a test to prove it, I can only say, "Sure, should do." Note that the calling code still needs to set up the SMTP instance, and it's the initializer of SMTP that sets up authentication. So, the calling code needs to be aware of whether it's trying to authenticate or not.

@@ -159,7 +194,13 @@ private extension SMTPSocket {
}
}
}
throw SMTPError.noAuthMethodsOrRequiresTLS(hostname: hostname)
if requiresAuth {
Copy link

@Juice805 Juice805 Mar 8, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I gave it a test on a server that supports auth, but does not require it. This check prevents sending.

It looks like you'll need to call the correct SMTPSocket.init from within SMTP.send for those using the new SMTP.init.

Not a contribution

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants