diff --git a/smp-angular/src/app/alert/alert-result.model.ts b/smp-angular/src/app/alert/alert-result.model.ts deleted file mode 100644 index c17db379fdc35022260d8cc8676e06fbfb847b52..0000000000000000000000000000000000000000 --- a/smp-angular/src/app/alert/alert-result.model.ts +++ /dev/null @@ -1,8 +0,0 @@ -import {AlertRo} from './alert-ro.model'; - -export interface AlertResult { - serviceEntities: Array<AlertRo>; - pageSize: number; - count: number; - filter: any; -} diff --git a/smp-angular/src/app/app.module.ts b/smp-angular/src/app/app.module.ts index 698e5b497649775b8c62691fd307451eba791b1a..f254994c6d7672d99f8618f8c8b646471a0ef458 100644 --- a/smp-angular/src/app/app.module.ts +++ b/smp-angular/src/app/app.module.ts @@ -1,12 +1,13 @@ import 'hammerjs'; import {AccessTokenPanelComponent} from "./user-settings/user-access-tokens/access-token-panel/access-token-panel.component"; +import {AlertPanelComponent} from "./common/panels/alert-panel/alert-panel.component"; +import {AdminAlertsComponent} from "./system-settings/admin-alerts/admin-alerts.component"; import {AdminDomainComponent} from "./system-settings/admin-domain/admin-domain.component"; import {AdminDomainService} from "./system-settings/admin-domain/admin-domain.service"; import {AdminKeystoreComponent} from "./system-settings/admin-keystore/admin-keystore.component"; import {AdminKeystoreService} from "./system-settings/admin-keystore/admin-keystore.service"; import {AdminTruststoreComponent} from "./system-settings/admin-truststore/admin-truststore.component"; import {AdminTruststoreService} from "./system-settings/admin-truststore/admin-truststore.service"; -import {AlertComponent} from "./alert/alert.component"; import {AlertMessageComponent} from './common/alert-message/alert-message.component'; import {AlertMessageService} from './common/alert-message/alert-message.service'; import {AppComponent} from './app.component'; @@ -142,15 +143,17 @@ import {SmpTitledLabelComponent} from "./common/components/smp-titled-label/smp- import {ServiceGroupSearchComponent} from "./service-group-search/service-group-search.component"; import {EditResourceController} from "./edit/edit-resources/edit-resource.controller"; import { ClipboardModule } from '@angular/cdk/clipboard'; +import {UserAlertsComponent} from "./user-settings/user-alerts/user-alerts.component"; @NgModule({ declarations: [ AccessTokenPanelComponent, + AdminAlertsComponent, AdminDomainComponent, AdminKeystoreComponent, AdminTruststoreComponent, AdminUserComponent, - AlertComponent, + AlertPanelComponent, AlertMessageComponent, AppComponent, AutoFocusDirective, @@ -220,6 +223,7 @@ import { ClipboardModule } from '@angular/cdk/clipboard'; SubresourceDocumentWizardComponent, SubresourcePanelComponent, ToolbarComponent, + UserAlertsComponent, UserAccessTokensComponent, UserCertificatePanelComponent, UserCertificatesComponent, diff --git a/smp-angular/src/app/app.routes.ts b/smp-angular/src/app/app.routes.ts index 698dd2eabee4e85b1c368c5139517970b597b990..94d1137d38c24cea6444cad61f1e16d800585074 100644 --- a/smp-angular/src/app/app.routes.ts +++ b/smp-angular/src/app/app.routes.ts @@ -1,7 +1,6 @@ import {RouterModule, Routes} from '@angular/router'; import {LoginComponent} from './login/login.component'; import {ServiceGroupSearchComponent} from './service-group-search/service-group-search.component'; -import {AlertComponent} from "./alert/alert.component"; import {PropertyComponent} from "./system-settings/property/property.component"; import {UserProfileComponent} from "./user-settings/user-profile/user-profile.component"; import {authenticationGuard} from "./guards/authentication.guard"; @@ -20,6 +19,8 @@ import {ResourceDocumentPanelComponent} from "./edit/edit-resources/resource-doc import {SubresourceDocumentPanelComponent} from "./edit/edit-resources/subresource-document-panel/subresource-document-panel.component"; import {authorizeChildSystemAdminGuard} from "./guards/authorize-child-system-admin.guard"; import {activateChildResourceGuard} from "./guards/activate-child-document.guard"; +import {UserAlertsComponent} from "./user-settings/user-alerts/user-alerts.component"; +import {AdminAlertsComponent} from "./system-settings/admin-alerts/admin-alerts.component"; const appRoutes: Routes = [ @@ -37,8 +38,18 @@ const appRoutes: Routes = [ path: 'edit-resource', canDeactivate: [dirtyDeactivateGuard], children: [ - {path: 'resource-document', canActivate: [activateChildResourceGuard], component: ResourceDocumentPanelComponent, canDeactivate: [dirtyDeactivateGuard]}, - {path: 'subresource-document', canActivate: [activateChildResourceGuard], component: SubresourceDocumentPanelComponent, canDeactivate: [dirtyDeactivateGuard]}, + { + path: 'resource-document', + canActivate: [activateChildResourceGuard], + component: ResourceDocumentPanelComponent, + canDeactivate: [dirtyDeactivateGuard] + }, + { + path: 'subresource-document', + canActivate: [activateChildResourceGuard], + component: SubresourceDocumentPanelComponent, + canDeactivate: [dirtyDeactivateGuard] + }, {path: '', component: EditResourceComponent, canDeactivate: [dirtyDeactivateGuard]}, ] } @@ -54,7 +65,7 @@ const appRoutes: Routes = [ {path: 'keystore', component: AdminKeystoreComponent, canDeactivate: [dirtyDeactivateGuard]}, {path: 'truststore', component: AdminTruststoreComponent, canDeactivate: [dirtyDeactivateGuard]}, {path: 'extension', component: ExtensionComponent, canDeactivate: [dirtyDeactivateGuard]}, - {path: 'alert', component: AlertComponent, canDeactivate: [dirtyDeactivateGuard]}, + {path: 'alert', component: AdminAlertsComponent, canDeactivate: [dirtyDeactivateGuard]}, ] }, { @@ -64,6 +75,7 @@ const appRoutes: Routes = [ {path: 'user-profile', component: UserProfileComponent, canDeactivate: [dirtyDeactivateGuard]}, {path: 'user-access-token', component: UserAccessTokensComponent, canDeactivate: [dirtyDeactivateGuard]}, {path: 'user-certificate', component: UserCertificatesComponent, canDeactivate: [dirtyDeactivateGuard]}, + {path: 'user-alert', component: UserAlertsComponent, canDeactivate: [dirtyDeactivateGuard]}, {path: 'user-membership', component: UserProfileComponent, canDeactivate: [dirtyDeactivateGuard]}, ] }, diff --git a/smp-angular/src/app/common/dialogs/dialog/dialog.component.html b/smp-angular/src/app/common/dialogs/dialog/dialog.component.html index 96296435edfbca96277cfc28757aed608ecbd989..edc260d3a27a71d39fc6b77c2924a1cf4a20a956 100644 --- a/smp-angular/src/app/common/dialogs/dialog/dialog.component.html +++ b/smp-angular/src/app/common/dialogs/dialog/dialog.component.html @@ -8,9 +8,6 @@ <mat-icon>check_circle</mat-icon> <span>Yes</span> </button> - </div> - - <div *ngIf="isConfirmationDialog()" class="divTableCell"> <button mat-raised-button color="primary" (click)="dialogRef.close(false)" id="nobuttondialog_id" tabindex="1"> <mat-icon>cancel</mat-icon> <span>No</span> diff --git a/smp-angular/src/app/alert/alert-controller.ts b/smp-angular/src/app/common/panels/alert-panel/alert-controller.ts similarity index 66% rename from smp-angular/src/app/alert/alert-controller.ts rename to smp-angular/src/app/common/panels/alert-panel/alert-controller.ts index c3078584a6d3b6ffb4f0716b2d7ae2c0d25573a1..b049f4058c024c080dd2eb0b9df43a03ac67c274 100644 --- a/smp-angular/src/app/alert/alert-controller.ts +++ b/smp-angular/src/app/common/panels/alert-panel/alert-controller.ts @@ -1,13 +1,12 @@ -import {SearchTableController} from '../common/search-table/search-table-controller'; +import {SearchTableController} from '../../search-table/search-table-controller'; import {MatDialog, MatDialogConfig, MatDialogRef} from '@angular/material/dialog'; -import {GlobalLookups} from "../common/global-lookups"; -import {SearchTableEntity} from "../common/search-table/search-table-entity.model"; -import {HttpClient} from "@angular/common/http"; -import {ObjectPropertiesDialogComponent} from "../common/dialogs/object-properties-dialog/object-properties-dialog.component"; +import {GlobalLookups} from "../../global-lookups"; +import {SearchTableEntity} from "../../search-table/search-table-entity.model"; +import {ObjectPropertiesDialogComponent} from "../../dialogs/object-properties-dialog/object-properties-dialog.component"; export class AlertController implements SearchTableController { - constructor(protected http: HttpClient, protected lookups: GlobalLookups, public dialog: MatDialog) { + constructor(protected lookups: GlobalLookups, public dialog: MatDialog) { } validateDeleteOperation(rows: SearchTableEntity[]) { diff --git a/smp-angular/src/app/alert/alert.component.css b/smp-angular/src/app/common/panels/alert-panel/alert-panel.component.css similarity index 100% rename from smp-angular/src/app/alert/alert.component.css rename to smp-angular/src/app/common/panels/alert-panel/alert-panel.component.css diff --git a/smp-angular/src/app/alert/alert.component.html b/smp-angular/src/app/common/panels/alert-panel/alert-panel.component.html similarity index 60% rename from smp-angular/src/app/alert/alert.component.html rename to smp-angular/src/app/common/panels/alert-panel/alert-panel.component.html index 3d1964d905ec519e159babea205d2db66a85551e..4ffed2ec4f6364f4407c97b8b0e0455a55557691 100644 --- a/smp-angular/src/app/alert/alert.component.html +++ b/smp-angular/src/app/common/panels/alert-panel/alert-panel.component.html @@ -1,6 +1,5 @@ <smp-search-table #searchTable - page_id='alert_id' [title]="'Alerts'" [columnPicker]="columnPicker" [url]="baseUrl" @@ -17,14 +16,15 @@ <span style="width: 2px"> </span> </ng-template> <ng-template #dateTimeColumn let-value="value" ngx-datatable-cell-template> - <div class='truncate-text' title="{{value | date:dateTimeFormat}}" >{{value | date:dateFormat}}</div></ng-template> - <ng-template #truncateText let-value="value" ngx-datatable-cell-template> - <div class='truncate-text' title="{{value}}" >{{value}}</div> + <div class='truncate-text' title="{{value | date:dateTimeFormat}}">{{ value | date:dateTimeFormat }}</div> </ng-template> - <ng-template #forUser let-row="row" let-value="value" ngx-datatable-cell-template> - <div class='truncate-text' title="{{value}} (Email:'{{row.mailTo}}')" >{{value}}</div> + <ng-template #truncateText let-value="value" ngx-datatable-cell-template> + <div class='truncate-text' title="{{value}}">{{ value }}</div> </ng-template> - <ng-template #credentialType let-row="row" let-value="value" ngx-datatable-cell-template> - <div class='truncate-text' >{{value['CREDENTIAL_TYPE']}}</div> + <ng-template #forUser let-row="row" let-value="value" ngx-datatable-cell-template> + <div class='truncate-text' title="{{value}} (Email:'{{row.mailTo}}')">{{ value }}</div> + </ng-template> + <ng-template #credentialType let-row="row" let-value="value" ngx-datatable-cell-template> + <div class='truncate-text'>{{ value['CREDENTIAL_TYPE'] }}</div> </ng-template> </smp-search-table> diff --git a/smp-angular/src/app/alert/alert.component.ts b/smp-angular/src/app/common/panels/alert-panel/alert-panel.component.ts similarity index 76% rename from smp-angular/src/app/alert/alert.component.ts rename to smp-angular/src/app/common/panels/alert-panel/alert-panel.component.ts index f89d0b4f4bdb6bbe7db69df381bb8e9792b478c6..9e9dd40d8c13f0c4e25a423e60b93d256d9d39b4 100644 --- a/smp-angular/src/app/alert/alert.component.ts +++ b/smp-angular/src/app/common/panels/alert-panel/alert-panel.component.ts @@ -2,29 +2,32 @@ import { AfterViewChecked, AfterViewInit, ChangeDetectorRef, - Component, + Component, Input, OnInit, TemplateRef, ViewChild } from '@angular/core'; -import {ColumnPicker} from '../common/column-picker/column-picker.model'; +import {ColumnPicker} from '../../column-picker/column-picker.model'; import {MatDialog} from '@angular/material/dialog'; -import {AlertMessageService} from '../common/alert-message/alert-message.service'; +import {AlertMessageService} from '../../alert-message/alert-message.service'; import {AlertController} from './alert-controller'; import {HttpClient} from '@angular/common/http'; -import {SmpConstants} from "../smp.constants"; -import {GlobalLookups} from "../common/global-lookups"; -import {SearchTableComponent} from "../common/search-table/search-table.component"; -import {SecurityService} from "../security/security.service"; -import {ObjectPropertiesDialogComponent} from "../common/dialogs/object-properties-dialog/object-properties-dialog.component"; - - +import {SmpConstants} from "../../../smp.constants"; +import {GlobalLookups} from "../../global-lookups"; +import {SearchTableComponent} from "../../search-table/search-table.component"; +import {SecurityService} from "../../../security/security.service"; +import {ObjectPropertiesDialogComponent} from "../../dialogs/object-properties-dialog/object-properties-dialog.component"; + +/** + * This is a generic alert panel component for previewing alert list + */ @Component({ - templateUrl: './alert.component.html', - styleUrls: ['./alert.component.css'] + selector: 'alert-panel', + templateUrl: './alert-panel.component.html', + styleUrls: ['./alert-panel.component.css'] }) -export class AlertComponent implements OnInit, AfterViewInit, AfterViewChecked { +export class AlertPanelComponent implements OnInit, AfterViewInit, AfterViewChecked { @ViewChild('rowMetadataAction') rowMetadataAction: TemplateRef<any>; @ViewChild('rowActions') rowActions: TemplateRef<any>; @@ -34,17 +37,15 @@ export class AlertComponent implements OnInit, AfterViewInit, AfterViewChecked { @ViewChild('credentialType') credentialType: TemplateRef<any>; @ViewChild('forUser') forUser: TemplateRef<any>; - readonly dateTimeFormat: string = SmpConstants.DATE_TIME_FORMAT; readonly dateFormat: string = SmpConstants.DATE_FORMAT; - baseUrl = SmpConstants.REST_INTERNAL_ALERT_MANAGE; + @Input() baseUrl = null; columnPicker: ColumnPicker = new ColumnPicker(); alertController: AlertController; filter: any = {}; isSMPIntegrationOn: boolean = false; - constructor(public securityService: SecurityService, protected lookups: GlobalLookups, protected http: HttpClient, @@ -54,7 +55,7 @@ export class AlertComponent implements OnInit, AfterViewInit, AfterViewChecked { } ngOnInit() { - this.alertController = new AlertController(this.http, this.lookups, this.dialog); + this.alertController = new AlertController(this.lookups, this.dialog); } ngAfterViewChecked() { @@ -68,9 +69,17 @@ export class AlertComponent implements OnInit, AfterViewInit, AfterViewChecked { title: "Alert date", prop: 'reportingTime', showInitially: true, - maxWidth: 100, + maxWidth: 250, cellTemplate: this.dateTimeColumn, }, + { + name: 'Alert level', + title: "Alert level.", + prop: 'alertLevel', + showInitially: true, + maxWidth: 100, + + }, { name: 'For User', title: "For User", @@ -109,14 +118,6 @@ export class AlertComponent implements OnInit, AfterViewInit, AfterViewChecked { showInitially: true, }, - { - name: 'Alert level', - title: "Alert level.", - prop: 'alertLevel', - showInitially: true, - maxWidth: 80, - - }, ]; this.columnPicker.selectedColumns = this.columnPicker.allColumns.filter(col => col.showInitially); this.searchTable.tableColumnInit(); @@ -141,6 +142,4 @@ export class AlertComponent implements OnInit, AfterViewInit, AfterViewChecked { isDirty(): boolean { return this.searchTable.isDirty(); } - - } diff --git a/smp-angular/src/app/alert/alert-ro.model.ts b/smp-angular/src/app/common/panels/alert-panel/alert-ro.model.ts similarity index 75% rename from smp-angular/src/app/alert/alert-ro.model.ts rename to smp-angular/src/app/common/panels/alert-panel/alert-ro.model.ts index 4f83ab2fc4a67e2301d39ac920926b470e028a4a..67af8e91fdfbc070bc7c89d0596d9debdc412f8a 100644 --- a/smp-angular/src/app/alert/alert-ro.model.ts +++ b/smp-angular/src/app/common/panels/alert-panel/alert-ro.model.ts @@ -1,4 +1,4 @@ -import {SearchTableEntity} from '../common/search-table/search-table-entity.model'; +import {SearchTableEntity} from '../../search-table/search-table-entity.model'; export interface AlertRo extends SearchTableEntity { sid: string; diff --git a/smp-angular/src/app/smp.constants.ts b/smp-angular/src/app/smp.constants.ts index 7d860932c33786a2071b43d76a7cfff1aeed1443..1ee5df2bbf818038ddce885c4f237164168b2717 100644 --- a/smp-angular/src/app/smp.constants.ts +++ b/smp-angular/src/app/smp.constants.ts @@ -33,6 +33,7 @@ export class SmpConstants { public static readonly PATH_PARAM_KEYSTORE_PWD = '{keystore-pwd}'; public static readonly PATH_PARAM_KEYSTORE_TYPE = '{keystore-type}'; + public static readonly PATH_RESOURCE_TYPE_ALERT = 'alert'; public static readonly PATH_RESOURCE_TYPE_DOMAIN = 'domain'; public static readonly PATH_RESOURCE_TYPE_MEMBER = 'member'; public static readonly PATH_RESOURCE_TYPE_GROUP = 'group'; @@ -138,11 +139,13 @@ export class SmpConstants { // user public services public static readonly REST_PUBLIC_USER = SmpConstants.REST_PUBLIC + 'user'; - public static readonly REST_PUBLIC_USER_UPDATE = SmpConstants.REST_PUBLIC_USER + '/' + SmpConstants.PATH_PARAM_ENC_USER_ID + '/'; - public static readonly REST_PUBLIC_USER_GENERATE_ACCESS_TOKEN = SmpConstants.REST_PUBLIC_USER_UPDATE + 'generate-access-token'; - public static readonly REST_PUBLIC_USER_CHANGE_PASSWORD = SmpConstants.REST_PUBLIC_USER_UPDATE + 'change-password'; - public static readonly REST_PUBLIC_USER_SEARCH = SmpConstants.REST_PUBLIC_USER + '/' + SmpConstants.PATH_PARAM_ENC_USER_ID + '/' + SmpConstants.PATH_ACTION_SEARCH; + public static readonly REST_PUBLIC_USER_MANAGE = SmpConstants.REST_PUBLIC_USER + '/' + SmpConstants.PATH_PARAM_ENC_USER_ID + '/'; + public static readonly REST_PUBLIC_USER_ALERT = SmpConstants.REST_PUBLIC_USER_MANAGE + 'alert'; + public static readonly REST_PUBLIC_USER_GENERATE_ACCESS_TOKEN = SmpConstants.REST_PUBLIC_USER_MANAGE + 'generate-access-token'; + public static readonly REST_PUBLIC_USER_CHANGE_PASSWORD = SmpConstants.REST_PUBLIC_USER_MANAGE + 'change-password'; + + public static readonly REST_PUBLIC_USER_SEARCH = SmpConstants.REST_PUBLIC_USER_MANAGE + SmpConstants.PATH_ACTION_SEARCH; // truststore public services public static readonly REST_PUBLIC_TRUSTSTORE = SmpConstants.REST_PUBLIC + "truststore/" + '/' + SmpConstants.PATH_PARAM_ENC_USER_ID + '/'; public static readonly REST_PUBLIC_TRUSTSTORE_CERT_VALIDATE = SmpConstants.REST_PUBLIC_TRUSTSTORE + 'validate-certificate'; @@ -174,7 +177,9 @@ export class SmpConstants { //------------------------------ // internal endpoints - public static readonly REST_INTERNAL_ALERT_MANAGE = SmpConstants.REST_INTERNAL + 'alert'; + public static readonly REST_INTERNAL_ALERT_MANAGE = SmpConstants.REST_INTERNAL + SmpConstants.PATH_RESOURCE_TYPE_ALERT + + '/' + SmpConstants.PATH_PARAM_ENC_USER_ID; + public static readonly REST_INTERNAL_DOMAIN_MANAGE_DEPRECATED = SmpConstants.REST_INTERNAL + SmpConstants.PATH_RESOURCE_TYPE_DOMAIN; public static readonly REST_INTERNAL_DOMAIN_MANAGE = SmpConstants.REST_INTERNAL + SmpConstants.PATH_RESOURCE_TYPE_DOMAIN + diff --git a/smp-angular/src/app/system-settings/admin-alerts/admin-alerts.component.html b/smp-angular/src/app/system-settings/admin-alerts/admin-alerts.component.html new file mode 100644 index 0000000000000000000000000000000000000000..d1bb9d3d926eb74a4976183c5da7a63f33c48995 --- /dev/null +++ b/smp-angular/src/app/system-settings/admin-alerts/admin-alerts.component.html @@ -0,0 +1 @@ +<alert-panel [baseUrl]="adminAlertsUrl"></alert-panel> diff --git a/smp-angular/src/app/system-settings/admin-alerts/admin-alerts.component.scss b/smp-angular/src/app/system-settings/admin-alerts/admin-alerts.component.scss new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/smp-angular/src/app/system-settings/admin-alerts/admin-alerts.component.ts b/smp-angular/src/app/system-settings/admin-alerts/admin-alerts.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..90a02bcfada042ca202fc2b50f45c6823f8096ad --- /dev/null +++ b/smp-angular/src/app/system-settings/admin-alerts/admin-alerts.component.ts @@ -0,0 +1,71 @@ +import {Component, OnDestroy, OnInit,} from '@angular/core'; +import {SecurityService} from "../../security/security.service"; +import {User} from "../../security/user.model"; +import {UserRo} from "../user/user-ro.model"; +import {MatDialog} from "@angular/material/dialog"; +import {SecurityEventService} from "../../security/security-event.service"; +import {Subscription} from "rxjs"; +import {SmpConstants} from "../../smp.constants"; +import {BeforeLeaveGuard} from "../../window/sidenav/navigation-on-leave-guard"; + + +@Component({ + templateUrl: './admin-alerts.component.html', + styleUrls: ['./admin-alerts.component.scss'] +}) +export class AdminAlertsComponent implements OnInit, OnDestroy, BeforeLeaveGuard { + + currentUserData: UserRo; + loggedInUser: User; + + private securityEventServiceSub: Subscription = Subscription.EMPTY; + private onProfileDataChangedEventSub: Subscription = Subscription.EMPTY; + + constructor( + private securityService: SecurityService, + private securityEventService: SecurityEventService, + public dialog: MatDialog) { + + + this.securityEventServiceSub = this.securityEventService.onLoginSuccessEvent().subscribe(() => { + this.updateUserData(this.securityService.getCurrentUser()) + } + ); + } + + get adminAlertsUrl(): string { + return SmpConstants.REST_INTERNAL_ALERT_MANAGE + .replace(SmpConstants.PATH_PARAM_ENC_USER_ID, this.loggedInUser?.userId); + } + + ngOnInit(): void { + this.updateUserData(this.securityService.getCurrentUser()) + } + + ngOnDestroy(): void { + this.securityEventServiceSub.unsubscribe(); + this.onProfileDataChangedEventSub.unsubscribe(); + } + + /** + * This is a Readonly components, and it always returns false. + */ + isDirty(): boolean { + return false; + } + + private updateUserData(user: User) { + this.currentUserData = this.convert(user); + this.loggedInUser = user; + } + + + private convert(user: User): UserRo { + return { + ...user, + active: true, + status: undefined, + statusPassword: 0 + } as UserRo; + } +} diff --git a/smp-angular/src/app/system-settings/user/user.service.ts b/smp-angular/src/app/system-settings/user/user.service.ts index 92deab57948fb3bff5e8e1db49370f43ad836f48..118fb96c36f202af6230787b7be68834cb4be748 100644 --- a/smp-angular/src/app/system-settings/user/user.service.ts +++ b/smp-angular/src/app/system-settings/user/user.service.ts @@ -35,7 +35,7 @@ export class UserService { } updateUser(user: User) { - this.http.put<User>(SmpConstants.REST_PUBLIC_USER_UPDATE.replace(SmpConstants.PATH_PARAM_ENC_USER_ID, user.userId), user).subscribe(response => { + this.http.put<User>(SmpConstants.REST_PUBLIC_USER_MANAGE.replace(SmpConstants.PATH_PARAM_ENC_USER_ID, user.userId), user).subscribe(response => { this.notifyProfileDataChanged(response) this.securityService.updateUserDetails(response); this.alertService.success('The operation \'update user\' completed successfully.'); diff --git a/smp-angular/src/app/user-settings/user-alerts/user-alerts.component.html b/smp-angular/src/app/user-settings/user-alerts/user-alerts.component.html new file mode 100644 index 0000000000000000000000000000000000000000..e406743a25f0d04d9ca8dd509fe97dc6bce0c582 --- /dev/null +++ b/smp-angular/src/app/user-settings/user-alerts/user-alerts.component.html @@ -0,0 +1 @@ +<alert-panel [baseUrl]="userAlertsUrl"></alert-panel> diff --git a/smp-angular/src/app/user-settings/user-alerts/user-alerts.component.scss b/smp-angular/src/app/user-settings/user-alerts/user-alerts.component.scss new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/smp-angular/src/app/user-settings/user-alerts/user-alerts.component.ts b/smp-angular/src/app/user-settings/user-alerts/user-alerts.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..d42062dd6574b4a53b7f7e811fc4f2cb3a774d8b --- /dev/null +++ b/smp-angular/src/app/user-settings/user-alerts/user-alerts.component.ts @@ -0,0 +1,78 @@ +import {Component, OnDestroy, OnInit,} from '@angular/core'; +import {SecurityService} from "../../security/security.service"; +import {User} from "../../security/user.model"; +import {UserService} from "../../system-settings/user/user.service"; +import {UserRo} from "../../system-settings/user/user-ro.model"; +import {MatDialog} from "@angular/material/dialog"; +import {SecurityEventService} from "../../security/security-event.service"; +import {Subscription} from "rxjs"; +import {SmpConstants} from "../../smp.constants"; +import {BeforeLeaveGuard} from "../../window/sidenav/navigation-on-leave-guard"; + + +@Component({ + templateUrl: './user-alerts.component.html', + styleUrls: ['./user-alerts.component.scss'] +}) +export class UserAlertsComponent implements OnInit, OnDestroy, BeforeLeaveGuard { + + currentUserData: UserRo; + loggedInUser: User; + + private securityEventServiceSub: Subscription = Subscription.EMPTY; + private onProfileDataChangedEventSub: Subscription = Subscription.EMPTY; + + constructor( + private userService: UserService, + private securityService: SecurityService, + private securityEventService: SecurityEventService, + public dialog: MatDialog) { + + + this.securityEventServiceSub = this.securityEventService.onLoginSuccessEvent().subscribe(() => { + this.updateUserData(this.securityService.getCurrentUser()) + } + ); + this.onProfileDataChangedEventSub = userService.onProfileDataChangedEvent().subscribe(updatedUser => { + this.updateUserData(updatedUser); + } + ); + } + + get userAlertsUrl(): string { + return SmpConstants.REST_PUBLIC_USER_ALERT + .replace(SmpConstants.PATH_PARAM_ENC_USER_ID, this.loggedInUser?.userId); + } + + ngOnInit(): void { + this.updateUserData(this.securityService.getCurrentUser()) + } + + ngOnDestroy(): void { + this.securityEventServiceSub.unsubscribe(); + this.onProfileDataChangedEventSub.unsubscribe(); + } + + /** + * This is a Readonly components, and it always returns false. + */ + isDirty(): boolean { + return false; + } + + + private updateUserData(user: User) { + this.currentUserData = this.convert(user); + this.loggedInUser = user; + } + + + private convert(user: User): UserRo { + return { + ...user, + active: true, + status: undefined, + statusPassword: 0 + } as UserRo; + } +} diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/ui/UIServiceBase.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/ui/UIServiceBase.java index 04cda852e6ca292231df685f9489ab72681bc45a..9943d468d675229cc36520a10927351b2ac4d333 100644 --- a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/ui/UIServiceBase.java +++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/ui/UIServiceBase.java @@ -58,11 +58,12 @@ abstract class UIServiceBase<E extends BaseEntity, R> { /** * Method returns UI resource object list for page. * - * @param page - * @param pageSize - * @param sortField - * @param sortOrder - * @return + * @param page - page number (0..n) + * @param pageSize - page size (0..n) - if -1 return all results + * @param sortField - sort field name (null - sorts by id) + * @param sortOrder - sort order (null - sorts by acs ) + * @param filter - filter object (null - no filter) + * @return ServiceResult<R> - list of UI resource objects */ public ServiceResult<R> getTableList(int page, int pageSize, String sortField, diff --git a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/external/UserController.java b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/external/UserController.java index f38cec9eea683aa641303c05eb7176d934bd556d..857054bfb28991478e87abba49fdd50f92249b0f 100644 --- a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/external/UserController.java +++ b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/external/UserController.java @@ -8,9 +8,9 @@ * versions of the EUPL (the "Licence"); * You may not use this work except in compliance with the Licence. * You may obtain a copy of the Licence at: - * + * * [PROJECT_HOME]\license\eupl-1.2\license.txt or https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 - * + * * Unless required by applicable law or agreed to in writing, software distributed under the Licence is * distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the Licence for the specific language governing permissions and limitations under the Licence. @@ -27,6 +27,7 @@ import eu.europa.ec.edelivery.smp.data.model.user.DBUser; import eu.europa.ec.edelivery.smp.data.ui.*; import eu.europa.ec.edelivery.smp.logging.SMPLogger; import eu.europa.ec.edelivery.smp.logging.SMPLoggerFactory; +import eu.europa.ec.edelivery.smp.services.ui.UIAlertService; import eu.europa.ec.edelivery.smp.services.ui.UIUserService; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.util.MimeTypeUtils; @@ -36,8 +37,7 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.util.List; -import static eu.europa.ec.edelivery.smp.ui.ResourceConstants.CONTEXT_PATH_PUBLIC_USER; -import static eu.europa.ec.edelivery.smp.ui.ResourceConstants.PARAM_PAGINATION_FILTER; +import static eu.europa.ec.edelivery.smp.ui.ResourceConstants.*; import static eu.europa.ec.edelivery.smp.utils.SessionSecurityUtils.decryptEntityId; /** @@ -49,14 +49,16 @@ import static eu.europa.ec.edelivery.smp.utils.SessionSecurityUtils.decryptEntit public class UserController { private static final SMPLogger LOG = SMPLoggerFactory.getLogger(UserController.class); - protected UIUserService uiUserService; - protected SMPAuthorizationService authorizationService; - protected SMPAuthenticationService authenticationService; + private final UIUserService uiUserService; + private final UIAlertService uiAlertService; + private final SMPAuthorizationService authorizationService; + private final SMPAuthenticationService authenticationService; - public UserController(UIUserService uiUserService, SMPAuthorizationService authorizationService, SMPAuthenticationService authenticationService) { + public UserController(UIUserService uiUserService, SMPAuthorizationService authorizationService, SMPAuthenticationService authenticationService, UIAlertService uiAlertService) { this.uiUserService = uiUserService; this.authorizationService = authorizationService; this.authenticationService = authenticationService; + this.uiAlertService = uiAlertService; } @PreAuthorize("@smpAuthorizationService.isCurrentlyLoggedIn(#userId)") @@ -161,7 +163,7 @@ public class UserController { Long accessTokenId = decryptEntityId(encAccessTokenId); // delete user credential - CredentialRO result = uiUserService.deleteUserCredentials(userId, + CredentialRO result = uiUserService.deleteUserCredentials(userId, accessTokenId, CredentialType.ACCESS_TOKEN, CredentialTargetType.REST_API); // set the same encrypted id so that UI can locate and update it result.setCredentialId(encAccessTokenId); @@ -178,7 +180,7 @@ public class UserController { Long accessTokenId = decryptEntityId(encAccessTokenId); // delete user credential - CredentialRO result = uiUserService.updateUserCredentials(userId, + CredentialRO result = uiUserService.updateUserCredentials(userId, accessTokenId, CredentialType.ACCESS_TOKEN, CredentialTargetType.REST_API, @@ -216,7 +218,7 @@ public class UserController { Long userId = decryptEntityId(encUserId); Long credentialId = decryptEntityId(encCredentialId); // delete user credential - CredentialRO result = uiUserService.deleteUserCredentials(userId, + CredentialRO result = uiUserService.deleteUserCredentials(userId, credentialId, CredentialType.CERTIFICATE, CredentialTargetType.REST_API); // set the same encrypted credential id so that UI can remove it from the list result.setCredentialId(encCredentialId); @@ -232,7 +234,7 @@ public class UserController { Long userId = decryptEntityId(encUserId); Long credentialId = decryptEntityId(encCredentialId); // delete user credential - CredentialRO result = uiUserService.updateUserCredentials(userId, + CredentialRO result = uiUserService.updateUserCredentials(userId, credentialId, CredentialType.CERTIFICATE, CredentialTargetType.REST_API, @@ -262,6 +264,35 @@ public class UserController { return uiUserService.storeCertificateCredentialForUser(userId, credentialRO); } + /** + * Method returns Users list of alerts. To access the list user must be logged in. + * <p> + * + * @param encUserId - encrypted user id (from session) - used for authorization check + * @param page - page number (0..n) + * @param pageSize - page size (0..n) - number of results on page/max number of returned results. + * @param orderBy - order by field + * @param orderType - order type (asc, desc) + * @return ServiceResult<AlertRO> - list of alerts + */ + @PreAuthorize("@smpAuthorizationService.isCurrentlyLoggedIn(#encUserId)") + @GetMapping(path = "/{user-id}/alert", produces = {MimeTypeUtils.APPLICATION_JSON_VALUE}) + public ServiceResult<AlertRO> getUserAlertList( + @PathVariable("user-id") String encUserId, + @RequestParam(value = PARAM_PAGINATION_PAGE, defaultValue = "0") int page, + @RequestParam(value = PARAM_PAGINATION_PAGE_SIZE, defaultValue = "10") int pageSize, + @RequestParam(value = PARAM_PAGINATION_ORDER_BY, defaultValue = "id", required = false) String orderBy, + @RequestParam(value = PARAM_PAGINATION_ORDER_TYPE, defaultValue = "desc", required = false) String orderType + ) { + LOG.info("Search for page: {}, page size: {}", page, pageSize); + UserRO loggedUserData = authorizationService.getLoggedUserData(); + // set filter to current user + AlertRO filter = new AlertRO(); + filter.setUsername(loggedUserData.getUsername()); + // return the user alert list + return uiAlertService.getTableList(page, pageSize, orderBy, orderType, filter); + } + protected NavigationTreeNodeRO createPublicNavigationTreeNode() { NavigationTreeNodeRO node = new NavigationTreeNodeRO("search-tools", "Search", "search", "public"); @@ -275,7 +306,7 @@ public class UserController { node.addChild(new NavigationTreeNodeRO("user-data-profile", "Profile", "account_circle", "user-profile")); node.addChild(new NavigationTreeNodeRO("user-data-access-token", "Access tokens", "key", "user-access-token")); node.addChild(new NavigationTreeNodeRO("user-data-certificates", "Certificates", "article", "user-certificate")); - // node.addChild(new NavigationTreeNodeRO("user-data-membership", "Membership", "person", "user-membership")); + node.addChild(new NavigationTreeNodeRO("user-data-alert", "Alerts", "notifications", "user-alert")); return node; } diff --git a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/internal/AlertController.java b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/internal/AlertController.java index 6c4855d6c08e04d63a94b2d70e8d4c157aee023b..b3ae7053b118fde6617ace93cf2ea1f0f9cb4572 100644 --- a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/internal/AlertController.java +++ b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/internal/AlertController.java @@ -21,16 +21,12 @@ package eu.europa.ec.edelivery.smp.ui.internal; import eu.europa.ec.edelivery.smp.data.ui.AlertRO; import eu.europa.ec.edelivery.smp.data.ui.ServiceResult; -import eu.europa.ec.edelivery.smp.data.ui.auth.SMPAuthority; import eu.europa.ec.edelivery.smp.logging.SMPLogger; import eu.europa.ec.edelivery.smp.logging.SMPLoggerFactory; import eu.europa.ec.edelivery.smp.services.ui.UIAlertService; -import org.springframework.security.access.annotation.Secured; +import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.util.MimeTypeUtils; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; import static eu.europa.ec.edelivery.smp.ui.ResourceConstants.*; @@ -50,9 +46,20 @@ public class AlertController { this.uiAlertService = uiAlertService; } - @GetMapping(produces = {MimeTypeUtils.APPLICATION_JSON_VALUE}) - @Secured({SMPAuthority.S_AUTHORITY_TOKEN_SYSTEM_ADMIN}) + /** + * Method returns list of all alerts. + * + * @param userEncId - user id (encrypted) - used for authorization + * @param page - page number of the results to be returned. + * @param pageSize - number of results to be returned per page. + * @param orderBy - column name to order by (default: id) + * @param orderType - order type (default: desc) + * @return + */ + @GetMapping(path = "/{user-enc-id}", produces = MimeTypeUtils.APPLICATION_JSON_VALUE) + @PreAuthorize("@smpAuthorizationService.isCurrentlyLoggedIn(#userEncId) and @smpAuthorizationService.isSystemAdministrator") public ServiceResult<AlertRO> getAlertList( + @PathVariable("user-enc-id") String userEncId, @RequestParam(value = PARAM_PAGINATION_PAGE, defaultValue = "0") int page, @RequestParam(value = PARAM_PAGINATION_PAGE_SIZE, defaultValue = "10") int pageSize, @RequestParam(value = PARAM_PAGINATION_ORDER_BY, defaultValue = "id", required = false) String orderBy, diff --git a/smp-webapp/src/test/resources/spring-security-test-context.xml b/smp-webapp/src/test/resources/spring-security-test-context.xml deleted file mode 100644 index 650738cff4a9bc34c11e59b9134e2a3af0c94069..0000000000000000000000000000000000000000 --- a/smp-webapp/src/test/resources/spring-security-test-context.xml +++ /dev/null @@ -1,33 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - #START_LICENSE# - smp-webapp - %% - Copyright (C) 2017 - 2023 European Commission | eDelivery | DomiSMP - %% - Licensed under the EUPL, Version 1.2 or – as soon they will be approved by the European Commission - subsequent - versions of the EUPL (the "Licence"); - You may not use this work except in compliance with the Licence. - You may obtain a copy of the Licence at: - - [PROJECT_HOME]\license\eupl-1.2\license.txt or https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 - - Unless required by applicable law or agreed to in writing, software distributed under the Licence is - distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the Licence for the specific language governing permissions and limitations under the Licence. - #END_LICENSE# - --> - -<beans xmlns="http://www.springframework.org/schema/beans" - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xmlns:context="http://www.springframework.org/schema/context" - xsi:schemaLocation=" - http://www.springframework.org/schema/beans - http://www.springframework.org/schema/beans/spring-beans.xsd - http://www.springframework.org/schema/context - http://www.springframework.org/schema/context/spring-context.xsd"> - - <context:component-scan base-package="eu.europa.ec.cipa.smp.server.security"/> - - -</beans>