[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