Skip to content

Add user_mountable functionality for Mounts (#2077)#2181

Open
lancepioch wants to merge 8 commits intomainfrom
lance/2077
Open

Add user_mountable functionality for Mounts (#2077)#2181
lancepioch wants to merge 8 commits intomainfrom
lance/2077

Conversation

@lancepioch
Copy link
Copy Markdown
Member

Resolves #2077

@lancepioch lancepioch self-assigned this Feb 6, 2026
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Feb 6, 2026

📝 Walkthrough

Walkthrough

Adds admin control for a mount's user_mountable flag, two subuser permissions (mount.read, mount.update), a new Filament server Mounts page for users to view/toggle compatible user-mountable mounts, adjusted navigation ordering, and new translation strings for admin and server UIs.

Changes

Cohort / File(s) Summary
Enum and Permission Definitions
app/Enums/SubuserPermission.php
Added enum cases MountRead = 'mount.read' and MountUpdate = 'mount.update'; mapped mount group icon to TablerIcon::LayersLinked.
Admin Mount Management
app/Filament/Admin/Resources/Mounts/MountResource.php, app/Filament/Admin/Resources/Mounts/Pages/CreateMount.php
Added user_mountable ToggleButtons to mount form and a user_mountable column to the admin table; removed hardcoded default assignment of user_mountable in mount creation.
Server Mounts Page (new)
app/Filament/Server/Pages/Mounts.php
New server-side Filament page to list and toggle mounts: enforces MountRead/MountUpdate permissions, queries user-mountable & server-compatible mounts, pre-fills selections, persists sync on save, records activity, and shows notifications; UI disables editing when update permission is missing.
Navigation Ordering
app/Filament/Server/Pages/Startup.php, app/Filament/Server/Pages/Settings.php
Incremented navigationSort values to adjust ordering so the new Mounts page appears in navigation.
Localization
lang/en/admin/mount.php, lang/en/server/mount.php, lang/en/server/user.php, lang/en/activity.php
Added admin user_mountable labels/help and toggles, server Mounts page strings and notifications, permission descriptions for mount_desc, mount_read, mount_update, and an activity message for mounts update.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant MountsPage as MountsPage
    participant Auth as Authorization
    participant DB as Database
    participant Activity as ActivityLog
    participant UI as Notification

    User->>MountsPage: open Mounts page
    MountsPage->>Auth: authorize(MountRead)
    Auth-->>MountsPage: allowed / denied
    MountsPage->>DB: query mounts (user_mountable && server-compatible)
    DB-->>MountsPage: mounts list
    MountsPage-->>User: render CheckboxList (pre-filled)
    User->>MountsPage: update selection / save
    MountsPage->>Auth: authorize(MountUpdate)
    Auth-->>MountsPage: allowed / denied
    MountsPage->>DB: sync server↔mount relations
    DB-->>MountsPage: success / error
    MountsPage->>Activity: record mounts.update
    Activity-->>MountsPage: logged
    MountsPage->>UI: show success or failure notification
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 20.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Add user_mountable functionality for Mounts' directly and clearly describes the main change—adding user_mountable functionality to mounts, which aligns with the core objective stated in issue #2077.
Description check ✅ Passed The description 'Resolves #2077' is minimal but directly related to the changeset by linking to the issue that outlines the requirements being implemented.
Linked Issues check ✅ Passed The PR successfully implements all primary coding requirements from issue #2077: adds user_mountable input to MountResource form [MountResource.php], updates CreateMount to handle user_mountable [CreateMount.php], and adds a client-area mounts management page [Mounts.php]. Permission enums and translations support these features.
Out of Scope Changes check ✅ Passed All changes are directly related to implementing user_mountable functionality. Navigation sort updates [Settings.php, Startup.php] are minor and support the new feature's integration into the UI structure.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

📝 Coding Plan
  • Generate coding plan for human review comments

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@lancepioch
Copy link
Copy Markdown
Member Author

@coderabbitai full review

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Feb 8, 2026

✅ Actions performed

Full review triggered.

@Boy132 Boy132 marked this pull request as ready for review March 17, 2026 15:04
@Boy132
Copy link
Copy Markdown
Member

Boy132 commented Mar 17, 2026

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 17, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown

@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)
app/Filament/Server/Pages/Mounts.php (1)

71-73: Drop unused callback parameter.

$state is never read in this callback.

♻️ Proposed cleanup
-                        ->afterStateUpdated(function ($state) {
+                        ->afterStateUpdated(function () {
                             $this->save();
                         })
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/Filament/Server/Pages/Mounts.php` around lines 71 - 73, The closure
passed to afterStateUpdated in the Mounts page is declaring an unused parameter
($state); remove the unused $state parameter from the anonymous function so it
becomes function () { $this->save(); } (i.e., update the afterStateUpdated
callback where ->afterStateUpdated(function ($state) { $this->save(); }) is
defined to drop the $state parameter).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@app/Filament/Server/Pages/Mounts.php`:
- Around line 65-67: The descriptions closure builds an HtmlString from mount
fields without escaping, creating XSS risk; update the descriptions callback
(the ->descriptions(fn () => ... ) closure that maps Mount $mount) to escape
$mount->source, $mount->target and the stripped $mount->description using e()
(or equivalent HTML-encoding) before concatenating and wrapping in HtmlString so
all dynamic values are HTML-escaped while still allowing the intended line
break; keep the existing stripTags() for description but pass its result through
e() as well.

---

Nitpick comments:
In `@app/Filament/Server/Pages/Mounts.php`:
- Around line 71-73: The closure passed to afterStateUpdated in the Mounts page
is declaring an unused parameter ($state); remove the unused $state parameter
from the anonymous function so it becomes function () { $this->save(); } (i.e.,
update the afterStateUpdated callback where ->afterStateUpdated(function
($state) { $this->save(); }) is defined to drop the $state parameter).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: b35254d1-7174-41d3-981e-eacbddbcd982

📥 Commits

Reviewing files that changed from the base of the PR and between 612041e and e0faf61.

📒 Files selected for processing (9)
  • app/Enums/SubuserPermission.php
  • app/Filament/Admin/Resources/Mounts/MountResource.php
  • app/Filament/Admin/Resources/Mounts/Pages/CreateMount.php
  • app/Filament/Server/Pages/Mounts.php
  • app/Filament/Server/Pages/Settings.php
  • app/Filament/Server/Pages/Startup.php
  • lang/en/admin/mount.php
  • lang/en/server/mount.php
  • lang/en/server/user.php
💤 Files with no reviewable changes (1)
  • app/Filament/Admin/Resources/Mounts/Pages/CreateMount.php

Comment thread app/Filament/Server/Pages/Mounts.php
Copy link
Copy Markdown

@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.

🧹 Nitpick comments (2)
app/Filament/Server/Pages/Mounts.php (2)

98-103: Consider sanitizing exception messages shown to users.

Displaying raw exception messages via $exception->getMessage() could potentially expose internal implementation details (e.g., SQL errors, file paths, class names) to users. While this is a server management panel, it's good practice to show generic error messages and log the details server-side.

Proposed fix
         } catch (Exception $exception) {
+            report($exception);
+
             Notification::make()
                 ->title(trans('server/mount.notification_failed'))
-                ->body($exception->getMessage())
+                ->body(trans('server/mount.notification_failed_body'))
                 ->danger()
                 ->send();
         }

This requires adding a new translation key server/mount.notification_failed_body with a generic error message.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/Filament/Server/Pages/Mounts.php` around lines 98 - 103, Replace the
direct usage of $exception->getMessage() in the catch block that builds the
Notification (the Notification::make() call in Mounts.php) with a generic
translated message key (server/mount.notification_failed_body), and ensure the
full exception is logged on the server (e.g., via Log::error or the existing
logger) so internal details are recorded but not shown to users; also add the
new translation key server/mount.notification_failed_body with a generic
user-facing message.

71-73: Remove unused $state parameter.

The $state parameter in the afterStateUpdated callback is not used. Consider removing it to avoid confusion.

Proposed fix
                         ->live()
-                        ->afterStateUpdated(function ($state) {
+                        ->afterStateUpdated(function () {
                             $this->save();
                         })
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/Filament/Server/Pages/Mounts.php` around lines 71 - 73, In the
afterStateUpdated callback on the Mounts form, remove the unused $state
parameter to avoid confusion; update the closure from function ($state) {
$this->save(); } to a parameterless closure (function () { $this->save(); }) so
the call to $this->save() remains but the unused $state argument is eliminated
in the afterStateUpdated usage.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@app/Filament/Server/Pages/Mounts.php`:
- Around line 98-103: Replace the direct usage of $exception->getMessage() in
the catch block that builds the Notification (the Notification::make() call in
Mounts.php) with a generic translated message key
(server/mount.notification_failed_body), and ensure the full exception is logged
on the server (e.g., via Log::error or the existing logger) so internal details
are recorded but not shown to users; also add the new translation key
server/mount.notification_failed_body with a generic user-facing message.
- Around line 71-73: In the afterStateUpdated callback on the Mounts form,
remove the unused $state parameter to avoid confusion; update the closure from
function ($state) { $this->save(); } to a parameterless closure (function () {
$this->save(); }) so the call to $this->save() remains but the unused $state
argument is eliminated in the afterStateUpdated usage.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: aa500256-3c29-4691-872c-8122f3ce0d70

📥 Commits

Reviewing files that changed from the base of the PR and between e0faf61 and 0e42f86.

📒 Files selected for processing (1)
  • app/Filament/Server/Pages/Mounts.php

Copy link
Copy Markdown

@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

♻️ Duplicate comments (1)
app/Filament/Server/Pages/Mounts.php (1)

65-66: ⚠️ Potential issue | 🟠 Major

Escape mount fields before wrapping in HtmlString.

The stripTags() method only removes HTML tags but does not escape HTML entities. An attacker with admin access to create mounts could use entity encoding (e.g., <script>) to inject XSS payloads that bypass stripTags().

Use e() to escape all dynamic values:

Proposed fix
-                        ->descriptions(fn () => $allowedMounts->mapWithKeys(fn (Mount $mount) => [$mount->id => new HtmlString(str("$mount->source -> $mount->target")->stripTags() . ($mount->description ? '<br>' . str($mount->description)->stripTags() : ''))]))
+                        ->descriptions(fn () => $allowedMounts->mapWithKeys(function (Mount $mount) {
+                            $path = e($mount->source) . ' -> ' . e($mount->target);
+                            $description = $mount->description ? '<br>' . e(strip_tags($mount->description)) : '';
+
+                            return [$mount->id => new HtmlString($path . $description)];
+                        }))
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/Filament/Server/Pages/Mounts.php` around lines 65 - 66, The descriptions
callback is currently building an HtmlString from unescaped mount fields (in the
->descriptions(...) lambda using $allowedMounts->mapWithKeys and new
HtmlString(...)), which allows entity-encoded XSS to bypass stripTags(); update
that callback to escape each dynamic value (use e($mount->source),
e($mount->target), and e($mount->description)) before concatenating and then
construct the HtmlString only from the escaped pieces (you can still insert a
literal '<br>' between escaped parts if you need a line break), ensuring all
$mount properties are passed through e() prior to wrapping in HtmlString.
🧹 Nitpick comments (1)
app/Filament/Server/Pages/Mounts.php (1)

71-73: Remove unused $state parameter.

The $state parameter is not used in the callback. Consider simplifying to an arrow function or removing the parameter.

Proposed fix
-                        ->afterStateUpdated(function ($state) {
-                            $this->save();
-                        })
+                        ->afterStateUpdated(fn () => $this->save())
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/Filament/Server/Pages/Mounts.php` around lines 71 - 73, The anonymous
callback passed to afterStateUpdated includes an unused $state parameter; remove
the parameter and simplify the callback to avoid unused variable warnings by
changing the handler to a parameterless function that calls $this->save() (i.e.,
update the afterStateUpdated(...) callback where ->afterStateUpdated(function
($state) { $this->save(); }) is defined to a parameterless version or an arrow
function that invokes $this->save()).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@app/Filament/Server/Pages/Mounts.php`:
- Around line 98-105: Replace the
Notification::make()->body($exception->getMessage()) usage so the UI shows a
generic message instead of the raw exception; keep report($exception) to log
details, change the body call to use the translation key
trans('server/mount.notification_failed_body'), and add that translation entry
with a generic message like "An error occurred while updating mounts. Please try
again." This avoids exposing $exception->getMessage() in Mounts.php while
preserving server-side reporting via report($exception).

---

Duplicate comments:
In `@app/Filament/Server/Pages/Mounts.php`:
- Around line 65-66: The descriptions callback is currently building an
HtmlString from unescaped mount fields (in the ->descriptions(...) lambda using
$allowedMounts->mapWithKeys and new HtmlString(...)), which allows
entity-encoded XSS to bypass stripTags(); update that callback to escape each
dynamic value (use e($mount->source), e($mount->target), and
e($mount->description)) before concatenating and then construct the HtmlString
only from the escaped pieces (you can still insert a literal '<br>' between
escaped parts if you need a line break), ensuring all $mount properties are
passed through e() prior to wrapping in HtmlString.

---

Nitpick comments:
In `@app/Filament/Server/Pages/Mounts.php`:
- Around line 71-73: The anonymous callback passed to afterStateUpdated includes
an unused $state parameter; remove the parameter and simplify the callback to
avoid unused variable warnings by changing the handler to a parameterless
function that calls $this->save() (i.e., update the afterStateUpdated(...)
callback where ->afterStateUpdated(function ($state) { $this->save(); }) is
defined to a parameterless version or an arrow function that invokes
$this->save()).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 040e838f-4531-4ab1-8a5b-507f622741fe

📥 Commits

Reviewing files that changed from the base of the PR and between 0e42f86 and c25c1cb.

📒 Files selected for processing (1)
  • app/Filament/Server/Pages/Mounts.php

Comment thread app/Filament/Server/Pages/Mounts.php
Copy link
Copy Markdown

@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.

♻️ Duplicate comments (1)
app/Filament/Server/Pages/Mounts.php (1)

97-100: ⚠️ Potential issue | 🟠 Major

Do not expose raw exception messages in this user-facing page.

At Line 99, $exception->getMessage() can leak internal details to subusers. Keep report($exception), but show a generic translated failure body in the notification instead.

Proposed fix
             Notification::make()
                 ->title(trans('server/mount.notification_failed'))
-                ->body($exception->getMessage())
+                ->body(trans('server/mount.notification_failed_body'))
                 ->danger()
                 ->send();

Also add server/mount.notification_failed_body to translations.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/Filament/Server/Pages/Mounts.php` around lines 97 - 100, Replace the
user-facing raw exception message in Notification::make() — keep the existing
report($exception) call to log the error, but change
->body($exception->getMessage()) to use a generic translated message like
->body(trans('server/mount.notification_failed_body')); also add the key
server/mount.notification_failed_body to your locale files with an appropriate
generic message so users see a safe, localized failure description while
internal details remain logged.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@app/Filament/Server/Pages/Mounts.php`:
- Around line 97-100: Replace the user-facing raw exception message in
Notification::make() — keep the existing report($exception) call to log the
error, but change ->body($exception->getMessage()) to use a generic translated
message like ->body(trans('server/mount.notification_failed_body')); also add
the key server/mount.notification_failed_body to your locale files with an
appropriate generic message so users see a safe, localized failure description
while internal details remain logged.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 5c196bbe-d546-4405-9028-39341814644d

📥 Commits

Reviewing files that changed from the base of the PR and between c25c1cb and bda5df8.

📒 Files selected for processing (2)
  • app/Filament/Server/Pages/Mounts.php
  • lang/en/activity.php
✅ Files skipped from review due to trivial changes (1)
  • lang/en/activity.php

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.

Add functionality to user_mountable

2 participants