Skip to content

Fix PermissionError reading config_secrets.json in web interface - #416

Merged
ChuckBuilds merged 1 commit into
mainfrom
claude/config-secrets-permission-denied-a1x5m2
Jul 15, 2026
Merged

Fix PermissionError reading config_secrets.json in web interface#416
ChuckBuilds merged 1 commit into
mainfrom
claude/config-secrets-permission-denied-a1x5m2

Conversation

@ChuckBuilds

@ChuckBuilds ChuckBuilds commented Jul 15, 2026

Copy link
Copy Markdown
Owner

Summary

ledmatrix.service (main display) runs as root, while ledmatrix-web.service runs as the non-root install user (see install_web_service.sh, which templates User=${ACTUAL_USER}). config/config_secrets.json was only ever chmod'd to 0o640 on write, never chgrp'd, so when the root-run display service created/rewrote the file it ended up group-owned by root — unreadable by the non-root web user. That produced the reported PermissionError: [Errno 13] Permission denied: 'config/config_secrets.json' and a raw 500 traceback out of pages_v3.py's settings page (get_raw_file_content('secrets')).

Type of change

  • Bug fix

Related issues

N/A

Changes

  • src/common/permission_utils.py: added get_shared_group_gid() (resolves the project root directory's owning gid — stable across reinstalls) and ensure_shared_group_ownership() (best-effort chgrp to that gid, root-only, no-op otherwise).
  • src/config_manager.py: calls the new helper after every secrets/config write (save_raw_file_content, _create_config_from_template) and self-heals stale group ownership on every load_config()/get_raw_file_content() call, so a previously-broken install repairs itself the next time the root-run service touches the file.
  • src/config_manager_atomic.py: same chgrp fix in _atomic_move, the atomic save path used by the settings UI.
  • src/config_manager.py: get_raw_file_content('secrets') now tolerates PermissionError the same way load_config() already does — degrades to {} with a warning log instead of raising ConfigError/500, so a still-unreadable file (e.g. before the root service next runs the self-heal) no longer crashes the settings page.

Test plan

  • Ran the test suite (pytest) — n/a subset touching these files has no existing coverage; verified with python3 -m py_compile on the changed modules (sandbox lacks pytest/PIL)
  • Ran on a real Raspberry Pi with hardware
  • Ran in emulator mode
  • Ran the dev preview server
  • Manually verified the affected code path in the web UI

Documentation

  • N/A — no docs needed

Plugin compatibility

  • N/A — change doesn't touch the plugin system

Checklist

  • I've not committed any secrets or hardcoded API keys

Notes for reviewer

This is a best-effort self-heal, not a hard guarantee: it only fixes ownership when the root-run process (ledmatrix.service) touches the file (root can chown to any group; the non-root web process cannot). If a user's install never restarts/reloads the root service, an already-broken config_secrets.json stays unreadable to the web UI until it does — but the settings page will no longer crash, it'll just show empty secrets in the meantime.


Generated by Claude Code

Summary by CodeRabbit

  • Bug Fixes
    • Improved reliability when reading and saving configuration and secrets files.
    • Automatically corrects file ownership when permissions drift, helping services maintain access.
    • Configuration updates now handle unreadable secrets files more gracefully.
    • Atomic saves preserve the appropriate shared ownership settings.

ledmatrix.service (main display) runs as root while ledmatrix-web.service
runs as the non-root install user (install_web_service.sh). Both
config_manager.py and config_manager_atomic.py only chmod'd
config_secrets.json to 0o640 without ever fixing its group, so a file
written by the root service ended up group-owned by root and unreadable
by the web user, crashing the settings page with a raw PermissionError.

Add ensure_shared_group_ownership() to chgrp secrets/config files (best
effort, root-only) to the project directory's owning group whenever they
are created or saved, and self-heal existing files on load. Also make
get_raw_file_content() tolerate an unreadable secrets file the same way
load_config() already does, degrading to empty secrets instead of a 500.
@coderabbitai

coderabbitai Bot commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 87f76601-1936-420f-b568-4fc547c6affb

📥 Commits

Reviewing files that changed from the base of the PR and between 2a1c47f and 9ace113.

📒 Files selected for processing (3)
  • src/common/permission_utils.py
  • src/config_manager.py
  • src/config_manager_atomic.py

📝 Walkthrough

Walkthrough

Shared-group permission helpers now resolve the project group and correct file ownership when running as root. Configuration loading, creation, regular saves, and atomic saves invoke the helper; unreadable secrets files now degrade to an empty object.

Changes

Shared group permissions

Layer / File(s) Summary
Shared group ownership helpers
src/common/permission_utils.py
Adds cached project-group GID lookup and root-only best-effort group ownership correction.
Configuration ownership integration
src/config_manager.py, src/config_manager_atomic.py
Applies ownership correction during configuration reads, creation, regular saves, and atomic replacement; secrets permission errors return {}.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant ConfigManager
  participant permission_utils
  participant Filesystem

  ConfigManager->>permission_utils: Resolve shared group GID
  permission_utils->>Filesystem: Read project-root group
  ConfigManager->>permission_utils: Ensure file group ownership
  permission_utils->>Filesystem: Apply chown when required
  ConfigManager->>Filesystem: Read or atomically save configuration
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main fix: preventing web-interface PermissionError when reading config_secrets.json.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/config-secrets-permission-denied-a1x5m2

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.

@codacy-production

Copy link
Copy Markdown

Up to standards ✅

🟢 Issues 0 issues

Results:
0 new issues

View in Codacy

🟢 Metrics 0 duplication

Metric Results
Duplication 0

View in Codacy

NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes.

@ChuckBuilds

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 15, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

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.

@ChuckBuilds
ChuckBuilds merged commit 4abcd0e into main Jul 15, 2026
8 checks passed
@ChuckBuilds
ChuckBuilds deleted the claude/config-secrets-permission-denied-a1x5m2 branch July 15, 2026 18:26
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