diff --git a/smp-angular/package.json b/smp-angular/package.json index 49773211f46c980bab0c24e9359bc805cea7a350..b4350405bbed9d2bffad8aba087b95f139538fcb 100644 --- a/smp-angular/package.json +++ b/smp-angular/package.json @@ -29,7 +29,7 @@ "@angular/flex-layout": "^6.0.0-beta.16", "@swimlane/ngx-datatable": "^13.0.0", "core-js": "^2.5.7", - "file-saver": "^1.3.3", + "file-saver": "1.3.3", "hammerjs": "^2.0.8", "rxjs": "^6.3.3", "ts-helpers": "^1.1.2", diff --git a/smp-angular/src/app/app.module.ts b/smp-angular/src/app/app.module.ts index a748369f29479addde99bb2e98c2f7a98a8f7113..e6055012d85516d1f5badcc8268e381b506897b2 100644 --- a/smp-angular/src/app/app.module.ts +++ b/smp-angular/src/app/app.module.ts @@ -17,7 +17,10 @@ import { MatDatepicker, MatCardModule, MatDatepickerModule, - MatSlideToggleModule, MatTab, MatAccordion, MatTabsModule, + MatSlideToggleModule, + MatTab, + MatAccordion, + MatTabsModule, } from '@angular/material'; import "hammerjs"; @@ -154,6 +157,7 @@ import {GlobalLookups} from "./common/global-lookups"; CertificateService, RoleService, GlobalLookups, + DatePipe, { provide: ExtendedHttpClient, useFactory: extendedHttpClientCreator, 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..d8bc1fd3cf957e3926a0cd9735f36e8ef2e13d92 --- /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>Do you want to cancel all unsaved operations?</h1> + + <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..add7e0939740ef955497de7d8dfc0c7926cd8eaa --- /dev/null +++ b/smp-angular/src/app/common/confirmation-dialog/confirmation-dialog.component.ts @@ -0,0 +1,14 @@ +import {Component} from '@angular/core'; +import {MatDialogRef} from '@angular/material'; + +@Component({ + selector: 'app-cancel-dialog', + templateUrl: './cancel-dialog.component.html', + styleUrls: ['./cancel-dialog.component.css'] +}) +export class CancelDialogComponent { + + constructor(public dialogRef: MatDialogRef<CancelDialogComponent>) { + } + +} 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/search-table/search-table.component.ts b/smp-angular/src/app/common/search-table/search-table.component.ts index 61af407ce68d9ee3e1075bf8be588ae312f991d7..b91db7ca117a91b7fde882839a0d3f6006f2876f 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 @@ -189,7 +189,6 @@ export class SearchTableComponent implements OnInit { } changePageSize(newPageLimit: number) { - alert("new page size"); this.page(0, newPageLimit, this.orderBy, this.asc); } 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 d2204ad31f18e180f3281d3ba3bc588d775ec40b..9454eafb35b26b148900ea11608a5942e8a49e1d 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 @@ -4,7 +4,6 @@ import {UserRo} from "../../user/user-ro.model"; import {FormBuilder, FormControl, FormGroup, Validators} from "@angular/forms"; import {DomainRo} from "../domain-ro.model"; import {AlertService} from "../../alert/alert.service"; -import {RoleService} from "../../security/role.service"; import {UserDetailsDialogComponent} from "../../user/user-details-dialog/user-details-dialog.component"; import {CertificateService} from "../../user/certificate.service"; import {UserService} from "../../user/user.service"; diff --git a/smp-angular/src/app/login/login.component.html b/smp-angular/src/app/login/login.component.html index eff74f10eb879f8d1fa6afcbc7f96aa1d0857abf..ac70965df017fef51051b99669ac837358bfdf0b 100644 --- a/smp-angular/src/app/login/login.component.html +++ b/smp-angular/src/app/login/login.component.html @@ -5,7 +5,7 @@ <tr> <td> <mat-form-field> - <input matInput placeholder="Username" name="userName" [(ngModel)]="model.userName" #userName="ngModel" + <input matInput placeholder="Username" name="username" [(ngModel)]="model.username" #username="ngModel" required id="username_id"> </mat-form-field> </td> diff --git a/smp-angular/src/app/security/role.model.ts b/smp-angular/src/app/security/role.model.ts index ac0c7a3c8e1537c60d96ca477c558d5cf9a7fb42..a30436218279dc41882bea02a275284a6341290d 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_ADMINISTRATOR, + SYSTEM_ADMINISTRATOR = 'System Administrator', /** * The SMP Administrator role. It is assimilable to the {@link SERVICE_GROUP_ADMINISTRATOR} role for now. */ - SMP_ADMINISTRATOR, + SMP_ADMINISTRATOR = 'SMP Administrator', /** * The ServiceGroup administrator role */ - SERVICE_GROUP_ADMINISTRATOR, + SERVICE_GROUP_ADMINISTRATOR = 'ServiceGroup Administrator', } diff --git a/smp-angular/src/app/security/role.service.ts b/smp-angular/src/app/security/role.service.ts deleted file mode 100644 index 11f3fa251d3ce5ec2d40824e4207fce192162be9..0000000000000000000000000000000000000000 --- a/smp-angular/src/app/security/role.service.ts +++ /dev/null @@ -1,25 +0,0 @@ -import {Injectable} from '@angular/core'; -import {Role} from './role.model'; - -@Injectable() -export class RoleService { - - /** - * Returns a user representation of the role or an empty string if the role is unknown. - * - * @param role the role for which we need to display the user representation - */ - public getLabel(role: Role): string { - switch (role) { - case Role.SMP_ADMINISTRATOR: - return 'SMP Administrator'; - case Role.SERVICE_GROUP_ADMINISTRATOR: - return 'ServiceGroup Administrator'; - case Role.SYSTEM_ADMINISTRATOR: - return 'System Administrator'; - default: - return ''; - } - } - -} 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-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..048bff1aef232f9641d1ff8731a54aa22a801f36 --- /dev/null +++ b/smp-angular/src/app/service-group-edit/service-group-domain-edit-ro.model.ts @@ -0,0 +1,14 @@ +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 {DomainRo} from "../domain/domain-ro.model"; + +export interface ServiceGroupEditRo extends SearchTableEntity { + id: number; + participantIdentifier: string; + participantScheme: string; + serviceMetadata: Array<ServiceMetadataEditRo>; + serviceGroupDomains: Array<ServiceGroupDomainRo>; + users: Array<UserRo>; + extension?: string; +} 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..b2b3246f004cab7719363c84323f490b515f551b --- /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,74 @@ +import {ChangeDetectorRef, Component, Inject, OnInit} from '@angular/core'; +import {MAT_DIALOG_DATA, 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"; + +@Component({ + selector: 'app-messagelog-details', + templateUrl: './service-group-metadata-wizard-dialog/service-group-extension-wizard-dialog.component.html', + styleUrls: ['./service-group-metadata-wizard-dialog/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-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..831c4715b5d3bb8e486c9dc13e914bf7dddb8c0c --- /dev/null +++ b/smp-angular/src/app/service-group-edit/service-metadata-wizard-dialog/service-metadata-wizard-dialog.component.html @@ -0,0 +1,72 @@ +<h2 mat-dialog-title>Extension properties</h2> + +<mat-dialog-content> + <form [formGroup]="dialogForm"> + <mat-card> + <mat-card-content> + <fieldset style="border: none;"> + + + <mat-form-field style="width:100%"> + <input matInput placeholder="Document identifier schema" name="documentIdentifierScheme" id="documentIdentifierScheme_id" + [formControl]="dialogForm.controls['documentIdentifierScheme']" maxlength="255"> + + </mat-form-field> + + <mat-form-field style="width:100%"> + <input matInput placeholder="Document identifier" name="documentIdentifier" id="documentIdentifier_id" + [formControl]="dialogForm.controls['documentIdentifier']" 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> + + <mat-form-field style="width:100%"> + <input matInput placeholder="Process schema" name="processSchema" id="processSchema_id" + [formControl]="dialogForm.controls['processSchema']" maxlength="255" required> + + <div *ngIf="(!editMode && dialogForm.controls['processSchema'].touched || editMode) && dialogForm.controls['processSchema'].hasError('required')" style="color:red; font-size: 70%"> + Process identifier must not be empty. + </div> + </mat-form-field> + + <mat-form-field style="width:100%"> + <input matInput placeholder="Process identifier" name="processidentifier" id="processidentifier_id" + [formControl]="dialogForm.controls['processIdentifier']" maxlength="255" required> + + <div *ngIf="(!editMode && dialogForm.controls['processIdentifier'].touched || editMode) && dialogForm.controls['processIdentifier'].hasError('required')" style="color:red; font-size: 70%"> + Process identifier must not be empty. + </div> + </mat-form-field> + + <mat-form-field style="width:100%"> + <input matInput placeholder="Endpoint Url" name="endpointUrl" id="endpointUrl_id" + [formControl]="dialogForm.controls['endpointUrl']" maxlength="255" required> + + <div *ngIf="(!editMode && dialogForm.controls['endpointUrl'].touched || editMode) && dialogForm.controls['endpointUrl'].hasError('required')" style="color:red; font-size: 70%"> + Endpoint URL must not be empty. + </div> + </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-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..fafc2a070e6f7fbcdd7e8beed488bcd24a4c2bfe --- /dev/null +++ b/smp-angular/src/app/service-group-edit/service-metadata-wizard-dialog/service-metadata-wizard-dialog.component.ts @@ -0,0 +1,68 @@ +import {ChangeDetectorRef, Component, Inject, OnInit} from '@angular/core'; +import {MAT_DIALOG_DATA, 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"; + +@Component({ + selector: 'app-messagelog-details', + 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>" + + + constructor(public dialogRef: MatDialogRef<ServiceGroupExtensionWizardDialogComponent>, + 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), + + }); + } + + 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..9c2d25b7df2a6da788c61d96af66e472d89e6e0a --- /dev/null +++ b/smp-angular/src/app/service-group-edit/service-metadata-wizard-dialog/service-metadata-wizard-edit-ro.model.ts @@ -0,0 +1,6 @@ + +export interface ServiceGroupExtensionRo { + serviceGroupId: number; + extension: string; + errorMessage?: string; +} diff --git a/smp-angular/src/app/smp.constants.ts b/smp-angular/src/app/smp.constants.ts index e0b6974de586b85c21e4ba7805e8025d96ec2905..0e1b1715ca0d4587271fd89edb80f61456a642ab 100644 --- a/smp-angular/src/app/smp.constants.ts +++ b/smp-angular/src/app/smp.constants.ts @@ -6,4 +6,6 @@ export class SmpConstants { public static readonly REST_EDIT = 'rest/servicegroup'; public static readonly REST_METADATA = 'rest/servicemetadata'; + public static readonly REST_CERTIFICATE = `${SmpConstants.REST_USER}/certdata`; + } diff --git a/smp-angular/src/app/user/certificate-ro.model.ts b/smp-angular/src/app/user/certificate-ro.model.ts index bb66f1de22a6312cf70397fc8560850e1e6beded..14b2705c43bcec23b45a895d93bbded2b49d2156 100644 --- a/smp-angular/src/app/user/certificate-ro.model.ts +++ b/smp-angular/src/app/user/certificate-ro.model.ts @@ -2,7 +2,7 @@ export interface CertificateRo { certificateId: string; subject: string; validFrom: Date; - validUntil: Date; + validTo: Date; issuer: string; serialNumber: string; fingerprints: string; diff --git a/smp-angular/src/app/user/certificate.service.ts b/smp-angular/src/app/user/certificate.service.ts index d7af521f998cd9dac938c5ae1c1ae803ec511ec9..7c969c1800b73f8c0ae1ce43fce1d1e62d322f30 100644 --- a/smp-angular/src/app/user/certificate.service.ts +++ b/smp-angular/src/app/user/certificate.service.ts @@ -2,20 +2,14 @@ import {Injectable} from '@angular/core'; import {Observable} from 'rxjs'; import {CertificateRo} from './certificate-ro.model'; import {HttpClient} from '@angular/common/http'; +import {SmpConstants} from "../smp.constants"; @Injectable() export class CertificateService { constructor(private http: HttpClient) {} - uploadCertificate$(payload, username: string): Observable<CertificateRo> { - return this.http.put<CertificateRo>(`rest/user/${username}/certificate`, payload); - } - - - - onUpload(file) { - - return this.http.post<CertificateRo>('rest/user/certdata',file, ) + uploadCertificate$(payload): Observable<CertificateRo> { + return this.http.post<CertificateRo>(SmpConstants.REST_CERTIFICATE, payload); } } diff --git a/smp-angular/src/app/user/user-details-dialog/user-details-dialog.component.css b/smp-angular/src/app/user/user-details-dialog/user-details-dialog.component.css index 71e62bc47d4bfd2d242895c3cd26740c5543134a..7bff79416ece96706ec2e31248ccb2fa921fcbf9 100644 --- a/smp-angular/src/app/user/user-details-dialog/user-details-dialog.component.css +++ b/smp-angular/src/app/user/user-details-dialog/user-details-dialog.component.css @@ -3,21 +3,24 @@ } .custom-file-upload { - border: 1px solid #ccc; display: inline-block; - padding: 6px 12px; cursor: pointer; - background-color: #2196f3; } -.custom-file-upload-inner { - color: white; - cursor: pointer; - background-color: #2196f3; +.username, .role, .password, .password-confirmation, .certificate-subject, .certificate-issuer, .certificate-serial-number { + width: 100%; +} + +.certificate-valid-from, .certificate-valid-to { + width: 40%; +} + +.required-fields { + text-align: right; + font-size: 70% } -.custom-file-upload[disabled] { - color: red; - cursor: not-allowed; - background-color: grey; +.has-error { + color:red; + font-size: 70%; } 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 83d39a4be1c6ad94eac020ed0c407b734d5285c1..cafe4764692f6f7a4b79ea22205dfbf0b4fcd77b 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,104 +1,76 @@ <h2 mat-dialog-title>{{formTitle}}</h2> <mat-dialog-content> - <!--<table>--> - <!--<tr>--> - <!--<td width="50%" valign="top">--> - <!--<mat-grid-list cols="2" gutterSize="40px">--> - <!--<mat-grid-tile class="panel">--> - <mat-card> - <mat-card-content> - <mat-slide-toggle mat-no-ink class="mat-primary" ngModel="userSwitch"> - User Details - </mat-slide-toggle> - <div class="panel"> - <div style="margin-top:15px;"> - <mat-form-field style="width:100%"> - <input matInput placeholder="Username" name="username" id="username" [value]="current.username" (blur)="updateUserName($event)" [formControl]="userForm.controls['username']" maxlength="255" required> - <div *ngIf="userForm.controls['username'].hasError('required') && userForm.controls['username'].touched" style="color:red; font-size: 70%">You should type an username</div> - </mat-form-field> - </div> + <mat-card> + <mat-card-content> + <mat-slide-toggle mat-no-ink class="mat-primary" [formControl]="userForm.controls['userToggle']" (change)="onUserToggleChanged($event)"> + User Details + </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 style="margin-top:10px;"> - <mat-select matInput placeholder="Role" [style.width]="'100%'" [formControl]="userForm.controls['role']" - [(ngModel)]="role" required> - <mat-option *ngFor="let item of existingRoles" [value]="item">{{getRoleLabel(item)}}</mat-option> - </mat-select> - <div *ngIf="userForm.controls['role'].hasError('required') && userForm.controls['role'].touched" style="color:red; font-size: 70%">You need to choose at least one role for this user</div> - </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> - <div style="margin-top:15px;"> - <mat-form-field [style.width]="'100%'"> - <input matInput placeholder="Password" name="password" type="password" [value]="current.password" (blur)="updatePassword($event)" [formControl]="userForm.controls['password']" [pattern]="passwordPattern" [required]="!editMode"> - <div *ngIf="!editMode && userForm.controls['password'].hasError('required') && userForm.controls['password'].touched" style="color:red; font-size: 70%">You should type a password</div> - <div *ngIf="userForm.controls['password'].dirty && userForm.controls['password'].hasError('pattern') && userForm.controls['password'].touched" style="color:red; font-size: 70%"> - 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> + <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> - <div> - <mat-form-field [style.width]="'100%'"> - <input matInput placeholder="Confirmation" name="confirmation" type="password" [value]="current.confirmation" [formControl]="userForm.controls['confirmation']" [required]="!editMode"> - <div *ngIf="!editMode && userForm.controls['confirmation'].hasError('required') && userForm.controls['confirmation'].touched" style="color:red; font-size: 70%">You should type a password</div> - <div *ngIf="userForm.errors?.confirmation && userForm.controls['confirmation'].touched" style="color:red; font-size: 70%">Passwords do not match</div> - </mat-form-field> - </div> + <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-card-content> - </mat-card> - <!--</mat-grid-tile>--> - <!--</td>--> + </mat-form-field> - <!--<td width="50%" valign="top">--> - <!--<mat-grid-tile class="panel">--> - <mat-card> - <mat-card-content> - <mat-slide-toggle mat-no-ink class="mat-primary" ngModel="certificateSwitch"> - Certificate - </mat-slide-toggle> - <div class="panel"> - <fieldset [disabled]="true" style="border: none;"> - <mat-form-field style="width:100%"> - <input matInput placeholder="Subject Name" name="subjectName" value="{{current.certificate?.subject}}" id="subjectName_id"> - </mat-form-field> - <mat-form-field style="width:40%"> - <input matInput placeholder="Valid From" name="validityFrom" value="{{current.certificate?.validFrom | smpDate: dateFormat}}" id="validityFrom_id"> - </mat-form-field> - <mat-form-field style="width:40%"> - <input matInput placeholder="Valid To" name="validityTo" value="{{current.certificate?.validUntil | smpDate: dateFormat}}" id="validityTo_id"> - </mat-form-field> - <mat-form-field style="width:100%"> - <input matInput placeholder="Issuer" name="issuer" value="{{current.certificate?.issuer}}" id="issuer_id"> - </mat-form-field> - <mat-form-field style="width:100%"> - <input matInput placeholder="Fingerprints" name="fingerPrint" value="{{current.certificate?.fingerprints}}" id="fingerPrint_id"> - </mat-form-field> - </fieldset> + <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> - <input - type="file" (change)="onFileChanged($event)" - #fileInput> - <button (click)="onUpload()">Upload!</button> + <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> + <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> - <label class="custom-file-upload"> - <input #fileInput type="file" id="custom-file-upload" accept=".cer" (change)="uploadCertificate()" [disabled]="!certificateSwitch"> - <span class="custom-file-upload-inner">Import</span> - </label> - </div> - </mat-card-content> - </mat-card> - <!--</mat-grid-tile>--> - <!--</mat-grid-list>--> - <!--</td>--> - <!--</tr>--> - <!--</table>--> + <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"> @@ -115,5 +87,5 @@ </td> </tr> </table> -<div style="text-align: right; font-size: 70%">* required fields</div> +<div class="required-fields">* required fields</div> 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 a5b36c61bc266a5007337ef0b4d9dc46bfc5f010..4a39a063b28f7e6e253add8a707441ab3abe32d9 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 @@ -1,17 +1,14 @@ import {Component, Inject, ViewChild} from '@angular/core'; -import {MAT_DIALOG_DATA, MatDialogRef} from '@angular/material'; -import {AbstractControl, FormBuilder, FormControl, FormGroup, ValidationErrors, ValidatorFn, Validators} from '@angular/forms'; +import {MAT_DIALOG_DATA, MatDialogRef, MatSlideToggleChange} from '@angular/material'; +import {FormBuilder, FormControl, FormGroup, ValidationErrors, ValidatorFn, Validators} from '@angular/forms'; import {UserService} from '../user.service'; import {Role} from '../../security/role.model'; -import {RoleService} from '../../security/role.service'; import {UserRo} from '../user-ro.model'; import {SearchTableEntityStatus} from '../../common/search-table/search-table-entity-status.model'; import {AlertService} from '../../alert/alert.service'; import {CertificateService} from '../certificate.service'; -import {Observable} from "rxjs/index"; import {CertificateRo} from "../certificate-ro.model"; -import {SearchTableResult} from "../../common/search-table/search-table-result.model"; - +import {DatePipe} from "../../custom-date/date.pipe"; @Component({ selector: 'user-details-dialog', @@ -25,47 +22,64 @@ export class UserDetailsDialogComponent { // 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'; editMode: boolean; formTitle: string; userRoles = []; - role: string; // temporally added by JRC just to compile the code - dateFormat: string; // temporally added by JRC just to compile the code existingRoles = []; - current: UserRo & { confirmation?: string }; userForm: FormGroup; - userSwitch: boolean; - certificateSwitch: boolean; - @ViewChild('fileInput') private fileInput; - - private passwordConfirmationValidator: ValidatorFn = (control: FormGroup): ValidationErrors | null => { + const userToggle = control.get('userToggle'); const password = control.get('password'); const confirmation = control.get('confirmation'); - return password && confirmation && password.value !== confirmation.value ? { confirmation: true } : null; + return userToggle && password && confirmation && userToggle.value && password.value !== confirmation.value ? { confirmationMatch: true } : null; + }; + + private atLeastOneToggleCheckedValidator: ValidatorFn = (control: FormGroup): ValidationErrors | null => { + const userToggle = control.get('userToggle'); + const certificateToggle = control.get('certificateToggle'); + return userToggle && certificateToggle && !userToggle.value && !certificateToggle.value ? { userDetailsOrCertificateRequired: true} : null; + }; + + private certificateValidator: ValidatorFn = (control: FormGroup): ValidationErrors | null => { + const certificateToggle = control.get('certificateToggle'); + const subject = control.get('subject'); + const validFrom = control.get('validFrom'); + const validTo = control.get('validTo'); + const issuer = control.get('issuer'); + const serialNumber = control.get('serialNumber'); + return certificateToggle && subject && validFrom && validTo && issuer && serialNumber + && certificateToggle.value && !(subject.value && validFrom.value && validTo.value && issuer.value && serialNumber.value) ? { certificateDetailsRequired: true} : null; }; constructor(private dialogRef: MatDialogRef<UserDetailsDialogComponent>, private userService: UserService, private certificateService: CertificateService, private alertService: AlertService, - private roleService: RoleService, + private datePipe: DatePipe, @Inject(MAT_DIALOG_DATA) public data: any, private fb: FormBuilder) { this.editMode = data.edit; this.formTitle = this.editMode ? UserDetailsDialogComponent.EDIT_MODE: UserDetailsDialogComponent.NEW_MODE; - this.current = this.editMode + const user: UserRo & { confirmation?: string } = this.editMode ? { ...data.row, - confirmation: data.row.password, - certificate: data.row.certificate, - } - : { + password: '', // ensures the user password is cleared before editing + confirmation: '', + certificate: { + subject: data.row.subject, + validFrom: data.row.validFrom, + validTo: data.row.validTo, + issuer: data.row.issuer, + serialNumber: data.row.serialNumber, + } + }: { username: '', email: '', password: '', @@ -75,17 +89,29 @@ export class UserDetailsDialogComponent { certificate: {}, }; + const userDetailsToggled: boolean = user && !!user.username; + this.userForm = fb.group({ - 'username': new FormControl({value: this.current.username, disabled: this.editMode}, this.editMode ? Validators.nullValidator : null), - 'role': new FormControl(this.current.role, Validators.required), - 'password': new FormControl(this.current.password, [Validators.required, Validators.pattern(this.passwordPattern)]), - 'confirmation': new FormControl(this.current.password, Validators.pattern(this.passwordPattern)), }, { validator: this.passwordConfirmationValidator + 'userToggle': new FormControl(userDetailsToggled), + 'username': new FormControl({ value: user.username, disabled: this.editMode || !userDetailsToggled }, this.editMode ? Validators.nullValidator : null), + 'role': new FormControl({ value: 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), + }, { + validator: [this.passwordConfirmationValidator, this.atLeastOneToggleCheckedValidator, this.certificateValidator] }); this.userService.getUserRoles$().subscribe(userRoles => { this.userRoles = userRoles.json(); this.existingRoles = this.editMode - ? this.getAllowedRoles(this.userRoles, this.current.role) + ? this.getAllowedRoles(this.userRoles, user.role) : this.userRoles; }); } @@ -94,41 +120,19 @@ export class UserDetailsDialogComponent { this.dialogRef.close(true); } - updateUserName(event) { - this.current.username = event.target.value; - } - - updatePassword(event) { - this.current.password = event.target.value; - } - - getRoleLabel(role: Role): string { - return this.roleService.getLabel(role); - } - - // filters out roles so that the user cannot change from system administrator to the other roles or vice-versa - private getAllowedRoles(allRoles, userRole) { - if (userRole === Role.SYSTEM_ADMINISTRATOR) { - return [Role.SYSTEM_ADMINISTRATOR]; - } else { - return allRoles.filter(role => role !== Role.SYSTEM_ADMINISTRATOR); - } - } - - uploadCertificate () { - const fi = this.fileInput.nativeElement; - const file = fi.files[0]; + uploadCertificate(event) { + const file = event.target.files[0]; const reader = new FileReader(); reader.onload = (e) => { - const arrayBuffer = reader.result; - const array = new Uint8Array(arrayBuffer); - const binaryString = String.fromCharCode.apply(null, array); - - // TODO define userName or use some sort of userId - this.certificateService.uploadCertificate$({content: binaryString}, 'TODO') - .subscribe(res => { - this.current.certificate = res; + this.certificateService.uploadCertificate$(reader.result).subscribe((res: CertificateRo) => { + this.userForm.patchValue({ + 'subject': res.subject, + 'validFrom': this.datePipe.transform(res.validFrom.toString(), this.dateFormat), + 'validTo': this.datePipe.transform(res.validTo.toString(), this.dateFormat), + 'issuer': res.issuer, + 'serialNumber': res.serialNumber + }); }, err => { this.alertService.exception('Error uploading certificate file ' + file.name, err); @@ -139,23 +143,27 @@ export class UserDetailsDialogComponent { this.alertService.exception('Error reading certificate file ' + file.name, err); }; - reader.readAsArrayBuffer(file); + reader.readAsBinaryString(file); } - - // example - selectedFile: File; - obrs: Observable< CertificateRo> ; - certData: CertificateRo; - onFileChanged(event) { - this.selectedFile = event.target.files[0] + onUserToggleChanged({checked}: MatSlideToggleChange) { + const action = checked ? 'enable' : 'disable'; + this.userForm.get('username')[action](); + this.userForm.get('role')[action](); + this.userForm.get('password')[action](); + this.userForm.get('confirmation')[action](); } - onUpload() { - this.obrs = this.certificateService.onUpload(this.selectedFile); - this.obrs.subscribe((cert: CertificateRo) => { - this.certData = cert; - }); + get current(): UserRo { + return this.userForm.getRawValue(); + } + // filters out roles so that the user cannot change from system administrator to the other roles or vice-versa + private getAllowedRoles(allRoles, userRole) { + if (userRole === Role.SYSTEM_ADMINISTRATOR) { + return [Role.SYSTEM_ADMINISTRATOR]; + } else { + return allRoles.filter(role => role !== Role.SYSTEM_ADMINISTRATOR); + } } } diff --git a/smp-angular/src/app/user/user.component.html b/smp-angular/src/app/user/user.component.html index 00fc2f9ece5894755c6c89f92a3df8cca10c1254..92c69b96b46a10856a50cb734a39750db60b804a 100644 --- a/smp-angular/src/app/user/user.component.html +++ b/smp-angular/src/app/user/user.component.html @@ -1,18 +1,13 @@ <smp-search-table - page_id= 'user_id' - title= 'Users' - [columnPicker] = "columnPicker" + page_id="user_id" + title="Users" + [columnPicker]="columnPicker" [url]="'rest/user'" [additionalToolButtons]="additionalToolButtons" [searchTableController]="userController" - [searchPanel]="searchPanel" + [showSearchPanel]="false" [filter]="filter"> <ng-template #additionalToolButtons></ng-template> - <ng-template #searchPanel> - <mat-form-field> - <input matInput placeholder="Username" name="Username" [(ngModel)]="filter.username" #messageId="ngModel"> - </mat-form-field> - </ng-template> </smp-search-table> diff --git a/smp-angular/src/app/user/user.component.ts b/smp-angular/src/app/user/user.component.ts index 4ae256bc10e8ff1baeee53d3247fc95abf49b580..48be157876250bada1117959cc237ae155363ac5 100644 --- a/smp-angular/src/app/user/user.component.ts +++ b/smp-angular/src/app/user/user.component.ts @@ -31,22 +31,20 @@ export class UserComponent implements OnInit { prop: 'username', canAutoResize: true }, + { + name: 'Certificate', + prop: 'subject', + canAutoResize: true + }, { name: 'Role', prop: 'role', canAutoResize: true }, - { - name: 'Password', - prop: 'password', - canAutoResize: true, - sortable: false, - width: 25 - } ]; this.columnPicker.selectedColumns = this.columnPicker.allColumns.filter(col => { - return ['Username', 'Role'].indexOf(col.name) != -1 + return ['Username', 'Certificate', 'Role'].indexOf(col.name) != -1 }); } diff --git a/smp-parent-pom/pom.xml b/smp-parent-pom/pom.xml index 63b7d0db3d5ef1723fafc070f2517e3d1b5844d8..3b6121fb492e2df2abe700b60192e507c3f605ff 100644 --- a/smp-parent-pom/pom.xml +++ b/smp-parent-pom/pom.xml @@ -451,6 +451,11 @@ <artifactId>jackson-annotations</artifactId> <version>${jackson.version}</version> </dependency> + <dependency> + <groupId>com.fasterxml.jackson.datatype</groupId> + <artifactId>jackson-datatype-jsr310</artifactId> + <version>${jackson.version}</version> + </dependency> <!-- End Jackson--> <dependency> <groupId>org.apache.commons</groupId> @@ -676,7 +681,7 @@ <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <configuration> - <!-- fork needs to be false to compile JAXB resources. Otherwise the + <!-- fork needs to be false to compile JAXB resources. Otherwise the problem with the forward slash in package-info.java on Windows occurs! --> <fork>false</fork> <source>1.8</source> diff --git a/smp-server-library/pom.xml b/smp-server-library/pom.xml index bb34c4f79c8cfe999f7af76910cde763f1ca2976..f5702e941a45a33a4d961abfc15cd6ed2eb62d30 100644 --- a/smp-server-library/pom.xml +++ b/smp-server-library/pom.xml @@ -112,6 +112,10 @@ <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-annotations</artifactId> </dependency> + <dependency> + <groupId>com.fasterxml.jackson.datatype</groupId> + <artifactId>jackson-datatype-jsr310</artifactId> + </dependency> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-core</artifactId> 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 37f7a40acb7ef6224a055828af32c81025fd908d..6555e37084f3e0d8ab48bf2cd7bf915bf7867ef1 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 @@ -1,25 +1,13 @@ package eu.europa.ec.edelivery.smp.data.ui; - - - - -import eu.europa.ec.edelivery.smp.data.model.CommonColumnsLengths; - -import javax.persistence.Column; -import javax.persistence.Id; -import java.math.BigInteger; import java.time.LocalDateTime; - /** * @author Joze Rihtarsic * @since 4.1 */ public class CertificateRO extends BaseRO { - - private static final long serialVersionUID = -4971552086560325302L; private String certificateId; @@ -30,7 +18,6 @@ public class CertificateRO extends BaseRO { private LocalDateTime validTo; public CertificateRO(){ - } public static long getSerialVersionUID() { 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..cbd5e1ed8b1fe575906d8a2d2745fa8997062c0b --- /dev/null +++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/ui/ServiceGroupDomainRO.java @@ -0,0 +1,81 @@ +package eu.europa.ec.edelivery.smp.data.ui; + + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; + +/** + * @author Joze Rihtarsic + * @since 4.1 + */ + + +public class ServiceGroupRO extends BaseRO { + + + private static final long serialVersionUID = -7523221767041516157L; + private Long id; + private String participantIdentifier; + private String participantScheme; + private boolean smlRegistered = false; + private List<ServiceMetadataRO> lstServiceMetadata = new ArrayList<>(); + private List<UserRO> lstUser = new ArrayList<>(); + private List<DomainRO> lstDomains = new ArrayList<>(); + private String extension; + + + + + + 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 boolean isSmlRegistered() { + return smlRegistered; + } + + public void setSmlRegistered(boolean smlRegistered) { + this.smlRegistered = smlRegistered; + } + + public String getExtension() { + return extension; + } + + public void setExtension(String extension) { + this.extension = extension; + } + + public List<ServiceMetadataRO> getServiceMetadata() { + return lstServiceMetadata; + } + public List<UserRO> getUsers() { + return lstUser; + } + public List<DomainRO> getDomains() { + return lstDomains; + } + +} 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..7867b2680b74fad1b52b597203ec00512f0b6b24 --- /dev/null +++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/ui/ServiceGroupExtensionRO.java @@ -0,0 +1,4 @@ +package eu.europa.ec.edelivery.smp.data.ui; + +public class ServiceGroupExtensionRO { +} 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..cbd5e1ed8b1fe575906d8a2d2745fa8997062c0b --- /dev/null +++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/ui/ServiceGroupSearchRO.java @@ -0,0 +1,81 @@ +package eu.europa.ec.edelivery.smp.data.ui; + + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; + +/** + * @author Joze Rihtarsic + * @since 4.1 + */ + + +public class ServiceGroupRO extends BaseRO { + + + private static final long serialVersionUID = -7523221767041516157L; + private Long id; + private String participantIdentifier; + private String participantScheme; + private boolean smlRegistered = false; + private List<ServiceMetadataRO> lstServiceMetadata = new ArrayList<>(); + private List<UserRO> lstUser = new ArrayList<>(); + private List<DomainRO> lstDomains = new ArrayList<>(); + private String extension; + + + + + + 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 boolean isSmlRegistered() { + return smlRegistered; + } + + public void setSmlRegistered(boolean smlRegistered) { + this.smlRegistered = smlRegistered; + } + + public String getExtension() { + return extension; + } + + public void setExtension(String extension) { + this.extension = extension; + } + + public List<ServiceMetadataRO> getServiceMetadata() { + return lstServiceMetadata; + } + public List<UserRO> getUsers() { + return lstUser; + } + public List<DomainRO> getDomains() { + return lstDomains; + } + +} 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 9872cf97ec7f03c2c8d3f662a82e9ae6f77b5fa1..a279e25d2b4a4ce06c29f0e660789b19f9d793bb 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 @@ -23,7 +23,8 @@ public enum ErrorCode { ILLEGAL_STATE_CERT_ID_MULTIPLE_ENTRY(504,"SMP:122",ErrorBusinessCode.TECHNICAL,"More than one certificate entry (cert. id: '%s') is defined in database!"), USER_NOT_EXISTS(400,"SMP:123",ErrorBusinessCode.USER_NOT_FOUND,"User not exists or wrong password!"), // OWASP recommendation\ USER_IS_NOT_OWNER(400,"SMP:124",ErrorBusinessCode.UNAUTHORIZED,"User %s is not owner of service group (part. id: %s, part. sch.: '%s')!"), // OWASP recommendation - + INVALID_CERTIFICATE_MESSAGE_DIGEST(500, "SMP:125", ErrorBusinessCode.TECHNICAL, "Could not initialize MessageDigest"), + INVALID_CERTIFICATE_ENCODING(500, "SMP:126", ErrorBusinessCode.TECHNICAL, "Could not encode certificate"), // service group error ILLEGAL_STATE_SG_MULTIPLE_ENTRY (500,"SMP:130",ErrorBusinessCode.TECHNICAL,"More than one service group ( part. id: %s, part. sch.: '%s') is defined in database!"), 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..e399f41ea4f4000297e351f98599bfae2228bb43 --- /dev/null +++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/ui/UIServiceGroupSearchService.java @@ -0,0 +1,285 @@ +package eu.europa.ec.edelivery.smp.services.ui; + +import eu.europa.ec.edelivery.smp.conversion.ExtensionConverter; +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.model.DBServiceGroupDomain; +import eu.europa.ec.edelivery.smp.data.model.DBUser; +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.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +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.Optional; + +import static eu.europa.ec.edelivery.smp.exceptions.ErrorCode.DOMAIN_NOT_EXISTS; +import static eu.europa.ec.edelivery.smp.exceptions.ErrorCode.INVALID_ENCODING; +import static eu.europa.ec.edelivery.smp.exceptions.ErrorCode.INVALID_REQEUST; + +@Service +public class UIServiceGroupService extends UIServiceBase<DBServiceGroup, ServiceGroupRO> { + private static final SMPLogger LOG = SMPLoggerFactory.getLogger(UIServiceGroupService.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<ServiceGroupRO> getTableList(int page, int pageSize, + String sortField, + String sortOrder, ServiceGroupFilter filter, String domainCode) { + + DBDomain d = null; + if (!StringUtils.isBlank(domainCode)){ + Optional<DBDomain> od = domainDao.getDomainByCode(domainCode); + if (od.isPresent()){ + d = od.get(); + } else { + throw new SMPRuntimeException(DOMAIN_NOT_EXISTS, domainCode); + } + + } + ServiceResult<ServiceGroupRO> sg = new ServiceResult<>(); + sg.setPage(page < 0 ? 0 : page); + sg.setPageSize(pageSize); + long iCnt = serviceGroupDao.getServiceGroupCount(filter, d); + sg.setCount(iCnt); + + if (iCnt > 0) { + int iStartIndex = pageSize < 0 ? -1 : page * pageSize; + List<DBServiceGroup> lst = serviceGroupDao.getServiceGroupList(iStartIndex, pageSize, sortField, sortOrder, filter, d); + List<ServiceGroupRO> lstRo = new ArrayList<>(); + for (DBServiceGroup dbServiceGroup : lst) { + ServiceGroupRO 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 ServiceGroupRO convertToRo(DBServiceGroup dbServiceGroup) { + ServiceGroupRO serviceGroupRo = new ServiceGroupRO(); + serviceGroupRo.setId(dbServiceGroup.getId()); + serviceGroupRo.setParticipantIdentifier(dbServiceGroup.getParticipantIdentifier()); + serviceGroupRo.setParticipantScheme(dbServiceGroup.getParticipantScheme()); + dbServiceGroup.getServiceGroupDomains().forEach(sgd -> { + DomainRO dmn = new DomainRO(); + dmn.setId(sgd.getDomain().getId()); + dmn.setDomainCode(sgd.getDomain().getDomainCode()); + dmn.setSmlSubdomain(sgd.getDomain().getSmlSubdomain()); + serviceGroupRo.getDomains().add(dmn); + + 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); + }); + return serviceGroupRo; + } + + @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 converts UI resource object entity to database entity and persists it to database + * @param dRo + */ + private void addNewServiceGroup(ServiceGroupRO dRo){ + DBServiceGroup dDb = convertFromRo(dRo); + for (UserRO userRO: dRo.getUsers()) { + DBUser du = userDao.find(userRO.getId()); + dDb.getUsers().add(du); + } + for (DomainRO domainRO: dRo.getDomains()) { + DBDomain dmn = domainDao.find(domainRO.getId()); + DBServiceGroupDomain dsgdomain = new DBServiceGroupDomain(); + dsgdomain.setDomain(dmn); + dDb.getServiceGroupDomains().add(dsgdomain); + } + if (dRo.getExtension()!=null){ + try { + dDb.setExtension(dRo.getExtension().getBytes("UTF-8")); + } catch (UnsupportedEncodingException e) { + throw new SMPRuntimeException(INVALID_ENCODING, "UTF-8"); + } + } + getDatabaseDao().persistFlushDetach(dDb); + } + + /** + * Method converts UI resource object entity to database entity and update changes to database + * @param dRo + */ + private void updateServiceGroup(ServiceGroupRO dRo){ + DBServiceGroup upd = getDatabaseDao().find(dRo.getId()); + upd.getUsers().clear(); + // update users + List<UserRO> lstUsers = dRo.getUsers(); + for (UserRO userRO: lstUsers) { + System.out.println("GET USER ID: " + userRO.getId()); + DBUser du = userDao.find(userRO.getId()); + upd.getUsers().add(du); + }/* + // get domains + List<DBServiceGroupDomain> lstDomains = dRo.getDomains(); + for (DBDomain domainRO: dRo.getDomains()) { + System.out.println("GET USER ID: " + userRO.getId()); + DBUser du = userDao.find(userRO.getId()); + upd.getUsers().add(du); + }*/ + + // and domain + getDatabaseDao().update(upd); + } + + + 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; + } + + /** + * 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..825cf29cb117bb2f57fda6ec7d6184f2aee99f1e --- /dev/null +++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/ui/UIServiceMetadataService.java @@ -0,0 +1,595 @@ +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.*; +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.busdox.transport.identifiers._1.DocumentIdentifierType; +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 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; + + @Autowired + UserDao userDao; + + @Autowired + private CaseSensitivityNormalizer caseSensitivityNormalizer; + + + @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<ServiceGroupRO> getTableList(int page, int pageSize, + String sortField, + String sortOrder, ServiceGroupFilter filter, String domainCode) { + + DBDomain d = null; + if (!StringUtils.isBlank(domainCode)) { + Optional<DBDomain> od = domainDao.getDomainByCode(domainCode); + if (od.isPresent()) { + d = od.get(); + } else { + throw new SMPRuntimeException(DOMAIN_NOT_EXISTS, domainCode); + } + + } + ServiceResult<ServiceGroupRO> sg = new ServiceResult<>(); + sg.setPage(page < 0 ? 0 : page); + sg.setPageSize(pageSize); + long iCnt = serviceGroupDao.getServiceGroupCount(filter, d); + sg.setCount(iCnt); + + if (iCnt > 0) { + int iStartIndex = pageSize < 0 ? -1 : page * pageSize; + List<DBServiceGroup> lst = serviceGroupDao.getServiceGroupList(iStartIndex, pageSize, sortField, sortOrder, filter, d); + List<ServiceGroupRO> lstRo = new ArrayList<>(); + for (DBServiceGroup dbServiceGroup : lst) { + ServiceGroupRO serviceGroupRo = convertToRo(dbServiceGroup); + serviceGroupRo.setStatus(EntityROStatus.PERSISTED.getStatusNumber()); + serviceGroupRo.setIndex(iStartIndex++); + lstRo.add(serviceGroupRo); + } + sg.getServiceEntities().addAll(lstRo); + } + return sg; + } + + @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.getServiceGroupForDomain(serviceMetadataRO.getDomainCode()); + // 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()) { + 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()) { + // get service metadata + byte[] buff = validateServiceMetadata(serviceMetadataRO); + + DBServiceGroupDomain dbServiceGroupDomain = optionalDbServiceGroupDomain.get(); + DBServiceMetadata dbServiceMetadata = dbServiceGroupDomain.getServiceMetadata(serviceMetadataRO.getDocumentIdentifier(), + serviceMetadataRO.getDocumentIdentifierScheme()); + + + dbServiceMetadata.setXmlContent(buff); + + } 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 domains + dbServiceGroup.getServiceGroupDomains().forEach(sgd -> { + 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.setDomainId(servGrpDomain.getDomainId()); + smdro.setServiceGroupDomainId(servGrpDomain.getId()); + serviceGroupRo.getServiceMetadata().add(smdro); + }); + //also add domain to service group + serviceGroupRo.getServiceGroupDomains().add(servGrpDomain); + }); + // 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); + }); + // do not add service extension to gain performance. + return serviceGroupRo; + } + + /** + * 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) { + + byte[] buff = validateServiceMetadata(serviceMetadataRO); + DBServiceMetadata dbServiceMetadata = new DBServiceMetadata(); + dbServiceMetadata.setDocumentIdentifier(serviceMetadataRO.getDocumentIdentifier()); + dbServiceMetadata.setDocumentIdentifierScheme(serviceMetadataRO.getDocumentIdentifierScheme()); + dbServiceMetadata.setXmlContent(buff); + + 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"); + } + 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; + } + + @Transactional + public ServiceMetadataRO getServiceMetadata(Long serviceMetadataId){ + + } + +} 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 480b2a00356dec43776099755be0085a735b9543..bceb3a0716e1c4aa685fc254517887e1faa1940f 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 @@ -5,16 +5,15 @@ import eu.europa.ec.edelivery.smp.BCryptPasswordHash; import eu.europa.ec.edelivery.smp.data.dao.BaseDao; import eu.europa.ec.edelivery.smp.data.dao.UserDao; import eu.europa.ec.edelivery.smp.data.model.DBCertificate; -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.CertificateRO; -import eu.europa.ec.edelivery.smp.data.ui.DomainRO; 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 eu.europa.ec.edelivery.smp.services.ServiceGroupService; import org.apache.commons.beanutils.BeanUtils; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; @@ -22,15 +21,16 @@ import org.springframework.security.crypto.bcrypt.BCrypt; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import javax.ejb.Local; +import javax.xml.bind.DatatypeConverter; import java.io.ByteArrayInputStream; -import java.io.FileInputStream; 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.LocalDate; import java.time.LocalDateTime; import java.time.ZoneId; import java.util.List; @@ -138,9 +138,6 @@ public class UIUserService extends UIServiceBase<DBUser, UserRO> { cro.setValidTo(LocalDateTime.ofInstant(cert.getNotAfter().toInstant(), ZoneId.systemDefault())); return cro; - - - } public String getCertificateIdFromCertificate(String subject, String issuer, BigInteger serial ){ 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..357dbf742eeabc06788473ce22c00cc532604b67 --- /dev/null +++ b/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/testutil/TestROUtils.java @@ -0,0 +1,146 @@ +package eu.europa.ec.edelivery.smp.testutil; + +import eu.europa.ec.edelivery.smp.data.model.*; + +import java.time.LocalDateTime; +import java.util.UUID; + +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 TestDBUtils { + + public static DBDomain createDBDomain(String domainCode) { + DBDomain domain = new DBDomain(); + domain.setDomainCode(domainCode); + domain.setSignatureKeyAlias(UUID.randomUUID().toString()); + domain.setSmlClientCertHeader(UUID.randomUUID().toString()); + domain.setSmlClientKeyAlias(UUID.randomUUID().toString()); + domain.setSmlSubdomain(UUID.randomUUID().toString()); + domain.setSmlParticipantIdentifierRegExp(UUID.randomUUID().toString()); + return domain; + } + + + public static DBDomain createDBDomain() { + return createDBDomain(TestConstants.TEST_DOMAIN_CODE_1); + } + + public static DBServiceGroup createDBServiceGroup() { + return createDBServiceGroup(TestConstants.TEST_SG_ID_1, TestConstants.TEST_SG_SCHEMA_1); + } + + public static DBServiceMetadata createDBServiceMetadata(String partcId, String partcSch) { + return createDBServiceMetadata(partcId,partcSch, UUID.randomUUID().toString(), UUID.randomUUID().toString(), UUID.randomUUID().toString() ); + } + public static DBServiceMetadata createDBServiceMetadata(String partcId, String partcSch, String docId, String docSch ) { + return createDBServiceMetadata(partcId,partcSch, docId, docSch, UUID.randomUUID().toString() ); + } + + public static DBServiceMetadata createDBServiceMetadata(String partcId, String partcSch, String docId, String docSch, String desc) { + DBServiceMetadata grp = new DBServiceMetadata(); + grp.setDocumentIdentifier(docId); + grp.setDocumentIdentifierScheme(docSch); + grp.setXmlContent(generateDocumentSample(partcSch, partcId,docSch, docId, desc)); + return grp; + } + + public static byte[] generateDocumentSample(String partcId, String partcSch, String docId, String docSch, String desc){ + return String.format(SIMPLE_DOCUMENT_XML,partcSch, partcId,docSch, docId, desc).getBytes(); + } + public static byte[] generateExtension(){ + return String.format(SIMPLE_EXTENSION_XML, UUID.randomUUID().toString()).getBytes(); + } + + public static DBServiceGroup createDBServiceGroup(String id, String sch) { + DBServiceGroup grp = new DBServiceGroup(); + grp.setParticipantIdentifier(id); + grp.setParticipantScheme(sch); + grp.setExtension(generateExtension()); + return grp; + } + + + + public static DBUser createDBUser(String username1) { + return createDBUserByUsername(TestConstants.USERNAME_1); + } + + public static DBUser createDBUserByUsername(String userName) { + DBUser dbuser = new DBUser(); + dbuser.setUsername(userName); + dbuser.setRole("test"); + dbuser.setEmail("test@test.eu"); + dbuser.setPasswordChanged(LocalDateTime.now()); + dbuser.setPassword(UUID.randomUUID().toString()); + return dbuser; + } + + public static DBCertificate createDBCertificate() { + return createDBCertificate(TestConstants.USER_CERT_1); + } + public static DBCertificate createDBCertificate(String certId) { + DBCertificate dbcert = new DBCertificate(); + dbcert.setCertificateId(certId); + dbcert.setValidFrom(LocalDateTime.now()); + dbcert.setValidTo(LocalDateTime.now()); + return dbcert; + } + public static DBUser createDBUserByCertificate(String certId) { + DBUser dbuser = new DBUser(); + dbuser.setRole("test"); + + DBCertificate dbcert = new DBCertificate(); + dbcert.setCertificateId(certId); + dbcert.setValidFrom(LocalDateTime.now()); + dbcert.setValidTo(LocalDateTime.now()); + dbuser.setCertificate(dbcert); + return dbuser; + } + + public static DBUser createDBUser(String userName, String certId) { + DBUser dbuser =createDBUserByUsername(userName); + DBCertificate dbcert =createDBCertificate(certId); + dbuser.setCertificate(dbcert); + return dbuser; + } + + + /* + public static DBOwnership createDBOwnership(){ + DBServiceGroup grp = createDBServiceGroup(); + + DBUser dbuser = createDBUser(); + + DBOwnershipId ownID = new DBOwnershipId(); + ownID.setBusinessIdentifier(grp.getId().getBusinessIdentifier()); + ownID.setBusinessIdentifierScheme(grp.getId().getBusinessIdentifierScheme()); + ownID.setUsername(dbuser.getUsername()); + + DBOwnership own = new DBOwnership(); + own.setId(ownID); + own.setServiceGroup(grp); + own.setUser(dbuser); + return own; + } + + public static DBServiceMetadata createDBServiceMetadata(){ + DBServiceGroup grp = createDBServiceGroup(); + + + DBServiceMetadataId smdId = new DBServiceMetadataId(); + smdId.setBusinessIdentifier(grp.getId().getBusinessIdentifier()); + smdId.setBusinessIdentifierScheme(grp.getId().getBusinessIdentifierScheme()); + smdId.setDocumentIdentifier(REVISION_DOCUMENT_ID); + smdId.setDocumentIdentifierScheme(REVISION_DOCUMENT_SCH); + + DBServiceMetadata smd = new DBServiceMetadata(); + smd.setId(smdId); + smd.setServiceGroup(grp); + smd.setXmlContent(UUID.randomUUID().toString()); + + + return smd; + } + */ +} 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..08e00ed29169d1c8876c8d593fc2d675df3b61bf --- /dev/null +++ b/smp-server-library/src/test/resources/examples/extensions/extensionCustom.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/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..a1aafe93346c5e746b3464af0d03180ea0eb37c5 --- /dev/null +++ b/smp-server-library/src/test/resources/examples/extensions/extensionInvalid.xml @@ -0,0 +1,18 @@ +<Extension xmlns:ns2="http://www.w3.org/2000/09/xmldsig#" 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> +</Extension> +<Extension xmlns:ns2="http://www.w3.org/2000/09/xmldsig#" xmlns="http://docs.oasis-open.org/bdxr/ns/SMP/2016/05"> + <ExtensionID>id2</ExtensionID> + <ExtensionName>name2</ExtensionName> + <ExtensionAgencyName>agencyName2</ExtensionAgencyName> + <ExtensionAgencyURI>agencyUri2</ExtensionAgencyURI> + <ExtensionVersionID>versionId2</ExtensionVersionID> + <ExtensionReasonCode>reasonCode2</ExtensionReasonCode> + <ExtensionReason>reason2</ExtensionReason> +</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..a11a10e5eca3cffaea45d1cca9ffe32990b79d03 --- /dev/null +++ b/smp-server-library/src/test/resources/examples/extensions/extensionValidMultiple.xml @@ -0,0 +1,10 @@ +<Extension xmlns:ns2="http://www.w3.org/2000/09/xmldsig#" 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> 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..7c20d3eb5940f9c5ce4e815afda0b33ccfac8de4 --- /dev/null +++ b/smp-server-library/src/test/resources/examples/extensions/extensionValidOne.xml @@ -0,0 +1 @@ +<Extension xmlns:ns2="http://www.w3.org/2000/09/xmldsig#" 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></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/truststore/SMPtest.crt b/smp-server-library/src/test/resources/truststore/SMPtest.crt new file mode 100644 index 0000000000000000000000000000000000000000..f02b169986b62740a8aa57b3a450ac2212d334b1 --- /dev/null +++ b/smp-server-library/src/test/resources/truststore/SMPtest.crt @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDGjCCAgKgAwIBAgIBAzANBgkqhkiG9w0BAQUFADA3MQswCQYDVQQGEwJCRTEO +MAwGA1UEChMFRElHSVQxGDAWBgNVBAMTD0ludGVybWVkaWF0ZSBDQTAeFw0xODA1 +MjIxODU5MDBaFw0xOTA1MjIxODU2MDBaME0xCzAJBgNVBAYTAkJFMQ4wDAYDVQQK +EwVESUdJVDERMA8GA1UEAxMIU01QIHRlc3QxGzAZBgkqhkiG9w0BCQEWDHNtcEB0 +ZXN0LmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAN2GBB4bW5HA +lPY4z71I36v34xFWz6iv80PFe9dSA4hSH621bC8xjB6tKvgol8/9oSDO8OL2Do+I +x16Scn7u5iFnV3c64XnsBVtglRcntjJi7RoXje3AfJnxkBOptbBeNaV6UFqsabRa +3mapZbJ/8Bog7rayCRp27UZEJVK/1lw/f2p8/9iyKInchHYWuC8KtUGxwpjVy6eb +Zh+TX32U3KU9UDNkbhEkKG0M+aC6BhCo21qnkv1KZ0QO4ol1MDoG7d1seQlJBb9C +dQOhDN4USc62biqShcZthDwsJpoPIJuGhwEH5tigyUVJ0zv+MzSPvzqpgn7BMvKe +MPH0/2m1Kv0CAwEAAaMbMBkwCQYDVR0TBAIwADAMBgNVHQ8EBQMDB/mAMA0GCSqG +SIb3DQEBBQUAA4IBAQAfJkY3mF/1aftAgiQHdSJEm0OwT/Ow1Wv3nlljz5RlR5Wz +Co3I83TLum6F8bnF0taA0nks3NzYBjInd6DKHhV54lhCga1JydCIMr1DFrGPbLui +OlnF89y4TWkIkGOWd74alxLv+ioE0PiPDWZjMXxOWcAkYz+Qjo0BkdbEaWglTjLA +LBWMjpiVV7Pf7wH2R+D6KW4ktKTnO+yM1VihJkaDPMtGxv4nGK7npATg560BR+hJ +kY53TEz2RhRMedwUz6weNibI4cfRyD1qIElMGOtol55bRNFcby1LfXLz7ljcY+LB +VXbP7yNUsICtrN+9pWW51TGw8/lkSTeYkzsiQ5as +-----END CERTIFICATE----- diff --git a/smp-server-library/src/test/resources/truststore/TestCase.crt b/smp-server-library/src/test/resources/truststore/TestCase.crt new file mode 100644 index 0000000000000000000000000000000000000000..15520e1ebc7d650f3f45e04e741294dbe90e976a --- /dev/null +++ b/smp-server-library/src/test/resources/truststore/TestCase.crt @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDGTCCAgGgAwIBAgIBATANBgkqhkiG9w0BAQUFADBQMQswCQYDVQQGEwJiZTER +MA8GA1UEBxMIYnJ1c3NlbHMxDjAMBgNVBAsTBURJR0lUMR4wHAYDVQQDExVUZXNU +IFVQUEVSIGxvd2VyIENBU0UwHhcNMTgwOTA2MDc1MDAwWhcNMTkwNTIyMTg0OTAw +WjBQMQswCQYDVQQGEwJiZTERMA8GA1UEBxMIYnJ1c3NlbHMxDjAMBgNVBAsTBURJ +R0lUMR4wHAYDVQQDExVUZXNUIFVQUEVSIGxvd2VyIENBU0UwggEiMA0GCSqGSIb3 +DQEBAQUAA4IBDwAwggEKAoIBAQDCYHH85zNNJaMuwdPJCCx67O+DNQ0NtpISLdFt +nLQ6EtGBbjxgoAeg7QTc6GFe3KhP9PdZiczwoTPl0PhmpT/hWinElDO6x0JBefWs +7lgPUgATceRYYH/ecPF0bl7a8BpQrYlIyPYmjb1aYSrz0SjQMYLaUhhoeFP3UmaU +CSNUnPqoZ7/DfUYhDPWayoHo0Q+w6QurqNzh8v5dbT19MhbAGv9L3Gx0dh2tviy/ +a3mg8roCtv44jsoKenJ+2Q433d7IgcbCJm8CHetTdtWJ7xYY19MoEqCFLxlo4iSC +E6WQuxfPrL5MK4+6Dl7vJhp6XiqBKuU35ptrWukgYh+bcIIJAgMBAAEwDQYJKoZI +hvcNAQEFBQADggEBAJSmevfTz/xSuqSZiSXb17SuGrDLhVbgm3ECdx+e6A/X3TDF +uZHcPHlascB++aRmCuE+zid93pFxpoHNG7Jr1He7jTp2OlZJU6P3mf9dj0tQ5WE4 +tg3d+r++iv6zABsTxJNU4McizuuhMzLaFzlKOn20CsJol7pFKzwGKGLZ60FTscnT +6aw/LWbeE0N5ODir456sJ/ySSBEFQqczJXFxCtkjqCPudbXxlMhwNWHqggVizjsE +GG86ZAY9230H6QNn0GL9jz4jxGIDkSSBvzGn3ehr1bc4eYHazPA7b/yTCQf+QzG6 +RBAuhkzeL3WW+DDEeMxld5Gue54HBjkL6hCrDhw= +-----END CERTIFICATE----- diff --git a/smp-soapui-tests/groovy/oralce-4.1_integration_test_data.sql b/smp-soapui-tests/groovy/oracle-4.1_integration_test_data.sql similarity index 100% rename from smp-soapui-tests/groovy/oralce-4.1_integration_test_data.sql rename to smp-soapui-tests/groovy/oracle-4.1_integration_test_data.sql 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 91141c04923188c15015c1041daafc416e3190b3..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 @@ -13,12 +13,24 @@ package eu.europa.ec.edelivery.smp.config; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; import eu.europa.ec.edelivery.smp.error.ErrorMappingControllerAdvice; +import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; -import org.springframework.web.servlet.config.annotation.*; +import org.springframework.context.annotation.Primary; +import org.springframework.http.converter.HttpMessageConverter; +import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder; +import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; +import org.springframework.web.servlet.config.annotation.EnableWebMvc; +import org.springframework.web.servlet.config.annotation.PathMatchConfigurer; +import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; +import org.springframework.web.servlet.config.annotation.ViewControllerRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; +import java.util.List; import static org.springframework.core.Ordered.HIGHEST_PRECEDENCE; @@ -35,6 +47,29 @@ import static org.springframework.core.Ordered.HIGHEST_PRECEDENCE; @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 + // default converters (@EnableWebMvc disables auto-configuration and most of the default converters are + // not available as Spring beans + // org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport.addDefaultHttpMessageConverters) + converters.removeIf(httpMessageConverter -> httpMessageConverter instanceof MappingJackson2HttpMessageConverter); + converters.add(jackson2HttpMessageConverter()); + } + + @Bean + public MappingJackson2HttpMessageConverter jackson2HttpMessageConverter() { + return new MappingJackson2HttpMessageConverter(objectMapper()); + } + + @Bean + @Primary + public ObjectMapper objectMapper() { + return Jackson2ObjectMapperBuilder.json() + .featuresToDisable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS) + .build(); + } + @Override public void addViewControllers(ViewControllerRegistry registry) { registry.addViewController("/").setViewName("/index.html"); 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..f4d66605904520b1e753202628a4522e710679e1 --- /dev/null +++ b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/ServiceMetadataResource.java @@ -0,0 +1,58 @@ +package eu.europa.ec.edelivery.smp.ui; + + +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.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.DomainResource; +import eu.europa.ec.edelivery.smp.validation.ServiceMetadataValidator; +import eu.europa.ec.smp.api.exceptions.XmlInvalidAgainstSchemaException; +import eu.europa.ec.smp.api.validators.BdxSmpOasisValidator; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.PostConstruct; +import java.util.Arrays; + +/** + * @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 + ServiceMetadataValidator serviceMetadataValidator; + + @Autowired + private UIServiceGroupService uiServiceGroupService; + + + @ResponseBody + @PutMapping(produces = {"application/json"}) + @RequestMapping(method = RequestMethod.GET, path = "{serviceMetadataId}") + public ServiceGroupRO getServiceGroupById(@PathVariable Long serviceMetadataId) { + return uiServiceGroupService.getServiceGroupById(serviceMetadataId); + } + + private void validateServiceMetadata(ServiceMetadataRO val){ +/* + try { + // serviceMetadataValidator.validate(); + //BdxSmpOasisValidator.validateXSD(xml.getBytes()); + + + } catch (XmlInvalidAgainstSchemaException e) { + e.printStackTrace(); + } +*/ + } +} +