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.5
Committed: Sat May 18 18:16:03 2002 UTC (21 years, 11 months ago) by tdb
Branch: MAIN
Changes since 1.4: +21 -2 lines
Log Message:
i-scream is now licensed under the GPL. I've added the GPL headers to every
source file, and put a full copy of the license in the appropriate places.
I think I've covered everything. This is going to be a mad commit ;)

File Contents

# Content
1 /*
2 * i-scream central monitoring system
3 * Copyright (C) 2000-2002 i-scream
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * as published by the Free Software Foundation; either version 2
8 * of the License, or (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 */
19
20 //---PACKAGE DECLARATION---
21 package uk.org.iscream.cms.server.util;
22
23 //---IMPORTS---
24 import java.util.ArrayList;
25 import java.util.StringTokenizer;
26 import java.net.InetAddress;
27 import java.io.Serializable;
28
29 /**
30 * Access Control List for use primarily
31 * with the ACLServerSocket. It could, however
32 * have other uses as it has a fairly generic
33 * behaviour. Rules are added using the add
34 * method, and then checks can be made using
35 * the relevant check method.
36 *
37 * @author $Author: tdb $
38 * @version $Id: ACL.java,v 1.4 2002/03/20 17:03:35 tdb Exp $
39 */
40 public class ACL implements Serializable {
41
42 //---FINAL ATTRIBUTES---
43
44 /**
45 * The current CVS revision of this class
46 */
47 public static final String REVISION = "$Revision: 1.4 $";
48
49 /**
50 * static to be used when adding an ALLOW rule to the ACL.
51 */
52 public static final boolean ALLOW = true;
53
54 /**
55 * static to be used when adding a DENY rule to the ACL.
56 */
57 public static final boolean DENY = false;
58
59 /**
60 * default setting for the default mode for a new ACL.
61 */
62 public static final boolean DEFMODE = ACL.ALLOW;
63
64 //---STATIC METHODS---
65
66 //---CONSTRUCTORS---
67
68 /**
69 * Construct a new Access Control List. The default
70 * mode is to ALLOW anything that isn't explicitly
71 * blocked by a rule.
72 */
73 public ACL() {
74 // default to DEFMODE
75 this(DEFMODE);
76 }
77
78 /**
79 * Construct a new Access Control List with a given
80 * default mode. This mode specifies what should
81 * happen if a check does not match any rules.
82 *
83 * @param defaultMode the default mode for non-matched checks
84 */
85 public ACL(boolean defaultMode) {
86 setDefaultMode(defaultMode);
87 }
88
89 /**
90 * Construct a new Access Control List with a given
91 * String representation of the ACL rules. The String
92 * should be of the format:
93 * expression:rule;expression:rule;expression:rule...
94 * Where expression is a wildcard to match against, and
95 * rule is either 'ALLOW' or 'DENY'. There is a special
96 * expression of 'DEFAULT' which represents the default
97 * rule (what should happen if no expression is matched
98 * when performing a check).
99 * The default mode is set to ALLOW if one is not
100 * specified in the String.
101 *
102 * @param acl a String representation of the ACL.
103 */
104 public ACL(String acl) {
105 setDefaultMode(DEFMODE);
106 add(acl);
107 }
108
109 //---PUBLIC METHODS---
110
111 /**
112 * Add a new rule to the ACL immediately after the
113 * previous rule. The rule can either be an ACL.ALLOW
114 * rule, or an ACL.DENY rule. The expression can
115 * contain a wildcard (a * only). Rules can only be
116 * added to the end of the list.
117 *
118 * param allow whether this is an ALLOW or DENY rule
119 * param expression what this rule matches using wildcards
120 */
121 public void add(boolean allow, String expression) {
122 // try and convert the expression into an IP address
123 short[] ipaddr = ipStringToShort(expression);
124 // a result of null means it's not an IP address
125 // add either a name rule or an IP rule
126 if(ipaddr != null) {
127 _acl.add(new ACLRule(allow, expression, ipaddr, true));
128 }
129 else {
130 _acl.add(new ACLRule(allow, expression, ipaddr, false));
131 }
132 }
133
134 /**
135 * Add some new rules to the Access Control List in
136 * the form of a String. The String should be of the
137 * following format:
138 * expression:rule;expression:rule;expression:rule...
139 * Where expression is a wildcard to match against, and
140 * rule is either 'ALLOW' or 'DENY'. There is a special
141 * expression of 'DEFAULT' which represents the default
142 * rule (what should happen if no expression is matched
143 * when performing a check).
144 *
145 * @param acl a String representation of the ACL.
146 */
147 public void add(String acl) {
148 if(acl != null) {
149 // split the String into expression:rule parts
150 StringTokenizer st1 = new StringTokenizer(acl, ";");
151 while(st1.hasMoreTokens()) {
152 String token1 = st1.nextToken();
153 // if it doesn't have a :, it's not the correct format
154 if(token1.indexOf(":") != -1) {
155 // split into expression and rule part
156 StringTokenizer st2 = new StringTokenizer(token1, ":");
157 String expression = "";
158 String rule = "";
159 if(st2.hasMoreTokens()) {
160 expression = st2.nextToken();
161 }
162 else {
163 // mall-formed?
164 continue;
165 }
166 if(st2.hasMoreTokens()) {
167 rule = st2.nextToken();
168 }
169 else {
170 // mall-formed?
171 continue;
172 }
173 // check to see what sort of rule
174 if(rule.equals("ALLOW")) {
175 // case for special 'DEFAULT' expression
176 if(expression.equals("DEFAULT")) {
177 setDefaultMode(ACL.ALLOW);
178 }
179 else {
180 add(ACL.ALLOW, expression);
181 }
182 }
183 else if(rule.equals("DENY")) {
184 // case for special 'DEFAULT' expression
185 if(expression.equals("DEFAULT")) {
186 setDefaultMode(ACL.DENY);
187 }
188 else {
189 add(ACL.DENY, expression);
190 }
191 }
192 // if it's not ALLOW or DENY, it's not a
193 // proper rule, so we'll ignore it
194 }
195 }
196 }
197 }
198
199 /**
200 * Check to see if a string is permitted by the
201 * ACL. Useful for testing, and non-Socket uses
202 * of this class.
203 *
204 * @param address the string to check
205 * @return whether the address was permitted by the ACL
206 */
207 public boolean check(String address) {
208 for(int i=0; i < _acl.size(); i++) {
209 ACLRule rule = (ACLRule) _acl.get(i);
210 if(StringUtils.wildcardMatch(address, rule._expression)) {
211 return rule._allow;
212 }
213 }
214 return _defaultMode;
215 }
216
217 /**
218 * Check to see if an InetAddress is permitted
219 * by the ACL. Perfect for Socket uses of this
220 * class. A rule will either be for a name, or
221 * an IP address (this is determined in the add
222 * method), and the appropriate comparison will
223 * be performed.
224 *
225 * @param address the InetAddress to check
226 * @return whether the InetAddress was permitted by the ACL
227 */
228 public boolean check(InetAddress address) {
229 // gather the details first
230 String hostname = address.getHostName();
231 String ip = address.getHostAddress();
232 short[] ipaddr = ipStringToShort(ip);
233 // check each rule against this InetAddress
234 for(int i=0; i < _acl.size(); i++) {
235 ACLRule rule = (ACLRule) _acl.get(i);
236 if(rule._iprule) {
237 // if this is an IP rule do a short comparison
238 // must specify the wildcarded rule first
239 if(compareShorts(rule._ipaddr, ipaddr)) {
240 return rule._allow;
241 }
242 }
243 else {
244 // if not do a full blown String comparsion
245 if(StringUtils.wildcardMatch(hostname, rule._expression)) {
246 return rule._allow;
247 }
248 }
249
250 }
251 // if we haven't matched a rule, return the default
252 return _defaultMode;
253 }
254
255 /**
256 * Clears the ACL and resets the default mode.
257 */
258 public void clear() {
259 // just clear out our underlying ArrayList
260 // containing our ACL objects
261 _acl.clear();
262 // and reset the default mode to the default
263 setDefaultMode(DEFMODE);
264 }
265
266 /**
267 * Changes the default mode of the ACL. This is what
268 * the check will return if it does not find an explict
269 * rule to match against.
270 *
271 * @param defaultMode the new default mode
272 */
273 public void setDefaultMode(boolean defaultMode) {
274 _defaultMode = defaultMode;
275 }
276
277 /**
278 * Gives a String representation of this ACL.
279 *
280 * @return A String representation of this ACL.
281 */
282 public String toString() {
283 StringBuffer acl = new StringBuffer();
284 // put in the i-scream toString code
285 acl.append(FormatName.getName(_name, getClass().getName(), REVISION));
286 acl.append("{");
287 // put the value of each Rule in the result
288 for(int i=0; i < _acl.size(); i++) {
289 acl.append((ACLRule) _acl.get(i));
290 acl.append(",");
291 }
292 // put the default mode in the result
293 if(_defaultMode) {
294 acl.append("DEFAULT=ALLOW");
295 }
296 else {
297 acl.append("DEFAULT=DENY");
298 }
299 acl.append("}");
300 return acl.toString();
301 }
302
303 //---PRIVATE METHODS---
304
305 /**
306 * Converts an IP address in String format into
307 * a short array of length 4. Any wildcards, *,
308 * found in the IP address are represented by
309 * a -1. If the given String is not an IP address
310 * null is returned instead.
311 *
312 * @param ip The IP address in String format
313 * @return The IP address in a short[]
314 */
315 private short[] ipStringToShort(String ip) {
316 // default to expecting it to be an IP
317 // we will try to disprove this :)
318 short[] ipaddr = {-1, -1, -1, -1};
319 int i = 0;
320 String s = "";
321 // tokenize the String on fullstops, so we can break
322 // up the quads of an IP (if it's an IP!)
323 StringTokenizer st = new StringTokenizer(ip, ".");
324 while(st.hasMoreTokens() && i++ < 4) {
325 s = st.nextToken();
326 // if it's a wildcard, we'll skip to the next one
327 // as no more checks are required
328 if(s.equals("*")) {
329 continue;
330 }
331 // attempt to parse it into a short
332 try {
333 short n = Short.parseShort(s);
334 // if it's an int but outside of the
335 // valid range, it can't be an IP
336 if(n < 0 || n > 255) {
337 // give up checking further
338 return null;
339 }
340 ipaddr[i-1] = n;
341 }
342 // if it didn't parse as a short it can't be an IP
343 catch (NumberFormatException e) {
344 // give up checking further
345 return null;
346 }
347 }
348 // we've done 4 parts, so if there's any
349 // more this can't be an IP
350 if(st.hasMoreTokens()) {
351 return null;
352 }
353 // if we've done less than 4, see if the last one
354 // was a wildcard - if it isn't then it's not an IP
355 // -- this allows 129.12.*
356 if(i < 4 && !s.equals("*")) {
357 return null;
358 }
359 // if we had one or less entries it can't be an IP
360 // -- this disallows * matching as an IP due
361 // to the rule above
362 if(i <= 1) {
363 return null;
364 }
365 return ipaddr;
366 }
367
368 /**
369 * Compares two short arrays. The first array can contain a -1,
370 * which will always match any value -- it's a wildcard.
371 * They must be the same length to match.
372 *
373 * @param first The first array to compare (with -1 wildcard if required)
374 * @param second The second array to compare
375 * @return the result of the comparison
376 */
377 private boolean compareShorts(short[] first, short[] second) {
378 if(first.length != second.length) {
379 return false;
380 }
381 for(int i=0; i < first.length; i++) {
382 if(first[i] == -1) {
383 continue;
384 }
385 if(first[i] != second[i]) {
386 return false;
387 }
388 }
389 return true;
390 }
391
392 //---ACCESSOR/MUTATOR METHODS---
393
394 //---ATTRIBUTES---
395
396 /**
397 * This is the friendly identifier of the
398 * component this class is running in.
399 * eg, a Filter may be called "filter1",
400 * If this class does not have an owning
401 * component, a name from the configuration
402 * can be placed here. This name could also
403 * be changed to null for utility classes.
404 */
405 private String _name = null;
406
407 /**
408 * The ACL is stored in this ArrayList.
409 */
410 private ArrayList _acl = new ArrayList();
411
412 /**
413 * The default mode of this ACL.
414 */
415 private boolean _defaultMode = DEFMODE;
416
417 //---STATIC ATTRIBUTES---
418
419 //---INNER CLASSES---
420
421 /**
422 * Wrapper class for an ACL rule.
423 */
424 private class ACLRule implements Serializable {
425
426 /**
427 * Construct an ACL rule.
428 *
429 * @param allow whether this is an ALLOW or DENY rule
430 * @param expression what this rule matches
431 * @param ipaddr the IP address wildcard this rule matches if it's an IP rule
432 * @param iprule whether this is an IP rule
433 */
434 private ACLRule(boolean allow, String expression, short[] ipaddr, boolean iprule) {
435 _allow = allow;
436 _expression = expression;
437 _ipaddr = ipaddr;
438 _iprule = iprule;
439 }
440
441 /**
442 * Returns a String representation of this rule.
443 *
444 * @return A String representation of this rule.
445 */
446 public String toString() {
447 if(_allow) {
448 return _expression + "=ALLOW";
449 }
450 else {
451 return _expression + "=DENY";
452 }
453 }
454
455 /**
456 * Whether this is an ALLOW or DENY rule.
457 */
458 private boolean _allow;
459
460 /**
461 * What this rule matches.
462 */
463 private String _expression;
464
465 /**
466 * The IP wildcard, only valid if this
467 * is an IP rule.
468 */
469 private short[] _ipaddr;
470
471 /**
472 * Whether this is an IP rule.
473 */
474 private boolean _iprule;
475
476 }
477
478 }