refactor permissions and allow hostmasks to be assigned to accounts
This commit is contained in:
parent
9f8c5acf52
commit
2dfc55fb9f
8 changed files with 253 additions and 301 deletions
|
@ -305,7 +305,7 @@ class Module(ModuleManager.BaseModule):
|
||||||
flags = channel.get_user_setting(user.get_id(), "flags", "")
|
flags = channel.get_user_setting(user.get_id(), "flags", "")
|
||||||
|
|
||||||
if flags:
|
if flags:
|
||||||
identified = not user.get_identified_account() == None
|
identified = not user._id_override == None
|
||||||
current_modes = channel.get_user_modes(user)
|
current_modes = channel.get_user_modes(user)
|
||||||
|
|
||||||
modes = []
|
modes = []
|
||||||
|
|
|
@ -218,12 +218,12 @@ class Module(ModuleManager.BaseModule):
|
||||||
# response to a WHO command for user information
|
# response to a WHO command for user information
|
||||||
@utils.hook("raw.received.352", default_event=True)
|
@utils.hook("raw.received.352", default_event=True)
|
||||||
def handle_352(self, event):
|
def handle_352(self, event):
|
||||||
core.handle_352(event)
|
core.handle_352(self.events, event)
|
||||||
|
|
||||||
# response to a WHOX command for user information, including account name
|
# response to a WHOX command for user information, including account name
|
||||||
@utils.hook("raw.received.354", default_event=True)
|
@utils.hook("raw.received.354", default_event=True)
|
||||||
def handle_354(self, event):
|
def handle_354(self, event):
|
||||||
core.handle_354(event)
|
core.handle_354(self.events, event)
|
||||||
|
|
||||||
# response to an empty mode command
|
# response to an empty mode command
|
||||||
@utils.hook("raw.received.324")
|
@utils.hook("raw.received.324")
|
||||||
|
|
|
@ -72,8 +72,7 @@ def join(events, event):
|
||||||
hostname=event["line"].source.hostname)
|
hostname=event["line"].source.hostname)
|
||||||
|
|
||||||
if account:
|
if account:
|
||||||
user.identified_account = account
|
user.account = account
|
||||||
user.identified_account_id = event["server"].get_user(account).get_id()
|
|
||||||
if realname:
|
if realname:
|
||||||
user.realname = realname
|
user.realname = realname
|
||||||
|
|
||||||
|
|
|
@ -105,7 +105,7 @@ def invite(events, event):
|
||||||
events.on("received.invite").call(user=user, target_channel=target_channel,
|
events.on("received.invite").call(user=user, target_channel=target_channel,
|
||||||
server=event["server"], target_user=target_user)
|
server=event["server"], target_user=target_user)
|
||||||
|
|
||||||
def handle_352(event):
|
def handle_352(events, event):
|
||||||
nickname = event["line"].args[5]
|
nickname = event["line"].args[5]
|
||||||
username = event["line"].args[2]
|
username = event["line"].args[2]
|
||||||
hostname = event["line"].args[3]
|
hostname = event["line"].args[3]
|
||||||
|
@ -117,8 +117,10 @@ def handle_352(event):
|
||||||
target = event["server"].get_user(nickname)
|
target = event["server"].get_user(nickname)
|
||||||
target.username = username
|
target.username = username
|
||||||
target.hostname = hostname
|
target.hostname = hostname
|
||||||
|
events.on("received.who").call(server=event["server"],
|
||||||
|
user=target)
|
||||||
|
|
||||||
def handle_354(event):
|
def handle_354(events, event):
|
||||||
if event["line"].args[1] == "111":
|
if event["line"].args[1] == "111":
|
||||||
nickname = event["line"].args[4]
|
nickname = event["line"].args[4]
|
||||||
username = event["line"].args[2]
|
username = event["line"].args[2]
|
||||||
|
@ -136,11 +138,11 @@ def handle_354(event):
|
||||||
target.hostname = hostname
|
target.hostname = hostname
|
||||||
target.realname = realname
|
target.realname = realname
|
||||||
if not account == "0":
|
if not account == "0":
|
||||||
target.identified_account = account
|
target.account = account
|
||||||
target.identified_account_id = event["server"].get_user(account
|
|
||||||
).get_id()
|
|
||||||
else:
|
else:
|
||||||
target.identified_account = None
|
target.account = None
|
||||||
|
events.on("received.whox").call(server=event["server"],
|
||||||
|
user=target)
|
||||||
|
|
||||||
def _nick_in_use(server):
|
def _nick_in_use(server):
|
||||||
new_nick = "%s|" % server.connection_params.nickname
|
new_nick = "%s|" % server.connection_params.nickname
|
||||||
|
|
|
@ -39,6 +39,8 @@ def nick(events, event):
|
||||||
new_nickname = event["line"].args.get(0)
|
new_nickname = event["line"].args.get(0)
|
||||||
user = event["server"].get_user(event["line"].source.nickname)
|
user = event["server"].get_user(event["line"].source.nickname)
|
||||||
old_nickname = user.nickname
|
old_nickname = user.nickname
|
||||||
|
user.set_nickname(new_nickname)
|
||||||
|
event["server"].change_user_nickname(old_nickname, new_nickname)
|
||||||
|
|
||||||
if not event["server"].is_own_nickname(event["line"].source.nickname):
|
if not event["server"].is_own_nickname(event["line"].source.nickname):
|
||||||
events.on("received.nick").call(new_nickname=new_nickname,
|
events.on("received.nick").call(new_nickname=new_nickname,
|
||||||
|
@ -48,9 +50,6 @@ def nick(events, event):
|
||||||
new_nickname=new_nickname, old_nickname=old_nickname)
|
new_nickname=new_nickname, old_nickname=old_nickname)
|
||||||
event["server"].set_own_nickname(new_nickname)
|
event["server"].set_own_nickname(new_nickname)
|
||||||
|
|
||||||
user.set_nickname(new_nickname)
|
|
||||||
event["server"].change_user_nickname(old_nickname, new_nickname)
|
|
||||||
|
|
||||||
def away(events, event):
|
def away(events, event):
|
||||||
user = event["server"].get_user(event["line"].source.nickname)
|
user = event["server"].get_user(event["line"].source.nickname)
|
||||||
message = event["line"].args.get(0)
|
message = event["line"].args.get(0)
|
||||||
|
@ -94,13 +93,10 @@ def account(events, event):
|
||||||
user = event["server"].get_user(event["line"].source.nickname)
|
user = event["server"].get_user(event["line"].source.nickname)
|
||||||
|
|
||||||
if not event["line"].args[0] == "*":
|
if not event["line"].args[0] == "*":
|
||||||
user.identified_account = event["line"].args[0]
|
user.account = event["line"].args[0]
|
||||||
user.identified_account_id = event["server"].get_user(
|
|
||||||
event["line"].args[0]).get_id()
|
|
||||||
events.on("received.account.login").call(user=user,
|
events.on("received.account.login").call(user=user,
|
||||||
server=event["server"], account=event["line"].args[0])
|
server=event["server"], account=event["line"].args[0])
|
||||||
else:
|
else:
|
||||||
user.identified_account = None
|
user.account = None
|
||||||
user.identified_account_id = None
|
|
||||||
events.on("received.account.logout").call(user=user,
|
events.on("received.account.logout").call(user=user,
|
||||||
server=event["server"])
|
server=event["server"])
|
||||||
|
|
|
@ -1,38 +0,0 @@
|
||||||
# Permissions
|
|
||||||
|
|
||||||
## Adding an admin user
|
|
||||||
|
|
||||||
This is a little complex at the moment but it will get easier some time soon.
|
|
||||||
|
|
||||||
### Registering user
|
|
||||||
|
|
||||||
Join a channel that BitBot is in (he'll automatically join #bitbot with default
|
|
||||||
configuration) and then type
|
|
||||||
|
|
||||||
> /msg <botnick> register <password>
|
|
||||||
|
|
||||||
### Give * permission
|
|
||||||
|
|
||||||
The `*` permission is a special permission that gives you completely unfettered
|
|
||||||
access to all of BitBot's functions.
|
|
||||||
|
|
||||||
On IRC, send this to BitBot and take note of the ID response
|
|
||||||
|
|
||||||
> /msg <botnick> myid
|
|
||||||
|
|
||||||
Then take that ID and open the database in sqlite3 (default database location is
|
|
||||||
`databases/bot.db`
|
|
||||||
|
|
||||||
> $ sqlite3 databases/bot.db
|
|
||||||
|
|
||||||
And then insert your `*` permission
|
|
||||||
|
|
||||||
> INSERT INTO user_settings VALUES (<id>, 'permissions', '["*"]');
|
|
||||||
|
|
||||||
(where `<id>` is the response from the `myid` command)
|
|
||||||
|
|
||||||
### Authenticating
|
|
||||||
|
|
||||||
To authenticate yourself as your admin user, use the following command
|
|
||||||
|
|
||||||
> /msg <botnick> identify <password>
|
|
|
@ -1,52 +1,20 @@
|
||||||
#--depends-on commands
|
|
||||||
#--depends-on config
|
|
||||||
|
|
||||||
import base64, binascii, os
|
import base64, binascii, os
|
||||||
import scrypt
|
import scrypt
|
||||||
from src import ModuleManager, utils
|
from src import ModuleManager, utils
|
||||||
|
|
||||||
REQUIRES_IDENTIFY = "You need to be identified to use that command"
|
HOSTMASKS_SETTING = "hostmask-account"
|
||||||
REQUIRES_IDENTIFY_INTERNAL = ("You need to be identified to use that command "
|
NO_PERMISSION = "You do not have permission to do that"
|
||||||
"(/msg %s register | /msg %s identify)")
|
|
||||||
|
|
||||||
@utils.export("serverset", utils.OptionsSetting(["internal", "ircv3-account"],
|
|
||||||
"identity-mechanism", "Set the identity mechanism for this server"))
|
|
||||||
class Module(ModuleManager.BaseModule):
|
class Module(ModuleManager.BaseModule):
|
||||||
@utils.hook("new.user")
|
@utils.hook("new.server")
|
||||||
def new_user(self, event):
|
def new_server(self, event):
|
||||||
self._logout(event["user"])
|
hostmasks = {}
|
||||||
event["user"].admin_master = False
|
|
||||||
|
|
||||||
def _master_password(self):
|
for account, user_hostmasks in event["server"].get_all_user_settings(
|
||||||
master_password = self._random_password()
|
HOSTMASKS_SETTING):
|
||||||
hash, salt = self._make_hash(master_password)
|
for hostmask in user_hostmasks:
|
||||||
self.bot.set_setting("master-password", [hash, salt])
|
hostmasks[hostmask] = account
|
||||||
return master_password
|
event["server"]._hostmasks = hostmasks
|
||||||
|
|
||||||
@utils.hook("control.master-password")
|
|
||||||
def command_line(self, event):
|
|
||||||
master_password = self._master_password()
|
|
||||||
return "One-time master password: %s" % master_password
|
|
||||||
@utils.hook("received.command.masterpassword", private_only=True)
|
|
||||||
def master_password(self, event):
|
|
||||||
"""
|
|
||||||
:permission: master-password
|
|
||||||
"""
|
|
||||||
master_password = self._master_password()
|
|
||||||
event["stdout"].write("One-time master password: %s" %
|
|
||||||
master_password)
|
|
||||||
|
|
||||||
@utils.hook("received.part")
|
|
||||||
def on_part(self, event):
|
|
||||||
if len(event["user"].channels) == 0 and event["user"
|
|
||||||
].identified_account_override:
|
|
||||||
event["user"].send_notice("You no longer share any channels "
|
|
||||||
"with me so you have been signed out")
|
|
||||||
|
|
||||||
def _get_hash(self, server, account):
|
|
||||||
hash, salt = server.get_user(account).get_setting("authentication",
|
|
||||||
(None, None))
|
|
||||||
return hash, salt
|
|
||||||
|
|
||||||
def _make_salt(self):
|
def _make_salt(self):
|
||||||
return base64.b64encode(os.urandom(64)).decode("utf8")
|
return base64.b64encode(os.urandom(64)).decode("utf8")
|
||||||
|
@ -59,46 +27,135 @@ class Module(ModuleManager.BaseModule):
|
||||||
hash = base64.b64encode(scrypt.hash(password, salt)).decode("utf8")
|
hash = base64.b64encode(scrypt.hash(password, salt)).decode("utf8")
|
||||||
return hash, salt
|
return hash, salt
|
||||||
|
|
||||||
def _identified(self, server, user, account):
|
def _get_hash(self, server, account):
|
||||||
user.identified_account_override = account
|
hash, salt = server.get_user(account).get_setting("authentication",
|
||||||
user.identified_account_id_override = server.get_user(account).get_id()
|
|
||||||
|
|
||||||
def _logout(self, user):
|
|
||||||
user.identified_account_override = None
|
|
||||||
user.identified_account_id_override = None
|
|
||||||
|
|
||||||
@utils.hook("received.command.masterlogin", private_only=True, min_args=1)
|
|
||||||
def master_login(self, event):
|
|
||||||
saved_hash, saved_salt = self.bot.get_setting("master-password",
|
|
||||||
(None, None))
|
(None, None))
|
||||||
|
return hash, salt
|
||||||
|
|
||||||
if saved_hash and saved_salt:
|
def _has_identified(self, server, user, account):
|
||||||
given_hash, _ = self._make_hash(event["args"], saved_salt)
|
user._id_override = server.get_user_id(account)
|
||||||
if utils.security.constant_time_compare(given_hash, saved_hash):
|
def _is_identified(self, user):
|
||||||
self.bot.del_setting("master-password")
|
return not user._id_override == None
|
||||||
event["user"].admin_master = True
|
def _signout(self, user):
|
||||||
event["stdout"].write("Master login successful")
|
user._id_override = None
|
||||||
return
|
|
||||||
event["stderr"].write("Master login failed")
|
|
||||||
|
|
||||||
|
def _find_hostmask(self, server, user):
|
||||||
|
user_hostmask = user.hostmask()
|
||||||
|
for hostmask in server._hostmasks.keys():
|
||||||
|
if utils.irc.hostmask_match(user_hostmask, hostmask):
|
||||||
|
return (hostmask, server._hostmasks[hostmask])
|
||||||
|
def _specific_hostmask(self, server, hostmask, account):
|
||||||
|
for user in server.users.values():
|
||||||
|
if utils.irc.hostmask_match(user.hostmask(), hostmask):
|
||||||
|
if account == None:
|
||||||
|
user._hostmask_account = None
|
||||||
|
self._signout(user)
|
||||||
|
else:
|
||||||
|
user._hostmask_account = (hostmask, account)
|
||||||
|
self._has_identified(server, user, account)
|
||||||
|
|
||||||
|
def _account_name(self, user):
|
||||||
|
if not user.account == None:
|
||||||
|
return user.account
|
||||||
|
elif not user._account_override == None:
|
||||||
|
return user._account_override
|
||||||
|
elif not user._hostmask_account == None:
|
||||||
|
return user._hostmask_account[1]
|
||||||
|
|
||||||
|
@utils.hook("new.user")
|
||||||
|
def new_user(self, event):
|
||||||
|
event["user"]._hostmask_account = None
|
||||||
|
event["user"]._account_override = None
|
||||||
|
|
||||||
|
def _set_hostmask(self, server, user):
|
||||||
|
account = self._find_hostmask(server, user)
|
||||||
|
if not account == None:
|
||||||
|
hostmask, account = account
|
||||||
|
user._hostmask_account = (hostmask, account)
|
||||||
|
self._has_identified(server, user, account)
|
||||||
|
|
||||||
|
@utils.hook("received.chghost")
|
||||||
|
@utils.hook("received.nick")
|
||||||
|
def chghost(self, event):
|
||||||
|
if not self._is_identified(event["user"]):
|
||||||
|
self._set_hostmask(event["server"], event["user"])
|
||||||
|
@utils.hook("received.whox")
|
||||||
|
@utils.hook("received.account")
|
||||||
|
@utils.hook("received.account.login")
|
||||||
|
@utils.hook("received.account.logout")
|
||||||
|
@utils.hook("received.join")
|
||||||
|
def check_account(self, event):
|
||||||
|
if not self._is_identified(event["user"]):
|
||||||
|
if event["user"].account:
|
||||||
|
self._has_identified(event["server"], event["user"],
|
||||||
|
event["user"].account)
|
||||||
|
else:
|
||||||
|
self._set_hostmask(event["server"], event["user"])
|
||||||
|
|
||||||
|
def _get_permissions(self, user):
|
||||||
|
if self._is_identified(user):
|
||||||
|
return user.get_setting("permissions", [])
|
||||||
|
return []
|
||||||
|
|
||||||
|
def _has_permission(self, user, permission):
|
||||||
|
permissions = self._get_permissions(user)
|
||||||
|
if permission in permissions:
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
permission_parts = permission.split(".")
|
||||||
|
for user_permission in permissions:
|
||||||
|
user_permission_parts = user_permission.split(".")
|
||||||
|
for i, part in enumerate(permission_parts):
|
||||||
|
last = i==(len(permission_parts)-1)
|
||||||
|
user_last = i==(len(user_permission_parts)-1)
|
||||||
|
if not permission_parts[i] == user_permission_parts[i]:
|
||||||
|
if user_permission_parts == "*" and user_last:
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
if last and user_last:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
@utils.hook("received.command.mypermissions")
|
||||||
|
@utils.kwarg("authenticated", True)
|
||||||
|
def my_permissions(self, event):
|
||||||
|
"""
|
||||||
|
:help: Show your permissions
|
||||||
|
"""
|
||||||
|
permissions = event["user"].get_setting("permissions", [])
|
||||||
|
event["stdout"].write("Your permissions: %s" % ", ".join(permissions))
|
||||||
|
|
||||||
|
|
||||||
|
@utils.hook("received.command.register", private_only=True, min_args=1)
|
||||||
|
@utils.kwarg("min_args", 1)
|
||||||
|
@utils.kwarg("private_only", True)
|
||||||
|
@utils.kwarg("help", "Register your nickname")
|
||||||
|
@utils.kwarg("usage", "<password>")
|
||||||
|
def register(self, event):
|
||||||
|
hash, salt = self._get_hash(event["server"], event["user"].nickname)
|
||||||
|
if not hash and not salt:
|
||||||
|
password = event["args"]
|
||||||
|
hash, salt = self._make_hash(password)
|
||||||
|
event["user"].set_setting("authentication", [hash, salt])
|
||||||
|
self._has_identified(event["server"], event["user"],
|
||||||
|
event["user"].nickname)
|
||||||
|
event["stdout"].write("Nickname registered successfully")
|
||||||
|
else:
|
||||||
|
event["stderr"].write("This nickname is already registered")
|
||||||
|
|
||||||
@utils.hook("received.command.identify", private_only=True, min_args=1)
|
@utils.hook("received.command.identify", private_only=True, min_args=1)
|
||||||
|
@utils.kwarg("min_args", 1)
|
||||||
|
@utils.kwarg("private_only", True)
|
||||||
|
@utils.kwarg("help", "Identify for your current nickname")
|
||||||
|
@utils.kwarg("usage", "[account] <password>")
|
||||||
def identify(self, event):
|
def identify(self, event):
|
||||||
"""
|
|
||||||
:help: Identify yourself
|
|
||||||
:usage: [account] <password>
|
|
||||||
"""
|
|
||||||
identity_mechanism = event["server"].get_setting("identity-mechanism",
|
|
||||||
"internal")
|
|
||||||
if not identity_mechanism == "internal":
|
|
||||||
raise utils.EventError("The 'identify' command isn't available "
|
|
||||||
"on this network")
|
|
||||||
|
|
||||||
if not event["user"].channels:
|
if not event["user"].channels:
|
||||||
raise utils.EventError("You must share at least one channel "
|
raise utils.EventError("You must share at least one channel "
|
||||||
"with me before you can identify")
|
"with me before you can identify")
|
||||||
|
|
||||||
if not event["user"].identified_account_override:
|
if not self._is_identified(event["user"]):
|
||||||
if len(event["args_split"]) > 1:
|
if len(event["args_split"]) > 1:
|
||||||
account = event["args_split"][0]
|
account = event["args_split"][0]
|
||||||
password = " ".join(event["args_split"][1:])
|
password = " ".join(event["args_split"][1:])
|
||||||
|
@ -110,9 +167,11 @@ class Module(ModuleManager.BaseModule):
|
||||||
if hash and salt:
|
if hash and salt:
|
||||||
attempt, _ = self._make_hash(password, salt)
|
attempt, _ = self._make_hash(password, salt)
|
||||||
if utils.security.constant_time_compare(attempt, hash):
|
if utils.security.constant_time_compare(attempt, hash):
|
||||||
self._identified(event["server"], event["user"], account)
|
event["user"]._account_override = account
|
||||||
|
self._has_identified(event["server"], event["user"], account)
|
||||||
|
|
||||||
event["stdout"].write("Correct password, you have "
|
event["stdout"].write("Correct password, you have "
|
||||||
"been identified as '%s'." % account)
|
"been identified as %s." % account)
|
||||||
self.events.on("internal.identified").call(
|
self.events.on("internal.identified").call(
|
||||||
user=event["user"])
|
user=event["user"])
|
||||||
else:
|
else:
|
||||||
|
@ -122,179 +181,118 @@ class Module(ModuleManager.BaseModule):
|
||||||
event["stderr"].write("Account '%s' is not registered" %
|
event["stderr"].write("Account '%s' is not registered" %
|
||||||
account)
|
account)
|
||||||
else:
|
else:
|
||||||
event["stderr"].write("You are already identified")
|
event["stderr"].write("You are already identified as %s" %
|
||||||
|
self._account_name(event["user"]))
|
||||||
|
|
||||||
@utils.hook("received.command.register", private_only=True, min_args=1)
|
@utils.hook("received.command.permission")
|
||||||
def register(self, event):
|
@utils.kwarg("min_args", 2)
|
||||||
"""
|
@utils.kwarg("usage", "list <account>")
|
||||||
:help: Register yourself
|
@utils.kwarg("usage", "clear <account>")
|
||||||
:usage: <password>
|
@utils.kwarg("usage", "add <account> <permission>")
|
||||||
"""
|
@utils.kwarg("usage", "remove <account> <permission>")
|
||||||
identity_mechanism = event["server"].get_setting("identity-mechanism",
|
@utils.kwarg("permission", "permissions.change")
|
||||||
"internal")
|
def permission(self, event):
|
||||||
if not identity_mechanism == "internal":
|
subcommand = event["args_split"][0].lower()
|
||||||
raise utils.EventError("The 'identify' command isn't available "
|
account = event["args_split"][1]
|
||||||
"on this network")
|
target_user = event["server"].get_user(account)
|
||||||
|
|
||||||
hash, salt = self._get_hash(event["server"], event["user"].nickname)
|
if subcommand == "list":
|
||||||
if not hash and not salt:
|
event["stdout"].write("Permissions for %s: %s" % (
|
||||||
password = event["args"]
|
account, ", ".join(self._get_permissions(target_user))))
|
||||||
hash, salt = self._make_hash(password)
|
elif subcommand == "clear":
|
||||||
event["user"].set_setting("authentication", [hash, salt])
|
if not self._get_permissions(target_user):
|
||||||
self._identified(event["server"], event["user"],
|
raise utils.EventError("%s has no permissions" % account)
|
||||||
event["user"].nickname)
|
target_user.del_setting("permissions")
|
||||||
event["stdout"].write("Nickname registered successfully")
|
event["stdout"].write("Cleared permissions for %s" % account)
|
||||||
else:
|
else:
|
||||||
event["stderr"].write("This nickname is already registered")
|
permissions = event["args_split"][2:]
|
||||||
|
if not permissions:
|
||||||
|
raise utils.EventError("Please provide at least 1 permission")
|
||||||
|
user_permissions = self._get_permissions(target_user)
|
||||||
|
|
||||||
@utils.hook("received.command.setpassword", authenticated=True, min_args=1)
|
if subcommand == "add":
|
||||||
def set_password(self, event):
|
new = list(set(permissions)-set(user_permissions))
|
||||||
"""
|
if not new:
|
||||||
:help: Change your password
|
raise utils.EventError("No new permissions to give")
|
||||||
:usage: <password>
|
target_user.set_setting("permissions", user_permissions+new)
|
||||||
"""
|
event["stdout"].write("Gave %s new permissions: %s" %
|
||||||
hash, salt = self._make_hash(event["args"])
|
(account, ", ".join(new)))
|
||||||
event["user"].set_setting("authentication", [hash, salt])
|
elif subcommand == "remove":
|
||||||
event["stdout"].write("Set your password")
|
permissions_set = set(permissions)
|
||||||
|
user_permissions_set = set(user_permissions)
|
||||||
|
removed = list(user_permissions_set&permissions_set)
|
||||||
|
if not (user_permissions_set & permissions_set):
|
||||||
|
raise utils.EventError("New permissions to remove")
|
||||||
|
change = list(user_permissions_set - permissions_set)
|
||||||
|
|
||||||
@utils.hook("received.command.logout", private_only=True)
|
if not change:
|
||||||
def logout(self, event):
|
target_user.del_setting("permissions")
|
||||||
"""
|
|
||||||
:help: Logout from your identified account
|
|
||||||
"""
|
|
||||||
if event["user"].identified_account_override:
|
|
||||||
self._logout(event["user"])
|
|
||||||
event["stdout"].write("You have been logged out")
|
|
||||||
else:
|
else:
|
||||||
event["stderr"].write("You are not logged in")
|
target_user.set_setting("permissions", change)
|
||||||
|
event["stdout"].write("Removed permissions from %s: %s" %
|
||||||
@utils.hook("received.command.resetpassword", private_only=True,
|
(account, ", ".join(change)))
|
||||||
min_args=2)
|
|
||||||
def reset_password(self, event):
|
|
||||||
"""
|
|
||||||
:help: Reset a given user's password
|
|
||||||
:usage: <nickname> <password>
|
|
||||||
:permission: resetpassword
|
|
||||||
"""
|
|
||||||
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:
|
else:
|
||||||
hash, salt = self._make_hash(password)
|
raise utils.EventError("Unknown subcommand %s" % subcommand)
|
||||||
target.set_setting("authentication", [hash, salt])
|
|
||||||
event["stdout"].write("Reset password for '%s'" %
|
|
||||||
target.nickname)
|
|
||||||
|
|
||||||
def _check_command(self, event, permission, authenticated):
|
@utils.hook("received.command.hostmask")
|
||||||
if event["user"].admin_master and (permission or authenticated):
|
@utils.kwarg("min_args", 1)
|
||||||
return utils.consts.PERMISSION_FORCE_SUCCESS, None
|
@utils.kwarg("authenticated", True)
|
||||||
|
@utils.kwarg("usage", "list")
|
||||||
|
@utils.kwarg("usage", "add [hostmask]")
|
||||||
|
@utils.kwarg("usage", "remove [hostmask]")
|
||||||
|
def hostmask(self, event):
|
||||||
|
subcommand = event["args_split"][0].lower()
|
||||||
|
hostmasks = event["user"].get_setting(HOSTMASKS_SETTING, [])
|
||||||
|
|
||||||
identity_mechanism = event["server"].get_setting("identity-mechanism",
|
if subcommand == "list":
|
||||||
"internal")
|
event["stdout"].write("Your hostmasks: %s" % ", ".join(hostmasks))
|
||||||
identified_account = None
|
|
||||||
if identity_mechanism == "internal":
|
|
||||||
identified_account = event["user"].identified_account_override
|
|
||||||
elif identity_mechanism == "ircv3-account":
|
|
||||||
identified_account = (event["user"].identified_account or
|
|
||||||
event["tags"].get("account", None))
|
|
||||||
|
|
||||||
identified_user = None
|
|
||||||
permissions = []
|
|
||||||
if identified_account:
|
|
||||||
identified_user = event["server"].get_user(identified_account)
|
|
||||||
permissions = identified_user.get_setting("permissions", [])
|
|
||||||
|
|
||||||
if permission:
|
|
||||||
has_permission = permission and (
|
|
||||||
permission in permissions or "*" in permissions)
|
|
||||||
if not identified_account or not has_permission:
|
|
||||||
return (utils.consts.PERMISSION_ERROR,
|
|
||||||
"You do not have permission to do that")
|
|
||||||
else:
|
else:
|
||||||
return utils.consts.PERMISSION_FORCE_SUCCESS, None
|
if event["args_split"][1:]:
|
||||||
elif authenticated:
|
hostmask = event["args_split"][1]
|
||||||
if not identified_account:
|
|
||||||
error = None
|
|
||||||
if identity_mechanism == "internal":
|
|
||||||
error = REQUIRES_IDENTIFY_INTERNAL % (
|
|
||||||
event["server"].nickname, event["server"].nickname)
|
|
||||||
else:
|
else:
|
||||||
error = REQUIRES_IDENTIFY
|
hostmask = "*!%s" % event["user"].userhost()
|
||||||
return utils.consts.PERMISSION_ERROR, error
|
account = self._account_name(event["user"])
|
||||||
|
|
||||||
|
if subcommand == "add":
|
||||||
|
if hostmask in hostmasks:
|
||||||
|
raise utils.EventError(
|
||||||
|
"Hostmask %s is already on your account" % hostmask)
|
||||||
|
hostmasks.append(hostmask)
|
||||||
|
event["user"].set_setting(HOSTMASKS_SETTING, hostmasks)
|
||||||
|
|
||||||
|
self._specific_hostmask(event["server"], hostmask, account)
|
||||||
|
event["server"]._hostmasks[hostmask] = account
|
||||||
|
|
||||||
|
event["stdout"].write("Added %s to your hostmasks" % hostmask)
|
||||||
|
elif subcommand == "remove":
|
||||||
|
if not hostmask in hostmasks:
|
||||||
|
raise utils.EventError("Hostmask %s is not on your account"
|
||||||
|
% hostmask)
|
||||||
|
while hostmask in hostmasks:
|
||||||
|
hostmasks.remove(hostmask)
|
||||||
|
event["user"].set_setting(HOSTMASKS_SETTING, hostmasks)
|
||||||
|
|
||||||
|
self._specific_hostmask(event["server"], hostmask, None)
|
||||||
|
if hostmask in event["server"]._hostmasks:
|
||||||
|
del event["server"]._hostmasks[hostmask]
|
||||||
|
|
||||||
|
event["stdout"].write("Removed %s from your hostmasks"
|
||||||
|
% hostmask)
|
||||||
else:
|
else:
|
||||||
return utils.consts.PERMISSION_FORCE_SUCCESS, None
|
raise utils.EventError("Unknown subcommand %s" % subcommand)
|
||||||
|
|
||||||
@utils.hook("preprocess.command")
|
@utils.hook("preprocess.command")
|
||||||
def preprocess_command(self, event):
|
def preprocess_command(self, event):
|
||||||
|
allowed = None
|
||||||
permission = event["hook"].get_kwarg("permission", None)
|
permission = event["hook"].get_kwarg("permission", None)
|
||||||
authenticated = event["hook"].get_kwarg("authenticated", False)
|
authenticated = event["hook"].get_kwarg("authenticated", False)
|
||||||
return self._check_command(event, permission, authenticated)
|
if not permission == None:
|
||||||
|
allowed = self._has_permission(event["user"], permission)
|
||||||
|
elif not authenticated == None:
|
||||||
|
allowed = self._is_identified(event["user"])
|
||||||
|
|
||||||
@utils.hook("check.command.permission")
|
if not allowed == None:
|
||||||
def check_command(self, event):
|
if allowed:
|
||||||
return self._check_command(event, event["request_args"][0], False)
|
return utils.consts.PERMISSION_FORCE_SUCCESS, None
|
||||||
|
|
||||||
@utils.hook("received.command.mypermissions", authenticated=True)
|
|
||||||
def my_permissions(self, event):
|
|
||||||
"""
|
|
||||||
:help: Show your permissions
|
|
||||||
"""
|
|
||||||
permissions = event["user"].get_setting("permissions", [])
|
|
||||||
event["stdout"].write("Your permissions: %s" % ", ".join(permissions))
|
|
||||||
|
|
||||||
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]
|
|
||||||
|
|
||||||
@utils.hook("received.command.givepermission", min_args=2)
|
|
||||||
def give_permission(self, event):
|
|
||||||
"""
|
|
||||||
:help: Give a given permission to a given user
|
|
||||||
:usage: <nickname> <permission>
|
|
||||||
:permission: givepermission
|
|
||||||
"""
|
|
||||||
permission = event["args_split"][1].lower()
|
|
||||||
target, registered, permissions = self._get_user_details(
|
|
||||||
event["server"], event["args_split"][0])
|
|
||||||
|
|
||||||
if target.get_identified_account() == None:
|
|
||||||
raise utils.EventError("%s isn't registered" % target.nickname)
|
|
||||||
|
|
||||||
if permission in permissions:
|
|
||||||
event["stderr"].write("%s already has permission '%s'" % (
|
|
||||||
target.nickname, permission))
|
|
||||||
else:
|
else:
|
||||||
permissions.append(permission)
|
return utils.consts.PERMISSION_ERROR, NO_PERMISSION
|
||||||
target.set_setting("permissions", permissions)
|
|
||||||
event["stdout"].write("Gave permission '%s' to %s" % (
|
|
||||||
permission, target.nickname))
|
|
||||||
@utils.hook("received.command.removepermission", min_args=2)
|
|
||||||
def remove_permission(self, event):
|
|
||||||
"""
|
|
||||||
:help: Remove a given permission from a given user
|
|
||||||
:usage: <nickname> <permission>
|
|
||||||
:permission: removepermission
|
|
||||||
"""
|
|
||||||
permission = event["args_split"][1].lower()
|
|
||||||
target, registered, permissions = self._get_user_details(
|
|
||||||
event["server"], event["args_split"][0])
|
|
||||||
|
|
||||||
if target.identified_account == None:
|
|
||||||
raise utils.EventError("%s isn't registered" % target.nickname)
|
|
||||||
|
|
||||||
if permission not in permissions:
|
|
||||||
event["stderr"].write("%s doesn't have permission '%s'" % (
|
|
||||||
target.nickname, permission))
|
|
||||||
else:
|
|
||||||
permissions.remove(permission)
|
|
||||||
if not permissions:
|
|
||||||
target.del_setting("permissions")
|
|
||||||
else:
|
|
||||||
target.set_setting("permissions", permissions)
|
|
||||||
event["stdout"].write("Removed permission '%s' from %s" % (
|
|
||||||
permission, target.nickname))
|
|
||||||
|
|
|
@ -11,17 +11,15 @@ class User(IRCObject.Object):
|
||||||
self.server = server
|
self.server = server
|
||||||
self.set_nickname(nickname)
|
self.set_nickname(nickname)
|
||||||
self._id = id
|
self._id = id
|
||||||
|
self._id_override: typing.Optional[int] = None
|
||||||
self.username: typing.Optional[str] = None
|
self.username: typing.Optional[str] = None
|
||||||
self.hostname: typing.Optional[str] = None
|
self.hostname: typing.Optional[str] = None
|
||||||
self.realname: typing.Optional[str] = None
|
self.realname: typing.Optional[str] = None
|
||||||
self.bot = bot
|
self.bot = bot
|
||||||
self.channels: typing.Set[IRCChannel.Channel] = set([])
|
self.channels: typing.Set[IRCChannel.Channel] = set([])
|
||||||
|
|
||||||
self.identified_account = None
|
self.account = None
|
||||||
self.identified_account_override = None
|
|
||||||
|
|
||||||
self.identified_account_id = None
|
|
||||||
self.identified_account_id_override = None
|
|
||||||
self.away = False
|
self.away = False
|
||||||
self.away_message: typing.Optional[str] = None
|
self.away_message: typing.Optional[str] = None
|
||||||
|
|
||||||
|
@ -42,10 +40,7 @@ class User(IRCObject.Object):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def get_id(self)-> int:
|
def get_id(self)-> int:
|
||||||
return (self.identified_account_id_override or
|
return self._id_override or self._id
|
||||||
self.identified_account_id or self._id)
|
|
||||||
def get_identified_account(self) -> typing.Optional[str]:
|
|
||||||
return (self.identified_account_override or self.identified_account)
|
|
||||||
|
|
||||||
def set_nickname(self, nickname: str):
|
def set_nickname(self, nickname: str):
|
||||||
self.nickname = nickname
|
self.nickname = nickname
|
||||||
|
|
Loading…
Reference in a new issue