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.8
Committed: Sun Aug 1 10:41:08 2004 UTC (19 years, 8 months ago) by tdb
Branch: MAIN
CVS Tags: HEAD
Changes since 1.7: +3 -3 lines
Log Message:
Catch a lot of old URL's and update them. Also remove a couple of old files
that aren't used.

File Contents

# Content
1 /*
2 * i-scream central monitoring system
3 * http://www.i-scream.org
4 * 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 //---PACKAGE DECLARATION---
22 package uk.org.iscream.cms.util;
23
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 * @version $Id: ACL.java,v 1.7 2003/02/05 14:27:58 tdb Exp $
40 */
41 public class ACL implements Serializable {
42
43 //---FINAL ATTRIBUTES---
44
45 /**
46 * The current CVS revision of this class
47 */
48 public static final String REVISION = "$Revision: 1.7 $";
49
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
60 /**
61 * default setting for the default mode for a new ACL.
62 */
63 public static final boolean DEFMODE = ACL.ALLOW;
64
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 // default to DEFMODE
76 this(DEFMODE);
77 }
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 setDefaultMode(defaultMode);
88 }
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 * specified in the String.
102 *
103 * @param acl a String representation of the ACL.
104 */
105 public ACL(String acl) {
106 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 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 setDefaultMode(ACL.ALLOW);
179 }
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 setDefaultMode(ACL.DENY);
188 }
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 }
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 * 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 * 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 * @return the result of the comparison
377 */
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 private boolean _defaultMode = DEFMODE;
417
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 }