From 23368655b0d8258db3ea009a35f61f9f7df1ea78 Mon Sep 17 00:00:00 2001
From: Asitha de Silva
Date: Mon, 8 Sep 2025 10:05:47 -0700
Subject: [PATCH 1/2] feat(permissions): implement role-based permission system
- Add AccessCheckService for backend permission validation
- Integrate permission checks in project, committee, and meeting services
- Add writer/organizer fields to shared interfaces
- Implement conditional UI rendering based on user permissions
- Hide action buttons for users without write access
- Add Zoom logo for meeting platform display
Generated with [Claude Code](https://claude.ai/code)
Signed-off-by: Asitha de Silva
---
README.md | 30 ++-
apps/lfx-pcc/.env.example | 2 +-
apps/lfx-pcc/public/images/zoom-logo.svg | 8 +
apps/lfx-pcc/src/app/app.component.scss | 8 +
.../modules/meeting/meeting.component.html | 9 +-
.../committee-dashboard.component.html | 18 +-
.../committee-dashboard.component.ts | 12 --
.../committee-members.component.html | 33 ++-
.../committee-table.component.html | 45 +++--
.../committee-table.component.ts | 42 ++--
.../project-dashboard/project.component.html | 18 +-
.../project-dashboard/project.component.ts | 34 ++--
.../meeting-card/meeting-card.component.html | 19 +-
.../meeting-card/meeting-card.component.ts | 55 ++---
.../meeting-manage.component.html | 20 +-
.../meeting-manage.component.ts | 32 ++-
.../meeting-registrants.component.ts | 2 +-
.../meeting-dashboard.component.html | 12 +-
.../meeting-dashboard.component.ts | 22 +-
.../app/shared/services/meeting.service.ts | 3 +-
.../server/services/access-check.service.ts | 189 ++++++++++++++++++
.../src/server/services/committee.service.ts | 13 +-
.../src/server/services/meeting.service.ts | 13 +-
.../src/server/services/project.service.ts | 13 +-
.../src/interfaces/access-check.interface.ts | 36 ++++
.../src/interfaces/committee.interface.ts | 2 +
packages/shared/src/interfaces/index.ts | 3 +
.../src/interfaces/meeting.interface.ts | 6 +-
.../src/interfaces/project.interface.ts | 2 +
29 files changed, 521 insertions(+), 180 deletions(-)
create mode 100644 apps/lfx-pcc/public/images/zoom-logo.svg
create mode 100644 apps/lfx-pcc/src/server/services/access-check.service.ts
create mode 100644 packages/shared/src/interfaces/access-check.interface.ts
diff --git a/README.md b/README.md
index edfbf178..f063c249 100644
--- a/README.md
+++ b/README.md
@@ -60,19 +60,43 @@ of conduct, development process, and how to submit pull requests.
2. **Configure required environment variables:**
**Auth0 Configuration:**
- - Get the Auth0 Application values from 1Password
- Set `PCC_AUTH0_CLIENT_ID` and `PCC_AUTH0_CLIENT_SECRET`
+ - Local Development: The default client ID is `lfx` and you can get the client secret from the k8s via `k get secrets authelia-clients -n lfx -o jsonpath='{.data.lfx}' | base64 --decode`
- Update `PCC_AUTH0_ISSUER_BASE_URL` with your Auth0 domain
+ - Local Development: `https://auth.k8s.orb.local`
- Configure `PCC_AUTH0_AUDIENCE` for your API
+ - Local Development: `http://lfx-api.k8s.orb.local/`
+ - Set `PCC_AUTH0_SECRET` to a sufficiently long random string (32+ characters)
+ - Generate a random 32 characters long string
+
+ **M2M (Machine-to-Machine) Authentication:**
+ - Set `M2M_AUTH_CLIENT_ID` and `M2M_AUTH_CLIENT_SECRET` for server-side API calls
+ - Configure `M2M_AUTH_ISSUER_BASE_URL` (typically same as Auth0 base URL)
+ - Set `M2M_AUTH_AUDIENCE` to match your API audience
**Supabase Configuration:**
- Create a project in [Supabase](https://supabase.com)
- Get your project URL and anon key from Project Settings → API
- Set `SUPABASE_URL` and `POSTGRES_API_KEY`
+ - Configure `SUPABASE_STORAGE_BUCKET` for file storage
**Microservice Configuration:**
- - Set `QUERY_SERVICE_URL` to your query service endpoint
- - Provide a valid JWT token in `QUERY_SERVICE_TOKEN`
+ - Set `LFX_V2_SERVICE` to your query service endpoint
+ - Local Development: `http://lfx-api.k8s.orb.local`
+
+ **AI Service Configuration (Optional):**
+ - Set `AI_PROXY_URL` to your LiteLLM proxy endpoint for meeting agenda generation
+ - Provide a valid API key in `AI_API_KEY`
+
+ **NATS Configuration:**
+ - Set `NATS_URL` for internal messaging system (typically in Kubernetes environments)
+ - Local Development: `nats://lfx-platform-nats.lfx.svc.cluster.local:4222`
+
+ **Testing Configuration (Optional):**
+ - Set `TEST_USERNAME` and `TEST_PASSWORD` for automated E2E testing
+
+ **Local Development:**
+ - Set `NODE_TLS_REJECT_UNAUTHORIZED=0` when using Authelia for local authentication
#### Install and Run
diff --git a/apps/lfx-pcc/.env.example b/apps/lfx-pcc/.env.example
index 33dd9bb2..fb5b92a5 100644
--- a/apps/lfx-pcc/.env.example
+++ b/apps/lfx-pcc/.env.example
@@ -3,7 +3,7 @@ ENV=development
PCC_BASE_URL=http://localhost:4200
LOG_LEVEL=info
-# Auth0 Authentication Configuration
+# Auth0/Authelia Authentication Configuration
# Get these values from your Auth0 dashboard
PCC_AUTH0_CLIENT_ID=your-auth0-client-id
PCC_AUTH0_CLIENT_SECRET=your-auth0-client-secret
diff --git a/apps/lfx-pcc/public/images/zoom-logo.svg b/apps/lfx-pcc/public/images/zoom-logo.svg
new file mode 100644
index 00000000..0ce345ba
--- /dev/null
+++ b/apps/lfx-pcc/public/images/zoom-logo.svg
@@ -0,0 +1,8 @@
+
diff --git a/apps/lfx-pcc/src/app/app.component.scss b/apps/lfx-pcc/src/app/app.component.scss
index 1527b03f..84cd62ce 100644
--- a/apps/lfx-pcc/src/app/app.component.scss
+++ b/apps/lfx-pcc/src/app/app.component.scss
@@ -102,4 +102,12 @@
}
}
}
+
+ .p-menu {
+ .p-menu-item-link {
+ .p-menu-item-icon {
+ @apply w-5;
+ }
+ }
+ }
}
diff --git a/apps/lfx-pcc/src/app/modules/meeting/meeting.component.html b/apps/lfx-pcc/src/app/modules/meeting/meeting.component.html
index 20047513..0906ed82 100644
--- a/apps/lfx-pcc/src/app/modules/meeting/meeting.component.html
+++ b/apps/lfx-pcc/src/app/modules/meeting/meeting.component.html
@@ -129,8 +129,9 @@
Platform
-
Zoom Meeting
- Link will be sent via email
+
+

+
@@ -260,7 +261,9 @@ Enter your information
Meeting invites and updates will be sent to your email
-
Join Meeting
+
Join Meeting
diff --git a/apps/lfx-pcc/src/app/modules/project/committees/committee-dashboard/committee-dashboard.component.html b/apps/lfx-pcc/src/app/modules/project/committees/committee-dashboard/committee-dashboard.component.html
index f7097dfe..9a9cfb95 100644
--- a/apps/lfx-pcc/src/app/modules/project/committees/committee-dashboard/committee-dashboard.component.html
+++ b/apps/lfx-pcc/src/app/modules/project/committees/committee-dashboard/committee-dashboard.component.html
@@ -20,14 +20,16 @@ Committee Management
Manage committees, their settings, and organizational structure. Sub-committees are indicated with indentation.
-
-
+ @if (project()?.writer) {
+
+
+ }
diff --git a/apps/lfx-pcc/src/app/modules/project/committees/committee-dashboard/committee-dashboard.component.ts b/apps/lfx-pcc/src/app/modules/project/committees/committee-dashboard/committee-dashboard.component.ts
index 7dd7a660..58bf13b9 100644
--- a/apps/lfx-pcc/src/app/modules/project/committees/committee-dashboard/committee-dashboard.component.ts
+++ b/apps/lfx-pcc/src/app/modules/project/committees/committee-dashboard/committee-dashboard.component.ts
@@ -65,7 +65,6 @@ export class CommitteeDashboardComponent {
public votingStatusOptions: Signal<{ label: string; value: string | null }[]>;
public filteredCommittees: Signal;
public totalRecords: Signal;
- public menuItems: MenuItem[];
public actionMenuItems: MenuItem[];
public refresh: BehaviorSubject;
private searchTerm: Signal;
@@ -94,7 +93,6 @@ export class CommitteeDashboardComponent {
this.votingStatusOptions = this.initializeVotingStatusOptions();
this.filteredCommittees = this.initializeFilteredCommittees();
this.totalRecords = this.initializeTotalRecords();
- this.menuItems = this.initializeMenuItems();
this.actionMenuItems = this.initializeActionMenuItems();
}
@@ -341,16 +339,6 @@ export class CommitteeDashboardComponent {
return computed(() => this.filteredCommittees().length);
}
- private initializeMenuItems(): MenuItem[] {
- return [
- {
- label: 'Create Committee',
- icon: 'fa-light fa-users-medical text-sm',
- command: () => this.openCreateDialog(),
- },
- ];
- }
-
private initializeActionMenuItems(): MenuItem[] {
return [
{
diff --git a/apps/lfx-pcc/src/app/modules/project/committees/components/committee-members/committee-members.component.html b/apps/lfx-pcc/src/app/modules/project/committees/components/committee-members/committee-members.component.html
index fbab5c9a..e7e058a6 100644
--- a/apps/lfx-pcc/src/app/modules/project/committees/components/committee-members/committee-members.component.html
+++ b/apps/lfx-pcc/src/app/modules/project/committees/components/committee-members/committee-members.component.html
@@ -7,7 +7,9 @@
Committee Members
-
+ @if (committee()?.writer) {
+
+ }
@@ -136,14 +138,27 @@ Committee Members
}
-
-
+ @if (committee()?.writer) {
+
+
+ } @else {
+
+
+ }
|
diff --git a/apps/lfx-pcc/src/app/modules/project/committees/components/committee-table/committee-table.component.html b/apps/lfx-pcc/src/app/modules/project/committees/components/committee-table/committee-table.component.html
index cab4a394..08cc4f80 100644
--- a/apps/lfx-pcc/src/app/modules/project/committees/components/committee-table/committee-table.component.html
+++ b/apps/lfx-pcc/src/app/modules/project/committees/components/committee-table/committee-table.component.html
@@ -113,15 +113,17 @@
-
+ @if (committee?.writer) {
+
+ }
-
+ [attr.data-testid]="'committee-members-' + committee.uid"
+ pTooltip="View members" />
+ @if (committee?.writer) {
+
+ }
|
-
+
diff --git a/apps/lfx-pcc/src/app/modules/project/committees/components/committee-table/committee-table.component.ts b/apps/lfx-pcc/src/app/modules/project/committees/components/committee-table/committee-table.component.ts
index 8a9cf9e4..9469268c 100644
--- a/apps/lfx-pcc/src/app/modules/project/committees/components/committee-table/committee-table.component.ts
+++ b/apps/lfx-pcc/src/app/modules/project/committees/components/committee-table/committee-table.component.ts
@@ -11,11 +11,12 @@ import { Committee } from '@lfx-pcc/shared/interfaces';
import { CommitteeTypeColorPipe } from '@pipes/committee-type-colors.pipe';
import { ProjectService } from '@services/project.service';
import { MenuItem } from 'primeng/api';
+import { TooltipModule } from 'primeng/tooltip';
@Component({
selector: 'lfx-committee-table',
standalone: true,
- imports: [CommonModule, RouterLink, ButtonComponent, MenuComponent, TableComponent, CommitteeTypeColorPipe],
+ imports: [CommonModule, RouterLink, ButtonComponent, MenuComponent, TableComponent, CommitteeTypeColorPipe, TooltipModule],
templateUrl: './committee-table.component.html',
})
export class CommitteeTableComponent {
@@ -32,7 +33,7 @@ export class CommitteeTableComponent {
public selectedCommittee: WritableSignal = signal(null);
// Menu items for committee actions
- public committeeActionMenuItems: MenuItem[] = this.initializeCommitteeActionMenuItems();
+ public committeeActionMenuItems: Signal