diff --git a/smp-angular/package-lock.json b/smp-angular/package-lock.json index 7bd1dcb8d76a85dd6cb0acb609d1e9c184705cec..6eab6f6b09083df8980abf543b9e76d921dd103a 100644 --- a/smp-angular/package-lock.json +++ b/smp-angular/package-lock.json @@ -3403,9 +3403,9 @@ } }, "file-saver": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/file-saver/-/file-saver-1.3.3.tgz", - "integrity": "sha512-spKHSBQIxxS81N/O21WmuXA2F6wppUCsutpzenOeZzOCCJ5gEfcbqJP983IrpLXzYmXnMUa6J03SubcNPdKrlg==" + "version": "1.3.3", + "resolved": "http://registry.npmjs.org/file-saver/-/file-saver-1.3.3.tgz", + "integrity": "sha1-zdTETTqiZOrC9o7BZbx5HDSvEjI=" }, "filename-regex": { "version": "2.0.1", diff --git a/smp-angular/src/app/alert/alert.component.css b/smp-angular/src/app/alert/alert.component.css index da456774d6e339d07a95e8bc49ef3b2933e9716b..fa0b48b862ffd624145ac3df5f650d3c7fbb22c0 100644 --- a/smp-angular/src/app/alert/alert.component.css +++ b/smp-angular/src/app/alert/alert.component.css @@ -4,6 +4,7 @@ opacity: 1; transition: opacity 0.6s; margin-bottom: 15px; + z-index: 1000; } .alert-error {background-color: #f44336;} diff --git a/smp-angular/src/app/alerts/alerts.component.ts b/smp-angular/src/app/alerts/alerts.component.ts index 320be0f5a24271d0873dcde610876e1084a887c5..1efd1223abece325efb84cd7b8cc3d4c93ac5757 100644 --- a/smp-angular/src/app/alerts/alerts.component.ts +++ b/smp-angular/src/app/alerts/alerts.component.ts @@ -206,10 +206,11 @@ export class AlertsComponent implements OnInit { this.aTypes = result.alertsType; this.loading = false; - +/* if(this.count > AlertComponent.MAX_COUNT_CSV) { this.alertService.error("Maximum number of rows reached for downloading CSV"); } + */ }, (error: any) => { console.log("error getting the alerts:" + error); this.loading = false; diff --git a/smp-angular/src/app/app-info/smp-info.service.ts b/smp-angular/src/app/app-info/smp-info.service.ts index 4a8a830ec7f0aa7ed720357f50cc71d8b14e0c03..605d37dc4d16435feffee2ebe173d72d69a21ac9 100644 --- a/smp-angular/src/app/app-info/smp-info.service.ts +++ b/smp-angular/src/app/app-info/smp-info.service.ts @@ -4,6 +4,7 @@ import {Observable, ReplaySubject} from 'rxjs'; import {Router} from '@angular/router'; import {HttpEventService} from '../http/http-event.service'; import {SmpInfo} from './smp-info.model'; +import {SmpConstants} from "../smp.constants"; @Injectable() export class SmpInfoService { @@ -12,7 +13,7 @@ export class SmpInfoService { getSmpInfo(): Observable<SmpInfo> { let subject = new ReplaySubject<SmpInfo>(); - this.http.get<SmpInfo>('rest/application/info') + this.http.get<SmpInfo>(SmpConstants.REST_APPLICATION) .subscribe((res: SmpInfo) => { subject.next(res); }, error => { diff --git a/smp-angular/src/app/app.component.html b/smp-angular/src/app/app.component.html index 4957b3e62a203a083267dc15e4bbc37ed0624638..03084d47bb1436fb36a76059f62baa8e8581fbd5 100644 --- a/smp-angular/src/app/app.component.html +++ b/smp-angular/src/app/app.component.html @@ -1,10 +1,11 @@ <mat-sidenav-container > <mat-sidenav mode="side" opened="true" ngClass="{{menuClass}}"> + <!-- sidenav content --> <div id="topLogo"> <img src="assets/images/smp_logo_icon.svg" [attr.height]="fullMenu ? '74px' : '40px'" [attr.width]="fullMenu ? '74px' : '40px'"/> <div id="topLogoText" *ngIf="fullMenu"> - <h1>eDeliver SMP <span>Administration <br>Console</span></h1> + <h1>eDelivery SMP <span>Administration <br>Console</span></h1> </div> </div> @@ -12,17 +13,17 @@ <mat-icon matTooltip="Search" matTooltipDisabled="{{fullMenu}}" matTooltipDisabled="right">search</mat-icon> <span>Search</span> </button> - <button mat-raised-button class="sideNavButton" [routerLink]="['/edit']" id="edit_id"> + <button mat-raised-button class="sideNavButton" *ngIf="isCurrentUserSMPAdmin() || isCurrentUserServiceGroupAdmin()" [routerLink]="['/edit']" id="edit_id"> <mat-icon matTooltip="Edit" matTooltipDisabled="{{fullMenu}}" matTooltipDisabled="right">edit</mat-icon> <span>Edit</span> </button> - <button mat-raised-button class="sideNavButton" [routerLink]="['/domain']" id="domain_id"> + <button mat-raised-button class="sideNavButton" [routerLink]="['/domain']" *ngIf="isCurrentUserSystemAdmin()" id="domain_id"> <mat-icon matTooltip="Domain" matTooltipDisabled="{{fullMenu}}" matTooltipDisabled="right">domain</mat-icon> <span>Domain</span> </button> <!-- button mat-raised-button class="sideNavButton" [routerLink]="['/user']" *ngIf="hasAdmin()" id="user_id" --> - <button mat-raised-button class="sideNavButton" [routerLink]="['/user']" id="user_id"> + <button mat-raised-button class="sideNavButton" [routerLink]="['/user']" *ngIf="isCurrentUserSystemAdmin()" id="user_id"> <mat-icon matTooltip="Users" matTooltipDisabled="{{fullMenu}}" matTooltipDisabled="right">people</mat-icon> <span>Users</span> </button> @@ -59,15 +60,14 @@ <div id=".my-content" style="position:absolute; bottom:5px; top:5px; right: 5px; left: 5px"> - <div id="sandwichMenuHolder"> - + <div id="sandwichMenuHolder" style="z-index: 500"> - <domain-selector *ngIf="currentUser" [currentComponent]="outlet.activated ? outlet.component : null"></domain-selector> - <div class="helpMenu"> - <page-helper></page-helper> - </div> <div id="sandwichMenu"> + + <a *ngIf="!currentUser" [routerLink]="['/login']" > Login </a> + <span *ngIf="currentUser" >User: {{currentUser}} </span > + <button mat-icon-button [mat-menu-trigger-for]="settingsMenu" id="settingsmenu_id" matTooltip="Menu"> <mat-icon>menu</mat-icon> </button> diff --git a/smp-angular/src/app/app.component.ts b/smp-angular/src/app/app.component.ts index 21632f9e89deb2b06405d7840a4bf3c08482aea9..2c2f1eb1a85f7ffc347bd9d76a6b2e3010e88cf0 100644 --- a/smp-angular/src/app/app.component.ts +++ b/smp-angular/src/app/app.component.ts @@ -3,8 +3,9 @@ import {SecurityService} from './security/security.service'; import {Router, RouterOutlet} from '@angular/router'; import {SecurityEventService} from './security/security-event.service'; import {Title} from '@angular/platform-browser'; -import {Observable} from 'rxjs'; import {HttpClient, HttpResponse} from '@angular/common/http'; +import {Authority} from "./security/authority.model"; + @Component({ selector: 'app-root', @@ -13,7 +14,6 @@ import {HttpClient, HttpResponse} from '@angular/common/http'; }) export class AppComponent implements OnInit { - isAdmin: boolean; _currentUser: string; fullMenu: boolean = true; menuClass: string = this.fullMenu ? "menu-expanded" : "menu-collapsed"; @@ -26,17 +26,17 @@ export class AppComponent implements OnInit { private securityEventService: SecurityEventService, private http: HttpClient, private titleService: Title) { - +/* let applicationNameResponse: Observable<string> = this.http.get<string>('rest/application/name'); applicationNameResponse.subscribe((name: string) => { this.titleService.setTitle(name); - - }); + */ } ngOnInit() { + /* this.securityEventService.onLoginSuccessEvent().subscribe( data => { this.isAdmin = this.securityService.isCurrentUserAdmin(); @@ -51,11 +51,18 @@ export class AppComponent implements OnInit { data => { this.isAdmin = this.securityService.isCurrentUserAdmin(); this.router.navigate(['/login']); - }); + });*/ + } + + isCurrentUserSystemAdmin(): boolean { + return this.securityService.isCurrentUserInRole([Authority.SYSTEM_ADMIN]); } - hasAdmin(): boolean { - return this.securityService.isCurrentUserAdmin(); + isCurrentUserSMPAdmin(): boolean { + return this.securityService.isCurrentUserInRole([ Authority.SMP_ADMIN]); + } + isCurrentUserServiceGroupAdmin(): boolean { + return this.securityService.isCurrentUserInRole([ Authority.SERVICE_GROUP_ADMIN]); } get currentUser(): string { @@ -65,7 +72,7 @@ export class AppComponent implements OnInit { logout(event: Event): void { event.preventDefault(); - this.router.navigate(['/login']).then((ok) => { + this.router.navigate(['/search']).then((ok) => { if (ok) { this.securityService.logout(); } diff --git a/smp-angular/src/app/app.module.ts b/smp-angular/src/app/app.module.ts index 969a44e5eb6f0dbe5cae12bcd5cfa947dc0b5d1d..2fe9e94bdc85352d25dbe220d5bf483959e8bd13 100644 --- a/smp-angular/src/app/app.module.ts +++ b/smp-angular/src/app/app.module.ts @@ -2,22 +2,23 @@ import {BrowserModule} from '@angular/platform-browser'; import {NgModule} from '@angular/core'; import {FormsModule, ReactiveFormsModule} from '@angular/forms'; import {HttpClient, HttpClientModule} from '@angular/common/http'; -import { FlexLayoutModule } from "@angular/flex-layout"; +import {FlexLayoutModule} from "@angular/flex-layout"; import { MatButtonModule, + MatCardModule, + MatDatepickerModule, MatDialogModule, + MatExpansionModule, MatIconModule, MatInputModule, MatListModule, MatMenuModule, MatSelectModule, MatSidenavModule, - MatTooltipModule, - MatExpansionModule, - MatDatepicker, - MatCardModule, - MatDatepickerModule, MatSlideToggleModule, + MatTabsModule, + MatToolbarModule, + MatTooltipModule, } from '@angular/material'; import "hammerjs"; @@ -70,6 +71,11 @@ import {UserDetailsDialogComponent} from './user/user-details-dialog/user-detail import {DownloadService} from './download/download.service'; import {UserService} from './user/user.service'; import {CertificateService} from './user/certificate.service'; +import {GlobalLookups} from "./common/global-lookups"; +import {ServiceGroupExtensionWizardDialogComponent} from "./service-group-edit/service-group-extension-wizard-dialog/service-group-extension-wizard-dialog.component"; +import {ServiceMetadataWizardDialogComponent} from "./service-group-edit/service-metadata-wizard-dialog/service-metadata-wizard-dialog.component"; +import {ConfirmationDialogComponent} from "./common/confirmation-dialog/confirmation-dialog.component"; +import {SpinnerComponent} from "./common/spinner/spinner.component"; @NgModule({ declarations: [ @@ -83,15 +89,19 @@ import {CertificateService} from './user/certificate.service'; UserComponent, AlertComponent, FooterComponent, + SpinnerComponent, IsAuthorized, SaveDialogComponent, ServiceGroupMetadataDialogComponent, CancelDialogComponent, + ConfirmationDialogComponent, RowLimiterComponent, DatePipe, CapitalizeFirstPipe, DefaultPasswordDialogComponent, ServiceGroupDetailsDialogComponent, + ServiceGroupExtensionWizardDialogComponent, + ServiceMetadataWizardDialogComponent, ColumnPickerComponent, PageHelperComponent, ClearInvalidDirective, @@ -105,9 +115,12 @@ import {CertificateService} from './user/certificate.service'; AppComponent, ServiceGroupMetadataDialogComponent, ServiceGroupDetailsDialogComponent, + ServiceGroupExtensionWizardDialogComponent, + ServiceMetadataWizardDialogComponent, DomainDetailsDialogComponent, UserDetailsDialogComponent, CancelDialogComponent, + ConfirmationDialogComponent, SaveDialogComponent, DefaultPasswordDialogComponent, ], @@ -123,12 +136,14 @@ import {CertificateService} from './user/certificate.service'; MatDatepickerModule, MatDialogModule, MatTooltipModule, + MatToolbarModule, MatMenuModule, MatInputModule, MatIconModule, MatListModule, MatSidenavModule, MatSelectModule, + MatTabsModule, MatSlideToggleModule, routing, ReactiveFormsModule, @@ -149,6 +164,7 @@ import {CertificateService} from './user/certificate.service'; DownloadService, UserService, CertificateService, + GlobalLookups, DatePipe, { provide: ExtendedHttpClient, diff --git a/smp-angular/src/app/app.routes.ts b/smp-angular/src/app/app.routes.ts index 9044c4b6660ab6f535f97cc2c839832d2313f4d3..a1eaebe8bc3bfff2fc376068602cc6941e08fddb 100644 --- a/smp-angular/src/app/app.routes.ts +++ b/smp-angular/src/app/app.routes.ts @@ -5,19 +5,17 @@ import {ServiceGroupEditComponent} from './service-group-edit/service-group-edit import {DomainComponent} from './domain/domain.component'; import {AuthenticatedGuard} from './guards/authenticated.guard'; import {UserComponent} from './user/user.component'; +import {DirtyGuard} from "./common/dirty.guard"; const appRoutes: Routes = [ {path: '', component: ServiceGroupSearchComponent}, - {path: 'search', component: ServiceGroupSearchComponent}, - {path: 'edit', component: ServiceGroupEditComponent}, - {path: 'domain', component: DomainComponent}, - {path: 'user', component: UserComponent}, - - + {path: 'search', redirectTo: ''}, + {path: 'edit', component: ServiceGroupEditComponent, canActivate: [AuthenticatedGuard], canDeactivate: [DirtyGuard]}, + {path: 'domain', component: DomainComponent, canActivate: [AuthenticatedGuard], canDeactivate: [DirtyGuard]}, + {path: 'user', component: UserComponent, canActivate: [AuthenticatedGuard], canDeactivate: [DirtyGuard]}, {path: 'login', component: LoginComponent}, - {path: '**', component: ServiceGroupEditComponent, canActivate: [AuthenticatedGuard]} - + {path: '**', redirectTo: ''} ]; -export const routing = RouterModule.forRoot(appRoutes); +export const routing = RouterModule.forRoot(appRoutes, {useHash: true}); diff --git a/smp-angular/src/app/common/confirmation-dialog/confirmation-dialog.component.css b/smp-angular/src/app/common/confirmation-dialog/confirmation-dialog.component.css new file mode 100644 index 0000000000000000000000000000000000000000..5e50dc67f4b2471a98467e74fa1e4d596015b9a2 --- /dev/null +++ b/smp-angular/src/app/common/confirmation-dialog/confirmation-dialog.component.css @@ -0,0 +1,42 @@ +label:hover, label:active, input:hover + label, input:active + label { + color: #3f51b5; +} + +.divTable { + display: table; + width: 100%; +} + +.divTableRow { + display: table-row; +} + +.divTableHeading { + background-color: #EEE; + display: table-header-group; +} + +.divTableCell, .divTableHead { + /*border: 1px solid #999999;*/ + display: table-cell; + padding: 3px 3px; + text-align: center; +} + +.divTableHeading { + background-color: #EEE; + display: table-header-group; + font-weight: bold; +} + +.divTableFoot { + background-color: #EEE; + display: table-footer-group; + font-weight: bold; +} + +.divTableBody { + display: table-row-group; +} + + diff --git a/smp-angular/src/app/common/confirmation-dialog/confirmation-dialog.component.html b/smp-angular/src/app/common/confirmation-dialog/confirmation-dialog.component.html new file mode 100644 index 0000000000000000000000000000000000000000..6b964053629e7993ed9d49d8a91cb28fcaee2f85 --- /dev/null +++ b/smp-angular/src/app/common/confirmation-dialog/confirmation-dialog.component.html @@ -0,0 +1,26 @@ +<div style="width: 500px;text-align: center"> + <h1 mat-dialog-title>{{title}}</h1> + <div class="panel">{{description}}</div> + <div class="divTable"> + <div class="divTableBody"> + + <div class="divTableRow"> + + <div class="divTableCell"> + <button mat-raised-button color="primary" (click)="dialogRef.close(true)" id="yesbuttondialog_id"> + <mat-icon>check_circle</mat-icon> + <span>Yes</span> + </button> + </div> + + <div class="divTableCell"> + <button mat-raised-button color="primary" (click)="dialogRef.close(false)" id="nobuttondialog_id"> + <mat-icon>cancel</mat-icon> + <span>No</span> + </button> + </div> + + </div> + </div> + </div> +</div> diff --git a/smp-angular/src/app/common/confirmation-dialog/confirmation-dialog.component.ts b/smp-angular/src/app/common/confirmation-dialog/confirmation-dialog.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..35d6694cf36b4a3bb45cd5182107bab6c8809540 --- /dev/null +++ b/smp-angular/src/app/common/confirmation-dialog/confirmation-dialog.component.ts @@ -0,0 +1,21 @@ +import {Component, Inject} from '@angular/core'; +import {MAT_DIALOG_DATA, MatDialogRef} from '@angular/material'; + +@Component({ + selector: 'app-confirmation-dialog', + templateUrl: './confirmation-dialog.component.html', + styleUrls: ['./confirmation-dialog.component.css'] +}) +export class ConfirmationDialogComponent { + + title: string; + description: string + + constructor(public dialogRef: MatDialogRef<ConfirmationDialogComponent>, + @Inject(MAT_DIALOG_DATA) public data: any) { + this.title=data.title; + this.description=data.description; + } + + +} diff --git a/smp-angular/src/app/common/dirty.guard.ts b/smp-angular/src/app/common/dirty.guard.ts index 246805b06894bdec6867b63df480238234b6d190..d81ea50201409962d802376bb55367a6c47886ee 100644 --- a/smp-angular/src/app/common/dirty.guard.ts +++ b/smp-angular/src/app/common/dirty.guard.ts @@ -1,6 +1,6 @@ -import { Injectable } from '@angular/core'; -import {CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, CanDeactivate} from '@angular/router'; -import { Observable } from 'rxjs'; +import {Injectable} from '@angular/core'; +import {ActivatedRouteSnapshot, CanActivate, CanDeactivate, RouterStateSnapshot} from '@angular/router'; +import {Observable} from 'rxjs'; import {MatDialog} from '@angular/material'; import {CancelDialogComponent} from './cancel-dialog/cancel-dialog.component'; @@ -16,7 +16,8 @@ export class DirtyGuard implements CanActivate, CanDeactivate<any> { return true; } - canDeactivate(component: any, currentRoute: ActivatedRouteSnapshot, currentState: RouterStateSnapshot, nextState?: RouterStateSnapshot): Observable<boolean> | Promise<boolean> | boolean { + canDeactivate(component: any, currentRoute: ActivatedRouteSnapshot, currentState: RouterStateSnapshot, + nextState?: RouterStateSnapshot): Observable<boolean> | Promise<boolean> | boolean { if (component.isDirty && !component.isDirty()) return true; return this.dialog.open(CancelDialogComponent).afterClosed(); } diff --git a/smp-angular/src/app/common/global-lookups.ts b/smp-angular/src/app/common/global-lookups.ts new file mode 100644 index 0000000000000000000000000000000000000000..413139126d0a90228ba7f39d09eb3e01190ece8d --- /dev/null +++ b/smp-angular/src/app/common/global-lookups.ts @@ -0,0 +1,59 @@ +import {Injectable, OnInit} from '@angular/core'; +import {HttpClient} from "@angular/common/http"; +import {DomainRo} from "../domain/domain-ro.model"; +import {SearchTableResult} from "./search-table/search-table-result.model"; +import {SmpConstants} from "../smp.constants"; +import {Observable} from "rxjs/internal/Observable"; +import {UserRo} from "../user/user-ro.model"; + +/** + * Purpose of object is to fetch lookups as domains and users + */ + +@Injectable() +export class GlobalLookups implements OnInit { + + domainObserver: Observable< SearchTableResult> + userObserver: Observable< SearchTableResult> + cachedDomainList: Array<any> = []; + cachedUserList: Array<any> = []; + + + constructor(protected http: HttpClient){ + this.refreshDomainLookup(); + this.refreshUserLookup(); + } + + ngOnInit() { + } + + public refreshDomainLookup(){ + // init domains + this.domainObserver = this.http.get<SearchTableResult>(SmpConstants.REST_DOMAIN); + this.domainObserver.subscribe((domains: SearchTableResult) => { + let gotList = new Array(domains.serviceEntities.length) + .map((v, index) => domains.serviceEntities[index] as DomainRo); + this.cachedDomainList = domains.serviceEntities.map(serviceEntity => { + return {...serviceEntity} + + }); + }); + } + + public refreshUserLookup(){ + // init users + this.userObserver = this.http.get<SearchTableResult>(SmpConstants.REST_USER); + this.userObserver.subscribe((users: SearchTableResult) => { + let gotList = new Array(users.serviceEntities.length) + .map((v, index) => users.serviceEntities[index] as UserRo); + this.cachedUserList = users.serviceEntities.map(serviceEntity => { + return {...serviceEntity} + + }); + }); + } + + + + +} diff --git a/smp-angular/src/app/common/page-header/page-header.component.css b/smp-angular/src/app/common/page-header/page-header.component.css index e25fefba37540448246865b3c647afe8642a33e8..f55524a5612e996ba49828718a5bdcc63e54b0e4 100644 --- a/smp-angular/src/app/common/page-header/page-header.component.css +++ b/smp-angular/src/app/common/page-header/page-header.component.css @@ -1,4 +1,9 @@ :host:before { display: none !important; content: '' !important; + margin-bottom: 0 !important; +} + +.h1 { + margin-bottom: 0 !important; } diff --git a/smp-angular/src/app/common/search-table/search-table-controller.ts b/smp-angular/src/app/common/search-table/search-table-controller.ts index ad5d26b1a02d6ce6042c84598fcb022001ff36e5..9ccf73d49cacd4d672869323348bee5edabd6409 100644 --- a/smp-angular/src/app/common/search-table/search-table-controller.ts +++ b/smp-angular/src/app/common/search-table/search-table-controller.ts @@ -7,4 +7,5 @@ export interface SearchTableController { delete(row); newRow(): SearchTableEntity; newDialog(config?: MatDialogConfig): MatDialogRef<any>; + dataSaved(); } diff --git a/smp-angular/src/app/common/search-table/search-table.component.css b/smp-angular/src/app/common/search-table/search-table.component.css index 3da6057eebb1f09455a73e22fcf8851345b02456..2a6978a2f1115d8db20d98ded072d2709591471a 100644 --- a/smp-angular/src/app/common/search-table/search-table.component.css +++ b/smp-angular/src/app/common/search-table/search-table.component.css @@ -41,3 +41,8 @@ padding: 0 !important; margin: 0 !important; } + +.h1 { + margin-bottom: 0 !important; +} + diff --git a/smp-angular/src/app/common/search-table/search-table.component.html b/smp-angular/src/app/common/search-table/search-table.component.html index 2b52cd6f8eeb1bbf98d85f65ad60578b6d3ceea4..653e98ff143bbe8f4af643441092e292a6a595b1 100644 --- a/smp-angular/src/app/common/search-table/search-table.component.html +++ b/smp-angular/src/app/common/search-table/search-table.component.html @@ -1,6 +1,5 @@ <div fxLayout="column" style="position: absolute; top: 5px; bottom: 5px; left: 5px; right: 5px;"> - <page-header flex id="{{id}}_header_id">{{title}}</page-header> - + <h1 flex id="{{id}}_header_id" style="margin: 0 !important;">{{title}}</h1> <div *ngIf="showSearchPanel" fxFlex="20" class="selectionCriteria"> <mat-card> <mat-card-content> @@ -22,7 +21,7 @@ <div class="panel" fxFlex fxLayout="column"> <div class="group-filter-button" fxFlex="50px"> <span class="row-button"> - <app-row-limiter [pageSizes]="rowLimiter.pageSizes" + <app-row-limiter [pageSizes]="rowLimiter.pageSizes" (onPageSizeChanged)="changePageSize($event.value)"></app-row-limiter> </span> <!-- no need for this for SMP 4.1 <span class="column-filter-button"> @@ -51,7 +50,7 @@ (page)="onPage($event)" (sort)="onSort($event)" [selected]="selected" - [selectionType]="'single'" + [selectionType]="'multi'" (activate)="onActivate($event)" (select)="onSelect($event)" > @@ -71,6 +70,9 @@ <ng-template #rowActions let-row="row" ngx-datatable-cell-template> <div> + <ng-container [ngTemplateOutlet]="additionalRowActionButtons" + [ngTemplateOutletContext]="{row:row}"></ng-container> + <button mat-icon-button color="primary" [disabled]="row.deleted || loading" (click)="editSearchTableEntityRow(row)" tooltip="Edit"> <mat-icon>edit</mat-icon> @@ -104,7 +106,9 @@ <mat-icon>save</mat-icon> <span>Save</span> </button> - <button id="newButton" mat-raised-button (click)="onNewButtonClicked()" [disabled]="loading" color="primary"> + <!-- new button enabled --> + <button id="newButton" mat-raised-button (click)="onNewButtonClicked()" + [disabled]="loading || !allowNewItems" color="primary"> <mat-icon>add</mat-icon> <span>New</span> </button> diff --git a/smp-angular/src/app/common/search-table/search-table.component.ts b/smp-angular/src/app/common/search-table/search-table.component.ts index 03849f5e9d62860911d65e80ca184db3bd065506..5403d18067c29e83ce4f6620472a8407e6742581 100644 --- a/smp-angular/src/app/common/search-table/search-table.component.ts +++ b/smp-angular/src/app/common/search-table/search-table.component.ts @@ -1,19 +1,20 @@ -import {ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnInit, TemplateRef, ViewChild} from '@angular/core'; +import {Component, Input, OnInit, TemplateRef, ViewChild} from '@angular/core'; import {SearchTableResult} from './search-table-result.model'; import {Observable} from 'rxjs'; import {AlertService} from '../../alert/alert.service'; import {MatDialog, MatDialogRef} from '@angular/material'; import {ColumnPicker} from '../column-picker/column-picker.model'; import {RowLimiter} from '../row-limiter/row-limiter.model'; -import {AlertComponent} from '../../alert/alert.component'; import {SearchTableController} from './search-table-controller'; -import {finalize, map} from 'rxjs/operators'; +import {finalize} from 'rxjs/operators'; import {SearchTableEntity} from './search-table-entity.model'; import {SearchTableEntityStatus} from './search-table-entity-status.model'; import {CancelDialogComponent} from '../cancel-dialog/cancel-dialog.component'; import {SaveDialogComponent} from '../save-dialog/save-dialog.component'; import {DownloadService} from '../../download/download.service'; import {HttpClient, HttpParams} from '@angular/common/http'; +import {ConfirmationDialogComponent} from "../confirmation-dialog/confirmation-dialog.component"; +import {SecurityService} from "../../security/security.service"; @Component({ @@ -30,6 +31,7 @@ export class SearchTableComponent implements OnInit { @ViewChild('rowIndex') rowIndex: TemplateRef<any>; @Input() @ViewChild('additionalToolButtons') additionalToolButtons: TemplateRef<any>; + @Input() @ViewChild('additionalRowActionButtons') additionalRowActionButtons: TemplateRef<any>; @Input() @ViewChild('searchPanel') searchPanel: TemplateRef<any>; @Input() @ViewChild('tableRowDetailContainer') tableRowDetailContainer: TemplateRef<any>; @@ -42,6 +44,7 @@ export class SearchTableComponent implements OnInit { @Input() showActionButtons: boolean = true; @Input() showSearchPanel: boolean = true; @Input() showIndexColumn: boolean = false; + @Input() allowNewItems: boolean = false; loading = false; @@ -60,11 +63,13 @@ export class SearchTableComponent implements OnInit { offset: number = 0; orderBy: string = null; asc = false; + forceRefresh: boolean = false; constructor(protected http: HttpClient, protected alertService: AlertService, private downloadService: DownloadService, public dialog: MatDialog) { + } ngOnInit() { @@ -72,33 +77,33 @@ export class SearchTableComponent implements OnInit { cellTemplate: this.rowIndex, name: 'Index', width: 50, - maxWidth:80, + maxWidth: 80, sortable: false }; this.columnActions = { cellTemplate: this.rowActions, name: 'Actions', - width: 120, - maxWidth:150, + width: 250, + maxWidth: 250, sortable: false }; - this.columnExpandDetails= { + this.columnExpandDetails = { cellTemplate: this.rowExpand, name: ' ', width: 40, - maxWidth:50, + maxWidth: 50, sortable: false }; // Add actions to last column if (this.columnPicker) { // prepend columns - if (!!this.tableRowDetailContainer){ + if (!!this.tableRowDetailContainer) { this.columnPicker.allColumns.unshift(this.columnExpandDetails); this.columnPicker.selectedColumns.unshift(this.columnExpandDetails); } - if (this.showIndexColumn){ + if (this.showIndexColumn) { this.columnPicker.allColumns.unshift(this.columnIndex); this.columnPicker.selectedColumns.unshift(this.columnIndex); } @@ -108,7 +113,6 @@ export class SearchTableComponent implements OnInit { this.columnPicker.selectedColumns.push(this.columnActions); } } - this.page(this.offset, this.rowLimiter.pageSize, this.orderBy, this.asc); } getRowClass(row) { @@ -123,8 +127,8 @@ export class SearchTableComponent implements OnInit { getTableDataEntries$(offset: number, pageSize: number, orderBy: string, asc: boolean): Observable<SearchTableResult> { let params: HttpParams = new HttpParams() - .set('page', offset.toString()) - .set('pageSize', pageSize.toString()); + .set('page', offset.toString()) + .set('pageSize', pageSize.toString()); for (let filterProperty in this.filter) { @@ -133,9 +137,8 @@ export class SearchTableComponent implements OnInit { } } - // TODO move to the HTTP service this.loading = true; - return this.http.get<SearchTableResult>(this.url, { params }).pipe( + return this.http.get<SearchTableResult>(this.url, {params}).pipe( finalize(() => { this.loading = false; }) @@ -143,29 +146,48 @@ export class SearchTableComponent implements OnInit { } page(offset: number, pageSize: number, orderBy: string, asc: boolean) { - this.getTableDataEntries$(offset, pageSize, orderBy, asc).subscribe((result: SearchTableResult ) => { + if (this.safeRefresh) { + + this.dialog.open(ConfirmationDialogComponent, { + data: { + title: "Not persisted data!", + description: "Action will refresh all data and not saved data will be lost. Do you wish to continue?" + } + }).afterClosed().subscribe(result => { + if (result) { + this.pageInternal(offset, pageSize, orderBy, asc); + } + }) + } else { + this.pageInternal(offset, pageSize, orderBy, asc); + } + } + + private pageInternal(offset: number, pageSize: number, orderBy: string, asc: boolean) { + this.getTableDataEntries$(offset, pageSize, orderBy, asc).subscribe((result: SearchTableResult) => { this.offset = offset; this.rowLimiter.pageSize = pageSize; this.orderBy = orderBy; this.asc = asc; this.unselectRows(); + this.forceRefresh=false; this.count = result.count; // must be set else table can not calculate page numbers this.rows = result.serviceEntities.map(serviceEntity => { - return {...serviceEntity, + return { + ...serviceEntity, status: SearchTableEntityStatus.PERSISTED, - deleted: false} + deleted: false + } }); - - if(this.count > AlertComponent.MAX_COUNT_CSV) { - this.alertService.error("Maximum number of rows reached for downloading CSV"); - } }, (error: any) => { this.alertService.error("Error occurred:" + error); }); } onPage(event) { + + this.page(event.offset, event.pageSize, this.orderBy, this.asc); } @@ -176,8 +198,8 @@ export class SearchTableComponent implements OnInit { onSelect({selected}) { this.selected = [...selected]; - if(this.editButtonEnabled) { - this.rowNumber = this.selected[0]["$$index"]; + if (this.editButtonEnabled) { + this.rowNumber = this.rows.indexOf(this.selected[0]); } } @@ -202,14 +224,14 @@ export class SearchTableComponent implements OnInit { onNewButtonClicked() { const formRef: MatDialogRef<any> = this.searchTableController.newDialog({ - data: { edit: false } + data: {edit: false} }); formRef.afterClosed().subscribe(result => { if (result) { - this.rows = [...this.rows, {...formRef.componentInstance.current}]; + this.rows = [...this.rows, {...formRef.componentInstance.getCurrent()}]; //this.rows = this.rows.concat(formRef.componentInstance.current); this.count++; - // this.searchTable.refresh(); + // this.searchtable.refresh(); } else { this.unselectRows(); } @@ -243,13 +265,13 @@ export class SearchTableComponent implements OnInit { // this.isBusy = false; // this.getUsers(); this.alertService.success('The operation \'update\' completed successfully.', false); + this.forceRefresh = true; this.onRefresh(); + this.searchTableController.dataSaved(); if (withDownloadCSV) { this.downloadService.downloadNative(/*UserComponent.USER_CSV_URL TODO: use CSV url*/ ''); } }, err => { - // this.isBusy = false; - // this.getUsers(); this.alertService.exception('The operation \'update\' not completed successfully.', err, false); }); } else { @@ -294,6 +316,10 @@ export class SearchTableComponent implements OnInit { return dirty; } + get safeRefresh(): boolean { + return !(!this.submitButtonsEnabled || this.forceRefresh) ; + } + private editSearchTableEntity(rowNumber: number) { const row = this.rows[rowNumber]; const formRef: MatDialogRef<any> = this.searchTableController.newDialog({ @@ -304,14 +330,25 @@ export class SearchTableComponent implements OnInit { const status = row.status === SearchTableEntityStatus.PERSISTED ? SearchTableEntityStatus.UPDATED : row.status; - this.rows[rowNumber] = {...formRef.componentInstance.current, status}; + this.rows[rowNumber] = {...formRef.componentInstance.getCurrent(), status}; this.rows = [...this.rows]; } }); } + + public updateTableRow(rowNumber: number, row: any, status: SearchTableEntityStatus) { + this.rows[rowNumber] = {...row, status}; + this.rows = [...this.rows]; + } + + public getRowNumber(row: any) { + return this.rows.indexOf(row); + } + + private editSearchTableEntityRow(row: SearchTableEntity) { - let rowNumber = this.rows.indexOf(row); - this.editSearchTableEntity(rowNumber); + let rowNumber = this.rows.indexOf(row); + this.editSearchTableEntity(rowNumber); } private deleteSearchTableEntities(rows: Array<SearchTableEntity>) { @@ -325,6 +362,7 @@ export class SearchTableComponent implements OnInit { if (row.status === SearchTableEntityStatus.NEW) { this.rows.splice(this.rows.indexOf(row), 1); } else { + this.searchTableController.delete(row); row.status = SearchTableEntityStatus.REMOVED; row.deleted = true; } @@ -337,12 +375,16 @@ export class SearchTableComponent implements OnInit { this.selected = []; } - toggleExpandRow(selectedRow: any){ + toggleExpandRow(selectedRow: any) { //this.searchTableController.toggleExpandRow(selectedRow); this.searchTable.rowDetail.toggleExpandRow(selectedRow); } - onDetailToggle (event){ + onDetailToggle(event) { + + } + isDirty (): boolean { + return this.submitButtonsEnabled; } } diff --git a/smp-angular/src/app/common/spinner/spinner.component.ts b/smp-angular/src/app/common/spinner/spinner.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..a6664bf0f2817bdc06698dd5afb0f262a2da98ce --- /dev/null +++ b/smp-angular/src/app/common/spinner/spinner.component.ts @@ -0,0 +1,16 @@ +import { Component, Input } from '@angular/core'; + +@Component({ + selector: 'spinner', + template: + ` <div *ngIf="show"> + <span><i class="fa fa-spinner fa-spin" [ngStyle]="{'font-size': size+'px'}" aria-hidden="true"></i></span> + </div> + ` +}) +export class SpinnerComponent { + @Input() size: number = 25; + @Input() show: boolean; + + +} diff --git a/smp-angular/src/app/domain/domain-controller.ts b/smp-angular/src/app/domain/domain-controller.ts index 7cf4aea7c8cbe559f94cb8e066fec31ff438685d..6eb47da5f56ffc97ee327c5b99ee4335e9266512 100644 --- a/smp-angular/src/app/domain/domain-controller.ts +++ b/smp-angular/src/app/domain/domain-controller.ts @@ -3,14 +3,14 @@ import {MatDialog, MatDialogConfig, MatDialogRef} from '@angular/material'; import {DomainDetailsDialogComponent} from './domain-details-dialog/domain-details-dialog.component'; import {DomainRo} from './domain-ro.model'; import {SearchTableEntityStatus} from '../common/search-table/search-table-entity-status.model'; -import {UserDetailsDialogComponent} from "../user/user-details-dialog/user-details-dialog.component"; +import {GlobalLookups} from "../common/global-lookups"; export class DomainController implements SearchTableController { - constructor(public dialog: MatDialog) { + constructor(protected lookups: GlobalLookups, public dialog: MatDialog) { } - public showDetails(row: any) { + public showDetails( row: any) { let dialogRef: MatDialogRef<DomainDetailsDialogComponent> = this.dialog.open(DomainDetailsDialogComponent); dialogRef.afterClosed().subscribe(result => { //Todo: @@ -40,4 +40,7 @@ export class DomainController implements SearchTableController { status: SearchTableEntityStatus.NEW } } + public dataSaved() { + this.lookups.refreshDomainLookup(); + } } diff --git a/smp-angular/src/app/domain/domain-details-dialog/domain-details-dialog.component.html b/smp-angular/src/app/domain/domain-details-dialog/domain-details-dialog.component.html index 48af8018950d9733009ae0dd26ab7d7dd80e9658..89af53a721970599eba5c8631914ff389e838896 100644 --- a/smp-angular/src/app/domain/domain-details-dialog/domain-details-dialog.component.html +++ b/smp-angular/src/app/domain/domain-details-dialog/domain-details-dialog.component.html @@ -1,54 +1,73 @@ <h2 mat-dialog-title>{{formTitle}}</h2> <mat-dialog-content> - + <form [formGroup]="domainForm"> <mat-card> + <mat-card-title>Domain properties</mat-card-title> <mat-card-content> - <div class="panel"> <fieldset style="border: none;"> <mat-form-field style="width:100%"> - <input matInput placeholder="Domain Code ()" name="domainCode" value="{{current.domainCode}}" id="domainCode_id" - (blur)="updateDomainCode($event)" [formControl]="domainForm.controls['domainCode']" maxlength="255" required> + <input matInput placeholder="Domain Code (For WS API integration: the Domain property.)" name="domainCode" id="domainCode_id" + [formControl]="domainForm.controls['domainCode']" maxlength="255" required> <div *ngIf="(!editMode && domainForm.controls['domainCode'].touched || editMode) && domainForm.controls['domainCode'].hasError('pattern')" style="color:red; font-size: 70%"> - Domain could must contain only chars. + Domain code must contain only chars and numbers. + </div> + <div *ngIf="(!editMode && domainForm.controls['domainCode'].touched || editMode) && domainForm.controls['domainCode'].hasError('notInList')" style="color:red; font-size: 70%"> + The Domain code already exists! </div> </mat-form-field> <mat-form-field style="width:100%"> - <input matInput placeholder="SML domain (Part of DNS Zone: ex.eHealth: 'ehealth', peppol: '', ..)" name="smlSubdomain" value="{{current.smlSubdomain}}" id="smldomain_id" - (blur)="updateSmlDomain($event)" [formControl]="domainForm.controls['smlSubdomain']" maxlength="255" required> + <input matInput placeholder="SML domain (Part of DNS Zone: ex.eHealth: 'ehealth', peppol: '', ..)" name="smlSubdomain" id="smldomain_id" + [formControl]="domainForm.controls['smlSubdomain']" maxlength="255" > <div *ngIf="(!editMode && domainForm.controls['smlSubdomain'].touched || editMode) && domainForm.controls['smlSubdomain'].hasError('pattern')" style="color:red; font-size: 70%"> - SML domain must be valid DNS part (AlphaNumeric with optional char -). + SML domain must be valid DNS part (AlphaNumeric with optional char - or must be empty for peppol). </div> - </mat-form-field> - <mat-form-field style="width:100%"> - <input matInput placeholder="SML SMP identifier (SMP ID used for SML )" name="smlSmpId" value="{{current.smlSmpId}}" id="smlSMPId_id" - (blur)="updateSmlSmpId($event)" [formControl]="domainForm.controls['smlSmpId']" maxlength="255" required> - <div *ngIf="(!editMode && domainForm.controls['smlSmpId'].touched || editMode) && domainForm.controls['smlSmpId'].hasError('pattern')" style="color:red; font-size: 70%"> - SML SMP ID must be valid DNS part (AlphaNumeric with optional char -). + <div *ngIf="(!editMode && domainForm.controls['smlSubdomain'].touched || editMode) && domainForm.controls['smlSubdomain'].hasError('notInList')" style="color:red; font-size: 70%"> + The SML subdomain is already defined! </div> - - </mat-form-field> - <mat-form-field style="width:100%"> - <input matInput placeholder="SML Client certificate" name="Client certificate" value="{{current.smlClientKeyAlias}}" id="smlClientKeyAlias_id" - (blur)="updateSmlClientKeyAlias($event)" [formControl]="domainForm.controls['smlClientKeyAlias']" maxlength="255" required> </mat-form-field> <mat-form-field style="width:100%"> - <input matInput placeholder="Response signature Certificate" name="signatureKeyAlias" value="{{current.signatureKeyAlias}}" id="signatureKeyAlias_id" - (blur)="updateSignatureKeyAlias($event)" [formControl]="domainForm.controls['signatureKeyAlias']" maxlength="255" required> + <input matInput placeholder="Response signature Certificate" name="signatureKeyAlias" id="signatureKeyAlias_id" + [formControl]="domainForm.controls['signatureKeyAlias']" maxlength="255" > </mat-form-field> </fieldset> - <!-- label class="custom-file-upload"> - <input #fileInput type="file" id="custom-file-upload" accept=".cer" (change)="uploadCertificate()" > - <span class="custom-file-upload-inner">Import</span> - </label--> - </div> </mat-card-content> </mat-card> + <mat-card> + <mat-card-title>SML integration data</mat-card-title> + <mat-card-content> + <fieldset style="border: none;"> + <mat-form-field style="width:100%"> + <input matInput placeholder="SML SMP identifier (SMP ID used for SML )" name="smlSmpId" id="smlSMPId_id" + [formControl]="domainForm.controls['smlSmpId']" maxlength="255" required> + <div *ngIf="(!editMode && domainForm.controls['smlSmpId'].touched || editMode) && domainForm.controls['smlSmpId'].hasError('pattern')" style="color:red; font-size: 70%"> + SML SMP ID must be valid DNS part (AlphaNumeric with optional char -)! + </div> + <div *ngIf="(!editMode && domainForm.controls['smlSmpId'].touched || editMode) && domainForm.controls['smlSmpId'].hasError('notInList')" style="color:red; font-size: 70%"> + SML SMP ID already exists! + </div> + </mat-form-field> + <mat-form-field style="width:100%"> + <input matInput placeholder="ClientCert Header" name="Client certificate" id="smlClientHeader_id" + [formControl]="domainForm.controls['smlClientCertHeader']" maxlength="255" > + </mat-form-field> + <mat-form-field style="width:100%"> + <input matInput placeholder="ClientCert Alias" name="Client certificate" id="smlClientKeyAlias_id" + [formControl]="domainForm.controls['smlClientKeyAlias']" maxlength="255" > + </mat-form-field> + </fieldset> + <!-- label class="custom-file-upload"> + <input #fileInput type="file" id="custom-file-upload" accept=".cer" (change)="uploadCertificate()" > + <span class="custom-file-upload-inner">Import</span> + </label--> + </mat-card-content> + </mat-card> + </form> </mat-dialog-content> <table class="buttonsRow"> <tr> <td> - <button mat-raised-button color="primary" [mat-dialog-close]="true" (click)="submitForm()" [disabled]="!domainForm.valid"> + <button mat-raised-button color="primary" (click)="submitForm()" [disabled]="!domainForm.valid"> <mat-icon>check_circle</mat-icon> <span>OK</span> </button> diff --git a/smp-angular/src/app/domain/domain-details-dialog/domain-details-dialog.component.ts b/smp-angular/src/app/domain/domain-details-dialog/domain-details-dialog.component.ts index fc8e1e4c12e22de4d96b0fe8271f4e3ba37a1f68..611a900677398b590108fa3d8d182c41175e9b97 100644 --- a/smp-angular/src/app/domain/domain-details-dialog/domain-details-dialog.component.ts +++ b/smp-angular/src/app/domain/domain-details-dialog/domain-details-dialog.component.ts @@ -1,13 +1,10 @@ import {Component, Inject} from '@angular/core'; import {MAT_DIALOG_DATA, MatDialogRef} from '@angular/material'; -import {UserRo} from "../../user/user-ro.model"; -import {FormBuilder, FormControl, FormGroup, Validators} from "@angular/forms"; +import {AbstractControl, FormBuilder, FormControl, FormGroup, Validators} from "@angular/forms"; import {DomainRo} from "../domain-ro.model"; import {AlertService} from "../../alert/alert.service"; -import {UserDetailsDialogComponent} from "../../user/user-details-dialog/user-details-dialog.component"; -import {CertificateService} from "../../user/certificate.service"; -import {UserService} from "../../user/user.service"; import {SearchTableEntityStatus} from "../../common/search-table/search-table-entity-status.model"; +import {GlobalLookups} from "../../common/global-lookups"; @Component({ selector: 'domain-details-dialog', @@ -17,25 +14,32 @@ export class DomainDetailsDialogComponent { static readonly NEW_MODE = 'New Domain'; static readonly EDIT_MODE = 'Domain Edit'; - readonly dnsDomainPattern = '^(?!(\\d|-|_).+)[a-zA-Z0-9-]{1,63}$'; - readonly domainCodePattern = '^[a-zA-Z]{1,255}$'; + readonly dnsDomainPattern = '^(?![0-9]+$)(?!.*-$)(?!-)[a-zA-Z0-9-]{0,63}$'; + readonly domainCodePattern = '^[a-zA-Z0-9]{1,255}$'; editMode: boolean; formTitle: string; current: DomainRo & { confirmation?: string }; domainForm: FormGroup; - - domain; + notInList(list: string[], exception: string) { + return (c: AbstractControl): { [key: string]: any } => { + if (c.value && c.value !== exception && list.includes(c.value)) + return {'notInList': {valid: false}}; + + return null; + } + } - constructor(private dialogRef: MatDialogRef<DomainDetailsDialogComponent>, + constructor(private lookups: GlobalLookups, + private dialogRef: MatDialogRef<DomainDetailsDialogComponent>, private alertService: AlertService, @Inject(MAT_DIALOG_DATA) public data: any, private fb: FormBuilder) { this.editMode = data.edit; - this.formTitle = this.editMode ? DomainDetailsDialogComponent.EDIT_MODE: DomainDetailsDialogComponent.NEW_MODE; + this.formTitle = this.editMode ? DomainDetailsDialogComponent.EDIT_MODE : DomainDetailsDialogComponent.NEW_MODE; this.current = this.editMode ? { ...data.row, @@ -51,20 +55,33 @@ export class DomainDetailsDialogComponent { this.domainForm = fb.group({ - 'domainCode': new FormControl({value: this.current.domainCode, disabled: this.editMode}, [Validators.pattern(this.domainCodePattern)]), - 'smlSubdomain': new FormControl({value: this.current.smlSubdomain, disabled: this.editMode}, [Validators.pattern(this.dnsDomainPattern)]), - 'smlSmpId': new FormControl({value: this.current.smlSmpId}, [Validators.required, Validators.pattern(this.dnsDomainPattern)]), - 'smlClientKeyAlias': new FormControl({value: this.current.smlClientKeyAlias}, null), - 'signatureKeyAlias': new FormControl({value: this.current.signatureKeyAlias}, null), + 'domainCode': new FormControl({value: '', disabled: this.editMode}, [Validators.pattern(this.domainCodePattern), + this.notInList(this.lookups.cachedDomainList.map(a => a.domainCode), this.current.domainCode)]), + 'smlSubdomain': new FormControl({ + value: '', + disabled: this.editMode + }, [Validators.pattern(this.dnsDomainPattern), + this.notInList(this.lookups.cachedDomainList.map(a => a.smlSubdomain), this.current.smlSubdomain)]), + 'smlSmpId': new FormControl({value: ''}, [Validators.pattern(this.dnsDomainPattern), + this.notInList(this.lookups.cachedDomainList.map(a => a.smlSmpId), this.current.smlSmpId)]), + 'smlClientCertHeader': new FormControl({value: ''}, null), + 'smlClientKeyAlias': new FormControl({value: ''}, null), + 'signatureKeyAlias': new FormControl({value: ''}, null), - - }, { - //validator: this.passwordConfirmationValidator }); + this.domainForm.controls['domainCode'].setValue(this.current.domainCode); + this.domainForm.controls['smlSubdomain'].setValue(this.current.smlSubdomain); + this.domainForm.controls['smlSmpId'].setValue(this.current.smlSmpId); + this.domainForm.controls['smlClientCertHeader'].setValue(this.current.smlClientCertHeader); + this.domainForm.controls['smlClientKeyAlias'].setValue(this.current.smlClientKeyAlias); + this.domainForm.controls['signatureKeyAlias'].setValue(this.current.signatureKeyAlias); + + } + submitForm() { this.checkValidity(this.domainForm) - this.dialogRef.close(true); + this.dialogRef.close(true); } checkValidity(g: FormGroup) { @@ -80,13 +97,29 @@ export class DomainDetailsDialogComponent { }); } + public getCurrent(): DomainRo { + + if (!this.editMode) { + this.current.domainCode = this.domainForm.value['domainCode']; + this.current.smlSubdomain = this.domainForm.value['smlSubdomain']; + } + this.current.smlSmpId = this.domainForm.value['smlSmpId']; + this.current.smlClientCertHeader = this.domainForm.value['smlClientCertHeader']; + this.current.smlClientKeyAlias = this.domainForm.value['smlClientKeyAlias']; + this.current.signatureKeyAlias = this.domainForm.value['signatureKeyAlias']; + + return this.current; + + } updateDomainCode(event) { this.current.domainCode = event.target.value; } + updateSmlDomain(event) { this.current.smlSubdomain = event.target.value; } + updateSmlSmpId(event) { this.current.smlSmpId = event.target.value; } diff --git a/smp-angular/src/app/domain/domain.component.html b/smp-angular/src/app/domain/domain.component.html index 6dd642be9cca558c1b08a2326210e490ff17de63..0ee884f9dd034394be8a6e649391c2d80a07348d 100644 --- a/smp-angular/src/app/domain/domain.component.html +++ b/smp-angular/src/app/domain/domain.component.html @@ -1,5 +1,6 @@ <smp-search-table + #searchTable page_id= 'domain_id' title= 'Domains' [columnPicker] = "columnPicker" @@ -8,6 +9,7 @@ [searchTableController]="domainController" [showSearchPanel]="false" [filter]="filter" + [allowNewItems]="securityService.isCurrentUserSystemAdmin()" > <ng-template #additionalToolButtons > diff --git a/smp-angular/src/app/domain/domain.component.ts b/smp-angular/src/app/domain/domain.component.ts index 73e617c23fcedfd0c9356c4d0a4c4cd73c3e4920..90125dc5b822aab895fa94099cd6a8edd9ed67e2 100644 --- a/smp-angular/src/app/domain/domain.component.ts +++ b/smp-angular/src/app/domain/domain.component.ts @@ -1,11 +1,14 @@ import {Component, OnInit, TemplateRef, ViewChild} from '@angular/core'; import {ColumnPicker} from '../common/column-picker/column-picker.model'; -import {MatDialog, MatDialogRef} from '@angular/material'; +import {MatDialog} from '@angular/material'; import {AlertService} from '../alert/alert.service'; import {DomainController} from './domain-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"; @Component({ moduleId: module.id, @@ -17,6 +20,7 @@ export class DomainComponent implements OnInit { @ViewChild('rowMetadataAction') rowMetadataAction: TemplateRef<any>; @ViewChild('rowExtensionAction') rowExtensionAction: TemplateRef<any>; @ViewChild('rowActions') rowActions: TemplateRef<any>; + @ViewChild('searchTable') searchTable: SearchTableComponent; baseUrl = SmpConstants.REST_DOMAIN; columnPicker: ColumnPicker = new ColumnPicker(); @@ -24,11 +28,15 @@ export class DomainComponent implements OnInit { filter: any = {}; - constructor(protected http: HttpClient, protected alertService: AlertService, public dialog: MatDialog) { + constructor(public securityService: SecurityService, + protected lookups: GlobalLookups, + protected http: HttpClient, + protected alertService: AlertService, + public dialog: MatDialog) { } ngOnInit() { - this.domainController = new DomainController(this.dialog); + this.domainController = new DomainController(this.lookups, this.dialog); this.columnPicker.allColumns = [ { @@ -71,4 +79,9 @@ export class DomainComponent implements OnInit { details(row: any) { this.domainController.showDetails(row); } + + // for dirty guard... + isDirty (): boolean { + return this.searchTable.isDirty(); + } } diff --git a/smp-angular/src/app/footer/footer.component.ts b/smp-angular/src/app/footer/footer.component.ts index dbb997f81ae9ff9706f5e19b5395c5694b6406ba..3bc2dad7c7d0805508e27dfd4514a5ab312f7ae4 100644 --- a/smp-angular/src/app/footer/footer.component.ts +++ b/smp-angular/src/app/footer/footer.component.ts @@ -16,6 +16,7 @@ export class FooterComponent implements OnInit { } ngOnInit(): void { + console.log("FooterComponent onInit"); this.smpInfoService.getSmpInfo().subscribe((smpInfo: SmpInfo) => { this.smpVersion = smpInfo.version; diff --git a/smp-angular/src/app/guards/authenticated.guard.ts b/smp-angular/src/app/guards/authenticated.guard.ts index b5463d3ba19393d0fbd86c9661314cb325927bd1..ddb84a79ae5e649ef6b41bf4c328720aa5d7d2ba 100644 --- a/smp-angular/src/app/guards/authenticated.guard.ts +++ b/smp-angular/src/app/guards/authenticated.guard.ts @@ -10,6 +10,7 @@ export class AuthenticatedGuard implements CanActivate { } canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) { + const subject = new ReplaySubject<boolean>(); this.securityService.isAuthenticated(true).subscribe((isAuthenticated: boolean) => { if(isAuthenticated) { @@ -20,6 +21,7 @@ export class AuthenticatedGuard implements CanActivate { subject.next(false); } }); - return subject.asObservable() + return subject.asObservable(); + } } diff --git a/smp-angular/src/app/guards/authorized-admin.guard.ts b/smp-angular/src/app/guards/authorized-admin.guard.ts index 381154b14f5b1e65e7f00f9dbe2b026608513ca9..1c8ddfc5bf8d3c54a7c35db18f4bcccf9f5e3556 100644 --- a/smp-angular/src/app/guards/authorized-admin.guard.ts +++ b/smp-angular/src/app/guards/authorized-admin.guard.ts @@ -2,7 +2,7 @@ import {ActivatedRouteSnapshot, RouterStateSnapshot} from '@angular/router'; import {SecurityService} from '../security/security.service'; import {AuthorizedGuard} from './authorized.guard'; -import {Role} from '../security/role.model'; +import {Authority} from '../security/authority.model'; @Injectable() export class AuthorizedAdminGuard extends AuthorizedGuard { @@ -16,8 +16,8 @@ export class AuthorizedAdminGuard extends AuthorizedGuard { } - getAllowedRoles(route: ActivatedRouteSnapshot): Array<Role> { + getAllowedRoles(route: ActivatedRouteSnapshot): Array<Authority> { // TODO check if we need the SMP admin in here - return [Role.SYSTEM_ADMIN/*, Role.SMP_ADMIN*/]; + return [Authority.SYSTEM_ADMIN , Authority.SMP_ADMIN]; } } diff --git a/smp-angular/src/app/guards/authorized.guard.ts b/smp-angular/src/app/guards/authorized.guard.ts index b85ec03ae74f7ff09b5ee9f2c5c8e82afa556678..57b320316f2272f9322d8d02c1a22efa5ad53bee 100644 --- a/smp-angular/src/app/guards/authorized.guard.ts +++ b/smp-angular/src/app/guards/authorized.guard.ts @@ -2,7 +2,7 @@ import {CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot} from '@angular/router'; import {SecurityService} from '../security/security.service'; import {ReplaySubject} from 'rxjs'; -import {Role} from '../security/role.model'; +import {Authority} from "../security/authority.model"; @Injectable() export class AuthorizedGuard implements CanActivate { @@ -11,6 +11,7 @@ export class AuthorizedGuard implements CanActivate { } canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) { + console.debug("AuthorizedGuard"); let allowedRoles = this.getAllowedRoles(route); let subject = new ReplaySubject<boolean>(); @@ -24,6 +25,6 @@ export class AuthorizedGuard implements CanActivate { } getAllowedRoles(route: ActivatedRouteSnapshot) { - return route.data["allowedRoles"] as Array<Role>; + return route.data["allowedRoles"] as Array<Authority>; } } diff --git a/smp-angular/src/app/login/login.component.ts b/smp-angular/src/app/login/login.component.ts index 91ca64a6a7216a0ad8615c9deed6c76587582700..8115e16b8aa92f3541f1f55c9275501fef68e638 100644 --- a/smp-angular/src/app/login/login.component.ts +++ b/smp-angular/src/app/login/login.component.ts @@ -7,6 +7,7 @@ import {SecurityEventService} from '../security/security-event.service'; import {User} from '../security/user.model'; import {MatDialogRef, MatDialog} from '@angular/material'; import {DefaultPasswordDialogComponent} from 'app/security/default-password-dialog/default-password-dialog.component'; +import {Subscription} from "rxjs"; @Component({ moduleId: module.id, @@ -17,7 +18,7 @@ export class LoginComponent implements OnInit, OnDestroy { model: any = {}; loading = false; returnUrl: string; - sub: any; + sub: Subscription; constructor(private route: ActivatedRoute, private router: Router, @@ -40,7 +41,7 @@ export class LoginComponent implements OnInit, OnDestroy { this.sub = this.securityEventService.onLoginSuccessEvent().subscribe( data => { console.log("Authentication successfull"); - this.verifyDefaultLoginUsed(); + //this.verifyDefaultLoginUsed(); this.router.navigate([this.returnUrl]); }); @@ -57,7 +58,7 @@ export class LoginComponent implements OnInit, OnDestroy { switch (error.status) { case HTTP_UNAUTHORIZED: case HTTP_FORBIDDEN: - let forbiddenCode = error.json().message; + let forbiddenCode = error.message; console.log("User forbiden code " + forbiddenCode); switch (forbiddenCode) { case USER_INACTIVE: diff --git a/smp-angular/src/app/security/authority.model.ts b/smp-angular/src/app/security/authority.model.ts new file mode 100644 index 0000000000000000000000000000000000000000..96ca6b332c30b8832349cf42f6170ae33e60efa1 --- /dev/null +++ b/smp-angular/src/app/security/authority.model.ts @@ -0,0 +1,19 @@ +/** + * Note the difference between authority and Role. User with one role could have multiple authorities. + * At the moment role and authorites matches - but this can change in the future. + */ + +export enum Authority { + /** + * The system administrator (a.k.a. the "super admin") role + */ + SYSTEM_ADMIN = 'ROLE_SYSTEM_ADMIN', + /** + * The SMP Administrator role. It is assimilable to the {@link SERVICE_GROUP_ADMINISTRATOR} role for now. + */ + SMP_ADMIN = 'ROLE_SMP_ADMIN', + /** + * The ServiceGroup administrator role + */ + SERVICE_GROUP_ADMIN = 'ROLE_SERVICE_GROUP_ADMIN', +} diff --git a/smp-angular/src/app/security/is-authorized.directive.ts b/smp-angular/src/app/security/is-authorized.directive.ts index d40f816debf9c77257abf17761d1bee272b3b0b4..7669e8929f37ef5593349ae97117f7a325d261a5 100644 --- a/smp-angular/src/app/security/is-authorized.directive.ts +++ b/smp-angular/src/app/security/is-authorized.directive.ts @@ -1,12 +1,12 @@ import {Directive, ElementRef, Input, OnInit} from '@angular/core'; import {SecurityService} from './security.service'; -import {Role} from './role.model'; +import {Authority} from "./authority.model"; @Directive({ selector:'[isAuthorized]' }) export class IsAuthorized implements OnInit { - @Input('isAuthorized') role: Role; + @Input('isAuthorized') role: Authority; constructor(private _elementRef:ElementRef, private securityService:SecurityService) { } diff --git a/smp-angular/src/app/security/role.model.ts b/smp-angular/src/app/security/role.model.ts index 0d959fbdce84c1bb646ba095944dbb39ceeacf90..135f827c60b1bbe55856e3ff90547e5d88998b7c 100644 --- a/smp-angular/src/app/security/role.model.ts +++ b/smp-angular/src/app/security/role.model.ts @@ -2,13 +2,13 @@ export enum Role { /** * The system administrator (a.k.a. the "super admin") role */ - SYSTEM_ADMIN = 'System Administrator', + SYSTEM_ADMIN = 'SYSTEM_ADMIN', /** - * The SMP Administrator role. It is assimilable to the {@link SERVICE_GROUP_ADMIN} role for now. + * The SMP Administrator role. It is assimilable to the {@link SERVICE_GROUP_ADMINISTRATOR} role for now. */ - SMP_ADMIN = 'SMP Administrator', + SMP_ADMIN = 'SMP_ADMIN', /** * The ServiceGroup administrator role */ - SERVICE_GROUP_ADMIN = 'ServiceGroup Administrator', + SERVICE_GROUP_ADMIN = 'SERVICE_GROUP_ADMIN', } diff --git a/smp-angular/src/app/security/security.service.ts b/smp-angular/src/app/security/security.service.ts index ffa63b454c69ed59a271e41d0137732da7f69848..d1445baf7f26a40edf9b5db316b3f3ca409b9c7c 100644 --- a/smp-angular/src/app/security/security.service.ts +++ b/smp-angular/src/app/security/security.service.ts @@ -2,20 +2,20 @@ import {Observable, ReplaySubject} from 'rxjs'; import {User} from './user.model'; import {SecurityEventService} from './security-event.service'; -import {DomainService} from './domain.service'; -import {Role} from './role.model'; import {HttpClient, HttpHeaders} from '@angular/common/http'; +import {SmpConstants} from "../smp.constants"; +import {Authority} from "./authority.model"; @Injectable() export class SecurityService { - constructor (private http: HttpClient, private securityEventService: SecurityEventService, private domainService: DomainService) { + constructor (private http: HttpClient, private securityEventService: SecurityEventService) { } login(username: string, password: string) { - this.domainService.resetDomain(); + let headers: HttpHeaders = new HttpHeaders({'Content-Type': 'application/json'}); - return this.http.post<string>('rest/security/authentication', + return this.http.post<string>(SmpConstants.REST_SECURITY_AUTHENTICATION, JSON.stringify({ username: username, password: password @@ -23,7 +23,7 @@ export class SecurityService { { headers }) .subscribe((response: string) => { console.log('Login success'); - localStorage.setItem('currentUser', response); + localStorage.setItem('currentUser', JSON.stringify(response)); this.securityEventService.notifyLoginSuccessEvent(response); }, (error: any) => { @@ -34,8 +34,8 @@ export class SecurityService { logout() { console.log('Logging out'); - this.domainService.resetDomain(); - this.http.delete('rest/security/authentication').subscribe((res: Response) => { + // this.domainService.resetDomain(); + this.http.delete(SmpConstants.REST_SECURITY_AUTHENTICATION).subscribe((res: Response) => { localStorage.removeItem('currentUser'); this.securityEventService.notifyLogoutSuccessEvent(res); }, @@ -46,12 +46,13 @@ export class SecurityService { } getCurrentUser(): User { + let userObj = localStorage.getItem('currentUser'); return JSON.parse(localStorage.getItem('currentUser')); } private getCurrentUsernameFromServer(): Observable<string> { let subject = new ReplaySubject<string>(); - this.http.get<string>('rest/security/user') + this.http.get<string>(SmpConstants.REST_SECURITY_USER) .subscribe((res: string) => { subject.next(res); }, (error: any) => { @@ -81,19 +82,22 @@ export class SecurityService { return subject.asObservable(); } - isCurrentUserSuperAdmin(): boolean { - return this.isCurrentUserInRole([Role.SYSTEM_ADMIN]); + isCurrentUserSystemAdmin(): boolean { + return this.isCurrentUserInRole([Authority.SYSTEM_ADMIN]); } - isCurrentUserAdmin(): boolean { - return this.isCurrentUserInRole([Role.SYSTEM_ADMIN, Role.SMP_ADMIN]); + isCurrentUserSMPAdmin(): boolean { + return this.isCurrentUserInRole([ Authority.SMP_ADMIN]); + } + isCurrentUserServiceGroupAdmin(): boolean { + return this.isCurrentUserInRole([ Authority.SERVICE_GROUP_ADMIN]); } - isCurrentUserInRole(roles: Array<Role>): boolean { + isCurrentUserInRole(roles: Array<Authority>): boolean { let hasRole = false; const currentUser = this.getCurrentUser(); if (currentUser && currentUser.authorities) { - roles.forEach((role: Role) => { + roles.forEach((role: Authority) => { if (currentUser.authorities.indexOf(role) !== -1) { hasRole = true; } @@ -102,7 +106,7 @@ export class SecurityService { return hasRole; } - isAuthorized(roles: Array<Role>): Observable<boolean> { + isAuthorized(roles: Array<Authority>): Observable<boolean> { let subject = new ReplaySubject<boolean>(); this.isAuthenticated(false).subscribe((isAuthenticated: boolean) => { diff --git a/smp-angular/src/app/security/user.model.ts b/smp-angular/src/app/security/user.model.ts index 085924d912eb884f692ec5fcabb2dd23775723d9..ad02b6ce78a2a8bf254180a0f11137336233e911 100644 --- a/smp-angular/src/app/security/user.model.ts +++ b/smp-angular/src/app/security/user.model.ts @@ -1,8 +1,8 @@ -import {Role} from './role.model'; +import {Authority} from "./authority.model"; export interface User { id: number; username: string; - authorities: Array<Role>; + authorities: Array<Authority>; defaultPasswordUsed: boolean; } diff --git a/smp-angular/src/app/service-group-edit/service-group-details-dialog/service-extension-edit-ro.model.ts b/smp-angular/src/app/service-group-edit/service-group-details-dialog/service-extension-edit-ro.model.ts new file mode 100644 index 0000000000000000000000000000000000000000..9c2d25b7df2a6da788c61d96af66e472d89e6e0a --- /dev/null +++ b/smp-angular/src/app/service-group-edit/service-group-details-dialog/service-extension-edit-ro.model.ts @@ -0,0 +1,6 @@ + +export interface ServiceGroupExtensionRo { + serviceGroupId: number; + extension: string; + errorMessage?: string; +} diff --git a/smp-angular/src/app/service-group-edit/service-group-details-dialog/service-group-details-dialog.component.html b/smp-angular/src/app/service-group-edit/service-group-details-dialog/service-group-details-dialog.component.html index d29e25dc3a8d47cb4659606b70451d800c9cc69b..a81f3ac6170ae4e2a78620aa078ad3377ae1c301 100644 --- a/smp-angular/src/app/service-group-edit/service-group-details-dialog/service-group-details-dialog.component.html +++ b/smp-angular/src/app/service-group-edit/service-group-details-dialog/service-group-details-dialog.component.html @@ -1,15 +1,16 @@ <h2 mat-dialog-title>{{formTitle}}</h2> +<spinner [show]="showSpinner" [size]="150"> </spinner> +<mat-dialog-content style="height:600px;width:1200px"> -<mat-dialog-content style="height:600px;width:1100px"> <div fxLayout="row"> <div fxLayout="column"> <mat-card fxFlex="200px"> - <mat-card-title>Identifier</mat-card-title> - <mat-card-content> + <!-- mat-card-title>Identifier</mat-card-title --> + <mat-card-content style="height: 100px"> + <div class="panel"> <mat-form-field style="width:100%"> <input matInput placeholder="Participant identifier" name="participantIdentifier" - value="{{current.participantIdentifier}}" id="participantIdentifier_id" - (blur)="updateParticipantIdentifier($event)" + id="participantIdentifier_id" [formControl]="dialogForm.controls['participantIdentifier']" maxlength="255" required> <div *ngIf="(!editMode && dialogForm.controls['participantIdentifier'].touched || editMode) && dialogForm.controls['participantIdentifier'].hasError('required')" @@ -20,8 +21,8 @@ <mat-form-field style="width:100%"> <input matInput placeholder="Participant scheme" name="participantScheme" - value="{{current.participantScheme}}" id="participantScheme_id" - (blur)="updateParticipantScheme($event)" [formControl]="dialogForm.controls['participantScheme']" + id="participantScheme_id" + [formControl]="dialogForm.controls['participantScheme']" maxlength="255" required> <div *ngIf="(!editMode && dialogForm.controls['participantScheme'].touched || editMode) && dialogForm.controls['participantScheme'].hasError('required')" @@ -29,38 +30,141 @@ Participant scheme must not be empty. </div> </mat-form-field> + </div> </mat-card-content> </mat-card> <mat-card> - <mat-card-title>Owners</mat-card-title> <mat-card-content> - <p>Selected user count: {{usersSelected.selectedOptions.selected.length}}</p> - <mat-selection-list #usersSelected [compareWith]="compareUserById" - (selectionChange)="userListChanged(usersSelected.selectedOptions.selected,$event)" - [formControl]="formControlUsers" - style="height: 200px; overflow-y: scroll; overflow-x: auto;"> - <mat-list-option *ngFor="let user of userlist" [value]='user'> - {{user.id}} - {{user.username?user.username:user.id}} - </mat-list-option> - </mat-selection-list> - <div - *ngIf="(!editMode && dialogForm.controls['users'].touched || editMode) && dialogForm.controls['users'].hasError('minCountOwners')" - style="color:red; font-size: 70%"> - At least one user (owner) must be selected! - </div> + <mat-accordion> + <mat-expansion-panel [expanded]="true"> + <mat-expansion-panel-header> + <mat-panel-title>Owners* + </mat-panel-title> + <mat-panel-description> + <div> + Selected user count: {{usersSelected.selectedOptions?.selected.length}} + <div + *ngIf="(!editMode && dialogForm.controls['users'].touched || editMode) && dialogForm.controls['users'].hasError('minSelectedListCount')" + style="color:red; font-size: 70%"> + At least one user (owner) must be selected! + </div> + </div> + </mat-panel-description> + </mat-expansion-panel-header> + <mat-selection-list #usersSelected + [disabled]="!securityService.isCurrentUserSMPAdmin()" + [compareWith]="compareTableItemById" + [formControl]="dialogForm.controls['users']" + style="height: 200px; overflow-y: scroll; overflow-x: auto;"> + <mat-list-option *ngFor="let user of lookups?.cachedUserList" [value]='user'> + {{user.id}} - {{user.username?user.username:user.id}} + </mat-list-option> + </mat-selection-list> + </mat-expansion-panel> + + <mat-expansion-panel > + <mat-expansion-panel-header> + <mat-panel-title>Domains*</mat-panel-title> + <mat-panel-description> + <div> + Selected domain count: {{domainSelector.selectedOptions?.selected.length}} + <div + *ngIf="(!editMode && dialogForm.controls['serviceGroupDomains'].touched || editMode) && dialogForm.controls['serviceGroupDomains'].hasError('minSelectedListCount')" + style="color:red; font-size: 70%"> + At least one domain must be selected! + </div> + </div> + </mat-panel-description> + </mat-expansion-panel-header> + <mat-selection-list #domainSelector + [disabled]="!securityService.isCurrentUserSMPAdmin()" + [compareWith]="compareDomain" + [formControl]="dialogForm.controls['serviceGroupDomains']" + (selectionChange)="onDomainSelectionChanged($event)" + style="height: 200px; overflow-y: scroll; overflow-x: auto;"> + <mat-list-option *ngFor="let domain of lookups.cachedDomainList" [value]='domain'> + {{domain.domainCode}} ({{domain.smlSubdomain}}) + </mat-list-option> + </mat-selection-list> + </mat-expansion-panel> + </mat-accordion> </mat-card-content> </mat-card> </div> <mat-card fxFlex="60"> - <mat-card-title>Extension</mat-card-title> - <mat-card-content> - <p> + <mat-card-title>Extension + <div style="font-size:70%"> Extension is automatically wrapped to root element to form vaild XML! No ExtensionWrapper element is needed. - </p> - <mat-form-field> - <textarea id="extensionTextArea" resizeable="false" placeholder="Extension" name="extension" ></textarea> - </mat-form-field> + </div> + </mat-card-title> + + <mat-card-content > + <mat-toolbar> + <mat-toolbar-row> + <button mat-raised-button color="primary" + matTooltip="Clear the extension content." + matTooltipPosition="below" + (click)="onExtensionDelete()" > + <mat-icon>clear</mat-icon> + <span>Clear</span> + </button> + <button mat-raised-button color="primary" + matTooltip="Opens wizard for adding new Extension. New extension is appended to existing extensions." + matTooltipPosition="below" + (click)="onStartWizardDialog()"> + <mat-icon>add_box</mat-icon> + <span>Extension wizard</span> + </button> + <button mat-raised-button color="primary" + atTooltip="Validate extension by XSD schema." + matTooltipPosition="below" + (click)="onExtensionValidate()"> + <mat-icon>warning</mat-icon> + <span>Validate</span> + </button> + <!-- add and test this fuction on backend + button mat-raised-button color="primary" + atTooltip="Pritty print extension!" + matTooltipPosition="below" + (click)="onPrettyPrintExtension()"> + <mat-icon>warning</mat-icon> + <span>format XML</span> + </button --> + </mat-toolbar-row> + </mat-toolbar> + + <div style="display: block;" style="border:1px; solid: #999999;margin:5px 0; padding:3px;"> + <div class="textwrapper"> + <textarea matInput style="width:100%;border: #03A9F4 1px solid" cols="2" rows="30" + resizeable="false" + id="extensionTextArea" + placeholder="Extension" name="extension" + [formControl]="dialogForm.controls['extension']" + ></textarea> + </div> + <div + *ngIf="extensionValidationMessage" + [style.color]="isExtensionValid?'green':'red'"> + {{extensionValidationMessage}} + </div> + + </div> + + + + <!-- mat-form-field> + <div> + <textarea style="width:100% !important;height: 400px !important; -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box;" + + matInput id="extensionTextArea" + placeholder="Extension" name="extension" + [formControl]="dialogForm.controls['extension']" + ></textarea> + </div> + </mat-form-field --> </mat-card-content> </mat-card> </div> @@ -68,7 +172,7 @@ <mat-dialog-actions> <div class="group-action-button"> - <button mat-raised-button color="primary" [mat-dialog-close]="true" (click)="submitForm()" + <button mat-raised-button color="primary" (click)="submitForm()" [disabled]="!dialogForm.valid"> <mat-icon>check_circle</mat-icon> <span>OK</span> diff --git a/smp-angular/src/app/service-group-edit/service-group-details-dialog/service-group-details-dialog.component.ts b/smp-angular/src/app/service-group-edit/service-group-details-dialog/service-group-details-dialog.component.ts index 1af2b0bd13e8fb486dbce7a374c0c27dc9e515dd..26ab7a1dcd19bbd08ecfbac273aaa7639df37042 100644 --- a/smp-angular/src/app/service-group-edit/service-group-details-dialog/service-group-details-dialog.component.ts +++ b/smp-angular/src/app/service-group-edit/service-group-details-dialog/service-group-details-dialog.component.ts @@ -1,126 +1,150 @@ -import {Component, Inject} from '@angular/core'; -import {MAT_DIALOG_DATA, MatDialogRef} from '@angular/material'; +import {ChangeDetectorRef, Component, Inject, OnInit, ViewChild} from '@angular/core'; +import {MAT_DIALOG_DATA, MatDialog, MatDialogRef} from '@angular/material'; import {Observable} from "rxjs/internal/Observable"; -import {SearchTableResult} from "../../common/search-table/search-table-result.model"; import {HttpClient} from "@angular/common/http"; import {SmpConstants} from "../../smp.constants"; -import {UserRo} from "../../user/user-ro.model"; import {AlertService} from "../../alert/alert.service"; -import {DomainDetailsDialogComponent} from "../../domain/domain-details-dialog/domain-details-dialog.component"; import {AbstractControl, FormBuilder, FormControl, FormGroup, Validators} from "@angular/forms"; import {SearchTableEntityStatus} from "../../common/search-table/search-table-entity-status.model"; -import {DomainRo} from "../../domain/domain-ro.model"; import {ServiceGroupEditRo} from "../service-group-edit-ro.model"; -import {ServiceMetadataEditRo} from "../service-metadata-edit-ro.model"; +import {GlobalLookups} from "../../common/global-lookups"; +import {ServiceGroupExtensionWizardDialogComponent} from "../service-group-extension-wizard-dialog/service-group-extension-wizard-dialog.component"; +import {ServiceGroupExtensionRo} from "./service-extension-edit-ro.model"; +import {DomainRo} from "../../domain/domain-ro.model"; +import {ServiceGroupDomainEditRo} from "../service-group-domain-edit-ro.model"; +import {ConfirmationDialogComponent} from "../../common/confirmation-dialog/confirmation-dialog.component"; +import {SecurityService} from "../../security/security.service"; @Component({ - selector: 'app-messagelog-details', + selector: 'service-group-details', templateUrl: './service-group-details-dialog.component.html', styleUrls: ['./service-group-details-dialog.component.css'] }) -export class ServiceGroupDetailsDialogComponent { +export class ServiceGroupDetailsDialogComponent implements OnInit { static readonly NEW_MODE = 'New ServiceGroup'; static readonly EDIT_MODE = 'ServiceGroup Edit'; + @ViewChild('domainSelector') domainSelector: any; - userObserver: Observable< SearchTableResult> ; - domainObserver: Observable< SearchTableResult> ; - userlist: Array<UserRo> = []; editMode: boolean; formTitle: string; current: ServiceGroupEditRo & { confirmation?: string }; + showSpinner: boolean = false; dialogForm: FormGroup; - dialogFormBuilder: FormBuilder; - formControlUsers: FormControl; - /* - selectedDomain: DomainRo; - domainList: Array<any>; -*/ - - minCountOwners(min: number) { - return (c: AbstractControl): {[key: string]: any} => { - if (c.value.length >= min) + extensionObserver: Observable<ServiceGroupExtensionRo>; + + extensionValidationMessage: String = null; + isExtensionValid: boolean = true; + + serviceGroupDomain: ServiceGroupDomainEditRo[]; + + + minSelectedListCount(min: number) { + return (c: AbstractControl): { [key: string]: any } => { + if (c.value && c.value.length >= min) return null; - return { 'minCountOwners': {valid: false }}; + return {'minSelectedListCount': {valid: false}}; } } - constructor(protected http: HttpClient, public dialogRef: MatDialogRef<ServiceGroupDetailsDialogComponent>, - private alertService: AlertService, + constructor(public securityService: SecurityService, + public dialog: MatDialog, + protected http: HttpClient, + public dialogRef: MatDialogRef<ServiceGroupDetailsDialogComponent>, + private alertService: AlertService, + public lookups: GlobalLookups, @Inject(MAT_DIALOG_DATA) public data: any, - private fb: FormBuilder) { - // init user list - - this.userObserver = this.http.get<SearchTableResult>(SmpConstants.REST_USER); - this.userObserver.subscribe((users: SearchTableResult) => { - this.userlist = new Array(users.serviceEntities.length) - .map((v, index) => users.serviceEntities[index] as UserRo); - - this.userlist = users.serviceEntities.map(serviceEntity => { - return {...<UserRo>serviceEntity} - }); - this.updateData(); - }); - /* - // init domains - this.domainObserver = this.http.get<SearchTableResult>(SmpConstants.REST_DOMAIN); - this.domainObserver.subscribe((domains: SearchTableResult) => { - this.domainList = new Array(domains.serviceEntities.length) - .map((v, index) => {domains.serviceEntities[index] as DomainRo; - if ( this.editMode && v.domainCode ===data.row.domainCode) { - this.selectedDomain = v as DomainRo; - } - }); - - this.domainList = domains.serviceEntities.map(serviceEntity => { - return {...serviceEntity} - }); - }); -*/ - this.dialogFormBuilder = fb; - this.editMode = data.edit; - this.formTitle = this.editMode ? ServiceGroupDetailsDialogComponent.EDIT_MODE: ServiceGroupDetailsDialogComponent.NEW_MODE; + private dialogFormBuilder: FormBuilder, + private changeDetector: ChangeDetectorRef) { + this.editMode = this.data.edit; + this.formTitle = this.editMode ? ServiceGroupDetailsDialogComponent.EDIT_MODE : ServiceGroupDetailsDialogComponent.NEW_MODE; this.current = this.editMode ? { - ...data.row, + ...this.data.row, } : { id: null, participantIdentifier: '', - participantScheme: '', - serviceMetadata:[], - users:[], - domainCode:'', + participantScheme: '', + serviceMetadata: [], + users: [], + serviceGroupDomains: [], + extension: '', status: SearchTableEntityStatus.NEW, }; + // user is new when reopening the new item in edit mode! + // allow to change data but warn on error! this.dialogForm = this.dialogFormBuilder.group({ + 'participantIdentifier': new FormControl({ + value: '', + disabled: this.current.status !== SearchTableEntityStatus.NEW + }, + this.current.status !== SearchTableEntityStatus.NEW ? Validators.required : null), + 'participantScheme': new FormControl({value: '', disabled: this.current.status !== SearchTableEntityStatus.NEW}, + this.current.status !== SearchTableEntityStatus.NEW ? Validators.required : null), + 'serviceGroupDomains': new FormControl({value: []}, [this.minSelectedListCount(1)]), + 'users': new FormControl({value: []}, [this.minSelectedListCount(1)]), + 'extension': new FormControl({value: []}, []), - 'participantIdentifier': new FormControl({value: this.current.participantIdentifier, disabled: this.editMode}, this.editMode ? Validators.required : null), - 'participantScheme': new FormControl({value: this.current.participantScheme, disabled: this.editMode}, this.editMode ? Validators.required : null), - 'domainCode': new FormControl({value: this.current.domainCode},this.editMode ? Validators.required : null), - //'users': [new FormControl({value: this.current.users}, [ this.minCountOwners(1)] )], }); - + // update values + this.dialogForm.controls['participantIdentifier'].setValue(this.current.participantIdentifier); + this.dialogForm.controls['participantScheme'].setValue(this.current.participantScheme); + this.dialogForm.controls['serviceGroupDomains'].setValue(this.current.serviceGroupDomains); + this.dialogForm.controls['users'].setValue(this.current.users) + this.dialogForm.controls['extension'].setValue(this.current.extension) } - updateData(){ - this.formControlUsers = new FormControl(this.current.users); - this.formControlUsers.setValidators( [ this.minCountOwners(1)]); - - this.dialogForm.addControl("users",this.formControlUsers ); + ngOnInit() { + // retrieve xml extension for this service group + if (this.current.status !== SearchTableEntityStatus.NEW && !this.current.extension) { + // init domains + this.extensionObserver = this.http.get<ServiceGroupExtensionRo>(SmpConstants.REST_SERVICE_GROUP_EXTENSION + '/' + this.current.id); + this.extensionObserver.subscribe((res: ServiceGroupExtensionRo) => { + this.dialogForm.get('extension').setValue(res.extension); + }); + } + // detect changes for updated values in mat-selection-list (check change detection operations) + // else the following error is thrown :xpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value: + // 'aria-selected: false'. Current value: 'aria-selected: true' + // + this.changeDetector.detectChanges() } submitForm() { - this.checkValidity(this.dialogForm) - this.dialogRef.close(true); + this.checkValidity(this.dialogForm); + + + let request: ServiceGroupExtensionRo = { + serviceGroupId: this.current.id, + extension: this.dialogForm.controls['extension'].value, + } + // + let validationObservable = this.http.post<ServiceGroupExtensionRo>(SmpConstants.REST_SERVICE_GROUP_EXTENSION_VALIDATE, request); + this.showSpinner = true; + validationObservable.toPromise().then((res: ServiceGroupExtensionRo) => { + if (res.errorMessage) { + this.extensionValidationMessage = res.errorMessage; + this.isExtensionValid = false; + this.showSpinner = false; + } else { + this.extensionValidationMessage = "Extension is valid!"; + this.isExtensionValid = true; + this.showSpinner = false; + // we can close the dialog + this.dialogRef.close(true); + } + }); + + } checkValidity(g: FormGroup) { @@ -134,27 +158,153 @@ export class ServiceGroupDetailsDialogComponent { Object.keys(g.controls).forEach(key => { g.get(key).updateValueAndValidity(); }); + + } - updateParticipantIdentifier(event) { - this.current.participantIdentifier = event.target.value; + compareTableItemById(item1, item2): boolean { + return item1.id === item2.id; } - updateParticipantScheme(event) { - this.current.participantScheme = event.target.value; + + compareDomain(domain: DomainRo, serviceGroupDomain: ServiceGroupDomainEditRo): boolean { + return domain.id === serviceGroupDomain.domainId; } - userListChanged(usersSelected, event){ - this.current.users = []; - for(let usr of usersSelected) { - this.current.users.push(usr.value); + + public getCurrent(): ServiceGroupEditRo { + // change this two properties only on new + if (this.current.status === SearchTableEntityStatus.NEW) { + this.current.participantIdentifier = this.dialogForm.value['participantIdentifier']; + this.current.participantScheme = this.dialogForm.value['participantScheme']; + } else { + this.current.extensionStatus = SearchTableEntityStatus.UPDATED; + } + this.current.users = this.dialogForm.value['users']; + this.current.extension = this.dialogForm.value['extension']; + + + let domainOptions = this.domainSelector.options._results; + domainOptions.forEach(opt => { + let domValue = opt.value; + let sgd = this.getServiceGroupDomain(domValue.domainCode); + // if contains and deselected - delete + if (sgd && !opt.selected) { + this.current.serviceMetadata.forEach(metadata => { + if (metadata.domainCode === sgd.domainCode) { + metadata.status = SearchTableEntityStatus.REMOVED; + metadata.deleted = true; + } + }); + + var index = this.current.serviceGroupDomains.indexOf(sgd); + if (index !== -1) this.current.serviceGroupDomains.splice(index, 1); + + // delete service group + } else if (!sgd && opt.selected) { + let newDomain: ServiceGroupDomainEditRo = { + id: null, + domainId: domValue.id, + domainCode: domValue.domainCode, + smlSubdomain: domValue.domainCode, + smlRegistered: false, + serviceMetadataCount: 0, + status: SearchTableEntityStatus.NEW, + }; + this.current.serviceGroupDomains.push(newDomain); + } + }); + return this.current; + } + + onExtensionDelete() { + this.dialogForm.controls['extension'].setValue(""); + } + + onStartWizardDialog() { + + const formRef: MatDialogRef<any> = this.dialog.open(ServiceGroupExtensionWizardDialogComponent); + formRef.afterClosed().subscribe(result => { + if (result) { + let existingXML = this.dialogForm.controls['extension'].value; + let val = (existingXML ? existingXML + '\n' : '') + formRef.componentInstance.getExtensionXML(); + this.dialogForm.controls['extension'].setValue(val); + } + }); + } + + public onExtensionValidate() { + + let request: ServiceGroupExtensionRo = { + serviceGroupId: this.current.id, + extension: this.dialogForm.controls['extension'].value, + } + // + let validationObservable = this.http.post<ServiceGroupExtensionRo>(SmpConstants.REST_SERVICE_GROUP_EXTENSION_VALIDATE, request); + this.showSpinner = true; + validationObservable.toPromise().then((res: ServiceGroupExtensionRo) => { + if (res.errorMessage) { + this.extensionValidationMessage = res.errorMessage; + this.isExtensionValid = false; + this.showSpinner = false; + } else { + this.extensionValidationMessage = "Extension is valid!"; + this.isExtensionValid = true; + this.showSpinner = false; + } + }); + + } + + onPrettyPrintExtension() { + let existingXML = this.dialogForm.controls['extension'].value; + let request: ServiceGroupExtensionRo = { + serviceGroupId: this.current.id, + extension: existingXML, + } + let validationObservable = this.http.post<ServiceGroupExtensionRo>(SmpConstants.REST_SERVICE_GROUP_EXTENSION_FORMAT, request); + validationObservable.subscribe((res: ServiceGroupExtensionRo) => { + if (res.errorMessage) { + this.extensionValidationMessage = res.errorMessage; + this.isExtensionValid = false; + } else { + this.dialogForm.get('extension').setValue(res.extension); + this.isExtensionValid = true; + } + + }); + } + + onDomainSelectionChanged(event) { + // if deselected warn serviceMetadata will be deleted + let domainCode = event.option.value.domainCode; + if (!event.option.selected) { + this.dialog.open(ConfirmationDialogComponent, { + data: { + title: "Registred serviceMetadata on domain!", + description: "Unregistration of domain will also delete it's serviceMetadata. Do you want to continue?" + } + }).afterClosed().subscribe(result => { + if (!result) { + event.option.selected = true; + } + }) + + } } - compareUserById(item1, item2): boolean{ - return item1.id=== item2.id; + + public getServiceMetadataCountOnDomain(domainCode: String) { + return this.current.serviceMetadata.filter(smd => { + return smd.domainCode === domainCode + }).length; } - isSelected(id): boolean { - return !!this.current.users.find(user => user.id===id); + public getServiceGroupDomain(domainCode: String) { + return this.current.serviceGroupDomains ? + this.current.serviceGroupDomains.find(smd => { + return smd.domainCode === domainCode + }) : null; } + } diff --git a/smp-angular/src/app/service-group-edit/service-group-domain-edit-ro.model.ts b/smp-angular/src/app/service-group-edit/service-group-domain-edit-ro.model.ts new file mode 100644 index 0000000000000000000000000000000000000000..e23ff4e734545f362e42a406b414f0ea8bee592c --- /dev/null +++ b/smp-angular/src/app/service-group-edit/service-group-domain-edit-ro.model.ts @@ -0,0 +1,10 @@ +import {SearchTableEntity} from "../common/search-table/search-table-entity.model"; + +export interface ServiceGroupDomainEditRo extends SearchTableEntity { + id: number; + domainId: number; + domainCode: string; + smlSubdomain: string; + smlRegistered: boolean; + serviceMetadataCount?: number; +} diff --git a/smp-angular/src/app/service-group-edit/service-group-edit-controller.ts b/smp-angular/src/app/service-group-edit/service-group-edit-controller.ts index fa802d8a05a0e7605d25d4bd886c3ae423627027..7c37c53c17cc47f8898b989110fba3075d4da0a1 100644 --- a/smp-angular/src/app/service-group-edit/service-group-edit-controller.ts +++ b/smp-angular/src/app/service-group-edit/service-group-edit-controller.ts @@ -4,46 +4,71 @@ import {ServiceGroupDetailsDialogComponent} from './service-group-details-dialog import {ServiceGroupEditRo} from './service-group-edit-ro.model'; import {SearchTableEntityStatus} from '../common/search-table/search-table-entity-status.model'; import {ServiceMetadataEditRo} from "./service-metadata-edit-ro.model"; -import {DomainDetailsDialogComponent} from "../domain/domain-details-dialog/domain-details-dialog.component"; +import {ServiceGroupMetadataDialogComponent} from "./service-group-metadata-dialog/service-group-metadata-dialog.component"; export class ServiceGroupEditController implements SearchTableController { - constructor(public dialog: MatDialog) { } + constructor(public dialog: MatDialog) { + } - public showDetails( row: any, config?: MatDialogConfig,) { + public showDetails(row: any, config?: MatDialogConfig,) { let dialogRef: MatDialogRef<ServiceGroupDetailsDialogComponent> = this.dialog.open(ServiceGroupDetailsDialogComponent); dialogRef.afterClosed().subscribe(result => { //Todo: }); - } - public showMetadataList(row: any) { + public edit(row: any) { } + public delete(row: any) { - public edit(row: any) { } + // set all rows as deleted + let sgRow = row as ServiceGroupEditRo; + sgRow.serviceMetadata.forEach(function(part, index, metaDataList) { + metaDataList[index].status = SearchTableEntityStatus.REMOVED; + metaDataList[index].deleted=true; + }); - public delete(row: any) { } + } public newDialog(config?: MatDialogConfig): MatDialogRef<ServiceGroupDetailsDialogComponent> { return this.dialog.open(ServiceGroupDetailsDialogComponent, config); } + public newMetadataDialog(config?: MatDialogConfig): MatDialogRef<ServiceGroupMetadataDialogComponent> { + return this.dialog.open(ServiceGroupMetadataDialogComponent, config); + } + public newRow(): ServiceGroupEditRo { return { id: null, index: null, - participantIdentifier:'', + participantIdentifier: '', participantScheme: '', - domainCode: '', - smlSubdomain: '', - serviceMetadata:[], + serviceMetadata: [], users: [], + serviceGroupDomains: [], + extensionStatus: SearchTableEntityStatus.NEW, status: SearchTableEntityStatus.NEW }; } + public newServiceMetadataRow(): ServiceMetadataEditRo { + return { + id:null, + documentIdentifier: '', + documentIdentifierScheme: '', + smlSubdomain: '', + domainCode: '', + domainId:null, + status: SearchTableEntityStatus.NEW, + xmlContentStatus: SearchTableEntityStatus.NEW, + }; + } + + public dataSaved() {} + } diff --git a/smp-angular/src/app/service-group-edit/service-group-edit-ro.model.ts b/smp-angular/src/app/service-group-edit/service-group-edit-ro.model.ts index 9b626bc7ee710dd378bce97b5eb42324f73249e5..df6bdd5b29f351bdac6ed68d3df10d8220e0e9eb 100644 --- a/smp-angular/src/app/service-group-edit/service-group-edit-ro.model.ts +++ b/smp-angular/src/app/service-group-edit/service-group-edit-ro.model.ts @@ -1,13 +1,16 @@ -import { ServiceMetadataEditRo } from './service-metadata-edit-ro.model'; +import {ServiceMetadataEditRo} from './service-metadata-edit-ro.model'; import {SearchTableEntity} from "../common/search-table/search-table-entity.model"; import {UserRo} from "../user/user-ro.model"; +import {ServiceGroupDomainEditRo} from "./service-group-domain-edit-ro.model"; +import {SearchTableEntityStatus} from "../common/search-table/search-table-entity-status.model"; export interface ServiceGroupEditRo extends SearchTableEntity { id: number; participantIdentifier: string; participantScheme: string; - domainCode:'', - smlSubdomain:'', serviceMetadata: Array<ServiceMetadataEditRo>; + serviceGroupDomains: Array<ServiceGroupDomainEditRo>; users: Array<UserRo>; + extension?: string; + extensionStatus: SearchTableEntityStatus; } diff --git a/smp-angular/src/app/service-group-edit/service-group-edit.component.css b/smp-angular/src/app/service-group-edit/service-group-edit.component.css index d1a612141160e70f00bfe5aee114e010052ee4a5..14b69d9316c9f84ada5943898458fa6417a4426e 100644 --- a/smp-angular/src/app/service-group-edit/service-group-edit.component.css +++ b/smp-angular/src/app/service-group-edit/service-group-edit.component.css @@ -16,3 +16,19 @@ .datatable-body{ overflow-y: scroll; } + + +/deep/ .inner-table { + margin-left: 100px; + width: 80%; +} + +/deep/ .inner-table .datatable-body-cell { + padding: 1.5em 2px !important; + +} + +/deep/ .inner-table .mat-icon-button { + height: auto !important;; + line-height: unset !important; +} diff --git a/smp-angular/src/app/service-group-edit/service-group-edit.component.html b/smp-angular/src/app/service-group-edit/service-group-edit.component.html index 894019e357c79ca80886e3d62a309a80c36a9103..c6a1c3f5952b8057c6d0672ef1ed5c3a6b8c01c8 100644 --- a/smp-angular/src/app/service-group-edit/service-group-edit.component.html +++ b/smp-angular/src/app/service-group-edit/service-group-edit.component.html @@ -1,4 +1,4 @@ -<smp-search-table +<smp-search-table #searchTable page_id='edit_id' title='Edit' [columnPicker]="columnPicker" @@ -8,48 +8,52 @@ [filter]="filter" [searchTableController]="serviceGroupEditController" [tableRowDetailContainer]="tableRowDetailContainer" + [additionalRowActionButtons]="additionalRowActionButtons" + [allowNewItems]="securityService.isCurrentUserSMPAdmin()" > <ng-template #rowMetadataAction let-row="row" let-value="value" ngx-datatable-cell-template> - <button mat-button color="primary" (click)="metadataRowButtonAction(row)" id="metadataRowButtonAction{{row.$$index}}_id" tooltip="Metadata"> <mat-icon>view_list</mat-icon> <span>Metadata</span> </button> + </ng-template> + <ng-template #rowSMPUrlLinkAction let-row="row" let-value="value" ngx-datatable-cell-template> + <a target="_blank" + href="{{contextPath}}{{row.participantScheme}}::{{row.participantIdentifier}}" >Open URL</a> </ng-template> <ng-template #searchPanel> <mat-form-field> - <input matInput placeholder="Participant Id" name="messageId" [(ngModel)]="filter.messageId" - #messageId="ngModel" id="messageid_id"> + <input matInput placeholder="Participant Identifier" name="ParticipantIdentifier" + [(ngModel)]="filter.participantIdentifier" + #messageId="ngModel" id="participantIdentifier"> </mat-form-field> <mat-form-field> - <input matInput placeholder="Participant schema" name="patricipantSchema" [(ngModel)]="filter.messageId" - #messageId="ngModel" id="participanschema_id"> + <input matInput placeholder="Participant scheme" name="patricipantScheme" [(ngModel)]="filter.participantScheme" + #messageId="ngModel" id="participantScheme"> </mat-form-field> - <mat-select placeholder="Domain " [(ngModel)]="filter.messageStatus" name="messageStatus" - id="messagestatus_id"> + <mat-select placeholder="Domain (sml subdomain)" [(ngModel)]="filter.domain" name="domain" + id="domain_id"> <mat-option [value]="''"> </mat-option> - <mat-option *ngFor="let mstatus of msgStatus" [value]="mstatus"> - {{mstatus}} + <mat-option *ngFor="let domain of lookups.cachedDomainList" [value]="domain.domainCode"> + {{domain.domainCode}} ({{domain.smlSubdomain}}) </mat-option> </mat-select> </ng-template> - <ng-template #additionalToolButtons> - <div style="background-color: #03A9F4; display: inline-block; width: 3px; height: 100%; margin-right: 8px"> - </div> - <button mat-raised-button color="primary" - id="metadatabutton_id"> - <mat-icon>view_list</mat-icon> - <span>Metadata</span> - </button> + <ng-template #additionalToolButtons></ng-template> + <ng-template #additionalRowActionButtons let-row="row"> + <button mat-icon-button color="primary" [disabled]="row?.deleted || loading" + (click)="onAddMetadataRow(row)" tooltip="Edit"> + <mat-icon>playlist_add</mat-icon> + </button> </ng-template> <ng-template #tableRowDetailContainer let-row="row"> @@ -59,20 +63,24 @@ </div> <div *ngIf="row.serviceMetadata.length !== 0" > <ngx-datatable - class='material striped' - style="width: 80%" + class='inner-table material striped' [loadingIndicator]="loading" [rows]='row.serviceMetadata' [columnMode]='"force"' [headerHeight]='50' [footerHeight]='50' - [rowHeight]='"auto"'> - <ngx-datatable-column prop="domainCode" name="Domain" maxWidth="250" ></ngx-datatable-column> + [rowHeight]='"auto"' + [rowClass]="getRowClass"> + <ngx-datatable-column [cellTemplate]="rowDomain" name="Domain" maxWidth="250" ></ngx-datatable-column> <ngx-datatable-column prop="documentIdentifierScheme" name="Document identifier scheme" maxWidth="350" ></ngx-datatable-column> <ngx-datatable-column prop="documentIdentifier" name="Document identifier" maxWidth="250" ></ngx-datatable-column> <ngx-datatable-column [cellTemplate]="rowMetadataSMPUrlLinkAction" name="URL" maxWidth="250" ></ngx-datatable-column> <ngx-datatable-column [cellTemplate]="rowMetadataActions" name="Actions" maxWidth="150" ></ngx-datatable-column> + <ng-template #rowDomain let-rowSmd="row" ngx-datatable-cell-template> + {{rowSmd.domainCode}} ({{rowSmd.smlSubdomain}}) + </ng-template> + <ng-template #rowMetadataSMPUrlLinkAction let-rowSmd="row" ngx-datatable-cell-template> <a target="_blank" href="{{contextPath}}{{row.participantScheme}}::{{row.participantIdentifier}}/services/{{rowSmd.documentIdentifierScheme}}::{{rowSmd.documentIdentifier}}" >Open URL</a> @@ -82,11 +90,11 @@ <ng-template #rowMetadataActions let-rowSmd="row" ngx-datatable-cell-template> <div> <button mat-icon-button color="primary" [disabled]="rowSmd.deleted || loading" - (click)="onEditMetadataRow(rowSmd)" tooltip="Edit"> + (click)="onEditMetadataRow(row, rowSmd)" tooltip="Edit"> <mat-icon>edit</mat-icon> </button> <button mat-icon-button color="primary" [disabled]="rowSmd.deleted || loading" - (click)="onDeleteMetadataRowActionClicked(rowSmd)" tooltip="Delete"> + (click)="onDeleteMetadataRowActionClicked(row, rowSmd)" tooltip="Delete"> <mat-icon>delete</mat-icon> </button> </div> diff --git a/smp-angular/src/app/service-group-edit/service-group-edit.component.ts b/smp-angular/src/app/service-group-edit/service-group-edit.component.ts index b81066cb4eb7e3ff342a58c5c3308d1c68c055b3..8eb457e4b9a0597bda3d043a6c86385083996b55 100644 --- a/smp-angular/src/app/service-group-edit/service-group-edit.component.ts +++ b/smp-angular/src/app/service-group-edit/service-group-edit.component.ts @@ -1,69 +1,68 @@ -import {Component, OnInit, TemplateRef, ViewChild} from '@angular/core'; +import {ChangeDetectorRef, Component, OnInit, TemplateRef, ViewChild} from '@angular/core'; import {ColumnPicker} from '../common/column-picker/column-picker.model'; import {MatDialog, MatDialogRef} from '@angular/material'; import {AlertService} from '../alert/alert.service'; import {ServiceGroupEditController} from './service-group-edit-controller'; import {HttpClient} from '@angular/common/http'; -import {ServiceGroupDetailsDialogComponent} from "./service-group-details-dialog/service-group-details-dialog.component"; import {SmpConstants} from "../smp.constants"; -import {Observable} from "rxjs/internal/Observable"; -import {UserRo} from "../user/user-ro.model"; -import {SearchTableResult} from "../common/search-table/search-table-result.model"; +import {SearchTableEntityStatus} from "../common/search-table/search-table-entity-status.model"; +import {SearchTableComponent} from "../common/search-table/search-table.component"; +import {GlobalLookups} from "../common/global-lookups"; +import {SecurityService} from "../security/security.service"; @Component({ - moduleId: module.id, - templateUrl:'./service-group-edit.component.html', + moduleId: 'edit', + templateUrl: './service-group-edit.component.html', styleUrls: ['./service-group-edit.component.css'] }) export class ServiceGroupEditComponent implements OnInit { @ViewChild('rowMetadataAction') rowMetadataAction: TemplateRef<any> @ViewChild('rowActions') rowActions: TemplateRef<any>; - @ViewChild('rowSMPUrlLinkAction') rowSMPUrlLinkAction: TemplateRef<any> + @ViewChild('rowSMPUrlLinkAction') rowSMPUrlLinkAction: TemplateRef<any>; + @ViewChild('searchTable') searchTable: SearchTableComponent; columnPicker: ColumnPicker = new ColumnPicker(); serviceGroupEditController: ServiceGroupEditController; filter: any = {}; baseUrl: string = SmpConstants.REST_EDIT; + contextPath: string = location.pathname.substring(0, location.pathname.length - 3); // remove /ui s - userObserver: Observable< SearchTableResult> ; - domainObserver: Observable< SearchTableResult> ; - userlist: Array<UserRo> = []; + constructor(public securityService: SecurityService, + protected lookups: GlobalLookups, + protected http: HttpClient, + protected alertService: AlertService, + public dialog: MatDialog, + private changeDetector: ChangeDetectorRef) { - constructor(protected http: HttpClient, protected alertService: AlertService, public dialog: MatDialog) { - - this.userObserver = this.http.get<SearchTableResult>(SmpConstants.REST_USER); - this.userObserver.subscribe((users: SearchTableResult) => { - this.userlist = new Array(users.serviceEntities.length) - .map((v, index) => users.serviceEntities[index] as UserRo); - - this.userlist = users.serviceEntities.map(serviceEntity => { - return {...<UserRo>serviceEntity} - }); - }); } ngOnInit() { + + this.serviceGroupEditController = new ServiceGroupEditController(this.dialog); this.columnPicker.allColumns = [ { name: 'Metadata size', prop: 'serviceMetadata.length', - width: 80, - maxWidth: 120 + width: 120, + maxWidth: 120, + resizable: "false" }, { name: 'Owners size', prop: 'users.length', - width: 80, - maxWidth: 120 + width: 120, + maxWidth: 120, + resizable: "false" }, { name: 'Participant scheme', prop: 'participantScheme', - width: 250, - maxWidth: 300 + width: 300, + maxWidth: 300, + resizable: "false" }, { name: 'Participant identifier', @@ -72,8 +71,9 @@ export class ServiceGroupEditComponent implements OnInit { { cellTemplate: this.rowSMPUrlLinkAction, name: 'OASIS ServiceGroup URL', - width: 150, + width: 250, maxWidth: 250, + resizable: "false", sortable: false }, @@ -84,19 +84,94 @@ export class ServiceGroupEditComponent implements OnInit { }); } - metadataRowButtonAction(row: any){ - this.serviceGroupEditController.showMetadataList(row); - } - details(row: any) { this.serviceGroupEditController.showDetails(row); } - onEditMetadataRow(row:any){ - alert("edit" + row); + onAddMetadataRow(row: any) { + let rowNumber = this.searchTable.rows.indexOf(row); + + const formRef: MatDialogRef<any> = this.serviceGroupEditController.newMetadataDialog({ + data: {edit: false, serviceGroup: row, metadata: this.serviceGroupEditController.newServiceMetadataRow()} + }); + formRef.afterClosed().subscribe(result => { + if (result) { + const status = row.status === SearchTableEntityStatus.PERSISTED + ? SearchTableEntityStatus.UPDATED + : row.status; + + let data = formRef.componentInstance.getCurrent(); + row.serviceMetadata.push(data); + this.searchTable.updateTableRow(rowNumber, row, status); + } + }); + + } + + getRowClass(row) { + return { + 'table-row-new': (row.status === SearchTableEntityStatus.NEW), + 'table-row-updated': (row.status === SearchTableEntityStatus.UPDATED), + 'deleted': (row.status === SearchTableEntityStatus.REMOVED) + }; } - onDeleteMetadataRowActionClicked(row:any){ - alert("delete" + row); + + onEditMetadataRow(serviceGroupRow: any,metaDataRow: any) { + let metadataRowNumber = serviceGroupRow.serviceMetadata.indexOf(metaDataRow); + + const formRef: MatDialogRef<any> = this.serviceGroupEditController.newMetadataDialog({ + data: {edit: true, serviceGroup: serviceGroupRow, metadata: metaDataRow} + }); + formRef.afterClosed().subscribe(result => { + if (result) { + + let statusMetadata =metaDataRow.status === SearchTableEntityStatus.PERSISTED + ? SearchTableEntityStatus.UPDATED + : metaDataRow; + + + metaDataRow.status = statusMetadata; + metaDataRow = {...formRef.componentInstance.getCurrent()}; + + serviceGroupRow.serviceMetadata [metadataRowNumber] = {...metaDataRow }; + // change reference to fire table update + serviceGroupRow.serviceMetadata = [...serviceGroupRow.serviceMetadata] + + // set row as updated + const status = serviceGroupRow.status === SearchTableEntityStatus.PERSISTED + ? SearchTableEntityStatus.UPDATED + : serviceGroupRow.status; + serviceGroupRow.status = status; + + this.changeDetector.detectChanges(); + + } + }); + } + + onDeleteMetadataRowActionClicked(serviceGroupRow: any, metaDataRow: any) { + let rowNumber = this.searchTable.rows.indexOf(serviceGroupRow); + + if (metaDataRow.status === SearchTableEntityStatus.NEW) { + serviceGroupRow.splice(serviceGroupRow.indexOf(metaDataRow), 1); + } else { + metaDataRow.status = SearchTableEntityStatus.REMOVED; + metaDataRow.deleted = true; + // set row as updated + const status = serviceGroupRow.status === SearchTableEntityStatus.PERSISTED + ? SearchTableEntityStatus.UPDATED + : serviceGroupRow.status; + serviceGroupRow.status = status; + + // do not do that it updates the whole table + // this.searchTable.rows[rowNumber] = {...serviceGroupRow, status}; + // this.searchTable.rows = [...this.searchTable.rows]; + } + } + + // for dirty guard... + isDirty (): boolean { + return this.searchTable.isDirty(); } } diff --git a/smp-angular/src/app/service-group-edit/service-group-extension-wizard-dialog/service-group-extension-wizard-dialog.component.css b/smp-angular/src/app/service-group-edit/service-group-extension-wizard-dialog/service-group-extension-wizard-dialog.component.css new file mode 100644 index 0000000000000000000000000000000000000000..66c5204a6d4d3b0cee28761c51cfce5310bdb950 --- /dev/null +++ b/smp-angular/src/app/service-group-edit/service-group-extension-wizard-dialog/service-group-extension-wizard-dialog.component.css @@ -0,0 +1,8 @@ +#extensionTextArea { + border: none; + width: 610px; + height:340px; + -webkit-box-sizing: border-box; /* <=iOS4, <= Android 2.3 */ + -moz-box-sizing: border-box; /* FF1+ */ + box-sizing: border-box; /* Chrome, IE8, Opera, Safari 5.1*/ +} diff --git a/smp-angular/src/app/service-group-edit/service-group-extension-wizard-dialog/service-group-extension-wizard-dialog.component.html b/smp-angular/src/app/service-group-edit/service-group-extension-wizard-dialog/service-group-extension-wizard-dialog.component.html new file mode 100644 index 0000000000000000000000000000000000000000..b29362418984bb4b044040e03afb6324d4afe5d5 --- /dev/null +++ b/smp-angular/src/app/service-group-edit/service-group-extension-wizard-dialog/service-group-extension-wizard-dialog.component.html @@ -0,0 +1,39 @@ +<h2 mat-dialog-title>MetadataService Wizard</h2> + +<mat-dialog-content> + <form [formGroup]="dialogForm"> + <mat-card> + <mat-card-content> + <fieldset style="border: none;"> + + + <mat-form-field *ngFor="let elmnt of elements;" style="width:100%"> + <input matInput + placeholder="{{elmnt.name}} - {{elmnt.description}}" + [name]="elmnt.name" + id="{{elmnt.name}}_id" + [formControl]="dialogForm.controls[elmnt.name]" maxlength="255"> + + </mat-form-field > + + + </fieldset> + </mat-card-content> + </mat-card> + </form> +</mat-dialog-content> + +<mat-dialog-actions> + <div class="group-action-button"> + <button mat-raised-button color="primary" [mat-dialog-close]="true" + [disabled]="!dialogForm.valid"> + <mat-icon>check_circle</mat-icon> + <span>OK</span> + </button> + + <button mat-raised-button color="primary" mat-dialog-close> + <mat-icon>cancel</mat-icon> + <span>Cancel</span> + </button> + </div> +</mat-dialog-actions> diff --git a/smp-angular/src/app/service-group-edit/service-group-extension-wizard-dialog/service-group-extension-wizard-dialog.component.ts b/smp-angular/src/app/service-group-edit/service-group-extension-wizard-dialog/service-group-extension-wizard-dialog.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..96428bb26042c8d6230f69df3cdb289937edb443 --- /dev/null +++ b/smp-angular/src/app/service-group-edit/service-group-extension-wizard-dialog/service-group-extension-wizard-dialog.component.ts @@ -0,0 +1,62 @@ +import {Component} from '@angular/core'; +import {MatDialogRef} from '@angular/material'; +import {FormBuilder, FormControl, FormGroup} from "@angular/forms"; + +@Component({ + selector: 'service-group-metadata-wizard', + templateUrl: './service-group-extension-wizard-dialog.component.html', + styleUrls: ['./service-group-extension-wizard-dialog.component.css'] +}) +export class ServiceGroupExtensionWizardDialogComponent { + dialogForm: FormGroup; + + dummyXML: string ="<!-- Custom element is mandatory by OASIS SMP schema.\n Replace following element with your XML structure. -->\n<ext:example xmlns:ext=\"http://my.namespace.eu\">my mandatory content</ext:example>" + + elements: any[] = [ + {name:'ExtensionID', description:'An identifier for the Extension assigned by the creator of the extension.'}, + {name:'ExtensionName', description:'A name for the Extension assigned by the creator of the extension.'}, + {name:'ExtensionAgencyID', description:'An agency that maintains one or more Extensions.'}, + {name:'ExtensionAgencyName', description:'The name of the agency that maintains the Extension.'}, + {name:'ExtensionAgencyURI', description:'A URI for the Agency that maintains the Extension.'}, + {name:'ExtensionVersionID', description:'The version of the Extension.'}, + {name:'ExtensionURI', description:'A URI for the Extension.'}, + {name:'ExtensionReasonCode', description:'A code for reason the Extension is being included.'}, + {name:'ExtensionReason', description:'A description of the reason for the Extension.'}, + ]; + + constructor(public dialogRef: MatDialogRef<ServiceGroupExtensionWizardDialogComponent>, + private dialogFormBuilder: FormBuilder) { + + this.dialogForm = this.dialogFormBuilder.group({ }); + + let arrayLength = this.elements.length; + for (var i = 0; i < arrayLength; i++) { + this.dialogForm.addControl(this.elements[i].name, new FormControl('')); + } + } + + getExtensionXML(){ + var xmlString = '<Extension xmlns="http://docs.oasis-open.org/bdxr/ns/SMP/2016/05">' + let arrayLength = this.elements.length; + for (var i = 0; i < arrayLength; i++) { + let str = this.dialogForm.get(this.elements[i].name).value; + if (str && 0 !== str.length) { + xmlString = xmlString + '\n <'+this.elements[i].name+'>' + this.xmlSpecialChars(str) + '</'+this.elements[i].name+'>'; + } + } + xmlString = xmlString+ '\n' +this.dummyXML+ '\n</Extension>' + + return xmlString; + } + + xmlSpecialChars(unsafe) { + return unsafe + .replace(/&/g, "&") + .replace(/</g, "<") + .replace(/>/g, ">") + .replace(/"/g, """); + } + + + +} diff --git a/smp-angular/src/app/service-group-edit/service-group-metadata-dialog/service-group-metadata-dialog.component.html b/smp-angular/src/app/service-group-edit/service-group-metadata-dialog/service-group-metadata-dialog.component.html index efb822ceb4e1b7ce277f27b6410d88d568be4c23..db576dbfca1adcc903eedde151d316de8f13fa71 100644 --- a/smp-angular/src/app/service-group-edit/service-group-metadata-dialog/service-group-metadata-dialog.component.html +++ b/smp-angular/src/app/service-group-edit/service-group-metadata-dialog/service-group-metadata-dialog.component.html @@ -1,55 +1,126 @@ -<h2 mat-dialog-title>ServiceGroup Metadata</h2> -<mat-dialog-content style="height:460px;width:950px"> - <textarea style="width: 100%; height: 95%"><ServiceMetadata xmlns="http://docs.oasis-open.org/bdxr/ns/SMP/2016/05"> - <ServiceInformation> - <ParticipantIdentifier scheme="participantSchema">participantIdentifier</ParticipantIdentifier> - <DocumentIdentifier scheme="docSchema">docTypeIdentifier</DocumentIdentifier> - <ProcessList> - <Process> - <ProcessIdentifier scheme="cenbii-procid-ubl">urn:www.cenbii.eu:profile:bii04:ver1.0</ProcessIdentifier> - <ServiceEndpointList> - <Endpoint transportProfile="busdox-transport-start"> - <EndpointURI>https://poland.pl/theService</EndpointURI> - <RequireBusinessLevelSignature>true</RequireBusinessLevelSignature> - <ServiceActivationDate>2003-01-01T00:00:00</ServiceActivationDate> - <ServiceExpirationDate>2020-05-01T00:00:00</ServiceExpirationDate> - <Certificate>MIICUTCCAbqgAwIBAgIEWoKrxzANBgkqhkiG9w0BAQsFADBtMQswCQYDVQQGEwJCRTEcMBoGA1UECgwTRXVyb3BlYW4gQ29tbWlzc2lvbjEYMBYGA1UECwwPRElHSVQgZURlbGl2ZXJ5MSYwJAYDVQQDDB1TYW1wbGUgU2lnbmF0dXJlcyBDZXJ0aWZpY2F0ZTAeFw0xODAyMTMwOTEzMjBaFw0zODAyMTMwOTEzMjBaMG0xCzAJBgNVBAYTAkJFMRwwGgYDVQQKDBNFdXJvcGVhbiBDb21taXNzaW9uMRgwFgYDVQQLDA9ESUdJVCBlRGVsaXZlcnkxJjAkBgNVBAMMHVNhbXBsZSBTaWduYXR1cmVzIENlcnRpZmljYXRlMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDIWYibbbJ6YT3uLQ0oup8kB1jJO/o16nlRfB3CbCbmpGZ2r+zCh67EMstKLltuk3peJ6QTcSaFV2oS1KLKWxwWsV4iEs5qggA5BTkzUicTsQWw39OcPPe0wniJeXdNUlOUFP2ab0ZQ9UMrJgVtlyF2uLijoKx1XR0mpTKmajh1CQIDAQABMA0GCSqGSIb3DQEBCwUAA4GBABoFFJ+vgOo/78SPv73+MEdxkydn5EMsZ+Q4Vt1BmBaq78RcjeS7LpvPZtRxSzW+w59825BbaOCJ5K0oc6lwOXc+SS0ZI0JK2vMlp/2REFLedqHqnhpSWFKWM0Zrq80o1SzBQHC1hq95RXml6RzzPEEK0Ll0dVH7HXRpekRScGic</Certificate> - <ServiceDescription>Sample description of invoicing service</ServiceDescription> - <TechnicalContactUrl>https://example.com</TechnicalContactUrl> - </Endpoint> - </ServiceEndpointList> - <Extension> - <ExtensionID>optional sample ID</ExtensionID> - <ExtensionName>UTF-8 characters are handled, i.e. polish special ones: zażółć gęślą jaźń</ExtensionName> - <ExtensionAgencyID>text</ExtensionAgencyID> - <ExtensionAgencyName>text</ExtensionAgencyName> - <ex:dummy xmlns:ex="http://test.eu">dummy1</ex:dummy> - </Extension> - <Extension> - <ex:dummy xmlns:ex="http://test.eu">dumm2</ex:dummy> - </Extension> - </Process> - </ProcessList> - <Extension> - <ex:dummynode xmlns:ex="http://test.eu">Sample not mandatory extension</ex:dummynode> - </Extension> - </ServiceInformation> -</ServiceMetadata></textarea> +<h2 mat-dialog-title>{{formTitle}}</h2> +<mat-dialog-content style="height:750px;width:1200px"> + <div fxLayout="column"> + <mat-card > -</mat-dialog-content> + <mat-card-content > + <div class="panel"> + <mat-form-field style="width:30%"> + <input matInput placeholder="Participant schema" name="patricipantSchema" id="participanSchema_id" + [formControl]="dialogForm.controls['participantScheme']" + > + </mat-form-field> + <mat-form-field style="width:40%"> + <input matInput placeholder="Participant Identifier" name="participantIdentifier" id="participantIdentifier_id" + [formControl]="dialogForm.controls['participantIdentifier']"> + </mat-form-field> + <mat-select #domainList [compareWith]="compareDomainCode" placeholder="Domain (sml subdomain)" id="domain_id" style="width:20%" + [formControl]="dialogForm.controls['domainCode']"> + <mat-option *ngFor="let domain of currentServiceGroup.serviceGroupDomains" [value]="domain"> + {{domain.domainCode}} ({{domain.smlSubdomain}}) + </mat-option> + </mat-select> + <div + *ngIf="(!editMode && dialogForm.controls['domainCode'].touched || editMode) && dialogForm.controls['domainCode'].hasError('required')" + style="color:red; font-size: 70%"> + Domain must be selected. + </div> + + <mat-form-field style="width:30%"> + <input matInput placeholder="Document identifier scheme" name="documentScheme" id="documentScheme_id" + [formControl]="dialogForm.controls['documentIdentifierScheme']"> + </mat-form-field> + <mat-form-field style="width:55%"> + <input matInput placeholder="Document identifier" name="documentIdentifier" id="documentIdentifier_id" + [formControl]="dialogForm.controls['documentIdentifier']"> + </mat-form-field> + </div> + </mat-card-content> + </mat-card> + + + <mat-card> + <mat-card-content> + <mat-toolbar> + <mat-toolbar-row> + <button mat-raised-button color="primary" + matTooltip="Clear the metadata content." + matTooltipPosition="below" + (click)="onClearServiceMetadata()"> + <mat-icon>clear</mat-icon> + <span>Clear</span> + </button> + <button mat-raised-button color="primary" + matTooltip="Generates simple XML" + matTooltipPosition="below" + (click)="onGenerateSimpleXML()"> + <mat-icon>settings_ethernet</mat-icon> + <span>Generate XML</span> + </button> + <!-- button mat-raised-button color="primary" + matTooltip="Opens wizard to configure ServiceMetadata xml." + matTooltipPosition="below" + (click)="onStartWizardDialog()"> + <mat-icon>add_box</mat-icon> + <span>Metadata wizard</span> + </button --> + <button mat-raised-button color="primary" + atTooltip="Validate serviceMetadata XML." + matTooltipPosition="below" + (click)="onServiceMetadataValidate()"> + <mat-icon>warning</mat-icon> + <span>Validate</span> + </button> + </mat-toolbar-row> + </mat-toolbar> + + <div style="display: block;" style="border:1px; solid: #999999;margin:5px 0; padding:3px;"> + <div class="textwrapper"> + <textarea matInput style="width:100%;border: #03A9F4 1px solid" cols="2" rows="25" + resizeable="false" + id="MetadataTextArea" + placeholder="Metadata XML" name="metadataXML" + [formControl]="dialogForm.controls['xmlContent']" + + ></textarea> + </div> + <div + *ngIf="metadataValidationMessage" + [style.color]="isMetadataValid?'green':'red'"> + {{metadataValidationMessage}} + </div> + </div> + + + <!-- mat-form-field> + <div> + <textarea style="width:100% !important;height: 400px !important; -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box;" + + matInput id="extensionTextArea" + placeholder="Extension" name="extension" + [formControl]="dialogForm.controls['extension']" + ></textarea> + </div> + </mat-form-field --> + </mat-card-content> + </mat-card> + </div> +</mat-dialog-content> <mat-dialog-actions> <div class="group-action-button"> - <button id="ServiceGroupsSaveButton" mat-raised-button color="primary" (click)="dialogRef.close({})" - style="margin-top:10px"> - <mat-icon>save</mat-icon> - <span>Save</span> + <button mat-raised-button color="primary" (click)="submitForm()" + [disabled]="!dialogForm.valid"> + <mat-icon>check_circle</mat-icon> + <span>OK</span> </button> - <button id="ServiceGroupsCloseButton" mat-raised-button color="primary" (click)="dialogRef.close({})" - style="margin-top:10px"> - <mat-icon>close</mat-icon> - <span>Close</span> + <button mat-raised-button color="primary" mat-dialog-close> + <mat-icon>cancel</mat-icon> + <span>Cancel</span> </button> </div> </mat-dialog-actions> +<div style="text-align: right; font-size: 70%">* required fields</div> diff --git a/smp-angular/src/app/service-group-edit/service-group-metadata-dialog/service-group-metadata-dialog.component.ts b/smp-angular/src/app/service-group-edit/service-group-metadata-dialog/service-group-metadata-dialog.component.ts index 196c6499d47e8945bd077fcef7b3a7deaf13b92d..9c947ff4f88906d770175e79909370bd3afa982a 100644 --- a/smp-angular/src/app/service-group-edit/service-group-metadata-dialog/service-group-metadata-dialog.component.ts +++ b/smp-angular/src/app/service-group-edit/service-group-metadata-dialog/service-group-metadata-dialog.component.ts @@ -1,14 +1,229 @@ -import {Component} from '@angular/core'; -import {MatDialogRef} from '@angular/material'; +import {Component, Inject, OnInit, ViewChild} from '@angular/core'; +import {MAT_DIALOG_DATA, MatDialog, MatDialogRef} from '@angular/material'; +import {FormBuilder, FormControl, FormGroup, Validators} from "@angular/forms"; +import {AlertService} from "../../alert/alert.service"; +import {SearchTableEntityStatus} from "../../common/search-table/search-table-entity-status.model"; +import {ServiceMetadataEditRo} from "../service-metadata-edit-ro.model"; +import {GlobalLookups} from "../../common/global-lookups"; +import {ServiceMetadataWizardDialogComponent} from "../service-metadata-wizard-dialog/service-metadata-wizard-dialog.component"; +import {ServiceGroupEditRo} from "../service-group-edit-ro.model"; +import {SmpConstants} from "../../smp.constants"; +import {Observable} from "rxjs/internal/Observable"; +import {HttpClient} from "@angular/common/http"; +import {ServiceGroupDomainEditRo} from "../service-group-domain-edit-ro.model"; +import {ServiceMetadataValidationEditRo} from "./service-metadata-validation-edit-ro.model"; @Component({ selector: 'app-messagelog-dialog', templateUrl: './service-group-metadata-dialog.component.html', styleUrls: ['./service-group-metadata-dialog.component.css'] }) -export class ServiceGroupMetadataDialogComponent { +export class ServiceGroupMetadataDialogComponent implements OnInit { - constructor(public dialogRef: MatDialogRef<ServiceGroupMetadataDialogComponent>) { + static readonly NEW_MODE = 'New ServiceMetadata'; + static readonly EDIT_MODE = 'Edit ServiceMetadata'; + + @ViewChild('domainList') domainList: any; + + + editMode: boolean; + formTitle: string; + current: ServiceMetadataEditRo & { confirmation?: string }; + currentServiceGroup: ServiceGroupEditRo; + dialogForm: FormGroup; + metadataValidationMessage: string; + xmlServiceMetadataObserver: Observable<ServiceMetadataEditRo>; + isMetadataValid: boolean = true; + + + constructor(public dialog: MatDialog, + protected http: HttpClient, + public lookups: GlobalLookups, + private dialogRef: MatDialogRef<ServiceGroupMetadataDialogComponent>, + private alertService: AlertService, + @Inject(MAT_DIALOG_DATA) public data: any, + private fb: FormBuilder) { + + this.editMode = data.edit; + this.formTitle = this.editMode ? ServiceGroupMetadataDialogComponent.EDIT_MODE : ServiceGroupMetadataDialogComponent.NEW_MODE; + this.currentServiceGroup = data.serviceGroup; + this.current = this.editMode + ? { + ...data.metadata, + } + : { + documentIdentifier: '', + documentIdentifierScheme: '', + smlSubdomain: this.currentServiceGroup.serviceGroupDomains[0].smlSubdomain, + domainCode: this.currentServiceGroup.serviceGroupDomains[0].domainCode, + domainId: null, + status: SearchTableEntityStatus.NEW, + xmlContentStatus: SearchTableEntityStatus.NEW, + }; + + this.dialogForm = fb.group({ + 'participantIdentifier': new FormControl({value: this.currentServiceGroup.participantIdentifier, disabled: true}), + 'participantScheme': new FormControl({value: this.currentServiceGroup.participantScheme, disabled: true}), + 'domainCode': new FormControl({}, [Validators.required]), + + 'documentIdentifier': new FormControl({value: this.current.documentIdentifier, disabled: this.editMode}, + [Validators.required]), + 'documentIdentifierScheme': new FormControl({ + value: this.current.documentIdentifierScheme, + disabled: this.editMode + }, + [Validators.required]), + 'xmlContent': new FormControl({value: []}, []), + }); + + // update values + this.dialogForm.controls['domainCode'].setValue(this.current.domainCode); + this.dialogForm.controls['xmlContent'].setValue(this.current.xmlContent); + } + + ngOnInit() { + + // retrieve xml extension for this service group + if (this.current.status !== SearchTableEntityStatus.NEW && !this.current.xmlContent) { + // init domains + this.xmlServiceMetadataObserver = this.http.get<ServiceMetadataEditRo>(SmpConstants.REST_METADATA + '/' + this.current.id); + this.xmlServiceMetadataObserver.subscribe((res: ServiceMetadataEditRo) => { + this.dialogForm.get('xmlContent').setValue(res.xmlContent); + }); + } + + // detect changes for updated values in mat-selection-list (check change detection operations) + // else the following error is thrown :xpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value: + // 'aria-selected: false'. Current value: 'aria-selected: true' + // + //this.changeDetector.detectChanges() + + } + + checkValidity(g: FormGroup) { + Object.keys(g.controls).forEach(key => { + g.get(key).markAsDirty(); + }); + Object.keys(g.controls).forEach(key => { + g.get(key).markAsTouched(); + }); + //!!! updateValueAndValidity - else some filed did no update current / on blur never happened + Object.keys(g.controls).forEach(key => { + g.get(key).updateValueAndValidity(); + }); + } + + submitForm() { + this.checkValidity(this.dialogForm); + + // before closing check the schema + let request: ServiceMetadataValidationEditRo = { + participantScheme: this.dialogForm.controls['participantScheme'].value, + participantIdentifier: this.dialogForm.controls['participantIdentifier'].value, + documentIdentifierScheme: this.dialogForm.controls['documentIdentifierScheme'].value, + documentIdentifier: this.dialogForm.controls['documentIdentifier'].value, + xmlContent: this.dialogForm.controls['xmlContent'].value, + } + // + let validationObservable = this.http.post<ServiceMetadataValidationEditRo>(SmpConstants.REST_METADATA_VALIDATE, request); + validationObservable.subscribe((res: ServiceMetadataValidationEditRo) => { + if (res.errorMessage) { + this.metadataValidationMessage = res.errorMessage; + this.isMetadataValid = false; + } else { + this.metadataValidationMessage = "ServiceMetada is valid!"; + this.isMetadataValid = true; + // now we can close the dialog + this.dialogRef.close(true); + } + + }); + } + + + onClearServiceMetadata() { + this.dialogForm.controls['xmlContent'].setValue(""); + } + + onStartWizardDialog() { + const formRef: MatDialogRef<any> = this.dialog.open(ServiceMetadataWizardDialogComponent); + formRef.afterClosed().subscribe(result => { + if (result) { + let existingXML = this.dialogForm.controls['extension'].value; + let val = (existingXML ? existingXML + '\n' : '') + formRef.componentInstance.getExtensionXML(); + this.dialogForm.controls['extension'].setValue(val); + } + }); + } + + onGenerateSimpleXML() { + let exampleXML = '<ServiceMetadata xmlns="http://docs.oasis-open.org/bdxr/ns/SMP/2016/05">' + + '\n <ServiceInformation>' + + '\n <ParticipantIdentifier scheme="' + this.dialogForm.controls['participantScheme'].value + '">' + this.dialogForm.controls['participantIdentifier'].value + '</ParticipantIdentifier>' + + '\n <DocumentIdentifier scheme="' + this.dialogForm.controls['documentIdentifierScheme'].value + '">' + this.dialogForm.controls['documentIdentifier'].value + '</DocumentIdentifier>' + + '\n <ProcessList>' + + '\n <Process>' + + '\n <ProcessIdentifier scheme="[enterProcessType]">[enterProcessName]</ProcessIdentifier>' + + '\n <ServiceEndpointList>' + + '\n <Endpoint transportProfile="bdxr-transport-ebms3-as4-v1p0">' + + '\n <EndpointURI>https://mypage.eu</EndpointURI>' + + '\n <RequireBusinessLevelSignature>true</RequireBusinessLevelSignature>' + + '\n <Certificate>UGFzdGUgYmFzZTY0IGVuY29kZWQgY2VydGlmaWNhdGUgb2YgQVA=</Certificate>' + + '\n <ServiceDescription>Service description for partners</ServiceDescription>' + + '\n <TechnicalContactUrl>www.best-page.eu</TechnicalContactUrl>' + + '\n </Endpoint>' + + '\n </ServiceEndpointList>' + + '\n </Process>' + + '\n </ProcessList>' + + '\n </ServiceInformation>' + + '\n</ServiceMetadata>'; + this.dialogForm.controls['xmlContent'].setValue(exampleXML); } + onServiceMetadataValidate() { + + let request: ServiceMetadataValidationEditRo = { + participantScheme: this.dialogForm.controls['participantScheme'].value, + participantIdentifier: this.dialogForm.controls['participantIdentifier'].value, + documentIdentifierScheme: this.dialogForm.controls['documentIdentifierScheme'].value, + documentIdentifier: this.dialogForm.controls['documentIdentifier'].value, + xmlContent: this.dialogForm.controls['xmlContent'].value, + } + // + let validationObservable = this.http.post<ServiceMetadataValidationEditRo>(SmpConstants.REST_METADATA_VALIDATE, request); + validationObservable.subscribe((res: ServiceMetadataValidationEditRo) => { + if (res.errorMessage) { + this.metadataValidationMessage = res.errorMessage; + this.isMetadataValid = false; + } else { + this.metadataValidationMessage = "ServiceMetada is valid!"; + this.isMetadataValid = true; + } + + }); + } + + public getCurrent(): ServiceMetadataEditRo { + + this.current.domainCode = this.domainList.selected.value.domainCode; + this.current.smlSubdomain = this.domainList.selected.value.smlSubdomain; + this.current.domainId = this.domainList.selected.value.domainId; + + this.current.xmlContent = this.dialogForm.value['xmlContent']; + // change this two properties only on new + if (this.current.status === SearchTableEntityStatus.NEW) { + this.current.documentIdentifier = this.dialogForm.value['documentIdentifier']; + this.current.documentIdentifierScheme = this.dialogForm.value['documentIdentifierScheme']; + } else if (this.current.status === SearchTableEntityStatus.PERSISTED) { + this.current.status = SearchTableEntityStatus.UPDATED; + this.current.xmlContentStatus = SearchTableEntityStatus.UPDATED; + } + return this.current; + } + + compareDomainCode(sgDomain: ServiceGroupDomainEditRo, domainCode: String): boolean { + return sgDomain.domainCode === domainCode; + } + + } diff --git a/smp-angular/src/app/service-group-edit/service-group-metadata-dialog/service-metadata-validation-edit-ro.model.ts b/smp-angular/src/app/service-group-edit/service-group-metadata-dialog/service-metadata-validation-edit-ro.model.ts new file mode 100644 index 0000000000000000000000000000000000000000..f6efa05dcca61d7f6f58d04562bc596309dc57e9 --- /dev/null +++ b/smp-angular/src/app/service-group-edit/service-group-metadata-dialog/service-metadata-validation-edit-ro.model.ts @@ -0,0 +1,10 @@ + +export interface ServiceMetadataValidationEditRo { + participantScheme: string; + participantIdentifier: string; + documentIdentifierScheme: string; + documentIdentifier: string; + + errorMessage?: string; + xmlContent?: string; +} diff --git a/smp-angular/src/app/service-group-edit/service-metadata-edit-ro.model.ts b/smp-angular/src/app/service-group-edit/service-metadata-edit-ro.model.ts index c6c2b94be733f336b61154a15ac86ed7aea0730f..50880525b0a09c6af8bfe8d7eceb9c311e95c0a8 100644 --- a/smp-angular/src/app/service-group-edit/service-metadata-edit-ro.model.ts +++ b/smp-angular/src/app/service-group-edit/service-metadata-edit-ro.model.ts @@ -1,9 +1,13 @@ import {SearchTableEntity} from "../common/search-table/search-table-entity.model"; -import {ServiceGroupDetailsDialogComponent} from "./service-group-details-dialog/service-group-details-dialog.component"; + export interface ServiceMetadataEditRo extends SearchTableEntity { documentIdentifier: string; - documentIdentifierScheme: string; + documentIdentifierScheme : string; smlSubdomain: string; domainCode: string; + domainId: null, + xmlContent?:string, + xmlContentStatus:number, + } diff --git a/smp-angular/src/app/service-group-edit/service-metadata-wizard-dialog/service-metadata-wizard-dialog.component.css b/smp-angular/src/app/service-group-edit/service-metadata-wizard-dialog/service-metadata-wizard-dialog.component.css new file mode 100644 index 0000000000000000000000000000000000000000..66c5204a6d4d3b0cee28761c51cfce5310bdb950 --- /dev/null +++ b/smp-angular/src/app/service-group-edit/service-metadata-wizard-dialog/service-metadata-wizard-dialog.component.css @@ -0,0 +1,8 @@ +#extensionTextArea { + border: none; + width: 610px; + height:340px; + -webkit-box-sizing: border-box; /* <=iOS4, <= Android 2.3 */ + -moz-box-sizing: border-box; /* FF1+ */ + box-sizing: border-box; /* Chrome, IE8, Opera, Safari 5.1*/ +} diff --git a/smp-angular/src/app/service-group-edit/service-metadata-wizard-dialog/service-metadata-wizard-dialog.component.html b/smp-angular/src/app/service-group-edit/service-metadata-wizard-dialog/service-metadata-wizard-dialog.component.html new file mode 100644 index 0000000000000000000000000000000000000000..e9fa2cd3a094ad3ad7366b43412adaac594686f8 --- /dev/null +++ b/smp-angular/src/app/service-group-edit/service-metadata-wizard-dialog/service-metadata-wizard-dialog.component.html @@ -0,0 +1,119 @@ +<h2 mat-dialog-title>ServiceMetadataWizard</h2> + +<mat-dialog-content> + <form [formGroup]="dialogForm"> + <mat-card> + <mat-card-content> + <div class="panel"> + <!-- Participant --> + <mat-form-field style="width:35%"> + <input matInput placeholder="Participant identifier schema" name="participantIdentifierScheme" + id="participantIdentifierScheme_id" maxlength="255"> + </mat-form-field> + <mat-form-field style="width:60%"> + <input matInput placeholder="Participant identifier" name="ParticipantIdentifier" id="ParticipantIdentifier_id" + maxlength="255" required> + <div + *ngIf="(!editMode && dialogForm.controls['documentIdentifier'].touched || editMode) && dialogForm.controls['documentIdentifier'].hasError('required')" + style="color:red; font-size: 70%"> + Document identifier must not be empty. + </div> + </mat-form-field> + <!-- Document --> + <mat-form-field style="width:35%"> + <input matInput placeholder="Document identifier schema" name="documentIdentifierScheme" + id="documentIdentifierScheme_id" + maxlength="255"> + </mat-form-field> + + <mat-form-field style="width:60%"> + <input matInput placeholder="Document identifier" name="documentIdentifier" id="documentIdentifier_id" + maxlength="255" required> + </mat-form-field> + <!-- Process --> + <mat-form-field style="width:35%"> + <input matInput placeholder="Process schema" name="processSchema" id="processSchema_id" + maxlength="255" required> + </mat-form-field> + + <mat-form-field style="width:60%"> + <input matInput placeholder="Process identifier" name="processidentifier" id="processidentifier_id" + maxlength="255" required> + + </mat-form-field> +<!-- transport --> + <mat-form-field style="width:35%"> + <input matInput placeholder="Transport profile" name="transportProfile" id="transportProfiler_id" + maxlength="255" required> + </mat-form-field> + + <mat-form-field style="width:60%"> + <input matInput placeholder="Endpoint Url" name="endpointUrl" id="endpointUrl_id" + maxlength="255" required> + </mat-form-field> + + <div style="display: block;" style="border:1px; solid: #999999;margin:5px 0; padding:3px;"> + <input + type="file" (change)="onFileChanged($event)" + #fileInput> + <button (click)="onUpload()">Upload!</button> + <div class="textwrapper"> + <textarea matInput style="width:100%;border: #03A9F4 1px solid" cols="2" rows="10" + resizeable="false" + id="metadatacertificate_id" + placeholder="X509Certificate" name="certificate" + + ></textarea> + </div> + <div + *ngIf="certificateValidationMessage" + [style.color]="isCertificateValid?'green':'red'"> + {{certificateValidationMessage}} + </div> + + </div> + + + <!-- --> + + + <!-- mat-form-field style="width:47%"> + <input matInput placeholder="ServiceActivationDate" name="serviceActivationDate" id="serviceActivationDate_id" + [formControl]="dialogForm.controls['serviceActivationDate']" maxlength="255" required> + </mat-form-field> + <mat-form-field style="width:47%"> + <input matInput placeholder="ServiceExpirationDate" name="serviceExpirationDate" id="serviceExpirationDate" + [formControl]="dialogForm.controls['serviceExpirationDate']" maxlength="255" required> + </mat-form-field --> + +<!-- ServiceDescription --> + <mat-form-field style="width:100%"> + <input matInput placeholder="Service description" name="serviceDescription" id="serviceDescription_id" + [formControl]="dialogForm.controls['serviceDescription']" maxlength="255" required> + </mat-form-field> + <mat-form-field style="width:100%"> + <input matInput placeholder="Technical Contact Url" name="technicalContactUrl" id="technicalContactUrl_id" + [formControl]="dialogForm.controls['technicalContactUrl_id']" maxlength="255" required> + </mat-form-field> + + + </div> + </mat-card-content> + </mat-card> + </form> +</mat-dialog-content> + +<mat-dialog-actions> + <div class="group-action-button"> + <button mat-raised-button color="primary" [mat-dialog-close]="true" + [disabled]="!dialogForm.valid"> + <mat-icon>check_circle</mat-icon> + <span>OK</span> + </button> + + <button mat-raised-button color="primary" mat-dialog-close> + <mat-icon>cancel</mat-icon> + <span>Cancel</span> + </button> + </div> +</mat-dialog-actions> diff --git a/smp-angular/src/app/service-group-edit/service-metadata-wizard-dialog/service-metadata-wizard-dialog.component.ts b/smp-angular/src/app/service-group-edit/service-metadata-wizard-dialog/service-metadata-wizard-dialog.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..9e9dfecc877572ee8bfd0220dcf55ee68fd63761 --- /dev/null +++ b/smp-angular/src/app/service-group-edit/service-metadata-wizard-dialog/service-metadata-wizard-dialog.component.ts @@ -0,0 +1,84 @@ +import {Component} from '@angular/core'; +import {MatDialogRef} from '@angular/material'; +import {FormBuilder, FormControl, FormGroup, Validators} from "@angular/forms"; +import {HttpClient} from "@angular/common/http"; +import {SmpConstants} from "../../smp.constants"; +import {ServiceMetadataEditRo} from "../service-metadata-edit-ro.model"; + + +@Component({ + selector: 'service-metadata-wizard-dialog', + templateUrl: './service-metadata-wizard-dialog.component.html', + styleUrls: ['./service-metadata-wizard-dialog.component.css'] +}) +export class ServiceMetadataWizardDialogComponent { + + static readonly NEW_MODE = 'New ServiceMetadata XML'; + static readonly EDIT_MODE = 'Edit ServiceMetadata XML'; + + editMode: boolean; + formTitle: string; + current: ServiceMetadataEditRo & { confirmation?: string }; + dialogForm: FormGroup; + certificateValidationMessage: string; + isCertificateValid: string; + selectedFile: File; + + dummyXML: string = "<!-- Custom element is mandatory by OASIS SMP schema.\n Replace following element with your XML structure. -->\n<ext:example xmlns:ext=\"http://my.namespace.eu\">my mandatory content</ext:example>" + + + constructor( protected http: HttpClient, + public dialogRef: MatDialogRef<ServiceMetadataWizardDialogComponent>, + private dialogFormBuilder: FormBuilder) { + + this.dialogForm = dialogFormBuilder.group({ + + 'documentIdentifier': new FormControl({value: ''}, [Validators.required]), + 'documentIdentifierScheme': new FormControl({value: ''}, null), + 'processSchema': new FormControl({value: ''}, [Validators.required]), + 'processIdentifier': new FormControl({value: ''}, [Validators.required]), + 'endpointUrl': new FormControl({value: ''}, [Validators.required]), + 'endpointCertificate': new FormControl({value: ''}, null), + + }); + } + + onFileChanged(event) { + this.selectedFile = event.target.files[0] + } + + onUpload() { + // this.http is the injected HttpClient + this.http.post(SmpConstants.REST_CERTIFICATE, this.selectedFile) + .subscribe(event => { + + console.log(event); // handle event here + }); + } + + + getExtensionXML() { + /* + var xmlString = '<Extension xmlns="http://docs.oasis-open.org/bdxr/ns/SMP/2016/05">' + let arrayLength = this.elements.length; + for (var i = 0; i < arrayLength; i++) { + let str = this.dialogForm.get(this.elements[i].name).value; + if (str && 0 !== str.length) { + xmlString = xmlString + '\n <'+this.elements[i].name+'>' + this.xmlSpecialChars(str) + '</'+this.elements[i].name+'>'; + } + } + xmlString = xmlString+ '\n' +this.dummyXML+ '\n</Extension>' + + return xmlString;*/ + } + + xmlSpecialChars(unsafe) { + return unsafe + .replace(/&/g, "&") + .replace(/</g, "<") + .replace(/>/g, ">") + .replace(/"/g, """); + } + + +} diff --git a/smp-angular/src/app/service-group-edit/service-metadata-wizard-dialog/service-metadata-wizard-edit-ro.model.ts b/smp-angular/src/app/service-group-edit/service-metadata-wizard-dialog/service-metadata-wizard-edit-ro.model.ts new file mode 100644 index 0000000000000000000000000000000000000000..abc19b6b0a24f20ce30a1eb89f9daf4716174ff4 --- /dev/null +++ b/smp-angular/src/app/service-group-edit/service-metadata-wizard-dialog/service-metadata-wizard-edit-ro.model.ts @@ -0,0 +1,16 @@ + +export interface ServiceMetadataWizardRo { + participantIdentifierScheme: string; + participantIdentifier: string; + documentIdentifierScheme: string; + documentIdentifier: string; + processSchema: string; + processIdentifier: string; + transportProfile: string; + endpointUrl: string; + certificate: string; + serviceDescription: string; + technicalContactUrl: string; + contentXML: string + errorMessage: string +} diff --git a/smp-angular/src/app/service-group-search/service-group-search-controller.ts b/smp-angular/src/app/service-group-search/service-group-search-controller.ts index 28c1698b66eeffa59163d88512b243bde5630a89..2432c65feea3ce49fdea13bcc6f05d58a9286e3c 100644 --- a/smp-angular/src/app/service-group-search/service-group-search-controller.ts +++ b/smp-angular/src/app/service-group-search/service-group-search-controller.ts @@ -1,5 +1,5 @@ import {SearchTableController} from '../common/search-table/search-table-controller'; -import {MatDialog, MatDialogConfig, MatDialogRef} from '@angular/material'; +import {MatDialog, MatDialogConfig} from '@angular/material'; import {ServiceGroupSearchRo} from './service-group-search-ro.model'; export class ServiceGroupSearchController implements SearchTableController { @@ -23,4 +23,6 @@ export class ServiceGroupSearchController implements SearchTableController { public newRow(): ServiceGroupSearchRo { return null; } + + public dataSaved() {} } diff --git a/smp-angular/src/app/service-group-search/service-group-search.component.css b/smp-angular/src/app/service-group-search/service-group-search.component.css index d1a612141160e70f00bfe5aee114e010052ee4a5..65c9eab51048787f6d1098fc3c1ecd438a6e86c4 100644 --- a/smp-angular/src/app/service-group-search/service-group-search.component.css +++ b/smp-angular/src/app/service-group-search/service-group-search.component.css @@ -5,8 +5,8 @@ /* --- Button ---*/ .group-btn { - margin-top:20px; - } + margin-top:20px; +} #hiddenButtonId { position: fixed; @@ -16,3 +16,19 @@ .datatable-body{ overflow-y: scroll; } + + +/deep/ .inner-table { + margin-left: 100px; + width: 80%; +} + +/deep/ .inner-table .datatable-body-cell { + padding: 1.5em 2px !important; + +} + +/deep/ .inner-table .mat-icon-button { + height: auto !important;; + line-height: unset !important; +} diff --git a/smp-angular/src/app/service-group-search/service-group-search.component.html b/smp-angular/src/app/service-group-search/service-group-search.component.html index ab20e7b28e73e24901b81a68d1a17ed255004cb6..5e0797c2ee9ae0099efada72f55b3e3005263bd8 100644 --- a/smp-angular/src/app/service-group-search/service-group-search.component.html +++ b/smp-angular/src/app/service-group-search/service-group-search.component.html @@ -11,8 +11,6 @@ [showActionButtons]="false" [showIndexColumn]="true" > - - <ng-template #rowExtensionAction let-row="row" let-value="value" ngx-datatable-cell-template> <button mat-button color="primary" @@ -25,8 +23,7 @@ <ng-template #rowSMPUrlLinkAction let-row="row" let-value="value" ngx-datatable-cell-template> <a target="_blank" - href="{{contextPath}}{{row.participantScheme}}::{{row.participantIdentifier}}" >Open URL</a> - + href="{{contextPath}}{{row.participantScheme}}::{{row.participantIdentifier}}">Open URL</a> </ng-template> @@ -40,25 +37,17 @@ <input matInput placeholder="Participant scheme" name="patricipantScheme" [(ngModel)]="filter.participantScheme" #messageId="ngModel" id="participantScheme"> </mat-form-field> - <mat-select placeholder="Domain " [(ngModel)]="filter.domain" name="domain" + <mat-select placeholder="All Domains" [(ngModel)]="filter.domain" name="domain" id="domain_id"> - <mat-option [value]="''"> - </mat-option> - <mat-option *ngFor="let domain of domainlist" [value]="domain.smlSubdomain"> - {{domain.smlSubdomain}} + <mat-option [value]="''">All Domains</mat-option> + <mat-option *ngFor="let domain of lookups.cachedDomainList" [value]="domain.domainCode"> + {{domain.domainCode}} ({{domain.smlSubdomain}}) </mat-option> </mat-select> </ng-template> - - <ng-template #additionalToolButtons> - <!-- button mat-raised-button color="primary" - id="extensionbutton_id"> - <mat-icon>code</mat-icon> - <span>Extension</span> - </button --> </ng-template> <ng-template #tableRowDetailContainer let-row="row"> @@ -66,24 +55,28 @@ <div *ngIf="row.serviceMetadata.length===0" style="padding-left:20px;"> No service metadata </div> - <div *ngIf="row.serviceMetadata.length !== 0" > + <div *ngIf="row.serviceMetadata.length !== 0"> <ngx-datatable class='material striped' style="width: 80%" [loadingIndicator]="loading" [rows]='row.serviceMetadata' - [columnMode]='"force"' + [columnMode]='"force"' [headerHeight]='50' [footerHeight]='50' [rowHeight]='"auto"'> - <ngx-datatable-column prop="domainCode" name="Domain" maxWidth="250" ></ngx-datatable-column> - <ngx-datatable-column prop="documentIdentifierScheme" name="Document identifier scheme" maxWidth="350" ></ngx-datatable-column> - <ngx-datatable-column prop="documentIdentifier" name="Document identifier" maxWidth="250" ></ngx-datatable-column> - <ngx-datatable-column [cellTemplate]="rowMetadataSMPUrlLinkAction" name="URL" maxWidth="250" ></ngx-datatable-column> + <ngx-datatable-column prop="domainCode" name="Domain" maxWidth="250"></ngx-datatable-column> + <ngx-datatable-column prop="documentIdentifierScheme" name="Document identifier scheme" + maxWidth="350"></ngx-datatable-column> + <ngx-datatable-column prop="documentIdentifier" name="Document identifier" + maxWidth="250"></ngx-datatable-column> + <ngx-datatable-column [cellTemplate]="rowMetadataSMPUrlLinkAction" name="URL" + maxWidth="250"></ngx-datatable-column> <ng-template #rowMetadataSMPUrlLinkAction let-rowSmd="row" ngx-datatable-cell-template> <a target="_blank" - href="{{contextPath}}{{row.participantScheme}}::{{row.participantIdentifier}}/services/{{rowSmd.documentIdentifierScheme}}::{{rowSmd.documentIdentifier}}" >Open URL</a> + href="{{contextPath}}{{row.participantScheme}}::{{row.participantIdentifier}}/services/{{rowSmd.documentIdentifierScheme}}::{{rowSmd.documentIdentifier}}">Open + URL</a> </ng-template> </ngx-datatable> diff --git a/smp-angular/src/app/service-group-search/service-group-search.component.ts b/smp-angular/src/app/service-group-search/service-group-search.component.ts index 04d95e9f0e346102347092d7260c49151b7cab46..c626c00bce684a63c4cedea3c2dfd3f21d9b0d85 100644 --- a/smp-angular/src/app/service-group-search/service-group-search.component.ts +++ b/smp-angular/src/app/service-group-search/service-group-search.component.ts @@ -1,18 +1,16 @@ ///<reference path="../smp.constants.ts"/> import {Component, OnInit, TemplateRef, ViewChild} from '@angular/core'; import {ColumnPicker} from '../common/column-picker/column-picker.model'; -import {MatDialog, MatDialogRef} from '@angular/material'; +import {MatDialog} from '@angular/material'; import {AlertService} from '../alert/alert.service'; import {ServiceGroupSearchController} from './service-group-search-controller'; import {HttpClient} from '@angular/common/http'; -import {Observable} from "rxjs/index"; -import {SearchTableResult} from "../common/search-table/search-table-result.model"; -import {DomainRo} from "../domain/domain-ro.model"; import {SmpConstants} from "../smp.constants"; +import {GlobalLookups} from "../common/global-lookups"; @Component({ moduleId: module.id, - templateUrl:'./service-group-search.component.html', + templateUrl: './service-group-search.component.html', styleUrls: ['./service-group-search.component.css'] }) export class ServiceGroupSearchComponent implements OnInit { @@ -24,21 +22,11 @@ export class ServiceGroupSearchComponent implements OnInit { columnPicker: ColumnPicker = new ColumnPicker(); serviceGroupSearchController: ServiceGroupSearchController; filter: any = {}; - domainlist: Array<any>; - domainObserver: Observable< SearchTableResult> ; - contextPath: string = location.pathname.substring(0,location.pathname.length -3); // remove /ui s + contextPath: string = location.pathname.substring(0, location.pathname.length - 3); // remove /ui s baseUrl: string = SmpConstants.REST_SEARCH; - constructor(protected http: HttpClient, protected alertService: AlertService, public dialog: MatDialog) { - this.domainObserver = this.http.get<SearchTableResult>(SmpConstants.REST_DOMAIN); - this.domainObserver.subscribe((domains: SearchTableResult) => { - this.domainlist = new Array(domains.serviceEntities.length) - .map((v, index) => domains.serviceEntities[index] as DomainRo); + constructor(protected lookups: GlobalLookups, protected http: HttpClient, protected alertService: AlertService, public dialog: MatDialog) { - this.domainlist = domains.serviceEntities.map(serviceEntity => { - return {...serviceEntity} - }); - }); } ngOnDestroy() { @@ -46,6 +34,7 @@ export class ServiceGroupSearchComponent implements OnInit { } ngOnInit() { + this.serviceGroupSearchController = new ServiceGroupSearchController(this.dialog); this.columnPicker.allColumns = [ @@ -71,18 +60,11 @@ export class ServiceGroupSearchComponent implements OnInit { maxWidth: 250, sortable: false }, - /* - { - cellTemplate: this.rowExtensionAction, - name: 'Extension', - width: 80, - sortable: false - }*/ ]; this.columnPicker.selectedColumns = this.columnPicker.allColumns.filter(col => { - return ["Metadata size", "Participant scheme", "Participant identifier","OASIS ServiceGroup URL"].indexOf(col.name) != -1 + return ["Metadata size", "Participant scheme", "Participant identifier", "OASIS ServiceGroup URL"].indexOf(col.name) != -1 }); } diff --git a/smp-angular/src/app/smp.constants.ts b/smp-angular/src/app/smp.constants.ts index 1a036d8d545cdcb3b1091505ce2f2b64e83ff08a..b43f00990272fc932ebe02e0c6e65d2b05fc2a77 100644 --- a/smp-angular/src/app/smp.constants.ts +++ b/smp-angular/src/app/smp.constants.ts @@ -4,6 +4,16 @@ export class SmpConstants { public static readonly REST_USER = 'rest/user'; public static readonly REST_SEARCH = 'rest/search'; public static readonly REST_EDIT = 'rest/servicegroup'; + public static readonly REST_METADATA = 'rest/servicemetadata'; + public static readonly REST_SECURITY_AUTHENTICATION = 'rest/security/authentication'; + public static readonly REST_SECURITY_USER = 'rest/security/user'; + public static readonly REST_APPLICATION = 'rest/application/info'; + + public static readonly REST_CERTIFICATE = `${SmpConstants.REST_USER}/certdata`; + public static readonly REST_SERVICE_GROUP_EXTENSION = `${SmpConstants.REST_EDIT}/extension`; + public static readonly REST_SERVICE_GROUP_EXTENSION_VALIDATE = `${SmpConstants.REST_SERVICE_GROUP_EXTENSION}/validate`; + public static readonly REST_SERVICE_GROUP_EXTENSION_FORMAT = `${SmpConstants.REST_SERVICE_GROUP_EXTENSION}/format`; + public static readonly REST_METADATA_VALIDATE = `${SmpConstants.REST_METADATA}/validate`; } diff --git a/smp-angular/src/app/user/user-controller.ts b/smp-angular/src/app/user/user-controller.ts index 448282595e98a5219c33d8118e20ddd60d3e3dee..e144f0682294b09f1f170771dbdf52743a62370e 100644 --- a/smp-angular/src/app/user/user-controller.ts +++ b/smp-angular/src/app/user/user-controller.ts @@ -28,9 +28,14 @@ export class UserController implements SearchTableController { id: null, index: null, username: '', - email: '', + emailAddress: '', role: '', - status: SearchTableEntityStatus.NEW + active: true, + status: SearchTableEntityStatus.NEW, + statusPassword: SearchTableEntityStatus.NEW } } + + public dataSaved() { + } } diff --git a/smp-angular/src/app/user/user-details-dialog/user-details-dialog.component.html b/smp-angular/src/app/user/user-details-dialog/user-details-dialog.component.html index cafe4764692f6f7a4b79ea22205dfbf0b4fcd77b..55dc6f5558e101f305ec28838ad8495ab6fe7205 100644 --- a/smp-angular/src/app/user/user-details-dialog/user-details-dialog.component.html +++ b/smp-angular/src/app/user/user-details-dialog/user-details-dialog.component.html @@ -1,82 +1,147 @@ +<mat-dialog-content fxFlex="column"> <h2 mat-dialog-title>{{formTitle}}</h2> -<mat-dialog-content> + + <mat-card> - <mat-card-content> - <mat-slide-toggle mat-no-ink class="mat-primary" [formControl]="userForm.controls['userToggle']" (change)="onUserToggleChanged($event)"> - User Details + <mat-card-content > + <mat-slide-toggle style="width:100%" + mat-no-ink class="mat-primary" [formControl]="userForm.controls['active']" + (change)="onUserToggleChanged($event)" id="active_id"> + Active </mat-slide-toggle> - <div *ngIf="userForm.errors?.userDetailsOrCertificateRequired && (userForm.get('userToggle').dirty || userForm.get('certificateToggle').dirty)" class="has-error">You need to enter at least the details or the certificate for this user</div> - <div class="panel"> - <mat-form-field class="username"> - <input matInput placeholder="Username" [formControl]="userForm.controls['username']" maxlength="255" required> - <div *ngIf="userForm.controls['username'].hasError('required') && userForm.controls['username'].touched" class="has-error">You should type an username</div> - </mat-form-field> + <mat-form-field style="width:100%"> + <mat-select matInput placeholder="Role" class="role" [formControl]="userForm.controls['role']" + id="role_id" required> + <mat-option *ngFor="let item of existingRoles" [value]="item">{{item}}</mat-option> + </mat-select> + <div *ngIf="userForm.controls['role'].hasError('required') && userForm.controls['role'].touched" + class="has-error">You need to choose at least one role for this user + </div> + </mat-form-field> + <mat-form-field class="emailAddress" style="width:100%"> + <input matInput placeholder="Email address" name="emailAddress" [formControl]="userForm.controls['emailAddress']" + id="emailAddress_id" maxlength="255"> + </mat-form-field> - <mat-form-field class="role"> - <mat-select matInput placeholder="Role" class="role" [formControl]="userForm.controls['role']" required> - <mat-option *ngFor="let item of existingRoles" [value]="item">{{item}}</mat-option> - </mat-select> - <div *ngIf="userForm.controls['role'].hasError('required') && userForm.controls['role'].touched" class="has-error">You need to choose at least one role for this user</div> - </mat-form-field> + </mat-card-content> + </mat-card> + <div fxLayout="row"> + <mat-card fxFlex="40"> + <mat-card-title> + <mat-slide-toggle mat-no-ink class="mat-primary" [formControl]="userForm.controls['userToggle']" + (change)="onUserToggleChanged($event)" id="userDetailsToggle_id"> + User/password authentication + </mat-slide-toggle> + <div + *ngIf="userForm.errors?.userDetailsOrCertificateRequired && (userForm.get('userToggle').dirty || userForm.get('certificateToggle').dirty)" + class="has-error">You need to enter at least the details or the certificate for this user + </div> + </mat-card-title> - <mat-form-field class="password"> - <input matInput placeholder="Password" type="password" [formControl]="userForm.controls['password']" [pattern]="passwordPattern" [required]="!editMode"> - <div *ngIf="!editMode && userForm.controls['password'].hasError('required') && userForm.controls['password'].touched" class="has-error">You should type a password</div> - <div *ngIf="userForm.controls['password'].dirty && userForm.controls['password'].hasError('pattern') && userForm.controls['password'].touched" class="has-error"> - Password should follow all of these rules:<br> - - Minimum length: 8 characters<br> - - Maximum length: 32 characters<br> - - At least one letter in lowercase<br> - - At least one letter in uppercase<br> - - At least one digit<br> - - At least one special character - </div> - </mat-form-field> + <mat-card-content> + <div class="panel" style="height: 100%"> + <mat-form-field class="username"> + <input matInput placeholder="Username" [formControl]="userForm.controls['username']" + id="username_id" maxlength="255" required> + <div *ngIf="userForm.controls['username'].hasError('required') && userForm.controls['username'].touched" + class="has-error">You should type an username</div> + </mat-form-field> - <mat-form-field class="password-confirmation"> - <input matInput placeholder="Confirmation" type="password" [formControl]="userForm.controls['confirmation']" [required]="!editMode"> - <div *ngIf="!editMode && userForm.controls['confirmation'].hasError('required') && userForm.controls['confirmation'].touched" class="has-error">You should type a password</div> - <div *ngIf="userForm.errors?.confirmationMatch && userForm.controls['confirmation'].touched" class="has-error">Passwords do not match</div> - </mat-form-field> - </div> + <mat-slide-toggle *ngIf="editMode" mat-no-ink class="mat-primary" [formControl]="userForm.controls['passwordToggle']" + (change)="onPasswordToggleChanged($event)" id="upasswordToggle_id"> + Change password + </mat-slide-toggle> - <mat-slide-toggle mat-no-ink class="mat-primary" [formControl]="userForm.controls['certificateToggle']"> - Certificate - </mat-slide-toggle> - <div *ngIf="userForm.errors?.userDetailsOrCertificateRequired && (userForm.get('userToggle').dirty || userForm.get('certificateToggle').dirty)" class="has-error">You need to enter at least the details or the certificate for this user</div> - <div *ngIf="userForm.errors?.certificateDetailsRequired && userForm.get('certificateToggle').touched" class="has-error">All the certificate fields are required so please upload a new certificate</div> + <mat-form-field class="password"> + <input matInput placeholder="Password" type="password" [formControl]="userForm.controls['password']" + [pattern]="passwordPattern" + id="password_id" [required]="!editMode"> + <div + *ngIf="!editMode && userForm.controls['password'].hasError('required') && userForm.controls['password'].touched" + class="has-error">You should type a password</div> + <div + *ngIf="userForm.controls['password'].dirty && userForm.controls['password'].hasError('pattern') && userForm.controls['password'].touched" + class="has-error"> + Password should follow all of these rules:<br> + - Minimum length: 8 characters<br> + - Maximum length: 32 characters<br> + - At least one letter in lowercase<br> + - At least one letter in uppercase<br> + - At least one digit<br> + - At least one special character + </div> + </mat-form-field> - <div class="panel"> - <mat-form-field class="certificate-subject"> - <input matInput placeholder="Subject Name" [formControl]="userForm.controls['subject']"> - </mat-form-field> - <mat-form-field class="certificate-valid-from"> - <input matInput placeholder="Valid From" [formControl]="userForm.controls['validFrom']"> - </mat-form-field> - <mat-form-field class="certificate-valid-to"> - <input matInput placeholder="Valid To" [formControl]="userForm.controls['validTo']"> - </mat-form-field> - <mat-form-field class="certificate-issuer"> - <input matInput placeholder="Issuer" [formControl]="userForm.controls['issuer']"> - </mat-form-field> - <mat-form-field class="certificate-serial-number"> - <input matInput placeholder="Serial Number" [formControl]="userForm.controls['serialNumber']"> - </mat-form-field> + <mat-form-field class="password-confirmation"> + <input matInput placeholder="Confirmation" type="password" [formControl]="userForm.controls['confirmation']" + id="usernameconfirmation_id" [required]="!editMode"> + <div + *ngIf="!editMode && userForm.controls['confirmation'].hasError('required') && userForm.controls['confirmation'].touched" + class="has-error">You should type a password + </div> + <div *ngIf="userForm.errors?.confirmationMatch && userForm.controls['confirmation'].touched" + class="has-error">Passwords do not match + </div> + </mat-form-field> + </div> + </mat-card-content> + </mat-card> + <mat-card fxFlex="60"> + <mat-card-title> + <mat-slide-toggle mat-no-ink class="mat-primary" [formControl]="userForm.controls['certificateToggle']" + id="certificateToggle_id"> + Certificate authentication + </mat-slide-toggle> + <div + *ngIf="userForm.errors?.userDetailsOrCertificateRequired && (userForm.get('userToggle').dirty || userForm.get('certificateToggle').dirty)" + class="has-error">You need to enter at least the details or the certificate for this user + </div> + <div *ngIf="userForm.errors?.certificateDetailsRequired && userForm.get('certificateToggle').touched" + class="has-error">All the certificate fields are required so please upload a new certificate + </div> + </mat-card-title> + <mat-card-content> + <div class="panel"> + <mat-form-field class="certificate-subject"> + <input matInput placeholder="Subject Name" [formControl]="userForm.controls['subject']" id="subject_id"> + </mat-form-field> + <mat-form-field class="certificate-valid-from"> + <input matInput placeholder="Valid From" [formControl]="userForm.controls['validFrom']" id="validFrom_id"> + </mat-form-field> + <mat-form-field class="certificate-valid-to"> + <input matInput placeholder="Valid To" [formControl]="userForm.controls['validTo']" id="validTo_id"> + </mat-form-field> + <mat-form-field class="certificate-issuer"> + <input matInput placeholder="Issuer" [formControl]="userForm.controls['issuer']" id="issuer_id"> + </mat-form-field> + <mat-form-field class="certificate-serial-number"> + <input matInput placeholder="Serial Number" [formControl]="userForm.controls['serialNumber']" + id="servialNumber_id"> + </mat-form-field> + <mat-form-field style="width: 100%;"> + <input matInput placeholder="SMP certificate ID" [formControl]="userForm.controls['certificateId']" + id="certificateId_id"> + </mat-form-field> + + <label class="custom-file-upload"> + <input #fileInput type="file" id="custom-file-upload" accept=".cer,.crt,.pem,.der" + (change)="uploadCertificate($event)" [disabled]="!userForm.controls['certificateToggle']?.value"> + <button mat-flat-button color="primary" (click)="fileInput.click()" + [disabled]="!userForm.controls['certificateToggle']?.value">Import + </button> + </label> + </div> + </mat-card-content> + </mat-card> + </div> - <label class="custom-file-upload"> - <input #fileInput type="file" id="custom-file-upload" accept=".cer" (change)="uploadCertificate($event)" [disabled]="!userForm.controls['certificateToggle']?.value"> - <button mat-flat-button color="primary" (click)="fileInput.click()" [disabled]="!userForm.controls['certificateToggle']?.value">Import</button> - </label> - </div> - </mat-card-content> - </mat-card> -</mat-dialog-content> <table class="buttonsRow"> <tr> <td> - <button mat-raised-button color="primary" [mat-dialog-close]="true" (click)="submitForm()" [disabled]="!userForm.valid"> + <button mat-raised-button color="primary" [mat-dialog-close]="true" (click)="submitForm()" + [disabled]="!userForm.valid"> <mat-icon>check_circle</mat-icon> <span>OK</span> </button> @@ -88,4 +153,4 @@ </tr> </table> <div class="required-fields">* required fields</div> - +</mat-dialog-content> diff --git a/smp-angular/src/app/user/user-details-dialog/user-details-dialog.component.ts b/smp-angular/src/app/user/user-details-dialog/user-details-dialog.component.ts index 9f8c0b64d35853ca961e761e1e2c4c3e666a6b63..d2f7756ec56624348749a579d926b621bb3f3ed3 100644 --- a/smp-angular/src/app/user/user-details-dialog/user-details-dialog.component.ts +++ b/smp-angular/src/app/user/user-details-dialog/user-details-dialog.component.ts @@ -20,17 +20,16 @@ export class UserDetailsDialogComponent { static readonly NEW_MODE = 'New User'; static readonly EDIT_MODE = 'User Edit'; - // readonly emailPattern = '[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}'; + readonly emailPattern = '[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}'; readonly passwordPattern = '^(?=.*[A-Z])(?=.*[ !#$%&\'()*+,-./:;<=>?@\\[^_`{|}~\\\]"])(?=.*[0-9])(?=.*[a-z]).{8,32}$'; readonly dateFormat: string = 'yyyy-MM-dd HH:mm:ssZ'; - id: number; - editMode: boolean; formTitle: string; userRoles = []; existingRoles = []; userForm: FormGroup; + current: UserRo; @ViewChild('fileInput') private fileInput; @@ -67,11 +66,9 @@ export class UserDetailsDialogComponent { @Inject(MAT_DIALOG_DATA) public data: any, private fb: FormBuilder) { this.editMode = data.edit; - this.id = data.row && data.row.id; - - this.formTitle = this.editMode ? UserDetailsDialogComponent.EDIT_MODE: UserDetailsDialogComponent.NEW_MODE; + this.formTitle = this.editMode ? UserDetailsDialogComponent.EDIT_MODE: UserDetailsDialogComponent.NEW_MODE; - const user: UserRo & { confirmation?: string } = this.editMode + this.current = this.editMode ? { ...data.row, password: '', // ensures the user password is cleared before editing @@ -82,41 +79,54 @@ export class UserDetailsDialogComponent { validTo: data.row.validTo, issuer: data.row.issuer, serialNumber: data.row.serialNumber, - }, + } }: { + active: true, username: '', - email: '', + emailAddress: '', password: '', confirmation: '', role: '', + status: SearchTableEntityStatus.NEW, + statusPassword: SearchTableEntityStatus.NEW, certificate: {}, }; - const userDetailsToggled: boolean = user && !!user.username; + const userDetailsToggled: boolean = this.current && !!this.current.username; + const passwordToggle: boolean = !this.editMode; + this.userForm = fb.group({ + // common values + 'active': new FormControl({ value: this.current.active},[]), + 'emailAddress': new FormControl({ value:this.current.emailAddress },[ Validators.pattern(this.emailPattern)]), + 'role': new FormControl({ value: this.current.role }, Validators.required), + // username/password authentication 'userToggle': new FormControl(userDetailsToggled), - 'username': new FormControl({ value: user.username, disabled: this.editMode || !userDetailsToggled }, this.editMode ? Validators.nullValidator : null), - 'role': new FormControl({ value: Role[user.role], disabled: !userDetailsToggled }, Validators.required), - 'password': new FormControl({ value: user.password, disabled: !userDetailsToggled }, [Validators.required, Validators.pattern(this.passwordPattern)]), - 'confirmation': new FormControl({ value: user.password, disabled: !userDetailsToggled }, Validators.pattern(this.passwordPattern)), - - 'certificateToggle': new FormControl(user && user.certificate && !!user.certificate.subject), - 'subject': new FormControl({ value: user.certificate.subject, disabled: true }, Validators.required), - 'validFrom': new FormControl({ value: user.certificate.validFrom, disabled: true }, Validators.required), - 'validTo': new FormControl({ value: user.certificate.validTo, disabled: true }, Validators.required), - 'issuer': new FormControl({ value: user.certificate.issuer, disabled: true }, Validators.required), - 'serialNumber': new FormControl({ value: user.certificate.serialNumber, disabled: true }, Validators.required), + 'passwordToggle': new FormControl(passwordToggle), + 'username': new FormControl({ value: this.current.username, disabled: this.editMode || !userDetailsToggled }, this.editMode ? Validators.nullValidator : null), + 'password': new FormControl({ value: this.current.password, disabled: !userDetailsToggled && !passwordToggle}, + [Validators.required, Validators.pattern(this.passwordPattern)]), + 'confirmation': new FormControl({ value: this.current.password, disabled: !userDetailsToggled && !passwordToggle}, + Validators.pattern(this.passwordPattern)), + // certificate authentication + 'certificateToggle': new FormControl(this.current && this.current.certificate && !!this.current.certificate.subject), + 'subject': new FormControl({ value: this.current.certificate.subject, disabled: true }, Validators.required), + 'validFrom': new FormControl({ value: this.current.certificate.validFrom, disabled: true }, Validators.required), + 'validTo': new FormControl({ value: this.current.certificate.validTo, disabled: true }, Validators.required), + 'issuer': new FormControl({ value: this.current.certificate.issuer, disabled: true }, Validators.required), + 'serialNumber': new FormControl({ value: this.current.certificate.serialNumber, disabled: true }, Validators.required), + 'certificateId': new FormControl({ value: this.current.certificate.serialNumber, disabled: true }, Validators.required), }, { validator: [this.passwordConfirmationValidator, this.atLeastOneToggleCheckedValidator, this.certificateValidator] }); + this.userService.getUserRoles$().subscribe(userRoles => { this.userRoles = userRoles.json(); this.existingRoles = this.editMode - ? this.getAllowedRoles(this.userRoles, user.role) + ? this.getAllowedRoles(this.userRoles, this.current.role) : this.userRoles; - console.log(this.userRoles, this.existingRoles); }); } @@ -152,21 +162,23 @@ export class UserDetailsDialogComponent { onUserToggleChanged({checked}: MatSlideToggleChange) { const action = checked ? 'enable' : 'disable'; - this.userForm.get('username')[action](); - this.userForm.get('role')[action](); + this.userForm.get('username')[checked && !this.editMode ? 'enable' : 'disable'](); + //this.userForm.get('role')[action](); + //this.userForm.get('password')[action](); + //this.userForm.get('confirmation')[action](); + } + + onPasswordToggleChanged({checked}: MatSlideToggleChange) { + const action = checked ? 'enable' : 'disable'; this.userForm.get('password')[action](); this.userForm.get('confirmation')[action](); } - get current(): UserRo { - const rawValue = this.userForm.getRawValue(); - return { - ...rawValue, - id: this.id, - role: Object.keys(Role).find(k => Role[k] === rawValue.role), // ugly hack to find the corresponding enum key as a string - status: this.id ? SearchTableEntityStatus.UPDATED: SearchTableEntityStatus.NEW, - }; + + getCurrent(): UserRo { + // update data + return this.current; } // filters out roles so that the user cannot change from system administrator to the other roles or vice-versa diff --git a/smp-angular/src/app/user/user-ro.model.ts b/smp-angular/src/app/user/user-ro.model.ts index 1b32a870879122223a724a416c75090cfa988500..1c6b9ac1b3cbc5155c991d1a0435ab3769cfc9f4 100644 --- a/smp-angular/src/app/user/user-ro.model.ts +++ b/smp-angular/src/app/user/user-ro.model.ts @@ -3,9 +3,11 @@ import {CertificateRo} from './certificate-ro.model'; export interface UserRo extends SearchTableEntity { username: string; - email: string; + emailAddress: string; password?: string; role: string; + active: boolean; suspended?: boolean; certificate?: CertificateRo; + statusPassword: number; } diff --git a/smp-angular/src/app/user/user.component.html b/smp-angular/src/app/user/user.component.html index eb71b56716820e02e441553bb5e367c13aceb875..81ee627ae40bbcbf52cb8317d9b590837290c0b8 100644 --- a/smp-angular/src/app/user/user.component.html +++ b/smp-angular/src/app/user/user.component.html @@ -1,4 +1,5 @@ <smp-search-table + #searchTable page_id="user_id" title="Users" [columnPicker]="columnPicker" @@ -6,7 +7,9 @@ [additionalToolButtons]="additionalToolButtons" [searchTableController]="userController" [showSearchPanel]="false" - [filter]="filter"> + [filter]="filter" + [allowNewItems]="securityService.isCurrentUserSystemAdmin()" +> <ng-template #roleCellTemplate let-value="value" ngx-datatable-cell-template>{{getRoleLabel(value)}}</ng-template> diff --git a/smp-angular/src/app/user/user.component.ts b/smp-angular/src/app/user/user.component.ts index 418c56dc3bc0bb2e8ec12e755e476da293b977f9..becea140cfa95aa6399eaa192ef2e0c7c29274b6 100644 --- a/smp-angular/src/app/user/user.component.ts +++ b/smp-angular/src/app/user/user.component.ts @@ -4,8 +4,8 @@ import {MatDialog, MatDialogRef} from '@angular/material'; import {AlertService} from '../alert/alert.service'; import {UserController} from './user-controller'; import {HttpClient} from '@angular/common/http'; -import {Role} from "../security/role.model"; -import {UserRo} from "./user-ro.model"; +import {SearchTableComponent} from "../common/search-table/search-table.component"; +import {SecurityService} from "../security/security.service"; @Component({ templateUrl:'./user.component.html', @@ -13,13 +13,19 @@ import {UserRo} from "./user-ro.model"; }) export class UserComponent implements OnInit { - @ViewChild('roleCellTemplate') roleCellTemplate: TemplateRef<any> + @ViewChild('rowMetadataAction') rowMetadataAction: TemplateRef<any> + @ViewChild('rowExtensionAction') rowExtensionAction: TemplateRef<any> + @ViewChild('rowActions') rowActions: TemplateRef<any>; + @ViewChild('searchTable') searchTable: SearchTableComponent; columnPicker: ColumnPicker = new ColumnPicker(); userController: UserController; filter: any = {}; - constructor(protected http: HttpClient, protected alertService: AlertService, public dialog: MatDialog) { + constructor(public securityService: SecurityService, + protected http: HttpClient, + protected alertService: AlertService, + public dialog: MatDialog) { } ngOnInit() { @@ -37,7 +43,6 @@ export class UserComponent implements OnInit { canAutoResize: true }, { - cellTemplate: this.roleCellTemplate, name: 'Role', prop: 'role', canAutoResize: true @@ -52,8 +57,8 @@ export class UserComponent implements OnInit { details(row: any) { this.userController.showDetails(row); } - - getRoleLabel(role: string): Role { - return Role[role]; + // for dirty guard... + isDirty (): boolean { + return this.searchTable.isDirty(); } } diff --git a/smp-angular/src/styles.css b/smp-angular/src/styles.css index 95c98d1931a264c7fbe7cc46dca41019056b5e51..0426d0567935b47d249ec50553dda5d1e07fc21d 100644 --- a/smp-angular/src/styles.css +++ b/smp-angular/src/styles.css @@ -25,7 +25,9 @@ html, body { font-weight: bolder !important; margin: 0 2px !important; } - +.mat-card{ + padding: 10px !important; +} .text-select { -webkit-user-select: text; -moz-user-select: text; diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/conversion/CaseSensitivityNormalizer.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/conversion/CaseSensitivityNormalizer.java index b66ba56f8d74fbf7bca1929176f2b6b62e103abb..ddd5bb0c4fbdf7e0e1110c5de709a6612c90c90f 100644 --- a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/conversion/CaseSensitivityNormalizer.java +++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/conversion/CaseSensitivityNormalizer.java @@ -45,9 +45,7 @@ public class CaseSensitivityNormalizer { toLowerCaseStringList(this.caseSensitiveParticipantSchemes); } - public ParticipantIdentifierType normalize(final ParticipantIdentifierType participantIdentifier) { - String scheme = participantIdentifier.getScheme(); - String value = participantIdentifier.getValue(); + public ParticipantIdentifierType normalizeParticipantIdentifier(String scheme, String value) { if (!caseSensitiveParticipantSchemes.contains(scheme.toLowerCase())) { scheme = scheme.toLowerCase(); value = value.toLowerCase(); @@ -55,9 +53,19 @@ public class CaseSensitivityNormalizer { return new ParticipantIdentifierType(value, scheme); } + public ParticipantIdentifierType normalize(final ParticipantIdentifierType participantIdentifier) { + String scheme = participantIdentifier.getScheme(); + String value = participantIdentifier.getValue(); + return normalizeParticipantIdentifier(scheme, value); + } + public DocumentIdentifier normalize(final DocumentIdentifier documentIdentifier) { String scheme = documentIdentifier.getScheme(); String value = documentIdentifier.getValue(); + return normalizeDocumentIdentifier(scheme, value); + } + + public DocumentIdentifier normalizeDocumentIdentifier( String scheme, String value) { if (!caseSensitiveDocumentSchemes.contains(scheme.toLowerCase())) { scheme = scheme.toLowerCase(); value = value.toLowerCase(); diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/conversion/ExtensionConverter.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/conversion/ExtensionConverter.java index 423875c0e5b3b7be0c36f59cf2c3a555043bdb12..931fef73a063d14a617cae102cc732dba149872e 100644 --- a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/conversion/ExtensionConverter.java +++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/conversion/ExtensionConverter.java @@ -15,6 +15,8 @@ package eu.europa.ec.edelivery.smp.conversion; import eu.europa.ec.edelivery.smp.logging.SMPLogger; import eu.europa.ec.edelivery.smp.logging.SMPLoggerFactory; +import eu.europa.ec.smp.api.exceptions.XmlInvalidAgainstSchemaException; +import eu.europa.ec.smp.api.validators.BdxSmpOasisValidator; import org.apache.cxf.staxutils.PrettyPrintXMLStreamWriter; import org.oasis_open.docs.bdxr.ns.smp._2016._05.ExtensionType; @@ -41,6 +43,8 @@ public class ExtensionConverter { // private static final String WRAPPED_FORMAT = "<ExtensionsWrapper xmlns=\"http://docs.oasis-open.org/bdxr/ns/SMP/2016/05\">%s</ExtensionsWrapper>"; private static final byte[] WRAPPED_FORMAT_START = "<ExtensionsWrapper xmlns=\"http://docs.oasis-open.org/bdxr/ns/SMP/2016/05\">".getBytes(); private static final byte[] WRAPPED_FORMAT_END = "</ExtensionsWrapper>".getBytes(); + private static final byte[] WRAPPED_SERVICE_GROUP_START = "<ServiceGroup xmlns=\"http://docs.oasis-open.org/bdxr/ns/SMP/2016/05\"> <ParticipantIdentifier scheme=\"schema\">value</ParticipantIdentifier><ServiceMetadataReferenceCollection/>".getBytes(); + private static final byte[] WRAPPED_SERVICE_GROUP_END = "</ServiceGroup>".getBytes(); private static final QName EXT_TYPE_QNAME = new QName("http://docs.oasis-open.org/bdxr/ns/SMP/2016/05", "Extension"); /** @@ -119,7 +123,7 @@ public class ExtensionConverter { return baos.toByteArray(); } - protected static List<ExtensionType> unmarshalExtensions(byte[] xml) throws JAXBException { + public static List<ExtensionType> unmarshalExtensions(byte[] xml) throws JAXBException { InputStream inStream = new ByteArrayInputStream(concatByteArrays(WRAPPED_FORMAT_START,xml,WRAPPED_FORMAT_END )); @@ -133,6 +137,16 @@ public class ExtensionConverter { } } + /** + * Method validates extension by schema In order to do that wraps the content to simple servicegroup. + * + * @param xml + */ + public static void validateExtensionBySchema(byte[] xml) throws XmlInvalidAgainstSchemaException { + byte[] buff = concatByteArrays(WRAPPED_SERVICE_GROUP_START,xml,WRAPPED_SERVICE_GROUP_END); + BdxSmpOasisValidator.validateXSD(buff); + } + /** * Method concat the bytearrays to one array * diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/conversion/ServiceMetadataConverter.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/conversion/ServiceMetadataConverter.java index 41db2fbdbb626724075d11762b0654a9297f2ae0..0e069b18881aefabdc4bb95cb5a846eb51d8d832 100644 --- a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/conversion/ServiceMetadataConverter.java +++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/conversion/ServiceMetadataConverter.java @@ -127,4 +127,6 @@ public class ServiceMetadataConverter { } + + } diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/dao/BaseDao.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/dao/BaseDao.java index f941d6f161af8843a9c23f2d67141afb6bd50417..d5d9f31282a83610738fef360e391891b1804bb2 100644 --- a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/dao/BaseDao.java +++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/dao/BaseDao.java @@ -14,6 +14,7 @@ package eu.europa.ec.edelivery.smp.data.dao; import eu.europa.ec.edelivery.smp.data.model.BaseEntity; +import eu.europa.ec.edelivery.smp.exceptions.SMPTestIsALiveException; import eu.europa.ec.edelivery.smp.logging.SMPLogger; import eu.europa.ec.edelivery.smp.logging.SMPLoggerFactory; import eu.europa.ec.edelivery.smp.services.ServiceGroupService; @@ -65,10 +66,12 @@ public abstract class BaseDao<E extends BaseEntity> { * @param entity */ @Transactional - public void testPersist(E entity, String message) { + public void testPersist(E entity, boolean rollbackWithException, String message) { memEManager.persist(entity); memEManager.flush(); - throw new RuntimeException(message); + if (rollbackWithException) { + throw new SMPTestIsALiveException(message); + } } @@ -226,7 +229,9 @@ public abstract class BaseDao<E extends BaseEntity> { (Comparable) searchValue)); } else if (searchValue instanceof String && fieldName.endsWith("Like")) { if (!((String) searchValue).isEmpty()) { - lstPredicate.add(cb.like(getPath(om, fieldName, "Like"), "%" + (String) searchValue + "%")); + // like search is also case insensitive + String searchPhraze = ((String) searchValue).toLowerCase().trim(); + lstPredicate.add(cb.like(cb.lower(getPath(om, fieldName, "Like")), "%" + searchPhraze + "%")); } } else if (searchValue instanceof String) { if (!((String) searchValue).isEmpty()) { @@ -237,7 +242,7 @@ public abstract class BaseDao<E extends BaseEntity> { } else if (searchValue instanceof Long) { lstPredicate.add(cb.equal(getPath(om, fieldName), searchValue)); } else { - LOG.warn("Unknown search value type %s for method %s! Parameter is ignored!", + LOG.info("Unknown search value type {} for method {}! Parameter is ignored!", searchValue, fieldName); } } diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/dao/DomainDao.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/dao/DomainDao.java index e126fe97f1fcf2fc7afe6cbd455c0f118b76656e..9f959911e627a761f7db433403c95303ffcad239 100644 --- a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/dao/DomainDao.java +++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/dao/DomainDao.java @@ -15,6 +15,8 @@ package eu.europa.ec.edelivery.smp.data.dao; import eu.europa.ec.edelivery.smp.data.model.DBDomain; import eu.europa.ec.edelivery.smp.exceptions.ErrorCode; +import eu.europa.ec.edelivery.smp.exceptions.SMPRuntimeException; +import org.apache.commons.lang.StringUtils; import org.springframework.stereotype.Repository; import javax.persistence.NoResultException; @@ -24,6 +26,7 @@ import javax.transaction.Transactional; import java.util.List; import java.util.Optional; +import static eu.europa.ec.edelivery.smp.exceptions.ErrorCode.DOMAIN_NOT_EXISTS; import static eu.europa.ec.edelivery.smp.exceptions.ErrorCode.ILLEGAL_STATE_DOMAIN_MULTIPLE_ENTRY; /** @@ -81,6 +84,26 @@ public class DomainDao extends BaseDao<DBDomain> { } } + /** + * Check if domain for domain code exists. If not SMPRuntimeException with DOMAIN_NOT_EXISTS is thrown. + * If code is null or blank - then null is returned. + * + * @param domainCode + * @return + */ + public DBDomain validateDomainCode(String domainCode){ + DBDomain domain = null; + if (!StringUtils.isBlank(domainCode)) { + Optional<DBDomain> od = getDomainByCode(domainCode); + if (od.isPresent()) { + domain = od.get(); + } else { + throw new SMPRuntimeException(DOMAIN_NOT_EXISTS, domainCode); + } + } + return domain; + } + /** * Removes Entity by given domain code * diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/dao/ServiceGroupDao.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/dao/ServiceGroupDao.java index cd58acc6f9098b5ad0993fae929d91dbdf54f685..45b34ba86fa02d799e95c4e48c85689097ebae3c 100644 --- a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/dao/ServiceGroupDao.java +++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/dao/ServiceGroupDao.java @@ -12,15 +12,21 @@ */ package eu.europa.ec.edelivery.smp.data.dao; +import eu.europa.ec.edelivery.smp.data.model.DBDomain; import eu.europa.ec.edelivery.smp.data.model.DBServiceGroup; +import eu.europa.ec.edelivery.smp.data.model.DBServiceGroupDomain; +import eu.europa.ec.edelivery.smp.data.model.DBUser; import eu.europa.ec.edelivery.smp.exceptions.ErrorCode; +import eu.europa.ec.edelivery.smp.services.ui.filters.ServiceGroupFilter; +import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Repository; import javax.persistence.NoResultException; import javax.persistence.NonUniqueResultException; import javax.persistence.TypedQuery; +import javax.persistence.criteria.*; import javax.transaction.Transactional; -import java.util.Optional; +import java.util.*; /** * Created by gutowpa on 14/11/2017. @@ -81,7 +87,89 @@ public class ServiceGroupDao extends BaseDao<DBServiceGroup> { memEManager.remove(memEManager.contains(dbServiceGroup) ? dbServiceGroup : memEManager.merge(dbServiceGroup)); } + public long getServiceGroupCount(ServiceGroupFilter filters) { + CriteriaQuery<Long> cqCount = createSearchCriteria(filters, true, + null, + null); + return memEManager.createQuery(cqCount).getSingleResult(); + } + + public List<DBServiceGroup> getServiceGroupList(int startingAt, int maxResultCnt, + String sortField, + String sortOrder, ServiceGroupFilter filters) { + + List<DBServiceGroup> lstResult; + try { + CriteriaQuery<DBServiceGroup> cq = createSearchCriteria(filters, + false, sortField, + sortOrder); + TypedQuery<DBServiceGroup> q = memEManager.createQuery(cq); + if (maxResultCnt > 0) { + q.setMaxResults(maxResultCnt); + } + if (startingAt > 0) { + q.setFirstResult(startingAt); + } + lstResult = q.getResultList(); + } catch (NoResultException ex) { + //LOG.warn("No result for '" + filterType.getName() + "' does not have a setter!", ex); + lstResult = new ArrayList<>(); + } + return lstResult; + } + + protected CriteriaQuery createSearchCriteria(ServiceGroupFilter searchParams, + boolean forCount, String sortField, String sortOrder) { + CriteriaBuilder cb = memEManager.getCriteriaBuilder(); + CriteriaQuery cq = forCount ? cb.createQuery(Long.class) : cb.createQuery(DBServiceGroup.class); + Root<DBServiceGroup> serviceGroup = cq.from(DBServiceGroup.class); + if (forCount) { + cq.select(cb.count(serviceGroup)); + } else if (sortField != null) { + if (sortOrder != null && sortOrder.equalsIgnoreCase("desc")) { + cq.orderBy(cb.asc(serviceGroup.get(sortField))); + } else { + cq.orderBy(cb.desc(serviceGroup.get(sortField))); + } + } else { + if (!StringUtils.isBlank(defaultSortMethod)) { + cq.orderBy(cb.desc(serviceGroup.get(defaultSortMethod))); + } + } + + Join<DBServiceGroup, DBServiceGroupDomain> serviceGroupJoinServiceGroupDomain = null; + Predicate ownerPredicate = null; + if (searchParams!=null) { + + if (searchParams.getDomain()!=null){ + serviceGroupJoinServiceGroupDomain = serviceGroup.join("serviceGroupDomains", JoinType.INNER); + serviceGroupJoinServiceGroupDomain = serviceGroupJoinServiceGroupDomain.on(cb.equal(serviceGroupJoinServiceGroupDomain.get("domain"), searchParams.getDomain())); + } + // limit for owner + if (searchParams.getOwner() !=null){ + ownerPredicate = cb.equal(serviceGroup.join("users"),searchParams.getOwner()); + } + } + + // set order by + if (searchParams != null) { + List<Predicate> lstPredicate = createPredicates(searchParams, serviceGroup, cb); + + if (serviceGroupJoinServiceGroupDomain!=null) { + lstPredicate.add(serviceGroupJoinServiceGroupDomain.getOn()); + } + if (ownerPredicate!=null) { + lstPredicate.add(ownerPredicate); + } + + if (!lstPredicate.isEmpty()) { + Predicate[] tblPredicate = lstPredicate.stream().toArray(Predicate[]::new); + cq.where(cb.and(tblPredicate)); + } + } + return cq; + } diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/model/DBDomain.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/model/DBDomain.java index 1ad5e40e647b0820a5d4431d1d9058f9e86ba560..23652e66a28c39b130aa2d94375f4f0e13127e0d 100644 --- a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/model/DBDomain.java +++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/model/DBDomain.java @@ -40,7 +40,7 @@ public class DBDomain extends BaseEntity { @Column(name = "DOMAIN_CODE", length = CommonColumnsLengths.MAX_DOMAIN_CODE_LENGTH, nullable = false, unique = true) String domainCode; - @Column(name = "SML_SUBDOMAIN", length = CommonColumnsLengths.MAX_SML_SUBDOMAIN_LENGTH, nullable = false, unique = true) + @Column(name = "SML_SUBDOMAIN", length = CommonColumnsLengths.MAX_SML_SUBDOMAIN_LENGTH, unique = true) String smlSubdomain; @Column(name = "SML_SMP_ID", length = CommonColumnsLengths.MAX_SML_SMP_ID_LENGTH) String smlSmpId; diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/model/DBServiceGroup.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/model/DBServiceGroup.java index d37e28c0af4694f745f7a76bc306835dc513c25c..e36ae4ec4e1f5771e66c52cff14ba040b6c2295b 100644 --- a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/model/DBServiceGroup.java +++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/model/DBServiceGroup.java @@ -70,8 +70,7 @@ public class DBServiceGroup extends BaseEntity { @Column(name = "PARTICIPANT_SCHEME", length = CommonColumnsLengths.MAX_PARTICIPANT_IDENTIFIER_SCHEME_LENGTH, nullable = false) String participantScheme; - @Column(name = "SML_REGISTRED", nullable = false) - private boolean smlRegistered = false; + @OneToOne(mappedBy = "dbServiceGroup", cascade = CascadeType.ALL, fetch = FetchType.LAZY, orphanRemoval = true) private DBServiceGroupExtension serviceGroupExtension; @@ -123,14 +122,6 @@ public class DBServiceGroup extends BaseEntity { return this.users; } - public boolean isSmlRegistered() { - return smlRegistered; - } - - public void setSmlRegistered(boolean smlRegistered) { - this.smlRegistered = smlRegistered; - } - public DBServiceGroupExtension getServiceGroupExtension() { return serviceGroupExtension; } @@ -173,6 +164,18 @@ public class DBServiceGroup extends BaseEntity { } } + public Optional<DBServiceGroupDomain> findServiceGroupDomainForMetadata(String docId, String docSch){ + for (DBServiceGroupDomain serviceGroupDomain : serviceGroupDomains) { + for (DBServiceMetadata dbServiceMetadata : serviceGroupDomain.getServiceMetadata()) { + if (Objects.equals(docId, dbServiceMetadata.getDocumentIdentifier()) + && Objects.equals(docId, dbServiceMetadata.getDocumentIdentifier()) ) { + return Optional.of(serviceGroupDomain); + } + } + } + return Optional.empty(); + } + @Transient diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/model/DBServiceGroupDomain.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/model/DBServiceGroupDomain.java index d47915011500a5d725596b0b8d43b8a967ec7f87..cde68f389be450419a5b805e5d1b8c29f599d212 100644 --- a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/model/DBServiceGroupDomain.java +++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/model/DBServiceGroupDomain.java @@ -46,11 +46,19 @@ public class DBServiceGroupDomain extends BaseEntity { // list<> and set<> does not make any difference in hi // hibernate performance this case! + // this list could also be on ServiceGroup entity because it does not make any difference for + // dynamic discovery but it is here just user -service group admin to know for which + // domain he orignally registred a service - to make metadata more organized... + @OneToMany(mappedBy = "serviceGroupDomain", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY) private List<DBServiceMetadata> serviceMetadata = new ArrayList<>(); + + @Column(name = "SML_REGISTRED", nullable = false) + private boolean smlRegistered = false; + @Column(name = "CREATED_ON" , nullable = false) LocalDateTime createdOn; @Column(name = "LAST_UPDATED_ON", nullable = false) @@ -94,6 +102,15 @@ public class DBServiceGroupDomain extends BaseEntity { this.domain = domain; } + public boolean isSmlRegistered() { + return smlRegistered; + } + + public void setSmlRegistered(boolean smlRegistered) { + this.smlRegistered = smlRegistered; + } + + public void addServiceMetadata(DBServiceMetadata metadata) { serviceMetadata.add(metadata); metadata.setServiceGroupDomain(this); @@ -104,6 +121,14 @@ public class DBServiceGroupDomain extends BaseEntity { metadata.setServiceGroupDomain(null); } + public DBServiceMetadata removeServiceMetadata(String docId, String docSch) { + DBServiceMetadata dbServiceMetadata = getServiceMetadata(docId,docSch ); + if (dbServiceMetadata!=null) { + removeServiceMetadata(dbServiceMetadata); + } + return dbServiceMetadata; + } + @Transient public DBServiceMetadata getServiceMetadata(int index) { return serviceMetadata.get(index); diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/model/DBUser.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/model/DBUser.java index 272b0dede0c5b14d4991ce7928369efb238c188b..3027071e028e65fc7fea09715aef60da779291bf 100644 --- a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/model/DBUser.java +++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/model/DBUser.java @@ -40,7 +40,7 @@ public class DBUser extends BaseEntity { @Column(name = "PASSWORD", length = CommonColumnsLengths.MAX_PASSWORD_LENGTH) private String password; @Column(name = "EMAIL", length = CommonColumnsLengths.MAX_PASSWORD_LENGTH) - private String email; + private String emailAddress; @Column(name = "PASSWORD_CHANGED") LocalDateTime passwordChanged; @@ -119,10 +119,10 @@ public class DBUser extends BaseEntity { this.certificate = certificate; } - public String getEmail() { return email; } + public String getEmailAddress() { return emailAddress; } - public void setEmail(String email) { - this.email = email; + public void setEmailAddress(String email) { + this.emailAddress = email; } public LocalDateTime getPasswordChanged() { diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/ui/BaseRO.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/ui/BaseRO.java index b19a0fec8baa9b0dba52dcd516c4fabdaad85acc..a2e393ba00d089ce49626e02954a4b8654eaffac 100644 --- a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/ui/BaseRO.java +++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/ui/BaseRO.java @@ -6,8 +6,8 @@ import java.io.Serializable; public class BaseRO implements Serializable { - private int status; - private int index = EntityROStatus.PERSISTED.getStatusNumber(); + private int status = EntityROStatus.PERSISTED.getStatusNumber(); + private int index; public int getStatus() { return status; diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/ui/CertificateRO.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/ui/CertificateRO.java index 6555e37084f3e0d8ab48bf2cd7bf915bf7867ef1..7637bdfc8ef1fabb45799351ff8c214b22dfa421 100644 --- a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/ui/CertificateRO.java +++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/ui/CertificateRO.java @@ -14,6 +14,7 @@ public class CertificateRO extends BaseRO { private String subject; private String issuer; private String serialNumber; + private String encodedValue; private LocalDateTime validFrom; private LocalDateTime validTo; @@ -71,4 +72,12 @@ public class CertificateRO extends BaseRO { public void setValidTo(LocalDateTime validTo) { this.validTo = validTo; } + + public String getEncodedValue() { + return encodedValue; + } + + public void setEncodedValue(String encodedValue) { + this.encodedValue = encodedValue; + } } diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/ui/ErrorRO.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/ui/ErrorRO.java new file mode 100644 index 0000000000000000000000000000000000000000..42930391f8f3206acb592f107153fa6388899cdc --- /dev/null +++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/ui/ErrorRO.java @@ -0,0 +1,37 @@ +package eu.europa.ec.edelivery.smp.data.ui; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; + +import java.io.Serializable; + +/** + * @author Sebastian-Ion TINCU + * @since 4.0.1 + */ +public class ErrorRO implements Serializable { + + protected String message; + + public ErrorRO(String message) { + this.message = message; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + @JsonIgnore + public int getContentLength() { + try { + return new ObjectMapper().writeValueAsString(this).length(); + } catch (JsonProcessingException e) { + return -1; + } + } +} \ No newline at end of file diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/ui/LoginRO.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/ui/LoginRO.java new file mode 100644 index 0000000000000000000000000000000000000000..7a1f9cef0cdb72698362c21ddfbcfb7756811cdc --- /dev/null +++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/ui/LoginRO.java @@ -0,0 +1,32 @@ +package eu.europa.ec.edelivery.smp.data.ui; + +import java.io.Serializable; + +/** + * @author Sebastian-Ion TINCU + * @since 4.0.1 + */ +public class LoginRO implements Serializable { + + private static final long serialVersionUID = -3841558544088527974L; + + private String username; + + private String password; + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } +} \ No newline at end of file diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/ui/ServiceGroupDomainRO.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/ui/ServiceGroupDomainRO.java new file mode 100644 index 0000000000000000000000000000000000000000..23e960e5ee91af50f9fe321a495bbd542b322b04 --- /dev/null +++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/ui/ServiceGroupDomainRO.java @@ -0,0 +1,60 @@ +package eu.europa.ec.edelivery.smp.data.ui; + + +/** + * @author Joze Rihtarsic + * @since 4.1 + */ + + +public class ServiceGroupDomainRO extends BaseRO { + + + private static final long serialVersionUID = -7111221767041516157L; + private Long id; + private Long domainId; + String domainCode; + String smlSubdomain; + boolean smlRegistered; + + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public Long getDomainId() { + return domainId; + } + + public void setDomainId(Long domainId) { + this.domainId = domainId; + } + + public String getDomainCode() { + return domainCode; + } + + public void setDomainCode(String domainCode) { + this.domainCode = domainCode; + } + + public String getSmlSubdomain() { + return smlSubdomain; + } + + public void setSmlSubdomain(String smlSubdomain) { + this.smlSubdomain = smlSubdomain; + } + + public boolean isSmlRegistered() { + return smlRegistered; + } + + public void setSmlRegistered(boolean registered) { + this.smlRegistered = registered; + } +} diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/ui/ServiceGroupExtensionRO.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/ui/ServiceGroupExtensionRO.java new file mode 100644 index 0000000000000000000000000000000000000000..0fc54379cb0fdf83a50808b85257d95c07b9dbd4 --- /dev/null +++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/ui/ServiceGroupExtensionRO.java @@ -0,0 +1,32 @@ +package eu.europa.ec.edelivery.smp.data.ui; + +public class ServiceGroupExtensionRO extends BaseRO { + private static final long serialVersionUID = -7555221767041516157L; + Long serviceGroupId; + String extension; + String errorMessage; + + public Long getServiceGroupId() { + return serviceGroupId; + } + + public void setServiceGroupId(Long serviceGroupId) { + this.serviceGroupId = serviceGroupId; + } + + public String getExtension() { + return extension; + } + + public void setExtension(String extension) { + this.extension = extension; + } + + public String getErrorMessage() { + return errorMessage; + } + + public void setErrorMessage(String errorMessage) { + this.errorMessage = errorMessage; + } +} diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/ui/ServiceGroupRO.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/ui/ServiceGroupRO.java index f3da826f10980d93b21242e5ac11f9c0a52952d6..6ca51f3a04d9f02a4211012ed05cc11cae366389 100644 --- a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/ui/ServiceGroupRO.java +++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/ui/ServiceGroupRO.java @@ -1,7 +1,8 @@ package eu.europa.ec.edelivery.smp.data.ui; -import java.io.Serializable; +import eu.europa.ec.edelivery.smp.data.ui.enums.EntityROStatus; + import java.util.ArrayList; import java.util.List; @@ -19,11 +20,12 @@ public class ServiceGroupRO extends BaseRO { private String participantIdentifier; private String participantScheme; private boolean smlRegistered = false; - private List<ServiceMetadataRO> lstServiceMetadata = new ArrayList<>(); private List<UserRO> lstUser = new ArrayList<>(); - - - + private List<ServiceGroupDomainRO> serviceGroupDomains = new ArrayList<>(); + // for UI service groups are in one list. + private List<ServiceMetadataRO> serviceMetadata = new ArrayList<>(); + private int extensionStatus = EntityROStatus.PERSISTED.getStatusNumber(); + private String extension; public Long getId() { @@ -58,12 +60,31 @@ public class ServiceGroupRO extends BaseRO { this.smlRegistered = smlRegistered; } + public int getExtensionStatus() { + return extensionStatus; + } - public List<ServiceMetadataRO> getServiceMetadata() { - return lstServiceMetadata; + public void setExtensionStatus(int extensionStatus) { + this.extensionStatus = extensionStatus; + } + + public String getExtension() { + return extension; } + + public void setExtension(String extension) { + this.extension = extension; + } + public List<UserRO> getUsers() { return lstUser; } + public List<ServiceGroupDomainRO> getServiceGroupDomains() { + return serviceGroupDomains; + } + + public List<ServiceMetadataRO> getServiceMetadata() { + return serviceMetadata; + } } diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/ui/ServiceGroupSearchRO.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/ui/ServiceGroupSearchRO.java new file mode 100644 index 0000000000000000000000000000000000000000..947392f7c58d1e9b235da4ec156ffbbbfe12c9c0 --- /dev/null +++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/ui/ServiceGroupSearchRO.java @@ -0,0 +1,51 @@ +package eu.europa.ec.edelivery.smp.data.ui; + + +import java.util.ArrayList; +import java.util.List; + +/** + * Lighter (without administration walues) ServiceGroup object for searching service group and its metadata. + * + * @author Joze Rihtarsic + * @since 4.1 + */ + +public class ServiceGroupSearchRO extends BaseRO { + + + private static final long serialVersionUID = -7523221767041516157L; + private Long id; + private String participantIdentifier; + private String participantScheme; + private List<ServiceMetadataRO> lstServiceMetadata = new ArrayList<>(); + + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getParticipantIdentifier() { + return participantIdentifier; + } + + public void setParticipantIdentifier(String participantIdentifier) { + this.participantIdentifier = participantIdentifier; + } + + public String getParticipantScheme() { + return participantScheme; + } + + public void setParticipantScheme(String participantScheme) { + this.participantScheme = participantScheme; + } + + public List<ServiceMetadataRO> getServiceMetadata() { + return lstServiceMetadata; + } +} diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/ui/ServiceMetadataRO.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/ui/ServiceMetadataRO.java index f47c78dc5a976a3521aa2c44bd93a56c7eac7223..dad9a1dd2ab9eab6c4fab0b8a2607c0bc6901a68 100644 --- a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/ui/ServiceMetadataRO.java +++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/ui/ServiceMetadataRO.java @@ -1,7 +1,7 @@ package eu.europa.ec.edelivery.smp.data.ui; -import java.io.Serializable; +import eu.europa.ec.edelivery.smp.data.ui.enums.EntityROStatus; /** * @author Joze Rihtarsic @@ -11,12 +11,40 @@ public class ServiceMetadataRO extends BaseRO { private static final long serialVersionUID = 67944640449327185L; - + private Long id; String documentIdentifier; String documentIdentifierScheme; + Long serviceGroupDomainId; + // Long domainId; String smlSubdomain; String domainCode; + private int xmlContentStatus = EntityROStatus.PERSISTED.getStatusNumber(); + String xmlContent; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public Long getServiceGroupDomainId() { + return serviceGroupDomainId; + } + + public void setServiceGroupDomainId(Long serviceGroupDomainId) { + this.serviceGroupDomainId = serviceGroupDomainId; + } +/* + public Long getDomainId() { + return domainId; + } + public void setDomainId(Long domainId) { + this.domainId = domainId; + } +*/ public String getDocumentIdentifier() { return documentIdentifier; } @@ -48,4 +76,20 @@ public class ServiceMetadataRO extends BaseRO { public void setDomainCode(String domainCode) { this.domainCode = domainCode; } + + public String getXmlContent() { + return xmlContent; + } + + public void setXmlContent(String xmlContent) { + this.xmlContent = xmlContent; + } + + public int getXmlContentStatus() { + return xmlContentStatus; + } + + public void setXmlContentStatus(int xmlContentStatus) { + this.xmlContentStatus = xmlContentStatus; + } } \ No newline at end of file diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/ui/ServiceMetadataValidationRO.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/ui/ServiceMetadataValidationRO.java new file mode 100644 index 0000000000000000000000000000000000000000..37e7bc5ec9bfdf57270c0da90506eaa3e1cb09f9 --- /dev/null +++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/ui/ServiceMetadataValidationRO.java @@ -0,0 +1,67 @@ +package eu.europa.ec.edelivery.smp.data.ui; + +/** + * @author Joze Rihtarsic + * @since 4.1 + */ +public class ServiceMetadataValidationRO extends BaseRO { + + + private static final long serialVersionUID = 65555540449327185L; + + String participantScheme; + String participantIdentifier; + String documentIdentifierScheme; + String documentIdentifier; + + String errorMessage; + String xmlContent; + + public String getParticipantScheme() { + return participantScheme; + } + + public void setParticipantScheme(String participantScheme) { + this.participantScheme = participantScheme; + } + + public String getParticipantIdentifier() { + return participantIdentifier; + } + + public void setParticipantIdentifier(String participantIdentifier) { + this.participantIdentifier = participantIdentifier; + } + + public String getDocumentIdentifierScheme() { + return documentIdentifierScheme; + } + + public void setDocumentIdentifierScheme(String documentIdentifierScheme) { + this.documentIdentifierScheme = documentIdentifierScheme; + } + + public String getDocumentIdentifier() { + return documentIdentifier; + } + + public void setDocumentIdentifier(String documentIdentifier) { + this.documentIdentifier = documentIdentifier; + } + + public String getErrorMessage() { + return errorMessage; + } + + public void setErrorMessage(String errorMessage) { + this.errorMessage = errorMessage; + } + + public String getXmlContent() { + return xmlContent; + } + + public void setXmlContent(String xmlContent) { + this.xmlContent = xmlContent; + } +} \ No newline at end of file diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/ui/SmpInfoRO.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/ui/SmpInfoRO.java new file mode 100644 index 0000000000000000000000000000000000000000..b96d8e0f9fb96b322b28d010b2b5d51076955cbe --- /dev/null +++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/ui/SmpInfoRO.java @@ -0,0 +1,16 @@ +package eu.europa.ec.edelivery.smp.data.ui; + +import java.io.Serializable; + +public class SmpInfoRO implements Serializable { + private static final long serialVersionUID = -49712226560325302L; + String version; + + public String getVersion() { + return version; + } + + public void setVersion(String version) { + this.version = version; + } +} diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/ui/UserRO.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/ui/UserRO.java index fd6764754daabeabbcb1ecae971126884a58a616..98928df5b5f6f9e0895c30114e6b38d7f96d1b67 100644 --- a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/ui/UserRO.java +++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/ui/UserRO.java @@ -1,11 +1,10 @@ package eu.europa.ec.edelivery.smp.data.ui; - - - +import eu.europa.ec.edelivery.smp.data.ui.enums.EntityROStatus; import java.io.Serializable; import java.time.LocalDateTime; +import java.util.List; /** @@ -19,12 +18,14 @@ public class UserRO extends BaseRO { private static final long serialVersionUID = -4971552086560325302L; private String username; private String password; - private String email; - LocalDateTime passwordChanged; + private String emailAddress; + private List<String> authorities; + private LocalDateTime passwordChanged; private boolean active = true; private String role; private Long id; private CertificateRO certificateData; + private int statusPassword = EntityROStatus.PERSISTED.getStatusNumber(); public UserRO(){ @@ -54,12 +55,12 @@ public class UserRO extends BaseRO { this.password = password; } - public String getEmail() { - return email; + public String getEmailAddress() { + return emailAddress; } - public void setEmail(String email) { - this.email = email; + public void setEmailAddress(String email) { + this.emailAddress = email; } public LocalDateTime getPasswordChanged() { @@ -93,4 +94,19 @@ public class UserRO extends BaseRO { public void setCertificateData(CertificateRO certificate) { this.certificateData = certificate; } + public List<String> getAuthorities() { + return authorities; + } + + public void setAuthorities(List<String> authorities) { + this.authorities = authorities; + } + + public int getStatusPassword() { + return statusPassword; + } + + public void setStatusPassword(int statusPassword) { + this.statusPassword = statusPassword; + } } diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/exceptions/ErrorBusinessCode.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/exceptions/ErrorBusinessCode.java index 0aa830f2253ccaca32271eff44a893d6cc5b25c5..13081b04cfa403539c25e680938e6a4f8fd309dc 100644 --- a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/exceptions/ErrorBusinessCode.java +++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/exceptions/ErrorBusinessCode.java @@ -27,5 +27,6 @@ public enum ErrorBusinessCode { UNAUTHORIZED, NOT_FOUND, USER_NOT_FOUND, + INVALID_INPUT_DATA, TECHNICAL; } 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 a279e25d2b4a4ce06c29f0e660789b19f9d793bb..bcf7791e2d3e7b9fc11def6bf1a4eb00e0501799 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 @@ -17,6 +17,8 @@ public enum ErrorCode { INVALID_DOMAIN_CODE(400,"SMP:112",ErrorBusinessCode.FORMAT_ERROR,"Provided Domain Code '%s' does not match required pattern: '%s'"), ILLEGAL_STATE_DOMAIN_MULTIPLE_ENTRY(500,"SMP:113",ErrorBusinessCode.TECHNICAL,"More than one domain entry (domain: '%s') is defined in database!"), MISSING_DOMAIN(400,"SMP:114",ErrorBusinessCode.MISSING_FIELD,"More than one domain registred on SMP. The domain must be defined!"), + + // user error messages INVALID_USER_NO_IDENTIFIERS (400,"SMP:120",ErrorBusinessCode.MISSING_FIELD,"Invalid user - no identifiers!"), ILLEGAL_STATE_USERNAME_MULTIPLE_ENTRY(500,"SMP:121",ErrorBusinessCode.TECHNICAL,"More than one user entry (username: '%s') is defined in database!"), @@ -31,11 +33,19 @@ public enum ErrorCode { SG_NOT_EXISTS(404,"SMP:131",ErrorBusinessCode.NOT_FOUND,"ServiceGroup not found (dpart. id: '%s', part. sch.: '%s')!"), SG_NOT_REGISTRED_FOR_DOMAIN(400,"SMP:131",ErrorBusinessCode.NOT_FOUND,"Service group not registred for domain (domain: %s, part. id:~ '%s', part. sch.: '%s')!"), INVALID_EXTENSION_FOR_SG (400,"SMP:132",ErrorBusinessCode.XSD_INVALID,"Invalid extension for service group (part. id: '%s', part. sch.: '%s'). Error: %s!"), + DUPLICATE_DOMAIN_FOR_SG (400,"SMP:133",ErrorBusinessCode.INVALID_INPUT_DATA,"Repeated domain for Service group (part. id: '%s', part. sch.: '%s', domainCode %s, smlDomain %s).!"), + MISSING_SG_ID (400,"SMP:134",ErrorBusinessCode.INVALID_INPUT_DATA,"Missing service group(part. id: '%s', part. sch.: '%s'!"), + + INVALID_SG_ID (400,"SMP:135",ErrorBusinessCode.INVALID_INPUT_DATA,"Invalid Id for Service group(part. id: '%s', part. sch.: '%s', id %d).!"), + + // service metadata error ILLEGAL_STATE_SMD_MULTIPLE_ENTRY (500,"SMP:140",ErrorBusinessCode.TECHNICAL,"More than one service metadata ( doc. id: %s, doc. sch.: '%s') for participant ( part. id %s, part. sch. : '%s') is defined in database!"), METADATA_NOT_EXISTS(404,"SMP:141",ErrorBusinessCode.NOT_FOUND,"ServiceMetadata not found (part. id: '%s', part. sch.: '%s',doc. id: '%s', doc. sch.: '%s')!"), SMD_NOT_EXISTS_FOR_DOMAIN(404,"SMP:142",ErrorBusinessCode.NOT_FOUND,"ServiceMetadata not found for domain (domain: %s, part. id: '%s', part. sch.: '%s')!"), INVALID_SMD_XML (400,"SMP:143",ErrorBusinessCode.XSD_INVALID,"Invalid service metada. Error: %s"), + IVALID_SMD_DOCUMENT_DATA (400,"SMP:143",ErrorBusinessCode.INVALID_INPUT_DATA,"XML serviceMetadata document (doc. id: '%s', doc. sch.: '%s') " + + "do not match metadata request (doc. id: '%s', doc. sch.: '%s')."), // SML integration SML_INTEGRATION_EXCEPTION (500,"SMP:150",ErrorBusinessCode.TECHNICAL,"Could not create new DNS entry through SML! Error: %s "), @@ -45,6 +55,7 @@ public enum ErrorCode { JAXB_INITIALIZATION (500,"SMP:511",ErrorBusinessCode.TECHNICAL, "Could not create Unmarshaller for class %s!"), XML_PARSE_EXCEPTION (500,"SMP:512",ErrorBusinessCode.TECHNICAL, "Error occured while parsing input stream for %s. Error: %s!"), + INVALID_REQEUST (500,"SMP:513",ErrorBusinessCode.TECHNICAL, "Invalid request %s. Error: %s!"), // ; diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/exceptions/SMPTestIsALiveException.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/exceptions/SMPTestIsALiveException.java new file mode 100644 index 0000000000000000000000000000000000000000..56e2841a63ed496bd723c418161a71c198367990 --- /dev/null +++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/exceptions/SMPTestIsALiveException.java @@ -0,0 +1,12 @@ +package eu.europa.ec.edelivery.smp.exceptions; + +/** + * Exception for testing database connection - rollback exception + */ +public class SMPTestIsALiveException extends RuntimeException { + + public SMPTestIsALiveException(String message ) { + super(message); + } + +} diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/logging/SMPMessageCode.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/logging/SMPMessageCode.java index aeb63d031a43a2d6b16348d52f0f25178fda714a..7db631120e83a40ad8b613010afb4d51c0910e1f 100644 --- a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/logging/SMPMessageCode.java +++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/logging/SMPMessageCode.java @@ -16,18 +16,19 @@ public enum SMPMessageCode implements MessageCode { BUS_HTTP_DELETE_END_SERVICE_GROUP ("BUS-004", "End Http DELETE ServiceGroup from user {} from host: {}. ServiceGroup id: {}."), BUS_HTTP_GET_SERVICE_GROUP ("BUS-005", "Http GET ServiceGroup from host: {}, ServiceGroup id: {}."), BUS_HTTP_GET_END_SERVICE_GROUP ("BUS-006", "End Http GET ServiceGroup from host: {}, ServiceGroup id: {}."), + BUS_HTTP_GET_END_STATIC_CONTENT ("BUS-007", "End Http GET static content from host: {}, Path: {}."), - BUS_HTTP_PUT_SERVICE_METADATA ("BUS-007", "Http PUT ServiceGroupMetadata from user {} from host: {}. ServiceGroup with domain: {}, ServiceGroup id: {} , metadata id {}."), - BUS_HTTP_PUT_END_SERVICE_METADATA ("BUS-008", "End http PUT ServiceGroupMetadata from user {}, host: {}. ServiceGroup with domain {}, ServiceGroup id: {} , metadata id {}, created {}"), - BUS_HTTP_DELETE_SERVICE_METADATA ("BUS-009", "Http DELETE ServiceGroupMetadata from user {} from host: {}. ServiceGroup id: {} , metadata id {}."), - BUS_HTTP_DELETE_END_SERVICE_METADATA ("BUS-010", "End Http DELETE ServiceGroupMetadata from user {} from host: {}. ServiceGroup id: {} , metadata id {}."), + BUS_HTTP_PUT_SERVICE_METADATA ("BUS-008", "Http PUT ServiceGroupMetadata from user {} from host: {}. ServiceGroup with domain: {}, ServiceGroup id: {} , metadata id {}."), + BUS_HTTP_PUT_END_SERVICE_METADATA ("BUS-009", "End http PUT ServiceGroupMetadata from user {}, host: {}. ServiceGroup with domain {}, ServiceGroup id: {} , metadata id {}, created {}"), + BUS_HTTP_DELETE_SERVICE_METADATA ("BUS-010", "Http DELETE ServiceGroupMetadata from user {} from host: {}. ServiceGroup id: {} , metadata id {}."), + BUS_HTTP_DELETE_END_SERVICE_METADATA ("BUS-011", "End Http DELETE ServiceGroupMetadata from user {} from host: {}. ServiceGroup id: {} , metadata id {}."), - BUS_HTTP_GET_SERVICE_METADATA ("BUS-011", "Http GET ServiceGroup from host: {}, ServiceGroup id: {}, metadata id {}."), - BUS_HTTP_GET_END_SERVICE_METADATA ("BUS-012", "End Http GET ServiceGroup from host: {}, ServiceGroup id: {}, metadata id {}."), + BUS_HTTP_GET_SERVICE_METADATA ("BUS-012", "Http GET ServiceGroup from host: {}, ServiceGroup id: {}, metadata id {}."), + BUS_HTTP_GET_END_SERVICE_METADATA ("BUS-013", "End Http GET ServiceGroup from host: {}, ServiceGroup id: {}, metadata id {}."), - BUS_SAVE_SERVICE_GROUP ("BUS-013", "Start inserting/updating ServiceGroup for domain {}, part. Id: {} part. schema {}."), - BUS_SAVE_SERVICE_GROUP_FAILED ("BUS-014", "Inserting/updating ServiceGroup for domain {}, part. Id: {} part. schema {} failed! Error: [{}]"), + BUS_SAVE_SERVICE_GROUP ("BUS-014", "Start inserting/updating ServiceGroup for domain {}, part. Id: {} part. schema {}."), + BUS_SAVE_SERVICE_GROUP_FAILED ("BUS-015", "Inserting/updating ServiceGroup for domain {}, part. Id: {} part. schema {} failed! Error: [{}]"), BUS_INVALID_XML("BUS-030", "Invalid XML for {}. Error: [{}]"), diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/ServiceGroupService.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/ServiceGroupService.java index ca7ab211cc1589107fc82e0e5d231e20e724c801..8f201e56ff6712fa5f9f8c10edc022e4590a0e77 100644 --- a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/ServiceGroupService.java +++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/ServiceGroupService.java @@ -124,14 +124,14 @@ public class ServiceGroupService { DBServiceGroup sg = dbServiceGroup.get(); validateOwnership(newOwnerName, sg); //check is domain exists - Optional<DBServiceGroupDomain> sgd = dbServiceGroup.get().getServiceGroupForDomain(dmn.getDomainCode()); + Optional<DBServiceGroupDomain> sgd = sg.getServiceGroupForDomain(dmn.getDomainCode()); if (!sgd.isPresent()){ SMPRuntimeException ex = new SMPRuntimeException(SG_NOT_REGISTRED_FOR_DOMAIN,domain,normalizedParticipantId.getValue(), normalizedParticipantId.getScheme()); LOG.businessError(SMPMessageCode.BUS_SAVE_SERVICE_GROUP_FAILED,domain,normalizedParticipantId.getValue(), normalizedParticipantId.getScheme(), ex.getMessage() ); throw ex; } //update extensions - dbServiceGroup.get().setExtension(extensions); + sg.setExtension(extensions); serviceGroupDao.update(sg); return false; } else { @@ -141,15 +141,16 @@ public class ServiceGroupService { newSg.setParticipantScheme(normalizedParticipantId.getScheme()); newSg.setExtension(extensions); newSg.addDomain(dmn); // add initial domain + // set initial domain as not registered + newSg.getServiceGroupDomains().get(0).setSmlRegistered(false); newSg.getUsers().add(newOwner.get()); - newSg.setSmlRegistered(false); // persist (make sure this is not in transaction) serviceGroupDao.persistFlushDetach(newSg); // register to SML boolean registered = smlConnector.registerInDns(normalizedParticipantId, dmn); if (registered) { // update status in database - newSg.setSmlRegistered(true); + newSg.getServiceGroupDomains().get(0).setSmlRegistered(false); serviceGroupDao.update(newSg); } return true; @@ -195,7 +196,7 @@ public class ServiceGroupService { /** * Method validates if user owner with identifier is owner of servicegroup * @param ownerIdentifier - * @param dbsg + * @param serviceGroupIdentifier */ @Transactional public boolean isServiceGroupOwner(String ownerIdentifier, String serviceGroupIdentifier ){ diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/ui/UIServiceGroupSearchService.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/ui/UIServiceGroupSearchService.java new file mode 100644 index 0000000000000000000000000000000000000000..02ecf6c7a985070007a19fed355aa331117f98b2 --- /dev/null +++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/ui/UIServiceGroupSearchService.java @@ -0,0 +1,106 @@ +package eu.europa.ec.edelivery.smp.services.ui; + +import eu.europa.ec.edelivery.smp.data.dao.BaseDao; +import eu.europa.ec.edelivery.smp.data.dao.DomainDao; +import eu.europa.ec.edelivery.smp.data.dao.ServiceGroupDao; +import eu.europa.ec.edelivery.smp.data.dao.UserDao; +import eu.europa.ec.edelivery.smp.data.model.DBDomain; +import eu.europa.ec.edelivery.smp.data.model.DBServiceGroup; +import eu.europa.ec.edelivery.smp.data.ui.DomainRO; +import eu.europa.ec.edelivery.smp.data.ui.ServiceGroupSearchRO; +import eu.europa.ec.edelivery.smp.data.ui.ServiceMetadataRO; +import eu.europa.ec.edelivery.smp.data.ui.ServiceResult; +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.ui.filters.ServiceGroupFilter; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +import static eu.europa.ec.edelivery.smp.exceptions.ErrorCode.DOMAIN_NOT_EXISTS; + +@Service +public class UIServiceGroupSearchService extends UIServiceBase<DBServiceGroup, ServiceGroupSearchRO> { + private static final SMPLogger LOG = SMPLoggerFactory.getLogger(UIServiceGroupSearchService.class); + + @Autowired + DomainDao domainDao; + + @Autowired + ServiceGroupDao serviceGroupDao; + + @Autowired + UserDao userDao; + + + @Override + protected BaseDao<DBServiceGroup> getDatabaseDao() { + return serviceGroupDao; + } + + /** + * Method return list of service group entities with service metadata for given search parameters and page. + * + * @param page + * @param pageSize + * @param sortField + * @param sortOrder + * @param filter + * @return + */ + @Transactional + public ServiceResult<ServiceGroupSearchRO> getTableList(int page, int pageSize, + String sortField, + String sortOrder, ServiceGroupFilter filter) { + + ServiceResult<ServiceGroupSearchRO> sg = new ServiceResult<>(); + sg.setPage(page < 0 ? 0 : page); + sg.setPageSize(pageSize); + long iCnt = serviceGroupDao.getServiceGroupCount(filter); + sg.setCount(iCnt); + + if (iCnt > 0) { + int iStartIndex = pageSize < 0 ? -1 : page * pageSize; + List<DBServiceGroup> lst = serviceGroupDao.getServiceGroupList(iStartIndex, pageSize, sortField, sortOrder, filter); + List<ServiceGroupSearchRO> lstRo = new ArrayList<>(); + for (DBServiceGroup dbServiceGroup : lst) { + ServiceGroupSearchRO serviceGroupRo = convertToRo(dbServiceGroup); + serviceGroupRo.setIndex(iStartIndex++); + lstRo.add(serviceGroupRo); + } + sg.getServiceEntities().addAll(lstRo); + } + return sg; + } + + /** + * Convert Database object to Rest object for UI + * + * @param dbServiceGroup - database entity + * @return ServiceGroupRO + */ + public ServiceGroupSearchRO convertToRo(DBServiceGroup dbServiceGroup) { + ServiceGroupSearchRO serviceGroupRo = new ServiceGroupSearchRO(); + serviceGroupRo.setId(dbServiceGroup.getId()); + serviceGroupRo.setParticipantIdentifier(dbServiceGroup.getParticipantIdentifier()); + serviceGroupRo.setParticipantScheme(dbServiceGroup.getParticipantScheme()); + dbServiceGroup.getServiceGroupDomains().forEach(sgd -> { + DomainRO dmn = new DomainRO(); + sgd.getServiceMetadata().forEach(sgmd -> { + ServiceMetadataRO smdro = new ServiceMetadataRO(); + smdro.setDocumentIdentifier(sgmd.getDocumentIdentifier()); + smdro.setDocumentIdentifierScheme(sgmd.getDocumentIdentifierScheme()); + smdro.setDomainCode(sgd.getDomain().getDomainCode()); + smdro.setSmlSubdomain(sgd.getDomain().getSmlSubdomain()); + serviceGroupRo.getServiceMetadata().add(smdro); + }); + }); + return serviceGroupRo; + } +} diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/ui/UIServiceGroupService.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/ui/UIServiceGroupService.java index de751e9a90ea02caff5eac49d39f6d8bbffa3328..0c1785d5ddf7ea4c58683017d2d1b880fc2cf8ca 100644 --- a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/ui/UIServiceGroupService.java +++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/ui/UIServiceGroupService.java @@ -1,26 +1,49 @@ package eu.europa.ec.edelivery.smp.services.ui; +import eu.europa.ec.edelivery.smp.conversion.CaseSensitivityNormalizer; +import eu.europa.ec.edelivery.smp.conversion.ExtensionConverter; +import eu.europa.ec.edelivery.smp.conversion.ServiceMetadataConverter; import eu.europa.ec.edelivery.smp.data.dao.BaseDao; +import eu.europa.ec.edelivery.smp.data.dao.DomainDao; import eu.europa.ec.edelivery.smp.data.dao.ServiceGroupDao; import eu.europa.ec.edelivery.smp.data.dao.UserDao; -import eu.europa.ec.edelivery.smp.data.model.DBServiceGroup; -import eu.europa.ec.edelivery.smp.data.model.DBUser; -import eu.europa.ec.edelivery.smp.data.ui.ServiceGroupRO; -import eu.europa.ec.edelivery.smp.data.ui.ServiceMetadataRO; -import eu.europa.ec.edelivery.smp.data.ui.ServiceResult; -import eu.europa.ec.edelivery.smp.data.ui.UserRO; +import eu.europa.ec.edelivery.smp.data.model.*; +import eu.europa.ec.edelivery.smp.data.ui.*; import eu.europa.ec.edelivery.smp.data.ui.enums.EntityROStatus; +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.ui.filters.ServiceGroupFilter; +import eu.europa.ec.smp.api.exceptions.XmlInvalidAgainstSchemaException; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.exception.ExceptionUtils; +import org.oasis_open.docs.bdxr.ns.smp._2016._05.DocumentIdentifier; +import org.oasis_open.docs.bdxr.ns.smp._2016._05.ParticipantIdentifierType; +import org.oasis_open.docs.bdxr.ns.smp._2016._05.ServiceMetadata; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import java.time.LocalDateTime; +import javax.xml.transform.*; +import javax.xml.transform.stream.StreamResult; +import javax.xml.transform.stream.StreamSource; +import java.io.StringReader; +import java.io.StringWriter; +import java.io.UnsupportedEncodingException; import java.util.ArrayList; import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.stream.Collectors; + +import static eu.europa.ec.edelivery.smp.exceptions.ErrorCode.*; @Service public class UIServiceGroupService extends UIServiceBase<DBServiceGroup, ServiceGroupRO> { + private static final SMPLogger LOG = SMPLoggerFactory.getLogger(UIServiceGroupService.class); + @Autowired + DomainDao domainDao; @Autowired ServiceGroupDao serviceGroupDao; @@ -28,6 +51,10 @@ public class UIServiceGroupService extends UIServiceBase<DBServiceGroup, Service @Autowired UserDao userDao; + @Autowired + private CaseSensitivityNormalizer caseSensitivityNormalizer; + + @Override protected BaseDao<DBServiceGroup> getDatabaseDao() { return serviceGroupDao; @@ -45,49 +72,25 @@ public class UIServiceGroupService extends UIServiceBase<DBServiceGroup, Service */ @Transactional public ServiceResult<ServiceGroupRO> getTableList(int page, int pageSize, - String sortField, - String sortOrder, Object filter) { + String sortField, + String sortOrder, ServiceGroupFilter filter) { ServiceResult<ServiceGroupRO> sg = new ServiceResult<>(); sg.setPage(page < 0 ? 0 : page); sg.setPageSize(pageSize); - long iCnt = getDatabaseDao().getDataListCount(filter); + long iCnt = serviceGroupDao.getServiceGroupCount(filter); sg.setCount(iCnt); if (iCnt > 0) { int iStartIndex = pageSize < 0 ? -1 : page * pageSize; - List<DBServiceGroup> lst = getDatabaseDao().getDataList(iStartIndex, pageSize, sortField, sortOrder, filter); - + List<DBServiceGroup> lst = serviceGroupDao.getServiceGroupList(iStartIndex, pageSize, sortField, sortOrder, filter); List<ServiceGroupRO> lstRo = new ArrayList<>(); for (DBServiceGroup dbServiceGroup : lst) { - ServiceGroupRO serviceGroupRo = new ServiceGroupRO(); + ServiceGroupRO serviceGroupRo = convertToRo(dbServiceGroup); + serviceGroupRo.setStatus(EntityROStatus.PERSISTED.getStatusNumber()); serviceGroupRo.setIndex(iStartIndex++); - serviceGroupRo.setId(dbServiceGroup.getId()); - serviceGroupRo.setParticipantIdentifier(dbServiceGroup.getParticipantIdentifier()); - serviceGroupRo.setParticipantScheme(dbServiceGroup.getParticipantScheme()); - dbServiceGroup.getServiceGroupDomains().forEach(sgd -> { - sgd.getServiceMetadata().forEach(sgmd -> { - ServiceMetadataRO smdro = new ServiceMetadataRO(); - smdro.setDocumentIdentifier(sgmd.getDocumentIdentifier()); - smdro.setDocumentIdentifierScheme(sgmd.getDocumentIdentifierScheme()); - smdro.setDomainCode(sgd.getDomain().getDomainCode()); - smdro.setSmlSubdomain(sgd.getDomain().getSmlSubdomain()); - serviceGroupRo.getServiceMetadata().add(smdro); - }); - }); - // add users - dbServiceGroup.getUsers().forEach(usr->{ - UserRO userRO = new UserRO(); - userRO.setId(usr.getId()); - userRO.setUsername(usr.getUsername()); - userRO.setActive(usr.isActive()); - userRO.setEmail(usr.getEmail()); - userRO.setRole(usr.getRole()); - serviceGroupRo.getUsers().add(userRO); - }); lstRo.add(serviceGroupRo); } - sg.getServiceEntities().addAll(lstRo); } return sg; @@ -96,74 +99,497 @@ public class UIServiceGroupService extends UIServiceBase<DBServiceGroup, Service @Transactional public ServiceGroupRO getServiceGroupById(Long serviceGroupId) { DBServiceGroup dbServiceGroup = getDatabaseDao().find(serviceGroupId); + return convertToRo(dbServiceGroup); + } + + @Transactional + public ServiceGroupExtensionRO getServiceGroupExtensionById(Long serviceGroupId) { + ServiceGroupExtensionRO ex = new ServiceGroupExtensionRO(); + DBServiceGroup dbServiceGroup = getDatabaseDao().find(serviceGroupId); + ex.setServiceGroupId(dbServiceGroup.getId()); + if (dbServiceGroup.getExtension() != null) { + ex.setExtension(new String(dbServiceGroup.getExtension())); + } + return ex; + } + + @Transactional + public void updateServiceGroupList(List<ServiceGroupRO> lst) { + boolean suc = false; + for (ServiceGroupRO dRo : lst) { + if (dRo.getStatus() == EntityROStatus.NEW.getStatusNumber()) { + addNewServiceGroup(dRo); + } else if (dRo.getStatus() == EntityROStatus.UPDATED.getStatusNumber()) { + updateServiceGroup(dRo); + } else if (dRo.getStatus() == EntityROStatus.REMOVE.getStatusNumber()) { + DBServiceGroup upd = getDatabaseDao().find(dRo.getId()); + serviceGroupDao.removeServiceGroup(upd); + } + } + } + + /** + * Method validates and converts UI resource object entity to database entity and persists it to database + * + * @param serviceGroupRO + */ + private void addNewServiceGroup(ServiceGroupRO serviceGroupRO) { + // normalize indentifiers + normalizeIdentifiers(serviceGroupRO); + + DBServiceGroup dbServiceGroup = new DBServiceGroup(); + dbServiceGroup.setParticipantIdentifier(serviceGroupRO.getParticipantIdentifier()); + dbServiceGroup.setParticipantScheme(serviceGroupRO.getParticipantScheme()); + + // add users + updateUsersOnServiceGroup(serviceGroupRO, dbServiceGroup); + + // first update domains + // validate (if domains are added only once) and create domain list for service group. + createDomainsForNewServiceGroup(serviceGroupRO, dbServiceGroup); + + + // sort service metadata by domain + List<ServiceMetadataRO> serviceMetadataROList = serviceGroupRO.getServiceMetadata(); + serviceMetadataROList.forEach(serviceMetadataRO -> { + // find the domain + Optional<DBServiceGroupDomain> dbServiceGroupDomain = dbServiceGroup.getServiceGroupForDomain(serviceMetadataRO.getDomainCode()); + if (dbServiceGroupDomain.isPresent()) { + dbServiceGroupDomain.get().addServiceMetadata(createServiceMetadataFromRo(serviceMetadataRO)); + } else { + throw new SMPRuntimeException(SG_NOT_REGISTRED_FOR_DOMAIN, serviceMetadataRO.getDomainCode(), + serviceGroupRO.getParticipantIdentifier(), serviceGroupRO.getParticipantScheme()); + } + }); + + // add extension + if (serviceGroupRO.getExtension() != null) { + byte[] buff = validateExtension(serviceGroupRO); + dbServiceGroup.setExtension(buff); + } + getDatabaseDao().persistFlushDetach(dbServiceGroup); + } + + private void normalizeIdentifiers(ServiceGroupRO sgo) { + ParticipantIdentifierType pti = caseSensitivityNormalizer.normalizeParticipant(sgo.getParticipantScheme() + "::" + sgo.getParticipantIdentifier()); + sgo.setParticipantScheme(pti.getScheme()); + sgo.setParticipantIdentifier(pti.getValue()); + sgo.getServiceMetadata().forEach(smd -> { + DocumentIdentifier dit = caseSensitivityNormalizer.normalizeDocumentIdentifier(smd.getDocumentIdentifierScheme(), smd.getDocumentIdentifier()); + smd.setDocumentIdentifierScheme(dit.getScheme()); + smd.setDocumentIdentifier(dit.getValue()); + + }); + + } + + /** + * Validate (if domains are added only once) and create domain list for service group. + * + * @param serviceGroupRO + * @param dbServiceGroup + */ + protected void createDomainsForNewServiceGroup(ServiceGroupRO serviceGroupRO, DBServiceGroup dbServiceGroup) { + // first update domains + List<ServiceGroupDomainRO> serviceGroupDomainROList = validateDomainList(serviceGroupRO); + // validate (if domains are added only once) and create domain list for service group. + serviceGroupDomainROList.forEach(dro -> { + // everting ok find domain and add it to service group + Optional<DBDomain> dmn = domainDao.getDomainByCode(dro.getDomainCode()); + if (dmn.isPresent()) { + dbServiceGroup.addDomain(dmn.get()); + } else { + throw new SMPRuntimeException(DOMAIN_NOT_EXISTS, dro.getDomainCode()); + } + }); + } + + + /** + * Method converts UI resource object entity to database entity and update changes to database + * + * @param serviceGroupRO + */ + protected void updateServiceGroup(ServiceGroupRO serviceGroupRO) { + // normalize indentifiers + normalizeIdentifiers(serviceGroupRO); + // find and validate service group + DBServiceGroup dbServiceGroup = findAndValidateServiceGroup(serviceGroupRO); + + // update users + updateUsersOnServiceGroup(serviceGroupRO, dbServiceGroup); + + // update domain + updateDomainsForServiceGroup(serviceGroupRO, dbServiceGroup); + + //update service metadata + List<ServiceMetadataRO> serviceMetadataROList = serviceGroupRO.getServiceMetadata(); + serviceMetadataROList.forEach(serviceMetadataRO -> { + + Optional<DBServiceGroupDomain> optionalDbServiceGroupDomain = + dbServiceGroup.findServiceGroupDomainForMetadata(serviceMetadataRO.getDocumentIdentifier(), serviceMetadataRO.getDocumentIdentifierScheme()); + // remove service metadata + if (serviceMetadataRO.getStatus() == EntityROStatus.REMOVE.getStatusNumber()) { + // if the domain was not removed then remove only metadata + if (optionalDbServiceGroupDomain.isPresent()) { + DBServiceGroupDomain dbServiceGroupDomain = optionalDbServiceGroupDomain.get(); + // remove from domain + dbServiceGroupDomain.removeServiceMetadata(serviceMetadataRO.getDocumentIdentifier(), + serviceMetadataRO.getDocumentIdentifierScheme()); + } + + } else if (serviceMetadataRO.getStatus() == EntityROStatus.NEW.getStatusNumber()) { + // add to new service group.. find servicegroup domain by code + optionalDbServiceGroupDomain = dbServiceGroup.getServiceGroupForDomain(serviceMetadataRO.getDomainCode()); + if (optionalDbServiceGroupDomain.isPresent()) { + optionalDbServiceGroupDomain.get().addServiceMetadata(createServiceMetadataFromRo(serviceMetadataRO)); + } else { + throw new SMPRuntimeException(SG_NOT_REGISTRED_FOR_DOMAIN, serviceMetadataRO.getDomainCode(), + serviceGroupRO.getParticipantIdentifier(), serviceGroupRO.getParticipantScheme()); + } + } else if (serviceMetadataRO.getStatus() == EntityROStatus.UPDATED.getStatusNumber()) { + if (optionalDbServiceGroupDomain.isPresent()) { + + DBServiceGroupDomain dbServiceGroupDomain = optionalDbServiceGroupDomain.get(); + if (serviceMetadataRO.getXmlContentStatus() == EntityROStatus.UPDATED.getStatusNumber()) { + // get service metadata + byte[] buff = validateServiceMetadata(serviceMetadataRO); + + DBServiceMetadata dbServiceMetadata = dbServiceGroupDomain.getServiceMetadata(serviceMetadataRO.getDocumentIdentifier(), + serviceMetadataRO.getDocumentIdentifierScheme()); + dbServiceMetadata.setXmlContent(buff); + } + + if (!Objects.equals(serviceMetadataRO.getDomainCode(), dbServiceGroupDomain.getDomain().getDomainCode())) { + // remove from old doman + DBServiceMetadata smd = dbServiceGroupDomain.removeServiceMetadata(serviceMetadataRO.getDocumentIdentifier(), + serviceMetadataRO.getDocumentIdentifierScheme()); + // find new domain and add + Optional<DBServiceGroupDomain> optNewDomain = dbServiceGroup.getServiceGroupForDomain(serviceMetadataRO.getDomainCode()); + if (optNewDomain.isPresent()) { + optNewDomain.get().addServiceMetadata(smd); + } else { + throw new SMPRuntimeException(SG_NOT_REGISTRED_FOR_DOMAIN, serviceMetadataRO.getDomainCode(), + serviceGroupRO.getParticipantIdentifier(), serviceGroupRO.getParticipantScheme()); + } + } + } else { + throw new SMPRuntimeException(SG_NOT_REGISTRED_FOR_DOMAIN, serviceMetadataRO.getDomainCode(), + serviceGroupRO.getParticipantIdentifier(), serviceGroupRO.getParticipantScheme()); + } + } + + }); + + // + // add extension + if (serviceGroupRO.getExtensionStatus() != EntityROStatus.PERSISTED.getStatusNumber()) { + byte[] buff = validateExtension(serviceGroupRO); + dbServiceGroup.setExtension(buff); + } + + + // persist it to databse + getDatabaseDao().update(dbServiceGroup); + } + + /** + * Validate (if domains are added only once) and update domain list for service group. + * + * @param serviceGroupRO + * @param dbServiceGroup + */ + protected void updateDomainsForServiceGroup(ServiceGroupRO serviceGroupRO, DBServiceGroup dbServiceGroup) { + // / validate (if domains are added only once) and create domain list for service group. + List<ServiceGroupDomainRO> serviceGroupDomainROList = validateDomainList(serviceGroupRO); + // copy array list of old domains and then put them back. Domain not added back will be deleted by hibernate + // ... + List<DBServiceGroupDomain> lstOldSGDomains = new ArrayList<>(); + lstOldSGDomains.addAll(dbServiceGroup.getServiceGroupDomains()); + dbServiceGroup.getServiceGroupDomains().clear(); + + + serviceGroupDomainROList.forEach(serviceGroupDomainRO -> { + DBServiceGroupDomain dsg = getSGDomainFromList(lstOldSGDomains, serviceGroupDomainRO); + if (dsg != null) { + // put it back - no need to call addDomain + dbServiceGroup.getServiceGroupDomains().add(dsg); + // remove from list + lstOldSGDomains.remove(dsg); + } else { + // everting ok find domain and add it to service group + Optional<DBDomain> dmn = domainDao.getDomainByCode(serviceGroupDomainRO.getDomainCode()); + if (dmn.isPresent()) { + dbServiceGroup.addDomain(dmn.get()); + } else { + throw new SMPRuntimeException(DOMAIN_NOT_EXISTS, serviceGroupDomainRO.getDomainCode()); + } + } + }); + // remove references + lstOldSGDomains.forEach(dbServiceGroupDomain -> { + dbServiceGroupDomain.setServiceGroup(null); + }); + } + + /** + * Method validates if domain list in consistent - code and sml subdomain are used only oncet + * + * @param serviceGroupRO + * @return + */ + protected List<ServiceGroupDomainRO> validateDomainList(ServiceGroupRO serviceGroupRO) { + List<ServiceGroupDomainRO> serviceGroupDomainROList = serviceGroupRO.getServiceGroupDomains(); + // validate (if domains are added only once) and create domain list for service group. + serviceGroupDomainROList.forEach(dro -> { + List<ServiceGroupDomainRO> result = serviceGroupDomainROList.stream() + .filter(domainToAdd -> Objects.equals(domainToAdd.getDomainCode(), dro.getDomainCode()) + || Objects.equals(domainToAdd.getSmlSubdomain(), dro.getSmlSubdomain())) + .collect(Collectors.toList()); + if (result.size() != 1) { + throw new SMPRuntimeException(DUPLICATE_DOMAIN_FOR_SG, serviceGroupRO.getParticipantIdentifier(), + serviceGroupRO.getParticipantScheme(), dro.getDomainCode(), dro.getSmlSubdomain()); + } + }); + return serviceGroupDomainROList; + } + + /** + * Update users on service group. Method is OK for update and add new domain + * + * @param serviceGroupRO + * @param dbServiceGroup + */ + protected void updateUsersOnServiceGroup(ServiceGroupRO serviceGroupRO, DBServiceGroup dbServiceGroup) { + // update users + dbServiceGroup.getUsers().clear(); + List<UserRO> lstUsers = serviceGroupRO.getUsers(); + for (UserRO userRO : lstUsers) { + DBUser du = userDao.find(userRO.getId()); + dbServiceGroup.getUsers().add(du); + } + } + + /** + * Method retrieve servicegroup data from database and validates id and participant + * + * @param serviceGroupRO + * @return + */ + private DBServiceGroup findAndValidateServiceGroup(ServiceGroupRO serviceGroupRO) { + // find and validate service group + if (serviceGroupRO.getId() == null) { + throw new SMPRuntimeException(MISSING_SG_ID, serviceGroupRO.getParticipantIdentifier(), serviceGroupRO.getParticipantScheme()); + } + // validate service group id + DBServiceGroup dbServiceGroup = getDatabaseDao().find(serviceGroupRO.getId()); + if (!Objects.equals(serviceGroupRO.getParticipantIdentifier(), dbServiceGroup.getParticipantIdentifier()) + || !Objects.equals(serviceGroupRO.getParticipantScheme(), dbServiceGroup.getParticipantScheme())) { + throw new SMPRuntimeException(INVALID_SG_ID, serviceGroupRO.getParticipantIdentifier(), + serviceGroupRO.getParticipantScheme(), serviceGroupRO.getId()); + } + return dbServiceGroup; + } + + /** + * Check if service metadata parsers and if data match servicemetadata and service group... + * + * @param serviceMetadataRO + * @return + */ + private byte[] validateServiceMetadata(ServiceMetadataRO serviceMetadataRO) { + byte[] buff; + + try { + buff = serviceMetadataRO.getXmlContent().getBytes("UTF-8"); + } catch (UnsupportedEncodingException e) { + throw new SMPRuntimeException(INVALID_ENCODING, "UTF-8"); + } + ServiceMetadata smd = ServiceMetadataConverter.unmarshal(buff); + DocumentIdentifier di = caseSensitivityNormalizer.normalize(smd.getServiceInformation().getDocumentIdentifier()); + if (Objects.equals(di.getScheme(), serviceMetadataRO.getDocumentIdentifierScheme()) + && Objects.equals(di.getValue(), serviceMetadataRO.getDocumentIdentifier())) { + return buff; + } else { + throw new SMPRuntimeException(IVALID_SMD_DOCUMENT_DATA, di.getValue(), di.getScheme(), + serviceMetadataRO.getDocumentIdentifier(), serviceMetadataRO.getDocumentIdentifierScheme()); + } + } + + + /** + * Convert Database object to Rest object for UI. It does not set blobs - extensions and metadataservice xml! + * They are retrieved to UI when needed. + * + * @param dbServiceGroup - database entity + * @return ServiceGroupRO + */ + public ServiceGroupRO convertToRo(DBServiceGroup dbServiceGroup) { ServiceGroupRO serviceGroupRo = new ServiceGroupRO(); serviceGroupRo.setId(dbServiceGroup.getId()); serviceGroupRo.setParticipantIdentifier(dbServiceGroup.getParticipantIdentifier()); serviceGroupRo.setParticipantScheme(dbServiceGroup.getParticipantScheme()); - // add service groups + // add domains dbServiceGroup.getServiceGroupDomains().forEach(sgd -> { - sgd.getServiceMetadata().forEach(sgmd -> { - ServiceMetadataRO smdro = new ServiceMetadataRO(); - smdro.setDocumentIdentifier(sgmd.getDocumentIdentifier()); - smdro.setDocumentIdentifierScheme(sgmd.getDocumentIdentifierScheme()); - smdro.setDomainCode(sgd.getDomain().getDomainCode()); - smdro.setSmlSubdomain(sgd.getDomain().getSmlSubdomain()); - serviceGroupRo.getServiceMetadata().add(smdro); - }); + ServiceGroupDomainRO servGrpDomain = new ServiceGroupDomainRO(); + servGrpDomain.setId(sgd.getId()); + servGrpDomain.setDomainId(sgd.getDomain().getId()); + servGrpDomain.setDomainCode(sgd.getDomain().getDomainCode()); + servGrpDomain.setSmlSubdomain(sgd.getDomain().getSmlSubdomain()); + // add service metadata to service group NOT TO service group domain + // little different view from DB Model - all for the users :) .. + sgd.getServiceMetadata().stream().map(this::convertServiceMetadataToRo) + .forEach(smdro -> { + smdro.setSmlSubdomain(servGrpDomain.getSmlSubdomain()); + smdro.setDomainCode(servGrpDomain.getDomainCode()); + smdro.setServiceGroupDomainId(servGrpDomain.getId()); + serviceGroupRo.getServiceMetadata().add(smdro); + }); + //also add domain to service group + serviceGroupRo.getServiceGroupDomains().add(servGrpDomain); }); // add users - dbServiceGroup.getUsers().forEach(usr->{ + dbServiceGroup.getUsers().forEach(usr -> { UserRO userRO = new UserRO(); userRO.setId(usr.getId()); userRO.setUsername(usr.getUsername()); userRO.setActive(usr.isActive()); - userRO.setEmail(usr.getEmail()); + userRO.setEmailAddress(usr.getEmailAddress()); userRO.setRole(usr.getRole()); serviceGroupRo.getUsers().add(userRO); }); + // do not add service extension to gain performance. return serviceGroupRo; } - @Transactional - public void updateServiceGroupList(List<ServiceGroupRO> lst) { - boolean suc = false; - for (ServiceGroupRO dRo: lst){ + /** + * Convert database entity to resource object ServiceMetadataRO. To gain UI performance do not copy XM for UI. + * It is retrieved when needed! + * + * @param sgmd + * @return + */ + private ServiceMetadataRO convertServiceMetadataToRo(DBServiceMetadata sgmd) { + ServiceMetadataRO smdro = new ServiceMetadataRO(); + smdro.setId(sgmd.getId()); + smdro.setDocumentIdentifier(sgmd.getDocumentIdentifier()); + smdro.setDocumentIdentifierScheme(sgmd.getDocumentIdentifierScheme()); + return smdro; + } + /** + * Create new database entity - service metadata from resource object + * + * @param serviceMetadataRO + * @return new database entity DBServiceMetadata + */ + private DBServiceMetadata createServiceMetadataFromRo(ServiceMetadataRO serviceMetadataRO) { - if (dRo.getStatus() == EntityROStatus.NEW.getStatusNumber()) { - DBServiceGroup dDb = convertFromRo(dRo); - for (UserRO userRO: dRo.getUsers()) { - System.out.println("GET USER ID: " + userRO.getId()); - DBUser du = userDao.find(userRO.getId()); - dDb.getUsers().add(du); + byte[] buff = validateServiceMetadata(serviceMetadataRO); + DBServiceMetadata dbServiceMetadata = new DBServiceMetadata(); + dbServiceMetadata.setDocumentIdentifier(serviceMetadataRO.getDocumentIdentifier()); + dbServiceMetadata.setDocumentIdentifierScheme(serviceMetadataRO.getDocumentIdentifierScheme()); + dbServiceMetadata.setXmlContent(buff); - } - getDatabaseDao().persistFlushDetach(dDb); - } else if (dRo.getStatus() == EntityROStatus.UPDATED.getStatusNumber()) { - DBServiceGroup upd = getDatabaseDao().find(dRo.getId()); - upd.getUsers().clear(); - for (UserRO userRO: dRo.getUsers()) { - System.out.println("GET USER ID: " + userRO.getId()); - DBUser du = userDao.find(userRO.getId()); - upd.getUsers().add(du); + return dbServiceMetadata; + } + + /** + * for ServiceGroupDomainRO returns DBServiceGroupDomain from ServiceGroup list of domain. ServiceGroup domain is matched by Id + * and verified by domain id. + * + * @param lstSGDomains + * @param domainRo + * @return + */ + private DBServiceGroupDomain getSGDomainFromList(List<DBServiceGroupDomain> lstSGDomains, ServiceGroupDomainRO domainRo) { + for (DBServiceGroupDomain dbServiceGroupDomain : lstSGDomains) { + if (Objects.equals(dbServiceGroupDomain.getId(), domainRo.getId())) { + // double check for domain + if (!Objects.equals(dbServiceGroupDomain.getDomain().getId(), domainRo.getDomainId())) { + throw new SMPRuntimeException(INVALID_REQEUST, "Domain mismatch!", "Domain id for does not match for servicegroup domain"); } - // only servicegroup users can be changed__ - /* - upd.setSmlSmpId(dRo.getSmlSmpId()); - upd.setSmlClientKeyAlias(dRo.getSmlClientKeyAlias()); - upd.setSmlClientCertHeader(dRo.getSmlClientCertHeader()); - upd.setSmlParticipantIdentifierRegExp(dRo.getSmlParticipantIdentifierRegExp()); - upd.setSmlSubdomain(dRo.getSmlSubdomain()); - upd.setDomainCode(dRo.getDomainCode()); - upd.setSignatureKeyAlias(dRo.getSignatureKeyAlias()); - upd.setLastUpdatedOn(LocalDateTime.now());*/ - getDatabaseDao().update(upd); - } else if (dRo.getStatus() == EntityROStatus.REMOVE.getStatusNumber()) { - DBServiceGroup upd = getDatabaseDao().find(dRo.getId()); - serviceGroupDao.removeServiceGroup(upd); + return dbServiceGroupDomain; + } + } + return null; + } + + /** + * Validate if extension is valid by schema. + * + * @param sgExtension + * @return + */ + public ServiceGroupExtensionRO validateExtension(ServiceGroupExtensionRO sgExtension) { + if (sgExtension == null) { + throw new SMPRuntimeException(INVALID_REQEUST, "Validate extension", "Missing Extension parameter"); + } else if (StringUtils.isBlank(sgExtension.getExtension())) { + sgExtension.setErrorMessage("Empty extension"); + } else { + try { + byte[] buff = sgExtension.getExtension().getBytes("UTF-8"); + ExtensionConverter.validateExtensionBySchema(buff); // validate by schema + sgExtension.setErrorMessage(null); + } catch (XmlInvalidAgainstSchemaException e) { + sgExtension.setErrorMessage(ExceptionUtils.getRootCauseMessage(e)); + } catch (UnsupportedEncodingException e) { + sgExtension.setErrorMessage(ExceptionUtils.getRootCauseMessage(e)); + } + } + return sgExtension; + } + + /** + * Validate if extension is valid by schema. + * + * @param serviceGroupRO + * @return + */ + public byte[] validateExtension(ServiceGroupRO serviceGroupRO) { + if (StringUtils.isBlank(serviceGroupRO.getExtension())) { + return null; + } + try { + byte[] buff = serviceGroupRO.getExtension().getBytes("UTF-8"); + ExtensionConverter.validateExtensionBySchema(buff); // validate by schema + return buff; + } catch (UnsupportedEncodingException | XmlInvalidAgainstSchemaException e) { + throw new SMPRuntimeException(INVALID_EXTENSION_FOR_SG, serviceGroupRO.getParticipantIdentifier(), + serviceGroupRO.getParticipantScheme(), ExceptionUtils.getRootCauseMessage(e)); + } + } + + /** + * TODO format extension - add root element and format... + * + * @param sgExtension + * @return + */ + public ServiceGroupExtensionRO formatExtension(ServiceGroupExtensionRO sgExtension) { + if (sgExtension == null) { + throw new SMPRuntimeException(INVALID_REQEUST, "Format extension", "Missing Extension parameter"); + } else if (StringUtils.isBlank(sgExtension.getExtension())) { + sgExtension.setErrorMessage("Empty extension"); + } else { + try { + Source xmlInput = new StreamSource(new StringReader(sgExtension.getExtension())); + StringWriter stringWriter = new StringWriter(); + StreamResult xmlOutput = new StreamResult(stringWriter); + TransformerFactory transformerFactory = TransformerFactory.newInstance(); + transformerFactory.setAttribute("indent-number", 4); + Transformer transformer = transformerFactory.newTransformer(); + transformer.setOutputProperty(OutputKeys.INDENT, "yes"); + transformer.transform(xmlInput, xmlOutput); + sgExtension.setExtension(xmlOutput.getWriter().toString()); + } catch (TransformerConfigurationException e) { + sgExtension.setErrorMessage(ExceptionUtils.getRootCauseMessage(e)); + } catch (TransformerException e) { + sgExtension.setErrorMessage(ExceptionUtils.getRootCauseMessage(e)); } } + return sgExtension; } } diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/ui/UIServiceMetadataService.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/ui/UIServiceMetadataService.java new file mode 100644 index 0000000000000000000000000000000000000000..2eabaffae4517bbe53e59a97d0c80833cf295242 --- /dev/null +++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/ui/UIServiceMetadataService.java @@ -0,0 +1,112 @@ +package eu.europa.ec.edelivery.smp.services.ui; + +import eu.europa.ec.edelivery.smp.conversion.CaseSensitivityNormalizer; +import eu.europa.ec.edelivery.smp.conversion.ServiceMetadataConverter; +import eu.europa.ec.edelivery.smp.data.dao.BaseDao; +import eu.europa.ec.edelivery.smp.data.dao.DomainDao; +import eu.europa.ec.edelivery.smp.data.dao.ServiceMetadataDao; +import eu.europa.ec.edelivery.smp.data.dao.UserDao; +import eu.europa.ec.edelivery.smp.data.model.DBServiceMetadata; +import eu.europa.ec.edelivery.smp.data.ui.ServiceMetadataRO; +import eu.europa.ec.edelivery.smp.data.ui.ServiceMetadataValidationRO; +import eu.europa.ec.edelivery.smp.logging.SMPLogger; +import eu.europa.ec.edelivery.smp.logging.SMPLoggerFactory; +import eu.europa.ec.smp.api.exceptions.XmlInvalidAgainstSchemaException; +import eu.europa.ec.smp.api.validators.BdxSmpOasisValidator; +import org.apache.commons.lang3.exception.ExceptionUtils; +import org.oasis_open.docs.bdxr.ns.smp._2016._05.DocumentIdentifier; +import org.oasis_open.docs.bdxr.ns.smp._2016._05.ParticipantIdentifierType; +import org.oasis_open.docs.bdxr.ns.smp._2016._05.ServiceMetadata; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.io.UnsupportedEncodingException; + +@Service +public class UIServiceMetadataService extends UIServiceBase<DBServiceMetadata, ServiceMetadataRO> { + private static final SMPLogger LOG = SMPLoggerFactory.getLogger(UIServiceMetadataService.class); + + @Autowired + DomainDao domainDao; + + @Autowired + ServiceMetadataDao serviceMetadataDao; + + + @Autowired + UserDao userDao; + + @Autowired + private CaseSensitivityNormalizer caseSensitivityNormalizer; + + + @Override + protected BaseDao<DBServiceMetadata> getDatabaseDao() { + return serviceMetadataDao; + } + + @Transactional + public ServiceMetadataRO getServiceMetadataXMLById(Long serviceMetadataId) { + DBServiceMetadata dbServiceMetadata = serviceMetadataDao.find(serviceMetadataId); + ServiceMetadataRO serviceMetadataRO = new ServiceMetadataRO(); + + serviceMetadataRO.setId(dbServiceMetadata.getId()); + serviceMetadataRO.setDocumentIdentifier(dbServiceMetadata.getDocumentIdentifier()); + serviceMetadataRO.setDocumentIdentifierScheme(dbServiceMetadata.getDocumentIdentifierScheme()); + serviceMetadataRO.setXmlContent(new String(dbServiceMetadata.getXmlContent())); + return serviceMetadataRO; + } + + /** + * Check if service metadata parsers and if data match servicemetadata and service group... + * + * @param serviceMetadataRO + * @return + */ + + public ServiceMetadataValidationRO validateServiceMetadata(ServiceMetadataValidationRO serviceMetadataRO) { + byte[] buff; + + // convert to utf-8 byte array + try { + buff = serviceMetadataRO.getXmlContent().getBytes("UTF-8"); + serviceMetadataRO.setXmlContent(""); // no need to return back schema + } catch (UnsupportedEncodingException e) { + serviceMetadataRO.setErrorMessage(ExceptionUtils.getRootCauseMessage(e)); + serviceMetadataRO.setXmlContent(""); // no need to return back schema + return serviceMetadataRO; + } + + // validate by schema + try { + BdxSmpOasisValidator.validateXSD(buff); + } catch (XmlInvalidAgainstSchemaException e) { + serviceMetadataRO.setErrorMessage(ExceptionUtils.getRootCauseMessage(e)); + return serviceMetadataRO; + } + + // validate data + ServiceMetadata smd = ServiceMetadataConverter.unmarshal(buff); + DocumentIdentifier xmlDI = caseSensitivityNormalizer.normalize(smd.getServiceInformation().getDocumentIdentifier()); + DocumentIdentifier headerDI = caseSensitivityNormalizer.normalizeDocumentIdentifier(serviceMetadataRO.getDocumentIdentifierScheme(), + serviceMetadataRO.getDocumentIdentifier()); + ParticipantIdentifierType xmlPI = caseSensitivityNormalizer.normalize(smd.getServiceInformation().getParticipantIdentifier()); + ParticipantIdentifierType headerPI = caseSensitivityNormalizer.normalizeParticipantIdentifier( + serviceMetadataRO.getParticipantScheme(), + serviceMetadataRO.getParticipantIdentifier()); + + if (!xmlDI.equals(headerDI)) { + serviceMetadataRO.setErrorMessage("Document identifier and schema does not match!"); + return serviceMetadataRO; + } + + if (!xmlPI.equals(headerPI)) { + serviceMetadataRO.setErrorMessage("Participant identifier and schema does not match!"); + return serviceMetadataRO; + } + + return serviceMetadataRO; + } + +} 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 bceb3a0716e1c4aa685fc254517887e1faa1940f..abdffd9e233b34d8f0852c96d74caeec1bd7d0a7 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 @@ -10,8 +10,6 @@ import eu.europa.ec.edelivery.smp.data.ui.CertificateRO; import eu.europa.ec.edelivery.smp.data.ui.ServiceResult; import eu.europa.ec.edelivery.smp.data.ui.UserRO; import eu.europa.ec.edelivery.smp.data.ui.enums.EntityROStatus; -import eu.europa.ec.edelivery.smp.exceptions.ErrorCode; -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 org.apache.commons.beanutils.BeanUtils; @@ -21,18 +19,18 @@ import org.springframework.security.crypto.bcrypt.BCrypt; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import javax.xml.bind.DatatypeConverter; import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; import java.lang.reflect.InvocationTargetException; import java.math.BigInteger; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.security.cert.CertificateEncodingException; import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; import java.time.LocalDateTime; import java.time.ZoneId; +import java.util.Base64; import java.util.List; @Service @@ -40,6 +38,9 @@ public class UIUserService extends UIServiceBase<DBUser, UserRO> { private static final SMPLogger LOG = SMPLoggerFactory.getLogger(UIUserService.class); + private static final byte[] S_PEM_START_TAG= "-----BEGIN CERTIFICATE-----\n".getBytes(); + private static final byte[] S_PEM_END_TAG= "\n-----END CERTIFICATE-----".getBytes(); + @Autowired UserDao userDao; @@ -49,21 +50,21 @@ public class UIUserService extends UIServiceBase<DBUser, UserRO> { } /** - * Method returns user resource object list for page. + * Method returns user resource object list for UI list page. * * @param page * @param pageSize * @param sortField * @param sortOrder * @param filter - * @return + * @return ServiceResult wiht list */ @Transactional public ServiceResult<UserRO> getTableList(int page, int pageSize, - String sortField, - String sortOrder, Object filter) { + String sortField, + String sortOrder, Object filter) { - ServiceResult<UserRO> resUsers = super.getTableList(page, pageSize, sortField, sortOrder, filter); + ServiceResult<UserRO> resUsers = super.getTableList(page, pageSize, sortField, sortOrder, filter); resUsers.getServiceEntities().forEach(usr -> usr.setPassword(null)); return resUsers; } @@ -78,7 +79,7 @@ public class UIUserService extends UIServiceBase<DBUser, UserRO> { userDao.persistFlushDetach(dbUser); } else if (userRO.getStatus() == EntityROStatus.UPDATED.getStatusNumber()) { DBUser dbUser = userDao.find(userRO.getId()); - dbUser.setEmail(userRO.getEmail()); + dbUser.setEmailAddress(userRO.getEmailAddress()); dbUser.setRole(userRO.getRole()); dbUser.setActive(userRO.isActive()); // check for new password @@ -116,31 +117,65 @@ public class UIUserService extends UIServiceBase<DBUser, UserRO> { } } - public CertificateRO getCertificateData(byte[] buff) throws CertificateException{ - - CertificateFactory fact = null; - - fact = CertificateFactory.getInstance("X.509"); - ByteArrayInputStream is = new ByteArrayInputStream(buff); - X509Certificate cert = (X509Certificate) fact.generateCertificate(is); - String subject = cert.getSubjectDN().getName(); - String issuer = cert.getIssuerDN().getName(); - String hash = cert.getIssuerDN().getName(); - BigInteger serial = cert.getSerialNumber(); - String certId = getCertificateIdFromCertificate(subject,issuer, serial ); - CertificateRO cro = new CertificateRO(); - cro.setCertificateId(certId); - cro.setSubject(subject); - cro.setIssuer(issuer); - // set serial as HEX - cro.setSerialNumber(serial.toString(16)); - cro.setValidFrom(LocalDateTime.ofInstant(cert.getNotBefore().toInstant(), ZoneId.systemDefault())); - cro.setValidTo(LocalDateTime.ofInstant(cert.getNotAfter().toInstant(), ZoneId.systemDefault())); - - return cro; + public CertificateRO getCertificateData(byte[] buff) throws CertificateException, IOException { + + // get pem encoding - + InputStream isCert = createPEMFormat(buff); + + CertificateFactory fact = CertificateFactory.getInstance("X.509"); + X509Certificate cert = (X509Certificate) fact.generateCertificate(isCert); + String subject = cert.getSubjectDN().getName(); + String issuer = cert.getIssuerDN().getName(); + String hash = cert.getIssuerDN().getName(); + BigInteger serial = cert.getSerialNumber(); + String certId = getCertificateIdFromCertificate(subject, issuer, serial); + CertificateRO cro = new CertificateRO(); + cro.setCertificateId(certId); + cro.setSubject(subject); + cro.setIssuer(issuer); + // set serial as HEX + cro.setSerialNumber(serial.toString(16)); + cro.setValidFrom(LocalDateTime.ofInstant(cert.getNotBefore().toInstant(), ZoneId.systemDefault())); + cro.setValidTo(LocalDateTime.ofInstant(cert.getNotAfter().toInstant(), ZoneId.systemDefault())); + cro.setEncodedValue(Base64.getMimeEncoder().encodeToString(cert.getEncoded())); + + return cro; + } + + public boolean isCertificatePemEncoded(byte[] certData){ + if (certData!=null && certData.length >S_PEM_START_TAG.length){ + + for (int i=0; i<certData.length;i++){ + if (certData[i] != S_PEM_START_TAG[i]) { + return false; + } + return true; + } + } + return false; + } + + /** + * Method tests if certificate is in PEM format. If not it creates pem format else returns original data. + * @param certData - certificate data + * @return + * @throws IOException + */ + public ByteArrayInputStream createPEMFormat(byte[] certData) throws IOException { + ByteArrayInputStream is; + if (isCertificatePemEncoded(certData)){ + is = new ByteArrayInputStream(certData); + } else { + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + bos.write(S_PEM_START_TAG); + bos.write(Base64.getMimeEncoder().encode(certData)); + bos.write(S_PEM_END_TAG); + is = new ByteArrayInputStream(bos.toByteArray()); + } + return is; } - public String getCertificateIdFromCertificate(String subject, String issuer, BigInteger serial ){ + public String getCertificateIdFromCertificate(String subject, String issuer, BigInteger serial) { return new PreAuthenticatedCertificatePrincipal(subject, issuer, serial).getName(); } @@ -150,15 +185,15 @@ public class UIUserService extends UIServiceBase<DBUser, UserRO> { UserRO dro = new UserRO(); BeanUtils.copyProperties(dro, d); - if (d.getCertificate()!=null) { + if (d.getCertificate() != null) { CertificateRO certData = new CertificateRO(); BeanUtils.copyProperties(certData, d.getCertificate()); dro.setCertificateData(certData); } return dro; - } catch ( InvocationTargetException | IllegalAccessException e) { - String msg = "Error occurred while converting to RO Entity for " +UserRO.class.getName(); - LOG.error(msg, e ); + } catch (InvocationTargetException | IllegalAccessException e) { + String msg = "Error occurred while converting to RO Entity for " + UserRO.class.getName(); + LOG.error(msg, e); throw new RuntimeException(msg, e); } } @@ -169,16 +204,16 @@ public class UIUserService extends UIServiceBase<DBUser, UserRO> { DBUser dro = new DBUser(); BeanUtils.copyProperties(dro, d); DBCertificate cert = new DBCertificate(); - if (d.getCertificateData()!=null) { + if (d.getCertificateData() != null) { DBCertificate certData = new DBCertificate(); BeanUtils.copyProperties(certData, d.getCertificateData()); dro.setCertificate(cert); } return dro; - } catch ( InvocationTargetException | IllegalAccessException e) { - String msg = "Error occurred while converting to RO Entity for " +UserRO.class.getName(); - LOG.error(msg, e ); + } catch (InvocationTargetException | IllegalAccessException e) { + String msg = "Error occurred while converting to RO Entity for " + UserRO.class.getName(); + LOG.error(msg, e); throw new RuntimeException(msg, e); } } diff --git a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/filters/ServiceGroupFilter.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/ui/filters/ServiceGroupFilter.java similarity index 53% rename from smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/filters/ServiceGroupFilter.java rename to smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/ui/filters/ServiceGroupFilter.java index 8e2fa893ed94f3dbba4fd4eeb41528e51bd900da..9aeac826fa6ceb97580a2d14cb0957f47f88f3d8 100644 --- a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/filters/ServiceGroupFilter.java +++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/ui/filters/ServiceGroupFilter.java @@ -1,9 +1,15 @@ -package eu.europa.ec.edelivery.smp.ui.filters; +package eu.europa.ec.edelivery.smp.services.ui.filters; + +import eu.europa.ec.edelivery.smp.data.model.DBDomain; +import eu.europa.ec.edelivery.smp.data.model.DBUser; public class ServiceGroupFilter { private String participantIdentifier; private String participantScheme; + private DBUser owner; + private DBDomain domain; + public String getParticipantIdentifierLike() { return participantIdentifier; } @@ -19,4 +25,20 @@ public class ServiceGroupFilter { public void setParticipantSchemeLike(String participantScheme) { this.participantScheme = participantScheme; } + + public DBUser getOwner() { + return owner; + } + + public void setOwner(DBUser owner) { + this.owner = owner; + } + + public DBDomain getDomain() { + return domain; + } + + public void setDomain(DBDomain domain) { + this.domain = domain; + } } diff --git a/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/conversion/ExtensionConverterTest.java b/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/conversion/ExtensionConverterTest.java index 5487b68d67fbddcfe2eda0d83809a1b9df33cfbe..0312a757926c3b0b9f0b45824a21b89ba8f6597f 100644 --- a/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/conversion/ExtensionConverterTest.java +++ b/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/conversion/ExtensionConverterTest.java @@ -27,7 +27,9 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; -import static org.junit.Assert.*; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertThat; /** * Created by migueti on 13/02/2017. @@ -36,7 +38,7 @@ public class ExtensionConverterTest { private static final String WRAPPED_FORMAT = "<ExtensionsWrapper xmlns=\"http://docs.oasis-open.org/bdxr/ns/SMP/2016/05\">%s</ExtensionsWrapper>"; - private static final String RES_PATH = "/examples/extensions/"; + public static final String RES_PATH = "/examples/extensions/"; private static final String UTF8_SEQUENCE = "ẞßÄäËëÏïÖöÜüẄẅŸÿЁёЇїӜӝ-Zażółć gęślą jaźń-ÆæØøÅå-ÀÆÇßãÿαΩƒ"; @@ -126,17 +128,17 @@ public class ExtensionConverterTest { private void checkExtensions(List<ExtensionType> extensions, int size) { Assert.assertNotNull(extensions); - Assert.assertEquals(size, extensions.size()); + assertEquals(size, extensions.size()); int number = 1; for (ExtensionType extension : extensions) { Assert.assertNotNull(extension); - Assert.assertEquals("name" + number, extension.getExtensionName()); - Assert.assertEquals("versionId" + number, extension.getExtensionVersionID()); - Assert.assertEquals("reason" + number, extension.getExtensionReason()); - Assert.assertEquals("reasonCode" + number, extension.getExtensionReasonCode()); - Assert.assertEquals("id" + number, extension.getExtensionID()); - Assert.assertEquals("agencyUri" + number, extension.getExtensionAgencyURI()); - Assert.assertEquals("agencyName" + number, extension.getExtensionAgencyName()); + assertEquals("name" + number, extension.getExtensionName()); + assertEquals("versionId" + number, extension.getExtensionVersionID()); + assertEquals("reason" + number, extension.getExtensionReason()); + assertEquals("reasonCode" + number, extension.getExtensionReasonCode()); + assertEquals("id" + number, extension.getExtensionID()); + assertEquals("agencyUri" + number, extension.getExtensionAgencyURI()); + assertEquals("agencyName" + number, extension.getExtensionAgencyName()); assertNull(extension.getExtensionURI()); number++; } diff --git a/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/data/dao/ServiceGroupDaoIntegrationBase.java b/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/data/dao/ServiceGroupDaoIntegrationBase.java index af6e2beb987aa41d427538cac30be3e7aa93314d..f6da7769f20d8b1d7bedc07b44808334bc835c38 100644 --- a/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/data/dao/ServiceGroupDaoIntegrationBase.java +++ b/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/data/dao/ServiceGroupDaoIntegrationBase.java @@ -5,6 +5,7 @@ import eu.europa.ec.edelivery.smp.data.model.DBDomain; import eu.europa.ec.edelivery.smp.data.model.DBServiceGroup; import eu.europa.ec.edelivery.smp.data.model.DBServiceMetadata; import eu.europa.ec.edelivery.smp.data.model.DBUser; +import eu.europa.ec.edelivery.smp.testutil.TestConstants; import eu.europa.ec.edelivery.smp.testutil.TestDBUtils; import org.junit.Before; import org.junit.Rule; @@ -54,16 +55,39 @@ public abstract class ServiceGroupDaoIntegrationBase extends AbstractBaseDao{ userDao.persistFlushDetach(u3); } - @Transactional + public DBServiceGroup createAndSaveNewServiceGroup(){ + return createAndSaveNewServiceGroup(TEST_DOMAIN_CODE_1, TestConstants.TEST_SG_ID_1, TestConstants.TEST_SG_SCHEMA_1); + } - DBDomain d = domainDao.getDomainByCode(TEST_DOMAIN_CODE_1).get(); - DBServiceGroup sg = TestDBUtils.createDBServiceGroup(); + private DBServiceGroup createAndSaveNewServiceGroup(String domain, String participantId, String participantSchema){ + return createAndSaveNewServiceGroup(domain, participantId, participantSchema, null); + } + @Transactional + public DBServiceGroup createAndSaveNewServiceGroup(String domain, String participantId, String participantSchema, DBUser usr){ + DBDomain d = domainDao.getDomainByCode(domain).get(); + DBServiceGroup sg = TestDBUtils.createDBServiceGroup(participantId, participantSchema); + if (usr!= null) { + sg.getUsers().add(usr); + } sg.addDomain(d); testInstance.persistFlushDetach(sg); return sg; } + + public void createAndSaveNewServiceGroups(int iCount, String domain, String participant){ + createAndSaveNewServiceGroups(iCount, domain, participant, null); + } + + @Transactional + public void createAndSaveNewServiceGroups(int iCount, String domain, String participant, DBUser usr){ + int i =0; + while (i++ < iCount){ + createAndSaveNewServiceGroup(domain, participant+":"+i, TestConstants.TEST_SG_SCHEMA_1, usr); + } + } + @Transactional public DBServiceGroup createAndSaveNewServiceGroupWithMetadata(){ diff --git a/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/data/dao/ServiceGroupDaoIntegrationTest.java b/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/data/dao/ServiceGroupDaoIntegrationTest.java index a59b51b753c51a4a41188f8c761d5f173dc05659..f79eeb416d0382c89c4355665e7c2cde4f3a85ee 100644 --- a/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/data/dao/ServiceGroupDaoIntegrationTest.java +++ b/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/data/dao/ServiceGroupDaoIntegrationTest.java @@ -2,14 +2,20 @@ package eu.europa.ec.edelivery.smp.data.dao; import eu.europa.ec.edelivery.smp.data.model.DBDomain; import eu.europa.ec.edelivery.smp.data.model.DBServiceGroup; +import eu.europa.ec.edelivery.smp.data.model.DBUser; +import eu.europa.ec.edelivery.smp.data.ui.ServiceGroupRO; +import eu.europa.ec.edelivery.smp.data.ui.ServiceResult; +import eu.europa.ec.edelivery.smp.services.ui.filters.ServiceGroupFilter; import eu.europa.ec.edelivery.smp.testutil.TestConstants; import eu.europa.ec.edelivery.smp.testutil.TestDBUtils; import org.junit.Test; import org.junit.runner.RunWith; +import org.oasis_open.docs.bdxr.ns.smp._2016._05.ServiceMetadata; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import javax.transaction.Transactional; import java.util.Arrays; +import java.util.List; import java.util.Optional; import java.util.UUID; @@ -160,4 +166,121 @@ public class ServiceGroupDaoIntegrationTest extends ServiceGroupDaoIntegrationBa assertFalse(optResDel.isPresent()); } + @Test + public void testGetServiceGroupListNotEmpty(){ + // given + // add additional domain + DBDomain d2 = TestDBUtils.createDBDomain(TEST_DOMAIN_CODE_2); + domainDao.persistFlushDetach(d2); + createAndSaveNewServiceGroups(10, TEST_DOMAIN_CODE_1, TestConstants.TEST_SG_ID_1); + createAndSaveNewServiceGroups(5, TEST_DOMAIN_CODE_2, TestConstants.TEST_SG_ID_2); + //when + List<DBServiceGroup> res = testInstance.getServiceGroupList(-1,-1,null, null,null); + // then + assertNotNull(res); + assertEquals(15, res.size()); + + } + + + @Test + public void testGetCaseInsensitive(){ + // given + DBServiceGroup sg = createAndSaveNewServiceGroup(); + + ServiceGroupFilter sf = new ServiceGroupFilter(); + sf.setParticipantSchemeLike(sg.getParticipantScheme().toLowerCase()); + sf.setParticipantIdentifierLike(sg.getParticipantIdentifier().toLowerCase()); + List<DBServiceGroup> res = testInstance.getServiceGroupList(-1,-1,null, null,sf); + assertEquals(1, res.size()); + + ServiceGroupFilter sf2 = new ServiceGroupFilter(); + sf2.setParticipantSchemeLike(sg.getParticipantScheme().toUpperCase()); + sf2.setParticipantIdentifierLike(sg.getParticipantIdentifier().toUpperCase()); + + List<DBServiceGroup> res2 = testInstance.getServiceGroupList(-1,-1,null, null,sf); + assertEquals(1, res2.size()); + assertEquals(res.get(0).getId(), res2.get(0).getId()); + assertNotEquals(sf.getParticipantIdentifierLike(), sf2.getParticipantIdentifierLike()); + assertNotEquals(sf.getParticipantSchemeLike(), sf2.getParticipantSchemeLike()); + + + } + + @Test + public void testGetServiceGroupListByDomain(){ + // given + // add additional domain + DBDomain d2 = TestDBUtils.createDBDomain(TEST_DOMAIN_CODE_2); + domainDao.persistFlushDetach(d2); + createAndSaveNewServiceGroups(10, TEST_DOMAIN_CODE_1, TestConstants.TEST_SG_ID_1); + createAndSaveNewServiceGroups(5, TEST_DOMAIN_CODE_2, TestConstants.TEST_SG_ID_2); + + ServiceGroupFilter sgf = new ServiceGroupFilter(); + sgf.setDomain(d2); + + //when + List<DBServiceGroup> res = testInstance.getServiceGroupList(-1,-1,null, null,sgf); + // then + assertNotNull(res); + assertEquals(5, res.size()); + + } + + @Test + public void testGetServiceGroupListByOwnerAndDomain(){ + // given + // add additional domain + DBUser usr1 = userDao.findUserByUsername(TestConstants.USERNAME_1).get(); + DBUser usr2 = userDao.findUserByUsername(TestConstants.USERNAME_3).get(); + assertNotNull(usr1); + assertNotNull(usr2); + + DBDomain d2 = TestDBUtils.createDBDomain(TEST_DOMAIN_CODE_2); + domainDao.persistFlushDetach(d2); + + createAndSaveNewServiceGroups(2, TEST_DOMAIN_CODE_1, TestConstants.TEST_SG_ID_1, usr1); + createAndSaveNewServiceGroups(3, TEST_DOMAIN_CODE_2, TestConstants.TEST_SG_ID_2, usr1); + createAndSaveNewServiceGroups(4, TEST_DOMAIN_CODE_1, TestConstants.TEST_SG_ID_3, usr2); + createAndSaveNewServiceGroups(8, TEST_DOMAIN_CODE_2, TestConstants.TEST_SG_ID_4, usr2); + + // test for domain two + ServiceGroupFilter sgf = new ServiceGroupFilter(); + //when + sgf.setDomain(d2); + List<DBServiceGroup> res = testInstance.getServiceGroupList(-1,-1,null, null,sgf); + // then + assertNotNull(res); + assertEquals(11, res.size()); + + //when + sgf.setDomain(null); + sgf.setOwner(usr1); + res = testInstance.getServiceGroupList(-1,-1,null, null,sgf); + // then + assertNotNull(res); + assertEquals(5, res.size()); + //when + sgf.setDomain(d2); + sgf.setOwner(usr2); + res = testInstance.getServiceGroupList(-1,-1,null, null,sgf); + // then + assertNotNull(res); + assertEquals(8, res.size()); + + } + + @Test + public void testGetTableListEmpty(){ + + // given + + //when + List<DBServiceGroup> res = testInstance.getServiceGroupList(-1,-1,null, null,null); + // then + assertNotNull(res); + assertTrue(res.isEmpty()); + + } + } \ No newline at end of file diff --git a/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/data/dao/UserDaoIntegrationTest.java b/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/data/dao/UserDaoIntegrationTest.java index 746ba3d83a2fb616dc1987193db58f485ad06eea..bc6181fadd85f6e68d2c205dc6afc5ef7db08b37 100644 --- a/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/data/dao/UserDaoIntegrationTest.java +++ b/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/data/dao/UserDaoIntegrationTest.java @@ -1,6 +1,5 @@ package eu.europa.ec.edelivery.smp.data.dao; -import eu.europa.ec.edelivery.smp.config.H2JPATestConfiguration; import eu.europa.ec.edelivery.smp.data.model.DBUser; import eu.europa.ec.edelivery.smp.exceptions.SMPRuntimeException; import eu.europa.ec.edelivery.smp.testutil.TestConstants; @@ -8,12 +7,7 @@ import eu.europa.ec.edelivery.smp.testutil.TestDBUtils; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; -import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.jdbc.Sql; -import org.springframework.test.context.jdbc.SqlConfig; -import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import java.time.temporal.ChronoUnit; import java.util.Optional; @@ -60,7 +54,7 @@ public class UserDaoIntegrationTest extends AbstractBaseDao{ Optional<DBUser> ou = testInstance.findUserByUsername(TestConstants.USERNAME_1); assertTrue(u!=ou.get()); assertEquals(u, ou.get()); - assertEquals(u.getEmail(), ou.get().getEmail()); + assertEquals(u.getEmailAddress(), ou.get().getEmailAddress()); assertEquals(u.getPassword(), ou.get().getPassword()); assertEquals(u.getRole(), ou.get().getRole()); assertEquals(u.getUsername(), ou.get().getUsername()); @@ -78,7 +72,7 @@ public class UserDaoIntegrationTest extends AbstractBaseDao{ Optional<DBUser> ou = testInstance.findUserByCertificateId(TestConstants.USER_CERT_1); assertTrue(u!=ou.get()); assertEquals(u, ou.get()); - assertEquals(u.getEmail(), ou.get().getEmail()); + assertEquals(u.getEmailAddress(), ou.get().getEmailAddress()); assertEquals(u.getCertificate().getCertificateId(), ou.get().getCertificate().getCertificateId()); assertEquals(u.getCertificate().getValidFrom().truncatedTo(ChronoUnit.MINUTES), ou.get().getCertificate().getValidFrom().truncatedTo(ChronoUnit.MINUTES)); assertEquals(u.getCertificate().getValidTo().truncatedTo(ChronoUnit.MINUTES), ou.get().getCertificate().getValidTo().truncatedTo(ChronoUnit.MINUTES)); @@ -96,7 +90,7 @@ public class UserDaoIntegrationTest extends AbstractBaseDao{ Optional<DBUser> ou = testInstance.findUserByIdentifier(TestConstants.USER_CERT_1); assertTrue(u!=ou.get()); assertEquals(u, ou.get()); - assertEquals(u.getEmail(), ou.get().getEmail()); + assertEquals(u.getEmailAddress(), ou.get().getEmailAddress()); assertEquals(u.getCertificate().getCertificateId(), ou.get().getCertificate().getCertificateId()); // some database timestamp objects does not store miliseconds assertEquals(u.getCertificate().getValidFrom().truncatedTo(ChronoUnit.MINUTES), ou.get().getCertificate().getValidFrom().truncatedTo(ChronoUnit.MINUTES)); @@ -115,7 +109,7 @@ public class UserDaoIntegrationTest extends AbstractBaseDao{ Optional<DBUser> ou = testInstance.findUserByIdentifier(TestConstants.USERNAME_1); assertTrue(u!=ou.get()); assertEquals(u, ou.get()); - assertEquals(u.getEmail(), ou.get().getEmail()); + assertEquals(u.getEmailAddress(), ou.get().getEmailAddress()); } @Test @@ -161,7 +155,7 @@ public class UserDaoIntegrationTest extends AbstractBaseDao{ Optional<DBUser> ou = testInstance.findUserByUsername(TestConstants.USERNAME_1.toUpperCase()); assertTrue(ou.isPresent()); assertEquals(u, ou.get()); - assertEquals(u.getEmail(), ou.get().getEmail()); + assertEquals(u.getEmailAddress(), ou.get().getEmailAddress()); } } \ No newline at end of file diff --git a/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/services/ServiceGroupServiceTestService.java b/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/services/ServiceGroupServiceTestService.java index 52da8a296dc62e5be207651d5c1e673cf553c5e8..aa6d1454eeef87dacd2b19ee7851e30ef624d524 100644 --- a/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/services/ServiceGroupServiceTestService.java +++ b/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/services/ServiceGroupServiceTestService.java @@ -77,5 +77,4 @@ public class ServiceGroupServiceTestService extends AbstractServiceIntegrationTe testInstance.validateOwnership(USER_CERT_3, dbsg.get()); } - } diff --git a/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/services/ui/UIServiceGroupServiceIntegrationTest.java b/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/services/ui/UIServiceGroupServiceIntegrationTest.java index 6813cda9ce64df381f85c52db54c290aec982491..6ee37dd0acf262c2afb687cad43f5ed9b1a75f04 100644 --- a/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/services/ui/UIServiceGroupServiceIntegrationTest.java +++ b/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/services/ui/UIServiceGroupServiceIntegrationTest.java @@ -1,28 +1,39 @@ package eu.europa.ec.edelivery.smp.services.ui; +import eu.europa.ec.edelivery.smp.data.model.DBDomain; import eu.europa.ec.edelivery.smp.data.model.DBServiceGroup; +import eu.europa.ec.edelivery.smp.data.model.DBServiceMetadata; +import eu.europa.ec.edelivery.smp.data.model.DBUser; +import eu.europa.ec.edelivery.smp.data.ui.ServiceGroupExtensionRO; import eu.europa.ec.edelivery.smp.data.ui.ServiceGroupRO; +import eu.europa.ec.edelivery.smp.data.ui.ServiceMetadataRO; import eu.europa.ec.edelivery.smp.data.ui.ServiceResult; +import eu.europa.ec.edelivery.smp.data.ui.enums.EntityROStatus; import eu.europa.ec.edelivery.smp.services.AbstractServiceIntegrationTest; import eu.europa.ec.edelivery.smp.testutil.TestConstants; import eu.europa.ec.edelivery.smp.testutil.TestDBUtils; +import eu.europa.ec.edelivery.smp.testutil.TestROUtils; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; +import java.io.IOException; +import java.util.Collections; + +import static org.hamcrest.core.StringContains.containsString; import static org.junit.Assert.*; /** - * Purpose of class is to test ServiceGroupService base methods + * Purpose of class is to test ServiceGroupService base methods * * @author Joze Rihtarsic * @since 4.1 */ -@ContextConfiguration(classes= UIServiceGroupService.class) +@ContextConfiguration(classes = {UIServiceGroupService.class, UIServiceMetadataService.class}) public class UIServiceGroupServiceIntegrationTest extends AbstractServiceIntegrationTest { @Rule public ExpectedException expectedExeption = ExpectedException.none(); @@ -30,20 +41,37 @@ public class UIServiceGroupServiceIntegrationTest extends AbstractServiceIntegra @Autowired protected UIServiceGroupService testInstance; - protected void insertDataObjects(int size){ - for (int i=0; i < size; i++){ - DBServiceGroup d = TestDBUtils.createDBServiceGroup(String.format("0007:%4d:utest",i),TestConstants.TEST_SG_SCHEMA_1); - serviceGroupDao.persistFlushDetach(d); + @Autowired + protected UIServiceMetadataService uiServiceMetadataService; + + + protected void insertDataObjectsForOwner(int size, DBUser owner) { + for (int i = 0; i < size; i++) { + insertServiceGroup(String.format("%4d", i), true, owner); + } + } + + protected void insertDataObjects(int size) { + insertDataObjectsForOwner(size, null); + } + + protected DBServiceGroup insertServiceGroup(String id, boolean withExtension, DBUser owner) { + DBServiceGroup d = TestDBUtils.createDBServiceGroup(String.format("0007:%s:utest", id), TestConstants.TEST_SG_SCHEMA_1, withExtension); + if (owner!= null) { + d.getUsers().add(owner); } + serviceGroupDao.persistFlushDetach(d); + return d; } + @Test - public void testGetTableListEmpty(){ + public void testGetTableListEmpty() { // given //when - ServiceResult<ServiceGroupRO> res = testInstance.getTableList(-1,-1,null, null,null); + ServiceResult<ServiceGroupRO> res = testInstance.getTableList(-1, -1, null, null, null); // then assertNotNull(res); assertEquals(0, res.getCount().intValue()); @@ -54,12 +82,12 @@ public class UIServiceGroupServiceIntegrationTest extends AbstractServiceIntegra } @Test - public void testGetTableList15(){ + public void testGetTableList15() { // given insertDataObjects(15); //when - ServiceResult<ServiceGroupRO> res = testInstance.getTableList(-1,-1,null, null, null); + ServiceResult<ServiceGroupRO> res = testInstance.getTableList(-1, -1, null, null, null); // then @@ -75,4 +103,236 @@ public class UIServiceGroupServiceIntegrationTest extends AbstractServiceIntegra assertNotNull(res.getServiceEntities().get(0).getParticipantIdentifier()); assertNotNull(res.getServiceEntities().get(0).getParticipantScheme()); } + + @Test + public void testAddServiceWithMetadata() { + + // given + DBDomain testDomain01 = TestDBUtils.createDBDomain(TestConstants.TEST_DOMAIN_CODE_1); + domainDao.persistFlushDetach(testDomain01); + + ServiceGroupRO sgnew = TestROUtils.createROServiceGroupForDomains(testDomain01); + // add service metadata + ServiceMetadataRO mtro = TestROUtils.createServiceMetadataDomain(testDomain01, sgnew, TestConstants.TEST_DOC_ID_1, TestConstants.TEST_DOC_SCHEMA_1); + sgnew.getServiceMetadata().add(mtro); + + //when + testInstance.updateServiceGroupList(Collections.singletonList(sgnew)); + + // then + ServiceResult<ServiceGroupRO> res = testInstance.getTableList(-1, -1, null, null, null); + + assertNotNull(res); + assertEquals(1, res.getCount().intValue()); + ServiceGroupRO sgAdded = res.getServiceEntities().get(0); + ServiceGroupExtensionRO sgExt = testInstance.getServiceGroupExtensionById(sgAdded.getId()); + + + // all table properties should not be null + assertNotNull(sgAdded); + assertEquals(sgnew.getParticipantIdentifier(), sgAdded.getParticipantIdentifier()); + assertEquals(sgnew.getParticipantScheme(), sgAdded.getParticipantScheme()); + assertNull(sgAdded.getExtension()); // with list extension must be empty - extension is retrived by some other call + assertEquals(sgnew.getExtension(), sgExt.getExtension()); + assertEquals(1, sgAdded.getServiceGroupDomains().size()); + assertEquals(1, sgAdded.getServiceMetadata().size()); + } + + @Test + public void testUpdateServiceGroupExtensionAndServiceMetadaXML() { + + // given + DBDomain testDomain01 = TestDBUtils.createDBDomain(TestConstants.TEST_DOMAIN_CODE_1); + domainDao.persistFlushDetach(testDomain01); + DBServiceGroup dbServiceGroup = TestDBUtils.createDBServiceGroup(); + dbServiceGroup.addDomain(testDomain01); + DBServiceMetadata dbServiceMetadata = TestDBUtils.createDBServiceMetadata(dbServiceGroup.getParticipantIdentifier(), dbServiceGroup.getParticipantScheme()); + dbServiceGroup.getServiceGroupDomains().get(0).addServiceMetadata(dbServiceMetadata); + serviceGroupDao.persistFlushDetach(dbServiceGroup); + + String newMetadataXML = TestROUtils.generateServiceMetadata(dbServiceGroup.getParticipantIdentifier(), dbServiceGroup.getParticipantScheme(), + dbServiceMetadata.getDocumentIdentifier(), dbServiceMetadata.getDocumentIdentifierScheme()); + String newExtension = TestROUtils.generateExtension(); + + ServiceResult<ServiceGroupRO> res = testInstance.getTableList(-1, -1, null, null, null); + assertEquals(1, res.getCount().intValue()); + ServiceGroupRO sgChange = res.getServiceEntities().get(0); + ServiceMetadataRO smdXML = uiServiceMetadataService.getServiceMetadataXMLById(res.getServiceEntities().get(0).getServiceMetadata().get(0).getId()); + // test new extension + assertNotEquals(newExtension, sgChange.getExtension()); + assertNotEquals(newMetadataXML, smdXML.getXmlContent()); + // set new extension + sgChange.setStatus(EntityROStatus.UPDATED.getStatusNumber()); + sgChange.setExtension(newExtension); + sgChange.setExtensionStatus(EntityROStatus.UPDATED.getStatusNumber()); + // set new XMLContent + sgChange.getServiceMetadata().get(0).setStatus(EntityROStatus.UPDATED.getStatusNumber()); + sgChange.getServiceMetadata().get(0).setXmlContentStatus(EntityROStatus.UPDATED.getStatusNumber()); + sgChange.getServiceMetadata().get(0).setXmlContent(newMetadataXML); + + //when + testInstance.updateServiceGroupList(Collections.singletonList(sgChange)); + + // then + res = testInstance.getTableList(-1, -1, null, null, null); + + assertNotNull(res); + assertEquals(1, res.getCount().intValue()); + ServiceGroupRO sgUpdated = res.getServiceEntities().get(0); + ServiceGroupExtensionRO sgExt = testInstance.getServiceGroupExtensionById(sgUpdated.getId()); + assertEquals(1, sgChange.getServiceMetadata().size()); + // retrive service metadata xml with special service - it is not retrieve by browsing list + ServiceMetadataRO smdXMLNew = uiServiceMetadataService.getServiceMetadataXMLById(sgUpdated.getServiceMetadata().get(0).getId()); + + // all table properties should not be null + assertNotNull(sgUpdated); + assertEquals(sgUpdated.getParticipantIdentifier(), sgUpdated.getParticipantIdentifier()); + assertEquals(sgUpdated.getParticipantScheme(), sgUpdated.getParticipantScheme()); + assertEquals(newExtension, sgExt.getExtension()); + assertEquals(1, sgChange.getServiceGroupDomains().size()); + assertNotNull(smdXMLNew.getXmlContent()); + assertEquals(newMetadataXML, smdXMLNew.getXmlContent()); + } + + + + @Test + public void testUpdateServiceMatadataChangeDomain() { + + // given + DBDomain testDomain01 = TestDBUtils.createDBDomain(TestConstants.TEST_DOMAIN_CODE_1); + domainDao.persistFlushDetach(testDomain01); + DBDomain testDomain02 = TestDBUtils.createDBDomain(TestConstants.TEST_DOMAIN_CODE_2); + domainDao.persistFlushDetach(testDomain02); + + DBServiceGroup dbServiceGroup = TestDBUtils.createDBServiceGroup(); + dbServiceGroup.addDomain(testDomain01); + DBServiceMetadata dbServiceMetadata = TestDBUtils.createDBServiceMetadata(dbServiceGroup.getParticipantIdentifier(), dbServiceGroup.getParticipantScheme()); + dbServiceGroup.getServiceGroupDomains().get(0).addServiceMetadata(dbServiceMetadata); + // add second domain + dbServiceGroup.addDomain(testDomain02); + serviceGroupDao.persistFlushDetach(dbServiceGroup); + + ServiceResult<ServiceGroupRO> res = testInstance.getTableList(-1, -1, null, null, null); + + assertNotNull(res); + assertEquals(1, res.getCount().intValue()); + ServiceGroupRO sgChanged = res.getServiceEntities().get(0); + ServiceMetadataRO smdToChange = sgChanged.getServiceMetadata().get(0); + assertEquals(testDomain01.getDomainCode(), smdToChange.getDomainCode()); + assertEquals(testDomain01.getSmlSubdomain(), smdToChange.getSmlSubdomain()); + + // then + sgChanged.setStatus(EntityROStatus.UPDATED.getStatusNumber()); + smdToChange.setStatus(EntityROStatus.UPDATED.getStatusNumber()); + smdToChange.setDomainCode(testDomain02.getDomainCode()); + smdToChange.setSmlSubdomain(testDomain02.getSmlSubdomain()); + testInstance.updateServiceGroupList(Collections.singletonList(sgChanged)); + + res = testInstance.getTableList(-1, -1, null, null, null); + ServiceGroupRO sgUpdated = res.getServiceEntities().get(0); + ServiceMetadataRO smdUpdated = sgUpdated.getServiceMetadata().get(0); + + assertEquals(testDomain02.getDomainCode(), smdUpdated.getDomainCode()); + assertEquals(testDomain02.getSmlSubdomain(), smdUpdated.getSmlSubdomain()); + + } + + + @Test + public void validateExtensionVaild() throws IOException { + // given + ServiceGroupExtensionRO sg = TestROUtils.getValidExtension(); + + // when + testInstance.validateExtension(sg); + + // then + assertNull(sg.getErrorMessage()); + assertNotNull(sg.getExtension()); + } + + @Test + public void validateExtensionMultipleVaild() throws IOException { + // given + ServiceGroupExtensionRO sg = TestROUtils.getValidMultipleExtension(); + + // when + testInstance.validateExtension(sg); + + // then + assertNull(sg.getErrorMessage()); + assertNotNull(sg.getExtension()); + } + + @Test + public void validateExtensionCustomTextInvaldValid() throws IOException { + // given + ServiceGroupExtensionRO sg = TestROUtils.getValidCustomText(); + + // when + testInstance.validateExtension(sg); + + // then + assertNotNull(sg.getErrorMessage()); + assertThat(sg.getErrorMessage(), containsString("Element 'ServiceGroup' cannot have character ")); + assertNotNull(sg.getExtension()); + } + + @Test + public void validateExtensionInvalid() throws IOException { + ServiceGroupExtensionRO sg = TestROUtils.getInvalid(); + + // when + testInstance.validateExtension(sg); + + // then + assertNotNull(sg.getErrorMessage()); + assertThat(sg.getErrorMessage(), containsString(" Invalid content was found starting with element 'ExtensionID'.")); + assertNotNull(sg.getExtension()); + } + + @Test + public void validateCustomExtension() throws IOException { + ServiceGroupExtensionRO sg = TestROUtils.getCustomExtension(); + + // when + testInstance.validateExtension(sg); + + // then + assertNull(sg.getErrorMessage()); + assertNotNull(sg.getExtension()); + } + + + @Test + public void getEmptyExtensionById() throws IOException { + DBServiceGroup sg = insertServiceGroup("testExt", false, null); + assertNotNull(sg); + assertNotNull(sg.getId()); + assertNull(sg.getExtension()); + + // when + ServiceGroupExtensionRO res = testInstance.getServiceGroupExtensionById(sg.getId()); + + // then + assertNotNull(res); + assertNull(res.getExtension()); + } + + @Test + public void getExtensionById() throws IOException { + DBServiceGroup sg = insertServiceGroup("testExt", true, null); + assertNotNull(sg); + assertNotNull(sg.getId()); + assertNotNull(sg.getExtension()); + + // when + ServiceGroupExtensionRO res = testInstance.getServiceGroupExtensionById(sg.getId()); + + // then + assertNotNull(res); + assertNotNull(res.getExtension()); + } + } diff --git a/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/services/ui/UIUserServiceIntegrationTest.java b/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/services/ui/UIUserServiceIntegrationTest.java index 62305ae17bcd0bc24b9244a8591a9eb176140c15..874d50abb8dfaf66e5ccc433de6291594575b2af 100644 --- a/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/services/ui/UIUserServiceIntegrationTest.java +++ b/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/services/ui/UIUserServiceIntegrationTest.java @@ -10,8 +10,6 @@ import eu.europa.ec.edelivery.smp.data.ui.enums.EntityROStatus; import eu.europa.ec.edelivery.smp.services.AbstractServiceIntegrationTest; import eu.europa.ec.edelivery.smp.testutil.TestDBUtils; import org.apache.commons.io.IOUtils; -import org.hibernate.type.BigIntegerType; -import org.hibernate.type.descriptor.java.UUIDTypeDescriptor; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; @@ -19,13 +17,15 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.crypto.bcrypt.BCrypt; import org.springframework.test.context.ContextConfiguration; +import java.io.FileInputStream; import java.io.IOException; -import java.math.BigInteger; import java.security.cert.CertificateException; import java.time.LocalDateTime; import java.time.temporal.ChronoUnit; -import java.time.temporal.TemporalUnit; -import java.util.*; +import java.util.Calendar; +import java.util.Collections; +import java.util.Optional; +import java.util.UUID; import static org.junit.Assert.*; @@ -88,7 +88,7 @@ public class UIUserServiceIntegrationTest extends AbstractServiceIntegrationTest assertNotNull(res); assertNotNull(res.getServiceEntities().get(0).getId()); assertNotNull(res.getServiceEntities().get(0).getUsername()); - assertNotNull(res.getServiceEntities().get(0).getEmail()); + assertNotNull(res.getServiceEntities().get(0).getEmailAddress()); assertNull(res.getServiceEntities().get(0).getPassword()); // Service list must not return passwords assertNotNull(res.getServiceEntities().get(0).getPasswordChanged()); assertNotNull(res.getServiceEntities().get(0).getRole()); @@ -124,7 +124,7 @@ public class UIUserServiceIntegrationTest extends AbstractServiceIntegrationTest UserRO user = new UserRO(); user.setPassword(UUID.randomUUID().toString()); user.setUsername(UUID.randomUUID().toString()); - user.setEmail(UUID.randomUUID().toString()); + user.setEmailAddress(UUID.randomUUID().toString()); user.setRole("ROLE"); user.setStatus(EntityROStatus.NEW.getStatusNumber()); @@ -138,7 +138,7 @@ public class UIUserServiceIntegrationTest extends AbstractServiceIntegrationTest assertEquals(user.getPassword(), oUsr.get().getPassword()); assertEquals(user.getUsername(), oUsr.get().getUsername()); assertEquals(user.getRole(), oUsr.get().getRole()); - assertEquals(user.getEmail(), oUsr.get().getEmail()); + assertEquals(user.getEmailAddress(), oUsr.get().getEmailAddress()); assertNull(oUsr.get().getCertificate()); } @@ -154,7 +154,7 @@ public class UIUserServiceIntegrationTest extends AbstractServiceIntegrationTest UserRO user = new UserRO(); user.setPassword(UUID.randomUUID().toString()); user.setUsername(UUID.randomUUID().toString()); - user.setEmail(UUID.randomUUID().toString()); + user.setEmailAddress(UUID.randomUUID().toString()); user.setRole("ROLE"); CertificateRO cert = new CertificateRO(); cert.setSubject(UUID.randomUUID().toString()); @@ -178,7 +178,7 @@ public class UIUserServiceIntegrationTest extends AbstractServiceIntegrationTest assertEquals(user.getPassword(), oUsr.get().getPassword()); assertEquals(user.getUsername(), oUsr.get().getUsername()); assertEquals(user.getRole(), oUsr.get().getRole()); - assertEquals(user.getEmail(), oUsr.get().getEmail()); + assertEquals(user.getEmailAddress(), oUsr.get().getEmailAddress()); assertNotNull(oUsr.get().getCertificate()); assertEquals(cert.getCertificateId(), cert.getCertificateId()); assertEquals(cert.getSubject(), cert.getSubject()); @@ -200,7 +200,7 @@ public class UIUserServiceIntegrationTest extends AbstractServiceIntegrationTest DBUser user = new DBUser(); user.setPassword(UUID.randomUUID().toString()); user.setUsername(UUID.randomUUID().toString()); - user.setEmail(UUID.randomUUID().toString()); + user.setEmailAddress(UUID.randomUUID().toString()); user.setRole("ROLE"); DBCertificate cert = new DBCertificate(); cert.setSubject(UUID.randomUUID().toString()); @@ -252,9 +252,9 @@ public class UIUserServiceIntegrationTest extends AbstractServiceIntegrationTest } @Test - public void testGetCertificateData() throws IOException, CertificateException { + public void testGetCertificateDataPEM() throws IOException, CertificateException { // given - byte[] buff = IOUtils.toByteArray(UIUserServiceIntegrationTest.class.getResourceAsStream("/keystores/SMPtest.crt")); + byte[] buff = IOUtils.toByteArray(UIUserServiceIntegrationTest.class.getResourceAsStream("/truststore/SMPtest.crt")); // when CertificateRO cer = testInstance.getCertificateData(buff); //then @@ -265,12 +265,23 @@ public class UIUserServiceIntegrationTest extends AbstractServiceIntegrationTest assertNotNull(cer.getValidFrom()); assertNotNull(cer.getValidTo()); assertTrue(cer.getValidFrom().isBefore(cer.getValidTo())); - - } + @Test + public void testGetCertificateDataDER() throws IOException, CertificateException { + // given + byte[] buff = IOUtils.toByteArray(new FileInputStream("src/test/resources/truststore/NewPeppolAPaa.crt")); - - + // when + CertificateRO cer = testInstance.getCertificateData(buff); + //then + assertEquals("CN=POP000004,O=European Commission,C=BE:474980c51478cf62761667461aef5e8e", cer.getCertificateId()); + assertEquals("CN=PEPPOL ACCESS POINT TEST CA - G2, OU=FOR TEST ONLY, O=OpenPEPPOL AISBL, C=BE", cer.getIssuer()); + assertEquals("C=BE, O=European Commission, OU=PEPPOL TEST AP, CN=POP000004", cer.getSubject()); + assertEquals("474980c51478cf62761667461aef5e8e", cer.getSerialNumber()); + assertNotNull(cer.getValidFrom()); + assertNotNull(cer.getValidTo()); + assertTrue(cer.getValidFrom().isBefore(cer.getValidTo())); + } } diff --git a/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/testutil/TestConstants.java b/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/testutil/TestConstants.java index 00725fb622c1df6664d2300ba097d29f32b02c46..d17843a9f39f130f82bcb25a46fc6313d409693f 100644 --- a/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/testutil/TestConstants.java +++ b/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/testutil/TestConstants.java @@ -16,6 +16,7 @@ public class TestConstants { public static final String TEST_SG_ID_1 = "0007:001:utest"; public static final String TEST_SG_ID_2 = "urn:eu:ncpb:utest"; public static final String TEST_SG_ID_3 = "0007:002:utest"; + public static final String TEST_SG_ID_4 = "0007:004:utest"; public static final String TEST_SG_ID_PL= "urn:poland:ncpb:utest"; public static final String TEST_SG_ID_PL2= "urn:Poland:ncpb"; diff --git a/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/testutil/TestDBUtils.java b/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/testutil/TestDBUtils.java index 357dbf742eeabc06788473ce22c00cc532604b67..08b73c004a06435cfd65b090dfbe0a7aa9dbb405 100644 --- a/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/testutil/TestDBUtils.java +++ b/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/testutil/TestDBUtils.java @@ -53,10 +53,16 @@ public class TestDBUtils { } public static DBServiceGroup createDBServiceGroup(String id, String sch) { + return createDBServiceGroup(id, sch, true); + } + + public static DBServiceGroup createDBServiceGroup(String id, String sch, boolean withExtension) { DBServiceGroup grp = new DBServiceGroup(); grp.setParticipantIdentifier(id); grp.setParticipantScheme(sch); - grp.setExtension(generateExtension()); + if (withExtension) { + grp.setExtension(generateExtension()); + } return grp; } @@ -70,7 +76,7 @@ public class TestDBUtils { DBUser dbuser = new DBUser(); dbuser.setUsername(userName); dbuser.setRole("test"); - dbuser.setEmail("test@test.eu"); + dbuser.setEmailAddress("test@test.eu"); dbuser.setPasswordChanged(LocalDateTime.now()); dbuser.setPassword(UUID.randomUUID().toString()); return dbuser; diff --git a/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/testutil/TestROUtils.java b/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/testutil/TestROUtils.java new file mode 100644 index 0000000000000000000000000000000000000000..2e318c1078a75fc2b7694bfb3d5054b7d8cc0342 --- /dev/null +++ b/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/testutil/TestROUtils.java @@ -0,0 +1,110 @@ +package eu.europa.ec.edelivery.smp.testutil; + +import eu.europa.ec.edelivery.smp.data.model.DBDomain; +import eu.europa.ec.edelivery.smp.data.ui.ServiceGroupDomainRO; +import eu.europa.ec.edelivery.smp.data.ui.ServiceGroupExtensionRO; +import eu.europa.ec.edelivery.smp.data.ui.ServiceGroupRO; +import eu.europa.ec.edelivery.smp.data.ui.ServiceMetadataRO; +import eu.europa.ec.edelivery.smp.data.ui.enums.EntityROStatus; + +import java.io.IOException; +import java.util.Arrays; +import java.util.UUID; + +import static eu.europa.ec.edelivery.smp.conversion.ExtensionConverterTest.RES_PATH; +import static eu.europa.ec.edelivery.smp.testutil.TestConstants.SIMPLE_DOCUMENT_XML; +import static eu.europa.ec.edelivery.smp.testutil.TestConstants.SIMPLE_EXTENSION_XML; + +public class TestROUtils { + + + public static ServiceMetadataRO createServiceMetadataDomain(DBDomain domain, ServiceGroupRO sgo, String docid, String docSch){ + ServiceMetadataRO sgdmd = new ServiceMetadataRO(); + sgdmd.setDomainCode(domain.getDomainCode()); + sgdmd.setSmlSubdomain(domain.getSmlSubdomain()); + sgdmd.setDocumentIdentifier(docid); + sgdmd.setDocumentIdentifierScheme(docSch); + sgdmd.setXmlContent(generateServiceMetadata(sgo.getParticipantIdentifier(), sgo.getParticipantScheme(), docid,docSch )); + return sgdmd; + } + + public static ServiceGroupDomainRO createServiceGroupDomain(DBDomain domain){ + + ServiceGroupDomainRO sgd = new ServiceGroupDomainRO(); + sgd.setDomainId(domain.getId()); + sgd.setDomainCode(domain.getDomainCode()); + sgd.setSmlSubdomain(domain.getSmlSubdomain()); + return sgd; + } + + public static ServiceGroupRO createROServiceGroup() { + return createROServiceGroup(TestConstants.TEST_SG_ID_1, TestConstants.TEST_SG_SCHEMA_1); + } + + public static ServiceGroupRO createROServiceGroupForDomains(DBDomain ... domains) { + ServiceGroupRO sgo = createROServiceGroup(TestConstants.TEST_SG_ID_1, TestConstants.TEST_SG_SCHEMA_1); + Arrays.asList(domains).forEach(domain -> { + ServiceGroupDomainRO sgd = createServiceGroupDomain(domain); + sgo.getServiceGroupDomains().add(sgd); + }); + return sgo; + } + + public static ServiceGroupRO createROServiceGroup(String id, String sch) { + return createROServiceGroup(id, sch, true); + } + + public static ServiceGroupRO createROServiceGroup(String id, String sch, boolean withExtension) { + ServiceGroupRO grp = new ServiceGroupRO(); + grp.setStatus(EntityROStatus.NEW.getStatusNumber()); + grp.setParticipantIdentifier(id); + grp.setParticipantScheme(sch); + if (withExtension) { + grp.setExtensionStatus(EntityROStatus.NEW.getStatusNumber()); + grp.setExtension(generateExtension()); + } + return grp; + } + + public static String generateExtension(){ + return String.format(SIMPLE_EXTENSION_XML, UUID.randomUUID().toString()); + } + + public static String generateServiceMetadata(String partId, String partSch, String docId, String docSch){ + return String.format(SIMPLE_DOCUMENT_XML, partSch, partId,docSch, docId, UUID.randomUUID().toString() ); + } + + public static ServiceGroupExtensionRO getValidExtension() throws IOException { + String inputDoc = XmlTestUtils.loadDocumentAsString(RES_PATH + "extensionValidOne.xml"); + return getExtensionRO(inputDoc); + } + + + public static ServiceGroupExtensionRO getValidMultipleExtension() throws IOException { + String inputDoc = XmlTestUtils.loadDocumentAsString(RES_PATH + "extensionValidMultiple.xml"); + return getExtensionRO(inputDoc); + } + + public static ServiceGroupExtensionRO getValidCustomText() throws IOException { + String inputDoc = XmlTestUtils.loadDocumentAsString(RES_PATH + "extensionCustomText.xml"); + return getExtensionRO(inputDoc); + } + + public static ServiceGroupExtensionRO getInvalid() throws IOException { + String inputDoc = XmlTestUtils.loadDocumentAsString(RES_PATH + "extensionInvalid.xml"); + return getExtensionRO(inputDoc); + } + + public static ServiceGroupExtensionRO getCustomExtension() throws IOException { + String inputDoc = XmlTestUtils.loadDocumentAsString(RES_PATH + "extensionCustom.xml"); + return getExtensionRO(inputDoc); + } + + + public static ServiceGroupExtensionRO getExtensionRO(String extension) { + ServiceGroupExtensionRO sg = new ServiceGroupExtensionRO(); + sg.setServiceGroupId((long) 1); + sg.setExtension(extension); + return sg; + } +} diff --git a/smp-server-library/src/test/resources/examples/extensions/extensionCustom.xml b/smp-server-library/src/test/resources/examples/extensions/extensionCustom.xml new file mode 100644 index 0000000000000000000000000000000000000000..a5038039ef20bc4925f0589d80c27bdbf940b1aa --- /dev/null +++ b/smp-server-library/src/test/resources/examples/extensions/extensionCustom.xml @@ -0,0 +1,3 @@ +<Extension xmlns="http://docs.oasis-open.org/bdxr/ns/SMP/2016/05"> + <ext:example xmlns:ext="http://my.namespace.eu">Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</ext:example> +</Extension> \ No newline at end of file diff --git a/smp-server-library/src/test/resources/examples/extensions/extensionCustomText.xml b/smp-server-library/src/test/resources/examples/extensions/extensionCustomText.xml new file mode 100644 index 0000000000000000000000000000000000000000..08e00ed29169d1c8876c8d593fc2d675df3b61bf --- /dev/null +++ b/smp-server-library/src/test/resources/examples/extensions/extensionCustomText.xml @@ -0,0 +1 @@ +Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. \ No newline at end of file diff --git a/smp-server-library/src/test/resources/examples/extensions/extensionInvalid.xml b/smp-server-library/src/test/resources/examples/extensions/extensionInvalid.xml new file mode 100644 index 0000000000000000000000000000000000000000..61fe8faf7bdeb82acb066d631f029f099855b9ea --- /dev/null +++ b/smp-server-library/src/test/resources/examples/extensions/extensionInvalid.xml @@ -0,0 +1,11 @@ +<Extension xmlns="http://docs.oasis-open.org/bdxr/ns/SMP/2016/05"> + <ExtensionName>name1</ExtensionName> + <ExtensionID>id1</ExtensionID> + <ExtensionName>name1</ExtensionName> + <ExtensionAgencyName>agencyName1</ExtensionAgencyName> + <ExtensionAgencyURI>agencyUri1</ExtensionAgencyURI> + <ExtensionVersionID>versionId1</ExtensionVersionID> + <ExtensionReasonCode>reasonCode1</ExtensionReasonCode> + <ExtensionReason>reason1</ExtensionReason> + <ext:example xmlns:ext="http://my.namespace.eu">Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</ext:example> +</Extension> diff --git a/smp-server-library/src/test/resources/examples/extensions/extensionValidMultiple.xml b/smp-server-library/src/test/resources/examples/extensions/extensionValidMultiple.xml new file mode 100644 index 0000000000000000000000000000000000000000..579531c7734f33f32106afcadb3008ecfade2c3b --- /dev/null +++ b/smp-server-library/src/test/resources/examples/extensions/extensionValidMultiple.xml @@ -0,0 +1,20 @@ +<Extension xmlns="http://docs.oasis-open.org/bdxr/ns/SMP/2016/05"> + <ExtensionID>id1</ExtensionID> + <ExtensionName>name1</ExtensionName> + <ExtensionAgencyName>agencyName1</ExtensionAgencyName> + <ExtensionAgencyURI>agencyUri1</ExtensionAgencyURI> + <ExtensionVersionID>versionId1</ExtensionVersionID> + <ExtensionReasonCode>reasonCode1</ExtensionReasonCode> + <ExtensionReason>reason1</ExtensionReason> + <ext:example xmlns:ext="http://my.namespace.eu">token1</ext:example> +</Extension> +<Extension xmlns="http://docs.oasis-open.org/bdxr/ns/SMP/2016/05"> +<ExtensionID>id1</ExtensionID> +<ExtensionName>name1</ExtensionName> +<ExtensionAgencyName>agencyName1</ExtensionAgencyName> +<ExtensionAgencyURI>agencyUri1</ExtensionAgencyURI> +<ExtensionVersionID>versionId1</ExtensionVersionID> +<ExtensionReasonCode>reasonCode1</ExtensionReasonCode> +<ExtensionReason>reason1</ExtensionReason> +<ext:example xmlns:ext="http://my.namespace.eu">Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</ext:example> +</Extension> \ No newline at end of file diff --git a/smp-server-library/src/test/resources/examples/extensions/extensionValidOne.xml b/smp-server-library/src/test/resources/examples/extensions/extensionValidOne.xml new file mode 100644 index 0000000000000000000000000000000000000000..3c667f4119e5e035099a612fd033e3c4dd2e4515 --- /dev/null +++ b/smp-server-library/src/test/resources/examples/extensions/extensionValidOne.xml @@ -0,0 +1,10 @@ +<Extension xmlns="http://docs.oasis-open.org/bdxr/ns/SMP/2016/05"> + <ExtensionID>id1</ExtensionID> + <ExtensionName>name1</ExtensionName> + <ExtensionAgencyName>agencyName1</ExtensionAgencyName> + <ExtensionAgencyURI>agencyUri1</ExtensionAgencyURI> + <ExtensionVersionID>versionId1</ExtensionVersionID> + <ExtensionReasonCode>reasonCode1</ExtensionReasonCode> + <ExtensionReason>reason1</ExtensionReason> + <ext:example xmlns:ext="http://my.namespace.eu">Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</ext:example> +</Extension> diff --git a/smp-server-library/src/test/resources/truststore/NewPeppolAP1.crt b/smp-server-library/src/test/resources/truststore/NewPeppolAP1.crt new file mode 100644 index 0000000000000000000000000000000000000000..01776da16c00a9089467f4849dc7679e08e1582b Binary files /dev/null and b/smp-server-library/src/test/resources/truststore/NewPeppolAP1.crt differ diff --git a/smp-server-library/src/test/resources/truststore/NewPeppolAP2.crt b/smp-server-library/src/test/resources/truststore/NewPeppolAP2.crt new file mode 100644 index 0000000000000000000000000000000000000000..afaa0f8ed3825834888eb6b0241044bb3ff5148e --- /dev/null +++ b/smp-server-library/src/test/resources/truststore/NewPeppolAP2.crt @@ -0,0 +1,32 @@ +-----BEGIN CERTIFICATE----- MIIFyzCCA7OgAwIBAgIQR0mAxRR4z2J2FmdGGu9ejjANBgkqhkiG9w0BAQsFADBr +MQswCQYDVQQGEwJCRTEZMBcGA1UEChMQT3BlblBFUFBPTCBBSVNCTDEWMBQGA1UE +CxMNRk9SIFRFU1QgT05MWTEpMCcGA1UEAxMgUEVQUE9MIEFDQ0VTUyBQT0lOVCBU +RVNUIENBIC0gRzIwHhcNMTgwNDE3MDAwMDAwWhcNMjAwNDA2MjM1OTU5WjBYMRIw +EAYDVQQDDAlQT1AwMDAwMDQxFzAVBgNVBAsMDlBFUFBPTCBURVNUIEFQMRwwGgYD +VQQKDBNFdXJvcGVhbiBDb21taXNzaW9uMQswCQYDVQQGEwJCRTCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBAKJkqEcBWQ5L9QM+a+jg8YYalSlktpfqQowu +X+X4xZ+Sgmy7OtbxSciHJQjaRbOTCzjnLL/K2zaZdC/0xob32bX1yT7i95gYMmpU +JL8GVijtvOxCPyKPKdVBfk6NDOKNLKoSN6Zv3Eldn2wLuIru3iMvBycOds7hyYTg +1pFchrhHlMRvtWZQnrngc3I64crnZbaYY+KY5RYy5mNDN3BzDC3By3KYIL2VZNRb +347QKij4BMqTe69PWduYlamjI+HnNttkrAOSKYY0PHpUIxcoxqEStUhqC/AK0eZm +X4NLz6cOXT+01dYUWGy0u/I2T4IPFisn3FaIEBmDoGrNdV7UCLECAwEAAaOCAXww +ggF4MAwGA1UdEwEB/wQCMAAwDgYDVR0PAQH/BAQDAgOoMBYGA1UdJQEB/wQMMAoG +CCsGAQUFBwMCMB0GA1UdDgQWBBQ+CXL5Yjkduz8B0Tb0Qrq6sumZ7DBdBgNVHR8E +VjBUMFKgUKBOhkxodHRwOi8vcGtpLWNybC5zeW1hdXRoLmNvbS9jYV82YTkzNzcz +NGEzOTNhMDgwNWJmMzNjZGE4YjMzMTA5My9MYXRlc3RDUkwuY3JsMDcGCCsGAQUF +BwEBBCswKTAnBggrBgEFBQcwAYYbaHR0cDovL3BraS1vY3NwLnN5bWF1dGguY29t +MB8GA1UdIwQYMBaAFGtvS7bxN7orPH8Yzborsrl8KjfrMC0GCmCGSAGG+EUBEAME +HzAdBhNghkgBhvhFARABAgMBAYGpkOEDFgY5NTc2MDgwOQYKYIZIAYb4RQEQBQQr +MCkCAQAWJGFIUjBjSE02THk5d2Eya3RjbUV1YzNsdFlYVjBhQzVqYjIwPTANBgkq +hkiG9w0BAQsFAAOCAgEAFniNKek5ZSbmbn9svJaDX8yvbl6yX1UDpteayKUztkGz +M5+hAgpQz97hayLTturSJR8pO1WjeaYYiXseRGy9cLnxbzSSOyMzYATyEZx4f65F +DfJbsK2pWQEbgwcxFcKVn4rNOMt6Whh9bcMtBhpYMH+6mEhjXfRcJ108z+P3MHyi +XuFTD4oCwR5Y82xIRNTf4xPuyeVI86SR61CSZIq9gnns4YXpJtE/dpJIHybzcre3 +APTgYuCbBbyYbjh4Wn2hYEkYAFeQdIZuz9Xnvd0EZWzFWKjrX0dtDvETuOzEtkno +TI4lZCDK+W+YMrwIBsTM7b9in6gXXugJD+EdzwR7Pnl0YQIqObwifmDeR6wdM8Ny +ylyEELPBc1N96hjEMPkdL/XwLZ+m1Nij8/VNIEaQ1UhG2wtXMFVIlkxI3J55BFoO +Rwot/3z5VMoZQzBlH1xs9eTrVvHgsDJ2k8X7zqhGW00Yym+HNIL+tJxNvNM1RtJp +qY9aihjxlgkckASz5HpuRBJ2l0v3Di4scdprxCRVXAiic1Ff6goff7GT99p/cuVv +PH65AOoUpGcS6xfAmTolig9hZnNFl1aR+x4dF9CZpN6d4pIK6iD4ULg568yHgoOR +PHOUpVRNAJY5n0MmapG4/YdI4LWdgSj5m/s1UQwhmdWIqvCt64upzh7hwJeUQ1g= +-----END CERTIFICATE----- \ No newline at end of file diff --git a/smp-server-library/src/test/resources/keystores/SMPtest.crt b/smp-server-library/src/test/resources/truststore/SMPtest.crt similarity index 100% rename from smp-server-library/src/test/resources/keystores/SMPtest.crt rename to smp-server-library/src/test/resources/truststore/SMPtest.crt diff --git a/smp-server-library/src/test/resources/keystores/TestCase.crt b/smp-server-library/src/test/resources/truststore/TestCase.crt similarity index 100% rename from smp-server-library/src/test/resources/keystores/TestCase.crt rename to smp-server-library/src/test/resources/truststore/TestCase.crt diff --git a/smp-soapui-tests/groovy/mysql-4.1_integration_test_data.sql b/smp-soapui-tests/groovy/mysql-4.1_integration_test_data.sql index 9f1bcf7ed1cacc10e328a2579ccd5b34e333429a..aa3b950ce5196f20988e314278713e516902e8b0 100644 --- a/smp-soapui-tests/groovy/mysql-4.1_integration_test_data.sql +++ b/smp-soapui-tests/groovy/mysql-4.1_integration_test_data.sql @@ -4,6 +4,9 @@ insert into SMP_USER (ID, USERNAME, PASSWORD, ROLE, ACTIVE, CREATED_ON, LAST_UPD insert into SMP_USER (ID, USERNAME, PASSWORD, ROLE, ACTIVE, CREATED_ON, LAST_UPDATED_ON) values (4, 'AdminSMP2TEST', '$2a$10$h8Q3Kjbs6ZrGkU6ditjNueINlJOMDJ/g/OKiqFZy32WmdhLjV5TAi', 'SMP_ADMIN', 1, NOW(), NOW()); insert into SMP_USER (ID, USERNAME, PASSWORD, ROLE, ACTIVE, CREATED_ON, LAST_UPDATED_ON) values (5, 'test', '', 'SMP_ADMIN', 1, NOW(), NOW()); insert into SMP_USER (ID, USERNAME, PASSWORD, ROLE, ACTIVE, CREATED_ON, LAST_UPDATED_ON) values (6, 'test1', '$2a$06$toKXJgjqQINZdjQqSao3NeWz2n1S64PFPhVU1e8gIHh4xdbwzy1Uy', 'SMP_ADMIN', 1, NOW(), NOW()); +insert into SMP_USER (ID, USERNAME, PASSWORD, ROLE, ACTIVE, CREATED_ON, LAST_UPDATED_ON) values (7, 'system', '$2a$06$FDmjewn/do3C219uysNm9.XG8mIn.ubHnMydAzC8lsv61HsRpOR36', 'SYSTEM_ADMIN', 1, NOW(), NOW()); +insert into SMP_USER (ID, USERNAME, PASSWORD, ROLE, ACTIVE, CREATED_ON, LAST_UPDATED_ON) values (8, 'smp', '$2a$06$FDmjewn/do3C219uysNm9.XG8mIn.ubHnMydAzC8lsv61HsRpOR36', 'SMP_ADMIN', 1, NOW(), NOW()); +insert into SMP_USER (ID, USERNAME, PASSWORD, ROLE, ACTIVE, CREATED_ON, LAST_UPDATED_ON) values (9, 'user', '$2a$06$FDmjewn/do3C219uysNm9.XG8mIn.ubHnMydAzC8lsv61HsRpOR36', 'SERVICE_GROUP_ADMIN', 1, NOW(), NOW()); insert into SMP_USER (ID, USERNAME, PASSWORD, ROLE, ACTIVE, CREATED_ON, LAST_UPDATED_ON) values (10, 'EHEALTH_SMP_EC', '', 'SMP_ADMIN', 1, NOW(), NOW()); @@ -42,7 +45,7 @@ update SMP_USER_SEQ set next_val=100 where next_val=1; -- insert domain insert into SMP_DOMAIN (ID, DOMAIN_CODE, SML_SUBDOMAIN, SIGNATURE_KEY_ALIAS, CREATED_ON, LAST_UPDATED_ON) values (1, 'domain','subdomain','sig-key', NOW(), NOW()); insert into SMP_DOMAIN (ID, DOMAIN_CODE, SML_SUBDOMAIN, SML_SMP_ID, SIGNATURE_KEY_ALIAS, CREATED_ON, LAST_UPDATED_ON) values (2, 'domainB','subdomain002', 'CEF-SMP-002','sig-key',CURRENT_TIMESTAMP(),CURRENT_TIMESTAMP()); -insert into SMP_DOMAIN (ID, DOMAIN_CODE, SML_SUBDOMAIN, SML_SMP_ID, SIGNATURE_KEY_ALIAS, CREATED_ON, LAST_UPDATED_ON) values (3, 'domainC','subdomain003', 'CEF-SMP-003','sig-key',CURRENT_TIMESTAMP(),CURRENT_TIMESTAMP()); +insert into SMP_DOMAIN (ID, DOMAIN_CODE, SML_SUBDOMAIN, SML_SMP_ID, SIGNATURE_KEY_ALIAS, CREATED_ON, LAST_UPDATED_ON) values (3, 'domainC$$','subdomain003$$', 'CEF$$SMP-003','sig-key',CURRENT_TIMESTAMP(),CURRENT_TIMESTAMP()); insert into SMP_DOMAIN (ID, DOMAIN_CODE, SML_SUBDOMAIN, SML_SMP_ID, SIGNATURE_KEY_ALIAS, CREATED_ON, LAST_UPDATED_ON) values (4, 'domainD','subdomain004', 'CEF-SMP-004','sig-key',CURRENT_TIMESTAMP(),CURRENT_TIMESTAMP()); insert into SMP_DOMAIN (ID, DOMAIN_CODE, SML_SUBDOMAIN, SML_SMP_ID, SIGNATURE_KEY_ALIAS, CREATED_ON, LAST_UPDATED_ON) values (5, 'domainE','subdomain005', 'CEF-SMP-005','sig-key',CURRENT_TIMESTAMP(),CURRENT_TIMESTAMP()); insert into SMP_DOMAIN (ID, DOMAIN_CODE, SML_SUBDOMAIN, SML_SMP_ID, SIGNATURE_KEY_ALIAS, CREATED_ON, LAST_UPDATED_ON) values (6, 'domainF','subdomain006', 'CEF-SMP-006','sig-key',CURRENT_TIMESTAMP(),CURRENT_TIMESTAMP()); diff --git a/smp-soapui-tests/soapui/project-temp-8349944969839799751.xml b/smp-soapui-tests/soapui/project-temp-8349944969839799751.xml new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/auth/SMPAuthenticationProvider.java b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/auth/SMPAuthenticationProvider.java index d7a9a3a55f5dee03edff09044ed810a20d767852..79dd665258745f0df385776475d9799adadd5a59 100644 --- a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/auth/SMPAuthenticationProvider.java +++ b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/auth/SMPAuthenticationProvider.java @@ -40,10 +40,10 @@ public class SMPAuthenticationProvider implements AuthenticationProvider { // Do not reveal the status of an existing account. Not to use UsernameNotFoundException throw new BadCredentialsException("Login failed; Invalid userID or password"); } - DBUser usr = oUsr.get(); - String role = usr.getRole(); + DBUser user = oUsr.get(); + String role = user.getRole(); try { - if (!BCrypt.checkpw(password, usr.getPassword())) { + if (!BCrypt.checkpw(password, user.getPassword())) { LOG.securityWarn(SMPMessageCode.SEC_INVALID_PASSWORD, username); throw new BadCredentialsException("Login failed; Invalid userID or password"); } @@ -54,12 +54,11 @@ public class SMPAuthenticationProvider implements AuthenticationProvider { throw new BadCredentialsException("Login failed; Invalid userID or password"); } LOG.securityInfo(SMPMessageCode.SEC_USER_AUTHENTICATED, username, role); - return new UsernamePasswordAuthenticationToken(username, password,Collections.singletonList(new SMPAuthority(role))); - + return new SMPAuthenticationToken(username, password,Collections.singletonList(new SMPAuthority(role)), user); } @Override public boolean supports(Class<?> auth) { - return auth.equals(UsernamePasswordAuthenticationToken.class); + return auth.equals(UsernamePasswordAuthenticationToken.class) || auth.equals(SMPAuthenticationToken.class); } } diff --git a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/auth/SMPAuthenticationService.java b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/auth/SMPAuthenticationService.java new file mode 100644 index 0000000000000000000000000000000000000000..c8ab53fc8c81a38a9b309ce60b2cf254d668c83b --- /dev/null +++ b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/auth/SMPAuthenticationService.java @@ -0,0 +1,32 @@ +package eu.europa.ec.edelivery.smp.auth; + +import eu.europa.ec.edelivery.smp.logging.SMPLogger; +import eu.europa.ec.edelivery.smp.logging.SMPLoggerFactory; +import eu.europa.ec.edelivery.smp.ui.AuthenticationResource; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +public class SMPAuthenticationService { + + private static final SMPLogger LOG = SMPLoggerFactory.getLogger(SMPAuthenticationService.class); + + @Autowired + @Qualifier("smpAuthenticationManager") + private AuthenticationManager authenticationManager; + + @Transactional(noRollbackFor = AuthenticationException.class) + public Authentication authenticate(String username, String password) { + UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(username, password); + UsernamePasswordAuthenticationToken authentication = (UsernamePasswordAuthenticationToken) authenticationManager.authenticate(token); + SecurityContextHolder.getContext().setAuthentication(authentication); + return authentication; + } +} \ No newline at end of file diff --git a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/auth/SMPAuthenticationToken.java b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/auth/SMPAuthenticationToken.java new file mode 100644 index 0000000000000000000000000000000000000000..02967d7f38782a1ac0e7a02e9383ea9dbc7879d5 --- /dev/null +++ b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/auth/SMPAuthenticationToken.java @@ -0,0 +1,24 @@ +package eu.europa.ec.edelivery.smp.auth; + +import eu.europa.ec.edelivery.smp.data.model.DBUser; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.GrantedAuthority; + +import java.util.Collection; + +public class SMPAuthenticationToken extends UsernamePasswordAuthenticationToken { + DBUser user; + + public SMPAuthenticationToken(Object principal, Object credentials, Collection<? extends GrantedAuthority> authorities) { + super(principal,credentials, authorities ); + } + + public SMPAuthenticationToken(Object principal, Object credentials, Collection<? extends GrantedAuthority> authorities, DBUser user) { + super(principal,credentials, authorities ); + this.user = user; + } + + public DBUser getUser() { + return user; + } +} diff --git a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/auth/SMPAuthority.java b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/auth/SMPAuthority.java index 8a64b60ec1494819837b9714ffc1e57db810b340..38ac2682072b3cbb49ce618231480c91bda43dea 100644 --- a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/auth/SMPAuthority.java +++ b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/auth/SMPAuthority.java @@ -6,9 +6,14 @@ import org.springframework.security.core.GrantedAuthority; public class SMPAuthority implements GrantedAuthority { // static constants for annotations! - public static final String S_AUTHORITY_SYSTEM_ADMIN = "ROLE_SYSTEM_ADMIN"; - public static final String S_AUTHORITY_SMP_ADMIN = "ROLE_SMP_ADMIN"; - public static final String S_AUTHORITY_SERVICE_GROUP_ADMIN = "ROLE_SERVICE_GROUP_ADMIN"; + public static final String S_AUTHORITY_TOKEN_SYSTEM_ADMIN = "ROLE_SYSTEM_ADMIN"; + public static final String S_AUTHORITY_TOKEN_SMP_ADMIN = "ROLE_SMP_ADMIN"; + public static final String S_AUTHORITY_TOKEN_SERVICE_GROUP_ADMIN = "ROLE_SERVICE_GROUP_ADMIN"; + + // static constants for verification... + public static final SMPAuthority S_AUTHORITY_SYSTEM_ADMIN = new SMPAuthority(SMPRole.SYSTEM_ADMIN.getCode()); + public static final SMPAuthority S_AUTHORITY_SMP_ADMIN = new SMPAuthority(SMPRole.SMP_ADMIN.getCode()); + public static final SMPAuthority S_AUTHORITY_SERVICE_GROUP = new SMPAuthority(SMPRole.SERVICE_GROUP_ADMIN.getCode()); String role; diff --git a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/auth/URLCsrfMatcher.java b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/auth/URLCsrfMatcher.java new file mode 100644 index 0000000000000000000000000000000000000000..260294500cebd3ff69c97ee9889bb95a8ed5bbf5 --- /dev/null +++ b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/auth/URLCsrfMatcher.java @@ -0,0 +1,45 @@ +package eu.europa.ec.edelivery.smp.auth; + +import eu.europa.ec.edelivery.smp.logging.SMPLoggerFactory; +import org.slf4j.Logger; +import org.springframework.security.web.util.matcher.RegexRequestMatcher; +import org.springframework.security.web.util.matcher.RequestMatcher; + +import javax.annotation.PostConstruct; +import javax.servlet.http.HttpServletRequest; +import java.util.Arrays; +import java.util.HashSet; + +public class URLCsrfMatcher implements RequestMatcher { + + private static final Logger LOGGER = SMPLoggerFactory.getLogger(URLCsrfMatcher.class); + + protected String ignoreUrl; + + private RegexRequestMatcher unprotectedMatcher = null; + + private final HashSet<String> allowedMethods = new HashSet<String>( Arrays.asList("GET", "HEAD", "TRACE", "OPTIONS")); + + @PostConstruct + public void init() { + LOGGER.debug("Initializing the matcher with [{}]", ignoreUrl); + unprotectedMatcher = new RegexRequestMatcher(ignoreUrl, null); + } + + @Override + public boolean matches(HttpServletRequest request) { + if(this.allowedMethods.contains(request.getMethod())) { + LOGGER.trace("Matched method [{}]", request.getMethod()); + return false; + } + return !unprotectedMatcher.matches(request); + } + + public String getIgnoreUrl() { + return ignoreUrl; + } + + public void setIgnoreUrl(String ignoreUrl) { + this.ignoreUrl = ignoreUrl; + } +} \ No newline at end of file diff --git a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/config/SmpWebAppConfig.java b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/config/SmpWebAppConfig.java index e2efdcdeb406f916718121883fd9e8e62f3ba0d9..abe9526b17b6d1efb8d0f2135573a3707c71af03 100644 --- a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/config/SmpWebAppConfig.java +++ b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/config/SmpWebAppConfig.java @@ -46,7 +46,7 @@ import static org.springframework.core.Ordered.HIGHEST_PRECEDENCE; "eu.europa.ec.edelivery.smp.ui"}) @Import({GlobalMethodSecurityConfig.class, ErrorMappingControllerAdvice.class}) public class SmpWebAppConfig implements WebMvcConfigurer { - +/* @Override public void extendMessageConverters(List<HttpMessageConverter<?>> converters) { // There is no clean way of replacing the default Jackson 2 HttpMessageConverter while keeping the other @@ -74,10 +74,9 @@ public class SmpWebAppConfig implements WebMvcConfigurer { public void addViewControllers(ViewControllerRegistry registry) { registry.addViewController("/").setViewName("/index.html"); registry.addViewController("/ui/").setViewName("/ui/index.html"); - //Home page used by SMP 2.x and 3.x - needed for backward compatibility in some EC's environments registry.addViewController("/web/index.html").setViewName("/index.html"); - } + }*/ @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { diff --git a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/config/SpringSecurityConfig.java b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/config/SpringSecurityConfig.java index 6cd40234dc81c86e54869c816c0be8bd63550f93..499c3fe9e9fef5d7de0925f644ff30c24534aef9 100644 --- a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/config/SpringSecurityConfig.java +++ b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/config/SpringSecurityConfig.java @@ -13,6 +13,7 @@ package eu.europa.ec.edelivery.smp.config; +import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.ImportResource; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; @@ -22,5 +23,6 @@ import org.springframework.security.config.annotation.web.configuration.EnableWe @EnableWebSecurity @ImportResource("classpath:spring-security.xml") +@ComponentScan("eu.europa.ec.edelivery.smp.auth") public class SpringSecurityConfig { } diff --git a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/controllers/RootController.java b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/controllers/RootController.java new file mode 100644 index 0000000000000000000000000000000000000000..0384287dccfa934ddf131b79d29a0dd060069a47 --- /dev/null +++ b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/controllers/RootController.java @@ -0,0 +1,65 @@ +/* + * Copyright 2017 European Commission | CEF eDelivery + * + * 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 attached in file: LICENCE-EUPL-v1.2.pdf + * + * 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. + */ + +package eu.europa.ec.edelivery.smp.controllers; + +import eu.europa.ec.edelivery.smp.logging.SMPLogger; +import eu.europa.ec.edelivery.smp.logging.SMPLoggerFactory; +import eu.europa.ec.edelivery.smp.logging.SMPMessageCode; +import org.apache.commons.lang.StringUtils; +import org.apache.cxf.helpers.IOUtils; +import org.springframework.core.annotation.Order; +import org.springframework.http.MediaType; +import org.springframework.ui.ModelMap; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.servlet.ModelAndView; + +import javax.servlet.http.HttpServletRequest; +import java.io.IOException; + +import static org.springframework.core.Ordered.HIGHEST_PRECEDENCE; +import static org.springframework.core.Ordered.LOWEST_PRECEDENCE; + + +@RestController +@RequestMapping("/") +public class RootController { + + private static final SMPLogger LOG = SMPLoggerFactory.getLogger(RootController.class); + + + @GetMapping( produces = MediaType.TEXT_HTML_VALUE, value={"", "/", "web/index.html","index.html"}) + @Order(HIGHEST_PRECEDENCE) + public byte[] getServiceGroup(HttpServletRequest httpReq) throws IOException { + String host = httpReq.getRemoteHost(); + LOG.businessInfo(SMPMessageCode.BUS_HTTP_GET_END_STATIC_CONTENT,host,httpReq.getPathInfo()); + return IOUtils.readBytesFromStream(RootController.class.getResourceAsStream("/index.html")); + } + + /** + * redirect angular pages to index.html + * solve the 404 error on refresh + * @param model + * @return +*/ + //@GetMapping(value={"/ui","/ui/edit","/ui/search","/ui/search","/ui/domain","/ui/user"}) + @GetMapping(value={"/ui"}) + public ModelAndView redirectWithUsingRedirectPrefix(ModelMap model) { + return new ModelAndView("redirect:/ui/index.html", model); + } + + public String getRemoteHost(HttpServletRequest httpReq){ + String host = httpReq.getHeader("X-Forwarded-For"); + return StringUtils.isBlank(host)?httpReq.getRemoteHost():host; + } +} diff --git a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/controllers/ServiceGroupController.java b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/controllers/ServiceGroupController.java index ce39c2a8fe3fa5faf6269c9b435d5f8dc45debf2..67ca91528cac21270b5833deeddbbbb4cda73ff6 100644 --- a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/controllers/ServiceGroupController.java +++ b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/controllers/ServiceGroupController.java @@ -29,24 +29,18 @@ import org.oasis_open.docs.bdxr.ns.smp._2016._05.DocumentIdentifier; import org.oasis_open.docs.bdxr.ns.smp._2016._05.ParticipantIdentifierType; import org.oasis_open.docs.bdxr.ns.smp._2016._05.ServiceGroup; import org.oasis_open.docs.bdxr.ns.smp._2016._05.ServiceMetadataReferenceType; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.core.annotation.Order; -import org.springframework.http.HttpHeaders; import org.springframework.http.ResponseEntity; import org.springframework.security.access.annotation.Secured; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.web.bind.annotation.*; import javax.servlet.http.HttpServletRequest; -import javax.ws.rs.core.Context; import java.util.List; import static eu.europa.ec.edelivery.smp.controllers.WebConstans.HTTP_PARAM_DOMAIN; import static eu.europa.ec.edelivery.smp.controllers.WebConstans.HTTP_PARAM_OWNER; import static eu.europa.ec.smp.api.Identifiers.asParticipantId; -import static org.springframework.core.Ordered.HIGHEST_PRECEDENCE; import static org.springframework.http.ResponseEntity.created; import static org.springframework.http.ResponseEntity.ok; @@ -89,7 +83,7 @@ public class ServiceGroupController { @PutMapping - @Secured({SMPAuthority.S_AUTHORITY_SYSTEM_ADMIN, SMPAuthority.S_AUTHORITY_SMP_ADMIN}) + @Secured({SMPAuthority.S_AUTHORITY_TOKEN_SYSTEM_ADMIN, SMPAuthority.S_AUTHORITY_TOKEN_SMP_ADMIN}) public ResponseEntity saveServiceGroup(HttpServletRequest httpReq, @PathVariable String serviceGroupId, @RequestHeader(name = HTTP_PARAM_OWNER, required = false) String serviceGroupOwner, @@ -113,7 +107,7 @@ public class ServiceGroupController { } @DeleteMapping - @Secured({SMPAuthority.S_AUTHORITY_SYSTEM_ADMIN, SMPAuthority.S_AUTHORITY_SMP_ADMIN}) + @Secured({SMPAuthority.S_AUTHORITY_TOKEN_SYSTEM_ADMIN, SMPAuthority.S_AUTHORITY_TOKEN_SMP_ADMIN}) public void deleteServiceGroup(HttpServletRequest httpReq, @PathVariable String serviceGroupId) { String authentUser = SecurityContextHolder.getContext().getAuthentication().getName(); String host = getRemoteHost(httpReq); diff --git a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/controllers/ServiceMetadataController.java b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/controllers/ServiceMetadataController.java index 9a8c649d9bc368933786da34b320004f25df64df..9a723fd0b82c2719905070c3f25f40d3bd4c239c 100644 --- a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/controllers/ServiceMetadataController.java +++ b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/controllers/ServiceMetadataController.java @@ -77,7 +77,7 @@ public class ServiceMetadataController { } @PutMapping - @PreAuthorize("hasAnyAuthority(T(eu.europa.ec.edelivery.smp.auth.SMPAuthority).S_AUTHORITY_SMP_ADMIN) OR" + + @PreAuthorize("hasAnyAuthority(T(eu.europa.ec.edelivery.smp.auth.SMPAuthority).S_AUTHORITY_TOKEN_SMP_ADMIN) OR" + " @serviceGroupService.isServiceGroupOwner(authentication.name, #serviceGroupId)") public ResponseEntity saveServiceMetadata(HttpServletRequest httpReq, @PathVariable String serviceGroupId, @@ -99,7 +99,7 @@ public class ServiceMetadataController { } @DeleteMapping - @PreAuthorize("hasAnyAuthority(T(eu.europa.ec.edelivery.smp.auth.SMPAuthority).S_AUTHORITY_SMP_ADMIN) OR" + + @PreAuthorize("hasAnyAuthority(T(eu.europa.ec.edelivery.smp.auth.SMPAuthority).S_AUTHORITY_TOKEN_SMP_ADMIN) OR" + " @serviceGroupService.isServiceGroupOwner(authentication.name, #serviceGroupId)") public ResponseEntity deleteServiceMetadata(HttpServletRequest httpReq, @PathVariable String serviceGroupId, diff --git a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/monitor/MonitorResource.java b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/monitor/MonitorResource.java index 96850dbcac3193c009a2a3cf941ea59026ab4be7..b4aa325dd4dc7edf40b3bdcebf3ed5bfb3761394 100644 --- a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/monitor/MonitorResource.java +++ b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/monitor/MonitorResource.java @@ -7,12 +7,14 @@ import eu.europa.ec.edelivery.smp.data.dao.DomainDao; import eu.europa.ec.edelivery.smp.data.dao.ServiceGroupDao; import eu.europa.ec.edelivery.smp.data.model.DBDomain; import eu.europa.ec.edelivery.smp.data.model.DBServiceGroup; +import eu.europa.ec.edelivery.smp.exceptions.SMPTestIsALiveException; import eu.europa.ec.edelivery.smp.logging.SMPLogger; import eu.europa.ec.edelivery.smp.logging.SMPLoggerFactory; import eu.europa.ec.edelivery.smp.services.ServiceGroupService; import eu.europa.ec.edelivery.smp.validation.ServiceGroupValidator; import eu.europa.ec.smp.api.exceptions.XmlInvalidAgainstSchemaException; import eu.europa.ec.smp.api.validators.BdxSmpOasisValidator; +import org.apache.commons.lang3.exception.ExceptionUtils; import org.apache.cxf.helpers.IOUtils; import org.oasis_open.docs.bdxr.ns.smp._2016._05.ServiceGroup; import org.springframework.beans.factory.annotation.Autowired; @@ -58,7 +60,7 @@ public class MonitorResource { @RequestMapping(method = RequestMethod.GET,path = "/is-alive") - @Secured({SMPAuthority.S_AUTHORITY_SYSTEM_ADMIN,SMPAuthority.S_AUTHORITY_SMP_ADMIN}) + @Secured({SMPAuthority.S_AUTHORITY_TOKEN_SYSTEM_ADMIN,SMPAuthority.S_AUTHORITY_TOKEN_SMP_ADMIN}) public ResponseEntity isAlive() { String user = SecurityContextHolder.getContext().getAuthentication().getName(); @@ -86,8 +88,10 @@ public class MonitorResource { boolean suc = false; try { suc= testDatabase(); - }catch (Exception ex){ + }catch (SMPTestIsALiveException ex){ suc = Objects.equals(TEST_DB_SUCCESSFUL_ROLLBACK, ex.getMessage()); + } catch(Throwable th) { + LOG.error("Error occured while testing database connection: Msg:" + ExceptionUtils.getRootCauseMessage(th), th); } return suc?ResponseEntity.ok().build():ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build(); @@ -105,9 +109,8 @@ public class MonitorResource { newSg.setParticipantScheme(TEST_PART_SCHEMA); newSg.setExtension(TEST_EXTENSION_XML.getBytes()); newSg.addDomain(lstDomain.get(0)); // add initial domain - newSg.setSmlRegistered(false); // persist (make sure this is not in transaction) - serviceGroupDao.testPersist(newSg, TEST_DB_SUCCESSFUL_ROLLBACK); + serviceGroupDao.testPersist(newSg,true, TEST_DB_SUCCESSFUL_ROLLBACK); return false; } diff --git a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/ApplicationResource.java b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/ApplicationResource.java index 397288592f1c495e4f85a4601e30070a509a9dbf..94d0893b04b37bdaa893290b156aaab0bc926ed9 100644 --- a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/ApplicationResource.java +++ b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/ApplicationResource.java @@ -3,14 +3,20 @@ package eu.europa.ec.edelivery.smp.ui; import eu.europa.ec.edelivery.smp.data.ui.ServiceGroupRO; import eu.europa.ec.edelivery.smp.data.ui.ServiceResult; +import eu.europa.ec.edelivery.smp.data.ui.SmpInfoRO; import eu.europa.ec.edelivery.smp.services.ui.UIServiceGroupService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; import org.springframework.core.env.Environment; import org.springframework.web.bind.annotation.*; import javax.annotation.PostConstruct; +import java.io.File; +import java.net.URL; +import java.util.TimeZone; +import java.util.jar.Manifest; /** * @author Joze Rihtarsic @@ -24,7 +30,12 @@ public class ApplicationResource { @Autowired private Environment env; - private static final Logger LOGGER = LoggerFactory.getLogger(ApplicationResource.class); + @Value("${Artifact-Name}") + private String artifactName; + @Value("${Artifact-Version}") + private String artifactVersion; + @Value("${Build-Time}") + private String buildTime; @RequestMapping(method = RequestMethod.GET, path = "name") @@ -34,6 +45,24 @@ public class ApplicationResource { @RequestMapping(method = RequestMethod.GET, path = "rootContext") public String getRootContext() { - return env .getProperty("server.contextPath", "/"); + return env.getProperty("server.contextPath", "/"); } + + @RequestMapping(method = RequestMethod.GET, path = "info") + public SmpInfoRO getDisplayVersion() { + StringBuilder display = new StringBuilder(); + display.append(artifactName); + display.append(" Version ["); + display.append(artifactVersion); + display.append("] Build-Time ["); + display.append(buildTime + "|" + TimeZone.getDefault().getDisplayName()); + display.append("]"); + + SmpInfoRO info = new SmpInfoRO(); + info.setVersion(display.toString()); + return info; + } + + + } diff --git a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/AuthenticationResource.java b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/AuthenticationResource.java new file mode 100644 index 0000000000000000000000000000000000000000..3a80d1972eb96f6d379d7f1875bdea647d10aa0b --- /dev/null +++ b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/AuthenticationResource.java @@ -0,0 +1,93 @@ +package eu.europa.ec.edelivery.smp.ui; + + +import eu.europa.ec.edelivery.smp.auth.SMPAuthority; +import eu.europa.ec.edelivery.smp.data.ui.ErrorRO; +import eu.europa.ec.edelivery.smp.data.ui.LoginRO; +import eu.europa.ec.edelivery.smp.data.ui.UserRO; +import eu.europa.ec.edelivery.smp.logging.SMPLogger; +import eu.europa.ec.edelivery.smp.logging.SMPLoggerFactory; +import eu.europa.ec.edelivery.smp.auth.SMPAuthenticationService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.security.access.annotation.Secured; +import org.springframework.security.authentication.BadCredentialsException; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.web.authentication.logout.CookieClearingLogoutHandler; +import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.bind.annotation.*; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import static java.util.stream.Collectors.toList; + +/** + * @author Sebastian-Ion TINCU + * @since 4.0 + */ +@RestController +@RequestMapping(value = "/ui/rest/security") +public class AuthenticationResource { + + private static final SMPLogger LOG = SMPLoggerFactory.getLogger(AuthenticationResource.class); + + @Autowired + protected SMPAuthenticationService authenticationService; + + @ResponseStatus(value = HttpStatus.FORBIDDEN) + @ExceptionHandler({AuthenticationException.class}) + public ErrorRO handleException(Exception ex) { + LOG.error(ex.getMessage(), ex); + return new ErrorRO(ex.getMessage()); + } + + @RequestMapping(value = "authentication", method = RequestMethod.POST) + @Transactional(noRollbackFor = BadCredentialsException.class) + public UserRO authenticate(@RequestBody LoginRO loginRO, HttpServletResponse response) { + LOG.debug("Authenticating user [{}]", loginRO.getUsername()); + final Authentication principal = authenticationService.authenticate(loginRO.getUsername(), loginRO.getPassword()); + + UserRO userRO = new UserRO(); + userRO.setUsername(loginRO.getUsername()); + userRO.setAuthorities( + principal.getAuthorities() + .stream() + .map(authority -> authority.getAuthority()) + .collect(toList())); + return userRO; + } + + @RequestMapping(value = "authentication", method = RequestMethod.DELETE) + public void logout(HttpServletRequest request, HttpServletResponse response) { + Authentication auth = SecurityContextHolder.getContext().getAuthentication(); + if (auth == null) { + LOG.debug("Cannot perform logout: no user is authenticated"); + return; + } + + LOG.info("Logging out user [{}]", auth.getName()); + new CookieClearingLogoutHandler("JSESSIONID", "XSRF-TOKEN").logout(request, response, null); + LOG.info("Cleared cookies"); + new SecurityContextLogoutHandler().logout(request, response, auth); + LOG.info("Logged out"); + } + + // @PutMapping(produces = {"text/plain"}) + @RequestMapping(value = "user", method = RequestMethod.GET) + @Secured({SMPAuthority.S_AUTHORITY_TOKEN_SYSTEM_ADMIN, SMPAuthority.S_AUTHORITY_TOKEN_SMP_ADMIN, SMPAuthority.S_AUTHORITY_TOKEN_SERVICE_GROUP_ADMIN}) + public UserRO getUser() { + // User securityUser = (User) SecurityContextHolder.getContext().getAuthentication().getPrincipal(); + //return securityUser.getUsername(); + String username = (String)SecurityContextHolder.getContext().getAuthentication().getPrincipal(); + System.out.println("get user: " + username); + UserRO ur =new UserRO(); + ur.setUsername(username); + return ur; + + } + +} \ No newline at end of file diff --git a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/DomainResource.java b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/DomainResource.java index 7e9fff5b4c4a3aa25a959ce795708cbd32e8be88..262d6e2883023d87fe3d88379924fe5eb1ae1942 100644 --- a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/DomainResource.java +++ b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/DomainResource.java @@ -1,19 +1,18 @@ package eu.europa.ec.edelivery.smp.ui; +import eu.europa.ec.edelivery.smp.auth.SMPAuthority; import eu.europa.ec.edelivery.smp.data.ui.DomainRO; import eu.europa.ec.edelivery.smp.data.ui.ServiceResult; import eu.europa.ec.edelivery.smp.logging.SMPLogger; import eu.europa.ec.edelivery.smp.logging.SMPLoggerFactory; import eu.europa.ec.edelivery.smp.services.ui.UIDomainService; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.annotation.Secured; import org.springframework.web.bind.annotation.*; import javax.annotation.PostConstruct; import java.util.Arrays; -import java.util.List; /** * @author Joze Rihtarsic @@ -49,6 +48,7 @@ public class DomainResource { @PutMapping(produces = {"application/json"}) @RequestMapping(method = RequestMethod.PUT) + @Secured({SMPAuthority.S_AUTHORITY_TOKEN_SYSTEM_ADMIN}) public void updateDomainList(@RequestBody(required = true) DomainRO [] updateEntities ){ LOG.info("GOT LIST OF DomainRO to UPDATE: " + updateEntities.length); uiDomainService.updateDomainList(Arrays.asList(updateEntities)); diff --git a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/SearchResource.java b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/SearchResource.java index db693136d3bd9d80dcc9c2531bdaab6ee0f79e00..a89077b362082115a85a4060652ffe7043628947 100644 --- a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/SearchResource.java +++ b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/SearchResource.java @@ -1,18 +1,23 @@ package eu.europa.ec.edelivery.smp.ui; -import eu.europa.ec.edelivery.smp.data.ui.ServiceGroupRO; +import eu.europa.ec.edelivery.smp.data.dao.DomainDao; +import eu.europa.ec.edelivery.smp.data.model.DBDomain; +import eu.europa.ec.edelivery.smp.data.ui.ServiceGroupSearchRO; import eu.europa.ec.edelivery.smp.data.ui.ServiceResult; +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.ui.UIServiceGroupService; -import eu.europa.ec.edelivery.smp.ui.filters.ServiceGroupFilter; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import eu.europa.ec.edelivery.smp.services.ui.UIServiceGroupSearchService; +import eu.europa.ec.edelivery.smp.services.ui.filters.ServiceGroupFilter; +import org.apache.commons.lang.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import javax.annotation.PostConstruct; +import java.util.Optional; + +import static eu.europa.ec.edelivery.smp.exceptions.ErrorCode.DOMAIN_NOT_EXISTS; /** * @author Joze Rihtarsic @@ -26,8 +31,9 @@ public class SearchResource { private static final SMPLogger LOG = SMPLoggerFactory.getLogger(SearchResource.class); @Autowired - private UIServiceGroupService uiServiceGroupService; - + private UIServiceGroupSearchService uiServiceGroupService; + @Autowired + private DomainDao domainDao; @PostConstruct protected void init() { @@ -36,20 +42,23 @@ public class SearchResource { @PutMapping(produces = {"application/json"}) @ResponseBody @RequestMapping(method = RequestMethod.GET) - public ServiceResult<ServiceGroupRO> getServiceGroupList( + public ServiceResult<ServiceGroupSearchRO> getServiceGroupList( @RequestParam(value = "page", defaultValue = "0") int page, @RequestParam(value = "pageSize", defaultValue = "10") int pageSize, @RequestParam(value = "orderBy", required = false) String orderBy, @RequestParam(value = "orderType", defaultValue = "asc", required = false) String orderType, @RequestParam(value = "participantIdentifier", required = false) String participantIdentifier, @RequestParam(value = "participantScheme", required = false) String participantScheme, - @RequestParam(value = "domain", required = false) String domain - ) { + @RequestParam(value = "domain", required = false) String domainCode + ) { - LOG.info("Search for page: {}, page size: {}, part. id: {}, part sch: {}, domain {}",page, pageSize, participantIdentifier, participantScheme, domain ); + LOG.info("Search for page: {}, page size: {}, part. id: {}, part sch: {}, domain {}", page, pageSize, participantIdentifier, participantScheme, domainCode); ServiceGroupFilter sgf = new ServiceGroupFilter(); sgf.setParticipantIdentifierLike(participantIdentifier); sgf.setParticipantSchemeLike(participantScheme); - return uiServiceGroupService.getTableList(page,pageSize, orderBy, orderType, sgf ); + // add domain search parameter + sgf.setDomain(domainDao.validateDomainCode(domainCode)); + + return uiServiceGroupService.getTableList(page, pageSize, orderBy, orderType, sgf); } } diff --git a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/ServiceGroupResource.java b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/ServiceGroupResource.java index 543657db916756c851c4eecf8f33bf3c061c1a6b..78357bf5408070b332759b667a50ff5ff9fa7206 100644 --- a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/ServiceGroupResource.java +++ b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/ServiceGroupResource.java @@ -1,19 +1,34 @@ package eu.europa.ec.edelivery.smp.ui; -import eu.europa.ec.edelivery.smp.data.ui.DomainRO; +import eu.europa.ec.edelivery.smp.auth.SMPAuthenticationToken; +import eu.europa.ec.edelivery.smp.auth.SMPAuthority; +import eu.europa.ec.edelivery.smp.auth.SMPRole; +import eu.europa.ec.edelivery.smp.data.dao.DomainDao; +import eu.europa.ec.edelivery.smp.data.model.DBDomain; +import eu.europa.ec.edelivery.smp.data.model.DBUser; +import eu.europa.ec.edelivery.smp.data.ui.ServiceGroupExtensionRO; import eu.europa.ec.edelivery.smp.data.ui.ServiceGroupRO; import eu.europa.ec.edelivery.smp.data.ui.ServiceResult; +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.ui.UIServiceGroupService; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import eu.europa.ec.edelivery.smp.services.ui.filters.ServiceGroupFilter; +import org.apache.commons.lang.StringUtils; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.annotation.Secured; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.web.bind.annotation.*; import javax.annotation.PostConstruct; +import javax.servlet.http.HttpServletRequest; import java.util.Arrays; +import java.util.Optional; + +import static eu.europa.ec.edelivery.smp.exceptions.ErrorCode.DOMAIN_NOT_EXISTS; /** * @author Joze Rihtarsic @@ -24,10 +39,12 @@ import java.util.Arrays; @RequestMapping(value = "/ui/rest/servicegroup") public class ServiceGroupResource { - private static final SMPLogger LOG = SMPLoggerFactory.getLogger(DomainResource.class); + private static final SMPLogger LOG = SMPLoggerFactory.getLogger(ServiceGroupResource.class); @Autowired private UIServiceGroupService uiServiceGroupService; + @Autowired + private DomainDao domainDao; @PostConstruct protected void init() { @@ -37,29 +54,69 @@ public class ServiceGroupResource { @PutMapping(produces = {"application/json"}) @ResponseBody @RequestMapping(method = RequestMethod.GET) + @Secured({SMPAuthority.S_AUTHORITY_TOKEN_SYSTEM_ADMIN, SMPAuthority.S_AUTHORITY_TOKEN_SMP_ADMIN, SMPAuthority.S_AUTHORITY_TOKEN_SERVICE_GROUP_ADMIN}) public ServiceResult<ServiceGroupRO> getServiceGroupList( + HttpServletRequest request, @RequestParam(value = "page", defaultValue = "0") int page, @RequestParam(value = "pageSize", defaultValue = "10") int pageSize, @RequestParam(value = "orderBy", required = false) String orderBy, @RequestParam(value = "orderType", defaultValue = "asc", required = false) String orderType, - @RequestParam(value = "participantId", required = false) String participantId, - @RequestParam(value = "participantSchema", required = false) String participantSchema, - @RequestParam(value = "domain", required = false) String domain + @RequestParam(value = "participantIdentifier", required = false) String participantIdentifier, + @RequestParam(value = "participantScheme", required = false) String participantScheme, + @RequestParam(value = "domain", required = false) String domainCode ) { - return uiServiceGroupService.getTableList(page, pageSize, orderBy, orderType, null); + + LOG.info("Search for page: {}, page size: {}, part. id: {}, part sch: {}, domain {}",page, pageSize, participantIdentifier, participantScheme, domainCode ); + ServiceGroupFilter sgf = new ServiceGroupFilter(); + sgf.setParticipantIdentifierLike(participantIdentifier); + sgf.setParticipantSchemeLike(participantScheme); + // add domain search parameter + sgf.setDomain(domainDao.validateDomainCode(domainCode)); + + // check if logged user is ServiceGroup admin if yes return only his servicegroups + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + + // show all service groups only for SMP Admin + System.out.println("USER authorites: "+ authentication.getAuthorities().size()); + if (!request.isUserInRole(SMPRole.SMP_ADMIN.getCode())){ + SMPAuthenticationToken authToken = (SMPAuthenticationToken) authentication; + DBUser user = authToken.getUser(); + sgf.setOwner(user); + } + return uiServiceGroupService.getTableList(page,pageSize, orderBy, orderType, sgf); } @ResponseBody @PutMapping(produces = {"application/json"}) @RequestMapping(method = RequestMethod.GET, path = "{serviceGroupId}") + @Secured({SMPAuthority.S_AUTHORITY_TOKEN_SYSTEM_ADMIN, SMPAuthority.S_AUTHORITY_TOKEN_SMP_ADMIN, SMPAuthority.S_AUTHORITY_TOKEN_SERVICE_GROUP_ADMIN}) public ServiceGroupRO getServiceGroupById(@PathVariable Long serviceGroupId) { + return uiServiceGroupService.getServiceGroupById(serviceGroupId); + } + @ResponseBody + @PutMapping(produces = {"application/json"}) + @RequestMapping(method = RequestMethod.GET, path = "extension/{serviceGroupId}") + @Secured({SMPAuthority.S_AUTHORITY_TOKEN_SYSTEM_ADMIN, SMPAuthority.S_AUTHORITY_TOKEN_SMP_ADMIN, SMPAuthority.S_AUTHORITY_TOKEN_SERVICE_GROUP_ADMIN}) + public ServiceGroupExtensionRO getExtensionServiceGroupById(@PathVariable Long serviceGroupId) { + return uiServiceGroupService.getServiceGroupExtensionById(serviceGroupId); + } + @RequestMapping(path = "extension/validate", method = RequestMethod.POST) + @Secured({SMPAuthority.S_AUTHORITY_TOKEN_SYSTEM_ADMIN, SMPAuthority.S_AUTHORITY_TOKEN_SMP_ADMIN, SMPAuthority.S_AUTHORITY_TOKEN_SERVICE_GROUP_ADMIN}) + public ServiceGroupExtensionRO getExtensionServiceGroupById(@RequestBody(required = true) ServiceGroupExtensionRO sg) { + return uiServiceGroupService.validateExtension(sg); + } - return uiServiceGroupService.getServiceGroupById(serviceGroupId); + @RequestMapping(path = "extension/format", method = RequestMethod.POST) + @Secured({SMPAuthority.S_AUTHORITY_TOKEN_SYSTEM_ADMIN, SMPAuthority.S_AUTHORITY_TOKEN_SMP_ADMIN, SMPAuthority.S_AUTHORITY_TOKEN_SERVICE_GROUP_ADMIN}) + public ServiceGroupExtensionRO formatExtension(@RequestBody(required = true) ServiceGroupExtensionRO sg) { + return uiServiceGroupService.formatExtension(sg); } + @PutMapping(produces = {"application/json"}) @RequestMapping(method = RequestMethod.PUT) + @Secured({SMPAuthority.S_AUTHORITY_TOKEN_SYSTEM_ADMIN, SMPAuthority.S_AUTHORITY_TOKEN_SMP_ADMIN, SMPAuthority.S_AUTHORITY_TOKEN_SERVICE_GROUP_ADMIN}) public void updateDomainList(@RequestBody(required = true) ServiceGroupRO[] updateEntities ){ LOG.info("GOT LIST OF ServiceGroupRO to UPDATE: " + updateEntities.length); uiServiceGroupService.updateServiceGroupList(Arrays.asList(updateEntities)); diff --git a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/ServiceMetadataResource.java b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/ServiceMetadataResource.java new file mode 100644 index 0000000000000000000000000000000000000000..58ada3b6bd111ba1c86cd38768c6bc4e64f582c4 --- /dev/null +++ b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/ServiceMetadataResource.java @@ -0,0 +1,43 @@ +package eu.europa.ec.edelivery.smp.ui; + + +import eu.europa.ec.edelivery.smp.auth.SMPAuthority; +import eu.europa.ec.edelivery.smp.data.ui.ServiceMetadataRO; +import eu.europa.ec.edelivery.smp.data.ui.ServiceMetadataValidationRO; +import eu.europa.ec.edelivery.smp.logging.SMPLogger; +import eu.europa.ec.edelivery.smp.logging.SMPLoggerFactory; +import eu.europa.ec.edelivery.smp.services.ui.UIServiceMetadataService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.annotation.Secured; +import org.springframework.web.bind.annotation.*; + +/** + * @author Joze Rihtarsic + * @since 4.1 + */ + +@RestController +@RequestMapping(value = "/ui/rest/servicemetadata") +public class ServiceMetadataResource { + + private static final SMPLogger LOG = SMPLoggerFactory.getLogger(ServiceMetadataResource.class); + + @Autowired + private UIServiceMetadataService uiServiceMetadataService; + + + @ResponseBody + @PutMapping(produces = {"application/json"}) + @RequestMapping(method = RequestMethod.GET, path = "{serviceMetadataId}") + @Secured({SMPAuthority.S_AUTHORITY_TOKEN_SYSTEM_ADMIN, SMPAuthority.S_AUTHORITY_TOKEN_SMP_ADMIN, SMPAuthority.S_AUTHORITY_TOKEN_SERVICE_GROUP_ADMIN}) + public ServiceMetadataRO getServiceGroupById(@PathVariable Long serviceMetadataId) { + return uiServiceMetadataService.getServiceMetadataXMLById(serviceMetadataId); + } + + @RequestMapping(path = "validate", method = RequestMethod.POST) + @Secured({SMPAuthority.S_AUTHORITY_TOKEN_SYSTEM_ADMIN, SMPAuthority.S_AUTHORITY_TOKEN_SMP_ADMIN, SMPAuthority.S_AUTHORITY_TOKEN_SERVICE_GROUP_ADMIN}) + public ServiceMetadataValidationRO validateServiceMetadata(@RequestBody(required = true) ServiceMetadataValidationRO serviceMetadataValidationRO) { + return uiServiceMetadataService.validateServiceMetadata(serviceMetadataValidationRO); + } +} + diff --git a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/UserResource.java b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/UserResource.java index 210426927d7f2d35caa8bef67ecbffa2789cbb04..1f5131a626da85ea6568071407358e3db736d047 100644 --- a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/UserResource.java +++ b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/UserResource.java @@ -1,5 +1,7 @@ package eu.europa.ec.edelivery.smp.ui; + +import eu.europa.ec.edelivery.smp.auth.SMPAuthority; import eu.europa.ec.edelivery.smp.data.ui.CertificateRO; import eu.europa.ec.edelivery.smp.data.ui.ServiceResult; import eu.europa.ec.edelivery.smp.data.ui.UserRO; @@ -7,16 +9,11 @@ import eu.europa.ec.edelivery.smp.logging.SMPLogger; import eu.europa.ec.edelivery.smp.logging.SMPLoggerFactory; import eu.europa.ec.edelivery.smp.services.ui.UIUserService; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.PutMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.ResponseBody; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.security.access.annotation.Secured; +import org.springframework.web.bind.annotation.*; +import javax.annotation.PostConstruct; +import java.io.IOException; import java.security.cert.CertificateException; import java.util.Arrays; @@ -34,31 +31,43 @@ public class UserResource { @Autowired private UIUserService uiUserService; - @GetMapping + @PostConstruct + protected void init() { + + } + + @PutMapping(produces = {"application/json"}) @ResponseBody + @RequestMapping(method = RequestMethod.GET) + //update gui to call this when somebody is logged in. + // @Secured({SMPAuthority.S_AUTHORITY_TOKEN_SYSTEM_ADMIN, SMPAuthority.S_AUTHORITY_TOKEN_SMP_ADMIN, SMPAuthority.S_AUTHORITY_TOKEN_SERVICE_GROUP_ADMIN}) public ServiceResult<UserRO> getUsers( @RequestParam(value = "page", defaultValue = "0") int page, @RequestParam(value = "pageSize", defaultValue = "10") int pageSize, @RequestParam(value = "orderBy", required = false) String orderBy, @RequestParam(value = "orderType", defaultValue = "asc", required = false) String orderType, - @RequestParam(value = "user", required = false) String user) { + @RequestParam(value = "user", required = false) String user + ) { return uiUserService.getTableList(page,pageSize, orderBy, orderType, null); } @PutMapping(produces = {"application/json"}) + @RequestMapping(method = RequestMethod.PUT) + @Secured({SMPAuthority.S_AUTHORITY_TOKEN_SYSTEM_ADMIN}) public void updateUserList(@RequestBody(required = true) UserRO[] updateEntities ){ - LOG.info("Update user list, count: {}", updateEntities.length); + LOG.info("Update user list, count: {}" + updateEntities.length); uiUserService.updateUserList(Arrays.asList(updateEntities)); } - @PostMapping(path = "certdata") + @RequestMapping(path = "certdata", method = RequestMethod.POST) public CertificateRO uploadFile(@RequestBody byte[] data) { LOG.info("Got certificate data: " + data.length); try { return uiUserService.getCertificateData(data); - } catch (CertificateException e) { - LOG.error("Error occurred while parsing certificate.", e); + } catch (IOException | CertificateException e) { + LOG.error("Error occured while parsing certificate.", e); } return null; + } } diff --git a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/exception/UIException.java b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/exception/UIException.java new file mode 100644 index 0000000000000000000000000000000000000000..0cb934e4b35b728fa4831d65c00fba6a37933f41 --- /dev/null +++ b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/exception/UIException.java @@ -0,0 +1,28 @@ +package eu.europa.ec.edelivery.smp.ui.exception; + +import org.springframework.http.HttpStatus; + +import java.util.Arrays; +import java.util.List; + +public class UIException { + + + private HttpStatus status; + private String message; + private List<String> errors; + + public UIException(HttpStatus status, String message, List<String> errors) { + super(); + this.status = status; + this.message = message; + this.errors = errors; + } + + public UIException(HttpStatus status, String message, String error) { + super(); + this.status = status; + this.message = message; + errors = Arrays.asList(error); + } + } diff --git a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/validation/ServiceMetadataValidator.java b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/validation/ServiceMetadataValidator.java index 219fe59222c6bf49daa338f1edfe543fd919d862..202a26d9cc8d8b48c0bc5d5b06171fe35840f516 100644 --- a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/validation/ServiceMetadataValidator.java +++ b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/validation/ServiceMetadataValidator.java @@ -74,6 +74,34 @@ public class ServiceMetadataValidator { validateServiceInformation(serviceInformation); } + public ServiceInformationType validate(String serviceGroupIdStr, + byte[] serviceMetadataBody + ) throws XmlInvalidAgainstSchemaException { + + BdxSmpOasisValidator.validateXSD(serviceMetadataBody); + + ParticipantIdentifierType serviceGroupId = asParticipantId(serviceGroupIdStr); + ServiceMetadata serviceMetadata = ServiceMetadataConverter.unmarshal(serviceMetadataBody); + ServiceInformationType serviceInformation = serviceMetadata.getServiceInformation(); + + if (serviceInformation == null) { + String errorMessage = "Missing service information"; + throw new BadRequestException(WRONG_FIELD, errorMessage); + } + + + + if (!serviceGroupId.equals(serviceInformation.getParticipantIdentifier())) { + String errorMessage = String.format("Save service metadata was called with bad Participant ID parameters. Message body param: %s URL param: %s", + asString(serviceInformation.getParticipantIdentifier()), + asString(serviceGroupId)); + log.info(errorMessage); + throw new BadRequestException(WRONG_FIELD, errorMessage); + } + validateServiceInformation(serviceInformation); + return serviceInformation; + } + private void validateServiceInformation(ServiceInformationType serviceInformation) { ProcessListType processList = serviceInformation.getProcessList(); if (processList == null) { diff --git a/smp-webapp/src/main/resources/config.properties b/smp-webapp/src/main/resources/config.properties index d0ec016820b45a426440c956af6af0bb4a0111aa..544058456dcdf859fb13d5cb2f749901007d1c0a 100644 --- a/smp-webapp/src/main/resources/config.properties +++ b/smp-webapp/src/main/resources/config.properties @@ -16,6 +16,10 @@ # Authentication with Blue Coat means that all HTTP requests having 'Client-Cert' header will be authenticated # as username placed in the header. # Never expose SMP to the WEB without properly configured reverse-proxy and active blue coat. +Artifact-Name= eDelivery SMP +Artifact-Version= 4.1.0-SNAPSHOT +Build-Time= 22/10/2018 + authentication.blueCoat.enabled=false ## Only set to false in PRODUCTION mode. This variable is used to clear the context path of the SMP diff --git a/smp-webapp/src/main/resources/index.html b/smp-webapp/src/main/resources/index.html new file mode 100644 index 0000000000000000000000000000000000000000..3f5273e657c5ff00483b0f158493bb83f6ee9a3f --- /dev/null +++ b/smp-webapp/src/main/resources/index.html @@ -0,0 +1,32 @@ +<!-- + ~ Copyright 2017 European Commission | CEF eDelivery + ~ + ~ 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 attached in file: LICENCE-EUPL-v1.2.pdf + ~ + ~ 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. + --> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> + <head> + <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> + <meta http-equiv="X-UA-Compatible" content="IE=edge" /> + <title>SMP</title> + <link rel="shortcut icon" href="favicon-16x16.png" /> + <style type="text/css"> + body { font-family: Arial, Helvetica, sans-serif; } + a, a:link, a:visited, a:hover, a:active { color: blue; } + </style> + </head> + <body> + <h1>SMP (Service Metadata Publishing)</h1> + <h2>Version: ${project.version}</h2> + <h6>Build timestamp: ${buildtimestamp}</h6> + <h6>Specification: <a href="http://docs.oasis-open.org/bdxr/bdx-smp/v1.0/bdx-smp-v1.0.html" target="_blank">http://docs.oasis-open.org/bdxr/bdx-smp/v1.0/bdx-smp-v1.0.html</a></h6> + <h6>UI: <a href="ui/index.html" target="_blank">eDelivery SMP</a></h6> + </body> +</html> diff --git a/smp-webapp/src/main/resources/spring-security.xml b/smp-webapp/src/main/resources/spring-security.xml index 9060d3bf630a58bae71181c42060de6d8d95d967..95acbfb125f5e8a79de047d2e4ff199046a89183 100644 --- a/smp-webapp/src/main/resources/spring-security.xml +++ b/smp-webapp/src/main/resources/spring-security.xml @@ -20,7 +20,7 @@ <b:bean id="securityExceptionHandler" class="eu.europa.ec.edelivery.smp.error.SpringSecurityExceptionHandler"/> - <http > + <http create-session="always"> <csrf disabled="true"/> <http-basic entry-point-ref="securityExceptionHandler"/> <anonymous granted-authority="ROLE_ANONYMOUS"/> @@ -30,6 +30,7 @@ <intercept-url method="PUT" access=" ! isAnonymous()" pattern="/*"/> <intercept-url method="DELETE" access=" ! isAnonymous()" pattern="/*"/> + <access-denied-handler ref="securityExceptionHandler"/> <headers defaults-disabled="true" /> </http> diff --git a/smp-webapp/src/main/smp-setup/database-scripts/mysql5innodb-4.1.0-SNAPSHOT.ddl b/smp-webapp/src/main/smp-setup/database-scripts/mysql5innodb-4.1.0-SNAPSHOT.ddl index 00c0e53d00e4ffedee211fbeb506fe0750ba2f17..4a4cb9e40467fbc89051d86b15c3552735e8082e 100644 --- a/smp-webapp/src/main/smp-setup/database-scripts/mysql5innodb-4.1.0-SNAPSHOT.ddl +++ b/smp-webapp/src/main/smp-setup/database-scripts/mysql5innodb-4.1.0-SNAPSHOT.ddl @@ -37,7 +37,7 @@ SML_CLIENT_KEY_ALIAS varchar(256) CHARACTER SET utf8 COLLATE utf8_bin, SML_PARTC_IDENT_REGEXP varchar(4000) CHARACTER SET utf8 COLLATE utf8_bin, SML_SMP_ID varchar(256) CHARACTER SET utf8 COLLATE utf8_bin, - SML_SUBDOMAIN varchar(256) CHARACTER SET utf8 COLLATE utf8_bin not null, + SML_SUBDOMAIN varchar(256) CHARACTER SET utf8 COLLATE utf8_bin, primary key (ID) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; @@ -97,7 +97,6 @@ LAST_UPDATED_ON datetime not null, PARTICIPANT_IDENTIFIER varchar(256) CHARACTER SET utf8 COLLATE utf8_bin not null, PARTICIPANT_SCHEME varchar(256) CHARACTER SET utf8 COLLATE utf8_bin not null, - SML_REGISTRED bit not null, primary key (ID) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; @@ -109,7 +108,6 @@ LAST_UPDATED_ON datetime, PARTICIPANT_IDENTIFIER varchar(256) CHARACTER SET utf8 COLLATE utf8_bin, PARTICIPANT_SCHEME varchar(256) CHARACTER SET utf8 COLLATE utf8_bin, - SML_REGISTRED bit, primary key (ID, REV) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; @@ -117,6 +115,7 @@ ID bigint not null, CREATED_ON datetime not null, LAST_UPDATED_ON datetime not null, + SML_REGISTRED bit not null, FK_DOMAIN_ID bigint, FK_SG_ID bigint, primary key (ID) @@ -128,6 +127,7 @@ REVTYPE tinyint, CREATED_ON datetime, LAST_UPDATED_ON datetime, + SML_REGISTRED bit, FK_DOMAIN_ID bigint, FK_SG_ID bigint, primary key (ID, REV) diff --git a/smp-webapp/src/main/smp-setup/database-scripts/oracle10g-4.1.0-SNAPSHOT.ddl b/smp-webapp/src/main/smp-setup/database-scripts/oracle10g-4.1.0-SNAPSHOT.ddl index 2cf7e6aac5b3817d92bbd49cfc784580e4f5aad9..81a43ce87078d5a6d272341dcff22e07836c2ce1 100644 --- a/smp-webapp/src/main/smp-setup/database-scripts/oracle10g-4.1.0-SNAPSHOT.ddl +++ b/smp-webapp/src/main/smp-setup/database-scripts/oracle10g-4.1.0-SNAPSHOT.ddl @@ -43,7 +43,7 @@ create sequence SMP_USER_SEQ start with 1 increment by 50; SML_CLIENT_KEY_ALIAS varchar2(256 char), SML_PARTC_IDENT_REGEXP varchar2(4000 char), SML_SMP_ID varchar2(256 char), - SML_SUBDOMAIN varchar2(256 char) not null, + SML_SUBDOMAIN varchar2(256 char), primary key (ID) ); @@ -91,7 +91,6 @@ create sequence SMP_USER_SEQ start with 1 increment by 50; LAST_UPDATED_ON timestamp not null, PARTICIPANT_IDENTIFIER varchar2(256 char) not null, PARTICIPANT_SCHEME varchar2(256 char) not null, - SML_REGISTRED number(1,0) not null, primary key (ID) ); @@ -103,7 +102,6 @@ create sequence SMP_USER_SEQ start with 1 increment by 50; LAST_UPDATED_ON timestamp, PARTICIPANT_IDENTIFIER varchar2(256 char), PARTICIPANT_SCHEME varchar2(256 char), - SML_REGISTRED number(1,0), primary key (ID, REV) ); @@ -111,6 +109,7 @@ create sequence SMP_USER_SEQ start with 1 increment by 50; ID number(19,0) not null, CREATED_ON timestamp not null, LAST_UPDATED_ON timestamp not null, + SML_REGISTRED number(1,0) not null, FK_DOMAIN_ID number(19,0), FK_SG_ID number(19,0), primary key (ID) @@ -122,6 +121,7 @@ create sequence SMP_USER_SEQ start with 1 increment by 50; REVTYPE number(3,0), CREATED_ON timestamp, LAST_UPDATED_ON timestamp, + SML_REGISTRED number(1,0), FK_DOMAIN_ID number(19,0), FK_SG_ID number(19,0), primary key (ID, REV) diff --git a/smp-webapp/src/main/webapp/index.html b/smp-webapp/src/main/webapp/index.html new file mode 100644 index 0000000000000000000000000000000000000000..c870461f34bb9403b114bc7d70049ebddff11df0 --- /dev/null +++ b/smp-webapp/src/main/webapp/index.html @@ -0,0 +1,31 @@ +<!-- + ~ Copyright 2017 European Commission | CEF eDelivery + ~ + ~ 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 attached in file: LICENCE-EUPL-v1.2.pdf + ~ + ~ 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. + --> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> + <head> + <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> + <meta http-equiv="X-UA-Compatible" content="IE=edge" /> + <title>SMP</title> + <link rel="shortcut icon" href="favicon-16x16.png" /> + <style type="text/css"> + body { font-family: Arial, Helvetica, sans-serif; } + a, a:link, a:visited, a:hover, a:active { color: blue; } + </style> + </head> + <body> + <h1>SMP (Service Metadata Publishing)</h1> + <h2>Version: ${project.version}</h2> + <h6>Build timestamp: ${buildtimestamp}</h6> + <h6>Specification: <a href="http://docs.oasis-open.org/bdxr/bdx-smp/v1.0/bdx-smp-v1.0.html" target="_blank">http://docs.oasis-open.org/bdxr/bdx-smp/v1.0/bdx-smp-v1.0.html</a></h6> + </body> +</html> diff --git a/smp-webapp/src/main/webapp/static_resources/index.html b/smp-webapp/src/main/webapp/static_resources/index.html index c870461f34bb9403b114bc7d70049ebddff11df0..3f5273e657c5ff00483b0f158493bb83f6ee9a3f 100644 --- a/smp-webapp/src/main/webapp/static_resources/index.html +++ b/smp-webapp/src/main/webapp/static_resources/index.html @@ -27,5 +27,6 @@ <h2>Version: ${project.version}</h2> <h6>Build timestamp: ${buildtimestamp}</h6> <h6>Specification: <a href="http://docs.oasis-open.org/bdxr/bdx-smp/v1.0/bdx-smp-v1.0.html" target="_blank">http://docs.oasis-open.org/bdxr/bdx-smp/v1.0/bdx-smp-v1.0.html</a></h6> + <h6>UI: <a href="ui/index.html" target="_blank">eDelivery SMP</a></h6> </body> </html> diff --git a/smp-webapp/src/test/java/eu/europa/ec/cipa/smp/server/security/SecurityConfigurationTest.java b/smp-webapp/src/test/java/eu/europa/ec/cipa/smp/server/security/SecurityConfigurationTest.java index 0422feb830e2574da637eea17baeb440a9c07627..9de3335b6250c8d076f4fdad195f152af4d215ec 100644 --- a/smp-webapp/src/test/java/eu/europa/ec/cipa/smp/server/security/SecurityConfigurationTest.java +++ b/smp-webapp/src/test/java/eu/europa/ec/cipa/smp/server/security/SecurityConfigurationTest.java @@ -97,19 +97,19 @@ public class SecurityConfigurationTest { .andExpect(status().isOk()) .andExpect(content().string("anonymousUser")); } - - @Test - public void notAuthenticatedUserCannotCallPutTest() throws Exception { - mvc.perform(MockMvcRequestBuilders.put(RETURN_LOGGED_USER_PATH)) - .andExpect(status().isUnauthorized()); - } - - @Test - public void notAuthenticatedUserCannotCallDeleteTest() throws Exception { - mvc.perform(MockMvcRequestBuilders.delete(RETURN_LOGGED_USER_PATH)) - .andExpect(status().isUnauthorized()); - } - + /* + @Test + public void notAuthenticatedUserCannotCallPutTest() throws Exception { + mvc.perform(MockMvcRequestBuilders.put(RETURN_LOGGED_USER_PATH)) + .andExpect(status().isUnauthozed()); + } + /* + @Test + public void notAuthenticatedUserCannotCallDeleteTest() throws Exception { + mvc.perform(MockMvcRequestBuilders.delete(RETURN_LOGGED_USER_PATH)) + .andExpect(status().isUnauthorized()); + } + */ @Test public void userStoredWithHashedPassIsAuthorizedForPutTest() throws Exception { mvc.perform(MockMvcRequestBuilders.put(RETURN_LOGGED_USER_PATH) @@ -125,6 +125,7 @@ public class SecurityConfigurationTest { .andExpect(status().isUnauthorized()); } + /* @Test public void malformedBlueCoatHeaderNotAuthorizedTest() throws Exception { HttpHeaders headers = new HttpHeaders(); @@ -133,7 +134,7 @@ public class SecurityConfigurationTest { .headers(headers)) .andExpect(status().isUnauthorized()); } - +*/ @Test public void validBlueCoatHeaderAuthorizedForPutTest() throws Exception { HttpHeaders headers = new HttpHeaders(); diff --git a/smp-webapp/src/test/java/eu/europa/ec/cipa/smp/server/security/SignatureValidatorTest.java b/smp-webapp/src/test/java/eu/europa/ec/cipa/smp/server/security/SignatureValidatorTest.java index 8b845b20c003ab39063d9d4174715f6d6e2fc4cc..14424f7ad2cd6ef7d2c0b29271a70a0feeadc95f 100644 --- a/smp-webapp/src/test/java/eu/europa/ec/cipa/smp/server/security/SignatureValidatorTest.java +++ b/smp-webapp/src/test/java/eu/europa/ec/cipa/smp/server/security/SignatureValidatorTest.java @@ -77,7 +77,7 @@ public class SignatureValidatorTest { private static final String C14N_METHOD = CanonicalizationMethod.INCLUSIVE; private static final String PARSER_DISALLOW_DTD_PARSING_FEATURE = "http://apache.org/xml/features/disallow-doctype-decl"; - private static final RequestPostProcessor ADMIN_CREDENTIALS = httpBasic("test_admin", "test123"); + private static final RequestPostProcessor ADMIN_CREDENTIALS = httpBasic("smp_admin", "test123"); @Autowired private WebApplicationContext webAppContext; diff --git a/smp-webapp/src/test/java/eu/europa/ec/edelivery/smp/controllers/ServiceGroupControllerTest.java b/smp-webapp/src/test/java/eu/europa/ec/edelivery/smp/controllers/ServiceGroupControllerTest.java index 186638d9a338be4c22a2229d10404074e2799631..0bd193813b481a5fc6297a28a2c14e0257e575f9 100644 --- a/smp-webapp/src/test/java/eu/europa/ec/edelivery/smp/controllers/ServiceGroupControllerTest.java +++ b/smp-webapp/src/test/java/eu/europa/ec/edelivery/smp/controllers/ServiceGroupControllerTest.java @@ -71,7 +71,7 @@ public class ServiceGroupControllerTest { private static final String OTHER_OWNER_NAME_URL_ENCODED = "CN=utf-8_%C5%BC_SMP,O=EC,C=BE:0000000000000666"; - private static final RequestPostProcessor ADMIN_CREDENTIALS = httpBasic("test_admin", "test123"); + private static final RequestPostProcessor ADMIN_CREDENTIALS = httpBasic("smp_admin", "test123"); @Autowired private WebApplicationContext webAppContext; diff --git a/smp-webapp/src/test/java/eu/europa/ec/edelivery/smp/monitor/MonitorResourceTest.java b/smp-webapp/src/test/java/eu/europa/ec/edelivery/smp/monitor/MonitorResourceTest.java index b680d69b80fd6042b848a08cbac6c02312793556..86bd74d90fb358d10629bfa086a796c48e893f2e 100644 --- a/smp-webapp/src/test/java/eu/europa/ec/edelivery/smp/monitor/MonitorResourceTest.java +++ b/smp-webapp/src/test/java/eu/europa/ec/edelivery/smp/monitor/MonitorResourceTest.java @@ -4,6 +4,7 @@ import eu.europa.ec.edelivery.smp.config.PropertiesTestConfig; import eu.europa.ec.edelivery.smp.config.SmpAppConfig; import eu.europa.ec.edelivery.smp.config.SmpWebAppConfig; import eu.europa.ec.edelivery.smp.config.SpringSecurityConfig; +import eu.europa.ec.edelivery.smp.exceptions.SMPTestIsALiveException; import eu.europa.ec.edelivery.smp.monitor.MonitorResource; import org.junit.Before; import org.junit.Rule; @@ -52,7 +53,7 @@ public class MonitorResourceTest { public ExpectedException expectedEx = ExpectedException.none(); private static final String URL = "/monitor/is-alive"; - private static final RequestPostProcessor ADMIN_CREDENTIALS = httpBasic("test_admin", "test123"); + private static final RequestPostProcessor ADMIN_CREDENTIALS = httpBasic("smp_admin", "test123"); @Autowired private WebApplicationContext webAppContext; @@ -95,7 +96,7 @@ public class MonitorResourceTest { public void testDatabase() { // given expectedEx.expectMessage("TEST_DB_SUCCESSFUL_ROLLBACK MESSAGE"); - expectedEx.expect(RuntimeException.class); + expectedEx.expect(SMPTestIsALiveException.class); // when boolean bval = testInstance.testDatabase(); //then diff --git a/smp-webapp/src/test/java/eu/europa/ec/edelivery/smp/ui/ServiceGroupResourceTest.java b/smp-webapp/src/test/java/eu/europa/ec/edelivery/smp/ui/ServiceGroupResourceTest.java index ab02e47f6b1f5099943d97349287eb41ab874ddc..ac1a1185e3039846008c470e69d6eda58376204d 100644 --- a/smp-webapp/src/test/java/eu/europa/ec/edelivery/smp/ui/ServiceGroupResourceTest.java +++ b/smp-webapp/src/test/java/eu/europa/ec/edelivery/smp/ui/ServiceGroupResourceTest.java @@ -20,6 +20,7 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.context.web.WebAppConfiguration; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.MvcResult; +import org.springframework.test.web.servlet.request.RequestPostProcessor; import org.springframework.test.web.servlet.setup.MockMvcBuilders; import org.springframework.web.context.ContextLoaderListener; import org.springframework.web.context.WebApplicationContext; @@ -27,11 +28,11 @@ import org.springframework.web.context.WebApplicationContext; import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; -import static org.junit.Assert.*; -import static org.springframework.http.MediaType.APPLICATION_JSON; -import static org.springframework.http.MediaType.APPLICATION_XML_VALUE; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertNotNull; +import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.httpBasic; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; /** @@ -59,7 +60,8 @@ public class ServiceGroupResourceTest { private WebApplicationContext webAppContext; private MockMvc mvc; - + private static final RequestPostProcessor SMP_ADMIN_CREDENTIALS = httpBasic("smp_admin", "test123"); + private static final RequestPostProcessor SG_ADMIN_CREDENTIALS = httpBasic("sg_admin", "test123"); @Before public void setup() { mvc = MockMvcBuilders.webAppContextSetup(webAppContext) @@ -76,10 +78,11 @@ public class ServiceGroupResourceTest { } @Test - public void getServiceGroupList() throws Exception { + public void getServiceGroupListForSMPAdmin() throws Exception { // given when - MvcResult result = mvc.perform(get(PATH)). - andExpect(status().isOk()).andReturn(); + MvcResult result = mvc.perform(get(PATH) + .with(SMP_ADMIN_CREDENTIALS) + ).andExpect(status().isOk()).andReturn(); //them ObjectMapper mapper = new ObjectMapper(); @@ -94,38 +97,41 @@ public class ServiceGroupResourceTest { assertNotNull(sgro.getParticipantScheme()); assertNotNull(sgro.getParticipantIdentifier()); assertEquals(1, sgro.getUsers().size()); - assertEquals("test_user_hashed_pass", sgro.getUsers().get(0).getUsername()); + assertNotEquals("smp_admin", sgro.getUsers().get(0).getUsername()); + }); } @Test - public void getServiceGroupById() throws Exception{ - + public void getServiceGroupListForServiceGroupAdmin() throws Exception { // given when - MvcResult result = mvc.perform(get(PATH+"/100000")). - andExpect(status().isOk()).andReturn(); + MvcResult result = mvc.perform(get(PATH) + .with(SG_ADMIN_CREDENTIALS) + ).andExpect(status().isOk()).andReturn(); //them ObjectMapper mapper = new ObjectMapper(); - ServiceGroupRO res = mapper.readValue(result.getResponse().getContentAsString(), ServiceGroupRO.class); + ServiceResult res = mapper.readValue(result.getResponse().getContentAsString(), ServiceResult.class); - assertNotNull(res); - assertEquals(100000, res.getId().intValue()); - assertEquals(PARTICIPANT_IDENTIFIER, res.getParticipantIdentifier()); - assertEquals(PARTICIPANT_SCHEME, res.getParticipantScheme()); - assertEquals(1, res.getUsers().size()); - assertEquals("test_user_hashed_pass", res.getUsers().get(0).getUsername()); - assertEquals(1, res.getServiceMetadata().size()); - assertEquals("doc_7", res.getServiceMetadata().get(0).getDocumentIdentifier()); + assertNotNull(res); + assertEquals(1, res.getServiceEntities().size()); + res.getServiceEntities().forEach(sgMap-> { + ServiceGroupRO sgro = mapper.convertValue(sgMap, ServiceGroupRO.class); + assertNotNull(sgro.getId()); + assertNotNull(sgro.getParticipantScheme()); + assertNotNull(sgro.getParticipantIdentifier()); + assertEquals(1, sgro.getUsers().size()); + assertEquals("sg_admin", sgro.getUsers().get(0).getUsername()); + }); } - @Test - public void updateServiceGroupList() throws Exception{ -/* + public void getServiceGroupById() throws Exception{ + // given when - MvcResult result = mvc.perform(put(PATH)). + MvcResult result = mvc.perform(get(PATH+"/100000") + .with(SMP_ADMIN_CREDENTIALS)). andExpect(status().isOk()).andReturn(); //them @@ -139,7 +145,11 @@ public class ServiceGroupResourceTest { assertEquals(1, res.getUsers().size()); assertEquals("test_user_hashed_pass", res.getUsers().get(0).getUsername()); + assertEquals(1, res.getServiceGroupDomains().size()); assertEquals(1, res.getServiceMetadata().size()); - assertEquals("doc_7", res.getServiceMetadata().get(0).getDocumentIdentifier());*/ + assertEquals("doc_7", res.getServiceMetadata().get(0).getDocumentIdentifier()); + assertEquals(res.getServiceGroupDomains().get(0).getId(), res.getServiceMetadata().get(0).getServiceGroupDomainId()); } + + } \ No newline at end of file diff --git a/smp-webapp/src/test/java/eu/europa/ec/edelivery/smp/ui/UserResourceTest.java b/smp-webapp/src/test/java/eu/europa/ec/edelivery/smp/ui/UserResourceTest.java index 6b41f4d018e725e198728d098492a5aeacfcbf3a..88faf6bb0e9a36d0372f975adf78e572aa203ef4 100644 --- a/smp-webapp/src/test/java/eu/europa/ec/edelivery/smp/ui/UserResourceTest.java +++ b/smp-webapp/src/test/java/eu/europa/ec/edelivery/smp/ui/UserResourceTest.java @@ -21,6 +21,7 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.context.web.WebAppConfiguration; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.MvcResult; +import org.springframework.test.web.servlet.request.RequestPostProcessor; import org.springframework.test.web.servlet.setup.MockMvcBuilders; import org.springframework.web.context.ContextLoaderListener; import org.springframework.web.context.WebApplicationContext; @@ -30,6 +31,7 @@ import javax.servlet.ServletContextListener; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; +import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.httpBasic; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @@ -55,7 +57,7 @@ public class UserResourceTest { private WebApplicationContext webAppContext; private MockMvc mvc; - + private static final RequestPostProcessor ADMIN_CREDENTIALS = httpBasic("smp_admin", "test123"); @Before public void setup() { mvc = MockMvcBuilders.webAppContextSetup(webAppContext) @@ -74,7 +76,7 @@ public class UserResourceTest { @Test public void getUserList() throws Exception { // given when - MvcResult result = mvc.perform(get(PATH)). + MvcResult result = mvc.perform(get(PATH).with(ADMIN_CREDENTIALS)). andExpect(status().isOk()).andReturn(); //them @@ -83,7 +85,7 @@ public class UserResourceTest { assertNotNull(res); - assertEquals(6, res.getServiceEntities().size()); + assertEquals(9, res.getServiceEntities().size()); res.getServiceEntities().forEach(sgMap-> { UserRO sgro = mapper.convertValue(sgMap, UserRO.class); assertNotNull(sgro.getId()); diff --git a/smp-webapp/src/test/resources/config.properties b/smp-webapp/src/test/resources/config.properties index 99ea37d0785ac3be6181fb115c4a454423d679fd..b556058c221aaf2bbb1be6dae7f3a4860cf0acd1 100644 --- a/smp-webapp/src/test/resources/config.properties +++ b/smp-webapp/src/test/resources/config.properties @@ -12,6 +12,10 @@ # ## Blue Coat reverse-proxy authentication +Artifact-Name= eDelivery SMP +Artifact-Version= 4.1.0-SNAPSHOT +Build-Time= 22/10/2018 + authentication.blueCoat.enabled=true encodedSlashesAllowedInUrl=false diff --git a/smp-webapp/src/test/resources/webapp_integration_test_data.sql b/smp-webapp/src/test/resources/webapp_integration_test_data.sql index 0c02bb89da6094a8c67a5385d6b4b75f66aeec71..7cc5fb4d22bc05b8a7cc7408d2c2e1a36fbe3577 100644 --- a/smp-webapp/src/test/resources/webapp_integration_test_data.sql +++ b/smp-webapp/src/test/resources/webapp_integration_test_data.sql @@ -8,31 +8,38 @@ -- 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. -insert into SMP_USER (ID, USERNAME, PASSWORD, ROLE, ACTIVE, CREATED_ON, LAST_UPDATED_ON) values (1, 'test_admin', '$2a$06$AXSSUDJlpzzq/gPZb7eIBeb8Mi0.PTKqDjzujZH.bWPwj5.ePEInW', 'SMP_ADMIN', 1,CURRENT_TIMESTAMP(),CURRENT_TIMESTAMP()); -insert into SMP_USER(ID, USERNAME, PASSWORD, ROLE, ACTIVE, CREATED_ON, LAST_UPDATED_ON) values (2, 'test_user_hashed_pass', '$2a$06$AXSSUDJlpzzq/gPZb7eIBeb8Mi0.PTKqDjzujZH.bWPwj5.ePEInW', 'SMP_ADMIN',1,CURRENT_TIMESTAMP(),CURRENT_TIMESTAMP()); -insert into SMP_USER(ID, USERNAME, PASSWORD, ROLE, ACTIVE, CREATED_ON, LAST_UPDATED_ON) values (3, 'test_user_clear_pass', 'test123', 'SMP_ADMIN',1,CURRENT_TIMESTAMP(),CURRENT_TIMESTAMP()); -insert into SMP_USER(ID, USERNAME, PASSWORD, ROLE, ACTIVE, CREATED_ON, LAST_UPDATED_ON) values (4, 'cert1', '', 'SMP_ADMIN', 1,CURRENT_TIMESTAMP(),CURRENT_TIMESTAMP()); -insert into SMP_CERTIFICATE (ID, CERTIFICATE_ID, VALID_FROM, VALID_TO, CREATED_ON, LAST_UPDATED_ON) values (4, 'CN=comon name,O=org,C=BE:0000000000000066', null,null,CURRENT_TIMESTAMP(),CURRENT_TIMESTAMP()); +insert into SMP_USER (ID, USERNAME, PASSWORD, ROLE, ACTIVE, CREATED_ON, LAST_UPDATED_ON) values (1, 'smp_admin', '$2a$06$AXSSUDJlpzzq/gPZb7eIBeb8Mi0.PTKqDjzujZH.bWPwj5.ePEInW', 'SMP_ADMIN', 1,CURRENT_TIMESTAMP(),CURRENT_TIMESTAMP()); +insert into SMP_USER (ID, USERNAME, PASSWORD, ROLE, ACTIVE, CREATED_ON, LAST_UPDATED_ON) values (2, 'sg_admin', '$2a$06$AXSSUDJlpzzq/gPZb7eIBeb8Mi0.PTKqDjzujZH.bWPwj5.ePEInW', 'SERVICE_GROUP_ADMIN', 1,CURRENT_TIMESTAMP(),CURRENT_TIMESTAMP()); +insert into SMP_USER (ID, USERNAME, PASSWORD, ROLE, ACTIVE, CREATED_ON, LAST_UPDATED_ON) values (3, 'sys_admin', '$2a$06$AXSSUDJlpzzq/gPZb7eIBeb8Mi0.PTKqDjzujZH.bWPwj5.ePEInW', 'SMP_ADMIN', 1,CURRENT_TIMESTAMP(),CURRENT_TIMESTAMP()); -insert into SMP_USER(ID, USERNAME, ROLE, ACTIVE, CREATED_ON, LAST_UPDATED_ON) values (5, 'cert2', 'SMP_ADMIN', 1,CURRENT_TIMESTAMP(),CURRENT_TIMESTAMP()); -insert into SMP_CERTIFICATE (ID, CERTIFICATE_ID, VALID_FROM, VALID_TO, CREATED_ON, LAST_UPDATED_ON) values (5, 'CN=EHEALTH_SMP_TEST_BRAZIL,O=European Commission,C=BE:48b681ee8e0dcc08', null,null,CURRENT_TIMESTAMP(),CURRENT_TIMESTAMP()); +insert into SMP_USER(ID, USERNAME, PASSWORD, ROLE, ACTIVE, CREATED_ON, LAST_UPDATED_ON) values (4, 'test_user_hashed_pass', '$2a$06$AXSSUDJlpzzq/gPZb7eIBeb8Mi0.PTKqDjzujZH.bWPwj5.ePEInW', 'SERVICE_GROUP_ADMIN',1,CURRENT_TIMESTAMP(),CURRENT_TIMESTAMP()); +insert into SMP_USER(ID, USERNAME, PASSWORD, ROLE, ACTIVE, CREATED_ON, LAST_UPDATED_ON) values (5, 'test_user_clear_pass', 'test123', 'SERVICE_GROUP_ADMIN',1,CURRENT_TIMESTAMP(),CURRENT_TIMESTAMP()); +insert into SMP_USER(ID, USERNAME, PASSWORD, ROLE, ACTIVE, CREATED_ON, LAST_UPDATED_ON) values (6, 'cert1', '', 'SMP_ADMIN', 1,CURRENT_TIMESTAMP(),CURRENT_TIMESTAMP()); +insert into SMP_CERTIFICATE (ID, CERTIFICATE_ID, VALID_FROM, VALID_TO, CREATED_ON, LAST_UPDATED_ON) values (6, 'CN=comon name,O=org,C=BE:0000000000000066', null,null,CURRENT_TIMESTAMP(),CURRENT_TIMESTAMP()); + +insert into SMP_USER(ID, USERNAME, ROLE, ACTIVE, CREATED_ON, LAST_UPDATED_ON) values (7, 'cert2', 'SMP_ADMIN', 1,CURRENT_TIMESTAMP(),CURRENT_TIMESTAMP()); +insert into SMP_CERTIFICATE (ID, CERTIFICATE_ID, VALID_FROM, VALID_TO, CREATED_ON, LAST_UPDATED_ON) values (7, 'CN=EHEALTH_SMP_TEST_BRAZIL,O=European Commission,C=BE:48b681ee8e0dcc08', null,null,CURRENT_TIMESTAMP(),CURRENT_TIMESTAMP()); + +insert into SMP_USER(ID, USERNAME, ROLE, ACTIVE, CREATED_ON, LAST_UPDATED_ON) values (8, 'Cert3', 'SMP_ADMIN', 1,CURRENT_TIMESTAMP(),CURRENT_TIMESTAMP()); +insert into SMP_CERTIFICATE (ID, CERTIFICATE_ID, VALID_FROM, VALID_TO, CREATED_ON, LAST_UPDATED_ON) values (8, 'CN=utf-8_ż_SMP,O=EC,C=BE:0000000000000666', null,null,CURRENT_TIMESTAMP(),CURRENT_TIMESTAMP()); + +insert into SMP_USER(ID, USERNAME, ROLE, ACTIVE, CREATED_ON, LAST_UPDATED_ON) values (9, 'Cert4', 'SMP_ADMIN', 1,CURRENT_TIMESTAMP(),CURRENT_TIMESTAMP()); +insert into SMP_CERTIFICATE (ID, CERTIFICATE_ID, VALID_FROM, VALID_TO, CREATED_ON, LAST_UPDATED_ON) values (9, 'CN=EHEALTH_SMP_EC,O=European Commission,C=BEf71ee8b11cb3b787', null,null,CURRENT_TIMESTAMP(),CURRENT_TIMESTAMP()); -insert into SMP_USER(ID, USERNAME, ROLE, ACTIVE, CREATED_ON, LAST_UPDATED_ON) values (6, 'Cert2', 'SMP_ADMIN', 1,CURRENT_TIMESTAMP(),CURRENT_TIMESTAMP()); -insert into SMP_CERTIFICATE (ID, CERTIFICATE_ID, VALID_FROM, VALID_TO, CREATED_ON, LAST_UPDATED_ON) values (6, 'CN=utf-8_ż_SMP,O=EC,C=BE:0000000000000666', null,null,CURRENT_TIMESTAMP(),CURRENT_TIMESTAMP()); -- set the ids to higher values - tests are using sequnce which stars from 1 -insert into SMP_SERVICE_GROUP(ID, PARTICIPANT_IDENTIFIER, PARTICIPANT_SCHEME,SML_REGISTRED, CREATED_ON, LAST_UPDATED_ON) values (100000, 'urn:australia:ncpb', 'ehealth-actorid-qns', 1,CURRENT_TIMESTAMP(),CURRENT_TIMESTAMP()); -insert into SMP_SERVICE_GROUP(ID, PARTICIPANT_IDENTIFIER, PARTICIPANT_SCHEME,SML_REGISTRED, CREATED_ON, LAST_UPDATED_ON) values (200000, 'urn:brazil:ncpb', 'ehealth-actorid-qns', 1,CURRENT_TIMESTAMP(),CURRENT_TIMESTAMP()); +insert into SMP_SERVICE_GROUP(ID, PARTICIPANT_IDENTIFIER, PARTICIPANT_SCHEME, CREATED_ON, LAST_UPDATED_ON) values (100000, 'urn:australia:ncpb', 'ehealth-actorid-qns', CURRENT_TIMESTAMP(),CURRENT_TIMESTAMP()); +insert into SMP_SERVICE_GROUP(ID, PARTICIPANT_IDENTIFIER, PARTICIPANT_SCHEME, CREATED_ON, LAST_UPDATED_ON) values (200000, 'urn:brazil:ncpb', 'ehealth-actorid-qns', CURRENT_TIMESTAMP(),CURRENT_TIMESTAMP()); -- set ownership -insert into SMP_OWNERSHIP (FK_SG_ID, FK_USER_ID) values (100000, 2); +insert into SMP_OWNERSHIP (FK_SG_ID, FK_USER_ID) values (100000, 4); insert into SMP_OWNERSHIP (FK_SG_ID, FK_USER_ID) values (200000, 2); insert into SMP_DOMAIN (ID, DOMAIN_CODE, SML_SUBDOMAIN, SML_SMP_ID, SIGNATURE_KEY_ALIAS, CREATED_ON, LAST_UPDATED_ON) values (1, 'domain','subdomain', 'CEF-SMP-001','sig-key',CURRENT_TIMESTAMP(),CURRENT_TIMESTAMP()); -insert into SMP_SERVICE_GROUP_DOMAIN (ID, FK_SG_ID, FK_DOMAIN_ID, CREATED_ON, LAST_UPDATED_ON) values (1000,200000, 1,CURRENT_TIMESTAMP(),CURRENT_TIMESTAMP()); +insert into SMP_SERVICE_GROUP_DOMAIN (ID, FK_SG_ID, FK_DOMAIN_ID,SML_REGISTRED, CREATED_ON, LAST_UPDATED_ON) values (1000,200000, 1, 0, CURRENT_TIMESTAMP(),CURRENT_TIMESTAMP()); -insert into SMP_SERVICE_GROUP_DOMAIN (ID, FK_SG_ID, FK_DOMAIN_ID, CREATED_ON, LAST_UPDATED_ON) values (1001,100000, 1,CURRENT_TIMESTAMP(),CURRENT_TIMESTAMP()); +insert into SMP_SERVICE_GROUP_DOMAIN (ID, FK_SG_ID, FK_DOMAIN_ID,SML_REGISTRED, CREATED_ON, LAST_UPDATED_ON) values (1001,100000, 1, 0 ,CURRENT_TIMESTAMP(),CURRENT_TIMESTAMP()); insert into SMP_SERVICE_METADATA (ID, FK_SG_DOM_ID, DOCUMENT_IDENTIFIER, DOCUMENT_SCHEME, LAST_UPDATED_ON, CREATED_ON) values (1000,1001,'doc_7','busdox-docid-qns',CURRENT_TIMESTAMP(),CURRENT_TIMESTAMP());