@@ -152,7 +152,7 @@ def set_secret(service_client, arn, token):
152152 previous_dict = None
153153 current_dict = get_secret_dict (service_client , arn , "AWSCURRENT" )
154154 pending_dict = get_secret_dict (service_client , arn , "AWSPENDING" , token )
155-
155+
156156 # First try to login with the pending secret, if it succeeds, return
157157 conn = get_connection (pending_dict )
158158 if conn :
@@ -175,12 +175,13 @@ def set_secret(service_client, arn, token):
175175
176176 # If both current and pending do not work, try previous
177177 if not conn and previous_dict :
178- # If both current and pending do not work, try previous
179178 # Update previous_dict to leverage current SSL settings
180179 previous_dict .pop ('ssl' , None )
181180 if 'ssl' in current_dict :
182181 previous_dict ['ssl' ] = current_dict ['ssl' ]
183182
183+ conn = get_connection (previous_dict )
184+
184185 # Make sure the user/host from previous and pending match
185186 if previous_dict ['username' ] != pending_dict ['username' ]:
186187 logger .error ("setSecret: Attempting to modify user %s other than previous valid user %s" % (pending_dict ['username' ], previous_dict ['username' ]))
@@ -277,8 +278,9 @@ def finish_secret(service_client, arn, token):
277278def get_connection (secret_dict ):
278279 """Gets a connection to MongoDB from a secret dictionary
279280
280- This helper function tries to connect to the database grabbing connection info
281- from the secret dictionary. If successful, it returns the connection, else None
281+ This helper function uses connectivity information from the secret dictionary to initiate
282+ connection attempt(s) to the database. Will attempt a fallback, non-SSL connection when
283+ initial connection fails using SSL and fall_back is True.
282284
283285 Args:
284286 secret_dict (dict): The Secret Dictionary
@@ -293,16 +295,83 @@ def get_connection(secret_dict):
293295 # Parse and validate the secret JSON string
294296 port = int (secret_dict ['port' ]) if 'port' in secret_dict else 27017
295297 dbname = secret_dict ['dbname' ] if 'dbname' in secret_dict else "admin"
296- ssl = False
297- if 'ssl' in secret_dict :
298- if type (secret_dict ['ssl' ]) is bool :
299- ssl = secret_dict ['ssl' ]
298+
299+ # Get SSL connectivity configuration
300+ use_ssl , fall_back = get_ssl_config (secret_dict )
301+
302+ # if an 'ssl' key is not found or does not contain a valid value, attempt an SSL connection and fall back to non-SSL on failure
303+ conn = connect_and_authenticate (secret_dict , port , dbname , use_ssl )
304+ if conn or not fall_back :
305+ return conn
306+ else :
307+ return connect_and_authenticate (secret_dict , port , dbname , False )
308+
309+
310+ def get_ssl_config (secret_dict ):
311+ """Gets the desired SSL and fall back behavior using a secret dictionary
312+
313+ This helper function uses the existance and value the 'ssl' key in a secret dictionary
314+ to determine desired SSL connectivity configuration. Its behavior is as follows:
315+ - 'ssl' key DNE or invalid type/value: return True, True
316+ - 'ssl' key is bool: return secret_dict['ssl'], False
317+ - 'ssl' key equals "true" ignoring case: return True, False
318+ - 'ssl' key equals "false" ignoring case: return False, False
319+
320+ Args:
321+ secret_dict (dict): The Secret Dictionary
322+
323+ Returns:
324+ Tuple(use_ssl, fall_back): SSL configuration
325+ - use_ssl (bool): Flag indicating if an SSL connection should be attempted
326+ - fall_back (bool): Flag indicating if non-SSL connection should be attempted if SSL connection fails
327+
328+ """
329+ # Default to True for SSL and fall_back mode if 'ssl' key DNE
330+ if 'ssl' not in secret_dict :
331+ return True , True
332+
333+ # Handle type bool
334+ if isinstance (secret_dict ['ssl' ], bool ):
335+ return secret_dict ['ssl' ], False
336+
337+ # Handle type string
338+ if isinstance (secret_dict ['ssl' ], str ):
339+ ssl = secret_dict ['ssl' ].lower ()
340+ if ssl == "true" :
341+ return True , False
342+ elif ssl == "false" :
343+ return False , False
300344 else :
301- ssl = (secret_dict ['ssl' ].lower () == "true" )
302-
345+ # Invalid string value, default to True for both SSL and fall_back mode
346+ return True , True
347+
348+ # Invalid type, default to True for both SSL and fall_back mode
349+ return True , True
350+
351+
352+ def connect_and_authenticate (secret_dict , port , dbname , use_ssl ):
353+ """Attempt to connect and authenticate to a MongoDB instance
354+
355+ This helper function tries to connect to the database using connectivity info passed in.
356+ If successful, it returns the connection, else None
357+
358+ Args:
359+ - secret_dict (dict): The Secret Dictionary
360+ - port (int): The databse port to connect to
361+ - dbname (str): Name of the database
362+ - use_ssl (bool): Flag indicating whether connection should use SSL/TLS
363+
364+ Returns:
365+ Connection: The pymongo.database.Database object if successful. None otherwise
366+
367+ Raises:
368+ KeyError: If the secret json does not contain the expected keys
369+
370+ """
303371 # Try to obtain a connection to the db
304372 try :
305- client = MongoClient (host = secret_dict ['host' ], port = port , connectTimeoutMS = 5000 , serverSelectionTimeoutMS = 5000 , ssl = ssl )
373+ # Hostname verfification and server certificate validation enabled by default when ssl=True
374+ client = MongoClient (host = secret_dict ['host' ], port = port , connectTimeoutMS = 5000 , serverSelectionTimeoutMS = 5000 , ssl = use_ssl )
306375 db = client [dbname ]
307376 db .authenticate (secret_dict ['username' ], secret_dict ['password' ])
308377 return db
0 commit comments