001/* 002 * The contents of this file are subject to the terms of the Common Development and 003 * Distribution License (the License). You may not use this file except in compliance with the 004 * License. 005 * 006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the 007 * specific language governing permission and limitations under the License. 008 * 009 * When distributing Covered Software, include this CDDL Header Notice in each file and include 010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL 011 * Header, with the fields enclosed by brackets [] replaced by your own identifying 012 * information: "Portions Copyright [year] [name of copyright owner]". 013 * 014 * Copyright 2008 Sun Microsystems, Inc. 015 * Portions Copyright 2014-2016 ForgeRock AS. 016 */ 017package org.opends.server.authorization.dseecompat; 018 019import org.forgerock.i18n.LocalizableMessage; 020 021import static org.opends.messages.AccessControlMessages.*; 022import static org.opends.server.authorization.dseecompat.Aci.*; 023import java.util.StringTokenizer; 024import java.util.LinkedHashSet; 025import java.util.regex.Pattern; 026import java.util.regex.Matcher; 027 028import org.opends.server.core.DirectoryServer; 029import org.forgerock.opendj.ldap.schema.AttributeType; 030import org.forgerock.opendj.ldap.DN; 031import org.opends.server.types.LDAPURL; 032import org.opends.server.types.DirectoryException; 033 034/** 035 * This class is used by USERDN and GROUPDN userattr types 036 * to determine what parent inheritance checks to make. 037 */ 038public class ParentInheritance { 039 040 /** The maximum number of parent inheritance levels supported. */ 041 private static final int MAX_LEVELS=10; 042 043 /** Pattern to match for parent inheritance. */ 044 private final String parentPat="parent["; 045 046 /** 047 * Array used to hold the level information. Each slot corresponds to a 048 * level parsed from the rule. 049 */ 050 private final int[] levels=new int[MAX_LEVELS]; 051 052 /** The number of levels parsed. */ 053 private int numLevels; 054 055 /** 056 * The attribute type string parsed from the rule. Only used in 057 * inheritance search. 058 */ 059 private String attrTypeStr; 060 061 /** 062 * The base DN of a URL parsed from the rule. Used to make sure groupdn 063 * are under this suffix. Originally a way to search all nested groups 064 * under this suffix, so the behavior is slightly different. 065 */ 066 private DN baseDN; 067 068 069 /** 070 * Construct a class from the inheritance pattern. The skipParsing boolean 071 * specifies that parent parsing should be skipped and sets up the class: 072 * with numLevels=1, level[0]=0 and an attribute type from the 073 * specified pattern. 074 * 075 * @param pattern The string pattern containing the inheritance 076 * information. 077 * @param skipParse Specify if the parent inheritance parsing should be 078 * skipped or not. 079 * @throws AciException If the pattern is invalid. 080 */ 081 ParentInheritance (String pattern, boolean skipParse) throws AciException { 082 if (skipParse) { 083 //The "parent[" pattern is invalid for ROLEDN user attr keyword. 084 if(pattern.startsWith(parentPat)) { 085 LocalizableMessage message = 086 WARN_ACI_SYNTAX_INVALID_USERATTR_ROLEDN_INHERITANCE_PATTERN 087 .get(pattern); 088 throw new AciException(message); 089 } else { 090 pattern=pattern.trim(); 091 Pattern pattern1=Pattern.compile(ATTR_NAME); 092 Matcher matcher=pattern1.matcher(pattern); 093 //Check if valid attribute type name. 094 if(!matcher.find() || matcher.groupCount() != 1) { 095 LocalizableMessage message = 096 WARN_ACI_SYNTAX_INVALID_ATTRIBUTE_TYPE_NAME.get(pattern); 097 throw new AciException(message); 098 } 099 numLevels=1; 100 levels[0]=0; 101 } 102 } else { 103 parse(pattern); 104 } 105} 106 107 /** 108 * Performs all parsing of the specified pattern string. 109 * @param pattern The string pattern containing the inheritance 110 * information. 111 * @throws AciException If the pattern is invalid. 112 */ 113 private void parse (String pattern) throws AciException { 114 pattern=pattern.trim(); 115 // Check if we have a "parent[" string. 116 if(pattern.startsWith(parentPat)) { 117 numLevels=0; 118 levels[0]=0; 119 String p=pattern.substring(parentPat.length()); 120 /* 121 * Format needs to be parent[XX].attribute -- everything after the 122 * '.' is the attribute type. 123 */ 124 String[] toks=p.split("\\."); 125 if(toks.length != 2) { 126 LocalizableMessage message = 127 WARN_ACI_SYNTAX_INVALID_USERATTR_INHERITANCE_PATTERN 128 .get(pattern); 129 throw new AciException(message); 130 } 131 Pattern pattern1=Pattern.compile(ATTR_NAME); 132 Matcher matcher=pattern1.matcher(toks[1]); 133 //Check if valid attribute type name. 134 if(!matcher.find() || matcher.groupCount() != 1) { 135 LocalizableMessage message = 136 WARN_ACI_SYNTAX_INVALID_ATTRIBUTE_TYPE_NAME.get(toks[1]); 137 throw new AciException(message); 138 } 139 attrTypeStr=toks[1]; 140 StringTokenizer tok=new StringTokenizer(toks[0],"],",false); 141 while(tok.hasMoreTokens()) { 142 String v=tok.nextToken(); 143 /* 144 * Everything between the brackets must be an integer or it's 145 * an error. 146 */ 147 try { 148 if(numLevels < MAX_LEVELS) { 149 levels[numLevels++]=Integer.decode(v); 150 } else { 151 LocalizableMessage message = 152 WARN_ACI_SYNTAX_MAX_USERATTR_INHERITANCE_LEVEL_EXCEEDED.get(pattern, MAX_LEVELS); 153 throw new AciException(message); 154 } 155 } catch (NumberFormatException ex) { 156 LocalizableMessage message = 157 WARN_ACI_SYNTAX_INVALID_INHERITANCE_VALUE.get(pattern); 158 throw new AciException(message); 159 } 160 } 161 } else { 162 attrTypeStr=pattern; 163 if(pattern.startsWith(NULL_LDAP_URL)) { 164 try { 165 LDAPURL url=LDAPURL.decode(pattern, true); 166 LinkedHashSet<String>attrs=url.getAttributes(); 167 if(attrs.size() != 1) { 168 LocalizableMessage message = 169 WARN_ACI_SYNTAX_INVALID_USERATTR_ATTR_URL.get(pattern); 170 throw new AciException(message); 171 } 172 baseDN=url.getBaseDN(); 173 if(baseDN.isRootDN()){ 174 LocalizableMessage message = 175 WARN_ACI_SYNTAX_INVALID_USERATTR_BASEDN_URL.get(pattern); 176 throw new AciException(message); 177 } 178 attrTypeStr=attrs.iterator().next(); 179 } catch (DirectoryException ex) { 180 LocalizableMessage message = WARN_ACI_SYNTAX_INVALID_USERATTR_URL.get( 181 ex.getMessageObject()); 182 throw new AciException(message); 183 } 184 } 185 numLevels=1; 186 levels[0]=0; 187 } 188 } 189 190 /** 191 * Returns the number of levels counted. 192 * @return The number of levels. 193 */ 194 public int getNumLevels() { 195 return numLevels; 196 } 197 198 /** 199 * Returns an array of levels, where levels are integers. 200 * @return Return an array of levels. 201 */ 202 public int[] getLevels() { 203 int[] levelsCopy = new int[levels.length]; 204 System.arraycopy(levels, 0, levelsCopy, 0, levels.length); 205 return levelsCopy; 206 } 207 208 /** 209 * Return the attribute type. 210 * @return The attribute type. 211 */ 212 public AttributeType getAttributeType() { 213 return DirectoryServer.getAttributeType(attrTypeStr); 214 } 215 216 /** 217 * Return the string representation of the attribute type. 218 * @return The attribute type string. 219 */ 220 public String getAttrTypeStr() { 221 return attrTypeStr; 222 } 223 224 /** 225 * Return the DN that groupdn must be under. 226 * 227 * @return DN that groupdn must be under. 228 */ 229 public DN getBaseDN() { 230 return baseDN; 231 } 232} 233