Add a form for changing the password and prompt the user to do so when there is no password on the account.
This commit is contained in:
parent
e016d5822f
commit
16ee147eae
7 changed files with 98 additions and 10 deletions
|
@ -4,8 +4,7 @@ import dateutil.parser
|
|||
import operator
|
||||
|
||||
from database import *
|
||||
from util.validation import (validate_email, validate_username,
|
||||
validate_password)
|
||||
from util.validation import *
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
@ -35,9 +34,7 @@ def create_user(username, password, email):
|
|||
|
||||
# We allow password none for the federated login case.
|
||||
if password is not None and not validate_password(password):
|
||||
raise InvalidPasswordException('Invalid password, password must be at ' +
|
||||
'least 8 characters and contain no ' +
|
||||
'whitespace.')
|
||||
raise InvalidPasswordException(INVALID_PASSWORD_MESSAGE)
|
||||
|
||||
try:
|
||||
existing = User.get((User.username == username) | (User.email == email))
|
||||
|
@ -210,6 +207,9 @@ def get_matching_repositories(repo_term, username=None):
|
|||
|
||||
|
||||
def change_password(user, new_password):
|
||||
if not validate_password(new_password):
|
||||
raise InvalidPasswordException(INVALID_PASSWORD_MESSAGE)
|
||||
|
||||
pw_hash = bcrypt.hashpw(new_password, bcrypt.gensalt())
|
||||
user.password_hash = pw_hash
|
||||
user.save()
|
||||
|
|
|
@ -51,8 +51,35 @@ def get_logged_in_user():
|
|||
'username': user.username,
|
||||
'email': user.email,
|
||||
'gravatar': compute_hash(user.email),
|
||||
'askForPassword': user.password_hash is None,
|
||||
})
|
||||
|
||||
@app.route('/api/user/', methods=['PUT'])
|
||||
@api_login_required
|
||||
def change_user_details():
|
||||
user = current_user.db_user
|
||||
|
||||
user_data = request.get_json();
|
||||
|
||||
try:
|
||||
if user_data['password']:
|
||||
logger.debug('Changing password for user: %s', user.username)
|
||||
model.change_password(user, user_data['password'])
|
||||
except model.InvalidPasswordException, ex:
|
||||
error_resp = jsonify({
|
||||
'message': ex.message,
|
||||
})
|
||||
error_resp.status_code = 400
|
||||
return error_resp
|
||||
|
||||
return jsonify({
|
||||
'verified': user.verified,
|
||||
'anonymous': False,
|
||||
'username': user.username,
|
||||
'email': user.email,
|
||||
'gravatar': compute_hash(user.email),
|
||||
'askForPassword': user.password_hash is None,
|
||||
})
|
||||
|
||||
@app.route('/api/user/', methods=['POST'])
|
||||
def create_user_api():
|
||||
|
@ -60,7 +87,7 @@ def create_user_api():
|
|||
existing_user = model.get_user(user_data['username'])
|
||||
if existing_user:
|
||||
error_resp = jsonify({
|
||||
'message': 'The username already exists'
|
||||
'message': 'The username already exists'
|
||||
})
|
||||
error_resp.status_code = 400
|
||||
return error_resp
|
||||
|
|
|
@ -137,11 +137,11 @@
|
|||
margin-left: 0px;
|
||||
}
|
||||
|
||||
.form-signup input.ng-invalid.ng-dirty {
|
||||
form input.ng-invalid.ng-dirty {
|
||||
background-color: #FDD7D9;
|
||||
}
|
||||
|
||||
.form-signup input.ng-valid.ng-dirty {
|
||||
form input.ng-valid.ng-dirty {
|
||||
background-color: #DDFFEE;
|
||||
}
|
||||
|
||||
|
@ -689,6 +689,11 @@ p.editable:hover i {
|
|||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.user-admin .form-change-pw input {
|
||||
margin-top: 12px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
#image-history-container .node circle {
|
||||
cursor: pointer;
|
||||
fill: #fff;
|
||||
|
|
|
@ -5,7 +5,8 @@ quayApp = angular.module('quay', ['restangular', 'angularMoment', 'angulartics',
|
|||
verified: false,
|
||||
anonymous: true,
|
||||
username: null,
|
||||
email: null
|
||||
email: null,
|
||||
askForPassword: false,
|
||||
}
|
||||
|
||||
var userService = {}
|
||||
|
|
|
@ -456,9 +456,13 @@ function RepoAdminCtrl($scope, Restangular, $routeParams, $rootScope) {
|
|||
});
|
||||
}
|
||||
|
||||
function UserAdminCtrl($scope, Restangular, PlanService, KeyService, $routeParams) {
|
||||
function UserAdminCtrl($scope, $timeout, Restangular, PlanService, UserService, KeyService, $routeParams) {
|
||||
$scope.plans = PlanService.planList();
|
||||
|
||||
$scope.$watch(function () { return UserService.currentUser(); }, function (currentUser) {
|
||||
$scope.askForPassword = currentUser.askForPassword;
|
||||
}, true);
|
||||
|
||||
var subscribedToPlan = function(sub) {
|
||||
$scope.subscription = sub;
|
||||
$scope.subscribedPlan = PlanService.getPlan(sub.plan);
|
||||
|
@ -545,4 +549,28 @@ function UserAdminCtrl($scope, Restangular, PlanService, KeyService, $routeParam
|
|||
$scope.subscribe(requested);
|
||||
}
|
||||
}
|
||||
|
||||
$scope.updatingUser = false;
|
||||
$scope.changePasswordSuccess = false;
|
||||
$('.form-change-pw').popover();
|
||||
|
||||
$scope.changePassword = function() {
|
||||
$('.form-change-pw').popover('hide');
|
||||
$scope.updatingUser = true;
|
||||
$scope.changePasswordSuccess = false;
|
||||
var changePasswordPost = Restangular.one('user/');
|
||||
changePasswordPost.customPUT($scope.user).then(function() {
|
||||
$scope.updatingUser = false;
|
||||
$scope.changePasswordSuccess = true;
|
||||
|
||||
UserService.load();
|
||||
}, function(result) {
|
||||
$scope.updatingUser = false;
|
||||
|
||||
$scope.changePasswordError = result.data.message;
|
||||
$timeout(function() {
|
||||
$('.form-change-pw').popover('show');
|
||||
});
|
||||
});
|
||||
};
|
||||
}
|
|
@ -7,6 +7,11 @@
|
|||
<div class="alert alert-danger">{{ errorMessage }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row" ng-show="askForPassword">
|
||||
<div class="col-md-12">
|
||||
<div class="alert alert-warning">Your account does not currently have a password. You will need to create a password before you will be able to <strong>push</strong> or <strong>pull</strong> repositories.</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row" ng-hide="planLoading">
|
||||
<div class="col-md-3" ng-repeat='plan in plans'>
|
||||
<div class="panel" ng-class="{'panel-success': subscription.plan == plan.stripeId, 'panel-default': subscription.plan != plan.stripeId}">
|
||||
|
@ -52,4 +57,24 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="loading" ng-show="updatingUser">
|
||||
<i class="icon-spinner icon-spin icon-3x"></i>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
Change Password
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<form class="form-change-pw" name="changePasswordForm" ng-submit="changePassword()" data-trigger="manual" data-content="{{ changePasswordError }}" data-placement="right" ng-show="!awaitingConfirmation && !registering">
|
||||
<input type="password" class="form-control" placeholder="Your new password" ng-model="user.password" required>
|
||||
<input type="password" class="form-control" placeholder="Verify your new password" ng-model="user.repeatePassword" match="user.password" required>
|
||||
<button class="btn btn-danger" ng-disabled="changePasswordForm.$invalid" type="submit" analytics-on analytics-event="register">Change Password</button>
|
||||
<span class="help-block" ng-show="changePasswordSuccess">Password changed successfully</span>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
import re
|
||||
import urllib
|
||||
|
||||
INVALID_PASSWORD_MESSAGE = 'Invalid password, password must be at least ' + \
|
||||
'8 characters and contain no whitespace.'
|
||||
|
||||
def validate_email(email_address):
|
||||
if re.match(r'[^@]+@[^@]+\.[^@]+', email_address):
|
||||
|
|
Reference in a new issue