14
14
*/
15
15
16
16
using System ;
17
+ using System . Linq ;
17
18
using System . Threading ;
18
19
using System . Threading . Tasks ;
20
+ using MongoDB . Bson ;
19
21
using MongoDB . Driver . Core . Connections ;
20
22
using MongoDB . Driver . Core . Misc ;
21
23
22
24
namespace MongoDB . Driver . Core . Authentication
23
25
{
24
26
/// <summary>
25
- /// The default authenticator (uses SCRAM-SHA1 if possible, falls back to MONGODB-CR otherwise).
27
+ /// The default authenticator.
28
+ /// If saslSupportedMechs is not present in the isMaster results for mechanism negotiation
29
+ /// uses SCRAM-SHA-1 when talking to servers >= 3.0. Prior to server 3.0, uses MONGODB-CR.
30
+ /// Else, uses SCRAM-SHA-256 if present in the list of mechanisms. Otherwise, uses
31
+ /// SCRAM-SHA-1 the default, regardless of whether SCRAM-SHA-1 is in the list.
26
32
/// </summary>
27
33
public class DefaultAuthenticator : IAuthenticator
28
34
{
@@ -48,10 +54,7 @@ internal DefaultAuthenticator(UsernamePasswordCredential credential, IRandomStri
48
54
49
55
// properties
50
56
/// <inheritdoc/>
51
- public string Name
52
- {
53
- get { return "DEFAULT" ; }
54
- }
57
+ public string Name => "DEFAULT" ;
55
58
56
59
// methods
57
60
/// <inheritdoc/>
@@ -60,30 +63,83 @@ public void Authenticate(IConnection connection, ConnectionDescription descripti
60
63
Ensure . IsNotNull ( connection , nameof ( connection ) ) ;
61
64
Ensure . IsNotNull ( description , nameof ( description ) ) ;
62
65
63
- var authenticator = CreateAuthenticator ( description ) ;
66
+ // If we don't have SaslSupportedMechs as part of the response, that means we didn't piggyback the initial
67
+ // isMaster request and should query the server (provided that the server >= 4.0), merging results into
68
+ // a new ConnectionDescription
69
+ if ( ! description . IsMasterResult . HasSaslSupportedMechs
70
+ && Feature . ScramSha256Authentication . IsSupported ( description . ServerVersion ) )
71
+ {
72
+ var command = CustomizeInitialIsMasterCommand ( IsMasterHelper . CreateCommand ( ) ) ;
73
+ var isMasterProtocol = IsMasterHelper . CreateProtocol ( command ) ;
74
+ var isMasterResult = IsMasterHelper . GetResult ( connection , isMasterProtocol , cancellationToken ) ;
75
+ var mergedIsMasterResult = new IsMasterResult ( description . IsMasterResult . Wrapped . Merge ( isMasterResult . Wrapped ) ) ;
76
+ description = new ConnectionDescription (
77
+ description . ConnectionId ,
78
+ mergedIsMasterResult ,
79
+ description . BuildInfoResult ) ;
80
+ }
81
+
82
+ var authenticator = CreateAuthenticator ( connection , description ) ;
64
83
authenticator . Authenticate ( connection , description , cancellationToken ) ;
84
+
65
85
}
66
86
67
87
/// <inheritdoc/>
68
- public Task AuthenticateAsync ( IConnection connection , ConnectionDescription description , CancellationToken cancellationToken )
88
+ public async Task AuthenticateAsync ( IConnection connection , ConnectionDescription description , CancellationToken cancellationToken )
69
89
{
70
90
Ensure . IsNotNull ( connection , nameof ( connection ) ) ;
71
91
Ensure . IsNotNull ( description , nameof ( description ) ) ;
92
+
93
+ // If we don't have SaslSupportedMechs as part of the response, that means we didn't piggyback the initial
94
+ // isMaster request and should query the server (provided that the server >= 4.0), merging results into
95
+ // a new ConnectionDescription
96
+ if ( ! description . IsMasterResult . HasSaslSupportedMechs
97
+ && Feature . ScramSha256Authentication . IsSupported ( description . ServerVersion ) )
98
+ {
99
+ var command = CustomizeInitialIsMasterCommand ( IsMasterHelper . CreateCommand ( ) ) ;
100
+ var isMasterProtocol = IsMasterHelper . CreateProtocol ( command ) ;
101
+ var isMasterResult = await IsMasterHelper . GetResultAsync ( connection , isMasterProtocol , cancellationToken ) . ConfigureAwait ( false ) ;
102
+ var mergedIsMasterResult = new IsMasterResult ( description . IsMasterResult . Wrapped . Merge ( isMasterResult . Wrapped ) ) ;
103
+ description = new ConnectionDescription (
104
+ description . ConnectionId ,
105
+ mergedIsMasterResult ,
106
+ description . BuildInfoResult ) ;
107
+ }
108
+
109
+ var authenticator = CreateAuthenticator ( connection , description ) ;
110
+ await authenticator . AuthenticateAsync ( connection , description , cancellationToken ) . ConfigureAwait ( false ) ;
111
+ }
72
112
73
- var authenticator = CreateAuthenticator ( description ) ;
74
- return authenticator . AuthenticateAsync ( connection , description , cancellationToken ) ;
113
+
114
+ /// <inheritdoc/>
115
+ public BsonDocument CustomizeInitialIsMasterCommand ( BsonDocument isMasterCommand )
116
+ {
117
+ return isMasterCommand . Merge ( CreateSaslSupportedMechsRequest ( _credential . Source , _credential . Username ) ) ;
75
118
}
76
119
77
- private IAuthenticator CreateAuthenticator ( ConnectionDescription description )
120
+ private static BsonDocument CreateSaslSupportedMechsRequest ( string authenticationDatabaseName , string userName )
78
121
{
79
- if ( Feature . ScramSha1Authentication . IsSupported ( description . ServerVersion ) )
80
- {
81
- return new ScramSha1Authenticator ( _credential , _randomStringGenerator ) ;
82
- }
83
- else
122
+ return new BsonDocument { { "saslSupportedMechs" , $ "{ authenticationDatabaseName } .{ userName } "} } ;
123
+ }
124
+
125
+ // see https://github.com/mongodb/specifications/blob/master/source/auth/auth.rst#defaults
126
+ private IAuthenticator CreateAuthenticator ( IConnection connection , ConnectionDescription description )
127
+ {
128
+ // If a saslSupportedMechs field was present in the isMaster results for mechanism negotiation,
129
+ // then it MUST be inspected to select a default mechanism.
130
+ if ( description . IsMasterResult . HasSaslSupportedMechs )
84
131
{
85
- return new MongoDBCRAuthenticator ( _credential ) ;
132
+ // If SCRAM-SHA-256 is present in the list of mechanisms, then it MUST be used as the default;
133
+ // otherwise, SCRAM-SHA-1 MUST be used as the default, regardless of whether SCRAM-SHA-1 is in the list.
134
+ return description . IsMasterResult . SaslSupportedMechs . Contains ( "SCRAM-SHA-256" )
135
+ ? ( IAuthenticator ) new ScramSha256Authenticator ( _credential , _randomStringGenerator )
136
+ : new ScramSha1Authenticator ( _credential , _randomStringGenerator ) ;
86
137
}
138
+ // If saslSupportedMechs is not present in the isMaster results for mechanism negotiation, then SCRAM-SHA-1
139
+ // MUST be used when talking to servers >= 3.0. Prior to server 3.0, MONGODB-CR MUST be used.
140
+ return Feature . ScramSha1Authentication . IsSupported ( description . ServerVersion )
141
+ ? ( IAuthenticator ) new ScramSha1Authenticator ( _credential , _randomStringGenerator )
142
+ : new MongoDBCRAuthenticator ( _credential ) ;
87
143
}
88
144
}
89
145
}
0 commit comments