From 328c78bc4a570a9aceaaa1a2124bacd4a0e8d295 Mon Sep 17 00:00:00 2001 From: Rigel Kent Date: Sat, 6 Oct 2018 13:54:00 +0200 Subject: allow administration to change/reset a user's password --- client/src/app/+admin/admin.module.ts | 3 +- client/src/app/+admin/users/user-edit/index.ts | 1 + .../users/user-edit/user-edit.component.html | 10 +++ .../users/user-edit/user-edit.component.scss | 9 +- .../users/user-edit/user-password.component.html | 25 ++++++ .../users/user-edit/user-password.component.scss | 21 +++++ .../users/user-edit/user-password.component.ts | 100 +++++++++++++++++++++ .../users/user-edit/user-update.component.ts | 24 ++++- client/src/app/+admin/users/users.routes.ts | 3 +- 9 files changed, 192 insertions(+), 4 deletions(-) create mode 100644 client/src/app/+admin/users/user-edit/user-password.component.html create mode 100644 client/src/app/+admin/users/user-edit/user-password.component.scss create mode 100644 client/src/app/+admin/users/user-edit/user-password.component.ts (limited to 'client/src/app/+admin') diff --git a/client/src/app/+admin/admin.module.ts b/client/src/app/+admin/admin.module.ts index c06ae1d60..f7f347105 100644 --- a/client/src/app/+admin/admin.module.ts +++ b/client/src/app/+admin/admin.module.ts @@ -10,7 +10,7 @@ import { FollowingListComponent } from './follows/following-list/following-list. import { JobsComponent } from './jobs/job.component' import { JobsListComponent } from './jobs/jobs-list/jobs-list.component' import { JobService } from './jobs/shared/job.service' -import { UserCreateComponent, UserListComponent, UsersComponent, UserUpdateComponent } from './users' +import { UserCreateComponent, UserListComponent, UsersComponent, UserUpdateComponent, UserPasswordComponent } from './users' import { ModerationCommentModalComponent, VideoAbuseListComponent, VideoBlacklistListComponent } from './moderation' import { ModerationComponent } from '@app/+admin/moderation/moderation.component' import { RedundancyCheckboxComponent } from '@app/+admin/follows/shared/redundancy-checkbox.component' @@ -36,6 +36,7 @@ import { InstanceAccountBlocklistComponent, InstanceServerBlocklistComponent } f UsersComponent, UserCreateComponent, UserUpdateComponent, + UserPasswordComponent, UserListComponent, ModerationComponent, diff --git a/client/src/app/+admin/users/user-edit/index.ts b/client/src/app/+admin/users/user-edit/index.ts index fd80a02e0..ec734ef92 100644 --- a/client/src/app/+admin/users/user-edit/index.ts +++ b/client/src/app/+admin/users/user-edit/index.ts @@ -1,2 +1,3 @@ export * from './user-create.component' export * from './user-update.component' +export * from './user-password.component' diff --git a/client/src/app/+admin/users/user-edit/user-edit.component.html b/client/src/app/+admin/users/user-edit/user-edit.component.html index 56cf7d17d..cbc06c157 100644 --- a/client/src/app/+admin/users/user-edit/user-edit.component.html +++ b/client/src/app/+admin/users/user-edit/user-edit.component.html @@ -81,3 +81,13 @@ + +
+ + +

Send a link to reset the password by mail to the user.

+ + +

Manually set the user password

+ +
\ No newline at end of file diff --git a/client/src/app/+admin/users/user-edit/user-edit.component.scss b/client/src/app/+admin/users/user-edit/user-edit.component.scss index 6675f65cc..2b4aae83c 100644 --- a/client/src/app/+admin/users/user-edit/user-edit.component.scss +++ b/client/src/app/+admin/users/user-edit/user-edit.component.scss @@ -14,7 +14,7 @@ input:not([type=submit]) { @include peertube-select-container(340px); } -input[type=submit] { +input[type=submit], button { @include peertube-button; @include orange-button; @@ -25,3 +25,10 @@ input[type=submit] { margin-top: 5px; font-size: 11px; } + +.account-title { + @include in-content-small-title; + + margin-top: 55px; + margin-bottom: 30px; +} diff --git a/client/src/app/+admin/users/user-edit/user-password.component.html b/client/src/app/+admin/users/user-edit/user-password.component.html new file mode 100644 index 000000000..ee7d8dff5 --- /dev/null +++ b/client/src/app/+admin/users/user-edit/user-password.component.html @@ -0,0 +1,25 @@ +
+
+ +
+
+
+ +
+
+ +
+ +
+
+
+ {{ formErrors.password }} +
+
+ + +
\ No newline at end of file diff --git a/client/src/app/+admin/users/user-edit/user-password.component.scss b/client/src/app/+admin/users/user-edit/user-password.component.scss new file mode 100644 index 000000000..9185e787c --- /dev/null +++ b/client/src/app/+admin/users/user-edit/user-password.component.scss @@ -0,0 +1,21 @@ +@import '_variables'; +@import '_mixins'; + +input:not([type=submit]):not([type=checkbox]) { + @include peertube-input-text(340px); + display: block; + border-top-right-radius: 0; + border-bottom-right-radius: 0; + border-right: none; +} + +input[type=submit] { + @include peertube-button; + @include orange-button; + + margin-top: 10px; +} + +.input-group-append { + height: 30px; +} diff --git a/client/src/app/+admin/users/user-edit/user-password.component.ts b/client/src/app/+admin/users/user-edit/user-password.component.ts new file mode 100644 index 000000000..1f9ccb4e8 --- /dev/null +++ b/client/src/app/+admin/users/user-edit/user-password.component.ts @@ -0,0 +1,100 @@ +import { Component, OnDestroy, OnInit, Input } from '@angular/core' +import { ActivatedRoute, Router } from '@angular/router' +import { Subscription } from 'rxjs' +import * as generator from 'generate-password-browser' +import { NotificationsService } from 'angular2-notifications' +import { UserService } from '@app/shared/users/user.service' +import { ServerService } from '../../../core' +import { User, UserUpdate } from '../../../../../../shared' +import { I18n } from '@ngx-translate/i18n-polyfill' +import { FormValidatorService } from '@app/shared/forms/form-validators/form-validator.service' +import { UserValidatorsService } from '@app/shared/forms/form-validators/user-validators.service' +import { ConfigService } from '@app/+admin/config/shared/config.service' +import { FormReactive } from '../../../shared' + +@Component({ + selector: 'my-user-password', + templateUrl: './user-password.component.html', + styleUrls: [ './user-password.component.scss' ] +}) +export class UserPasswordComponent extends FormReactive implements OnInit, OnDestroy { + error: string + userId: number + username: string + showPassword = false + + private paramsSub: Subscription + + constructor ( + protected formValidatorService: FormValidatorService, + protected serverService: ServerService, + protected configService: ConfigService, + private userValidatorsService: UserValidatorsService, + private route: ActivatedRoute, + private router: Router, + private notificationsService: NotificationsService, + private userService: UserService, + private i18n: I18n + ) { + super() + } + + ngOnInit () { + this.buildForm({ + password: this.userValidatorsService.USER_PASSWORD + }) + + this.paramsSub = this.route.params.subscribe(routeParams => { + const userId = routeParams['id'] + this.userService.getUser(userId).subscribe( + user => this.onUserFetched(user), + + err => this.error = err.message + ) + }) + } + + ngOnDestroy () { + this.paramsSub.unsubscribe() + } + + formValidated () { + this.error = undefined + + const userUpdate: UserUpdate = this.form.value + + this.userService.updateUser(this.userId, userUpdate).subscribe( + () => { + this.notificationsService.success( + this.i18n('Success'), + this.i18n('Password changed for user {{username}}.', { username: this.username }) + ) + }, + + err => this.error = err.message + ) + } + + generatePassword () { + this.form.patchValue({ + password: generator.generate({ + length: 16, + excludeSimilarCharacters: true, + strict: true + }) + }) + } + + togglePasswordVisibility () { + this.showPassword = !this.showPassword + } + + getFormButtonTitle () { + return this.i18n('Update user password') + } + + private onUserFetched (userJson: User) { + this.userId = userJson.id + this.username = userJson.username + } +} diff --git a/client/src/app/+admin/users/user-edit/user-update.component.ts b/client/src/app/+admin/users/user-edit/user-update.component.ts index 61e641823..cb74897d0 100644 --- a/client/src/app/+admin/users/user-edit/user-update.component.ts +++ b/client/src/app/+admin/users/user-edit/user-update.component.ts @@ -1,4 +1,4 @@ -import { Component, OnDestroy, OnInit } from '@angular/core' +import { Component, OnDestroy, OnInit, Input } from '@angular/core' import { ActivatedRoute, Router } from '@angular/router' import { Subscription } from 'rxjs' import { Notifier } from '@app/core' @@ -19,9 +19,12 @@ import { UserService } from '@app/shared' export class UserUpdateComponent extends UserEdit implements OnInit, OnDestroy { error: string userId: number + userEmail: string username: string + isAdministration = false private paramsSub: Subscription + private isAdministrationSub: Subscription constructor ( protected formValidatorService: FormValidatorService, @@ -56,10 +59,15 @@ export class UserUpdateComponent extends UserEdit implements OnInit, OnDestroy { err => this.error = err.message ) }) + + this.isAdministrationSub = this.route.data.subscribe(data => { + if (data.isAdministration) this.isAdministration = data.isAdministration + }) } ngOnDestroy () { this.paramsSub.unsubscribe() + this.isAdministrationSub.unsubscribe() } formValidated () { @@ -89,9 +97,23 @@ export class UserUpdateComponent extends UserEdit implements OnInit, OnDestroy { return this.i18n('Update user') } + resetPassword () { + this.userService.askResetPassword(this.userEmail).subscribe( + () => { + this.notificationsService.success( + this.i18n('Success'), + this.i18n('An email asking for password reset has been sent to {{username}}.', { username: this.username }) + ) + }, + + err => this.error = err.message + ) + } + private onUserFetched (userJson: User) { this.userId = userJson.id this.username = userJson.username + this.userEmail = userJson.email this.form.patchValue({ email: userJson.email, diff --git a/client/src/app/+admin/users/users.routes.ts b/client/src/app/+admin/users/users.routes.ts index 8b3791bd3..460ebd89e 100644 --- a/client/src/app/+admin/users/users.routes.ts +++ b/client/src/app/+admin/users/users.routes.ts @@ -44,7 +44,8 @@ export const UsersRoutes: Routes = [ data: { meta: { title: 'Update a user' - } + }, + isAdministration: true } } ] -- cgit v1.2.3