diff --git a/v2/pkg/handler/disabled_bind_test.go b/v2/pkg/handler/disabled_bind_test.go new file mode 100644 index 0000000..c4da8a5 --- /dev/null +++ b/v2/pkg/handler/disabled_bind_test.go @@ -0,0 +1,56 @@ +package handler + +import ( + "context" + "encoding/hex" + "net" + "testing" + + "github.com/rs/zerolog" + "go.opentelemetry.io/otel/trace/noop" + "golang.org/x/crypto/bcrypt" + + "github.com/glauth/glauth/v2/pkg/config" + "github.com/glauth/ldap" +) + +func TestConfigBackendDisabledUserCannotBind(t *testing.T) { + pw := "correct horse" + raw, _ := bcrypt.GenerateFromPassword([]byte(pw), bcrypt.MinCost) + hexHash := hex.EncodeToString(raw) + + cfg := &config.Config{} + cfg.Users = []config.User{ + {Name: "activeuser", PrimaryGroup: 5501, PassBcrypt: hexHash, Disabled: false}, + {Name: "disableduser", PrimaryGroup: 5501, PassBcrypt: hexHash, Disabled: true}, + } + + logger := zerolog.Nop() + tracer := noop.NewTracerProvider().Tracer("f001") + helper := NewLDAPOpsHelper(tracer) + h := configHandler{ + backend: config.Backend{ + BaseDN: "dc=example,dc=com", + NameFormatAsArray: []string{"cn"}, + GroupFormatAsArray: []string{"ou"}, + }, + log: &logger, + cfg: cfg, + ldohelper: helper, + tracer: tracer, + } + _, server := net.Pipe() + defer server.Close() + + bind := func(dn, pass string) ldap.LDAPResultCode { + code, _ := helper.Bind(context.Background(), h, dn, pass, server) + return code + } + + if code := bind("cn=activeuser,dc=example,dc=com", pw); code != ldap.LDAPResultSuccess { + t.Fatalf("active user with correct pw should bind, got %d", code) + } + if code := bind("cn=disableduser,dc=example,dc=com", pw); code != ldap.LDAPResultInvalidCredentials { + t.Errorf("disabled user must be rejected; got code %d, want InvalidCredentials(49)", code) + } +} diff --git a/v2/pkg/handler/ldapopshelper.go b/v2/pkg/handler/ldapopshelper.go index 1dc3bb4..3a9fb0a 100644 --- a/v2/pkg/handler/ldapopshelper.go +++ b/v2/pkg/handler/ldapopshelper.go @@ -102,6 +102,12 @@ func (l LDAPOpsHelper) Bind(ctx context.Context, h LDAPOpsHandler, bindDN, bindS return ldapcode, nil } + // a disabled account must never authenticate, on any backend + if user.Disabled { + h.GetLog().Info().Str("binddn", bindDN).Str("src", conn.RemoteAddr().String()).Msg("Bind attempt on disabled account") + return ldap.LDAPResultInvalidCredentials, nil + } + validotp := false if len(user.Yubikey) == 0 && len(user.OTPSecret) == 0 {