You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
boundary/internal/auth/ldap/account_test.go

418 lines
12 KiB

// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1
package ldap
import (
"context"
"fmt"
"strings"
"testing"
"github.com/hashicorp/boundary/globals"
"github.com/hashicorp/boundary/internal/auth/ldap/store"
"github.com/hashicorp/boundary/internal/db"
"github.com/hashicorp/boundary/internal/errors"
"github.com/hashicorp/boundary/internal/iam"
"github.com/hashicorp/boundary/internal/oplog"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"google.golang.org/protobuf/proto"
)
func TestNewAccount(t *testing.T) {
t.Parallel()
testCtx := context.Background()
testAuthMethodId := fmt.Sprintf("%s_1", globals.LdapAuthMethodPrefix)
tests := []struct {
name string
ctx context.Context
scopeId string
authMethodId string
loginName string
opt []Option
want *Account
wantErrMatch *errors.Template
wantErrContains string
}{
{
name: "success-with-all-opts",
ctx: testCtx,
scopeId: "global",
authMethodId: testAuthMethodId,
loginName: "test-login-name",
opt: []Option{
WithName(testCtx, "test-name"),
WithDescription(testCtx, "test-description"),
WithEmail(testCtx, "alice@bob.com"),
WithFullName(testCtx, "alice eve smith"),
WithDn(testCtx, "uid=alice, ou=people, o=test org"),
WithMemberOfGroups(testCtx, "test-group"),
},
want: &Account{
Account: &store.Account{
AuthMethodId: testAuthMethodId,
ScopeId: "global",
LoginName: "test-login-name",
Name: "test-name",
Description: "test-description",
Email: "alice@bob.com",
FullName: "alice eve smith",
Dn: "uid=alice, ou=people, o=test org",
MemberOfGroups: "[\"test-group\"]",
},
},
},
{
name: "missing-scope-id",
ctx: testCtx,
authMethodId: testAuthMethodId,
loginName: "test-login-name",
wantErrMatch: errors.T(errors.InvalidParameter),
wantErrContains: "missing scope id",
},
{
name: "missing-auth-method-id",
ctx: testCtx,
scopeId: "global",
loginName: "test-login-name",
wantErrMatch: errors.T(errors.InvalidParameter),
wantErrContains: "missing auth method id",
},
{
name: "missing-login-name",
ctx: testCtx,
scopeId: "global",
authMethodId: testAuthMethodId,
wantErrMatch: errors.T(errors.InvalidParameter),
wantErrContains: "missing login name",
},
{
name: "validate-err",
ctx: testCtx,
scopeId: "global",
authMethodId: testAuthMethodId,
loginName: "test-login-name",
opt: []Option{WithEmail(testCtx, strings.Repeat("-", 400))},
wantErrMatch: errors.T(errors.InvalidParameter),
wantErrContains: "email address is too long",
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
assert, require := assert.New(t), require.New(t)
got, err := NewAccount(tc.ctx, tc.scopeId, tc.authMethodId, tc.loginName, tc.opt...)
if tc.wantErrMatch != nil {
require.Error(err)
assert.Nil(got)
assert.True(errors.Match(tc.wantErrMatch, err))
if tc.wantErrContains != "" {
assert.Contains(err.Error(), tc.wantErrContains)
}
return
}
require.NoError(err)
assert.Equal(tc.want, got)
})
}
}
func TestAccount_clone(t *testing.T) {
t.Parallel()
testCtx := context.TODO()
testConn, _ := db.TestSetup(t, "postgres")
testWrapper := db.TestWrapper(t)
t.Run("valid", func(t *testing.T) {
assert, require := assert.New(t), require.New(t)
org, _ := iam.TestScopes(t, iam.TestRepo(t, testConn, testWrapper))
m := TestAuthMethod(t, testConn, testWrapper, org.PublicId, []string{"ldaps://ldap1"})
orig, err := NewAccount(testCtx, m.ScopeId, m.PublicId, "alice", WithFullName(testCtx, "Alice Eve Smith"), WithEmail(testCtx, "alice@alice.com"))
require.NoError(err)
cp := orig.clone()
assert.True(proto.Equal(cp.Account, orig.Account))
})
t.Run("not-equal", func(t *testing.T) {
assert, require := assert.New(t), require.New(t)
org, _ := iam.TestScopes(t, iam.TestRepo(t, testConn, testWrapper))
m := TestAuthMethod(t, testConn, testWrapper, org.PublicId, []string{"ldaps://ldap1"})
orig, err := NewAccount(testCtx, m.ScopeId, m.PublicId, "alice", WithFullName(testCtx, "Alice Eve Smith"), WithEmail(testCtx, "alice@alice.com"))
require.NoError(err)
orig2, err := NewAccount(testCtx, m.ScopeId, m.PublicId, "bob", WithFullName(testCtx, "Bob Eve Smith"), WithEmail(testCtx, "bob@alice.com"))
require.NoError(err)
cp := orig.clone()
assert.True(!proto.Equal(cp.Account, orig2.Account))
})
}
func TestAccount_SetTableName(t *testing.T) {
t.Parallel()
defaultTableName := accountTableName
tests := []struct {
name string
setNameTo string
want string
}{
{
name: "new-name",
setNameTo: "new-name",
want: "new-name",
},
{
name: "reset to default",
setNameTo: "",
want: defaultTableName,
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
assert, require := assert.New(t), require.New(t)
def := AllocAccount()
require.Equal(defaultTableName, def.TableName())
m := AllocAccount()
m.SetTableName(tc.setNameTo)
assert.Equal(tc.want, m.TableName())
})
}
}
func TestAccount_oplog(t *testing.T) {
t.Parallel()
testCtx := context.Background()
testAcct, err := NewAccount(testCtx, "global", "test-id", "test-login-name")
testAcct.PublicId = "test-public-id"
require.NoError(t, err)
tests := []struct {
name string
ctx context.Context
acct *Account
opType oplog.OpType
want oplog.Metadata
wantErrMatch *errors.Template
wantErrContains string
}{
{
name: "create",
ctx: testCtx,
acct: testAcct,
opType: oplog.OpType_OP_TYPE_CREATE,
want: oplog.Metadata{
"auth-method-id": {"test-id"},
"resource-public-id": {"test-public-id"},
"scope-id": {"global"},
"op-type": {oplog.OpType_OP_TYPE_CREATE.String()},
"resource-type": {"ldap account"},
},
},
{
name: "update",
ctx: testCtx,
acct: testAcct,
opType: oplog.OpType_OP_TYPE_UPDATE,
want: oplog.Metadata{
"auth-method-id": {"test-id"},
"resource-public-id": {"test-public-id"},
"scope-id": {"global"},
"op-type": {oplog.OpType_OP_TYPE_UPDATE.String()},
"resource-type": {"ldap account"},
},
},
{
name: "missing-auth-method-id",
ctx: testCtx,
acct: func() *Account {
cp := testAcct.clone()
cp.AuthMethodId = ""
return cp
}(),
opType: oplog.OpType_OP_TYPE_UPDATE,
wantErrMatch: errors.T(errors.InvalidParameter),
wantErrContains: "missing auth method id",
},
{
name: "missing-scope-id",
ctx: testCtx,
acct: func() *Account {
cp := testAcct.clone()
cp.ScopeId = ""
return cp
}(),
opType: oplog.OpType_OP_TYPE_UPDATE,
wantErrMatch: errors.T(errors.InvalidParameter),
wantErrContains: "missing scope id",
},
{
name: "missing-public-id",
ctx: testCtx,
acct: func() *Account {
cp := testAcct.clone()
cp.PublicId = ""
return cp
}(),
opType: oplog.OpType_OP_TYPE_UPDATE,
wantErrMatch: errors.T(errors.InvalidParameter),
wantErrContains: "missing public id",
},
{
name: "missing-op-type",
ctx: testCtx,
acct: testAcct,
wantErrMatch: errors.T(errors.InvalidParameter),
wantErrContains: "missing op type",
},
{
name: "missing-account",
ctx: testCtx,
opType: oplog.OpType_OP_TYPE_UPDATE,
wantErrMatch: errors.T(errors.InvalidParameter),
wantErrContains: "missing account",
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
assert, require := assert.New(t), require.New(t)
got, err := tc.acct.oplog(tc.ctx, tc.opType)
if tc.wantErrMatch != nil {
require.Error(err)
assert.Nil(got)
assert.True(errors.Match(tc.wantErrMatch, err))
if tc.wantErrContains != "" {
assert.Contains(err.Error(), tc.wantErrContains)
}
return
}
require.NoError(err)
assert.Equal(tc.want, got)
})
}
}
func TestAccount_GetAuthMethodId(t *testing.T) {
t.Parallel()
assert.Empty(t, AllocAccount().GetAuthMethodId())
}
func TestAccount_GetSubject(t *testing.T) {
t.Parallel()
assert.Empty(t, AllocAccount().GetSubject())
}
func TestAccount_validate(t *testing.T) {
t.Parallel()
testCtx := context.Background()
tests := []struct {
name string
ctx context.Context
caller errors.Op
acct *Account
wantErrMatch *errors.Template
wantErrContains string
}{
{
name: "missing-caller",
ctx: testCtx,
acct: func() *Account {
a, err := NewAccount(testCtx, "global", "test-auth-method-id", "test-login-name")
require.NoError(t, err)
return a
}(),
wantErrMatch: errors.T(errors.InvalidParameter),
wantErrContains: "missing caller",
},
{
name: "missing-scope-id",
ctx: testCtx,
caller: "test",
acct: func() *Account {
a, err := NewAccount(testCtx, "global", "test-auth-method-id", "test-login-name")
require.NoError(t, err)
a.ScopeId = ""
return a
}(),
wantErrMatch: errors.T(errors.InvalidParameter),
wantErrContains: "missing scope id",
},
{
name: "missing-auth-method-id",
ctx: testCtx,
caller: "test",
acct: func() *Account {
a, err := NewAccount(testCtx, "global", "test-auth-method-id", "test-login-name")
require.NoError(t, err)
a.AuthMethodId = ""
return a
}(),
wantErrMatch: errors.T(errors.InvalidParameter),
wantErrContains: "missing auth method id",
},
{
name: "missing-login-name",
ctx: testCtx,
caller: "test",
acct: func() *Account {
a, err := NewAccount(testCtx, "global", "test-auth-method-id", "test-login-name")
require.NoError(t, err)
a.LoginName = ""
return a
}(),
wantErrMatch: errors.T(errors.InvalidParameter),
wantErrContains: "missing login name",
},
{
name: "email-too-long",
ctx: testCtx,
caller: "test",
acct: func() *Account {
a, err := NewAccount(testCtx, "global", "test-auth-method-id", "test-login-name")
require.NoError(t, err)
a.Email = strings.Repeat("-", 321)
return a
}(),
wantErrMatch: errors.T(errors.InvalidParameter),
wantErrContains: "email address is too long",
},
{
name: "full-name-too-long",
ctx: testCtx,
caller: "test",
acct: func() *Account {
a, err := NewAccount(testCtx, "global", "test-auth-method-id", "test-login-name")
require.NoError(t, err)
a.FullName = strings.Repeat("-", 513)
return a
}(),
wantErrMatch: errors.T(errors.InvalidParameter),
wantErrContains: "full name is too long",
},
{
name: "login-name-not-lower-case",
ctx: testCtx,
caller: "test",
acct: func() *Account {
a, err := NewAccount(testCtx, "global", "test-auth-method-id", "test-login-name")
require.NoError(t, err)
a.LoginName = "Test-Login-Name"
return a
}(),
wantErrMatch: errors.T(errors.InvalidParameter),
wantErrContains: "login name must be lower case",
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
assert, require := assert.New(t), require.New(t)
err := tc.acct.validate(tc.ctx, tc.caller)
if tc.wantErrMatch != nil {
require.Error(err)
assert.True(errors.Match(tc.wantErrMatch, err))
if tc.wantErrContains != "" {
assert.Contains(err.Error(), tc.wantErrContains)
}
return
}
require.NoError(err)
})
}
}