Archived
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:
1 parent
1c0b613c81
commit
fdcb4f5d57
10 files changed
+203
-4
No files matched your search
@@ -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):
|
||||
|
||||
@@ -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
@@ -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'
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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()
|
||||
Reference in new issue
Block a user