1313
1414
1515from __future__ import annotations
16-
1716import contextvars
1817import functools
1918import inspect
2019import secrets
2120from collections .abc import Callable
2221from dataclasses import asdict , dataclass
22+ from functools import update_wrapper
23+ from http import HTTPStatus
2324from itertools import dropwhile
2425from typing import (
2526 Any ,
4041
4142from nwastdlib import const , identity
4243from oauth2_lib .fastapi import OIDCUserModel
44+ from orchestrator .api .error_handling import raise_status
4345from orchestrator .config .assignee import Assignee
4446from orchestrator .db import db , transactional
4547from orchestrator .services .settings import get_engine_settings
@@ -99,7 +101,7 @@ def __call__(self) -> NoReturn: ...
99101
100102
101103def make_step_function (
102- f : Callable , name : str , form : InputFormGenerator | None = None , assignee : Assignee | None = Assignee .SYSTEM
104+ f : Callable , name : str , form : InputFormGenerator | None = None , assignee : Assignee | None = Assignee .SYSTEM , authorize_callback : Callable [[ OIDCUserModel | None ], bool ] | None = None
103105) -> Step :
104106 step_func = cast (Step , f )
105107
@@ -166,14 +168,39 @@ def __repr__(self) -> str:
166168 return f"StepList [{ ', ' .join (repr (x ) for x in self )} ]"
167169
168170
169- def _handle_simple_input_form_generator (f : StateInputStepFunc ) -> StateInputFormGenerator :
171+ def _handle_simple_input_form_generator (f : StateInputStepFunc , authorize_callback : Callable [[OIDCUserModel | None ], bool ] | None = None ) -> StateInputFormGenerator :
172+ """Processes f into a form generator and injects a pre-hook for user authorization"""
173+ def authorize_user_from_state (state : State ) -> None :
174+ logger .error ("authorize_user_from_state: called" )
175+ user_model = state .pop ("__process_user" , None )
176+ if user_model is not None :
177+ user_model = cast (OIDCUserModel , user_model )
178+ else :
179+ logger .error ("authorize_user_from_state: no user model" )
180+
181+ if authorize_callback is not None :
182+ logger .error ("authorize_user_from_state: callback found" )
183+ authorize_callback (user_model )
184+ if not authorize_callback (user_model ):
185+ logger .error ("authorize_user_from_state: FORBIDDEN" )
186+ #TODO not sure that step name is available here, but could put it on state?
187+ raise_status (HTTPStatus .FORBIDDEN , "User is not authorized to execute step" )
188+ else :
189+ logger .error ("authorize_user_from_state: no callback!" )
190+
170191 if inspect .isgeneratorfunction (f ):
171- return cast (StateInputFormGenerator , f )
192+ def generator_wrapper (state : State ):
193+ authorize_user_from_state (state )
194+ return f (state )
195+
196+ update_wrapper (generator_wrapper , f )
197+ return cast (StateInputFormGenerator , generator_wrapper )
172198 if inspect .isgenerator (f ):
173199 raise ValueError ("Got a generator object instead of function, this is not correct" )
174200
175201 # If f is a SimpleInputFormGenerator convert to new style generator function
176202 def form_generator (state : State ) -> FormGenerator :
203+ authorize_user_from_state (state )
177204 user_input : FormPage = yield cast (StateSimpleInputFormGenerator , f )(state )
178205 return user_input .model_dump ()
179206
@@ -270,7 +297,7 @@ def wrapper(state: State) -> Process:
270297 return decorator
271298
272299
273- def inputstep (name : str , assignee : Assignee ) -> Callable [[InputStepFunc ], Step ]:
300+ def inputstep (name : str , assignee : Assignee , authorize_callback : Callable [[ OIDCUserModel | None ], bool ] | None = None ) -> Callable [[InputStepFunc ], Step ]:
274301 """Add user input step to workflow.
275302
276303 IMPORTANT: In contrast to other workflow steps, the `@inputstep` wrapped function will not run in the
@@ -291,7 +318,7 @@ def decorator(func: InputStepFunc) -> Step:
291318 def wrapper (state : State ) -> FormGenerator :
292319 form_generator_in_form_inject_args = form_inject_args (func )
293320
294- form_generator = _handle_simple_input_form_generator (form_generator_in_form_inject_args )
321+ form_generator = _handle_simple_input_form_generator (form_generator_in_form_inject_args , authorize_callback = authorize_callback )
295322
296323 return form_generator (state )
297324
0 commit comments