Apply password requirements to admin-created users (#6667)

This was requested by a customer.
This commit is contained in:
Zach Wasserman 2022-07-19 09:47:25 -07:00 committed by GitHub
parent 80070cd273
commit 710c304d94
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 127 additions and 32 deletions

1
changes/admin-user Normal file
View file

@ -0,0 +1 @@
* Enforce the same password requirements when admins create users directly as are already applied for users created through invites.

View file

@ -144,28 +144,7 @@ type UserPayload struct {
func (p *UserPayload) VerifyInviteCreate() error {
invalid := &InvalidArgumentError{}
if p.Name == nil {
invalid.Append("name", "Full name missing required argument")
} else if *p.Name == "" {
invalid.Append("name", "Full name cannot be empty")
}
// we don't need a password for single sign on
if p.SSOInvite == nil || !*p.SSOInvite {
if p.Password == nil {
invalid.Append("password", "Password missing required argument")
} else if *p.Password == "" {
invalid.Append("password", "Password cannot be empty")
} else if err := ValidatePasswordRequirements(*p.Password); err != nil {
invalid.Append("password", err.Error())
}
}
if p.Email == nil {
invalid.Append("email", "Email missing required argument")
} else if *p.Email == "" {
invalid.Append("email", "Email cannot be empty")
}
p.verifyCreateShared(invalid)
if p.InviteToken == nil {
invalid.Append("invite_token", "Invite token missing required argument")
@ -181,6 +160,19 @@ func (p *UserPayload) VerifyInviteCreate() error {
func (p *UserPayload) VerifyAdminCreate() error {
invalid := &InvalidArgumentError{}
p.verifyCreateShared(invalid)
if p.InviteToken != nil {
invalid.Append("invite_token", "Invite token should not be specified with admin user creation")
}
if invalid.HasErrors() {
return invalid
}
return nil
}
func (p *UserPayload) verifyCreateShared(invalid *InvalidArgumentError) {
if p.Name == nil {
invalid.Append("name", "Full name missing required argument")
} else if *p.Name == "" {
@ -193,8 +185,9 @@ func (p *UserPayload) VerifyAdminCreate() error {
invalid.Append("password", "Password missing required argument")
} else if *p.Password == "" {
invalid.Append("password", "Password cannot be empty")
} else if err := ValidatePasswordRequirements(*p.Password); err != nil {
invalid.Append("password", err.Error())
}
// Skip password validation in the case of admin created users
}
if p.SSOEnabled != nil && *p.SSOEnabled && p.Password != nil && len(*p.Password) > 0 {
@ -206,15 +199,6 @@ func (p *UserPayload) VerifyAdminCreate() error {
} else if *p.Email == "" {
invalid.Append("email", "Email cannot be empty")
}
if p.InviteToken != nil {
invalid.Append("invite_token", "Invite token should not be specified with admin user creation")
}
if invalid.HasErrors() {
return invalid
}
return nil
}
func (p *UserPayload) VerifyModify(ownUser bool) error {

View file

@ -4,6 +4,7 @@ import (
"fmt"
"testing"
"github.com/fleetdm/fleet/v4/server/ptr"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"golang.org/x/crypto/bcrypt"
@ -98,3 +99,112 @@ func TestSaltAndHashPassword(t *testing.T) {
require.Error(t, err)
}
}
func TestAdminCreateValidate(t *testing.T) {
testCases := []struct {
payload UserPayload
// if errContains is empty then no error is expected
errContains []string
}{
{
payload: UserPayload{},
errContains: []string{"name", "email", "password"},
},
{
payload: UserPayload{Name: ptr.String(""), Email: ptr.String(""), Password: ptr.String("")},
errContains: []string{"name", "email", "password"},
},
{
payload: UserPayload{Name: ptr.String("Foo"), Email: ptr.String(""), Password: ptr.String("")},
errContains: []string{"email", "password"},
},
{
payload: UserPayload{Name: ptr.String("Foo"), Email: ptr.String("foo@example.com"), Password: ptr.String("")},
errContains: []string{"password"},
},
{
payload: UserPayload{Name: ptr.String("Foo"), Email: ptr.String("foo@example.com"), Password: ptr.String("foo")},
errContains: []string{"password"},
},
{
payload: UserPayload{Name: ptr.String("Foo"), Email: ptr.String("foo@example.com"), Password: ptr.String("Foofoofoo1337#"), InviteToken: ptr.String("foo")},
errContains: []string{"invite_token"},
},
{
payload: UserPayload{Name: ptr.String("Foo"), Email: ptr.String("foo@example.com"), Password: ptr.String("Foofoofoo1337#")},
errContains: nil,
},
}
for _, tc := range testCases {
t.Run("", func(t *testing.T) {
err := tc.payload.VerifyAdminCreate()
if len(tc.errContains) == 0 {
require.NoError(t, err)
} else {
ierr := err.(*InvalidArgumentError)
require.Equal(t, len(tc.errContains), len(*ierr))
for _, expected := range tc.errContains {
assertContainsErrorName(t, *ierr, expected)
}
}
})
}
}
func TestInviteCreateValidate(t *testing.T) {
testCases := []struct {
payload UserPayload
// if errContains is empty then no error is expected
errContains []string
}{
{
payload: UserPayload{},
errContains: []string{"name", "email", "password", "invite_token"},
},
{
payload: UserPayload{Name: ptr.String(""), Email: ptr.String(""), Password: ptr.String("")},
errContains: []string{"name", "email", "password", "invite_token"},
},
{
payload: UserPayload{Name: ptr.String("Foo"), Email: ptr.String(""), Password: ptr.String("")},
errContains: []string{"email", "password", "invite_token"},
},
{
payload: UserPayload{Name: ptr.String("Foo"), Email: ptr.String("foo@example.com"), Password: ptr.String("")},
errContains: []string{"password", "invite_token"},
},
{
payload: UserPayload{Name: ptr.String("Foo"), Email: ptr.String("foo@example.com"), Password: ptr.String("foo")},
errContains: []string{"password", "invite_token"},
},
{
payload: UserPayload{Name: ptr.String("Foo"), Email: ptr.String("foo@example.com"), Password: ptr.String("Foofoofoo1337#"), InviteToken: ptr.String("foo")},
errContains: nil,
},
}
for _, tc := range testCases {
t.Run("", func(t *testing.T) {
err := tc.payload.VerifyInviteCreate()
if len(tc.errContains) == 0 {
require.NoError(t, err)
} else {
ierr := err.(*InvalidArgumentError)
for _, expected := range tc.errContains {
require.Equal(t, len(tc.errContains), len(*ierr))
assertContainsErrorName(t, *ierr, expected)
}
}
})
}
}
func assertContainsErrorName(t *testing.T, invalid InvalidArgumentError, name string) {
for _, argErr := range invalid {
if argErr.name == name {
return
}
}
t.Errorf("%v does not contain error %s", invalid, name)
}