1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.codelibs.fess.ldap;
17
18 import static org.codelibs.core.stream.StreamUtil.stream;
19
20 import java.util.ArrayList;
21 import java.util.Base64;
22 import java.util.Collections;
23 import java.util.Hashtable;
24 import java.util.List;
25 import java.util.Locale;
26 import java.util.function.BiConsumer;
27 import java.util.function.Consumer;
28 import java.util.function.Supplier;
29 import java.util.stream.Collectors;
30
31 import javax.naming.Context;
32 import javax.naming.NamingException;
33 import javax.naming.directory.Attribute;
34 import javax.naming.directory.Attributes;
35 import javax.naming.directory.BasicAttribute;
36 import javax.naming.directory.BasicAttributes;
37 import javax.naming.directory.DirContext;
38 import javax.naming.directory.InitialDirContext;
39 import javax.naming.directory.ModificationItem;
40 import javax.naming.directory.SearchControls;
41 import javax.naming.directory.SearchResult;
42
43 import org.codelibs.core.lang.StringUtil;
44 import org.codelibs.fess.Constants;
45 import org.codelibs.fess.entity.FessUser;
46 import org.codelibs.fess.es.user.exentity.Group;
47 import org.codelibs.fess.es.user.exentity.Role;
48 import org.codelibs.fess.es.user.exentity.User;
49 import org.codelibs.fess.exception.LdapOperationException;
50 import org.codelibs.fess.helper.SystemHelper;
51 import org.codelibs.fess.mylasta.direction.FessConfig;
52 import org.codelibs.fess.util.ComponentUtil;
53 import org.codelibs.fess.util.OptionalUtil;
54 import org.dbflute.optional.OptionalEntity;
55 import org.dbflute.util.DfTypeUtil;
56 import org.slf4j.Logger;
57 import org.slf4j.LoggerFactory;
58
59 public class LdapManager {
60 private static final Logger logger = LoggerFactory.getLogger(LdapManager.class);
61
62 protected ThreadLocal<DirContextHolder> contextLocal = new ThreadLocal<>();
63
64 protected volatile boolean isBind = false;
65
66 protected Hashtable<String, String> createEnvironment(final String initialContextFactory, final String securityAuthentication,
67 final String providerUrl, final String principal, final String credntials) {
68 final Hashtable<String, String> env = new Hashtable<>();
69 env.put(Context.INITIAL_CONTEXT_FACTORY, initialContextFactory);
70 env.put(Context.SECURITY_AUTHENTICATION, securityAuthentication);
71 env.put(Context.PROVIDER_URL, providerUrl);
72 env.put(Context.SECURITY_PRINCIPAL, principal);
73 env.put(Context.SECURITY_CREDENTIALS, credntials);
74 if (providerUrl != null && providerUrl.startsWith("ldaps://")) {
75 env.put(Context.SECURITY_PROTOCOL, "ssl");
76 }
77 return env;
78 }
79
80 protected Hashtable<String, String> createAdminEnv() {
81 final FessConfig fessConfig = ComponentUtil.getFessConfig();
82 return createEnvironment(
83 fessConfig.getLdapInitialContextFactory(),
84 fessConfig.getLdapSecurityAuthentication(), fessConfig.getLdapProviderUrl(),
85 fessConfig.getLdapAdminSecurityPrincipal(),
86 fessConfig.getLdapAdminSecurityCredentials());
87 }
88
89 protected Hashtable<String, String> createSearchEnv(final String username, final String password) {
90 final FessConfig fessConfig = ComponentUtil.getFessConfig();
91 return createEnvironment(
92 fessConfig.getLdapInitialContextFactory(),
93 fessConfig.getLdapSecurityAuthentication(),
94 fessConfig.getLdapProviderUrl(),
95 fessConfig.getLdapSecurityPrincipal(username), password);
96 }
97
98 protected Hashtable<String, String> createSearchEnv() {
99 final FessConfig fessConfig = ComponentUtil.getFessConfig();
100 return createEnvironment(
101 fessConfig.getLdapInitialContextFactory(),
102 fessConfig.getLdapSecurityAuthentication(), fessConfig.getLdapProviderUrl(),
103 fessConfig.getLdapAdminSecurityPrincipal(),
104 fessConfig.getLdapAdminSecurityCredentials());
105 }
106
107 public void updateConfig() {
108 isBind = false;
109 }
110
111 protected boolean validate() {
112 if (!isBind) {
113 final Hashtable<String, String> env = createAdminEnv();
114 try (DirContextHolder holder = getDirContext(() -> env)) {
115 final DirContext context = holder.get();
116 if (logger.isDebugEnabled()) {
117 logger.debug("Logged in as Bind DN.", context);
118 }
119 isBind = true;
120 } catch (final Exception e) {
121 logger.warn("LDAP configuration is wrong.", e);
122 }
123 }
124 return isBind;
125 }
126
127 public OptionalEntity<FessUser> login(final String username, final String password) {
128 final FessConfig fessConfig = ComponentUtil.getFessConfig();
129
130 if (StringUtil.isBlank(fessConfig.getLdapProviderUrl())) {
131 return OptionalEntity.empty();
132 }
133
134 if (!validate()) {
135 return OptionalEntity.empty();
136 }
137
138 final Hashtable<String, String> env = createSearchEnv(username, password);
139 try (DirContextHolder holder = getDirContext(() -> env)) {
140 final DirContext context = holder.get();
141 if (logger.isDebugEnabled()) {
142 logger.debug("Logged in.", context);
143 }
144 return OptionalEntity.of(createLdapUser(username, env));
145 } catch (final Exception e) {
146 logger.debug("Login failed.", e);
147 }
148 return OptionalEntity.empty();
149 }
150
151 public OptionalEntity<FessUser> login(final String username) {
152 final Hashtable<String, String> env = createSearchEnv();
153 try (DirContextHolder holder = getDirContext(() -> env)) {
154 final DirContext context = holder.get();
155 if (logger.isDebugEnabled()) {
156 logger.debug("Logged in.", context);
157 }
158 return OptionalEntity.of(createLdapUser(username, env));
159 } catch (final Exception e) {
160 logger.debug("Login failed.", e);
161 }
162 return OptionalEntity.empty();
163 }
164
165 protected LdapUser createLdapUser(final String username, final Hashtable<String, String> env) {
166 return new LdapUser(env, username);
167 }
168
169 public String[] getRoles(final LdapUser ldapUser, final String bindDn, final String accountFilter) {
170 final SystemHelper systemHelper = ComponentUtil.getSystemHelper();
171 final FessConfig fessConfig = ComponentUtil.getFessConfig();
172 final List<String> roleList = new ArrayList<>();
173
174 if (fessConfig.isLdapRoleSearchUserEnabled()) {
175 roleList.add(systemHelper.getSearchRoleByUser(ldapUser.getName()));
176 }
177
178
179
180 final String filter = String.format(accountFilter, ldapUser.getName());
181 search(bindDn, filter, new String[] { fessConfig.getLdapMemberofAttribute() }, () -> ldapUser.getEnvironment(), result -> {
182 processSearchRoles(result, (entryDn, name) -> {
183 final boolean isRole = entryDn.toLowerCase(Locale.ROOT).indexOf("ou=role") != -1;
184 if (isRole) {
185 if (fessConfig.isLdapRoleSearchRoleEnabled()) {
186 roleList.add(systemHelper.getSearchRoleByRole(name));
187 }
188 } else if (fessConfig.isLdapRoleSearchGroupEnabled()) {
189 roleList.add(systemHelper.getSearchRoleByGroup(name));
190 }
191 });
192 });
193
194 return roleList.toArray(new String[roleList.size()]);
195 }
196
197 protected void processSearchRoles(final List<SearchResult> result, final BiConsumer<String, String> consumer) throws NamingException {
198 final FessConfig fessConfig = ComponentUtil.getFessConfig();
199 for (final SearchResult srcrslt : result) {
200 final Attributes attrs = srcrslt.getAttributes();
201
202
203 final Attribute attr = attrs.get(fessConfig.getLdapMemberofAttribute());
204 if (attr == null) {
205 continue;
206 }
207
208 for (int i = 0; i < attr.size(); i++) {
209 final Object attrValue = attr.get(i);
210 if (attrValue != null) {
211 final String entryDn = attrValue.toString();
212
213 int start = 0;
214 int end = 0;
215
216 start = entryDn.indexOf("CN=");
217 if (start < 0) {
218 start = entryDn.indexOf("cn=");
219 }
220 if (start == -1) {
221 continue;
222 }
223 start += 3;
224 end = entryDn.indexOf(',');
225
226 String name;
227 if (end == -1) {
228 name = entryDn.substring(start);
229 } else {
230 name = entryDn.substring(start, end);
231 }
232
233 consumer.accept(entryDn, name);
234 }
235 }
236 }
237 }
238
239 protected void setAttributeValue(final List<SearchResult> result, final String name, final Consumer<Object> consumer) {
240 final List<Object> attrList = getAttributeValueList(result, name);
241 if (!attrList.isEmpty()) {
242 consumer.accept(attrList.get(0));
243 }
244 }
245
246 protected List<Object> getAttributeValueList(final List<SearchResult> result, final String name) {
247 try {
248 for (final SearchResult srcrslt : result) {
249 final Attributes attrs = srcrslt.getAttributes();
250
251 final Attribute attr = attrs.get(name);
252 if (attr == null) {
253 continue;
254 }
255
256 final List<Object> attrList = new ArrayList<>();
257 for (int i = 0; i < attr.size(); i++) {
258 final Object attrValue = attr.get(i);
259 if (attrValue != null) {
260 attrList.add(attrValue);
261 }
262 }
263 return attrList;
264 }
265 return Collections.emptyList();
266 } catch (final NamingException e) {
267 throw new LdapOperationException("Failed to parse attribute values for " + name, e);
268 }
269 }
270
271 public void apply(final User user) {
272 final FessConfig fessConfig = ComponentUtil.getFessConfig();
273 if (!fessConfig.isLdapAdminEnabled(user.getName())) {
274 return;
275 }
276
277 final Supplier<Hashtable<String, String>> adminEnv = () -> createAdminEnv();
278 search(fessConfig.getLdapAdminUserBaseDn(),
279 fessConfig.getLdapAdminUserFilter(user.getName()),
280 null,
281 adminEnv,
282 result -> {
283 if (!result.isEmpty()) {
284 setAttributeValue(result, fessConfig.getLdapAttrSurname(), o -> user.setSurname(o.toString()));
285 setAttributeValue(result, fessConfig.getLdapAttrGivenName(), o -> user.setGivenName(o.toString()));
286 setAttributeValue(result, fessConfig.getLdapAttrMail(), o -> user.setMail(o.toString()));
287 setAttributeValue(result, fessConfig.getLdapAttrEmployeeNumber(), o -> user.setEmployeeNumber(o.toString()));
288 setAttributeValue(result, fessConfig.getLdapAttrTelephoneNumber(), o -> user.setTelephoneNumber(o.toString()));
289 setAttributeValue(result, fessConfig.getLdapAttrHomePhone(), o -> user.setHomePhone(o.toString()));
290 setAttributeValue(result, fessConfig.getLdapAttrHomePostalAddress(), o -> user.setHomePostalAddress(o.toString()));
291 setAttributeValue(result, fessConfig.getLdapAttrLabeleduri(), o -> user.setLabeledURI(o.toString()));
292 setAttributeValue(result, fessConfig.getLdapAttrRoomNumber(), o -> user.setRoomNumber(o.toString()));
293 setAttributeValue(result, fessConfig.getLdapAttrDescription(), o -> user.setDescription(o.toString()));
294 setAttributeValue(result, fessConfig.getLdapAttrTitle(), o -> user.setTitle(o.toString()));
295 setAttributeValue(result, fessConfig.getLdapAttrPager(), o -> user.setPager(o.toString()));
296 setAttributeValue(result, fessConfig.getLdapAttrStreet(), o -> user.setStreet(o.toString()));
297 setAttributeValue(result, fessConfig.getLdapAttrPostalCode(), o -> user.setPostalCode(o.toString()));
298 setAttributeValue(result, fessConfig.getLdapAttrPhysicalDeliveryOfficeName(),
299 o -> user.setPhysicalDeliveryOfficeName(o.toString()));
300 setAttributeValue(result, fessConfig.getLdapAttrDestinationIndicator(),
301 o -> user.setDestinationIndicator(o.toString()));
302 setAttributeValue(result, fessConfig.getLdapAttrInternationalisdnNumber(),
303 o -> user.setInternationaliSDNNumber(o.toString()));
304 setAttributeValue(result, fessConfig.getLdapAttrState(), o -> user.setState(o.toString()));
305 setAttributeValue(result, fessConfig.getLdapAttrEmployeeType(), o -> user.setEmployeeType(o.toString()));
306 setAttributeValue(result, fessConfig.getLdapAttrFacsimileTelephoneNumber(),
307 o -> user.setFacsimileTelephoneNumber(o.toString()));
308 setAttributeValue(result, fessConfig.getLdapAttrPostOfficeBox(), o -> user.setPostOfficeBox(o.toString()));
309 setAttributeValue(result, fessConfig.getLdapAttrInitials(), o -> user.setInitials(o.toString()));
310 setAttributeValue(result, fessConfig.getLdapAttrCarLicense(), o -> user.setCarLicense(o.toString()));
311 setAttributeValue(result, fessConfig.getLdapAttrMobile(), o -> user.setMobile(o.toString()));
312 setAttributeValue(result, fessConfig.getLdapAttrPostalAddress(), o -> user.setPostalAddress(o.toString()));
313 setAttributeValue(result, fessConfig.getLdapAttrCity(), o -> user.setCity(o.toString()));
314 setAttributeValue(result, fessConfig.getLdapAttrTeletexTerminalIdentifier(),
315 o -> user.setTeletexTerminalIdentifier(o.toString()));
316 setAttributeValue(result, fessConfig.getLdapAttrX121Address(), o -> user.setX121Address(o.toString()));
317 setAttributeValue(result, fessConfig.getLdapAttrBusinessCategory(), o -> user.setBusinessCategory(o.toString()));
318 setAttributeValue(result, fessConfig.getLdapAttrRegisteredAddress(), o -> user.setRegisteredAddress(o.toString()));
319 setAttributeValue(result, fessConfig.getLdapAttrDisplayName(), o -> user.setDisplayName(o.toString()));
320 setAttributeValue(result, fessConfig.getLdapAttrPreferredLanguage(), o -> user.setPreferredLanguage(o.toString()));
321 setAttributeValue(result, fessConfig.getLdapAttrDepartmentNumber(), o -> user.setDepartmentNumber(o.toString()));
322 setAttributeValue(result, fessConfig.getLdapAttrUidNumber(), o -> user.setUidNumber(DfTypeUtil.toLong(o)));
323 setAttributeValue(result, fessConfig.getLdapAttrGidNumber(), o -> user.setGidNumber(DfTypeUtil.toLong(o)));
324 setAttributeValue(result, fessConfig.getLdapAttrHomeDirectory(), o -> user.setHomeDirectory(o.toString()));
325 }
326 });
327
328
329 search(fessConfig.getLdapAdminUserBaseDn(), fessConfig.getLdapAdminUserFilter(user.getName()),
330 new String[] { fessConfig.getLdapMemberofAttribute() }, adminEnv, result -> {
331 if (!result.isEmpty()) {
332 final List<String> groupList = new ArrayList<>();
333 final List<String> roleList = new ArrayList<>();
334 final String lowerGroupDn = fessConfig.getLdapAdminGroupBaseDn().toLowerCase(Locale.ROOT);
335 final String lowerRoleDn = fessConfig.getLdapAdminRoleBaseDn().toLowerCase(Locale.ROOT);
336 processSearchRoles(result, (entryDn, name) -> {
337 final String lowerEntryDn = entryDn.toLowerCase(Locale.ROOT);
338 if (lowerEntryDn.indexOf(lowerGroupDn) != -1) {
339 groupList.add(Base64.getUrlEncoder().encodeToString(name.getBytes(Constants.CHARSET_UTF_8)));
340 } else if (lowerEntryDn.indexOf(lowerRoleDn) != -1) {
341 roleList.add(Base64.getUrlEncoder().encodeToString(name.getBytes(Constants.CHARSET_UTF_8)));
342 }
343 });
344 user.setGroups(groupList.toArray(new String[groupList.size()]));
345 user.setRoles(roleList.toArray(new String[roleList.size()]));
346 }
347 });
348
349 }
350
351 public void insert(final User user) {
352 final FessConfig fessConfig = ComponentUtil.getFessConfig();
353 if (!fessConfig.isLdapAdminEnabled(user.getName())) {
354 return;
355 }
356
357 final Supplier<Hashtable<String, String>> adminEnv = () -> createAdminEnv();
358 final String userDN = fessConfig.getLdapAdminUserSecurityPrincipal(user.getName());
359
360 search(fessConfig.getLdapAdminUserBaseDn(), fessConfig.getLdapAdminUserFilter(user.getName()), null, adminEnv, result -> {
361 if (!result.isEmpty()) {
362 modifyUserAttributes(user, adminEnv, userDN, result, fessConfig);
363 } else {
364 final BasicAttributes entry = new BasicAttributes();
365 addUserAttributes(entry, user, fessConfig);
366 final Attribute oc = fessConfig.getLdapAdminUserObjectClassAttribute();
367 entry.put(oc);
368 insert(userDN, entry, adminEnv);
369 }
370 });
371
372
373 search(fessConfig.getLdapAdminUserBaseDn(),
374 fessConfig.getLdapAdminUserFilter(user.getName()),
375 new String[] { fessConfig.getLdapMemberofAttribute() },
376 adminEnv,
377 result -> {
378 if (!result.isEmpty()) {
379 final List<String> oldGroupList = new ArrayList<>();
380 final List<String> oldRoleList = new ArrayList<>();
381 final String lowerGroupDn = fessConfig.getLdapAdminGroupBaseDn().toLowerCase(Locale.ROOT);
382 final String lowerRoleDn = fessConfig.getLdapAdminRoleBaseDn().toLowerCase(Locale.ROOT);
383 processSearchRoles(result, (entryDn, name) -> {
384 final String lowerEntryDn = entryDn.toLowerCase(Locale.ROOT);
385 if (lowerEntryDn.indexOf(lowerGroupDn) != -1) {
386 oldGroupList.add(name);
387 } else if (lowerEntryDn.indexOf(lowerRoleDn) != -1) {
388 oldRoleList.add(name);
389 }
390 });
391 final List<String> newGroupList = stream(user.getGroupNames()).get(stream -> stream.collect(Collectors.toList()));
392 stream(user.getGroupNames()).of(stream -> stream.forEach(name -> {
393 if (oldGroupList.contains(name)) {
394 oldGroupList.remove(name);
395 newGroupList.remove(name);
396 }
397 }));
398 oldGroupList.stream().forEach(
399 name -> {
400 search(fessConfig.getLdapAdminGroupBaseDn(), fessConfig.getLdapAdminGroupFilter(name), null, adminEnv,
401 subResult -> {
402 if (!subResult.isEmpty()) {
403 final List<ModificationItem> modifyList = new ArrayList<>();
404 modifyDeleteEntry(modifyList, "member", userDN);
405 modify(fessConfig.getLdapAdminGroupSecurityPrincipal(name), modifyList, adminEnv);
406 }
407 });
408 });
409 newGroupList.stream().forEach(
410 name -> {
411 search(fessConfig.getLdapAdminGroupBaseDn(), fessConfig.getLdapAdminGroupFilter(name), null, adminEnv,
412 subResult -> {
413 if (!!subResult.isEmpty()) {
414 final Group group = new Group();
415 group.setName(name);
416 insert(group);
417 }
418 final List<ModificationItem> modifyList = new ArrayList<>();
419 modifyAddEntry(modifyList, "member", userDN);
420 modify(fessConfig.getLdapAdminGroupSecurityPrincipal(name), modifyList, adminEnv);
421 });
422 });
423
424 final List<String> newRoleList = stream(user.getRoleNames()).get(stream -> stream.collect(Collectors.toList()));
425 stream(user.getRoleNames()).of(stream -> stream.forEach(name -> {
426 if (oldRoleList.contains(name)) {
427 oldRoleList.remove(name);
428 newRoleList.remove(name);
429 }
430 }));
431 oldRoleList.stream().forEach(
432 name -> {
433 search(fessConfig.getLdapAdminRoleBaseDn(), fessConfig.getLdapAdminRoleFilter(name), null, adminEnv,
434 subResult -> {
435 if (!subResult.isEmpty()) {
436 final List<ModificationItem> modifyList = new ArrayList<>();
437 modifyDeleteEntry(modifyList, "member", userDN);
438 modify(fessConfig.getLdapAdminRoleSecurityPrincipal(name), modifyList, adminEnv);
439 }
440 });
441 });
442 newRoleList.stream().forEach(
443 name -> {
444 search(fessConfig.getLdapAdminRoleBaseDn(), fessConfig.getLdapAdminRoleFilter(name), null, adminEnv,
445 subResult -> {
446 if (!!subResult.isEmpty()) {
447 final Role role = new Role();
448 role.setName(name);
449 insert(role);
450 }
451 final List<ModificationItem> modifyList = new ArrayList<>();
452 modifyAddEntry(modifyList, "member", userDN);
453 modify(fessConfig.getLdapAdminRoleSecurityPrincipal(name), modifyList, adminEnv);
454 });
455 });
456 } else {
457 stream(user.getGroupNames()).of(
458 stream -> stream.forEach(name -> {
459 search(fessConfig.getLdapAdminGroupBaseDn(), fessConfig.getLdapAdminGroupFilter(name), null, adminEnv,
460 subResult -> {
461 if (!!subResult.isEmpty()) {
462 final Group group = new Group();
463 group.setName(name);
464 insert(group);
465 }
466 final List<ModificationItem> modifyList = new ArrayList<>();
467 modifyAddEntry(modifyList, "member", userDN);
468 modify(fessConfig.getLdapAdminGroupSecurityPrincipal(name), modifyList, adminEnv);
469 });
470 }));
471
472 stream(user.getRoleNames()).of(
473 stream -> stream.forEach(name -> {
474 search(fessConfig.getLdapAdminRoleBaseDn(), fessConfig.getLdapAdminRoleFilter(name), null, adminEnv,
475 subResult -> {
476 if (!!subResult.isEmpty()) {
477 final Role role = new Role();
478 role.setName(name);
479 insert(role);
480 }
481 final List<ModificationItem> modifyList = new ArrayList<>();
482 modifyAddEntry(modifyList, "member", userDN);
483 modify(fessConfig.getLdapAdminRoleSecurityPrincipal(name), modifyList, adminEnv);
484 });
485 }));
486 }
487 });
488
489 }
490
491 protected void modifyUserAttributes(final User user, final Supplier<Hashtable<String, String>> adminEnv, final String userDN,
492 final List<SearchResult> result, final FessConfig fessConfig) {
493 final List<ModificationItem> modifyList = new ArrayList<>();
494 if (user.getOriginalPassword() != null) {
495 modifyReplaceEntry(modifyList, "userPassword", user.getOriginalPassword());
496 }
497
498 final String attrSurname = fessConfig.getLdapAttrSurname();
499 OptionalUtil
500 .ofNullable(user.getSurname())
501 .filter(StringUtil::isNotBlank)
502 .ifPresent(s -> modifyReplaceEntry(modifyList, attrSurname, s))
503 .orElse(() -> getAttributeValueList(result, attrSurname).stream().forEach(
504 v -> modifyDeleteEntry(modifyList, attrSurname, v)));
505 final String attrGivenName = fessConfig.getLdapAttrGivenName();
506 OptionalUtil
507 .ofNullable(user.getGivenName())
508 .filter(StringUtil::isNotBlank)
509 .ifPresent(s -> modifyReplaceEntry(modifyList, attrGivenName, s))
510 .orElse(() -> getAttributeValueList(result, attrGivenName).stream().forEach(
511 v -> modifyDeleteEntry(modifyList, attrGivenName, v)));
512 final String attrMail = fessConfig.getLdapAttrMail();
513 OptionalUtil.ofNullable(user.getMail()).filter(StringUtil::isNotBlank).ifPresent(s -> modifyReplaceEntry(modifyList, attrMail, s))
514 .orElse(() -> getAttributeValueList(result, attrMail).stream().forEach(v -> modifyDeleteEntry(modifyList, attrMail, v)));
515 final String attrEmployeeNumber = fessConfig.getLdapAttrEmployeeNumber();
516 OptionalUtil
517 .ofNullable(user.getEmployeeNumber())
518 .filter(StringUtil::isNotBlank)
519 .ifPresent(s -> modifyReplaceEntry(modifyList, attrEmployeeNumber, s))
520 .orElse(() -> getAttributeValueList(result, attrEmployeeNumber).stream().forEach(
521 v -> modifyDeleteEntry(modifyList, attrEmployeeNumber, v)));
522 final String attrTelephoneNumber = fessConfig.getLdapAttrTelephoneNumber();
523 OptionalUtil
524 .ofNullable(user.getTelephoneNumber())
525 .filter(StringUtil::isNotBlank)
526 .ifPresent(s -> modifyReplaceEntry(modifyList, attrTelephoneNumber, s))
527 .orElse(() -> getAttributeValueList(result, attrTelephoneNumber).stream().forEach(
528 v -> modifyDeleteEntry(modifyList, attrTelephoneNumber, v)));
529 final String attrHomePhone = fessConfig.getLdapAttrHomePhone();
530 OptionalUtil
531 .ofNullable(user.getHomePhone())
532 .filter(StringUtil::isNotBlank)
533 .ifPresent(s -> modifyReplaceEntry(modifyList, attrHomePhone, s))
534 .orElse(() -> getAttributeValueList(result, attrHomePhone).stream().forEach(
535 v -> modifyDeleteEntry(modifyList, attrHomePhone, v)));
536 final String attrHomePostalAddress = fessConfig.getLdapAttrHomePostalAddress();
537 OptionalUtil
538 .ofNullable(user.getHomePostalAddress())
539 .filter(StringUtil::isNotBlank)
540 .ifPresent(s -> modifyReplaceEntry(modifyList, attrHomePostalAddress, s))
541 .orElse(() -> getAttributeValueList(result, attrHomePostalAddress).stream().forEach(
542 v -> modifyDeleteEntry(modifyList, attrHomePostalAddress, v)));
543 final String attrLabeledURI = fessConfig.getLdapAttrLabeleduri();
544 OptionalUtil
545 .ofNullable(user.getLabeledURI())
546 .filter(StringUtil::isNotBlank)
547 .ifPresent(s -> modifyReplaceEntry(modifyList, attrLabeledURI, s))
548 .orElse(() -> getAttributeValueList(result, attrLabeledURI).stream().forEach(
549 v -> modifyDeleteEntry(modifyList, attrLabeledURI, v)));
550 final String attrRoomNumber = fessConfig.getLdapAttrRoomNumber();
551 OptionalUtil
552 .ofNullable(user.getRoomNumber())
553 .filter(StringUtil::isNotBlank)
554 .ifPresent(s -> modifyReplaceEntry(modifyList, attrRoomNumber, s))
555 .orElse(() -> getAttributeValueList(result, attrRoomNumber).stream().forEach(
556 v -> modifyDeleteEntry(modifyList, attrRoomNumber, v)));
557 final String attrDescription = fessConfig.getLdapAttrDescription();
558 OptionalUtil
559 .ofNullable(user.getDescription())
560 .filter(StringUtil::isNotBlank)
561 .ifPresent(s -> modifyReplaceEntry(modifyList, attrDescription, s))
562 .orElse(() -> getAttributeValueList(result, attrDescription).stream().forEach(
563 v -> modifyDeleteEntry(modifyList, attrDescription, v)));
564 final String attrTitle = fessConfig.getLdapAttrTitle();
565 OptionalUtil.ofNullable(user.getTitle()).filter(StringUtil::isNotBlank)
566 .ifPresent(s -> modifyReplaceEntry(modifyList, attrTitle, s))
567 .orElse(() -> getAttributeValueList(result, attrTitle).stream().forEach(v -> modifyDeleteEntry(modifyList, attrTitle, v)));
568 final String attrPager = fessConfig.getLdapAttrPager();
569 OptionalUtil.ofNullable(user.getPager()).filter(StringUtil::isNotBlank)
570 .ifPresent(s -> modifyReplaceEntry(modifyList, attrPager, s))
571 .orElse(() -> getAttributeValueList(result, attrPager).stream().forEach(v -> modifyDeleteEntry(modifyList, attrPager, v)));
572 final String attrStreet = fessConfig.getLdapAttrStreet();
573 OptionalUtil
574 .ofNullable(user.getStreet())
575 .filter(StringUtil::isNotBlank)
576 .ifPresent(s -> modifyReplaceEntry(modifyList, attrStreet, s))
577 .orElse(() -> getAttributeValueList(result, attrStreet).stream().forEach(v -> modifyDeleteEntry(modifyList, attrStreet, v)));
578 final String attrPostalCode = fessConfig.getLdapAttrPostalCode();
579 OptionalUtil
580 .ofNullable(user.getPostalCode())
581 .filter(StringUtil::isNotBlank)
582 .ifPresent(s -> modifyReplaceEntry(modifyList, attrPostalCode, s))
583 .orElse(() -> getAttributeValueList(result, attrPostalCode).stream().forEach(
584 v -> modifyDeleteEntry(modifyList, attrPostalCode, v)));
585 final String attrPhysicalDeliveryOfficeName = fessConfig.getLdapAttrPhysicalDeliveryOfficeName();
586 OptionalUtil
587 .ofNullable(user.getPhysicalDeliveryOfficeName())
588 .filter(StringUtil::isNotBlank)
589 .ifPresent(s -> modifyReplaceEntry(modifyList, attrPhysicalDeliveryOfficeName, s))
590 .orElse(() -> getAttributeValueList(result, attrPhysicalDeliveryOfficeName).stream().forEach(
591 v -> modifyDeleteEntry(modifyList, attrPhysicalDeliveryOfficeName, v)));
592 final String attrDestinationIndicator = fessConfig.getLdapAttrDestinationIndicator();
593 OptionalUtil
594 .ofNullable(user.getDestinationIndicator())
595 .filter(StringUtil::isNotBlank)
596 .ifPresent(s -> modifyReplaceEntry(modifyList, attrDestinationIndicator, s))
597 .orElse(() -> getAttributeValueList(result, attrDestinationIndicator).stream().forEach(
598 v -> modifyDeleteEntry(modifyList, attrDestinationIndicator, v)));
599 final String attrInternationaliSDNNumber = fessConfig.getLdapAttrInternationalisdnNumber();
600 OptionalUtil
601 .ofNullable(user.getInternationaliSDNNumber())
602 .filter(StringUtil::isNotBlank)
603 .ifPresent(s -> modifyReplaceEntry(modifyList, attrInternationaliSDNNumber, s))
604 .orElse(() -> getAttributeValueList(result, attrInternationaliSDNNumber).stream().forEach(
605 v -> modifyDeleteEntry(modifyList, attrInternationaliSDNNumber, v)));
606 final String attrState = fessConfig.getLdapAttrState();
607 OptionalUtil.ofNullable(user.getState()).filter(StringUtil::isNotBlank)
608 .ifPresent(s -> modifyReplaceEntry(modifyList, attrState, s))
609 .orElse(() -> getAttributeValueList(result, attrState).stream().forEach(v -> modifyDeleteEntry(modifyList, attrState, v)));
610 final String attrEmployeeType = fessConfig.getLdapAttrEmployeeType();
611 OptionalUtil
612 .ofNullable(user.getEmployeeType())
613 .filter(StringUtil::isNotBlank)
614 .ifPresent(s -> modifyReplaceEntry(modifyList, attrEmployeeType, s))
615 .orElse(() -> getAttributeValueList(result, attrEmployeeType).stream().forEach(
616 v -> modifyDeleteEntry(modifyList, attrEmployeeType, v)));
617 final String attrFacsimileTelephoneNumber = fessConfig.getLdapAttrFacsimileTelephoneNumber();
618 OptionalUtil
619 .ofNullable(user.getFacsimileTelephoneNumber())
620 .filter(StringUtil::isNotBlank)
621 .ifPresent(s -> modifyReplaceEntry(modifyList, attrFacsimileTelephoneNumber, s))
622 .orElse(() -> getAttributeValueList(result, attrFacsimileTelephoneNumber).stream().forEach(
623 v -> modifyDeleteEntry(modifyList, attrFacsimileTelephoneNumber, v)));
624 final String attrPostOfficeBox = fessConfig.getLdapAttrPostOfficeBox();
625 OptionalUtil
626 .ofNullable(user.getPostOfficeBox())
627 .filter(StringUtil::isNotBlank)
628 .ifPresent(s -> modifyReplaceEntry(modifyList, attrPostOfficeBox, s))
629 .orElse(() -> getAttributeValueList(result, attrPostOfficeBox).stream().forEach(
630 v -> modifyDeleteEntry(modifyList, attrPostOfficeBox, v)));
631 final String attrInitials = fessConfig.getLdapAttrInitials();
632 OptionalUtil
633 .ofNullable(user.getInitials())
634 .filter(StringUtil::isNotBlank)
635 .ifPresent(s -> modifyReplaceEntry(modifyList, attrInitials, s))
636 .orElse(() -> getAttributeValueList(result, attrInitials).stream().forEach(
637 v -> modifyDeleteEntry(modifyList, attrInitials, v)));
638 final String attrCarLicense = fessConfig.getLdapAttrCarLicense();
639 OptionalUtil
640 .ofNullable(user.getCarLicense())
641 .filter(StringUtil::isNotBlank)
642 .ifPresent(s -> modifyReplaceEntry(modifyList, attrCarLicense, s))
643 .orElse(() -> getAttributeValueList(result, attrCarLicense).stream().forEach(
644 v -> modifyDeleteEntry(modifyList, attrCarLicense, v)));
645 final String attrMobile = fessConfig.getLdapAttrMobile();
646 OptionalUtil
647 .ofNullable(user.getMobile())
648 .filter(StringUtil::isNotBlank)
649 .ifPresent(s -> modifyReplaceEntry(modifyList, attrMobile, s))
650 .orElse(() -> getAttributeValueList(result, attrMobile).stream().forEach(v -> modifyDeleteEntry(modifyList, attrMobile, v)));
651 final String attrPostalAddress = fessConfig.getLdapAttrPostalAddress();
652 OptionalUtil
653 .ofNullable(user.getPostalAddress())
654 .filter(StringUtil::isNotBlank)
655 .ifPresent(s -> modifyReplaceEntry(modifyList, attrPostalAddress, s))
656 .orElse(() -> getAttributeValueList(result, attrPostalAddress).stream().forEach(
657 v -> modifyDeleteEntry(modifyList, attrPostalAddress, v)));
658 final String attrCity = fessConfig.getLdapAttrCity();
659 OptionalUtil.ofNullable(user.getCity()).filter(StringUtil::isNotBlank).ifPresent(s -> modifyReplaceEntry(modifyList, attrCity, s))
660 .orElse(() -> getAttributeValueList(result, attrCity).stream().forEach(v -> modifyDeleteEntry(modifyList, attrCity, v)));
661 final String attrTeletexTerminalIdentifier = fessConfig.getLdapAttrTeletexTerminalIdentifier();
662 OptionalUtil
663 .ofNullable(user.getTeletexTerminalIdentifier())
664 .filter(StringUtil::isNotBlank)
665 .ifPresent(s -> modifyReplaceEntry(modifyList, attrTeletexTerminalIdentifier, s))
666 .orElse(() -> getAttributeValueList(result, attrTeletexTerminalIdentifier).stream().forEach(
667 v -> modifyDeleteEntry(modifyList, attrTeletexTerminalIdentifier, v)));
668 final String attrX121Address = fessConfig.getLdapAttrX121Address();
669 OptionalUtil
670 .ofNullable(user.getX121Address())
671 .filter(StringUtil::isNotBlank)
672 .ifPresent(s -> modifyReplaceEntry(modifyList, attrX121Address, s))
673 .orElse(() -> getAttributeValueList(result, attrX121Address).stream().forEach(
674 v -> modifyDeleteEntry(modifyList, attrX121Address, v)));
675 final String attrBusinessCategory = fessConfig.getLdapAttrBusinessCategory();
676 OptionalUtil
677 .ofNullable(user.getBusinessCategory())
678 .filter(StringUtil::isNotBlank)
679 .ifPresent(s -> modifyReplaceEntry(modifyList, attrBusinessCategory, s))
680 .orElse(() -> getAttributeValueList(result, attrBusinessCategory).stream().forEach(
681 v -> modifyDeleteEntry(modifyList, attrBusinessCategory, v)));
682 final String attrRegisteredAddress = fessConfig.getLdapAttrRegisteredAddress();
683 OptionalUtil
684 .ofNullable(user.getRegisteredAddress())
685 .filter(StringUtil::isNotBlank)
686 .ifPresent(s -> modifyReplaceEntry(modifyList, attrRegisteredAddress, s))
687 .orElse(() -> getAttributeValueList(result, attrRegisteredAddress).stream().forEach(
688 v -> modifyDeleteEntry(modifyList, attrRegisteredAddress, v)));
689 final String attrDisplayName = fessConfig.getLdapAttrDisplayName();
690 OptionalUtil
691 .ofNullable(user.getDisplayName())
692 .filter(StringUtil::isNotBlank)
693 .ifPresent(s -> modifyReplaceEntry(modifyList, attrDisplayName, s))
694 .orElse(() -> getAttributeValueList(result, attrDisplayName).stream().forEach(
695 v -> modifyDeleteEntry(modifyList, attrDisplayName, v)));
696 final String attrPreferredLanguage = fessConfig.getLdapAttrPreferredLanguage();
697 OptionalUtil
698 .ofNullable(user.getPreferredLanguage())
699 .filter(StringUtil::isNotBlank)
700 .ifPresent(s -> modifyReplaceEntry(modifyList, attrPreferredLanguage, s))
701 .orElse(() -> getAttributeValueList(result, attrPreferredLanguage).stream().forEach(
702 v -> modifyDeleteEntry(modifyList, attrPreferredLanguage, v)));
703 final String attrDepartmentNumber = fessConfig.getLdapAttrDepartmentNumber();
704 OptionalUtil
705 .ofNullable(user.getDepartmentNumber())
706 .filter(StringUtil::isNotBlank)
707 .ifPresent(s -> modifyReplaceEntry(modifyList, attrDepartmentNumber, s))
708 .orElse(() -> getAttributeValueList(result, attrDepartmentNumber).stream().forEach(
709 v -> modifyDeleteEntry(modifyList, attrDepartmentNumber, v)));
710 final String attrUidNumber = fessConfig.getLdapAttrUidNumber();
711 OptionalUtil
712 .ofNullable(user.getUidNumber())
713 .filter(s -> StringUtil.isNotBlank(s.toString()))
714 .ifPresent(s -> modifyReplaceEntry(modifyList, attrUidNumber, s.toString()))
715 .orElse(() -> getAttributeValueList(result, attrUidNumber).stream().forEach(
716 v -> modifyDeleteEntry(modifyList, attrUidNumber, v)));
717 final String attrGidNumber = fessConfig.getLdapAttrGidNumber();
718 OptionalUtil
719 .ofNullable(user.getGidNumber())
720 .filter(s -> StringUtil.isNotBlank(s.toString()))
721 .ifPresent(s -> modifyReplaceEntry(modifyList, attrGidNumber, s.toString()))
722 .orElse(() -> getAttributeValueList(result, attrGidNumber).stream().forEach(
723 v -> modifyDeleteEntry(modifyList, attrGidNumber, v)));
724 final String attrHomeDirectory = fessConfig.getLdapAttrHomeDirectory();
725 OptionalUtil
726 .ofNullable(user.getHomeDirectory())
727 .filter(StringUtil::isNotBlank)
728 .ifPresent(s -> modifyReplaceEntry(modifyList, attrHomeDirectory, s))
729 .orElse(() -> getAttributeValueList(result, attrHomeDirectory).stream().forEach(
730 v -> modifyDeleteEntry(modifyList, attrHomeDirectory, v)));
731
732 modify(userDN, modifyList, adminEnv);
733 }
734
735 protected void addUserAttributes(final BasicAttributes entry, final User user, final FessConfig fessConfig) {
736 entry.put(new BasicAttribute("cn", user.getName()));
737 entry.put(new BasicAttribute("userPassword", user.getOriginalPassword()));
738
739 OptionalUtil.ofNullable(user.getSurname()).filter(StringUtil::isNotBlank)
740 .ifPresent(s -> entry.put(new BasicAttribute(fessConfig.getLdapAttrSurname(), s)));
741 OptionalUtil.ofNullable(user.getGivenName()).filter(StringUtil::isNotBlank)
742 .ifPresent(s -> entry.put(new BasicAttribute(fessConfig.getLdapAttrGivenName(), s)));
743 OptionalUtil.ofNullable(user.getMail()).filter(StringUtil::isNotBlank)
744 .ifPresent(s -> entry.put(new BasicAttribute(fessConfig.getLdapAttrMail(), s)));
745 OptionalUtil.ofNullable(user.getEmployeeNumber()).filter(StringUtil::isNotBlank)
746 .ifPresent(s -> entry.put(new BasicAttribute(fessConfig.getLdapAttrEmployeeNumber(), s)));
747 OptionalUtil.ofNullable(user.getTelephoneNumber()).filter(StringUtil::isNotBlank)
748 .ifPresent(s -> entry.put(new BasicAttribute(fessConfig.getLdapAttrTelephoneNumber(), s)));
749 OptionalUtil.ofNullable(user.getHomePhone()).filter(StringUtil::isNotBlank)
750 .ifPresent(s -> entry.put(new BasicAttribute(fessConfig.getLdapAttrHomePhone(), s)));
751 OptionalUtil.ofNullable(user.getHomePostalAddress()).filter(StringUtil::isNotBlank)
752 .ifPresent(s -> entry.put(new BasicAttribute(fessConfig.getLdapAttrHomePostalAddress(), s)));
753 OptionalUtil.ofNullable(user.getLabeledURI()).filter(StringUtil::isNotBlank)
754 .ifPresent(s -> entry.put(new BasicAttribute(fessConfig.getLdapAttrLabeleduri(), s)));
755 OptionalUtil.ofNullable(user.getRoomNumber()).filter(StringUtil::isNotBlank)
756 .ifPresent(s -> entry.put(new BasicAttribute(fessConfig.getLdapAttrRoomNumber(), s)));
757 OptionalUtil.ofNullable(user.getDescription()).filter(StringUtil::isNotBlank)
758 .ifPresent(s -> entry.put(new BasicAttribute(fessConfig.getLdapAttrDescription(), s)));
759 OptionalUtil.ofNullable(user.getTitle()).filter(StringUtil::isNotBlank)
760 .ifPresent(s -> entry.put(new BasicAttribute(fessConfig.getLdapAttrTitle(), s)));
761 OptionalUtil.ofNullable(user.getPager()).filter(StringUtil::isNotBlank)
762 .ifPresent(s -> entry.put(new BasicAttribute(fessConfig.getLdapAttrPager(), s)));
763 OptionalUtil.ofNullable(user.getStreet()).filter(StringUtil::isNotBlank)
764 .ifPresent(s -> entry.put(new BasicAttribute(fessConfig.getLdapAttrStreet(), s)));
765 OptionalUtil.ofNullable(user.getPostalCode()).filter(StringUtil::isNotBlank)
766 .ifPresent(s -> entry.put(new BasicAttribute(fessConfig.getLdapAttrPostalCode(), s)));
767 OptionalUtil.ofNullable(user.getPhysicalDeliveryOfficeName()).filter(StringUtil::isNotBlank)
768 .ifPresent(s -> entry.put(new BasicAttribute(fessConfig.getLdapAttrPhysicalDeliveryOfficeName(), s)));
769 OptionalUtil.ofNullable(user.getDestinationIndicator()).filter(StringUtil::isNotBlank)
770 .ifPresent(s -> entry.put(new BasicAttribute(fessConfig.getLdapAttrDestinationIndicator(), s)));
771 OptionalUtil.ofNullable(user.getInternationaliSDNNumber()).filter(StringUtil::isNotBlank)
772 .ifPresent(s -> entry.put(new BasicAttribute(fessConfig.getLdapAttrInternationalisdnNumber(), s)));
773 OptionalUtil.ofNullable(user.getState()).filter(StringUtil::isNotBlank)
774 .ifPresent(s -> entry.put(new BasicAttribute(fessConfig.getLdapAttrState(), s)));
775 OptionalUtil.ofNullable(user.getEmployeeType()).filter(StringUtil::isNotBlank)
776 .ifPresent(s -> entry.put(new BasicAttribute(fessConfig.getLdapAttrEmployeeType(), s)));
777 OptionalUtil.ofNullable(user.getFacsimileTelephoneNumber()).filter(StringUtil::isNotBlank)
778 .ifPresent(s -> entry.put(new BasicAttribute(fessConfig.getLdapAttrFacsimileTelephoneNumber(), s)));
779 OptionalUtil.ofNullable(user.getPostOfficeBox()).filter(StringUtil::isNotBlank)
780 .ifPresent(s -> entry.put(new BasicAttribute(fessConfig.getLdapAttrPostOfficeBox(), s)));
781 OptionalUtil.ofNullable(user.getInitials()).filter(StringUtil::isNotBlank)
782 .ifPresent(s -> entry.put(new BasicAttribute(fessConfig.getLdapAttrInitials(), s)));
783 OptionalUtil.ofNullable(user.getCarLicense()).filter(StringUtil::isNotBlank)
784 .ifPresent(s -> entry.put(new BasicAttribute(fessConfig.getLdapAttrCarLicense(), s)));
785 OptionalUtil.ofNullable(user.getMobile()).filter(StringUtil::isNotBlank)
786 .ifPresent(s -> entry.put(new BasicAttribute(fessConfig.getLdapAttrMobile(), s)));
787 OptionalUtil.ofNullable(user.getPostalAddress()).filter(StringUtil::isNotBlank)
788 .ifPresent(s -> entry.put(new BasicAttribute(fessConfig.getLdapAttrPostalAddress(), s)));
789 OptionalUtil.ofNullable(user.getCity()).filter(StringUtil::isNotBlank)
790 .ifPresent(s -> entry.put(new BasicAttribute(fessConfig.getLdapAttrCity(), s)));
791 OptionalUtil.ofNullable(user.getTeletexTerminalIdentifier()).filter(StringUtil::isNotBlank)
792 .ifPresent(s -> entry.put(new BasicAttribute(fessConfig.getLdapAttrTeletexTerminalIdentifier(), s)));
793 OptionalUtil.ofNullable(user.getX121Address()).filter(StringUtil::isNotBlank)
794 .ifPresent(s -> entry.put(new BasicAttribute(fessConfig.getLdapAttrX121Address(), s)));
795 OptionalUtil.ofNullable(user.getBusinessCategory()).filter(StringUtil::isNotBlank)
796 .ifPresent(s -> entry.put(new BasicAttribute(fessConfig.getLdapAttrBusinessCategory(), s)));
797 OptionalUtil.ofNullable(user.getRegisteredAddress()).filter(StringUtil::isNotBlank)
798 .ifPresent(s -> entry.put(new BasicAttribute(fessConfig.getLdapAttrRegisteredAddress(), s)));
799 OptionalUtil.ofNullable(user.getDisplayName()).filter(StringUtil::isNotBlank)
800 .ifPresent(s -> entry.put(new BasicAttribute(fessConfig.getLdapAttrDisplayName(), s)));
801 OptionalUtil.ofNullable(user.getPreferredLanguage()).filter(StringUtil::isNotBlank)
802 .ifPresent(s -> entry.put(new BasicAttribute(fessConfig.getLdapAttrPreferredLanguage(), s)));
803 OptionalUtil.ofNullable(user.getDepartmentNumber()).filter(StringUtil::isNotBlank)
804 .ifPresent(s -> entry.put(new BasicAttribute(fessConfig.getLdapAttrDepartmentNumber(), s)));
805 OptionalUtil.ofNullable(user.getUidNumber()).filter(s -> StringUtil.isNotBlank(s.toString()))
806 .ifPresent(s -> entry.put(new BasicAttribute(fessConfig.getLdapAttrUidNumber(), s)));
807 OptionalUtil.ofNullable(user.getGidNumber()).filter(s -> StringUtil.isNotBlank(s.toString()))
808 .ifPresent(s -> entry.put(new BasicAttribute(fessConfig.getLdapAttrGidNumber(), s)));
809 OptionalUtil.ofNullable(user.getHomeDirectory()).filter(StringUtil::isNotBlank)
810 .ifPresent(s -> entry.put(new BasicAttribute(fessConfig.getLdapAttrHomeDirectory(), s)));
811 }
812
813 public void delete(final User user) {
814 final FessConfig fessConfig = ComponentUtil.getFessConfig();
815 if (!fessConfig.isLdapAdminEnabled(user.getName())) {
816 return;
817 }
818
819 final Supplier<Hashtable<String, String>> adminEnv = () -> createAdminEnv();
820 final String userDN = fessConfig.getLdapAdminUserSecurityPrincipal(user.getName());
821
822 stream(user.getGroupNames()).of(stream -> stream.forEach(name -> {
823 search(fessConfig.getLdapAdminGroupBaseDn(), fessConfig.getLdapAdminGroupFilter(name), null, adminEnv, subResult -> {
824 if (!!subResult.isEmpty()) {
825 final Group group = new Group();
826 group.setName(name);
827 insert(group);
828 }
829 final List<ModificationItem> modifyList = new ArrayList<>();
830 modifyDeleteEntry(modifyList, "member", userDN);
831 modify(fessConfig.getLdapAdminGroupSecurityPrincipal(name), modifyList, adminEnv);
832 });
833 }));
834 stream(user.getRoleNames()).of(stream -> stream.forEach(name -> {
835 search(fessConfig.getLdapAdminRoleBaseDn(), fessConfig.getLdapAdminRoleFilter(name), null, adminEnv, subResult -> {
836 if (!!subResult.isEmpty()) {
837 final Role role = new Role();
838 role.setName(name);
839 insert(role);
840 }
841 final List<ModificationItem> modifyList = new ArrayList<>();
842 modifyDeleteEntry(modifyList, "member", userDN);
843 modify(fessConfig.getLdapAdminRoleSecurityPrincipal(name), modifyList, adminEnv);
844 });
845 }));
846
847 search(fessConfig.getLdapAdminUserBaseDn(), fessConfig.getLdapAdminUserFilter(user.getName()), null, adminEnv, result -> {
848 if (!result.isEmpty()) {
849 delete(userDN, adminEnv);
850 } else {
851 logger.info("{} does not exist in LDAP server.", user.getName());
852 }
853 });
854
855 }
856
857 public void insert(final Role role) {
858 final FessConfig fessConfig = ComponentUtil.getFessConfig();
859 if (!fessConfig.isLdapAdminEnabled()) {
860 return;
861 }
862
863 final Supplier<Hashtable<String, String>> adminEnv = () -> createAdminEnv();
864 search(fessConfig.getLdapAdminRoleBaseDn(), fessConfig.getLdapAdminRoleFilter(role.getName()), null, adminEnv, result -> {
865 if (!result.isEmpty()) {
866 logger.info("{} exists in LDAP server.", role.getName());
867 } else {
868 final String entryDN = fessConfig.getLdapAdminRoleSecurityPrincipal(role.getName());
869 final BasicAttributes entry = new BasicAttributes();
870 addRoleAttributes(entry, role, fessConfig);
871 final Attribute oc = fessConfig.getLdapAdminRoleObjectClassAttribute();
872 entry.put(oc);
873 insert(entryDN, entry, adminEnv);
874 }
875 });
876
877 }
878
879 protected void addRoleAttributes(final BasicAttributes entry, final Role user, final FessConfig fessConfig) {
880
881 }
882
883 public void delete(final Role role) {
884 final FessConfig fessConfig = ComponentUtil.getFessConfig();
885 if (!fessConfig.isLdapAdminEnabled()) {
886 return;
887 }
888
889 final Supplier<Hashtable<String, String>> adminEnv = () -> createAdminEnv();
890 search(fessConfig.getLdapAdminRoleBaseDn(), fessConfig.getLdapAdminRoleFilter(role.getName()), null, adminEnv, result -> {
891 if (!result.isEmpty()) {
892 final String entryDN = fessConfig.getLdapAdminRoleSecurityPrincipal(role.getName());
893 delete(entryDN, adminEnv);
894 } else {
895 logger.info("{} does not exist in LDAP server.", role.getName());
896 }
897 });
898
899 }
900
901 public void apply(final Group group) {
902 final FessConfig fessConfig = ComponentUtil.getFessConfig();
903 if (!fessConfig.isLdapAdminEnabled()) {
904 return;
905 }
906
907 final Supplier<Hashtable<String, String>> adminEnv = () -> createAdminEnv();
908 search(fessConfig.getLdapAdminGroupBaseDn(), fessConfig.getLdapAdminGroupFilter(group.getName()), null, adminEnv, result -> {
909 if (!result.isEmpty()) {
910 setAttributeValue(result, fessConfig.getLdapAttrGidNumber(), o -> group.setGidNumber(DfTypeUtil.toLong(o)));
911 }
912 });
913 }
914
915 public void insert(final Group group) {
916 final FessConfig fessConfig = ComponentUtil.getFessConfig();
917 if (!fessConfig.isLdapAdminEnabled()) {
918 return;
919 }
920
921 final Supplier<Hashtable<String, String>> adminEnv = () -> createAdminEnv();
922 final String entryDN = fessConfig.getLdapAdminGroupSecurityPrincipal(group.getName());
923 search(fessConfig.getLdapAdminGroupBaseDn(), fessConfig.getLdapAdminGroupFilter(group.getName()), null, adminEnv, result -> {
924 if (!result.isEmpty()) {
925 logger.info("{} exists in LDAP server.", group.getName());
926 modifyGroupAttributes(group, adminEnv, entryDN, result, fessConfig);
927 } else {
928 final BasicAttributes entry = new BasicAttributes();
929 addGroupAttributes(entry, group, fessConfig);
930 final Attribute oc = fessConfig.getLdapAdminGroupObjectClassAttribute();
931 entry.put(oc);
932 insert(entryDN, entry, adminEnv);
933 }
934 });
935 }
936
937 protected void modifyGroupAttributes(final Group group, final Supplier<Hashtable<String, String>> adminEnv, final String entryDN,
938 final List<SearchResult> result, final FessConfig fessConfig) {
939 final List<ModificationItem> modifyList = new ArrayList<>();
940
941 final String attrGidNumber = fessConfig.getLdapAttrGidNumber();
942 OptionalUtil
943 .ofNullable(group.getGidNumber())
944 .filter(s -> StringUtil.isNotBlank(s.toString()))
945 .ifPresent(s -> modifyReplaceEntry(modifyList, attrGidNumber, s.toString()))
946 .orElse(() -> getAttributeValueList(result, attrGidNumber).stream().forEach(
947 v -> modifyDeleteEntry(modifyList, attrGidNumber, v)));
948
949 modify(entryDN, modifyList, adminEnv);
950 }
951
952 protected void addGroupAttributes(final BasicAttributes entry, final Group group, final FessConfig fessConfig) {
953 OptionalUtil.ofNullable(group.getGidNumber()).filter(s -> StringUtil.isNotBlank(s.toString()))
954 .ifPresent(s -> entry.put(new BasicAttribute(fessConfig.getLdapAttrGidNumber(), s)));
955 }
956
957 public void delete(final Group group) {
958 final FessConfig fessConfig = ComponentUtil.getFessConfig();
959 if (!fessConfig.isLdapAdminEnabled()) {
960 return;
961 }
962
963 final Supplier<Hashtable<String, String>> adminEnv = () -> createAdminEnv();
964 search(fessConfig.getLdapAdminGroupBaseDn(), fessConfig.getLdapAdminGroupFilter(group.getName()), null, adminEnv, result -> {
965 if (!result.isEmpty()) {
966 final String entryDN = fessConfig.getLdapAdminGroupSecurityPrincipal(group.getName());
967 delete(entryDN, adminEnv);
968 } else {
969 logger.info("{} does not exist in LDAP server.", group.getName());
970 }
971 });
972 }
973
974 public boolean changePassword(final String username, final String password) {
975 final FessConfig fessConfig = ComponentUtil.getFessConfig();
976 if (!fessConfig.isLdapAdminEnabled(username)) {
977 return false;
978 }
979
980 final Supplier<Hashtable<String, String>> adminEnv = () -> createAdminEnv();
981 final String userDN = fessConfig.getLdapAdminUserSecurityPrincipal(username);
982 search(fessConfig.getLdapAdminUserBaseDn(), fessConfig.getLdapAdminUserFilter(username), null, adminEnv, result -> {
983 if (!result.isEmpty()) {
984 final List<ModificationItem> modifyList = new ArrayList<>();
985 modifyReplaceEntry(modifyList, "userPassword", password);
986 modify(userDN, modifyList, adminEnv);
987 } else {
988 throw new LdapOperationException("User is not found: " + username);
989 }
990 });
991 return true;
992 }
993
994 protected void insert(final String entryDN, final Attributes entry, final Supplier<Hashtable<String, String>> envSupplier) {
995 try (DirContextHolder holder = getDirContext(envSupplier)) {
996 logger.debug("Inserting {}", entryDN);
997 holder.get().createSubcontext(entryDN, entry);
998 } catch (final NamingException e) {
999 throw new LdapOperationException("Failed to add " + entryDN, e);
1000 }
1001 }
1002
1003 protected void delete(final String entryDN, final Supplier<Hashtable<String, String>> envSupplier) {
1004 try (DirContextHolder holder = getDirContext(envSupplier)) {
1005 logger.debug("Deleting {}", entryDN);
1006 holder.get().destroySubcontext(entryDN);
1007 } catch (final NamingException e) {
1008 throw new LdapOperationException("Failed to delete " + entryDN, e);
1009 }
1010 }
1011
1012 protected void search(final String baseDn, final String filter, final String[] returningAttrs,
1013 final Supplier<Hashtable<String, String>> envSupplier, final SearcConsumer consumer) {
1014 try (DirContextHolder holder = getDirContext(envSupplier)) {
1015 final SearchControls controls = new SearchControls();
1016 controls.setSearchScope(SearchControls.SUBTREE_SCOPE);
1017 if (returningAttrs != null) {
1018 controls.setReturningAttributes(returningAttrs);
1019 }
1020
1021 consumer.accept(Collections.list(holder.get().search(baseDn, filter, controls)));
1022 } catch (final NamingException e) {
1023 throw new LdapOperationException("Failed to search " + baseDn + " with " + filter, e);
1024 }
1025 }
1026
1027 protected void modifyAddEntry(final List<ModificationItem> modifyList, final String name, final String value) {
1028 final Attribute attr = new BasicAttribute(name, value);
1029 final ModificationItem mod = new ModificationItem(DirContext.ADD_ATTRIBUTE, attr);
1030 modifyList.add(mod);
1031 }
1032
1033 protected void modifyReplaceEntry(final List<ModificationItem> modifyList, final String name, final String value) {
1034 final Attribute attr = new BasicAttribute(name, value);
1035 final ModificationItem mod = new ModificationItem(DirContext.REPLACE_ATTRIBUTE, attr);
1036 modifyList.add(mod);
1037 }
1038
1039 protected void modifyDeleteEntry(final List<ModificationItem> modifyList, final String name, final Object value) {
1040 final Attribute attr = new BasicAttribute(name, value);
1041 final ModificationItem mod = new ModificationItem(DirContext.REMOVE_ATTRIBUTE, attr);
1042 modifyList.add(mod);
1043 }
1044
1045 protected void modify(final String dn, final List<ModificationItem> modifyList, final Supplier<Hashtable<String, String>> envSupplier) {
1046 if (modifyList.isEmpty()) {
1047 return;
1048 }
1049 try (DirContextHolder holder = getDirContext(envSupplier)) {
1050 holder.get().modifyAttributes(dn, modifyList.toArray(new ModificationItem[modifyList.size()]));
1051 } catch (final NamingException e) {
1052 throw new LdapOperationException("Failed to search " + dn, e);
1053 }
1054 }
1055
1056 interface SearcConsumer {
1057 void accept(List<SearchResult> t) throws NamingException;
1058 }
1059
1060 protected DirContextHolder getDirContext(final Supplier<Hashtable<String, String>> envSupplier) {
1061 DirContextHolder holder = contextLocal.get();
1062 if (holder == null) {
1063 final Hashtable<String, String> env = envSupplier.get();
1064 try {
1065 holder = new DirContextHolder(new InitialDirContext(env));
1066 contextLocal.set(holder);
1067 return holder;
1068 } catch (final NamingException e) {
1069 throw new LdapOperationException("Failed to create DirContext.", e);
1070 }
1071 } else {
1072 holder.inc();
1073 return holder;
1074 }
1075 }
1076
1077 protected class DirContextHolder implements AutoCloseable {
1078 private final DirContext context;
1079
1080 private int counter = 1;
1081
1082 protected DirContextHolder(final DirContext context) {
1083 this.context = context;
1084 }
1085
1086 public DirContext get() {
1087 return context;
1088 }
1089
1090 public void inc() {
1091 counter++;
1092 }
1093
1094 @Override
1095 public void close() {
1096 if (counter > 1) {
1097 counter--;
1098 } else {
1099 contextLocal.remove();
1100 if (context != null) {
1101 try {
1102 context.close();
1103 } catch (final NamingException e) {
1104
1105 }
1106 }
1107 }
1108 }
1109 }
1110 }