diff options
-rw-r--r-- | login.py | 129 | ||||
-rw-r--r-- | loginsecurity.py | 275 | ||||
-rw-r--r-- | main.py | 2 | ||||
-rw-r--r-- | mysql_utils.py | 12 | ||||
-rw-r--r-- | util_events.py | 71 | ||||
-rw-r--r-- | wrapper.py | 3 | ||||
-rw-r--r-- | wrapper_event.py | 66 | ||||
-rw-r--r-- | wrapper_player.py | 139 |
8 files changed, 382 insertions, 315 deletions
diff --git a/login.py b/login.py new file mode 100644 index 0000000..ada7162 --- /dev/null +++ b/login.py @@ -0,0 +1,129 @@ +from wrapper import * +from passlib.hash import pbkdf2_sha256 as crypt + +@event_handler("player_login","normal", utils = True) +def player_join(*args): + player = args[1] + if not player.registered: + player.authenticated = True + player.msg("Successfully logged in!") + +""" +@event_handler("player.PlayerCommandPreprocessEvent", "lowest") +def on_login_command(event): + player = event.player + password = event.message.replace("/login", "").replace(" ", "") + event.message = event.player.name + " Attempting to login!" + event.cancelled = True + + @async(daemon = True) + def check_pass(player, password): + print password + if not player.registered: + player.msg("You are not registered! use /register <password> to register!") + return + else: + if crypt.verify(password, player.password): + player.authenticated = True + player.msg("Successfully logged in!") + else: + print event.message + player.msg("Wrong password!") + check_pass(player, password)""" + +@command("login") +@async(daemon = True) +def on_login_command(**kwargs): + player = kwargs["sender"] + args = kwargs["args"] + + if not player.registered: + player.msg("You are not registered! use /register <password> to register!") + return + if len(args) > 1: + player.msg("The syntax is /login <password>") + return + elif len(args) is 1: + if crypt.verify(args[0], player.password): + player.authenticated = True + player.msg("Successfully logged in!") + else: + player.msg("Wrong password!") + +@command("changepass") +@async(daemon = True) +def on_changepass_command(**kwargs): + player = kwargs["sender"] + args = kwargs["args"] + + if not player.registered: + player.msg("You are not registered! use /register <password> to register!") + return + + if len(args) < 2: + player.msg("The syntax is /login <current_password> <new_password>") + return + elif len(args) is 2: + if crypt.verify(args[0], player.password): + player.password = crypt.encrypt(args[1], rounds=200000, salt_size=16) + player.msg("Successfully changed your password!") + player.save() + else: + player.msg("You have entered an incorrect current password!") + +@command("removepass") +@async(daemon = True) +def on_removepass_command(**kwargs): + player = kwargs["sender"] + args = kwargs["args"] + + if not player.registered: + player.msg("You are not registered! use /register <password> to register!") + return + + if len(args) < 1: + player.msg("The syntax is /removepass <current_password>") + return + + elif len(args) is 1: + if crypt.verify(args[0], player.password): + player.password = "None" + player.registered = False + player.save() + player.msg("Successfully removed your password!") + else: + player.msg("You have entered an incorrect current password!") + + + + +@command("register") +@async(daemon = True) +def on_register_command(**kwargs): + player = kwargs["sender"] + args = kwargs["args"] + if len(args) > 1: + player.msg("The syntax is /register <password>") + return + elif len(args) is 1: + if player.registered: + player.msg("You are already registered!") + return + player.password = crypt.encrypt(args[0], rounds=200000, salt_size=16) + player.registered = True + print player.password + player.save() + player.msg("Successfully registered!") + + +blocked_events = ["block.BlockBreakEvent", "block.BlockPlaceEvent", "player.PlayerMoveEvent", + "player.AsyncPlayerChatEvent","player.PlayerTeleportEvent", + "player.PlayerInteractEvent"] + +for event in blocked_events: + @event_handler(event_name = event, priority = "highest") + def on_blocked_event(event): + if not event.player.authenticated: + event.cancelled = True + + diff --git a/loginsecurity.py b/loginsecurity.py deleted file mode 100644 index 89f9e47..0000000 --- a/loginsecurity.py +++ /dev/null @@ -1,275 +0,0 @@ -from helpers import * -from passlib.hash import pbkdf2_sha256 as crypt -from basecommands import simplecommand -import time -import threading -from login_secrets import * #Don't forget to make login_secrets aswell -import mysqlhack -from com.ziclix.python.sql import zxJDBC -from java.lang import Runnable - -wait_time = 60 #seconds -admin_perm = "utils.loginsecurity.admin" -min_pass_length = 8 -blocked_events = ["block.BlockBreakEvent", "block.BlockPlaceEvent", "player.PlayerMoveEvent","player.AsyncPlayerChatEvent"] - - - - -def matches(password,user): - thread = threading.Thread(target=matches_thread, args = (password,user)) - thread.start() - - -def matches_thread(password, user): - hashed = get_pass(uid(user)) - py_player = get_py_player(user) - if crypt.verify(password, hashed): - if py_player.logging_in: - py_player.logging_in = False - msg(user, "&aLogged in successfully!") - else: - if py_player.logging_in: - msg(user, "&cInvalid password!") - - - - - -@simplecommand("cgpass", - usage = "<password> <new password>", - description = "Changes your password", - senderLimit = 0, - helpNoargs = True) -def change_pass_command(sender, command, label, args): - - py_player = get_py_player(sender) - - if py_player.logging_in: - return "&cYou are not logged in" - if not len(args) == 2: - return "&cInvalid arguments" - - password = args[0] - new_password = args[1] - uuid = uid(sender) - - if is_registered(uuid): - change_pass(uuid, crypt.encrypt(new_password, rounds=200000, salt_size=16)) - return "&aPassword changed" - return "&cYou are not registered" - - - - -@simplecommand("login", - usage = "<password>", - description = "Logs you in if <password> matches your password.", - senderLimit = 0, - helpNoargs = True) -def login_command(sender, command, label, args): - py_player = get_py_player(sender) - - if not py_player.logging_in: - msg(sender,"&cAlready logged in!") - - password = args[0] - matches(password, sender) - - - - -@simplecommand("register", - usage = "<password>", - description = "Registers you with <password>. Next time you join, log in with /login", - senderLimit = 0, - helpNoargs = True) -def register_command(sender, command, label, args): - - py_player = get_py_player(sender) - - if len(args) > 1: - return "&cPassword can only be one word!" - - uuid = uid(sender) - if is_registered(uuid): - return "&cYou are already registered!" - - password = args[0] - - if len(password) < min_pass_length: - return "&cThe password has to be made up of at least %s characters!" % min_pass_length - - create_pass(uuid, password) - return "&cPassword set. Use /login <password> upon join." - - - - -@simplecommand("rmpass", - description = "Removes your password if the password matches", - senderLimit = 0, - amax = 0, - helpNoargs = False) -def rmpass_command(sender, command, label, args): - - py_player = get_py_player(sender) - - if py_player.logging_in: - return "&cYou are not logged in" - - if not is_registered(uid(sender)): - return "&cYou are not registered!" - - if py_player.logging_in == False: - delete_pass(uid(sender)) - return "&aPassword removed successfully. You will not be prompted anymore." - return "&cFailed to remove password, please contact a staff member" - - - - -@simplecommand("rmotherpass", - aliases = ["lacrmpass"], - usage = "<user>", - senderLimit = -1, - description = "Removes password of <user> and sends them a notification", - helpNoargs = True) -def rmotherpass_command(sender, command, label, args): - - py_player = get_py_player(sender) - - if py_player.logging_in: - return "&cYou are not logged in" - - if not sender.hasPermission(admin_perm): - noperm(sender) - return - - user = server.getOfflinePlayer(args[0]) - - if is_registered(uid(user)): - delete_pass(uid(user)) - runas(server.getConsoleSender(), colorify("mail send %s &cYour password was reset by a staff member. Use &6/register&c to set a new one." % user.getName())) - return "&aPassword of %s reset successfully" % user.getName() - return "&cThat player could not be found (or is not registered)" - -def change_pass(uuid, pw): - conn = zxJDBC.connect(mysql_database, mysql_user, mysql_pass, "com.mysql.jdbc.Driver") - curs = conn.cursor() - curs.execute("UPDATE secret SET pass = ? WHERE uuid = ?", (pw,uuid,)) - conn.commit() - curs.close() - conn.close() - -def get_pass(uuid): - conn = zxJDBC.connect(mysql_database, mysql_user, mysql_pass, "com.mysql.jdbc.Driver") - curs = conn.cursor() - curs.execute("SELECT pass FROM secret WHERE uuid = ?", (uuid,)) - results = curs.fetchall() - curs.close() - conn.close() - return results[0][0] - -def create_pass(uuid,pw): - thread = threading.Thread(target=create_pass_thread, args=(uuid,pw)) - thread.start() - -def create_pass_thread(uuid, pw): - pw = crypt.encrypt(pw, rounds=200000, salt_size=16) - conn = zxJDBC.connect(mysql_database, mysql_user, mysql_pass, "com.mysql.jdbc.Driver") - curs = conn.cursor() - curs.execute("INSERT INTO secret VALUES (?,?)", (uuid,pw,)) - conn.commit() - curs.close() - conn.close() - -def is_registered(uuid): - conn = zxJDBC.connect(mysql_database, mysql_user, mysql_pass, "com.mysql.jdbc.Driver") - curs = conn.cursor() - curs.execute("SELECT EXISTS(SELECT * FROM secret WHERE uuid = ?)", (uuid,)) - results = curs.fetchall() - curs.close() - conn.close() - if results[0][0] == 1: - return True - return False - -def delete_pass(uuid): - conn = zxJDBC.connect(mysql_database, mysql_user, mysql_pass, "com.mysql.jdbc.Driver") - curs = conn.cursor() - curs.execute("DELETE FROM secret WHERE uuid = ?", (uuid,)) - conn.commit() - curs.close() - conn.close() - -@hook.event("player.PlayerJoinEvent", "highest") -def on_join(event): - user = event.getPlayer() - py_player = get_py_player(event.getPlayer()) - if is_registered(uid(user)): - msg(event.getPlayer(), "&4You will be disconnected after 60 seconds if you don't &alogin") - msg(user, "&cUse /login <password>") - py_player.logging_in = True - py_player.login_time = time.time() - return - elif user.hasPermission(admin_perm): - pass #Do what? force them to make a password, lots of code, maybe just message us on slack? - -#This shouldn't be needed anymore as py_player gets removed anyway. -""" - -@hook.event("player.PlayerQuitEvent", "high") -def on_quit(event): - if event.getPlayer().getName() in logging_in: - del logging_in[event.getPlayer().getName()] -""" - -##Threading start -class kick_class(Runnable): - - def __init__(self, player): - self.player = player - - def run(self): - if self.player.isOnline(): - self.player.kickPlayer(colorify("&aLogin timed out")) - -def kick_thread(): - while True: - time.sleep(1) - now = time.time() - for py_player in py_players: - if py_player.logging_in: - if now - py_player.login_time > wait_time: - player = py_player.player - kick = kick_class(player) - server.getScheduler().runTask(server.getPluginManager().getPlugin("RedstonerUtils"), kick) - - - """if name in logging_in: - del logging_in[name] - break - """ - - -thread = threading.Thread(target = kick_thread) -thread.daemon = True -thread.start() -##Threading end - -for blocked_event in blocked_events: - @hook.event(blocked_event, "high") - def on_blocked_event(event): - user = get_py_player(event.getPlayer()) - if user.logging_in: - event.setCancelled(True) - -@hook.event("player.PlayerCommandPreprocessEvent","normal") -def pre_command_proccess(event): - player = get_py_player(event.getPlayer()) - if player.logging_in: - args = event.getMessage().split(" ") - if not args[0].lower() == "/login": - msg(player.player, "&4You need to login before you do that!") - event.setCancelled(True) @@ -34,7 +34,7 @@ info("Loading RedstonerUtils...") # Import all modules, in this order -shared["load_modules"] = ["test"] +shared["load_modules"] = ["test", "login"] shared["modules"] = {} for module in shared["load_modules"]: diff --git a/mysql_utils.py b/mysql_utils.py index e59c3d9..1edfe35 100644 --- a/mysql_utils.py +++ b/mysql_utils.py @@ -13,11 +13,23 @@ class mysql_connect: if args is None: return self.curs.execute(query) else: + print query + print args return self.curs.execute(query, args) def fetchall(self): return self.curs.fetchall() + @property + def columns(self): + self.execute("SHOW COLUMNS FROM utils_players") + fetched = self.fetchall() + columns = [] + + for row in fetched: + columns.append(row[0]) + return columns + def __enter__(self): return self diff --git a/util_events.py b/util_events.py new file mode 100644 index 0000000..93645ba --- /dev/null +++ b/util_events.py @@ -0,0 +1,71 @@ +from thread_utils import * + +MONITOR_PRIORITY = "monitor" +HIGHEST_PRIORITY = "highest" +HIGH_PRIORITY = "high" +NORMAL_PRIORITY = "normal" +LOW_PRIORITY = "low" +LOWEST_PRIORITY = "lowest" + +priorities = ["lowest","low","normal","high","highest","monitor"] +events = [] + +class base_event(): + def __init__(self,event_name): + self.name = event_name + self.canceled = False + self._handlers = [ [],[],[],[],[],[] ] + + self.canceled_lock = threading.Lock() + + def add_handler(self,function,priority): + for prior in priorities: + if prior == priority: + self._handlers[priorities.index(prior)].append(function) + + def fire(self,*args): + for priority in self._handlers: + for handler in priority: + handler(self,*args) + + def set_canceled(self,state): + with self.canceled_lock: + self.canceled = state + + +class utils_events(base_event): + def __init__(self,event_name): + base_event.__init__(self,event_name) + + +def add_event(event_name,event = base_event): #Adds a new event + event = event(event_name) + events.append(event) + +def fire_event(event_name,*args): #Fires the event + for event in events: + if event.name == event_name: + event.fire(*args) + return event + +def check_events(event_name): #Returns false if the even does not exist. + for event in events: + if event.name == event_name: + return True + return False + + +#Decorator +def utils_event(event_name, priority, create_event=base_event): + def event_decorator(function): + def wrapper(*args, **kwargs): + pass + + if not check_events(event_name): #Check if the event exists, if not create it. + add_event(event_name,create_event) + + for event in events: #Go through the list of events, find the one we need and call all of its handlers + if event.name == event_name: + event.add_handler(function,priority) + return wrapper + return event_decorator
\ No newline at end of file @@ -8,4 +8,5 @@ Before you run away from this if the class you need to use isn't here, please cr from helpers import * from wrapper_event import * from wrapper_player import * -from wrapper_command import *
\ No newline at end of file +from wrapper_command import * +from util_events import utils_event, fire_event, utils_events
\ No newline at end of file diff --git a/wrapper_event.py b/wrapper_event.py index 083bf33..8632c70 100644 --- a/wrapper_event.py +++ b/wrapper_event.py @@ -1,22 +1,56 @@ from wrapper import * +from util_events import utils_event, utils_events from wrapper_player import * from traceback import format_exc as print_traceback -class py_event: - def __init__(self,event): - self.event = event - try: - self.player = py_players[event.getPlayer()] - except: - warn("Player doesn't exist") +class py_event(object): + def __init__(self,event): + self.event = event + try: + self.player = py_players[event.getPlayer()] + except: + warn("Player doesn't exist") + + @property + def cancelled(self): + return self.event.isCancelled() + + @cancelled.setter + def cancelled(self, value): + self.event.setCancelled(value) + + @property + def message(self): + try: + return self.event.getMessage() + except: + raise AttributeError + + @message.setter + def message(self, msg): + try: + self.event.setMessage(msg) + except: + raise AttributeError + +def event_handler(event_name = None, priority = "normal", utils = False): + if not utils: + def decorator(wrapped): + @hook.event(event_name, priority) + def wrapper(event): + try: + wrapped(py_event(event)) + except: + print(print_traceback()) + return decorator + elif utils: + def decorator(wrapped): + @utils_event(event_name, priority, create_event = utils_events) + def wrapper(*args): + try: + wrapped(*args) + except: + print(print_traceback()) + return decorator -def event_handler(event_name = None, priority = "normal"): - def decorator(wrapped): - @hook.event(event_name, priority) - def wrapper(event): - try: - wrapped(py_event(event)) - except: - print(print_traceback()) - return decorator diff --git a/wrapper_player.py b/wrapper_player.py index a889ede..5fb5c79 100644 --- a/wrapper_player.py +++ b/wrapper_player.py @@ -1,29 +1,82 @@ import time import mysqlhack from mysql_utils import * +from util_events import fire_event from thread_utils import * from players_secret import * from datetime import datetime from com.ziclix.python.sql import zxJDBC +from traceback import format_exc as print_traceback -class py_player: +class py_player(object): def __init__(self,player): self.player = player + self.logging_in = True + self.authenticated = False self.login_time = time.time() - self.logging_in = False + + self.props ={"uuid":self.uuid, + "name":self.name, + "nickname":self.name, + "registered":False, + "password":"None", + "banned":False, + "banned_reason":"You have been banned!", + "played_time":time.time() - self.login_time, + "last_login":datetime.now(), + "first_seen":datetime.now()} + + def __setattr__(self, attribute, value): + if not attribute in dir(self): + if not 'props' in self.__dict__: + self.__dict__[attribute] = value + else: + self.props[attribute] = value + else: + object.__setattr__(self, attribute, value) + + def __getattr__(self, attribute): + try: + return self.props[attribute] + except: + pass + + def save(self): + properties = [] + keys = [] + columns = [] + + with mysql_connect() as sql: + columns = sql.columns + + for key, value in self.props.items(): + if key not in columns: + with mysql_connect() as sql: + if isinstance(value, int): + sql.execute("ALTER TABLE utils_players ADD %s INT" % key) + elif isinstance(value, str): + sql.execute("ALTER TABLE utils_players ADD %s TEXT" % key) + elif isinstance(value, bool): + sql.execute("ALTER TABLE utils_players ADD %s TINYINT(1)" % key) + if key == "uuid": + continue + keys.append(key+"=?") + properties.append(value) + print value + properties.append(self.props["uuid"]) + keys = str(tuple(keys)).replace("\'","").replace("(","").replace(")","") + + + with mysql_connect() as sql: + sql.execute("UPDATE utils_players set %s WHERE uuid = ?" % keys, properties) - self.nickname = self.name - self.registered = False - self.password = "None" - self.banned = False - self.banned_reason = "You have been banned!" - self.played_time = time.time() - self.login_time - self.last_login = datetime.now() - self.first_seen = datetime.now() def kick(self, kick_message = "You have been kicked from the server!"): self.player.KickPlayer(kick_message) + def msg(self, message): + self.player.sendMessage(message) + @property def name(self): return self.player.getName() @@ -31,6 +84,7 @@ class py_player: @property def uuid(self): return str(self.player.getUniqueId()) + class Py_players: @@ -57,30 +111,71 @@ py_players = Py_players() @async(daemon=True) def fetch_player(player): + + properties = [] + keys = [] + for key, value in player.props.iteritems(): + keys.append(key) + properties.append(value) + with mysql_connect() as sql: sql.execute("SELECT * FROM utils_players WHERE uuid = ?", (player.uuid,)) result = sql.fetchall() if len(result) is 0: with mysql_connect() as sql: - sql.execute("INSERT INTO utils_players \ - (uuid, name, nickname, registered, password, banned, \ - banned_reason, played_time, last_login, first_seen) \ - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", - - args=(player.uuid, player.name, player.nickname, player.registered, - player.password, player.banned, - player.banned_reason, player.played_time, - player.last_login, player.first_seen)) + sql.execute("INSERT INTO utils_players %s \ + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" % str(tuple(keys)).replace("\'","") + ,args=tuple(properties)) + + elif len(result) is 1: + keys = [] + props = result[0] + with mysql_connect() as sql: + sql.execute("SHOW COLUMNS FROM utils_players") + result = sql.fetchall() + for row in result: + keys.append(row[0]) + + for key in keys: + player.props[key] = props[keys.index(key)] + + for prop in properties: + print str(prop) + else: - pass - #test + player.kick("Something went wrong while loading your player data, please contact an admin") + return + + player.logging_in = False + player.msg("You have succesfully logged into redstoner!") + fire_event("player_login", player) + + + + +blocked_events = ["block.BlockBreakEvent", "block.BlockPlaceEvent", "player.PlayerMoveEvent", + "player.AsyncPlayerChatEvent","player.PlayerTeleportEvent", + "player.PlayerCommandPreprocessEvent", "player.PlayerInteractEvent"] + +for event in blocked_events: + @hook.event(event,"highest") + def on_blocked_event(event): + player = py_players[event.getPlayer()] + if player.logging_in: + event.setCancelled(True) + @hook.event("player.PlayerJoinEvent","lowest") def on_join(event): - player = py_player(event.getPlayer()) + try: + player = py_player(event.getPlayer()) + except: + print(print_traceback()) + time.sleep(10) py_players.append(player) + player.msg("Your input will be blocked for a short while") fetch_player(player) |