stages/email: token_expiry format (#13394)
* Change token_expiry type from integer to text in Email Stage to unify with timedelta_string_validator * Add migration file for token_expiry format, change from number to text field in the UI * Fix token_expiry new format in stage.py in Email Stage * fix linting * Update web/src/admin/stages/email/EmailStageForm.ts Co-authored-by: Marc 'risson' Schmitt <marc.schmitt@risson.space> Signed-off-by: Marcelo Elizeche Landó <marce@melizeche.com> * Use db_alias and using() for the queries * Make valid_delta more readable * use <ak-utils-time-delta-help> in the UI * fix missing import Signed-off-by: Jens Langhammer <jens@goauthentik.io> --------- Signed-off-by: Marcelo Elizeche Landó <marce@melizeche.com> Signed-off-by: Jens Langhammer <jens@goauthentik.io> Co-authored-by: Marc 'risson' Schmitt <marc.schmitt@risson.space> Co-authored-by: Jens Langhammer <jens@goauthentik.io>
This commit is contained in:

committed by
GitHub

parent
f185a41813
commit
b0671e26c8
@ -0,0 +1,54 @@
|
||||
# Generated by Django 5.0.12 on 2025-02-27 04:32
|
||||
|
||||
import authentik.lib.utils.time
|
||||
from authentik.lib.utils.time import timedelta_from_string
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
def convert_integer_to_string_format(apps, schema_editor):
|
||||
db_alias = schema_editor.connection.alias
|
||||
EmailStage = apps.get_model("authentik_stages_email", "EmailStage")
|
||||
for stage in EmailStage.objects.using(db_alias).all():
|
||||
stage.token_expiry = f"minutes={stage.token_expiry}"
|
||||
stage.save(using=db_alias)
|
||||
|
||||
|
||||
def convert_string_to_integer_format(apps, schema_editor):
|
||||
db_alias = schema_editor.connection.alias
|
||||
EmailStage = apps.get_model("authentik_stages_email", "EmailStage")
|
||||
for stage in EmailStage.objects.using(db_alias).all():
|
||||
# Check if token_expiry is a string
|
||||
if isinstance(stage.token_expiry, str):
|
||||
try:
|
||||
# Use the timedelta_from_string utility to convert to timedelta
|
||||
# then convert to minutes by dividing seconds by 60
|
||||
td = timedelta_from_string(stage.token_expiry)
|
||||
minutes_value = int(td.total_seconds() / 60)
|
||||
stage.token_expiry = minutes_value
|
||||
stage.save(using=db_alias)
|
||||
except (ValueError, TypeError):
|
||||
# If the string can't be parsed or converted properly, skip
|
||||
pass
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("authentik_stages_email", "0004_emailstage_activate_user_on_success"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name="emailstage",
|
||||
name="token_expiry",
|
||||
field=models.TextField(
|
||||
default="minutes=30",
|
||||
help_text="Time the token sent is valid (Format: hours=3,minutes=17,seconds=300).",
|
||||
validators=[authentik.lib.utils.time.timedelta_string_validator],
|
||||
),
|
||||
),
|
||||
migrations.RunPython(
|
||||
convert_integer_to_string_format,
|
||||
convert_string_to_integer_format,
|
||||
),
|
||||
]
|
@ -14,6 +14,7 @@ from structlog.stdlib import get_logger
|
||||
|
||||
from authentik.flows.models import Stage
|
||||
from authentik.lib.config import CONFIG
|
||||
from authentik.lib.utils.time import timedelta_string_validator
|
||||
|
||||
LOGGER = get_logger()
|
||||
|
||||
@ -74,8 +75,10 @@ class EmailStage(Stage):
|
||||
default=False, help_text=_("Activate users upon completion of stage.")
|
||||
)
|
||||
|
||||
token_expiry = models.IntegerField(
|
||||
default=30, help_text=_("Time in minutes the token sent is valid.")
|
||||
token_expiry = models.TextField(
|
||||
default="minutes=30",
|
||||
validators=[timedelta_string_validator],
|
||||
help_text=_("Time the token sent is valid (Format: hours=3,minutes=17,seconds=300)."),
|
||||
)
|
||||
subject = models.TextField(default="authentik")
|
||||
template = models.TextField(default=EmailTemplates.PASSWORD_RESET)
|
||||
|
@ -22,6 +22,7 @@ from authentik.flows.planner import PLAN_CONTEXT_IS_RESTORED, PLAN_CONTEXT_PENDI
|
||||
from authentik.flows.stage import ChallengeStageView
|
||||
from authentik.flows.views.executor import QS_KEY_TOKEN, QS_QUERY
|
||||
from authentik.lib.utils.errors import exception_to_string
|
||||
from authentik.lib.utils.time import timedelta_from_string
|
||||
from authentik.stages.email.models import EmailStage
|
||||
from authentik.stages.email.tasks import send_mails
|
||||
from authentik.stages.email.utils import TemplateEmailMessage
|
||||
@ -73,8 +74,8 @@ class EmailStageView(ChallengeStageView):
|
||||
"""Get token"""
|
||||
pending_user = self.get_pending_user()
|
||||
current_stage: EmailStage = self.executor.current_stage
|
||||
valid_delta = timedelta(
|
||||
minutes=current_stage.token_expiry + 1
|
||||
valid_delta = timedelta_from_string(current_stage.token_expiry) + timedelta(
|
||||
minutes=1
|
||||
) # + 1 because django timesince always rounds down
|
||||
identifier = slugify(f"ak-email-stage-{current_stage.name}-{str(uuid4())}")
|
||||
# Don't check for validity here, we only care if the token exists
|
||||
|
@ -57,7 +57,7 @@ entries:
|
||||
use_ssl: false
|
||||
timeout: 10
|
||||
from_address: system@authentik.local
|
||||
token_expiry: 30
|
||||
token_expiry: minutes=30
|
||||
subject: authentik
|
||||
template: email/password_reset.html
|
||||
activate_user_on_success: true
|
||||
|
@ -11369,11 +11369,10 @@
|
||||
"title": "From address"
|
||||
},
|
||||
"token_expiry": {
|
||||
"type": "integer",
|
||||
"minimum": -2147483648,
|
||||
"maximum": 2147483647,
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"title": "Token expiry",
|
||||
"description": "Time in minutes the token sent is valid."
|
||||
"description": "Time the token sent is valid (Format: hours=3,minutes=17,seconds=300)."
|
||||
},
|
||||
"subject": {
|
||||
"type": "string",
|
||||
|
22
schema.yml
22
schema.yml
@ -35146,7 +35146,7 @@ paths:
|
||||
- in: query
|
||||
name: token_expiry
|
||||
schema:
|
||||
type: integer
|
||||
type: string
|
||||
- in: query
|
||||
name: use_global_settings
|
||||
schema:
|
||||
@ -42774,10 +42774,8 @@ components:
|
||||
format: email
|
||||
maxLength: 254
|
||||
token_expiry:
|
||||
type: integer
|
||||
maximum: 2147483647
|
||||
minimum: -2147483648
|
||||
description: Time in minutes the token sent is valid.
|
||||
type: string
|
||||
description: 'Time the token sent is valid (Format: hours=3,minutes=17,seconds=300).'
|
||||
subject:
|
||||
type: string
|
||||
template:
|
||||
@ -42833,10 +42831,9 @@ components:
|
||||
minLength: 1
|
||||
maxLength: 254
|
||||
token_expiry:
|
||||
type: integer
|
||||
maximum: 2147483647
|
||||
minimum: -2147483648
|
||||
description: Time in minutes the token sent is valid.
|
||||
type: string
|
||||
minLength: 1
|
||||
description: 'Time the token sent is valid (Format: hours=3,minutes=17,seconds=300).'
|
||||
subject:
|
||||
type: string
|
||||
minLength: 1
|
||||
@ -50389,10 +50386,9 @@ components:
|
||||
minLength: 1
|
||||
maxLength: 254
|
||||
token_expiry:
|
||||
type: integer
|
||||
maximum: 2147483647
|
||||
minimum: -2147483648
|
||||
description: Time in minutes the token sent is valid.
|
||||
type: string
|
||||
minLength: 1
|
||||
description: 'Time the token sent is valid (Format: hours=3,minutes=17,seconds=300).'
|
||||
subject:
|
||||
type: string
|
||||
minLength: 1
|
||||
|
@ -3,6 +3,7 @@ import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
||||
import { first } from "@goauthentik/common/utils";
|
||||
import "@goauthentik/elements/forms/FormGroup";
|
||||
import "@goauthentik/elements/forms/HorizontalFormElement";
|
||||
import "@goauthentik/elements/utils/TimeDeltaHelp";
|
||||
|
||||
import { msg } from "@lit/localize";
|
||||
import { TemplateResult, html } from "lit";
|
||||
@ -202,19 +203,20 @@ export class EmailStageForm extends BaseStageForm<EmailStage> {
|
||||
</p>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${msg("Token expiry")}
|
||||
label=${msg("Token expiration")}
|
||||
?required=${true}
|
||||
name="tokenExpiry"
|
||||
>
|
||||
<input
|
||||
type="number"
|
||||
value="${first(this.instance?.tokenExpiry, 30)}"
|
||||
type="text"
|
||||
value="${first(this.instance?.tokenExpiry, "minutes=30")}"
|
||||
class="pf-c-form-control"
|
||||
required
|
||||
/>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${msg("Time in minutes the token sent is valid.")}
|
||||
${msg("Time the token sent is valid.")}
|
||||
</p>
|
||||
<ak-utils-time-delta-help></ak-utils-time-delta-help>
|
||||
</ak-form-element-horizontal>
|
||||
<ak-form-element-horizontal
|
||||
label=${msg("Subject")}
|
||||
|
Reference in New Issue
Block a user