001: /**
002: * $Id: LDAPABSSOAdapter.java,v 1.25 2005/09/21 11:10:17 dg154973 Exp $
003: * Copyright 2002 Sun Microsystems, Inc. All
004: * rights reserved. Use of this product is subject
005: * to license terms. Federal Acquisitions:
006: * Commercial Software -- Government Users
007: * Subject to Standard License Terms and
008: * Conditions.
009: *
010: * Sun, Sun Microsystems, the Sun logo, and iPlanet
011: * are trademarks or registered trademarks of Sun Microsystems,
012: * Inc. in the United States and other countries.
013: */package com.sun.ssoadapter.impl;
014:
015: import java.io.BufferedReader;
016: import java.io.InputStreamReader;
017: import java.io.IOException;
018: import java.net.MalformedURLException;
019: import java.net.HttpURLConnection;
020: import java.net.URL;
021: import java.util.Date;
022: import java.util.List;
023: import java.util.Locale;
024: import java.util.Enumeration;
025: import java.util.Properties;
026: import java.util.logging.Level;
027: import java.util.logging.Logger;
028: import java.util.logging.LogRecord;
029:
030: import javax.mail.MessagingException;
031: import javax.mail.AuthenticationFailedException;
032: import javax.mail.Store;
033: import javax.servlet.http.HttpServletRequest;
034: import com.sun.ssoadapter.SSOAdapter;
035: import com.sun.ssoadapter.AbstractSSOAdapter;
036: import com.sun.ssoadapter.SSOAdapterException;
037: import com.sun.addressbook.ABStore;
038: import com.sun.addressbook.ABStoreException;
039: import com.sun.addressbook.ABSession;
040: import com.sun.ssoadapter.SSOAdapterLogger;
041: import com.iplanet.am.sdk.AMException;
042: import com.iplanet.am.sdk.AMStoreConnection;
043: import com.iplanet.am.sdk.AMUser;
044: import com.iplanet.sso.SSOToken;
045: import com.iplanet.sso.SSOTokenEvent;
046: import com.iplanet.sso.SSOException;
047:
048: /**
049: * This class implements SSOAdapter and ApplicationHelper functionality
050: * specific to the Sun One Portal and iMS PAB service. <p>
051: *
052: * Specific features include:
053: * <ul>
054: * <li> Support for username/password style authentication.</li>
055: * <li> Ability to generate web application URLs for the following:
056: * <ul>
057: * <li> Messenger Express</li>
058: * <li> MAP JSP Addressbook application. The URL generated in this case
059: * will specify a configuration index via the query string parameter: "ai".
060: * </li>
061: * </ul>
062: * </li>
063: * </ul>
064: * <p>
065: *
066: * At the present time, username/password is stored in the clear. <p>
067: *
068: * This SSOAdapter implementation consumes the following Configuration
069: * properties:
070: * <ul>
071: * <li> <b>uid</b> : Required value. Username (uid) of imap user.
072: * <li> <b>password</b> : Required value. Password of imap user.
073: * <li> <b>host</b> : Required value. Name of host providing PAB service.
074: *
075: * <li> <b>port</b> : Optional value. Port number of PAB server. Defaults to
076: * "389"
077: * <li> <b>imapHost</b> :
078: * <li> <b>imapPort</b> :
079: * <li> <b>aid</b> :
080: * <li> <b>adminPassword</b> :
081: * <li> <b>userSearchBase</b> :
082: * <li> <b>pabSearchBase</b> :
083: * <li> <b>lang</b> :
084: * <li> <b>connPoolMin</b> :
085: * <li> <b>connPoolMax</b> :
086: * <li> <b>timeout</b> :
087: * <li> <b>clientProtocol</b> : Protocol to specify within URLs that activate
088: * web application functionality. Defaults to "http".
089: * <li> <b>clientPort</b> : Port to specify within URLs that that activate
090: * web application functionality. Defaults to "80".
091: * <li> <b>jspContextPath</b> : The "request context path" to use when
092: * forming a URL that activates MAP JSP application functionality. Defaults
093: * to request.getContextPath().
094: * <li> <b>jspLaunch</b> : The document path to use when forming a URL that
095: * activates MAP JSP application functionality.
096: * <li> <b></b> :
097: * </ul>
098: *
099: *
100: *@author Administrator
101: *@created April 28, 2004
102: *@version 1.0
103: *@see com.sun.ssoadapter.SSOAdapter
104: *@see com.sun.ssoadapter.SSOAdapterFactory
105: */
106:
107: public class LDAPABSSOAdapter extends AbstractSSOAdapter {
108:
109: protected ABStore abStore = null;
110: protected ABSession abSession = null;
111:
112: protected long lastCheckTime = 0;
113:
114: protected static String LDAP_DEFAULT_PORT = "389";
115: protected static String IMAP_DEFAULT_PORT = "143";
116: protected static String CLIENT_DEFAULT_PORT = "80";
117: protected static String CLIENT_DEFAULT_PROTOCOL = "http";
118: protected static String serviceClass = "com.sun.addressbook.ldap.LdapABStore";
119:
120: /* Custom Provider for admin proxy authentication */
121: public String psimap = "psimap";
122:
123: private static Logger logger = SSOAdapterLogger
124: .getLogger("com.sun.portal.ssoadapter.impl");
125:
126: /**
127: * Initialize and validate
128: *
129: *@param adapterName Used to identify the SSOAdapter
130: *@param token Used to identify the user on who's behalf the
131: * request is being processed.
132: *@param adapterProperties Contains the adapter information that will drive
133: * the operation of this instance of an SSOAdapter.
134: */
135: public void init(String adapterName, SSOToken token,
136: Properties adapterProperties, List userPropertiesList,
137: List encodedProperteisList, Locale locale)
138: throws SSOAdapterException {
139:
140: super .init(adapterName, token, adapterProperties,
141: userPropertiesList, encodedProperteisList, locale);
142:
143: if (logger.isLoggable(Level.INFO)) {
144: Properties dp = new Properties();
145: dp.putAll(adapterProperties);
146: dp.remove(PROP_PASSWORD_NAME);
147: dp.remove("proxyAdminPassword");
148:
149: String[] param = new String[5];
150: param[0] = adapterName;
151: param[1] = (String) dp.toString();
152: param[2] = identifier;
153: param[3] = userPropertiesList.toString();
154: param[4] = encodedProperteisList.toString();
155:
156: logger.log(Level.INFO, "PSSA_CSSI0001", param);
157: }
158:
159: if (adapterProperties.getProperty("validate", "false").equals(
160: "true")) {
161: try {
162: validate();
163: } catch (ValidationException ve) {
164: throw new SSOAdapterException(ve.getLocalizedMessage(
165: "ssoadapter", locale), true);
166: }
167: }
168: }
169:
170: /** Validates configuration. */
171: public void validate() throws ValidationException {
172:
173: if (logger.isLoggable(Level.INFO)) {
174: logger.log(Level.INFO, "PSSA_CSSI0003", new String[] {
175: adapterName, identifier });
176: }
177:
178: String portString = adapterProperties
179: .getProperty(PROP_PORT_NAME);
180: if ((portString != null) && (portString.length() > 0)) {
181: try {
182: int testInt = Integer.parseInt(portString);
183: } catch (Exception e) {
184: ValidationException ve = new ValidationException();
185: ve.setErrorMessageID("invalidPort");
186: throw ve;
187: }
188: }
189:
190: String hostString = adapterProperties
191: .getProperty(PROP_HOST_NAME);
192: if (hostString == null) {
193: ValidationException ve = new ValidationException();
194: ve.setErrorMessageID("missingHost");
195: throw ve;
196: }
197:
198: String clientPortString = adapterProperties
199: .getProperty("clientPort");
200: if ((clientPortString != null)
201: && (clientPortString.length() > 0)) {
202: try {
203: int testInt = Integer.parseInt(clientPortString);
204: } catch (Exception e) {
205: ValidationException ve = new ValidationException();
206: ve.setErrorMessageID("invalidClientPort");
207: throw ve;
208: }
209: }
210:
211: String imapHostString = adapterProperties
212: .getProperty("imapHost");
213: if (imapHostString == null) {
214: ValidationException ve = new ValidationException();
215: ve.setErrorMessageID("missingImapHost");
216: throw ve;
217: }
218:
219: String imapPortString = adapterProperties
220: .getProperty("imapPort");
221: if ((imapPortString != null) && (imapPortString.length() > 0)) {
222: try {
223: int testInt = Integer.parseInt(imapPortString);
224: } catch (Exception e) {
225: ValidationException ve = new ValidationException();
226: ve.setErrorMessageID("invalidImapPort");
227: throw ve;
228: }
229: }
230:
231: String aidString = adapterProperties.getProperty("aid");
232: if (aidString == null) {
233: ValidationException ve = new ValidationException();
234: ve.setErrorMessageID("missingAid");
235: throw ve;
236: }
237:
238: String adminPasswordString = adapterProperties
239: .getProperty("adminPassword");
240: if (adminPasswordString == null) {
241: ValidationException ve = new ValidationException();
242: ve.setErrorMessageID("missingAdminPassword");
243: throw ve;
244: }
245:
246: String userSearchBaseString = adapterProperties
247: .getProperty("userSearchBase");
248: if (userSearchBaseString == null) {
249: ValidationException ve = new ValidationException();
250: ve.setErrorMessageID("missingUserSearchBase");
251: throw ve;
252: }
253:
254: String pabSearchBaseString = adapterProperties
255: .getProperty("pabSearchBase");
256: if (pabSearchBaseString == null) {
257: ValidationException ve = new ValidationException();
258: ve.setErrorMessageID("missingPabSearchBase");
259: throw ve;
260: }
261:
262: String connPoolMinString = adapterProperties
263: .getProperty("connPoolMin");
264: if (connPoolMinString != null) {
265: try {
266: int testInt = Integer.parseInt(connPoolMinString);
267: } catch (Exception e) {
268: ValidationException ve = new ValidationException();
269: ve.setErrorMessageID("invalidConnPoolMin");
270: throw ve;
271: }
272: }
273:
274: String connPoolMaxString = adapterProperties
275: .getProperty("connPoolMax");
276: if (connPoolMaxString != null) {
277: try {
278: int testInt = Integer.parseInt(connPoolMaxString);
279: } catch (Exception e) {
280: ValidationException ve = new ValidationException();
281: ve.setErrorMessageID("invalidConnPoolMax");
282: throw ve;
283: }
284: }
285:
286: String timeoutString = adapterProperties.getProperty("timeout");
287: if (timeoutString != null) {
288: try {
289: int testInt = Integer.parseInt(timeoutString);
290: } catch (Exception e) {
291: ValidationException ve = new ValidationException();
292: ve.setErrorMessageID("invalidTimeout");
293: throw ve;
294: }
295: }
296:
297: String enableProxyAuth = adapterProperties.getProperty(
298: "enableProxyAuth", "false");
299:
300: if (enableProxyAuth.equals("false")) {
301: String uidString = adapterProperties
302: .getProperty(PROP_UID_NAME);
303: if (uidString == null) {
304: ValidationException ve = new ValidationException();
305: ve.setErrorMessageID("missingUid");
306: throw ve;
307: }
308:
309: String passwordString = adapterProperties
310: .getProperty(PROP_PASSWORD_NAME);
311: if (passwordString == null) {
312: ValidationException ve = new ValidationException();
313: ve.setErrorMessageID("missingPassword");
314: throw ve;
315: }
316: }
317: }
318:
319: /**
320: * Adapter specific Connection.
321: *
322: *@return The connection value
323: */
324: public Object getConnection() {
325: Object obj = null;
326:
327: try {
328: obj = getABStore();
329: } catch (Exception e) {
330: if (logger.isLoggable(Level.INFO)) {
331: logger.log(Level.INFO, "PSSA_CSSI0050", new String[] {
332: adapterName, identifier });
333: logger.log(Level.INFO, "PSSA_CSSI0051", e);
334: }
335: return null;
336: }
337:
338: if (obj != null) {
339: if (logger.isLoggable(Level.INFO)) {
340: logger.log(Level.INFO, "PSSA_CSSI0052", new String[] {
341: adapterName, identifier });
342: }
343: }
344:
345: return obj;
346: }
347:
348: /**
349: * Returns a connected Ldap ABStore object.
350: *
351: *@return The aBStore value
352: */
353: public ABStore getABStore() throws Exception {
354: if (isAvailable()) {
355: return abStore;
356: }
357: if (authenticate() == false) {
358: throw new Exception(
359: "LDAPABSSOAdapter.getABStore(): User credentials invalid. Access to Address Book denied!");
360: }
361:
362: abSession = getABSession();
363: abStore = abSession.getABStore(serviceClass);
364: abStore.connect();
365: return abStore;
366: }
367:
368: /**
369: * Returns a JABAPI ABSession object.
370: *
371: *@return The aBSession value
372: */
373: public ABSession getABSession() throws Exception {
374: if (abSession != null) {
375: if (logger.isLoggable(Level.INFO)) {
376: logger.log(Level.INFO, "PSSA_CSSI0053", new String[] {
377: adapterName, identifier });
378: }
379: return abSession;
380: }
381:
382: Properties props = new Properties();
383:
384: // mandatory properties
385: String host = adapterProperties.getProperty(PROP_HOST_NAME);
386: String port = adapterProperties.getProperty(PROP_PORT_NAME,
387: LDAP_DEFAULT_PORT);
388: String aid = adapterProperties.getProperty("aid");
389: String adminPassword = adapterProperties
390: .getProperty("adminPassword");
391: String userSearchBase = adapterProperties
392: .getProperty("userSearchBase");
393: String pabSearchBase = adapterProperties
394: .getProperty("pabSearchBase");
395: String enableProxyAuth = adapterProperties.getProperty(
396: "enableProxyAuth", "false");
397: String domain = adapterProperties.getProperty("domain");
398: String uid = null;
399:
400: // RFE 5021537
401: // Check to see if the ugHost and ugPort settings are non null. If so use
402: // These settings for the remote DS containing users and groups instead of
403: // the Messaging Server DS.
404: String ugHost = adapterProperties.getProperty("ugHost");
405: String ugPort = adapterProperties.getProperty("ugPort");
406: if (ugHost != null && (ugHost.length() != 0)) {
407: host = ugHost;
408: if (ugPort != null)
409: port = ugPort;
410: }
411:
412: // check to see if admin proxyauth has been enabled and
413: // that we have a valid session (ssotoken)
414: //
415: if (enableProxyAuth.equals("true")) {
416: if (logger.isLoggable(Level.INFO)) {
417: logger.log(Level.INFO, "PSSA_CSSI0054", new String[] {
418: adapterName, identifier });
419: }
420:
421: SSOToken tok = getSSOToken();
422: AMStoreConnection amsc = null;
423: AMUser auser = null;
424: String usrAttr = null;
425:
426: if (tok != null) {
427: usrAttr = adapterProperties.getProperty(
428: "userAttribute", "uid");
429:
430: try {
431: amsc = new AMStoreConnection(tok);
432: auser = amsc.getUser(tok.getPrincipal().getName());
433: uid = auser.getStringAttribute(usrAttr);
434: } catch (SSOException ssoe) {
435: if (logger.isLoggable(Level.SEVERE)) {
436: LogRecord rec = new LogRecord(Level.INFO,
437: "PSSA_CSSI0004");
438: String[] param = { adapterName, usrAttr,
439: identifier };
440: rec.setParameters(param);
441: rec.setThrown(ssoe);
442: rec.setLoggerName(logger.getName());
443: logger.log(rec);
444: }
445: } catch (AMException ame) {
446: if (logger.isLoggable(Level.SEVERE)) {
447: LogRecord rec = new LogRecord(Level.INFO,
448: "PSSA_CSSI0004");
449: String[] param = { adapterName, usrAttr,
450: identifier };
451: rec.setParameters(param);
452: rec.setThrown(ame);
453: rec.setLoggerName(logger.getName());
454: logger.log(rec);
455: }
456: }
457: }
458: } else {
459: uid = adapterProperties.getProperty(PROP_UID_NAME);
460: }
461:
462: // optional properties
463: String lang = adapterProperties.getProperty("lang");
464: String connPoolMin = adapterProperties
465: .getProperty("connPoolMin");
466: String connPoolMax = adapterProperties
467: .getProperty("connPoolMax");
468: String connTimeout = adapterProperties.getProperty("timeout");
469:
470: if (host != null) {
471: props.put("ab.host", host);
472: }
473: if (port != null) {
474: props.put("ab.port", port);
475: }
476: if (aid != null) {
477: props.put("ab.ldap.authId", aid);
478: }
479: if (adminPassword != null) {
480: props.put("ab.ldap.authPw", adminPassword);
481: }
482: if (userSearchBase != null) {
483: props.put("ab.ldap.dirSearchBase", userSearchBase);
484: }
485: if (pabSearchBase != null) {
486: props.put("ab.ldap.pabSearchBase", pabSearchBase);
487: }
488: if (uid != null) {
489: props.put("ab.userName", uid);
490: }
491:
492: if (domain != null) {
493: props.put("ab.domain", domain);
494: }
495:
496: if (lang != null) {
497: props.put("ab.ldap.lang", lang);
498: }
499: if (connPoolMin != null) {
500: props.put("ab.ldap.connPoolMin", connPoolMin);
501: }
502: if (connPoolMax != null) {
503: props.put("ab.ldap.connPoolMax", connPoolMax);
504: }
505: if (connTimeout != null) {
506: props.put("ab.ldap.timeout", connTimeout);
507: }
508:
509: if (logger.isLoggable(Level.INFO)) {
510: String[] param = new String[3];
511: param[0] = adapterName;
512: param[1] = (String) props.toString();
513: param[2] = identifier;
514: logger.log(Level.INFO, "PSSA_CSSI0055", param);
515: }
516:
517: abSession = abSession.getInstance(props);
518:
519: return abSession;
520: }
521:
522: /**
523: * Authenticate the user to the given imap mail before letting the user
524: * access the address book.
525: *
526: *@return boolean Indicates if the store is valid or not.
527: */
528: protected boolean authenticate() {
529:
530: // check for admin proxy authentication. if it is enabled, do not
531: // authenticate against the messaging server, because the admin
532: // credentials will be used for authentication instead of the user's.
533: //
534: String enableProxyAuth = adapterProperties.getProperty(
535: "enableProxyAuth", "false");
536: if (logger.isLoggable(Level.INFO)) {
537: logger.log(Level.INFO, "PSSA_CSSI0059", new String[] {
538: adapterName, identifier });
539: }
540: if (enableProxyAuth.equalsIgnoreCase("true")) {
541: return true;
542: }
543:
544: Properties mailProps = new Properties();
545: // Get MS server name and port
546: String imapServer = adapterProperties.getProperty("imapHost");
547: String imapPortStr = adapterProperties.getProperty("imapPort");
548: if (imapPortStr == null) {
549: imapPortStr = IMAP_DEFAULT_PORT;
550: }
551: int imapPort = Integer.parseInt(imapPortStr);
552: //Get the user information and set the domain.
553: String user = null;
554: String pass = adapterProperties.getProperty(PROP_PASSWORD_NAME);
555: String domain = adapterProperties.getProperty(PROP_UID_DOMAIN);
556:
557: if (domain == null || domain.equals("")) {
558: user = adapterProperties.getProperty(PROP_UID_NAME);
559: } else {
560: user = adapterProperties.getProperty(PROP_UID_NAME) + "@"
561: + adapterProperties.getProperty(PROP_UID_DOMAIN);
562: }
563:
564: mailProps.put("mail.host", imapServer);
565:
566: if (user == null) {
567: if (logger.isLoggable(Level.WARNING)) {
568: logger.log(Level.WARNING, "PSSA_CSSI0019",
569: new String[] { adapterName, identifier });
570: }
571: return false;
572: } else {
573: if (user.length() > 0) {
574: mailProps.put("mail.user", user);
575: }
576: }
577:
578: if (logger.isLoggable(Level.INFO)) {
579: String[] param = new String[3];
580: param[0] = adapterName;
581: param[1] = (String) mailProps.toString();
582: param[2] = identifier;
583: logger.log(Level.INFO, "PSSA_CSSI0061", param);
584: }
585:
586: try {
587:
588: // Check to see if domain is set
589: javax.mail.Session session = javax.mail.Session
590: .getInstance(mailProps, null);
591: Store newStore = session.getStore("imap");
592: newStore.connect(imapServer, imapPort, user, pass);
593: // This store is not going to be used in AB .. so close it ..
594: newStore.close();
595: } catch (AuthenticationFailedException afe) {
596: if (logger.isLoggable(Level.SEVERE))
597: logger.log(Level.SEVERE, "PSSA_CSSI0062", afe);
598: return false;
599: } catch (MessagingException me) {
600: if (logger.isLoggable(Level.SEVERE))
601: logger.log(Level.SEVERE, "PSSA_CSSI0062", me);
602: return false;
603: }
604:
605: return true;
606: }
607:
608: /**
609: * Tests service availability.
610: *
611: *@return The available value
612: */
613: public boolean isAvailable() {
614: try {
615: if (abStore != null && abStore.isConnected()) {
616: if (logger.isLoggable(Level.INFO)) {
617: logger.log(Level.INFO, "PSSA_CSSI0057",
618: new String[] { adapterName, identifier });
619: }
620: return true;
621: } else {
622: return false;
623: }
624: } catch (ABStoreException abse) {
625: if (logger.isLoggable(Level.SEVERE)) {
626: logger.log(Level.SEVERE, "PSSA_CSSI0058", abse);
627: }
628: return false;
629: }
630: }
631:
632: /**
633: * Adapter specific Connection termination.
634: *
635: *@return true if the connection was terminated successfully.
636: */
637: public boolean closeConnection() {
638: boolean retval = true;
639:
640: try {
641: abStore.disconnect();
642: abStore = null;
643: abSession = null;
644: } catch (Exception e) {
645: retval = false;
646: }
647:
648: if (logger.isLoggable(Level.INFO)) {
649: logger.log(Level.INFO, "PSSA_CSSI0005", new String[] {
650: adapterName, identifier });
651: }
652:
653: return retval;
654: }
655:
656: /**
657: * Implements SSOTokenListener "ssoTokenChanged" method. The following are
658: * possible SSO token event types:
659: * <ul>
660: * <li> SSO_TOKEN_IDLE_TIMEOUT
661: * <li> SSO_TOKEN_MAX_TIMEOUT
662: * <li> SSO_TOKEN_DESTROY
663: * </ul>
664: * The event getType() method is used to ensure that one of the three types
665: * above are the basis for this event. If getType() returns a type not listed
666: * above, then an SSOException is thrown.
667: *
668: *@param evt SSOTokenEvent
669: */
670: public void ssoTokenChanged(SSOTokenEvent evt) {
671:
672: try {
673: int evtType = evt.getType();
674:
675: if (abStore != null) {
676: abStore.disconnect();
677: }
678:
679: abStore = null;
680: abSession = null;
681: } catch (Exception e) {
682: if (logger.isLoggable(Level.WARNING)) {
683: logger.log(Level.WARNING, "PSSA_CSSI0006", e);
684: }
685: return;
686: }
687:
688: if (logger.isLoggable(Level.INFO)) {
689: logger.log(Level.INFO, "PSSA_CSSI0002", new String[] {
690: adapterName, identifier });
691: }
692: }
693:
694: }
|