25
25
import java .io .ByteArrayOutputStream ;
26
26
import java .io .IOException ;
27
27
import java .util .ArrayList ;
28
+ import java .util .Arrays ;
28
29
import java .util .Collections ;
29
30
import java .util .HashSet ;
30
31
import java .util .Iterator ;
40
41
public abstract class DB {
41
42
42
43
private static final Set <String > _obedientCommands = new HashSet <String >();
43
-
44
+
44
45
static {
45
46
_obedientCommands .add ("group" );
46
47
_obedientCommands .add ("aggregate" );
@@ -63,6 +64,17 @@ public DB( Mongo mongo , String name ){
63
64
_options = new Bytes .OptionHolder ( _mongo ._netOptions );
64
65
}
65
66
67
+ /**
68
+ * @param mongo the mongo instance
69
+ * @param name the database name
70
+ */
71
+ DB ( Mongo mongo , String name , String username , char [] password ) {
72
+ _mongo = mongo ;
73
+ _name = name ;
74
+ _options = new Bytes .OptionHolder ( _mongo ._netOptions );
75
+ authenticationCredentialsReference .set (new AuthenticationCredentials (username , password ));
76
+ }
77
+
66
78
/**
67
79
* Determines the read preference that should be used for the given command.
68
80
* @param command the <code>DBObject</code> representing the command
@@ -558,53 +570,70 @@ public boolean isAuthenticated() {
558
570
}
559
571
560
572
/**
561
- * Authenticates to db with the given name and password
573
+ * Authenticates to db with the given credentials. If this method (or {@code authenticateCommand} has already been
574
+ * called with the same credentials and the authentication test succeeded, this method will return true. If this method
575
+ * has already been called with different credentials and the authentication test succeeded,
576
+ * this method will throw an {@code IllegalStateException}. If this method has already been called with any credentials
577
+ * and the authentication test failed, this method will re-try the authentication test with the
578
+ * given credentials.
562
579
*
563
580
* @param username name of user for this database
564
581
* @param password password of user for this database
565
582
* @return true if authenticated, false otherwise
566
- * @throws MongoException
583
+ * @throws MongoException if authentication failed due to invalid user/pass, or other exceptions like I/O
584
+ * @throws IllegalStateException if authentiation test has already succeeded with different credentials
585
+ * @see #authenticateCommand(String, char[])
567
586
* @dochub authenticate
568
587
*/
569
588
public boolean authenticate (String username , char [] password ){
570
-
571
- if (authenticationCredentialsReference .get () != null ) {
572
- throw new IllegalStateException ("can't authenticate twice on the same database" );
573
- }
574
-
575
- AuthenticationCredentials newCredentials = new AuthenticationCredentials (username , password );
576
- CommandResult res = newCredentials .authenticate ();
577
- if (!res .ok ())
578
- return false ;
579
-
580
- boolean wasNull = authenticationCredentialsReference .compareAndSet (null , newCredentials );
581
- if (!wasNull ) {
582
- throw new IllegalStateException ("can't authenticate twice on the same database" );
583
- }
584
- return true ;
589
+ return authenticateCommandHelper (username , password ).ok ();
585
590
}
586
591
587
592
/**
588
- * Authenticates to db with the given name and password
593
+ * Authenticates to db with the given credentials. If this method (or {@code authenticate} has already been
594
+ * called with the same credentials and the authentication test succeeded, this method will return true. If this method
595
+ * has already been called with different credentials and the authentication test succeeded,
596
+ * this method will throw an {@code IllegalStateException}. If this method has already been called with any credentials
597
+ * and the authentication test failed, this method will re-try the authentication test with the
598
+ * given credentials.
599
+ *
589
600
*
590
601
* @param username name of user for this database
591
602
* @param password password of user for this database
592
603
* @return the CommandResult from authenticate command
593
604
* @throws MongoException if authentication failed due to invalid user/pass, or other exceptions like I/O
605
+ * @throws IllegalStateException if authentiation test has already succeeded with different credentials
606
+ * @see #authenticate(String, char[])
594
607
* @dochub authenticate
595
608
*/
596
609
public synchronized CommandResult authenticateCommand (String username , char [] password ){
610
+ CommandResult res = authenticateCommandHelper (username , password );
611
+ res .throwOnError ();
612
+ return res ;
613
+ }
597
614
598
- if (authenticationCredentialsReference .get () != null ) {
599
- throw new IllegalStateException ( "can't authenticate twice on the same database" );
615
+ private CommandResult authenticateCommandHelper (String username , char [] password ) {
616
+ AuthenticationCredentials currentCredentials = authenticationCredentialsReference .get ();
617
+ AuthenticationCredentials newCredentials = new AuthenticationCredentials (username , password );
618
+
619
+ if (currentCredentials != null ) {
620
+ if (currentCredentials .equals (newCredentials )) {
621
+ if (credentialsAlreadySuccessfullyTested ) {
622
+ return authenticationTestCommandResult ;
623
+ }
624
+ } else {
625
+ throw new IllegalStateException ("can't authenticate twice on the same database" );
626
+ }
600
627
}
601
628
602
- AuthenticationCredentials newCredentials = new AuthenticationCredentials (username , password );
603
629
CommandResult res = newCredentials .authenticate ();
604
- res .throwOnError ();
605
- boolean wasNull = authenticationCredentialsReference .compareAndSet (null , newCredentials );
606
- if (!wasNull ) {
607
- throw new IllegalStateException ("can't authenticate twice on the same database" );
630
+ if (res .ok ()) {
631
+ boolean wasNull = authenticationCredentialsReference .compareAndSet (null , newCredentials );
632
+ if (!wasNull && credentialsAlreadySuccessfullyTested ) {
633
+ throw new IllegalStateException ("can't authenticate twice on the same database" );
634
+ }
635
+ credentialsAlreadySuccessfullyTested = true ;
636
+ authenticationTestCommandResult = res ;
608
637
}
609
638
return res ;
610
639
}
@@ -776,8 +805,14 @@ AuthenticationCredentials getAuthenticationCredentials() {
776
805
private com .mongodb .ReadPreference _readPref ;
777
806
final Bytes .OptionHolder _options ;
778
807
808
+ // the credentials, possibly set in the constructor, in which case they have not been tested yet.
779
809
private AtomicReference <AuthenticationCredentials > authenticationCredentialsReference =
780
810
new AtomicReference <AuthenticationCredentials >();
811
+ // this can be false with credentials set if the credentials were passed in to the constructor
812
+ private volatile boolean credentialsAlreadySuccessfullyTested = false ;
813
+ // cached authentication command result, to return in case of multiple calls to authenticateCommand with the
814
+ // same credentials
815
+ private volatile CommandResult authenticationTestCommandResult ;
781
816
782
817
/**
783
818
* Encapsulate everything relating to authorization of a user on a database
@@ -797,6 +832,26 @@ private AuthenticationCredentials(final String userName, final char[] password)
797
832
this .authHash = createHash (userName , password );
798
833
}
799
834
835
+ @ Override
836
+ public boolean equals (final Object o ) {
837
+ if (this == o ) return true ;
838
+ if (o == null || getClass () != o .getClass ()) return false ;
839
+
840
+ final AuthenticationCredentials that = (AuthenticationCredentials ) o ;
841
+
842
+ if (!Arrays .equals (authHash , that .authHash )) return false ;
843
+ if (!userName .equals (that .userName )) return false ;
844
+
845
+ return true ;
846
+ }
847
+
848
+ @ Override
849
+ public int hashCode () {
850
+ int result = userName .hashCode ();
851
+ result = 31 * result + Arrays .hashCode (authHash );
852
+ return result ;
853
+ }
854
+
800
855
CommandResult authenticate () {
801
856
requestStart ();
802
857
try {
0 commit comments