Skip to content

Commit

Permalink
Fix committee admin account list (#3897)
Browse files Browse the repository at this point in the history
  • Loading branch information
luisa-beerboom authored Jul 19, 2024
1 parent cbfb05c commit 980581b
Show file tree
Hide file tree
Showing 8 changed files with 158 additions and 32 deletions.
2 changes: 2 additions & 0 deletions client/src/app/site/pages/meetings/view-models/view-user.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { Observable } from 'rxjs';
import { User } from 'src/app/domain/models/users/user';
import { BaseViewModel } from 'src/app/site/base/base-view-model';

Expand Down Expand Up @@ -335,6 +336,7 @@ interface IUserRelations {
meeting_users: ViewMeetingUser[];
poll_voted: ViewPoll[];
committee_managements: ViewCommittee[];
committee_managements_as_observable: Observable<ViewCommittee[]>;
options: ViewOption[];
votes: ViewVote[];
poll_candidates: ViewPollCandidate[];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ const accountListSubsciptionContent = {
follow: [
{
idField: `meeting_id`,
fieldset: [`is_active_in_organization_id`, `is_archived_in_organization_id`],
fieldset: [`is_active_in_organization_id`, `is_archived_in_organization_id`, `default_group_id`],
follow: [{ idField: `committee_id`, fieldset: [`manager_ids`] }]
}
]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,20 @@ export class AccountListComponent extends BaseListViewComponent<ViewUser> {
return this.operator.hasOrganizationPermissions(OML.can_manage_users);
}

public get fakeFilters(): Observable<{ [key: string]: () => void }> {
if (this.meeting) {
return this.meeting.pipe(
map(meeting => {
if (meeting) {
return { [meeting.name]: () => this.navigateToBaseList() };
}
return {};
})
);
}
return null;
}

public constructor(
protected override translate: TranslateService,
public readonly controller: AccountControllerService,
Expand Down Expand Up @@ -83,20 +97,6 @@ export class AccountListComponent extends BaseListViewComponent<ViewUser> {
this.router.navigate([`/accounts`]);
}

public get fakeFilters(): Observable<{ [key: string]: () => void }> {
if (this.meeting) {
return this.meeting.pipe(
map(meeting => {
if (meeting) {
return { [meeting.name]: () => this.navigateToBaseList() };
}
return {};
})
);
}
return null;
}

public async deleteUsers(accounts: ViewUser[] = this.selectedRows): Promise<void> {
await this.controller.doDeleteOrRemove(accounts);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { Injectable } from '@angular/core';
import { marker as _ } from '@colsen1991/ngx-translate-extract-marker';
import { firstValueFrom } from 'rxjs';
import { firstValueFrom, map, Observable } from 'rxjs';
import { Id } from 'src/app/domain/definitions/key-types';
import { OML } from 'src/app/domain/definitions/organization-permission';
import { Identifiable } from 'src/app/domain/interfaces';
import { User } from 'src/app/domain/models/users/user';
import { Action } from 'src/app/gateways/actions';
Expand All @@ -20,6 +22,8 @@ import { AccountCommonServiceModule } from './account-common-service.module';
providedIn: AccountCommonServiceModule
})
export class AccountControllerService extends BaseController<ViewUser, User> {
private _committee_users_set: Set<Id> = new Set();

public constructor(
controllerServiceCollector: ControllerServiceCollectorService,
protected override repo: UserRepositoryService,
Expand All @@ -28,6 +32,32 @@ export class AccountControllerService extends BaseController<ViewUser, User> {
private operator: OperatorService
) {
super(controllerServiceCollector, User, repo);
this.operator.user.committee_managements_as_observable.subscribe(committees => {
this._committee_users_set = new Set(committees.flatMap(committee => committee.user_ids ?? []));
});
}

public override getViewModelListObservable(): Observable<ViewUser[]> {
return super
.getViewModelListObservable()
.pipe(map(accounts => this.filterAccountsForCommitteeAdmins(accounts)));
}

public override getSortedViewModelListObservable(): Observable<ViewUser[]> {
return super
.getSortedViewModelListObservable()
.pipe(map(accounts => this.filterAccountsForCommitteeAdmins(accounts)));
}

public override getViewModelList(): ViewUser[] {
return this.filterAccountsForCommitteeAdmins(super.getViewModelList());
}

private filterAccountsForCommitteeAdmins(accounts: ViewUser[]): ViewUser[] {
if (this.operator.hasOrganizationPermissions(OML.can_manage_users)) {
return accounts;
}
return accounts.filter(account => this._committee_users_set.has(account.id));
}

public bulkAddUserToMeeting(users: ViewUser[], ...meetings: ViewMeeting[]): Action<void> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,11 +78,12 @@
<!-- users -->
<div class="user-footer">
<ng-container
*osOmlPerms="
OML.can_manage_users;
allowCommitteeAdmin: true;
*osCmlPerms="
CML.can_manage;
committeeId: committee.id;
orOML: OML.can_manage_users;
then: canManageUsersTemplate;
else canNotManageUsersTemplate
else: canNotManageUsersTemplate
"
></ng-container>
</div>
Expand Down
69 changes: 60 additions & 9 deletions client/src/app/ui/directives/perms/cml-perms.directive.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { ComponentFixture, TestBed } from '@angular/core/testing';
import { By } from '@angular/platform-browser';
import { Observable, Subject } from 'rxjs';
import { Id } from 'src/app/domain/definitions/key-types';
import { CML } from 'src/app/domain/definitions/organization-permission';
import { CML, OML } from 'src/app/domain/definitions/organization-permission';
import { OperatorService } from 'src/app/site/services/operator.service';

import { CmlPermsDirective } from './cml-perms.directive';
Expand All @@ -15,6 +15,7 @@ type TestConditionalType = {
complement: boolean;
id: number;
nonAdmin: boolean;
orOML: OML | undefined;
};

@Component({
Expand All @@ -29,12 +30,22 @@ type TestConditionalType = {
*osCmlPerms="permission; committeeId: conditionals.id; complement: conditionals.complement"
id="complement"
></div>
<div *osCmlPerms="permission; committeeId: conditionals.id; orOML: conditionals.orOML" id="oml"></div>
<ng-container
*osCmlPerms="permission; committeeId: conditionals.id; then: thenTemplate; else elseTemplate"
></ng-container>
<ng-template #thenTemplate>
<div id="then"></div>
</ng-template>
<ng-template #elseTemplate>
<div id="else"></div>
</ng-template>
`
})
class TestComponent extends BasePermsTestComponent<TestConditionalType> {
public permission = CML.can_manage;
public constructor() {
super({ and: true, or: true, complement: true, id: 1, nonAdmin: false });
super({ and: true, or: true, complement: true, id: 1, nonAdmin: false, orOML: undefined });
}
}

Expand All @@ -45,8 +56,13 @@ class MockOperatorService {

private _operatorUpdatedSubject = new Subject<void>();
private _permList: CML[] = [];
private _oml: OML | undefined = undefined;
private _isAdmin = false;

public hasOrganizationPermissions(...checkPerms: OML[]): boolean {
return checkPerms.some(perm => perm === this._oml);
}

public hasCommitteePermissions(committeeId: Id | null, ...checkPerms: CML[]): boolean {
return this._isAdmin || this.hasCommitteePermissionsNonAdminCheck(committeeId, ...checkPerms);
}
Expand All @@ -55,11 +71,12 @@ class MockOperatorService {
return checkPerms.some(perm => this._permList.includes(perm));
}

public changeOperatorPermsForTest(newPermList: CML[], isAdmin?: boolean): void {
public changeOperatorPermsForTest(newPermList: CML[], oml?: OML | undefined): void {
this._permList = newPermList;
if (isAdmin !== undefined) {
this._isAdmin = isAdmin;
if (oml) {
this._isAdmin = oml === OML.superadmin;
}
this._oml = oml;
this._operatorUpdatedSubject.next();
}
}
Expand Down Expand Up @@ -96,10 +113,10 @@ describe(`CmlPermsDirective`, () => {
operatorService.changeOperatorPermsForTest([]);
update();
expect(getElement(`#normal`)).toBeFalsy();
operatorService.changeOperatorPermsForTest([CML.can_manage], true);
operatorService.changeOperatorPermsForTest([CML.can_manage], OML.superadmin);
update();
expect(getElement(`#normal`)).toBeTruthy();
operatorService.changeOperatorPermsForTest([]);
operatorService.changeOperatorPermsForTest([], OML.superadmin);
update();
expect(getElement(`#normal`)).toBeTruthy();
});
Expand All @@ -112,10 +129,10 @@ describe(`CmlPermsDirective`, () => {
operatorService.changeOperatorPermsForTest([]);
update();
expect(getElement(`#normal`)).toBeFalsy();
operatorService.changeOperatorPermsForTest([CML.can_manage], true);
operatorService.changeOperatorPermsForTest([CML.can_manage], OML.superadmin);
update();
expect(getElement(`#normal`)).toBeTruthy();
operatorService.changeOperatorPermsForTest([]);
operatorService.changeOperatorPermsForTest([], OML.superadmin);
update();
expect(getElement(`#normal`)).toBeFalsy();
});
Expand Down Expand Up @@ -161,4 +178,38 @@ describe(`CmlPermsDirective`, () => {
update();
expect(getElement(`#normal`)).toBeFalsy();
});

it(`check if orOML works`, async () => {
operatorService.changeOperatorPermsForTest([], OML.can_manage_organization);
update();
expect(getElement(`#oml`)).toBeFalsy();
operatorService.changeOperatorPermsForTest([], undefined);
fixture.componentInstance.setTestComponentData({ orOML: OML.can_manage_users });
update();
expect(getElement(`#oml`)).toBeFalsy();
operatorService.changeOperatorPermsForTest([CML.can_manage]);
update();
expect(getElement(`#oml`)).toBeTruthy();
operatorService.changeOperatorPermsForTest([CML.can_manage], OML.can_manage_users);
update();
expect(getElement(`#oml`)).toBeTruthy();
operatorService.changeOperatorPermsForTest([], OML.can_manage_users);
update();
expect(getElement(`#oml`)).toBeTruthy();
operatorService.changeOperatorPermsForTest([], OML.can_manage_organization);
update();
expect(getElement(`#oml`)).toBeFalsy();
});

it(`check if then and else work`, async () => {
update();
console.log(`BEFORE: THEN:`, getElement(`#then`), `ELSE:`, getElement(`#else`));
// expect(getElement(`#else`)).toBeTruthy();
// expect(getElement(`#then`)).toBeFalsy();
operatorService.changeOperatorPermsForTest([CML.can_manage]);
update();
console.log(`AFTER: THEN:`, getElement(`#then`), `ELSE:`, getElement(`#else`));
expect(getElement(`#else`)).toBeFalsy();
expect(getElement(`#then`)).toBeTruthy();
});
});
24 changes: 22 additions & 2 deletions client/src/app/ui/directives/perms/cml-perms.directive.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Directive, Input } from '@angular/core';
import { Directive, Input, TemplateRef } from '@angular/core';
import { Id } from 'src/app/domain/definitions/key-types';
import { CML } from 'src/app/domain/definitions/organization-permission';
import { CML, OML } from 'src/app/domain/definitions/organization-permission';

import { BasePermsDirective } from './base-perms.directive';

Expand Down Expand Up @@ -40,10 +40,30 @@ export class CmlPermsDirective extends BasePermsDirective<CML> {
this.updatePermission();
}

@Input()
public set osCmlPermsThen(template: TemplateRef<any>) {
this.setThenTemplate(template);
}

@Input()
public set osCmlPermsElse(template: TemplateRef<any>) {
this.setElseTemplate(template);
}

@Input()
public set osCmlPermsOrOML(value: OML | undefined) {
this._orOML = value;
this.updatePermission();
}

private _committeeId: Id | undefined = undefined;
private _checkNonAdmin = false;
private _orOML: OML | undefined = undefined;

protected hasPermissions(): boolean {
if (this._orOML && this.operator.hasOrganizationPermissions(this._orOML)) {
return true;
}
if (!this._committeeId) {
return false;
}
Expand Down
24 changes: 23 additions & 1 deletion client/src/app/ui/directives/perms/oml-perms.directive.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ type TestConditionalType = {
<div *osOmlPerms="permission; or: conditionals.or" id="or"></div>
<div *osOmlPerms="permission; and: conditionals.and" id="and"></div>
<div *osOmlPerms="permission; complement: conditionals.complement" id="complement"></div>
<div *osOmlPerms="permission; allowCommitteeAdmin: true" id="committee"></div>
<ng-container *osOmlPerms="permission; then thenTemplate; else elseTemplate"></ng-container>
<ng-template #thenTemplate>
<div id="then"></div>
Expand All @@ -43,15 +44,21 @@ class MockOperatorService {

private _operatorUpdatedSubject = new Subject<void>();
private _permList: OML[] = [];
private _isCommitteeAdmin = true;

public hasOrganizationPermissions(...checkPerms: OML[]): boolean {
return checkPerms.some(perm => this._permList.includes(perm));
}

public changeOperatorPermsForTest(newPermList: OML[]): void {
public changeOperatorPermsForTest(newPermList: OML[], isCommitteeAdmin: boolean = this._isCommitteeAdmin): void {
this._permList = newPermList;
this._isCommitteeAdmin = isCommitteeAdmin;
this._operatorUpdatedSubject.next();
}

public isAnyCommitteeAdmin(): boolean {
return this._isCommitteeAdmin;
}
}

describe(`OmlPermsDirective`, () => {
Expand Down Expand Up @@ -129,4 +136,19 @@ describe(`OmlPermsDirective`, () => {
expect(getElement(`#else`)).toBeFalsy();
expect(getElement(`#then`)).toBeTruthy();
});

it(`check if allowCommitteeAdmin works`, async () => {
operatorService.changeOperatorPermsForTest([OML.can_manage_organization]);
update();
expect(getElement(`#committee`)).toBeTruthy();
operatorService.changeOperatorPermsForTest([OML.can_manage_organization], true);
update();
expect(getElement(`#committee`)).toBeTruthy();
operatorService.changeOperatorPermsForTest([]);
update();
expect(getElement(`#committee`)).toBeTruthy();
operatorService.changeOperatorPermsForTest([], false);
update();
expect(getElement(`#committee`)).toBeFalsy();
});
});

0 comments on commit 980581b

Please sign in to comment.