Merge pull request #2314 from FinnStutzenstein/Issue2137

new change password view (fixes #2137)
This commit is contained in:
Norman Jäckel 2016-09-05 13:46:15 +02:00 committed by GitHub
commit 29f9203377
6 changed files with 144 additions and 28 deletions

View File

@ -124,7 +124,7 @@ def users_passwords_to_pdf(pdf):
stylesheet['formfield']))
cell2.append(Paragraph(escape(user.username),
stylesheet['formfield_value']))
cell2.append(Paragraph("%s:" % _("Password"),
cell2.append(Paragraph("%s:" % _("Initial password"),
stylesheet['formfield']))
cell2.append(Paragraph(escape(user.default_password),
stylesheet['formfield_value']))

View File

@ -92,6 +92,16 @@ angular.module('OpenSlidesApp.users.site', ['OpenSlidesApp.users'])
url: '/password',
controller: 'UserPasswordCtrl',
})
.state('users.user.change-password', {
url: '/change-password/{id}',
controller: 'UserChangePasswordCtrl',
templateUrl: 'static/templates/users/user-change-password.html',
resolve: {
user: function(User, $stateParams) {
return User.find($stateParams.id);
}
}
})
.state('users.user.import', {
url: '/import',
controller: 'UserImportCtrl',
@ -229,6 +239,24 @@ angular.module('OpenSlidesApp.users.site', ['OpenSlidesApp.users'])
}
])
.factory('PasswordGenerator', [
function () {
return {
generate: function (length) {
if (!length) {
length = 8;
}
var chars = 'abcdefghijkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789',
pw = '';
for (var i = 0; i < length; ++i) {
pw += chars.charAt(Math.floor(Math.random() * chars.length));
}
return pw;
}
};
}
])
// Service for generic assignment form (create and update)
.factory('UserForm', [
'$http',
@ -236,7 +264,8 @@ angular.module('OpenSlidesApp.users.site', ['OpenSlidesApp.users'])
'Editor',
'Group',
'Mediafile',
function ($http, gettextCatalog, Editor, Group, Mediafile) {
'PasswordGenerator',
function ($http, gettextCatalog, Editor, Group, Mediafile, PasswordGenerator) {
return {
// ngDialog for user form
getDialog: function (user) {
@ -318,21 +347,17 @@ angular.module('OpenSlidesApp.users.site', ['OpenSlidesApp.users'])
key: 'default_password',
type: 'input',
templateOptions: {
label: gettextCatalog.getString('Default password'),
description: '',
addonRight: { text: 'Reset', class: 'fa fa-undo', onClick:
function (options, scope) {
$http.post(
'/rest/users/user/' + scope.model.id + '/reset_password/',
{'password': scope.model.default_password})
.then(function() {
options.templateOptions.description =
gettextCatalog.getString('Password successfully resetted to:') +
' ' + scope.model.default_password;
});
label: gettextCatalog.getString('Initial password'),
description: gettextCatalog.getString('Initial password can not be changed.'),
addonRight: {
text: gettextCatalog.getString('Generate'),
class: 'fa fa-magic',
onClick:function (options, scope) {
scope.$parent.model.default_password = PasswordGenerator.generate();
}
}
}
},
hide: !hideOnCreateForm
},
{
key: 'comment',
@ -616,6 +641,39 @@ angular.module('OpenSlidesApp.users.site', ['OpenSlidesApp.users'])
}
])
.controller('UserChangePasswordCtrl', [
'$scope',
'$state',
'$http',
'User',
'user',
'gettextCatalog',
'PasswordGenerator',
function($scope, $state, $http, User, user, gettextCatalog, PasswordGenerator) {
User.bindOne(user.id, $scope, 'user');
$scope.alert={};
$scope.generatePassword = function () {
$scope.new_password = PasswordGenerator.generate();
};
$scope.save = function (user) {
if ($scope.new_password !== '') {
$http.post(
'/rest/users/user/' + user.id + '/reset_password/',
{'password': $scope.new_password}
).then(
function (success) {
$scope.alert = {type: 'success', msg: success.data.detail, show: true};
$scope.new_password = '';
},
function (error) {
$scope.alert = {type: 'danger', msg: error.data.detail, show: true};
}
);
}
};
}
])
.controller('UserPasswordCtrl', [
'$scope',
'$state',

View File

@ -0,0 +1,50 @@
<div class="header">
<div class="title">
<div class="submenu">
<a ui-sref="users.user.list" class="btn btn-sm btn-default">
<i class="fa fa-angle-double-left fa-lg"></i>
<translate>Back to overview</translate>
</a>
</div>
<h1 translate>Change password for {{ user.get_short_name() }}</h1>
</div>
</div>
<div class="details">
<form name="userForm">
<h3 class="text-danger" style="margin-top: 0;">
<i class="fa fa-exclamation-triangle"></i>
<translate>You override the personally set password of</translate> {{ user.get_short_name() }}!
</h3>
<uib-alert ng-show="alert.show" type="{{ alert.type }}" ng-click="alert={}" close="alert={}">
{{ alert.msg }}
</uib-alert>
<div class="form-group">
<label for="inputOldPassword" translate>New password</label>
<div class="input-group">
<input type="text"
ng-model="new_password"
class="form-control"
name="inputNewPassword"
required>
</input>
<div class="input-group-addon pointer" ng-click="generatePassword()">
<i class="fa fa-magic"></i>
<translate>Generate</translate>
</div>
</div>
<div class="spacer-top">
<span uib-tooltip="{{ 'Initial password can not be changed.' | translate }}">
<i class="fa fa-info-circle"></i>
<translate>Initial password</translate>: {{ user.default_password }}
</span>
</div>
</div>
<button type="submit" ng-click="save(user)" class="btn btn-primary" translate>
Change password
</button>
<button ui-sref="users.user.list" class="btn btn-default" translate>
Cancel
</button>
</form>
</div>

View File

@ -199,6 +199,7 @@
<div ng-if="user.number"> {{ user.number }} </div>
<div os-perms="users.can_manage" class="hoverActions" ng-class="{'hiddenDiv': !user.hover}">
<a href="" ng-click="openDialog(user)" translate>Edit</a> |
<a ui-sref="users.user.change-password({id: user.id})" translate>Change password</a> |
<a href="" class="text-danger"
ng-bootbox-confirm="{{ 'Are you sure you want to delete this entry?' | translate }}<br>
<b>{{ user.get_short_name() }}</b>"

View File

@ -161,11 +161,12 @@ class UserViewSet(ModelViewSet):
View to reset the password using the requested password.
"""
user = self.get_object()
if request.data.get('password'):
user.default_password = request.data['password']
user.set_password(user.default_password)
user.save()
return Response({'detail': _('Password successfully reset.')})
if isinstance(request.data.get('password'), str):
user.set_password(request.data.get('password'))
user.save()
return Response({'detail': _('Password successfully reset.')})
else:
raise ValidationError({'detail': 'Password has to be a string.'})
class GroupViewSetMetadata(SimpleMetadata):

View File

@ -4,6 +4,7 @@ from rest_framework.test import APIClient
from openslides.core.config import config
from openslides.users.models import Group, User
from openslides.users.serializers import UserFullSerializer
from openslides.utils.test import TestCase
@ -172,17 +173,22 @@ class UserResetPassword(TestCase):
self.assertTrue(User.objects.get(pk=user.pk).check_password(
'new_password_Yuuh8OoQueePahngohy3_new'))
def test_reset_to_default(self):
"""
Tests whether a random password is set as default and actual password
if no default password is provided.
"""
def test_set_random_initial_password(self):
admin_client = APIClient()
admin_client.login(username='admin', password='admin')
user = User.objects.create(username='Test name ooMoa4ou4mohn2eo1ree')
user.default_password = 'new_password_Yuuh8OoQueePahngohy3'
serializer = UserFullSerializer()
user = serializer.create({'username': 'Test name 9gt043qwvnj2d0cr'})
user.save()
response = admin_client.post(
reverse('user-reset-password', args=[user.pk]), {})
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertTrue(User.objects.get(pk=user.pk).check_password(
'new_password_Yuuh8OoQueePahngohy3'))
default_password = User.objects.get(pk=user.pk).default_password
self.assertIsNotNone(default_password)
self.assertEqual(len(default_password), 8)
self.assertTrue(User.objects.get(pk=user.pk).check_password(default_password))
class GroupMetadata(TestCase):