Merge branch 'develop'

This commit is contained in:
Lars Hoogestraat 2020-12-07 18:40:17 +01:00
commit 385c1207d3
59 changed files with 509 additions and 623 deletions

3
.gitignore vendored
View File

@ -32,9 +32,6 @@ releases
.idea .idea
.csrftoken .csrftoken
/clt/createUser
/clt/initDatabase
/go-blog
releases releases
db/ db/

View File

@ -1,8 +1,14 @@
BINARYNAME=go-blog BINARYNAME=go-blog
TMP=tmp TMP=tmp
DIST=release
GITHASH=$(shell git rev-parse HEAD) GITHASH=$(shell git rev-parse HEAD)
BUILD_VERSION=$(shell git describe --tags) BUILD_VERSION=$(shell git describe --tags)
ifndef $(GOPATH)
GOPATH=$(shell go env GOPATH)
export GOPATH
endif
RELEASE="releases" RELEASE="releases"
LDFLAGS=-ldflags '-X main.BuildVersion=${BUILD_VERSION} -X main.GitHash=${GITHASH}' LDFLAGS=-ldflags '-X main.BuildVersion=${BUILD_VERSION} -X main.GitHash=${GITHASH}'
@ -24,7 +30,7 @@ install:
package: package:
-rm -r ${TMP} -rm -r ${TMP}
mkdir -p ${TMP}/clt mkdir -p ${TMP}/clt
-mkdir -p releases -mkdir -p releases/custom
cp ${GOPATH}/bin/go-blog ${TMP}/ cp ${GOPATH}/bin/go-blog ${TMP}/
cp ${GOPATH}/bin/create_user ${TMP}/clt cp ${GOPATH}/bin/create_user ${TMP}/clt
cp ${GOPATH}/bin/init_database ${TMP}/clt cp ${GOPATH}/bin/init_database ${TMP}/clt

View File

@ -12,10 +12,9 @@ import (
"os" "os"
"syscall" "syscall"
"git.hoogi.eu/snafu/go-blog/components/database" "git.hoogi.eu/snafu/go-blog/database"
"git.hoogi.eu/snafu/go-blog/components/logger" "git.hoogi.eu/snafu/go-blog/logger"
"git.hoogi.eu/snafu/go-blog/models" "git.hoogi.eu/snafu/go-blog/models"
"git.hoogi.eu/snafu/go-blog/utils"
"golang.org/x/crypto/ssh/terminal" "golang.org/x/crypto/ssh/terminal"
) )
@ -38,14 +37,31 @@ func main() {
fmt.Printf("create_user version %s\n", BuildVersion) fmt.Printf("create_user version %s\n", BuildVersion)
username := flag.String("username", "", "Username for the admin user ") username := flag.String("username", "", "Username for the admin user. (required)")
email := flag.String("email", "", "Email for the created user ") email := flag.String("email", "", "Email for the created user. (required)")
displayName := flag.String("displayname", "", "Display name for the admin user ") displayName := flag.String("displayname", "", "Display name for the admin user. (required)")
isAdmin := flag.Bool("admin", false, "If set a new administrator will be created; otherwise a non-admin is created") isAdmin := flag.Bool("admin", false, "If set a new user with admin permissions will be created; otherwise a non-admin is created.")
file := flag.String("sqlite", "", "Location to the sqlite3 database file") file := flag.String("sqlite", "", "Location to the sqlite3 database file. (required)")
flag.Parse() flag.Parse()
if *username == "" {
fmt.Println("the username (-username) must be specified")
os.Exit(1)
}
if *email == "" {
fmt.Println("the email (-email) must be specified")
os.Exit(1)
}
if *displayName == "" {
fmt.Println("the display name (-displayname) must be specified")
os.Exit(1)
}
if *file == "" {
fmt.Println("the argument -sqlite is empty. Please specify the location of the sqlite3 database file")
os.Exit(1)
}
if flag.Parsed() { if flag.Parsed() {
initUser := createUserFlag{ initUser := createUserFlag{
username: *username, username: *username,
@ -55,16 +71,13 @@ func main() {
sqlite: *file, sqlite: *file,
} }
if err := initUser.validate(); err != nil {
fmt.Println(err)
os.Exit(1)
}
fmt.Printf("Password: ") fmt.Printf("Password: ")
pw, err := terminal.ReadPassword(int(syscall.Stdin)) pw, err := terminal.ReadPassword(int(syscall.Stdin))
fmt.Println("")
if err != nil { if err != nil {
fmt.Printf("could not read password %v", err) fmt.Printf("could not read password %v\n", err)
os.Exit(1) os.Exit(1)
} }
@ -85,26 +98,7 @@ func main() {
} }
} }
func (userFlags createUserFlag) validate() error {
if utils.TrimmedStringIsEmpty(userFlags.username) {
return fmt.Errorf("the username (-username) must be specified")
}
if utils.TrimmedStringIsEmpty(userFlags.email) {
return fmt.Errorf("the email (-email) must be specified")
}
if utils.TrimmedStringIsEmpty(userFlags.displayName) {
return fmt.Errorf("the display name (-displayname) must be specified")
}
if utils.TrimmedStringIsEmpty(userFlags.sqlite) {
return fmt.Errorf("the argument -sqlite is empty. Please specify the location of the sqlite3 database file")
}
return nil
}
func (userFlags createUserFlag) CreateUser() error { func (userFlags createUserFlag) CreateUser() error {
var userService models.UserService var userService models.UserService
dbConfig := database.SQLiteConfig{ dbConfig := database.SQLiteConfig{

View File

@ -12,8 +12,8 @@ import (
"os" "os"
"strings" "strings"
"git.hoogi.eu/snafu/go-blog/components/database" "git.hoogi.eu/snafu/go-blog/database"
"git.hoogi.eu/snafu/go-blog/components/logger" "git.hoogi.eu/snafu/go-blog/logger"
) )
var ( var (

View File

@ -2,46 +2,45 @@
// Use of this source code is governed by a MIT-style // Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
package utils //package crypt
package crypt
import ( import (
"crypto/rand" "crypto/rand"
"crypto/sha512" "crypto/sha512"
"encoding/base64"
"encoding/hex" "encoding/hex"
"fmt"
"io" "io"
"math/big" "math/big"
"golang.org/x/crypto/bcrypt" "golang.org/x/crypto/bcrypt"
) )
const ( var (
//AlphaUpper all upper alphas chars //AlphaUpper all upper alphas chars
AlphaUpper = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" AlphaUpper = RandomSource("ABCDEFGHIJKLMNOPQRSTUVWXYZ")
//AlphaLower all lowers alphas chars //AlphaLower all lowers alphas chars
AlphaLower = "abcdefghijklmnopqrstuvwxyz" AlphaLower = RandomSource("abcdefghijklmnopqrstuvwxyz")
//AlphaUpperLower all upper and lowers aplhas chars //AlphaUpperLower all upper and lowers aplhas chars
AlphaUpperLower = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" AlphaUpperLower = RandomSource("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz")
//AlphaUpperLowerNumeric all upper lowers alphas and numerics //AlphaUpperLowerNumeric all upper lowers alphas and numerics
AlphaUpperLowerNumeric = "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890abcdefghijklmnopqrstuvwxyz" AlphaUpperLowerNumeric = RandomSource("ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890abcdefghijklmnopqrstuvwxyz")
//AlphaUpperLowerNumericSpecial all upper lowers alphas, numerics and special chas //AlphaUpperLowerNumericSpecial all upper lowers alphas, numerics and special chars
AlphaUpperLowerNumericSpecial = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz123456890" + AlphaUpperLowerNumericSpecial = RandomSource("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz123456890" +
"!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~" "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~")
) )
//RandomSource express which chars should be considered //RandomSource string containing which characters should be considered when generating random sequences
type RandomSource struct { type RandomSource string
CharsToGen string
}
//RandomSequence returns random character with given length; //RandomSequence returns random character with given length;
//random source express which chars should be considered
func (r RandomSource) RandomSequence(length int) []byte { func (r RandomSource) RandomSequence(length int) []byte {
result := make([]byte, length) result := make([]byte, length)
for i := 0; i < length; i++ { for i := 0; i < length; i++ {
char, _ := rand.Int(rand.Reader, big.NewInt(int64(len(r.CharsToGen)))) char, _ := rand.Int(rand.Reader, big.NewInt(int64(len(r))))
result[i] = r.CharsToGen[int(char.Int64())] result[i] = r[int(char.Int64())]
} }
fmt.Println(result)
return result return result
} }
@ -54,7 +53,7 @@ func RandomSecureKey(length int) []byte {
return k return k
} }
//CryptPassword bcrypts a password at given costs //CryptPassword hashes a password with bcrypt and a given cost
func CryptPassword(password []byte, cost int) ([]byte, error) { func CryptPassword(password []byte, cost int) ([]byte, error) {
s, err := bcrypt.GenerateFromPassword(password, cost) s, err := bcrypt.GenerateFromPassword(password, cost)
@ -67,21 +66,7 @@ func CryptPassword(password []byte, cost int) ([]byte, error) {
//GenerateSalt generates a random salt with alphanumerics and some special characters //GenerateSalt generates a random salt with alphanumerics and some special characters
func GenerateSalt() []byte { func GenerateSalt() []byte {
r := RandomSource{ return AlphaUpperLowerNumericSpecial.RandomSequence(32)
CharsToGen: AlphaUpperLowerNumericSpecial,
}
return r.RandomSequence(32)
}
//EncodeBase64 encodes a string to base64
func EncodeBase64(input string) string {
return base64.StdEncoding.EncodeToString([]byte(input))
}
//DecodeBase64 descodes a string to base64
func DecodeBase64(b64 string) (string, error) {
out, err := base64.StdEncoding.DecodeString(b64)
return string(out), err
} }
func RandomHash(length int) string { func RandomHash(length int) string {

View File

@ -17,13 +17,7 @@ type SQLiteConfig struct {
//Open receives handle for sqlite database, returns an error if connection failed //Open receives handle for sqlite database, returns an error if connection failed
func (d SQLiteConfig) Open() (*sql.DB, error) { func (d SQLiteConfig) Open() (*sql.DB, error) {
db, err := sql.Open("sqlite3", d.File) return sql.Open("sqlite3", d.File)
if err != nil {
return nil, err
}
return db, nil
} }
//InitTables creates the tables //InitTables creates the tables
@ -148,5 +142,6 @@ func InitTables(db *sql.DB) error {
");"); err != nil { ");"); err != nil {
return err return err
} }
return nil return nil
} }

15
go.mod
View File

@ -4,17 +4,16 @@ go 1.15
require ( require (
git.hoogi.eu/snafu/cfg v1.0.6 git.hoogi.eu/snafu/cfg v1.0.6
git.hoogi.eu/snafu/session v1.1.2 git.hoogi.eu/snafu/session v1.2.0
github.com/gorilla/csrf v1.7.0 github.com/gorilla/csrf v1.7.0
github.com/gorilla/handlers v1.5.1 github.com/gorilla/handlers v1.5.1
github.com/gorilla/mux v1.8.0 github.com/gorilla/mux v1.8.0
github.com/justinas/alice v1.2.0 github.com/justinas/alice v1.2.0
github.com/mattn/go-sqlite3 v1.14.3 github.com/mattn/go-sqlite3 v1.14.5
github.com/microcosm-cc/bluemonday v1.0.4 github.com/microcosm-cc/bluemonday v1.0.4
github.com/russross/blackfriday/v2 v2.0.1 github.com/russross/blackfriday/v2 v2.1.0
github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect github.com/sirupsen/logrus v1.7.0
github.com/sirupsen/logrus v1.6.0 golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c
golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb // indirect
golang.org/x/net v0.0.0-20200904194848-62affa334b73 // indirect golang.org/x/sys v0.0.0-20201204225414-ed752295db88 // indirect
golang.org/x/sys v0.0.0-20200909081042-eff7692f9009 // indirect
) )

39
go.sum
View File

@ -2,6 +2,8 @@ git.hoogi.eu/snafu/cfg v1.0.6 h1:O34hYFqjwfnMjEwB4M8GaQQmBtf3H+AA0RFHb7PoMNs=
git.hoogi.eu/snafu/cfg v1.0.6/go.mod h1:LQolv8bqH8ZPz7h9PSVswdBXj1BIM+kW79AVS/jpE7A= git.hoogi.eu/snafu/cfg v1.0.6/go.mod h1:LQolv8bqH8ZPz7h9PSVswdBXj1BIM+kW79AVS/jpE7A=
git.hoogi.eu/snafu/session v1.1.2 h1:MclTbSqD/9JodRUqFc4OwyJGk5AtonqB6BaG5RRJHf8= git.hoogi.eu/snafu/session v1.1.2 h1:MclTbSqD/9JodRUqFc4OwyJGk5AtonqB6BaG5RRJHf8=
git.hoogi.eu/snafu/session v1.1.2/go.mod h1:kgRDrnHcKc9H18G9533BXy6qO+81eBf6e9gkUzBMDuA= git.hoogi.eu/snafu/session v1.1.2/go.mod h1:kgRDrnHcKc9H18G9533BXy6qO+81eBf6e9gkUzBMDuA=
git.hoogi.eu/snafu/session v1.2.0 h1:qmZXMCAZRrzcOOLsOltwPUeA4ckpjwjjJ8fQpFDSx+w=
git.hoogi.eu/snafu/session v1.2.0/go.mod h1:kgRDrnHcKc9H18G9533BXy6qO+81eBf6e9gkUzBMDuA=
github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk= github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=
github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4= github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=
github.com/chris-ramon/douceur v0.2.0 h1:IDMEdxlEUUBYBKE4z/mJnFyVXox+MjuEVDJNN27glkU= github.com/chris-ramon/douceur v0.2.0 h1:IDMEdxlEUUBYBKE4z/mJnFyVXox+MjuEVDJNN27glkU=
@ -22,37 +24,36 @@ github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyC
github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4= github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4=
github.com/justinas/alice v1.2.0 h1:+MHSA/vccVCF4Uq37S42jwlkvI2Xzl7zTPCN5BnZNVo= github.com/justinas/alice v1.2.0 h1:+MHSA/vccVCF4Uq37S42jwlkvI2Xzl7zTPCN5BnZNVo=
github.com/justinas/alice v1.2.0/go.mod h1:fN5HRH/reO/zrUflLfTN43t3vXvKzvZIENsNEe7i7qA= github.com/justinas/alice v1.2.0/go.mod h1:fN5HRH/reO/zrUflLfTN43t3vXvKzvZIENsNEe7i7qA=
github.com/konsorten/go-windows-terminal-sequences v1.0.3 h1:CE8S1cTafDpPvMhIxNJKvHsGVBgn1xWYf1NbHQhywc8= github.com/mattn/go-sqlite3 v1.14.5 h1:1IdxlwTNazvbKJQSxoJ5/9ECbEeaTTyeU7sEAZ5KKTQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/mattn/go-sqlite3 v1.14.5/go.mod h1:WVKg1VTActs4Qso6iwGbiFih2UIHo0ENGwNd0Lj+XmI=
github.com/mattn/go-sqlite3 v1.14.3 h1:j7a/xn1U6TKA/PHHxqZuzh64CdtRc7rU9M+AvkOl5bA=
github.com/mattn/go-sqlite3 v1.14.3/go.mod h1:WVKg1VTActs4Qso6iwGbiFih2UIHo0ENGwNd0Lj+XmI=
github.com/microcosm-cc/bluemonday v1.0.4 h1:p0L+CTpo/PLFdkoPcJemLXG+fpMD7pYOoDEq1axMbGg= github.com/microcosm-cc/bluemonday v1.0.4 h1:p0L+CTpo/PLFdkoPcJemLXG+fpMD7pYOoDEq1axMbGg=
github.com/microcosm-cc/bluemonday v1.0.4/go.mod h1:8iwZnFn2CDDNZ0r6UXhF4xawGvzaqzCRa1n3/lO3W2w= github.com/microcosm-cc/bluemonday v1.0.4/go.mod h1:8iwZnFn2CDDNZ0r6UXhF4xawGvzaqzCRa1n3/lO3W2w=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= github.com/sirupsen/logrus v1.7.0 h1:ShrD1U9pZB12TX0cVy0DtePoCH97K8EtX+mg7ZARUtM=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/sirupsen/logrus v1.6.0 h1:UBcNElsrwanuuMsnGSlYmtmgbb23qDR5dG+6X6Oo89I=
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a h1:vclmkQCjlDX5OydZ9wv8rBCcS0QyQY66Mpf/7BZbInM= golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c h1:9HhBz5L/UjnK9XLtiZhYAdue5BVKep3PMmS2LuPDt8k=
golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20200904194848-62affa334b73 h1:MXfv8rhZWmFeqX3GNZRsd6vOLoaCHjYEX3qkRo3YBUA= golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb h1:eBmm0M9fYhWpKZLjQUUKka/LtIxf46G4fxeEz5KJr9U=
golang.org/x/net v0.0.0-20200904194848-62affa334b73/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201204225414-ed752295db88 h1:KmZPnMocC93w341XZp26yTJg8Za7lhb2KhkYmixoeso=
golang.org/x/sys v0.0.0-20200909081042-eff7692f9009 h1:W0lCpv29Hv0UaM1LXb9QlBHLNP8UFfcKjblhVCWftOM= golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221 h1:/ZHdbVpdR/jk3g30/d4yUL0JU9kksj8+F/bnQUVLGDM=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=

View File

@ -1,4 +1,4 @@
package controllers package handler
import ( import (
"bytes" "bytes"
@ -8,8 +8,8 @@ import (
"net/http" "net/http"
"time" "time"
"git.hoogi.eu/snafu/go-blog/components/httperror" "git.hoogi.eu/snafu/go-blog/httperror"
"git.hoogi.eu/snafu/go-blog/components/logger" "git.hoogi.eu/snafu/go-blog/logger"
"git.hoogi.eu/snafu/go-blog/middleware" "git.hoogi.eu/snafu/go-blog/middleware"
"git.hoogi.eu/snafu/go-blog/models" "git.hoogi.eu/snafu/go-blog/models"
) )
@ -57,7 +57,7 @@ func AdminProfilePostHandler(ctx *middleware.AppContext, w http.ResponseWriter,
if len(u.PlainPassword) > 0 { if len(u.PlainPassword) > 0 {
changePassword = true changePassword = true
// Password change
u.PlainPassword = []byte(r.FormValue("password")) u.PlainPassword = []byte(r.FormValue("password"))
if !bytes.Equal(u.PlainPassword, []byte(r.FormValue("retyped_password"))) { if !bytes.Equal(u.PlainPassword, []byte(r.FormValue("retyped_password"))) {
@ -81,11 +81,11 @@ func AdminProfilePostHandler(ctx *middleware.AppContext, w http.ResponseWriter,
session.SetValue("userid", u.ID) session.SetValue("userid", u.ID)
sids := ctx.SessionService.SessionProvider.SessionIDsFromValues("userid", u.ID) sessions := ctx.SessionService.SessionProvider.FindSessionsByValue("userid", u.ID)
for _, sid := range sids { for _, sid := range sessions {
if sid != session.SessionID() { if sid.SessionID() != session.SessionID() {
ctx.SessionService.SessionProvider.Remove(sid) ctx.SessionService.SessionProvider.Remove(sid.SessionID())
} }
} }
} }
@ -230,7 +230,7 @@ func ResetPasswordHandler(ctx *middleware.AppContext, w http.ResponseWriter, r *
//ResetPasswordPostHandler handles the resetting of the password //ResetPasswordPostHandler handles the resetting of the password
func ResetPasswordPostHandler(ctx *middleware.AppContext, w http.ResponseWriter, r *http.Request) *middleware.Template { func ResetPasswordPostHandler(ctx *middleware.AppContext, w http.ResponseWriter, r *http.Request) *middleware.Template {
password := r.FormValue("password") password := r.FormValue("password")
repassword := r.FormValue("password_repeat") password2 := r.FormValue("password_repeat")
hash := getVar(r, "hash") hash := getVar(r, "hash")
t, err := ctx.TokenService.Get(hash, models.PasswordReset, time.Duration(1)*time.Hour) t, err := ctx.TokenService.Get(hash, models.PasswordReset, time.Duration(1)*time.Hour)
@ -251,7 +251,7 @@ func ResetPasswordPostHandler(ctx *middleware.AppContext, w http.ResponseWriter,
} }
} }
if password != repassword { if password != password2 {
return &middleware.Template{ return &middleware.Template{
Name: tplAdminResetPassword, Name: tplAdminResetPassword,
Err: httperror.New(http.StatusUnprocessableEntity, "The passwords entered do not match.", errors.New("the password entered did not match")), Err: httperror.New(http.StatusUnprocessableEntity, "The passwords entered do not match.", errors.New("the password entered did not match")),
@ -297,7 +297,7 @@ func ForgotPasswordPostHandler(ctx *middleware.AppContext, w http.ResponseWriter
u, err := ctx.UserService.GetByMail(email) u, err := ctx.UserService.GetByMail(email)
if err != nil { if err != nil {
if httperror.Equals(err, sql.ErrNoRows) { if errors.Is(err, sql.ErrNoRows) {
logger.Log.Error(err) logger.Log.Error(err)
return &middleware.Template{ return &middleware.Template{
RedirectPath: "admin", RedirectPath: "admin",

View File

@ -1,11 +1,11 @@
package controllers_test package handler_test
import ( import (
"net/http/httptest" "net/http/httptest"
"net/url" "net/url"
"testing" "testing"
"git.hoogi.eu/snafu/go-blog/controllers" "git.hoogi.eu/snafu/go-blog/handler"
"git.hoogi.eu/snafu/go-blog/models" "git.hoogi.eu/snafu/go-blog/models"
) )
@ -94,7 +94,7 @@ func doAdminProfileRequest(user reqUser, u *models.User, currentPassword string)
rw := httptest.NewRecorder() rw := httptest.NewRecorder()
re := r.buildRequest() re := r.buildRequest()
tpl := controllers.AdminProfilePostHandler(ctx, rw, re) tpl := handler.AdminProfilePostHandler(ctx, rw, re)
if tpl.Err != nil { if tpl.Err != nil {
return tpl.Err return tpl.Err
@ -122,7 +122,7 @@ func doActivateAccountRequest(user reqUser, password, passwordRepeat, hash strin
} }
rw := httptest.NewRecorder() rw := httptest.NewRecorder()
tpl := controllers.ActivateAccountPostHandler(ctx, rw, r.buildRequest()) tpl := handler.ActivateAccountPostHandler(ctx, rw, r.buildRequest())
if tpl.Err != nil { if tpl.Err != nil {
return tpl.Err return tpl.Err
@ -150,7 +150,7 @@ func doResetPasswordRequest(user reqUser, password, passwordRepeat, hash string)
} }
rw := httptest.NewRecorder() rw := httptest.NewRecorder()
tpl := controllers.AdminSiteEditPostHandler(ctx, rw, r.buildRequest()) tpl := handler.AdminSiteEditPostHandler(ctx, rw, r.buildRequest())
if tpl.Err != nil { if tpl.Err != nil {
return tpl.Err return tpl.Err

View File

@ -2,7 +2,7 @@
// Use of this source code is governed by a MIT-style // Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
package controllers package handler
import ( import (
"database/sql" "database/sql"
@ -10,10 +10,9 @@ import (
"net/http" "net/http"
"time" "time"
"git.hoogi.eu/snafu/go-blog/components/httperror" "git.hoogi.eu/snafu/go-blog/httperror"
"git.hoogi.eu/snafu/go-blog/middleware" "git.hoogi.eu/snafu/go-blog/middleware"
"git.hoogi.eu/snafu/go-blog/models" "git.hoogi.eu/snafu/go-blog/models"
"git.hoogi.eu/snafu/go-blog/utils"
) )
//GetArticleHandler returns a specific article //GetArticleHandler returns a specific article
@ -21,9 +20,11 @@ import (
func GetArticleHandler(ctx *middleware.AppContext, w http.ResponseWriter, r *http.Request) *middleware.Template { func GetArticleHandler(ctx *middleware.AppContext, w http.ResponseWriter, r *http.Request) *middleware.Template {
year := getVar(r, "year") year := getVar(r, "year")
month := getVar(r, "month") month := getVar(r, "month")
slug := getVar(r, "slug") headline := getVar(r, "slug")
a, err := ctx.ArticleService.GetBySlug(utils.AppendString(year, "/", month, "/", slug), nil, models.OnlyPublished) slug := year + "/" + month + "/" + headline
a, err := ctx.ArticleService.GetBySlug(slug, nil, models.OnlyPublished)
if err != nil { if err != nil {
return &middleware.Template{ return &middleware.Template{

View File

@ -2,7 +2,7 @@
// Use of this source code is governed by a MIT-style // Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
package controllers_test package handler_test
import ( import (
"fmt" "fmt"
@ -13,7 +13,7 @@ import (
"strconv" "strconv"
"testing" "testing"
"git.hoogi.eu/snafu/go-blog/controllers" "git.hoogi.eu/snafu/go-blog/handler"
"git.hoogi.eu/snafu/go-blog/models" "git.hoogi.eu/snafu/go-blog/models"
) )
@ -143,7 +143,7 @@ func doGetArticleBySlugRequest(user reqUser, article *models.Article) (*models.A
} }
rw := httptest.NewRecorder() rw := httptest.NewRecorder()
tpl := controllers.GetArticleHandler(ctx, rw, r.buildRequest()) tpl := handler.GetArticleHandler(ctx, rw, r.buildRequest())
if tpl.Err != nil { if tpl.Err != nil {
return nil, tpl.Err return nil, tpl.Err
@ -166,7 +166,7 @@ func doGetArticleByIDRequest(user reqUser, articleID int) (*models.Article, erro
} }
rw := httptest.NewRecorder() rw := httptest.NewRecorder()
tpl := controllers.GetArticleByIDHandler(ctx, rw, r.buildRequest()) tpl := handler.GetArticleByIDHandler(ctx, rw, r.buildRequest())
if tpl.Err != nil { if tpl.Err != nil {
return nil, tpl.Err return nil, tpl.Err
@ -196,7 +196,7 @@ func doAdminEditArticleRequest(user reqUser, articleID int, article *models.Arti
rw := httptest.NewRecorder() rw := httptest.NewRecorder()
tpl := controllers.AdminArticleEditPostHandler(ctx, rw, r.buildRequest()) tpl := handler.AdminArticleEditPostHandler(ctx, rw, r.buildRequest())
if tpl.Err != nil { if tpl.Err != nil {
return tpl.Err return tpl.Err
@ -222,7 +222,7 @@ func doAdminCreateArticleRequest(user reqUser, article *models.Article) (int, er
} }
rw := httptest.NewRecorder() rw := httptest.NewRecorder()
tpl := controllers.AdminArticleNewPostHandler(ctx, rw, r.buildRequest()) tpl := handler.AdminArticleNewPostHandler(ctx, rw, r.buildRequest())
if tpl.Err != nil { if tpl.Err != nil {
return 0, tpl.Err return 0, tpl.Err
@ -249,7 +249,7 @@ func doAdminGetArticleByIDRequest(user reqUser, articleID int) (*models.Article,
} }
rw := httptest.NewRecorder() rw := httptest.NewRecorder()
tpl := controllers.AdminPreviewArticleByIDHandler(ctx, rw, r.buildRequest()) tpl := handler.AdminPreviewArticleByIDHandler(ctx, rw, r.buildRequest())
if tpl.Err != nil { if tpl.Err != nil {
return nil, tpl.Err return nil, tpl.Err
@ -266,7 +266,7 @@ func doAdminListArticleRequest(user reqUser) ([]models.Article, error) {
} }
rw := httptest.NewRecorder() rw := httptest.NewRecorder()
tpl := controllers.AdminListArticlesHandler(ctx, rw, r.buildRequest()) tpl := handler.AdminListArticlesHandler(ctx, rw, r.buildRequest())
if tpl.Err != nil { if tpl.Err != nil {
return nil, tpl.Err return nil, tpl.Err
@ -289,7 +289,7 @@ func doAdminPublishArticleRequest(user reqUser, articleID int) error {
} }
rw := httptest.NewRecorder() rw := httptest.NewRecorder()
tpl := controllers.AdminArticlePublishPostHandler(ctx, rw, r.buildRequest()) tpl := handler.AdminArticlePublishPostHandler(ctx, rw, r.buildRequest())
if tpl.Err != nil { if tpl.Err != nil {
return tpl.Err return tpl.Err
@ -312,7 +312,7 @@ func doAdminRemoveArticleRequest(user reqUser, articleID int) error {
} }
rw := httptest.NewRecorder() rw := httptest.NewRecorder()
tpl := controllers.AdminArticleDeletePostHandler(ctx, rw, r.buildRequest()) tpl := handler.AdminArticleDeletePostHandler(ctx, rw, r.buildRequest())
if tpl.Err != nil { if tpl.Err != nil {
return tpl.Err return tpl.Err

View File

@ -2,13 +2,13 @@
// Use of this source code is governed by a MIT-style // Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
package controllers package handler
import ( import (
"fmt" "fmt"
"net/http" "net/http"
"git.hoogi.eu/snafu/go-blog/components/httperror" "git.hoogi.eu/snafu/go-blog/httperror"
"git.hoogi.eu/snafu/go-blog/middleware" "git.hoogi.eu/snafu/go-blog/middleware"
"git.hoogi.eu/snafu/go-blog/models" "git.hoogi.eu/snafu/go-blog/models"
) )

View File

@ -1,4 +1,4 @@
package controllers_test package handler_test
import ( import (
"net/http/httptest" "net/http/httptest"
@ -6,7 +6,7 @@ import (
"strconv" "strconv"
"testing" "testing"
"git.hoogi.eu/snafu/go-blog/controllers" "git.hoogi.eu/snafu/go-blog/handler"
"git.hoogi.eu/snafu/go-blog/models" "git.hoogi.eu/snafu/go-blog/models"
) )
@ -82,7 +82,7 @@ func doAdminGetCategoryRequest(user reqUser, categoryID int) (*models.Category,
} }
rw := httptest.NewRecorder() rw := httptest.NewRecorder()
tpl := controllers.AdminGetCategoryHandler(ctx, rw, r.buildRequest()) tpl := handler.AdminGetCategoryHandler(ctx, rw, r.buildRequest())
if tpl.Err != nil { if tpl.Err != nil {
return nil, tpl.Err return nil, tpl.Err
@ -99,7 +99,7 @@ func doAdminListCategoriesRequest(user reqUser) ([]models.Category, error) {
} }
rw := httptest.NewRecorder() rw := httptest.NewRecorder()
tpl := controllers.AdminListCategoriesHandler(ctx, rw, r.buildRequest()) tpl := handler.AdminListCategoriesHandler(ctx, rw, r.buildRequest())
if tpl.Err != nil { if tpl.Err != nil {
return nil, tpl.Err return nil, tpl.Err
@ -119,7 +119,7 @@ func doAdminCategoryNewRequest(user reqUser, c *models.Category) (int, error) {
} }
rw := httptest.NewRecorder() rw := httptest.NewRecorder()
tpl := controllers.AdminCategoryNewPostHandler(ctx, rw, r.buildRequest()) tpl := handler.AdminCategoryNewPostHandler(ctx, rw, r.buildRequest())
if tpl.Err != nil { if tpl.Err != nil {
return -1, tpl.Err return -1, tpl.Err
@ -144,7 +144,7 @@ func doAdminCategoryEditRequest(user reqUser, c *models.Category) error {
} }
rw := httptest.NewRecorder() rw := httptest.NewRecorder()
tpl := controllers.AdminCategoryEditPostHandler(ctx, rw, r.buildRequest()) tpl := handler.AdminCategoryEditPostHandler(ctx, rw, r.buildRequest())
if tpl.Err != nil { if tpl.Err != nil {
return tpl.Err return tpl.Err
@ -166,7 +166,7 @@ func doAdminDeleteCategoryRequest(user reqUser, categoryID int) error {
} }
rw := httptest.NewRecorder() rw := httptest.NewRecorder()
tpl := controllers.AdminCategoryDeletePostHandler(ctx, rw, r.buildRequest()) tpl := handler.AdminCategoryDeletePostHandler(ctx, rw, r.buildRequest())
if tpl.Err != nil { if tpl.Err != nil {
return tpl.Err return tpl.Err

View File

@ -1,4 +1,4 @@
package controllers package handler
const ( const (
tplArticle = "front/article" tplArticle = "front/article"

View File

@ -1,4 +1,4 @@
package controllers package handler
import ( import (
"errors" "errors"
@ -9,8 +9,8 @@ import (
"path/filepath" "path/filepath"
"syscall" "syscall"
"git.hoogi.eu/snafu/go-blog/components/httperror" "git.hoogi.eu/snafu/go-blog/httperror"
"git.hoogi.eu/snafu/go-blog/components/logger" "git.hoogi.eu/snafu/go-blog/logger"
"git.hoogi.eu/snafu/go-blog/middleware" "git.hoogi.eu/snafu/go-blog/middleware"
"git.hoogi.eu/snafu/go-blog/models" "git.hoogi.eu/snafu/go-blog/models"
) )

View File

@ -1,4 +1,4 @@
package controllers_test package handler_test
import ( import (
"fmt" "fmt"
@ -7,7 +7,7 @@ import (
"strconv" "strconv"
"testing" "testing"
"git.hoogi.eu/snafu/go-blog/controllers" "git.hoogi.eu/snafu/go-blog/handler"
"git.hoogi.eu/snafu/go-blog/models" "git.hoogi.eu/snafu/go-blog/models"
) )
@ -63,7 +63,7 @@ func doAdminListFilesRequest(user reqUser) ([]models.File, error) {
} }
rw := httptest.NewRecorder() rw := httptest.NewRecorder()
tpl := controllers.AdminListFilesHandler(ctx, rw, r.buildRequest()) tpl := handler.AdminListFilesHandler(ctx, rw, r.buildRequest())
if tpl.Err != nil { if tpl.Err != nil {
return nil, tpl.Err return nil, tpl.Err
@ -87,7 +87,7 @@ func doAdminGetFileRequest(user reqUser, uniquename string) (*httptest.ResponseR
rw := httptest.NewRecorder() rw := httptest.NewRecorder()
fh := controllers.FileHandler{ fh := handler.FileHandler{
Context: ctx, Context: ctx,
} }
@ -116,7 +116,7 @@ func doAdminUploadFileRequest(user reqUser, file string) error {
} }
rw := httptest.NewRecorder() rw := httptest.NewRecorder()
tpl := controllers.AdminUploadFilePostHandler(ctx, rw, r.buildRequest()) tpl := handler.AdminUploadFilePostHandler(ctx, rw, r.buildRequest())
if tpl.Err != nil { if tpl.Err != nil {
return tpl.Err return tpl.Err
@ -139,7 +139,7 @@ func doAdminFileDeleteRequest(user reqUser, fileID int) error {
} }
rw := httptest.NewRecorder() rw := httptest.NewRecorder()
tpl := controllers.AdminUploadDeletePostHandler(ctx, rw, r.buildRequest()) tpl := handler.AdminUploadDeletePostHandler(ctx, rw, r.buildRequest())
if tpl.Err != nil { if tpl.Err != nil {
return tpl.Err return tpl.Err

View File

@ -2,13 +2,13 @@
// Use of this source code is governed by a MIT-style // Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
package controllers package handler
import ( import (
"net/http" "net/http"
"strconv" "strconv"
"git.hoogi.eu/snafu/go-blog/components/logger" "git.hoogi.eu/snafu/go-blog/logger"
"github.com/gorilla/mux" "github.com/gorilla/mux"
) )

View File

@ -2,7 +2,7 @@
// Use of this source code is governed by a MIT-style // Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
package controllers package handler
import ( import (
"net/http" "net/http"

View File

@ -1,4 +1,4 @@
package controllers_test package handler_test
import ( import (
"fmt" "fmt"
@ -6,7 +6,7 @@ import (
"net/url" "net/url"
"testing" "testing"
"git.hoogi.eu/snafu/go-blog/controllers" "git.hoogi.eu/snafu/go-blog/handler"
) )
func TestLogin(t *testing.T) { func TestLogin(t *testing.T) {
@ -71,7 +71,7 @@ func doLoginRequest(user reqUser, login, password string) (responseWrapper, erro
} }
rr := httptest.NewRecorder() rr := httptest.NewRecorder()
tpl := controllers.LoginPostHandler(ctx, rr, r.buildRequest()) tpl := handler.LoginPostHandler(ctx, rr, r.buildRequest())
if tpl.Err != nil { if tpl.Err != nil {
return responseWrapper{response: rr, template: tpl}, tpl.Err return responseWrapper{response: rr, template: tpl}, tpl.Err

View File

@ -2,7 +2,7 @@
// Use of this source code is governed by a MIT-style // Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
package controllers package handler
import ( import (
"database/sql" "database/sql"
@ -10,7 +10,7 @@ import (
"fmt" "fmt"
"net/http" "net/http"
"git.hoogi.eu/snafu/go-blog/components/httperror" "git.hoogi.eu/snafu/go-blog/httperror"
"git.hoogi.eu/snafu/go-blog/middleware" "git.hoogi.eu/snafu/go-blog/middleware"
"git.hoogi.eu/snafu/go-blog/models" "git.hoogi.eu/snafu/go-blog/models"
) )

View File

@ -1,4 +1,4 @@
package controllers_test package handler_test
import ( import (
"fmt" "fmt"
@ -7,7 +7,7 @@ import (
"strconv" "strconv"
"testing" "testing"
"git.hoogi.eu/snafu/go-blog/controllers" "git.hoogi.eu/snafu/go-blog/handler"
"git.hoogi.eu/snafu/go-blog/models" "git.hoogi.eu/snafu/go-blog/models"
) )
@ -92,7 +92,7 @@ func doGetSiteRequest(user reqUser, link string) (*models.Site, error) {
} }
rw := httptest.NewRecorder() rw := httptest.NewRecorder()
tpl := controllers.GetSiteHandler(ctx, rw, r.buildRequest()) tpl := handler.GetSiteHandler(ctx, rw, r.buildRequest())
if tpl.Err != nil { if tpl.Err != nil {
return nil, tpl.Err return nil, tpl.Err
@ -115,7 +115,7 @@ func doAdminGetSiteRequest(user reqUser, siteID int) (*models.Site, error) {
} }
rw := httptest.NewRecorder() rw := httptest.NewRecorder()
tpl := controllers.AdminGetSiteHandler(ctx, rw, r.buildRequest()) tpl := handler.AdminGetSiteHandler(ctx, rw, r.buildRequest())
if tpl.Err != nil { if tpl.Err != nil {
return nil, tpl.Err return nil, tpl.Err
@ -132,7 +132,7 @@ func doAdminListSitesRequest(user reqUser) ([]models.Site, error) {
} }
rw := httptest.NewRecorder() rw := httptest.NewRecorder()
tpl := controllers.AdminSitesHandler(ctx, rw, r.buildRequest()) tpl := handler.AdminSitesHandler(ctx, rw, r.buildRequest())
if tpl.Err != nil { if tpl.Err != nil {
return nil, tpl.Err return nil, tpl.Err
@ -156,7 +156,7 @@ func doAdminSiteCreateRequest(user reqUser, s *models.Site) (int, error) {
} }
rw := httptest.NewRecorder() rw := httptest.NewRecorder()
tpl := controllers.AdminSiteNewPostHandler(ctx, rw, r.buildRequest()) tpl := handler.AdminSiteNewPostHandler(ctx, rw, r.buildRequest())
if tpl.Err != nil { if tpl.Err != nil {
return 0, tpl.Err return 0, tpl.Err
@ -185,7 +185,7 @@ func doAdminSitePublishRequest(user reqUser, siteID int) error {
} }
rw := httptest.NewRecorder() rw := httptest.NewRecorder()
tpl := controllers.AdminSitePublishPostHandler(ctx, rw, r.buildRequest()) tpl := handler.AdminSitePublishPostHandler(ctx, rw, r.buildRequest())
if tpl.Err != nil { if tpl.Err != nil {
return tpl.Err return tpl.Err
@ -215,7 +215,7 @@ func doAdminSiteEditRequest(user reqUser, s *models.Site) error {
} }
rw := httptest.NewRecorder() rw := httptest.NewRecorder()
tpl := controllers.AdminSiteEditPostHandler(ctx, rw, r.buildRequest()) tpl := handler.AdminSiteEditPostHandler(ctx, rw, r.buildRequest())
if tpl.Err != nil { if tpl.Err != nil {
return tpl.Err return tpl.Err
@ -238,7 +238,7 @@ func doAdminSiteDeleteRequest(user reqUser, siteID int) error {
} }
rw := httptest.NewRecorder() rw := httptest.NewRecorder()
tpl := controllers.AdminSiteDeletePostHandler(ctx, rw, r.buildRequest()) tpl := handler.AdminSiteDeletePostHandler(ctx, rw, r.buildRequest())
if tpl.Err != nil { if tpl.Err != nil {
return tpl.Err return tpl.Err
@ -261,7 +261,7 @@ func doAdminSiteOrderRequest(user reqUser, siteID int) error {
} }
rw := httptest.NewRecorder() rw := httptest.NewRecorder()
tpl := controllers.AdminSiteOrderHandler(ctx, rw, r.buildRequest()) tpl := handler.AdminSiteOrderHandler(ctx, rw, r.buildRequest())
if tpl.Err != nil { if tpl.Err != nil {
return tpl.Err return tpl.Err

View File

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@ -2,13 +2,13 @@
// Use of this source code is governed by a MIT-style // Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
package controllers package handler
import ( import (
"fmt" "fmt"
"net/http" "net/http"
"git.hoogi.eu/snafu/go-blog/components/httperror" "git.hoogi.eu/snafu/go-blog/httperror"
"git.hoogi.eu/snafu/go-blog/middleware" "git.hoogi.eu/snafu/go-blog/middleware"
"git.hoogi.eu/snafu/go-blog/models" "git.hoogi.eu/snafu/go-blog/models"
) )
@ -196,11 +196,11 @@ func AdminUserEditPostHandler(ctx *middleware.AppContext, w http.ResponseWriter,
} }
} }
sids := ctx.SessionService.SessionProvider.SessionIDsFromValues("userid", u.ID) sessions := ctx.SessionService.SessionProvider.FindSessionsByValue("userid", u.ID)
for _, id := range sids { for _, s := range sessions {
if session.SessionID() != id { if session.SessionID() != s.SessionID() {
ctx.SessionService.SessionProvider.Remove(id) ctx.SessionService.SessionProvider.Remove(s.SessionID())
} }
} }
} }

View File

@ -1,4 +1,4 @@
package controllers package handler
import ( import (
"fmt" "fmt"

View File

@ -1,4 +1,4 @@
package controllers_test package handler_test
import ( import (
"net/http/httptest" "net/http/httptest"
@ -6,7 +6,7 @@ import (
"strconv" "strconv"
"testing" "testing"
"git.hoogi.eu/snafu/go-blog/controllers" "git.hoogi.eu/snafu/go-blog/handler"
"git.hoogi.eu/snafu/go-blog/models" "git.hoogi.eu/snafu/go-blog/models"
) )
@ -70,7 +70,7 @@ func doAdminCreateUserInviteRequest(user reqUser, ui *models.UserInvite) (int, s
} }
rw := httptest.NewRecorder() rw := httptest.NewRecorder()
tpl := controllers.AdminUserInviteNewPostHandler(ctx, rw, r.buildRequest()) tpl := handler.AdminUserInviteNewPostHandler(ctx, rw, r.buildRequest())
if tpl.Err != nil { if tpl.Err != nil {
return -1, "", tpl.Err return -1, "", tpl.Err
@ -93,7 +93,7 @@ func doAdminResendUserInviteRequest(user reqUser, inviteID int) (int, string, er
} }
rw := httptest.NewRecorder() rw := httptest.NewRecorder()
tpl := controllers.AdminUserInviteResendPostHandler(ctx, rw, r.buildRequest()) tpl := handler.AdminUserInviteResendPostHandler(ctx, rw, r.buildRequest())
if tpl.Err != nil { if tpl.Err != nil {
return -1, "", tpl.Err return -1, "", tpl.Err
@ -116,7 +116,7 @@ func doAdminRemoveUserInviteRequest(user reqUser, inviteID int) error {
} }
rw := httptest.NewRecorder() rw := httptest.NewRecorder()
tpl := controllers.AdminUserInviteDeletePostHandler(ctx, rw, r.buildRequest()) tpl := handler.AdminUserInviteDeletePostHandler(ctx, rw, r.buildRequest())
if tpl.Err != nil { if tpl.Err != nil {
return tpl.Err return tpl.Err

View File

@ -2,7 +2,7 @@
// Use of this source code is governed by a MIT-style // Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
package controllers_test package handler_test
import ( import (
"fmt" "fmt"
@ -11,7 +11,7 @@ import (
"strconv" "strconv"
"testing" "testing"
"git.hoogi.eu/snafu/go-blog/controllers" "git.hoogi.eu/snafu/go-blog/handler"
"git.hoogi.eu/snafu/go-blog/models" "git.hoogi.eu/snafu/go-blog/models"
) )
@ -110,7 +110,7 @@ func doAdminGetUserRequest(user reqUser, userID int) (*models.User, error) {
} }
rw := httptest.NewRecorder() rw := httptest.NewRecorder()
tpl := controllers.AdminUserEditHandler(ctx, rw, r.buildRequest()) tpl := handler.AdminUserEditHandler(ctx, rw, r.buildRequest())
if tpl.Err != nil { if tpl.Err != nil {
return nil, tpl.Err return nil, tpl.Err
@ -156,7 +156,7 @@ func doAdminEditUsersRequest(user reqUser, u *models.User) error {
} }
rw := httptest.NewRecorder() rw := httptest.NewRecorder()
tpl := controllers.AdminUserEditPostHandler(ctx, rw, r.buildRequest()) tpl := handler.AdminUserEditPostHandler(ctx, rw, r.buildRequest())
if tpl.Err != nil { if tpl.Err != nil {
return tpl.Err return tpl.Err
@ -176,7 +176,7 @@ func doAdminListUsersRequest(user reqUser) ([]models.User, error) {
} }
rw := httptest.NewRecorder() rw := httptest.NewRecorder()
tpl := controllers.AdminUsersHandler(ctx, rw, r.buildRequest()) tpl := handler.AdminUsersHandler(ctx, rw, r.buildRequest())
if tpl.Err != nil { if tpl.Err != nil {
return nil, tpl.Err return nil, tpl.Err
@ -202,7 +202,7 @@ func doAdminCreateUserRequest(user reqUser, u *models.User) (int, error) {
} }
rw := httptest.NewRecorder() rw := httptest.NewRecorder()
tpl := controllers.AdminUserNewPostHandler(ctx, rw, r.buildRequest()) tpl := handler.AdminUserNewPostHandler(ctx, rw, r.buildRequest())
if tpl.Err != nil { if tpl.Err != nil {
return 0, tpl.Err return 0, tpl.Err

View File

@ -2,7 +2,7 @@
// Use of this source code is governed by a MIT-style // Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
package controllers_test package handler_test
import ( import (
"bytes" "bytes"
@ -20,13 +20,13 @@ import (
"path/filepath" "path/filepath"
"testing" "testing"
"git.hoogi.eu/snafu/go-blog/components/database" "git.hoogi.eu/snafu/go-blog/crypt"
"git.hoogi.eu/snafu/go-blog/components/logger" "git.hoogi.eu/snafu/go-blog/database"
"git.hoogi.eu/snafu/go-blog/components/mail" "git.hoogi.eu/snafu/go-blog/logger"
"git.hoogi.eu/snafu/go-blog/mail"
"git.hoogi.eu/snafu/go-blog/middleware" "git.hoogi.eu/snafu/go-blog/middleware"
"git.hoogi.eu/snafu/go-blog/models" "git.hoogi.eu/snafu/go-blog/models"
"git.hoogi.eu/snafu/go-blog/settings" "git.hoogi.eu/snafu/go-blog/settings"
"git.hoogi.eu/snafu/go-blog/utils"
"git.hoogi.eu/snafu/session" "git.hoogi.eu/snafu/session"
_ "github.com/mattn/go-sqlite3" _ "github.com/mattn/go-sqlite3"
@ -145,9 +145,9 @@ func teardown() {
} }
func fillSeeds(db *sql.DB) error { func fillSeeds(db *sql.DB) error {
salt := utils.GenerateSalt() salt := crypt.GenerateSalt()
saltedPassword := utils.AppendBytes([]byte("123456789012"), salt) saltedPassword := append([]byte("123456789012"), salt[:]...)
password, err := utils.CryptPassword([]byte(saltedPassword), 12) password, err := crypt.CryptPassword([]byte(saltedPassword), 12)
if err != nil { if err != nil {
return err return err

View File

@ -84,23 +84,6 @@ func ValueRequired(param string) *Error {
} }
} }
func Equals(a error, b error) bool {
v, ok := a.(*Error)
v2, ok2 := b.(*Error)
if ok && ok2 {
return v.Err == v2.Err
} else if !ok && !ok2 {
return v == v2
} else if ok && !ok2 {
return v.Err == b
} else if !ok && ok2 {
return a == v2.Err
}
return false
}
func (e Error) Error() string { func (e Error) Error() string {
return fmt.Sprintf("code=[%d], error=[%s], displayMsg=[%s]", e.HTTPStatus, e.Err.Error(), e.DisplayMsg) return fmt.Sprintf("code=[%d], error=[%s], displayMsg=[%s]", e.HTTPStatus, e.Err.Error(), e.DisplayMsg)
} }

View File

@ -14,14 +14,9 @@ import (
//Log returns a new logrus instance //Log returns a new logrus instance
var Log = logrus.New() var Log = logrus.New()
//InitLogger initializes the logger. Writes to file with the specified log level //InitLogger initializes the logger
//Valid log levels are: //Valid log levels are: debug|info|warn|error|fatal|panic
// debug //Fallback: info
// info (fallback)
// warn
// error
// fatal
// panic
func InitLogger(w io.Writer, level string) { func InitLogger(w io.Writer, level string) {
level = strings.ToLower(level) level = strings.ToLower(level)

View File

@ -6,7 +6,7 @@ import (
"fmt" "fmt"
"net/smtp" "net/smtp"
"git.hoogi.eu/snafu/go-blog/components/logger" "git.hoogi.eu/snafu/go-blog/logger"
) )
//Service holds configuration for the SMTP server //Service holds configuration for the SMTP server

View File

@ -13,9 +13,9 @@ import (
"time" "time"
"git.hoogi.eu/snafu/cfg" "git.hoogi.eu/snafu/cfg"
"git.hoogi.eu/snafu/go-blog/components/database" "git.hoogi.eu/snafu/go-blog/database"
"git.hoogi.eu/snafu/go-blog/components/logger" "git.hoogi.eu/snafu/go-blog/logger"
"git.hoogi.eu/snafu/go-blog/components/mail" "git.hoogi.eu/snafu/go-blog/mail"
m "git.hoogi.eu/snafu/go-blog/middleware" m "git.hoogi.eu/snafu/go-blog/middleware"
"git.hoogi.eu/snafu/go-blog/models" "git.hoogi.eu/snafu/go-blog/models"
"git.hoogi.eu/snafu/go-blog/routers" "git.hoogi.eu/snafu/go-blog/routers"

View File

@ -8,8 +8,8 @@ import (
"encoding/json" "encoding/json"
"net/http" "net/http"
"git.hoogi.eu/snafu/go-blog/components/httperror" "git.hoogi.eu/snafu/go-blog/httperror"
"git.hoogi.eu/snafu/go-blog/components/logger" "git.hoogi.eu/snafu/go-blog/logger"
"git.hoogi.eu/snafu/go-blog/models" "git.hoogi.eu/snafu/go-blog/models"
) )
@ -23,7 +23,7 @@ type JSONHandler struct {
type JHandler func(*AppContext, http.ResponseWriter, *http.Request) (*models.JSONData, error) type JHandler func(*AppContext, http.ResponseWriter, *http.Request) (*models.JSONData, error)
func (fn JSONHandler) ServeHTTP(rw http.ResponseWriter, r *http.Request) { func (fn JSONHandler) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
statusCode := 200 code := http.StatusOK
rw.Header().Set("Content-Type", "application/json") rw.Header().Set("Content-Type", "application/json")
@ -32,34 +32,34 @@ func (fn JSONHandler) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
if err != nil { if err != nil {
switch e := err.(type) { switch e := err.(type) {
case *httperror.Error: case *httperror.Error:
statusCode = e.HTTPStatus code = e.HTTPStatus
default: default:
statusCode = 500 code = http.StatusInternalServerError
logger.Log.Error(e) logger.Log.Error(e)
} }
logger.Log.Error(err) logger.Log.Error(err)
mjson, err2 := json.Marshal(err) j, err := json.Marshal(err)
if err2 != nil { if err != nil {
logger.Log.Error(err2) logger.Log.Error(err)
http.Error(rw, err2.Error(), http.StatusInternalServerError) http.Error(rw, err.Error(), http.StatusInternalServerError)
return return
} }
rw.WriteHeader(statusCode) rw.WriteHeader(code)
rw.Write(mjson) rw.Write(j)
return return
} }
mjson, err2 := json.Marshal(data) j, err := json.Marshal(data)
if err2 != nil { if err != nil {
http.Error(rw, err2.Error(), http.StatusInternalServerError) http.Error(rw, err.Error(), http.StatusInternalServerError)
rw.WriteHeader(500) rw.WriteHeader(http.StatusInternalServerError)
return return
} }
rw.WriteHeader(statusCode) rw.WriteHeader(code)
rw.Write(mjson) rw.Write(j)
} }

View File

@ -5,13 +5,13 @@
package middleware package middleware
import ( import (
"encoding/base64"
"net" "net"
"net/http" "net/http"
"strings" "strings"
"time" "time"
"git.hoogi.eu/snafu/go-blog/components/logger" "git.hoogi.eu/snafu/go-blog/logger"
"git.hoogi.eu/snafu/go-blog/utils"
) )
var locals = [...]net.IPNet{ var locals = [...]net.IPNet{
@ -72,7 +72,7 @@ func setCookie(rw http.ResponseWriter, name, path, data string) {
c := &http.Cookie{ c := &http.Cookie{
Name: name, Name: name,
Path: path, Path: path,
Value: utils.EncodeBase64(data), Value: base64.StdEncoding.EncodeToString([]byte(data)),
} }
http.SetCookie(rw, c) http.SetCookie(rw, c)
@ -80,6 +80,7 @@ func setCookie(rw http.ResponseWriter, name, path, data string) {
func getFlash(w http.ResponseWriter, r *http.Request, name string) (string, error) { func getFlash(w http.ResponseWriter, r *http.Request, name string) (string, error) {
c, err := r.Cookie(name) c, err := r.Cookie(name)
if err != nil { if err != nil {
switch err { switch err {
case http.ErrNoCookie: case http.ErrNoCookie:
@ -88,7 +89,9 @@ func getFlash(w http.ResponseWriter, r *http.Request, name string) (string, erro
return "", err return "", err
} }
} }
value, err := utils.DecodeBase64(c.Value)
value, err := base64.StdEncoding.DecodeString(c.Value)
if err != nil { if err != nil {
return "", err return "", err
} }
@ -98,9 +101,10 @@ func getFlash(w http.ResponseWriter, r *http.Request, name string) (string, erro
Name: name, Name: name,
MaxAge: -1, MaxAge: -1,
Expires: time.Unix(1, 0), Expires: time.Unix(1, 0),
Path: "/"} Path: "/",
}
http.SetCookie(w, dc) http.SetCookie(w, dc)
return value, nil return string(value), nil
} }

View File

@ -16,11 +16,10 @@ import (
"time" "time"
"git.hoogi.eu/snafu/cfg" "git.hoogi.eu/snafu/cfg"
"git.hoogi.eu/snafu/go-blog/components/httperror" "git.hoogi.eu/snafu/go-blog/httperror"
"git.hoogi.eu/snafu/go-blog/components/logger" "git.hoogi.eu/snafu/go-blog/logger"
"git.hoogi.eu/snafu/go-blog/models" "git.hoogi.eu/snafu/go-blog/models"
"git.hoogi.eu/snafu/go-blog/settings" "git.hoogi.eu/snafu/go-blog/settings"
"git.hoogi.eu/snafu/go-blog/utils"
) )
// Template contains the information about the template to render. // Template contains the information about the template to render.
@ -50,6 +49,7 @@ func NotFound(ctx *AppContext, rw http.ResponseWriter, r *http.Request) *Templat
getFlash(rw, r, "SuccessMsg") getFlash(rw, r, "SuccessMsg")
session, _ := ctx.SessionService.Get(rw, r) session, _ := ctx.SessionService.Get(rw, r)
if session != nil && strings.HasPrefix(r.URL.EscapedPath(), "/admin") { if session != nil && strings.HasPrefix(r.URL.EscapedPath(), "/admin") {
return &Template{ return &Template{
Name: "admin/error", Name: "admin/error",
@ -157,8 +157,7 @@ func FuncMap(ss models.SiteService, settings *settings.Settings) template.FuncMa
return t.Time.In(time.Local).Format("January 2, 2006 at 3:04 PM") return t.Time.In(time.Local).Format("January 2, 2006 at 3:04 PM")
}, },
"HumanizeFilesize": func(size int64) string { "HumanizeFilesize": func(size int64) string {
fs := cfg.FileSize(size) return cfg.FileSize(size).HumanReadable()
return fs.HumanReadable()
}, },
"FormatDateTime": func(t time.Time) string { "FormatDateTime": func(t time.Time) string {
return t.In(time.Local).Format("January 2, 2006 at 3:04 PM") return t.In(time.Local).Format("January 2, 2006 at 3:04 PM")
@ -185,8 +184,7 @@ func FuncMap(ss models.SiteService, settings *settings.Settings) template.FuncMa
return template.HTML(models.MarkdownToHTML([]byte(s))) return template.HTML(models.MarkdownToHTML([]byte(s)))
}, },
"NToBr": func(in string) template.HTML { "NToBr": func(in string) template.HTML {
out := models.NewlineToBr(models.EscapeHTML(in)) return template.HTML(models.NewlineToBr(models.EscapeHTML(in)))
return template.HTML(out)
}, },
"EscapeHTML": func(in string) string { "EscapeHTML": func(in string) string {
return html.EscapeString(in) return html.EscapeString(in)
@ -234,5 +232,5 @@ func (t Template) RedirectURL() string {
if t.RedirectPath[0] == byte('/') { if t.RedirectPath[0] == byte('/') {
return t.RedirectPath return t.RedirectPath
} }
return utils.AppendString("/", t.RedirectPath) return "/" + t.RedirectPath
} }

View File

@ -12,8 +12,8 @@ import (
"github.com/gorilla/csrf" "github.com/gorilla/csrf"
"git.hoogi.eu/snafu/go-blog/components/httperror" "git.hoogi.eu/snafu/go-blog/httperror"
"git.hoogi.eu/snafu/go-blog/components/logger" "git.hoogi.eu/snafu/go-blog/logger"
"git.hoogi.eu/snafu/go-blog/models" "git.hoogi.eu/snafu/go-blog/models"
) )
@ -34,7 +34,8 @@ type Handler func(*AppContext, http.ResponseWriter, *http.Request) *Template
func (fn TemplateHandler) ServeHTTP(rw http.ResponseWriter, r *http.Request) { func (fn TemplateHandler) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
var errorMsg, warnMsg, successMsg string var errorMsg, warnMsg, successMsg string
statusCode := 200
code := http.StatusOK
ip := getIP(r) ip := getIP(r)
en := logger.Log.WithField("ip", ip) en := logger.Log.WithField("ip", ip)
@ -60,7 +61,7 @@ func (fn TemplateHandler) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
switch e := t.Err.(type) { switch e := t.Err.(type) {
case *httperror.Error: case *httperror.Error:
statusCode = e.HTTPStatus code = e.HTTPStatus
en.Error(e) en.Error(e)
errorMsg = e.DisplayMsg errorMsg = e.DisplayMsg
default: default:
@ -99,14 +100,14 @@ func (fn TemplateHandler) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
t.Data[csrf.TemplateTag] = csrf.TemplateField(r) t.Data[csrf.TemplateTag] = csrf.TemplateField(r)
t.Data["active"] = t.Active t.Data["active"] = t.Active
rw.WriteHeader(statusCode) rw.WriteHeader(code)
if err := fn.AppCtx.Templates.ExecuteTemplate(rw, t.Name, t.Data); err != nil { if err := fn.AppCtx.Templates.ExecuteTemplate(rw, t.Name, t.Data); err != nil {
en.Error(err) en.Error(err)
http.Error(rw, err.Error(), http.StatusInternalServerError) http.Error(rw, err.Error(), http.StatusInternalServerError)
} }
} else { } else {
statusCode = http.StatusFound code = http.StatusFound
if len(errorMsg) > 0 { if len(errorMsg) > 0 {
setCookie(rw, "ErrorMsg", "/", errorMsg) setCookie(rw, "ErrorMsg", "/", errorMsg)
} else if len(warnMsg) > 0 { } else if len(warnMsg) > 0 {
@ -114,7 +115,7 @@ func (fn TemplateHandler) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
} else if len(successMsg) > 0 { } else if len(successMsg) > 0 {
setCookie(rw, "SuccessMsg", "/", successMsg) setCookie(rw, "SuccessMsg", "/", successMsg)
} }
http.Redirect(rw, r, path.Clean(t.RedirectURL()), statusCode) http.Redirect(rw, r, path.Clean(t.RedirectURL()), code)
} }
} }
@ -135,6 +136,7 @@ func (ctx AppContext) AuthHandler(handler http.Handler) http.Handler {
} }
userid, ok := session.GetValue("userid").(int) userid, ok := session.GetValue("userid").(int)
if !ok { if !ok {
logger.Log.Errorf("userid is not an integer %v", userid) logger.Log.Errorf("userid is not an integer %v", userid)
@ -148,6 +150,7 @@ func (ctx AppContext) AuthHandler(handler http.Handler) http.Handler {
} }
u, err := ctx.UserService.GetByID(userid) u, err := ctx.UserService.GetByID(userid)
if err != nil { if err != nil {
logger.Log.Error(err) logger.Log.Error(err)
rw.WriteHeader(http.StatusUnauthorized) rw.WriteHeader(http.StatusUnauthorized)
@ -159,8 +162,7 @@ func (ctx AppContext) AuthHandler(handler http.Handler) http.Handler {
return return
} }
ctx := context.WithValue(r.Context(), UserContextKey, u) handler.ServeHTTP(rw, r.WithContext(context.WithValue(r.Context(), UserContextKey, u)))
handler.ServeHTTP(rw, r.WithContext(ctx))
} }
return http.HandlerFunc(fn) return http.HandlerFunc(fn)
} }
@ -195,7 +197,7 @@ func (ctx AppContext) RequireAdmin(handler http.Handler) http.Handler {
func User(r *http.Request) (*models.User, error) { func User(r *http.Request) (*models.User, error) {
v := r.Context().Value(UserContextKey) v := r.Context().Value(UserContextKey)
if v == nil { if v == nil {
return nil, httperror.InternalServerError(errors.New("user is not available in context. is the authentication handler in chain?")) return nil, httperror.InternalServerError(errors.New("user is not available in context"))
} }
return v.(*models.User), nil return v.(*models.User), nil

View File

@ -5,7 +5,7 @@ import (
"encoding/xml" "encoding/xml"
"net/http" "net/http"
"git.hoogi.eu/snafu/go-blog/components/logger" "git.hoogi.eu/snafu/go-blog/logger"
"git.hoogi.eu/snafu/go-blog/models" "git.hoogi.eu/snafu/go-blog/models"
) )
@ -26,11 +26,11 @@ func (fn XMLHandler) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
if err != nil { if err != nil {
logger.Log.Error(err) logger.Log.Error(err)
x, err2 := xml.Marshal(err) x, err := xml.Marshal(err)
if err2 != nil { if err != nil {
logger.Log.Error(err2) logger.Log.Error(err)
http.Error(rw, err2.Error(), http.StatusInternalServerError) http.Error(rw, err.Error(), http.StatusInternalServerError)
return return
} }

View File

@ -14,9 +14,9 @@ import (
"strings" "strings"
"time" "time"
"git.hoogi.eu/snafu/go-blog/components/httperror" "git.hoogi.eu/snafu/go-blog/httperror"
"git.hoogi.eu/snafu/go-blog/settings" "git.hoogi.eu/snafu/go-blog/settings"
"git.hoogi.eu/snafu/go-blog/utils" "git.hoogi.eu/snafu/go-blog/slug"
) )
// Article represents an article // Article represents an article
@ -60,17 +60,21 @@ func (a Article) SlugEscape() string {
} }
func (a *Article) buildSlug(now time.Time, suffix int) string { func (a *Article) buildSlug(now time.Time, suffix int) string {
return utils.AppendString(strconv.Itoa(now.Year()), "/", strconv.Itoa(int(now.Month())), "/", utils.CreateURLSafeSlug(a.Headline, suffix)) var sb strings.Builder
sb.WriteString(strconv.Itoa(now.Year()))
sb.WriteString("/")
sb.WriteString(strconv.Itoa(int(now.Month())))
sb.WriteString("/")
sb.WriteString(slug.CreateURLSafeSlug(a.Headline, suffix))
return sb.String()
} }
func (a *Article) slug(as ArticleService, now time.Time) error { func (a *Article) slug(as ArticleService, now time.Time) error {
for i := 0; i < 10; i++ { for i := 0; i < 10; i++ {
a.Slug = a.buildSlug(now, i) a.Slug = a.buildSlug(now, i)
_, err := as.Datasource.GetBySlug(a.Slug, nil, All) if _, err := as.Datasource.GetBySlug(a.Slug, nil, All); err != nil {
if errors.Is(err, sql.ErrNoRows) {
if err != nil {
if err == sql.ErrNoRows {
break break
} }
return err return err
@ -118,18 +122,11 @@ func (as ArticleService) Create(a *Article) (int, error) {
return 0, err return 0, err
} }
err := a.slug(as, now) if err := a.slug(as, now); err != nil {
if err != nil {
return -1, err return -1, err
} }
artID, err := as.Datasource.Create(a) return as.Datasource.Create(a)
if err != nil {
return 0, err
}
return artID, nil
} }
//Update updates an article //Update updates an article
@ -204,7 +201,7 @@ func (as ArticleService) GetBySlug(s string, u *User, pc PublishedCriteria) (*Ar
a, err := as.Datasource.GetBySlug(s, u, pc) a, err := as.Datasource.GetBySlug(s, u, pc)
if err != nil { if err != nil {
if err == sql.ErrNoRows { if errors.Is(err, sql.ErrNoRows) {
return nil, httperror.NotFound("article", err) return nil, httperror.NotFound("article", err)
} }
return nil, err return nil, err
@ -227,7 +224,7 @@ func (as ArticleService) GetByID(id int, u *User, pc PublishedCriteria) (*Articl
a, err := as.Datasource.Get(id, u, pc) a, err := as.Datasource.Get(id, u, pc)
if err != nil { if err != nil {
if err == sql.ErrNoRows { if errors.Is(err, sql.ErrNoRows) {
return nil, httperror.NotFound("article", fmt.Errorf("the article with id %d was not found", id)) return nil, httperror.NotFound("article", fmt.Errorf("the article with id %d was not found", id))
} }
return nil, err return nil, err
@ -272,7 +269,8 @@ func (as ArticleService) RSSFeed(p *Pagination, pc PublishedCriteria) (RSS, erro
return RSS{}, err return RSS{}, err
} }
items := []RSSItem{} var items []RSSItem
for _, a := range articles { for _, a := range articles {
link := fmt.Sprint(as.AppConfig.Domain, "/article/by-id/", a.ID) link := fmt.Sprint(as.AppConfig.Domain, "/article/by-id/", a.ID)
item := RSSItem{ item := RSSItem{

View File

@ -76,10 +76,9 @@ func (rdb SQLiteArticleDatasource) List(u *User, c *Category, p *Pagination, pc
//the PublishedCritera specifies which articles should be considered //the PublishedCritera specifies which articles should be considered
func (rdb SQLiteArticleDatasource) Count(u *User, c *Category, pc PublishedCriteria) (int, error) { func (rdb SQLiteArticleDatasource) Count(u *User, c *Category, pc PublishedCriteria) (int, error) {
var total int var total int
var args []interface{} var args []interface{}
var stmt bytes.Buffer var stmt bytes.Buffer
stmt.WriteString("SELECT count(a.id) FROM article a ") stmt.WriteString("SELECT count(a.id) FROM article a ")
if c != nil { if c != nil {
@ -211,19 +210,21 @@ func selectArticleStmt(db *sql.DB, articleID int, slug string, u *User, pc Publi
stmt.WriteString("AND a.id=? ") stmt.WriteString("AND a.id=? ")
args = append(args, articleID) args = append(args, articleID)
} }
if u != nil { if u != nil {
if !u.IsAdmin { if !u.IsAdmin {
stmt.WriteString("AND a.user_id=? ") stmt.WriteString("AND a.user_id=? ")
args = append(args, u.ID) args = append(args, u.ID)
} }
} }
stmt.WriteString("LIMIT 1") stmt.WriteString("LIMIT 1")
return db.QueryRow(stmt.String(), args...) return db.QueryRow(stmt.String(), args...)
} }
func selectArticlesStmt(db *sql.DB, u *User, c *Category, p *Pagination, pc PublishedCriteria) (*sql.Rows, error) { func selectArticlesStmt(db *sql.DB, u *User, c *Category, p *Pagination, pc PublishedCriteria) (*sql.Rows, error) {
var stmt bytes.Buffer var stmt bytes.Buffer
var args []interface{} var args []interface{}
stmt.WriteString("SELECT a.id, a.headline, a.teaser, a.content, a.published, a.published_on, a.slug, a.last_modified, ") stmt.WriteString("SELECT a.id, a.headline, a.teaser, a.content, a.published, a.published_on, a.slug, a.last_modified, ")

View File

@ -8,8 +8,8 @@ import (
"strings" "strings"
"time" "time"
"git.hoogi.eu/snafu/go-blog/components/httperror" "git.hoogi.eu/snafu/go-blog/httperror"
"git.hoogi.eu/snafu/go-blog/utils" "git.hoogi.eu/snafu/go-blog/slug"
) )
type Category struct { type Category struct {
@ -66,7 +66,7 @@ func (cs CategoryService) GetBySlug(s string, fc FilterCriteria) (*Category, err
c, err := cs.Datasource.GetBySlug(s, fc) c, err := cs.Datasource.GetBySlug(s, fc)
if err != nil { if err != nil {
if err == sql.ErrNoRows { if errors.Is(err, sql.ErrNoRows) {
return nil, httperror.NotFound("category", fmt.Errorf("the category with slug %s was not found", s)) return nil, httperror.NotFound("category", fmt.Errorf("the category with slug %s was not found", s))
} }
return nil, err return nil, err
@ -79,7 +79,7 @@ func (cs CategoryService) GetByID(id int, fc FilterCriteria) (*Category, error)
c, err := cs.Datasource.Get(id, fc) c, err := cs.Datasource.Get(id, fc)
if err != nil { if err != nil {
if err == sql.ErrNoRows { if errors.Is(err, sql.ErrNoRows) {
return nil, httperror.NotFound("category", fmt.Errorf("the category with id %d was not found", id)) return nil, httperror.NotFound("category", fmt.Errorf("the category with id %d was not found", id))
} }
return nil, err return nil, err
@ -99,11 +99,11 @@ func (cs CategoryService) List(fc FilterCriteria) ([]Category, error) {
//Create creates a category //Create creates a category
func (cs CategoryService) Create(c *Category) (int, error) { func (cs CategoryService) Create(c *Category) (int, error) {
for i := 0; i < 10; i++ { for i := 0; i < 10; i++ {
c.Slug = utils.CreateURLSafeSlug(c.Name, i) c.Slug = slug.CreateURLSafeSlug(c.Name, i)
_, err := cs.Datasource.GetBySlug(c.Slug, AllCategories) _, err := cs.Datasource.GetBySlug(c.Slug, AllCategories)
if err != nil { if err != nil {
if err == sql.ErrNoRows { if errors.Is(err, sql.ErrNoRows) {
break break
} }
return -1, err return -1, err

View File

@ -33,9 +33,8 @@ func (rdb SQLiteCategoryDatasource) Create(c *Category) (int, error) {
} }
func (rdb SQLiteCategoryDatasource) List(fc FilterCriteria) ([]Category, error) { func (rdb SQLiteCategoryDatasource) List(fc FilterCriteria) ([]Category, error) {
var stmt bytes.Buffer
var args []interface{} var args []interface{}
var stmt bytes.Buffer
stmt.WriteString("SELECT DISTINCT c.id, c.name, c.slug, c.last_modified, ") stmt.WriteString("SELECT DISTINCT c.id, c.name, c.slug, c.last_modified, ")
stmt.WriteString("u.id, u.display_name, u.username, u.email, u.is_admin ") stmt.WriteString("u.id, u.display_name, u.username, u.email, u.is_admin ")
@ -63,7 +62,7 @@ func (rdb SQLiteCategoryDatasource) List(fc FilterCriteria) ([]Category, error)
defer rows.Close() defer rows.Close()
cs := []Category{} var cs []Category
for rows.Next() { for rows.Next() {
var c Category var c Category
@ -139,6 +138,7 @@ func (rdb SQLiteCategoryDatasource) GetBySlug(slug string, fc FilterCriteria) (*
stmt.WriteString("FROM category as c ") stmt.WriteString("FROM category as c ")
stmt.WriteString("INNER JOIN user as u ") stmt.WriteString("INNER JOIN user as u ")
stmt.WriteString("ON u.id = c.user_id ") stmt.WriteString("ON u.id = c.user_id ")
if fc == CategoriesWithPublishedArticles { if fc == CategoriesWithPublishedArticles {
stmt.WriteString("INNER JOIN article as a ") stmt.WriteString("INNER JOIN article as a ")
stmt.WriteString("ON c.id = a.category_id ") stmt.WriteString("ON c.id = a.category_id ")

View File

@ -11,11 +11,11 @@ import (
"path/filepath" "path/filepath"
"strings" "strings"
"time" "time"
"unicode"
"git.hoogi.eu/snafu/go-blog/components/httperror" "git.hoogi.eu/snafu/go-blog/httperror"
"git.hoogi.eu/snafu/go-blog/components/logger" "git.hoogi.eu/snafu/go-blog/logger"
"git.hoogi.eu/snafu/go-blog/settings" "git.hoogi.eu/snafu/go-blog/settings"
"git.hoogi.eu/snafu/go-blog/utils"
) )
//File represents a file //File represents a file
@ -67,7 +67,7 @@ func (f *File) validate() error {
func (f File) randomFilename() string { func (f File) randomFilename() string {
var buf bytes.Buffer var buf bytes.Buffer
sanFilename := utils.SanitizeFilename(f.FileInfo.Name) sanFilename := sanitizeFilename(f.FileInfo.Name)
if len(sanFilename) == 0 { if len(sanFilename) == 0 {
sanFilename = "unnamed" sanFilename = "unnamed"
} }
@ -238,3 +238,36 @@ func (fs FileService) Upload(f *File) (int, error) {
return i, nil return i, nil
} }
var filenameSubs = map[rune]string{
'/': "",
'\\': "",
':': "",
'*': "",
'?': "",
'"': "",
'<': "",
'>': "",
'|': "",
' ': "",
}
func isDot(r rune) bool {
return '.' == r
}
//SanitizeFilename sanitizes a filename for safe use when serving file
func sanitizeFilename(s string) string {
s = strings.ToValidUTF8(s, "")
s = strings.TrimFunc(s, unicode.IsSpace)
s = strings.Map(func(r rune) rune {
if _, ok := filenameSubs[r]; ok {
return -1
}
return r
}, s)
s = strings.TrimLeftFunc(s, isDot)
return s
}

View File

@ -15,7 +15,6 @@ type SQLiteFileDatasource struct {
//only file specific to this user is returned //only file specific to this user is returned
func (rdb SQLiteFileDatasource) GetByUniqueName(uniqueName string, u *User) (*File, error) { func (rdb SQLiteFileDatasource) GetByUniqueName(uniqueName string, u *User) (*File, error) {
var stmt bytes.Buffer var stmt bytes.Buffer
var args []interface{} var args []interface{}
stmt.WriteString("SELECT f.id, f.filename, f.unique_name, f.content_type, f.inline, f.size, f.last_modified, f.user_id, ") stmt.WriteString("SELECT f.id, f.filename, f.unique_name, f.content_type, f.inline, f.size, f.last_modified, f.user_id, ")
@ -51,7 +50,6 @@ func (rdb SQLiteFileDatasource) GetByUniqueName(uniqueName string, u *User) (*Fi
//only file specific to this user is returned //only file specific to this user is returned
func (rdb SQLiteFileDatasource) Get(fileID int, u *User) (*File, error) { func (rdb SQLiteFileDatasource) Get(fileID int, u *User) (*File, error) {
var stmt bytes.Buffer var stmt bytes.Buffer
var args []interface{} var args []interface{}
stmt.WriteString("SELECT f.id, f.filename, f.unique_name, f.content_type, f.inline, f.size, f.last_modified, f.user_id, ") stmt.WriteString("SELECT f.id, f.filename, f.unique_name, f.content_type, f.inline, f.size, f.last_modified, f.user_id, ")
@ -144,8 +142,7 @@ func (rdb SQLiteFileDatasource) List(u *User, p *Pagination) ([]File, error) {
defer rows.Close() defer rows.Close()
files := []File{} var files []File
var f File var f File
var us User var us User

View File

@ -14,7 +14,7 @@ import (
) )
// Defines the extensions that are used // Defines the extensions that are used
var exts = bf.NoIntraEmphasis | bf.Tables | bf.FencedCode | bf.Autolink | var ext = bf.NoIntraEmphasis | bf.Tables | bf.FencedCode | bf.Autolink |
bf.Strikethrough | bf.SpaceHeadings | bf.BackslashLineBreak | bf.Strikethrough | bf.SpaceHeadings | bf.BackslashLineBreak |
bf.DefinitionLists | bf.Footnotes | bf.HardLineBreak bf.DefinitionLists | bf.Footnotes | bf.HardLineBreak
@ -33,7 +33,7 @@ func init() {
//MarkdownToHTML sanitizes and parses markdown to HTML //MarkdownToHTML sanitizes and parses markdown to HTML
func MarkdownToHTML(md []byte) []byte { func MarkdownToHTML(md []byte) []byte {
md = bytes.Replace(md, []byte("\r\n"), []byte("\n"), -1) md = bytes.Replace(md, []byte("\r\n"), []byte("\n"), -1)
unsafe := bf.Run((md), bf.WithExtensions(exts)) unsafe := bf.Run((md), bf.WithExtensions(ext))
return sanitize(unsafe) return sanitize(unsafe)
} }

View File

@ -3,9 +3,8 @@ package models
import ( import (
"fmt" "fmt"
"git.hoogi.eu/snafu/go-blog/components/mail" "git.hoogi.eu/snafu/go-blog/mail"
"git.hoogi.eu/snafu/go-blog/settings" "git.hoogi.eu/snafu/go-blog/settings"
"git.hoogi.eu/snafu/go-blog/utils"
) )
type Mailer struct { type Mailer struct {
@ -14,7 +13,7 @@ type Mailer struct {
} }
func (m Mailer) SendActivationLink(ui *UserInvite) { func (m Mailer) SendActivationLink(ui *UserInvite) {
activation := utils.AppendString(m.AppConfig.Domain, "/admin/activate-account/", ui.Hash) activation := m.AppConfig.Domain + "/admin/activate-account/" + ui.Hash
mail := mail.Mail{ mail := mail.Mail{
To: ui.Email, To: ui.Email,
@ -36,7 +35,7 @@ func (m Mailer) SendPasswordChangeConfirmation(u *User) {
} }
func (m Mailer) SendPasswordResetLink(u *User, t *Token) { func (m Mailer) SendPasswordResetLink(u *User, t *Token) {
resetLink := utils.AppendString(m.AppConfig.Domain, "/admin/reset-password/", t.Hash) resetLink := m.AppConfig.Domain + "/admin/reset-password/" + t.Hash
mail := mail.Mail{ mail := mail.Mail{
To: u.Email, To: u.Email,

View File

@ -5,12 +5,10 @@
package models package models
import ( import (
"bytes"
"fmt" "fmt"
"html/template" "html/template"
"math" "math"
"strings"
"git.hoogi.eu/snafu/go-blog/utils"
) )
//Pagination type is used to provide a page selector //Pagination type is used to provide a page selector
@ -29,9 +27,9 @@ func (p Pagination) Offset() int {
//url returns the absolute url //url returns the absolute url
func (p Pagination) url() string { func (p Pagination) url() string {
if p.RelURL[0] == '/' { if p.RelURL[0] == '/' {
return utils.AppendString(p.RelURL) return p.RelURL
} }
return utils.AppendString("/", p.RelURL) return "/" + p.RelURL
} }
//pages returns the amount of pages //pages returns the amount of pages
@ -75,32 +73,32 @@ func (p Pagination) previousPage() int {
//PaginationBar returns the HTML for the pagination bar which can be embedded //PaginationBar returns the HTML for the pagination bar which can be embedded
func (p Pagination) PaginationBar() template.HTML { func (p Pagination) PaginationBar() template.HTML {
var buffer bytes.Buffer var sb strings.Builder
if p.pages() > 1 { if p.pages() > 1 {
buffer.WriteString(`<div id="pagination">`) sb.WriteString(`<div id="pagination">`)
if !p.hasPrevious() { if !p.hasPrevious() {
buffer.WriteString(`<a class="button button-inactive" href="#">&laquo; Backward</a>`) sb.WriteString(`<a class="button button-inactive" href="#">&laquo; Backward</a>`)
} else { } else {
buffer.WriteString(fmt.Sprintf(`<a class="button button-active" href="%s/%d">&laquo; Backward</a>`, p.url(), p.previousPage())) sb.WriteString(fmt.Sprintf(`<a class="button button-active" href="%s/%d">&laquo; Backward</a>`, p.url(), p.previousPage()))
} }
for i := 1; i <= p.pages(); i++ { for i := 1; i <= p.pages(); i++ {
if p.CurrentPage == i { if p.CurrentPage == i {
buffer.WriteString(fmt.Sprintf(`<a class="button button-inactive" href="#">%d</a></li>`, i)) sb.WriteString(fmt.Sprintf(`<a class="button button-inactive" href="#">%d</a></li>`, i))
} else { } else {
buffer.WriteString(fmt.Sprintf(`<a class="button button-active" href="%s/%d">%d</a></li>`, p.url(), i, i)) sb.WriteString(fmt.Sprintf(`<a class="button button-active" href="%s/%d">%d</a></li>`, p.url(), i, i))
} }
} }
if !p.hasNext() { if !p.hasNext() {
buffer.WriteString(`<a class="button button-inactive" href="#">Forward &raquo;</a>`) sb.WriteString(`<a class="button button-inactive" href="#">Forward &raquo;</a>`)
} else { } else {
buffer.WriteString(fmt.Sprintf(`<a class="button button-active" href="%s/%d">Forward &raquo;</a>`, p.url(), p.nextPage())) sb.WriteString(fmt.Sprintf(`<a class="button button-active" href="%s/%d">Forward &raquo;</a>`, p.url(), p.nextPage()))
} }
buffer.WriteString(`</div>`) sb.WriteString(`</div>`)
} }
return template.HTML(buffer.String()) return template.HTML(sb.String())
} }

View File

@ -7,8 +7,8 @@ import (
"net/url" "net/url"
"time" "time"
"git.hoogi.eu/snafu/go-blog/components/httperror" "git.hoogi.eu/snafu/go-blog/httperror"
"git.hoogi.eu/snafu/go-blog/utils" "git.hoogi.eu/snafu/go-blog/slug"
) )
//SiteDatasourceService defines an interface for CRUD operations on sites //SiteDatasourceService defines an interface for CRUD operations on sites
@ -54,14 +54,14 @@ func (s Site) LinkEscape() string {
if s.isExternal() { if s.isExternal() {
return s.Link return s.Link
} }
return utils.AppendString("/site/", url.PathEscape(s.Link)) return "/site/" + url.PathEscape(s.Link)
} }
func (s Site) safeLink() string { func (s Site) safeLink() string {
if s.isExternal() { if s.isExternal() {
return s.Link return s.Link
} }
return utils.CreateURLSafeSlug(s.Link, -1) return slug.CreateURLSafeSlug(s.Link, -1)
} }
func (s Site) isExternal() bool { func (s Site) isExternal() bool {
@ -174,6 +174,7 @@ func (ss SiteService) Update(s *Site) error {
} }
changeLink := false changeLink := false
if oldSite.Link != s.Link { if oldSite.Link != s.Link {
changeLink = true changeLink = true
s.Link = s.safeLink() s.Link = s.safeLink()

View File

@ -5,7 +5,7 @@ import (
"database/sql" "database/sql"
"time" "time"
"git.hoogi.eu/snafu/go-blog/components/logger" "git.hoogi.eu/snafu/go-blog/logger"
) )
//SQLiteSiteDatasource providing an implementation of SiteDatasourceService for sqlite //SQLiteSiteDatasource providing an implementation of SiteDatasourceService for sqlite
@ -16,7 +16,6 @@ type SQLiteSiteDatasource struct {
//List returns a array of sites //List returns a array of sites
func (rdb SQLiteSiteDatasource) List(pc PublishedCriteria, p *Pagination) ([]Site, error) { func (rdb SQLiteSiteDatasource) List(pc PublishedCriteria, p *Pagination) ([]Site, error) {
var stmt bytes.Buffer var stmt bytes.Buffer
var args []interface{} var args []interface{}
stmt.WriteString("SELECT s.id, s.title, s.link, s.section, s.content, s.published, s.published_on, s.last_modified, s.order_no, u.id, u.display_name, u.email, u.username ") stmt.WriteString("SELECT s.id, s.title, s.link, s.section, s.content, s.published, s.published_on, s.last_modified, s.order_no, u.id, u.display_name, u.email, u.username ")
@ -40,14 +39,14 @@ func (rdb SQLiteSiteDatasource) List(pc PublishedCriteria, p *Pagination) ([]Sit
} }
rows, err := rdb.SQLConn.Query(stmt.String(), args...) rows, err := rdb.SQLConn.Query(stmt.String(), args...)
if err != nil { if err != nil {
return nil, err return nil, err
} }
defer rows.Close() defer rows.Close()
sites := []Site{} var sites []Site
var s Site var s Site
var u User var u User
@ -129,6 +128,7 @@ func (rdb SQLiteSiteDatasource) GetByLink(link string, pc PublishedCriteria) (*S
//Publish publishes or unpublishes a site //Publish publishes or unpublishes a site
func (rdb SQLiteSiteDatasource) Publish(s *Site) error { func (rdb SQLiteSiteDatasource) Publish(s *Site) error {
publishOn := NullTime{Valid: false} publishOn := NullTime{Valid: false}
if !s.Published { if !s.Published {
publishOn = NullTime{Time: time.Now(), Valid: true} publishOn = NullTime{Time: time.Now(), Valid: true}
} }
@ -172,10 +172,6 @@ func (rdb SQLiteSiteDatasource) Order(id int, d Direction) error {
} }
}() }()
if err != nil {
return err
}
if d == Up { if d == Up {
if _, err = tx.Exec("UPDATE site "+ if _, err = tx.Exec("UPDATE site "+
"SET order_no=(SELECT order_no AS order_no FROM site WHERE id=?) "+ "SET order_no=(SELECT order_no AS order_no FROM site WHERE id=?) "+

View File

@ -29,7 +29,7 @@ const (
All All
) )
//NullTime reprensents a time which may not valid if time is null //NullTime represents a time which may not valid if time is null
type NullTime struct { type NullTime struct {
Time time.Time Time time.Time
Valid bool Valid bool

View File

@ -7,9 +7,9 @@ import (
"net/http" "net/http"
"time" "time"
"git.hoogi.eu/snafu/go-blog/components/httperror" "git.hoogi.eu/snafu/go-blog/crypt"
"git.hoogi.eu/snafu/go-blog/components/logger" "git.hoogi.eu/snafu/go-blog/httperror"
"git.hoogi.eu/snafu/go-blog/utils" "git.hoogi.eu/snafu/go-blog/logger"
) )
//TokenDatasourceService defines an interface for CRUD operations for tokens //TokenDatasourceService defines an interface for CRUD operations for tokens
@ -68,11 +68,13 @@ type TokenService struct {
//Create creates a new token //Create creates a new token
func (ts TokenService) Create(t *Token) error { func (ts TokenService) Create(t *Token) error {
t.Hash = utils.RandomHash(32) t.Hash = crypt.RandomHash(32)
_, err := ts.Datasource.Create(t) if _, err := ts.Datasource.Create(t); err != nil {
return err
}
return err return nil
} }
//Get token for a defined token type expires after a defined time //Get token for a defined token type expires after a defined time
@ -106,7 +108,7 @@ func (ts TokenService) RateLimit(userID int, tt TokenType) error {
now := time.Now() now := time.Now()
rate := []Token{} var rate []Token
for _, t := range tokens { for _, t := range tokens {
if now.Sub(t.RequestedAt) < time.Minute*15 { if now.Sub(t.RequestedAt) < time.Minute*15 {
rate = append(rate, t) rate = append(rate, t)

View File

@ -12,10 +12,10 @@ import (
"strings" "strings"
"time" "time"
"git.hoogi.eu/snafu/go-blog/components/httperror" "git.hoogi.eu/snafu/go-blog/crypt"
"git.hoogi.eu/snafu/go-blog/components/logger" "git.hoogi.eu/snafu/go-blog/httperror"
"git.hoogi.eu/snafu/go-blog/logger"
"git.hoogi.eu/snafu/go-blog/settings" "git.hoogi.eu/snafu/go-blog/settings"
"git.hoogi.eu/snafu/go-blog/utils"
"golang.org/x/crypto/bcrypt" "golang.org/x/crypto/bcrypt"
) )
@ -133,8 +133,9 @@ func (u *User) validate(us UserService, minPasswordLength int, v Validations) er
func (us UserService) duplicateMail(mail string) error { func (us UserService) duplicateMail(mail string) error {
user, err := us.Datasource.GetByMail(mail) user, err := us.Datasource.GetByMail(mail)
if err != nil { if err != nil {
if err != sql.ErrNoRows { if !errors.Is(err, sql.ErrNoRows) {
return err return err
} }
} }
@ -149,7 +150,7 @@ func (us UserService) duplicateMail(mail string) error {
func (us UserService) duplicateUsername(username string) error { func (us UserService) duplicateUsername(username string) error {
user, err := us.Datasource.GetByUsername(username) user, err := us.Datasource.GetByUsername(username)
if err != nil { if err != nil {
if err != sql.ErrNoRows { if !errors.Is(err, sql.ErrNoRows) {
return err return err
} }
} }
@ -175,8 +176,9 @@ func (us UserService) List(p *Pagination) ([]User, error) {
//GetByID gets the user based on the given id; will not contain the user password //GetByID gets the user based on the given id; will not contain the user password
func (us UserService) GetByID(userID int) (*User, error) { func (us UserService) GetByID(userID int) (*User, error) {
u, err := us.Datasource.Get(userID) u, err := us.Datasource.Get(userID)
if err != nil { if err != nil {
if err == sql.ErrNoRows { if errors.Is(err, sql.ErrNoRows) {
return nil, httperror.NotFound("user", fmt.Errorf("the user with id %d was not found", userID)) return nil, httperror.NotFound("user", fmt.Errorf("the user with id %d was not found", userID))
} }
return nil, err return nil, err
@ -189,7 +191,7 @@ func (us UserService) GetByID(userID int) (*User, error) {
func (us UserService) GetByUsername(username string) (*User, error) { func (us UserService) GetByUsername(username string) (*User, error) {
u, err := us.Datasource.GetByUsername(username) u, err := us.Datasource.GetByUsername(username)
if err != nil { if err != nil {
if err == sql.ErrNoRows { if errors.Is(err, sql.ErrNoRows) {
return nil, httperror.NotFound("user", err) return nil, httperror.NotFound("user", err)
} }
return nil, err return nil, err
@ -203,7 +205,7 @@ func (us UserService) GetByMail(mail string) (*User, error) {
u, err := us.Datasource.GetByMail(mail) u, err := us.Datasource.GetByMail(mail)
if err != nil { if err != nil {
if err == sql.ErrNoRows { if errors.Is(err, sql.ErrNoRows) {
return nil, httperror.NotFound("user", err) return nil, httperror.NotFound("user", err)
} }
@ -225,9 +227,9 @@ func (us UserService) Create(u *User) (int, error) {
return -1, err return -1, err
} }
salt := utils.GenerateSalt() salt := crypt.GenerateSalt()
saltedPassword := utils.AppendBytes(u.PlainPassword, salt) saltedPassword := append(u.PlainPassword[:], salt[:]...)
password, err := utils.CryptPassword([]byte(saltedPassword), bcryptRounds) password, err := crypt.CryptPassword([]byte(saltedPassword), bcryptRounds)
if err != nil { if err != nil {
return -1, err return -1, err
@ -270,7 +272,6 @@ func (us UserService) Update(u *User, changePassword bool) error {
} }
if us.UserInterceptor != nil { if us.UserInterceptor != nil {
if err := us.UserInterceptor.PreUpdate(oldUser, u); err != nil { if err := us.UserInterceptor.PreUpdate(oldUser, u); err != nil {
return httperror.InternalServerError(fmt.Errorf("error while executing user interceptor 'PreUpdate' error %v", err)) return httperror.InternalServerError(fmt.Errorf("error while executing user interceptor 'PreUpdate' error %v", err))
} }
@ -309,9 +310,9 @@ func (us UserService) Update(u *User, changePassword bool) error {
} }
if changePassword { if changePassword {
salt := utils.GenerateSalt() salt := crypt.GenerateSalt()
saltedPassword := utils.AppendBytes(u.PlainPassword, salt) saltedPassword := append(u.PlainPassword[:], salt[:]...)
password, err := utils.CryptPassword([]byte(saltedPassword), bcryptRounds) password, err := crypt.CryptPassword([]byte(saltedPassword), bcryptRounds)
if err != nil { if err != nil {
return err return err
@ -321,7 +322,9 @@ func (us UserService) Update(u *User, changePassword bool) error {
u.Salt = salt u.Salt = salt
} }
err = us.Datasource.Update(u, changePassword) if err = us.Datasource.Update(u, changePassword); err != nil {
return err
}
u.Password = nil u.Password = nil
@ -330,9 +333,10 @@ func (us UserService) Update(u *User, changePassword bool) error {
logger.Log.Errorf("error while executing PostUpdate user interceptor method %v", err) logger.Log.Errorf("error while executing PostUpdate user interceptor method %v", err)
} }
} }
u.PlainPassword = nil u.PlainPassword = nil
return err return nil
} }
// Authenticate authenticates the user by the given login method (email or username) // Authenticate authenticates the user by the given login method (email or username)
@ -353,7 +357,7 @@ func (us UserService) Authenticate(u *User, loginMethod settings.LoginMethod) (*
} }
if err != nil { if err != nil {
if err == sql.ErrNoRows { if errors.Is(err, sql.ErrNoRows) {
//Do some extra work //Do some extra work
bcrypt.CompareHashAndPassword([]byte("$2a$12$bQlRnXTNZMp6kCyoAlnf3uZW5vtmSj9CHP7pYplRUVK2n0C5xBHBa"), password) bcrypt.CompareHashAndPassword([]byte("$2a$12$bQlRnXTNZMp6kCyoAlnf3uZW5vtmSj9CHP7pYplRUVK2n0C5xBHBa"), password)
return nil, httperror.New(http.StatusUnauthorized, "Your username or password is invalid.", err) return nil, httperror.New(http.StatusUnauthorized, "Your username or password is invalid.", err)
@ -429,5 +433,5 @@ func (us UserService) OneAdmin() (bool, error) {
} }
func (u User) comparePassword() error { func (u User) comparePassword() error {
return bcrypt.CompareHashAndPassword(u.Password, utils.AppendBytes(u.PlainPassword, u.Salt)) return bcrypt.CompareHashAndPassword(u.Password, append(u.PlainPassword[:], u.Salt[:]...))
} }

View File

@ -3,11 +3,11 @@ package models
import ( import (
"time" "time"
"git.hoogi.eu/snafu/go-blog/components/mail" "git.hoogi.eu/snafu/go-blog/crypt"
"git.hoogi.eu/snafu/go-blog/utils" "git.hoogi.eu/snafu/go-blog/mail"
) )
//User represents a user //UserInvite represents a new invited user
type UserInvite struct { type UserInvite struct {
ID int ID int
Hash string Hash string
@ -64,7 +64,7 @@ func (uis UserInviteService) List() ([]UserInvite, error) {
} }
func (uis UserInviteService) Update(ui *UserInvite) error { func (uis UserInviteService) Update(ui *UserInvite) error {
ui.Hash = utils.RandomHash(32) ui.Hash = crypt.RandomHash(32)
if err := ui.validate(uis); err != nil { if err := ui.validate(uis); err != nil {
return err return err
@ -74,7 +74,7 @@ func (uis UserInviteService) Update(ui *UserInvite) error {
} }
func (uis UserInviteService) Create(ui *UserInvite) (int, error) { func (uis UserInviteService) Create(ui *UserInvite) (int, error) {
ui.Hash = utils.RandomHash(32) ui.Hash = crypt.RandomHash(32)
if err := ui.validate(uis); err != nil { if err := ui.validate(uis); err != nil {
return -1, err return -1, err

View File

@ -14,6 +14,9 @@ type SQLiteUserInviteDatasource struct {
func (rdb SQLiteUserInviteDatasource) List() ([]UserInvite, error) { func (rdb SQLiteUserInviteDatasource) List() ([]UserInvite, error) {
var stmt bytes.Buffer var stmt bytes.Buffer
var args []interface{} var args []interface{}
var invites []UserInvite
var ui UserInvite
var u User
stmt.WriteString("SELECT ui.id, ui.username, ui.email, ui.display_name, ui.created_at, ui.is_admin, ") stmt.WriteString("SELECT ui.id, ui.username, ui.email, ui.display_name, ui.created_at, ui.is_admin, ")
stmt.WriteString("u.id, u.username, u.email, u.display_name ") stmt.WriteString("u.id, u.username, u.email, u.display_name ")
@ -30,11 +33,6 @@ func (rdb SQLiteUserInviteDatasource) List() ([]UserInvite, error) {
defer rows.Close() defer rows.Close()
invites := []UserInvite{}
var ui UserInvite
var u User
for rows.Next() { for rows.Next() {
if err = rows.Scan(&ui.ID, &ui.Username, &ui.Email, &ui.DisplayName, &ui.CreatedAt, &ui.IsAdmin, &u.ID, &u.Username, &u.Email, &u.DisplayName); err != nil { if err = rows.Scan(&ui.ID, &ui.Username, &ui.Email, &ui.DisplayName, &ui.CreatedAt, &ui.IsAdmin, &u.ID, &u.Username, &u.Email, &u.DisplayName); err != nil {
return nil, err return nil, err
@ -111,6 +109,7 @@ func (rdb SQLiteUserInviteDatasource) Create(ui *UserInvite) (int, error) {
if err != nil { if err != nil {
return -1, err return -1, err
} }
return int(i), nil return int(i), nil
} }
@ -127,11 +126,7 @@ func (rdb SQLiteUserInviteDatasource) Count() (int, error) {
//Removes an user invitation //Removes an user invitation
func (rdb SQLiteUserInviteDatasource) Remove(inviteID int) error { func (rdb SQLiteUserInviteDatasource) Remove(inviteID int) error {
var stmt bytes.Buffer if _, err := rdb.SQLConn.Exec("DELETE FROM user_invite WHERE id=?", inviteID); err != nil {
stmt.WriteString("DELETE FROM user_invite WHERE id=?")
if _, err := rdb.SQLConn.Exec(stmt.String(), inviteID); err != nil {
return err return err
} }

View File

@ -15,6 +15,8 @@ type SQLiteUserDatasource struct {
func (rdb SQLiteUserDatasource) List(p *Pagination) ([]User, error) { func (rdb SQLiteUserDatasource) List(p *Pagination) ([]User, error) {
var stmt bytes.Buffer var stmt bytes.Buffer
var args []interface{} var args []interface{}
var users []User
var u User
stmt.WriteString("SELECT id, username, email, display_name, last_modified, active, is_admin FROM user ORDER BY username ASC ") stmt.WriteString("SELECT id, username, email, display_name, last_modified, active, is_admin FROM user ORDER BY username ASC ")
@ -31,10 +33,6 @@ func (rdb SQLiteUserDatasource) List(p *Pagination) ([]User, error) {
defer rows.Close() defer rows.Close()
users := []User{}
var u User
for rows.Next() { for rows.Next() {
if err = rows.Scan(&u.ID, &u.Username, &u.Email, &u.DisplayName, &u.LastModified, &u.Active, &u.IsAdmin); err != nil { if err = rows.Scan(&u.ID, &u.Username, &u.Email, &u.DisplayName, &u.LastModified, &u.Active, &u.IsAdmin); err != nil {
return nil, err return nil, err
@ -106,7 +104,6 @@ func (rdb SQLiteUserDatasource) Create(u *User) (int, error) {
//Update updates an user //Update updates an user
func (rdb SQLiteUserDatasource) Update(u *User, changePassword bool) error { func (rdb SQLiteUserDatasource) Update(u *User, changePassword bool) error {
var stmt bytes.Buffer var stmt bytes.Buffer
var args []interface{} var args []interface{}
stmt.WriteString("UPDATE user SET display_name=?, username=?, email=?, last_modified=?, active=?, is_admin=? ") stmt.WriteString("UPDATE user SET display_name=?, username=?, email=?, last_modified=?, active=?, is_admin=? ")
@ -128,10 +125,9 @@ func (rdb SQLiteUserDatasource) Update(u *User, changePassword bool) error {
return nil return nil
} }
//Count retuns the amount of users matches the AdminCriteria //Count returns the amount of users matches the AdminCriteria
func (rdb SQLiteUserDatasource) Count(ac AdminCriteria) (int, error) { func (rdb SQLiteUserDatasource) Count(ac AdminCriteria) (int, error) {
var stmt bytes.Buffer var stmt bytes.Buffer
stmt.WriteString("SELECT count(id) FROM user ") stmt.WriteString("SELECT count(id) FROM user ")
if ac == OnlyAdmins { if ac == OnlyAdmins {
@ -151,11 +147,7 @@ func (rdb SQLiteUserDatasource) Count(ac AdminCriteria) (int, error) {
//Removes an user //Removes an user
func (rdb SQLiteUserDatasource) Remove(userID int) error { func (rdb SQLiteUserDatasource) Remove(userID int) error {
var stmt bytes.Buffer if _, err := rdb.SQLConn.Exec("DELETE FROM user WHERE id=?", userID); err != nil {
stmt.WriteString("DELETE FROM user WHERE id=?")
if _, err := rdb.SQLConn.Exec(stmt.String(), userID); err != nil {
return err return err
} }

View File

@ -8,7 +8,7 @@ import (
"net/http" "net/http"
"os" "os"
c "git.hoogi.eu/snafu/go-blog/controllers" "git.hoogi.eu/snafu/go-blog/handler"
m "git.hoogi.eu/snafu/go-blog/middleware" m "git.hoogi.eu/snafu/go-blog/middleware"
"git.hoogi.eu/snafu/go-blog/settings" "git.hoogi.eu/snafu/go-blog/settings"
@ -90,108 +90,108 @@ func fileLoggingHandler(accessLogPath string) (flh func(http.Handler) http.Handl
func restrictedRoutes(ctx *m.AppContext, router *mux.Router, chain alice.Chain) { func restrictedRoutes(ctx *m.AppContext, router *mux.Router, chain alice.Chain) {
//article //article
router.Handle("/articles", chain.Then(useTemplateHandler(ctx, c.AdminListArticlesHandler))).Methods("GET") router.Handle("/articles", chain.Then(useTemplateHandler(ctx, handler.AdminListArticlesHandler))).Methods("GET")
router.Handle("/articles/page/{page}", chain.Then(useTemplateHandler(ctx, c.AdminListArticlesHandler))).Methods("GET") router.Handle("/articles/page/{page}", chain.Then(useTemplateHandler(ctx, handler.AdminListArticlesHandler))).Methods("GET")
router.Handle("/article/new", chain.Then(useTemplateHandler(ctx, c.AdminArticleNewHandler))).Methods("GET") router.Handle("/article/new", chain.Then(useTemplateHandler(ctx, handler.AdminArticleNewHandler))).Methods("GET")
router.Handle("/article/new", chain.Then(useTemplateHandler(ctx, c.AdminArticleNewPostHandler))).Methods("POST") router.Handle("/article/new", chain.Then(useTemplateHandler(ctx, handler.AdminArticleNewPostHandler))).Methods("POST")
router.Handle("/article/edit/{articleID}", chain.Then(useTemplateHandler(ctx, c.AdminArticleEditHandler))).Methods("GET") router.Handle("/article/edit/{articleID}", chain.Then(useTemplateHandler(ctx, handler.AdminArticleEditHandler))).Methods("GET")
router.Handle("/article/edit/{articleID}", chain.Then(useTemplateHandler(ctx, c.AdminArticleEditPostHandler))).Methods("POST") router.Handle("/article/edit/{articleID}", chain.Then(useTemplateHandler(ctx, handler.AdminArticleEditPostHandler))).Methods("POST")
router.Handle("/article/publish/{articleID}", chain.Then(useTemplateHandler(ctx, c.AdminArticlePublishHandler))).Methods("GET") router.Handle("/article/publish/{articleID}", chain.Then(useTemplateHandler(ctx, handler.AdminArticlePublishHandler))).Methods("GET")
router.Handle("/article/publish/{articleID}", chain.Then(useTemplateHandler(ctx, c.AdminArticlePublishPostHandler))).Methods("POST") router.Handle("/article/publish/{articleID}", chain.Then(useTemplateHandler(ctx, handler.AdminArticlePublishPostHandler))).Methods("POST")
router.Handle("/article/delete/{articleID}", chain.Then(useTemplateHandler(ctx, c.AdminArticleDeleteHandler))).Methods("GET") router.Handle("/article/delete/{articleID}", chain.Then(useTemplateHandler(ctx, handler.AdminArticleDeleteHandler))).Methods("GET")
router.Handle("/article/delete/{articleID}", chain.Then(useTemplateHandler(ctx, c.AdminArticleDeletePostHandler))).Methods("POST") router.Handle("/article/delete/{articleID}", chain.Then(useTemplateHandler(ctx, handler.AdminArticleDeletePostHandler))).Methods("POST")
router.Handle("/article/{articleID}", chain.Then(useTemplateHandler(ctx, c.AdminPreviewArticleByIDHandler))).Methods("GET") router.Handle("/article/{articleID}", chain.Then(useTemplateHandler(ctx, handler.AdminPreviewArticleByIDHandler))).Methods("GET")
//user //user
router.Handle("/user/profile", chain.Then(useTemplateHandler(ctx, c.AdminProfileHandler))).Methods("GET") router.Handle("/user/profile", chain.Then(useTemplateHandler(ctx, handler.AdminProfileHandler))).Methods("GET")
router.Handle("/user/profile", chain.Then(useTemplateHandler(ctx, c.AdminProfilePostHandler))).Methods("POST") router.Handle("/user/profile", chain.Then(useTemplateHandler(ctx, handler.AdminProfilePostHandler))).Methods("POST")
router.Handle("/users", chain.Append(ctx.RequireAdmin).Then(useTemplateHandler(ctx, c.AdminUsersHandler))).Methods("GET") router.Handle("/users", chain.Append(ctx.RequireAdmin).Then(useTemplateHandler(ctx, handler.AdminUsersHandler))).Methods("GET")
router.Handle("/users/page/{page}", chain.Then(useTemplateHandler(ctx, c.AdminUsersHandler))).Methods("GET") router.Handle("/users/page/{page}", chain.Then(useTemplateHandler(ctx, handler.AdminUsersHandler))).Methods("GET")
router.Handle("/user/new", chain.Append(ctx.RequireAdmin).Then(useTemplateHandler(ctx, c.AdminUserNewHandler))).Methods("GET") router.Handle("/user/new", chain.Append(ctx.RequireAdmin).Then(useTemplateHandler(ctx, handler.AdminUserNewHandler))).Methods("GET")
router.Handle("/user/new", chain.Append(ctx.RequireAdmin).Then(useTemplateHandler(ctx, c.AdminUserNewPostHandler))).Methods("POST") router.Handle("/user/new", chain.Append(ctx.RequireAdmin).Then(useTemplateHandler(ctx, handler.AdminUserNewPostHandler))).Methods("POST")
router.Handle("/user/edit/{userID}", chain.Append(ctx.RequireAdmin).Then(useTemplateHandler(ctx, c.AdminUserEditHandler))).Methods("GET") router.Handle("/user/edit/{userID}", chain.Append(ctx.RequireAdmin).Then(useTemplateHandler(ctx, handler.AdminUserEditHandler))).Methods("GET")
router.Handle("/user/edit/{userID}", chain.Append(ctx.RequireAdmin).Then(useTemplateHandler(ctx, c.AdminUserEditPostHandler))).Methods("POST") router.Handle("/user/edit/{userID}", chain.Append(ctx.RequireAdmin).Then(useTemplateHandler(ctx, handler.AdminUserEditPostHandler))).Methods("POST")
router.Handle("/user/delete/{userID}", chain.Append(ctx.RequireAdmin).Then(useTemplateHandler(ctx, c.AdminUserDeleteHandler))).Methods("GET") router.Handle("/user/delete/{userID}", chain.Append(ctx.RequireAdmin).Then(useTemplateHandler(ctx, handler.AdminUserDeleteHandler))).Methods("GET")
router.Handle("/user/delete/{userID}", chain.Append(ctx.RequireAdmin).Then(useTemplateHandler(ctx, c.AdminUserDeletePostHandler))).Methods("POST") router.Handle("/user/delete/{userID}", chain.Append(ctx.RequireAdmin).Then(useTemplateHandler(ctx, handler.AdminUserDeletePostHandler))).Methods("POST")
//user invites //user invites
router.Handle("/user-invite/new", chain.Append(ctx.RequireAdmin).Then(useTemplateHandler(ctx, c.AdminUserInviteNewHandler))).Methods("GET") router.Handle("/user-invite/new", chain.Append(ctx.RequireAdmin).Then(useTemplateHandler(ctx, handler.AdminUserInviteNewHandler))).Methods("GET")
router.Handle("/user-invite/new", chain.Append(ctx.RequireAdmin).Then(useTemplateHandler(ctx, c.AdminUserInviteNewPostHandler))).Methods("POST") router.Handle("/user-invite/new", chain.Append(ctx.RequireAdmin).Then(useTemplateHandler(ctx, handler.AdminUserInviteNewPostHandler))).Methods("POST")
router.Handle("/user-invite/resend/{inviteID}", chain.Append(ctx.RequireAdmin).Then(useTemplateHandler(ctx, c.AdminUserInviteResendPostHandler))).Methods("POST") router.Handle("/user-invite/resend/{inviteID}", chain.Append(ctx.RequireAdmin).Then(useTemplateHandler(ctx, handler.AdminUserInviteResendPostHandler))).Methods("POST")
router.Handle("/user-invite/delete/{inviteID}", chain.Append(ctx.RequireAdmin).Then(useTemplateHandler(ctx, c.AdminUserInviteDeleteHandler))).Methods("GET") router.Handle("/user-invite/delete/{inviteID}", chain.Append(ctx.RequireAdmin).Then(useTemplateHandler(ctx, handler.AdminUserInviteDeleteHandler))).Methods("GET")
router.Handle("/user-invite/delete/{inviteID}", chain.Append(ctx.RequireAdmin).Then(useTemplateHandler(ctx, c.AdminUserInviteDeletePostHandler))).Methods("POST") router.Handle("/user-invite/delete/{inviteID}", chain.Append(ctx.RequireAdmin).Then(useTemplateHandler(ctx, handler.AdminUserInviteDeletePostHandler))).Methods("POST")
//site //site
router.Handle("/sites", chain.Append(ctx.RequireAdmin).Then(useTemplateHandler(ctx, c.AdminSitesHandler))).Methods("GET") router.Handle("/sites", chain.Append(ctx.RequireAdmin).Then(useTemplateHandler(ctx, handler.AdminSitesHandler))).Methods("GET")
router.Handle("/site/page/{page}", chain.Then(useTemplateHandler(ctx, c.AdminSitesHandler))).Methods("GET") router.Handle("/site/page/{page}", chain.Then(useTemplateHandler(ctx, handler.AdminSitesHandler))).Methods("GET")
router.Handle("/site/new", chain.Append(ctx.RequireAdmin).Then(useTemplateHandler(ctx, c.AdminSiteNewHandler))).Methods("GET") router.Handle("/site/new", chain.Append(ctx.RequireAdmin).Then(useTemplateHandler(ctx, handler.AdminSiteNewHandler))).Methods("GET")
router.Handle("/site/new", chain.Append(ctx.RequireAdmin).Then(useTemplateHandler(ctx, c.AdminSiteNewPostHandler))).Methods("POST") router.Handle("/site/new", chain.Append(ctx.RequireAdmin).Then(useTemplateHandler(ctx, handler.AdminSiteNewPostHandler))).Methods("POST")
router.Handle("/site/publish/{siteID}", chain.Append(ctx.RequireAdmin).Then(useTemplateHandler(ctx, c.AdminSitePublishHandler))).Methods("GET") router.Handle("/site/publish/{siteID}", chain.Append(ctx.RequireAdmin).Then(useTemplateHandler(ctx, handler.AdminSitePublishHandler))).Methods("GET")
router.Handle("/site/publish/{siteID}", chain.Append(ctx.RequireAdmin).Then(useTemplateHandler(ctx, c.AdminSitePublishPostHandler))).Methods("POST") router.Handle("/site/publish/{siteID}", chain.Append(ctx.RequireAdmin).Then(useTemplateHandler(ctx, handler.AdminSitePublishPostHandler))).Methods("POST")
router.Handle("/site/edit/{siteID}", chain.Append(ctx.RequireAdmin).Then(useTemplateHandler(ctx, c.AdminSiteEditHandler))).Methods("GET") router.Handle("/site/edit/{siteID}", chain.Append(ctx.RequireAdmin).Then(useTemplateHandler(ctx, handler.AdminSiteEditHandler))).Methods("GET")
router.Handle("/site/edit/{siteID}", chain.Append(ctx.RequireAdmin).Then(useTemplateHandler(ctx, c.AdminSiteEditPostHandler))).Methods("POST") router.Handle("/site/edit/{siteID}", chain.Append(ctx.RequireAdmin).Then(useTemplateHandler(ctx, handler.AdminSiteEditPostHandler))).Methods("POST")
router.Handle("/site/delete/{siteID}", chain.Append(ctx.RequireAdmin).Then(useTemplateHandler(ctx, c.AdminSiteDeleteHandler))).Methods("GET") router.Handle("/site/delete/{siteID}", chain.Append(ctx.RequireAdmin).Then(useTemplateHandler(ctx, handler.AdminSiteDeleteHandler))).Methods("GET")
router.Handle("/site/delete/{siteID}", chain.Append(ctx.RequireAdmin).Then(useTemplateHandler(ctx, c.AdminSiteDeletePostHandler))).Methods("POST") router.Handle("/site/delete/{siteID}", chain.Append(ctx.RequireAdmin).Then(useTemplateHandler(ctx, handler.AdminSiteDeletePostHandler))).Methods("POST")
router.Handle("/site/order/{siteID}", chain.Append(ctx.RequireAdmin).Then(useTemplateHandler(ctx, c.AdminSiteOrderHandler))).Methods("POST") router.Handle("/site/order/{siteID}", chain.Append(ctx.RequireAdmin).Then(useTemplateHandler(ctx, handler.AdminSiteOrderHandler))).Methods("POST")
router.Handle("/site/{siteID:[0-9]+}}", chain.Then(useTemplateHandler(ctx, c.AdminGetSiteHandler))).Methods("GET") router.Handle("/site/{siteID:[0-9]+}}", chain.Then(useTemplateHandler(ctx, handler.AdminGetSiteHandler))).Methods("GET")
//article //article
router.Handle("/categories", chain.Then(useTemplateHandler(ctx, c.AdminListCategoriesHandler))).Methods("GET") router.Handle("/categories", chain.Then(useTemplateHandler(ctx, handler.AdminListCategoriesHandler))).Methods("GET")
router.Handle("/category/{categoryID:[0-9]+}}", chain.Then(useTemplateHandler(ctx, c.AdminGetCategoryHandler))).Methods("POST") router.Handle("/category/{categoryID:[0-9]+}}", chain.Then(useTemplateHandler(ctx, handler.AdminGetCategoryHandler))).Methods("POST")
router.Handle("/category/new", chain.Then(useTemplateHandler(ctx, c.AdminCategoryNewHandler))).Methods("GET") router.Handle("/category/new", chain.Then(useTemplateHandler(ctx, handler.AdminCategoryNewHandler))).Methods("GET")
router.Handle("/category/new", chain.Then(useTemplateHandler(ctx, c.AdminCategoryNewPostHandler))).Methods("POST") router.Handle("/category/new", chain.Then(useTemplateHandler(ctx, handler.AdminCategoryNewPostHandler))).Methods("POST")
router.Handle("/category/edit/{categoryID}", chain.Then(useTemplateHandler(ctx, c.AdminCategoryEditHandler))).Methods("GET") router.Handle("/category/edit/{categoryID}", chain.Then(useTemplateHandler(ctx, handler.AdminCategoryEditHandler))).Methods("GET")
router.Handle("/category/edit/{categoryID}", chain.Then(useTemplateHandler(ctx, c.AdminCategoryEditPostHandler))).Methods("POST") router.Handle("/category/edit/{categoryID}", chain.Then(useTemplateHandler(ctx, handler.AdminCategoryEditPostHandler))).Methods("POST")
router.Handle("/category/delete/{categoryID}", chain.Then(useTemplateHandler(ctx, c.AdminCategoryDeleteHandler))).Methods("GET") router.Handle("/category/delete/{categoryID}", chain.Then(useTemplateHandler(ctx, handler.AdminCategoryDeleteHandler))).Methods("GET")
router.Handle("/category/delete/{categoryID}", chain.Then(useTemplateHandler(ctx, c.AdminCategoryDeletePostHandler))).Methods("POST") router.Handle("/category/delete/{categoryID}", chain.Then(useTemplateHandler(ctx, handler.AdminCategoryDeletePostHandler))).Methods("POST")
//file //file
router.Handle("/files", chain.Then(useTemplateHandler(ctx, c.AdminListFilesHandler))).Methods("GET") router.Handle("/files", chain.Then(useTemplateHandler(ctx, handler.AdminListFilesHandler))).Methods("GET")
router.Handle("/files/page/{page}", chain.Then(useTemplateHandler(ctx, c.AdminListFilesHandler))).Methods("GET") router.Handle("/files/page/{page}", chain.Then(useTemplateHandler(ctx, handler.AdminListFilesHandler))).Methods("GET")
router.Handle("/file/upload", chain.Then(useTemplateHandler(ctx, c.AdminUploadFileHandler))).Methods("GET") router.Handle("/file/upload", chain.Then(useTemplateHandler(ctx, handler.AdminUploadFileHandler))).Methods("GET")
router.Handle("/file/upload", chain.Then(useTemplateHandler(ctx, c.AdminUploadFilePostHandler))).Methods("POST") router.Handle("/file/upload", chain.Then(useTemplateHandler(ctx, handler.AdminUploadFilePostHandler))).Methods("POST")
router.Handle("/file/toggleInline/{fileID}", chain.Then(useTemplateHandler(ctx, c.AdminToggleInlineFilePostHandler))).Methods("POST") router.Handle("/file/toggleInline/{fileID}", chain.Then(useTemplateHandler(ctx, handler.AdminToggleInlineFilePostHandler))).Methods("POST")
router.Handle("/file/delete/{fileID}", chain.Then(useTemplateHandler(ctx, c.AdminUploadDeleteHandler))).Methods("GET") router.Handle("/file/delete/{fileID}", chain.Then(useTemplateHandler(ctx, handler.AdminUploadDeleteHandler))).Methods("GET")
router.Handle("/file/delete/{fileID}", chain.Then(useTemplateHandler(ctx, c.AdminUploadDeletePostHandler))).Methods("POST") router.Handle("/file/delete/{fileID}", chain.Then(useTemplateHandler(ctx, handler.AdminUploadDeletePostHandler))).Methods("POST")
router.Handle("/logout", chain.Then(useTemplateHandler(ctx, c.LogoutHandler))).Methods("GET") router.Handle("/logout", chain.Then(useTemplateHandler(ctx, handler.LogoutHandler))).Methods("GET")
router.Handle("/json/session/keep-alive", chain.Then(useJSONHandler(ctx, c.KeepAliveSessionHandler))).Methods("POST") router.Handle("/json/session/keep-alive", chain.Then(useJSONHandler(ctx, handler.KeepAliveSessionHandler))).Methods("POST")
router.Handle("/json/file/upload", chain.Then(useJSONHandler(ctx, c.AdminUploadJSONFilePostHandler))).Methods("POST") router.Handle("/json/file/upload", chain.Then(useJSONHandler(ctx, handler.AdminUploadJSONFilePostHandler))).Methods("POST")
} }
func publicRoutes(ctx *m.AppContext, router *mux.Router, chain alice.Chain) { func publicRoutes(ctx *m.AppContext, router *mux.Router, chain alice.Chain) {
fh := c.FileHandler{ fh := handler.FileHandler{
Context: ctx, Context: ctx,
} }
router.Handle("/", chain.Then(useTemplateHandler(ctx, c.ListArticlesHandler))).Methods("GET") router.Handle("/", chain.Then(useTemplateHandler(ctx, handler.ListArticlesHandler))).Methods("GET")
router.Handle("/articles/category/{categorySlug}", chain.Then(useTemplateHandler(ctx, c.ListArticlesCategoryHandler))).Methods("GET") router.Handle("/articles/category/{categorySlug}", chain.Then(useTemplateHandler(ctx, handler.ListArticlesCategoryHandler))).Methods("GET")
router.Handle("/articles/category/{categorySlug}/{page}", chain.Then(useTemplateHandler(ctx, c.ListArticlesCategoryHandler))).Methods("GET") router.Handle("/articles/category/{categorySlug}/{page}", chain.Then(useTemplateHandler(ctx, handler.ListArticlesCategoryHandler))).Methods("GET")
router.Handle("/index", chain.Then(useTemplateHandler(ctx, c.IndexArticlesHandler))).Methods("GET") router.Handle("/index", chain.Then(useTemplateHandler(ctx, handler.IndexArticlesHandler))).Methods("GET")
router.Handle("/index/category/{categorySlug}", chain.Then(useTemplateHandler(ctx, c.IndexArticlesCategoryHandler))).Methods("GET") router.Handle("/index/category/{categorySlug}", chain.Then(useTemplateHandler(ctx, handler.IndexArticlesCategoryHandler))).Methods("GET")
router.Handle("/articles/page/{page}", chain.Then(useTemplateHandler(ctx, c.ListArticlesHandler))).Methods("GET") router.Handle("/articles/page/{page}", chain.Then(useTemplateHandler(ctx, handler.ListArticlesHandler))).Methods("GET")
router.Handle("/article/{year}/{month}/{slug}", chain.Then(useTemplateHandler(ctx, c.GetArticleHandler))).Methods("GET") router.Handle("/article/{year}/{month}/{slug}", chain.Then(useTemplateHandler(ctx, handler.GetArticleHandler))).Methods("GET")
router.Handle("/article/by-id/{articleID}", chain.Then(useTemplateHandler(ctx, c.GetArticleByIDHandler))).Methods("GET") router.Handle("/article/by-id/{articleID}", chain.Then(useTemplateHandler(ctx, handler.GetArticleByIDHandler))).Methods("GET")
router.Handle("/rss.xml", chain.Then(useXMLHandler(ctx, c.RSSFeed))).Methods("GET") router.Handle("/rss.xml", chain.Then(useXMLHandler(ctx, handler.RSSFeed))).Methods("GET")
router.Handle("/site/{site}", chain.Then(useTemplateHandler(ctx, c.GetSiteHandler))).Methods("GET") router.Handle("/site/{site}", chain.Then(useTemplateHandler(ctx, handler.GetSiteHandler))).Methods("GET")
router.Handle("/file/{uniquename}", chain.ThenFunc(fh.FileGetHandler)).Methods("GET") router.Handle("/file/{uniquename}", chain.ThenFunc(fh.FileGetHandler)).Methods("GET")
router.Handle("/admin", chain.Then(useTemplateHandler(ctx, c.LoginHandler))).Methods("GET") router.Handle("/admin", chain.Then(useTemplateHandler(ctx, handler.LoginHandler))).Methods("GET")
router.Handle("/admin", chain.Then(useTemplateHandler(ctx, c.LoginPostHandler))).Methods("POST") router.Handle("/admin", chain.Then(useTemplateHandler(ctx, handler.LoginPostHandler))).Methods("POST")
router.Handle("/admin/forgot-password", chain.Then(useTemplateHandler(ctx, c.ForgotPasswordHandler))).Methods("GET") router.Handle("/admin/forgot-password", chain.Then(useTemplateHandler(ctx, handler.ForgotPasswordHandler))).Methods("GET")
router.Handle("/admin/forgot-password", chain.Then(useTemplateHandler(ctx, c.ForgotPasswordPostHandler))).Methods("POST") router.Handle("/admin/forgot-password", chain.Then(useTemplateHandler(ctx, handler.ForgotPasswordPostHandler))).Methods("POST")
router.Handle("/admin/reset-password/{hash}", chain.Then(useTemplateHandler(ctx, c.ResetPasswordHandler))).Methods("GET") router.Handle("/admin/reset-password/{hash}", chain.Then(useTemplateHandler(ctx, handler.ResetPasswordHandler))).Methods("GET")
router.Handle("/admin/reset-password/{hash}", chain.Then(useTemplateHandler(ctx, c.ResetPasswordPostHandler))).Methods("POST") router.Handle("/admin/reset-password/{hash}", chain.Then(useTemplateHandler(ctx, handler.ResetPasswordPostHandler))).Methods("POST")
router.Handle("/admin/activate-account/{hash}", chain.Then(useTemplateHandler(ctx, c.ActivateAccountHandler))).Methods("GET") router.Handle("/admin/activate-account/{hash}", chain.Then(useTemplateHandler(ctx, handler.ActivateAccountHandler))).Methods("GET")
router.Handle("/admin/activate-account/{hash}", chain.Then(useTemplateHandler(ctx, c.ActivateAccountPostHandler))).Methods("POST") router.Handle("/admin/activate-account/{hash}", chain.Then(useTemplateHandler(ctx, handler.ActivateAccountPostHandler))).Methods("POST")
} }
func useTemplateHandler(ctx *m.AppContext, handler m.Handler) m.TemplateHandler { func useTemplateHandler(ctx *m.AppContext, handler m.Handler) m.TemplateHandler {

View File

@ -15,8 +15,8 @@ import (
"time" "time"
"git.hoogi.eu/snafu/cfg" "git.hoogi.eu/snafu/cfg"
"git.hoogi.eu/snafu/go-blog/components/logger" "git.hoogi.eu/snafu/go-blog/crypt"
"git.hoogi.eu/snafu/go-blog/utils" "git.hoogi.eu/snafu/go-blog/logger"
) )
type LoginMethod int type LoginMethod int
@ -286,11 +286,7 @@ func (cfg *Settings) GenerateCSRF() (bool, error) {
if _, err := os.Stat(csrfTokenFilename); os.IsNotExist(err) { if _, err := os.Stat(csrfTokenFilename); os.IsNotExist(err) {
//create a random csrf token //create a random csrf token
r := utils.RandomSource{ b = crypt.AlphaUpperLowerNumericSpecial.RandomSequence(32)
CharsToGen: utils.AlphaUpperLowerNumericSpecial,
}
b = r.RandomSequence(32)
err := ioutil.WriteFile(csrfTokenFilename, b, 0640) err := ioutil.WriteFile(csrfTokenFilename, b, 0640)

50
slug/slug.go Normal file
View File

@ -0,0 +1,50 @@
package slug
import (
"bytes"
"regexp"
"strconv"
"strings"
"unicode"
)
var multipleDashes = regexp.MustCompile(`[-]{2,}`)
//CreateURLSafeSlug creates a URL safe slug to use in URL
func CreateURLSafeSlug(input string, suffix int) string {
input = strings.Replace(input, "-", "", -1)
input = strings.Map(func(r rune) rune {
switch {
case r == ' ':
return '-'
case unicode.IsLetter(r), unicode.IsDigit(r):
return r
default:
return -1
}
}, strings.ToLower(strings.TrimSpace(input)))
input = strings.Trim(input, "-")
input = multipleDashes.ReplaceAllString(input, "-")
if suffix > 0 {
input += strconv.Itoa(suffix)
}
return input
}
func substitute(input string, subs map[rune]string) string {
var b bytes.Buffer
for _, c := range input {
if _, ok := subs[c]; ok {
b.WriteString(subs[c])
} else {
b.WriteRune(c)
}
}
return b.String()
}

View File

@ -2,12 +2,12 @@
// Use of this source code is governed by a MIT-style // Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
package utils_test package slug_test
import ( import (
"testing" "testing"
"git.hoogi.eu/snafu/go-blog/utils" "git.hoogi.eu/snafu/go-blog/slug"
) )
func TestCreateURLSafeSlug(t *testing.T) { func TestCreateURLSafeSlug(t *testing.T) {
@ -26,7 +26,7 @@ func TestCreateURLSafeSlug(t *testing.T) {
} }
for i := 0; i < len(testcases)-1; i = i + 2 { for i := 0; i < len(testcases)-1; i = i + 2 {
actual := utils.CreateURLSafeSlug(testcases[i], 0) actual := slug.CreateURLSafeSlug(testcases[i], 0)
if actual != testcases[i+1] { if actual != testcases[i+1] {
t.Errorf("Got: '%s'; want '%s'", actual, testcases[i+1]) t.Errorf("Got: '%s'; want '%s'", actual, testcases[i+1])

View File

@ -1,136 +0,0 @@
// Copyright 2018 Lars Hoogestraat
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package utils
import (
"bytes"
"regexp"
"strconv"
"strings"
"unicode"
)
//AppendString uses byte buffer to append multiple strings
func AppendString(s ...string) string {
var buffer bytes.Buffer
for _, value := range s {
buffer.WriteString(value)
}
return buffer.String()
}
//AppendBytes uses byte buffer to append multiple byte arrays
func AppendBytes(s ...[]byte) []byte {
var buffer bytes.Buffer
for _, value := range s {
buffer.Write(value)
}
return buffer.Bytes()
}
//TrimmedStringIsEmpty trims spaces returns true if length is 0
func TrimmedStringIsEmpty(s string) bool {
return len(strings.TrimSpace(s)) == 0
}
//IsOneOfStringsEmpty checks if one of the given strings is empty
func IsOneOfStringsEmpty(s ...string) bool {
for _, value := range s {
if len(value) == 0 {
return true
}
}
return true
}
var filenameSubs = map[rune]string{
'/': "",
'\\': "",
':': "",
'*': "",
'?': "",
'"': "",
'<': "",
'>': "",
'|': "",
' ': "",
}
func isDot(r rune) bool {
return '.' == r
}
//SanitizeFilename sanitizes a filename for safe use when serving file
func SanitizeFilename(s string) string {
s = strings.TrimFunc(s, unicode.IsSpace)
s = removeControlCharacters(s)
s = substitute(s, filenameSubs)
s = strings.TrimLeftFunc(s, isDot)
return s
}
var slugSubs = map[rune]string{
'&': "",
'$': "",
'+': "",
',': "",
'/': "",
':': "",
';': "",
'=': "",
'?': "",
'@': "",
'#': "",
'!': "",
'\'': "",
'(': "",
')': "",
'*': "",
'%': "",
}
var multipleDashes = regexp.MustCompile(`[-]{2,}`)
//CreateURLSafeSlug creates a url safe slug to use in urls
func CreateURLSafeSlug(input string, suffix int) string {
input = removeControlCharacters(input)
input = substitute(input, slugSubs)
input = strings.TrimSpace(input)
input = strings.Replace(input, " ", "-", -1)
input = strings.ToLower(input)
input = multipleDashes.ReplaceAllString(input, "-")
if suffix > 0 {
input += strconv.Itoa(suffix)
}
return input
}
func substitute(input string, subs map[rune]string) string {
var b bytes.Buffer
for _, c := range input {
if _, ok := subs[c]; ok {
b.WriteString(subs[c])
} else {
b.WriteRune(c)
}
}
return b.String()
}
func removeControlCharacters(input string) string {
var b bytes.Buffer
for _, c := range input {
if c > 31 {
b.WriteRune(c)
}
}
return b.String()
}