@@ -19,6 +19,8 @@ from frappe.core.doctype.authentication_log.authentication_log import add_authen | |||
from urllib import quote | |||
import pyotp | |||
class HTTPRequest: | |||
def __init__(self): | |||
# Get Environment variables | |||
@@ -116,15 +118,73 @@ class LoginManager: | |||
def login(self): | |||
# clear cache | |||
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.validate_ip_address() | |||
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): | |||
# set sid again | |||
@@ -105,6 +105,37 @@ | |||
"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": 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_on_submit": 0, | |||
@@ -148,7 +179,7 @@ | |||
"issingle": 0, | |||
"istable": 0, | |||
"max_attachments": 0, | |||
"modified": "2017-05-04 11:03:41.533058", | |||
"modified": "2017-06-28 13:29:49.915545", | |||
"modified_by": "Administrator", | |||
"module": "Core", | |||
"name": "Role", | |||
@@ -1956,6 +1956,68 @@ | |||
"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, | |||
@@ -1971,7 +2033,7 @@ | |||
"istable": 0, | |||
"max_attachments": 5, | |||
"menu_index": 0, | |||
"modified": "2017-05-19 09:12:35.697915", | |||
"modified": "2017-06-28 14:40:26.616254", | |||
"modified_by": "Administrator", | |||
"module": "Core", | |||
"name": "User", | |||
@@ -57,6 +57,7 @@ class User(Document): | |||
self.validate_email_type(self.name) | |||
self.add_system_manager_role() | |||
self.set_system_user() | |||
self.set_two_factor_auth() | |||
self.set_full_name() | |||
self.check_enable_disable() | |||
self.ensure_unique_roles() | |||
@@ -146,6 +147,14 @@ class User(Document): | |||
else: | |||
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): | |||
'''Return true if any of the set roles has desk access''' | |||
if not self.roles: | |||
@@ -37,6 +37,9 @@ class SessionStopped(Exception): | |||
class UnsupportedMediaType(Exception): | |||
http_status_code = 415 | |||
class RequestToken(Exception): | |||
http_status_code = 200 | |||
class Redirect(Exception): | |||
http_status_code = 301 | |||
@@ -5,11 +5,14 @@ window.disable_signup = {{ disable_signup and "true" or "false" }}; | |||
window.login = {}; | |||
window.verify = {}; | |||
login.bind_events = function() { | |||
$(window).on("hashchange", function() { | |||
login.route(); | |||
}); | |||
$(".form-login").on("submit", function(event) { | |||
event.preventDefault(); | |||
var args = {}; | |||
@@ -90,6 +93,11 @@ login.login = function() { | |||
$(".for-login").toggle(true); | |||
} | |||
login.steptwo = function() { | |||
login.reset_sections(); | |||
$(".for-login").toggle(true); | |||
} | |||
login.forgot = function() { | |||
login.reset_sections(); | |||
$(".for-forgot").toggle(true); | |||
@@ -148,7 +156,17 @@ login.login_handlers = (function() { | |||
var login_handlers = { | |||
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'); | |||
window.location.href = get_url_arg("redirect-to") || data.home_page; | |||
} else if(data.message=="No App") { | |||
@@ -194,10 +212,14 @@ login.login_handlers = (function() { | |||
}; | |||
return login_handlers; | |||
})(); | |||
} )(); | |||
frappe.ready(function() { | |||
login.bind_events(); | |||
console.log("Why"); | |||
if (!window.location.hash) { | |||
window.location.hash = "#login"; | |||
@@ -208,3 +230,23 @@ frappe.ready(function() { | |||
$(".form-signup, .form-forgot").removeClass("hide"); | |||
$(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): | |||
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 | |||
# get settings from site config | |||