@@ -82,6 +82,39 @@ public function __construct(ManagerRegistry $doctrine, Utils $utils, string $LDA
8282 $ this ->utils = $ utils ;
8383 }
8484
85+ /**
86+ * Builds the bind DN for a username by filling the placeholders of LDAP_DN_PATTERN.
87+ *
88+ * Every substituted value is escaped for a DN context: without that, a username such as
89+ * `someone,ou=admins` would not be a value inside the DN but extra structure, changing
90+ * which entry we bind against.
91+ */
92+ protected function buildDn (string $ username ): string
93+ {
94+ $ escape = static fn (string $ value ): string => ldap_escape ($ value , '' , LDAP_ESCAPE_DN );
95+
96+ // Extract user and domain from username (in the form user@domain.org)
97+ $ user_parts = explode ('@ ' , $ username , 2 );
98+
99+ $ ldap_user = $ user_parts [0 ];
100+ $ ldap_domain = $ user_parts [1 ] ?? '' ;
101+
102+ // Replace common placeholders
103+ $ dn = str_replace (
104+ ['%u ' , '%U ' , '%d ' ],
105+ [$ escape ($ username ), $ escape ($ ldap_user ), $ escape ($ ldap_domain )],
106+ $ this ->LDAPDnPattern
107+ );
108+
109+ // Replace domain parts
110+ $ domain_split = array_reverse (explode ('. ' , $ ldap_domain ));
111+ for ($ i = 1 ; $ i <= count ($ domain_split ) and $ i <= 9 ; ++$ i ) {
112+ $ dn = str_replace ('% ' .$ i , $ escape ($ domain_split [$ i - 1 ]), $ dn );
113+ }
114+
115+ return $ dn ;
116+ }
117+
85118 /**
86119 * Connects to an LDAP server and tries to authenticate.
87120 *
@@ -140,25 +173,7 @@ protected function ldapOpen($username, $password)
140173 return false ;
141174 }
142175
143- // Extract user and domain from username (in the form user@domain.org)
144- $ user_parts = explode ('@ ' , $ username , 2 );
145-
146- $ ldap_user = $ user_parts [0 ];
147-
148- if (count ($ user_parts ) > 1 ) {
149- $ ldap_domain = $ user_parts [1 ];
150- } else {
151- $ ldap_domain = '' ;
152- }
153-
154- // Replace common placeholders
155- $ dn = str_replace (['%u ' , '%U ' , '%d ' ], [$ username , $ ldap_user , $ ldap_domain ], $ this ->LDAPDnPattern );
156-
157- // Replace domain parts
158- $ domain_split = array_reverse (explode ('. ' , $ ldap_domain ));
159- for ($ i = 1 ; $ i <= count ($ domain_split ) and $ i <= 9 ; ++$ i ) {
160- $ dn = str_replace ('% ' .$ i , $ domain_split [$ i - 1 ], $ dn );
161- }
176+ $ dn = $ this ->buildDn ($ username );
162177
163178 $ success = false ;
164179 try {
@@ -200,14 +215,16 @@ protected function ldapOpen($username, $password)
200215 }
201216 }
202217
203- $ this ->utils ->createPasswordlessUserWithDefaultObjects ($ username , $ displayName , $ email );
204-
205- $ em = $ this ->doctrine ->getManager ();
206-
207218 try {
208- $ em ->flush ();
209- } catch (\Exception $ e ) {
210- error_log ('LDAP Error (flush): ' .$ e ->getMessage ());
219+ $ this ->utils ->createPasswordlessUserWithDefaultObjects ($ username , $ displayName , $ email );
220+ $ this ->doctrine ->getManager ()->flush ();
221+ } catch (\Throwable $ e ) {
222+ // Letting the login through without a principal would leave the account
223+ // authenticated but unusable: no calendar home, so clients fall back to the
224+ // server root and every write is refused.
225+ error_log ('LDAP Error (could not create the user " ' .$ username .'"): ' .$ e ->getMessage ());
226+
227+ $ success = false ;
211228 }
212229 }
213230 }
0 commit comments