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