Sunday, November 8, 2015

Shibboleth-We Are Not Done Yet-Part 2

First tests were done in a LAN type environment; unfortunately life is not so simple: targeted solution should support users outside organization as well as Service Providers hosted in third party data centres. Since we do not want our IdP to be published directly on the Internet we have to use a reverse proxy which breaks our working solution. This might be particular (in terms of lack of out of the box solution-it might exist but I was unable to figure one) to ISA server but it looks (after google-ing a little bit) that a lot of people faced the same problem.

The Problem

Let's suppose our proxy has the public FQDN of proxy.mycompany.com while the internal FQDN of our Shibboleth IdP is idp.mycompany.local.

As a consequence external SP will have IdP metadata configured with URLs like https://proxy.mycompany.com/ipd/profile/SAML2/Redirect/SSO. When a request comes, the SAMLRequest will have AuthnRequest tag the attribute Destination set to this address while the proxy will forward (under default configuration) in relayed request's Host header the value of idp.mycompany.local:8443. This behaviour will lead to the failure of the request with this error message logged (sort of...) "SAML message intended destination endpoint 'https://proxy.mycompany.com/ipd/profile/SAML2/Redirect/SSO' did not match the recipient endpoint '{https://idp.mycompany.local:8443/ipd/profile/SAML2/Redirect/SSO'".

The error is generated in class org.opensaml.saml.common.binding.security.impl.ReceivedEndpointSecurityHandler, method checkEndpointURI.

We have two issues that make finding a solution non trivial:

  • We want to use HTTPS also for accessing our internal IdP (dropping this requirement will not actually lead to a working solution due to second issue)
  • Port translation from default 443 (not specified in external URL) to 8443

Usage of HTTPS for internal IdP

Initially Jetty was configured to use for SSL a certificate with common name of idp.mycompany.local with matches the URL configured in ISA publishing rule.

First solution to try was to change the default ISA behaviour in terms of Host header: check the option to forward the original header but another problem popped up: Jetty was refusing the connection since the Host header did not match any valid certificates in terms of common name.

Second solution tried was to generate a trusted (internally) certificate with common name of proxy.mycompany.com. Now ISA would reject it because it attempted a SSL connection on idp.mycompany.local and received a certificate on proxy.mycompany.com.

Third solution (I haven't tried because it would not solve the problem anyway due to port translation) would be:

  • The certificate installed on ISA should be wild card type
  • We have control on both external and internal DNS
  • Add an external DNS alias of idp.mycompany.com resolving to the ip of the external interface of ISA
  • Add an internal DNS alias of idp.mycompany.com resolving to the ip of the internal interface of ISA
  • Generate a trusted certificate for idp.mycompany.com and configure Jetty to use it
  • Add a publish rule for idp.mycompany.com that forwards requests to the IP of idp.mycompany.local and has filled in "This rule appliest to this published site:" idp.mycompany.com so that header Host will contain this value.

Now we should have circumvented our first problem but it will be of no help since the checkEndpointURI (through calling compareEndpointURIs, etc) will do a string comparison which will obviously fail due to the additional substring ":8443" in the actual receiverEndpoint.

8443 Port

Looking at Jetty documentation we quickly find four solutions to overcome this problem:

  • Start Jetty as root and change HTPPS port to 443; we do not want to do that (would we?)
  • Solutions based on ipchains/iptable which is not actually what we need: there will be an internal kernel routing from 443 to 8443 and at the end of the day we will get into the same problem as mentioned earlier
  • Configuring SetUID Jetty feature; this looks like solving our problem but needs some additional know how which I was not willing to acquire (native compiling, running the service as root...)

The Solution

The solution was to use Jetty rewrite handler capabilities (http://www.eclipse.org/jetty/documentation/current/rewrite-handler.html).

Unfortunately, none of the out of the box handlers provided the required functionality so I quickly wrote one.

Before going into into writing/deploying the custom handler we have to perform some prerequisites:

  • Check to see if org.eclipse.jetty.server.Request supports updating hots and port properties (since standard HttpServletRequest does not)
  • Check how to specify updated port so that it will not be included in receiverEndpoint string
  • Check how rewrite handler is activated

First create a maven project having jetty-rewrite dependency and download sources...

        <dependency>
            <groupId>org.eclipse.jetty</groupId>
            <artifactId>jetty-rewrite</artifactId>
            <version>9.3.5.v20151012</version>
        </dependency>

First item check passed since looks like we could use

        Request jettyRequest=(Request) request;
        jettyRequest.getMetaData().getURI().setAuthority(host, port);

Second item check was also quick: org.eclipse.jetty.util.appendSchemeHostPort shows that if specifying default ports (depending on schema) or 0 they will not be included.

Last item requires the following steps:

Add

--module=rewrite
etc/rewrite-rules.xml

in $JETTY_BASE/start.ini, where rewrite-rules.xml contains the actual rules and should be located in $JETTY_BASE/etc/rewrite-rules.xml:

<?xml version="1.0"?>
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">

<!-- =============================================================== -->
<!-- Configure the demos                                             -->
<!-- =============================================================== -->
<Configure id="Server" class="org.eclipse.jetty.server.Server">

  <!-- ============================================================= -->
  <!-- Add rewrite rules                                             -->
  <!-- ============================================================= -->
  <Ref refid="Rewrite">

      <!-- protect favicon handling -->
      <Call name="addRule">
        <Arg>
          <New class="ro.totalsoft.jetty.hostheaderrewriter.HostHeaderRewriter">
            <Set name="header">Host</Set>
            <Set name="host">proxy.mycompany.com</Set>
            <Set name="port">0</Set>
            <Set name="terminating">true</Set>
          </New>
        </Arg>
      </Call>
  </Ref>
</Configure>

In order to be selective (if we host also other applications in the same Jetty instance) we should also add the filtering condition

<Set name="headerValue">idp.mycompany.local:8443</Set>

The actual rewrite handler definition is located in $JETTY_HOME/etc/jetty-rewrite.xml as can be seen by inspection the $JETTY_HOME/module/rewrite.mod module definition file.

Remark: looking (as a newbie) in jetty-rewrite.xml might raise a question: what is oldhandler about? The naming is misleading, it should be something like firsthandler since it refers to the first handler in the chain of Jetty handlers. After Jetty applies the rewrite handler definition this will become the first handler in chain.

Now (supposing we have written our own header rule) we have to deploy it in the classpath known by Jetty. To do this we have to add the ext module:

--module=ext

--module=ext
The ext module will enable the lib/ext/*.jar logic.

If this module is activated, then all jar files found in the lib/ext/ paths will be automatically added to the Jetty Server Classpath.

Afterwards we can copy our jar to $JETTY_BASE/lib/ext/ and restart Jetty

**Important remark **: due to a bug (omission) in the logic that activates rules inside the rewrite handler if there is no rule defined a NullPointerException will be thrown in class org.eclipse.jetty.rewrite.handler.RuleContainer, method apply:

    protected String apply(String target, HttpServletRequest request, HttpServletResponse response) throws IOException
    {
        boolean original_set=_originalPathAttribute==null;
                
        for (Rule rule : _rules)
        {
            String applied=rule.matchAndApply(target,request, response);
            if (applied!=null)
            {       

If no rules are defined we will land here with _rules being null...

Epilogue

Ww are not done yet but have made significant progress having IdP side working. As we can assume, we will face same problem on SP side (we do not really want web sites exposed directly on the Internet).

So, next post about Kentor SSO. I would expect to apply the solution with external/internal DNS and using 443 default port (Windows/IIS looks more permissive in this area)

The Code

package com.mycompany.jetty.hostheaderrewriter;

import java.io.IOException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.rewrite.handler.HeaderRule;
import org.eclipse.jetty.server.Request;

public class HostHeaderRewriter extends HeaderRule{
    String host;
    int port=0;

    public String getHost() {
        return host;
    }
    public void setHost(String host) {
        this.host = host;
    }

    public int getPort() {
        return port;
    }
    public void setPort(int port) {
        this.port = port;
    }
    
    @Override
    protected String apply(String target, String value, HttpServletRequest request, HttpServletResponse response) throws IOException {
        Request jettyRequest=(Request) request;
        jettyRequest.getMetaData().getURI().setAuthority(host, port);
        return target;
    }
}
Rmlyc3QgdGVzdHMgd2VyZSBkb25lIGluIGEgTEFOIHR5cGUgZW52aXJvbm1lbnQ7IHVuZm9ydHVuYXRlbHkgbGlmZSBpcyBub3Qgc28gc2ltcGxlOiB0YXJnZXRlZCBzb2x1dGlvbiBzaG91bGQgc3VwcG9ydCB1c2VycyBvdXRzaWRlIG9yZ2FuaXphdGlvbiBhcyB3ZWxsIGFzIFNlcnZpY2UgUHJvdmlkZXJzIGhvc3RlZCBpbiB0aGlyZCBwYXJ0eSBkYXRhIGNlbnRyZXMuIFNpbmNlIHdlIGRvIG5vdCB3YW50IG91ciBJZFAgdG8gYmUgcHVibGlzaGVkIGRpcmVjdGx5IG9uIHRoZSBJbnRlcm5ldCB3ZSBoYXZlIHRvIHVzZSBhIHJldmVyc2UgcHJveHkgd2hpY2ggYnJlYWtzIG91ciB3b3JraW5nIHNvbHV0aW9uLiBUaGlzIG1pZ2h0IGJlIHBhcnRpY3VsYXIgKGluIHRlcm1zIG9mIGxhY2sgb2Ygb3V0IG9mIHRoZSBib3ggc29sdXRpb24taXQgbWlnaHQgZXhpc3QgYnV0IEkgd2FzIHVuYWJsZSB0byBmaWd1cmUgb25lKSB0byBJU0Egc2VydmVyIGJ1dCBpdCBsb29rcyAoYWZ0ZXIgZ29vZ2xlLWluZyBhIGxpdHRsZSBiaXQpIHRoYXQgYSBsb3Qgb2YgcGVvcGxlIGZhY2VkIHRoZSBzYW1lIHByb2JsZW0uDQoNCiMgVGhlIFByb2JsZW0NCg0KTGV0J3Mgc3VwcG9zZSBvdXIgcHJveHkgaGFzIHRoZSBwdWJsaWMgRlFETiBvZiAqKnByb3h5Lm15Y29tcGFueS5jb20qKiB3aGlsZSB0aGUgaW50ZXJuYWwgRlFETiBvZiBvdXIgU2hpYmJvbGV0aCBJZFAgaXMgKippZHAubXljb21wYW55LmxvY2FsKiouIA0KDQpBcyBhIGNvbnNlcXVlbmNlIGV4dGVybmFsIFNQIHdpbGwgaGF2ZSBJZFAgbWV0YWRhdGEgY29uZmlndXJlZCB3aXRoIFVSTHMgbGlrZSBgaHR0cHM6Ly9wcm94eS5teWNvbXBhbnkuY29tL2lwZC9wcm9maWxlL1NBTUwyL1JlZGlyZWN0L1NTT2AuIFdoZW4gYSByZXF1ZXN0IGNvbWVzLCB0aGUgU0FNTFJlcXVlc3Qgd2lsbCBoYXZlIEF1dGhuUmVxdWVzdCB0YWcgdGhlIGF0dHJpYnV0ZSAqKkRlc3RpbmF0aW9uKiogc2V0IHRvIHRoaXMgYWRkcmVzcyB3aGlsZSB0aGUgcHJveHkgd2lsbCBmb3J3YXJkICh1bmRlciBkZWZhdWx0IGNvbmZpZ3VyYXRpb24pIGluIHJlbGF5ZWQgcmVxdWVzdCdzICoqSG9zdCoqIGhlYWRlciB0aGUgdmFsdWUgb2YgKippZHAubXljb21wYW55LmxvY2FsOjg0NDMqKi4gDQpUaGlzIGJlaGF2aW91ciB3aWxsIGxlYWQgdG8gdGhlIGZhaWx1cmUgb2YgdGhlIHJlcXVlc3Qgd2l0aCB0aGlzIGVycm9yIG1lc3NhZ2UgbG9nZ2VkIChzb3J0IG9mLi4uKSAqIlNBTUwgbWVzc2FnZSBpbnRlbmRlZCBkZXN0aW5hdGlvbiBlbmRwb2ludCAnaHR0cHM6Ly9wcm94eS5teWNvbXBhbnkuY29tL2lwZC9wcm9maWxlL1NBTUwyL1JlZGlyZWN0L1NTTycgZGlkIG5vdCBtYXRjaCB0aGUgcmVjaXBpZW50IGVuZHBvaW50ICd7aHR0cHM6Ly9pZHAubXljb21wYW55LmxvY2FsOjg0NDMvaXBkL3Byb2ZpbGUvU0FNTDIvUmVkaXJlY3QvU1NPJyIqLg0KDQpUaGUgZXJyb3IgaXMgZ2VuZXJhdGVkIGluIGNsYXNzIGBvcmcub3BlbnNhbWwuc2FtbC5jb21tb24uYmluZGluZy5zZWN1cml0eS5pbXBsLlJlY2VpdmVkRW5kcG9pbnRTZWN1cml0eUhhbmRsZXJgLCBtZXRob2QgYGNoZWNrRW5kcG9pbnRVUklgLg0KDQpXZSBoYXZlIHR3byBpc3N1ZXMgdGhhdCBtYWtlIGZpbmRpbmcgYSBzb2x1dGlvbiBub24gdHJpdmlhbDoNCg0KKiBXZSB3YW50IHRvIHVzZSBIVFRQUyBhbHNvIGZvciBhY2Nlc3Npbmcgb3VyIGludGVybmFsIElkUCAoZHJvcHBpbmcgdGhpcyByZXF1aXJlbWVudCB3aWxsIG5vdCBhY3R1YWxseSBsZWFkIHRvIGEgd29ya2luZyBzb2x1dGlvbiBkdWUgdG8gc2Vjb25kIGlzc3VlKQ0KKiBQb3J0IHRyYW5zbGF0aW9uIGZyb20gZGVmYXVsdCA0NDMgKG5vdCBzcGVjaWZpZWQgaW4gZXh0ZXJuYWwgVVJMKSB0byA4NDQzDQoNCiMjIFVzYWdlIG9mIEhUVFBTIGZvciBpbnRlcm5hbCBJZFANCg0KSW5pdGlhbGx5IEpldHR5IHdhcyBjb25maWd1cmVkIHRvIHVzZSBmb3IgU1NMIGEgY2VydGlmaWNhdGUgd2l0aCBjb21tb24gbmFtZSBvZiAqKmlkcC5teWNvbXBhbnkubG9jYWwqKiB3aXRoIG1hdGNoZXMgdGhlIFVSTCBjb25maWd1cmVkIGluIElTQSBwdWJsaXNoaW5nIHJ1bGUuDQoNCkZpcnN0IHNvbHV0aW9uIHRvIHRyeSB3YXMgdG8gY2hhbmdlIHRoZSBkZWZhdWx0IElTQSBiZWhhdmlvdXIgaW4gdGVybXMgb2YgKipIb3N0KiogaGVhZGVyOiBjaGVjayB0aGUgb3B0aW9uIHRvIGZvcndhcmQgdGhlIG9yaWdpbmFsIGhlYWRlciBidXQgYW5vdGhlciBwcm9ibGVtIHBvcHBlZCB1cDogSmV0dHkgd2FzIHJlZnVzaW5nIHRoZSBjb25uZWN0aW9uIHNpbmNlIHRoZSAqKkhvc3QqKiBoZWFkZXIgZGlkIG5vdCBtYXRjaCBhbnkgdmFsaWQgY2VydGlmaWNhdGVzIGluIHRlcm1zIG9mIGNvbW1vbiBuYW1lLg0KDQpTZWNvbmQgc29sdXRpb24gdHJpZWQgd2FzIHRvIGdlbmVyYXRlIGEgdHJ1c3RlZCAoaW50ZXJuYWxseSkgY2VydGlmaWNhdGUgd2l0aCBjb21tb24gbmFtZSBvZiAqKnByb3h5Lm15Y29tcGFueS5jb20qKi4gTm93IElTQSB3b3VsZCByZWplY3QgaXQgYmVjYXVzZSBpdCBhdHRlbXB0ZWQgYSBTU0wgY29ubmVjdGlvbiBvbiAqKmlkcC5teWNvbXBhbnkubG9jYWwqKiBhbmQgcmVjZWl2ZWQgYSBjZXJ0aWZpY2F0ZSBvbiAqKnByb3h5Lm15Y29tcGFueS5jb20qKi4NCg0KVGhpcmQgc29sdXRpb24gKEkgaGF2ZW4ndCB0cmllZCBiZWNhdXNlIGl0IHdvdWxkIG5vdCBzb2x2ZSB0aGUgcHJvYmxlbSBhbnl3YXkgZHVlIHRvIHBvcnQgdHJhbnNsYXRpb24pIHdvdWxkIGJlOg0KDQoqIFRoZSBjZXJ0aWZpY2F0ZSBpbnN0YWxsZWQgb24gSVNBIHNob3VsZCBiZSB3aWxkIGNhcmQgdHlwZQ0KKiBXZSBoYXZlIGNvbnRyb2wgb24gYm90aCBleHRlcm5hbCBhbmQgaW50ZXJuYWwgRE5TDQoqIEFkZCBhbiBleHRlcm5hbCBETlMgYWxpYXMgb2YgKippZHAubXljb21wYW55LmNvbSoqIHJlc29sdmluZyB0byB0aGUgaXAgb2YgdGhlIGV4dGVybmFsIGludGVyZmFjZSBvZiBJU0ENCiogQWRkIGFuIGludGVybmFsIEROUyBhbGlhcyBvZiAqKmlkcC5teWNvbXBhbnkuY29tKiogcmVzb2x2aW5nIHRvIHRoZSBpcCBvZiB0aGUgaW50ZXJuYWwgaW50ZXJmYWNlIG9mIElTQQ0KKiBHZW5lcmF0ZSBhIHRydXN0ZWQgY2VydGlmaWNhdGUgZm9yICoqaWRwLm15Y29tcGFueS5jb20qKiBhbmQgY29uZmlndXJlIEpldHR5IHRvIHVzZSBpdA0KKiBBZGQgYSBwdWJsaXNoIHJ1bGUgZm9yICoqaWRwLm15Y29tcGFueS5jb20qKiB0aGF0IGZvcndhcmRzIHJlcXVlc3RzIHRvIHRoZSBJUCBvZiAgKippZHAubXljb21wYW55LmxvY2FsKiogYW5kIGhhcyBmaWxsZWQgaW4gIlRoaXMgcnVsZSBhcHBsaWVzdCB0byB0aGlzIHB1Ymxpc2hlZCBzaXRlOiIgKippZHAubXljb21wYW55LmNvbSoqIHNvIHRoYXQgaGVhZGVyIEhvc3Qgd2lsbCBjb250YWluIHRoaXMgdmFsdWUuDQoNCk5vdyB3ZSBzaG91bGQgaGF2ZSBjaXJjdW12ZW50ZWQgb3VyIGZpcnN0IHByb2JsZW0gYnV0IGl0IHdpbGwgYmUgb2Ygbm8gaGVscCBzaW5jZSB0aGUgYGNoZWNrRW5kcG9pbnRVUklgICh0aHJvdWdoIGNhbGxpbmcgYGNvbXBhcmVFbmRwb2ludFVSSXNgLCBldGMpIHdpbGwgZG8gYSBzdHJpbmcgY29tcGFyaXNvbiB3aGljaCB3aWxsIG9idmlvdXNseSBmYWlsIGR1ZSB0byB0aGUgYWRkaXRpb25hbCBzdWJzdHJpbmcgIjo4NDQzIiBpbiB0aGUgYWN0dWFsIHJlY2VpdmVyRW5kcG9pbnQuDQoNCiMjIDg0NDMgUG9ydA0KDQpMb29raW5nIGF0IEpldHR5IGRvY3VtZW50YXRpb24gd2UgcXVpY2tseSBmaW5kIGZvdXIgc29sdXRpb25zIHRvIG92ZXJjb21lIHRoaXMgcHJvYmxlbToNCg0KKiBTdGFydCBKZXR0eSBhcyByb290IGFuZCBjaGFuZ2UgSFRQUFMgcG9ydCB0byA0NDM7IHdlIGRvIG5vdCB3YW50IHRvIGRvIHRoYXQgKHdvdWxkIHdlPykNCiogU29sdXRpb25zIGJhc2VkIG9uIGlwY2hhaW5zL2lwdGFibGUgd2hpY2ggaXMgbm90IGFjdHVhbGx5IHdoYXQgd2UgbmVlZDogdGhlcmUgd2lsbCBiZSBhbiBpbnRlcm5hbCBrZXJuZWwgcm91dGluZyBmcm9tIDQ0MyB0byA4NDQzIGFuZCBhdCB0aGUgZW5kIG9mIHRoZSBkYXkgd2Ugd2lsbCBnZXQgaW50byB0aGUgc2FtZSBwcm9ibGVtIGFzIG1lbnRpb25lZCBlYXJsaWVyDQoqIENvbmZpZ3VyaW5nIFNldFVJRCBKZXR0eSBmZWF0dXJlOyB0aGlzIGxvb2tzIGxpa2Ugc29sdmluZyBvdXIgcHJvYmxlbSBidXQgbmVlZHMgc29tZSBhZGRpdGlvbmFsIGtub3cgaG93IHdoaWNoIEkgd2FzIG5vdCB3aWxsaW5nIHRvIGFjcXVpcmUgKG5hdGl2ZSBjb21waWxpbmcsIHJ1bm5pbmcgdGhlIHNlcnZpY2UgYXMgcm9vdC4uLikNCg0KIyBUaGUgU29sdXRpb24NCg0KVGhlIHNvbHV0aW9uIHdhcyB0byB1c2UgSmV0dHkgcmV3cml0ZSBoYW5kbGVyIGNhcGFiaWxpdGllcyAoW2h0dHA6Ly93d3cuZWNsaXBzZS5vcmcvamV0dHkvZG9jdW1lbnRhdGlvbi9jdXJyZW50L3Jld3JpdGUtaGFuZGxlci5odG1sXShodHRwOi8vd3d3LmVjbGlwc2Uub3JnL2pldHR5L2RvY3VtZW50YXRpb24vY3VycmVudC9yZXdyaXRlLWhhbmRsZXIuaHRtbCkpLg0KDQpVbmZvcnR1bmF0ZWx5LCBub25lIG9mIHRoZSBvdXQgb2YgdGhlIGJveCBoYW5kbGVycyBwcm92aWRlZCB0aGUgcmVxdWlyZWQgZnVuY3Rpb25hbGl0eSBzbyBJIHF1aWNrbHkgd3JvdGUgb25lLg0KDQpCZWZvcmUgZ29pbmcgaW50byBpbnRvIHdyaXRpbmcvZGVwbG95aW5nIHRoZSBjdXN0b20gaGFuZGxlciB3ZSBoYXZlIHRvIHBlcmZvcm0gc29tZSBwcmVyZXF1aXNpdGVzOg0KDQoqIENoZWNrIHRvIHNlZSBpZiBgb3JnLmVjbGlwc2UuamV0dHkuc2VydmVyLlJlcXVlc3RgIHN1cHBvcnRzIHVwZGF0aW5nIGhvdHMgYW5kIHBvcnQgcHJvcGVydGllcyAoc2luY2Ugc3RhbmRhcmQgSHR0cFNlcnZsZXRSZXF1ZXN0IGRvZXMgbm90KQ0KKiBDaGVjayBob3cgdG8gc3BlY2lmeSB1cGRhdGVkIHBvcnQgc28gdGhhdCBpdCB3aWxsIG5vdCBiZSBpbmNsdWRlZCBpbiByZWNlaXZlckVuZHBvaW50IHN0cmluZw0KKiBDaGVjayBob3cgcmV3cml0ZSBoYW5kbGVyIGlzIGFjdGl2YXRlZA0KDQogDQpGaXJzdCBjcmVhdGUgYSBtYXZlbiBwcm9qZWN0IGhhdmluZyBqZXR0eS1yZXdyaXRlIGRlcGVuZGVuY3kgYW5kIGRvd25sb2FkIHNvdXJjZXMuLi4NCg0KYGBgeG1sDQogICAgICAgIDxkZXBlbmRlbmN5Pg0KICAgICAgICAgICAgPGdyb3VwSWQ+b3JnLmVjbGlwc2UuamV0dHk8L2dyb3VwSWQ+DQogICAgICAgICAgICA8YXJ0aWZhY3RJZD5qZXR0eS1yZXdyaXRlPC9hcnRpZmFjdElkPg0KICAgICAgICAgICAgPHZlcnNpb24+OS4zLjUudjIwMTUxMDEyPC92ZXJzaW9uPg0KICAgICAgICA8L2RlcGVuZGVuY3k+DQpgYGAgDQpGaXJzdCBpdGVtIGNoZWNrIHBhc3NlZCBzaW5jZSBsb29rcyBsaWtlIHdlIGNvdWxkIHVzZSANCmBgYGphdmENCiAgICAgICAgUmVxdWVzdCBqZXR0eVJlcXVlc3Q9KFJlcXVlc3QpIHJlcXVlc3Q7DQogICAgICAgIGpldHR5UmVxdWVzdC5nZXRNZXRhRGF0YSgpLmdldFVSSSgpLnNldEF1dGhvcml0eShob3N0LCBwb3J0KTsNCg0KYGBgDQpTZWNvbmQgaXRlbSBjaGVjayB3YXMgYWxzbyBxdWljazogYG9yZy5lY2xpcHNlLmpldHR5LnV0aWwuYXBwZW5kU2NoZW1lSG9zdFBvcnRgIHNob3dzIHRoYXQgaWYgc3BlY2lmeWluZyBkZWZhdWx0IHBvcnRzIChkZXBlbmRpbmcgb24gc2NoZW1hKSBvciAwIHRoZXkgd2lsbCBub3QgYmUgaW5jbHVkZWQuDQoNCg0KTGFzdCBpdGVtIHJlcXVpcmVzIHRoZSBmb2xsb3dpbmcgc3RlcHM6DQoNCkFkZCANCmBgYA0KLS1tb2R1bGU9cmV3cml0ZQ0KZXRjL3Jld3JpdGUtcnVsZXMueG1sDQpgYGANCmluIGAkSkVUVFlfQkFTRS9zdGFydC5pbmlgLCB3aGVyZSBgcmV3cml0ZS1ydWxlcy54bWxgIGNvbnRhaW5zIHRoZSBhY3R1YWwgcnVsZXMgYW5kIHNob3VsZCBiZSBsb2NhdGVkIGluICBgJEpFVFRZX0JBU0UvZXRjL3Jld3JpdGUtcnVsZXMueG1sYDoNCg0KYGBgeG1sDQo8P3htbCB2ZXJzaW9uPSIxLjAiPz4NCjwhRE9DVFlQRSBDb25maWd1cmUgUFVCTElDICItLy9KZXR0eS8vQ29uZmlndXJlLy9FTiIgImh0dHA6Ly93d3cuZWNsaXBzZS5vcmcvamV0dHkvY29uZmlndXJlXzlfMy5kdGQiPg0KDQo8IS0tID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSAtLT4NCjwhLS0gQ29uZmlndXJlIHRoZSBkZW1vcyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC0tPg0KPCEtLSA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0gLS0+DQo8Q29uZmlndXJlIGlkPSJTZXJ2ZXIiIGNsYXNzPSJvcmcuZWNsaXBzZS5qZXR0eS5zZXJ2ZXIuU2VydmVyIj4NCg0KICA8IS0tID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0gLS0+DQogIDwhLS0gQWRkIHJld3JpdGUgcnVsZXMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAtLT4NCiAgPCEtLSA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09IC0tPg0KICA8UmVmIHJlZmlkPSJSZXdyaXRlIj4NCg0KICAgICAgPCEtLSBwcm90ZWN0IGZhdmljb24gaGFuZGxpbmcgLS0+DQogICAgICA8Q2FsbCBuYW1lPSJhZGRSdWxlIj4NCiAgICAgICAgPEFyZz4NCiAgICAgICAgICA8TmV3IGNsYXNzPSJyby50b3RhbHNvZnQuamV0dHkuaG9zdGhlYWRlcnJld3JpdGVyLkhvc3RIZWFkZXJSZXdyaXRlciI+DQogICAgICAgICAgICA8U2V0IG5hbWU9ImhlYWRlciI+SG9zdDwvU2V0Pg0KICAgICAgICAgICAgPFNldCBuYW1lPSJob3N0Ij5wcm94eS5teWNvbXBhbnkuY29tPC9TZXQ+DQogICAgICAgICAgICA8U2V0IG5hbWU9InBvcnQiPjA8L1NldD4NCiAgICAgICAgICAgIDxTZXQgbmFtZT0idGVybWluYXRpbmciPnRydWU8L1NldD4NCiAgICAgICAgICA8L05ldz4NCiAgICAgICAgPC9Bcmc+DQogICAgICA8L0NhbGw+DQogIDwvUmVmPg0KPC9Db25maWd1cmU+DQpgYGANCg0KSW4gb3JkZXIgdG8gYmUgc2VsZWN0aXZlIChpZiB3ZSBob3N0IGFsc28gb3RoZXIgYXBwbGljYXRpb25zIGluIHRoZSBzYW1lIEpldHR5IGluc3RhbmNlKSB3ZSBzaG91bGQgYWxzbyBhZGQgdGhlIGZpbHRlcmluZyBjb25kaXRpb24NCg0KYGBgeG1sDQo8U2V0IG5hbWU9ImhlYWRlclZhbHVlIj5pZHAubXljb21wYW55LmxvY2FsOjg0NDM8L1NldD4NCmBgYA0KDQpUaGUgYWN0dWFsIHJld3JpdGUgaGFuZGxlciBkZWZpbml0aW9uIGlzIGxvY2F0ZWQgaW4gYCRKRVRUWV9IT01FL2V0Yy9qZXR0eS1yZXdyaXRlLnhtbGAgYXMgY2FuIGJlIHNlZW4gYnkgaW5zcGVjdGlvbiB0aGUgYCRKRVRUWV9IT01FL21vZHVsZS9yZXdyaXRlLm1vZGAgbW9kdWxlIGRlZmluaXRpb24gZmlsZS4NCg0KUmVtYXJrOiBsb29raW5nIChhcyBhIG5ld2JpZSkgaW4gYGpldHR5LXJld3JpdGUueG1sYCBtaWdodCByYWlzZSBhIHF1ZXN0aW9uOiB3aGF0IGlzIGBvbGRoYW5kbGVyYCBhYm91dD8gVGhlIG5hbWluZyBpcyBtaXNsZWFkaW5nLCBpdCBzaG91bGQgYmUgc29tZXRoaW5nIGxpa2UgYGZpcnN0aGFuZGxlcmAgc2luY2UgaXQgcmVmZXJzIHRvIHRoZSBmaXJzdCBoYW5kbGVyIGluIHRoZSBjaGFpbiBvZiBKZXR0eSBoYW5kbGVycy4gQWZ0ZXIgSmV0dHkgYXBwbGllcyB0aGUgcmV3cml0ZSBoYW5kbGVyIGRlZmluaXRpb24gdGhpcyB3aWxsIGJlY29tZSB0aGUgZmlyc3QgaGFuZGxlciBpbiBjaGFpbi4NCg0KTm93IChzdXBwb3Npbmcgd2UgaGF2ZSB3cml0dGVuIG91ciBvd24gaGVhZGVyIHJ1bGUpIHdlIGhhdmUgdG8gZGVwbG95IGl0IGluIHRoZSBjbGFzc3BhdGgga25vd24gYnkgSmV0dHkuIFRvIGRvIHRoaXMgd2UgaGF2ZSB0byBhZGQgdGhlICoqZXh0KiogbW9kdWxlOg0KDQotLW1vZHVsZT1leHQNCmBgYA0KLS1tb2R1bGU9ZXh0DQpUaGUgZXh0IG1vZHVsZSB3aWxsIGVuYWJsZSB0aGUgbGliL2V4dC8qLmphciBsb2dpYy4NCg0KSWYgdGhpcyBtb2R1bGUgaXMgYWN0aXZhdGVkLCB0aGVuIGFsbCBqYXIgZmlsZXMgZm91bmQgaW4gdGhlIGxpYi9leHQvIHBhdGhzIHdpbGwgYmUgYXV0b21hdGljYWxseSBhZGRlZCB0byB0aGUgSmV0dHkgU2VydmVyIENsYXNzcGF0aC4NCmBgYA0KDQpBZnRlcndhcmRzIHdlIGNhbiBjb3B5IG91ciBqYXIgdG8gJEpFVFRZX0JBU0UvbGliL2V4dC8gYW5kIHJlc3RhcnQgSmV0dHkNCg0KKipJbXBvcnRhbnQgcmVtYXJrICoqOiBkdWUgdG8gYSBidWcgKG9taXNzaW9uKSBpbiB0aGUgbG9naWMgdGhhdCBhY3RpdmF0ZXMgcnVsZXMgaW5zaWRlIHRoZSByZXdyaXRlIGhhbmRsZXIgaWYgdGhlcmUgaXMgbm8gcnVsZSBkZWZpbmVkIGEgTnVsbFBvaW50ZXJFeGNlcHRpb24gd2lsbCBiZSB0aHJvd24gaW4gY2xhc3MgYG9yZy5lY2xpcHNlLmpldHR5LnJld3JpdGUuaGFuZGxlci5SdWxlQ29udGFpbmVyYCwgbWV0aG9kIGBhcHBseWA6DQoNCmBgYGphdmENCiAgICBwcm90ZWN0ZWQgU3RyaW5nIGFwcGx5KFN0cmluZyB0YXJnZXQsIEh0dHBTZXJ2bGV0UmVxdWVzdCByZXF1ZXN0LCBIdHRwU2VydmxldFJlc3BvbnNlIHJlc3BvbnNlKSB0aHJvd3MgSU9FeGNlcHRpb24NCiAgICB7DQogICAgICAgIGJvb2xlYW4gb3JpZ2luYWxfc2V0PV9vcmlnaW5hbFBhdGhBdHRyaWJ1dGU9PW51bGw7DQogICAgICAgICAgICAgICAgDQogICAgICAgIGZvciAoUnVsZSBydWxlIDogX3J1bGVzKQ0KICAgICAgICB7DQogICAgICAgICAgICBTdHJpbmcgYXBwbGllZD1ydWxlLm1hdGNoQW5kQXBwbHkodGFyZ2V0LHJlcXVlc3QsIHJlc3BvbnNlKTsNCiAgICAgICAgICAgIGlmIChhcHBsaWVkIT1udWxsKQ0KICAgICAgICAgICAgeyAgICAgICANCg0KYGBgDQpJZiBubyBydWxlcyBhcmUgZGVmaW5lZCB3ZSB3aWxsIGxhbmQgaGVyZSB3aXRoIGBfcnVsZXNgIGJlaW5nIG51bGwuLi4NCg0KIyBFcGlsb2d1ZQ0KDQpXdyBhcmUgbm90IGRvbmUgeWV0IGJ1dCBoYXZlIG1hZGUgc2lnbmlmaWNhbnQgcHJvZ3Jlc3MgaGF2aW5nIElkUCBzaWRlIHdvcmtpbmcuIEFzIHdlIGNhbiBhc3N1bWUsIHdlIHdpbGwgZmFjZSBzYW1lIHByb2JsZW0gb24gU1Agc2lkZSAod2UgZG8gbm90IHJlYWxseSB3YW50IHdlYiBzaXRlcyBleHBvc2VkIGRpcmVjdGx5IG9uIHRoZSBJbnRlcm5ldCkuDQoNClNvLCBuZXh0IHBvc3QgYWJvdXQgS2VudG9yIFNTTy4gSSB3b3VsZCBleHBlY3QgdG8gYXBwbHkgdGhlIHNvbHV0aW9uIHdpdGggZXh0ZXJuYWwvaW50ZXJuYWwgRE5TIGFuZCB1c2luZyA0NDMgZGVmYXVsdCBwb3J0IChXaW5kb3dzL0lJUyBsb29rcyBtb3JlIHBlcm1pc3NpdmUgaW4gdGhpcyBhcmVhKQ0KDQojIFRoZSBDb2RlDQoNCmBgYGphdmENCnBhY2thZ2UgY29tLm15Y29tcGFueS5qZXR0eS5ob3N0aGVhZGVycmV3cml0ZXI7DQoNCmltcG9ydCBqYXZhLmlvLklPRXhjZXB0aW9uOw0KaW1wb3J0IGphdmF4LnNlcnZsZXQuaHR0cC5IdHRwU2VydmxldFJlcXVlc3Q7DQppbXBvcnQgamF2YXguc2VydmxldC5odHRwLkh0dHBTZXJ2bGV0UmVzcG9uc2U7DQppbXBvcnQgb3JnLmVjbGlwc2UuamV0dHkucmV3cml0ZS5oYW5kbGVyLkhlYWRlclJ1bGU7DQppbXBvcnQgb3JnLmVjbGlwc2UuamV0dHkuc2VydmVyLlJlcXVlc3Q7DQoNCnB1YmxpYyBjbGFzcyBIb3N0SGVhZGVyUmV3cml0ZXIgZXh0ZW5kcyBIZWFkZXJSdWxlew0KICAgIFN0cmluZyBob3N0Ow0KICAgIGludCBwb3J0PTA7DQoNCiAgICBwdWJsaWMgU3RyaW5nIGdldEhvc3QoKSB7DQogICAgICAgIHJldHVybiBob3N0Ow0KICAgIH0NCiAgICBwdWJsaWMgdm9pZCBzZXRIb3N0KFN0cmluZyBob3N0KSB7DQogICAgICAgIHRoaXMuaG9zdCA9IGhvc3Q7DQogICAgfQ0KDQogICAgcHVibGljIGludCBnZXRQb3J0KCkgew0KICAgICAgICByZXR1cm4gcG9ydDsNCiAgICB9DQogICAgcHVibGljIHZvaWQgc2V0UG9ydChpbnQgcG9ydCkgew0KICAgICAgICB0aGlzLnBvcnQgPSBwb3J0Ow0KICAgIH0NCiAgICANCiAgICBAT3ZlcnJpZGUNCiAgICBwcm90ZWN0ZWQgU3RyaW5nIGFwcGx5KFN0cmluZyB0YXJnZXQsIFN0cmluZyB2YWx1ZSwgSHR0cFNlcnZsZXRSZXF1ZXN0IHJlcXVlc3QsIEh0dHBTZXJ2bGV0UmVzcG9uc2UgcmVzcG9uc2UpIHRocm93cyBJT0V4Y2VwdGlvbiB7DQogICAgICAgIFJlcXVlc3QgamV0dHlSZXF1ZXN0PShSZXF1ZXN0KSByZXF1ZXN0Ow0KICAgICAgICBqZXR0eVJlcXVlc3QuZ2V0TWV0YURhdGEoKS5nZXRVUkkoKS5zZXRBdXRob3JpdHkoaG9zdCwgcG9ydCk7DQogICAgICAgIHJldHVybiB0YXJnZXQ7DQogICAgfQ0KfQ0KYGBg

No comments :

Post a Comment