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 java.util.regex.Pattern;
020
021import org.forgerock.i18n.LocalizedIllegalArgumentException;
022import org.forgerock.opendj.ldap.DN;
023import org.opends.server.types.DirectoryException;
024import org.opends.server.types.LDAPURL;
025
026import static org.opends.messages.AccessControlMessages.*;
027import static org.opends.server.authorization.dseecompat.Aci.*;
028
029/** A class representing an ACI target keyword. */
030public class Target
031{
032    /** Enumeration representing the target operator. */
033    private final EnumTargetOperator operator;
034    /** True if the URL contained a DN wild-card pattern. */
035    private final boolean isPattern;
036    /** The target DN from the URL or null if it was a wild-card pattern. */
037    private final DN urlDN;
038    /** The pattern matcher for a wild-card pattern or null if the URL contained an ordinary DN. */
039    private final PatternDN patternDN;
040
041    /*
042     * TODO Save aciDN parameter and use it in matchesPattern re-write.
043     *
044     * Should the aciDN argument provided to the constructor be stored so that
045     * it can be used in the matchesPattern() method?  The DN should only be
046     * considered a potential match if it is at or below the entry containing
047     * the ACI.
048     */
049    /**
050     * This constructor parses the target string.
051     * @param operator  An enumeration of the operation of this target.
052     * @param target A string representation of the target.
053     * @param aciDN The dn of the ACI entry used for a descendant check.
054     * @throws AciException If the target string is invalid.
055     */
056    private Target(EnumTargetOperator operator, String target, DN aciDN)
057            throws AciException {
058        this.operator = operator;
059        try {
060          //The NULL_LDAP_URL corresponds to the root DSE.
061          if (!NULL_LDAP_URL.equals(target) && !Pattern.matches(LDAP_URL, target)) {
062              throw new AciException(WARN_ACI_SYNTAX_INVALID_TARGETKEYWORD_EXPRESSION.get(target));
063          }
064          LDAPURL targetURL =  LDAPURL.decode(target, false);
065          if (targetURL.getRawBaseDN().contains("*")) {
066              this.isPattern=true;
067              patternDN = PatternDN.decodeSuffix(targetURL.getRawBaseDN());
068              urlDN = null;
069          } else {
070              this.isPattern = false;
071              patternDN = null;
072              urlDN=targetURL.getBaseDN();
073              if(!urlDN.isSubordinateOrEqualTo(aciDN)) {
074                  throw new AciException(WARN_ACI_SYNTAX_TARGET_DN_NOT_DESCENDENTOF.get(urlDN, aciDN));
075              }
076          }
077        }
078        catch (LocalizedIllegalArgumentException | DirectoryException e) {
079            throw new AciException(WARN_ACI_SYNTAX_INVALID_TARGETKEYWORD_EXPRESSION.get(target));
080        }
081    }
082
083    /**
084     *  Decode an expression string representing a target keyword expression.
085     * @param operator  An enumeration of the operation of this target.
086     * @param expr A string representation of the target.
087     * @param aciDN  The DN of the ACI entry used for a descendant check.
088     * @return  A Target class representing this target.
089     * @throws AciException  If the expression string is invalid.
090     */
091    public static Target decode(EnumTargetOperator operator,
092                                String expr, DN aciDN)
093            throws AciException {
094        return new Target(operator, expr, aciDN);
095    }
096
097    /**
098     * Returns the operator of this expression.
099     * @return An enumeration of the operation value.
100     */
101    public EnumTargetOperator getOperator() {
102        return operator;
103    }
104
105    /**
106     * Returns the URL DN of the expression.
107     * @return A DN of the URL or null if the URL contained a DN pattern.
108     */
109    public DN getDN() {
110        return urlDN;
111    }
112
113    /**
114     * Returns boolean if a pattern was seen during parsing.
115     * @return  True if the URL contained a DN pattern.
116     */
117    public boolean isPattern() {
118        return isPattern;
119    }
120
121    /**
122     * This method tries to match a pattern against a DN.
123     * @param dn  The DN to try an match.
124     * @return True if the pattern matches.
125     */
126    public boolean matchesPattern(DN dn) {
127        return patternDN.matchesDN(dn);
128    }
129}