diff options
| author | jan <jan@ruken.pw> | 2015-12-20 16:28:22 (UTC) |
|---|---|---|
| committer | jan <jan@ruken.pw> | 2015-12-20 16:28:22 (UTC) |
| commit | bd88166988bb31ff82da847d1e69c907f5f53930 (patch) | |
| tree | b89ac79c2631f1bd0a6eabc4fab04d99298ae843 | |
| parent | 9cdf03be6b141d10b2a53656912f4bdcea553365 (diff) | |
registration. yay.
| -rw-r--r-- | charakterin.go | 179 | ||||
| -rw-r--r-- | user.go | 13 |
2 files changed, 157 insertions, 35 deletions
diff --git a/charakterin.go b/charakterin.go index abf9627..142484c 100644 --- a/charakterin.go +++ b/charakterin.go | |||
| @@ -12,10 +12,19 @@ import ( | |||
| 12 | _ "github.com/lib/pq" | 12 | _ "github.com/lib/pq" |
| 13 | ) | 13 | ) |
| 14 | 14 | ||
| 15 | const ( | ||
| 16 | NoSuchUser = "pq: no_such_user" | ||
| 17 | InvalidPassword = "pq: invalid_password" | ||
| 18 | UsernameTaken = "pq: username_taken" | ||
| 19 | EmailAlreadyRegistered = "pq: email_already_registered" | ||
| 20 | ) | ||
| 21 | |||
| 15 | // Renderer wird verwendet, um die Routen (bspw. Login-Route) zu rendern. Damit bleibt Charakterin selbst ohne Template. | 22 | // Renderer wird verwendet, um die Routen (bspw. Login-Route) zu rendern. Damit bleibt Charakterin selbst ohne Template. |
| 16 | type Renderer interface { | 23 | type Renderer interface { |
| 17 | // RenderLoginPage zeigt die Login-Seite an. | 24 | // RenderLoginPage zeigt die Login-Seite an. |
| 18 | RenderLoginPage(w http.ResponseWriter, data map[string]interface{}) | 25 | RenderLoginPage(w http.ResponseWriter, data map[string]interface{}) |
| 26 | // RenderRegistrationPage zeigt die Registrations-Seite an. | ||
| 27 | RenderRegistrationPage(w http.ResponseWriter, data map[string]interface{}) | ||
| 19 | } | 28 | } |
| 20 | 29 | ||
| 21 | // Charakterin ist das tolle Login- und Accountmanagementsystem. | 30 | // Charakterin ist das tolle Login- und Accountmanagementsystem. |
| @@ -70,33 +79,35 @@ func (c *Charakterin) DisplayLogin(w http.ResponseWriter, r *http.Request) { | |||
| 70 | c.DisplayLoginWithData(w, r, make(map[string]interface{})) | 79 | c.DisplayLoginWithData(w, r, make(map[string]interface{})) |
| 71 | } | 80 | } |
| 72 | 81 | ||
| 73 | // Login versucht einen User einzuloggen. | 82 | // LoginRequest versucht, einen Benutzer mit den gegebenen Daten einzuloggen. |
| 83 | func (c *Charakterin) LoginRequest(username, password string) (string, error) { | ||
| 84 | var result string | ||
| 85 | err := c.Database.QueryRow("SELECT * FROM login.new_session($1, $2)", username, password).Scan(&result) | ||
| 86 | if err != nil { | ||
| 87 | return "", err | ||
| 88 | } | ||
| 89 | return result, nil | ||
| 90 | } | ||
| 91 | |||
| 92 | // Login versucht einen User durch einen Request einzuloggen. | ||
| 74 | func (c *Charakterin) Login(w http.ResponseWriter, r *http.Request) { | 93 | func (c *Charakterin) Login(w http.ResponseWriter, r *http.Request) { |
| 75 | if r.Method != "POST" { | 94 | if r.Method != "POST" { |
| 76 | return | 95 | return |
| 77 | } | 96 | } |
| 78 | 97 | ||
| 79 | // POST-Data lesen | 98 | // POST-Data lesen |
| 80 | defer r.Body.Close() | 99 | values, err := readBody(r) |
| 81 | data, err := ioutil.ReadAll(r.Body) | ||
| 82 | if err != nil { | ||
| 83 | http.Error(w, err.Error(), http.StatusInternalServerError) | ||
| 84 | return | ||
| 85 | } | ||
| 86 | |||
| 87 | values, err := url.ParseQuery(string(data)) | ||
| 88 | if err != nil { | 100 | if err != nil { |
| 89 | http.Error(w, err.Error(), http.StatusInternalServerError) | 101 | http.Error(w, err.Error(), http.StatusInternalServerError) |
| 90 | return | 102 | return |
| 91 | } | 103 | } |
| 92 | 104 | ||
| 93 | username := values.Get("username") | 105 | username := values.Get("username") |
| 94 | var result string | 106 | session, err := c.LoginRequest(username, values.Get("password")) |
| 95 | err = c.Database.QueryRow("SELECT * FROM login.new_session($1, $2)", username, values.Get("password")).Scan(&result) | ||
| 96 | if err != nil { | 107 | if err != nil { |
| 97 | errStr := err.Error() | 108 | errStr := err.Error() |
| 98 | 109 | ||
| 99 | if errStr == "pq: no_such_user" || errStr == "pq: invalid_password" { | 110 | if errStr == NoSuchUser || errStr == InvalidPassword { |
| 100 | log.Printf("invalid login attempt by '%s': %s\n", username, errStr[4:]) | 111 | log.Printf("invalid login attempt by '%s': %s\n", username, errStr[4:]) |
| 101 | data := make(map[string]interface{}) | 112 | data := make(map[string]interface{}) |
| 102 | data["previous_user"] = username | 113 | data["previous_user"] = username |
| @@ -111,7 +122,7 @@ func (c *Charakterin) Login(w http.ResponseWriter, r *http.Request) { | |||
| 111 | 122 | ||
| 112 | http.SetCookie(w, &http.Cookie{ | 123 | http.SetCookie(w, &http.Cookie{ |
| 113 | Name: "session", | 124 | Name: "session", |
| 114 | Value: result, | 125 | Value: session, |
| 115 | Expires: time.Now().AddDate(1, 0, 0), | 126 | Expires: time.Now().AddDate(1, 0, 0), |
| 116 | }) | 127 | }) |
| 117 | http.Redirect(w, r, c.FallbackRoute, 302) | 128 | http.Redirect(w, r, c.FallbackRoute, 302) |
| @@ -119,32 +130,37 @@ func (c *Charakterin) Login(w http.ResponseWriter, r *http.Request) { | |||
| 119 | 130 | ||
| 120 | // Logout loggt einen Charakter aus. Wird direkt über den Request gehandlet. | 131 | // Logout loggt einen Charakter aus. Wird direkt über den Request gehandlet. |
| 121 | func (c *Charakterin) Logout(w http.ResponseWriter, r *http.Request) { | 132 | func (c *Charakterin) Logout(w http.ResponseWriter, r *http.Request) { |
| 122 | user, err := c.GetUserFromRequest(r) | 133 | cookie, err := r.Cookie("session") |
| 123 | if err != nil { | 134 | if err != nil { |
| 124 | http.Redirect(w, r, c.FallbackRoute, 302) | 135 | http.Redirect(w, r, c.FallbackRoute, 302) |
| 125 | return | 136 | return |
| 126 | } | 137 | } |
| 127 | 138 | ||
| 128 | stmt, err := c.Database.Prepare("DELETE FROM login.sessions WHERE id=$1") | 139 | stmt, err := c.Database.Prepare("DELETE FROM login.sessions WHERE id=$1") |
| 129 | if err != nil { | 140 | if err != nil { |
| 130 | http.Error(w, "500", http.StatusInternalServerError) | 141 | http.Error(w, "500", http.StatusInternalServerError) |
| 131 | return | 142 | return |
| 132 | } | 143 | } |
| 133 | 144 | ||
| 134 | result, err := stmt.Exec(user.SessionID) | 145 | result, err := stmt.Exec(cookie.Value) |
| 135 | if err != nil { | 146 | if err != nil { |
| 136 | log.Println(err) | 147 | log.Println(err) |
| 137 | http.Error(w, "500", http.StatusInternalServerError) | 148 | http.Error(w, "500", http.StatusInternalServerError) |
| 138 | return | 149 | return |
| 139 | } | 150 | } |
| 140 | 151 | ||
| 141 | if val, err := result.RowsAffected(); err != nil || val == 0 { | 152 | if val, err := result.RowsAffected(); err != nil || val == 0 { |
| 142 | log.Println("could not remove session",user.SessionID,err) | 153 | log.Println("could not remove session", cookie.Value, err) |
| 143 | http.Redirect(w, r, c.FallbackRoute, 302) | 154 | http.Redirect(w, r, c.FallbackRoute, 302) |
| 144 | return | 155 | return |
| 145 | } | 156 | } |
| 146 | 157 | ||
| 147 | user.Logout(w) | 158 | http.SetCookie(w, &http.Cookie{ |
| 159 | Name: "session", | ||
| 160 | Value: "benis", | ||
| 161 | Expires: time.Now(), | ||
| 162 | MaxAge: 0, | ||
| 163 | }) | ||
| 148 | http.Redirect(w, r, c.FallbackRoute, 302) | 164 | http.Redirect(w, r, c.FallbackRoute, 302) |
| 149 | } | 165 | } |
| 150 | 166 | ||
| @@ -165,6 +181,96 @@ func (c *Charakterin) IsLoggedIn(r *http.Request) bool { | |||
| 165 | return true | 181 | return true |
| 166 | } | 182 | } |
| 167 | 183 | ||
| 184 | // DisplayRegistrationWithData rendert die Registration mit Daten (vorheriger Benutzer, Fehlermeldung) | ||
| 185 | func (c *Charakterin) DisplayRegistrationWithData(w http.ResponseWriter, r *http.Request, data map[string]interface{}) { | ||
| 186 | if c.IsLoggedIn(r) { | ||
| 187 | http.Redirect(w, r, c.FallbackRoute, 302) | ||
| 188 | return | ||
| 189 | } | ||
| 190 | |||
| 191 | if c.renderer == nil { | ||
| 192 | log.Println("charakterin: no renderer set") | ||
| 193 | return | ||
| 194 | } | ||
| 195 | |||
| 196 | if _, ok := data["previous_user"]; !ok { | ||
| 197 | data["previous_user"] = "" | ||
| 198 | } | ||
| 199 | if _, ok := data["previous_email"]; !ok { | ||
| 200 | data["previous_email"] = "" | ||
| 201 | } | ||
| 202 | if _, ok := data["error"]; !ok { | ||
| 203 | data["error"] = "" | ||
| 204 | } | ||
| 205 | |||
| 206 | c.renderer.RenderRegistrationPage(w, data) | ||
| 207 | } | ||
| 208 | |||
| 209 | // DisplayRegistration zeigt die Route für die Registration an, wenn der User nicht bereits eingeloggt ist. | ||
| 210 | func (c *Charakterin) DisplayRegistration(w http.ResponseWriter, r *http.Request) { | ||
| 211 | c.DisplayRegistrationWithData(w, r, make(map[string]interface{})) | ||
| 212 | } | ||
| 213 | |||
| 214 | // Register versucht einen Benutzer zu registrieren. | ||
| 215 | func (c *Charakterin) Register(w http.ResponseWriter, r *http.Request) { | ||
| 216 | if r.Method != "POST" { | ||
| 217 | return | ||
| 218 | } | ||
| 219 | |||
| 220 | // POST-Data lesen | ||
| 221 | values, err := readBody(r) | ||
| 222 | if err != nil { | ||
| 223 | http.Error(w, err.Error(), http.StatusInternalServerError) | ||
| 224 | return | ||
| 225 | } | ||
| 226 | |||
| 227 | username := values.Get("username") | ||
| 228 | password := values.Get("password") | ||
| 229 | email := values.Get("email") | ||
| 230 | var result string | ||
| 231 | err = c.Database.QueryRow("SELECT * FROM login.register_user($1, $2, $3)", username, password, email).Scan(&result) | ||
| 232 | if err != nil { | ||
| 233 | errStr := err.Error() | ||
| 234 | |||
| 235 | if errStr == UsernameTaken { | ||
| 236 | data := make(map[string]interface{}) | ||
| 237 | data["error"] = "Der Benutzername wird bereits verwendet." | ||
| 238 | data["previous_email"] = email | ||
| 239 | c.DisplayLoginWithData(w, r, data) | ||
| 240 | return | ||
| 241 | } else if errStr == EmailAlreadyRegistered { | ||
| 242 | data := make(map[string]interface{}) | ||
| 243 | data["error"] = "Diese E-Mail wird bereits verwendet." | ||
| 244 | data["previous_user"] = username | ||
| 245 | c.DisplayLoginWithData(w, r, data) | ||
| 246 | return | ||
| 247 | } | ||
| 248 | |||
| 249 | http.Error(w, errStr, http.StatusInternalServerError) | ||
| 250 | return | ||
| 251 | } | ||
| 252 | |||
| 253 | log.Printf("user '%s' has been registered.\n", username) | ||
| 254 | if err := c.ConfirmEmail(result); err != nil { | ||
| 255 | log.Println("could not activate user", err) | ||
| 256 | http.Error(w, "user created, could not activate", http.StatusInternalServerError) | ||
| 257 | return | ||
| 258 | } | ||
| 259 | session, err := c.LoginRequest(username, password) | ||
| 260 | if err != nil { | ||
| 261 | log.Println("failed auto-login for", username, err) | ||
| 262 | http.Redirect(w, r, c.FallbackRoute, 302) | ||
| 263 | return | ||
| 264 | } | ||
| 265 | |||
| 266 | http.SetCookie(w, &http.Cookie{ | ||
| 267 | Name: "session", | ||
| 268 | Value: session, | ||
| 269 | Expires: time.Now().AddDate(1, 0, 0), | ||
| 270 | }) | ||
| 271 | http.Redirect(w, r, c.FallbackRoute, 302) | ||
| 272 | } | ||
| 273 | |||
| 168 | func (c *Charakterin) GetUserFromRequest(r *http.Request) (*User, error) { | 274 | func (c *Charakterin) GetUserFromRequest(r *http.Request) (*User, error) { |
| 169 | cookie, err := r.Cookie("session") | 275 | cookie, err := r.Cookie("session") |
| 170 | if err != nil { | 276 | if err != nil { |
| @@ -176,7 +282,8 @@ func (c *Charakterin) GetUserFromRequest(r *http.Request) (*User, error) { | |||
| 176 | var password []byte | 282 | var password []byte |
| 177 | var id int | 283 | var id int |
| 178 | var lastActivity time.Time | 284 | var lastActivity time.Time |
| 179 | err = c.Database.QueryRow(`SELECT * FROM login.get_user_by_session($1)`, cookie.Value).Scan(&id, &email, &name, &password, &displayName, &lastActivity) | 285 | var isActive bool |
| 286 | 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) | ||
| 180 | if err != nil { | 287 | if err != nil { |
| 181 | return nil, err | 288 | return nil, err |
| 182 | } | 289 | } |
| @@ -193,8 +300,34 @@ func (c *Charakterin) GetUserFromRequest(r *http.Request) (*User, error) { | |||
| 193 | password, | 300 | password, |
| 194 | dspName, | 301 | dspName, |
| 195 | lastActivity, | 302 | lastActivity, |
| 196 | cookie.Value, | 303 | isActive, |
| 197 | } | 304 | } |
| 198 | 305 | ||
| 199 | return user, nil | 306 | return user, nil |
| 200 | } | 307 | } |
| 308 | |||
| 309 | // ConfirmEmail konfirmiert die email addresse mit der gegebenen confirm id. | ||
| 310 | func (c *Charakterin) ConfirmEmail(confirmId string) error { | ||
| 311 | var result string | ||
| 312 | err := c.Database.QueryRow(`SELECT * FROM login.confirm_user_email($1)`, confirmId).Scan(&result) | ||
| 313 | if err != nil { | ||
| 314 | return err | ||
| 315 | } | ||
| 316 | |||
| 317 | return nil | ||
| 318 | } | ||
| 319 | |||
| 320 | func readBody(r *http.Request) (url.Values, error) { | ||
| 321 | defer r.Body.Close() | ||
| 322 | data, err := ioutil.ReadAll(r.Body) | ||
| 323 | if err != nil { | ||
| 324 | return nil, err | ||
| 325 | } | ||
| 326 | |||
| 327 | values, err := url.ParseQuery(string(data)) | ||
| 328 | if err != nil { | ||
| 329 | return nil, err | ||
| 330 | } | ||
| 331 | |||
| 332 | return values, nil | ||
| 333 | } | ||
| @@ -2,7 +2,6 @@ package charakterin | |||
| 2 | 2 | ||
| 3 | import ( | 3 | import ( |
| 4 | "time" | 4 | "time" |
| 5 | "net/http" | ||
| 6 | ) | 5 | ) |
| 7 | 6 | ||
| 8 | // Ein User ist ein ganz toller Benutzer. | 7 | // Ein User ist ein ganz toller Benutzer. |
| @@ -13,7 +12,7 @@ type User struct { | |||
| 13 | Password []byte | 12 | Password []byte |
| 14 | DisplayName string | 13 | DisplayName string |
| 15 | LastActivity time.Time | 14 | LastActivity time.Time |
| 16 | SessionID string | 15 | IsActive bool |
| 17 | } | 16 | } |
| 18 | 17 | ||
| 19 | // GetName gibt den Anzeigenamen oder wenn dieser nicht gesetzt ist den Benutzernamen zurück. | 18 | // GetName gibt den Anzeigenamen oder wenn dieser nicht gesetzt ist den Benutzernamen zurück. |
| @@ -23,13 +22,3 @@ func (u *User) GetName() string { | |||
| 23 | } | 22 | } |
| 24 | return u.Name | 23 | return u.Name |
| 25 | } | 24 | } |
| 26 | |||
| 27 | func (u *User) Logout(w http.ResponseWriter) { | ||
| 28 | http.SetCookie(w, &http.Cookie{ | ||
| 29 | Name: "session", | ||
| 30 | Value: "benis", | ||
| 31 | Expires: time.Now(), | ||
| 32 | MaxAge: 0, | ||
| 33 | }) | ||
| 34 | return | ||
| 35 | } \ No newline at end of file | ||
