import es import services import services.auth import playerlib info = es.AddonInfo() info.name = "Cute Auth" info.version = "0.5" info.author = "Cutie Bunny-Helper" info.description = "Auth provider for everyone, from simple to very advanced configs" info.basename = "cuteauth" info.url = "http://addons.eventscripts.com/addons/view/cuteauth" authinst = None def load(): ''' Initialize addon, triggered by es_load cuteauth ''' global authinst authinst = CuteAuthService(es.getAddonPath(info.basename)+"/authdata.txt") authinst.registerCapability('admin_cuteauth', authinst.ROOT) services.register("auth", authinst) es.regcmd('cute', 'cuteauth/serv_cmd', 'Administer CuteAuth') es.regclientcmd('cute', 'cuteauth/client_cmd', 'Administer CuteAuth') def unload(): ''' Remove auth service, triggered by es_unload cuteauth ''' services.unregister("auth") es.unregclientcmd('cute') class CuteAuthService(services.auth.AuthorizationService): ''' The auth service itself ''' strranks = { 'ROOT': 0, 'ADMIN': 1, 'POWERUSER': 2, 'USER': 4, } def __init__(self, authfile): ''' Initialize class instance ''' self.fallbackdata = dict() self.authfile = authfile self.authdata = self._LoadAuthData(authfile) self.name = "cuteauth" if not self.authdata: self.authdata = { 'users': dict(), 'groups': dict(), } es.dbgmsg(0, "WARNING! CuteAuth could not load authdata.txt!") def registerCapability(self, auth_capability, auth_recommendedlevel): ''' Registers a capability with a recommended level ''' self.fallbackdata[auth_capability] = int(auth_recommendedlevel) es.dbgmsg(1, 'CuteAuth, capability "%s" registered for level %s'%( auth_capability, auth_recommendedlevel)) return True def isUseridAuthorized(self, auth_userid, auth_capability): ''' Returns boolean information about if the specified userid is allowed to use the specified capability ''' user = self.getOfflineIdentifier(auth_userid) data = self.isIdAuthorized(user, auth_capability) es.dbgmsg(1, 'CuteAuth, userid %s (%s) authed for %s = %s'%( auth_userid, user, auth_capability, str(data))) return data def getOfflineIdentifier(self, auth_userid): ''' Returns an identifier used to actually get the player permission ''' player = playerlib.getPlayer(int(auth_userid)) s = player.get('steamid') if s == 'STEAM_ID_LAN' or s == 'STEAM_ID_PENDING': return player.get('address').split(':')[0] return s def isIdAuthorized(self, auth_identifier, auth_capability): ''' Returns boolean information on whether the player is allowed to use the specified capability, using the indetifier given by getOfflineIdentifier ''' es.dbgmsg(1, 'CuteAuth isIdAuthorized(%s, %s)'%(auth_identifier, auth_capability)) # Check if we actually have any real identifier or capability if (not auth_identifier) or (not auth_capability): es.dbgmsg(1, 'CuteAuth: either parameter was empty') return False # Check if the auth_capability is known if not self.fallbackdata.has_key(auth_capability): es.dbgmsg(1, 'CuteAuth: unknown capability') if self.authdata['users'].has_key(auth_identifier): if self.authdata['users'][auth_identifier]['level']<=self.ADMIN: return True return False # Check if the user has permission to use the capability if self.authdata['users'].has_key(auth_identifier): es.dbgmsg(1, 'CuteAuth: user found') u = self.authdata['users'][auth_identifier] if u.has_key(auth_capability): # The capability has been explicitly set for this user es.dbgmsg(1, 'CuteAuth: explicitly set for user') return bool(u[auth_capability]) # Go through all groups the user belongs to if u['level'] <= self.fallbackdata[auth_capability]: es.dbgmsg(1, 'CuteAuth: user is on higher level than capability') allowed = True else: allowed = False level = self.isCapabilityInGroups(u['groups'], auth_capability) if level == 2: return True return allowed and bool(level) es.dbgmsg(1, 'CuteAuth: user not found') # User has not been found in database, check the required auth level if self.fallbackdata[auth_capability] >= self.IDENTIFIED: es.dbgmsg(1, 'CuteAuth: capability is marked for normal users') return True es.dbgmsg(1, 'CuteAuth: no rules match, disallowing') # If everything else fails, assume the player is not allowed return False def isCapabilityInGroups(self, gd, auth_capability): ''' Checks if the capability can be find in recursive group structure ''' allowed = 1 for g in gd: if self.authdata['groups'].has_key(g): if self.authdata['groups'][g].has_key(auth_capability): es.dbgmsg(1, 'CuteAuth: Set on group') return int(self.authdata['groups'][g][auth_capability])*2 allowed = self.isCapabilityInGroups( self.authdata['groups'][g]['groups'], auth_capability ) if not allowed: return 0 return allowed def _LoadAuthData(self, sourcefile): ''' Loads the authentication data from file ''' authdata = None try: authfile = open(sourcefile, 'r') authdata = { 'users': dict(), 'groups': dict(), } inedit = None linenumber = 0 es.dbgmsg(1, 'CuteAuth: begin reading authdata') for line in authfile: linenumber += 1 text = line.strip() es.dbgmsg(1, 'CuteAuth: line %d: "%s"'%(linenumber,text)) commands = text.split('=') c = commands[0].upper().strip() if c == 'GROUP': try: groupname = commands[1].strip() inedit = authdata['groups'][groupname] = { 'groups': set(), } es.dbgmsg(1, 'CuteAuth, line %d, GROUP %s'%(linenumber,groupname)) except IndexError: es.dbgmsg(0, 'CuteAuth parsing error on line %d, "=" expected'%linenumber) elif c == 'INGROUP': try: groupname = commands[1].strip() inedit['groups'].add(groupname) es.dbgmsg(1, 'CuteAuth, line %d, assigned to group %s'%(linenumber,groupname)) except IndexError: es.dbgmsg(0, 'CuteAuth parsing error on line %d, "=" expected'%linenumber) elif c in self.strranks or c.isdigit(): try: userident = commands[1].strip() if c in self.strranks: level = self.strranks[c] else: level = int(c) inedit = authdata['users'][userident] = { 'groups': set(), 'level': level, } es.dbgmsg(1, 'CuteAuth, line %d, USER %s, level %d'%(linenumber,userident,level)) except IndexError: es.dbgmsg(0, 'CuteAuth parsing error on line %d, "=" expected'%linenumber) elif c == 'DENY': try: authname = commands[1].strip() inedit[authname] = False es.dbgmsg(1, 'CuteAuth, line %d, DENY capability %s'%(linenumber,authname)) except IndexError: es.dbgmsg(0, 'CuteAuth parsing error on line %d, "=" expected'%linenumber) except TypeError: es.dbgmsg(0, 'CuteAuth logic error on line %d, unassigned capability'%linenumber) elif len(text)>0: try: inedit[text] = True es.dbgmsg(1, 'CuteAuth, line %d, ALLOW capability %s'%(linenumber,text)) except TypeError: es.dbgmsg(0, 'CuteAuth logic error on line %d, unassigned capability'%linenumber) else: es.dbgmsg(1, 'CuteAuth: empty line') es.dbgmsg(1, 'CuteAuth: stop reading datafile') except IOError, e: es.dbgmsg(0, "CuteAuth IO error: "+str(e)) finally: es.dbgmsg(1, 'CuteAuth: finally') try: authfile.close() except NameError: pass return authdata def _save(self): ''' Saves the data back to file ''' try: authfile = open(self.authfile, 'w') for ukey in self.authdata['users']: u = self.authdata['users'][ukey] level = u['level'] authfile.write('%d = %s\n'%(level, ukey)) for gkey in u['groups']: authfile.write('\tINGROUP = %s\n'%gkey) for ckey in u: if ckey != 'level' and ckey != 'groups': if u[ckey]: authfile.write('\t%s\n'%ckey) else: authfile.write('\tDENY = %s\n'%ckey) authfile.write('\n') for ukey in self.authdata['groups']: u = self.authdata['groups'][ukey] authfile.write('GROUP = %s\n'%ukey) for gkey in u['groups']: authfile.write('\tINGROUP = %s\n'%gkey) for ckey in u: if ckey != 'groups': if u[ckey]: authfile.write('\t%s\n'%ckey) else: authfile.write('\tDENY = %s\n'%ckey) authfile.write('\n') except IOError,e: es.dbgmsg(0, "CuteAuth IO error: "+str(e)) return False finally: try: authfile.close() except NameError: pass return True def _reload(self): ''' Reloads auth data from the file ''' newdata = self._LoadAuthData(self.authfile) if newdata: self.authdata = newdata return True return False def serv_cmd(): do_cmd(0, es.dbgmsg) def client_cmd(): userid = int(es.getcmduserid()) if authinst.isUseridAuthorized(userid, 'admin_cuteauth'): do_cmd(userid, sendecho) else: es.tell(userid, '#green', 'You do not have permission to edit CuteAuth') def sendecho(userid, text): es.cexec(userid, 'echo %s'%text) def do_cmd(userid, function): argc = es.getargc() command = es.getargv(1).lower() try: if argc == 1: function(userid, 'CuteAuth administration command usage:') function(userid, 'cute ') function(userid, ' adduser deluser addgroup delgroup') function(userid, ' adduserperm deluserperm addgroupperm delgroupperm') function(userid, ' utogroup gtogroup ufromgroup gfromgroup') function(userid, ' save reload') elif command == 'adduser': if argc < 3: function(userid, 'cute adduser [level]') function(userid, ' - adds specified user to auth database') function(userid, ' - level: %d=ADMIN, %d=POWERUSER, %d=USER'%( authinst.ADMIN, authinst.POWERUSER, authinst.IDENTIFIED)) function(userid, ' - defaults to level %d (ADMIN)'%authinst.ADMIN) else: if argc >= 4: level = int(es.getargv(3)) else: level = authinst.ADMIN uid = es.getargv(2) if uid.isdigit(): uid = authinst.getOfflineIdentifier(uid) if authinst.authdata['users'].has_key(uid): function(userid, 'Level changed for existing user %s'%uid) authinst.authdata['users'][uid]['level'] = level else: authinst.authdata['users'][uid] = { 'groups': set(), 'level': level, } function(userid, 'User %s added'%uid) elif command == 'deluser': if argc < 3: function(userid, 'cute deluser ') function(userid, ' - deletes specified user from auth database') else: uid = es.getargv(2) if uid.isdigit(): uid = authinst.getOfflineIdentifier(uid) if authinst.authdata['users'].has_key(uid): del authinst.authdata['users'][uid] function(userid, 'User %s deleted'%uid) else: function(userid, 'User %s not found'%uid) elif command == 'addgroup': if argc < 3: function(userid, 'cute addgroup ') function(userid, ' - creates a new group') else: g = es.getargv(2) if authinst.authdata['groups'].has_key(g): function(userid, 'Group %s already exists'%g) else: authinst.authdata['groups'][g] = { 'groups': set(), } function(userid, 'Group %s added'%g) elif command == 'delgroup': if argc < 3: function(userid, 'cute delgroup ') function(userid, ' - deletes a group') else: g = es.getargv(2) if authinst.authdata['groups'].has_key(g): del authinst.authdata['groups'][g] function(userid, 'Group %s deleted'%g) else: function(userid, 'Group %s not found'%g) elif command == 'adduserperm': if argc < 4: function(userid, 'cute adduserperm [0]') function(userid, ' - adds new permission/capability for user') function(userid, ' - optional 0 parameter denies this permission') else: if argc >= 5: level = bool(int(es.getargv(4))) else: level = True uid = es.getargv(2) perm = es.getargv(3) if uid.isdigit(): uid = authinst.getOfflineIdentifier(uid) if authinst.authdata['users'].has_key(uid): authinst.authdata['users'][uid][perm] = level if level: function(userid, 'User %s is now allowed to %s'%(uid,perm)) else: function(userid, 'User %s is now denied to %s'%(uid,perm)) else: function(userid, 'User %s not found'%uid) elif command == 'deluserperm': if argc < 4: function(userid, 'cute deluserperm ') function(userid, ' - deletes a permission/capability from user') else: uid = es.getargv(2) perm = es.getargv(3) if uid.isdigit(): uid = authinst.getOfflineIdentifier(uid) if authinst.authdata['users'].has_key(uid): del authinst.authdata['users'][uid][perm] function(userid, 'Permission %s deleted from %s'%(perm,uid)) else: function(userid, 'User %s not found'%uid) elif command == 'addgroupperm': if argc < 4: function(userid, 'cute addgroupperm [0]') function(userid, ' - adds new permission/capability for user') function(userid, ' - optional 0 parameter denies this permission') else: if argc >= 5: level = bool(int(es.getargv(4))) else: level = True g = es.getargv(2) perm = es.getargv(3) if authinst.authdata['groups'].has_key(g): authinst.authdata['groups'][g][perm] = level if level: function(userid, 'Group %s is now allowed to %s'%(g,perm)) else: function(userid, 'Group %s is now denied to %s'%(g,perm)) else: function(userid, 'Group %s not found'%g) elif command == 'delgroupperm': if argc < 4: function(userid, 'cute delgroupperm ') function(userid, ' - deletes a permission/capability from group') else: g = es.getargv(2) perm = es.getargv(3) if authinst.authdata['groups'].has_key(g): del authinst.authdata['groups'][g][perm] function(userid, 'Permission %s deleted from %s'%(perm,g)) else: function(userid, 'Group %s not found'%g) elif command == 'utogroup': if argc < 4: function(userid, 'cute utogroup ') function(userid, ' - assigns user to a group') else: uid = es.getargv(2) g2 = es.getargv(3) if uid.isdigit(): uid = authinst.getOfflineIdentifier(uid) if authinst.authdata['users'].has_key(uid): authinst.authdata['users'][uid]['groups'].add(g2) function(userid, 'User %s is now in %s'%(uid,g2)) else: function(userid, 'User %s not found'%uid) elif command == 'gtogroup': if argc < 4: function(userid, 'cute gtogroup ') function(userid, ' - assigns group to a group') else: g = es.getargv(2) g2 = es.getargv(3) if authinst.authdata['groups'].has_key(g): authinst.authdata['groups'][g]['groups'].add(g2) function(userid, 'Group %s is now in %s'%(g,g2)) else: function(userid, 'Group %s not found'%g) elif command == 'ufromgroup': if argc < 4: function(userid, 'cute ufromgroup ') function(userid, ' - removes user from a group') else: uid = es.getargv(2) g2 = es.getargv(3) if uid.isdigit(): uid = authinst.getOfflineIdentifier(uid) if authinst.authdata['users'].has_key(uid): if g2 in authinst.authdata['users'][uid]['groups']: authinst.authdata['users'][uid]['groups'].remove(g2) function(userid, 'User %s is no longer in %s'%(uid,g2)) else: function(userid, 'User %s was not in %s'%(uid,g2)) else: function(userid, 'User %s not found'%uid) elif command == 'gfromgroup': if argc < 4: function(userid, 'cute gfromgroup ') function(userid, ' - removes group from a group') else: g = es.getargv(2) g2 = es.getargv(3) if authinst.authdata['groups'].has_key(g): if g2 in authinst.authdata['groups'][g]['groups']: authinst.authdata['groups'][g]['groups'].remove(g2) function(userid, 'Group %s is no longer in %s'%(g,g2)) else: function(userid, 'Group %s was not in %s'%(g,g2)) else: function(userid, 'Group %s not found'%g) elif command == 'save': if authinst._save(): function(userid, 'Data saved') else: function(userid, 'Error saving data!') elif command == 'reload': if authinst._reload(): function(userid, 'Data reloaded') else: function(userid, 'Error loading data!') else: function(userid, 'Unknown subcommand %s'%command) except playerlib.UseridError: function(userid, 'Invalid userid')