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