Profile now displays LDAP-stuff via AJAX, is_active flag, cleanups

This commit is contained in:
nikolark committed 2015-02-22 20:40:03 +01:00
1 parent 2f745cf002
commit 7943fcfefb
14 files changed
+170 -86

No files matched your search

-2
View File
@@ -33,9 +33,7 @@ For simple dev you need at least:
- settings.FILESERVER_USER might need some sudo access
# TODO
- Rewrite usersync.neuf.no as one or more endpoints [DONE?]
- On password update, then update ldap hash on InsideUser.ldap_password
- allow non LDAP-users (conditional LDAP-stuff), because we don't want LDAP filled with non-active members
- Integrity check view which provides two way diffs for the following:
- user vs user group
- user vs kerberos principal
+37 -27
View File
@@ -11,69 +11,79 @@ logger = logging.getLogger(__name__)
class InsideBackend(object):
"""
Authenticate against the inside database
Authenticate against the inside database by username, passord or email, password.
"""
user = None
inside_user = None
def authenticate(self, username=None, password=None):
password_hash = utils.mysql_create(password)
inside_user = InsideUser.objects.filter(
self.inside_user = InsideUser.objects.filter(
Q(password=password_hash) & (Q(ldap_username=username) | Q(email=username))
)
# TODO log all login attempts
# AuthLog.objects.create()
if len(inside_user) != 1:
if len(self.inside_user) != 1:
return None
inside_user = inside_user[0]
self.inside_user = self.inside_user[0]
try:
user = User.objects.get(username=inside_user.ldap_username)
self.user = User.objects.get(username=self.inside_user.ldap_username)
except User.DoesNotExist:
# Create a new user.
user = User(username=inside_user.ldap_username, password=password)
self.user = User(username=self.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()
self._update_user_details()
self.user.save()
user = self._update_groups(user, inside_user)
self._update_groups()
return user
self._update_user_flags()
self.user.save()
def get_user(self, user_id):
try:
return User.objects.get(pk=user_id)
except User.DoesNotExist:
return None
return self.user
def _update_user_details(self, user, inside_user):
def _update_user_details(self):
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))
# Copy from Inside user to local Django user
setattr(self.user, local_attr, getattr(self.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='')
def _update_groups(self):
existing_groups = list(self.user.groups.all())
inside_groups = InsideGroup.objects.filter(user_rels__user=self.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)
self.user.groups.add(g)
if not settings.INSIDE_GROUPS_SYNC_DELETE:
return user
return
# Remove old relationships
for g in existing_groups:
if g.name not in inside_groups:
user.groups.remove(g)
self.user.groups.remove(g)
return user
def _update_user_flags(self):
flag_to_group = settings.INSIDE_USER_FLAGS_SYNC
user_groups = self.user.groups.values_list('name', flat=True)
for flag, group in flag_to_group.iteritems():
if group in user_groups:
setattr(self.user, flag, True)
else:
setattr(self.user, flag, False)
def get_user(self, user_id):
try:
return User.objects.get(pk=user_id)
except User.DoesNotExist:
return None
+6 -6
View File
@@ -1,14 +1,14 @@
from django.contrib.auth.decorators import login_required
from django.http import JsonResponse
from rest_framework.decorators import api_view
from rest_framework.response import Response
from neuf_kerberos.utils import get_kerberos_principal, format_krb5_date
@login_required
@api_view(['GET', 'OPTIONS', 'HEAD'])
def client_status(request):
krb5_principal = get_kerberos_principal(request.GET.get('username'))
krb5_principal = get_kerberos_principal(request.QUERY_PARAMS.get('username'))
if not krb5_principal:
return JsonResponse({'active': False})
return Response({'active': False})
last_succ_auth = format_krb5_date(krb5_principal['Last successful authentication'])
status = {
@@ -17,4 +17,4 @@ def client_status(request):
'last_modified': krb5_principal['Last modified']
}
return JsonResponse(status)
return Response(status)
+3 -2
View File
@@ -52,7 +52,8 @@ class LdapUser(ldapdb.models.Model):
# Hack to use django internals for password reset
last_login = datetime.datetime(2001, 1, 1)
def _days_since_1970(self):
@staticmethod
def _days_since_1970():
epoch = datetime.datetime.utcfromtimestamp(0)
today = datetime.datetime.today()
delta = today - epoch
@@ -62,7 +63,7 @@ class LdapUser(ldapdb.models.Model):
def set_password(self, raw_password, commit=True):
self.password = ldap_create(raw_password)
# Update last changed password date
self.shadowLastChange = self._days_since_1970()
self.shadowLastChange = LdapUser._days_since_1970()
if commit:
self.save()
+22
View File
@@ -0,0 +1,22 @@
from neuf_ldap.models import LdapUser, LdapGroup, LdapAutomountHome
from rest_framework import serializers
class UserSerializer(serializers.HyperlinkedModelSerializer):
user_group = serializers.HyperlinkedIdentityField(view_name='ldapgroup-detail')
class Meta:
model = LdapUser
fields = ('url', 'username', 'first_name', 'last_name', 'email', 'user_group') # 'dn', 'id', 'group',)
class StringListField(serializers.ListField):
child = serializers.CharField()
class GroupSerializer(serializers.HyperlinkedModelSerializer):
# members = StringListField()
class Meta:
model = LdapGroup
fields = ('url', 'name',) # 'dn', 'gid', 'members',)
+12
View File
@@ -0,0 +1,12 @@
from django.conf.urls import url, include
from rest_framework import routers
from neuf_ldap import views
router = routers.DefaultRouter()
router.register(r'users', views.UserViewSet)
router.register(r'groups', views.GroupViewSet)
urlpatterns = [
url(r'^ldap/', include(router.urls)),
]
+36 -2
View File
@@ -1,3 +1,37 @@
from django.shortcuts import render
from neuf_ldap.models import LdapGroup, LdapUser
from neuf_ldap.serializers import UserSerializer, GroupSerializer
from rest_framework import viewsets, filters
# Create your views here.
class UserViewSet(viewsets.ReadOnlyModelViewSet):
"""
API endpoint that allows users to be viewed
"""
queryset = LdapUser.objects.all()
serializer_class = UserSerializer
def get_queryset(self):
if self.request.user.is_superuser:
return super(UserViewSet, self).get_queryset()
return LdapUser.objects.filter(username=self.request.user.username)
class GroupViewSet(viewsets.ReadOnlyModelViewSet):
"""
API endpoint that allows groups to be viewed
"""
queryset = LdapGroup.objects.all()
serializer_class = GroupSerializer
filter_backends = (filters.DjangoFilterBackend,)
filter_fields = ('members',)
def get_queryset(self):
if self.request.user.is_superuser:
member = self.request.QUERY_PARAMS.get('member')
if member:
return LdapGroup.objects.filter(members__contains=member)
return super(GroupViewSet, self).get_queryset()
return LdapGroup.objects.filter(members__contains=self.request.user.username)
+6 -6
View File
@@ -1,15 +1,15 @@
from django.contrib.auth.decorators import login_required
from django.http import JsonResponse
from neuf_radius.models import Radcheck, Radpostauth
from rest_framework.decorators import api_view
from rest_framework.response import Response
@login_required
@api_view(['GET', 'OPTIONS', 'HEAD'])
def wireless_status(request):
username = request.GET.get('username')
username = request.QUERY_PARAMS.get('username')
try:
radius_user = Radcheck.objects.get(username=username)
except Radcheck.DoesNotExist:
return JsonResponse({'active': False})
return Response({'active': False})
# get last authentication
last_auth = Radpostauth.objects.filter(username__iexact=username, reply='Access-Accept').order_by('authdate')
@@ -17,4 +17,4 @@ def wireless_status(request):
if len(last_auth) != 0:
status['last_successful_auth'] = last_auth[0].authdate.strftime('%Y-%m-%d %H:%M:%S')
return JsonResponse(status)
return Response(status)
+3 -1
View File
@@ -1,11 +1,13 @@
from django.conf.urls import patterns, url
from neuf_userinfo.forms import NeufSetPasswordForm
from neuf_userinfo.views import AddNewUserView
urlpatterns = patterns(
'neuf_userinfo.views',
url(r'^$', 'index', name='index'),
url(r'^accounts/profile/(?P<username>\w*)$', 'profile'),
url(r'^accounts/profile/(?P<username>\w*)$', 'profile', name='profile'),
url(r'^accounts/profile/$', 'profile', name='my-profile'),
url(r'^accounts/logout/$', 'logout'),
# 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})/$',
+8 -25
View File
@@ -18,7 +18,6 @@ import logging
from inside.models import InsideUser
from neuf_kerberos.utils import kerberos_create_principal
from neuf_ldap.models import LdapGroup, LdapUser
from neuf_ldap.utils import ldap_create_user, ldap_create_automount
from neuf_radius.utils import radius_create_user
from neuf_userinfo.forms import NewUserForm
@@ -35,7 +34,7 @@ def index(request):
login(request, form.get_user())
else:
if request.user.is_authenticated():
return HttpResponseRedirect(reverse('neuf_userinfo.views.profile'))
return HttpResponseRedirect(reverse('my-profile'))
else:
form = AuthenticationForm(request)
@@ -43,34 +42,18 @@ def index(request):
@login_required
def profile(request, username=None):
if not username:
user = request.user
username = request.user.username
else:
def profile(request, username=''):
if username != '' and not request.user.is_superuser:
# Only allow superusers
if not request.user.is_superuser:
return HttpResponseNotFound()
return HttpResponseNotFound()
if username != '':
user = get_object_or_404(User, username=username)
# TODO: Move LDAP stuff to JSON-view and load via AJAX (more fault tolerant)
ldap_user = None
ldap_groups = None
ldap_private_group = None
try:
ldap_user = LdapUser.objects.get(username=username)
except LdapUser.DoesNotExist:
pass
if ldap_user:
ldap_groups = LdapGroup.objects.filter(usernames__contains=username)
ldap_private_group = LdapGroup.objects.get(gid=ldap_user.group)
else:
user = request.user
response = {
'ldap_user': ldap_user,
'ldap_groups': ldap_groups,
'ldap_private_group': ldap_private_group,
'groups': user.groups.all(),
'user': user
}
return render(request, 'private/profile.html', response)
+1
View File
@@ -2,6 +2,7 @@ Django==1.7.4
django-auth-ldap==1.2.0
django-extensions==1.5.0
django-ldapdb==0.3.2
djangorestframework==3.0.5
dj-static==0.0.6
Fabric==1.10.1
mysqlclient==1.3.5
+23 -9
View File
@@ -18,6 +18,7 @@
<h2>Dine tjenester</h2>
<div class="well">
<ul>
<li><span id="sso">Felles innlogging (DNS-konto)</span></li>
<li><span id="wireless_status">Trådløst nettverk</span></li>
<li><span id="client_status">Foreningsmaskiner</span></li>
</ul>
@@ -27,9 +28,8 @@
<h2>Dine grupper</h2>
<div class="well">
<ul>
{% if ldap_groups %}
{% if ldap_private_group %}<li>{{ ldap_private_group }}</li>{% endif %}
{% for group in ldap_groups %}
{% if groups %}
{% for group in groups %}
{% if group = "dns-aktiv" %}
<li title="Du har tilgang til foreningsmaskinene.">{{ group }}</li>
{% else %}
@@ -50,7 +50,7 @@
<script>
$(function() {
/* FIXME cleanup this mess */
var username = "{{username}}";
var username = "{{user.username}}";
/* Display the service status of the user */
var img_check = ' <img src="{{ STATIC_URL }}style/img/check.png" alt="OK" />';
var img_error = ' <img src="{{ STATIC_URL }}style/img/exclamation_mark.png" alt="ERROR" />';
@@ -61,11 +61,11 @@
$.getJSON("/userstatus/client/?format=json&username="+username, function(data) {
if(data.active) {
image = img_check;
title = 'Du har en bruker til foreningsmaskinene.';
title = 'Du har en bruker til foreningsmaskinene';
data_content = 'Siste vellykkede pålogging: ' + data.last_successful_auth;
} else {
image = img_error;
title = 'Ingen konto.';
title = 'Ingen konto';
data_content = 'Du mangler Kerberos principal.';
}
$("#client_status").append(image).prop('title', title).attr('data-content', data_content).popover();
@@ -73,15 +73,29 @@
$.getJSON("/userstatus/wireless/?format=json&username="+username, function(data) {
if(data.active) {
image = img_check;
title = 'Du har tilgang til trådløst nettverk.';
title = 'Du har tilgang til trådløst nettverk';
data_content = 'Siste vellykkede pålogging: ' + data.last_successful_auth;
} else {
image = img_error;
title = 'Ingen trådløskonto.';
data_content = 'Du har ikke en trådløskonto i Radius.' + data.last_successful_auth;
title = 'Ingen trådløskonto';
data_content = 'Du har ikke en trådløskonto i Radius.';
}
$("#wireless_status").append(image).prop('title', title).attr('data-content', data_content).popover();
});
$.getJSON("/ldap/users/"+username+"/")
.done(function(data) {
image = img_check;
title = 'Gyldig DNS-konto for aktive';
data_content = '';
$("#sso").append(image).prop('title', title).attr('data-content', data_content).popover();
})
.fail(function() {
image = img_error;
title = 'Ingen DNS-konto for aktive';
data_content = 'Kun medlemmer av gruppen dns-aktiv kan bruke felles innlogging.';
$("#sso").append(image).prop('title', title).attr('data-content', data_content).popover();
});
});
</script>
+11 -6
View File
@@ -38,6 +38,7 @@ INSTALLED_APPS = (
'django_extensions',
)
INSTALLED_APPS += (
'rest_framework',
'neuf_userinfo',
'neuf_ldap',
'neuf_radius',
@@ -196,18 +197,17 @@ AUTH_LDAP_E_USER_FLAGS_BY_GROUP = AUTH_LDAP_U_USER_FLAGS_BY_GROUP
AUTH_LDAP_E_USER_ATTR_MAP = AUTH_LDAP_U_USER_ATTR_MAP
AUTH_LDAP_E_ALWAYS_UPDATE_USER = AUTH_LDAP_U_ALWAYS_UPDATE_USER
# Debug logging
import logging
logger = logging.getLogger('django_auth_ldap')
logger.addHandler(logging.StreamHandler())
logger.setLevel(logging.DEBUG)
# Kerberos realm
KERBEROS_REALM = "NEUF.NO"
KERBEROS_PASSWORD_CHANGING_PRINCIPAL = 'brukerinfo'
KERBEROS_PASSWORD = ''
# Inside
INSIDE_USER_FLAGS_SYNC = {
"is_active": "dns-alle",
# "is_staff": "edb",
# "is_superuser": "edbadmin"
}
INSIDE_GROUPS_SYNC_DELETE = True # When user logs in, groups are synced (locally in Django), this deletes aswell
INSIDE_USERSYNC_API_KEY = ''
INSIDE_USERSYNC_ENC_KEY = ''
@@ -217,6 +217,11 @@ FILESERVER_HOST = "localhost"
FILESERVER_USER = 'nikolark' # change this to your own user for development
FILESERVER_HOME_PATH = "/tmp/homes"
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': ('rest_framework.permissions.IsAuthenticated',),
'PAGINATE_BY': 10
}
try:
from local_settings import *
except ImportError:
+2
View File
@@ -9,9 +9,11 @@ admin.autodiscover()
urlpatterns = patterns(
'',
url(r'', include('neuf_userinfo.urls')),
url(r'', include('neuf_ldap.urls')),
url(r'^userstatus/client/$', 'neuf_kerberos.views.client_status'),
url(r'^userstatus/wireless/$', 'neuf_radius.views.wireless_status'),
url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework')),
url(r'^admin/', include(admin.site.urls)),
)