/*
|
* 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 legal-notices/CDDLv1_0.txt
|
* or http://forgerock.org/license/CDDLv1.0.html.
|
* 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 legal-notices/CDDLv1_0.txt.
|
* 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
|
*
|
*
|
* Copyright 2008 Sun Microsystems, Inc.
|
* Portions Copyright 2014 ForgeRock AS
|
*/
|
|
package org.opends.server.authorization.dseecompat;
|
import org.forgerock.i18n.LocalizableMessage;
|
|
import static org.opends.messages.AccessControlMessages.*;
|
import static org.opends.server.authorization.dseecompat.Aci.*;
|
import java.util.StringTokenizer;
|
import java.util.LinkedHashSet;
|
import java.util.regex.Pattern;
|
import java.util.regex.Matcher;
|
|
import org.opends.server.core.DirectoryServer;
|
import org.opends.server.types.AttributeType;
|
import org.opends.server.types.DN;
|
import org.opends.server.types.LDAPURL;
|
import org.opends.server.types.DirectoryException;
|
|
/**
|
* This class is used by USERDN and GROUPDN userattr types
|
* to determine what parent inheritance checks to make.
|
*/
|
public class ParentInheritance {
|
|
/*
|
* The maximum number of parent inheritance levels supported.
|
*/
|
private static final int MAX_LEVELS=10;
|
|
/*
|
* Pattern to match for parent inheritance.
|
*/
|
private final String parentPat="parent[";
|
|
/*
|
* Array used to hold the level information. Each slot corresponds to a
|
* level parsed from the rule.
|
*/
|
private final int[] levels=new int[MAX_LEVELS];
|
|
/*
|
* The number of levels parsed.
|
*/
|
private int numLevels;
|
|
/*
|
* The attribute type string parsed from the rule. Only used in
|
* inheritance search.
|
*/
|
private String attrTypeStr;
|
|
/*
|
* The base DN of a URL parsed from the rule. Used to make sure groupdn
|
* are under this suffix. Originally a way to search all nested groups
|
* under this suffix, so the behavior is slightly different.
|
*/
|
private DN baseDN=null;
|
|
|
/**
|
* Construct a class from the inheritance pattern. The skipParsing boolean
|
* specifies that parent parsing should be skipped and sets up the class:
|
* with numLevels=1, level[0]=0 and an attribute type from the
|
* specified pattern.
|
*
|
* @param pattern The string pattern containing the inheritance
|
* information.
|
* @param skipParse Specify if the parent inheritance parsing should be
|
* skipped or not.
|
* @throws AciException If the pattern is invalid.
|
*/
|
ParentInheritance (String pattern, boolean skipParse) throws AciException {
|
if (skipParse) {
|
//The "parent[" pattern is invalid for ROLEDN user attr keyword.
|
if(pattern.startsWith(parentPat)) {
|
LocalizableMessage message =
|
WARN_ACI_SYNTAX_INVALID_USERATTR_ROLEDN_INHERITANCE_PATTERN
|
.get(pattern);
|
throw new AciException(message);
|
} else {
|
pattern=pattern.trim();
|
Pattern pattern1=Pattern.compile(ATTR_NAME);
|
Matcher matcher=pattern1.matcher(pattern);
|
//Check if valid attribute type name.
|
if(!matcher.find() || matcher.groupCount() != 1) {
|
LocalizableMessage message =
|
WARN_ACI_SYNTAX_INVALID_ATTRIBUTE_TYPE_NAME.get(pattern);
|
throw new AciException(message);
|
}
|
numLevels=1;
|
levels[0]=0;
|
}
|
} else parse(pattern);
|
}
|
|
/**
|
* Performs all parsing of the specified pattern string.
|
* @param pattern The string pattern containing the inheritance
|
* information.
|
* @throws AciException If the pattern is invalid.
|
*/
|
private void parse (String pattern) throws AciException {
|
pattern=pattern.trim();
|
/**
|
* Check if we have a "parent[" string.
|
*/
|
if(pattern.startsWith(parentPat)) {
|
numLevels=0;
|
levels[0]=0;
|
String p=pattern.substring(parentPat.length());
|
/**
|
* Format needs to be parent[XX].attribute -- everything after the
|
* '.' is the attribute type.
|
*/
|
String[] toks=p.split("\\.");
|
if(toks.length != 2) {
|
LocalizableMessage message =
|
WARN_ACI_SYNTAX_INVALID_USERATTR_INHERITANCE_PATTERN
|
.get(pattern);
|
throw new AciException(message);
|
}
|
Pattern pattern1=Pattern.compile(ATTR_NAME);
|
Matcher matcher=pattern1.matcher(toks[1]);
|
//Check if valid attribute type name.
|
if(!matcher.find() || matcher.groupCount() != 1) {
|
LocalizableMessage message =
|
WARN_ACI_SYNTAX_INVALID_ATTRIBUTE_TYPE_NAME.get(toks[1]);
|
throw new AciException(message);
|
}
|
attrTypeStr=toks[1];
|
StringTokenizer tok=new StringTokenizer(toks[0],"],",false);
|
while(tok.hasMoreTokens()) {
|
String v=tok.nextToken();
|
/**
|
* Everything between the brackets must be an integer or it's
|
* an error.
|
*/
|
try {
|
if(numLevels < MAX_LEVELS) {
|
levels[numLevels++]=Integer.decode(v);
|
} else {
|
LocalizableMessage message =
|
WARN_ACI_SYNTAX_MAX_USERATTR_INHERITANCE_LEVEL_EXCEEDED.get(pattern, MAX_LEVELS);
|
throw new AciException(message);
|
}
|
} catch (NumberFormatException ex) {
|
LocalizableMessage message =
|
WARN_ACI_SYNTAX_INVALID_INHERITANCE_VALUE.get(pattern);
|
throw new AciException(message);
|
}
|
}
|
} else {
|
attrTypeStr=pattern;
|
if(pattern.startsWith(NULL_LDAP_URL)) {
|
try {
|
LDAPURL url=LDAPURL.decode(pattern, true);
|
LinkedHashSet<String>attrs=url.getAttributes();
|
if(attrs.size() != 1) {
|
LocalizableMessage message =
|
WARN_ACI_SYNTAX_INVALID_USERATTR_ATTR_URL.get(pattern);
|
throw new AciException(message);
|
}
|
baseDN=url.getBaseDN();
|
if(baseDN.isRootDN()){
|
LocalizableMessage message =
|
WARN_ACI_SYNTAX_INVALID_USERATTR_BASEDN_URL.get(pattern);
|
throw new AciException(message);
|
}
|
attrTypeStr=attrs.iterator().next();
|
} catch (DirectoryException ex) {
|
LocalizableMessage message = WARN_ACI_SYNTAX_INVALID_USERATTR_URL.get(
|
ex.getMessageObject());
|
throw new AciException(message);
|
}
|
}
|
numLevels=1;
|
levels[0]=0;
|
}
|
}
|
|
/**
|
* Returns the number of levels counted.
|
* @return The number of levels.
|
*/
|
public int getNumLevels() {
|
return numLevels;
|
}
|
|
/**
|
* Returns an array of levels, where levels are integers.
|
* @return Return an array of levels.
|
*/
|
public int[] getLevels() {
|
int[] levelsCopy = new int[levels.length];
|
System.arraycopy(levels, 0, levelsCopy, 0, levels.length);
|
return levelsCopy;
|
}
|
|
/**
|
* Return the attribute type.
|
* @return The attribute type.
|
*/
|
public AttributeType getAttributeType() {
|
AttributeType attrType;
|
if((attrType =
|
DirectoryServer.getAttributeType(attrTypeStr.toLowerCase())) == null)
|
attrType=
|
DirectoryServer.getDefaultAttributeType(attrTypeStr.toLowerCase());
|
return attrType;
|
}
|
|
/**
|
* Return the string representation of the attribute type.
|
* @return The attribute type string.
|
*/
|
public String getAttrTypeStr() {
|
return attrTypeStr;
|
}
|
|
/**
|
* Return the DN that groupdn must be under.
|
*
|
* @return DN that groupdn must be under.
|
*/
|
public DN getBaseDN() {
|
return baseDN;
|
}
|
}
|