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.sso.SSOToken;
015: import com.iplanet.sso.SSOTokenEvent;
016: import com.iplanet.sso.SSOException;
017: import com.sun.ssoadapter.SSOAdapter;
018: import com.sun.ssoadapter.AbstractSSOAdapter;
019: import com.sun.ssoadapter.SSOAdapterException;
020: import com.sun.comclient.calendar.*;
021: import com.sun.ssoadapter.SSOAdapterLogger;
022:
023: import java.util.Properties;
024: import java.net.HttpURLConnection;
025: import java.net.URL;
026: import java.io.*;
027: import java.util.*;
028: import java.util.logging.Level;
029: import java.util.logging.Logger;
030:
031: import javax.servlet.http.*;
032:
033: /**
034: * This class implements CalendarSSOAdapter and CalendarApplicationAdapter functionality
035: * specific to the Sun One Portal and Calendar services.
036: * <p>Specific features include:
037: * <ul>
038: * <li>Support for username/password style authentication.</li>
039: * <li>Ability to generate web application URLs for the following:
040: * <ul>
041: * <li>Calendar Express</li>
042: * <li>MAP JSP Calendar application. The URL generated in this
043: * case will specify a configuration index via the
044: * query string parameter: "ci". </li>
045: * </ul>
046: * </li>
047: * </ul>
048: * <p>At the present time, username/password is stored in the clear.
049: * <p>This CalendarSSOAdapter implementation consumes the following
050: * Configuration properties:
051: * <ul>
052: * <li><b>protocol</b>: Required value. Specifies the protocol
053: * JCAPI will use to connect to the calendar server.
054: * For the time being, this should always be "http".
055: * <li><b>uid</b>: Required value. Username (uid) of calendar user.
056: * <li><b>password</b>: Required value. Password of calendar user.
057: * <li><b>host</b>: Required value. Name of host providing IMAP service.
058: * <li><b>winDomain</b>: Required value. NT domain to which user belongs.
059: * <li><b>port</b>: Optional value. Port number of calendar server.
060: * <li><b>clientProtocol</b>: Protocol to specify within URLs that activate
061: * web application functionality. Defaults to "http".
062: * <li><b>clientPort</b>: Port to specify within URLs that that activate web
063: * application functionality. Defaults to "80".
064: * <li><b>jspContextPath</b>: The "request context path" to use when forming a URL
065: * that activates MAP JSP application functionality.
066: * Defaults to request.getContextPath().
067: * <li><b>jspLaunch</b>: The document path to use when forming a URL
068: * that activates MAP JSP application functionality.
069: * Defaults to "/jsp/default/launchCal.jsp".
070: * <li><b>exchangeContextPath</b>: The "request context path" to use when forming a URL
071: * that activates Exchange OWA functionality.
072: * Defaults to "/exchange".
073: * <li><b>exchangeOperation</b>: The operation to be invoked within Exchange OWA.
074: * Defaults to null, but should be set to "calendar"
075: * for Exchange 2000 installations.
076: * </ul>
077: *
078: * @version 1.0
079: * @see com.sun.ssoadapter.SSOAdapter
080: * @see com.sun.ssoadapter.SSOAdapterFactory
081: *
082: */
083:
084: public class ExchangeCalendarSSOAdapter extends AbstractSSOAdapter {
085:
086: protected CalendarSession calSession;
087: protected CalendarStore calStore;
088:
089: protected static String CALENDAR_DEFAULT_PROTOCOL = "http";
090: protected static String serviceClass = "com.sun.ssoadapter.calendar.pim.APimCalStore";
091:
092: private static Logger logger = SSOAdapterLogger
093: .getLogger("com.sun.portal.ssoadapter.impl");
094:
095: /**
096: * Initialize and validate
097: *
098: * @param adapterName Used to identify the SSOAdapter
099: * @param token Used to identify the user on who's behalf the request is
100: * being processed.
101: * @param adapterProperties Contains the adapter information that will drive
102: * the operation of this instance of an SSOAdapter.
103: */
104: public void init(String adapterName, SSOToken token,
105: Properties adapterProperties, List userPropertiesList,
106: List encodedProperteisList, Locale locale)
107: throws SSOAdapterException {
108:
109: super .init(adapterName, token, adapterProperties,
110: userPropertiesList, encodedProperteisList, locale);
111:
112: if (logger.isLoggable(Level.INFO)) {
113: Properties dp = new Properties();
114: dp.putAll(adapterProperties);
115: dp.remove(PROP_PASSWORD_NAME);
116:
117: String[] param = new String[5];
118: param[0] = adapterName;
119: param[1] = (String) dp.toString();
120: param[2] = identifier;
121: param[3] = userPropertiesList.toString();
122: param[4] = encodedProperteisList.toString();
123:
124: logger.log(Level.INFO, "PSSA_CSSI0001", param);
125: }
126:
127: if (adapterProperties.getProperty("validate", "false").equals(
128: "true")) {
129: try {
130: validate();
131: } catch (ValidationException ve) {
132: throw new SSOAdapterException(ve.getLocalizedMessage(
133: "ssoadapter", locale), true);
134: }
135: }
136:
137: }
138:
139: /**
140: * Validates configuration.
141: */
142: public void validate() throws ValidationException {
143:
144: if (logger.isLoggable(Level.INFO)) {
145: logger.log(Level.INFO, "PSSA_CSSI0003", new String[] {
146: adapterName, identifier });
147: }
148:
149: String portString = adapterProperties
150: .getProperty(PROP_PORT_NAME);
151: if ((portString != null) && (portString.length() > 0)) {
152: try {
153: int portInt = Integer.parseInt(portString);
154: } catch (Exception e) {
155: ValidationException ve = new ValidationException();
156: ve.setErrorMessageID("invalidPort");
157: throw ve;
158: }
159: }
160:
161: String hostString = adapterProperties
162: .getProperty(PROP_HOST_NAME);
163: if (hostString == null) {
164: ValidationException ve = new ValidationException();
165: ve.setErrorMessageID("missingHost");
166: throw ve;
167: }
168:
169: String uidString = adapterProperties.getProperty(PROP_UID_NAME);
170: if (uidString == null) {
171: ValidationException ve = new ValidationException();
172: ve.setErrorMessageID("missingUid");
173: throw ve;
174: }
175:
176: String passwordString = adapterProperties
177: .getProperty(PROP_PASSWORD_NAME);
178: if (passwordString == null) {
179: ValidationException ve = new ValidationException();
180: ve.setErrorMessageID("missingPassword");
181: throw ve;
182: }
183:
184: String winDomainString = adapterProperties
185: .getProperty("winDomain");
186: if (winDomainString == null) {
187: ValidationException ve = new ValidationException();
188: ve.setErrorMessageID("missingWinDomain");
189: throw ve;
190: }
191:
192: String clientPortString = adapterProperties
193: .getProperty("clientPort");
194: if ((clientPortString != null)
195: && (clientPortString.length() > 0)) {
196: try {
197: int portInt = Integer.parseInt(clientPortString);
198: } catch (Exception e) {
199: ValidationException ve = new ValidationException();
200: ve.setErrorMessageID("invalidClientPort");
201: throw ve;
202: }
203: }
204:
205: }
206:
207: /**
208: * Tests service availability.
209: */
210: public boolean isAvailable() {
211: if (calStore != null && calStore.isConnected()) {
212: if (logger.isLoggable(Level.INFO)) {
213: logger.log(Level.INFO, "PSSA_CSSI0014", new String[] {
214: adapterName, identifier });
215: }
216: return true;
217: } else {
218: return false;
219: }
220: }
221:
222: /**
223: * Adapter specific Connection.
224: */
225: public Object getConnection() {
226: Object obj = null;
227:
228: try {
229: obj = getCalStore();
230: } catch (Exception e) {
231: if (logger.isLoggable(Level.INFO)) {
232: logger.log(Level.INFO, "PSSA_CSSI0010", new String[] {
233: adapterName, identifier });
234: logger.log(Level.INFO, "PSSA_CSSI0011", e);
235: }
236: return null;
237: }
238:
239: if (obj != null) {
240: if (logger.isLoggable(Level.INFO)) {
241: logger.log(Level.INFO, "PSSA_CSSI0012", new String[] {
242: adapterName, identifier });
243: }
244: }
245:
246: return obj;
247: }
248:
249: /**
250: * Returns a JCAPI CalStore object.
251: */
252: public CalendarStore getCalStore() throws Exception {
253:
254: // check for an existing store
255: //
256: if (isAvailable()) {
257: return calStore;
258: }
259:
260: // no existing store, so get the session. if it is null, then do
261: // not attempt to connect to the server.
262: //
263: calSession = getCalSession();
264:
265: if (calSession == null) {
266: return null;
267: }
268:
269: String host = adapterProperties.getProperty(PROP_HOST_NAME);
270: calStore = calSession.getStore(serviceClass);
271: calStore.connect(host);
272:
273: return calStore;
274: }
275:
276: /**
277: * Returns a JCAPI CalSession object.
278: */
279: public CalendarSession getCalSession() throws Exception {
280:
281: if (calSession != null) {
282: if (logger.isLoggable(Level.INFO)) {
283: logger.log(Level.INFO, "PSSA_CSSI0015", new String[] {
284: adapterName, identifier });
285: }
286: return calSession;
287: }
288:
289: // check for mandatory properties, if they are null then there's
290: // no point in trying to establish a connection
291: //
292: String host = adapterProperties.getProperty(PROP_HOST_NAME);
293: String password = adapterProperties
294: .getProperty(PROP_PASSWORD_NAME);
295: String uid = adapterProperties.getProperty(PROP_UID_NAME);
296:
297: if ((host == null) || (password == null) || (uid == null)) {
298: if (logger.isLoggable(Level.INFO)) {
299: logger.log(Level.INFO, "PSSA_CSSI0016", new String[] {
300: adapterName, identifier });
301: }
302: return null;
303: }
304:
305: String protocol = adapterProperties.getProperty(
306: PROP_PROTOCOL_NAME, CALENDAR_DEFAULT_PROTOCOL);
307: String domain = adapterProperties.getProperty("winDomain");
308: String ocxHost = adapterProperties.getProperty("ocxHost", host);
309: String pimInterfaceType = adapterProperties.getProperty(
310: "pimInterfaceType", "webdav");
311: String usersDn = adapterProperties.getProperty("usersDn", ""); //e.g: CN=Users
312: String hostDn = adapterProperties.getProperty("hostDn", ""); //e.g: DC=[HOST]
313: String rootSuffix = adapterProperties.getProperty("rootSuffix",
314: "");
315: String webdavProtocol = adapterProperties.getProperty(
316: "webdavProtocol", "http");
317: String webdavPort = adapterProperties.getProperty("webdavPort",
318: "80");
319: String webdavCustomTaskFilter = adapterProperties.getProperty(
320: "webdavCustomTaskFilter", "true");
321: String activeDirectoryLdapPort = adapterProperties.getProperty(
322: "activeDirectoryLdapPort", "389");
323: Properties props = new Properties();
324:
325: if (uid != null)
326: props.put("cal.user", uid);
327: if (password != null)
328: props.put("cal.password", password);
329: if (protocol != null)
330: props.put("cal.protocol", protocol);
331: if (domain != null)
332: props.put("cal.exchange.domain", domain);
333: if (ocxHost != null)
334: props.put("cal.exchange.ocxhost", ocxHost);
335: if (pimInterfaceType != null)
336: props
337: .put("cal.exchange.pimInterfaceType",
338: pimInterfaceType);
339: if (webdavProtocol != null)
340: props.put("cal.exchange.webdavProtocol", webdavProtocol);
341: if (webdavPort != null)
342: props.put("cal.exchange.webdavPort", webdavPort);
343: if (webdavCustomTaskFilter != null)
344: props.put("cal.exchange.webdavCustomTaskFilter",
345: webdavCustomTaskFilter);
346: if (activeDirectoryLdapPort != null)
347: props.put("cal.exchange.activeDirectoryLdapPort",
348: activeDirectoryLdapPort);
349:
350: if (logger.isLoggable(Level.INFO))
351: props.put("cal.pim.debug", "true");
352:
353: props.put("cal.pim.service", "exchange");
354:
355: //compute the usersBaseDn
356: if (usersDn == null) {
357: usersDn = "";
358: }
359: StringTokenizer strTok = new StringTokenizer(host, ".");
360: String unQualifiedHost = host;
361: if (strTok.hasMoreTokens()) {
362: unQualifiedHost = strTok.nextToken();
363: }
364:
365: String computedHostDn = "";
366: if (hostDn != null) {
367: //if hostDn is as DC=[HOST] , replace the [HOST] by host
368: int indexStart = hostDn.indexOf("[");
369: int indexEnd = hostDn.indexOf("]", indexStart);
370: if (indexStart > -1 && indexEnd > -1) {
371: //if ( indexEnd = hostFn;.length() ) indexEnd --;
372: computedHostDn = hostDn.substring(0, indexStart)
373: + unQualifiedHost
374: + hostDn.substring(indexEnd + 1);
375: } else {
376: //HOST might have been hardcoded
377: computedHostDn = hostDn;
378: }
379: }
380:
381: if (rootSuffix == null || rootSuffix.equals("")) {
382: rootSuffix = "";
383: //check if host is fqdn
384: while (strTok.hasMoreTokens()) {
385: rootSuffix += ("DC=" + strTok.nextToken());
386: if (strTok.hasMoreTokens())
387: rootSuffix += ",";
388: }
389: }
390: //usersBaseDn should be like CN=Users,dc=chertsey,dc=red,dc=iplanet,dc=com for 2000 and 2003
391: //cn=Recipients,ou=PLANET3,o=Sun Microsystems for 5.5 . Hence defaults to 2000
392: String usersBaseDn = usersDn;
393: if (!usersBaseDn.trim().equals("")) {
394: if (!computedHostDn.trim().equals(""))
395: usersBaseDn += ("," + computedHostDn);
396: } else {
397: usersBaseDn = computedHostDn;
398: }
399: if (!usersBaseDn.trim().equals("")) {
400: if (!rootSuffix.trim().equals(""))
401: usersBaseDn += ("," + rootSuffix);
402: } else {
403: usersBaseDn = rootSuffix;
404: }
405: if (logger.isLoggable(Level.INFO)) {
406: logger.log(Level.INFO, "PSSA_CSSI0021",
407: new String[] { usersBaseDn });
408: }
409:
410: if (usersBaseDn != null)
411: props.put("cal.exchange.usersBaseDn", usersBaseDn);
412:
413: calSession = calSession.getInstance(props);
414:
415: return calSession;
416: }
417:
418: /**
419: * Adapter specific Connection termination.
420: *
421: * @return true if the connection was terminated successfully.
422: */
423: public boolean closeConnection() {
424: boolean retval = true;
425:
426: try {
427: calStore.disconnect();
428: calStore = null;
429: calSession = null;
430: } catch (Exception e) {
431: retval = false;
432: }
433:
434: if (logger.isLoggable(Level.INFO)) {
435: logger.log(Level.INFO, "PSSA_CSSI0005", new String[] {
436: adapterName, identifier });
437: }
438:
439: return retval;
440: }
441:
442: /**
443: * Implements SSOTokenListener "ssoTokenChanged" method.
444: *
445: * The following are possible SSO token event types:
446: * <ul>
447: * <li>SSO_TOKEN_IDLE_TIMEOUT
448: * <li>SSO_TOKEN_MAX_TIMEOUT
449: * <li>SSO_TOKEN_DESTROY
450: * </ul>
451: *
452: * The event getType() method is used to ensure that one of the
453: * three types above are the basis for this event. If getType()
454: * returns a type not listed above, then an SSOException is thrown.
455: *
456: * @param evt SSOTokenEvent
457: */
458: public void ssoTokenChanged(SSOTokenEvent evt) {
459:
460: try {
461: int evtType = evt.getType();
462:
463: if (calStore != null) {
464: calStore.disconnect();
465: }
466:
467: calStore = null;
468: calSession = null;
469: } catch (Exception e) {
470: if (logger.isLoggable(Level.WARNING)) {
471: logger.log(Level.WARNING, "PSSA_CSSI0006", e);
472: }
473: return;
474: }
475:
476: if (logger.isLoggable(Level.INFO)) {
477: logger.log(Level.INFO, "PSSA_CSSI0002", new String[] {
478: adapterName, identifier });
479: }
480: }
481:
482: }
|