mhale / smtpd

An SMTP server package written in Go, in the style of the built-in HTTP server.
The Unlicense
397 stars 92 forks source link

Save state after authentication #19

Open zakcutner opened 4 years ago

zakcutner commented 4 years ago

Hello! Thanks for creating this library, it's really useful and I love the simplicity 👌 I'm looking at the example for authentication that you give in the readme...

func authHandler(remoteAddr net.Addr, mechanism string, username []byte, password []byte, shared []byte) (bool, error) {
    return string(username) == "valid" && string(password) == "password", nil
}

Is there a way to store some kind of state at authentication time that can later be used in a mail handler? That way you could, for instance, only allow sending emails that are from that user's account.

If not, do you have any ideas for how to implement this? I'd be really happy to help with an implementation 😁

(as a side-note, docs for this library can't be viewed at pkg.go.dev/github.com/mhale/smtpd because you don't have a license, you may want to create a simple LICENSE file)

mhale commented 4 years ago

You're welcome! Thanks for the tip about the LICENSE file, I've added one.

To implement that kind of state, assuming the username and MAIL FROM address were identical, the feature would need to store the allowed username(s) in the session, and set them in the auth-specific handlers after the username is decoded, and a flag to enable/disable the restriction on the server. If you'd like to send a PR, I'd be happy to look at it.

zakcutner commented 4 years ago

That's interesting, I was also looking at the approach used in github.com/emersion/go-smtp which seems slightly more generic and wouldn't require an identical username to the MAIL FROM address. However, implementing something like that would almost certainly be a breaking change 😕 I'll take a look some more at the code, thanks for getting back to me!

mhale commented 4 years ago

Another option (that would avoid a breaking change) is to add an optional handler for MAIL FROM that could check some kind of state (even in an external database) that was set in the auth handler.

ashishtiwari1993 commented 3 years ago

@mhale Thanks for this awesome library which helped me a lot !!!

@zakcutner I also faced the same challenge. I need my username and password on mailHandler. I added small patch to the library. I think similarly you can also keep your data across the session.

Check this commit https://github.com/ashishtiwari1993/smtpd/commit/c4067615ecefcfc54162416aedc664e0dfe8bf5a

@mhale I don't know if it is correct way or not but it's worked and still running on my production. It will be great if you can review it, Any suggestion will be appreciated.

Also let me know whether i can raise my PR with this commit.

Thanks

mhale commented 3 years ago

The problem with changing the Handler type like that (adding parameters) is that will break the code of the other users of this library. It might be better to define a new type with the extra parameters you want.

kunal-saini commented 3 years ago

Hey @mhale

I had the same use case and was looking for session values. I have forked and made relevant changes, could you please review them and let me know. to explain the changes I have added a map type (SessionValues) to new handlers and this can be used to set and get the values within a session.

here is the commit: https://github.com/kunal-saini/smtpd/commit/731b454dd4b70a593179563d88d49486cd1e663f and readme: https://github.com/kunal-saini/smtpd#session-example

let me know if it makes sense, I would raise a pull request!

fergtm commented 1 month ago

Hi @mhale

I also have the same need to store additional data with the session.

Since you don't want to modify existing handler types would you allow adding new handlers? for example, in addition to the existing AuthHandler we could add AuthHandlerWithSession and use the later if set.

Something like this:

type AuthHandlerWithSession func(remoteAddr net.Addr, mechanism string, username []byte, password []byte, shared []byte) (bool, interface{}, error)

type HandlerRcptWithSession func(remoteAddr net.Addr, from string, to string, session interface{}) bool

type HandlerWithSession func(remoteAddr net.Addr, from string, to []string, data []byte, session interface{}) error

I you agree with this I can submit a pull request.

Thanks

mhale commented 1 month ago

Hi, what's your use case here? Do you want to know the user's identity in your handler function?