diff --git a/.gitignore b/.gitignore index 99ec428..db472e5 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,5 @@ *.pyc .idea +local_settings.py +build +*.sqlite3 diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..5fc6723 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "static/c-base-bootstrap"] + path = static/c-base-bootstrap + url = git://dev.c-base.org/c-portal/c-portal-bootstrap.git diff --git a/account/cbase_members.py b/account/cbase_members.py new file mode 100644 index 0000000..47dcbfa --- /dev/null +++ b/account/cbase_members.py @@ -0,0 +1,132 @@ +#!/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_list = self._new.get(key, default) + if value_list: + value = value_list[0] + else: + value = default + + # Decode + 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 + + l = ldap.initialize(settings.CBASE_LDAP_URL) + 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 + l.unbind_s() + + def change_password(self, new_password): + """ + Change the password of the member. + You do not need to call save() after calling change_password(). + """ + l = ldap.initialize(settings.CBASE_LDAP_URL) + user_dn = self._get_bind_dn() + l.simple_bind_s(user_dn, self._password) + l.passwd_s(user_dn, self._password, new_password) + l.unbind_s() + + def to_dict(self): + result = {} + for key, value in self._new.items(): + result[key] = self.get(key) + return 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] + session.unbind_s() diff --git a/account/conf/locale/de/LC_MESSAGES/django.mo b/account/conf/locale/de/LC_MESSAGES/django.mo new file mode 100644 index 0000000..4613a95 Binary files /dev/null and b/account/conf/locale/de/LC_MESSAGES/django.mo differ diff --git a/account/conf/locale/de/LC_MESSAGES/django.po b/account/conf/locale/de/LC_MESSAGES/django.po new file mode 100644 index 0000000..536e444 --- /dev/null +++ b/account/conf/locale/de/LC_MESSAGES/django.po @@ -0,0 +1,260 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2013-10-25 20:39+0200\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: forms.py:20 +msgid "Sorry, that login was invalid. Please try again." +msgstr "" + +#: forms.py:39 +msgid "PIN must be 4 to 6 digits." +msgstr "Die PIN muss 4 - 6 Ziffern enthalten." + +#: forms.py:43 +msgid "New Gastro-PIN" +msgstr "Neue Gastro-PIN" + +#: forms.py:44 +msgid "Repeat Gastro-PIN" +msgstr "Gastro-PIN wiederholen" + +#: forms.py:52 forms.py:86 +msgid "The PINs entered were not identical." +msgstr "Die beiden eingegeben PINs waren nicht identisch." + +#: forms.py:60 +msgid "Enable WiFi presence" +msgstr "WLAN-Presence einschalten" + +#: forms.py:65 +msgid "New password" +msgstr "Neues Passwort" + +#: forms.py:67 +msgid "Repeat password" +msgstr "Passwort wiederholen" + +#: forms.py:71 +msgid "Your RFID" +msgstr "Deine RFID" + +#: forms.py:72 +msgid "" +"Find out your RFID by holding your RFID tag to the reader in the airlock." +msgstr "Du kannst deine RFID herausfinden, in dem du eine nicht registrierte RFID-Karte an die Schleuse hältst." + +#: forms.py:77 +msgid "Your SIP PIN" +msgstr "Deine SIP-PIN" + +#: forms.py:78 +msgid "Repeat SIP PIN" +msgstr "SIP-PIN wiederholen" + +#: forms.py:92 +msgid "NRF24-ID" +msgstr "NRF24-ID" + +#: forms.py:93 +msgid "Your r0ket's NRF24 identification" +msgstr "Die NRF24-ID deiner r0ket" + +#: forms.py:97 +msgid "New c-lab PIN" +msgstr "Neue c-lab-PIN" + +#: forms.py:98 +msgid "Repeat c-lab PIN" +msgstr "c-lab-PIN wiederholen" + +#: forms.py:99 +msgid "Numerical only, 4 to 6 digits" +msgstr "Nur Zahlen, 4 bis 6 Ziffern lang" + +#: views.py:119 +msgid "Your changes have been saved. Thank you!" +msgstr "Deine Änderungen wurden gespeichert. Vielen Dank!" + +#: templates/clabpin.html:5 +msgid "c-lab PIN" +msgstr "c-lab-PIN" + +#: templates/clabpin.html:8 +msgid "Change your c-lab PIN to access the c-lab in the basement." +msgstr "PIN ändern für den Zugang um c-lab im Keller." + +#: templates/clabpin.html:18 templates/gastropin.html:23 +#: templates/nrf24.html:19 templates/password.html:19 templates/sippin.html:18 +msgid "Save" +msgstr "Speichern" + +#: templates/gastropin.html:5 +msgid "Gastro PIN" +msgstr "Gastro-PIN" + +#: templates/gastropin.html:9 +msgid "" +"Change your Gastro PIN to access the vending machines.\n" +" To use unlock member prices, press the A button then " +"enter\n" +" your numeric user ID" +msgstr "" + +#: templates/gastropin.html:13 +msgid "and your Gastro PIN." +msgstr "" + +#: templates/login.html:20 +msgid "Login" +msgstr "" + +#: templates/member_base.html:9 +msgid "Home" +msgstr "" + +#: templates/member_base.html:13 templates/password.html:5 +msgid "Password" +msgstr "" + +#: templates/member_base.html:17 +msgid "Gastro-PIN" +msgstr "" + +#: templates/member_base.html:21 +msgid "WiFi presence" +msgstr "WLAN-Presence" + +#: templates/member_base.html:25 templates/rfid.html:5 +msgid "RFID" +msgstr "" + +#: templates/member_base.html:29 +msgid "c-lab-PIN" +msgstr "" + +#: templates/member_base.html:33 templates/nrf24.html:5 +msgid "NRF24" +msgstr "" + +#: templates/member_base.html:37 +msgid "SIP-PIN" +msgstr "" + +#: templates/nrf24.html:8 +msgid "" +"The NRF24 identification is used to interface with the\n" +" CCC's r0ket badge." +msgstr "" + +#: templates/password.html:8 +msgid "You can change your password here." +msgstr "" + +#: templates/rfid.html:8 +msgid "" +"A Radio Frequency Identification (RFID) tag can be used\n" +" to announce your presence to other c-base members when you arrive.\n" +" Place your RFID tag on the RFID reader in the airlock terminal.\n" +" If you configured your RFID correctly, the airlock terminal will greet\n" +" you and show additional information. If you place your RFID tag in the\n" +" reader again when leaving, you will be logged out." +msgstr "" + +#: templates/sippin.html:5 +msgid "SIP PIN" +msgstr "" + +#: templates/sippin.html:8 +msgid "" +"The SIP PIN is your access to the c-base SIP server for voice telephony." +msgstr "" + +#: templates/start.html:7 +msgid "Welcome to the c-base member interface" +msgstr "" + +#: templates/start.html:8 +msgid "" +"Here you can change\n" +" some parameters of your c-base member account." +msgstr "" + +#: templates/start.html:11 +msgid "Basic information about your account" +msgstr "" + +#: templates/start.html:15 +msgid "Your user ID:" +msgstr "" + +#: templates/start.html:17 +msgid "Numeric user ID:" +msgstr "" + +#: templates/start.html:19 +msgid "" +"The numeric user ID can be used to login with\n" +" the vending machines and network connected combination\n" +" locks." +msgstr "" + +#: templates/start.html:23 +msgid "Your c-base e-mail address:" +msgstr "" + +#: templates/start.html:28 +msgid "Management information" +msgstr "" + +#: templates/start.html:31 +msgid "Name:" +msgstr "" + +#: templates/start.html:35 +msgid "External e-mail address:" +msgstr "" + +#: templates/start.html:37 +msgid "" +"The external e-mail address is used by the\n" +" board of c-base to reach you in cases where your c-base\n" +" address (see above) is not working. To change your\n" +" externe e-mail address please contact the c-base board\n" +" (vorstand@c-base." +"org)." +msgstr "" + +#: templates/start.html:44 +msgid "Your c-base member account balance:" +msgstr "" + +#: templates/start.html:45 +msgid "Check balance" +msgstr "" + +#: templates/wlan_presence.html:5 +msgid "WiFi Presence" +msgstr "" + +#: templates/wlan_presence.html:8 +msgid "" +"The WiFi Presence automatically logs you in\n" +" to the c-base presence system when you connect a device to the Crew-" +"Wifi\n" +" (SSID: c-base-crew) with your username and password." +msgstr "" diff --git a/account/forms.py b/account/forms.py index 1eec70f..01b5f24 100644 --- a/account/forms.py +++ b/account/forms.py @@ -1,6 +1,124 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import re + from django import forms +from django.contrib.auth import authenticate +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) + + def clean(self): + username = self.cleaned_data.get('username') + password = self.cleaned_data.get('password') + user = authenticate(username=username, password=password) + if not user or not user.is_active: + raise forms.ValidationError(_('Sorry, that login was invalid. ' + 'Please try again.'), code='invalid_login') + return self.cleaned_data + + def login(self, request): + username = self.cleaned_data.get('username') + password = self.cleaned_data.get('password') + user = authenticate(username=username, password=password) + return user + + +class GastroPinField(forms.CharField): + widget = forms.PasswordInput + 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): + gastropin1 = GastroPinField(label=_('New Gastro-PIN')) + gastropin2 = GastroPinField(label=_('Repeat Gastro-PIN')) + + def clean(self): + cleaned_data = super(GastroPinForm, self).clean() + gastropin1 = cleaned_data.get("gastropin1") + gastropin2 = cleaned_data.get("gastropin2") + if gastropin1 != gastropin2: + raise forms.ValidationError( + _('The PINs entered were not identical.'), + code='not_identical') + return cleaned_data + + +class WlanPresenceForm(forms.Form): + # Boolean fields must never be required. + presence = forms.BooleanField(required=False, + label=_('Enable WiFi presence')) + + +class PasswordForm(forms.Form): + old_password = forms.CharField(max_length=255, widget=forms.PasswordInput, + label=_('Old password'), + help_text=_('Enter your current password here.')) + password1 = forms.CharField(max_length=255, widget=forms.PasswordInput, + label=_('New password')) + password2 = forms.CharField(max_length=255, widget=forms.PasswordInput, + label=_('Repeat password')) + + def __init__(self, *args, **kwargs): + self._request = kwargs.pop('request', None) + super(PasswordForm, self).__init__(*args, **kwargs) + + def clean(self): + cleaned_data = super(PasswordForm, self).clean() + old_password = cleaned_data.get('old_password') + username = self._request.user.username + user = authenticate(username=username, password=old_password) + + if not user or not user.is_active: + raise forms.ValidationError(_('The old password was incorrect.'), + code='old_password_wrong') + + password1 = cleaned_data.get('password1') + password2 = cleaned_data.get('password2') + if password1 != password2: + raise forms.ValidationError( + _('The new passwords were not identical.'), + code='not_identical') + return cleaned_data + + +class RFIDForm(forms.Form): + rfid = forms.CharField(max_length=255, label=_('Your RFID'), + help_text=_('Find out your RFID by holding your RFID tag to the ' + 'reader in the airlock.')) + + +class SIPPinForm(forms.Form): + sippin1 = GastroPinField(label=_('Your SIP PIN')) + sippin2 = GastroPinField(label=_('Repeat SIP PIN')) + + def clean(self): + cleaned_data = super(SIPPinForm, self).clean() + sippin1 = cleaned_data.get("sippin1") + sippin2 = cleaned_data.get("sippin2") + if sippin1 != sippin2: + raise forms.ValidationError( + _('The PINs entered were not identical.'), + code='not_identical') + + +class NRF24Form(forms.Form): + nrf24 = forms.CharField(max_length=255, + label = _('NRF24-ID'), + help_text=_("Your r0ket's NRF24 identification")) + + +class CLabPinForm(forms.Form): + c_lab_pin1 = GastroPinField(label=_('New c-lab PIN')) + c_lab_pin2 = GastroPinField(label=_('Repeat c-lab PIN'), + help_text=_('Numerical only, 4 to 6 digits')) \ No newline at end of file diff --git a/cbmi/templates/base.html b/account/templates/base.html similarity index 52% rename from cbmi/templates/base.html rename to account/templates/base.html index ce4d198..32af654 100644 --- a/cbmi/templates/base.html +++ b/account/templates/base.html @@ -1,3 +1,6 @@ +{% load i18n %} +{% load crispy_forms_tags %} + @@ -13,12 +16,22 @@ - - + + + + {% trans "member interface" %} + @@ -63,14 +81,18 @@
{% block body %}
+ {% block container %}
-
+

c-base member-interface

- Auf dieser Seite kann jedes Member seine LDAP-Daten editieren und u.A. sein Passwort neu setzen. + {% blocktrans %}On this website any member can view and + edit their data stored in the LDAP directory of + c-base.{% endblocktrans %}
- neumember können sich ihr initiales passwort mit einem der folgenden member setzen: + {% blocktrans %}If you are a new member and don't have a + password yet, please contact one of the following people:{% endblocktrans %}
    {% for admin in admins %}
  • {{ admin}}
  • @@ -78,12 +100,32 @@
+
+
+

Login

+
+ {% csrf_token %} + {{ form|crispy }} +
+
+ +
+
+
+
+
+ {% endblock container %} +
+
+ Copyright © 2013 by c-base e.V.
+
{% endblock body %} - - - + + + diff --git a/account/templates/clabpin.html b/account/templates/clabpin.html new file mode 100644 index 0000000..0525404 --- /dev/null +++ b/account/templates/clabpin.html @@ -0,0 +1,22 @@ +{% extends "form_base.html" %} +{% load i18n %} +{% load crispy_forms_tags %} + +{% block form_title %}{% trans "c-lab PIN"%}{% endblock %} + +{% block form_description %} +

{% blocktrans %}Change your c-lab PIN to access the c-lab in the basement.{% endblocktrans %}

+{% endblock %} + +{% block form_fields %} +
+ {% csrf_token %} + {{ form|crispy }} + +
+
+ +
+
+
+{% endblock form_fields %} \ No newline at end of file diff --git a/account/templates/dashboard.html b/account/templates/dashboard.html index a0cf778..87b9e79 100644 --- a/account/templates/dashboard.html +++ b/account/templates/dashboard.html @@ -1,32 +1,31 @@ {% extends "base.html" %} -{% block body %} -
+{% block container %}
-

{{ request.user.username }}

+

{{ request.user.username }}

{% for g in request.user.groups.all %} - {% if g.name == "ceymaster" %} - - - - {% elif "cey" in g.name %} - - - - {% elif g.name == "ldap_admins" %} - - - - {% else %} - - - + {% if g.name == "ceymaster" %} + + {{ g }} + + {% elif "cey" in g.name %} + + {{ g }} + + {% elif g.name == "ldap_admins" %} + + {{ g }} + + {% else %} + + {{ g }} + {% endif %} +
{% empty %} no groups... {% 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..c10f3fc --- /dev/null +++ b/account/templates/form_base.html @@ -0,0 +1,18 @@ +{% extends "member_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..593f7fb --- /dev/null +++ b/account/templates/gastropin.html @@ -0,0 +1,27 @@ +{% extends "form_base.html" %} +{% load i18n %} +{% load crispy_forms_tags %} + +{% block form_title %}{% trans "Gastro PIN"%}{% endblock %} + +{% block form_description %} + +

{% blocktrans %}Change your Gastro PIN to access the vending machines. + To use unlock member prices, press the A button then enter + your numeric user ID{% endblocktrans %} + {{ member.uidNumber }} + {% blocktrans %}and your Gastro PIN.{% endblocktrans %}

+{% endblock %} + +{% block form_fields %} +
+ {% csrf_token %} + {{ form|crispy }} + +
+
+ +
+
+
+{% endblock form_fields %} \ No newline at end of file diff --git a/cbmi/templates/group_list.html b/account/templates/group_list.html similarity index 87% rename from cbmi/templates/group_list.html rename to account/templates/group_list.html index 9356146..0548fb2 100644 --- a/cbmi/templates/group_list.html +++ b/account/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/account/templates/home.html b/account/templates/home.html new file mode 100644 index 0000000..f8f2aaa --- /dev/null +++ b/account/templates/home.html @@ -0,0 +1,59 @@ +{% extends "member_base.html" %} +{% load i18n %} + +{% block container %} +
+
+

{% trans "Welcome to the c-base member interface" %}

+

{% blocktrans %}Here you can change + some parameters of your c-base member account.{% endblocktrans %}

+ +

{% trans "Basic information about your account" %}

+ +
    + +
  • {% trans "Your user ID:" %} + {{ member.uid }}
  • +
  • {% trans "Numeric user ID:" %} + {{ member.uidNumber }}
    + {% blocktrans %}The numeric user ID can be used to login with + the vending machines and network connected combination + locks.{% endblocktrans %}
  • +
  • + {% trans "Your c-base e-mail address:" %} + {{ member.mail }} +
  • +
+ +

{% trans "Your group memberships" %}

+ {% trans "You are part of the following LDAP groups:" %} +
    + {% for group in groups %} +
  • {{ group }}
  • + {% endfor %} +
+ +

{% trans "Management information" %}

+ +
    +
  • {% trans "Name:" %} + {{ member.displayName }} +
  • +
  • + {% trans "External e-mail address:" %} + {{ member.externalEmail }}
    + {% blocktrans %}The external e-mail address is used by the + board of c-base to reach you in cases where your c-base + address (see above) is not working. To change your + externe e-mail address please contact the c-base board + (vorstand@c-base.org).{% endblocktrans %} +
  • +
  • + {% trans "Your c-base member account balance:" %} + {% trans "Check balance" %} +
  • +
+ +
+
+{% endblock container %} \ No newline at end of file diff --git a/account/templates/login.html b/account/templates/login.html index 8d90e55..0315f96 100644 --- a/account/templates/login.html +++ b/account/templates/login.html @@ -1,4 +1,6 @@ {% extends "base.html" %} +{% load i18n %} +{% load crispy_forms_tags %} {% block body %}
@@ -7,13 +9,23 @@
-
-
+
+

Login

+ + {% csrf_token %} - {{ form.as_p }} - + {{ form|crispy }} +
+
+ +
+
+
+
+ Copyright © 2013 by c-base e.V. +
{% endblock body %} diff --git a/account/templates/member_base.html b/account/templates/member_base.html new file mode 100644 index 0000000..67bfd7d --- /dev/null +++ b/account/templates/member_base.html @@ -0,0 +1,47 @@ +{% extends "base.html" %} +{% load i18n %} + +{% block body %} +
+ + {% block container %}{% endblock container %} + +
+
+ Copyright © 2013 by c-base e.V. +
+
+{% endblock body %} diff --git a/account/templates/nrf24.html b/account/templates/nrf24.html new file mode 100644 index 0000000..3f1617d --- /dev/null +++ b/account/templates/nrf24.html @@ -0,0 +1,23 @@ +{% extends "form_base.html" %} +{% load i18n %} +{% load crispy_forms_tags %} + +{% block form_title %}{% trans "NRF24"%}{% endblock %} + +{% block form_description %} +

{% blocktrans %}The NRF24 identification is used to interface with the + CCC's r0ket badge.{% endblocktrans %}

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

{% blocktrans %}You can change your password here.{% endblocktrans %}

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

{% blocktrans %}A Radio Frequency Identification (RFID) tag can be used + to announce your presence to other c-base members when you arrive. + Place your RFID tag on the RFID reader in the airlock terminal. + If you configured your RFID correctly, the airlock terminal will greet + you and show additional information. If you place your RFID tag in the + reader again when leaving, you will be logged out.{% 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/sippin.html b/account/templates/sippin.html new file mode 100644 index 0000000..2ff7d29 --- /dev/null +++ b/account/templates/sippin.html @@ -0,0 +1,22 @@ +{% extends "form_base.html" %} +{% load i18n %} +{% load crispy_forms_tags %} + +{% block form_title %}{% trans "SIP PIN"%}{% endblock %} + +{% block form_description %} +

{% blocktrans %}The SIP PIN is your access to the c-base SIP server for voice telephony.{% endblocktrans %}

+{% endblock %} + +{% block form_fields %} +
+ {% 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..cb721bd 100644 --- a/account/urls.py +++ b/account/urls.py @@ -3,6 +3,15 @@ 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="cbase_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'^nrf24/$', 'account.views.nrf24', name='nrf24'), + url(r'^password/$', 'account.views.password', name='password'), + url(r'^sippin/$', 'account.views.sippin', name='sippin'), + url(r'^clabpin/$', 'account.views.clabpin', name='clabpin'), + url(r'^$', 'account.views.home', name="home"), + 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..f8b8f5e 100644 --- a/account/views.py +++ b/account/views.py @@ -1,37 +1,220 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import os +import base64 +import hashlib + +from django.conf import settings 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, PasswordForm, \ + RFIDForm, NRF24Form, SIPPinForm, CLabPinForm +from cbase_members import retrieve_member +def landingpage(request): + if request.user.is_authenticated(): + return HttpResponseRedirect('/account') + form = LoginForm() + 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() + + # values = get_user_values(request.user.username, request.session['ldap_password']) + #return render_to_response("dashboard.html", locals()) + return render(request, 'base.html', {'form': form, 'admins': admins}) 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(): username = form.cleaned_data['username'] password = form.cleaned_data['password'] - user = authenticate(username=username, password=password) + user = form.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 + # TODO: Change the + + response = HttpResponseRedirect(redirect_to) + response.set_cookie('sessionkey', 'bla') + return response else: - print 'user is none' + return render(request, 'login.html', {'form': form}) else: form = LoginForm() + return render_to_response('login.html', - RequestContext(request, locals())) + RequestContext(request, locals())) +@login_required +def home(request): + member = retrieve_member(request) + context = {'member': member.to_dict(), 'groups': request.user.groups.all()} + return render(request, 'home.html', context) +@login_required 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 + +@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 sippin(request): + return set_ldap_field(request, SIPPinForm, [('sippin', 'sippin')], + 'sippin.html') + +def set_hash_field(request, form_type, in_field, out_field, hash_func, + template_name): + """ + Abstract view for changing LDAP attributes that need to be hashed. + Takes a function that converts the value into the hashed_value. + """ + member = retrieve_member(request) + initial = {} + + if request.method == 'POST': + form = form_type(request.POST) + if form.is_valid(): + hashed_value = hash_func(form.cleaned_data[in_field]) + print 'hashed value: ', hashed_value + member.set(out_field, hashed_value) + member.save() + new_form = form_type(initial=initial) + return render(request, template_name, + {'message': _('Your changes have been saved. Thank you!'), + 'form': new_form, 'member': member.to_dict()}) + else: + return render(request, template_name, + {'form': form, 'member': member.to_dict()}) + else: + form = form_type(initial=initial) + return render(request, template_name, + {'form': form, 'member': member.to_dict()}) + +@login_required +def gastropin(request): + def calculate_gastro_hash(pin): + key = settings.CBASE_GASTRO_KEY + bla = '%s%s' % (key, pin) + return hashlib.sha256(bla).hexdigest() + + return set_hash_field(request, GastroPinForm, + 'gastropin1', 'gastroPIN', calculate_gastro_hash, 'gastropin.html') + +@login_required +def clabpin(request): + def calculate_clab_hash(pin): + salt = os.urandom(12) + digest = hashlib.sha1(bytearray(pin, 'UTF-8')+salt).digest() + return '{SSHA}' + base64.b64encode(digest + salt) + + return set_hash_field(request, CLabPinForm, 'c_lab_pin1', 'c-labPIN', + calculate_clab_hash, 'clabpin.html') + +@login_required +def password(request): + """ + """ + member = retrieve_member(request) + + if request.method == 'POST': + form = PasswordForm(request.POST, request=request) + + if form.is_valid(): + new_password = form.cleaned_data['password1'] + member.change_password(new_password) + request.session['ldap_password'] = new_password + request.session.save() + new_form = PasswordForm() + return render(request, 'password.html', + {'message': _('Your password was changed. Thank you!'), + 'form': new_form, 'member': member.to_dict()}) + else: + return render(request, 'password.html', + {'form': form, 'member': member.to_dict()}) + else: + form = PasswordForm() + return render(request, 'password.html', + {'form': form, 'member': member.to_dict()}) + +def set_ldap_field(request, form_type, field_names, template_name): + """ + Abstract view for each of the different forms. + + field_names contains the mapping of the field name in the form to + """ + 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, 'member': member.to_dict()}) + else: + return render(request, template_name, + {'form': form, 'member': member.to_dict()}) + 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, 'member': member.to_dict()}) + +@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') + +@login_required +def nrf24(request): + return set_ldap_field(request, NRF24Form, [('nrf24', 'nrf24')], 'nrf24.html') + + + + diff --git a/cbapi_ldap/__init__.py b/cbapi_ldap/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/cbapi_ldap/urls.py b/cbapi_ldap/urls.py new file mode 100644 index 0000000..bb83b0b --- /dev/null +++ b/cbapi_ldap/urls.py @@ -0,0 +1,9 @@ +from django.conf.urls import patterns, url +from jsonrpc import jsonrpc_site +from cbapi_ldap import views + +urlpatterns = patterns( + '', + url(r'^ldap/browse/$', 'jsonrpc.views.browse', name='jsonrpc_browser'), + url(r'^ldap/$', jsonrpc_site.dispatch, name='jsonrpc_mountpoint'), +) diff --git a/cbapi_ldap/views.py b/cbapi_ldap/views.py new file mode 100644 index 0000000..d417c14 --- /dev/null +++ b/cbapi_ldap/views.py @@ -0,0 +1,22 @@ +from jsonrpc import jsonrpc_method + +TODO = [ + '', +] + +@jsonrpc_method("ping", authenticated=True) +def ping(request, username, password): + """Ping - Echo Request + + :returns str: echo_response + """ + echo_response = "PONG" + return echo_response + +@jsonrpc_method("todo") +def todo(request): + """Todo - List ToDo Items + + :returns list: todolist + """ + return TODO diff --git a/cbmi/forms.py b/cbmi/forms.py new file mode 100644 index 0000000..e69de29 diff --git a/cbmi/local_settings.py.smile b/cbmi/local_settings.py.smile new file mode 100644 index 0000000..58cf7b0 --- /dev/null +++ b/cbmi/local_settings.py.smile @@ -0,0 +1,59 @@ +# 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 + +CBASE_GASTRO_KEY = '12345690askdkadlasjdaj REPLACE ME' \ No newline at end of file diff --git a/cbmi/local_settings.py.uk b/cbmi/local_settings.py.uk new file mode 100644 index 0000000..58cf7b0 --- /dev/null +++ b/cbmi/local_settings.py.uk @@ -0,0 +1,59 @@ +# 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 + +CBASE_GASTRO_KEY = '12345690askdkadlasjdaj REPLACE ME' \ No newline at end of file diff --git a/cbmi/settings.py b/cbmi/settings.py index 7c9609b..60fd668 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', @@ -165,8 +175,11 @@ INSTALLED_APPS = ( 'django.contrib.staticfiles', 'django.contrib.admin', 'django.contrib.admindocs', - 'cbmi', + 'jsonrpc', + 'crispy_forms', + # 'cbmi', 'account', + #'cbapi_ldap', ) # A sample logging configuration. The only tangible logging @@ -197,3 +210,19 @@ 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' + +# Set session cookie timeout to 10 minutes +SESSION_COOKIE_AGE = 600 +LOGIN_URL = '/account/login/' +#LOCALE_PATHS = + +try: + from local_settings import * +except ImportError, e: + print 'Unable to load local_settings.py:', e diff --git a/cbmi/urls.py b/cbmi/urls.py index 5f24716..e52b3b9 100644 --- a/cbmi/urls.py +++ b/cbmi/urls.py @@ -6,7 +6,11 @@ admin.autodiscover() urlpatterns = patterns('', url(r'^admin/doc/', include('django.contrib.admindocs.urls')), url(r'^admin/', include(admin.site.urls)), + + 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'^$', 'account.views.landingpage', name="landingpage"), + ) diff --git a/cbmi/views.py b/cbmi/views.py index 8d9300d..7a5526d 100644 --- a/cbmi/views.py +++ b/cbmi/views.py @@ -1,27 +1,5 @@ -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 -*- +# Create your views here -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()) 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 diff --git a/static/c-base-bootstrap b/static/c-base-bootstrap new file mode 160000 index 0000000..279f25a --- /dev/null +++ b/static/c-base-bootstrap @@ -0,0 +1 @@ +Subproject commit 279f25a00dc1ee300009350a1ddc59963cef0da8 diff --git a/static/img/c-base_raumstation.gif b/static/img/c-base_raumstation.gif new file mode 100644 index 0000000..dc80bf8 Binary files /dev/null and b/static/img/c-base_raumstation.gif differ diff --git a/static/img/logo.gif b/static/img/logo.gif new file mode 100644 index 0000000..f17304d Binary files /dev/null and b/static/img/logo.gif differ diff --git a/static/img/logo.xcf b/static/img/logo.xcf new file mode 100644 index 0000000..df40600 Binary files /dev/null and b/static/img/logo.xcf differ