mirror of
https://github.com/mjl-/mox.git
synced 2024-12-26 08:23:48 +03:00
fix login for account names with non-ascii chars
we include the username in session cookie values. but cookie values must be ascii-only, go's net/http's drops bad values. the typical solution is to querystring-encode/decode the cookie values, which we'll now do. problem found by arnt, thanks for reporting!
This commit is contained in:
parent
d74610c345
commit
666f84edea
4 changed files with 19 additions and 11 deletions
4
testdata/httpaccount/domains.conf
vendored
4
testdata/httpaccount/domains.conf
vendored
|
@ -1,11 +1,11 @@
|
|||
Domains:
|
||||
mox.example: nil
|
||||
Accounts:
|
||||
mjl:
|
||||
mjl☺:
|
||||
Domain: mox.example
|
||||
FullName: mjl
|
||||
Destinations:
|
||||
mjl@mox.example:
|
||||
mjl☺@mox.example:
|
||||
Mailbox: Inbox
|
||||
Rulesets:
|
||||
-
|
||||
|
|
2
testdata/httpaccount/mox.conf
vendored
2
testdata/httpaccount/mox.conf
vendored
|
@ -3,7 +3,7 @@ User: 1000
|
|||
LogLevel: trace
|
||||
Hostname: mox.example
|
||||
Postmaster:
|
||||
Account: mjl
|
||||
Account: mjl☺
|
||||
Mailbox: postmaster
|
||||
Listeners:
|
||||
local: nil
|
||||
|
|
|
@ -79,7 +79,7 @@ func TestAccount(t *testing.T) {
|
|||
mox.ConfigDynamicPath = filepath.Join(filepath.Dir(mox.ConfigStaticPath), "domains.conf")
|
||||
mox.MustLoadConfig(true, false)
|
||||
log := mlog.New("webaccount", nil)
|
||||
acc, err := store.OpenAccount(log, "mjl")
|
||||
acc, err := store.OpenAccount(log, "mjl☺")
|
||||
tcheck(t, err, "open account")
|
||||
err = acc.SetPassword(log, "test1234")
|
||||
tcheck(t, err, "set password")
|
||||
|
@ -99,14 +99,14 @@ func TestAccount(t *testing.T) {
|
|||
ctx := context.WithValue(ctxbg, requestInfoCtxKey, reqInfo)
|
||||
|
||||
// Missing login token.
|
||||
tneedErrorCode(t, "user:error", func() { api.Login(ctx, "", "mjl@mox.example", "test1234") })
|
||||
tneedErrorCode(t, "user:error", func() { api.Login(ctx, "", "mjl☺@mox.example", "test1234") })
|
||||
|
||||
// Login with loginToken.
|
||||
loginCookie := &http.Cookie{Name: "webaccountlogin"}
|
||||
loginCookie.Value = api.LoginPrep(ctx)
|
||||
reqInfo.Request.Header = http.Header{"Cookie": []string{loginCookie.String()}}
|
||||
|
||||
csrfToken := api.Login(ctx, loginCookie.Value, "mjl@mox.example", "test1234")
|
||||
csrfToken := api.Login(ctx, loginCookie.Value, "mjl☺@mox.example", "test1234")
|
||||
var sessionCookie *http.Cookie
|
||||
for _, c := range respRec.Result().Cookies() {
|
||||
if c.Name == "webaccountsession" {
|
||||
|
@ -121,7 +121,7 @@ func TestAccount(t *testing.T) {
|
|||
// Valid loginToken, but bad credentials.
|
||||
loginCookie.Value = api.LoginPrep(ctx)
|
||||
reqInfo.Request.Header = http.Header{"Cookie": []string{loginCookie.String()}}
|
||||
tneedErrorCode(t, "user:loginFailed", func() { api.Login(ctx, loginCookie.Value, "mjl@mox.example", "badauth") })
|
||||
tneedErrorCode(t, "user:loginFailed", func() { api.Login(ctx, loginCookie.Value, "mjl☺@mox.example", "badauth") })
|
||||
tneedErrorCode(t, "user:loginFailed", func() { api.Login(ctx, loginCookie.Value, "baduser@mox.example", "badauth") })
|
||||
tneedErrorCode(t, "user:loginFailed", func() { api.Login(ctx, loginCookie.Value, "baduser@baddomain.example", "badauth") })
|
||||
|
||||
|
@ -211,13 +211,13 @@ func TestAccount(t *testing.T) {
|
|||
|
||||
// SetPassword needs the token.
|
||||
sessionToken := store.SessionToken(strings.SplitN(sessionCookie.Value, " ", 2)[0])
|
||||
reqInfo = requestInfo{"mjl@mox.example", "mjl", sessionToken, respRec, &http.Request{RemoteAddr: "127.0.0.1:1234"}}
|
||||
reqInfo = requestInfo{"mjl☺@mox.example", "mjl☺", sessionToken, respRec, &http.Request{RemoteAddr: "127.0.0.1:1234"}}
|
||||
ctx = context.WithValue(ctxbg, requestInfoCtxKey, reqInfo)
|
||||
|
||||
api.SetPassword(ctx, "test1234")
|
||||
|
||||
fullName, _, dests, _, _ := api.Account(ctx)
|
||||
api.DestinationSave(ctx, "mjl@mox.example", dests["mjl@mox.example"], dests["mjl@mox.example"]) // todo: save modified value and compare it afterwards
|
||||
api.DestinationSave(ctx, "mjl☺@mox.example", dests["mjl☺@mox.example"], dests["mjl☺@mox.example"]) // todo: save modified value and compare it afterwards
|
||||
|
||||
api.AccountSaveFullName(ctx, fullName+" changed") // todo: check if value was changed
|
||||
api.AccountSaveFullName(ctx, fullName)
|
||||
|
|
|
@ -41,6 +41,7 @@ import (
|
|||
"log/slog"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
|
@ -154,9 +155,15 @@ func Check(ctx context.Context, log mlog.Log, sessionAuth SessionAuth, kind stri
|
|||
return "", "", "", false
|
||||
}
|
||||
sessionToken = store.SessionToken(t[0])
|
||||
accountName = t[1]
|
||||
|
||||
var err error
|
||||
accountName, err = url.QueryUnescape(t[1])
|
||||
if err != nil {
|
||||
time.Sleep(BadAuthDelay)
|
||||
respondAuthError("user:badAuth", "malformed session account name")
|
||||
return "", "", "", false
|
||||
}
|
||||
|
||||
loginAddress, err = sessionAuth.use(ctx, log, accountName, sessionToken, csrfToken)
|
||||
if err != nil {
|
||||
time.Sleep(BadAuthDelay)
|
||||
|
@ -258,7 +265,8 @@ func Login(ctx context.Context, log mlog.Log, sessionAuth SessionAuth, kind, coo
|
|||
// Add session cookie.
|
||||
http.SetCookie(w, &http.Cookie{
|
||||
Name: kind + "session",
|
||||
Value: string(sessionToken) + " " + accountName,
|
||||
// Cookies values are ascii only, so we keep the account name query escaped.
|
||||
Value: string(sessionToken) + " " + url.QueryEscape(accountName),
|
||||
Path: cookiePath,
|
||||
Secure: isHTTPS(isForwarded, r),
|
||||
HttpOnly: true,
|
||||
|
|
Loading…
Reference in a new issue