ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/i-scream/experimental/server/ACL/ACL.java
Revision: 1.9
Committed: Mon Dec 31 19:19:03 2001 UTC (22 years, 4 months ago) by tdb
Branch: MAIN
Changes since 1.8: +44 -22 lines
Log Message:
Some tidying up. Removed debugging code. Added required comments.

File Contents

# Content
1 //---PACKAGE DECLARATION---
2 //package uk.org.iscream.cms.server.util;
3
4 //---IMPORTS---
5 import uk.org.iscream.cms.server.util.*;
6 import java.util.ArrayList;
7 import java.util.StringTokenizer;
8 import java.net.InetAddress;
9 import java.io.Serializable;
10
11 /**
12 * Access Control List for use primarily
13 * with the ACLServerSocket. It could, however
14 * have other uses as it has a fairly generic
15 * behaviour. Rules are added using the add
16 * method, and then checks can be made using
17 * the relevant check method.
18 *
19 * @author $Author: tdb $
20 * @version $Id: ACL.java,v 1.8 2001/12/31 02:57:00 tdb Exp $
21 */
22 public class ACL implements Serializable {
23
24 //---FINAL ATTRIBUTES---
25
26 /**
27 * The current CVS revision of this class
28 */
29 public static final String REVISION = "$Revision: 1.8 $";
30
31 /**
32 * static to be used when adding an ALLOW rule to the ACL.
33 */
34 public static final boolean ALLOW = true;
35
36 /**
37 * static to be used when adding a DENY rule to the ACL.
38 */
39 public static final boolean DENY = false;
40
41 //---STATIC METHODS---
42
43 //---CONSTRUCTORS---
44
45 /**
46 * Construct a new Access Control List. The default
47 * mode is to ALLOW anything that isn't explicitly
48 * blocked by a rule.
49 */
50 public ACL() {
51 // default to ACL.ALLOW
52 this(ACL.ALLOW);
53 }
54
55 /**
56 * Construct a new Access Control List with a given
57 * default mode. This mode specifies what should
58 * happen if a check does not match any rules.
59 *
60 * @param defaultMode the default mode for non-matched checks
61 */
62 public ACL(boolean defaultMode) {
63 _defaultMode = defaultMode;
64 }
65
66 //---PUBLIC METHODS---
67
68 /**
69 * Add a new rule to the ACL immediately after the
70 * previous rule. The rule can either be an ACL.ALLOW
71 * rule, or an ACL.DENY rule. The expression can
72 * contain a wildcard (a * only). Rules can only be
73 * added to the end of the list.
74 *
75 * param allow whether this is an ALLOW or DENY rule
76 * param expression what this rule matches using wildcards
77 */
78 public void add(boolean allow, String expression) {
79 // default to expecting it to be an IP
80 // we will try to disprove this :)
81 boolean ip = true;
82 short[] ipaddr = {-1, -1, -1, -1};
83 int i = 0;
84 String s = "";
85 // tokenize the expression on fullstops, so we can break
86 // up the quads of an IP (if it's an IP!)
87 StringTokenizer st = new StringTokenizer(expression, ".");
88 while(st.hasMoreTokens() && i++ < 4) {
89 s = st.nextToken();
90 // if it's a wildcard, we'll skip to the next one
91 // as no more checks are required
92 if(s.equals("*")) {
93 continue;
94 }
95 // attempt to parse it into a short
96 try {
97 short n = Short.parseShort(s);
98 // if it's an int but outside of the
99 // valid range, it can't be an IP
100 if(n < 0 || n > 255) {
101 ip = false;
102 // give up checking further
103 break;
104 }
105 ipaddr[i-1] = n;
106 }
107 // if it didn't parse as an int it can't be an IP
108 catch (NumberFormatException e) {
109 ip = false;
110 // give up checking further
111 break;
112 }
113 }
114 // we've done 4 parts, so if there's any
115 // more this can't be an IP
116 if(st.hasMoreTokens()) {
117 ip = false;
118 }
119 // if we've done less than 4, see if the last one
120 // was a wildcard - if it isn't then it's not an IP
121 // -- this allows 129.12.*
122 if(i < 4 && !s.equals("*")) {
123 ip = false;
124 }
125 // if we had one or less entries it can't be an IP
126 // -- this disallows * matching as an IP due
127 // to the rule above
128 if(i <= 1) {
129 ip = false;
130 }
131 // add the rule to our array
132 _acl.add(new ACLRule(allow, expression, ipaddr, ip));
133 }
134
135 /**
136 * Check to see if a string is permitted by the
137 * ACL. Useful for testing, and non-Socket uses
138 * of this class.
139 *
140 * @param address the string to check
141 * @return whether the address was permitted by the ACL
142 */
143 public boolean check(String address) {
144 for(int i=0; i < _acl.size(); i++) {
145 ACLRule rule = (ACLRule) _acl.get(i);
146 if(StringUtils.wildcardCheck(address, rule._expression)) {
147 return rule._allow;
148 }
149 }
150 return _defaultMode;
151 }
152
153 /**
154 * Check to see if an InetAddress is permitted
155 * by the ACL. Perfect for Socket uses of this
156 * class. A rule will either be for a name, or
157 * an IP address (this is determined in the add
158 * method), and the appropriate comparison will
159 * be performed.
160 *
161 * @param address the InetAddress to check
162 * @return whether the InetAddress was permitted by the ACL
163 */
164 public boolean check(InetAddress address) {
165 // gather the details first
166 String hostname = address.getHostName();
167 String ip = address.getHostAddress();
168 short[] ipaddr = ipStringToShort(ip);
169 // check each rule against this InetAddress
170 for(int i=0; i < _acl.size(); i++) {
171 ACLRule rule = (ACLRule) _acl.get(i);
172 if(rule._iprule) {
173 // if this is an IP rule do a short comparison
174 if(compareShorts(ipaddr, rule._ipaddr)) {
175 return rule._allow;
176 }
177 }
178 else {
179 // if not do a full blown String comparsion
180 if(StringUtils.wildcardCheck(hostname, rule._expression)) {
181 return rule._allow;
182 }
183 }
184
185 }
186 // if we haven't matched a rule, return the default
187 return _defaultMode;
188 }
189
190 /**
191 * Gives a String representation of this ACL.
192 *
193 * @return A String representation of this ACL.
194 */
195 public String toString() {
196 StringBuffer acl = new StringBuffer();
197 // put in the i-scream toString code
198 acl.append(FormatName.getName(_name, getClass().getName(), REVISION));
199 acl.append("{");
200 // put the value of each Rule in the result
201 for(int i=0; i < _acl.size(); i++) {
202 acl.append((ACLRule) _acl.get(i));
203 acl.append(",");
204 }
205 // put the default mode in the result
206 if(_defaultMode) {
207 acl.append("DEFAULT=ALLOW");
208 }
209 else {
210 acl.append("DEFAULT=DENY");
211 }
212 acl.append("}");
213 return acl.toString();
214 }
215
216 //---PRIVATE METHODS---
217
218 /**
219 * Converts an IP address in String format into
220 * a short array of length 4. Any wildcards, *,
221 * found in the IP address are represented by
222 * a -1.
223 *
224 * @param ip The IP address in String format
225 * @return The IP address in a short[]
226 */
227 private short[] ipStringToShort(String ip) {
228 short[] ipaddr = {-1, -1, -1, -1};
229 StringTokenizer st = new StringTokenizer(ip, ".");
230 for(int i=0; i < 4 && st.hasMoreTokens(); i++) {
231 try {
232 ipaddr[i] = Short.parseShort(st.nextToken());
233 }
234 catch(NumberFormatException e) {
235 // do nothing?
236 // we just want to leave it as -1
237 // -- actually, maybe we want to do more checks in here?
238 // although in this code context it'll probably be ok,
239 // it might be worth verifying wildcards and that the
240 // number is in range...
241 }
242 }
243 return ipaddr;
244 }
245
246 /**
247 * Compares two short arrays. The array can contain a -1, which
248 * will always match any value -- it's a wildcard. They must be
249 * the same length to match. At the moment the order of the
250 * parameters does not matter.
251 *
252 * @param first The first array to compare
253 * @param second The second array to compare
254 * @result the result of the comparison
255 */
256 private boolean compareShorts(short[] first, short[] second) {
257 if(first.length != second.length) {
258 return false;
259 }
260 for(int i=0; i < first.length; i++) {
261 // -- might want to consider specify which is the wildcard one?
262 if(first[i] == -1 || second[i] == -1) {
263 continue;
264 }
265 if(first[i] != second[i]) {
266 return false;
267 }
268 return true;
269 }
270
271 //---ACCESSOR/MUTATOR METHODS---
272
273 //---ATTRIBUTES---
274
275 /**
276 * This is the friendly identifier of the
277 * component this class is running in.
278 * eg, a Filter may be called "filter1",
279 * If this class does not have an owning
280 * component, a name from the configuration
281 * can be placed here. This name could also
282 * be changed to null for utility classes.
283 */
284 private String _name = null;
285
286 /**
287 * The ACL is stored in this ArrayList.
288 */
289 private ArrayList _acl = new ArrayList();
290
291 /**
292 * The default mode of this ACL.
293 */
294 private boolean _defaultMode;
295
296 //---STATIC ATTRIBUTES---
297
298 //---INNER CLASSES---
299
300 /**
301 * Wrapper class for an ACL rule.
302 */
303 private class ACLRule implements Serializable {
304
305 /**
306 * Construct an ACL rule.
307 *
308 * @param allow whether this is an ALLOW or DENY rule
309 * @param expression what this rule matches
310 * @param ipaddr the IP address wildcard this rule matches if it's an IP rule
311 * @param iprule whether this is an IP rule
312 */
313 private ACLRule(boolean allow, String expression, short[] ipaddr, boolean iprule) {
314 _allow = allow;
315 _expression = expression;
316 _ipaddr = ipaddr;
317 _iprule = iprule;
318 }
319
320 /**
321 * Returns a String representation of this rule.
322 *
323 * @return A String representation of this rule.
324 */
325 public String toString() {
326 if(_allow) {
327 return _expression + "=ALLOW";
328 }
329 else {
330 return _expression + "=DENY";
331 }
332 }
333
334 /**
335 * Whether this is an ALLOW or DENY rule.
336 */
337 private boolean _allow;
338
339 /**
340 * What this rule matches.
341 */
342 private String _expression;
343
344 /**
345 * The IP wildcard, only valid if this
346 * is an IP rule.
347 */
348 private short[] _ipaddr;
349
350 /**
351 * Whether this is an IP rule.
352 */
353 private boolean _iprule;
354
355 }
356
357 }