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