How to implement openIdConnect on a Camel-REST consumer - keycloak

I have an OAS specification requiring openIdConnect as a securityScheme on all operations. The consumer will be deployed on in an OSGI container (Karaf 4.4.1). The camel version used is 3.18.4 (but could be moved higher will require some work but doable).
The rest consumer is described using blueprint:
<restConfiguration component="netty-http" port="{{LISTEN_PORT}}" host="{{LISTEN_HOST}}" apiComponent="rest-api">
</restConfiguration>
<rest path="/base_path" consumes="application/json" produces="application/json">
<securityDefinitions>
<openIdConnect key="openIdConnect" description="OpenIdConnect via KeyCloak" url="http://localhost:8080/realms/publishvisit-entry/.well-known/openid-configuration"/>
</securityDefinitions>
<securityRequirements key="openIdConnect"/>
<post path="/entities" >
<param name="body" type="body" required="true"/>
<security key="openIdConnect"/>
<to uri="direct:handle-entities" />
</post>
</rest>
Just to keep things simple (all components are run a local development system not using https yet for the openId discovery (and KeyCloak is running in dev mode).
The problem with this config is wether I have a token (which would make sense) or not (the problem) the route is executed. I would expect an error to be returned when no token is send in the request or an expired a token is send.
It seems no validate on the security is being done.
The Rest component is supposed to be used for both consumer as well producers, I am (for now) only interested in the consumer part.

Related

Elytron programmatic login with FORM authentication

we are currently migrating from legacy security subsystem to Elytron and have a Struts2 based web application deployed in JBoss EAP 7.3.6 which should support multiple "flavors" of authentication.
The standard way of logging in should be that a user manually provides credentials in a login form (j_security_check) and clicks the corresponding button. This works well with Elytron in our setup.
The second possibility is, that the GET request to protected content of the web application can contain a custom cookie that contains a JWT token. This cookie is intercepted by a io.undertow.server.HttpHandler which deals with the incoming request in its io.undertow.server.HttpHandler#handleRequest method. This handler is registered by io.undertow.servlet.api.DeploymentInfo#addSecurityWrapper with a DeploymentInfo which is provided by an implementation of io.undertow.servlet.ServletExtension. The ServletExtension is registered as a service provider in META-INF/services/io.undertow.servlet.ServletExtension.
The request handling in our implementation of io.undertow.server.HttpHandler#handleRequest extracts the JWT token from the cookie, pre-validates it and determines the contained username. This username and the token as a password are used as inputs for a call to javax.servlet.http.HttpServletRequest#login.
With the legacy security subsystem, the behavior of the server was, that this call to login triggered the authentication against the configured legacy security domain AND created a session in Undertow so that the HTTP 200 response for the previous GET request contained a Set-Cookie header with a fresh JSESSIONID cookie.
With Elytron, javax.servlet.http.HttpServletRequest#login doesn't do anything, neither an authentication against an Elytron security domain and security realm nor the creation of a session is triggered. The browser simply shows the login form which should get skipped by the described interception process.
I debugged the implementation of javax.servlet.http.HttpServletRequest#login that comes with JBoss. We start in io.undertow.servlet.spec.HttpServletRequestImpl#login which calls login = sc.login(username, password). This SecurityContext, when using Elytron, is org.wildfly.elytron.web.undertow.server.SecurityContextImpl. org.wildfly.elytron.web.undertow.server.SecurityContextImpl#login first checks if (httpAuthenticator == null). The httpAuthenticator is only set in org.wildfly.elytron.web.undertow.server.SecurityContextImpl#authenticate which gets called by a call to javax.servlet.http.HttpServletRequest#authenticate.
This explains, why a plain call to io.undertow.servlet.spec.HttpServletRequestImpl#login was doing nothing. I tried to call javax.servlet.http.HttpServletRequest#authenticate first, to instantiate that httpAuthenticator internally, and then javax.servlet.http.HttpServletRequest#login. This at least finally triggered the authentication and authorization against the configured Elytron security domain and security realm. Authentication/authorization were successful but Undertow still didn't issue a new JSESSIONID cookie and the browser again showed the login form instead of proceeding to the protected resources.
I'm currently out of ideas, how to proceed with this issue und how to achieve the same behavior as with the legacy security subsystem. Why does the Elytron implementation of io.undertow.security.api.SecurityContext behave so differently compared to the one for legacy security (io.undertow.security.impl.SecurityContextImpl)? How am I supposed to log in programatically in a FORM based web application using Elytron with javax.servlet.http.HttpServletRequest#login and/or javax.servlet.http.HttpServletRequest#authenticate?
The relevant JBoss configuration for all this looks like this:
Undertow:
<application-security-domains>
<application-security-domain name="my_app_security_domain" http-authentication-factory="MyHttpAuthFactory"/>
</application-security-domains>
Elytron:
<security-domains>
<security-domain name="MySecurityDomain" default-realm="MyCachingRealm" permission-mapper="default-permission-mapper">
<realm name="MyCachingRealm" role-decoder="FromRolesAttributeDecoder"/>
</security-domain>
</security-domains>
<security-realms>
<custom-realm name="MyCustomRealm" module="module name redacted" class-name="class name redacted"/>
<caching-realm name="MyCachingRealm" realm="MyCustomRealm" maximum-age="300000"/>
<identity-realm name="local" identity="$local"/>
</security-realms>
<mappers>
<simple-permission-mapper name="default-permission-mapper" mapping-mode="first">
<permission-mapping>
<principal name="anonymous"/>
<permission-set name="default-permissions"/>
</permission-mapping>
<permission-mapping match-all="true">
<permission-set name="login-permission"/>
<permission-set name="default-permissions"/>
</permission-mapping>
</simple-permission-mapper>
<constant-realm-mapper name="local" realm-name="local"/>
<constant-realm-mapper name="MyRealmMapper" realm-name="MyCachingRealm"/>
<simple-role-decoder name="FromRolesAttributeDecoder" attribute="Roles"/>
</mappers>
<http>
<http-authentication-factory name="MyHttpAuthFactory" security-domain="MySecurityDomain" http-server-mechanism-factory="global">
<mechanism-configuration>
<mechanism mechanism-name="FORM" realm-mapper="MyRealmMapper">
<mechanism-realm realm-name="MyRealm"/>
</mechanism>
</mechanism-configuration>
</http-authentication-factory>
<provider-http-server-mechanism-factory name="global"/>
</http>
This was a bug in JBoss EAP which has been fixed in EAP 7.3.8 and 7.4.1. See https://issues.redhat.com/browse/JBEAP-21737 and https://issues.redhat.com/browse/JBEAP-21738 for details.

Apache Camel Route performance degradation under load

I'm using Apache Camel to route a SOAP request based on a certain attribute in the request message. The message is matched against a regex and if a match is found the request will be routed to "calldestination1" and if not, it will be routed to "calldestination2".
I'm using the following configuration:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:camel="http://camel.apache.org/schema/spring"
xmlns:cxf="http://camel.apache.org/schema/cxf"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring.xsd
http://camel.apache.org/schema/cxf http://camel.apache.org/schema/cxf/camel-cxf.xsd">
<!-- ... -->
<cxf:cxfEndpoint id="testEndpointTest"
address="http://localhost:8080/testEndpoint"
endpointName="s:testEndpoint_Port"
serviceName="s:testEndpoint"
wsdlURL="wsdl/testEndpoint.wsdl"
xmlns:s="http://teste.com/testEndpoint"/>
<!-- ... -->
<camelContext xmlns="http://camel.apache.org/schema/spring">
<endpoint id="calldestination1" uri="http://localhost:8080/destination1?bridgeEndpoint=true&throwExceptionOnFailure=false"/>
<endpoint id="calldestination2" uri="http://localhost:8080/destination2?bridgeEndpoint=true&throwExceptionOnFailure=false"/>
<route streamCache="true">
<!--CXF consumer using MESSAGE format-->
<from uri="cxf:bean:testEndpointTest?dataFormat=MESSAGE"/>
<choice>
<when>
<simple>${bodyAs(java.lang.String)} regex ${properties:router.regex}</simple>
<to uri="calldestination1"/>
</when>
<otherwise>
<to uri="calldestination2"/>
</otherwise>
</choice>
</route>
</camelContext>
When the destination server, where "calldestination2" runs, is under load the requests can take around 1150ms to respond. Apache Camel does not seem to handle this very well.
To replicate this behavior I used SoapUI with a SOAP MockService with a delay (OnRequest Script) and jmeter.
Fist I ran the test against SoapUI MockService with no delay and then with a 1100ms delay.
Then I configured Apache Camel to route the request to SoapUI service and repeated the tests.
JMeter -> SoapUI - 0ms delay
~1200 requests per second; 25ms request average; 0% Errors
JMeter -> SoapUI - 1100ms delay
~100 requests per second; 1128ms request average; 0% Errors
JMeter -> Apache Camel -> SoapUI - 0ms delay
~420 requests per second; 285ms request average; 0% Errors
JMeter -> Apache Camel -> SoapUI - 1100ms delay
~8 requests per second; 14800ms request average; 97.23% Errors by timeout
The timeout in Apache Camel is set to 30 seconds.
Why is Apache Camel having such a low performance in the last case and how can I improve the it?
EDIT 1:
I created a repository in GitHub that contains the Apache Camel project, SoapUI mock service and jmeter tests for easy testing.
https://github.com/jraimundo/apache-camel-route-tester
Basic problem
Such problems are always a problem of resources. As long as all components have enough resources and answer fast, all is fine. As soon as one of them encounters a resource limitation it becomes slow.
In the JMeter-SoapUI scenario, the intentional latency of SoapUI is handled by JMeter. Because SoapUI takes more than a second to respond, the JMeter requests are held open for this time. If the JMeter thread pool for requests is exhausted (all threads are waiting for an answer from SoapUI), it cannot further scale. Based on your measures, the thread pool size could be 100.
Then you put Camel in the middle. With this you introduce new thread pools. There must be one to receive requests (CXF) and probably one that sends requests (Camel HTTP). Now the SoapUI latency must also be handled by these pools. Same situation, but now the thread pools of the Camel component are the limitation.
Let's assume the thread pool for Camel HTTP requests is 10 by default. JMeter begins to send requests. If JMeter sends new requests faster than SoapUI responds, the 10 threads to send HTTP requests to SoapUI are very fast all busy (waiting for SoapUI).
New requests of JMeter arrive, but no new HTTP requests to SoapUI are possible until until one of the threads is free again. Around 8 parallel requests (from your measures) seems to be reasonable in this case.
So it is obvious that if you want to serve 100 requests per second in a scenario like this you need to tune all involved thread pools to handle this. And you must also fine tune the different timeouts (CXF, Camel HTTP).
Your code
One point I noticed in your code is that you use the Camel HTTP component for your target endpoints. That component uses the Apache HTTP client 3.x.
If you want to use a more current Apache HTTP client, you must use the Camel HTTP4 component (4 because it uses Apache HTTP client 4.x). I don't know if it makes a big difference, but the old version is declared as "end of life" since years.
Another thing are the timeouts. You write that you set the Camel timeout to 30 seconds. But that is probably not the timeout of CXF or the Apache HTTP client. HTTP clients have multiple timeouts: it can take too long to establish a connection and it can take too long to receive a response.

Spring Security multiple filter chain issue

We have a functioning application with multiple filter chain configuration. One of the first filter chains belongs to REST POST requests that requier no authentication:
...
<http pattern="/*.info**" entry-point-ref="infoEntryPoint" use-expressions="true" create-session="never">
<intercept-url pattern="/*.info" access="permitAll" />
</http>
<beans:bean id="infoEntryPoint" class="org.springframework.security.web.authentication.www.BasicAuthenticationEntryPoint">
<beans:property name="realmName" value="Info REST Realm" />
</beans:bean>...
It worked for GET and POST requests, but after upgrading to Spring Security 4, the POST requests are not captured by this filter chain, but the next filter chain captures them (and creates session and throws 403, doing right its job).
How could I fix it?
CSRF protection is switched on by default in Spring Security, it was switched off by default formerly. The following entry (inside the http tag) solved the problem:
<csrf disabled="true"/>

Use REST calls in Hybris

I've installed Hybris 6.4 and I want to make use of its secured RESTful access to all Hybris models, that comes with the platformwebservices.
In order to have access to the REST API, i.e. requests like http://localhost:9001/ws410/rest/countries, I need to configure the OAuth 2.0
I think, I need to understand what values I need to provide in the headers:
client_id=...&client_secret=...&grant_type=...&username=...&password=...?
To test, you can simply use basic authentication with username/password (admin/***). In postman select basic authentication with username and password (Update request).
Find sample request from wiki.
Webservices are getting authenticated using the configuration in security-spring.xml.
<oauth:client client-id="client-side" resource-ids="hybris" authorized-grant-types="implicit,client_credentials"
authorities="ROLE_CLIENT" secret="secret" redirect-uri="http://localhost:9001/rest/oauth2_implicit_callback" />
<oauth:client client-id="mobile_android" resource-ids="hybris"
authorized-grant-types="authorization_code,refresh_token,password,client_credentials" authorities="ROLE_CLIENT" secret="secret"
redirect-uri="http://localhost:9001/rest/oauth2_callback" />
<oauth:client client-id="trusted_client" resource-ids="hybris"
authorized-grant-types="authorization_code,refresh_token,password,client_credentials" authorities="ROLE_TRUSTED_CLIENT"
secret="secret" />
These are the different client-id and secret codes available by default.

Create a REST proxy for SOAP service in wso2 esb

We have a SOAP service that I want to proxy in WSO2 ESB. This is for POC that I am working on. I came across various documents and some explanation on forums but nothing concrete yet. I looked at API option but couldn't get anything to work. Whats the best way to do this? Can the transformation occur in esb itself or is the api the only option? Please advise.
You can better choose RESTAPI option.
Here is the documentation for that
What do you mean by couldn't get this working? This is not a big configuration, you need to define your proxy service such that you send the message (REST message) to the back end as a SOAP message. Here is a sample configuration.
<proxy name="StockQuoteProxy" transports="http https" startOnLoad="true">
<target>
<endpoint>
<address uri="http://localhost:9000/services/SimpleStockQuoteService"
format="soap11"/>
</endpoint>
<outSequence>
<send/>
</outSequence>
</target>
</proxy>
You can call this proxy in REST manner and it will automatically send the message to the backend SOAP service.
you can do it with the sample axis2 client as below.
ant stockquote -Daddurl=http://localhost:8280/services/StockQuoteProxy -Drest=true