]> git.aero2k.de Git - tmp/jakarta-migration.git/blob
2fef70989449d4f3f6c02f7822c9c1d4680f6df2
[tmp/jakarta-migration.git] /
1 /**
2  *  This document is a part of the source code and related artifacts
3  *  for CollectionSpace, an open source collections management system
4  *  for museums and related institutions:
5
6  *  http://www.collectionspace.org
7  *  http://wiki.collectionspace.org
8
9  *  Copyright 2009 University of California at Berkeley
10
11  *  Licensed under the Educational Community License (ECL), Version 2.0.
12  *  You may not use this file except in compliance with this License.
13
14  *  You may obtain a copy of the ECL 2.0 License at
15
16  *  https://source.collectionspace.org/collection-space/LICENSE.txt
17
18  *  Unless required by applicable law or agreed to in writing, software
19  *  distributed under the License is distributed on an "AS IS" BASIS,
20  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
21  *  See the License for the specific language governing permissions and
22  *  limitations under the License.
23  *//**
24  *  This document is a part of the source code and related artifacts
25  *  for CollectionSpace, an open source collections management system
26  *  for museums and related institutions:
27
28  *  http://www.collectionspace.org
29  *  http://wiki.collectionspace.org
30
31  *  Copyright 2009 University of California at Berkeley
32
33  *  Licensed under the Educational Community License (ECL), Version 2.0.
34  *  You may not use this file except in compliance with this License.
35
36  *  You may obtain a copy of the ECL 2.0 License at
37
38  *  https://source.collectionspace.org/collection-space/LICENSE.txt
39
40  *  Unless required by applicable law or agreed to in writing, software
41  *  distributed under the License is distributed on an "AS IS" BASIS,
42  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
43  *  See the License for the specific language governing permissions and
44  *  limitations under the License.
45  */
46 /*
47  * To change this template, choose Tools | Templates
48  * and open the template in the editor.
49  */
50 package org.collectionspace.authentication.realm.db;
51
52 import java.net.ConnectException;
53 import java.sql.Connection;
54 import java.sql.PreparedStatement;
55 import java.sql.ResultSet;
56 import java.sql.SQLException;
57 import java.util.LinkedHashSet;
58 import java.util.Map;
59 import java.util.Set;
60
61 import javax.naming.Context;
62 import javax.naming.InitialContext;
63 import javax.naming.NamingException;
64 import javax.security.auth.login.AccountException;
65 import javax.security.auth.login.AccountNotFoundException;
66 import javax.sql.DataSource;
67
68 import org.collectionspace.authentication.AuthN;
69 import org.collectionspace.authentication.CSpaceTenant;
70 import org.collectionspace.authentication.realm.CSpaceRealm;
71 import org.postgresql.util.PSQLState;
72 import org.slf4j.Logger;
73 import org.slf4j.LoggerFactory;
74
75 /**
76  * CSpaceDbRealm provides access to user, password, role, tenant database
77  * @author 
78  */
79 public class CSpaceDbRealm implements CSpaceRealm {
80         public static String DEFAULT_DATASOURCE_NAME = "CspaceDS";
81         
82     private Logger logger = LoggerFactory.getLogger(CSpaceDbRealm.class);
83     
84     private String datasourceName;
85     private String principalsQuery;
86     private String saltQuery;
87     private String requireSSOQuery;
88     private String rolesQuery;
89     private String tenantsQueryNoDisabled;
90     private String tenantsQueryWithDisabled;
91     private boolean suspendResume;
92
93     private long maxRetrySeconds = MAX_RETRY_SECONDS;
94     private static final int MAX_RETRY_SECONDS = 5;
95     private static final String MAX_RETRY_SECONDS_STR = "maxRetrySeconds";
96
97         private long delayBetweenAttemptsMillis = DELAY_BETWEEN_ATTEMPTS_MILLISECONDS;
98     private static final String DELAY_BETWEEN_ATTEMPTS_MILLISECONDS_STR = "delayBetweenAttemptsMillis";
99         private static final long DELAY_BETWEEN_ATTEMPTS_MILLISECONDS = 200;
100         
101         protected void setMaxRetrySeconds(Map<String, ?> options) {
102                 Object optionsObj = options.get(MAX_RETRY_SECONDS_STR);
103                 if (optionsObj != null) {
104                         String paramValue = optionsObj.toString();
105                         try {
106                                 maxRetrySeconds = Long.parseLong(paramValue);
107                         } catch (NumberFormatException e) {
108                                 logger.warn(String.format("The Spring Security login authentication parameter '%s' with value '%s' could not be parsed to a long value.  The default value of '%d' will be used instead.",
109                                                 MAX_RETRY_SECONDS_STR, paramValue, maxRetrySeconds));
110                         }
111                 }
112         }
113         
114         protected long getMaxRetrySeconds() {
115                 return this.maxRetrySeconds;
116         }
117         
118         protected void setDelayBetweenAttemptsMillis(Map<String, ?> options) {
119                 Object optionsObj = options.get(DELAY_BETWEEN_ATTEMPTS_MILLISECONDS_STR);
120                 if (optionsObj != null) {
121                         String paramValue = optionsObj.toString();
122                         try {
123                                 delayBetweenAttemptsMillis = Long.parseLong(paramValue);
124                         } catch (NumberFormatException e) {
125                                 logger.warn(String.format("The Spring Security login authentication parameter '%s' with value '%s' could not be parsed to a long value.  The default value of '%d' will be used instead.",
126                                                 MAX_RETRY_SECONDS_STR, paramValue, delayBetweenAttemptsMillis));
127                         }
128                 }
129         }
130         
131         protected long getDelayBetweenAttemptsMillis() {
132                 return this.delayBetweenAttemptsMillis;
133         }
134         
135         public CSpaceDbRealm() {
136         datasourceName = DEFAULT_DATASOURCE_NAME;
137         }
138     
139     /**
140      * CSpace Database Realm
141      * @param datasourceName datasource name
142      */
143     public CSpaceDbRealm(Map<String, ?> options) {
144         datasourceName = (String) options.get("dsJndiName");
145         if (datasourceName == null) {
146             datasourceName = DEFAULT_DATASOURCE_NAME;
147         }
148         Object tmp = options.get("principalsQuery");
149         if (tmp != null) {
150             principalsQuery = tmp.toString();
151         }
152         tmp = options.get("saltQuery");
153         if (tmp != null) {
154                 saltQuery = tmp.toString();
155         }
156         tmp = options.get("requireSSOQuery");
157         if (tmp != null) {
158                 requireSSOQuery = tmp.toString();
159         }
160         tmp = options.get("rolesQuery");
161         if (tmp != null) {
162             rolesQuery = tmp.toString();
163         }
164         tmp = options.get("tenantsQueryNoDisabled");
165         if (tmp != null) {
166             tenantsQueryNoDisabled = tmp.toString();
167         }
168         tmp = options.get("tenantsQueryWithDisabled");
169         if (tmp != null) {
170                 tenantsQueryWithDisabled = tmp.toString();
171         }
172         tmp = options.get("suspendResume");
173         if (tmp != null) {
174             suspendResume = Boolean.valueOf(tmp.toString()).booleanValue();
175         }
176         
177         this.setMaxRetrySeconds(options);
178         this.setDelayBetweenAttemptsMillis(options);
179         
180         if (logger.isTraceEnabled()) {
181             logger.trace("DatabaseServerLoginModule, dsJndiName=" + datasourceName);
182             logger.trace("principalsQuery=" + principalsQuery);
183             logger.trace("rolesQuery=" + rolesQuery);
184             logger.trace("suspendResume=" + suspendResume);
185         }
186     }
187
188     @Override
189     public String getPassword(String username) throws AccountException {
190
191         String password = null;
192         Connection conn = null;
193         PreparedStatement ps = null;
194         ResultSet rs = null;
195         try {
196             conn = getConnection();
197             // Get the password
198             if (logger.isDebugEnabled()) {
199                 logger.debug("Executing query: " + principalsQuery + ", with username: " + username);
200             }
201             ps = conn.prepareStatement(principalsQuery);
202             ps.setString(1, username);
203             rs = ps.executeQuery();
204             if (rs.next() == false) {
205                 if (logger.isDebugEnabled()) {
206                     logger.debug(principalsQuery + " returned no matches from db");
207                 }
208                 throw new AccountNotFoundException("No matching username found");
209             }
210
211             password = rs.getString(1);
212         } catch (SQLException ex) {
213             if (logger.isTraceEnabled() == true) {
214                 logger.error("Could not open database to read AuthN tables.", ex);
215             }
216             AccountException ae = new AccountException("Authentication query failed: " + ex.getLocalizedMessage());
217             ae.initCause(ex);
218             throw ae;
219         } catch (AccountNotFoundException ex) {
220             throw ex;
221         } catch (Exception ex) {
222             AccountException ae = new AccountException("Unknown Exception");
223             ae.initCause(ex);
224             throw ae;
225         } finally {
226             if (rs != null) {
227                 try {
228                     rs.close();
229                 } catch (SQLException e) {
230                 }
231             }
232             if (ps != null) {
233                 try {
234                     ps.close();
235                 } catch (SQLException e) {
236                 }
237             }
238             if (conn != null) {
239                 try {
240                     conn.close();
241                 } catch (SQLException ex) {
242                 }
243             }
244         }
245         return password;
246     }
247
248     @Override
249     public Set<String> getRoles(String username) throws AccountException {
250         if (logger.isDebugEnabled()) {
251             logger.debug("getRoleSets using rolesQuery: " + rolesQuery + ", username: " + username);
252         }
253
254         Set<String> roles = new LinkedHashSet<String>();
255
256         Connection conn = null;
257         PreparedStatement ps = null;
258         ResultSet rs = null;
259
260         try {
261             conn = getConnection();
262             // Get the user role names
263             if (logger.isDebugEnabled()) {
264                 logger.debug("Executing query: " + rolesQuery + ", with username: " + username);
265             }
266
267             ps = conn.prepareStatement(rolesQuery);
268             try {
269                 ps.setString(1, username);
270             } catch (ArrayIndexOutOfBoundsException ignore) {
271                 // The query may not have any parameters so just try it
272             }
273             rs = ps.executeQuery();
274             if (rs.next() == false) {
275                 if (logger.isDebugEnabled()) {
276                     logger.debug("No roles found");
277                 }
278                 
279                 return roles;
280             }
281
282             do {
283                 String roleName = rs.getString(1);
284                 roles.add(roleName);
285                 
286             } while (rs.next());
287         } catch (SQLException ex) {
288             AccountException ae = new AccountException("Query failed");
289             ae.initCause(ex);
290             throw ae;
291         } catch (Exception e) {
292             AccountException ae = new AccountException("unknown exception");
293             ae.initCause(e);
294             throw ae;
295         } finally {
296             if (rs != null) {
297                 try {
298                     rs.close();
299                 } catch (SQLException e) {
300                 }
301             }
302             if (ps != null) {
303                 try {
304                     ps.close();
305                 } catch (SQLException e) {
306                 }
307             }
308             if (conn != null) {
309                 try {
310                     conn.close();
311                 } catch (Exception ex) {
312                 }
313             }
314
315         }
316
317         return roles;
318
319     }
320     @Override
321     public Set<CSpaceTenant> getTenants(String username) throws AccountException {
322         return getTenants(username, false);
323     }
324     
325     private boolean userIsTenantManager(Connection conn, String username) {
326         String acctQuery = "SELECT csid FROM accounts_common WHERE userid=?";
327         PreparedStatement ps = null;
328         ResultSet rs = null;
329         boolean accountIsTenantManager = false;
330         try {
331             ps = conn.prepareStatement(acctQuery);
332             ps.setString(1, username);
333             rs = ps.executeQuery();
334             if (rs.next()) {
335                 String acctCSID = rs.getString(1);
336                 if(AuthN.TENANT_MANAGER_ACCT_ID.equals(acctCSID)) {
337                         accountIsTenantManager = true;
338                 }
339             }
340         } catch (SQLException ex) {
341             if(logger.isDebugEnabled()) {
342                 logger.debug("userIsTenantManager query failed on SQL error: " + ex.getLocalizedMessage());
343             }
344         } catch (Exception e) {
345             if(logger.isDebugEnabled()) {
346                 logger.debug("userIsTenantManager unknown error: " + e.getLocalizedMessage());
347             }
348         } finally {
349             if (rs != null) {
350                 try {
351                     rs.close();
352                 } catch (SQLException e) {
353                 }
354             }
355             if (ps != null) {
356                 try {
357                     ps.close();
358                 } catch (SQLException e) {
359                 }
360             }
361         }
362         return accountIsTenantManager;
363     }
364     
365     /**
366      * Execute the tenantsQuery against the datasourceName to obtain the tenants for
367      * the authenticated user.
368      * @return set containing the roles
369      */
370     @Override
371     public Set<CSpaceTenant> getTenants(String username, boolean includeDisabledTenants) throws AccountException {
372
373         String tenantsQuery = getTenantQuery(includeDisabledTenants);
374         
375         if (logger.isDebugEnabled()) {
376             logger.debug("getTenants using tenantsQuery: " + tenantsQuery + ", username: " + username);
377         }
378
379         Set<CSpaceTenant> tenants = new LinkedHashSet<CSpaceTenant>();
380         
381         Connection conn = null;
382         PreparedStatement ps = null;
383         ResultSet rs = null;
384
385         try {
386             conn = getConnection();
387
388             ps = conn.prepareStatement(tenantsQuery);
389             try {
390                 ps.setString(1, username);
391             } catch (ArrayIndexOutOfBoundsException ignore) {
392                 // The query may not have any parameters so just try it
393             }
394             rs = ps.executeQuery();
395             if (rs.next() == false) {
396                 // Check for the tenantManager
397                 if(userIsTenantManager(conn, username)) {
398                     if (logger.isDebugEnabled()) {
399                         logger.debug("GetTenants called with tenantManager - synthesizing the pseudo-tenant");
400                     }
401                     
402                     tenants.add(new CSpaceTenant(AuthN.TENANT_MANAGER_ACCT_ID, "PseudoTenant"));
403                 } else {
404                     if (logger.isDebugEnabled()) {
405                         logger.debug("No tenants found");
406                     }
407                     // We are running with an unauthenticatedIdentity so return an
408                     // empty Tenants set.
409                     // FIXME  should this be allowed?
410                 }
411                 
412                 return tenants;
413             }
414
415             do {
416                 String tenantId = rs.getString(1);
417                 String tenantName = rs.getString(2);
418
419                 tenants.add(new CSpaceTenant(tenantId, tenantName));
420             } while (rs.next());
421         } catch (SQLException ex) {
422             AccountException ae = new AccountException("Query failed");
423             ae.initCause(ex);
424             throw ae;
425         } catch (Exception e) {
426             AccountException ae = new AccountException("unknown exception");
427             ae.initCause(e);
428             throw ae;
429         } finally {
430             if (rs != null) {
431                 try {
432                     rs.close();
433                 } catch (SQLException e) {
434                 }
435             }
436             if (ps != null) {
437                 try {
438                     ps.close();
439                 } catch (SQLException e) {
440                 }
441             }
442             if (conn != null) {
443                 try {
444                     conn.close();
445                 } catch (Exception ex) {
446                 }
447             }
448         }
449
450         return tenants;
451     }
452
453     /*
454      * This method will attempt to get a connection.  If a network error prevents it from getting a connection on the first try
455      * it will retry for the next 'getMaxRetrySeconds()' seconds.  If it is unable to get the connection then it will timeout and
456      * throw an exception.
457      */
458     public Connection getConnection() throws Exception {
459         Connection result = null;
460                 boolean failed = true;
461                 Exception lastException = null;
462                 int requestAttempts = 0;
463
464                 long quittingTime = System.currentTimeMillis() + getMaxRetrySeconds() * 1000; // This is how long we attempt retries
465                 do {
466                         if (requestAttempts > 0) {
467                                 Thread.sleep(getDelayBetweenAttemptsMillis()); // Wait a little time between reattempts.
468                         }
469                         
470                         try {
471                                 // proceed to the original request by calling doFilter()
472                                 result = this.getConnection(getDataSourceName());
473                                 if (result != null) {
474                                         failed = false;
475                                         break; // the request was successfully executed, so we can break out of this retry loop
476                                 } else {
477                                         failed = true;
478                                         throw new ConnectException(); // The 'response' argument indicated a network related failure, so let's throw a generic connection exception
479                                 }
480                         } catch (Exception e) {
481                                 lastException = e;
482                                 if (exceptionChainContainsNetworkError(lastException) == false) {
483                                         // Break if the exception chain does not contain a
484                                         // network related exception because we don't want to retry if it's not a network related failure
485                                         break;
486                                 }
487                                 requestAttempts++; // keep track of how many times we've tried the request
488                         }
489                 } while (System.currentTimeMillis() < quittingTime);  // keep trying until we run out of time
490                 
491                 //
492                 // Add a warning to the logs if we encountered *any* failures on our re-attempts.  Only add the warning
493                 // if we were eventually successful.
494                 //
495                 if (requestAttempts > 0 && failed == false) {
496                         logger.warn(String.format("Request to get a connection from data source '%s' failed with exception '%s' at attempt number '%d' before finally succeeding on the next attempt.",
497                                         getDataSourceName(),
498                                         lastException.getClass().getName(),
499                                         requestAttempts));
500                 }
501
502                 if (failed == true) {
503                         // If we get here, it means all of our attempts to get a successful call to chain.doFilter() have failed.
504                         throw lastException;
505                 }
506                 
507                 return result;
508         }
509     
510         /*
511          * Don't call this method directly.  Instead, use the getConnection() method that take no arguments.
512          */
513         private Connection getConnection(String dataSourceName) throws AccountException, SQLException {
514         InitialContext ctx = null;
515         Connection conn = null;
516         DataSource ds = null;
517         
518         try {
519             ctx = new InitialContext();
520             try {
521                 ds = (DataSource) ctx.lookup(dataSourceName);
522             } catch (Exception e) {}
523             
524                 try {
525                         Context envCtx = (Context) ctx.lookup("java:comp/env");
526                         ds = (DataSource) envCtx.lookup(dataSourceName);
527                 } catch (Exception e) {}
528                 
529                 try {
530                         Context envCtx = (Context) ctx.lookup("java:comp");
531                         ds = (DataSource) envCtx.lookup(dataSourceName);
532                 } catch (Exception e) {}
533                 
534                 try {
535                         Context envCtx = (Context) ctx.lookup("java:");
536                         ds = (DataSource) envCtx.lookup(dataSourceName);
537                 } catch (Exception e) {}
538                 
539                 try {
540                         Context envCtx = (Context) ctx.lookup("java");
541                         ds = (DataSource) envCtx.lookup(dataSourceName);
542                 } catch (Exception e) {}
543                 
544                 try {
545                         ds = (DataSource) ctx.lookup("java:/" + dataSourceName);
546                 } catch (Exception e) {}  
547
548                 if (ds == null) {
549                 ds = AuthN.getDataSource();
550                 }
551                 
552             if (ds == null) {
553                 throw new IllegalArgumentException("datasource not found: " + dataSourceName);
554             }
555             
556             conn = ds.getConnection();
557             if (conn == null) {
558                 conn = AuthN.getDataSource().getConnection();  //FIXME:REM - This is the result of some type of JNDI mess.  Should try to solve this problem and clean up this code.
559             }
560             
561             return conn;
562             
563         } catch (NamingException ex) {
564             AccountException ae = new AccountException("Error looking up DataSource from: " + dataSourceName);
565             ae.initCause(ex);
566             throw ae;
567         } finally {
568             if (ctx != null) {
569                 try {
570                     ctx.close();
571                 } catch (Exception e) {
572                         e.printStackTrace();  // We should be using a logger here instead.
573                 }
574             }
575         }
576
577     }
578
579     /**
580      * @return the datasourceName
581      */
582     public String getDataSourceName() {
583         return datasourceName;
584     }
585
586     /**
587      * @return the principalQuery
588      */
589     public String getPrincipalQuery() {
590         return principalsQuery;
591     }
592
593     /**
594      * @param principalQuery the principalQuery to set
595      */
596     public void setPrincipalQuery(String principalQuery) {
597         this.principalsQuery = principalQuery;
598     }
599
600     /**
601      * @return the roleQuery
602      */
603     public String getRoleQuery() {
604         return rolesQuery;
605     }
606
607     /**
608      * @param roleQuery the roleQuery to set
609      */
610     public void setRoleQuery(String roleQuery) {
611         this.rolesQuery = roleQuery;
612     }
613
614     /**
615      * @return the tenantQuery
616      */
617     public String getTenantQuery(boolean includeDisabledTenants) {
618         return includeDisabledTenants?tenantsQueryWithDisabled:tenantsQueryNoDisabled;
619     }
620
621     /**
622      * @param tenantQuery the tenantQuery to set
623     public void setTenantQuery(String tenantQuery) {
624         this.tenantsQueryNoDisabled = tenantQuery;
625     }
626      */
627     
628     /*
629      * This method crawls the exception chain looking for network related exceptions and
630      * returns 'true' if it finds one.
631      */
632         public static boolean exceptionChainContainsNetworkError(Throwable exceptionChain) {
633                 boolean result = false;
634                 Throwable cause = exceptionChain;
635
636                 while (cause != null) {
637                         if (isExceptionNetworkRelated(cause) == true) {
638                                 result = true;
639                                 break;
640                         }
641                         
642                         cause = cause.getCause();
643                 }
644
645                 return result;
646         }
647         
648         /*
649          * Return 'true' if the exception is in the "java.net" package.
650          */
651         private static boolean isExceptionNetworkRelated(Throwable cause) {
652                 boolean result = false;
653
654                 String className = cause.getClass().getCanonicalName();
655                 if (className.contains("java.net") == true) {
656                         result = true;
657                 }
658
659                 return result;
660         }
661
662         @Override
663         public String getSalt(String username) throws AccountException {
664         String salt = null;
665         Connection conn = null;
666         PreparedStatement ps = null;
667         ResultSet rs = null;
668         try {
669             conn = getConnection();
670             // Get the salt
671             if (logger.isDebugEnabled()) {
672                 logger.debug("Executing query: " + saltQuery + ", with username: " + username);
673             }
674             ps = conn.prepareStatement(saltQuery);
675             ps.setString(1, username);
676             rs = ps.executeQuery();
677             if (rs.next() == false) {
678                 if (logger.isDebugEnabled()) {
679                     logger.debug(saltQuery + " returned no matches from db");
680                 }
681                 throw new AccountNotFoundException("No matching username found");
682             }
683
684             salt = rs.getString(1);
685         } catch (SQLException ex) {
686                 // Assuming PostgreSQL
687             if (PSQLState.UNDEFINED_COLUMN.getState().equals(ex.getSQLState())) {
688                 String msg = "'USERS' table is missing 'salt' column for password encyrption.  Assuming existing passwords are unsalted.";
689                 logger.warn(msg);
690             } else {
691                 AccountException ae = new AccountException("Authentication query failed: " + ex.getLocalizedMessage());
692                 ae.initCause(ex);
693                 throw ae;
694             }
695         } catch (AccountNotFoundException ex) {
696             throw ex;
697         } catch (Exception ex) {
698             AccountException ae = new AccountException("Unknown Exception");
699             ae.initCause(ex);
700             throw ae;
701         } finally {
702             if (rs != null) {
703                 try {
704                     rs.close();
705                 } catch (SQLException e) {
706                 }
707             }
708             if (ps != null) {
709                 try {
710                     ps.close();
711                 } catch (SQLException e) {
712                 }
713             }
714             if (conn != null) {
715                 try {
716                     conn.close();
717                 } catch (SQLException ex) {
718                 }
719             }
720         }
721         
722         return salt;
723     }
724
725     @Override
726     public boolean isRequireSSO(String username) throws AccountException {
727         Boolean requireSSO = null;
728         Connection conn = null;
729         PreparedStatement ps = null;
730         ResultSet rs = null;
731
732         try {
733             conn = getConnection();
734
735             if (logger.isDebugEnabled()) {
736                 logger.debug("Executing query: " + requireSSOQuery + ", with username: " + username);
737             }
738
739             ps = conn.prepareStatement(requireSSOQuery);
740
741             ps.setString(1, username);
742
743             rs = ps.executeQuery();
744
745             if (rs.next() == false) {
746                 if (logger.isDebugEnabled()) {
747                     logger.debug(requireSSOQuery + " returned no matches from db");
748                 }
749
750                 throw new AccountNotFoundException("No matching username found");
751             }
752
753             requireSSO = rs.getBoolean(1);
754         } catch (SQLException ex) {
755             if (logger.isTraceEnabled() == true) {
756                 logger.error("Could not open database to read AuthN tables.", ex);
757             }
758
759             AccountException ae = new AccountException("Authentication query failed: " + ex.getLocalizedMessage());
760
761             ae.initCause(ex);
762             
763             throw ae;
764         } catch (AccountNotFoundException ex) {
765             throw ex;
766         } catch (Exception ex) {
767             AccountException ae = new AccountException("Unknown Exception");
768
769             ae.initCause(ex);
770             
771             throw ae;
772         } finally {
773             if (rs != null) {
774                 try {
775                     rs.close();
776                 } catch (SQLException e) {
777                 }
778             }
779
780             if (ps != null) {
781                 try {
782                     ps.close();
783                 } catch (SQLException e) {
784                 }
785             }
786
787             if (conn != null) {
788                 try {
789                     conn.close();
790                 } catch (SQLException ex) {
791                 }
792             }
793         }
794
795         return requireSSO;
796     }
797 }