def _new_connection(self): from inbox.auth.base import handler_from_provider # Ensure that connections are initialized serially, so as not to use # many db sessions on startup. with self._new_conn_lock: auth_handler = handler_from_provider(self.provider_name) for retry_count in range(MAX_TRANSIENT_ERRORS): try: conn = auth_handler.connect_account(self.email_address, self.credential, self.imap_endpoint, self.account_id) # If we can connect the account, then we can set the sate # to 'running' if it wasn't already if self.sync_state != 'running': with session_scope() as db_session: query = db_session.query(ImapAccount) account = query.get(self.account_id) self.sync_state = account.sync_state = 'running' return self.client_cls(self.account_id, self.provider_info, self.email_address, conn, readonly=self.readonly) except ConnectionError, e: if isinstance(e, TransientConnectionError): return None else: logger.error('Error connecting', account_id=self.account_id) with session_scope() as db_session: query = db_session.query(ImapAccount) account = query.get(self.account_id) account.sync_state = 'connerror' account.update_sync_error(str(e)) return None except ValidationError, e: # If we failed to validate, but the account is oauth2, we # may just need to refresh the access token. Try this one # time. if (self.provider_info['auth'] == 'oauth2' and retry_count == 0): with session_scope() as db_session: query = db_session.query(ImapAccount) account = query.get(self.account_id) self.credential = token_manager.get_token( account, force_refresh=True) else: logger.error('Error validating', account_id=self.account_id, logstash_tag='mark_invalid') with session_scope() as db_session: query = db_session.query(ImapAccount) account = query.get(self.account_id) account.mark_invalid() account.update_sync_error(str(e)) raise
def _new_connection(self): from inbox.auth.base import handler_from_provider # Ensure that connections are initialized serially, so as not to use # many db sessions on startup. with self._new_conn_lock: auth_handler = handler_from_provider(self.provider_name) for retry_count in range(MAX_TRANSIENT_ERRORS): try: conn = auth_handler.connect_account( self.email_address, self.credential, self.imap_endpoint, self.account_id) # If we can connect the account, then we can set the sate # to 'running' if it wasn't already if self.sync_state != 'running': with session_scope() as db_session: query = db_session.query(ImapAccount) account = query.get(self.account_id) self.sync_state = account.sync_state = 'running' return self.client_cls(self.account_id, self.provider_info, self.email_address, conn, readonly=self.readonly) except ConnectionError, e: if isinstance(e, TransientConnectionError): return None else: logger.error('Error connecting', account_id=self.account_id) with session_scope() as db_session: query = db_session.query(ImapAccount) account = query.get(self.account_id) account.sync_state = 'connerror' account.update_sync_error(str(e)) return None except ValidationError, e: # If we failed to validate, but the account is oauth2, we # may just need to refresh the access token. Try this one # time. if (self.provider_info['auth'] == 'oauth2' and retry_count == 0): with session_scope() as db_session: query = db_session.query(ImapAccount) account = query.get(self.account_id) self.credential = token_manager.get_token( account, force_refresh=True) else: logger.error('Error validating', account_id=self.account_id) with session_scope() as db_session: query = db_session.query(ImapAccount) account = query.get(self.account_id) account.mark_invalid() account.update_sync_error(str(e)) raise
def test_auth_handler_dispatch(): assert isinstance(handler_from_provider("custom"), GenericAuthHandler) assert isinstance(handler_from_provider("fastmail"), GenericAuthHandler) assert isinstance(handler_from_provider("aol"), GenericAuthHandler) assert isinstance(handler_from_provider("yahoo"), GenericAuthHandler) assert isinstance(handler_from_provider("gmail"), GmailAuthHandler) assert isinstance(handler_from_provider("outlook"), OutlookAuthHandler) with pytest.raises(NotSupportedError): handler_from_provider("NOTAREALMAILPROVIDER")
def test_auth_handler_dispatch(): assert isinstance(handler_from_provider('custom'), GenericAuthHandler) assert isinstance(handler_from_provider('fastmail'), GenericAuthHandler) assert isinstance(handler_from_provider('aol'), GenericAuthHandler) assert isinstance(handler_from_provider('yahoo'), GenericAuthHandler) assert isinstance(handler_from_provider('gmail'), GmailAuthHandler) assert isinstance(handler_from_provider('outlook'), OutlookAuthHandler) with pytest.raises(NotSupportedError): handler_from_provider('NOTAREALMAILPROVIDER')
def confim_oauth_user(): response = {} encoder = APIEncoder() data = request.get_json(force=True) email_address = data.get('email_address') token = data.get('token') target = data.get('target', 0) if not email_address: response['error'] = 'Missing key - "email_address"!' return encoder.jsonify(response) if not token: response['error'] = 'Missing key - "token"!' return encoder.jsonify(response) shard_id = target << 48 with session_scope(shard_id) as db_session: account = db_session.query(Account).filter_by( email_address=email_address).first() if account is None: response['error'] = 'Don\'t have this account!' return encoder.jsonify(response) auth_info = {} provider = provider_from_address(email_address) auth_info['provider'] = provider auth_handler = handler_from_provider(provider) try: auth_response = auth_handler._get_authenticated_user(token) auth_response['contacts'] = True auth_response['events'] = True auth_info.update(auth_response) except OAuthError: response['error'] = "Invalid authorization code, try again..." return encoder.jsonify(response) account = auth_handler.update_account(account, auth_info) try: if auth_handler.verify_account(account): db_session.add(account) db_session.commit() response['data'] = 'OK. Authenticated account for {}'.format( email_address) except NotSupportedError as e: response['error'] = str(e) return encoder.jsonify(response) return encoder.jsonify(response)
def create_account(db_session, email, password): provider = provider_from_address(email) auth_handler = handler_from_provider(provider) # Special-case Gmail and Outlook, because we need to provide an oauth token # and not merely a password. response = {'email': email} if provider == 'gmail': code = google_auth(email, password) response = auth_handler._get_authenticated_user(code) elif provider == 'outlook': code = outlook_auth(email, password) response = auth_handler._get_authenticated_user(code) else: response = {"email": email, "password": password} account = auth_handler.create_account(email, response) auth_handler.verify_account(account) account.throttled = False account.sync_host = platform.node() db_session.add(account) db_session.commit() return account
def main(email_address, reauth, target, provider): """ Auth an email account. """ preflight() maybe_enable_rollbar() shard_id = target << 48 with session_scope(shard_id) as db_session: account = (db_session.query(Account).filter_by( email_address=email_address).first()) if account is not None and not reauth: sys.exit("Already have this account!") if not provider: provider = provider_from_address(email_address) # Resolve unknown providers into either custom IMAP or EAS. if provider == "unknown": is_imap = raw_input( "IMAP account? [Y/n] ").strip().lower() != "n" provider = "custom" if is_imap else "eas" auth_handler = handler_from_provider(provider) account_data = auth_handler.interactive_auth(email_address) if reauth: account = auth_handler.update_account(account, account_data) else: account = auth_handler.create_account(account_data) try: if auth_handler.verify_account(account): db_session.add(account) db_session.commit() except NotSupportedError as e: sys.exit(str(e)) print("OK. Authenticated account for {}".format(email_address))
def auth_handler(self): from inbox.auth.base import handler_from_provider return handler_from_provider(self.provider)
def add_new_user(): response = {} encoder = APIEncoder() data = request.get_json(force=True) email_address = data.get('email_address') password = data.get('password') auth_details = data.get('auth_details') reauth = data.get('reauth') target = data.get('target', 0) if not email_address: response['error'] = 'Missing key - "email_address"!' return encoder.jsonify(response) shard_id = target << 48 with session_scope(shard_id) as db_session: account = db_session.query(Account).filter_by( email_address=email_address).first() if account is not None and not reauth: response['error'] = 'Already have this account!' return encoder.jsonify(response) auth_info = {} provider = provider_from_address(email_address) if 'gmail' in provider: auth_handler = handler_from_provider(provider) response['oauth_url'] = auth_handler.get_oauth_url(email_address) response['links'] = {'confirm_url': request.url + '/confirm_oauth'} namespace = Namespace() account = GmailAccount(namespace=namespace) account.sync_should_run = False account.refresh_token = '_placeholder_' account.email_address = email_address else: if not password: response['error'] = 'Missing key - "password"!' return encoder.jsonify(response) auth_info['email'] = email_address auth_info['password'] = password if provider != 'unknown': auth_handler = handler_from_provider(provider) auth_info['provider'] = provider try: if reauth: account = auth_handler.update_account( account, auth_info) else: account = auth_handler.create_account( email_address, auth_info) except Exception as e: response['error'] = e.msg else: auth_info['provider'] = 'custom' auth_handler = handler_from_provider('custom') if not auth_details: auth_info.update( try_fill_config_data(email_address, password)) else: auth_info.update(auth_details) try: if reauth: account = auth_handler.update_account( account, auth_info) else: account = auth_handler.create_account( email_address, auth_info) except Exception as e: response['error'] = str(e) try: auth_handler.verify_account(account) response['data'] = 'OK. Authenticated account for {}'.format( email_address) except Exception as e: response['error'] = str(e) db_session.add(account) db_session.commit() return encoder.jsonify(response)
def test_auth_handler_dispatch(): assert isinstance(handler_from_provider("custom"), GenericAuthHandler) assert isinstance(handler_from_provider("gmail"), GoogleAuthHandler) with pytest.raises(NotSupportedError): handler_from_provider("NOTAREALMAILPROVIDER")
def create_account(): g.parser.add_argument('email', required=True, type=bounded_str, location='form') g.parser.add_argument('smtp_host', required=True, type=bounded_str, location='form') g.parser.add_argument('smtp_port', type=int, location='form') g.parser.add_argument('smtp_username', required=True, type=bounded_str, location='form') g.parser.add_argument('smtp_password', required=True, type=bounded_str, location='form') g.parser.add_argument('imap_host', required=True, type=bounded_str, location='form') g.parser.add_argument('imap_port', type=int, location='form') g.parser.add_argument('imap_username', required=True, type=bounded_str, location='form') g.parser.add_argument('imap_password', required=True, type=bounded_str, location='form') g.parser.add_argument('ssl_required', required=True, type=bool, location='form') args = strict_parse_args(g.parser, request.args) target = find_account_shard(args['email']) if target is None: target = config.get('NEW_ACCOUNTS_DEFAULT_SHARD', 0) << 48 with session_scope(target) as db_session: account = db_session.query(Account).filter_by( email_address=args['email']).first() provider_auth_info = dict(provider='custom', email=args['email'], imap_server_host=args['imap_host'], imap_server_port=(args.get('imap_port') or DEFAULT_IMAP_SSL_PORT), imap_username=args['imap_username'], imap_password=args['imap_password'], smtp_server_host=args['smtp_host'], smtp_server_port=(args.get('smtp_port') or DEFAULT_SMTP_SSL_PORT), smtp_username=args['smtp_username'], smtp_password=args['smtp_password'], ssl_required=args['ssl_required']) auth_handler = handler_from_provider(provider_auth_info['provider']) if account is None: account = auth_handler.create_account(args['email'], provider_auth_info) else: account = account.auth_handler.update_account( account, provider_auth_info) try: resp = None if auth_handler.verify_account(account): db_session.add(account) db_session.commit() resp = simplejson.dumps({ 'account_id': account.public_id, 'namespace_id': account.namespace.public_id }) return make_response((resp, 201, { 'Content-Type': 'application/json' })) else: resp = simplejson.dumps({ 'message': 'Account verification failed', 'type': 'api_error' }) return make_response((resp, 422, { 'Content-Type': 'application/json' })) except ValidationError as e: return unprocessable_entity_response(e.message.message) except gaierror as error: if error[1] == 'Name or service not known': return unprocessable_entity_response(error[1]) else: raise error except NotSupportedError as e: resp = simplejson.dumps({ 'message': str(e), 'type': 'custom_api_error' }) return make_response((resp, 400, { 'Content-Type': 'application/json' }))
def auth_callback(): g.parser.add_argument('authorization_code', type=bounded_str, location='args', required=True) g.parser.add_argument('email', required=True, type=bounded_str, location='args') args = strict_parse_args(g.parser, request.args) target = find_account_shard(args['email']) if target is None: target = config.get('NEW_ACCOUNTS_DEFAULT_SHARD', 0) << 48 with session_scope(target) as db_session: account = db_session.query(Account).filter_by( email_address=args['email']).first() updating_account = False if account is not None: updating_account = True auth_handler = handler_from_provider('gmail') request_args = { 'client_id': GmailAuthHandler.OAUTH_CLIENT_ID, 'client_secret': GmailAuthHandler.OAUTH_CLIENT_SECRET, 'redirect_uri': GmailAuthHandler.OAUTH_REDIRECT_URI, 'code': args['authorization_code'], 'grant_type': 'authorization_code' } headers = { 'Content-type': 'application/x-www-form-urlencoded', 'Accept': 'text/plain' } data = urllib.urlencode(request_args) resp_dict = requests.post(auth_handler.OAUTH_ACCESS_TOKEN_URL, data=data, headers=headers).json() if u'error' in resp_dict: raise APIException('Internal error: ' + str(resp_dict['error'])) access_token = resp_dict['access_token'] validation_dict = auth_handler.validate_token(access_token) userinfo_dict = auth_handler._get_user_info(access_token) if userinfo_dict['email'].lower() != args['email'].lower(): raise InputError('Email mismatch') resp_dict.update(validation_dict) resp_dict.update(userinfo_dict) resp_dict['contacts'] = True resp_dict['events'] = True auth_info = {'provider': 'gmail'} auth_info.update(resp_dict) if updating_account: account.auth_handler.update_account(account, auth_info) else: account = auth_handler.create_account(args['email'], auth_info) try: if auth_handler.verify_account(account): db_session.add(account) db_session.commit() except NotSupportedError: raise APIException('Internal error: ' + str(resp_dict['error'])) resp = simplejson.dumps({ 'account_id': account.public_id, 'namespace_id': account.namespace.public_id }) return make_response((resp, 201, {'Content-Type': 'application/json'}))