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.7
Committed: Wed Feb 5 14:27:58 2003 UTC (21 years, 2 months ago) by tdb
Branch: MAIN
Changes since 1.6: +3 -3 lines
Log Message:
Util package has been pulled out of the server. Next step will be to modify
the server and conient (and anything else?) to use this instead. New
package name is uk.org.iscream.cms.util. All the java files were moved with
a repo copy, so they retain their history.

File Contents

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