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

| AccessControllerImpl |