@@ -19,6 +19,8 @@ from frappe.core.doctype.authentication_log.authentication_log import add_authen | |||||
from urllib import quote | from urllib import quote | ||||
import pyotp | |||||
class HTTPRequest: | class HTTPRequest: | ||||
def __init__(self): | def __init__(self): | ||||
# Get Environment variables | # Get Environment variables | ||||
@@ -116,15 +118,73 @@ class LoginManager: | |||||
def login(self): | def login(self): | ||||
# clear cache | # clear cache | ||||
frappe.clear_cache(user = frappe.form_dict.get('usr')) | frappe.clear_cache(user = frappe.form_dict.get('usr')) | ||||
self.authenticate() | |||||
self.post_login() | |||||
otp = frappe.form_dict.get('otp') | |||||
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: | |||||
if user_info.two_factor_setup: | |||||
frappe.local.response['verification'] = {'setup_completed':True} | |||||
raise frappe.RequestToken | |||||
otp_secret = frappe.db.get_default(self.user + '_otpsecret') | |||||
else: | |||||
import os | |||||
import base64 | |||||
otp_secret = base64.b32encode(os.urandom(10)).decode('utf-8') | |||||
frappe.db.set_default(self.user + '_otpsecret', otp_secret) | |||||
# set two_factor_setup as 1 meaning user has copied otpsecret | |||||
frappe.db.set_value("User", self.user, 'two_factor_setup', 1) | |||||
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} | |||||
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}) | |||||
frappe.local.response['tmp_id'] = tmp_id | |||||
raise frappe.RequestToken | |||||
else: | |||||
self.post_login(no_two_auth=True) | |||||
def post_login(self): | |||||
else: | |||||
try: | |||||
tmp_info = frappe.cache().hget('token', frappe.form_dict.get('tmp_id')) | |||||
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): | |||||
self.run_trigger('on_login') | self.run_trigger('on_login') | ||||
self.validate_ip_address() | self.validate_ip_address() | ||||
self.validate_hour() | self.validate_hour() | ||||
self.make_session() | |||||
self.set_user_info() | |||||
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')) | |||||
self.make_session() | |||||
self.set_user_info() | |||||
else: | |||||
self.make_session() | |||||
self.set_user_info() | |||||
def confirm_token(self,otp=None, tmp_id=None): | |||||
try: | |||||
otp_secret = frappe.cache().hget('token',tmp_id).get('otp_secret') | |||||
except AttributeError: | |||||
return False | |||||
totp = pyotp.TOTP(otp_secret) | |||||
if totp.verify(otp): | |||||
frappe.cache().hdel('token', tmp_id) | |||||
return True | |||||
else: | |||||
self.fail('Incorrect Verification code', user=frappe.cache().hget('token',tmp_id).get('usr')) | |||||
def set_user_info(self, resume=False): | def set_user_info(self, resume=False): | ||||
# set sid again | # set sid again | ||||
@@ -105,6 +105,37 @@ | |||||
"set_only_once": 0, | "set_only_once": 0, | ||||
"unique": 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": 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": "Two Factor Authenticaction", | |||||
"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_bulk_edit": 0, | ||||
"allow_on_submit": 0, | "allow_on_submit": 0, | ||||
@@ -148,7 +179,7 @@ | |||||
"issingle": 0, | "issingle": 0, | ||||
"istable": 0, | "istable": 0, | ||||
"max_attachments": 0, | "max_attachments": 0, | ||||
"modified": "2017-05-04 11:03:41.533058", | |||||
"modified": "2017-06-28 13:29:49.915545", | |||||
"modified_by": "Administrator", | "modified_by": "Administrator", | ||||
"module": "Core", | "module": "Core", | ||||
"name": "Role", | "name": "Role", | ||||
@@ -1956,6 +1956,68 @@ | |||||
"search_index": 0, | "search_index": 0, | ||||
"set_only_once": 0, | "set_only_once": 0, | ||||
"unique": 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, | "has_web_view": 0, | ||||
@@ -1971,7 +2033,7 @@ | |||||
"istable": 0, | "istable": 0, | ||||
"max_attachments": 5, | "max_attachments": 5, | ||||
"menu_index": 0, | "menu_index": 0, | ||||
"modified": "2017-05-19 09:12:35.697915", | |||||
"modified": "2017-06-28 14:40:26.616254", | |||||
"modified_by": "Administrator", | "modified_by": "Administrator", | ||||
"module": "Core", | "module": "Core", | ||||
"name": "User", | "name": "User", | ||||
@@ -57,6 +57,7 @@ class User(Document): | |||||
self.validate_email_type(self.name) | self.validate_email_type(self.name) | ||||
self.add_system_manager_role() | self.add_system_manager_role() | ||||
self.set_system_user() | self.set_system_user() | ||||
self.set_two_factor_auth() | |||||
self.set_full_name() | self.set_full_name() | ||||
self.check_enable_disable() | self.check_enable_disable() | ||||
self.ensure_unique_roles() | self.ensure_unique_roles() | ||||
@@ -146,6 +147,14 @@ class User(Document): | |||||
else: | else: | ||||
self.user_type = 'Website User' | self.user_type = 'Website User' | ||||
def set_two_factor_auth(self): | |||||
'''Set two factor authentication for user''' | |||||
if (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]))): | |||||
self.two_factor_auth = 1 | |||||
def has_desk_access(self): | def has_desk_access(self): | ||||
'''Return true if any of the set roles has desk access''' | '''Return true if any of the set roles has desk access''' | ||||
if not self.roles: | if not self.roles: | ||||
@@ -37,6 +37,9 @@ class SessionStopped(Exception): | |||||
class UnsupportedMediaType(Exception): | class UnsupportedMediaType(Exception): | ||||
http_status_code = 415 | http_status_code = 415 | ||||
class RequestToken(Exception): | |||||
http_status_code = 200 | |||||
class Redirect(Exception): | class Redirect(Exception): | ||||
http_status_code = 301 | http_status_code = 301 | ||||
@@ -5,11 +5,14 @@ window.disable_signup = {{ disable_signup and "true" or "false" }}; | |||||
window.login = {}; | window.login = {}; | ||||
window.verify = {}; | |||||
login.bind_events = function() { | login.bind_events = function() { | ||||
$(window).on("hashchange", function() { | $(window).on("hashchange", function() { | ||||
login.route(); | login.route(); | ||||
}); | }); | ||||
$(".form-login").on("submit", function(event) { | $(".form-login").on("submit", function(event) { | ||||
event.preventDefault(); | event.preventDefault(); | ||||
var args = {}; | var args = {}; | ||||
@@ -90,6 +93,11 @@ login.login = function() { | |||||
$(".for-login").toggle(true); | $(".for-login").toggle(true); | ||||
} | } | ||||
login.steptwo = function() { | |||||
login.reset_sections(); | |||||
$(".for-login").toggle(true); | |||||
} | |||||
login.forgot = function() { | login.forgot = function() { | ||||
login.reset_sections(); | login.reset_sections(); | ||||
$(".for-forgot").toggle(true); | $(".for-forgot").toggle(true); | ||||
@@ -148,7 +156,17 @@ login.login_handlers = (function() { | |||||
var login_handlers = { | var login_handlers = { | ||||
200: function(data) { | 200: function(data) { | ||||
if(data.message=="Logged In") { | |||||
console.log(data); | |||||
if(data.token) { | |||||
login.set_indicator("{{ _("Success") }}", 'green'); | |||||
$('.login-content').empty().append($('<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>')); | |||||
document.cookie = "tmp_id="+data.tmp_id; | |||||
verify_token(); | |||||
return false; | |||||
} else if(data.message == 'Logged In'){ | |||||
login.set_indicator("{{ _("Success") }}", 'green'); | login.set_indicator("{{ _("Success") }}", 'green'); | ||||
window.location.href = get_url_arg("redirect-to") || data.home_page; | window.location.href = get_url_arg("redirect-to") || data.home_page; | ||||
} else if(data.message=="No App") { | } else if(data.message=="No App") { | ||||
@@ -194,10 +212,14 @@ login.login_handlers = (function() { | |||||
}; | }; | ||||
return login_handlers; | return login_handlers; | ||||
})(); | |||||
} )(); | |||||
frappe.ready(function() { | frappe.ready(function() { | ||||
login.bind_events(); | login.bind_events(); | ||||
console.log("Why"); | |||||
if (!window.location.hash) { | if (!window.location.hash) { | ||||
window.location.hash = "#login"; | window.location.hash = "#login"; | ||||
@@ -208,3 +230,23 @@ frappe.ready(function() { | |||||
$(".form-signup, .form-forgot").removeClass("hide"); | $(".form-signup, .form-forgot").removeClass("hide"); | ||||
$(document).trigger('login_rendered'); | $(document).trigger('login_rendered'); | ||||
}); | }); | ||||
var verify_token = function(event) { | |||||
$('#verify_token').bind("click", function() { | |||||
console.log("Why XX2"); | |||||
//eventx.preventDefault(); | |||||
var args = {}; | |||||
args.cmd = "login"; | |||||
args.otp = $("#login_token").val(); | |||||
console.log("LLLLLLLLLLLLLLLLLLL"); | |||||
args.tmp_id = frappe.get_cookie('tmp_id'); | |||||
if(!args.otp) { | |||||
frappe.msgprint('{{ _("Login token required") }}'); | |||||
return false; | |||||
} | |||||
console.log("Button Clicked") | |||||
console.log(args) | |||||
login.call(args); | |||||
return false; | |||||
}); | |||||
} |
@@ -14,7 +14,7 @@ no_cache = True | |||||
def get_context(context): | def get_context(context): | ||||
if frappe.session.user != "Guest" and frappe.session.data.user_type=="System User": | if frappe.session.user != "Guest" and frappe.session.data.user_type=="System User": | ||||
frappe.local.flags.redirect_location = "/desk" | |||||
frappe.local.flags.redirect_location = "/testpayment" | |||||
raise frappe.Redirect | raise frappe.Redirect | ||||
# get settings from site config | # get settings from site config | ||||