浏览代码

Check if user role on login, return otpauth uri

version-14
crossxcell99 8 年前
父节点
当前提交
9771308072
共有 7 个文件被更改,包括 217 次插入10 次删除
  1. +65
    -5
      frappe/auth.py
  2. +32
    -1
      frappe/core/doctype/role/role.json
  3. +63
    -1
      frappe/core/doctype/user/user.json
  4. +9
    -0
      frappe/core/doctype/user/user.py
  5. +3
    -0
      frappe/exceptions.py
  6. +44
    -2
      frappe/templates/includes/login/login.js
  7. +1
    -1
      frappe/www/login.py

+ 65
- 5
frappe/auth.py 查看文件

@@ -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


+ 32
- 1
frappe/core/doctype/role/role.json 查看文件

@@ -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",


+ 63
- 1
frappe/core/doctype/user/user.json 查看文件

@@ -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",


+ 9
- 0
frappe/core/doctype/user/user.py 查看文件

@@ -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:


+ 3
- 0
frappe/exceptions.py 查看文件

@@ -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




+ 44
- 2
frappe/templates/includes/login/login.js 查看文件

@@ -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;
});
}

+ 1
- 1
frappe/www/login.py 查看文件

@@ -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


正在加载...
取消
保存