1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package fr.cnes.doi.plugin.impl.db;
20
21 import java.util.ArrayList;
22 import java.util.Hashtable;
23 import java.util.List;
24
25 import javax.naming.Context;
26 import javax.naming.NamingEnumeration;
27 import javax.naming.NamingException;
28 import javax.naming.directory.Attribute;
29 import javax.naming.directory.DirContext;
30 import javax.naming.directory.SearchControls;
31 import javax.naming.directory.SearchResult;
32 import javax.naming.ldap.InitialLdapContext;
33
34 import org.apache.logging.log4j.LogManager;
35 import org.apache.logging.log4j.Logger;
36
37 import fr.cnes.doi.exception.AuthenticationAccessException;
38 import fr.cnes.doi.db.model.AuthSystemUser;
39 import fr.cnes.doi.exception.DoiRuntimeException;
40 import fr.cnes.doi.utils.Utils;
41 import fr.cnes.doi.plugin.AbstractAuthenticationPluginHelper;
42 import fr.cnes.doi.settings.DoiSettings;
43 import java.util.Map;
44
45
46
47
48
49
50 public final class DefaultLDAPImpl extends AbstractAuthenticationPluginHelper {
51
52
53
54
55 public static final String LDAP_URL = "Starter.LDAP.url";
56
57
58
59
60 public static final String LDAP_USER = "Starter.LDAP.user";
61
62
63
64
65 public static final String LDAP_PWD = "Starter.LDAP.password";
66
67
68
69
70 public static final String LDAP_PROJECT = "Starter.LDAP.project";
71
72
73
74
75 public static final String LDAP_DOI_ADMIN = "Starter.LDAP.user.admin";
76
77
78
79
80 public static final String LDAP_SEARCH_GROUP = "Starter.LDAP.search.group";
81
82
83
84
85 public static final String LDAP_SEARCH_USER = "Starter.LDAP.search.user";
86
87
88
89
90 public static final String LDAP_ATTR_USERNAME = "Starter.LDAP.attr.username";
91
92
93
94
95 public static final String LDAP_ATTR_MAIL = "Starter.LDAP.attr.mail";
96
97
98
99
100 public static final String LDAP_ATTR_FULLNAME = "Starter.LDAP.attr.fullname";
101
102
103
104
105 private static final Logger LOGGER = LogManager.getLogger(DefaultLDAPImpl.class.getName());
106
107
108
109
110 private static final String DESCRIPTION = "Provides a pre-defined list of users and groups";
111
112
113
114 private static final String VERSION = "1.0.0";
115
116
117
118 private static final String OWNER = "CNES";
119
120
121
122 private static final String AUTHOR = "Jean-Christophe Malapert";
123
124
125
126 private static final String LICENSE = "LGPLV3";
127
128
129
130 private final String NAME = this.getClass().getName();
131
132
133
134
135 private Map<String, String> conf;
136
137
138
139
140 private boolean configured = false;
141
142
143
144
145 @Override
146 public void setConfiguration(final Object configuration) {
147 this.conf = (Map<String, String>) configuration;
148 LOGGER.info("[CONF] Plugin LDAP URL : {}", this.conf.get(LDAP_URL));
149 LOGGER.info("[CONF] Plugin LDAP user : {}", this.conf.get(LDAP_USER));
150 LOGGER.info("[CONF] Plugin LDAP password : {}", Utils.transformPasswordToStars(this.conf.
151 get(LDAP_PWD)));
152 LOGGER.info("[CONF] Plugin LDAP project : {}", this.conf.get(LDAP_PROJECT));
153 LOGGER.info("[CONF] Plugin LDAP admin for DOI : {}", this.conf.get(LDAP_DOI_ADMIN));
154 LOGGER.info("[CONF] Plugin LDAP attribute for fullname : {}", this.conf.get(
155 LDAP_ATTR_FULLNAME));
156 LOGGER.info("[CONF] Plugin LDAP attribute for mail : {}", this.conf.get(LDAP_ATTR_MAIL));
157 LOGGER.info("[CONF] Plugin LDAP attribute for username : {}", this.conf.get(
158 LDAP_ATTR_USERNAME));
159 LOGGER.info("[CONF] Plugin LDAP search group : {}", this.conf.get(LDAP_SEARCH_GROUP));
160 LOGGER.info("[CONF] Plugin LDAP search user : {}", this.conf.get(LDAP_SEARCH_USER));
161 this.configured = true;
162 }
163
164
165
166
167 @Override
168 public void initConnection() throws DoiRuntimeException {
169 try {
170 final InitialLdapContext context = getContext();
171 if (context == null) {
172 throw new DoiRuntimeException("LDAPAccessImpl: Unable to connect to Ldap");
173 } else {
174 context.close();
175 }
176 } catch (NamingException ex) {
177 throw new DoiRuntimeException("LDAPAccessImpl: Unable to connect to Ldap", ex);
178 }
179 }
180
181
182
183
184 @Override
185 public List<AuthSystemUser> getDOIProjectMembers() throws AuthenticationAccessException {
186 LOGGER.traceEntry();
187 DirContext context = null;
188 try {
189 try {
190 context = getContext();
191 } catch (NamingException ex) {
192 LOGGER.error("LDAPAccessImpl getContext: Unable to connect to Ldap", ex);
193 }
194 if (context == null) {
195 throw new AuthenticationAccessException("Configuration problem with the LDAP",
196 new Exception());
197 } else {
198 return LOGGER.traceExit(getAllDOIProjectMembers((InitialLdapContext) context));
199 }
200 } finally {
201 if (context != null) {
202 try {
203 context.close();
204 } catch (NamingException e) {
205 LOGGER.warn("LDAPAccessImpl getDOIProjectMembers: Unable to close the context",
206 e);
207 }
208 }
209 }
210 }
211
212
213
214
215
216
217 private boolean isLdapConfigured() {
218 final String ldapUser = conf.getOrDefault(LDAP_USER, "");
219 final String ldapPwd = conf.getOrDefault(LDAP_PWD, "");
220 final String ldapSearchUser = conf.getOrDefault(LDAP_SEARCH_USER, "");
221 return !ldapUser.isEmpty() && !ldapPwd.isEmpty() && !ldapSearchUser.isEmpty();
222 }
223
224
225
226
227
228
229
230 private InitialLdapContext getContext() throws NamingException {
231 LOGGER.traceEntry();
232 InitialLdapContext context = null;
233 if (isLdapConfigured()) {
234 final Hashtable<String, String> prop = new Hashtable<>();
235 final String ldapUser = conf.get(LDAP_USER);
236 final String ldapPwd = DoiSettings.getInstance().getSecretValue(conf.get(LDAP_PWD));
237 final String securityPrincipal = ldapUser;
238 final String ldapUrl = conf.get(LDAP_URL);
239 prop.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
240 prop.put(Context.PROVIDER_URL, ldapUrl);
241 prop.put(Context.SECURITY_AUTHENTICATION, "simple");
242 prop.put(Context.SECURITY_PRINCIPAL, securityPrincipal);
243 prop.put(Context.SECURITY_CREDENTIALS, ldapPwd);
244
245 LOGGER.info("LDAP context:\n {}={}\n {}={}\n {}={}\n {}={}\n {}={}",
246 Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory",
247 Context.PROVIDER_URL, ldapUrl,
248 Context.SECURITY_AUTHENTICATION, "simple",
249 Context.SECURITY_PRINCIPAL, securityPrincipal,
250 Context.SECURITY_CREDENTIALS, Utils.transformPasswordToStars(ldapPwd));
251 context = new InitialLdapContext(prop, null);
252 } else {
253 LOGGER.error("LDAP is not well configured. Checks the configuration file");
254 }
255 return LOGGER.traceExit(context);
256 }
257
258
259
260
261 @Override
262 public boolean authenticateUser(final String login, final String password) {
263 final Hashtable<String, String> prop = new Hashtable<>();
264 final String securityPrincipal = String.format(
265 "uid=%s,%s",
266 login,
267 conf.get(LDAP_SEARCH_USER));
268 final String ldapUrl = conf.get(LDAP_URL);
269 prop.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
270 prop.put(Context.PROVIDER_URL, ldapUrl);
271 prop.put(Context.SECURITY_AUTHENTICATION, "simple");
272 prop.put(Context.SECURITY_PRINCIPAL, securityPrincipal);
273 prop.put(Context.SECURITY_CREDENTIALS, password);
274
275 LOGGER.info("LDAP authentication:\n {}={}\n {}={}\n {}={}\n {}={}\n {}={}",
276 Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory",
277 Context.PROVIDER_URL, ldapUrl,
278 Context.SECURITY_AUTHENTICATION, "simple",
279 Context.SECURITY_PRINCIPAL, securityPrincipal,
280 Context.SECURITY_CREDENTIALS, Utils.transformPasswordToStars(password));
281
282 InitialLdapContext context = null;
283 boolean isAuthenticate;
284 try {
285 context = new InitialLdapContext(prop, null);
286 isAuthenticate = true;
287 } catch (NamingException e) {
288 LOGGER.error("LDAPAccessImpl getContext: Unable to identify user", e);
289 isAuthenticate = false;
290 } catch (Exception e) {
291 LOGGER.error("LDAPAccessImpl getContext: Unexpected exception", e);
292 isAuthenticate = false;
293 } finally {
294 try {
295 if (context != null) {
296 context.close();
297 }
298 } catch (NamingException e) {
299 LOGGER.error("LDAPAccessImpl getContext: Unable to close context", e);
300 }
301 }
302 LOGGER.info("LDAP authentication: {}", isAuthenticate);
303 return isAuthenticate;
304 }
305
306
307
308
309
310
311
312
313 public List<AuthSystemUser> getAllDOIProjectMembers(final InitialLdapContext context)
314 throws AuthenticationAccessException {
315 try {
316 LOGGER.traceEntry("Parameters : {}", context);
317 final String searchGroup = conf.get(LDAP_SEARCH_GROUP);
318 final String ldapProject = conf.get(LDAP_PROJECT);
319
320 final SearchControls constraints = new SearchControls();
321 constraints.setSearchScope(SearchControls.SUBTREE_SCOPE);
322 final String[] attrIDs = {"gidNumber",};
323 constraints.setReturningAttributes(attrIDs);
324
325 LOGGER.info("LDAP search({},{},{})", searchGroup, "cn=" + ldapProject, constraints);
326 final NamingEnumeration answer = context.search(searchGroup, "cn=" + ldapProject,
327 constraints);
328 LOGGER.info("LDAP search : OK");
329 final List<AuthSystemUser> members = new ArrayList<>();
330 if (answer.hasMore()) {
331 final NamingEnumeration<?> attrs = ((SearchResult) answer.next()).getAttributes().
332 get("gidNumber").getAll();
333 members.addAll(getLdapUsers(context, attrs.next().toString()));
334 }
335 return LOGGER.traceExit(members);
336 } catch (NamingException e) {
337 LOGGER.error(e);
338 throw new AuthenticationAccessException("", e);
339 }
340 }
341
342
343
344
345
346
347
348
349
350
351
352 private List<AuthSystemUser> getLdapUsers(final DirContext context, final String gidNumber)
353 throws NamingException {
354 LOGGER.traceEntry("Parameters : {}", context, gidNumber);
355 final List<AuthSystemUser> ldapuserList = new ArrayList<>();
356 final SearchControls controls = new SearchControls();
357 controls.setSearchScope(SearchControls.SUBTREE_SCOPE);
358 final String[] attrIDs = {
359 conf.get(LDAP_ATTR_USERNAME),
360 conf.get(LDAP_ATTR_MAIL),
361 conf.get(LDAP_ATTR_FULLNAME)
362 };
363 LOGGER.info("Getting attributes from LDAP: {}", (Object[]) attrIDs);
364 controls.setReturningAttributes(attrIDs);
365
366 final String ldapProject = conf.get(LDAP_PROJECT);
367 final String ldapSearchGroup = conf.get(LDAP_SEARCH_GROUP);
368 final String ldapSearchUser = conf.get(LDAP_SEARCH_USER);
369 final String ldapSearchAttr = String.format(
370 "(|(gidNumber=%s)(memberOf=cn=%s,%s))",
371 gidNumber, ldapProject, ldapSearchGroup
372 );
373
374 LOGGER.info("LDAP search({},{},{}", ldapSearchUser, ldapSearchAttr, controls);
375 final NamingEnumeration answer = context.search(ldapSearchUser, ldapSearchAttr, controls);
376 LOGGER.info("LDAP search : OK");
377 while (answer.hasMore()) {
378 final NamingEnumeration<? extends Attribute> attrbs = ((SearchResult) answer.next())
379 .getAttributes().getAll();
380 String fullname = null;
381 String uid = null;
382 String mail = null;
383 while (attrbs.hasMore()) {
384 final Attribute att = attrbs.next();
385 final String attId = att.getID();
386 if (attrIDs[0].equals(attId)) {
387 final NamingEnumeration<?> values = att.getAll();
388 while (values.hasMoreElements()) {
389 uid = values.next().toString();
390 break;
391 }
392 } else if (attrIDs[1].equals(attId)) {
393 final NamingEnumeration<?> values = att.getAll();
394 while (values.hasMoreElements()) {
395 mail = values.next().toString();
396 break;
397 }
398 } else if (attrIDs[2].equals(attId)) {
399 final NamingEnumeration<?> values = att.getAll();
400 while (values.hasMoreElements()) {
401 fullname = values.next().toString();
402 break;
403 }
404 }
405 }
406 if ((mail != null) && (uid != null)) {
407 final AuthSystemUser ldapuser = new AuthSystemUser();
408 ldapuser.setFullname(fullname);
409 ldapuser.setEmail(mail);
410 ldapuser.setUsername(uid);
411 ldapuserList.add(ldapuser);
412 LOGGER.debug("Create LDAP user : {}", ldapuser);
413 }
414 }
415 return LOGGER.traceExit(ldapuserList);
416 }
417
418
419
420
421 @Override
422 public String getName() {
423 return NAME;
424 }
425
426
427
428
429 @Override
430 public String getDescription() {
431 return DESCRIPTION;
432 }
433
434
435
436
437 @Override
438 public String getVersion() {
439 return VERSION;
440 }
441
442
443
444
445 @Override
446 public String getAuthor() {
447 return AUTHOR;
448 }
449
450
451
452
453 @Override
454 public String getOwner() {
455 return OWNER;
456 }
457
458
459
460
461 @Override
462 public String getLicense() {
463 return LICENSE;
464 }
465
466
467
468
469 @Override
470 public String getDOIAdmin() {
471 return conf.get(LDAP_DOI_ADMIN);
472 }
473
474
475
476
477 @Override
478 public StringBuilder validate() {
479 final StringBuilder validation = new StringBuilder();
480 final String message = "Sets ";
481 if (!this.conf.containsKey(LDAP_ATTR_FULLNAME)) {
482 validation.append(message).append(LDAP_ATTR_FULLNAME).append("\n");
483 }
484 if (!this.conf.containsKey(LDAP_ATTR_MAIL)) {
485 validation.append(message).append(LDAP_ATTR_MAIL).append("\n");
486 }
487 if (!this.conf.containsKey(LDAP_ATTR_USERNAME)) {
488 validation.append(message).append(LDAP_ATTR_USERNAME).append("\n");
489 }
490 if (!this.conf.containsKey(LDAP_PROJECT)) {
491 validation.append(message).append(LDAP_PROJECT).append("\n");
492 }
493 if (!this.conf.containsKey(LDAP_URL)) {
494 validation.append(message).append(LDAP_URL).append("\n");
495 }
496 if (!this.conf.containsKey(LDAP_SEARCH_GROUP)) {
497 validation.append(message).append(LDAP_SEARCH_GROUP).append("\n");
498 }
499 if (!this.conf.containsKey(LDAP_SEARCH_USER)) {
500 validation.append(message).append(LDAP_SEARCH_USER).append("\n");
501 }
502 if (!this.conf.containsKey(LDAP_USER)) {
503 validation.append(message).append(LDAP_USER).append("\n");
504 }
505 if (!this.conf.containsKey(LDAP_PWD)) {
506 validation.append(message).append(LDAP_PWD).append("\n");
507 }
508 return validation;
509 }
510
511
512
513
514
515
516
517 public static boolean isPassword(final String key) {
518 return LDAP_PWD.equals(key);
519 }
520
521
522
523
524 @Override
525 public void release() {
526 this.configured = false;
527 }
528
529
530
531
532 @Override
533 public boolean isConfigured() {
534 return this.configured;
535 }
536 }