security
This commit is contained in:
parent
3dd8ca4c27
commit
ae71abda0b
13 changed files with 113 additions and 22 deletions
|
|
@ -1,13 +1,16 @@
|
||||||
package handler
|
package handler
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/url"
|
||||||
"status/config"
|
"status/config"
|
||||||
"status/storage"
|
"status/storage"
|
||||||
"status/web/session"
|
"status/web/session"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
func serverError(w http.ResponseWriter, err error) {
|
func serverError(w http.ResponseWriter, err error) {
|
||||||
|
|
@ -30,28 +33,24 @@ type Handler struct {
|
||||||
sess *session.Session
|
sess *session.Session
|
||||||
}
|
}
|
||||||
|
|
||||||
//func (h *Handler) getUser(r *http.Request) (string, error) {
|
func protectClickJacking(w http.ResponseWriter) {
|
||||||
// user, err := h.sess.Get(r)
|
w.Header().Set("X-Frame-Options", "DENY")
|
||||||
// if err != nil {
|
w.Header().Set("Content-Security-Policy", "frame-ancestors 'none'")
|
||||||
// return "", err
|
}
|
||||||
// }
|
|
||||||
// if h.cfg.Env != "PROD" {
|
|
||||||
// return user, err
|
|
||||||
// }
|
|
||||||
// // Removes "https://" from referer before checking.
|
|
||||||
// // Thank you crussel for this fix!
|
|
||||||
// if !strings.HasPrefix(r.Referer()[8:], h.cfg.Host) && !strings.HasPrefix(r.Referer()[8:], user+h.cfg.Host) {
|
|
||||||
// err = errors.New("wrong referer")
|
|
||||||
// }
|
|
||||||
// return user, err
|
|
||||||
//}
|
|
||||||
|
|
||||||
func (h *Handler) getUser(r *http.Request) (string, error) {
|
func (h *Handler) getUser(r *http.Request) (string, error) {
|
||||||
user, err := h.sess.Get(r)
|
user, err := h.sess.Get(r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
return user, nil
|
u, err := url.Parse(r.Referer())
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
if strings.HasPrefix(u.Path, "/users") {
|
||||||
|
err = errors.New("forbidden access")
|
||||||
|
}
|
||||||
|
return user, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(cfg *config.Config, sess *session.Session, data *storage.Storage) (http.Handler, error) {
|
func New(cfg *config.Config, sess *session.Session, data *storage.Storage) (http.Handler, error) {
|
||||||
|
|
@ -84,9 +83,11 @@ func New(cfg *config.Config, sess *session.Session, data *storage.Storage) (http
|
||||||
router.HandleFunc("/about/status-updater", h.showStatusUpdaterView).Methods(http.MethodGet)
|
router.HandleFunc("/about/status-updater", h.showStatusUpdaterView).Methods(http.MethodGet)
|
||||||
router.HandleFunc("/users/{user}.atom", h.showAtomView).Methods(http.MethodGet)
|
router.HandleFunc("/users/{user}.atom", h.showAtomView).Methods(http.MethodGet)
|
||||||
router.HandleFunc("/users/{user}", h.showUserView).Methods(http.MethodGet)
|
router.HandleFunc("/users/{user}", h.showUserView).Methods(http.MethodGet)
|
||||||
router.HandleFunc("/users/{user}/status.json", h.showUserStatusView).Methods(http.MethodGet)
|
router.HandleFunc("/users/{user}/status", h.showUserStatusView).Methods(http.MethodGet)
|
||||||
|
router.HandleFunc("/users/{user}/status.json", h.showUserStatusJSONView).Methods(http.MethodGet)
|
||||||
|
|
||||||
router.HandleFunc("/users/{user}/status.png", h.showUserStatusImageView).Methods(http.MethodGet)
|
router.HandleFunc("/users/{user}/status.png", h.showUserStatusImageView).Methods(http.MethodGet)
|
||||||
|
router.HandleFunc("/users/{user}/badge.png", h.showUserStatusBadgeView).Methods(http.MethodGet)
|
||||||
router.PathPrefix("/assets/").Handler(
|
router.PathPrefix("/assets/").Handler(
|
||||||
http.StripPrefix("/assets/",
|
http.StripPrefix("/assets/",
|
||||||
http.FileServer(
|
http.FileServer(
|
||||||
|
|
|
||||||
|
|
@ -297,6 +297,21 @@ var TplMap = map[string]string{
|
||||||
<input type="submit" value="Submit">
|
<input type="submit" value="Submit">
|
||||||
</form>
|
</form>
|
||||||
{{ end }}`,
|
{{ end }}`,
|
||||||
|
"status": `<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>status cafe</title>
|
||||||
|
<link rel="stylesheet" href="/assets/style.css"/>
|
||||||
|
<meta name="description" content="your friends' updates">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
{{ if .status.Content }}
|
||||||
|
{{ template "status" .status }}
|
||||||
|
{{ end }}
|
||||||
|
</body>
|
||||||
|
</html>`,
|
||||||
"status-updater": `{{ define "content" }}
|
"status-updater": `{{ define "content" }}
|
||||||
<section>
|
<section>
|
||||||
<h1>Status Updater</h1>
|
<h1>Status Updater</h1>
|
||||||
|
|
@ -314,7 +329,7 @@ var TplMap = map[string]string{
|
||||||
Drag the following link to your bookmarks toolbar:
|
Drag the following link to your bookmarks toolbar:
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<a href="javascript:void(open('https://status.cafe/add','status.cafe','resizable,scrollbars,width=350,height=250'))">status updater</a>
|
<a href="javascript:void(open('https://status.cafe/add','status.cafe','resizable,scrollbars,width=350,height=290'))">status updater</a>
|
||||||
</p>
|
</p>
|
||||||
<p>That's it! From now on, whenever you want to update your status, click the status updater button from your bookmarks and a pop-up window will launch to let you update it.</p>
|
<p>That's it! From now on, whenever you want to update your status, click the status updater button from your bookmarks and a pop-up window will launch to let you update it.</p>
|
||||||
</section>
|
</section>
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@
|
||||||
Drag the following link to your bookmarks toolbar:
|
Drag the following link to your bookmarks toolbar:
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<a href="javascript:void(open('https://status.cafe/add','status.cafe','resizable,scrollbars,width=350,height=250'))">status updater</a>
|
<a href="javascript:void(open('https://status.cafe/add','status.cafe','resizable,scrollbars,width=350,height=290'))">status updater</a>
|
||||||
</p>
|
</p>
|
||||||
<p>That's it! From now on, whenever you want to update your status, click the status updater button from your bookmarks and a pop-up window will launch to let you update it.</p>
|
<p>That's it! From now on, whenever you want to update your status, click the status updater button from your bookmarks and a pop-up window will launch to let you update it.</p>
|
||||||
</section>
|
</section>
|
||||||
|
|
|
||||||
15
web/handler/html/status.html
Normal file
15
web/handler/html/status.html
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>status cafe</title>
|
||||||
|
<link rel="stylesheet" href="/assets/style.css"/>
|
||||||
|
<meta name="description" content="your friends' updates">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
{{ if .status.Content }}
|
||||||
|
{{ template "status" .status }}
|
||||||
|
{{ end }}
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
@ -11,6 +11,7 @@ type Update struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Handler) showIndexView(w http.ResponseWriter, r *http.Request) {
|
func (h *Handler) showIndexView(w http.ResponseWriter, r *http.Request) {
|
||||||
|
protectClickJacking(w)
|
||||||
user, _ := h.sess.Get(r)
|
user, _ := h.sess.Get(r)
|
||||||
statuses, err := h.storage.LatestStatuses()
|
statuses, err := h.storage.LatestStatuses()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ package handler
|
||||||
import "net/http"
|
import "net/http"
|
||||||
|
|
||||||
func (h *Handler) showSettingsView(w http.ResponseWriter, r *http.Request) {
|
func (h *Handler) showSettingsView(w http.ResponseWriter, r *http.Request) {
|
||||||
|
protectClickJacking(w)
|
||||||
username, err := h.getUser(r)
|
username, err := h.getUser(r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
unauthorized(w)
|
unauthorized(w)
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func (h *Handler) showNewStatusView(w http.ResponseWriter, r *http.Request) {
|
func (h *Handler) showNewStatusView(w http.ResponseWriter, r *http.Request) {
|
||||||
|
protectClickJacking(w)
|
||||||
_, err := h.getUser(r)
|
_, err := h.getUser(r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
unauthorized(w)
|
unauthorized(w)
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ func RouteInt64Param(r *http.Request, param string) int64 {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Handler) showEditStatusView(w http.ResponseWriter, r *http.Request) {
|
func (h *Handler) showEditStatusView(w http.ResponseWriter, r *http.Request) {
|
||||||
|
protectClickJacking(w)
|
||||||
user, err := h.getUser(r)
|
user, err := h.getUser(r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
unauthorized(w)
|
unauthorized(w)
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func (h *Handler) handleRemoveStatus(w http.ResponseWriter, r *http.Request) {
|
func (h *Handler) handleRemoveStatus(w http.ResponseWriter, r *http.Request) {
|
||||||
|
protectClickJacking(w)
|
||||||
user, err := h.getUser(r)
|
user, err := h.getUser(r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
unauthorized(w)
|
unauthorized(w)
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ func (h *Handler) initTpl() {
|
||||||
for name, content := range TplMap {
|
for name, content := range TplMap {
|
||||||
views[name] = template.Must(template.New("main").Funcs(template.FuncMap{
|
views[name] = template.Must(template.New("main").Funcs(template.FuncMap{
|
||||||
"faces": func() []string {
|
"faces": func() []string {
|
||||||
return []string{"🙂", "😎", "😛", "🥰", "👽", "😱", "🤔", "😯", "🤒", "😡", "🥺", "🥳", "🤖", "💀", "😴", "😭", "☕", "🍺"}
|
return []string{"🙂", "😎", "😛", "🥰", "👽", "😱", "🤔", "😯", "🤒", "😡", "🥺", "🥳", "🤖", "💀", "😴", "😭", "☕", "🍺", "📖", "🔥", "❄️", "✨", "💡", "🎼"}
|
||||||
}}).Parse(commonTemplates + content))
|
}}).Parse(commonTemplates + content))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ import (
|
||||||
"image"
|
"image"
|
||||||
"image/color"
|
"image/color"
|
||||||
"image/draw"
|
"image/draw"
|
||||||
|
"status/model"
|
||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
|
@ -96,7 +97,7 @@ type statusjson struct {
|
||||||
TimeAgo string `json:"timeAgo"`
|
TimeAgo string `json:"timeAgo"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Handler) showUserStatusView(w http.ResponseWriter, r *http.Request) {
|
func (h *Handler) showUserStatusJSONView(w http.ResponseWriter, r *http.Request) {
|
||||||
user := mux.Vars(r)["user"]
|
user := mux.Vars(r)["user"]
|
||||||
if !h.storage.UserExists(user) {
|
if !h.storage.UserExists(user) {
|
||||||
notFound(w)
|
notFound(w)
|
||||||
|
|
@ -119,6 +120,19 @@ func (h *Handler) showUserStatusView(w http.ResponseWriter, r *http.Request) {
|
||||||
json.NewEncoder(w).Encode(res)
|
json.NewEncoder(w).Encode(res)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (h *Handler) showUserStatusView(w http.ResponseWriter, r *http.Request) {
|
||||||
|
statuses, _, err := h.storage.StatusByUsername(mux.Vars(r)["user"], 1, 0)
|
||||||
|
if err != nil {
|
||||||
|
serverError(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var status model.Status
|
||||||
|
if len(statuses) > 0 {
|
||||||
|
status = statuses[0]
|
||||||
|
}
|
||||||
|
h.view("status").Execute(w, map[string]interface{}{"status": status})
|
||||||
|
}
|
||||||
|
|
||||||
func drawText(canvas *image.RGBA, text string) error {
|
func drawText(canvas *image.RGBA, text string) error {
|
||||||
var (
|
var (
|
||||||
fgColor image.Image
|
fgColor image.Image
|
||||||
|
|
@ -162,6 +176,46 @@ func createAvatar(text string) *image.RGBA {
|
||||||
return background
|
return background
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (h *Handler) showUserStatusBadgeView(w http.ResponseWriter, r *http.Request) {
|
||||||
|
user := mux.Vars(r)["user"]
|
||||||
|
if !h.storage.UserExists(user) {
|
||||||
|
notFound(w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
statuses, _, err := h.storage.StatusByUsername(mux.Vars(r)["user"], 1, 0)
|
||||||
|
if err != nil {
|
||||||
|
serverError(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var username string
|
||||||
|
if len(statuses) > 0 {
|
||||||
|
username = statuses[0].User
|
||||||
|
}
|
||||||
|
|
||||||
|
background := "#f0ffff"
|
||||||
|
|
||||||
|
dc := gg.NewContext(88, 31)
|
||||||
|
dc.SetHexColor(background)
|
||||||
|
dc.Clear()
|
||||||
|
dc.SetHexColor("#191970")
|
||||||
|
font, err := truetype.Parse(goregular.TTF)
|
||||||
|
if err != nil {
|
||||||
|
panic("")
|
||||||
|
}
|
||||||
|
face := truetype.NewFace(font, &truetype.Options{
|
||||||
|
Size: 12,
|
||||||
|
})
|
||||||
|
dc.SetFontFace(face)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
}
|
||||||
|
//dc.DrawString(username, 10, 14)
|
||||||
|
dc.DrawStringAnchored(username, 88/2, 8, 0.5, 0.5)
|
||||||
|
dc.DrawStringAnchored("status.cafe", 88/2, 20, 0.5, 0.5)
|
||||||
|
dc.EncodePNG(w)
|
||||||
|
}
|
||||||
|
|
||||||
func (h *Handler) showUserStatusImageView(w http.ResponseWriter, r *http.Request) {
|
func (h *Handler) showUserStatusImageView(w http.ResponseWriter, r *http.Request) {
|
||||||
user := mux.Vars(r)["user"]
|
user := mux.Vars(r)["user"]
|
||||||
if !h.storage.UserExists(user) {
|
if !h.storage.UserExists(user) {
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ fetch("https://status.cafe/users/` + name + `/status.json")
|
||||||
document.getElementById("statuscafe-content").innerHTML = "No status yet."
|
document.getElementById("statuscafe-content").innerHTML = "No status yet."
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
document.getElementById("statuscafe-username").innerHTML = '<a href="https://status.cafe/users/` + name + `">' + r.author + '</a> ' + r.face + ' ' + r.timeAgo
|
document.getElementById("statuscafe-username").innerHTML = '<a href="https://status.cafe/users/` + name + `" target="_blank">' + r.author + '</a> ' + r.face + ' ' + r.timeAgo
|
||||||
document.getElementById("statuscafe-content").innerHTML = r.content
|
document.getElementById("statuscafe-content").innerHTML = r.content
|
||||||
})
|
})
|
||||||
`))
|
`))
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ func New(key string, storage *storage.Storage) *Session {
|
||||||
store := sessions.NewCookieStore([]byte(key))
|
store := sessions.NewCookieStore([]byte(key))
|
||||||
store.Options = &sessions.Options{
|
store.Options = &sessions.Options{
|
||||||
HttpOnly: true,
|
HttpOnly: true,
|
||||||
|
Secure: true,
|
||||||
MaxAge: 86400 * 30,
|
MaxAge: 86400 * 30,
|
||||||
}
|
}
|
||||||
return &Session{
|
return &Session{
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue