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 2009 Sun Microsystems, Inc.
015 * Portions Copyright 2012-2016 ForgeRock AS.
016 */
017package org.opends.server.extensions;
018
019import java.util.List;
020
021import org.forgerock.i18n.LocalizableMessage;
022import org.forgerock.opendj.ldap.AVA;
023import org.forgerock.opendj.ldap.ByteString;
024import org.forgerock.opendj.ldap.ConditionResult;
025import org.forgerock.opendj.ldap.RDN;
026import org.forgerock.opendj.ldap.ResultCode;
027import org.forgerock.opendj.ldap.schema.AttributeType;
028import org.opends.server.admin.std.server.GoverningStructureRuleVirtualAttributeCfg;
029import org.opends.server.api.VirtualAttributeProvider;
030import org.opends.server.core.DirectoryServer;
031import org.opends.server.core.SearchOperation;
032import org.opends.server.types.AcceptRejectWarn;
033import org.opends.server.types.Attribute;
034import org.opends.server.types.Attributes;
035import org.opends.server.types.DITStructureRule;
036import org.opends.server.types.Entry;
037import org.opends.server.types.NameForm;
038import org.opends.server.types.ObjectClass;
039import org.opends.server.types.VirtualAttributeRule;
040
041import static org.opends.messages.ExtensionMessages.*;
042
043/**
044 * This class implements a virtual attribute provider that is meant to serve
045 * the governingStructuralRule operational attribute as described in RFC 4512.
046 */
047public class GoverningStructureRuleVirtualAttributeProvider  extends
048         VirtualAttributeProvider<GoverningStructureRuleVirtualAttributeCfg>
049{
050  /**
051   * Creates a new instance of this governingStructureRule virtual attribute
052   * provider.
053   */
054  public GoverningStructureRuleVirtualAttributeProvider()
055  {
056    super();
057
058    // All initialization should be performed in the
059    // initializeVirtualAttributeProvider method.
060  }
061
062  /** {@inheritDoc} */
063  @Override
064  public boolean isMultiValued()
065  {
066    return false;
067  }
068
069  /** {@inheritDoc} */
070  @Override
071  public Attribute getValues(Entry entry, VirtualAttributeRule rule)
072  {
073    DITStructureRule ditRule = getDITStructureRule(entry);
074    if(ditRule !=null)
075    {
076      return Attributes.create(
077          rule.getAttributeType(), String.valueOf(ditRule.getRuleID()));
078    }
079    return Attributes.empty(rule.getAttributeType());
080  }
081
082  /** {@inheritDoc} */
083  @Override
084  public boolean hasValue(Entry entry, VirtualAttributeRule rule)
085  {
086    return getDITStructureRule(entry)!=null;
087  }
088
089  /** {@inheritDoc} */
090  @Override
091  public ConditionResult matchesSubstring(Entry entry,
092                                          VirtualAttributeRule rule,
093                                          ByteString subInitial,
094                                          List<ByteString> subAny,
095                                          ByteString subFinal)
096  {
097    // DITStructureRule cannot be used in substring matching.
098    return ConditionResult.UNDEFINED;
099  }
100
101  /** {@inheritDoc} */
102  @Override
103  public ConditionResult greaterThanOrEqualTo(Entry entry,
104                              VirtualAttributeRule rule,
105                              ByteString value)
106  {
107    // DITStructureRule cannot be used in ordering matching.
108    return ConditionResult.UNDEFINED;
109  }
110
111  /** {@inheritDoc} */
112  @Override
113  public ConditionResult lessThanOrEqualTo(Entry entry,
114                              VirtualAttributeRule rule,
115                              ByteString value)
116  {
117    // DITStructureRule cannot be used in ordering matching.
118    return ConditionResult.UNDEFINED;
119  }
120
121  /** {@inheritDoc} */
122  @Override
123  public ConditionResult approximatelyEqualTo(Entry entry,
124                              VirtualAttributeRule rule,
125                              ByteString value)
126  {
127    // DITStructureRule cannot be used in approximate matching.
128    return ConditionResult.UNDEFINED;
129  }
130
131  /** {@inheritDoc} */
132  @Override
133  public boolean isSearchable(VirtualAttributeRule rule,
134                              SearchOperation searchOperation,
135                              boolean isPreIndexed)
136  {
137    //Non-searchable.
138    return false;
139  }
140
141  /** {@inheritDoc} */
142  @Override
143  public void processSearch(VirtualAttributeRule rule,
144                            SearchOperation searchOperation)
145  {
146    searchOperation.setResultCode(ResultCode.UNWILLING_TO_PERFORM);
147
148    LocalizableMessage message = ERR_VATTR_NOT_SEARCHABLE.get(
149            rule.getAttributeType().getNameOrOID());
150    searchOperation.appendErrorMessage(message);
151  }
152
153  /** Checks if the entry matches the nameform. */
154  private boolean matchesNameForm(NameForm nameForm,
155                       AcceptRejectWarn structuralPolicy,
156                       Entry entry)
157  {
158    RDN rdn = entry.getName().rdn();
159    if (rdn != null)
160    {
161      // Make sure that all the required attributes are present.
162      for (AttributeType t : nameForm.getRequiredAttributes())
163      {
164        if (!rdn.hasAttributeType(t)
165            && structuralPolicy == AcceptRejectWarn.REJECT)
166        {
167          return false;
168        }
169      }
170
171      // Make sure that all attributes in the RDN are allowed.
172      for (AVA ava : rdn)
173      {
174        AttributeType t = ava.getAttributeType();
175        if (!nameForm.isRequiredOrOptional(t)
176            && structuralPolicy == AcceptRejectWarn.REJECT)
177        {
178          return false;
179        }
180       }
181     }
182    return true;
183  }
184
185  /** Finds the appropriate DIT structure rule for an entry. */
186  private DITStructureRule getDITStructureRule(Entry entry) {
187    ObjectClass oc = entry.getStructuralObjectClass();
188    if (oc == null) {
189      return null;
190    }
191    List<NameForm> listForms = DirectoryServer.getNameForm(oc);
192    NameForm nameForm = null;
193    DITStructureRule ditRule = null;
194    //We iterate over all the nameforms while creating the entry and
195    //select the first one that matches. Since the entry exists, the same
196    //algorithm should work fine to retrieve the nameform which was
197    //applied while creating the entry.
198    if (listForms != null)
199    {
200      boolean obsolete = true;
201      AcceptRejectWarn structuralPolicy =
202        DirectoryServer.getSingleStructuralObjectClassPolicy();
203      for (NameForm nf : listForms)
204      {
205        if (!nf.isObsolete())
206        {
207          obsolete = false;
208          if (matchesNameForm(nf, structuralPolicy, entry))
209          {
210           nameForm = nf;
211           break;
212          }
213        }
214      }
215      if (nameForm != null && !obsolete)
216      {
217        ditRule = DirectoryServer.getDITStructureRule(nameForm);
218      }
219    }
220    return ditRule;
221  }
222}
223