1919
2020package org .apache .druid .security .pac4j ;
2121
22- import com .google .common .collect .ImmutableMap ;
2322import org .apache .druid .java .util .common .logger .Logger ;
2423import org .apache .druid .server .security .AuthConfig ;
2524import org .apache .druid .server .security .AuthenticationResult ;
2625import org .pac4j .core .config .Config ;
27- import org .pac4j .core .context .JEEContext ;
28- import org .pac4j .core .context .session .SessionStore ;
29- import org .pac4j .core .engine .CallbackLogic ;
3026import org .pac4j .core .engine .DefaultCallbackLogic ;
3127import org .pac4j .core .engine .DefaultSecurityLogic ;
32- import org .pac4j .core .engine . SecurityLogic ;
33- import org .pac4j .core . http . adapter . JEEHttpActionAdapter ;
34- import org .pac4j .core . profile . UserProfile ;
28+ import org .pac4j .core .exception . http . HttpAction ;
29+ import org .pac4j .jee . context . JEEContext ;
30+ import org .pac4j .jee . http . adapter . JEEHttpActionAdapter ;
3531
3632import javax .servlet .Filter ;
3733import javax .servlet .FilterChain ;
4238import javax .servlet .http .HttpServletRequest ;
4339import javax .servlet .http .HttpServletResponse ;
4440import java .io .IOException ;
45- import java .util .Collection ;
4641
4742public class Pac4jFilter implements Filter
4843{
4944 private static final Logger LOGGER = new Logger (Pac4jFilter .class );
5045
5146 private final Config pac4jConfig ;
52- private final SecurityLogic <Object , JEEContext > securityLogic ;
53- private final CallbackLogic <Object , JEEContext > callbackLogic ;
54- private final SessionStore <JEEContext > sessionStore ;
55-
47+ private final Pac4jSessionStore sessionStore ;
48+ private final String callbackPath ;
5649 private final String name ;
5750 private final String authorizerName ;
5851
59- public Pac4jFilter (String name , String authorizerName , Config pac4jConfig , String cookiePassphrase )
52+ public Pac4jFilter (
53+ String name ,
54+ String authorizerName ,
55+ Config pac4jConfig ,
56+ String callbackPath ,
57+ String cookiePassphrase
58+ )
6059 {
6160 this .pac4jConfig = pac4jConfig ;
62- this .securityLogic = new DefaultSecurityLogic <>();
63- this .callbackLogic = new DefaultCallbackLogic <>();
64-
61+ this .callbackPath = callbackPath ;
6562 this .name = name ;
6663 this .authorizerName = authorizerName ;
67-
68- this .sessionStore = new Pac4jSessionStore <>(cookiePassphrase );
64+ this .sessionStore = new Pac4jSessionStore (cookiePassphrase );
6965 }
7066
7167 @ Override
7268 public void init (FilterConfig filterConfig )
7369 {
7470 }
7571
76-
7772 @ Override
7873 public void doFilter (ServletRequest servletRequest , ServletResponse servletResponse , FilterChain filterChain )
79- throws IOException , ServletException
74+ throws IOException , ServletException
8075 {
8176 // If there's already an auth result, then we have authenticated already, skip this or else caller
8277 // could get HTTP redirect even if one of the druid authenticators in chain has successfully authenticated.
@@ -85,38 +80,59 @@ public void doFilter(ServletRequest servletRequest, ServletResponse servletRespo
8580 return ;
8681 }
8782
88- HttpServletRequest httpServletRequest = (HttpServletRequest ) servletRequest ;
89- HttpServletResponse httpServletResponse = (HttpServletResponse ) servletResponse ;
90- JEEContext context = new JEEContext (httpServletRequest , httpServletResponse , sessionStore );
83+ HttpServletRequest request = (HttpServletRequest ) servletRequest ;
84+ HttpServletResponse response = (HttpServletResponse ) servletResponse ;
85+ JEEContext context = new JEEContext (request , response );
86+
87+ if (request .getRequestURI ().equals (callbackPath )) {
88+ DefaultCallbackLogic callbackLogic = new DefaultCallbackLogic ();
89+ String originalUrl = (String ) request .getSession ().getAttribute ("pac4j.originalUrl" );
90+ String redirectUrl = originalUrl != null ? originalUrl : "/" ;
9191
92- if (Pac4jCallbackResource .SELF_URL .equals (httpServletRequest .getRequestURI ())) {
9392 callbackLogic .perform (
94- context ,
95- pac4jConfig ,
96- JEEHttpActionAdapter .INSTANCE ,
97- "/" ,
98- true , false , false , null );
93+ context ,
94+ sessionStore ,
95+ pac4jConfig ,
96+ JEEHttpActionAdapter .INSTANCE ,
97+ redirectUrl , // Redirect to original URL or root
98+ null ,
99+ null
100+ );
99101 } else {
100- UserProfile profile = (UserProfile ) securityLogic .perform (
101- context ,
102- pac4jConfig ,
103- (JEEContext ctx , Collection <UserProfile > profiles , Object ... parameters ) -> {
104- if (profiles .isEmpty ()) {
105- LOGGER .warn ("No profiles found after OIDC auth." );
106- return null ;
107- } else {
108- return profiles .iterator ().next ();
109- }
110- },
111- JEEHttpActionAdapter .INSTANCE ,
112- null , "none" , null , null );
113- // Changed the Authorizer from null to "none".
114- // In the older version, if it is null, it simply grant access and returns authorized.
115- // But in the newer pac4j version, it uses CsrfAuthorizer as default, And because of this, It was returning 403 in API calls.
116- if (profile != null && profile .getId () != null ) {
117- AuthenticationResult authenticationResult = new AuthenticationResult (profile .getId (), authorizerName , name , ImmutableMap .of ("profile" , profile ));
118- servletRequest .setAttribute (AuthConfig .DRUID_AUTHENTICATION_RESULT , authenticationResult );
119- filterChain .doFilter (servletRequest , servletResponse );
102+ DefaultSecurityLogic securityLogic = new DefaultSecurityLogic ();
103+ try {
104+ securityLogic .perform (
105+ context ,
106+ sessionStore ,
107+ pac4jConfig ,
108+ (ctx , session , profiles , parameters ) -> {
109+ try {
110+ // Extract user ID from pac4j profiles and create AuthenticationResult
111+ if (profiles != null && !profiles .isEmpty ()) {
112+ String uid = profiles .iterator ().next ().getId ();
113+ if (uid != null ) {
114+ AuthenticationResult authenticationResult = new AuthenticationResult (uid , authorizerName , name , null );
115+ servletRequest .setAttribute (AuthConfig .DRUID_AUTHENTICATION_RESULT , authenticationResult );
116+ filterChain .doFilter (servletRequest , servletResponse );
117+ }
118+ } else {
119+ LOGGER .warn ("No profiles found after OIDC auth." );
120+ // Don't continue the filter chain - let pac4j handle the authentication failure
121+ }
122+ }
123+ catch (IOException | ServletException e ) {
124+ throw new RuntimeException (e );
125+ }
126+ return null ;
127+ },
128+ JEEHttpActionAdapter .INSTANCE ,
129+ null ,
130+ "none" , // Use "none" instead of authorizerName to avoid CSRF issues
131+ null
132+ );
133+ }
134+ catch (HttpAction e ) {
135+ JEEHttpActionAdapter .INSTANCE .adapt (e , context );
120136 }
121137 }
122138 }
@@ -125,4 +141,4 @@ public void doFilter(ServletRequest servletRequest, ServletResponse servletRespo
125141 public void destroy ()
126142 {
127143 }
128- }
144+ }
0 commit comments