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