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
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/gorilla/mux"
|
||||
"log"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"status/config"
|
||||
"status/storage"
|
||||
"status/web/session"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func serverError(w http.ResponseWriter, err error) {
|
||||
|
|
@ -30,28 +33,24 @@ type Handler struct {
|
|||
sess *session.Session
|
||||
}
|
||||
|
||||
//func (h *Handler) getUser(r *http.Request) (string, error) {
|
||||
// user, err := h.sess.Get(r)
|
||||
// if err != nil {
|
||||
// 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 protectClickJacking(w http.ResponseWriter) {
|
||||
w.Header().Set("X-Frame-Options", "DENY")
|
||||
w.Header().Set("Content-Security-Policy", "frame-ancestors 'none'")
|
||||
}
|
||||
|
||||
func (h *Handler) getUser(r *http.Request) (string, error) {
|
||||
user, err := h.sess.Get(r)
|
||||
if err != nil {
|
||||
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) {
|
||||
|
|
@ -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("/users/{user}.atom", h.showAtomView).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}/badge.png", h.showUserStatusBadgeView).Methods(http.MethodGet)
|
||||
router.PathPrefix("/assets/").Handler(
|
||||
http.StripPrefix("/assets/",
|
||||
http.FileServer(
|
||||
|
|
|
|||
|
|
@ -297,6 +297,21 @@ var TplMap = map[string]string{
|
|||
<input type="submit" value="Submit">
|
||||
</form>
|
||||
{{ 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" }}
|
||||
<section>
|
||||
<h1>Status Updater</h1>
|
||||
|
|
@ -314,7 +329,7 @@ var TplMap = map[string]string{
|
|||
Drag the following link to your bookmarks toolbar:
|
||||
</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>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>
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@
|
|||
Drag the following link to your bookmarks toolbar:
|
||||
</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>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>
|
||||
|
|
|
|||
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) {
|
||||
protectClickJacking(w)
|
||||
user, _ := h.sess.Get(r)
|
||||
statuses, err := h.storage.LatestStatuses()
|
||||
if err != nil {
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package handler
|
|||
import "net/http"
|
||||
|
||||
func (h *Handler) showSettingsView(w http.ResponseWriter, r *http.Request) {
|
||||
protectClickJacking(w)
|
||||
username, err := h.getUser(r)
|
||||
if err != nil {
|
||||
unauthorized(w)
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import (
|
|||
)
|
||||
|
||||
func (h *Handler) showNewStatusView(w http.ResponseWriter, r *http.Request) {
|
||||
protectClickJacking(w)
|
||||
_, err := h.getUser(r)
|
||||
if err != nil {
|
||||
unauthorized(w)
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ func RouteInt64Param(r *http.Request, param string) int64 {
|
|||
}
|
||||
|
||||
func (h *Handler) showEditStatusView(w http.ResponseWriter, r *http.Request) {
|
||||
protectClickJacking(w)
|
||||
user, err := h.getUser(r)
|
||||
if err != nil {
|
||||
unauthorized(w)
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import (
|
|||
)
|
||||
|
||||
func (h *Handler) handleRemoveStatus(w http.ResponseWriter, r *http.Request) {
|
||||
protectClickJacking(w)
|
||||
user, err := h.getUser(r)
|
||||
if err != nil {
|
||||
unauthorized(w)
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ func (h *Handler) initTpl() {
|
|||
for name, content := range TplMap {
|
||||
views[name] = template.Must(template.New("main").Funcs(template.FuncMap{
|
||||
"faces": func() []string {
|
||||
return []string{"🙂", "😎", "😛", "🥰", "👽", "😱", "🤔", "😯", "🤒", "😡", "🥺", "🥳", "🤖", "💀", "😴", "😭", "☕", "🍺"}
|
||||
return []string{"🙂", "😎", "😛", "🥰", "👽", "😱", "🤔", "😯", "🤒", "😡", "🥺", "🥳", "🤖", "💀", "😴", "😭", "☕", "🍺", "📖", "🔥", "❄️", "✨", "💡", "🎼"}
|
||||
}}).Parse(commonTemplates + content))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ import (
|
|||
"image"
|
||||
"image/color"
|
||||
"image/draw"
|
||||
"status/model"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
|
|
@ -96,7 +97,7 @@ type statusjson struct {
|
|||
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"]
|
||||
if !h.storage.UserExists(user) {
|
||||
notFound(w)
|
||||
|
|
@ -119,6 +120,19 @@ func (h *Handler) showUserStatusView(w http.ResponseWriter, r *http.Request) {
|
|||
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 {
|
||||
var (
|
||||
fgColor image.Image
|
||||
|
|
@ -162,6 +176,46 @@ func createAvatar(text string) *image.RGBA {
|
|||
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) {
|
||||
user := mux.Vars(r)["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."
|
||||
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
|
||||
})
|
||||
`))
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ func New(key string, storage *storage.Storage) *Session {
|
|||
store := sessions.NewCookieStore([]byte(key))
|
||||
store.Options = &sessions.Options{
|
||||
HttpOnly: true,
|
||||
Secure: true,
|
||||
MaxAge: 86400 * 30,
|
||||
}
|
||||
return &Session{
|
||||
|
|
|
|||
Loading…
Reference in a new issue