security(storage): restrict backend credentials to storage administrators#352
Draft
gonzalesedwin1123 wants to merge 4 commits into
Draft
security(storage): restrict backend credentials to storage administrators#352gonzalesedwin1123 wants to merge 4 commits into
gonzalesedwin1123 wants to merge 4 commits into
Conversation
…up_user TDD red: spp.storage.backend grants base.group_user model read and the S3 access/secret keys + Azure connection string have no field-level groups, so any internal user can read the raw credentials via read/search_read. New tests assert base.group_user cannot read the three credential fields (read + search_read), that they carry a group restriction, while storage admins and the server-side client builders still resolve them.
spp.storage.backend stored S3 access/secret keys and Azure connection strings as plain Char fields, and ir.model.access.csv grants base.group_user model read, so any internal user could read the raw credentials via read/search_read and use them to reach the configured bucket/container outside Odoo (password=True only masks the widget). - add groups="spp_storage_backend.group_storage_admin" to s3_access_key, s3_secret_key and azure_connection_string so Odoo strips them from read/search_read for non-admins (explicit requests raise AccessError) - resolve the credentials via self.sudo() inside _get_s3_client and _get_azure_client so a non-admin who is allowed to operate a backend can still store/retrieve without being able to read the secret (use-without-see) - keep base.group_user model read (non-secret fields stay usable) - bump to 19.0.2.0.1 with HISTORY fragment; no migration (field metadata, no schema/data change)
|
Caution The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased. |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## 19.0 #352 +/- ##
==========================================
- Coverage 74.28% 68.43% -5.86%
==========================================
Files 372 97 -275
Lines 25385 8578 -16807
==========================================
- Hits 18857 5870 -12987
+ Misses 6528 2708 -3820
Flags with carried forward coverage won't be shown. Click here to find out more.
🚀 New features to boost your workflow:
|
Staff review found _get_azure_public_url read self.azure_connection_string directly (no sudo), so SAS-URL generation would fail with AccessError for the non-admin operating users the fix supports (fails closed; latent as nothing calls get_public_url yet). Extract a shared _get_azure_connection_string() sudo helper and use it in both _get_azure_client and _get_azure_public_url; add a regression test that the SAS path resolves the credential for a base.group_user.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
spp.storage.backendstores S3 access/secret keys and Azure connection strings as plainCharfields, andir.model.access.csvgrantsbase.group_usermodel read. Because Odoo read access applies to every field unless a field hasgroups=(andpassword=Trueonly masks the UI widget — it does not protect values fromread/search_read/RPC), any internal user could read the raw cloud-storage credentials and use them to access, modify, or delete files in the configured bucket/container outside Odoo — potentially beneficiary documents or data exports. The module's owngroups.xmlsays only "trusted system administrators" should manage these; the ACL contradicted that. Shipped in released tag2026.07.Fix
groupson the three credential fields —s3_access_key,s3_secret_key,azure_connection_stringnow carrygroups="spp_storage_backend.group_storage_admin". Odoo strips group-restricted fields fromread/search_readfor non-members, and an explicit request for them raisesAccessError. Applies to existing2026.07DBs automatically on module update (field metadata) — no migration (no schema/data change).sudo()inside the client builders —_get_s3_clientand_get_azure_clientread the credentials offself.sudo(), so a non-admin who is allowed to operate a backend can stillstore/retrievewithout ever receiving the secret ("use without see"). The value is only used to build the boto3/Azure client server-side.# nosemgrepwith justification on the sudo lines.base.group_usermodel read kept — non-secret fields (name, type, bucket/container, endpoint) remain readable, supporting a future non-admin "resolve default backend + store/retrieve" flow. The credentials are protected at the field layer regardless of model read.19.0.2.0.0 → 19.0.2.0.1+ HISTORY.Scope note
Nothing in the repo currently consumes or depends on
spp.storage.backend, so this can't break a caller. The field-groups+ sudo approach was chosen over dropping thebase.group_userread row so the intended "internal users may use storage without seeing credentials" design keeps working. (If the team prefers, the read row could also be removed as belt-and-suspenders — noted for reviewers.)Tests (TDD)
New
tests/test_credential_access.py:base.group_usercannot reads3_access_key/s3_secret_key/azure_connection_string(per-fieldread, and explicitsearch_readraisesAccessError); defaultsearch_readexcludes them.groupsrestriction (fields_get).group_storage_adminuser reads them normally.base.group_user(sudo path) — pins "use without see" and that the fix doesn't break operation.spp_storage_backend36/36; ruff/ruff-format/semgrep clean.Operational remediation
On
2026.07deployments where a backend was configured with real credentials, those credentials were readable by every internal user for the exposure window. After upgrading, operators should treat the affected S3 keys / Azure connection strings as compromised and rotate them, and review bucket/container access logs.Reviewed
Independent staff review verified against Odoo 19 source that the credential exposure is fully closed: field-level
groups=strips the three credential fields from defaultread/search_readand raisesAccessErroron explicit requests,sudo()bypasses for internal client construction only (never returns the secret), and no other leak path exists (the model does not inheritmail.thread, norelated=field anywhere points at the credentials,copy()/export_dataare ACL-gated). The review's two block items were fixed: (1)_get_azure_public_urlread the connection string directly without sudo — extracted a shared_get_azure_connection_string()sudo helper used by both Azure read sites, with a regression test for the SAS-URL path; (2) regeneratedREADME.rst/index.html. The sudo is pinned — removing it makes the client-builder tests fail. Non-blocking notes: non-secret config fields (bucket/endpoint/container/path/region) remainbase.group_user-readable (info disclosure only, not credentials);test_connection()is RPC-reachable by non-admins but only returns a bool and discloses nothing.