Added CSRF token verification for desk
This commit is contained in:
parent
11708bbcda
commit
6084775ed0
11 changed files with 56 additions and 28 deletions
|
|
@ -66,7 +66,6 @@ db = local("db")
|
|||
conf = local("conf")
|
||||
form = form_dict = local("form_dict")
|
||||
request = local("request")
|
||||
request_method = local("request_method")
|
||||
response = local("response")
|
||||
session = local("session")
|
||||
user = local("user")
|
||||
|
|
@ -109,7 +108,6 @@ def init(site, sites_path=None):
|
|||
local.sites_path = sites_path
|
||||
local.site_path = os.path.join(sites_path, site)
|
||||
|
||||
local.request_method = request.method if request else None
|
||||
local.request_ip = None
|
||||
local.response = _dict({"docs":[]})
|
||||
local.task_id = None
|
||||
|
|
|
|||
|
|
@ -99,8 +99,12 @@ def application(request):
|
|||
if frappe.local.is_ajax or 'application/json' in request.headers.get('Accept', ''):
|
||||
response = frappe.utils.response.report_error(http_status_code)
|
||||
else:
|
||||
traceback = "<pre>"+frappe.get_traceback()+"</pre>"
|
||||
if frappe.local.flags.disable_traceback:
|
||||
traceback = ""
|
||||
|
||||
frappe.respond_as_web_page("Server Error",
|
||||
"<pre>"+frappe.get_traceback()+"</pre>",
|
||||
traceback,
|
||||
http_status_code=http_status_code)
|
||||
response = frappe.website.render.render("message", http_status_code=http_status_code)
|
||||
|
||||
|
|
|
|||
|
|
@ -37,17 +37,14 @@ class HTTPRequest:
|
|||
# load cookies
|
||||
frappe.local.cookie_manager = CookieManager()
|
||||
|
||||
# override request method. All request to be of type POST, but if _type == "POST" then commit
|
||||
if frappe.form_dict.get("_type"):
|
||||
frappe.local.request_method = frappe.form_dict.get("_type")
|
||||
del frappe.form_dict["_type"]
|
||||
|
||||
# set db
|
||||
self.connect()
|
||||
|
||||
# login
|
||||
frappe.local.login_manager = LoginManager()
|
||||
|
||||
self.validate_csrf_token()
|
||||
|
||||
# write out latest cookies
|
||||
frappe.local.cookie_manager.init_cookies()
|
||||
|
||||
|
|
@ -58,6 +55,15 @@ class HTTPRequest:
|
|||
if frappe.form_dict.get('cmd')=='login':
|
||||
frappe.local.login_manager.run_trigger('on_session_creation')
|
||||
|
||||
def validate_csrf_token(self):
|
||||
if frappe.local.request and frappe.local.request.method=="POST":
|
||||
if not frappe.local.session.data.csrf_token:
|
||||
# not via boot
|
||||
return
|
||||
|
||||
if frappe.local.session.data.csrf_token != frappe.get_request_header("X-Frappe-CSRF-Token"):
|
||||
frappe.local.flags.disable_traceback = True
|
||||
frappe.throw(_("Invalid Request"), frappe.CSRFTokenError)
|
||||
|
||||
def set_lang(self, lang_codes):
|
||||
from frappe.translate import guess_language
|
||||
|
|
@ -81,8 +87,10 @@ class LoginManager:
|
|||
|
||||
if frappe.local.form_dict.get('cmd')=='login' or frappe.local.request.path=="/api/method/login":
|
||||
self.login()
|
||||
self.resume = False
|
||||
else:
|
||||
try:
|
||||
self.resume = True
|
||||
self.make_session(resume=True)
|
||||
self.set_user_info(resume=True)
|
||||
except AttributeError:
|
||||
|
|
|
|||
|
|
@ -35,6 +35,9 @@ class UnsupportedMediaType(Exception):
|
|||
class Redirect(Exception):
|
||||
http_status_code = 301
|
||||
|
||||
class CSRFTokenError(Exception):
|
||||
http_status_code = 400
|
||||
|
||||
class DuplicateEntryError(NameError):pass
|
||||
class DataError(ValidationError): pass
|
||||
class UnknownDomainError(Exception): pass
|
||||
|
|
@ -53,4 +56,4 @@ class EmptyTableError(ValidationError): pass
|
|||
class LinkExistsError(ValidationError): pass
|
||||
class InvalidEmailAddressError(ValidationError): pass
|
||||
class TemplateNotFoundError(ValidationError): pass
|
||||
class UniqueValidationError(ValidationError): pass
|
||||
class UniqueValidationError(ValidationError): pass
|
||||
|
|
|
|||
|
|
@ -62,9 +62,6 @@ frappe.call = function(opts) {
|
|||
frappe.request.call = function(opts) {
|
||||
frappe.request.prepare(opts);
|
||||
|
||||
// all requests will be post, set _type as POST for commit
|
||||
opts.args._type = opts.type;
|
||||
|
||||
var statusCode = {
|
||||
200: function(data, xhr) {
|
||||
if(typeof data === "string") data = JSON.parse(data);
|
||||
|
|
@ -121,9 +118,10 @@ frappe.request.call = function(opts) {
|
|||
var ajax_args = {
|
||||
url: opts.url || frappe.request.url,
|
||||
data: opts.args,
|
||||
type: 'POST',
|
||||
type: opts.type,
|
||||
dataType: opts.dataType || 'json',
|
||||
async: opts.async
|
||||
async: opts.async,
|
||||
headers: { "X-Frappe-CSRF-Token": frappe.boot.csrf_token }
|
||||
};
|
||||
|
||||
frappe.last_request = ajax_args.data;
|
||||
|
|
|
|||
|
|
@ -68,6 +68,7 @@ def delete_session(sid=None, user=None):
|
|||
frappe.cache().hdel("session", sid)
|
||||
frappe.cache().hdel("last_db_session_update", sid)
|
||||
frappe.db.sql("""delete from tabSessions where sid=%s""", sid)
|
||||
frappe.db.commit()
|
||||
|
||||
def clear_all_sessions():
|
||||
"""This effectively logs out all users"""
|
||||
|
|
@ -127,8 +128,19 @@ def get():
|
|||
bootinfo["lang"] = frappe.translate.get_user_lang()
|
||||
bootinfo["dev_server"] = os.environ.get('DEV_SERVER', False)
|
||||
bootinfo["disable_async"] = frappe.conf.disable_async
|
||||
|
||||
return bootinfo
|
||||
|
||||
def get_csrf_token():
|
||||
if not frappe.local.session.data.csrf_token:
|
||||
generate_csrf_token()
|
||||
|
||||
return frappe.local.session.data.csrf_token
|
||||
|
||||
def generate_csrf_token():
|
||||
frappe.local.session.data.csrf_token = frappe.generate_hash()
|
||||
frappe.local.session_obj.update(force=True)
|
||||
|
||||
class Session:
|
||||
def __init__(self, user, resume=False, full_name=None, user_type=None):
|
||||
self.sid = cstr(frappe.form_dict.get('sid') or
|
||||
|
|
@ -145,6 +157,7 @@ class Session:
|
|||
|
||||
if resume:
|
||||
self.resume()
|
||||
|
||||
else:
|
||||
if self.user:
|
||||
self.start()
|
||||
|
|
@ -168,7 +181,7 @@ class Session:
|
|||
"full_name": self.full_name,
|
||||
"user_type": self.user_type,
|
||||
"device": self.device,
|
||||
"session_country": get_geo_ip_country(frappe.local.request_ip) if frappe.local.request_ip else None
|
||||
"session_country": get_geo_ip_country(frappe.local.request_ip) if frappe.local.request_ip else None,
|
||||
})
|
||||
|
||||
# insert session
|
||||
|
|
@ -178,6 +191,7 @@ class Session:
|
|||
# update user
|
||||
frappe.db.sql("""UPDATE tabUser SET last_login = %s, last_ip = %s
|
||||
where name=%s""", (frappe.utils.now(), frappe.local.request_ip, self.data['user']))
|
||||
|
||||
frappe.db.commit()
|
||||
|
||||
def insert_session_record(self):
|
||||
|
|
@ -194,10 +208,12 @@ class Session:
|
|||
import frappe
|
||||
|
||||
data = self.get_session_record()
|
||||
|
||||
if data:
|
||||
# set language
|
||||
self.data.update({'data': data, 'user':data.user, 'sid': self.sid})
|
||||
self.user = data.user
|
||||
self.device = data.device
|
||||
else:
|
||||
self.start_as_guest()
|
||||
|
||||
|
|
@ -300,12 +316,13 @@ class Session:
|
|||
return updated_in_db
|
||||
|
||||
def get_expiry_period(device="desktop"):
|
||||
if device=="desktop":
|
||||
key = "session_expiry"
|
||||
default = "06:00:00"
|
||||
else:
|
||||
if device=="mobile":
|
||||
key = "session_expiry_mobile"
|
||||
default = "720:00:00"
|
||||
else:
|
||||
key = "session_expiry"
|
||||
default = "06:00:00"
|
||||
|
||||
exp_sec = frappe.defaults.get_global_default(key) or default
|
||||
|
||||
# incase seconds is missing
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ login.bind_events = function() {
|
|||
args.cmd = "login";
|
||||
args.usr = ($("#login_email").val() || "").trim();
|
||||
args.pwd = $("#login_password").val();
|
||||
args.device = "desktop";
|
||||
if(!args.usr || !args.pwd) {
|
||||
frappe.msgprint(__("Both login and password required"));
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -19,10 +19,14 @@ def get_context(context):
|
|||
|
||||
hooks = frappe.get_hooks()
|
||||
boot = frappe.sessions.get()
|
||||
boot_json = frappe.as_json(boot)
|
||||
|
||||
# this needs commit
|
||||
boot["csrf_token"] = frappe.sessions.get_csrf_token()
|
||||
|
||||
frappe.db.commit()
|
||||
|
||||
boot_json = frappe.as_json(boot)
|
||||
|
||||
# remove script tags from boot
|
||||
boot_json = re.sub("\<script\>[^<]*\</script\>", "", boot_json)
|
||||
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ from werkzeug.wrappers import Response
|
|||
from werkzeug.exceptions import NotFound, Forbidden
|
||||
|
||||
def report_error(status_code):
|
||||
if status_code!=404 or frappe.conf.logging:
|
||||
if (status_code!=404 or frappe.conf.logging) and not frappe.local.flags.disable_traceback:
|
||||
frappe.errprint(frappe.utils.get_traceback())
|
||||
|
||||
response = build_response("json")
|
||||
|
|
|
|||
|
|
@ -66,11 +66,6 @@ $.extend(frappe, {
|
|||
|
||||
if(!opts.args) opts.args = {};
|
||||
|
||||
// get or post?
|
||||
if(!opts.args._type) {
|
||||
opts.args._type = opts.type || "GET";
|
||||
}
|
||||
|
||||
// method
|
||||
if(opts.method) {
|
||||
opts.args.cmd = opts.method;
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ io.on('connection', function(socket){
|
|||
return;
|
||||
}
|
||||
// console.log("firing get_user_info");
|
||||
request.post(get_url(socket, '/api/method/frappe.async.get_user_info'))
|
||||
request.get(get_url(socket, '/api/method/frappe.async.get_user_info'))
|
||||
.type('form')
|
||||
.send({
|
||||
sid: sid
|
||||
|
|
@ -63,7 +63,7 @@ io.on('connection', function(socket){
|
|||
|
||||
socket.on('doc_subscribe', function(doctype, docname) {
|
||||
// console.log('trying to subscribe', doctype, docname)
|
||||
request.post(get_url(socket, '/api/method/frappe.async.can_subscribe_doc'))
|
||||
request.get(get_url(socket, '/api/method/frappe.async.can_subscribe_doc'))
|
||||
.type('form')
|
||||
.send({
|
||||
sid: sid,
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue