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.3
Committed: Tue Mar 19 12:18:22 2002 UTC (22 years, 1 month ago) by tdb
Branch: MAIN
Changes since 1.2: +71 -2 lines
Log Message:
Added new constructor to take a String of the form:
    "expression:rule;expression:rule;expression:rule"
This allows an ACL to be constructed from a single String, which is nice in
the context of i-scream because we can read that from the config :)

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