aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPavlos Ratis <dastergon@gentoo.org>2013-08-28 17:46:29 +0300
committerPavlos Ratis <dastergon@gentoo.org>2013-09-01 20:02:14 +0300
commitc259b7c0f1935a4464946a66119504957921b7a8 (patch)
tree57aa268f5ea2b37f5ef80ac0115ebde85f57d5b8
parentMerge pull request #81 from tampakrap/test_ldapuser (diff)
downloadidentity.gentoo.org-c259b7c0f1935a4464946a66119504957921b7a8.tar.gz
identity.gentoo.org-c259b7c0f1935a4464946a66119504957921b7a8.tar.bz2
identity.gentoo.org-c259b7c0f1935a4464946a66119504957921b7a8.zip
add support for user settings
Basic setting pages that allow users to edit easily their LDAP attributes via web forms. Settings: * Profile settings (first name , last name, email , timezone, birthday, * passwords) * Contact settings (website, IM , GPG fingerprint, location, longitude, latitude) * Gentoo settings (mentors, developer bug, join date, retire date, ssh key) Notes: * Currently settings work only with single values and not * listfields. * Timezone needed for OpenID (cc: @mgorny) * Gentoo Account Settings page is available for everyone.(testing purposes)
-rw-r--r--okupy/accounts/forms.py58
-rw-r--r--okupy/accounts/urls.py12
-rw-r--r--okupy/accounts/views.py184
-rw-r--r--okupy/templates/base.html4
-rw-r--r--okupy/templates/settings-contact.html52
-rw-r--r--okupy/templates/settings-gentoo.html42
-rw-r--r--okupy/templates/settings-profile.html54
-rw-r--r--requirements.txt1
-rwxr-xr-xsetup.py1
9 files changed, 399 insertions, 9 deletions
diff --git a/okupy/accounts/forms.py b/okupy/accounts/forms.py
index 8997e1c..37c05d0 100644
--- a/okupy/accounts/forms.py
+++ b/okupy/accounts/forms.py
@@ -5,6 +5,8 @@ from django import forms
from okupy.accounts.models import OpenID_Attributes
from okupy.crypto.ciphers import sessionrefcipher
+import pytz
+
class LoginForm(forms.Form):
username = forms.CharField(max_length=100, label='Username:')
@@ -59,8 +61,64 @@ class SignupForm(forms.Form):
return password_verify
+#Settings
+
+class ProfileSettingsForm(forms.Form):
+ first_name = forms.CharField(
+ max_length=100, label='First Name', required=False)
+ last_name = forms.CharField(
+ max_length=100, label='Last Name', required=False)
+ email = forms.EmailField(max_length=254, label='Email', help_text='A valid email address, please.', required=False)
+ birthday = forms.DateField(
+ input_formats='%m/%d/%Y', label='Birthday (format: month/day/year)', required=False)
+ timezone = forms.ChoiceField(
+ choices=[(x, x) for x in pytz.common_timezones])
+ old_password = forms.CharField(max_length=30, widget=forms.PasswordInput(
+ ), label='Old Password', required=False)
+ new_password = forms.CharField(max_length=30, widget=forms.PasswordInput(
+ ), label='New Password', required=False)
+ new_password_verify = forms.CharField(max_length=30, widget=forms.PasswordInput(), label='Repeat New Password', required=False)
+
+ def clean(self):
+ cleaned_data = super(ProfileSettingsForm, self).clean()
+ new_password = cleaned_data.get('new_password')
+ new_password_verify = cleaned_data.get('new_password_verify')
+ old_password = cleaned_data.get('old_password')
+ if (new_password or new_password_verify) and (not old_password):
+ raise forms.ValidationError(
+ 'Please enter your current password to change the password.')
+ elif new_password != new_password_verify:
+ raise forms.ValidationError('Passsword verification failed. Please re-enter the new password.')
+ elif (old_password and new_password) and (not new_password_verify):
+ raise forms.ValidationError('Password verification failed. Please repeat your new password.')
+ return cleaned_data
+
+
+class ContactSettingsForm(forms.Form):
+ website = forms.URLField(label='Website', required=False)
+ im = forms.CharField(max_length=100, label='IM', required=False)
+ location = forms.CharField(label='Location', required=False)
+ longitude = forms.FloatField(label='Longitude', required=False)
+ latitude = forms.FloatField(label='Latitude', required=False)
+ phone = forms.CharField(label='Home Phone', required=False)
+ gpg_fingerprint = forms.CharField(label='GPG Fingerprint', required=False)
+
+
+class GentooAccountSettingsForm(forms.Form):
+ gentoo_join_date = forms.CharField(
+ label='Gentoo Join Date', required=False)
+ gentoo_retire_date = forms.CharField(
+ label='Gentoo Retire Date', required=False)
+ developer_bug = forms.CharField(
+ label='Developer Bugs (Bug Number)', required=False)
+ mentor = forms.CharField(max_length=100, label='Mentor', required=False)
+ ssh_key = forms.CharField(widget=forms.Textarea(
+ attrs={'cols': 50, 'rows': 8}), label='SSH Key', required=False)
+
+
# OpenID forms.
+
class SiteAuthForm(forms.ModelForm):
class Meta:
model = OpenID_Attributes
diff --git a/okupy/accounts/urls.py b/okupy/accounts/urls.py
index 86b158b..d5e37c9 100644
--- a/okupy/accounts/urls.py
+++ b/okupy/accounts/urls.py
@@ -9,12 +9,12 @@ accounts_urlpatterns = patterns(
url(r'^login/$', v.login),
url(r'^ssl-auth/$', v.ssl_auth),
url(r'^logout/$', v.logout, name="logout"),
- url(r'^devlist/$', v.lists, {'acc_list': 'devlist'},
- name="active_developers"),
- url(r'^former-devlist/$', v.lists, {'acc_list': 'former-devlist'},
- name="former_developers"),
- url(r'^foundation-members/$', v.lists, {'acc_list': 'foundation-members'},
- name="foundation_members"),
+ url(r'^devlist/$', v.lists, {'acc_list': 'devlist'}, name="active_developers"),
+ url(r'^former-devlist/$', v.lists, {'acc_list': 'former-devlist'}, name="former_developers"),
+ url(r'^foundation-members/$', v.lists, {'acc_list': 'foundation-members'}, name="foundation_members"),
+ url(r'^contact-settings/$', v.contact_settings, name="contact-settings"),
+ url(r'^gentoo-dev-settings/$', v.gentoo_dev_settings, name="gentoo-dev-settings"),
+ url(r'^profile-settings/$', v.profile_settings, name="profile-settings"),
url(r'^signup/$', v.signup),
url(r'^activate/(?P<token>[a-zA-Z0-9-_]+)/$', v.activate),
url(r'^otp-setup/$', v.otp_setup),
diff --git a/okupy/accounts/views.py b/okupy/accounts/views.py
index fc96f55..da48634 100644
--- a/okupy/accounts/views.py
+++ b/okupy/accounts/views.py
@@ -28,7 +28,7 @@ from urlparse import urljoin
from okupy import OkupyError
from okupy.accounts.forms import (LoginForm, OpenIDLoginForm, SSLCertLoginForm,
OTPForm, SignupForm, SiteAuthForm,
- StrongAuthForm)
+ StrongAuthForm, ProfileSettingsForm, ContactSettingsForm, GentooAccountSettingsForm)
from okupy.accounts.models import LDAPUser, OpenID_Attributes, Queue
from okupy.accounts.openid_store import DjangoDBOpenIDStore
from okupy.common.ldap_helpers import (get_bound_ldapuser,
@@ -47,6 +47,7 @@ import openid.yadis.discover
import openid.fetchers
import django_otp
import io
+import ldap
import logging
import qrcode
@@ -70,7 +71,7 @@ def lists(request, acc_list):
def index(request):
ldb_user = LDAPUser.objects.filter(username=request.user.username)
return render(request, 'index.html', {
- 'ldb_user': ldb_user
+ 'ldb_user': ldb_user,
})
@@ -366,6 +367,185 @@ def activate(request, token):
return redirect(login)
+# Settings
+
+@strong_auth_required
+@otp_required
+def profile_settings(request):
+ """ Primary account settings, """
+ user_profile_info = get_bound_ldapuser(request)
+ profile_settings = None
+ if request.method == "POST":
+ profile_settings = ProfileSettingsForm(request.POST)
+ if profile_settings.is_valid():
+ try:
+ #birthday = profile_settings.cleaned_data['birthday']
+ email = profile_settings.cleaned_data['email']
+ first_name = profile_settings.cleaned_data['first_name']
+ last_name = profile_settings.cleaned_data['last_name']
+ new_password = profile_settings.cleaned_data['new_password']
+ new_password_verify = profile_settings.cleaned_data['new_password_verify']
+ old_password = profile_settings.cleaned_data['old_password']
+
+ if user_profile_info.first_name != first_name:
+ user_profile_info.first_name = first_name
+
+ if user_profile_info.last_name != last_name:
+ user_profile_info.last_name = last_name
+
+ user_profile_info.full_name = '%s %s' % (first_name, last_name)
+ user_profile_info.gecos = '%s %s' % (first_name, last_name)
+
+ """
+ if user_profile_info.birthday != birthday:
+ user_profile_info.birthday = birthday
+ """
+ if user_profile_info.email != email:
+ user_profile_info.email.pop()
+ user_profile_info.email.append(email)
+
+ if old_password and (new_password == new_password_verify):
+ for hash in list(user_profile_info.password):
+ print hash
+ try:
+ if ldap_md5_crypt.verify(old_password, hash):
+ user_profile_info.password.append(ldap_md5_crypt.encrypt(new_password_verify))
+ user_profile_info.password.remove(hash)
+ break
+ except ValueError:
+ # ignore unknown hashes
+ pass
+ try:
+ user_profile_info.save()
+ except IntegrityError:
+ pass
+ except ldap.TYPE_OR_VALUE_EXISTS:
+ pass
+ except Exception as error:
+ logger.critical(error, extra=log_extra_data(request))
+ logger_mail.exception(error)
+ raise OkupyError("Can't contact LDAP server")
+ else:
+ profile_settings = ProfileSettingsForm()
+
+ return render(request, 'settings-profile.html', {
+ 'profile_settings': profile_settings,
+ 'user_profile_info': user_profile_info,
+ })
+
+
+@strong_auth_required
+@otp_required
+def contact_settings(request):
+ """ Contact details """
+ user_profile_info = get_bound_ldapuser(request)
+ contact_settings = None
+ if request.method == "POST":
+ contact_settings = ContactSettingsForm(request.POST)
+ if contact_settings.is_valid():
+ try:
+ gpg_fingerprint = contact_settings.cleaned_data['gpg_fingerprint']
+ im = contact_settings.cleaned_data['im']
+ latitude = contact_settings.cleaned_data['latitude']
+ location = contact_settings.cleaned_data['location']
+ longitude = contact_settings.cleaned_data['longitude']
+ phone = contact_settings.cleaned_data['phone']
+ website = contact_settings.cleaned_data['website']
+
+ if user_profile_info.location != location:
+ user_profile_info.location = location
+
+ if user_profile_info.phone != phone:
+ user_profile_info.phone = phone
+
+ if user_profile_info.website != website:
+ user_profile_info.website.pop()
+ user_profile_info.website.append(website)
+
+ if user_profile_info.im != im:
+ user_profile_info.im.pop()
+ user_profile_info.im.append(im)
+
+ if user_profile_info.longitude != longitude:
+ user_profile_info.longitude = longitude
+
+ if user_profile_info.latitude != latitude:
+ user_profile_info.latitude = latitude
+
+ if user_profile_info.gpg_fingerprint != gpg_fingerprint:
+ user_profile_info.gpg_fingerprint.pop()
+ user_profile_info.gpg_fingerprint.append(gpg_fingerprint)
+
+ try:
+ user_profile_info.save()
+ except IntegrityError:
+ pass
+ except ldap.TYPE_OR_VALUE_EXISTS:
+ pass
+ except Exception as error:
+ logger.critical(error, extra=log_extra_data(request))
+ logger_mail.exception(error)
+ raise OkupyError("Can't contact LDAP server")
+ else:
+ contact_settings = ContactSettingsForm()
+
+ return render(request, 'settings-contact.html', {
+ 'contact_settings': contact_settings,
+ 'user_profile_info': user_profile_info,
+ })
+
+
+@strong_auth_required
+@otp_required
+def gentoo_dev_settings(request):
+ """ Gentoo related information """
+ user_profile_info = get_bound_ldapuser(request)
+ gentoo_account_settings = None
+ if request.method == "POST":
+ gentoo_account_settings = GentooAccountSettingsForm(request.POST)
+ if gentoo_account_settings.is_valid():
+ try:
+ devbug = gentoo_account_settings.cleaned_data['developer_bug']
+ gentoo_join_date = gentoo_account_settings.cleaned_data['gentoo_join_date']
+ gentoo_mentor = gentoo_account_settings.cleaned_data['mentor']
+ ssh_pubkey = gentoo_account_settings.cleaned_data['ssh_key']
+
+ if user_profile_info.developer_bug != devbug:
+ user_profile_info.developer_bug.append(devbug)
+
+ if user_profile_info.gentoo_join_date != gentoo_join_date:
+ user_profile_info.gentoo_join_date.append(gentoo_join_date)
+
+ if user_profile_info.mentor != gentoo_mentor:
+ user_profile_info.mentor.append(gentoo_mentor)
+
+ if ssh_pubkey:
+ user_profile_info.ssh_key.append(ssh_pubkey)
+
+ if user_profile_info.is_retired or user_profile_info.gentoo_retire_date:
+ gentoo_retire_date = gentoo_account_settings.cleaned_data['gentoo_retire_date']
+ if user_profile_info.gentoo_retire_date != gentoo_retire_date:
+ user_profile_info.gentoo_retire_date.append(gentoo_retire_date)
+
+ try:
+ user_profile_info.save()
+ except IntegrityError:
+ pass
+ except ldap.TYPE_OR_VALUE_EXISTS:
+ pass
+ except Exception as error:
+ logger.critical(error, extra=log_extra_data(request))
+ logger_mail.exception(error)
+ raise OkupyError("Can't contact LDAP server")
+ else:
+ gentoo_account_settings = GentooAccountSettingsForm()
+
+ return render(request, 'settings-gentoo.html', {
+ 'gentoo_account_settings': gentoo_account_settings,
+ 'user_profile_info': user_profile_info,
+ })
+
+
@strong_auth_required
@otp_required
def otp_setup(request):
diff --git a/okupy/templates/base.html b/okupy/templates/base.html
index 3e886a0..0fc8c3a 100644
--- a/okupy/templates/base.html
+++ b/okupy/templates/base.html
@@ -65,7 +65,9 @@
<a href="#" class="dropdown-toggle" data-toggle="dropdown">{{ user.username }}<b class="caret"></b></a>
<ul class="dropdown-menu">
<li><a href="{% url 'index' %}" title="View Profile">View Profile</a></li>
- <li><a href="#" title="Edit Profile">Edit Profile</a></li>
+ <li><a href="{% url 'profile-settings' %}" title="Edit Profile">Edit Profile</a></li>
+ <li><a href="{% url 'contact-settings' %}" title="Contact Settings">Contact Settings</a></li>
+ <li><a href="{% url 'gentoo-dev-settings' %}" title="Gentoo Account Settings">Gentoo Account Settings</a></li>
<li><a href="#" title="Invite others">Invite</a></li>
<li><a href="{% url 'logout' %}" title="Logout">Logout</a></li>
</ul>
diff --git a/okupy/templates/settings-contact.html b/okupy/templates/settings-contact.html
new file mode 100644
index 0000000..502e056
--- /dev/null
+++ b/okupy/templates/settings-contact.html
@@ -0,0 +1,52 @@
+{% extends "base.html" %}
+
+{% block content %}
+<div id="pageRow" class="container">
+ <div class="row">
+ <div id="page" class="span10">
+ <div>
+ <h1>Contact Settings</h1>
+ <h3>Change your contact to details.</h3>
+ <div class="form well">
+ <form action="." method="POST">{% csrf_token %}
+ {{ contact_settings.errors }}
+ {{ contact_settings.non_field_errors }}
+ <div class="row-fluid">
+ {{ contact_settings.website.label_tag }}
+ <input name="website" type="text" value="{{ user_profile_info.website.0 }}" /> {{ contact_settings.website.errors|striptags }}
+ </div>
+ <div class="row-fluid">
+ {{ contact_settings.im.label_tag }}
+ <input name="im" type="text" value="{{ user_profile_info.im.0 }}" /> {{ contact_settings.im.errors|striptags }}
+ </div>
+ <div class="row-fluid">
+ {{ contact_settings.location.label_tag }}
+ <input name="location" type="text" value="{{ user_profile_info.location }}" /> {{ contact_settings.location.errors|striptags }}
+ </div>
+ <div class="row-fluid">
+ {{ contact_settings.longitude.label_tag }}
+ <input name="longitude" type="text" value="{{ user_profile_info.longitude }}" /> {{ contact_settings.longitude.errors|striptags }}
+ </div>
+ <div class="row-fluid">
+ {{ contact_settings.latitude.label_tag }}
+ <input name="latitude" type="text" value="{{ user_profile_info.latitude }}" /> {{ contact_settings.latitude.errors|striptags }}
+ </div>
+ <div class="row-fluid">
+ {{ contact_settings.phone.label_tag }}
+ <input name="phone" type="text" value="{{ user_profile_info.phone }}" /> {{ contact_settings.phone.errors|striptags }}
+ </div>
+ <div class="row-fluid">
+ {{ contact_settings.gpg_fingerprint.label_tag }}
+ <input name="gpg_fingerprint" type="text" value="{{ user_profile_info.gpg_fingerprint.0 }}" /> {{ contact_settings.gpg_fingerprint.errors|striptags }}
+
+ </div>
+ <div class="row-fluid buttons">
+ <input class="btn btn-primary" type="submit" value="Save" />
+ </div>
+ </form>
+ </div>
+ </div>
+ </div>
+{% endblock %}
+
+{# vim:se fileencoding=utf8 et ts=4 sts=4 sw=4 ft=htmldjango : #}
diff --git a/okupy/templates/settings-gentoo.html b/okupy/templates/settings-gentoo.html
new file mode 100644
index 0000000..224c838
--- /dev/null
+++ b/okupy/templates/settings-gentoo.html
@@ -0,0 +1,42 @@
+{% extends "base.html" %}
+
+{% block content %}
+<div id="pageRow" class="container">
+ <div class="row">
+ <div id="page" class="span10">
+ <div>
+ <h1>Gentoo Developer Information</h1>
+ <h3>Change your Gentoo related details</h3>
+ {% if user_profile_info.is_developer %}
+ <div class="form well">
+ <form action="." method="POST">{% csrf_token %}
+ <div class="row-fluid">
+ {{ gentoo_account_settings.developer_bug.label_tag }}
+ <input name="developer_bug" type="text" value="{{ user_profile_info.developer_bug.0 }}" /> {{ gentoo_account_settings.developer_bug.errors|striptags }}
+ </div>
+ <div class="row-fluid">
+ {{ gentoo_account_settings.mentor.label_tag }}
+ <input name="mentor" type="text" value="{{ user_profile_info.mentor.0 }}" /> {{ gentoo_account_settings.mentor.errors|striptags }}
+ </div>
+ <div class="row-fluid">
+ {{ gentoo_account_settings.gentoo_join_date.label_tag }}
+ <input name="gentoo_join_date" type="text" value="{{ user_profile_info.gentoo_join_date.0 }}" /> {{ gentoo_account_settings.gentoo_join_date.errors|striptags }}
+ </div>
+ <div class="row-fluid">
+ {{ gentoo_account_settings.gentoo_retire_date.label_tag }}
+ <input name="gentoo_retire_date" type="text" value="{{ user_profile_info.gentoo_retire_date.0 }}" /> {{ gentoo_account_settings.gentoo_retire_date.errors|striptags }}
+ </div>
+ <div class="row-fluid">
+ {{ gentoo_account_settings.ssh_key.label_tag }} {{ gentoo_account_settings.ssh_key.errors|striptags }}
+ {{ gentoo_account_settings.ssh_key }}
+ </div>
+ <div class="row-fluid buttons">
+ <input class="btn btn-primary" type="submit" value="Update" />
+ </div>
+ </form>
+ </div>{% endif %}
+ </div>
+ </div>
+{% endblock %}
+
+{# vim:se fileencoding=utf8 et ts=4 sts=4 sw=4 ft=htmldjango : #}
diff --git a/okupy/templates/settings-profile.html b/okupy/templates/settings-profile.html
new file mode 100644
index 0000000..05cd57c
--- /dev/null
+++ b/okupy/templates/settings-profile.html
@@ -0,0 +1,54 @@
+{% extends "base.html" %}
+
+{% block content %}
+<div id="pageRow" class="container">
+ <div class="row">
+ <div id="page" class="span10">
+ <div>
+ <h1>Profile Information</h1>
+ <h3>Change your basic account information</h3>
+ <div class="form well">
+ <form action="." method="POST">{% csrf_token %}
+ {{ profile_settings.errors }}
+ {{ profile_settings.non_field_errors }}
+ <div class="row-fluid">
+ {{ profile_settings.first_name.label_tag }}
+ <input name="first_name" type="text" value="{{ user_profile_info.first_name }}" /> {{ profile_settings.first_name.errors|striptags }}
+ </div>
+ <div class="row-fluid">
+ {{ profile_settings.last_name.label_tag }}
+ <input name="last_name" type="text" value="{{ user_profile_info.last_name }}" /> {{ profile_settings.last_name.errors|striptags }}
+ </div>
+ <div class="row-fluid">
+ {{ profile_settings.email.label_tag }}
+ <input name="email" type="text" value="{{ user_profile_info.email.0 }}" /> {{ profile_settings.email.errors|striptags }}
+ </div>
+ <!-- <div class="row-fluid">
+ {{ profile_settings.birthday.label_tag }}
+ <input name="birthday" type="text" value="{{ user_profile_info.birthday }}" /> {{ profile_settings.birthday.errors|striptags }}
+ </div> -->
+ <div class="row-fluid">
+ {{ profile_settings.timezone.label_tag }}
+ {{ profile_settings.timezone }}
+ <div class="row-fluid">
+ {{ profile_settings.old_password.label_tag }}
+ {{ profile_settings.old_password }} {{ profile_settings.old_password.non_field_errors|striptags }}
+ </div>
+ <div class="row-fluid">
+ {{ profile_settings.new_password.label_tag }}
+ {{ profile_settings.new_password }} {{ profile_settings.new_password.errors|striptags }}
+ </div>
+ <div class="row-fluid">
+ {{ profile_settings.new_password_verify.label_tag }}
+ {{ profile_settings.new_password_verify }} {{ profile_settings.new_password_verify.errors|striptags }}
+ </div>
+ <div class="row-fluid buttons">
+ <input class="btn btn-primary" type="submit" value="Update Profile" />
+ </div>
+ </form>
+ </div>
+ </div>
+ </div>
+{% endblock %}
+
+{# vim:se fileencoding=utf8 et ts=4 sts=4 sw=4 ft=htmldjango : #}
diff --git a/requirements.txt b/requirements.txt
index 710459a..067e449 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -13,4 +13,5 @@ pyopenssl>=0.13
python-ldap>=2.4.10
python-memcached>=1.53
python-openid>=2.2.5
+pytz>=2012j
qrcode>=3.0
diff --git a/setup.py b/setup.py
index 92adfe0..306ee40 100755
--- a/setup.py
+++ b/setup.py
@@ -46,6 +46,7 @@ setup(
'python-ldap>=2.4.10',
'python-memcached>=1.53',
'python-openid>=2.2.5',
+ 'pytz>=2012j',
'qrcode>=3.0',
],
setup_requires=[