use OTP App, SMS or Email to authenticate
This commit is contained in:
parent
33f416801f
commit
9741ca7dcf
8 changed files with 508 additions and 115 deletions
153
frappe/auth.py
153
frappe/auth.py
|
|
@ -17,6 +17,8 @@ from frappe.translate import get_lang_code
|
|||
from frappe.utils.password import check_password
|
||||
from frappe.core.doctype.authentication_log.authentication_log import add_authentication_log
|
||||
|
||||
from erpnext.setup.doctype.sms_settings.sms_settings import send_request
|
||||
|
||||
from urllib import quote
|
||||
|
||||
import pyotp
|
||||
|
|
@ -127,25 +129,90 @@ class LoginManager:
|
|||
if not otp:
|
||||
self.authenticate()
|
||||
# after authenticate, self.user is set (from check_password() call)
|
||||
user_info = frappe.db.get_value('User', self.user, ['two_factor_auth','two_factor_setup'], as_dict=1)
|
||||
if user_info.two_factor_auth == 1:
|
||||
user_obj = frappe.get_doc('User', self.user)
|
||||
two_factor_auth_user = len(frappe.db.sql("""select name from `tabRole` where two_factor_auth=1
|
||||
and name in ({0}) limit 1""".format(', '.join(['%s'] * len(user_obj.roles))),
|
||||
[d.role for d in user_obj.roles]))
|
||||
if two_factor_auth_user == 1:
|
||||
|
||||
if user_info.two_factor_setup:
|
||||
frappe.local.response['verification'] = {'setup_completed':True}
|
||||
otp_secret = frappe.db.get_default(self.user + '_otpsecret')
|
||||
otp_secret = frappe.db.get_default(self.user + '_otpsecret')
|
||||
|
||||
restrict_method = frappe.db.get_value('System Settings', None, 'fix_2fa_method')
|
||||
verification_meth = frappe.db.get_value('User', self.user, 'two_factor_method')
|
||||
|
||||
if restrict_method:
|
||||
try:
|
||||
fixed_method = frappe.db.sql('''SELECT DEFAULT(two_factor_method) AS 'default_method' FROM
|
||||
(SELECT 1) AS dummy LEFT JOIN tabUser on True LIMIT 1;''', as_dict=1)
|
||||
except OperationalError:
|
||||
fixed_method = [frappe._dict()]
|
||||
|
||||
if not verification_meth:
|
||||
verification_method = fixed_method[0].default_method or 'OTP App'
|
||||
else:
|
||||
verification_method = fixed_method[0].default_method or verification_meth
|
||||
|
||||
if otp_secret:
|
||||
|
||||
|
||||
token = int(pyotp.TOTP(otp_secret).now())
|
||||
|
||||
if verification_method == 'SMS':
|
||||
user_phone = frappe.db.get_value('User', self.user, ['phone','mobile_no'], as_dict=1)
|
||||
usr_phone = user_phone.mobile_no or user_phone.phone
|
||||
status = self.send_token_via_sms(token=token, phone_no=usr_phone, otpsecret=otp_secret)
|
||||
verification_obj = {'token_delivery': status,
|
||||
'prompt': status and 'Enter verification code sent to {}'.format(usr_phone[:4] + '******' + usr_phone[-3:]),
|
||||
'method': 'SMS'}
|
||||
elif verification_method == 'OTP App':
|
||||
totp_uri = False
|
||||
|
||||
if frappe.db.get_default(self.user + '_otpsecret', otp_secret):
|
||||
totp_uri = pyotp.TOTP(otp_secret).provisioning_uri(self.user, issuer_name="Estate Manager")
|
||||
|
||||
verification_obj = {'token_delivery': True,
|
||||
'prompt': False,
|
||||
'totp_uri': totp_uri,
|
||||
'method': 'OTP App'}
|
||||
elif verification_method == 'Email':
|
||||
status = self.send_token_via_email(token=token,otpsecret=otp_secret)
|
||||
verification_obj = {'token_delivery': status,
|
||||
'prompt': status and 'Enter verification code sent to your registered email address',
|
||||
'method': 'Email'}
|
||||
frappe.local.response['verification'] = verification_obj
|
||||
else:
|
||||
import os
|
||||
import base64
|
||||
otp_secret = base64.b32encode(os.urandom(10)).decode('utf-8')
|
||||
frappe.db.set_default(self.user + '_otpsecret', otp_secret)
|
||||
frappe.db.commit()
|
||||
totp_uri = pyotp.totp.TOTP(otp_secret).provisioning_uri(self.user, issuer_name="Estate Manager")
|
||||
frappe.local.response['verification'] = {'setup_completed':False, 'totp_uri':totp_uri}
|
||||
|
||||
# not actual token but counter ( hotp.at(counter) gives token)
|
||||
token = int(pyotp.TOTP(otp_secret).now())
|
||||
|
||||
frappe.local.response['verification'] = {
|
||||
'method_first_time': True,
|
||||
'token_delivery': True,
|
||||
'prompt': False,
|
||||
'totp_uri': totp_uri,
|
||||
'restrict_method': fixed_method[0].default_method or 'OTP App'
|
||||
}
|
||||
|
||||
tmp_id = frappe.generate_hash(length=8)
|
||||
usr = frappe.form_dict.get('usr')
|
||||
pwd = frappe.form_dict.get('pwd')
|
||||
frappe.cache().hset('token',tmp_id,{'usr':usr,'pwd':pwd,'otp_secret':otp_secret})
|
||||
|
||||
if verification_method in ['SMS', 'Email']:
|
||||
frappe.cache().set(tmp_id + '_token',token)
|
||||
frappe.cache().expire(tmp_id + '_token',300)
|
||||
|
||||
frappe.cache().set(tmp_id + '_usr', usr)
|
||||
frappe.cache().set(tmp_id + '_pwd', pwd)
|
||||
frappe.cache().set(tmp_id + '_otp_secret', otp_secret)
|
||||
frappe.cache().set(tmp_id + '_user', self.user)
|
||||
|
||||
for field in [tmp_id + nm for nm in ['_usr', '_pwd', '_otp_secret', '_user']]:
|
||||
frappe.cache().expire(field,120)
|
||||
|
||||
frappe.local.response['tmp_id'] = tmp_id
|
||||
|
||||
raise frappe.RequestToken
|
||||
|
|
@ -155,13 +222,14 @@ class LoginManager:
|
|||
|
||||
else:
|
||||
try:
|
||||
tmp_info = frappe.cache().hget('token', frappe.form_dict.get('tmp_id'))
|
||||
tmp_info = {
|
||||
'usr': frappe.cache().get(frappe.form_dict.get('tmp_id')+'_usr'),
|
||||
'pwd': frappe.cache().get(frappe.form_dict.get('tmp_id')+'_pwd')
|
||||
}
|
||||
self.authenticate(user=tmp_info['usr'], pwd=tmp_info['pwd'])
|
||||
except:
|
||||
frappe.log_error(frappe.get_traceback(),"AUTHENTICATION PROBLEM")
|
||||
#frappe.respond_as_web_page("Logged Out", """<p>You have been logged out.</p><p><a href='index'>Back to Home</a></p>""")
|
||||
#frappe.throw("+++++ YOUR LOGIN WAS SUCCESSFUL, CONGRATS +++++")
|
||||
#frappe.website.render('/404.html')
|
||||
|
||||
self.post_login()
|
||||
|
||||
def post_login(self,no_two_auth=False):
|
||||
|
|
@ -169,27 +237,42 @@ class LoginManager:
|
|||
self.validate_ip_address()
|
||||
self.validate_hour()
|
||||
if frappe.form_dict.get('otp') and not no_two_auth:
|
||||
self.confirm_token(otp=frappe.form_dict.get('otp'), tmp_id=frappe.form_dict.get('tmp_id'))
|
||||
hotp_token = frappe.cache().get(frappe.form_dict.get('tmp_id') + '_token')
|
||||
self.confirm_token(otp=frappe.form_dict.get('otp'), tmp_id=frappe.form_dict.get('tmp_id'), hotp_token=hotp_token)
|
||||
self.make_session()
|
||||
self.set_user_info()
|
||||
else:
|
||||
self.make_session()
|
||||
self.set_user_info()
|
||||
|
||||
def confirm_token(self,otp=None, tmp_id=None):
|
||||
def confirm_token(self,otp=None, tmp_id=None, hotp_token=False):
|
||||
try:
|
||||
otp_secret = frappe.cache().hget('token',tmp_id).get('otp_secret')
|
||||
otp_secret = frappe.cache().get(tmp_id + '_otp_secret') or frappe.db.get_default(self.user + '_otpsecret')
|
||||
if not otp_secret:
|
||||
return False
|
||||
except AttributeError:
|
||||
return False
|
||||
|
||||
if hotp_token:
|
||||
u_hotp = pyotp.HOTP(otp_secret)
|
||||
if u_hotp.verify(otp, int(hotp_token)):
|
||||
if not frappe.db.get_default(self.user + '_otpsecret'):
|
||||
frappe.db.set_default(self.user + '_otpsecret', otp_secret)
|
||||
|
||||
frappe.cache().delete(tmp_id + '_token')
|
||||
return True
|
||||
else:
|
||||
self.fail('Incorrect Verification code', self.user)
|
||||
|
||||
totp = pyotp.TOTP(otp_secret)
|
||||
if totp.verify(otp):
|
||||
frappe.cache().hdel('token', tmp_id)
|
||||
# show qr code only once
|
||||
frappe.db.set_value("User", self.user, 'two_factor_setup', 1)
|
||||
frappe.db.commit()
|
||||
if not frappe.db.get_default(self.user + '_otpsecret'):
|
||||
frappe.db.set_default(self.user + '_otpsecret', otp_secret)
|
||||
frappe.db.set_default(self.user + '_otplogin', 1)
|
||||
return True
|
||||
else:
|
||||
self.fail('Incorrect Verification code', user=frappe.cache().hget('token',tmp_id).get('usr'))
|
||||
self.fail('Incorrect Verification code', self.user)
|
||||
|
||||
def set_user_info(self, resume=False):
|
||||
# set sid again
|
||||
|
|
@ -333,6 +416,35 @@ class LoginManager:
|
|||
def clear_cookies(self):
|
||||
clear_cookies()
|
||||
|
||||
def send_token_via_sms(self,otpsecret,token=None,phone_no=None):
|
||||
ss = frappe.get_doc('SMS Settings', 'SMS Settings')
|
||||
if not ss.sms_gateway_url:
|
||||
return False
|
||||
hotp = pyotp.HOTP(otpsecret)
|
||||
args = {ss.message_parameter: 'verification code is {}'.format(hotp.at(int(token)))}
|
||||
for d in ss.get("parameters"):
|
||||
args[d.parameter] = d.value
|
||||
|
||||
if not phone_no:
|
||||
return False
|
||||
args[ss.receiver_parameter] = phone_no
|
||||
|
||||
status = send_request(ss.sms_gateway_url, args)
|
||||
|
||||
if 200 <= status < 300:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def send_token_via_email(self,token,otpsecret):
|
||||
user_email = frappe.db.get_value('User',self.user, 'email')
|
||||
if not user_email:
|
||||
return False
|
||||
hotp = pyotp.HOTP(otpsecret)
|
||||
frappe.sendmail(recipients=user_email, sender=None, subject='Verification Code',
|
||||
message='<p>Your verification code is {}</p>'.format(hotp.at(int(token))),delayed=False, retry=3)
|
||||
return True
|
||||
|
||||
class CookieManager:
|
||||
def __init__(self):
|
||||
self.cookies = {}
|
||||
|
|
@ -367,6 +479,7 @@ class CookieManager:
|
|||
for key in set(self.to_delete):
|
||||
response.set_cookie(key, "", expires=expires)
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_logged_user():
|
||||
return frappe.session.user
|
||||
|
|
|
|||
|
|
@ -121,7 +121,7 @@
|
|||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Two Factor Authenticaction",
|
||||
"label": "Two Factor Authentication",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
|
|
@ -179,7 +179,7 @@
|
|||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"modified": "2017-06-28 13:29:49.915545",
|
||||
"modified": "2017-07-06 12:42:57.097914",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Core",
|
||||
"name": "Role",
|
||||
|
|
|
|||
|
|
@ -710,6 +710,39 @@
|
|||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"default": "0",
|
||||
"depends_on": "eval:doc.enable_two_factor_auth==1",
|
||||
"description": "If this is checked, the default 2FA method in User > two_factor_method will be used",
|
||||
"fieldname": "fix_2fa_method",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Fix authentication method",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
|
|
@ -996,7 +1029,7 @@
|
|||
"issingle": 1,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"modified": "2017-06-29 18:01:46.292635",
|
||||
"modified": "2017-07-06 14:44:04.601775",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Core",
|
||||
"name": "System Settings",
|
||||
|
|
|
|||
|
|
@ -107,6 +107,28 @@ frappe.ui.form.on('User', {
|
|||
}
|
||||
cur_frm.dirty();
|
||||
}
|
||||
|
||||
frappe.call({
|
||||
method: "get_2fa_params",
|
||||
doc:frm.doc,
|
||||
callback: function(r) {
|
||||
if (r.message){
|
||||
frm.toggle_display('two_factor_method', r.message.show_method_field == true);
|
||||
if (r.message.restrict_method){
|
||||
$("select[data-fieldname=two_factor_method] > option").each(function() {
|
||||
if ($(this).val() != r.message.restrict_method){
|
||||
$(this).attr('disabled','');
|
||||
} else {
|
||||
$(this).removeAttr('disabled')
|
||||
}
|
||||
});
|
||||
//frm.set_df_property('two_factor_method', 'options', [r.message.restrict_method]);
|
||||
//frm.set_value('two_factor_method',r.message.restrict_method)
|
||||
//frm.refresh_field('two_factor_method');
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
validate: function(frm) {
|
||||
if(frm.roles_editor) {
|
||||
|
|
|
|||
|
|
@ -1729,9 +1729,10 @@
|
|||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"default": "OTP App",
|
||||
"fieldname": "two_factor_method",
|
||||
"fieldtype": "Select",
|
||||
"hidden": 1,
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
|
|
@ -1741,7 +1742,7 @@
|
|||
"label": "Two Factor Authentication Method",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "OTP App\nSMS",
|
||||
"options": "OTP App\nSMS\nEmail",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
|
|
@ -1987,68 +1988,6 @@
|
|||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"default": "0",
|
||||
"fieldname": "two_factor_auth",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 1,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Two Factor Authentication",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"default": "0",
|
||||
"fieldname": "two_factor_setup",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 1,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Two Factor Initial Setup Completed",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
}
|
||||
],
|
||||
"has_web_view": 0,
|
||||
|
|
@ -2064,7 +2003,7 @@
|
|||
"istable": 0,
|
||||
"max_attachments": 5,
|
||||
"menu_index": 0,
|
||||
"modified": "2017-06-30 16:26:06.481438",
|
||||
"modified": "2017-07-04 15:53:25.877843",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Core",
|
||||
"name": "User",
|
||||
|
|
|
|||
|
|
@ -489,6 +489,38 @@ class User(Document):
|
|||
if len(email_accounts) != len(set(email_accounts)):
|
||||
frappe.throw(_("Email Account added multiple times"))
|
||||
|
||||
def get_2fa_params(self, twoFA_method=None,user=None):
|
||||
show_method_field = frappe.db.get_value('System Settings', 'System Settings', 'enable_two_factor_auth') == unicode(1)
|
||||
try:
|
||||
two_factor_auth_user = len(frappe.db.sql("""select name from `tabRole` where two_factor_auth=1
|
||||
and name in ({0}) limit 1""".format(', '.join(['%s'] * len(self.roles))),
|
||||
[d.role for d in self.roles]))
|
||||
except Exception as e:
|
||||
return {'show_method_field' : False}
|
||||
|
||||
restrict_method = frappe.db.get_value('System Settings', None, 'fix_2fa_method')
|
||||
if int(restrict_method):
|
||||
try:
|
||||
a = frappe.db.sql('''SELECT DEFAULT(two_factor_method) AS 'default_method' FROM
|
||||
(SELECT 1) AS dummy LEFT JOIN tabUser on True LIMIT 1;''', as_dict=1)
|
||||
restrict_method = a[0].default_method
|
||||
except OperationalError:
|
||||
a = [frappe._dict()]
|
||||
restrict_method = False
|
||||
else:
|
||||
restrict_method = False
|
||||
|
||||
return {'show_method_field' : (two_factor_auth_user == 1) and show_method_field, 'restrict_method': restrict_method}
|
||||
#if not twoFA_method:
|
||||
#else:
|
||||
# if twoFA_method == 'Email':
|
||||
# if not self.email:
|
||||
# frappe.throw(_('No User Email Found'))
|
||||
# elif twoFA_method == 'SMS':
|
||||
# #user_no = frappe.db.get_values('User', user, ['mobile_no', 'phone'], as_dict=1)
|
||||
# if not (self.phone or self.mobile_no):
|
||||
# frappe.throw(_('No User Phone Number Found'))
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_timezones():
|
||||
import pytz
|
||||
|
|
@ -903,4 +935,69 @@ def handle_password_test_fail(result):
|
|||
def update_gravatar(name):
|
||||
gravatar = has_gravatar(name)
|
||||
if gravatar:
|
||||
frappe.db.set_value('User', name, 'user_image', gravatar)
|
||||
frappe.db.set_value('User', name, 'user_image', gravatar)
|
||||
|
||||
@frappe.whitelist(allow_guest=True)
|
||||
def send_token_via_sms(tmp_id,phone_no=None,user=None):
|
||||
from erpnext.setup.doctype.sms_settings.sms_settings import send_request
|
||||
|
||||
if not frappe.cache().ttl(tmp_id + '_token'):
|
||||
return False
|
||||
|
||||
token = frappe.cache().get(tmp_id + '_token')
|
||||
|
||||
ss = frappe.get_doc('SMS Settings', 'SMS Settings')
|
||||
if not ss.sms_gateway_url:
|
||||
return False
|
||||
|
||||
args = {ss.message_parameter: 'verification code is {}'.format(token)}
|
||||
|
||||
for d in ss.get("parameters"):
|
||||
args[d.parameter] = d.value
|
||||
|
||||
if user:
|
||||
user_phone = frappe.db.get_value('User', user, ['phone','mobile_no'], as_dict=1)
|
||||
usr_phone = user_phone.mobile_no or user_phone.phone
|
||||
if not usr_phone:
|
||||
return False
|
||||
else:
|
||||
if phone_no:
|
||||
usr_phone = phone_no
|
||||
else:
|
||||
return False
|
||||
|
||||
args[ss.receiver_parameter] = usr_phone
|
||||
|
||||
status = send_request(ss.sms_gateway_url, args)
|
||||
|
||||
if 200 <= status < 300:
|
||||
frappe.cache().delete(tmp_id + '_token')
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
@frappe.whitelist(allow_guest=True)
|
||||
def send_token_via_email(tmp_id,token=None):
|
||||
import pyotp
|
||||
|
||||
user = frappe.cache().get(tmp_id + '_user')
|
||||
count = token or frappe.cache().get(tmp_id + '_token')
|
||||
if ((not user) or (user == 'None') or (not count)):
|
||||
return False
|
||||
|
||||
otpsecret = frappe.cache().get(tmp_id + '_otp_secret')
|
||||
hotp = pyotp.HOTP(otpsecret)
|
||||
user_email = frappe.db.get_value('User',user, 'email')
|
||||
if not user_email:
|
||||
return False
|
||||
frappe.sendmail(recipients=user_email, sender=None, subject='Verification Code',
|
||||
message='<p>Your verification code is {0}</p>'.format(hotp.at(int(count))),delayed=False, retry=3)
|
||||
return True
|
||||
|
||||
@frappe.whitelist(allow_guest=True)
|
||||
def set_verification_method(tmp_id,method=None):
|
||||
user = frappe.cache().get(tmp_id + '_user')
|
||||
if ((not user) or (user == 'None') or (not method)):
|
||||
return False
|
||||
frappe.db.set_value('User', user, 'two_factor_method', method)
|
||||
frappe.db.commit()
|
||||
|
|
@ -159,37 +159,211 @@ login.login_handlers = (function() {
|
|||
console.log(data);
|
||||
if(data.verification) {
|
||||
login.set_indicator("{{ _("Success") }}", 'green');
|
||||
$('.login-content').empty().append($('<div>').attr({'id':'otp_div'}).html('<form class="form-verify"><div class="page-card-head">\
|
||||
|
||||
var continue_otp = function(setup_completed,method_prompt){
|
||||
|
||||
$('.login-content').empty().append($('<div>').attr({'id':'otp_div'}).html('<form class="form-verify"><div class="page-card-head">\
|
||||
<span class="indicator blue" data-text="Verification">Verification</span></div>\
|
||||
<input type="text" id="login_token" class="form-control" placeholder="Verification Code" required="" autofocus="">\
|
||||
<input type="text" id="login_token" class="form-control" placeholder="Verification Code" required="" autocomplete="off" autofocus="">\
|
||||
<button class="btn btn-sm btn-primary btn-block" id="verify_token">Verify</button></form>'));
|
||||
if (!data.verification.setup_completed){
|
||||
var qrcode = $('<div>').attr('id','qrcode_div');
|
||||
var direction = $('<div>').attr('id','qr_info').text('Scan QR Code and enter the resulting code displayed');
|
||||
var qrcanvas = $('<canvas>');
|
||||
qrcanvas.attr('id','qrcanvass');
|
||||
qrcode.append(direction);
|
||||
qrcode.append(qrcanvas);
|
||||
$('#otp_div').prepend(qrcode)
|
||||
qr = new QRious({
|
||||
element: document.getElementById('qrcanvass'),
|
||||
value: data.verification.totp_uri,
|
||||
background: 'white', // background color
|
||||
foreground: 'black', // foreground color
|
||||
level: 'L', // Error correction level of the QR code
|
||||
mime: 'image/png', // MIME type used to render
|
||||
size: 200
|
||||
});
|
||||
} else {
|
||||
var qrcode = $('<div>').attr('id','qrcode_div');
|
||||
var direction = $('<div>').attr('id','qr_info').text('Enter the code displayed in otp app under the appropriate account');
|
||||
direction.attr('style','padding-bottom:10px;');
|
||||
qrcode.append(direction);
|
||||
$('#otp_div').prepend(qrcode)
|
||||
|
||||
verify_token();
|
||||
|
||||
if (!setup_completed){
|
||||
var qrcode = $('<div>').attr('id','qrcode_div');
|
||||
|
||||
var direction = $('<div>').attr('id','qr_info').text(method_prompt || 'Scan QR Code and enter the resulting code displayed');
|
||||
|
||||
var qrcanvas = $('<canvas>');
|
||||
qrcanvas.attr('id','qrcanvass');
|
||||
qrcode.append(direction);
|
||||
qrcode.append(qrcanvas);
|
||||
$('#otp_div').prepend(qrcode)
|
||||
qr = new QRious({
|
||||
element: document.getElementById('qrcanvass'),
|
||||
value: data.verification.totp_uri,
|
||||
background: 'white', // background color
|
||||
foreground: 'black', // foreground color
|
||||
level: 'L', // Error correction level of the QR code
|
||||
mime: 'image/png', // MIME type used to render
|
||||
size: 200
|
||||
});
|
||||
} else {
|
||||
var qrcode = $('<div>').attr('id','qrcode_div');
|
||||
var direction = $('<div>').attr('id','qr_info').text(method_prompt || 'Enter Code displayed in OTP App');
|
||||
direction.attr('style','padding-bottom:10px;');
|
||||
qrcode.append(direction);
|
||||
$('#otp_div').prepend(qrcode)
|
||||
}
|
||||
}
|
||||
|
||||
var continue_sms = function(setup_completed,method_prompt){
|
||||
|
||||
$('.login-content').empty().append($('<div>').attr({'id':'otp_div'}).html(
|
||||
'<form class="form-verify">\
|
||||
<div class="page-card-head">\
|
||||
<span class="indicator blue" data-text="Verification">Verification</span>\
|
||||
</div>\
|
||||
<input type="text" id="login_token" class="form-control" placeholder="Verification Code" required="" autofocus="">\
|
||||
<button class="btn btn-sm btn-primary btn-block" id="verify_token">Verify</button>\
|
||||
</form>'));
|
||||
|
||||
verify_token();
|
||||
|
||||
if (!setup_completed){
|
||||
var sms_div = $('<div>').attr({'id':'sms_div','style':'margin-bottom: 20px;'});
|
||||
var direction = $('<div>').attr({'id':'sms_info','style':'margin-bottom: 15px;'}).text('Enter phone number to send verification code');
|
||||
sms_div.append(direction);
|
||||
sms_div.append($('<div>').attr({'id':'sms_code_div'}).html(
|
||||
'<div class="form-group text-center">\
|
||||
<input type="text" id="phone_no" class="form-control" placeholder="2347001234567" required="" autofocus="">\
|
||||
<button class="btn btn-sm btn-primary" id="submit_phone_no" >Send SMS</button>\
|
||||
</div><hr>'));
|
||||
|
||||
$('#otp_div').prepend(sms_div);
|
||||
|
||||
$('#submit_phone_no').on('click',function(){
|
||||
frappe.call({
|
||||
method: "frappe.core.doctype.user.user.send_token_via_sms",
|
||||
args: {'phone_no': $('#phone_no').val(), 'tmp_id':data.tmp_id },
|
||||
freeze: true,
|
||||
callback: function(r) {
|
||||
if (r.message){
|
||||
$('#sms_div').empty().append(
|
||||
'<p class="lead">SMS sent.<br><small><small>Enter verification code received</small></small></p><hr>'
|
||||
);
|
||||
} else {
|
||||
$('#sms_div').empty().append(
|
||||
'<p class="lead">SMS not sent</p><hr>'
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
})
|
||||
} else {
|
||||
var smscode = $('<div>').attr('id','smscode_div');
|
||||
var direction = $('<div>').attr('id','qr_info').text(method_prompt || 'Enter verification code sent to registered phone number');
|
||||
direction.attr('style','padding-bottom:10px;');
|
||||
smscode.append(direction);
|
||||
$('#otp_div').prepend(smscode)
|
||||
}
|
||||
}
|
||||
|
||||
var continue_email = function(setup_completed,method_prompt){
|
||||
|
||||
$('.login-content').empty().append($('<div>').attr({'id':'otp_div'}).html(
|
||||
'<form class="form-verify">\
|
||||
<div class="page-card-head">\
|
||||
<span class="indicator blue" data-text="Verification">Verification</span>\
|
||||
</div>\
|
||||
<input type="text" id="login_token" class="form-control" placeholder="Verification Code" required="" autofocus="">\
|
||||
<button class="btn btn-sm btn-primary btn-block" id="verify_token">Verify</button>\
|
||||
</form>'));
|
||||
|
||||
verify_token();
|
||||
|
||||
if (!setup_completed){
|
||||
var email_div = $('<div>').attr({'id':'email_div','style':'margin-bottom: 20px;'});
|
||||
email_div.append('<p>Verification code email will be sent to registered email address. Enter code received below</p>')
|
||||
|
||||
$('#otp_div').prepend(email_div);
|
||||
|
||||
frappe.call({
|
||||
method: "frappe.core.doctype.user.user.send_token_via_email",
|
||||
args: {'tmp_id':data.tmp_id },
|
||||
callback: function(r) {
|
||||
if (r.message){
|
||||
} else {
|
||||
$('#email_div').empty().append(
|
||||
'<p>Email not sent</p><hr>'
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
if (method_prompt){
|
||||
var emailcode = $('<div>').attr('id','emailcode_div');
|
||||
var direction = $('<div>').attr('id','qr_info').text(method_prompt || 'Verification code email will be sent to registered email address. Enter code received below');
|
||||
direction.attr('style','padding-bottom:10px;');
|
||||
emailcode.append(direction);
|
||||
$('#otp_div').prepend(emailcode);
|
||||
} else {
|
||||
var emailcode = $('<div>').attr('id','emailcode_div');
|
||||
var direction = $('<div>').attr('id','qr_info').text('Verification code email not sent');
|
||||
direction.attr('style','padding-bottom:10px;');
|
||||
emailcode.append(direction);
|
||||
$('#otp_div').prepend(emailcode)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if (data.verification.method_first_time){
|
||||
$('.login-content').empty().append('<div id="verification_method">\
|
||||
<div>\
|
||||
<p class="lead">Select verification Method <br>\
|
||||
<small><small><small class="text-muted">method may be changed later in settings</small></small></small></p>\
|
||||
</div>\
|
||||
<div class="form-check">\
|
||||
<label class="form-check-label">\
|
||||
<input class="form-check-input" type="radio" name="method" value="OTP App" checked>\
|
||||
OTP App\
|
||||
</label>\
|
||||
</div>\
|
||||
<div class="form-check">\
|
||||
<label class="form-check-label">\
|
||||
<input class="form-check-input" type="radio" name="method" value="SMS">\
|
||||
SMS\
|
||||
</label>\
|
||||
</div>\
|
||||
<div class="form-check disabled">\
|
||||
<label class="form-check-label">\
|
||||
<input class="form-check-input" type="radio" name="method" value="Email">\
|
||||
Email\
|
||||
</label>\
|
||||
</div>\
|
||||
<button id="submit_method" class="btn btn-sm btn-primary">Continue</button>\
|
||||
</div>')
|
||||
|
||||
if (data.verification.restrict_method){
|
||||
$('input[name=method]').each(function(){
|
||||
if ($(this).val() != data.verification.restrict_method){
|
||||
$(this).attr('disabled',true)
|
||||
}
|
||||
})
|
||||
}
|
||||
$('#submit_method').on('click',function(event){
|
||||
if ($('input[name=method]:checked').val() == 'OTP App'){
|
||||
continue_otp(setup_completed=false);
|
||||
} else if ($('input[name=method]:checked').val() == 'SMS'){
|
||||
continue_sms(setup_completed=false);
|
||||
console.log('SMS');
|
||||
} else if ($('input[name=method]:checked').val() == 'Email'){
|
||||
continue_email(setup_completed=false);
|
||||
}
|
||||
|
||||
frappe.call({
|
||||
method: "frappe.core.doctype.user.user.set_verification_method",
|
||||
args: {'tmp_id':data.tmp_id, 'method': $('input[name=method]:checked').val()},
|
||||
callback: function(r) { }
|
||||
});
|
||||
});
|
||||
} else {
|
||||
if (data.verification.method == 'OTP App'){
|
||||
console.log(data.verification.totp_uri)
|
||||
continue_otp(setup_completed = !data.verification.totp_uri);
|
||||
} else if (data.verification.method == 'SMS'){
|
||||
continue_sms(setup_completed=true, method_prompt=data.verification.prompt);
|
||||
console.log('SMS');
|
||||
} else if (data.verification.method == 'Email'){
|
||||
continue_sms(setup_completed=true, method_prompt=data.verification.prompt);
|
||||
}
|
||||
}
|
||||
|
||||
document.cookie = "tmp_id="+data.tmp_id;
|
||||
verify_token();
|
||||
//verify_token();
|
||||
return false;
|
||||
|
||||
} else if(data.message == 'Logged In'){
|
||||
login.set_indicator("{{ _("Success") }}", 'green');
|
||||
window.location.href = get_url_arg("redirect-to") || data.home_page;
|
||||
|
|
|
|||
|
|
@ -12,6 +12,20 @@ from frappe.integrations.doctype.ldap_settings.ldap_settings import get_ldap_set
|
|||
|
||||
no_cache = True
|
||||
|
||||
import pyqrcode
|
||||
from StringIO import StringIO
|
||||
from werkzeug.wrappers import Response
|
||||
|
||||
def get_qr_code():
|
||||
|
||||
url = pyqrcode.create('http://www.google.com')
|
||||
stream = StringIO()
|
||||
url.svg(stream, scale=5)
|
||||
responses = Response(stream.getvalue().encode('utf-8'))
|
||||
responses.status_code = 200
|
||||
responses.headers['content-type'] = 'image/svg+xml; charset=utf-8'
|
||||
return responses
|
||||
|
||||
def get_context(context):
|
||||
if frappe.session.user != "Guest" and frappe.session.data.user_type=="System User":
|
||||
frappe.local.flags.redirect_location = "/testpayment"
|
||||
|
|
@ -30,6 +44,7 @@ def get_context(context):
|
|||
|
||||
ldap_settings = get_ldap_settings()
|
||||
context["ldap_settings"] = ldap_settings
|
||||
context['qqrcode'] = frappe.render_template(get_qr_code())
|
||||
|
||||
return context
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue