Merge pull request #2314 from FinnStutzenstein/Issue2137
new change password view (fixes #2137)
This commit is contained in:
commit
29f9203377
@ -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']))
|
||||
|
@ -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',
|
||||
|
@ -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>
|
@ -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>"
|
||||
|
@ -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):
|
||||
|
@ -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):
|
||||
|
Loading…
Reference in New Issue
Block a user