2021-02-04 10:36:02 +00:00
|
|
|
package web
|
|
|
|
|
|
|
|
import (
|
|
|
|
"errors"
|
2021-02-08 16:47:42 +00:00
|
|
|
"fmt"
|
2021-02-04 11:00:12 +00:00
|
|
|
"html/template"
|
2021-02-08 16:47:42 +00:00
|
|
|
"io/ioutil"
|
2021-02-04 10:36:02 +00:00
|
|
|
"net/url"
|
2021-02-08 16:47:42 +00:00
|
|
|
"os"
|
|
|
|
"path/filepath"
|
2021-02-04 10:36:02 +00:00
|
|
|
"strconv"
|
|
|
|
|
2021-02-08 16:47:42 +00:00
|
|
|
"github.com/gorilla/securecookie"
|
|
|
|
|
2021-02-04 10:36:02 +00:00
|
|
|
"github.com/go-kit/kit/log/level"
|
|
|
|
"github.com/gorilla/mux"
|
2021-02-08 16:47:42 +00:00
|
|
|
"github.com/ssb-ngi-pointer/gossb-rooms/internal/repo"
|
2021-02-04 10:36:02 +00:00
|
|
|
"go.mindeco.de/logging"
|
|
|
|
)
|
|
|
|
|
2021-02-04 16:46:51 +00:00
|
|
|
//go:generate go run -tags=dev embedded_generate.go
|
2021-02-04 16:21:21 +00:00
|
|
|
|
2021-02-04 11:00:12 +00:00
|
|
|
func TemplateFuncs(m *mux.Router) template.FuncMap {
|
|
|
|
return template.FuncMap{
|
|
|
|
"urlTo": NewURLTo(m),
|
|
|
|
"inc": func(i int) int { return i + 1 },
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-04 10:36:02 +00:00
|
|
|
func NewURLTo(appRouter *mux.Router) func(string, ...interface{}) *url.URL {
|
|
|
|
l := logging.Logger("helper.URLTo") // TOOD: inject in a scoped way
|
|
|
|
return func(routeName string, ps ...interface{}) *url.URL {
|
|
|
|
route := appRouter.Get(routeName)
|
|
|
|
if route == nil {
|
|
|
|
level.Warn(l).Log("msg", "no such route", "route", routeName, "params", ps)
|
|
|
|
return &url.URL{}
|
|
|
|
}
|
|
|
|
|
|
|
|
var params []string
|
|
|
|
for _, p := range ps {
|
|
|
|
switch v := p.(type) {
|
|
|
|
case string:
|
|
|
|
params = append(params, v)
|
|
|
|
case int:
|
|
|
|
params = append(params, strconv.Itoa(v))
|
|
|
|
case int64:
|
|
|
|
params = append(params, strconv.FormatInt(v, 10))
|
|
|
|
|
|
|
|
default:
|
|
|
|
level.Error(l).Log("msg", "invalid param type", "param", p, "route", routeName)
|
|
|
|
logging.CheckFatal(errors.New("invalid param"))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
u, err := route.URLPath(params...)
|
|
|
|
if err != nil {
|
|
|
|
level.Error(l).Log("msg", "failed to create URL",
|
|
|
|
"route", routeName,
|
|
|
|
"params", params,
|
|
|
|
"error", err)
|
|
|
|
return &url.URL{}
|
|
|
|
}
|
|
|
|
return u
|
|
|
|
}
|
|
|
|
}
|
2021-02-08 16:47:42 +00:00
|
|
|
|
|
|
|
// LoadOrCreateCookieSecrets either parses the bytes from $repo/web/cookie-secret or creates a new file with suitable keys in it
|
|
|
|
func LoadOrCreateCookieSecrets(repo repo.Interface) ([]securecookie.Codec, error) {
|
|
|
|
secretPath := repo.GetPath("web", "cookie-secret")
|
|
|
|
err := os.MkdirAll(filepath.Dir(secretPath), 0700)
|
|
|
|
if err != nil && !os.IsExist(err) {
|
|
|
|
return nil, fmt.Errorf("failed to create folder for cookie secret: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// load the existing data
|
|
|
|
secrets, err := ioutil.ReadFile(secretPath)
|
|
|
|
if err != nil {
|
|
|
|
if !os.IsNotExist(err) {
|
|
|
|
return nil, fmt.Errorf("failed to load cookie secrets: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// create new keys, save them and return the codec
|
|
|
|
hashKey := securecookie.GenerateRandomKey(32)
|
|
|
|
blockKey := securecookie.GenerateRandomKey(32)
|
|
|
|
|
|
|
|
data := append(hashKey, blockKey...)
|
|
|
|
err = ioutil.WriteFile(secretPath, data, 0600)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
sc := securecookie.CodecsFromPairs(hashKey, blockKey)
|
|
|
|
return sc, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// secrets should contain multiple of 64byte (to enable key rotation as supported by gorilla)
|
|
|
|
if n := len(secrets); n%64 != 0 {
|
|
|
|
return nil, fmt.Errorf("expected multiple of 64bytes in cookie secret file but got: %d", n)
|
|
|
|
}
|
|
|
|
|
|
|
|
// range over the secrets []byte in chunks of 64 bytes
|
|
|
|
// and slice it into 32byte pairs
|
|
|
|
var pairs [][]byte
|
|
|
|
|
|
|
|
// the increment/next part (which usually is i++)
|
|
|
|
// is the multiple comma assigment (a,b = b+1,a-1)
|
|
|
|
// so chunk is the next 64 bytes and then it slices of the first 64 bytes of secrets for the next iteration
|
|
|
|
for chunk := secrets[:64]; len(secrets) >= 64; chunk, secrets = secrets[:64], secrets[64:] {
|
|
|
|
pairs = append(pairs,
|
|
|
|
chunk[0:32], // hash key
|
|
|
|
chunk[32:64], // block key
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
sc := securecookie.CodecsFromPairs(pairs...)
|
|
|
|
return sc, nil
|
|
|
|
}
|