[Apiman-dev] Apiman - WS Security policy
Benjamin Kastelic
kastelic.benjamin at gmail.com
Wed Mar 30 04:26:59 EDT 2016
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 at 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 at 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 at redhat.com
>>> <mailto:kbabo at 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 at redhat.com <mailto:eric.wittmann at 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 at redhat.com <mailto:eric.wittmann at redhat.com>
>>> >>> <mailto:eric.wittmann at redhat.com
>>> <mailto:eric.wittmann at 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 at redhat.com <mailto:eric.wittmann at redhat.com>
>>> >>>> <mailto:eric.wittmann at redhat.com
>>> <mailto:eric.wittmann at redhat.com>>
>>> >>>> <mailto:eric.wittmann at redhat.com
>>> <mailto:eric.wittmann at 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 at redhat.com
>>> <mailto:eric.wittmann at redhat.com>
>>> >>>> <mailto:eric.wittmann at redhat.com
>>> <mailto:eric.wittmann at redhat.com>><mailto:eric.wittmann at redhat.com
>>> <mailto:eric.wittmann at redhat.com>>
>>> >>>> <mailto:eric.wittmann at redhat.com
>>> <mailto:eric.wittmann at redhat.com>
>>> >>>> <mailto:eric.wittmann at redhat.com
>>> <mailto:eric.wittmann at 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 at redhat.com
>>> <mailto:eric.wittmann at redhat.com>
>>> >>>> <mailto:eric.wittmann at redhat.com
>>> <mailto:eric.wittmann at redhat.com>><mailto:eric.wittmann at redhat.com
>>> <mailto:eric.wittmann at redhat.com>>
>>> >>>> <mailto:eric.wittmann at redhat.com
>>> <mailto:eric.wittmann at redhat.com>
>>> >>>> <mailto:eric.wittmann at redhat.com
>>> <mailto:eric.wittmann at redhat.com>>>
>>> >>>> <mailto:eric.wittmann at redhat.com
>>> <mailto:eric.wittmann at redhat.com>
>>> >>>> <mailto:eric.wittmann at redhat.com
>>> <mailto:eric.wittmann at 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 at lists.jboss.org <mailto:Apiman-dev at lists.jboss.org>
>>> <mailto:Apiman-dev at lists.jboss.org <mailto:
>>> Apiman-dev at lists.jboss.org>>
>>> >>>> <mailto:Apiman-dev at lists.jboss.org
>>> <mailto:Apiman-dev at lists.jboss.org>>
>>> >>>> <mailto:Apiman-dev at lists.jboss.org
>>> <mailto:Apiman-dev at lists.jboss.org>
>>> >>>> <mailto:Apiman-dev at lists.jboss.org
>>> <mailto:Apiman-dev at lists.jboss.org>>>
>>> >>>> https://lists.jboss.org/mailman/listinfo/apiman-dev
>>> >>>>
>>> >>>>
>>> >>>>
>>> >>>>
>>> >>>>
>>> >>>> --
>>> >>>> Lp, Benjamin
>>> >>
>>>
>>>
>>>
>>>
>>> --
>>> Lp, Benjamin
>>>
>>
>
>
> --
> Lp, Benjamin
>
--
Lp, Benjamin
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.jboss.org/pipermail/apiman-dev/attachments/20160330/0a3fc643/attachment-0001.html
More information about the Apiman-dev
mailing list