1
0
mirror of https://github.com/haiwen/seahub.git synced 2025-08-01 07:10:55 +00:00

[sysadmin] Force passwd change when add/reset user

This commit is contained in:
zhengxie 2015-11-23 17:55:42 +08:00
parent 808a5e186d
commit 054ef03687
11 changed files with 165 additions and 7 deletions

View File

@ -23,6 +23,7 @@ from seahub.auth.forms import AuthenticationForm, CaptchaAuthenticationForm
from seahub.auth.forms import PasswordResetForm, SetPasswordForm, PasswordChangeForm
from seahub.auth.tokens import default_token_generator
from seahub.base.accounts import User
from seahub.options.models import UserOptions
from seahub.utils import is_ldap_user
from seahub.utils.http import is_safe_url
from seahub.utils.ip import get_remote_ip
@ -134,6 +135,11 @@ def login(request, template_name='registration/login.html',
# have captcha
form = CaptchaAuthenticationForm(data=request.POST)
if form.is_valid():
if UserOptions.objects.passwd_change_required(
form.get_user().username):
redirect_to = reverse('auth_password_change')
request.session['force_passwd_change'] = True
# captcha & passwod is valid, log user in
request.session['remember_me'] = remember_me
return log_user_in(request, form.get_user(), redirect_to)
@ -143,6 +149,11 @@ def login(request, template_name='registration/login.html',
else:
form = authentication_form(data=request.POST)
if form.is_valid():
if UserOptions.objects.passwd_change_required(
form.get_user().username):
redirect_to = reverse('auth_password_change')
request.session['force_passwd_change'] = True
# password is valid, log user in
request.session['remember_me'] = remember_me
return log_user_in(request, form.get_user(), redirect_to)
@ -365,6 +376,12 @@ def password_change(request, template_name='registration/password_change_form.ht
form = password_change_form(user=request.user, data=request.POST)
if form.is_valid():
form.save()
if request.session.get('force_passwd_change', False):
del request.session['force_passwd_change']
UserOptions.objects.unset_force_passwd_change(
request.user.username)
update_session_auth_hash(request, request.user)
return HttpResponseRedirect(post_change_redirect)
else:
@ -375,6 +392,7 @@ def password_change(request, template_name='registration/password_change_form.ht
'min_len': config.USER_PASSWORD_MIN_LENGTH,
'strong_pwd_required': config.USER_STRONG_PASSWORD_REQUIRED,
'level': config.USER_PASSWORD_STRENGTH_LEVEL,
'force_passwd_change': request.session.get('force_passwd_change', False),
}, context_instance=RequestContext(request))
def password_change_done(request, template_name='registration/password_change_done.html'):

View File

@ -1,4 +1,8 @@
import re
from django.core.cache import cache
from django.core.urlresolvers import reverse
from django.http import HttpResponseRedirect
import seaserv
@ -12,6 +16,7 @@ try:
from seahub.settings import MULTI_TENANCY
except ImportError:
MULTI_TENANCY = False
from seahub.settings import SITE_ROOT
class BaseMiddleware(object):
"""
@ -70,3 +75,21 @@ class InfobarMiddleware(object):
def process_response(self, request, response):
return response
class ForcePasswdChangeMiddleware(object):
def _request_in_black_list(self, request):
path = request.path
black_list = (r'^%s$' % SITE_ROOT, r'home/.+', r'repo/.+',
r'[f|d]/[a-f][0-9]{10}', r'group/\d+', r'groups/',
r'share/', r'profile/', r'notification/list/')
for patt in black_list:
if re.search(patt, path) is not None:
return True
return False
def process_request(self, request):
if request.session.get('force_passwd_change', False):
if self._request_in_black_list(request):
return HttpResponseRedirect(reverse('auth_password_change'))

View File

@ -18,6 +18,9 @@ KEY_SUB_LIB = "sub_lib"
VAL_SUB_LIB_ENABLED = "1"
VAL_SUB_LIB_DISABLED = "0"
KEY_FORCE_PASSWD_CHANGE = "force_passwd_change"
VAL_FORCE_PASSWD_CHANGE = "1"
KEY_DEFAULT_REPO = "default_repo"
class CryptoOptionNotSetError(Exception):
@ -43,6 +46,11 @@ class UserOptionsManager(models.Manager):
return user_option
def unset_user_option(self, username, k):
"""Remove user's option.
"""
super(UserOptionsManager, self).filter(email=username, option_key=k).delete()
def enable_server_crypto(self, username):
"""
@ -185,7 +193,24 @@ class UserOptionsManager(models.Manager):
return user_option.option_val
except UserOptions.DoesNotExist:
return None
def passwd_change_required(self, username):
"""Check whether user need to change password.
"""
try:
r = super(UserOptionsManager, self).get(
email=username, option_key=KEY_FORCE_PASSWD_CHANGE)
return r.option_val == VAL_FORCE_PASSWD_CHANGE
except UserOptions.DoesNotExist:
return False
def set_force_passwd_change(self, username):
return self.set_user_option(username, KEY_FORCE_PASSWD_CHANGE,
VAL_FORCE_PASSWD_CHANGE)
def unset_force_passwd_change(self, username):
return self.unset_user_option(username, KEY_FORCE_PASSWD_CHANGE)
class UserOptions(models.Model):
email = LowerCaseCharField(max_length=255, db_index=True)
option_key = models.CharField(max_length=50)

View File

@ -117,7 +117,8 @@ MIDDLEWARE_CLASSES = (
'seahub.auth.middleware.AuthenticationMiddleware',
'seahub.base.middleware.BaseMiddleware',
'seahub.base.middleware.InfobarMiddleware',
'seahub.password_session.middleware.CheckPasswordHash'
'seahub.password_session.middleware.CheckPasswordHash',
'seahub.base.middleware.ForcePasswdChangeMiddleware',
)
SITE_ROOT_URLCONF = 'seahub.urls'

View File

@ -7,6 +7,10 @@
{% endblock %}
{% block main_panel %}
{% if force_passwd_change %}
<p>{% trans "Please update your password before continue." %} </p>
{% endif %}
<div class="new-narrow-panel">
<h2 class="hd">{% trans "Password Modification" %}</h2>
<form action="" method="post" class="con">{% csrf_token %}

View File

@ -104,7 +104,7 @@ class Fixtures(Exam):
class BaseTestCase(TestCase, Fixtures):
def login_as(self, user):
self.client.post(
return self.client.post(
reverse('auth_login'), {'login': user.username,
'password': 'secret'}
'password': self.user_password}
)

View File

@ -1,5 +1,7 @@
"""Copied from latest django/utils/http.py::is_safe_url
"""
from __future__ import unicode_literals
import unicodedata
import urlparse
import json

View File

@ -46,6 +46,7 @@ from seahub.views.ajax import (get_related_users_by_org_repo,
get_related_users_by_repo)
from seahub.views import get_system_default_repo_id, gen_path_link
from seahub.forms import SetUserQuotaForm, AddUserForm, BatchAddUserForm
from seahub.options.models import UserOptions
from seahub.profile.models import Profile, DetailedProfile
from seahub.signals import repo_deleted
from seahub.share.models import FileShare, UploadLinkShare
@ -1236,7 +1237,9 @@ def user_reset(request, email):
new_password = INIT_PASSWD
user.set_password(new_password)
user.save()
clear_token(user.username)
UserOptions.objects.set_force_passwd_change(user.username)
if IS_EMAIL_CONFIGURED:
if SEND_EMAIL_ON_RESETTING_USER_PASSWD:
@ -1309,6 +1312,7 @@ def user_add(request):
if user:
User.objects.update_role(email, role)
UserOptions.objects.set_force_passwd_change(email)
if request.user.org:
org_id = request.user.org.org_id

View File

@ -0,0 +1,25 @@
from django.conf import settings
from django.core.urlresolvers import reverse
from seahub.options.models import UserOptions
from seahub.test_utils import BaseTestCase
class LoginTest(BaseTestCase):
def test_can_login(self):
resp = self.login_as(self.user)
self.assertEqual(302, resp.status_code)
self.assertRedirects(resp, settings.LOGIN_REDIRECT_URL)
def test_force_passwd_change_when_login(self):
UserOptions.objects.set_force_passwd_change(self.user.username)
resp = self.login_as(self.user)
self.assertEqual(302, resp.status_code)
self.assertRedirects(resp, '/accounts/password/change/')
resp = self.client.get(reverse('auth_password_change'))
self.assertEqual(200, resp.status_code)
self.assertEqual(resp.context['force_passwd_change'], True)

View File

@ -0,0 +1,26 @@
from django.core.urlresolvers import reverse
from seahub.test_utils import BaseTestCase
class PasswordChangeTest(BaseTestCase):
def test_can_render(self):
self.login_as(self.user)
resp = self.client.get(reverse('auth_password_change'))
self.assertEqual(200, resp.status_code)
self.assertContains(resp, 'Password Modification')
def test_can_change(self):
self.login_as(self.user)
resp = self.client.post(
reverse('auth_password_change'), {
'old_password': self.user_password,
'new_password1': '123',
'new_password2': '123',
}
)
self.assertEqual(302, resp.status_code)
self.assertRedirects(resp, reverse('auth_password_change_done'))

View File

@ -6,9 +6,11 @@ from django.http.cookie import parse_cookie
from tests.common.utils import randstring
from seahub.base.accounts import User
from seahub.utils.ms_excel import write_xls as real_write_xls
from seahub.test_utils import BaseTestCase
from seahub.options.models import (UserOptions, KEY_FORCE_PASSWD_CHANGE,
VAL_FORCE_PASSWD_CHANGE)
from seahub.share.models import FileShare
from seahub.test_utils import BaseTestCase
from seahub.utils.ms_excel import write_xls as real_write_xls
from seaserv import ccnet_threaded_rpc, seafile_api
@ -50,6 +52,9 @@ class UserResetTest(BaseTestCase):
self.login_as(self.admin)
def test_can_reset(self):
assert len(UserOptions.objects.filter(
email=self.user.username, option_key=KEY_FORCE_PASSWD_CHANGE)) == 0
old_passwd = self.user.enc_password
resp = self.client.post(
reverse('user_reset', args=[self.user.email])
@ -58,7 +63,9 @@ class UserResetTest(BaseTestCase):
u = User.objects.get(email=self.user.username)
assert u.enc_password != old_passwd
assert UserOptions.objects.get(
email=self.user.username,
option_key=KEY_FORCE_PASSWD_CHANGE).option_val == VAL_FORCE_PASSWD_CHANGE
class BatchUserMakeAdminTest(BaseTestCase):
def setUp(self):
@ -98,6 +105,29 @@ class BatchUserMakeAdminTest(BaseTestCase):
# assert u.enc_password == old_passwd
class UserAddTest(BaseTestCase):
def setUp(self):
self.new_user = 'new_user@test.com'
self.login_as(self.admin)
self.remove_user(self.new_user)
def test_can_add(self):
assert len(UserOptions.objects.filter(
email=self.new_user, option_key=KEY_FORCE_PASSWD_CHANGE)) == 0
resp = self.client.post(
reverse('user_add',), {
'email': self.new_user,
'password1': '123',
'password2': '123',
}, HTTP_X_REQUESTED_WITH='XMLHttpRequest'
)
self.assertEqual(200, resp.status_code)
assert UserOptions.objects.get(
email=self.new_user,
option_key=KEY_FORCE_PASSWD_CHANGE).option_val == VAL_FORCE_PASSWD_CHANGE
class UserRemoveTest(BaseTestCase):
def setUp(self):
self.login_as(self.admin)