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'])) stylesheet['formfield']))
cell2.append(Paragraph(escape(user.username), cell2.append(Paragraph(escape(user.username),
stylesheet['formfield_value'])) stylesheet['formfield_value']))
cell2.append(Paragraph("%s:" % _("Password"), cell2.append(Paragraph("%s:" % _("Initial password"),
stylesheet['formfield'])) stylesheet['formfield']))
cell2.append(Paragraph(escape(user.default_password), cell2.append(Paragraph(escape(user.default_password),
stylesheet['formfield_value'])) stylesheet['formfield_value']))

View File

@ -92,6 +92,16 @@ angular.module('OpenSlidesApp.users.site', ['OpenSlidesApp.users'])
url: '/password', url: '/password',
controller: 'UserPasswordCtrl', 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', { .state('users.user.import', {
url: '/import', url: '/import',
controller: 'UserImportCtrl', 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) // Service for generic assignment form (create and update)
.factory('UserForm', [ .factory('UserForm', [
'$http', '$http',
@ -236,7 +264,8 @@ angular.module('OpenSlidesApp.users.site', ['OpenSlidesApp.users'])
'Editor', 'Editor',
'Group', 'Group',
'Mediafile', 'Mediafile',
function ($http, gettextCatalog, Editor, Group, Mediafile) { 'PasswordGenerator',
function ($http, gettextCatalog, Editor, Group, Mediafile, PasswordGenerator) {
return { return {
// ngDialog for user form // ngDialog for user form
getDialog: function (user) { getDialog: function (user) {
@ -318,21 +347,17 @@ angular.module('OpenSlidesApp.users.site', ['OpenSlidesApp.users'])
key: 'default_password', key: 'default_password',
type: 'input', type: 'input',
templateOptions: { templateOptions: {
label: gettextCatalog.getString('Default password'), label: gettextCatalog.getString('Initial password'),
description: '', description: gettextCatalog.getString('Initial password can not be changed.'),
addonRight: { text: 'Reset', class: 'fa fa-undo', onClick: addonRight: {
function (options, scope) { text: gettextCatalog.getString('Generate'),
$http.post( class: 'fa fa-magic',
'/rest/users/user/' + scope.model.id + '/reset_password/', onClick:function (options, scope) {
{'password': scope.model.default_password}) scope.$parent.model.default_password = PasswordGenerator.generate();
.then(function() {
options.templateOptions.description =
gettextCatalog.getString('Password successfully resetted to:') +
' ' + scope.model.default_password;
});
} }
} }
} },
hide: !hideOnCreateForm
}, },
{ {
key: 'comment', 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', [ .controller('UserPasswordCtrl', [
'$scope', '$scope',
'$state', '$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 ng-if="user.number"> {{ user.number }} </div>
<div os-perms="users.can_manage" class="hoverActions" ng-class="{'hiddenDiv': !user.hover}"> <div os-perms="users.can_manage" class="hoverActions" ng-class="{'hiddenDiv': !user.hover}">
<a href="" ng-click="openDialog(user)" translate>Edit</a> | <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" <a href="" class="text-danger"
ng-bootbox-confirm="{{ 'Are you sure you want to delete this entry?' | translate }}<br> ng-bootbox-confirm="{{ 'Are you sure you want to delete this entry?' | translate }}<br>
<b>{{ user.get_short_name() }}</b>" <b>{{ user.get_short_name() }}</b>"

View File

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

View File

@ -4,6 +4,7 @@ from rest_framework.test import APIClient
from openslides.core.config import config from openslides.core.config import config
from openslides.users.models import Group, User from openslides.users.models import Group, User
from openslides.users.serializers import UserFullSerializer
from openslides.utils.test import TestCase from openslides.utils.test import TestCase
@ -172,17 +173,22 @@ class UserResetPassword(TestCase):
self.assertTrue(User.objects.get(pk=user.pk).check_password( self.assertTrue(User.objects.get(pk=user.pk).check_password(
'new_password_Yuuh8OoQueePahngohy3_new')) '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 = APIClient()
admin_client.login(username='admin', password='admin') 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() user.save()
response = admin_client.post(
reverse('user-reset-password', args=[user.pk]), {}) default_password = User.objects.get(pk=user.pk).default_password
self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertIsNotNone(default_password)
self.assertTrue(User.objects.get(pk=user.pk).check_password( self.assertEqual(len(default_password), 8)
'new_password_Yuuh8OoQueePahngohy3')) self.assertTrue(User.objects.get(pk=user.pk).check_password(default_password))
class GroupMetadata(TestCase): class GroupMetadata(TestCase):