view av_user.py @ 508:64f8666690d8

README update (Logical change 1.149)
author optonline.net!jeffpc
date Fri, 16 Jan 2004 02:53:48 +0000
parents 524a0baf7f24
children 903781c081b7
line wrap: on
line source

#/*
# * AV Admin - Helps to manage an AV department
# *
# * Copyright (C) 2003, 2004 Josef "Jeff" Sipek
# *
# * This program is free software; you can redistribute it and/or modify
# * it under the terms of the GNU General Public License as published by
# * the Free Software Foundation; either version 2 of the License, or
# * (at your option) any later version.
# *
# * This program is distributed in the hope that it will be useful,
# * but WITHOUT ANY WARRANTY; without even the implied warranty of
# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# * GNU General Public License for more details.
# *
# * You should have received a copy of the GNU General Public License
# * along with this program; if not, write to the Free Software
# * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
# *
# * $Id$
# */

""" Framework for accessing user data

This class really contains 3 types of functions:
	1) private - all of these begin with "__" (2 underscores)
	2) public, user related - all of these are public, and have to do
		something with the user access management
	3) public, utilities - all of these are public, however, they do
		something different that access management, ie. they return
		md5 digest in HEX

"""

import av_log
import av_debug
import av_settings

class User:
	""" Class to hold all user data and more """
	noperm = {"eq":0, "eqa":0, "eqh":0, "eql":0, "eqm":0, "eqr":0,		# equipment
		"user":0, "userl":0,						# user
		"work":0, "worka":0, "workh":0, "workl":0, "workm":0, "workr":0,# work
		"feat":0, "rules":0, "room":0, "secl":0, "sysl":0, "settings":0,# misc
		"mess":0, "messs":0}						# messaging
	DisAllowedChars = "!@#$%^&*()_+-={}[]:\";'<>?,./|\\~`"
	def __init__(self):
		""" Set all variables to their default values

		does not return anything """
		pass
	
	def attach(self,conn):
		self.conn = conn
		self.db = conn.db
		self.html = conn.html
		self.utils = conn.utils
		
		self.__SID = self.conn.getparam("sid")
		self.__UID = -1
		self.__USER = ""
		self.__NAME = ""
		self.__PASS = ""
		self.__PERM = self.__class__.noperm
		self.__TIMEDOUT = 0
		
		self.TEXTBUF = {"loginerror":"", "logincheckerror":""}
		
		if (not self.conn.regexp_a0.match(self.__SID)):
			self.__SID = ""
		
		if (self.__SID or self.conn.getparam("login")):
			self.checklogin()

	def getSID(self):
		return self.__SID

	def setSID(self,newsid):
		self.__SID = newsid
	
	def getUID(self):
		return self.__UID

	def getUSER(self):
		return self.__USER

	def setUSER(self,newuser):
		self.__USER = newuser
	
	def getNAME(self):
		return self.__NAME
	
	def setNAME(self,name):
		self.__NAME = name

	def getPASS(self):
		return self.__PASS

	def setPASS(self,newpass):
		self.__PASS = newpass

	def getPERM(self,user=-1):
		""" Get permissions from DB, if user==-1, use current
		
		returns dictionary """
		if (user==-1):
			return self.__PERM
		# ok, look up user
		
		dbid = self.db.newid()
		dbid.execute(
			"""SELECT `priv_eq`, `priv_eqa`, `priv_eqh`, `priv_eql`,
				`priv_eqm`, `priv_eqr`, `priv_user`, `priv_userl`, `priv_work`,
				`priv_worka`, `priv_workh`, `priv_workl`, `priv_workm`, `priv_workr`,
				`priv_feat`, `priv_rules`, `priv_room`, `priv_lsec`, `priv_lsys`,
				`priv_settings`, `priv_mess`, `priv_messs` FROM `users` WHERE `id`=""" + str(user) + ";")
		rows = dbid.rowcount()
		av_debug.BUGON(rows > 1, "Multiple record found")
		
		if (rows == 0): # user not logged in
			return self.__class__.noperm
		if (rows > 1): # multiple records found
			return self.__class__.noperm

		result = dbid.fetchone()
		tmp__PERM		= self.__class__.noperm
		tmp__PERM["eq"]		= result[0]
		tmp__PERM["eqa"]	= result[1]
		tmp__PERM["eqh"]	= result[2]
		tmp__PERM["eql"]	= result[3]
		tmp__PERM["eqm"]	= result[4]
		tmp__PERM["eqr"]	= result[5]
		tmp__PERM["user"]	= result[6]
		tmp__PERM["userl"]	= result[7]
		tmp__PERM["work"]	= result[8]
		tmp__PERM["worka"]	= result[9]
		tmp__PERM["workh"]	= result[10]
		tmp__PERM["workl"]	= result[11]
		tmp__PERM["workm"]	= result[12]
		tmp__PERM["workr"]	= result[13]
		tmp__PERM["feat"]	= result[14]
		tmp__PERM["rules"]	= result[15]
		tmp__PERM["room"]	= result[16]
		tmp__PERM["secl"]	= result[17]
		tmp__PERM["sysl"]	= result[18]
		tmp__PERM["settings"]	= result[19]
		tmp__PERM["mess"]	= result[20]
		tmp__PERM["messs"]	= result[21]
		return tmp__PERM

	def setPERM(self,newperm):
		self.__PERM = newperm
	
	def loadPERM(self):
		""" Get permissions from DB
		if login succeeds, 0 is returned,
		if the user does not exist, 1 is returned,
		if multiple instances of the user are found (internal error), 2 is returned

		returns integer """
		dbid = self.db.newid()
		dbid.execute(
			"""SELECT `priv_eq`, `priv_eqa`, `priv_eqh`, `priv_eql`,
				`priv_eqm`, `priv_eqr`, `priv_user`, `priv_userl`, `priv_work`,
				`priv_worka`, `priv_workh`, `priv_workl`, `priv_workm`, `priv_workr`,
				`priv_feat`, `priv_rules`, `priv_room`, `priv_lsec`, `priv_lsys`,
				`priv_settings`, `priv_mess`, `priv_messs` FROM `users` WHERE `id`=""" + str(self.__UID) + ";")
		rows = dbid.rowcount()
		av_debug.BUGON(rows > 1, "Multiple record found")
		
		if (rows == 0): # user not logged in
			return self.__class__.noperm
		if (rows > 1): # multiple records found
			return self.__class__.noperm

		result = dbid.fetchone()
		self.__PERM["eq"]	= result[0]
		self.__PERM["eqa"]	= result[1]
		self.__PERM["eqh"]	= result[2]
		self.__PERM["eql"]	= result[3]
		self.__PERM["eqm"]	= result[4]
		self.__PERM["eqr"]	= result[5]
		self.__PERM["user"]	= result[6]
		self.__PERM["userl"]	= result[7]
		self.__PERM["work"]	= result[8]
		self.__PERM["worka"]	= result[9]
		self.__PERM["workh"]	= result[10]
		self.__PERM["workl"]	= result[11]
		self.__PERM["workm"]	= result[12]
		self.__PERM["workr"]	= result[13]
		self.__PERM["feat"]	= result[14]
		self.__PERM["rules"]	= result[15]
		self.__PERM["room"]	= result[16]
		self.__PERM["secl"]	= result[17]
		self.__PERM["sysl"]	= result[18]
		self.__PERM["settings"]	= result[19]
		self.__PERM["mess"]	= result[20]
		self.__PERM["messs"]	= result[21]
		return 0
	
	def isloggedin(self):
		if (self.__SID.__len__()>0):
			return 1
		return 0
		
	def timedout(self):
		if (self.__TIMEDOUT):
			return 1
		return 0

	def checklogin(self):
		""" Check that the user is logged in, and adjust any variables as needed
		if login succeeds, "" is returned,
		if not, HTML string is returned

		returns string """
		if (self.conn.getparam("login")=="yes" and self.__SID):
			self.TEXTBUF["loginerror"] = "<br /><br /><div class=\"error\">Sorry, fatal error has occured</div>"
			return
		
		if (self.conn.getparam("login")=="yes"):
			if ((not self.conn.validatestring(self.conn.getparam("user"))) or (not self.conn.validatestring(self.conn.getparam("pass")))):
				self.TEXTBUF["loginerror"] = "<br /><br /><div class=\"error\">Sorry, username/password contains invalid characters</div>"
				return
			if ((not self.conn.getparam("user")) or (not self.conn.getparam("pass"))):
				self.TEXTBUF["loginerror"] = "<br /><br /><div class=\"error\">Sorry, both username and password are required</div>"
				return

			self.TEXTBUF["loginerror"] = self.__login()
			if (self.TEXTBUF["loginerror"]):
				return

		dbid = self.db.newid()
		dbid.execute("SELECT `id`, `ip`, `user`, `lastac` FROM `online` WHERE `uhash`=\"" + str(self.__SID) + "\";")
		rowcount = dbid.rowcount()
		if (rowcount == 0): # user not logged in
			av_log.syslog(self.conn,"Fatal Error: no records found in online")
			self.__SID = ""
			self.TEXTBUF["logincheckerror"] = "<br /><br />You do not appear to be logged in. To log in, select the appropreate option from the menu."
			return
		
		if (rowcount > 1): # multiple records found
			av_log.syslog(self.conn,"Fatal Error: mutiple records found in online")
			dbid.execute("DELETE FROM `online` WHERE `uhash`=\"" + self.__SID + "\";")
			self.__SID = ""
			self.TEXTBUF["logincheckerror"] = "<br /><br /><div class=\"error\">Error unexpected error occured.</div>You do not appear to be logged in. To log in, select the appropreate option from the menu."
			return
		
		result = dbid.fetchone()
		if ((result[3]+int(av_settings.getSetting(self.conn,"timeout"))*60)<self.conn.now()): # timed out
			dbid.execute("DELETE FROM `online` WHERE `id`=" + str(result[0]) + ";")
			self.__SID = ""
			self.__PERM = self.__class__.noperm
			self.__TIMEDOUT = 1
			av_log.syslog(self.conn,"User \"" + self.UID2User(result[2]) + "\"'s (UID = " + str(result[2]) + ") session timed out")
			return # We do not need to set HTML here, the timeout HTML is a LARGE piece of code, that appears in menu() in av_html.py

		if (result[1]!=self.conn.getIP()): # highjacking attempt occured
			dbid.execute("DELETE FROM `online` WHERE `id`=" + str(result[0]) + ";")
			self.__SID = ""
			self.__PERM = self.__class__.noperm
			av_log.syslog(self.conn,"User \"" + self.UID2User(result[2]) + "\"'s (UID = " + str(result[2]) + ") session highjacking attempt occured\nLogging user out")
			self.TEXTBUF["logincheckerror"] = "<br /><br /><div class=\"error\">Error, somebody tried to &quot;highjack&quot; your connection</div>You have been logged out to prevent the attaker from gaining more information."
			return

		self.__UID = result[2]
		self.loadPERM()
		self.updatelastac()
		self.kickoldusers()
		
	def checkpass(self,pwd):
		""" Checks user's password against pwd
		
		returns bool """
		self.__loadUID()
		dbid = self.db.newid()
		dbid.execute("SELECT `pass` FROM `users` WHERE `id`=" + str(self.__UID) + ";")
		if (dbid.rowcount()):
			# FIXME: what if more than one record is found
			result = dbid.fetchone()
			if (self.utils.md5sum(pwd)==result[0]):
				return 1
		return 0
		
	def UID2User(self,uid=-1):
		""" Find username for user id: uid
		if uid==-1, use current user
		
		returns string """
		if (uid==-1):
			self.__loadUID()
			uid = self.__UID
		dbid = self.db.newid()
		dbid.execute("SELECT `name` FROM `users` WHERE `id`=" + str(uid) + ";")
		if (dbid.rowcount()):
			# FIXME: what if multiple recors were returned
			return dbid.fetchone()[0]
		return "?"
	
	def UID2Name(self,uid=-1):
		""" Find real user's name for user id: uid
		if uid==-1, use current user
		
		returns string """
		if (uid==-1):
			self.__loadUID()
			uid = self.__UID
		dbid = self.db.newid()
		dbid.execute("SELECT `realname` FROM `users` WHERE `id`=" + str(uid) + ";")
		if (dbid.rowcount()):
			# FIXME: what if multiple recors were returned
			return dbid.fetchone()[0]
		return "?"
		
	def __loadUID(self):
		""" Retrieve UID from DB
		
		does not return anything """
		dbid = self.db.newid()
		dbid.execute("SELECT `user` FROM `online` WHERE `uhash`=\"" + self.__SID + "\";")
		if (dbid.rowcount()):
			# FIXME: what if more than one record is returned
			result = dbid.fetchone()
			self.__UID = result[0]	
			
	def changepass(self,npass):
		""" Change user's password to npass
		
		returns bool """
		self.__loadUID()
		dbid = self.db.newid()
		dbid.execute("UPDATE `users` SET `pass`=\"" + self.utils.md5sum(npass) + "\" WHERE `id`=" + str(self.__UID) + ";")
		if (dbid.rowcount()):
			av_log.syslog(self.conn,"User \"" + self.UID2User() + "\" (UID = " + str(self.__UID) + ") changed his password")
			return 1
		return 0
	
	def kickoldusers(self):
		""" Delete all users that are inactive for more that hardtimeout
		
		does not return anything """
		dbid = self.db.newid()
		dbid.execute("SELECT `id`, `user`, `ip` FROM `online` WHERE `lastac`<" + str((self.conn.now()-(int(av_settings.getSetting(self.conn,"hardtimeout"))*60))))
		for rec in dbid.fetchall():
			av_log.syslog(self.conn,"User \"" + self.UID2User(rec[1]) + "\" (UID = " + str(rec[1]) + ") reached the hardtimeout")
			dbid.execute("DELETE FROM `online` WHERE `id`=" + str(rec[0]) + ";")
		
	def updatelastac(self):
		""" Update last access time to self.conn.now()

		does not return anything """
		dbid = self.db.newid()
		dbid.execute("UPDATE `online` SET `lastac`=" + str(self.conn.now()) + " WHERE `uhash`=\"" + self.__SID + "\";")

	def __login(self):
		""" Checks user credentials against the database and logs him in is possible
		if login succeeds, "" is returned,
		if not, HTML string is returned

		returns string """
		user = self.conn.getparam("user")
		passwd = self.conn.getparam("pass")
		md5pass = self.utils.md5sum(passwd)

		dbid = self.db.newid()
		dbid.execute(
			"""SELECT `id`, `priv_eq`, `priv_eqa`, `priv_eqh`, `priv_eql`,
				`priv_eqm`, `priv_eqr`, `priv_user`, `priv_userl`, `priv_work`,
				`priv_worka`, `priv_workh`, `priv_workl`, `priv_workm`, `priv_workr`,
				`priv_feat`, `priv_rules`, `priv_room`, `priv_lsec`, `priv_lsys`,
				`priv_settings`, `priv_mess`, `priv_messs` FROM `users` WHERE `name`='"""
				+ user + "' AND `pass`='" + md5pass + "' AND `active`=1")
		rowcount = dbid.rowcount()
		if (rowcount == 0): # user not found
			av_log.seclog(self.conn,"Failed login with these credintials: user = \"" + user + "\"")
			return "<br /><br /><div class=\"error\">Sorry, invalid username/password</div>"
		if (rowcount > 1): # multiple instances found
			av_log.syslog(self.conn,"Fatal Error: multiple records found in users (user = \"" + user + "\", pass = \"" + md5pass + "\")")
			return "<br /><br /><div class=\"error\">Sorry, a fatal error occured</div>"

		result = dbid.fetchone()
		self.__PERM["eq"]	= result[1]
		self.__PERM["eqa"]	= result[2]
		self.__PERM["eqh"]	= result[3]
		self.__PERM["eql"]	= result[4]
		self.__PERM["eqm"]	= result[5]
		self.__PERM["eqr"]	= result[6]
		self.__PERM["user"]	= result[7]
		self.__PERM["userl"]	= result[8]
		self.__PERM["work"]	= result[9]
		self.__PERM["worka"]	= result[10]
		self.__PERM["workh"]	= result[11]
		self.__PERM["workl"]	= result[12]
		self.__PERM["workm"]	= result[13]
		self.__PERM["workr"]	= result[14]
		self.__PERM["feat"]	= result[15]
		self.__PERM["rules"]	= result[16]
		self.__PERM["room"]	= result[17]
		self.__PERM["secl"]	= result[18]
		self.__PERM["sysl"]	= result[19]
		self.__PERM["settings"]	= result[20]
		self.__PERM["mess"]	= result[21]
		self.__PERM["messs"]	= result[22]
		self.__SID = self.createSID()

		dbid.execute("INSERT INTO `online` (`id`, `ip`, `user`, `uhash`, `lastac`) VALUES (NULL, '" + self.conn.getIP() + "', " + str(result[0]) + ", '" + self.__SID + "', " + str(self.conn.now()) + ");")
				# FIXME: need to check that the above works
		av_log.syslog(self.conn,"User \"" + user + "\" (UID = " + str(result[0]) + ") logged in")

		return ""
		
	def logout(self):
		""" Log user out
		
		does not return anything """
		if (not self.isloggedin()): # bail out if used is not logged in
			av_log.syslog(self.conn,"Someone attempted to log out, without specifying Session ID")
			return
			
		dbid = self.db.newid()
		dbid.execute("DELETE FROM `online` WHERE `uhash`=\"" + self.__SID + "\";")
		av_log.syslog(self.conn,"User \"" + self.UID2User() + "\" logged out")
		
		self.__SID = ""
		self.__UID = -1
		self.__USER = ""
		self.__PASS = ""
		self.__PERM = self.__class__.noperm
		self.__TIMEDOUT = 0
		
	def createSID(self):
		""" Creates SID and returns is
		
		returns string """
		return self.utils.md5sum(self.conn.getIP() + self.conn.getUserAgent() + str(self.conn.now()))
		
if __name__ == "__main__":
	print "ERROR"