Major update on the way to less users in LDAP
- Adds new app inside with models and moves related logic. - Working InsideAuthBackend with group sync - use passlib (<3) for MySQL hashes - Renames main app to neuf_userinfo - introduced local_settings.py
No files matched your search
@@ -1,7 +1,7 @@
|
||||
*.swp
|
||||
*.pyc
|
||||
settings.py
|
||||
db.sqlite3
|
||||
static/style/.sass-cache/
|
||||
local_settings.py
|
||||
venv
|
||||
.idea
|
||||
@@ -12,9 +12,10 @@
|
||||
|
||||
## LDAP
|
||||
sudo docker run -e LDAP_DOMAIN=neuf.no -e LDAP_ORGANISATION="Neuf" -e LDAP_ROOTPASS="toor" -p 389:389 -d osixia/openldap
|
||||
ldapadd -D "cn=admin,dc=neuf,dc=no" -w "toor" -f testdata.ldif
|
||||
ldapadd -D "cn=admin,dc=neuf,dc=no" -w "toor" -f test/testdata.ldif
|
||||
# Verify import
|
||||
ldapsearch -x -b dc=neuf,dc=no
|
||||
# Login with test@example.com:test
|
||||
|
||||
# External dependencies:
|
||||
- LDAP: Read the tutorial on [Howto setup a LDAP server with Docker](https://edb.neuf.no/wiki/index.php?title=Howto_setup_a_LDAP_server_with_Docker)
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
from django.contrib import admin
|
||||
|
||||
# Register your models here.
|
||||
@@ -0,0 +1,78 @@
|
||||
from django.conf import settings
|
||||
from django.contrib.auth.models import User, Group
|
||||
from django.db.models import Q
|
||||
import logging
|
||||
|
||||
from inside import utils
|
||||
from inside.models import InsideUser, InsideGroup, AuthLog
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class InsideBackend(object):
|
||||
"""
|
||||
Authenticate against the inside database
|
||||
"""
|
||||
|
||||
def authenticate(self, username=None, password=None):
|
||||
password_hash = utils.mysql_create(password)
|
||||
inside_user = InsideUser.objects.filter(
|
||||
Q(password=password_hash) & (Q(ldap_username=username) | Q(email=username))
|
||||
)
|
||||
# AuthLog.objects.create()
|
||||
|
||||
if not len(inside_user) == 1:
|
||||
return None
|
||||
|
||||
inside_user = inside_user[0]
|
||||
|
||||
try:
|
||||
user = User.objects.get(username=inside_user.ldap_username)
|
||||
except User.DoesNotExist:
|
||||
# Create a new user.
|
||||
user = User(username=inside_user.ldap_username, password=password)
|
||||
|
||||
# Sync detail and groups from Inside to local Django install (for display and permissions)
|
||||
user = self._update_user_details(user, inside_user)
|
||||
user.save()
|
||||
|
||||
user = self._update_groups(user, inside_user)
|
||||
|
||||
return user
|
||||
|
||||
def get_user(self, user_id):
|
||||
try:
|
||||
return User.objects.get(pk=user_id)
|
||||
except User.DoesNotExist:
|
||||
return None
|
||||
|
||||
def _update_user_details(self, user, inside_user):
|
||||
sync_attrs = {
|
||||
'firstname': 'first_name',
|
||||
'lastname': 'last_name',
|
||||
'email': 'email'
|
||||
}
|
||||
for inside_attr, local_attr in sync_attrs.iteritems():
|
||||
setattr(user, local_attr, getattr(inside_user, inside_attr))
|
||||
|
||||
return user
|
||||
|
||||
def _update_groups(self, user, inside_user):
|
||||
existing_groups = list(user.groups.all())
|
||||
inside_groups = InsideGroup.objects.filter(user_rels__user=inside_user).exclude(posix_group='')
|
||||
inside_groups = inside_groups.values_list('posix_group', flat=True)
|
||||
|
||||
# Add new relationships
|
||||
for group in inside_groups:
|
||||
g, created = Group.objects.get_or_create(name=group)
|
||||
user.groups.add(g)
|
||||
|
||||
if not settings.INSIDE_GROUPS_SYNC_DELETE:
|
||||
return user
|
||||
|
||||
# Remove old relationships
|
||||
for g in existing_groups:
|
||||
if g.name not in inside_groups:
|
||||
user.groups.remove(g)
|
||||
|
||||
return user
|
||||
@@ -0,0 +1,471 @@
|
||||
# coding: utf-8
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models
|
||||
|
||||
|
||||
class Action(models.Model):
|
||||
connection_name = 'inside'
|
||||
|
||||
name = models.CharField(unique=True, max_length=50)
|
||||
text = models.CharField(max_length=255)
|
||||
|
||||
class Meta:
|
||||
managed = False
|
||||
db_table = 'din_action'
|
||||
|
||||
|
||||
class ActionGroupRelationship(models.Model):
|
||||
connection_name = 'inside'
|
||||
|
||||
action = models.ForeignKey(Action)
|
||||
group = models.ForeignKey('inside.InsideGroup')
|
||||
|
||||
class Meta:
|
||||
managed = False
|
||||
db_table = 'din_actiongrouprelationship'
|
||||
|
||||
|
||||
class BugReport(models.Model):
|
||||
connection_name = 'inside'
|
||||
|
||||
date = models.DateTimeField()
|
||||
type = models.CharField(max_length=45)
|
||||
filename = models.CharField(max_length=45)
|
||||
query = models.TextField()
|
||||
referer = models.TextField()
|
||||
useragent = models.CharField(max_length=255)
|
||||
user_id = models.IntegerField(blank=True, null=True)
|
||||
comment = models.TextField()
|
||||
title = models.CharField(max_length=50)
|
||||
active = models.IntegerField()
|
||||
status_comment = models.TextField()
|
||||
|
||||
class Meta:
|
||||
managed = False
|
||||
db_table = 'din_bugreport'
|
||||
|
||||
|
||||
class Division(models.Model):
|
||||
connection_name = 'inside'
|
||||
|
||||
name = models.CharField(unique=True, max_length=50)
|
||||
nicename = models.CharField(max_length=20)
|
||||
text = models.TextField()
|
||||
phone = models.CharField(max_length=8)
|
||||
email = models.CharField(max_length=120)
|
||||
office = models.CharField(max_length=3)
|
||||
user_id_contact = models.ForeignKey('inside.InsideUser', db_column='user_id_contact')
|
||||
url = models.CharField(max_length=120)
|
||||
divisioncategory = models.ForeignKey('inside.DivisionCategory')
|
||||
picture = models.CharField(max_length=7)
|
||||
hidden = models.IntegerField()
|
||||
updated = models.DateTimeField(blank=True, null=True)
|
||||
|
||||
class Meta:
|
||||
managed = False
|
||||
db_table = 'din_division'
|
||||
|
||||
|
||||
class DivisionCategory(models.Model):
|
||||
connection_name = 'inside'
|
||||
|
||||
title = models.CharField(unique=True, max_length=50)
|
||||
text = models.TextField()
|
||||
|
||||
class Meta:
|
||||
managed = False
|
||||
db_table = 'din_divisioncategory'
|
||||
|
||||
|
||||
class Document(models.Model):
|
||||
connection_name = 'inside'
|
||||
|
||||
type = models.CharField(max_length=60)
|
||||
name = models.CharField(max_length=120)
|
||||
size = models.BigIntegerField()
|
||||
date = models.DateTimeField()
|
||||
documentcategory = models.ForeignKey('inside.DocumentCategory')
|
||||
tags = models.CharField(max_length=255)
|
||||
|
||||
class Meta:
|
||||
managed = False
|
||||
db_table = 'din_document'
|
||||
|
||||
|
||||
class DocumentCategory(models.Model):
|
||||
connection_name = 'inside'
|
||||
|
||||
title = models.CharField(unique=True, max_length=50)
|
||||
text = models.TextField()
|
||||
|
||||
class Meta:
|
||||
managed = False
|
||||
db_table = 'din_documentcategory'
|
||||
|
||||
|
||||
class DocumentData(models.Model):
|
||||
connection_name = 'inside'
|
||||
|
||||
data = models.TextField()
|
||||
document = models.ForeignKey(Document)
|
||||
|
||||
class Meta:
|
||||
managed = False
|
||||
db_table = 'din_documentdata'
|
||||
|
||||
|
||||
class InsideGroup(models.Model):
|
||||
connection_name = 'inside'
|
||||
|
||||
name = models.CharField(unique=True, max_length=50)
|
||||
text = models.TextField()
|
||||
division = models.ForeignKey(Division, blank=True, null=True)
|
||||
admin = models.CharField(max_length=1)
|
||||
mailinglist = models.CharField(max_length=50)
|
||||
posix_group = models.CharField(max_length=128)
|
||||
|
||||
class Meta:
|
||||
managed = False
|
||||
db_table = 'din_group'
|
||||
|
||||
|
||||
class MemberCard(models.Model):
|
||||
connection_name = 'inside'
|
||||
|
||||
userid = models.IntegerField(db_column='userId', blank=True, null=True) # Field name made lowercase.
|
||||
ordered = models.DateTimeField(blank=True, null=True)
|
||||
produced = models.DateTimeField(blank=True, null=True)
|
||||
delivered = models.DateTimeField(blank=True, null=True)
|
||||
active = models.IntegerField()
|
||||
|
||||
class Meta:
|
||||
managed = False
|
||||
db_table = 'din_membercard'
|
||||
|
||||
|
||||
class Order(models.Model):
|
||||
connection_name = 'inside'
|
||||
|
||||
user = models.ForeignKey('inside.InsideUser')
|
||||
timestamp = models.DateTimeField()
|
||||
order_status = models.ForeignKey('inside.OrderStatus')
|
||||
order_deliverystatus_id = models.IntegerField()
|
||||
comment = models.TextField()
|
||||
|
||||
class Meta:
|
||||
managed = False
|
||||
db_table = 'din_order'
|
||||
|
||||
|
||||
class OrderDeliveryStatus(models.Model):
|
||||
connection_name = 'inside'
|
||||
|
||||
order_deliverystatus_id = models.IntegerField(primary_key=True)
|
||||
value = models.CharField(max_length=45)
|
||||
|
||||
class Meta:
|
||||
managed = False
|
||||
db_table = 'din_order_deliverystatus'
|
||||
|
||||
|
||||
class OrderItem(models.Model):
|
||||
connection_name = 'inside'
|
||||
|
||||
product = models.ForeignKey('inside.Product')
|
||||
quantity = models.IntegerField()
|
||||
order = models.ForeignKey(Order)
|
||||
discount = models.IntegerField()
|
||||
comment = models.TextField()
|
||||
|
||||
class Meta:
|
||||
managed = False
|
||||
db_table = 'din_order_item'
|
||||
|
||||
|
||||
class OrderStatus(models.Model):
|
||||
connection_name = 'inside'
|
||||
|
||||
value = models.CharField(max_length=45)
|
||||
|
||||
class Meta:
|
||||
managed = False
|
||||
db_table = 'din_order_status'
|
||||
|
||||
|
||||
class Position(models.Model):
|
||||
connection_name = 'inside'
|
||||
|
||||
name = models.CharField(max_length=50)
|
||||
division = models.ForeignKey(Division)
|
||||
text = models.TextField()
|
||||
|
||||
class Meta:
|
||||
managed = False
|
||||
db_table = 'din_position'
|
||||
|
||||
|
||||
class InsidePostalCode(models.Model):
|
||||
connection_name = 'inside'
|
||||
|
||||
postnummer = models.CharField(primary_key=True, max_length=4)
|
||||
poststed = models.CharField(max_length=32)
|
||||
kommunenr = models.CharField(max_length=4)
|
||||
kommunenavn = models.CharField(max_length=30)
|
||||
kategori = models.CharField(max_length=1)
|
||||
|
||||
class Meta:
|
||||
managed = False
|
||||
db_table = 'din_postnummer'
|
||||
|
||||
|
||||
class Product(models.Model):
|
||||
connection_name = 'inside'
|
||||
|
||||
title = models.CharField(max_length=255)
|
||||
description = models.TextField()
|
||||
price = models.IntegerField()
|
||||
allow_comment = models.IntegerField()
|
||||
display_in_shop = models.IntegerField()
|
||||
picture = models.CharField(max_length=10)
|
||||
|
||||
class Meta:
|
||||
managed = False
|
||||
db_table = 'din_product'
|
||||
|
||||
|
||||
class InsideSettings(models.Model):
|
||||
connection_name = 'inside'
|
||||
|
||||
name = models.CharField(primary_key=True, max_length=45)
|
||||
value = models.CharField(max_length=45)
|
||||
|
||||
class Meta:
|
||||
managed = False
|
||||
db_table = 'din_settings'
|
||||
|
||||
|
||||
class SmsReceived(models.Model):
|
||||
connection_name = 'inside'
|
||||
|
||||
smsid = models.IntegerField(primary_key=True)
|
||||
userid = models.IntegerField(blank=True, null=True)
|
||||
gsm = models.CharField(max_length=15)
|
||||
codeword = models.CharField(max_length=20)
|
||||
message = models.CharField(max_length=160)
|
||||
operator = models.CharField(max_length=20)
|
||||
shortno = models.CharField(max_length=15)
|
||||
action = models.CharField(max_length=30)
|
||||
ip = models.CharField(db_column='IP', max_length=15) # Field name made lowercase.
|
||||
date = models.DateTimeField()
|
||||
simulation = models.IntegerField()
|
||||
|
||||
class Meta:
|
||||
managed = False
|
||||
db_table = 'din_sms_received'
|
||||
|
||||
|
||||
class SmsSent(models.Model):
|
||||
connection_name = 'inside'
|
||||
|
||||
smsid = models.IntegerField(primary_key=True)
|
||||
response_to = models.IntegerField()
|
||||
msgid = models.IntegerField()
|
||||
sender = models.CharField(max_length=15)
|
||||
receiver = models.CharField(max_length=15)
|
||||
countrycode = models.IntegerField()
|
||||
message = models.CharField(max_length=160)
|
||||
operator = models.CharField(max_length=20, blank=True)
|
||||
codeword = models.CharField(max_length=20, blank=True)
|
||||
billing_price = models.IntegerField()
|
||||
use_dlr = models.IntegerField()
|
||||
date = models.DateTimeField()
|
||||
simulation = models.IntegerField()
|
||||
activation_code = models.CharField(max_length=10, blank=True)
|
||||
|
||||
class Meta:
|
||||
managed = False
|
||||
db_table = 'din_sms_sent'
|
||||
|
||||
|
||||
class Transaction(models.Model):
|
||||
connection_name = 'inside'
|
||||
|
||||
id_string = models.CharField(max_length=25)
|
||||
user = models.ForeignKey('inside.InsideUser')
|
||||
order = models.ForeignKey(Order)
|
||||
timestamp = models.DateTimeField()
|
||||
status = models.CharField(max_length=8, blank=True)
|
||||
amount = models.IntegerField()
|
||||
|
||||
class Meta:
|
||||
managed = False
|
||||
db_table = 'din_transaction'
|
||||
|
||||
|
||||
class TransactionCheck(models.Model):
|
||||
connection_name = 'inside'
|
||||
|
||||
order_id = models.IntegerField(primary_key=True)
|
||||
inserted = models.DateTimeField()
|
||||
order_ref = models.CharField(max_length=255)
|
||||
order_ref2 = models.CharField(max_length=255, blank=True)
|
||||
|
||||
class Meta:
|
||||
managed = False
|
||||
db_table = 'din_transaction_check'
|
||||
|
||||
|
||||
class UsedCardNo(models.Model):
|
||||
connection_name = 'inside'
|
||||
|
||||
date = models.DateTimeField()
|
||||
|
||||
class Meta:
|
||||
managed = False
|
||||
db_table = 'din_usedcardno'
|
||||
|
||||
|
||||
class InsideUser(models.Model):
|
||||
connection_name = 'inside'
|
||||
|
||||
cardno = models.IntegerField(unique=True, blank=True, null=True)
|
||||
username = models.CharField(unique=True, max_length=12)
|
||||
password = models.CharField(max_length=50)
|
||||
firstname = models.CharField(max_length=120)
|
||||
lastname = models.CharField(max_length=120)
|
||||
addresstype = models.CharField(max_length=3)
|
||||
valid_address = models.IntegerField()
|
||||
email = models.CharField(unique=True, max_length=120)
|
||||
birthdate = models.DateField()
|
||||
placeofstudy = models.IntegerField(db_column='placeOfStudy') # Field name made lowercase.
|
||||
passwordreset = models.CharField(db_column='passwordReset', max_length=1) # Field name made lowercase.
|
||||
expires = models.DateField(blank=True, null=True)
|
||||
division_id_request = models.ForeignKey(Division, db_column='division_id_request', blank=True, null=True)
|
||||
cardproduced = models.IntegerField(db_column='cardProduced') # Field name made lowercase.
|
||||
carddelivered = models.IntegerField(db_column='cardDelivered') # Field name made lowercase.
|
||||
laststicker = models.CharField(db_column='lastSticker', max_length=9) # Field name made lowercase.
|
||||
migrated = models.DateTimeField(blank=True, null=True)
|
||||
ldap_username = models.CharField(unique=True, max_length=20, blank=True)
|
||||
source = models.CharField(max_length=255)
|
||||
registration_status = models.CharField(max_length=255)
|
||||
created = models.DateTimeField(blank=True, null=True)
|
||||
membership_trial = models.CharField(max_length=255)
|
||||
|
||||
def __unicode__(self):
|
||||
if not self.ldap_username:
|
||||
return 'User {}'.format(self.pk)
|
||||
|
||||
return '{} ({})'.format(self.ldap_username, self.pk)
|
||||
|
||||
class Meta:
|
||||
managed = False
|
||||
db_table = 'din_user'
|
||||
|
||||
|
||||
class UserAddressInt(models.Model):
|
||||
connection_name = 'inside'
|
||||
|
||||
user = models.ForeignKey('inside.InsideUser', primary_key=True)
|
||||
street = models.CharField(max_length=255)
|
||||
zipcode = models.CharField(max_length=10)
|
||||
city = models.CharField(max_length=50)
|
||||
state = models.CharField(max_length=50)
|
||||
country = models.CharField(max_length=50)
|
||||
|
||||
class Meta:
|
||||
managed = False
|
||||
db_table = 'din_useraddressint'
|
||||
|
||||
|
||||
class UserAddressNo(models.Model):
|
||||
connection_name = 'inside'
|
||||
|
||||
user = models.ForeignKey('inside.InsideUser', primary_key=True)
|
||||
street = models.CharField(max_length=255)
|
||||
zipcode = models.ForeignKey('inside.PostalCode', db_column='zipcode')
|
||||
|
||||
class Meta:
|
||||
managed = False
|
||||
db_table = 'din_useraddressno'
|
||||
|
||||
|
||||
class UserGroupRelationship(models.Model):
|
||||
connection_name = 'inside'
|
||||
|
||||
user = models.ForeignKey('inside.InsideUser', related_name='group_rels')
|
||||
group = models.ForeignKey('inside.InsideGroup', related_name='user_rels')
|
||||
|
||||
class Meta:
|
||||
managed = False
|
||||
db_table = 'din_usergrouprelationship'
|
||||
|
||||
|
||||
class UserPhoneNumber(models.Model):
|
||||
connection_name = 'inside'
|
||||
|
||||
user = models.ForeignKey('inside.InsideUser', primary_key=True)
|
||||
number = models.CharField(max_length=16)
|
||||
validated = models.IntegerField()
|
||||
|
||||
class Meta:
|
||||
managed = False
|
||||
db_table = 'din_userphonenumber'
|
||||
|
||||
|
||||
class UserUpdate(models.Model):
|
||||
connection_name = 'inside'
|
||||
|
||||
date = models.DateTimeField(auto_now_add=True)
|
||||
user_updated = models.ForeignKey(
|
||||
'inside.InsideUser',
|
||||
db_column='user_id_updated',
|
||||
related_name='updates')
|
||||
comment = models.CharField(max_length=255)
|
||||
user_updated_by = models.ForeignKey(
|
||||
'inside.InsideUser',
|
||||
db_column='user_id_updated_by',
|
||||
blank=True,
|
||||
null=True,
|
||||
related_name='admin_updates')
|
||||
|
||||
class Meta:
|
||||
managed = False
|
||||
db_table = 'din_userupdate'
|
||||
|
||||
|
||||
class AuthLog(models.Model):
|
||||
connection_name = 'inside'
|
||||
|
||||
username = models.CharField(max_length=80)
|
||||
password = models.IntegerField()
|
||||
time = models.DateTimeField()
|
||||
error = models.CharField(max_length=100)
|
||||
|
||||
class Meta:
|
||||
managed = False
|
||||
db_table = 'inside_auth_log'
|
||||
|
||||
|
||||
class PostalCode(models.Model):
|
||||
connection_name = 'inside'
|
||||
|
||||
postnummer = models.CharField(primary_key=True, max_length=4)
|
||||
poststed = models.CharField(max_length=32)
|
||||
kommunenr = models.CharField(max_length=4)
|
||||
kommunenavn = models.CharField(max_length=30)
|
||||
kategori = models.CharField(max_length=1)
|
||||
|
||||
class Meta:
|
||||
managed = False
|
||||
db_table = 'postnummer'
|
||||
|
||||
|
||||
class PlaceOfStudy(models.Model):
|
||||
connection_name = 'inside'
|
||||
|
||||
navn = models.CharField(unique=True, max_length=120)
|
||||
|
||||
class Meta:
|
||||
managed = False
|
||||
db_table = 'studiesteder'
|
||||
@@ -0,0 +1,3 @@
|
||||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
||||
@@ -0,0 +1,39 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
from django.db import connections
|
||||
import logging
|
||||
from passlib.hash import mysql41
|
||||
|
||||
from inside.models import InsideUser, UserUpdate
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def mysql_create(raw_password):
|
||||
return mysql41.encrypt(raw_password)
|
||||
|
||||
|
||||
def log_inside_userupdate(username, message):
|
||||
try:
|
||||
user = InsideUser.objects.get(ldap_username=username)
|
||||
except InsideUser.DoesNotExist:
|
||||
logger.warning('Could not log update in Inside for user with username {}'.format(username))
|
||||
return
|
||||
|
||||
UserUpdate.objects.create(user_updated=user, comment=message, user_updated_by=user)
|
||||
|
||||
|
||||
def set_inside_password(username, raw_password):
|
||||
hashed_password = mysql_create(raw_password)
|
||||
|
||||
try:
|
||||
user = InsideUser.objects.get(ldap_username=username)
|
||||
except InsideUser.DoesNotExist:
|
||||
logger.warning('Could not set password in Inside for user with username {}'.format(username))
|
||||
return
|
||||
|
||||
user.password = hashed_password
|
||||
user.save()
|
||||
|
||||
log_inside_userupdate(username, "Satt passord på nytt via brukerinfo.neuf.no.")
|
||||
@@ -0,0 +1,3 @@
|
||||
from django.shortcuts import render
|
||||
|
||||
# Create your views here.
|
||||
@@ -1,77 +0,0 @@
|
||||
from django.contrib.auth.models import User, Group
|
||||
from django.db import connections
|
||||
|
||||
|
||||
class InsideBackend(object):
|
||||
"""
|
||||
Authenticate against the inside database
|
||||
"""
|
||||
|
||||
def authenticate(self, username=None, password=None):
|
||||
row = None
|
||||
cur = connections['inside'].cursor()
|
||||
try:
|
||||
cur.execute("SELECT id,ldap_username,firstname,lastname,email FROM din_user WHERE password=PASSWORD(%s) AND (ldap_username=%s OR email=%s)", [password, username, username])
|
||||
row = cur.fetchone()
|
||||
except Exception as e:
|
||||
# FIXME errorhandling
|
||||
print "Auth ERROR: {}".format(e)
|
||||
raise e
|
||||
finally:
|
||||
cur.close()
|
||||
|
||||
if row:
|
||||
username = row[1]
|
||||
try:
|
||||
user = User.objects.get(username=username)
|
||||
except User.DoesNotExist:
|
||||
# Create a new user.
|
||||
user = User(username=username, password=password)
|
||||
|
||||
user = self._update_user_details(user, row) # Saves user object
|
||||
user = self._update_groups(user, row)
|
||||
|
||||
return user
|
||||
|
||||
return None
|
||||
|
||||
def get_user(self, user_id):
|
||||
try:
|
||||
return User.objects.get(pk=user_id)
|
||||
except User.DoesNotExist:
|
||||
return None
|
||||
|
||||
def _update_user_details(self, user, row):
|
||||
uid, username, first_name, last_name, email = row
|
||||
user.first_name = first_name
|
||||
user.last_name = last_name
|
||||
user.email = email
|
||||
user.save()
|
||||
return user
|
||||
|
||||
def _update_groups(self, user, user_row):
|
||||
groups = None
|
||||
cur = connections['inside'].cursor()
|
||||
user_id = user_row[0]
|
||||
|
||||
existing_groups = list(user.groups.all())
|
||||
try:
|
||||
cur.execute("SELECT g.posix_group FROM din_user AS u LEFT JOIN din_usergrouprelationship AS ugr ON u.id = ugr.user_id LEFT JOIN din_group AS g ON ugr.group_id = g.id WHERE u.id = %s AND g.posix_group != ''", (int(user_row[0]),))
|
||||
groups = map(lambda x: x[0], cur.fetchall())
|
||||
for group in groups:
|
||||
g, created = Group.objects.get_or_create(name=group)
|
||||
user.groups.add(g)
|
||||
|
||||
# Remove old relationships
|
||||
# TODO: if setting is set. not tested
|
||||
for g in existing_groups:
|
||||
if g.name not in groups:
|
||||
user.groups.remove(g)
|
||||
|
||||
except Exception as e:
|
||||
# FIXME errorhandling
|
||||
print "ERROR: {}".format(e)
|
||||
finally:
|
||||
cur.close()
|
||||
|
||||
return user
|
||||
@@ -1,23 +0,0 @@
|
||||
"""
|
||||
This file demonstrates two different styles of tests (one doctest and one
|
||||
unittest). These will both pass when you run "manage.py test".
|
||||
|
||||
Replace these with more appropriate tests for your application.
|
||||
"""
|
||||
|
||||
from django.test import TestCase
|
||||
|
||||
class SimpleTest(TestCase):
|
||||
def test_basic_addition(self):
|
||||
"""
|
||||
Tests that 1 + 1 always equals 2.
|
||||
"""
|
||||
self.failUnlessEqual(1 + 1, 2)
|
||||
|
||||
__test__ = {"doctest": """
|
||||
Another way to test that 1 + 1 is equal to 2.
|
||||
|
||||
>>> 1 + 1 == 2
|
||||
True
|
||||
"""}
|
||||
|
||||
@@ -1,102 +0,0 @@
|
||||
# coding: utf-8
|
||||
from django.conf import settings
|
||||
from django.core.exceptions import ObjectDoesNotExist
|
||||
from subprocess import Popen, PIPE
|
||||
import datetime
|
||||
import re
|
||||
|
||||
from models import Radcheck
|
||||
import passwd
|
||||
|
||||
def get_kerberos_principal(username):
|
||||
principal = u"{0}@{1}".format(username, settings.KERBEROS_REALM)
|
||||
kadmin_query = u" -p '{0}' -w {1} -q 'get_principal {2}'".format(settings.KERBEROS_PASSWORD_CHANGING_PRINCIPAL,
|
||||
settings.KERBEROS_PASSWORD,
|
||||
principal)
|
||||
p = Popen('kadmin' + kadmin_query, shell=True, stdout=PIPE, stderr=PIPE)
|
||||
output, error = p.communicate()
|
||||
if error:
|
||||
# Can not init with KDC?
|
||||
# KADM5_UNK_PRINC (principal does not exist)
|
||||
# KADM5_AUTH_GET (requires the get (inquire) privilege)
|
||||
return None
|
||||
else:
|
||||
cleaned_output = {}
|
||||
match = re.findall(r'([\w ]*): (.+)', output)
|
||||
for key,value in match:
|
||||
cleaned_output[key] = value
|
||||
return cleaned_output
|
||||
|
||||
def set_kerberos_password(username, raw_password):
|
||||
principal = u"{0}@{1}".format(username, settings.KERBEROS_REALM)
|
||||
kadmin_query = u" -p {0} -w {1} -q 'change_password -pw {2} {3}'".format(settings.KERBEROS_PASSWORD_CHANGING_PRINCIPAL,
|
||||
settings.KERBEROS_PASSWORD,
|
||||
raw_password,
|
||||
principal)
|
||||
# Note: Running this shell command could need escaping: http://qntm.org/bash
|
||||
p = Popen('kadmin' + kadmin_query, shell=True, stdout=PIPE, stderr=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):
|
||||
"""
|
||||
Set password in Radius mysql backend.
|
||||
Create user if one with an NT-Password does not exist.
|
||||
"""
|
||||
try:
|
||||
radius_user = Radcheck.objects.get(username=username)
|
||||
except ObjectDoesNotExist:
|
||||
radius_user = Radcheck(username=username, attribute='NT-Password', op=':=')
|
||||
|
||||
# Some have clear text passwords!
|
||||
if radius_user.attribute != 'NT-Password':
|
||||
radius_user.attribute = 'NT-Password'
|
||||
|
||||
radius_user.value = passwd.radius_create(raw_password)
|
||||
radius_user.save()
|
||||
return True
|
||||
|
||||
def log_inside_userupdate(username, message):
|
||||
from django.db import connections
|
||||
c = connections['inside'].cursor()
|
||||
|
||||
# TODO errorhandling
|
||||
try:
|
||||
c.execute("SELECT id FROM din_user WHERE username= %s", [username])
|
||||
row = c.fetchone()
|
||||
if row is not None:
|
||||
user_id = row[0]
|
||||
c.execute(
|
||||
"INSERT INTO din_userupdate (date, user_id_updated, comment, user_id_updated_by) VALUES (NOW(), %s, %s, %s)",
|
||||
[user_id, message, user_id])
|
||||
finally:
|
||||
c.close()
|
||||
|
||||
return True
|
||||
|
||||
def set_inside_password(username, raw_password):
|
||||
from django.db import connections
|
||||
c = connections['inside'].cursor()
|
||||
|
||||
# TODO errorhandling
|
||||
try:
|
||||
c.execute("UPDATE din_user SET password=PASSWORD(%s) WHERE username=%s", [raw_password, username])
|
||||
finally:
|
||||
c.close()
|
||||
|
||||
log_inside_userupdate(username, u"Satt passord på nytt via brukerinfo.neuf.no.")
|
||||
|
||||
return True
|
||||
|
||||
def format_krb5_date(date):
|
||||
try:
|
||||
formatted_date = datetime.datetime.strptime(date, '%a %b %d %H:%M:%S %Z %Y')
|
||||
formatted_date = formatted_date.strftime('%Y-%m-%d %H:%M:%S')
|
||||
except ValueError:
|
||||
formatted_date = 'never'
|
||||
return formatted_date
|
||||
@@ -1,11 +1,13 @@
|
||||
from django.contrib import admin
|
||||
from main.models import LdapGroup, LdapUser
|
||||
from neuf_userinfo.models import LdapGroup, LdapUser
|
||||
|
||||
|
||||
class LdapGroupAdmin(admin.ModelAdmin):
|
||||
exclude = ['dn', 'usernames']
|
||||
list_display = ['name', 'gid']
|
||||
search_fields = ['name']
|
||||
|
||||
|
||||
class LdapUserAdmin(admin.ModelAdmin):
|
||||
exclude = ['dn', 'password', 'photo']
|
||||
list_display = ['username', 'first_name', 'last_name', 'email', 'id']
|
||||
@@ -1,4 +1,4 @@
|
||||
from django_auth_ldap.backend import LDAPBackend, _LDAPUser
|
||||
from django_auth_ldap.backend import LDAPBackend
|
||||
|
||||
|
||||
class LDAPUsernameBackend(LDAPBackend):
|
||||
@@ -1,4 +1,5 @@
|
||||
# coding: utf-8
|
||||
from __future__ import unicode_literals
|
||||
from django.contrib.auth.forms import SetPasswordForm, PasswordResetForm
|
||||
from django.contrib.auth.tokens import default_token_generator
|
||||
from django.contrib.sites.models import get_current_site
|
||||
@@ -9,50 +10,53 @@ from django.utils.encoding import force_bytes
|
||||
from django.utils.http import urlsafe_base64_encode
|
||||
from django.utils.translation import ugettext as _
|
||||
import sys
|
||||
from inside.models import InsideUser
|
||||
from inside.utils import set_inside_password
|
||||
|
||||
from models import *
|
||||
from validators import PasswordValidator
|
||||
import utils
|
||||
from neuf_userinfo.utils import set_kerberos_password, set_radius_password
|
||||
|
||||
class LDAPSetPasswordForm(SetPasswordForm):
|
||||
|
||||
class NeufSetPasswordForm(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'])
|
||||
|
||||
# set inside password
|
||||
set_inside = utils.set_inside_password(self.user.username, self.cleaned_data['new_password1'])
|
||||
# Membership database
|
||||
set_inside_password(self.user.username, self.cleaned_data['new_password1'])
|
||||
|
||||
# Lookup the Ldap user with the identical username (1-to-1).
|
||||
# Active services
|
||||
set_kerberos_password(self.user.username, self.cleaned_data['new_password1'])
|
||||
set_radius_password(self.user.username, self.cleaned_data['new_password1'])
|
||||
|
||||
# LDAP: Lookup the Ldap user with the identical username (1-to-1).
|
||||
self.user = LdapUser.objects.get(username=self.user.username)
|
||||
|
||||
# set ldap password
|
||||
self.user.set_password(self.cleaned_data['new_password1'])
|
||||
if commit:
|
||||
self.user.save()
|
||||
|
||||
# TODO: Log the result of the above.
|
||||
|
||||
return self.user
|
||||
|
||||
def clean_new_password1(self):
|
||||
raw_password = self.cleaned_data.get('new_password1')
|
||||
# Validation here
|
||||
|
||||
MinLengthValidator(8)(raw_password)
|
||||
PasswordValidator(raw_password)
|
||||
|
||||
return raw_password
|
||||
|
||||
class LDAPPasswordChangeForm(LDAPSetPasswordForm):
|
||||
|
||||
class NeufPasswordChangeForm(NeufSetPasswordForm):
|
||||
pass
|
||||
|
||||
class LDAPPasswordResetForm(PasswordResetForm):
|
||||
|
||||
class NeufPasswordResetForm(PasswordResetForm):
|
||||
EMAIL_ERROR_MSG = "That e-mail address doesn't have an associated user account. Are you sure you've registered?"
|
||||
|
||||
def clean_email(self):
|
||||
email = self.cleaned_data["email"].lower()
|
||||
self.users_cache = LdapUser.objects.filter(email=email)
|
||||
self.users_cache = InsideUser.objects.filter(email=email)
|
||||
if len(self.users_cache) == 0:
|
||||
raise forms.ValidationError(_("That e-mail address doesn't have an associated user account. Are you sure you've registered?"))
|
||||
raise forms.ValidationError(_(self.EMAIL_ERROR_MSG))
|
||||
return email
|
||||
|
||||
def save(self, domain_override=None,
|
||||
@@ -88,8 +92,7 @@ class LDAPPasswordResetForm(PasswordResetForm):
|
||||
subject = ''.join(subject.splitlines())
|
||||
email = loader.render_to_string(email_template_name, c)
|
||||
if sys.version_info < (2, 6, 6):
|
||||
# Workaround for http://bugs.python.org/issue1368247 ?
|
||||
# Workaround for http://bugs.python.org/issue1368247
|
||||
email = email.encode('utf-8')
|
||||
|
||||
send_mail(subject, email, from_email, [user.email])
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
from ldapdb.models.fields import CharField, ImageField, IntegerField, ListField
|
||||
import datetime
|
||||
from django.db import models
|
||||
import ldapdb.models
|
||||
from ldapdb.models.fields import CharField, ImageField, IntegerField, ListField
|
||||
import passwd
|
||||
import datetime
|
||||
|
||||
|
||||
class LdapUser(ldapdb.models.Model):
|
||||
"""
|
||||
@@ -12,7 +13,7 @@ class LdapUser(ldapdb.models.Model):
|
||||
connection_name = 'ldap'
|
||||
|
||||
# LDAP meta-data
|
||||
base_dn = "ou=People,dc=neuf,dc=no" # CONFIG
|
||||
base_dn = "ou=People,dc=neuf,dc=no" # FXIME: CONFIG
|
||||
object_classes = ['inetOrgPerson', 'posixAccount', 'shadowAccount']
|
||||
|
||||
# inetOrgPerson
|
||||
@@ -25,9 +26,9 @@ class LdapUser(ldapdb.models.Model):
|
||||
photo = ImageField(db_column='jpegPhoto')
|
||||
|
||||
# posixAccount
|
||||
id = IntegerField(db_column='uidNumber', unique=True) # referenced in reset password form
|
||||
id = IntegerField(db_column='uidNumber', unique=True) # referenced in reset password form
|
||||
group = IntegerField(db_column='gidNumber')
|
||||
gecos = CharField(db_column='gecos')
|
||||
gecos = CharField(db_column='gecos')
|
||||
home_directory = CharField(db_column='homeDirectory')
|
||||
login_shell = CharField(db_column='loginShell', default='/bin/bash')
|
||||
username = CharField(db_column='uid', primary_key=True)
|
||||
@@ -55,6 +56,7 @@ class LdapUser(ldapdb.models.Model):
|
||||
def __unicode__(self):
|
||||
return self.full_name
|
||||
|
||||
|
||||
class LdapGroup(ldapdb.models.Model):
|
||||
"""
|
||||
Class for representing an LDAP group entry.
|
||||
@@ -63,7 +65,7 @@ class LdapGroup(ldapdb.models.Model):
|
||||
connection_name = 'ldap'
|
||||
|
||||
# LDAP meta-data
|
||||
base_dn = "ou=Groups,dc=neuf,dc=no" # CONFIG
|
||||
base_dn = "ou=Groups,dc=neuf,dc=no" # CONFIG
|
||||
object_classes = ['posixGroup']
|
||||
|
||||
# posixGroup attributes
|
||||
@@ -116,9 +118,11 @@ class Radacct(models.Model):
|
||||
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'
|
||||
|
||||
@@ -126,9 +130,11 @@ class Radcheck(models.Model):
|
||||
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'
|
||||
|
||||
@@ -140,6 +146,7 @@ class Radgroupcheck(models.Model):
|
||||
class Meta:
|
||||
db_table = u'radgroupcheck'
|
||||
|
||||
|
||||
class Radgroupreply(models.Model):
|
||||
connection_name = 'radius'
|
||||
|
||||
@@ -151,6 +158,7 @@ class Radgroupreply(models.Model):
|
||||
class Meta:
|
||||
db_table = u'radgroupreply'
|
||||
|
||||
|
||||
class Radpostauth(models.Model):
|
||||
connection_name = 'radius'
|
||||
|
||||
@@ -162,6 +170,7 @@ class Radpostauth(models.Model):
|
||||
class Meta:
|
||||
db_table = u'radpostauth'
|
||||
|
||||
|
||||
class Radreply(models.Model):
|
||||
connection_name = 'radius'
|
||||
|
||||
@@ -173,6 +182,7 @@ class Radreply(models.Model):
|
||||
class Meta:
|
||||
db_table = u'radreply'
|
||||
|
||||
|
||||
class Radusergroup(models.Model):
|
||||
connection_name = 'radius'
|
||||
|
||||
@@ -7,6 +7,7 @@ from base64 import decodestring as decode
|
||||
# - http://www.openldap.org/faq/data/cache/347.html
|
||||
# - http://www.openldap.org/doc/admin24/security.html
|
||||
|
||||
|
||||
def ldap_create(raw_password, hash_type='ssha'):
|
||||
# Generate a salted SHA hash
|
||||
salt = os.urandom(4)
|
||||
@@ -14,6 +15,7 @@ def ldap_create(raw_password, hash_type='ssha'):
|
||||
h.update(salt)
|
||||
return "{SSHA}" + encode(h.digest() + salt)[:-1]
|
||||
|
||||
|
||||
def ldap_validate(raw_password, challenge_password):
|
||||
# challenge_password is hash from db
|
||||
# raw_password is the cleartext password.
|
||||
@@ -24,6 +26,7 @@ def ldap_validate(raw_password, challenge_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()
|
||||
@@ -1,6 +1,7 @@
|
||||
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.
|
||||
@@ -28,4 +29,3 @@ class Router(object):
|
||||
if is_radius_model(model):
|
||||
return model.connection_name
|
||||
return None
|
||||
|
||||
|
Before Width: | Height: | Size: 6.7 KiB After Width: | Height: | Size: 6.7 KiB |
|
Before Width: | Height: | Size: 481 B After Width: | Height: | Size: 481 B |
|
Before Width: | Height: | Size: 923 B After Width: | Height: | Size: 923 B |
|
Before Width: | Height: | Size: 3.2 KiB After Width: | Height: | Size: 3.2 KiB |
|
Before Width: | Height: | Size: 4.3 KiB After Width: | Height: | Size: 4.3 KiB |
|
Before Width: | Height: | Size: 4.3 KiB After Width: | Height: | Size: 4.3 KiB |
@@ -0,0 +1 @@
|
||||
from django.test import TestCase
|
||||
@@ -0,0 +1,78 @@
|
||||
# coding: utf-8
|
||||
from __future__ import unicode_literals
|
||||
import datetime
|
||||
from django.conf import settings
|
||||
import logging
|
||||
import passwd
|
||||
import re
|
||||
from subprocess import Popen, PIPE
|
||||
|
||||
from models import Radcheck
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def get_kerberos_principal(username):
|
||||
principal = "{0}@{1}".format(username, settings.KERBEROS_REALM)
|
||||
kadmin_query = " -p '{0}' -w {1} -q 'get_principal {2}'".format(
|
||||
settings.KERBEROS_PASSWORD_CHANGING_PRINCIPAL,
|
||||
settings.KERBEROS_PASSWORD,
|
||||
principal)
|
||||
p = Popen('kadmin' + kadmin_query, shell=True, stdout=PIPE, stderr=PIPE)
|
||||
output, error = p.communicate()
|
||||
if error:
|
||||
# Can not init with KDC?
|
||||
# KADM5_UNK_PRINC (principal does not exist)
|
||||
# KADM5_AUTH_GET (requires the get (inquire) privilege)
|
||||
logger.error(error)
|
||||
return None
|
||||
else:
|
||||
cleaned_output = {}
|
||||
match = re.findall(r'([\w ]*): (.+)', output)
|
||||
for key, value in match:
|
||||
cleaned_output[key] = value
|
||||
return cleaned_output
|
||||
|
||||
|
||||
def set_kerberos_password(username, raw_password):
|
||||
principal = "{0}@{1}".format(username, settings.KERBEROS_REALM)
|
||||
kadmin_query = " -p {0} -w {1} -q 'change_password -pw {2} {3}'".format(
|
||||
settings.KERBEROS_PASSWORD_CHANGING_PRINCIPAL,
|
||||
settings.KERBEROS_PASSWORD,
|
||||
raw_password,
|
||||
principal)
|
||||
# FIXME: Running this shell command needs escaping: http://qntm.org/bash
|
||||
p = Popen('kadmin' + kadmin_query, shell=True, stdout=PIPE, stderr=PIPE)
|
||||
output, error = p.communicate()
|
||||
if error:
|
||||
# Can not init with KDC?
|
||||
# KADM5_AUTH_MODIFY (requires the modify privilege)
|
||||
logger.error(error)
|
||||
|
||||
|
||||
def set_radius_password(username, raw_password):
|
||||
"""
|
||||
Set password in Radius mysql backend.
|
||||
Create user if one with an NT-Password does not exist.
|
||||
"""
|
||||
pw_type = 'NT-Password'
|
||||
radius_defaults = {'attribute': pw_type, 'op': ':='}
|
||||
radius_user, created = Radcheck.objects.get_or_create(
|
||||
username=username,
|
||||
defaults=radius_defaults)
|
||||
|
||||
# Some have clear text passwords!
|
||||
if radius_user.attribute != pw_type:
|
||||
radius_user.attribute = pw_type
|
||||
|
||||
radius_user.value = passwd.radius_create(raw_password)
|
||||
radius_user.save()
|
||||
|
||||
|
||||
def format_krb5_date(date):
|
||||
try:
|
||||
formatted_date = datetime.datetime.strptime(date, '%a %b %d %H:%M:%S %Z %Y')
|
||||
formatted_date = formatted_date.strftime('%Y-%m-%d %H:%M:%S')
|
||||
except ValueError:
|
||||
formatted_date = 'never'
|
||||
return formatted_date
|
||||
@@ -1,26 +1,23 @@
|
||||
# coding: utf-8
|
||||
import json
|
||||
|
||||
from django.contrib.auth.decorators import login_required, permission_required
|
||||
from django.contrib.auth.forms import SetPasswordForm, AuthenticationForm
|
||||
from django.contrib.auth import login, logout as auth_logout
|
||||
from django.contrib.auth.models import User
|
||||
from django.contrib.auth.tokens import default_token_generator
|
||||
from django.core.exceptions import ObjectDoesNotExist
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.http import HttpResponse, HttpResponseRedirect
|
||||
from django.shortcuts import render_to_response, get_object_or_404, resolve_url
|
||||
from django.shortcuts import render_to_response, resolve_url, render
|
||||
from django.template import RequestContext
|
||||
from django.template.response import TemplateResponse
|
||||
from django.utils.http import urlsafe_base64_decode
|
||||
from django.views.decorators.cache import never_cache
|
||||
from django.views.decorators.csrf import csrf_protect
|
||||
from django.views.decorators.debug import sensitive_post_parameters
|
||||
import json
|
||||
from inside.models import InsideUser
|
||||
|
||||
from forms import LDAPPasswordResetForm
|
||||
from models import *
|
||||
import utils
|
||||
|
||||
|
||||
def index(request):
|
||||
if request.method == "POST":
|
||||
form = AuthenticationForm(data=request.POST)
|
||||
@@ -29,12 +26,13 @@ def index(request):
|
||||
login(request, form.get_user())
|
||||
else:
|
||||
if request.user.is_authenticated():
|
||||
return HttpResponseRedirect( reverse('main.views.profile') )
|
||||
return HttpResponseRedirect(reverse('neuf_userinfo.views.profile'))
|
||||
else:
|
||||
form = AuthenticationForm(request)
|
||||
|
||||
return render_to_response('public/index.html', locals(), context_instance=RequestContext(request))
|
||||
|
||||
|
||||
@login_required
|
||||
def profile(request):
|
||||
username = request.user.username
|
||||
@@ -42,7 +40,7 @@ def profile(request):
|
||||
ldap_user = None
|
||||
try:
|
||||
ldap_user = LdapUser.objects.get(username=username)
|
||||
except:
|
||||
except LdapUser.DoesNotExist:
|
||||
return render_to_response('private/profile.html', locals(), context_instance=RequestContext(request))
|
||||
|
||||
ldap_groups = LdapGroup.objects.filter(usernames__contains=username)
|
||||
@@ -50,12 +48,13 @@ def profile(request):
|
||||
|
||||
return render_to_response('private/profile.html', locals(), context_instance=RequestContext(request))
|
||||
|
||||
|
||||
@permission_required('main.is_superuser')
|
||||
def user_profile(request, username):
|
||||
ldap_user = None
|
||||
try:
|
||||
ldap_user = LdapUser.objects.get(username=username)
|
||||
except:
|
||||
except LdapUser.DoesNotExist:
|
||||
return render_to_response('private/profile.html', locals(), context_instance=RequestContext(request))
|
||||
|
||||
groups = LdapGroup.objects.filter(usernames__contains=username)
|
||||
@@ -63,37 +62,42 @@ def user_profile(request, username):
|
||||
|
||||
return render_to_response('private/profile.html', locals(), context_instance=RequestContext(request))
|
||||
|
||||
#todo security
|
||||
|
||||
# TODO security
|
||||
def client_status(request):
|
||||
krb5_principal = utils.get_kerberos_principal(request.GET.get('username'))
|
||||
if krb5_principal:
|
||||
last_succ_auth = utils.format_krb5_date(krb5_principal['Last successful authentication'])
|
||||
status = { 'active' : True,
|
||||
'last_successful_auth' : last_succ_auth,
|
||||
'last_modified' : krb5_principal['Last modified'] }
|
||||
status = {
|
||||
'active': True,
|
||||
'last_successful_auth': last_succ_auth,
|
||||
'last_modified': krb5_principal['Last modified']
|
||||
}
|
||||
else:
|
||||
status = { 'active' : False }
|
||||
status = {'active': False}
|
||||
return HttpResponse(json.dumps(status), content_type='application/javascript; charset=utf8')
|
||||
|
||||
#todo security
|
||||
|
||||
# TODO security
|
||||
def wireless_status(request):
|
||||
username = request.GET.get('username')
|
||||
try:
|
||||
radius_user = Radcheck.objects.get(username=request.GET.get('username'))
|
||||
# get last authentication
|
||||
last_auth = Radpostauth.objects.filter(username__iexact=request.GET.get('username'), reply='Access-Accept').order_by('authdate')
|
||||
if len(last_auth) != 0:
|
||||
status = { 'active' : True,
|
||||
'last_successful_auth': last_auth[0].authdate.strftime('%Y-%m-%d %H:%M:%S'),
|
||||
'hash': radius_user.attribute }
|
||||
else:
|
||||
status = { 'active' : True, 'hash': radius_user.attribute }
|
||||
except ObjectDoesNotExist:
|
||||
status = { 'active' : False }
|
||||
radius_user = Radcheck.objects.get(username=username)
|
||||
except Radcheck.ObjectDoesNotExist:
|
||||
return HttpResponse(json.dumps({'active': False}), content_type='application/javascript; charset=utf8')
|
||||
|
||||
# get last authentication
|
||||
last_auth = Radpostauth.objects.filter(username__iexact=username, reply='Access-Accept').order_by('authdate')
|
||||
status = {'active': True, 'hash': radius_user.attribute}
|
||||
if len(last_auth) != 0:
|
||||
status['last_successful_auth'] = last_auth[0].authdate.strftime('%Y-%m-%d %H:%M:%S')
|
||||
|
||||
return HttpResponse(json.dumps(status), content_type='application/javascript; charset=utf8')
|
||||
|
||||
|
||||
def logout(request):
|
||||
auth_logout(request)
|
||||
return render_to_response('registration/logout.html', locals(), context_instance=RequestContext(request))
|
||||
return render(request, 'registration/logout.html')
|
||||
|
||||
|
||||
# Doesn't need csrf_protect since no-one can guess the URL
|
||||
@@ -119,8 +123,8 @@ def password_reset_confirm(request, uidb64=None, token=None,
|
||||
post_reset_redirect = resolve_url(post_reset_redirect)
|
||||
try:
|
||||
uid = urlsafe_base64_decode(uidb64)
|
||||
user = LdapUser.objects.get(pk=uid) # Diff line vs internal django view
|
||||
except (TypeError, ValueError, OverflowError, LdapUser.DoesNotExist):
|
||||
user = InsideUser.objects.get(pk=uid) # Diff line vs internal django view
|
||||
except (TypeError, ValueError, OverflowError, InsideUser.DoesNotExist):
|
||||
user = None
|
||||
|
||||
if user is not None and token_generator.check_token(user, token):
|
||||
@@ -1,15 +1,17 @@
|
||||
import json, codecs, subprocess, os
|
||||
import json
|
||||
import codecs
|
||||
import subprocess
|
||||
import os
|
||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "userinfosite.settings")
|
||||
from django.conf import settings
|
||||
|
||||
from main.models import *
|
||||
from neuf_userinfo.models import LdapGroup, LdapUser
|
||||
|
||||
PHP_SCRIPT_PATH = '/var/www/neuf.no/userinfo/scripts'
|
||||
OUT_FILENAME = os.path.join(PHP_SCRIPT_PATH,"users_in_group_active.json")
|
||||
OUT_FILENAME = os.path.join(PHP_SCRIPT_PATH, "users_in_group_active.json")
|
||||
|
||||
usernames = LdapGroup.objects.get(name="dns-aktiv").usernames
|
||||
usernames = LdapGroup.objects.get(posix_name="dns-aktiv").usernames
|
||||
users = LdapUser.objects.filter(username__in=usernames)
|
||||
users_out = [[u.username,u.first_name,u.last_name,u.email] for u in users]
|
||||
users_out = [[u.username, u.first_name, u.last_name, u.email] for u in users]
|
||||
|
||||
|
||||
out_file = codecs.open(OUT_FILENAME, "w", encoding="utf-8")
|
||||
@@ -29,4 +31,3 @@ for load_path in wp_load_paths:
|
||||
proc = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE)
|
||||
script_response = proc.stdout.read()
|
||||
print script_response,
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
{% if active %}
|
||||
<li class="{% if active = "profile" %}active{% endif %}"><a href="/accounts/profile"><i class="icon-home icon-white"></i> Din konto</a></li>
|
||||
<li class="{% if active = "password_change" %}active{% endif %}"><a href="{% url 'django.contrib.auth.views.password_change' %}"><i class="icon-cog icon-white"></i> Endre passord</a></li>
|
||||
<li class="{% if active = "logout" %}active{% endif %}"><a href="{% url 'main.views.logout' %}">Logg ut</a></li>
|
||||
<li class="{% if active = "logout" %}active{% endif %}"><a href="{% url 'neuf_userinfo.views.logout' %}">Logg ut</a></li>
|
||||
{% if request.user.is_superuser %}
|
||||
<li><a href="/admin">Administrasjon</a></li>
|
||||
{% endif %}
|
||||
|
||||
@@ -7,5 +7,5 @@
|
||||
{%endblock%}
|
||||
|
||||
{% block content %}
|
||||
Du har blitt logget ut, vil du <a href="{% url "main.views.index" %}">logge inn</a> igjen?
|
||||
Du har blitt logget ut, vil du <a href="{% url "neuf_userinfo.views.index" %}">logge inn</a> igjen?
|
||||
{% endblock %}
|
||||
@@ -1,2 +1,2 @@
|
||||
Noen har bedt om at passordet for brukeren {{ user.username }} med epostadresse {{ email }} skal endres. Vennligst følg lenken nedenfor for å sette et nytt passord:
|
||||
{{ protocol }}://{{ domain }}{% url "main.views.password_reset_confirm" uidb64=uid token=token %}
|
||||
{{ protocol }}://{{ domain }}{% url "neuf_userinfo.views.password_reset_confirm" uidb64=uid token=token %}
|
||||
@@ -9,8 +9,8 @@ dn: uid=test,ou=People,dc=neuf,dc=no
|
||||
objectClass: inetOrgPerson
|
||||
objectClass: posixAccount
|
||||
objectClass: shadowAccount
|
||||
uid: testesen
|
||||
gecos: testesen
|
||||
uid: test
|
||||
gecos: test
|
||||
loginShell: /bin/bash
|
||||
shadowExpire: -1
|
||||
shadowFlag: 0
|
||||
|
||||
@@ -35,12 +35,12 @@ INSTALLED_APPS = (
|
||||
'django.contrib.sessions',
|
||||
'django.contrib.messages',
|
||||
'django.contrib.staticfiles',
|
||||
)
|
||||
INSTALLED_APPS += (
|
||||
'django_extensions',
|
||||
'south',
|
||||
# Our app
|
||||
'main',
|
||||
)
|
||||
INSTALLED_APPS += (
|
||||
'neuf_userinfo',
|
||||
'inside',
|
||||
)
|
||||
|
||||
MIDDLEWARE_CLASSES = (
|
||||
@@ -67,38 +67,29 @@ DATABASES = {
|
||||
'default': {
|
||||
'ENGINE': 'django.db.backends.sqlite3',
|
||||
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
|
||||
# Example PostgreSQL config:
|
||||
#'ENGINE': 'django.db.backends.postgresql_psycopg2',
|
||||
#'NAME': 'dusken_test',
|
||||
#'USER': 'postgres',
|
||||
#'PASSWORD': '',
|
||||
#'HOST': '127.0.0.1',
|
||||
#'PORT': '',
|
||||
},
|
||||
'ldap': {
|
||||
'ENGINE': 'ldapdb.backends.ldap',
|
||||
'NAME': 'ldap://localhost/',
|
||||
'USER': 'uid=admin,ou=People,dc=neuf,dc=no',
|
||||
'PASSWORD': '',
|
||||
'USER': 'uid=test,ou=People,dc=neuf,dc=no',
|
||||
'PASSWORD': 'test',
|
||||
},
|
||||
'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.
|
||||
'ENGINE': 'django.db.backends.mysql',
|
||||
'NAME': 'radius',
|
||||
'USER': 'radius',
|
||||
'PASSWORD': '',
|
||||
'HOST': 'localhost',
|
||||
},
|
||||
'inside': {
|
||||
'ENGINE': 'django.db.backends.mysql', # Add 'postgresql_psycopg2', 'postgresql', 'mysql', 'sqlite3' or 'oracle'.
|
||||
'NAME': 'dev_inside', # Or path to database file if using sqlite3.
|
||||
'USER': 'dev', # Not used with sqlite3.
|
||||
'PASSWORD': 'dev', # 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.
|
||||
'ENGINE': 'django.db.backends.mysql',
|
||||
'NAME': 'dev_inside',
|
||||
'USER': 'dev',
|
||||
'PASSWORD': 'dev',
|
||||
'HOST': 'localhost',
|
||||
}
|
||||
}
|
||||
DATABASE_ROUTERS = ['main.router.Router']
|
||||
DATABASE_ROUTERS = ['neuf_userinfo.router.Router']
|
||||
|
||||
# Internationalization
|
||||
# https://docs.djangoproject.com/en/1.6/topics/i18n/
|
||||
@@ -110,14 +101,10 @@ USE_TZ = True
|
||||
|
||||
# Static files (CSS, JavaScript, Images)
|
||||
# https://docs.djangoproject.com/en/1.6/howto/static-files/
|
||||
STATIC_ROOT = 'staticfiles'
|
||||
STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')
|
||||
STATIC_URL = '/static/'
|
||||
STATICFILES_DIRS = (
|
||||
os.path.join(BASE_DIR, 'static'),
|
||||
)
|
||||
|
||||
# Uploads
|
||||
# TODO S3
|
||||
MEDIA_ROOT = os.path.join(BASE_DIR, "media")
|
||||
MEDIA_URL = "/media/"
|
||||
|
||||
@@ -126,18 +113,19 @@ SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
|
||||
|
||||
SITE_ID = 1
|
||||
|
||||
EMAIL_HOST = 'snes.neuf.no'
|
||||
EMAIL_HOST = 'mx.neuf.no'
|
||||
EMAIL_PORT = 25
|
||||
DEFAULT_FROM_EMAIL = 'noreply@neuf.no'
|
||||
|
||||
AUTHENTICATION_BACKENDS = (
|
||||
'main.backends.LDAPEmailBackend',
|
||||
'main.backends.LDAPUsernameBackend',
|
||||
# 'inside.authentication.InsideBackend',
|
||||
'neuf_userinfo.backends.LDAPEmailBackend',
|
||||
'neuf_userinfo.backends.LDAPUsernameBackend',
|
||||
'django.contrib.auth.backends.ModelBackend',
|
||||
)
|
||||
# No cleaningladies.
|
||||
SESSION_EXPIRE_AT_BROWSER_CLOSE = True
|
||||
SESSION_COOKIE_AGE = 60 * 15 # in seconds
|
||||
SESSION_COOKIE_AGE = 60 * 15 # in seconds
|
||||
|
||||
# LDAP server URI and BIND_DN, same as db-settings
|
||||
AUTH_LDAP_U_SERVER_URI = DATABASES['ldap']['NAME']
|
||||
@@ -162,7 +150,7 @@ AUTH_LDAP_U_USER_FLAGS_BY_GROUP = {
|
||||
}
|
||||
# Group to profile flag mappings, not used.
|
||||
AUTH_LDAP_U_PROFILE_FLAGS_BY_GROUP = {
|
||||
#"is_edb": "cn=edb,ou=Groups,dc=neuf,dc=no"
|
||||
# "is_edb": "cn=edb,ou=Groups,dc=neuf,dc=no"
|
||||
}
|
||||
# User attribute mappings
|
||||
AUTH_LDAP_U_USER_ATTR_MAP = {
|
||||
@@ -203,3 +191,11 @@ logger.setLevel(logging.DEBUG)
|
||||
KERBEROS_REALM = "NEUF.NO"
|
||||
KERBEROS_PASSWORD_CHANGING_PRINCIPAL = 'brukerinfo'
|
||||
KERBEROS_PASSWORD = ''
|
||||
|
||||
# Inside
|
||||
INSIDE_GROUPS_SYNC_DELETE = True # When user logs in, groups are synced (locally in Django), this deletes aswell
|
||||
|
||||
try:
|
||||
from local_settings import *
|
||||
except ImportError:
|
||||
pass
|
||||
@@ -1,35 +1,40 @@
|
||||
from django.conf.urls import include, patterns, url
|
||||
from django.contrib import admin
|
||||
from main.forms import LDAPPasswordChangeForm, LDAPSetPasswordForm, LDAPPasswordResetForm
|
||||
from neuf_userinfo.forms import NeufPasswordChangeForm, NeufSetPasswordForm, NeufPasswordResetForm
|
||||
admin.autodiscover()
|
||||
|
||||
import settings
|
||||
|
||||
urlpatterns = patterns('',
|
||||
urlpatterns = patterns(
|
||||
'',
|
||||
url(r'^admin/', include(admin.site.urls)),
|
||||
url(r'^admin/doc/', include('django.contrib.admindocs.urls')),
|
||||
url(r'^accounts/profile/$', 'main.views.profile'),
|
||||
url(r'^accounts/profile/(?P<username>\w+)$', 'main.views.user_profile'),
|
||||
|
||||
url(r'^userstatus/client/$', 'neuf_userinfo.views.client_status'),
|
||||
url(r'^userstatus/wireless/$', 'neuf_userinfo.views.wireless_status'),
|
||||
url(r'^$', 'neuf_userinfo.views.index'),
|
||||
)
|
||||
|
||||
# User account views
|
||||
urlpatterns += patterns(
|
||||
'',
|
||||
url(r'^accounts/profile/$', 'neuf_userinfo.views.profile'),
|
||||
url(r'^accounts/profile/(?P<username>\w+)$', 'neuf_userinfo.views.user_profile'),
|
||||
url(r'^accounts/login/$', 'django.contrib.auth.views.login'),
|
||||
url(r'^accounts/logout/$', 'main.views.logout'),
|
||||
url(r'^accounts/logout/$', 'neuf_userinfo.views.logout'),
|
||||
|
||||
# Change password
|
||||
url(r'^accounts/password/change$', 'django.contrib.auth.views.password_change',
|
||||
{ 'password_change_form': LDAPPasswordChangeForm }),
|
||||
{'password_change_form': NeufPasswordChangeForm}),
|
||||
url(r'^accounts/password/change/done$', 'django.contrib.auth.views.password_change_done', name='password_change_done'),
|
||||
|
||||
# Reset password (forgot password)
|
||||
url(r'^accounts/password/reset$', 'django.contrib.auth.views.password_reset',
|
||||
{ 'password_reset_form' : LDAPPasswordResetForm,
|
||||
'from_email' : settings.DEFAULT_FROM_EMAIL }),
|
||||
{'password_reset_form': NeufPasswordResetForm, 'from_email': settings.DEFAULT_FROM_EMAIL}),
|
||||
url(r'^accounts/password/reset/done$', 'django.contrib.auth.views.password_reset_done', name='password_reset_done'),
|
||||
# Set password
|
||||
url(r'^accounts/password/reset/confirm/(?P<uidb64>[0-9A-Za-z_\-]+)/(?P<token>[0-9A-Za-z]{1,13}-[0-9A-Za-z]{1,20})/$',
|
||||
'main.views.password_reset_confirm',
|
||||
{ 'set_password_form': LDAPSetPasswordForm }),
|
||||
'neuf_userinfo.views.password_reset_confirm',
|
||||
{'set_password_form': NeufSetPasswordForm}),
|
||||
url(r'^accounts/password/reset/complete$', 'django.contrib.auth.views.password_reset_complete', name='password_reset_complete'),
|
||||
url(r'^userstatus/client/$', 'main.views.client_status'),
|
||||
url(r'^userstatus/wireless/$', 'main.views.wireless_status'),
|
||||
url(r'^$', 'main.views.index'),
|
||||
)
|
||||
|
||||
from django.contrib.staticfiles.urls import staticfiles_urlpatterns # for dev
|
||||
urlpatterns += staticfiles_urlpatterns()
|
||||
)
|
||||