Files
threshold/internal/auth/password/argon2_test.go
T
Michael Gaffney c163d790a4 Add configuration settings for Argon2 password KDF (#217)
* Bump generated proto code

* Add argon2 conf sql and proto files

* Add generated code for argon2 conf

* Add Resources for Argon2 configuration

* Add get and set Configuration to repository

* Add current config reference to AuthMethod

* Fix authmethod_test

* Fix TestAccount_New

* Add WithConfiguration to options

* Add sql views for current configuration

* Fix SetTableName

* Remove duplicate testAccounts function

* Make tests clearer

* Fix comment

* Rename test variable

* Use NewPrivateId function for private Ids

* Refactor: rename public_id to private_id for password_conf and password_credential

* Remove bad comment

* Check if embedded argon2 configuration is nil

* Add checks around the WithConfiguration option

* Use spaces not tabs in proto file

* Add doc to explain choice of oplog ticket

* Verify oplog in test
2020-07-29 07:58:19 -04:00

271 lines
6.6 KiB
Go

package password
import (
"context"
"errors"
"testing"
"github.com/hashicorp/watchtower/internal/auth/password/store"
"github.com/hashicorp/watchtower/internal/db"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestArgon2Configuration_New(t *testing.T) {
conn, _ := db.TestSetup(t, "postgres")
rw := db.New(conn)
authMethods := testAuthMethods(t, conn, 1)
authMethod := authMethods[0]
authMethodId := authMethod.GetPublicId()
ctx := context.Background()
// There should already be a configuration when an authMethod is created.
t.Run("default-configuration", func(t *testing.T) {
assert, require := assert.New(t), require.New(t)
var confs []*Argon2Configuration
err := rw.SearchWhere(ctx, &confs, "password_method_id = ?", []interface{}{authMethodId})
require.NoError(err)
require.Equal(1, len(confs))
got := confs[0]
want := &Argon2Configuration{
Argon2Configuration: &store.Argon2Configuration{
PrivateId: got.GetPrivateId(),
CreateTime: got.GetCreateTime(),
PasswordMethodId: authMethodId,
Iterations: 3,
Memory: 64 * 1024,
Threads: 1,
SaltLength: 32,
KeyLength: 32,
},
}
assert.Equal(want, got)
})
t.Run("no-duplicate-configurations", func(t *testing.T) {
assert, require := assert.New(t), require.New(t)
got := NewArgon2Configuration()
require.NotNil(got)
var err error
got.PrivateId, err = newArgon2ConfigurationId()
require.NoError(err)
got.PasswordMethodId = authMethodId
err = rw.Create(ctx, got)
assert.Error(err)
})
t.Run("multiple-configurations", func(t *testing.T) {
assert, require := assert.New(t), require.New(t)
var confs []*Argon2Configuration
err := rw.SearchWhere(ctx, &confs, "password_method_id = ?", []interface{}{authMethodId})
require.NoError(err)
assert.Equal(1, len(confs))
c1 := NewArgon2Configuration()
require.NotNil(c1)
c1.PrivateId, err = newArgon2ConfigurationId()
require.NoError(err)
c1.PasswordMethodId = authMethodId
c1.Iterations = c1.Iterations + 1
c1.Threads = c1.Threads + 1
err = rw.Create(ctx, c1)
assert.NoError(err)
c2 := NewArgon2Configuration()
require.NotNil(c2)
c2.PrivateId, err = newArgon2ConfigurationId()
require.NoError(err)
c2.PasswordMethodId = authMethodId
c2.Memory = 32 * 1024
c2.SaltLength = 16
c2.KeyLength = 16
err = rw.Create(ctx, c2)
assert.NoError(err)
confs = nil
err = rw.SearchWhere(ctx, &confs, "password_method_id = ?", []interface{}{authMethodId})
require.NoError(err)
assert.Equal(3, len(confs))
})
}
func TestArgon2Configuration_Readonly(t *testing.T) {
conn, _ := db.TestSetup(t, "postgres")
rw := db.New(conn)
changeIterations := func() func(*Argon2Configuration) (*Argon2Configuration, []string) {
return func(c *Argon2Configuration) (*Argon2Configuration, []string) {
c.Iterations = c.Iterations + 1
return c, []string{"Iterations"}
}
}
changeThreads := func() func(*Argon2Configuration) (*Argon2Configuration, []string) {
return func(c *Argon2Configuration) (*Argon2Configuration, []string) {
c.Threads = c.Threads + 1
return c, []string{"Threads"}
}
}
changeMemory := func() func(*Argon2Configuration) (*Argon2Configuration, []string) {
return func(c *Argon2Configuration) (*Argon2Configuration, []string) {
c.Memory = c.Memory + 1
return c, []string{"Memory"}
}
}
authMethods := testAuthMethods(t, conn, 1)
authMethod := authMethods[0]
authMethodId := authMethod.GetPublicId()
var tests = []struct {
name string
chgFn func(*Argon2Configuration) (*Argon2Configuration, []string)
}{
{
name: "iterations",
chgFn: changeIterations(),
},
{
name: "threads",
chgFn: changeThreads(),
},
{
name: "Memory",
chgFn: changeMemory(),
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
assert, require := assert.New(t), require.New(t)
var confs []*Argon2Configuration
err := rw.SearchWhere(context.Background(), &confs, "password_method_id = ?", []interface{}{authMethodId})
require.NoError(err)
assert.Greater(len(confs), 0)
orig := confs[0]
changed, masks := tt.chgFn(orig)
require.NotEmpty(changed.GetPrivateId())
count, err := rw.Update(context.Background(), changed, masks, nil)
assert.Error(err)
assert.Equal(0, count)
})
}
}
func TestArgon2Configuration_Validate(t *testing.T) {
var tests = []struct {
name string
in *Argon2Configuration
want error
}{
{
name: "nil-configuration",
in: nil,
want: ErrInvalidConfiguration,
},
{
name: "nil-embedded-config",
in: &Argon2Configuration{},
want: ErrInvalidConfiguration,
},
{
name: "valid-default",
in: NewArgon2Configuration(),
},
{
name: "valid-changes",
in: &Argon2Configuration{
Argon2Configuration: &store.Argon2Configuration{
Iterations: 3 * 2,
Memory: 32 * 1024,
Threads: 10,
SaltLength: 16,
KeyLength: 16,
},
},
},
{
name: "invalid-iterations",
in: &Argon2Configuration{
Argon2Configuration: &store.Argon2Configuration{
Iterations: 0,
Memory: 1,
Threads: 1,
SaltLength: 1,
KeyLength: 1,
},
},
want: ErrInvalidConfiguration,
},
{
name: "invalid-memory",
in: &Argon2Configuration{
Argon2Configuration: &store.Argon2Configuration{
Iterations: 1,
Memory: 0,
Threads: 1,
SaltLength: 1,
KeyLength: 1,
},
},
want: ErrInvalidConfiguration,
},
{
name: "invalid-threads",
in: &Argon2Configuration{
Argon2Configuration: &store.Argon2Configuration{
Iterations: 1,
Memory: 1,
Threads: 0,
SaltLength: 1,
KeyLength: 1,
},
},
want: ErrInvalidConfiguration,
},
{
name: "invalid-salt-length",
in: &Argon2Configuration{
Argon2Configuration: &store.Argon2Configuration{
Iterations: 1,
Memory: 1,
Threads: 1,
SaltLength: 0,
KeyLength: 1,
},
},
want: ErrInvalidConfiguration,
},
{
name: "invalid-key-length",
in: &Argon2Configuration{
Argon2Configuration: &store.Argon2Configuration{
Iterations: 1,
Memory: 1,
Threads: 1,
SaltLength: 1,
KeyLength: 0,
},
},
want: ErrInvalidConfiguration,
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
assert, require := assert.New(t), require.New(t)
got := tt.in.validate()
if tt.want == nil {
assert.NoErrorf(got, "valid argon2 configuration: %+v", tt.in)
return
}
require.Error(got)
assert.Truef(errors.Is(got, tt.want), "want err: %q got: %q", tt.want, got)
})
}
}