diff --git a/.travis.yml b/.travis.yml
index 1d0a871..4194ab7 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -2,10 +2,13 @@ language: python
sudo: false
python:
- 2.7
+ - 3.3
+ - 3.4
branches:
only:
- master
- develop
install:
- "pip install -r requirements.txt"
-script: nosetests tests --logging-level=INFO
+script: nosetests tests --with-coverage --cover-package=bankid
+
diff --git a/README.rst b/README.rst
index 7d45b63..4bf4b4e 100644
--- a/README.rst
+++ b/README.rst
@@ -84,14 +84,7 @@ The PyBankID solution can be tested with ``nosetests``:
.. code-block:: bash
- nosetests tests/ --logging-level=INFO
-
-The logging level option is needed due to an issue with the `suds
-`_ module, where a debug level line
-breaks the tests.
-
-Only one test is run against the actual BankID test server, to see that the client
-can connect and that all urls are up to date.
+ nosetests tests/
Documentation
-------------
diff --git a/bankid/__init__.py b/bankid/__init__.py
index e957b75..4be9b26 100644
--- a/bankid/__init__.py
+++ b/bankid/__init__.py
@@ -10,10 +10,10 @@
# release. 'dev' as a _version_extra string means this is a development
# version.
_version_major = 0
-_version_minor = 1
-_version_patch = 4
-#_version_extra = 'dev4'
-#_version_extra = 'b1'
+_version_minor = 2
+_version_patch = 0
+# _version_extra = 'dev4'
+# _version_extra = 'alpha0'
_version_extra = '' # Uncomment this for full releases
# Construct full version string from these.
@@ -28,8 +28,8 @@
description = "BankID client for Python"
-long_description = \
-"""PyBankID is a client for performing BankID signing.
+long_description = """
+PyBankID is a client for performing BankID signing.
The Swedish BankID solution for digital signing uses a SOAP
connection solution, and this module aims at providing a simplifying
@@ -43,8 +43,9 @@
license = 'MIT'
-authors = {'hbldh': ('Henrik Blidh', 'henrik.blidh@nedomkull.com'),
- }
+authors = {
+ 'hbldh': ('Henrik Blidh', 'henrik.blidh@nedomkull.com'),
+}
author = 'Henrik Blidh'
author_email = 'henrik.blidh@nedomkull.com'
url = 'https://github.com/hbldh/pybankid/'
@@ -54,11 +55,13 @@
keywords = ['BankID', 'SOAP']
classifiers = [
'Programming Language :: Python :: 2.7',
+ 'Programming Language :: Python :: 3.3',
+ 'Programming Language :: Python :: 3.4',
'License :: OSI Approved :: MIT License',
'Operating System :: POSIX :: Linux',
'Operating System :: Microsoft :: Windows',
'Operating System :: MacOS :: MacOS X',
- 'Development Status :: 3 - Alpha',
+ 'Development Status :: 4 - Beta',
'Intended Audience :: Developers',
'Topic :: Utilities'
- ]
+]
diff --git a/bankid/client.py b/bankid/client.py
index 2ca96e8..77f44fb 100644
--- a/bankid/client.py
+++ b/bankid/client.py
@@ -20,7 +20,7 @@
from __future__ import absolute_import
import warnings
-import StringIO
+import six
import base64
import datetime
@@ -37,7 +37,7 @@
class BankIDClient(object):
- def __init__(self, certificates, test_server=True):
+ def __init__(self, certificates, test_server=False):
self.certs = certificates
if test_server:
@@ -76,7 +76,7 @@ def authenticate(self, personal_number, **kwargs):
out = self.client.service.Authenticate(
personalNumber=personal_number, **kwargs)
except WebFault as e:
- raise get_error_class(e)("Could not complete Authenticate order.")
+ raise get_error_class(e, "Could not complete Authenticate order.")
return self._dictify(out)
@@ -102,7 +102,7 @@ def sign(self, user_visible_data, personal_number=None, **kwargs):
userVisibleData=base64.b64encode(user_visible_data),
personalNumber=personal_number, **kwargs)
except WebFault as e:
- raise get_error_class(e)("Could not complete Sign order.")
+ raise get_error_class(e, "Could not complete Sign order.")
return self._dictify(out)
@@ -121,7 +121,7 @@ def collect(self, order_ref):
try:
out = self.client.service.Collect(orderRef=order_ref)
except WebFault as e:
- raise get_error_class(e)("Could not complete Collect call.")
+ raise get_error_class(e, "Could not complete Collect call.")
return self._dictify(out)
@@ -153,7 +153,7 @@ def _dictify(self, doc):
try:
for k in doc:
if isinstance(doc[k], Text):
- out[k] = unicode(doc[k])
+ out[k] = doc[k].decode('utf8')
elif isinstance(doc[k], datetime.datetime):
out[k] = doc[k]
else:
@@ -163,6 +163,7 @@ def _dictify(self, doc):
return out
+
class RequestsTransport(HttpAuthenticated):
"""A Requests-based transport for suds, enabling the use of https and
certificates when communicating with the SOAP service.
@@ -184,7 +185,7 @@ def open(self, request):
resp = self.requests_session.get(request.url,
data=request.message,
headers=request.headers)
- result = StringIO.StringIO(resp.content.decode('utf-8'))
+ result = six.BytesIO(six.b(resp.content.decode('utf-8')))
return result
def send(self, request):
diff --git a/bankid/exceptions.py b/bankid/exceptions.py
index b59d9c6..16ea57c 100644
--- a/bankid/exceptions.py
+++ b/bankid/exceptions.py
@@ -6,7 +6,7 @@
.. module:: exceptions
:platform: Unix, Windows
- :synopsis:
+ :synopsis:
.. moduleauthor:: hbldh
@@ -19,15 +19,15 @@
from __future__ import unicode_literals
from __future__ import absolute_import
-import re
+import six
-def get_error_class(exc):
- s = re.search("Server raised fault: '([\w_]+)'", unicode(exc))
- if s:
- return _ERROR_CODE_TO_CLASS[s.groups()[0]]
+def get_error_class(exc, exception_text):
+ error_class = _ERROR_CODE_TO_CLASS.get(six.text_type(exc.fault.faultstring))
+ if error_class is None:
+ return BankIDError("{0}: {1}".format(exc, exception_text))
else:
- raise exc
+ return error_class(exception_text)
class BankIDError(Exception):
diff --git a/bankid/testcert.py b/bankid/testcert.py
index 9408eb0..03ba9fd 100644
--- a/bankid/testcert.py
+++ b/bankid/testcert.py
@@ -26,7 +26,7 @@
import requests
_TEST_CERT_PASSWORD = 'qwerty123'
-_TEST_CERT_URL = "https://www.bankid.com/assets/bankid/rp/fptestcert1.P12"
+_TEST_CERT_URL = "https://www.bankid.com/assets/bankid/rp/FPTestcert2_20150818_102329.pfx"
def create_test_server_cert_and_key(destination_path):
@@ -63,10 +63,10 @@ def create_test_server_cert_and_key(destination_path):
def split_test_cert_and_key():
"""Fetch the P12 certificate from BankID servers, split it into
a certificate part and a key part and return the two components as text data.
-
+
:returns: Tuple of certificate and key string data.
:rtype: tuple
-
+
"""
# Paths to temporary files.
cert_tmp_path = os.path.join(tempfile.gettempdir(), os.path.basename(_TEST_CERT_URL))
@@ -114,6 +114,7 @@ def split_test_cert_and_key():
return certificate, key
+
def main():
paths = create_test_server_cert_and_key(os.path.expanduser('~'))
print('Saved certificate as {0}'.format(paths[0]))
diff --git a/requirements.txt b/requirements.txt
index 9b2af19..535e5ee 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,2 +1,3 @@
requests>=2.7.0
-suds==0.4
+suds-jurko>=0.6
+six>=1.9.0
diff --git a/tests/test_client.py b/tests/test_client.py
index e4b4ebe..df9b8b1 100644
--- a/tests/test_client.py
+++ b/tests/test_client.py
@@ -6,7 +6,7 @@
.. module:: test_client
:platform: Unix, Windows
- :synopsis:
+ :synopsis:
.. moduleauthor:: hbldh
@@ -24,8 +24,11 @@
import tempfile
import uuid
+from nose.tools import raises
+
import bankid.client
import bankid.testcert
+import bankid.exceptions
def get_random_personal_number():
@@ -37,7 +40,7 @@ def _luhn_digit(id_):
Code adapted from [Faker]
(https://github.com/joke2k/faker/blob/master/faker/providers/ssn/sv_SE/__init__.py)
- :param id_: The partial number to calcualte checksum of.
+ :param id_: The partial number to calculate checksum of.
:type id_: str
:return: Integer digit in [0, 9].
:rtype: int
@@ -62,21 +65,22 @@ def digits_of(n):
pn = "{0:04d}{1:02d}{2:02d}{3:03d}".format(year, month, day, suffix)
return pn + str(_luhn_digit(pn[2:]))
+
class TestClientOnTestServer(object):
"""Test Suite testing once against the actual BankID test server to
test that connection can be made with BankIDClient.
"""
def __init__(self):
- self.cert_file = None
+ self.certificate_file = None
self.key_file = None
- def setUp(self):
+ def setup(self):
certificate, key = bankid.testcert.create_test_server_cert_and_key(tempfile.gettempdir())
self.certificate_file = certificate
self.key_file = key
- def tearDown(self):
+ def teardown(self):
try:
os.remove(self.certificate_file)
os.remove(self.key_file)
@@ -85,11 +89,23 @@ def tearDown(self):
def test_authentication_and_collect(self):
"""Authenticate call and then collect with the returned orderRef UUID."""
-
+
c = bankid.client.BankIDClient(certificates=(self.certificate_file, self.key_file), test_server=True)
out = c.authenticate(get_random_personal_number())
assert isinstance(out, dict)
# UUID.__init__ performs the UUID compliance assertion.
order_ref = uuid.UUID(out.get('orderRef'), version=4)
- collect_status = c.collect(order_ref)
- assert collect_status.get('progressStatus') in ('OUTSTANDING_TRANSACTION', 'NO_CLIENT')
\ No newline at end of file
+ collect_status = c.collect(out.get('orderRef'))
+ assert collect_status.get('progressStatus') in ('OUTSTANDING_TRANSACTION', 'NO_CLIENT')
+
+ @raises(bankid.exceptions.InvalidParametersError)
+ def test_invalid_orderref_raises_error(self):
+ c = bankid.client.BankIDClient(certificates=(self.certificate_file, self.key_file), test_server=True)
+ collect_status = c.collect('invalid-uuid')
+
+ @raises(bankid.exceptions.AlreadyInProgressError)
+ def test_already_in_progress_raises_error(self):
+ c = bankid.client.BankIDClient(certificates=(self.certificate_file, self.key_file), test_server=True)
+ pn = get_random_personal_number()
+ out = c.authenticate(pn)
+ out2 = c.authenticate(pn)
\ No newline at end of file