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 }