def test_password_digest(self): self.assertRaises(TypeError, auth._password_digest, 5) self.assertRaises(TypeError, auth._password_digest, True) self.assertRaises(TypeError, auth._password_digest, None) self.assertTrue(isinstance(auth._password_digest("mike", "password"), unicode)) self.assertEqual(auth._password_digest("mike", "password"), u"cd7e45b3b2767dc2fa9b6b548457ed00") self.assertEqual(auth._password_digest("mike", "password"), auth._password_digest(u"mike", u"password")) self.assertEqual(auth._password_digest("Gustave", u"Dor\xe9"), u"81e0e2364499209f466e75926a162d73")
def test_password_digest(self): self.assertRaises(TypeError, auth._password_digest, 5) self.assertRaises(TypeError, auth._password_digest, True) self.assertRaises(TypeError, auth._password_digest, None) self.assertTrue( isinstance(auth._password_digest("mike", "password"), text_type)) self.assertEqual(auth._password_digest("mike", "password"), u"cd7e45b3b2767dc2fa9b6b548457ed00") self.assertEqual(auth._password_digest("mike", "password"), auth._password_digest(u"mike", u"password")) self.assertEqual(auth._password_digest("Gustave", u"Dor\xe9"), u"81e0e2364499209f466e75926a162d73")
def add_user(self, name, password, read_only=False): """Create user `name` with password `password`. Add a new user with permissions for this :class:`Database`. .. note:: Will change the password if user `name` already exists. :Parameters: - `name`: the name of the user to create - `password`: the password of the user to create - `read_only` (optional): if ``True`` it will make user read only .. versionchanged:: 2.2 Added support for read only users .. versionadded:: 1.4 """ user = self.system.users.find_one({"user": name}) or {"user": name} user["pwd"] = auth._password_digest(name, password) user["readOnly"] = common.validate_boolean('read_only', read_only) try: self.system.users.save(user, **self._get_wc_override()) except OperationFailure, e: # First admin user add fails gle in MongoDB >= 2.1.2 # See SERVER-4225 for more information. if 'login' in str(e): pass else: raise
def _create_user(self, name, password, read_only, **kwargs): """Uses v2 commands for creating a new user. """ if read_only or not kwargs.get("roles"): warnings.warn("Creating a user with the read_only option " "or without roles is deprecated in MongoDB " ">= 2.6", DeprecationWarning) create_opts = {} if password is not None: # We always salt and hash client side. if "digestPassword" in kwargs: raise ConfigurationError("The digestPassword option is not " "supported via add_user. Please use " "db.command('createUser', ...) " "instead for this option.") create_opts["pwd"] = auth._password_digest(name, password) create_opts["digestPassword"] = False if "roles" not in kwargs: roles = [] if self.name == "admin": if read_only: roles = ["readAnyDatabase"] else: roles = ["root"] else: if read_only: roles = ["read"] else: roles = ["dbOwner"] create_opts["roles"] = roles create_opts["writeConcern"] = self._get_wc_override() create_opts.update(kwargs) self.command("createUser", name, **create_opts)
def authenticate_scram_sha1(self, database_name, username, password): # Totally stolen from pymongo.auth user = username.replace('=', "=3D").replace(',', "=2C") nonce = base64.standard_b64encode( str(SystemRandom().random()).encode('ascii'))[2:] first_bare = "n={0},r={1}".format(user, nonce.decode()).encode('ascii') cmd = SON([("saslStart", 1), ("mechanism", "SCRAM-SHA-1"), ("autoAuthorize", 1), ("payload", Binary(b"n,," + first_bare))]) result = yield self.__run_command(database_name, cmd) server_first = result["payload"] parsed = auth._parse_scram_response(server_first) iterations = int(parsed[b'i']) salt = parsed[b's'] rnonce = parsed[b'r'] if not rnonce.startswith(nonce): raise MongoAuthenticationError( "TxMongo: server returned an invalid nonce.") without_proof = b"c=biws,r=" + rnonce salted_pass = auth._hi( auth._password_digest(username, password).encode("utf-8"), base64.standard_b64decode(salt), iterations) client_key = hmac.HMAC(salted_pass, b"Client Key", sha1).digest() stored_key = sha1(client_key).digest() auth_msg = b','.join((first_bare, server_first, without_proof)) client_sig = hmac.HMAC(stored_key, auth_msg, sha1).digest() client_proof = b"p=" + base64.standard_b64encode( auth._xor(client_key, client_sig)) client_final = b','.join((without_proof, client_proof)) server_key = hmac.HMAC(salted_pass, b"Server Key", sha1).digest() server_sig = base64.standard_b64encode( hmac.HMAC(server_key, auth_msg, sha1).digest()) cmd = SON([("saslContinue", 1), ("conversationId", result["conversationId"]), ("payload", Binary(client_final))]) result = yield self.__run_command(database_name, cmd) if not result["ok"]: raise MongoAuthenticationError("TxMongo: authentication failed.") parsed = auth._parse_scram_response(result["payload"]) if parsed[b'v'] != server_sig: raise MongoAuthenticationError( "TxMongo: server returned an invalid signature.") # Depending on how it's configured, Cyrus SASL (which the server uses) # requires a third empty challenge. if not result["done"]: cmd = SON([("saslContinue", 1), ("conversationId", result["conversationId"]), ("payload", Binary(b''))]) result = yield self.__run_command(database_name, cmd) if not result["done"]: raise MongoAuthenticationError( "TxMongo: SASL conversation failed to complete.")
def _legacy_add_user(self, name, password, read_only, **kwargs): """Uses v1 system to add users, i.e. saving to system.users. """ # Use a Collection with the default codec_options. system_users = self._collection_default_options('system.users') user = system_users.find_one({"user": name}) or {"user": name} if password is not None: user["pwd"] = auth._password_digest(name, password) if read_only is not None: user["readOnly"] = read_only user.update(kwargs) # We don't care what the _id is, only that it has one # for the replace_one call below. user.setdefault("_id", ObjectId()) try: system_users.replace_one({"_id": user["_id"]}, user, True) except OperationFailure as exc: # First admin user add fails gle in MongoDB >= 2.1.2 # See SERVER-4225 for more information. if 'login' in str(exc): pass # First admin user add fails gle from mongos 2.0.x # and 2.2.x. elif (exc.details and 'getlasterror' in exc.details.get('note', '')): pass else: raise
def _create_or_update_user( self, create, name, password, read_only, **kwargs): """Use a command to create (if create=True) or modify a user. """ opts = {} if read_only or (create and "roles" not in kwargs): warnings.warn("Creating a user with the read_only option " "or without roles is deprecated in MongoDB " ">= 2.6", DeprecationWarning) opts["roles"] = [self._default_role(read_only)] elif read_only: warnings.warn("The read_only option is deprecated in MongoDB " ">= 2.6, use 'roles' instead", DeprecationWarning) if password is not None: # We always salt and hash client side. if "digestPassword" in kwargs: raise ConfigurationError("The digestPassword option is not " "supported via add_user. Please use " "db.command('createUser', ...) " "instead for this option.") opts["pwd"] = auth._password_digest(name, password) opts["digestPassword"] = False opts["writeConcern"] = self._get_wc_override() opts.update(kwargs) if create: command_name = "createUser" else: command_name = "updateUser" self.command(command_name, name, **opts)
async def _authenticate_scram_sha1(credentials: MongoCredential, connection: 'aiomongo.Connection') -> None: """Authenticate using SCRAM-SHA-1.""" username = credentials.username password = credentials.password source = credentials.source # Make local _hmac = hmac.HMAC _sha1 = sha1 user = username.encode('utf-8').replace(b'=', b'=3D').replace(b',', b'=2C') nonce = standard_b64encode( (('{}'.format(SystemRandom().random(), ))[2:]).encode('utf-8')) first_bare = b'n=' + user + b',r=' + nonce cmd = SON([('saslStart', 1), ('mechanism', 'SCRAM-SHA-1'), ('payload', Binary(b'n,,' + first_bare)), ('autoAuthorize', 1)]) res = await connection.command(source, cmd) server_first = res['payload'] parsed = _parse_scram_response(server_first) iterations = int(parsed[b'i']) salt = parsed[b's'] rnonce = parsed[b'r'] if not rnonce.startswith(nonce): raise OperationFailure('Server returned an invalid nonce.') without_proof = b'c=biws,r=' + rnonce salted_pass = _hi( _password_digest(username, password).encode('utf-8'), standard_b64decode(salt), iterations) client_key = _hmac(salted_pass, b'Client Key', _sha1).digest() stored_key = _sha1(client_key).digest() auth_msg = b','.join((first_bare, server_first, without_proof)) client_sig = _hmac(stored_key, auth_msg, _sha1).digest() client_proof = b'p=' + standard_b64encode(_xor(client_key, client_sig)) client_final = b','.join((without_proof, client_proof)) server_key = _hmac(salted_pass, b'Server Key', _sha1).digest() server_sig = standard_b64encode( _hmac(server_key, auth_msg, _sha1).digest()) cmd = SON([('saslContinue', 1), ('conversationId', res['conversationId']), ('payload', Binary(client_final))]) res = await connection.command(source, cmd) parsed = _parse_scram_response(res['payload']) if not compare_digest(parsed[b'v'], server_sig): raise OperationFailure('Server returned an invalid signature.') # Depending on how it's configured, Cyrus SASL (which the server uses) # requires a third empty challenge. if not res['done']: cmd = SON([('saslContinue', 1), ('conversationId', res['conversationId']), ('payload', Binary(b''))]) res = await connection.command(source, cmd) if not res['done']: raise OperationFailure('SASL conversation failed to complete.')
def salt_password(self, user, password): if self.method_str == 'sha1': password = _password_digest(user, password).encode("utf-8") elif self.method_str == 'sha256': password = saslprep(password).encode("utf-8") return hashlib.pbkdf2_hmac(self.method_str, password, base64.b64decode(self.salt), self.iterations)
def authenticate_scram_sha1(self, database_name, username, password): # Totally stolen from pymongo.auth user = username.encode("utf-8").replace('=', "=3D").replace(',', "=2C") nonce = base64.standard_b64encode(str(SystemRandom().random())[2:].encode("utf-8")) first_bare = "n={0},r={1}".format(user, nonce) cmd = SON([("saslStart", 1), ("mechanism", "SCRAM-SHA-1"), ("autoAuthorize", 1), ("payload", Binary("n,," + first_bare))]) result = yield self.__run_command(database_name, cmd) server_first = result["payload"] parsed = auth._parse_scram_response(server_first) iterations = int(parsed['i']) salt = parsed['s'] rnonce = parsed['r'] if not rnonce.startswith(nonce): raise MongoAuthenticationError("Server returned an invalid nonce.") without_proof = "c=biws,r=" + rnonce salted_pass = auth._hi(auth._password_digest(username, password).encode("utf-8"), base64.standard_b64decode(salt), iterations) client_key = hmac.HMAC(salted_pass, "Client Key", sha1).digest() stored_key = sha1(client_key).digest() auth_msg = ','.join((first_bare, server_first, without_proof)) client_sig = hmac.HMAC(stored_key, auth_msg, sha1).digest() client_proof = "p=" + base64.standard_b64encode(auth._xor(client_key, client_sig)) client_final = ','.join((without_proof, client_proof)) server_key = hmac.HMAC(salted_pass, "Server Key", sha1).digest() server_sig = base64.standard_b64encode( hmac.HMAC(server_key, auth_msg, sha1).digest()) cmd = SON([("saslContinue", 1), ("conversationId", result["conversationId"]), ("payload", Binary(client_final))]) result = yield self.__run_command(database_name, cmd) if not result["ok"]: raise MongoAuthenticationError("Authentication failed") parsed = auth._parse_scram_response(result["payload"]) if parsed['v'] != server_sig: raise MongoAuthenticationError("Server returned an invalid signature.") # Depending on how it's configured, Cyrus SASL (which the server uses) # requires a third empty challenge. if not result["done"]: cmd = SON([("saslContinue", 1), ("conversationId", result["conversationId"]), ("payload", Binary(''))]) result = yield self.__run_command(database_name, cmd) if not result["done"]: raise MongoAuthenticationError("SASL conversation failed to complete.")
def _update_user(self, name, password, **kwargs): """Uses v2 commands for updating a user. """ update_opts = {} if password is not None: # We always salt and hash client side. if "digestPassword" in kwargs: raise ConfigurationError("The digestPassword option is not " "supported via add_user. Please use " "db.command('updateUser', ...) " "instead for this option.") update_opts["pwd"] = auth._password_digest(name, password) update_opts["digestPassword"] = False update_opts["writeConcern"] = self._get_wc_override() update_opts.update(kwargs) self.command("updateUser", name, **update_opts)
def add_user(self, name, password=None, read_only=None, **kwargs): """Create user `name` with password `password`. Add a new user with permissions for this :class:`Database`. .. note:: Will change the password if user `name` already exists. :Parameters: - `name`: the name of the user to create - `password` (optional): the password of the user to create. Can not be used with the ``userSource`` argument. - `read_only` (optional): if ``True`` the user will be read only - `**kwargs` (optional): optional fields for the user document (e.g. ``userSource``, ``otherDBRoles``, or ``roles``). See `<http://docs.mongodb.org/manual/reference/privilege-documents>`_ for more information. .. note:: The use of optional keyword arguments like ``userSource``, ``otherDBRoles``, or ``roles`` requires MongoDB >= 2.4.0 .. versionchanged:: 2.5 Added kwargs support for optional fields introduced in MongoDB 2.4 .. versionchanged:: 2.2 Added support for read only users .. versionadded:: 1.4 """ user = self.system.users.find_one({"user": name}) or {"user": name} if password is not None: user["pwd"] = auth._password_digest(name, password) if read_only is not None: user["readOnly"] = common.validate_boolean('read_only', read_only) user.update(kwargs) try: self.system.users.save(user, **self._get_wc_override()) except OperationFailure, e: # First admin user add fails gle in MongoDB >= 2.1.2 # See SERVER-4225 for more information. if 'login' in str(e): pass else: raise
def _legacy_add_user(self, name, password, read_only, **kwargs): """Uses v1 system to add users, i.e. saving to system.users. """ user = self.system.users.find_one({"user": name}) or {"user": name} if password is not None: user["pwd"] = auth._password_digest(name, password) if read_only is not None: user["readOnly"] = read_only user.update(kwargs) try: self.system.users.save(user, **self._get_wc_override()) except OperationFailure, exc: # First admin user add fails gle in MongoDB >= 2.1.2 # See SERVER-4225 for more information. if 'login' in str(exc): pass else: raise
def _create_or_update_user(self, create, name, password, read_only, **kwargs): """Use a command to create (if create=True) or modify a user. """ opts = {} if read_only or (create and "roles" not in kwargs): warnings.warn( "Creating a user with the read_only option " "or without roles is deprecated in MongoDB " ">= 2.6", DeprecationWarning) opts["roles"] = [self._default_role(read_only)] elif read_only: warnings.warn( "The read_only option is deprecated in MongoDB " ">= 2.6, use 'roles' instead", DeprecationWarning) if password is not None: # We always salt and hash client side. if "digestPassword" in kwargs: raise ConfigurationError("The digestPassword option is not " "supported via add_user. Please use " "db.command('createUser', ...) " "instead for this option.") opts["pwd"] = auth._password_digest(name, password) opts["digestPassword"] = False write_concern = self._get_wc_override() or self.write_concern if write_concern: opts["writeConcern"] = write_concern opts.update(kwargs) if create: command_name = "createUser" else: command_name = "updateUser" self.command(command_name, name, read_preference=ReadPreference.PRIMARY, **opts)
async def _create_or_update_user(self, create: bool, name: str, password: str, read_only: bool, **kwargs) -> None: """Use a command to create (if create=True) or modify a user. """ opts = {} if read_only or (create and 'roles' not in kwargs): warnings.warn( 'Creating a user with the read_only option ' 'or without roles is deprecated in MongoDB ' '>= 2.6', DeprecationWarning) opts['roles'] = [self._default_role(read_only)] elif read_only: warnings.warn( 'The read_only option is deprecated in MongoDB ' '>= 2.6, use "roles" instead', DeprecationWarning) if password is not None: # We always salt and hash client side. if 'digestPassword' in kwargs: raise ConfigurationError('The digestPassword option is not ' 'supported via add_user. Please use ' 'db.command("createUser", ...) ' 'instead for this option.') opts['pwd'] = auth._password_digest(name, password) opts['digestPassword'] = False # Don't send {} as writeConcern. if self.write_concern.acknowledged and self.write_concern.document: opts['writeConcern'] = self.write_concern.document opts.update(kwargs) if create: command_name = 'createUser' else: command_name = 'updateUser' await self.command(command_name, value=name, **opts)
def _salt_password(self): password = _password_digest(self.user, self.password).encode("utf-8") return hashlib.pbkdf2_hmac('sha1', password, base64.b64decode(self.salt), self.iterations)