def test_auth_network_error(self):
        # Make sure there's no semaphore leak if we get a network error
        # when authenticating a new socket with cached credentials.
        auth_client = self._get_client()
        if not server_started_with_auth(auth_client):
            raise SkipTest('Authentication is not enabled on server')

        auth_client.admin.add_user('admin', 'password')
        auth_client.admin.authenticate('admin', 'password')
        try:
            # Get a client with one socket so we detect if it's leaked.
            c = self._get_client(max_pool_size=1, waitQueueTimeoutMS=1)

            # Simulate an authenticate() call on a different socket.
            credentials = auth._build_credentials_tuple(
                'MONGODB-CR', 'admin',
                unicode('admin'), unicode('password'),
                {})

            c._cache_credentials('test', credentials, connect=False)

            # Cause a network error on the actual socket.
            pool = get_pool(c)
            socket_info = one(pool.sockets)
            socket_info.sock.close()

            # In __check_auth, the client authenticates its socket with the
            # new credential, but gets a socket.error. Should be reraised as
            # AutoReconnect.
            self.assertRaises(AutoReconnect, c.test.collection.find_one)

            # No semaphore leak, the pool is allowed to make a new socket.
            c.test.collection.find_one()
        finally:
            remove_all_users(auth_client.admin)
    def test_auth_network_error(self):
        # Make sure there's no semaphore leak if we get a network error
        # when authenticating a new socket with cached credentials.

        # Get a client with one socket so we detect if it's leaked.
        c = connected(rs_or_single_client(maxPoolSize=1,
                                          waitQueueTimeoutMS=1))

        # Simulate an authenticate() call on a different socket.
        credentials = auth._build_credentials_tuple(
            'DEFAULT', 'admin', db_user, db_pwd, {})

        c._cache_credentials('test', credentials, connect=False)

        # Cause a network error on the actual socket.
        pool = get_pool(c)
        socket_info = one(pool.sockets)
        socket_info.sock.close()

        # SocketInfo.check_auth logs in with the new credential, but gets a
        # socket.error. Should be reraised as AutoReconnect.
        self.assertRaises(AutoReconnect, c.test.collection.find_one)

        # No semaphore leak, the pool is allowed to make a new socket.
        c.test.collection.find_one()
def _parse_credentials(username, password, database, options):
    """Parse authentication credentials."""
    if username is None:
        return None
    mechanism = options.get("authmechanism", "DEFAULT")
    source = options.get("authsource", database or "admin")
    return _build_credentials_tuple(mechanism, source, username, password, options)
    def test_auth_network_error(self):
        # Make sure there's no semaphore leak if we get a network error
        # when authenticating a new socket with cached credentials.
        # Get a client with one socket so we detect if it's leaked.

        # Generous wait queue timeout in case the main thread contends
        # with the monitor, though -- a semaphore leak will be detected
        # eventually, even with a long timeout.
        c = self._get_client(max_pool_size=1, waitQueueTimeoutMS=10000)

        # Simulate an authenticate() call on a different socket.
        credentials = auth._build_credentials_tuple(
            'DEFAULT', 'admin',
            unicode(db_user), unicode(db_pwd),
            {})

        c._cache_credentials('test', credentials, connect=False)

        # Cause a network error on the actual socket.
        pool = get_pool(c)
        socket_info = one(pool.sockets)
        socket_info.sock.close()

        # In __check_auth, the client authenticates its socket with the
        # new credential, but gets a socket.error. Reraised as AutoReconnect,
        # unless periodic monitoring or Pool._check prevent the error.
        try:
            c.test.collection.find_one()
        except AutoReconnect:
            pass

        # No semaphore leak, the pool is allowed to make a new socket.
        c.test.collection.find_one()
Esempio n. 5
0
def _parse_credentials(username, password, database, options):
    """Parse authentication credentials."""
    if username is None:
        return None
    mechanism = options.get('authmechanism', 'DEFAULT')
    source = options.get('authsource', database or 'admin')
    return _build_credentials_tuple(mechanism, source, username, password,
                                    options)
def _parse_credentials(username, password, database, options):
    """Parse authentication credentials."""
    mechanism = options.get('authmechanism', 'DEFAULT')
    if username is None and mechanism != 'MONGODB-X509':
        return None
    source = options.get('authsource', database or 'admin')
    return _build_credentials_tuple(
        mechanism, source, username, password, options)
Esempio n. 7
0
def _parse_credentials(username, password, database, options):
    """Parse authentication credentials."""
    mechanism = options.get('authmechanism', 'DEFAULT' if username else None)
    source = options.get('authsource')
    if username or mechanism:
        return _build_credentials_tuple(mechanism, source, username, password,
                                        options, database)
    return None
def _parse_credentials(username, password, database, options):
    """Parse authentication credentials."""
    mechanism = options.get('authmechanism', 'DEFAULT' if username else None)
    source = options.get('authsource')
    if username or mechanism:
        return _build_credentials_tuple(
            mechanism, source, username, password, options, database)
    return None
Esempio n. 9
0
    def test_credentials_hashing(self):
        # GSSAPI credentials are properly hashed.
        creds0 = _build_credentials_tuple(
            'GSSAPI', '', 'user', 'pass', {})

        creds1 = _build_credentials_tuple(
            'GSSAPI', '', 'user', 'pass',
            {'authmechanismproperties': {'SERVICE_NAME': 'A'}})

        creds2 = _build_credentials_tuple(
            'GSSAPI', '', 'user', 'pass',
            {'authmechanismproperties': {'SERVICE_NAME': 'A'}})

        creds3 = _build_credentials_tuple(
            'GSSAPI', '', 'user', 'pass',
            {'authmechanismproperties': {'SERVICE_NAME': 'B'}})

        self.assertEqual(1, len(set([creds1, creds2])))
        self.assertEqual(3, len(set([creds0, creds1, creds2, creds3])))
Esempio n. 10
0
    def test_credentials_hashing(self):
        # GSSAPI credentials are properly hashed.
        creds0 = _build_credentials_tuple(
            'GSSAPI', '', 'user', 'pass', {})

        creds1 = _build_credentials_tuple(
            'GSSAPI', '', 'user', 'pass',
            {'authmechanismproperties': {'SERVICE_NAME': 'A'}})

        creds2 = _build_credentials_tuple(
            'GSSAPI', '', 'user', 'pass',
            {'authmechanismproperties': {'SERVICE_NAME': 'A'}})

        creds3 = _build_credentials_tuple(
            'GSSAPI', '', 'user', 'pass',
            {'authmechanismproperties': {'SERVICE_NAME': 'B'}})

        self.assertEqual(1, len(set([creds1, creds2])))
        self.assertEqual(3, len(set([creds0, creds1, creds2, creds3])))
Esempio n. 11
0
    def test_auth_network_error(self):
        # Make sure there's no semaphore leak if we get a network error
        # when authenticating a new socket with cached credentials.

        # Get a client with one socket so we detect if it's leaked.
        c = connected(rs_or_single_client(maxPoolSize=1, waitQueueTimeoutMS=1))

        # Simulate an authenticate() call on a different socket.
        credentials = auth._build_credentials_tuple('DEFAULT', 'admin',
                                                    db_user, db_pwd, {})

        c._cache_credentials('test', credentials, connect=False)

        # Cause a network error on the actual socket.
        pool = get_pool(c)
        socket_info = one(pool.sockets)
        socket_info.sock.close()

        # SocketInfo.check_auth logs in with the new credential, but gets a
        # socket.error. Should be reraised as AutoReconnect.
        self.assertRaises(AutoReconnect, c.test.collection.find_one)

        # No semaphore leak, the pool is allowed to make a new socket.
        c.test.collection.find_one()
Esempio n. 12
0
    def authenticate(self, name=None, password=None,
                     source=None, mechanism='DEFAULT', **kwargs):
        """**DEPRECATED**: Authenticate to use this database.

        .. warning:: Starting in MongoDB 3.6, calling :meth:`authenticate`
          invalidates all existing cursors. It may also leave logical sessions
          open on the server for up to 30 minutes until they time out.

        Authentication lasts for the life of the underlying client
        instance, or until :meth:`logout` is called.

        Raises :class:`TypeError` if (required) `name`, (optional) `password`,
        or (optional) `source` is not an instance of :class:`basestring`
        (:class:`str` in python 3).

        .. note::
          - This method authenticates the current connection, and
            will also cause all new :class:`~socket.socket` connections
            in the underlying client instance to be authenticated automatically.

          - Authenticating more than once on the same database with different
            credentials is not supported. You must call :meth:`logout` before
            authenticating with new credentials.

          - When sharing a client instance between multiple threads, all
            threads will share the authentication. If you need different
            authentication profiles for different purposes you must use
            distinct client instances.

        :Parameters:
          - `name`: the name of the user to authenticate. Optional when
            `mechanism` is MONGODB-X509 and the MongoDB server version is
            >= 3.4.
          - `password` (optional): the password of the user to authenticate.
            Not used with GSSAPI or MONGODB-X509 authentication.
          - `source` (optional): the database to authenticate on. If not
            specified the current database is used.
          - `mechanism` (optional): See :data:`~pymongo.auth.MECHANISMS` for
            options. If no mechanism is specified, PyMongo automatically uses
            MONGODB-CR when connected to a pre-3.0 version of MongoDB,
            SCRAM-SHA-1 when connected to MongoDB 3.0 through 3.6, and
            negotiates the mechanism to use (SCRAM-SHA-1 or SCRAM-SHA-256) when
            connected to MongoDB 4.0+.
          - `authMechanismProperties` (optional): Used to specify
            authentication mechanism specific options. To specify the service
            name for GSSAPI authentication pass
            ``authMechanismProperties='SERVICE_NAME:<service name>'``.
            To specify the session token for MONGODB-AWS authentication pass
            ``authMechanismProperties='AWS_SESSION_TOKEN:<session token>'``.

        .. versionchanged:: 3.7
           Added support for SCRAM-SHA-256 with MongoDB 4.0 and later.

        .. versionchanged:: 3.5
           Deprecated. Authenticating multiple users conflicts with support for
           logical sessions in MongoDB 3.6. To authenticate as multiple users,
           create multiple instances of MongoClient.

        .. versionadded:: 2.8
           Use SCRAM-SHA-1 with MongoDB 3.0 and later.

        .. versionchanged:: 2.5
           Added the `source` and `mechanism` parameters. :meth:`authenticate`
           now raises a subclass of :class:`~pymongo.errors.PyMongoError` if
           authentication fails due to invalid credentials or configuration
           issues.

        .. mongodoc:: authenticate
        """
        if name is not None and not isinstance(name, str):
            raise TypeError("name must be an instance of str")
        if password is not None and not isinstance(password, str):
            raise TypeError("password must be an instance of str")
        if source is not None and not isinstance(source, str):
            raise TypeError("source must be an instance of str")
        common.validate_auth_mechanism('mechanism', mechanism)

        validated_options = common._CaseInsensitiveDictionary()
        for option, value in kwargs.items():
            normalized, val = common.validate_auth_option(option, value)
            validated_options[normalized] = val

        credentials = auth._build_credentials_tuple(
            mechanism,
            source,
            name,
            password,
            validated_options,
            self.name)

        self.client._cache_credentials(
            self.name,
            credentials,
            connect=True)

        return True
Esempio n. 13
0
    def authenticate(self,
                     name=None,
                     password=None,
                     source=None,
                     mechanism='DEFAULT',
                     **kwargs):
        """Authenticate to use this database.

        Authentication lasts for the life of the underlying client
        instance, or until :meth:`logout` is called.

        Raises :class:`TypeError` if (required) `name`, (optional) `password`,
        or (optional) `source` is not an instance of :class:`basestring`
        (:class:`str` in python 3).

        .. note::
          - This method authenticates the current connection, and
            will also cause all new :class:`~socket.socket` connections
            in the underlying client instance to be authenticated automatically.

          - Authenticating more than once on the same database with different
            credentials is not supported. You must call :meth:`logout` before
            authenticating with new credentials.

          - When sharing a client instance between multiple threads, all
            threads will share the authentication. If you need different
            authentication profiles for different purposes you must use
            distinct client instances.

        :Parameters:
          - `name`: the name of the user to authenticate. Optional when
            `mechanism` is MONGODB-X509 and the MongoDB server version is
            >= 3.4.
          - `password` (optional): the password of the user to authenticate.
            Not used with GSSAPI or MONGODB-X509 authentication.
          - `source` (optional): the database to authenticate on. If not
            specified the current database is used.
          - `mechanism` (optional): See
            :data:`~pymongo.auth.MECHANISMS` for options.
            By default, use SCRAM-SHA-1 with MongoDB 3.0 and later,
            MONGODB-CR (MongoDB Challenge Response protocol) for older servers.
          - `authMechanismProperties` (optional): Used to specify
            authentication mechanism specific options. To specify the service
            name for GSSAPI authentication pass
            authMechanismProperties='SERVICE_NAME:<service name>'

        .. versionadded:: 2.8
           Use SCRAM-SHA-1 with MongoDB 3.0 and later.

        .. versionchanged:: 2.5
           Added the `source` and `mechanism` parameters. :meth:`authenticate`
           now raises a subclass of :class:`~pymongo.errors.PyMongoError` if
           authentication fails due to invalid credentials or configuration
           issues.

        .. mongodoc:: authenticate
        """
        if name is not None and not isinstance(name, string_type):
            raise TypeError("name must be an "
                            "instance of %s" % (string_type.__name__, ))
        if password is not None and not isinstance(password, string_type):
            raise TypeError("password must be an "
                            "instance of %s" % (string_type.__name__, ))
        if source is not None and not isinstance(source, string_type):
            raise TypeError("source must be an "
                            "instance of %s" % (string_type.__name__, ))
        common.validate_auth_mechanism('mechanism', mechanism)

        validated_options = {}
        for option, value in iteritems(kwargs):
            normalized, val = common.validate_auth_option(option, value)
            validated_options[normalized] = val

        credentials = auth._build_credentials_tuple(mechanism, source
                                                    or self.name, name,
                                                    password,
                                                    validated_options)

        self.client._cache_credentials(self.name, credentials, connect=True)

        return True
Esempio n. 14
0
    def authenticate(self, name, password=None,
                     source=None, mechanism='MONGODB-CR', **kwargs):
        """Authenticate to use this database.

        Authentication lasts for the life of the underlying client
        instance, or until :meth:`logout` is called.

        Raises :class:`TypeError` if (required) `name`, (optional) `password`,
        or (optional) `source` is not an instance of :class:`basestring`
        (:class:`str` in python 3).

        .. note::
          - This method authenticates the current connection, and
            will also cause all new :class:`~socket.socket` connections
            in the underlying client instance to be authenticated automatically.

          - Authenticating more than once on the same database with different
            credentials is not supported. You must call :meth:`logout` before
            authenticating with new credentials.

          - When sharing a client instance between multiple threads, all
            threads will share the authentication. If you need different
            authentication profiles for different purposes you must use
            distinct client instances.

          - To get authentication to apply immediately to all
            existing sockets you may need to reset this client instance's
            sockets using :meth:`~pymongo.mongo_client.MongoClient.disconnect`.

        :Parameters:
          - `name`: the name of the user to authenticate.
          - `password` (optional): the password of the user to authenticate.
            Not used with GSSAPI or MONGODB-X509 authentication.
          - `source` (optional): the database to authenticate on. If not
            specified the current database is used.
          - `mechanism` (optional): See
            :data:`~pymongo.auth.MECHANISMS` for options.
            Defaults to MONGODB-CR (MongoDB Challenge Response protocol)
          - `gssapiServiceName` (optional): Used with the GSSAPI mechanism
            to specify the service name portion of the service principal name.
            Defaults to 'mongodb'.

        .. versionchanged:: 2.5
           Added the `source` and `mechanism` parameters. :meth:`authenticate`
           now raises a subclass of :class:`~pymongo.errors.PyMongoError` if
           authentication fails due to invalid credentials or configuration
           issues.

        .. mongodoc:: authenticate
        """
        if not isinstance(name, str):
            raise TypeError("name must be an instance "
                            "of %s" % (str.__name__,))
        if password is not None and not isinstance(password, str):
            raise TypeError("password must be an instance "
                            "of %s" % (str.__name__,))
        if source is not None and not isinstance(source, str):
            raise TypeError("source must be an instance "
                            "of %s" % (str.__name__,))
        common.validate_auth_mechanism('mechanism', mechanism)

        validated_options = {}
        for option, value in kwargs.items():
            normalized, val = common.validate_auth_option(option, value)
            validated_options[normalized] = val

        credentials = auth._build_credentials_tuple(mechanism,
                                source or self.name, str(name),
                                password and str(password) or None,
                                validated_options)
        self.connection._cache_credentials(self.name, credentials)
        return True
Esempio n. 15
0
def _copy_database(fromdb, todb, fromhost, mechanism, username, password,
                   sock_info, cmd_func):
    """Copy a database, perhaps from a remote host.

    :Parameters:
      - `fromdb`: Source database.
      - `todb`: Target database.
      - `fromhost`: Source host like 'foo.com', 'foo.com:27017', or None.
      - `mechanism`: An authentication mechanism.
      - `username`: A str or unicode, or None.
      - `password`: A str or unicode, or None.
      - `sock_info`: A SocketInfo instance.
      - `cmd_func`: A callback taking args sock_info, database, command doc.
    """
    if not isinstance(fromdb, str):
        raise TypeError('from_name must be an instance '
                        'of %s' % (str.__name__, ))
    if not isinstance(todb, str):
        raise TypeError('to_name must be an instance '
                        'of %s' % (str.__name__, ))

    _check_database_name(todb)

    warnings.warn(
        "copy_database is deprecated. Use the raw 'copydb' command"
        " or db.copyDatabase() in the mongo shell. See"
        " doc/examples/copydb.",
        DeprecationWarning,
        stacklevel=2)

    # It would be better if the user told us what mechanism to use, but for
    # backwards compatibility with earlier PyMongos we don't require the
    # mechanism. Hope 'fromhost' runs the same version as the target.
    if mechanism == 'DEFAULT':
        if sock_info.max_wire_version >= 3:
            mechanism = 'SCRAM-SHA-1'
        else:
            mechanism = 'MONGODB-CR'

    if username is not None:
        if mechanism == 'SCRAM-SHA-1':
            credentials = auth._build_credentials_tuple(mech=mechanism,
                                                        source='admin',
                                                        user=username,
                                                        passwd=password,
                                                        extra=None)

            try:
                auth._copydb_scram_sha1(credentials=credentials,
                                        sock_info=sock_info,
                                        cmd_func=cmd_func,
                                        fromdb=fromdb,
                                        todb=todb,
                                        fromhost=fromhost)
            except OperationFailure as exc:
                errmsg = exc.details and exc.details.get('errmsg') or ''
                if 'no such cmd: saslStart' in errmsg:
                    explanation = ("%s doesn't support SCRAM-SHA-1, pass"
                                   " mechanism='MONGODB-CR' to copy_database" %
                                   fromhost)

                    raise OperationFailure(explanation, exc.code, exc.details)
                else:
                    raise

        elif mechanism == 'MONGODB-CR':
            get_nonce_cmd = SON([('copydbgetnonce', 1),
                                 ('fromhost', fromhost)])

            get_nonce_response, _ = cmd_func(sock_info, 'admin', get_nonce_cmd)
            nonce = get_nonce_response['nonce']
            copydb_cmd = SON([('copydb', 1), ('fromdb', fromdb),
                              ('todb', todb)])

            copydb_cmd['username'] = username
            copydb_cmd['nonce'] = nonce
            copydb_cmd['key'] = auth._auth_key(nonce, username, password)
            if fromhost is not None:
                copydb_cmd['fromhost'] = fromhost

            cmd_func(sock_info, 'admin', copydb_cmd)
        else:
            raise InvalidOperation('Authentication mechanism %r not supported'
                                   ' for copy_database' % mechanism)
    else:
        # No username.
        copydb_cmd = SON([('copydb', 1), ('fromdb', fromdb), ('todb', todb)])

        if fromhost:
            copydb_cmd['fromhost'] = fromhost

        cmd_func(sock_info, 'admin', copydb_cmd)
Esempio n. 16
0
    def authenticate(self, name, password=None,
                     source=None, mechanism='DEFAULT', **kwargs):
        """Authenticate to use this database.

        Authentication lasts for the life of the underlying client
        instance, or until :meth:`logout` is called.

        Raises :class:`TypeError` if (required) `name`, (optional) `password`,
        or (optional) `source` is not an instance of :class:`basestring`
        (:class:`str` in python 3).

        .. note::
          - This method authenticates the current connection, and
            will also cause all new :class:`~socket.socket` connections
            in the underlying client instance to be authenticated automatically.

          - Authenticating more than once on the same database with different
            credentials is not supported. You must call :meth:`logout` before
            authenticating with new credentials.

          - When sharing a client instance between multiple threads, all
            threads will share the authentication. If you need different
            authentication profiles for different purposes you must use
            distinct client instances.

        :Parameters:
          - `name`: the name of the user to authenticate.
          - `password` (optional): the password of the user to authenticate.
            Not used with GSSAPI or MONGODB-X509 authentication.
          - `source` (optional): the database to authenticate on. If not
            specified the current database is used.
          - `mechanism` (optional): See
            :data:`~pymongo.auth.MECHANISMS` for options.
            By default, use SCRAM-SHA-1 with MongoDB 3.0 and later,
            MONGODB-CR (MongoDB Challenge Response protocol) for older servers.
          - `authMechanismProperties` (optional): Used to specify
            authentication mechanism specific options. To specify the service
            name for GSSAPI authentication pass
            authMechanismProperties='SERVICE_NAME:<service name>'

        .. versionadded:: 2.8
           Use SCRAM-SHA-1 with MongoDB 3.0 and later.

        .. versionchanged:: 2.5
           Added the `source` and `mechanism` parameters. :meth:`authenticate`
           now raises a subclass of :class:`~pymongo.errors.PyMongoError` if
           authentication fails due to invalid credentials or configuration
           issues.

        .. mongodoc:: authenticate
        """
        if not isinstance(name, string_type):
            raise TypeError("name must be an "
                            "instance of %s" % (string_type.__name__,))
        if password is not None and not isinstance(password, string_type):
            raise TypeError("password must be an "
                            "instance of %s" % (string_type.__name__,))
        if source is not None and not isinstance(source, string_type):
            raise TypeError("source must be an "
                            "instance of %s" % (string_type.__name__,))
        common.validate_auth_mechanism('mechanism', mechanism)

        validated_options = {}
        for option, value in iteritems(kwargs):
            normalized, val = common.validate_auth_option(option, value)
            validated_options[normalized] = val

        credentials = auth._build_credentials_tuple(
            mechanism,
            source or self.name,
            name,
            password,
            validated_options)

        self.client._cache_credentials(
            self.name,
            credentials,
            connect=True)

        return True
Esempio n. 17
0
def _copy_database(
        fromdb,
        todb,
        fromhost,
        mechanism,
        username,
        password,
        sock_info,
        cmd_func):
    """Copy a database, perhaps from a remote host.

    :Parameters:
      - `fromdb`: Source database.
      - `todb`: Target database.
      - `fromhost`: Source host like 'foo.com', 'foo.com:27017', or None.
      - `mechanism`: An authentication mechanism.
      - `username`: A str or unicode, or None.
      - `password`: A str or unicode, or None.
      - `sock_info`: A SocketInfo instance.
      - `cmd_func`: A callback taking args sock_info, database, command doc.
    """
    if not isinstance(fromdb, basestring):
        raise TypeError('from_name must be an instance '
                        'of %s' % (basestring.__name__,))
    if not isinstance(todb, basestring):
        raise TypeError('to_name must be an instance '
                        'of %s' % (basestring.__name__,))

    _check_database_name(todb)

    warnings.warn("copy_database is deprecated. Use the raw 'copydb' command"
                  " or db.copyDatabase() in the mongo shell. See"
                  " doc/examples/copydb.",
                  DeprecationWarning, stacklevel=2)

    # It would be better if the user told us what mechanism to use, but for
    # backwards compatibility with earlier PyMongos we don't require the
    # mechanism. Hope 'fromhost' runs the same version as the target.
    if mechanism == 'DEFAULT':
        if sock_info.max_wire_version >= 3:
            mechanism = 'SCRAM-SHA-1'
        else:
            mechanism = 'MONGODB-CR'

    if username is not None:
        if mechanism == 'SCRAM-SHA-1':
            credentials = auth._build_credentials_tuple(mech=mechanism,
                                                        source='admin',
                                                        user=username,
                                                        passwd=password,
                                                        extra=None)

            try:
                auth._copydb_scram_sha1(credentials=credentials,
                                        sock_info=sock_info,
                                        cmd_func=cmd_func,
                                        fromdb=fromdb,
                                        todb=todb,
                                        fromhost=fromhost)
            except OperationFailure, exc:
                errmsg = exc.details and exc.details.get('errmsg') or ''
                if 'no such cmd: saslStart' in errmsg:
                    explanation = (
                        "%s doesn't support SCRAM-SHA-1, pass"
                        " mechanism='MONGODB-CR' to copy_database" % fromhost)

                    raise OperationFailure(explanation,
                                           exc.code,
                                           exc.details)
                else:
                    raise

        elif mechanism == 'MONGODB-CR':
            get_nonce_cmd = SON([('copydbgetnonce', 1),
                                 ('fromhost', fromhost)])

            get_nonce_response, _ = cmd_func(sock_info, 'admin', get_nonce_cmd)
            nonce = get_nonce_response['nonce']
            copydb_cmd = SON([('copydb', 1),
                              ('fromdb', fromdb),
                              ('todb', todb)])

            copydb_cmd['username'] = username
            copydb_cmd['nonce'] = nonce
            copydb_cmd['key'] = auth._auth_key(nonce, username, password)
            if fromhost is not None:
                copydb_cmd['fromhost'] = fromhost

            cmd_func(sock_info, 'admin', copydb_cmd)
        else:
            raise InvalidOperation('Authentication mechanism %r not supported'
                                   ' for copy_database' % mechanism)
Esempio n. 18
0
    def authenticate(self,
                     name,
                     password=None,
                     source=None,
                     mechanism='MONGODB-CR',
                     **kwargs):
        """Authenticate to use this database.

        Authentication lasts for the life of the underlying client
        instance, or until :meth:`logout` is called.

        Raises :class:`TypeError` if (required) `name`, (optional) `password`,
        or (optional) `source` is not an instance of :class:`basestring`
        (:class:`str` in python 3).

        .. note::
          - This method authenticates the current connection, and
            will also cause all new :class:`~socket.socket` connections
            in the underlying client instance to be authenticated automatically.

          - Authenticating more than once on the same database with different
            credentials is not supported. You must call :meth:`logout` before
            authenticating with new credentials.

          - When sharing a client instance between multiple threads, all
            threads will share the authentication. If you need different
            authentication profiles for different purposes you must use
            distinct client instances.

          - To get authentication to apply immediately to all
            existing sockets you may need to reset this client instance's
            sockets using :meth:`~pymongo.mongo_client.MongoClient.disconnect`.

        :Parameters:
          - `name`: the name of the user to authenticate.
          - `password` (optional): the password of the user to authenticate.
            Not used with GSSAPI or MONGODB-X509 authentication.
          - `source` (optional): the database to authenticate on. If not
            specified the current database is used.
          - `mechanism` (optional): See
            :data:`~pymongo.auth.MECHANISMS` for options.
            Defaults to MONGODB-CR (MongoDB Challenge Response protocol)
          - `gssapiServiceName` (optional): Used with the GSSAPI mechanism
            to specify the service name portion of the service principal name.
            Defaults to 'mongodb'.

        .. versionchanged:: 2.5
           Added the `source` and `mechanism` parameters. :meth:`authenticate`
           now raises a subclass of :class:`~pymongo.errors.PyMongoError` if
           authentication fails due to invalid credentials or configuration
           issues.

        .. mongodoc:: authenticate
        """
        if not isinstance(name, basestring):
            raise TypeError("name must be an instance "
                            "of %s" % (basestring.__name__, ))
        if password is not None and not isinstance(password, basestring):
            raise TypeError("password must be an instance "
                            "of %s" % (basestring.__name__, ))
        if source is not None and not isinstance(source, basestring):
            raise TypeError("source must be an instance "
                            "of %s" % (basestring.__name__, ))
        common.validate_auth_mechanism('mechanism', mechanism)

        validated_options = {}
        for option, value in kwargs.iteritems():
            normalized, val = common.validate_auth_option(option, value)
            validated_options[normalized] = val

        credentials = auth._build_credentials_tuple(
            mechanism, source or self.name, unicode(name),
            password and unicode(password) or None, validated_options)
        self.connection._cache_credentials(self.name, credentials)
        return True