From 8acca9aa806b42c081add00de6563659fa98b046 Mon Sep 17 00:00:00 2001 From: RIHTARSIC Joze <joze.rihtarsic@ext.ec.europa.eu> Date: Fri, 14 Apr 2023 17:05:01 +0200 Subject: [PATCH] Implement UI domain edit --- smp-angular/src/app/app.module.ts | 8 +- smp-angular/src/app/app.routes.ts | 4 +- .../alert-message/alert-message.component.ts | 2 - .../member-dialog/member-dialog.component.css | 0 .../member-dialog.component.html | 2 +- .../member-dialog/member-dialog.component.ts | 16 +-- .../search-user-ro.model.ts | 2 +- .../domain-member-panel.component.html | 1 + .../domain-member-panel.component.ts | 15 +- .../domain-member-panel/membership.service.ts | 7 +- .../user-profile-panel.component.html | 2 +- .../domain-group.component.html | 1 + .../domain-group.component.ts | 45 +++++- .../group-dialog/group-dialog.component.css | 13 ++ .../group-dialog/group-dialog.component.html | 46 ++++++ .../group-dialog/group-dialog.component.ts | 134 ++++++++++++++++++ .../edit/edit-domain/edit-domain.component.ts | 1 - .../edit/edit-domain/edit-domain.service.ts | 21 ++- .../edit/edit-group/edit-group.component.css | 28 ++++ .../edit/edit-group/edit-group.component.html | 44 ++++++ .../edit/edit-group/edit-group.component.ts | 64 +++++++++ .../app/edit/edit-group/edit-group.service.ts | 40 ++++++ .../domain-panel/domain-panel.component.html | 30 ++-- ...omain-sml-integration-panel.component.html | 50 +++---- .../admin-users/admin-user.component.html | 35 ++--- .../admin-users/admin-user.component.ts | 2 +- smp-angular/src/theme.scss | 30 +++- .../smp/data/dao/DomainMemberDao.java | 6 +- .../ec/edelivery/smp/data/dao/GroupDao.java | 21 +++ .../smp/data/dao/GroupMemberDao.java | 54 ++++++- .../ec/edelivery/smp/data/dao/QueryNames.java | 7 + .../ec/edelivery/smp/data/model/DBGroup.java | 15 +- .../smp/data/model/user/DBGroupMember.java | 52 +++++-- .../smp/services/ui/UIGroupPublicService.java | 53 +++++-- .../smp/data/dao/GroupMemberDaoTest.java | 48 +++++++ .../ec/springboot/smp/SMPApplication.java | 1 + .../smp/auth/SMPAuthorizationService.java | 24 ++-- .../edelivery/smp/ui/ResourceConstants.java | 2 + .../smp/ui/external/DomainResource.java | 13 +- .../smp/ui/external/GroupResource.java | 28 +++- .../smp/ui/external/UserResource.java | 8 +- .../smp/auth/SMPAuthorizationServiceTest.java | 6 +- 42 files changed, 830 insertions(+), 151 deletions(-) rename smp-angular/src/app/common/{panels/domain-member-panel => dialogs}/member-dialog/member-dialog.component.css (100%) rename smp-angular/src/app/common/{panels/domain-member-panel => dialogs}/member-dialog/member-dialog.component.html (97%) rename smp-angular/src/app/common/{panels/domain-member-panel => dialogs}/member-dialog/member-dialog.component.ts (88%) rename smp-angular/src/app/common/{panels/domain-member-panel/member-dialog => model}/search-user-ro.model.ts (59%) create mode 100644 smp-angular/src/app/edit/edit-domain/domain-group-panel/group-dialog/group-dialog.component.css create mode 100644 smp-angular/src/app/edit/edit-domain/domain-group-panel/group-dialog/group-dialog.component.html create mode 100644 smp-angular/src/app/edit/edit-domain/domain-group-panel/group-dialog/group-dialog.component.ts create mode 100644 smp-angular/src/app/edit/edit-group/edit-group.component.css create mode 100644 smp-angular/src/app/edit/edit-group/edit-group.component.html create mode 100644 smp-angular/src/app/edit/edit-group/edit-group.component.ts create mode 100644 smp-angular/src/app/edit/edit-group/edit-group.service.ts diff --git a/smp-angular/src/app/app.module.ts b/smp-angular/src/app/app.module.ts index 77c1b3578..14a19afcd 100644 --- a/smp-angular/src/app/app.module.ts +++ b/smp-angular/src/app/app.module.ts @@ -130,7 +130,7 @@ import { } from "./common/panels/domain-member-panel/domain-member-panel.component"; import { MemberDialogComponent -} from "./common/panels/domain-member-panel/member-dialog/member-dialog.component"; +} from "./common/dialogs/member-dialog/member-dialog.component"; import {MatAutocompleteModule} from "@angular/material/autocomplete"; import {MembershipService} from "./common/panels/domain-member-panel/membership.service"; import {AdminUserComponent} from "./system-settings/admin-users/admin-user.component"; @@ -142,6 +142,9 @@ import {EditDomainComponent} from "./edit/edit-domain/edit-domain.component"; import {EditDomainService} from "./edit/edit-domain/edit-domain.service"; import {SmpFieldErrorComponent} from "./common/components/smp-field-error/smp-field-error.component"; import {DomainGroupComponent} from "./edit/edit-domain/domain-group-panel/domain-group.component"; +import {GroupDialogComponent} from "./edit/edit-domain/domain-group-panel/group-dialog/group-dialog.component"; +import {EditGroupComponent} from "./edit/edit-group/edit-group.component"; +import {EditGroupService} from "./edit/edit-group/edit-group.service"; @NgModule({ @@ -179,9 +182,11 @@ import {DomainGroupComponent} from "./edit/edit-domain/domain-group-panel/domain DomainResourceTypePanelComponent, DomainSelectorComponent, EditDomainComponent, + EditGroupComponent, ExpiredPasswordDialogComponent, ExtensionComponent, ExtensionPanelComponent, + GroupDialogComponent, FooterComponent, InformationDialogComponent, IsAuthorized, @@ -271,6 +276,7 @@ import {DomainGroupComponent} from "./edit/edit-domain/domain-group-panel/domain MembershipService, DownloadService, EditDomainService, + EditGroupService, ExtensionService, GlobalLookups, HttpEventService, diff --git a/smp-angular/src/app/app.routes.ts b/smp-angular/src/app/app.routes.ts index d7988abf5..7ff9afaa2 100644 --- a/smp-angular/src/app/app.routes.ts +++ b/smp-angular/src/app/app.routes.ts @@ -1,7 +1,6 @@ import {RouterModule, Routes} from '@angular/router'; import {LoginComponent} from './login/login.component'; import {ServiceGroupSearchComponent} from './service-group-search/service-group-search.component'; -import {UserComponent} from './system-settings/user/user.component'; import {AlertComponent} from "./alert/alert.component"; import {PropertyComponent} from "./system-settings/property/property.component"; import {UserProfileComponent} from "./user-settings/user-profile/user-profile.component"; @@ -15,6 +14,7 @@ import {AdminDomainComponent} from "./system-settings/admin-domain/admin-domain. import {dirtyDeactivateGuard} from "./guards/dirty.guard"; import {AdminUserComponent} from "./system-settings/admin-users/admin-user.component"; import {EditDomainComponent} from "./edit/edit-domain/edit-domain.component"; +import {EditGroupComponent} from "./edit/edit-group/edit-group.component"; const appRoutes: Routes = [ @@ -27,7 +27,7 @@ const appRoutes: Routes = [ canActivateChild: [authenticationGuard], children: [ {path: 'edit-domain', component: EditDomainComponent, canDeactivate: [dirtyDeactivateGuard]}, - {path: 'edit-group', component: UserComponent, canDeactivate: [dirtyDeactivateGuard]}, + {path: 'edit-group', component: EditGroupComponent, canDeactivate: [dirtyDeactivateGuard]}, {path: 'edit-resource', component: PropertyComponent, canDeactivate: [dirtyDeactivateGuard]} ] }, diff --git a/smp-angular/src/app/common/alert-message/alert-message.component.ts b/smp-angular/src/app/common/alert-message/alert-message.component.ts index 908ab6243..60edc305f 100644 --- a/smp-angular/src/app/common/alert-message/alert-message.component.ts +++ b/smp-angular/src/app/common/alert-message/alert-message.component.ts @@ -25,9 +25,7 @@ export class AlertMessageComponent implements OnInit { } setSticky(sticky: boolean):void { - console.log("set sticky " + sticky) this.showSticky = sticky; - } get messageText(){ diff --git a/smp-angular/src/app/common/panels/domain-member-panel/member-dialog/member-dialog.component.css b/smp-angular/src/app/common/dialogs/member-dialog/member-dialog.component.css similarity index 100% rename from smp-angular/src/app/common/panels/domain-member-panel/member-dialog/member-dialog.component.css rename to smp-angular/src/app/common/dialogs/member-dialog/member-dialog.component.css diff --git a/smp-angular/src/app/common/panels/domain-member-panel/member-dialog/member-dialog.component.html b/smp-angular/src/app/common/dialogs/member-dialog/member-dialog.component.html similarity index 97% rename from smp-angular/src/app/common/panels/domain-member-panel/member-dialog/member-dialog.component.html rename to smp-angular/src/app/common/dialogs/member-dialog/member-dialog.component.html index 66c9c2922..2f5eda50a 100644 --- a/smp-angular/src/app/common/panels/domain-member-panel/member-dialog/member-dialog.component.html +++ b/smp-angular/src/app/common/dialogs/member-dialog/member-dialog.component.html @@ -28,7 +28,7 @@ {{role.key}} </mat-option> </mat-select> - <mat-hint> Chose member role</mat-hint> + <mat-hint> Choose member role</mat-hint> </mat-form-field> </form> </mat-dialog-content> diff --git a/smp-angular/src/app/common/panels/domain-member-panel/member-dialog/member-dialog.component.ts b/smp-angular/src/app/common/dialogs/member-dialog/member-dialog.component.ts similarity index 88% rename from smp-angular/src/app/common/panels/domain-member-panel/member-dialog/member-dialog.component.ts rename to smp-angular/src/app/common/dialogs/member-dialog/member-dialog.component.ts index e7fa0916f..880c9a5b2 100644 --- a/smp-angular/src/app/common/panels/domain-member-panel/member-dialog/member-dialog.component.ts +++ b/smp-angular/src/app/common/dialogs/member-dialog/member-dialog.component.ts @@ -1,14 +1,14 @@ import {Component, Inject, Input, OnInit} from '@angular/core'; import {MAT_DIALOG_DATA, MatDialogRef} from '@angular/material/dialog'; import {FormBuilder, FormControl, FormGroup} from "@angular/forms"; -import {MembershipRoleEnum} from "../../../enums/membership-role.enum"; +import {MembershipRoleEnum} from "../../enums/membership-role.enum"; import {Observable} from "rxjs"; -import {SearchUserRo} from "./search-user-ro.model"; -import {MembershipService} from "../membership.service"; -import {MemberRo} from "../../../model/member-ro.model"; -import {DomainRo} from "../../../model/domain-ro.model"; -import {MemberTypeEnum} from "../../../enums/member-type.enum"; -import {AlertMessageService} from "../../../alert-message/alert-message.service"; +import {SearchUserRo} from "../../model/search-user-ro.model"; +import {MembershipService} from "../../panels/domain-member-panel/membership.service"; +import {MemberRo} from "../../model/member-ro.model"; +import {DomainRo} from "../../model/domain-ro.model"; +import {MemberTypeEnum} from "../../enums/member-type.enum"; +import {AlertMessageService} from "../../alert-message/alert-message.service"; @Component({ @@ -92,7 +92,7 @@ export class MemberDialogComponent implements OnInit { this.memberForm.controls['member-memberOf'].setValue(""); this.memberForm.controls['member-roleType'].setValue(""); } - + this.memberForm.markAsPristine(); } ngOnInit() { diff --git a/smp-angular/src/app/common/panels/domain-member-panel/member-dialog/search-user-ro.model.ts b/smp-angular/src/app/common/model/search-user-ro.model.ts similarity index 59% rename from smp-angular/src/app/common/panels/domain-member-panel/member-dialog/search-user-ro.model.ts rename to smp-angular/src/app/common/model/search-user-ro.model.ts index 4b96bc262..e47992589 100644 --- a/smp-angular/src/app/common/panels/domain-member-panel/member-dialog/search-user-ro.model.ts +++ b/smp-angular/src/app/common/model/search-user-ro.model.ts @@ -1,4 +1,4 @@ -import {SearchTableEntity} from "../../../search-table/search-table-entity.model"; +import {SearchTableEntity} from "../search-table/search-table-entity.model"; export interface SearchUserRo extends SearchTableEntity { userId: string, diff --git a/smp-angular/src/app/common/panels/domain-member-panel/domain-member-panel.component.html b/smp-angular/src/app/common/panels/domain-member-panel/domain-member-panel.component.html index bcbf9dfe2..4148b0542 100644 --- a/smp-angular/src/app/common/panels/domain-member-panel/domain-member-panel.component.html +++ b/smp-angular/src/app/common/panels/domain-member-panel/domain-member-panel.component.html @@ -62,6 +62,7 @@ <tr mat-header-row *matHeaderRowDef="displayedColumns"></tr> <tr mat-row *matRowDef="let odd = odd; let row; columns: displayedColumns;" (click)="memberSelected(row)" + (dblclick)="showEditDailogForMember(row)" [ngClass]="{'datatable-row-selected': row==selectedMember,'datatable-row-odd': odd}" ></tr> diff --git a/smp-angular/src/app/common/panels/domain-member-panel/domain-member-panel.component.ts b/smp-angular/src/app/common/panels/domain-member-panel/domain-member-panel.component.ts index 9b22863f6..865f4af18 100644 --- a/smp-angular/src/app/common/panels/domain-member-panel/domain-member-panel.component.ts +++ b/smp-angular/src/app/common/panels/domain-member-panel/domain-member-panel.component.ts @@ -1,4 +1,4 @@ -import {Component, EventEmitter, Input, Output, ViewChild,} from '@angular/core'; +import {Component, Input, ViewChild,} from '@angular/core'; import {DomainRo} from "../../model/domain-ro.model"; import {FormBuilder, FormControl, FormGroup} from "@angular/forms"; import {AdminDomainService} from "../../../system-settings/admin-domain/admin-domain.service"; @@ -9,7 +9,7 @@ import {MatPaginator, PageEvent} from "@angular/material/paginator"; import {MemberRo} from "../../model/member-ro.model"; import {finalize} from "rxjs/operators"; import {TableResult} from "../../model/table-result.model"; -import {MemberDialogComponent} from "./member-dialog/member-dialog.component"; +import {MemberDialogComponent} from "../../dialogs/member-dialog/member-dialog.component"; import {MembershipService} from "./membership.service"; import {MembershipRoleEnum} from "../../enums/membership-role.enum"; import {MemberTypeEnum} from "../../enums/member-type.enum"; @@ -136,22 +136,27 @@ export class DomainMemberPanelComponent implements BeforeLeaveGuard { } as MemberRo } - public onEditSelectedButtonClicked() { + this.showEditDailogForMember(this.selectedMember); + } + + public showEditDailogForMember(member: MemberRo) { this.dialog.open(MemberDialogComponent, { data: { domain: this._domain, - member: this.selectedMember, + member: member, formTitle: "Edit member role for domain" } }).afterClosed().subscribe(value => { this.refresh(); }); } + public onDeleteSelectedButtonClicked() { this.membershipService.deleteMemberFromDomain(this._domain.domainId, this.selectedMember).subscribe(value => { this.refresh(); - });; + }); + ; } isDirty(): boolean { diff --git a/smp-angular/src/app/common/panels/domain-member-panel/membership.service.ts b/smp-angular/src/app/common/panels/domain-member-panel/membership.service.ts index ba50f8dd3..1dae173c9 100644 --- a/smp-angular/src/app/common/panels/domain-member-panel/membership.service.ts +++ b/smp-angular/src/app/common/panels/domain-member-panel/membership.service.ts @@ -8,8 +8,7 @@ import {SecurityService} from "../../../security/security.service"; import {AlertMessageService} from "../../alert-message/alert-message.service"; import {MemberRo} from "../../model/member-ro.model"; import {TableResult} from "../../model/table-result.model"; -import {SearchUserRo} from "./member-dialog/search-user-ro.model"; -import {MembershipRoleEnum} from "../../enums/membership-role.enum"; +import {SearchUserRo} from "../../model/search-user-ro.model"; @Injectable() @@ -56,10 +55,10 @@ export class MembershipService { return this.http.put<MemberRo>(SmpConstants.REST_PUBLIC_DOMAIN_MEMBERS_ADD .replace(SmpConstants.PATH_PARAM_ENC_USER_ID, currentUser.userId) - .replace(SmpConstants.PATH_PARAM_ENC_DOMAIN_ID, domainId),member); + .replace(SmpConstants.PATH_PARAM_ENC_DOMAIN_ID, domainId), member); } - deleteMemberFromDomain( domainId: string, member: MemberRo): Observable<MemberRo> { + deleteMemberFromDomain(domainId: string, member: MemberRo): Observable<MemberRo> { const currentUser: User = this.securityService.getCurrentUser(); return this.http.delete<MemberRo>(SmpConstants.REST_PUBLIC_DOMAIN_MEMBERS_DELETE diff --git a/smp-angular/src/app/common/panels/user-settings-panel/user-profile-panel.component.html b/smp-angular/src/app/common/panels/user-settings-panel/user-profile-panel.component.html index 4450cc236..8d8bf6314 100644 --- a/smp-angular/src/app/common/panels/user-settings-panel/user-profile-panel.component.html +++ b/smp-angular/src/app/common/panels/user-settings-panel/user-profile-panel.component.html @@ -23,7 +23,7 @@ {{role.key}} </mat-option> </mat-select> - <mat-hint> Chose member role</mat-hint> + <mat-hint> Choose member role</mat-hint> </mat-form-field> <mat-checkbox diff --git a/smp-angular/src/app/edit/edit-domain/domain-group-panel/domain-group.component.html b/smp-angular/src/app/edit/edit-domain/domain-group-panel/domain-group.component.html index bb6abd3c1..ebcc96f4d 100644 --- a/smp-angular/src/app/edit/edit-domain/domain-group-panel/domain-group.component.html +++ b/smp-angular/src/app/edit/edit-domain/domain-group-panel/domain-group.component.html @@ -56,6 +56,7 @@ <tr mat-header-row *matHeaderRowDef="displayedColumns"></tr> <tr mat-row *matRowDef="let odd = odd; let row; columns: displayedColumns;" (click)="onGroupSelected(row)" + (dblclick)="showEditDialogForGroup(row)" [ngClass]="{'datatable-row-selected': row==selectedGroup,'datatable-row-odd': odd}" ></tr> diff --git a/smp-angular/src/app/edit/edit-domain/domain-group-panel/domain-group.component.ts b/smp-angular/src/app/edit/edit-domain/domain-group-panel/domain-group.component.ts index 5c9e15896..ad9397ac6 100644 --- a/smp-angular/src/app/edit/edit-domain/domain-group-panel/domain-group.component.ts +++ b/smp-angular/src/app/edit/edit-domain/domain-group-panel/domain-group.component.ts @@ -1,4 +1,4 @@ -import {Component, Input,} from '@angular/core'; +import {Component, Input, ViewChild,} from '@angular/core'; import {FormBuilder} from "@angular/forms"; import {AlertMessageService} from "../../../common/alert-message/alert-message.service"; import {MatDialog} from "@angular/material/dialog"; @@ -8,6 +8,9 @@ import {finalize} from "rxjs/operators"; import {MatTableDataSource} from "@angular/material/table"; import {GroupRo} from "../../../common/model/group-ro.model"; import {EditDomainService} from "../edit-domain.service"; +import {GroupDialogComponent} from "./group-dialog/group-dialog.component"; +import {VisibilityEnum} from "../../../common/enums/visibility.enum"; +import {MatPaginator} from "@angular/material/paginator"; @Component({ selector: 'domain-group-panel', @@ -28,6 +31,8 @@ export class DomainGroupComponent implements BeforeLeaveGuard { selectedGroup: GroupRo; + @ViewChild(MatPaginator) paginator: MatPaginator; + constructor(private editDomainService: EditDomainService, private alertService: AlertMessageService, private dialog: MatDialog, @@ -57,6 +62,13 @@ export class DomainGroupComponent implements BeforeLeaveGuard { } } + public refresh() { + if (this.paginator) { + this.paginator.firstPage(); + } + this.loadTableData(); + } + loadTableData() { if (!this._domain) { this.dataSource.data = null; @@ -83,9 +95,31 @@ export class DomainGroupComponent implements BeforeLeaveGuard { } onAddButtonClicked() { + this.dialog.open(GroupDialogComponent, { + data: { + domain: this._domain, + group: this.createGroup(), + formTitle: "Invite new member to domain" + } + }).afterClosed().subscribe(value => { + this.refresh(); + }); }; onEditSelectedButtonClicked() { + this.showEditDialogForGroup(this.selectedGroup); + }; + + showEditDialogForGroup(group:GroupRo) { + this.dialog.open(GroupDialogComponent, { + data: { + domain: this._domain, + group: group, + formTitle: "Invite new member to domain" + } + }).afterClosed().subscribe(value => { + this.refresh(); + }); }; onDeleteSelectedButtonClicked() { @@ -102,10 +136,17 @@ export class DomainGroupComponent implements BeforeLeaveGuard { } }, (error) => { this.alertService.error(error.error?.errorDescription) - } + } ) }; + public createGroup(): GroupRo { + return { + visibility: VisibilityEnum.Public + } as GroupRo + } + + onGroupSelected(group: GroupRo) { this.selectedGroup = group; } diff --git a/smp-angular/src/app/edit/edit-domain/domain-group-panel/group-dialog/group-dialog.component.css b/smp-angular/src/app/edit/edit-domain/domain-group-panel/group-dialog/group-dialog.component.css new file mode 100644 index 000000000..1c224cec9 --- /dev/null +++ b/smp-angular/src/app/edit/edit-domain/domain-group-panel/group-dialog/group-dialog.component.css @@ -0,0 +1,13 @@ +.empty-field-label { + color: gray; +} + + +#custom-file-upload { + display: none; +} + +.custom-file-upload { + display: inline-block; + cursor: pointer; +} diff --git a/smp-angular/src/app/edit/edit-domain/domain-group-panel/group-dialog/group-dialog.component.html b/smp-angular/src/app/edit/edit-domain/domain-group-panel/group-dialog/group-dialog.component.html new file mode 100644 index 000000000..a7e701006 --- /dev/null +++ b/smp-angular/src/app/edit/edit-domain/domain-group-panel/group-dialog/group-dialog.component.html @@ -0,0 +1,46 @@ +<h2 mat-dialog-title>{{formTitle}}</h2> +<mat-dialog-content style="width:700px"> + <form [formGroup]="groupForm"> + <b *ngIf="newMode">To create a new group enter unique group name for the domain and click save.</b> + <mat-form-field style="width: 100%"> + <mat-label>Group name</mat-label> + <input id="name_id" type="text" matInput formControlName="name" + required> + <mat-hint >Unique group name for the domain</mat-hint> + </mat-form-field> + + <mat-form-field style="width: 100%"> + <mat-label>Group description</mat-label> + <input id="description_id" type="text" matInput + formControlName="description" + > + </mat-form-field> + + + <mat-form-field style="width:100%"> + <mat-label>Group visibility</mat-label> + <mat-select placeholder="Group visibility" + formControlName="visibility" + matTooltip="Group visibility." + id="group_id" required> + <mat-option *ngFor="let visibility of groupVisibilityOptions" + [value]="visibility.value"> + {{visibility.key}} + </mat-option> + </mat-select> + <mat-hint>Choose group visibility</mat-hint> + </mat-form-field> + </form> +</mat-dialog-content> +<mat-dialog-actions> + <button mat-raised-button color="primary" (click)="closeDialog()"> + <mat-icon>cancel</mat-icon> + <span>Close</span> + </button> + <button id="saveButton" mat-raised-button (click)="onSaveButtonClicked()" color="primary" + [disabled]="!submitButtonEnabled"> + <mat-icon>save</mat-icon> + <span>Save</span> + </button> +</mat-dialog-actions> + diff --git a/smp-angular/src/app/edit/edit-domain/domain-group-panel/group-dialog/group-dialog.component.ts b/smp-angular/src/app/edit/edit-domain/domain-group-panel/group-dialog/group-dialog.component.ts new file mode 100644 index 000000000..fc30e583e --- /dev/null +++ b/smp-angular/src/app/edit/edit-domain/domain-group-panel/group-dialog/group-dialog.component.ts @@ -0,0 +1,134 @@ +import {Component, Inject, Input} from '@angular/core'; +import {MAT_DIALOG_DATA, MatDialogRef} from '@angular/material/dialog'; +import {FormBuilder, FormControl, FormGroup} from "@angular/forms"; +import {DomainRo} from "../../../../common/model/domain-ro.model"; +import {AlertMessageService} from "../../../../common/alert-message/alert-message.service"; +import {VisibilityEnum} from "../../../../common/enums/visibility.enum"; +import {GroupRo} from "../../../../common/model/group-ro.model"; +import {EditDomainService} from "../../edit-domain.service"; + + +@Component({ + templateUrl: './group-dialog.component.html', + styleUrls: ['./group-dialog.component.css'] +}) +export class GroupDialogComponent { + + readonly groupVisibilityOptions = Object.keys(VisibilityEnum) + .filter(el => el !== "Private").map(el => { + return {key: el, value: VisibilityEnum[el]} + }); + formTitle = "Group dialog"; + groupForm: FormGroup; + + message: string; + messageType: string = "alert-error"; + + _group: GroupRo; + _currentDomain: DomainRo; + + constructor(@Inject(MAT_DIALOG_DATA) public data: any, + private editDomainService: EditDomainService, + public dialogRef: MatDialogRef<GroupDialogComponent>, + private alertService: AlertMessageService, + private formBuilder: FormBuilder + ) { + dialogRef.disableClose = true;//disable default close operation + this.formTitle = data.formTitle; + this._currentDomain = data.domain; + + this.groupForm = formBuilder.group({ + 'name': new FormControl({value: null}), + 'description': new FormControl({value: null}), + 'visibility': new FormControl({value: null}), + '': new FormControl({value: null}) + }); + this.group = data.group; + } + + get newMode(): boolean { + return !this._group?.groupId + } + + get group(): GroupRo { + let group = {...this._group}; + group.groupName = this.groupForm.get('name').value; + group.groupDescription = this.groupForm.get('description').value; + group.visibility = this.groupForm.get('visibility').value; + return group; + } + + @Input() set group(value: GroupRo) { + this._group = value; + + if (!!value) { + this.groupForm.enable(); + this.groupForm.controls['name'].setValue(value.groupName); + // control disable enable did not work?? + if (this.newMode) { + this.groupForm.controls['name'].enable(); + } else { + this.groupForm.controls['name'].disable(); + } + + this.groupForm.controls['description'].setValue(value.groupDescription); + this.groupForm.controls['visibility'].setValue(value.visibility); + + } else { + this.groupForm.disable(); + this.groupForm.controls['name'].setValue(""); + this.groupForm.controls['description'].setValue(""); + this.groupForm.controls['visibility'].setValue(""); + } + + this.groupForm.markAsPristine(); + } + + clearAlert() { + this.message = null; + this.messageType = null; + } + + + closeDialog() { + this.dialogRef.close() + } + + get submitButtonEnabled(): boolean { + return this.groupForm.valid && this.groupForm.dirty; + } + + public onSaveButtonClicked() { + + let group = this.group; + if (this.newMode) { + this.createGroup(group); + } else { + this.saveGroup(group); + } + } + + public createGroup(group: GroupRo) { + + this.editDomainService.createDomainGroupObservable(this._currentDomain.domainId, group).subscribe((group: GroupRo) => { + if (!!group) { + this.closeDialog(); + } + }, (error) => { + this.alertService.error(error.error?.errorDescription) + }); + + } + + public saveGroup(group: GroupRo) { + this.editDomainService.saveDomainGroupObservable(this._currentDomain.domainId, group).subscribe((group: GroupRo) => { + if (!!group) { + this.closeDialog(); + } + }, (error) => { + this.alertService.error(error.error?.errorDescription) + }); + } + + +} diff --git a/smp-angular/src/app/edit/edit-domain/edit-domain.component.ts b/smp-angular/src/app/edit/edit-domain/edit-domain.component.ts index 97b3c37b3..16a84f0a8 100644 --- a/smp-angular/src/app/edit/edit-domain/edit-domain.component.ts +++ b/smp-angular/src/app/edit/edit-domain/edit-domain.component.ts @@ -39,7 +39,6 @@ export class EditDomainComponent implements OnInit, AfterViewInit, BeforeLeaveGu ); domainService.getDomains(); - } diff --git a/smp-angular/src/app/edit/edit-domain/edit-domain.service.ts b/smp-angular/src/app/edit/edit-domain/edit-domain.service.ts index 2cfb18dc3..f61409702 100644 --- a/smp-angular/src/app/edit/edit-domain/edit-domain.service.ts +++ b/smp-angular/src/app/edit/edit-domain/edit-domain.service.ts @@ -31,7 +31,6 @@ export class EditDomainService { }); } - public getDomainGroupsObservable(domainId: string): Observable<GroupRo[]> { const currentUser: User = this.securityService.getCurrentUser(); @@ -40,8 +39,7 @@ export class EditDomainService { .replace(SmpConstants.PATH_PARAM_ENC_DOMAIN_ID, domainId)); } - public deleteDomainGroupObservable(domainId: string, groupId:string ): Observable<GroupRo> - { + public deleteDomainGroupObservable(domainId: string, groupId:string ): Observable<GroupRo> { const currentUser: User = this.securityService.getCurrentUser(); return this.http.delete<GroupRo>(SmpConstants.REST_PUBLIC_GROUP_DOMAIN_DELETE .replace(SmpConstants.PATH_PARAM_ENC_USER_ID, currentUser.userId) @@ -49,7 +47,24 @@ export class EditDomainService { .replace(SmpConstants.PATH_PARAM_ENC_GROUP_ID, groupId)); } + public createDomainGroupObservable(domainId: string, group:GroupRo ): Observable<GroupRo> + { + const currentUser: User = this.securityService.getCurrentUser(); + return this.http.put<GroupRo>(SmpConstants.REST_PUBLIC_GROUP_DOMAIN_CREATE + .replace(SmpConstants.PATH_PARAM_ENC_USER_ID, currentUser.userId) + .replace(SmpConstants.PATH_PARAM_ENC_DOMAIN_ID, domainId) + , group); + } + public saveDomainGroupObservable(domainId: string, group:GroupRo ): Observable<GroupRo> + { + const currentUser: User = this.securityService.getCurrentUser(); + return this.http.post<GroupRo>(SmpConstants.REST_PUBLIC_GROUP_DOMAIN_UPDATE + .replace(SmpConstants.PATH_PARAM_ENC_USER_ID, currentUser.userId) + .replace(SmpConstants.PATH_PARAM_ENC_DOMAIN_ID, domainId) + .replace(SmpConstants.PATH_PARAM_ENC_GROUP_ID, group.groupId) + , group); + } notifyDomainsUpdated(res: DomainRo[]) { this.domainUpdateSubject.next(res); } diff --git a/smp-angular/src/app/edit/edit-group/edit-group.component.css b/smp-angular/src/app/edit/edit-group/edit-group.component.css new file mode 100644 index 000000000..167fddb57 --- /dev/null +++ b/smp-angular/src/app/edit/edit-group/edit-group.component.css @@ -0,0 +1,28 @@ + +#admin-group-panel { + display: flex; + flex-flow: column; + align-items: center; + height: 100%; + min-height: 600px; + padding: 0 2em; +} +#group-filter { + width: 100%; + padding-top: 1em; +} + + +#admin-group-table { + width: 100%; + padding-top: 1em; +} + +#custom-file-upload { + display: none; +} + +.custom-file-upload { + display: block; + cursor: pointer; +} diff --git a/smp-angular/src/app/edit/edit-group/edit-group.component.html b/smp-angular/src/app/edit/edit-group/edit-group.component.html new file mode 100644 index 000000000..dfbda58df --- /dev/null +++ b/smp-angular/src/app/edit/edit-group/edit-group.component.html @@ -0,0 +1,44 @@ +<div id="admin-group-panel"> + <data-panel id="admin-group-data-panel" + title="Edit Group" + text="Edit group administration panel is a tool for group administrators to manage group members and resources" + [labelColumnContent]="searchGroupPanel"> + </data-panel> + +</div> + +<ng-template #searchGroupPanel> + <mat-form-field id="group-filter"> + <mat-label>Filter by group name</mat-label> + <input matInput (keyup)="applyGroupFilter($event)" placeholder="Group name" #inputDomainFilter> + </mat-form-field> + + <table class="mat-elevation-z2" id="admin-domain-table" mat-table [dataSource]="dataSource" matSort> + <ng-container matColumnDef="groupName"> + <th mat-header-cell *matHeaderCellDef mat-sort-header>Group name</th> + <td mat-cell *matCellDef="let row" [matTooltip]="row.groupDescription">{{row.groupName}}</td> + </ng-container> + + <tr mat-header-row *matHeaderRowDef="displayedColumns"></tr> + <tr mat-row *matRowDef="let odd = odd; let row; columns: displayedColumns;" + (click)="onGroupSelected(row)" + [ngClass]="{'datatable-row-selected': groupSelected,'datatable-row-odd': odd}" + ></tr> + + + <tr class="mat-row" *matNoDataRow> + <td *ngIf="inputDomainFilter.value;else noDataFound" class="mat-cell" colspan="2">No data matching the filter + "{{inputDomainFilter.value}}" + </td> + <ng-template #noDataFound> + <td class="mat-cell" colspan="2">No data</td> + </ng-template> + </tr> + </table> + + <mat-paginator class="mat-elevation-z2" id="extension-paginator" + [hidePageSize]="true" + [showFirstLastButtons]="true" + [pageSize]="5" aria-label="Select page"></mat-paginator> + +</ng-template> diff --git a/smp-angular/src/app/edit/edit-group/edit-group.component.ts b/smp-angular/src/app/edit/edit-group/edit-group.component.ts new file mode 100644 index 000000000..7325d84ec --- /dev/null +++ b/smp-angular/src/app/edit/edit-group/edit-group.component.ts @@ -0,0 +1,64 @@ +import {Component, ViewChild} from '@angular/core'; +import {BeforeLeaveGuard} from "../../window/sidenav/navigation-on-leave-guard"; +import {MatTableDataSource} from "@angular/material/table"; +import {GroupRo} from "../../common/model/group-ro.model"; +import {MatPaginator} from "@angular/material/paginator"; +import {AlertMessageService} from "../../common/alert-message/alert-message.service"; +import {MatDialog} from "@angular/material/dialog"; +import {EditGroupService} from "./edit-group.service"; + + +@Component({ + moduleId: module.id, + templateUrl: './edit-group.component.html', + styleUrls: ['./edit-group.component.css'] +}) +export class EditGroupComponent implements BeforeLeaveGuard { + displayedColumns: string[] = ['groupName']; + dataSource: MatTableDataSource<GroupRo> = new MatTableDataSource(); + selected?: GroupRo; + + @ViewChild(MatPaginator) paginator: MatPaginator; + + constructor(private editGroupService: EditGroupService, + private alertService: AlertMessageService, + private dialog: MatDialog) { + /* + editGroupService.onGroupUpdatedEvent().subscribe((groupList: GroupRo[]) => { + this.updateGroupList(groupList); + } + ); + + editGroupService.getUserAdminGroups(); + + */ + } + + + updateGroupList(groupList: GroupRo[]) { + this.dataSource.data = groupList; + } + + + isDirty(): boolean { + return false; + } + + applyGroupFilter(event: Event) { + const filterValue = (event.target as HTMLInputElement).value; + this.dataSource.filter = filterValue.trim().toLowerCase(); + + if (this.dataSource.paginator) { + this.dataSource.paginator.firstPage(); + } + } + + onGroupSelected(group: GroupRo) { + this.selected = group; + } + + + get groupSelected(): boolean { + return !!this.selected; + } +} diff --git a/smp-angular/src/app/edit/edit-group/edit-group.service.ts b/smp-angular/src/app/edit/edit-group/edit-group.service.ts new file mode 100644 index 000000000..58410c18f --- /dev/null +++ b/smp-angular/src/app/edit/edit-group/edit-group.service.ts @@ -0,0 +1,40 @@ +import {Injectable} from '@angular/core'; +import {Observable, Subject} from 'rxjs'; + +import {HttpClient} from '@angular/common/http'; +import {SecurityService} from "../../security/security.service"; +import {AlertMessageService} from "../../common/alert-message/alert-message.service"; +import {User} from "../../security/user.model"; +import {SmpConstants} from "../../smp.constants"; +import {GroupRo} from "../../common/model/group-ro.model"; + +@Injectable() +export class EditGroupService { + private groupsUpdateSubject = new Subject<GroupRo[]>(); + + + constructor( + private http: HttpClient, + private securityService: SecurityService, + private alertService: AlertMessageService) { + } + + public getUserAdminGroups() { + const currentUser: User = this.securityService.getCurrentUser(); + this.http.get<GroupRo[]>(SmpConstants.REST_PUBLIC_GROUP_EDIT + .replace(SmpConstants.PATH_PARAM_ENC_USER_ID, currentUser.userId)) + .subscribe((result: GroupRo[]) => { + this.notifyGroupUpdated(result); + }, (error: any) => { + this.alertService.error(error.error?.errorDescription) + }); + } + + notifyGroupUpdated(res: GroupRo[]) { + this.groupsUpdateSubject.next(res); + } + + onGroupUpdatedEvent(): Observable<GroupRo[]> { + return this.groupsUpdateSubject.asObservable(); + } +} diff --git a/smp-angular/src/app/system-settings/admin-domain/domain-panel/domain-panel.component.html b/smp-angular/src/app/system-settings/admin-domain/domain-panel/domain-panel.component.html index dd7b49082..5c173ac85 100644 --- a/smp-angular/src/app/system-settings/admin-domain/domain-panel/domain-panel.component.html +++ b/smp-angular/src/app/system-settings/admin-domain/domain-panel/domain-panel.component.html @@ -1,20 +1,5 @@ <div id="domain-panel" class="mat-elevation-z2" > <form [formGroup]="domainForm" > - <mat-toolbar> - <mat-toolbar-row> - <button id="cancelButton" mat-raised-button (click)="onResetButtonClicked()" color="primary" - [disabled]="!resetButtonEnabled"> - <mat-icon>refresh</mat-icon> - <span>Reset</span> - </button> - <button id="saveButton" mat-raised-button (click)="onSaveButtonClicked()" color="primary" - [disabled]="!submitButtonEnabled"> - <mat-icon>save</mat-icon> - <span>Save</span> - </button> - - </mat-toolbar-row> - </mat-toolbar> <h3>Domain details</h3> <div class="panel" *ngIf="_domain!=null && !_domain.domainId"><p style="font-weight: bold">Enter data and click 'Save' to create new domain</div> <mat-form-field style="width:100%"> @@ -92,5 +77,20 @@ to get read the domain resources </mat-hint> </mat-form-field> + <mat-toolbar> + <mat-toolbar-row> + <button id="cancelButton" mat-raised-button (click)="onResetButtonClicked()" color="primary" + [disabled]="!resetButtonEnabled"> + <mat-icon>refresh</mat-icon> + <span>Reset</span> + </button> + <button id="saveButton" mat-raised-button (click)="onSaveButtonClicked()" color="primary" + [disabled]="!submitButtonEnabled"> + <mat-icon>save</mat-icon> + <span>Save</span> + </button> + + </mat-toolbar-row> + </mat-toolbar> </form> </div> diff --git a/smp-angular/src/app/system-settings/admin-domain/domain-sml-panel/domain-sml-integration-panel.component.html b/smp-angular/src/app/system-settings/admin-domain/domain-sml-panel/domain-sml-integration-panel.component.html index 8b3d5eb79..dfe8911ce 100644 --- a/smp-angular/src/app/system-settings/admin-domain/domain-sml-panel/domain-sml-integration-panel.component.html +++ b/smp-angular/src/app/system-settings/admin-domain/domain-sml-panel/domain-sml-integration-panel.component.html @@ -1,32 +1,7 @@ <form [formGroup]="domainForm" > <div id="domain-sml-integration-panel" class="mat-elevation-z2"> - <mat-toolbar> - <mat-toolbar-row> - <button id="cancelButton" mat-raised-button (click)="onResetButtonClicked()" color="primary" - [disabled]="!resetButtonEnabled"> - <mat-icon>refresh</mat-icon> - <span>Reset</span> - </button> - <button id="saveButton" mat-raised-button (click)="onSaveButtonClicked()" color="primary" - [disabled]="!submitButtonEnabled"> - <mat-icon>save</mat-icon> - <span>Save</span> - </button> - <tool-button-spacer></tool-button-spacer> - <button id="registerButton" mat-raised-button (click)="smlRegisterSelectedDomain()" - [disabled]="!enableSMLRegister()" color="primary"> - <mat-icon>link</mat-icon> - <span>Register</span> - </button> - <button id="unregisterButton" mat-raised-button (click)="smlUnregisterSelectedDomain()" - [disabled]="!enableSMLUnregister()" color="primary"> - <mat-icon>link_off</mat-icon> - <span>Unregister</span> - </button> - </mat-toolbar-row> - </mat-toolbar> <h3>SML integration data</h3> <div class="panel" *ngIf="isDomainRegistered"><p style="font-weight: bold">Domain is registered to SML!</p>The Registered domain can not be deleted or changed the SMP SML identifier</div> <mat-form-field style="width:100%"> @@ -114,5 +89,30 @@ Use ClientCert http header authentication. </mat-slide-toggle> </div> + <mat-toolbar> + <mat-toolbar-row> + <button id="cancelButton" mat-raised-button (click)="onResetButtonClicked()" color="primary" + [disabled]="!resetButtonEnabled"> + <mat-icon>refresh</mat-icon> + <span>Reset</span> + </button> + <button id="saveButton" mat-raised-button (click)="onSaveButtonClicked()" color="primary" + [disabled]="!submitButtonEnabled"> + <mat-icon>save</mat-icon> + <span>Save</span> + </button> + <tool-button-spacer></tool-button-spacer> + <button id="registerButton" mat-raised-button (click)="smlRegisterSelectedDomain()" + [disabled]="!enableSMLRegister()" color="primary"> + <mat-icon>link</mat-icon> + <span>Register</span> + </button> + <button id="unregisterButton" mat-raised-button (click)="smlUnregisterSelectedDomain()" + [disabled]="!enableSMLUnregister()" color="primary"> + <mat-icon>link_off</mat-icon> + <span>Unregister</span> + </button> + </mat-toolbar-row> + </mat-toolbar> </form> diff --git a/smp-angular/src/app/system-settings/admin-users/admin-user.component.html b/smp-angular/src/app/system-settings/admin-users/admin-user.component.html index a306de2f0..269f06921 100644 --- a/smp-angular/src/app/system-settings/admin-users/admin-user.component.html +++ b/smp-angular/src/app/system-settings/admin-users/admin-user.component.html @@ -19,23 +19,6 @@ <input matInput (keyup)="applyUserFilter($event)" placeholder="User name or full name" #inputUserFilter> </mat-form-field> - <mat-toolbar> - <mat-toolbar-row class="mat-elevation-z5"> - <button mat-raised-button - mat-flat-button color="primary" - (click)="onCreateUserClicked()" - >Create User - </button> - - <button mat-raised-button - [disabled]="canNotDelete" - color="primary" - (click)="onDeleteSelectedUserClicked()"> - <mat-icon>delete</mat-icon> - <span>Delete selected</span> - </button> - </mat-toolbar-row> - </mat-toolbar> <table class="mat-elevation-z2" id="admin-domain-table" mat-table [dataSource]="userData" > <ng-container matColumnDef="username"> <th mat-header-cell *matHeaderCellDef >Username</th> @@ -72,4 +55,22 @@ [showFirstLastButtons]="true" aria-label="Select page"></mat-paginator> + <mat-toolbar> + <mat-toolbar-row class="mat-elevation-z5"> + <button mat-raised-button + mat-flat-button color="primary" + (click)="onCreateUserClicked()" + >Create User + </button> + + <button mat-raised-button + [disabled]="canNotDelete" + color="primary" + (click)="onDeleteSelectedUserClicked()"> + <mat-icon>delete</mat-icon> + <span>Delete selected</span> + </button> + </mat-toolbar-row> + </mat-toolbar> + </ng-template> diff --git a/smp-angular/src/app/system-settings/admin-users/admin-user.component.ts b/smp-angular/src/app/system-settings/admin-users/admin-user.component.ts index a201f5ea5..5ccabc9a1 100644 --- a/smp-angular/src/app/system-settings/admin-users/admin-user.component.ts +++ b/smp-angular/src/app/system-settings/admin-users/admin-user.component.ts @@ -5,7 +5,7 @@ import {ConfirmationDialogComponent} from "../../common/dialogs/confirmation-dia import {MatDialog, MatDialogConfig, MatDialogRef} from "@angular/material/dialog"; import {BeforeLeaveGuard} from "../../window/sidenav/navigation-on-leave-guard"; import {CancelDialogComponent} from "../../common/dialogs/cancel-dialog/cancel-dialog.component"; -import {SearchUserRo} from "../../common/panels/domain-member-panel/member-dialog/search-user-ro.model"; +import {SearchUserRo} from "../../common/model/search-user-ro.model"; import {AdminUserService} from "./admin-user.service"; import {TableResult} from "../../common/model/table-result.model"; import {finalize} from "rxjs/operators"; diff --git a/smp-angular/src/theme.scss b/smp-angular/src/theme.scss index f2c41ddad..8b9a31638 100644 --- a/smp-angular/src/theme.scss +++ b/smp-angular/src/theme.scss @@ -23,7 +23,32 @@ $smp-theme: mat.define-light-theme(( typography: mat.define-typography-config(), density: 0, )); - +/* +$smp-theme: mat.define-light-theme(( + color: ( + primary: mat.define-palette(smp-colors.$smp-primary-palette), + accent: mat.define-palette(smp-colors.$smp-accent-palette), + warn: mat.define-palette(smp-colors.$smp-warn-palette), + ), + typography: mat.define-typography-config( + $font-family: null, + $headline-1: mat.define-typography-level(32px, 48px, 700), + $headline-2: null, + $headline-3: null, + $headline-4: null, + $headline-5: null, + $headline-6: null, + $subtitle-1: null, + $subtitle-2: null, + $body-1: mat.define-typography-level(12px, 14px, 500), + $body-2: mat.define-typography-level(11px, 14px, 500), + $caption: mat.define-typography-level(10px, 12px, 500), + $button: null, + $overline: null, + ), + density: 0, +)); +*/ // ----------------------------------------- // blue theme @@ -81,14 +106,17 @@ $purple_green_theme: mat.define-dark-theme(( @include mat.all-component-colors($indigo_pink_theme); @include smp.all-component-colors($indigo_pink_theme); } + .pink_blue-grey_theme { @include mat.all-component-colors($pink_blue-grey_theme); @include smp.all-component-colors($pink_blue-grey_theme); } + .purple_green_theme { @include mat.all-component-colors($purple_green_theme); @include smp.all-component-colors($purple_green_theme); } + // Include theme styles for core and each component used in your app. // Alternatively, you can import and @include the theme mixins for each component // that you are using. diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/dao/DomainMemberDao.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/dao/DomainMemberDao.java index fe4c8183e..95a176b32 100644 --- a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/dao/DomainMemberDao.java +++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/dao/DomainMemberDao.java @@ -36,6 +36,7 @@ public class DomainMemberDao extends BaseDao<DBDomainMember> { private final DomainDao domainDao; + public DomainMemberDao(DomainDao domainDao) { this.domainDao = domainDao; } @@ -45,7 +46,7 @@ public class DomainMemberDao extends BaseDao<DBDomainMember> { } public boolean isUserDomainsMember(DBUser user, List<DBDomain> domainList) { - List<Long> domainIds = domainList.stream().map(dbDomain -> dbDomain.getId()).collect(Collectors.toList()); + List<Long> domainIds = domainList.stream().map(DBDomain::getId).collect(Collectors.toList()); return isUserDomainsMember(user.getId(), domainIds); } @@ -68,9 +69,6 @@ public class DomainMemberDao extends BaseDao<DBDomainMember> { public boolean isUserAnyDomainAdministrator(Long userId){ return domainDao.getDomainsByUserIdAndRolesCount(userId, MembershipRoleType.ADMIN)>0; } - public boolean isUserGroupAdministrator(Long userId){ - return false; - } public boolean isUserResourceAdministrator(Long userId){ return false; diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/dao/GroupDao.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/dao/GroupDao.java index 327073d77..8f29a9d9a 100644 --- a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/dao/GroupDao.java +++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/dao/GroupDao.java @@ -13,6 +13,7 @@ package eu.europa.ec.edelivery.smp.data.dao; +import eu.europa.ec.edelivery.smp.data.enums.MembershipRoleType; import eu.europa.ec.edelivery.smp.data.model.DBDomain; import eu.europa.ec.edelivery.smp.data.model.DBGroup; import org.springframework.stereotype.Repository; @@ -21,6 +22,7 @@ import javax.persistence.NoResultException; import javax.persistence.NonUniqueResultException; import javax.persistence.TypedQuery; import javax.transaction.Transactional; +import java.util.Arrays; import java.util.List; import java.util.Optional; @@ -65,6 +67,7 @@ public class GroupDao extends BaseDao<DBGroup> { return query.getResultList(); } + /** * Returns the group or Optional.empty() if there is no group for name and domain. * @@ -122,6 +125,24 @@ public class GroupDao extends BaseDao<DBGroup> { throw new IllegalStateException(ILLEGAL_STATE_DOMAIN_MULTIPLE_ENTRY.getMessage(name,domainCode)); } } + + public Long getGroupsByUserIdAndRolesCount(Long userId, MembershipRoleType... roleTypes) { + + List<MembershipRoleType> list = Arrays.asList(roleTypes ==null || roleTypes.length==0 ?MembershipRoleType.values(): roleTypes); + TypedQuery<Long> query = memEManager.createNamedQuery(QUERY_GROUP_BY_USER_ROLES_COUNT, Long.class); + query.setParameter(PARAM_USER_ID, userId); + query.setParameter(PARAM_MEMBERSHIP_ROLES, list); + return query.getSingleResult(); + } + + public List<DBGroup> getGroupsByUserIdAndRoles(Long userId, MembershipRoleType... roleTypes) { + + List<MembershipRoleType> list = Arrays.asList(roleTypes ==null || roleTypes.length==0 ?MembershipRoleType.values(): roleTypes); + TypedQuery<DBGroup> query = memEManager.createNamedQuery(QUERY_GROUP_BY_USER_ROLES, DBGroup.class); + query.setParameter(PARAM_USER_ID, userId); + query.setParameter(PARAM_MEMBERSHIP_ROLES, list); + return query.getResultList(); + } /** * Removes Entity by given domain code * diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/dao/GroupMemberDao.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/dao/GroupMemberDao.java index 643c52a39..09f84764f 100644 --- a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/dao/GroupMemberDao.java +++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/dao/GroupMemberDao.java @@ -20,6 +20,7 @@ import eu.europa.ec.edelivery.smp.data.model.user.DBGroupMember; import eu.europa.ec.edelivery.smp.data.model.user.DBUser; import eu.europa.ec.edelivery.smp.logging.SMPLogger; import eu.europa.ec.edelivery.smp.logging.SMPLoggerFactory; +import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Repository; import javax.persistence.TypedQuery; @@ -35,8 +36,15 @@ import static eu.europa.ec.edelivery.smp.data.dao.QueryNames.*; @Repository public class GroupMemberDao extends BaseDao<DBGroupMember> { private static final SMPLogger LOG = SMPLoggerFactory.getLogger(GroupMemberDao.class); + + private final GroupDao groupDao; + + public GroupMemberDao(GroupDao groupDao) { + this.groupDao = groupDao; + } + public boolean isUserGroupMember(DBUser user, List<DBGroup> groups) { - List<Long> groupIds = groups.stream().map(grop -> grop.getId()).collect(Collectors.toList()); + List<Long> groupIds = groups.stream().map(DBGroup::getId).collect(Collectors.toList()); return isGroupResourceMember(user.getId(), groupIds); } @@ -64,7 +72,7 @@ public class GroupMemberDao extends BaseDao<DBGroupMember> { query.setParameter(PARAM_USER_ID, userId); query.setParameter(PARAM_GROUP_IDS, groupIds); - return query.getResultList().stream().anyMatch(member ->member.getRole() == roleType ); + return query.getResultList().stream().anyMatch(member -> member.getRole() == roleType); } public boolean isUserAnyDomainGroupResourceMemberWithRole(DBUser user, DBDomain domain, MembershipRoleType roleType) { @@ -76,4 +84,46 @@ public class GroupMemberDao extends BaseDao<DBGroupMember> { query.setParameter(PARAM_MEMBERSHIP_ROLE, roleType); return query.getSingleResult() > 0; } + + public boolean isUserGroupAdministrator(Long userId) { + return groupDao.getGroupsByUserIdAndRolesCount(userId, MembershipRoleType.ADMIN) > 0; + } + + public List<DBGroupMember> getGroupMembers(Long groupId, int iPage, int iPageSize, String filter) { + boolean hasFilter = StringUtils.isNotBlank(filter); + TypedQuery<DBGroupMember> query = memEManager.createNamedQuery(hasFilter ? + QUERY_GROUP_MEMBERS_FILTER : QUERY_GROUP_MEMBERS, DBGroupMember.class); + + if (iPageSize > -1 && iPage > -1) { + query.setFirstResult(iPage * iPageSize); + } + if (iPageSize > 0) { + query.setMaxResults(iPageSize); + } + query.setParameter(PARAM_GROUP_ID, groupId); + if (hasFilter) { + query.setParameter(PARAM_USER_FILTER, "%" + StringUtils.trim(filter) + "%"); + } + return query.getResultList(); + } + + public Long getGroupMemberCount(Long groupId, String filter) { + boolean hasFilter = StringUtils.isNotBlank(filter); + TypedQuery<Long> query = memEManager.createNamedQuery(hasFilter ? QUERY_GROUP_MEMBERS_FILTER_COUNT : QUERY_GROUP_MEMBERS_COUNT, Long.class); + query.setParameter(PARAM_GROUP_ID, groupId); + if (hasFilter) { + query.setParameter(PARAM_USER_FILTER, "%" + StringUtils.trim(filter) + "%"); + } + return query.getSingleResult(); + } + + + public DBGroupMember addMemberToDomain(DBGroup group, DBUser user, MembershipRoleType role) { + DBGroupMember groupMember = new DBGroupMember(); + groupMember.setRole(role); + groupMember.setUser(user); + groupMember.setGroup(group); + groupMember = merge(groupMember); + return groupMember; + } } diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/dao/QueryNames.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/dao/QueryNames.java index 2e9b56e51..a111fae20 100644 --- a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/dao/QueryNames.java +++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/dao/QueryNames.java @@ -23,6 +23,12 @@ public class QueryNames { public static final String QUERY_GROUP_BY_DOMAIN = "DBGroup.getByDomain"; public static final String QUERY_GROUP_BY_NAME_DOMAIN = "DBGroup.getByNameDomain"; public static final String QUERY_GROUP_BY_NAME_DOMAIN_CODE = "DBGroup.getByNameDomainCode"; + public static final String QUERY_GROUP_BY_USER_ROLES = "DBGroup.getByUserAndRoles"; + public static final String QUERY_GROUP_BY_USER_ROLES_COUNT = "DBGroup.getByUserAndRolesCount"; + public static final String QUERY_GROUP_MEMBERS_COUNT = "DBGroup.getByGroupCount"; + public static final String QUERY_GROUP_MEMBERS_FILTER_COUNT = "DBGroup.getByGroupFilterCount"; + public static final String QUERY_GROUP_MEMBERS = "DBGroup.getByGroup"; + public static final String QUERY_GROUP_MEMBERS_FILTER = "DBGroup.getByGroupFilter"; public static final String QUERY_DOMAIN_MEMBER_ALL = "DBDomainMember.getAll"; public static final String QUERY_DOMAIN_MEMBER_BY_USER_DOMAINS_COUNT = "DBDomainMember.getByUserAndDomainsCount"; @@ -123,6 +129,7 @@ public class QueryNames { public static final String PARAM_DOCUMENT_ID = "document_id"; + public static final String PARAM_GROUP_ID = "group_id"; public static final String PARAM_GROUP_IDS = "group_ids"; public static final String PARAM_MEMBERSHIP_ROLE = "membership_role"; diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/model/DBGroup.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/model/DBGroup.java index c7af3e058..f54fe80c7 100644 --- a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/model/DBGroup.java +++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/model/DBGroup.java @@ -38,12 +38,15 @@ import static eu.europa.ec.edelivery.smp.data.dao.QueryNames.*; }) @org.hibernate.annotations.Table(appliesTo = "SMP_GROUP", comment = "The group spans the resources belonging to the domain group.") -@NamedQueries({ - @NamedQuery(name = QUERY_GROUP_ALL, query = "SELECT u FROM DBGroup u"), - @NamedQuery(name = QUERY_GROUP_BY_DOMAIN, query = "SELECT u FROM DBGroup u where u.domain.id = :domain_id"), - @NamedQuery(name = QUERY_GROUP_BY_NAME_DOMAIN, query = "SELECT u FROM DBGroup u where u.groupName = :name and u.domain.id = :domain_id"), - @NamedQuery(name = QUERY_GROUP_BY_NAME_DOMAIN_CODE, query = "SELECT u FROM DBGroup u where u.groupName = :name and u.domain.domainCode = :domain_code"), -}) +@NamedQuery(name = QUERY_GROUP_ALL, query = "SELECT u FROM DBGroup u") +@NamedQuery(name = QUERY_GROUP_BY_DOMAIN, query = "SELECT u FROM DBGroup u where u.domain.id = :domain_id") +@NamedQuery(name = QUERY_GROUP_BY_NAME_DOMAIN, query = "SELECT u FROM DBGroup u where u.groupName = :name and u.domain.id = :domain_id") +@NamedQuery(name = QUERY_GROUP_BY_NAME_DOMAIN_CODE, query = "SELECT u FROM DBGroup u where u.groupName = :name and u.domain.domainCode = :domain_code") +@NamedQuery(name = QUERY_GROUP_BY_USER_ROLES_COUNT, query = "SELECT count(c) FROM DBGroup c JOIN DBGroupMember dm ON c.id = dm.group.id " + + " WHERE dm.role in (:membership_roles) and dm.user.id= :user_id") + +@NamedQuery(name = QUERY_GROUP_BY_USER_ROLES, query = "SELECT c FROM DBGroup c JOIN DBGroupMember dm ON c.id = dm.group.id " + + " WHERE dm.role in (:membership_roles) and dm.user.id= :user_id") public class DBGroup extends BaseEntity { @Id diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/model/user/DBGroupMember.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/model/user/DBGroupMember.java index 40b6cfbe7..bf89df0aa 100644 --- a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/model/user/DBGroupMember.java +++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/data/model/user/DBGroupMember.java @@ -4,6 +4,8 @@ import eu.europa.ec.edelivery.smp.data.enums.MembershipRoleType; import eu.europa.ec.edelivery.smp.data.model.BaseEntity; import eu.europa.ec.edelivery.smp.data.model.CommonColumnsLengths; import eu.europa.ec.edelivery.smp.data.model.DBGroup; +import org.apache.commons.lang3.builder.EqualsBuilder; +import org.apache.commons.lang3.builder.HashCodeBuilder; import org.hibernate.annotations.GenericGenerator; import org.hibernate.envers.Audited; @@ -21,18 +23,28 @@ import static eu.europa.ec.edelivery.smp.data.dao.QueryNames.*; @Audited @Table(name = "SMP_GROUP_MEMBER", indexes = {@Index(name = "SMP_GRP_MEM_IDX", columnList = "FK_GROUP_ID, FK_USER_ID", unique = true) -}) -@NamedQueries({ - @NamedQuery(name = QUERY_GROUP_MEMBER_ALL, query = "SELECT u FROM DBGroupMember u"), - @NamedQuery(name = QUERY_GROUP_MEMBER_BY_USER_GROUPS_COUNT, query = "SELECT count(c) FROM DBGroupMember c " + - " WHERE c.user.id = :user_id AND c.group.id IN (:group_ids)"), - @NamedQuery(name = QUERY_GROUP_MEMBER_BY_USER_DOMAIN_GROUPS_COUNT, query = "SELECT count(c) FROM DBGroupMember c JOIN c.group.domain d " + - " WHERE c.user.id = :user_id AND d.id = :domain_id"), - @NamedQuery(name = QUERY_GROUP_MEMBER_BY_USER_GROUPS, query = "SELECT c FROM DBGroupMember c " + - " WHERE c.user.id = :user_id AND c.group.id IN (:group_ids)"), - @NamedQuery(name = QUERY_GROUP_MEMBER_BY_USER_DOMAIN_GROUPS_ROLE_COUNT, query = "SELECT count(c) FROM DBGroupMember c " + - " WHERE c.user.id = :user_id AND c.group.domain.id = :domain_id AND c.role= :membership_role ") -}) + }) + +@NamedQuery(name = QUERY_GROUP_MEMBER_ALL, query = "SELECT u FROM DBGroupMember u") +@NamedQuery(name = QUERY_GROUP_MEMBER_BY_USER_GROUPS_COUNT, query = "SELECT count(c) FROM DBGroupMember c " + + " WHERE c.user.id = :user_id AND c.group.id IN (:group_ids)") +@NamedQuery(name = QUERY_GROUP_MEMBER_BY_USER_DOMAIN_GROUPS_COUNT, query = "SELECT count(c) FROM DBGroupMember c JOIN c.group.domain d " + + " WHERE c.user.id = :user_id AND d.id = :domain_id") +@NamedQuery(name = QUERY_GROUP_MEMBER_BY_USER_GROUPS, query = "SELECT c FROM DBGroupMember c " + + " WHERE c.user.id = :user_id AND c.group.id IN (:group_ids)") +@NamedQuery(name = QUERY_GROUP_MEMBERS_COUNT, query = "SELECT count(c) FROM DBGroupMember c " + + " WHERE c.group.id = :group_id") +@NamedQuery(name = QUERY_GROUP_MEMBERS, query = "SELECT c FROM DBGroupMember c " + + " WHERE c.group.id = :group_id order by c.user.username") +@NamedQuery(name = QUERY_GROUP_MEMBERS_FILTER_COUNT, query = "SELECT count(c) FROM DBGroupMember c " + + " WHERE c.group.id = :group_id AND (lower(c.user.fullName) like lower(:user_filter) OR lower(c.user.username) like lower(:user_filter))") +@NamedQuery(name = QUERY_GROUP_MEMBERS_FILTER, query = "SELECT c FROM DBGroupMember c " + + " WHERE c.group.id = :group_id AND (lower(c.user.fullName) like lower(:user_filter) OR lower(c.user.username) like lower(:user_filter)) order by c.user.username") + + +@NamedQuery(name = QUERY_GROUP_MEMBER_BY_USER_DOMAIN_GROUPS_ROLE_COUNT, query = "SELECT count(c) FROM DBGroupMember c " + + " WHERE c.user.id = :user_id AND c.group.domain.id = :domain_id AND c.role= :membership_role ") + public class DBGroupMember extends BaseEntity { @Id @@ -97,4 +109,20 @@ public class DBGroupMember extends BaseEntity { public void setRole(MembershipRoleType role) { this.role = role; } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + + if (o == null || getClass() != o.getClass()) return false; + + DBGroupMember that = (DBGroupMember) o; + + return new EqualsBuilder().appendSuper(super.equals(o)).append(id, that.id).append(role, that.role).isEquals(); + } + + @Override + public int hashCode() { + return new HashCodeBuilder(17, 37).appendSuper(super.hashCode()).append(id).append(role).toHashCode(); + } } diff --git a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/ui/UIGroupPublicService.java b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/ui/UIGroupPublicService.java index 818bda65d..4a5d05df6 100644 --- a/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/ui/UIGroupPublicService.java +++ b/smp-server-library/src/main/java/eu/europa/ec/edelivery/smp/services/ui/UIGroupPublicService.java @@ -3,6 +3,8 @@ package eu.europa.ec.edelivery.smp.services.ui; import eu.europa.ec.edelivery.smp.data.dao.BaseDao; import eu.europa.ec.edelivery.smp.data.dao.DomainDao; import eu.europa.ec.edelivery.smp.data.dao.GroupDao; +import eu.europa.ec.edelivery.smp.data.dao.GroupMemberDao; +import eu.europa.ec.edelivery.smp.data.enums.MembershipRoleType; import eu.europa.ec.edelivery.smp.data.model.DBDomain; import eu.europa.ec.edelivery.smp.data.model.DBGroup; import eu.europa.ec.edelivery.smp.data.ui.GroupRO; @@ -11,6 +13,7 @@ import eu.europa.ec.edelivery.smp.exceptions.ErrorCode; import eu.europa.ec.edelivery.smp.exceptions.SMPRuntimeException; import eu.europa.ec.edelivery.smp.logging.SMPLogger; import eu.europa.ec.edelivery.smp.logging.SMPLoggerFactory; +import org.apache.commons.lang3.StringUtils; import org.springframework.core.convert.ConversionService; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -31,13 +34,15 @@ public class UIGroupPublicService extends UIServiceBase<DBGroup, GroupRO> { private static final SMPLogger LOG = SMPLoggerFactory.getLogger(UIGroupPublicService.class); private final GroupDao groupDao; + private final GroupMemberDao groupMemberDao; private final DomainDao domainDao; private final ConversionService conversionService; - public UIGroupPublicService(GroupDao groupDao, DomainDao domainDao, ConversionService conversionService) { + public UIGroupPublicService(GroupDao groupDao, DomainDao domainDao, GroupMemberDao groupMemberDao, ConversionService conversionService) { this.groupDao = groupDao; this.domainDao = domainDao; this.conversionService = conversionService; + this.groupMemberDao = groupMemberDao; } @Override @@ -69,6 +74,14 @@ public class UIGroupPublicService extends UIServiceBase<DBGroup, GroupRO> { .collect(Collectors.toList()); } + @Transactional + public List<GroupRO> getAllGroupsForUser(Long userId, MembershipRoleType role) { + List<DBGroup> domainGroups = groupDao.getGroupsByUserIdAndRoles(userId, role); + + return domainGroups.stream().map(domain -> conversionService.convert(domain, GroupRO.class)) + .collect(Collectors.toList()); + } + @Transactional public GroupRO createGroupForDomain(Long domainId, GroupRO groupRO) { LOG.info("create group [{}] to domain [{}]", groupRO, domainId); @@ -85,7 +98,7 @@ public class UIGroupPublicService extends UIServiceBase<DBGroup, GroupRO> { group.setVisibility(groupRO.getVisibility()); group.setDomain(domain); // to get ID for conversion - domainDao.persistFlushDetach(domain); + groupDao.persistFlushDetach(group); return conversionService.convert(group, GroupRO.class); } @@ -99,27 +112,37 @@ public class UIGroupPublicService extends UIServiceBase<DBGroup, GroupRO> { throw new SMPRuntimeException(ErrorCode.INVALID_REQUEST, "DeleteGroup", "Can not find group to delete"); } - if (Objects.equals(group.getDomain().getId(), domainId)) { + if (!Objects.equals(group.getDomain().getId(), domainId)) { throw new SMPRuntimeException(ErrorCode.INVALID_REQUEST, "DeleteGroup", "Group does not belong to domain"); } + Long userCount = groupMemberDao.getGroupMemberCount(groupId, null); + if (userCount > 0) { + throw new SMPRuntimeException(ErrorCode.INVALID_REQUEST, "DeleteGroup", "Group has members [" + userCount + "] and can not be deleted"); + } + groupDao.remove(group); return conversionService.convert(group, GroupRO.class); } -/* + @Transactional - public MemberRO deleteMemberFromDomain(Long domainId, Long memberId) { - LOG.info("Delete member [{}] from domain [{}]", memberId, domainId); - DBDomainMember domainMember = domainMemberDao.find(memberId); - if (domainMember == null) { - throw new SMPRuntimeException(ErrorCode.INVALID_REQUEST, "Membership", "Membership does not exists!"); + public GroupRO saveGroupForDomain(Long domainId, Long groupId, GroupRO groupRO) { + LOG.info("save group [{}] to domain [{}]", groupRO, domainId); + + if (StringUtils.isBlank(groupRO.getGroupName())) { + throw new SMPRuntimeException(ErrorCode.INVALID_REQUEST, "UpdateGroup", "Group name must not be blank!"); } - if (!Objects.equals(domainMember.getDomain().getId(),domainId )){ - throw new SMPRuntimeException(ErrorCode.INVALID_REQUEST, "Membership", "Membership does not belong to domain!"); + + DBGroup group = groupDao.find(groupId); + if (group == null) { + throw new SMPRuntimeException(ErrorCode.INVALID_REQUEST, "UpdateGroup", "Group with does not exists!"); } - domainMemberDao.remove(domainMember); - return conversionService.convert(domainMember, MemberRO.class); - } + group.setGroupName(groupRO.getGroupName()); + group.setGroupDescription(groupRO.getGroupDescription()); + group.setVisibility(groupRO.getVisibility()); + // to get ID for conversion + groupDao.persistFlushDetach(group); - */ + return conversionService.convert(group, GroupRO.class); + } } diff --git a/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/data/dao/GroupMemberDaoTest.java b/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/data/dao/GroupMemberDaoTest.java index 1abe05b76..b2b377ab9 100644 --- a/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/data/dao/GroupMemberDaoTest.java +++ b/smp-server-library/src/test/java/eu/europa/ec/edelivery/smp/data/dao/GroupMemberDaoTest.java @@ -1,5 +1,6 @@ package eu.europa.ec.edelivery.smp.data.dao; +import eu.europa.ec.edelivery.smp.conversion.DBGroupToGroupROConverter; import eu.europa.ec.edelivery.smp.data.enums.MembershipRoleType; import eu.europa.ec.edelivery.smp.data.model.DBDomain; import eu.europa.ec.edelivery.smp.data.model.DBGroup; @@ -10,11 +11,13 @@ import eu.europa.ec.edelivery.smp.data.model.user.DBResourceMember; import eu.europa.ec.edelivery.smp.data.model.user.DBUser; import eu.europa.ec.edelivery.smp.testutil.TestConstants; import eu.europa.ec.edelivery.smp.testutil.TestDBUtils; +import org.apache.commons.lang3.StringUtils; import org.junit.Before; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; import java.util.Collections; +import java.util.List; import static org.junit.Assert.*; /** @@ -112,4 +115,49 @@ public class GroupMemberDaoTest extends AbstractBaseDao { result = testInstance.isUserAnyDomainGroupResourceMemberWithRole(user, testUtilsDao.getD1(), MembershipRoleType.ADMIN); assertFalse(result); } + + @Test + public void testGetGroupMembersOne() { + DBGroup group = testUtilsDao.getGroupD1G1(); + DBUser user = testUtilsDao.getUser1(); + addMemberToGroup(user, group, MembershipRoleType.ADMIN); + // then + Long resultCount = testInstance.getGroupMemberCount(group.getId(), null); + List<DBGroupMember> result = testInstance.getGroupMembers(group.getId(), 0, 10, null); + assertEquals(1, resultCount.intValue()); + assertEquals(1, result.size()); + } + + @Test + public void testGetDomainMembersOneFilter() { + DBGroup group = testUtilsDao.getGroupD1G1(); + DBUser user = testUtilsDao.getUser1(); + addMemberToGroup(user, group, MembershipRoleType.ADMIN); + // then filter no match + assertFilter("NotExistsAtAll", 0, group); + assertFilter(user.getUsername(), 1, group); + assertFilter(user.getFullName(), 1, group); + + assertFilter(StringUtils.upperCase(user.getUsername()), 1, group); + assertFilter(StringUtils.upperCase(user.getFullName()), 1, group); + assertFilter(StringUtils.lowerCase(user.getUsername()), 1, group); + assertFilter(StringUtils.lowerCase(user.getFullName()), 1, group); + assertFilter("", 1, group); + assertFilter(null, 1, group); + } + + private void assertFilter(String filter, int expectedCount, DBGroup group) { + Long resultCount = testInstance.getGroupMemberCount(group.getId(), filter); + List<DBGroupMember> result = testInstance.getGroupMembers(group.getId(), 0, 10, filter); + assertEquals(expectedCount, resultCount.intValue()); + assertEquals(expectedCount, result.size()); + } + + private void addMemberToGroup(DBUser user, DBGroup group, MembershipRoleType role) { + DBGroupMember groupMember = new DBGroupMember(); + groupMember.setGroup(group); + groupMember.setUser(user); + groupMember.setRole(role); + testInstance.persistFlushDetach(groupMember); + } } diff --git a/smp-springboot/src/main/java/eu/europa/ec/springboot/smp/SMPApplication.java b/smp-springboot/src/main/java/eu/europa/ec/springboot/smp/SMPApplication.java index 0c273efd8..0828e5c00 100644 --- a/smp-springboot/src/main/java/eu/europa/ec/springboot/smp/SMPApplication.java +++ b/smp-springboot/src/main/java/eu/europa/ec/springboot/smp/SMPApplication.java @@ -69,6 +69,7 @@ public class SMPApplication implements ApplicationRunner { public static void main(String... args) { // validate parameters LOG.info("Start the SMP with parameters: [{}].", String.join(",", args)); + System.setProperty("org.apache.tomcat.util.buf.UDecoder.ALLOW_ENCODED_SLASH", "true"); // start spring boot application APPLICATION_CONTEXT = SpringApplication.run(SMPApplication.class, args); } diff --git a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/auth/SMPAuthorizationService.java b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/auth/SMPAuthorizationService.java index ca3ef952a..4bae166e7 100644 --- a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/auth/SMPAuthorizationService.java +++ b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/auth/SMPAuthorizationService.java @@ -2,6 +2,7 @@ package eu.europa.ec.edelivery.smp.auth; import eu.europa.ec.edelivery.smp.auth.enums.SMPUserAuthenticationTypes; import eu.europa.ec.edelivery.smp.data.dao.DomainMemberDao; +import eu.europa.ec.edelivery.smp.data.dao.GroupMemberDao; import eu.europa.ec.edelivery.smp.data.dao.UserDao; import eu.europa.ec.edelivery.smp.data.enums.MembershipRoleType; import eu.europa.ec.edelivery.smp.data.model.user.DBUser; @@ -38,20 +39,24 @@ public class SMPAuthorizationService { private static final String ERR_INVALID_OR_NULL = "Invalid or Expired session! Please login again."; private static final SMPLogger LOG = SMPLoggerFactory.getLogger(SMPAuthorizationService.class); - DomainMemberDao domainMemberDao; + private final UserDao userDao; + private final DomainMemberDao domainMemberDao; + private final GroupMemberDao groupMemberDao; private final ConversionService conversionService; private final ConfigurationService configurationService; - private final UserDao userDao; - public SMPAuthorizationService(ConversionService conversionService, - ConfigurationService configurationService, - UserDao userDao, - DomainMemberDao domainMemberDao) { - this.conversionService = conversionService; - this.configurationService = configurationService; + + public SMPAuthorizationService(UserDao userDao, + DomainMemberDao domainMemberDao, + GroupMemberDao groupMemberDao, + ConversionService conversionService, + ConfigurationService configurationService) { this.userDao = userDao; this.domainMemberDao = domainMemberDao; + this.groupMemberDao = groupMemberDao; + this.conversionService = conversionService; + this.configurationService = configurationService; } public boolean isSystemAdministrator() { @@ -80,7 +85,8 @@ public class SMPAuthorizationService { public boolean isAnyGroupAdministrator() { SMPUserDetails userDetails = getAndValidateUserDetails(); - return domainMemberDao.isUserGroupAdministrator(userDetails.getUser().getId()); + return groupMemberDao.isUserGroupAdministrator(userDetails.getUser().getId()) + || domainMemberDao.isUserAnyDomainAdministrator(userDetails.getUser().getId()) ; } public boolean isAnyResourceAdministrator() { SMPUserDetails userDetails = getAndValidateUserDetails(); diff --git a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/ResourceConstants.java b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/ResourceConstants.java index 291cfb011..33a19e39c 100644 --- a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/ResourceConstants.java +++ b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/ResourceConstants.java @@ -43,6 +43,8 @@ public class ResourceConstants { public static final String PARAM_PAGINATION_ORDER_TYPE="orderType"; + public static final String PARAM_ROLE="role"; + public static final String PARAM_QUERY_PARTC_ID="participantIdentifier"; public static final String PARAM_QUERY_PARTC_SCHEME="participantScheme"; public static final String PARAM_QUERY_DOMAIN_CODE ="domainCode"; diff --git a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/external/DomainResource.java b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/external/DomainResource.java index 22f91ec26..9af618087 100644 --- a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/external/DomainResource.java +++ b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/external/DomainResource.java @@ -47,14 +47,19 @@ public class DomainResource { @RequestParam(value = PARAM_QUERY_USER, required = false) String user) { LOG.info("Search for page: {}, page size: {}, user: {}", page, pageSize, user); - ServiceResult<DomainPublicRO> result = uiDomainService.getTableList(page, pageSize, orderBy, orderType, null); - return result; + return uiDomainService.getTableList(page, pageSize, orderBy, orderType, null); } + + /** + * Method returns all domains where user is administrator + * @param userEncId encrypted user identifier + * @return Domain list where user has role domain administrator + */ @GetMapping(path = "/{user-enc-id}", produces = MimeTypeUtils.APPLICATION_JSON_VALUE) @PreAuthorize("@smpAuthorizationService.isCurrentlyLoggedIn(#userEncId) and @smpAuthorizationService.anyDomainAdministrator") - public List<DomainRO> getAllDomainList(@PathVariable("user-enc-id") String userEncId) { - logAdminAccess("getAllDomainListForUser"); + public List<DomainRO> getAllDomainsForDomainAdminUser(@PathVariable("user-enc-id") String userEncId) { + logAdminAccess("getAllDomainsForDomainAdminUser"); Long userId = SessionSecurityUtils.decryptEntityId(userEncId); return uiDomainService.getAllDomainsForDomainAdminUser(userId); diff --git a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/external/GroupResource.java b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/external/GroupResource.java index c175020fb..afb79241f 100644 --- a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/external/GroupResource.java +++ b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/external/GroupResource.java @@ -1,6 +1,8 @@ package eu.europa.ec.edelivery.smp.ui.external; +import eu.europa.ec.edelivery.smp.data.enums.MembershipRoleType; +import eu.europa.ec.edelivery.smp.data.ui.DomainRO; import eu.europa.ec.edelivery.smp.data.ui.GroupRO; import eu.europa.ec.edelivery.smp.logging.SMPLogger; import eu.europa.ec.edelivery.smp.logging.SMPLoggerFactory; @@ -13,6 +15,7 @@ import org.springframework.web.bind.annotation.*; import java.util.List; import static eu.europa.ec.edelivery.smp.ui.ResourceConstants.CONTEXT_PATH_PUBLIC_GROUP; +import static eu.europa.ec.edelivery.smp.ui.ResourceConstants.PARAM_ROLE; /** * Purpose of the DomainResource is to provide search method to retrieve configured domains in SMP. @@ -33,8 +36,15 @@ public class GroupResource { } + + /** + * Return all Groups for the domain + * @param userEncId + * @param domainEncId + * @return + */ @GetMapping(path = "/{user-enc-id}/domain/{domain-enc-id}", produces = MimeTypeUtils.APPLICATION_JSON_VALUE) - @PreAuthorize("@smpAuthorizationService.isCurrentlyLoggedIn(#userEncId) and @smpAuthorizationService.isDomainAdministrator(#userEncId)") + @PreAuthorize("@smpAuthorizationService.isCurrentlyLoggedIn(#userEncId) and @smpAuthorizationService.isDomainAdministrator(#domainEncId)") public List<GroupRO> getAllGroupsForDomain(@PathVariable("user-enc-id") String userEncId, @PathVariable("domain-enc-id") String domainEncId) { logAdminAccess("getAllGroupsForDomain"); @@ -43,7 +53,7 @@ public class GroupResource { } @PutMapping(path = "/{user-enc-id}/domain/{domain-enc-id}/create", produces = MimeTypeUtils.APPLICATION_JSON_VALUE, consumes = MimeTypeUtils.APPLICATION_JSON_VALUE) - @PreAuthorize("@smpAuthorizationService.isCurrentlyLoggedIn(#userEncId) and @smpAuthorizationService.isDomainAdministrator(#userEncId)") + @PreAuthorize("@smpAuthorizationService.isCurrentlyLoggedIn(#userEncId) and @smpAuthorizationService.isDomainAdministrator(#domainEncId)") public GroupRO putGroupForDomain(@PathVariable("user-enc-id") String userEncId, @PathVariable("domain-enc-id") String domainEncId, @RequestBody GroupRO group) { @@ -52,8 +62,20 @@ public class GroupResource { return uiGroupPublicService.createGroupForDomain(domainId, group); } + @PostMapping(path = "/{user-enc-id}/{group-enc-id}/domain/{domain-enc-id}/update", produces = MimeTypeUtils.APPLICATION_JSON_VALUE, consumes = MimeTypeUtils.APPLICATION_JSON_VALUE) + @PreAuthorize("@smpAuthorizationService.isCurrentlyLoggedIn(#userEncId) and @smpAuthorizationService.isDomainAdministrator(#domainEncId)") + public GroupRO submitGroupForDomain(@PathVariable("user-enc-id") String userEncId, + @PathVariable("domain-enc-id") String domainEncId, + @PathVariable("group-enc-id") String groupEncId, + @RequestBody GroupRO group) { + logAdminAccess("updateGroupForDomain"); + Long domainId = SessionSecurityUtils.decryptEntityId(domainEncId); + Long groupId = SessionSecurityUtils.decryptEntityId(groupEncId); + return uiGroupPublicService.saveGroupForDomain(domainId,groupId, group); + } + @DeleteMapping(path = "/{user-enc-id}/{group-enc-id}/domain/{domain-enc-id}/delete", produces = MimeTypeUtils.APPLICATION_JSON_VALUE) - @PreAuthorize("@smpAuthorizationService.isCurrentlyLoggedIn(#userEncId) and @smpAuthorizationService.isDomainAdministrator(#userEncId)") + @PreAuthorize("@smpAuthorizationService.isCurrentlyLoggedIn(#userEncId) and @smpAuthorizationService.isDomainAdministrator(#domainEncId)") public GroupRO deleteGroupFromDomain(@PathVariable("user-enc-id") String userEncId, @PathVariable("domain-enc-id") String domainEncId, @PathVariable("group-enc-id") String groupEncId) { diff --git a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/external/UserResource.java b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/external/UserResource.java index 2951303d0..465762ac3 100644 --- a/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/external/UserResource.java +++ b/smp-webapp/src/main/java/eu/europa/ec/edelivery/smp/ui/external/UserResource.java @@ -135,7 +135,7 @@ public class UserResource { @PreAuthorize("@smpAuthorizationService.isCurrentlyLoggedIn(#userId)") @GetMapping(path = "/{user-id}/username-credential-status") public CredentialRO getUsernameCredentialStatus(@PathVariable("user-id") String userId) { - LOG.debug("get User credential status: [{}]", userId); + LOG.debug("Get user credential status for user: [{}]", userId); Long entityId = decryptEntityId(userId); // Update the user and mark the password as changed at this very instant of time List<CredentialRO> credentialROList = uiUserService.getUserCredentials(entityId, @@ -147,7 +147,7 @@ public class UserResource { @PreAuthorize("@smpAuthorizationService.isCurrentlyLoggedIn(#encUserId)") @GetMapping(path = "/{user-id}/access-token-credentials") public List<CredentialRO> getAccessTokenCredentials(@PathVariable("user-id") String encUserId) { - LOG.debug("get User credential status: [{}]", encUserId); + LOG.debug("Get access token credential status for user:: [{}]", encUserId); Long userId = decryptEntityId(encUserId); // Update the user and mark the password as changed at this very instant of time return uiUserService.getUserCredentials(userId, @@ -256,7 +256,7 @@ public class UserResource { protected NavigationTreeNodeRO createPublicNavigationTreeNode() { NavigationTreeNodeRO node = new NavigationTreeNodeRO("search-tools", "Search", "search", "public"); node.addChild(new NavigationTreeNodeRO("search-resources", "Resources", "find_in_page", "search-resource","Search registered resources")); - node.addChild(new NavigationTreeNodeRO("search-lookup", "DNS lookup", "dns", "dns-lookup" , "DNS lookup tools")); + //node.addChild(new NavigationTreeNodeRO("search-lookup", "DNS lookup", "dns", "dns-lookup" , "DNS lookup tools")); return node; } @@ -290,7 +290,7 @@ public class UserResource { } if (authorizationService.isAnyGroupAdministrator()) { // is group admin - node.addChild(new NavigationTreeNodeRO("edit-group", "Edit groups", "key", "edit-group")); + node.addChild(new NavigationTreeNodeRO("edit-group", "Edit groups", "people", "edit-group")); } if (authorizationService.isAnyResourceAdministrator()) { // is resource admin diff --git a/smp-webapp/src/test/java/eu/europa/ec/edelivery/smp/auth/SMPAuthorizationServiceTest.java b/smp-webapp/src/test/java/eu/europa/ec/edelivery/smp/auth/SMPAuthorizationServiceTest.java index 5fadb0834..04c58d10e 100644 --- a/smp-webapp/src/test/java/eu/europa/ec/edelivery/smp/auth/SMPAuthorizationServiceTest.java +++ b/smp-webapp/src/test/java/eu/europa/ec/edelivery/smp/auth/SMPAuthorizationServiceTest.java @@ -1,6 +1,7 @@ package eu.europa.ec.edelivery.smp.auth; import eu.europa.ec.edelivery.smp.data.dao.DomainMemberDao; +import eu.europa.ec.edelivery.smp.data.dao.GroupMemberDao; import eu.europa.ec.edelivery.smp.data.dao.UserDao; import eu.europa.ec.edelivery.smp.data.model.user.DBUser; import eu.europa.ec.edelivery.smp.data.ui.UserRO; @@ -33,9 +34,10 @@ public class SMPAuthorizationServiceTest { ConfigurationService configurationService = Mockito.mock(ConfigurationService.class); UserDao userDao = Mockito.mock(UserDao.class); DomainMemberDao domainMemberDao = Mockito.mock(DomainMemberDao.class); + GroupMemberDao groupMemberDao = Mockito.mock(GroupMemberDao.class); - SMPAuthorizationService testInstance = new SMPAuthorizationService(conversionService, - configurationService, userDao, domainMemberDao); + SMPAuthorizationService testInstance = new SMPAuthorizationService(userDao, domainMemberDao, groupMemberDao, conversionService, + configurationService); @Before -- GitLab