AccessRuleList.java |
/* * $Id: AccessRuleList.java,v 1.53 2010/09/29 17:21:48 agoubard Exp $ * * See the COPYRIGHT file for redistribution and use restrictions. */ package org.xins.server; import java.util.StringTokenizer; import org.xins.common.MandatoryArgumentChecker; import org.xins.common.Utils; import org.xins.common.text.ParseException; /** * Access rule list. * * <h3>Descriptor format</h3> * * <p>An access rule list <em>descriptor</em>, a character string, can be * converted to produce an {@link AccessRuleList} object. A valid descriptor * consists of a list of access rule descriptors (see class * {@link AccessRule}) and/or access rule file descriptors (see class * {@link AccessRuleFile}), separated by semi-colon characters (<code>';'</code>). * Optionally, the rules can have any amount of whitespace (space-, tab-, * newline- and carriage return-characters), before and after them. The last * descriptor cannot end with a semi-colon. * * <h3>Descriptor examples</h3> * * <p>An example of an access rule list descriptor is: * * <blockquote><code>allow 194.134.168.213/32 *; * <br>deny 194.134.168.213/24 _*; * <br>allow 194.134.168.213/24 *; * <br>file /var/conf/file1.acl; * <br>deny 0.0.0.0/0 *</code></blockquote> * * <p>The above access control list grants the IP address 194.134.168.213 * access to all functions. Then in the second rule it denies * access to all IP addresses in the range 194.134.168.0 to 194.134.168.255 to * all functions that start with an underscore (<code>'_'</code>). Then it * allows access for those IP addresses to all other functions, then it * applies the rules in the <code>/var/conf/file1.acl</code> file and finally * all other IP addresses are denied access to any of the functions. * * @version $Revision: 1.53 $ $Date: 2010/09/29 17:21:48 $ * @author <a href="mailto:ernst@ernstdehaan.com">Ernst de Haan</a> * * @since XINS 1.0.0 */ public final class AccessRuleList implements AccessRuleContainer { /** * An empty access rule list. This field is never <code>null</code>. */ static final AccessRuleList EMPTY = new AccessRuleList(new AccessRuleContainer[0]); /** * The list of rules. Cannot be <code>null</code>. */ private AccessRuleContainer[] _rules; /** * The string representation of this instance. Cannot be <code>null</code>. */ private String _asString; /** * Flag that indicates whether this object is disposed. */ private boolean _disposed; /** * Creates a new <code>AccessRuleList</code> object. The passed * {@link AccessRuleContainer} array is assumed to be owned by the * constructor. * * @param rules * the list of rules, not <code>null</code> and should not contain any * duplicate or <code>null</code> elements; if one of these latter 2 * constraints are violated, the behaviour is undefined. * * @throws NullPointerException * if <code>rules == null</code>. */ private AccessRuleList(AccessRuleContainer[] rules) throws NullPointerException { // Count number of rules (may throw NPE) int ruleCount = rules.length; // Build string representation and log StringBuffer buffer = new StringBuffer(ruleCount * 40); if (ruleCount > 0) { String s = rules[0].toString(); buffer.append(s); Log.log_3429(0, s); for (int i = 1; i < ruleCount; i++) { s = rules[i].toString(); buffer.append(';'); buffer.append(s); Log.log_3429(i, s); } } _asString = buffer.toString(); // Store the rules _rules = rules; } /** * Parses the specified character string to construct a new * <code>AccessRuleList</code> object, with the specified watch interval * for referenced files. * * <p>If the specified interval is <code>0</code>, then no watching will be * performed. * * @param descriptor * the access rule list descriptor, the character string to parse, * cannot be <code>null</code>. * * @param interval * the interval used to check the ACL files for modification, in * seconds, must be >= 0. * * @return * an {@link AccessRuleList} instance, never <code>null</code>. * * @throws IllegalArgumentException * if <code>descriptor == null || interval < 0</code>. * * @throws ParseException * if there was a parsing error. * * @since XINS 1.1.0 */ public static final AccessRuleList parseAccessRuleList(String descriptor, int interval) throws IllegalArgumentException, ParseException { // Check preconditions MandatoryArgumentChecker.check("descriptor", descriptor); if (interval < 0) { throw new IllegalArgumentException("interval (" + interval + ") < 0"); } // First trim whitespace from the descriptor descriptor = descriptor.trim(); // Tokenize the descriptor, separator is semi-colon StringTokenizer tokenizer = new StringTokenizer(descriptor, ";"); int ruleCount = tokenizer.countTokens(); // Parse all tokens AccessRuleContainer[] rules = new AccessRuleContainer[ruleCount]; for (int i = 0; i < ruleCount; i++) { // Remove leading and trailing whitespace from the next token String token = tokenizer.nextToken().trim(); // Parse and add the rule if (token.startsWith("allow") || token.startsWith("deny")) { rules[i] = AccessRule.parseAccessRule(token); } else if (token.startsWith("file")) { rules[i] = new AccessRuleFile(token, interval); } else { String detail = "Failed to parse access rule list. " + "Expected token \"" + token + "\" to start with " + "\"allow\", \"deny\" or \"file\"."; throw new ParseException(detail); } } return new AccessRuleList(rules); } /** * Counts the number of rules in this list. * * @return * the number of rules, always >= 0. */ public int getRuleCount() { return _rules.length; } /** * Determines if the specified IP address is allowed to access the * specified function, returning a <code>Boolean</code> object or * <code>null</code>. * * <p>This method finds the first matching rule and then returns the * <em>allow</em> property of that rule (see * {@link AccessRule#isAllowRule()}). If there is no matching rule, then * <code>null</code> is returned. * * @param ip * the IP address, cannot be <code>null</code>. * * @param functionName * the name of the function, cannot be <code>null</code>. * * @param conventionName * the name of the calling convention to match, can be <code>null</code>. * * @return * {@link Boolean#TRUE} if the specified IP address is allowed to access * the specified function, {@link Boolean#FALSE} if it is disallowed * access or <code>null</code> if no match is found. * * @throws IllegalStateException * if this object is disposed (<em>since XINS 1.3.0</em>). * * @throws IllegalArgumentException * if <code>ip == null || functionName == null</code>. * * @throws ParseException * if the specified IP address is malformed. * * @since XINS 2.1. */ public Boolean isAllowed(String ip, String functionName, String conventionName) throws IllegalStateException, IllegalArgumentException, ParseException { // Check state if (_disposed) { String detail = "This AccessRuleList is disposed."; Utils.logProgrammingError(detail); throw new IllegalStateException(detail); } // Check preconditions MandatoryArgumentChecker.check("ip", ip, "functionName", functionName); int ruleCount = _rules.length; for (int i = 0; i < ruleCount; i++) { AccessRuleContainer rule = _rules[i]; String ruleString = rule.toString(); Boolean allowed = rule.isAllowed(ip, functionName, conventionName); if (allowed != null) { // Choose between 'allow' and 'deny' boolean allow = allowed.booleanValue(); // Log this match // XXX: Should this logging really be done in this class? if (allow) { Log.log_3550(ip, functionName, conventionName, i, ruleString); } else { Log.log_3551(ip, functionName, conventionName, i, ruleString); } return allowed; } } return null; } /** * Disposes this access rule. All claimed resources are freed as much as * possible. * * <p>Once disposed, the {@link #isAllowed} method should no longer be * called. */ public void dispose() { // Check state if (_disposed) { String detail = "This AccessRule is already disposed."; Utils.logProgrammingError(detail); throw new IllegalStateException(detail); } // Do not dispose the EMPTY list if (this == EMPTY) { return; } // Mark this object as disposed _disposed = true; // Dispose the current rules int count = _rules == null ? 0 : _rules.length; for (int i = 0; i < count; i++) { AccessRuleContainer rule = _rules[i]; if (rule != null) { try { rule.dispose(); } catch (Throwable exception) { Utils.logIgnoredException(exception); } } } _rules = null; } /** * Returns a character string representation of this object. The returned * string is in the form: * * <blockquote><em>type a.b.c.d/m pattern;type a.b.c.d/m pattern</em></blockquote> * * where <em>type</em> is either <code>"allow"</code> or * <code>"deny"</code>, <em>a.b.c.d</em> is the base IP address, <em>m</em> * is the mask, and <em>pattern</em> is the function name simple pattern. * * @return * a character string representation of this access rule, never * <code>null</code>. */ public String toString() { return _asString; } }