Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.
 
 
 
 
 
 

439 řádky
14 KiB

  1. # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
  2. # MIT License. See license.txt
  3. from __future__ import unicode_literals
  4. import datetime
  5. from frappe import _
  6. import frappe
  7. import frappe.database
  8. import frappe.utils
  9. from frappe.utils import cint, flt, get_datetime, datetime, date_diff, today
  10. import frappe.utils.user
  11. from frappe import conf
  12. from frappe.sessions import Session, clear_sessions, delete_session
  13. from frappe.modules.patch_handler import check_session_stopped
  14. from frappe.translate import get_lang_code
  15. from frappe.utils.password import check_password, delete_login_failed_cache
  16. from frappe.core.doctype.activity_log.activity_log import add_authentication_log
  17. from frappe.twofactor import (should_run_2fa, authenticate_for_2factor,
  18. confirm_otp_token, get_cached_user_pass)
  19. from frappe.website.utils import get_home_page
  20. from six.moves.urllib.parse import quote
  21. class HTTPRequest:
  22. def __init__(self):
  23. # Get Environment variables
  24. self.domain = frappe.request.host
  25. if self.domain and self.domain.startswith('www.'):
  26. self.domain = self.domain[4:]
  27. if frappe.get_request_header('X-Forwarded-For'):
  28. frappe.local.request_ip = (frappe.get_request_header('X-Forwarded-For').split(",")[0]).strip()
  29. elif frappe.get_request_header('REMOTE_ADDR'):
  30. frappe.local.request_ip = frappe.get_request_header('REMOTE_ADDR')
  31. else:
  32. frappe.local.request_ip = '127.0.0.1'
  33. # language
  34. self.set_lang()
  35. # load cookies
  36. frappe.local.cookie_manager = CookieManager()
  37. # set db
  38. self.connect()
  39. # login
  40. frappe.local.login_manager = LoginManager()
  41. if frappe.form_dict._lang:
  42. lang = get_lang_code(frappe.form_dict._lang)
  43. if lang:
  44. frappe.local.lang = lang
  45. self.validate_csrf_token()
  46. # write out latest cookies
  47. frappe.local.cookie_manager.init_cookies()
  48. # check status
  49. check_session_stopped()
  50. def validate_csrf_token(self):
  51. if frappe.local.request and frappe.local.request.method in ("POST", "PUT", "DELETE"):
  52. if not frappe.local.session: return
  53. if not frappe.local.session.data.csrf_token \
  54. or frappe.local.session.data.device=="mobile" \
  55. or frappe.conf.get('ignore_csrf', None):
  56. # not via boot
  57. return
  58. csrf_token = frappe.get_request_header("X-Frappe-CSRF-Token")
  59. if not csrf_token and "csrf_token" in frappe.local.form_dict:
  60. csrf_token = frappe.local.form_dict.csrf_token
  61. del frappe.local.form_dict["csrf_token"]
  62. if frappe.local.session.data.csrf_token != csrf_token:
  63. frappe.local.flags.disable_traceback = True
  64. frappe.throw(_("Invalid Request"), frappe.CSRFTokenError)
  65. def set_lang(self):
  66. from frappe.translate import guess_language
  67. frappe.local.lang = guess_language()
  68. def get_db_name(self):
  69. """get database name from conf"""
  70. return conf.db_name
  71. def connect(self, ac_name = None):
  72. """connect to db, from ac_name or db_name"""
  73. frappe.local.db = frappe.database.get_db(user = self.get_db_name(), \
  74. password = getattr(conf, 'db_password', ''))
  75. class LoginManager:
  76. def __init__(self):
  77. self.user = None
  78. self.info = None
  79. self.full_name = None
  80. self.user_type = None
  81. if frappe.local.form_dict.get('cmd')=='login' or frappe.local.request.path=="/api/method/login":
  82. if self.login()==False: return
  83. self.resume = False
  84. # run login triggers
  85. self.run_trigger('on_session_creation')
  86. else:
  87. try:
  88. self.resume = True
  89. self.make_session(resume=True)
  90. self.get_user_info()
  91. self.set_user_info(resume=True)
  92. except AttributeError:
  93. self.user = "Guest"
  94. self.get_user_info()
  95. self.make_session()
  96. self.set_user_info()
  97. def login(self):
  98. # clear cache
  99. frappe.clear_cache(user = frappe.form_dict.get('usr'))
  100. user, pwd = get_cached_user_pass()
  101. self.authenticate(user=user, pwd=pwd)
  102. if self.force_user_to_reset_password():
  103. doc = frappe.get_doc("User", self.user)
  104. frappe.local.response["redirect_to"] = doc.reset_password(send_email=False, password_expired=True)
  105. frappe.local.response["message"] = "Password Reset"
  106. return False
  107. if should_run_2fa(self.user):
  108. authenticate_for_2factor(self.user)
  109. if not confirm_otp_token(self):
  110. return False
  111. self.post_login()
  112. def post_login(self):
  113. self.run_trigger('on_login')
  114. validate_ip_address(self.user)
  115. self.validate_hour()
  116. self.get_user_info()
  117. self.make_session()
  118. self.setup_boot_cache()
  119. self.set_user_info()
  120. def get_user_info(self, resume=False):
  121. self.info = frappe.db.get_value("User", self.user,
  122. ["user_type", "first_name", "last_name", "user_image"], as_dict=1)
  123. self.user_type = self.info.user_type
  124. def setup_boot_cache(self):
  125. frappe.cache_manager.build_table_count_cache()
  126. frappe.cache_manager.build_domain_restriced_doctype_cache()
  127. frappe.cache_manager.build_domain_restriced_page_cache()
  128. def set_user_info(self, resume=False):
  129. # set sid again
  130. frappe.local.cookie_manager.init_cookies()
  131. self.full_name = " ".join(filter(None, [self.info.first_name,
  132. self.info.last_name]))
  133. if self.info.user_type=="Website User":
  134. frappe.local.cookie_manager.set_cookie("system_user", "no")
  135. if not resume:
  136. frappe.local.response["message"] = "No App"
  137. frappe.local.response["home_page"] = '/' + get_home_page()
  138. else:
  139. frappe.local.cookie_manager.set_cookie("system_user", "yes")
  140. if not resume:
  141. frappe.local.response['message'] = 'Logged In'
  142. frappe.local.response["home_page"] = "/app"
  143. if not resume:
  144. frappe.response["full_name"] = self.full_name
  145. # redirect information
  146. redirect_to = frappe.cache().hget('redirect_after_login', self.user)
  147. if redirect_to:
  148. frappe.local.response["redirect_to"] = redirect_to
  149. frappe.cache().hdel('redirect_after_login', self.user)
  150. frappe.local.cookie_manager.set_cookie("full_name", self.full_name)
  151. frappe.local.cookie_manager.set_cookie("user_id", self.user)
  152. frappe.local.cookie_manager.set_cookie("user_image", self.info.user_image or "")
  153. def make_session(self, resume=False):
  154. # start session
  155. frappe.local.session_obj = Session(user=self.user, resume=resume,
  156. full_name=self.full_name, user_type=self.user_type)
  157. # reset user if changed to Guest
  158. self.user = frappe.local.session_obj.user
  159. frappe.local.session = frappe.local.session_obj.data
  160. self.clear_active_sessions()
  161. def clear_active_sessions(self):
  162. """Clear other sessions of the current user if `deny_multiple_sessions` is not set"""
  163. if not (cint(frappe.conf.get("deny_multiple_sessions")) or cint(frappe.db.get_system_setting('deny_multiple_sessions'))):
  164. return
  165. if frappe.session.user != "Guest":
  166. clear_sessions(frappe.session.user, keep_current=True)
  167. def authenticate(self, user=None, pwd=None):
  168. if not (user and pwd):
  169. user, pwd = frappe.form_dict.get('usr'), frappe.form_dict.get('pwd')
  170. if not (user and pwd):
  171. self.fail(_('Incomplete login details'), user=user)
  172. if cint(frappe.db.get_value("System Settings", "System Settings", "allow_login_using_mobile_number")):
  173. user = frappe.db.get_value("User", filters={"mobile_no": user}, fieldname="name") or user
  174. if cint(frappe.db.get_value("System Settings", "System Settings", "allow_login_using_user_name")):
  175. user = frappe.db.get_value("User", filters={"username": user}, fieldname="name") or user
  176. self.check_if_enabled(user)
  177. if not frappe.form_dict.get('tmp_id'):
  178. self.user = self.check_password(user, pwd)
  179. else:
  180. self.user = user
  181. def force_user_to_reset_password(self):
  182. if not self.user:
  183. return
  184. from frappe.core.doctype.user.user import STANDARD_USERS
  185. if self.user in STANDARD_USERS:
  186. return False
  187. reset_pwd_after_days = cint(frappe.db.get_single_value("System Settings",
  188. "force_user_to_reset_password"))
  189. if reset_pwd_after_days:
  190. last_password_reset_date = frappe.db.get_value("User",
  191. self.user, "last_password_reset_date") or today()
  192. last_pwd_reset_days = date_diff(today(), last_password_reset_date)
  193. if last_pwd_reset_days > reset_pwd_after_days:
  194. return True
  195. def check_if_enabled(self, user):
  196. """raise exception if user not enabled"""
  197. doc = frappe.get_doc("System Settings")
  198. if cint(doc.allow_consecutive_login_attempts) > 0:
  199. check_consecutive_login_attempts(user, doc)
  200. if user=='Administrator': return
  201. if not cint(frappe.db.get_value('User', user, 'enabled')):
  202. self.fail('User disabled or missing', user=user)
  203. def check_password(self, user, pwd):
  204. """check password"""
  205. try:
  206. # returns user in correct case
  207. return check_password(user, pwd)
  208. except frappe.AuthenticationError:
  209. self.update_invalid_login(user)
  210. self.fail('Incorrect password', user=user)
  211. def fail(self, message, user=None):
  212. if not user:
  213. user = _('Unknown User')
  214. frappe.local.response['message'] = message
  215. add_authentication_log(message, user, status="Failed")
  216. frappe.db.commit()
  217. raise frappe.AuthenticationError
  218. def update_invalid_login(self, user):
  219. last_login_tried = get_last_tried_login_data(user)
  220. failed_count = 0
  221. if last_login_tried > get_datetime():
  222. failed_count = get_login_failed_count(user)
  223. frappe.cache().hset('login_failed_count', user, failed_count + 1)
  224. def run_trigger(self, event='on_login'):
  225. for method in frappe.get_hooks().get(event, []):
  226. frappe.call(frappe.get_attr(method), login_manager=self)
  227. def validate_hour(self):
  228. """check if user is logging in during restricted hours"""
  229. login_before = int(frappe.db.get_value('User', self.user, 'login_before', ignore=True) or 0)
  230. login_after = int(frappe.db.get_value('User', self.user, 'login_after', ignore=True) or 0)
  231. if not (login_before or login_after):
  232. return
  233. from frappe.utils import now_datetime
  234. current_hour = int(now_datetime().strftime('%H'))
  235. if login_before and current_hour > login_before:
  236. frappe.throw(_("Login not allowed at this time"), frappe.AuthenticationError)
  237. if login_after and current_hour < login_after:
  238. frappe.throw(_("Login not allowed at this time"), frappe.AuthenticationError)
  239. def login_as_guest(self):
  240. """login as guest"""
  241. self.login_as("Guest")
  242. def login_as(self, user):
  243. self.user = user
  244. self.post_login()
  245. def logout(self, arg='', user=None):
  246. if not user: user = frappe.session.user
  247. self.run_trigger('on_logout')
  248. if user == frappe.session.user:
  249. delete_session(frappe.session.sid, user=user, reason="User Manually Logged Out")
  250. self.clear_cookies()
  251. else:
  252. clear_sessions(user)
  253. def clear_cookies(self):
  254. clear_cookies()
  255. class CookieManager:
  256. def __init__(self):
  257. self.cookies = {}
  258. self.to_delete = []
  259. def init_cookies(self):
  260. if not frappe.local.session.get('sid'): return
  261. # sid expires in 3 days
  262. expires = datetime.datetime.now() + datetime.timedelta(days=3)
  263. if frappe.session.sid:
  264. self.set_cookie("sid", frappe.session.sid, expires=expires, httponly=True)
  265. if frappe.session.session_country:
  266. self.set_cookie("country", frappe.session.session_country)
  267. def set_cookie(self, key, value, expires=None, secure=False, httponly=False, samesite="Lax"):
  268. if not secure and hasattr(frappe.local, 'request'):
  269. secure = frappe.local.request.scheme == "https"
  270. # Cordova does not work with Lax
  271. if frappe.local.session.data.device == "mobile":
  272. samesite = None
  273. self.cookies[key] = {
  274. "value": value,
  275. "expires": expires,
  276. "secure": secure,
  277. "httponly": httponly,
  278. "samesite": samesite
  279. }
  280. def delete_cookie(self, to_delete):
  281. if not isinstance(to_delete, (list, tuple)):
  282. to_delete = [to_delete]
  283. self.to_delete.extend(to_delete)
  284. def flush_cookies(self, response):
  285. for key, opts in self.cookies.items():
  286. response.set_cookie(key, quote((opts.get("value") or "").encode('utf-8')),
  287. expires=opts.get("expires"),
  288. secure=opts.get("secure"),
  289. httponly=opts.get("httponly"),
  290. samesite=opts.get("samesite"))
  291. # expires yesterday!
  292. expires = datetime.datetime.now() + datetime.timedelta(days=-1)
  293. for key in set(self.to_delete):
  294. response.set_cookie(key, "", expires=expires)
  295. @frappe.whitelist()
  296. def get_logged_user():
  297. return frappe.session.user
  298. def clear_cookies():
  299. if hasattr(frappe.local, "session"):
  300. frappe.session.sid = ""
  301. frappe.local.cookie_manager.delete_cookie(["full_name", "user_id", "sid", "user_image", "system_user"])
  302. def get_last_tried_login_data(user, get_last_login=False):
  303. locked_account_time = frappe.cache().hget('locked_account_time', user)
  304. if get_last_login and locked_account_time:
  305. return locked_account_time
  306. last_login_tried = frappe.cache().hget('last_login_tried', user)
  307. if not last_login_tried or last_login_tried < get_datetime():
  308. last_login_tried = get_datetime() + datetime.timedelta(seconds=60)
  309. frappe.cache().hset('last_login_tried', user, last_login_tried)
  310. return last_login_tried
  311. def get_login_failed_count(user):
  312. return cint(frappe.cache().hget('login_failed_count', user)) or 0
  313. def check_consecutive_login_attempts(user, doc):
  314. login_failed_count = get_login_failed_count(user)
  315. last_login_tried = (get_last_tried_login_data(user, True)
  316. + datetime.timedelta(seconds=doc.allow_login_after_fail))
  317. if login_failed_count >= cint(doc.allow_consecutive_login_attempts):
  318. locked_account_time = frappe.cache().hget('locked_account_time', user)
  319. if not locked_account_time:
  320. frappe.cache().hset('locked_account_time', user, get_datetime())
  321. if last_login_tried > get_datetime():
  322. frappe.throw(_("Your account has been locked and will resume after {0} seconds")
  323. .format(doc.allow_login_after_fail), frappe.SecurityException)
  324. else:
  325. delete_login_failed_cache(user)
  326. def validate_ip_address(user):
  327. """check if IP Address is valid"""
  328. user = frappe.get_cached_doc("User", user) if not frappe.flags.in_test else frappe.get_doc("User", user)
  329. ip_list = user.get_restricted_ip_list()
  330. if not ip_list:
  331. return
  332. system_settings = frappe.get_cached_doc("System Settings") if not frappe.flags.in_test else frappe.get_single("System Settings")
  333. # check if bypass restrict ip is enabled for all users
  334. bypass_restrict_ip_check = system_settings.bypass_restrict_ip_check_if_2fa_enabled
  335. # check if two factor auth is enabled
  336. if system_settings.enable_two_factor_auth and not bypass_restrict_ip_check:
  337. # check if bypass restrict ip is enabled for login user
  338. bypass_restrict_ip_check = user.bypass_restrict_ip_check_if_2fa_enabled
  339. for ip in ip_list:
  340. if frappe.local.request_ip.startswith(ip) or bypass_restrict_ip_check:
  341. return
  342. frappe.throw(_("Access not allowed from this IP Address"), frappe.AuthenticationError)