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