BryanDavis has uploaded a new change for review. ( https://gerrit.wikimedia.org/r/328622 )
Change subject: Allow changing LDAP password ...................................................................... Allow changing LDAP password Bug: T153935 Change-Id: I04b1bf0b5cf7122e23364505c39689e9a691181e --- M striker/labsauth/models.py M striker/profile/forms.py M striker/profile/urls.py M striker/profile/views.py M striker/templates/profile/settings/accounts/ldap.html M striker/templates/profile/settings/base.html A striker/templates/profile/settings/change-password.html 7 files changed, 140 insertions(+), 4 deletions(-) git pull ssh://gerrit.wikimedia.org:29418/labs/striker refs/changes/22/328622/1 diff --git a/striker/labsauth/models.py b/striker/labsauth/models.py index 88c8d8e..771e6a8 100644 --- a/striker/labsauth/models.py +++ b/striker/labsauth/models.py @@ -30,6 +30,7 @@ from django.utils.translation import ugettext_lazy as _ from ldapdb.models import fields as ldap_fields +import ldap import ldapdb.models import mwoauth @@ -109,12 +110,28 @@ verbose_name_plural = _('users') def set_password(self, raw_password): - """Can't change password.""" - return + # Set password by directly manipulating the associated LDAP record. + # The ldap-auth backend we use does not support password + # checks/changes. + ldapuser = self.ldapuser + ldapuser.password = raw_password + ldapuser.save() def check_password(self, raw_password): - """Can't be used directly for authn.""" - return False + """Return a boolean of whether the raw_password was correct.""" + # Validate the current password by doing an authbind as the user. + # The ldap-auth backend we use does not support password + # checks/changes. + try: + con = ldap.initialize(settings.AUTH_LDAP_SERVER_URI) + if settings.AUTH_LDAP_START_TLS: + con.start_tls_s() + con.simple_bind_s(self.ldap_dn, raw_password) + except ldap.INVALID_CREDENTIALS: + return False + else: + con.unbind() + return True def set_unusable_password(self): return diff --git a/striker/profile/forms.py b/striker/profile/forms.py index bfae5b4..3087835 100644 --- a/striker/profile/forms.py +++ b/striker/profile/forms.py @@ -19,7 +19,10 @@ # along with Striker. If not, see <http://www.gnu.org/licenses/>. from django import forms +from django.conf import settings from django.utils.translation import ugettext_lazy as _ + +from parsley.decorators import parsleyfy from striker.profile import utils @@ -78,3 +81,57 @@ _('Invalid public key.'), code='key_invalid') self.key = key return pub_key + + +@parsleyfy +class PasswordChangeForm(forms.Form): + class Meta: + parsley_extras = { + 'confirm': { + 'equalto': 'passwd', + 'error-message': _('Passwords do not match.'), + } + } + + old_password = forms.CharField( + label=_('Old password'), + widget=forms.PasswordInput( + attrs={ + 'autofocus': 'autofocus', + } + ) + ) + passwd = forms.CharField( + label=_('New password'), + min_length=10, + widget=forms.PasswordInput + ) + confirm = forms.CharField( + label=_('Confirm new password'), + widget=forms.PasswordInput + ) + + def __init__(self, user=None, *args, **kwargs): + self.user = user + super(PasswordChangeForm, self).__init__(*args, **kwargs) + + def clean_old_password(self): + old_password = self.cleaned_data.get('old_password') + if not self.user.check_password(old_password): + raise forms.ValidationError( + _('Your old password was entered incorrectly. ' + 'Please enter it again.'), + code='password_incorrect') + return old_password + + def clean_confirm(self): + """Validate that both password entries match.""" + passwd = self.cleaned_data.get('passwd') + confirm = self.cleaned_data.get('confirm') + if passwd != confirm: + raise forms.ValidationError(_('Passwords do not match.')) + return confirm + + def save(self): + self.user.set_password(self.cleaned_data['passwd']) + return self.user diff --git a/striker/profile/urls.py b/striker/profile/urls.py index e6a4b5a..8d21080 100644 --- a/striker/profile/urls.py +++ b/striker/profile/urls.py @@ -55,4 +55,9 @@ 'striker.profile.views.ssh_key_add', name='ssh_key_add' ), + urls.url( + r'^settings/change_password$', + 'striker.profile.views.change_password', + name='change_password' + ), ] diff --git a/striker/profile/views.py b/striker/profile/views.py index 2f5c00e..9f84b04 100644 --- a/striker/profile/views.py +++ b/striker/profile/views.py @@ -26,6 +26,7 @@ from django.contrib.auth.decorators import login_required from django.core import urlresolvers from django.db.utils import DatabaseError +from django.views.decorators.debug import sensitive_post_parameters from django.utils.translation import ugettext_lazy as _ from striker import decorators @@ -124,3 +125,25 @@ else: messages.error(req, _('Invalid public key.')) return shortcuts.redirect(urlresolvers.reverse('profile:ssh_keys')) + + +@sensitive_post_parameters() +@login_required +def change_password(req): + if req.method == 'POST': + form = forms.PasswordChangeForm(data=req.POST, user=req.user) + if form.is_valid(): + form.save() + messages.info(req, _('Password changed')) + # We do not need to mess with update_session_auth_hash because + # LDAP passwords are detached from the normal Django session + # management methods. + return shortcuts.redirect( + urlresolvers.reverse('profile:change_password')) + else: + form = forms.PasswordChangeForm(user=req.user) + ctx = { + 'change_password_form': form, + } + return shortcuts.render( + req, 'profile/settings/change-password.html', ctx) diff --git a/striker/templates/profile/settings/accounts/ldap.html b/striker/templates/profile/settings/accounts/ldap.html index 60c0032..a51373f 100644 --- a/striker/templates/profile/settings/accounts/ldap.html +++ b/striker/templates/profile/settings/accounts/ldap.html @@ -12,4 +12,7 @@ <dt>{% trans "Email" %}</dt><dd>{{ user.ldapemail }}</dd> </dl> </div> + <div class="panel-footer"> + <a class="btn btn-default" href="{% url 'profile:change_password' %}">{% trans "Change password" %}</a> + </div> </div> diff --git a/striker/templates/profile/settings/base.html b/striker/templates/profile/settings/base.html index d84b878..750869c 100644 --- a/striker/templates/profile/settings/base.html +++ b/striker/templates/profile/settings/base.html @@ -6,6 +6,7 @@ {% block pre_content %} {% url 'profile:accounts' as settings_accounts %} {% url 'profile:ssh_keys' as settings_ssh_keys %} +{% url 'profile:change_password' as settings_change_password %} {{ block.super }} <div class="container-fluid"> <div class="row"> @@ -17,6 +18,7 @@ <div class="list-group" role="navigation"> <a class="list-group-item {% if request.path == settings_accounts %}active{% endif %}" href="{{ settings_accounts }}">{% bootstrap_icon "user" %} {% trans "Linked accounts" %}</a> <a class="list-group-item {% if request.path == settings_ssh_keys %}active{% endif %}" href="{{ settings_ssh_keys }}">{% fa_icon "key" %} {% trans "SSH keys" %}</a> + <a class="list-group-item {% if request.path == settings_change_password %}active{% endif %}" href="{{ settings_change_password }}">{% bootstrap_icon "lock" %} {% trans "Change password" %}</a> </div> </div> </div> diff --git a/striker/templates/profile/settings/change-password.html b/striker/templates/profile/settings/change-password.html new file mode 100644 index 0000000..a59c121 --- /dev/null +++ b/striker/templates/profile/settings/change-password.html @@ -0,0 +1,29 @@ +{% extends "profile/settings/base.html" %} +{% load bootstrap3 %} +{% load i18n %} +{% load staticfiles %} + +{% block title %}{% trans "Change password" %}{% endblock %} +{% block content %} +<div class="panel panel-default"> + <div class="panel-heading"> + <h3 class="panel-title">{% trans "Change LDAP password" %}</h3> + </div> + <div class="panel-body"> + <form method="post" action="{% url 'profile:change_password' %}" class="form parsley"> + {% csrf_token %} + {% bootstrap_form change_password_form %} + {% buttons %} + <button class="btn btn-primary" type="submit">{% trans "Update password" %}</button> + {% endbuttons %} + </form> + </div> +</div> +{% endblock %} + +{% block js %} +{{ block.super }} +<script lang="javascript" src="{% static 'js/parsley.min.js' %}"></script> +<script lang="javascript" src="{% static 'js/parsley-bootstrap.js' %}"></script> +{% endblock %} +{# vim:sw=2:ts=2:sts=2:et: #} -- To view, visit https://gerrit.wikimedia.org/r/328622 To unsubscribe, visit https://gerrit.wikimedia.org/r/settings Gerrit-MessageType: newchange Gerrit-Change-Id: I04b1bf0b5cf7122e23364505c39689e9a691181e Gerrit-PatchSet: 1 Gerrit-Project: labs/striker Gerrit-Branch: master Gerrit-Owner: BryanDavis <bda...@wikimedia.org> _______________________________________________ MediaWiki-commits mailing list MediaWiki-commits@lists.wikimedia.org https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits