Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Canonical username as LDAP alias #5353

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 12 additions & 12 deletions builtin/credential/ldap/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,18 +58,18 @@ type backend struct {
*framework.Backend
}

func (b *backend) Login(ctx context.Context, req *logical.Request, username string, password string) ([]string, *logical.Response, []string, error) {
func (b *backend) Login(ctx context.Context, req *logical.Request, username string, password string) ([]string, *logical.Response, string, []string, error) {

cfg, err := b.Config(ctx, req)
if err != nil {
return nil, nil, nil, err
return nil, nil, "", nil, err
}
if cfg == nil {
return nil, logical.ErrorResponse("ldap backend not configured"), nil, nil
return nil, logical.ErrorResponse("ldap backend not configured"), "", nil, nil
}

if cfg.DenyNullBind && len(password) == 0 {
return nil, logical.ErrorResponse("password cannot be of zero length when passwordless binds are being denied"), nil, nil
return nil, logical.ErrorResponse("password cannot be of zero length when passwordless binds are being denied"), "", nil, nil
}

ldapClient := ldaputil.Client{
Expand All @@ -79,10 +79,10 @@ func (b *backend) Login(ctx context.Context, req *logical.Request, username stri

c, err := ldapClient.DialLDAP(cfg)
if err != nil {
return nil, logical.ErrorResponse(err.Error()), nil, nil
return nil, logical.ErrorResponse(err.Error()), "", nil, nil
}
if c == nil {
return nil, logical.ErrorResponse("invalid connection returned from LDAP dial"), nil, nil
return nil, logical.ErrorResponse("invalid connection returned from LDAP dial"), "", nil, nil
}

// Clean connection
Expand All @@ -93,7 +93,7 @@ func (b *backend) Login(ctx context.Context, req *logical.Request, username stri
if b.Logger().IsDebug() {
b.Logger().Debug("error getting user bind DN", "error", err)
}
return nil, logical.ErrorResponse("ldap operation failed"), nil, nil
return nil, logical.ErrorResponse("ldap operation failed"), "", nil, nil
}

if b.Logger().IsDebug() {
Expand All @@ -110,7 +110,7 @@ func (b *backend) Login(ctx context.Context, req *logical.Request, username stri
if b.Logger().IsDebug() {
b.Logger().Debug("ldap bind failed", "error", err)
}
return nil, logical.ErrorResponse("ldap operation failed"), nil, nil
return nil, logical.ErrorResponse("ldap operation failed"), "", nil, nil
}

// We re-bind to the BindDN if it's defined because we assume
Expand All @@ -120,7 +120,7 @@ func (b *backend) Login(ctx context.Context, req *logical.Request, username stri
if b.Logger().IsDebug() {
b.Logger().Debug("error while attempting to re-bind with the BindDN User", "error", err)
}
return nil, logical.ErrorResponse("ldap operation failed"), nil, nil
return nil, logical.ErrorResponse("ldap operation failed"), "", nil, nil
}
if b.Logger().IsDebug() {
b.Logger().Debug("re-bound to original binddn")
Expand All @@ -129,12 +129,12 @@ func (b *backend) Login(ctx context.Context, req *logical.Request, username stri

userDN, err := ldapClient.GetUserDN(cfg, c, userBindDN)
if err != nil {
return nil, logical.ErrorResponse(err.Error()), nil, nil
return nil, logical.ErrorResponse(err.Error()), "", nil, nil
}

ldapGroups, err := ldapClient.GetLdapGroups(cfg, c, userDN, username)
if err != nil {
return nil, logical.ErrorResponse(err.Error()), nil, nil
return nil, logical.ErrorResponse(err.Error()), "", nil, nil
}
if b.Logger().IsDebug() {
b.Logger().Debug("groups fetched from server", "num_server_groups", len(ldapGroups), "server_groups", ldapGroups)
Expand Down Expand Up @@ -190,7 +190,7 @@ func (b *backend) Login(ctx context.Context, req *logical.Request, username stri
// Policies from each group may overlap
policies = strutil.RemoveDuplicates(policies, true)

return policies, ldapResponse, allGroups, nil
return policies, ldapResponse, canonicalUsername, allGroups, nil
}

const backendHelp = `
Expand Down
10 changes: 5 additions & 5 deletions builtin/credential/ldap/path_login.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ func (b *backend) pathLogin(ctx context.Context, req *logical.Request, d *framew
username := d.Get("username").(string)
password := d.Get("password").(string)

policies, resp, groupNames, err := b.Login(ctx, req, username, password)
policies, resp, canonicalUsername, groupNames, err := b.Login(ctx, req, username, password)
// Handle an internal error
if err != nil {
return nil, err
Expand All @@ -73,17 +73,17 @@ func (b *backend) pathLogin(ctx context.Context, req *logical.Request, d *framew
resp.Auth = &logical.Auth{
Policies: policies,
Metadata: map[string]string{
"username": username,
"username": canonicalUsername,
},
InternalData: map[string]interface{}{
"password": password,
},
DisplayName: username,
DisplayName: canonicalUsername,
LeaseOptions: logical.LeaseOptions{
Renewable: true,
},
Alias: &logical.Alias{
Name: username,
Name: canonicalUsername,
},
}

Expand All @@ -102,7 +102,7 @@ func (b *backend) pathLoginRenew(ctx context.Context, req *logical.Request, d *f
username := req.Auth.Metadata["username"]
password := req.Auth.InternalData["password"].(string)

loginPolicies, resp, groupNames, err := b.Login(ctx, req, username, password)
loginPolicies, resp, _, groupNames, err := b.Login(ctx, req, username, password)
if len(loginPolicies) == 0 {
return resp, err
}
Expand Down
88 changes: 88 additions & 0 deletions builtin/credential/ldap/path_login_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
package ldap

import (
"testing"

"github.com/hashicorp/vault/api"
vaulthttp "github.com/hashicorp/vault/http"
"github.com/hashicorp/vault/logical"
"github.com/hashicorp/vault/vault"
)

func TestLdapAuthBackend_UsernameAliasCaseSensitivity(t *testing.T) {
coreConfig := &vault.CoreConfig{
CredentialBackends: map[string]logical.Factory{
"ldap": Factory,
},
}
cluster := vault.NewTestCluster(t, coreConfig, &vault.TestClusterOptions{
HandlerFunc: vaulthttp.Handler,
})
cluster.Start()
defer cluster.Cleanup()

core := cluster.Cores[0].Core
vault.TestWaitActive(t, core)
client := cluster.Cores[0].Client

// Create an ldap mount
err := client.Sys().EnableAuthWithOptions("ldap", &api.EnableAuthOptions{
Type: "ldap",
})
if err != nil {
t.Fatal(err)
}

_, err = client.Logical().Write("auth/ldap/config", map[string]interface{}{
"url": "ldap://ldap.forumsys.com",
"userattr": "uid",
"userdn": "dc=example,dc=com",
"groupdn": "dc=example,dc=com",
"binddn": "cn=read-only-admin,dc=example,dc=com",
})
if err != nil {
t.Fatal(err)
}

_, err = client.Logical().Write("auth/ldap/groups/testgroup", map[string]interface{}{
"policies": "testgrouppolicy",
})
if err != nil {
t.Fatal(err)
}

_, err = client.Logical().Write("auth/ldap/users/tesla", map[string]interface{}{
"policies": "default",
"groups": "testgroup",
})
if err != nil {
t.Fatal(err)
}

usernames := []string{
"tesla",
"Tesla",
"teSlA",
}

// Login using different cases for usernames and ensure that only one
// entity is getting created
for _, username := range usernames {
secret, err := client.Logical().Write("auth/ldap/login/"+username, map[string]interface{}{
"password": "password",
})
if err != nil {
t.Fatal(err)
}
if secret.Auth.ClientToken == "" {
t.Fatalf("failed to perform login")
}
resp, err := client.Logical().List("identity/entity/id")
if err != nil {
t.Fatal(err)
}
if len(resp.Data["keys"].([]interface{})) != 1 {
t.Fatalf("failed to list entities")
}
}
}