2016-04-04 17:42:37 +00:00
|
|
|
import base64, os
|
|
|
|
import scrypt
|
2018-09-26 17:27:17 +00:00
|
|
|
from src import ModuleManager, Utils
|
2016-04-04 17:42:37 +00:00
|
|
|
|
2018-08-28 17:16:19 +00:00
|
|
|
REQUIRES_IDENTIFY = ("You need to be identified to use that command "
|
|
|
|
"(/msg %s register | /msg %s identify)")
|
|
|
|
|
2018-09-26 17:27:17 +00:00
|
|
|
class Module(ModuleManager.BaseModule):
|
|
|
|
@Utils.hook("new.user")
|
2016-04-04 17:42:37 +00:00
|
|
|
def new_user(self, event):
|
|
|
|
self._logout(event["user"])
|
|
|
|
|
2018-09-26 17:27:17 +00:00
|
|
|
@Utils.hook("received.part")
|
2016-04-04 17:42:37 +00:00
|
|
|
def on_part(self, event):
|
2018-09-05 10:58:10 +00:00
|
|
|
if len(event["user"].channels) == 1 and event["user"
|
|
|
|
].identified_account_override:
|
2016-04-04 17:42:37 +00:00
|
|
|
event["user"].send_notice("You no longer share any channels "
|
|
|
|
"with me so you have been signed out")
|
|
|
|
|
2018-09-05 10:58:10 +00:00
|
|
|
def _get_hash(self, server, account):
|
|
|
|
hash, salt = server.get_user(account).get_setting("authentication",
|
|
|
|
(None, None))
|
2016-04-04 17:42:37 +00:00
|
|
|
return hash, salt
|
2016-04-06 17:23:02 +00:00
|
|
|
|
2016-04-04 17:42:37 +00:00
|
|
|
def _make_salt(self):
|
|
|
|
return base64.b64encode(os.urandom(64)).decode("utf8")
|
2016-04-06 17:23:02 +00:00
|
|
|
|
2016-04-04 17:42:37 +00:00
|
|
|
def _make_hash(self, password, salt=None):
|
|
|
|
salt = salt or self._make_salt()
|
|
|
|
hash = base64.b64encode(scrypt.hash(password, salt)).decode("utf8")
|
|
|
|
return hash, salt
|
2016-04-06 17:23:02 +00:00
|
|
|
|
2018-09-05 10:58:10 +00:00
|
|
|
def _identified(self, server, user, account):
|
|
|
|
user.identified_account_override = account
|
2018-09-18 23:45:01 +00:00
|
|
|
user.identified_account_id_override = server.get_user(account).get_id()
|
2016-04-06 17:23:02 +00:00
|
|
|
|
2016-04-04 17:42:37 +00:00
|
|
|
def _logout(self, user):
|
2018-09-05 10:58:10 +00:00
|
|
|
user.identified_account_override = None
|
|
|
|
user.identified_account_id_override = None
|
2016-04-06 17:23:02 +00:00
|
|
|
|
2018-09-30 16:29:09 +00:00
|
|
|
@Utils.hook("received.command.identify", private_only=True, min_args=1)
|
2016-04-04 17:42:37 +00:00
|
|
|
def identify(self, event):
|
2018-09-26 17:27:17 +00:00
|
|
|
"""
|
2018-09-30 16:29:09 +00:00
|
|
|
:help: Identify yourself
|
|
|
|
:usage: [account] <password>
|
2018-09-26 17:27:17 +00:00
|
|
|
"""
|
2018-09-05 10:58:10 +00:00
|
|
|
identity_mechanism = event["server"].get_setting("identity-mechanism",
|
|
|
|
"internal")
|
|
|
|
if not identity_mechanism == "internal":
|
|
|
|
event["stderr"].write("The 'identify' command isn't available "
|
|
|
|
"on this network")
|
|
|
|
return
|
|
|
|
|
2016-04-04 17:48:39 +00:00
|
|
|
if not event["user"].channels:
|
|
|
|
event["stderr"].write("You must share at least one channel "
|
|
|
|
"with me before you can identify")
|
|
|
|
return
|
2018-09-05 10:58:10 +00:00
|
|
|
|
|
|
|
if not event["user"].identified_account_override:
|
2018-09-29 09:51:16 +00:00
|
|
|
if len(event["args_split"]) > 1:
|
|
|
|
account = event["args_split"][0]
|
|
|
|
password = " ".join(event["args_split"][1:])
|
|
|
|
else:
|
|
|
|
account = event["user"].nickname
|
|
|
|
password = event["args"]
|
|
|
|
|
2018-09-05 10:58:10 +00:00
|
|
|
hash, salt = self._get_hash(event["server"], account)
|
2016-04-04 17:42:37 +00:00
|
|
|
if hash and salt:
|
|
|
|
attempt, _ = self._make_hash(password, salt)
|
|
|
|
if attempt == hash:
|
2018-09-05 10:58:10 +00:00
|
|
|
self._identified(event["server"], event["user"], account)
|
2016-04-04 17:42:37 +00:00
|
|
|
event["stdout"].write("Correct password, you have "
|
2018-09-05 10:58:10 +00:00
|
|
|
"been identified as '%s'." % account)
|
2016-04-04 17:42:37 +00:00
|
|
|
else:
|
2018-09-05 10:58:10 +00:00
|
|
|
event["stderr"].write("Incorrect password for '%s'" %
|
|
|
|
account)
|
2016-04-04 17:42:37 +00:00
|
|
|
else:
|
2018-09-05 10:58:10 +00:00
|
|
|
event["stderr"].write("Account '%s' is not registered" %
|
|
|
|
account)
|
2016-04-04 17:42:37 +00:00
|
|
|
else:
|
|
|
|
event["stderr"].write("You are already identified")
|
|
|
|
|
2018-09-30 16:29:09 +00:00
|
|
|
@Utils.hook("received.command.register", private_only=True, min_args=1)
|
2016-04-04 17:42:37 +00:00
|
|
|
def register(self, event):
|
2018-09-26 17:27:17 +00:00
|
|
|
"""
|
2018-09-30 16:29:09 +00:00
|
|
|
:help: Register yourself
|
|
|
|
:usage: <password>
|
2018-09-26 17:27:17 +00:00
|
|
|
"""
|
2018-09-18 23:45:14 +00:00
|
|
|
identity_mechanism = event["server"].get_setting("identity-mechanism",
|
|
|
|
"internal")
|
|
|
|
if not identity_mechanism == "internal":
|
|
|
|
event["stderr"].write("The 'identify' command isn't available "
|
|
|
|
"on this network")
|
|
|
|
return
|
|
|
|
|
2018-09-05 10:58:10 +00:00
|
|
|
hash, salt = self._get_hash(event["server"], event["user"].nickname)
|
2016-04-04 17:42:37 +00:00
|
|
|
if not hash and not salt:
|
|
|
|
password = event["args_split"][0]
|
|
|
|
hash, salt = self._make_hash(password)
|
|
|
|
event["user"].set_setting("authentication", [hash, salt])
|
2018-09-19 00:19:04 +00:00
|
|
|
self._identified(event["server"], event["user"],
|
|
|
|
event["user"].nickname)
|
2016-04-04 17:42:37 +00:00
|
|
|
event["stdout"].write("Nickname registered successfully")
|
|
|
|
else:
|
|
|
|
event["stderr"].write("This nickname is already registered")
|
|
|
|
|
2018-09-26 17:27:17 +00:00
|
|
|
@Utils.hook("received.command.logout", private_only=True)
|
2016-04-04 17:42:37 +00:00
|
|
|
def logout(self, event):
|
2018-09-26 17:27:17 +00:00
|
|
|
"""
|
2018-09-30 16:29:09 +00:00
|
|
|
:help: Logout from your identified account
|
2018-09-26 17:27:17 +00:00
|
|
|
"""
|
2018-09-05 10:58:10 +00:00
|
|
|
if event["user"].identified_account_override:
|
2016-04-04 17:42:37 +00:00
|
|
|
self._logout(event["user"])
|
|
|
|
event["stdout"].write("You have been logged out")
|
|
|
|
else:
|
|
|
|
event["stderr"].write("You are not logged in")
|
2016-04-06 17:23:02 +00:00
|
|
|
|
2018-09-26 17:27:17 +00:00
|
|
|
@Utils.hook("received.command.resetpassword", private_only=True,
|
2018-09-30 16:29:09 +00:00
|
|
|
min_args=2)
|
2018-09-03 10:30:54 +00:00
|
|
|
def reset_password(self, event):
|
2018-09-26 17:27:17 +00:00
|
|
|
"""
|
2018-09-30 16:29:09 +00:00
|
|
|
:help: Reset a given user's password
|
|
|
|
:usage: <nickname> <password>
|
|
|
|
:permission: resetpassword
|
2018-09-26 17:27:17 +00:00
|
|
|
"""
|
2018-09-03 10:30:54 +00:00
|
|
|
target = event["server"].get_user(event["args_split"][0])
|
|
|
|
password = " ".join(event["args_split"][1:])
|
|
|
|
registered = target.get_setting("authentication", None)
|
|
|
|
|
|
|
|
if registered == None:
|
|
|
|
event["stderr"].write("'%s' isn't registered" % target.nickname)
|
|
|
|
else:
|
|
|
|
hash, salt = self._make_hash(password)
|
|
|
|
target.set_setting("authentication", [hash, salt])
|
|
|
|
event["stdout"].write("Reset password for '%s'" %
|
|
|
|
target.nickname)
|
|
|
|
|
2018-09-26 17:27:17 +00:00
|
|
|
@Utils.hook("preprocess.command")
|
2016-04-06 17:23:02 +00:00
|
|
|
def preprocess_command(self, event):
|
2018-09-30 16:29:09 +00:00
|
|
|
permission = event["hook"].get_kwarg("permission", None)
|
2018-08-18 17:28:41 +00:00
|
|
|
authenticated = event["hook"].kwargs.get("authenticated", False)
|
2018-09-05 10:58:10 +00:00
|
|
|
|
|
|
|
identity_mechanism = event["server"].get_setting("identity-mechanism",
|
|
|
|
"internal")
|
|
|
|
identified_account = None
|
|
|
|
if identity_mechanism == "internal":
|
|
|
|
identified_account = event["user"].identified_account_override
|
|
|
|
elif identity_mechanism == "ircv3-account":
|
2018-09-05 14:39:29 +00:00
|
|
|
identified_account = (event["user"].identified_account or
|
|
|
|
event["tags"].get("account", None))
|
2018-09-05 10:58:10 +00:00
|
|
|
|
|
|
|
identified_user = None
|
|
|
|
permissions = []
|
|
|
|
if identified_account:
|
|
|
|
identified_user = event["server"].get_user(identified_account)
|
|
|
|
permissions = identified_user.get_setting("permissions", [])
|
2018-08-28 17:16:19 +00:00
|
|
|
|
2018-08-02 22:00:42 +00:00
|
|
|
if permission:
|
|
|
|
has_permission = permission and (
|
2018-09-05 10:58:10 +00:00
|
|
|
permission in permissions or "*" in permissions)
|
|
|
|
if not identified_account or not has_permission:
|
2018-08-02 22:00:42 +00:00
|
|
|
return "You do not have permission to do that"
|
2018-08-18 17:28:41 +00:00
|
|
|
elif authenticated:
|
2018-09-05 10:58:10 +00:00
|
|
|
if not identified_account:
|
2018-08-28 17:16:19 +00:00
|
|
|
return REQUIRES_IDENTIFY % (event["server"].nickname,
|
|
|
|
event["server"].nickname)
|
2016-05-17 13:50:48 +00:00
|
|
|
|
2018-09-26 17:27:17 +00:00
|
|
|
@Utils.hook("received.command.mypermissions", authenticated=True)
|
2016-05-17 13:50:48 +00:00
|
|
|
def my_permissions(self, event):
|
2018-09-26 17:27:17 +00:00
|
|
|
"""
|
2018-09-30 16:29:09 +00:00
|
|
|
:help: Show your permissions
|
2018-09-26 17:27:17 +00:00
|
|
|
"""
|
2018-08-02 22:00:42 +00:00
|
|
|
permissions = event["user"].get_setting("permissions", [])
|
2016-05-17 13:50:48 +00:00
|
|
|
event["stdout"].write("Your permissions: %s" % ", ".join(permissions))
|
2018-08-28 15:53:21 +00:00
|
|
|
|
|
|
|
def _get_user_details(self, server, nickname):
|
|
|
|
target = server.get_user(nickname)
|
|
|
|
registered = bool(target.get_setting("authentication", None))
|
|
|
|
permissions = target.get_setting("permissions", [])
|
|
|
|
return [target, registered, permissions]
|
|
|
|
|
2018-09-30 16:29:09 +00:00
|
|
|
@Utils.hook("received.command.givepermission", min_args=2)
|
2018-08-28 15:53:21 +00:00
|
|
|
def give_permission(self, event):
|
2018-09-26 17:27:17 +00:00
|
|
|
"""
|
2018-09-30 16:29:09 +00:00
|
|
|
:help: Give a given permission to a given user
|
|
|
|
:usage: <nickname> <permission>
|
|
|
|
:permission: givepermission
|
2018-09-26 17:27:17 +00:00
|
|
|
"""
|
2018-08-28 15:53:21 +00:00
|
|
|
permission = event["args_split"][1].lower()
|
|
|
|
target, registered, permissions = self._get_user_details(
|
|
|
|
event["server"], event["args_split"][0])
|
|
|
|
|
2018-09-23 10:01:24 +00:00
|
|
|
if target.identified_account == None:
|
2018-08-28 15:53:21 +00:00
|
|
|
event["stderr"].write("%s isn't registered" % target.nickname)
|
|
|
|
return
|
|
|
|
|
|
|
|
if permission in permissions:
|
|
|
|
event["stderr"].write("%s already has permission '%s'" % (
|
|
|
|
target.nickname, permission))
|
|
|
|
else:
|
|
|
|
permissions.append(permission)
|
|
|
|
target.set_setting("permissions", permissions)
|
|
|
|
event["stdout"].write("Gave permission '%s' to %s" % (
|
|
|
|
permission, target.nickname))
|
2018-09-30 16:29:09 +00:00
|
|
|
@Utils.hook("received.command.removepermission", min_args=2)
|
2018-08-28 15:53:21 +00:00
|
|
|
def remove_permission(self, event):
|
2018-09-26 17:27:17 +00:00
|
|
|
"""
|
2018-09-30 16:29:09 +00:00
|
|
|
:help: Remove a given permission from a given user
|
|
|
|
:usage: <nickname> <permission>
|
|
|
|
:permission: removepermission
|
2018-09-26 17:27:17 +00:00
|
|
|
"""
|
2018-08-28 15:53:21 +00:00
|
|
|
permission = event["args_split"][1].lower()
|
|
|
|
target, registered, permissions = self._get_user_details(
|
|
|
|
event["server"], event["args_split"][0])
|
|
|
|
|
2018-09-23 10:01:24 +00:00
|
|
|
if target.identified_account == None:
|
2018-08-28 15:53:21 +00:00
|
|
|
event["stderr"].write("%s isn't registered" % target.nickname)
|
|
|
|
return
|
|
|
|
|
2018-09-23 10:09:46 +00:00
|
|
|
if permission not in permissions:
|
2018-09-23 10:06:15 +00:00
|
|
|
event["stderr"].write("%s doesn't have permission '%s'" % (
|
2018-08-28 15:53:21 +00:00
|
|
|
target.nickname, permission))
|
|
|
|
else:
|
|
|
|
permissions.remove(permission)
|
2018-08-29 13:34:52 +00:00
|
|
|
if not permissions:
|
|
|
|
target.del_setting("permissions")
|
|
|
|
else:
|
|
|
|
target.set_setting("permissions", permissions)
|
2018-08-28 15:53:21 +00:00
|
|
|
event["stdout"].write("Removed permission '%s' from %s" % (
|
|
|
|
permission, target.nickname))
|