Commit 0f8d1e77 authored by Leonidas Poulopoulos's avatar Leonidas Poulopoulos

Added managerial overview

Added a managerial overview for a group of users. Implemented an
alternate login mechanism based on LDAP. Will serve as a helpdesk
single point of reference for the app.
parent 8c663060
# encoding: utf-8
import datetime
from south.db import db
from south.v2 import DataMigration
from django.db import models
class Migration(DataMigration):
def forwards(self, orm):
"Write your forwards methods here."
ct, created = orm['contenttypes.ContentType'].objects.get_or_create(
model='userprofile', app_label='accounts') # model must be lowercase!
perm, created = orm['auth.permission'].objects.get_or_create(
content_type=ct, codename='overview', defaults=dict(name=u'Helpdesk/Managerial Overview'))
def backwards(self, orm):
"Write your backwards methods here."
models = {
'accounts.userprofile': {
'Meta': {'object_name': 'UserProfile'},
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'institution': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['edumanage.Institution']"}),
'user': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.User']", 'unique': 'True'})
},
'auth.group': {
'Meta': {'object_name': 'Group'},
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
},
'auth.message': {
'Meta': {'object_name': 'Message'},
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'message': ('django.db.models.fields.TextField', [], {}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'_message_set'", 'to': "orm['auth.User']"})
},
'auth.permission': {
'Meta': {'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
},
'auth.user': {
'Meta': {'object_name': 'User'},
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}),
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'})
},
'contenttypes.contenttype': {
'Meta': {'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
},
'edumanage.contact': {
'Meta': {'object_name': 'Contact'},
'email': ('django.db.models.fields.CharField', [], {'max_length': '80', 'db_column': "'contact_email'"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_column': "'contact_name'"}),
'phone': ('django.db.models.fields.CharField', [], {'max_length': '80', 'db_column': "'contact_phone'"})
},
'edumanage.institution': {
'Meta': {'object_name': 'Institution'},
'ertype': ('django.db.models.fields.PositiveIntegerField', [], {'max_length': '1', 'db_column': "'type'"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'realmid': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['edumanage.Realm']"})
},
'edumanage.name_i18n': {
'Meta': {'object_name': 'Name_i18n'},
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']", 'null': 'True', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'lang': ('django.db.models.fields.CharField', [], {'max_length': '5'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '80'}),
'object_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'})
},
'edumanage.realm': {
'Meta': {'object_name': 'Realm'},
'address_city': ('django.db.models.fields.CharField', [], {'max_length': '24'}),
'address_street': ('django.db.models.fields.CharField', [], {'max_length': '32'}),
'contact': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['edumanage.Contact']", 'symmetrical': 'False'}),
'country': ('django.db.models.fields.CharField', [], {'max_length': '2'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'stype': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0', 'max_length': '1'}),
'ts': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'})
},
'edumanage.url_i18n': {
'Meta': {'object_name': 'URL_i18n'},
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']", 'null': 'True', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'lang': ('django.db.models.fields.CharField', [], {'max_length': '5'}),
'object_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'blank': 'True'}),
'url': ('django.db.models.fields.CharField', [], {'max_length': '180', 'db_column': "'URL'"}),
'urltype': ('django.db.models.fields.CharField', [], {'max_length': '10', 'db_column': "'type'"})
}
}
complete_apps = ['contenttypes', 'auth', 'accounts']
......@@ -7,6 +7,11 @@ class UserProfile(models.Model):
user = models.OneToOneField(User)
institution = models.ForeignKey(Institution)
class Meta:
permissions = (
("overview", "Can see registered user and respective institutions"),
)
def __unicode__(self):
return "%s:%s" %(self.user.username, self.institution)
# -*- coding: utf-8 -*- vim:encoding=utf-8:
# vim: tabstop=4:shiftwidth=4:softtabstop=4:expandtab
import ldap
from django.contrib.auth.models import User, UserManager, Permission, Group
from django.conf import settings
class ldapBackend:
def authenticate(self, username=None, password=None):
ldap_settings = settings.LDAP_AUTH_SETTINGS
# Authenticate the base user so we can search
# Go through servers using their corresponding DNs
for ldap_setting in ldap_settings:
uri = ldap_setting['url']
base = ldap_setting['base']
try:
l = ldap.initialize(uri)
l.start_tls_s()
except ldap.LDAPError:
continue
else:
l.protocol_version = ldap.VERSION3
l.simple_bind_s()
myUser = self._auth_user(base, username, password, l)
if not myUser:
continue
return myUser
def _auth_user(self, base, username, password, l):
scope = ldap.SCOPE_SUBTREE
filter = "uid=" + username
ret = ['dn', 'mail', 'givenName', 'sn']
try:
result_id = l.search(base, scope, filter, ret)
result_type, result_data = l.result(result_id, 0)
# If the user does not exist in LDAP, Fail.
if (len(result_data) != 1):
return None
# We prevent a situation where binding could raise an exception with empty password
# Plus security...
if (len(password) == 0):
return None
# Attempt to bind to the user's DN
l.simple_bind_s(result_data[0][0], password)
# Checking to see if user has an e-mail
try:
mail = result_data[0][1]['mail'][0]
except:
mail = ''
# The user existed and authenticated. Get the user record
try:
user = User.objects.get(username__exact=username)
user.email = mail
user.first_name = result_data[0][1]['givenName'][0]
user.last_name = result_data[0][1]['sn'][0]
user.is_active = True
user.save()
# The user did not exist. Create one with no privileges
except:
user = User.objects.create_user(username, mail, None)
user.first_name = result_data[0][1]['givenName'][0]
user.last_name = result_data[0][1]['sn'][0]
user.is_staff = settings.LDAP_AUTH_IS_STAFF
user.is_superuser = False
user.is_active = True
if settings.LDAP_AUTH_GROUP:
try:
g = Group.objects.get(name=settings.LDAP_AUTH_GROUP)
user.groups.add(g)
user.save()
except:
pass
return user
except ldap.INVALID_CREDENTIALS:
return None
def get_user(self, user_id):
try:
return User.objects.get(pk=user_id)
except User.DoesNotExist:
return None
......@@ -914,6 +914,24 @@ def get_service_points(request):
else:
return HttpResponseNotFound('<h1>Something went really wrong</h1>')
@never_cache
def overview(request):
user = request.user
if user.is_authenticated():
if user.has_perm('accounts.overview'):
users = User.objects.all()
institutions = Institution.objects.all()
return render_to_response('overview/index.html', {'users': users, 'institutions': institutions},
context_instance=RequestContext(request))
else:
violation=True
return render_to_response('overview/index.html', {'violation': violation},
context_instance=RequestContext(request))
else:
return HttpResponseRedirect(reverse("altlogin"))
@never_cache
def get_all_services(request):
lang = request.LANGUAGE_CODE
......
......@@ -77,6 +77,7 @@ SECRET_KEY = 'x)gmuyo2h=l@tmpyh4b(-!gki%@u$-=3^@z+vf&&!ci7$*!+k9'
AUTHENTICATION_BACKENDS = (
'eduroam.djangobackends.shibauthBackend.shibauthBackend',
'django.contrib.auth.backends.ModelBackend',
'grnet.djangobackends.ldapBackend.ldapBackend',
)
......@@ -167,3 +168,13 @@ TINYMCE_DEFAULT_CONFIG = {
CACHE_BACKEND = 'memcached://127.0.0.1:11211/?timeout=5184000'
# Overview LDAP
LDAP_AUTH_SETTINGS = (
{'url': 'ldap://ds.example.com', 'base': 'dc=dept,dc=example,dc=com'},
)
# If defined as a string new users will belong in this group. Group must exist
LDAP_AUTH_GROUP = None
# Whether new users will have admin access
LDAP_AUTH_IS_STAFF = False
{% extends "base.html"%}
{% load i18n %}
{% load tolocale %}
{% load i18n %}
{% block extrahead %}
<script type="text/javascript" src="/static/js/jquery.dataTables.min.js"></script>
<script type="text/javascript" src="/static/js/datatables_bootstrap.js"></script>
<style>
.tab-content {
overflow: visible;
}
</style>
<script type="text/javascript">
$(document).ready(function(){
var oTableU = $('#usertable').dataTable({
"sPaginationType": "bootstrap",
"sDom": "<'row-fluid'<'span6'l><'span6'f>r>t<'row-fluid'<'span6'i><'span6'p>>",
"aoColumns": [{
"bSearchable": true,
"bSortable": true
}, {
"bSearchable": true,
"bSortable": true
}, {
"bSearchable": true,
"bSortable": true
}, {
"bSearchable": true,
"bSortable": true
}, {
"bSearchable": true,
"bSortable": true
}],
"aaSorting": [[0, 'desc']],
"iDisplayLength": 25,
"oSearch": {"bSmart": false, "bRegex":true},
"oLanguage": {
"sLengthMenu": '{% trans "Display" %} <select><option value="25">25</option><option value="50">50</option><option value="-1">{% trans "All" %}</option></select> {% trans "users" %}',
"sProcessing": "Processing...",
"sZeroRecords": '{% trans "No records to display" %}',
"sInfo": "Showing _START_ to _END_ of _TOTAL_ entries",
"sInfoEmpty": "{% trans "Showing 0 to 0 of 0 entries" %}",
"sInfoFiltered": "(filtered from _MAX_ total entries)",
"sInfoPostFix": "",
"sSearch": '{% trans "Search:" %}',
"sUrl": "",
"oPaginate": {
"sFirst": '{% trans "First" %}',
"sPrevious": '{% trans "Previous" %}',
"sNext": '{% trans "Next" %}',
"sLast": '{% trans "Last" %}'
}
}
});
oTableU.fnDraw();
var oTableI = $('#insttable').dataTable({
"sPaginationType": "bootstrap",
"sDom": "<'row-fluid'<'span6'l><'span6'f>r>t<'row-fluid'<'span6'i><'span6'p>>",
"aoColumns": [{
"bSearchable": true,
"bSortable": true
}, {
"bSearchable": true,
"bSortable": true
}, {
"bSearchable": true,
"bSortable": true
}, {
"bSearchable": true,
"bSortable": true
}, {
"bSearchable": true,
"bSortable": true
}],
"aaSorting": [[0, 'desc']],
"iDisplayLength": 25,
"oSearch": {"bSmart": false, "bRegex":true},
"oLanguage": {
"sLengthMenu": '{% trans "Display" %} <select><option value="25">25</option><option value="50">50</option><option value="-1">{% trans "All" %}</option></select> {% trans "institutions" %}',
"sProcessing": "Processing...",
"sZeroRecords": '{% trans "No records to display" %}',
"sInfo": "Showing _START_ to _END_ of _TOTAL_ entries",
"sInfoEmpty": "{% trans "Showing 0 to 0 of 0 entries" %}",
"sInfoFiltered": "(filtered from _MAX_ total entries)",
"sInfoPostFix": "",
"sSearch": '{% trans "Search:" %}',
"sUrl": "",
"oPaginate": {
"sFirst": '{% trans "First" %}',
"sPrevious": '{% trans "Previous" %}',
"sNext": '{% trans "Next" %}',
"sLast": '{% trans "Last" %}'
}
}
});
oTableI.fnDraw();
});
</script>
{% endblock %}
{% block content %}
<div class="container-fluid">
<div class="row-fluid">
<div class="span2"></div>
<div class="span10">
<div class="row-fluid">
<div class="span12">
<ul class="breadcrumb" style="display: inline-table; padding: 0">
{% block crumbs %}
<li class="active">{% trans "Overview" %}</li>
{% endblock %}
</ul>
<p class="pull-right"><strong>{{user}}</strong> (<a href="{% url logout %}">{% trans "Logout" %}</a>)</p>
</div></div>
</div>
</div>
<div class="row-fluid">
<div class="span2">
<div class="well sidebar-nav">
<ul class="nav nav-list">
<li {% block homeactive %}class="active"{% endblock %}>
<a href="{% url overview %}">{% trans "Overview" %}</a>
</li>
<li>
<a href="{% url logout %}">{% trans "Logout" %}</a>
</li>
</ul>
</div><!--/.well -->
</div><!--/span-->
<div class="span10">
<div class="row-fluid">
<!--/span-->
<div class="span12">
{% if violation %}
<table class="table table-condensed">
<tr class="error"><td>You have no rights to access this area. Please contact your administrator<td></tr>
</table>
{% else %}
{% block subcontent %}
<h4>{% trans "Users - Institutions" %}</h4>
<hr>
{% load tolocale %}
<ul class="nav nav-tabs">
<li class="active"><a href="#users" data-toggle="tab">{% trans "Users" %}</a></li>
<li><a href="#institutions" data-toggle="tab">{% trans "Institutions" %}</a></li>
</ul>
<div class="tab-content">
<div class="tab-pane active" id="users">
{% if users %}
<table cellpadding="0" cellspacing="0" border="0" class="table table-striped table-bordered" width="100%" id="usertable">
<thead>
<tr>
<th>{% trans "Username" %}</th>
<th>{% trans "Email" %}</th>
<th>{% trans "Name" %}</th>
<th>{% trans "Status" %}</th>
<th>{% trans "Institution" %}</th>
</tr>
</thead>
<tbody>
{% for user in users %}
<tr class="GradeC">
<td>{{user.username}}</td>
<td>{{user.email}}</td>
<td>{{user.first_name}} {{user.last_name}}</td>
<td>{% if user.registrationprofile_set.all %}{% if user.registrationprofile_set.all.0.activation_key == "ALREADY_ACTIVATED" %}{% trans "Active" %}{% else %}{% trans "Inactive" %} or {% trans "Key Expired" %}{% endif %}{% else %}-{% endif %}</td>
<td>{% if user.get_profile.institution %}{% tolocale user.get_profile.institution LANGUAGE_CODE %}{% endif %}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endif %}
</div>
<div class="tab-pane" id="institutions">
{% if institutions %}
<table cellpadding="0" cellspacing="0" border="0" class="table table-striped table-bordered" width="100%" id="insttable">
<thead>
<tr>
<th>{% trans "Name" %}</th>
<th>{% trans "Type" %}</th>
<th>{% trans "Contacts" %}</th>
<th>{% trans "Address" %}</th>
<th>{% trans "Admin Users" %}</th>
</tr>
</thead>
<tbody>
{% for institution in institutions %}
<tr class="GradeC">
<td>{% tolocale institution LANGUAGE_CODE %}</td>
<td>{{institution.get_ertype_display}}</td>
<td>{% if institution.institutiondetails.contact.all %}{% for contact in institution.institutiondetails.contact.all %}{{contact}}<br>{% endfor %}{% endif %}</td>
<td>{{institution.institutiondetails.address_street}}<br>{{institution.institutiondetails.address_city}}</td>
<td>{% if institution.userprofile_set.all %}{% for admuser in institution.userprofile_set.all %}{{admuser.user}}<br>{% endfor %}{% endif %}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endif %}
</div>
</div>
{% endblock %}
{% endif %}
</div><!--/span-->
<!--/span-->
</div><!--/row-->
</div><!--/span-->
</div><!--/row-->
</div><!--/.fluid-container-->
{% endblock %}
\ No newline at end of file
{% extends "base.html" %} {% block content %}
<div class="container-fluid">
<div class="row-fluid">
<div class="span4"></div>
<div class="span4">
<div class="well">
<div class="row-fluid">
<div class="span12"><h5>Login</h5></div>
<div class="span12"><hr></div>
<hr>
</div>
<div class="row-fluid">
<div class="span12">
<form class="form-horizontal" method="post"
action="{% url altlogin %}">
{% if form.non_field_errors %} {% for err in form.non_field_errors %}
<div class="control-group error ">
<div class="controls error">
<span style="color: #B94A48;">{{err}}</span>
</div>
</div>
{% endfor %} {% endif %} {% csrf_token %}
<div
class="control-group {% if form.username.errors %} error {% endif %}">
<label class="control-label" for="id_username">Username</label>
<div class="controls">
{{ form.username }} {% if form.username.errors %} <span
class="help-inline"> {{ form.username.errors|join:", "}} </span> {% endif %}
</div>
</div>
<div
class="control-group {% if form.password.errors %} error {% endif %}">
<label class="control-label" for="id_password">Password</label>
<div class="controls">
{{ form.password }} {% if form.password.errors %} <span
class="help-inline"> {{ form.password.errors|join:", "}} </span> {% endif %}
</div>
</div>
<div class="control-group">
<div class="controls">
<button type="submit" class="btn">Sign in</button>
<input type="hidden" name="next" value="{% url overview %}" />
</div>
</div>
</form>
</div>
</div>
</div>
</div>
<div class="span4"></div>
</div>
</div>
{% endblock %}
......@@ -15,6 +15,7 @@ urlpatterns = patterns('',
# Uncomment the next line to enable the admin:
(r'^admin/', include(admin.site.urls)),
url(r'^login/?', 'eduroam.edumanage.views.user_login', name="login"),
url(r'^altlogin/?', 'django.contrib.auth.views.login', {'template_name': 'overview/login.html'}, name="altlogin"),
url(r'^logout/?', 'django.contrib.auth.views.logout', {'next_page': '/'}, name="logout"),
url(r'^registration/accounts/activate/(?P<activation_key>\w+)/$', 'accounts.views.activate', name='activate_account'),
......@@ -73,7 +74,9 @@ urlpatterns += patterns('edumanage.views',
url(r'^manage/monlocauthpar/edit/(?P<instrealmmon_pk>\d+)/(?P<monlocauthpar_pk>\d+)?$', 'add_monlocauthpar', name="edit-monlocauthpar"),
url(r'^manage/monlocauthpar/del/?$', 'del_monlocauthpar', name="del-monlocauthpar"),
url(r'^overview/?$', 'overview', name="overview"),
)
if settings.DEBUG:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment