Exemplo n.º 1
0
    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")
Exemplo n.º 2
0
    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
Exemplo n.º 4
0
    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)
Exemplo n.º 5
0
    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
Exemplo n.º 6
0
    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.")
Exemplo n.º 7
0
    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
Exemplo n.º 8
0
    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
Exemplo n.º 9
0
    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)
Exemplo n.º 10
0
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.')
Exemplo n.º 11
0
    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)
Exemplo n.º 12
0
    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.")
Exemplo n.º 13
0
    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)
Exemplo n.º 14
0
    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
Exemplo n.º 15
0
    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
Exemplo n.º 16
0
    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
Exemplo n.º 17
0
    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
Exemplo n.º 18
0
    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)
Exemplo n.º 19
0
    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)
Exemplo n.º 20
0
 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)