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.7
Committed: Wed Feb 5 14:27:58 2003 UTC (21 years, 2 months ago) by tdb
Branch: MAIN
Changes since 1.6: +3 -3 lines
Log Message:
Util package has been pulled out of the server. Next step will be to modify
the server and conient (and anything else?) to use this instead. New
package name is uk.org.iscream.cms.util. All the java files were moved with
a repo copy, so they retain their history.

File Contents

# Content
1 /*
2 * i-scream central monitoring system
3 * http://www.i-scream.org.uk
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.6 2002/05/21 16:47:18 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.6 $";
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 }