Archived
Profile now displays LDAP-stuff via AJAX, is_active flag, cleanups
This commit is contained in:
1 parent
2f745cf002
commit
7943fcfefb
14 files changed
+170
-86
No files matched your search
@@ -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
@@ -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
|
||||
@@ -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
@@ -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()
|
||||
|
||||
@@ -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',)
|
||||
@@ -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
@@ -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)
|
||||
@@ -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)
|
||||
@@ -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
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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)),
|
||||
)
|
||||
|
||||
|
||||
Reference in new issue
Block a user