~m15o/ichi

cc807bb08b69264e7a963ff54493e635531a14de — m15o 4 months ago 28709ac master
add csrf
M config/cfg.go => config/cfg.go +2 -0
@@ 18,6 18,7 @@ type (
		CheckPwdScript  string
		NewFolderScript string
		QuotaScript     string
		CSRFKey         string
		NeedsKey        bool
	}
)


@@ 38,6 39,7 @@ func New() *Config {
		CheckPwdScript:  os.Getenv("CHECK_PWD_SCRIPT"),
		NewFolderScript: os.Getenv("NEW_FOLDER_SCRIPT"),
		QuotaScript:     os.Getenv("QUOTA_SCRIPT"),
		CSRFKey:         os.Getenv("CSRF_KEY"),
		NeedsKey:        os.Getenv("NEEDS_KEY") != "",
	}
}

M go.mod => go.mod +1 -0
@@ 3,6 3,7 @@ module ichi
go 1.16

require (
	github.com/gorilla/csrf v1.7.1 // indirect
	github.com/gorilla/mux v1.8.0
	github.com/gorilla/sessions v1.2.1
	github.com/lib/pq v1.10.3

M go.sum => go.sum +4 -0
@@ 1,3 1,5 @@
github.com/gorilla/csrf v1.7.1 h1:Ir3o2c1/Uzj6FBxMlAUB6SivgVMy1ONXwYgXn+/aHPE=
github.com/gorilla/csrf v1.7.1/go.mod h1:+a/4tCmqhG6/w4oafeAZ9pEa3/NZOWYVbD9fV0FwIQA=
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ=


@@ 6,6 8,8 @@ github.com/gorilla/sessions v1.2.1 h1:DHd3rPN5lE3Ts3D8rKkQ8x/0kqfeNmBAaiSi+o7Fsg
github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM=
github.com/lib/pq v1.10.3 h1:v9QZf2Sn6AmjXtQeFpdoq/eaNtYP6IN+7lcrygsIAtg=
github.com/lib/pq v1.10.3/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
golang.org/x/net v0.0.0-20210929193557-e81a3d93ecf6 h1:Z04ewVs7JhXaYkmDhBERPi41gnltfQpMWDnTnQbaCqk=
golang.org/x/net v0.0.0-20210929193557-e81a3d93ecf6/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=

M web/handler/common.go => web/handler/common.go +2 -0
@@ 28,7 28,9 @@ var TplCommonMap = map[string]string{
    <footer>
        <p>
            <a href="/"><img src="/banner.png" alt="Ichi"/></a>
            <a href="https://status.cafe"><img src="https://status.cafe/assets/button.png" alt="Status Cafe"/></a>
            <a target="_blank" href="https://melonking.net"><img alt="Visit Melonking.Net!" src="https://melonking.net/images/badges/MELON-BADGE-2.GIF"></a>
            <a href="https://forum.agoraroad.com/"><img alt="Agora Road" src="https://forum.agoraroad.com/data/addonflare/awardsystem/icons/180.gif"></a>
            <a href="/help#exchange-badge">Your badge here?</a>
        </p>
        <script src="https://yesterwebring.neocities.org/script-text.js"></script>

M web/handler/editor.go => web/handler/editor.go +8 -6
@@ 1,6 1,7 @@
package handler

import (
	"github.com/gorilla/csrf"
	"ichi/web/handler/form"
	"io"
	"net/http"


@@ 69,12 70,13 @@ func (h *Handler) handleEditor(w http.ResponseWriter, r *http.Request) {
			mode = "css"
		}
		h.view("editor").Execute(w, map[string]interface{}{
			"Author":  user,
			"Dir":     dir,
			"File":    file,
			"Content": content,
			"Nav":     makeNav(dir),
			"Mode":    mode,
			"Author":         user,
			"Dir":            dir,
			"File":           file,
			"Content":        content,
			"Nav":            makeNav(dir),
			"Mode":           mode,
			csrf.TemplateTag: csrf.TemplateField(r),
		})
		return
	} else {

M web/handler/files.go => web/handler/files.go +17 -12
@@ 3,6 3,7 @@ package handler
import (
	"errors"
	"fmt"
	"github.com/gorilla/csrf"
	"html/template"
	"ichi/web/handler/form"
	"io"


@@ 88,11 89,12 @@ func (h *Handler) handleFiles(w http.ResponseWriter, r *http.Request) {
		serverError(w, err)
	}
	h.renderLayout(w, "files", map[string]interface{}{
		"Dir":   dir,
		"Page":  page,
		"Files": finfos,
		"Nav":   makeNav(dir),
		"Quota": q,
		"Dir":            dir,
		"Page":           page,
		"Files":          finfos,
		"Nav":            makeNav(dir),
		"Quota":          q,
		csrf.TemplateTag: csrf.TemplateField(r),
	}, user)
}



@@ 148,8 150,9 @@ func (h *Handler) handleNewFile(w http.ResponseWriter, r *http.Request) {
	switch r.Method {
	case "GET":
		h.renderLayout(w, "new-file", map[string]interface{}{
			"Dir": dir,
			"Nav": makeNav(dir),
			"Dir":            dir,
			"Nav":            makeNav(dir),
			csrf.TemplateTag: csrf.TemplateField(r),
		}, user)
		return
	case "POST":


@@ 197,9 200,10 @@ func (h *Handler) handleRename(w http.ResponseWriter, r *http.Request) {
	switch r.Method {
	case "GET":
		h.renderLayout(w, "rename", map[string]interface{}{
			"Dir":  dir,
			"File": file,
			"Nav":  makeNav(dir),
			"Dir":            dir,
			"File":           file,
			"Nav":            makeNav(dir),
			csrf.TemplateTag: csrf.TemplateField(r),
		}, user)
		return
	case "POST":


@@ 230,8 234,9 @@ func (h *Handler) handleNewFolder(w http.ResponseWriter, r *http.Request) {
	switch r.Method {
	case "GET":
		h.renderLayout(w, "new-folder", map[string]interface{}{
			"Dir": dir,
			"Nav": makeNav(dir),
			"Dir":            dir,
			"Nav":            makeNav(dir),
			csrf.TemplateTag: csrf.TemplateField(r),
		}, user)
		return
	case "POST":

M web/handler/handler.go => web/handler/handler.go +1 -1
@@ 59,7 59,7 @@ func New(cfg *config.Config, sess *session.Session, host string, data *storage.S
	}
	h.initTpl()

	// Index
	// Routes
	router.HandleFunc("/", h.showIndexView).Methods(http.MethodGet)
	router.HandleFunc("/login", h.showLoginView).Methods(http.MethodGet)
	router.HandleFunc("/check-login", h.checkLogin).Methods(http.MethodPost)

M web/handler/html.go => web/handler/html.go +8 -9
@@ 42,6 42,7 @@ var TplMap = map[string]string{
</head>
<body>
<form>
    {{ .csrfField }}
    <nav><a href="/"><img src="/logo.png" alt="ichi" style="width: 88px; height: 31px;"></a></nav>
    <div class="file">
        <input type="hidden" name="name" value="{{ .File }}"/>


@@ 80,15 81,6 @@ var TplMap = map[string]string{
            }
        });
    })
    // function save(e) {
    //     e.stopPropagation();
    //     e.preventDefault();
    //     const form = new FormData(document.querySelector('form'));
    //     fetch('/editor?dir={{ .Dir }}', {
    //         method: 'POST',
    //         body: form
    //     });
    // }
</script>
</body>
</html>`,


@@ 180,6 172,7 @@ var TplMap = map[string]string{
            <td style="padding: 2px 8px; text-align: right;"><a href="/rename?dir={{ $.Dir }}&file={{ .Name }}">rename</a></li></td>
            <td style="text-align: right;">
                <form action="/delete?dir={{ $.Dir }}&file={{ .Name }}" method="post" class="auth-form">
                    {{ $.csrfField }}
                    <input style="padding: 0 4px;" class="button" type="submit" onclick="return confirm('delete?');" value="delete">
                </form>
            </td>


@@ 193,6 186,7 @@ var TplMap = map[string]string{
            action="/upload?dir={{ .Dir }}"
            method="post"
    >
        {{ .csrfField }}
        <input type="file" name="file" />
        <input type="submit" value="upload" />
    </form>


@@ 377,6 371,7 @@ var TplMap = map[string]string{
    <p>{{ .form.Error }}</p>
    {{ end }}
    <form action="/check-login" method="post" class="auth-form">
        {{ .csrfField }}
        <div class="field">
            <label for="name">Username</label>
            <input type="text" id="name" name="name" autocomplete="off" required autofocus/>


@@ 395,6 390,7 @@ var TplMap = map[string]string{
<article>
    <nav>{{ .Nav }}</nav>
    <form action="/new-file?dir={{ .Dir }}" method="post" class="auth-form">
        {{ .csrfField }}
        <div class="field">
            <input placeholder="file name" type="text" id="name" name="name" autocomplete="off" required/>
            <input type="hidden" name="content" value=""/>


@@ 408,6 404,7 @@ var TplMap = map[string]string{
<article>
    <nav>{{ .Nav }}</nav>
    <form action="/new-folder?dir={{ .Dir }}" method="post" class="auth-form">
        {{ .csrfField }}
        <div class="field">
            <input placeholder="folder name" type="text" id="name" name="name" autocomplete="off" required/>
        </div>


@@ 433,6 430,7 @@ var TplMap = map[string]string{
    <p>{{ .form.Error }}</p>
    {{ end }}
    <form action="/register" method="post" class="auth-form">
        {{ .csrfField }}
        <div class="field">
            <label for="name">Username</label>
            <input type="text" id="name" name="name" autocomplete="off" required autofocus/>


@@ 464,6 462,7 @@ var TplMap = map[string]string{
<article>
    <nav>{{ .Nav }}/{{ .File }}</nav>
    <form action="/rename?dir={{ .Dir }}&file={{ .File }}" method="post" class="auth-form">
        {{ .csrfField }}
        <div class="field">
            <input placeholder="file name" type="text" id="name" name="name" autocomplete="off" required/>
            <input type="hidden" name="content" value=""/>

M web/handler/html/common/layout.html => web/handler/html/common/layout.html +2 -0
@@ 23,7 23,9 @@
    <footer>
        <p>
            <a href="/"><img src="/banner.png" alt="Ichi"/></a>
            <a href="https://status.cafe"><img src="https://status.cafe/assets/button.png" alt="Status Cafe"/></a>
            <a target="_blank" href="https://melonking.net"><img alt="Visit Melonking.Net!" src="https://melonking.net/images/badges/MELON-BADGE-2.GIF"></a>
            <a href="https://forum.agoraroad.com/"><img alt="Agora Road" src="https://forum.agoraroad.com/data/addonflare/awardsystem/icons/180.gif"></a>
            <a href="/help#exchange-badge">Your badge here?</a>
        </p>
        <script src="https://yesterwebring.neocities.org/script-text.js"></script>

M web/handler/html/editor.html => web/handler/html/editor.html +1 -9
@@ 37,6 37,7 @@
</head>
<body>
<form>
    {{ .csrfField }}
    <nav><a href="/"><img src="/logo.png" alt="ichi" style="width: 88px; height: 31px;"></a></nav>
    <div class="file">
        <input type="hidden" name="name" value="{{ .File }}"/>


@@ 75,15 76,6 @@
            }
        });
    })
    // function save(e) {
    //     e.stopPropagation();
    //     e.preventDefault();
    //     const form = new FormData(document.querySelector('form'));
    //     fetch('/editor?dir={{ .Dir }}', {
    //         method: 'POST',
    //         body: form
    //     });
    // }
</script>
</body>
</html>
\ No newline at end of file

M web/handler/html/files.html => web/handler/html/files.html +2 -0
@@ 39,6 39,7 @@
            <td style="padding: 2px 8px; text-align: right;"><a href="/rename?dir={{ $.Dir }}&file={{ .Name }}">rename</a></li></td>
            <td style="text-align: right;">
                <form action="/delete?dir={{ $.Dir }}&file={{ .Name }}" method="post" class="auth-form">
                    {{ $.csrfField }}
                    <input style="padding: 0 4px;" class="button" type="submit" onclick="return confirm('delete?');" value="delete">
                </form>
            </td>


@@ 52,6 53,7 @@
            action="/upload?dir={{ .Dir }}"
            method="post"
    >
        {{ .csrfField }}
        <input type="file" name="file" />
        <input type="submit" value="upload" />
    </form>

M web/handler/html/login.html => web/handler/html/login.html +1 -0
@@ 5,6 5,7 @@
    <p>{{ .form.Error }}</p>
    {{ end }}
    <form action="/check-login" method="post" class="auth-form">
        {{ .csrfField }}
        <div class="field">
            <label for="name">Username</label>
            <input type="text" id="name" name="name" autocomplete="off" required autofocus/>

M web/handler/html/new-file.html => web/handler/html/new-file.html +1 -0
@@ 3,6 3,7 @@
<article>
    <nav>{{ .Nav }}</nav>
    <form action="/new-file?dir={{ .Dir }}" method="post" class="auth-form">
        {{ .csrfField }}
        <div class="field">
            <input placeholder="file name" type="text" id="name" name="name" autocomplete="off" required/>
            <input type="hidden" name="content" value=""/>

M web/handler/html/new-folder.html => web/handler/html/new-folder.html +1 -0
@@ 3,6 3,7 @@
<article>
    <nav>{{ .Nav }}</nav>
    <form action="/new-folder?dir={{ .Dir }}" method="post" class="auth-form">
        {{ .csrfField }}
        <div class="field">
            <input placeholder="folder name" type="text" id="name" name="name" autocomplete="off" required/>
        </div>

M web/handler/html/register.html => web/handler/html/register.html +1 -0
@@ 5,6 5,7 @@
    <p>{{ .form.Error }}</p>
    {{ end }}
    <form action="/register" method="post" class="auth-form">
        {{ .csrfField }}
        <div class="field">
            <label for="name">Username</label>
            <input type="text" id="name" name="name" autocomplete="off" required autofocus/>

M web/handler/html/rename.html => web/handler/html/rename.html +1 -0
@@ 3,6 3,7 @@
<article>
    <nav>{{ .Nav }}/{{ .File }}</nav>
    <form action="/rename?dir={{ .Dir }}&file={{ .File }}" method="post" class="auth-form">
        {{ .csrfField }}
        <div class="field">
            <input placeholder="file name" type="text" id="name" name="name" autocomplete="off" required/>
            <input type="hidden" name="content" value=""/>

M web/handler/login_check.go => web/handler/login_check.go +3 -1
@@ 1,6 1,7 @@
package handler

import (
	"github.com/gorilla/csrf"
	"ichi/web/handler/form"
	"net/http"
	"os/exec"


@@ 12,7 13,8 @@ func (h *Handler) checkLogin(w http.ResponseWriter, r *http.Request) {
	if err != nil {
		f.Error = "incorrect password"
		h.renderLayout(w, "login", map[string]interface{}{
			"form": f,
			"form":           f,
			csrf.TemplateTag: csrf.TemplateField(r),
		}, "")
		return
	}

M web/handler/login_show.go => web/handler/login_show.go +3 -1
@@ 1,6 1,7 @@
package handler

import (
	"github.com/gorilla/csrf"
	"ichi/web/handler/form"
	"net/http"
)


@@ 8,6 9,7 @@ import (
func (h *Handler) showLoginView(w http.ResponseWriter, r *http.Request) {
	loginForm := form.LoginForm{}
	h.renderLayout(w, "login", map[string]interface{}{
		"form": loginForm,
		"form":           loginForm,
		csrf.TemplateTag: csrf.TemplateField(r),
	}, "")
}

M web/handler/register.go => web/handler/register.go +2 -1
@@ 5,6 5,7 @@ import (
	"encoding/json"
	"errors"
	"fmt"
	"github.com/gorilla/csrf"
	"html/template"
	"ichi/web/handler/form"
	"io/ioutil"


@@ 31,7 32,7 @@ func (h *Handler) handleRegister(w http.ResponseWriter, r *http.Request) {
func (h *Handler) showRegisterView(w http.ResponseWriter, r *http.Request) {
	switch r.Method {
	case "GET":
		h.renderLayout(w, "register", map[string]interface{}{"needsKey": h.cfg.NeedsKey}, "")
		h.renderLayout(w, "register", map[string]interface{}{"needsKey": h.cfg.NeedsKey, csrf.TemplateTag: csrf.TemplateField(r)}, "")
	}
}


M web/web.go => web/web.go +4 -2
@@ 2,6 2,7 @@ package web

import (
	"fmt"
	"github.com/gorilla/csrf"
	"ichi/config"
	"ichi/storage"
	"ichi/web/handler"


@@ 31,11 32,12 @@ func Serve(data *storage.Storage, cfg *config.Config) error {
	if err != nil {
		log.Fatal(err)
	}
	CSRF := csrf.Protect([]byte(cfg.CSRFKey), csrf.MaxAge(0))
	switch cfg.Env {
	case "PROD":
		go func() {
			fmt.Printf("Starting HTTP server on :443\n")
			err := http.ListenAndServeTLS(":443", cfg.CertFile, cfg.KeyFile, s)
			err := http.ListenAndServeTLS(":443", cfg.CertFile, cfg.KeyFile, CSRF(s))
			if err != nil {
				log.Fatalf("httpsSrv.ListendAndServeTLS() failed with %s", err)
			}


@@ 45,7 47,7 @@ func Serve(data *storage.Storage, cfg *config.Config) error {
		break
	default:
		fmt.Printf("Starting HTTP server on %s\n", host)
		err = http.ListenAndServe(host, s)
		err = http.ListenAndServe(host, CSRF(s))
	}
	return err
}