mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-07 01:54:15 +00:00
Merge pull request #1804 from 4Science/CST-6153
Ask current password to user when setting a new password
This commit is contained in:
@@ -307,7 +307,7 @@ describe('EPersonDataService', () => {
|
|||||||
it('should sent a patch request with an uuid, token and new password to the epersons endpoint', () => {
|
it('should sent a patch request with an uuid, token and new password to the epersons endpoint', () => {
|
||||||
service.patchPasswordWithToken('test-uuid', 'test-token', 'test-password');
|
service.patchPasswordWithToken('test-uuid', 'test-token', 'test-password');
|
||||||
|
|
||||||
const operation = Object.assign({ op: 'add', path: '/password', value: 'test-password' });
|
const operation = Object.assign({ op: 'add', path: '/password', value: { new_password: 'test-password' } });
|
||||||
const expected = new PatchRequest(requestService.generateRequestId(), epersonsEndpoint + '/test-uuid?token=test-token', [operation]);
|
const expected = new PatchRequest(requestService.generateRequestId(), epersonsEndpoint + '/test-uuid?token=test-token', [operation]);
|
||||||
|
|
||||||
expect(requestService.send).toHaveBeenCalledWith(expected);
|
expect(requestService.send).toHaveBeenCalledWith(expected);
|
||||||
|
@@ -3,7 +3,10 @@ import { createSelector, select, Store } from '@ngrx/store';
|
|||||||
import { Operation } from 'fast-json-patch';
|
import { Operation } from 'fast-json-patch';
|
||||||
import { Observable } from 'rxjs';
|
import { Observable } from 'rxjs';
|
||||||
import { find, map, take } from 'rxjs/operators';
|
import { find, map, take } from 'rxjs/operators';
|
||||||
import { EPeopleRegistryCancelEPersonAction, EPeopleRegistryEditEPersonAction } from '../../access-control/epeople-registry/epeople-registry.actions';
|
import {
|
||||||
|
EPeopleRegistryCancelEPersonAction,
|
||||||
|
EPeopleRegistryEditEPersonAction
|
||||||
|
} from '../../access-control/epeople-registry/epeople-registry.actions';
|
||||||
import { EPeopleRegistryState } from '../../access-control/epeople-registry/epeople-registry.reducers';
|
import { EPeopleRegistryState } from '../../access-control/epeople-registry/epeople-registry.reducers';
|
||||||
import { AppState } from '../../app.reducer';
|
import { AppState } from '../../app.reducer';
|
||||||
import { hasNoValue, hasValue } from '../../shared/empty.util';
|
import { hasNoValue, hasValue } from '../../shared/empty.util';
|
||||||
@@ -318,7 +321,7 @@ export class EPersonDataService extends IdentifiableDataService<EPerson> impleme
|
|||||||
patchPasswordWithToken(uuid: string, token: string, password: string): Observable<RemoteData<EPerson>> {
|
patchPasswordWithToken(uuid: string, token: string, password: string): Observable<RemoteData<EPerson>> {
|
||||||
const requestId = this.requestService.generateRequestId();
|
const requestId = this.requestService.generateRequestId();
|
||||||
|
|
||||||
const operation = Object.assign({ op: 'add', path: '/password', value: password });
|
const operation = Object.assign({ op: 'add', path: '/password', value: { 'new_password': password } });
|
||||||
|
|
||||||
const hrefObs = this.halService.getEndpoint(this.linkPath).pipe(
|
const hrefObs = this.halService.getEndpoint(this.linkPath).pipe(
|
||||||
map((endpoint: string) => this.getIDHref(endpoint, uuid)),
|
map((endpoint: string) => this.getIDHref(endpoint, uuid)),
|
||||||
|
@@ -74,6 +74,19 @@ describe('ProfilePageSecurityFormComponent', () => {
|
|||||||
|
|
||||||
expect(component.passwordValue.emit).toHaveBeenCalledWith('new-password');
|
expect(component.passwordValue.emit).toHaveBeenCalledWith('new-password');
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
it('should emit the value on password change with current password for profile-page', fakeAsync(() => {
|
||||||
|
spyOn(component.passwordValue, 'emit');
|
||||||
|
spyOn(component.currentPasswordValue, 'emit');
|
||||||
|
component.FORM_PREFIX = 'profile.security.form.';
|
||||||
|
component.ngOnInit();
|
||||||
|
component.formGroup.patchValue({password: 'new-password'});
|
||||||
|
component.formGroup.patchValue({'current-password': 'current-password'});
|
||||||
|
tick(300);
|
||||||
|
|
||||||
|
expect(component.passwordValue.emit).toHaveBeenCalledWith('new-password');
|
||||||
|
expect(component.currentPasswordValue.emit).toHaveBeenCalledWith('current-password');
|
||||||
|
}));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@@ -27,6 +27,10 @@ export class ProfilePageSecurityFormComponent implements OnInit {
|
|||||||
* Emits the value of the password
|
* Emits the value of the password
|
||||||
*/
|
*/
|
||||||
@Output() passwordValue = new EventEmitter<string>();
|
@Output() passwordValue = new EventEmitter<string>();
|
||||||
|
/**
|
||||||
|
* Emits the value of the current-password
|
||||||
|
*/
|
||||||
|
@Output() currentPasswordValue = new EventEmitter<string>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The form's input models
|
* The form's input models
|
||||||
@@ -70,6 +74,14 @@ export class ProfilePageSecurityFormComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
|
if (this.FORM_PREFIX === 'profile.security.form.') {
|
||||||
|
this.formModel.unshift(new DynamicInputModel({
|
||||||
|
id: 'current-password',
|
||||||
|
name: 'current-password',
|
||||||
|
inputType: 'password',
|
||||||
|
required: true
|
||||||
|
}));
|
||||||
|
}
|
||||||
if (this.passwordCanBeEmpty) {
|
if (this.passwordCanBeEmpty) {
|
||||||
this.formGroup = this.formService.createFormGroup(this.formModel,
|
this.formGroup = this.formService.createFormGroup(this.formModel,
|
||||||
{ validators: [this.checkPasswordsEqual] });
|
{ validators: [this.checkPasswordsEqual] });
|
||||||
@@ -94,6 +106,9 @@ export class ProfilePageSecurityFormComponent implements OnInit {
|
|||||||
debounceTime(300),
|
debounceTime(300),
|
||||||
).subscribe((valueChange) => {
|
).subscribe((valueChange) => {
|
||||||
this.passwordValue.emit(valueChange.password);
|
this.passwordValue.emit(valueChange.password);
|
||||||
|
if (this.FORM_PREFIX === 'profile.security.form.') {
|
||||||
|
this.currentPasswordValue.emit(valueChange['current-password']);
|
||||||
|
}
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -24,6 +24,7 @@
|
|||||||
[FORM_PREFIX]="'profile.security.form.'"
|
[FORM_PREFIX]="'profile.security.form.'"
|
||||||
(isInvalid)="setInvalid($event)"
|
(isInvalid)="setInvalid($event)"
|
||||||
(passwordValue)="setPasswordValue($event)"
|
(passwordValue)="setPasswordValue($event)"
|
||||||
|
(currentPasswordValue)="setCurrentPasswordValue($event)"
|
||||||
></ds-profile-page-security-form>
|
></ds-profile-page-security-form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -180,7 +180,7 @@ describe('ProfilePageComponent', () => {
|
|||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
component.setPasswordValue('');
|
component.setPasswordValue('');
|
||||||
|
component.setCurrentPasswordValue('current-password');
|
||||||
result = component.updateSecurity();
|
result = component.updateSecurity();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -199,6 +199,7 @@ describe('ProfilePageComponent', () => {
|
|||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
component.setPasswordValue('test');
|
component.setPasswordValue('test');
|
||||||
component.setInvalid(true);
|
component.setInvalid(true);
|
||||||
|
component.setCurrentPasswordValue('current-password');
|
||||||
result = component.updateSecurity();
|
result = component.updateSecurity();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -215,8 +216,11 @@ describe('ProfilePageComponent', () => {
|
|||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
component.setPasswordValue('testest');
|
component.setPasswordValue('testest');
|
||||||
component.setInvalid(false);
|
component.setInvalid(false);
|
||||||
|
component.setCurrentPasswordValue('current-password');
|
||||||
|
|
||||||
operations = [{ op: 'add', path: '/password', value: 'testest' }];
|
operations = [
|
||||||
|
{ 'op': 'add', 'path': '/password', 'value': { 'new_password': 'testest', 'current_password': 'current-password' } }
|
||||||
|
];
|
||||||
result = component.updateSecurity();
|
result = component.updateSecurity();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -228,6 +232,28 @@ describe('ProfilePageComponent', () => {
|
|||||||
expect(epersonService.patch).toHaveBeenCalledWith(user, operations);
|
expect(epersonService.patch).toHaveBeenCalledWith(user, operations);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('when password is filled in, and is valid but return 403', () => {
|
||||||
|
let result;
|
||||||
|
let operations;
|
||||||
|
|
||||||
|
it('should return call epersonService.patch', (done) => {
|
||||||
|
epersonService.patch.and.returnValue(observableOf(Object.assign(new RestResponse(false, 403, 'Error'))));
|
||||||
|
component.setPasswordValue('testest');
|
||||||
|
component.setInvalid(false);
|
||||||
|
component.setCurrentPasswordValue('current-password');
|
||||||
|
operations = [
|
||||||
|
{ 'op': 'add', 'path': '/password', 'value': {'new_password': 'testest', 'current_password': 'current-password' }}
|
||||||
|
];
|
||||||
|
result = component.updateSecurity();
|
||||||
|
epersonService.patch(user, operations).subscribe((response) => {
|
||||||
|
expect(response.statusCode).toEqual(403);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
expect(epersonService.patch).toHaveBeenCalledWith(user, operations);
|
||||||
|
expect(result).toEqual(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('canChangePassword$', () => {
|
describe('canChangePassword$', () => {
|
||||||
|
@@ -67,6 +67,10 @@ export class ProfilePageComponent implements OnInit {
|
|||||||
* The password filled in, in the security form
|
* The password filled in, in the security form
|
||||||
*/
|
*/
|
||||||
private password: string;
|
private password: string;
|
||||||
|
/**
|
||||||
|
* The current-password filled in, in the security form
|
||||||
|
*/
|
||||||
|
private currentPassword: string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The authenticated user
|
* The authenticated user
|
||||||
@@ -138,15 +142,14 @@ export class ProfilePageComponent implements OnInit {
|
|||||||
*/
|
*/
|
||||||
updateSecurity() {
|
updateSecurity() {
|
||||||
const passEntered = isNotEmpty(this.password);
|
const passEntered = isNotEmpty(this.password);
|
||||||
|
|
||||||
if (this.invalidSecurity) {
|
if (this.invalidSecurity) {
|
||||||
this.notificationsService.error(this.translate.instant(this.PASSWORD_NOTIFICATIONS_PREFIX + 'error.general'));
|
this.notificationsService.error(this.translate.instant(this.PASSWORD_NOTIFICATIONS_PREFIX + 'error.general'));
|
||||||
}
|
}
|
||||||
if (!this.invalidSecurity && passEntered) {
|
if (!this.invalidSecurity && passEntered) {
|
||||||
const operation = {op: 'add', path: '/password', value: this.password} as Operation;
|
const operations = [
|
||||||
this.epersonService.patch(this.currentUser, [operation]).pipe(
|
{ 'op': 'add', 'path': '/password', 'value': { 'new_password': this.password, 'current_password': this.currentPassword } }
|
||||||
getFirstCompletedRemoteData()
|
] as Operation[];
|
||||||
).subscribe((response: RemoteData<EPerson>) => {
|
this.epersonService.patch(this.currentUser, operations).pipe(getFirstCompletedRemoteData()).subscribe((response: RemoteData<EPerson>) => {
|
||||||
if (response.hasSucceeded) {
|
if (response.hasSucceeded) {
|
||||||
this.notificationsService.success(
|
this.notificationsService.success(
|
||||||
this.translate.instant(this.PASSWORD_NOTIFICATIONS_PREFIX + 'success.title'),
|
this.translate.instant(this.PASSWORD_NOTIFICATIONS_PREFIX + 'success.title'),
|
||||||
@@ -154,7 +157,8 @@ export class ProfilePageComponent implements OnInit {
|
|||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
this.notificationsService.error(
|
this.notificationsService.error(
|
||||||
this.translate.instant(this.PASSWORD_NOTIFICATIONS_PREFIX + 'error.title'), response.errorMessage
|
this.translate.instant(this.PASSWORD_NOTIFICATIONS_PREFIX + 'error.title'),
|
||||||
|
this.translate.instant(this.PASSWORD_NOTIFICATIONS_PREFIX + 'error.change-failed')
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -170,6 +174,14 @@ export class ProfilePageComponent implements OnInit {
|
|||||||
this.password = $event;
|
this.password = $event;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the current-password value based on the value emitted from the security form
|
||||||
|
* @param $event
|
||||||
|
*/
|
||||||
|
setCurrentPasswordValue($event: string) {
|
||||||
|
this.currentPassword = $event;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Submit of the security form that triggers the updateProfile method
|
* Submit of the security form that triggers the updateProfile method
|
||||||
*/
|
*/
|
||||||
|
@@ -3081,12 +3081,16 @@
|
|||||||
|
|
||||||
"profile.security.form.label.passwordrepeat": "Retype to confirm",
|
"profile.security.form.label.passwordrepeat": "Retype to confirm",
|
||||||
|
|
||||||
|
"profile.security.form.label.current-password": "Current password",
|
||||||
|
|
||||||
"profile.security.form.notifications.success.content": "Your changes to the password were saved.",
|
"profile.security.form.notifications.success.content": "Your changes to the password were saved.",
|
||||||
|
|
||||||
"profile.security.form.notifications.success.title": "Password saved",
|
"profile.security.form.notifications.success.title": "Password saved",
|
||||||
|
|
||||||
"profile.security.form.notifications.error.title": "Error changing passwords",
|
"profile.security.form.notifications.error.title": "Error changing passwords",
|
||||||
|
|
||||||
|
"profile.security.form.notifications.error.change-failed": "An error occurred while trying to change the password. Please check if the current password is correct.",
|
||||||
|
|
||||||
"profile.security.form.notifications.error.not-same": "The provided passwords are not the same.",
|
"profile.security.form.notifications.error.not-same": "The provided passwords are not the same.",
|
||||||
|
|
||||||
"profile.security.form.notifications.error.general": "Please fill required fields of security form.",
|
"profile.security.form.notifications.error.general": "Please fill required fields of security form.",
|
||||||
|
Reference in New Issue
Block a user