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.LinkedList;
033import java.util.List;
034import java.util.regex.Matcher;
035import java.util.regex.Pattern;
036
037import org.forgerock.i18n.LocalizableMessage;
038import org.forgerock.opendj.ldap.ByteString;
039import org.opends.server.api.Group;
040import org.opends.server.core.DirectoryServer;
041import org.opends.server.core.GroupManager;
042import org.opends.server.types.*;
043
044/**
045 * This class implements the groupdn bind rule keyword.
046 */
047public class GroupDN implements KeywordBindRule {
048
049    /** List of group DNs. */
050    private List<DN> groupDNs;
051
052    /** Enumeration representing the groupdn operator type. */
053    private EnumBindRuleType type;
054
055    /**
056     * Regular expression matching one or more LDAP URLs separated by
057     * "||".
058     */
059    public static final String LDAP_URLS = LDAP_URL +
060            ZERO_OR_MORE_WHITESPACE + "(" + LOGICAL_OR +
061            ZERO_OR_MORE_WHITESPACE + LDAP_URL + ")*";
062
063    /**
064     * Create a class representing a groupdn bind rule keyword.
065     * @param type An enumeration representing the bind rule type.
066     * @param groupDNs A list of the dns representing groups.
067     */
068    private GroupDN(EnumBindRuleType type, List<DN> groupDNs ) {
069        this.groupDNs=groupDNs;
070        this.type=type;
071    }
072
073    /**
074     * Decode an string expression representing a groupdn bind rule.
075     * @param expr  A string representation of the bind rule.
076     * @param type An enumeration of the type of the bind rule.
077     * @return  A keyword bind rule class that can be used to evaluate
078     * this bind rule.
079     * @throws AciException   If the expression string is invalid.
080     */
081    public static KeywordBindRule decode(String expr, EnumBindRuleType type)
082    throws AciException  {
083        if (!Pattern.matches(LDAP_URLS, expr)) {
084            LocalizableMessage message =
085                WARN_ACI_SYNTAX_INVALID_GROUPDN_EXPRESSION.get(expr);
086            throw new AciException(message);
087        }
088        List<DN> groupDNs = new LinkedList<>();
089        int ldapURLPos = 1;
090        Pattern ldapURLPattern = Pattern.compile(LDAP_URL);
091        Matcher ldapURLMatcher = ldapURLPattern.matcher(expr);
092        while (ldapURLMatcher.find()) {
093            try {
094               String value = ldapURLMatcher.group(ldapURLPos).trim();
095               DN dn=LDAPURL.decode(value, true).getBaseDN();
096               groupDNs.add(dn);
097            } catch (DirectoryException ex) {
098                LocalizableMessage message = WARN_ACI_SYNTAX_INVALID_GROUPDN_URL.get(
099                    ex.getMessageObject());
100                throw new AciException(message);
101            }
102        }
103        return new GroupDN(type, groupDNs);
104    }
105
106    /**
107     * Performs the evaluation of a groupdn bind rule based on the
108     * evaluation context passed to it. The evaluation stops when there
109     * are no more group DNs to evaluate, or if a group DN evaluates to true
110     * if it contains the client DN.
111     * @param evalCtx  An evaluation context to use  in the evaluation.
112     * @return  Enumeration evaluation result.
113     */
114    @Override
115    public EnumEvalResult evaluate(AciEvalContext evalCtx) {
116        EnumEvalResult matched = EnumEvalResult.FALSE;
117        for (DN groupDN : groupDNs) {
118            Group<?> group = getGroupManager().getGroupInstance(groupDN);
119            if(group != null && evalCtx.isMemberOf(group)) {
120               matched = EnumEvalResult.TRUE;
121               break;
122            }
123        }
124        return matched.getRet(type, false);
125    }
126
127    /**
128     * Performs an evaluation of a group that was specified in an attribute
129     * type value of the specified entry and attribute type. Each
130     * value of the attribute type is assumed to be a group DN and evaluation
131     * stops when there are no more values or if the group DN evaluates to
132     * true if it contains the client DN.
133     * @param e The entry to use in the evaluation.
134     * @param evalCtx  The evaluation context to use in the evaluation.
135     * @param attributeType The attribute type of the entry to use to get the
136     * values for the groupd DNs.
137     * @param suffixDN The suffix that the groupDN must be under. If it's null,
138     *                 then the groupDN can be anywhere in the DIT.
139     * @return Enumeration evaluation result.
140     */
141    public static EnumEvalResult evaluate (Entry e, AciEvalContext evalCtx,
142                                           AttributeType attributeType,
143                                           DN suffixDN) {
144        EnumEvalResult matched= EnumEvalResult.FALSE;
145        List<Attribute> attrs = e.getAttribute(attributeType);
146        for(ByteString v : attrs.get(0)) {
147            try {
148                DN groupDN = DN.valueOf(v.toString());
149                if(suffixDN != null && !groupDN.isDescendantOf(suffixDN))
150                {
151                  continue;
152                }
153                Group<?> group = getGroupManager().getGroupInstance(groupDN);
154                if(group != null && evalCtx.isMemberOf(group)) {
155                    matched=EnumEvalResult.TRUE;
156                    break;
157                }
158            } catch (DirectoryException ex) {
159                break;
160            }
161        }
162        return matched;
163    }
164
165    private static GroupManager getGroupManager() {
166        return DirectoryServer.getGroupManager();
167    }
168
169    /** {@inheritDoc} */
170    @Override
171    public String toString() {
172        final StringBuilder sb = new StringBuilder();
173        toString(sb);
174        return sb.toString();
175    }
176
177    /** {@inheritDoc} */
178    @Override
179    public final void toString(StringBuilder buffer) {
180        buffer.append(super.toString());
181    }
182
183}