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

[Fix] Project Manager Or Permission Validation #8794

Open
wants to merge 31 commits into
base: develop
Choose a base branch
from

Conversation

samuelmbabhazi
Copy link
Contributor

@samuelmbabhazi samuelmbabhazi commented Feb 19, 2025

PR

Please note: we will close your PR without comment if you do not check the boxes above and provide ALL requested information.


Summary by CodeRabbit

  • New Features

    • Enhanced project and team management with advanced role and permission checks.
    • Automatically assigns a default manager in team settings when needed.
    • Introduced additional route safeguards to ensure that only authorized users can access and modify project details.
    • New methods for checking project manager status and user permissions have been added.
  • Refactor

    • Streamlined project lookup and integration error handling for a more consistent and secure user experience.

@samuelmbabhazi samuelmbabhazi self-assigned this Feb 19, 2025
Copy link
Contributor

coderabbitai bot commented Feb 19, 2025

Walkthrough

This pull request introduces several permission and role management enhancements in both frontend and backend modules. On the frontend, new methods and guards enable granular user permission checks during project selection and team management. Backend API endpoints and services are updated to support managerial status evaluations and include refined error handling. Core guard classes have been refactored to centralize access logic. Routing has also been modified to incorporate the new guard for project editing.

Changes

File(s) Change Summary
apps/gauzy/.../project-list/list.component.ts
apps/gauzy/.../teams/teams-mutation/teams-mutation.component.ts
apps/gauzy/.../project-edit/edit.component.ts
In ProjectListComponent, added isManagerOfProject and _hasAllPermissions methods and updated selectProject to manage CAN_MANAGE_PROJECT dynamically. In TeamsMutationComponent, added setDefaultManagerIfEmployee and updated form type. ProjectEditMutationComponent now uses a streamlined observable pipeline in _getEditProject.
apps/gauzy/.../projects/guards/project-manager.guard.ts
apps/gauzy/.../projects/guards/project-manager-or-permission.guard.ts
apps/gauzy/.../projects/projects-routing.module.ts
Introduced new route guards to check if a user is a project manager or meets the required permissions. Updated the routing configuration to utilize these new guards and added route data (e.g., allowMissingIntegration).
packages/core/.../organization-project/organization-project.controller.ts
packages/core/.../organization-project/organization-project.service.ts
Added an endpoint isProjectManager and the service method isManagerOfProject to evaluate managerial status. Renamed methods (findProjectsByEmployee, updateProjectByEmployee) and updated API documentation and response codes.
packages/ui-core/core/.../organization/organization-projects.service.ts Added the isManagerOfProject method to make a GET request for checking if an employee is a project manager.
packages/ui-core/core/.../resolvers/integration.resolver.ts Modified error handling logic by checking the allowMissingIntegration flag to conditionally return an observable result instead of redirecting immediately.
packages/core/.../shared/guards/base.guard.ts
packages/core/.../shared/guards/permission.guard.ts
packages/core/.../organization-project/guards/project-manager-or-permission.guard.ts
Introduced an abstract BaseGuard with helper methods. Updated PermissionGuard to extend BaseGuard and modified access levels. Added a backend ProjectManagerOrPermissionGuard to combine manager and permission checks with logging and error handling.

Sequence Diagram(s)

sequenceDiagram
    participant U as User
    participant PLC as ProjectListComponent
    participant PS as NgxPermissionsService
    participant OPS as OrganizationProjectsService

    U->>PLC: selectProject(project)
    PLC->>PLC: isManagerOfProject(project)
    alt User is Manager
        PLC->>PS: addPermission(CAN_MANAGE_PROJECT)
    else Not Manager
        PLC->>PLC: _hasAllPermissions(permissions)
    end
    PLC->>PLC: Update component state
Loading
sequenceDiagram
    participant R as Router
    participant PMGuard as ProjectManagerOrPermissionGuard
    participant OPS as OrganizationProjectService
    participant PGuard as PermissionGuard

    R->>PMGuard: canActivate(route, state)
    PMGuard->>OPS: isManagerOfProject(projectId, employeeId)
    alt User is Manager
        PMGuard-->>R: Allow access
    else Not Manager
        PMGuard->>PGuard: Check additional permissions
        PGuard-->>PMGuard: Return permission result
        PMGuard-->>R: Allow or deny access based on result
    end
Loading

Possibly related PRs

Suggested reviewers

  • rahul-rocket
  • evereq

Poem

I'm a rabbit coding day and night,
Hopping through permissions with sheer delight.
New guards and methods pave the way,
For projects and teams to smoothly play.
With checks and hops, our code is tight —
A carrot-sized cheer for bugs taking flight!
🐰✨


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between adb3b9d and 2cece68.

📒 Files selected for processing (1)
  • packages/ui-core/shared/src/lib/project/project-mutation/project-mutation.component.html (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: Codacy Static Code Analysis
  • GitHub Check: build
  • GitHub Check: test
🔇 Additional comments (1)
packages/ui-core/shared/src/lib/project/project-mutation/project-mutation.component.html (1)

113-114: Update Translation Keys for Manager Multi-select Component

The [placeholder] and [label] attributes now use the FORM.LABELS.ADD_REMOVE_MANAGERS key instead of the previous FORM.PLACEHOLDERS.ADD_REMOVE_MANAGERS. This change improves clarity by aligning the displayed text with UI label semantics. Please verify that the new translation keys exist in the translation files to ensure consistent UI rendering.


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@samuelmbabhazi samuelmbabhazi marked this pull request as ready for review February 19, 2025 17:11
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (2)
apps/gauzy/src/app/pages/teams/teams-mutation/teams-mutation.component.ts (1)

75-86: LGTM! Consider adding error handling.

The implementation of initializeManager() is clean and well-documented. It effectively sets the current user as a manager when they are an employee.

Consider adding error handling for the form operations:

 initializeManager() {
   const { employee } = this.store.user;
   if (employee) {
+    try {
       this.form.patchValue({
         managerIds: [employee.id]
       });
       this.form.get('managerIds').updateValueAndValidity();
+    } catch (error) {
+      console.error('Error initializing manager:', error);
+    }
   }
 }
apps/gauzy/src/app/pages/projects/components/project-list/list.component.ts (1)

547-564: Consider adding error handling for permission operations.

The permission management in selectProject is well-structured but could benefit from error handling around permission operations.

Consider wrapping the permission operations in a try-catch block:

 if (isSelected && this.selectedProject) {
   const isManager = this.isManagerOfProject(this.selectedProject);
   const hasAllPermissions = this.hasAllPermissions([
     PermissionsEnum.ALL_ORG_EDIT,
     PermissionsEnum.ORG_PROJECT_EDIT,
     PermissionsEnum.ORG_PROJECT_DELETE
   ]);

+  try {
     if (isManager || hasAllPermissions) {
       this._permissionsService.addPermission('CAN_MANAGE_PROJECT');
     } else {
       this._permissionsService.removePermission('CAN_MANAGE_PROJECT');
     }
+  } catch (error) {
+    console.error('Error managing project permissions:', error);
+  }
 }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ab4a297 and 4879211.

📒 Files selected for processing (3)
  • apps/gauzy/src/app/pages/projects/components/project-list/list.component.html (3 hunks)
  • apps/gauzy/src/app/pages/projects/components/project-list/list.component.ts (5 hunks)
  • apps/gauzy/src/app/pages/teams/teams-mutation/teams-mutation.component.ts (2 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: Codacy Static Code Analysis
  • GitHub Check: test
  • GitHub Check: build
🔇 Additional comments (3)
apps/gauzy/src/app/pages/projects/components/project-list/list.component.ts (2)

284-286: LGTM! The manager check is accurate and efficient.

The isManagerOfProject method correctly verifies if the current user is a manager of the project by checking their user ID against the project's manager list.


614-616: LGTM! The permission check is well-implemented.

The hasAllPermissions method efficiently verifies multiple permissions using every to ensure all required permissions are present.

apps/gauzy/src/app/pages/projects/components/project-list/list.component.html (1)

67-108: LGTM! Permission checks are well-consolidated.

The template changes effectively simplify permission management by:

  • Reducing duplication in permission checks
  • Using a single, consistent 'CAN_MANAGE_PROJECT' permission
  • Improving template readability

Copy link

nx-cloud bot commented Feb 19, 2025

View your CI Pipeline Execution ↗ for commit 2cece68.

Command Status Duration Result
nx build gauzy -c=production --prod --verbose ✅ Succeeded 3m 50s View ↗
nx build api -c=production --prod ✅ Succeeded 1m 6s View ↗
nx build desktop-ui-lib --configuration=develop... ✅ Succeeded 29s View ↗
nx build plugin-integration-wakatime ✅ Succeeded <1s View ↗
nx build desktop-lib ✅ Succeeded <1s View ↗
nx build desktop-window ✅ Succeeded <1s View ↗
nx build desktop-core ✅ Succeeded <1s View ↗
nx build plugin-videos ✅ Succeeded <1s View ↗
Additional runs (35) ✅ Succeeded ... View ↗

☁️ Nx Cloud last updated this comment at 2025-02-28 07:50:41 UTC

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
apps/gauzy/src/app/pages/projects/components/project-list/list.component.ts (1)

253-253: Remove debug console.log statement.

Remove the console.log statement as it's not needed in production code.

-				console.log(project);
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4879211 and 8025ad6.

📒 Files selected for processing (1)
  • apps/gauzy/src/app/pages/projects/components/project-list/list.component.ts (7 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: build
  • GitHub Check: test
🔇 Additional comments (3)
apps/gauzy/src/app/pages/projects/components/project-list/list.component.ts (3)

285-287: LGTM! Well-structured method for checking project manager status.

The method efficiently checks if the current user is a manager of the project by comparing the user ID with the project member's employee user ID.


365-365: LGTM! Improved property access path.

The change correctly accesses the employee ID through the user's employee property.


619-621: LGTM! Clean implementation of permission check.

The method efficiently checks if the user has all the required permissions using Array.every().

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (6)
apps/gauzy/src/app/pages/projects/guards/project-manager-permission.guard.ts (1)

36-46: Consider enhancing error handling in the permissions check flow.

While the current implementation handles the basic flow well, consider adding error handling for the permissions guard check similar to what's done for the manager guard.

 this._permissionsGuard.canActivate(route, state).subscribe(
   (hasPermissions) => {
     if (hasPermissions) {
       observer.next(true);
     } else {
       this._router.navigate([route.data['permissions'].redirectTo || '/pages/dashboard']);
       observer.next(false);
     }
     observer.complete();
+  },
+  (error) => {
+    console.error('Error checking permissions:', error);
+    this._router.navigate(['/pages/dashboard']);
+    observer.next(false);
+    observer.complete();
   }
 );
apps/gauzy/src/app/pages/projects/guards/project-manager.guard.ts (1)

29-35: Enhance type safety in permission check condition.

The current implementation might be prone to type-related issues. Consider using explicit type checks.

-if (
-  !this._store.user?.employee &&
-  this._store.hasAllPermissions(PermissionsEnum.ORG_PROJECT_EDIT, PermissionsEnum.ORG_PROJECT_DELETE)
-) {
+if (
+  this._store.user !== null &&
+  !this._store.user.employee &&
+  this._store.hasAllPermissions(PermissionsEnum.ORG_PROJECT_EDIT, PermissionsEnum.ORG_PROJECT_DELETE)
+) {
packages/ui-core/core/src/lib/services/organization/organization-projects.service.ts (1)

46-48: Consider adding error handling guidance for consumers.

While the implementation is clean, consider adding error handling documentation for service consumers.

 isManagerOfProject(projectId: ID, employeeId: ID): Observable<boolean> {
-  return this._http.get<boolean>(`${this.API_URL}/${projectId}/is-manager/${employeeId}`);
+  return this._http.get<boolean>(`${this.API_URL}/${projectId}/is-manager/${employeeId}`).pipe(
+    catchError((error) => {
+      console.error('Error checking project manager status:', error);
+      return of(false);
+    })
+  );
 }
packages/core/src/lib/organization-project/organization-project.controller.ts (1)

279-297: Consider enhancing method name and error handling.

The method implementation is good, but could be improved in a few areas:

  1. The method name isManager could be more descriptive, e.g., isEmployeeProjectManager.
  2. The API operation summary could include more details about the parameters.
  3. Consider adding error handling for cases where the project or employee doesn't exist.
-	@ApiOperation({
-		summary: 'Check if an employee is a manager of a specific project.'
-	})
+	@ApiOperation({
+		summary: 'Check if an employee is a manager of a specific project.',
+		description: 'Verifies whether the specified employee (by employeeId) has manager privileges for the given project (by projectId).'
+	})
-	async isManager(
+	async isEmployeeProjectManager(
 		@Param('projectId', UUIDValidationPipe) projectId: ID,
 		@Param('employeeId', UUIDValidationPipe) employeeId: ID
 	): Promise<boolean> {
+		// Verify that both project and employee exist
+		const [project, employee] = await Promise.all([
+			this.organizationProjectService.findOneByIdString(projectId),
+			this.employeeService.findOneByIdString(employeeId)
+		]);
+
+		if (!project || !employee) {
+			throw new NotFoundException('Project or employee not found');
+		}
+
 		return await this.organizationProjectService.isManagerOfProject(projectId, employeeId);
 	}
apps/gauzy/src/app/pages/projects/components/project-list/list.component.ts (1)

548-569: Consider caching permission check results.

The permission checks in the selectProject method are well-structured but could be optimized by caching the results to avoid repeated checks.

+	private cachedPermissions = new Map<string, boolean>();
+
+	private getCachedPermissionResult(key: string, checkFn: () => boolean): boolean {
+		if (!this.cachedPermissions.has(key)) {
+			this.cachedPermissions.set(key, checkFn());
+		}
+		return this.cachedPermissions.get(key);
+	}
+
 	async selectProject({ isSelected, data }): Promise<void> {
 		try {
 			this.disableButton = !isSelected;
 			this.selectedProject = isSelected ? data : null;
 
 			if (isSelected && this.selectedProject) {
-				const isManager = this.isManagerOfProject(this.selectedProject);
+				const isManager = this.getCachedPermissionResult(
+					`manager_${this.selectedProject.id}`,
+					() => this.isManagerOfProject(this.selectedProject)
+				);
 
-				const hasAllPermissions = this.hasAllPermissions([
-					PermissionsEnum.ALL_ORG_EDIT,
-					PermissionsEnum.ORG_PROJECT_EDIT,
-					PermissionsEnum.ORG_PROJECT_DELETE
-				]);
+				const hasAllPermissions = this.getCachedPermissionResult(
+					'all_permissions',
+					() => this.hasAllPermissions([
+						PermissionsEnum.ALL_ORG_EDIT,
+						PermissionsEnum.ORG_PROJECT_EDIT,
+						PermissionsEnum.ORG_PROJECT_DELETE
+					])
+				);
packages/core/src/lib/organization-project/organization-project.service.ts (1)

656-673: Consider enhancing error handling and query optimization.

The isManagerOfProject method is well-implemented but could benefit from:

  1. Error handling for database query failures.
  2. Query optimization by selecting only necessary columns.
 	async isManagerOfProject(projectId: ID, employeeId: ID): Promise<boolean> {
+		try {
 			const project = await this.typeOrmRepository
 				.createQueryBuilder('project')
+				.select('project.id')
 				.innerJoin('project.members', 'members')
 				.where('project.id = :projectId', { projectId })
 				.andWhere('members.employeeId = :employeeId', { employeeId })
 				.andWhere('members.isManager = true')
 				.getOne();
 
 			return !!project;
+		} catch (error) {
+			console.error('Error checking project manager status:', error);
+			throw new HttpException(
+				'Failed to check project manager status',
+				HttpStatus.INTERNAL_SERVER_ERROR
+			);
+		}
 	}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8025ad6 and 56346d7.

📒 Files selected for processing (7)
  • apps/gauzy/src/app/pages/projects/components/project-list/list.component.ts (7 hunks)
  • apps/gauzy/src/app/pages/projects/guards/project-manager-permission.guard.ts (1 hunks)
  • apps/gauzy/src/app/pages/projects/guards/project-manager.guard.ts (1 hunks)
  • apps/gauzy/src/app/pages/projects/projects-routing.module.ts (2 hunks)
  • packages/core/src/lib/organization-project/organization-project.controller.ts (1 hunks)
  • packages/core/src/lib/organization-project/organization-project.service.ts (1 hunks)
  • packages/ui-core/core/src/lib/services/organization/organization-projects.service.ts (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: Codacy Static Code Analysis
  • GitHub Check: test
  • GitHub Check: build
🔇 Additional comments (5)
apps/gauzy/src/app/pages/projects/guards/project-manager-permission.guard.ts (1)

27-50: LGTM! Well-structured permission validation flow.

The guard effectively implements a hierarchical permission check, first verifying project manager status before falling back to general permissions.

apps/gauzy/src/app/pages/projects/guards/project-manager.guard.ts (1)

46-61: LGTM! Well-implemented permission validation with proper error handling.

The guard effectively uses RxJS operators for handling the async flow and error cases.

apps/gauzy/src/app/pages/projects/projects-routing.module.ts (1)

65-70: LGTM! Appropriate guard replacement with maintained permission requirements.

The routing configuration correctly implements the new ProjectManagerPermissionGuard while maintaining the existing permission requirements.

apps/gauzy/src/app/pages/projects/components/project-list/list.component.ts (2)

285-287: LGTM! Well-structured method for checking project manager status.

The isManagerOfProject method is well-implemented, using a clean and efficient approach to check if the current user is a manager of the project.


619-621: LGTM! Efficient permission check implementation.

The hasAllPermissions method is well-implemented, using the every array method for efficient permission checking.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
packages/core/src/lib/organization-project/organization-project.module.ts (1)

15-31: Consider documenting the guard's purpose and usage.

To improve maintainability, consider adding a comment block above the @Module decorator explaining:

  • The purpose of the PermissionGuard
  • How it interacts with other guards (ManagerOrPermissionGuard, ProjectManagerPermissionGuard)
  • The permission validation strategy
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 56346d7 and b89fc73.

📒 Files selected for processing (5)
  • packages/core/src/lib/organization-project/decorators/manager-or-permission.decorator.ts (1 hunks)
  • packages/core/src/lib/organization-project/guards/manager-or-permission.guard.ts (1 hunks)
  • packages/core/src/lib/organization-project/organization-project.controller.ts (2 hunks)
  • packages/core/src/lib/organization-project/organization-project.module.ts (2 hunks)
  • packages/core/src/lib/organization-project/organization-project.service.ts (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/core/src/lib/organization-project/organization-project.service.ts
⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: build
  • GitHub Check: test
🔇 Additional comments (5)
packages/core/src/lib/organization-project/organization-project.module.ts (1)

15-15: LGTM! Addition of PermissionGuard enhances access control.

The integration of PermissionGuard aligns well with the PR's objective of fixing project manager permission validation logic.

Also applies to: 31-31

packages/core/src/lib/organization-project/decorators/manager-or-permission.decorator.ts (1)

1-7: LGTM! Well-structured decorator implementation.

The decorator follows NestJS best practices by combining permission metadata and guard functionality using applyDecorators. The implementation is concise and follows the single responsibility principle.

packages/core/src/lib/organization-project/guards/manager-or-permission.guard.ts (1)

15-37: LGTM! Well-structured guard implementation.

The guard follows NestJS best practices:

  • Properly implements the CanActivate interface
  • Includes informative logging
  • Has a clear fallback mechanism to the permission guard
  • Handles edge cases when employeeId or projectId are missing
packages/core/src/lib/organization-project/organization-project.controller.ts (2)

45-49: LGTM! Good replacement of permission guard.

The switch to ManagerOrPermissionGuard while maintaining TenantPermissionGuard provides better access control by checking both project manager status and permissions.


281-299: LGTM! Well-implemented endpoint for checking manager status.

The new endpoint is:

  • Well-documented with OpenAPI decorators
  • Uses proper validation for UUID parameters
  • Has clear and focused implementation

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (2)
packages/ui-core/core/src/lib/resolvers/integration.resolver.ts (1)

44-51: Enhance error logging for better debugging.

While the error handling is improved, consider adding more context to the error messages:

-				console.warn('Access to integration is forbidden (403), continuing without integration data.');
+				console.warn(`Access to integration "${name}" is forbidden (403). User: ${_store.user?.id}. Continuing without integration data.`);
-				console.error('Unexpected error in IntegrationResolver:', error);
+				console.error('Unexpected error in IntegrationResolver:', { 
+					error, 
+					integration: name, 
+					organizationId, 
+					tenantId 
+				});
apps/gauzy/src/app/pages/projects/components/project-list/list.component.ts (1)

547-568: Add error handling for permission operations.

The dynamic permission management logic should include error handling for permission operations.

 if (!hasAllPermissions) {
   const permissions = [PermissionsEnum.ORG_PROJECT_EDIT, PermissionsEnum.ORG_PROJECT_DELETE];

-  permissions.forEach((permission) =>
-    isManager
-      ? this._permissionsService.addPermission(permission)
-      : this._permissionsService.removePermission(permission)
-  );
+  try {
+    await Promise.all(permissions.map(async (permission) => {
+      if (isManager) {
+        await this._permissionsService.addPermission(permission);
+      } else {
+        await this._permissionsService.removePermission(permission);
+      }
+    }));
+  } catch (error) {
+    console.error('Error updating permissions:', error);
+    this._errorHandlingService.handleError(error);
+  }
 }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b89fc73 and 0b72754.

📒 Files selected for processing (6)
  • apps/gauzy/src/app/pages/projects/components/project-edit/edit.component.ts (1 hunks)
  • apps/gauzy/src/app/pages/projects/components/project-list/list.component.ts (6 hunks)
  • apps/gauzy/src/app/pages/projects/guards/project-manager.guard.ts (1 hunks)
  • packages/core/src/lib/organization-project/organization-project.controller.ts (3 hunks)
  • packages/core/src/lib/organization-project/organization-project.service.ts (3 hunks)
  • packages/ui-core/core/src/lib/resolvers/integration.resolver.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/core/src/lib/organization-project/organization-project.service.ts
  • apps/gauzy/src/app/pages/projects/guards/project-manager.guard.ts
⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: Codacy Static Code Analysis
  • GitHub Check: build
  • GitHub Check: test
🔇 Additional comments (4)
apps/gauzy/src/app/pages/projects/components/project-edit/edit.component.ts (1)

70-73: Remove debug console.log statement.

Remove the debug console.log statement to avoid exposing debug information in production.

-				console.log(integration);
packages/core/src/lib/organization-project/organization-project.controller.ts (1)

281-299: Well-implemented manager check endpoint!

The implementation is secure with proper parameter validation and well-documented with API metadata. The method aligns well with the permission management changes across the codebase.

apps/gauzy/src/app/pages/projects/components/project-list/list.component.ts (2)

284-286: Well-implemented manager check!

The implementation efficiently checks if the current user is a manager of the project.


618-620: Clean permission check implementation!

The method efficiently checks for multiple permissions using Array.every().

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
apps/gauzy/src/app/pages/projects/guards/project-manager-permission.guard.ts (1)

41-41: Add type safety for route data access.

The direct access to route data properties could fail if the data structure is not as expected.

Consider adding an interface for the route data and using proper type guards:

interface ProjectRouteData {
  permissions?: {
    redirectTo?: string;
  };
}

// Then in the guard:
const routeData = route.data as ProjectRouteData;
const redirectPath = routeData?.permissions?.redirectTo || '/pages/dashboard';
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between fba42ba and 06b26dd.

📒 Files selected for processing (2)
  • apps/gauzy/src/app/pages/projects/guards/project-manager-permission.guard.ts (1 hunks)
  • packages/ui-core/core/src/lib/resolvers/integration.resolver.ts (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/ui-core/core/src/lib/resolvers/integration.resolver.ts
⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: test
  • GitHub Check: Codacy Static Code Analysis
  • GitHub Check: build
🔇 Additional comments (1)
apps/gauzy/src/app/pages/projects/guards/project-manager-permission.guard.ts (1)

1-15: Well-structured guard implementation!

The class follows Angular best practices with proper dependency injection, typing, and scoping.

@samuelmbabhazi
Copy link
Contributor Author

@rahul This PR includes modifications to the project permission logic.

The goal was to grant managers the necessary permissions to edit, delete, and manage a project. Since a manager, in the context of a project, team, or task, is not considered a standalone role but is instead identified by the isManager flag, I applied the following logic:

Frontend:

  • When selecting a project, the frontend checks whether the logged-in user is a manager of that project or has the required permissions to manage it.
  • If the user is a manager, their permissions are temporarily added on the frontend to allow them to perform certain actions.
  • For editing, if the user needs to be redirected to the edit page, I created a new guard that verifies whether they are a manager or, if not, checks if they have the necessary permissions.

Note: I had to modify the logic of the integration resolver because it blocked the redirection for editing. (Another way to do this?)

Backend:

  • I created a new guard for projects that combines two checks: verifying if the user is a project manager or, if not, confirming they have the required permissions.

CC:@evereq

@@ -41,17 +41,17 @@ export const IntegrationResolver: ResolveFn<Observable<IIntegrationTenant | bool

// Handle errors and redirect if an error occurs
return integration$.pipe(
catchError(() => {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@samuelmbabhazi Why you changed integration.resolver logic here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@samuelmbabhazi Why you changed integration.resolver logic here?

I change this logic since the employee does not have the necessary permissions to access it and this blocks the redirection for editing,
Here I allow him to access the project without the data coming from the integrations,

Here the question is whether the permissions for the integration are necessary and for our case this data is it really associated with a specific project

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@samuelmbabhazi That integration.resolver we used multiple places you completly changed the logic and make it work here, but broken everywhere we used it. So please revert it.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

image

After your chages, still having same issue.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (3)
packages/core/src/lib/organization-project/guards/manager-or-permission.guard.ts (3)

11-17: Replace console.log with a proper logging service.

The constructor properly extends PermissionGuard and passes the required dependencies to the parent class. However, consider replacing all console.log statements with a proper logging service such as NestJS's built-in Logger for better observability and control in production environments.


28-37: Add more robust validation for employeeId and projectId.

The current implementation checks if both employeeId and projectId exist, but doesn't validate their formats. Consider adding more robust validation to prevent potential issues with invalid IDs.

// Get user details from the request context
const { employeeId } = request.user;

// Check if the user is a project manager
-if (employeeId && projectId) {
+if (employeeId && projectId && isUUID(projectId)) {
	const isManager = await this._projectService.isManagerOfProject(projectId, employeeId);
	if (isManager) {
		console.log(`✅ User (employeeId: ${employeeId}) is manager of project ${projectId}, access granted.`);
		return true;
	}
}

You'll need to import isUUID from 'class-validator' or implement your own validation approach.


21-41: Replace console.log statements with proper logging.

Replace all console.log statements with a proper logging service, such as NestJS's built-in Logger. This will provide better control over log levels and formatting in different environments.

+import { Logger } from '@nestjs/common';

@Injectable()
export class ManagerOrPermissionGuard extends PermissionGuard implements CanActivate {
+	private readonly logger = new Logger(ManagerOrPermissionGuard.name);
	
	constructor(
		// ...existing constructor
	) {
		super(cacheManager, reflector, rolePermissionService);
	}

	async canActivate(context: ExecutionContext): Promise<boolean> {
-		console.log('✅ ManagerOrPermissionGuard canActivate called');
+		this.logger.debug('ManagerOrPermissionGuard canActivate called');

		// ...existing code

		if (isManager) {
-			console.log(`✅ User (employeeId: ${employeeId}) is manager of project ${projectId}, access granted.`);
+			this.logger.debug(`User (employeeId: ${employeeId}) is manager of project ${projectId}, access granted.`);
			return true;
		}

		// If the user is not a manager, delegate the check to PermissionGuard
-		console.log(`🔄 User is not a manager, deferring to PermissionGuard.`);
+		this.logger.debug(`User is not a manager, deferring to PermissionGuard.`);
		return super.canActivate(context);
	}
}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 725560f and ddfd123.

📒 Files selected for processing (4)
  • apps/gauzy/src/app/pages/projects/components/project-edit/edit.component.ts (1 hunks)
  • apps/gauzy/src/app/pages/projects/guards/project-manager-permission.guard.ts (1 hunks)
  • apps/gauzy/src/app/pages/teams/teams-mutation/teams-mutation.component.ts (1 hunks)
  • packages/core/src/lib/organization-project/guards/manager-or-permission.guard.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • apps/gauzy/src/app/pages/projects/components/project-edit/edit.component.ts
  • apps/gauzy/src/app/pages/teams/teams-mutation/teams-mutation.component.ts
  • apps/gauzy/src/app/pages/projects/guards/project-manager-permission.guard.ts
⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: build
  • GitHub Check: test
🔇 Additional comments (2)
packages/core/src/lib/organization-project/guards/manager-or-permission.guard.ts (2)

12-12: Remove unused CACHE_MANAGER parameter name.

The cache manager is injected but never directly used in this class's implementation. While it's passed to the parent class, consider removing the parameter name to avoid confusion.

-	@Inject(CACHE_MANAGER) cacheManager: Cache,
+	@Inject(CACHE_MANAGER) _: Cache,

20-42: Good implementation of the manager-first permission check approach.

The canActivate method correctly implements the PR objectives by first checking if a user is a project manager and granting access immediately if true, before falling back to standard permission checks. This is a clean implementation of the requirement.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (2)
apps/gauzy/src/app/pages/teams/teams-mutation/teams-mutation.component.ts (1)

87-105: Consider preserving existing managers.
The current implementation overwrites any existing manager IDs with just the current employee's ID. If multiple managers can exist, switch to an additive approach to avoid unintentionally discarding other managers.

Here is an example diff to preserve existing managers while adding the current employee:

- managerControl.patchValue([employee.id]);
+ const currentManagers = new Set(managerControl.value || []);
+ currentManagers.add(employee.id);
+ managerControl.patchValue([...currentManagers]);
packages/core/src/lib/organization-project/guards/project-manager-or-permission.guard.ts (1)

41-45: Consider upgrading console logs to a logger.
Use a standardized logger instead of console.log to ensure consistent formatting and log levels (info, warn, error) in production code.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 845146d and caae943.

📒 Files selected for processing (8)
  • apps/gauzy/src/app/pages/projects/components/project-list/list.component.ts (6 hunks)
  • apps/gauzy/src/app/pages/teams/teams-mutation/teams-mutation.component.ts (2 hunks)
  • packages/core/src/lib/organization-project/decorators/manager-or-permission.decorator.ts (1 hunks)
  • packages/core/src/lib/organization-project/guards/project-manager-or-permission.guard.ts (1 hunks)
  • packages/core/src/lib/organization-project/organization-project.controller.ts (9 hunks)
  • packages/core/src/lib/organization-project/organization-project.service.ts (6 hunks)
  • packages/core/src/lib/shared/guards/base.guard.ts (1 hunks)
  • packages/core/src/lib/shared/guards/permission.guard.ts (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/core/src/lib/organization-project/decorators/manager-or-permission.decorator.ts
⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: Codacy Static Code Analysis
  • GitHub Check: build
  • GitHub Check: test
🔇 Additional comments (16)
apps/gauzy/src/app/pages/teams/teams-mutation/teams-mutation.component.ts (2)

5-14: Imports look good.
No issues found with newly added imports for IEmployee, IOrganization, IImageAsset, IOrganizationProject, IOrganizationTeam, ITag, IUser, and PermissionsEnum.


82-85: Double-check permission-driven logic.
Currently, the default manager is set if the user lacks CHANGE_SELECTED_EMPLOYEE permission. Please confirm that this aligns with the intended business logic (i.e., if they cannot change the employee, they automatically become a manager).

packages/core/src/lib/shared/guards/base.guard.ts (1)

12-15: Placeholder authorization logic.
The guard is allowing all requests by default. Ensure that you override or implement actual checks in extending classes, or replace this placeholder to enforce real authorization rules.

packages/core/src/lib/organization-project/guards/project-manager-or-permission.guard.ts (1)

36-38: Fallback strategy is valid.
Gracefully falling back to PermissionGuard when employeeId or projectId is missing is a solid approach to avoid unexpected breakage.

apps/gauzy/src/app/pages/projects/components/project-list/list.component.ts (3)

283-294: Well-implemented manager check function

This function correctly checks if the current user is a manager of a given project by examining project members and comparing with the current user ID. The boolean return type and clear naming make the function purpose explicit.


555-577: Good implementation of dynamic permission handling

The permission management logic is well-structured. The code dynamically assigns or removes permissions based on whether the user is a project manager or has the necessary global permissions. This approach ensures managers can edit/delete projects even without explicit permissions.

However, consider adding a cleanup mechanism to remove temporary permissions when navigating away from the project context:

+// Add a method to clean up temporary permissions
+cleanupTemporaryPermissions(): void {
+  const permissions = [PermissionsEnum.ORG_PROJECT_EDIT, PermissionsEnum.ORG_PROJECT_DELETE];
+  permissions.forEach((permission: PermissionsEnum) => 
+    this._permissionsService.removePermission(permission)
+  );
+}

+// Call this method in ngOnDestroy or when navigating away

628-630: Efficient permission check utility method

This utility function elegantly checks if all specified permissions are present using Array.every() for concise evaluation.

packages/core/src/lib/shared/guards/permission.guard.ts (2)

15-22: Good inheritance structure with proper access modifiers

Extending BaseGuard and changing access modifiers from private to protected enables inheritance functionality while maintaining encapsulation. This change allows subclasses like ProjectManagerOrPermissionGuard to access these properties while preventing external access.


57-57: Updated variable name for consistency

The renamed _cacheManager variable is now used correctly, maintaining the caching functionality while aligning with naming conventions.

packages/core/src/lib/organization-project/organization-project.service.ts (3)

179-179: Improved entity destructuring

The destructuring now includes ...entity to collect all remaining properties, which is a more flexible and maintainable approach.


189-207: Enhanced member update logic with conditional processing

The conditional check for array types before processing members prevents unnecessary updates when only partial data is provided (e.g., when just updating project visibility). This addresses the issue noted in the past review comment.


662-681: Well-implemented manager check method

This method efficiently queries the database to determine if an employee is a manager of a specific project. The query correctly filters by project ID, employee ID, manager status, and active/archived flags, ensuring accurate results.

packages/core/src/lib/organization-project/organization-project.controller.ts (4)

48-49: Improved guard implementation for project permissions

Replacing the PermissionGuard with ProjectManagerOrPermissionGuard enhances the security model by first checking if a user is a project manager before falling back to permission checks. This aligns with the PR objective of correcting project manager permission validation logic.


92-97: Method renamed for better clarity

Renaming from findByEmployee to findProjectsByEmployee improves API readability by being more explicit about what the endpoint returns.


124-126: Method renamed for better clarity

Renaming from updateByEmployee to updateProjectByEmployee provides better clarity about the endpoint's purpose.


359-392: Well-implemented project manager check endpoint

This new endpoint provides a clean way to verify if an employee is a project manager, which is essential for the frontend permission checks. The endpoint is well-documented with appropriate parameters and return type.

@rahul-rocket rahul-rocket changed the title [Fix] Correct project manager permission validation logic [Fix] Project Manager Or Permission Validation Feb 27, 2025
import { PermissionsEnum } from '@gauzy/contracts';
import { ProjectManagerOrPermissionGuard } from '../guards/project-manager-or-permission.guard';

export const ManagerOrPermissions = (...permissions: PermissionsEnum[]) =>
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@samuelmbabhazi Please remove this decorators if not needed.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
apps/gauzy/src/app/pages/projects/guards/project-manager.guard.ts (1)

1-68: Thorough implementation of project manager verification guard

This guard has a well-designed implementation that follows Angular best practices for route protection. It properly handles multiple conditions to determine if a user should be granted access:

  1. First checks for existence of a project ID
  2. Checks for admin permissions (edit rights and employee selection rights)
  3. If not an admin, verifies if the user is a manager of the specific project

The console log on line 51 should be removed as it exposes debug information that shouldn't be present in production code:

-			console.log(`✅ Access granted: User (employeeId: ${employee?.id}) is manager of project ${projectId}.`);
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between caae943 and 9277f75.

📒 Files selected for processing (4)
  • apps/gauzy/src/app/pages/projects/components/project-list/list.component.ts (6 hunks)
  • apps/gauzy/src/app/pages/projects/guards/project-manager-or-permission.guard.ts (1 hunks)
  • apps/gauzy/src/app/pages/projects/guards/project-manager.guard.ts (1 hunks)
  • apps/gauzy/src/app/pages/projects/projects-routing.module.ts (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/gauzy/src/app/pages/projects/projects-routing.module.ts
⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: Codacy Static Code Analysis
  • GitHub Check: test
  • GitHub Check: build
🔇 Additional comments (6)
apps/gauzy/src/app/pages/projects/guards/project-manager-or-permission.guard.ts (1)

1-43: Well-structured guard implementation combining project manager status and permissions

The implementation of this guard provides an elegant solution to the use case mentioned in the PR objectives. It properly checks if a user is a project manager first, and only if that fails, it falls back to checking regular permissions.

Some noteworthy aspects:

  • Good use of RxJS operators (switchMap, tap, catchError)
  • Comprehensive error handling
  • Clear documentation explaining the logic flow
  • Proper redirection handling based on route data

The guard effectively combines the functionality of two separate guards, making the authorization flow cleaner and more maintainable.

apps/gauzy/src/app/pages/projects/components/project-list/list.component.ts (5)

289-294: Effective implementation of manager detection

This method efficiently checks if the current user is a manager of a project by filtering the project members. The implementation is concise and focuses on the specific condition that needs to be verified.


555-577: Robust temporary permissions handling based on project role

The implementation matches the PR objective description well by dynamically granting or removing permissions based on the user's role in the selected project.

When a user selects a project:

  1. It checks if they're a manager of that specific project
  2. It verifies if they already have all required permissions
  3. If they don't have all permissions but are a manager, it temporarily grants the necessary edit and delete permissions
  4. If they're not a manager, it ensures these permissions are removed

This approach correctly implements the requirement of granting temporary permissions only when a project is selected and only if the user is a manager of that specific project.


634-637: Efficient permission checking utility method

The _hasAllPermissions method is well implemented using Array.prototype.every() which provides early termination on the first failure. This is more efficient than checking each permission individually and combining the results.


6-6: Dependencies updated properly for permission management

The component has been correctly updated to include the necessary imports and dependencies:

  • RxJS operators for handling reactive streams
  • NgxPermissionsService for dynamic permission management
  • OrganizationProjectStore for project state management

These additions align well with the new functionality being added to the component.

Also applies to: 9-9, 89-90


372-372: Fixed property access path for employee ID

This change correctly updates the property access path to handle the nested structure of the user object.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants