pogo/router/router.go

241 lines
6 KiB
Go
Raw Normal View History

2017-10-03 12:08:27 +01:00
package router
import (
2017-11-29 20:56:51 +00:00
"database/sql"
2017-10-03 12:08:27 +01:00
"fmt"
2017-11-29 20:56:51 +00:00
"golang.org/x/crypto/bcrypt"
2017-10-03 12:08:27 +01:00
"log"
"net/http"
_ "github.com/mattn/go-sqlite3"
2017-10-03 12:08:27 +01:00
"github.com/gmemstr/pogo/admin"
"github.com/gmemstr/pogo/auth"
"github.com/gmemstr/pogo/common"
2017-11-29 20:56:51 +00:00
"github.com/gorilla/mux"
2017-10-03 12:08:27 +01:00
)
type NewConfig struct {
Name string
Host string
Email string
Description string
Image string
PodcastURL string
}
// Handle takes multiple Handler and executes them in a serial order starting from first to last.
// In case, Any middle ware returns an error, The error is logged to console and sent to the user, Middlewares further up in chain are not executed.
func Handle(handlers ...common.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
rc := &common.RouterContext{}
for _, handler := range handlers {
err := handler(rc, w, r)
if err != nil {
log.Printf("%v", err)
w.Write([]byte(http.StatusText(err.StatusCode)))
return
}
}
})
}
func Init() *mux.Router {
r := mux.NewRouter()
// "Static" paths
r.PathPrefix("/assets/").Handler(http.StripPrefix("/assets/", http.FileServer(http.Dir("assets/web/static"))))
r.PathPrefix("/static/").Handler(http.StripPrefix("/static/", http.FileServer(http.Dir("assets/web/static"))))
2017-10-03 12:08:27 +01:00
r.PathPrefix("/download/").Handler(http.StripPrefix("/download/", http.FileServer(http.Dir("podcasts"))))
// Paths that require specific handlers
r.Handle("/", Handle(
rootHandler(),
)).Methods("GET")
r.Handle("/rss", Handle(
rootHandler(),
)).Methods("GET")
r.Handle("/json", Handle(
rootHandler(),
)).Methods("GET")
2017-12-04 00:24:04 +00:00
// RequireAuthorization() handles authentication
// and takes a single argument for permission level.
// 0 any user, 1 most users, 2 only admin users
2017-10-03 12:08:27 +01:00
r.Handle("/admin", Handle(
2017-11-29 20:56:51 +00:00
auth.RequireAuthorization(0),
2017-10-03 12:08:27 +01:00
adminHandler(),
)).Methods("GET", "POST")
r.Handle("/login", Handle(
loginHandler(),
)).Methods("POST")
2017-10-05 06:48:02 +01:00
r.Handle("/admin/publish", Handle(
2017-11-29 20:56:51 +00:00
auth.RequireAuthorization(0),
2017-10-05 06:48:02 +01:00
admin.CreateEpisode(),
)).Methods("POST")
r.Handle("/admin/edituser", Handle(
2017-11-29 20:56:51 +00:00
auth.RequireAuthorization(2),
admin.EditUser(),
)).Methods("POST")
r.Handle("/admin/newuser", Handle(
2017-11-29 20:56:51 +00:00
auth.RequireAuthorization(2),
admin.AddUser(),
)).Methods("POST")
r.Handle("/admin/deleteuser/{id}", Handle(
2017-11-29 20:56:51 +00:00
auth.RequireAuthorization(2),
admin.DeleteUser(),
)).Methods("GET")
r.Handle("/admin/edit", Handle(
auth.RequireAuthorization(1),
admin.EditEpisode(),
)).Methods("POST")
2017-10-05 06:48:02 +01:00
r.Handle("/admin/delete", Handle(
auth.RequireAuthorization(1),
2017-10-05 06:48:02 +01:00
admin.RemoveEpisode(),
)).Methods("GET")
r.Handle("/admin/css", Handle(
auth.RequireAuthorization(1),
2017-10-05 06:48:02 +01:00
admin.CustomCss(),
)).Methods("GET", "POST")
2017-10-03 12:08:27 +01:00
r.Handle("/admin/adduser", Handle(
auth.RequireAuthorization(2),
admin.AddUser(),
)).Methods("POST")
r.Handle("/admin/listusers", Handle(
2017-11-29 20:56:51 +00:00
auth.RequireAuthorization(1),
admin.ListUsers(),
)).Methods("GET")
r.Handle("/admin/settings", Handle(
auth.RequireAuthorization(2),
admin.ConfigurationManager(),
)).Methods("GET", "POST")
2017-10-03 12:08:27 +01:00
return r
}
func loginHandler() common.Handler {
return func(rc *common.RouterContext, w http.ResponseWriter, r *http.Request) *common.HTTPError {
db, err := sql.Open("sqlite3", "assets/config/users.db")
if err != nil {
return &common.HTTPError{
Message: fmt.Sprintf("error in reading user database: %v", err),
StatusCode: http.StatusInternalServerError,
}
}
statement, err := db.Prepare("SELECT * FROM users WHERE username=?")
if _, err := auth.DecryptCookie(r); err == nil {
http.Redirect(w, r, "/admin", http.StatusTemporaryRedirect)
return nil
}
err = r.ParseForm()
if err != nil {
return &common.HTTPError{
Message: fmt.Sprintf("error in parsing form: %v", err),
StatusCode: http.StatusBadRequest,
}
}
username := r.Form.Get("username")
password := r.Form.Get("password")
rows, err := statement.Query(username)
if username == "" || password == "" || err != nil {
return &common.HTTPError{
Message: "username or password is invalid",
StatusCode: http.StatusBadRequest,
}
}
2017-11-29 20:56:51 +00:00
var id int
var dbun string
var dbhsh string
2017-11-29 20:56:51 +00:00
var dbrn string
var dbem string
var dbperm int
for rows.Next() {
2017-11-29 20:56:51 +00:00
err := rows.Scan(&id, &dbun, &dbhsh, &dbrn, &dbem, &dbperm)
if err != nil {
return &common.HTTPError{
Message: fmt.Sprintf("error in decoding sql data", err),
StatusCode: http.StatusBadRequest,
}
}
}
// Create a cookie here because the credentials are correct
if bcrypt.CompareHashAndPassword([]byte(dbhsh), []byte(password)) == nil {
2017-11-29 20:56:51 +00:00
c, err := auth.CreateSession(&common.User{
Username: username,
})
if err != nil {
return &common.HTTPError{
Message: err.Error(),
StatusCode: http.StatusInternalServerError,
}
}
// r.AddCookie(c)
w.Header().Add("Set-Cookie", c.String())
// And now redirect the user to admin page
http.Redirect(w, r, "/admin", http.StatusTemporaryRedirect)
db.Close()
return nil
}
return &common.HTTPError{
Message: "Invalid credentials!",
StatusCode: http.StatusUnauthorized,
}
}
}
2017-10-03 12:08:27 +01:00
// Handles /, /feed and /json endpoints
func rootHandler() common.Handler {
return func(rc *common.RouterContext, w http.ResponseWriter, r *http.Request) *common.HTTPError {
var file string
switch r.URL.Path {
case "/rss":
w.Header().Set("Content-Type", "application/rss+xml")
file = "assets/web/feed.rss"
case "/json":
w.Header().Set("Content-Type", "application/json")
file = "assets/web/feed.json"
case "/":
w.Header().Set("Content-Type", "text/html")
file = "assets/web/index.html"
default:
return &common.HTTPError{
Message: fmt.Sprintf("%s: Not Found", r.URL.Path),
StatusCode: http.StatusNotFound,
}
}
return common.ReadAndServeFile(file, w)
}
}
func adminHandler() common.Handler {
return func(rc *common.RouterContext, w http.ResponseWriter, r *http.Request) *common.HTTPError {
return common.ReadAndServeFile("assets/web/admin.html", w)
}
}