[Apiman-dev] Apiman - WS Security policy

Eric Wittmann eric.wittmann at redhat.com
Wed Mar 30 09:59:23 EDT 2016


Thanks for the feedback - I'll give this some thought.  I think this 
suggestion does make sense - but I'm also glad that the url re-writing 
policy solved the issue. :)

We'll have a similar issue with swagger based REST apis, so baking it 
into the gateway probably makes sense.

-Eric

On 3/30/2016 4:26 AM, Benjamin Kastelic wrote:
> 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 <mailto: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
>     <mailto: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 <http://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 <http://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>
>             <mailto: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>
>             <mailto: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>>
>                   >>> <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:
>                   >>>
>                   >>> 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>
>                  <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>>>>:
>                   >>>>
>                   >>>>   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>
>                  <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>>>>> 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>
>                  <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
>             <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>
>                  <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
>             <mailto:Apiman-dev at lists.jboss.org>>>>
>                   >>>> https://lists.jboss.org/mailman/listinfo/apiman-dev
>                   >>>>
>                   >>>>
>                   >>>>
>                   >>>>
>                   >>>>
>                   >>>> --
>                   >>>> Lp, Benjamin
>                   >>
>
>
>
>
>             --
>             Lp, Benjamin
>
>
>
>
>     --
>     Lp, Benjamin
>
>
>
>
> --
> Lp, Benjamin


More information about the Apiman-dev mailing list