def _test_login_success(self, url_regex, salesforce_login_kwargs, response_body=tests.LOGIN_RESPONSE_SUCCESS): """Test SalesforceLogin with one set of arguments. Mock login requests at url_regex, returning a successful response, response_body. Check that the fake-login process works when passing salesforce_login_kwargs as keyword arguments to SalesforceLogin in addition to the mocked session and a default username. """ responses.add( responses.POST, url_regex, body=response_body, status=http.OK ) session_state = { 'used': False, } # pylint: disable=missing-docstring,unused-argument def on_response(*args, **kwargs): session_state['used'] = True session = requests.Session() session.hooks = { 'response': on_response, } session_id, instance = SalesforceLogin( session=session, username='******', **salesforce_login_kwargs ) self.assertTrue(session_state['used']) self.assertEqual(session_id, tests.SESSION_ID) self.assertEqual(instance, urlparse(tests.SERVER_URL).netloc)
def test_token_login_success(self): """Test a successful JWT Token login""" responses.add( responses.POST, re.compile(r"^https://login.*$"), body=tests.TOKEN_LOGIN_RESPONSE_SUCCESS, status=http.OK ) session_state = { "used": False, } # pylint: disable=missing-docstring,unused-argument def on_response(*args, **kwargs): session_state["used"] = True session = requests.Session() session.hooks = { "response": on_response, } session_id, instance = SalesforceLogin( session=session, username="******", consumer_key="12345.abcde", privatekey_file=os.path.join(os.path.dirname(__file__), "sample-key.pem"), ) self.assertTrue(session_state["used"]) self.assertEqual(session_id, tests.SESSION_ID) self.assertEqual(instance, urlparse(tests.SERVER_URL).netloc)
def test_custom_session_success(self): """Test custom session""" responses.add(responses.POST, re.compile(r'^https://.*$'), body=tests.LOGIN_RESPONSE_SUCCESS, status=http.OK) session_state = { 'used': False, } # pylint: disable=missing-docstring,unused-argument def on_response(*args, **kwargs): session_state['used'] = True session = requests.Session() session.hooks = { 'response': on_response, } session_id, instance = SalesforceLogin(session=session, username='******', password='******', security_token='token') self.assertTrue(session_state['used']) self.assertEqual(session_id, tests.SESSION_ID) self.assertEqual(instance, urlparse(tests.SERVER_URL).netloc)
def test_token_login_failure_with_warning(self): """Test a failed JWT Token login that also produces a helful warning""" responses.add( responses.POST, re.compile(r"^https://login.*$"), # pylint: disable=line-too-long body='{"error": "invalid_grant", "error_description": "user hasn\'t approved this consumer"}', status=400, ) session_state = { "used": False, } # pylint: disable=missing-docstring,unused-argument def on_response(*args, **kwargs): session_state["used"] = True session = requests.Session() session.hooks = { "response": on_response, } with warnings.catch_warnings(record=True) as warning: with self.assertRaises(SalesforceAuthenticationFailed): # pylint: disable=unused-variable session_id, instance = SalesforceLogin( session=session, username="******", consumer_key="12345.abcde", privatekey_file=os.path.join(os.path.dirname(__file__), "sample-key.pem"), ) assert len(warning) >= 1 assert issubclass(warning[-1].category, UserWarning) assert str(warning[-1].message) == tests.TOKEN_WARNING self.assertTrue(session_state["used"])
def test_domain_sandbox_mutual_exclusion_failure(self): """Test sandbox and domain mutual exclusion""" with self.assertRaises(ValueError): SalesforceLogin( username='******', password='******', security_token='token', domain='login', sandbox=False )
def _reconnect(self, method=None, url=None, **kwargs): if self._security_token is None: self.session_id, self.sf_instance = SalesforceLogin( session=self.session, username=self._username, password=self._password, organizationId=self._organizationId, sf_version=self.sf_version, proxies=self.proxies, client_id=self._client_id, domain=self.domain) elif self._organizationId is None: self.session_id, self.sf_instance = SalesforceLogin( session=self.session, username=self._username, password=self._password, security_token=self._security_token, sf_version=self.sf_version, proxies=self.proxies, client_id=self._client_id, domain=self.domain) self.headers = { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + self.session_id, 'X-PrettyPrint': '1' } self.base_url = ('https://{instance}/services/data/v{version}/'.format( instance=self.sf_instance, version=self.sf_version)) self.apex_url = ('https://{instance}/services/apexrest/'.format( instance=self.sf_instance)) self.bulk_url = ('https://{instance}/services/async/{version}/'.format( instance=self.sf_instance, version=self.sf_version)) self.api_usage = {} if method is not None: self._call_salesforce(self, method, url, **kwargs) return 1
def _call_salesforce(self, method, url, **kwargs): """Utility method for performing HTTP call to Salesforce. Returns a `requests.result` object. """ # Under some conditions, we'll allow the retrying of the call after an # attempt to fix what's wrong. E.g. expired session token retries_remaining = 1 while retries_remaining >= 0: retries_remaining = retries_remaining - 1 # Make the call result = self.request.request(method, url, headers=self.headers, **kwargs) # If we had trouble if result.status_code >= 300: # Was it an expired session error AND do we have what we need # to attempt a refresh? if result.status_code == RESPONSE_CODE_EXPIRED_SESSION \ and self.auth_type == AUTH_TYPE_DIRECT_WITH_REFRESH: # Let's try to refresh the access_token session_id, sf_instance = SalesforceLogin( refresh_token=self.refresh_token, consumer_id=self.consumer_id, consumer_secret=self.consumer_secret) # If it looks like things went well: if session_id and sf_instance: # Store the new session ID and rebuild the headers # to use it self.session_id = session_id self._build_headers() # Replace the old instance URL with the new one for this # call and then store it internally for future calls url = url.replace(self.sf_instance, sf_instance) self.sf_instance = sf_instance # Continue through the loop again, hopefully # with success continue # If we got here, it's a plain fat old exception _exception_handler(result) # All good, so return the result return result
def test_failure(self): """Test A Failed Login Response""" return_mock = Mock() return_mock.status_code = 500 # pylint: disable=line-too-long return_mock.content = '<?xml version="1.0" encoding="UTF-8"?><soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:sf="urn:fault.partner.soap.sforce.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><soapenv:Body><soapenv:Fault><faultcode>INVALID_LOGIN</faultcode><faultstring>INVALID_LOGIN: Invalid username, password, security token; or user locked out.</faultstring><detail><sf:LoginFault xsi:type="sf:LoginFault"><sf:exceptionCode>INVALID_LOGIN</sf:exceptionCode><sf:exceptionMessage>Invalid username, password, security token; or user locked out.</sf:exceptionMessage></sf:LoginFault></detail></soapenv:Fault></soapenv:Body></soapenv:Envelope>' self.mockrequest.post.return_value = return_mock with self.assertRaises(SalesforceAuthenticationFailed): SalesforceLogin( username="******", password="******", security_token="token", domain="test" ) self.assertTrue(self.mockrequest.post.called)
def test_token_login_failure(self): """Test a failed JWT Token login""" return_mock = Mock() return_mock.status_code = 400 # pylint: disable=line-too-long return_mock.content = '{"error": "invalid_client_id", "error_description": "client identifier invalid"}' self.mockrequest.post.return_value = return_mock with self.assertRaises(SalesforceAuthenticationFailed): SalesforceLogin(username='******', consumer_key='12345.abcde', privatekey_file=os.path.join( os.path.dirname(__file__), 'sample-key.pem')) self.assertTrue(self.mockrequest.post.called)
def test_requests_args(self): mock_sess = Mock() mock_sess.post.return_value = Mock(**{'status_code': 200}) proxies = dict(https='https://my.host.proxy') with patch('simple_salesforce.login.getUniqueElementValueFromXmlString' ) as xml_patch: xml_patch.return_value = "" SalesforceLogin(session=mock_sess, username='******', password='******', security_token='token', proxies=proxies) mock_sess.post.assert_called_once_with(url=ANY, data=ANY, headers=ANY, proxies=proxies, timeout=60)
def test_refresh_token_success(self): """Test refresh_token success""" responses.add( responses.POST, re.compile(r'^https://.*$'), body=tests.REFRESH_TOKEN_RESPONSE_SUCCESS_STRING, # json=tests.REFRESH_TOKEN_RESPONSE_SUCCESS_JSON, content_type='application/json', status=200) session_id, sf_instance = SalesforceLogin( refresh_token='REFRESH_TOKEN_PROVIDED_DURING_A_PRIOR_AUTH', consumer_id='MY_CONNECTED_APP_ID', consumer_secret='MY_CONNECTED_APP_SECRET') self.assertEqual(session_id, tests.SESSION_ID) self.assertEqual(sf_instance, urlparse(tests.SERVER_URL).netloc)
def login(self): config = self.container.config['SALESFORCE'] access_token, host = SalesforceLogin( session=None, username=self.username, password=self.password, security_token=self.security_token, sandbox=self.sandbox, sf_version=self.api_version, ) self.access_token = access_token self.server_uri = 'https://{}/cometd/{}'.format(host, self.api_version) logger.info('Logged in to salesforce as %s', config['USERNAME'])
def test_custom_session_success(self): httpretty.register_uri(httpretty.POST, re.compile(r'^https://.*$'), body=tests.LOGIN_RESPONSE_SUCCESS, status=http.OK) session_state = { 'used': False, } def on_response(*args, **kwargs): session_state['used'] = True session = requests.Session() session.hooks = { 'response': on_response, } session_id, instance = SalesforceLogin(session=session, username='******', password='******', security_token='token') self.assertTrue(session_state['used']) self.assertEqual(session_id, tests.SESSION_ID) self.assertEqual(instance, urlparse(tests.SERVER_URL).netloc)
def test_custom_domain_success(self): """Test custom domain login""" responses.add( responses.POST, re.compile(r"^https://testdomain.my.*$"), body=tests.LOGIN_RESPONSE_SUCCESS, status=http.OK ) session_state = { "used": False, } # pylint: disable=missing-docstring,unused-argument def on_response(*args, **kwargs): session_state["used"] = True session = requests.Session() session.hooks = { "response": on_response, } session_id, instance = SalesforceLogin( session=session, username="******", password="******", security_token="token", domain="testdomain.my" ) self.assertTrue(session_state["used"]) self.assertEqual(session_id, tests.SESSION_ID) self.assertEqual(instance, urlparse(tests.SERVER_URL).netloc)
def __init__(self, username=None, password=None, security_token=None, session_id=None, instance=None, instance_url=None, organizationId=None, sandbox=False, version=DEFAULT_API_VERSION, proxies=None, session=None, client_id=None): """Initialize the instance with the given parameters. Available kwargs Password Authentication: * username -- the Salesforce username to use for authentication * password -- the password for the username * security_token -- the security token for the username * sandbox -- True if you want to login to `test.salesforce.com`, False if you want to login to `login.salesforce.com`. Direct Session and Instance Access: * session_id -- Access token for this session Then either * instance -- Domain of your Salesforce instance, i.e. `na1.salesforce.com` OR * instance_url -- Full URL of your instance i.e. `https://na1.salesforce.com Universal Kwargs: * version -- the version of the Salesforce API to use, for example `29.0` * proxies -- the optional map of scheme to proxy server * session -- Custom requests session, created in calling code. This enables the use of requests Session features not otherwise exposed by simple_salesforce. """ # Determine if the user passed in the optional version and/or sandbox # kwargs self.sf_version = version self.sandbox = sandbox self.session = session or requests.Session() self.proxies = self.session.proxies # override custom session proxies dance if proxies is not None: if not session: self.session.proxies = self.proxies = proxies else: logger.warning( 'Proxies must be defined on custom session object, ' 'ignoring proxies: %s', proxies) # Determine if the user wants to use our username/password auth or pass # in their own information if all(arg is not None for arg in (username, password, security_token)): self.auth_type = "password" # Pass along the username/password to our login helper self.session_id, self.sf_instance = SalesforceLogin( session=self.session, username=username, password=password, security_token=security_token, sandbox=self.sandbox, sf_version=self.sf_version, proxies=self.proxies, client_id=client_id) elif all(arg is not None for arg in (session_id, instance or instance_url)): self.auth_type = "direct" self.session_id = session_id # If the user provides the full url (as returned by the OAuth # interface for example) extract the hostname (which we rely on) if instance_url is not None: self.sf_instance = urlparse(instance_url).hostname else: self.sf_instance = instance elif all(arg is not None for arg in (username, password, organizationId)): self.auth_type = 'ipfilter' # Pass along the username/password to our login helper self.session_id, self.sf_instance = SalesforceLogin( session=self.session, username=username, password=password, organizationId=organizationId, sandbox=self.sandbox, sf_version=self.sf_version, proxies=self.proxies, client_id=client_id) else: raise TypeError( 'You must provide login information or an instance and token') if self.sandbox: self.auth_site = 'https://test.salesforce.com' else: self.auth_site = 'https://login.salesforce.com' self.headers = { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + self.session_id, 'X-PrettyPrint': '1' } self.base_url = ('https://{instance}/services/data/v{version}/'.format( instance=self.sf_instance, version=self.sf_version)) self.apex_url = ('https://{instance}/services/apexrest/'.format( instance=self.sf_instance)) self.bulk_url = ('https://{instance}/services/async/{version}/'.format( instance=self.sf_instance, version=self.sf_version)) self.api_usage = {}
def __init__(self, username=None, password=None, security_token=None, session_id=None, instance=None, instance_url=None, organizationId=None, sandbox=None, version=DEFAULT_API_VERSION, proxies=None, session=None, client_id=None, domain=None, consumer_key=None, privatekey_file=None): """Initialize the instance with the given parameters. Available kwargs Password Authentication: * username -- the Salesforce username to use for authentication * password -- the password for the username * security_token -- the security token for the username * sandbox -- DEPRECATED: Use domain instead. * domain -- The domain to using for connecting to Salesforce. Use common domains, such as 'login' or 'test', or Salesforce My domain. If not used, will default to 'login'. OAuth 2.0 JWT Bearer Token Authentication: * consumer_key -- the consumer key generated for the user * privatekey_file -- the path to the private key file used for signing the JWT token Direct Session and Instance Access: * session_id -- Access token for this session Then either * instance -- Domain of your Salesforce instance, i.e. `na1.salesforce.com` OR * instance_url -- Full URL of your instance i.e. `https://na1.salesforce.com Universal Kwargs: * version -- the version of the Salesforce API to use, for example `29.0` * proxies -- the optional map of scheme to proxy server * session -- Custom requests session, created in calling code. This enables the use of requests Session features not otherwise exposed by simple_salesforce. """ if (sandbox is not None) and (domain is not None): raise ValueError("Both 'sandbox' and 'domain' arguments were " "supplied. Either may be supplied, but not " "both.") if sandbox is not None: warnings.warn( "'sandbox' argument is deprecated. Use " "'domain' instead. Overriding 'domain' " "with 'sandbox' value.", DeprecationWarning) domain = 'test' if sandbox else 'login' if domain is None: domain = 'login' # Determine if the user passed in the optional version and/or # domain kwargs self.sf_version = version self.domain = domain self.session = session or requests.Session() self.proxies = self.session.proxies # override custom session proxies dance if proxies is not None: if not session: self.session.proxies = self.proxies = proxies else: logger.warning( 'Proxies must be defined on custom session object, ' 'ignoring proxies: %s', proxies) # Determine if the user wants to use our username/password auth or pass # in their own information if all(arg is not None for arg in (username, password, security_token)): self.auth_type = "password" # Pass along the username/password to our login helper self.session_id, self.sf_instance = SalesforceLogin( session=self.session, username=username, password=password, security_token=security_token, sf_version=self.sf_version, proxies=self.proxies, client_id=client_id, domain=self.domain) elif all(arg is not None for arg in (session_id, instance or instance_url)): self.auth_type = "direct" self.session_id = session_id # If the user provides the full url (as returned by the OAuth # interface for example) extract the hostname (which we rely on) if instance_url is not None: self.sf_instance = urlparse(instance_url).hostname else: self.sf_instance = instance elif all(arg is not None for arg in (username, password, organizationId)): self.auth_type = 'ipfilter' # Pass along the username/password to our login helper self.session_id, self.sf_instance = SalesforceLogin( session=self.session, username=username, password=password, organizationId=organizationId, sf_version=self.sf_version, proxies=self.proxies, client_id=client_id, domain=self.domain) elif all(arg is not None for arg in (username, consumer_key, privatekey_file)): self.auth_type = "jwt-bearer" # Pass along the username/password to our login helper self.session_id, self.sf_instance = SalesforceLogin( session=self.session, username=username, consumer_key=consumer_key, privatekey_file=privatekey_file, proxies=self.proxies, domain=self.domain) else: raise TypeError( 'You must provide login information or an instance and token') self.auth_site = ('https://{domain}.salesforce.com'.format( domain=self.domain)) self.headers = { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + self.session_id, 'X-PrettyPrint': '1' } self.base_url = ('https://{instance}/services/data/v{version}/'.format( instance=self.sf_instance, version=self.sf_version)) self.apex_url = ('https://{instance}/services/apexrest/'.format( instance=self.sf_instance)) self.bulk_url = ('https://{instance}/services/async/{version}/'.format( instance=self.sf_instance, version=self.sf_version)) self.api_usage = {}
def __init__(self, username=None, password=None, security_token=None, session_id=None, instance=None, instance_url=None, refresh_token=None, consumer_id=None, consumer_secret=None, organizationId=None, sandbox=False, version=DEFAULT_API_VERSION, proxies=None, session=None, client_id=None): """Initialize the instance with the given parameters. Available kwargs Password Authentication: * username -- the Salesforce username to use for authentication * password -- the password for the username * security_token -- the security token for the username * sandbox -- True if you want to login to `test.salesforce.com` or False if you want to login to `login.salesforce.com`. Direct Session and Instance Access: * session_id -- Access token for this session * Then either: * instance -- Domain of your Salesforce instance, i.e. `na1.salesforce.com` OR * instance_url -- Full URL of your instance i.e. `https://na1.salesforce.com * Optionally include ALL below for refreshable tokens given to Connected Apps: * consumer_id -- Your Connected App's public key identifier * consumer_secret -- Your Connected App's private key identifier * refresh_token -- The refresh token provided as part of the response to your app's OAuth authentication process. Universal Kwargs: * version -- the version of the Salesforce API to use, for example `29.0` * proxies -- the optional map of scheme to proxy server * session -- Custom requests session, created in calling code. This enables the use of requets Session features not otherwiseexposed by simple_salesforce. """ # Determine if the user passed in the optional version and/or sandbox # kwargs self.sf_version = version self.sandbox = sandbox self.proxies = proxies # Determine if the user wants to use our username/password auth or pass # in their own information if all(arg is not None for arg in (username, password, security_token)): self.auth_type = AUTH_TYPE_PASSWORD # Pass along the username/password to our login helper self.session_id, self.sf_instance = SalesforceLogin( session=session, username=username, password=password, security_token=security_token, sandbox=self.sandbox, sf_version=self.sf_version, proxies=self.proxies, client_id=client_id) elif all(arg is not None for arg in (session_id, instance or instance_url)): self.auth_type = AUTH_TYPE_DIRECT self.session_id = session_id # If the user provides the full url (as returned by the OAuth # interface for example) extract the hostname (which we rely on) if instance_url is not None: self.sf_instance = urlparse(instance_url).hostname else: self.sf_instance = instance # If the user provided a refresh token AND client id and secret, # then this session/access_token is refreshable and we may need to # utilize this if session expires if all(arg is not None for arg in (refresh_token, consumer_id, consumer_secret)): self.auth_type = AUTH_TYPE_DIRECT_WITH_REFRESH self.refresh_token = refresh_token self.consumer_id = consumer_id self.consumer_secret = consumer_secret elif all(arg is not None for arg in (username, password, organizationId)): self.auth_type = AUTH_TYPE_IP_FILTER # Pass along the username/password to our login helper self.session_id, self.sf_instance = SalesforceLogin( session=session, username=username, password=password, organizationId=organizationId, sandbox=self.sandbox, sf_version=self.sf_version, proxies=self.proxies, client_id=client_id) else: raise TypeError( 'You must provide login information or an instance and token') if self.sandbox: self.auth_site = 'https://test.salesforce.com' else: self.auth_site = 'https://login.salesforce.com' self.request = session or requests.Session() self.request.proxies = self.proxies self._build_headers() self.base_url = ('https://{instance}/services/data/v{version}/'.format( instance=self.sf_instance, version=self.sf_version)) self.apex_url = ('https://{instance}/services/apexrest/'.format( instance=self.sf_instance))
def __init__(self, **kwargs): """Initialize the instance with the given parameters. Available kwargs Password Authentication: * username -- the Salesforce username to use for authentication * password -- the password for the username * security_token -- the security token for the username * sandbox -- True if you want to login to `test.salesforce.com`, False if you want to login to `login.salesforce.com`. Direct Session and Instance Access: * session_id -- Access token for this session Then either * instance -- Domain of your Salesforce instance, i.e. `na1.salesforce.com` OR * instance_url -- Full URL of your instance i.e. `https://na1.salesforce.com Universal Kwargs: * version -- the version of the Salesforce API to use, for example `29.0` * proxies -- the optional map of scheme to proxy server """ # Determine if the user passed in the optional version and/or sandbox kwargs self.sf_version = kwargs.get('version', '29.0') self.sandbox = kwargs.get('sandbox', False) self.proxies = kwargs.get('proxies') # Determine if the user wants to use our username/password auth or pass in their own information if 'username' in kwargs and 'password' in kwargs and 'security_token' in kwargs: self.auth_type = "password" username = kwargs['username'] password = kwargs['password'] security_token = kwargs['security_token'] # Pass along the username/password to our login helper self.session_id, self.sf_instance = SalesforceLogin( username=username, password=password, security_token=security_token, sandbox=self.sandbox, sf_version=self.sf_version, proxies=self.proxies) elif 'session_id' in kwargs and ('instance' in kwargs or 'instance_url' in kwargs): self.auth_type = "direct" self.session_id = kwargs['session_id'] # If the user provides the full url (as returned by the OAuth interface for # example) extract the hostname (which we rely on) if 'instance_url' in kwargs: self.sf_instance = urlparse(kwargs['instance_url']).hostname else: self.sf_instance = kwargs['instance'] elif 'username' in kwargs and 'password' in kwargs and 'organizationId' in kwargs: self.auth_type = 'ipfilter' username = kwargs['username'] password = kwargs['password'] organizationId = kwargs['organizationId'] # Pass along the username/password to our login helper self.session_id, self.sf_instance = SalesforceLogin( username=username, password=password, organizationId=organizationId, sandbox=self.sandbox, sf_version=self.sf_version, proxies=self.proxies) else: raise SalesforceGeneralError( 'You must provide login information or an instance and token') if self.sandbox: self.auth_site = 'https://test.salesforce.com' else: self.auth_site = 'https://login.salesforce.com' self.request = requests.Session() self.request.proxies = self.proxies self.headers = { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + self.session_id, 'X-PrettyPrint': '1' } self.base_url = ('https://{instance}/services/data/v{version}/'.format( instance=self.sf_instance, version=self.sf_version)) self.apex_url = ('https://{instance}/services/apexrest/'.format( instance=self.sf_instance))