3838import java .util .HashMap ;
3939import java .util .Map ;
4040import java .util .concurrent .ConcurrentHashMap ;
41+ import java .util .concurrent .atomic .AtomicLong ;
4142import java .util .logging .Level ;
4243import java .util .logging .Logger ;
4344
@@ -55,7 +56,6 @@ public class DBPort {
5556 static final boolean USE_NAGLE = false ;
5657
5758 static final long CONN_RETRY_TIME_MS = 15000 ;
58- private boolean globallyAuthed = false ;
5959
6060 /**
6161 * creates a new DBPort
@@ -112,7 +112,7 @@ private synchronized Response go(OutMessage msg, DBCollection coll, boolean forc
112112 }
113113 }
114114
115- _calls ++ ;
115+ _calls . incrementAndGet () ;
116116
117117 if ( _socket == null )
118118 _open ();
@@ -176,7 +176,7 @@ private CommandResult convertToCommandResult(DBObject cmd, Response res) {
176176 }
177177
178178 synchronized CommandResult tryGetLastError ( DB db , long last , WriteConcern concern ) throws IOException {
179- if ( last != _calls )
179+ if ( last != _calls . get () )
180180 return null ;
181181
182182 return getLastError (db , concern );
@@ -288,6 +288,9 @@ int getLocalPort() {
288288 * closes the underlying connection and streams
289289 */
290290 protected void close (){
291+ if (_saslAuthenticator != null ) {
292+ _saslAuthenticator .clear ();
293+ }
291294 _authed .clear ();
292295
293296 if ( _socket != null ){
@@ -307,14 +310,10 @@ protected void close(){
307310 void checkAuth (DB db ) throws IOException {
308311 // TODO: add support for retry when credentials ticket times out
309312 if (db .getMongo ().getCredentials () != null ) {
310- if (!globallyAuthed ) {
311- if (!db .getMongo ().getCredentials ().getMechanism ().equals (MongoClientCredentials .GSSAPI_MECHANISM )) {
312- throw new MongoException ("Unsupported authentication mechanism: " + db .getMongo ().getCredentials ().getMechanism ());
313- }
314- new GSSAPIAuthenticator (this , db .getMongo ()).authenticate ();
315- globallyAuthed = true ;
313+ if (_saslAuthenticator == null ) {
314+ _saslAuthenticator = new GSSAPIAuthenticator (db .getMongo ());
316315 }
317- saslAcquirePrivilegeForDatabase (db );
316+ _saslAuthenticator . acquirePrivilegeForDatabase (db );
318317 }
319318 else {
320319 DB .AuthenticationCredentials credentials = db .getAuthenticationCredentials ();
@@ -337,18 +336,6 @@ void checkAuth(DB db) throws IOException {
337336 }
338337 }
339338
340- private void saslAcquirePrivilegeForDatabase (final DB db ) throws IOException {
341- BasicDBObject acquirePrivilegeCmd = new BasicDBObject ("acquirePrivilege" , 1 ).
342- append ("principal" , db .getMongo ().getCredentials ().getUserName ()).
343- append ("resource" , db .getName ());
344- if (_saslAuthed .get (db ) == null ) {
345- acquirePrivilegeCmd .append ("actions" , Arrays .asList ("oldWrite" ));
346- CommandResult res = runCommand (db .getSisterDB ("admin" ), acquirePrivilegeCmd );
347- res .throwOnError ();
348- _saslAuthed .put (db , true );
349- }
350- }
351-
352339 /**
353340 * Gets the pool that this port belongs to
354341 * @return
@@ -365,16 +352,17 @@ public DBPortPool getPool() {
365352 final Logger _logger ;
366353 final DBDecoder _decoder ;
367354
368- private Socket _socket ;
369- private InputStream _in ;
370- private OutputStream _out ;
355+ private volatile Socket _socket ;
356+ private volatile InputStream _in ;
357+ private volatile OutputStream _out ;
371358
372- private boolean _processingResponse ;
359+ private volatile boolean _processingResponse ;
373360
374- private Map <DB ,Boolean > _authed = new ConcurrentHashMap <DB , Boolean >( );
375- private Map <DB ,Boolean > _saslAuthed = new ConcurrentHashMap <DB , Boolean >( );
376- int _lastThread ;
377- long _calls = 0 ;
361+ private volatile SaslAuthenticator _saslAuthenticator ;
362+ private final Map <DB ,Boolean > _authed = new ConcurrentHashMap <DB , Boolean >( );
363+
364+ volatile int _lastThread ;
365+ final AtomicLong _calls = new AtomicLong ();
378366 private volatile ActiveState _activeState ;
379367
380368 class ActiveState {
@@ -388,21 +376,27 @@ class ActiveState {
388376 final String threadName ;
389377 }
390378
391- //TODO: add password support
392- static class GSSAPIAuthenticator extends SaslAuthenticator {
379+ class GSSAPIAuthenticator extends SaslAuthenticator {
393380 public static final String GSSAPI_OID = "1.2.840.113554.1.2.2" ;
394381 public static final String GSSAPI_MECHANISM = "GSSAPI" ;
395382 public static final String MONGODB_PROTOCOL = "mongodb" ;
396383
397- GSSAPIAuthenticator (DBPort port , final Mongo mongo ) {
398- super (port , mongo );
384+ GSSAPIAuthenticator (final Mongo mongo ) {
385+ super (mongo );
386+
387+ if (!mongo .getCredentials ().getMechanism ().equals (MongoClientCredentials .GSSAPI_MECHANISM )) {
388+ throw new MongoException ("Unsupported authentication mechanism: " + mongo .getCredentials ().getMechanism ());
389+ }
390+ }
391+
392+ @ Override
393+ protected SaslClient createSaslClient () {
399394 try {
400395 Map <String , Object > props = new HashMap <String , Object >();
401- // NOTE: I couldn't find this part documented anywhere
402396 props .put (Sasl .CREDENTIALS , getGSSCredential (mongo .getCredentials ().getUserName ()));
403397
404- saslClient = Sasl .createSaslClient (new String []{GSSAPI_MECHANISM }, mongo .getCredentials ().getUserName (), MONGODB_PROTOCOL ,
405- port . serverAddress ().getHost (), props , null );
398+ return Sasl .createSaslClient (new String []{GSSAPI_MECHANISM }, mongo .getCredentials ().getUserName (), MONGODB_PROTOCOL ,
399+ serverAddress ().getHost (), props , null );
406400 } catch (SaslException e ) {
407401 throw new MongoException ("Exception initializing SASL client" , e );
408402 } catch (GSSException e ) {
@@ -419,60 +413,91 @@ private GSSCredential getGSSCredential(String userName) throws GSSException {
419413 }
420414 }
421415
422- static class SaslAuthenticator {
423- final DBPort port ;
424- final Mongo mongo ;
425- Integer conversationId ;
426- SaslClient saslClient ;
416+ abstract class SaslAuthenticator {
417+ protected final Mongo mongo ;
418+ private final Map <DB , Boolean > authorizeDatabases = new ConcurrentHashMap <DB , Boolean >();
419+ private volatile boolean authenticated ;
427420
428- SaslAuthenticator (final DBPort port , final Mongo mongo ) {
429- this .port = port ;
421+ SaslAuthenticator (final Mongo mongo ) {
430422 this .mongo = mongo ;
431423 }
432424
433- void authenticate () throws SaslException {
434- try {
435- // Get optional initial response
436- byte [] response = (saslClient .hasInitialResponse () ? saslClient .evaluateChallenge (new byte [0 ]) : null );
437- CommandResult res = sendSaslStart (response );
438- res .throwOnError ();
425+ void clear () {
426+ authorizeDatabases .clear ();
427+ authenticated = false ;
428+ }
439429
440- while (!saslClient .isComplete () && (res .get ("done" ).equals (Boolean .FALSE ))) {
441- // Evaluate server challenge
442- response = saslClient .evaluateChallenge ((byte []) res .get ("payload" ));
430+ void authenticate () {
431+ if (authenticated ) {
432+ return ;
433+ }
443434
444- if (res .get ("done" ).equals (Boolean .TRUE )) {
445- // done; server doesn't expect any more SASL data
446- if (response != null ) {
447- throw new MongoException ("SASL protocol error: attempting to send response after completion" );
435+ SaslClient saslClient = createSaslClient ();
436+ try {
437+ // Get optional initial response
438+ byte [] response = (saslClient .hasInitialResponse () ? saslClient .evaluateChallenge (new byte [0 ]) : null );
439+ CommandResult res = sendSaslStart (response );
440+ res .throwOnError ();
441+
442+ int conversationId = (Integer ) res .get ("conversationId" );
443+
444+ while (!saslClient .isComplete () && (res .get ("done" ).equals (Boolean .FALSE ))) {
445+ // Evaluate server challenge
446+ response = saslClient .evaluateChallenge ((byte []) res .get ("payload" ));
447+
448+ if (res .get ("done" ).equals (Boolean .TRUE )) {
449+ // done; server doesn't expect any more SASL data
450+ if (response != null ) {
451+ throw new MongoException ("SASL protocol error: attempting to send response after completion" );
452+ }
453+ break ;
454+ } else {
455+ res = sendSaslContinue (conversationId , response );
456+ res .throwOnError ();
448457 }
449- break ;
450- } else {
451- res = sendSaslContinue (response );
452- res .throwOnError ();
453458 }
454- }
459+ authenticated = true ;
455460 } catch (IOException e ) {
456461 // TODO: Fix exception message.
457- throw new MongoException ("" , e );
462+ throw new MongoException ("Exception authenticating with SASL" , e );
463+ } finally {
464+ try {
465+ saslClient .dispose ();
466+ } catch (SaslException e ) {
467+ // ignore
468+ }
458469 }
459470 }
460471
472+ protected abstract SaslClient createSaslClient ();
473+
461474 private CommandResult sendSaslStart (final byte [] outToken ) throws IOException {
462475// System.out.println("Sending saslStart with num bytes: " + outToken.length);
463476 DB adminDB = mongo .getDB ("admin" );
464477 DBObject cmd = new BasicDBObject ("saslStart" , 1 ).append ("mechanism" , "GSSAPI" ).append ("payload" , outToken );
465- CommandResult res = port .runCommand (adminDB , cmd );
466- conversationId = (Integer ) res .get ("conversationId" );
467- return res ;
478+ return runCommand (adminDB , cmd );
468479 }
469480
470- private CommandResult sendSaslContinue (final byte [] outToken ) throws IOException {
481+ private CommandResult sendSaslContinue (final int conversationId , final byte [] outToken ) throws IOException {
471482// System.out.println("Sending saslContinue with num bytes: " + outToken.length);
472483 DB adminDB = mongo .getDB ("admin" );
473484 DBObject cmd = new BasicDBObject ("saslContinue" , 1 ).append ("conversationId" , conversationId ).
474485 append ("payload" , outToken );
475- return port .runCommand (adminDB , cmd );
486+ return runCommand (adminDB , cmd );
487+ }
488+
489+ public void acquirePrivilegeForDatabase (final DB db ) throws IOException {
490+ authenticate ();
491+
492+ if (authorizeDatabases .get (db ) == null ) {
493+ BasicDBObject acquirePrivilegeCmd = new BasicDBObject ("acquirePrivilege" , 1 ).
494+ append ("principal" , db .getMongo ().getCredentials ().getUserName ()).
495+ append ("resource" , db .getName ()).
496+ append ("actions" , Arrays .asList ("oldWrite" ));
497+ CommandResult res = runCommand (db .getSisterDB ("admin" ), acquirePrivilegeCmd );
498+ res .throwOnError ();
499+ authorizeDatabases .put (db , true );
500+ }
476501 }
477502 }
478503
0 commit comments