More config settings and age gating

pull/5/head
Noah 2018-04-11 19:34:37 -07:00
parent 532135094c
commit aca9734b14
8 changed files with 182 additions and 1 deletions

View File

@ -133,6 +133,7 @@ func (b *Blog) SetupHTTP() {
negroni.HandlerFunc(sessions.Middleware),
negroni.HandlerFunc(middleware.CSRF(responses.Forbidden)),
negroni.HandlerFunc(auth.Middleware),
negroni.HandlerFunc(middleware.AgeGate(authctl.AgeGate)),
)
n.UseHandler(r)

View File

@ -5,9 +5,9 @@ import (
"strconv"
"github.com/kirsle/blog/internal/forms"
"github.com/kirsle/blog/models/settings"
"github.com/kirsle/blog/internal/render"
"github.com/kirsle/blog/internal/responses"
"github.com/kirsle/blog/models/settings"
)
func settingsHandler(w http.ResponseWriter, r *http.Request) {
@ -21,11 +21,16 @@ func settingsHandler(w http.ResponseWriter, r *http.Request) {
redisPort, _ := strconv.Atoi(r.FormValue("redis-port"))
redisDB, _ := strconv.Atoi(r.FormValue("redis-db"))
mailPort, _ := strconv.Atoi(r.FormValue("mail-port"))
ppp, _ := strconv.Atoi(r.FormValue("posts-per-page"))
ppf, _ := strconv.Atoi(r.FormValue("posts-per-feed"))
form := &forms.Settings{
Title: r.FormValue("title"),
Description: r.FormValue("description"),
AdminEmail: r.FormValue("admin-email"),
URL: r.FormValue("url"),
NSFW: r.FormValue("nsfw") == "true",
PostsPerPage: ppp,
PostsPerFeed: ppf,
RedisEnabled: len(r.FormValue("redis-enabled")) > 0,
RedisHost: r.FormValue("redis-host"),
RedisPort: redisPort,
@ -45,6 +50,9 @@ func settingsHandler(w http.ResponseWriter, r *http.Request) {
settings.Site.Description = form.Description
settings.Site.AdminEmail = form.AdminEmail
settings.Site.URL = form.URL
settings.Site.NSFW = form.NSFW
settings.Blog.PostsPerPage = form.PostsPerPage
settings.Blog.PostsPerFeed = form.PostsPerFeed
settings.Redis.Enabled = form.RedisEnabled
settings.Redis.Host = form.RedisHost
settings.Redis.Port = form.RedisPort

View File

@ -0,0 +1,35 @@
package authctl
import (
"net/http"
"github.com/kirsle/blog/internal/log"
"github.com/kirsle/blog/internal/render"
"github.com/kirsle/blog/internal/responses"
"github.com/kirsle/blog/internal/sessions"
)
// AgeGate handles age verification for NSFW blogs.
func AgeGate(w http.ResponseWriter, r *http.Request) {
next := r.FormValue("next")
if next == "" {
next = "/"
}
v := map[string]interface{}{
"Next": next,
}
if r.Method == http.MethodPost {
confirm := r.FormValue("confirm")
log.Info("confirm: %s", confirm)
if r.FormValue("confirm") == "true" {
session := sessions.Get(r)
session.Values["age-ok"] = true
session.Save(r, w)
responses.Redirect(w, next)
return
}
}
render.Template(w, r, ".age-gate.gohtml", v)
}

View File

@ -11,6 +11,9 @@ type Settings struct {
Description string
AdminEmail string
URL string
NSFW bool
PostsPerPage int
PostsPerFeed int
RedisEnabled bool
RedisHost string
RedisPort int

View File

@ -0,0 +1,59 @@
package middleware
import (
"net/http"
"strings"
"github.com/kirsle/blog/internal/responses"
"github.com/kirsle/blog/internal/sessions"
"github.com/kirsle/blog/models/settings"
"github.com/urfave/negroni"
)
var ageGateSuffixes = []string{
".js",
".css",
".txt",
".ico",
".png",
".jpg",
".jpeg",
".gif",
}
// AgeGate is a middleware generator that does age verification for NSFW sites.
func AgeGate(verifyHandler func(http.ResponseWriter, *http.Request)) negroni.HandlerFunc {
middleware := func(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
s, _ := settings.Load()
if !s.Site.NSFW {
next(w, r)
return
}
path := r.URL.Path
if strings.HasPrefix(path, "/age-verify") {
verifyHandler(w, r) // defer to the age gate handler itself.
return
}
// Allow static files and things through.
for _, prefix := range ageGateSuffixes {
if strings.HasSuffix(path, prefix) {
next(w, r)
return
}
}
// See if they've been cleared.
session := sessions.Get(r)
if val, _ := session.Values["age-ok"].(bool); !val {
// They haven't been verified.
responses.Redirect(w, "/age-verify?next="+r.URL.Path)
return
}
next(w, r)
}
return middleware
}

View File

@ -20,6 +20,7 @@ type Settings struct {
Title string `json:"title"`
Description string `json:"description"`
AdminEmail string `json:"adminEmail"`
NSFW bool `json:"nsfw"`
URL string `json:"url"`
} `json:"site"`
@ -29,6 +30,12 @@ type Settings struct {
HashCost int `json:"hashCost"` // Bcrypt hash cost for passwords
} `json:"security"`
// Blog settings.
Blog struct {
PostsPerPage int `json:"postsPerPage"`
PostsPerFeed int `json:"postsPerFeed"`
} `json:"blog"`
// Redis settings for caching in JsonDB.
Redis struct {
Enabled bool `json:"enabled"`
@ -58,6 +65,8 @@ func Defaults() *Settings {
s.Site.Title = "Untitled Site"
s.Security.HashCost = 14
s.Security.SecretKey = RandomKey()
s.Blog.PostsPerPage = 10
s.Blog.PostsPerFeed = 10
s.Redis.Host = "localhost"
s.Redis.Port = 6379
s.Redis.DB = 0

34
root/.age-gate.gohtml Normal file
View File

@ -0,0 +1,34 @@
{{ define "title" }}Age Verification{{ end }}
{{ define "content" }}
<div class="card">
<div class="card-body">
<form action="/age-verify" method="POST">
<input type="hidden" name="_csrf" value="{{ .CSRF }}">
<input type="hidden" name="next" value="{{ .Data.Next }}">
<input type="hidden" name="confirm" value="true">
<h1>Restricted Content</h1>
<p>
This website has been marked <abbr title="Not Safe For Work">NSFW</abbr>
by its owner. It may contain nudity or content not suited for users
under the age of 18.
</p>
<p>
To proceed, you must verify you are at least 18 years or older.
</p>
<button type="submit"
class="btn btn-danger">
I am 18 years or older
</button>
<a class="btn btn-primary"
href="https://duckduckgo.com/">
Get me out of here!
</a>
</form>
</div>
</div>
{{ end }}

View File

@ -50,6 +50,38 @@
placeholder="https://www.example.com/">
</div>
<strong>NSFW Website</strong>
<div class="form-check mb-4">
<label class="form-check-label">
<input type="checkbox"
class="form-check-input"
name="nsfw"
value="true"
{{ if .Site.NSFW }}checked{{ end }}>
Website is NSFW. Requires an age verification to enter.
</label>
</div>
<h3>Blog Settings</h3>
<div class="form-group">
<label for="admin-email">Posts Per Page</label>
<input type="text"
class="form-control"
name="posts-per-page"
value="{{ .Blog.PostsPerPage }}"
placeholder="https://www.example.com/">
</div>
<div class="form-group">
<label for="admin-email">Posts Per (RSS) Feed</label>
<input type="text"
class="form-control"
name="posts-per-page"
value="{{ .Blog.PostsPerFeed }}"
placeholder="https://www.example.com/">
</div>
<h3>Redis Cache</h3>
<p>