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}