Skip to content

Commit

Permalink
Version 0.1.4
Browse files Browse the repository at this point in the history
Modified transport to use own Requests Session object and thereby storing SSL key passwords.
Wrote one test against BankID test server and added Travis CI building.
Updated README with testing info.
Problems in Python 2.6. Removing from testing for time being.
Fetching test certificate now removes password for the key.
  • Loading branch information
hbldh committed Aug 7, 2015
1 parent 15ef756 commit 8a83fe4
Show file tree
Hide file tree
Showing 12 changed files with 339 additions and 120 deletions.
132 changes: 123 additions & 9 deletions .gitignore
@@ -1,12 +1,126 @@
*.pyc
*~
# Created by .ignore support plugin (hsz.mobi)

### Python template
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class

# C extensions
*.so
.idea

# Distribution / packaging
.Python
env/
build/
_build/
_static/
_templates/
.vagrant
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
*.egg-info/
.installed.cfg
*.egg

# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec

# Installer logs
pip-log.txt
pip-delete-this-directory.txt

# Unit test / coverage reports
htmlcov/
.tox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*,cover

# Translations
*.mo
*.pot

# Django stuff:
*.log

# Sphinx documentation
docs/_build/

# PyBuilder
target/


### Vagrant template
.vagrant/


### Linux template
*~

# KDE directory preferences
.directory

# Linux trash folder which might appear on any partition or disk
.Trash-*


### JetBrains template
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion

*.iml

## Directory-based project format:
.idea/
# if you remove the above rule, at least ignore the following:

# User-specific stuff:
# .idea/workspace.xml
# .idea/tasks.xml
# .idea/dictionaries

# Sensitive or high-churn files:
# .idea/dataSources.ids
# .idea/dataSources.xml
# .idea/sqlDataSources.xml
# .idea/dynamic.xml
# .idea/uiDesigner.xml

# Gradle:
# .idea/gradle.xml
# .idea/libraries

# Mongo Explorer plugin:
# .idea/mongoSettings.xml

## File-based project format:
*.ipr
*.iws

## Plugin-specific files:

# IntelliJ
/out/

# mpeltonen/sbt-idea plugin
.idea_modules/

# JIRA plugin
atlassian-ide-plugin.xml

# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties


cert.pem
key.pem
11 changes: 11 additions & 0 deletions .travis.yml
@@ -0,0 +1,11 @@
language: python
sudo: false
python:
- 2.7
branches:
only:
- master
- develop
install:
- "pip install -r requirements.txt"
script: nosetests tests --logging-level=INFO
21 changes: 20 additions & 1 deletion README.rst
@@ -1,6 +1,9 @@
PyBankID
========

.. image:: https://travis-ci.org/hbldh/pybankid.svg?branch=master
:target: https://travis-ci.org/hbldh/pybankid

PyBankID is a client for performing BankID signing.

The Swedish BankID solution for digital signing uses a SOAP
Expand All @@ -9,7 +12,7 @@ client for making authentication, signing and collect requests to
the BankID servers.

For more details about BankID implementation, see the `official documentation
<http://www.bankid.com/rp/info/>`_. There, one can find information
<https://www.bankid.com/bankid_i_dina_tjanster/rp_info>`_. There, one can find information
about how the BankID methods are defined, how to set up the test environment
and obtain the SSL certificate for the test server.

Expand Down Expand Up @@ -74,6 +77,22 @@ with the ``collect`` method:
The ``collect`` should be used sparingly, as not to burden the server unnecessarily.

Testing
-------

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
<https://fedorahosted.org/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.

Documentation
-------------

Expand Down
10 changes: 5 additions & 5 deletions bankid/__init__.py
Expand Up @@ -11,22 +11,22 @@
# version.
_version_major = 0
_version_minor = 1
_version_patch = 3
#_version_extra = 'dev'
#_version_extra = 'a'
_version_patch = 4
#_version_extra = 'dev4'
#_version_extra = 'b1'
_version_extra = '' # Uncomment this for full releases

# Construct full version string from these.
_ver = [_version_major, _version_minor, _version_patch]

__version__ = '.'.join(map(str, _ver))
if _version_extra:
__version__ = __version__ + '-' + str(_version_extra)
__version__ = __version__ + '.' + str(_version_extra)

version = __version__ # backwards compatibility name
version_info = (_version_major, _version_minor, _version_patch, _version_extra)

description = "BankID client in Python"
description = "BankID client for Python"

long_description = \
"""PyBankID is a client for performing BankID signing.
Expand Down
46 changes: 17 additions & 29 deletions bankid/client.py
Expand Up @@ -19,7 +19,6 @@
from __future__ import unicode_literals
from __future__ import absolute_import

import os
import warnings
import StringIO
import base64
Expand All @@ -33,33 +32,27 @@
from suds.sax.text import Text
from pkg_resources import resource_filename

from bankid.exceptions import get_error_class
from bankid.warnings import BankIDWarning

_CERT_PATH = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
_CERTS = (os.path.join(_CERT_PATH, 'cert.pem'),
os.path.join(_CERT_PATH, 'key.pem'))
from bankid.exceptions import get_error_class, BankIDWarning


class BankIDClient(object):

def __init__(self, certificates, test_server=True):
self.certs = certificates


if test_server:
self.api_url = 'https://appapi.test.bankid.com/rp/v4'
self.wsdl_url = 'https://appapi.test.bankid.com/rp/v4?wsdl'
self.verify_cert = resource_filename('bankid.certs','appapi.test.bankid.com.pem')
self.verify_cert = resource_filename('bankid.certs', 'appapi.test.bankid.com.pem')
else:
self.api_url = 'https://appapi.bankid.com/rp/v4'
self.wsdl_url = 'https://appapi.bankid.com/rp/v4?wsdl'
self.verify_cert = resource_filename('bankid.certs','appapi.bankid.com.pem')

self.verify_cert = resource_filename('bankid.certs', 'appapi.bankid.com.pem')


headers = {"Content-Type": "text/xml;charset=UTF-8",
"SOAPAction": ""}
headers = {
"Content-Type": "text/xml;charset=UTF-8",
"SOAPAction": ""
}
t = RequestsTransport(cert=self.certs, verify_cert=self.verify_cert)
self.client = Client(self.wsdl_url, location=self.api_url,
headers=headers, transport=t)
Expand Down Expand Up @@ -140,10 +133,6 @@ def file_sign(self, **kwargs):
Not implemented due to that the method is deprecated.
:param kwargs:
:type kwargs:
:return:
:rtype:
"""
raise NotImplementedError(
"FileSign is deprecated and therefore not implemented.")
Expand Down Expand Up @@ -174,7 +163,6 @@ 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.
Expand All @@ -184,26 +172,26 @@ class RequestsTransport(HttpAuthenticated):
"""
def __init__(self, **kwargs):
self.cert = kwargs.pop('cert', None)
self.verify_cert = kwargs.pop('verify_cert', None)
# super won't work because HttpAuthenticated
# does not use new style class
self.requests_session = requests.Session()
self.requests_session.cert = kwargs.pop('cert', None)
self.requests_session.verify = kwargs.pop('verify_cert', None)
# `super` won't work because HttpAuthenticated does not use new style class.
HttpAuthenticated.__init__(self, **kwargs)

def open(self, request):
"""Fetches the WSDL specification using certificates."""
self.addcredentials(request)
resp = requests.get(request.url, data=request.message,
headers=request.headers,
cert=self.cert, verify=self.verify_cert)
resp = self.requests_session.get(request.url,
data=request.message,
headers=request.headers)
result = StringIO.StringIO(resp.content.decode('utf-8'))
return result

def send(self, request):
"""Posts to SOAP service using certificates."""
self.addcredentials(request)
resp = requests.post(request.url, data=request.message,
headers=request.headers,
cert=self.cert, verify=self.verify_cert)
resp = self.requests_session.post(request.url,
data=request.message,
headers=request.headers)
result = Reply(resp.status_code, resp.headers, resp.content)
return result
9 changes: 7 additions & 2 deletions bankid/exceptions.py
Expand Up @@ -31,13 +31,18 @@ def get_error_class(exc):


class BankIDError(Exception):
"""Parent error for all PyBankID errors."""
"""Parent exception class for all PyBankID errors."""

def __init__(self, *args, **kwargs):
super(BankIDError, self).__init__(*args, **kwargs)
self.rfa = None


class BankIDWarning(Warning):
"""Warning class for PyBankID."""
pass


class InvalidParametersError(BankIDError):
"""User induced error.
Expand Down Expand Up @@ -217,6 +222,7 @@ def __init__(self, *args, **kwargs):
# TODO: Dual cause, in which only one requires RFA. Remove?
self.rfa = 17


_ERROR_CODE_TO_CLASS = {
'INVALID_PARAMETERS': InvalidParametersError,
'ALREADY_IN_PROGRESS': AlreadyInProgressError,
Expand All @@ -230,4 +236,3 @@ def __init__(self, *args, **kwargs):
'CANCELLED': CancelledError,
'START_FAILED': StartFailedError,
}

0 comments on commit 8a83fe4

Please sign in to comment.