Skip to content

Check read permissions for regressor sensors when triggering forecasts#2096

Open
Copilot wants to merge 4 commits intomainfrom
copilot/check-regressor-permissions-forecast-trigger
Open

Check read permissions for regressor sensors when triggering forecasts#2096
Copilot wants to merge 4 commits intomainfrom
copilot/check-regressor-permissions-forecast-trigger

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Apr 10, 2026

The POST /<sensor>/forecasts/trigger endpoint checked create-children permissions for the target sensor but silently skipped read permission checks for regressor sensors (future-regressors, past-regressors, regressors in config), allowing users to reference sensors they shouldn't be able to see.

Changes

  • Permission check (api/v3_0/sensors.py): Added a regressors_loader function (analogous to flex_model_loader/flex_context_loader in PR 904 read permissions of entities within flex model or flex context #1071) that extracts regressor Sensor objects from the deserialized config. A declarative @permission_required_for_context("read", ctx_arg_name="config", ctx_loader=regressors_loader, pass_ctx_to_loader=True) decorator on trigger_forecast enforces read access on each regressor sensor before the job is dispatched.

  • Schema (data/schemas/forecasting/pipeline.py): ForecastingTriggerSchema.config gains load_default={} so the kwarg is always present in the decorator chain (mirrors how flex_model/flex_context are handled in PR 904 read permissions of entities within flex model or flex context #1071). ForecasterParametersSchema.resolve_config now passes through extra keys from subclass schemas so config survives the @post_load transformation.

  • Auth decorator (auth/decorators.py): permission_required_for_context is extended with a _check_access_for_context helper that normalises a loader result to a list and calls check_access for each item, enabling multi-sensor permission checks from a single decorator.

  • Tests (api/v3_0/tests/test_forecasting_api.py): Added test_trigger_forecast_with_unreadable_regressor_returns_403, parametrized over all three regressor fields. Creates a target sensor on the requesting user's account (has create-children) and a regressor sensor on a different account (no read access), asserts 403.

@read-the-docs-community
Copy link
Copy Markdown

read-the-docs-community bot commented Apr 10, 2026

Copilot AI and others added 2 commits April 10, 2026 09:42
…mplify asset type name

Agent-Logs-Url: https://github.com/FlexMeasures/flexmeasures/sessions/9b3ff676-cf08-4d79-8cd1-657ef1581d40

Co-authored-by: BelhsanHmida <149331360+BelhsanHmida@users.noreply.github.com>
Copilot AI changed the title [WIP] Check read permissions for regressors when triggering forecasts Check read permissions for regressor sensors when triggering forecasts Apr 10, 2026
Copilot AI requested a review from BelhsanHmida April 10, 2026 09:46
@BelhsanHmida
Copy link
Copy Markdown
Contributor

@copilot Please use PR #1071 as context for this branch:
#1071

This branch is addressing a similar permissions issue, but for forecasting regressors. In particular, please compare the authorization semantics and implementation shape:

  • indirect sensor references in request payload
  • requiring read access on referenced sensors
  • expected 403 behavior
  • safe handling of missing/optional config

I’d like something that is consistent with the intent of #1071, while still fitting this branch cleanly.

Copy link
Copy Markdown
Contributor Author

Copilot AI commented Apr 15, 2026

@copilot Please use PR #1071 as context for this branch:
#1071

...

Aligned with PR #1071's pattern in commit 7eff89ac. Specifically:

  • regressors_loader function (analogous to flex_model_loader/flex_context_loader) extracts Sensor objects from the already-deserialized config dict.
  • ForecastingTriggerSchema.config gets load_default={} so the kwarg is always present in the decorator chain (same approach 904 read permissions of entities within flex model or flex context #1071 uses for flex_model/flex_context in TriggerScheduleKwargsSchema).
  • permission_required_for_context extended with a _check_access_for_context helper that normalises the loader result to a list and checks each sensor — the minimal version of 904 read permissions of entities within flex model or flex context #1071's multi-context support.
  • ForecasterParametersSchema.resolve_config now passes through extra keys from subclass schemas so config survives the @post_load.
  • Imperative check_access loop removed from the function body in favour of the declarative @permission_required_for_context("read", ctx_arg_name="config", ctx_loader=regressors_loader, pass_ctx_to_loader=True) decorator.

@BelhsanHmida
Copy link
Copy Markdown
Contributor

Manually tested this end-to-end in OpenAPI with a plain non-admin user from the target sensor’s account.

Setup:

  • target sensor: 286056
  • unreadable regressor: 286055 (owned by another account)
  • readable regressor: 286057 (owned by the same account as the target sensor)

Results:

  • Triggering a forecast for 286056 with 286055 in config was rejected with 403
  • Triggering the same forecast with 286057 in config succeeded and queued a forecasting job

This gives the expected behavior: permission to trigger forecasting on the target sensor does not automatically allow indirect access to regressor sensors. Referenced regressors now need to be readable as well.

@BelhsanHmida BelhsanHmida marked this pull request as ready for review April 16, 2026 00:55
Copy link
Copy Markdown
Contributor

@Flix6x Flix6x left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks nice, and thanks for reporting on your tests, too.

Just a changelog entry, please, and one request to enhance the test.

trigger_res = client.post(
trigger_url, json=payload, headers={"Authorization": token}
)
assert trigger_res.status_code == 403
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there anything in the response message that we could check that let's the user know which sensor or which field (e.g. "config", or even better, regressor_field) was to blame?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Check read permissions for regressors when triggering forecasts

3 participants