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