2022-02-21 09:55:02 +00:00
|
|
|
package auth
|
|
|
|
|
|
|
|
import (
|
|
|
|
"net/http"
|
|
|
|
|
2022-12-01 11:00:42 +00:00
|
|
|
"github.com/rs/zerolog/log"
|
|
|
|
|
2022-02-21 09:55:02 +00:00
|
|
|
"github.com/emersion/go-imap/client"
|
|
|
|
"github.com/emersion/go-sasl"
|
|
|
|
)
|
|
|
|
|
|
|
|
type IMAPProvider struct {
|
|
|
|
addr string
|
|
|
|
tls bool
|
|
|
|
}
|
|
|
|
|
|
|
|
// Initializes a new IMAP auth provider with the given connection string.
|
|
|
|
func NewIMAP(addr string, tls bool) AuthProvider {
|
|
|
|
prov := &IMAPProvider{addr, tls}
|
|
|
|
conn, err := prov.dial()
|
|
|
|
if err != nil {
|
2022-12-01 11:00:42 +00:00
|
|
|
log.Fatal().Err(err).Msg("error dialing configured IMAP auth server")
|
2022-02-21 09:55:02 +00:00
|
|
|
}
|
|
|
|
conn.Close()
|
|
|
|
return prov
|
|
|
|
}
|
|
|
|
|
|
|
|
func (prov *IMAPProvider) Middleware() func(http.Handler) http.Handler {
|
2022-02-21 10:11:27 +00:00
|
|
|
return func(next http.Handler) http.Handler {
|
2022-02-21 09:55:02 +00:00
|
|
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
prov.doAuth(next, w, r)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (prov *IMAPProvider) doAuth(next http.Handler,
|
|
|
|
w http.ResponseWriter, r *http.Request) {
|
|
|
|
user, pass, ok := r.BasicAuth()
|
|
|
|
if !ok {
|
|
|
|
w.Header().Add("WWW-Authenticate", `Basic realm="Please provide your IMAP credentials", charset="UTF-8"`)
|
|
|
|
http.Error(w, "HTTP Basic auth is required", http.StatusUnauthorized)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
conn, err := prov.dial()
|
|
|
|
if err != nil {
|
2024-02-05 21:23:58 +00:00
|
|
|
log.Warn().Err(err).Msg("auth dial error")
|
2022-02-21 09:55:02 +00:00
|
|
|
http.Error(w, "Temporary authentication error, try again later", http.StatusServiceUnavailable)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
defer conn.Close()
|
|
|
|
|
|
|
|
auth := sasl.NewPlainClient("", user, pass)
|
|
|
|
if err := conn.Authenticate(auth); err != nil {
|
2024-02-05 21:23:58 +00:00
|
|
|
log.Debug().Str("user", user).Err(err).Msg("auth error")
|
2022-02-21 09:55:02 +00:00
|
|
|
http.Error(w, "Invalid username or password", http.StatusUnauthorized)
|
|
|
|
return
|
|
|
|
}
|
2022-05-24 11:34:29 +01:00
|
|
|
conn.Close()
|
2022-02-21 09:55:02 +00:00
|
|
|
|
2022-02-23 12:42:28 +00:00
|
|
|
authCtx := AuthContext{
|
|
|
|
AuthMethod: "imap",
|
|
|
|
UserName: user,
|
|
|
|
}
|
2022-03-16 13:33:47 +00:00
|
|
|
ctx := NewContext(r.Context(), &authCtx)
|
2022-02-23 12:42:28 +00:00
|
|
|
r = r.WithContext(ctx)
|
2022-02-21 09:55:02 +00:00
|
|
|
next.ServeHTTP(w, r)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (prov *IMAPProvider) dial() (*client.Client, error) {
|
|
|
|
if prov.tls {
|
|
|
|
return client.DialTLS(prov.addr, nil)
|
|
|
|
} else {
|
|
|
|
return client.Dial(prov.addr)
|
|
|
|
}
|
|
|
|
}
|