From 750dd3c34584c1ee6281bb8ec3d27a98c2605b21 Mon Sep 17 00:00:00 2001 From: mativm02 Date: Mon, 22 May 2023 10:30:50 -0300 Subject: [PATCH 1/3] fixing cosmosdb URL bug --- persistent/internal/helper/functions.go | 33 +++++++++++++++++--- persistent/internal/helper/functions_test.go | 9 ++++-- 2 files changed, 36 insertions(+), 6 deletions(-) diff --git a/persistent/internal/helper/functions.go b/persistent/internal/helper/functions.go index b62ec3b7..e22244e6 100644 --- a/persistent/internal/helper/functions.go +++ b/persistent/internal/helper/functions.go @@ -32,10 +32,16 @@ func IsCosmosDB(connectionString string) bool { // Example: mongodb://user:p@ssword@localhost:27017/db -> mongodb://user:p%40word@40localhost:27017/db // If there's any conflict, the function returns the original connection string. func ParsePassword(connectionString string) string { - // Find the last '@' (the delimiter between credentials and host) - at := strings.LastIndex(connectionString, "@") + // Find the last '@' before the last ':' (the delimiter between credentials and host) + // we use ':' since the URL can contain '@' characters after the port number + at := findLastAtBeforeLastColon(connectionString) if at == -1 { - return connectionString + // If there's no ':' in the connection string, we use the last '@' as delimiter + at = findLastAt(connectionString) + // If there's no '@' in the connection string, we return the original connection string + if at == -1 { + return connectionString + } } credentialsAndScheme := connectionString[:at] @@ -62,8 +68,27 @@ func ParsePassword(connectionString string) string { // URL encode the password encodedPassword := url.QueryEscape(password) + encodedUsername := url.QueryEscape(username) + // Construct the new connection string - newConnectionString := fmt.Sprintf("%s://%s:%s@%s", scheme, username, encodedPassword, hostAndDB) + newConnectionString := fmt.Sprintf("%s://%s:%s@%s", scheme, encodedUsername, encodedPassword, hostAndDB) return newConnectionString } + +func findLastAtBeforeLastColon(str string) int { + lastColon := strings.LastIndex(str, ":") + if lastColon == -1 { + return -1 + } + + subStr := str[:lastColon] + + lastAt := strings.LastIndex(subStr, "@") + + return lastAt +} + +func findLastAt(str string) int { + return strings.LastIndex(str, "@") +} diff --git a/persistent/internal/helper/functions_test.go b/persistent/internal/helper/functions_test.go index c5c519eb..fdfacc47 100644 --- a/persistent/internal/helper/functions_test.go +++ b/persistent/internal/helper/functions_test.go @@ -80,8 +80,8 @@ func TestParsePassword(t *testing.T) { }, { name: "valid connection string with @ and /", - originalConnString: "mongodb://user:p@sswor/d@localhost:27017/test", - expectedConnString: "mongodb://user:p%40sswor%2Fd@localhost:27017/test", + originalConnString: "mongodb://u=s@r:p@sswor/d@localhost:27017/test", + expectedConnString: "mongodb://u%3Ds%40r:p%40sswor%2Fd@localhost:27017/test", }, { name: "valid connection string with @ and / and '?' outside of the credentials part", @@ -138,6 +138,11 @@ func TestParsePassword(t *testing.T) { originalConnString: "mongodb://user:password@localhost:27017", expectedConnString: "mongodb://user:password@localhost:27017", }, + { + name: "cosmosdb url", + originalConnString: "mongodb://4-0-qa:zFAQ==@4-0-qa.azure:10/a1?maxIdleTimeMS=120000&appName=@4-testing@", + expectedConnString: "mongodb://4-0-qa:zFAQ%3D%3D@4-0-qa.azure:10/a1?maxIdleTimeMS=120000&appName=@4-testing@", + }, } for _, test := range tests { From 4e08cc6960ed05d171402e65da566996c6251d79 Mon Sep 17 00:00:00 2001 From: mativm02 Date: Mon, 22 May 2023 10:35:12 -0300 Subject: [PATCH 2/3] changing function name --- persistent/internal/helper/functions.go | 6 +++--- persistent/internal/helper/functions_test.go | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/persistent/internal/helper/functions.go b/persistent/internal/helper/functions.go index e22244e6..74d509ad 100644 --- a/persistent/internal/helper/functions.go +++ b/persistent/internal/helper/functions.go @@ -27,11 +27,11 @@ func IsCosmosDB(connectionString string) bool { strings.Contains(connectionString, "AccountKey=") } -// ParsePassword parses the password from the connection string and URL encodes it. -// It's useful when the password contains special characters. +// EncodeConnectionString URL encodes the password and the username from the connection string. +// It's useful when they contains special characters. // Example: mongodb://user:p@ssword@localhost:27017/db -> mongodb://user:p%40word@40localhost:27017/db // If there's any conflict, the function returns the original connection string. -func ParsePassword(connectionString string) string { +func EncodeConnectionString(connectionString string) string { // Find the last '@' before the last ':' (the delimiter between credentials and host) // we use ':' since the URL can contain '@' characters after the port number at := findLastAtBeforeLastColon(connectionString) diff --git a/persistent/internal/helper/functions_test.go b/persistent/internal/helper/functions_test.go index fdfacc47..9ae5c503 100644 --- a/persistent/internal/helper/functions_test.go +++ b/persistent/internal/helper/functions_test.go @@ -62,7 +62,7 @@ func TestIsCosmosDB(t *testing.T) { } } -func TestParsePassword(t *testing.T) { +func TestEncodeConnectionString(t *testing.T) { tests := []struct { name string originalConnString string @@ -147,7 +147,7 @@ func TestParsePassword(t *testing.T) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { - connString := ParsePassword(test.originalConnString) + connString := EncodeConnectionString(test.originalConnString) assert.Equal(t, test.expectedConnString, connString) }) } From cbc5e70c0969dc1a8186e80d1c3d34a59404b6e2 Mon Sep 17 00:00:00 2001 From: mativm02 Date: Mon, 22 May 2023 10:36:37 -0300 Subject: [PATCH 3/3] using new function name in life cycle methods --- persistent/internal/driver/mgo/life_cycle.go | 2 +- persistent/internal/driver/mongo/life_cycle.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/persistent/internal/driver/mgo/life_cycle.go b/persistent/internal/driver/mgo/life_cycle.go index 7c83aad6..f5765213 100644 --- a/persistent/internal/driver/mgo/life_cycle.go +++ b/persistent/internal/driver/mgo/life_cycle.go @@ -24,7 +24,7 @@ type lifeCycle struct { // Connect connects to the mongo database given the ClientOpts. func (lc *lifeCycle) Connect(opts *types.ClientOpts) error { - opts.ConnectionString = helper.ParsePassword(opts.ConnectionString) + opts.ConnectionString = helper.EncodeConnectionString(opts.ConnectionString) dialInfo, err := mgo.ParseURL(opts.ConnectionString) if err != nil { diff --git a/persistent/internal/driver/mongo/life_cycle.go b/persistent/internal/driver/mongo/life_cycle.go index 1309c0e0..84f9085b 100644 --- a/persistent/internal/driver/mongo/life_cycle.go +++ b/persistent/internal/driver/mongo/life_cycle.go @@ -31,7 +31,7 @@ func (lc *lifeCycle) Connect(opts *types.ClientOpts) error { var err error var client *mongo.Client - opts.ConnectionString = helper.ParsePassword(opts.ConnectionString) + opts.ConnectionString = helper.EncodeConnectionString(opts.ConnectionString) // we check if the connection string is valid before building the connOpts. cs, err := connstring.ParseAndValidate(opts.ConnectionString)