1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 *
19 */
20 package org.apache.directory.server.ldap;
21
22
23 import java.util.Collections;
24 import java.util.HashMap;
25 import java.util.Map;
26 import java.util.concurrent.ConcurrentHashMap;
27
28 import org.apache.directory.server.core.CoreSession;
29 import org.apache.directory.shared.ldap.message.AbandonableRequest;
30 import org.apache.directory.shared.ldap.message.BindStatus;
31 import org.apache.mina.common.IoSession;
32 import org.slf4j.Logger;
33 import org.slf4j.LoggerFactory;
34
35
36 /**
37 * An object representing an LdapSession. Any connection established with the
38 * LDAP server forms a session.
39 *
40 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
41 * @version $Rev$, $Date$
42 */
43 public class LdapSession
44 {
45 /** The logger */
46 private static final Logger LOG = LoggerFactory.getLogger( LdapSession.class );
47
48 /** A speedup for logs */
49 private static final boolean IS_DEBUG = LOG.isDebugEnabled();
50
51
52 private static final AbandonableRequest[] EMPTY_ABANDONABLES = new AbandonableRequest[0];
53
54 private final String outstandingLock;
55
56 /**
57 * The associated IoSession. Usually, a LdapSession is established
58 * at the user request, which means we have a IoSession.
59 */
60 private final IoSession ioSession;
61
62 /** The CoreSession */
63 private CoreSession coreSession;
64
65 /** A reference on the LdapService instance */
66 private LdapService ldapService;
67
68
69 private Map<Integer, AbandonableRequest> outstandingRequests;
70
71
72 /** The current Bind status */
73 private BindStatus bindStatus;
74
75 /** The current mechanism used to authenticate the user */
76 private String currentMechanism;
77
78
79 /**
80 * A Map containing Objects used during the SASL negotiation
81 */
82 private Map<String, Object> saslProperties;
83
84
85 /**
86 * Creates a new instance of LdapSession associated with the underlying
87 * connection (MINA IoSession) to the server.
88 *
89 * @param ioSession the MINA session associated this LdapSession
90 */
91 public LdapSession( IoSession ioSession )
92 {
93 this.ioSession = ioSession;
94 outstandingLock = "OutstandingRequestLock: " + ioSession.toString();
95 outstandingRequests = new ConcurrentHashMap<Integer, AbandonableRequest>();
96 bindStatus = BindStatus.ANONYMOUS;
97 saslProperties = new HashMap<String, Object>();
98 }
99
100
101 /**
102 * Check if the session is authenticated. There are two conditions for
103 * a session to be authenticated :<br>
104 * - the coreSession must not be null<br>
105 * - and the state should be Authenticated.
106 *
107 * @return <code>true</code> if the session is not anonymous
108 */
109 public boolean isAuthenticated()
110 {
111 return ( coreSession != null ) && bindStatus == BindStatus.AUTHENTICATED;
112 }
113
114
115 /**
116 * Check if the session is authenticated. There are two conditions for
117 * a session to be authenticated :<br>
118 * - it has to exist<br>
119 * - and the session should not be anonymous.
120 *
121 * @return <code>true</code> if the session is not anonymous
122 */
123 public boolean isAnonymous()
124 {
125 return bindStatus == BindStatus.ANONYMOUS;
126 }
127
128
129 /**
130 * Check if the session is in the middle of a SASL negotiation.
131 *
132 * @return <code>true</code> if the session is in AuthPending state
133 */
134 public boolean isAuthPending()
135 {
136 return bindStatus == BindStatus.AUTH_PENDING;
137 }
138
139
140 /**
141 * Gets the MINA IoSession associated with this LdapSession.
142 *
143 * @return the MINA IoSession
144 */
145 public IoSession getIoSession()
146 {
147 return ioSession;
148 }
149
150
151 /**
152 * Gets the logical core DirectoryService session associated with this
153 * LdapSession.
154 *
155 * @return the logical core DirectoryService session
156 */
157 public CoreSession getCoreSession()
158 {
159 return coreSession;
160 }
161
162
163 /**
164 * Sets the logical core DirectoryService session.
165 *
166 * @param coreSession the logical core DirectoryService session
167 */
168 public void setCoreSession( CoreSession coreSession )
169 {
170 this.coreSession = coreSession;
171 }
172
173
174 /**
175 * Abandons all outstanding requests associated with this session.
176 */
177 public void abandonAllOutstandingRequests()
178 {
179 synchronized ( outstandingLock )
180 {
181 AbandonableRequest[] abandonables = outstandingRequests.values().toArray( EMPTY_ABANDONABLES );
182
183 for ( AbandonableRequest abandonable : abandonables )
184 {
185 abandonOutstandingRequest( abandonable.getMessageId() );
186 }
187 }
188 }
189
190
191 /**
192 * Abandons a specific request by messageId.
193 */
194 public AbandonableRequest abandonOutstandingRequest( Integer messageId )
195 {
196 AbandonableRequest request = null;
197
198 synchronized ( outstandingLock )
199 {
200 request = outstandingRequests.remove( messageId );
201 }
202
203 if ( request == null )
204 {
205 LOG.warn( "AbandonableRequest with messageId {} not found in outstandingRequests.", messageId );
206 return null;
207 }
208
209 if ( request.isAbandoned() )
210 {
211 LOG.warn( "AbandonableRequest with messageId {} has already been abandoned", messageId );
212 return request;
213 }
214
215 request.abandon();
216
217 if ( IS_DEBUG )
218 {
219 LOG.debug( "AbandonRequest on AbandonableRequest wth messageId {} was successful.", messageId );
220 }
221
222 return request;
223 }
224
225
226 /**
227 * Registers an outstanding request which can be abandoned later.
228 *
229 * @param request an outstanding request that can be abandoned
230 */
231 public void registerOutstandingRequest( AbandonableRequest request )
232 {
233 synchronized( outstandingLock )
234 {
235 outstandingRequests.put( request.getMessageId(), request );
236 }
237 }
238
239
240 /**
241 * Unregisters an outstanding request.
242 *
243 * @param request the request to unregister
244 */
245 public void unregisterOutstandingRequest( AbandonableRequest request )
246 {
247 synchronized( outstandingLock )
248 {
249 outstandingRequests.remove( request.getMessageId() );
250 }
251 }
252
253
254 public Map<Integer, AbandonableRequest> getOutstandingRequests()
255 {
256 synchronized( outstandingLock )
257 {
258 return Collections.unmodifiableMap( outstandingRequests );
259 }
260 }
261
262
263 /**
264 * @return the current bind status for this session
265 */
266 public BindStatus getBindStatus()
267 {
268 return bindStatus;
269 }
270
271
272 /**
273 * Set the current BindStatus to authentication pending
274 */
275 public void setAuthPending()
276 {
277 bindStatus = BindStatus.AUTH_PENDING;
278 }
279
280
281 /**
282 * Set the current BindStatus to Anonymous
283 */
284 public void setAnonymous()
285 {
286 bindStatus = BindStatus.ANONYMOUS;
287 }
288
289
290 /**
291 * Set the current BindStatus to authenticated
292 */
293 public void setAuthenticated()
294 {
295 bindStatus = BindStatus.AUTHENTICATED;
296 }
297
298
299 /**
300 * Get the mechanism selected by a user during a SASL Bind negotiation.
301 *
302 * @return The used mechanism, if any
303 */
304 public String getCurrentMechanism()
305 {
306 return currentMechanism;
307 }
308
309
310 /**
311 * Add a Sasl property and value
312 *
313 * @param property the property to add
314 * @param value the value for this property
315 */
316 public void putSaslProperty( String property, Object value )
317 {
318 saslProperties.put( property, value );
319 }
320
321
322 /**
323 * Get a Sasl property's value
324 *
325 * @param property the property to get
326 * @return the associated value, or null if we don't have such a property
327 */
328 public Object getSaslProperty( String property )
329 {
330 return saslProperties.get( property );
331 }
332
333
334 /**
335 * Clear all the Sasl values stored into the Map
336 */
337 public void clearSaslProperties()
338 {
339 saslProperties.clear();
340 }
341
342
343 /**
344 * Remove a property from the SaslProperty map
345 *
346 * @param property the property to remove
347 */
348 public void removeSaslProperty( String property )
349 {
350 saslProperties.remove( property );
351 }
352
353
354 /**
355 * @return The LdapService reference
356 */
357 public LdapService getLdapServer()
358 {
359 return ldapService;
360 }
361
362
363 /**
364 * Store a reference on the LdapService intance
365 *
366 * @param ldapService the LdapService instance
367 */
368 public void setLdapServer( LdapService ldapService )
369 {
370 this.ldapService = ldapService;
371 }
372 }