ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/i-scream/projects/cms/source/util/uk/org/iscream/cms/util/ACL.java
Revision: 1.4
Committed: Wed Mar 20 17:03:35 2002 UTC (22 years, 1 month ago) by tdb
Branch: MAIN
Changes since 1.3: +79 -36 lines
Log Message:
Added three new methods;
clear() allows one to completely reset the ACL
add(String acl) allows one to add rules in a String format
setDefaultMode(boolean defaultMode) allows one to change the default mode

Also made it clearer what the default "default mode" is, and what it will
get set to upon construction.

The thinking behind some of this is that you can "on the fly" change the
setup of the ACL should it take your fancy. In terms of i-scream, you'd
simply call clear() and the add(String acl) with the new String. You could
do this by just creating a new ACL, and replacing the ACLServerSocket with
a new one, but this seems neater. A downside is that there will be a point
in time when the ACL will allow stuff that maybe it shouldn't. Maybe some
synchronization is needed?

File Contents

# User Rev Content
1 tdb 1.1 //---PACKAGE DECLARATION---
2     package uk.org.iscream.cms.server.util;
3    
4     //---IMPORTS---
5     import java.util.ArrayList;
6     import java.util.StringTokenizer;
7     import java.net.InetAddress;
8     import java.io.Serializable;
9    
10     /**
11     * Access Control List for use primarily
12     * with the ACLServerSocket. It could, however
13     * have other uses as it has a fairly generic
14     * behaviour. Rules are added using the add
15     * method, and then checks can be made using
16     * the relevant check method.
17     *
18     * @author $Author: tdb $
19 tdb 1.4 * @version $Id: ACL.java,v 1.3 2002/03/19 12:18:22 tdb Exp $
20 tdb 1.1 */
21     public class ACL implements Serializable {
22    
23     //---FINAL ATTRIBUTES---
24    
25     /**
26     * The current CVS revision of this class
27     */
28 tdb 1.4 public static final String REVISION = "$Revision: 1.3 $";
29 tdb 1.1
30     /**
31     * static to be used when adding an ALLOW rule to the ACL.
32     */
33     public static final boolean ALLOW = true;
34    
35     /**
36     * static to be used when adding a DENY rule to the ACL.
37     */
38     public static final boolean DENY = false;
39 tdb 1.4
40     /**
41     * default setting for the default mode for a new ACL.
42     */
43     public static final boolean DEFMODE = ACL.ALLOW;
44 tdb 1.1
45     //---STATIC METHODS---
46    
47     //---CONSTRUCTORS---
48    
49     /**
50     * Construct a new Access Control List. The default
51     * mode is to ALLOW anything that isn't explicitly
52     * blocked by a rule.
53     */
54     public ACL() {
55 tdb 1.4 // default to DEFMODE
56     this(DEFMODE);
57 tdb 1.1 }
58    
59     /**
60     * Construct a new Access Control List with a given
61     * default mode. This mode specifies what should
62     * happen if a check does not match any rules.
63     *
64     * @param defaultMode the default mode for non-matched checks
65     */
66     public ACL(boolean defaultMode) {
67 tdb 1.4 setDefaultMode(defaultMode);
68 tdb 1.3 }
69    
70     /**
71     * Construct a new Access Control List with a given
72     * String representation of the ACL rules. The String
73     * should be of the format:
74     * expression:rule;expression:rule;expression:rule...
75     * Where expression is a wildcard to match against, and
76     * rule is either 'ALLOW' or 'DENY'. There is a special
77     * expression of 'DEFAULT' which represents the default
78     * rule (what should happen if no expression is matched
79     * when performing a check).
80     * The default mode is set to ALLOW if one is not
81 tdb 1.4 * specified in the String.
82 tdb 1.3 *
83     * @param acl a String representation of the ACL.
84     */
85     public ACL(String acl) {
86 tdb 1.4 setDefaultMode(DEFMODE);
87     add(acl);
88     }
89    
90     //---PUBLIC METHODS---
91    
92     /**
93     * Add a new rule to the ACL immediately after the
94     * previous rule. The rule can either be an ACL.ALLOW
95     * rule, or an ACL.DENY rule. The expression can
96     * contain a wildcard (a * only). Rules can only be
97     * added to the end of the list.
98     *
99     * param allow whether this is an ALLOW or DENY rule
100     * param expression what this rule matches using wildcards
101     */
102     public void add(boolean allow, String expression) {
103     // try and convert the expression into an IP address
104     short[] ipaddr = ipStringToShort(expression);
105     // a result of null means it's not an IP address
106     // add either a name rule or an IP rule
107     if(ipaddr != null) {
108     _acl.add(new ACLRule(allow, expression, ipaddr, true));
109     }
110     else {
111     _acl.add(new ACLRule(allow, expression, ipaddr, false));
112     }
113     }
114    
115     /**
116     * Add some new rules to the Access Control List in
117     * the form of a String. The String should be of the
118     * following format:
119     * expression:rule;expression:rule;expression:rule...
120     * Where expression is a wildcard to match against, and
121     * rule is either 'ALLOW' or 'DENY'. There is a special
122     * expression of 'DEFAULT' which represents the default
123     * rule (what should happen if no expression is matched
124     * when performing a check).
125     *
126     * @param acl a String representation of the ACL.
127     */
128     public void add(String acl) {
129 tdb 1.3 if(acl != null) {
130     // split the String into expression:rule parts
131     StringTokenizer st1 = new StringTokenizer(acl, ";");
132     while(st1.hasMoreTokens()) {
133     String token1 = st1.nextToken();
134     // if it doesn't have a :, it's not the correct format
135     if(token1.indexOf(":") != -1) {
136     // split into expression and rule part
137     StringTokenizer st2 = new StringTokenizer(token1, ":");
138     String expression = "";
139     String rule = "";
140     if(st2.hasMoreTokens()) {
141     expression = st2.nextToken();
142     }
143     else {
144     // mall-formed?
145     continue;
146     }
147     if(st2.hasMoreTokens()) {
148     rule = st2.nextToken();
149     }
150     else {
151     // mall-formed?
152     continue;
153     }
154     // check to see what sort of rule
155     if(rule.equals("ALLOW")) {
156     // case for special 'DEFAULT' expression
157     if(expression.equals("DEFAULT")) {
158 tdb 1.4 setDefaultMode(ACL.ALLOW);
159 tdb 1.3 }
160     else {
161     add(ACL.ALLOW, expression);
162     }
163     }
164     else if(rule.equals("DENY")) {
165     // case for special 'DEFAULT' expression
166     if(expression.equals("DEFAULT")) {
167 tdb 1.4 setDefaultMode(ACL.DENY);
168 tdb 1.3 }
169     else {
170     add(ACL.DENY, expression);
171     }
172     }
173     // if it's not ALLOW or DENY, it's not a
174     // proper rule, so we'll ignore it
175     }
176     }
177     }
178 tdb 1.1 }
179    
180     /**
181     * Check to see if a string is permitted by the
182     * ACL. Useful for testing, and non-Socket uses
183     * of this class.
184     *
185     * @param address the string to check
186     * @return whether the address was permitted by the ACL
187     */
188     public boolean check(String address) {
189     for(int i=0; i < _acl.size(); i++) {
190     ACLRule rule = (ACLRule) _acl.get(i);
191     if(StringUtils.wildcardMatch(address, rule._expression)) {
192     return rule._allow;
193     }
194     }
195     return _defaultMode;
196     }
197    
198     /**
199     * Check to see if an InetAddress is permitted
200     * by the ACL. Perfect for Socket uses of this
201     * class. A rule will either be for a name, or
202     * an IP address (this is determined in the add
203     * method), and the appropriate comparison will
204     * be performed.
205     *
206     * @param address the InetAddress to check
207     * @return whether the InetAddress was permitted by the ACL
208     */
209     public boolean check(InetAddress address) {
210     // gather the details first
211     String hostname = address.getHostName();
212     String ip = address.getHostAddress();
213     short[] ipaddr = ipStringToShort(ip);
214     // check each rule against this InetAddress
215     for(int i=0; i < _acl.size(); i++) {
216     ACLRule rule = (ACLRule) _acl.get(i);
217     if(rule._iprule) {
218     // if this is an IP rule do a short comparison
219     // must specify the wildcarded rule first
220     if(compareShorts(rule._ipaddr, ipaddr)) {
221     return rule._allow;
222     }
223     }
224     else {
225     // if not do a full blown String comparsion
226     if(StringUtils.wildcardMatch(hostname, rule._expression)) {
227     return rule._allow;
228     }
229     }
230    
231     }
232     // if we haven't matched a rule, return the default
233     return _defaultMode;
234     }
235    
236     /**
237 tdb 1.4 * Clears the ACL and resets the default mode.
238     */
239     public void clear() {
240     // just clear out our underlying ArrayList
241     // containing our ACL objects
242     _acl.clear();
243     // and reset the default mode to the default
244     setDefaultMode(DEFMODE);
245     }
246    
247     /**
248     * Changes the default mode of the ACL. This is what
249     * the check will return if it does not find an explict
250     * rule to match against.
251     *
252     * @param defaultMode the new default mode
253     */
254     public void setDefaultMode(boolean defaultMode) {
255     _defaultMode = defaultMode;
256     }
257    
258     /**
259 tdb 1.1 * Gives a String representation of this ACL.
260     *
261     * @return A String representation of this ACL.
262     */
263     public String toString() {
264     StringBuffer acl = new StringBuffer();
265     // put in the i-scream toString code
266     acl.append(FormatName.getName(_name, getClass().getName(), REVISION));
267     acl.append("{");
268     // put the value of each Rule in the result
269     for(int i=0; i < _acl.size(); i++) {
270     acl.append((ACLRule) _acl.get(i));
271     acl.append(",");
272     }
273     // put the default mode in the result
274     if(_defaultMode) {
275     acl.append("DEFAULT=ALLOW");
276     }
277     else {
278     acl.append("DEFAULT=DENY");
279     }
280     acl.append("}");
281     return acl.toString();
282     }
283    
284     //---PRIVATE METHODS---
285    
286     /**
287     * Converts an IP address in String format into
288     * a short array of length 4. Any wildcards, *,
289     * found in the IP address are represented by
290     * a -1. If the given String is not an IP address
291     * null is returned instead.
292     *
293     * @param ip The IP address in String format
294     * @return The IP address in a short[]
295     */
296     private short[] ipStringToShort(String ip) {
297     // default to expecting it to be an IP
298     // we will try to disprove this :)
299     short[] ipaddr = {-1, -1, -1, -1};
300     int i = 0;
301     String s = "";
302     // tokenize the String on fullstops, so we can break
303     // up the quads of an IP (if it's an IP!)
304     StringTokenizer st = new StringTokenizer(ip, ".");
305     while(st.hasMoreTokens() && i++ < 4) {
306     s = st.nextToken();
307     // if it's a wildcard, we'll skip to the next one
308     // as no more checks are required
309     if(s.equals("*")) {
310     continue;
311     }
312     // attempt to parse it into a short
313     try {
314     short n = Short.parseShort(s);
315     // if it's an int but outside of the
316     // valid range, it can't be an IP
317     if(n < 0 || n > 255) {
318     // give up checking further
319     return null;
320     }
321     ipaddr[i-1] = n;
322     }
323     // if it didn't parse as a short it can't be an IP
324     catch (NumberFormatException e) {
325     // give up checking further
326     return null;
327     }
328     }
329     // we've done 4 parts, so if there's any
330     // more this can't be an IP
331     if(st.hasMoreTokens()) {
332     return null;
333     }
334     // if we've done less than 4, see if the last one
335     // was a wildcard - if it isn't then it's not an IP
336     // -- this allows 129.12.*
337     if(i < 4 && !s.equals("*")) {
338     return null;
339     }
340     // if we had one or less entries it can't be an IP
341     // -- this disallows * matching as an IP due
342     // to the rule above
343     if(i <= 1) {
344     return null;
345     }
346     return ipaddr;
347     }
348    
349     /**
350     * Compares two short arrays. The first array can contain a -1,
351     * which will always match any value -- it's a wildcard.
352     * They must be the same length to match.
353     *
354     * @param first The first array to compare (with -1 wildcard if required)
355     * @param second The second array to compare
356 tdb 1.2 * @return the result of the comparison
357 tdb 1.1 */
358     private boolean compareShorts(short[] first, short[] second) {
359     if(first.length != second.length) {
360     return false;
361     }
362     for(int i=0; i < first.length; i++) {
363     if(first[i] == -1) {
364     continue;
365     }
366     if(first[i] != second[i]) {
367     return false;
368     }
369     }
370     return true;
371     }
372    
373     //---ACCESSOR/MUTATOR METHODS---
374    
375     //---ATTRIBUTES---
376    
377     /**
378     * This is the friendly identifier of the
379     * component this class is running in.
380     * eg, a Filter may be called "filter1",
381     * If this class does not have an owning
382     * component, a name from the configuration
383     * can be placed here. This name could also
384     * be changed to null for utility classes.
385     */
386     private String _name = null;
387    
388     /**
389     * The ACL is stored in this ArrayList.
390     */
391     private ArrayList _acl = new ArrayList();
392    
393     /**
394     * The default mode of this ACL.
395     */
396 tdb 1.4 private boolean _defaultMode = DEFMODE;
397 tdb 1.1
398     //---STATIC ATTRIBUTES---
399    
400     //---INNER CLASSES---
401    
402     /**
403     * Wrapper class for an ACL rule.
404     */
405     private class ACLRule implements Serializable {
406    
407     /**
408     * Construct an ACL rule.
409     *
410     * @param allow whether this is an ALLOW or DENY rule
411     * @param expression what this rule matches
412     * @param ipaddr the IP address wildcard this rule matches if it's an IP rule
413     * @param iprule whether this is an IP rule
414     */
415     private ACLRule(boolean allow, String expression, short[] ipaddr, boolean iprule) {
416     _allow = allow;
417     _expression = expression;
418     _ipaddr = ipaddr;
419     _iprule = iprule;
420     }
421    
422     /**
423     * Returns a String representation of this rule.
424     *
425     * @return A String representation of this rule.
426     */
427     public String toString() {
428     if(_allow) {
429     return _expression + "=ALLOW";
430     }
431     else {
432     return _expression + "=DENY";
433     }
434     }
435    
436     /**
437     * Whether this is an ALLOW or DENY rule.
438     */
439     private boolean _allow;
440    
441     /**
442     * What this rule matches.
443     */
444     private String _expression;
445    
446     /**
447     * The IP wildcard, only valid if this
448     * is an IP rule.
449     */
450     private short[] _ipaddr;
451    
452     /**
453     * Whether this is an IP rule.
454     */
455     private boolean _iprule;
456    
457     }
458    
459     }