aboutsummaryrefslogtreecommitdiff
path: root/cmd
diff options
context:
space:
mode:
Diffstat (limited to 'cmd')
-rw-r--r--cmd/parser/main.go37
-rw-r--r--cmd/web/handlers/blog.go9
-rw-r--r--cmd/web/handlers/login.go170
-rw-r--r--cmd/web/handlers/routes.go13
-rw-r--r--cmd/web/main.go3
-rw-r--r--cmd/web/middleware/auth.go81
6 files changed, 277 insertions, 36 deletions
diff --git a/cmd/parser/main.go b/cmd/parser/main.go
index 6d3c3c4..3d0b132 100644
--- a/cmd/parser/main.go
+++ b/cmd/parser/main.go
@@ -16,7 +16,7 @@ import (
"github.com/yuin/goldmark/extension"
"github.com/yuin/goldmark/parser"
"github.com/yuin/goldmark/renderer/html"
- "golang.org/x/crypto/bcrypt"
+ "paterissa.net/mblog/internal"
_ "github.com/jackc/pgx/v5/stdlib"
)
@@ -121,40 +121,7 @@ func main() {
}
defer db.Close()
- _, table_check := db.Query("SELECT * FROM posts;")
- if table_check != nil {
- _, err = db.Exec("CREATE TABLE posts (id SERIAL PRIMARY KEY, name VARCHAR(50) NOT NULL, time TIMESTAMP DEFAULT CURRENT_TIMESTAMP, content TEXT NOT NULL);")
- if err != nil {
- fmt.Fprintf(os.Stderr, "Unable to create posts table: %v\n", err)
- os.Exit(1)
- }
- }
-
- webmaster := os.Getenv("webmaster")
- passOne := os.Getenv("blog_pass1")
- passTwo := os.Getenv("blog_pass2")
-
- _, table_check = db.Query("SELECT * FROM logins;")
- if table_check != nil {
- _, err = db.Exec("CREATE TABLE logins (id SERIAL PRIMARY KEY, name VARCHAR(50) NOT NULL, time DATE DEFAULT CURRENT_DATE, pass_one TEXT NOT NULL, pass_two TEXT NOT NULL);")
- if err != nil {
- fmt.Fprintf(os.Stderr, "Unable to create logins table: %v\n", err)
- os.Exit(1)
- }
-
- hashOne, errHashOne := bcrypt.GenerateFromPassword([]byte(passOne), 12)
- hashTwo, errHashTwo := bcrypt.GenerateFromPassword([]byte(passTwo), 12)
- if errHashOne != nil || errHashTwo != nil {
- fmt.Fprintf(os.Stderr, "Failed to hash password")
- os.Exit(1)
- }
-
- _, err = db.Exec(fmt.Sprintf("INSERT INTO logins (name, pass_one, pass_two) VALUES ('%s', '%s', '%s');", webmaster, hashOne, hashTwo))
- if err != nil {
- fmt.Fprintf(os.Stderr, "Unable to add webmaster to logins table: %v\n", err)
- os.Exit(1)
- }
- }
+ internal.Migrate(db, os.Getenv("webmaster"), os.Getenv("blog_pass1"), os.Getenv("blog_pass2"))
router := http.NewServeMux()
router.HandleFunc("/", mdServe)
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)
diff --git a/cmd/web/main.go b/cmd/web/main.go
index 42b672a..0b35ff2 100644
--- a/cmd/web/main.go
+++ b/cmd/web/main.go
@@ -14,6 +14,7 @@ import (
"paterissa.net/mblog/cmd/web/handlers"
"paterissa.net/mblog/cmd/web/types"
+ "paterissa.net/mblog/internal"
)
func main() {
@@ -57,6 +58,8 @@ func main() {
}
defer db.Close()
+ internal.Migrate(db, os.Getenv("webmaster"), os.Getenv("blog_pass1"), os.Getenv("blog_pass2"))
+
router := handlers.RegisterEndpoints(app, db)
srv := &http.Server{
Addr: fmt.Sprintf(":%d", app.Env.AppPort),
diff --git a/cmd/web/middleware/auth.go b/cmd/web/middleware/auth.go
new file mode 100644
index 0000000..b53980a
--- /dev/null
+++ b/cmd/web/middleware/auth.go
@@ -0,0 +1,81 @@
+package middleware
+
+import (
+ "database/sql"
+ "log"
+ "net/http"
+ "time"
+)
+
+type AuthMiddleware struct {
+ Err *log.Logger
+ Db *sql.DB
+}
+
+func (auth *AuthMiddleware) Resolve(next http.HandlerFunc) http.HandlerFunc {
+ return http.HandlerFunc(
+ func (w http.ResponseWriter, r *http.Request) {
+ cookie, err := r.Cookie("paterissa_session_token")
+ if err != nil {
+ w.WriteHeader(401)
+ w.Write([]byte("Unauthorized"))
+ return
+ }
+
+ stmt, err := auth.Db.Prepare("SELECT * FROM cookies WHERE content = $1;")
+ if err != nil {
+ cookie = &http.Cookie{
+ Name: "paterissa_session_token",
+ Value: "",
+ Path: "/",
+ MaxAge: -1,
+ HttpOnly: true,
+ }
+ http.SetCookie(w, cookie)
+
+ w.Write([]byte("Unauthorized"))
+ auth.Err.Printf("Could not retrieve cookie from DB: %v\n", err)
+ return
+ }
+ defer stmt.Close()
+
+ var id int
+ var content string
+ var userId int
+ var expiration time.Time
+
+ row := stmt.QueryRow(cookie.Value)
+ err = row.Scan(&id, &content, &userId, &expiration)
+ if err != nil {
+ cookie = &http.Cookie{
+ Name: "paterissa_session_token",
+ Value: "",
+ Path: "/",
+ MaxAge: -1,
+ HttpOnly: true,
+ }
+ http.SetCookie(w, cookie)
+
+ w.Write([]byte("Unauthorized"))
+ auth.Err.Printf("Could not retrieve cookie from DB: %v\n", err)
+ return
+ }
+
+ if time.Now().After(expiration) {
+ cookie = &http.Cookie{
+ Name: "paterissa_session_token",
+ Value: "",
+ Path: "/",
+ MaxAge: -1,
+ HttpOnly: true,
+ }
+ http.SetCookie(w, cookie)
+
+ w.Write([]byte("Expired"))
+ return
+ }
+
+ next.ServeHTTP(w, r)
+ return
+ })
+}