Skip to content

Refactor datetime #518

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 23 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
d3346ed
Add saml2.datetime module
c00kiemon5ter Jul 4, 2018
b84fc0f
Update assertion to use the datetime module
c00kiemon5ter Jul 4, 2018
eb35f6b
Update authn to use the datetime module
c00kiemon5ter Jul 5, 2018
cbfbb1c
Update cache to use the datetime module
c00kiemon5ter Jul 4, 2018
c22811a
Update cert to use the datetime module
c00kiemon5ter Jul 4, 2018
4a69ad8
Update client to use the datetime module
c00kiemon5ter Jul 4, 2018
44e9912
Update client_base to use the datetime module
c00kiemon5ter Jul 5, 2018
45c5141
Update entity to use the datetime module
c00kiemon5ter Jul 4, 2018
8477c4c
Update httputil and httpbase to use the datetime module
c00kiemon5ter Jul 5, 2018
9dd3200
Update mcache to use the datetime module
c00kiemon5ter Jul 4, 2018
c903afc
Update mdbcache to use the datetime module
c00kiemon5ter Jul 4, 2018
b3f2f6d
Update mdstore to use the datetime module
c00kiemon5ter Jul 4, 2018
e113510
Update metadata to use the datetime module
c00kiemon5ter Jul 4, 2018
6159d9a
Update population to use the datetime module
c00kiemon5ter Jul 4, 2018
9e5f374
Update validate to use the datetime module
c00kiemon5ter Jul 5, 2018
79321d0
Make issue_instant_ok part of validate module
c00kiemon5ter Jul 5, 2018
7f9eea6
Update request to use the datetime module
c00kiemon5ter Jul 4, 2018
e46ad9f
Update response to use the datetime module
c00kiemon5ter Jul 4, 2018
922d21f
Update s_utils to use the datetime module
c00kiemon5ter Jul 4, 2018
4a0cee1
Update sigver to use the datetime module
c00kiemon5ter Jul 4, 2018
ce66189
Update tests
c00kiemon5ter Jul 4, 2018
9b9240e
Remove time_util module
c00kiemon5ter Jul 4, 2018
e6fb4a3
Update documentation
c00kiemon5ter Jul 4, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions docs/howto/config.rst
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,17 @@ If your computer and another computer that you are communicating with are not
in synch regarding the computer clock, then here you can state how big a
difference you are prepared to accept.

The value of accepted_time_diff can be:
- a number indicating the amount of time in seconds, or
- a dictionary with a single entry where the key is the time unit type and the
value is the amount.

example::

"accepted_time_diff": 300,
# or
"accepted_time_diff": {"minutes": 5},

.. note:: This will indiscriminately effect all time comparisons.
Hence your server my accept a statement that in fact is to old.

Expand Down
4 changes: 2 additions & 2 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -48,12 +48,12 @@ scripts =
tools/merge_metadata.py
tools/parse_xsd2.py
install_requires =
aniso8601
cryptography
defusedxml
enum34
future
pyOpenSSL
python-dateutil
pytz
requests >= 1.0.0
six

Expand Down
70 changes: 41 additions & 29 deletions src/saml2/assertion.py
Original file line number Diff line number Diff line change
@@ -1,22 +1,25 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import copy
import importlib
import logging
import re

import six

import saml2.datetime
import saml2.datetime.compute
import saml2.datetime.duration
import saml2.datetime.utils
from saml2 import saml
from saml2 import xmlenc
from saml2.attribute_converter import from_local, ac_factory
from saml2.attribute_converter import ac_factory
from saml2.attribute_converter import from_local
from saml2.attribute_converter import get_local_name
from saml2.s_utils import MissingValue
from saml2.s_utils import assertion_factory
from saml2.s_utils import factory
from saml2.s_utils import sid
from saml2.s_utils import MissingValue
from saml2.saml import NAME_FORMAT_URI
from saml2.time_util import instant
from saml2.time_util import in_a_while


logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -433,12 +436,14 @@ def get_name_form(self, sp_entity_id):
return self.get("name_form", sp_entity_id, NAME_FORMAT_URI)

def get_lifetime(self, sp_entity_id):
""" The lifetime of the assertion
"""
The lifetime of the assertion

:param sp_entity_id: The SP entity ID
:param: lifetime as a dictionary
"""
# default is a hour
return self.get("lifetime", sp_entity_id, {"hours": 1})
lifetime_default = {"hours": 1}
return self.get('lifetime', sp_entity_id, lifetime_default)

def get_attribute_restrictions(self, sp_entity_id):
""" Return the attribute restriction for SP that want the information
Expand Down Expand Up @@ -473,7 +478,6 @@ def entity_category_attributes(self, ec):

def get_entity_categories(self, sp_entity_id, mds, required):
"""

:param sp_entity_id:
:param mds: MetadataStore instance
:return: A dictionary with restrictions
Expand All @@ -485,14 +489,17 @@ def get_entity_categories(self, sp_entity_id, mds, required):
post_func=post_entity_categories, **kwargs)

def not_on_or_after(self, sp_entity_id):
""" When the assertion stops being valid, should not be
used after this time.
"""
When the assertion stops being valid, should not be used after this
time.

:param sp_entity_id: The SP entity ID
:return: String representation of the time
"""

return in_a_while(**self.get_lifetime(sp_entity_id))
lifetime = self.get_lifetime(sp_entity_id)
period = saml2.datetime.duration.parse(lifetime)
nooa = saml2.datetime.compute.add_to_now(period)
return saml2.datetime.to_string(nooa)

def filter(self, ava, sp_entity_id, mdstore, required=None, optional=None):
""" What attribute and attribute values returns depends on what
Expand Down Expand Up @@ -558,14 +565,17 @@ def conditions(self, sp_entity_id):
:param sp_entity_id: The SP entity ID
:return: A saml.Condition instance
"""
return factory(saml.Conditions,
not_before=instant(),
# How long might depend on who's getting it
not_on_or_after=self.not_on_or_after(sp_entity_id),
audience_restriction=[factory(
saml.AudienceRestriction,
audience=[factory(saml.Audience,
text=sp_entity_id)])])
instant = saml2.datetime.utils.instant()
audience = [factory(saml.Audience, text=sp_entity_id)]
audience_restriction = [
factory(saml.AudienceRestriction, audience=audience)
]
return factory(
saml.Conditions,
not_before=instant,
# How long might depend on who's getting it
not_on_or_after=self.not_on_or_after(sp_entity_id),
audience_restriction=audience_restriction)

def get_sign(self, sp_entity_id):
"""
Expand Down Expand Up @@ -644,9 +654,10 @@ def authn_statement(authn_class=None, authn_auth=None,
:return: An AuthnContext instance
"""
if authn_instant:
_instant = instant(time_stamp=authn_instant)
_date_time_instant = saml2.datetime.fromtimestamp(authn_instant)
_instant = saml2.datetime.to_string(_date_time_instant)
else:
_instant = instant()
_instant = saml2.datetime.utils.instant()

if authn_class:
res = factory(
Expand Down Expand Up @@ -785,11 +796,12 @@ def construct(self, sp_entity_id, attrconvs, policy, issuer, farg,
if authn_statem:
_authn_statement = authn_statem
elif authn_auth or authn_class or authn_decl or authn_decl_ref:
_authn_statement = authn_statement(authn_class, authn_auth,
authn_decl, authn_decl_ref,
authn_instant,
subject_locality,
session_not_on_or_after=session_not_on_or_after)
_authn_statement = authn_statement(
authn_class, authn_auth,
authn_decl, authn_decl_ref,
authn_instant,
subject_locality,
session_not_on_or_after=session_not_on_or_after)
else:
_authn_statement = None

Expand Down
16 changes: 9 additions & 7 deletions src/saml2/authn.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
import logging

import six
import time
from six.moves.urllib.parse import parse_qs
from six.moves.urllib.parse import urlencode
from six.moves.urllib.parse import urlsplit

import saml2.datetime.utils
from saml2 import SAMLError
from saml2.aes import AESCipher
from saml2.httputil import Response
from saml2.httputil import make_cookie
from saml2.httputil import Redirect
from saml2.httputil import Response
from saml2.httputil import Unauthorized
from saml2.httputil import make_cookie
from saml2.httputil import parse_cookie

from six.moves.urllib.parse import urlencode, parse_qs, urlsplit

__author__ = 'rolandh'

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -170,7 +172,7 @@ def verify(self, request, **kwargs):
# verify username and password
try:
self._verify(_dict["password"][0], _dict["login"][0])
timestamp = str(int(time.mktime(time.gmtime())))
timestamp = saml2.datetime.utils.instant()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These kind of changes makes me happy. 👍

msg = "::".join([_dict["login"][0], timestamp])
info = self.aes.encrypt(msg.encode())
self.active[info] = timestamp
Expand Down
29 changes: 19 additions & 10 deletions src/saml2/cache.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
#!/usr/bin/env python

import logging
import shelve

import six
from saml2.ident import code, decode
from saml2 import time_util, SAMLError
import logging

import saml2.datetime
import saml2.datetime.compare
from saml2 import SAMLError
from saml2.ident import code
from saml2.ident import decode


logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -43,8 +47,7 @@ def delete(self, name_id):
except AttributeError:
pass

def get_identity(self, name_id, entities=None,
check_not_on_or_after=True):
def get_identity(self, name_id, entities=None, check_not_on_or_after=True):
""" Get all the identity information that has been received and
are still valid about the subject.

Expand Down Expand Up @@ -97,8 +100,12 @@ def get(self, name_id, entity_id, check_not_on_or_after=True):
cni = code(name_id)
(timestamp, info) = self._db[cni][entity_id]
info = info.copy()
if check_not_on_or_after and time_util.after(timestamp):
raise ToOld("past %s" % str(timestamp))

if check_not_on_or_after:
date_time = saml2.datetime.fromtimestamp(timestamp)
is_valid = saml2.datetime.compare.after_now(date_time)
if not is_valid:
raise ToOld("past %s" % str(timestamp))

if 'name_id' in info and isinstance(info['name_id'], six.string_types):
info['name_id'] = decode(info['name_id'])
Expand Down Expand Up @@ -172,7 +179,9 @@ def active(self, name_id, entity_id):
if not info:
return False
else:
return time_util.not_on_or_after(timestamp)
date_time = saml2.datetime.fromtimestamp(timestamp)
is_valid = saml2.datetime.compare.after_now(date_time)
return is_valid

def subjects(self):
""" Return identifiers for all the subjects that are in the cache.
Expand Down
39 changes: 21 additions & 18 deletions src/saml2/cert.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,20 @@
__author__ = 'haho0032'

import base64
import datetime
import dateutil.parser
import pytz
import six
import os

from OpenSSL import crypto
from os.path import join
from os import remove

from cryptography.hazmat.backends import default_backend
from cryptography.x509 import load_pem_x509_certificate

import six

import saml2.datetime.asn1
import saml2.datetime.compare


backend = default_backend()


class WrongInput(Exception):
pass

Expand Down Expand Up @@ -113,15 +114,15 @@ def create_certificate(self, cert_info, request=False, valid_from=0,
cert_file = "%s.crt" % cn
key_file = "%s.key" % cn
try:
remove(cert_file)
os.remove(cert_file)
except:
pass
try:
remove(key_file)
os.remove(key_file)
except:
pass
c_f = join(cert_dir, cert_file)
k_f = join(cert_dir, key_file)
c_f = os.path.join(cert_dir, cert_file)
k_f = os.path.join(cert_dir, key_file)


# create a key pair
Expand Down Expand Up @@ -289,12 +290,14 @@ def verify_chain(self, cert_chain_str_list, cert_str):
"certificate.")

def certificate_not_valid_yet(self, cert):
starts_to_be_valid = dateutil.parser.parse(cert.get_notBefore())
now = pytz.UTC.localize(datetime.datetime.utcnow())
if starts_to_be_valid < now:
return False
return True

not_before = cert.get_notBefore()
if not not_before:
return True

not_before = not_before.decode()
starts_to_be_valid = saml2.datetime.asn1.parse(not_before)
is_valid = saml2.datetime.compare.before_now(starts_to_be_valid)
return not is_valid

def verify(self, signing_cert_str, cert_str):
"""
Expand Down
Loading