diff --git a/apps/admin-gui/src/app/shared/side-menu/side-menu-item.service.ts b/apps/admin-gui/src/app/shared/side-menu/side-menu-item.service.ts index 05f8128e0..dc605223b 100644 --- a/apps/admin-gui/src/app/shared/side-menu/side-menu-item.service.ts +++ b/apps/admin-gui/src/app/shared/side-menu/side-menu-item.service.ts @@ -994,20 +994,20 @@ export class SideMenuItemService { this.apiRequest.dontHandleErrorForNext(); this.attributesManager .getGroupAttributeByName(group.id, Urns.GROUP_DEF_EXPIRATION_RULES) - .subscribe( - () => { - settingsChildrenLinks.push({ + .subscribe({ + next: () => { + settingsChildrenLinks.unshift({ label: 'MENU_ITEMS.GROUP.EXPIRATION', url: [`/organizations/${group.voId}/groups/${group.id}/settings/expiration`], activatedRegex: '/organizations/\\d+/groups/\\d+/settings/expiration$', }); }, - (error: RPCError) => { + error: (error: RPCError) => { if (error.name !== 'PrivilegeException') { this.notificator.showRPCError(error); } - } - ); + }, + }); //SettingsManagers if (this.routePolicyService.canNavigate('groups-settings-managers', group)) { diff --git a/apps/admin-gui/src/app/vos/components/notification-list/notification-list.component.html b/apps/admin-gui/src/app/vos/components/notification-list/notification-list.component.html index 20a69266f..020f70e4a 100644 --- a/apps/admin-gui/src/app/vos/components/notification-list/notification-list.component.html +++ b/apps/admin-gui/src/app/vos/components/notification-list/notification-list.component.html @@ -52,13 +52,17 @@ {{'VO_DETAIL.SETTINGS.NOTIFICATIONS.TABLE_APPLICATION_TYPE' | translate}} -
+
arrow_right_alt {{'VO_DETAIL.SETTINGS.NOTIFICATIONS.TABLE_APPLICATION_TYPE_INITIAL' | translate}}
- cached - {{'VO_DETAIL.SETTINGS.NOTIFICATIONS.TABLE_APPLICATION_TYPE_EXTENSION' | translate}} +
+ cached + {{'VO_DETAIL.SETTINGS.NOTIFICATIONS.TABLE_APPLICATION_TYPE_EXTENSION' | translate}} +
diff --git a/apps/admin-gui/src/app/vos/pages/group-detail-page/group-subgroups/group-subgroups.component.html b/apps/admin-gui/src/app/vos/pages/group-detail-page/group-subgroups/group-subgroups.component.html index ddc902c37..d940753df 100644 --- a/apps/admin-gui/src/app/vos/pages/group-detail-page/group-subgroups/group-subgroups.component.html +++ b/apps/admin-gui/src/app/vos/pages/group-detail-page/group-subgroups/group-subgroups.component.html @@ -53,6 +53,7 @@

{{'GROUP_DETAIL.SUBGROUPS.TITLE' | translate}}

[groups]="groups" (moveGroup)="onMoveGroup($event)" (refreshTable)="refreshTable()" + [displayedColumns]="['nameWithId', 'description', 'menu']" [hideCheckbox]="!deleteAuth" [filterValue]="filterValue" [selection]="selected"> diff --git a/apps/admin-gui/src/app/vos/pages/member-detail-page/member-groups/member-groups.component.html b/apps/admin-gui/src/app/vos/pages/member-detail-page/member-groups/member-groups.component.html index af586c620..48665d6a1 100644 --- a/apps/admin-gui/src/app/vos/pages/member-detail-page/member-groups/member-groups.component.html +++ b/apps/admin-gui/src/app/vos/pages/member-detail-page/member-groups/member-groups.component.html @@ -25,20 +25,45 @@

{{'MEMBER_DETAIL.GROUPS.TITLE' | translate}}

(filter)="applyFilter($event)" [placeholder]="'SHARED_LIB.PERUN.COMPONENTS.RESOURCES_LIST.TABLE_SEARCH'"> + + + +
- - +
+ + +
+
+ + +
diff --git a/apps/admin-gui/src/app/vos/pages/member-detail-page/member-groups/member-groups.component.scss b/apps/admin-gui/src/app/vos/pages/member-detail-page/member-groups/member-groups.component.scss index e69de29bb..f83aab731 100644 --- a/apps/admin-gui/src/app/vos/pages/member-detail-page/member-groups/member-groups.component.scss +++ b/apps/admin-gui/src/app/vos/pages/member-detail-page/member-groups/member-groups.component.scss @@ -0,0 +1,4 @@ +.slide-label { + display: inline; + cursor: pointer; +} diff --git a/apps/admin-gui/src/app/vos/pages/member-detail-page/member-groups/member-groups.component.ts b/apps/admin-gui/src/app/vos/pages/member-detail-page/member-groups/member-groups.component.ts index ef197f728..2476273d1 100644 --- a/apps/admin-gui/src/app/vos/pages/member-detail-page/member-groups/member-groups.component.ts +++ b/apps/admin-gui/src/app/vos/pages/member-detail-page/member-groups/member-groups.component.ts @@ -1,4 +1,4 @@ -import { Component, HostBinding, OnInit } from '@angular/core'; +import { Component, HostBinding, OnInit, ViewChild } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { Group, @@ -17,6 +17,9 @@ import { GuiAuthResolver } from '@perun-web-apps/perun/services'; import { Urns } from '@perun-web-apps/perun/urns'; import { Observable } from 'rxjs'; import { map, startWith } from 'rxjs/operators'; +import { MatSlideToggle } from '@angular/material/slide-toggle'; +import { GroupsListComponent } from '@perun-web-apps/perun/components'; +import { GroupWithStatus } from '@perun-web-apps/perun/models'; @Component({ selector: 'app-member-groups', @@ -28,6 +31,8 @@ export class MemberGroupsComponent implements OnInit { // used for router animation @HostBinding('class.router-component') true; + @ViewChild('toggle', { static: true }) toggle: MatSlideToggle; + @ViewChild('list') private list: GroupsListComponent; groups: Group[] = []; memberId: number; @@ -35,7 +40,9 @@ export class MemberGroupsComponent implements OnInit { allGroups: Group[]; loading: boolean; filterValue = ''; + filtering = false; tableId = TABLE_MEMBER_DETAIL_GROUPS; + showGroupList = false; selection = new SelectionModel(true, []); addAuth: boolean; routeAuth: boolean; @@ -67,6 +74,16 @@ export class MemberGroupsComponent implements OnInit { this.groupsService.getAllGroups(this.member.voId).subscribe((allGroups) => { this.allGroups = allGroups; this.refreshTable(); + if (localStorage.getItem('preferedValue') === 'list') { + this.toggle.toggle(); + this.showGroupList = true; + } + + this.toggle.change.subscribe(() => { + const value = this.toggle.checked ? 'list' : 'tree'; + localStorage.setItem('preferedValue', value); + this.refreshTable(); + }); }); }); }); @@ -80,15 +97,19 @@ export class MemberGroupsComponent implements OnInit { Urns.MEMBER_GROUP_STATUS, Urns.MEMBER_GROUP_STATUS_INDIRECT, ]) - .subscribe( - (groups) => { + .subscribe({ + next: (groups) => { this.selection.clear(); this.groups = groups; this.setAuthRights(); this.loading = false; }, - () => (this.loading = false) - ); + error: () => (this.loading = false), + }); + } + + changeExpiration(group: GroupWithStatus): void { + this.list.changeExpiration(group); } setAuthRights(): void { @@ -148,5 +169,11 @@ export class MemberGroupsComponent implements OnInit { applyFilter(filterValue: string): void { this.filterValue = filterValue; + this.filtering = filterValue !== ''; + } + + labelToggle(): void { + this.showGroupList = !this.showGroupList; + this.refreshTable(); } } diff --git a/apps/admin-gui/src/app/vos/pages/vo-detail-page/vo-groups/vo-groups.component.html b/apps/admin-gui/src/app/vos/pages/vo-detail-page/vo-groups/vo-groups.component.html index aab0a4055..08ea4381c 100644 --- a/apps/admin-gui/src/app/vos/pages/vo-detail-page/vo-groups/vo-groups.component.html +++ b/apps/admin-gui/src/app/vos/pages/vo-detail-page/vo-groups/vo-groups.component.html @@ -42,6 +42,7 @@

{{'VO_DETAIL.GROUPS.TITLE' | translate}}

*perunWebAppsLoader="loading$ | async; indicator: spinner" (moveGroup)="onMoveGroup($event)" (refreshTable)="refresh()" + [displayedColumns]="['nameWithId', 'description', 'menu']" [expandAll]="filtering" [disableRouting]="!routeAuth" [groups]="groups" diff --git a/apps/admin-gui/src/assets/i18n/en.json b/apps/admin-gui/src/assets/i18n/en.json index 285bddb65..68e2c39b9 100644 --- a/apps/admin-gui/src/assets/i18n/en.json +++ b/apps/admin-gui/src/assets/i18n/en.json @@ -832,7 +832,9 @@ "TITLE": "Member groups", "ADD": "Add", "REMOVE": "Remove", - "REMOVE_PERMISSION_HINT": "You don't have permission to remove some of the selected groups." + "REMOVE_PERMISSION_HINT": "You don't have permission to remove some of the selected groups.", + "LIST_VIEW": "List view", + "TREE_VIEW": "Tree view" }, "APPLICATIONS": { "TITLE": "Application", @@ -3104,10 +3106,22 @@ "USER_DONT_EXIST": { "TITLE": "The requested user (by Id or external identity) doesn't exist." }, + "USER_NOT_ALLOWED": { + "TITLE": "Access not allowed", + "MESSAGE": "Service accounts are not allowed to access this application.", + "REDIRECT": "Redirect to login page" + }, "TABLE_OPTIONS": { "EXPORT_TO_FILE": "Export to file", - "ALL_DATA": "All data", + "EXPORT_TITLE": "Export table", + "BUTTON_EXPORT": "Export", + "BUTTON_CLOSE": "Close", + "SELECT_FORMAT": "Select format", + "EXPORT_OPTIONS": "Export options", "DISPLAYED_DATA": "Displayed data", + "ALL_DATA": "All data", + "ERROR_FORMAT":"Export format is not selected", + "ERROR_OPTION": "Export option is not selected", "EXPORT_LOADING": "Exporting data...", "MORE": "More" }, @@ -3161,6 +3175,15 @@ "FIELD_EMPTY": "This field cannot be empty", "SUCCESS": "Password has been changed successfully," }, + "RESET_PASSWORD_DIALOG": { + "LOGIN": "Login", + "NAMESPACE": "Namespace", + "TITLE": "Reset password", + "CANCEL": "Cancel", + "CHANGE": "Confirm", + "FIELD_EMPTY": "This field cannot be empty", + "SUCCESS": "Password has been reset successfully," + }, "PASSWORD_FORM_FIELD": { "PASSWORD": "Password", "PASSWORD_AGAIN": "Confirm password", diff --git a/apps/consolidator/src/assets/i18n/en.json b/apps/consolidator/src/assets/i18n/en.json index 1a37fdea8..4d8635380 100644 --- a/apps/consolidator/src/assets/i18n/en.json +++ b/apps/consolidator/src/assets/i18n/en.json @@ -76,6 +76,11 @@ "REMOVE": "Remove", "SUCCESS": "User account successfully removed" } + }, + "USER_NOT_ALLOWED": { + "TITLE": "Access not allowed", + "MESSAGE": "Service accounts are not allowed to access this application.", + "REDIRECT": "Redirect to login page" } }, "CONSOLIDATOR": { diff --git a/apps/linker/src/assets/i18n/en.json b/apps/linker/src/assets/i18n/en.json index 9082b16b0..91b0c3cde 100644 --- a/apps/linker/src/assets/i18n/en.json +++ b/apps/linker/src/assets/i18n/en.json @@ -35,6 +35,11 @@ "FOCUS_ON_MFA_DIALOG": { "MODAL": "Modal window is opened.", "MODAL_WARNING": "Please check your browser settings if no modal window is open." + }, + "USER_NOT_ALLOWED": { + "TITLE": "Access not allowed", + "MESSAGE": "Service accounts are not allowed to access this application.", + "REDIRECT": "Redirect to login page" } } }, diff --git a/apps/password-reset/src/assets/i18n/cs.json b/apps/password-reset/src/assets/i18n/cs.json index 4f3048afc..a43115e1b 100644 --- a/apps/password-reset/src/assets/i18n/cs.json +++ b/apps/password-reset/src/assets/i18n/cs.json @@ -38,6 +38,11 @@ "USER_DONT_EXIST": { "TITLE": "Požadovaný uživatel (dle ID nebo externí identity) neexistuje." }, + "USER_NOT_ALLOWED": { + "TITLE": "Přístup není povolen", + "MESSAGE": "Servisní účty (Service account) nemají povolen přístup k této aplikaci.", + "REDIRECT": "Přesměrovaní na přihlášení" + }, "SESSION_EXPIRATION": { "TITLE": "Platnost přihlášení vypršela", "DESCRIPTION": "Byli jste automaticky odhlášeni. Pro pokračování se znovu přihlaste.", diff --git a/apps/password-reset/src/assets/i18n/en.json b/apps/password-reset/src/assets/i18n/en.json index 888489b12..12f2169d0 100644 --- a/apps/password-reset/src/assets/i18n/en.json +++ b/apps/password-reset/src/assets/i18n/en.json @@ -38,6 +38,11 @@ "USER_DONT_EXIST": { "TITLE": "Requested user (by ID or external identity) doesn't exist." }, + "USER_NOT_ALLOWED": { + "TITLE": "Access not allowed", + "MESSAGE": "Service accounts are not allowed to access this application.", + "REDIRECT": "Redirect to login page" + }, "SESSION_EXPIRATION": { "TITLE": "Session expiration", "DESCRIPTION": "Your session has expired. Please sign in to continue.", diff --git a/apps/publications/src/assets/i18n/en.json b/apps/publications/src/assets/i18n/en.json index a36fa025f..2387ea2e6 100644 --- a/apps/publications/src/assets/i18n/en.json +++ b/apps/publications/src/assets/i18n/en.json @@ -356,11 +356,24 @@ "USER_DONT_EXIST": { "TITLE": "Requested user (by ID or external identity) doesn't exist." }, + "USER_NOT_ALLOWED": { + "TITLE": "Access not allowed", + "MESSAGE": "Service accounts are not allowed to access this application.", + "REDIRECT": "Redirect to login page" + }, "TABLE_OPTIONS": { "EXPORT_TO_FILE": "Export to file", - "ALL_DATA": "All data", + "EXPORT_TITLE": "Export table", + "BUTTON_EXPORT": "Export", + "BUTTON_CLOSE": "Close", + "SELECT_FORMAT": "Select format", + "EXPORT_OPTIONS": "Export options", "DISPLAYED_DATA": "Displayed data", - "EXPORT_LOADING": "Exporting data..." + "ALL_DATA": "All data", + "ERROR_FORMAT":"Export format is not selected", + "ERROR_OPTION": "Export option is not selected", + "EXPORT_LOADING": "Exporting data...", + "MORE": "More" }, "NOTIFICATOR": { "NOTIFICATION": { diff --git a/apps/user-profile/src/assets/i18n/cs.json b/apps/user-profile/src/assets/i18n/cs.json index 44b2c8c99..7fbd0a81c 100644 --- a/apps/user-profile/src/assets/i18n/cs.json +++ b/apps/user-profile/src/assets/i18n/cs.json @@ -12,17 +12,14 @@ "PRIVACY": "Soukromí", "CONSENTS": "Souhlasy", "CONSENT_REQUEST": "Žádost o souhlas", + "AUTHENTICATION": "Autentizace", "SETTINGS": "Nastavení" }, "SETTINGS": { - "ALTERNATIVE_PASSWORDS": "Alternativní hesla", "DATA_QUOTAS": "Kvóty dat", "MAILING_LISTS": "Vyřazení ze seznamu adresátů", "PREFERRED_SHELLS": "Preferované shelly", "PREFERRED_UNIX_GROUP_NAMES": "Preferovaná jména Unixových skupin", - "SAMBA_PASSWORD": "Heslo pro službu SAMBA", - "SSH_KEYS": "SSH klíče", - "PASSWORD_RESET": "Změna hesla", "AUTHENTICATION": "Autentizace", "LOCAL_ACCOUNT": "Lokální účet" }, @@ -154,7 +151,14 @@ "SAVE_IMG_SUCCESS": "Bezpečnostní obrázek byl změněn.", "REMOVE_IMG_SUCCESS": "Bezpečnostní obrázek byl odebrán.", "SAVE_TEXT_SUCCESS": "Bezpečnostní text byl změněn.", - "REMOVE_TEXT_SUCCESS": "Bezpečnostní text byl odebrán." + "REMOVE_TEXT_SUCCESS": "Bezpečnostní text byl odebrán.", + "SAMBA_PASSWORD": "Heslo pro službu SAMBA", + "LOCAL_ACCOUNT": "Lokální účet", + "ANTI_PHISHING": "Anti-phishingová ochrana", + "ACCOUNT_ACTIVATION": "Aktivace účtu", + "SSH_KEYS": "SSH klíče", + "PASSWORD_RESET": "Změna hesla", + "ALTERNATIVE_PASSWORDS": "Alternativní hesla" }, "LOCAL_ACCOUNT": { "TITLE": "Lokální účet", @@ -309,11 +313,24 @@ "USER_DONT_EXIST": { "TITLE": "Požadovaný uživatel (dle ID nebo externí identity) neexistuje." }, + "USER_NOT_ALLOWED": { + "TITLE": "Přístup není povolen", + "MESSAGE": "Servisní účty (Service account) nemají povolen přístup k této aplikaci.", + "REDIRECT": "Přesměrovaní na přihlášení" + }, "TABLE_OPTIONS": { "EXPORT_TO_FILE": "Exportovat do souboru", - "ALL_DATA": "Všechna data", + "EXPORT_TITLE": "Exportovat tabulku", + "BUTTON_EXPORT": "Export", + "BUTTON_CLOSE": "Zrušit", + "SELECT_FORMAT": "Zvolte formát výstupu", + "EXPORT_OPTIONS": "Možnosti exportu", "DISPLAYED_DATA": "Zobrazená data", - "EXPORT_LOADING": "Probíhá exportování dat..." + "ALL_DATA": "Všechna data", + "ERROR_FORMAT":"Formát exportu není zvolen", + "ERROR_OPTION": "Není vybrána možnost exportu", + "EXPORT_LOADING": "Probíhá exportování dat...", + "MORE": "Více" }, "NOTIFICATOR": { "NOTIFICATION": { @@ -412,6 +429,15 @@ "FIELD_EMPTY": "Tohle políčko musí být vyplněné.", "SUCCESS": "Heslo bylo úspěšně změněno" }, + "RESET_PASSWORD_DIALOG": { + "LOGIN": "Jméno", + "NAMESPACE": "Namespace", + "TITLE": "Resetovat heslo", + "CANCEL": "Zrušit", + "CHANGE": "Potvrdit", + "FIELD_EMPTY": "Tohle políčko musí být vyplněné.", + "SUCCESS": "Heslo bylo úspěšně resetováno" + }, "PASSWORD_FORM_FIELD": { "PASSWORD": "Nové heslo", "PASSWORD_AGAIN": "Znovu zadejte nové heslo", diff --git a/apps/user-profile/src/assets/i18n/en.json b/apps/user-profile/src/assets/i18n/en.json index ecd7677ec..b3d6541a3 100644 --- a/apps/user-profile/src/assets/i18n/en.json +++ b/apps/user-profile/src/assets/i18n/en.json @@ -353,11 +353,24 @@ "USER_DONT_EXIST": { "TITLE": "Requested user (by ID or external identity) doesn't exist." }, + "USER_NOT_ALLOWED": { + "TITLE": "Access not allowed", + "MESSAGE": "Service accounts are not allowed to access this application.", + "REDIRECT": "Redirect to login page" + }, "TABLE_OPTIONS": { "EXPORT_TO_FILE": "Export to file", - "ALL_DATA": "All data", + "EXPORT_TITLE": "Export table", + "BUTTON_EXPORT": "Export", + "BUTTON_CLOSE": "Close", + "SELECT_FORMAT": "Select format", + "EXPORT_OPTIONS": "Export options", "DISPLAYED_DATA": "Displayed data", - "EXPORT_LOADING": "Exporting data..." + "ALL_DATA": "All data", + "ERROR_FORMAT":"Export format is not selected", + "ERROR_OPTION": "Export option is not selected", + "EXPORT_LOADING": "Exporting data...", + "MORE": "More" }, "NOTIFICATOR": { "NOTIFICATION": { @@ -480,6 +493,15 @@ "FIELD_EMPTY": "This field cannot be empty", "SUCCESS": "Password has been changed successfully," }, + "RESET_PASSWORD_DIALOG": { + "LOGIN": "Login", + "NAMESPACE": "Namespace", + "TITLE": "Reset password", + "CANCEL": "Cancel", + "CHANGE": "Confirm", + "FIELD_EMPTY": "This field cannot be empty", + "SUCCESS": "Password has been reset successfully," + }, "PASSWORD_FORM_FIELD": { "PASSWORD": "New password", "PASSWORD_AGAIN": "New password again", diff --git a/libs/general/src/index.ts b/libs/general/src/index.ts index 621b08a8e..cefb9ef80 100644 --- a/libs/general/src/index.ts +++ b/libs/general/src/index.ts @@ -2,3 +2,4 @@ export * from './lib/general.module'; export * from './lib/server-down-dialog/server-down-dialog.component'; export * from './lib/user-dont-exist-dialog/user-dont-exist-dialog.component'; export * from './lib/prevent-proxy-overload-dialog/prevent-proxy-overload-dialog.component'; +export * from './lib/user-not-allowed-access/user-not-allowed-access.component'; diff --git a/libs/general/src/lib/general.module.ts b/libs/general/src/lib/general.module.ts index bde6a12a7..22ae0f7cf 100644 --- a/libs/general/src/lib/general.module.ts +++ b/libs/general/src/lib/general.module.ts @@ -6,17 +6,20 @@ import { MatButtonModule } from '@angular/material/button'; import { UserDontExistDialogComponent } from './user-dont-exist-dialog/user-dont-exist-dialog.component'; import { TranslateModule } from '@ngx-translate/core'; import { PreventProxyOverloadDialogComponent } from './prevent-proxy-overload-dialog/prevent-proxy-overload-dialog.component'; +import { UserNotAllowedAccessComponent } from './user-not-allowed-access/user-not-allowed-access.component'; @NgModule({ imports: [CommonModule, MatDialogModule, MatButtonModule, TranslateModule], exports: [ ServerDownDialogComponent, UserDontExistDialogComponent, + UserNotAllowedAccessComponent, PreventProxyOverloadDialogComponent, ], declarations: [ ServerDownDialogComponent, UserDontExistDialogComponent, + UserNotAllowedAccessComponent, PreventProxyOverloadDialogComponent, ], }) diff --git a/libs/general/src/lib/user-not-allowed-access/user-not-allowed-access.component.html b/libs/general/src/lib/user-not-allowed-access/user-not-allowed-access.component.html new file mode 100644 index 000000000..35fdd34cb --- /dev/null +++ b/libs/general/src/lib/user-not-allowed-access/user-not-allowed-access.component.html @@ -0,0 +1,11 @@ +

+ {{'SHARED_LIB.PERUN.COMPONENTS.USER_NOT_ALLOWED.TITLE' | translate}} +

+
+ {{'SHARED_LIB.PERUN.COMPONENTS.USER_NOT_ALLOWED.MESSAGE' | translate}} +
+
+ +
diff --git a/libs/general/src/lib/user-not-allowed-access/user-not-allowed-access.component.scss b/libs/general/src/lib/user-not-allowed-access/user-not-allowed-access.component.scss new file mode 100644 index 000000000..e69de29bb diff --git a/libs/general/src/lib/user-not-allowed-access/user-not-allowed-access.component.ts b/libs/general/src/lib/user-not-allowed-access/user-not-allowed-access.component.ts new file mode 100644 index 000000000..92a2989eb --- /dev/null +++ b/libs/general/src/lib/user-not-allowed-access/user-not-allowed-access.component.ts @@ -0,0 +1,15 @@ +import { Component } from '@angular/core'; +import { MatDialogRef } from '@angular/material/dialog'; + +@Component({ + selector: 'perun-web-apps-user-dont-exist-dialog', + templateUrl: './user-not-allowed-access.component.html', + styleUrls: ['./user-not-allowed-access.component.scss'], +}) +export class UserNotAllowedAccessComponent { + constructor(public dialogRef: MatDialogRef) {} + + redirect(): void { + this.dialogRef.close(); + } +} diff --git a/libs/perun/components/src/lib/groups-list/groups-list.component.html b/libs/perun/components/src/lib/groups-list/groups-list.component.html index de02896f8..f32ee5a35 100644 --- a/libs/perun/components/src/lib/groups-list/groups-list.component.html +++ b/libs/perun/components/src/lib/groups-list/groups-list.component.html @@ -160,16 +160,18 @@ {{'SHARED_LIB.PERUN.COMPONENTS.GROUPS_LIST.TABLE_GROUP_EXPIRATION' | translate}} - {{group | groupExpiration | parseDate}} - + + {{group | groupExpiration | parseDate}} + + diff --git a/libs/perun/components/src/lib/groups-list/groups-list.component.ts b/libs/perun/components/src/lib/groups-list/groups-list.component.ts index 02566afa0..b4528fb91 100644 --- a/libs/perun/components/src/lib/groups-list/groups-list.component.ts +++ b/libs/perun/components/src/lib/groups-list/groups-list.component.ts @@ -5,12 +5,7 @@ import { EditFacilityResourceGroupVoDialogOptions, GroupSyncDetailDialogComponent, } from '@perun-web-apps/perun/dialogs'; -import { - Group, - Member, - PaginatedRichGroups, - VosManagerService, -} from '@perun-web-apps/perun/openapi'; +import { Group, PaginatedRichGroups, VosManagerService } from '@perun-web-apps/perun/openapi'; import { GuiAuthResolver, TableCheckbox } from '@perun-web-apps/perun/services'; import { customDataSourceFilterPredicate, @@ -259,8 +254,8 @@ export class GroupsListComponent { }; const dialogRef = this.dialog.open(ChangeGroupExpirationDialogComponent, config); - dialogRef.afterClosed().subscribe((success: { success: boolean; member: Member }) => { - if (success.success) { + dialogRef.afterClosed().subscribe((success) => { + if (success) { this.refreshTable.emit(); } }); diff --git a/libs/perun/components/src/lib/groups-tree/groups-tree.component.html b/libs/perun/components/src/lib/groups-tree/groups-tree.component.html index 8179fba3d..c9c6597fb 100644 --- a/libs/perun/components/src/lib/groups-tree/groups-tree.component.html +++ b/libs/perun/components/src/lib/groups-tree/groups-tree.component.html @@ -37,7 +37,7 @@ {{treeControl.isExpanded(group) ? 'expand_more' : 'chevron_right'}} -
+
{{group.name}} @@ -45,13 +45,41 @@ #{{group.id}}
-
+
{{group.description}}
-
+
+ {{group | groupExpiration | parseDate}} + +
+
+ + + {{groupStatus.status | memberStatusIcon}} + + +
+
(); @Output() refreshTable = new EventEmitter(); + @Output() changeExpiration = new EventEmitter(); @Input() groups: RichGroup[]; @Input() filterValue: string; @Input() expandAll = false; @@ -44,8 +45,10 @@ export class GroupsTreeComponent implements OnChanges { @Input() selection = new SelectionModel(true, []); @Input() hideCheckbox = false; @Input() vo: Vo; + @Input() displayedColumns = ['nameWithId', 'description', 'menu', 'expiration', 'status']; @ViewChild('scrollViewport', { static: false }) scrollViewport: CdkVirtualScrollViewport; + disabledRouting = false; displayButtons = window.innerWidth > 600; removeAuth: boolean; diff --git a/libs/perun/components/src/lib/password-reset/password-reset.component.html b/libs/perun/components/src/lib/password-reset/password-reset.component.html index 701b5d34a..2f3a0852d 100644 --- a/libs/perun/components/src/lib/password-reset/password-reset.component.html +++ b/libs/perun/components/src/lib/password-reset/password-reset.component.html @@ -32,7 +32,7 @@

+ +

+
+
+
diff --git a/libs/perun/dialogs/src/lib/password-reset-dialog/password-reset-dialog.component.scss b/libs/perun/dialogs/src/lib/password-reset-dialog/password-reset-dialog.component.scss new file mode 100644 index 000000000..e69de29bb diff --git a/libs/perun/dialogs/src/lib/password-reset-dialog/password-reset-dialog.component.ts b/libs/perun/dialogs/src/lib/password-reset-dialog/password-reset-dialog.component.ts new file mode 100644 index 000000000..803880b1e --- /dev/null +++ b/libs/perun/dialogs/src/lib/password-reset-dialog/password-reset-dialog.component.ts @@ -0,0 +1,94 @@ +import { Component, Inject, OnInit } from '@angular/core'; +import { TranslateService } from '@ngx-translate/core'; +import { + ApiRequestConfigurationService, + NotificatorService, + StoreService, +} from '@perun-web-apps/perun/services'; +import { UsersManagerService } from '@perun-web-apps/perun/openapi'; +import { loginAsyncValidator } from '@perun-web-apps/perun/namespace-password-form'; +import { FormBuilder, FormControl, FormGroup, ValidatorFn, Validators } from '@angular/forms'; +import { CustomValidators } from '@perun-web-apps/perun/utils'; +import { PasswordLabels } from '@perun-web-apps/perun/models'; +import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; + +export interface ResetPasswordDialogData { + namespace: string; + login: string; +} + +@Component({ + selector: 'perun-web-apps-password-reset-dialog', + templateUrl: './password-reset-dialog.component.html', + styleUrls: ['./password-reset-dialog.component.scss'], +}) +export class PasswordResetDialogComponent implements OnInit { + loading = false; + language = 'en'; + newPasswdForm: FormGroup<{ + passwordCtrl: FormControl; + passwordAgainCtrl: FormControl; + }>; + labels: PasswordLabels; + + constructor( + @Inject(MAT_DIALOG_DATA) public data: ResetPasswordDialogData, + private dialogRef: MatDialogRef, + private storeService: StoreService, + private translate: TranslateService, + private apiRequestConfiguration: ApiRequestConfigurationService, + private usersService: UsersManagerService, + private formBuilder: FormBuilder, + private notificator: NotificatorService + ) {} + + ngOnInit(): void { + this.newPasswdForm = this.formBuilder.group( + { + passwordCtrl: [ + '', + Validators.required, + [ + loginAsyncValidator( + this.data.namespace, + this.usersService, + this.apiRequestConfiguration + ), + ], + ], + passwordAgainCtrl: ['', Validators.required], + }, + { + validators: CustomValidators.passwordMatchValidator as ValidatorFn, + } + ); + this.setLabels(this.translate.currentLang); + } + + onSubmit(): void { + this.loading = true; + this.usersService + .changePasswordForLogin({ + login: this.data.login, + namespace: this.data.namespace, + newPassword: this.newPasswdForm.value.passwordCtrl, + }) + .subscribe(() => { + this.notificator.showInstantSuccess( + 'SHARED_LIB.PERUN.COMPONENTS.RESET_PASSWORD_DIALOG.SUCCESS' + ); + this.loading = false; + this.dialogRef.close(true); + }); + } + + close(): void { + this.dialogRef.close(false); + } + + private setLabels(lang: string): void { + this.labels = this.storeService.getProperty( + lang === 'en' ? 'password_labels' : 'password_labels_cs' + ); + } +} diff --git a/libs/perun/dialogs/src/lib/perun-dialogs.module.ts b/libs/perun/dialogs/src/lib/perun-dialogs.module.ts index 61d112d6b..7e928fad7 100644 --- a/libs/perun/dialogs/src/lib/perun-dialogs.module.ts +++ b/libs/perun/dialogs/src/lib/perun-dialogs.module.ts @@ -48,6 +48,7 @@ import { ExportDataDialogComponent } from './exporting-data-dialog/export-data-d import { RequestChangeDataQuotaDialogComponent } from './request-change-data-quota-dialog/request-change-data-quota-dialog.component'; import { ExpirationSelectComponent } from './expiration-select/expiration-select.component'; import { DeleteUserDialogComponent } from './delete-user-dialog/delete-user-dialog.component'; +import { PasswordResetDialogComponent } from './password-reset-dialog/password-reset-dialog.component'; @NgModule({ imports: [ @@ -103,6 +104,7 @@ import { DeleteUserDialogComponent } from './delete-user-dialog/delete-user-dial RequestChangeDataQuotaDialogComponent, ExpirationSelectComponent, DeleteUserDialogComponent, + PasswordResetDialogComponent, ], exports: [ ChangeExpirationDialogComponent, @@ -128,6 +130,7 @@ import { DeleteUserDialogComponent } from './delete-user-dialog/delete-user-dial RequestChangeDataQuotaDialogComponent, ExpirationSelectComponent, DeleteUserDialogComponent, + PasswordResetDialogComponent, ], }) export class PerunDialogsModule {} diff --git a/libs/perun/login/src/lib/login-screen-service-access/login-screen-service-access.component.html b/libs/perun/login/src/lib/login-screen-service-access/login-screen-service-access.component.html index 6f8dc74a0..6f9b3f54d 100644 --- a/libs/perun/login/src/lib/login-screen-service-access/login-screen-service-access.component.html +++ b/libs/perun/login/src/lib/login-screen-service-access/login-screen-service-access.component.html @@ -5,7 +5,10 @@

{{'SHARED_LIB.PERUN.LOGIN_SERVICE_ACCESS.LABEL' | translate}}

{{'SHARED_LIB.PERUN.LOGIN_SERVICE_ACCESS.WRONG_LOGIN_OR_PASSWORD' | translate}} - + {{'SHARED_LIB.PERUN.LOGIN.LOGOUT_INFO' | translate}} diff --git a/libs/perun/services/src/lib/auth.service.ts b/libs/perun/services/src/lib/auth.service.ts index 5f69cd29a..1a08af3d2 100644 --- a/libs/perun/services/src/lib/auth.service.ts +++ b/libs/perun/services/src/lib/auth.service.ts @@ -49,13 +49,14 @@ export class AuthService { void this.router.navigate(['/service-access'], { queryParamsHandling: 'preserve' }); } else { this.logoutProcess = true; - this.oauthService.logOut(); + const proxyLogout = this.store.getProperty('proxy_logout'); + this.oauthService.logOut(!proxyLogout); const postLogoutUrl = this.store.getProperty('oidc_client').oauth_post_logout_redirect_uri; if (!postLogoutUrl) { // redirect to the login page if there is no postLogoutUrl void this.router.navigate(['/login'], { queryParamsHandling: 'preserve' }); - } else if (this.store.getProperty('proxy_logout')) { + } else if (proxyLogout) { // redirect to the logout loading page if postLogoutUrl exist and logout should be handled by proxy void this.router.navigate(['/logout'], { queryParamsHandling: 'preserve' }); } else { diff --git a/libs/perun/services/src/lib/init-auth.service.ts b/libs/perun/services/src/lib/init-auth.service.ts index aca838ddd..4a3dad150 100644 --- a/libs/perun/services/src/lib/init-auth.service.ts +++ b/libs/perun/services/src/lib/init-auth.service.ts @@ -12,6 +12,7 @@ import { OAuthInfoEvent, OAuthService } from 'angular-oauth2-oidc'; import { filter } from 'rxjs/operators'; import { firstValueFrom, timer } from 'rxjs'; import { MfaHandlerService } from './mfa-handler.service'; +import { UserNotAllowedAccessComponent } from '@perun-web-apps/general'; @Injectable({ providedIn: 'root', @@ -109,6 +110,14 @@ export class InitAuthService { if (perunPrincipal.user === null) { const config = getDefaultDialogConfig(); this.dialog.open(UserDontExistDialogComponent, config); + } else if (perunPrincipal.user.serviceUser) { + const config = getDefaultDialogConfig(); + this.dialog + .open(UserNotAllowedAccessComponent, config) + .afterClosed() + .subscribe(() => { + this.authService.logout(); + }); } else { this.storeService.setPerunPrincipal(perunPrincipal); this.authResolver.init(perunPrincipal); diff --git a/libs/perun/utils/src/index.ts b/libs/perun/utils/src/index.ts index d3dba9427..dd1667590 100644 --- a/libs/perun/utils/src/index.ts +++ b/libs/perun/utils/src/index.ts @@ -2,3 +2,4 @@ export * from './lib/perun-utils.module'; export * from './lib/perun-utils'; export * from './lib/table-wrapper/table-wrapper.component'; export * from './lib/custom-validators'; +export * from './lib/export-table-dialog/export-table-dialog.component'; diff --git a/libs/perun/utils/src/lib/export-table-dialog/export-table-dialog.component.html b/libs/perun/utils/src/lib/export-table-dialog/export-table-dialog.component.html new file mode 100644 index 000000000..9a9d63acc --- /dev/null +++ b/libs/perun/utils/src/lib/export-table-dialog/export-table-dialog.component.html @@ -0,0 +1,45 @@ +

+ {{'SHARED_LIB.PERUN.COMPONENTS.TABLE_OPTIONS.EXPORT_TITLE'| translate}} +

+
+ + {{'SHARED_LIB.PERUN.COMPONENTS.TABLE_OPTIONS.SELECT_FORMAT'| translate}} + + + {{format.viewValue}} + + + + +

{{'SHARED_LIB.PERUN.COMPONENTS.TABLE_OPTIONS.EXPORT_OPTIONS'| translate}}

+ + + + {{'SHARED_LIB.PERUN.COMPONENTS.TABLE_OPTIONS.DISPLAYED_DATA'| translate}} + + + {{'SHARED_LIB.PERUN.COMPONENTS.TABLE_OPTIONS.ALL_DATA'| translate}} + + +
+
+ +
+
+ +
+
+
diff --git a/libs/perun/utils/src/lib/export-table-dialog/export-table-dialog.component.scss b/libs/perun/utils/src/lib/export-table-dialog/export-table-dialog.component.scss new file mode 100644 index 000000000..e69de29bb diff --git a/libs/perun/utils/src/lib/export-table-dialog/export-table-dialog.component.ts b/libs/perun/utils/src/lib/export-table-dialog/export-table-dialog.component.ts new file mode 100644 index 000000000..8f82ee624 --- /dev/null +++ b/libs/perun/utils/src/lib/export-table-dialog/export-table-dialog.component.ts @@ -0,0 +1,34 @@ +import { Component, Inject } from '@angular/core'; +import { FormControl, Validators } from '@angular/forms'; +import { MAT_DIALOG_DATA } from '@angular/material/dialog'; + +interface Format { + value: string; + viewValue: string; +} + +interface InputData { + allowExportAll: boolean; +} + +@Component({ + selector: 'perun-web-apps-export-table-dialog', + templateUrl: './export-table-dialog.component.html', + styleUrls: ['./export-table-dialog.component.scss'], +}) +export class ExportTableDialogComponent { + formats: Format[] = [{ value: 'csv', viewValue: 'CSV' }]; + selectedFormat = new FormControl('csv', Validators.required); + selectedExportType = new FormControl('current', Validators.required); + constructor(@Inject(MAT_DIALOG_DATA) public inputData: InputData) {} + isValidSelection(): boolean { + return this.selectedFormat.value !== null && this.selectedExportType.value !== null; + } + + export(): { exportType: string; format: string } { + return { + exportType: this.selectedExportType.value, + format: this.selectedFormat.value, + }; + } +} diff --git a/libs/perun/utils/src/lib/perun-utils.module.ts b/libs/perun/utils/src/lib/perun-utils.module.ts index 3ffc03042..32899b2aa 100644 --- a/libs/perun/utils/src/lib/perun-utils.module.ts +++ b/libs/perun/utils/src/lib/perun-utils.module.ts @@ -1,26 +1,17 @@ import { NgModule } from '@angular/core'; import { TableWrapperComponent } from './table-wrapper/table-wrapper.component'; -import { MatPaginatorModule } from '@angular/material/paginator'; import { TableOptionsComponent } from './table-options/table-options.component'; -import { MatMenuModule } from '@angular/material/menu'; -import { MatIconModule } from '@angular/material/icon'; -import { MatButtonModule } from '@angular/material/button'; import { TranslateModule } from '@ngx-translate/core'; import { CommonModule } from '@angular/common'; -import { MatTooltipModule } from '@angular/material/tooltip'; +import { ExportTableDialogComponent } from './export-table-dialog/export-table-dialog.component'; +import { UiMaterialModule } from '@perun-web-apps/ui/material'; +import { MatRadioModule } from '@angular/material/radio'; +import { ReactiveFormsModule } from '@angular/forms'; @NgModule({ - imports: [ - MatPaginatorModule, - MatMenuModule, - MatIconModule, - MatButtonModule, - TranslateModule, - CommonModule, - MatTooltipModule, - ], - declarations: [TableWrapperComponent, TableOptionsComponent], - exports: [TableWrapperComponent, TableOptionsComponent], + imports: [TranslateModule, CommonModule, ReactiveFormsModule, UiMaterialModule, MatRadioModule], + declarations: [TableWrapperComponent, TableOptionsComponent, ExportTableDialogComponent], + exports: [TableWrapperComponent, TableOptionsComponent, ExportTableDialogComponent], providers: [], }) export class PerunUtilsModule {} diff --git a/libs/perun/utils/src/lib/table-options/table-options.component.html b/libs/perun/utils/src/lib/table-options/table-options.component.html index 39d7e01dd..d56a44cfb 100644 --- a/libs/perun/utils/src/lib/table-options/table-options.component.html +++ b/libs/perun/utils/src/lib/table-options/table-options.component.html @@ -7,21 +7,8 @@ - - - - - - - - - -
diff --git a/libs/perun/utils/src/lib/table-options/table-options.component.ts b/libs/perun/utils/src/lib/table-options/table-options.component.ts index 41aca1559..801ecd869 100644 --- a/libs/perun/utils/src/lib/table-options/table-options.component.ts +++ b/libs/perun/utils/src/lib/table-options/table-options.component.ts @@ -1,4 +1,12 @@ import { Component, EventEmitter, Input, Output } from '@angular/core'; +import { MatDialog } from '@angular/material/dialog'; +import { ExportTableDialogComponent } from '../export-table-dialog/export-table-dialog.component'; +import { getDefaultDialogConfig } from '../perun-utils'; + +interface DialogData { + exportType: string; + format: string; +} @Component({ selector: 'perun-web-apps-table-options', @@ -9,4 +17,25 @@ export class TableOptionsComponent { @Input() allowExportAll: boolean; @Output() exportDisplayedData = new EventEmitter(); @Output() exportAllData = new EventEmitter(); + + constructor(private dialog: MatDialog) {} + openDialog(): void { + const config = getDefaultDialogConfig(); + config.width = '500px'; + config.data = { + allowExportAll: this.allowExportAll, + }; + this.dialog + .open(ExportTableDialogComponent, config) + .afterClosed() + .subscribe((result: DialogData) => { + if (result) { + if (result.exportType === 'all') { + this.exportAllData.emit(result.format); + } else { + this.exportDisplayedData.emit(result.format); + } + } + }); + } }