1818
1919package com .mongodb ;
2020
21+ import com .mongodb .DBApiLayer .Result ;
22+ import com .mongodb .util .Util ;
23+ import org .bson .BSONObject ;
24+
2125import java .io .ByteArrayOutputStream ;
2226import java .io .IOException ;
2327import java .util .ArrayList ;
2731import java .util .LinkedHashSet ;
2832import java .util .List ;
2933import java .util .Set ;
30-
31- import com .mongodb .DBApiLayer .Result ;
32- import com .mongodb .util .Util ;
33- import org .bson .BSONObject ;
34+ import java .util .concurrent .atomic .AtomicReference ;
3435
3536/**
3637 * an abstract class that represents a logical database on a server
@@ -533,97 +534,62 @@ public void dropDatabase(){
533534 * @return true if authenticated, false otherwise
534535 * @dochub authenticate
535536 */
536- public synchronized boolean isAuthenticated () {
537- return ( _username != null ) ;
537+ public boolean isAuthenticated () {
538+ return authorizationCredentialsReference . get () != null ;
538539 }
539540
540541 /**
541542 * Authenticates to db with the given name and password
542543 *
543544 * @param username name of user for this database
544- * @param passwd password of user for this database
545+ * @param password password of user for this database
545546 * @return true if authenticated, false otherwise
546547 * @throws MongoException
547548 * @dochub authenticate
548549 */
549- public synchronized boolean authenticate (String username , char [] passwd ){
550-
551- if ( username == null || passwd == null )
552- throw new NullPointerException ( "username can't be null" );
550+ public boolean authenticate (String username , char [] password ){
553551
554- if ( _username != null )
555- throw new IllegalStateException ( "can't call authenticate twice on the same DBObject" );
552+ if (authorizationCredentialsReference .get () != null ) {
553+ throw new IllegalStateException ("can't authenticate twice on the same database" );
554+ }
556555
557- String hash = _hash ( username , passwd );
558- CommandResult res = _doauth ( username , hash . getBytes () );
559- if ( !res .ok ())
556+ AuthorizationCredentials newCredentials = new AuthorizationCredentials ( username , password );
557+ CommandResult res = newCredentials . authorize ( );
558+ if (!res .ok ())
560559 return false ;
561- _username = username ;
562- _authhash = hash .getBytes ();
560+
561+ boolean wasNull = authorizationCredentialsReference .compareAndSet (null , newCredentials );
562+ if (!wasNull ) {
563+ throw new IllegalStateException ("can't authenticate twice on the same database" );
564+ }
563565 return true ;
564566 }
565567
566568 /**
567569 * Authenticates to db with the given name and password
568570 *
569571 * @param username name of user for this database
570- * @param passwd password of user for this database
572+ * @param password password of user for this database
571573 * @return the CommandResult from authenticate command
572574 * @throws MongoException if authentication failed due to invalid user/pass, or other exceptions like I/O
573575 * @dochub authenticate
574576 */
575- public synchronized CommandResult authenticateCommand (String username , char [] passwd ){
576-
577- if ( username == null || passwd == null )
578- throw new NullPointerException ( "username can't be null" );
577+ public synchronized CommandResult authenticateCommand (String username , char [] password ){
579578
580- if ( _username != null )
581- throw new IllegalStateException ( "can't call authenticate twice on the same DBObject" );
579+ if (authorizationCredentialsReference .get () != null ) {
580+ throw new IllegalStateException ( "can't authenticate twice on the same database" );
581+ }
582582
583- String hash = _hash ( username , passwd );
584- CommandResult res = _doauth ( username , hash . getBytes () );
583+ AuthorizationCredentials newCredentials = new AuthorizationCredentials ( username , password );
584+ CommandResult res = newCredentials . authorize ( );
585585 res .throwOnError ();
586- _username = username ;
587- _authhash = hash .getBytes ();
586+ boolean wasNull = authorizationCredentialsReference .compareAndSet (null , newCredentials );
587+ if (!wasNull ) {
588+ throw new IllegalStateException ("can't authenticate twice on the same database" );
589+ }
588590 return res ;
589591 }
590592
591- /*
592- boolean reauth(){
593- if ( _username == null || _authhash == null )
594- throw new IllegalStateException( "no auth info!" );
595- return _doauth( _username , _authhash );
596- }
597- */
598-
599- synchronized DBObject _authCommand ( String nonce ){
600- if ( _username == null || _authhash == null )
601- throw new IllegalStateException ( "no auth info!" );
602-
603- return _authCommand ( nonce , _username , _authhash );
604- }
605-
606- static DBObject _authCommand ( String nonce , String username , byte [] hash ){
607- String key = nonce + username + new String ( hash );
608-
609- BasicDBObject cmd = new BasicDBObject ();
610-
611- cmd .put ("authenticate" , 1 );
612- cmd .put ("user" , username );
613- cmd .put ("nonce" , nonce );
614- cmd .put ("key" , Util .hexMD5 (key .getBytes ()));
615-
616- return cmd ;
617- }
618-
619- private CommandResult _doauth ( String username , byte [] hash ) {
620- CommandResult res = command (new BasicDBObject ("getnonce" , 1 ));
621- res .throwOnError ();
622-
623- DBObject cmd = _authCommand ( res .getString ( "nonce" ) , username , hash );
624- return command (cmd );
625- }
626-
627593 /**
628594 * Adds a new user for this db
629595 * @param username
@@ -739,7 +705,7 @@ public DB getSisterDB( String name ){
739705 * Makes it possible to execute "read" queries on a slave node
740706 *
741707 * @deprecated Replaced with ReadPreference.SECONDARY
742- * @see com.mongodb.ReadPreference. SECONDARY
708+ * @see com.mongodb.ReadPreference# SECONDARY
743709 */
744710 @ Deprecated
745711 public void slaveOk (){
@@ -779,9 +745,9 @@ public int getOptions(){
779745
780746 public abstract void cleanCursors ( boolean force );
781747
782- synchronized String getUsername () {
783- return _username ;
784- }
748+ AuthorizationCredentials getAuthorizationCredentials () {
749+ return authorizationCredentialsReference . get () ;
750+ }
785751
786752 final Mongo _mongo ;
787753 final String _name ;
@@ -791,7 +757,66 @@ synchronized String getUsername() {
791757 private com .mongodb .ReadPreference _readPref ;
792758 final Bytes .OptionHolder _options ;
793759
794- private String _username ;
795- private byte [] _authhash = null ;
760+ private AtomicReference <AuthorizationCredentials > authorizationCredentialsReference =
761+ new AtomicReference <AuthorizationCredentials >();
762+
763+ /**
764+ * Encapsulate everything relating to authorization of a user on a database
765+ */
766+ class AuthorizationCredentials {
767+ private final String userName ;
768+ private final byte [] authHash ;
796769
770+ private AuthorizationCredentials (final String userName , final char [] password ) {
771+ if (userName == null ) {
772+ throw new IllegalArgumentException ("userName can not be null" );
773+ }
774+ if (password == null ) {
775+ throw new IllegalArgumentException ("password can not be null" );
776+ }
777+ this .userName = userName ;
778+ this .authHash = createHash (userName , password );
779+ }
780+
781+ CommandResult authorize () {
782+ CommandResult res = command (getNonceCommand ());
783+ res .throwOnError ();
784+
785+ return command (getAuthCommand (res .getString ("nonce" )));
786+ }
787+
788+ DBObject getAuthCommand ( String nonce ){
789+ String key = nonce + userName + new String ( authHash );
790+
791+ BasicDBObject cmd = new BasicDBObject ();
792+
793+ cmd .put ("authenticate" , 1 );
794+ cmd .put ("user" , userName );
795+ cmd .put ("nonce" , nonce );
796+ cmd .put ("key" , Util .hexMD5 (key .getBytes ()));
797+
798+ return cmd ;
799+ }
800+
801+ BasicDBObject getNonceCommand () {
802+ return new BasicDBObject ("getnonce" , 1 );
803+ }
804+
805+ private byte [] createHash ( String userName , char [] password ){
806+ ByteArrayOutputStream bout = new ByteArrayOutputStream ( userName .length () + 20 + password .length );
807+ try {
808+ bout .write (userName .getBytes ());
809+ bout .write ( ":mongo:" .getBytes () );
810+ for (final char ch : password ) {
811+ if (ch >= 128 )
812+ throw new IllegalArgumentException ("can't handle non-ascii passwords yet" );
813+ bout .write ((byte ) ch );
814+ }
815+ }
816+ catch ( IOException ioe ){
817+ throw new RuntimeException ( "impossible" , ioe );
818+ }
819+ return Util .hexMD5 ( bout .toByteArray () ).getBytes ();
820+ }
821+ }
797822}
0 commit comments