/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at
* trunk/opends/resource/legal-notices/OpenDS.LICENSE
* or https://OpenDS.dev.java.net/OpenDS.LICENSE.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at
* trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable,
* add the following below this CDDL HEADER, with the fields enclosed
* by brackets "[]" replaced with your own identifying information:
* Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*
*
* Portions Copyright 2007 Sun Microsystems, Inc.
*/
package org.opends.server.authorization.dseecompat;
import static org.opends.server.messages.AciMessages.*;
import static org.opends.server.authorization.dseecompat.Aci.*;
import static org.opends.server.messages.MessageHandler.getMessage;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.HashSet;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* This class represents a ip bind rule keyword.
*/
public class IpCriteria implements KeywordBindRule {
private EnumBindRuleType type=null;
// private token to express that any address is accepted
private final static String ANY_ADDRESSES = "ALL";
private boolean matchAnyAddress = false;
private IpBitsNetworkCriteria[] ipBitsCriteria = null;
private IpMaskNetworkCriteria[] ipMaskCriteria = null;
private static final String valueRegex =
"([^," + ZERO_OR_MORE_WHITESPACE + "]+)";
private static final String valuesRegex =
valueRegex + ZERO_OR_MORE_WHITESPACE + "(," +
ZERO_OR_MORE_WHITESPACE + valueRegex + ")*";
/*
* TODO Verifiy IpCriteria constructor adheres to DS 5.2 ip keyword
* syntax.
*
* Based on the contents of the constructor, it doesn't appear that the
* IpCriteria class uses the same set of allowed values as DS 5.2 does
* with the "ip" keyword (as documented at
* http://docs.sun.com/source/817-7613/aci.html#wp20242). Was that the
* intention? It looks like it doesn't allow asterisks as wild-ccards or
* plus signs to specify netmasks (although it appears that it expects a
* slash to be used if you want a netmask). While I don't mind allowing
* alternate formats (e.g., CIDR-style addresses), we can't drop support
* for the existing ones if we're trying to maintain compatibility.
*/
/**
* Constructor that creates an IpCriteria from an array of values and
* an enumeration bind rule type.
* @param values An array of address values.
* @param type An enumeration of the bind rule type.
* @throws UnknownHostException If the host address cannot be resolved to
* a hostname.
* @throws AciException If a part of the address is invalid.
* @throws IndexOutOfBoundsException If an index is incremented past an
* array bounds when copying or evaluating an address.
*/
public IpCriteria(String[] values, EnumBindRuleType type)
throws UnknownHostException, IndexOutOfBoundsException, AciException {
IpBitsNetworkCriteria[] ipBitsCriteria_2 = null;
IpMaskNetworkCriteria[] ipMaskCriteria_2 = null;
try
{
for (String value : values)
{
if (value.equalsIgnoreCase(ANY_ADDRESSES))
{
matchAnyAddress = true;
continue;
}
// determine what format it is to instantiate
// the right criteria object
int slash = value.indexOf("/");
if (slash == -1)
{
// simple raw IP address
IpBitsNetworkCriteria newInstance;
if (InetAddress.getByName(value)
instanceof Inet6Address)
{
newInstance = new IpBitsNetworkCriteria(value, 128);
} else
{
newInstance = new IpBitsNetworkCriteria(value, 32);
}
if (ipBitsCriteria_2 == null)
{
ipBitsCriteria_2 = new IpBitsNetworkCriteria[1];
} else
{
IpBitsNetworkCriteria[] newIpBitsCriteria =
new IpBitsNetworkCriteria[ipBitsCriteria_2.length + 1];
System.arraycopy(ipBitsCriteria_2, 0,
newIpBitsCriteria, 0, ipBitsCriteria_2.length);
ipBitsCriteria_2 = newIpBitsCriteria;
}
ipBitsCriteria_2[ipBitsCriteria_2.length - 1] = newInstance;
} else
{
// Extract data following the / and figure out whether it
// is a bit number or a mask
try
{
int bits =
Integer.parseInt(value.substring(slash + 1));
// Well, no exception, so this is a bit
// Let's instantiate the corresponding criterion
if (ipBitsCriteria_2 == null)
{
ipBitsCriteria_2 = new IpBitsNetworkCriteria[1];
} else
{
IpBitsNetworkCriteria[] newIpBitsCriteria =
new IpBitsNetworkCriteria[ipBitsCriteria_2.length + 1];
System.arraycopy(ipBitsCriteria_2, 0,
newIpBitsCriteria, 0, ipBitsCriteria_2.length);
ipBitsCriteria_2 = newIpBitsCriteria;
}
ipBitsCriteria_2[ipBitsCriteria_2.length - 1] =
new IpBitsNetworkCriteria(value.
substring(0, slash), bits);
}
catch (IndexOutOfBoundsException e1)
{
throw e1;
}
catch (Exception e2)
{
// Looks like this is a network mask.
if (ipMaskCriteria_2 == null)
{
ipMaskCriteria_2 = new IpMaskNetworkCriteria[1];
} else
{
IpMaskNetworkCriteria[] newIpMaskCriteria =
new IpMaskNetworkCriteria[ipMaskCriteria_2.length + 1];
System.arraycopy(ipMaskCriteria_2, 0,
newIpMaskCriteria, 0, ipMaskCriteria_2.length);
ipMaskCriteria_2 = newIpMaskCriteria;
}
try
{
ipMaskCriteria_2[ipMaskCriteria_2.length - 1] =
new IpMaskNetworkCriteria(value.
substring(0, slash), value.substring(slash + 1));
}
catch (IndexOutOfBoundsException e3)
{
throw e3;
}
}
}
}
}
catch (UnknownHostException ue)
{
throw ue;
}
ipBitsCriteria = ipBitsCriteria_2;
ipMaskCriteria = ipMaskCriteria_2;
this.type=type;
}
/**
* Return the ipBitsNetworkCriteria of this IpCriteria.
* @return Returns the ipBitsNetworkCriteria.
*/
public IpBitsNetworkCriteria[] getIpBitsNetworkCriteria() {
return ipBitsCriteria;
}
/**
* Return the ipMaskNetworkCriteria of this IpCriteria.
* @return Returns the ipMaskNetworkCriteria.
*/
public IpMaskNetworkCriteria[] getIpMaskNetworkCriteria() {
return ipMaskCriteria;
}
/**
* Compare an IP address with the network rule.
*
* @param theSourceAddress IP source address of the client.
* @return true if client matches the network rule or
* false if they may not.
*/
public boolean match (InetAddress theSourceAddress)
{
if (matchAnyAddress){
return true;
}
if (ipMaskCriteria != null)
{
for (IpMaskNetworkCriteria anIpMaskCriteria : ipMaskCriteria)
{
if (anIpMaskCriteria.match(theSourceAddress))
{
return true;
}
}
}
if (ipBitsCriteria != null)
{
for (IpBitsNetworkCriteria anIpBitsCriteria : ipBitsCriteria)
{
if (anIpBitsCriteria.match(theSourceAddress))
{
return true;
}
}
}
return (ipBitsCriteria == null) && (ipMaskCriteria == null);
}
/**
* Decode an expression string representing a ip keyword bind rule
* expression.
* @param expr A string representing the expression.
* @param type An enumeration representing the bind rule type.
* @return An keyword bind rule that can be used to evaluate the
* expression.
* @throws AciException If the expression string is invalid.
*/
public static KeywordBindRule decode(String expr, EnumBindRuleType type)
throws AciException {
if (!Pattern.matches(valuesRegex, expr)) {
int msgID = MSGID_ACI_SYNTAX_INVALID_IP_EXPRESSION;
String message = getMessage(msgID, expr);
throw new AciException(msgID, message);
}
int valuePos = 1;
Pattern valuePattern = Pattern.compile(valueRegex);
Matcher valueMatcher = valuePattern.matcher(expr);
HashSet values = new HashSet();
while (valueMatcher.find()) {
String value = valueMatcher.group(valuePos);
values.add(value);
}
IpCriteria ipCriteria;
String[] strValues = null;
if (!values.isEmpty()) {
strValues = values.toArray(new String[values.size()]);
}
try {
ipCriteria = new IpCriteria(strValues, type);
} catch (Exception e) {
int msgID = MSGID_ACI_SYNTAX_INVALID_IP_CRITERIA_DECODE;
String message = getMessage(msgID, e.getMessage());
throw new AciException(msgID, message);
}
return ipCriteria;
}
/**
* Evaluate the evaluation context against this ip criteria.
* @param evalCtx An evaluation context to use.
* @return An enumeration evaluation result.
*/
public EnumEvalResult evaluate(AciEvalContext evalCtx) {
EnumEvalResult matched=EnumEvalResult.FALSE;
if(match(evalCtx.getRemoteAddress()))
matched=EnumEvalResult.TRUE;
return matched.getRet(type, false);
}
}