From bd88166988bb31ff82da847d1e69c907f5f53930 Mon Sep 17 00:00:00 2001 From: jan Date: Sun, 20 Dec 2015 17:28:22 +0100 Subject: registration. yay. diff --git a/charakterin.go b/charakterin.go index abf9627..142484c 100644 --- a/charakterin.go +++ b/charakterin.go @@ -12,10 +12,19 @@ import ( _ "github.com/lib/pq" ) +const ( + NoSuchUser = "pq: no_such_user" + InvalidPassword = "pq: invalid_password" + UsernameTaken = "pq: username_taken" + EmailAlreadyRegistered = "pq: email_already_registered" +) + // Renderer wird verwendet, um die Routen (bspw. Login-Route) zu rendern. Damit bleibt Charakterin selbst ohne Template. type Renderer interface { // RenderLoginPage zeigt die Login-Seite an. RenderLoginPage(w http.ResponseWriter, data map[string]interface{}) + // RenderRegistrationPage zeigt die Registrations-Seite an. + RenderRegistrationPage(w http.ResponseWriter, data map[string]interface{}) } // Charakterin ist das tolle Login- und Accountmanagementsystem. @@ -70,33 +79,35 @@ func (c *Charakterin) DisplayLogin(w http.ResponseWriter, r *http.Request) { c.DisplayLoginWithData(w, r, make(map[string]interface{})) } -// Login versucht einen User einzuloggen. +// LoginRequest versucht, einen Benutzer mit den gegebenen Daten einzuloggen. +func (c *Charakterin) LoginRequest(username, password string) (string, error) { + var result string + err := c.Database.QueryRow("SELECT * FROM login.new_session($1, $2)", username, password).Scan(&result) + if err != nil { + return "", err + } + return result, nil +} + +// Login versucht einen User durch einen Request einzuloggen. func (c *Charakterin) Login(w http.ResponseWriter, r *http.Request) { if r.Method != "POST" { return } // POST-Data lesen - defer r.Body.Close() - data, err := ioutil.ReadAll(r.Body) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - - values, err := url.ParseQuery(string(data)) + values, err := readBody(r) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } username := values.Get("username") - var result string - err = c.Database.QueryRow("SELECT * FROM login.new_session($1, $2)", username, values.Get("password")).Scan(&result) + session, err := c.LoginRequest(username, values.Get("password")) if err != nil { errStr := err.Error() - if errStr == "pq: no_such_user" || errStr == "pq: invalid_password" { + if errStr == NoSuchUser || errStr == InvalidPassword { log.Printf("invalid login attempt by '%s': %s\n", username, errStr[4:]) data := make(map[string]interface{}) data["previous_user"] = username @@ -111,7 +122,7 @@ func (c *Charakterin) Login(w http.ResponseWriter, r *http.Request) { http.SetCookie(w, &http.Cookie{ Name: "session", - Value: result, + Value: session, Expires: time.Now().AddDate(1, 0, 0), }) http.Redirect(w, r, c.FallbackRoute, 302) @@ -119,32 +130,37 @@ func (c *Charakterin) Login(w http.ResponseWriter, r *http.Request) { // Logout loggt einen Charakter aus. Wird direkt über den Request gehandlet. func (c *Charakterin) Logout(w http.ResponseWriter, r *http.Request) { - user, err := c.GetUserFromRequest(r) + cookie, err := r.Cookie("session") if err != nil { http.Redirect(w, r, c.FallbackRoute, 302) return } - + stmt, err := c.Database.Prepare("DELETE FROM login.sessions WHERE id=$1") if err != nil { http.Error(w, "500", http.StatusInternalServerError) return } - - result, err := stmt.Exec(user.SessionID) + + result, err := stmt.Exec(cookie.Value) if err != nil { log.Println(err) http.Error(w, "500", http.StatusInternalServerError) return } - + if val, err := result.RowsAffected(); err != nil || val == 0 { - log.Println("could not remove session",user.SessionID,err) + log.Println("could not remove session", cookie.Value, err) http.Redirect(w, r, c.FallbackRoute, 302) return } - - user.Logout(w) + + http.SetCookie(w, &http.Cookie{ + Name: "session", + Value: "benis", + Expires: time.Now(), + MaxAge: 0, + }) http.Redirect(w, r, c.FallbackRoute, 302) } @@ -165,6 +181,96 @@ func (c *Charakterin) IsLoggedIn(r *http.Request) bool { return true } +// DisplayRegistrationWithData rendert die Registration mit Daten (vorheriger Benutzer, Fehlermeldung) +func (c *Charakterin) DisplayRegistrationWithData(w http.ResponseWriter, r *http.Request, data map[string]interface{}) { + if c.IsLoggedIn(r) { + http.Redirect(w, r, c.FallbackRoute, 302) + return + } + + if c.renderer == nil { + log.Println("charakterin: no renderer set") + return + } + + if _, ok := data["previous_user"]; !ok { + data["previous_user"] = "" + } + if _, ok := data["previous_email"]; !ok { + data["previous_email"] = "" + } + if _, ok := data["error"]; !ok { + data["error"] = "" + } + + c.renderer.RenderRegistrationPage(w, data) +} + +// DisplayRegistration zeigt die Route für die Registration an, wenn der User nicht bereits eingeloggt ist. +func (c *Charakterin) DisplayRegistration(w http.ResponseWriter, r *http.Request) { + c.DisplayRegistrationWithData(w, r, make(map[string]interface{})) +} + +// Register versucht einen Benutzer zu registrieren. +func (c *Charakterin) Register(w http.ResponseWriter, r *http.Request) { + if r.Method != "POST" { + return + } + + // POST-Data lesen + values, err := readBody(r) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + username := values.Get("username") + password := values.Get("password") + email := values.Get("email") + var result string + err = c.Database.QueryRow("SELECT * FROM login.register_user($1, $2, $3)", username, password, email).Scan(&result) + if err != nil { + errStr := err.Error() + + if errStr == UsernameTaken { + data := make(map[string]interface{}) + data["error"] = "Der Benutzername wird bereits verwendet." + data["previous_email"] = email + c.DisplayLoginWithData(w, r, data) + return + } else if errStr == EmailAlreadyRegistered { + data := make(map[string]interface{}) + data["error"] = "Diese E-Mail wird bereits verwendet." + data["previous_user"] = username + c.DisplayLoginWithData(w, r, data) + return + } + + http.Error(w, errStr, http.StatusInternalServerError) + return + } + + log.Printf("user '%s' has been registered.\n", username) + if err := c.ConfirmEmail(result); err != nil { + log.Println("could not activate user", err) + http.Error(w, "user created, could not activate", http.StatusInternalServerError) + return + } + session, err := c.LoginRequest(username, password) + if err != nil { + log.Println("failed auto-login for", username, err) + http.Redirect(w, r, c.FallbackRoute, 302) + return + } + + http.SetCookie(w, &http.Cookie{ + Name: "session", + Value: session, + Expires: time.Now().AddDate(1, 0, 0), + }) + http.Redirect(w, r, c.FallbackRoute, 302) +} + func (c *Charakterin) GetUserFromRequest(r *http.Request) (*User, error) { cookie, err := r.Cookie("session") if err != nil { @@ -176,7 +282,8 @@ func (c *Charakterin) GetUserFromRequest(r *http.Request) (*User, error) { var password []byte var id int var lastActivity time.Time - err = c.Database.QueryRow(`SELECT * FROM login.get_user_by_session($1)`, cookie.Value).Scan(&id, &email, &name, &password, &displayName, &lastActivity) + var isActive bool + err = c.Database.QueryRow(`SELECT id, email, name, password, display_name, last_activity, is_active FROM login.get_user_by_session($1)`, cookie.Value).Scan(&id, &email, &name, &password, &displayName, &lastActivity, &isActive) if err != nil { return nil, err } @@ -193,8 +300,34 @@ func (c *Charakterin) GetUserFromRequest(r *http.Request) (*User, error) { password, dspName, lastActivity, - cookie.Value, + isActive, } return user, nil } + +// ConfirmEmail konfirmiert die email addresse mit der gegebenen confirm id. +func (c *Charakterin) ConfirmEmail(confirmId string) error { + var result string + err := c.Database.QueryRow(`SELECT * FROM login.confirm_user_email($1)`, confirmId).Scan(&result) + if err != nil { + return err + } + + return nil +} + +func readBody(r *http.Request) (url.Values, error) { + defer r.Body.Close() + data, err := ioutil.ReadAll(r.Body) + if err != nil { + return nil, err + } + + values, err := url.ParseQuery(string(data)) + if err != nil { + return nil, err + } + + return values, nil +} diff --git a/user.go b/user.go index 1546c6b..909859e 100644 --- a/user.go +++ b/user.go @@ -2,7 +2,6 @@ package charakterin import ( "time" - "net/http" ) // Ein User ist ein ganz toller Benutzer. @@ -13,7 +12,7 @@ type User struct { Password []byte DisplayName string LastActivity time.Time - SessionID string + IsActive bool } // GetName gibt den Anzeigenamen oder wenn dieser nicht gesetzt ist den Benutzernamen zurück. @@ -23,13 +22,3 @@ func (u *User) GetName() string { } return u.Name } - -func (u *User) Logout(w http.ResponseWriter) { - http.SetCookie(w, &http.Cookie{ - Name: "session", - Value: "benis", - Expires: time.Now(), - MaxAge: 0, - }) - return -} \ No newline at end of file -- cgit v0.10.1