528 lines
18 KiB
Go
528 lines
18 KiB
Go
package auth
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"testing"
|
|
|
|
"github.com/alicebob/miniredis/v2"
|
|
"github.com/redis/go-redis/v9"
|
|
|
|
"github.com/gochat/gochat/internal/config"
|
|
"github.com/gochat/gochat/internal/model"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
"gorm.io/driver/sqlite"
|
|
"gorm.io/gorm"
|
|
"gorm.io/gorm/logger"
|
|
)
|
|
|
|
// --- Helpers for LDAP tests ---
|
|
|
|
func newTestLDAPDB(t *testing.T) *gorm.DB {
|
|
t.Helper()
|
|
db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{
|
|
Logger: logger.Default.LogMode(logger.Silent),
|
|
})
|
|
require.NoError(t, err)
|
|
err = db.AutoMigrate(&model.AccountLDAPSettings{}, &model.User{}, &model.Account{}, &model.AccountUser{})
|
|
require.NoError(t, err)
|
|
return db
|
|
}
|
|
|
|
func newTestLDAPConfig(enabled bool) *config.LDAPConfig {
|
|
return &config.LDAPConfig{
|
|
Enabled: enabled,
|
|
DefaultHost: "localhost",
|
|
DefaultPort: 389,
|
|
DefaultUseTLS: false,
|
|
DefaultBaseDN: "dc=example,dc=com",
|
|
DefaultBindDN: "cn=admin,dc=example,dc=com",
|
|
DefaultBindPassword: "adminpassword",
|
|
DefaultUserFilter: "(uid=%s)",
|
|
DefaultEmailAttribute: "mail",
|
|
DefaultNameAttribute: "cn",
|
|
DefaultGroupAttribute: "memberOf",
|
|
SyncInterval: 300,
|
|
}
|
|
}
|
|
|
|
// --- NewLDAPService tests ---
|
|
|
|
func TestNewLDAPService_Disabled(t *testing.T) {
|
|
mr := miniredis.RunT(t)
|
|
rdb := redis.NewClient(&redis.Options{Addr: mr.Addr()})
|
|
defer rdb.Close()
|
|
|
|
db := newTestLDAPDB(t)
|
|
cfg := newTestLDAPConfig(false)
|
|
svc, err := NewLDAPService(cfg, rdb, db)
|
|
assert.NoError(t, err, "disabled LDAP should not error")
|
|
assert.NotNil(t, svc)
|
|
}
|
|
|
|
func TestNewLDAPService_EnabledValidConfig(t *testing.T) {
|
|
mr := miniredis.RunT(t)
|
|
rdb := redis.NewClient(&redis.Options{Addr: mr.Addr()})
|
|
defer rdb.Close()
|
|
|
|
db := newTestLDAPDB(t)
|
|
cfg := newTestLDAPConfig(true)
|
|
svc, err := NewLDAPService(cfg, rdb, db)
|
|
assert.NoError(t, err, "valid LDAP config should not error")
|
|
assert.NotNil(t, svc)
|
|
}
|
|
|
|
func TestNewLDAPService_MissingBaseDN(t *testing.T) {
|
|
mr := miniredis.RunT(t)
|
|
rdb := redis.NewClient(&redis.Options{Addr: mr.Addr()})
|
|
defer rdb.Close()
|
|
|
|
db := newTestLDAPDB(t)
|
|
cfg := newTestLDAPConfig(true)
|
|
cfg.DefaultHost = "ldap.example.com"
|
|
cfg.DefaultBaseDN = ""
|
|
_, err := NewLDAPService(cfg, rdb, db)
|
|
assert.ErrorIs(t, err, ErrLDAPInvalidConfig, "host set but empty BaseDN should error")
|
|
}
|
|
|
|
func TestNewLDAPService_EmptyHost(t *testing.T) {
|
|
mr := miniredis.RunT(t)
|
|
rdb := redis.NewClient(&redis.Options{Addr: mr.Addr()})
|
|
defer rdb.Close()
|
|
|
|
db := newTestLDAPDB(t)
|
|
cfg := newTestLDAPConfig(true)
|
|
cfg.DefaultHost = ""
|
|
// Empty host with BaseDN set — should succeed (per-account config can override)
|
|
svc, err := NewLDAPService(cfg, rdb, db)
|
|
assert.NoError(t, err, "empty host is valid when BaseDN is also empty (defer to per-account)")
|
|
assert.NotNil(t, svc)
|
|
}
|
|
|
|
// --- extractCNFromDN tests ---
|
|
|
|
func TestExtractCNFromDN(t *testing.T) {
|
|
assert.Equal(t, "admins", extractCNFromDN("cn=admins,ou=groups,dc=example,dc=com"))
|
|
assert.Equal(t, "John Doe", extractCNFromDN("cn=John Doe,ou=people,dc=example,dc=com"))
|
|
assert.Equal(t, "user1", extractCNFromDN("CN=user1,dc=example,dc=com"), "case-insensitive CN prefix")
|
|
}
|
|
|
|
func TestExtractCNFromDN_NoCN(t *testing.T) {
|
|
// No cn= prefix in DN — should return full DN as fallback
|
|
assert.Equal(t, "ou=groups,dc=example,dc=com", extractCNFromDN("ou=groups,dc=example,dc=com"))
|
|
}
|
|
|
|
func TestExtractCNFromDN_Empty(t *testing.T) {
|
|
assert.Equal(t, "", extractCNFromDN(""))
|
|
}
|
|
|
|
func TestExtractCNFromDN_SingleCN(t *testing.T) {
|
|
assert.Equal(t, "admin", extractCNFromDN("cn=admin"))
|
|
}
|
|
|
|
// --- getAttr tests ---
|
|
|
|
func TestGetAttr(t *testing.T) {
|
|
attrs := map[string]string{"mail": "user@example.com", "cn": "Test User"}
|
|
|
|
assert.Equal(t, "user@example.com", getAttr(attrs, "mail", "fallback"))
|
|
assert.Equal(t, "Test User", getAttr(attrs, "cn", "fallback"))
|
|
assert.Equal(t, "fallback", getAttr(attrs, "missing", "fallback"), "missing key should return fallback")
|
|
assert.Equal(t, "", getAttr(attrs, "missing", ""), "missing key with empty fallback should return empty string")
|
|
}
|
|
|
|
// --- MapLDAPGroupsToRoles tests ---
|
|
|
|
func TestMapLDAPGroupsToRoles_AdminGroup(t *testing.T) {
|
|
settings := &model.AccountLDAPSettings{
|
|
RoleMappings: json.RawMessage(`{"admins": "administrator", "developers": "agent"}`),
|
|
}
|
|
|
|
role := (&LDAPService{}).MapLDAPGroupsToRoles(settings, []string{"cn=admins,ou=groups,dc=example,dc=com"})
|
|
assert.Equal(t, "administrator", role, "admins group should map to administrator")
|
|
}
|
|
|
|
func TestMapLDAPGroupsToRoles_MultipleGroups(t *testing.T) {
|
|
settings := &model.AccountLDAPSettings{
|
|
RoleMappings: json.RawMessage(`{"admins": "administrator", "developers": "agent", "managers": "supervisor"}`),
|
|
}
|
|
|
|
role := (&LDAPService{}).MapLDAPGroupsToRoles(settings, []string{
|
|
"cn=developers,ou=groups,dc=example,dc=com",
|
|
"cn=admins,ou=groups,dc=example,dc=com",
|
|
})
|
|
assert.Equal(t, "administrator", role, "highest privilege group should win")
|
|
}
|
|
|
|
func TestMapLDAPGroupsToRoles_SupervisorAndAgent(t *testing.T) {
|
|
settings := &model.AccountLDAPSettings{
|
|
RoleMappings: json.RawMessage(`{"managers": "supervisor", "developers": "agent"}`),
|
|
}
|
|
|
|
role := (&LDAPService{}).MapLDAPGroupsToRoles(settings, []string{
|
|
"cn=managers,ou=groups,dc=example,dc=com",
|
|
"cn=developers,ou=groups,dc=example,dc=com",
|
|
})
|
|
assert.Equal(t, "supervisor", role, "supervisor should beat agent")
|
|
}
|
|
|
|
func TestMapLDAPGroupsToRoles_NoMappings(t *testing.T) {
|
|
settings := &model.AccountLDAPSettings{
|
|
RoleMappings: nil,
|
|
}
|
|
|
|
role := (&LDAPService{}).MapLDAPGroupsToRoles(settings, []string{"cn=admins,ou=groups,dc=example,dc=com"})
|
|
assert.Equal(t, "agent", role, "no mappings should default to agent")
|
|
}
|
|
|
|
func TestMapLDAPGroupsToRoles_EmptyMappings(t *testing.T) {
|
|
settings := &model.AccountLDAPSettings{
|
|
RoleMappings: json.RawMessage(``),
|
|
}
|
|
|
|
role := (&LDAPService{}).MapLDAPGroupsToRoles(settings, []string{"cn=admins,ou=groups,dc=example,dc=com"})
|
|
assert.Equal(t, "agent", role, "empty mappings should default to agent")
|
|
}
|
|
|
|
func TestMapLDAPGroupsToRoles_InvalidJSONMappings(t *testing.T) {
|
|
settings := &model.AccountLDAPSettings{
|
|
RoleMappings: json.RawMessage(`invalid json`),
|
|
}
|
|
|
|
role := (&LDAPService{}).MapLDAPGroupsToRoles(settings, []string{"cn=admins,ou=groups,dc=example,dc=com"})
|
|
assert.Equal(t, "agent", role, "invalid JSON should default to agent")
|
|
}
|
|
|
|
func TestMapLDAPGroupsToRoles_NoMatchingGroup(t *testing.T) {
|
|
settings := &model.AccountLDAPSettings{
|
|
RoleMappings: json.RawMessage(`{"admins": "administrator"}`),
|
|
}
|
|
|
|
role := (&LDAPService{}).MapLDAPGroupsToRoles(settings, []string{"cn=unknown,ou=groups,dc=example,dc=com"})
|
|
assert.Equal(t, "agent", role, "no matching group should default to agent")
|
|
}
|
|
|
|
func TestMapLDAPGroupsToRoles_DirectDNMapping(t *testing.T) {
|
|
settings := &model.AccountLDAPSettings{
|
|
RoleMappings: json.RawMessage(`{"cn=admins,ou=groups,dc=example,dc=com": "administrator"}`),
|
|
}
|
|
|
|
role := (&LDAPService{}).MapLDAPGroupsToRoles(settings, []string{"cn=admins,ou=groups,dc=example,dc=com"})
|
|
assert.Equal(t, "administrator", role, "should match by full DN as well as CN")
|
|
}
|
|
|
|
func TestMapLDAPGroupsToRoles_EmptyGroupsList(t *testing.T) {
|
|
settings := &model.AccountLDAPSettings{
|
|
RoleMappings: json.RawMessage(`{"admins": "administrator"}`),
|
|
}
|
|
|
|
role := (&LDAPService{}).MapLDAPGroupsToRoles(settings, []string{})
|
|
assert.Equal(t, "agent", role, "empty groups list should default to agent")
|
|
}
|
|
|
|
func TestMapLDAPGroupsToRoles_AdminVsAdministrator(t *testing.T) {
|
|
settings := &model.AccountLDAPSettings{
|
|
RoleMappings: json.RawMessage(`{"superadmins": "administrator", "admins": "admin"}`),
|
|
}
|
|
|
|
// administrator (priority 4) > admin (priority 3)
|
|
role := (&LDAPService{}).MapLDAPGroupsToRoles(settings, []string{
|
|
"cn=admins,ou=groups,dc=example,dc=com",
|
|
"cn=superadmins,ou=groups,dc=example,dc=com",
|
|
})
|
|
assert.Equal(t, "administrator", role, "administrator should beat admin in priority")
|
|
}
|
|
|
|
// --- BER encoding tests ---
|
|
|
|
func TestBerEncodeInteger(t *testing.T) {
|
|
// Zero
|
|
result := berEncodeInteger(0)
|
|
assert.Equal(t, []byte{0x02, 0x01, 0x00}, result, "zero integer encoding")
|
|
|
|
// Small positive integer
|
|
result = berEncodeInteger(1)
|
|
assert.Equal(t, byte(0x02), result[0], "should have INTEGER tag")
|
|
assert.Equal(t, byte(0x01), result[1], "length should be 1")
|
|
assert.Equal(t, byte(0x01), result[2], "value should be 1")
|
|
|
|
// Larger integer
|
|
result = berEncodeInteger(255)
|
|
assert.Equal(t, byte(0x02), result[0])
|
|
// 255 = 0x00FF, needs 2 bytes (leading zero for positive sign)
|
|
assert.True(t, len(result) >= 3, "255 needs at least 3 bytes")
|
|
}
|
|
|
|
func TestBerEncodeEnumerated(t *testing.T) {
|
|
result := berEncodeEnumerated(0)
|
|
assert.Equal(t, byte(0x0a), result[0], "ENUMERATED tag should be 0x0a")
|
|
assert.Equal(t, byte(0x01), result[1], "length should be 1")
|
|
assert.Equal(t, byte(0x00), result[2], "value should be 0")
|
|
|
|
result = berEncodeEnumerated(2)
|
|
assert.Equal(t, byte(0x0a), result[0])
|
|
}
|
|
|
|
func TestBerEncodeOctetString(t *testing.T) {
|
|
// Empty string
|
|
result := berEncodeOctetString("")
|
|
assert.Equal(t, []byte{0x04, 0x00}, result, "empty string encoding")
|
|
|
|
// Short string (< 128 chars)
|
|
result = berEncodeOctetString("hello")
|
|
assert.Equal(t, byte(0x04), result[0], "should have OCTET STRING tag")
|
|
assert.Equal(t, byte(0x05), result[1], "length should be 5")
|
|
assert.Equal(t, []byte("hello"), result[2:], "value should be 'hello'")
|
|
|
|
// Long string (>= 128 chars) — uses long form length
|
|
longStr := string(make([]byte, 200))
|
|
result = berEncodeOctetString(longStr)
|
|
assert.Equal(t, byte(0x04), result[0])
|
|
// Long form: second byte has high bit set
|
|
assert.True(t, result[1]&0x80 != 0, "long form length should have high bit set")
|
|
}
|
|
|
|
func TestBerEncodeBoolean(t *testing.T) {
|
|
result := berEncodeBoolean(true)
|
|
assert.Equal(t, []byte{0x01, 0x01, 0xFF}, result, "TRUE encoding")
|
|
|
|
result = berEncodeBoolean(false)
|
|
assert.Equal(t, []byte{0x01, 0x01, 0x00}, result, "FALSE encoding")
|
|
}
|
|
|
|
func TestBerEncodeSequence(t *testing.T) {
|
|
// Empty sequence
|
|
result := berEncodeSequence()
|
|
assert.Equal(t, []byte{0x30, 0x00}, result, "empty sequence encoding")
|
|
|
|
// Sequence with one element
|
|
elem := berEncodeInteger(1)
|
|
result = berEncodeSequence(elem)
|
|
assert.Equal(t, byte(0x30), result[0], "SEQUENCE tag")
|
|
assert.Equal(t, byte(byte(len(elem))), result[1], "length should match element size")
|
|
|
|
// Sequence with multiple elements
|
|
elem1 := berEncodeInteger(1)
|
|
elem2 := berEncodeOctetString("test")
|
|
result = berEncodeSequence(elem1, elem2)
|
|
assert.Equal(t, byte(0x30), result[0])
|
|
expectedLen := len(elem1) + len(elem2)
|
|
assert.Equal(t, byte(expectedLen), result[1], "length should be sum of elements")
|
|
}
|
|
|
|
func TestBerEncodeApplicationTag(t *testing.T) {
|
|
content := berEncodeOctetString("test")
|
|
result := berEncodeApplicationTag(0, content)
|
|
|
|
// Application tag 0: class=01 (application), constructed=1, tag=0 → 0x60
|
|
assert.Equal(t, byte(0x60), result[0], "Application tag 0 should be 0x60")
|
|
assert.Equal(t, byte(len(content)), result[1], "length should match content")
|
|
}
|
|
|
|
func TestBerEncodeApplicationTag_HigherTag(t *testing.T) {
|
|
content := berEncodeOctetString("test")
|
|
result := berEncodeApplicationTag(3, content)
|
|
|
|
// Application tag 3: 0x40 | 0x20 | 0x03 = 0x63
|
|
assert.Equal(t, byte(0x63), result[0], "Application tag 3 should be 0x63")
|
|
}
|
|
|
|
func TestBerEncodeContextTag(t *testing.T) {
|
|
content := berEncodeOctetString("test")
|
|
result := berEncodeContextTag(0, content)
|
|
|
|
// Context tag 0: class=10 (context), constructed=1, tag=0 → 0xA0
|
|
assert.Equal(t, byte(0xA0), result[0], "Context tag 0 should be 0xA0")
|
|
assert.Equal(t, byte(len(content)), result[1], "length should match content")
|
|
}
|
|
|
|
func TestBerEncodeContextTag_HigherTag(t *testing.T) {
|
|
content := berEncodeOctetString("test")
|
|
result := berEncodeContextTag(3, content)
|
|
|
|
// Context tag 3: 0x80 | 0x20 | 0x03 = 0xA3
|
|
assert.Equal(t, byte(0xA3), result[0], "Context tag 3 should be 0xA3")
|
|
}
|
|
|
|
// --- LDAP Bind Request encoding test ---
|
|
|
|
func TestEncodeLDAPBindRequest(t *testing.T) {
|
|
result := encodeLDAPBindRequest(1, 3, "cn=admin,dc=example,dc=com", "password")
|
|
|
|
// The result should start with a SEQUENCE (0x30)
|
|
assert.Equal(t, byte(0x30), result[0], "bind request should start with SEQUENCE")
|
|
|
|
// Total length should be reasonable
|
|
assert.True(t, len(result) > 20, "bind request should have meaningful length")
|
|
|
|
// Should contain the integer message ID (1)
|
|
// Inside the sequence: first element is messageID integer
|
|
assert.Equal(t, byte(0x02), result[2], "should contain INTEGER tag for messageID")
|
|
}
|
|
|
|
func TestEncodeLDAPBindRequest_DifferentMessageID(t *testing.T) {
|
|
result1 := encodeLDAPBindRequest(1, 3, "cn=user1,dc=example,dc=com", "pass1")
|
|
result2 := encodeLDAPBindRequest(2, 3, "cn=user1,dc=example,dc=com", "pass1")
|
|
|
|
// Different message IDs should produce different encodings
|
|
assert.NotEqual(t, result1, result2, "different message IDs should produce different encodings")
|
|
}
|
|
|
|
// --- LDAP Search Request encoding test ---
|
|
|
|
func TestEncodeLDAPSearchRequest(t *testing.T) {
|
|
result := encodeLDAPSearchRequest(
|
|
2, // messageID
|
|
"dc=example,dc=com", // baseDN
|
|
2, // scope (wholeSubtree)
|
|
0, // derefAliases (never)
|
|
0, // sizeLimit
|
|
0, // timeLimit
|
|
false, // typesOnly
|
|
"(uid=testuser)", // filter
|
|
[]string{"mail", "cn"}, // attributes
|
|
)
|
|
|
|
// Should start with SEQUENCE
|
|
assert.Equal(t, byte(0x30), result[0], "search request should start with SEQUENCE")
|
|
assert.True(t, len(result) > 30, "search request should have meaningful length")
|
|
}
|
|
|
|
func TestEncodeLDAPSearchRequest_EmptyAttributes(t *testing.T) {
|
|
result := encodeLDAPSearchRequest(
|
|
1, "dc=example,dc=com", 0, 0, 0, 0, false, "(objectClass=*)", []string{},
|
|
)
|
|
|
|
assert.Equal(t, byte(0x30), result[0])
|
|
assert.True(t, len(result) > 10, "even empty attributes should produce valid encoding")
|
|
}
|
|
|
|
// --- LDAP error sentinel tests ---
|
|
|
|
func TestLDAPErrorSentinels(t *testing.T) {
|
|
assert.Equal(t, "ldap authentication is not enabled", ErrLDAPDisabled.Error())
|
|
assert.Equal(t, "ldap configuration is invalid", ErrLDAPInvalidConfig.Error())
|
|
assert.Equal(t, "failed to connect to ldap server", ErrLDAPConnection.Error())
|
|
assert.Equal(t, "ldap bind authentication failed", ErrLDAPBindFailed.Error())
|
|
assert.Equal(t, "ldap user not found", ErrLDAPUserNotFound.Error())
|
|
assert.Equal(t, "ldap search failed", ErrLDAPSearchFailed.Error())
|
|
}
|
|
|
|
// --- LDAP Authenticate tests (disabled) ---
|
|
|
|
func TestLDAPService_Authenticate_Disabled(t *testing.T) {
|
|
mr := miniredis.RunT(t)
|
|
rdb := redis.NewClient(&redis.Options{Addr: mr.Addr()})
|
|
defer rdb.Close()
|
|
|
|
db := newTestLDAPDB(t)
|
|
cfg := newTestLDAPConfig(false)
|
|
svc, err := NewLDAPService(cfg, rdb, db)
|
|
require.NoError(t, err)
|
|
|
|
_, err = svc.Authenticate(context.Background(), 1, "uid", "password")
|
|
assert.ErrorIs(t, err, ErrLDAPDisabled, "disabled service should reject authentication")
|
|
}
|
|
|
|
// --- LDAP SyncGroups tests (disabled) ---
|
|
|
|
func TestLDAPService_SyncGroups_Disabled(t *testing.T) {
|
|
mr := miniredis.RunT(t)
|
|
rdb := redis.NewClient(&redis.Options{Addr: mr.Addr()})
|
|
defer rdb.Close()
|
|
|
|
db := newTestLDAPDB(t)
|
|
cfg := newTestLDAPConfig(false)
|
|
svc, err := NewLDAPService(cfg, rdb, db)
|
|
require.NoError(t, err)
|
|
|
|
err = svc.SyncGroups(nil, 1)
|
|
assert.ErrorIs(t, err, ErrLDAPDisabled, "disabled service should reject sync")
|
|
}
|
|
|
|
// --- LDAP per-account settings tests ---
|
|
|
|
func TestLDAPService_WithPerAccountSettings(t *testing.T) {
|
|
mr := miniredis.RunT(t)
|
|
rdb := redis.NewClient(&redis.Options{Addr: mr.Addr()})
|
|
defer rdb.Close()
|
|
|
|
db := newTestLDAPDB(t)
|
|
|
|
account := &model.Account{Name: "Test Corp", Active: true}
|
|
require.NoError(t, db.Create(account).Error)
|
|
|
|
settings := &model.AccountLDAPSettings{
|
|
AccountID: account.ID,
|
|
Host: "ldap.corp.example.com",
|
|
Port: 636,
|
|
UseTLS: true,
|
|
BaseDN: "dc=corp,dc=example,dc=com",
|
|
BindDN: "cn=admin,dc=corp,dc=example,dc=com",
|
|
BindPassword: "corppassword",
|
|
UserFilter: "(uid=%s)",
|
|
EmailAttribute: "mail",
|
|
NameAttribute: "cn",
|
|
GroupAttribute: "memberOf",
|
|
RoleMappings: json.RawMessage(`{"admins": "administrator"}`),
|
|
AutoProvision: true,
|
|
Active: true,
|
|
}
|
|
require.NoError(t, db.Create(settings).Error)
|
|
|
|
cfg := newTestLDAPConfig(true)
|
|
svc, err := NewLDAPService(cfg, rdb, db)
|
|
require.NoError(t, err)
|
|
assert.NotNil(t, svc)
|
|
}
|
|
|
|
// --- LDAPUserInfo struct test ---
|
|
|
|
func TestLDAPUserInfo(t *testing.T) {
|
|
info := LDAPUserInfo{
|
|
DN: "cn=testuser,ou=people,dc=example,dc=com",
|
|
Email: "testuser@example.com",
|
|
DisplayName: "Test User",
|
|
FirstName: "Test",
|
|
LastName: "User",
|
|
Groups: []string{"cn=admins,ou=groups,dc=example,dc=com"},
|
|
Attributes: map[string]string{"mail": "testuser@example.com", "cn": "Test User"},
|
|
}
|
|
|
|
assert.Equal(t, "cn=testuser,ou=people,dc=example,dc=com", info.DN)
|
|
assert.Equal(t, "testuser@example.com", info.Email)
|
|
assert.Equal(t, []string{"cn=admins,ou=groups,dc=example,dc=com"}, info.Groups)
|
|
assert.Equal(t, "Test User", info.Attributes["cn"])
|
|
}
|
|
|
|
// --- LDAP role priority constants verification ---
|
|
|
|
func TestLDAPRolePriority(t *testing.T) {
|
|
// Verify role priority is: administrator > admin > supervisor > agent
|
|
// via MapLDAPGroupsToRoles behavior
|
|
settings := &model.AccountLDAPSettings{
|
|
RoleMappings: json.RawMessage(`{
|
|
"grp1": "administrator",
|
|
"grp2": "admin",
|
|
"grp3": "supervisor",
|
|
"grp4": "agent"
|
|
}`),
|
|
}
|
|
|
|
// administrator should win over all
|
|
role := (&LDAPService{}).MapLDAPGroupsToRoles(settings, []string{"grp1", "grp2", "grp3", "grp4"})
|
|
assert.Equal(t, "administrator", role)
|
|
|
|
// admin should win over supervisor and agent
|
|
role = (&LDAPService{}).MapLDAPGroupsToRoles(settings, []string{"grp2", "grp3", "grp4"})
|
|
assert.Equal(t, "admin", role)
|
|
|
|
// supervisor should win over agent
|
|
role = (&LDAPService{}).MapLDAPGroupsToRoles(settings, []string{"grp3", "grp4"})
|
|
assert.Equal(t, "supervisor", role)
|
|
|
|
// agent alone
|
|
role = (&LDAPService{}).MapLDAPGroupsToRoles(settings, []string{"grp4"})
|
|
assert.Equal(t, "agent", role)
|
|
} |