lib: move SAML timestring utils into lib
This commit is contained in:
parent
05c3393669
commit
37b2400cdb
|
@ -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)
|
|
@ -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
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
|
|
|
@ -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()
|
||||||
|
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -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"""
|
||||||
|
|
|
@ -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",
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
Reference in New Issue