2024-10-20 18:05:13 +00:00
|
|
|
package server
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/json"
|
|
|
|
"errors"
|
|
|
|
"io"
|
|
|
|
"net/http"
|
|
|
|
"strings"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/rs/zerolog/hlog"
|
|
|
|
"gitlab.com/mstarongitlab/goutils/other"
|
|
|
|
|
|
|
|
"gitlab.com/mstarongitlab/linstrom/storage"
|
|
|
|
)
|
|
|
|
|
|
|
|
func forceCorrectPasskeyAuthFlowMiddleware(
|
|
|
|
handler http.Handler,
|
|
|
|
) http.Handler {
|
|
|
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
log := hlog.FromRequest(r)
|
|
|
|
// Don't fuck with the request if not intended for starting to register or login
|
|
|
|
if strings.HasSuffix(r.URL.Path, "loginFinish") {
|
|
|
|
log.Debug().Msg("Request to finish login method, doing nothing")
|
|
|
|
handler.ServeHTTP(w, r)
|
|
|
|
return
|
|
|
|
} else if strings.HasSuffix(r.URL.Path, "registerFinish") {
|
|
|
|
handler.ServeHTTP(w, r)
|
|
|
|
// Force unset session cookie here
|
|
|
|
w.Header().Del("Set-Cookie")
|
|
|
|
http.SetCookie(w, &http.Cookie{
|
|
|
|
Name: "sid",
|
|
|
|
Value: "",
|
|
|
|
Path: "",
|
|
|
|
MaxAge: 0,
|
|
|
|
Expires: time.UnixMilli(0),
|
|
|
|
})
|
|
|
|
return
|
|
|
|
} else if strings.HasSuffix(r.URL.Path, "loginBegin") {
|
|
|
|
fuckWithLoginRequest(w, r, handler)
|
|
|
|
} else if strings.HasSuffix(r.URL.Path, "registerBegin") {
|
|
|
|
fuckWithRegisterRequest(w, r, handler)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
func fuckWithRegisterRequest(
|
|
|
|
w http.ResponseWriter,
|
|
|
|
r *http.Request,
|
|
|
|
nextHandler http.Handler,
|
|
|
|
) {
|
|
|
|
log := hlog.FromRequest(r)
|
|
|
|
log.Debug().Msg("Messing with register start request")
|
2024-10-31 15:53:42 +00:00
|
|
|
store := StorageFromRequest(r)
|
2024-10-20 18:05:13 +00:00
|
|
|
if store == nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
cookie, cookieErr := r.Cookie("sid")
|
|
|
|
var username struct {
|
|
|
|
Username string `json:"username"`
|
|
|
|
}
|
|
|
|
body, _ := io.ReadAll(r.Body)
|
|
|
|
log.Debug().Bytes("body", body).Msg("Body of auth begin request")
|
|
|
|
err := json.Unmarshal(body, &username)
|
|
|
|
if err != nil {
|
|
|
|
other.HttpErr(w, HttpErrIdBadRequest, "Not a username json object", http.StatusBadRequest)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if cookieErr == nil {
|
|
|
|
// Already authenticated, overwrite username to logged in account's name
|
|
|
|
// Get session from cookie
|
|
|
|
log.Debug().Msg("Session token exists, force overwriting username of register request")
|
|
|
|
session, ok := store.GetSession(cookie.Value)
|
|
|
|
if !ok {
|
|
|
|
log.Error().Str("session-id", cookie.Value).Msg("Passkey session missing")
|
|
|
|
other.HttpErr(
|
|
|
|
w,
|
|
|
|
HttpErrIdDbFailure,
|
|
|
|
"Passkey session missing",
|
|
|
|
http.StatusInternalServerError,
|
|
|
|
)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
acc, err := store.FindAccountByPasskeyId(session.UserID)
|
|
|
|
// Assume account must exist if a session for it exists
|
|
|
|
if err != nil {
|
|
|
|
log.Error().Err(err).Msg("Failed to get account from passkey id from session")
|
|
|
|
other.HttpErr(
|
|
|
|
w,
|
|
|
|
HttpErrIdDbFailure,
|
|
|
|
"Failed to get authenticated account",
|
|
|
|
http.StatusInternalServerError,
|
|
|
|
)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
// Replace whatever username may be given with username of logged in account
|
|
|
|
newBody := strings.ReplaceAll(string(body), username.Username, acc.Username)
|
|
|
|
// Assign to request
|
|
|
|
r.Body = io.NopCloser(strings.NewReader(newBody))
|
|
|
|
r.ContentLength = int64(len(newBody))
|
|
|
|
// And pass on
|
|
|
|
nextHandler.ServeHTTP(w, r)
|
|
|
|
} else {
|
|
|
|
// Not authenticated, ensure that no existing name is registered with
|
|
|
|
_, err = store.FindLocalAccountByUsername(username.Username)
|
|
|
|
switch err {
|
|
|
|
case nil:
|
|
|
|
// No error while getting account means account exists, refuse access
|
|
|
|
log.Info().
|
|
|
|
Str("username", username.Username).
|
|
|
|
Msg("Account with same name already exists, preventing login")
|
|
|
|
other.HttpErr(
|
|
|
|
w,
|
|
|
|
HttpErrIdAlreadyExists,
|
|
|
|
"Account with that name already exists",
|
|
|
|
http.StatusBadRequest,
|
|
|
|
)
|
|
|
|
case storage.ErrEntryNotFound:
|
|
|
|
// Didn't find account with that name, give access
|
|
|
|
log.Debug().
|
|
|
|
Str("username", username.Username).
|
|
|
|
Msg("No account with this username exists yet, passing through")
|
|
|
|
// Copy original body since previous reader hit EOF
|
|
|
|
r.Body = io.NopCloser(strings.NewReader(string(body)))
|
|
|
|
r.ContentLength = int64(len(body))
|
|
|
|
nextHandler.ServeHTTP(w, r)
|
|
|
|
default:
|
|
|
|
// Some other error, log it and return appropriate message
|
|
|
|
log.Error().
|
|
|
|
Err(err).
|
|
|
|
Str("username", username.Username).
|
|
|
|
Msg("Failed to check if account with username already exists")
|
|
|
|
other.HttpErr(
|
|
|
|
w,
|
|
|
|
HttpErrIdDbFailure,
|
|
|
|
"Failed to check if account with that name already exists",
|
|
|
|
http.StatusInternalServerError,
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func fuckWithLoginRequest(
|
|
|
|
w http.ResponseWriter,
|
|
|
|
r *http.Request,
|
|
|
|
nextHandler http.Handler,
|
|
|
|
) {
|
|
|
|
log := hlog.FromRequest(r)
|
|
|
|
log.Debug().Msg("Messing with login start request")
|
2024-10-31 15:53:42 +00:00
|
|
|
store := StorageFromRequest(r)
|
2024-10-20 18:05:13 +00:00
|
|
|
if store == nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
cookie, cookieErr := r.Cookie("sid")
|
|
|
|
var username struct {
|
|
|
|
Username string `json:"username"`
|
|
|
|
}
|
|
|
|
// Force ignore cookie for now
|
|
|
|
_ = cookieErr
|
|
|
|
var err error = errors.New("placeholder")
|
|
|
|
if err == nil {
|
|
|
|
// Someone is logged in, overwrite username with logged in account's one
|
|
|
|
body, _ := io.ReadAll(r.Body)
|
|
|
|
log.Debug().Bytes("body", body).Msg("Body of auth begin request")
|
|
|
|
err := json.Unmarshal(body, &username)
|
|
|
|
if err != nil {
|
|
|
|
other.HttpErr(
|
|
|
|
w,
|
|
|
|
HttpErrIdBadRequest,
|
|
|
|
"Not a username json object",
|
|
|
|
http.StatusBadRequest,
|
|
|
|
)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
session, ok := store.GetSession(cookie.Value)
|
|
|
|
if !ok {
|
|
|
|
log.Error().Str("session-id", cookie.Value).Msg("Passkey session missing")
|
|
|
|
other.HttpErr(
|
|
|
|
w,
|
|
|
|
HttpErrIdDbFailure,
|
|
|
|
"Passkey session missing",
|
|
|
|
http.StatusInternalServerError,
|
|
|
|
)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
acc, err := store.FindAccountByPasskeyId(session.UserID)
|
|
|
|
// Assume account must exist if a session for it exists
|
|
|
|
if err != nil {
|
|
|
|
log.Error().Err(err).Msg("Failed to get account from passkey id from session")
|
|
|
|
other.HttpErr(
|
|
|
|
w,
|
|
|
|
HttpErrIdDbFailure,
|
|
|
|
"Failed to get authenticated account",
|
|
|
|
http.StatusInternalServerError,
|
|
|
|
)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
// Replace whatever username may be given with username of logged in account
|
|
|
|
newBody := strings.ReplaceAll(string(body), username.Username, acc.Username)
|
|
|
|
// Assign to request
|
|
|
|
r.Body = io.NopCloser(strings.NewReader(newBody))
|
|
|
|
r.ContentLength = int64(len(newBody))
|
|
|
|
// And pass on
|
|
|
|
nextHandler.ServeHTTP(w, r)
|
|
|
|
} else {
|
|
|
|
// No one logged in, check if user exists to prevent creating a bugged account
|
|
|
|
body, _ := io.ReadAll(r.Body)
|
|
|
|
log.Debug().Bytes("body", body).Msg("Body of auth begin request")
|
|
|
|
err := json.Unmarshal(body, &username)
|
|
|
|
if err != nil {
|
|
|
|
other.HttpErr(w, HttpErrIdBadRequest, "Not a username json object", http.StatusBadRequest)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
_, err = store.FindLocalAccountByUsername(username.Username)
|
|
|
|
switch err {
|
|
|
|
case nil:
|
|
|
|
// All good, account exists, keep going
|
|
|
|
// Do nothing in this branch
|
|
|
|
case storage.ErrEntryNotFound:
|
|
|
|
// Account doesn't exist, catch it
|
|
|
|
other.HttpErr(w, HttpErrIdNotFound, "Username not found", http.StatusNotFound)
|
|
|
|
return
|
|
|
|
default:
|
|
|
|
// catch db failures
|
|
|
|
log.Error().Err(err).Str("username", username.Username).Msg("Db failure while getting account")
|
|
|
|
other.HttpErr(w, HttpErrIdDbFailure, "Failed to check for account in db", http.StatusInternalServerError)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
// Restore body as new reader of the same content
|
|
|
|
r.Body = io.NopCloser(strings.NewReader(string(body)))
|
|
|
|
nextHandler.ServeHTTP(w, r)
|
|
|
|
}
|
|
|
|
}
|