Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Users Table: Restrict org/space roles and prefix space name with org... depending on depth #2402

Merged
merged 7 commits into from
Jun 20, 2018
Merged
2 changes: 1 addition & 1 deletion src/frontend/app/core/current-user-permissions.checker.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Store } from '@ngrx/store';
import { combineLatest, Observable, of as observableOf } from 'rxjs';
import { distinctUntilChanged, filter, map, switchMap, tap } from 'rxjs/operators';
import { distinctUntilChanged, filter, map, switchMap } from 'rxjs/operators';

import { CFFeatureFlagTypes } from '../shared/components/cf-auth/cf-auth.types';
import {
Expand Down
5 changes: 2 additions & 3 deletions src/frontend/app/core/current-user-permissions.service.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@

import { of as observableOf, Observable, combineLatest } from 'rxjs';
import { Injectable } from '@angular/core';
import { Store } from '@ngrx/store';
import { combineLatest, Observable, of as observableOf } from 'rxjs';
import { distinctUntilChanged, map, switchMap } from 'rxjs/operators';

import { AppState } from '../store/app-state';
import {
CHECKER_GROUPS,
Expand All @@ -19,7 +19,6 @@ import {
PermissionTypes,
} from './current-user-permissions.config';


interface ICheckCombiner {
checks: Observable<boolean>[];
combineType?: '&&';
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { DatePipe } from '@angular/common';
import { Component } from '@angular/core';
import { Store } from '@ngrx/store';
import { Observable , combineLatest , ReplaySubject } from 'rxjs';
import { combineLatest, Observable, ReplaySubject } from 'rxjs';
import { filter, first, map, pairwise, shareReplay, startWith, switchMap, tap } from 'rxjs/operators';

import { IServiceBinding } from '../../../core/cf-api-svc.types';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,6 @@ export class CloudFoundryOrganizationBaseComponent {
{
link: 'users',
label: 'Users',
// Hide the users tab unless we are in development
hidden: observableOf(environment.production)
}
];

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,6 @@ export class CloudFoundrySpaceBaseComponent implements OnDestroy {
{
link: 'users',
label: 'Users',
// Hide the users tab unless we are in development
hidden: observableOf(environment.production)
}
];

Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';

import { BaseTestModules } from '../../../../../../../test-framework/cloud-foundry-endpoint-service.helper';
import { CloudFoundrySpaceUsersComponent } from './cloud-foundry-space-users.component';
import { CloudFoundrySpaceService } from '../../../../../services/cloud-foundry-space.service';
import {
CloudFoundryOrganizationServiceMock,
} from '../../../../../../../test-framework/cloud-foundry-organization.service.mock';
import { CloudFoundrySpaceServiceMock } from '../../../../../../../test-framework/cloud-foundry-space.service.mock';
import { ActiveRouteCfOrgSpace } from '../../../../../cf-page.types';
import { CloudFoundryOrganizationService } from '../../../../../services/cloud-foundry-organization.service';
import { CloudFoundrySpaceService } from '../../../../../services/cloud-foundry-space.service';
import { CloudFoundrySpaceUsersComponent } from './cloud-foundry-space-users.component';

describe('CloudFoundrySpaceUsersComponent', () => {
let component: CloudFoundrySpaceUsersComponent;
Expand All @@ -16,7 +20,8 @@ describe('CloudFoundrySpaceUsersComponent', () => {
imports: [...BaseTestModules],
providers: [
{ provide: CloudFoundrySpaceService, useClass: CloudFoundrySpaceServiceMock },
ActiveRouteCfOrgSpace
{ provide: CloudFoundryOrganizationService, useClass: CloudFoundryOrganizationServiceMock },
ActiveRouteCfOrgSpace,
]
})
.compileComponents();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,27 @@
import { CfUserListConfigService } from './../../../../shared/components/list/list-types/cf-users/cf-user-list-config.service';
import { ListConfig } from './../../../../shared/components/list/list.component.types';
import { Component, OnInit } from '@angular/core';
import { Store } from '@ngrx/store';
import { CfUserService } from '../../../../shared/data-services/cf-user.service';
import { Router } from '@angular/router';
import { ActiveRouteCfOrgSpace } from '../../cf-page.types';
import { CurrentUserPermissionsService } from '../../../../core/current-user-permissions.service';
import { AppState } from '../../../../store/app-state';

@Component({
selector: 'app-cloud-foundry-users',
templateUrl: './cloud-foundry-users.component.html',
styleUrls: ['./cloud-foundry-users.component.scss'],
providers: [{
provide: ListConfig,
useClass: CfUserListConfigService
useFactory: (
store: Store<AppState>,
cfUserService: CfUserService,
router: Router,
activeRouteCfOrgSpace: ActiveRouteCfOrgSpace,
userPerms: CurrentUserPermissionsService,
) => new CfUserListConfigService(store, cfUserService, router, activeRouteCfOrgSpace, userPerms),
deps: [Store, CfUserService, Router, ActiveRouteCfOrgSpace, CurrentUserPermissionsService]
}]
})
export class CloudFoundryUsersComponent { }
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import {
} from '../../../../../features/cloud-foundry/services/cloud-foundry-organization.service';
import { AppState } from '../../../../../store/app-state';
import { CfUserService } from '../../../../data-services/cf-user.service';
import { CfUserDataSourceService } from '../cf-users/cf-user-data-source.service';
import { CfUserListConfigService } from '../cf-users/cf-user-list-config.service';

@Injectable()
Expand All @@ -22,8 +21,7 @@ export class CfOrgUsersListConfigService extends CfUserListConfigService {
router: Router,
activeRouteCfOrgSpace: ActiveRouteCfOrgSpace,
userPerms: CurrentUserPermissionsService) {
super(store, cfUserService, router, activeRouteCfOrgSpace, userPerms);
this.dataSource = new CfUserDataSourceService(store, cfOrgService.allOrgUsersAction, this);
super(store, cfUserService, router, activeRouteCfOrgSpace, userPerms, cfOrgService.org$);
}

}
Original file line number Diff line number Diff line change
@@ -1,19 +1,23 @@
import { inject, TestBed } from '@angular/core/testing';

import { ActiveRouteCfOrgSpace } from '../../../../../features/cloud-foundry/cf-page.types';
import {
CloudFoundryOrganizationService,
} from '../../../../../features/cloud-foundry/services/cloud-foundry-organization.service';
import { CloudFoundrySpaceService } from '../../../../../features/cloud-foundry/services/cloud-foundry-space.service';
import { BaseTestModules } from '../../../../../test-framework/cloud-foundry-endpoint-service.helper';
import { CloudFoundryOrganizationServiceMock } from '../../../../../test-framework/cloud-foundry-organization.service.mock';
import { CloudFoundrySpaceServiceMock } from '../../../../../test-framework/cloud-foundry-space.service.mock';
import { CfSpaceUsersListConfigService } from './cf-space-users-list-config.service';
import { ActiveRouteCfOrgSpace } from '../../../../../features/cloud-foundry/cf-page.types';

describe('CfSpaceUsersListConfigService', () => {
beforeEach(() => {
TestBed.configureTestingModule({
providers: [
CfSpaceUsersListConfigService,
{ provide: CloudFoundrySpaceService, useClass: CloudFoundrySpaceServiceMock },
{ provide: CloudFoundryOrganizationService, useClass: CloudFoundryOrganizationServiceMock },
ActiveRouteCfOrgSpace

],
imports: [...BaseTestModules]
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,24 @@ import { Store } from '@ngrx/store';

import { CurrentUserPermissionsService } from '../../../../../core/current-user-permissions.service';
import { ActiveRouteCfOrgSpace } from '../../../../../features/cloud-foundry/cf-page.types';
import {
CloudFoundryOrganizationService,
} from '../../../../../features/cloud-foundry/services/cloud-foundry-organization.service';
import { CloudFoundrySpaceService } from '../../../../../features/cloud-foundry/services/cloud-foundry-space.service';
import { AppState } from '../../../../../store/app-state';
import { CfUserService } from '../../../../data-services/cf-user.service';
import { CfUserDataSourceService } from '../cf-users/cf-user-data-source.service';
import { CfUserListConfigService } from '../cf-users/cf-user-list-config.service';

@Injectable()
export class CfSpaceUsersListConfigService extends CfUserListConfigService {
constructor(
store: Store<AppState>,
cfSpaceService: CloudFoundrySpaceService,
cfOrgService: CloudFoundryOrganizationService,
cfUserService: CfUserService,
router: Router,
activeRouteCfOrgSpace: ActiveRouteCfOrgSpace,
userPerms: CurrentUserPermissionsService) {
super(store, cfUserService, router, activeRouteCfOrgSpace, userPerms);
this.dataSource = new CfUserDataSourceService(store, cfSpaceService.allSpaceUsersAction, this);
super(store, cfUserService, router, activeRouteCfOrgSpace, userPerms, cfOrgService.org$, cfSpaceService.space$);
}
}
Original file line number Diff line number Diff line change
@@ -1 +1 @@
<app-chips [chips]="chipsConfig"></app-chips>
<app-chips [chips]="chipsConfig$ | async"></app-chips>
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { ChangeDetectionStrategy, Component } from '@angular/core';
import { Store } from '@ngrx/store';
import { map } from 'rxjs/operators';
import { combineLatest } from 'rxjs';
import { map, switchMap } from 'rxjs/operators';

import { IOrganization } from '../../../../../../core/cf-api.types';
import { CurrentUserPermissions } from '../../../../../../core/current-user-permissions.config';
import { CurrentUserPermissionsService } from '../../../../../../core/current-user-permissions.service';
import { arrayHelper } from '../../../../../../core/helper-classes/array.helper';
Expand All @@ -13,9 +15,11 @@ import { APIResource } from '../../../../../../store/types/api.types';
import { CfUser, IUserPermissionInOrg, OrgUserRoleNames } from '../../../../../../store/types/user.types';
import { CfUserService } from '../../../../../data-services/cf-user.service';
import { EntityMonitor } from '../../../../../monitors/entity-monitor';
import { AppChip } from '../../../../chips/chips.component';
import { ConfirmationDialogService } from '../../../../confirmation-dialog.service';
import { CfPermissionCell, ICellPermissionList } from '../cf-permission-cell';


@Component({
selector: 'app-org-user-permission-cell',
templateUrl: './cf-org-permission-cell.component.html',
Expand All @@ -30,14 +34,20 @@ export class CfOrgPermissionCellComponent extends CfPermissionCell<OrgUserRoleNa
confirmDialog: ConfirmationDialogService
) {
super(confirmDialog);
this.chipsConfig$ = combineLatest(
this.rowSubject.asObservable(),
this.configSubject.asObservable().pipe(switchMap(config => config.org$))
).pipe(
map(([user, org]: [APIResource<CfUser>, APIResource<IOrganization>]) => this.setChipConfig(user, org))
);
}

protected setChipConfig(row: APIResource<CfUser>) {
const userRoles = this.cfUserService.getOrgRolesFromUser(row.entity);
private setChipConfig(row: APIResource<CfUser>, org: APIResource<IOrganization>): AppChip<ICellPermissionList<OrgUserRoleNames>>[] {
const userRoles = this.cfUserService.getOrgRolesFromUser(row.entity, org);
const userOrgPermInfo = arrayHelper.flatten<ICellPermissionList<OrgUserRoleNames>>(
userRoles.map(orgPerms => this.getOrgPermissions(orgPerms, row))
);
this.chipsConfig = this.getChipConfig(userOrgPermInfo);
return this.getChipConfig(userOrgPermInfo);
}

private getOrgPermissions(orgPerms: IUserPermissionInOrg, row: APIResource<CfUser>): ICellPermissionList<OrgUserRoleNames>[] {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import { Input } from '@angular/core';
import { Observable, of as observableOf } from 'rxjs';
import { BehaviorSubject, Observable, of as observableOf } from 'rxjs';
import { map } from 'rxjs/operators';

import { IUserRole } from '../../../../../features/cloud-foundry/cf.helpers';
import { APIResource } from '../../../../../store/types/api.types';
import { CfUser } from '../../../../../store/types/user.types';
import { AppChip } from '../../../chips/chips.component';
import { TableCellCustom } from '../../list.types';
import { ConfirmationDialogService } from '../../../confirmation-dialog.service';
import { ConfirmationDialogConfig } from '../../../confirmation-dialog.config';
import { ConfirmationDialogService } from '../../../confirmation-dialog.service';
import { TableCellCustom } from '../../list.types';


export interface ICellPermissionList<T> extends IUserRole<T> {
Expand All @@ -26,24 +26,29 @@ interface ICellPermissionUpdates {
[key: string]: Observable<boolean>;
}

export abstract class CfPermissionCell<T> extends TableCellCustom<APIResource<CfUser>> {
export abstract class CfPermissionCell<T> extends TableCellCustom<APIResource<CfUser>> {

@Input('row')
set row(row: APIResource<CfUser>) {
this.setChipConfig(row);
this.rowSubject.next(row);
this.guid = row.metadata.guid;
}
public chipsConfig: AppChip<ICellPermissionList<T>>[];

@Input('config')
set config(config: any) {
this.configSubject.next(config);
}

public chipsConfig$: Observable<AppChip<ICellPermissionList<T>>[]>;
protected guid: string;

protected rowSubject = new BehaviorSubject<APIResource<CfUser>>(null);
protected configSubject = new BehaviorSubject<any>(null);

constructor(private confirmDialog: ConfirmationDialogService) {
super();
}

protected setChipConfig(user: APIResource<CfUser>) {

}

protected getChipConfig(cellPermissionList: ICellPermissionList<T>[]) {
return cellPermissionList.map(perm => {
const chipConfig = new AppChip<ICellPermissionList<T>>();
Expand Down
Original file line number Diff line number Diff line change
@@ -1 +1 @@
<app-chips [chips]="chipsConfig"></app-chips>
<app-chips [chips]="chipsConfig$ | async"></app-chips>
Original file line number Diff line number Diff line change
@@ -1,21 +1,25 @@
import { ChangeDetectionStrategy, Component } from '@angular/core';
import { Store } from '@ngrx/store';
import { map } from 'rxjs/operators';
import { combineLatest, of as observableOf } from 'rxjs';
import { map, switchMap } from 'rxjs/operators';

import { IOrganization, ISpace } from '../../../../../../core/cf-api.types';
import { CurrentUserPermissions } from '../../../../../../core/current-user-permissions.config';
import { CurrentUserPermissionsService } from '../../../../../../core/current-user-permissions.service';
import { arrayHelper } from '../../../../../../core/helper-classes/array.helper';
import { getSpaceRoles } from '../../../../../../features/cloud-foundry/cf.helpers';
import { RemoveUserPermission } from '../../../../../../store/actions/users.actions';
import { AppState } from '../../../../../../store/app-state';
import { entityFactory, spaceSchemaKey } from '../../../../../../store/helpers/entity-factory';
import { entityFactory, organizationSchemaKey, spaceSchemaKey } from '../../../../../../store/helpers/entity-factory';
import { selectEntity } from '../../../../../../store/selectors/api.selectors';
import { APIResource } from '../../../../../../store/types/api.types';
import { CfUser, IUserPermissionInSpace, SpaceUserRoleNames } from '../../../../../../store/types/user.types';
import { CfUserService } from '../../../../../data-services/cf-user.service';
import { EntityMonitor } from '../../../../../monitors/entity-monitor';
import { ConfirmationDialogService } from '../../../../confirmation-dialog.service';
import { CfPermissionCell, ICellPermissionList } from '../cf-permission-cell';


@Component({
selector: 'app-cf-space-permission-cell',
templateUrl: './cf-space-permission-cell.component.html',
Expand All @@ -31,14 +35,53 @@ export class CfSpacePermissionCellComponent extends CfPermissionCell<SpaceUserRo
confirmDialog: ConfirmationDialogService
) {
super(confirmDialog);
this.chipsConfig$ = combineLatest(
this.rowSubject.asObservable(),
this.configSubject.asObservable().pipe(switchMap(config => config.org$)),
this.configSubject.asObservable().pipe(switchMap(config => config.spaces$))
).pipe(
switchMap(([user, org, spaces]: [APIResource<CfUser>, APIResource<IOrganization>, APIResource<ISpace>[]]) => {
const permissionList = this.createPermissions(user, spaces && spaces.length ? spaces : null);
// If we're showing spaces from multiple orgs prefix the org name to the space name
return org ? observableOf(this.getChipConfig(permissionList)) : this.prefixOrgName(permissionList);
})
);
}

private prefixOrgName(permissionList) {
// Find all unique org guids
const orgGuids = permissionList.map(permission => permission.orgGuid).filter((value, index, self) => self.indexOf(value) === index);
// Find names of all orgs
const orgNames$ = combineLatest(
orgGuids.map(orgGuid => this.store.select<APIResource<IOrganization>>(selectEntity(organizationSchemaKey, orgGuid)))
).pipe(
map((orgs: APIResource<IOrganization>[]) => {
const orgNames: { [orgGuid: string]: string } = {};
orgs.forEach(org => {
orgNames[org.metadata.guid] = org.entity.name;
});
return orgNames;
})
);
return combineLatest(
observableOf(permissionList),
orgNames$
).pipe(
map(([permissions, orgNames]) => {
// Prefix permission name with org name
permissions.forEach(permission => {
permission.name = `${orgNames[permission.orgGuid]}: ${permission.name}`;
});
return this.getChipConfig(permissions);
})
);
}

protected setChipConfig(row: APIResource<CfUser>) {
const userRoles = this.cfUserService.getSpaceRolesFromUser(row.entity);
const userPermInfo = arrayHelper.flatten<ICellPermissionList<SpaceUserRoleNames>>(
private createPermissions(row: APIResource<CfUser>, spaces: APIResource<ISpace>[]): ICellPermissionList<SpaceUserRoleNames>[] {
const userRoles = this.cfUserService.getSpaceRolesFromUser(row.entity, spaces);
return arrayHelper.flatten<ICellPermissionList<SpaceUserRoleNames>>(
userRoles.map(spacePerms => this.getSpacePermissions(spacePerms, row))
);
this.chipsConfig = this.getChipConfig(userPermInfo);
}

private getSpacePermissions(spacePerms: IUserPermissionInSpace, row: APIResource<CfUser>) {
Expand Down
Loading