1515 */
1616package io .gravitee .gateway .reactive .handlers .api .security ;
1717
18- import static io .gravitee .common .http .HttpStatusCode .SERVICE_UNAVAILABLE_503 ;
19- import static io .gravitee .common .http .HttpStatusCode .UNAUTHORIZED_401 ;
20- import static io .gravitee .gateway .reactive .api .context .InternalContextAttributes .ATTR_INTERNAL_FLOW_STAGE ;
21- import static io .gravitee .gateway .reactive .api .context .InternalContextAttributes .ATTR_INTERNAL_SECURITY_SKIP ;
22- import static io .gravitee .gateway .reactive .api .context .InternalContextAttributes .ATTR_INTERNAL_SECURITY_TOKEN ;
23- import static io .reactivex .rxjava3 .core .Completable .defer ;
24-
2518import io .gravitee .definition .model .Api ;
2619import io .gravitee .gateway .reactive .api .ExecutionFailure ;
2720import io .gravitee .gateway .reactive .api .ExecutionPhase ;
2821import io .gravitee .gateway .reactive .api .context .http .HttpExecutionContext ;
22+ import io .gravitee .gateway .reactive .api .context .http .HttpPlainExecutionContext ;
2923import io .gravitee .gateway .reactive .api .hook .Hookable ;
3024import io .gravitee .gateway .reactive .api .hook .SecurityPlanHook ;
3125import io .gravitee .gateway .reactive .core .hook .HookHelper ;
3428import io .gravitee .gateway .reactive .policy .PolicyManager ;
3529import io .reactivex .rxjava3 .core .Completable ;
3630import io .reactivex .rxjava3 .core .Flowable ;
37- import io .reactivex .rxjava3 .core .Single ;
31+ import io .reactivex .rxjava3 .core .SingleSource ;
3832import java .util .ArrayList ;
3933import java .util .Comparator ;
4034import java .util .List ;
4135import java .util .Objects ;
4236import java .util .stream .Collectors ;
43- import org .slf4j .Logger ;
44- import org .slf4j .LoggerFactory ;
4537
4638/**
4739 * {@link SecurityChain} is a special chain dedicated to execute policy associated with plans.
5244 * @author Jeoffrey HAEYAERT (jeoffrey.haeyaert at graviteesource.com)
5345 * @author GraviteeSource Team
5446 */
55- public class SecurityChain implements Hookable <SecurityPlanHook > {
56-
57- protected static final String PLAN_UNRESOLVABLE = "GATEWAY_PLAN_UNRESOLVABLE" ;
58- protected static final String PLAN_RESOLUTION_FAILURE = "GATEWAY_PLAN_RESOLUTION_FAILURE" ;
59- protected static final String UNAUTHORIZED_MESSAGE = "Unauthorized" ;
60- protected static final String TEMPORARILY_UNAVAILABLE_MESSAGE = "Temporarily Unavailable" ;
61- protected static final String ATTR_INTERNAL_PLAN_RESOLUTION_FAILURE = "securityChain.planResolutionFailure" ;
47+ public class SecurityChain extends AbstractSecurityChain <SecurityPlan , HttpPlainExecutionContext > implements Hookable <SecurityPlanHook > {
6248
63- protected static final Single <Boolean > TRUE = Single .just (true );
64- protected static final Single <Boolean > FALSE = Single .just (false );
65- private static final Logger log = LoggerFactory .getLogger (SecurityChain .class );
66- private final Flowable <SecurityPlan > chain ;
6749 private final ExecutionPhase executionPhase ;
6850
6951 private List <SecurityPlanHook > securityPlanHooks ;
7052
7153 public SecurityChain (Api api , PolicyManager policyManager , ExecutionPhase executionPhase ) {
72- this (
54+ super (
7355 Flowable .fromIterable (
7456 api
7557 .getPlans ()
@@ -78,81 +60,32 @@ public SecurityChain(Api api, PolicyManager policyManager, ExecutionPhase execut
7860 .filter (Objects ::nonNull )
7961 .sorted (Comparator .comparingInt (SecurityPlan ::order ))
8062 .collect (Collectors .toList ())
81- ),
82- executionPhase
63+ )
8364 );
65+ this .executionPhase = executionPhase ;
8466 }
8567
8668 public SecurityChain (Flowable <SecurityPlan > securityPlans , ExecutionPhase executionPhase ) {
87- this . chain = securityPlans ;
69+ super ( securityPlans ) ;
8870 this .executionPhase = executionPhase ;
8971 }
9072
91- /**
92- * Executes the security chain by executing all the {@link SecurityPlan}s in an ordered sequence.
93- * It's up to each {@link SecurityPlan} to provide its order. The lower is the order, the highest priority is.
94- * The result of the security chain execution depends on the first {@link SecurityPlan} able to execute the request.
95- * If no {@link SecurityPlan} has been executed because there is no {@link SecurityPlan} in the chain or none of them can execute the request,
96- * then the chain is interrupted with a 401 response status and the {@link Completable} returns an error.
97- *
98- * @param ctx the current execution context.
99- * @return a {@link Completable} that completes if the request has been successfully handled by a {@link SecurityPlan} or returns
100- * an error if no {@link SecurityPlan} can execute the request or the {@link SecurityPlan} failed.
101- */
102- public Completable execute (HttpExecutionContext ctx ) {
103- return defer (() -> {
104- if (!Objects .equals (true , ctx .getInternalAttribute (ATTR_INTERNAL_SECURITY_SKIP ))) {
105- return chain
106- .concatMapSingle (securityPlan -> continueChain (ctx , securityPlan ))
107- .any (Boolean ::booleanValue )
108- .flatMapCompletable (securityHandled -> {
109- if (Boolean .FALSE .equals (securityHandled )) {
110- Throwable throwable = ctx .getInternalAttribute (ATTR_INTERNAL_PLAN_RESOLUTION_FAILURE );
111- if (throwable != null ) {
112- return ctx .interruptWith (
113- new ExecutionFailure (SERVICE_UNAVAILABLE_503 )
114- .key (PLAN_RESOLUTION_FAILURE )
115- .message (TEMPORARILY_UNAVAILABLE_MESSAGE )
116- );
117- }
118- return ctx .interruptWith (
119- new ExecutionFailure (UNAUTHORIZED_401 ).key (PLAN_UNRESOLVABLE ).message (UNAUTHORIZED_MESSAGE )
120- );
121- }
122- return Completable .complete ();
123- })
124- .doOnSubscribe (disposable -> {
125- log .debug ("Executing security chain" );
126- ctx .putInternalAttribute (ATTR_INTERNAL_FLOW_STAGE , "security" );
127- })
128- .doFinally (() -> {
129- ctx .removeInternalAttribute (ATTR_INTERNAL_FLOW_STAGE );
130- ctx .removeInternalAttribute (ATTR_INTERNAL_PLAN_RESOLUTION_FAILURE );
131- ctx .removeInternalAttribute (ATTR_INTERNAL_SECURITY_TOKEN );
132- });
133- }
134-
135- log .debug ("Skipping security chain because it has been explicitly required" );
136- return Completable .complete ();
137- });
73+ @ Override
74+ protected Completable sendError (HttpPlainExecutionContext ctx , ExecutionFailure failure ) {
75+ return ctx .interruptWith (failure );
13876 }
13977
140- private Single <Boolean > continueChain (HttpExecutionContext ctx , SecurityPlan securityPlan ) {
141- return securityPlan
142- .canExecute (ctx )
143- .onErrorResumeNext (throwable -> {
144- log .error ("An error occurred while checking if security plan {} can be executed" , securityPlan .id (), throwable );
145- ctx .setInternalAttribute (ATTR_INTERNAL_PLAN_RESOLUTION_FAILURE , throwable );
146- return FALSE ;
147- })
148- .flatMap (canExecute -> {
149- if (Boolean .TRUE .equals (canExecute )) {
150- return HookHelper
151- .hook (() -> securityPlan .execute (ctx , executionPhase ), securityPlan .id (), securityPlanHooks , ctx , executionPhase )
152- .andThen (TRUE );
153- }
154- return FALSE ;
155- });
78+ @ Override
79+ protected SingleSource <Boolean > executePlan (SecurityPlan securityPlan , HttpPlainExecutionContext ctx ) {
80+ return HookHelper
81+ .hook (
82+ () -> securityPlan .execute (ctx , executionPhase ),
83+ securityPlan .id (),
84+ securityPlanHooks ,
85+ (HttpExecutionContext ) ctx ,
86+ executionPhase
87+ )
88+ .andThen (TRUE );
15689 }
15790
15891 @ Override
0 commit comments