lib: move SAML timestring utils into lib

This commit is contained in:
Jens Langhammer 2020-07-20 11:35:16 +02:00
parent 05c3393669
commit 37b2400cdb
9 changed files with 50 additions and 58 deletions

View File

@ -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)

View File

@ -3,7 +3,7 @@
import django.db.models.deletion import django.db.models.deletion
from django.db import migrations, models from django.db import migrations, models
import passbook.providers.saml.utils.time import passbook.lib.utils.time
class Migration(migrations.Migration): class Migration(migrations.Migration):
@ -66,9 +66,7 @@ class Migration(migrations.Migration):
models.TextField( models.TextField(
default="minutes=-5", default="minutes=-5",
help_text="Assertion valid not before current time + this value (Format: hours=-1;minutes=-2;seconds=-3).", help_text="Assertion valid not before current time + this value (Format: hours=-1;minutes=-2;seconds=-3).",
validators=[ validators=[passbook.lib.utils.time.timedelta_string_validator],
passbook.providers.saml.utils.time.timedelta_string_validator
],
), ),
), ),
( (
@ -76,9 +74,7 @@ class Migration(migrations.Migration):
models.TextField( models.TextField(
default="minutes=5", default="minutes=5",
help_text="Assertion not valid on or after current time + this value (Format: hours=1;minutes=2;seconds=3).", help_text="Assertion not valid on or after current time + this value (Format: hours=1;minutes=2;seconds=3).",
validators=[ validators=[passbook.lib.utils.time.timedelta_string_validator],
passbook.providers.saml.utils.time.timedelta_string_validator
],
), ),
), ),
( (
@ -86,9 +82,7 @@ class Migration(migrations.Migration):
models.TextField( models.TextField(
default="minutes=86400", default="minutes=86400",
help_text="Session not valid on or after current time + this value (Format: hours=1;minutes=2;seconds=3).", help_text="Session not valid on or after current time + this value (Format: hours=1;minutes=2;seconds=3).",
validators=[ validators=[passbook.lib.utils.time.timedelta_string_validator],
passbook.providers.saml.utils.time.timedelta_string_validator
],
), ),
), ),
( (

View File

@ -10,7 +10,7 @@ from structlog import get_logger
from passbook.core.models import PropertyMapping, Provider from passbook.core.models import PropertyMapping, Provider
from passbook.crypto.models import CertificateKeyPair from passbook.crypto.models import CertificateKeyPair
from passbook.lib.utils.template import render_to_string 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() LOGGER = get_logger()

View File

@ -9,10 +9,11 @@ from signxml import XMLSigner, XMLVerifier
from structlog import get_logger from structlog import get_logger
from passbook.core.exceptions import PropertyMappingExpressionException 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.models import SAMLPropertyMapping, SAMLProvider
from passbook.providers.saml.processors.request_parser import AuthNRequest from passbook.providers.saml.processors.request_parser import AuthNRequest
from passbook.providers.saml.utils import get_random_id 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.exceptions import UnsupportedNameIDFormat
from passbook.sources.saml.processors.constants import ( from passbook.sources.saml.processors.constants import (
NS_MAP, NS_MAP,

View File

@ -4,10 +4,7 @@ from datetime import timedelta
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from django.test import TestCase from django.test import TestCase
from passbook.providers.saml.utils.time import ( from passbook.lib.utils.time import timedelta_from_string, timedelta_string_validator
timedelta_from_string,
timedelta_string_validator,
)
class TestTimeUtils(TestCase): class TestTimeUtils(TestCase):

View File

@ -2,42 +2,6 @@
import datetime import datetime
from typing import Optional 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: def get_time_string(delta: Optional[datetime.timedelta] = None) -> str:
"""Get Data formatted in SAML format""" """Get Data formatted in SAML format"""

View File

@ -3,7 +3,7 @@
import django.db.models.deletion import django.db.models.deletion
from django.db import migrations, models from django.db import migrations, models
import passbook.providers.saml.utils.time import passbook.lib.utils.time
class Migration(migrations.Migration): class Migration(migrations.Migration):
@ -27,9 +27,7 @@ class Migration(migrations.Migration):
field=models.TextField( field=models.TextField(
default="days=1", 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).", 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=[ validators=[passbook.lib.utils.time.timedelta_string_validator],
passbook.providers.saml.utils.time.timedelta_string_validator
],
verbose_name="Delete temporary users after", verbose_name="Delete temporary users after",
), ),
), ),

View File

@ -8,7 +8,7 @@ from django.utils.translation import gettext_lazy as _
from passbook.core.models import Source from passbook.core.models import Source
from passbook.core.types import UILoginButton from passbook.core.types import UILoginButton
from passbook.crypto.models import CertificateKeyPair 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 ( from passbook.sources.saml.processors.constants import (
SAML_NAME_ID_FORMAT_EMAIL, SAML_NAME_ID_FORMAT_EMAIL,
SAML_NAME_ID_FORMAT_PERSISTENT, SAML_NAME_ID_FORMAT_PERSISTENT,

View File

@ -3,7 +3,7 @@ from django.utils.timezone import now
from structlog import get_logger from structlog import get_logger
from passbook.core.models import User 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.root.celery import CELERY_APP
from passbook.sources.saml.models import SAMLSource from passbook.sources.saml.models import SAMLSource