Class: Mongo::Auth::SCRAM::Conversation

Inherits:
Object
  • Object
show all
Defined in:
lib/mongo/auth/scram/conversation.rb

Overview

Defines behaviour around a single SCRAM-SHA-1 conversation between the client and server.

Since:

  • 2.0.0

Constant Summary

CLIENT_CONTINUE_MESSAGE =

The base client continue message.

Since:

  • 2.0.0

{ saslContinue: 1 }.freeze
CLIENT_FIRST_MESSAGE =

The base client first message.

Since:

  • 2.0.0

{ saslStart: 1, autoAuthorize: 1 }.freeze
CLIENT_KEY =

The client key string.

Since:

  • 2.0.0

'Client Key'.freeze
DIGEST =

The digest to use for encryption.

Since:

  • 2.0.0

OpenSSL::Digest::SHA1.new.freeze
DONE =

The key for the done field in the responses.

Since:

  • 2.0.0

'done'.freeze
ID =

The conversation id field.

Since:

  • 2.0.0

'conversationId'.freeze
ITERATIONS =

The iterations key in the responses.

Since:

  • 2.0.0

/i=(\d+)/.freeze
PAYLOAD =

The payload field.

Since:

  • 2.0.0

'payload'.freeze
RNONCE =

The rnonce key in the responses.

Since:

  • 2.0.0

/r=([^,]*)/.freeze
SALT =

The salt key in the responses.

Since:

  • 2.0.0

/s=([^,]*)/.freeze
SERVER_KEY =

The server key string.

Since:

  • 2.0.0

'Server Key'.freeze
VERIFIER =

The server signature verifier in the response.

Since:

  • 2.0.0

/v=([^,]*)/.freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(user) ⇒ Conversation

Create the new conversation.

Examples:

Create the new conversation.

Conversation.new(user)

Parameters:

  • user (Auth::User)

    The user to converse about.

Since:

  • 2.0.0



216
217
218
219
220
# File 'lib/mongo/auth/scram/conversation.rb', line 216

def initialize(user)
  @user = user
  @nonce = SecureRandom.base64
  @client_key = user.send(:client_key)
end

Instance Attribute Details

#nonceString (readonly)

Returns nonce The initial user nonce.

Returns:

  • (String)

    nonce The initial user nonce.

Since:

  • 2.0.0



89
90
91
# File 'lib/mongo/auth/scram/conversation.rb', line 89

def nonce
  @nonce
end

#replyProtocol::Message (readonly)

Returns reply The current reply in the conversation.

Returns:

Since:

  • 2.0.0



93
94
95
# File 'lib/mongo/auth/scram/conversation.rb', line 93

def reply
  @reply
end

#userUser (readonly)

Returns user The user for the conversation.

Returns:

  • (User)

    user The user for the conversation.

Since:

  • 2.0.0



96
97
98
# File 'lib/mongo/auth/scram/conversation.rb', line 96

def user
  @user
end

Instance Method Details

#continue(reply, connection = nil) ⇒ Protocol::Query

Continue the SCRAM conversation. This sends the client final message to the server after setting the reply from the previous server communication.

Examples:

Continue the conversation.

conversation.continue(reply)

Parameters:

Returns:

Since:

  • 2.0.0



112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
# File 'lib/mongo/auth/scram/conversation.rb', line 112

def continue(reply, connection = nil)
  validate_first_message!(reply)

  # The salted password needs to be calculated now; otherwise, if the
  # client key is cached from a previous authentication, the salt in the
  # reply will no longer be available for when the salted password is
  # needed to calculate the server key.
  salted_password

  if connection && connection.features.op_msg_enabled?
    selector = CLIENT_CONTINUE_MESSAGE.merge(payload: client_final_message, conversationId: id)
    selector[Protocol::Msg::DATABASE_IDENTIFIER] = user.auth_source
    cluster_time = connection.mongos? && connection.cluster_time
    selector[Operation::CLUSTER_TIME] = cluster_time if cluster_time
    Protocol::Msg.new([:none], {}, selector)
  else
    Protocol::Query.new(
      user.auth_source,
      Database::COMMAND,
      CLIENT_CONTINUE_MESSAGE.merge(payload: client_final_message, conversationId: id),
      limit: -1
    )
  end
end

#finalize(reply, connection = nil) ⇒ Protocol::Query

Finalize the SCRAM conversation. This is meant to be iterated until the provided reply indicates the conversation is finished.

Examples:

Finalize the conversation.

conversation.finalize(reply)

Parameters:

Returns:

Since:

  • 2.0.0



150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
# File 'lib/mongo/auth/scram/conversation.rb', line 150

def finalize(reply, connection = nil)
  validate_final_message!(reply)
  if connection && connection.features.op_msg_enabled?
    selector = CLIENT_CONTINUE_MESSAGE.merge(payload: client_empty_message, conversationId: id)
    selector[Protocol::Msg::DATABASE_IDENTIFIER] = user.auth_source
    cluster_time = connection.mongos? && connection.cluster_time
    selector[Operation::CLUSTER_TIME] = cluster_time if cluster_time
    Protocol::Msg.new([:none], {}, selector)
  else
    Protocol::Query.new(
      user.auth_source,
      Database::COMMAND,
      CLIENT_CONTINUE_MESSAGE.merge(payload: client_empty_message, conversationId: id),
      limit: -1
    )
  end
end

#idInteger

Get the id of the conversation.

Examples:

Get the id of the conversation.

conversation.id

Returns:

  • (Integer)

    The conversation id.

Since:

  • 2.0.0



204
205
206
# File 'lib/mongo/auth/scram/conversation.rb', line 204

def id
  reply.documents[0][ID]
end

#start(connection = nil) ⇒ Protocol::Query

Start the SCRAM conversation. This returns the first message that needs to be send to the server.

Examples:

Start the conversation.

conversation.start

Parameters:

Returns:

Since:

  • 2.0.0



179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
# File 'lib/mongo/auth/scram/conversation.rb', line 179

def start(connection = nil)
  if connection && connection.features.op_msg_enabled?
    selector = CLIENT_FIRST_MESSAGE.merge(payload: client_first_message, mechanism: SCRAM::MECHANISM)
    selector[Protocol::Msg::DATABASE_IDENTIFIER] = user.auth_source
    cluster_time = connection.mongos? && connection.cluster_time
    selector[Operation::CLUSTER_TIME] = cluster_time if cluster_time
    Protocol::Msg.new([:none], {}, selector)
  else
    Protocol::Query.new(
      user.auth_source,
      Database::COMMAND,
      CLIENT_FIRST_MESSAGE.merge(payload: client_first_message, mechanism: SCRAM::MECHANISM),
      limit: -1
    )
  end
end