4.7 KiB
ADDED Requirements
Requirement: Least-privilege ACLs for group order models
group.order and group.order.slot SHALL NOT grant write, create or unlink to portal, public
or unassigned internal users. Write/create/unlink SHALL be limited to the group-order manager
group (website_sale_aplicoop.group_group_order_manager) and ERP admins. Read MAY be granted to
internal users and portal users through explicit, group-scoped ACLs (no empty group_id rows).
Controller mutations continue to run via sudo() and MUST NOT depend on broad ACLs.
Scenario: Portal user cannot mutate group orders
- WHEN a portal user calls the external API to
create,writeorunlinkongroup.orderorgroup.order.slot - THEN the operation is denied with an
AccessError
Scenario: Manager retains full access
- WHEN a user in
group_group_order_managercreates or edits agroup.orderof their company - THEN the operation succeeds
Scenario: Controllers still operate
- WHEN an authenticated portal member confirms a cart through the eskaera controller (which
uses
sudo()) - THEN the underlying
sale.order/group.orderwrites succeed despite the tighter ACLs
Requirement: Supplier cost not broadly exposed to portal
Portal users SHALL NOT read product.supplierinfo cost/price rows for products or vendors
unrelated to them. The unrestricted record rule (domain=[(1,'=',1)]) granting portal read on
product.supplierinfo SHALL NOT exist. The product origin and main seller shown in the
eskaera shop — currently painted from supplierinfo — MUST keep working, either by preparing those
values server-side (via sudo() in the controller, per the repo's "no logic in QWeb" rule) or via
a record rule scoped to the products the user can access, exposing no supplier cost/price fields.
Scenario: Portal user cannot list unrelated supplier cost
- WHEN a portal user calls
search_readonproduct.supplierinfovia the external API - THEN the result does not include supplier cost/price rows of products/vendors unrelated to the user
Scenario: Origin and main seller still render
- WHEN a portal member opens the eskaera shop
- THEN each product's origin and main seller display correctly, without exposing supplier cost/price across all vendors
Requirement: CSRF protection on state-changing eskaera endpoints
State-changing eskaera endpoints SHALL be protected against cross-site request forgery. The
endpoints /eskaera/save-order, /eskaera/confirm, /eskaera/clear-cart and /eskaera/save-cart
(which create, confirm, merge or cancel sale.order records) SHALL NOT be type="http" with
csrf=False; they SHALL be type="json" (or otherwise validate a CSRF token) so that a cross-site
HTML form POST cannot trigger them.
Scenario: Cross-site form POST is rejected
- WHEN a logged-in user's browser is induced to submit a cross-site form POST to
/eskaera/clear-cartor/eskaera/confirm - THEN the request is rejected and no order is cancelled/confirmed
Scenario: Legitimate same-origin call works
- WHEN the eskaera frontend submits the JSON-RPC request from the same origin
- THEN the order is created/confirmed as before
Requirement: Consumer-group membership enforced on group-order endpoints
Every eskaera endpoint that resolves a group.order from a request id SHALL verify that the
requesting user belongs to a consumer group of that order before returning data. This applies to
load_eskaera_page, load_products_ajax, add_to_eskaera_cart, eskaera_checkout and
check_group_order_status, reusing _validate_user_group_access / _get_consumer_group_for_user.
Internal users MAY be exempt (matching eskaera_shop).
Scenario: Non-member gets no cross-group data
- WHEN a portal user who is not a member of a group order requests
/eskaera/<order_id>/load-products-ajaxfor that order - THEN the endpoint returns empty/redirect and discloses no catalog or pricing of that order
Scenario: Member accesses own group order
- WHEN a portal member of the order's consumer group requests the same endpoint
- THEN the products and pricing are returned normally
Requirement: Safe template output in inline scripts
QWeb templates SHALL NOT inject data into inline <script> blocks via t-raw. JSON payloads for
the client SHALL be emitted through a safe pattern (e.g. <script type="application/json"> read by
the JS layer).
Scenario: Rendered history payload is not raw-injected
- WHEN the load-from-history template renders its JSON payload
- THEN the payload is emitted through a safe (escaped/typed) mechanism, not
t-rawinside a<script>tag