diff options
Diffstat (limited to 'cmd/web/handlers')
| -rw-r--r-- | cmd/web/handlers/blog.go | 9 | ||||
| -rw-r--r-- | cmd/web/handlers/login.go | 170 | ||||
| -rw-r--r-- | cmd/web/handlers/routes.go | 13 |
3 files changed, 191 insertions, 1 deletions
diff --git a/cmd/web/handlers/blog.go b/cmd/web/handlers/blog.go index bd2d97f..573e52f 100644 --- a/cmd/web/handlers/blog.go +++ b/cmd/web/handlers/blog.go @@ -15,22 +15,29 @@ type blogContext struct { Rows []models.Post Name string + IsAuth bool } func (ctx *blogContext) index(w http.ResponseWriter, r *http.Request) { ctx.Rows = []models.Post{} + ctx.IsAuth = false if r.URL.Path != "/" { http.NotFound(w, r) return } + + _, err := r.Cookie("paterissa_session_token") + if err == nil { + ctx.IsAuth = true; + } offset := r.URL.Query().Get("offset") if offset == "" { offset = "20" } - rows, err := ctx.db.Query("SELECT * FROM posts WHERE id < " + offset + " ORDER BY id DESC LIMIT 20;") + rows, err := ctx.db.Query("SELECT p.id, u.name, p.time, p.content FROM posts p INNER JOIN logins u ON p.user_id = u.id WHERE p.id < " + offset + " ORDER BY p.id DESC LIMIT 20;") if err != nil { ctx.err.Print(err.Error()) http.Error(w, "Internal Server Error", 500) diff --git a/cmd/web/handlers/login.go b/cmd/web/handlers/login.go new file mode 100644 index 0000000..e867e07 --- /dev/null +++ b/cmd/web/handlers/login.go @@ -0,0 +1,170 @@ +package handlers + +import ( + "database/sql" + "html/template" + "log" + "net/http" + "os" + "time" + + "github.com/google/uuid" + "golang.org/x/crypto/bcrypt" + "paterissa.net/mblog/internal/models" +) + +type loginContext struct { + err *log.Logger + db *sql.DB +} + +func (ctx *loginContext) index(w http.ResponseWriter, r *http.Request) { + files := []string{ + "ui/html/base.tmpl.html", + "ui/html/login.tmpl.html", + "ui/html/music_player.tmpl.html", + } + + compiled, err := template.ParseFiles(files...) + if err != nil { + w.WriteHeader(500) + w.Write([]byte("Internal Server Error")) + ctx.err.Printf("Failed to parse templates: %v\n", err) + return + } + + err = compiled.ExecuteTemplate(w, "base", ctx) + if err != nil { + w.WriteHeader(500) + w.Write([]byte("Internal Server Error")) + ctx.err.Printf("Failed to parse templates: %v\n", err) + return + } + + return +} + +func (ctx *loginContext) login(w http.ResponseWriter, r *http.Request) { + if r.Method != "POST" { + w.WriteHeader(405) + w.Write([]byte("Method Not Allowed")) + return + } + + err := r.ParseForm() + if err != nil { + w.WriteHeader(500) + w.Write([]byte("Internal Error")) + ctx.err.Printf("Failed to retrieve form data: %v\n", err) + return + } + + user := r.PostForm.Get("user") + passOne := r.PostForm.Get("pass_one") + passTwo := r.PostForm.Get("pass_two") + + stmt, err := ctx.db.Prepare("SELECT * FROM logins WHERE name = $1;") + if err != nil { + w.WriteHeader(500) + w.Write([]byte("Internal Error")) + ctx.err.Printf("Failed to retrieve form data: %v\n", err) + return + } + defer stmt.Close() + + var u models.User + + row := stmt.QueryRow(user) + err = row.Scan(&u.Id, &u.Name, &u.Time, &u.PassOne, &u.PassTwo) + if err != nil { + w.WriteHeader(401) + w.Write([]byte("Unauthorized")) + ctx.err.Printf("Failed to retrieve user info from DB: %v\n", err) + return + } + + passOneErr := bcrypt.CompareHashAndPassword([]byte(u.PassOne), []byte(passOne)) + passTwoErr := bcrypt.CompareHashAndPassword([]byte(u.PassTwo), []byte(passTwo)) + if passOneErr != nil || passTwoErr != nil { + w.WriteHeader(401) + w.Write([]byte("Failed to login - not authorized")) + return + } + + cookie := http.Cookie{ + Name: "paterissa_session_token", + Value: uuid.New().String(), + Expires: time.Now().AddDate(0, 0, 1), + Path: "/", + Domain: os.Getenv("serv"), + HttpOnly: true, + Secure: true, + } + + commit, err := ctx.db.Prepare("INSERT INTO cookies (content, user_id, expiration) VALUES ($1, $2, $3);") + if err != nil { + w.WriteHeader(500) + w.Write([]byte("Internal Error")) + ctx.err.Printf("Failed to prepare DB statement: %v\n", err) + return + } + + _, err = commit.Exec(cookie.Value, u.Id, cookie.Expires) + if err != nil { + w.WriteHeader(500) + w.Write([]byte("Internal Error")) + ctx.err.Printf("Failed to prepare DB statement: %v\n", err) + } + + http.SetCookie(w, &cookie) + http.Redirect(w, r, "/", http.StatusFound) + return +} + +func (ctx *loginContext) logout(w http.ResponseWriter, r *http.Request) { + cookie, err := r.Cookie("paterissa_session_token") + if err != nil { + w.WriteHeader(405) + w.Write([]byte("Unauthorized")) + return + } + + stmt, err := ctx.db.Prepare("UPDATE cookies SET expiration = $1 WHERE content = $2;") + if err != nil { + w.WriteHeader(500) + w.Write([]byte("Internal Error")) + ctx.err.Printf("Could not prepare DB statement: %v\n", err) + return + } + defer stmt.Close() + + _, err = stmt.Exec(time.Now(), cookie.Value) + if err != nil { + w.WriteHeader(500) + w.Write([]byte("Internal Error")) + ctx.err.Printf("Could not execute DB statement: %v\n", err) + return + } + + cookie = &http.Cookie{ + Name: "paterissa_session_token", + Value: "", + Path: "/", + MaxAge: -1, + HttpOnly: true, + } + + http.SetCookie(w, cookie) + http.Redirect(w, r, "/", http.StatusFound) + return +} + +func (ctx *loginContext) handle(w http.ResponseWriter, r *http.Request) { + if r.Method != "POST" { + ctx.index(w, r) + return + } else { + ctx.login(w, r) + return + } +} diff --git a/cmd/web/handlers/routes.go b/cmd/web/handlers/routes.go index e9fd0f5..0196331 100644 --- a/cmd/web/handlers/routes.go +++ b/cmd/web/handlers/routes.go @@ -4,15 +4,25 @@ import ( "database/sql" "net/http" + "paterissa.net/mblog/cmd/web/middleware" "paterissa.net/mblog/cmd/web/types" ) func RegisterEndpoints(app types.Application, db *sql.DB) *http.ServeMux { + auth := middleware.AuthMiddleware{ + Err: app.Err, + Db: db, + } + blog := blogContext{ err: app.Err, db: db, Name: app.Env.Webmaster, } + login := loginContext{ + err: app.Err, + db: db, + } audio := fsContext{ err: app.Err, path: app.AudioDir, @@ -26,6 +36,9 @@ func RegisterEndpoints(app types.Application, db *sql.DB) *http.ServeMux { blogRouter := http.NewServeMux() blogRouter.HandleFunc("/", blog.index) + blogRouter.HandleFunc("/login", login.handle) + blogRouter.HandleFunc("/logout", auth.Resolve(login.logout)) + blogRouter.HandleFunc("/audio", audio.readdir) blogRouter.HandleFunc("/audio/get", audio.get) |
