UserImpl.java
001 /*
002  *  UserImpl.java
003  *
004  *  Copyright (c) 1995-2010, The University of Sheffield. See the file
005  *  COPYRIGHT.txt in the software or at http://gate.ac.uk/gate/COPYRIGHT.txt
006  *
007  *  This file is part of GATE (see http://gate.ac.uk/), and is free
008  *  software, licenced under the GNU Library General Public License,
009  *  Version 2, June 1991 (in the distribution as file licence.html,
010  *  and also available at http://gate.ac.uk/gate/licence.html).
011  *
012  *  Marin Dimitrov, 19/Sep/2001
013  *
014  *  $Id: UserImpl.java 12006 2009-12-01 17:24:28Z thomas_heitz $
015  */
016 
017 package gate.security;
018 
019 import java.sql.*;
020 import java.util.List;
021 import java.util.Vector;
022 
023 import junit.framework.Assert;
024 
025 import gate.Gate;
026 import gate.event.*;
027 import gate.persist.DBHelper;
028 import gate.persist.PersistenceException;
029 import gate.util.MethodNotImplementedException;
030 
031 
032 public class UserImpl
033   implements User, ObjectModificationListener {
034 
035   /** user ID (must be unique) */
036   private Long    id;
037 
038   /** user name (must be unique) */
039   private String  name;
040 
041   /** list of groups the user belongs to */
042   private List    groups;
043 
044   /** Connection to the data store
045    *  used for updates */
046   private Connection conn;
047 
048   /** --- */
049   private int dbType;
050 
051   /** reference to the security factory */
052   private AccessControllerImpl ac;
053 
054   /** list of objects that should be modified when the state
055    *  of this object is changed */
056   private Vector omModificationListeners;
057 
058   /** list of objects that should be modified when
059    *  this object is created */
060   private Vector omCreationListeners;
061 
062   /** list of objects that should be modified when
063    *  this object is deleted */
064   private Vector omDeletionListeners;
065 
066 
067   /** --- */
068   public UserImpl(Long id, String name, List groups,AccessControllerImpl ac,Connection conn) {
069 
070     this.id = id;
071     this.name = name;
072     this.groups = groups;
073     this.ac = ac;
074     this.conn = conn;
075 
076     try {
077       String jdbcURL = conn.getMetaData().getURL();
078       this.dbType = DBHelper.getDatabaseType(jdbcURL);
079       Assert.assertTrue(this.dbType == DBHelper.ORACLE_DB ||
080                         this.dbType == DBHelper.POSTGRES_DB);
081     }
082     catch(SQLException sqex) {
083       sqex.printStackTrace();
084     }
085 
086     this.omModificationListeners = new Vector();
087     this.omCreationListeners = new Vector();
088     this.omDeletionListeners = new Vector();
089 
090     //register self as listener for the security factory events
091     //of type OBJECT_DELETED (groups)
092     //don't forget that only AC can delete groups, so he's the only
093     //source of such events
094     this.ac.registerObjectModificationListener(
095                                 this,
096                                 ObjectModificationEvent.OBJECT_DELETED);
097 
098   }
099 
100 
101   /* Interface USER */
102 
103   /** returns the ID of the user
104    *  user IDs are uniques in the same
105    *  data store
106    *  */
107 
108   public Long getID() {
109 
110     return id;
111   }
112 
113   /** returns the name of the user
114    *  user names are unique in the
115    *  same data store */
116   public String getName() {
117 
118     return name;
119   }
120 
121   /** returns a list with the groups that the
122    *  user is member of  */
123   public List getGroups() {
124 
125     /** NOTE that we're returning a copy of the actuall collection of groups
126      *  so that someone would not accidentaly modify it */
127     Vector copy = new Vector();
128     copy.addAll(this.groups);
129     return copy;
130   }
131 
132   /** changes user name
133    *  Only members of the ADMIN group have sufficient privileges.
134    *  fires ObjectModificationEvent
135    *  @see ObjectModificationEvent
136    *  */
137   public void setName(String newName, Session s)
138     throws PersistenceException,SecurityException {
139 
140     //1.  check the session
141     if (this.ac.isValidSession(s== false) {
142       throw new SecurityException("invalid session supplied");
143     }
144 
145     //1.5 check if user has right to change name
146     if (s.getID() != this.id && false == s.isPrivilegedSession()) {
147       throw new SecurityException("insufficient privileges");
148     }
149 
150     CallableStatement stmt = null;
151     PreparedStatement pstmt = null;
152 
153     //2. update database
154 
155     //Oracle / Postgres ?
156     if (this.dbType == DBHelper.ORACLE_DB) {
157       try {
158         stmt = this.conn.prepareCall(
159                 "{ call "+Gate.DB_OWNER+".security.set_user_name(?,?)} ");
160         stmt.setLong(1,this.id.longValue());
161         stmt.setString(2,newName);
162         stmt.execute();
163       }
164       catch(SQLException sqle) {
165         throw new PersistenceException("can't change user name in DB: ["+ sqle.getMessage()+"]");
166       }
167       finally {
168         DBHelper.cleanup(stmt);
169       }
170     }
171 
172     else if (this.dbType == DBHelper.POSTGRES_DB) {
173       try {
174         String sql = "select security_set_user_name(?,?)";
175         pstmt = this.conn.prepareStatement(sql);
176         pstmt.setLong(1,this.id.longValue());
177         pstmt.setString(2,newName);
178         pstmt.execute();
179       }
180       catch(SQLException sqle) {
181         throw new PersistenceException("can't change user name in DB: ["+ sqle.getMessage()+"]");
182       }
183       finally {
184         DBHelper.cleanup(pstmt);
185       }
186     }
187 
188     else {
189       throw new IllegalArgumentException();
190     }
191 
192     //4. create ObjectModificationEvent
193     ObjectModificationEvent e = new ObjectModificationEvent(
194                                           this,
195                                           ObjectModificationEvent.OBJECT_MODIFIED,
196                                           User.OBJECT_CHANGE_NAME);
197 
198     //5. update member variable
199     this.name = newName;
200 
201     //6. fire ObjectModificationEvent for all who care
202     fireObjectModifiedEvent(e);
203   }
204 
205 
206   /** changes user password
207    *  Only members of the ADMIN group and the user himself
208    *  have sufficient privileges */
209   public void setPassword(String newPass, Session s)
210     throws PersistenceException,SecurityException {
211 
212     //1. first check the session
213     if (this.ac.isValidSession(s== false) {
214       throw new SecurityException("invalid session supplied");
215     }
216 
217     //2. check privileges
218     if (false == s.isPrivilegedSession() && s.getID() != this.id) {
219       throw new SecurityException("insuffieicent privileges");
220     }
221 
222     CallableStatement stmt = null;
223     PreparedStatement pstmt = null;
224 
225     //Oracle / Postgres ?
226     if (this.dbType == DBHelper.ORACLE_DB) {
227       try {
228         stmt = this.conn.prepareCall(
229                 "{ call "+Gate.DB_OWNER+".security.set_user_password(?,?)} ");
230         stmt.setLong(1,this.id.longValue());
231         stmt.setString(2,newPass);
232         stmt.execute();
233         //release stmt???
234       }
235       catch(SQLException sqle) {
236         throw new PersistenceException("can't change user password in DB: ["+ sqle.getMessage()+"]");
237       }
238       finally {
239         DBHelper.cleanup(stmt);
240       }
241     }
242 
243     else if (this.dbType == DBHelper.POSTGRES_DB) {
244       try {
245         String sql = "select security_set_user_password(?,?)";
246         pstmt = this.conn.prepareStatement(sql);
247         pstmt.setLong(1,this.id.longValue());
248         pstmt.setString(2,newPass);
249         pstmt.execute();
250         //release stmt???
251       }
252       catch(SQLException sqle) {
253         throw new PersistenceException("can't change user password in DB: ["+ sqle.getMessage()+"]");
254       }
255       finally {
256         DBHelper.cleanup(pstmt);
257       }
258     }
259 
260     else {
261       throw new IllegalArgumentException();
262     }
263 
264   }
265 
266   /**
267    *
268    *  this one is necessary for the contains() operations in Lists
269    *  It is possible that two users have two different UserImpl that refer
270    *  to the very same user in the DB, because they got it fromt he security
271    *  factory at different times. So we assume that two instances refer the same
272    *  GATE user if ID1==ID2 && NAME1==NAME2
273    *
274    *  */
275   public boolean equals(Object obj)
276   {
277     Assert.assertTrue(obj instanceof User);
278 
279     User usr2 = (User)obj;
280 
281     return (this.id.equals(usr2.getID()));
282   }
283 
284   /** registers an object fore receiving ObjectModificationEvent-s
285    *  send by this object
286    *  the only types of events sent by a user object are
287    *  OBJECT_DELETED and OBJECT_MODIFIED, so any attempt for
288    *  registering for other events is invalid  */
289   public void registerObjectModificationListener(ObjectModificationListener l,
290                                                  int eventType) {
291 
292     if (eventType != ObjectModificationEvent.OBJECT_DELETED &&
293         eventType != ObjectModificationEvent.OBJECT_MODIFIED) {
294 
295         throw new IllegalArgumentException();
296     }
297 
298     switch(eventType) {
299       case ObjectModificationEvent.OBJECT_CREATED :
300         this.omCreationListeners.add(l);
301         break;
302       case ObjectModificationEvent.OBJECT_DELETED :
303         this.omDeletionListeners.add(l);
304         break;
305       case ObjectModificationEvent.OBJECT_MODIFIED :
306         this.omModificationListeners.add(l);
307         break;
308       default:
309         Assert.fail();
310     }
311 
312   }
313 
314   /** unregisters an object fore receiving ObjectModificationEvent-s
315    *  send by this object
316    *  the only types of events sent by a user object are
317    *  OBJECT_DELETED and OBJECT_MODIFIED, so any attempt for
318    *  unregistering for other events is invalid  */
319   public void unregisterObjectModificationListener(ObjectModificationListener l,
320                                                    int eventType) {
321 
322     if (eventType != ObjectModificationEvent.OBJECT_DELETED &&
323         eventType != ObjectModificationEvent.OBJECT_MODIFIED) {
324 
325         throw new IllegalArgumentException();
326     }
327 
328     switch(eventType) {
329       case ObjectModificationEvent.OBJECT_CREATED :
330         this.omCreationListeners.remove(l);
331         break;
332       case ObjectModificationEvent.OBJECT_DELETED :
333         this.omDeletionListeners.remove(l);
334         break;
335       case ObjectModificationEvent.OBJECT_MODIFIED :
336         this.omModificationListeners.remove(l);
337         break;
338       default:
339         Assert.fail();
340     }
341   }
342 
343   /** sends ObjectModificationEvent of type OBJECT_MODIFIED to all
344    *  who have already registered */
345   private void fireObjectModifiedEvent(ObjectModificationEvent e) {
346 
347     //sanity check
348     if (e.getType() != ObjectModificationEvent.OBJECT_MODIFIED) {
349       throw new IllegalArgumentException();
350     }
351 
352     for (int i=0; i< this.omModificationListeners.size(); i++) {
353       ((ObjectModificationListener)omModificationListeners.elementAt(i)).objectModified(e);
354     }
355   }
356 
357   //ObjectModificationListener interface
358 
359   /** callback that is invoked from objects that were <b>created</b>
360    *  and this user object is interested in
361    *  <b>NOTE</b> that this events are just ignored*/
362   public void objectCreated(ObjectModificationEvent e) {
363     //ignore, we don't care about creations
364     return;
365   }
366 
367   /** callback that is invoked from objects that were <b>modified</b>
368    *  and this user object is interested in
369    *  Useful when a group drops the user as member and
370    *  this user should be notified so that it will remove the
371    *  reference to the group from its internal collections
372    *  (the user is no longer member of the group)
373    *  */
374   public void objectModified(ObjectModificationEvent e) {
375 
376     //only groups can disturb the user
377     Assert.assertTrue(e.getSubType() == Group.OBJECT_CHANGE_ADDUSER ||
378                   e.getSubType() == Group.OBJECT_CHANGE_REMOVEUSER ||
379                   e.getSubType() == Group.OBJECT_CHANGE_NAME);
380 
381     //we get this event only if a group adds/removes user to it
382     Group grp = (Group)e.getSource();
383 
384     switch(e.getSubType()) {
385 
386       case Group.OBJECT_CHANGE_ADDUSER:
387 
388         //1.check that the groupis not already in collection
389         Assert.assertTrue(false == this.groups.contains(grp));
390         //1.1 verify grp
391         Assert.assertTrue(grp instanceof Group);
392         //2.add group to collection
393         this.groups.add(grp);
394         //3. the group has laredy registered
395         //the user as listener for this group
396         ;
397         break;
398 
399       case Group.OBJECT_CHANGE_REMOVEUSER:
400         //1.check that the group is in collection
401         Assert.assertTrue(true == this.groups.contains(grp));
402         //2.remove group from collection
403         this.groups.remove(grp);
404         //3. the group has laredy UNregistered
405         //the user as listener for this group
406         ;
407         break;
408 
409       case Group.OBJECT_CHANGE_NAME:
410         //do nothing
411         break;
412 
413       default:
414         throw new IllegalArgumentException();
415     }
416 
417 
418   }
419 
420   /** callback that is invoked from objects that were <b>deleted</b>
421    *  and this user object is interested in
422    *  Useful when a group is deleted from the security factory and
423    *  this user should be notified so that it will remove the
424    *  reference to the group from its internal collections
425    *  (the user is no longer member of the group)
426    *  */
427   public void objectDeleted(ObjectModificationEvent e) {
428 
429     if (e.getSource() instanceof Group) {
430 
431       Group grp = (Group)e.getSource();
432       //check if the Group being deleted is one we belong to
433       if (true == this.groups.contains(grp)) {
434         this.groups.remove(grp);
435       }
436 
437     }
438   }
439 
440   /** huh? */
441   public void processGateEvent(GateEvent e){
442     throw new MethodNotImplementedException();
443   }
444 
445 
446   /*package*/ void setGroups(Vector groupIDs) {
447 
448     for (int i=0; i< groupIDs.size(); i++) {
449       Long grp_id = (Long)groupIDs.elementAt(i);
450       Group grp = null;
451 
452       try {
453         grp = (Group)this.ac.findGroup(grp_id);
454       }
455       catch(SecurityException se) {
456         Assert.fail();
457       }
458       catch(PersistenceException se) {
459         Assert.fail();
460       }
461 
462       //is valid?
463       Assert.assertNotNull(grp);
464       Assert.assertTrue(grp instanceof Group);
465       //add to our collection, which was empty so far
466       this.groups.add(grp);
467     }
468   }
469 
470 
471 }