|
AccessControllerImpl |
|
1 /* 2 * AccessControllerImpl.java 3 * 4 * Copyright (c) 1998-2001, The University of Sheffield. 5 * 6 * This file is part of GATE (see http://gate.ac.uk/), and is free 7 * software, licenced under the GNU Library General Public License, 8 * Version 2, June 1991 (in the distribution as file licence.html, 9 * and also available at http://gate.ac.uk/gate/licence.html). 10 * 11 * Marin Dimitrov, 19/Sep/2001 12 * 13 * $Id: AccessControllerImpl.java,v 1.50 2003/02/20 18:40:16 valyt Exp $ 14 */ 15 16 package gate.security; 17 18 import java.util.*; 19 import java.sql.*; 20 import java.net.*; 21 22 import junit.framework.*; 23 24 import gate.*; 25 import gate.event.*; 26 import gate.persist.*; 27 import gate.util.MethodNotImplementedException; 28 29 30 public class AccessControllerImpl 31 implements AccessController, ObjectModificationListener { 32 33 public static final int DEFAULT_SESSION_TIMEOUT_MIN = 4*60; 34 35 public static final int LOGIN_OK = 1; 36 public static final int LOGIN_FAILED = 2; 37 38 private static long MY_VERY_SECRET_CONSTANT; 39 private static final int RANDOM_MAX = 1024; 40 41 private HashMap sessions; 42 private HashMap sessionLastUsed; 43 private HashMap sessionTimeouts; 44 45 private Connection jdbcConn; 46 private String jdbcURL; 47 private String jdbcSchema; 48 protected int dbType; 49 50 private HashMap usersByID; 51 private HashMap usersByName; 52 53 private HashMap groupsByID; 54 private HashMap groupsByName; 55 56 private static Random r; 57 private boolean isPooled; 58 59 private int refCnt; 60 61 /** --- */ 62 private Vector omModificationListeners; 63 /** --- */ 64 private Vector omCreationListeners; 65 /** --- */ 66 private Vector omDeletionListeners; 67 68 69 static { 70 r = new Random(); 71 MY_VERY_SECRET_CONSTANT = r.nextInt(RANDOM_MAX) * r.nextInt(RANDOM_MAX) 72 + Math.round(Math.PI * Math.E); 73 } 74 75 /** --- */ 76 public AccessControllerImpl(String jdbcURL) { 77 78 Assert.assertNotNull(jdbcURL); 79 80 this.refCnt = 0; 81 this.jdbcURL = jdbcURL; 82 this.jdbcSchema = DBHelper.getSchemaPrefix(this.jdbcURL); 83 this.dbType = DBHelper.getDatabaseType(this.jdbcURL); 84 85 Assert.assertNotNull(this.jdbcSchema); 86 Assert.assertTrue(this.dbType == DBHelper.ORACLE_DB || 87 this.dbType == DBHelper.POSTGRES_DB); 88 89 sessions = new HashMap(); 90 sessionLastUsed = new HashMap(); 91 sessionTimeouts = new HashMap(); 92 93 usersByID = new HashMap(); 94 usersByName= new HashMap(); 95 96 groupsByID = new HashMap(); 97 groupsByName = new HashMap(); 98 99 this.omModificationListeners = new Vector(); 100 this.omCreationListeners = new Vector(); 101 this.omDeletionListeners = new Vector(); 102 } 103 104 /** --- */ 105 public void open() 106 throws PersistenceException{ 107 108 synchronized(this) { 109 if (refCnt++ == 0) { 110 //open connection 111 try { 112 //1. get connection to the database 113 jdbcConn = DBHelper.connect(this.jdbcURL); 114 115 Assert.assertNotNull(jdbcConn); 116 117 //2. initialize group/user collections 118 //init, i.e. read users and groups from DB 119 init(); 120 } 121 catch(SQLException sqle) { 122 throw new PersistenceException("could not get DB connection ["+ sqle.getMessage() +"]"); 123 } 124 catch(ClassNotFoundException clse) { 125 throw new PersistenceException("cannot locate JDBC driver ["+ clse.getMessage() +"]"); 126 } 127 } 128 } 129 130 131 } 132 133 /** --- */ 134 public void close() 135 throws PersistenceException{ 136 137 if (--this.refCnt == 0) { 138 139 //0. Invalidate all sessions 140 this.sessions.clear(); 141 this.sessionLastUsed.clear(); 142 this.sessionTimeouts.clear(); 143 144 //1. deregister self as listener for groups 145 Set groupMappings = this.groupsByName.entrySet(); 146 Iterator itGroups = groupMappings.iterator(); 147 148 while (itGroups.hasNext()) { 149 Map.Entry mapEntry = (Map.Entry)itGroups.next(); 150 GroupImpl grp = (GroupImpl)mapEntry.getValue(); 151 grp.unregisterObjectModificationListener(this, 152 ObjectModificationEvent.OBJECT_MODIFIED); 153 } 154 155 //1.1. deregister self as listener for users 156 Set userMappings = this.usersByName.entrySet(); 157 Iterator itUsers = userMappings.iterator(); 158 159 while (itUsers.hasNext()) { 160 Map.Entry mapEntry = (Map.Entry)itUsers.next(); 161 UserImpl usr = (UserImpl)mapEntry.getValue(); 162 usr.unregisterObjectModificationListener(this, 163 ObjectModificationEvent.OBJECT_MODIFIED); 164 } 165 166 //1.2 release all listeners registered for this object 167 this.omCreationListeners.removeAllElements(); 168 this.omDeletionListeners.removeAllElements(); 169 this.omModificationListeners.removeAllElements(); 170 171 //2. delete all groups/users collections 172 this.groupsByID.clear(); 173 this.groupsByName.clear(); 174 this.usersByID.clear(); 175 this.groupsByName.clear(); 176 177 //3.close connection (if not pooled) 178 try { 179 if (false == this.isPooled) { 180 this.jdbcConn.close(); 181 } 182 } 183 catch(SQLException sqle) { 184 throw new PersistenceException("can't close connection to DB:["+ 185 sqle.getMessage()+"]"); 186 } 187 } 188 } 189 190 /** --- */ 191 public Group findGroup(String name) 192 throws PersistenceException,SecurityException{ 193 194 Group grp = (Group)this.groupsByName.get(name); 195 196 if (null == grp) { 197 throw new SecurityException("No such group"); 198 } 199 200 return grp; 201 } 202 203 /** --- */ 204 public Group findGroup(Long id) 205 throws PersistenceException,SecurityException { 206 207 Group grp = (Group)this.groupsByID.get(id); 208 209 if (null == grp) { 210 throw new SecurityException("No such group"); 211 } 212 213 return grp; 214 } 215 216 /** --- */ 217 public User findUser(String name) 218 throws PersistenceException,SecurityException { 219 220 User usr = (User)this.usersByName.get(name); 221 222 if (null == usr) { 223 throw new SecurityException("No such user (" + name + ")"); 224 } 225 226 return usr; 227 } 228 229 /** --- */ 230 public User findUser(Long id) 231 throws PersistenceException,SecurityException { 232 233 User usr = (User)this.usersByID.get(id); 234 235 if (null == usr) { 236 throw new SecurityException("No such user"); 237 } 238 239 return usr; 240 } 241 242 /** --- */ 243 public Session findSession(Long id) 244 throws SecurityException { 245 246 Session s = (Session)this.sessions.get(id); 247 248 if (null==s) { 249 throw new SecurityException("No such session ID!"); 250 } 251 252 return s; 253 } 254 255 /** --- */ 256 public Group createGroup(String name,Session s) 257 throws PersistenceException, SecurityException { 258 259 Assert.assertNotNull(name); 260 261 //-1. check session 262 if (false == isValidSession(s)) { 263 throw new SecurityException("invalid session supplied"); 264 } 265 266 //0. check privileges 267 if (false == s.isPrivilegedSession()) { 268 throw new SecurityException("insufficient privileges"); 269 } 270 271 272 //1. create group in DB 273 CallableStatement stmt = null; 274 PreparedStatement pstmt = null; 275 ResultSet rs = null; 276 Long new_id; 277 278 //Oracle / Postgres ? 279 280 if (this.dbType == DBHelper.ORACLE_DB) { 281 282 try { 283 stmt = this.jdbcConn.prepareCall( 284 "{ call "+Gate.DB_OWNER+".security.create_group(?,?)} "); 285 stmt.setString(1,name); 286 //numbers generated from Oracle sequences are BIGINT 287 stmt.registerOutParameter(2,java.sql.Types.BIGINT); 288 stmt.execute(); 289 new_id = new Long(stmt.getLong(2)); 290 } 291 catch(SQLException sqle) { 292 293 //check for more specifi exceptions 294 switch(sqle.getErrorCode()) { 295 296 case DBHelper.X_ORACLE_DUPLICATE_GROUP_NAME: 297 throw new PersistenceException( 298 "can't create a group in DB, name is not unique: [" 299 + sqle.getMessage()+"]"); 300 301 default: 302 throw new PersistenceException( 303 "can't create a group in DB: ["+ sqle.getMessage()+"]"); 304 } 305 } 306 finally { 307 DBHelper.cleanup(stmt); 308 } 309 } 310 else if (this.dbType == DBHelper.POSTGRES_DB) { 311 try { 312 String sql = "select security_create_group(?) "; 313 pstmt = this.jdbcConn.prepareStatement(sql); 314 pstmt.setString(1,name); 315 pstmt.execute(); 316 rs = pstmt.getResultSet(); 317 318 if (false == rs.next()) { 319 throw new PersistenceException("empty resultset"); 320 } 321 322 new_id = new Long(rs.getLong(1)); 323 } 324 catch(SQLException sqle) { 325 326 //check for more specifi exceptions 327 switch(sqle.getErrorCode()) { 328 329 case DBHelper.X_ORACLE_DUPLICATE_GROUP_NAME: 330 throw new PersistenceException( 331 "can't create a group in DB, name is not unique: [" 332 + sqle.getMessage()+"]"); 333 334 default: 335 throw new PersistenceException( 336 "can't create a group in DB: ["+ sqle.getMessage()+"]"); 337 } 338 } 339 finally { 340 DBHelper.cleanup(rs); 341 DBHelper.cleanup(pstmt); 342 } 343 344 } 345 else { 346 throw new IllegalArgumentException(); 347 } 348 349 //2. create GroupImpl for the new group and 350 // users list is empty 351 GroupImpl grp = new GroupImpl(new_id,name,new Vector(),this,this.jdbcConn); 352 353 //3. register as objectModification listener for this group 354 //we care only about name changes 355 grp.registerObjectModificationListener(this,ObjectModificationEvent.OBJECT_MODIFIED); 356 357 //4.put in collections 358 this.groupsByID.put(new_id,grp); 359 this.groupsByName.put(name,grp); 360 361 return grp; 362 } 363 364 /** --- */ 365 public void deleteGroup(Long id, Session s) 366 throws PersistenceException,SecurityException { 367 368 Group grp = (Group)this.groupsByID.get(id); 369 if (null == grp) { 370 throw new SecurityException("incorrect group id supplied ( id = ["+id+"])"); 371 } 372 373 //delegate 374 deleteGroup(grp,s); 375 } 376 377 /** --- */ 378 public void deleteGroup(Group grp, Session s) 379 throws PersistenceException,SecurityException { 380 381 //1. check session 382 if (false == isValidSession(s)) { 383 throw new SecurityException("invalid session supplied"); 384 } 385 386 //2. check privileges 387 if (false == s.isPrivilegedSession()) { 388 throw new SecurityException("insufficient privileges"); 389 } 390 391 //3. delete in DB 392 CallableStatement stmt = null; 393 PreparedStatement pstmt = null; 394 395 //Oracle /Postgres ? 396 if (this.dbType == DBHelper.ORACLE_DB) { 397 398 try { 399 stmt = this.jdbcConn.prepareCall( 400 "{ call "+Gate.DB_OWNER+".security.delete_group(?) } "); 401 stmt.setLong(1,grp.getID().longValue()); 402 stmt.execute(); 403 } 404 catch(SQLException sqle) { 405 //check for more specific exceptions 406 switch(sqle.getErrorCode()) { 407 408 case DBHelper.X_ORACLE_GROUP_OWNS_RESOURCES: 409 throw new PersistenceException( 410 "can't delete a group from DB, the group owns LR(s): [" 411 + sqle.getMessage()+"]"); 412 413 default: 414 throw new PersistenceException( 415 "can't delete a group from DB: ["+ sqle.getMessage()+"]"); 416 } 417 } 418 finally { 419 DBHelper.cleanup(stmt); 420 } 421 } 422 423 else if (this.dbType == DBHelper.POSTGRES_DB) { 424 425 try { 426 String sql = "select security_delete_group(?)"; 427 pstmt = this.jdbcConn.prepareStatement(sql); 428 pstmt.setLong(1,grp.getID().longValue()); 429 pstmt.execute(); 430 } 431 catch(SQLException sqle) { 432 //check for more specific exceptions 433 switch(sqle.getErrorCode()) { 434 435 case DBHelper.X_ORACLE_GROUP_OWNS_RESOURCES: 436 throw new PersistenceException( 437 "can't delete a group from DB, the group owns LR(s): [" 438 + sqle.getMessage()+"]"); 439 440 default: 441 throw new PersistenceException( 442 "can't delete a group from DB: ["+ sqle.getMessage()+"]"); 443 } 444 } 445 finally { 446 DBHelper.cleanup(pstmt); 447 } 448 449 } 450 else { 451 throw new IllegalArgumentException(); 452 } 453 454 455 //4. delete from collections 456 this.groupsByID.remove(grp.getID()); 457 this.groupsByName.remove(grp.getName()); 458 459 //5. notify all other listeners 460 //this one is tricky - sent OBJECT_DELETED event to all who care 461 //but note that the SOURCE is not us but the object being deleted 462 ObjectModificationEvent e = new ObjectModificationEvent( 463 grp, 464 ObjectModificationEvent.OBJECT_DELETED, 465 0); 466 467 fireObjectDeletedEvent(e); 468 469 //6. this one is tricky: invalidate all sessions 470 //that are for user logged in as members of this group 471 Set sessionMappings = this.sessions.entrySet(); 472 Iterator itSessions = sessionMappings.iterator(); 473 474 //6.1 to avoid ConcurrentModificationException store the sessions 475 //found in a temp vector 476 Vector sessionsToDelete = new Vector(); 477 while (itSessions.hasNext()) { 478 Map.Entry mapEntry = (Map.Entry)itSessions.next(); 479 SessionImpl ses = (SessionImpl)mapEntry.getValue(); 480 if (ses.getGroup().equals(grp)) { 481 //logout(ses); --> this will cause ConcurrentModificationException 482 sessionsToDelete.add(ses); 483 } 484 } 485 //6.2 now delete sessions 486 for (int i=0; i< sessionsToDelete.size(); i++) { 487 Session ses = (Session)sessionsToDelete.elementAt(i); 488 logout(ses); 489 } 490 491 } 492 493 /** --- */ 494 public User createUser(String name, String passwd,Session s) 495 throws PersistenceException,SecurityException { 496 497 Assert.assertNotNull(name); 498 499 //-1. check session 500 if (false == isValidSession(s)) { 501 throw new SecurityException("invalid session supplied"); 502 } 503 504 //0. check privileges 505 if (false == s.isPrivilegedSession()) { 506 throw new SecurityException("insufficient privileges"); 507 } 508 509 //1. create user in DB 510 CallableStatement stmt = null; 511 PreparedStatement pstmt = null; 512 ResultSet rs = null; 513 514 Long new_id; 515 516 if (this.dbType == DBHelper.ORACLE_DB) { 517 518 try { 519 stmt = this.jdbcConn.prepareCall( 520 "{ call "+Gate.DB_OWNER+".security.create_user(?,?,?)} "); 521 stmt.setString(1,name); 522 stmt.setString(2,passwd); 523 //numbers generated from Oracle sequences are BIGINT 524 stmt.registerOutParameter(3,java.sql.Types.BIGINT); 525 stmt.execute(); 526 new_id = new Long(stmt.getLong(3)); 527 } 528 catch(SQLException sqle) { 529 //check for more specific exceptions 530 switch(sqle.getErrorCode()) { 531 532 case DBHelper.X_ORACLE_DUPLICATE_USER_NAME: 533 throw new PersistenceException( 534 "can't create a user in DB, name is not unique: [" 535 + sqle.getMessage()+"]"); 536 default: 537 throw new PersistenceException( 538 "can't create a user in DB: ["+ sqle.getMessage()+"]"); 539 } 540 } 541 finally { 542 DBHelper.cleanup(stmt); 543 } 544 } 545 546 else if (this.dbType == DBHelper.POSTGRES_DB) { 547 548 try { 549 String sql = "select security_create_user(?,?) "; 550 pstmt = this.jdbcConn.prepareStatement(sql); 551 pstmt.setString(1,name); 552 pstmt.setString(2,passwd); 553 pstmt.execute(); 554 rs = pstmt.getResultSet(); 555 556 if (false == rs.next()) { 557 throw new PersistenceException("empty resultset"); 558 } 559 560 new_id = new Long(rs.getLong(1)); 561 } 562 catch(SQLException sqle) { 563 //check for more specific exceptions 564 switch(sqle.getErrorCode()) { 565 566 case DBHelper.X_ORACLE_DUPLICATE_USER_NAME: 567 throw new PersistenceException( 568 "can't create a user in DB, name is not unique: [" 569 + sqle.getMessage()+"]"); 570 default: 571 throw new PersistenceException( 572 "can't create a user in DB: ["+ sqle.getMessage()+"]"); 573 } 574 } 575 finally { 576 DBHelper.cleanup(rs); 577 DBHelper.cleanup(pstmt); 578 } 579 580 } 581 582 else { 583 throw new IllegalArgumentException(); 584 } 585 586 //2. create UserImpl for the new user 587 // groups list is empty 588 UserImpl usr = new UserImpl(new_id,name,new Vector(),this,this.jdbcConn); 589 590 //3. register as objectModification listener for this user 591 //we care only about user changing name 592 usr.registerObjectModificationListener(this,ObjectModificationEvent.OBJECT_MODIFIED); 593 594 //4. put in collections 595 this.usersByID.put(new_id,usr); 596 this.usersByName.put(name,usr); 597 598 return usr; 599 } 600 601 /** --- */ 602 public void deleteUser(User usr, Session s) 603 throws PersistenceException,SecurityException { 604 605 //1. check session 606 if (false == isValidSession(s)) { 607 throw new SecurityException("invalid session supplied"); 608 } 609 610 //2. check privileges 611 if (false == s.isPrivilegedSession()) { 612 throw new SecurityException("insufficient privileges"); 613 } 614 615 //3. delete in DB 616 CallableStatement cstmt = null; 617 PreparedStatement pstmt = null; 618 619 if (this.dbType == DBHelper.ORACLE_DB) { 620 621 try { 622 cstmt = this.jdbcConn.prepareCall( 623 "{ call "+Gate.DB_OWNER+".security.delete_user(?) } "); 624 cstmt.setLong(1,usr.getID().longValue()); 625 cstmt.execute(); 626 } 627 catch(SQLException sqle) { 628 switch(sqle.getErrorCode()) { 629 630 case DBHelper.X_ORACLE_USER_OWNS_RESOURCES: 631 throw new PersistenceException( 632 "can't delete user from DB, the user owns LR(s): [" 633 + sqle.getMessage()+"]"); 634 default: 635 throw new PersistenceException( 636 "can't delete user from DB: ["+ sqle.getMessage()+"]"); 637 } 638 } 639 finally { 640 DBHelper.cleanup(cstmt); 641 } 642 } 643 644 else if (this.dbType == DBHelper.POSTGRES_DB) { 645 646 try { 647 String sql = "select security_delete_user(?) "; 648 pstmt = this.jdbcConn.prepareStatement(sql); 649 pstmt.setLong(1,usr.getID().longValue()); 650 pstmt.execute(); 651 } 652 catch(SQLException sqle) { 653 switch(sqle.getErrorCode()) { 654 655 case DBHelper.X_ORACLE_USER_OWNS_RESOURCES: 656 throw new PersistenceException( 657 "can't delete user from DB, the user owns LR(s): [" 658 + sqle.getMessage()+"]"); 659 default: 660 throw new PersistenceException( 661 "can't delete user from DB: ["+ sqle.getMessage()+"]"); 662 } 663 } 664 finally { 665 DBHelper.cleanup(pstmt); 666 } 667 668 } 669 670 else { 671 throw new IllegalArgumentException(); 672 } 673 674 675 //4. delete from collections 676 this.usersByID.remove(usr.getID()); 677 this.usersByName.remove(usr.getName()); 678 679 //6. notify all other listeners 680 //this one is tricky - sent OBJECT_DELETED event to all who care 681 //but note that the SOURCE is not us but the object being deleted 682 ObjectModificationEvent e = new ObjectModificationEvent( 683 usr, 684 ObjectModificationEvent.OBJECT_DELETED, 685 0); 686 687 fireObjectDeletedEvent(e); 688 689 //7. this one is tricky: invalidate all sessions for the user 690 Set sessionMappings = this.sessions.entrySet(); 691 Iterator itSessions = sessionMappings.iterator(); 692 693 //7.1 to avoid ConcurrentModificationException store the sessions 694 //found in a temp vector 695 Vector sessionsToDelete = new Vector(); 696 while (itSessions.hasNext()) { 697 Map.Entry mapEntry = (Map.Entry)itSessions.next(); 698 SessionImpl ses = (SessionImpl)mapEntry.getValue(); 699 if (ses.getUser().equals(usr)) { 700 //logout(ses); --> this will cause ConcurrentModificationException 701 sessionsToDelete.add(ses); 702 } 703 } 704 //7.2 now delete sessions 705 for (int i=0; i< sessionsToDelete.size(); i++) { 706 Session ses = (Session)sessionsToDelete.elementAt(i); 707 logout(ses); 708 } 709 710 } 711 712 713 /** --- */ 714 public void deleteUser(Long id, Session s) 715 throws PersistenceException,SecurityException { 716 717 User usr = (User)usersByID.get(id); 718 if (null == usr) { 719 throw new SecurityException("incorrect user id supplied ( id = ["+id+"])"); 720 } 721 722 //delegate 723 deleteUser(usr,s); 724 } 725 726 /** --- */ 727 public Session login(String usr_name, String passwd,Long prefGroupID) 728 throws PersistenceException,SecurityException { 729 730 //1. check the user locally 731 User usr = (User)this.usersByName.get(usr_name); 732 if (null == usr) { 733 throw new SecurityException("no such user (username=["+usr_name+"])"); 734 } 735 736 //2. check group localy 737 Group grp = (Group)this.groupsByID.get(prefGroupID); 738 if (null == grp) { 739 throw new SecurityException("no such group (id=["+prefGroupID+"])"); 740 } 741 742 //2. check user/pass in DB 743 CallableStatement cstmt = null; 744 PreparedStatement pstmt = null; 745 ResultSet rs = null; 746 747 boolean isPrivilegedUser = false; 748 749 if (this.dbType == DBHelper.ORACLE_DB) { 750 751 try { 752 cstmt = this.jdbcConn.prepareCall( 753 "{ call "+Gate.DB_OWNER+".security.login(?,?,?,?)} "); 754 cstmt.setString(1,usr_name); 755 cstmt.setString(2,passwd); 756 cstmt.setLong(3,prefGroupID.longValue()); 757 cstmt.registerOutParameter(4,java.sql.Types.NUMERIC); 758 cstmt.execute(); 759 isPrivilegedUser = (cstmt.getInt(4) == DBHelper.FALSE ? false : true ); 760 } 761 catch(SQLException sqle) { 762 switch(sqle.getErrorCode()) 763 { 764 case DBHelper.X_ORACLE_INVALID_USER_NAME : 765 throw new SecurityException("Login failed: incorrect user"); 766 case DBHelper.X_ORACLE_INVALID_USER_PASS : 767 throw new SecurityException("Login failed: incorrect password"); 768 case DBHelper.X_ORACLE_INVALID_USER_GROUP : 769 throw new SecurityException("Login failed: incorrect group"); 770 default: 771 throw new PersistenceException("can't login user, DB error is: ["+ 772 sqle.getMessage()+"]"); 773 } 774 } 775 finally { 776 DBHelper.cleanup(cstmt); 777 } 778 } 779 780 else if (this.dbType == DBHelper.POSTGRES_DB) { 781 782 try { 783 String sql = "select security_login(?,?,?) "; 784 pstmt = this.jdbcConn.prepareStatement(sql); 785 pstmt.setString(1,usr_name); 786 pstmt.setString(2,passwd); 787 pstmt.setLong(3,prefGroupID.longValue()); 788 pstmt.execute(); 789 rs = pstmt.getResultSet(); 790 791 if (false == rs.next()) { 792 throw new PersistenceException("empty resultset"); 793 } 794 795 isPrivilegedUser = rs.getBoolean(1); 796 } 797 catch(SQLException sqle) { 798 switch(sqle.getErrorCode()) 799 { 800 case DBHelper.X_ORACLE_INVALID_USER_NAME : 801 throw new SecurityException("Login failed: incorrect user"); 802 case DBHelper.X_ORACLE_INVALID_USER_PASS : 803 throw new SecurityException("Login failed: incorrect password"); 804 case DBHelper.X_ORACLE_INVALID_USER_GROUP : 805 throw new SecurityException("Login failed: incorrect group"); 806 default: 807 throw new PersistenceException("can't login user, DB error is: ["+ 808 sqle.getMessage()+"]"); 809 } 810 } 811 finally { 812 DBHelper.cleanup(rs); 813 DBHelper.cleanup(pstmt); 814 } 815 } 816 817 else { 818 throw new IllegalArgumentException(); 819 } 820 821 //3. create a Session and set User/Group 822 Long sessionID = createSessionID(); 823 while (this.sessions.containsKey(sessionID)) { 824 sessionID = createSessionID(); 825 } 826 827 SessionImpl s = new SessionImpl(sessionID, 828 usr, 829 grp, 830 DEFAULT_SESSION_TIMEOUT_MIN, 831 isPrivilegedUser); 832 833 //4. add session to session collections 834 this.sessions.put(s.getID(),s); 835 836 //5. set the session timeouts and keep alives 837 this.sessionTimeouts.put(sessionID,new Long(DEFAULT_SESSION_TIMEOUT_MIN)); 838 touchSession(s); //this one changes the keepAlive time 839 840 return s; 841 } 842 843 /** --- */ 844 public void logout(Session s) 845 throws SecurityException { 846 847 Assert.assertNotNull(s); 848 Long SID = s.getID(); 849 850 //1.sessions 851 Session removedSession = (Session)this.sessions.remove(SID); 852 Assert.assertNotNull(removedSession); 853 854 //2. keep alives 855 Object lastUsed = this.sessionLastUsed.remove(SID); 856 Assert.assertNotNull(lastUsed); 857 858 //3. timeouts 859 Object timeout = this.sessionTimeouts.remove(SID); 860 Assert.assertNotNull(timeout); 861 } 862 863 /** --- */ 864 public void setSessionTimeout(Session s, int timeoutMins) 865 throws SecurityException { 866 867 this.sessionTimeouts.put(s.getID(),new Long(timeoutMins)); 868 } 869 870 /** --- */ 871 public boolean isValidSession(Session s) { 872 873 //1. do we have such session? 874 if (false == this.sessions.containsKey(s.getID())) { 875 return false; 876 } 877 878 //2. has it expired meanwhile? 879 Assert.assertNotNull(this.sessionLastUsed.get(s.getID())); 880 881 long lastUsedMS = ((Long)this.sessionLastUsed.get(s.getID())).longValue(); 882 long sessTimeoutMin = ((Long)this.sessionTimeouts.get(s.getID())).longValue(); 883 long currTimeMS = System.currentTimeMillis(); 884 //timeout is in minutes 885 long lastUsedMin = (currTimeMS-lastUsedMS)/(1000*60); 886 887 if (lastUsedMin > sessTimeoutMin) { 888 //oops, session expired 889 //invalidate it and fail 890 try { 891 logout(s); 892 } 893 catch(SecurityException se) { 894 //well, this can happen only if logout() was called together 895 //with isValidSesion() but the possibility it too low to care 896 //and synchronize access 897 ; 898 } 899 900 return false; 901 } 902 903 //everything ok 904 //touch session 905 touchSession(s); 906 907 return true; 908 } 909 910 /** -- */ 911 public List listGroups() 912 throws PersistenceException { 913 914 //1. read all groups from DB 915 Statement stmt = null; 916 ResultSet rs = null; 917 String sql; 918 Vector result = new Vector(); 919 920 try { 921 stmt = this.jdbcConn.createStatement(); 922 923 //1.1 read groups 924 sql = " SELECT grp_name "+ 925 " FROM "+this.jdbcSchema+"t_group "+ 926 " ORDER BY grp_name ASC"; 927 rs = stmt.executeQuery(sql); 928 929 while (rs.next()) { 930 //access by index is faster 931 //first column index is 1 932 String grp_name = rs.getString(1); 933 result.add(grp_name); 934 } 935 936 return result; 937 } 938 catch (SQLException sqle) { 939 throw new PersistenceException("cannot read groups from DB :["+ 940 sqle.getMessage() +"]"); 941 } 942 finally { 943 DBHelper.cleanup(rs); 944 DBHelper.cleanup(stmt); 945 } 946 } 947 948 /** -- */ 949 public List listUsers() 950 throws PersistenceException { 951 952 //1. read all users from DB 953 Statement stmt = null; 954 ResultSet rs = null; 955 String sql; 956 Vector result = new Vector(); 957 958 try { 959 stmt = this.jdbcConn.createStatement(); 960 961 //1.1 read groups 962 sql = " SELECT usr_login "+ 963 " FROM "+this.jdbcSchema+"t_user "+ 964 " ORDER BY usr_login DESC"; 965 rs = stmt.executeQuery(sql); 966 967 while (rs.next()) { 968 //access by index is faster 969 //first column index is 1 970 String usr_name = rs.getString(1); 971 result.add(usr_name); 972 } 973 974 return result; 975 } 976 catch (SQLException sqle) { 977 throw new PersistenceException("cannot read groups from DB :["+ 978 sqle.getMessage() +"]"); 979 } 980 finally { 981 DBHelper.cleanup(rs); 982 DBHelper.cleanup(stmt); 983 } 984 } 985 986 987 988 /* private methods */ 989 990 private void touchSession(Session s) { 991 992 this.sessionLastUsed.put(s.getID(), new Long(System.currentTimeMillis())); 993 } 994 995 996 private Long createSessionID() { 997 998 //need a hint? 999 return new Long(((System.currentTimeMillis() << 16) >> 16)* 1000 (r.nextInt(RANDOM_MAX))* 1001 Runtime.getRuntime().freeMemory()* 1002 MY_VERY_SECRET_CONSTANT); 1003 } 1004 1005 1006 private boolean canDeleteGroup(Group grp) 1007 throws PersistenceException, SecurityException{ 1008 1009 //1. check group localy 1010 if (false == this.groupsByID.containsValue(grp)) { 1011 throw new SecurityException("no such group (id=["+grp.getID()+"])"); 1012 } 1013 1014 //2. check DB 1015 CallableStatement stmt = null; 1016 PreparedStatement pstmt = null; 1017 ResultSet rs = null; 1018 1019 if (this.dbType == DBHelper.ORACLE_DB) { 1020 1021 try { 1022 stmt = this.jdbcConn.prepareCall( 1023 "{ ? = call "+Gate.DB_OWNER+".security.can_delete_group(?) }"); 1024 stmt.registerOutParameter(1,java.sql.Types.INTEGER); 1025 stmt.setLong(2,grp.getID().longValue()); 1026 stmt.execute(); 1027 boolean res = stmt.getBoolean(1); 1028 1029 return res; 1030 } 1031 catch(SQLException sqle) { 1032 throw new PersistenceException("can't perform document checks, DB error is: ["+ 1033 sqle.getMessage()+"]"); 1034 } 1035 finally { 1036 DBHelper.cleanup(stmt); 1037 } 1038 } 1039 1040 else if (this.dbType == DBHelper.POSTGRES_DB) { 1041 1042 try { 1043 String sql = "select security_can_delete_group(?)"; 1044 pstmt = this.jdbcConn.prepareCall(sql); 1045 pstmt.setLong(1,grp.getID().longValue()); 1046 pstmt.execute(); 1047 rs = pstmt.getResultSet(); 1048 1049 if (false == rs.next()) { 1050 throw new PersistenceException("empty resultset"); 1051 } 1052 1053 boolean res = rs.getBoolean(1); 1054 1055 return res; 1056 } 1057 catch(SQLException sqle) { 1058 throw new PersistenceException("can't perform document checks, DB error is: ["+ 1059 sqle.getMessage()+"]"); 1060 } 1061 finally { 1062 DBHelper.cleanup(rs); 1063 DBHelper.cleanup(pstmt); 1064 } 1065 1066 } 1067 1068 else { 1069 throw new IllegalArgumentException(); 1070 } 1071 } 1072 1073 1074 private boolean canDeleteUser(User usr) 1075 throws PersistenceException, SecurityException{ 1076 1077 //1. check group localy 1078 if (false == this.usersByID.containsValue(usr)) { 1079 throw new SecurityException("no such user (id=["+usr.getID()+"])"); 1080 } 1081 1082 //2. check DB 1083 CallableStatement stmt = null; 1084 PreparedStatement pstmt = null; 1085 ResultSet rs = null; 1086 1087 if (this.dbType == DBHelper.ORACLE_DB) { 1088 1089 try { 1090 stmt = this.jdbcConn.prepareCall( 1091 "{ ? = call "+Gate.DB_OWNER+".security.can_delete_user(?) }"); 1092 1093 stmt.registerOutParameter(1,java.sql.Types.INTEGER); 1094 stmt.setLong(2,usr.getID().longValue()); 1095 stmt.execute(); 1096 boolean res = stmt.getBoolean(1); 1097 1098 return res; 1099 } 1100 catch(SQLException sqle) { 1101 throw new PersistenceException("can't perform document checks, DB error is: ["+ 1102 sqle.getMessage()+"]"); 1103 } 1104 finally { 1105 DBHelper.cleanup(stmt); 1106 } 1107 } 1108 1109 else if (this.dbType == DBHelper.POSTGRES_DB) { 1110 1111 try { 1112 String sql = "select security_can_delete_user(?) "; 1113 pstmt = this.jdbcConn.prepareCall(sql); 1114 pstmt.setLong(1,usr.getID().longValue()); 1115 pstmt.execute(); 1116 boolean res = rs.getBoolean(1); 1117 1118 return res; 1119 } 1120 catch(SQLException sqle) { 1121 throw new PersistenceException("can't perform document checks, DB error is: ["+ 1122 sqle.getMessage()+"]"); 1123 } 1124 finally { 1125 DBHelper.cleanup(rs); 1126 DBHelper.cleanup(pstmt); 1127 } 1128 1129 } 1130 1131 else { 1132 throw new IllegalArgumentException(); 1133 } 1134 1135 } 1136 1137 private void init() 1138 throws PersistenceException { 1139 1140 //1. read all groups and users from DB 1141 Statement stmt = null; 1142 ResultSet rs = null; 1143 String sql; 1144 Hashtable groupNames = new Hashtable(); 1145 Hashtable groupMembers= new Hashtable(); 1146 Hashtable userNames= new Hashtable(); 1147 Hashtable userGroups= new Hashtable(); 1148 1149 try { 1150 stmt = this.jdbcConn.createStatement(); 1151 1152 //1.1 read groups 1153 sql = " SELECT grp_id, " + 1154 " grp_name "+ 1155 " FROM "+this.jdbcSchema+"t_group"; 1156 rs = stmt.executeQuery(sql); 1157 1158 1159 1160 while (rs.next()) { 1161 //access by index is faster 1162 //first column index is 1 1163 long grp_id = rs.getLong(1); 1164 String grp_name = rs.getString(2); 1165 groupNames.put(new Long(grp_id),grp_name); 1166 groupMembers.put(new Long(grp_id),new Vector()); 1167 } 1168 DBHelper.cleanup(rs); 1169 1170 1171 //1.2 read users 1172 sql = " SELECT usr_id, " + 1173 " usr_login "+ 1174 " FROM "+this.jdbcSchema+"t_user"; 1175 rs = stmt.executeQuery(sql); 1176 1177 while (rs.next()) { 1178 //access by index is faster 1179 //first column index is 1 1180 long usr_id = rs.getLong(1); 1181 String usr_name = rs.getString(2); 1182 userNames.put(new Long(usr_id),usr_name); 1183 userGroups.put(new Long(usr_id),new Vector()); 1184 } 1185 DBHelper.cleanup(rs); 1186 1187 1188 //1.3 read user/group relations 1189 sql = " SELECT UGRP_GROUP_ID, " + 1190 " UGRP_USER_ID "+ 1191 " FROM "+this.jdbcSchema+"t_user_group " + 1192 " ORDER BY UGRP_GROUP_ID asc"; 1193 rs = stmt.executeQuery(sql); 1194 1195 while (rs.next()) { 1196 //access by index is faster 1197 //first column index is 1 1198 Long grp_id = new Long(rs.getLong(1)); 1199 Long usr_id = new Long(rs.getLong(2)); 1200 1201 //append user to group members list 1202 Vector currMembers = (Vector)groupMembers.get(grp_id); 1203 currMembers.add(usr_id); 1204 1205 Vector currGroups = (Vector)userGroups.get(usr_id); 1206 currGroups.add(grp_id); 1207 } 1208 DBHelper.cleanup(rs); 1209 } 1210 catch(SQLException sqle) { 1211 throw new PersistenceException("DB error is: ["+ 1212 sqle.getMessage()+"]"); 1213 } 1214 finally { 1215 DBHelper.cleanup(rs); 1216 DBHelper.cleanup(stmt); 1217 } 1218 1219 //2. create USerImpl's and GroupImpl's and put them in collections 1220 1221 //2.1 create Groups 1222 Vector toBeInitializedGroups = new Vector(); 1223 1224 Enumeration enGroups = groupNames.keys(); 1225 while (enGroups.hasMoreElements()) { 1226 Long grpId = (Long)enGroups.nextElement(); 1227// Vector grpMembers = (Vector)groupMembers.get(grpId); 1228 String grpName = (String)groupNames.get(grpId); 1229 1230 //note that the Vector with group members is empty 1231 //will beinitalized later (ugly hack for bad desgin) 1232 GroupImpl grp = new GroupImpl(grpId,grpName,new Vector(),this,this.jdbcConn); 1233 //register as listener for thsi group 1234 //we care only about name changes 1235 grp.registerObjectModificationListener(this,ObjectModificationEvent.OBJECT_MODIFIED); 1236 1237 //add to collection 1238 this.groupsByID.put(grp.getID(),grp); 1239 this.groupsByName.put(grp.getName(),grp); 1240 1241 //add to vector of the objects to be initialized 1242 toBeInitializedGroups.add(grp); 1243 } 1244 1245 //2.2 create Users 1246 Vector toBeInitializedUsers = new Vector(); 1247 1248 Enumeration enUsers = userNames.keys(); 1249 while (enUsers.hasMoreElements()) { 1250 Long usrId = (Long)enUsers.nextElement(); 1251// Vector usrGroups = (Vector)userGroups.get(usrId); 1252 String usrName = (String)userNames.get(usrId); 1253 1254 //note that the Vector with user's group is empty 1255 //will be initalized later (ugly hack for bad desgin) 1256 UserImpl usr = new UserImpl(usrId,usrName,new Vector(),this,this.jdbcConn); 1257 //register as listener for thsi user 1258 //we care only about user changing name 1259 usr.registerObjectModificationListener(this,ObjectModificationEvent.OBJECT_MODIFIED); 1260 1261 //add to collection 1262 this.usersByID.put(usr.getID(),usr); 1263 this.usersByName.put(usr.getName(),usr); 1264 1265 //add to vector of the objects to be initialized 1266 toBeInitializedUsers.add(usr); 1267 } 1268 1269 //3. the hack itself: 1270 //all the groups and users are not fully initialized yet 1271 //(the groups/users Vectors are empty) 1272 //initialize them now 1273 1274 //3.1 initialize groups 1275 for (int i=0; i< toBeInitializedGroups.size(); i++) { 1276 GroupImpl grp = (GroupImpl)toBeInitializedGroups.elementAt(i); 1277 grp.setUsers((Vector)groupMembers.get(grp.getID())); 1278 } 1279 1280 //3.2 initialize users 1281 for (int i=0; i< toBeInitializedUsers.size(); i++) { 1282 UserImpl usr = (UserImpl)toBeInitializedUsers.elementAt(i); 1283 usr.setGroups((Vector)userGroups.get(usr.getID())); 1284 } 1285 1286 } 1287 1288 1289 private void fireObjectCreatedEvent(ObjectModificationEvent e) { 1290 1291 //sanity check 1292 if (e.getType() != ObjectModificationEvent.OBJECT_CREATED) { 1293 throw new IllegalArgumentException(); 1294 } 1295 1296 for (int i=0; i< this.omCreationListeners.size(); i++) { 1297 ((ObjectModificationListener)this.omCreationListeners.elementAt(i)).objectCreated(e); 1298 } 1299 } 1300 1301 1302 private void fireObjectDeletedEvent(ObjectModificationEvent e) { 1303 1304 //sanity check 1305 if (e.getType() != ObjectModificationEvent.OBJECT_DELETED) { 1306 throw new IllegalArgumentException(); 1307 } 1308 1309 for (int i=0; i< this.omDeletionListeners.size(); i++) { 1310 ((ObjectModificationListener)this.omDeletionListeners.elementAt(i)).objectDeleted(e); 1311 } 1312 } 1313 1314 1315 private void fireObjectModifiedEvent(ObjectModificationEvent e) { 1316 1317 //sanity check 1318 if (e.getType() != ObjectModificationEvent.OBJECT_MODIFIED) { 1319 throw new IllegalArgumentException(); 1320 } 1321 1322 for (int i=0; i< this.omModificationListeners.size(); i++) { 1323 ((ObjectModificationListener)omModificationListeners.elementAt(i)).objectModified(e); 1324 } 1325 } 1326 1327 1328 1329 1330 public void registerObjectModificationListener(ObjectModificationListener l, 1331 int eventType) { 1332 1333 if (eventType != ObjectModificationEvent.OBJECT_CREATED && 1334 eventType != ObjectModificationEvent.OBJECT_DELETED && 1335 eventType != ObjectModificationEvent.OBJECT_MODIFIED) { 1336 1337 throw new IllegalArgumentException(); 1338 } 1339 1340 switch(eventType) { 1341 case ObjectModificationEvent.OBJECT_CREATED : 1342 this.omCreationListeners.add(l); 1343 break; 1344 case ObjectModificationEvent.OBJECT_DELETED : 1345 this.omDeletionListeners.add(l); 1346 break; 1347 case ObjectModificationEvent.OBJECT_MODIFIED : 1348 this.omModificationListeners.add(l); 1349 break; 1350 default: 1351 Assert.fail(); 1352 } 1353 1354 } 1355 1356 public void unregisterObjectModificationListener(ObjectModificationListener l, 1357 int eventType) { 1358 1359 if (eventType != ObjectModificationEvent.OBJECT_CREATED && 1360 eventType != ObjectModificationEvent.OBJECT_DELETED && 1361 eventType != ObjectModificationEvent.OBJECT_MODIFIED) { 1362 1363 throw new IllegalArgumentException(); 1364 } 1365 1366 switch(eventType) { 1367 case ObjectModificationEvent.OBJECT_CREATED : 1368 this.omCreationListeners.remove(l); 1369 break; 1370 case ObjectModificationEvent.OBJECT_DELETED : 1371 this.omDeletionListeners.remove(l); 1372 break; 1373 case ObjectModificationEvent.OBJECT_MODIFIED : 1374 this.omModificationListeners.remove(l); 1375 break; 1376 default: 1377 Assert.fail(); 1378 } 1379 1380 } 1381 1382 1383 1384 1385 /* ObjectModificationListener methods */ 1386 1387 public void objectCreated(ObjectModificationEvent e) { 1388 //I've never registered for these events 1389 Assert.fail(); 1390 } 1391 1392 public void objectModified(ObjectModificationEvent e) { 1393 1394 Object source = e.getSource(); 1395 int type = e.getType(); 1396 int subtype = e.getSubType(); 1397 1398 //sanity checks 1399 if (type != ObjectModificationEvent.OBJECT_MODIFIED) { 1400 throw new IllegalArgumentException(); 1401 } 1402 1403 //I'm interested only in Groups and Users 1404 if (false == source instanceof Group && 1405 false == source instanceof User) { 1406 1407 throw new IllegalArgumentException(); 1408 } 1409 1410 1411 if (source instanceof Group) { 1412 1413 Assert.assertTrue(subtype == Group.OBJECT_CHANGE_ADDUSER || 1414 subtype == Group.OBJECT_CHANGE_NAME || 1415 subtype == Group.OBJECT_CHANGE_REMOVEUSER); 1416 1417 //the name of the group could be different now (IDs are fixed) 1418 if (subtype == Group.OBJECT_CHANGE_NAME) { 1419 //rehash 1420 //any better idea how to do it? 1421 Set mappings = this.groupsByName.entrySet(); 1422 Iterator it = mappings.iterator(); 1423 1424 boolean found = false; 1425 while (it.hasNext()) { 1426 Map.Entry mapEntry = (Map.Entry)it.next(); 1427 String key = (String)mapEntry.getKey(); 1428 Group grp = (Group)mapEntry.getValue(); 1429 1430 if (false == key.equals(grp.getName())) { 1431 //gotcha 1432 this.groupsByName.remove(key); 1433 this.groupsByName.put(grp.getName(),grp); 1434 found = true; 1435 break; 1436 } 1437 } 1438 1439 Assert.assertTrue(found); 1440 } 1441 } 1442 else { 1443 1444 Assert.assertTrue(source instanceof User); 1445 1446 //the name of the user could be different now (IDs are fixed) 1447 1448 Assert.assertTrue(subtype == User.OBJECT_CHANGE_NAME); 1449 1450 //the name of the group could be different now (IDs are fixed) 1451 if (subtype == User.OBJECT_CHANGE_NAME) { 1452 //rehash 1453 //any better idea how to do it? 1454 Set mappings = this.usersByName.entrySet(); 1455 Iterator it = mappings.iterator(); 1456 1457 boolean found = false; 1458 while (it.hasNext()) { 1459 Map.Entry mapEntry = (Map.Entry)it.next(); 1460 String key = (String)mapEntry.getKey(); 1461 User usr = (User)mapEntry.getValue(); 1462 1463 if (false == key.equals(usr.getName())) { 1464 //gotcha 1465 this.usersByName.remove(key); 1466 this.usersByName.put(usr.getName(),usr); 1467 found = true; 1468 break; 1469 } 1470 } 1471 1472 Assert.assertTrue(found); 1473 } 1474 } 1475 1476 1477 } 1478 1479 public void objectDeleted(ObjectModificationEvent e) { 1480 //I've never registered for these events 1481 Assert.fail(); 1482 } 1483 1484 public void processGateEvent(GateEvent e){ 1485 throw new MethodNotImplementedException(); 1486 } 1487 1488 /** -- */ 1489 public boolean isValidSecurityInfo(SecurityInfo si) { 1490 1491 switch(si.getAccessMode()) { 1492 1493 case SecurityInfo.ACCESS_WR_GW: 1494 case SecurityInfo.ACCESS_GR_GW: 1495 return (null != si.getGroup()); 1496 1497 case SecurityInfo.ACCESS_GR_OW: 1498 return (null != si.getGroup() && 1499 null != si.getUser()); 1500 1501 case SecurityInfo.ACCESS_OR_OW: 1502 return (null != si.getUser()); 1503 1504 default: 1505 throw new IllegalArgumentException(); 1506 } 1507 } 1508 1509 public void finalize() { 1510 //close connection 1511 try { 1512 this.jdbcConn.close(); 1513 } 1514 catch(SQLException sqle) {} 1515 1516 } 1517 1518} 1519
|
AccessControllerImpl |
|