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.5
Committed: Sat May 18 18:16:03 2002 UTC (21 years, 11 months ago) by tdb
Branch: MAIN
Changes since 1.4: +21 -2 lines
Log Message:
i-scream is now licensed under the GPL. I've added the GPL headers to every
source file, and put a full copy of the license in the appropriate places.
I think I've covered everything. This is going to be a mad commit ;)

File Contents

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