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