@@ -59,55 +59,24 @@ abstract class AbstractHttpAuthorizer {
5959 * be invoked, if not appropriate action will be taken to either report the failure or attempt authentication.
6060 */
6161 public void checkPermission (RoutingContext routingContext ) {
62- if (!controller .isAuthorizationEnabled ()) {
62+ if (!controller .isAuthorizationEnabled () || policies . isEmpty () ) {
6363 routingContext .next ();
6464 return ;
6565 }
66- //check their permissions
67- doPermissionCheck (routingContext , QuarkusHttpUser .getSecurityIdentity (routingContext , identityProviderManager ), 0 , null ,
68- policies );
69- }
7066
71- private void doPermissionCheck (RoutingContext routingContext ,
72- Uni <SecurityIdentity > identity , int index ,
73- SecurityIdentity augmentedIdentity ,
74- List <HttpSecurityPolicy > permissionCheckers ) {
75- if (index == permissionCheckers .size ()) {
76- QuarkusHttpUser currentUser = (QuarkusHttpUser ) routingContext .user ();
77- if (augmentedIdentity != null ) {
78- if (!augmentedIdentity .isAnonymous ()
79- && (currentUser == null || currentUser .getSecurityIdentity () != augmentedIdentity )) {
80- setIdentity (augmentedIdentity , routingContext );
81- }
82- if (securityEventHelper .fireEventOnSuccess ()) {
83- securityEventHelper .fireSuccessEvent (new AuthorizationSuccessEvent (augmentedIdentity ,
84- Map .of (RoutingContext .class .getName (), routingContext )));
85- }
86- } else if (securityEventHelper .fireEventOnSuccess ()
87- && permissionCheckPerformed (permissionCheckers , routingContext , index )) {
88- securityEventHelper .fireSuccessEvent (
89- new AuthorizationSuccessEvent (currentUser == null ? null : currentUser .getSecurityIdentity (),
90- Map .of (RoutingContext .class .getName (), routingContext )));
91- }
92- routingContext .next ();
93- return ;
94- }
95- //get the current checker
96- HttpSecurityPolicy res = permissionCheckers .get (index );
97- res .checkPermission (routingContext , identity , context )
67+ doPermissionCheck (routingContext , QuarkusHttpUser .getSecurityIdentity (routingContext , identityProviderManager ), 0 , null )
9868 .subscribe ().with (new Consumer <HttpSecurityPolicy .CheckResult >() {
9969 @ Override
10070 public void accept (HttpSecurityPolicy .CheckResult checkResult ) {
101- if (!checkResult .isPermitted ()) {
102- doDeny (identity , routingContext , res , checkResult .getAugmentedIdentity ());
71+ if (routingContext .response ().ended () || routingContext .failed ()) {
72+ return ;
73+ }
74+ if (checkResult .isPermitted ()) {
75+ routingContext .next ();
10376 } else {
104- if (checkResult .getAugmentedIdentity () != null ) {
105- doPermissionCheck (routingContext , checkResult .getAugmentedIdentityAsUni (),
106- index + 1 , checkResult .getAugmentedIdentity (), permissionCheckers );
107- } else {
108- //attempt to run the next checker
109- doPermissionCheck (routingContext , identity , index + 1 , augmentedIdentity , permissionCheckers );
110- }
77+ // if this is invoked, there is a bug in our code because all the denied checks must result
78+ // in the DoDenyException exception
79+ routingContext .fail (new IllegalStateException ("Received unhandled HttpSecurityPolicy.CheckResult" ));
11180 }
11281 }
11382 }, new Consumer <Throwable >() {
@@ -116,7 +85,13 @@ public void accept(Throwable throwable) {
11685 // we don't fail event if it's already failed with same exception as we don't want to process
11786 // the exception twice;at this point, the exception could be failed by the default auth failure handler
11887 if (!routingContext .response ().ended () && !throwable .equals (routingContext .failure ())) {
119- routingContext .fail (throwable );
88+ if (throwable instanceof DoDenyException doDenyException ) {
89+ // HTTP Security policy has forbidden access
90+ doDeny (doDenyException .augmentedIdentityUni , routingContext ,
91+ doDenyException .policyWhichForbiddenAccess , doDenyException .augmentedIdentity );
92+ } else {
93+ routingContext .fail (throwable );
94+ }
12095 } else if (throwable instanceof AuthenticationFailedException ) {
12196 log .debug ("Authentication challenge is required" );
12297 } else if (throwable instanceof AuthenticationRedirectException ) {
@@ -125,7 +100,56 @@ public void accept(Throwable throwable) {
125100 } else {
126101 log .error ("Exception occurred during authorization" , throwable );
127102 }
103+ }
104+ });
105+ }
128106
107+ private Uni <HttpSecurityPolicy .CheckResult > doPermissionCheck (RoutingContext routingContext ,
108+ Uni <SecurityIdentity > identityUni , int index , SecurityIdentity identity ) {
109+ return policies .get (index )
110+ .checkPermission (routingContext , identityUni , context )
111+ .onItem ().transformToUni (checkResult -> {
112+ final SecurityIdentity augmentedIdentity ;
113+ final Uni <SecurityIdentity > augmentedIdentityUni ;
114+ if (checkResult .getAugmentedIdentity () != null ) {
115+ // HTTP Security policy provided a new SecurityIdentity
116+ augmentedIdentity = checkResult .getAugmentedIdentity ();
117+ augmentedIdentityUni = checkResult .getAugmentedIdentityAsUni ();
118+ } else {
119+ // this is identity either augmented by the previous HTTP Security policy, or from RoutingContext
120+ augmentedIdentity = identity ;
121+ augmentedIdentityUni = identityUni ;
122+ }
123+
124+ if (!checkResult .isPermitted ()) {
125+ var failure = new DoDenyException (policies .get (index ), augmentedIdentity , augmentedIdentityUni );
126+ return Uni .createFrom ().failure (failure );
127+ }
128+
129+ if (index + 1 == policies .size ()) {
130+ // all checks permitted access
131+ QuarkusHttpUser currentUser = (QuarkusHttpUser ) routingContext .user ();
132+ if (augmentedIdentity != null ) {
133+ if (!augmentedIdentity .isAnonymous ()
134+ && (currentUser == null || currentUser .getSecurityIdentity () != augmentedIdentity )) {
135+ // perform security identity augmentation
136+ setIdentity (augmentedIdentity , routingContext );
137+ }
138+ if (securityEventHelper .fireEventOnSuccess ()) {
139+ securityEventHelper .fireSuccessEvent (new AuthorizationSuccessEvent (augmentedIdentity ,
140+ Map .of (RoutingContext .class .getName (), routingContext )));
141+ }
142+ } else if (securityEventHelper .fireEventOnSuccess ()
143+ && permissionCheckPerformed (policies , routingContext , index )) {
144+ securityEventHelper .fireSuccessEvent (
145+ new AuthorizationSuccessEvent (
146+ currentUser == null ? null : currentUser .getSecurityIdentity (),
147+ Map .of (RoutingContext .class .getName (), routingContext )));
148+ }
149+ return Uni .createFrom ().item (checkResult );
150+ } else {
151+ // perform the next check
152+ return doPermissionCheck (routingContext , augmentedIdentityUni , index + 1 , augmentedIdentity );
129153 }
130154 });
131155 }
@@ -206,9 +230,23 @@ private static boolean permissionCheckPerformed(List<HttpSecurityPolicy> permiss
206230 RoutingContext routingContext , int index ) {
207231 // the path matching policy is not permission check itself, it selects policy based on
208232 // configured HTTP permissions, but if there are no path matching HTTP permissions, there is no check
209- if (index == 1 && permissionCheckers .get (0 ) instanceof AbstractPathMatchingHttpSecurityPolicy ) {
233+ if (index == 0 && permissionCheckers .get (0 ) instanceof AbstractPathMatchingHttpSecurityPolicy ) {
210234 return AbstractPathMatchingHttpSecurityPolicy .policyApplied (routingContext );
211235 }
212236 return index > 0 ;
213237 }
238+
239+ private static final class DoDenyException extends RuntimeException {
240+
241+ private final HttpSecurityPolicy policyWhichForbiddenAccess ;
242+ private final SecurityIdentity augmentedIdentity ;
243+ private final Uni <SecurityIdentity > augmentedIdentityUni ;
244+
245+ private DoDenyException (HttpSecurityPolicy policyWhichForbiddenAccess , SecurityIdentity augmentedIdentity ,
246+ Uni <SecurityIdentity > augmentedIdentityUni ) {
247+ this .policyWhichForbiddenAccess = policyWhichForbiddenAccess ;
248+ this .augmentedIdentity = augmentedIdentity ;
249+ this .augmentedIdentityUni = augmentedIdentityUni ;
250+ }
251+ }
214252}
0 commit comments