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 2013-2015 ForgeRock AS
026 */
027package org.opends.server.authorization.dseecompat;
028
029import static org.opends.messages.AccessControlMessages.*;
030import static org.opends.server.authorization.dseecompat.Aci.*;
031
032import java.util.LinkedHashMap;
033import java.util.regex.Matcher;
034import java.util.regex.Pattern;
035
036import org.forgerock.i18n.LocalizableMessage;
037import org.opends.server.core.DirectoryServer;
038import org.opends.server.types.AttributeType;
039import org.opends.server.types.DirectoryException;
040import org.opends.server.types.SearchFilter;
041
042/**
043 * The TargAttrFilterList class represents an targattrfilters list. A
044 * targattrfilters list looks like:
045 *
046 *   "Op=attr1:F1 [(&& attr2:F2)*]
047 */
048public class TargAttrFilterList {
049
050  /**
051   * The mask corresponding to the operation of this list (add or del).
052   */
053    private int mask;
054
055  /**
056   * ListHashMap keyed by the attribute type and mapping to the corresponding
057   * search filter. LinkedHashMap is used so everything is in order.
058   */
059    private LinkedHashMap<AttributeType, SearchFilter> attrFilterList;
060
061  /**
062   * Regular expression group count.
063   */
064    private static int expectedGroupCount=2;
065
066  /**
067   * Regular expression attribute group position.
068   */
069    private static int attributePos=1;
070
071  /**
072   * Regular expression filter group position.
073   */
074    private static int filterPos=2;
075
076  /**
077   * Regular expression used to match a filter list including the strange "and"
078   * token used to join the multiple attribute type filter pairs.
079   */
080    private static final String filterListSeperator =
081              ZERO_OR_MORE_WHITESPACE  + "&&" + ZERO_OR_MORE_WHITESPACE;
082
083  /**
084   * Regular expression used to match an attribute filter pair.
085   */
086    private static final String attributeFilter=
087            ATTR_NAME + ZERO_OR_MORE_WHITESPACE + ":{1}" +
088            ZERO_OR_MORE_WHITESPACE + "(\\({1}.*\\){1})";
089
090    /**
091     * Construct a class representing an targattrfilters filter list.
092     * @param mask The mask representing the operation.
093     * @param attrFilterList The list map containing the attribute type
094     * filter mappings.
095     */
096    public TargAttrFilterList(int mask,
097                    LinkedHashMap<AttributeType, SearchFilter> attrFilterList) {
098        this.mask=mask;
099        this.attrFilterList=attrFilterList;
100    }
101
102    /**
103     * Decode an TargAttrFilterList from the specified expression string.
104     * @param mask  The mask representing the operation.
105     * @param expression The expression string to decode.
106     * @return A TargAttrFilterList class representing the targattrfilters
107     * filter list.
108     * @throws AciException If the expression string contains errors.
109     */
110    public static TargAttrFilterList decode(int mask, String expression)
111            throws AciException {
112        LinkedHashMap<AttributeType, SearchFilter> attrFilterList = new LinkedHashMap<>();
113        String[] subExpressions=expression.split(filterListSeperator, -1);
114        //Iterate over each sub-expression, parse and add them to the list
115        //if there are no errors.
116        for(String subs : subExpressions) {
117            Pattern pattern=Pattern.compile(attributeFilter);
118            Matcher matcher=pattern.matcher(subs);
119            //Match the attribute:filter pair part of the expression
120            if(!matcher.find() || matcher.groupCount() != expectedGroupCount) {
121                LocalizableMessage message =
122                    WARN_ACI_SYNTAX_INVALID_TARGATTRFILTERS_FILTER_LIST_FORMAT.
123                      get(expression);
124                throw new AciException(message);
125            }
126            String attributeName=matcher.group(attributePos).toLowerCase();
127            //Strip off any options, so it will match the filter option
128            //handling.
129            int semicolon = attributeName.indexOf(';');
130            if (semicolon != -1)
131            {
132              attributeName=attributeName.substring(0, semicolon);
133            }
134            String filterString=matcher.group(filterPos);
135            AttributeType attrType = DirectoryServer.getAttributeTypeOrDefault(attributeName);
136            SearchFilter filter;
137            //Check if it is a valid filter and add it to the list map if ok.
138            try {
139               filter = SearchFilter.createFilterFromString(filterString);
140               attrFilterList.put(attrType, filter);
141            } catch (DirectoryException ex) {
142                LocalizableMessage er=ex.getMessageObject();
143                LocalizableMessage message =
144                    WARN_ACI_SYNTAX_INVALID_TARGATTRFILTERS_FILTER_LISTS_FILTER.
145                      get(filterString, er);
146                throw new AciException(message);
147            }
148            //Verify the filter components. This check assures that each
149            //attribute type in the filter matches the provided attribute type.
150            verifyFilterComponents(filter, attrType);
151        }
152        return new TargAttrFilterList(mask, attrFilterList);
153    }
154
155    /**
156     * Verify the filter component attribute types by assuring that each
157     * attribute type in the filter matches the specified attribute type.
158     * @param filter  The filter to verify.
159     * @param type The attribute type to use in the verification.
160     * @throws AciException  If the filter contains an attribute type not
161     * specified.
162     */
163    private static void  verifyFilterComponents(SearchFilter filter,
164                                                AttributeType type)
165            throws AciException {
166        switch (filter.getFilterType()) {
167            case AND:
168            case OR: {
169                for (SearchFilter f : filter.getFilterComponents()) {
170                    verifyFilterComponents(f, type);
171                }
172                break;
173            }
174            case NOT:  {
175                SearchFilter f = filter.getNotComponent();
176                verifyFilterComponents(f, type);
177                break;
178            }
179            default: {
180                AttributeType attrType=filter.getAttributeType();
181                if(!attrType.equals(type)) {
182                    throw new AciException(
183                        WARN_ACI_SYNTAX_INVALID_TARGATTRFILTERS_FILTER_LISTS_ATTR_FILTER.get(filter));
184                }
185            }
186        }
187    }
188
189    /**
190     * Return the mask of this TargAttrFilterList.
191     * @return  The mask value.
192     */
193    public int getMask() {
194        return this.mask;
195    }
196
197    /**
198     * Check if the mask value of this TargAttrFilterList class contains the
199     * specified mask value.
200     * @param mask The mask to check for.
201     * @return  True if the mask matches the specified value.
202     */
203    public boolean hasMask(int mask) {
204        return (this.mask & mask) != 0;
205    }
206
207    /**
208     * Return the list map holding the attribute type to filter mappings.
209     * @return  The list map.
210     */
211    public
212    LinkedHashMap<AttributeType, SearchFilter> getAttributeTypeFilterList() {
213        return  attrFilterList;
214    }
215}