Exemplo n.º 1
0
    def verify_account(self, account):
        """Verifies a generic IMAP account by logging in and logging out.

        Note: Raises exceptions from connect_account() on error.

        Returns
        -------
        True: If the client can successfully connect.
        """
        conn = self.connect_account(account)
        info = account.provider_info
        if "condstore" not in info:
            if self._supports_condstore(conn):
                account.supports_condstore = True
        try:
            conn.list_folders()
        except Exception as e:
            log.error("account_folder_list_failed",
                      email=account.email_address,
                      account_id=account.id,
                      error=e.message)
            raise UserRecoverableConfigError("Full IMAP support is not "
                                             "enabled for this account. "
                                             "Please contact your domain "
                                             "administrator and try again.")
        finally:
            conn.logout()
        try:
            # Check that SMTP settings work by establishing and closing and
            # SMTP session.
            smtp_client = SMTPClient(account)
            with smtp_client._get_connection():
                pass
        except Exception as exc:
            log.error('Failed to establish an SMTP connection',
                      email=account.email_address,
                      account_id=account.id,
                      error=exc)
            raise UserRecoverableConfigError("Please check that your SMTP "
                                             "settings are correct.")
        return True
Exemplo n.º 2
0
    def create_account(self, db_session, email_address, response):
        email_address = response.get('email')
        # See if the account exists in db, otherwise create it
        try:
            account = db_session.query(GmailAccount) \
                .filter_by(email_address=email_address).one()
        except NoResultFound:
            namespace = Namespace()
            account = GmailAccount(namespace=namespace)

        # We only get refresh tokens on initial login (or failed credentials)
        # otherwise, we don't force the login screen and therefore don't get a
        # refresh token back from google.
        new_refresh_token = response.get('refresh_token')
        if new_refresh_token:
            account.refresh_token = new_refresh_token
        else:
            if not account.refresh_token or account.sync_state == 'invalid':
                # We got a new auth without a refresh token, so we need to back
                # out and force the auth flow, since we don't already have
                # a refresh (or the one we have doesn't work.)
                raise OAuthError("Missing refresh token")

        tok = response.get('access_token')
        expires_in = response.get('expires_in')
        token_manager.cache_token(account, tok, expires_in)
        account.scope = response.get('scope')
        account.email_address = email_address
        account.family_name = response.get('family_name')
        account.given_name = response.get('given_name')
        account.name = response.get('name')
        account.gender = response.get('gender')
        account.g_id = response.get('id')
        account.g_user_id = response.get('user_id')
        account.g_id_token = response.get('id_token')
        account.link = response.get('link')
        account.locale = response.get('locale')
        account.picture = response.get('picture')
        account.home_domain = response.get('hd')
        account.client_id = response.get('client_id')
        account.client_secret = response.get('client_secret')
        account.sync_contacts = response.get('contacts', True)
        account.sync_events = response.get('events', True)

        try:
            self.verify_config(account)
        except GmailSettingError as e:
            raise UserRecoverableConfigError(e)

        # Ensure account has sync enabled.
        account.enable_sync()

        return account
Exemplo n.º 3
0
    def update_account(self, account, response):
        account.email_address = response['email']
        for attribute in [
                'name', 'imap_username', 'imap_password', 'smtp_username',
                'smtp_password', 'password'
        ]:
            if response.get(attribute):
                setattr(account, attribute, response[attribute])

        # Shim for back-compatability with legacy auth
        if response.get('imap_password'):
            # The new API sends separate IMAP/ SMTP credentials but we need to
            # set the legacy password attribute.
            # TODO[k]: Remove once column in dropped.
            account.password = response['imap_password']
        else:
            # The old API does NOT send these but authentication now uses them
            # so update them.
            for attr in ('imap_username', 'smtp_username'):
                if attr not in response:
                    setattr(account, attr, response['email'])
            for attr in ('imap_password', 'smtp_password'):
                if attr not in response:
                    setattr(account, attr, response['password'])

        account.date = datetime.datetime.utcnow()

        if self.provider_name == 'custom':
            for attribute in ('imap_server_host', 'smtp_server_host'):
                old_value = getattr(account, '_{}'.format(attribute), None)
                new_value = response.get(attribute)
                if (new_value and old_value and new_value != old_value):
                    # Before updating the domain name, check if:
                    # 1/ they have the same parent domain
                    # 2/ they direct to the same IP.
                    if not matching_subdomains(new_value, old_value):
                        raise UserRecoverableConfigError(
                            "Updating the IMAP/SMTP servers is not permitted. Please "
                            "verify that the server names you entered are correct. "
                            "If your IMAP/SMTP server has in fact changed, please "
                            "contact Nylas support to update it. More details here: "
                            "https://support.nylas.com/hc/en-us/articles/218006767"
                        )

                    # If all those conditions are met, update the address.
                    setattr(account, '_{}'.format(attribute), new_value)

        account.ssl_required = response.get('ssl_required', True)

        # Ensure account has sync enabled after authing.
        account.enable_sync()
        return account
Exemplo n.º 4
0
    def create_account(self, db_session, email_address, response):
        email_address = response.get('email')
        # See if the account exists in db, otherwise create it
        try:
            account = db_session.query(GmailAccount) \
                .filter_by(email_address=email_address).one()
        except NoResultFound:
            namespace = Namespace()
            account = GmailAccount(namespace=namespace)

        # We only get refresh tokens on initial login (or failed credentials)
        # otherwise, we don't force the login screen and therefore don't get a
        # refresh token back from google.
        new_refresh_token = response.get('refresh_token')
        if new_refresh_token:
            account.refresh_token = new_refresh_token

        tok = response.get('access_token')
        expires_in = response.get('expires_in')
        account.set_access_token(tok, expires_in)
        account.scope = response.get('scope')
        account.email_address = email_address
        account.family_name = response.get('family_name')
        account.given_name = response.get('given_name')
        account.name = response.get('name')
        account.gender = response.get('gender')
        account.g_id = response.get('id')
        account.g_user_id = response.get('user_id')
        account.g_id_token = response.get('id_token')
        account.link = response.get('link')
        account.locale = response.get('locale')
        account.picture = response.get('picture')
        account.home_domain = response.get('hd')
        account.client_id = response.get('client_id')
        account.client_secret = response.get('client_secret')

        try:
            self.verify_config(account)
        except GmailSettingError as e:
            raise UserRecoverableConfigError(e)

        # Hack to ensure that account syncs get restarted if they were stopped
        # because of e.g. invalid credentials and the user re-auths.
        # TODO(emfree): remove after status overhaul.
        if account.sync_state != 'running':
            account.sync_state = None

        return account
Exemplo n.º 5
0
    def create_account(self, db_session, email_address, response):
        # Override create_account to persist the 'login hint' email_address
        # rather than the canonical email that is contained in response.
        # This allows us to trigger errors by authing with addresses of the
        # format:
        #    [email protected]

        # Since verify_config throws an Exception if no specific case is
        # triggered, this account is never committed.
        namespace = Namespace()
        account = GmailAccount(namespace=namespace)
        account.email_address = email_address

        try:
            self.verify_config(account)
        except GmailSettingError as e:
            print e
            raise UserRecoverableConfigError(e)

        return account
Exemplo n.º 6
0
    def update_account(self, account, response):
        account.email_address = response['email']
        for attribute in [
                'name', 'imap_username', 'imap_password', 'smtp_username',
                'smtp_password', 'password'
        ]:
            if response.get(attribute):
                setattr(account, attribute, response[attribute])

        # Shim for back-compatability with legacy auth
        if response.get('imap_password'):
            # The new API sends separate IMAP/ SMTP credentials but we need to
            # set the legacy password attribute.
            # TODO[k]: Remove once column in dropped.
            account.password = response['imap_password']
        else:
            # The old API does NOT send these but authentication now uses them
            # so update them.
            for attr in ('imap_username', 'smtp_username'):
                if attr not in response:
                    setattr(account, attr, response['email'])
            for attr in ('imap_password', 'smtp_password'):
                if attr not in response:
                    setattr(account, attr, response['password'])

        account.date = datetime.datetime.utcnow()

        if self.provider_name == 'custom':
            for attribute in ('imap_server_host', 'smtp_server_host'):
                value = getattr(account, '_{}'.format(attribute), None)
                if (response.get(attribute) and value
                        and response[attribute] != value):
                    raise UserRecoverableConfigError(
                        "Updating IMAP/ SMTP endpoints is not permitted. "
                        "Please contact Nylas support to do so.")

        account.ssl_required = response.get('ssl_required', True)

        # Ensure account has sync enabled after authing.
        account.enable_sync()
        return account
Exemplo n.º 7
0
    def verify_account(self, account):
        """
        Verifies a generic IMAP account by logging in and logging out to both
        the IMAP/ SMTP servers.

        Note:
        Raises exceptions from connect_account(), SMTPClient._get_connection()
        on error.

        Returns
        -------
        True: If the client can successfully connect to both.

        """
        # Verify IMAP login
        conn = self.connect_account(account)
        crispin = CrispinClient(account.id, account.provider_info,
                                account.email_address, conn)

        info = account.provider_info
        if "condstore" not in info:
            if self._supports_condstore(conn):
                account.supports_condstore = True
        try:
            conn.list_folders()
            account.folder_separator = crispin.folder_separator
            account.folder_prefix = crispin.folder_prefix
        except Exception as e:
            log.error("account_folder_list_failed",
                      account_id=account.id,
                      error=e.message)
            error_message = (
                "Full IMAP support is not enabled for this account. "
                "Please contact your domain "
                "administrator and try again.")
            raise UserRecoverableConfigError(error_message)
        finally:
            conn.logout()

        # Verify SMTP login
        try:
            # Check that SMTP settings work by establishing and closing and
            # SMTP session.
            smtp_client = SMTPClient(account)
            with smtp_client._get_connection():
                pass
        except socket.gaierror as exc:
            log.error('Failed to resolve SMTP server domain',
                      account_id=account.id,
                      error=exc)
            error_message = (
                "Couldn't resolve the SMTP server domain name. "
                "Please check that your SMTP settings are correct.")
            raise UserRecoverableConfigError(error_message)

        except socket.timeout as exc:
            log.error('TCP timeout when connecting to SMTP server',
                      account_id=account.id,
                      error=exc)

            error_message = (
                "Connection timeout when connecting to SMTP server. "
                "Please check that your SMTP settings are correct.")
            raise UserRecoverableConfigError(error_message)

        except Exception as exc:
            log.error('Failed to establish an SMTP connection',
                      smtp_endpoint=account.smtp_endpoint,
                      account_id=account.id,
                      error=exc)
            raise UserRecoverableConfigError("Please check that your SMTP "
                                             "settings are correct.")

        # Reset the sync_state to 'running' on a successful re-auth.
        # Necessary for API requests to proceed and an account modify delta to
        # be returned to delta/ streaming clients.
        # NOTE: Setting this does not restart the sync. Sync scheduling occurs
        # via the sync_should_run bit (set to True in update_account() above).
        account.sync_state = ('running'
                              if account.sync_state else account.sync_state)
        return True
Exemplo n.º 8
0
    def verify_account(self, account):
        """
        Verifies a generic IMAP account by logging in and logging out to both
        the IMAP/ SMTP servers.

        Note:
        Raises exceptions from connect_account(), SMTPClient._get_connection()
        on error.

        Returns
        -------
        True: If the client can successfully connect to both.

        """
        # Verify IMAP login
        conn = self.connect_account(account)
        info = account.provider_info
        if "condstore" not in info:
            if self._supports_condstore(conn):
                account.supports_condstore = True
        try:
            conn.list_folders()
        except Exception as e:
            log.error("account_folder_list_failed",
                      email=account.email_address,
                      account_id=account.id,
                      error=e.message)
            error_message = ("Full IMAP support is not enabled for this account. "
                             "Please contact your domain "
                             "administrator and try again.")
            raise UserRecoverableConfigError(error_message)

        finally:
            conn.logout()

        # Verify SMTP login
        try:
            # Check that SMTP settings work by establishing and closing and
            # SMTP session.
            smtp_client = SMTPClient(account)
            with smtp_client._get_connection():
                pass
        except socket.gaierror as exc:
            log.error('Failed to resolve SMTP server domain',
                      email=account.email_address,
                      account_id=account.id,
                      error=exc)
            error_message = ("Couldn't resolve the SMTP server domain name. "
                             "Please check that your SMTP settings are correct.")
            raise UserRecoverableConfigError(error_message)

        except socket.timeout as exc:
            log.error('TCP timeout when connecting to SMTP server',
                      email=account.email_address,
                      account_id=account.id,
                      error=exc)

            error_message = ("Connection timeout when connecting to SMTP server. "
                             "Please check that your SMTP settings are correct.")
            raise UserRecoverableConfigError(error_message)

        except Exception as exc:
            log.error('Failed to establish an SMTP connection',
                      email=account.email_address,
                      smtp_endpoint=account.smtp_endpoint,
                      account_id=account.id,
                      error=exc)
            raise UserRecoverableConfigError("Please check that your SMTP "
                                             "settings are correct.")
        return True
Exemplo n.º 9
0
    def create_account(self, db_session, email_address, response):
        email_address = response.get('email')
        # See if the account exists in db, otherwise create it
        try:
            account = db_session.query(GmailAccount) \
                .filter_by(email_address=email_address).one()
        except NoResultFound:
            namespace = Namespace()
            account = GmailAccount(namespace=namespace)

        # We only get refresh tokens on initial login (or failed credentials)
        # otherwise, we don't force the login screen and therefore don't get a
        # refresh token back from google.
        new_refresh_token = response.get('refresh_token')
        if new_refresh_token:
            account.refresh_token = new_refresh_token
        else:
            if (len(account.valid_auth_credentials) == 0
                    or account.sync_state == 'invalid'):
                # We got a new auth without a refresh token, so we need to back
                # out and force the auth flow, since we don't already have
                # a refresh (or the ones we have don't work.)
                raise OAuthError("No valid refresh tokens")

        account.email_address = email_address
        account.family_name = response.get('family_name')
        account.given_name = response.get('given_name')
        account.name = response.get('name')
        account.gender = response.get('gender')
        account.g_id = response.get('id')
        account.g_user_id = response.get('user_id')
        account.link = response.get('link')
        account.locale = response.get('locale')
        account.picture = response.get('picture')
        account.home_domain = response.get('hd')
        account.sync_contacts = (account.sync_contacts
                                 or response.get('contacts', True))
        account.sync_events = (account.sync_events
                               or response.get('events', True))

        # These values are deprecated and should not be used, along
        # with the account's refresh_token. Access all these values
        # through the GmailAuthCredentials objects instead.
        account.client_id = response.get('client_id')
        account.client_secret = response.get('client_secret')
        account.scope = response.get('scope')
        account.g_id_token = response.get('id_token')

        # Don't need to actually save these now
        # tok = response.get('access_token')
        # expires_in = response.get('expires_in')

        client_id = response.get('client_id') or OAUTH_CLIENT_ID
        client_secret = response.get('client_secret') or OAUTH_CLIENT_SECRET

        if new_refresh_token:
            # See if we already have credentials for this client_id/secret
            # pair. If those don't exist, make a new GmailAuthCredentials
            auth_creds = next(
                (auth_creds for auth_creds in account.auth_credentials
                 if (auth_creds.client_id == client_id
                     and auth_creds.client_secret == client_secret)),
                GmailAuthCredentials())

            auth_creds.gmailaccount = account
            auth_creds.scopes = response.get('scope')
            auth_creds.g_id_token = response.get('id_token')
            auth_creds.client_id = client_id
            auth_creds.client_secret = client_secret
            auth_creds.refresh_token = new_refresh_token
            auth_creds.is_valid = True

            db_session.add(auth_creds)

        try:
            self.verify_config(account)
        except GmailSettingError as e:
            raise UserRecoverableConfigError(e)

        # Ensure account has sync enabled.
        account.enable_sync()

        return account