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