Support radius and kerberos passwords.

Kerberos using kadmin which needs an /admin-prinipal to work. Talk with openradius mysqldb directly. New db router.
This commit is contained in:
nikolark committed 2011-11-22 01:14:57 +01:00
1 parent 1c0b613c81
commit fdcb4f5d57
10 files changed
+203 -4

No files matched your search

+1
View File
@@ -43,6 +43,7 @@ class Router(object):
NOTE: if you have more than one LDAP database, you will need to
write your own router.
Yes.
"""
def __init__(self):
+8
View File
@@ -3,16 +3,24 @@ from django.core.validators import MinLengthValidator
from validators import PasswordValidator
from models import *
import utils
# not needed?
class LDAPSetPasswordForm(SetPasswordForm):
def save(self, commit=True):
# set kerberos password
set_krb5 = utils.set_kerberos_password(self.user.username, self.cleaned_data['new_password1'])
# set radius password
set_radius = utils.set_radius_password(self.user.username, self.cleaned_data['new_password1'])
# Lookup the Ldap user with the identical username (1-to-1).
self.user = LdapUser.objects.get(username=self.user.username)
self.user.set_password(self.cleaned_data['new_password1'])
if commit:
self.user.save()
return self.user
def clean_new_password1(self):
+113
View File
@@ -1,11 +1,16 @@
from ldapdb.models.fields import CharField, ImageField, IntegerField, ListField
from django.db import models
import ldapdb.models
import passwd
import datetime
class LdapUser(ldapdb.models.Model):
"""
Class for representing an LDAP user entry.
"""
connection_name = 'ldap'
# LDAP meta-data
base_dn = "ou=People,dc=neuf,dc=no" # CONFIG
object_classes = ['inetOrgPerson', 'posixAccount', 'shadowAccount']
@@ -68,3 +73,111 @@ class LdapGroup(ldapdb.models.Model):
def __unicode__(self):
return self.name
# This is an auto-generated Django model module.
# You'll have to do the following manually to clean this up:
# * Rearrange models' order
# * Make sure each model has one field with primary_key=True
# Feel free to rename the models, but don't rename db_table values or field names.
#
# Also note: You'll have to insert the output of 'django-admin.py sqlcustom [appname]'
# into your database.
class Radacct(models.Model):
connection_name = 'radius'
radacctid = models.BigIntegerField(primary_key=True)
acctsessionid = models.CharField(max_length=192)
acctuniqueid = models.CharField(max_length=96)
username = models.CharField(max_length=192)
groupname = models.CharField(max_length=192)
realm = models.CharField(max_length=192, blank=True)
nasipaddress = models.CharField(max_length=45)
nasportid = models.CharField(max_length=45, blank=True)
nasporttype = models.CharField(max_length=96, blank=True)
acctstarttime = models.DateTimeField(null=True, blank=True)
acctstoptime = models.DateTimeField(null=True, blank=True)
acctsessiontime = models.IntegerField(null=True, blank=True)
acctauthentic = models.CharField(max_length=96, blank=True)
connectinfo_start = models.CharField(max_length=150, blank=True)
connectinfo_stop = models.CharField(max_length=150, blank=True)
acctinputoctets = models.BigIntegerField(null=True, blank=True)
acctoutputoctets = models.BigIntegerField(null=True, blank=True)
calledstationid = models.CharField(max_length=150)
callingstationid = models.CharField(max_length=150)
acctterminatecause = models.CharField(max_length=96)
servicetype = models.CharField(max_length=96, blank=True)
framedprotocol = models.CharField(max_length=96, blank=True)
framedipaddress = models.CharField(max_length=45)
acctstartdelay = models.IntegerField(null=True, blank=True)
acctstopdelay = models.IntegerField(null=True, blank=True)
xascendsessionsvrkey = models.CharField(max_length=30, blank=True)
class Meta:
db_table = u'radacct'
class Radcheck(models.Model):
connection_name = 'radius'
username = models.CharField(max_length=192)
attribute = models.CharField(max_length=192)
op = models.CharField(max_length=6)
value = models.CharField(max_length=759)
class Meta:
db_table = u'radcheck'
class Radgroupcheck(models.Model):
connection_name = 'radius'
groupname = models.CharField(max_length=192)
attribute = models.CharField(max_length=192)
op = models.CharField(max_length=6)
value = models.CharField(max_length=759)
class Meta:
db_table = u'radgroupcheck'
class Radgroupreply(models.Model):
connection_name = 'radius'
groupname = models.CharField(max_length=192)
attribute = models.CharField(max_length=192)
op = models.CharField(max_length=6)
value = models.CharField(max_length=759)
class Meta:
db_table = u'radgroupreply'
class Radpostauth(models.Model):
connection_name = 'radius'
username = models.CharField(max_length=192)
pass_field = models.CharField(max_length=192, db_column='pass')
reply = models.CharField(max_length=96)
authdate = models.DateTimeField()
class Meta:
db_table = u'radpostauth'
class Radreply(models.Model):
connection_name = 'radius'
username = models.CharField(max_length=192)
attribute = models.CharField(max_length=192)
op = models.CharField(max_length=6)
value = models.CharField(max_length=759)
class Meta:
db_table = u'radreply'
class Radusergroup(models.Model):
connection_name = 'radius'
username = models.CharField(max_length=192)
groupname = models.CharField(max_length=192)
priority = models.IntegerField()
class Meta:
db_table = u'radusergroup'
+5
View File
@@ -23,3 +23,8 @@ def ldap_validate(raw_password, challenge_password):
hr = hashlib.sha1(raw_password)
hr.update(salt)
return digest == hr.digest()
def radius_create(raw_password):
# found in the wild.
nt_password = hashlib.new('md4', raw_password.encode('utf-16le')).hexdigest()
return nt_password
+31
View File
@@ -0,0 +1,31 @@
def is_radius_model(model):
return hasattr(model, 'connection_name')
class Router(object):
"""
A router to point database operations on models to the right db.
Using the attribute connection_name referring to the
DATABASES-label in settings.py.
Ref: https://docs.djangoproject.com/en/dev/topics/db/multi-db/
"""
def allow_syncdb(self, db, model):
"""Do not create tables on the default db for models on $./manage.py sync,
but this function maybe used know which db a model is on"""
if is_radius_model(model):
return db == model.connection_name
return db == 'default'
def db_for_read(self, model, **hints):
"Point all operations on models connection_name to db=connection_name"
if is_radius_model(model):
return model.connection_name
return None
def db_for_write(self, model, **hints):
"Point all operations on models connection_name to db=connection_name"
if is_radius_model(model):
return model.connection_name
return None
+27
View File
@@ -0,0 +1,27 @@
# coding: utf-8
from subprocess import Popen, PIPE
from django.core.exceptions import ObjectDoesNotExist
from models import Radcheck
import passwd
def set_kerberos_password(username, raw_password):
principal = u"{0}@NEUF.NO".format(username)
kadmin_query = u"-q change_password {0} -pw {1}".format(principal, raw_password)
p = Popen(['kadmin', kadmin_query], stdout=PIPE)
output, error = p.communicate()
if error:
# Can not init with KDC?
# KADM5_AUTH_MODIFY (requires the modify privilege)
return False
else:
return True
def set_radius_password(username, raw_password):
try:
radius_user = Radcheck.objects.get(username=username)
except ObjectDoesNotExist:
return False
radius_user.value = passwd.radius_create(raw_password)
radius_user.save()
return True
+3 -1
View File
@@ -37,12 +37,14 @@ def profile(request):
try:
ldap_user = LdapUser.objects.get(username=request.user)
except:
pass
return render_to_response('private/profile.html', locals(), context_instance=RequestContext(request))
groups = LdapGroup.objects.filter(usernames__contains=request.user)
private_group = LdapGroup.objects.filter(gid=ldap_user.group)[0]
return render_to_response('private/profile.html', locals(), context_instance=RequestContext(request))
def radius_info():
return {'active': True}
+9 -1
View File
@@ -28,9 +28,17 @@ DATABASES = {
'NAME': 'ldap://localhost/',
'USER': 'uid=admin,ou=People,dc=neuf,dc=no',
'PASSWORD': '',
},
'radius': {
'ENGINE': 'django.db.backends.mysql', # Add 'postgresql_psycopg2', 'postgresql', 'mysql', 'sqlite3' or 'oracle'.
'NAME': 'radius', # Or path to database file if using sqlite3.
'USER': 'radius', # Not used with sqlite3.
'PASSWORD': '', # Not used with sqlite3.
'HOST': 'localhost', # Set to empty string for localhost. Not used with sqlite3.
'PORT': '', # Set to empty string for default. Not used with sqlite3.
}
}
DATABASE_ROUTERS = ['ldapdb.router.Router']
DATABASE_ROUTERS = ['main.router.Router']
# Local time zone for this installation. Choices can be found here:
# http://en.wikipedia.org/wiki/List_of_tz_zones_by_name
+4
View File
@@ -21,10 +21,14 @@
</ul>
<h1>Dine grupper</h1>
<ul>
{% if groups %}
<li>{{ private_group }}</li>
{% for group in groups %}
<li>{{ group }}</li>
{% endfor %}
{% else %}
<li>Ingen grupper.</li>
{% endif %}
</ul>
<h1>Dine tjenester</h1>
<ul>
+2 -2
View File
@@ -1,4 +1,4 @@
#from django.contrib.staticfiles.urls import staticfiles_urlpatterns # for dev
from django.contrib.staticfiles.urls import staticfiles_urlpatterns # for dev
from django.conf.urls.defaults import *
from django.contrib import admin
from main.forms import LDAPPasswordChangeForm, LDAPSetPasswordForm, LDAPPasswordResetForm
@@ -27,4 +27,4 @@ urlpatterns = patterns('',
(r'^$', 'main.views.index'),
)
#urlpatterns += staticfiles_urlpatterns()
urlpatterns += staticfiles_urlpatterns()