333
schangxiang@126.com
2025-09-19 18966e02fb573c7e2bb0c6426ed792b38b910940
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
'use strict';
 
const MongoError = require('../error').MongoError;
 
/**
 * Creates a new AuthProvider, which dictates how to authenticate for a given
 * mechanism.
 * @class
 */
class AuthProvider {
  constructor(bson) {
    this.bson = bson;
    this.authStore = [];
  }
 
  /**
   * Authenticate
   * @method
   * @param {SendAuthCommand} sendAuthCommand Writes an auth command directly to a specific connection
   * @param {Connection[]} connections Connections to authenticate using this authenticator
   * @param {MongoCredentials} credentials Authentication credentials
   * @param {authResultCallback} callback The callback to return the result from the authentication
   */
  auth(sendAuthCommand, connections, credentials, callback) {
    // Total connections
    let count = connections.length;
 
    if (count === 0) {
      callback(null, null);
      return;
    }
 
    // Valid connections
    let numberOfValidConnections = 0;
    let errorObject = null;
 
    const execute = connection => {
      this._authenticateSingleConnection(sendAuthCommand, connection, credentials, (err, r) => {
        // Adjust count
        count = count - 1;
 
        // If we have an error
        if (err) {
          errorObject = new MongoError(err);
        } else if (r && (r.$err || r.errmsg)) {
          errorObject = new MongoError(r);
        } else {
          numberOfValidConnections = numberOfValidConnections + 1;
        }
 
        // Still authenticating against other connections.
        if (count !== 0) {
          return;
        }
 
        // We have authenticated all connections
        if (numberOfValidConnections > 0) {
          // Store the auth details
          this.addCredentials(credentials);
          // Return correct authentication
          callback(null, true);
        } else {
          if (errorObject == null) {
            errorObject = new MongoError(`failed to authenticate using ${credentials.mechanism}`);
          }
          callback(errorObject, false);
        }
      });
    };
 
    const executeInNextTick = _connection => process.nextTick(() => execute(_connection));
 
    // For each connection we need to authenticate
    while (connections.length > 0) {
      executeInNextTick(connections.shift());
    }
  }
 
  /**
   * Implementation of a single connection authenticating. Is meant to be overridden.
   * Will error if called directly
   * @ignore
   */
  _authenticateSingleConnection(/*sendAuthCommand, connection, credentials, callback*/) {
    throw new Error('_authenticateSingleConnection must be overridden');
  }
 
  /**
   * Adds credentials to store only if it does not exist
   * @param {MongoCredentials} credentials credentials to add to store
   */
  addCredentials(credentials) {
    const found = this.authStore.some(cred => cred.equals(credentials));
 
    if (!found) {
      this.authStore.push(credentials);
    }
  }
 
  /**
   * Re authenticate pool
   * @method
   * @param {SendAuthCommand} sendAuthCommand Writes an auth command directly to a specific connection
   * @param {Connection[]} connections Connections to authenticate using this authenticator
   * @param {authResultCallback} callback The callback to return the result from the authentication
   */
  reauthenticate(sendAuthCommand, connections, callback) {
    const authStore = this.authStore.slice(0);
    let count = authStore.length;
    if (count === 0) {
      return callback(null, null);
    }
 
    for (let i = 0; i < authStore.length; i++) {
      this.auth(sendAuthCommand, connections, authStore[i], function(err) {
        count = count - 1;
        if (count === 0) {
          callback(err, null);
        }
      });
    }
  }
 
  /**
   * Remove credentials that have been previously stored in the auth provider
   * @method
   * @param {string} source Name of database we are removing authStore details about
   * @return {object}
   */
  logout(source) {
    this.authStore = this.authStore.filter(credentials => credentials.source !== source);
  }
}
 
/**
 * A function that writes authentication commands to a specific connection
 * @callback SendAuthCommand
 * @param {Connection} connection The connection to write to
 * @param {Command} command A command with a toBin method that can be written to a connection
 * @param {AuthWriteCallback} callback Callback called when command response is received
 */
 
/**
 * A callback for a specific auth command
 * @callback AuthWriteCallback
 * @param {Error} err If command failed, an error from the server
 * @param {object} r The response from the server
 */
 
/**
 * This is a result from an authentication strategy
 *
 * @callback authResultCallback
 * @param {error} error An error object. Set to null if no error present
 * @param {boolean} result The result of the authentication process
 */
 
module.exports = { AuthProvider };