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

Commit

Permalink
Merge pull request #2435 from cloudfoundry-incubator/update-user-roles
Browse files Browse the repository at this point in the history
Update the connected user roles section of store on roles change
  • Loading branch information
richard-cox committed Jun 20, 2018
2 parents 1078f83 + 4482a49 commit 7c93550
Show file tree
Hide file tree
Showing 10 changed files with 173 additions and 70 deletions.
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 @@ -8,7 +8,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 @@ -28,12 +28,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);
this.chipsConfig$ = combineLatest(
this.rowSubject.asObservable(),
this.configSubject.asObservable().pipe(switchMap(config => config.org$))
Expand All @@ -52,7 +52,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 @@ -76,13 +76,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,8 +1,11 @@
import { Input } from '@angular/core';
import { Store } from '@ngrx/store';
import { BehaviorSubject, Observable, of as observableOf } from 'rxjs';
import { map } from 'rxjs/operators';
import { first, map } from 'rxjs/operators';

import { IUserRole } from '../../../../../features/cloud-foundry/cf.helpers';
import { AppState } from '../../../../../store/app-state';
import { selectSessionData } from '../../../../../store/reducers/auth.reducer';
import { APIResource } from '../../../../../store/types/api.types';
import { CfUser } from '../../../../../store/types/user.types';
import { AppChip } from '../../../chips/chips.component';
Expand Down Expand Up @@ -45,7 +48,7 @@ export abstract class CfPermissionCell<T> extends TableCellCustom<APIResource<Cf
protected rowSubject = new BehaviorSubject<APIResource<CfUser>>(null);
protected configSubject = new BehaviorSubject<any>(null);

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

Expand Down Expand Up @@ -75,11 +78,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 @@ -8,7 +8,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, organizationSchemaKey, spaceSchemaKey } from '../../../../../../store/helpers/entity-factory';
import { selectEntity } from '../../../../../../store/selectors/api.selectors';
Expand All @@ -29,12 +29,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);
this.chipsConfig$ = combineLatest(
this.rowSubject.asObservable(),
this.configSubject.asObservable().pipe(switchMap(config => config.org$)),
Expand Down Expand Up @@ -86,7 +86,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 @@ -111,13 +111,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

0 comments on commit 7c93550

Please sign in to comment.