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     */
020    package org.apache.directory.server.core.jndi;
021    
022    
023    import java.util.Hashtable;
024    
025    import javax.naming.Name;
026    import javax.naming.NamingException;
027    import javax.naming.directory.InvalidAttributeIdentifierException;
028    import javax.naming.ldap.Control;
029    import javax.naming.ldap.ExtendedRequest;
030    import javax.naming.ldap.ExtendedResponse;
031    import javax.naming.ldap.LdapContext;
032    import javax.naming.ldap.LdapName;
033    
034    import org.apache.directory.server.core.CoreSession;
035    import org.apache.directory.server.core.DirectoryService;
036    import org.apache.directory.server.core.LdapPrincipal;
037    import org.apache.directory.server.core.interceptor.context.CompareOperationContext;
038    import org.apache.directory.server.core.interceptor.context.UnbindOperationContext;
039    import org.apache.directory.server.i18n.I18n;
040    import org.apache.directory.shared.ldap.NotImplementedException;
041    import org.apache.directory.shared.ldap.entry.BinaryValue;
042    import org.apache.directory.shared.ldap.entry.StringValue;
043    import org.apache.directory.shared.ldap.entry.Value;
044    import org.apache.directory.shared.ldap.exception.LdapException;
045    import org.apache.directory.shared.ldap.jndi.JndiUtils;
046    import org.apache.directory.shared.ldap.name.DN;
047    import org.apache.directory.shared.ldap.schema.AttributeType;
048    import org.apache.directory.shared.ldap.util.StringTools;
049    
050    
051    /**
052     * An implementation of a JNDI LdapContext.
053     *
054     * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
055     * @version $Rev: 928945 $
056     */
057    public 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(), DN.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            if ( attributeType.getSyntax().isHumanReadable() )
205            {
206                if ( value instanceof String )
207                {
208                    val = new StringValue( attributeType, (String)value );
209                }
210                else if ( value instanceof byte[] )
211                {
212                    val = new StringValue( attributeType, StringTools.utf8ToString( (byte[])value ) );
213                }
214                else
215                {
216                    throw new NamingException( I18n.err( I18n.ERR_309, oid ) );
217                }
218            }
219            else
220            {
221                if ( value instanceof String )
222                {
223                    val = new BinaryValue( attributeType, StringTools.getBytesUtf8( (String)value ) );
224                }
225                else if ( value instanceof byte[] )
226                {
227                    val = new BinaryValue( attributeType, (byte[])value );
228                }
229                else
230                {
231                    throw new NamingException( I18n.err( I18n.ERR_309, oid ) );
232                }
233            }
234            
235            
236            CompareOperationContext opCtx = new CompareOperationContext( getSession(), name, oid, val );
237            opCtx.addRequestControls( JndiUtils.fromJndiControls( requestControls ) );
238            
239            // Inject the Referral flag
240            injectReferralControl( opCtx );
241    
242            // execute operation
243            boolean result = false;
244            try
245            {
246                result = super.getDirectoryService().getOperationManager().compare( opCtx );
247            }
248            catch ( Exception e )
249            {
250                JndiUtils.wrap( e );
251            }
252            
253            // extract the response controls from the operation and return
254            responseControls = getResponseControls();
255            requestControls = EMPTY_CONTROLS;
256            return result;
257        }
258    
259    
260        /**
261         * Calling this method tunnels an unbind call down into the partition holding 
262         * the bindDn.  The bind() counter part is not exposed because it is automatically
263         * called when you create a new initial context for a new connection (on wire) or 
264         * (programatic) caller.
265         * 
266         * @throws NamingException if there are failures encountered while unbinding
267         */
268        public void ldapUnbind() throws NamingException
269        {
270            UnbindOperationContext opCtx = new UnbindOperationContext( getSession() );
271            opCtx.addRequestControls( JndiUtils.fromJndiControls( requestControls ) );
272    
273            try
274            {
275                super.getDirectoryService().getOperationManager().unbind( opCtx );
276            }
277            catch ( Exception e )
278            {
279                JndiUtils.wrap( e );
280            }
281            
282            responseControls = JndiUtils.toJndiControls( opCtx.getResponseControls() );
283            requestControls = EMPTY_CONTROLS;
284        }
285    
286    
287        public ServerContext getRootContext() throws NamingException
288        {
289            ServerContext ctx = null;
290            
291            try
292            {
293                ctx = new ServerLdapContext( getService(), getSession().getEffectivePrincipal(), new LdapName( "" ) );
294            }
295            catch ( Exception e )
296            {
297                JndiUtils.wrap( e );
298            }
299            
300            return ctx;
301        }
302    }