From 37b2400cdb0e0c52863c58077684ada0bf7cc8ce Mon Sep 17 00:00:00 2001 From: Jens Langhammer Date: Mon, 20 Jul 2020 11:35:16 +0200 Subject: [PATCH] lib: move SAML timestring utils into lib --- passbook/lib/utils/time.py | 38 +++++++++++++++++++ .../providers/saml/migrations/0001_initial.py | 14 ++----- passbook/providers/saml/models.py | 2 +- .../providers/saml/processors/assertion.py | 3 +- .../providers/saml/tests/test_utils_time.py | 5 +-- passbook/providers/saml/utils/time.py | 36 ------------------ .../migrations/0003_auto_20200624_1957.py | 6 +-- passbook/sources/saml/models.py | 2 +- passbook/sources/saml/tasks.py | 2 +- 9 files changed, 50 insertions(+), 58 deletions(-) create mode 100644 passbook/lib/utils/time.py diff --git a/passbook/lib/utils/time.py b/passbook/lib/utils/time.py new file mode 100644 index 000000000..4ba41ddb5 --- /dev/null +++ b/passbook/lib/utils/time.py @@ -0,0 +1,38 @@ +"""Time utilities""" +import datetime + +from django.core.exceptions import ValidationError +from django.utils.translation import gettext_lazy as _ + +ALLOWED_KEYS = ( + "days", + "seconds", + "microseconds", + "milliseconds", + "minutes", + "hours", + "weeks", +) + + +def timedelta_string_validator(value: str): + """Validator for Django that checks if value can be parsed with `timedelta_from_string`""" + try: + timedelta_from_string(value) + except ValueError as exc: + raise ValidationError( + _("%(value)s is not in the correct format of 'hours=3;minutes=1'."), + params={"value": value}, + ) from exc + + +def timedelta_from_string(expr: str) -> datetime.timedelta: + """Convert a string with the format of 'hours=1;minute=3;seconds=5' to a + `datetime.timedelta` Object with hours = 1, minutes = 3, seconds = 5""" + kwargs = {} + for duration_pair in expr.split(";"): + key, value = duration_pair.split("=") + if key.lower() not in ALLOWED_KEYS: + continue + kwargs[key.lower()] = float(value) + return datetime.timedelta(**kwargs) diff --git a/passbook/providers/saml/migrations/0001_initial.py b/passbook/providers/saml/migrations/0001_initial.py index 22ca56c21..c50faae78 100644 --- a/passbook/providers/saml/migrations/0001_initial.py +++ b/passbook/providers/saml/migrations/0001_initial.py @@ -3,7 +3,7 @@ import django.db.models.deletion from django.db import migrations, models -import passbook.providers.saml.utils.time +import passbook.lib.utils.time class Migration(migrations.Migration): @@ -66,9 +66,7 @@ class Migration(migrations.Migration): models.TextField( default="minutes=-5", help_text="Assertion valid not before current time + this value (Format: hours=-1;minutes=-2;seconds=-3).", - validators=[ - passbook.providers.saml.utils.time.timedelta_string_validator - ], + validators=[passbook.lib.utils.time.timedelta_string_validator], ), ), ( @@ -76,9 +74,7 @@ class Migration(migrations.Migration): models.TextField( default="minutes=5", help_text="Assertion not valid on or after current time + this value (Format: hours=1;minutes=2;seconds=3).", - validators=[ - passbook.providers.saml.utils.time.timedelta_string_validator - ], + validators=[passbook.lib.utils.time.timedelta_string_validator], ), ), ( @@ -86,9 +82,7 @@ class Migration(migrations.Migration): models.TextField( default="minutes=86400", help_text="Session not valid on or after current time + this value (Format: hours=1;minutes=2;seconds=3).", - validators=[ - passbook.providers.saml.utils.time.timedelta_string_validator - ], + validators=[passbook.lib.utils.time.timedelta_string_validator], ), ), ( diff --git a/passbook/providers/saml/models.py b/passbook/providers/saml/models.py index 56b6a8c74..6e2e02162 100644 --- a/passbook/providers/saml/models.py +++ b/passbook/providers/saml/models.py @@ -10,7 +10,7 @@ from structlog import get_logger from passbook.core.models import PropertyMapping, Provider from passbook.crypto.models import CertificateKeyPair from passbook.lib.utils.template import render_to_string -from passbook.providers.saml.utils.time import timedelta_string_validator +from passbook.lib.utils.time import timedelta_string_validator LOGGER = get_logger() diff --git a/passbook/providers/saml/processors/assertion.py b/passbook/providers/saml/processors/assertion.py index a36aa7d68..014817a88 100644 --- a/passbook/providers/saml/processors/assertion.py +++ b/passbook/providers/saml/processors/assertion.py @@ -9,10 +9,11 @@ from signxml import XMLSigner, XMLVerifier from structlog import get_logger from passbook.core.exceptions import PropertyMappingExpressionException +from passbook.lib.utils.time import timedelta_from_string from passbook.providers.saml.models import SAMLPropertyMapping, SAMLProvider from passbook.providers.saml.processors.request_parser import AuthNRequest from passbook.providers.saml.utils import get_random_id -from passbook.providers.saml.utils.time import get_time_string, timedelta_from_string +from passbook.providers.saml.utils.time import get_time_string from passbook.sources.saml.exceptions import UnsupportedNameIDFormat from passbook.sources.saml.processors.constants import ( NS_MAP, diff --git a/passbook/providers/saml/tests/test_utils_time.py b/passbook/providers/saml/tests/test_utils_time.py index 7a35ed8f9..a596481df 100644 --- a/passbook/providers/saml/tests/test_utils_time.py +++ b/passbook/providers/saml/tests/test_utils_time.py @@ -4,10 +4,7 @@ from datetime import timedelta from django.core.exceptions import ValidationError from django.test import TestCase -from passbook.providers.saml.utils.time import ( - timedelta_from_string, - timedelta_string_validator, -) +from passbook.lib.utils.time import timedelta_from_string, timedelta_string_validator class TestTimeUtils(TestCase): diff --git a/passbook/providers/saml/utils/time.py b/passbook/providers/saml/utils/time.py index 2fe490ba9..c807315a2 100644 --- a/passbook/providers/saml/utils/time.py +++ b/passbook/providers/saml/utils/time.py @@ -2,42 +2,6 @@ import datetime from typing import Optional -from django.core.exceptions import ValidationError -from django.utils.translation import gettext_lazy as _ - -ALLOWED_KEYS = ( - "days", - "seconds", - "microseconds", - "milliseconds", - "minutes", - "hours", - "weeks", -) - - -def timedelta_string_validator(value: str): - """Validator for Django that checks if value can be parsed with `timedelta_from_string`""" - try: - timedelta_from_string(value) - except ValueError as exc: - raise ValidationError( - _("%(value)s is not in the correct format of 'hours=3;minutes=1'."), - params={"value": value}, - ) from exc - - -def timedelta_from_string(expr: str) -> datetime.timedelta: - """Convert a string with the format of 'hours=1;minute=3;seconds=5' to a - `datetime.timedelta` Object with hours = 1, minutes = 3, seconds = 5""" - kwargs = {} - for duration_pair in expr.split(";"): - key, value = duration_pair.split("=") - if key.lower() not in ALLOWED_KEYS: - continue - kwargs[key.lower()] = float(value) - return datetime.timedelta(**kwargs) - def get_time_string(delta: Optional[datetime.timedelta] = None) -> str: """Get Data formatted in SAML format""" diff --git a/passbook/sources/saml/migrations/0003_auto_20200624_1957.py b/passbook/sources/saml/migrations/0003_auto_20200624_1957.py index 8164b5d6a..9f9f0fc90 100644 --- a/passbook/sources/saml/migrations/0003_auto_20200624_1957.py +++ b/passbook/sources/saml/migrations/0003_auto_20200624_1957.py @@ -3,7 +3,7 @@ import django.db.models.deletion from django.db import migrations, models -import passbook.providers.saml.utils.time +import passbook.lib.utils.time class Migration(migrations.Migration): @@ -27,9 +27,7 @@ class Migration(migrations.Migration): field=models.TextField( default="days=1", help_text="Time offset when temporary users should be deleted. This only applies if your IDP uses the NameID Format 'transient', and the user doesn't log out manually. (Format: hours=1;minutes=2;seconds=3).", - validators=[ - passbook.providers.saml.utils.time.timedelta_string_validator - ], + validators=[passbook.lib.utils.time.timedelta_string_validator], verbose_name="Delete temporary users after", ), ), diff --git a/passbook/sources/saml/models.py b/passbook/sources/saml/models.py index 33e1b3da9..975629b70 100644 --- a/passbook/sources/saml/models.py +++ b/passbook/sources/saml/models.py @@ -8,7 +8,7 @@ from django.utils.translation import gettext_lazy as _ from passbook.core.models import Source from passbook.core.types import UILoginButton from passbook.crypto.models import CertificateKeyPair -from passbook.providers.saml.utils.time import timedelta_string_validator +from passbook.lib.utils.time import timedelta_string_validator from passbook.sources.saml.processors.constants import ( SAML_NAME_ID_FORMAT_EMAIL, SAML_NAME_ID_FORMAT_PERSISTENT, diff --git a/passbook/sources/saml/tasks.py b/passbook/sources/saml/tasks.py index caad70dab..dde27b87d 100644 --- a/passbook/sources/saml/tasks.py +++ b/passbook/sources/saml/tasks.py @@ -3,7 +3,7 @@ from django.utils.timezone import now from structlog import get_logger from passbook.core.models import User -from passbook.providers.saml.utils.time import timedelta_from_string +from passbook.lib.utils.time import timedelta_from_string from passbook.root.celery import CELERY_APP from passbook.sources.saml.models import SAMLSource