diff --git a/.env.example b/.env.example index be207216..95fad330 100644 --- a/.env.example +++ b/.env.example @@ -226,8 +226,10 @@ TINYAUTH_LDAP_BINDPASSWORDFILE= TINYAUTH_LDAP_BASEDN= # Allow insecure LDAP connections. TINYAUTH_LDAP_INSECURE=false -# LDAP search filter. +# LDAP user search filter. Use %s as the username placeholder. TINYAUTH_LDAP_SEARCHFILTER="(uid=%s)" +# LDAP group search filter. Use %s as the user DN placeholder. +TINYAUTH_LDAP_GROUPSEARCHFILTER="(&(objectclass=groupOfUniqueNames)(uniquemember=%s))" # Certificate for mTLS authentication. TINYAUTH_LDAP_AUTHCERT= # Certificate key for mTLS authentication. diff --git a/internal/model/config.go b/internal/model/config.go index e535adc7..619e061f 100644 --- a/internal/model/config.go +++ b/internal/model/config.go @@ -58,9 +58,10 @@ func NewDefaultConfiguration(runtimeEnv RuntimeEnv) *Config { WarningsEnabled: true, }, LDAP: LDAPConfig{ - Insecure: false, - SearchFilter: "(uid=%s)", - GroupCacheTTL: 900, // 15 minutes + Insecure: false, + SearchFilter: "(uid=%s)", + GroupSearchFilter: "(&(objectclass=groupOfUniqueNames)(uniquemember=%s))", + GroupCacheTTL: 900, // 15 minutes }, Log: LogConfig{ Level: "info", @@ -213,16 +214,17 @@ type UIConfig struct { } type LDAPConfig struct { - Address string `description:"LDAP server address." yaml:"address,omitempty"` - BindDN string `description:"Bind DN for LDAP authentication." yaml:"bindDn,omitempty"` - BindPassword string `description:"Bind password for LDAP authentication." yaml:"bindPassword,omitempty"` - BindPasswordFile string `description:"Path to the Bind password." yaml:"bindPasswordFile,omitempty"` - BaseDN string `description:"Base DN for LDAP searches." yaml:"baseDn,omitempty"` - Insecure bool `description:"Allow insecure LDAP connections." yaml:"insecure,omitempty"` - SearchFilter string `description:"LDAP search filter." yaml:"searchFilter,omitempty"` - AuthCert string `description:"Certificate for mTLS authentication." yaml:"authCert,omitempty"` - AuthKey string `description:"Certificate key for mTLS authentication." yaml:"authKey,omitempty"` - GroupCacheTTL int `description:"Cache duration for LDAP group membership in seconds." yaml:"groupCacheTTL,omitempty"` + Address string `description:"LDAP server address." yaml:"address,omitempty"` + BindDN string `description:"Bind DN for LDAP authentication." yaml:"bindDn,omitempty"` + BindPassword string `description:"Bind password for LDAP authentication." yaml:"bindPassword,omitempty"` + BindPasswordFile string `description:"Path to the Bind password." yaml:"bindPasswordFile,omitempty"` + BaseDN string `description:"Base DN for LDAP searches." yaml:"baseDn,omitempty"` + Insecure bool `description:"Allow insecure LDAP connections." yaml:"insecure,omitempty"` + SearchFilter string `description:"LDAP user search filter. Use %s as the username placeholder." yaml:"searchFilter,omitempty"` + GroupSearchFilter string `description:"LDAP group search filter. Use %s as the user DN placeholder." yaml:"groupSearchFilter,omitempty"` + AuthCert string `description:"Certificate for mTLS authentication." yaml:"authCert,omitempty"` + AuthKey string `description:"Certificate key for mTLS authentication." yaml:"authKey,omitempty"` + GroupCacheTTL int `description:"Cache duration for LDAP group membership in seconds." yaml:"groupCacheTTL,omitempty"` } type LogConfig struct { diff --git a/internal/service/ldap_service.go b/internal/service/ldap_service.go index 7c27cf38..6f30b01d 100644 --- a/internal/service/ldap_service.go +++ b/internal/service/ldap_service.go @@ -5,6 +5,7 @@ import ( "crypto/tls" "errors" "fmt" + "strings" "sync" "time" @@ -160,12 +161,11 @@ func (ldap *LdapService) connect() (*ldapgo.Conn, error) { func (ldap *LdapService) GetUserInfo(username string) (*UserInfoResult, error) { escapedUsername := ldapgo.EscapeFilter(username) - filter := fmt.Sprintf(ldap.config.LDAP.SearchFilter, escapedUsername) searchRequest := ldapgo.NewSearchRequest( ldap.config.LDAP.BaseDN, ldapgo.ScopeWholeSubtree, ldapgo.NeverDerefAliases, 0, 0, false, - filter, + strings.Replace(ldap.config.LDAP.SearchFilter, "%s", escapedUsername, 1), []string{"dn", "mail", "cn"}, nil, ) @@ -219,7 +219,7 @@ func (ldap *LdapService) GetUserGroups(userDN string) ([]string, error) { searchRequest := ldapgo.NewSearchRequest( ldap.config.LDAP.BaseDN, ldapgo.ScopeWholeSubtree, ldapgo.NeverDerefAliases, 0, 0, false, - fmt.Sprintf("(&(objectclass=groupOfUniqueNames)(uniquemember=%s))", escapedUserDN), + strings.Replace(ldap.config.LDAP.GroupSearchFilter, "%s", escapedUserDN, 1), []string{"dn"}, nil, )