-
Notifications
You must be signed in to change notification settings - Fork 120
Google Calendar/Meet + Drive (free side) — IDOR-hardened (supersedes #625) #634
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
Changes from all commits
fc989dd
9539800
7555116
f79b10f
1037499
ce4a0ff
a9ec5f3
7b1ac96
f4cb579
00d207a
9b7cd6c
f10e167
ba41840
166c9f1
2acb2ce
d1bb174
9583ec8
b31ca5c
d98d534
da31127
87b5531
6d9c762
b6db5f2
58bf81d
9fbf23d
5039d4c
6556c55
f7d3c8d
3e76174
c64ecb2
26cf82a
531a527
77c2b5b
8555a81
fb1c399
a561a32
2894974
f0c2d82
ac13e54
43629a3
b09de19
b6b771e
c6e1378
8855828
c23d629
07dcaf4
72e00c3
f0f4515
b67d28f
3602e71
32c0eff
231d7b9
3e9fcd9
2473629
2012098
4e3238c
2aeeb45
0d23756
8b8f103
6d7fc47
3bdc23c
a06f1e3
b0d7807
609b985
c23e327
45f471a
48b4c02
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,63 @@ | ||
| <?php | ||
| use WeDevs\PM\Core\Router\Router; | ||
|
|
||
| $wedevs_pm_router = Router::singleton(); | ||
|
|
||
| $gw_base = 'WeDevs/PM/Google_Workspace/Controllers/'; | ||
| $gw_admin = 'WeDevs\PM\Core\Permissions\Settings_Page_Access'; | ||
| $gw_auth = 'WeDevs\PM\Core\Permissions\Authentic'; | ||
| $gw_access = 'WeDevs\PM\Core\Permissions\Access_Project'; | ||
|
|
||
| // ── Admin: site-level OAuth credentials + Picker keys ──────────────── | ||
| $wedevs_pm_router->get( 'google-workspace/settings', $gw_base . 'Settings_Controller@get' ) | ||
| ->permission( [ $gw_admin ] ); | ||
|
|
||
| $wedevs_pm_router->post( 'google-workspace/settings', $gw_base . 'Settings_Controller@save' ) | ||
| ->permission( [ $gw_admin ] ); | ||
|
|
||
| // ── Per-user connection ────────────────────────────────────────────── | ||
| $wedevs_pm_router->get( 'google-workspace/status', $gw_base . 'OAuth_Controller@status' ) | ||
| ->permission( [ $gw_auth ] ); | ||
|
|
||
| $wedevs_pm_router->get( 'google-workspace/auth-url', $gw_base . 'OAuth_Controller@auth_url' ) | ||
| ->permission( [ $gw_auth ] ); | ||
|
|
||
| $wedevs_pm_router->post( 'google-workspace/disconnect', $gw_base . 'OAuth_Controller@disconnect' ) | ||
| ->permission( [ $gw_auth ] ); | ||
|
|
||
| $wedevs_pm_router->post( 'google-workspace/my-prefs', $gw_base . 'OAuth_Controller@save_my_prefs' ) | ||
| ->permission( [ $gw_auth ] ); | ||
|
|
||
| // ── Drive Picker config (vends caller's own access token + Picker keys) ── | ||
| $wedevs_pm_router->get( 'google-workspace/drive/picker-config', $gw_base . 'Drive_Controller@picker_config' ) | ||
| ->permission( [ $gw_auth ] ); | ||
|
|
||
| // ── Per-project Drive role access (manager configures; members query) ── | ||
| // Same gate as the project Capabilities/Settings tab where these toggles live. | ||
| $gw_manager = 'WeDevs\PM\Core\Permissions\Project_Settings_Page_Access'; | ||
|
|
||
| $wedevs_pm_router->get( 'projects/{project_id}/google-workspace/access', $gw_base . 'Drive_Controller@get_access' ) | ||
| ->permission( [ $gw_manager ] ); | ||
|
|
||
| $wedevs_pm_router->post( 'projects/{project_id}/google-workspace/access', $gw_base . 'Drive_Controller@save_access' ) | ||
| ->permission( [ $gw_manager ] ); | ||
|
|
||
| $wedevs_pm_router->get( 'projects/{project_id}/google-workspace/can-use', $gw_base . 'Drive_Controller@can_use' ) | ||
| ->permission( [ $gw_access ] ); | ||
|
|
||
| // ── Drive attachments (polymorphic: task, comment, discussion, project) ── | ||
| $wedevs_pm_router->get( 'projects/{project_id}/google-drive', $gw_base . 'Drive_Controller@index' ) | ||
| ->permission( [ $gw_access ] ); | ||
|
|
||
| $wedevs_pm_router->post( 'projects/{project_id}/google-drive', $gw_base . 'Drive_Controller@attach' ) | ||
| ->permission( [ $gw_access ] ); | ||
|
|
||
| $wedevs_pm_router->delete( 'projects/{project_id}/google-drive/{id}', $gw_base . 'Drive_Controller@destroy' ) | ||
| ->permission( [ $gw_access ] ); | ||
|
|
||
| // Legacy task-scoped routes (kept for back-compat). | ||
| $wedevs_pm_router->get( 'projects/{project_id}/tasks/{task_id}/google-drive', $gw_base . 'Drive_Controller@index' ) | ||
| ->permission( [ $gw_access ] ); | ||
|
|
||
| $wedevs_pm_router->post( 'projects/{project_id}/tasks/{task_id}/google-drive', $gw_base . 'Drive_Controller@attach' ) | ||
| ->permission( [ $gw_access ] ); | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -37,8 +37,14 @@ protected function content( Comment $comment, $old_value ) { | |
|
|
||
| private function log_activity( Comment $comment, $action_type ) { | ||
| $parent_comment = Comment::parent_comment( $comment->id ); | ||
| if ( ! $parent_comment ) { | ||
| return; // can't resolve the thread root — skip activity logging | ||
| } | ||
| $commentable_type = $parent_comment->commentable_type; | ||
| $commentable = $this->get_commentable( $parent_comment ); | ||
| if ( ! $commentable ) { | ||
| return; // commentable (task/discussion/file) gone — skip logging | ||
| } | ||
|
|
||
| switch ( $commentable_type ) { | ||
| case 'task': | ||
|
|
@@ -67,11 +73,24 @@ private function log_activity( Comment $comment, $action_type ) { | |
| } | ||
| } | ||
|
|
||
| /** Flags whether a comment body contains a Drive / Meet link (for activity icons). */ | ||
| private function gw_meta( Comment $comment ) { | ||
| $content = (string) $comment->content; | ||
| return [ | ||
| 'has_drive' => ( strpos( $content, 'drive.google.com' ) !== false || strpos( $content, 'docs.google.com' ) !== false ), | ||
| 'has_meet' => ( strpos( $content, 'meet.google.com' ) !== false ), | ||
| ]; | ||
| } | ||
|
|
||
| private function comment_on_task( Comment $comment, Task $task, $action_type ) { | ||
| $meta = [ | ||
| 'comment_id' => $comment->id, | ||
| 'task_title' => $task->title, | ||
| ]; | ||
| // Don't mark a deleted comment with Drive/Meet icons. | ||
| if ( $action_type !== 'delete' ) { | ||
| $meta = array_merge( $meta, $this->gw_meta( $comment ) ); | ||
| } | ||
|
Comment on lines
+76
to
+93
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -euo pipefail
git ls-files 'src/Comment/Observers/Comment_Observer.php' 'src/**/Comment_Observer.php' | cat
echo '---'
rg -n "gw_meta\(|comment_on_(task|discussion_board|task_list|milestone|project|file)\(" src/Comment/Observers/Comment_Observer.php src -S
echo '---'
sed -n '1,220p' src/Comment/Observers/Comment_Observer.phpRepository: weDevsOfficial/wp-project-manager Length of output: 12585 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo '--- usages of has_drive / has_meet ---'
rg -n "\bhas_drive\b|\bhas_meet\b" src core -S
echo '--- remaining part of Comment_Observer.php ---'
sed -n '220,320p' src/Comment/Observers/Comment_Observer.php
echo '--- activity/comment UI references for comment_on_discussion_board and similar actions ---'
rg -n "comment_on_discussion_board|update_comment_on_discussion_board|reply_comment_on_discussion_board|has_drive|has_meet|drive.google.com|meet.google.com" src core -SRepository: weDevsOfficial/wp-project-manager Length of output: 6976 Propagate Drive/Meet metadata to all comment activity types. 🤖 Prompt for AI Agents |
||
|
|
||
| if ( $action_type == 'create' && $comment->commentable_type == 'comment' ) { | ||
| $action = 'reply_comment_on_task'; | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🔒 Security & Privacy | 🟠 Major | 🏗️ Heavy lift
Missing
->validator()/->sanitizer()on every new route.None of the 13 Google Workspace routes attach
->validator()or->sanitizer(), only->permission(). This is required for every REST endpoint in this codebase. Inlinesanitize_text_field()/filter_var()calls insideSettings_Controller/OAuth_Controllerdon't substitute for the router-level contract, and params likeproject_id,task_id,id,client_id,api_key,drive_on, etc. reach controllers unvalidated at the routing layer.As per coding guidelines, "Every REST endpoint must have a validator via
->validator()and sanitizer via->sanitizer()methods."🤖 Prompt for AI Agents
Source: Coding guidelines