Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
E
edx-platform
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
edx
edx-platform
Commits
9b50b816
Commit
9b50b816
authored
Jul 31, 2017
by
tasawernawaz
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
length complexity added on password reset page
LEARNER-1492
parent
75021b35
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
70 additions
and
19 deletions
+70
-19
common/djangoapps/student/tests/test_reset_password.py
+30
-0
common/djangoapps/student/views.py
+40
-19
No files found.
common/djangoapps/student/tests/test_reset_password.py
View file @
9b50b816
...
@@ -13,6 +13,7 @@ from django.contrib.auth.tokens import default_token_generator
...
@@ -13,6 +13,7 @@ from django.contrib.auth.tokens import default_token_generator
from
django.core.cache
import
cache
from
django.core.cache
import
cache
from
django.core.urlresolvers
import
reverse
from
django.core.urlresolvers
import
reverse
from
django.test.client
import
RequestFactory
from
django.test.client
import
RequestFactory
from
django.test.utils
import
override_settings
from
django.utils.http
import
int_to_base36
from
django.utils.http
import
int_to_base36
from
edx_oauth2_provider.tests.factories
import
AccessTokenFactory
,
ClientFactory
,
RefreshTokenFactory
from
edx_oauth2_provider.tests.factories
import
AccessTokenFactory
,
ClientFactory
,
RefreshTokenFactory
from
mock
import
Mock
,
patch
from
mock
import
Mock
,
patch
...
@@ -287,6 +288,35 @@ class ResetPasswordTests(EventTestMixin, CacheIsolationTestCase):
...
@@ -287,6 +288,35 @@ class ResetPasswordTests(EventTestMixin, CacheIsolationTestCase):
self
.
assertEqual
(
resp
.
status_code
,
200
)
self
.
assertEqual
(
resp
.
status_code
,
200
)
self
.
assertFalse
(
User
.
objects
.
get
(
pk
=
self
.
user
.
pk
)
.
is_active
)
self
.
assertFalse
(
User
.
objects
.
get
(
pk
=
self
.
user
.
pk
)
.
is_active
)
@override_settings
(
PASSWORD_MIN_LENGTH
=
2
)
@override_settings
(
PASSWORD_MAX_LENGTH
=
10
)
@ddt.data
(
{
'password'
:
'1'
,
'error_message'
:
'Password: Invalid Length (must be 2 characters or more)'
,
},
{
'password'
:
'01234567891'
,
'error_message'
:
'Password: Invalid Length (must be 10 characters or fewer)'
}
)
def
test_password_reset_with_invalid_length
(
self
,
password_dict
):
"""Tests that if we provide password characters less then PASSWORD_MIN_LENGTH,
or more than PASSWORD_MAX_LENGTH, password reset will fail with error message.
"""
url
=
reverse
(
'password_reset_confirm'
,
kwargs
=
{
'uidb36'
:
self
.
uidb36
,
'token'
:
self
.
token
}
)
request_params
=
{
'new_password1'
:
password_dict
[
'password'
],
'new_password2'
:
password_dict
[
'password'
]}
confirm_request
=
self
.
request_factory
.
post
(
url
,
data
=
request_params
)
# Make a password reset request with minimum/maximum passwords characters.
response
=
password_reset_confirm_wrapper
(
confirm_request
,
self
.
uidb36
,
self
.
token
)
self
.
assertEqual
(
response
.
context_data
[
'err_msg'
],
password_dict
[
'error_message'
])
@patch
(
'student.views.password_reset_confirm'
)
@patch
(
'student.views.password_reset_confirm'
)
@patch
(
"openedx.core.djangoapps.site_configuration.helpers.get_value"
,
fake_get_value
)
@patch
(
"openedx.core.djangoapps.site_configuration.helpers.get_value"
,
fake_get_value
)
def
test_reset_password_good_token_configuration_override
(
self
,
reset_confirm
):
def
test_reset_password_good_token_configuration_override
(
self
,
reset_confirm
):
...
...
common/djangoapps/student/views.py
View file @
9b50b816
...
@@ -127,7 +127,7 @@ from util.bad_request_rate_limiter import BadRequestRateLimiter
...
@@ -127,7 +127,7 @@ from util.bad_request_rate_limiter import BadRequestRateLimiter
from
util.db
import
outer_atomic
from
util.db
import
outer_atomic
from
util.json_request
import
JsonResponse
from
util.json_request
import
JsonResponse
from
util.milestones_helpers
import
get_pre_requisite_courses_not_completed
from
util.milestones_helpers
import
get_pre_requisite_courses_not_completed
from
util.password_policy_validators
import
validate_password_strength
from
util.password_policy_validators
import
validate_password_
length
,
validate_password_
strength
from
xmodule.modulestore.django
import
modulestore
from
xmodule.modulestore.django
import
modulestore
log
=
logging
.
getLogger
(
"edx.student"
)
log
=
logging
.
getLogger
(
"edx.student"
)
...
@@ -2476,7 +2476,30 @@ def uidb36_to_uidb64(uidb36):
...
@@ -2476,7 +2476,30 @@ def uidb36_to_uidb64(uidb36):
return
uidb64
return
uidb64
def
validate_password
(
user
,
password
):
def
validate_password
(
password
):
"""
Validate password overall strength if ENFORCE_PASSWORD_POLICY is enable
otherwise only validate the length of the password.
Args:
password: the user's proposed new password.
Returns:
err_msg: an error message if there's a violation of one of the password
checks. Otherwise, `None`.
"""
try
:
if
settings
.
FEATURES
.
get
(
'ENFORCE_PASSWORD_POLICY'
,
False
):
validate_password_strength
(
password
)
else
:
validate_password_length
(
password
)
except
ValidationError
as
err
:
return
_
(
'Password: '
)
+
'; '
.
join
(
err
.
messages
)
def
validate_password_security_policy
(
user
,
password
):
"""
"""
Tie in password policy enforcement as an optional level of
Tie in password policy enforcement as an optional level of
security protection
security protection
...
@@ -2486,19 +2509,11 @@ def validate_password(user, password):
...
@@ -2486,19 +2509,11 @@ def validate_password(user, password):
password: the user's proposed new password.
password: the user's proposed new password.
Returns:
Returns:
is_valid_password: a boolean indicating if the new password
passes the validation.
err_msg: an error message if there's a violation of one of the password
err_msg: an error message if there's a violation of one of the password
checks. Otherwise, `None`.
checks. Otherwise, `None`.
"""
"""
err_msg
=
None
if
settings
.
FEATURES
.
get
(
'ENFORCE_PASSWORD_POLICY'
,
False
):
try
:
validate_password_strength
(
password
)
except
ValidationError
as
err
:
err_msg
=
_
(
'Password: '
)
+
'; '
.
join
(
err
.
messages
)
err_msg
=
None
# also, check the password reuse policy
# also, check the password reuse policy
if
not
PasswordHistory
.
is_allowable_password_reuse
(
user
,
password
):
if
not
PasswordHistory
.
is_allowable_password_reuse
(
user
,
password
):
if
user
.
is_staff
:
if
user
.
is_staff
:
...
@@ -2524,9 +2539,7 @@ def validate_password(user, password):
...
@@ -2524,9 +2539,7 @@ def validate_password(user, password):
num_days
num_days
)
.
format
(
num
=
num_days
)
)
.
format
(
num
=
num_days
)
is_password_valid
=
err_msg
is
None
return
err_msg
return
is_password_valid
,
err_msg
def
password_reset_confirm_wrapper
(
request
,
uidb36
=
None
,
token
=
None
):
def
password_reset_confirm_wrapper
(
request
,
uidb36
=
None
,
token
=
None
):
...
@@ -2552,16 +2565,24 @@ def password_reset_confirm_wrapper(request, uidb36=None, token=None):
...
@@ -2552,16 +2565,24 @@ def password_reset_confirm_wrapper(request, uidb36=None, token=None):
if
request
.
method
==
'POST'
:
if
request
.
method
==
'POST'
:
password
=
request
.
POST
[
'new_password1'
]
password
=
request
.
POST
[
'new_password1'
]
is_password_valid
,
password_err_msg
=
validate_password
(
user
,
password
)
valid_link
=
False
if
not
is_password_valid
:
error_message
=
validate_password_security_policy
(
user
,
password
)
if
not
error_message
:
# if security is not violated, we need to validate password
error_message
=
validate_password
(
password
)
if
error_message
:
# password reset link will be valid if there is no security violation
valid_link
=
True
if
error_message
:
# We have a password reset attempt which violates some security
# We have a password reset attempt which violates some security
# policy. Use the existing Django template to communicate that
# policy
, or any other validation
. Use the existing Django template to communicate that
# back to the user.
# back to the user.
context
=
{
context
=
{
'validlink'
:
False
,
'validlink'
:
valid_link
,
'form'
:
None
,
'form'
:
None
,
'title'
:
_
(
'Password reset unsuccessful'
),
'title'
:
_
(
'Password reset unsuccessful'
),
'err_msg'
:
password_err_msg
,
'err_msg'
:
error_message
,
}
}
context
.
update
(
platform_name
)
context
.
update
(
platform_name
)
return
TemplateResponse
(
return
TemplateResponse
(
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment