Created
March 25, 2013 22:59
-
-
Save thwarted/5241639 to your computer and use it in GitHub Desktop.
Stupid and simple supybot plugin that does LDAP/AD lookups and returns phone and pager contact information. From Yelp! Hack-a-thon X, going for the "Useful" award.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
### | |
# Copyright (c) 2013, Andy Bakun and Yelp! | |
# All rights reserved. | |
# | |
# Redistribution and use in source and binary forms, with or without | |
# modification, are permitted provided that the following conditions are met: | |
# | |
# * Redistributions of source code must retain the above copyright notice, | |
# this list of conditions, and the following disclaimer. | |
# * Redistributions in binary form must reproduce the above copyright notice, | |
# this list of conditions, and the following disclaimer in the | |
# documentation and/or other materials provided with the distribution. | |
# * Neither the name of the author of this software nor the name of | |
# contributors to this software may be used to endorse or promote products | |
# derived from this software without specific prior written consent. | |
# | |
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | |
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE | |
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | |
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | |
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | |
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | |
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | |
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | |
# POSSIBILITY OF SUCH DAMAGE. | |
### | |
import supybot.conf as conf | |
import supybot.registry as registry | |
def configure(advanced): | |
# This will be called by supybot to configure this module. advanced is | |
# a bool that specifies whether the user identified himself as an advanced | |
# user or not. You should effect your configuration by manipulating the | |
# registry as appropriate. | |
from supybot.questions import expect, anything, something, yn | |
conf.registerPlugin('Scout', True) | |
Scout = conf.registerPlugin('Scout') | |
# This is where your configuration variables (if any) should go. For example: | |
# conf.registerGlobalValue(Random, 'someConfigVariableName', | |
# registry.Boolean(False, """Help for someConfigVariableName.""")) | |
conf.registerGlobalValue(Scout, 'ForceMailTo', registry.String('', """For debugging, send all email to a single address""")) | |
conf.registerGlobalValue(Scout, 'LDAP_URL', registry.String('', """LDAP Server URL""")) | |
conf.registerGlobalValue(Scout, 'LDAP_basedn_users', registry.String('', """LDAP base dn when searching for users""")) | |
conf.registerGlobalValue(Scout, 'LDAP_basedn_groups', registry.String('', """LDAP base dn when searching for groups""")) | |
conf.registerGlobalValue(Scout, 'LDAP_username', registry.String('', """Bind to LDAP using this username""")) | |
conf.registerGlobalValue(Scout, 'LDAP_password', registry.String('', """Bind to LDAP using this password""")) | |
conf.registerGlobalValue(Scout, 'LDAP_user_filter', registry.String('', """LDAP search filter to use when searching for users""")) | |
conf.registerGlobalValue(Scout, 'LDAP_grouplist_filter', registry.String('', """LDAP search filter to use when discoverying the groups""")) | |
conf.registerGlobalValue(Scout, 'LDAP_groupmember_filter', registry.String('', """LDAP search filter to use when listing the members of a group""")) | |
conf.registerGlobalValue(Scout, 'mailfrom', registry.String('', """email address that email from the bot comes from""")) | |
# vim:set shiftwidth=4 tabstop=4 expandtab textwidth=79: |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
### | |
# Copyright (c) 2013, Andy Bakun and Yelp! | |
# All rights reserved. | |
# | |
# Redistribution and use in source and binary forms, with or without | |
# modification, are permitted provided that the following conditions are met: | |
# | |
# * Redistributions of source code must retain the above copyright notice, | |
# this list of conditions, and the following disclaimer. | |
# * Redistributions in binary form must reproduce the above copyright notice, | |
# this list of conditions, and the following disclaimer in the | |
# documentation and/or other materials provided with the distribution. | |
# * Neither the name of the author of this software nor the name of | |
# contributors to this software may be used to endorse or promote products | |
# derived from this software without specific prior written consent. | |
# | |
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | |
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE | |
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | |
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | |
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | |
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | |
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | |
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | |
# POSSIBILITY OF SUCH DAMAGE. | |
### | |
import supybot.utils as utils | |
from supybot.commands import * | |
import supybot.plugins as plugins | |
import supybot.ircutils as ircutils | |
import supybot.callbacks as callbacks | |
import random | |
import re | |
import string | |
import ldap | |
import os | |
import sys | |
import smtplib | |
from email.mime.text import MIMEText | |
class Scout(callbacks.Plugin): | |
"""This plugin provides a few random number commands and some | |
commands for getting random samples. Use the "seed" command to seed | |
the plugin's random number generator if you like, though it is | |
unnecessary as it gets seeded upon loading of the plugin. The | |
"random" command is most likely what you're looking for, though | |
there are a number of other useful commands in this plugin. Use | |
'list random' to check them out. """ | |
threaded = False | |
def __init__(self, irc): | |
self.__parent = super(Scout, self) | |
self.__parent.__init__(irc) | |
self.rng = random.Random() | |
self.rng.seed() | |
def random(self, irc, msg, args): | |
"""takes no arguments | |
Returns the next random number from the random number generator. | |
""" | |
irc.reply(str(self.rng.random())) | |
random = wrap(random) | |
def ldap_open(self): | |
con = ldap.initialize(self.registryValue('LDAP_URL'), trace_level=0, trace_file=sys.stderr) | |
con.set_option(ldap.OPT_DEBUG_LEVEL, 255) | |
con.set_option(ldap.OPT_NETWORK_TIMEOUT, 3) | |
con.set_option(ldap.OPT_REFERRALS, 0) | |
con.set_option(ldap.OPT_PROTOCOL_VERSION, ldap.VERSION3) | |
con.start_tls_s() | |
con.simple_bind_s(self.registryValue('LDAP_username'), self.registryValue('LDAP_password')) | |
return con | |
def ldap_close(self, con): | |
con.unbind_s() | |
def find_user(self, who): | |
if not re.match(r'^(\w+)$', who): | |
return None | |
con = self.ldap_open() | |
try: | |
filterstr = self.registryValue('LDAP_user_filter') % (who,) | |
results = con.search_s(self.registryValue('LDAP_basedn_users'), ldap.SCOPE_SUBTREE, filterstr=filterstr, attrlist=['pager', 'mobile', 'cn']) | |
except: | |
results = () | |
finally: | |
self.ldap_close(con) | |
return results | |
def send_message(self, mto, subject, message): | |
mfrom = self.registryValue('mailfrom') | |
debugmto = self.registryValue('ForceMailTo') | |
if debugmto: | |
mto = debugmto | |
msg = MIMEText(message) | |
msg['Subject'] = subject | |
msg['From'] = mfrom | |
msg['To'] = mto | |
s = smtplib.SMTP('localhost') | |
s.sendmail(mfrom, [mto], msg.as_string()) | |
s.quit() | |
def page(self, irc, msg, args, username, message): | |
"""<username> [<message>] | |
Sends a message to <username> | |
""" | |
try: | |
if msg.args[0][0] != "#": | |
irc.reply("I refuse to summon someone to a /private/ conversation.") | |
return | |
except: | |
return | |
results = self.find_user(username) | |
if results is None: | |
irc.reply('is confused by the username "%s".' % username, action=True) | |
return | |
if results: | |
userattr = results[0][1] | |
if 'pager' in userattr: | |
if message: | |
subject = "%s in %s" % (msg.nick, msg.args[0]) | |
message = message.strip() | |
irc.reply("sends \"%s\" to %s's pager." % (message, username), action=True) | |
else: | |
subject = "%s requests your presence in %s" % (msg.nick, msg.args[0]) | |
message = "" | |
irc.reply("tries to summon %s at %s's request." % (username, msg.nick), action=True) | |
self.send_message(userattr['pager'][0], subject, message) | |
else: | |
irc.reply("%s (%s) has no pager listed in the directory." % (userattr['cn'][0], username)) | |
else: | |
irc.reply('could not find "%s"' % username, action=True) | |
page = wrap(page, ['something', optional('text')]) | |
def lookup(self, irc, msg, args, username): | |
"""<username> | |
Shows contact information for <username> | |
""" | |
results = self.find_user(username) | |
if results is None: | |
irc.reply('is confused by the username "%s".' % username, action=True) | |
return | |
if results: | |
userattr = results[0][1] | |
info = "%s (%s)" % (userattr['cn'][0], username) | |
if "mobile" in userattr: | |
info += " can be reached at %s." % userattr['mobile'][0] | |
elif "pager" in userattr: | |
info += " can be paged at %s (no phone number listed)." % userattr['pager'][0] | |
else: | |
info += " does not have contact information recorded in the directory." | |
irc.reply(info) | |
else: | |
irc.reply('can\'t find a user named "%s".' % username, action=True) | |
lookup = wrap(lookup, ['something']) | |
def find_groups(self): | |
con = self.ldap_open() | |
try: | |
filterstr = self.registryValue('LDAP_grouplist_filter') | |
results = con.search_s(self.registryValue('LDAP_basedn_groups'), ldap.SCOPE_SUBTREE, filterstr=filterstr, attrlist=['cn']) | |
finally: | |
self.ldap_close(con) | |
return results | |
def groups(self, irc, msg, args): | |
""" | |
List the groups | |
""" | |
results = self.find_groups() | |
groups = [x[1]['cn'][0].lower() for x in results] | |
groups = filter(lambda x: x, groups) | |
irc.reply('I found %d groups: %s' % (len(groups), utils.str.commaAndify(sorted(groups)))) | |
groups = wrap(groups, []) | |
def find_members(self, groupname): | |
if not re.match(r'^(\w+)$', groupname): | |
return None | |
con = self.ldap_open(); | |
try: | |
filterstr = self.registryValue('LDAP_groupmember_filter') % groupname | |
results = con.search_s(self.registryValue('LDAP_basedn_users'), ldap.SCOPE_SUBTREE, filterstr=filterstr, attrlist=['sAMAccountName']) | |
finally: | |
self.ldap_close(con) | |
return results | |
def members(self, irc, msg, args, groupname): | |
"""<groupname> | |
List the members of <groupname> | |
""" | |
results = self.find_members(groupname) | |
if results is None: | |
irc.reply('is confused by the groupname "%s".' % groupname, action=True) | |
return | |
if results: | |
members = [x[1]['sAMAccountName'][0].lower() for x in results] | |
members = filter(lambda x: x, members) | |
irc.reply('I found %d members of %s: %s' % (len(members), groupname, utils.str.commaAndify(sorted(members)))) | |
else: | |
irc.reply('Group %s appears to have no members or doesn\'t exist.' % groupname) | |
members = wrap(members, ['something']) | |
def help(self, irc, msg, args): | |
irc.reply("Known commands: groups; members <groupname>; lookup <username>; page <username> [<message>]") | |
help = wrap(help, []) | |
#def doPrivmsg(self, irc, msg): | |
# print repr(msg) | |
# (tochannel, textmsg) = msg.args | |
# return | |
#def inFilter(self, irc, msg): | |
# print repr(msg) | |
# return msg | |
#def doTopic(self, irc, msg): | |
# if len(msg.args) == 1: | |
# return # It's an empty TOPIC just to get the current topic. | |
# channel = msg.args[0] | |
# self.doLog(irc, channel, | |
# '*** %s changes topic to "%s"\n', msg.nick, msg.args[1]) | |
Class = Scout | |
# vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79: |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# These are just samples, supybot should write a list of these to | |
# the config file for you to edit. | |
# note that any SSL options that are necessary to connect to your | |
# LDAP/AD server will need to be specified in the environment before | |
# starting supybot: | |
# LDAPTLS_REQCERT=demand | |
# LDAPTLS_CACERT=/path/to/ca.cert | |
# since the python-ldap library uses the ldap library which reads | |
# these and this code doesn't handle setting them. | |
supybot.plugins.Scout: True | |
supybot.plugins.Scout.public: True | |
supybot.plugins.Scout.ForceMailTo: | |
supybot.plugins.Scout.LDAP_URL: ldap://your-ldap-server:389 | |
supybot.plugins.Scout.LDAP_basedn_users: ou=Users... | |
supybot.plugins.Scout.LDAP_basedn_groups: ou=Groups... | |
supybot.plugins.Scout.LDAP_username: bind-username | |
supybot.plugins.Scout.LDAP_password: bind-password | |
supybot.plugins.Scout.LDAP_user_filter: (&(objectclass=user)(sAMAccountName=%s)(memberOf=CN=...)) | |
supybot.plugins.Scout.LDAP_grouplist_filter: (&(objectclass=group)(|(cn=...)(cn=...))) | |
supybot.plugins.Scout.LDAP_groupmember_filter: (&(objectclass=user)(memberOf=CN=%s,OU=Groups...)) | |
supybot.plugins.Scout.mailfrom: some-name |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment