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}