Skip to content

Commit

Permalink
Deprecate Entrust Certificate
Browse files Browse the repository at this point in the history
Remove all references to the client SSL certificate, as this is
no longer required/supported by Xero.

See https://developer.xero.com/documentation/auth-and-limits/entrust-certificate-deprecation
for more.
  • Loading branch information
MattHealy authored and jneves committed Mar 28, 2018
1 parent c0ea54c commit 0c75e42
Show file tree
Hide file tree
Showing 6 changed files with 20 additions and 36 deletions.
19 changes: 8 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ PyXero

[![Build Status](https://travis-ci.org/freakboy3742/pyxero.svg?branch=master)](https://travis-ci.org/freakboy3742/pyxero)

PyXero is a Python API for accessing the REST API provided by the [Xero](http://developer.xero.com)
PyXero is a Python API for accessing the REST API provided by the [Xero](https://developer.xero.com)
accounting tool. It allows access to both Public, Private and Partner applications.

## Quickstart:
Expand All @@ -20,7 +20,7 @@ You'll need to follow the [Xero Developer documentation](https://developer.xero.

Public applications use a 3-step OAuth process.

When you [register your public application with Xero](http://developer.xero.com/documentation/auth-and-limits/public-applications/), you'll be given a
When you [register your public application with Xero](https://developer.xero.com/documentation/auth-and-limits/public-applications/), you'll be given a
**Consumer Key** and a **Consumer secret**. These are both strings.

To access the Xero API you must first create some credentials:
Expand Down Expand Up @@ -102,10 +102,10 @@ to reconstruct an instance of the credentials::
If using a Private application, you will need to install `PyCrypto`, a pure
Python cryptographic module. You'll also need to generate an signed RSA
certificate, and submit that certificate as part of registering your
application with Xero. See the [Xero Developer documentation](http://developer.xero.com/) for more
application with Xero. See the [Xero Developer documentation](https://developer.xero.com/) for more
details.

When you [register your private application with Xero](http://developer.xero.com/documentation/auth-and-limits/private-applications/), you'll be given a
When you [register your private application with Xero](https://developer.xero.com/documentation/auth-and-limits/private-applications/), you'll be given a
**Consumer Key**. You'll also be given a **Consumer secret** - this can be
ignored.

Expand All @@ -122,7 +122,7 @@ signed API requests::
>>> xero = Xero(credentials)
```

[Follow these steps](http://developer.xero.com/documentation/api-guides/create-publicprivate-key/) to generate a public/private key pair to sign your requests. You'll upload your public key when you create your Xero Private app at https://app.xero.com. You'll use the private key (aka RSA key) to generate your oAuth signature.
[Follow these steps](https://developer.xero.com/documentation/api-guides/create-publicprivate-key/) to generate a public/private key pair to sign your requests. You'll upload your public key when you create your Xero Private app at https://app.xero.com. You'll use the private key (aka RSA key) to generate your oAuth signature.

The RSA key is a multi-line string that will look something like::

Expand All @@ -147,13 +147,10 @@ store the key value as a constant, remember two things:
### Partner Applications

Partner Application authentication works similarly to the 3-step OAuth used by
Public Applications, but with RSA signed requests and a client-side SSL
certificate which is issued by Xero. Partner OAuth tokens still have a 30 minute
expiry, but can be swapped for a new token at any time.
Public Applications, but with RSA signed requests. Partner OAuth tokens still
have a 30 minute expiry, but can be swapped for a new token at any time.

When you [register your partner application with Xero](http://developer.xero.com/documentation/auth-and-limits/partner-applications/), you'll have a
**Consumer Key**, **Consumer Secret** and **RSA Key**
All three elements are required.
When you [register your partner application with Xero](https://developer.xero.com/documentation/auth-and-limits/partner-applications/), you'll have a **Consumer Key**, **Consumer Secret** and **RSA Key**. All three elements are required.


```python
Expand Down
2 changes: 0 additions & 2 deletions tests/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,6 @@ def test_initial_constructor(self, r_post):
consumer_key='key',
consumer_secret='secret',
rsa_key='abc',
client_cert=('/fake/path', '/fake/otherpath'),
scope='payroll.endpoint'
)

Expand Down Expand Up @@ -264,7 +263,6 @@ def test_refresh(self, r_post):
consumer_key='key',
consumer_secret='secret',
rsa_key="key",
client_cert=None,
oauth_token='token',
oauth_token_secret='token_secret',
verified=True
Expand Down
28 changes: 10 additions & 18 deletions xero/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,7 @@
from six.moves.urllib.parse import urlencode, parse_qs

from .constants import (
XERO_BASE_URL, XERO_PARTNER_BASE_URL,
REQUEST_TOKEN_URL, AUTHORIZE_URL, ACCESS_TOKEN_URL
XERO_BASE_URL, REQUEST_TOKEN_URL, AUTHORIZE_URL, ACCESS_TOKEN_URL
)
from .exceptions import (
XeroBadRequest, XeroException, XeroExceptionUnknown, XeroForbidden,
Expand Down Expand Up @@ -128,7 +127,6 @@ def __init__(self, consumer_key, consumer_secret,
# These are not strictly used by Public Credentials, but
# are reserved for use by other credentials (i.e. Partner)
self.rsa_key = None
self.client_cert = None
self.oauth_session_handle = None

self._init_credentials(oauth_token, oauth_token_secret)
Expand Down Expand Up @@ -161,7 +159,7 @@ def _init_credentials(self, oauth_token, oauth_token_secret):

url = self.base_url + REQUEST_TOKEN_URL
headers = {'User-Agent': self.user_agent}
response = requests.post(url=url, headers=headers, auth=oauth, cert=self.client_cert)
response = requests.post(url=url, headers=headers, auth=oauth)
self._process_oauth_response(response)

def _init_oauth(self, oauth_token, oauth_token_secret):
Expand Down Expand Up @@ -276,7 +274,7 @@ def verify(self, verifier):
# Make the verification request, gettiung back an access token
url = self.base_url + ACCESS_TOKEN_URL
headers = {'User-Agent': self.user_agent}
response = requests.post(url=url, headers=headers, auth=oauth, cert=self.client_cert)
response = requests.post(url=url, headers=headers, auth=oauth)
self._process_oauth_response(response)
self.verified = True

Expand Down Expand Up @@ -326,30 +324,25 @@ class PartnerCredentials(PublicCredentials):
>>> rsa_key = "-----BEGIN RSA PRIVATE KEY----- ..."
2) client_cert is no longer used regarding new Xero security policy,
but stays for now for backward-compatibility.
>>> client_cert = ('/path/to/entrust-cert.pem',
'/path/to/entrust-private-nopass.pem')
3) Once a token has expired, you can refresh it to get another 30 mins
2) Once a token has expired, you can refresh it to get another 30 mins
>>> credentials = PartnerCredentials(**state)
>>> if credentials.expired():
credentials.refresh()
4) Authorization expiry and token expiry become different things.
3) Authorization expiry and token expiry become different things.
oauth_expires_at tells when the current token expires (~30 min window)
oauth_authorization_expires_at tells when the overall access
permissions expire (~10 year window)
"""
def __init__(self, consumer_key, consumer_secret, rsa_key, client_cert=None,
def __init__(self, consumer_key, consumer_secret, rsa_key,
callback_uri=None, verified=False,
oauth_token=None, oauth_token_secret=None,
oauth_expires_at=None, oauth_authorization_expires_at=None,
oauth_session_handle=None, scope=None, user_agent=None):
oauth_session_handle=None, scope=None, user_agent=None,
**kwargs):
"""Construct the auth instance.
Must provide the consumer key and secret.
Expand All @@ -372,10 +365,9 @@ def __init__(self, consumer_key, consumer_secret, rsa_key, client_cert=None,
self.user_agent = user_agent

self._signature_method = SIGNATURE_RSA
self.base_url = XERO_PARTNER_BASE_URL
self.base_url = XERO_BASE_URL

self.rsa_key = rsa_key
self.client_cert = client_cert
self.oauth_session_handle = oauth_session_handle

self._init_credentials(oauth_token, oauth_token_secret)
Expand All @@ -397,5 +389,5 @@ def refresh(self):
headers = {'User-Agent': self.user_agent}
params = {'oauth_session_handle': self.oauth_session_handle}
response = requests.post(url=self.base_url + ACCESS_TOKEN_URL,
params=params, headers=headers, auth=oauth, cert=self.client_cert)
params=params, headers=headers, auth=oauth)
self._process_oauth_response(response)
3 changes: 1 addition & 2 deletions xero/basemanager.py
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,6 @@ def wrapper(*args, **kwargs):
timeout = kwargs.pop('timeout', None)

uri, params, method, body, headers, singleobject = func(*args, **kwargs)
cert = getattr(self.credentials, 'client_cert', None)

if headers is None:
headers = {}
Expand All @@ -184,7 +183,7 @@ def wrapper(*args, **kwargs):

response = getattr(requests, method)(
uri, data=body, headers=headers, auth=self.credentials.oauth,
params=params, cert=cert, timeout=timeout)
params=params, timeout=timeout)

if response.status_code == 200:
# If we haven't got XML or JSON, assume we're being returned a binary file
Expand Down
1 change: 0 additions & 1 deletion xero/constants.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
XERO_BASE_URL = "https://api.xero.com"
XERO_PARTNER_BASE_URL = "https://api.xero.com"

REQUEST_TOKEN_URL = "/oauth/RequestToken"
AUTHORIZE_URL = "/oauth/Authorize"
Expand Down
3 changes: 1 addition & 2 deletions xero/filesmanager.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,10 +60,9 @@ def _get_data(self, func):
def wrapper(*args, **kwargs):
uri, params, method, body, headers, singleobject, files = func(*args, **kwargs)

cert = getattr(self.credentials, 'client_cert', None)
response = getattr(requests, method)(
uri, data=body, headers=headers, auth=self.credentials.oauth,
params=params, cert=cert, files = files)
params=params, files=files)

if response.status_code == 200 or response.status_code == 201:
if response.headers['content-type'].startswith('application/json'):
Expand Down

0 comments on commit 0c75e42

Please sign in to comment.