From 570442865b308b38b6f431264f590c43ac2037c7 Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Tue, 20 Dec 2022 11:57:20 +0530 Subject: [PATCH 01/41] feat: Login without password (through link sent on email) --- .../system_settings/system_settings.json | 20 ++++-- frappe/public/scss/login.bundle.scss | 2 + .../emails/login_without_password.html | 66 +++++++++++++++++++ frappe/templates/includes/login/login.js | 34 +++++++++- frappe/www/login.html | 43 +++++++++++- frappe/www/login.py | 49 +++++++++++++- 6 files changed, 205 insertions(+), 9 deletions(-) create mode 100644 frappe/templates/emails/login_without_password.html diff --git a/frappe/core/doctype/system_settings/system_settings.json b/frappe/core/doctype/system_settings/system_settings.json index f9d7adceef..6f2454813c 100644 --- a/frappe/core/doctype/system_settings/system_settings.json +++ b/frappe/core/doctype/system_settings/system_settings.json @@ -39,6 +39,7 @@ "allow_login_using_mobile_number", "allow_login_using_user_name", "disable_user_pass_login", + "login_without_password", "allow_error_traceback", "strip_exif_metadata_from_uploaded_images", "allow_older_web_view_links", @@ -416,11 +417,11 @@ "label": "Send document Web View link in email" }, { - "collapsible": 1, - "fieldname": "prepared_report_section", - "fieldtype": "Section Break", - "label": "Reports" - }, + "collapsible": 1, + "fieldname": "prepared_report_section", + "fieldtype": "Section Break", + "label": "Reports" + }, { "default": "Frappe", "description": "The application name will be used in the Login page.", @@ -504,12 +505,19 @@ "fieldname": "disable_user_pass_login", "fieldtype": "Check", "label": "Disable Username/Password Login" + }, + { + "default": "0", + "description": "User will be able to login using the link sent on the email", + "fieldname": "login_without_password", + "fieldtype": "Check", + "label": "Login Without Password" } ], "icon": "fa fa-cog", "issingle": 1, "links": [], - "modified": "2022-11-28 17:57:05.099512", + "modified": "2022-12-20 11:20:40.735668", "modified_by": "Administrator", "module": "Core", "name": "System Settings", diff --git a/frappe/public/scss/login.bundle.scss b/frappe/public/scss/login.bundle.scss index 488dd4106e..9f94cde8f2 100644 --- a/frappe/public/scss/login.bundle.scss +++ b/frappe/public/scss/login.bundle.scss @@ -7,6 +7,7 @@ body { } .for-forgot, +.for-login-without-password, .for-signup, .for-email-login { display: none; @@ -14,6 +15,7 @@ body { .for-login, .for-forgot, +.for-login-without-password, .for-signup, .for-email-login { padding: max(10vh, 60px) 0; diff --git a/frappe/templates/emails/login_without_password.html b/frappe/templates/emails/login_without_password.html new file mode 100644 index 0000000000..9fb3d87413 --- /dev/null +++ b/frappe/templates/emails/login_without_password.html @@ -0,0 +1,66 @@ +{% macro table(content, td_align, tb_color, tb_width) %} + + + + + + +
+ {{ content }} +
+{% endmacro %} + +{% macro body() %} + + + + + + + + + +
+ +
The link will expire in {{ minutes }} minutes
+
+ +
+{% endmacro %} + +{% macro image() %} +{% if logo %} +{{ app_name }} +{% endif %} +{% endmacro %} + +{% macro footer() %} + +{% endmacro %} + +
+
+ + + + + + + +
+
\ No newline at end of file diff --git a/frappe/templates/includes/login/login.js b/frappe/templates/includes/login/login.js index defbbc2975..7ef388abcc 100644 --- a/frappe/templates/includes/login/login.js +++ b/frappe/templates/includes/login/login.js @@ -55,6 +55,25 @@ login.bind_events = function () { return false; }); + $(".form-login-without-password").on("submit", function (event) { + event.preventDefault(); + var args = {}; + args.cmd = "frappe.www.login.send_login_link"; + args.email = ($("#login_without_password_email").val() || "").trim(); + if (!args.email) { + login.set_status('{{ _("Valid Login id required.") }}', 'red'); + return false; + } + login.call(args).then(() => { + login.set_status('{{ _("Login link sent to your email.") }}', 'blue'); + $("#login_without_password_email").val(""); + }).catch(() => { + login.set_status('{{ _("Send login link") }}', 'blue'); + }); + + return false; + }); + $(".toggle-password").click(function () { var input = $($(this).attr("toggle")); if (input.attr("type") == "password") { @@ -94,6 +113,7 @@ login.reset_sections = function (hide) { $("section.for-login").toggle(false); $("section.for-email-login").toggle(false); $("section.for-forgot").toggle(false); + $("section.for-login-without-password").toggle(false); $("section.for-signup").toggle(false); } $('section:not(.signup-disabled) .indicator').each(function () { @@ -121,10 +141,22 @@ login.steptwo = function () { login.forgot = function () { login.reset_sections(); + if ($("#login_email").val()) { + $("#forgot_email").val($("#login_email").val()); + } $(".for-forgot").toggle(true); $("#forgot_email").focus(); } +login.loginWithoutPassword = function () { + login.reset_sections(); + if ($("#login_email").val()) { + $("#login_without_password_email").val($("#login_email").val()); + } + $(".for-login-without-password").toggle(true); + $("#login_without_password_email").focus(); +} + login.signup = function () { login.reset_sections(); $(".for-signup").toggle(true); @@ -270,7 +302,7 @@ frappe.ready(function () { $(window).trigger("hashchange"); } - $(".form-signup, .form-forgot").removeClass("hide"); + $(".form-signup, .form-forgot, .form-login-without-password").removeClass("hide"); $(document).trigger('login_rendered'); }); diff --git a/frappe/www/login.html b/frappe/www/login.html index e9f2b9103a..e5a714315e 100644 --- a/frappe/www/login.html +++ b/frappe/www/login.html @@ -80,7 +80,7 @@
{{ logo_section() }}
+
+ {% if login_without_password %} + + {% endif %} +
{% else %} @@ -181,6 +190,38 @@ + +
+ +
{% endblock %} diff --git a/frappe/www/login.py b/frappe/www/login.py index 32e4bb4344..44daa87a64 100644 --- a/frappe/www/login.py +++ b/frappe/www/login.py @@ -7,7 +7,7 @@ from frappe import _ from frappe.auth import LoginManager from frappe.integrations.doctype.ldap_settings.ldap_settings import LDAPSettings from frappe.integrations.oauth2_logins import decoder_compat -from frappe.utils import cint +from frappe.utils import cint, get_url from frappe.utils.html_utils import get_icon_html from frappe.utils.jinja import guess_is_path from frappe.utils.oauth import ( @@ -102,6 +102,8 @@ def get_context(context): context["login_label"] = f" {_('or')} ".join(login_label) + context["login_without_password"] = frappe.get_system_settings("login_without_password") + return context @@ -143,3 +145,48 @@ def login_via_token(login_token): redirect_post_login( desk_user=frappe.db.get_value("User", frappe.session.user, "user_type") == "System User" ) + + +@frappe.whitelist(allow_guest=True) +def send_login_link(email, subject=None, message=None): + if not frappe.db.exists("User", email): + frappe.throw("No registered account with this email address") + + key = frappe.generate_hash("Login Link", 20) + minutes = 10 + frappe.cache().set_value(f"one_time_login_key:{key}", email, expires_in_sec=minutes * 60) + + link = get_url(f"/api/method/frappe.www.login.login_via_key?key={key}") + + logo = frappe.get_website_settings("app_logo") or frappe.get_hooks("app_logo_url")[-1] + app_name = ( + frappe.get_website_settings("app_name") or frappe.get_system_settings("app_name") or _("Frappe") + ) + + subject = subject or _("Login To {0}").format(app_name) + + frappe.sendmail( + subject=subject, + recipients=email, + template="login_without_password", + args={"link": link, "minutes": minutes, "app_name": app_name, "logo": logo}, + ) + + +@frappe.whitelist(allow_guest=True) +def login_via_key(key): + cache_key = f"one_time_login_key:{key}" + email = frappe.cache().get_value(cache_key) + + if email: + frappe.cache().delete_value(cache_key) + frappe.local.login_manager.login_as(email) + frappe.response.type = "redirect" + frappe.response.location = "/app" + else: + frappe.respond_as_web_page( + _("Not Permitted"), + _("The link you trying to login is invalid or expired."), + http_status_code=403, + indicator_color="red", + ) From 60c29b69390f6ad5e041cc1ef9fe51148f21a351 Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Tue, 20 Dec 2022 13:54:40 +0530 Subject: [PATCH 02/41] fix: removed logo implementation --- frappe/templates/emails/login_without_password.html | 11 +---------- frappe/www/login.py | 5 ++--- 2 files changed, 3 insertions(+), 13 deletions(-) diff --git a/frappe/templates/emails/login_without_password.html b/frappe/templates/emails/login_without_password.html index 9fb3d87413..44e521d18f 100644 --- a/frappe/templates/emails/login_without_password.html +++ b/frappe/templates/emails/login_without_password.html @@ -30,12 +30,6 @@ {% endmacro %} -{% macro image() %} -{% if logo %} -{{ app_name }} -{% endif %} -{% endmacro %} - {% macro footer() %}