diff --git a/.gitignore b/.gitignore index efc1dfb..db472e5 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ *.pyc .idea local_settings.py +build +*.sqlite3 diff --git a/account/cbase_members.py b/account/cbase_members.py new file mode 100644 index 0000000..eddbebd --- /dev/null +++ b/account/cbase_members.py @@ -0,0 +1,109 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import ldap +import copy + +from django.conf import settings + +""" +Example configuration: + +CBASE_LDAP_URL = 'ldap://lea.cbrp3.c-base.org:389/' +CBASE_BASE_DN = 'ou=crew,dc=c-base,dc=org' +""" + +def retrieve_member(request): + # TODO: Put password in encrypted session storage + return MemberValues(request.user.username, request.session['ldap_password']) + + +class MemberValues(object): + """ + Dictionary-like abstraction of the c-base member attributes. + """ + def __init__(self, username, password): + self._username = username + self._password = password + self._old = self._get_user_values() + + # Make a complete copy of the old values so we can later check + # which + self._new = copy.deepcopy(self._old) + + def get(self, key, default=None): + value = self._new.get(key, default)[0] + if value == 'TRUE': + return True + elif value == 'FALSE': + return False + else: + return value + + def set(self, key, value): + converted_value = value + if isinstance(value, bool): + if value == True: + converted_value = 'TRUE' + else: + converted_value = 'FALSE' + self._new[key] = [converted_value.encode('latin-1')] + + def save(self): + """ + Save the values back to the LDAP server. + """ + dn = "uid=%s,ou=crew,dc=c-base,dc=org" % self._username + print 'setting dn=', dn + + # TODO: Use settings for url + l = ldap.initialize("ldap://lea.cbrp3.c-base.org:389/") + l.simple_bind_s(dn, self._password) + + mod_attrs = [] + for new_key, new_value in self._new.items(): + # Replace is the default. + action = ldap.MOD_REPLACE + if new_key not in self._old.keys(): + action = ldap.MOD_ADD + mod_attrs.append((action, '%s' % new_key, new_value )) + continue + # Set the attribute and wait for the LDAP server to complete. + if self._old[new_key][0] != new_value[0]: + action = ldap.MOD_REPLACE + mod_attrs.append((action, '%s' % new_key, new_value )) + continue + + print "modattrs: ",mod_attrs + result = l.modify_s(dn, mod_attrs) + print "result is: ", result + + def _get_bind_dn(self): + """ + Adds the uid=userid, to the base dn and returns that. + """ + bind_dn = 'uid=%s,' % self._username + bind_dn += settings.CBASE_BASE_DN + return bind_dn + + def _get_user_values(self): + """ + Get an attribute from the ldap storage. + """ + # Create a new LDAP bind (aka connection or session) + session = ldap.initialize(settings.CBASE_LDAP_URL) + session.simple_bind_s(self._get_bind_dn(), self._password) + + # Set the attribute and wait for the LDAP server to complete. + searchScope = ldap.SCOPE_SUBTREE + + # retrieve all attributes + retrieveAttributes = None + searchFilter = "uid=%s" % self._username + + dn = settings.CBASE_BASE_DN + result = session.search_s(dn, searchScope, searchFilter, retrieveAttributes) + # TODO: latin1 + print "result is: ", result + # TODO: if len(result)==0 + return result[0][1] diff --git a/account/forms.py b/account/forms.py index 1eec70f..d51623d 100644 --- a/account/forms.py +++ b/account/forms.py @@ -1,6 +1,45 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import re + from django import forms +from django.utils.translation import ugettext as _ class LoginForm(forms.Form): username = forms.CharField(max_length=255) password = forms.CharField(max_length=255, widget=forms.PasswordInput) + + +class GastroPinField(forms.CharField): + def validate(self, value): + """ + Check if the value is all numeric and 4 - 6 chars long. + """ + match = re.match(r'^\d{4,6}$', value) + if not match: + raise forms.ValidationError(_('PIN must be 4 to 6 digits.')) + + +class GastroPinForm(forms.Form): + gastropin = GastroPinField() + + +class WlanPresenceForm(forms.Form): + # Boolean fields must never be required. + presence = forms.BooleanField(required=False, + help_text=_('Enable WiFi presence?')) + + +class PaswordForm(forms.Form): + password1 = forms.CharField(max_length=255, widget=forms.PasswordInput, + help_text=_('New password')) + password2 = forms.CharField(max_length=255, widget=forms.PasswordInput, + help_text=_('Repeat password')) + + +class RFIDForm(forms.Form): + rfid = forms.CharField(max_length=255, help_text=_('Your RFID')) + + diff --git a/account/templates/dashboard.html b/account/templates/dashboard.html index ff65e1c..87b9e79 100644 --- a/account/templates/dashboard.html +++ b/account/templates/dashboard.html @@ -1,7 +1,6 @@ {% extends "base.html" %} -{% block body %} -
+{% block container %}

{{ request.user.username }}

@@ -29,5 +28,4 @@ {% endfor %}
-
-{% endblock body %} +{% endblock container %} diff --git a/account/templates/form_base.html b/account/templates/form_base.html new file mode 100644 index 0000000..1000011 --- /dev/null +++ b/account/templates/form_base.html @@ -0,0 +1,18 @@ +{% extends "base.html" %} +{% load i18n %} + +{% block container %} +
+
+

{% block form_title %}{% endblock %}

+ {% block form_description %}{% endblock %} + + {% if message %} +
{{ message }}
+ {% endif %} + + {% block form_fields %}{% endblock form_fields %} + +
+
+{% endblock container %} diff --git a/account/templates/gastropin.html b/account/templates/gastropin.html new file mode 100644 index 0000000..b1480f1 --- /dev/null +++ b/account/templates/gastropin.html @@ -0,0 +1,20 @@ +{% extends "base.html" %} + +{% block body %} +
+
+
+

Gastro-Pin

+ {% if message %} +
{{ message }}
+ {% endif %} +
+ {{ form.non_field_errors }} + {% csrf_token %} + {{ form.as_p }} + +
+
+
+
+{% endblock %} diff --git a/account/templates/rfid.html b/account/templates/rfid.html new file mode 100644 index 0000000..40e4636 --- /dev/null +++ b/account/templates/rfid.html @@ -0,0 +1,23 @@ +{% extends "form_base.html" %} +{% load i18n %} +{% load crispy_forms_tags %} + +{% block form_title %}{% trans "RFID"%}{% endblock %} + +{% block form_description %} +

{% blocktrans %}Blabla testblab bla bla blub{% endblocktrans %}

+{% endblock %} + +{% block form_fields %} + {{ form.non_field_errors }} +
+ {% csrf_token %} + {{ form|crispy }} + +
+
+ +
+
+
+{% endblock form_fields %} \ No newline at end of file diff --git a/account/templates/wlan_presence.html b/account/templates/wlan_presence.html new file mode 100644 index 0000000..d26df1e --- /dev/null +++ b/account/templates/wlan_presence.html @@ -0,0 +1,25 @@ +{% extends "form_base.html" %} +{% load i18n %} +{% load crispy_forms_tags %} + +{% block form_title %}{% trans "WiFi Presence"%}{% endblock %} + +{% block form_description %} +

{% blocktrans %}The WiFi Presence automatically logs you in + to the c-base presence system when you connect a device to the Crew-Wifi + (SSID: c-base-crew) with your username and password.{% endblocktrans %}

+{% endblock %} + +{% block form_fields %} + {{ form.non_field_errors }} +
+ {% csrf_token %} + {{ form|crispy }} + +
+
+ +
+
+
+{% endblock form_fields %} diff --git a/account/urls.py b/account/urls.py index 6e24af4..0f3d722 100644 --- a/account/urls.py +++ b/account/urls.py @@ -3,6 +3,10 @@ from django.conf.urls import patterns, url urlpatterns = patterns( '', - url(r'^login/$', 'account.views.auth_login'), - url(r'^logout/$', 'account.views.auth_logout'), + url(r'^login/$', 'account.views.auth_login', name="auth_login"), + url(r'^logout/$', 'account.views.auth_logout', name="auth_logout"), + url(r'^gastropin/$', 'account.views.gastropin', name='gastropin'), + url(r'^wlan_presence/$', 'account.views.wlan_presence', name='wlan_presence'), + url(r'^rfid/$', 'account.views.rfid', name='rfid'), + url(r'^groups/(?P[^/]+)/', 'account.views.groups_list'), ) \ No newline at end of file diff --git a/account/views.py b/account/views.py index 9e36077..90f8c64 100644 --- a/account/views.py +++ b/account/views.py @@ -1,14 +1,22 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + from django.http import HttpResponseRedirect from django.shortcuts import render_to_response from django.template.context import RequestContext from django.contrib.auth import login, logout, authenticate from django.contrib.auth.models import User +from django.shortcuts import get_object_or_404 +from django.contrib.auth.decorators import login_required +from django.contrib.auth.models import Group +from django.shortcuts import render +from django.utils.translation import ugettext as _ -from account.forms import LoginForm - +from forms import GastroPinForm, WlanPresenceForm, LoginForm, PaswordForm, RFIDForm +from cbase_members import MemberValues, retrieve_member def auth_login(request): - redirect_to = request.REQUEST.get('next', '') or '/' + redirect_to = request.GET.get('next', '') or '/' if request.method == 'POST': form = LoginForm(request.POST) if form.is_valid(): @@ -18,20 +26,106 @@ def auth_login(request): if user is not None: if user.is_active: login(request, user) - member, created = User.objects.get_or_create( - username=username) + member, created = \ + User.objects.get_or_create(username=username) if created: member.save() - return HttpResponseRedirect(redirect_to) + # save password in the session for later use with LDAP + request.session['ldap_password'] = password + response = HttpResponseRedirect(redirect_to) + response.set_cookie('sessionkey', 'bla') + return response else: print 'user is none' else: form = LoginForm() - return render_to_response('login.html', - RequestContext(request, locals())) + return render_to_response('login.html', + RequestContext(request, locals())) def auth_logout(request): - redirect_to = request.REQUEST.get('next', '') or '/' + redirect_to = request.GET.get('next', '') or '/' logout(request) - return HttpResponseRedirect(redirect_to) + response = HttpResponseRedirect(redirect_to) + response.delete_cookie('sessionkey') + return response + +def landingpage(request): + is_ceymaster = is_admin = False + if 'ceymaster' in [g.name for g in request.user.groups.all()]: + is_ceymaster = True + if 'ldap_admins' in [g.name for g in request.user.groups.all()]: + is_admin = True + groups = Group.objects.all() + admins = Group.objects.get(name="ldap_admins").user_set.all() + if request.user.is_authenticated(): + # values = get_user_values(request.user.username, request.session['ldap_password']) + return render_to_response("dashboard.html", locals()) + return render_to_response("base.html", locals()) + +@login_required(redirect_field_name="/" ,login_url="/account/login/") +def groups_list(request, group_name): + group = get_object_or_404(Group, name=group_name) + groups = Group.objects.all() + if 'ceymaster' in [g.name for g in request.user.groups.all()]: + is_ceymaster = True + if 'ldap_admins' in [g.name for g in request.user.groups.all()]: + is_admin = True + return render_to_response("group_list.html", locals()) + +@login_required +def gastropin(request): + if request.method == 'POST': + form = GastroPinForm(request.POST) + if form.is_valid(): + user = request.user + user_profile = user.get_profile() + user_profile.gastropin = form.cleaned_data['gastropin'] + user_profile.save() + return render(request, 'gastropin.html', + {'message': _('Your Gastro-PIN was changed. Thank you!'), + 'form:': form}) + else: + return render(request, 'gastropin.html', {'form:': form}) + + else: + form = GastroPinForm() + + return render(request, 'gastropin.html', {'form': form}) + + +def set_ldap_field(request, form_type, field_names, template_name): + member = retrieve_member(request) + initial = {} + + if request.method == 'POST': + form = form_type(request.POST) + if form.is_valid(): + + for form_field, ldap_field in field_names: + member.set(ldap_field, form.cleaned_data[form_field]) + initial[form_field] = member.get(ldap_field) + member.save() + new_form = form_type(initial=initial) + return render(request, template_name, + {'message': _('Your changes have been saved. Thank you!'), + 'form': new_form}) + else: + return render(request, template_name, {'form:': form}) + else: + for form_field, ldap_field in field_names: + initial[form_field] = member.get(ldap_field) + form = form_type(initial=initial) + return render(request, template_name, {'form': form}) + + +@login_required +def wlan_presence(request): + return set_ldap_field(request, WlanPresenceForm, + [('presence', 'wlanPresence')], 'wlan_presence.html') + +@login_required +def rfid(request): + return set_ldap_field(request, RFIDForm, [('rfid', 'rfid')], 'rfid.html') + + diff --git a/cbmi/forms.py b/cbmi/forms.py new file mode 100644 index 0000000..e69de29 diff --git a/cbmi/local_settings.py.uk b/cbmi/local_settings.py.uk new file mode 100644 index 0000000..1ea4e09 --- /dev/null +++ b/cbmi/local_settings.py.uk @@ -0,0 +1,57 @@ +# Django settings for cbmi project. + +DEBUG = True +TEMPLATE_DEBUG = DEBUG + +ADMINS = ( + ('Brian Wiborg', 'baccenfutter@c-base.org') + # ('Your Name', 'your_email@example.com'), +) + +MANAGERS = ADMINS + +DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.sqlite3', + 'NAME': 'cbmi.sqlite3', + 'USER': '', + 'PASSWORD': '', + 'HOST': '', + 'PORT': '', + } +} + +# Absolute filesystem path to the directory that will hold user-uploaded files. +# Example: "/home/media/media.lawrence.com/media/" +MEDIA_ROOT = '' + +# URL that handles the media served from MEDIA_ROOT. Make sure to use a +# trailing slash. +# Examples: "http://media.lawrence.com/media/", "http://example.com/media/" +MEDIA_URL = '' + +# Absolute path to the directory static files should be collected to. +# Don't put anything in this directory yourself; store your static files +# in apps' "static/" subdirectories and in STATICFILES_DIRS. +# Example: "/home/media/media.lawrence.com/static/" +STATIC_ROOT = '' + +# URL prefix for static files. +# Example: "http://media.lawrence.com/static/" +STATIC_URL = '/static/' + +# Additional locations of static files +STATICFILES_DIRS = ( + # Put strings here, like "/home/html/static" or "C:/www/django/static". + # Always use forward slashes, even on Windows. + # Don't forget to use absolute paths, not relative paths. + '/home/smile/projects/cbmi/src/static', +) + +TEMPLATE_DIRS = ( + # Put strings here, like "/home/html/django_templates" or "C:/www/django/templates". + # Always use forward slashes, even on Windows. + # Don't forget to use absolute paths, not relative paths. +) + +AUTH_LDAP_START_TLS = False diff --git a/cbmi/settings.py b/cbmi/settings.py index f322292..94431ba 100644 --- a/cbmi/settings.py +++ b/cbmi/settings.py @@ -88,6 +88,16 @@ TEMPLATE_LOADERS = ( 'django.template.loaders.app_directories.Loader', # 'django.template.loaders.eggs.Loader', ) +TEMPLATE_CONTEXT_PROCESSORS = ( + "django.contrib.auth.context_processors.auth", + "django.core.context_processors.debug", + "django.core.context_processors.i18n", + "django.core.context_processors.media", + "django.core.context_processors.static", + "django.core.context_processors.tz", + "django.contrib.messages.context_processors.messages", + "django.core.context_processors.request" +) MIDDLEWARE_CLASSES = ( 'django.middleware.common.CommonMiddleware', @@ -166,6 +176,7 @@ INSTALLED_APPS = ( 'django.contrib.admin', 'django.contrib.admindocs', 'jsonrpc', + 'crispy_forms', 'cbmi', 'account', 'cbapi_ldap', @@ -200,6 +211,12 @@ LOGGING = { } } +CRISPY_TEMPLATE_PACK = 'bootstrap' + +# c-base specific settings +CBASE_LDAP_URL = 'ldap://lea.cbrp3.c-base.org:389/' +CBASE_BASE_DN = 'ou=crew,dc=c-base,dc=org' + try: from local_settings import * except ImportError, e: diff --git a/cbmi/templates/base.html b/cbmi/templates/base.html index 4f30d80..6cf6ce6 100644 --- a/cbmi/templates/base.html +++ b/cbmi/templates/base.html @@ -1,3 +1,4 @@ +{% load i18n %} @@ -63,6 +64,21 @@
{% block body %}
+ + {% block container %}
@@ -79,7 +95,8 @@
-
+ {% endblock container %} + {% endblock body %} diff --git a/cbmi/templates/group_list.html b/cbmi/templates/group_list.html index 9356146..0548fb2 100644 --- a/cbmi/templates/group_list.html +++ b/cbmi/templates/group_list.html @@ -1,7 +1,7 @@ {% extends "base.html" %} -{% block body %} -
+{% block container %} + BLA

members of {{ group.name }}

@@ -13,5 +13,4 @@ {% endfor %}
-
-{% endblock %} +{% endblock container %} diff --git a/cbmi/urls.py b/cbmi/urls.py index b0f696d..b6efca6 100644 --- a/cbmi/urls.py +++ b/cbmi/urls.py @@ -10,6 +10,7 @@ urlpatterns = patterns('', url(r'^cbapi/', include("cbapi_ldap.urls")), url(r'account/', include('account.urls')), - url(r'^groups/(?P[^/]+)/', 'cbmi.views.groups_list'), - url(r'^$', 'cbmi.views.landingpage') + + url(r'^$', 'cbmi.views.landingpage'), + ) diff --git a/cbmi/views.py b/cbmi/views.py index 8d9300d..2360549 100644 --- a/cbmi/views.py +++ b/cbmi/views.py @@ -1,27 +1,4 @@ -from django.shortcuts import render_to_response, get_object_or_404 -from django.contrib.auth.decorators import login_required -from django.contrib.auth.models import Group +#!/usr/bin/env python +# -*- coding: utf-8 -*- - -def landingpage(request): - is_ceymaster = is_admin = False - if 'ceymaster' in [g.name for g in request.user.groups.all()]: - is_ceymaster = True - if 'ldap_admins' in [g.name for g in request.user.groups.all()]: - is_admin = True - groups = Group.objects.all() - admins = Group.objects.get(name="ldap_admins").user_set.all() - if request.user.is_authenticated(): - return render_to_response("dashboard.html", locals()) - return render_to_response("base.html", locals()) - - -@login_required(redirect_field_name="/" ,login_url="/account/login/") -def groups_list(request, group_name): - group = get_object_or_404(Group, name=group_name) - groups = Group.objects.all() - if 'ceymaster' in [g.name for g in request.user.groups.all()]: - is_ceymaster = True - if 'ldap_admins' in [g.name for g in request.user.groups.all()]: - is_admin = True - return render_to_response("group_list.html", locals()) +# Create your views here \ No newline at end of file diff --git a/manage.py b/manage.py old mode 100644 new mode 100755 diff --git a/requirements.txt b/requirements.txt index 8063f0b..5a224e6 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ Django==1.4.2 MySQL-python==1.2.4 -django-auth-ldap==1.1.4 + django-auth-ldap==1.1.4 django-json-rpc==0.6.1