Skip to content

Commit cda4127

Browse files
committed
Make password strength configurable
1 parent 8cffa66 commit cda4127

11 files changed

+31
-30
lines changed

api/user.go

+7-6
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ type UserDatabase interface {
2121

2222
// The UserAPI provides handlers for managing users.
2323
type UserAPI struct {
24-
DB UserDatabase
24+
DB UserDatabase
25+
PasswordStrength int
2526
}
2627

2728
// GetUsers returns all the users
@@ -49,7 +50,7 @@ func (a *UserAPI) CreateUser(ctx *gin.Context) {
4950
if len(user.Pass) == 0 {
5051
ctx.AbortWithError(400, errors.New("password may not be empty"))
5152
} else {
52-
internal := toInternal(&user, []byte{})
53+
internal := a.toInternal(&user, []byte{})
5354
if a.DB.GetUserByName(internal.Name) == nil {
5455
a.DB.CreateUser(internal)
5556
ctx.JSON(200, toExternal(internal))
@@ -95,7 +96,7 @@ func (a *UserAPI) ChangePassword(ctx *gin.Context) {
9596
pw := userPassword{}
9697
if err := ctx.Bind(&pw); err == nil {
9798
user := a.DB.GetUserByID(auth.GetUserID(ctx))
98-
user.Pass = auth.CreatePassword(pw.Pass)
99+
user.Pass = auth.CreatePassword(pw.Pass, a.PasswordStrength)
99100
a.DB.UpdateUser(user)
100101
}
101102
}
@@ -106,7 +107,7 @@ func (a *UserAPI) UpdateUserByID(ctx *gin.Context) {
106107
var user *model.UserExternal
107108
if err := ctx.Bind(&user); err == nil {
108109
if oldUser := a.DB.GetUserByID(id); oldUser != nil {
109-
internal := toInternal(user, oldUser.Pass)
110+
internal := a.toInternal(user, oldUser.Pass)
110111
internal.ID = id
111112
a.DB.UpdateUser(internal)
112113
ctx.JSON(200, toExternal(internal))
@@ -124,13 +125,13 @@ func toUInt(id string) (uint, error) {
124125
return uint(parsed), err
125126
}
126127

127-
func toInternal(response *model.UserExternal, pw []byte) *model.User {
128+
func (a *UserAPI) toInternal(response *model.UserExternal, pw []byte) *model.User {
128129
user := &model.User{
129130
Name: response.Name,
130131
Admin: response.Admin,
131132
}
132133
if response.Pass != "" {
133-
user.Pass = auth.CreatePassword(response.Pass)
134+
user.Pass = auth.CreatePassword(response.Pass, a.PasswordStrength)
134135
} else {
135136
user.Pass = pw
136137
}

api/user_test.go

+4-4
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ func (s *UserSuite) Test_DeleteUserByID() {
119119

120120
func (s *UserSuite) Test_CreateUser() {
121121
pwByte := []byte{1, 2, 3}
122-
patch := monkey.Patch(auth.CreatePassword, func(pw string) []byte {
122+
patch := monkey.Patch(auth.CreatePassword, func(pw string, strength int) []byte {
123123
if pw == "mylittlepony" {
124124
return pwByte
125125
}
@@ -160,7 +160,7 @@ func (s *UserSuite) Test_CreateUser_NoName() {
160160

161161
func (s *UserSuite) Test_CreateUser_NameAlreadyExists() {
162162
pwByte := []byte{1, 2, 3}
163-
monkey.Patch(auth.CreatePassword, func(pw string) []byte { return pwByte })
163+
monkey.Patch(auth.CreatePassword, func(pw string, strength int) []byte { return pwByte })
164164

165165
s.db.On("GetUserByName", "tom").Return(&model.User{ID: 3, Name: "tom"})
166166

@@ -215,7 +215,7 @@ func (s *UserSuite) Test_UpdateUserByID_UpdateNotPassword() {
215215

216216
func (s *UserSuite) Test_UpdateUserByID_UpdatePassword() {
217217
pwByte := []byte{1, 2, 3}
218-
patch := monkey.Patch(auth.CreatePassword, func(pw string) []byte { return pwByte })
218+
patch := monkey.Patch(auth.CreatePassword, func(pw string, strength int) []byte { return pwByte })
219219
defer patch.Unpatch()
220220

221221
s.db.On("GetUserByID", uint(2)).Return(normalUser)
@@ -236,7 +236,7 @@ func (s *UserSuite) Test_UpdateUserByID_UpdatePassword() {
236236

237237
func (s *UserSuite) Test_UpdatePassword() {
238238
pwByte := []byte{1, 2, 3}
239-
createPasswordPatch := monkey.Patch(auth.CreatePassword, func(pw string) []byte { return pwByte })
239+
createPasswordPatch := monkey.Patch(auth.CreatePassword, func(pw string, strength int) []byte { return pwByte })
240240
defer createPasswordPatch.Unpatch()
241241
patchUser := monkey.Patch(auth.GetUserID, func(*gin.Context) uint { return 1 })
242242
defer patchUser.Unpatch()

app.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -31,14 +31,14 @@ func main() {
3131
fmt.Println("Starting Gotify version", vInfo.Version+"@"+BuildDate)
3232
rand.Seed(time.Now().UnixNano())
3333
conf := config.Get()
34-
db, err := database.New(conf.Database.Dialect, conf.Database.Connection, conf.DefaultUser.Name, conf.DefaultUser.Pass)
34+
db, err := database.New(conf.Database.Dialect, conf.Database.Connection, conf.DefaultUser.Name, conf.DefaultUser.Pass, conf.PassStrength)
3535
if err != nil {
3636
panic(err)
3737
}
3838
defer db.Close()
3939

4040
gin.SetMode(gin.ReleaseMode)
41-
engine, closeable := router.Create(db, vInfo)
41+
engine, closeable := router.Create(db, vInfo, conf)
4242
defer closeable()
4343

4444
runner.Run(engine, conf)

auth/authentication_test.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,8 @@ func (s *AuthenticationSuite) SetupSuite() {
3939
s.DB.On("GetUserByID", uint(1)).Return(&model.User{ID: 1, Name: "irrelevant", Admin: false})
4040
s.DB.On("GetUserByID", uint(2)).Return(&model.User{ID: 2, Name: "irrelevant", Admin: true})
4141

42-
s.DB.On("GetUserByName", "existing").Return(&model.User{Name: "existing", Pass: CreatePassword("pw")})
43-
s.DB.On("GetUserByName", "admin").Return(&model.User{Name: "admin", Pass: CreatePassword("pw"), Admin: true})
42+
s.DB.On("GetUserByName", "existing").Return(&model.User{Name: "existing", Pass: CreatePassword("pw", 5)})
43+
s.DB.On("GetUserByName", "admin").Return(&model.User{Name: "admin", Pass: CreatePassword("pw", 5), Admin: true})
4444
s.DB.On("GetUserByName", mock.Anything).Return(nil)
4545
}
4646

auth/password.go

+1-3
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,8 @@ package auth
22

33
import "golang.org/x/crypto/bcrypt"
44

5-
var strength = 13
6-
75
// CreatePassword returns a hashed version of the given password.
8-
func CreatePassword(pw string) []byte {
6+
func CreatePassword(pw string, strength int) []byte {
97
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(pw), strength)
108
if err != nil {
119
panic(err)

auth/password_test.go

+3-4
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,15 @@ import (
99
)
1010

1111
func TestPasswordSuccess(t *testing.T) {
12-
password := CreatePassword("secret")
12+
password := CreatePassword("secret", 5)
1313
assert.Equal(t, true, ComparePassword(password, []byte("secret")))
1414
}
1515

1616
func TestPasswordFailure(t *testing.T) {
17-
password := CreatePassword("secret")
17+
password := CreatePassword("secret", 5)
1818
assert.Equal(t, false, ComparePassword(password, []byte("secretx")))
1919
}
2020

2121
func TestBCryptFailure(t *testing.T) {
22-
strength = 12312 // invalid value
23-
assert.Panics(t, func() { CreatePassword("secret") })
22+
assert.Panics(t, func() { CreatePassword("secret", 12312) })
2423
}

config/config.go

+1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ type Configuration struct {
2828
Name string `default:"admin"`
2929
Pass string `default:"admin"`
3030
}
31+
PassStrength int `default:"10"`
3132
}
3233

3334
// Get returns the configuration extracted from env variables or config file.

database/database.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import (
1010
)
1111

1212
// New creates a new wrapper for the gorm database framework.
13-
func New(dialect, connection, defaultUser, defaultPass string) (*GormDatabase, error) {
13+
func New(dialect, connection, defaultUser, defaultPass string, strength int) (*GormDatabase, error) {
1414
db, err := gorm.Open(dialect, connection)
1515
if err != nil {
1616
return nil, err
@@ -25,7 +25,7 @@ func New(dialect, connection, defaultUser, defaultPass string) (*GormDatabase, e
2525
if !db.HasTable(new(model.User)) && !db.HasTable(new(model.Message)) &&
2626
!db.HasTable(new(model.Client)) && !db.HasTable(new(model.Application)) {
2727
db.AutoMigrate(new(model.User), new(model.Application), new(model.Message), new(model.Client))
28-
db.Create(&model.User{Name: defaultUser, Pass: auth.CreatePassword(defaultPass), Admin: true})
28+
db.Create(&model.User{Name: defaultUser, Pass: auth.CreatePassword(defaultPass, strength), Admin: true})
2929
}
3030

3131
return &GormDatabase{DB: db}, nil

database/database_test.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ type DatabaseSuite struct {
1818
}
1919

2020
func (s *DatabaseSuite) BeforeTest(suiteName, testName string) {
21-
db, err := New("sqlite3", "testdb.db", "defaultUser", "defaultPass")
21+
db, err := New("sqlite3", "testdb.db", "defaultUser", "defaultPass", 5)
2222
assert.Nil(s.T(), err)
2323
s.db = db
2424
}
@@ -29,6 +29,6 @@ func (s *DatabaseSuite) AfterTest(suiteName, testName string) {
2929
}
3030

3131
func TestInvalidDialect(t *testing.T) {
32-
_, err := New("asdf", "testdb.db", "defaultUser", "defaultPass")
32+
_, err := New("asdf", "testdb.db", "defaultUser", "defaultPass", 5)
3333
assert.NotNil(t, err)
3434
}

router/router.go

+4-3
Original file line numberDiff line numberDiff line change
@@ -12,18 +12,19 @@ import (
1212

1313
"net/http"
1414

15+
"github.com/gotify/server/config"
1516
"github.com/gotify/server/docs"
16-
"github.com/gotify/server/stream"
1717
"github.com/gotify/server/model"
18+
"github.com/gotify/server/stream"
1819
)
1920

2021
// Create creates the gin engine with all routes.
21-
func Create(db *database.GormDatabase, vInfo *model.VersionInfo) (*gin.Engine, func()) {
22+
func Create(db *database.GormDatabase, vInfo *model.VersionInfo, conf *config.Configuration) (*gin.Engine, func()) {
2223
streamHandler := stream.New(200*time.Second, 15*time.Second)
2324
authentication := auth.Auth{DB: db}
2425
messageHandler := api.MessageAPI{Notifier: streamHandler, DB: db}
2526
tokenHandler := api.TokenAPI{DB: db}
26-
userHandler := api.UserAPI{DB: db}
27+
userHandler := api.UserAPI{DB: db, PasswordStrength: conf.PassStrength}
2728

2829
g := gin.New()
2930

router/router_test.go

+3-2
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import (
1515
"github.com/gotify/server/model"
1616
"github.com/stretchr/testify/assert"
1717
"github.com/stretchr/testify/suite"
18+
"github.com/gotify/server/config"
1819
)
1920

2021
var (
@@ -36,9 +37,9 @@ type IntegrationSuite struct {
3637
func (s *IntegrationSuite) BeforeTest(string, string) {
3738
gin.SetMode(gin.TestMode)
3839
var err error
39-
s.db, err = database.New("sqlite3", "itest.db", "admin", "pw")
40+
s.db, err = database.New("sqlite3", "itest.db", "admin", "pw", 5)
4041
assert.Nil(s.T(), err)
41-
g, closable := Create(s.db, &model.VersionInfo{Version:"1.0.0", BuildDate:"2018-02-20-17:30:47", Branch:"master", Commit:"asdasds"})
42+
g, closable := Create(s.db, &model.VersionInfo{Version:"1.0.0", BuildDate:"2018-02-20-17:30:47", Branch:"master", Commit:"asdasds"}, &config.Configuration{PassStrength:5})
4243
s.closable = closable
4344
s.server = httptest.NewServer(g)
4445
}

0 commit comments

Comments
 (0)