diff --git a/.gitignore b/.gitignore index 31a3dfc8..60ea7449 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,7 @@ /eggs /build /dist + +example.cfg +*.crt +*.pem diff --git a/README.rst b/README.rst index a7ccdadb..eeaa084d 100644 --- a/README.rst +++ b/README.rst @@ -30,6 +30,7 @@ Python Requirements - python2.6 - python2.6-dev - lxml 2.3 or greater +- python-dateutil<2.0 (>2.0 for python 3.x) Test Requirements ================= @@ -57,8 +58,8 @@ To run the unit-tests:: Running the example app ======================= -To run with the default configuration file, example.cfg, which needs to be -filled out completely:: +To run with the default configuration, copy example.cfg.dist to example.cfg +and fill it out completely. Then run the example with:: python setup.py example diff --git a/example.cfg b/example.cfg.dist similarity index 100% rename from example.cfg rename to example.cfg.dist diff --git a/example.py b/example.py index f244bef1..09a886c6 100644 --- a/example.py +++ b/example.py @@ -57,7 +57,7 @@ def do_GET(self): self._bad_request() return - url = AuthnRequest.create(**self.settings) + url = AuthRequest.create(**self.settings) self.send_response(301) self.send_header("Location", url) self.end_headers() diff --git a/onelogin/saml/Response.py b/onelogin/saml/Response.py index 2bffc4e4..d7e531de 100644 --- a/onelogin/saml/Response.py +++ b/onelogin/saml/Response.py @@ -2,6 +2,8 @@ from lxml import etree from datetime import datetime, timedelta +import dateutil.tz +import dateutil.parser from onelogin.saml import SignatureVerifier @@ -58,7 +60,7 @@ def __init__( self._signature = signature def _parse_datetime(self, dt): - return datetime.strptime(dt, '%Y-%m-%dT%H:%M:%SZ') + return dateutil.parser.parse(dt) def _get_name_id(self): result = self._document.xpath( @@ -101,7 +103,7 @@ def is_valid( Return True if valid, otherwise False. """ if _clock is None: - _clock = datetime.utcnow + _clock = lambda: datetime.now(dateutil.tz.tzutc()) if _verifier is None: _verifier = SignatureVerifier.verify diff --git a/onelogin/saml/test/TestAuthRequest.py b/onelogin/saml/test/TestAuthRequest.py index a3ede75d..1a63e411 100644 --- a/onelogin/saml/test/TestAuthRequest.py +++ b/onelogin/saml/test/TestAuthRequest.py @@ -3,9 +3,9 @@ from datetime import datetime from nose.tools import eq_ as eq -from onelogin.saml import AuthnRequest +from onelogin.saml import AuthRequest -class TestAuthnRequest(object): +class TestAuthRequest(object): def setUp(self): fudge.clear_expectations() @@ -42,7 +42,7 @@ def fake_clock(): ) fake_urlencode.returns('foo_urlencoded') - req = AuthnRequest.create( + req = AuthRequest.create( _clock=fake_clock, _uuid=fake_uuid_func, _zlib=fake_zlib, diff --git a/onelogin/saml/test/TestResponse.py b/onelogin/saml/test/TestResponse.py index 52a3bbf4..13d2653f 100644 --- a/onelogin/saml/test/TestResponse.py +++ b/onelogin/saml/test/TestResponse.py @@ -3,6 +3,8 @@ import base64 import fudge +import dateutil.tz + from nose.tools import eq_ as eq from onelogin.saml.test.util import assert_raises @@ -310,12 +312,11 @@ def test_is_valid_not_before_missing(self): signature=None, ) msg = assert_raises( - ResponseConditionError, + ResponseValidationError, res.is_valid, ) - eq(str(msg), - ('There was a problem validating a condition: Did not find NotBefore ' + ('There was a problem validating the response: Current time is on or after NotOnOrAfter ' + 'condition' ), ) @@ -399,7 +400,7 @@ def test_is_valid_current_time_earlier(self): ) def fake_clock(): - return datetime(2004, 12, 05, 9, 16, 45, 462796) + return datetime(2004, 12, 05, 9, 16, 45, 462796,tzinfo=dateutil.tz.tzutc()) msg = assert_raises( ResponseValidationError, res.is_valid, @@ -421,7 +422,7 @@ def test_is_valid_current_time_on_or_after(self): ) def fake_clock(): - return datetime(2004, 12, 05, 9, 30, 45, 462796) + return datetime(2004, 12, 05, 9, 30, 45, 462796,tzinfo=dateutil.tz.tzutc()) msg = assert_raises( ResponseValidationError, res.is_valid, @@ -443,7 +444,34 @@ def test_is_valid_simple(self): ) def fake_clock(): - return datetime(2004, 12, 05, 9, 18, 45, 462796) + return datetime(2004, 12, 05, 9, 18, 45, 462796,tzinfo=dateutil.tz.tzutc()) + + fake_verifier = fudge.Fake( + 'verifier', + callable=True, + ) + fake_verifier.times_called(1) + fake_verifier.with_args(res._document, 'foo signature') + + fake_verifier.returns(True) + + msg = res.is_valid( + _clock=fake_clock, + _verifier=fake_verifier, + ) + + eq(msg, True) + + @fudge.with_fakes + def test_is_valid_timezonedifferent(self): + encoded_response = base64.b64encode(test_response) + res = Response( + response=encoded_response, + signature='foo signature', + ) + + def fake_clock(): + return datetime(2004, 12, 05, 4, 18, 45, 462796,tzinfo=dateutil.tz.tzstr('EST5EDT')) fake_verifier = fudge.Fake( 'verifier', @@ -460,3 +488,34 @@ def fake_clock(): ) eq(msg, True) + @fudge.with_fakes + def test_is_invalid_timezonedifferent(self): + encoded_response = base64.b64encode(test_response) + res = Response( + response=encoded_response, + signature='foo signature', + ) + + def fake_clock(): + return datetime(2004, 12, 05, 9, 18, 45, 462796,tzinfo=dateutil.tz.tzstr('EST5EDT')) + + fake_verifier = fudge.Fake( + 'verifier', + callable=True, + ) + fake_verifier.times_called(1) + fake_verifier.with_args(res._document, 'foo signature') + + fake_verifier.returns(True) + + msg = assert_raises( + ResponseValidationError, + res.is_valid, + _clock=fake_clock, + ) + + eq(str(msg), + ('There was a problem validating the response: Current time is ' + + 'on or after NotOnOrAfter condition' + ), + ) diff --git a/setup.py b/setup.py index bc02394d..6441d299 100644 --- a/setup.py +++ b/setup.py @@ -34,6 +34,7 @@ def run(self): install_requires = [ 'lxml>=2.3', + 'python-dateutil', ] tests_require = [ 'fudge >=0.9.5', @@ -42,7 +43,7 @@ def run(self): setup( name='onelogin.saml', - version='0.0.1', + version='0.0.2', description="Python client library for SAML Version 2.0", packages = find_packages(), namespace_packages = ['onelogin'],