Skip to content
This repository has been archived by the owner on Jan 24, 2023. It is now read-only.

Update the connected user roles section of store on roles change #2435

Merged
merged 2 commits into from
Jun 20, 2018
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@

import {combineLatest as observableCombineLatest, BehaviorSubject , Observable } from 'rxjs';
import { combineLatest as observableCombineLatest, BehaviorSubject, Observable } from 'rxjs';
import { AfterContentInit, Component, OnInit } from '@angular/core';
import { Store } from '@ngrx/store';
import { distinctUntilChanged, filter, first, map, mergeMap, withLatestFrom } from 'rxjs/operators';
Expand All @@ -22,7 +22,7 @@ import {
} from '../../../../../shared/components/list/list-types/cf-confirm-roles/table-cell-confirm-role-add-rem/table-cell-confirm-role-add-rem.component';
import { CfUserService } from '../../../../../shared/data-services/cf-user.service';
import { UsersRolesClearUpdateState } from '../../../../../store/actions/users-roles.actions';
import { ChangeUserPermission } from '../../../../../store/actions/users.actions';
import { ChangeUserRole } from '../../../../../store/actions/users.actions';
import { AppState } from '../../../../../store/app-state';
import {
cfUserSchemaKey,
Expand Down Expand Up @@ -100,7 +100,7 @@ export class UsersRolesConfirmComponent implements OnInit, AfterContentInit {
entityKey: schema.key,
schema: schema,
monitorState: AppMonitorComponentTypes.UPDATE,
updateKey: ChangeUserPermission.generateUpdatingKey(row.role, row.userGuid),
updateKey: ChangeUserRole.generateUpdatingKey(row.role, row.userGuid),
getId: () => guid
};
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { CurrentUserPermissions } from '../../../../../../core/current-user-perm
import { CurrentUserPermissionsService } from '../../../../../../core/current-user-permissions.service';
import { arrayHelper } from '../../../../../../core/helper-classes/array.helper';
import { getOrgRoles } from '../../../../../../features/cloud-foundry/cf.helpers';
import { RemoveUserPermission } from '../../../../../../store/actions/users.actions';
import { RemoveUserRole } from '../../../../../../store/actions/users.actions';
import { AppState } from '../../../../../../store/app-state';
import { entityFactory, organizationSchemaKey } from '../../../../../../store/helpers/entity-factory';
import { APIResource } from '../../../../../../store/types/api.types';
Expand All @@ -24,12 +24,12 @@ import { CfPermissionCell, ICellPermissionList } from '../cf-permission-cell';
})
export class CfOrgPermissionCellComponent extends CfPermissionCell<OrgUserRoleNames> {
constructor(
public store: Store<AppState>,
store: Store<AppState>,
public cfUserService: CfUserService,
private userPerms: CurrentUserPermissionsService,
confirmDialog: ConfirmationDialogService
) {
super(confirmDialog);
super(store, confirmDialog);
}

protected setChipConfig(row: APIResource<CfUser>) {
Expand All @@ -42,7 +42,7 @@ export class CfOrgPermissionCellComponent extends CfPermissionCell<OrgUserRoleNa

private getOrgPermissions(orgPerms: IUserPermissionInOrg, row: APIResource<CfUser>): ICellPermissionList<OrgUserRoleNames>[] {
return getOrgRoles(orgPerms.permissions).map(perm => {
const updatingKey = RemoveUserPermission.generateUpdatingKey(
const updatingKey = RemoveUserRole.generateUpdatingKey(
perm.key,
row.metadata.guid
);
Expand All @@ -66,13 +66,14 @@ export class CfOrgPermissionCellComponent extends CfPermissionCell<OrgUserRoleNa
});
}

public removePermission(cellPermission: ICellPermissionList<OrgUserRoleNames>) {
this.store.dispatch(new RemoveUserPermission(
public removePermission(cellPermission: ICellPermissionList<OrgUserRoleNames>, updateConnectedUser: boolean) {
this.store.dispatch(new RemoveUserRole(
this.cfUserService.activeRouteCfOrgSpace.cfGuid,
cellPermission.userGuid,
cellPermission.guid,
cellPermission.key,
false
false,
updateConnectedUser
));
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Input } from '@angular/core';
import { Observable, of as observableOf } from 'rxjs';
import { map } from 'rxjs/operators';
import { map, first } from 'rxjs/operators';

import { IUserRole } from '../../../../../features/cloud-foundry/cf.helpers';
import { APIResource } from '../../../../../store/types/api.types';
Expand All @@ -9,6 +9,9 @@ import { AppChip } from '../../../chips/chips.component';
import { TableCellCustom } from '../../list.types';
import { ConfirmationDialogService } from '../../../confirmation-dialog.service';
import { ConfirmationDialogConfig } from '../../../confirmation-dialog.config';
import { Store } from '@ngrx/store';
import { AppState } from '../../../../../store/app-state';
import { selectSessionData } from '../../../../../store/reducers/auth.reducer';


export interface ICellPermissionList<T> extends IUserRole<T> {
Expand Down Expand Up @@ -36,7 +39,7 @@ export abstract class CfPermissionCell<T> extends TableCellCustom<APIResource<Cf
protected guid: string;


constructor(private confirmDialog: ConfirmationDialogService) {
constructor(public store: Store<AppState>, private confirmDialog: ConfirmationDialogService) {
super();
}

Expand Down Expand Up @@ -70,11 +73,17 @@ export abstract class CfPermissionCell<T> extends TableCellCustom<APIResource<Cf
true
);
this.confirmDialog.open(confirmation, () => {
this.removePermission(cellPermission);
this.store.select(selectSessionData()).pipe(
first()
).subscribe(sessionData => {
const cfSession = sessionData.endpoints.cf[cellPermission.cfGuid];
const updateConnectedUser = !cfSession.user.admin && cellPermission.userGuid === cfSession.user.guid;
this.removePermission(cellPermission, updateConnectedUser);
});
});
}

protected removePermission(cellPermission: ICellPermissionList<T>) {
protected removePermission(cellPermission: ICellPermissionList<T>, updateConnectedUser: boolean) {

}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { CurrentUserPermissions } from '../../../../../../core/current-user-perm
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 { RemoveUserRole } from '../../../../../../store/actions/users.actions';
import { AppState } from '../../../../../../store/app-state';
import { entityFactory, spaceSchemaKey } from '../../../../../../store/helpers/entity-factory';
import { APIResource } from '../../../../../../store/types/api.types';
Expand All @@ -25,12 +25,12 @@ import { CfPermissionCell, ICellPermissionList } from '../cf-permission-cell';
export class CfSpacePermissionCellComponent extends CfPermissionCell<SpaceUserRoleNames> {

constructor(
public store: Store<AppState>,
store: Store<AppState>,
public cfUserService: CfUserService,
private userPerms: CurrentUserPermissionsService,
confirmDialog: ConfirmationDialogService
) {
super(confirmDialog);
super(store, confirmDialog);
}

protected setChipConfig(row: APIResource<CfUser>) {
Expand All @@ -43,7 +43,7 @@ export class CfSpacePermissionCellComponent extends CfPermissionCell<SpaceUserRo

private getSpacePermissions(spacePerms: IUserPermissionInSpace, row: APIResource<CfUser>) {
return getSpaceRoles(spacePerms.permissions).map(perm => {
const updatingKey = RemoveUserPermission.generateUpdatingKey(
const updatingKey = RemoveUserRole.generateUpdatingKey(
perm.key,
row.metadata.guid
);
Expand All @@ -68,13 +68,14 @@ export class CfSpacePermissionCellComponent extends CfPermissionCell<SpaceUserRo
});
}

public removePermission(cellPermission: ICellPermissionList<SpaceUserRoleNames>) {
this.store.dispatch(new RemoveUserPermission(
public removePermission(cellPermission: ICellPermissionList<SpaceUserRoleNames>, updateConnectedUser: boolean) {
this.store.dispatch(new RemoveUserRole(
this.cfUserService.activeRouteCfOrgSpace.cfGuid,
cellPermission.userGuid,
cellPermission.guid,
cellPermission.key,
true
true,
updateConnectedUser
));
}

Expand Down
37 changes: 21 additions & 16 deletions src/frontend/app/store/actions/users.actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,13 @@ export const GET_ALL = '[Users] Get all';
export const GET_ALL_SUCCESS = '[Users] Get all success';
export const GET_ALL_FAILED = '[Users] Get all failed';

export const REMOVE_PERMISSION = '[Users] Remove Permission';
export const REMOVE_PERMISSION_SUCCESS = '[Users] Remove Permission success';
export const REMOVE_PERMISSION_FAILED = '[Users] Remove Permission failed';
export const REMOVE_ROLE = '[Users] Remove role';
export const REMOVE_ROLE_SUCCESS = '[Users] Remove role success';
export const REMOVE_ROLE_FAILED = '[Users] Remove role failed';

export const ADD_PERMISSION = '[Users] Add Permission';
export const ADD_PERMISSION_SUCCESS = '[Users] Add Permission success';
export const ADD_PERMISSION_FAILED = '[Users] Add Permission failed';
export const ADD_ROLE = '[Users] Add role';
export const ADD_ROLE_SUCCESS = '[Users] Add role success';
export const ADD_ROLE_FAILED = '[Users] Add role failed';

const defaultUserRelations = [
createEntityRelationKey(cfUserSchemaKey, organizationSchemaKey),
Expand Down Expand Up @@ -101,7 +101,7 @@ export class GetCFUser extends CFStartAction implements IRequestAction {
options: RequestOptions;
}

export class ChangeUserPermission extends CFStartAction implements IRequestAction {
export class ChangeUserRole extends CFStartAction implements IRequestAction {
constructor(
public endpointGuid: string,
public userGuid: string,
Expand All @@ -110,10 +110,11 @@ export class ChangeUserPermission extends CFStartAction implements IRequestActio
public permissionTypeKey: OrgUserRoleNames | SpaceUserRoleNames,
public entityGuid: string,
public isSpace = false,
public updateConnectedUser = false
) {
super();
this.guid = entityGuid;
this.updatingKey = ChangeUserPermission.generateUpdatingKey(permissionTypeKey, userGuid);
this.updatingKey = ChangeUserRole.generateUpdatingKey(permissionTypeKey, userGuid);
this.options = new RequestOptions();
this.options.url = `${isSpace ? 'spaces' : 'organizations'}/${this.guid}/${this.updatingKey}`;
this.options.method = method;
Expand All @@ -132,42 +133,46 @@ export class ChangeUserPermission extends CFStartAction implements IRequestActio
}
}

export class AddUserPermission extends ChangeUserPermission {
export class AddUserRole extends ChangeUserRole {
constructor(
endpointGuid: string,
userGuid: string,
entityGuid: string,
permissionTypeKey: OrgUserRoleNames | SpaceUserRoleNames,
isSpace = false
isSpace = false,
updateConnectedUser = false
) {
super(
endpointGuid,
userGuid,
'put',
[ADD_PERMISSION, ADD_PERMISSION_SUCCESS, ADD_PERMISSION_FAILED],
[ADD_ROLE, ADD_ROLE_SUCCESS, ADD_ROLE_FAILED],
permissionTypeKey,
entityGuid,
isSpace
isSpace,
updateConnectedUser,
);
}
}

export class RemoveUserPermission extends ChangeUserPermission {
export class RemoveUserRole extends ChangeUserRole {
constructor(
endpointGuid: string,
userGuid: string,
entityGuid: string,
permissionTypeKey: OrgUserRoleNames | SpaceUserRoleNames,
isSpace = false
isSpace = false,
updateConnectedUser = false
) {
super(
endpointGuid,
userGuid,
'delete',
[REMOVE_PERMISSION, REMOVE_PERMISSION_SUCCESS, REMOVE_PERMISSION_FAILED],
[REMOVE_ROLE, REMOVE_ROLE_SUCCESS, REMOVE_ROLE_FAILED],
permissionTypeKey,
entityGuid,
isSpace
isSpace,
updateConnectedUser
);
}
}
Expand Down
38 changes: 23 additions & 15 deletions src/frontend/app/store/effects/users-roles.effects.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@ import { filter, first, map, mergeMap, pairwise, withLatestFrom } from 'rxjs/ope

import { EntityMonitor } from '../../shared/monitors/entity-monitor';
import { UsersRolesActions, UsersRolesClearUpdateState, UsersRolesExecuteChanges } from '../actions/users-roles.actions';
import { AddUserPermission, ChangeUserPermission, RemoveUserPermission } from '../actions/users.actions';
import { AddUserRole, ChangeUserRole, RemoveUserRole } from '../actions/users.actions';
import { AppState } from '../app-state';
import { entityFactory, organizationSchemaKey, spaceSchemaKey } from '../helpers/entity-factory';
import { selectSessionData } from '../reducers/auth.reducer';
import { selectUsersRoles } from '../selectors/users-roles.selector';
import { SessionDataEndpoint } from '../types/auth.types';
import { ICFAction, UpdateCfAction } from '../types/request.types';
import { OrgUserRoleNames } from '../types/user.types';
import { CfRoleChange } from '../types/users-roles.types';
Expand All @@ -30,7 +32,7 @@ export class UsersRolesEffects {
const apiAction = {
guid: change.spaceGuid ? change.spaceGuid : change.orgGuid,
entityKey: change.spaceGuid ? spaceSchemaKey : organizationSchemaKey,
updatingKey: ChangeUserPermission.generateUpdatingKey(change.role, change.userGuid),
updatingKey: ChangeUserRole.generateUpdatingKey(change.role, change.userGuid),
options: null,
actions: []
} as ICFAction;
Expand All @@ -42,14 +44,18 @@ export class UsersRolesEffects {
);

@Effect() executeUsersRolesChange$ = this.actions$.ofType<UsersRolesExecuteChanges>(UsersRolesActions.ExecuteChanges).pipe(
withLatestFrom(this.store.select(selectUsersRoles)),
mergeMap(([action, usersRoles]) => {
withLatestFrom(
this.store.select(selectUsersRoles),
this.store.select(selectSessionData())
),
mergeMap(([action, usersRoles, sessionData]) => {
// If the user is adding the org user role then that needs to execute and succeed first, otherwise the other changes will fail
// Conversely if the org user role is being removed all other changes need to execute first
// Note - we should never be in the state where a user is adding space/org roles and removing org user. We could safeguard against
// this here, however the UX should have already ensured this

const cfGuid = usersRoles.cfGuid;
const cfSession = sessionData.endpoints.cf[cfGuid];
const changes = [...usersRoles.changedRoles];

// Split changes into `org user` and `other`
Expand All @@ -69,30 +75,32 @@ export class UsersRolesEffects {
if (orgUserChanges.length) {
// Are we adding the org user role (can never add to one user and remove from another)
if (orgUserChanges[0].add) {

// Do org user changes first
return this.executeChanges(cfGuid, orgUserChanges).pipe(
return this.executeChanges(cfGuid, cfSession, orgUserChanges).pipe(
// Then do all other changes
mergeMap(() => this.executeChanges(cfGuid, nonOrgUserChanges))
mergeMap(() => this.executeChanges(cfGuid, cfSession, nonOrgUserChanges))
);
} else {
// Do all other changes first
return this.executeChanges(cfGuid, nonOrgUserChanges).pipe(
return this.executeChanges(cfGuid, cfSession, nonOrgUserChanges).pipe(
// Then do org user change
mergeMap(() => this.executeChanges(cfGuid, orgUserChanges))
mergeMap(() => this.executeChanges(cfGuid, cfSession, orgUserChanges))
);
}
} else {
return this.executeChanges(cfGuid, nonOrgUserChanges);
return this.executeChanges(cfGuid, cfSession, nonOrgUserChanges);
}
}),
mergeMap(() => [])
);


private executeChanges(cfGuid: string, changes: CfRoleChange[]): Observable<boolean[]> {
private executeChanges(cfGuid: string, cfSession: SessionDataEndpoint, changes: CfRoleChange[]): Observable<boolean[]> {
const observables: Observable<boolean>[] = [];
changes.forEach(change => {
const action = this.createAction(cfGuid, change);
const updateConnectedUser = !cfSession.user.admin && change.userGuid === cfSession.user.guid;
const action = this.createAction(cfGuid, updateConnectedUser, change);
this.store.dispatch(action);
observables.push(this.createActionObs(action));
});
Expand All @@ -102,15 +110,15 @@ export class UsersRolesEffects {
);
}

private createAction(cfGuid: string, change: CfRoleChange): ChangeUserPermission {
private createAction(cfGuid: string, updateConnectedUser: boolean, change: CfRoleChange): ChangeUserRole {
const isSpace = !!change.spaceGuid;
const entityGuid = isSpace ? change.spaceGuid : change.orgGuid;
return change.add ?
new AddUserPermission(cfGuid, change.userGuid, entityGuid, change.role, isSpace) :
new RemoveUserPermission(cfGuid, change.userGuid, entityGuid, change.role, isSpace);
new AddUserRole(cfGuid, change.userGuid, entityGuid, change.role, isSpace, updateConnectedUser) :
new RemoveUserRole(cfGuid, change.userGuid, entityGuid, change.role, isSpace, updateConnectedUser);
}

private createActionObs(action: ChangeUserPermission): Observable<boolean> {
private createActionObs(action: ChangeUserRole): Observable<boolean> {
return new EntityMonitor(
this.store,
action.guid,
Expand Down
3 changes: 2 additions & 1 deletion src/frontend/app/store/reducers/auth.reducer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
VERIFY_SESSION,
} from './../actions/auth.actions';
import { RouterRedirect } from './routing.reducer';
import { AppState } from '../app-state';

export interface AuthUser {
guid: string;
Expand Down Expand Up @@ -86,5 +87,5 @@ export function authReducer(state: AuthState = defaultState, action): AuthState
}

export function selectSessionData() {
return (state) => state.auth.sessionData;
return (state: AppState) => state.auth.sessionData;
}
Loading