feat: configure SMS OTP template for 2FA (#34585)
* feat: configure SMS OTP template for 2FA * fix: jinja tags * refactor: better func name
This commit is contained in:
parent
f9e51b7abf
commit
1774021901
3 changed files with 42 additions and 2 deletions
|
|
@ -51,12 +51,15 @@
|
|||
"column_break_34",
|
||||
"allow_login_after_fail",
|
||||
"two_factor_authentication",
|
||||
"column_break_odhl",
|
||||
"enable_two_factor_auth",
|
||||
"bypass_2fa_for_retricted_ip_users",
|
||||
"bypass_restrict_ip_check_if_2fa_enabled",
|
||||
"column_break_bzfr",
|
||||
"two_factor_method",
|
||||
"lifespan_qrcode_image",
|
||||
"otp_issuer_name",
|
||||
"otp_sms_template",
|
||||
"password_tab",
|
||||
"password_settings",
|
||||
"logout_on_password_reset",
|
||||
|
|
@ -752,12 +755,27 @@
|
|||
"fieldtype": "Select",
|
||||
"label": "Show External Link Warning",
|
||||
"options": "Never\nAsk\nAlways"
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_odhl",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:doc.enable_two_factor_auth==1 && doc.two_factor_method==\"SMS\"",
|
||||
"description": "OTP placeholder should be defined as <code>{{ otp }}</code> ",
|
||||
"fieldname": "otp_sms_template",
|
||||
"fieldtype": "Small Text",
|
||||
"label": "OTP SMS Template"
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_bzfr",
|
||||
"fieldtype": "Column Break"
|
||||
}
|
||||
],
|
||||
"icon": "fa fa-cog",
|
||||
"issingle": 1,
|
||||
"links": [],
|
||||
"modified": "2025-09-24 16:04:02.016562",
|
||||
"modified": "2025-11-04 16:47:54.230874",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Core",
|
||||
"name": "System Settings",
|
||||
|
|
|
|||
|
|
@ -89,6 +89,7 @@ class SystemSettings(Document):
|
|||
"#,###",
|
||||
]
|
||||
otp_issuer_name: DF.Data | None
|
||||
otp_sms_template: DF.SmallText | None
|
||||
password_reset_limit: DF.Int
|
||||
rate_limit_email_link_login: DF.Int
|
||||
reset_password_link_expiry_duration: DF.Duration | None
|
||||
|
|
@ -145,6 +146,7 @@ class SystemSettings(Document):
|
|||
self.validate_user_pass_login()
|
||||
self.validate_backup_limit()
|
||||
self.validate_file_extensions()
|
||||
self.validate_otp_sms_template()
|
||||
|
||||
if not self.link_field_results_limit:
|
||||
self.link_field_results_limit = 10
|
||||
|
|
@ -156,6 +158,17 @@ class SystemSettings(Document):
|
|||
_("{0} can not be more than {1}").format(label, 50), alert=True, indicator="yellow"
|
||||
)
|
||||
|
||||
def validate_otp_sms_template(self):
|
||||
if not self.enable_two_factor_auth or self.two_factor_method != "SMS" or not self.otp_sms_template:
|
||||
return
|
||||
|
||||
if "{{otp}}" not in self.otp_sms_template.replace(" ", ""):
|
||||
frappe.throw(
|
||||
_("OTP SMS Template must contain <code>{0}</code> placeholder to insert the OTP.").format(
|
||||
"{{otp}}"
|
||||
)
|
||||
)
|
||||
|
||||
def validate_user_pass_login(self):
|
||||
if not self.disable_user_pass_login:
|
||||
return
|
||||
|
|
|
|||
|
|
@ -320,7 +320,8 @@ def send_token_via_sms(otpsecret, token=None, phone_no=None):
|
|||
return False
|
||||
|
||||
hotp = pyotp.HOTP(otpsecret)
|
||||
args = {ss.message_parameter: f"Your verification code is {hotp.at(int(token))}"}
|
||||
otp = hotp.at(int(token))
|
||||
args = {ss.message_parameter: get_rendered_otp_message(otp)}
|
||||
|
||||
for d in ss.get("parameters"):
|
||||
args[d.parameter] = d.value
|
||||
|
|
@ -341,6 +342,14 @@ def send_token_via_sms(otpsecret, token=None, phone_no=None):
|
|||
return True
|
||||
|
||||
|
||||
def get_rendered_otp_message(otp: str) -> str:
|
||||
default_template = "Your verification code is {{otp}}"
|
||||
custom_template = frappe.get_system_settings("otp_sms_template")
|
||||
template = custom_template or default_template
|
||||
|
||||
return frappe.render_template(template, {"otp": otp})
|
||||
|
||||
|
||||
def send_token_via_email(user, token, otp_secret, otp_issuer, subject=None, message=None):
|
||||
"""Send token to user as email."""
|
||||
user_email = frappe.db.get_value("User", user, "email")
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue