0
Fork 0
mirror of https://github.com/project-zot/zot.git synced 2025-01-27 23:01:43 -05:00

feat(ldap): add option to load ldap from file (#1778)

Signed-off-by: Laurentiu Niculae <niculae.laurentiu1@gmail.com>
This commit is contained in:
LaurentiuNiculae 2023-11-15 02:21:36 +02:00 committed by GitHub
parent b2a9239c03
commit 272eb7cc43
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 668 additions and 41 deletions

View file

@ -0,0 +1,4 @@
{
"bindDN":"cn=ldap-searcher,ou=Users,dc=example,dc=org",
"bindPassword":"ldap-searcher-password"
}

View file

@ -13,14 +13,13 @@
},
"auth": {
"ldap": {
"credentialsFile": "examples/config-ldap-credentials.json",
"address": "ldap.example.org",
"port": 389,
"startTLS": false,
"baseDN":"ou=Users,dc=example,dc=org",
"userAttribute": "uid",
"userGroupAttribute": "memberOf",
"bindDN":"cn=ldap-searcher,ou=Users,dc=example,dc=org",
"bindPassword":"ldap-searcher-password",
"skipVerify": true,
"subtreeSearch": true
},

View file

@ -266,9 +266,9 @@ func (amw *AuthnMiddleware) tryAuthnHandlers(ctlr *Controller) mux.MiddlewareFun
UseSSL: !ldapConfig.Insecure,
SkipTLS: !ldapConfig.StartTLS,
Base: ldapConfig.BaseDN,
BindDN: ldapConfig.BindDN,
BindDN: ldapConfig.BindDN(),
BindPassword: ldapConfig.BindPassword(),
UserGroupAttribute: ldapConfig.UserGroupAttribute, // from config
BindPassword: ldapConfig.BindPassword,
UserFilter: fmt.Sprintf("(%s=%%s)", ldapConfig.UserAttribute),
InsecureSkipVerify: ldapConfig.SkipVerify,
ServerName: ldapConfig.Address,

View file

@ -121,21 +121,47 @@ type SchedulerConfig struct {
NumWorkers int
}
type LDAPCredentials struct {
BindDN string
BindPassword string
}
type LDAPConfig struct {
CredentialsFile string
Port int
Insecure bool
StartTLS bool // if !Insecure, then StartTLS or LDAPs
SkipVerify bool
SubtreeSearch bool
Address string
BindDN string
bindDN string `json:"-"`
bindPassword string `json:"-"`
UserGroupAttribute string
BindPassword string
BaseDN string
UserAttribute string
CACert string
}
func (ldapConf *LDAPConfig) BindDN() string {
return ldapConf.bindDN
}
func (ldapConf *LDAPConfig) SetBindDN(bindDN string) *LDAPConfig {
ldapConf.bindDN = bindDN
return ldapConf
}
func (ldapConf *LDAPConfig) BindPassword() string {
return ldapConf.bindPassword
}
func (ldapConf *LDAPConfig) SetBindPassword(bindPassword string) *LDAPConfig {
ldapConf.bindPassword = bindPassword
return ldapConf
}
type LogConfig struct {
Level string
Output string
@ -266,14 +292,14 @@ func (c *Config) Sanitize() *Config {
panic(err)
}
if c.HTTP.Auth != nil && c.HTTP.Auth.LDAP != nil && c.HTTP.Auth.LDAP.BindPassword != "" {
if c.HTTP.Auth != nil && c.HTTP.Auth.LDAP != nil && c.HTTP.Auth.LDAP.bindPassword != "" {
sanitizedConfig.HTTP.Auth.LDAP = &LDAPConfig{}
if err := DeepCopy(c.HTTP.Auth.LDAP, sanitizedConfig.HTTP.Auth.LDAP); err != nil {
panic(err)
}
sanitizedConfig.HTTP.Auth.LDAP.BindPassword = "******"
sanitizedConfig.HTTP.Auth.LDAP.bindPassword = "******"
}
return sanitizedConfig

View file

@ -69,11 +69,11 @@ func TestConfig(t *testing.T) {
Convey("Test DeepCopy() & Sanitize()", t, func() {
conf := config.New()
So(conf, ShouldNotBeNil)
authConfig := &config.AuthConfig{LDAP: &config.LDAPConfig{BindPassword: "oina"}}
authConfig := &config.AuthConfig{LDAP: (&config.LDAPConfig{}).SetBindPassword("oina")}
conf.HTTP.Auth = authConfig
So(func() { conf.Sanitize() }, ShouldNotPanic)
conf = conf.Sanitize()
So(conf.HTTP.Auth.LDAP.BindPassword, ShouldEqual, "******")
So(conf.HTTP.Auth.LDAP.BindPassword(), ShouldEqual, "******")
// negative
obj := make(chan int)

View file

@ -53,6 +53,7 @@ import (
"zotregistry.io/zot/pkg/api/config"
"zotregistry.io/zot/pkg/api/constants"
apiErr "zotregistry.io/zot/pkg/api/errors"
"zotregistry.io/zot/pkg/cli/server"
"zotregistry.io/zot/pkg/common"
extconf "zotregistry.io/zot/pkg/extensions/config"
"zotregistry.io/zot/pkg/log"
@ -1985,8 +1986,13 @@ func (l *testLDAPServer) Stop() {
}
func (l *testLDAPServer) Bind(bindDN, bindSimplePw string, conn net.Conn) (vldap.LDAPResultCode, error) {
if bindDN == "" || bindSimplePw == "" {
return vldap.LDAPResultInappropriateAuthentication, errors.ErrRequireCred
if bindSimplePw == "" {
switch bindDN {
case "bad-user", "cn=fail-user-bind,ou=test":
return vldap.LDAPResultInvalidCredentials, errors.ErrInvalidCred
default:
return vldap.LDAPResultSuccess, nil
}
}
if (bindDN == LDAPBindDN && bindSimplePw == LDAPBindPassword) ||
@ -2000,7 +2006,25 @@ func (l *testLDAPServer) Bind(bindDN, bindSimplePw string, conn net.Conn) (vldap
func (l *testLDAPServer) Search(boundDN string, req vldap.SearchRequest,
conn net.Conn,
) (vldap.ServerSearchResult, error) {
if req.Filter == "(uid=fail-user-bind)" {
return vldap.ServerSearchResult{
Entries: []*vldap.Entry{
{
DN: fmt.Sprintf("cn=%s,%s", "fail-user-bind", LDAPBaseDN),
Attributes: []*vldap.EntryAttribute{
{
Name: "memberOf",
Values: []string{group},
},
},
},
},
ResultCode: vldap.LDAPResultSuccess,
}, nil
}
check := fmt.Sprintf("(uid=%s)", username)
if check == req.Filter {
return vldap.ServerSearchResult{
Entries: []*vldap.Entry{
@ -2036,15 +2060,13 @@ func TestBasicAuthWithLDAP(t *testing.T) {
conf := config.New()
conf.HTTP.Port = port
conf.HTTP.Auth = &config.AuthConfig{
LDAP: &config.LDAPConfig{
LDAP: (&config.LDAPConfig{
Insecure: true,
Address: LDAPAddress,
Port: ldapPort,
BindDN: LDAPBindDN,
BindPassword: LDAPBindPassword,
BaseDN: LDAPBaseDN,
UserAttribute: "uid",
},
}).SetBindDN(LDAPBindDN).SetBindPassword(LDAPBindPassword),
}
ctlr := makeController(conf, t.TempDir())
@ -2077,6 +2099,293 @@ func TestBasicAuthWithLDAP(t *testing.T) {
})
}
func TestLDAPWithoutCreds(t *testing.T) {
Convey("Make a new LDAP server", t, func() {
l := newTestLDAPServer()
port := test.GetFreePort()
ldapPort, err := strconv.Atoi(port)
So(err, ShouldBeNil)
l.Start(ldapPort)
defer l.Stop()
Convey("Server credentials succed ldap auth", func() {
port = test.GetFreePort()
baseURL := test.GetBaseURL(port)
conf := config.New()
conf.HTTP.Port = port
conf.HTTP.Auth = &config.AuthConfig{
LDAP: (&config.LDAPConfig{
Insecure: true,
Address: LDAPAddress,
Port: ldapPort,
BaseDN: LDAPBaseDN,
UserAttribute: "uid",
}).SetBindDN("anonym"),
}
ctlr := makeController(conf, t.TempDir())
cm := test.NewControllerManager(ctlr)
cm.StartAndWait(port)
defer cm.StopServer()
// without creds, should get access error
resp, err := resty.R().Get(baseURL + "/v2/")
So(err, ShouldBeNil)
So(resp, ShouldNotBeNil)
So(resp.StatusCode(), ShouldEqual, http.StatusUnauthorized)
var e apiErr.Error
err = json.Unmarshal(resp.Body(), &e)
So(err, ShouldBeNil)
resp, _ = resty.R().SetBasicAuth(username, "").Get(baseURL)
So(resp, ShouldNotBeNil)
So(resp.StatusCode(), ShouldEqual, http.StatusNotFound)
resp, _ = resty.R().SetBasicAuth(username, "").Get(baseURL + "/v2/")
So(resp, ShouldNotBeNil)
So(resp.StatusCode(), ShouldEqual, http.StatusUnauthorized)
resp, _ = resty.R().SetBasicAuth(username, password).Get(baseURL + "/v2/")
So(resp, ShouldNotBeNil)
So(resp.StatusCode(), ShouldEqual, http.StatusOK)
})
Convey("Server credentials fail ldap auth", func() {
port = test.GetFreePort()
baseURL := test.GetBaseURL(port)
conf := config.New()
conf.HTTP.Port = port
conf.HTTP.Auth = &config.AuthConfig{
LDAP: (&config.LDAPConfig{
Insecure: true,
Address: LDAPAddress,
Port: ldapPort,
BaseDN: LDAPBaseDN,
UserAttribute: "uid",
}).SetBindDN("bad-user"),
}
ctlr := makeController(conf, t.TempDir())
cm := test.NewControllerManager(ctlr)
cm.StartAndWait(port)
defer cm.StopServer()
resp, _ := resty.R().SetBasicAuth(username, password).Get(baseURL + "/v2/")
So(resp, ShouldNotBeNil)
So(resp.StatusCode(), ShouldEqual, http.StatusUnauthorized)
})
})
}
func TestBasicAuthWithLDAPFromFile(t *testing.T) {
Convey("Make a new controller", t, func() {
l := newTestLDAPServer()
port := test.GetFreePort()
ldapPort, err := strconv.Atoi(port)
So(err, ShouldBeNil)
l.Start(ldapPort)
defer l.Stop()
port = test.GetFreePort()
baseURL := test.GetBaseURL(port)
tempDir := t.TempDir()
ldapConfigContent := fmt.Sprintf(`
{
"BindDN": "%v",
"BindPassword": "%v"
}`, LDAPBindDN, LDAPBindPassword)
ldapConfigPath := filepath.Join(tempDir, "ldap.json")
err = os.WriteFile(ldapConfigPath, []byte(ldapConfigContent), 0o600)
So(err, ShouldBeNil)
configStr := fmt.Sprintf(`
{
"Storage": {
"RootDirectory": "%s"
},
"HTTP": {
"Address": "%s",
"Port": "%s",
"Auth": {
"LDAP": {
"CredentialsFile": "%s",
"BaseDN": "%v",
"UserAttribute": "uid",
"UserGroupAttribute": "memberOf",
"Insecure": true,
"Address": "%v",
"Port": %v
}
}
}
}`, tempDir, "127.0.0.1", port, ldapConfigPath, LDAPBaseDN, LDAPAddress, ldapPort)
configPath := filepath.Join(tempDir, "config.json")
err = os.WriteFile(configPath, []byte(configStr), 0o0600)
So(err, ShouldBeNil)
server := server.NewServerRootCmd()
server.SetArgs([]string{"serve", configPath})
go func() {
err := server.Execute()
if err != nil {
panic(err)
}
}()
test.WaitTillServerReady(baseURL)
// without creds, should get access error
resp, err := resty.R().Get(baseURL + "/v2/")
So(err, ShouldBeNil)
So(resp, ShouldNotBeNil)
So(resp.StatusCode(), ShouldEqual, http.StatusUnauthorized)
var e apiErr.Error
err = json.Unmarshal(resp.Body(), &e)
So(err, ShouldBeNil)
// with creds, should get expected status code
resp, _ = resty.R().SetBasicAuth(username, password).Get(baseURL)
So(resp, ShouldNotBeNil)
So(resp.StatusCode(), ShouldEqual, http.StatusNotFound)
resp, _ = resty.R().SetBasicAuth(username, password).Get(baseURL + "/v2/")
So(resp, ShouldNotBeNil)
So(resp.StatusCode(), ShouldEqual, http.StatusOK)
// missing password
resp, _ = resty.R().SetBasicAuth(username, "").Get(baseURL + "/v2/")
So(resp, ShouldNotBeNil)
So(resp.StatusCode(), ShouldEqual, http.StatusUnauthorized)
})
}
func TestLDAPConfigErrors(t *testing.T) {
const configTemplate = `
{
"Storage": {
"RootDirectory": "%s"
},
"HTTP": {
"Address": "%s",
"Port": "%s",
"Auth": {
"LDAP": {
"CredentialsFile": "%s",
"BaseDN": "%v",
"UserAttribute": "%v",
"UserGroupAttribute": "memberOf",
"Insecure": true,
"Address": "%v",
"Port": %v
}
}
}
}`
Convey("bad credentials file", t, func() {
conf := config.New()
tempDir := t.TempDir()
ldapPort := 9000
userAttribute := ""
ldapConfigContent := `bad-json`
ldapConfigPath := filepath.Join(tempDir, "ldap.json")
err := os.WriteFile(ldapConfigPath, []byte(ldapConfigContent), 0o600)
So(err, ShouldBeNil)
configStr := fmt.Sprintf(configTemplate,
tempDir, "127.0.0.1", "8000", ldapConfigPath, LDAPBaseDN, userAttribute, LDAPAddress, ldapPort)
configPath := filepath.Join(tempDir, "config.json")
err = os.WriteFile(configPath, []byte(configStr), 0o0600)
So(err, ShouldBeNil)
err = server.LoadConfiguration(conf, configPath)
So(err, ShouldNotBeNil)
})
Convey("UserAttribute is empty", t, func() {
conf := config.New()
tempDir := t.TempDir()
ldapPort := 9000
userAttribute := ""
ldapConfigContent := fmt.Sprintf(`
{
"BindDN": "%v",
"BindPassword": "%v"
}`, LDAPBindDN, LDAPBindPassword)
ldapConfigPath := filepath.Join(tempDir, "ldap.json")
err := os.WriteFile(ldapConfigPath, []byte(ldapConfigContent), 0o600)
So(err, ShouldBeNil)
configStr := fmt.Sprintf(configTemplate,
tempDir, "127.0.0.1", "8000", ldapConfigPath, LDAPBaseDN, userAttribute, LDAPAddress, ldapPort)
configPath := filepath.Join(tempDir, "config.json")
err = os.WriteFile(configPath, []byte(configStr), 0o0600)
So(err, ShouldBeNil)
err = server.LoadConfiguration(conf, configPath)
So(err, ShouldNotBeNil)
})
Convey("address is empty", t, func() {
conf := config.New()
tempDir := t.TempDir()
ldapPort := 9000
userAttribute := "uid"
ldapConfigContent := fmt.Sprintf(`
{
"BindDN": "%v",
"BindPassword": "%v"
}`, LDAPBindDN, LDAPBindPassword)
ldapConfigPath := filepath.Join(tempDir, "ldap.json")
err := os.WriteFile(ldapConfigPath, []byte(ldapConfigContent), 0o600)
So(err, ShouldBeNil)
configStr := fmt.Sprintf(configTemplate,
tempDir, "127.0.0.1", "8000", ldapConfigPath, LDAPBaseDN, userAttribute, "", ldapPort)
configPath := filepath.Join(tempDir, "config.json")
err = os.WriteFile(configPath, []byte(configStr), 0o0600)
So(err, ShouldBeNil)
err = server.LoadConfiguration(conf, configPath)
So(err, ShouldNotBeNil)
})
Convey("BaseDN is empty", t, func() {
conf := config.New()
tempDir := t.TempDir()
ldapPort := 9000
userAttribute := "uid"
ldapConfigContent := fmt.Sprintf(`
{
"BindDN": "%v",
"BindPassword": "%v"
}`, LDAPBindDN, LDAPBindPassword)
ldapConfigPath := filepath.Join(tempDir, "ldap.json")
err := os.WriteFile(ldapConfigPath, []byte(ldapConfigContent), 0o600)
So(err, ShouldBeNil)
configStr := fmt.Sprintf(configTemplate,
tempDir, "127.0.0.1", "8000", ldapConfigPath, "", userAttribute, LDAPAddress, ldapPort)
configPath := filepath.Join(tempDir, "config.json")
err = os.WriteFile(configPath, []byte(configStr), 0o0600)
So(err, ShouldBeNil)
err = server.LoadConfiguration(conf, configPath)
So(err, ShouldNotBeNil)
})
}
func TestGroupsPermissionsForLDAP(t *testing.T) {
Convey("Make a new controller", t, func() {
l := newTestLDAPServer()
@ -2093,16 +2402,14 @@ func TestGroupsPermissionsForLDAP(t *testing.T) {
conf := config.New()
conf.HTTP.Port = port
conf.HTTP.Auth = &config.AuthConfig{
LDAP: &config.LDAPConfig{
LDAP: (&config.LDAPConfig{
Insecure: true,
Address: LDAPAddress,
Port: ldapPort,
BindDN: LDAPBindDN,
BindPassword: LDAPBindPassword,
BaseDN: LDAPBaseDN,
UserAttribute: "uid",
UserGroupAttribute: "memberOf",
},
}).SetBindDN(LDAPBindDN).SetBindPassword(LDAPBindPassword),
}
repoName, seed := test.GenerateRandomName()
@ -2145,6 +2452,101 @@ func TestGroupsPermissionsForLDAP(t *testing.T) {
})
}
func TestLDAPConfigFromFile(t *testing.T) {
Convey("Make a new controller", t, func() {
l := newTestLDAPServer()
port := test.GetFreePort()
ldapPort, err := strconv.Atoi(port)
So(err, ShouldBeNil)
l.Start(ldapPort)
defer l.Stop()
port = test.GetFreePort()
baseURL := test.GetBaseURL(port)
tempDir := t.TempDir()
ldapConfigContent := fmt.Sprintf(`
{
"bindDN": "%v",
"bindPassword": "%v"
}`, LDAPBindDN, LDAPBindPassword)
ldapConfigPath := filepath.Join(tempDir, "ldap.json")
err = os.WriteFile(ldapConfigPath, []byte(ldapConfigContent), 0o600)
So(err, ShouldBeNil)
configStr := fmt.Sprintf(`
{
"Storage": {
"RootDirectory": "%s"
},
"HTTP": {
"Address": "%s",
"Port": "%s",
"Auth": {
"LDAP": {
"CredentialsFile": "%s",
"BaseDN": "%v",
"UserAttribute": "uid",
"UserGroupAttribute": "memberOf",
"Insecure": true,
"Address": "%v",
"Port": %v
}
},
"AccessControl": {
"repositories": {
"test-ldap": {
"Policies": [
{
"Users": null,
"Actions": [
"read",
"create"
],
"Groups": [
"test"
]
}
]
}
},
"Groups": {
"test": {
"Users": [
"test"
]
}
}
}
}
}`, tempDir, "127.0.0.1", port, ldapConfigPath, LDAPBaseDN, LDAPAddress, ldapPort)
configPath := filepath.Join(tempDir, "config.json")
err = os.WriteFile(configPath, []byte(configStr), 0o0600)
So(err, ShouldBeNil)
server := server.NewServerRootCmd()
server.SetArgs([]string{"serve", configPath})
go func() {
err := server.Execute()
if err != nil {
panic(err)
}
}()
test.WaitTillServerReady(baseURL)
repo := "test-ldap"
img := CreateDefaultImage()
err = UploadImageWithBasicAuth(img, baseURL, repo, img.DigestStr(), username, password)
So(err, ShouldBeNil)
})
}
func TestLDAPFailures(t *testing.T) {
Convey("Make a LDAP conn", t, func() {
l := newTestLDAPServer()
@ -2181,6 +2583,69 @@ func TestLDAPFailures(t *testing.T) {
})
}
func TestLDAPClient(t *testing.T) {
Convey("LDAP Client", t, func() {
l := newTestLDAPServer()
port := test.GetFreePort()
ldapPort, err := strconv.Atoi(port)
So(err, ShouldBeNil)
l.Start(ldapPort)
defer l.Stop()
// bad server credentials
lClient := &api.LDAPClient{
Host: LDAPAddress,
Port: ldapPort,
BindDN: "bad-user",
BindPassword: "bad-pass",
SkipTLS: true,
}
_, _, _, err = lClient.Authenticate("bad-user", "bad-pass")
So(err, ShouldNotBeNil)
// bad credentials with anonymous authentication
lClient = &api.LDAPClient{
Host: LDAPAddress,
Port: ldapPort,
BindDN: "bad-user",
BindPassword: "",
SkipTLS: true,
}
_, _, _, err = lClient.Authenticate("user", "")
So(err, ShouldNotBeNil)
// bad user credentials with anonymous authentication
lClient = &api.LDAPClient{
Host: LDAPAddress,
Port: ldapPort,
BindDN: LDAPBindDN,
BindPassword: LDAPBindPassword,
Base: LDAPBaseDN,
UserFilter: "(uid=%s)",
SkipTLS: true,
}
_, _, _, err = lClient.Authenticate("fail-user-bind", "")
So(err, ShouldNotBeNil)
// bad user credentials with anonymous authentication
lClient = &api.LDAPClient{
Host: LDAPAddress,
Port: ldapPort,
BindDN: LDAPBindDN,
BindPassword: LDAPBindPassword,
Base: LDAPBaseDN,
UserFilter: "(uid=%s)",
SkipTLS: true,
}
_, _, _, err = lClient.Authenticate("fail-user-bind", "pass")
So(err, ShouldNotBeNil)
})
}
func TestBearerAuth(t *testing.T) {
Convey("Make a new controller", t, func() {
authTestServer := authutils.MakeAuthTestServer(ServerKey, UnauthorizedNamespace)
@ -2681,15 +3146,13 @@ func TestOpenIDMiddleware(t *testing.T) {
HTPasswd: config.AuthHTPasswd{
Path: htpasswdPath,
},
LDAP: &config.LDAPConfig{
LDAP: (&config.LDAPConfig{
Insecure: true,
Address: LDAPAddress,
Port: ldapPort,
BindDN: LDAPBindDN,
BindPassword: LDAPBindPassword,
BaseDN: LDAPBaseDN,
UserAttribute: "uid",
},
}).SetBindDN(LDAPBindDN).SetBindPassword(LDAPBindPassword),
OpenID: &config.OpenIDConfig{
Providers: map[string]config.OpenIDProviderConfig{
"oidc": {
@ -3162,15 +3625,13 @@ func TestAuthnSessionErrors(t *testing.T) {
HTPasswd: config.AuthHTPasswd{
Path: htpasswdPath,
},
LDAP: &config.LDAPConfig{
LDAP: (&config.LDAPConfig{
Insecure: true,
Address: LDAPAddress,
Port: ldapPort,
BindDN: LDAPBindDN,
BindPassword: LDAPBindPassword,
BaseDN: LDAPBaseDN,
UserAttribute: "uid",
},
}).SetBindDN(LDAPBindDN).SetBindPassword(LDAPBindPassword),
OpenID: &config.OpenIDConfig{
Providers: map[string]config.OpenIDProviderConfig{
"oidc": {

View file

@ -140,7 +140,7 @@ func (lc *LDAPClient) Authenticate(username, password string) (bool, map[string]
}
// First bind with a read only user
if lc.BindDN != "" && lc.BindPassword != "" {
if lc.BindPassword != "" {
err := lc.Conn.Bind(lc.BindDN, lc.BindPassword)
if err != nil {
lc.Log.Error().Err(err).Str("bindDN", lc.BindDN).Msg("bind failed")
@ -148,6 +148,16 @@ func (lc *LDAPClient) Authenticate(username, password string) (bool, map[string]
lc.Conn.Close()
lc.Conn = nil
continue
}
} else {
err := lc.Conn.UnauthenticatedBind(lc.BindDN)
if err != nil {
lc.Log.Error().Err(err).Str("bindDN", lc.BindDN).Msg("bind failed")
// clean up the cached conn, so we can retry
lc.Conn.Close()
lc.Conn = nil
continue
}
}

View file

@ -738,6 +738,10 @@ func LoadConfiguration(config *config.Config, configPath string) error {
return zerr.ErrBadConfig
}
if err := updateLDAPConfig(config); err != nil {
return err
}
// defaults
applyDefaultValues(config, viperInstance, log)
@ -752,6 +756,50 @@ func LoadConfiguration(config *config.Config, configPath string) error {
return nil
}
func updateLDAPConfig(conf *config.Config) error {
if conf.HTTP.Auth == nil || conf.HTTP.Auth.LDAP == nil {
return nil
}
if conf.HTTP.Auth.LDAP.CredentialsFile == "" {
conf.HTTP.Auth.LDAP.SetBindDN("anonym-user")
return nil
}
newLDAPCredentials, err := readLDAPCredentials(conf.HTTP.Auth.LDAP.CredentialsFile)
if err != nil {
return err
}
conf.HTTP.Auth.LDAP.SetBindDN(newLDAPCredentials.BindDN)
conf.HTTP.Auth.LDAP.SetBindPassword(newLDAPCredentials.BindPassword)
return nil
}
func readLDAPCredentials(ldapConfigPath string) (config.LDAPCredentials, error) {
viperInstance := viper.NewWithOptions(viper.KeyDelimiter("::"))
viperInstance.SetConfigFile(ldapConfigPath)
if err := viperInstance.ReadInConfig(); err != nil {
log.Error().Err(err).Msg("error while reading configuration")
return config.LDAPCredentials{}, err
}
var ldapCredentials config.LDAPCredentials
if err := viperInstance.Unmarshal(&ldapCredentials); err != nil {
log.Error().Err(err).Msg("error while unmarshaling new config")
return config.LDAPCredentials{}, err
}
return ldapCredentials, nil
}
func authzContainsOnlyAnonymousPolicy(cfg *config.Config) bool {
adminPolicy := cfg.HTTP.AccessControl.AdminPolicy
anonymousPolicyPresent := false

View file

@ -5,6 +5,7 @@ import (
"fmt"
"os"
"path"
"path/filepath"
"testing"
"time"
@ -1447,6 +1448,88 @@ func TestScrub(t *testing.T) {
})
}
func TestUpdateLDAPConfig(t *testing.T) {
Convey("updateLDAPConfig errors while unmarshaling ldap config", t, func() {
tempDir := t.TempDir()
ldapConfigContent := "bad-json"
ldapConfigPath := filepath.Join(tempDir, "ldap.json")
err := os.WriteFile(ldapConfigPath, []byte(ldapConfigContent), 0o000)
So(err, ShouldBeNil)
configStr := fmt.Sprintf(`
{
"Storage": {
"RootDirectory": "%s"
},
"HTTP": {
"Address": "%s",
"Port": "%s",
"Auth": {
"LDAP": {
"CredentialsFile": "%s",
"BaseDN": "%v",
"UserAttribute": "uid",
"UserGroupAttribute": "memberOf",
"Insecure": true,
"Address": "%v",
"Port": %v
}
}
}
}`, tempDir, "127.0.0.1", "8000", ldapConfigPath, "LDAPBaseDN", "LDAPAddress", 1000)
configPath := filepath.Join(tempDir, "config.json")
err = os.WriteFile(configPath, []byte(configStr), 0o0600)
So(err, ShouldBeNil)
server := cli.NewServerRootCmd()
server.SetArgs([]string{"serve", configPath})
So(func() { err = server.Execute() }, ShouldPanic)
err = os.Chmod(ldapConfigPath, 0o600)
So(err, ShouldBeNil)
server = cli.NewServerRootCmd()
server.SetArgs([]string{"serve", configPath})
So(func() { err = server.Execute() }, ShouldPanic)
})
Convey("unauthenticated LDAP config", t, func() {
tempDir := t.TempDir()
configStr := fmt.Sprintf(`
{
"Storage": {
"RootDirectory": "%s"
},
"HTTP": {
"Address": "%s",
"Port": "%s",
"Auth": {
"LDAP": {
"BaseDN": "%v",
"UserAttribute": "uid",
"UserGroupAttribute": "memberOf",
"Insecure": true,
"Address": "%v",
"Port": %v
}
}
}
}`, tempDir, "127.0.0.1", "8000", "LDAPBaseDN", "LDAPAddress", 1000)
configPath := filepath.Join(tempDir, "config.json")
err := os.WriteFile(configPath, []byte(configStr), 0o0600)
So(err, ShouldBeNil)
err = cli.LoadConfiguration(config.New(), configPath)
So(err, ShouldBeNil)
})
}
// run cli and return output.
func runCLIWithConfig(tempDir string, config string) (string, error) {
port := GetFreePort()

View file

@ -225,11 +225,10 @@ func TestMgmtExtension(t *testing.T) {
Convey("Verify mgmt auth info route enabled with ldap", t, func() {
defer os.Remove(conf.HTTP.Auth.HTPasswd.Path) // cleanup of a file created in previous Convey
conf.HTTP.Auth.LDAP = &config.LDAPConfig{
BindDN: "binddn",
conf.HTTP.Auth.LDAP = (&config.LDAPConfig{
BaseDN: "basedn",
Address: "ldapexample",
}
}).SetBindDN("binddn")
conf.Extensions = &extconf.ExtensionConfig{}
conf.Extensions.Search = &extconf.SearchConfig{}
@ -290,11 +289,10 @@ func TestMgmtExtension(t *testing.T) {
htpasswdPath := test.MakeHtpasswdFileFromString(test.GetCredString(username, password))
defer os.Remove(htpasswdPath)
conf.HTTP.Auth.HTPasswd.Path = htpasswdPath
conf.HTTP.Auth.LDAP = &config.LDAPConfig{
BindDN: "binddn",
conf.HTTP.Auth.LDAP = (&config.LDAPConfig{
BaseDN: "basedn",
Address: "ldapexample",
}
}).SetBindDN("binddn")
conf.Extensions = &extconf.ExtensionConfig{}
conf.Extensions.Search = &extconf.SearchConfig{}
@ -369,11 +367,10 @@ func TestMgmtExtension(t *testing.T) {
htpasswdPath := test.MakeHtpasswdFileFromString(test.GetCredString(username, password))
defer os.Remove(htpasswdPath)
conf.HTTP.Auth.HTPasswd.Path = htpasswdPath
conf.HTTP.Auth.LDAP = &config.LDAPConfig{
BindDN: "binddn",
conf.HTTP.Auth.LDAP = (&config.LDAPConfig{
BaseDN: "basedn",
Address: "ldapexample",
}
}).SetBindDN("binddn")
conf.HTTP.Auth.Bearer = &config.BearerConfig{
Realm: "realm",
@ -449,11 +446,10 @@ func TestMgmtExtension(t *testing.T) {
Convey("Verify mgmt auth info route enabled with ldap + bearer", t, func() {
conf.HTTP.Auth.HTPasswd.Path = ""
conf.HTTP.Auth.LDAP = &config.LDAPConfig{
BindDN: "binddn",
conf.HTTP.Auth.LDAP = (&config.LDAPConfig{
BaseDN: "basedn",
Address: "ldapexample",
}
}).SetBindDN("binddn")
conf.HTTP.Auth.Bearer = &config.BearerConfig{
Realm: "realm",