1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
|
package charakterin
import (
"database/sql"
"io/ioutil"
"log"
"net/http"
"net/url"
"time"
_ "github.com/lib/pq"
)
// 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{})
}
// Charakterin ist das tolle Login- und Accountmanagementsystem.
type Charakterin struct {
renderer Renderer
FallbackRoute string
Database *sql.DB
}
// New erstellt eine neue Instanz von Charakterin.
func New(db *sql.DB) *Charakterin {
if err := db.Ping(); err != nil {
log.Fatalln("no valid database connection supplied:", err)
return nil
}
return &Charakterin{
nil,
"/",
db,
}
}
// UseRenderer sagt charakterin, welchen Renderer es benutzen soll.
func (c *Charakterin) UseRenderer(renderer Renderer) {
c.renderer = renderer
}
// DisplayLoginWithData rendert die Loginseite mit Daten (vorheriger Benutzer, Fehlermeldung)
func (c *Charakterin) DisplayLoginWithData(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["error"]; !ok {
data["error"] = ""
}
c.renderer.RenderLoginPage(w, data)
}
// DisplayLogin zeigt die Route für den Login an, wenn der User nicht bereits eingeloggt ist.
func (c *Charakterin) DisplayLogin(w http.ResponseWriter, r *http.Request) {
c.DisplayLoginWithData(w, r, make(map[string]interface{}))
}
// Login versucht einen User 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))
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)
if err != nil {
errStr := err.Error()
if errStr == "pq: no_such_user" || errStr == "pq: invalid_password" {
log.Printf("invalid login attempt by '%s': %s\n", username, errStr[4:])
data := make(map[string]interface{})
data["previous_user"] = username
data["error"] = "Ungültiger Benutzername oder Passwort. Oder Lukas hats mal wieder kaputt gemacht."
c.DisplayLoginWithData(w, r, data)
return
}
http.Error(w, errStr, http.StatusInternalServerError)
return
}
http.SetCookie(w, &http.Cookie{
Name: "session",
Value: result,
Expires: time.Now().AddDate(1, 0, 0),
})
http.Redirect(w, r, c.FallbackRoute, 302)
}
// IsLoggedIn überprüft anhand eines Request, ob der User eingeloggt ist.
func (c *Charakterin) IsLoggedIn(r *http.Request) bool {
cookie, err := r.Cookie("session")
if err != nil {
return false
}
rows, err := c.Database.Query(`SELECT * FROM login.sessions WHERE id = $1`, cookie.Value)
if err != nil {
log.Println(err)
return false
}
if rows.Next() {
return true
}
return false
}
|