Permalink
Browse files

Merge pull request #54 from liveaverage/patch-1

Update src/baruwa/auth/ad.py
  • Loading branch information...
2 parents b9573e8 + 043e953 commit 2894ea83be7764538708d72aa43a1444cc489d8e @akissa akissa committed Dec 21, 2012
Showing with 218 additions and 17 deletions.
  1. +53 −17 src/baruwa/auth/ad.py
  2. +148 −0 src/baruwa/config/migrations/0004_auto__add_mailadauthhost.py
  3. +17 −0 src/baruwa/config/models.py
View
@@ -21,6 +21,7 @@
#
import ldap
+import struct
import logging
from django.contrib.auth.models import User
@@ -29,10 +30,10 @@
from baruwa.utils.misc import get_exc_str
from baruwa.config.models import MailAuthHost
+from baruwa.config.models import MailADAuthHost
from baruwa.accounts.models import UserProfile
from baruwa.accounts.models import UserAddresses
-
logger = logging.getLogger()
fhandle = logging.FileHandler(settings.AD_LOG_FILE)
fhandle.setLevel(logging.DEBUG)
@@ -42,28 +43,35 @@
class ADUser(object):
ldap_connection = None
- AD_SEARCH_FIELDS = settings.AD_SEARCH_FIELDS
+ ad_search_fields = settings.AD_SEARCH_FIELDS
+ ad_ldap_scheme = settings.AD_LDAP_SCHEME
def get_ldap_url(self):
"""return ldap url"""
- return '%s%s:%s' % (settings.AD_LDAP_SCHEME,
+ return '%s%s:%s' % (self.ad_ldap_scheme,
self.ad_host,
self.ad_port)
- def __init__(self, username, host=None, port=None):
+ def __init__(self, username, host=None, port=None, ad_search_dn=None, ad_admin_group=None, ad_user_group=None, ad_auth_domain=None):
"""initialization"""
+
self.username = username
self.uname = username
self.ad_host = host if host else settings.AD_HOST_NAME
self.ad_port = port if port else settings.AD_LDAP_PORT
+ # Added for multi-domain authentication with AD parameters stored in auth_domain
+ self.ad_search_dn = ad_search_dn if ad_search_dn else settings.AD_SEARCH_DN
+ self.ad_admin_group = ad_admin_group if ad_admin_group else settings.AD_ADMIN_GROUP
+ self.ad_user_group = ad_user_group if ad_user_group else settings.AD_USER_GROUP
+
try:
- self.domain = self.username.split('@')[1]
+ self.domain = ad_auth_domain if ad_auth_domain else settings.AD_AUTH_DOMAIN
self.uname = self.username.split('@')[0]
self.username = self.username.split('@')[0]
self.user_bind_name = "%s@%s" % (self.username, self.domain)
except IndexError:
- self.domain = settings.AD_AUTH_DOMAIN
+ self.domain = ad_auth_domain if ad_auth_domain else settings.AD_AUTH_DOMAIN
self.user_bind_name = "%s@%s" % (self.username, self.domain)
self.is_bound = False
@@ -115,6 +123,14 @@ def disconnect(self):
self.ldap_connection.unbind_s()
self.is_bound = False
+ def sid2str(self, sid):
+ srl = ord(sid[0])
+ number_sub_id = ord(sid[1])
+ iav = struct.unpack('!Q','\x00\x00'+sid[2:8])[0]
+ sub_ids = [struct.unpack('<I',sid[8+4*i:12+4*i])[0] for i in range(number_sub_id)]
+
+ return 'S-%d-%d-%s' % (srl, iav, '-'.join([str(s) for s in sub_ids]))
+
def check_group(self, obj, group):
"""Check if user is in AD group"""
found = False
@@ -124,17 +140,28 @@ def check_group(self, obj, group):
res2 = self.ldap_connection.search_ext_s(obj,
ldap.SCOPE_BASE,
"(objectClass=*)",
- self.AD_SEARCH_FIELDS)
+ self.ad_search_fields)
+
if not res2:
return False
assert len(res2) >= 1, "Result should contain at least one element: %s\n" % res2
result = res2[0][1]
+ if result.has_key('primaryGroupID'):
+ pri_grp_rid = result['primaryGroupID'][0]
+ domain_sid = self.ldap_connection.search_s(self.ad_search_dn, ldap.SCOPE_BASE)[0][1]['objectSid'][0]
+ domain_sid_s = self.sid2str(domain_sid)
+ obj_sid = domain_sid_s + '-' + pri_grp_rid
+ pri_grp_cn = self.ldap_connection.search_s(self.ad_search_dn, ldap.SCOPE_SUBTREE, "objectSid=%s" % obj_sid, ['cn'])
+ if self.check_group (pri_grp_cn[0][0], group):
+ return True
if result.has_key('sAMAccountName'):
if result['sAMAccountName'][0] == group:
return True
- for group2 in result['memberOf']:
- if self.check_group (group2, group):
- return True
+ if result.has_key('memberOf'):
+ for group2 in result['memberOf']:
+ if self.check_group (group2, group):
+ return True
+
except Exception, exp:
logger.debug("AD auth backend error by fetching"
" ldap data: %s (%s)\n" % (str(exp), get_exc_str()))
@@ -144,14 +171,15 @@ def check_group(self, obj, group):
def get_data(self):
"""Get the user data from AD"""
try:
- res = self.ldap_connection.search_ext_s(settings.AD_SEARCH_DN,
+ res = self.ldap_connection.search_ext_s(self.ad_search_dn,
ldap.SCOPE_SUBTREE,
"sAMAccountName=%s" % self.uname,
- self.AD_SEARCH_FIELDS)
+ self.ad_search_fields)
+
if not res:
logger.error("b) AD auth ldap backend error by "
- "searching %s. No result.\n" % settings.AD_SEARCH_DN)
+ "searching %s. No result.\n" % self.ad_search_dn)
return False
assert len(res) >= 1, "c) Result should contain at least one element: %s\n" % res
result = res[0][1]
@@ -181,9 +209,10 @@ def get_data(self):
logger.error("Adding Address: %s\n", mail1)
basedn = res[0][0]
- if self.check_group(basedn, settings.AD_ADMIN_GROUP):
+
+ if self.check_group(basedn, self.ad_admin_group):
self.is_superuser = True
- elif self.check_group(basedn, settings.AD_USER_GROUP):
+ elif self.check_group(basedn, self.ad_user_group):
self.is_superuser = False
else:
logger.error("User %s not in group", self.username)
@@ -235,15 +264,22 @@ def authenticate(self, username=None, password=None):
logger.warning("No AD servers found for %s\n" % domain)
return None
+ adset = None
+
for host in hosts:
# process all hosts
- aduser = ADUser(username, host.address, host.port)
+
+ # Query each host for configured AD settings:
+ adset = MailADAuthHost.objects.get(ad_host=host)
+
+ aduser = ADUser(username, host.address, host.port, adset.ad_search_dn, adset.ad_admin_group, adset.ad_user_group, adset.ad_auth_domain)
if not aduser.connect(password):
logger.warning("AD bind failed for %s\n" % username)
continue
user = None
+
try:
user = User.objects.get(username=username)
except User.DoesNotExist:
@@ -255,7 +291,7 @@ def authenticate(self, username=None, password=None):
if not aduser.get_data():
logger.warning("AD auth backend failed when reading data for"
- " %s. No Group information available.\n" % username)
+ " %s. No Group information available.\nAD_Auth_Domain:%s\tAD_Admin_Group:%s\tAD_User_Group:%s" % (username,adset.ad_auth_domain,adset.ad_admin_group,adset.ad_user_group))
user = None
continue
else:
@@ -0,0 +1,148 @@
+# encoding: utf-8
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+class Migration(SchemaMigration):
+
+ def forwards(self, orm):
+
+ # Adding model 'MailADAuthHost'
+ db.create_table('auth_domain', (
+ ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+ ('ad_host', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['config.MailAuthHost'])),
+ ('ad_search_dn', self.gf('django.db.models.fields.CharField')(max_length=255)),
+ ('ad_admin_group', self.gf('django.db.models.fields.CharField')(max_length=255)),
+ ('ad_user_group', self.gf('django.db.models.fields.CharField')(max_length=255)),
+ ('ad_search_fields', self.gf('django.db.models.fields.CharField')(max_length=255)),
+ ('ad_ldap_scheme', self.gf('django.db.models.fields.CharField')(max_length=255)),
+ ('ad_auth_domain', self.gf('django.db.models.fields.CharField')(max_length=255)),
+ ('ad_log_file', self.gf('django.db.models.fields.CharField')(max_length=255)),
+ ))
+ db.send_create_signal('config', ['MailADAuthHost'])
+
+
+ def backwards(self, orm):
+
+ # Deleting model 'MailADAuthHost'
+ db.delete_table('auth_domain')
+
+
+ models = {
+ 'accounts.useraddresses': {
+ 'Meta': {'object_name': 'UserAddresses', 'db_table': "'user_addresses'"},
+ 'address': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
+ 'address_type': ('django.db.models.fields.IntegerField', [], {'default': '2'}),
+ 'enabled': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'load_balance': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
+ },
+ 'auth.group': {
+ 'Meta': {'object_name': 'Group'},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
+ 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
+ },
+ 'auth.permission': {
+ 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
+ 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
+ },
+ 'auth.user': {
+ 'Meta': {'object_name': 'User'},
+ 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
+ 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+ 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+ 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+ 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+ 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
+ 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'})
+ },
+ 'config.configsection': {
+ 'Meta': {'object_name': 'ConfigSection', 'db_table': "'scanner_config_sections'"},
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+ },
+ 'config.domainsignature': {
+ 'Meta': {'unique_together': "(('useraddress', 'signature_type'),)", 'object_name': 'DomainSignature', 'db_table': "'domain_signatures'"},
+ 'enabled': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'image': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'ds_si'", 'null': 'True', 'to': "orm['fixups.SignatureImg']"}),
+ 'signature_content': ('django.db.models.fields.TextField', [], {}),
+ 'signature_type': ('django.db.models.fields.IntegerField', [], {}),
+ 'useraddress': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'ds_ua'", 'to': "orm['accounts.UserAddresses']"})
+ },
+ 'config.mailadauthhost': {
+ 'Meta': {'object_name': 'MailADAuthHost', 'db_table': "'auth_domain'"},
+ 'ad_admin_group': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'ad_auth_domain': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'ad_host': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['config.MailAuthHost']"}),
+ 'ad_ldap_scheme': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'ad_log_file': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'ad_search_dn': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'ad_search_fields': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'ad_user_group': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
+ },
+ 'config.mailauthhost': {
+ 'Meta': {'object_name': 'MailAuthHost', 'db_table': "'auth_hosts'"},
+ 'address': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'enabled': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'port': ('django.db.models.fields.IntegerField', [], {'blank': 'True'}),
+ 'protocol': ('django.db.models.fields.IntegerField', [], {'default': '1'}),
+ 'split_address': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+ 'useraddress': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'mah_ua'", 'to': "orm['accounts.UserAddresses']"})
+ },
+ 'config.mailhost': {
+ 'Meta': {'object_name': 'MailHost', 'db_table': "'mail_hosts'"},
+ 'address': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'enabled': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'port': ('django.db.models.fields.IntegerField', [], {'default': '25'}),
+ 'useraddress': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'mh_ua'", 'to': "orm['accounts.UserAddresses']"})
+ },
+ 'config.scannerconfig': {
+ 'Meta': {'unique_together': "(('internal', 'host'),)", 'object_name': 'ScannerConfig', 'db_table': "'scanner_config'"},
+ 'display': ('django.db.models.fields.TextField', [], {}),
+ 'external': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'help': ('django.db.models.fields.TextField', [], {}),
+ 'host': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['config.ScannerHost']"}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'internal': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'section': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['config.ConfigSection']"}),
+ 'value': ('django.db.models.fields.TextField', [], {})
+ },
+ 'config.scannerhost': {
+ 'Meta': {'object_name': 'ScannerHost', 'db_table': "'scanners'"},
+ 'address': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
+ },
+ 'contenttypes.contenttype': {
+ 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
+ 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+ },
+ 'fixups.signatureimg': {
+ 'Meta': {'unique_together': "(('name', 'owner'),)", 'object_name': 'SignatureImg', 'db_table': "'signature_imgs'"},
+ 'content_type': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+ 'image': ('django.db.models.fields.TextField', [], {}),
+ 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+ 'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
+ }
+ }
+
+ complete_apps = ['config']
@@ -65,6 +65,23 @@ class MailAuthHost(models.Model):
class Meta:
db_table = 'auth_hosts'
+class MailADAuthHost(models.Model):
+ "Holds AD authentication hosts"
+
+ ad_host = models.ForeignKey(MailAuthHost)
+ ad_search_dn = models.CharField(max_length=255)
+ ad_admin_group = models.CharField(max_length=255)
+ ad_user_group = models.CharField(max_length=255)
+ ad_search_fields = models.CharField(max_length=255)
+ ad_ldap_scheme = models.CharField(max_length=255)
+ ad_auth_domain = models.CharField(max_length=255)
+ ad_log_file = models.CharField(max_length=255)
+
+ class Meta:
+ db_table = 'auth_domain'
+
+ def __unicode__(self):
+ return self.ad_search_dn
class ScannerHost(models.Model):
"Holds scanning nodes"

0 comments on commit 2894ea8

Please sign in to comment.