View Javadoc

1   /*
2    * UserGroup.java
3    *
4    * Created on May 24, 2004, 2:37 PM
5    */
6   
7   package gov.bnl.gums.userGroup;
8   
9   import gov.bnl.gums.GridUser;
10  import gov.bnl.gums.configuration.ConfigElement;
11  import gov.bnl.gums.configuration.Configuration;
12  import gov.bnl.gums.util.RWLock;
13  
14  import java.util.*;
15  import java.util.regex.Matcher;
16  import java.util.regex.Pattern;
17  
18  import javax.persistence.CascadeType;
19  import javax.persistence.DiscriminatorColumn;
20  import javax.persistence.DiscriminatorType;
21  import javax.persistence.Entity;
22  import javax.persistence.Inheritance;
23  import javax.persistence.InheritanceType;
24  import javax.persistence.OneToMany;
25  
26  import org.apache.log4j.Logger;
27  
28  /** 
29   * An interface that defines a group of people, which GUMS will associate to a 
30   * mapping policy. An implementation could take/manage a list of users in
31   * any way it wanted, or it could combine different groups.
32   *
33   * @author Gabriele Carcassi, Jay Packard
34   */
35  @Entity
36  @Inheritance(strategy=InheritanceType.SINGLE_TABLE)
37  @DiscriminatorColumn(
38      name="type",
39      discriminatorType=DiscriminatorType.STRING
40  )
41  public abstract class UserGroup extends ConfigElement {
42      protected static String[] accessTypes = {"write", "read all", "read self"};
43      private static Logger log = Logger.getLogger(UserGroup.class);
44      
45  	protected List<Pattern> dnFqanPatternList = new ArrayList<Pattern>();
46  	protected List<Pattern> dnPatternList = new ArrayList<Pattern>();
47  	protected Date patternListLastUpdated = null;
48  	protected RWLock patternRWLock = new RWLock();
49  	protected int secondsBetweenPatternRefresh = 30;
50  	protected boolean hasCache = false;
51  	protected int accessIndex = 2;
52  	protected String access = accessTypes[accessIndex];
53  
54  	// persistent variables
55  	protected Date lastUpdate;
56  	protected Set<GridUser> members = new HashSet<GridUser>();
57  	
58  	/**
59  	 * Create a new user group.  This empty constructor is needed by the XML Digestor.
60  	 */
61  	public UserGroup() {
62  	}
63  
64  	/**
65  	 * Create a new user group with a configuration and a name.
66  	 * 
67  	 * @param configuration
68  	 * @param name
69  	 */
70  	public UserGroup(Configuration configuration, String name) {
71  		super(configuration, name);
72  	}
73      
74  	public void clearCachedItems() {
75  		if (hasCache)
76  			members.clear();
77  	}
78  	
79      public ConfigElement clone(Configuration conf, boolean allowReplace) {
80      	UserGroup userGroup = (UserGroup)super.clone(conf, allowReplace);
81     		userGroup.setMembers(new HashSet<GridUser>(members));
82      	return userGroup;
83      }
84  	
85  	/**
86  	 * Getter for property access, that determines what a member of this
87  	 * user group has access to in GUMS.
88  	 * 
89  	 * @return access as string
90  	 */
91  	public String getAccess() {
92      	return access;
93      }
94      
95  	public Date getLastUpdate() {
96  		return lastUpdate;
97  	}
98      
99      /**
100      * Returns the list of user identities that are part of the group.
101      * <p>
102      * Some UserGroups, however, could be defined by a rule that doesn't
103      * allow listing. For example, a group could be 'all the users
104      * with a DOEGrids certificate'. Though one could argue whether or
105      * not is a good idea to have such a group, one can implement one
106      * and throw an UnsupportedOperationException. This will make it
107      * impossible for GUMS to create a grid-mapfile, but would still
108      * allow direct user to account mapping through a call-out.
109      * @return a List of GridUser objects representing the user certificate DN.
110      */
111 	@OneToMany(cascade=CascadeType.ALL, mappedBy="userGroup") 
112 	@org.hibernate.annotations.Cascade(org.hibernate.annotations.CascadeType.DELETE_ORPHAN)
113 	public Set<GridUser> getMembers() {
114 		return members;
115 	}
116     
117     /**
118      * @return true if this group allows at least read all access
119      */
120     public boolean hasReadAllAccess() {
121     	return (accessIndex<=1);
122     }
123 
124 	/**
125      * @return true if this group allows at least read self access
126      */
127     public boolean hasReadSelfAccess() {
128     	return (accessIndex<=2);
129     }
130 	/**
131      * @return true if this group allows write access (admin privileges)
132      */
133     public boolean hasWriteAccess() {
134     	return (accessIndex==0);
135     }
136 
137 	public boolean isMember(GridUser user) {
138 		return isMember(user, false);
139 	}
140     
141     /**
142      * Determines whether the given user identity is part of the group.
143      * <p>
144      * @param user - a grid user
145      * @return true if it's in the group
146      */
147 	public boolean isMember(GridUser user, boolean ignoreFqan) {
148     	boolean returnVal = false;
149 		
150 		Calendar cal = Calendar.getInstance();
151 		cal.add(Calendar.SECOND, -secondsBetweenPatternRefresh);
152     	if (patternListLastUpdated == null || patternListLastUpdated.before(cal.getTime()))
153     		refreshPatternList();
154         	
155        	try {
156         	patternRWLock.getReadLock();
157     		Iterator<Pattern> it;
158     		if (ignoreFqan)
159     			it = dnPatternList.iterator();
160     		else
161     			it = dnFqanPatternList.iterator();
162 	    	while (it.hasNext()) {
163 	    		Pattern p = (Pattern)it.next();
164 	    		Matcher m;
165 	    		if (ignoreFqan)
166 	    			m = p.matcher(user.getDn());
167 	    		else
168 	    			m = p.matcher(user.toString());
169 	    		if (log.isTraceEnabled())
170 	    			log.trace("trying to match user "+user+" against "+m.toString());
171 	    		if (m.matches()) {
172 	    			returnVal = true;
173 	    			break;
174 	    		}
175 	    	}
176 		}
177 		catch(Exception e) {
178 			log.error(e);
179 		}
180 		finally {
181 			patternRWLock.releaseLock();
182 		}
183     	
184         return returnVal;
185     }
186 
187     /**
188      * Calculate efficient pattern matcher of all current members
189      */
190     public void refreshPatternList() {
191     	patternRWLock.getWriteLock();
192     	try {
193     		log.debug("refreshed pattern list for usergroup "+getName());
194     		dnPatternList.clear();
195     		dnFqanPatternList.clear();
196 	 		synchronized(members) {
197 		 		Iterator<GridUser> it = members.iterator();
198 		 		while (it.hasNext()) 
199 		 		{
200 		 			GridUser itUser = it.next();
201 		 			Pattern p = Pattern.compile(itUser.getDn());
202 		 			dnPatternList.add(p);
203 		 			p = Pattern.compile(itUser.toString());
204 		 			dnFqanPatternList.add(p);
205 		 		}
206 	 		}
207 	 		patternListLastUpdated = Calendar.getInstance().getTime();
208 		}
209 		catch(Exception e) {
210 			log.error(e);
211 		}
212 		finally {
213 			patternRWLock.releaseLock();
214 		}
215     }
216 
217     @ConfigFieldAnnotation(label="GUMS Access", help="GUMS access by members of this user group", choices="write, read all, read self", order=4)
218     public void setAccess(String access) {
219     	for(int i=0; i<accessTypes.length; i++) {
220     		if ( accessTypes[i].equalsIgnoreCase(access) ) {
221     			this.access = access;
222     			this.accessIndex = i;
223     			return;
224     		}
225     	}
226     	throw new RuntimeException("Invalid access type: "+access);
227     }
228     
229     public void setLastUpdate(Date lastUpdate) {
230 		this.lastUpdate = lastUpdate;
231 	}
232 
233     private void setMembers(Set<GridUser> members) {
234     	if (members==null)
235     		return;
236     	this.members = members;
237     	for (GridUser m: members)
238     		m.setUserGroup(this);
239 	}
240 
241 	/** 
242      * Updates the local list of the users from the source of the group.
243      * <p>
244      * Most user groups will get the information from a separate database
245      * accessible via WAN. For that reason, the user group will maintain a
246      * local cache with the list of members, which can be updated through
247      * this method.
248      */
249 	public final void updateMembers() {
250 		synchronized(members) {
251 			members.clear();
252 			members.addAll(retrieveMembers());
253 			for (GridUser gu: members)
254 				gu.setUserGroup(this);
255 		}
256 		refreshPatternList();
257     }
258 	
259 	protected abstract Set<GridUser> retrieveMembers();
260 }