Case insensitive behavior for LDAP (#4238)

This commit is contained in:
Jeff Mitchell
2018-04-03 09:52:43 -04:00
committed by GitHub
parent a4fc5a40cd
commit 266a57fab2
7 changed files with 365 additions and 59 deletions

View File

@@ -15,6 +15,11 @@ DEPRECATIONS/CHANGES:
accommodate this as best as possible, and users of other tools may have to accommodate this as best as possible, and users of other tools may have to
make adjustments, but in the end we felt that the ends did not justify the make adjustments, but in the end we felt that the ends did not justify the
means and we needed to prioritize security over operational convenience. means and we needed to prioritize security over operational convenience.
* LDAP auth method case sensitivity: We now treat usernames and groups
configured locally for policy assignment in a case insensitive fashion by
default. Existing configurations will continue to work as they do now;
however, the next time a configuration is written `case_sensitive_names`
will need to be explicitly set to `true`.
FEATURES: FEATURES:

View File

@@ -5,6 +5,7 @@ import (
"context" "context"
"fmt" "fmt"
"math" "math"
"strings"
"text/template" "text/template"
"github.com/go-ldap/ldap" "github.com/go-ldap/ldap"
@@ -173,8 +174,13 @@ func (b *backend) Login(ctx context.Context, req *logical.Request, username stri
} }
var allGroups []string var allGroups []string
canonicalUsername := username
cs := *cfg.CaseSensitiveNames
if !cs {
canonicalUsername = strings.ToLower(username)
}
// Import the custom added groups from ldap backend // Import the custom added groups from ldap backend
user, err := b.User(ctx, req.Storage, username) user, err := b.User(ctx, req.Storage, canonicalUsername)
if err == nil && user != nil && user.Groups != nil { if err == nil && user != nil && user.Groups != nil {
if b.Logger().IsDebug() { if b.Logger().IsDebug() {
b.Logger().Debug("adding local groups", "num_local_groups", len(user.Groups), "local_groups", user.Groups) b.Logger().Debug("adding local groups", "num_local_groups", len(user.Groups), "local_groups", user.Groups)
@@ -184,9 +190,18 @@ func (b *backend) Login(ctx context.Context, req *logical.Request, username stri
// Merge local and LDAP groups // Merge local and LDAP groups
allGroups = append(allGroups, ldapGroups...) allGroups = append(allGroups, ldapGroups...)
canonicalGroups := allGroups
// If not case sensitive, lowercase all
if !cs {
canonicalGroups = make([]string, len(allGroups))
for i, v := range allGroups {
canonicalGroups[i] = strings.ToLower(v)
}
}
// Retrieve policies // Retrieve policies
var policies []string var policies []string
for _, groupName := range allGroups { for _, groupName := range canonicalGroups {
group, err := b.Group(ctx, req.Storage, groupName) group, err := b.Group(ctx, req.Storage, groupName)
if err == nil && group != nil { if err == nil && group != nil {
policies = append(policies, group.Policies...) policies = append(policies, group.Policies...)

View File

@@ -31,6 +31,182 @@ func createBackendWithStorage(t *testing.T) (*backend, logical.Storage) {
return b, config.StorageView return b, config.StorageView
} }
func TestLdapAuthBackend_CaseSensitivity(t *testing.T) {
var resp *logical.Response
var err error
b, storage := createBackendWithStorage(t)
ctx := context.Background()
testVals := func(caseSensitive bool) {
// Clear storage
userList, err := storage.List(ctx, "user/")
if err != nil {
t.Fatal(err)
}
for _, user := range userList {
err = storage.Delete(ctx, "user/"+user)
if err != nil {
t.Fatal(err)
}
}
groupList, err := storage.List(ctx, "group/")
if err != nil {
t.Fatal(err)
}
for _, group := range groupList {
err = storage.Delete(ctx, "group/"+group)
if err != nil {
t.Fatal(err)
}
}
configReq := &logical.Request{
Path: "config",
Operation: logical.ReadOperation,
Storage: storage,
}
resp, err = b.HandleRequest(ctx, configReq)
if err != nil || (resp != nil && resp.IsError()) {
t.Fatalf("err:%v resp:%#v", err, resp)
}
if resp == nil {
t.Fatal("nil response")
}
if resp.Data["case_sensitive_names"].(bool) != caseSensitive {
t.Fatalf("expected case sensitivity %t, got %t", caseSensitive, resp.Data["case_sensitive_names"].(bool))
}
groupReq := &logical.Request{
Operation: logical.UpdateOperation,
Data: map[string]interface{}{
"policies": "grouppolicy",
},
Path: "groups/EngineerS",
Storage: storage,
}
resp, err = b.HandleRequest(ctx, groupReq)
if err != nil || (resp != nil && resp.IsError()) {
t.Fatalf("err:%v resp:%#v", err, resp)
}
keys, err := storage.List(ctx, "group/")
if err != nil {
t.Fatal(err)
}
switch caseSensitive {
case true:
if keys[0] != "EngineerS" {
t.Fatalf("bad: %s", keys[0])
}
default:
if keys[0] != "engineers" {
t.Fatalf("bad: %s", keys[0])
}
}
userReq := &logical.Request{
Operation: logical.UpdateOperation,
Data: map[string]interface{}{
"groups": "EngineerS",
"policies": "userpolicy",
},
Path: "users/teSlA",
Storage: storage,
}
resp, err = b.HandleRequest(ctx, userReq)
if err != nil || (resp != nil && resp.IsError()) {
t.Fatalf("err:%v resp:%#v", err, resp)
}
keys, err = storage.List(ctx, "user/")
if err != nil {
t.Fatal(err)
}
switch caseSensitive {
case true:
if keys[0] != "teSlA" {
t.Fatalf("bad: %s", keys[0])
}
default:
if keys[0] != "tesla" {
t.Fatalf("bad: %s", keys[0])
}
}
if caseSensitive {
// The online test server is actually case sensitive so we need to
// write again so it works
userReq = &logical.Request{
Operation: logical.UpdateOperation,
Data: map[string]interface{}{
"groups": "EngineerS",
"policies": "userpolicy",
},
Path: "users/tesla",
Storage: storage,
}
resp, err = b.HandleRequest(ctx, userReq)
if err != nil || (resp != nil && resp.IsError()) {
t.Fatalf("err:%v resp:%#v", err, resp)
}
}
loginReq := &logical.Request{
Operation: logical.UpdateOperation,
Path: "login/tesla",
Data: map[string]interface{}{
"password": "password",
},
Storage: storage,
}
resp, err = b.HandleRequest(ctx, loginReq)
if err != nil || (resp != nil && resp.IsError()) {
t.Fatalf("err:%v resp:%#v", err, resp)
}
expected := []string{"grouppolicy", "userpolicy"}
if !reflect.DeepEqual(expected, resp.Auth.Policies) {
t.Fatalf("bad: policies: expected: %q, actual: %q", expected, resp.Auth.Policies)
}
}
configReq := &logical.Request{
Operation: logical.UpdateOperation,
Path: "config",
Data: map[string]interface{}{
// Online LDAP test server
// http://www.forumsys.com/tutorials/integration-how-to/ldap/online-ldap-test-server/
"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",
},
Storage: storage,
}
resp, err = b.HandleRequest(ctx, configReq)
if err != nil || (resp != nil && resp.IsError()) {
t.Fatalf("err:%v resp:%#v", err, resp)
}
testVals(false)
// Check that if the value is nil, on read it is case sensitive
configEntry, err := b.Config(ctx, configReq)
if err != nil {
t.Fatal(err)
}
configEntry.CaseSensitiveNames = nil
entry, err := logical.StorageEntryJSON("config", configEntry)
if err != nil {
t.Fatal(err)
}
err = configReq.Storage.Put(ctx, entry)
if err != nil {
t.Fatal(err)
}
testVals(true)
}
func TestLdapAuthBackend_UserPolicies(t *testing.T) { func TestLdapAuthBackend_UserPolicies(t *testing.T) {
var resp *logical.Response var resp *logical.Response
var err error var err error
@@ -278,10 +454,11 @@ func testAccStepConfigUrl(t *testing.T) logicaltest.TestStep {
Data: map[string]interface{}{ Data: map[string]interface{}{
// Online LDAP test server // Online LDAP test server
// http://www.forumsys.com/tutorials/integration-how-to/ldap/online-ldap-test-server/ // http://www.forumsys.com/tutorials/integration-how-to/ldap/online-ldap-test-server/
"url": "ldap://ldap.forumsys.com", "url": "ldap://ldap.forumsys.com",
"userattr": "uid", "userattr": "uid",
"userdn": "dc=example,dc=com", "userdn": "dc=example,dc=com",
"groupdn": "dc=example,dc=com", "groupdn": "dc=example,dc=com",
"case_sensitive_names": true,
}, },
} }
} }
@@ -294,12 +471,13 @@ func testAccStepConfigUrlWithAuthBind(t *testing.T) logicaltest.TestStep {
// Online LDAP test server // Online LDAP test server
// http://www.forumsys.com/tutorials/integration-how-to/ldap/online-ldap-test-server/ // http://www.forumsys.com/tutorials/integration-how-to/ldap/online-ldap-test-server/
// In this test we also exercise multiple URL support // In this test we also exercise multiple URL support
"url": "foobar://ldap.example.com,ldap://ldap.forumsys.com", "url": "foobar://ldap.example.com,ldap://ldap.forumsys.com",
"userattr": "uid", "userattr": "uid",
"userdn": "dc=example,dc=com", "userdn": "dc=example,dc=com",
"groupdn": "dc=example,dc=com", "groupdn": "dc=example,dc=com",
"binddn": "cn=read-only-admin,dc=example,dc=com", "binddn": "cn=read-only-admin,dc=example,dc=com",
"bindpass": "password", "bindpass": "password",
"case_sensitive_names": true,
}, },
} }
} }
@@ -311,11 +489,12 @@ func testAccStepConfigUrlWithDiscover(t *testing.T) logicaltest.TestStep {
Data: map[string]interface{}{ Data: map[string]interface{}{
// Online LDAP test server // Online LDAP test server
// http://www.forumsys.com/tutorials/integration-how-to/ldap/online-ldap-test-server/ // http://www.forumsys.com/tutorials/integration-how-to/ldap/online-ldap-test-server/
"url": "ldap://ldap.forumsys.com", "url": "ldap://ldap.forumsys.com",
"userattr": "uid", "userattr": "uid",
"userdn": "dc=example,dc=com", "userdn": "dc=example,dc=com",
"groupdn": "dc=example,dc=com", "groupdn": "dc=example,dc=com",
"discoverdn": true, "discoverdn": true,
"case_sensitive_names": true,
}, },
} }
} }
@@ -327,10 +506,11 @@ func testAccStepConfigUrlNoGroupDN(t *testing.T) logicaltest.TestStep {
Data: map[string]interface{}{ Data: map[string]interface{}{
// Online LDAP test server // Online LDAP test server
// http://www.forumsys.com/tutorials/integration-how-to/ldap/online-ldap-test-server/ // http://www.forumsys.com/tutorials/integration-how-to/ldap/online-ldap-test-server/
"url": "ldap://ldap.forumsys.com", "url": "ldap://ldap.forumsys.com",
"userattr": "uid", "userattr": "uid",
"userdn": "dc=example,dc=com", "userdn": "dc=example,dc=com",
"discoverdn": true, "discoverdn": true,
"case_sensitive_names": true,
}, },
} }
} }

View File

@@ -14,6 +14,7 @@ import (
"github.com/go-ldap/ldap" "github.com/go-ldap/ldap"
log "github.com/hashicorp/go-hclog" log "github.com/hashicorp/go-hclog"
multierror "github.com/hashicorp/go-multierror" multierror "github.com/hashicorp/go-multierror"
"github.com/hashicorp/vault/helper/consts"
"github.com/hashicorp/vault/helper/tlsutil" "github.com/hashicorp/vault/helper/tlsutil"
"github.com/hashicorp/vault/logical" "github.com/hashicorp/vault/logical"
"github.com/hashicorp/vault/logical/framework" "github.com/hashicorp/vault/logical/framework"
@@ -109,11 +110,17 @@ Default: cn`,
Default: "tls12", Default: "tls12",
Description: "Maximum TLS version to use. Accepted values are 'tls10', 'tls11' or 'tls12'. Defaults to 'tls12'", Description: "Maximum TLS version to use. Accepted values are 'tls10', 'tls11' or 'tls12'. Defaults to 'tls12'",
}, },
"deny_null_bind": &framework.FieldSchema{ "deny_null_bind": &framework.FieldSchema{
Type: framework.TypeBool, Type: framework.TypeBool,
Default: true, Default: true,
Description: "Denies an unauthenticated LDAP bind request if the user's password is empty; defaults to true", Description: "Denies an unauthenticated LDAP bind request if the user's password is empty; defaults to true",
}, },
"case_sensitive_names": &framework.FieldSchema{
Type: framework.TypeBool,
Description: "If true, case sensitivity will be used when comparing usernames and groups for matching policies.",
},
}, },
Callbacks: map[logical.Operation]framework.OperationFunc{ Callbacks: map[logical.Operation]framework.OperationFunc{
@@ -149,6 +156,9 @@ func (b *backend) Config(ctx context.Context, req *logical.Request) (*ConfigEntr
if storedConfig == nil { if storedConfig == nil {
// No user overrides, return default configuration // No user overrides, return default configuration
result.CaseSensitiveNames = new(bool)
*result.CaseSensitiveNames = false
return result, nil return result, nil
} }
@@ -158,6 +168,24 @@ func (b *backend) Config(ctx context.Context, req *logical.Request) (*ConfigEntr
return nil, err return nil, err
} }
var persistNeeded bool
if result.CaseSensitiveNames == nil {
// Upgrade from before switching to case-insensitive
result.CaseSensitiveNames = new(bool)
*result.CaseSensitiveNames = true
persistNeeded = true
}
if persistNeeded && (b.System().LocalMount() || !b.System().ReplicationState().HasState(consts.ReplicationPerformanceSecondary)) {
entry, err := logical.StorageEntryJSON("config", result)
if err != nil {
return nil, err
}
if err := req.Storage.Put(ctx, entry); err != nil {
return nil, err
}
}
result.logger = b.Logger() result.logger = b.Logger()
return result, nil return result, nil
@@ -174,21 +202,22 @@ func (b *backend) pathConfigRead(ctx context.Context, req *logical.Request, d *f
resp := &logical.Response{ resp := &logical.Response{
Data: map[string]interface{}{ Data: map[string]interface{}{
"url": cfg.Url, "url": cfg.Url,
"userdn": cfg.UserDN, "userdn": cfg.UserDN,
"groupdn": cfg.GroupDN, "groupdn": cfg.GroupDN,
"groupfilter": cfg.GroupFilter, "groupfilter": cfg.GroupFilter,
"groupattr": cfg.GroupAttr, "groupattr": cfg.GroupAttr,
"upndomain": cfg.UPNDomain, "upndomain": cfg.UPNDomain,
"userattr": cfg.UserAttr, "userattr": cfg.UserAttr,
"certificate": cfg.Certificate, "certificate": cfg.Certificate,
"insecure_tls": cfg.InsecureTLS, "insecure_tls": cfg.InsecureTLS,
"starttls": cfg.StartTLS, "starttls": cfg.StartTLS,
"binddn": cfg.BindDN, "binddn": cfg.BindDN,
"deny_null_bind": cfg.DenyNullBind, "deny_null_bind": cfg.DenyNullBind,
"discoverdn": cfg.DiscoverDN, "discoverdn": cfg.DiscoverDN,
"tls_min_version": cfg.TLSMinVersion, "tls_min_version": cfg.TLSMinVersion,
"tls_max_version": cfg.TLSMaxVersion, "tls_max_version": cfg.TLSMaxVersion,
"case_sensitive_names": *cfg.CaseSensitiveNames,
}, },
} }
return resp, nil return resp, nil
@@ -282,23 +311,33 @@ func (b *backend) newConfigEntry(d *framework.FieldData) (*ConfigEntry, error) {
if startTLS { if startTLS {
cfg.StartTLS = startTLS cfg.StartTLS = startTLS
} }
bindDN := d.Get("binddn").(string) bindDN := d.Get("binddn").(string)
if bindDN != "" { if bindDN != "" {
cfg.BindDN = bindDN cfg.BindDN = bindDN
} }
bindPass := d.Get("bindpass").(string) bindPass := d.Get("bindpass").(string)
if bindPass != "" { if bindPass != "" {
cfg.BindPassword = bindPass cfg.BindPassword = bindPass
} }
denyNullBind := d.Get("deny_null_bind").(bool) denyNullBind := d.Get("deny_null_bind").(bool)
if denyNullBind { if denyNullBind {
cfg.DenyNullBind = denyNullBind cfg.DenyNullBind = denyNullBind
} }
discoverDN := d.Get("discoverdn").(bool) discoverDN := d.Get("discoverdn").(bool)
if discoverDN { if discoverDN {
cfg.DiscoverDN = discoverDN cfg.DiscoverDN = discoverDN
} }
caseSensitiveNames, ok := d.GetOk("case_sensitive_names")
if ok {
cfg.CaseSensitiveNames = new(bool)
*cfg.CaseSensitiveNames = caseSensitiveNames.(bool)
}
return cfg, nil return cfg, nil
} }
@@ -309,6 +348,13 @@ func (b *backend) pathConfigWrite(ctx context.Context, req *logical.Request, d *
return logical.ErrorResponse(err.Error()), nil return logical.ErrorResponse(err.Error()), nil
} }
// On write, if not specified, use false. We do this here so upgrade logic
// works since it calls the same newConfigEntry function
if cfg.CaseSensitiveNames == nil {
cfg.CaseSensitiveNames = new(bool)
*cfg.CaseSensitiveNames = false
}
entry, err := logical.StorageEntryJSON("config", cfg) entry, err := logical.StorageEntryJSON("config", cfg)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -321,23 +367,24 @@ func (b *backend) pathConfigWrite(ctx context.Context, req *logical.Request, d *
} }
type ConfigEntry struct { type ConfigEntry struct {
logger log.Logger logger log.Logger
Url string `json:"url" structs:"url" mapstructure:"url"` Url string `json:"url"`
UserDN string `json:"userdn" structs:"userdn" mapstructure:"userdn"` UserDN string `json:"userdn"`
GroupDN string `json:"groupdn" structs:"groupdn" mapstructure:"groupdn"` GroupDN string `json:"groupdn"`
GroupFilter string `json:"groupfilter" structs:"groupfilter" mapstructure:"groupfilter"` GroupFilter string `json:"groupfilter"`
GroupAttr string `json:"groupattr" structs:"groupattr" mapstructure:"groupattr"` GroupAttr string `json:"groupattr"`
UPNDomain string `json:"upndomain" structs:"upndomain" mapstructure:"upndomain"` UPNDomain string `json:"upndomain"`
UserAttr string `json:"userattr" structs:"userattr" mapstructure:"userattr"` UserAttr string `json:"userattr"`
Certificate string `json:"certificate" structs:"certificate" mapstructure:"certificate"` Certificate string `json:"certificate"`
InsecureTLS bool `json:"insecure_tls" structs:"insecure_tls" mapstructure:"insecure_tls"` InsecureTLS bool `json:"insecure_tls"`
StartTLS bool `json:"starttls" structs:"starttls" mapstructure:"starttls"` StartTLS bool `json:"starttls"`
BindDN string `json:"binddn" structs:"binddn" mapstructure:"binddn"` BindDN string `json:"binddn"`
BindPassword string `json:"bindpass" structs:"bindpass" mapstructure:"bindpass"` BindPassword string `json:"bindpass"`
DenyNullBind bool `json:"deny_null_bind" structs:"deny_null_bind" mapstructure:"deny_null_bind"` DenyNullBind bool `json:"deny_null_bind"`
DiscoverDN bool `json:"discoverdn" structs:"discoverdn" mapstructure:"discoverdn"` DiscoverDN bool `json:"discoverdn"`
TLSMinVersion string `json:"tls_min_version" structs:"tls_min_version" mapstructure:"tls_min_version"` TLSMinVersion string `json:"tls_min_version"`
TLSMaxVersion string `json:"tls_max_version" structs:"tls_max_version" mapstructure:"tls_max_version"` TLSMaxVersion string `json:"tls_max_version"`
CaseSensitiveNames *bool `json:"case_sensitive_names,omitempty`
} }
func (c *ConfigEntry) GetTLSConfig(host string) (*tls.Config, error) { func (c *ConfigEntry) GetTLSConfig(host string) (*tls.Config, error) {

View File

@@ -2,6 +2,7 @@ package ldap
import ( import (
"context" "context"
"strings"
"github.com/hashicorp/vault/helper/policyutil" "github.com/hashicorp/vault/helper/policyutil"
"github.com/hashicorp/vault/logical" "github.com/hashicorp/vault/logical"
@@ -74,7 +75,20 @@ func (b *backend) pathGroupDelete(ctx context.Context, req *logical.Request, d *
} }
func (b *backend) pathGroupRead(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) { func (b *backend) pathGroupRead(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
group, err := b.Group(ctx, req.Storage, d.Get("name").(string)) groupname := d.Get("name").(string)
cfg, err := b.Config(ctx, req)
if err != nil {
return nil, err
}
if cfg == nil {
return logical.ErrorResponse("ldap backend not configured"), nil
}
if !*cfg.CaseSensitiveNames {
groupname = strings.ToLower(groupname)
}
group, err := b.Group(ctx, req.Storage, groupname)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -90,8 +104,21 @@ func (b *backend) pathGroupRead(ctx context.Context, req *logical.Request, d *fr
} }
func (b *backend) pathGroupWrite(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) { func (b *backend) pathGroupWrite(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
groupname := d.Get("name").(string)
cfg, err := b.Config(ctx, req)
if err != nil {
return nil, err
}
if cfg == nil {
return logical.ErrorResponse("ldap backend not configured"), nil
}
if !*cfg.CaseSensitiveNames {
groupname = strings.ToLower(groupname)
}
// Store it // Store it
entry, err := logical.StorageEntryJSON("group/"+d.Get("name").(string), &GroupEntry{ entry, err := logical.StorageEntryJSON("group/"+groupname, &GroupEntry{
Policies: policyutil.ParsePolicies(d.Get("policies")), Policies: policyutil.ParsePolicies(d.Get("policies")),
}) })
if err != nil { if err != nil {

View File

@@ -81,7 +81,20 @@ func (b *backend) pathUserDelete(ctx context.Context, req *logical.Request, d *f
} }
func (b *backend) pathUserRead(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) { func (b *backend) pathUserRead(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
user, err := b.User(ctx, req.Storage, d.Get("name").(string)) username := d.Get("name").(string)
cfg, err := b.Config(ctx, req)
if err != nil {
return nil, err
}
if cfg == nil {
return logical.ErrorResponse("ldap backend not configured"), nil
}
if !*cfg.CaseSensitiveNames {
username = strings.ToLower(username)
}
user, err := b.User(ctx, req.Storage, username)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -98,15 +111,29 @@ func (b *backend) pathUserRead(ctx context.Context, req *logical.Request, d *fra
} }
func (b *backend) pathUserWrite(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) { func (b *backend) pathUserWrite(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
name := d.Get("name").(string) lowercaseGroups := false
groups := strutil.RemoveDuplicates(strutil.ParseStringSlice(d.Get("groups").(string), ","), false) username := d.Get("name").(string)
cfg, err := b.Config(ctx, req)
if err != nil {
return nil, err
}
if cfg == nil {
return logical.ErrorResponse("ldap backend not configured"), nil
}
if !*cfg.CaseSensitiveNames {
username = strings.ToLower(username)
lowercaseGroups = true
}
groups := strutil.RemoveDuplicates(strutil.ParseStringSlice(d.Get("groups").(string), ","), lowercaseGroups)
policies := policyutil.ParsePolicies(d.Get("policies")) policies := policyutil.ParsePolicies(d.Get("policies"))
for i, g := range groups { for i, g := range groups {
groups[i] = strings.TrimSpace(g) groups[i] = strings.TrimSpace(g)
} }
// Store it // Store it
entry, err := logical.StorageEntryJSON("user/"+name, &UserEntry{ entry, err := logical.StorageEntryJSON("user/"+username, &UserEntry{
Groups: groups, Groups: groups,
Policies: policies, Policies: policies,
}) })

View File

@@ -28,6 +28,11 @@ This endpoint configures the LDAP auth method.
- `url` `(string: <required>)` The LDAP server to connect to. Examples: - `url` `(string: <required>)` The LDAP server to connect to. Examples:
`ldap://ldap.myorg.com`, `ldaps://ldap.myorg.com:636` `ldap://ldap.myorg.com`, `ldaps://ldap.myorg.com:636`
- `case_sensitive_names` `(bool: false)` If set, user and group names
assigned to policies within the backend will be case sensitive. Otherwise,
names will be normalized to lower case. Case will still be preserved when
sending the username to the LDAP server at login time; this is only for
matching local user/group definitions.
- `starttls` `(bool: false)` If true, issues a `StartTLS` command after - `starttls` `(bool: false)` If true, issues a `StartTLS` command after
establishing an unencrypted connection. establishing an unencrypted connection.
- `tls_min_version` `(string: tls12)` Minimum TLS version to use. Accepted - `tls_min_version` `(string: tls12)` Minimum TLS version to use. Accepted