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