66package auth
77
88import (
9- "errors"
109 "fmt"
10+ "net"
1111 "net/http"
1212 "os"
1313 "strings"
14+ "time"
1415
1516 "github.com/ClusterCockpit/cc-backend/internal/repository"
1617 cclog "github.com/ClusterCockpit/cc-lib/v2/ccLogger"
@@ -25,16 +26,19 @@ type LdapConfig struct {
2526 UserBind string `json:"user-bind"`
2627 UserFilter string `json:"user-filter"`
2728 UserAttr string `json:"username-attr"`
29+ UidAttr string `json:"uid-attr"`
2830 SyncInterval string `json:"sync-interval"` // Parsed using time.ParseDuration.
2931 SyncDelOldUsers bool `json:"sync-del-old-users"`
3032
31- // Should an non-existent user be added to the DB if user exists in ldap directory
32- SyncUserOnLogin bool `json:"sync-user-on-login"`
33+ // Should a non-existent user be added to the DB if user exists in ldap directory
34+ SyncUserOnLogin bool `json:"sync-user-on-login"`
35+ UpdateUserOnLogin bool `json:"update-user-on-login"`
3336}
3437
3538type LdapAuthenticator struct {
3639 syncPassword string
3740 UserAttr string
41+ UidAttr string
3842}
3943
4044var _ Authenticator = (* LdapAuthenticator )(nil )
@@ -51,6 +55,12 @@ func (la *LdapAuthenticator) Init() error {
5155 la .UserAttr = "gecos"
5256 }
5357
58+ if Keys .LdapConfig .UidAttr != "" {
59+ la .UidAttr = Keys .LdapConfig .UidAttr
60+ } else {
61+ la .UidAttr = "uid"
62+ }
63+
5464 return nil
5565}
5666
@@ -66,55 +76,44 @@ func (la *LdapAuthenticator) CanLogin(
6676 if user .AuthSource == schema .AuthViaLDAP {
6777 return user , true
6878 }
69- } else {
70- if lc .SyncUserOnLogin {
71- l , err := la .getLdapConnection (true )
72- if err != nil {
73- cclog .Error ("LDAP connection error" )
74- return nil , false
75- }
76- defer l .Close ()
77-
78- // Search for the given username
79- searchRequest := ldap .NewSearchRequest (
80- lc .UserBase ,
81- ldap .ScopeWholeSubtree , ldap .NeverDerefAliases , 0 , 0 , false ,
82- fmt .Sprintf ("(&%s(uid=%s))" , lc .UserFilter , username ),
83- []string {"dn" , "uid" , la .UserAttr }, nil )
84-
85- sr , err := l .Search (searchRequest )
86- if err != nil {
87- cclog .Warn (err )
88- return nil , false
89- }
90-
91- if len (sr .Entries ) != 1 {
92- cclog .Warn ("LDAP: User does not exist or too many entries returned" )
93- return nil , false
94- }
95-
96- entry := sr .Entries [0 ]
97- name := entry .GetAttributeValue (la .UserAttr )
98- var roles []string
99- roles = append (roles , schema .GetRoleString (schema .RoleUser ))
100- projects := make ([]string , 0 )
101-
102- user = & schema.User {
103- Username : username ,
104- Name : name ,
105- Roles : roles ,
106- Projects : projects ,
107- AuthType : schema .AuthSession ,
108- AuthSource : schema .AuthViaLDAP ,
109- }
79+ } else if lc .SyncUserOnLogin {
80+ l , err := la .getLdapConnection (true )
81+ if err != nil {
82+ cclog .Error ("LDAP connection error" )
83+ return nil , false
84+ }
85+ defer l .Close ()
86+
87+ // Search for the given username
88+ searchRequest := ldap .NewSearchRequest (
89+ lc .UserBase ,
90+ ldap .ScopeWholeSubtree , ldap .NeverDerefAliases , 0 , 0 , false ,
91+ fmt .Sprintf ("(&%s(%s=%s))" , lc .UserFilter , la .UidAttr , ldap .EscapeFilter (username )),
92+ []string {"dn" , la .UidAttr , la .UserAttr }, nil )
93+
94+ sr , err := l .Search (searchRequest )
95+ if err != nil {
96+ cclog .Warn (err )
97+ return nil , false
98+ }
11099
111- if err := repository . GetUserRepository (). AddUser ( user ); err != nil {
112- cclog .Errorf ( " User '%s' LDAP: Insert into DB failed" , username )
113- return nil , false
114- }
100+ if len ( sr . Entries ) != 1 {
101+ cclog .Warn ( "LDAP: User does not exist or too many entries returned" )
102+ return nil , false
103+ }
115104
116- return user , true
105+ entry := sr .Entries [0 ]
106+ user = & schema.User {
107+ Username : username ,
108+ Name : entry .GetAttributeValue (la .UserAttr ),
109+ Roles : []string {schema .GetRoleString (schema .RoleUser )},
110+ Projects : make ([]string , 0 ),
111+ AuthType : schema .AuthSession ,
112+ AuthSource : schema .AuthViaLDAP ,
117113 }
114+
115+ handleLdapUser (user )
116+ return user , true
118117 }
119118
120119 return nil , false
@@ -132,7 +131,7 @@ func (la *LdapAuthenticator) Login(
132131 }
133132 defer l .Close ()
134133
135- userDn := strings .ReplaceAll (Keys .LdapConfig .UserBind , "{username}" , user .Username )
134+ userDn := strings .ReplaceAll (Keys .LdapConfig .UserBind , "{username}" , ldap . EscapeDN ( user .Username ) )
136135 if err := l .Bind (userDn , r .FormValue ("password" )); err != nil {
137136 cclog .Errorf ("AUTH/LDAP > Authentication for user %s failed: %v" ,
138137 user .Username , err )
@@ -170,17 +169,17 @@ func (la *LdapAuthenticator) Sync() error {
170169 lc .UserBase ,
171170 ldap .ScopeWholeSubtree , ldap .NeverDerefAliases , 0 , 0 , false ,
172171 lc .UserFilter ,
173- []string {"dn" , "uid" , la .UserAttr }, nil ))
172+ []string {"dn" , la . UidAttr , la .UserAttr }, nil ))
174173 if err != nil {
175174 cclog .Warn ("LDAP search error" )
176175 return err
177176 }
178177
179178 newnames := map [string ]string {}
180179 for _ , entry := range ldapResults .Entries {
181- username := entry .GetAttributeValue ("uid" )
180+ username := entry .GetAttributeValue (la . UidAttr )
182181 if username == "" {
183- return errors . New ("no attribute 'uid'" )
182+ return fmt . Errorf ("no attribute '%s'" , la . UidAttr )
184183 }
185184
186185 _ , ok := users [username ]
@@ -194,20 +193,19 @@ func (la *LdapAuthenticator) Sync() error {
194193
195194 for username , where := range users {
196195 if where == InDB && lc .SyncDelOldUsers {
197- ur .DelUser (username )
196+ if err := ur .DelUser (username ); err != nil {
197+ cclog .Errorf ("User '%s' LDAP: Delete from DB failed: %v" , username , err )
198+ return err
199+ }
198200 cclog .Debugf ("sync: remove %v (does not show up in LDAP anymore)" , username )
199201 } else if where == InLdap {
200202 name := newnames [username ]
201203
202- var roles []string
203- roles = append (roles , schema .GetRoleString (schema .RoleUser ))
204- projects := make ([]string , 0 )
205-
206204 user := & schema.User {
207205 Username : username ,
208206 Name : name ,
209- Roles : roles ,
210- Projects : projects ,
207+ Roles : [] string { schema . GetRoleString ( schema . RoleUser )} ,
208+ Projects : make ([] string , 0 ) ,
211209 AuthSource : schema .AuthViaLDAP ,
212210 }
213211
@@ -224,11 +222,13 @@ func (la *LdapAuthenticator) Sync() error {
224222
225223func (la * LdapAuthenticator ) getLdapConnection (admin bool ) (* ldap.Conn , error ) {
226224 lc := Keys .LdapConfig
227- conn , err := ldap .DialURL (lc .URL )
225+ conn , err := ldap .DialURL (lc .URL ,
226+ ldap .DialWithDialer (& net.Dialer {Timeout : 10 * time .Second }))
228227 if err != nil {
229228 cclog .Warn ("LDAP URL dial failed" )
230229 return nil , err
231230 }
231+ conn .SetTimeout (30 * time .Second )
232232
233233 if admin {
234234 if err := conn .Bind (lc .SearchDN , la .syncPassword ); err != nil {
0 commit comments