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.
 
 
 
 
 
 

416 line
12 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 conf
  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. # load profile
  54. self.setup_profile()
  55. # run login triggers
  56. if webnotes.form_dict.get('cmd')=='login':
  57. webnotes.login_manager.run_trigger('on_login_post_session')
  58. # write out cookies
  59. webnotes.cookie_manager.set_cookies()
  60. webnotes.conn.commit()
  61. # end transaction
  62. # -----------------------------
  63. # setup profile
  64. # -------------
  65. def setup_profile(self):
  66. webnotes.user = webnotes.profile.Profile()
  67. # load the profile data
  68. if not webnotes.session['data'].get('profile'):
  69. webnotes.session['data']['profile'] = webnotes.user.load_profile()
  70. webnotes.user.load_from_session(webnotes.session['data']['profile'])
  71. # set database login
  72. # ------------------
  73. def get_db_name(self):
  74. """get database name from conf"""
  75. return conf.db_name
  76. def set_db(self, ac_name = None):
  77. """connect to db, from ac_name or db_name"""
  78. webnotes.conn = webnotes.db.Database(user = self.get_db_name(), \
  79. password = getattr(conf,'db_password', ''))
  80. # =================================================================================
  81. # Login Manager
  82. # =================================================================================
  83. class LoginManager:
  84. def __init__(self):
  85. self.cp = None
  86. if webnotes.form_dict.get('cmd')=='login':
  87. # clear cache
  88. from webnotes.session_cache import clear_cache
  89. clear_cache(webnotes.form_dict.get('usr'))
  90. self.authenticate()
  91. self.post_login()
  92. webnotes.response['message'] = 'Logged In'
  93. # run triggers, write cookies
  94. # ---------------------------
  95. def post_login(self):
  96. self.run_trigger()
  97. self.validate_ip_address()
  98. self.validate_hour()
  99. # check password
  100. # --------------
  101. def authenticate(self, user=None, pwd=None):
  102. if not (user and pwd):
  103. user, pwd = webnotes.form_dict.get('usr'), webnotes.form_dict.get('pwd')
  104. if not (user and pwd):
  105. webnotes.response['message'] = 'Incomplete Login Details'
  106. raise webnotes.AuthenticationError
  107. # custom authentication (for single-sign on)
  108. self.load_control_panel()
  109. if hasattr(self.cp, 'authenticate'):
  110. self.user = self.cp.authenticate()
  111. # check the password
  112. if user=='Administrator':
  113. p = webnotes.conn.sql("""select name, first_name, last_name
  114. from tabProfile where name=%s
  115. and (`password`=%s OR `password`=PASSWORD(%s))""", (user, pwd, pwd), as_dict=1)
  116. else:
  117. p = webnotes.conn.sql("""select name, first_name, last_name
  118. from tabProfile where name=%s
  119. and (`password`=%s OR `password`=PASSWORD(%s))
  120. and IFNULL(enabled,0)=1""", (user, pwd, pwd), as_dict=1)
  121. if not p:
  122. webnotes.response['message'] = 'Authentication Failed'
  123. raise webnotes.AuthenticationError
  124. #webnotes.msgprint('Authentication Failed',raise_exception=1)
  125. p = p[0]
  126. self.user = p['name']
  127. self.user_fullname = (p.get('first_name') and (p.get('first_name') + ' ') or '') \
  128. + (p.get('last_name') or '')
  129. # triggers
  130. # --------
  131. def load_control_panel(self):
  132. import webnotes.model.code
  133. try:
  134. if not self.cp:
  135. self.cp = webnotes.model.code.get_obj('Control Panel')
  136. except Exception, e:
  137. webnotes.response['Control Panel Exception'] = webnotes.utils.getTraceback()
  138. # --------
  139. def run_trigger(self, method='on_login'):
  140. try:
  141. from startup import event_handlers
  142. if hasattr(event_handlers, method):
  143. getattr(event_handlers, method)(self)
  144. return
  145. except ImportError, e:
  146. pass
  147. # deprecated
  148. self.load_control_panel()
  149. if self.cp and hasattr(self.cp, method):
  150. getattr(self.cp, method)(self)
  151. # ip validation
  152. # -------------
  153. def validate_ip_address(self):
  154. ip_list = webnotes.conn.get_value('Profile', self.user, 'restrict_ip', ignore=True)
  155. if not ip_list:
  156. return
  157. ip_list = ip_list.replace(",", "\n").split('\n')
  158. ip_list = [i.strip() for i in ip_list]
  159. for ip in ip_list:
  160. if webnotes.remote_ip.startswith(ip):
  161. return
  162. webnotes.msgprint('Not allowed from this IP Address')
  163. raise webnotes.AuthenticationError
  164. def validate_hour(self):
  165. """
  166. check if user is logging in during restricted hours
  167. """
  168. login_before = int(webnotes.conn.get_value('Profile', self.user, 'login_before', ignore=True) or 0)
  169. login_after = int(webnotes.conn.get_value('Profile', self.user, 'login_after', ignore=True) or 0)
  170. if not (login_before or login_after):
  171. return
  172. from webnotes.utils import now_datetime
  173. current_hour = int(now_datetime().strftime('%H'))
  174. if login_before and current_hour > login_before:
  175. webnotes.msgprint('Not allowed to login after restricted hour', raise_exception=1)
  176. if login_after and current_hour < login_after:
  177. webnotes.msgprint('Not allowed to login before restricted hour', raise_exception=1)
  178. # login as guest
  179. # --------------
  180. def login_as_guest(self):
  181. self.user = 'Guest'
  182. self.post_login()
  183. # Logout
  184. # ------
  185. def call_on_logout_event(self):
  186. import webnotes.model.code
  187. cp = webnotes.model.code.get_obj('Control Panel', 'Control Panel')
  188. if hasattr(cp, 'on_logout'):
  189. cp.on_logout(self)
  190. def logout(self, arg='', user=None):
  191. if not user: user = webnotes.session.get('user')
  192. self.user = user
  193. self.run_trigger('on_logout')
  194. if user=='demo@webnotestech.com':
  195. webnotes.conn.sql('delete from tabSessions where sid=%s', webnotes.session.get('sid'))
  196. else:
  197. webnotes.conn.sql('delete from tabSessions where user=%s', user)
  198. # =================================================================================
  199. # Cookie Manager
  200. # =================================================================================
  201. class CookieManager:
  202. def __init__(self):
  203. import Cookie
  204. webnotes.cookies = Cookie.SimpleCookie()
  205. self.get_incoming_cookies()
  206. # get incoming cookies
  207. # --------------------
  208. def get_incoming_cookies(self):
  209. import os
  210. cookies = {}
  211. if 'HTTP_COOKIE' in os.environ:
  212. c = os.environ['HTTP_COOKIE']
  213. webnotes.cookies.load(c)
  214. for c in webnotes.cookies.values():
  215. cookies[c.key] = c.value
  216. webnotes.incoming_cookies = cookies
  217. # Set cookies
  218. # -----------
  219. def set_cookies(self):
  220. if webnotes.session.get('sid'):
  221. webnotes.cookies['sid'] = webnotes.session['sid']
  222. # sid expires in 3 days
  223. import datetime
  224. expires = datetime.datetime.now() + datetime.timedelta(days=3)
  225. webnotes.cookies['sid']['expires'] = expires.strftime('%a, %d %b %Y %H:%M:%S')
  226. webnotes.cookies['sid']['path'] = '/'
  227. # Set Remember Me
  228. # ---------------
  229. def set_remember_me(self):
  230. if webnotes.utils.cint(webnotes.form_dict.get('remember_me')):
  231. remember_days = webnotes.conn.get_value('Control Panel',None,'remember_for_days') or 7
  232. webnotes.cookies['remember_me'] = 1
  233. import datetime
  234. expires = datetime.datetime.now() + datetime.timedelta(days=remember_days)
  235. for k in webnotes.cookies.keys():
  236. webnotes.cookies[k]['expires'] = expires.strftime('%a, %d %b %Y %H:%M:%S')
  237. # =================================================================================
  238. # Session
  239. # =================================================================================
  240. class Session:
  241. def __init__(self, user=None):
  242. self.user = user
  243. self.sid = webnotes.form_dict.get('sid') or webnotes.incoming_cookies.get('sid', 'Guest')
  244. self.data = {'user':user,'data':{}}
  245. if webnotes.form_dict.get('cmd')=='login':
  246. self.start()
  247. return
  248. self.load()
  249. # start a session
  250. # ---------------
  251. def get_session_record(self):
  252. """get session record, or return the standard Guest Record"""
  253. r = webnotes.conn.sql("""select user, sessiondata, status from
  254. tabSessions where sid='%s'""" % self.sid)
  255. if not r:
  256. self.sid = 'Guest'
  257. r = webnotes.conn.sql("""select user, sessiondata, status from
  258. tabSessions where sid='%s'""" % self.sid)
  259. return r and r[0] or None
  260. def load(self):
  261. """non-login request: load a session"""
  262. import webnotes
  263. r = self.get_session_record()
  264. if r:
  265. self.data = {'data': (r[1] and eval(r[1]) or {}),
  266. 'user':r[0], 'sid': self.sid}
  267. else:
  268. self.start_as_guest()
  269. def start_as_guest(self):
  270. """all guests share the same 'Guest' session"""
  271. webnotes.login_manager.login_as_guest()
  272. self.start()
  273. def start(self):
  274. """start a new session"""
  275. import os
  276. import webnotes
  277. import webnotes.utils
  278. # generate sid
  279. if webnotes.login_manager.user=='Guest':
  280. sid = 'Guest'
  281. else:
  282. sid = webnotes.utils.generate_hash()
  283. self.data['user'] = webnotes.login_manager.user
  284. self.data['sid'] = sid
  285. self.data['data']['session_ip'] = os.environ.get('REMOTE_ADDR');
  286. self.data['data']['tenant_id'] = webnotes.form_dict.get('tenant_id', 0)
  287. # get ipinfo
  288. if webnotes.conn.get_global('get_ip_info'):
  289. self.get_ipinfo()
  290. # insert session
  291. self.insert_session_record()
  292. # update profile
  293. webnotes.conn.sql("""UPDATE tabProfile SET last_login = '%s', last_ip = '%s'
  294. where name='%s'""" % (webnotes.utils.now(), webnotes.remote_ip, self.data['user']))
  295. # set cookies to write
  296. webnotes.session = self.data
  297. webnotes.cookie_manager.set_cookies()
  298. def update(self):
  299. """extend session expiry"""
  300. if webnotes.session['user'] != 'Guest':
  301. self.check_expired()
  302. webnotes.conn.sql("""update tabSessions set sessiondata=%s, user=%s, lastupdate=NOW()
  303. where sid=%s""" , (str(self.data['data']), self.data['user'], self.data['sid']))
  304. # check expired
  305. # -------------
  306. def check_expired(self):
  307. """expire non-guest sessions"""
  308. exp_sec = webnotes.conn.get_value('Control Panel', None, 'session_expiry') or '06:00:00'
  309. # incase seconds is missing
  310. if len(exp_sec.split(':')) == 2:
  311. exp_sec = exp_sec + ':00'
  312. # set sessions as expired
  313. webnotes.conn.sql("""delete from tabSessions
  314. where TIMEDIFF(NOW(), lastupdate) > TIME(%s) and sid!='Guest'""", exp_sec)
  315. def get_ipinfo(self):
  316. import os
  317. try:
  318. import pygeoip
  319. except:
  320. return
  321. gi = pygeoip.GeoIP('data/GeoIP.dat')
  322. self.data['data']['ipinfo'] = {'countryName': gi.country_name_by_addr(os.environ.get('REMOTE_ADDR'))}
  323. def insert_session_record(self):
  324. webnotes.conn.sql("""insert into tabSessions
  325. (sessiondata, user, lastupdate, sid, status)
  326. values (%s , %s, NOW(), %s, 'Active')""",
  327. (str(self.data['data']), self.data['user'], self.data['sid']))