diff --git a/smp-angular/src/app/common/panels/document-events-panel/document-events-panel.component.ts b/smp-angular/src/app/common/panels/document-events-panel/document-events-panel.component.ts index c22cc2a6298b302fc024799744238fe8e47348ee..be12ada60435b5ee346e4f7839f801928143fb7e 100644 --- a/smp-angular/src/app/common/panels/document-events-panel/document-events-panel.component.ts +++ b/smp-angular/src/app/common/panels/document-events-panel/document-events-panel.component.ts @@ -34,7 +34,9 @@ import { import {MatSort} from "@angular/material/sort"; import {MatDialog} from "@angular/material/dialog"; import {MatPaginator} from "@angular/material/paginator"; -import {DocumentVersionEventRo} from "../../model/document-version-event-ro.model"; +import { + DocumentVersionEventRo +} from "../../model/document-version-event-ro.model"; import { BeforeLeaveGuard } from "../../../window/sidenav/navigation-on-leave-guard"; @@ -60,7 +62,7 @@ import {GlobalLookups} from "../../global-lookups"; export class DocumentEventsPanelComponent implements AfterViewInit, BeforeLeaveGuard, ControlValueAccessor { - displayedColumns: string[] = [ 'date', 'eventType', 'username', 'eventSource']; + displayedColumns: string[] = ['date', 'eventType', 'username', 'eventSource']; private onChangeCallback: (_: any) => void = () => { }; eventDataSource: MatTableDataSource<DocumentVersionEventRo> = new MatTableDataSource(); @@ -72,8 +74,8 @@ export class DocumentEventsPanelComponent implements AfterViewInit, BeforeLeaveG constructor( private globalLookups: GlobalLookups, - public dialog: MatDialog, - private controlContainer: ControlContainer) { + public dialog: MatDialog, + private controlContainer: ControlContainer) { } get dateTimeFormat(): string { @@ -103,6 +105,13 @@ export class DocumentEventsPanelComponent implements AfterViewInit, BeforeLeaveG ngAfterViewInit() { this.eventDataSource.paginator = this.paginator; this.eventDataSource.sort = this.sort; + // add custom filter to exclude filtering on event description + this.eventDataSource.filterPredicate = (data: DocumentVersionEventRo, filter: string) => { + return data.eventType?.toLowerCase().includes(filter) + || data.username?.toLowerCase().includes(filter) + || data.eventSourceType?.toLowerCase().includes(filter) + || data.eventOn?.toLocaleString().toLowerCase().includes(filter); + }; } applyFilter(event: Event) { diff --git a/smp-angular/src/app/system-settings/admin-users/admin-user.component.ts b/smp-angular/src/app/system-settings/admin-users/admin-user.component.ts index ef5e6c12d7c84e1ba4f4d2f1314b10393cbe52eb..ed5684d53955b4f32d0c1e6fddf386166b4f82f9 100644 --- a/smp-angular/src/app/system-settings/admin-users/admin-user.component.ts +++ b/smp-angular/src/app/system-settings/admin-users/admin-user.component.ts @@ -151,74 +151,81 @@ export class AdminUserComponent implements AfterViewInit, BeforeLeaveGuard { } updateUserData(user: UserRo) { + + // capture this to variable because of async call 'this' inside targets the wrong object + const thatAdminUserComponent = this; // change only allowed data this.adminUserService.updateManagedUser(user).subscribe({ async next(user: UserRo) { if (user) { - this.selected = null; - this.managedUserData = null; - this.loadTableData(user.username); - this.alertService.success(await lastValueFrom(this.translateService.get("admin.user.success.update", {username: user.username}))); + thatAdminUserComponent.selected = null; + thatAdminUserComponent.managedUserData = null; + thatAdminUserComponent.loadTableData(user.username); + thatAdminUserComponent.alertService.success(await lastValueFrom(thatAdminUserComponent.translateService.get("admin.user.success.update", {username: user.username}))); } }, error(error) { - if (this.httpErrorHandlerService.logoutOnInvalidSessionError(error)) { + if (thatAdminUserComponent.httpErrorHandlerService.logoutOnInvalidSessionError(error)) { return; } - this.alertService.error(error.error?.errorDescription) + thatAdminUserComponent.alertService.error(error.error?.errorDescription) } }); } createUserData(user: UserRo) { // change only allowed data + // capture this to variable because of async call 'this' inside targets the wrong object + const thatAdminUserComponent = this; this.adminUserService.createManagedUser(user).subscribe({ async next(user: UserRo) { if (user) { - this.selected = null; - this.managedUserData = null; - this.loadTableData(user.username); - this.alertService.success(await lastValueFrom(this.translateService.get("admin.user.success.create", {username: user.username}))); + thatAdminUserComponent.selected = null; + thatAdminUserComponent.managedUserData = null; + thatAdminUserComponent.loadTableData(user.username); + thatAdminUserComponent.alertService.success(await lastValueFrom(thatAdminUserComponent.translateService.get("admin.user.success.create", {username: user.username}))); } }, error(error) { - if (this.httpErrorHandlerService.logoutOnInvalidSessionError(error)) { + if (thatAdminUserComponent.httpErrorHandlerService.logoutOnInvalidSessionError(error)) { return; } - this.alertService.error(error.error?.errorDescription) + thatAdminUserComponent.alertService.error(error.error?.errorDescription) } }); } async onDeleteSelectedUserClicked() { - + // capture this to variable because of async call 'this' inside targets the wrong object + const thatAdminUserComponent = this; this.dialog.open(ConfirmationDialogComponent, { data: { - title: await lastValueFrom(this.translateService.get("admin.user.delete.confirmation.dialog.title", {username: this.managedUserData?.username})), - description: await lastValueFrom(this.translateService.get("admin.user.delete.confirmation.dialog.description")) + title: await lastValueFrom(thatAdminUserComponent.translateService.get("admin.user.delete.confirmation.dialog.title", {username: this.managedUserData?.username})), + description: await lastValueFrom(thatAdminUserComponent.translateService.get("admin.user.delete.confirmation.dialog.description")) } }).afterClosed().subscribe(result => { if (result) { - this.deleteUser(this.managedUserData); + this.deleteUser(thatAdminUserComponent.managedUserData); } }); } deleteUser(user: UserRo) { - + // capture this to variable because of async call 'this' inside targets the wrong object + const thatAdminUserComponent = this; // change only allowed data this.adminUserService.deleteManagedUser(user).subscribe({ async next(user: UserRo) { if (user) { - this.selected = null; - this.managedUserData = null; - this.loadTableData(); - this.alertService.success(await lastValueFrom(this.translateService.get("admin.user.success.delete", {username: user.username}))); + thatAdminUserComponent.selected = null; + thatAdminUserComponent.managedUserData = null; + thatAdminUserComponent.loadTableData(); + thatAdminUserComponent.alertService.success(await lastValueFrom(thatAdminUserComponent.translateService.get("admin.user.success.delete", {username: user.username}))); } }, error(error) { - if (this.httpErrorHandlerService.logoutOnInvalidSessionError(error)) { + if (thatAdminUserComponent.httpErrorHandlerService.logoutOnInvalidSessionError(error)) { return; } - this.alertService.error(error.error?.errorDescription) + thatAdminUserComponent.alertService.error(error.error?.errorDescription) } }); @@ -235,7 +242,7 @@ export class AdminUserComponent implements AfterViewInit, BeforeLeaveGuard { if (result) { this.selected = null; this.managedUserData = null; - this.loadTableData(); + this.loadTableData(user.username); this.alertService.success(await lastValueFrom(this.translateService.get("admin.user.success.password.updated"))); } }); diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/exceptions/ErrorCode.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/exceptions/ErrorCode.java index 77f5a3dd9f385e5195f72187b6aa6e0238262ca8..f1f2b319695f64e247bdd36ad9b1b79c5471665c 100644 --- a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/exceptions/ErrorCode.java +++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/exceptions/ErrorCode.java @@ -32,7 +32,7 @@ public enum ErrorCode { UNAUTHORIZED(401, "SMP:003",ErrorBusinessCode.UNAUTHORIZED, "User not authorized!"), UNAUTHORIZED_INVALID_USER_IDENTIFIER(401, "SMP:004",ErrorBusinessCode.UNAUTHORIZED, "Invalid user identifier! User not authorized."), UNAUTHORIZED_INVALID_IDENTIFIER(401, "SMP:005",ErrorBusinessCode.UNAUTHORIZED, "Invalid entity identifier! User not authorized to access the entity data"), - UNAUTHORIZED_INVALID_RESET_TOKEN(401, "SMP:006",ErrorBusinessCode.UNAUTHORIZED, "Reset token; Invalid or not active reset token!"), + UNAUTHORIZED_INVALID_RESET_TOKEN(401, "SMP:006",ErrorBusinessCode.UNAUTHORIZED, "The reset token it is invalid or not active any more. Please try to reset your password again."), INVALID_ENCODING (500, "SMP:100",ErrorBusinessCode.TECHNICAL, "Unsupported or invalid encoding for %s!"), SML_INVALID_IDENTIFIER (400,"SMP:101",ErrorBusinessCode.FORMAT_ERROR,"Malformed identifier, scheme and id should be delimited by double colon: %s "), diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/CredentialService.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/CredentialService.java index 38791f45918a3b337dab826ae98c5ead3ab414f2..859f922ac81eff2057296be36de3a34d924b331a 100644 --- a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/CredentialService.java +++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/CredentialService.java @@ -26,6 +26,7 @@ import eu.europa.ec.edelivery.smp.auth.UILoginAuthenticationToken; import eu.europa.ec.edelivery.smp.config.SMPEnvironmentProperties; import eu.europa.ec.edelivery.smp.data.dao.CredentialDao; import eu.europa.ec.edelivery.smp.data.dao.UserDao; +import eu.europa.ec.edelivery.smp.data.enums.CredentialTargetType; import eu.europa.ec.edelivery.smp.data.enums.CredentialType; import eu.europa.ec.edelivery.smp.data.model.user.DBCertificate; import eu.europa.ec.edelivery.smp.data.model.user.DBCredential; @@ -76,6 +77,8 @@ public class CredentialService { protected static final BadCredentialsException SUSPENDED_CREDENTIALS_EXCEPTION = new BadCredentialsException(ErrorCode.UNAUTHORIZED_CREDENTIAL_SUSPENDED.getMessage()); protected static final int RESET_TOKEN_LENGTH = 64; + private static final String USER_ID_REQUEST_TYPE = "UserId"; + final UserDao userDao; final CredentialDao credentialDao; final ConversionService conversionService; @@ -341,7 +344,7 @@ public class CredentialService { // retrieve user Optional credentials by username Optional<DBCredential> optCredential = getActiveCredentialsForUsernameToReset(username, true); if (!optCredential.isPresent()) { - LOG.info("Skip generating reset token for username [{}]!", username); + LOG.info("Skip generating reset token for username [{}]. User is not active!", username); return; } DBCredential dbCredential = optCredential.get(); @@ -407,13 +410,24 @@ public class CredentialService { * @return Optional of DBCredential: if the user has active credentials and active else empty is returned */ private Optional<DBCredential> getActiveCredentialsForUsernameToReset(String username, boolean toGenerateResetToken) { - // retrieve user Optional credentials by username + Optional<DBCredential> optCredential = credentialDao.findUsernamePasswordCredentialForUsernameAndUI(username); + DBCredential dbCredential; if (!optCredential.isPresent()) { - LOG.warn("There is not credentials for User [{}]!", username); - return optCredential; + DBUser user = userDao.findUserByUsername(username).orElseThrow(() -> { + LOG.warn("There is no user with username [{}]!", username); + throw new SMPRuntimeException(ErrorCode.UNAUTHORIZED_INVALID_USERNAME_PASSWORD, "User not found!"); + }); + LOG.info("User [{}] does not have username/password credentials. Create new credentials!", username); + dbCredential = createCredentialsForUser(user.getId(), + CredentialType.USERNAME_PASSWORD, + CredentialTargetType.UI); + // persist new credential + credentialDao.persist(dbCredential); + optCredential = Optional.of(dbCredential); + } else { + dbCredential = optCredential.get(); } - DBCredential dbCredential = optCredential.get(); if (!dbCredential.getUser().isActive() || !dbCredential.isActive()) { LOG.info("User [{}] or credentials are not active. Skip reset password request!", username); @@ -434,6 +448,31 @@ public class CredentialService { return optCredential; } + + /** + * Method creates Username/passwords credentials for the user with given userId. + * The method must be called inside active transactions. + * + * @param userID to change/create username-password credentials + * @param credentialType the credential type + * @param credentialTargetType the credential target + */ + public DBCredential createCredentialsForUser(Long userID, CredentialType credentialType, CredentialTargetType credentialTargetType) { + + DBUser dbUserToUpdate = userDao.find(userID); + if (dbUserToUpdate == null) { + LOG.error("Can not create user password credentials, because user [{}] does not exist!", userID); + throw new SMPRuntimeException(ErrorCode.INVALID_REQUEST, USER_ID_REQUEST_TYPE, "Can not find user id to update!"); + } + DBCredential credential = new DBCredential(); + credential.setUser(dbUserToUpdate); + credential.setName(dbUserToUpdate.getUsername()); + credential.setCredentialType(credentialType); + credential.setCredentialTarget(credentialTargetType); + return credential; + } + + public void validatePasswordResetToken(String resetToken){ Optional<DBCredential> optCredential = credentialDao.findUCredentialForUsernamePasswordTypeAndResetToken(resetToken); if (!optCredential.isPresent()) { @@ -478,8 +517,9 @@ public class CredentialService { * Method validates if the certificate contains one of allowed Certificate policy. At the moment it does not validates * the whole chain. Because in some configuration cases does not use the truststore * - * @param certificateId - * @throws CertificateException + * @param certificateId certificate id to be validated + * @param certPolicyList certificate policy list + * @throws AuthenticationServiceException */ protected void validateCertificatePolicyMatchLegacy(String certificateId, List<String> certPolicyList) throws AuthenticationServiceException { diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/ui/UIUserService.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/ui/UIUserService.java index 127fd98860d7a01bc0d500cbf0762ad5e5550199..152d36a4ae58807df9f6fdcce4a84409eb053217 100644 --- a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/ui/UIUserService.java +++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/ui/UIUserService.java @@ -39,6 +39,7 @@ import eu.europa.ec.edelivery.smp.exceptions.SMPRuntimeException; import eu.europa.ec.edelivery.smp.logging.SMPLogger; import eu.europa.ec.edelivery.smp.logging.SMPLoggerFactory; import eu.europa.ec.edelivery.smp.services.ConfigurationService; +import eu.europa.ec.edelivery.smp.services.CredentialService; import eu.europa.ec.edelivery.smp.services.CredentialsAlertService; import eu.europa.ec.edelivery.smp.utils.BCryptPasswordHash; import eu.europa.ec.edelivery.smp.utils.SessionSecurityUtils; @@ -70,6 +71,7 @@ public class UIUserService extends UIServiceBase<DBUser, UserRO> { private static final String USER_ID_REQUEST_TYPE = "UserId"; private final UserDao userDao; + private final CredentialService credentialService; CredentialDao credentialDao; private final ConfigurationService configurationService; private final ConversionService conversionService; @@ -81,13 +83,15 @@ public class UIUserService extends UIServiceBase<DBUser, UserRO> { ConfigurationService configurationService, ConversionService conversionService, UITruststoreService truststoreService, - CredentialsAlertService alertService) { + CredentialsAlertService alertService, + CredentialService credentialService) { this.userDao = userDao; this.credentialDao = credentialDao; this.configurationService = configurationService; this.conversionService = conversionService; this.truststoreService = truststoreService; this.alertService = alertService; + this.credentialService = credentialService; } @Override @@ -242,7 +246,7 @@ public class UIUserService extends UIServiceBase<DBUser, UserRO> { protected DBUser updateUsernamePasswordForUser(Long userID, String password, boolean adminUpdate) { Optional<DBCredential> optCredential = credentialDao.findUsernamePasswordCredentialForUserIdAndUI(userID); - DBCredential dbCredential = optCredential.orElse(createCredentialsForUser(userID, + DBCredential dbCredential = optCredential.orElse(credentialService.createCredentialsForUser(userID, CredentialType.USERNAME_PASSWORD, CredentialTargetType.UI)); @@ -277,31 +281,6 @@ public class UIUserService extends UIServiceBase<DBUser, UserRO> { return dbCredential.getUser(); } - - /** - * Method creates Username/passwords credentials for the user with given userId. - * The method must be called inside active transactions. - * - * @param userID to change/create username-password credentials - * @param credentialType the credential type - * @param credentialTargetType the credential target - */ - protected DBCredential createCredentialsForUser(Long userID, CredentialType credentialType, CredentialTargetType credentialTargetType) { - - DBUser dbUserToUpdate = userDao.find(userID); - if (dbUserToUpdate == null) { - LOG.error("Can not update user password because user,[{}] does not exist!", userID); - throw new SMPRuntimeException(ErrorCode.INVALID_REQUEST, USER_ID_REQUEST_TYPE, "Can not find user id to update!"); - } - DBCredential credential = new DBCredential(); - credential.setUser(dbUserToUpdate); - credential.setName(dbUserToUpdate.getUsername()); - credential.setCredentialType(credentialType); - credential.setCredentialTarget(credentialTargetType); - - return credential; - } - /** * Method updates the user password *