101 lines
3.0 KiB
Go
101 lines
3.0 KiB
Go
package handlers
|
|
|
|
import (
|
|
"net/http"
|
|
"strings"
|
|
|
|
"amnezia-share/internal/auth"
|
|
"amnezia-share/internal/settings"
|
|
"amnezia-share/internal/web"
|
|
)
|
|
|
|
func registerLogin(a *web.App, mux *http.ServeMux) {
|
|
mux.HandleFunc("GET /login", LoginForm(a))
|
|
mux.HandleFunc("POST /login", a.CSRFProtect(LoginSubmit(a)))
|
|
mux.HandleFunc("GET /logout", Logout(a))
|
|
}
|
|
|
|
// oidcConfigFromSettings loads the Pocket ID OIDC settings (shared by login.go
|
|
// and oidc.go).
|
|
func oidcConfigFromSettings(a *web.App, r *http.Request) auth.OIDCConfig {
|
|
ctx := r.Context()
|
|
vals, _ := a.Settings.GetMany(ctx, settings.KeyPocketURL, settings.KeyPocketClientID, settings.KeyPocketSecret)
|
|
return auth.OIDCConfig{
|
|
URL: vals[settings.KeyPocketURL],
|
|
ClientID: vals[settings.KeyPocketClientID],
|
|
ClientSecret: vals[settings.KeyPocketSecret],
|
|
}
|
|
}
|
|
|
|
type loginView struct {
|
|
Error string
|
|
PocketEnabled bool
|
|
NoAdmins bool
|
|
}
|
|
|
|
// LoginForm renders the admin login page, or starts the Pocket ID SSO flow when
|
|
// called as GET /login?oidc=1 (login.php).
|
|
func LoginForm(a *web.App) http.HandlerFunc {
|
|
return func(w http.ResponseWriter, r *http.Request) {
|
|
ctx := r.Context()
|
|
if a.CurrentAdmin(r) != nil {
|
|
redirectTo(w, r, a, "admin")
|
|
return
|
|
}
|
|
|
|
cfg := oidcConfigFromSettings(a, r)
|
|
pocketEnabled := cfg.Configured()
|
|
|
|
if pocketEnabled && r.URL.Query().Has("oidc") {
|
|
state, err := auth.GenerateState()
|
|
if err != nil {
|
|
http.Error(w, "не удалось начать вход", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
verifier, challenge, err := auth.GeneratePKCE()
|
|
if err != nil {
|
|
http.Error(w, "не удалось начать вход", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
a.Sessions.Put(ctx, "oidc_state", state)
|
|
a.Sessions.Put(ctx, "oidc_verifier", verifier)
|
|
redirectURI := a.AbsoluteURL(r, "oidc/callback")
|
|
http.Redirect(w, r, auth.AuthURL(cfg, redirectURI, state, challenge), http.StatusSeeOther)
|
|
return
|
|
}
|
|
|
|
count, _ := a.Auth.AdminCount(ctx)
|
|
a.Render(w, r, "page_login", loginView{PocketEnabled: pocketEnabled, NoAdmins: count == 0})
|
|
}
|
|
}
|
|
|
|
// LoginSubmit verifies username/password and signs the admin in (login.php POST).
|
|
func LoginSubmit(a *web.App) http.HandlerFunc {
|
|
return func(w http.ResponseWriter, r *http.Request) {
|
|
ctx := r.Context()
|
|
username := strings.TrimSpace(r.FormValue("username"))
|
|
password := r.FormValue("password")
|
|
|
|
admin, err := a.Auth.LoginAdmin(ctx, username, password)
|
|
if err != nil {
|
|
cfg := oidcConfigFromSettings(a, r)
|
|
count, _ := a.Auth.AdminCount(ctx)
|
|
a.Render(w, r, "page_login", loginView{Error: err.Error(), PocketEnabled: cfg.Configured(), NoAdmins: count == 0})
|
|
return
|
|
}
|
|
|
|
_ = a.Sessions.RenewToken(ctx)
|
|
a.Sessions.Put(ctx, web.SessionAdminID, admin.ID)
|
|
redirectTo(w, r, a, "admin")
|
|
}
|
|
}
|
|
|
|
// Logout clears the admin session (logout.php).
|
|
func Logout(a *web.App) http.HandlerFunc {
|
|
return func(w http.ResponseWriter, r *http.Request) {
|
|
a.Sessions.Remove(r.Context(), web.SessionAdminID)
|
|
_ = a.Sessions.RenewToken(r.Context())
|
|
redirectTo(w, r, a, "login")
|
|
}
|
|
}
|