Skip to content

Instantly share code, notes, and snippets.

@giftig
Last active August 29, 2015 14:06
Show Gist options
  • Save giftig/41c69b1bdc5e3bf93257 to your computer and use it in GitHub Desktop.
Save giftig/41c69b1bdc5e3bf93257 to your computer and use it in GitHub Desktop.
"""Author: Rob Moore <giftiger.wunsch@xantoria.com>"""
import logging
import signal
import struct
import sys
from django.contrib.auth.models import check_password
from django.contrib.auth.models import User
from django.core.management.base import BaseCommand
logger = logging.getLogger(__name__)
class Command(BaseCommand):
help = 'Interface between ejabberd and django; an auth service for XMPP'
stopping = False
@staticmethod
def write_response(success=False):
"""Creates and sends a response back to the ejabberd server."""
result = 1 if success else 0
sys.stdout.write(struct.pack('>hh', 2, result))
sys.stdout.flush()
@staticmethod
def _handle_isuser(username, server):
"""
Handles the `isuser` ejabberd command, indicating whether or not
the requested user exists in the system.
"""
return Command.write_response(
User.objects.filter(username=username).exists()
)
@staticmethod
def _handle_auth(username, server, password):
"""Handles authentication of the user"""
user = User.objects.filter(username=username)
if not user:
return Command.write_response(False)
user = user[0]
result = check_password(password, user.password)
return Command.write_response(result)
def handle_request(self):
"""Read an ejabberd request and respond to it"""
try:
data_size = struct.unpack('>h', sys.stdin.read(2))[0]
data = sys.stdin.read(data_size).split(':')
command = input.pop(0)
# If anything goes wrong, tell ejabberd it's being dumb; don't crash
except:
logger.exception('ejabberd gave us a bad request!')
return Command.write_response(False)
handler = getattr(Command, '_handle_%s' % command, None)
# Fail silently; we don't support all commands, but it doesn't mean
# ejabberd isn't going to attempt them, possibly frequently
if handler is None:
return Command.write_response(False)
return handler(*data)
def handle(self, **options):
"""
Per ejabberd external auth API specification, keep listening for
auth or 'isuser' requests and responding as appropriate using utility
methods.
"""
# If we get a SIGINT, stop after we've dealt with the current command
signal.signal(signal.SIGINT, self.stop_gracefully)
while not Command.stopping:
Command.handle_request()
@staticmethod
def stop_gracefully(*args):
Command.stopping = True
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment