diff --git a/cmd/userstyles-world/main.go b/cmd/userstyles-world/main.go index 67bb66c9..83a05582 100644 --- a/cmd/userstyles-world/main.go +++ b/cmd/userstyles-world/main.go @@ -56,7 +56,8 @@ func main() { log.Initialize() cache.Init() images.CheckVips() - util.InitCrypto() + util.InitCrypto(config.Secrets) + util.InitPool(config.Secrets) jwtware.Init() email.Init() validator.Init() @@ -65,7 +66,7 @@ func main() { web.Init() app := fiber.New(fiber.Config{ - Views: templates.New(http.FS(web.ViewsDir)), + Views: templates.New(http.FS(web.ViewsDir), config.App), ViewsLayout: "layouts/main", ProxyHeader: config.App.ProxyHeader, JSONEncoder: util.JSONEncoder, diff --git a/handlers/core/errors_test.go b/handlers/core/errors_test.go index 79420651..0844cd76 100644 --- a/handlers/core/errors_test.go +++ b/handlers/core/errors_test.go @@ -6,6 +6,8 @@ import ( "testing" "github.com/gofiber/fiber/v2" + + "userstyles.world/modules/config" "userstyles.world/modules/templates" ) @@ -53,8 +55,10 @@ func Test404Normal(t *testing.T) { func Test404Pages(t *testing.T) { t.Parallel() + c := &config.AppConfig{Production: true} + app := fiber.New(fiber.Config{ - Views: templates.New(http.Dir("../../web/views")), + Views: templates.New(http.Dir("../../web/views"), c), }) app.Get("/", func(c *fiber.Ctx) error { c.Response().Header.SetContentType(fiber.MIMETextHTMLCharsetUTF8) diff --git a/handlers/user/account.go b/handlers/user/account.go index 2091f754..0d0a5941 100644 --- a/handlers/user/account.go +++ b/handlers/user/account.go @@ -120,7 +120,7 @@ func EditAccount(c *fiber.Ctx) error { }) } - pw, err := util.HashPassword(newPassword) + pw, err := util.HashPassword(newPassword, config.Secrets) if err != nil { return c.Status(fiber.StatusInternalServerError).Render("err", fiber.Map{ "Title": "Failed to hash password", diff --git a/handlers/user/reset.go b/handlers/user/reset.go index ceb2f766..69a02245 100644 --- a/handlers/user/reset.go +++ b/handlers/user/reset.go @@ -108,7 +108,7 @@ func ResetPost(c *fiber.Ctx) error { }) } - pw, err := util.HashPassword(newPassword) + pw, err := util.HashPassword(newPassword, config.Secrets) if err != nil { return c.Status(fiber.StatusInternalServerError).Render("err", fiber.Map{ "Title": "Failed to hash password", diff --git a/handlers/user/verify.go b/handlers/user/verify.go index 6b5353b0..4f116cbb 100644 --- a/handlers/user/verify.go +++ b/handlers/user/verify.go @@ -60,7 +60,7 @@ func VerifyGet(c *fiber.Ctx) error { Email: claims["email"].(string), } - pw, err := util.HashPassword(u.Password) + pw, err := util.HashPassword(u.Password, config.Secrets) if err != nil { return c.Status(fiber.StatusInternalServerError).Render("err", fiber.Map{ "Title": "Failed to hash password", diff --git a/modules/config/config.go b/modules/config/config.go index c1b36c93..11d29640 100644 --- a/modules/config/config.go +++ b/modules/config/config.go @@ -124,6 +124,7 @@ var ( Storage *StorageConfig ) +// UpdateGitInfo updates copyright year at the start of every year. func (app *AppConfig) UpdateCopyright() { if app.Name == "" { log.Fatal("config: App.Name can't be an empty string") @@ -143,8 +144,9 @@ func (app *AppConfig) UpdateGitInfo() { app.GitSignature = GitSignature } -// defaultConfig is a set of default configs used in development environment. -func defaultConfig() *config { +// DefaultConfig is a set of default configurations used for development and +// testing environments. +func DefaultConfig() *config { return &config{ App: AppConfig{ Addr: ":3000", @@ -199,7 +201,7 @@ func Load(path string) error { return err } - c := defaultConfig() + c := DefaultConfig() if err = json.Unmarshal(b, &c); err != nil { return err } diff --git a/modules/database/init/init.go b/modules/database/init/init.go index 47d27de5..370d6d82 100644 --- a/modules/database/init/init.go +++ b/modules/database/init/init.go @@ -169,7 +169,7 @@ func seed() { defer log.Info.Println("Finished seeding mock data.") pw := func(s string) string { - s, err := util.HashPassword(s) + s, err := util.HashPassword(s, config.Secrets) if err != nil { log.Warn.Fatal(err) } diff --git a/modules/templates/templates.go b/modules/templates/templates.go index f0ab16b7..7d1b3b03 100644 --- a/modules/templates/templates.go +++ b/modules/templates/templates.go @@ -39,7 +39,7 @@ func status() sys { } } -func New(views http.FileSystem) *html.Engine { +func New(views http.FileSystem, app *config.AppConfig) *html.Engine { engine := html.NewFileSystem(views, ".tmpl") engine.AddFunc("sys", status) @@ -145,7 +145,7 @@ func New(views http.FileSystem) *html.Engine { return string(b) }) - if !config.App.Production { + if !app.Production { engine.Reload(true) } diff --git a/modules/util/bcrypt.go b/modules/util/bcrypt.go index 02484592..80de96ef 100644 --- a/modules/util/bcrypt.go +++ b/modules/util/bcrypt.go @@ -7,8 +7,8 @@ import ( ) // HashPassword generates a hash out of a password. -func HashPassword(pw string) (string, error) { - hash, err := bcrypt.GenerateFromPassword([]byte(pw), config.Secrets.PasswordCost) +func HashPassword(pw string, secrets *config.SecretsConfig) (string, error) { + hash, err := bcrypt.GenerateFromPassword([]byte(pw), secrets.PasswordCost) if err != nil { return "", err } diff --git a/modules/util/bcrypt_test.go b/modules/util/bcrypt_test.go index ed7059e6..d39ad80d 100644 --- a/modules/util/bcrypt_test.go +++ b/modules/util/bcrypt_test.go @@ -10,9 +10,9 @@ const pw = "UserStyles.world" func TestHashPassword(t *testing.T) { t.Parallel() - got, err := HashPassword(pw) + got, err := HashPassword(pw, &c.Secrets) if err != nil { - t.Fatalf("bcrypt failed: %s", err) + t.Fatalf("bcrypt failed to hash a password: %s", err) } exp := "$2a$10" @@ -23,14 +23,14 @@ func TestHashPassword(t *testing.T) { func BenchmarkHashPassword(b *testing.B) { for i := 0; i < b.N; i++ { - _, _ = HashPassword(pw) + _, _ = HashPassword(pw, &c.Secrets) } } func TestVerifyPassword(t *testing.T) { t.Parallel() - got, err := HashPassword(pw) + got, err := HashPassword(pw, &c.Secrets) if err != nil { t.Fatalf("bcrypt failed: %s", err) } @@ -42,7 +42,7 @@ func TestVerifyPassword(t *testing.T) { } func BenchmarkVerifyPassword(b *testing.B) { - hash, err := HashPassword(pw) + hash, err := HashPassword(pw, &c.Secrets) if err != nil { b.Fatal(err) } diff --git a/modules/util/chacha20poly1305.go b/modules/util/chacha20poly1305.go index b64fd8d3..f24bf0c4 100644 --- a/modules/util/chacha20poly1305.go +++ b/modules/util/chacha20poly1305.go @@ -21,22 +21,22 @@ var ( ) // InitCrypto initializes cryptographic ciphers. -func InitCrypto() { - VerifySigningKey = []byte(config.Secrets.RecoverTokenKey) - OAuthPSigningKey = []byte(config.Secrets.ProviderTokenKey) +func InitCrypto(secrets *config.SecretsConfig) { + VerifySigningKey = []byte(secrets.RecoverTokenKey) + OAuthPSigningKey = []byte(secrets.ProviderTokenKey) var err error - AEADCrypto, err = chacha20poly1305.NewX([]byte(config.Secrets.CryptoKey)) + AEADCrypto, err = chacha20poly1305.NewX([]byte(secrets.CryptoKey)) if err != nil { log.Warn.Fatalf("Cannot create AEAD_CRYPTO cipher: %s\n", err) } - AEADOAuth, err = chacha20poly1305.NewX([]byte(config.Secrets.OAuthClientKey)) + AEADOAuth, err = chacha20poly1305.NewX([]byte(secrets.OAuthClientKey)) if err != nil { log.Warn.Fatalf("Cannot create AEAD_OAUTH cipher: %s\n", err) } - AEADOAuthp, err = chacha20poly1305.NewX([]byte(config.Secrets.OAuthProviderKey)) + AEADOAuthp, err = chacha20poly1305.NewX([]byte(secrets.OAuthProviderKey)) if err != nil { log.Warn.Fatalf("Cannot create AEAD_OAUTHP cipher: %s\n", err) } diff --git a/modules/util/chacha20poly1305_test.go b/modules/util/chacha20poly1305_test.go index 4af5ca8f..b0883834 100644 --- a/modules/util/chacha20poly1305_test.go +++ b/modules/util/chacha20poly1305_test.go @@ -10,9 +10,11 @@ import ( "userstyles.world/modules/config" ) +var c = config.DefaultConfig() + func TestSimpleKey(t *testing.T) { t.Parallel() - InitCrypto() + InitCrypto(&c.Secrets) jwtToken, err := NewJWT(). SetClaim("email", "vednoc@usw.local"). @@ -21,13 +23,8 @@ func TestSimpleKey(t *testing.T) { t.Error(err) } - scrambleConfig := &config.SecretsConfig{ - ScrambleStepSize: 3, - ScrambleBytesPerInsert: 2, - } - - sealedText := sealText(jwtToken, AEADCrypto, scrambleConfig) - unSealedText, err := openText(UnsafeString(sealedText), AEADCrypto, scrambleConfig) + sealedText := sealText(jwtToken, AEADCrypto, &c.Secrets) + unSealedText, err := openText(UnsafeString(sealedText), AEADCrypto, &c.Secrets) if err != nil { t.Error(err) } @@ -94,7 +91,7 @@ func benchamarkDecodePreparedText(b *testing.B, buf []byte, scrambleConfig *conf } func BenchmarkPureChaCha20Poly1305(b *testing.B) { - InitCrypto() + InitCrypto(&c.Secrets) b.ResetTimer() scrambleConfig := &config.SecretsConfig{ @@ -113,7 +110,7 @@ func BenchmarkPureChaCha20Poly1305(b *testing.B) { } func BenchmarkPrepareText(b *testing.B) { - InitCrypto() + InitCrypto(&c.Secrets) b.ResetTimer() scrambleConfig := &config.SecretsConfig{ diff --git a/modules/util/crypto.go b/modules/util/crypto.go index ae245777..57b9d340 100644 --- a/modules/util/crypto.go +++ b/modules/util/crypto.go @@ -18,12 +18,16 @@ var ( return &b }, } + hmacPool sync.Pool +) + +func InitPool(secrets *config.SecretsConfig) { hmacPool = sync.Pool{ New: func() interface{} { - return hmac.New(sha512.New, []byte(config.Secrets.StatsKey)) + return hmac.New(sha512.New, []byte(secrets.StatsKey)) }, } -) +} // HashIP generates a unique hash for stats. func HashIP(key string) (string, error) { diff --git a/modules/util/crypto_test.go b/modules/util/crypto_test.go index 71b77cee..c8c1ed94 100644 --- a/modules/util/crypto_test.go +++ b/modules/util/crypto_test.go @@ -15,6 +15,7 @@ var hashCases = []struct { func TestHashIP(t *testing.T) { t.Parallel() + InitPool(&c.Secrets) for _, c := range hashCases { actual, err := HashIP(c.ip + " " + c.id)