Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

map Jakarta.servlet.http.HttpServletRequest to org.eclipse.jetty.server.Request #11964

Closed
fgolzari opened this issue Jun 26, 2024 · 11 comments
Closed
Labels

Comments

@fgolzari
Copy link

fgolzari commented Jun 26, 2024

Jetty Version
jetty-12

Jetty Environment
ee10

Java Version
Java 21

Question
I want to upgrade our project from jetty 9 to 12. In the previous version, a servlet class was written in our project, whose doGet method is as follows

public void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException  {

     Request request= (req instanceof Request) ? (Request) req : null;
     
     //other codes

     UserIdentity identity= loginService.login(null, token, request);
      
      //other codes
      
      Authentication authentication= new UserAuthentication(...);
      request.setAthentication(authentication);
      
      //other codes
}

Request package : org.eclipse.jetty.server.Request
HttpServletRequest package: javax.servlet.http.HttpServletRequest
UserIdentity package: org.eclipse.jetty.server.UserIdentity
and loginService is a object of : org.eclipse.jetty.security.LoginService

Because that in jetty 9 org.eclipse.jetty.server.Request extends javax.servlet.http.HttpServletRequest, we cast HttpServletRequest class to Request class and therefore we did not have any problems in the code.
but in jetty 12 org.eclipse.jetty.server.Request does not extend HttpServletRequest.
loginService.login() in jetty 12 Takes org.eclipse.jetty.server.Request as input,
Furthermore HttpServletRequest does not have a method to set authentication, so we need to have the Request object

How do I map HttpServletRequest that I received as input of the servlet method into org.eclipse.jetty.server.Request that I can pass to LoginService.login() method? How do I initialize org.eclipse.jetty.server.Request to send to this method?

Other than mapping, is there a better solution?

@joakime
Copy link
Contributor

joakime commented Jun 26, 2024

The core LoginService is not the place you call login() and where you get a UserIdentity out of it.

That's the role of LoginAuthenticator, it has the login() method you want.

/**
* If the UserIdentity returned from
* {@link LoginService#login(String, Object, Request, Function)} is not null, it
* is assumed that the user is fully authenticated and we need to change the session id to prevent
* session fixation vulnerability. If the UserIdentity is not necessarily fully
* authenticated, then subclasses must override this method and
* determine when the UserIdentity IS fully authenticated and renew the session id.
*
* @param username the username of the client to be authenticated
* @param password the user's credential
* @param request the inbound request that needs authentication
*/
public UserIdentity login(String username, Object password, Request request, Response response)

To see a LoginAuthenticator in action, see the ServerUpgradeRequestTest.java

The key takeaway is that the login occurs via the Servlet authentication techniques, based on constraints.
If you get to the point of a HttpServletRequest existing, you are already past the point where servlet constraints are implemented (where login occurs).

@joakime
Copy link
Contributor

joakime commented Jun 26, 2024

Furthermore HttpServletRequest does not have a method to set authentication, so we need to have the Request object

Sounds like what you are looking for is the HttpServletRequest.login() method.

https://github.com/jakartaee/servlet/blob/6.0.0-RELEASE/api/src/main/java/jakarta/servlet/http/HttpServletRequest.java#L549-L574

Been there since Servlet 3.0

Example usage: https://github.com/jetty/jetty.project/blob/jetty-12.0.x/jetty-ee10/jetty-ee10-demos/jetty-ee10-demo-jetty-webapp/src/main/java/org/example/LoginServlet.java

@fgolzari
Copy link
Author

fgolzari commented Jul 2, 2024

Thanks a lot Mr @joakime for your answer.

The key takeaway is that the login occurs via the Servlet authentication techniques, based on constraints.
If you get to the point of a HttpServletRequest existing, you are already past the point where servlet constraints are implemented (where login occurs).

I did not understand this part of your speech. Thank you for giving me a clearer explanation.

My main problem right now is:
in the old version, Our code worked in such a way that we had implemented a Filter in our code and when deploying war file on Jetty, we identified this Filter to it and Jetty itself passed Request extends HttpServletRequest to it.
Now, instead of Filter, how should I call the login method (in what way or in which method) so that I can introduce it to Jetty exactly like Filter, and Jetty itself fills Request and sends it to my code.
What should I replace now so that I can introduce it to Jetty exactly like Filter and it will fill Request and send it to my code?

In general, how is the login operation done in Jetty 12? Please send me an example of it?
How can I override the login action that is implemented in the Jetty and make it known to Jetty and leave the entire work to Jetty itself?

Jetty is not embeded in our code and we deploy our project on Jetty. It's just that we have used the login method of Jetty library in our code. And I want to write the code in such a way that Jetty itself turns the request that comes to it from client into Request and calls our login method.

@gregw
Copy link
Contributor

gregw commented Jul 2, 2024

A few more comments that might be helpful.

If you use the EE8 or EE9 environment of jetty-12, then the Servlet Request passed does extend a nested.Request in the same way that jetty-9 did. It might be simpler to get your app working first on ee8 or ee9 and then migrate to ee10.

However, as @joakime has said, it looks like your application is trying to mimic the behaviour of a SecurityHandler. Rather than replicate that code, it may be far simpler to implement your own Authenticator rather than write a handler that is doing Authentication. Note that in EE10, the request is not mutable and authentication is done by wrapping rather than setting.

@fgolzari
Copy link
Author

fgolzari commented Jul 2, 2024

@joakime , @gregw
I have made a very big mistake in explaining the problem
And that is that we call these codes inside the filter and not inside the servlet.

public void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException  {
     Request request= (req instanceof Request) ? (Request) req : null;
     //other codes
     UserIdentity identity= loginService.login(null, token, request);
      //other codes
      Authentication authentication= new UserAuthentication(...);
      request.setAthentication(authentication);
      //other codes
}

In fact, this part is not the method of doGet of a servlet, but the implementation of doFilter method of a Filter

public void doFilter(HttpServletRequest req, HttpServletResponse res,FilterChain Chain) throws ServletException, IOException  {
     Request request= (req instanceof Request) ? (Request) req : null;
     //other codes
     UserIdentity identity= loginService.login(null, token, request);
      //other codes
      Authentication authentication= new UserAuthentication(...);
      request.setAthentication(authentication);
      //other codes
}

@joakime
Copy link
Contributor

joakime commented Jul 2, 2024

I have made a very big mistake in explaining the problem
And that is that we call these codes inside the filter and not inside the servlet.

If you are handling the request in a Filter or Servlet, then you are already dispatched to the Filter Chain, and that means you are past the point of Servlet constraints and Servlet authentication. (The "Authentication" of the request is already set by this point)

Instead of doing that login in a Filter, write a proper authentication layer, and define the constraints in your webapp to use that authentication layer (you wont have that Filter too).

Other libraries that want security / authentication / authorization, but want to handle it during a Servlet dispatch (in a Filter or Servlet) do not use actually use the Servlet spec authentication / constraints to accomplish it. (eg: Spring security, and many others) But this means an entirely new security framework and APIs that your webapps must use instead of the Servlet security APIs.

@joakime
Copy link
Contributor

joakime commented Jul 2, 2024

This is a very high level description of the steps involved. (there's a lot of nuance and detail missing)

  1. Connnection arrives on network
  2. HTTP protocol is parsed
  3. Request object is created
  4. WebApp to call is determined based on contextPath of available WebApps and path present on Request
  5. WebApp receives Request handling
  6. Servlet Security is handled
  7. Session handling occurs
  8. FilterChain is calculated (all Filters to eventual end Servlet)
  9. Constraints based on FilterChain performed
  10. HttpServletRequest and HttpServletResponse are created
  11. FilterChain servicing is called (first Filter, or if no Filters, the Servlet itself)
  12. FilterChain is determined based on

The LoginService and things like the Authenticator happens around step 6.

You are attempting to do things all the way down at step 12.

@fgolzari
Copy link
Author

fgolzari commented Jul 2, 2024

@gregw , @joakime
Both of you mentioned that I should write my own authenticator.
Could you please show me an example of doing this in a project on GitHub or anywhere else?

Is it possible to write my own authentication using handlers?
Jetty 12 is still too dumb for me

And the next question, when deploying the project on Jetty 12, how do we introduce the handlers to it?

@gregw
Copy link
Contributor

gregw commented Jul 3, 2024

@fgolzari It is possible to write your "own authentication using a handler". You will need to reverse engineer what the SecurityHandler is doing and do that yourself.

But why do that? the SecurityHandler is designed to be pluggable with Authenticators. So just plug in your own algorithm there and don't duplicate all the effort in SecurityHandler.
Start of simple by looking at org.eclipse.jetty.security.authentication.BasicAuthenticator to see how that works for very simple authentication of every request.
If your authentication wants to be session based, then look at org.eclipse.jetty.security.authentication.FormAuthenticator.

For a more complex Authenticator you can look at org.eclipse.jetty.security.openid.OpenIdAuthenticator or this PR that is adding an Authenticator for etherium.

As for adding handlers to jetty-12, there are many answers that depend on how you are using the server, if you are using servlets etc. etc. Have a read of the documentation available via https://jetty.org/

@gregw
Copy link
Contributor

gregw commented Jul 3, 2024

.... and yet another approach.... If you really want to do it in a filter, then just so the request you forward to return the values you want for authentication. No need to modify the underlying request, as you have already passed the votes authentication later and we don't think you are authenticated no matter what you do in a filter.

@gregw
Copy link
Contributor

gregw commented Jul 3, 2024

I think we've given you several directions to try, so I'm closing this now.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants