001: /*
002: * $Id: UserProfileCache.java,v 1.11 2005/11/30 11:27:19 ss150821 Exp $
003: * $Source: /m/portal/ps/srap/src/com/sun/portal/rproxy/configservlet/server/UserProfileCache.java,v $
004: * $Log: UserProfileCache.java,v $
005: * Revision 1.11 2005/11/30 11:27:19 ss150821
006: * 6356996 - Srap Code base needs to save files in the unix file format and not windows
007: *
008: * Revision 1.10 2005/07/13 09:32:49 ss150821
009: * Bug Number : 6268889 - AM Policy evaluation should not be done for Portal Feature Access
010: *
011: * Revision 1.9 2005/02/23 09:14:08 ss150821
012: * RFE 6223490 - SRA Should use JDK based logging
013: *
014: * Revision 1.8 2004/01/12 08:46:47 ss144266
015: * Added proxylet parameters
016: *
017: * Revision 1.7 2003/08/11 13:36:57 mm132998
018: * Bug : 4893409
019: *
020: * Revision 1.6 2003/07/10 14:04:14 mm132998
021: * Basic Auth related Fix
022: *
023: * Revision 1.5 2003/05/30 12:54:30 mm132998
024: * Session Validation at server side
025: *
026: * Revision 1.4 2003/05/28 13:21:25 mm132998
027: * Custom Auth level
028: *
029: * Revision 1.3 2003/01/23 10:10:12 bv131302
030: * id60 changes merged
031: *
032: * Revision 1.2.18.1 2002/12/30 11:13:47 bv131302
033: * id60 related changes
034: *
035: * Revision 1.2 2002/06/21 13:04:13 bv131302
036: * LDAP Attribute name changes
037: *
038: * Revision 1.1 2002/06/14 09:53:50 rt130506
039: * SRAP rebranding
040: *
041: * Revision 1.7 2002/06/11 16:02:10 bv131302
042: * new branded
043: *
044: * Revision 1.6 2002/05/13 06:47:20 mm132998
045: * Bug ID : 4668063 , CRT : 816 Desc : gateway migration to iDSAME 9
046: *
047: * Revision 1.5 2002/05/13 06:22:26 mm132998
048: * Perf related modifications
049: *
050: * Revision 1.4 2002/04/15 08:49:50 mm132998
051: * Bug ID : 4668063 , CRT : 816 , Desc : gateway migration to iDSAME 9
052: *
053: * Revision 1.3 2002/02/21 10:04:25 mm132998
054: * Checkin for Bug id : 4640268 - RProxy migration to iDSAME
055: *
056: * Revision 1.2 2002/02/21 06:01:32 mm132998
057: * Checkin for Bug id : 4640268 - RProxy migration to iDSAME
058: *
059: *
060: */
061: /*
062: * UserProfileCache.java
063: *
064: * Created on February 7, 2002, 8:07 PM
065: * @author Mridul Muralidharan
066: *
067: * This class implements a Response cache at the servlet side and also
068: * handles setAttribute requests.
069: */
070:
071: package com.sun.portal.rproxy.configservlet.server;
072:
073: /*
074: * import com.iplanet.am.sdk.AMException; import
075: * com.sun.portal.log.common.PortalLogger; import
076: * com.iplanet.am.sdk.AMStoreConnection; import com.iplanet.am.sdk.AMUser;
077: * import com.iplanet.am.sdk.AMTemplate;
078: *
079: * import com.sun.portal.rproxy.configservlet.client.AttributeExtractor;
080: */
081: import java.io.FileWriter;
082: import java.io.PrintWriter;
083: import java.util.HashMap;
084: import java.util.HashSet;
085: import java.util.Iterator;
086: import java.util.Map;
087: import java.util.NoSuchElementException;
088: import java.util.Set;
089: import java.util.StringTokenizer;
090:
091: import com.iplanet.am.sdk.AMException;
092: import com.iplanet.am.sdk.AMStoreConnection;
093: import com.iplanet.am.sdk.AMUser;
094: import com.iplanet.sso.SSOException;
095: import com.iplanet.sso.SSOToken;
096: import com.iplanet.sso.SSOTokenEvent;
097: import com.iplanet.sso.SSOTokenListener;
098: import com.iplanet.sso.SSOTokenManager;
099: import com.sun.identity.policy.PolicyEvaluator;
100: import com.sun.identity.policy.PolicyException;
101: import com.sun.portal.rproxy.configservlet.Response;
102:
103: // public class UserProfileCache implements SSOTokenListener ,Runnable{
104: public class UserProfileCache implements SSOTokenListener {
105:
106: private static UserProfileCache instance = null;
107:
108: private HashMap cache = null;
109:
110: // private Hashtable cache = null;
111: private int numEntries = 0;
112:
113: private int numOfCleanCacheSkips = 0;
114:
115: private boolean keepGoing = true;
116:
117: NoSuchElementException noSuchElementException = null;
118:
119: /*
120: * Disabling cacheCleaner thread. Since the iDSAME session notification
121: * seems to be working fine , we are removing this thread - Mridul
122: */
123: /*
124: * As Sanjib pointed out in his review these values are a compromise
125: * depending on the size to which the cache may grow (if kept large) and how
126: * frequently the synchronized method cleanupCache() gets called - which may
127: * result in perf hit in case there are a lot of entries in the cache.
128: * Arbitrary values , which may be ok compromise (??) - Mridul
129: */
130: // 7 1/2 mins
131: // private static final long SLEEP_TIME = 450000L;
132: // 30 mins , sufficient ??
133: // protected static final long CACHE_CLEAR_TIMEOUT_VALUE = 1800000L;
134: // This may be a partial solution to the above ??
135: // Maximum number of entries in cache
136: // private static final int MAX_CACHE_SIZE = 1024;
137: // Temp debug code :-)
138: // - Mridul
139: static final boolean doDebug = false;
140:
141: static PrintWriter log = null;
142: static {
143: instance = new UserProfileCache();
144: // Thread thr = new Thread(instance);
145: // thr.start();
146: if (doDebug) {
147: try {
148: log = new PrintWriter(
149: new FileWriter("/tmp/servlet.out"));
150: } catch (Exception ex) {
151: }
152: }
153: }
154:
155: /*
156: * Since the Session Notification does not seem to be consistent , we have
157: * this method being called regularly so that we clear the cache of old
158: * values
159: *
160: * IMP_NOTE :: This is only a workaround until SessionListener is properly
161: * and reliably working in iDSAME This has to be removed in Final drop and
162: * only ssoTokenListener will be used. - Mridul
163: */
164: /*
165: * private synchronized void cleanupCache(long time, boolean force){
166: *
167: * if (force || numEntries >= MAX_CACHE_SIZE){ numOfCleanCacheSkips = 0;
168: * Enumeration ee = cache.keys(); UserSessionListenerTableEntry entry;
169: * Object key;
170: *
171: * while (ee.hasMoreElements()){ key = ee.nextElement(); entry =
172: * (UserSessionListenerTableEntry)cache.get(key);
173: *
174: * if (time - entry.getTimeStamp() > CACHE_CLEAR_TIMEOUT_VALUE){ // Clear
175: * entry removeEntry((String)key); } } } }
176: *
177: * public void run(){
178: *
179: * long currtime; while (keepGoing){ try{
180: * Thread.currentThread().sleep(SLEEP_TIME); } catch(InterruptedException
181: * iex){} currtime = System.currentTimeMillis(); if (numOfCleanCacheSkips >=
182: * 10){ cleanupCache(currtime,true); } else{ numOfCleanCacheSkips++;
183: * cleanupCache(currtime,false); } } }
184: */
185:
186: private UserProfileCache() {
187: // cache = new Hashtable();
188: cache = new HashMap();
189: }
190:
191: /*
192: * Listener for session events The events are : 1) SSO_TOKEN_DESTROY 2)
193: * SSO_TOKEN_IDLE_TIMEOUT 3) SSO_TOKEN_MAX_TIMEOUT All of these indicate
194: * that the session has been destroyed/invalidated.
195: */
196: // iDSAME does not seem to be not handling the SessionNotification properly
197: // ,
198: // so what do we do ?
199: // Thinking of creating a timestamp for each entry and periodically cleaning
200: // the cache.
201: // That would only be a workaround and should be removed before final drop.
202: public void ssoTokenChanged(SSOTokenEvent evt) {
203:
204: int type = -1;
205: if (doDebug) {
206: writeLog("Notification Message recieved : It works !!\n");
207: }
208:
209: try {
210: type = evt.getType();
211: } catch (SSOException ssoex) {
212: }
213:
214: if (type == SSOTokenEvent.SSO_TOKEN_DESTROY
215: || type == SSOTokenEvent.SSO_TOKEN_IDLE_TIMEOUT
216: || type == SSOTokenEvent.SSO_TOKEN_MAX_TIMEOUT) {
217:
218: String tokid = evt.getToken().getTokenID().toString();
219: // Remove this entry from this session listener cache.
220: removeEntry(tokid);
221: }
222: }
223:
224: private synchronized void addEntry(String id,
225: UserSessionListenerTableEntry entry) {
226: /*
227: * if (numEntries >= MAX_CACHE_SIZE){
228: * cleanupCache(System.currentTimeMillis(),true); }
229: */
230: if (cache.put(id, entry) == null) {
231: // Some other value got replaced !
232: numEntries++;
233: }
234: // Else , some other value got replaced.
235: }
236:
237: private synchronized Object getEntry(String key) {
238: return cache.get(key);
239: }
240:
241: private synchronized void removeEntry(String key) {
242: if (cache.remove(key) != null) {
243: numEntries--;
244: }
245: }
246:
247: private synchronized boolean containsKey(String sessid) {
248: return cache.containsKey(sessid);
249: }
250:
251: static void writeLog(String message) {
252:
253: if (doDebug) {
254: try {
255: log.write(message);
256: log.flush();
257: } catch (Exception ex) {
258: }
259: }
260: }
261:
262: static void writeLog(Exception exMessage) {
263: if (doDebug) {
264: try {
265: exMessage.printStackTrace(log);
266: writeLog("\n");
267: } catch (Exception ex) {
268: }
269: }
270: }
271:
272: private Object getValue(String key) {
273: UserSessionListenerTableEntry entry = (UserSessionListenerTableEntry) getEntry(key);
274:
275: if (entry != null) {
276: if (doDebug) {
277: writeLog("Cache hit !\n");
278: }
279: return entry.getResponse();
280: }
281: // Add this entry
282: try {
283: if (doDebug) {
284: writeLog("New entry : " + key + "\n");
285: }
286: SSOToken tok = getSSOToken(key);
287: Response resp = fetchUserAttributes(tok);
288: addNewEntry(tok, key, resp);
289: entry = (UserSessionListenerTableEntry) getEntry(key);
290: } catch (Exception ex) {
291: if (doDebug) {
292: writeLog("Exception !\n");
293: ex.printStackTrace(log);
294: writeLog("\n");
295: }
296: return null;
297: }
298: return entry.getResponse();
299: }
300:
301: private void addNewEntry(SSOToken tok, String tokid, Response resp) {
302: UserSessionListenerTableEntry entry = new UserSessionListenerTableEntry(
303: resp);
304: addEntry(tokid, entry);
305: try {
306: tok.addSSOTokenListener(this );
307: } catch (SSOException ssoex) {
308: // Remove this entry from cache !
309: /*
310: * Since iDSAME seems to have problems with SSOTokenListener ,
311: * comment this out. Must be uncommented by Final Drop.
312: */
313: // SSOTokenListener works now !
314: removeEntry(tokid);
315: }
316: }
317:
318: public static void addEntry(SSOToken tok, String tokid,
319: Response resp) {
320: instance.addNewEntry(tok, tokid, resp);
321: }
322:
323: public static Response getResponse(String tokid) {
324: return (Response) instance.getValue(tokid);
325: }
326:
327: public static boolean contains(String sessid) {
328: return instance.containsKey(sessid);
329: }
330:
331: private void setAttributeInternal(String sessid, String attr,
332: Set value) throws NoSuchElementException {
333: UserSessionListenerTableEntry entry = (UserSessionListenerTableEntry) getEntry(sessid);
334:
335: if (entry != null) {
336: entry.setAttribute(attr, value);
337: } else {
338: // throw new NoSuchElementException("Missing profile object !");
339: /*
340: * Instead of throwing an Exception , we add this sessid. This is
341: * necessary 'cos the cleaner thread may have removed this entry
342: * from cache. Must be changed when SessionNotification comes into
343: * play
344: */
345: try {
346: SSOToken tok = getSSOToken(sessid);
347: Response resp = fetchUserAttributes(tok);
348: addNewEntry(tok, sessid, resp);
349: entry = (UserSessionListenerTableEntry) getEntry(sessid);
350: entry.setAttribute(attr, value);
351: } catch (Exception ex) {
352: // What do we now ??
353: if (noSuchElementException == null) {
354: noSuchElementException = new NoSuchElementException(
355: "Missing profile object !");
356: }
357: throw noSuchElementException;
358: }
359: }
360: // Update iDSAME profile
361: HashMap map = new HashMap();
362: map.put(attr, value);
363: updateDsameProfile(sessid, map);
364: }
365:
366: public static void setAttribute(String sessid, String attr,
367: Set value) throws NoSuchElementException {
368: // Sets a map of values for the specified attribute.
369: // Here as of now , the assumptions is that the Map will contain Strings
370: // only.
371: instance.setAttributeInternal(sessid, attr, value);
372: }
373:
374: protected SSOToken getSSOToken(String tokid) throws SSOException {
375:
376: SSOTokenManager tokenManager = SSOTokenManager.getInstance();
377: SSOToken tok = null;
378: try {
379: tok = tokenManager.createSSOToken(tokid);
380: } catch (SSOException ex) {
381: if (doDebug) {
382: writeLog("Exception thrown :");
383: ex.printStackTrace(log);
384: }
385: // Only 'cos the client may have sent a decoded sessid -
386: try {
387: tok = tokenManager.createSSOToken(java.net.URLEncoder
388: .encode(tokid));
389: } catch (SSOException ex1) {
390: if (doDebug) {
391: writeLog("Exception thrown again :");
392: ex1.printStackTrace(log);
393: }
394: throw ex1;
395: } catch (Exception gex1) {
396: // 'cos of URLEncode
397: throw ex;
398: }
399: }
400: /*
401: * if (tok == null){ throw new SSOException("Token == null"); }
402: */
403: return tok;
404: }
405:
406: private Response fetchUserAttributes(SSOToken token)
407: throws AMException, SSOException {
408:
409: AMStoreConnection connection = new AMStoreConnection(token);
410: AMUser user = connection
411: .getUser(token.getPrincipal().getName());
412: Map attr = null;
413: Map attr2 = null;
414: Map attr3 = null;
415: Map attr4 = null;
416: Map attr5 = null;
417: String authResult = "true";
418:
419: try {
420: attr = user
421: .getServiceAttributes("srapGatewayAccessService");
422: if (doDebug) {
423: writeLog("srapGatewayAccessService Attributes : ");
424: writeLog(attr.toString());
425: }
426:
427: Set authLevelAttr = (Set) attr
428: .get("sunPortalGatewayAllowedAuthLevel");
429:
430: if (authLevelAttr != null) {
431: Iterator iter = authLevelAttr.iterator();
432: if (iter.hasNext()) {
433: authResult = evaluateAuthAllowed(iter.next()
434: .toString(), token, authResult);
435: if (doDebug) {
436: writeLog("authLevelAttr : " + authLevelAttr);
437: writeLog("authResult : " + authResult);
438: }
439: }
440: }
441:
442: } catch (Exception ex) {
443: if (doDebug) {
444: writeLog(token.getTokenID().toString());
445: writeLog(ex);
446: }
447: attr = new HashMap();
448: }
449: try {
450: attr2 = user.getServiceAttributes("srapNetletService");
451: if (doDebug) {
452: writeLog("srapNetletService Attributes : ");
453: writeLog(attr2.toString());
454: }
455: } catch (Exception ex) {
456: if (doDebug) {
457: writeLog(token.getTokenID().toString());
458: writeLog(ex);
459: }
460: }
461:
462: try {
463: attr5 = user.getServiceAttributes("srapProxyletService");
464: if (doDebug) {
465: writeLog("srapProxyletService Attributes : ");
466: writeLog(attr5.toString());
467: }
468: } catch (Exception ex) {
469: if (doDebug) {
470: writeLog(token.getTokenID().toString());
471: writeLog(ex);
472: }
473: }
474:
475: try {
476: attr3 = user.getServiceAttributes("iplanetAMUserService");
477: if (doDebug) {
478: writeLog("iplanetAMUserService Attributes : ");
479: writeLog(attr3.toString());
480: }
481: } catch (Exception ex) {
482: if (doDebug) {
483: writeLog(token.getTokenID().toString());
484: writeLog(ex);
485: }
486: }
487:
488: /* Bug Number : 6268889 - AM Policy evaluation should not be done for Portal Feature Access */
489: /* Removing this code and making this as always allowed - Sandeep Soni */
490: /*
491: boolean allowed = false;
492:
493: try {
494: PolicyEvaluator policyEval = new PolicyEvaluator("srapGatewayAccessService");
495: allowed = policyEval.isAllowed(token, "", "sunPortalGatewayEnableSSO");
496: } catch (PolicyException pe) {
497: if (doDebug) {
498: writeLog("srapGatewayAccessService Policy : " + (new Boolean(allowed)).toString());
499: }
500: } catch (SSOException ssoe) {
501: if (doDebug) {
502: writeLog("srapGatewayAccessService Policy : SSOException");
503: }
504: }
505: */
506: attr4 = new HashMap();
507: Set policySet = new HashSet();
508: policySet.add(Boolean.TRUE.toString());
509: attr4.put("sunPortalGatewayEnableSSO", policySet);
510:
511: Map allAttributes = new com.sun.portal.rproxy.configservlet.AMHashMap();
512: if (attr != null) {
513: allAttributes.putAll(attr);
514: }
515: if (attr2 != null) {
516: allAttributes.putAll(attr2);
517: }
518: if (attr3 != null) {
519: allAttributes.putAll(attr3);
520: }
521: if (attr4 != null) {
522: allAttributes.putAll(attr4);
523: }
524: if (attr5 != null) {
525: allAttributes.putAll(attr5);
526: }
527: Set set = new HashSet();
528: set.add(authResult);
529: allAttributes
530: .put("sunPortalGatewayAllowedAuthLevelResult", set);
531:
532: if (doDebug) {
533: writeLog("\n\nComplete attributes : " + allAttributes);
534: }
535:
536: Response response = new Response("", "", allAttributes);
537: return response;
538: }
539:
540: private String evaluateAuthAllowed(String authLevelAttr,
541: SSOToken token, String defResult) {
542:
543: StringTokenizer st = new StringTokenizer(authLevelAttr, "|");
544: String result = defResult;
545:
546: int authLevel = 0;
547:
548: try {
549: authLevel = token.getAuthLevel();
550: } catch (SSOException ssoEx) {
551: return defResult;
552: }
553:
554: // For each of these token , check whether token.getAuthLevel() falls in
555: // the
556: // ranges specified.
557:
558: while (st.hasMoreTokens()) {
559: String tok = st.nextToken().trim();
560: Operation oper = new Operation(tok);
561:
562: if (oper.isValid()) {
563: if (oper.evaluate(authLevel)) {
564: return "true";
565: }
566: }
567: }
568:
569: return "false";
570: }
571:
572: protected void updateDsameProfile(String tokid, Map toUpdate) {
573:
574: try {
575: if (doDebug) {
576: writeLog("Setting attribute " + toUpdate + "\n");
577: }
578: SSOToken token = getSSOToken(tokid);
579: AMStoreConnection connection = new AMStoreConnection(token);
580: AMUser user = connection.getUser(token.getPrincipal()
581: .getName());
582: // Is this necessary ?
583: if (user.isExists()) {
584: user.setAttributes(toUpdate);
585: user.store();
586: }
587: if (doDebug) {
588: writeLog("Attribute set " + toUpdate + " suceeded !\n");
589: }
590: } catch (SSOException ex) {
591: if (doDebug) {
592: writeLog("Exception while Setting attribute "
593: + toUpdate + "\n");
594: writeLog(ex);
595: }
596: } catch (AMException ex) {
597: if (doDebug) {
598: writeLog("Exception while Setting attribute "
599: + toUpdate + "\n");
600: writeLog(ex);
601: }
602: }
603: }
604:
605: /*
606: * // Not required right now : But maybe required in future. public String
607: * getOrganizationAttribute(String szServiceName, String szAttributeName,
608: * String szDefaultVal,SSOToken token) {
609: *
610: * AMUser user; AMStoreConnection connection;
611: *
612: * String orgDN = null; String val = szDefaultVal; String value=
613: * szDefaultVal;
614: *
615: * try{ connection = new AMStoreConnection(token); user =
616: * connection.getUser(token.getPrincipal().getName()); orgDN =
617: * user.getOrganizationDN(); if(orgDN == null) return null;
618: *
619: * AMOrganization org = connection.getOrganization(orgDN);
620: *
621: * if(org.orgTemplateExists(szServiceName)){
622: *
623: * AMTemplate temp = org.getTemplate(szServiceName,
624: * AMTemplate.ORGANIZATION_TEMPLATE); //val =
625: * temp.getStringAttribute(szAttributeName); Set vals =
626: * temp.getAttribute(szAttributeName);
627: *
628: * if (vals != null && vals.size() > 0) { Iterator iter = vals.iterator();
629: * value = (String) iter.next(); } } else { AMSchema schema =
630: * connection.getSchema(szServiceName, AMSchema.Type.ORGANIZATION);
631: *
632: * Map mapVals = schema.getAttributeDefaults();
633: *
634: * String data = AttributeExtractor.getString( mapVals , szAttributeName,
635: * szDefaultVal);
636: *
637: * if((data == null) || (data.equals(""))) { return szDefaultVal; } else {
638: * if (doDebug){ writeLog(szAttributeName + " Org Attribute obtained thru
639: * schema is " + data); } return data; } } } catch(AMException ame){ if
640: * (doDebug){ writeLog("AMException in getting Org Attribute " + ame); } }
641: * catch(SSOException ssoe){ if (doDebug){ writeLog("SSOException in getting
642: * Org Attribute " + ssoe); } } catch(Exception e){ if (doDebug){
643: * writeLog("Exception in getting Org Attribute " + e); } } val = value;
644: * return val; }
645: */
646:
647: class UserSessionListenerTableEntry {
648:
649: private Response resp = null;
650:
651: // private HashMap changed = null;
652: /*
653: * Should timestamp be the time since last access to this variable or
654: * should it hold the time of creation ? Currently doing the former
655: */
656: private long timestamp;
657:
658: public UserSessionListenerTableEntry(Response resp) {
659: this .resp = resp;
660: // this.changed = new HashMap();
661: timestamp = System.currentTimeMillis();
662: }
663:
664: public long getTimeStamp() {
665: return timestamp;
666: }
667:
668: public Response getResponse() {
669: timestamp = System.currentTimeMillis();
670: return resp;
671: }
672:
673: /*
674: * public Map getChanged(){ return changed; }
675: */
676:
677: public void setAttribute(String attr, Set value) {
678:
679: Map entries = (Map) resp.getReturnedObject();
680: entries.put(attr, value);
681: resp.setReturnedObject(entries);
682: timestamp = System.currentTimeMillis();
683: // changed.put(attr,value);
684: }
685: }
686: }
|