Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Support for the PLAIN authMechanism #223

Merged
merged 4 commits into from
Nov 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -705,6 +705,11 @@ separated in sub folders and module namespaces.
## Auth Mechanisms

For versions of Mongo 3.0 and greater, the auth mechanism defaults to SCRAM.

If connecting to MongoDB Enterprise Edition or MongoDB Atlas, the [PLAIN](https://www.mongodb.com/docs/manual/tutorial/authenticate-nativeldap-activedirectory/)
auth mechanism is supported for LDAP authentication. The GSSAPI auth mechanism used for Kerberos authentication
is not currently supported.

If you'd like to use [MONGODB-X509](https://www.mongodb.com/docs/v6.0/tutorial/configure-x509-client-authentication/)
authentication, you can specify that as a `start_link` option.

Expand Down
4 changes: 4 additions & 0 deletions lib/mongo/auth.ex
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@ defmodule Mongo.Auth do
Mongo.Auth.X509
end

defp mechanism(%{wire_version: version, auth_mechanism: :plain}) when version >= 3 do
Mongo.Auth.PLAIN
end

defp mechanism(%{wire_version: version}) when version >= 3 do
Mongo.Auth.SCRAM
end
Expand Down
31 changes: 31 additions & 0 deletions lib/mongo/auth/plain.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
defmodule Mongo.Auth.PLAIN do
@moduledoc false
alias Mongo.MongoDBConnection.Utils

def auth({nil, nil}, _db, _s) do
:ok
end

def auth({username, password}, _db, s) do
auth_payload = build_auth_payload(username, password)
message = [saslStart: 1, mechanism: "PLAIN", payload: auth_payload]

case Utils.command(-3, message, s) do
{:ok, _flags, %{"ok" => ok, "done" => true}} when ok == 1 ->
:ok

{:ok, _flags, %{"ok" => ok, "errmsg" => reason, "code" => code}} when ok == 0 ->
{:error, Mongo.Error.exception(message: "auth failed for user #{username}: #{reason}", code: code)}

error ->
error
end
end

defp build_auth_payload(username, password) do
# https://www.ietf.org/rfc/rfc4616.txt
# Null separate listed of authorization ID (blank), username, password. These are sent as raw UTF-8.
payload = "\0#{username}\0#{password}"
%BSON.Binary{binary: payload}
end
end
1 change: 1 addition & 0 deletions lib/mongo/url_parser.ex
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ defmodule Mongo.UrlParser do

defp decode_percent(:username, value), do: URI.decode_www_form(value)
defp decode_percent(:password, value), do: URI.decode_www_form(value)
defp decode_percent(:auth_source, value), do: URI.decode_www_form(value)
defp decode_percent(_other, value), do: value

defp parse_query_options(opts, %{"options" => options}) when is_binary(options) do
Expand Down
20 changes: 20 additions & 0 deletions test/mongo/url_parser_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -199,5 +199,25 @@ defmodule Mongo.UrlParserTest do
username = Keyword.get(opts, :username)
assert username == real_username
end

test "external auth source " do
encoded_external_auth_source = URI.encode_www_form("$external")
url = "mongodb://user:[email protected]:27017,seed2.domain.com:27017,seed3.domain.com:27017/db_name?replicaSet=set-name&authMechanism=PLAIN&authSource=#{encoded_external_auth_source}&tls=true"

assert UrlParser.parse_url(url: url) |> Keyword.drop([:pw_safe]) == [
password: "*****",
username: "user",
database: "db_name",
tls: true,
auth_source: "$external",
auth_mechanism: :plain,
set_name: "set-name",
seeds: [
"seed1.domain.com:27017",
"seed2.domain.com:27017",
"seed3.domain.com:27017"
]
]
end
end
end
Loading