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.3
Committed: Tue Mar 19 12:18:22 2002 UTC (22 years, 1 month ago) by tdb
Branch: MAIN
Changes since 1.2: +71 -2 lines
Log Message:
Added new constructor to take a String of the form:
    "expression:rule;expression:rule;expression:rule"
This allows an ACL to be constructed from a single String, which is nice in
the context of i-scream because we can read that from the config :)

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.2 2002/02/15 22:27:15 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.2 $";
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 //---STATIC METHODS---
41
42 //---CONSTRUCTORS---
43
44 /**
45 * Construct a new Access Control List. The default
46 * mode is to ALLOW anything that isn't explicitly
47 * blocked by a rule.
48 */
49 public ACL() {
50 // default to ACL.ALLOW
51 this(ACL.ALLOW);
52 }
53
54 /**
55 * Construct a new Access Control List with a given
56 * default mode. This mode specifies what should
57 * happen if a check does not match any rules.
58 *
59 * @param defaultMode the default mode for non-matched checks
60 */
61 public ACL(boolean defaultMode) {
62 _defaultMode = defaultMode;
63 }
64
65 /**
66 * Construct a new Access Control List with a given
67 * String representation of the ACL rules. The String
68 * should be of the format:
69 * expression:rule;expression:rule;expression:rule...
70 * Where expression is a wildcard to match against, and
71 * rule is either 'ALLOW' or 'DENY'. There is a special
72 * expression of 'DEFAULT' which represents the default
73 * rule (what should happen if no expression is matched
74 * when performing a check).
75 * The default mode is set to ALLOW if one is not
76 * specified.
77 *
78 * @param acl a String representation of the ACL.
79 */
80 public ACL(String acl) {
81 // default to ALLOW
82 _defaultMode = ACL.ALLOW;
83 if(acl != null) {
84 // split the String into expression:rule parts
85 StringTokenizer st1 = new StringTokenizer(acl, ";");
86 while(st1.hasMoreTokens()) {
87 String token1 = st1.nextToken();
88 // if it doesn't have a :, it's not the correct format
89 if(token1.indexOf(":") != -1) {
90 // split into expression and rule part
91 StringTokenizer st2 = new StringTokenizer(token1, ":");
92 String expression = "";
93 String rule = "";
94 if(st2.hasMoreTokens()) {
95 expression = st2.nextToken();
96 }
97 else {
98 // mall-formed?
99 continue;
100 }
101 if(st2.hasMoreTokens()) {
102 rule = st2.nextToken();
103 }
104 else {
105 // mall-formed?
106 continue;
107 }
108 // check to see what sort of rule
109 if(rule.equals("ALLOW")) {
110 // case for special 'DEFAULT' expression
111 if(expression.equals("DEFAULT")) {
112 _defaultMode = ACL.ALLOW;
113 }
114 else {
115 add(ACL.ALLOW, expression);
116 }
117 }
118 else if(rule.equals("DENY")) {
119 // case for special 'DEFAULT' expression
120 if(expression.equals("DEFAULT")) {
121 _defaultMode = ACL.DENY;
122 }
123 else {
124 add(ACL.DENY, expression);
125 }
126 }
127 // if it's not ALLOW or DENY, it's not a
128 // proper rule, so we'll ignore it
129 }
130 }
131 }
132 }
133
134 //---PUBLIC METHODS---
135
136 /**
137 * Add a new rule to the ACL immediately after the
138 * previous rule. The rule can either be an ACL.ALLOW
139 * rule, or an ACL.DENY rule. The expression can
140 * contain a wildcard (a * only). Rules can only be
141 * added to the end of the list.
142 *
143 * param allow whether this is an ALLOW or DENY rule
144 * param expression what this rule matches using wildcards
145 */
146 public void add(boolean allow, String expression) {
147 // try and convert the expression into an IP address
148 short[] ipaddr = ipStringToShort(expression);
149 // a result of null means it's not an IP address
150 // add either a name rule or an IP rule
151 if(ipaddr != null) {
152 _acl.add(new ACLRule(allow, expression, ipaddr, true));
153 }
154 else {
155 _acl.add(new ACLRule(allow, expression, ipaddr, false));
156 }
157 }
158
159 /**
160 * Check to see if a string is permitted by the
161 * ACL. Useful for testing, and non-Socket uses
162 * of this class.
163 *
164 * @param address the string to check
165 * @return whether the address was permitted by the ACL
166 */
167 public boolean check(String address) {
168 for(int i=0; i < _acl.size(); i++) {
169 ACLRule rule = (ACLRule) _acl.get(i);
170 if(StringUtils.wildcardMatch(address, rule._expression)) {
171 return rule._allow;
172 }
173 }
174 return _defaultMode;
175 }
176
177 /**
178 * Check to see if an InetAddress is permitted
179 * by the ACL. Perfect for Socket uses of this
180 * class. A rule will either be for a name, or
181 * an IP address (this is determined in the add
182 * method), and the appropriate comparison will
183 * be performed.
184 *
185 * @param address the InetAddress to check
186 * @return whether the InetAddress was permitted by the ACL
187 */
188 public boolean check(InetAddress address) {
189 // gather the details first
190 String hostname = address.getHostName();
191 String ip = address.getHostAddress();
192 short[] ipaddr = ipStringToShort(ip);
193 // check each rule against this InetAddress
194 for(int i=0; i < _acl.size(); i++) {
195 ACLRule rule = (ACLRule) _acl.get(i);
196 if(rule._iprule) {
197 // if this is an IP rule do a short comparison
198 // must specify the wildcarded rule first
199 if(compareShorts(rule._ipaddr, ipaddr)) {
200 return rule._allow;
201 }
202 }
203 else {
204 // if not do a full blown String comparsion
205 if(StringUtils.wildcardMatch(hostname, rule._expression)) {
206 return rule._allow;
207 }
208 }
209
210 }
211 // if we haven't matched a rule, return the default
212 return _defaultMode;
213 }
214
215 /**
216 * Gives a String representation of this ACL.
217 *
218 * @return A String representation of this ACL.
219 */
220 public String toString() {
221 StringBuffer acl = new StringBuffer();
222 // put in the i-scream toString code
223 acl.append(FormatName.getName(_name, getClass().getName(), REVISION));
224 acl.append("{");
225 // put the value of each Rule in the result
226 for(int i=0; i < _acl.size(); i++) {
227 acl.append((ACLRule) _acl.get(i));
228 acl.append(",");
229 }
230 // put the default mode in the result
231 if(_defaultMode) {
232 acl.append("DEFAULT=ALLOW");
233 }
234 else {
235 acl.append("DEFAULT=DENY");
236 }
237 acl.append("}");
238 return acl.toString();
239 }
240
241 //---PRIVATE METHODS---
242
243 /**
244 * Converts an IP address in String format into
245 * a short array of length 4. Any wildcards, *,
246 * found in the IP address are represented by
247 * a -1. If the given String is not an IP address
248 * null is returned instead.
249 *
250 * @param ip The IP address in String format
251 * @return The IP address in a short[]
252 */
253 private short[] ipStringToShort(String ip) {
254 // default to expecting it to be an IP
255 // we will try to disprove this :)
256 short[] ipaddr = {-1, -1, -1, -1};
257 int i = 0;
258 String s = "";
259 // tokenize the String on fullstops, so we can break
260 // up the quads of an IP (if it's an IP!)
261 StringTokenizer st = new StringTokenizer(ip, ".");
262 while(st.hasMoreTokens() && i++ < 4) {
263 s = st.nextToken();
264 // if it's a wildcard, we'll skip to the next one
265 // as no more checks are required
266 if(s.equals("*")) {
267 continue;
268 }
269 // attempt to parse it into a short
270 try {
271 short n = Short.parseShort(s);
272 // if it's an int but outside of the
273 // valid range, it can't be an IP
274 if(n < 0 || n > 255) {
275 // give up checking further
276 return null;
277 }
278 ipaddr[i-1] = n;
279 }
280 // if it didn't parse as a short it can't be an IP
281 catch (NumberFormatException e) {
282 // give up checking further
283 return null;
284 }
285 }
286 // we've done 4 parts, so if there's any
287 // more this can't be an IP
288 if(st.hasMoreTokens()) {
289 return null;
290 }
291 // if we've done less than 4, see if the last one
292 // was a wildcard - if it isn't then it's not an IP
293 // -- this allows 129.12.*
294 if(i < 4 && !s.equals("*")) {
295 return null;
296 }
297 // if we had one or less entries it can't be an IP
298 // -- this disallows * matching as an IP due
299 // to the rule above
300 if(i <= 1) {
301 return null;
302 }
303 return ipaddr;
304 }
305
306 /**
307 * Compares two short arrays. The first array can contain a -1,
308 * which will always match any value -- it's a wildcard.
309 * They must be the same length to match.
310 *
311 * @param first The first array to compare (with -1 wildcard if required)
312 * @param second The second array to compare
313 * @return the result of the comparison
314 */
315 private boolean compareShorts(short[] first, short[] second) {
316 if(first.length != second.length) {
317 return false;
318 }
319 for(int i=0; i < first.length; i++) {
320 if(first[i] == -1) {
321 continue;
322 }
323 if(first[i] != second[i]) {
324 return false;
325 }
326 }
327 return true;
328 }
329
330 //---ACCESSOR/MUTATOR METHODS---
331
332 //---ATTRIBUTES---
333
334 /**
335 * This is the friendly identifier of the
336 * component this class is running in.
337 * eg, a Filter may be called "filter1",
338 * If this class does not have an owning
339 * component, a name from the configuration
340 * can be placed here. This name could also
341 * be changed to null for utility classes.
342 */
343 private String _name = null;
344
345 /**
346 * The ACL is stored in this ArrayList.
347 */
348 private ArrayList _acl = new ArrayList();
349
350 /**
351 * The default mode of this ACL.
352 */
353 private boolean _defaultMode;
354
355 //---STATIC ATTRIBUTES---
356
357 //---INNER CLASSES---
358
359 /**
360 * Wrapper class for an ACL rule.
361 */
362 private class ACLRule implements Serializable {
363
364 /**
365 * Construct an ACL rule.
366 *
367 * @param allow whether this is an ALLOW or DENY rule
368 * @param expression what this rule matches
369 * @param ipaddr the IP address wildcard this rule matches if it's an IP rule
370 * @param iprule whether this is an IP rule
371 */
372 private ACLRule(boolean allow, String expression, short[] ipaddr, boolean iprule) {
373 _allow = allow;
374 _expression = expression;
375 _ipaddr = ipaddr;
376 _iprule = iprule;
377 }
378
379 /**
380 * Returns a String representation of this rule.
381 *
382 * @return A String representation of this rule.
383 */
384 public String toString() {
385 if(_allow) {
386 return _expression + "=ALLOW";
387 }
388 else {
389 return _expression + "=DENY";
390 }
391 }
392
393 /**
394 * Whether this is an ALLOW or DENY rule.
395 */
396 private boolean _allow;
397
398 /**
399 * What this rule matches.
400 */
401 private String _expression;
402
403 /**
404 * The IP wildcard, only valid if this
405 * is an IP rule.
406 */
407 private short[] _ipaddr;
408
409 /**
410 * Whether this is an IP rule.
411 */
412 private boolean _iprule;
413
414 }
415
416 }