001: /**
002: * Copyright 2002 Sun Microsystems, Inc. All
003: * rights reserved. Use of this product is subject
004: * to license terms. Federal Acquisitions:
005: * Commercial Software -- Government Users
006: * Subject to Standard License Terms and
007: * Conditions.
008: *
009: * Sun, Sun Microsystems, the Sun logo, and iPlanet
010: * are trademarks or registered trademarks of Sun Microsystems,
011: * Inc. in the United States and other countries.
012: */package com.sun.ssoadapter.impl;
013:
014: import com.iplanet.am.sdk.AMException;
015: import com.iplanet.am.sdk.AMStoreConnection;
016: import com.iplanet.am.sdk.AMUser;
017: import com.iplanet.sso.SSOToken;
018: import com.iplanet.sso.SSOTokenEvent;
019: import com.iplanet.sso.SSOException;
020: import com.sun.ssoadapter.SSOAdapter;
021: import com.sun.ssoadapter.AbstractSSOAdapter;
022: import com.sun.ssoadapter.SSOAdapterException;
023: import com.sun.ssoadapter.SSOAdapterLogger;
024:
025: import javax.mail.*;
026: import javax.mail.internet.*;
027: import java.util.*;
028: import java.util.logging.Level;
029: import java.util.logging.Logger;
030: import java.util.logging.LogRecord;
031: import java.io.*;
032: import java.security.Provider;
033:
034: /**
035: * This class extends the abstract SSOAdapter functionality
036: * specific to the Sun One Portal and Mail services.
037: * <p>Specific features include:
038: * <ul>
039: * <li>Support for username/password style authentication.</li>
040: * </ul>
041: * <p>This SSOAdapter implementation consumes the following
042: * Configuration properties:
043: * <ul>
044: * <li><b>protocol</b>: Required value. Must be either "imap" or "pop3".
045: * <li><b>uid</b>: Required value. Username (uid) of imap user.
046: * <li><b>password</b>: Required value. Password of imap user.
047: * <li><b>host</b>: Required value. Name of host providing IMAP service.
048: * <li><b>port</b>: Optional value. Port number of IMAP server. Defaults to "143"
049: * when protocol is "imap", and "110" when protocol is "pop3".
050: * <li><b>smtpServer</b>: Optional value. Specifies name of outgoing mail server.
051: * Defaults to value of host property.
052: * <li><b>clientProtocol</b>: Protocol to specify within URLs that activate
053: * web application functionality. Defaults to "http".
054: * <li><b>clientPort</b>: Port to specify within URLs that that activate web
055: * application functionality. Defaults to "80".
056: * </ul>
057: *
058: * @author John Saare
059: * @version 1.0
060: * @see com.sun.ssoadapter.SSOAdapter
061: * @see com.sun.ssoadapter.SSOAdapterFactory
062: *
063: */
064:
065: public class JavaMailSSOAdapter extends AbstractSSOAdapter {
066:
067: //
068: // Mail Store
069: //
070: protected javax.mail.Store mailStore = null;
071:
072: //
073: // Mail Session
074: //
075: protected javax.mail.Session mailSession = null;
076:
077: //
078: // Standard Javamail provided IMAP provider that allows
079: // proxy auth in Javamail 1.3.2 (introduced for PS 6.3.1)
080: //
081: public static final String imap = "imap";
082:
083: //As the javamail 1.3.2 supports imaps
084: public static final String imaps = "imaps";
085:
086: //
087: // Custom secured propvider for admin proxy authentication
088: //
089: public static final String pop3 = "pop3";
090:
091: // protocol added in javamail 1.3.2
092: public static final String pop3s = "pop3s";
093:
094: //
095: // IMAP protocol to be used (secured or unsecured)
096: //
097: public String imapProtocol = "";
098:
099: //
100: // Default SSL Factory
101: //
102: private final String SSL_FACTORY = "javax.net.ssl.SSLSocketFactory";
103:
104: private static Logger logger = SSOAdapterLogger
105: .getLogger("com.sun.portal.ssoadapter.impl");
106:
107: /**
108: * Initialize and validate JavaMailSSOAdapter
109: *
110: * @param adapterName Used to identify the SSOAdapter
111: * @param token Used to identify the user on who's behalf the request is
112: * being processed.
113: * @param adapterProperties Contains the adapter information that will drive
114: * the operation of this instance of an SSOAdapter.
115: */
116: public void init(String adapterName, SSOToken token,
117: Properties adapterProperties, List userPropertiesList,
118: List encodedProperteisList, Locale locale)
119: throws SSOAdapterException {
120:
121: super .init(adapterName, token, adapterProperties,
122: userPropertiesList, encodedProperteisList, locale);
123:
124: if (logger.isLoggable(Level.INFO)) {
125: Properties dp = new Properties();
126: dp.putAll(adapterProperties);
127: dp.remove(PROP_PASSWORD_NAME);
128: dp.remove("proxyAdminPassword");
129:
130: String[] param = new String[5];
131: param[0] = adapterName;
132: param[1] = (String) dp.toString();
133: param[2] = identifier;
134: param[3] = userPropertiesList.toString();
135: param[4] = encodedProperteisList.toString();
136:
137: logger.log(Level.INFO, "PSSA_CSSI0001", param);
138: }
139:
140: if (adapterProperties.getProperty("validate", "false").equals(
141: "true")) {
142: try {
143: validate();
144: } catch (ValidationException ve) {
145: throw new SSOAdapterException(ve.getLocalizedMessage(
146: "ssoadapter", locale), true);
147: }
148: }
149:
150: // Set IMAP protocol (secure or unsecure)
151: String protocolString = adapterProperties
152: .getProperty(PROP_PROTOCOL_NAME);
153: if (protocolString.equalsIgnoreCase("imaps")) {
154: imapProtocol = imaps;
155: } else if (protocolString.equalsIgnoreCase("imap")) {
156: imapProtocol = imap;
157: } else if (protocolString.equalsIgnoreCase("pop3s")) {
158: imapProtocol = pop3s;
159: } else {
160: imapProtocol = pop3;
161: }
162:
163: if (logger.isLoggable(Level.INFO)) {
164: String[] param = new String[3];
165: param[0] = adapterName;
166: param[1] = imapProtocol;
167: param[2] = identifier;
168: logger.log(Level.INFO, "PSSA_CSSI0030", param);
169: }
170: }
171:
172: /**
173: * Validates configuration.
174: */
175: public void validate() throws ValidationException {
176:
177: if (logger.isLoggable(Level.INFO)) {
178: logger.log(Level.INFO, "PSSA_CSSI0003", new String[] {
179: adapterName, identifier });
180: }
181:
182: String portString = adapterProperties
183: .getProperty(PROP_PORT_NAME);
184: if ((portString != null) && (portString.length() > 0)) {
185: try {
186: int portInt = Integer.parseInt(portString);
187: } catch (Exception e) {
188: ValidationException ve = new ValidationException();
189: ve.setErrorMessageID("invalidPort");
190: throw ve;
191: }
192: }
193:
194: String hostString = adapterProperties
195: .getProperty(PROP_HOST_NAME);
196: if (hostString == null) {
197: ValidationException ve = new ValidationException();
198: ve.setErrorMessageID("missingHost");
199: throw ve;
200: }
201:
202: String clientPortString = adapterProperties
203: .getProperty(PROP_CLIENT_PORT);
204: if ((clientPortString != null)
205: && (clientPortString.length() > 0)) {
206: try {
207: int portInt = Integer.parseInt(clientPortString);
208: } catch (Exception e) {
209: ValidationException ve = new ValidationException();
210: ve.setErrorMessageID("invalidClientPort");
211: throw ve;
212: }
213: }
214:
215: String enableProxyAuth = adapterProperties.getProperty(
216: "enableProxyAuth", "false");
217: if (enableProxyAuth.equals("false")) {
218: String uidString = adapterProperties
219: .getProperty(PROP_UID_NAME);
220: if (uidString == null) {
221: ValidationException ve = new ValidationException();
222: ve.setErrorMessageID("missingUid");
223: throw ve;
224: }
225:
226: String passwordString = adapterProperties
227: .getProperty(PROP_PASSWORD_NAME);
228: if (passwordString == null) {
229: ValidationException ve = new ValidationException();
230: ve.setErrorMessageID("missingPassword");
231: throw ve;
232: }
233: }
234:
235: }
236:
237: /**
238: * Adapter specific Connection.
239: */
240: public Object getConnection() {
241: Object obj = null;
242:
243: try {
244: obj = getStore();
245: } catch (Exception e) {
246: if (logger.isLoggable(Level.INFO)) {
247: logger.log(Level.INFO, "PSSA_CSSI0031", new String[] {
248: adapterName, identifier });
249: logger.log(Level.INFO, "PSSA_CSSI0032", e);
250: }
251: return null;
252: }
253:
254: if (obj != null) {
255: if (logger.isLoggable(Level.INFO)) {
256: logger.log(Level.INFO, "PSSA_CSSI0033", new String[] {
257: adapterName, identifier });
258: }
259: }
260:
261: return obj;
262: }
263:
264: /**
265: * Tests service availability.
266: */
267: public boolean isAvailable() {
268: if (mailStore != null && mailStore.isConnected()) {
269: if (logger.isLoggable(Level.INFO)) {
270: logger.log(Level.INFO, "PSSA_CSSI0034", new String[] {
271: adapterName, identifier });
272: }
273: return true;
274: } else {
275: return false;
276: }
277: }
278:
279: /**
280: * Returns a JavaMail Store object.
281: */
282: public javax.mail.Store getStore() throws Exception {
283: if (isAvailable()) {
284: return mailStore;
285: }
286:
287: Session mailSession = getSession();
288:
289: if (mailSession == null) {
290: return null;
291: }
292:
293: String enableProxyAuth = adapterProperties.getProperty(
294: "enableProxyAuth", "false");
295: String uid = null;
296: String password = null;
297: String portString = null;
298:
299: // Set default port
300: if (imapProtocol.equalsIgnoreCase(imaps)) {
301: portString = adapterProperties.getProperty(PROP_PORT_NAME,
302: "993");
303: } else {
304: portString = adapterProperties.getProperty(PROP_PORT_NAME,
305: imapProtocol.equals("pop3") ? "110" : "143");
306: }
307:
308: if (logger.isLoggable(Level.INFO)) {
309: logger.log(Level.INFO, "PSSA_CSSI0039", new String[] {
310: adapterName, portString, identifier });
311: }
312:
313: // check to see if admin proxyauth has been enabled and
314: // that we have a valid session (ssotoken)
315: //
316: // remove when javamail supports proxyauth
317: //
318: String protocolString = imapProtocol;
319: // end
320: if (enableProxyAuth.equals("true")) {
321: SSOToken tok = getSSOToken();
322:
323: if (tok != null) {
324: uid = adapterProperties.getProperty("proxyAdminUid");
325: password = adapterProperties
326: .getProperty("proxyAdminPassword");
327: }
328:
329: if (logger.isLoggable(Level.INFO)) {
330: logger.log(Level.INFO, "PSSA_CSSI0040", new String[] {
331: adapterName, uid, identifier });
332: }
333:
334: } else {
335: uid = adapterProperties.getProperty(PROP_UID_NAME);
336: password = adapterProperties
337: .getProperty(PROP_PASSWORD_NAME);
338: String domain = adapterProperties
339: .getProperty(PROP_UID_DOMAIN);
340: if ((domain != null) && (!domain.equals(""))) {
341: uid = uid + "@" + domain;
342: }
343:
344: if (logger.isLoggable(Level.INFO)) {
345: logger.log(Level.INFO, "PSSA_CSSI0041", new String[] {
346: adapterName, uid, identifier });
347: }
348: }
349:
350: mailStore = mailSession.getStore(protocolString);
351: mailStore.connect(
352: adapterProperties.getProperty(PROP_HOST_NAME), Integer
353: .parseInt(portString), uid, password);
354:
355: return mailStore;
356: }
357:
358: /**
359: * Returns a JavaMail Session object.
360: */
361: public javax.mail.Session getSession() throws Exception {
362: if (mailSession != null) {
363: if (logger.isLoggable(Level.INFO)) {
364: logger.log(Level.INFO, "PSSA_CSSI0035", new String[] {
365: adapterName, identifier });
366: }
367: return mailSession;
368: }
369:
370: // check for mandatory properties, if they are null then there's
371: // no point in trying to establish a connection
372: //
373: String host = adapterProperties.getProperty(PROP_HOST_NAME);
374:
375: if (host == null) {
376: if (logger.isLoggable(Level.INFO)) {
377: logger.log(Level.INFO, "PSSA_CSSI0036", new String[] {
378: adapterName, identifier });
379: }
380: return null;
381: }
382:
383: String smtpPort = adapterProperties.getProperty("smtpPort");
384: String enableProxyAuth = adapterProperties.getProperty(
385: "enableProxyAuth", "false");
386: Properties props = new Properties();
387:
388: props.put("mail.debug", "false");
389: props.put("mail.store.pkgs", "com.sun.mail");
390: props.put("mail.transport.protocol", "smtp");
391: props.put("mail.imap.fetchsize", "16384");
392: props.put("mail.smtp.host", adapterProperties.getProperty(
393: "smtpServer", host));
394:
395: if (smtpPort != null) {
396: props.put("mail.smtp.port", smtpPort);
397: }
398:
399: //Set timeout for imap and pop3 connections
400: //mail.imap.connectiontimeout = Socket connection timeout value in milliseconds
401: //mail.imap.timeout = Socket I/O timeout value in milliseconds
402: //mail.pop3.connectiontimeout = Socket connection timeout value in milliseconds
403: //mail.pop3.timeout = Socket I/O timeout value in milliseconds
404: String p = null;
405: if ((imapProtocol.equals(pop3)) || (imapProtocol.equals(pop3s))) {
406: p = pop3;
407: } else {
408: p = imap;
409: }
410: String timeoutString = adapterProperties.getProperty("timeout");
411: if (timeoutString != null) {
412: try {
413: int timeoutInt = Integer.parseInt(timeoutString);
414: props.put("mail." + p + ".timeout", timeoutString);
415: } catch (Exception e) {
416: logger.log(Level.WARNING, "PSSA_CSSI0022", e);
417: }
418: }
419:
420: timeoutString = adapterProperties
421: .getProperty("connectiontimeout");
422: if (timeoutString != null) {
423: try {
424: int timeoutInt = Integer.parseInt(timeoutString);
425: props.put("mail." + p + ".connectiontimeout",
426: timeoutString);
427: } catch (Exception e) {
428: logger.log(Level.WARNING, "PSSA_CSSI0023", e);
429: }
430: }
431:
432: // check to see if admin proxyauth has been enabled and
433: // that we have a valid session (ssotoken)
434: //
435: if (enableProxyAuth.equals("true")) {
436: if (logger.isLoggable(Level.INFO)) {
437: logger.log(Level.INFO, "PSSA_CSSI0037", new String[] {
438: adapterName, identifier });
439: }
440: SSOToken tok = getSSOToken();
441: AMStoreConnection amsc = null;
442: AMUser auser = null;
443: String usrAttr = null;
444: String puid = null;
445:
446: if (tok != null) {
447: usrAttr = adapterProperties.getProperty(
448: "userAttribute", "uid");
449:
450: try {
451: amsc = new AMStoreConnection(tok);
452: auser = amsc.getUser(tok.getPrincipal().getName());
453: puid = auser.getStringAttribute(usrAttr);
454: } catch (SSOException ssoe) {
455: if (logger.isLoggable(Level.SEVERE)) {
456: LogRecord rec = new LogRecord(Level.INFO,
457: "PSSA_CSSI0004");
458: String[] param = { adapterName, usrAttr,
459: identifier };
460: rec.setParameters(param);
461: rec.setThrown(ssoe);
462: rec.setLoggerName(logger.getName());
463: logger.log(rec);
464: }
465: } catch (AMException ame) {
466: if (logger.isLoggable(Level.SEVERE)) {
467: LogRecord rec = new LogRecord(Level.INFO,
468: "PSSA_CSSI0004");
469: String[] param = { adapterName, usrAttr,
470: identifier };
471: rec.setParameters(param);
472: rec.setThrown(ame);
473: rec.setLoggerName(logger.getName());
474: logger.log(rec);
475: }
476: }
477:
478: if ((puid != null) && (puid.length() > 0)) {
479: // use
480: //
481: //props.put("mail.imap.proxyauth.user", puid);
482: //
483: // when javamail supports proxyauth
484: //
485: String domain = adapterProperties
486: .getProperty(PROP_UID_DOMAIN);
487: if ((domain != null) && (domain.length() > 0)) {
488: puid = puid + "@" + domain;
489: }
490: props.put("mail." + imapProtocol
491: + ".proxyauth.user", puid);
492: // end
493: }
494: }
495: }
496:
497: if (logger.isLoggable(Level.INFO)) {
498: String[] param = new String[3];
499: param[0] = adapterName;
500: param[1] = (String) props.toString();
501: param[2] = identifier;
502: logger.log(Level.INFO, "PSSA_CSSI0038", param);
503: }
504:
505: mailSession = javax.mail.Session.getInstance(props, null);
506:
507: return mailSession;
508: }
509:
510: /**
511: * Adapter specific Connection termination.
512: *
513: * @return true if the connection was terminated successfully.
514: */
515: public boolean closeConnection() {
516: boolean retval = true;
517:
518: try {
519: mailStore.close();
520: mailStore = null;
521: mailSession = null;
522: } catch (Exception e) {
523: retval = false;
524: }
525:
526: if (logger.isLoggable(Level.INFO)) {
527: logger.log(Level.INFO, "PSSA_CSSI0005", new String[] {
528: adapterName, identifier });
529: }
530:
531: return retval;
532: }
533:
534: /**
535: * Implements SSOTokenListener "ssoTokenChanged" method.
536: *
537: * The following are possible SSO token event types:
538: * <ul>
539: * <li>SSO_TOKEN_IDLE_TIMEOUT
540: * <li>SSO_TOKEN_MAX_TIMEOUT
541: * <li>SSO_TOKEN_DESTROY
542: * </ul>
543: *
544: * The event getType() method is used to ensure that one of the
545: * three types above are the basis for this event. If getType()
546: * returns a type not listed above, then an SSOException is thrown.
547: *
548: * @param evt SSOTokenEvent
549: */
550: public void ssoTokenChanged(SSOTokenEvent evt) {
551:
552: try {
553: int evtType = evt.getType();
554:
555: if (mailStore != null) {
556: mailStore.close();
557: }
558:
559: mailStore = null;
560: mailSession = null;
561: } catch (Exception e) {
562: if (logger.isLoggable(Level.WARNING))
563: logger.log(Level.WARNING, "PSSA_CSSI0005", e);
564: return;
565: }
566:
567: if (logger.isLoggable(Level.INFO)) {
568: logger.log(Level.INFO, "PSSA_CSSI0002", new String[] {
569: adapterName, identifier });
570: }
571: }
572:
573: }
|