feature (boundary dev): add support for dev ldap auth method (#3192)
This commit is contained in:
19 files changed
+345
-14
No files matched your search
@@ -97,7 +97,7 @@ require (
|
||||
github.com/hashicorp/go-kms-wrapping/extras/kms/v2 v2.0.0-20221122211539-47c893099f13
|
||||
github.com/hashicorp/go-version v1.3.0
|
||||
github.com/hashicorp/nodeenrollment v0.2.0
|
||||
github.com/jimlambrt/gldap v0.1.2
|
||||
github.com/jimlambrt/gldap v0.1.5
|
||||
github.com/kelseyhightower/envconfig v1.4.0
|
||||
github.com/mikesmitty/edkey v0.0.0-20170222072505-3356ea4e686a
|
||||
golang.org/x/exp v0.0.0-20230224173230-c95f2b4c22f2
|
||||
@@ -108,7 +108,7 @@ require (
|
||||
github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 // indirect
|
||||
github.com/AlecAivazis/survey/v2 v2.2.9 // indirect
|
||||
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
|
||||
github.com/Azure/go-ntlmssp v0.0.0-20211209120228-48547f28849e // indirect
|
||||
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 // indirect
|
||||
github.com/Masterminds/goutils v1.1.1 // indirect
|
||||
github.com/Masterminds/semver/v3 v3.2.0 // indirect
|
||||
github.com/Masterminds/sprig/v3 v3.2.3 // indirect
|
||||
@@ -119,6 +119,7 @@ require (
|
||||
github.com/armon/go-radix v1.0.0 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/bgentry/speakeasy v0.1.0 // indirect
|
||||
github.com/cenkalti/backoff v2.2.1+incompatible // indirect
|
||||
github.com/cenkalti/backoff/v3 v3.0.0 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
||||
github.com/containerd/continuity v0.3.0 // indirect
|
||||
@@ -132,7 +133,7 @@ require (
|
||||
github.com/docker/go-units v0.4.0 // indirect
|
||||
github.com/dvsekhvalnov/jose2go v1.5.0 // indirect
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.4 // indirect
|
||||
github.com/go-ldap/ldap/v3 v3.4.3 // indirect
|
||||
github.com/go-ldap/ldap/v3 v3.4.4 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/golang/glog v1.0.0 // indirect
|
||||
github.com/golang/snappy v0.0.4 // indirect
|
||||
|
||||
@@ -68,6 +68,9 @@ github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZ
|
||||
github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU=
|
||||
github.com/Azure/go-ntlmssp v0.0.0-20211209120228-48547f28849e h1:ZU22z/2YRFLyf/P4ZwUYSdNCWsMEI0VeyrFoI2rAhJQ=
|
||||
github.com/Azure/go-ntlmssp v0.0.0-20211209120228-48547f28849e/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU=
|
||||
github.com/Azure/go-ntlmssp v0.0.0-20220621081337-cb9428e4ac1e/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU=
|
||||
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 h1:mFRzDkZVAjdal+s7s0MwaRv9igoPqLRdzOLzw/8Xvq8=
|
||||
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
github.com/ClickHouse/clickhouse-go v1.4.3/go.mod h1:EaI/sW7Azgz9UATzd5ZdZHRUhHgv5+JMS9NSr2smCJI=
|
||||
@@ -192,6 +195,8 @@ github.com/buger/jsonparser v0.0.0-20180808090653-f4dd9f5a6b44/go.mod h1:bbYlZJ7
|
||||
github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd/go.mod h1:2oa8nejYd4cQ/b0hMIopN0lCRxU0bueqREvZLWFrtK8=
|
||||
github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b/go.mod h1:obH5gd0BsqsP2LwDJ9aOkm/6J86V6lyAXCoQWGw3K50=
|
||||
github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE=
|
||||
github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4=
|
||||
github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
|
||||
github.com/cenkalti/backoff/v3 v3.0.0 h1:ske+9nBpD9qZsTBoF41nW5L+AIuFBKMeze18XQ3eG1c=
|
||||
github.com/cenkalti/backoff/v3 v3.0.0/go.mod h1:cIeZDE3IrqwwJl6VUwCN6trj1oXrTS4rc0ij+ULvLYs=
|
||||
github.com/cenkalti/backoff/v4 v4.0.2/go.mod h1:eEew/i+1Q6OrCDZh3WiXYv3+nJwBASZ8Bog/87DQnVg=
|
||||
@@ -454,6 +459,8 @@ github.com/go-ldap/ldap v3.0.2+incompatible/go.mod h1:qfd9rJvER9Q0/D/Sqn1DfHRoBp
|
||||
github.com/go-ldap/ldap/v3 v3.1.10/go.mod h1:5Zun81jBTabRaI8lzN7E1JjyEl1g6zI6u9pd8luAK4Q=
|
||||
github.com/go-ldap/ldap/v3 v3.4.3 h1:JCKUtJPIcyOuG7ctGabLKMgIlKnGumD/iGjuWeEruDI=
|
||||
github.com/go-ldap/ldap/v3 v3.4.3/go.mod h1:7LdHfVt6iIOESVEe3Bs4Jp2sHEKgDeduAhgM1/f9qmo=
|
||||
github.com/go-ldap/ldap/v3 v3.4.4 h1:qPjipEpt+qDa6SI/h1fzuGWoRUY+qqQ9sOZq67/PYUs=
|
||||
github.com/go-ldap/ldap/v3 v3.4.4/go.mod h1:fe1MsuN5eJJ1FeLT/LEBVdWfNWKh459R7aXgXtJC+aI=
|
||||
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
||||
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
|
||||
@@ -870,6 +877,8 @@ github.com/jhump/protoreflect v1.6.0/go.mod h1:eaTn3RZAmMBcV0fifFvlm6VHNz3wSkYyX
|
||||
github.com/jhump/protoreflect v1.9.1-0.20210817181203-db1a327a393e h1:Yb4fEGk+GtBSNuvy5rs0ZJt/jtopc/z9azQaj3xbies=
|
||||
github.com/jimlambrt/gldap v0.1.2 h1:Xprug+i9WdvdQd8u2bi05JVbllZ+SHhNu4alDY38+Kw=
|
||||
github.com/jimlambrt/gldap v0.1.2/go.mod h1:sKo9VprcJwZRj7OoE7p8YLaPEeNxw3WIEY42NS/iV7E=
|
||||
github.com/jimlambrt/gldap v0.1.5 h1:m7473IVYxbNvcOWpGQ4uq0fLoiSEdC8Zbbv/mujVO8U=
|
||||
github.com/jimlambrt/gldap v0.1.5/go.mod h1:ia/l4Jhm+tdupLvZe7tRCbpv+HyXr1B5QFirsewfWEA=
|
||||
github.com/jinzhu/gorm v1.9.12 h1:Drgk1clyWT9t9ERbzHza6Mj/8FY/CqMyVzOiHviMo6Q=
|
||||
github.com/jinzhu/gorm v1.9.12/go.mod h1:vhTjlKSJUTWNtcbQtrMBFCxy7eXTzeCAzfL5fBZT/Qs=
|
||||
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
|
||||
@@ -1409,6 +1418,7 @@ golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5y
|
||||
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20220331220935-ae2d96664a29/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
|
||||
golang.org/x/crypto v0.6.0 h1:qfktjS5LUO+fFKeJXZ+ikTRijMmljikvG68fpMMruSc=
|
||||
golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
|
||||
|
||||
@@ -123,6 +123,7 @@ func TestRecursiveListingDifferentOutputFields(t *testing.T) {
|
||||
tc := controller.NewTestController(t, &controller.TestControllerOpts{
|
||||
// Disable this to avoid having to deal with sorting them in the test
|
||||
DisableOidcAuthMethodCreation: true,
|
||||
DisableLdapAuthMethodCreation: true,
|
||||
})
|
||||
defer tc.Shutdown()
|
||||
|
||||
|
||||
@@ -45,6 +45,7 @@ type options struct {
|
||||
withAccountAttributeMap map[string]AccountToAttribute
|
||||
withMemberOfGroups string
|
||||
withUrls []string
|
||||
withPublicId string
|
||||
}
|
||||
|
||||
// Option - how options are passed as args
|
||||
@@ -363,3 +364,12 @@ func WithMemberOfGroups(ctx context.Context, groupName ...string) Option {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithPublicId provides an option for passing a public id to the operation
|
||||
func WithPublicId(ctx context.Context, publicId string) Option {
|
||||
const op = "ldap.WithPublicId"
|
||||
return func(o *options) error {
|
||||
o.withPublicId = publicId
|
||||
return nil
|
||||
}
|
||||
}
|
||||
@@ -315,4 +315,13 @@ func Test_getOpts(t *testing.T) {
|
||||
testOpts.withMemberOfGroups = "[\"test\"]"
|
||||
assert.Equal(opts, testOpts)
|
||||
})
|
||||
t.Run("WithPublicId", func(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
opts, err := getOpts(WithPublicId(testCtx, "test"))
|
||||
require.NoError(t, err)
|
||||
testOpts := getDefaultOptions()
|
||||
assert.NotEqual(opts, testOpts)
|
||||
testOpts.withPublicId = "test"
|
||||
assert.Equal(opts, testOpts)
|
||||
})
|
||||
}
|
||||
@@ -6,7 +6,9 @@ package ldap
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/boundary/globals"
|
||||
"github.com/hashicorp/boundary/internal/db"
|
||||
"github.com/hashicorp/boundary/internal/errors"
|
||||
"github.com/hashicorp/boundary/internal/kms"
|
||||
@@ -21,7 +23,7 @@ import (
|
||||
// The AuthMethod's public id and version must be empty (zero values).
|
||||
//
|
||||
// All options are ignored.
|
||||
func (r *Repository) CreateAuthMethod(ctx context.Context, am *AuthMethod, _ ...Option) (*AuthMethod, error) {
|
||||
func (r *Repository) CreateAuthMethod(ctx context.Context, am *AuthMethod, opt ...Option) (*AuthMethod, error) {
|
||||
const op = "ldap.(Repository).CreateAuthMethod"
|
||||
switch {
|
||||
case am == nil:
|
||||
@@ -38,11 +40,22 @@ func (r *Repository) CreateAuthMethod(ctx context.Context, am *AuthMethod, _ ...
|
||||
return nil, errors.New(ctx, errors.InvalidParameter, op, "missing urls (there must be at least one)")
|
||||
}
|
||||
|
||||
var err error
|
||||
am.PublicId, err = newAuthMethodId(ctx)
|
||||
opts, err := getOpts(opt...)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op)
|
||||
}
|
||||
am.PublicId = opts.withPublicId
|
||||
if am.PublicId == "" {
|
||||
id, err := newAuthMethodId(ctx)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(ctx, err, op)
|
||||
}
|
||||
am.PublicId = id
|
||||
} else {
|
||||
if !strings.HasPrefix(am.PublicId, globals.LdapAuthMethodPrefix+"_") {
|
||||
return nil, errors.New(ctx, errors.InvalidParameter, op, "wrong auth method id prefix")
|
||||
}
|
||||
}
|
||||
|
||||
cv, err := am.convertValueObjects(ctx)
|
||||
if err != nil {
|
||||
|
||||
@@ -54,7 +54,7 @@ func TestRepository_authenticate(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
|
||||
testAm := TestAuthMethod(t, testConn, orgDbWrapper, org.PublicId,
|
||||
[]string{fmt.Sprintf("ldaps://127.0.0.1:%d", td.Port())},
|
||||
[]string{fmt.Sprintf("ldaps://%s:%d", td.Host(), td.Port())},
|
||||
WithCertificates(testCtx, tdCerts...),
|
||||
WithDiscoverDn(testCtx),
|
||||
WithEnableGroups(testCtx),
|
||||
@@ -238,7 +238,7 @@ func TestRepository_authenticate(t *testing.T) {
|
||||
t.Run("use-token-groups", func(t *testing.T) {
|
||||
assert, require := assert.New(t), require.New(t)
|
||||
amWithTokenGroups := TestAuthMethod(t, testConn, orgDbWrapper, org.PublicId,
|
||||
[]string{fmt.Sprintf("ldaps://127.0.0.1:%d", td.Port())},
|
||||
[]string{fmt.Sprintf("ldaps://%s:%d", td.Host(), td.Port())},
|
||||
WithCertificates(testCtx, tdCerts...),
|
||||
WithDiscoverDn(testCtx),
|
||||
WithEnableGroups(testCtx),
|
||||
@@ -270,7 +270,7 @@ func TestRepository_authenticate(t *testing.T) {
|
||||
assert, require := assert.New(t), require.New(t)
|
||||
|
||||
amWithNoCerts := TestAuthMethod(t, testConn, orgDbWrapper, org.PublicId,
|
||||
[]string{fmt.Sprintf("ldaps://127.0.0.1:%d", td.Port())},
|
||||
[]string{fmt.Sprintf("ldaps://%s:%d", td.Host(), td.Port())},
|
||||
WithDiscoverDn(testCtx),
|
||||
WithEnableGroups(testCtx),
|
||||
WithUserDn(testCtx, testdirectory.DefaultUserDN),
|
||||
|
||||
@@ -62,7 +62,7 @@ func TestAuthenticate(t *testing.T) {
|
||||
td.SetGroups(groups...)
|
||||
|
||||
testPrimaryAuthMethod := TestAuthMethod(t, testConn, orgDbWrapper, org.PublicId,
|
||||
[]string{fmt.Sprintf("ldaps://127.0.0.1:%d", td.Port())},
|
||||
[]string{fmt.Sprintf("ldaps://%s:%d", td.Host(), td.Port())},
|
||||
WithCertificates(testCtx, tdCerts...),
|
||||
WithDiscoverDn(testCtx),
|
||||
WithEnableGroups(testCtx),
|
||||
@@ -72,7 +72,7 @@ func TestAuthenticate(t *testing.T) {
|
||||
iam.TestSetPrimaryAuthMethod(t, iamRepo, org, testPrimaryAuthMethod.PublicId)
|
||||
|
||||
testNotPrimaryAuthMethod := TestAuthMethod(t, testConn, orgDbWrapper, org.PublicId,
|
||||
[]string{fmt.Sprintf("ldaps://127.0.0.1:%d", td.Port())},
|
||||
[]string{fmt.Sprintf("ldaps://%s:%d", td.Host(), td.Port())},
|
||||
WithCertificates(testCtx, tdCerts...),
|
||||
WithDiscoverDn(testCtx),
|
||||
WithEnableGroups(testCtx),
|
||||
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/boundary/globals"
|
||||
"github.com/hashicorp/boundary/internal/auth/ldap"
|
||||
"github.com/hashicorp/boundary/internal/auth/oidc"
|
||||
"github.com/hashicorp/boundary/internal/cmd/base/internal/docker"
|
||||
"github.com/hashicorp/boundary/internal/db"
|
||||
@@ -24,6 +25,8 @@ import (
|
||||
"github.com/hashicorp/boundary/testing/dbtest"
|
||||
capoidc "github.com/hashicorp/cap/oidc"
|
||||
"github.com/hashicorp/go-multierror"
|
||||
"github.com/jimlambrt/gldap"
|
||||
"github.com/jimlambrt/gldap/testdirectory"
|
||||
)
|
||||
|
||||
func (b *Server) CreateDevDatabase(ctx context.Context, opt ...Option) error {
|
||||
@@ -135,6 +138,12 @@ func (b *Server) CreateDevDatabase(ctx context.Context, opt ...Option) error {
|
||||
}
|
||||
}
|
||||
|
||||
if !opts.withSkipLdapAuthMethodCreation {
|
||||
if err := b.CreateDevLdapAuthMethod(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if opts.withSkipScopesCreation {
|
||||
// now that we have passed all the error cases, reset c to be a noop so the
|
||||
// defer doesn't do anything.
|
||||
@@ -177,6 +186,208 @@ func (b *Server) CreateDevDatabase(ctx context.Context, opt ...Option) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
type ldapSetup struct {
|
||||
testDirectory *testdirectory.Directory
|
||||
authMethod *ldap.AuthMethod
|
||||
}
|
||||
|
||||
func (b *Server) CreateDevLdapAuthMethod(ctx context.Context) error {
|
||||
var (
|
||||
err error
|
||||
port int
|
||||
host string
|
||||
createUnpriv bool
|
||||
)
|
||||
|
||||
if b.DevLdapAuthMethodId == "" {
|
||||
b.DevLdapAuthMethodId, err = db.NewPublicId(globals.LdapAuthMethodPrefix)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error generating initial ldap auth method id: %w", err)
|
||||
}
|
||||
}
|
||||
b.InfoKeys = append(b.InfoKeys, "generated ldap auth method id")
|
||||
b.Info["generated ldap auth method id"] = b.DevLdapAuthMethodId
|
||||
|
||||
switch {
|
||||
case b.DevUnprivilegedLoginName == "",
|
||||
b.DevUnprivilegedPassword == "",
|
||||
b.DevUnprivilegedUserId == "",
|
||||
b.DevUnprivilegedOidcAccountId == "":
|
||||
|
||||
default:
|
||||
createUnpriv = true
|
||||
}
|
||||
|
||||
// Trawl through the listeners and find the api listener so we can use the
|
||||
// same host name/IP
|
||||
{
|
||||
for _, ln := range b.Listeners {
|
||||
purpose := strings.ToLower(ln.Config.Purpose[0])
|
||||
if purpose != "api" {
|
||||
continue
|
||||
}
|
||||
host, _, err = net.SplitHostPort(ln.Config.Address)
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "missing port") {
|
||||
host = ln.Config.Address
|
||||
} else {
|
||||
return fmt.Errorf("error splitting host/port: %w", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
if host == "" {
|
||||
return fmt.Errorf("could not determine address to use for built-in oidc dev listener")
|
||||
}
|
||||
}
|
||||
|
||||
tb := &oidcLogger{}
|
||||
|
||||
port = testdirectory.FreePort(tb)
|
||||
b.DevLdapSetup.testDirectory = testdirectory.Start(tb,
|
||||
testdirectory.WithNoTLS(tb),
|
||||
testdirectory.WithHost(tb, host),
|
||||
testdirectory.WithPort(tb, port),
|
||||
testdirectory.WithDefaults(tb, &testdirectory.Defaults{AllowAnonymousBind: true}),
|
||||
)
|
||||
b.ShutdownFuncs = append(b.ShutdownFuncs, func() error {
|
||||
b.DevLdapSetup.testDirectory.Stop()
|
||||
return nil
|
||||
})
|
||||
|
||||
groups := []*gldap.Entry{
|
||||
testdirectory.NewGroup(tb, "admin", []string{"admin"}),
|
||||
}
|
||||
|
||||
createUserFn := func(userName, passwd string, withMembersOf []string) *gldap.Entry {
|
||||
entryAttrs := map[string][]string{
|
||||
"name": {userName},
|
||||
"email": {fmt.Sprintf("%s@localhost", userName)},
|
||||
"password": {passwd},
|
||||
}
|
||||
if len(withMembersOf) > 0 {
|
||||
entryAttrs["memberOf"] = withMembersOf
|
||||
}
|
||||
DN := fmt.Sprintf("%s=%s,%s", testdirectory.DefaultUserAttr, userName, testdirectory.DefaultUserDN)
|
||||
return gldap.NewEntry(
|
||||
DN,
|
||||
entryAttrs,
|
||||
)
|
||||
}
|
||||
users := []*gldap.Entry{
|
||||
createUserFn(b.DevLoginName, b.DevPassword, []string{"admin"}),
|
||||
}
|
||||
|
||||
if createUnpriv {
|
||||
users = append(users, createUserFn(b.DevUnprivilegedLoginName, b.DevUnprivilegedPassword, nil))
|
||||
}
|
||||
b.DevLdapSetup.testDirectory.SetUsers(users...)
|
||||
b.DevLdapSetup.testDirectory.SetGroups(groups...)
|
||||
|
||||
// Create auth method and link accounts
|
||||
{
|
||||
b.DevLdapSetup.authMethod, err = b.createInitialLdapAuthMethod(ctx, host, port, createUnpriv)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error creating initial ldap auth method: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *Server) createInitialLdapAuthMethod(ctx context.Context, host string, port int, createUnprivAccount bool) (*ldap.AuthMethod, error) {
|
||||
rw := db.New(b.Database)
|
||||
kmsCache, err := kms.New(ctx, rw, rw)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error creating kms cache: %w", err)
|
||||
}
|
||||
if err := kmsCache.AddExternalWrappers(
|
||||
b.Context,
|
||||
kms.WithRootWrapper(b.RootKms),
|
||||
); err != nil {
|
||||
return nil, fmt.Errorf("error adding config keys to kms: %w", err)
|
||||
}
|
||||
ldapRepo, err := ldap.NewRepository(ctx, rw, rw, kmsCache)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error creating ldap repo: %w", err)
|
||||
}
|
||||
|
||||
u, err := url.Parse(fmt.Sprintf("ldap://%s:%d", host, port))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error creating ldap url: %w", err)
|
||||
}
|
||||
authMethod, err := ldap.NewAuthMethod(
|
||||
ctx,
|
||||
scope.Global.String(),
|
||||
ldap.WithUrls(ctx, u),
|
||||
ldap.WithName(ctx, "Generated global scope initial ldap auth method"),
|
||||
ldap.WithDescription(ctx, "Provides initial administrative and unprivileged authentication into Boundary"),
|
||||
ldap.WithDiscoverDn(ctx),
|
||||
ldap.WithUserDn(ctx, testdirectory.DefaultUserDN),
|
||||
ldap.WithGroupDn(ctx, testdirectory.DefaultGroupDN),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error creating new in memory ldap auth method: %w", err)
|
||||
}
|
||||
if b.DevLdapAuthMethodId == "" {
|
||||
b.DevLdapAuthMethodId, err = db.NewPublicId(globals.LdapAuthMethodPrefix)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error generating initial ldap auth method id: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
createdAuthMethod, err := ldapRepo.CreateAuthMethod(ctx, authMethod, ldap.WithPublicId(ctx, b.DevLdapAuthMethodId))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error saving ldap auth method: %w", err)
|
||||
}
|
||||
|
||||
// create dev ldap accounts
|
||||
{
|
||||
createAndLinkAccount := func(loginName, userId, typ string) error {
|
||||
acct, err := ldap.NewAccount(
|
||||
ctx,
|
||||
createdAuthMethod.GetScopeId(),
|
||||
createdAuthMethod.GetPublicId(),
|
||||
loginName,
|
||||
ldap.WithDescription(ctx, fmt.Sprintf("Initial %s ldap account", typ)),
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error generating %s ldap account: %w", typ, err)
|
||||
}
|
||||
acct, err = ldapRepo.CreateAccount(ctx, acct)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error creating %s ldap account: %w", typ, err)
|
||||
}
|
||||
|
||||
// Link accounts to existing user
|
||||
iamRepo, err := iam.NewRepository(rw, rw, kmsCache)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to create iam repo: %w", err)
|
||||
}
|
||||
|
||||
u, _, err := iamRepo.LookupUser(ctx, userId)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error looking up %s user: %w", typ, err)
|
||||
}
|
||||
if _, err = iamRepo.AddUserAccounts(ctx, u.GetPublicId(), u.GetVersion(), []string{acct.GetPublicId()}); err != nil {
|
||||
return fmt.Errorf("error associating initial %s user with account: %w", typ, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := createAndLinkAccount(b.DevLoginName, b.DevUserId, "admin"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if createUnprivAccount {
|
||||
if err := createAndLinkAccount(b.DevUnprivilegedLoginName, b.DevUnprivilegedUserId, "unprivileged"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return createdAuthMethod, nil
|
||||
}
|
||||
|
||||
type oidcSetup struct {
|
||||
clientId string
|
||||
clientSecret oidc.ClientSecret
|
||||
@@ -469,3 +680,7 @@ func (_ *oidcLogger) caller() event.Op {
|
||||
}
|
||||
return caller
|
||||
}
|
||||
|
||||
func (l *oidcLogger) Log(args ...interface{}) {
|
||||
event.WriteSysEvent(l.Ctx, l.caller(), fmt.Sprintf("%v", args...))
|
||||
}
|
||||
@@ -30,6 +30,7 @@ type Options struct {
|
||||
withSkipDatabaseDestruction bool
|
||||
withSkipAuthMethodCreation bool
|
||||
withSkipOidcAuthMethodCreation bool
|
||||
withSkipLdapAuthMethodCreation bool
|
||||
withSkipScopesCreation bool
|
||||
withSkipHostResourcesCreation bool
|
||||
withSkipTargetCreation bool
|
||||
@@ -93,6 +94,14 @@ func WithSkipOidcAuthMethodCreation() Option {
|
||||
}
|
||||
}
|
||||
|
||||
// WithSkipLdapAuthMethodCreation tells the command not to instantiate an LDAP auth
|
||||
// method on first run, useful in some tests.
|
||||
func WithSkipLdapAuthMethodCreation() Option {
|
||||
return func(o *Options) {
|
||||
o.withSkipLdapAuthMethodCreation = true
|
||||
}
|
||||
}
|
||||
|
||||
// WithSkipScopesCreation tells the command not to instantiate scopes on first
|
||||
// run.
|
||||
func WithSkipScopesCreation() Option {
|
||||
|
||||
@@ -85,6 +85,13 @@ func Test_GetOpts(t *testing.T) {
|
||||
testOpts.withSkipOidcAuthMethodCreation = true
|
||||
assert.Equal(opts, testOpts)
|
||||
})
|
||||
t.Run("WithSkipLdapAuthMethodCreation", func(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
opts := getOpts(WithSkipLdapAuthMethodCreation())
|
||||
testOpts := getDefaultOptions()
|
||||
testOpts.withSkipLdapAuthMethodCreation = true
|
||||
assert.Equal(opts, testOpts)
|
||||
})
|
||||
t.Run("WithSkipScopesCreation", func(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
opts := getOpts(WithSkipScopesCreation())
|
||||
|
||||
@@ -101,6 +101,7 @@ type Server struct {
|
||||
|
||||
DevPasswordAuthMethodId string
|
||||
DevOidcAuthMethodId string
|
||||
DevLdapAuthMethodId string
|
||||
DevLoginName string
|
||||
DevPassword string
|
||||
DevUserId string
|
||||
@@ -129,6 +130,7 @@ type Server struct {
|
||||
HostPlugins map[string]plgpb.HostPluginServiceClient
|
||||
|
||||
DevOidcSetup oidcSetup
|
||||
DevLdapSetup ldapSetup
|
||||
|
||||
DatabaseUrl string
|
||||
DatabaseMaxOpenConnections int
|
||||
|
||||
@@ -460,6 +460,7 @@ func (c *Command) Run(args []string) int {
|
||||
}
|
||||
c.DevPasswordAuthMethodId = fmt.Sprintf("%s_%s", globals.PasswordAuthMethodPrefix, c.flagIdSuffix)
|
||||
c.DevOidcAuthMethodId = fmt.Sprintf("%s_%s", globals.OidcAuthMethodPrefix, c.flagIdSuffix)
|
||||
c.DevLdapAuthMethodId = fmt.Sprintf("%s_%s", globals.LdapAuthMethodPrefix, c.flagIdSuffix)
|
||||
c.DevUserId = fmt.Sprintf("%s_%s", globals.UserPrefix, c.flagIdSuffix)
|
||||
c.DevPasswordAccountId = fmt.Sprintf("%s_%s", globals.PasswordAccountPrefix, c.flagIdSuffix)
|
||||
c.DevOidcAccountId = fmt.Sprintf("%s_%s", globals.OidcAccountPrefix, c.flagIdSuffix)
|
||||
|
||||
@@ -82,7 +82,7 @@ func testServerCommand(t *testing.T, opts testServerCommandOpts) *Command {
|
||||
cmd.Server.DevSecondaryTargetId = defaultSecondaryTestTargetId
|
||||
}
|
||||
|
||||
err = cmd.CreateDevDatabase(cmd.Context, base.WithDatabaseTemplate("boundary_template"), base.WithSkipOidcAuthMethodCreation())
|
||||
err = cmd.CreateDevDatabase(cmd.Context, base.WithDatabaseTemplate("boundary_template"), base.WithSkipOidcAuthMethodCreation(), base.WithSkipLdapAuthMethodCreation())
|
||||
if err != nil {
|
||||
if cmd.DevDatabaseCleanupFunc != nil {
|
||||
require.NoError(cmd.DevDatabaseCleanupFunc())
|
||||
|
||||
@@ -790,7 +790,7 @@ func TestAuthenticate_Ldap(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
|
||||
testAm := ldap.TestAuthMethod(t, testConn, orgDbWrapper, o.PublicId,
|
||||
[]string{fmt.Sprintf("ldaps://127.0.0.1:%d", td.Port())},
|
||||
[]string{fmt.Sprintf("ldaps://%s:%d", td.Host(), td.Port())},
|
||||
ldap.WithCertificates(testCtx, tdCerts...),
|
||||
ldap.WithDiscoverDn(testCtx),
|
||||
ldap.WithEnableGroups(testCtx),
|
||||
|
||||
@@ -50,6 +50,7 @@ const (
|
||||
DefaultProjectId = "p_1234567890"
|
||||
DefaultTestPasswordAuthMethodId = "ampw_1234567890"
|
||||
DefaultTestOidcAuthMethodId = "amoidc_1234567890"
|
||||
DefaultTestLdapAuthMethodId = globals.LdapAuthMethodPrefix + "_1234567890"
|
||||
DefaultTestLoginName = "admin"
|
||||
DefaultTestUnprivilegedLoginName = "user"
|
||||
DefaultTestPassword = "passpass"
|
||||
@@ -355,6 +356,9 @@ type TestControllerOpts struct {
|
||||
// DefaultOidcAuthMethodId is the default OIDC method ID to use, if set.
|
||||
DefaultOidcAuthMethodId string
|
||||
|
||||
// DefaultLdapAuthMethodId is the default LDAP method ID to use, if set.
|
||||
DefaultLdapAuthMethodId string
|
||||
|
||||
// DefaultLoginName is the login name used when creating the default admin account.
|
||||
DefaultLoginName string
|
||||
|
||||
@@ -376,6 +380,10 @@ type TestControllerOpts struct {
|
||||
// OIDC listener. Useful for e.g. unix listener tests.
|
||||
DisableOidcAuthMethodCreation bool
|
||||
|
||||
// DisableLdapAuthMethodCreation can be set true to disable the built-in
|
||||
// ldap listener. Useful for e.g. unix listener tests.
|
||||
DisableLdapAuthMethodCreation bool
|
||||
|
||||
// DisableScopesCreation can be set true to disable creating scopes
|
||||
// automatically.
|
||||
DisableScopesCreation bool
|
||||
@@ -564,6 +572,11 @@ func TestControllerConfig(t testing.TB, ctx context.Context, tc *TestController,
|
||||
} else {
|
||||
tc.b.DevOidcAuthMethodId = DefaultTestOidcAuthMethodId
|
||||
}
|
||||
if opts.DefaultLdapAuthMethodId != "" {
|
||||
tc.b.DevLdapAuthMethodId = opts.DefaultLdapAuthMethodId
|
||||
} else {
|
||||
tc.b.DevLdapAuthMethodId = DefaultTestLdapAuthMethodId
|
||||
}
|
||||
if opts.DefaultLoginName != "" {
|
||||
tc.b.DevLoginName = opts.DefaultLoginName
|
||||
} else {
|
||||
@@ -643,6 +656,7 @@ func TestControllerConfig(t testing.TB, ctx context.Context, tc *TestController,
|
||||
suffix := opts.InitialResourcesSuffix
|
||||
tc.b.DevPasswordAuthMethodId = "ampw_" + suffix
|
||||
tc.b.DevOidcAuthMethodId = "amoidc_" + suffix
|
||||
tc.b.DevLdapAuthMethodId = globals.LdapAuthMethodPrefix + "_" + suffix
|
||||
tc.b.DevHostCatalogId = "hcst_" + suffix
|
||||
tc.b.DevHostId = "hst_" + suffix
|
||||
tc.b.DevHostSetId = "hsst_" + suffix
|
||||
@@ -714,6 +728,11 @@ func TestControllerConfig(t testing.TB, ctx context.Context, tc *TestController,
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
if !opts.DisableLdapAuthMethodCreation {
|
||||
if err := tc.b.CreateDevLdapAuthMethod(ctx); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
if !opts.DisableScopesCreation {
|
||||
if _, _, err := tc.b.CreateInitialScopes(ctx); err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -740,6 +759,9 @@ func TestControllerConfig(t testing.TB, ctx context.Context, tc *TestController,
|
||||
if opts.DisableOidcAuthMethodCreation {
|
||||
createOpts = append(createOpts, base.WithSkipOidcAuthMethodCreation())
|
||||
}
|
||||
if opts.DisableOidcAuthMethodCreation {
|
||||
createOpts = append(createOpts, base.WithSkipLdapAuthMethodCreation())
|
||||
}
|
||||
if !opts.DisableDatabaseTemplate {
|
||||
createOpts = append(createOpts, base.WithDatabaseTemplate("boundary_template"))
|
||||
}
|
||||
@@ -764,6 +786,7 @@ func (tc *TestController) AddClusterControllerMember(t testing.TB, opts *TestCon
|
||||
DatabaseUrl: tc.c.conf.DatabaseUrl,
|
||||
DefaultPasswordAuthMethodId: tc.c.conf.DevPasswordAuthMethodId,
|
||||
DefaultOidcAuthMethodId: tc.c.conf.DevOidcAuthMethodId,
|
||||
DefaultLdapAuthMethodId: tc.c.conf.DevLdapAuthMethodId,
|
||||
RootKms: tc.c.conf.RootKms,
|
||||
WorkerAuthKms: tc.c.conf.WorkerAuthKms,
|
||||
DownstreamWorkerAuthKms: tc.c.conf.DownstreamWorkerAuthKms,
|
||||
|
||||
@@ -5,11 +5,16 @@ package controller
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"io"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/boundary/globals"
|
||||
"github.com/hashicorp/boundary/internal/auth/ldap"
|
||||
"github.com/hashicorp/boundary/internal/db"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func Test_TestController(t *testing.T) {
|
||||
@@ -65,4 +70,19 @@ func Test_TestController(t *testing.T) {
|
||||
defer tc.Shutdown()
|
||||
}))
|
||||
})
|
||||
t.Run("set-default-ldap-auth-method-id", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
assert, require := assert.New(t), require.New(t)
|
||||
testCtx := context.Background()
|
||||
testLdapAuthMethodId := globals.LdapAuthMethodPrefix + "_0123456789"
|
||||
tc := NewTestController(t, &TestControllerOpts{DefaultLdapAuthMethodId: testLdapAuthMethodId})
|
||||
defer tc.Shutdown()
|
||||
|
||||
testRw := db.New(tc.DbConn())
|
||||
testLdapRepo, err := ldap.NewRepository(testCtx, testRw, testRw, tc.c.kms)
|
||||
require.NoError(err)
|
||||
got, err := testLdapRepo.LookupAuthMethod(testCtx, testLdapAuthMethodId)
|
||||
require.NoError(err)
|
||||
assert.Equal(testLdapAuthMethodId, got.GetPublicId())
|
||||
})
|
||||
}
|
||||
@@ -36,7 +36,7 @@ func TestListAnonymousRecursing(t *testing.T) {
|
||||
l, err := amClient.List(tc.Context(), scope.Global.String(), amapi.WithRecursive(true))
|
||||
require.NoError(err)
|
||||
require.NotNil(l)
|
||||
require.Len(l.GetItems(), 3)
|
||||
require.Len(l.GetItems(), 4)
|
||||
|
||||
// Originally we also expect to see all three as anon user
|
||||
amClient.ApiClient().SetToken("")
|
||||
|
||||
@@ -24,6 +24,7 @@ type option struct {
|
||||
setDisableDatabaseDestruction bool
|
||||
setDefaultPasswordAuthMethodId bool
|
||||
setDefaultOidcAuthMethodId bool
|
||||
setDefaultLdapAuthMethodId bool
|
||||
setDefaultLoginName bool
|
||||
setDefaultPassword bool
|
||||
setRootKms bool
|
||||
@@ -51,6 +52,7 @@ func getOpts(opt ...Option) (*controller.TestControllerOpts, error) {
|
||||
var setDbParams bool
|
||||
if opts.setDefaultPasswordAuthMethodId ||
|
||||
opts.setDefaultOidcAuthMethodId ||
|
||||
opts.setDefaultLdapAuthMethodId ||
|
||||
opts.setDefaultLoginName ||
|
||||
opts.setDefaultPassword {
|
||||
setDbParams = true
|
||||
@@ -147,6 +149,14 @@ func WithDefaultOidcAuthMethodId(id string) Option {
|
||||
}
|
||||
}
|
||||
|
||||
func WithDefaultLdapAuthMethodId(id string) Option {
|
||||
return func(c *option) error {
|
||||
c.setDefaultLdapAuthMethodId = true
|
||||
c.tcOptions.DefaultLdapAuthMethodId = id
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func WithDefaultLoginName(ln string) Option {
|
||||
return func(c *option) error {
|
||||
c.setDefaultLoginName = true
|
||||
|
||||
Reference in new issue
Block a user