diff --git a/.github/workflows/merge_group,pull_request.all.lint.yaml b/.github/workflows/merge_group,pull_request.all.lint.yaml index 086b816b..9c7bbfb6 100644 --- a/.github/workflows/merge_group,pull_request.all.lint.yaml +++ b/.github/workflows/merge_group,pull_request.all.lint.yaml @@ -14,5 +14,12 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 # v5.0.0 + with: + go-version: 1.21 + - uses: golangci/golangci-lint-action@3cfe3a4abbb849e10058ce4af15d205b6da42804 # v4.0.0 + with: + version: latest + args: --help # Just force the action to download the latest version of golangci-lint + - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - uses: trunk-io/trunk-action@97ecd21fe6c743bf7a606791584b683a7995c70e # v1.1.9 diff --git a/.github/workflows/merge_group,pull_request.go.test.yaml b/.github/workflows/merge_group,pull_request.go.test.yaml index 40eacec6..f8308273 100644 --- a/.github/workflows/merge_group,pull_request.go.test.yaml +++ b/.github/workflows/merge_group,pull_request.go.test.yaml @@ -7,6 +7,8 @@ on: - go.mod - go.sum - .github/workflows/pull_request,push.go.test.yaml + push: + branches: [main] permissions: read-all diff --git a/Dockerfile b/Dockerfile index d9cd9ec7..eaebf2ea 100644 --- a/Dockerfile +++ b/Dockerfile @@ -28,7 +28,7 @@ RUN set -eux; \ # └───────────────────────────────────────────────────────────────────────────┘ FROM docker.io/library/alpine:3.19.1 -# renovate: datasource=github-tags depName=xunleii/yaldap versioning=semver +# renovate: datasource=github-tags depName=chezmoi-sh/yaldap versioning=semver ARG YALDAP_VERSION="v0.1.2" # renovate: datasource=repology depName=alpine_3_19/ca-certificates versioning=loose @@ -47,7 +47,7 @@ ENV PATH=/opt/yaldap:${PATH} USER yaldap WORKDIR /opt/yaldap -ENTRYPOINT [ "yaldap" ] +ENTRYPOINT yaldap EXPOSE 389 HEALTHCHECK --interval=30s --timeout=30s --start-period=5s --retries=3 \ @@ -59,10 +59,10 @@ LABEL \ org.opencontainers.image.authors="xunleii " \ org.opencontainers.image.created="01/01/1970T00:00:00.000" \ org.opencontainers.image.description="Your identity, your rules." \ - org.opencontainers.image.documentation="https://github.com/xunleii/yaldap" \ + org.opencontainers.image.documentation="https://github.com/chezmoi-sh/yaldap" \ org.opencontainers.image.licenses="AGPL-3.0" \ org.opencontainers.image.revision="" \ org.opencontainers.image.source="" \ org.opencontainers.image.title="yaldap" \ - org.opencontainers.image.url="https://github.com/xunleii/yaldap" \ + org.opencontainers.image.url="https://github.com/chezmoi-sh/yaldap" \ org.opencontainers.image.version=${YALDAP_VERSION} diff --git a/README.md b/README.md index ca5c29af..af51af12 100644 --- a/README.md +++ b/README.md @@ -2,11 +2,11 @@ yaLDAP is an easy-to-use LDAP server using YAML file as directory definition. -![GitHub go.mod Go version](https://img.shields.io/github/go-mod/go-version/xunleii/yaldap) -[![Go](https://github.com/xunleii/yaldap/actions/workflows/pull_request,push.go.test.yaml/badge.svg)](https://github.com/xunleii/yaldap/actions/workflows/pull_request,push.go.test.yaml) -[![CodeQL](https://github.com/xunleii/yaldap/actions/workflows/pull_request,push,schedule.codeql.yaml/badge.svg)](https://github.com/xunleii/yaldap/actions/workflows/pull_request,push,schedule.codeql.yaml) -[![codecov](https://codecov.io/gh/xunleii/yaldap/branch/main/graph/badge.svg?token=20J4XPYH1H)](https://codecov.io/gh/xunleii/yaldap) -[![Go Report Card](https://goreportcard.com/badge/github.com/xunleii/yaldap)](https://goreportcard.com/report/github.com/xunleii/yaldap) +![GitHub go.mod Go version](https://img.shields.io/github/go-mod/go-version/chezmoi-sh/yaldap) +[![Test code (Go)](https://github.com/chezmoi-sh/yaldap/actions/workflows/merge_group,pull_request.go.test.yaml/badge.svg?event=push)](https://github.com/chezmoi-sh/yaldap/actions/workflows/merge_group,pull_request.go.test.yaml) +[![CodeQL](https://github.com/chezmoi-sh/yaldap/actions/workflows/pull_request,push,schedule.codeql.yaml/badge.svg)](https://github.com/chezmoi-sh/yaldap/actions/workflows/pull_request,push,schedule.codeql.yaml) +[![codecov](https://codecov.io/gh/chezmoi-sh/yaldap/branch/main/graph/badge.svg?token=20J4XPYH1H)](https://codecov.io/gh/chezmoi-sh/yaldap) +[![Go Report Card](https://goreportcard.com/badge/github.com/chezmoi-sh/yaldap)](https://goreportcard.com/report/github.com/chezmoi-sh/yaldap) _Sometimes, we just need a simple LDAP compatible server to store user/group information and other information. For this purpose, many simple LDAP server exists and manage user/group in a better way than yaLDAP. However, no one can @@ -103,4 +103,22 @@ dc:org: #dn: dc=org userPassword: eve ``` +### Hashed passwords + +In order to avoid storing clear text passwords in the YAML file, yaLDAP supports hashed passwords. +Currently, only `argon2`, `bcrypt`, `pbkdf2` and `scrypt` are supported. + +#### How to hash a password + +```sh +echo -n "" | yaldap tools hash [] - +``` + +For example, to hash a password using `bcrypt` and a cost of 10: + +```sh +$ echo -n "password" | yaldap tools hash bcrypt --rounds 10 - +$bcrypt$v=0$r=10$$243261243130247935525748646434736f52794a2e474f3162714856755331496c616e54384b4d387346494a746c6b3141776e7a6c36736f377a6471 +``` + ## Contribution diff --git a/cmd/yaldap/main.go b/cmd/yaldap/main.go index 2009a0d9..8559294e 100644 --- a/cmd/yaldap/main.go +++ b/cmd/yaldap/main.go @@ -4,26 +4,38 @@ import ( "os" "github.com/alecthomas/kong" - "github.com/xunleii/yaldap/pkg/cmd" + "github.com/chezmoi-sh/yaldap/pkg/cmd" ) +// yaLDAP represents the main application struct, interpretted by kong. +type yaLDAP struct { + cmd.Base `embed:""` + + Server cmd.Server `cmd:"" name:"run" help:"Start the yaLDAP server"` + Tools cmd.Tools `cmd:"" name:"tools" help:"yaLDAP utilities"` +} + func main() { - var server cmd.Server + var yaldap yaLDAP + + yaldap.Server.Base = &yaldap.Base + yaldap.Tools.Base = &yaldap.Base + // Parse command-line arguments using kong. ctx := kong.Parse( - &server, + &yaldap, kong.Name("yaldap"), kong.Description(` yaLDAP is an LDAP server that is backed by different read-only data sources, such as YAML files. It is intended to be lightweight, secure and easy to configure. - See https://github.com/xunleii/yaldap for more information. + See https://github.com/chezmoi-sh/yaldap for more information. `), kong.UsageOnError(), ) if err := ctx.Run(); err != nil { - server.Logger().Error(err.Error()) + yaldap.Logger().Error(err.Error()) os.Exit(1) } } diff --git a/go.mod b/go.mod index 5c6076a7..efa7e9a6 100644 --- a/go.mod +++ b/go.mod @@ -1,4 +1,4 @@ -module github.com/xunleii/yaldap +module github.com/chezmoi-sh/yaldap go 1.21 @@ -14,6 +14,7 @@ require ( github.com/prometheus/common v0.47.0 github.com/puzpuzpuz/xsync/v3 v3.0.2 github.com/stretchr/testify v1.8.4 + go.pact.im/x/phcformat v0.0.6 golang.org/x/exp v0.0.0-20240205201215-2c58cdc269a3 golang.org/x/sync v0.3.0 gopkg.in/yaml.v3 v3.0.1 @@ -34,11 +35,13 @@ require ( github.com/prometheus/procfs v0.12.0 // indirect github.com/shopspring/decimal v1.2.0 // indirect github.com/spf13/cast v1.3.1 // indirect + go.pact.im/x/option v0.0.6 // indirect google.golang.org/protobuf v1.32.0 // indirect ) require ( github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 // indirect + github.com/aldy505/phc-crypto v1.2.0 github.com/davecgh/go-spew v1.1.1 // indirect github.com/fatih/color v1.16.0 // indirect github.com/google/uuid v1.6.0 // indirect diff --git a/go.sum b/go.sum index 17fcdee1..31844032 100644 --- a/go.sum +++ b/go.sum @@ -6,6 +6,8 @@ github.com/Masterminds/semver/v3 v3.2.0 h1:3MEsd0SM6jqZojhjLWWeBY+Kcjy9i6MQAeY7Y github.com/Masterminds/semver/v3 v3.2.0/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= github.com/Masterminds/sprig/v3 v3.2.3 h1:eL2fZNezLomi0uOLqjQoN6BfsDD+fyLtgbJMAj9n6YA= github.com/Masterminds/sprig/v3 v3.2.3/go.mod h1:rXcFaZ2zZbLRJv/xSysmlgIM1u11eBaRMhvYXJNkGuM= +github.com/aldy505/phc-crypto v1.2.0 h1:IbovWYUDUqqqI1wva9U2+sMJ4HABK5L32pScoJw2tc4= +github.com/aldy505/phc-crypto v1.2.0/go.mod h1:tpy0uE4Cqb7I6LUO7BLihCdX56MxvZylsZ1LIzuRw0M= github.com/alecthomas/assert/v2 v2.1.0 h1:tbredtNcQnoSd3QBhQWI7QZ3XHOVkw1Moklp2ojoH/0= github.com/alecthomas/assert/v2 v2.1.0/go.mod h1:b/+1DI2Q6NckYi+3mXyH3wFb8qG37K/DuK80n7WefXA= github.com/alecthomas/kong v0.8.1 h1:acZdn3m4lLRobeh3Zi2S2EpnXTd1mOL6U7xVml+vfkY= @@ -100,6 +102,10 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +go.pact.im/x/option v0.0.6 h1:8lam/SPqUxr/AhQV8S6pNs+2npB+Fcwgmfwqea6vrik= +go.pact.im/x/option v0.0.6/go.mod h1:mpzKc/UIz4m3X4/47trnsCXP15YoUJsAcSZTiqTkmqA= +go.pact.im/x/phcformat v0.0.6 h1:1Vww+WEVbTTxyr+SLSsHaI3zCYwOe7KmY/8RRZ4RVgU= +go.pact.im/x/phcformat v0.0.6/go.mod h1:q+HHj0v7P1fA3WObC9Ts7YvAm21Jmviw+Kep/DlJjRc= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= diff --git a/internal/ldap/auth/auth.go b/internal/ldap/auth/auth.go index d68b0acd..a3838443 100644 --- a/internal/ldap/auth/auth.go +++ b/internal/ldap/auth/auth.go @@ -6,8 +6,8 @@ import ( "sync" "time" + ldap "github.com/chezmoi-sh/yaldap/pkg/ldap/directory" xsync "github.com/puzpuzpuz/xsync/v3" - ldap "github.com/xunleii/yaldap/pkg/ldap/directory" ) type ( diff --git a/internal/ldap/auth/auth_test.go b/internal/ldap/auth/auth_test.go index 53651b20..4baf6a90 100644 --- a/internal/ldap/auth/auth_test.go +++ b/internal/ldap/auth/auth_test.go @@ -6,10 +6,10 @@ import ( "testing" "time" + ldap "github.com/chezmoi-sh/yaldap/pkg/ldap/directory" "github.com/jimlambrt/gldap" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - ldap "github.com/xunleii/yaldap/pkg/ldap/directory" ) type mockLDAPObject map[string][]string @@ -17,7 +17,7 @@ type mockLDAPObject map[string][]string func (o mockLDAPObject) DN() string { return "" } func (o mockLDAPObject) Attributes() ldap.Attributes { return ldap.Attributes(o) } func (o mockLDAPObject) Search(gldap.Scope, string) ([]ldap.Object, error) { return nil, nil } -func (o mockLDAPObject) Bind(string) bool { return false } +func (o mockLDAPObject) Bind(string) (bool, error) { return false, nil } func (o mockLDAPObject) CanSearchOn(string) bool { return true } func TestSessions_NewSession(t *testing.T) { diff --git a/pkg/cmd/common.go b/pkg/cmd/common.go index 455a9871..5a6e9907 100644 --- a/pkg/cmd/common.go +++ b/pkg/cmd/common.go @@ -8,8 +8,8 @@ import ( "slices" "github.com/alecthomas/kong" + "github.com/chezmoi-sh/yaldap/pkg/utils" "github.com/prometheus/common/version" - "github.com/xunleii/yaldap/pkg/utils" ) type ( diff --git a/pkg/cmd/common_test.go b/pkg/cmd/common_test.go index 994019ad..9d05d766 100644 --- a/pkg/cmd/common_test.go +++ b/pkg/cmd/common_test.go @@ -6,9 +6,9 @@ import ( "testing" "github.com/alecthomas/kong" + "github.com/chezmoi-sh/yaldap/pkg/cmd" + "github.com/chezmoi-sh/yaldap/pkg/utils" "github.com/stretchr/testify/assert" - "github.com/xunleii/yaldap/pkg/cmd" - "github.com/xunleii/yaldap/pkg/utils" ) func TestLogger_Format(t *testing.T) { diff --git a/pkg/cmd/server.go b/pkg/cmd/server.go index a93c8ef3..6cd9929a 100644 --- a/pkg/cmd/server.go +++ b/pkg/cmd/server.go @@ -11,17 +11,17 @@ import ( "time" "github.com/alecthomas/kong" + "github.com/chezmoi-sh/yaldap/internal/ldap/auth" + "github.com/chezmoi-sh/yaldap/pkg/ldap" + "github.com/chezmoi-sh/yaldap/pkg/ldap/directory" + yamldir "github.com/chezmoi-sh/yaldap/pkg/ldap/directory/yaml" + "github.com/chezmoi-sh/yaldap/pkg/utils" "github.com/jimlambrt/gldap" - "github.com/xunleii/yaldap/internal/ldap/auth" - "github.com/xunleii/yaldap/pkg/ldap" - "github.com/xunleii/yaldap/pkg/ldap/directory" - yamldir "github.com/xunleii/yaldap/pkg/ldap/directory/yaml" - "github.com/xunleii/yaldap/pkg/utils" "golang.org/x/sync/errgroup" ) type Server struct { - Base `embed:""` + *Base `kong:"-"` ListenAddr string `name:"listen-address" help:"Address to listen on" default:":389"` diff --git a/pkg/cmd/server_test.go b/pkg/cmd/server_test.go index a6b59f5e..26bd22fe 100644 --- a/pkg/cmd/server_test.go +++ b/pkg/cmd/server_test.go @@ -3,7 +3,6 @@ package cmd import ( "crypto/tls" "fmt" - "log/slog" "net" "os" "testing" @@ -18,9 +17,10 @@ import ( func TestServer_Defaults(t *testing.T) { var actual, expected Server + actual.Base = &Base{} + expected.Base = &Base{} + expected.ListenAddr = ":389" - expected.Base.Log.Format = "json" - expected.Base.Log.Level = LogLevel(slog.LevelInfo) expected.Backend.Name = "yaml" expected.Backend.URL = "file://../ldap/directory/yaml/fixtures/basic.yaml" //nolint:goconst expected.SessionTTL = 168 * time.Hour @@ -34,6 +34,7 @@ func TestServer_Defaults(t *testing.T) { func TestServer_YAML_Simple(t *testing.T) { server := Server{ListenAddr: fmt.Sprintf("localhost:%d", freePort(t))} + server.Base = &Base{} server.Base.Log.Format = "test" server.Backend.Name = "yaml" server.Backend.URL = "file://../ldap/directory/yaml/fixtures/basic.yaml" @@ -63,6 +64,7 @@ func TestServer_YAML_WithTLS(t *testing.T) { require.NoError(t, err) server := Server{ListenAddr: fmt.Sprintf("localhost:%d", freePort(t))} + server.Base = &Base{} server.Base.Log.Format = "test" server.Backend.Name = "yaml" server.Backend.URL = "file://../ldap/directory/yaml/fixtures/basic.yaml" @@ -98,6 +100,7 @@ func TestServer_YAML_WithMutualTLS(t *testing.T) { require.NoError(t, err) server := Server{ListenAddr: fmt.Sprintf("localhost:%d", freePort(t))} + server.Base = &Base{} server.Base.Log.Format = "test" server.Backend.Name = "yaml" server.Backend.URL = "file://../ldap/directory/yaml/fixtures/basic.yaml" diff --git a/pkg/cmd/tool.go b/pkg/cmd/tool.go new file mode 100644 index 00000000..88780cfb --- /dev/null +++ b/pkg/cmd/tool.go @@ -0,0 +1,156 @@ +package cmd + +import ( + allow_fmt "fmt" + "io" + "os" + + "github.com/aldy505/phc-crypto/argon2" + "github.com/aldy505/phc-crypto/bcrypt" + "github.com/aldy505/phc-crypto/pbkdf2" + "github.com/aldy505/phc-crypto/scrypt" +) + +type ( + Tools struct { + *Base `kong:"-"` + + Hash Hash `cmd:"" help:"Hashing tool"` + } + + Hash struct { + Argon2 Argon2 `cmd:"" name:"argon2" help:"Argon2 hashing algorithm"` + Scrypt Scrypt `cmd:"" name:"scrypt" help:"Scrypt hashing algorithm"` + Bcrypt Bcrypt `cmd:"" name:"bcrypt" help:"Bcrypt hashing algorithm"` + PBKDF2 PBKDF2 `cmd:"" name:"pbkdf2" help:"PBKDF2 hashing algorithm"` + } + + HashCommon struct { + Password string `arg:"" name:"password" help:"Password to hash" required:""` + + // This is a workaround to allow fmt.Println to be mocked in tests. + writer io.Writer `kong:"-"` + } + + Argon2 struct { + HashCommon + + Variant string `name:"variant" enum:"i, id" help:"Variant of argon2 to use" default:"id"` + Iterations int `name:"iterations" help:"Number of iterations to use" default:"10"` + Memory int `name:"memory" help:"Memory to use in kibibytes" default:"64"` + Parallelism int `name:"parallelism" help:" Degree of parallelism to use" default:"1"` + } + + Scrypt struct { + HashCommon + + Blocksize int `name:"block-size" help:"Amount of memory to use in kibibytes" default:"8"` + Cost int `name:"cost" help:"CPU/memory cost of the scrypt algorithm" default:"16"` + Parallelism int `name:"parallelism" help:"Degree of parallelism to use" default:"1"` + } + + Bcrypt struct { + HashCommon + + Rounds int `name:"rounds" help:"Number of iterations to use as 2^rounds" default:"8"` + } + + PBKDF2 struct { + HashCommon + + Iterations int `name:"iterations" help:"Number of iterations to use" default:"10"` + Digest string `name:"digest" enum:"md5, sha1, sha256, sha224, sha384, sha512" help:"Digest to use when applying the key derivation function" default:"sha256"` + } +) + +func (h *HashCommon) prepare() { + // Handle password from stdin (- argument) + if h.Password == "-" { + allow_fmt.Scanln(&h.Password) + } + + // Set writer to stdout if not set (should be set in tests only) + if h.writer == nil { + h.writer = os.Stdout + } +} + +func (a *Argon2) Run() error { + a.HashCommon.prepare() + config := argon2.Config{ + Memory: a.Memory * 1024, + Parallelism: a.Parallelism, + Time: a.Iterations, + } + switch a.Variant { + case "i": + config.Variant = argon2.I + case "id": + config.Variant = argon2.ID + } + + hash, err := argon2.Hash(a.Password, config) + if err != nil { + return err + } + + allow_fmt.Fprintln(a.writer, hash) + return nil +} + +func (s *Scrypt) Run() error { + s.HashCommon.prepare() + config := scrypt.Config{ + Cost: s.Cost, + Parallelism: s.Parallelism, + Rounds: s.Blocksize, + } + + hash, err := scrypt.Hash(s.Password, config) + if err != nil { + return err + } + + allow_fmt.Fprintln(s.writer, hash) + return nil +} + +func (b *Bcrypt) Run() error { + b.HashCommon.prepare() + config := bcrypt.Config{Rounds: b.Rounds} + + hash, err := bcrypt.Hash(b.Password, config) + if err != nil { + return err + } + + allow_fmt.Fprintln(b.writer, hash) + return nil +} + +func (p *PBKDF2) Run() error { + p.HashCommon.prepare() + config := pbkdf2.Config{Rounds: p.Iterations} + switch p.Digest { + case "md5": + config.HashFunc = pbkdf2.MD5 + case "sha1": + config.HashFunc = pbkdf2.SHA1 + case "sha256": + config.HashFunc = pbkdf2.SHA256 + case "sha224": + config.HashFunc = pbkdf2.SHA224 + case "sha384": + config.HashFunc = pbkdf2.SHA384 + case "sha512": + config.HashFunc = pbkdf2.SHA512 + } + + hash, err := pbkdf2.Hash(p.Password, config) + if err != nil { + return err + } + + allow_fmt.Fprintln(p.writer, hash) + return nil +} diff --git a/pkg/cmd/tool_test.go b/pkg/cmd/tool_test.go new file mode 100644 index 00000000..ee3627db --- /dev/null +++ b/pkg/cmd/tool_test.go @@ -0,0 +1,90 @@ +package cmd + +import ( + "bytes" + "strings" + "testing" + + "github.com/aldy505/phc-crypto/argon2" + "github.com/aldy505/phc-crypto/bcrypt" + "github.com/aldy505/phc-crypto/pbkdf2" + "github.com/aldy505/phc-crypto/scrypt" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestArgon2(t *testing.T) { + buff := bytes.NewBuffer(nil) + tool := Argon2{ + HashCommon: HashCommon{ + Password: "password", + writer: buff, + }, + Iterations: 1, + } + + err := tool.Run() + require.NoError(t, err) + + hash := strings.TrimSpace(buff.String()) + valid, err := argon2.Verify(hash, "password") + require.NoError(t, err) + assert.True(t, valid) +} + +func TestScrypt(t *testing.T) { + buff := bytes.NewBuffer(nil) + tool := Scrypt{ + HashCommon: HashCommon{ + Password: "password", + writer: buff, + }, + Cost: 2, + } + + err := tool.Run() + require.NoError(t, err) + + hash := strings.TrimSpace(buff.String()) + valid, err := scrypt.Verify(hash, "password") + require.NoError(t, err) + assert.True(t, valid) +} + +func TestBcrypt(t *testing.T) { + buff := bytes.NewBuffer(nil) + tool := Bcrypt{ + HashCommon: HashCommon{ + Password: "password", + writer: buff, + }, + Rounds: 1, + } + + err := tool.Run() + require.NoError(t, err) + + hash := strings.TrimSpace(buff.String()) + valid, err := bcrypt.Verify(hash, "password") + require.NoError(t, err) + assert.True(t, valid) +} + +func TestPBKDF2(t *testing.T) { + buff := bytes.NewBuffer(nil) + tool := PBKDF2{ + HashCommon: HashCommon{ + Password: "password", + writer: buff, + }, + Iterations: 1, + } + + err := tool.Run() + require.NoError(t, err) + + hash := strings.TrimSpace(buff.String()) + valid, err := pbkdf2.Verify(hash, "password") + require.NoError(t, err) + assert.True(t, valid) +} diff --git a/pkg/ldap/directory/common/types.go b/pkg/ldap/directory/common/types.go index b413d611..6e7a12d3 100644 --- a/pkg/ldap/directory/common/types.go +++ b/pkg/ldap/directory/common/types.go @@ -5,12 +5,17 @@ import ( "sort" "strings" + "github.com/aldy505/phc-crypto/argon2" + "github.com/aldy505/phc-crypto/bcrypt" + "github.com/aldy505/phc-crypto/pbkdf2" + "github.com/aldy505/phc-crypto/scrypt" + ldap "github.com/chezmoi-sh/yaldap/pkg/ldap/directory" + "github.com/chezmoi-sh/yaldap/pkg/ldap/filters" ber "github.com/go-asn1-ber/asn1-ber" goldap "github.com/go-ldap/ldap/v3" "github.com/jimlambrt/gldap" "github.com/moznion/go-optional" - ldap "github.com/xunleii/yaldap/pkg/ldap/directory" - "github.com/xunleii/yaldap/pkg/ldap/filters" + "go.pact.im/x/phcformat" ) type ( @@ -69,13 +74,33 @@ func (obj Object) Search(scope gldap.Scope, filter string) ([]ldap.Object, error } // Bind returns true if the current object is able to authenticate and the password is correct. -// It returns false if the password is wrong and optional.None if it cannot be authenticated. -func (obj Object) Bind(password string) bool { +// It returns false if the password is wrong or not set. +func (obj Object) Bind(password string) (bool, error) { if obj.BindPasswords.IsNone() { - return false + return false, nil + } + bindPassword := obj.BindPasswords.Unwrap() + + phcInfo, ok := phcformat.Parse(bindPassword) + if !ok { + // NOTE: if the password is not a valid PHC string, we assume it's a plain text password + // and we compare it directly with the stored password. + // NOT RECOMMENDED + return bindPassword == password, nil } - return obj.BindPasswords.Unwrap() == password + switch { + case strings.HasPrefix(phcInfo.ID, "argon2"): + return argon2.Verify(bindPassword, password) + case phcInfo.ID == "bcrypt": + return bcrypt.Verify(bindPassword, password) + case strings.HasPrefix(phcInfo.ID, "pbkdf2"): + return pbkdf2.Verify(bindPassword, password) + case phcInfo.ID == "scrypt": + return scrypt.Verify(bindPassword, password) + default: + return false, fmt.Errorf("unsupported PHC algorithm: %s", phcInfo.ID) + } } // CanSearchOn returns true if the current object is able to perform a search on the given DN. diff --git a/pkg/ldap/directory/common/types_test.go b/pkg/ldap/directory/common/types_test.go index 861b8770..ac381b15 100644 --- a/pkg/ldap/directory/common/types_test.go +++ b/pkg/ldap/directory/common/types_test.go @@ -1,11 +1,12 @@ +//nolint:goconst package common import ( "testing" + ldap "github.com/chezmoi-sh/yaldap/pkg/ldap/directory" "github.com/jimlambrt/gldap" "github.com/stretchr/testify/assert" - ldap "github.com/xunleii/yaldap/pkg/ldap/directory" ) func TestObjectDN(t *testing.T) { @@ -218,24 +219,80 @@ func TestObjectBind(t *testing.T) { // Test with correct password password := "password123" expectedResult := true - actualResult := obj.Bind(password) + actualResult, err := obj.Bind(password) + assert.NoError(t, err) assert.Equal(t, expectedResult, actualResult) // Test with incorrect password password = "wrongpassword" expectedResult = false - actualResult = obj.Bind(password) + actualResult, err = obj.Bind(password) + assert.NoError(t, err) assert.Equal(t, expectedResult, actualResult) // Test with no bind passwords obj.BindPasswords = nil password = "password123" expectedResult = false - actualResult = obj.Bind(password) + actualResult, err = obj.Bind(password) + assert.NoError(t, err) assert.Equal(t, expectedResult, actualResult) + + // Test with unsupported PHC algorithm + obj.BindPasswords = []string{"$unknown$v=0$r=0$salt$hash"} + password = "password123" + expectedResult = false + actualResult, err = obj.Bind(password) + + assert.EqualError(t, err, "unsupported PHC algorithm: unknown") + assert.Equal(t, expectedResult, actualResult) +} + +func TestObjectBindWithHashedPassword(t *testing.T) { + tests := []struct { + Name string + HashedPassword string + ExpectedResult bool + }{ + { + Name: "Test with argon2id", + HashedPassword: "$argon2id$v=19$m=65536,t=10,p=1$fc833b1da8729366224df547834badc7914906b7add02b6e709e1ffe4de56ed3$cbfe0dce36ac1d0db8d869b60cb0f264ea44b2e50d1376cfc4ff3412d73e38c7eebc9d07674b9337297edfe2f64877769b09bbb7f80ef974dc7263eb48002b9f", // yaldap_utils hash argon2 password123 + ExpectedResult: true, + }, + { + Name: "Test with bcrypt", + HashedPassword: "$bcrypt$v=0$r=8$$243261243038244e4e78745643644d4f7a33442f6a37534e72345a7075586b772f416d58456a2f6e544856706f784b45656446547570332f41474743", // yaldap_utils hash bcrypt password123 + ExpectedResult: true, + }, + { + Name: "Test with pbkdf2", + HashedPassword: "$pbkdf2sha256$v=0$i=10$67447629899eeb0d6fdb1e4d784a8a78$b467bb3ec3a9c4d46cf0fabb8207f4da139022836168fd29f1258f85f3c4bd6f", // yaldap_utils hash pbkdf2 password123 + ExpectedResult: true, + }, + { + Name: "Test with scrypt", + HashedPassword: "$scrypt$v=0$ln=16,r=8,p=1$fca08f0f4120c4fd2b2d5e36a114d4fb$fd3db2ea0f59d547d0687ef64c7b2afcdc6f98cbd416442f3ccda41f62a66348", // yaldap_utils hash scrypt password123 + ExpectedResult: true, + }, + } + + for _, tt := range tests { + t.Run(tt.Name, func(t *testing.T) { + obj := Object{ + ImplObject: ImplObject{ + BindPasswords: []string{tt.HashedPassword}, + }, + } + + actualResult, err := obj.Bind("password123") + + assert.NoError(t, err) + assert.Equal(t, tt.ExpectedResult, actualResult) + }) + } } func TestObjectCanSearchOn(t *testing.T) { diff --git a/pkg/ldap/directory/types.go b/pkg/ldap/directory/types.go index 83b87240..509a9a11 100644 --- a/pkg/ldap/directory/types.go +++ b/pkg/ldap/directory/types.go @@ -24,7 +24,7 @@ type ( // Bind returns true if the current object is able to authenticate and the password is correct. // It returns false if the password is wrong and optional.None if it cannot be authenticated. - Bind(password string) bool + Bind(password string) (bool, error) // CanSearchOn returns true if the current object is able to perform a search on the given DN. CanSearchOn(dn string) bool } diff --git a/pkg/ldap/directory/yaml/README.md b/pkg/ldap/directory/yaml/README.md index 36b39251..99ac821e 100644 --- a/pkg/ldap/directory/yaml/README.md +++ b/pkg/ldap/directory/yaml/README.md @@ -48,6 +48,10 @@ dc:org: #dn: dc=org - Can be a scalar (one) or a sequence (several) node - **These values are not stored inside the attribute** +> [!NOTE] +> The `!!ldap/bind:password` handle hashed password during the `bind` operation. +> Currently, only `argon2`, `bcrypt`, `pbkdf2` and `scrypt` are supported. See [README.md](../../../../README.md) for more details. + ### Extension: `go` template To extend the `YAML` syntax _(injecting secrets for example)_, the `YAML` parser will use the `text/template` package to parse the `YAML` file. diff --git a/pkg/ldap/directory/yaml/directory.go b/pkg/ldap/directory/yaml/directory.go index 81d70892..cd0632cf 100644 --- a/pkg/ldap/directory/yaml/directory.go +++ b/pkg/ldap/directory/yaml/directory.go @@ -8,8 +8,8 @@ import ( "os" "strings" - ldap "github.com/xunleii/yaldap/pkg/ldap/directory" - "github.com/xunleii/yaldap/pkg/ldap/directory/common" + ldap "github.com/chezmoi-sh/yaldap/pkg/ldap/directory" + "github.com/chezmoi-sh/yaldap/pkg/ldap/directory/common" "gopkg.in/yaml.v3" ) diff --git a/pkg/ldap/directory/yaml/directory_fixtures_test.go b/pkg/ldap/directory/yaml/directory_fixtures_test.go index bd200e76..c37e85cb 100644 --- a/pkg/ldap/directory/yaml/directory_fixtures_test.go +++ b/pkg/ldap/directory/yaml/directory_fixtures_test.go @@ -3,10 +3,10 @@ package yamldir_test import ( "testing" + ldap "github.com/chezmoi-sh/yaldap/pkg/ldap/directory" + yamldir "github.com/chezmoi-sh/yaldap/pkg/ldap/directory/yaml" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - ldap "github.com/xunleii/yaldap/pkg/ldap/directory" - yamldir "github.com/xunleii/yaldap/pkg/ldap/directory/yaml" ) func TestFixture_Basic(t *testing.T) { @@ -150,7 +150,9 @@ func TestFixture_Basic(t *testing.T) { }, obj.Attributes(), ) - assert.True(t, obj.Bind("alice")) + isBind, err := obj.Bind("alice") + require.NoError(t, err) + assert.True(t, isBind) assert.True(t, obj.CanSearchOn("dc=org")) }) @@ -169,7 +171,9 @@ func TestFixture_Basic(t *testing.T) { }, obj.Attributes(), ) - assert.True(t, obj.Bind("bob")) + isBind, err := obj.Bind("bob") + require.NoError(t, err) + assert.True(t, isBind) assert.False(t, obj.CanSearchOn("dc=org")) assert.True(t, obj.CanSearchOn("ou=group,dc=example,dc=org")) }) @@ -213,7 +217,9 @@ func TestFixture_Basic(t *testing.T) { }, obj.Attributes(), ) - assert.True(t, obj.Bind("charlie")) + isBind, err := obj.Bind("charlie") + require.NoError(t, err) + assert.True(t, isBind) assert.False(t, obj.CanSearchOn("dc=org")) assert.True(t, obj.CanSearchOn("ou=group,dc=example,dc=org")) assert.False(t, obj.CanSearchOn("cn=owner,ou=group,dc=example,dc=org")) @@ -258,7 +264,9 @@ func TestFixture_Basic(t *testing.T) { }, obj.Attributes(), ) - assert.False(t, obj.Bind("eve")) + isBind, err := obj.Bind("eve") + require.NoError(t, err) + assert.False(t, isBind) assert.False(t, obj.CanSearchOn("dc=org")) assert.False(t, obj.CanSearchOn("ou=group,dc=example,dc=org")) assert.False(t, obj.CanSearchOn("c=fr,dc=example,dc=org")) @@ -374,7 +382,9 @@ func TestFixture_Templated(t *testing.T) { }, obj.Attributes(), ) - assert.True(t, obj.Bind("bind")) + isBind, err := obj.Bind("bind") + require.NoError(t, err) + assert.True(t, isBind) assert.True(t, obj.CanSearchOn("dc=org")) }) @@ -397,7 +407,9 @@ func TestFixture_Templated(t *testing.T) { obj.Attributes(), ) - assert.True(t, obj.Bind("alice")) + isBind, err := obj.Bind("alice") + require.NoError(t, err) + assert.True(t, isBind) assert.False(t, obj.CanSearchOn("dc=org")) assert.True(t, obj.CanSearchOn("cn=alice,ou=people,dc=example,dc=org")) }) @@ -420,7 +432,9 @@ func TestFixture_Templated(t *testing.T) { }, obj.Attributes(), ) - assert.True(t, obj.Bind("bob")) + isBind, err := obj.Bind("bob") + require.NoError(t, err) + assert.True(t, isBind) assert.False(t, obj.CanSearchOn("dc=org")) assert.True(t, obj.CanSearchOn("cn=bob,ou=people,dc=example,dc=org")) }) @@ -443,7 +457,9 @@ func TestFixture_Templated(t *testing.T) { }, obj.Attributes(), ) - assert.True(t, obj.Bind("charlie")) + isBind, err := obj.Bind("charlie") + require.NoError(t, err) + assert.True(t, isBind) assert.False(t, obj.CanSearchOn("dc=org")) assert.True(t, obj.CanSearchOn("cn=charlie,ou=people,dc=example,dc=org")) }) @@ -466,7 +482,9 @@ func TestFixture_Templated(t *testing.T) { }, obj.Attributes(), ) - assert.True(t, obj.Bind("eve")) + isBind, err := obj.Bind("eve") + require.NoError(t, err) + assert.True(t, isBind) assert.False(t, obj.CanSearchOn("dc=org")) assert.True(t, obj.CanSearchOn("cn=eve,ou=people,dc=example,dc=org")) }) diff --git a/pkg/ldap/directory/yaml/directory_test.go b/pkg/ldap/directory/yaml/directory_test.go index ac1d752a..bbb35656 100644 --- a/pkg/ldap/directory/yaml/directory_test.go +++ b/pkg/ldap/directory/yaml/directory_test.go @@ -3,9 +3,9 @@ package yamldir import ( "testing" + ldap "github.com/chezmoi-sh/yaldap/pkg/ldap/directory" + "github.com/chezmoi-sh/yaldap/pkg/ldap/directory/common" "github.com/stretchr/testify/assert" - ldap "github.com/xunleii/yaldap/pkg/ldap/directory" - "github.com/xunleii/yaldap/pkg/ldap/directory/common" ) func TestNewDirectory_NoFile(t *testing.T) { diff --git a/pkg/ldap/directory/yaml/fixtures/basic.yaml b/pkg/ldap/directory/yaml/fixtures/basic.yaml index 698182f2..7fd60536 100644 --- a/pkg/ldap/directory/yaml/fixtures/basic.yaml +++ b/pkg/ldap/directory/yaml/fixtures/basic.yaml @@ -56,7 +56,7 @@ dc:org: #dn: dc=org homeDirectory: /home/bob uidNumber: 1001 gidNumber: 1001 - userPassword: !!ldap/bind:password bob + userPassword: !!ldap/bind:password bob c:de: #dn: c=de,dc=example,dc=org objectClass: [top, country] diff --git a/pkg/ldap/directory/yaml/parse.go b/pkg/ldap/directory/yaml/parse.go index 9ad2fdd5..414e1615 100644 --- a/pkg/ldap/directory/yaml/parse.go +++ b/pkg/ldap/directory/yaml/parse.go @@ -5,8 +5,8 @@ import ( "slices" "strings" - ldap "github.com/xunleii/yaldap/pkg/ldap/directory" - common "github.com/xunleii/yaldap/pkg/ldap/directory/common" + ldap "github.com/chezmoi-sh/yaldap/pkg/ldap/directory" + common "github.com/chezmoi-sh/yaldap/pkg/ldap/directory/common" "gopkg.in/yaml.v3" ) diff --git a/pkg/ldap/directory/yaml/parse_tags.go b/pkg/ldap/directory/yaml/parse_tags.go index 5933fda1..85cdec19 100644 --- a/pkg/ldap/directory/yaml/parse_tags.go +++ b/pkg/ldap/directory/yaml/parse_tags.go @@ -3,8 +3,8 @@ package yamldir import ( "fmt" + "github.com/chezmoi-sh/yaldap/pkg/ldap/directory/common" "github.com/moznion/go-optional" - "github.com/xunleii/yaldap/pkg/ldap/directory/common" "gopkg.in/yaml.v3" ) diff --git a/pkg/ldap/directory/yaml/parse_tags_test.go b/pkg/ldap/directory/yaml/parse_tags_test.go index c5d05f9b..f19b1016 100644 --- a/pkg/ldap/directory/yaml/parse_tags_test.go +++ b/pkg/ldap/directory/yaml/parse_tags_test.go @@ -3,9 +3,9 @@ package yamldir import ( "testing" + "github.com/chezmoi-sh/yaldap/pkg/ldap/directory/common" "github.com/moznion/go-optional" "github.com/stretchr/testify/assert" - "github.com/xunleii/yaldap/pkg/ldap/directory/common" "gopkg.in/yaml.v3" ) diff --git a/pkg/ldap/directory/yaml/parse_test.go b/pkg/ldap/directory/yaml/parse_test.go index 00996576..05772d1b 100644 --- a/pkg/ldap/directory/yaml/parse_test.go +++ b/pkg/ldap/directory/yaml/parse_test.go @@ -3,10 +3,10 @@ package yamldir import ( "testing" + ldap "github.com/chezmoi-sh/yaldap/pkg/ldap/directory" + "github.com/chezmoi-sh/yaldap/pkg/ldap/directory/common" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - ldap "github.com/xunleii/yaldap/pkg/ldap/directory" - "github.com/xunleii/yaldap/pkg/ldap/directory/common" "gopkg.in/yaml.v3" ) diff --git a/pkg/ldap/filters/boolean_resolvers.go b/pkg/ldap/filters/boolean_resolvers.go index 80b265a2..9a4d0e43 100644 --- a/pkg/ldap/filters/boolean_resolvers.go +++ b/pkg/ldap/filters/boolean_resolvers.go @@ -3,9 +3,9 @@ package filters import ( "fmt" + ldap "github.com/chezmoi-sh/yaldap/pkg/ldap/directory" ber "github.com/go-asn1-ber/asn1-ber" goldap "github.com/go-ldap/ldap/v3" - ldap "github.com/xunleii/yaldap/pkg/ldap/directory" ) //nolint:gochecknoinits diff --git a/pkg/ldap/filters/boolean_resolvers_test.go b/pkg/ldap/filters/boolean_resolvers_test.go index 966f3de7..61ec5421 100644 --- a/pkg/ldap/filters/boolean_resolvers_test.go +++ b/pkg/ldap/filters/boolean_resolvers_test.go @@ -3,11 +3,11 @@ package filters_test import ( "testing" + "github.com/chezmoi-sh/yaldap/pkg/ldap/filters" ber "github.com/go-asn1-ber/asn1-ber" goldap "github.com/go-ldap/ldap/v3" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/xunleii/yaldap/pkg/ldap/filters" ) func TestAndResolver(t *testing.T) { diff --git a/pkg/ldap/filters/comparator_resolvers.go b/pkg/ldap/filters/comparator_resolvers.go index b03266ce..d466fe18 100644 --- a/pkg/ldap/filters/comparator_resolvers.go +++ b/pkg/ldap/filters/comparator_resolvers.go @@ -5,9 +5,9 @@ import ( "strconv" "strings" + ldap "github.com/chezmoi-sh/yaldap/pkg/ldap/directory" ber "github.com/go-asn1-ber/asn1-ber" goldap "github.com/go-ldap/ldap/v3" - ldap "github.com/xunleii/yaldap/pkg/ldap/directory" "golang.org/x/exp/slices" ) diff --git a/pkg/ldap/filters/comparator_resolvers_test.go b/pkg/ldap/filters/comparator_resolvers_test.go index 3d34d1bd..29570ae2 100644 --- a/pkg/ldap/filters/comparator_resolvers_test.go +++ b/pkg/ldap/filters/comparator_resolvers_test.go @@ -3,11 +3,11 @@ package filters_test import ( "testing" + "github.com/chezmoi-sh/yaldap/pkg/ldap/filters" ber "github.com/go-asn1-ber/asn1-ber" goldap "github.com/go-ldap/ldap/v3" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/xunleii/yaldap/pkg/ldap/filters" ) func TestGreaterOrEqualResolver(t *testing.T) { diff --git a/pkg/ldap/filters/match_resolver.go b/pkg/ldap/filters/match_resolver.go index 1cbfdb90..f3e874e2 100644 --- a/pkg/ldap/filters/match_resolver.go +++ b/pkg/ldap/filters/match_resolver.go @@ -4,10 +4,10 @@ import ( "strconv" "strings" + ldap "github.com/chezmoi-sh/yaldap/pkg/ldap/directory" ber "github.com/go-asn1-ber/asn1-ber" phonetics "github.com/go-dedup/metaphone" goldap "github.com/go-ldap/ldap/v3" - ldap "github.com/xunleii/yaldap/pkg/ldap/directory" "golang.org/x/exp/slices" ) diff --git a/pkg/ldap/filters/match_resolver_test.go b/pkg/ldap/filters/match_resolver_test.go index fb97b634..3d534b2c 100644 --- a/pkg/ldap/filters/match_resolver_test.go +++ b/pkg/ldap/filters/match_resolver_test.go @@ -3,11 +3,11 @@ package filters_test import ( "testing" + "github.com/chezmoi-sh/yaldap/pkg/ldap/filters" ber "github.com/go-asn1-ber/asn1-ber" goldap "github.com/go-ldap/ldap/v3" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/xunleii/yaldap/pkg/ldap/filters" ) func TestApproxResolver(t *testing.T) { diff --git a/pkg/ldap/filters/present_resolver.go b/pkg/ldap/filters/present_resolver.go index 9871a051..62cc7dae 100644 --- a/pkg/ldap/filters/present_resolver.go +++ b/pkg/ldap/filters/present_resolver.go @@ -4,9 +4,9 @@ import ( "fmt" "strings" + ldap "github.com/chezmoi-sh/yaldap/pkg/ldap/directory" ber "github.com/go-asn1-ber/asn1-ber" goldap "github.com/go-ldap/ldap/v3" - ldap "github.com/xunleii/yaldap/pkg/ldap/directory" ) //nolint:gochecknoinits diff --git a/pkg/ldap/filters/present_resolver_test.go b/pkg/ldap/filters/present_resolver_test.go index 7fbf2cc7..80f226ca 100644 --- a/pkg/ldap/filters/present_resolver_test.go +++ b/pkg/ldap/filters/present_resolver_test.go @@ -3,11 +3,11 @@ package filters_test import ( "testing" + "github.com/chezmoi-sh/yaldap/pkg/ldap/filters" ber "github.com/go-asn1-ber/asn1-ber" goldap "github.com/go-ldap/ldap/v3" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/xunleii/yaldap/pkg/ldap/filters" ) func TestPresentResolver(t *testing.T) { diff --git a/pkg/ldap/filters/resolvers.go b/pkg/ldap/filters/resolvers.go index 34748cd3..e6084548 100644 --- a/pkg/ldap/filters/resolvers.go +++ b/pkg/ldap/filters/resolvers.go @@ -3,9 +3,9 @@ package filters import ( "fmt" + ldap "github.com/chezmoi-sh/yaldap/pkg/ldap/directory" ber "github.com/go-asn1-ber/asn1-ber" goldap "github.com/go-ldap/ldap/v3" - ldap "github.com/xunleii/yaldap/pkg/ldap/directory" ) //nolint:gochecknoinits diff --git a/pkg/ldap/filters/resolvers_test.go b/pkg/ldap/filters/resolvers_test.go index 3dc82866..b2525f4a 100644 --- a/pkg/ldap/filters/resolvers_test.go +++ b/pkg/ldap/filters/resolvers_test.go @@ -3,13 +3,13 @@ package filters_test import ( "testing" + ldap "github.com/chezmoi-sh/yaldap/pkg/ldap/directory" + "github.com/chezmoi-sh/yaldap/pkg/ldap/directory/common" + "github.com/chezmoi-sh/yaldap/pkg/ldap/filters" ber "github.com/go-asn1-ber/asn1-ber" goldap "github.com/go-ldap/ldap/v3" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - ldap "github.com/xunleii/yaldap/pkg/ldap/directory" - "github.com/xunleii/yaldap/pkg/ldap/directory/common" - "github.com/xunleii/yaldap/pkg/ldap/filters" ) var object = common.Object{ diff --git a/pkg/ldap/filters/substring_resolver.go b/pkg/ldap/filters/substring_resolver.go index 2e9c89c3..97089269 100644 --- a/pkg/ldap/filters/substring_resolver.go +++ b/pkg/ldap/filters/substring_resolver.go @@ -5,9 +5,9 @@ import ( "regexp" "strings" + ldap "github.com/chezmoi-sh/yaldap/pkg/ldap/directory" ber "github.com/go-asn1-ber/asn1-ber" goldap "github.com/go-ldap/ldap/v3" - ldap "github.com/xunleii/yaldap/pkg/ldap/directory" "golang.org/x/exp/slices" ) diff --git a/pkg/ldap/filters/substring_resolver_test.go b/pkg/ldap/filters/substring_resolver_test.go index c2edc75a..c67805e5 100644 --- a/pkg/ldap/filters/substring_resolver_test.go +++ b/pkg/ldap/filters/substring_resolver_test.go @@ -3,11 +3,11 @@ package filters_test import ( "testing" + "github.com/chezmoi-sh/yaldap/pkg/ldap/filters" ber "github.com/go-asn1-ber/asn1-ber" goldap "github.com/go-ldap/ldap/v3" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/xunleii/yaldap/pkg/ldap/filters" ) func TestSubstringResolver(t *testing.T) { diff --git a/pkg/ldap/mux.go b/pkg/ldap/mux.go index 96252209..36c88d92 100644 --- a/pkg/ldap/mux.go +++ b/pkg/ldap/mux.go @@ -5,10 +5,10 @@ import ( "log/slog" "strings" + "github.com/chezmoi-sh/yaldap/internal/ldap/auth" + "github.com/chezmoi-sh/yaldap/pkg/ldap/directory" + "github.com/chezmoi-sh/yaldap/pkg/utils" "github.com/jimlambrt/gldap" - "github.com/xunleii/yaldap/internal/ldap/auth" - "github.com/xunleii/yaldap/pkg/ldap/directory" - "github.com/xunleii/yaldap/pkg/utils" "golang.org/x/exp/slices" ) @@ -69,7 +69,12 @@ func (s *server) bind(w *gldap.ResponseWriter, req *gldap.Request) { return } - if !obj.Bind(string(msg.Password)) { + isBinded, err := obj.Bind(string(msg.Password)) + if err != nil { + log.Error("unable to bind user", slog.String("username", msg.UserName), slog.String("error", err.Error())) + } + + if !isBinded { log.Error("unable to bind user", slog.String("username", msg.UserName)) resp.SetResultCode(gldap.ResultInvalidCredentials) // NOTE: we don't want to give any information about the user existence diff --git a/pkg/ldap/mux_test.go b/pkg/ldap/mux_test.go index 4cc4e7eb..57718d32 100644 --- a/pkg/ldap/mux_test.go +++ b/pkg/ldap/mux_test.go @@ -7,14 +7,14 @@ import ( "testing" "time" + "github.com/chezmoi-sh/yaldap/internal/ldap/auth" + "github.com/chezmoi-sh/yaldap/pkg/ldap" + yamldir "github.com/chezmoi-sh/yaldap/pkg/ldap/directory/yaml" goldap "github.com/go-ldap/ldap/v3" "github.com/jimlambrt/gldap" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" - "github.com/xunleii/yaldap/internal/ldap/auth" - "github.com/xunleii/yaldap/pkg/ldap" - yamldir "github.com/xunleii/yaldap/pkg/ldap/directory/yaml" ) type (