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

# Content
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 * @version $Id: ACL.java,v 1.3 2002/03/19 12:18:22 tdb Exp $
20 */
21 public class ACL implements Serializable {
22
23 //---FINAL ATTRIBUTES---
24
25 /**
26 * The current CVS revision of this class
27 */
28 public static final String REVISION = "$Revision: 1.3 $";
29
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 /**
41 * default setting for the default mode for a new ACL.
42 */
43 public static final boolean DEFMODE = ACL.ALLOW;
44
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 // default to DEFMODE
56 this(DEFMODE);
57 }
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 setDefaultMode(defaultMode);
68 }
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 * specified in the String.
82 *
83 * @param acl a String representation of the ACL.
84 */
85 public ACL(String acl) {
86 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 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 setDefaultMode(ACL.ALLOW);
159 }
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 setDefaultMode(ACL.DENY);
168 }
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 }
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 * 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 * 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 * @return the result of the comparison
357 */
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 private boolean _defaultMode = DEFMODE;
397
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 }