diff --git a/conf.py b/conf.py index 7d1e213..e47ea29 100644 --- a/conf.py +++ b/conf.py @@ -51,11 +51,20 @@ def validateConf(conf): for section in ('bot', 'servers', 'login', 'logging'): assert conf.get(section), "Missing %r section in config." % section - assert type(conf['login'].get('password')) == type(conf['login'].get('user')) == str and \ - conf['login']['password'] != "changeme", "You have not set the login details correctly!" + # Make sure at least one form of authentication is valid. + old_login_valid = type(conf['login'].get('password')) == type(conf['login'].get('user')) == str + newlogins = conf['login'].get('accounts', {}) + new_login_valid = len(newlogins) >= 1 + assert old_login_valid or new_login_valid, "No accounts were set, aborting!" + for account, block in newlogins.items(): + assert type(account) == str, "Bad username format %s" % account + assert type(block.get('password')) == str, "Bad password %s for account %s" % (block.get('password'), account) + + assert conf['login'].get('password') != "changeme", "You have not set the login details correctly!" return conf + def loadConf(filename, errors_fatal=True): """Loads a PyLink configuration file from the filename given.""" global confname, conf, fname diff --git a/coremods/corecommands.py b/coremods/corecommands.py index 8616b77..f39a63f 100644 --- a/coremods/corecommands.py +++ b/coremods/corecommands.py @@ -16,6 +16,18 @@ from pylinkirc.log import log # Essential, core commands go here so that the "commands" plugin with less-important, # but still generic functions can be reloaded. +def _login(irc, source, username): + """Internal function to process logins.""" + irc.users[source].account = username + irc.reply('Successfully logged in as %s.' % username) + log.info("(%s) Successful login to %r by %s", + irc.name, username, irc.getHostmask(source)) + +def _loginfail(irc, source, username): + """Internal function to process login failures.""" + irc.reply('Error: Incorrect credentials.') + log.warning("(%s) Failed login to %r from %s", irc.name, username, irc.getHostmask(source)) + @utils.add_cmd def identify(irc, source, args): """ @@ -30,18 +42,29 @@ def identify(irc, source, args): except IndexError: irc.reply('Error: Not enough arguments.') return - # Usernames are case-insensitive, passwords are NOT. - if username.lower() == conf.conf['login']['user'].lower() and password == conf.conf['login']['password']: - realuser = conf.conf['login']['user'] - irc.users[source].account = realuser - irc.reply('Successfully logged in as %s.' % realuser) - log.info("(%s) Successful login to %r by %s", - irc.name, username, irc.getHostmask(source)) + + username = username.lower() + + # Process new-style accounts. Note: usernames are case-insensitive, passwords are NOT. + for account, block in conf.conf['login'].get('accounts', {}).items(): + if account.lower() == username: + # Username matched config. + if password and password == block.get('password'): + # Password exists and matched config. TODO: hash user passwords in config. + _login(irc, source, account) + break + else: + _loginfail(irc, source, account) + break else: - irc.reply('Error: Incorrect credentials.') - u = irc.users[source] - log.warning("(%s) Failed login to %r from %s", - irc.name, username, irc.getHostmask(source)) + # Process legacy logins (login:user). + if username.lower() == conf.conf['login'].get('user', '').lower() and password == conf.conf['login'].get('password'): + realuser = conf.conf['login']['user'] + _login(irc, source, realuser) + else: + # Username not found. + _loginfail(irc, source, username) + @utils.add_cmd def shutdown(irc, source, args): diff --git a/example-conf.yml b/example-conf.yml index b53dabd..2830175 100644 --- a/example-conf.yml +++ b/example-conf.yml @@ -46,9 +46,12 @@ bot: save_delay: 300 login: - # PyLink administrative login - Change this, or the service will not start! - user: admin - password: changeme + accounts: + # Creates an account with username "user1". + user1: + # Defines the password for the user. + password: "somestring" + servers: # Please note: these are only EXAMPLE link blocks. You should edit them and @@ -97,7 +100,7 @@ servers: # disable autoconnect entirely. autoconnect: 5 - # Sets the ping frequency in seconds (i.e. how long we should wait between + # Sets the ping frequency in seconds (i.e. how long we should wait between # sending pings to our uplink). When more than two consecutive pings are missed, # PyLink will disconnect with a ping timeout. This defaults to 90 if not set. pingfreq: 90 @@ -320,7 +323,7 @@ plugins: # nick or configurable prefix characters. #- fantasy - # Changehost plugin: Automatically changes the hostmask (i.e. sets vHosts) on + # Changehost plugin: Automatically changes the hostmask (i.e. sets vHosts) on # matching users as they connect. This requires the changehost: block # to be configured correctly below. #- changehost