diff --git a/smp-angular/src/app/app.module.ts b/smp-angular/src/app/app.module.ts index 2234f5b068e8f4ffe850120bbdcd96ec23f7730e..3ecf401ae17a1f3dfba33927c164918fde250618 100644 --- a/smp-angular/src/app/app.module.ts +++ b/smp-angular/src/app/app.module.ts @@ -42,12 +42,11 @@ import {AlertService} from './alert/alert.service'; import {FooterComponent} from './footer/footer.component'; import {SmpInfoService} from './app-info/smp-info.service'; import {AuthorizedAdminGuard} from './guards/authorized-admin.guard'; -import {ServiceGroupComponent} from './service-group-edit/service-group.component'; +import {ServiceGroupEditComponent} from './service-group-edit/service-group-edit.component'; import {ServiceGroupSearchComponent} from './service-group-search/service-group-search.component'; import {DomainComponent} from './domain/domain.component'; import {UserComponent} from './user/user.component'; import {BrowserAnimationsModule} from '@angular/platform-browser/animations'; -import {ServiceGroupMetadataListDialogComponent} from './service-group-edit/service-group-metadata-list-dialog/service-group-metadata-list-dialog.component'; import {RowLimiterComponent} from './common/row-limiter/row-limiter.component'; import {DatePipe} from './custom-date/date.pipe'; import {CapitalizeFirstPipe} from './common/capitalize-first.pipe'; @@ -79,7 +78,7 @@ import {CertificateService} from './user/certificate.service'; AppComponent, LoginComponent, HomeComponent, - ServiceGroupComponent, + ServiceGroupEditComponent, ServiceGroupSearchComponent, DomainComponent, DomainDetailsDialogComponent, @@ -88,7 +87,6 @@ import {CertificateService} from './user/certificate.service'; FooterComponent, IsAuthorized, SaveDialogComponent, - ServiceGroupMetadataListDialogComponent, ServiceGroupMetadataDialogComponent, ServiceGroupExtensionDialogComponent, CancelDialogComponent, @@ -108,7 +106,6 @@ import {CertificateService} from './user/certificate.service'; ], entryComponents: [ AppComponent, - ServiceGroupMetadataListDialogComponent, ServiceGroupMetadataDialogComponent, ServiceGroupDetailsDialogComponent, ServiceGroupExtensionDialogComponent, diff --git a/smp-angular/src/app/app.routes.ts b/smp-angular/src/app/app.routes.ts index 6d09b43e6dbb38723cc9d1b084e9be070f88bd43..9044c4b6660ab6f535f97cc2c839832d2313f4d3 100644 --- a/smp-angular/src/app/app.routes.ts +++ b/smp-angular/src/app/app.routes.ts @@ -1,7 +1,7 @@ import {RouterModule, Routes} from '@angular/router'; import {LoginComponent} from './login/login.component'; import {ServiceGroupSearchComponent} from './service-group-search/service-group-search.component'; -import {ServiceGroupComponent} from './service-group-edit/service-group.component'; +import {ServiceGroupEditComponent} from './service-group-edit/service-group-edit.component'; import {DomainComponent} from './domain/domain.component'; import {AuthenticatedGuard} from './guards/authenticated.guard'; import {UserComponent} from './user/user.component'; @@ -10,13 +10,13 @@ import {UserComponent} from './user/user.component'; const appRoutes: Routes = [ {path: '', component: ServiceGroupSearchComponent}, {path: 'search', component: ServiceGroupSearchComponent}, - {path: 'edit', component: ServiceGroupComponent}, + {path: 'edit', component: ServiceGroupEditComponent}, {path: 'domain', component: DomainComponent}, {path: 'user', component: UserComponent}, {path: 'login', component: LoginComponent}, - {path: '**', component: ServiceGroupComponent, canActivate: [AuthenticatedGuard]} + {path: '**', component: ServiceGroupEditComponent, canActivate: [AuthenticatedGuard]} ]; diff --git a/smp-angular/src/app/common/search-table/search-table-entity.model.ts b/smp-angular/src/app/common/search-table/search-table-entity.model.ts index bb9c07e56d35f99584586972297f341cd5b23763..ba45d9bd72ad4a85c29103b186f9a559e4690eb3 100644 --- a/smp-angular/src/app/common/search-table/search-table-entity.model.ts +++ b/smp-angular/src/app/common/search-table/search-table-entity.model.ts @@ -1,6 +1,7 @@ import {SearchTableEntityStatus} from './search-table-entity-status.model'; export interface SearchTableEntity { + index: number; status: SearchTableEntityStatus; deleted?: boolean; } diff --git a/smp-angular/src/app/common/search-table/search-table.component.css b/smp-angular/src/app/common/search-table/search-table.component.css index 9cdb4f04898b062cc8287c4f9a45e319793a9ce3..84f05baf748be2e6d2d54c3adfe5f21cb4378f41 100644 --- a/smp-angular/src/app/common/search-table/search-table.component.css +++ b/smp-angular/src/app/common/search-table/search-table.component.css @@ -18,6 +18,18 @@ /deep/ .deleted span[title] { text-decoration: line-through !important; + font-weight: bold; +} +/deep/ .newTableRow span[title] { + + color: darkgreen !important; + font-weight: bold; +} +/deep/ .updatedTableRow span[title] { + font-weight: bold; +} +/deep/ .tableRow span[title] { + font-weight: normal; } .group-action-button { diff --git a/smp-angular/src/app/common/search-table/search-table.component.html b/smp-angular/src/app/common/search-table/search-table.component.html index 5f1075801fcdb3a60f2f369d58779014170d600a..2b52cd6f8eeb1bbf98d85f65ad60578b6d3ceea4 100644 --- a/smp-angular/src/app/common/search-table/search-table.component.html +++ b/smp-angular/src/app/common/search-table/search-table.component.html @@ -25,7 +25,7 @@ <app-row-limiter [pageSizes]="rowLimiter.pageSizes" (onPageSizeChanged)="changePageSize($event.value)"></app-row-limiter> </span> - <!-- no need for this span class="column-filter-button"> + <!-- no need for this for SMP 4.1 <span class="column-filter-button"> <app-column-picker [allColumns]="columnPicker.allColumns" [selectedColumns]="columnPicker.selectedColumns" (onSelectedColumnsChanged)="columnPicker.changeSelectedColumns($event)"></app-column-picker> </span --> @@ -53,7 +53,8 @@ [selected]="selected" [selectionType]="'single'" (activate)="onActivate($event)" - (select)="onSelect($event)"> + (select)="onSelect($event)" + > <!-- Row Detail Template --> <ngx-datatable-row-detail id="rowDetail" [rowHeight]="'auto'" #searchTableDetailRow (toggle)="onDetailToggle($event)"> @@ -65,7 +66,7 @@ </ngx-datatable> <ng-template #rowIndex let-row="row" ngx-datatable-cell-template> - <span>{{row.index}}</span> + <span>{{row.index + 1}}</span> </ng-template> <ng-template #rowActions let-row="row" ngx-datatable-cell-template> @@ -82,7 +83,7 @@ </ng-template> <ng-template #rowExpand let-row="row" let-expanded="expanded" let-disabled="disabled" ngx-datatable-cell-template > - <span *ngIf="!!disabled">( )</span> + <span *ngIf="disabled">( )</span> <a *ngIf="!disabled" class="table-button-expand" href="javascript:void(0)" title="Expand/Collapse Row" 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 821648863b7d4cbca5b60b3ad75761d8a7bcbcb0..14c3c7d206e390b54217478f5e110c1dc59b86fe 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 @@ -111,10 +111,15 @@ export class SearchTableComponent implements OnInit { this.page(this.offset, this.rowLimiter.pageSize, this.orderBy, this.asc); } - getRowClass(row): string { - return row.deleted ? 'deleted' : ''; + getRowClass(row) { + return { + 'newTableRow': (row.status === SearchTableEntityStatus.NEW), + 'updatedTableRow': (row.status === SearchTableEntityStatus.UPDATED), + 'deleted': (row.status === SearchTableEntityStatus.REMOVED) + }; } + getTableDataEntries$(offset: number, pageSize: number, orderBy: string, asc: boolean): Observable<SearchTableResult> { let params: HttpParams = new HttpParams() diff --git a/smp-angular/src/app/domain/domain-controller.ts b/smp-angular/src/app/domain/domain-controller.ts index 38499f3da01dfb0fe2abceefcdd5aeb3547f9dd9..7cf4aea7c8cbe559f94cb8e066fec31ff438685d 100644 --- a/smp-angular/src/app/domain/domain-controller.ts +++ b/smp-angular/src/app/domain/domain-controller.ts @@ -29,6 +29,7 @@ export class DomainController implements SearchTableController { public newRow(): DomainRo { return { + index: null, domainCode: '', smlSubdomain: '', smlSmpId: '', diff --git a/smp-angular/src/app/domain/domain-details-dialog/domain-details-dialog.component.html b/smp-angular/src/app/domain/domain-details-dialog/domain-details-dialog.component.html index ebb925e0ecbb5d1f00035c70f4336fb39c1bb3b2..48af8018950d9733009ae0dd26ab7d7dd80e9658 100644 --- a/smp-angular/src/app/domain/domain-details-dialog/domain-details-dialog.component.html +++ b/smp-angular/src/app/domain/domain-details-dialog/domain-details-dialog.component.html @@ -49,7 +49,7 @@ <tr> <td> <button mat-raised-button color="primary" [mat-dialog-close]="true" (click)="submitForm()" [disabled]="!domainForm.valid"> - <mat-icon>ok</mat-icon> + <mat-icon>check_circle</mat-icon> <span>OK</span> </button> <button mat-raised-button color="primary" mat-dialog-close> 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 3e4478b989cfb0b721f03d5f8213238a01aab6e4..092a1165267012cecfca3c12645b65d0316a2ce0 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 @@ -18,7 +18,7 @@ export class DomainDetailsDialogComponent { static readonly NEW_MODE = 'New Domain'; static readonly EDIT_MODE = 'Domain Edit'; - readonly dnsDomainPattern = '^(?!(\\d|-|_)+)[a-zA-Z0-9-]{1,63}$'; + readonly dnsDomainPattern = '^(?!(\\d|-|_).+)[a-zA-Z0-9-]{1,63}$'; readonly domainCodePattern = '^[a-zA-Z]{1,255}$'; editMode: boolean; @@ -26,8 +26,6 @@ export class DomainDetailsDialogComponent { current: DomainRo & { confirmation?: string }; domainForm: FormGroup; - userSwitch: boolean; - certificateSwitch: boolean; domain; @@ -45,12 +43,11 @@ export class DomainDetailsDialogComponent { } : { domainCode: '', - email: '', - password: '', - confirmation: '', - role: '', + smlSubdomain: '', + smlSmpId: '', + smlClientKeyAlias: '', + signatureKeyAlias: '', status: SearchTableEntityStatus.NEW, - certificate: {}, }; this.domainForm = fb.group({ @@ -58,7 +55,7 @@ export class DomainDetailsDialogComponent { 'domainCode': new FormControl({value: this.current.domainCode, disabled: this.editMode}, [Validators.pattern(this.domainCodePattern)]), 'smlSubdomain': new FormControl({value: this.current.smlSubdomain, disabled: this.editMode}, [Validators.pattern(this.dnsDomainPattern)]), 'smlSmpId': new FormControl({value: this.current.smlSmpId}, [Validators.required, Validators.pattern(this.dnsDomainPattern)]), - 'smlClientKeyAlias': new FormControl({value: this.current.signatureKeyAlias}, null), + 'smlClientKeyAlias': new FormControl({value: this.current.smlClientKeyAlias}, null), 'signatureKeyAlias': new FormControl({value: this.current.signatureKeyAlias}, null), diff --git a/smp-angular/src/app/domain/domain.component.html b/smp-angular/src/app/domain/domain.component.html index 89d894c64bde8d4481e31de00c9c4354cb313717..6dd642be9cca558c1b08a2326210e490ff17de63 100644 --- a/smp-angular/src/app/domain/domain.component.html +++ b/smp-angular/src/app/domain/domain.component.html @@ -3,7 +3,7 @@ page_id= 'domain_id' title= 'Domains' [columnPicker] = "columnPicker" - url="rest/domain" + [url]="baseUrl" [additionalToolButtons]="additionalToolButtons" [searchTableController]="domainController" [showSearchPanel]="false" diff --git a/smp-angular/src/app/domain/domain.component.ts b/smp-angular/src/app/domain/domain.component.ts index 49167818efb8aadb164b74d7bcd2a0fd9c9d4550..73e617c23fcedfd0c9356c4d0a4c4cd73c3e4920 100644 --- a/smp-angular/src/app/domain/domain.component.ts +++ b/smp-angular/src/app/domain/domain.component.ts @@ -5,6 +5,7 @@ import {MatDialog, MatDialogRef} from '@angular/material'; import {AlertService} from '../alert/alert.service'; import {DomainController} from './domain-controller'; import {HttpClient} from '@angular/common/http'; +import {SmpConstants} from "../smp.constants"; @Component({ moduleId: module.id, @@ -17,10 +18,12 @@ export class DomainComponent implements OnInit { @ViewChild('rowExtensionAction') rowExtensionAction: TemplateRef<any>; @ViewChild('rowActions') rowActions: TemplateRef<any>; + baseUrl = SmpConstants.REST_DOMAIN; columnPicker: ColumnPicker = new ColumnPicker(); domainController: DomainController; filter: any = {}; + constructor(protected http: HttpClient, protected alertService: AlertService, public dialog: MatDialog) { } diff --git a/smp-angular/src/app/service-group-edit/service-group-details-dialog/service-group-details-dialog.component.css b/smp-angular/src/app/service-group-edit/service-group-details-dialog/service-group-details-dialog.component.css new file mode 100644 index 0000000000000000000000000000000000000000..66c5204a6d4d3b0cee28761c51cfce5310bdb950 --- /dev/null +++ b/smp-angular/src/app/service-group-edit/service-group-details-dialog/service-group-details-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-details-dialog/service-group-details-dialog.component.html b/smp-angular/src/app/service-group-edit/service-group-details-dialog/service-group-details-dialog.component.html index 0810d76791c2c22bdd5c1ffda86908680c30c538..d29e25dc3a8d47cb4659606b70451d800c9cc69b 100644 --- a/smp-angular/src/app/service-group-edit/service-group-details-dialog/service-group-details-dialog.component.html +++ b/smp-angular/src/app/service-group-edit/service-group-details-dialog/service-group-details-dialog.component.html @@ -1,38 +1,83 @@ -<h2 mat-dialog-title>ServiceGroup details</h2> -<mat-dialog-content style="height:260px;width:650px"> - <mat-card> - <mat-card-content> - <mat-form-field style="width:100%"> - <input matInput placeholder="Participant Id" value="{{servicegroup.serviceGroupROId.participantId}}" readonly/> - </mat-form-field> +<h2 mat-dialog-title>{{formTitle}}</h2> - <mat-form-field style="width:100%"> - <input matInput placeholder="Participant schema" value="{{servicegroup.serviceGroupROId.participantSchema}}" readonly/> - </mat-form-field> +<mat-dialog-content style="height:600px;width:1100px"> + <div fxLayout="row"> + <div fxLayout="column"> + <mat-card fxFlex="200px"> + <mat-card-title>Identifier</mat-card-title> + <mat-card-content> + <mat-form-field style="width:100%"> + <input matInput placeholder="Participant identifier" name="participantIdentifier" + value="{{current.participantIdentifier}}" id="participantIdentifier_id" + (blur)="updateParticipantIdentifier($event)" + [formControl]="dialogForm.controls['participantIdentifier']" maxlength="255" required> + <div + *ngIf="(!editMode && dialogForm.controls['participantIdentifier'].touched || editMode) && dialogForm.controls['participantIdentifier'].hasError('required')" + style="color:red; font-size: 70%"> + Participant identifier must not be empty. + </div> + </mat-form-field> - <mat-form-field style="width:100%"> - <input matInput placeholder="Domain" value="{{servicegroup.domain}}" readonly/> - </mat-form-field> - - </mat-card-content> - </mat-card> + <mat-form-field style="width:100%"> + <input matInput placeholder="Participant scheme" name="participantScheme" + value="{{current.participantScheme}}" id="participantScheme_id" + (blur)="updateParticipantScheme($event)" [formControl]="dialogForm.controls['participantScheme']" + maxlength="255" required> + <div + *ngIf="(!editMode && dialogForm.controls['participantScheme'].touched || editMode) && dialogForm.controls['participantScheme'].hasError('required')" + style="color:red; font-size: 70%"> + Participant scheme must not be empty. + </div> + </mat-form-field> + </mat-card-content> + </mat-card> + <mat-card> + <mat-card-title>Owners</mat-card-title> + <mat-card-content> + <p>Selected user count: {{usersSelected.selectedOptions.selected.length}}</p> + <mat-selection-list #usersSelected [compareWith]="compareUserById" + (selectionChange)="userListChanged(usersSelected.selectedOptions.selected,$event)" + [formControl]="formControlUsers" + style="height: 200px; overflow-y: scroll; overflow-x: auto;"> + <mat-list-option *ngFor="let user of userlist" [value]='user'> + {{user.id}} - {{user.username?user.username:user.id}} + </mat-list-option> + </mat-selection-list> + <div + *ngIf="(!editMode && dialogForm.controls['users'].touched || editMode) && dialogForm.controls['users'].hasError('minCountOwners')" + style="color:red; font-size: 70%"> + At least one user (owner) must be selected! + </div> + </mat-card-content> + </mat-card> + </div> + <mat-card fxFlex="60"> + <mat-card-title>Extension</mat-card-title> + <mat-card-content> + <p> + Extension is automatically wrapped to root element to form vaild XML! No ExtensionWrapper element is needed. + </p> + <mat-form-field> + <textarea id="extensionTextArea" resizeable="false" placeholder="Extension" name="extension" ></textarea> + </mat-form-field> + </mat-card-content> + </mat-card> + </div> </mat-dialog-content> <mat-dialog-actions> - <div class="group-action-button" > - <button id="ServiceGroupsSaveButton" mat-raised-button color="primary" (click)="dialogRef.close({})" - style="margin-top:10px"> - <mat-icon>save</mat-icon> - <span>Save</span> - </button> + <div class="group-action-button"> + <button mat-raised-button color="primary" [mat-dialog-close]="true" (click)="submitForm()" + [disabled]="!dialogForm.valid"> + <mat-icon>check_circle</mat-icon> + <span>OK</span> + </button> - - <button id="ServiceGroupsCloseButton" mat-raised-button color="primary" (click)="dialogRef.close({})" - style="margin-top:10px"> - <mat-icon>close</mat-icon> - <span>Close</span> - </button> + <button mat-raised-button color="primary" mat-dialog-close> + <mat-icon>cancel</mat-icon> + <span>Cancel</span> + </button> </div> </mat-dialog-actions> - +<div style="text-align: right; font-size: 70%">* required fields</div> diff --git a/smp-angular/src/app/service-group-edit/service-group-details-dialog/service-group-details-dialog.component.ts b/smp-angular/src/app/service-group-edit/service-group-details-dialog/service-group-details-dialog.component.ts index c29da914f969e189522816a2bde4c3a849f33b5f..1af2b0bd13e8fb486dbce7a374c0c27dc9e515dd 100644 --- a/smp-angular/src/app/service-group-edit/service-group-details-dialog/service-group-details-dialog.component.ts +++ b/smp-angular/src/app/service-group-edit/service-group-details-dialog/service-group-details-dialog.component.ts @@ -1,16 +1,160 @@ -import {Component} from '@angular/core'; -import {MatDialogRef} from '@angular/material'; +import {Component, Inject} 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"; @Component({ selector: 'app-messagelog-details', - templateUrl: './service-group-details-dialog.component.html' + templateUrl: './service-group-details-dialog.component.html', + styleUrls: ['./service-group-details-dialog.component.css'] }) export class ServiceGroupDetailsDialogComponent { - servicegroup; - dateFormat: String = 'yyyy-MM-dd HH:mm:ssZ'; + static readonly NEW_MODE = 'New ServiceGroup'; + static readonly EDIT_MODE = 'ServiceGroup Edit'; + + + userObserver: Observable< SearchTableResult> ; + domainObserver: Observable< SearchTableResult> ; + userlist: Array<UserRo> = []; + editMode: boolean; + formTitle: string; + current: ServiceGroupEditRo & { confirmation?: string }; + + dialogForm: FormGroup; + dialogFormBuilder: FormBuilder; + formControlUsers: FormControl; + /* + selectedDomain: DomainRo; + domainList: Array<any>; +*/ + + minCountOwners(min: number) { + return (c: AbstractControl): {[key: string]: any} => { + if (c.value.length >= min) + return null; + + return { 'minCountOwners': {valid: false }}; + } + } + + constructor(protected http: HttpClient, public dialogRef: MatDialogRef<ServiceGroupDetailsDialogComponent>, + private alertService: AlertService, + @Inject(MAT_DIALOG_DATA) public data: any, + private fb: FormBuilder) { + // init user list + + this.userObserver = this.http.get<SearchTableResult>(SmpConstants.REST_USER); + this.userObserver.subscribe((users: SearchTableResult) => { + this.userlist = new Array(users.serviceEntities.length) + .map((v, index) => users.serviceEntities[index] as UserRo); + + this.userlist = users.serviceEntities.map(serviceEntity => { + return {...<UserRo>serviceEntity} + }); + this.updateData(); + }); + /* + // init domains + this.domainObserver = this.http.get<SearchTableResult>(SmpConstants.REST_DOMAIN); + this.domainObserver.subscribe((domains: SearchTableResult) => { + this.domainList = new Array(domains.serviceEntities.length) + .map((v, index) => {domains.serviceEntities[index] as DomainRo; + if ( this.editMode && v.domainCode ===data.row.domainCode) { + this.selectedDomain = v as DomainRo; + } + }); + + this.domainList = domains.serviceEntities.map(serviceEntity => { + return {...serviceEntity} + }); + }); +*/ + this.dialogFormBuilder = fb; + this.editMode = data.edit; + this.formTitle = this.editMode ? ServiceGroupDetailsDialogComponent.EDIT_MODE: ServiceGroupDetailsDialogComponent.NEW_MODE; + this.current = this.editMode + ? { + ...data.row, + } + : { + id: null, + participantIdentifier: '', + participantScheme: '', + serviceMetadata:[], + users:[], + domainCode:'', + status: SearchTableEntityStatus.NEW, + }; + + this.dialogForm = this.dialogFormBuilder.group({ + + 'participantIdentifier': new FormControl({value: this.current.participantIdentifier, disabled: this.editMode}, this.editMode ? Validators.required : null), + 'participantScheme': new FormControl({value: this.current.participantScheme, disabled: this.editMode}, this.editMode ? Validators.required : null), + 'domainCode': new FormControl({value: this.current.domainCode},this.editMode ? Validators.required : null), + + //'users': [new FormControl({value: this.current.users}, [ this.minCountOwners(1)] )], + }); + - constructor(public dialogRef: MatDialogRef<ServiceGroupDetailsDialogComponent>) { } + updateData(){ + this.formControlUsers = new FormControl(this.current.users); + this.formControlUsers.setValidators( [ this.minCountOwners(1)]); + + this.dialogForm.addControl("users",this.formControlUsers ); + + } + + + submitForm() { + this.checkValidity(this.dialogForm) + this.dialogRef.close(true); + } + + checkValidity(g: FormGroup) { + Object.keys(g.controls).forEach(key => { + g.get(key).markAsDirty(); + }); + Object.keys(g.controls).forEach(key => { + g.get(key).markAsTouched(); + }); + //!!! updateValueAndValidity - else some filed did no update current / on blur never happened + Object.keys(g.controls).forEach(key => { + g.get(key).updateValueAndValidity(); + }); + } + + + updateParticipantIdentifier(event) { + this.current.participantIdentifier = event.target.value; + } + updateParticipantScheme(event) { + this.current.participantScheme = event.target.value; + } + + userListChanged(usersSelected, event){ + this.current.users = []; + for(let usr of usersSelected) { + this.current.users.push(usr.value); + } + } + compareUserById(item1, item2): boolean{ + return item1.id=== item2.id; + } + + isSelected(id): boolean { + return !!this.current.users.find(user => user.id===id); + } } diff --git a/smp-angular/src/app/service-group-edit/service-group-controller.ts b/smp-angular/src/app/service-group-edit/service-group-edit-controller.ts similarity index 56% rename from smp-angular/src/app/service-group-edit/service-group-controller.ts rename to smp-angular/src/app/service-group-edit/service-group-edit-controller.ts index c3c790a216e8d71563239c89260de544b44a959b..4942ebc87fedcc6d052b697d6e224f279252e509 100644 --- a/smp-angular/src/app/service-group-edit/service-group-controller.ts +++ b/smp-angular/src/app/service-group-edit/service-group-edit-controller.ts @@ -1,24 +1,23 @@ import {SearchTableController} from '../common/search-table/search-table-controller'; import {MatDialog, MatDialogConfig, MatDialogRef} from '@angular/material'; import {ServiceGroupDetailsDialogComponent} from './service-group-details-dialog/service-group-details-dialog.component'; -import {AlertService} from '../alert/alert.service'; import {ServiceGroupExtensionDialogComponent} from './service-group-extension-dialog/service-group-extension-dialog.component'; -import {ServiceGroupMetadataListDialogComponent} from './service-group-metadata-list-dialog/service-group-metadata-list-dialog.component'; -import {UserDetailsDialogComponent} from '../user/user-details-dialog/user-details-dialog.component'; -import {SearchTableEntity} from '../common/search-table/search-table-entity.model'; -import {ServiceGroupRo} from './service-group-ro.model'; +import {ServiceGroupEditRo} from './service-group-edit-ro.model'; import {SearchTableEntityStatus} from '../common/search-table/search-table-entity-status.model'; +import {ServiceMetadataEditRo} from "./service-metadata-edit-ro.model"; +import {DomainDetailsDialogComponent} from "../domain/domain-details-dialog/domain-details-dialog.component"; -export class ServiceGroupController implements SearchTableController { +export class ServiceGroupEditController implements SearchTableController { constructor(public dialog: MatDialog) { } - public showDetails(row: any) { - let dialogRef: MatDialogRef<ServiceGroupDetailsDialogComponent> = this.newDialog(); - dialogRef.componentInstance.servicegroup = row; + public showDetails( row: any, config?: MatDialogConfig,) { + let dialogRef: MatDialogRef<ServiceGroupDetailsDialogComponent> + = this.dialog.open(ServiceGroupDetailsDialogComponent); dialogRef.afterClosed().subscribe(result => { //Todo: }); + } public showExtension(row: any) { @@ -30,11 +29,7 @@ export class ServiceGroupController implements SearchTableController { } public showMetadataList(row: any) { - let dialogRef: MatDialogRef<ServiceGroupMetadataListDialogComponent> = this.dialog.open(ServiceGroupMetadataListDialogComponent); - // dialogRef.componentInstance.servicegroup = row; - dialogRef.afterClosed().subscribe(result => { - //Todo: - }); + } @@ -46,13 +41,16 @@ export class ServiceGroupController implements SearchTableController { return this.dialog.open(ServiceGroupDetailsDialogComponent, config); } - public newRow(): ServiceGroupRo { + public newRow(): ServiceGroupEditRo { return { - domain: '', - serviceGroupROId: { - participantId: '', - participantSchema: '' - }, + id: null, + index: null, + participantIdentifier:'', + participantScheme: '', + domainCode: '', + smlSubdomain: '', + serviceMetadata:[], + users: [], status: SearchTableEntityStatus.NEW }; } diff --git a/smp-angular/src/app/service-group-edit/service-group-edit-ro.model.ts b/smp-angular/src/app/service-group-edit/service-group-edit-ro.model.ts new file mode 100644 index 0000000000000000000000000000000000000000..9b626bc7ee710dd378bce97b5eb42324f73249e5 --- /dev/null +++ b/smp-angular/src/app/service-group-edit/service-group-edit-ro.model.ts @@ -0,0 +1,13 @@ +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"; + +export interface ServiceGroupEditRo extends SearchTableEntity { + id: number; + participantIdentifier: string; + participantScheme: string; + domainCode:'', + smlSubdomain:'', + serviceMetadata: Array<ServiceMetadataEditRo>; + users: Array<UserRo>; +} diff --git a/smp-angular/src/app/service-group-edit/service-group.component.css b/smp-angular/src/app/service-group-edit/service-group-edit.component.css similarity index 100% rename from smp-angular/src/app/service-group-edit/service-group.component.css rename to smp-angular/src/app/service-group-edit/service-group-edit.component.css diff --git a/smp-angular/src/app/service-group-edit/service-group-edit.component.html b/smp-angular/src/app/service-group-edit/service-group-edit.component.html new file mode 100644 index 0000000000000000000000000000000000000000..379697f11a2fda226b0e9ade4736e19d8c85b6fa --- /dev/null +++ b/smp-angular/src/app/service-group-edit/service-group-edit.component.html @@ -0,0 +1,112 @@ +<smp-search-table + page_id='edit_id' + title='Edit' + [columnPicker]="columnPicker" + [url]="baseUrl" + [additionalToolButtons]="additionalToolButtons" + [searchPanel]="searchPanel" + [filter]="filter" + [searchTableController]="serviceGroupEditController" + [tableRowDetailContainer]="tableRowDetailContainer" +> + + <ng-template #rowMetadataAction let-row="row" let-value="value" ngx-datatable-cell-template> + + <button mat-button color="primary" + (click)="metadataRowButtonAction(row)" id="metadataRowButtonAction{{row.$$index}}_id" tooltip="Metadata"> + <mat-icon>view_list</mat-icon> + <span>Metadata</span> + </button> + + </ng-template> + + <ng-template #rowExtensionAction let-row="row" let-value="value" ngx-datatable-cell-template> + + <button mat-button color="primary" + (click)="extensionRowButtonAction(row)" id="extensionRowButtonAction{{row.$$index}}_id" tooltip="Extension"> + <mat-icon>code</mat-icon> + <span>Extension</span> + </button> + + </ng-template> + + <ng-template #searchPanel> + <mat-form-field> + <input matInput placeholder="Participant Id" name="messageId" [(ngModel)]="filter.messageId" + #messageId="ngModel" id="messageid_id"> + </mat-form-field> + <mat-form-field> + <input matInput placeholder="Participant schema" name="patricipantSchema" [(ngModel)]="filter.messageId" + #messageId="ngModel" id="participanschema_id"> + </mat-form-field> + <mat-select placeholder="Domain " [(ngModel)]="filter.messageStatus" name="messageStatus" + id="messagestatus_id"> + <mat-option [value]="''"> + </mat-option> + <mat-option *ngFor="let mstatus of msgStatus" [value]="mstatus"> + {{mstatus}} + </mat-option> + </mat-select> + </ng-template> + + + <ng-template #additionalToolButtons> + <div style="background-color: #03A9F4; display: inline-block; width: 3px; height: 100%; margin-right: 8px"> + </div> + <button mat-raised-button color="primary" + id="metadatabutton_id"> + <mat-icon>view_list</mat-icon> + <span>Metadata</span> + </button> + + <button mat-raised-button color="primary" + id="extensionbutton_id"> + <mat-icon>code</mat-icon> + <span>Extension</span> + </button> + </ng-template> + + <ng-template #tableRowDetailContainer let-row="row"> + + <div *ngIf="row.serviceMetadata.length===0" style="padding-left:20px;"> + No service metadata + </div> + <div *ngIf="row.serviceMetadata.length !== 0" > + <ngx-datatable + class='material striped' + style="width: 80%" + [loadingIndicator]="loading" + [rows]='row.serviceMetadata' + [columnMode]='"force"' + [headerHeight]='50' + [footerHeight]='50' + [rowHeight]='"auto"'> + <ngx-datatable-column prop="domainCode" name="Domain" maxWidth="250" ></ngx-datatable-column> + <ngx-datatable-column prop="documentIdentifierScheme" name="Document identifier scheme" maxWidth="350" ></ngx-datatable-column> + <ngx-datatable-column prop="documentIdentifier" name="Document identifier" maxWidth="250" ></ngx-datatable-column> + <ngx-datatable-column [cellTemplate]="rowMetadataSMPUrlLinkAction" name="URL" maxWidth="250" ></ngx-datatable-column> + <ngx-datatable-column [cellTemplate]="rowMetadataActions" name="Actions" maxWidth="150" ></ngx-datatable-column> + + <ng-template #rowMetadataSMPUrlLinkAction let-rowSmd="row" ngx-datatable-cell-template> + <a target="_blank" + href="{{contextPath}}{{row.participantScheme}}::{{row.participantIdentifier}}/services/{{rowSmd.documentIdentifierScheme}}::{{rowSmd.documentIdentifier}}" >Open URL</a> + </ng-template> + </ngx-datatable> + + <ng-template #rowMetadataActions let-rowSmd="row" ngx-datatable-cell-template> + <div> + <button mat-icon-button color="primary" [disabled]="rowSmd.deleted || loading" + (click)="onEditMetadataRow(rowSmd)" tooltip="Edit"> + <mat-icon>edit</mat-icon> + </button> + <button mat-icon-button color="primary" [disabled]="rowSmd.deleted || loading" + (click)="onDeleteMetadataRowActionClicked(rowSmd)" tooltip="Delete"> + <mat-icon>delete</mat-icon> + </button> + </div> + </ng-template> + + </div> + </ng-template> + +</smp-search-table> diff --git a/smp-angular/src/app/service-group-edit/service-group-edit.component.ts b/smp-angular/src/app/service-group-edit/service-group-edit.component.ts new file mode 100644 index 0000000000000000000000000000000000000000..64e4c6709a1d28f36ab1542c508c476f41234f8a --- /dev/null +++ b/smp-angular/src/app/service-group-edit/service-group-edit.component.ts @@ -0,0 +1,114 @@ +import {Component, OnInit, TemplateRef, ViewChild} from '@angular/core'; +import {ColumnPicker} from '../common/column-picker/column-picker.model'; +import {MatDialog, MatDialogRef} from '@angular/material'; +import {AlertService} from '../alert/alert.service'; +import {ServiceGroupEditController} from './service-group-edit-controller'; +import {HttpClient} from '@angular/common/http'; +import {ServiceGroupDetailsDialogComponent} from "./service-group-details-dialog/service-group-details-dialog.component"; +import {SmpConstants} from "../smp.constants"; +import {Observable} from "rxjs/internal/Observable"; +import {UserRo} from "../user/user-ro.model"; +import {SearchTableResult} from "../common/search-table/search-table-result.model"; + +@Component({ + moduleId: module.id, + templateUrl:'./service-group-edit.component.html', + styleUrls: ['./service-group-edit.component.css'] +}) +export class ServiceGroupEditComponent implements OnInit { + + @ViewChild('rowMetadataAction') rowMetadataAction: TemplateRef<any> + @ViewChild('rowExtensionAction') rowExtensionAction: TemplateRef<any> + @ViewChild('rowActions') rowActions: TemplateRef<any>; + @ViewChild('rowSMPUrlLinkAction') rowSMPUrlLinkAction: TemplateRef<any> + + columnPicker: ColumnPicker = new ColumnPicker(); + serviceGroupEditController: ServiceGroupEditController; + filter: any = {}; + baseUrl: string = SmpConstants.REST_EDIT; + + userObserver: Observable< SearchTableResult> ; + domainObserver: Observable< SearchTableResult> ; + userlist: Array<UserRo> = []; + + constructor(protected http: HttpClient, protected alertService: AlertService, public dialog: MatDialog) { + + this.userObserver = this.http.get<SearchTableResult>(SmpConstants.REST_USER); + this.userObserver.subscribe((users: SearchTableResult) => { + this.userlist = new Array(users.serviceEntities.length) + .map((v, index) => users.serviceEntities[index] as UserRo); + + this.userlist = users.serviceEntities.map(serviceEntity => { + return {...<UserRo>serviceEntity} + }); + }); + } + + ngOnInit() { + this.serviceGroupEditController = new ServiceGroupEditController(this.dialog); + + this.columnPicker.allColumns = [ + { + name: 'Metadata size', + prop: 'serviceMetadata.length', + width: 80, + maxWidth: 120 + }, + { + name: 'Owners size', + prop: 'users.length', + width: 80, + maxWidth: 120 + }, + { + name: 'Participant scheme', + prop: 'participantScheme', + width: 250, + maxWidth: 300 + }, + { + name: 'Participant identifier', + prop: 'participantIdentifier', + }, + { + cellTemplate: this.rowExtensionAction, + name: 'Extension', + width: 80, + maxwidth: 120, + sortable: false + }, + { + cellTemplate: this.rowSMPUrlLinkAction, + name: 'OASIS ServiceGroup URL', + width: 150, + maxWidth: 250, + sortable: false + }, + + ]; + + this.columnPicker.selectedColumns = this.columnPicker.allColumns.filter(col => { + return ["Metadata size", 'Owners size', "Participant scheme", "Participant identifier", "Extension", "OASIS ServiceGroup URL"].indexOf(col.name) != -1 + }); + } + + extensionRowButtonAction(row: any){ + this.serviceGroupEditController.showExtension(row); + } + + metadataRowButtonAction(row: any){ + this.serviceGroupEditController.showMetadataList(row); + } + + details(row: any) { + this.serviceGroupEditController.showDetails(row); + + } + + onEditMetadataRow(row:any){ + alert("edit" + row); + } + onDeleteMetadataRowActionClicked(row:any){ + alert("delete" + row); + } +} diff --git a/smp-angular/src/app/service-group-edit/service-group-metadata-list-dialog/service-group-metadata-list-dialog.component.css b/smp-angular/src/app/service-group-edit/service-group-metadata-list-dialog/service-group-metadata-list-dialog.component.css deleted file mode 100644 index 7389196e506b0fce5076d0efd30cd2795f7454bd..0000000000000000000000000000000000000000 --- a/smp-angular/src/app/service-group-edit/service-group-metadata-list-dialog/service-group-metadata-list-dialog.component.css +++ /dev/null @@ -1,41 +0,0 @@ -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/service-group-edit/service-group-metadata-list-dialog/service-group-metadata-list-dialog.component.html b/smp-angular/src/app/service-group-edit/service-group-metadata-list-dialog/service-group-metadata-list-dialog.component.html deleted file mode 100644 index 3527a80f2e88b3c6540f5f79117a493b4dc03c0f..0000000000000000000000000000000000000000 --- a/smp-angular/src/app/service-group-edit/service-group-metadata-list-dialog/service-group-metadata-list-dialog.component.html +++ /dev/null @@ -1,75 +0,0 @@ -<h2 mat-dialog-title>ServiceGroup Metadata list</h2> -<mat-dialog-content style="height:460px;width:950px"> - - - <ngx-datatable - id="metadataTable" - class="material striped" - style="height: 400px;width: 920px;" - [rows]="rows" - [columns]="columnPicker.selectedColumns" - [columnMode]="'force'" - [headerHeight]="50" - [footerHeight]="50" - [rowHeight]="'auto'" - [scrollbarH]="true" - [externalPaging]="true" - [externalSorting]="true" - [loadingIndicator]="loading" - [count]="count" - [offset]="offset" - [limit]="rowLimiter.pageSize" - [sorts]="[{prop: 'received', dir: 'desc'}]" - - [selected]="selected" - [selectionType]="'multi'" - > - </ngx-datatable> - - - <ng-template #rowActions let-row="row" let-value="value" ngx-datatable-cell-template> - - <button mat-icon-button color="primary" - (click)="editRowButtonAction(row)" id="editButtonRow{{row.$$index}}_id" tooltip="Edit"> - <mat-icon>edit</mat-icon> - </button> - - - <button mat-icon-button color="primary" (click)="deleteRowButtonAction(row)" - id="deleteButtonRow{{row.$$index}}_id" tooltip="Delete"> - <mat-icon>delete</mat-icon> - </button> - </ng-template> - -</mat-dialog-content> - -<mat-dialog-actions> - <div class="group-action-button"> - - <button mat-raised-button color="primary" (click)="newButtonAction()" - id="add_id"> - <mat-icon>add</mat-icon> - <span>New</span> - </button> - - <button mat-raised-button color="primary" [disabled]="!isRowSelected()" (click)="editButtonAction()" - id="edit_id"> - <mat-icon>edit</mat-icon> - <span>Edit</span> - </button> - - <button mat-raised-button color="primary" [disabled]="!isRowSelected()" (click)="deleteButtonAction()" - id="resendbutton_id"> - <mat-icon>delete</mat-icon> - <span>Delete</span> - </button> - <div style="background-color: #03A9F4; display: inline-block; width: 3px; height: 20px; margin-right: 8px"> - </div> - <button id="ServiceGroupMetadataListCloseButton" mat-raised-button color="primary" (click)="dialogRef.close({})" - style="margin-top:10px"> - <mat-icon>close</mat-icon> - <span>Close</span> - </button> - </div> -</mat-dialog-actions> - diff --git a/smp-angular/src/app/service-group-edit/service-group-metadata-list-dialog/service-group-metadata-list-dialog.component.spec.ts b/smp-angular/src/app/service-group-edit/service-group-metadata-list-dialog/service-group-metadata-list-dialog.component.spec.ts deleted file mode 100644 index d01831eaf44f03a3efa49e631290cfd493e2821b..0000000000000000000000000000000000000000 --- a/smp-angular/src/app/service-group-edit/service-group-metadata-list-dialog/service-group-metadata-list-dialog.component.spec.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { async, ComponentFixture, TestBed } from '@angular/core/testing'; - -import { ServiceGroupMetadataListDialogComponent } from './service-group-metadata-list-dialog.component'; - -describe('ServiceGroupMetadataListDialogComponent', () => { - let component: ServiceGroupMetadataListDialogComponent; - let fixture: ComponentFixture<ServiceGroupMetadataListDialogComponent>; - - beforeEach(async(() => { - TestBed.configureTestingModule({ - declarations: [ ServiceGroupMetadataListDialogComponent ] - }) - .compileComponents(); - })); - - beforeEach(() => { - fixture = TestBed.createComponent(ServiceGroupMetadataListDialogComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); -}); diff --git a/smp-angular/src/app/service-group-edit/service-group-metadata-list-dialog/service-group-metadata-list-dialog.component.ts b/smp-angular/src/app/service-group-edit/service-group-metadata-list-dialog/service-group-metadata-list-dialog.component.ts deleted file mode 100644 index f4e0d3933a600b5cc1190a04531ec9fff97a358d..0000000000000000000000000000000000000000 --- a/smp-angular/src/app/service-group-edit/service-group-metadata-list-dialog/service-group-metadata-list-dialog.component.ts +++ /dev/null @@ -1,100 +0,0 @@ -import {Component, EventEmitter, OnInit, TemplateRef, ViewChild} from '@angular/core'; -import {MatDialog, MatDialogRef} from '@angular/material'; -import {ColumnPicker} from '../../common/column-picker/column-picker.model'; -import {ServiceGroupController} from '../service-group-controller'; -import {RowLimiter} from '../../common/row-limiter/row-limiter.model'; -import {ServiceGroupExtensionDialogComponent} from '../service-group-extension-dialog/service-group-extension-dialog.component'; -import {ServiceGroupMetadataDialogComponent} from '../service-group-metadata-dialog/service-group-metadata-dialog.component'; - -@Component({ - selector: 'app-messagelog-dialog', - templateUrl: './service-group-metadata-list-dialog.component.html', - styleUrls: ['./service-group-metadata-list-dialog.component.css'] -}) -export class ServiceGroupMetadataListDialogComponent implements OnInit { - - @ViewChild('rowActions') rowActions: TemplateRef<any>; - - columnPicker: ColumnPicker = new ColumnPicker(); - columnActions:any; - rowLimiter: RowLimiter = new RowLimiter(); - selected = []; - - filter: any = {}; - loading: boolean = false; - rows = []; - count: number = 0; - offset: number = 0; - //default value - orderBy: string = null; - //default value - asc: boolean = false; - - messageResent = new EventEmitter(false); - - constructor(public dialogRef: MatDialogRef<ServiceGroupMetadataListDialogComponent>, public dialog: MatDialog) { - } - - ngOnInit() { - this.columnPicker.allColumns = [ - { - name: 'Document Id', - prop: 'documentId', - width: 200, - }, - { - name: 'Document schema', - prop: 'documentSchema', - width: 200, - }, { - cellTemplate: this.rowActions, - name: 'Actions', - width: 60, - sortable: false - } - ]; - - this.columnPicker.selectedColumns = this.columnPicker.allColumns.filter(col => { - return ["Document Id", "Document schema", "Actions"].indexOf(col.name) != -1 - }); - - this.rows = [{ - documentId:"urn:be:ncpb", - documentSchema:"ehealth-docid-qns", - }, - { - documentId:"urn:pl:ncpb", - documentSchema:"ehealth-docid-qns", - }, - { - documentId:"urn:ge:ncpb", - documentSchema:"ehealth-docid-qns", - }, - { - documentId:"urn:si:ncpb", - documentSchema:"ehealth-docid-qns", - }]; - this.count=3; - this.offset=0; - this.loading = false; - } - - isRowSelected() { - if (this.selected) - return true; - - return false; - } - - newButtonAction (){this.details()} - editButtonAction (){this.details()} - deleteButtonAction (){} - - details() { - let dialogRef: MatDialogRef<ServiceGroupMetadataDialogComponent> = this.dialog.open(ServiceGroupMetadataDialogComponent); - //dialogRef.componentInstance.servicegroup = row; - dialogRef.afterClosed().subscribe(result => { - //Todo: - }); - } -} diff --git a/smp-angular/src/app/service-group-edit/service-group-ro-id.model.ts b/smp-angular/src/app/service-group-edit/service-group-ro-id.model.ts deleted file mode 100644 index 003f2db4347f667f2b4fd755a873a6768a340cd8..0000000000000000000000000000000000000000 --- a/smp-angular/src/app/service-group-edit/service-group-ro-id.model.ts +++ /dev/null @@ -1,4 +0,0 @@ -export interface ServiceGroupROId { - participantId: string; - participantSchema: string; -} diff --git a/smp-angular/src/app/service-group-edit/service-group-ro.model.ts b/smp-angular/src/app/service-group-edit/service-group-ro.model.ts deleted file mode 100644 index 8b29fd39670215a747894b21745d288d5db961d0..0000000000000000000000000000000000000000 --- a/smp-angular/src/app/service-group-edit/service-group-ro.model.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { ServiceGroupROId } from './service-group-ro-id.model'; -import {SearchTableEntity} from '../common/search-table/search-table-entity.model'; - -export interface ServiceGroupRo extends SearchTableEntity { - serviceGroupROId: ServiceGroupROId; - domain: string; -} diff --git a/smp-angular/src/app/service-group-edit/service-group.component.html b/smp-angular/src/app/service-group-edit/service-group.component.html deleted file mode 100644 index 0bdf69b5bfde596a446e45656788df3b8e5b5049..0000000000000000000000000000000000000000 --- a/smp-angular/src/app/service-group-edit/service-group.component.html +++ /dev/null @@ -1,73 +0,0 @@ -<smp-search-table - page_id='participants_id' - title='Participants' - [columnPicker]="columnPicker" - url="rest/servicegroup" - [additionalToolButtons]="additionalToolButtons" - [searchPanel]="searchPanel" - [filter]="filter" - [searchTableController]="serviceGroupController" -> - - <ng-template #rowMetadataAction let-row="row" let-value="value" ngx-datatable-cell-template> - - <button mat-button color="primary" - (click)="metadataRowButtonAction(row)" id="metadataRowButtonAction{{row.$$index}}_id" tooltip="Metadata"> - <mat-icon>view_list</mat-icon> - <span>Metadata</span> - </button> - - </ng-template> - - <ng-template #rowExtensionAction let-row="row" let-value="value" ngx-datatable-cell-template> - - <button mat-button color="primary" - (click)="extensionRowButtonAction(row)" id="extensionRowButtonAction{{row.$$index}}_id" tooltip="Extension"> - <mat-icon>code</mat-icon> - <span>Extension</span> - </button> - - </ng-template> - - <ng-template #searchPanel> - <mat-form-field> - <input matInput placeholder="Participant Id" name="messageId" [(ngModel)]="filter.messageId" - #messageId="ngModel" id="messageid_id"> - </mat-form-field> - <mat-form-field> - <input matInput placeholder="Participant schema" name="patricipantSchema" [(ngModel)]="filter.messageId" - #messageId="ngModel" id="participanschema_id"> - </mat-form-field> - <mat-select placeholder="Domain " [(ngModel)]="filter.messageStatus" name="messageStatus" - id="messagestatus_id"> - <mat-option [value]="''"> - </mat-option> - <mat-option *ngFor="let mstatus of msgStatus" [value]="mstatus"> - {{mstatus}} - </mat-option> - </mat-select> - </ng-template> - - - <ng-template #additionalToolButtons> - - <div style="background-color: #03A9F4; display: inline-block; width: 3px; height: 100%; margin-right: 8px"> - </div> - - - <button mat-raised-button color="primary" - id="metadatabutton_id"> - <mat-icon>view_list</mat-icon> - <span>Metadata</span> - </button> - - <button mat-raised-button color="primary" - id="extensionbutton_id"> - <mat-icon>code</mat-icon> - <span>Extension</span> - </button> - - - </ng-template> - -</smp-search-table> diff --git a/smp-angular/src/app/service-group-edit/service-group.component.ts b/smp-angular/src/app/service-group-edit/service-group.component.ts deleted file mode 100644 index 3edbb969c6be831ee961e2b6427594dc51ff3688..0000000000000000000000000000000000000000 --- a/smp-angular/src/app/service-group-edit/service-group.component.ts +++ /dev/null @@ -1,74 +0,0 @@ -import {Component, OnInit, TemplateRef, ViewChild} from '@angular/core'; -import {ColumnPicker} from '../common/column-picker/column-picker.model'; -import {MatDialog, MatDialogRef} from '@angular/material'; -import {AlertService} from '../alert/alert.service'; -import {ServiceGroupController} from './service-group-controller'; -import {HttpClient} from '@angular/common/http'; - -@Component({ - moduleId: module.id, - templateUrl:'./service-group.component.html', - styleUrls: ['./service-group.component.css'] -}) -export class ServiceGroupComponent implements OnInit { - - @ViewChild('rowMetadataAction') rowMetadataAction: TemplateRef<any> - @ViewChild('rowExtensionAction') rowExtensionAction: TemplateRef<any> - @ViewChild('rowActions') rowActions: TemplateRef<any>; - - columnPicker: ColumnPicker = new ColumnPicker(); - serviceGroupController: ServiceGroupController; - filter: any = {}; - - constructor(protected http: HttpClient, protected alertService: AlertService, public dialog: MatDialog) { - } - - ngOnInit() { - this.serviceGroupController = new ServiceGroupController(this.dialog); - - this.columnPicker.allColumns = [ - { - name: 'Participant Id', - prop: 'serviceGroupROId.participantId', - width: 275 - }, - { - name: 'Participant schema', - prop: 'serviceGroupROId.participantSchema', - }, - { - name: 'Domain', - prop: 'domain', - }, - { - cellTemplate: this.rowMetadataAction, - name: 'Matadata', - width: 80, - sortable: false - }, - { - cellTemplate: this.rowExtensionAction, - name: 'Extesion', - width: 80, - sortable: false - } - ]; - - this.columnPicker.selectedColumns = this.columnPicker.allColumns.filter(col => { - return ["Participant Id", "Participant schema", "Domain", "Matadata", "Extesion"].indexOf(col.name) != -1 - }); - } - - extensionRowButtonAction(row: any){ - this.serviceGroupController.showExtension(row); - } - - metadataRowButtonAction(row: any){ - this.serviceGroupController.showMetadataList(row); - } - - details(row: any) { - this.serviceGroupController.showDetails(row); - - } -} diff --git a/smp-angular/src/app/service-group-edit/service-metadata-edit-ro.model.ts b/smp-angular/src/app/service-group-edit/service-metadata-edit-ro.model.ts new file mode 100644 index 0000000000000000000000000000000000000000..abff0abd849f755adba43986ff04c169246e8b85 --- /dev/null +++ b/smp-angular/src/app/service-group-edit/service-metadata-edit-ro.model.ts @@ -0,0 +1,10 @@ +import {SearchTableEntity} from "../common/search-table/search-table-entity.model"; +import {ServiceGroupDetailsDialogComponent} from "./service-group-details-dialog/service-group-details-dialog.component"; + +export interface ServiceMetadataEditRo extends SearchTableEntity { + id: number; + documentIdentifier: string; + documentIdentifierScheme: string; + smlSubdomain: string; + domainCode: string; +} diff --git a/smp-angular/src/app/service-group-search/service-group-search.component.html b/smp-angular/src/app/service-group-search/service-group-search.component.html index d9f40a701964ec8f183017c322dffa907e7b87e9..ab20e7b28e73e24901b81a68d1a17ed255004cb6 100644 --- a/smp-angular/src/app/service-group-search/service-group-search.component.html +++ b/smp-angular/src/app/service-group-search/service-group-search.component.html @@ -2,7 +2,7 @@ page_id='search_id' title='Search' [columnPicker]="columnPicker" - url="rest/search" + [url]="baseUrl" [additionalToolButtons]="additionalToolButtons" [searchPanel]="searchPanel" [filter]="filter" @@ -67,42 +67,26 @@ No service metadata </div> <div *ngIf="row.serviceMetadata.length !== 0" > - - <!-- ngx-datatable + <ngx-datatable class='material striped' style="width: 80%" [loadingIndicator]="loading" [rows]='row.serviceMetadata' - [columns]='[{name:"Domain", prop:"domainCode", maxWidth: 250 },{name:"Document identifier scheme",prop:"documentIdentifierScheme",maxWidth: 350},{name:"Document identifier", prop:"documentIdentifier"},{name:"OASIS SMP URL",maxWidth: 350}]' - [columnMode]='"force"' + [columnMode]='"force"' [headerHeight]='50' [footerHeight]='50' [rowHeight]='"auto"'> - <ng-template #rowMetadataSMPUrlLinkAction let-row="smdRow" let-value="value" ngx-datatable-cell-template> + <ngx-datatable-column prop="domainCode" name="Domain" maxWidth="250" ></ngx-datatable-column> + <ngx-datatable-column prop="documentIdentifierScheme" name="Document identifier scheme" maxWidth="350" ></ngx-datatable-column> + <ngx-datatable-column prop="documentIdentifier" name="Document identifier" maxWidth="250" ></ngx-datatable-column> + <ngx-datatable-column [cellTemplate]="rowMetadataSMPUrlLinkAction" name="URL" maxWidth="250" ></ngx-datatable-column> + <ng-template #rowMetadataSMPUrlLinkAction let-rowSmd="row" ngx-datatable-cell-template> <a target="_blank" - href="{{contextPath}}{{row.participantScheme}}::{{row.participantIdentifier}}/services/{{smdRow.documentIdentifierScheme}}::{{smdRow.documentIdentifier}}" >Open URL</a> - + href="{{contextPath}}{{row.participantScheme}}::{{row.participantIdentifier}}/services/{{rowSmd.documentIdentifierScheme}}::{{rowSmd.documentIdentifier}}" >Open URL</a> </ng-template> - </ngx-datatable --> - - <table style="width: 80%"> - <tr> - <th>Domain</th> - <th>Document identifier scheme</th> - <th>Document identifier</th> - <th>OASIS SMP URL</th> - </tr> - <tr *ngFor="let smd of row.serviceMetadata"> - <td style="width: 100em">{{smd.domainCode}}</td> - <td style="width: 250em">{{smd.documentIdentifierScheme}}</td> - <td >{{smd.documentIdentifier}}</td> - <td style="width: 50em"> - <a target="_blank" - href="{{contextPath}}{{row.participantScheme}}::{{row.participantIdentifier}}/services/{{smd.documentIdentifierScheme}}::{{smd.documentIdentifier}}">OASIS ServiceMetadata URL</a> - </td> - </tr> - </table> + </ngx-datatable> + </div> </ng-template> diff --git a/smp-angular/src/app/service-group-search/service-group-search.component.ts b/smp-angular/src/app/service-group-search/service-group-search.component.ts index c2768351135ab9b90256a7ec3973d8cd0198a228..04d95e9f0e346102347092d7260c49151b7cab46 100644 --- a/smp-angular/src/app/service-group-search/service-group-search.component.ts +++ b/smp-angular/src/app/service-group-search/service-group-search.component.ts @@ -1,3 +1,4 @@ +///<reference path="../smp.constants.ts"/> import {Component, OnInit, TemplateRef, ViewChild} from '@angular/core'; import {ColumnPicker} from '../common/column-picker/column-picker.model'; import {MatDialog, MatDialogRef} from '@angular/material'; @@ -7,6 +8,7 @@ import {HttpClient} from '@angular/common/http'; import {Observable} from "rxjs/index"; import {SearchTableResult} from "../common/search-table/search-table-result.model"; import {DomainRo} from "../domain/domain-ro.model"; +import {SmpConstants} from "../smp.constants"; @Component({ moduleId: module.id, @@ -25,9 +27,10 @@ export class ServiceGroupSearchComponent implements OnInit { domainlist: Array<any>; domainObserver: Observable< SearchTableResult> ; contextPath: string = location.pathname.substring(0,location.pathname.length -3); // remove /ui s + baseUrl: string = SmpConstants.REST_SEARCH; constructor(protected http: HttpClient, protected alertService: AlertService, public dialog: MatDialog) { - this.domainObserver = this.http.get<SearchTableResult>('rest/domain'); + this.domainObserver = this.http.get<SearchTableResult>(SmpConstants.REST_DOMAIN); this.domainObserver.subscribe((domains: SearchTableResult) => { this.domainlist = new Array(domains.serviceEntities.length) .map((v, index) => domains.serviceEntities[index] as DomainRo); @@ -39,7 +42,7 @@ export class ServiceGroupSearchComponent implements OnInit { } ngOnDestroy() { - // this.domainObserver.unsubscribe(); + } ngOnInit() { @@ -47,10 +50,10 @@ export class ServiceGroupSearchComponent implements OnInit { this.columnPicker.allColumns = [ { - name: 'Metadata count', + name: 'Metadata size', prop: 'serviceMetadata.length', - width: 60, - maxWidth: 80 + width: 80, + maxWidth: 120 }, { name: 'Participant scheme', @@ -79,7 +82,7 @@ export class ServiceGroupSearchComponent implements OnInit { this.columnPicker.selectedColumns = this.columnPicker.allColumns.filter(col => { - return ["Metadata count", "Participant scheme", "Participant identifier","OASIS ServiceGroup URL"].indexOf(col.name) != -1 + return ["Metadata size", "Participant scheme", "Participant identifier","OASIS ServiceGroup URL"].indexOf(col.name) != -1 }); } diff --git a/smp-angular/src/app/smp.constants.ts b/smp-angular/src/app/smp.constants.ts new file mode 100644 index 0000000000000000000000000000000000000000..f78f13a5fb3ef36b66c92f53b7bdcce8309bf047 --- /dev/null +++ b/smp-angular/src/app/smp.constants.ts @@ -0,0 +1,7 @@ +export class SmpConstants { + + public static readonly REST_DOMAIN = 'rest/domain'; + public static readonly REST_USER = 'rest/user'; + public static readonly REST_SEARCH = 'rest/search'; + public static readonly REST_EDIT = 'rest/servicegroup'; +} diff --git a/smp-angular/src/app/user/certificate-ro.model.ts b/smp-angular/src/app/user/certificate-ro.model.ts index e697f01b35a8ee4f7042bd61fa21d9cb99f040d9..bb66f1de22a6312cf70397fc8560850e1e6beded 100644 --- a/smp-angular/src/app/user/certificate-ro.model.ts +++ b/smp-angular/src/app/user/certificate-ro.model.ts @@ -1,7 +1,9 @@ export interface CertificateRo { + certificateId: string; subject: string; validFrom: Date; validUntil: 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 284c7c190efb0ec46550a4e44150035f466535a4..d7af521f998cd9dac938c5ae1c1ae803ec511ec9 100644 --- a/smp-angular/src/app/user/certificate.service.ts +++ b/smp-angular/src/app/user/certificate.service.ts @@ -8,8 +8,14 @@ export class CertificateService { constructor(private http: HttpClient) {} - uploadCertificate$(payload, userName: string): Observable<CertificateRo> { - return this.http.put<CertificateRo>(`rest/user/${userName}/certificate`, payload); + 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, ) + } } diff --git a/smp-angular/src/app/user/user-controller.ts b/smp-angular/src/app/user/user-controller.ts index 0eee2a77ea9c75011bacbcbf5bc5b82e27163b8e..448282595e98a5219c33d8118e20ddd60d3e3dee 100644 --- a/smp-angular/src/app/user/user-controller.ts +++ b/smp-angular/src/app/user/user-controller.ts @@ -25,7 +25,9 @@ export class UserController implements SearchTableController { public newRow(): UserRo { return { - userName: '', + id: null, + index: null, + username: '', email: '', role: '', status: SearchTableEntityStatus.NEW 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 3ced86082ea021159cfd019d1f65262fdf35516c..83d39a4be1c6ad94eac020ed0c407b734d5285c1 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 @@ -13,8 +13,8 @@ <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> + <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> @@ -80,6 +80,13 @@ <input matInput placeholder="Fingerprints" name="fingerPrint" value="{{current.certificate?.fingerprints}}" id="fingerPrint_id"> </mat-form-field> </fieldset> + + <input + type="file" (change)="onFileChanged($event)" + #fileInput> + <button (click)="onUpload()">Upload!</button> + + <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> 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 90dd083ec00ff4fa0234b9c824990c5decf92fc7..a5b36c61bc266a5007337ef0b4d9dc46bfc5f010 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 @@ -8,6 +8,10 @@ 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"; + @Component({ selector: 'user-details-dialog', @@ -37,6 +41,8 @@ export class UserDetailsDialogComponent { @ViewChild('fileInput') private fileInput; + + private passwordConfirmationValidator: ValidatorFn = (control: FormGroup): ValidationErrors | null => { const password = control.get('password'); const confirmation = control.get('confirmation'); @@ -60,7 +66,7 @@ export class UserDetailsDialogComponent { certificate: data.row.certificate, } : { - userName: '', + username: '', email: '', password: '', confirmation: '', @@ -70,12 +76,10 @@ export class UserDetailsDialogComponent { }; this.userForm = fb.group({ - 'userName': new FormControl({value: this.current.userName, disabled: this.editMode}, this.editMode ? Validators.nullValidator : null), + '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 + 'confirmation': new FormControl(this.current.password, Validators.pattern(this.passwordPattern)), }, { validator: this.passwordConfirmationValidator }); this.userService.getUserRoles$().subscribe(userRoles => { @@ -91,7 +95,7 @@ export class UserDetailsDialogComponent { } updateUserName(event) { - this.current.userName = event.target.value; + this.current.username = event.target.value; } updatePassword(event) { @@ -137,4 +141,21 @@ export class UserDetailsDialogComponent { reader.readAsArrayBuffer(file); } + + + // example + selectedFile: File; + obrs: Observable< CertificateRo> ; + certData: CertificateRo; + onFileChanged(event) { + this.selectedFile = event.target.files[0] + } + + onUpload() { + this.obrs = this.certificateService.onUpload(this.selectedFile); + this.obrs.subscribe((cert: CertificateRo) => { + this.certData = cert; + }); + + } } diff --git a/smp-angular/src/app/user/user-ro.model.ts b/smp-angular/src/app/user/user-ro.model.ts index c6e8dfa9a6ae7d38522eb3a08c741d3216b41836..6dcc00e2b6ec5f86942ee094a592ba1e8826e864 100644 --- a/smp-angular/src/app/user/user-ro.model.ts +++ b/smp-angular/src/app/user/user-ro.model.ts @@ -2,7 +2,8 @@ import {SearchTableEntity} from '../common/search-table/search-table-entity.mode import {CertificateRo} from './certificate-ro.model'; export interface UserRo extends SearchTableEntity { - userName: string; + id: number; + username: string; email: string; password?: string; role: string; diff --git a/smp-angular/src/app/user/user.component.html b/smp-angular/src/app/user/user.component.html index 4ff6670bfabbd16692fdef038dc1917cb8c64f75..00fc2f9ece5894755c6c89f92a3df8cca10c1254 100644 --- a/smp-angular/src/app/user/user.component.html +++ b/smp-angular/src/app/user/user.component.html @@ -12,7 +12,7 @@ <ng-template #searchPanel> <mat-form-field> - <input matInput placeholder="Username" name="Username" [(ngModel)]="filter.userName" #messageId="ngModel"> + <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 294159cc257ff097752d520adf7dcd37b1fbb972..4ae256bc10e8ff1baeee53d3247fc95abf49b580 100644 --- a/smp-angular/src/app/user/user.component.ts +++ b/smp-angular/src/app/user/user.component.ts @@ -28,7 +28,7 @@ export class UserComponent implements OnInit { this.columnPicker.allColumns = [ { name: 'Username', - prop: 'userName', + prop: 'username', canAutoResize: true }, { diff --git a/smp-server-library/pom.xml b/smp-server-library/pom.xml index bdc60fe6fae4edc37b01e55a241f24bea8179004..bb34c4f79c8cfe999f7af76910cde763f1ca2976 100644 --- a/smp-server-library/pom.xml +++ b/smp-server-library/pom.xml @@ -51,6 +51,10 @@ <groupId>eu.europa.ec.bdmsl</groupId> <artifactId>bdmsl-client</artifactId> </dependency> + <dependency> + <groupId>eu.europa.ec.edelivery</groupId> + <artifactId>edelivery-springsecurity-2-way-ssl-auth</artifactId> + </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/model/CommonColumnsLengths.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/model/CommonColumnsLengths.java index b9883b6b03336ab625bd2cfbbfc9af7dafd9b3ce..bb50e6f397976ec7d3785d5118250979913e4a5c 100644 --- a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/model/CommonColumnsLengths.java +++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/model/CommonColumnsLengths.java @@ -29,5 +29,8 @@ public class CommonColumnsLengths { public static final int MAX_SML_SUBDOMAIN_LENGTH = 256; public static final int MAX_SML_SMP_ID_LENGTH = 256; public static final int MAX_USER_ROLE_LENGTH = 256; + public static final int MAX_TEXT_LENGTH_512 = 512; + public static final int MAX_TEXT_LENGTH_128 = 128; + } diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/model/DBCertificate.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/model/DBCertificate.java index cca264671cea29ee975eacc1b4862077c870df12..93787a203689e7682c97f649d085b2f156a2eba2 100644 --- a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/model/DBCertificate.java +++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/model/DBCertificate.java @@ -39,6 +39,15 @@ public class DBCertificate extends BaseEntity { private LocalDateTime validFrom; @Column(name = "VALID_TO") private LocalDateTime validTo; + + @Column(name = "subject", length = CommonColumnsLengths.MAX_TEXT_LENGTH_512) + private String subject; + @Column(name = "issuer", length = CommonColumnsLengths.MAX_TEXT_LENGTH_512) + private String issuer; + @Column(name = "serialNumber", length = CommonColumnsLengths.MAX_TEXT_LENGTH_128) + private String serialNumber; + + @Column(name = "CREATED_ON" , nullable = false) LocalDateTime createdOn; @Column(name = "LAST_UPDATED_ON", nullable = false) @@ -93,6 +102,31 @@ public class DBCertificate extends BaseEntity { this.dbUser = dbUser; } + + public String getSubject() { + return subject; + } + + public void setSubject(String subject) { + this.subject = subject; + } + + public String getIssuer() { + return issuer; + } + + public void setIssuer(String issuer) { + this.issuer = issuer; + } + + public String getSerialNumber() { + return serialNumber; + } + + public void setSerialNumber(String serialNumber) { + this.serialNumber = serialNumber; + } + @Override public boolean equals(Object o) { if (this == o) return true; @@ -138,4 +172,5 @@ public class DBCertificate extends BaseEntity { public void setLastUpdatedOn(LocalDateTime lastUpdatedOn) { this.lastUpdatedOn = lastUpdatedOn; } + } 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 new file mode 100644 index 0000000000000000000000000000000000000000..37f7a40acb7ef6224a055828af32c81025fd908d --- /dev/null +++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/ui/CertificateRO.java @@ -0,0 +1,87 @@ +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; + private String subject; + private String issuer; + private String serialNumber; + private LocalDateTime validFrom; + private LocalDateTime validTo; + + public CertificateRO(){ + + } + + public static long getSerialVersionUID() { + return serialVersionUID; + } + + public String getCertificateId() { + return certificateId; + } + + public void setCertificateId(String certificateId) { + this.certificateId = certificateId; + } + + public String getSubject() { + return subject; + } + + public void setSubject(String subject) { + this.subject = subject; + } + + public String getIssuer() { + return issuer; + } + + public void setIssuer(String issuer) { + this.issuer = issuer; + } + + public String getSerialNumber() { + return serialNumber; + } + + public void setSerialNumber(String serialNumber) { + this.serialNumber = serialNumber; + } + + public LocalDateTime getValidFrom() { + return validFrom; + } + + public void setValidFrom(LocalDateTime validFrom) { + this.validFrom = validFrom; + } + + public LocalDateTime getValidTo() { + return validTo; + } + + public void setValidTo(LocalDateTime validTo) { + this.validTo = validTo; + } +} diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/ui/ServiceGroupRO.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/ui/ServiceGroupRO.java index 9755ed0418a1d82b2d9dadafef0385f25377b1ac..f3da826f10980d93b21242e5ac11f9c0a52952d6 100644 --- a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/ui/ServiceGroupRO.java +++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/ui/ServiceGroupRO.java @@ -15,22 +15,23 @@ 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 String domain; - public String getDomain() { - return domain; + public Long getId() { + return id; } - public void setDomain(String domain) { - this.domain = domain; + public void setId(Long id) { + this.id = id; } public String getParticipantIdentifier() { @@ -61,5 +62,8 @@ public class ServiceGroupRO extends BaseRO { public List<ServiceMetadataRO> getServiceMetadata() { return lstServiceMetadata; } + public List<UserRO> getUsers() { + return lstUser; + } } diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/ui/UserRO.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/ui/UserRO.java index dd6c885c5b95e260144684d48304d0b11e13269d..fd6764754daabeabbcb1ecae971126884a58a616 100644 --- a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/ui/UserRO.java +++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/ui/UserRO.java @@ -15,6 +15,7 @@ import java.time.LocalDateTime; public class UserRO extends BaseRO { + private static final long serialVersionUID = -4971552086560325302L; private String username; private String password; @@ -22,11 +23,20 @@ public class UserRO extends BaseRO { LocalDateTime passwordChanged; private boolean active = true; private String role; + private Long id; + private CertificateRO certificateData; public UserRO(){ } + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } public String getUsername() { return username; @@ -75,4 +85,12 @@ public class UserRO extends BaseRO { public void setRole(String role) { this.role = role; } + + public CertificateRO getCertificateData() { + return certificateData; + } + + public void setCertificateData(CertificateRO certificate) { + this.certificateData = certificate; + } } diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/ui/UIServiceBase.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/ui/UIServiceBase.java index edf8b64afcf5a83dabaf868a85ce68ac1b74f418..c796fc57f0d7491f4b66dde05b38e9376c32fc31 100644 --- a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/ui/UIServiceBase.java +++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/ui/UIServiceBase.java @@ -77,6 +77,7 @@ abstract class UIServiceBase<E extends BaseEntity, R> { return sg; } + /** * Simple method for converting types. Property name and property type must match * @param d diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/ui/UIServiceGroupService.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/ui/UIServiceGroupService.java index 0782d9830e14edda44908c23af22320b68b5c379..de751e9a90ea02caff5eac49d39f6d8bbffa3328 100644 --- a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/ui/UIServiceGroupService.java +++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/ui/UIServiceGroupService.java @@ -2,23 +2,32 @@ package eu.europa.ec.edelivery.smp.services.ui; import eu.europa.ec.edelivery.smp.data.dao.BaseDao; import eu.europa.ec.edelivery.smp.data.dao.ServiceGroupDao; +import eu.europa.ec.edelivery.smp.data.dao.UserDao; import eu.europa.ec.edelivery.smp.data.model.DBServiceGroup; +import eu.europa.ec.edelivery.smp.data.model.DBUser; import eu.europa.ec.edelivery.smp.data.ui.ServiceGroupRO; import eu.europa.ec.edelivery.smp.data.ui.ServiceMetadataRO; import eu.europa.ec.edelivery.smp.data.ui.ServiceResult; +import eu.europa.ec.edelivery.smp.data.ui.UserRO; +import eu.europa.ec.edelivery.smp.data.ui.enums.EntityROStatus; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import java.time.LocalDateTime; import java.util.ArrayList; import java.util.List; @Service public class UIServiceGroupService extends UIServiceBase<DBServiceGroup, ServiceGroupRO> { + @Autowired ServiceGroupDao serviceGroupDao; + @Autowired + UserDao userDao; + @Override protected BaseDao<DBServiceGroup> getDatabaseDao() { return serviceGroupDao; @@ -53,9 +62,9 @@ public class UIServiceGroupService extends UIServiceBase<DBServiceGroup, Service for (DBServiceGroup dbServiceGroup : lst) { ServiceGroupRO serviceGroupRo = new ServiceGroupRO(); serviceGroupRo.setIndex(iStartIndex++); + serviceGroupRo.setId(dbServiceGroup.getId()); serviceGroupRo.setParticipantIdentifier(dbServiceGroup.getParticipantIdentifier()); serviceGroupRo.setParticipantScheme(dbServiceGroup.getParticipantScheme()); - dbServiceGroup.getServiceGroupDomains().forEach(sgd -> { sgd.getServiceMetadata().forEach(sgmd -> { ServiceMetadataRO smdro = new ServiceMetadataRO(); @@ -66,6 +75,16 @@ public class UIServiceGroupService extends UIServiceBase<DBServiceGroup, Service serviceGroupRo.getServiceMetadata().add(smdro); }); }); + // add users + dbServiceGroup.getUsers().forEach(usr->{ + UserRO userRO = new UserRO(); + userRO.setId(usr.getId()); + userRO.setUsername(usr.getUsername()); + userRO.setActive(usr.isActive()); + userRO.setEmail(usr.getEmail()); + userRO.setRole(usr.getRole()); + serviceGroupRo.getUsers().add(userRO); + }); lstRo.add(serviceGroupRo); } @@ -74,4 +93,77 @@ public class UIServiceGroupService extends UIServiceBase<DBServiceGroup, Service return sg; } + @Transactional + public ServiceGroupRO getServiceGroupById(Long serviceGroupId) { + DBServiceGroup dbServiceGroup = getDatabaseDao().find(serviceGroupId); + ServiceGroupRO serviceGroupRo = new ServiceGroupRO(); + serviceGroupRo.setId(dbServiceGroup.getId()); + serviceGroupRo.setParticipantIdentifier(dbServiceGroup.getParticipantIdentifier()); + serviceGroupRo.setParticipantScheme(dbServiceGroup.getParticipantScheme()); + // add service groups + dbServiceGroup.getServiceGroupDomains().forEach(sgd -> { + sgd.getServiceMetadata().forEach(sgmd -> { + ServiceMetadataRO smdro = new ServiceMetadataRO(); + smdro.setDocumentIdentifier(sgmd.getDocumentIdentifier()); + smdro.setDocumentIdentifierScheme(sgmd.getDocumentIdentifierScheme()); + smdro.setDomainCode(sgd.getDomain().getDomainCode()); + smdro.setSmlSubdomain(sgd.getDomain().getSmlSubdomain()); + serviceGroupRo.getServiceMetadata().add(smdro); + }); + }); + // add users + dbServiceGroup.getUsers().forEach(usr->{ + UserRO userRO = new UserRO(); + userRO.setId(usr.getId()); + userRO.setUsername(usr.getUsername()); + userRO.setActive(usr.isActive()); + userRO.setEmail(usr.getEmail()); + userRO.setRole(usr.getRole()); + serviceGroupRo.getUsers().add(userRO); + }); + return serviceGroupRo; + } + + @Transactional + public void updateServiceGroupList(List<ServiceGroupRO> lst) { + boolean suc = false; + for (ServiceGroupRO dRo: lst){ + + + if (dRo.getStatus() == EntityROStatus.NEW.getStatusNumber()) { + DBServiceGroup dDb = convertFromRo(dRo); + for (UserRO userRO: dRo.getUsers()) { + System.out.println("GET USER ID: " + userRO.getId()); + DBUser du = userDao.find(userRO.getId()); + dDb.getUsers().add(du); + + } + getDatabaseDao().persistFlushDetach(dDb); + } else if (dRo.getStatus() == EntityROStatus.UPDATED.getStatusNumber()) { + DBServiceGroup upd = getDatabaseDao().find(dRo.getId()); + upd.getUsers().clear(); + for (UserRO userRO: dRo.getUsers()) { + System.out.println("GET USER ID: " + userRO.getId()); + DBUser du = userDao.find(userRO.getId()); + upd.getUsers().add(du); + + } + // only servicegroup users can be changed__ + /* + upd.setSmlSmpId(dRo.getSmlSmpId()); + upd.setSmlClientKeyAlias(dRo.getSmlClientKeyAlias()); + upd.setSmlClientCertHeader(dRo.getSmlClientCertHeader()); + upd.setSmlParticipantIdentifierRegExp(dRo.getSmlParticipantIdentifierRegExp()); + upd.setSmlSubdomain(dRo.getSmlSubdomain()); + upd.setDomainCode(dRo.getDomainCode()); + upd.setSignatureKeyAlias(dRo.getSignatureKeyAlias()); + upd.setLastUpdatedOn(LocalDateTime.now());*/ + getDatabaseDao().update(upd); + } else if (dRo.getStatus() == EntityROStatus.REMOVE.getStatusNumber()) { + DBServiceGroup upd = getDatabaseDao().find(dRo.getId()); + serviceGroupDao.removeServiceGroup(upd); + } + } + } + } 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 6a0ef1eb8b9227ff2f047b96f8e413493aa5595c..480b2a00356dec43776099755be0085a735b9543 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 @@ -1,17 +1,45 @@ package eu.europa.ec.edelivery.smp.services.ui; +import eu.europa.ec.edelivery.security.PreAuthenticatedCertificatePrincipal; +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.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; +import org.springframework.security.crypto.bcrypt.BCrypt; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import javax.ejb.Local; +import java.io.ByteArrayInputStream; +import java.io.FileInputStream; +import java.lang.reflect.InvocationTargetException; +import java.math.BigInteger; +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; + @Service public class UIUserService extends UIServiceBase<DBUser, UserRO> { + private static final SMPLogger LOG = SMPLoggerFactory.getLogger(UIUserService.class); + @Autowired UserDao userDao; @@ -21,7 +49,7 @@ public class UIUserService extends UIServiceBase<DBUser, UserRO> { } /** - * Method returns Domain resource object list for page. + * Method returns user resource object list for page. * * @param page * @param pageSize @@ -35,7 +63,127 @@ public class UIUserService extends UIServiceBase<DBUser, UserRO> { String sortField, String sortOrder, Object filter) { - return super.getTableList(page, pageSize, sortField, sortOrder, filter); + ServiceResult<UserRO> resUsers = super.getTableList(page, pageSize, sortField, sortOrder, filter); + resUsers.getServiceEntities().forEach(usr -> usr.setPassword(null)); + return resUsers; + } + + @Transactional + public void updateUserList(List<UserRO> lst) { + boolean suc = false; + for (UserRO userRO : lst) { + + if (userRO.getStatus() == EntityROStatus.NEW.getStatusNumber()) { + DBUser dbUser = convertFromRo(userRO); + userDao.persistFlushDetach(dbUser); + } else if (userRO.getStatus() == EntityROStatus.UPDATED.getStatusNumber()) { + DBUser dbUser = userDao.find(userRO.getId()); + dbUser.setEmail(userRO.getEmail()); + dbUser.setRole(userRO.getRole()); + dbUser.setActive(userRO.isActive()); + // check for new password + if (!StringUtils.isBlank(userRO.getPassword())) { + if (!StringUtils.isBlank(dbUser.getPassword())) { + if (!BCrypt.checkpw(userRO.getPassword(), dbUser.getPassword())) { + LOG.debug("User with id {} changed password!", dbUser.getId()); + + dbUser.setPassword(BCryptPasswordHash.hashPassword(userRO.getPassword().trim())); + dbUser.setPasswordChanged(LocalDateTime.now()); + } + } else { + dbUser.setPassword(BCryptPasswordHash.hashPassword(userRO.getPassword())); + } + } + // update certificate data + if (userRO.getCertificateData() == null) { + dbUser.setCertificate(null); + } else { + CertificateRO certificateRO = userRO.getCertificateData(); + DBCertificate dbCertificate = dbUser.getCertificate() != null ? dbUser.getCertificate() : new DBCertificate(); + dbUser.setCertificate(dbCertificate); + dbCertificate.setValidFrom(certificateRO.getValidFrom()); + dbCertificate.setValidFrom(certificateRO.getValidTo()); + dbCertificate.setCertificateId(certificateRO.getCertificateId()); + dbCertificate.setSerialNumber(certificateRO.getSerialNumber()); + dbCertificate.setSubject(certificateRO.getSubject()); + dbCertificate.setIssuer(certificateRO.getIssuer()); + } + dbUser.setLastUpdatedOn(LocalDateTime.now()); + userDao.update(dbUser); + } else if (userRO.getStatus() == EntityROStatus.REMOVE.getStatusNumber()) { + userDao.removeById(userRO.getId()); + } + } + } + + public CertificateRO getCertificateData(byte[] buff) throws CertificateException{ + + CertificateFactory fact = null; + + fact = CertificateFactory.getInstance("X.509"); + ByteArrayInputStream is = new ByteArrayInputStream(buff); + X509Certificate cert = (X509Certificate) fact.generateCertificate(is); + String subject = cert.getSubjectDN().getName(); + String issuer = cert.getIssuerDN().getName(); + String hash = cert.getIssuerDN().getName(); + BigInteger serial = cert.getSerialNumber(); + String certId = getCertificateIdFromCertificate(subject,issuer, serial ); + CertificateRO cro = new CertificateRO(); + cro.setCertificateId(certId); + cro.setSubject(subject); + cro.setIssuer(issuer); + // set serial as HEX + cro.setSerialNumber(serial.toString(16)); + cro.setValidFrom(LocalDateTime.ofInstant(cert.getNotBefore().toInstant(), ZoneId.systemDefault())); + cro.setValidTo(LocalDateTime.ofInstant(cert.getNotAfter().toInstant(), ZoneId.systemDefault())); + + return cro; + + + + } + + public String getCertificateIdFromCertificate(String subject, String issuer, BigInteger serial ){ + return new PreAuthenticatedCertificatePrincipal(subject, issuer, serial).getName(); + } + + @Override + public UserRO convertToRo(DBUser d) { + try { + UserRO dro = new UserRO(); + BeanUtils.copyProperties(dro, d); + + if (d.getCertificate()!=null) { + CertificateRO certData = new CertificateRO(); + BeanUtils.copyProperties(certData, d.getCertificate()); + dro.setCertificateData(certData); + } + return dro; + } catch ( InvocationTargetException | IllegalAccessException e) { + String msg = "Error occurred while converting to RO Entity for " +UserRO.class.getName(); + LOG.error(msg, e ); + throw new RuntimeException(msg, e); + } + } + + @Override + public DBUser convertFromRo(UserRO d) { + try { + DBUser dro = new DBUser(); + BeanUtils.copyProperties(dro, d); + DBCertificate cert = new DBCertificate(); + if (d.getCertificateData()!=null) { + DBCertificate certData = new DBCertificate(); + BeanUtils.copyProperties(certData, d.getCertificateData()); + dro.setCertificate(cert); + } + + return dro; + } catch ( InvocationTargetException | IllegalAccessException e) { + String msg = "Error occurred while converting to RO Entity for " +UserRO.class.getName(); + LOG.error(msg, e ); + throw new RuntimeException(msg, e); + } } } diff --git a/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/services/ui/UIUserServiceIntegrationTest.java b/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/services/ui/UIUserServiceIntegrationTest.java index 4950f5255efad8589205b4539330c504e7bf4781..62305ae17bcd0bc24b9244a8591a9eb176140c15 100644 --- a/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/services/ui/UIUserServiceIntegrationTest.java +++ b/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/services/ui/UIUserServiceIntegrationTest.java @@ -1,17 +1,32 @@ package eu.europa.ec.edelivery.smp.services.ui; +import eu.europa.ec.edelivery.smp.data.model.DBCertificate; 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.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.services.AbstractServiceIntegrationTest; import eu.europa.ec.edelivery.smp.testutil.TestDBUtils; +import org.apache.commons.io.IOUtils; +import org.hibernate.type.BigIntegerType; +import org.hibernate.type.descriptor.java.UUIDTypeDescriptor; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.crypto.bcrypt.BCrypt; import org.springframework.test.context.ContextConfiguration; +import java.io.IOException; +import java.math.BigInteger; +import java.security.cert.CertificateException; +import java.time.LocalDateTime; +import java.time.temporal.ChronoUnit; +import java.time.temporal.TemporalUnit; +import java.util.*; + import static org.junit.Assert.*; @@ -29,20 +44,21 @@ public class UIUserServiceIntegrationTest extends AbstractServiceIntegrationTest @Autowired protected UIUserService testInstance; - protected void insertDataObjects(int size){ - for (int i=0; i < size; i++){ - DBUser d = TestDBUtils.createDBUserByUsername("user"+i); + protected void insertDataObjects(int size) { + for (int i = 0; i < size; i++) { + DBUser d = TestDBUtils.createDBUserByUsername("user" + i); + d.setPassword(BCrypt.hashpw(d.getPassword(), BCrypt.gensalt())); userDao.persistFlushDetach(d); } } @Test - public void testGetTableListEmpty(){ + public void testGetTableListEmpty() { // given //when - ServiceResult<UserRO> res = testInstance.getTableList(-1,-1,null, null,null); + ServiceResult<UserRO> res = testInstance.getTableList(-1, -1, null, null, null); // then assertNotNull(res); assertEquals(0, res.getCount().intValue()); @@ -53,13 +69,12 @@ public class UIUserServiceIntegrationTest extends AbstractServiceIntegrationTest } @Test - public void testGetTableList15(){ + public void testGetTableList15() { // given insertDataObjects(15); //when - ServiceResult<UserRO> res = testInstance.getTableList(-1,-1,null, null,null); - + ServiceResult<UserRO> res = testInstance.getTableList(-1, -1, null, null, null); // then assertNotNull(res); @@ -71,10 +86,191 @@ public class UIUserServiceIntegrationTest extends AbstractServiceIntegrationTest // all table properties should not be null assertNotNull(res); + assertNotNull(res.getServiceEntities().get(0).getId()); assertNotNull(res.getServiceEntities().get(0).getUsername()); assertNotNull(res.getServiceEntities().get(0).getEmail()); - assertNotNull(res.getServiceEntities().get(0).getPassword()); + assertNull(res.getServiceEntities().get(0).getPassword()); // Service list must not return passwords assertNotNull(res.getServiceEntities().get(0).getPasswordChanged()); assertNotNull(res.getServiceEntities().get(0).getRole()); } + + @Test + public void testUpdateUserPassword() { + // given + insertDataObjects(1); + String newPassword = "TestPasswd!@#" + Calendar.getInstance().getTime(); + ServiceResult<UserRO> urTest = testInstance.getTableList(-1,-1,null, null, null); + assertEquals(1, urTest.getServiceEntities().size()); + + UserRO usr = urTest.getServiceEntities().get(0); + + //when + usr.setPassword(newPassword); + usr.setStatus(EntityROStatus.UPDATED.getStatusNumber()); + testInstance.updateUserList(Collections.singletonList(usr)); + + // then + DBUser dbuser = userDao.find(usr.getId()); + assertTrue(BCrypt.checkpw(newPassword, dbuser.getPassword())); + } + + + @Test + public void testAddUserWithoutCertificate() { + // given + insertDataObjects(15); + long iCnt = userDao.getDataListCount(null); + + UserRO user = new UserRO(); + user.setPassword(UUID.randomUUID().toString()); + user.setUsername(UUID.randomUUID().toString()); + user.setEmail(UUID.randomUUID().toString()); + user.setRole("ROLE"); + user.setStatus(EntityROStatus.NEW.getStatusNumber()); + + //when + testInstance.updateUserList(Collections.singletonList(user)); + // then + long iCntNew = userDao.getDataListCount(null); + assertEquals(iCnt+1, iCntNew); + Optional<DBUser> oUsr = userDao.findUserByUsername(user.getUsername()); + assertTrue(oUsr.isPresent()); + assertEquals(user.getPassword(), oUsr.get().getPassword()); + assertEquals(user.getUsername(), oUsr.get().getUsername()); + assertEquals(user.getRole(), oUsr.get().getRole()); + assertEquals(user.getEmail(), oUsr.get().getEmail()); + assertNull(oUsr.get().getCertificate()); + } + + @Test + public void testAddUserWithCertificate() { + // given + insertDataObjects(15); + long iCnt = userDao.getDataListCount(null); + + LocalDateTime now = LocalDateTime.now().truncatedTo(ChronoUnit.MINUTES); + LocalDateTime future =now.plusYears(1); + + UserRO user = new UserRO(); + user.setPassword(UUID.randomUUID().toString()); + user.setUsername(UUID.randomUUID().toString()); + user.setEmail(UUID.randomUUID().toString()); + user.setRole("ROLE"); + CertificateRO cert = new CertificateRO(); + cert.setSubject(UUID.randomUUID().toString()); + cert.setIssuer(UUID.randomUUID().toString()); + cert.setSerialNumber(UUID.randomUUID().toString()); + cert.setCertificateId(UUID.randomUUID().toString()); + cert.setValidFrom(now); + cert.setValidTo(future); + user.setCertificateData(cert); + + user.setStatus(EntityROStatus.NEW.getStatusNumber()); + + + //when + testInstance.updateUserList(Collections.singletonList(user)); + // then + long iCntNew = userDao.getDataListCount(null); + assertEquals(iCnt+1, iCntNew); + Optional<DBUser> oUsr = userDao.findUserByUsername(user.getUsername()); + assertTrue(oUsr.isPresent()); + assertEquals(user.getPassword(), oUsr.get().getPassword()); + assertEquals(user.getUsername(), oUsr.get().getUsername()); + assertEquals(user.getRole(), oUsr.get().getRole()); + assertEquals(user.getEmail(), oUsr.get().getEmail()); + assertNotNull(oUsr.get().getCertificate()); + assertEquals(cert.getCertificateId(), cert.getCertificateId()); + assertEquals(cert.getSubject(), cert.getSubject()); + assertEquals(cert.getIssuer(), cert.getIssuer()); + assertEquals(cert.getSerialNumber(), cert.getSerialNumber()); + assertEquals(now, cert.getValidFrom()); + assertEquals(future, cert.getValidTo()); + } + + + @Test + public void testUserRemoveCertificate() { + // given + + + LocalDateTime now = LocalDateTime.now().truncatedTo(ChronoUnit.MINUTES); + LocalDateTime future =now.plusYears(1); + + DBUser user = new DBUser(); + user.setPassword(UUID.randomUUID().toString()); + user.setUsername(UUID.randomUUID().toString()); + user.setEmail(UUID.randomUUID().toString()); + user.setRole("ROLE"); + DBCertificate cert = new DBCertificate(); + cert.setSubject(UUID.randomUUID().toString()); + cert.setIssuer(UUID.randomUUID().toString()); + cert.setSerialNumber(UUID.randomUUID().toString()); + cert.setCertificateId(UUID.randomUUID().toString()); + cert.setValidFrom(now); + cert.setValidTo(future); + user.setCertificate(cert); + userDao.persistFlushDetach(user); + ServiceResult<UserRO> urTest = testInstance.getTableList(-1,-1,null, null, null); + assertEquals(1, urTest.getServiceEntities().size()); + UserRO userRO = urTest.getServiceEntities().get(0); + assertNotNull(userRO.getCertificateData()); + + //when + userRO.setCertificateData(null); + userRO.setStatus(EntityROStatus.UPDATED.getStatusNumber()); + + testInstance.updateUserList(Collections.singletonList(userRO)); + // then + ServiceResult<UserRO> res = testInstance.getTableList(-1,-1,null, null, null); + assertEquals(1, urTest.getServiceEntities().size()); + UserRO userResRO = urTest.getServiceEntities().get(0); + assertNull(userResRO.getCertificateData()); + + } + + @Test + public void testDeleteUser() { + // given + insertDataObjects(15); + ServiceResult<UserRO> urTest = testInstance.getTableList(-1,-1,null, null, null); + assertEquals(15, urTest.getServiceEntities().size()); + + UserRO user = urTest.getServiceEntities().get(0); + user.setStatus(EntityROStatus.REMOVE.getStatusNumber()); + + //when + testInstance.updateUserList(Collections.singletonList(user)); + + // then + long iCntNew = userDao.getDataListCount(null); + Optional<DBUser> rmUsr = userDao.findUserByUsername(user.getUsername()); + + assertEquals( urTest.getServiceEntities().size()-1, iCntNew); + assertFalse(rmUsr.isPresent()); + + } + + @Test + public void testGetCertificateData() throws IOException, CertificateException { + // given + byte[] buff = IOUtils.toByteArray(UIUserServiceIntegrationTest.class.getResourceAsStream("/keystores/SMPtest.crt")); + // when + CertificateRO cer = testInstance.getCertificateData(buff); + //then + assertEquals("CN=SMP test,O=DIGIT,C=BE:0000000000000003", cer.getCertificateId()); + assertEquals("CN=Intermediate CA, O=DIGIT, C=BE", cer.getIssuer()); + assertEquals("EMAILADDRESS=smp@test.com, CN=SMP test, O=DIGIT, C=BE", cer.getSubject()); + assertEquals("3", cer.getSerialNumber()); + assertNotNull(cer.getValidFrom()); + assertNotNull(cer.getValidTo()); + assertTrue(cer.getValidFrom().isBefore(cer.getValidTo())); + + + } + + + + + } 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 d496bb6c9a1557714d88ab5ee22927d26b923e09..da24d8c4e3e8fb287d9cfb19682447c5ca6d4e48 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 @@ -38,7 +38,7 @@ public class SmpWebAppConfig implements WebMvcConfigurer { @Override public void addViewControllers(ViewControllerRegistry registry) { registry.addViewController("/").setViewName("/index.html"); - registry.addRedirectViewController("/ui/","/ui/index.html"); + registry.addViewController("/ui/").setViewName("/ui/index.html"); //Home page used by SMP 2.x and 3.x - needed for backward compatibility in some EC's environments registry.addViewController("/web/index.html").setViewName("/index.html"); diff --git a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/controllers/ServiceGroupController.java b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/controllers/ServiceGroupController.java index 909a0d78653d8c083bc067fd2e59449a6b7bff24..ce39c2a8fe3fa5faf6269c9b435d5f8dc45debf2 100644 --- a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/controllers/ServiceGroupController.java +++ b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/controllers/ServiceGroupController.java @@ -55,7 +55,7 @@ import static org.springframework.http.ResponseEntity.ok; */ @RestController -@RequestMapping("/{serviceGroupId}") +@RequestMapping("/{serviceGroupId:^(?!ui).*}") public class ServiceGroupController { private static final SMPLogger LOG = SMPLoggerFactory.getLogger(ServiceGroupController.class); @@ -75,6 +75,8 @@ public class ServiceGroupController { @GetMapping(produces = "text/xml; charset=UTF-8") public ServiceGroup getServiceGroup(HttpServletRequest httpReq, @PathVariable String serviceGroupId) { + + String host = httpReq.getRemoteHost(); LOG.businessInfo(SMPMessageCode.BUS_HTTP_GET_SERVICE_GROUP,host, serviceGroupId); diff --git a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/SearchResource.java b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/SearchResource.java index 64ff10bfb4136947a141de31d20269bcbc74debc..db693136d3bd9d80dcc9c2531bdaab6ee0f79e00 100644 --- a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/SearchResource.java +++ b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/SearchResource.java @@ -50,8 +50,6 @@ public class SearchResource { ServiceGroupFilter sgf = new ServiceGroupFilter(); sgf.setParticipantIdentifierLike(participantIdentifier); sgf.setParticipantSchemeLike(participantScheme); - - return uiServiceGroupService.getTableList(page,pageSize, orderBy, orderType, sgf ); } } diff --git a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/ServiceGroupResource.java b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/ServiceGroupResource.java index f28b7c3dc531d8414551d4f32d7c302ac02b5e87..543657db916756c851c4eecf8f33bf3c061c1a6b 100644 --- a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/ServiceGroupResource.java +++ b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/ServiceGroupResource.java @@ -1,8 +1,11 @@ package eu.europa.ec.edelivery.smp.ui; +import eu.europa.ec.edelivery.smp.data.ui.DomainRO; import eu.europa.ec.edelivery.smp.data.ui.ServiceGroupRO; 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 org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -10,6 +13,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import javax.annotation.PostConstruct; +import java.util.Arrays; /** * @author Joze Rihtarsic @@ -20,7 +24,7 @@ import javax.annotation.PostConstruct; @RequestMapping(value = "/ui/rest/servicegroup") public class ServiceGroupResource { - private static final Logger LOGGER = LoggerFactory.getLogger(ServiceGroupResource.class); + private static final SMPLogger LOG = SMPLoggerFactory.getLogger(DomainResource.class); @Autowired private UIServiceGroupService uiServiceGroupService; @@ -33,7 +37,7 @@ public class ServiceGroupResource { @PutMapping(produces = {"application/json"}) @ResponseBody @RequestMapping(method = RequestMethod.GET) - public ServiceResult<ServiceGroupRO> getServiceGroupList( + public ServiceResult<ServiceGroupRO> getServiceGroupList( @RequestParam(value = "page", defaultValue = "0") int page, @RequestParam(value = "pageSize", defaultValue = "10") int pageSize, @RequestParam(value = "orderBy", required = false) String orderBy, @@ -41,9 +45,24 @@ public class ServiceGroupResource { @RequestParam(value = "participantId", required = false) String participantId, @RequestParam(value = "participantSchema", required = false) String participantSchema, @RequestParam(value = "domain", required = false) String domain - ) { + ) { + return uiServiceGroupService.getTableList(page, pageSize, orderBy, orderType, null); + } + + @ResponseBody + @PutMapping(produces = {"application/json"}) + @RequestMapping(method = RequestMethod.GET, path = "{serviceGroupId}") + public ServiceGroupRO getServiceGroupById(@PathVariable Long serviceGroupId) { - return uiServiceGroupService.getTableList(page,pageSize, orderBy, orderType, null ); + return uiServiceGroupService.getServiceGroupById(serviceGroupId); + } + + @PutMapping(produces = {"application/json"}) + @RequestMapping(method = RequestMethod.PUT) + public void updateDomainList(@RequestBody(required = true) ServiceGroupRO[] updateEntities ){ + LOG.info("GOT LIST OF ServiceGroupRO to UPDATE: " + updateEntities.length); + uiServiceGroupService.updateServiceGroupList(Arrays.asList(updateEntities)); } } + diff --git a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/UserResource.java b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/UserResource.java index 0898cbec527ef683db4b5392a918f1e48c76f7b1..569447d41d4d543309e944f8323186077f3b4799 100644 --- a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/UserResource.java +++ b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/UserResource.java @@ -1,15 +1,28 @@ package eu.europa.ec.edelivery.smp.ui; +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.logging.SMPLogger; +import eu.europa.ec.edelivery.smp.logging.SMPLoggerFactory; import eu.europa.ec.edelivery.smp.services.ui.UIUserService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; import javax.annotation.PostConstruct; +import javax.ws.rs.Consumes; +import javax.ws.rs.POST; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import java.io.IOException; +import java.io.InputStream; +import java.security.cert.CertificateException; +import java.util.Arrays; /** * @author Joze Rihtarsic @@ -20,7 +33,7 @@ import javax.annotation.PostConstruct; @RequestMapping(value = "/ui/rest/user") public class UserResource { - private static final Logger LOGGER = LoggerFactory.getLogger(UserResource.class); + private static final SMPLogger LOG = SMPLoggerFactory.getLogger(UserResource.class); @Autowired private UIUserService uiUserService; @@ -44,4 +57,24 @@ public class UserResource { return uiUserService.getTableList(page,pageSize, orderBy, orderType, null); } + + @PutMapping(produces = {"application/json"}) + @RequestMapping(method = RequestMethod.PUT) + public void updateUserList(@RequestBody(required = true) UserRO[] updateEntities ){ + LOG.info("Update user list, count: {}" + updateEntities.length); + uiUserService.updateUserList(Arrays.asList(updateEntities)); + } + + + @RequestMapping(path = "certdata", method = RequestMethod.POST) + public CertificateRO uploadFile(@RequestBody byte[] data) { + LOG.info("Got certificate data: " + data.length); + try { + return uiUserService.getCertificateData(data); + } catch (CertificateException e) { + LOG.error("Error occured while parsing certificate.", e); + } + return null; + + } } diff --git a/smp-webapp/src/main/smp-setup/database-scripts/mysql5innodb-4.1.0-SNAPSHOT.ddl b/smp-webapp/src/main/smp-setup/database-scripts/mysql5innodb-4.1.0-SNAPSHOT.ddl index 4c8221aa86dc2cebca60beac48e5c07db0882197..00c0e53d00e4ffedee211fbeb506fe0750ba2f17 100644 --- a/smp-webapp/src/main/smp-setup/database-scripts/mysql5innodb-4.1.0-SNAPSHOT.ddl +++ b/smp-webapp/src/main/smp-setup/database-scripts/mysql5innodb-4.1.0-SNAPSHOT.ddl @@ -3,7 +3,10 @@ ID bigint not null, CERTIFICATE_ID varchar(4000) CHARACTER SET utf8 COLLATE utf8_bin, CREATED_ON datetime not null, + issuer varchar(512) CHARACTER SET utf8 COLLATE utf8_bin, LAST_UPDATED_ON datetime not null, + serialNumber varchar(128) CHARACTER SET utf8 COLLATE utf8_bin, + subject varchar(512) CHARACTER SET utf8 COLLATE utf8_bin, VALID_FROM datetime, VALID_TO datetime, primary key (ID) @@ -15,7 +18,10 @@ REVTYPE tinyint, CERTIFICATE_ID varchar(4000) CHARACTER SET utf8 COLLATE utf8_bin, CREATED_ON datetime, + issuer varchar(512) CHARACTER SET utf8 COLLATE utf8_bin, LAST_UPDATED_ON datetime, + serialNumber varchar(128) CHARACTER SET utf8 COLLATE utf8_bin, + subject varchar(512) CHARACTER SET utf8 COLLATE utf8_bin, VALID_FROM datetime, VALID_TO datetime, primary key (ID, REV) diff --git a/smp-webapp/src/main/smp-setup/database-scripts/oracle10g-4.1.0-SNAPSHOT.ddl b/smp-webapp/src/main/smp-setup/database-scripts/oracle10g-4.1.0-SNAPSHOT.ddl index d3531a4c216208f1c4bf239d2dc9432592fd9355..2cf7e6aac5b3817d92bbd49cfc784580e4f5aad9 100644 --- a/smp-webapp/src/main/smp-setup/database-scripts/oracle10g-4.1.0-SNAPSHOT.ddl +++ b/smp-webapp/src/main/smp-setup/database-scripts/oracle10g-4.1.0-SNAPSHOT.ddl @@ -9,7 +9,10 @@ create sequence SMP_USER_SEQ start with 1 increment by 50; ID number(19,0) not null, CERTIFICATE_ID varchar2(4000 char), CREATED_ON timestamp not null, + issuer varchar2(512 char), LAST_UPDATED_ON timestamp not null, + serialNumber varchar2(128 char), + subject varchar2(512 char), VALID_FROM timestamp, VALID_TO timestamp, primary key (ID) @@ -21,7 +24,10 @@ create sequence SMP_USER_SEQ start with 1 increment by 50; REVTYPE number(3,0), CERTIFICATE_ID varchar2(4000 char), CREATED_ON timestamp, + issuer varchar2(512 char), LAST_UPDATED_ON timestamp, + serialNumber varchar2(128 char), + subject varchar2(512 char), VALID_FROM timestamp, VALID_TO timestamp, primary key (ID, REV) diff --git a/smp-webapp/src/main/webapp/WEB-INF/web.xml b/smp-webapp/src/main/webapp/WEB-INF/web.xml index 4f2375f2880c40ad267ef17828dfcf90045f0457..b8ff0b251e59001364370260f7fe3815ffc6ab22 100644 --- a/smp-webapp/src/main/webapp/WEB-INF/web.xml +++ b/smp-webapp/src/main/webapp/WEB-INF/web.xml @@ -24,7 +24,22 @@ --> - <display-name>SMP - OASIS REST services</display-name> + <display-name>eDelivery SMP</display-name> + + <servlet> + <servlet-name>fistPage</servlet-name> + <jsp-file>/static_resources/index.html</jsp-file> + </servlet> + + <servlet-mapping> + <servlet-name>fistPage</servlet-name> + <url-pattern>/</url-pattern> + </servlet-mapping> + <servlet-mapping> + <servlet-name>fistPage</servlet-name> + <url-pattern>/web/index.html</url-pattern> + </servlet-mapping> + <servlet> <servlet-name>smpRestServlet</servlet-name> @@ -44,6 +59,7 @@ </servlet-mapping> + <!-- SpringSecurity & Application Context--> <context-param> diff --git a/smp-webapp/src/test/java/eu/europa/ec/edelivery/smp/ui/ServiceGroupResourceTest.java b/smp-webapp/src/test/java/eu/europa/ec/edelivery/smp/ui/ServiceGroupResourceTest.java new file mode 100644 index 0000000000000000000000000000000000000000..ab02e47f6b1f5099943d97349287eb41ab874ddc --- /dev/null +++ b/smp-webapp/src/test/java/eu/europa/ec/edelivery/smp/ui/ServiceGroupResourceTest.java @@ -0,0 +1,145 @@ +package eu.europa.ec.edelivery.smp.ui; + +import com.fasterxml.jackson.databind.ObjectMapper; +import eu.europa.ec.edelivery.smp.config.PropertiesTestConfig; +import eu.europa.ec.edelivery.smp.config.SmpAppConfig; +import eu.europa.ec.edelivery.smp.config.SmpWebAppConfig; +import eu.europa.ec.edelivery.smp.config.SpringSecurityConfig; +import eu.europa.ec.edelivery.smp.data.ui.ServiceGroupRO; +import eu.europa.ec.edelivery.smp.data.ui.ServiceResult; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.mock.web.MockServletContext; +import org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.jdbc.Sql; +import org.springframework.test.context.jdbc.SqlConfig; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.web.WebAppConfiguration; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.MvcResult; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import org.springframework.web.context.ContextLoaderListener; +import org.springframework.web.context.WebApplicationContext; + +import javax.servlet.ServletContextEvent; +import javax.servlet.ServletContextListener; + +import static org.junit.Assert.*; +import static org.springframework.http.MediaType.APPLICATION_JSON; +import static org.springframework.http.MediaType.APPLICATION_XML_VALUE; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +/** + * @author Joze Rihtarsic + * @since 4.1 + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(classes = { + PropertiesTestConfig.class, + SmpAppConfig.class, + SmpWebAppConfig.class, + SpringSecurityConfig.class}) +@WebAppConfiguration +@Sql("classpath:/cleanup-database.sql") +@Sql("classpath:/webapp_integration_test_data.sql") +@SqlConfig(encoding = "UTF-8") +public class ServiceGroupResourceTest { + + private static final String PATH="/ui/rest/servicegroup"; + + private static final String PARTICIPANT_IDENTIFIER= "urn:australia:ncpb"; + private static final String PARTICIPANT_SCHEME= "ehealth-actorid-qns"; + + @Autowired + private WebApplicationContext webAppContext; + + private MockMvc mvc; + + @Before + public void setup() { + mvc = MockMvcBuilders.webAppContextSetup(webAppContext) + .apply(SecurityMockMvcConfigurers.springSecurity()) + .build(); + + initServletContext(); + } + + private void initServletContext() { + MockServletContext sc = new MockServletContext(""); + ServletContextListener listener = new ContextLoaderListener(webAppContext); + ServletContextEvent event = new ServletContextEvent(sc); + } + + @Test + public void getServiceGroupList() throws Exception { + // given when + MvcResult result = mvc.perform(get(PATH)). + andExpect(status().isOk()).andReturn(); + + //them + ObjectMapper mapper = new ObjectMapper(); + ServiceResult res = mapper.readValue(result.getResponse().getContentAsString(), ServiceResult.class); + + + assertNotNull(res); + assertEquals(2, res.getServiceEntities().size()); + res.getServiceEntities().forEach(sgMap-> { + ServiceGroupRO sgro = mapper.convertValue(sgMap, ServiceGroupRO.class); + assertNotNull(sgro.getId()); + assertNotNull(sgro.getParticipantScheme()); + assertNotNull(sgro.getParticipantIdentifier()); + assertEquals(1, sgro.getUsers().size()); + assertEquals("test_user_hashed_pass", sgro.getUsers().get(0).getUsername()); + }); + } + + @Test + public void getServiceGroupById() throws Exception{ + + // given when + MvcResult result = mvc.perform(get(PATH+"/100000")). + andExpect(status().isOk()).andReturn(); + + //them + ObjectMapper mapper = new ObjectMapper(); + ServiceGroupRO res = mapper.readValue(result.getResponse().getContentAsString(), ServiceGroupRO.class); + + assertNotNull(res); + assertEquals(100000, res.getId().intValue()); + assertEquals(PARTICIPANT_IDENTIFIER, res.getParticipantIdentifier()); + assertEquals(PARTICIPANT_SCHEME, res.getParticipantScheme()); + assertEquals(1, res.getUsers().size()); + assertEquals("test_user_hashed_pass", res.getUsers().get(0).getUsername()); + + assertEquals(1, res.getServiceMetadata().size()); + assertEquals("doc_7", res.getServiceMetadata().get(0).getDocumentIdentifier()); + } + + + @Test + public void updateServiceGroupList() throws Exception{ +/* + // given when + MvcResult result = mvc.perform(put(PATH)). + andExpect(status().isOk()).andReturn(); + + //them + ObjectMapper mapper = new ObjectMapper(); + ServiceGroupRO res = mapper.readValue(result.getResponse().getContentAsString(), ServiceGroupRO.class); + + assertNotNull(res); + assertEquals(100000, res.getId().intValue()); + assertEquals(PARTICIPANT_IDENTIFIER, res.getParticipantIdentifier()); + assertEquals(PARTICIPANT_SCHEME, res.getParticipantScheme()); + assertEquals(1, res.getUsers().size()); + assertEquals("test_user_hashed_pass", res.getUsers().get(0).getUsername()); + + assertEquals(1, res.getServiceMetadata().size()); + assertEquals("doc_7", res.getServiceMetadata().get(0).getDocumentIdentifier());*/ + } +} \ No newline at end of file diff --git a/smp-webapp/src/test/java/eu/europa/ec/edelivery/smp/ui/UserResourceTest.java b/smp-webapp/src/test/java/eu/europa/ec/edelivery/smp/ui/UserResourceTest.java new file mode 100644 index 0000000000000000000000000000000000000000..6b41f4d018e725e198728d098492a5aeacfcbf3a --- /dev/null +++ b/smp-webapp/src/test/java/eu/europa/ec/edelivery/smp/ui/UserResourceTest.java @@ -0,0 +1,96 @@ +package eu.europa.ec.edelivery.smp.ui; + +import com.fasterxml.jackson.databind.ObjectMapper; +import eu.europa.ec.edelivery.smp.config.PropertiesTestConfig; +import eu.europa.ec.edelivery.smp.config.SmpAppConfig; +import eu.europa.ec.edelivery.smp.config.SmpWebAppConfig; +import eu.europa.ec.edelivery.smp.config.SpringSecurityConfig; +import eu.europa.ec.edelivery.smp.data.ui.ServiceGroupRO; +import eu.europa.ec.edelivery.smp.data.ui.ServiceResult; +import eu.europa.ec.edelivery.smp.data.ui.UserRO; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.mock.web.MockServletContext; +import org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.jdbc.Sql; +import org.springframework.test.context.jdbc.SqlConfig; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.web.WebAppConfiguration; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.MvcResult; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import org.springframework.web.context.ContextLoaderListener; +import org.springframework.web.context.WebApplicationContext; + +import javax.servlet.ServletContextEvent; +import javax.servlet.ServletContextListener; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +/** + * @author Joze Rihtarsic + * @since 4.1 + */ +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(classes = { + PropertiesTestConfig.class, + SmpAppConfig.class, + SmpWebAppConfig.class, + SpringSecurityConfig.class}) +@WebAppConfiguration +@Sql("classpath:/cleanup-database.sql") +@Sql("classpath:/webapp_integration_test_data.sql") +@SqlConfig(encoding = "UTF-8") +public class UserResourceTest { + + private static final String PATH="/ui/rest/user"; + + @Autowired + private WebApplicationContext webAppContext; + + private MockMvc mvc; + + @Before + public void setup() { + mvc = MockMvcBuilders.webAppContextSetup(webAppContext) + .apply(SecurityMockMvcConfigurers.springSecurity()) + .build(); + + initServletContext(); + } + + private void initServletContext() { + MockServletContext sc = new MockServletContext(""); + ServletContextListener listener = new ContextLoaderListener(webAppContext); + ServletContextEvent event = new ServletContextEvent(sc); + } + + @Test + public void getUserList() throws Exception { + // given when + MvcResult result = mvc.perform(get(PATH)). + andExpect(status().isOk()).andReturn(); + + //them + ObjectMapper mapper = new ObjectMapper(); + ServiceResult res = mapper.readValue(result.getResponse().getContentAsString(), ServiceResult.class); + + + assertNotNull(res); + assertEquals(6, res.getServiceEntities().size()); + res.getServiceEntities().forEach(sgMap-> { + UserRO sgro = mapper.convertValue(sgMap, UserRO.class); + assertNotNull(sgro.getId()); + assertNotNull(sgro.getUsername()); + assertNotNull(sgro.getRole()); + }); + } + + +} \ No newline at end of file diff --git a/smp-webapp/src/test/resources/webapp_integration_test_data.sql b/smp-webapp/src/test/resources/webapp_integration_test_data.sql index c049c0018ab9fee8ef31e7d041e2ae8c32b61350..0c02bb89da6094a8c67a5385d6b4b75f66aeec71 100644 --- a/smp-webapp/src/test/resources/webapp_integration_test_data.sql +++ b/smp-webapp/src/test/resources/webapp_integration_test_data.sql @@ -23,13 +23,16 @@ insert into SMP_CERTIFICATE (ID, CERTIFICATE_ID, VALID_FROM, VALID_TO, CREATED_O -- set the ids to higher values - tests are using sequnce which stars from 1 insert into SMP_SERVICE_GROUP(ID, PARTICIPANT_IDENTIFIER, PARTICIPANT_SCHEME,SML_REGISTRED, CREATED_ON, LAST_UPDATED_ON) values (100000, 'urn:australia:ncpb', 'ehealth-actorid-qns', 1,CURRENT_TIMESTAMP(),CURRENT_TIMESTAMP()); insert into SMP_SERVICE_GROUP(ID, PARTICIPANT_IDENTIFIER, PARTICIPANT_SCHEME,SML_REGISTRED, CREATED_ON, LAST_UPDATED_ON) values (200000, 'urn:brazil:ncpb', 'ehealth-actorid-qns', 1,CURRENT_TIMESTAMP(),CURRENT_TIMESTAMP()); ---insert into SMP_SERVICE_GROUP(ID, PARTICIPANT_IDENTIFIER, PARTICIPANT_SCHEME,SML_REGISTRED) values (3, 'urn:poland:ncpb', 'ehealth-participantid-qns', 1); + +-- set ownership +insert into SMP_OWNERSHIP (FK_SG_ID, FK_USER_ID) values (100000, 2); +insert into SMP_OWNERSHIP (FK_SG_ID, FK_USER_ID) values (200000, 2); insert into SMP_DOMAIN (ID, DOMAIN_CODE, SML_SUBDOMAIN, SML_SMP_ID, SIGNATURE_KEY_ALIAS, CREATED_ON, LAST_UPDATED_ON) values (1, 'domain','subdomain', 'CEF-SMP-001','sig-key',CURRENT_TIMESTAMP(),CURRENT_TIMESTAMP()); - --insert into SMP_OWNERSHIP (FK_SG_ID, FK_USER_ID) values (3, 1); ---insert into SMP_SERVICE_GROUP_DOMAIN (ID, FK_SG_ID, FK_DOMAIN_ID) values (1, 3, 1); --- insert into smp_ownership(username, businessidentifier, businessidentifierscheme) values ('CN=EHEALTH_SMP_TEST_BRAZIL,O=European Commission,C=BE:48b681ee8e0dcc08', 'urn:australia:ncpb', 'ehealth-actorid-qns'); +insert into SMP_SERVICE_GROUP_DOMAIN (ID, FK_SG_ID, FK_DOMAIN_ID, CREATED_ON, LAST_UPDATED_ON) values (1000,200000, 1,CURRENT_TIMESTAMP(),CURRENT_TIMESTAMP()); +insert into SMP_SERVICE_GROUP_DOMAIN (ID, FK_SG_ID, FK_DOMAIN_ID, CREATED_ON, LAST_UPDATED_ON) values (1001,100000, 1,CURRENT_TIMESTAMP(),CURRENT_TIMESTAMP()); +insert into SMP_SERVICE_METADATA (ID, FK_SG_DOM_ID, DOCUMENT_IDENTIFIER, DOCUMENT_SCHEME, LAST_UPDATED_ON, CREATED_ON) values (1000,1001,'doc_7','busdox-docid-qns',CURRENT_TIMESTAMP(),CURRENT_TIMESTAMP());