You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
360 lines
9.4 KiB
360 lines
9.4 KiB
package ldap
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"testing"
|
|
|
|
"github.com/seaweedfs/seaweedfs/weed/iam/providers"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
// TestLDAPProviderInitialization tests LDAP provider initialization
|
|
func TestLDAPProviderInitialization(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
config *LDAPConfig
|
|
wantErr bool
|
|
}{
|
|
{
|
|
name: "valid config",
|
|
config: &LDAPConfig{
|
|
Server: "ldap://localhost:389",
|
|
BaseDN: "DC=example,DC=com",
|
|
BindDN: "CN=admin,DC=example,DC=com",
|
|
BindPass: "password",
|
|
UserFilter: "(sAMAccountName=%s)",
|
|
GroupFilter: "(member=%s)",
|
|
},
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "missing server",
|
|
config: &LDAPConfig{
|
|
BaseDN: "DC=example,DC=com",
|
|
},
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "missing base DN",
|
|
config: &LDAPConfig{
|
|
Server: "ldap://localhost:389",
|
|
},
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "invalid server URL",
|
|
config: &LDAPConfig{
|
|
Server: "invalid-url",
|
|
BaseDN: "DC=example,DC=com",
|
|
},
|
|
wantErr: true,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
provider := NewLDAPProvider("test-ldap")
|
|
|
|
err := provider.Initialize(tt.config)
|
|
|
|
if tt.wantErr {
|
|
assert.Error(t, err)
|
|
} else {
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, "test-ldap", provider.Name())
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// TestLDAPProviderAuthentication tests LDAP authentication
|
|
func TestLDAPProviderAuthentication(t *testing.T) {
|
|
// Skip if no LDAP test server available
|
|
if testing.Short() {
|
|
t.Skip("Skipping LDAP integration test in short mode")
|
|
}
|
|
|
|
provider := NewLDAPProvider("test-ldap")
|
|
config := &LDAPConfig{
|
|
Server: "ldap://localhost:389",
|
|
BaseDN: "DC=example,DC=com",
|
|
BindDN: "CN=admin,DC=example,DC=com",
|
|
BindPass: "password",
|
|
UserFilter: "(sAMAccountName=%s)",
|
|
GroupFilter: "(member=%s)",
|
|
Attributes: map[string]string{
|
|
"email": "mail",
|
|
"displayName": "displayName",
|
|
"groups": "memberOf",
|
|
},
|
|
RoleMapping: &providers.RoleMapping{
|
|
Rules: []providers.MappingRule{
|
|
{
|
|
Claim: "groups",
|
|
Value: "*CN=Admins*",
|
|
Role: "arn:seaweed:iam::role/AdminRole",
|
|
},
|
|
{
|
|
Claim: "groups",
|
|
Value: "*CN=Users*",
|
|
Role: "arn:seaweed:iam::role/UserRole",
|
|
},
|
|
},
|
|
DefaultRole: "arn:seaweed:iam::role/GuestRole",
|
|
},
|
|
}
|
|
|
|
err := provider.Initialize(config)
|
|
require.NoError(t, err)
|
|
|
|
t.Run("authenticate with username/password", func(t *testing.T) {
|
|
// This would require an actual LDAP server for integration testing
|
|
credentials := "user:password" // Basic auth format
|
|
|
|
identity, err := provider.Authenticate(context.Background(), credentials)
|
|
if err != nil {
|
|
t.Skip("LDAP server not available for testing")
|
|
}
|
|
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, "user", identity.UserID)
|
|
assert.Equal(t, "test-ldap", identity.Provider)
|
|
assert.NotEmpty(t, identity.Email)
|
|
})
|
|
|
|
t.Run("authenticate with invalid credentials", func(t *testing.T) {
|
|
_, err := provider.Authenticate(context.Background(), "invalid:credentials")
|
|
assert.Error(t, err)
|
|
})
|
|
}
|
|
|
|
// TestLDAPProviderUserInfo tests LDAP user info retrieval
|
|
func TestLDAPProviderUserInfo(t *testing.T) {
|
|
if testing.Short() {
|
|
t.Skip("Skipping LDAP integration test in short mode")
|
|
}
|
|
|
|
provider := NewLDAPProvider("test-ldap")
|
|
config := &LDAPConfig{
|
|
Server: "ldap://localhost:389",
|
|
BaseDN: "DC=example,DC=com",
|
|
BindDN: "CN=admin,DC=example,DC=com",
|
|
BindPass: "password",
|
|
UserFilter: "(sAMAccountName=%s)",
|
|
Attributes: map[string]string{
|
|
"email": "mail",
|
|
"displayName": "displayName",
|
|
"department": "department",
|
|
},
|
|
}
|
|
|
|
err := provider.Initialize(config)
|
|
require.NoError(t, err)
|
|
|
|
t.Run("get user info", func(t *testing.T) {
|
|
identity, err := provider.GetUserInfo(context.Background(), "testuser")
|
|
if err != nil {
|
|
t.Skip("LDAP server not available for testing")
|
|
}
|
|
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, "testuser", identity.UserID)
|
|
assert.Equal(t, "test-ldap", identity.Provider)
|
|
assert.NotEmpty(t, identity.Email)
|
|
assert.NotEmpty(t, identity.DisplayName)
|
|
})
|
|
|
|
t.Run("get user info with empty username", func(t *testing.T) {
|
|
_, err := provider.GetUserInfo(context.Background(), "")
|
|
assert.Error(t, err)
|
|
})
|
|
|
|
t.Run("get user info for non-existent user", func(t *testing.T) {
|
|
_, err := provider.GetUserInfo(context.Background(), "nonexistent")
|
|
assert.Error(t, err)
|
|
})
|
|
}
|
|
|
|
// TestLDAPAttributeMapping tests LDAP attribute mapping
|
|
func TestLDAPAttributeMapping(t *testing.T) {
|
|
provider := NewLDAPProvider("test-ldap")
|
|
config := &LDAPConfig{
|
|
Server: "ldap://localhost:389",
|
|
BaseDN: "DC=example,DC=com",
|
|
Attributes: map[string]string{
|
|
"email": "mail",
|
|
"displayName": "cn",
|
|
"department": "departmentNumber",
|
|
"groups": "memberOf",
|
|
},
|
|
}
|
|
|
|
err := provider.Initialize(config)
|
|
require.NoError(t, err)
|
|
|
|
t.Run("map LDAP attributes to identity", func(t *testing.T) {
|
|
ldapAttrs := map[string][]string{
|
|
"mail": {"user@example.com"},
|
|
"cn": {"John Doe"},
|
|
"departmentNumber": {"IT"},
|
|
"memberOf": {
|
|
"CN=Users,OU=Groups,DC=example,DC=com",
|
|
"CN=Developers,OU=Groups,DC=example,DC=com",
|
|
},
|
|
}
|
|
|
|
identity := provider.mapLDAPAttributes("testuser", ldapAttrs)
|
|
|
|
assert.Equal(t, "testuser", identity.UserID)
|
|
assert.Equal(t, "user@example.com", identity.Email)
|
|
assert.Equal(t, "John Doe", identity.DisplayName)
|
|
assert.Equal(t, "test-ldap", identity.Provider)
|
|
|
|
// Check groups
|
|
assert.Contains(t, identity.Groups, "CN=Users,OU=Groups,DC=example,DC=com")
|
|
assert.Contains(t, identity.Groups, "CN=Developers,OU=Groups,DC=example,DC=com")
|
|
|
|
// Check attributes
|
|
assert.Equal(t, "IT", identity.Attributes["department"])
|
|
})
|
|
}
|
|
|
|
// TestLDAPGroupFiltering tests LDAP group filtering and role mapping
|
|
func TestLDAPGroupFiltering(t *testing.T) {
|
|
provider := NewLDAPProvider("test-ldap")
|
|
config := &LDAPConfig{
|
|
Server: "ldap://localhost:389",
|
|
BaseDN: "DC=example,DC=com",
|
|
RoleMapping: &providers.RoleMapping{
|
|
Rules: []providers.MappingRule{
|
|
{
|
|
Claim: "groups",
|
|
Value: "*Admins*",
|
|
Role: "arn:seaweed:iam::role/AdminRole",
|
|
},
|
|
{
|
|
Claim: "groups",
|
|
Value: "*Users*",
|
|
Role: "arn:seaweed:iam::role/UserRole",
|
|
},
|
|
},
|
|
DefaultRole: "arn:seaweed:iam::role/GuestRole",
|
|
},
|
|
}
|
|
|
|
err := provider.Initialize(config)
|
|
require.NoError(t, err)
|
|
|
|
tests := []struct {
|
|
name string
|
|
groups []string
|
|
expectedRole string
|
|
expectedClaims map[string]interface{}
|
|
}{
|
|
{
|
|
name: "admin user",
|
|
groups: []string{"CN=Admins,OU=Groups,DC=example,DC=com", "CN=Users,OU=Groups,DC=example,DC=com"},
|
|
expectedRole: "arn:seaweed:iam::role/AdminRole",
|
|
},
|
|
{
|
|
name: "regular user",
|
|
groups: []string{"CN=Users,OU=Groups,DC=example,DC=com"},
|
|
expectedRole: "arn:seaweed:iam::role/UserRole",
|
|
},
|
|
{
|
|
name: "guest user",
|
|
groups: []string{"CN=Guests,OU=Groups,DC=example,DC=com"},
|
|
expectedRole: "arn:seaweed:iam::role/GuestRole",
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
identity := &providers.ExternalIdentity{
|
|
UserID: "testuser",
|
|
Groups: tt.groups,
|
|
Provider: "test-ldap",
|
|
}
|
|
|
|
role := provider.mapUserToRole(identity)
|
|
assert.Equal(t, tt.expectedRole, role)
|
|
})
|
|
}
|
|
}
|
|
|
|
// TestLDAPConnectionPool tests LDAP connection pooling
|
|
func TestLDAPConnectionPool(t *testing.T) {
|
|
if testing.Short() {
|
|
t.Skip("Skipping LDAP connection pool test in short mode")
|
|
}
|
|
|
|
provider := NewLDAPProvider("test-ldap")
|
|
config := &LDAPConfig{
|
|
Server: "ldap://localhost:389",
|
|
BaseDN: "DC=example,DC=com",
|
|
BindDN: "CN=admin,DC=example,DC=com",
|
|
BindPass: "password",
|
|
MaxConnections: 5,
|
|
ConnTimeout: 30,
|
|
}
|
|
|
|
err := provider.Initialize(config)
|
|
require.NoError(t, err)
|
|
|
|
t.Run("connection pool management", func(t *testing.T) {
|
|
// Test that multiple concurrent requests work
|
|
// This would require actual LDAP server for full testing
|
|
pool := provider.getConnectionPool()
|
|
|
|
// In CI environments where no LDAP server is available, pool might be nil
|
|
// Skip the test if we can't establish a connection
|
|
conn, err := provider.getConnection()
|
|
if err != nil {
|
|
t.Skip("LDAP server not available - skipping connection pool test")
|
|
return
|
|
}
|
|
|
|
// Only test if we successfully got a connection
|
|
assert.NotNil(t, pool)
|
|
assert.NotNil(t, conn)
|
|
provider.releaseConnection(conn)
|
|
})
|
|
}
|
|
|
|
// MockLDAPServer for unit testing (without external dependencies)
|
|
type MockLDAPServer struct {
|
|
users map[string]map[string][]string
|
|
}
|
|
|
|
func NewMockLDAPServer() *MockLDAPServer {
|
|
return &MockLDAPServer{
|
|
users: map[string]map[string][]string{
|
|
"testuser": {
|
|
"mail": {"testuser@example.com"},
|
|
"cn": {"Test User"},
|
|
"department": {"Engineering"},
|
|
"memberOf": {"CN=Users,OU=Groups,DC=example,DC=com"},
|
|
},
|
|
"admin": {
|
|
"mail": {"admin@example.com"},
|
|
"cn": {"Administrator"},
|
|
"department": {"IT"},
|
|
"memberOf": {"CN=Admins,OU=Groups,DC=example,DC=com", "CN=Users,OU=Groups,DC=example,DC=com"},
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
func (m *MockLDAPServer) Authenticate(username, password string) bool {
|
|
_, exists := m.users[username]
|
|
return exists && password == "password" // Mock authentication
|
|
}
|
|
|
|
func (m *MockLDAPServer) GetUserAttributes(username string) (map[string][]string, error) {
|
|
if attrs, exists := m.users[username]; exists {
|
|
return attrs, nil
|
|
}
|
|
return nil, fmt.Errorf("user not found: %s", username)
|
|
}
|