1   /*
2    *  UserImpl.java
3    *
4    *  Copyright (c) 1998-2005, 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: UserImpl.java,v 1.23 2005/01/11 13:51:36 ian Exp $
14   */
15  
16  package gate.security;
17  
18  import java.sql.*;
19  import java.util.List;
20  import java.util.Vector;
21  
22  import junit.framework.Assert;
23  
24  import gate.Gate;
25  import gate.event.*;
26  import gate.persist.DBHelper;
27  import gate.persist.PersistenceException;
28  import gate.util.MethodNotImplementedException;
29  
30  
31  public class UserImpl
32    implements User, ObjectModificationListener {
33  
34    /** user ID (must be unique) */
35    private Long    id;
36  
37    /** user name (must be unique) */
38    private String  name;
39  
40    /** list of groups the user belongs to */
41    private List    groups;
42  
43    /** Connection to the data store
44     *  used for updates */
45    private Connection conn;
46  
47    /** --- */
48    private int dbType;
49  
50    /** reference to the security factory */
51    private AccessControllerImpl ac;
52  
53    /** list of objects that should be modified when the state
54     *  of this object is changed */
55    private Vector omModificationListeners;
56  
57    /** list of objects that should be modified when
58     *  this object is created */
59    private Vector omCreationListeners;
60  
61    /** list of objects that should be modified when
62     *  this object is deleted */
63    private Vector omDeletionListeners;
64  
65  
66    /** --- */
67    public UserImpl(Long id, String name, List groups,AccessControllerImpl ac,Connection conn) {
68  
69      this.id = id;
70      this.name = name;
71      this.groups = groups;
72      this.ac = ac;
73      this.conn = conn;
74  
75      try {
76        String jdbcURL = conn.getMetaData().getURL();
77        this.dbType = DBHelper.getDatabaseType(jdbcURL);
78        Assert.assertTrue(this.dbType == DBHelper.ORACLE_DB ||
79                          this.dbType == DBHelper.POSTGRES_DB);
80      }
81      catch(SQLException sqex) {
82        sqex.printStackTrace();
83      }
84  
85      this.omModificationListeners = new Vector();
86      this.omCreationListeners = new Vector();
87      this.omDeletionListeners = new Vector();
88  
89      //register self as listener for the security factory events
90      //of type OBJECT_DELETED (groups)
91      //don't forget that only AC can delete groups, so he's the only
92      //source of such events
93      this.ac.registerObjectModificationListener(
94                                  this,
95                                  ObjectModificationEvent.OBJECT_DELETED);
96  
97    }
98  
99  
100   /* Interface USER */
101 
102   /** returns the ID of the user
103    *  user IDs are uniques in the same
104    *  data store
105    *  */
106 
107   public Long getID() {
108 
109     return id;
110   }
111 
112   /** returns the name of the user
113    *  user names are unique in the
114    *  same data store */
115   public String getName() {
116 
117     return name;
118   }
119 
120   /** returns a list with the groups that the
121    *  user is member of  */
122   public List getGroups() {
123 
124     /** NOTE that we're returning a copy of the actuall collection of groups
125      *  so that someone would not accidentaly modify it */
126     Vector copy = new Vector();
127     copy.addAll(this.groups);
128     return copy;
129   }
130 
131   /** changes user name
132    *  Only members of the ADMIN group have sufficient privileges.
133    *  fires ObjectModificationEvent
134    *  @see ObjectModificationEvent
135    *  */
136   public void setName(String newName, Session s)
137     throws PersistenceException,SecurityException {
138 
139     //1.  check the session
140     if (this.ac.isValidSession(s) == false) {
141       throw new SecurityException("invalid session supplied");
142     }
143 
144     //1.5 check if user has right to change name
145     if (s.getID() != this.id && false == s.isPrivilegedSession()) {
146       throw new SecurityException("insufficient privileges");
147     }
148 
149     CallableStatement stmt = null;
150     PreparedStatement pstmt = null;
151 
152     //2. update database
153 
154     //Oracle / Postgres ?
155     if (this.dbType == DBHelper.ORACLE_DB) {
156       try {
157         stmt = this.conn.prepareCall(
158                 "{ call "+Gate.DB_OWNER+".security.set_user_name(?,?)} ");
159         stmt.setLong(1,this.id.longValue());
160         stmt.setString(2,newName);
161         stmt.execute();
162       }
163       catch(SQLException sqle) {
164         throw new PersistenceException("can't change user name in DB: ["+ sqle.getMessage()+"]");
165       }
166       finally {
167         DBHelper.cleanup(stmt);
168       }
169     }
170 
171     else if (this.dbType == DBHelper.POSTGRES_DB) {
172       try {
173         String sql = "select security_set_user_name(?,?)";
174         pstmt = this.conn.prepareStatement(sql);
175         pstmt.setLong(1,this.id.longValue());
176         pstmt.setString(2,newName);
177         pstmt.execute();
178       }
179       catch(SQLException sqle) {
180         throw new PersistenceException("can't change user name in DB: ["+ sqle.getMessage()+"]");
181       }
182       finally {
183         DBHelper.cleanup(pstmt);
184       }
185     }
186 
187     else {
188       throw new IllegalArgumentException();
189     }
190 
191     //4. create ObjectModificationEvent
192     ObjectModificationEvent e = new ObjectModificationEvent(
193                                           this,
194                                           ObjectModificationEvent.OBJECT_MODIFIED,
195                                           User.OBJECT_CHANGE_NAME);
196 
197     //5. update member variable
198     this.name = newName;
199 
200     //6. fire ObjectModificationEvent for all who care
201     fireObjectModifiedEvent(e);
202   }
203 
204 
205   /** changes user password
206    *  Only members of the ADMIN group and the user himself
207    *  have sufficient privileges */
208   public void setPassword(String newPass, Session s)
209     throws PersistenceException,SecurityException {
210 
211     //1. first check the session
212     if (this.ac.isValidSession(s) == false) {
213       throw new SecurityException("invalid session supplied");
214     }
215 
216     //2. check privileges
217     if (false == s.isPrivilegedSession() && s.getID() != this.id) {
218       throw new SecurityException("insuffieicent privileges");
219     }
220 
221     CallableStatement stmt = null;
222     PreparedStatement pstmt = null;
223 
224     //Oracle / Postgres ?
225     if (this.dbType == DBHelper.ORACLE_DB) {
226       try {
227         stmt = this.conn.prepareCall(
228                 "{ call "+Gate.DB_OWNER+".security.set_user_password(?,?)} ");
229         stmt.setLong(1,this.id.longValue());
230         stmt.setString(2,newPass);
231         stmt.execute();
232         //release stmt???
233       }
234       catch(SQLException sqle) {
235         throw new PersistenceException("can't change user password in DB: ["+ sqle.getMessage()+"]");
236       }
237       finally {
238         DBHelper.cleanup(stmt);
239       }
240     }
241 
242     else if (this.dbType == DBHelper.POSTGRES_DB) {
243       try {
244         String sql = "select security_set_user_password(?,?)";
245         pstmt = this.conn.prepareStatement(sql);
246         pstmt.setLong(1,this.id.longValue());
247         pstmt.setString(2,newPass);
248         pstmt.execute();
249         //release stmt???
250       }
251       catch(SQLException sqle) {
252         throw new PersistenceException("can't change user password in DB: ["+ sqle.getMessage()+"]");
253       }
254       finally {
255         DBHelper.cleanup(pstmt);
256       }
257     }
258 
259     else {
260       throw new IllegalArgumentException();
261     }
262 
263   }
264 
265   /**
266    *
267    *  this one is necessary for the contains() operations in Lists
268    *  It is possible that two users have two different UserImpl that refer
269    *  to the very same user in the DB, because they got it fromt he security
270    *  factory at different times. So we assume that two instances refer the same
271    *  GATE user if ID1==ID2 && NAME1==NAME2
272    *
273    *  */
274   public boolean equals(Object obj)
275   {
276     Assert.assertTrue(obj instanceof User);
277 
278     User usr2 = (User)obj;
279 
280     return (this.id.equals(usr2.getID()));
281   }
282 
283   /** registers an object fore receiving ObjectModificationEvent-s
284    *  send by this object
285    *  the only types of events sent by a user object are
286    *  OBJECT_DELETED and OBJECT_MODIFIED, so any attempt for
287    *  registering for other events is invalid  */
288   public void registerObjectModificationListener(ObjectModificationListener l,
289                                                  int eventType) {
290 
291     if (eventType != ObjectModificationEvent.OBJECT_DELETED &&
292         eventType != ObjectModificationEvent.OBJECT_MODIFIED) {
293 
294         throw new IllegalArgumentException();
295     }
296 
297     switch(eventType) {
298       case ObjectModificationEvent.OBJECT_CREATED :
299         this.omCreationListeners.add(l);
300         break;
301       case ObjectModificationEvent.OBJECT_DELETED :
302         this.omDeletionListeners.add(l);
303         break;
304       case ObjectModificationEvent.OBJECT_MODIFIED :
305         this.omModificationListeners.add(l);
306         break;
307       default:
308         Assert.fail();
309     }
310 
311   }
312 
313   /** unregisters an object fore receiving ObjectModificationEvent-s
314    *  send by this object
315    *  the only types of events sent by a user object are
316    *  OBJECT_DELETED and OBJECT_MODIFIED, so any attempt for
317    *  unregistering for other events is invalid  */
318   public void unregisterObjectModificationListener(ObjectModificationListener l,
319                                                    int eventType) {
320 
321     if (eventType != ObjectModificationEvent.OBJECT_DELETED &&
322         eventType != ObjectModificationEvent.OBJECT_MODIFIED) {
323 
324         throw new IllegalArgumentException();
325     }
326 
327     switch(eventType) {
328       case ObjectModificationEvent.OBJECT_CREATED :
329         this.omCreationListeners.remove(l);
330         break;
331       case ObjectModificationEvent.OBJECT_DELETED :
332         this.omDeletionListeners.remove(l);
333         break;
334       case ObjectModificationEvent.OBJECT_MODIFIED :
335         this.omModificationListeners.remove(l);
336         break;
337       default:
338         Assert.fail();
339     }
340   }
341 
342   /** sends ObjectModificationEvent of type OBJECT_MODIFIED to all
343    *  who have already registered */
344   private void fireObjectModifiedEvent(ObjectModificationEvent e) {
345 
346     //sanity check
347     if (e.getType() != ObjectModificationEvent.OBJECT_MODIFIED) {
348       throw new IllegalArgumentException();
349     }
350 
351     for (int i=0; i< this.omModificationListeners.size(); i++) {
352       ((ObjectModificationListener)omModificationListeners.elementAt(i)).objectModified(e);
353     }
354   }
355 
356   //ObjectModificationListener interface
357 
358   /** callback that is invoked from objects that were <b>created</b>
359    *  and this user object is interested in
360    *  <b>NOTE</b> that this events are just ignored*/
361   public void objectCreated(ObjectModificationEvent e) {
362     //ignore, we don't care about creations
363     return;
364   }
365 
366   /** callback that is invoked from objects that were <b>modified</b>
367    *  and this user object is interested in
368    *  Useful when a group drops the user as member and
369    *  this user should be notified so that it will remove the
370    *  reference to the group from its internal collections
371    *  (the user is no longer member of the group)
372    *  */
373   public void objectModified(ObjectModificationEvent e) {
374 
375     //only groups can disturb the user
376     Assert.assertTrue(e.getSubType() == Group.OBJECT_CHANGE_ADDUSER ||
377                   e.getSubType() == Group.OBJECT_CHANGE_REMOVEUSER ||
378                   e.getSubType() == Group.OBJECT_CHANGE_NAME);
379 
380     //we get this event only if a group adds/removes user to it
381     Group grp = (Group)e.getSource();
382 
383     switch(e.getSubType()) {
384 
385       case Group.OBJECT_CHANGE_ADDUSER:
386 
387         //1.check that the groupis not already in collection
388         Assert.assertTrue(false == this.groups.contains(grp));
389         //1.1 verify grp
390         Assert.assertTrue(grp instanceof Group);
391         //2.add group to collection
392         this.groups.add(grp);
393         //3. the group has laredy registered
394         //the user as listener for this group
395         ;
396         break;
397 
398       case Group.OBJECT_CHANGE_REMOVEUSER:
399         //1.check that the group is in collection
400         Assert.assertTrue(true == this.groups.contains(grp));
401         //2.remove group from collection
402         this.groups.remove(grp);
403         //3. the group has laredy UNregistered
404         //the user as listener for this group
405         ;
406         break;
407 
408       case Group.OBJECT_CHANGE_NAME:
409         //do nothing
410         break;
411 
412       default:
413         throw new IllegalArgumentException();
414     }
415 
416 
417   }
418 
419   /** callback that is invoked from objects that were <b>deleted</b>
420    *  and this user object is interested in
421    *  Useful when a group is deleted from the security factory and
422    *  this user should be notified so that it will remove the
423    *  reference to the group from its internal collections
424    *  (the user is no longer member of the group)
425    *  */
426   public void objectDeleted(ObjectModificationEvent e) {
427 
428     if (e.getSource() instanceof Group) {
429 
430       Group grp = (Group)e.getSource();
431       //check if the Group being deleted is one we belong to
432       if (true == this.groups.contains(grp)) {
433         this.groups.remove(grp);
434       }
435 
436     }
437   }
438 
439   /** huh? */
440   public void processGateEvent(GateEvent e){
441     throw new MethodNotImplementedException();
442   }
443 
444 
445   /*package*/ void setGroups(Vector groupIDs) {
446 
447     for (int i=0; i< groupIDs.size(); i++) {
448       Long grp_id = (Long)groupIDs.elementAt(i);
449       Group grp = null;
450 
451       try {
452         grp = (Group)this.ac.findGroup(grp_id);
453       }
454       catch(SecurityException se) {
455         Assert.fail();
456       }
457       catch(PersistenceException se) {
458         Assert.fail();
459       }
460 
461       //is valid?
462       Assert.assertNotNull(grp);
463       Assert.assertTrue(grp instanceof Group);
464       //add to our collection, which was empty so far
465       this.groups.add(grp);
466     }
467   }
468 
469 
470 }
471