001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 * 019 */ 020package org.apache.directory.server.core.jndi; 021 022 023import java.util.Hashtable; 024 025import javax.naming.Name; 026import javax.naming.NamingException; 027import javax.naming.directory.InvalidAttributeIdentifierException; 028import javax.naming.ldap.Control; 029import javax.naming.ldap.ExtendedRequest; 030import javax.naming.ldap.ExtendedResponse; 031import javax.naming.ldap.LdapContext; 032import javax.naming.ldap.LdapName; 033 034import org.apache.directory.api.asn1.DecoderException; 035import org.apache.directory.api.asn1.EncoderException; 036import org.apache.directory.api.ldap.model.entry.Value; 037import org.apache.directory.api.ldap.model.exception.LdapException; 038import org.apache.directory.api.ldap.model.exception.LdapInvalidAttributeValueException; 039import org.apache.directory.api.ldap.model.name.Dn; 040import org.apache.directory.api.ldap.model.schema.AttributeType; 041import org.apache.directory.api.ldap.util.JndiUtils; 042import org.apache.directory.api.util.Strings; 043import org.apache.directory.api.util.exception.NotImplementedException; 044import org.apache.directory.server.core.api.CoreSession; 045import org.apache.directory.server.core.api.DirectoryService; 046import org.apache.directory.server.core.api.LdapPrincipal; 047import org.apache.directory.server.core.api.interceptor.context.CompareOperationContext; 048import org.apache.directory.server.core.api.interceptor.context.UnbindOperationContext; 049import org.apache.directory.server.i18n.I18n; 050 051 052/** 053 * An implementation of a JNDI LdapContext. 054 * 055 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> 056 */ 057public class ServerLdapContext extends ServerDirContext implements LdapContext 058{ 059 /** 060 * Creates an instance of an ServerLdapContext. 061 * 062 * @param service the parent service that manages this context 063 * @param env the JNDI environment parameters 064 * @throws NamingException the context cannot be created 065 */ 066 public ServerLdapContext( DirectoryService service, Hashtable<String, Object> env ) throws Exception 067 { 068 super( service, env ); 069 } 070 071 072 /** 073 * Creates a new ServerDirContext with a distinguished name which is used to 074 * set the PROVIDER_URL to the distinguished name for this context. 075 * 076 * @param principal the directory user principal that is propagated 077 * @param dn the distinguished name of this context 078 * @param service the directory service core 079 * @throws NamingException if there are problems instantiating 080 */ 081 public ServerLdapContext( DirectoryService service, LdapPrincipal principal, Name dn ) throws Exception 082 { 083 super( service, principal, dn ); 084 } 085 086 087 public ServerLdapContext( DirectoryService service, CoreSession session, Name bindDn ) throws Exception 088 { 089 super( service, session, bindDn ); 090 } 091 092 093 /** 094 * @see javax.naming.ldap.LdapContext#extendedOperation( 095 * javax.naming.ldap.ExtendedRequest) 096 */ 097 public ExtendedResponse extendedOperation( ExtendedRequest request ) 098 { 099 throw new NotImplementedException(); 100 } 101 102 103 /** 104 * @see javax.naming.ldap.LdapContext#newInstance( 105 * javax.naming.ldap.Control[]) 106 */ 107 public LdapContext newInstance( Control[] requestControls ) throws NamingException 108 { 109 ServerLdapContext ctx = null; 110 111 try 112 { 113 ctx = new ServerLdapContext( getService(), getSession().getEffectivePrincipal(), JndiUtils.toName( getDn() ) ); 114 } 115 catch ( Exception e ) 116 { 117 JndiUtils.wrap( e ); 118 } 119 120 ctx.setRequestControls( requestControls ); 121 return ctx; 122 } 123 124 125 /** 126 * @see javax.naming.ldap.LdapContext#reconnect(javax.naming.ldap.Control[]) 127 */ 128 public void reconnect( Control[] connCtls ) throws NamingException 129 { 130 this.connectControls = connCtls; 131 } 132 133 134 /** 135 * @see javax.naming.ldap.LdapContext#getConnectControls() 136 */ 137 public Control[] getConnectControls() throws NamingException 138 { 139 return this.connectControls; 140 } 141 142 143 /** 144 * @see javax.naming.ldap.LdapContext#setRequestControls( 145 * javax.naming.ldap.Control[]) 146 */ 147 public void setRequestControls( Control[] requestControls ) throws NamingException 148 { 149 this.requestControls = requestControls; 150 } 151 152 153 /** 154 * @see javax.naming.ldap.LdapContext#getRequestControls() 155 */ 156 public Control[] getRequestControls() throws NamingException 157 { 158 return requestControls; 159 } 160 161 162 /** 163 * @see javax.naming.ldap.LdapContext#getResponseControls() 164 */ 165 public Control[] getResponseControls() throws NamingException 166 { 167 return responseControls; 168 } 169 170 171 // ------------------------------------------------------------------------ 172 // Additional ApacheDS Specific JNDI Functionality 173 // ------------------------------------------------------------------------ 174 175 /** 176 * Explicitly exposes an LDAP compare operation which JNDI does not 177 * directly provide. All normalization and schema checking etcetera 178 * is handled by this call. 179 * 180 * @param name the name of the entri 181 * @param oid the name or object identifier for the attribute to compare 182 * @param value the value to compare the attribute to 183 * @return true if the entry has the value for the attribute, false otherwise 184 * @throws NamingException if the backing store cannot be accessed, or 185 * permission is not allowed for this operation or the oid is not recognized, 186 * or the attribute is not present in the entry ... you get the picture. 187 */ 188 public boolean compare( Dn name, String oid, Object value ) throws NamingException 189 { 190 Value val = null; 191 192 AttributeType attributeType = null; 193 194 try 195 { 196 attributeType = getService().getSchemaManager().lookupAttributeTypeRegistry( oid ); 197 } 198 catch ( LdapException le ) 199 { 200 throw new InvalidAttributeIdentifierException( le.getMessage() ); 201 } 202 203 // make sure we add the request controls to operation 204 try 205 { 206 if ( attributeType.getSyntax().isHumanReadable() ) 207 { 208 if ( value instanceof String ) 209 { 210 val = new Value( attributeType, ( String ) value ); 211 } 212 else if ( value instanceof byte[] ) 213 { 214 val = new Value( attributeType, Strings.utf8ToString( ( byte[] ) value ) ); 215 } 216 else 217 { 218 throw new NamingException( I18n.err( I18n.ERR_309, oid ) ); 219 } 220 } 221 else 222 { 223 if ( value instanceof String ) 224 { 225 val = new Value( attributeType, Strings.getBytesUtf8( ( String ) value ) ); 226 } 227 else if ( value instanceof byte[] ) 228 { 229 val = new Value( attributeType, ( byte[] ) value ); 230 } 231 else 232 { 233 throw new NamingException( I18n.err( I18n.ERR_309, oid ) ); 234 } 235 } 236 } 237 catch ( LdapInvalidAttributeValueException liave ) 238 { 239 throw new NamingException( I18n.err( I18n.ERR_309, oid ) ); 240 } 241 242 CompareOperationContext opCtx = new CompareOperationContext( getSession(), name, oid, val ); 243 244 try 245 { 246 opCtx.addRequestControls( JndiUtils.fromJndiRequestControls( getDirectoryService().getLdapCodecService(), 247 requestControls ) ); 248 } 249 catch ( DecoderException e1 ) 250 { 251 throw new NamingException( I18n.err( I18n.ERR_309, oid ) ); 252 } 253 254 // Inject the Referral flag 255 injectReferralControl( opCtx ); 256 257 // execute operation 258 boolean result = false; 259 try 260 { 261 result = super.getDirectoryService().getOperationManager().compare( opCtx ); 262 } 263 catch ( Exception e ) 264 { 265 JndiUtils.wrap( e ); 266 } 267 268 // extract the response controls from the operation and return 269 responseControls = getResponseControls(); 270 requestControls = EMPTY_CONTROLS; 271 return result; 272 } 273 274 275 /** 276 * Calling this method tunnels an unbind call down into the partition holding 277 * the bindDn. The bind() counter part is not exposed because it is automatically 278 * called when you create a new initial context for a new connection (on wire) or 279 * (programatic) caller. 280 * 281 * @throws NamingException if there are failures encountered while unbinding 282 */ 283 public void ldapUnbind() throws NamingException 284 { 285 UnbindOperationContext opCtx = new UnbindOperationContext( getSession() ); 286 287 try 288 { 289 opCtx.addRequestControls( JndiUtils.fromJndiRequestControls( getDirectoryService().getLdapCodecService(), 290 requestControls ) ); 291 } 292 catch ( DecoderException e1 ) 293 { 294 throw new NamingException( I18n.err( I18n.ERR_309, "unbind encoder exception" ) ); 295 } 296 297 try 298 { 299 super.getDirectoryService().getOperationManager().unbind( opCtx ); 300 } 301 catch ( Exception e ) 302 { 303 JndiUtils.wrap( e ); 304 } 305 306 try 307 { 308 responseControls = JndiUtils.toJndiControls( getDirectoryService().getLdapCodecService(), 309 opCtx.getResponseControls() ); 310 } 311 catch ( EncoderException e ) 312 { 313 throw new NamingException( I18n.err( I18n.ERR_309, "unbind encoder exception" ) ); 314 } 315 requestControls = EMPTY_CONTROLS; 316 } 317 318 319 public ServerContext getRootContext() throws NamingException 320 { 321 ServerContext ctx = null; 322 323 try 324 { 325 ctx = new ServerLdapContext( getService(), getSession().getEffectivePrincipal(), new LdapName( "" ) ); 326 } 327 catch ( Exception e ) 328 { 329 JndiUtils.wrap( e ); 330 } 331 332 return ctx; 333 } 334}