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