Add a requirement for the current password to change the user's password or email address
This commit is contained in:
parent
6c60e078fc
commit
1e7e012b92
5 changed files with 55 additions and 8 deletions
|
@ -117,6 +117,10 @@ class User(ApiResource):
|
||||||
'type': 'object',
|
'type': 'object',
|
||||||
'description': 'Fields which can be updated in a user.',
|
'description': 'Fields which can be updated in a user.',
|
||||||
'properties': {
|
'properties': {
|
||||||
|
'current_password': {
|
||||||
|
'type': 'string',
|
||||||
|
'description': 'The user\'s current password',
|
||||||
|
},
|
||||||
'password': {
|
'password': {
|
||||||
'type': 'string',
|
'type': 'string',
|
||||||
'description': 'The user\'s password',
|
'description': 'The user\'s password',
|
||||||
|
@ -152,8 +156,22 @@ class User(ApiResource):
|
||||||
user = get_authenticated_user()
|
user = get_authenticated_user()
|
||||||
user_data = request.get_json()
|
user_data = request.get_json()
|
||||||
|
|
||||||
try:
|
def verify_current_password(user, user_data):
|
||||||
|
current_password = user_data.get('current_password', '')
|
||||||
|
|
||||||
|
verified = False
|
||||||
|
try:
|
||||||
|
verified = model.verify_user(user.username, current_password)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
if not verified:
|
||||||
|
raise request_error(message='Current password does not match')
|
||||||
|
|
||||||
|
try:
|
||||||
if 'password' in user_data:
|
if 'password' in user_data:
|
||||||
|
verify_current_password(user, user_data)
|
||||||
|
|
||||||
logger.debug('Changing password for user: %s', user.username)
|
logger.debug('Changing password for user: %s', user.username)
|
||||||
log_action('account_change_password', user.username)
|
log_action('account_change_password', user.username)
|
||||||
model.change_password(user, user_data['password'])
|
model.change_password(user, user_data['password'])
|
||||||
|
@ -163,6 +181,8 @@ class User(ApiResource):
|
||||||
model.change_invoice_email(user, user_data['invoice_email'])
|
model.change_invoice_email(user, user_data['invoice_email'])
|
||||||
|
|
||||||
if 'email' in user_data and user_data['email'] != user.email:
|
if 'email' in user_data and user_data['email'] != user.email:
|
||||||
|
verify_current_password(user, user_data)
|
||||||
|
|
||||||
new_email = user_data['email']
|
new_email = user_data['email']
|
||||||
if model.find_user_by_email(new_email):
|
if model.find_user_by_email(new_email):
|
||||||
# Email already used.
|
# Email already used.
|
||||||
|
|
|
@ -384,7 +384,7 @@ quayApp = angular.module('quay', quayDependencies, function($provide, cfpLoading
|
||||||
var uiService = {};
|
var uiService = {};
|
||||||
|
|
||||||
uiService.hidePopover = function(elem) {
|
uiService.hidePopover = function(elem) {
|
||||||
var popover = $('#signupButton').data('bs.popover');
|
var popover = $(elem).data('bs.popover');
|
||||||
if (popover) {
|
if (popover) {
|
||||||
popover.hide();
|
popover.hide();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1763,6 +1763,7 @@ function UserAdminCtrl($scope, $timeout, $location, ApiService, PlanService, Use
|
||||||
|
|
||||||
// Reset the form.
|
// Reset the form.
|
||||||
delete $scope.cuser['repeatEmail'];
|
delete $scope.cuser['repeatEmail'];
|
||||||
|
delete $scope.cuser['current_password'];
|
||||||
|
|
||||||
$scope.changeEmailForm.$setPristine();
|
$scope.changeEmailForm.$setPristine();
|
||||||
}, function(result) {
|
}, function(result) {
|
||||||
|
@ -1784,6 +1785,7 @@ function UserAdminCtrl($scope, $timeout, $location, ApiService, PlanService, Use
|
||||||
// Reset the form
|
// Reset the form
|
||||||
delete $scope.cuser['password']
|
delete $scope.cuser['password']
|
||||||
delete $scope.cuser['repeatPassword']
|
delete $scope.cuser['repeatPassword']
|
||||||
|
delete $scope.cuser['current_password'];
|
||||||
|
|
||||||
$scope.changePasswordForm.$setPristine();
|
$scope.changePasswordForm.$setPristine();
|
||||||
|
|
||||||
|
|
|
@ -128,6 +128,8 @@
|
||||||
<div class="panel-body">
|
<div class="panel-body">
|
||||||
<form class="form-change col-md-6" id="changeEmailForm" name="changeEmailForm" ng-submit="changeEmail()"
|
<form class="form-change col-md-6" id="changeEmailForm" name="changeEmailForm" ng-submit="changeEmail()"
|
||||||
ng-show="!awaitingConfirmation && !registering">
|
ng-show="!awaitingConfirmation && !registering">
|
||||||
|
<input type="password" class="form-control" placeholder="Your current password" ng-model="cuser.current_password" required
|
||||||
|
ng-pattern="/^.{8,}$/">
|
||||||
<input type="email" class="form-control" placeholder="Your new e-mail address" ng-model="cuser.email" required>
|
<input type="email" class="form-control" placeholder="Your new e-mail address" ng-model="cuser.email" required>
|
||||||
<button class="btn btn-primary" ng-disabled="changeEmailForm.$invalid || cuser.email == user.email" type="submit">Change E-mail Address</button>
|
<button class="btn btn-primary" ng-disabled="changeEmailForm.$invalid || cuser.email == user.email" type="submit">Change E-mail Address</button>
|
||||||
</form>
|
</form>
|
||||||
|
@ -138,18 +140,21 @@
|
||||||
|
|
||||||
<!-- Change password tab -->
|
<!-- Change password tab -->
|
||||||
<div id="password" class="tab-pane">
|
<div id="password" class="tab-pane">
|
||||||
<div class="loading" ng-show="updatingUser">
|
|
||||||
<div class="quay-spinner 3x"></div>
|
|
||||||
</div>
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="panel">
|
<div class="panel">
|
||||||
<div class="panel-title">Change Password</div>
|
<div class="panel-title">Change Password</div>
|
||||||
|
|
||||||
|
<div class="loading" ng-show="updatingUser">
|
||||||
|
<div class="quay-spinner 3x"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<span class="help-block" ng-show="changePasswordSuccess">Password changed successfully</span>
|
<span class="help-block" ng-show="changePasswordSuccess">Password changed successfully</span>
|
||||||
|
|
||||||
<div ng-show="!updatingUser" class="panel-body">
|
<div ng-show="!updatingUser" class="panel-body">
|
||||||
<form class="form-change col-md-6" id="changePasswordForm" name="changePasswordForm" ng-submit="changePassword()"
|
<form class="form-change col-md-6" id="changePasswordForm" name="changePasswordForm" ng-submit="changePassword()"
|
||||||
ng-show="!awaitingConfirmation && !registering">
|
ng-show="!awaitingConfirmation && !registering">
|
||||||
|
<input type="password" class="form-control" placeholder="Your current password" ng-model="cuser.current_password" required
|
||||||
|
ng-pattern="/^.{8,}$/">
|
||||||
<input type="password" class="form-control" placeholder="Your new password" ng-model="cuser.password" required
|
<input type="password" class="form-control" placeholder="Your new password" ng-model="cuser.password" required
|
||||||
ng-pattern="/^.{8,}$/">
|
ng-pattern="/^.{8,}$/">
|
||||||
<input type="password" class="form-control" placeholder="Verify your new password" ng-model="cuser.repeatPassword"
|
<input type="password" class="form-control" placeholder="Verify your new password" ng-model="cuser.repeatPassword"
|
||||||
|
|
|
@ -172,14 +172,14 @@ class TestCSRFFailure(ApiTestCase):
|
||||||
|
|
||||||
# Make sure a simple post call succeeds.
|
# Make sure a simple post call succeeds.
|
||||||
self.putJsonResponse(User,
|
self.putJsonResponse(User,
|
||||||
data=dict(password='newpasswordiscool'))
|
data=dict(password='newpasswordiscool', current_password='password'))
|
||||||
|
|
||||||
# Change the session's CSRF token.
|
# Change the session's CSRF token.
|
||||||
self.setCsrfToken('someinvalidtoken')
|
self.setCsrfToken('someinvalidtoken')
|
||||||
|
|
||||||
# Verify that the call now fails.
|
# Verify that the call now fails.
|
||||||
self.putJsonResponse(User,
|
self.putJsonResponse(User,
|
||||||
data=dict(password='newpasswordiscool'),
|
data=dict(password='newpasswordiscool', current_password='password'),
|
||||||
expected_code=403)
|
expected_code=403)
|
||||||
|
|
||||||
|
|
||||||
|
@ -325,8 +325,28 @@ class TestChangeUserDetails(ApiTestCase):
|
||||||
def test_changepassword(self):
|
def test_changepassword(self):
|
||||||
self.login(READ_ACCESS_USER)
|
self.login(READ_ACCESS_USER)
|
||||||
self.putJsonResponse(User,
|
self.putJsonResponse(User,
|
||||||
data=dict(password='newpasswordiscool'))
|
data=dict(password='newpasswordiscool', current_password='password'))
|
||||||
self.login(READ_ACCESS_USER, password='newpasswordiscool')
|
self.login(READ_ACCESS_USER, password='newpasswordiscool')
|
||||||
|
|
||||||
|
def test_changepassword_invalidpasswor(self):
|
||||||
|
self.login(READ_ACCESS_USER)
|
||||||
|
self.putJsonResponse(User,
|
||||||
|
data=dict(password='newpasswordiscool', current_password='notcorrect'),
|
||||||
|
expected_code=400)
|
||||||
|
|
||||||
|
def test_changeeemail(self):
|
||||||
|
self.login(READ_ACCESS_USER)
|
||||||
|
|
||||||
|
self.putJsonResponse(User,
|
||||||
|
data=dict(email='test+foo@devtable.com', current_password='password'))
|
||||||
|
|
||||||
|
def test_changeeemail_invalidpassword(self):
|
||||||
|
self.login(READ_ACCESS_USER)
|
||||||
|
|
||||||
|
self.putJsonResponse(User,
|
||||||
|
data=dict(email='test+foo@devtable.com', current_password='notcorrect'),
|
||||||
|
expected_code=400)
|
||||||
|
|
||||||
|
|
||||||
def test_changeinvoiceemail(self):
|
def test_changeinvoiceemail(self):
|
||||||
self.login(READ_ACCESS_USER)
|
self.login(READ_ACCESS_USER)
|
||||||
|
|
Reference in a new issue