From 78e5c26015fffef7158de881ca1612a78cf77872 Mon Sep 17 00:00:00 2001 From: Max Peintner Date: Tue, 27 Oct 2020 10:29:14 +0100 Subject: [PATCH] feat(console): authorizations from user context, add grants to auth user view, fix app saving (#895) * user grant on project * grant in auth user, enable creation, fix inv regex * use autocomplete solutions, section for usre ctx * user grant create for user context * fix edit from table * fix create context * fix authorization to write * grant overview component * fix user grants without context * lint * turn table highlighting off, rm logs * fix app name saving * fix table refresh for project grants * translate toast * i18n * lint * Update console/src/assets/i18n/de.json Co-authored-by: Florian Forster Co-authored-by: Florian Forster --- console/src/app/app-routing.module.ts | 26 ++++++ console/src/app/app.component.html | 15 ++++ console/src/app/app.component.scss | 2 +- .../idp-table/idp-table.component.html | 4 +- .../project-roles.component.html | 2 +- .../refresh-table/refresh-table.component.ts | 5 +- .../search-user-autocomplete.component.html | 33 ++++++-- .../search-user-autocomplete.component.scss | 40 ++++++--- .../search-user-autocomplete.component.ts | 34 ++++++-- .../search-user-autocomplete.module.ts | 2 + .../user-grants/user-grants-datasource.ts | 17 ++-- .../user-grants/user-grants.component.html | 81 ++++++++++--------- .../user-grants/user-grants.component.scss | 40 ++++++--- .../user-grants/user-grants.component.ts | 66 +++++---------- .../app/pages/grants/grants-routing.module.ts | 17 ++++ .../app/pages/grants/grants.component.html | 9 +++ .../app/pages/grants/grants.component.scss | 4 + .../app/pages/grants/grants.component.spec.ts | 25 ++++++ .../src/app/pages/grants/grants.component.ts | 8 ++ console/src/app/pages/grants/grants.module.ts | 24 ++++++ .../apps/app-detail/app-detail.component.html | 2 +- .../apps/app-detail/app-detail.component.ts | 16 ++++ .../granted-project-detail.component.html | 6 +- .../owned-project-detail.component.html | 9 ++- .../project-grants.component.html | 2 +- .../project-grants.component.ts | 3 +- .../user-grant-create.component.html | 30 +++---- .../user-grant-create.component.scss | 10 +++ .../user-grant-create.component.ts | 62 +++++++++++++- .../auth-user-detail.component.html | 9 +++ .../auth-user-detail.component.ts | 3 + .../contact/contact.component.html | 2 +- .../contact/contact.component.scss | 6 +- .../user-detail/user-detail.component.html | 6 +- .../user-detail/user-detail.component.ts | 2 + .../user-table/user-table.component.html | 2 +- .../user-table/user-table.component.ts | 2 +- console/src/app/services/toast.service.ts | 19 +++-- console/src/assets/i18n/de.json | 6 +- console/src/assets/i18n/en.json | 6 +- console/src/styles/table.scss | 4 +- 41 files changed, 483 insertions(+), 178 deletions(-) create mode 100644 console/src/app/pages/grants/grants-routing.module.ts create mode 100644 console/src/app/pages/grants/grants.component.html create mode 100644 console/src/app/pages/grants/grants.component.scss create mode 100644 console/src/app/pages/grants/grants.component.spec.ts create mode 100644 console/src/app/pages/grants/grants.component.ts create mode 100644 console/src/app/pages/grants/grants.module.ts diff --git a/console/src/app/app-routing.module.ts b/console/src/app/app-routing.module.ts index 57d980f1de..4c562966f2 100644 --- a/console/src/app/app-routing.module.ts +++ b/console/src/app/app-routing.module.ts @@ -65,6 +65,14 @@ const routes: Routes = [ roles: ['org.read'], }, }, + { + path: 'grants', + loadChildren: () => import('./pages/grants/grants.module').then(m => m.GrantsModule), + canActivate: [AuthGuard, RoleGuard], + data: { + roles: ['user.grant.read'], + }, + }, { path: 'grant-create', canActivate: [AuthGuard], @@ -87,6 +95,24 @@ const routes: Routes = [ roles: ['user.grant.write'], }, }, + { + path: 'user/:userid', + loadChildren: () => import('src/app/pages/user-grant-create/user-grant-create.module') + .then(m => m.UserGrantCreateModule), + canActivate: [RoleGuard], + data: { + roles: ['user.grant.write'], + }, + }, + { + path: '', + loadChildren: () => import('src/app/pages/user-grant-create/user-grant-create.module') + .then(m => m.UserGrantCreateModule), + canActivate: [RoleGuard], + data: { + roles: ['user.grant.write'], + }, + }, ], }, { diff --git a/console/src/app/app.component.html b/console/src/app/app.component.html index dfbb789d22..f189c3ffec 100644 --- a/console/src/app/app.component.html +++ b/console/src/app/app.component.html @@ -138,6 +138,21 @@ {{ 'MENU.MACHINEUSERS' | translate }} + + +
+
+ + {{ 'MENU.GRANTSECTION' | translate }} +
+
+ + + + {{ 'MENU.GRANTS' | translate }} + +
diff --git a/console/src/app/app.component.scss b/console/src/app/app.component.scss index 739ffcf6cb..b0ba7a6ab1 100644 --- a/console/src/app/app.component.scss +++ b/console/src/app/app.component.scss @@ -212,7 +212,7 @@ padding: 2px 1rem; border-radius: 50vw; color: var(--grey); - font-size: 12px; + font-size: 11px; } .line { diff --git a/console/src/app/modules/idp-table/idp-table.component.html b/console/src/app/modules/idp-table/idp-table.component.html index 74388dd9f7..510c8f4522 100644 --- a/console/src/app/modules/idp-table/idp-table.component.html +++ b/console/src/app/modules/idp-table/idp-table.component.html @@ -1,5 +1,5 @@ + [emitRefreshOnPreviousRoutes]="['/iam/idp/create']" [timestamp]="idpResult?.viewTimestamp" [selection]="selection"> -
+
{{'USER.SEARCH.FOUND' | translate}}:
+
+ + + +
+ +
+
+
+ {{user.preferredLoginName}} -
- -

{{(target == UserTarget.SELF ? 'USER.TARGET.SELF' : 'USER.TARGET.EXTERNAL') | translate}} - {{'USER.TARGET.CLICKHERE' | translate}} -

\ No newline at end of file diff --git a/console/src/app/modules/search-user-autocomplete/search-user-autocomplete.component.scss b/console/src/app/modules/search-user-autocomplete/search-user-autocomplete.component.scss index 45077bc907..753bfe830a 100644 --- a/console/src/app/modules/search-user-autocomplete/search-user-autocomplete.component.scss +++ b/console/src/app/modules/search-user-autocomplete/search-user-autocomplete.component.scss @@ -3,11 +3,6 @@ } .target-desc { - color: var(--grey); - font-size: .8rem; - margin: 0; - margin-bottom: 1rem; - a { color: #4072b4; @@ -37,12 +32,35 @@ font-size: .8rem; } -.found-user-row { - display: flex; - align-items: center; +.found { + margin: .5rem 0; + background: #4072b410; + border-radius: .5rem; + padding: .5rem; + + .found-user-row { + padding: .5rem 0; + display: flex; + align-items: center; + min-height: 56px; + + button { + visibility: hidden; + } + + &:hover { + button { + visibility: visible; + } + } + } + + .found-label { + font-size: .9rem; + color: var(--grey); + } } -.found-label { - font-size: .9rem; - color: var(--grey); +.circle { + margin-right: .5rem; } diff --git a/console/src/app/modules/search-user-autocomplete/search-user-autocomplete.component.ts b/console/src/app/modules/search-user-autocomplete/search-user-autocomplete.component.ts index d5509a997a..d4809db3e4 100644 --- a/console/src/app/modules/search-user-autocomplete/search-user-autocomplete.component.ts +++ b/console/src/app/modules/search-user-autocomplete/search-user-autocomplete.component.ts @@ -1,5 +1,15 @@ import { COMMA, ENTER } from '@angular/cdk/keycodes'; -import { Component, ElementRef, EventEmitter, Input, Output, ViewChild } from '@angular/core'; +import { + AfterContentChecked, + ChangeDetectorRef, + Component, + ElementRef, + EventEmitter, + Input, + OnInit, + Output, + ViewChild, +} from '@angular/core'; import { FormControl } from '@angular/forms'; import { MatAutocomplete, MatAutocompleteSelectedEvent } from '@angular/material/autocomplete'; import { MatChipInputEvent } from '@angular/material/chips'; @@ -19,7 +29,7 @@ export enum UserTarget { templateUrl: './search-user-autocomplete.component.html', styleUrls: ['./search-user-autocomplete.component.scss'], }) -export class SearchUserAutocompleteComponent { +export class SearchUserAutocompleteComponent implements OnInit, AfterContentChecked { public selectable: boolean = true; public removable: boolean = true; public addOnBlur: boolean = true; @@ -32,7 +42,7 @@ export class SearchUserAutocompleteComponent { @Input() public users: Array = []; public filteredUsers: Array = []; public isLoading: boolean = false; - public target: UserTarget = UserTarget.SELF; + @Input() public target: UserTarget = UserTarget.SELF; public hint: string = ''; public UserTarget: any = UserTarget; @ViewChild('usernameInput') public usernameInput!: ElementRef; @@ -41,8 +51,19 @@ export class SearchUserAutocompleteComponent { @Input() public singleOutput: boolean = false; private unsubscribed$: Subject = new Subject(); - constructor(private userService: ManagementService, private toast: ToastService) { - this.getFilteredResults(); + constructor(private userService: ManagementService, private toast: ToastService, private cdref: ChangeDetectorRef) { } + + public ngOnInit(): void { + if (this.target === UserTarget.EXTERNAL) { + this.filteredUsers = []; + this.unsubscribed$.next(); // clear old subscription + } else if (this.target === UserTarget.SELF) { + this.getFilteredResults(); // new subscription + } + } + + public ngAfterContentChecked(): void { + this.cdref.detectChanges(); } private getFilteredResults(): void { @@ -143,10 +164,11 @@ export class SearchUserAutocompleteComponent { public getGlobalUser(): void { this.userService.GetUserByLoginNameGlobal(this.globalLoginNameControl.value).then(user => { - this.users = [user.toObject()]; if (this.singleOutput) { + this.users = [user.toObject()]; this.selectionChanged.emit(this.users[0]); } else { + this.users.push(user.toObject()); this.selectionChanged.emit(this.users); } }).catch(error => { diff --git a/console/src/app/modules/search-user-autocomplete/search-user-autocomplete.module.ts b/console/src/app/modules/search-user-autocomplete/search-user-autocomplete.module.ts index 4933dcee87..cfe7443f73 100644 --- a/console/src/app/modules/search-user-autocomplete/search-user-autocomplete.module.ts +++ b/console/src/app/modules/search-user-autocomplete/search-user-autocomplete.module.ts @@ -11,6 +11,7 @@ import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; import { MatSelectModule } from '@angular/material/select'; import { TranslateModule } from '@ngx-translate/core'; +import { AvatarModule } from '../avatar/avatar.module'; import { SearchUserAutocompleteComponent } from './search-user-autocomplete.component'; @@ -29,6 +30,7 @@ import { SearchUserAutocompleteComponent } from './search-user-autocomplete.comp FormsModule, TranslateModule, MatSelectModule, + AvatarModule, ], exports: [SearchUserAutocompleteComponent], }) diff --git a/console/src/app/modules/user-grants/user-grants-datasource.ts b/console/src/app/modules/user-grants/user-grants-datasource.ts index 002568f572..787c51e258 100644 --- a/console/src/app/modules/user-grants/user-grants-datasource.ts +++ b/console/src/app/modules/user-grants/user-grants-datasource.ts @@ -13,7 +13,7 @@ import { import { ManagementService } from 'src/app/services/mgmt.service'; export enum UserGrantContext { - // AUTHUSER = 'authuser', + NONE = 'none', USER = 'user', OWNED_PROJECT = 'owned', GRANTED_PROJECT = 'granted', @@ -42,14 +42,13 @@ export class UserGrantsDataSource extends DataSource { }, queries?: UserGrantSearchQuery[], ): void { - const offset = pageIndex * pageSize; - switch (context) { case UserGrantContext.USER: if (data && data.userId) { this.loadingSubject.next(true); const userfilter = new UserGrantSearchQuery(); userfilter.setKey(UserGrantSearchKey.USERGRANTSEARCHKEY_USER_ID); + userfilter.setMethod(SearchMethod.SEARCHMETHOD_EQUALS); userfilter.setValue(data.userId); if (queries) { queries.push(userfilter); @@ -57,7 +56,7 @@ export class UserGrantsDataSource extends DataSource { queries = [userfilter]; } - const promise = this.userService.SearchUserGrants(10, 0, queries); + const promise = this.userService.SearchUserGrants(pageSize, pageSize * pageIndex, queries); this.loadResponse(promise); } break; @@ -66,6 +65,7 @@ export class UserGrantsDataSource extends DataSource { this.loadingSubject.next(true); const projectfilter = new UserGrantSearchQuery(); projectfilter.setKey(UserGrantSearchKey.USERGRANTSEARCHKEY_PROJECT_ID); + projectfilter.setMethod(SearchMethod.SEARCHMETHOD_EQUALS); projectfilter.setValue(data.projectId); if (queries) { queries.push(projectfilter); @@ -73,7 +73,7 @@ export class UserGrantsDataSource extends DataSource { queries = [projectfilter]; } - const promise1 = this.userService.SearchUserGrants(10, 0, queries); + const promise1 = this.userService.SearchUserGrants(pageSize, pageSize * pageIndex, queries); this.loadResponse(promise1); } break; @@ -97,10 +97,15 @@ export class UserGrantsDataSource extends DataSource { queries = [projectfilter, grantquery]; } - const promise2 = this.userService.SearchUserGrants(10, 0, queries); + const promise2 = this.userService.SearchUserGrants(pageSize, pageSize * pageIndex, queries); this.loadResponse(promise2); } break; + default: + this.loadingSubject.next(true); + const promise3 = this.userService.SearchUserGrants(pageSize, pageSize * pageIndex, []); + this.loadResponse(promise3); + break; } } diff --git a/console/src/app/modules/user-grants/user-grants.component.html b/console/src/app/modules/user-grants/user-grants.component.html index be733be1f5..c8a0721d92 100644 --- a/console/src/app/modules/user-grants/user-grants.component.html +++ b/console/src/app/modules/user-grants/user-grants.component.html @@ -1,12 +1,12 @@ - + add{{ 'GRANTS.ADD_BTN' | translate }} @@ -14,75 +14,81 @@ - - - - - - - +
- - - + {{ 'PROJECT.GRANT.USER' | translate }} + {{grant?.displayName}} {{ 'PROJECT.GRANT.GRANTEDORGDOMAIN' | translate }} + {{grant.orgName}} {{ 'PROJECT.GRANT.PROJECTNAME' | translate }} + {{grant.projectName}} {{ 'PROJECT.GRANT.CREATIONDATE' | translate }} + {{grant.creationDate | timestampToDate | localizedDate: 'dd. MMM, HH:mm' }} {{ 'PROJECT.GRANT.CHANGEDATE' | translate }} + {{grant.changeDate | timestampToDate | localizedDate: 'dd. MMM, HH:mm' }} {{ 'PROJECT.GRANT.ROLENAMESLIST' | translate }} - - {{'PROJECT.GRANT.NOROLES' | translate}} - {{ (role.length>8)? (role | slice:0:8)+'..':(role) }} + + +
+ {{ role }} + +
+
- - - {{ (role.length>6)? (role | slice:0:6)+'..':(role) }} - - + + *ngIf="loadedProjectId && loadedProjectId === grant.projectId"> {{ 'PROJECT.GRANT.ROLENAMESLIST' | translate }} - {{role.key}} @@ -91,10 +97,13 @@ - - + + {{ 'PROJECT.GRANT.ROLENAMESLIST' | translate }} - {{role}} @@ -106,11 +115,11 @@
-
diff --git a/console/src/app/modules/user-grants/user-grants.component.scss b/console/src/app/modules/user-grants/user-grants.component.scss index e248c76d30..6587614d4b 100644 --- a/console/src/app/modules/user-grants/user-grants.component.scss +++ b/console/src/app/modules/user-grants/user-grants.component.scss @@ -25,20 +25,38 @@ width: 50px; max-width: 50px; } + } +} - .role { - display: inline-block; - margin: .25rem; +.no-roles { + font-size: 13px; + color: var(--grey); +} + +.flex-row { + display: flex; + flex-direction: column; + max-width: 400px; + + .role { + display: block; + margin: .25rem; + font-size: 14px; + text-overflow: ellipsis; + overflow: hidden; + } + + button { + margin: .5rem 0; + max-width: 120px; + + i { + font-size: 1.2rem; + margin-bottom: 5px; } } } -.pointer { - outline: none; - cursor: pointer; -} - -.no-roles { - font-size: 14px; - color: var(--grey); +.fill-space { + flex: 1; } diff --git a/console/src/app/modules/user-grants/user-grants.component.ts b/console/src/app/modules/user-grants/user-grants.component.ts index cfa66602d5..a2b67d2be1 100644 --- a/console/src/app/modules/user-grants/user-grants.component.ts +++ b/console/src/app/modules/user-grants/user-grants.component.ts @@ -4,14 +4,7 @@ import { MatPaginator, PageEvent } from '@angular/material/paginator'; import { MatSelectChange } from '@angular/material/select'; import { MatTable } from '@angular/material/table'; import { tap } from 'rxjs/operators'; -import { - ProjectRoleView, - SearchMethod, - UserGrant, - UserGrantSearchKey, - UserGrantSearchQuery, - UserGrantView, -} from 'src/app/proto/generated/management_pb'; +import { ProjectRoleView, UserGrant, UserGrantView } from 'src/app/proto/generated/management_pb'; import { ManagementService } from 'src/app/services/mgmt.service'; import { ToastService } from 'src/app/services/toast.service'; @@ -23,8 +16,9 @@ import { UserGrantContext, UserGrantsDataSource } from './user-grants-datasource styleUrls: ['./user-grants.component.scss'], }) export class UserGrantsComponent implements OnInit, AfterViewInit { - @Input() context: UserGrantContext = UserGrantContext.USER; - @Input() refreshOnPreviousRoute: string = ''; + public INITIAL_PAGE_SIZE: number = 50; + @Input() context: UserGrantContext = UserGrantContext.NONE; + @Input() refreshOnPreviousRoutes: string[] = []; public grants: UserGrantView.AsObject[] = []; public dataSource!: UserGrantsDataSource; @@ -32,8 +26,8 @@ export class UserGrantsComponent implements OnInit, AfterViewInit { @ViewChild(MatPaginator) public paginator!: MatPaginator; @ViewChild(MatTable) public table!: MatTable; - @Input() allowWrite: boolean = false; - @Input() allowDelete: boolean = false; + @Input() disableWrite: boolean = false; + @Input() disableDelete: boolean = false; @Input() userId: string = ''; @Input() projectId: string = ''; @@ -80,15 +74,11 @@ export class UserGrantsComponent implements OnInit, AfterViewInit { this.routerLink = ['/grant-create', 'user', this.userId]; } break; - default: + case UserGrantContext.NONE: this.routerLink = ['/grant-create']; } - this.dataSource.loadGrants(this.context, 0, 25, { - projectId: this.projectId, - grantId: this.grantId, - userId: this.userId, - }); + this.loadGrantsPage(); } public ngAfterViewInit(): void { @@ -102,11 +92,12 @@ export class UserGrantsComponent implements OnInit, AfterViewInit { private loadGrantsPage(): void { this.dataSource.loadGrants( this.context, - this.paginator.pageIndex, - this.paginator.pageSize, + this.paginator?.pageIndex ?? 0, + this.paginator?.pageSize ?? this.INITIAL_PAGE_SIZE, { projectId: this.projectId, grantId: this.grantId, + userId: this.userId, }, ); } @@ -125,7 +116,7 @@ export class UserGrantsComponent implements OnInit, AfterViewInit { public getGrantRoleOptions(grantId: string, projectId: string): void { this.mgmtService.GetGrantedProjectByID(projectId, grantId).then(resp => { - this.loadedGrantId = projectId; + this.loadedGrantId = grantId; this.grantRoleOptions = resp.toObject().roleKeysList; }).catch(error => { this.toast.showError(error); @@ -140,33 +131,12 @@ export class UserGrantsComponent implements OnInit, AfterViewInit { } updateRoles(grant: UserGrant.AsObject, selectionChange: MatSelectChange): void { - switch (this.context) { - case UserGrantContext.OWNED_PROJECT: - if (grant.id && grant.projectId) { - this.userService.UpdateUserGrant(grant.id, grant.userId, selectionChange.value) - .then(() => { - this.toast.showInfo('GRANTS.TOAST.UPDATED', true); - }).catch(error => { - this.toast.showError(error); - }); - } - break; - case UserGrantContext.GRANTED_PROJECT: - if (this.grantId && this.projectId) { - const projectQuery: UserGrantSearchQuery = new UserGrantSearchQuery(); - projectQuery.setKey(UserGrantSearchKey.USERGRANTSEARCHKEY_PROJECT_ID); - projectQuery.setMethod(SearchMethod.SEARCHMETHOD_EQUALS); - projectQuery.setValue(this.projectId); - this.userService.UpdateUserGrant( - grant.id, grant.userId, selectionChange.value) - .then(() => { - this.toast.showInfo('GRANTS.TOAST.UPDATED', true); - }).catch(error => { - this.toast.showError(error); - }); - } - break; - } + this.userService.UpdateUserGrant(grant.id, grant.userId, selectionChange.value) + .then(() => { + this.toast.showInfo('GRANTS.TOAST.UPDATED', true); + }).catch(error => { + this.toast.showError(error); + }); } deleteGrantSelection(): void { diff --git a/console/src/app/pages/grants/grants-routing.module.ts b/console/src/app/pages/grants/grants-routing.module.ts new file mode 100644 index 0000000000..0a53cee5f0 --- /dev/null +++ b/console/src/app/pages/grants/grants-routing.module.ts @@ -0,0 +1,17 @@ +import { NgModule } from '@angular/core'; +import { RouterModule, Routes } from '@angular/router'; + +import { GrantsComponent } from './grants.component'; + +const routes: Routes = [ + { + path: '', + component: GrantsComponent, + }, +]; + +@NgModule({ + imports: [RouterModule.forChild(routes)], + exports: [RouterModule], +}) +export class GrantsRoutingModule { } diff --git a/console/src/app/pages/grants/grants.component.html b/console/src/app/pages/grants/grants.component.html new file mode 100644 index 0000000000..91267c1377 --- /dev/null +++ b/console/src/app/pages/grants/grants.component.html @@ -0,0 +1,9 @@ +
+

{{ 'GRANTS.TITLE' | translate }}

+

{{'GRANTS.DESC' | translate }}

+ + +
\ No newline at end of file diff --git a/console/src/app/pages/grants/grants.component.scss b/console/src/app/pages/grants/grants.component.scss new file mode 100644 index 0000000000..204270a91a --- /dev/null +++ b/console/src/app/pages/grants/grants.component.scss @@ -0,0 +1,4 @@ +.desc { + color: var(--grey); + margin-bottom: 2rem; +} diff --git a/console/src/app/pages/grants/grants.component.spec.ts b/console/src/app/pages/grants/grants.component.spec.ts new file mode 100644 index 0000000000..482b8bcb5c --- /dev/null +++ b/console/src/app/pages/grants/grants.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { GrantsComponent } from './grants.component'; + +describe('GrantsComponent', () => { + let component: GrantsComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [GrantsComponent], + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(GrantsComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/console/src/app/pages/grants/grants.component.ts b/console/src/app/pages/grants/grants.component.ts new file mode 100644 index 0000000000..ff622bdc30 --- /dev/null +++ b/console/src/app/pages/grants/grants.component.ts @@ -0,0 +1,8 @@ +import { Component } from '@angular/core'; + +@Component({ + selector: 'app-grants', + templateUrl: './grants.component.html', + styleUrls: ['./grants.component.scss'], +}) +export class GrantsComponent { } diff --git a/console/src/app/pages/grants/grants.module.ts b/console/src/app/pages/grants/grants.module.ts new file mode 100644 index 0000000000..a55b5ddce0 --- /dev/null +++ b/console/src/app/pages/grants/grants.module.ts @@ -0,0 +1,24 @@ +import { CommonModule } from '@angular/common'; +import { NgModule } from '@angular/core'; +import { TranslateModule } from '@ngx-translate/core'; +import { HasRoleModule } from 'src/app/directives/has-role/has-role.module'; +import { UserGrantsModule } from 'src/app/modules/user-grants/user-grants.module'; +import { HasRolePipeModule } from 'src/app/pipes/has-role-pipe/has-role-pipe.module'; + +import { GrantsRoutingModule } from './grants-routing.module'; +import { GrantsComponent } from './grants.component'; + +@NgModule({ + declarations: [ + GrantsComponent, + ], + imports: [ + CommonModule, + GrantsRoutingModule, + UserGrantsModule, + TranslateModule, + HasRoleModule, + HasRolePipeModule, + ], +}) +export class GrantsModule { } diff --git a/console/src/app/pages/projects/apps/app-detail/app-detail.component.html b/console/src/app/pages/projects/apps/app-detail/app-detail.component.html index ead4807057..5570add6e3 100644 --- a/console/src/app/pages/projects/apps/app-detail/app-detail.component.html +++ b/console/src/app/pages/projects/apps/app-detail/app-detail.component.html @@ -12,7 +12,7 @@ {{errorMessage}} -
+
{ + this.toast.showInfo('APP.TOAST.OIDCUPDATED', true); + }) + .catch(error => { + this.toast.showError(error); + }); + } + } + + public saveOIDCApp(): void { if (this.appNameForm.valid) { this.app.name = this.name?.value; diff --git a/console/src/app/pages/projects/granted-projects/granted-project-detail/granted-project-detail.component.html b/console/src/app/pages/projects/granted-projects/granted-project-detail/granted-project-detail.component.html index f35cf23bbb..58afb475fa 100644 --- a/console/src/app/pages/projects/granted-projects/granted-project-detail/granted-project-detail.component.html +++ b/console/src/app/pages/projects/granted-projects/granted-project-detail/granted-project-detail.component.html @@ -18,9 +18,9 @@ + [disableWrite]="(['user.grant.write$','user.grant.write:'+grantId] | hasRole | async) == false" + [disableDelete]="(['user.grant.delete$','user.grant.delete:'+grantId] | hasRole | async) == false" + refreshOnPreviousRoutes="['/grant-create/project/{{projectId}}/grant/{{grantId}}']"> diff --git a/console/src/app/pages/projects/owned-projects/owned-project-detail/owned-project-detail.component.html b/console/src/app/pages/projects/owned-projects/owned-project-detail/owned-project-detail.component.html index 0ac7ec87ec..3215be062a 100644 --- a/console/src/app/pages/projects/owned-projects/owned-project-detail/owned-project-detail.component.html +++ b/console/src/app/pages/projects/owned-projects/owned-project-detail/owned-project-detail.component.html @@ -68,7 +68,8 @@ [appHasRole]="['project.grant.read:' + project.projectId, 'project.grant.read']"> - @@ -97,9 +98,9 @@ + [refreshOnPreviousRoutes]="['/grant-create/project/'+projectId]" + [disableWrite]="((['user.grant.write$', 'user.grant.write:'+projectId] | hasRole) | async) == false" + [disableDelete]="((['user.grant.delete$','user.grant.delete:'+projectId] | hasRole) | async) == false"> diff --git a/console/src/app/pages/projects/owned-projects/owned-project-detail/project-grants/project-grants.component.html b/console/src/app/pages/projects/owned-projects/owned-project-detail/project-grants/project-grants.component.html index 1c111641aa..4e29faeb68 100644 --- a/console/src/app/pages/projects/owned-projects/owned-project-detail/project-grants/project-grants.component.html +++ b/console/src/app/pages/projects/owned-projects/owned-project-detail/project-grants/project-grants.component.html @@ -1,6 +1,6 @@ + [emitRefreshOnPreviousRoutes]="refreshOnPreviousRoutes" (refreshed)="getRoleOptions(projectId)"> diff --git a/console/src/app/pages/user-grant-create/user-grant-create.component.scss b/console/src/app/pages/user-grant-create/user-grant-create.component.scss index 41e55414c9..0de500de0e 100644 --- a/console/src/app/pages/user-grant-create/user-grant-create.component.scss +++ b/console/src/app/pages/user-grant-create/user-grant-create.component.scss @@ -64,3 +64,13 @@ padding: .5rem 4rem; } } + +.sa-icon { + display: block; + width: 32px; + margin: 0 .5rem; + + i { + margin: auto; + } +} diff --git a/console/src/app/pages/user-grant-create/user-grant-create.component.ts b/console/src/app/pages/user-grant-create/user-grant-create.component.ts index ab69bf9900..4bc8b2a03a 100644 --- a/console/src/app/pages/user-grant-create/user-grant-create.component.ts +++ b/console/src/app/pages/user-grant-create/user-grant-create.component.ts @@ -2,6 +2,7 @@ import { Location } from '@angular/common'; import { Component, OnDestroy } from '@angular/core'; import { ActivatedRoute, Params } from '@angular/router'; import { Subscription } from 'rxjs'; +import { UserTarget } from 'src/app/modules/search-user-autocomplete/search-user-autocomplete.component'; import { UserGrantContext } from 'src/app/modules/user-grants/user-grants-datasource'; import { Org } from 'src/app/proto/generated/auth_pb'; import { ProjectGrantView, ProjectRole, ProjectView, UserGrant, UserView } from 'src/app/proto/generated/management_pb'; @@ -19,7 +20,10 @@ export class UserGrantCreateComponent implements OnDestroy { public org!: Org.AsObject; public userId: string = ''; + public projectId: string = ''; + public project!: ProjectGrantView.AsObject | ProjectView.AsObject; + public grantId: string = ''; public rolesList: string[] = []; @@ -33,6 +37,12 @@ export class UserGrantCreateComponent implements OnDestroy { public UserGrantContext: any = UserGrantContext; public grantRolesKeyList: string[] = []; + + public user!: UserView.AsObject; + public UserTarget: any = UserTarget; + + public ProjectGrantView: any = ProjectGrantView; + public ProjectView: any = ProjectView; constructor( private userService: ManagementService, private toast: ToastService, @@ -42,8 +52,8 @@ export class UserGrantCreateComponent implements OnDestroy { private mgmtService: ManagementService, ) { this.subscription = this.route.params.subscribe((params: Params) => { - const { context, projectid, grantid, userid } = params; - this.context = context; + const { projectid, grantid, userid } = params; + this.context = UserGrantContext.NONE; this.projectId = projectid; this.grantId = grantid; @@ -58,6 +68,14 @@ export class UserGrantCreateComponent implements OnDestroy { }).catch((error: any) => { this.toast.showError(error); }); + } else if (this.userId) { + this.context = UserGrantContext.USER; + this.mgmtService.GetUserByID(this.userId).then(resp => { + this.user = resp.toObject(); + console.log(this.user); + }).catch((error: any) => { + this.toast.showError(error); + }); } }); @@ -97,12 +115,52 @@ export class UserGrantCreateComponent implements OnDestroy { this.toast.showError(error); }); break; + case UserGrantContext.USER: + let grantId; + + if ((this.project as ProjectGrantView.AsObject)?.id) { + grantId = (this.project as ProjectGrantView.AsObject).id; + } + + this.userService.CreateUserGrant( + this.userId, + this.rolesList, + this.project.projectId, + grantId, + ).then((data: UserGrant) => { + this.toast.showInfo('PROJECT.GRANT.TOAST.PROJECTGRANTUSERGRANTADDED', true); + this.close(); + }).catch((error: any) => { + this.toast.showError(error); + }); + break; + case UserGrantContext.NONE: + let tempGrantId; + + if ((this.project as ProjectGrantView.AsObject)?.id) { + tempGrantId = (this.project as ProjectGrantView.AsObject).id; + } + + this.userService.CreateUserGrant( + this.userId, + this.rolesList, + this.project.projectId, + tempGrantId, + ).then((data: UserGrant) => { + this.toast.showInfo('PROJECT.GRANT.TOAST.PROJECTGRANTUSERGRANTADDED', true); + this.close(); + }).catch((error: any) => { + this.toast.showError(error); + }); + break; } } public selectProject(project: ProjectView.AsObject | ProjectGrantView.AsObject | any): void { + this.project = project; this.projectId = project.projectId; + this.grantRolesKeyList = project.roleKeysList ?? []; } public selectUser(user: UserView.AsObject): void { diff --git a/console/src/app/pages/users/user-detail/auth-user-detail/auth-user-detail.component.html b/console/src/app/pages/users/user-detail/auth-user-detail/auth-user-detail.component.html index 2a38dd59cb..ad79e7d455 100644 --- a/console/src/app/pages/users/user-detail/auth-user-detail/auth-user-detail.component.html +++ b/console/src/app/pages/users/user-detail/auth-user-detail/auth-user-detail.component.html @@ -47,6 +47,15 @@ + + + + +
diff --git a/console/src/app/pages/users/user-detail/auth-user-detail/auth-user-detail.component.ts b/console/src/app/pages/users/user-detail/auth-user-detail/auth-user-detail.component.ts index 56c9dac0cf..3523f17476 100644 --- a/console/src/app/pages/users/user-detail/auth-user-detail/auth-user-detail.component.ts +++ b/console/src/app/pages/users/user-detail/auth-user-detail/auth-user-detail.component.ts @@ -2,6 +2,7 @@ import { Component, OnDestroy } from '@angular/core'; import { TranslateService } from '@ngx-translate/core'; import { Subscription } from 'rxjs'; import { ChangeType } from 'src/app/modules/changes/changes.component'; +import { UserGrantContext } from 'src/app/modules/user-grants/user-grants-datasource'; import { Gender, UserAddress, UserEmail, UserPhone, UserProfile, UserView } from 'src/app/proto/generated/auth_pb'; import { GrpcAuthService } from 'src/app/services/grpc-auth.service'; import { ToastService } from 'src/app/services/toast.service'; @@ -26,6 +27,8 @@ export class AuthUserDetailComponent implements OnDestroy { public ChangeType: any = ChangeType; public userLoginMustBeDomain: boolean = false; + public USERGRANTCONTEXT: UserGrantContext = UserGrantContext.USER; + constructor( public translate: TranslateService, private toast: ToastService, diff --git a/console/src/app/pages/users/user-detail/contact/contact.component.html b/console/src/app/pages/users/user-detail/contact/contact.component.html index 1178b442ed..c4f1be7f08 100644 --- a/console/src/app/pages/users/user-detail/contact/contact.component.html +++ b/console/src/app/pages/users/user-detail/contact/contact.component.html @@ -3,7 +3,7 @@ {{ 'USER.PROFILE.PASSWORD' | translate }} ********* -
+ diff --git a/console/src/app/pages/users/user-detail/user-detail/user-detail.component.ts b/console/src/app/pages/users/user-detail/user-detail/user-detail.component.ts index 8c12f3b27d..70fdb0fe5a 100644 --- a/console/src/app/pages/users/user-detail/user-detail/user-detail.component.ts +++ b/console/src/app/pages/users/user-detail/user-detail/user-detail.component.ts @@ -5,6 +5,7 @@ import { ActivatedRoute } from '@angular/router'; import { TranslateService } from '@ngx-translate/core'; import { Subscription } from 'rxjs'; import { ChangeType } from 'src/app/modules/changes/changes.component'; +import { UserGrantContext } from 'src/app/modules/user-grants/user-grants-datasource'; import { WarnDialogComponent } from 'src/app/modules/warn-dialog/warn-dialog.component'; import { Gender, @@ -37,6 +38,7 @@ export class UserDetailComponent implements OnInit, OnDestroy { public UserState: any = UserState; public copied: string = ''; + public USERGRANTCONTEXT: UserGrantContext = UserGrantContext.USER; constructor( public translate: TranslateService, diff --git a/console/src/app/pages/users/user-list/user-table/user-table.component.html b/console/src/app/pages/users/user-list/user-table/user-table.component.html index 4a0572e96f..dfcead088c 100644 --- a/console/src/app/pages/users/user-list/user-table/user-table.component.html +++ b/console/src/app/pages/users/user-list/user-table/user-table.component.html @@ -1,6 +1,6 @@ + [emitRefreshOnPreviousRoutes]="refreshOnPreviousRoutes"> {{'USER.PAGES.FILTER' | translate}} { - this.showMessage(data, 'close'); + this.translate.get('ACTIONS.CLOSE').pipe(take(1)).subscribe(value => { + this.showMessage(data, value); + }); }); } else { - this.showMessage(message, 'close'); + this.translate.get('ACTIONS.CLOSE').pipe(take(1)).subscribe(value => { + this.showMessage(message, value); + }); } } public showError(grpcError: any): void { const { message, code, metadata } = grpcError; if (code !== 16) { - this.showMessage(decodeURI(message), 'close'); + this.translate.get('ACTIONS.CLOSE').pipe(take(1)).subscribe(value => { + this.showMessage(decodeURI(message), value); + }); } } diff --git a/console/src/assets/i18n/de.json b/console/src/assets/i18n/de.json index be864ad882..30d64bf21a 100644 --- a/console/src/assets/i18n/de.json +++ b/console/src/assets/i18n/de.json @@ -35,7 +35,9 @@ "LOGOUT": "Alle Benutzer abmelden", "NEWORG":"Neue Organisation", "IAMADMIN":"Du bist ein IAM-Administrator. Beachte, dass Du erhöhte Rechte besitzt.", - "SHOWORGS":"Alle Organisationen anzeigen" + "SHOWORGS":"Alle Organisationen anzeigen", + "GRANTSECTION":"Berechtigungssektion", + "GRANTS":"Berechtigungen" }, "ACTIONS": { "SAVE": "Speichern", @@ -870,6 +872,8 @@ }, "ROLESLABEL":"Rollen", "GRANTS": { + "TITLE":"Berechtigungen", + "DESC":"Hier kannst Du die Berechtigungen Deiner Organisation verwalten.", "DELETE":"Berechtigung löschen", "ADD":"Berechtigung erstellen", "ADD_BTN":"Neu", diff --git a/console/src/assets/i18n/en.json b/console/src/assets/i18n/en.json index faa9343ae9..75276cae37 100644 --- a/console/src/assets/i18n/en.json +++ b/console/src/assets/i18n/en.json @@ -35,7 +35,9 @@ "LOGOUT": "Logout All Users", "NEWORG":"New Organisation", "IAMADMIN":"You are an IAM Administrator. Note that you have extended permissions.", - "SHOWORGS":"Show All Organisations" + "SHOWORGS":"Show All Organisations", + "GRANTSECTION":"Authorization Section", + "GRANTS":"Authorizations" }, "ACTIONS": { "SAVE": "Save", @@ -870,6 +872,8 @@ }, "ROLESLABEL":"Roles", "GRANTS": { + "TITLE":"Authorisations", + "DESC":"Here you can manage authorizations of your organization users.", "DELETE":"Delete Authorisation", "ADD":"Create Authorisation", "ADD_BTN":"New", diff --git a/console/src/styles/table.scss b/console/src/styles/table.scss index 2c02a31c0f..3397770f57 100644 --- a/console/src/styles/table.scss +++ b/console/src/styles/table.scss @@ -31,9 +31,9 @@ } tr { - cursor: pointer; - &.highlight { + cursor: pointer; + &:hover { td { background-color: var(--table-row-back); // rgba($inv-color, .05);