Compare commits
3 Commits
pongo-temp
...
50187f5a34
| Author | SHA1 | Date | |
|---|---|---|---|
| 50187f5a34 | |||
| dd671d561c | |||
| 7512d75a4d |
@ -16,6 +16,11 @@
|
|||||||
- [Password Hashing (bcrypt)](https://gowebexamples.com/password-hashing/)
|
- [Password Hashing (bcrypt)](https://gowebexamples.com/password-hashing/)
|
||||||
|
|
||||||
|
|
||||||
|
## Tools
|
||||||
|
|
||||||
|
- [Bombardier benchmarking](https://github.com/codesenberg/bombardier)
|
||||||
|
|
||||||
|
|
||||||
## Bombardier benchmark
|
## Bombardier benchmark
|
||||||
|
|
||||||
Pandora - Jet templating engine
|
Pandora - Jet templating engine
|
||||||
@ -56,3 +61,5 @@ Statistics Avg Stdev Max
|
|||||||
others - 0
|
others - 0
|
||||||
Throughput: 19.91MB/s
|
Throughput: 19.91MB/s
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
1
app/lib/auth/auth.go
Normal file
1
app/lib/auth/auth.go
Normal file
@ -0,0 +1 @@
|
|||||||
|
package auth
|
||||||
41
app/lib/auth/passwords.go
Normal file
41
app/lib/auth/passwords.go
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
package auth
|
||||||
|
|
||||||
|
import (
|
||||||
|
"regexp"
|
||||||
|
|
||||||
|
"golang.org/x/crypto/bcrypt"
|
||||||
|
)
|
||||||
|
|
||||||
|
// about bcrypt cost: https://docs.laminas.dev/laminas-crypt/password/#bcrypt
|
||||||
|
// bcrypt cost benchmarks: https://github.com/nsmithuk/bcrypt-cost-go
|
||||||
|
const BCRYPT_COST = 10
|
||||||
|
const MIN_PASSWORD_LENGTH = 10
|
||||||
|
|
||||||
|
func IsPasswordGoodEnough(password string) bool {
|
||||||
|
var re *regexp.Regexp
|
||||||
|
passwordBytes := []byte(password)
|
||||||
|
|
||||||
|
if len(password) < MIN_PASSWORD_LENGTH {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
re, _ = regexp.Compile("[a-z]")
|
||||||
|
if re.Find(passwordBytes) == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
re, _ = regexp.Compile("[A-Z]")
|
||||||
|
if re.Find(passwordBytes) == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
re, _ = regexp.Compile("[0-9]")
|
||||||
|
//lint:ignore S1008 allow early exit instead optimization
|
||||||
|
if re.Find(passwordBytes) == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func HashPassword(password string, secretKey string) (string, error) {
|
||||||
|
bytes, err := bcrypt.GenerateFromPassword([]byte(password+secretKey), BCRYPT_COST)
|
||||||
|
return string(bytes), err
|
||||||
|
}
|
||||||
@ -1,4 +1,4 @@
|
|||||||
package common
|
package cfg
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
@ -1,7 +1,9 @@
|
|||||||
package common
|
package db
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"iris-test/app/lib/cfg"
|
||||||
|
"iris-test/app/lib/logging"
|
||||||
"iris-test/app/repository"
|
"iris-test/app/repository"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
@ -20,18 +22,18 @@ var DBConn *gorm.DB
|
|||||||
func InitDB() *gorm.DB {
|
func InitDB() *gorm.DB {
|
||||||
var connectionString = strings.Join([]string{
|
var connectionString = strings.Join([]string{
|
||||||
"postgres://",
|
"postgres://",
|
||||||
Config.Database.Username, ":",
|
cfg.Config.Database.Username, ":",
|
||||||
Config.Database.Password, "@",
|
cfg.Config.Database.Password, "@",
|
||||||
Config.Database.Host, ":",
|
cfg.Config.Database.Host, ":",
|
||||||
Config.Database.Port, "/",
|
cfg.Config.Database.Port, "/",
|
||||||
Config.Database.Name,
|
cfg.Config.Database.Name,
|
||||||
"?sslmode=disable",
|
"?sslmode=disable",
|
||||||
"&TimeZone=UTC",
|
"&TimeZone=UTC",
|
||||||
"&connect_timeout=", strconv.Itoa(DB_CONNECTION_TIMEOUT),
|
"&connect_timeout=", strconv.Itoa(DB_CONNECTION_TIMEOUT),
|
||||||
}, "")
|
}, "")
|
||||||
|
|
||||||
var logLevel = gormLogger.Silent
|
var logLevel = gormLogger.Silent
|
||||||
if Config.Application.DebugSQL {
|
if cfg.Config.Application.DebugSQL {
|
||||||
logLevel = gormLogger.Info
|
logLevel = gormLogger.Info
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -41,7 +43,7 @@ func InitDB() *gorm.DB {
|
|||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
msg := fmt.Sprintf("Error connecting to database: %s. Terminating!", err)
|
msg := fmt.Sprintf("Error connecting to database: %s. Terminating!", err)
|
||||||
Log.Error(msg)
|
logging.Error(msg)
|
||||||
panic(msg)
|
panic(msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1,9 +1,10 @@
|
|||||||
package common
|
package logging
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"iris-test/app/lib/cfg"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
@ -28,9 +29,9 @@ func Warn(message string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func InitLogging() {
|
func InitLogging() {
|
||||||
logLevel, err := logrus.ParseLevel(Config.Application.LogLevel)
|
logLevel, err := logrus.ParseLevel(cfg.Config.Application.LogLevel)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(fmt.Sprintf("Invalid configured logLevel: %s\n", Config.Application.LogLevel))
|
panic(fmt.Sprintf("Invalid configured logLevel: %s\n", cfg.Config.Application.LogLevel))
|
||||||
}
|
}
|
||||||
|
|
||||||
Log.SetLevel(logLevel)
|
Log.SetLevel(logLevel)
|
||||||
@ -42,14 +43,14 @@ func InitLogging() {
|
|||||||
DisableQuote: true,
|
DisableQuote: true,
|
||||||
})
|
})
|
||||||
|
|
||||||
LogFile := Config.Application.LogFile
|
LogFile := cfg.Config.Application.LogFile
|
||||||
file, err := os.OpenFile(
|
file, err := os.OpenFile(
|
||||||
LogFile,
|
LogFile,
|
||||||
os.O_CREATE|os.O_WRONLY|os.O_APPEND,
|
os.O_CREATE|os.O_WRONLY|os.O_APPEND,
|
||||||
0655,
|
0655,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
msg := fmt.Sprintf("Failed to log to file %s: %s", Config.Application.LogFile, err)
|
msg := fmt.Sprintf("Failed to log to file %s: %s", cfg.Config.Application.LogFile, err)
|
||||||
Log.Warning(msg)
|
Log.Warning(msg)
|
||||||
panic(msg)
|
panic(msg)
|
||||||
}
|
}
|
||||||
@ -57,7 +58,7 @@ func InitLogging() {
|
|||||||
mw := io.MultiWriter(os.Stdout, file)
|
mw := io.MultiWriter(os.Stdout, file)
|
||||||
Log.SetOutput(mw)
|
Log.SetOutput(mw)
|
||||||
|
|
||||||
configJson, err := json.Marshal(Config)
|
configJson, err := json.Marshal(cfg.Config)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
Info(fmt.Sprintf("Using config: %s", configJson))
|
Info(fmt.Sprintf("Using config: %s", configJson))
|
||||||
}
|
}
|
||||||
22
app/main.go
22
app/main.go
@ -2,7 +2,9 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"iris-test/app/common"
|
"iris-test/app/lib/cfg"
|
||||||
|
"iris-test/app/lib/db"
|
||||||
|
"iris-test/app/lib/logging"
|
||||||
"iris-test/app/views"
|
"iris-test/app/views"
|
||||||
"os"
|
"os"
|
||||||
"time"
|
"time"
|
||||||
@ -16,17 +18,17 @@ import (
|
|||||||
var redisDB *redis.Database
|
var redisDB *redis.Database
|
||||||
|
|
||||||
func createSessionEngine() *sessions.Sessions {
|
func createSessionEngine() *sessions.Sessions {
|
||||||
redisAddr := fmt.Sprintf("%s:%d", common.Config.Redis.Host, common.Config.Redis.Port)
|
redisAddr := fmt.Sprintf("%s:%d", cfg.Config.Redis.Host, cfg.Config.Redis.Port)
|
||||||
|
|
||||||
redisDB = redis.New(redis.Config{
|
redisDB = redis.New(redis.Config{
|
||||||
Network: "tcp",
|
Network: "tcp",
|
||||||
Addr: redisAddr,
|
Addr: redisAddr,
|
||||||
Timeout: time.Duration(30) * time.Second,
|
Timeout: time.Duration(30) * time.Second,
|
||||||
MaxActive: 10,
|
MaxActive: 10,
|
||||||
Username: common.Config.Redis.Username,
|
Username: cfg.Config.Redis.Username,
|
||||||
Password: common.Config.Redis.Password,
|
Password: cfg.Config.Redis.Password,
|
||||||
Database: common.Config.Redis.Database,
|
Database: cfg.Config.Redis.Database,
|
||||||
Prefix: common.Config.Redis.Prefix,
|
Prefix: cfg.Config.Redis.Prefix,
|
||||||
Driver: redis.GoRedis(), // defaults to this driver.
|
Driver: redis.GoRedis(), // defaults to this driver.
|
||||||
// To set a custom, existing go-redis client, use the "SetClient" method:
|
// To set a custom, existing go-redis client, use the "SetClient" method:
|
||||||
// Driver: redis.GoRedis().SetClient(customGoRedisClient)
|
// Driver: redis.GoRedis().SetClient(customGoRedisClient)
|
||||||
@ -84,7 +86,7 @@ func createApp() *iris.Application {
|
|||||||
accessLog := createAccessLog()
|
accessLog := createAccessLog()
|
||||||
|
|
||||||
app := iris.New()
|
app := iris.New()
|
||||||
app.Logger().SetLevel(common.Config.Application.LogLevel)
|
app.Logger().SetLevel(cfg.Config.Application.LogLevel)
|
||||||
app.Use(sessionsEngine.Handler())
|
app.Use(sessionsEngine.Handler())
|
||||||
app.UseRouter(accessLog.Handler)
|
app.UseRouter(accessLog.Handler)
|
||||||
app.RegisterView(iris.Jet("./app/templates", ".jet").Reload(true))
|
app.RegisterView(iris.Jet("./app/templates", ".jet").Reload(true))
|
||||||
@ -93,9 +95,9 @@ func createApp() *iris.Application {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
common.InitCfg()
|
cfg.InitCfg()
|
||||||
common.InitLogging()
|
logging.InitLogging()
|
||||||
common.InitDB()
|
db.InitDB()
|
||||||
|
|
||||||
app := createApp()
|
app := createApp()
|
||||||
defer redisDB.Close()
|
defer redisDB.Close()
|
||||||
|
|||||||
@ -1,6 +1,8 @@
|
|||||||
package models
|
package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"iris-test/app/lib/auth"
|
||||||
|
"iris-test/app/lib/cfg"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -19,8 +21,12 @@ func (u *User) TableName() string {
|
|||||||
return "users"
|
return "users"
|
||||||
}
|
}
|
||||||
|
|
||||||
// func (u *User) SetPassword(password string) (string, error) {
|
func (u *User) SetPassword(password string) error {
|
||||||
// secretKey := Config.Application.SecretKey
|
secretKey := cfg.Config.Application.SecretKey
|
||||||
// bytes, err := bcrypt.GenerateFromPassword([]byte(password), 14)
|
hashedPassword, err := auth.HashPassword(password, secretKey)
|
||||||
// return string(bytes), err
|
if err != nil {
|
||||||
// }
|
return err
|
||||||
|
}
|
||||||
|
u.Password = hashedPassword
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
@ -20,6 +20,10 @@
|
|||||||
<label class="form-label">Email</label>
|
<label class="form-label">Email</label>
|
||||||
<input type="email" name="email" class="form-control" value="{{ user.Email }}" required>
|
<input type="email" name="email" class="form-control" value="{{ user.Email }}" required>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="form-label">Password</label>
|
||||||
|
<input type="text" name="password" class="form-control">
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="d-flex">
|
<div class="d-flex">
|
||||||
<a href="/users" class="btn btn-outline-secondary ms-auto me-2">
|
<a href="/users" class="btn btn-outline-secondary ms-auto me-2">
|
||||||
|
|||||||
@ -1,6 +1,8 @@
|
|||||||
package views
|
package views
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
"iris-test/app/lib/auth"
|
||||||
"iris-test/app/repository"
|
"iris-test/app/repository"
|
||||||
|
|
||||||
"github.com/kataras/iris/v12"
|
"github.com/kataras/iris/v12"
|
||||||
@ -10,6 +12,7 @@ type editUserForm struct {
|
|||||||
FirstName string `form:"first-name"`
|
FirstName string `form:"first-name"`
|
||||||
LastName string `form:"last-name"`
|
LastName string `form:"last-name"`
|
||||||
Email string `form:"email"`
|
Email string `form:"email"`
|
||||||
|
Password string `form:"password"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetUsersPage(ctx iris.Context) {
|
func GetUsersPage(ctx iris.Context) {
|
||||||
@ -66,6 +69,13 @@ func SaveUserPage(ctx iris.Context) {
|
|||||||
user.LastName = form.LastName
|
user.LastName = form.LastName
|
||||||
user.Email = form.Email
|
user.Email = form.Email
|
||||||
|
|
||||||
|
if len(form.Password) > 0 {
|
||||||
|
user.SetPassword(form.Password)
|
||||||
|
fmt.Printf("Set password: %s\n", user.Password)
|
||||||
|
fmt.Printf("IsPasswordGoodEnough: %v\n", auth.IsPasswordGoodEnough(form.Password))
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
userRepository.Save(user)
|
userRepository.Save(user)
|
||||||
|
|
||||||
ctx.Redirect("/users")
|
ctx.Redirect("/users")
|
||||||
|
|||||||
Reference in New Issue
Block a user