13
13
14
14
15
15
from __future__ import annotations
16
-
17
16
import contextvars
18
17
import functools
19
18
import inspect
20
19
import secrets
21
20
from collections .abc import Callable
22
21
from dataclasses import asdict , dataclass
22
+ from functools import update_wrapper
23
+ from http import HTTPStatus
23
24
from itertools import dropwhile
24
25
from typing import (
25
26
Any ,
40
41
41
42
from nwastdlib import const , identity
42
43
from oauth2_lib .fastapi import OIDCUserModel
44
+ from orchestrator .api .error_handling import raise_status
43
45
from orchestrator .config .assignee import Assignee
44
46
from orchestrator .db import db , transactional
45
47
from orchestrator .services .settings import get_engine_settings
@@ -99,7 +101,7 @@ def __call__(self) -> NoReturn: ...
99
101
100
102
101
103
def 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
103
105
) -> Step :
104
106
step_func = cast (Step , f )
105
107
@@ -166,14 +168,39 @@ def __repr__(self) -> str:
166
168
return f"StepList [{ ', ' .join (repr (x ) for x in self )} ]"
167
169
168
170
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
+
170
191
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 )
172
198
if inspect .isgenerator (f ):
173
199
raise ValueError ("Got a generator object instead of function, this is not correct" )
174
200
175
201
# If f is a SimpleInputFormGenerator convert to new style generator function
176
202
def form_generator (state : State ) -> FormGenerator :
203
+ authorize_user_from_state (state )
177
204
user_input : FormPage = yield cast (StateSimpleInputFormGenerator , f )(state )
178
205
return user_input .model_dump ()
179
206
@@ -270,7 +297,7 @@ def wrapper(state: State) -> Process:
270
297
return decorator
271
298
272
299
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 ]:
274
301
"""Add user input step to workflow.
275
302
276
303
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:
291
318
def wrapper (state : State ) -> FormGenerator :
292
319
form_generator_in_form_inject_args = form_inject_args (func )
293
320
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 )
295
322
296
323
return form_generator (state )
297
324
0 commit comments