You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

480 lines
14 KiB

  1. # Copyright (c) 2012 Web Notes Technologies Pvt Ltd (http://erpnext.com)
  2. #
  3. # MIT License (MIT)
  4. #
  5. # Permission is hereby granted, free of charge, to any person obtaining a
  6. # copy of this software and associated documentation files (the "Software"),
  7. # to deal in the Software without restriction, including without limitation
  8. # the rights to use, copy, modify, merge, publish, distribute, sublicense,
  9. # and/or sell copies of the Software, and to permit persons to whom the
  10. # Software is furnished to do so, subject to the following conditions:
  11. #
  12. # The above copyright notice and this permission notice shall be included in
  13. # all copies or substantial portions of the Software.
  14. #
  15. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
  16. # INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
  17. # PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
  18. # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
  19. # CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
  20. # OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  21. #
  22. import webnotes
  23. import webnotes.db
  24. import webnotes.utils
  25. import webnotes.profile
  26. import webnotes.defs
  27. # =================================================================================
  28. # HTTPRequest
  29. # =================================================================================
  30. class HTTPRequest:
  31. def __init__(self):
  32. # Get Environment variables
  33. self.domain = webnotes.get_env_vars('HTTP_HOST')
  34. if self.domain and self.domain.startswith('www.'):
  35. self.domain = self.domain[4:]
  36. webnotes.remote_ip = webnotes.get_env_vars('REMOTE_ADDR')
  37. # load cookies
  38. webnotes.cookie_manager = CookieManager()
  39. # set db
  40. self.set_db()
  41. # -----------------------------
  42. # start transaction
  43. webnotes.conn.begin()
  44. # login
  45. webnotes.login_manager = LoginManager()
  46. # start session
  47. webnotes.session_obj = Session()
  48. webnotes.session = webnotes.session_obj.data
  49. # check status
  50. if webnotes.conn.get_global("__session_status")=='stop':
  51. webnotes.msgprint(webnotes.conn.get_global("__session_status_message"))
  52. raise webnotes.SessionStopped('Session Stopped')
  53. # write out cookies if sid is supplied (this is a pre-logged in redirect)
  54. if webnotes.form_dict.get('sid'):
  55. webnotes.cookie_manager.set_cookies()
  56. # run login triggers
  57. if webnotes.form_dict.get('cmd')=='login':
  58. webnotes.login_manager.run_trigger('on_login_post_session')
  59. # load profile
  60. self.setup_profile()
  61. webnotes.conn.commit()
  62. # end transaction
  63. # -----------------------------
  64. # setup profile
  65. # -------------
  66. def setup_profile(self):
  67. webnotes.user = webnotes.profile.Profile()
  68. # load the profile data
  69. if webnotes.session['data'].get('profile'):
  70. webnotes.user.load_from_session(webnotes.session['data']['profile'])
  71. else:
  72. webnotes.user.load_profile()
  73. # set database login
  74. # ------------------
  75. def get_db_name(self):
  76. # highest priority if comes via form
  77. if webnotes.form_dict.get('ac_name'):
  78. db_name = webnotes.form_dict.get('ac_name')
  79. elif webnotes.form_dict.get('acx'):
  80. db_name = webnotes.form_dict.get('acx')
  81. # then from cookie
  82. elif webnotes.incoming_cookies.get('account_id'):
  83. db_name = webnotes.incoming_cookies.get('account_id')
  84. # then via defs
  85. elif hasattr(webnotes.defs, 'get_db_name'):
  86. db_name = webnotes.defs.get_db_name()
  87. # then default
  88. else:
  89. db_name = getattr(webnotes.defs,'default_db_name','')
  90. if not db_name:
  91. raise Exception, "Unable to resolve database name"
  92. return db_name
  93. def set_db(self, ac_name = None):
  94. """connect to db, from ac_name or db_name"""
  95. webnotes.conn = webnotes.db.Database(user = self.get_db_name(), \
  96. password = getattr(webnotes.defs,'db_password', ''))
  97. # =================================================================================
  98. # Login Manager
  99. # =================================================================================
  100. class LoginManager:
  101. def __init__(self):
  102. self.cp = None
  103. if webnotes.form_dict.get('cmd')=='login':
  104. # clear cache
  105. from webnotes.session_cache import clear_cache
  106. clear_cache(webnotes.form_dict.get('usr'))
  107. self.authenticate()
  108. self.post_login()
  109. webnotes.response['message'] = 'Logged In'
  110. # run triggers, write cookies
  111. # ---------------------------
  112. def post_login(self):
  113. self.run_trigger()
  114. self.validate_ip_address()
  115. self.validate_hour()
  116. # check password
  117. # --------------
  118. def authenticate(self, user=None, pwd=None):
  119. if not (user and pwd):
  120. user, pwd = webnotes.form_dict.get('usr'), webnotes.form_dict.get('pwd')
  121. if not (user and pwd):
  122. webnotes.response['message'] = 'Incomplete Login Details'
  123. raise webnotes.AuthenticationError
  124. # custom authentication (for single-sign on)
  125. self.load_control_panel()
  126. if hasattr(self.cp, 'authenticate'):
  127. self.user = self.cp.authenticate()
  128. # check the password
  129. if user=='Administrator':
  130. p = webnotes.conn.sql("""select name, first_name, last_name
  131. from tabProfile where name=%s
  132. and (`password`=%s OR `password`=PASSWORD(%s))""", (user, pwd, pwd), as_dict=1)
  133. else:
  134. p = webnotes.conn.sql("""select name, first_name, last_name
  135. from tabProfile where name=%s
  136. and (`password`=%s OR `password`=PASSWORD(%s))
  137. and IFNULL(enabled,0)=1""", (user, pwd, pwd), as_dict=1)
  138. if not p:
  139. webnotes.response['message'] = 'Authentication Failed'
  140. raise webnotes.AuthenticationError
  141. #webnotes.msgprint('Authentication Failed',raise_exception=1)
  142. p = p[0]
  143. self.user = p['name']
  144. self.user_fullname = (p.get('first_name') and (p.get('first_name') + ' ') or '') \
  145. + (p.get('last_name') or '')
  146. # triggers
  147. # --------
  148. def load_control_panel(self):
  149. import webnotes.model.code
  150. try:
  151. if not self.cp:
  152. self.cp = webnotes.model.code.get_obj('Control Panel')
  153. except Exception, e:
  154. webnotes.response['Control Panel Exception'] = webnotes.utils.getTraceback()
  155. # --------
  156. def run_trigger(self, method='on_login'):
  157. try:
  158. from startup import event_handlers
  159. if hasattr(event_handlers, method):
  160. getattr(event_handlers, method)(self)
  161. return
  162. except ImportError, e:
  163. webnotes.errprint(str(e))
  164. # deprecated
  165. self.load_control_panel()
  166. if self.cp and hasattr(self.cp, method):
  167. getattr(self.cp, method)(self)
  168. # ip validation
  169. # -------------
  170. def validate_ip_address(self):
  171. ip_list = webnotes.conn.get_value('Profile', self.user, 'restrict_ip', ignore=True)
  172. if not ip_list:
  173. return
  174. ip_list = ip_list.replace(",", "\n").split('\n')
  175. ip_list = [i.strip() for i in ip_list]
  176. for ip in ip_list:
  177. if webnotes.remote_ip.startswith(ip):
  178. return
  179. elif webnotes.form_dict.get('via_ip') and webnotes.form_dict.get('via_ip').startswith(ip):
  180. return
  181. webnotes.msgprint('Not allowed from this IP Address', raise_exception=1)
  182. def validate_hour(self):
  183. """
  184. check if user is logging in during restricted hours
  185. """
  186. login_before = int(webnotes.conn.get_value('Profile', self.user, 'login_before', ignore=True) or 0)
  187. login_after = int(webnotes.conn.get_value('Profile', self.user, 'login_after', ignore=True) or 0)
  188. if not (login_before or login_after):
  189. return
  190. from webnotes.utils import now_datetime
  191. current_hour = int(now_datetime().strftime('%H'))
  192. if login_before and current_hour > login_before:
  193. webnotes.msgprint('Not allowed to login after restricted hour', raise_exception=1)
  194. if login_after and current_hour < login_after:
  195. webnotes.msgprint('Not allowed to login before restricted hour', raise_exception=1)
  196. # login as guest
  197. # --------------
  198. def login_as_guest(self):
  199. self.user = 'Guest'
  200. self.post_login()
  201. # Logout
  202. # ------
  203. def call_on_logout_event(self):
  204. import webnotes.model.code
  205. cp = webnotes.model.code.get_obj('Control Panel', 'Control Panel')
  206. if hasattr(cp, 'on_logout'):
  207. cp.on_logout(self)
  208. def logout(self, arg='', user=None):
  209. if not user: user = webnotes.session.get('user')
  210. self.user = user
  211. self.run_trigger('on_logout')
  212. webnotes.conn.sql('delete from tabSessions where user=%s', user)
  213. # =================================================================================
  214. # Cookie Manager
  215. # =================================================================================
  216. class CookieManager:
  217. def __init__(self):
  218. import Cookie
  219. webnotes.cookies = Cookie.SimpleCookie()
  220. self.get_incoming_cookies()
  221. # get incoming cookies
  222. # --------------------
  223. def get_incoming_cookies(self):
  224. import os
  225. cookies = {}
  226. if 'HTTP_COOKIE' in os.environ:
  227. c = os.environ['HTTP_COOKIE']
  228. webnotes.cookies.load(c)
  229. for c in webnotes.cookies.values():
  230. cookies[c.key] = c.value
  231. webnotes.incoming_cookies = cookies
  232. # Set cookies
  233. # -----------
  234. def set_cookies(self):
  235. if webnotes.form_dict.get('cmd')=='logout':
  236. webnotes.cookies['account_id'] = ''
  237. else:
  238. webnotes.cookies['account_id'] = webnotes.conn.cur_db_name
  239. if webnotes.session.get('sid'):
  240. webnotes.cookies['sid'] = webnotes.session['sid']
  241. # sid expires in 3 days
  242. import datetime
  243. expires = datetime.datetime.now() + datetime.timedelta(days=3)
  244. webnotes.cookies['sid']['expires'] = expires.strftime('%a, %d %b %Y %H:%M:%S')
  245. # Set Remember Me
  246. # ---------------
  247. def set_remember_me(self):
  248. if webnotes.utils.cint(webnotes.form_dict.get('remember_me')):
  249. remember_days = webnotes.conn.get_value('Control Panel',None,'remember_for_days') or 7
  250. webnotes.cookies['remember_me'] = 1
  251. import datetime
  252. expires = datetime.datetime.now() + datetime.timedelta(days=remember_days)
  253. for k in webnotes.cookies.keys():
  254. webnotes.cookies[k]['expires'] = expires.strftime('%a, %d %b %Y %H:%M:%S')
  255. # =================================================================================
  256. # Session
  257. # =================================================================================
  258. class Session:
  259. def __init__(self, user=None):
  260. self.user = user
  261. self.sid = webnotes.form_dict.get('sid') or webnotes.incoming_cookies.get('sid')
  262. self.data = {'user':user,'data':{}}
  263. if webnotes.form_dict.get('cmd')=='login':
  264. self.start()
  265. return
  266. self.load()
  267. # start a session
  268. # ---------------
  269. def get_session_record(self):
  270. """get session record, or return the standard Guest Record"""
  271. r = webnotes.conn.sql("""select user, sessiondata, status from
  272. tabSessions where sid='%s'""" % self.sid)
  273. if not r:
  274. # return guest record if present
  275. self.sid = 'Guest'
  276. r = webnotes.conn.sql("""select user, sessiondata, status from
  277. tabSessions where sid='%s'""" % self.sid)
  278. return r
  279. def load(self):
  280. import webnotes
  281. r = self.get_session_record()
  282. if r:
  283. r=r[0]
  284. # ExipredSession
  285. if r[2]=='Expired' and (webnotes.form_dict.get('cmd')!='resume_session'):
  286. if r[0]=='Guest' or (not webnotes.form_dict.get('cmd')) or webnotes.form_dict.get('cmd')=='logout':
  287. self.start_as_guest()
  288. else:
  289. webnotes.response['session_status'] = 'Session Expired'
  290. raise Exception, 'Session Expired'
  291. elif r[2]=='Logged Out':
  292. self.start_as_guest()
  293. # allow refresh or logout
  294. if webnotes.form_dict.get('cmd') and webnotes.form_dict.get('cmd')!='logout':
  295. webnotes.response['session_status'] = 'Logged Out'
  296. raise Exception, 'Logged Out'
  297. else:
  298. self.data = {'data': (r[1] and eval(r[1]) or {}),
  299. 'user':r[0], 'sid': self.sid}
  300. else:
  301. self.start_as_guest()
  302. def start_as_guest(self):
  303. """all guests share the same 'Guest' session"""
  304. webnotes.login_manager.login_as_guest()
  305. self.start()
  306. # start a session
  307. # ---------------
  308. def start(self):
  309. import os
  310. import webnotes
  311. import webnotes.utils
  312. # generate sid
  313. if webnotes.login_manager.user=='Guest':
  314. sid = 'Guest'
  315. else:
  316. sid = webnotes.utils.generate_hash()
  317. self.data['user'] = webnotes.login_manager.user
  318. self.data['sid'] = sid
  319. self.data['data']['session_ip'] = os.environ.get('REMOTE_ADDR');
  320. self.data['data']['tenant_id'] = webnotes.form_dict.get('tenant_id', 0)
  321. # get ipinfo
  322. if webnotes.conn.get_global('get_ip_info'):
  323. self.get_ipinfo()
  324. # insert session
  325. try:
  326. self.insert_session_record()
  327. except Exception, e:
  328. if e.args[0]==1054:
  329. self.add_status_column()
  330. self.insert_session_record()
  331. else:
  332. raise e
  333. # update profile
  334. webnotes.conn.sql("UPDATE tabProfile SET last_login = '%s', last_ip = '%s' where name='%s'" % (webnotes.utils.now(), webnotes.remote_ip, self.data['user']))
  335. # set cookies to write
  336. webnotes.session = self.data
  337. webnotes.cookie_manager.set_cookies()
  338. # resume session
  339. # --------------
  340. def resume(self):
  341. pwd = webnotes.form_dict.get('pwd')
  342. webnotes.login_manager.authenticate(self.data['user'], pwd)
  343. webnotes.conn.sql("update tabSessions set status='Active' where sid=%s", self.data['sid'])
  344. return 'Logged In'
  345. # update session
  346. # --------------
  347. def update(self):
  348. # update session
  349. if webnotes.session['user'] != 'Guest':
  350. webnotes.conn.sql("""update tabSessions set sessiondata=%s, user=%s, lastupdate=NOW()
  351. where sid=%s""" , (str(self.data['data']), self.data['user'], self.data['sid']))
  352. self.check_expired()
  353. # check expired
  354. # -------------
  355. def check_expired(self):
  356. """expire non-guest sessions"""
  357. exp_sec = webnotes.conn.get_value('Control Panel', None, 'session_expiry') or '6:00:00'
  358. # set sessions as expired
  359. try:
  360. webnotes.conn.sql("""update tabSessions where TIMEDIFF(NOW(), lastupdate) >
  361. %s and sid!='Guest' SET `status`='Expired'""", exp_sec)
  362. except Exception, e:
  363. if e.args[0]==1054:
  364. self.add_status_column()
  365. # clear out old sessions
  366. webnotes.conn.sql("""delete from tabSessions where TIMEDIFF(NOW(), lastupdate)
  367. > '72:00:00' and sid!='Guest'""")
  368. # Get IP Info from ipinfodb.com
  369. # -----------------------------
  370. def get_ipinfo(self):
  371. import os
  372. try:
  373. import pygeoip
  374. except:
  375. return
  376. gi = pygeoip.GeoIP('data/GeoIP.dat')
  377. self.data['data']['ipinfo'] = {'countryName': gi.country_name_by_addr(os.environ.get('REMOTE_ADDR'))}
  378. # -----------------------------
  379. def insert_session_record(self):
  380. webnotes.conn.sql("insert into tabSessions (sessiondata, user, lastupdate, sid, status) values (%s , %s, NOW(), %s, 'Active')", (str(self.data['data']), self.data['user'], self.data['sid']))