James Moger
2015-11-22 ed552ba47c02779c270ffd62841d6d1048dade70
commit | author | age
04a985 1 /*
JM 2  * Copyright 2012 John Crygier
3  * Copyright 2012 gitblit.com
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  *     http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17 package com.gitblit.auth;
18
19 import java.net.URI;
20 import java.net.URISyntaxException;
21 import java.security.GeneralSecurityException;
6659fa 22 import java.text.MessageFormat;
04a985 23 import java.util.Arrays;
JM 24 import java.util.HashMap;
25 import java.util.List;
26 import java.util.Map;
eb1264 27 import java.util.concurrent.Executors;
AS 28 import java.util.concurrent.ScheduledExecutorService;
04a985 29 import java.util.concurrent.TimeUnit;
JM 30
31 import com.gitblit.Constants;
32 import com.gitblit.Constants.AccountType;
6e3481 33 import com.gitblit.Constants.Role;
04a985 34 import com.gitblit.Keys;
JM 35 import com.gitblit.auth.AuthenticationProvider.UsernamePasswordAuthenticationProvider;
36 import com.gitblit.models.TeamModel;
37 import com.gitblit.models.UserModel;
eb1264 38 import com.gitblit.service.LdapSyncService;
04a985 39 import com.gitblit.utils.ArrayUtils;
JM 40 import com.gitblit.utils.StringUtils;
41 import com.unboundid.ldap.sdk.Attribute;
42 import com.unboundid.ldap.sdk.DereferencePolicy;
43 import com.unboundid.ldap.sdk.ExtendedResult;
44 import com.unboundid.ldap.sdk.LDAPConnection;
45 import com.unboundid.ldap.sdk.LDAPException;
46 import com.unboundid.ldap.sdk.LDAPSearchException;
47 import com.unboundid.ldap.sdk.ResultCode;
48 import com.unboundid.ldap.sdk.SearchRequest;
49 import com.unboundid.ldap.sdk.SearchResult;
50 import com.unboundid.ldap.sdk.SearchResultEntry;
51 import com.unboundid.ldap.sdk.SearchScope;
52 import com.unboundid.ldap.sdk.SimpleBindRequest;
53 import com.unboundid.ldap.sdk.extensions.StartTLSExtendedRequest;
54 import com.unboundid.util.ssl.SSLUtil;
55 import com.unboundid.util.ssl.TrustAllTrustManager;
56
57 /**
58  * Implementation of an LDAP user service.
59  *
60  * @author John Crygier
61  */
62 public class LdapAuthProvider extends UsernamePasswordAuthenticationProvider {
63
6659fa 64     private final ScheduledExecutorService scheduledExecutorService;
04a985 65
JM 66     public LdapAuthProvider() {
67         super("ldap");
6659fa 68
JM 69         scheduledExecutorService = Executors.newSingleThreadScheduledExecutor();
04a985 70     }
JM 71
6659fa 72      private long getSynchronizationPeriodInMilliseconds() {
JM 73          String period = settings.getString(Keys.realm.ldap.syncPeriod, null);
74          if (StringUtils.isEmpty(period)) {
75               period = settings.getString("realm.ldap.ldapCachePeriod", null);
76               if (StringUtils.isEmpty(period)) {
77                   period = "5 MINUTES";
78               } else {
79                   logger.warn("realm.ldap.ldapCachePeriod is obsolete!");
80                   logger.warn(MessageFormat.format("Please set {0}={1} in gitblit.properties!", Keys.realm.ldap.syncPeriod, period));
81                   settings.overrideSetting(Keys.realm.ldap.syncPeriod, period);
82               }
83          }
84
04a985 85         try {
6659fa 86             final String[] s = period.split(" ", 2);
edae53 87             long duration = Math.abs(Long.parseLong(s[0]));
04a985 88             TimeUnit timeUnit = TimeUnit.valueOf(s[1]);
JM 89             return timeUnit.toMillis(duration);
90         } catch (RuntimeException ex) {
6659fa 91             throw new IllegalArgumentException(Keys.realm.ldap.syncPeriod + " must have format '<long> <TimeUnit>' where <TimeUnit> is one of 'MILLISECONDS', 'SECONDS', 'MINUTES', 'HOURS', 'DAYS'");
04a985 92         }
JM 93     }
94
95     @Override
96     public void setup() {
6659fa 97         configureSyncService();
04a985 98     }
JM 99
6659fa 100     @Override
JM 101     public void stop() {
102         scheduledExecutorService.shutdownNow();
103     }
04a985 104
6659fa 105     public synchronized void sync() {
JM 106         final boolean enabled = settings.getBoolean(Keys.realm.ldap.synchronize, false);
107         if (enabled) {
108             logger.info("Synchronizing with LDAP @ " + settings.getRequiredString(Keys.realm.ldap.server));
109             final boolean deleteRemovedLdapUsers = settings.getBoolean(Keys.realm.ldap.removeDeletedUsers, true);
110             LDAPConnection ldapConnection = getLdapConnection();
111             if (ldapConnection != null) {
112                 try {
113                     String accountBase = settings.getString(Keys.realm.ldap.accountBase, "");
114                     String uidAttribute = settings.getString(Keys.realm.ldap.uid, "uid");
115                     String accountPattern = settings.getString(Keys.realm.ldap.accountPattern, "(&(objectClass=person)(sAMAccountName=${username}))");
116                     accountPattern = StringUtils.replace(accountPattern, "${username}", "*");
04a985 117
6659fa 118                     SearchResult result = doSearch(ldapConnection, accountBase, accountPattern);
JM 119                     if (result != null && result.getEntryCount() > 0) {
120                         final Map<String, UserModel> ldapUsers = new HashMap<String, UserModel>();
04a985 121
6659fa 122                         for (SearchResultEntry loggingInUser : result.getSearchEntries()) {
ef4c45 123                             Attribute uid = loggingInUser.getAttribute(uidAttribute);
JM 124                             if (uid == null) {
125                                 logger.error("Can not synchronize with LDAP, missing \"{}\" attribute", uidAttribute);
126                                 continue;
127                             }
128                             final String username = uid.getValue();
6659fa 129                             logger.debug("LDAP synchronizing: " + username);
04a985 130
6659fa 131                             UserModel user = userManager.getUserModel(username);
JM 132                             if (user == null) {
133                                 user = new UserModel(username);
134                             }
04a985 135
6659fa 136                             if (!supportsTeamMembershipChanges()) {
JM 137                                 getTeamsFromLdap(ldapConnection, username, loggingInUser, user);
138                             }
04a985 139
6659fa 140                             // Get User Attributes
JM 141                             setUserAttributes(user, loggingInUser);
04a985 142
6659fa 143                             // store in map
JM 144                             ldapUsers.put(username.toLowerCase(), user);
145                         }
04a985 146
6659fa 147                         if (deleteRemovedLdapUsers) {
JM 148                             logger.debug("detecting removed LDAP users...");
04a985 149
6659fa 150                             for (UserModel userModel : userManager.getAllUsers()) {
JM 151                                 if (AccountType.LDAP == userModel.accountType) {
152                                     if (!ldapUsers.containsKey(userModel.username)) {
153                                         logger.info("deleting removed LDAP user " + userModel.username + " from user service");
154                                         userManager.deleteUser(userModel.username);
155                                     }
156                                 }
157                             }
158                         }
04a985 159
6659fa 160                         userManager.updateUserModels(ldapUsers.values());
JM 161
162                         if (!supportsTeamMembershipChanges()) {
163                             final Map<String, TeamModel> userTeams = new HashMap<String, TeamModel>();
164                             for (UserModel user : ldapUsers.values()) {
165                                 for (TeamModel userTeam : user.teams) {
166                                     userTeams.put(userTeam.name, userTeam);
167                                 }
168                             }
169                             userManager.updateTeamModels(userTeams.values());
170                         }
171                     }
172                     if (!supportsTeamMembershipChanges()) {
173                         getEmptyTeamsFromLdap(ldapConnection);
174                     }
175                 } finally {
176                     ldapConnection.close();
177                 }
178             }
179         }
180     }
04a985 181
JM 182     private LDAPConnection getLdapConnection() {
183         try {
184
185             URI ldapUrl = new URI(settings.getRequiredString(Keys.realm.ldap.server));
186             String ldapHost = ldapUrl.getHost();
187             int ldapPort = ldapUrl.getPort();
188             String bindUserName = settings.getString(Keys.realm.ldap.username, "");
189             String bindPassword = settings.getString(Keys.realm.ldap.password, "");
190
191             LDAPConnection conn;
192             if (ldapUrl.getScheme().equalsIgnoreCase("ldaps")) {
193                 // SSL
194                 SSLUtil sslUtil = new SSLUtil(new TrustAllTrustManager());
195                 conn = new LDAPConnection(sslUtil.createSSLSocketFactory());
4ce3ff 196                 if (ldapPort == -1) {
JM 197                     ldapPort = 636;
198                 }
04a985 199             } else if (ldapUrl.getScheme().equalsIgnoreCase("ldap") || ldapUrl.getScheme().equalsIgnoreCase("ldap+tls")) {
JM 200                 // no encryption or StartTLS
201                 conn = new LDAPConnection();
4ce3ff 202                  if (ldapPort == -1) {
JM 203                      ldapPort = 389;
204                  }
04a985 205             } else {
JM 206                 logger.error("Unsupported LDAP URL scheme: " + ldapUrl.getScheme());
207                 return null;
208             }
209
210             conn.connect(ldapHost, ldapPort);
211
212             if (ldapUrl.getScheme().equalsIgnoreCase("ldap+tls")) {
213                 SSLUtil sslUtil = new SSLUtil(new TrustAllTrustManager());
214                 ExtendedResult extendedResult = conn.processExtendedOperation(
215                         new StartTLSExtendedRequest(sslUtil.createSSLContext()));
216                 if (extendedResult.getResultCode() != ResultCode.SUCCESS) {
217                     throw new LDAPException(extendedResult.getResultCode());
218                 }
219             }
220
4ce3ff 221             if (StringUtils.isEmpty(bindUserName) && StringUtils.isEmpty(bindPassword)) {
JM 222                 // anonymous bind
223                 conn.bind(new SimpleBindRequest());
224             } else {
225                 // authenticated bind
04a985 226                 conn.bind(new SimpleBindRequest(bindUserName, bindPassword));
JM 227             }
228
229             return conn;
230
231         } catch (URISyntaxException e) {
232             logger.error("Bad LDAP URL, should be in the form: ldap(s|+tls)://<server>:<port>", e);
233         } catch (GeneralSecurityException e) {
234             logger.error("Unable to create SSL Connection", e);
235         } catch (LDAPException e) {
236             logger.error("Error Connecting to LDAP", e);
237         }
238
239         return null;
240     }
241
242     /**
243      * Credentials are defined in the LDAP server and can not be manipulated
244      * from Gitblit.
245      *
246      * @return false
247      * @since 1.0.0
248      */
249     @Override
250     public boolean supportsCredentialChanges() {
251         return false;
252     }
253
254     /**
255      * If no displayName pattern is defined then Gitblit can manage the display name.
256      *
257      * @return true if Gitblit can manage the user display name
258      * @since 1.0.0
259      */
260     @Override
261     public boolean supportsDisplayNameChanges() {
262         return StringUtils.isEmpty(settings.getString(Keys.realm.ldap.displayName, ""));
263     }
264
265     /**
266      * If no email pattern is defined then Gitblit can manage the email address.
267      *
268      * @return true if Gitblit can manage the user email address
269      * @since 1.0.0
270      */
271     @Override
272     public boolean supportsEmailAddressChanges() {
273         return StringUtils.isEmpty(settings.getString(Keys.realm.ldap.email, ""));
274     }
275
276     /**
277      * If the LDAP server will maintain team memberships then LdapUserService
278      * will not allow team membership changes.  In this scenario all team
279      * changes must be made on the LDAP server by the LDAP administrator.
280      *
281      * @return true or false
282      * @since 1.0.0
283      */
284     @Override
285     public boolean supportsTeamMembershipChanges() {
286         return !settings.getBoolean(Keys.realm.ldap.maintainTeams, false);
287     }
288
6e3481 289     @Override
JM 290     public boolean supportsRoleChanges(UserModel user, Role role) {
291         if (Role.ADMIN == role) {
292             if (!supportsTeamMembershipChanges()) {
293                 List<String> admins = settings.getStrings(Keys.realm.ldap.admins);
294                 if (admins.contains(user.username)) {
295                     return false;
296                 }
297             }
298         }
299         return true;
300     }
301
302     @Override
303     public boolean supportsRoleChanges(TeamModel team, Role role) {
304         if (Role.ADMIN == role) {
305             if (!supportsTeamMembershipChanges()) {
306                 List<String> admins = settings.getStrings(Keys.realm.ldap.admins);
307                 if (admins.contains("@" + team.name)) {
308                     return false;
309                 }
310             }
311         }
312         return true;
313     }
314
04a985 315     @Override
JM 316     public AccountType getAccountType() {
317          return AccountType.LDAP;
318     }
319
320     @Override
321     public UserModel authenticate(String username, char[] password) {
322         String simpleUsername = getSimpleUsername(username);
323
324         LDAPConnection ldapConnection = getLdapConnection();
325         if (ldapConnection != null) {
326             try {
e4b0ae 327                 boolean alreadyAuthenticated = false;
ef4c45 328
e4b0ae 329                 String bindPattern = settings.getString(Keys.realm.ldap.bindpattern, "");
J 330                 if (!StringUtils.isEmpty(bindPattern)) {
331                     try {
c30c2b 332                         String bindUser = StringUtils.replace(bindPattern, "${username}", escapeLDAPSearchFilter(simpleUsername));
e4b0ae 333                         ldapConnection.bind(bindUser, new String(password));
ef4c45 334
e4b0ae 335                         alreadyAuthenticated = true;
J 336                     } catch (LDAPException e) {
337                         return null;
338                     }
339                 }
340
04a985 341                 // Find the logging in user's DN
JM 342                 String accountBase = settings.getString(Keys.realm.ldap.accountBase, "");
343                 String accountPattern = settings.getString(Keys.realm.ldap.accountPattern, "(&(objectClass=person)(sAMAccountName=${username}))");
344                 accountPattern = StringUtils.replace(accountPattern, "${username}", escapeLDAPSearchFilter(simpleUsername));
345
346                 SearchResult result = doSearch(ldapConnection, accountBase, accountPattern);
347                 if (result != null && result.getEntryCount() == 1) {
348                     SearchResultEntry loggingInUser = result.getSearchEntries().get(0);
349                     String loggingInUserDN = loggingInUser.getDN();
350
e4b0ae 351                     if (alreadyAuthenticated || isAuthenticated(ldapConnection, loggingInUserDN, new String(password))) {
04a985 352                         logger.debug("LDAP authenticated: " + username);
JM 353
354                         UserModel user = null;
355                         synchronized (this) {
356                             user = userManager.getUserModel(simpleUsername);
1293c2 357                             if (user == null) {
JM 358                                 // create user object for new authenticated user
04a985 359                                 user = new UserModel(simpleUsername);
1293c2 360                             }
04a985 361
JM 362                             // create a user cookie
c1b0e4 363                             setCookie(user, password);
04a985 364
1293c2 365                             if (!supportsTeamMembershipChanges()) {
04a985 366                                 getTeamsFromLdap(ldapConnection, simpleUsername, loggingInUser, user);
1293c2 367                             }
04a985 368
JM 369                             // Get User Attributes
370                             setUserAttributes(user, loggingInUser);
371
372                             // Push the ldap looked up values to backing file
373                             updateUser(user);
374
375                             if (!supportsTeamMembershipChanges()) {
1293c2 376                                 for (TeamModel userTeam : user.teams) {
04a985 377                                     updateTeam(userTeam);
1293c2 378                                 }
04a985 379                             }
JM 380                         }
381
382                         return user;
383                     }
384                 }
385             } finally {
386                 ldapConnection.close();
387             }
388         }
389         return null;
390     }
391
392     /**
393      * Set the admin attribute from team memberships retrieved from LDAP.
394      * If we are not storing teams in LDAP and/or we have not defined any
395      * administrator teams, then do not change the admin flag.
396      *
397      * @param user
398      */
399     private void setAdminAttribute(UserModel user) {
400         if (!supportsTeamMembershipChanges()) {
401             List<String> admins = settings.getStrings(Keys.realm.ldap.admins);
402             // if we have defined administrative teams, then set admin flag
403             // otherwise leave admin flag unchanged
404             if (!ArrayUtils.isEmpty(admins)) {
405                 user.canAdmin = false;
406                 for (String admin : admins) {
1293c2 407                     if (admin.startsWith("@") && user.isTeamMember(admin.substring(1))) {
JM 408                         // admin team
409                         user.canAdmin = true;
410                     } else if (user.getName().equalsIgnoreCase(admin)) {
411                         // admin user
412                         user.canAdmin = true;
413                     }
04a985 414                 }
JM 415             }
416         }
417     }
418
419     private void setUserAttributes(UserModel user, SearchResultEntry userEntry) {
420         // Is this user an admin?
421         setAdminAttribute(user);
422
423         // Don't want visibility into the real password, make up a dummy
424         user.password = Constants.EXTERNAL_ACCOUNT;
425         user.accountType = getAccountType();
426
427         // Get full name Attribute
428         String displayName = settings.getString(Keys.realm.ldap.displayName, "");
429         if (!StringUtils.isEmpty(displayName)) {
430             // Replace embedded ${} with attributes
431             if (displayName.contains("${")) {
1293c2 432                 for (Attribute userAttribute : userEntry.getAttributes()) {
04a985 433                     displayName = StringUtils.replace(displayName, "${" + userAttribute.getName() + "}", userAttribute.getValue());
1293c2 434                 }
04a985 435                 user.displayName = displayName;
JM 436             } else {
437                 Attribute attribute = userEntry.getAttribute(displayName);
438                 if (attribute != null && attribute.hasValue()) {
439                     user.displayName = attribute.getValue();
440                 }
441             }
442         }
443
444         // Get email address Attribute
445         String email = settings.getString(Keys.realm.ldap.email, "");
446         if (!StringUtils.isEmpty(email)) {
447             if (email.contains("${")) {
1293c2 448                 for (Attribute userAttribute : userEntry.getAttributes()) {
04a985 449                     email = StringUtils.replace(email, "${" + userAttribute.getName() + "}", userAttribute.getValue());
1293c2 450                 }
04a985 451                 user.emailAddress = email;
JM 452             } else {
453                 Attribute attribute = userEntry.getAttribute(email);
454                 if (attribute != null && attribute.hasValue()) {
455                     user.emailAddress = attribute.getValue();
2f0fe2 456                 } else {
JM 457                     // issue-456/ticket-134
458                     // allow LDAP to delete an email address
459                     user.emailAddress = null;
04a985 460                 }
JM 461             }
462         }
463     }
464
465     private void getTeamsFromLdap(LDAPConnection ldapConnection, String simpleUsername, SearchResultEntry loggingInUser, UserModel user) {
466         String loggingInUserDN = loggingInUser.getDN();
467
1293c2 468         // Clear the users team memberships - we're going to get them from LDAP
JM 469         user.teams.clear();
470
04a985 471         String groupBase = settings.getString(Keys.realm.ldap.groupBase, "");
JM 472         String groupMemberPattern = settings.getString(Keys.realm.ldap.groupMemberPattern, "(&(objectClass=group)(member=${dn}))");
473
474         groupMemberPattern = StringUtils.replace(groupMemberPattern, "${dn}", escapeLDAPSearchFilter(loggingInUserDN));
475         groupMemberPattern = StringUtils.replace(groupMemberPattern, "${username}", escapeLDAPSearchFilter(simpleUsername));
476
477         // Fill in attributes into groupMemberPattern
478         for (Attribute userAttribute : loggingInUser.getAttributes()) {
479             groupMemberPattern = StringUtils.replace(groupMemberPattern, "${" + userAttribute.getName() + "}", escapeLDAPSearchFilter(userAttribute.getValue()));
480         }
481
482         SearchResult teamMembershipResult = doSearch(ldapConnection, groupBase, true, groupMemberPattern, Arrays.asList("cn"));
483         if (teamMembershipResult != null && teamMembershipResult.getEntryCount() > 0) {
484             for (int i = 0; i < teamMembershipResult.getEntryCount(); i++) {
485                 SearchResultEntry teamEntry = teamMembershipResult.getSearchEntries().get(i);
486                 String teamName = teamEntry.getAttribute("cn").getValue();
487
488                 TeamModel teamModel = userManager.getTeamModel(teamName);
489                 if (teamModel == null) {
490                     teamModel = createTeamFromLdap(teamEntry);
491                 }
492
493                 user.teams.add(teamModel);
494                 teamModel.addUser(user.getName());
495             }
496         }
497     }
498
f6d7de 499     private void getEmptyTeamsFromLdap(LDAPConnection ldapConnection) {
6659fa 500         logger.info("Start fetching empty teams from ldap.");
f6d7de 501         String groupBase = settings.getString(Keys.realm.ldap.groupBase, "");
AS 502         String groupMemberPattern = settings.getString(Keys.realm.ldap.groupEmptyMemberPattern, "(&(objectClass=group)(!(member=*)))");
503
504         SearchResult teamMembershipResult = doSearch(ldapConnection, groupBase, true, groupMemberPattern, null);
505         if (teamMembershipResult != null && teamMembershipResult.getEntryCount() > 0) {
506             for (int i = 0; i < teamMembershipResult.getEntryCount(); i++) {
507                 SearchResultEntry teamEntry = teamMembershipResult.getSearchEntries().get(i);
508                 if (!teamEntry.hasAttribute("member")) {
509                     String teamName = teamEntry.getAttribute("cn").getValue();
6659fa 510
f6d7de 511                     TeamModel teamModel = userManager.getTeamModel(teamName);
AS 512                     if (teamModel == null) {
513                         teamModel = createTeamFromLdap(teamEntry);
514                         userManager.updateTeamModel(teamModel);
515                     }
516                 }
517             }
518         }
6659fa 519         logger.info("Finished fetching empty teams from ldap.");
f6d7de 520     }
AS 521
04a985 522     private TeamModel createTeamFromLdap(SearchResultEntry teamEntry) {
JM 523         TeamModel answer = new TeamModel(teamEntry.getAttributeValue("cn"));
524         answer.accountType = getAccountType();
525         // potentially retrieve other attributes here in the future
526
527         return answer;
528     }
529
530     private SearchResult doSearch(LDAPConnection ldapConnection, String base, String filter) {
531         try {
532             return ldapConnection.search(base, SearchScope.SUB, filter);
533         } catch (LDAPSearchException e) {
534             logger.error("Problem Searching LDAP", e);
535
536             return null;
537         }
538     }
539
540     private SearchResult doSearch(LDAPConnection ldapConnection, String base, boolean dereferenceAliases, String filter, List<String> attributes) {
541         try {
542             SearchRequest searchRequest = new SearchRequest(base, SearchScope.SUB, filter);
543             if (dereferenceAliases) {
544                 searchRequest.setDerefPolicy(DereferencePolicy.SEARCHING);
545             }
546             if (attributes != null) {
547                 searchRequest.setAttributes(attributes);
548             }
549             return ldapConnection.search(searchRequest);
550
551         } catch (LDAPSearchException e) {
552             logger.error("Problem Searching LDAP", e);
553
554             return null;
555         } catch (LDAPException e) {
556             logger.error("Problem creating LDAP search", e);
557             return null;
558         }
559     }
560
561     private boolean isAuthenticated(LDAPConnection ldapConnection, String userDn, String password) {
562         try {
563             // Binding will stop any LDAP-Injection Attacks since the searched-for user needs to bind to that DN
564             ldapConnection.bind(userDn, password);
565             return true;
566         } catch (LDAPException e) {
567             logger.error("Error authenticating user", e);
568             return false;
569         }
570     }
571
572     /**
573      * Returns a simple username without any domain prefixes.
574      *
575      * @param username
576      * @return a simple username
577      */
578     protected String getSimpleUsername(String username) {
579         int lastSlash = username.lastIndexOf('\\');
580         if (lastSlash > -1) {
581             username = username.substring(lastSlash + 1);
582         }
583
584         return username;
585     }
586
587     // From: https://www.owasp.org/index.php/Preventing_LDAP_Injection_in_Java
588     public static final String escapeLDAPSearchFilter(String filter) {
589         StringBuilder sb = new StringBuilder();
590         for (int i = 0; i < filter.length(); i++) {
591             char curChar = filter.charAt(i);
592             switch (curChar) {
593             case '\\':
594                 sb.append("\\5c");
595                 break;
596             case '*':
597                 sb.append("\\2a");
598                 break;
599             case '(':
600                 sb.append("\\28");
601                 break;
602             case ')':
603                 sb.append("\\29");
604                 break;
605             case '\u0000':
606                 sb.append("\\00");
607                 break;
608             default:
609                 sb.append(curChar);
610             }
611         }
612         return sb.toString();
613     }
eb1264 614
6659fa 615     private void configureSyncService() {
eb1264 616         LdapSyncService ldapSyncService = new LdapSyncService(settings, this);
AS 617         if (ldapSyncService.isReady()) {
6659fa 618             long ldapSyncPeriod = getSynchronizationPeriodInMilliseconds();
eb1264 619             int delay = 1;
a1b5df 620             logger.info("Ldap sync service will update users and groups every {} minutes.",
JM 621                     TimeUnit.MILLISECONDS.toMinutes(ldapSyncPeriod));
edae53 622             scheduledExecutorService.scheduleAtFixedRate(ldapSyncService, delay, ldapSyncPeriod,  TimeUnit.MILLISECONDS);
eb1264 623         } else {
AS 624             logger.info("Ldap sync service is disabled.");
625         }
626     }
627
04a985 628 }