Just one more thing ...
If a SOAP API is published on Apiman and you request the WSDL by using the
gateway endpoint URL, you get the original WSDL - address is pointing to
the real endpoint URL and not the gateway endpoint URL.
I have solved this temporarily by adding URL rewrite policies to all SOAP
APIs, which replace the real endpoint URL with the gateway endpoint URL.
I guess it would be best if this policy was handled by the gateway
internally so that you wouldn't have to worry by adding a correct policy to
the API configuration.
2016-03-29 20:29 GMT+02:00 Benjamin Kastelic <kastelic.benjamin(a)gmail.com>:
OK, now I understand what you meant.
I believe this would be a reasonable solution :)
2016-03-29 20:18 GMT+02:00 Eric Wittmann <eric.wittmann(a)redhat.com>:
> My plan was unclear! Let's go with another example:
>
> Step 1 - the Gateway receives the following HTTP request:
>
> ----
> POST /apiman-gateway/MyOrg/soap-api/2.7 HTTP/1.1
> Host:
www.example.org
> Content-Type: application/soap+xml; charset=utf-8
> X-API-Key: API-KEY-FOR-ACTIVE-CLIENT
> SOAPAction: ExampleAction
>
> <?xml version="1.0"?>
> <soap:Envelope
xmlns:soap="http://www.w3.org/2003/05/soap-envelope">
> <soap:Header>
> <ns1:Header1 xmlns:ns1="uri:ns1">foo</ns1:Header1>
> <ns2:Header2 xmlns:ns2="uri:ns2">bar</ns2:Header2>
> </soap:Header>
> <soap:Body>
> <m:GetStockPrice
xmlns:m="http://www.example.org/stock/RHT">
> <m:StockName>Red Hat</m:StockName>
> </m:GetStockPrice>
> </soap:Body>
> </soap:Envelope>
> ----
>
> We'll parse the first part of the envelope so that we can read the
> headers and make them available to any policies. After that's done, we'll
> invoke the policy chain as per normal. However, because it's a SOAP api,
> there will exist a SOAPRequestInfo object in the policy context. So
> policies can read and/or modify the soap information. This class might
> look something like this:
>
> public class SOAPRequestInfo {
> private String action;
> private String encoding;
> private Map<QName, SOAPHeader> headers;
> }
>
> This allows interested policies (like your ws-security) policy to have
> easy access to all the soap related stuff. It also allows you to alter
> these things. Including adding/removing/modifying the SOAP headers.
>
> So let's assume that we have a policy which *adds* a SOAP header
> (ns3:AddedHeader) and another policy which *removes* one (ns2:Header2).
> The policy code might look like this:
>
> SOAPRequestInfo soapInfo = context.getAttribute(
> Constants.SOAP_INFO, (SOAPRequestInfo) null);
> soapInfo.getHeaders().remove(new QName("uri:ns2", "Header2"));
> soapInfo.getHeaders().put(
> new QName("uri:ns3", "AddedHeader"),
> createSoapHeader()
> );
>
>
> In that case, this is the HTTP request that will be sent/proxied to the
> back-end API:
>
> ----
> POST / HTTP/1.1
> Host:
www.example.org
> Content-Type: application/soap+xml; charset=utf-8
> SOAPAction: ExampleAction
>
> <?xml version="1.0"?>
> <soap:Envelope
xmlns:soap="http://www.w3.org/2003/05/soap-envelope">
> <soap:Header>
> <ns1:Header1 xmlns:ns1="uri:ns1">foo</ns1:Header1>
> <ns3:AddedHeader xmlns:ns3="uri:ns3">bar</ns3:AddedHeader>
> </soap:Header>
> <soap:Body>
> <m:GetStockPrice
xmlns:m="http://www.example.org/stock/RHT">
> <m:StockName>Red Hat</m:StockName>
> </m:GetStockPrice>
> </soap:Body>
> </soap:Envelope>
> ----
>
> Sound reasonable?
>
> -Eric
>
>
> On 3/29/2016 1:42 PM, Benjamin Kastelic wrote:
>
>> If I understand you correctly Eric, only the soap:Body part will be
>> forwarded to the registered API?
>> What if the API still needs to receive the soap:Header part?
>>
>> 2016-03-29 15:34 GMT+02:00 Keith Babo <kbabo(a)redhat.com
>> <mailto:kbabo@redhat.com>>:
>>
>> Sounds cool to me. Header policy will need to be QName and
>> DOM-aware, so that namespace-qualified headers can be added and
>> appropriate namespace definitions can be added to the DOM if
>> required. Of course you already know all this, but pointing it out
>> makes me feel useful.
>>
>> > On Mar 29, 2016, at 8:38 AM, Eric Wittmann
>> <eric.wittmann(a)redhat.com <mailto:eric.wittmann@redhat.com>>
wrote:
>> >
>> > That's an interesting idea. It'd be harder to inject the
headers
>> into the request than it is to inject HTTP request headers,
>> obviously. But not impossible. We'd need to be aware of the change
>> to any Content-Length that may be set.
>> >
>> > This makes the implementation slightly more complicated, because
>> I think the HTTP connector will need to be made smarter (it will
>> need to send the <soap:Envelope> and <soap:Header> sections
first,
>> then just stream the remaining request body as-is.
>> >
>> > So perhaps what we have is this:
>> >
>> > 1. <?xml version="1.0"?>
>> > 2. <soap:Envelope
>>
xmlns:soap="http://www.w3.org/2003/05/soap-envelope">
>> > 3. <soap:Header>
>> > 4. <ns1:Foo actor="..."
xmlns:ns1="uri:ns1">BAR</ns1:Foo>
>> > 5. </soap:Header>
>> > 6. <soap:Body>
>> > 7. <m:GetStockPrice
>>
xmlns:m="http://www.example.org/stock/Surya">
>> > 8. <m:StockName>IBM</m:StockName>
>> > 9. </m:GetStockPrice>
>> > 10. </soap:Body>
>> > 11. </soap:Envelope>
>> >
>> > Lines 1-5 will be read and consumed by apiman *before* any
>> policies are executed. We'll actually keep reading until we find
>> <soap:Body> in the request body. We'll throw out everything
before
>> line #6 from our in-memory buffer, resulting in a buffer with just
>> line #6 (and any extra bytes after that based on our I/O chunk size).
>> >
>> > The policies will be executed, which may result in soap headers
>> being added, modified, or removed. If all policies pass, then we
>> proxy the request to the back-end. Normally the HTTP connection
>> would simply send all bytes from the HTTP request as-is. Instead,
>> we'll need to *generate* new content for lines 1-5, with the newly
>> modified soap headers. Once the generated content is sent, then we
>> send the contents of the in-memory buffer (which contains line #6+
>> any additional bytes). After that, we proxy the remaining bytes
>> from the HTTP request as-is.
>> >
>> > This may be starting to take shape. :)
>> >
>> > Additional thoughts?
>> >
>> > -Eric
>> >
>> > PS: @Keith - we'll likely have a separate Policy for
>> manipulating SOAP headers, rather than re-use the existing HTTP
>> headers policy.
>> >
>> > On 3/29/2016 8:13 AM, Keith Babo wrote:
>> >> Sounds like a reasonable first step to me. Just to make life
>> slightly
>> >> more complicated, will the headers policy be updated to allow
>> add/remove
>> >> of SOAP:Headers? :-)
>> >>
>> >> ~ keith
>> >>
>> >>> On Mar 29, 2016, at 7:52 AM, Eric Wittmann
>> <eric.wittmann(a)redhat.com <mailto:eric.wittmann@redhat.com>
>> >>> <mailto:eric.wittmann@redhat.com
>> <mailto:eric.wittmann@redhat.com>>> wrote:
>> >>>
>> >>> OK so here's what I propose (feedback welcome):
>> >>>
>> >>> *If* an API's 'type' is set to SOAP, then we will
*always* look
>> for a
>> >>> soap envelope in the body. If no body is found or no soap
>> envelope is
>> >>> found in the body, then a standard apiman error will be
thrown.
>> >>>
>> >>> If an envelope *is* found, then we will read the body of the
>> HTTP
>> >>> request until we find "<soap:Body>". We'll
extract the
>> <soap:Header>
>> >>> and parse its children. While parsing, we'll obviously
keep
>> the data
>> >>> we read in a memory buffer. Once parsing is done, we'll
>> include the
>> >>> soap headers, soap action, and the global encoding type in
some
>> sort
>> >>> of soapinfo object and include that in the policy context.
>> >>>
>> >>> Finally, after all that is done, we'll process the request
as
>> normal,
>> >>> executing the policy chain, then processing the request body,
>> etc. The
>> >>> entire request payload will still be processed (remember that
>> we saved
>> >>> the bytes we read in a memory buffer).
>> >>>
>> >>> So from the perspective of a policy, everything will look
>> identical
>> >>> except that a SOAPInfo object will be available in the policy
>> context.
>> >>>
>> >>> Thoughts?
>> >>>
>> >>> -Eric
>> >>>
>> >>> On 3/28/2016 1:48 PM, Benjamin Kastelic wrote:
>> >>>> Yup, I agree. That would probably be best, since several
>> validators
>> >>>> (wss4j for example) require DOM Elements
>> (javax.xml.soap.SOAPHeader) to
>> >>>> function.
>> >>>>
>> >>>> Best regards,
>> >>>> Benjamin
>> >>>>
>> >>>> 2016-03-28 19:14 GMT+02:00 Eric Wittmann
>> <eric.wittmann(a)redhat.com <mailto:eric.wittmann@redhat.com>
>> >>>> <mailto:eric.wittmann@redhat.com
>> <mailto:eric.wittmann@redhat.com>>
>> >>>> <mailto:eric.wittmann@redhat.com
>> <mailto:eric.wittmann@redhat.com>>>:
>> >>>>
>> >>>> Thanks! In that case, making the headers available as
DOM
>> Element
>> >>>> objects (perhaps with a simple QName based lookup) would
be
>> best.
>> >>>>
>> >>>> -Eric
>> >>>>
>> >>>> On 3/28/2016 12:39 PM, Keith Babo wrote:
>> >>>>
>> >>>> SOAP:Headers can be complex types. WS-Security is a
good
>> >>>> example of
>> >>>> this in practice.
>> >>>>
>> >>>> ~ keith
>> >>>>
>> >>>> On Mar 28, 2016, at 11:37 AM, Eric Wittmann
>> >>>> <eric.wittmann(a)redhat.com
>> <mailto:eric.wittmann@redhat.com>
>> >>>> <mailto:eric.wittmann@redhat.com
>>
<mailto:eric.wittmann@redhat.com>><mailto:eric.wittmann@redhat.com
>> <mailto:eric.wittmann@redhat.com>>
>> >>>> <mailto:eric.wittmann@redhat.com
>> <mailto:eric.wittmann@redhat.com>
>> >>>> <mailto:eric.wittmann@redhat.com
>> <mailto:eric.wittmann@redhat.com>>>> wrote:
>> >>>>
>> >>>> That's a bit hacky, but also sort of a
genius
>> approach as
>> >>>> well. I'm
>> >>>> actually a little bummed I didn't think of
it. :)
>> >>>>
>> >>>> As for extending SOAP support - I was thinking
that
>> I could
>> >>>> make the
>> >>>> relevant changes to apiman if you would be
willing
>> to provide
>> >>>> feedback/guidance/testing. My SOAP expertise is
>> quite stale
>> >>>> at this
>> >>>> point, so having some eyeballs on these changes
>> would be
>> >>>> very useful.
>> >>>>
>> >>>> To start off with, what pieces of the SOAP
envelope
>> should
>> >>>> be extracted
>> >>>> prior to calling the policy chain? Some
candidates
>> are:
>> >>>>
>> >>>> * The encoding style
>> >>>> * All SOAP headers
>> >>>> * SOAPAction (already available as an HTTP
header)
>> >>>> * ???
>> >>>>
>> >>>> For the soap headers, all of the examples
I've seen
>> take the
>> >>>> following
>> >>>> form:
>> >>>>
>> >>>> <HeaderName
>> xmlns="elementNS">Header-Value</HeaderName>
>> >>>>
>> >>>> It can also have the optional "actor"
or
>> "mustUnderstand"
>> >>>> attributes.
>> >>>> The SOAP envelope schema is pretty lax though,
so
>> I'm not
>> >>>> sure if the
>> >>>> above is a convention or a rule. Can headers be
more
>> >>>> complex than the
>> >>>> above?
>> >>>>
>> >>>> -Eric
>> >>>>
>> >>>> On 3/26/2016 7:06 AM, Benjamin Kastelic wrote:
>> >>>>
>> >>>> Hi,
>> >>>>
>> >>>> I temporarily solved the problem by storing
the
>> request
>> >>>> body, which is
>> >>>> contained in ApiRequest.rawRequest object, in
a
>> >>>> temporary buffer. I then
>> >>>> process the data (authentication) and based
on
>> the
>> >>>> results proceed with
>> >>>> the policy chain or report a failure. Then
in
>> the end()
>> >>>> method of the
>> >>>> requestDataHandler method I write the
contents
>> of my
>> >>>> temporary buffer
>> >>>> using super.write(IApimanBuffer). That way I
can
>> forward
>> >>>> the request to
>> >>>> then ext policy in the chain. But this is
still
>> a hacky
>> >>>> way of doing
>> >>>> this.
>> >>>>
>> >>>> I would be glad to help with extending SOAP
>> support. But
>> >>>> I would need a
>> >>>> few pointers where to start. The way of
storing
>> SOAP
>> >>>> headers in the
>> >>>> ApiRequest object seems like a good idea.
>> >>>>
>> >>>> 2016-03-24 18:40 GMT+01:00 Eric Wittmann
>> >>>> <eric.wittmann(a)redhat.com
>> <mailto:eric.wittmann@redhat.com>
>> >>>> <mailto:eric.wittmann@redhat.com
>>
<mailto:eric.wittmann@redhat.com>><mailto:eric.wittmann@redhat.com
>> <mailto:eric.wittmann@redhat.com>>
>> >>>> <mailto:eric.wittmann@redhat.com
>> <mailto:eric.wittmann@redhat.com>
>> >>>> <mailto:eric.wittmann@redhat.com
>> <mailto:eric.wittmann@redhat.com>>>
>> >>>> <mailto:eric.wittmann@redhat.com
>> <mailto:eric.wittmann@redhat.com>
>> >>>> <mailto:eric.wittmann@redhat.com
>> <mailto:eric.wittmann@redhat.com>>>>:
>> >>>>
>> >>>> Hi Benjamin - thanks for the excellent
>> question. I
>> >>>> will do my best
>> >>>> to answer and note that I am CC'ing
the
>> apiman-dev
>> >>>> mailing list so
>> >>>> others can chime in.
>> >>>>
>> >>>> First let me say that a WS-Security
policy
>> sounds
>> >>>> great - we haven't
>> >>>> focused much on the WS-* protocols
because
>> we get
>> >>>> much more demand
>> >>>> for managing REST APIs than SOAP APIs.
That
>> said,
>> >>>> better SOAP
>> >>>> support is certainly on the radar. When
that
>> >>>> happens, my hope is
>> >>>> that processing the envelope might be a
core
>> part of
>> >>>> the gateway and
>> >>>> so implementing policies that use
>> information in
>> >>>> there will be
>> >>>> easier. Perhaps your implementation can
be
>> the
>> >>>> genesis of some of
>> >>>> that work!
>> >>>>
>> >>>> To your question - without core changes
to
>> apiman,
>> >>>> the approach you
>> >>>> *need* to take is to have your policy
>> implement
>> >>>> IDataPolicy. I
>> >>>> believe you may have already tried that,
and
>> >>>> observed that you
>> >>>> cannot send proper policy failures from
that
>> >>>> method. You are right
>> >>>> - that's something we will need to
fix! I
>> think you
>> >>>> should be able
>> >>>> to throw a runtime exception from the
>> >>>> write(IApimanBuffer chunk)
>> >>>> method if you detect an error. However,
>> this is a
>> >>>> little bit hacky!
>> >>>>
>> >>>> Instead, I suggest (if you're up for
it)
>> that we
>> >>>> perhaps work
>> >>>> together to bake SOAP support directly
into
>> the core
>> >>>> of apiman, such
>> >>>> that the SOAP envelope is read/parsed
>> *before* the
>> >>>> policy chain is
>> >>>> executed. We could expose, for example,
the
>> SOAP
>> >>>> headers as a
>> >>>> proper Map<> stored either in the
context or
>> on the
>> >>>> ApiRequest.
>> >>>> This would allow you to properly
implement
>> most
>> >>>> (all?) WS-*
>> >>>> protocols as proper apiman policies in
the
>> >>>> apply(ApiRequest request)
>> >>>> method.
>> >>>>
>> >>>> Thoughts?
>> >>>>
>> >>>> -Eric
>> >>>>
>> >>>>
>> >>>> On 3/24/2016 7:58 AM, Benjamin Kastelic
>> wrote:
>> >>>>
>> >>>> Greetings,
>> >>>>
>> >>>> I first thought to write this
question
>> as an
>> >>>> issue on Github,
>> >>>> but it
>> >>>> seemed better to write you a direct
>> email.
>> >>>>
>> >>>> I am making a custom WS Security
policy,
>> that
>> >>>> reads the body and
>> >>>> check
>> >>>> the UsernameToken security header.
This
>> works
>> >>>> OK, but now I've
>> >>>> hit a wall.
>> >>>>
>> >>>> In the doApply method I get the
>> rawRequest
>> >>>> object and read the
>> >>>> body from
>> >>>> the ServletInputStream of the
request.
>> The
>> >>>> problem I'm facing
>> >>>> now is
>> >>>> that the input stream was read and
it
>> can't be
>> >>>> reset back to it's
>> >>>> initial state.
>> >>>>
>> >>>> I was also trying to implement the
same
>> logic
>> >>>> in the
>> >>>> requestDataHandler
>> >>>> method, but I don't know if it is
even
>> possible
>> >>>> to send a failure
>> >>>> message to the request chain from
there.
>> >>>>
>> >>>> Any suggesstions ?
>> >>>>
>> >>>> Best regards,
>> >>>> Benjamin
>> >>>>
>> >>>>
>> >>>>
>> >>>>
>> >>>> --
>> >>>> Lp, Benjamin
>> >>>>
>> >>>> _______________________________________________
>> >>>> Apiman-dev mailing list
>> >>>> Apiman-dev(a)lists.jboss.org
<mailto:Apiman-dev@lists.jboss.org>
>> <mailto:Apiman-dev@lists.jboss.org <mailto:
>> Apiman-dev(a)lists.jboss.org>>
>> >>>> <mailto:Apiman-dev@lists.jboss.org
>> <mailto:Apiman-dev@lists.jboss.org>>
>> >>>> <mailto:Apiman-dev@lists.jboss.org
>> <mailto:Apiman-dev@lists.jboss.org>
>> >>>> <mailto:Apiman-dev@lists.jboss.org
>> <mailto:Apiman-dev@lists.jboss.org>>>
>> >>>>
https://lists.jboss.org/mailman/listinfo/apiman-dev
>> >>>>
>> >>>>
>> >>>>
>> >>>>
>> >>>>
>> >>>> --
>> >>>> Lp, Benjamin
>> >>
>>
>>
>>
>>
>> --
>> Lp, Benjamin
>>
>
--
Lp, Benjamin