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