diff --git a/conf/ldap.toml b/conf/ldap.toml index 647b56ca5ab..bda48ad3946 100644 --- a/conf/ldap.toml +++ b/conf/ldap.toml @@ -11,6 +11,11 @@ port = 389 use_ssl = false # If set to true, use LDAP with STARTTLS instead of LDAPS start_tls = false +# The value of an accepted TLS cipher. By default, this value is empty. Example value: ["TLS_AES_256_GCM_SHA384"]) +# For a complete list of supported ciphers and TLS versions, refer to: https://go.dev/src/crypto/tls/cipher_suites.go +tls_ciphers = [] +# This is the minimum TLS version allowed. By default, this value is empty. Accepted values are: TLS1.1, TLS1.2, TLS1.3. +min_tls_version = "" # set to true if you want to skip ssl cert validation ssl_skip_verify = false # set to the path to your root CA certificate or leave unset to use system defaults diff --git a/docs/sources/setup-grafana/configure-security/configure-authentication/ldap/index.md b/docs/sources/setup-grafana/configure-security/configure-authentication/ldap/index.md index e0e47755172..1f690eaff7f 100644 --- a/docs/sources/setup-grafana/configure-security/configure-authentication/ldap/index.md +++ b/docs/sources/setup-grafana/configure-security/configure-authentication/ldap/index.md @@ -78,6 +78,11 @@ port = 636 use_ssl = true # If set to true, use LDAP with STARTTLS instead of LDAPS start_tls = false +# The value of an accepted TLS cipher. By default, this value is empty. Example value: ["TLS_AES_256_GCM_SHA384"]) +# For a complete list of supported ciphers and TLS versions, refer to: https://go.dev/src/crypto/tls/cipher_suites.go +tls_ciphers = [] +# This is the minimum TLS version allowed. By default, this value is empty. Accepted values are: TLS1.1, TLS1.2, TLS1.3. +min_tls_version = "" # set to true if you want to skip SSL cert validation ssl_skip_verify = false # set to the path to your root CA certificate or leave unset to use system defaults diff --git a/go.mod b/go.mod index 3aa7662f8df..c99b2911555 100644 --- a/go.mod +++ b/go.mod @@ -51,6 +51,7 @@ require ( github.com/gchaincl/sqlhooks v1.3.0 github.com/getsentry/sentry-go v0.13.0 github.com/go-git/go-git/v5 v5.4.2 + github.com/go-ldap/ldap/v3 v3.4.4 github.com/go-openapi/strfmt v0.21.3 github.com/go-redis/redis/v8 v8.11.5 github.com/go-sourcemap/sourcemap v2.1.3+incompatible @@ -126,12 +127,11 @@ require ( google.golang.org/protobuf v1.28.1 gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect gopkg.in/ini.v1 v1.67.0 - gopkg.in/ldap.v3 v3.1.0 gopkg.in/mail.v2 v2.3.1 gopkg.in/square/go-jose.v2 v2.5.1 gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 - xorm.io/builder v0.3.6 // indirect + xorm.io/builder v0.3.6 xorm.io/core v0.7.3 xorm.io/xorm v0.8.2 ) @@ -234,7 +234,6 @@ require ( golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/genproto v0.0.0-20221227171554-f9683d7f8bef - gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d // indirect ) require ( @@ -283,6 +282,7 @@ require ( cloud.google.com/go/compute/metadata v0.2.2 // indirect github.com/Azure/azure-pipeline-go v0.2.3 // indirect github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect + github.com/Azure/go-ntlmssp v0.0.0-20220621081337-cb9428e4ac1e // indirect github.com/HdrHistogram/hdrhistogram-go v1.1.2 // indirect github.com/Masterminds/goutils v1.1.1 // indirect github.com/apapsch/go-jsonmerge/v2 v2.0.0 // indirect @@ -304,6 +304,7 @@ require ( github.com/envoyproxy/go-control-plane v0.10.3 // indirect github.com/envoyproxy/protoc-gen-validate v0.6.13 // indirect github.com/fsnotify/fsnotify v1.6.0 // indirect + github.com/go-asn1-ber/asn1-ber v1.5.4 // indirect github.com/google/go-querystring v1.1.0 // indirect github.com/google/gofuzz v1.2.0 // indirect github.com/googleapis/enterprise-certificate-proxy v0.2.0 // indirect diff --git a/go.sum b/go.sum index e07a0ef9007..c413b8a65b2 100644 --- a/go.sum +++ b/go.sum @@ -193,6 +193,8 @@ github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZ github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk= github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo= github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= +github.com/Azure/go-ntlmssp v0.0.0-20220621081337-cb9428e4ac1e h1:NeAW1fUYUEWhft7pkxDf6WoUvEZJ/uOKsvtpjLnn8MU= +github.com/Azure/go-ntlmssp v0.0.0-20220621081337-cb9428e4ac1e/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU= github.com/AzureAD/microsoft-authentication-library-for-go v0.7.0 h1:VgSJlZH5u0k2qxSpqyghcFQKmvYckj46uymKK5XzkBM= github.com/AzureAD/microsoft-authentication-library-for-go v0.7.0/go.mod h1:BDJ5qMFKx9DugEg3+uQSDCdbYPr5s9vBTrL9P8TpqOU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= @@ -802,6 +804,8 @@ github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0 github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= github.com/glycerine/go-unsnap-stream v0.0.0-20180323001048-9f0cb55181dd/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE= github.com/glycerine/goconvey v0.0.0-20190410193231-58a59202ab31/go.mod h1:Ogl1Tioa0aV7gstGFO7KhffUsb9M4ydbEbbxpcEDc24= +github.com/go-asn1-ber/asn1-ber v1.5.4 h1:vXT6d/FNDiELJnLb6hGNa309LMsrCoYFvpwHDF0+Y1A= +github.com/go-asn1-ber/asn1-ber v1.5.4/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0= github.com/go-chi/chi v4.1.0+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ= github.com/go-chi/chi/v5 v5.0.8/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= github.com/go-errors/errors v1.0.1 h1:LUHzmkK3GUKUrL/1gfBUxAHzcev3apQlezX/+O7ma6w= @@ -832,6 +836,8 @@ github.com/go-kit/log v0.2.1 h1:MRVx0/zhvdseW+Gza6N9rVzU/IVzaeE1SFI4raAhmBU= github.com/go-kit/log v0.2.1/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= github.com/go-latex/latex v0.0.0-20210118124228-b3d85cf34e07/go.mod h1:CO1AlKB2CSIqUrmQPqA0gdRIlnLEY0gK5JGjh37zN5U= github.com/go-ldap/ldap v3.0.2+incompatible/go.mod h1:qfd9rJvER9Q0/D/Sqn1DfHRoBp40uXYvFoEVrNEPqRc= +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= @@ -2181,6 +2187,7 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5 github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= github.com/stretchr/testify v1.7.5/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= @@ -3118,7 +3125,6 @@ gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4 gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc h1:2gGKlE2+asNV9m7xrywl36YYNnBG5ZQ0r/BOOxqPpmk= gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk= -gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d h1:TxyelI5cVkbREznMhfzycHdkp5cLA7DpE+GKjSslYhM= gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d/go.mod h1:cuepJuh7vyXfUyUwEgHQXw849cJrilpS5NeIjOWESAw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20141024133853-64131543e789/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -3143,8 +3149,6 @@ gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/ldap.v3 v3.1.0 h1:DIDWEjI7vQWREh0S8X5/NFPCZ3MCVd55LmXKPW4XLGE= -gopkg.in/ldap.v3 v3.1.0/go.mod h1:dQjCc0R0kfyFjIlWNMH1DORwUASZyDxo2Ry1B51dXaQ= gopkg.in/mail.v2 v2.3.1 h1:WYFn/oANrAGP2C0dcV6/pbkPzv8yGzqTjPmTeO7qoXk= gopkg.in/mail.v2 v2.3.1/go.mod h1:htwXN1Qh09vZJ1NVKxQqHPBaCBbzKhp5GzuJEA4VJWw= gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= diff --git a/pkg/services/ldap/helpers.go b/pkg/services/ldap/helpers.go index 384f2adb7c4..3d12a800f8e 100644 --- a/pkg/services/ldap/helpers.go +++ b/pkg/services/ldap/helpers.go @@ -3,7 +3,7 @@ package ldap import ( "strings" - "gopkg.in/ldap.v3" + "github.com/go-ldap/ldap/v3" ) func IsMemberOf(memberOf []string, group string) bool { diff --git a/pkg/services/ldap/ldap.go b/pkg/services/ldap/ldap.go index 9d1f67150fd..04b77cb831c 100644 --- a/pkg/services/ldap/ldap.go +++ b/pkg/services/ldap/ldap.go @@ -12,7 +12,7 @@ import ( "strings" "time" - "gopkg.in/ldap.v3" + "github.com/go-ldap/ldap/v3" "github.com/grafana/grafana/pkg/infra/log" "github.com/grafana/grafana/pkg/services/login" @@ -130,6 +130,8 @@ func (server *Server) Dial() error { InsecureSkipVerify: server.Config.SkipVerifySSL, ServerName: host, RootCAs: certPool, + MinVersion: server.Config.minTLSVersion, + CipherSuites: server.Config.tlsCiphers, } if len(clientCert.Certificate) > 0 { tlsCfg.Certificates = append(tlsCfg.Certificates, clientCert) diff --git a/pkg/services/ldap/ldap_helpers_test.go b/pkg/services/ldap/ldap_helpers_test.go index bf45cb0b0a3..0f28d366418 100644 --- a/pkg/services/ldap/ldap_helpers_test.go +++ b/pkg/services/ldap/ldap_helpers_test.go @@ -4,8 +4,8 @@ import ( "fmt" "testing" + "github.com/go-ldap/ldap/v3" "github.com/stretchr/testify/assert" - "gopkg.in/ldap.v3" ) func TestIsMemberOf(t *testing.T) { diff --git a/pkg/services/ldap/ldap_login_test.go b/pkg/services/ldap/ldap_login_test.go index 717256a2fdd..5f987353692 100644 --- a/pkg/services/ldap/ldap_login_test.go +++ b/pkg/services/ldap/ldap_login_test.go @@ -4,9 +4,9 @@ import ( "errors" "testing" + "github.com/go-ldap/ldap/v3" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "gopkg.in/ldap.v3" "github.com/grafana/grafana/pkg/infra/log" "github.com/grafana/grafana/pkg/services/login" diff --git a/pkg/services/ldap/ldap_private_test.go b/pkg/services/ldap/ldap_private_test.go index 7db73df0359..132e1cfd0be 100644 --- a/pkg/services/ldap/ldap_private_test.go +++ b/pkg/services/ldap/ldap_private_test.go @@ -3,9 +3,9 @@ package ldap import ( "testing" + "github.com/go-ldap/ldap/v3" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "gopkg.in/ldap.v3" "github.com/grafana/grafana/pkg/infra/log" "github.com/grafana/grafana/pkg/services/login" diff --git a/pkg/services/ldap/ldap_test.go b/pkg/services/ldap/ldap_test.go index 9fae2f16467..3ee4d1f8626 100644 --- a/pkg/services/ldap/ldap_test.go +++ b/pkg/services/ldap/ldap_test.go @@ -5,9 +5,9 @@ import ( "fmt" "testing" + "github.com/go-ldap/ldap/v3" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "gopkg.in/ldap.v3" "github.com/grafana/grafana/pkg/infra/log" "github.com/grafana/grafana/pkg/models/roletype" diff --git a/pkg/services/ldap/settings.go b/pkg/services/ldap/settings.go index aa961752484..a9acde52b5f 100644 --- a/pkg/services/ldap/settings.go +++ b/pkg/services/ldap/settings.go @@ -1,8 +1,10 @@ package ldap import ( + "crypto/tls" "fmt" "os" + "strings" "sync" "github.com/BurntSushi/toml" @@ -21,18 +23,24 @@ type Config struct { // ServerConfig holds connection data to LDAP type ServerConfig struct { - Host string `toml:"host"` - Port int `toml:"port"` - UseSSL bool `toml:"use_ssl"` - StartTLS bool `toml:"start_tls"` - SkipVerifySSL bool `toml:"ssl_skip_verify"` - RootCACert string `toml:"root_ca_cert"` - ClientCert string `toml:"client_cert"` - ClientKey string `toml:"client_key"` - BindDN string `toml:"bind_dn"` - BindPassword string `toml:"bind_password"` - Timeout int `toml:"timeout"` - Attr AttributeMap `toml:"attributes"` + Host string `toml:"host"` + Port int `toml:"port"` + + UseSSL bool `toml:"use_ssl"` + StartTLS bool `toml:"start_tls"` + SkipVerifySSL bool `toml:"ssl_skip_verify"` + MinTLSVersion string `toml:"min_tls_version"` + minTLSVersion uint16 `toml:"-"` + TLSCiphers []string `toml:"tls_ciphers"` + tlsCiphers []uint16 `toml:"-"` + + RootCACert string `toml:"root_ca_cert"` + ClientCert string `toml:"client_cert"` + ClientKey string `toml:"client_key"` + BindDN string `toml:"bind_dn"` + BindPassword string `toml:"bind_password"` + Timeout int `toml:"timeout"` + Attr AttributeMap `toml:"attributes"` SearchFilter string `toml:"search_filter"` SearchBaseDNs []string `toml:"search_base_dns"` @@ -135,6 +143,20 @@ func readConfig(configFile string) (*Config, error) { return nil, fmt.Errorf("%v: %w", "Failed to validate SearchBaseDNs section", err) } + if server.MinTLSVersion != "" { + server.minTLSVersion, err = tlsNameToVersion(server.MinTLSVersion) + if err != nil { + logger.Error("Failed to set min TLS version. Ignoring", "err", err) + } + } + + if len(server.TLSCiphers) > 0 { + server.tlsCiphers, err = tlsCiphersToIDs(server.TLSCiphers) + if err != nil { + logger.Error("Unrecognized TLS Cipher(s). Ignoring", "err", err) + } + } + for _, groupMap := range server.Groups { if groupMap.OrgRole == "" && groupMap.IsGrafanaAdmin == nil { return nil, fmt.Errorf("LDAP group mapping: organization role or grafana admin status is required") @@ -169,3 +191,53 @@ func assertNotEmptyCfg(val interface{}, propName string) error { } return nil } + +// tlsNameToVersion converts a string to a tls version +func tlsNameToVersion(name string) (uint16, error) { + name = strings.ToUpper(name) + switch name { + case "TLS1.0": + return tls.VersionTLS10, nil + case "TLS1.1": + return tls.VersionTLS11, nil + case "TLS1.2": + return tls.VersionTLS12, nil + case "TLS1.3": + return tls.VersionTLS13, nil + } + + return 0, fmt.Errorf("unknown tls version: %q", name) +} + +// Cipher strings https://go.dev/src/crypto/tls/cipher_suites.go +// Ex: "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256" or "TLS_RSA_WITH_AES_128_CBC_SHA" +func tlsCiphersToIDs(names []string) ([]uint16, error) { + if len(names) == 0 || names == nil { + // no ciphers specified, use defaults + return nil, nil + } + var ids []uint16 + var missing []string + + ciphers := tls.CipherSuites() + var cipherMap = make(map[string]uint16, len(ciphers)) + for _, cipher := range ciphers { + cipherMap[cipher.Name] = cipher.ID + } + + for _, name := range names { + name = strings.ToUpper(name) + id, ok := cipherMap[name] + if !ok { + missing = append(missing, name) + continue + } + ids = append(ids, id) + } + + if len(missing) > 0 { + return ids, fmt.Errorf("unknown ciphers: %v", missing) + } + + return ids, nil +} diff --git a/pkg/services/ldap/settings_test.go b/pkg/services/ldap/settings_test.go index 0c542798de0..b1562afe513 100644 --- a/pkg/services/ldap/settings_test.go +++ b/pkg/services/ldap/settings_test.go @@ -1,7 +1,7 @@ package ldap import ( - "os" + "crypto/tls" "testing" "github.com/stretchr/testify/assert" @@ -12,11 +12,14 @@ func TestReadingLDAPSettings(t *testing.T) { config, err := readConfig("testdata/ldap.toml") assert.Nil(t, err, "No error when reading ldap config") assert.EqualValues(t, "127.0.0.1", config.Servers[0].Host) + assert.EqualValues(t, "tls1.3", config.Servers[0].MinTLSVersion) + assert.EqualValues(t, uint16(tls.VersionTLS13), config.Servers[0].minTLSVersion) + assert.EqualValues(t, []string{"TLS_CHACHA20_POLY1305_SHA256", "TLS_AES_128_GCM_SHA256"}, config.Servers[0].TLSCiphers) + assert.ElementsMatch(t, []uint16{tls.TLS_CHACHA20_POLY1305_SHA256, tls.TLS_AES_128_GCM_SHA256}, config.Servers[0].tlsCiphers) } func TestReadingLDAPSettingsWithEnvVariable(t *testing.T) { - err := os.Setenv("ENV_PASSWORD", "MySecret") - require.NoError(t, err) + t.Setenv("ENV_PASSWORD", "MySecret") config, err := readConfig("testdata/ldap.toml") require.NoError(t, err) diff --git a/pkg/services/ldap/testdata/ldap.toml b/pkg/services/ldap/testdata/ldap.toml index e60ef4e6b73..64952098d09 100644 --- a/pkg/services/ldap/testdata/ldap.toml +++ b/pkg/services/ldap/testdata/ldap.toml @@ -4,6 +4,8 @@ port = 389 use_ssl = false start_tls = false ssl_skip_verify = false +tls_ciphers = ["TLS_CHACHA20_POLY1305_SHA256", "TLS_AES_128_GCM_SHA256"] +min_tls_version = "tls1.3" bind_dn = "cn=admin,dc=grafana,dc=org" bind_password = '${ENV_PASSWORD}' search_filter = "(cn=%s)" @@ -24,4 +26,3 @@ grafana_admin = true [[servers.group_mappings]] group_dn = "cn=users,ou=groups,dc=grafana,dc=org" org_role = "Editor" - diff --git a/pkg/services/ldap/testing.go b/pkg/services/ldap/testing.go index d0d649185ac..292b8be7d8a 100644 --- a/pkg/services/ldap/testing.go +++ b/pkg/services/ldap/testing.go @@ -3,7 +3,7 @@ package ldap import ( "crypto/tls" - "gopkg.in/ldap.v3" + "github.com/go-ldap/ldap/v3" //TODO(sh0rez): remove once import cycle resolved _ "github.com/grafana/grafana/pkg/api/response"