Friday, October 30, 2015

Shibboleth-Finally

After about two weeks of trial and error I finally succeeded to have a SSO/SAML POC based on open source components (Shibbolet/ApacheDS/KentorIT) working.

The last step was struggling with convincing Shibbolet to respond to SAML requests.

The main problem (for newbies) is the lack of any overview type information; almost all documentation (https://wiki.shibboleth.net/confluence/display/IDP30/Home) focuses on detailed specification aspects so you get lost in tens of .properties/.xml configuration files.

Debugging

Of course my first concern was about being able to set breakpoints and go step by step through sources.

Go to jetty_base and edit start.ini by adding the following lines:

-Xdebug 
-Xrunjdwp:transport=dt_socket,address=5005,server=y,suspend=n

Restart jetty.

Download latest source, open main pom.xml and rebuild project.

First steps

Supposing you have the metadata file for a target SP (we do not need the SP implementation for now) deploy it in shibbolet_sp/metadata (you will also find there idp_metadata.xml, the metadata related with IsP).

Edit conf/metadata-providers.xml by adding information about you SP; something like this:

  • <MetadataProvider id="LocalMetadata" xsi:type="FilesystemMetadataProvider" metadataFile="%{idp.home}/metadata/mySPmetadata.xml"/>

Edit conf/ldap.proprierties (watch out for useStartTLS):

  • idp.authn.LDAP.ldapURL = <ldap url>
  • idp.authn.LDAP.useStartTLS = false
  • idp.authn.LDAP.returnAttributes = <comma separated LDAP attributes>
  • idp.authn.LDAP.baseDN = <base DN for search)
  • idp.authn.LDAP.userFilter = (<LDAP attribute>={user})

Remark: This is enaough for default anonSearchAuthenticator; if bindSearchAuthenticator is required be sure to:

  • uncomment and change accordingly idp.authn.LDAP.authenticator = bindSearchAuthenticator
  • change the two configartion entries:
    • idp.authn.LDAP.bindDN = <DN of bind user>
    • idp.authn.LDAP.bindDNCredential = <password>

At this moment we can test authentication by using the IdP initiated login URL : HTTPS://<shibblolet-url>:<https-port>/idp/profile/SAML2/Unsolicited/SSO?providerId=<SP_entityID>

<SP_entityID> is the the SP EntityId as specified in metadata file.

Logout can be achieved through https://<shibblolet-url>:<https-port>/idp/profile/Logout

In order to debug the authentication proceeds have a look in net.shibboleth.idp.authn.impl.ValidateUsernamePasswordAgainstLDAP (method doExecute), module Shibboleth IdP :: Authentication Implementation; also look at org.ldaptive.auth.Authenticator from ladptive-1.0.0.jar (select the jar with right click->Download source, etc).

Configure Response

Now, even if you will get a browser error after going through the login UI process (since we dot have the actual SP implementation) you can use browser developer tools to check the SAMLResponse (use SSOCircle POST decoder). We notice that the Subject part is encrypted.

Response configuration can be done through conf/relying-party.xml; inside <bean id="shibboleth.DefaultRelyingParty" parent="RelyingParty"> change the SAML2.SSO attributes according to your needs:

  • (sample): <bean parent="SAML2.SSO" p:postAuthenticationFlows="attribute-release" p:encryptAssertions="false" p:encryptAttributes="false" p:encryptNameIDs="false"/>

Now we can see in clear text (after decoding) the Subject info but surprise(s):

  • NameID has a strange value (some Base64 encoded value)
  • No other assertions, even if we configured LDAP to return more attributes

NameID

I was used with ADFS where you can easily return sAMAcountName as NameID. It turned out to be a very difficult task so at the end of the day I gave up since I (somehow) understood that the role of NameID is different of what I wanted to achieve.

Some insights: You can have a look at (as a starting debugging point) getSAML2NameIDGenerator method from net.shibboleth.idp.saml.nameid.impl.NameIdentifierGenerationServiceImpl, module Shibboleth IdP :: SAML Profile Implementation. The main issue here is that the returned value (saml2Generator) is an implementation of org.opensaml.saml.saml2.profile.SAML2NameIDGenerator. Since in the module there is a compile dependency only to open-saml-api-3.1.1.jar you will have to add manually in pom.xml the dependency to the actual implementation.

        <dependency>
            <groupId>${opensaml.groupId}</groupId>
            <artifactId>opensaml-saml-impl</artifactId>
            <version>${opensaml.version}</version>
        </dependency>
 

Be sure to remove the test time dependency (a compile error will prevent to successfully compile the module anyway).

The default configured strategy relies on TransientSAML2NameIDGenerator and CryptoTransientIdGenerationStrategy (generate method).

Configuration of the strategy is done in conf/saml-nameid.xml. Initially I tried switching from <ref bean="shibboleth.SAML2TransientGenerator" /> to

    <!-- urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress -->
    <bean parent="shibboleth.SAML2AttributeSourcedGenerator"
            p:format="urn:oasis:names:tc:SAML:2.0:nameid-format:transient"
            p:attributeSourceIds="#{ {'cn'} }" />

Watch out of the p:format attribute value, changed from email to transient as the net.shibboleth.idp.saml.nameid.impl.AttributeSourcedSAML2NameIDGenerator (one of the subsequent calls to other classes methods) will check for attributes with this format.

Anyway, it didn't work because here we speak about SAML attributes (that will go into Assertions) not about LDAP attributes and since I didn't get in the response no Assertions...no NameID was also generated.

Finally Getting the Assertions

After further hours spent in analysing online wiki it was obvious that AttributeResolverConfiguration was next step to success.

Having a look in conf/attribute-resolver.xml and putting breakpoints in classes like from net.shibboleth.idp.attribute.resolver.ad.impl namespace like (PrincipalNameAttributeDefinition, ScopedAttributeDefinition, AbstractAttributeReleaseAction or AttributeResolverImpl) it looked that everything went Ok since I've got all 4 attributes with the expected values (by watching variables during breakpoint sessions) but no Assertions were generated.

So I had a look in conf/atribute-filter.xml and surprise, attribute generation was filtered by a fictious EntityId of https://sp.example.org. By changing the attribute value to the right value in <afp:PolicyRequirementRule xsi:type="basic:AttributeRequesterString" value="<mySPEntityID>" /> everything started to work as expected.

QWZ0ZXIgYWJvdXQgdHdvIHdlZWtzIG9mIHRyaWFsIGFuZCBlcnJvciBJIGZpbmFsbHkgc3VjY2VlZGVkIHRvIGhhdmUgYSBTU08vU0FNTCBQT0MgYmFzZWQgb24gb3BlbiBzb3VyY2UgY29tcG9uZW50cyAoU2hpYmJvbGV0L0FwYWNoZURTL0tlbnRvcklUKSB3b3JraW5nLiANCg0KVGhlIGxhc3Qgc3RlcCB3YXMgc3RydWdnbGluZyB3aXRoIGNvbnZpbmNpbmcgU2hpYmJvbGV0IHRvIHJlc3BvbmQgdG8gU0FNTCByZXF1ZXN0cy4NCg0KVGhlIG1haW4gcHJvYmxlbSAoZm9yIG5ld2JpZXMpIGlzIHRoZSBsYWNrIG9mIGFueSBvdmVydmlldyB0eXBlIGluZm9ybWF0aW9uOyBhbG1vc3QgYWxsIGRvY3VtZW50YXRpb24gKFtodHRwczovL3dpa2kuc2hpYmJvbGV0aC5uZXQvY29uZmx1ZW5jZS9kaXNwbGF5L0lEUDMwL0hvbWVdKGh0dHBzOi8vd2lraS5zaGliYm9sZXRoLm5ldC9jb25mbHVlbmNlL2Rpc3BsYXkvSURQMzAvSG9tZSkpIGZvY3VzZXMgb24gZGV0YWlsZWQgc3BlY2lmaWNhdGlvbiBhc3BlY3RzIHNvIHlvdSBnZXQgbG9zdCBpbiB0ZW5zIG9mIC5wcm9wZXJ0aWVzLy54bWwgY29uZmlndXJhdGlvbiBmaWxlcy4NCg0KIyBEZWJ1Z2dpbmcNCg0KT2YgY291cnNlIG15IGZpcnN0IGNvbmNlcm4gd2FzIGFib3V0IGJlaW5nIGFibGUgdG8gc2V0IGJyZWFrcG9pbnRzIGFuZCBnbyBzdGVwIGJ5IHN0ZXAgdGhyb3VnaCBzb3VyY2VzLg0KDQpHbyB0byBqZXR0eV9iYXNlIGFuZCBlZGl0IHN0YXJ0LmluaSBieSBhZGRpbmcgdGhlIGZvbGxvd2luZyBsaW5lczoNCg0KYGBgDQotWGRlYnVnIA0KLVhydW5qZHdwOnRyYW5zcG9ydD1kdF9zb2NrZXQsYWRkcmVzcz01MDA1LHNlcnZlcj15LHN1c3BlbmQ9bg0KYGBgDQoNClJlc3RhcnQgamV0dHkuDQoNCkRvd25sb2FkIGxhdGVzdCBzb3VyY2UsIG9wZW4gbWFpbiBwb20ueG1sIGFuZCByZWJ1aWxkIHByb2plY3QuDQoNCiMgRmlyc3Qgc3RlcHMNCg0KU3VwcG9zaW5nIHlvdSBoYXZlIHRoZSBtZXRhZGF0YSBmaWxlIGZvciBhIHRhcmdldCBTUCAod2UgZG8gbm90IG5lZWQgdGhlIFNQIGltcGxlbWVudGF0aW9uIGZvciBub3cpIGRlcGxveSBpdCBpbiBgc2hpYmJvbGV0X3NwL21ldGFkYXRhYCAoeW91IHdpbGwgYWxzbyBmaW5kIHRoZXJlIGlkcF9tZXRhZGF0YS54bWwsIHRoZSBtZXRhZGF0YSByZWxhdGVkIHdpdGggSXNQKS4NCg0KRWRpdCBgY29uZi9tZXRhZGF0YS1wcm92aWRlcnMueG1sYCBieSBhZGRpbmcgaW5mb3JtYXRpb24gYWJvdXQgeW91IFNQOyBzb21ldGhpbmcgbGlrZSB0aGlzOg0KKiAgICBgPE1ldGFkYXRhUHJvdmlkZXIgaWQ9IkxvY2FsTWV0YWRhdGEiICB4c2k6dHlwZT0iRmlsZXN5c3RlbU1ldGFkYXRhUHJvdmlkZXIiIG1ldGFkYXRhRmlsZT0iJXtpZHAuaG9tZX0vbWV0YWRhdGEvbXlTUG1ldGFkYXRhLnhtbCIvPmANCg0KDQpFZGl0IGNvbmYvbGRhcC5wcm9wcmllcnRpZXMgKHdhdGNoIG91dCBmb3IgdXNlU3RhcnRUTFMpOg0KDQoqIGlkcC5hdXRobi5MREFQLmxkYXBVUkwJCQkJPSA8bGRhcCB1cmxcPg0KKiBpZHAuYXV0aG4uTERBUC51c2VTdGFydFRMUyAgICAJCT0gZmFsc2UNCiogaWRwLmF1dGhuLkxEQVAucmV0dXJuQXR0cmlidXRlcyAgIAk9IFw8Y29tbWEgc2VwYXJhdGVkIExEQVAgYXR0cmlidXRlc1w+DQoqIGlkcC5hdXRobi5MREFQLmJhc2VETgkJCQkJPSA8YmFzZSBETiBmb3Igc2VhcmNoKQ0KKiBpZHAuYXV0aG4uTERBUC51c2VyRmlsdGVyICAgICAgICAgICAgID0gKFw8TERBUCBhdHRyaWJ1dGVcPj17dXNlcn0pDQoNCioqUmVtYXJrKio6IFRoaXMgaXMgZW5hb3VnaCBmb3IgZGVmYXVsdCAqYW5vblNlYXJjaEF1dGhlbnRpY2F0b3IqOyBpZiAqYmluZFNlYXJjaEF1dGhlbnRpY2F0b3IqIGlzIHJlcXVpcmVkIGJlIHN1cmUgdG86DQoNCiogdW5jb21tZW50IGFuZCBjaGFuZ2UgYWNjb3JkaW5nbHkgYGlkcC5hdXRobi5MREFQLmF1dGhlbnRpY2F0b3IgPSBiaW5kU2VhcmNoQXV0aGVudGljYXRvcmANCiogY2hhbmdlIHRoZSB0d28gY29uZmlnYXJ0aW9uIGVudHJpZXM6DQoJKiBpZHAuYXV0aG4uTERBUC5iaW5kRE4gICAgICAgICAgICAgICAgICAgICAgICAgICA9IFw8RE4gb2YgYmluZCB1c2VyXD4NCgkqIGlkcC5hdXRobi5MREFQLmJpbmRETkNyZWRlbnRpYWwgICAgICAgICAgICAgICAgID0gXDxwYXNzd29yZFw+DQoNCg0KDQpBdCB0aGlzIG1vbWVudCB3ZSBjYW4gdGVzdCBhdXRoZW50aWNhdGlvbiBieSB1c2luZyB0aGUgSWRQIGluaXRpYXRlZCBsb2dpbiBVUkwgOiBgSFRUUFM6Ly88c2hpYmJsb2xldC11cmw+OjxodHRwcy1wb3J0Pi9pZHAvcHJvZmlsZS9TQU1MMi9VbnNvbGljaXRlZC9TU08/cHJvdmlkZXJJZD08U1BfZW50aXR5SUQ+YA0KDQo8U1BfZW50aXR5SUQ+IGlzIHRoZSB0aGUgU1AgRW50aXR5SWQgYXMgc3BlY2lmaWVkIGluIG1ldGFkYXRhIGZpbGUuDQoNCkxvZ291dCBjYW4gYmUgYWNoaWV2ZWQgdGhyb3VnaCBgaHR0cHM6Ly88c2hpYmJsb2xldC11cmw+OjxodHRwcy1wb3J0Pi9pZHAvcHJvZmlsZS9Mb2dvdXRgDQoNCkluIG9yZGVyIHRvIGRlYnVnIHRoZSBhdXRoZW50aWNhdGlvbiBwcm9jZWVkcyBoYXZlIGEgbG9vayBpbiBgbmV0LnNoaWJib2xldGguaWRwLmF1dGhuLmltcGwuVmFsaWRhdGVVc2VybmFtZVBhc3N3b3JkQWdhaW5zdExEQVBgIChtZXRob2QgZG9FeGVjdXRlKSwgbW9kdWxlIGBTaGliYm9sZXRoIElkUCA6OiBBdXRoZW50aWNhdGlvbiBJbXBsZW1lbnRhdGlvbmA7IGFsc28gbG9vayBhdCBgb3JnLmxkYXB0aXZlLmF1dGguQXV0aGVudGljYXRvcmAgZnJvbSBsYWRwdGl2ZS0xLjAuMC5qYXIgKHNlbGVjdCB0aGUgamFyIHdpdGggcmlnaHQgY2xpY2stXD5Eb3dubG9hZCBzb3VyY2UsIGV0YykuDQoNCiMgQ29uZmlndXJlIFJlc3BvbnNlDQoNCk5vdywgZXZlbiBpZiB5b3Ugd2lsbCBnZXQgYSBicm93c2VyIGVycm9yIGFmdGVyIGdvaW5nIHRocm91Z2ggdGhlIGxvZ2luIFVJIHByb2Nlc3MgKHNpbmNlIHdlIGRvdCBoYXZlIHRoZSBhY3R1YWwgU1AgaW1wbGVtZW50YXRpb24pIHlvdSBjYW4gdXNlIGJyb3dzZXIgZGV2ZWxvcGVyIHRvb2xzIHRvIGNoZWNrIHRoZSBTQU1MUmVzcG9uc2UgKHVzZSBTU09DaXJjbGUgUE9TVCBkZWNvZGVyKS4gV2Ugbm90aWNlIHRoYXQgdGhlIFN1YmplY3QgcGFydCBpcyBlbmNyeXB0ZWQuDQoNClJlc3BvbnNlIGNvbmZpZ3VyYXRpb24gY2FuIGJlIGRvbmUgdGhyb3VnaCAgY29uZi9yZWx5aW5nLXBhcnR5LnhtbDsgaW5zaWRlIGAgPGJlYW4gaWQ9InNoaWJib2xldGguRGVmYXVsdFJlbHlpbmdQYXJ0eSIgcGFyZW50PSJSZWx5aW5nUGFydHkiPmAgY2hhbmdlIHRoZSBTQU1MMi5TU08gYXR0cmlidXRlcyBhY2NvcmRpbmcgdG8geW91ciBuZWVkczoNCg0KKiAoc2FtcGxlKTogYDxiZWFuIHBhcmVudD0iU0FNTDIuU1NPIiBwOnBvc3RBdXRoZW50aWNhdGlvbkZsb3dzPSJhdHRyaWJ1dGUtcmVsZWFzZSIgcDplbmNyeXB0QXNzZXJ0aW9ucz0iZmFsc2UiIHA6ZW5jcnlwdEF0dHJpYnV0ZXM9ImZhbHNlIiBwOmVuY3J5cHROYW1lSURzPSJmYWxzZSIvPmANCiANCk5vdyB3ZSBjYW4gc2VlIGluIGNsZWFyIHRleHQgKGFmdGVyIGRlY29kaW5nKSB0aGUgU3ViamVjdCBpbmZvIGJ1dCBzdXJwcmlzZShzKToNCg0KKiBOYW1lSUQgaGFzIGEgc3RyYW5nZSB2YWx1ZSAoc29tZSBCYXNlNjQgZW5jb2RlZCB2YWx1ZSkNCiogTm8gb3RoZXIgYXNzZXJ0aW9ucywgZXZlbiBpZiB3ZSBjb25maWd1cmVkIExEQVAgdG8gcmV0dXJuIG1vcmUgYXR0cmlidXRlcw0KDQojIE5hbWVJRA0KDQpJIHdhcyB1c2VkIHdpdGggQURGUyB3aGVyZSB5b3UgY2FuIGVhc2lseSByZXR1cm4gc0FNQWNvdW50TmFtZSBhcyBOYW1lSUQuIEl0IHR1cm5lZCBvdXQgdG8gYmUgYSB2ZXJ5IGRpZmZpY3VsdCB0YXNrIHNvIGF0IHRoZSBlbmQgb2YgdGhlIGRheSBJIGdhdmUgdXAgc2luY2UgSSAoc29tZWhvdykgdW5kZXJzdG9vZCB0aGF0IHRoZSByb2xlIG9mIE5hbWVJRCBpcyBkaWZmZXJlbnQgb2Ygd2hhdCBJIHdhbnRlZCB0byBhY2hpZXZlLg0KDQpTb21lIGluc2lnaHRzOg0KWW91IGNhbiBoYXZlIGEgbG9vayBhdCAoYXMgYSBzdGFydGluZyBkZWJ1Z2dpbmcgcG9pbnQpIGBnZXRTQU1MMk5hbWVJREdlbmVyYXRvcmAgbWV0aG9kIGZyb20gYG5ldC5zaGliYm9sZXRoLmlkcC5zYW1sLm5hbWVpZC5pbXBsLk5hbWVJZGVudGlmaWVyR2VuZXJhdGlvblNlcnZpY2VJbXBsYCwgbW9kdWxlIGBTaGliYm9sZXRoIElkUCA6OiBTQU1MIFByb2ZpbGUgSW1wbGVtZW50YXRpb25gLiBUaGUgbWFpbiBpc3N1ZSBoZXJlIGlzIHRoYXQgdGhlIHJldHVybmVkIHZhbHVlIChzYW1sMkdlbmVyYXRvcikgaXMgYW4gaW1wbGVtZW50YXRpb24gb2YgYG9yZy5vcGVuc2FtbC5zYW1sLnNhbWwyLnByb2ZpbGUuU0FNTDJOYW1lSURHZW5lcmF0b3JgLiBTaW5jZSBpbiB0aGUgbW9kdWxlIHRoZXJlIGlzIGEgY29tcGlsZSBkZXBlbmRlbmN5IG9ubHkgdG8gYG9wZW4tc2FtbC1hcGktMy4xLjEuamFyYCB5b3Ugd2lsbCBoYXZlIHRvIGFkZCBtYW51YWxseSBpbiBwb20ueG1sIHRoZSBkZXBlbmRlbmN5IHRvIHRoZSBhY3R1YWwgaW1wbGVtZW50YXRpb24uDQoNCmBgYHhtbA0KICAgICAgICA8ZGVwZW5kZW5jeT4NCiAgICAgICAgICAgIDxncm91cElkPiR7b3BlbnNhbWwuZ3JvdXBJZH08L2dyb3VwSWQ+DQogICAgICAgICAgICA8YXJ0aWZhY3RJZD5vcGVuc2FtbC1zYW1sLWltcGw8L2FydGlmYWN0SWQ+DQogICAgICAgICAgICA8dmVyc2lvbj4ke29wZW5zYW1sLnZlcnNpb259PC92ZXJzaW9uPg0KICAgICAgICA8L2RlcGVuZGVuY3k+DQogDQpgYGANCg0KQmUgc3VyZSB0byByZW1vdmUgdGhlIHRlc3QgdGltZSBkZXBlbmRlbmN5IChhIGNvbXBpbGUgZXJyb3Igd2lsbCBwcmV2ZW50IHRvIHN1Y2Nlc3NmdWxseSBjb21waWxlIHRoZSBtb2R1bGUgYW55d2F5KS4NCg0KVGhlIGRlZmF1bHQgY29uZmlndXJlZCBzdHJhdGVneSByZWxpZXMgb24gYFRyYW5zaWVudFNBTUwyTmFtZUlER2VuZXJhdG9yYCBhbmQgYENyeXB0b1RyYW5zaWVudElkR2VuZXJhdGlvblN0cmF0ZWd5YCAoYGdlbmVyYXRlYCBtZXRob2QpLg0KDQpDb25maWd1cmF0aW9uIG9mIHRoZSBzdHJhdGVneSBpcyBkb25lIGluIGBjb25mL3NhbWwtbmFtZWlkLnhtbGAuIEluaXRpYWxseSBJIHRyaWVkIHN3aXRjaGluZyBmcm9tIGA8cmVmIGJlYW49InNoaWJib2xldGguU0FNTDJUcmFuc2llbnRHZW5lcmF0b3IiIC8+YCB0byANCmBgYA0KCTwhLS0gdXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6MS4xOm5hbWVpZC1mb3JtYXQ6ZW1haWxBZGRyZXNzIC0tPg0KCTxiZWFuIHBhcmVudD0ic2hpYmJvbGV0aC5TQU1MMkF0dHJpYnV0ZVNvdXJjZWRHZW5lcmF0b3IiDQogICAgICAgICAgICBwOmZvcm1hdD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOm5hbWVpZC1mb3JtYXQ6dHJhbnNpZW50Ig0KICAgICAgICAgICAgcDphdHRyaWJ1dGVTb3VyY2VJZHM9IiN7IHsnY24nfSB9IiAvPg0KDQpgYGANCldhdGNoIG91dCBvZiB0aGUgYHA6Zm9ybWF0YCBhdHRyaWJ1dGUgdmFsdWUsIGNoYW5nZWQgZnJvbSAqZW1haWwqIHRvICp0cmFuc2llbnQqIGFzIHRoZSBgbmV0LnNoaWJib2xldGguaWRwLnNhbWwubmFtZWlkLmltcGwuQXR0cmlidXRlU291cmNlZFNBTUwyTmFtZUlER2VuZXJhdG9yYCAob25lIG9mIHRoZSBzdWJzZXF1ZW50IGNhbGxzIHRvIG90aGVyIGNsYXNzZXMgbWV0aG9kcykgd2lsbCBjaGVjayAgZm9yIGF0dHJpYnV0ZXMgd2l0aCB0aGlzIGZvcm1hdC4NCg0KQW55d2F5LCBpdCBkaWRuJ3Qgd29yayBiZWNhdXNlIGhlcmUgd2Ugc3BlYWsgYWJvdXQgU0FNTCBhdHRyaWJ1dGVzICh0aGF0IHdpbGwgZ28gaW50byBBc3NlcnRpb25zKSAgbm90IGFib3V0IExEQVAgYXR0cmlidXRlcyBhbmQgc2luY2UgSSBkaWRuJ3QgZ2V0IGluIHRoZSByZXNwb25zZSBubyBBc3NlcnRpb25zLi4ubm8gTmFtZUlEIHdhcyBhbHNvIGdlbmVyYXRlZC4NCg0KIyBGaW5hbGx5IEdldHRpbmcgdGhlIEFzc2VydGlvbnMNCg0KQWZ0ZXIgZnVydGhlciBob3VycyBzcGVudCBpbiBhbmFseXNpbmcgb25saW5lIHdpa2kgaXQgd2FzIG9idmlvdXMgdGhhdCAgW0F0dHJpYnV0ZVJlc29sdmVyQ29uZmlndXJhdGlvbl0oaHR0cHM6Ly93aWtpLnNoaWJib2xldGgubmV0L2NvbmZsdWVuY2UvZGlzcGxheS9JRFAzMC9BdHRyaWJ1dGVSZXNvbHZlckNvbmZpZ3VyYXRpb24pIHdhcyBuZXh0IHN0ZXAgdG8gc3VjY2Vzcy4NCg0KSGF2aW5nIGEgbG9vayBpbiBgY29uZi9hdHRyaWJ1dGUtcmVzb2x2ZXIueG1sYCBhbmQgcHV0dGluZyBicmVha3BvaW50cyBpbiBjbGFzc2VzIGxpa2UgZnJvbSBgbmV0LnNoaWJib2xldGguaWRwLmF0dHJpYnV0ZS5yZXNvbHZlci5hZC5pbXBsYCBuYW1lc3BhY2UgbGlrZSAoYFByaW5jaXBhbE5hbWVBdHRyaWJ1dGVEZWZpbml0aW9uYCwgYFNjb3BlZEF0dHJpYnV0ZURlZmluaXRpb25gLCBgQWJzdHJhY3RBdHRyaWJ1dGVSZWxlYXNlQWN0aW9uYCBvciBgQXR0cmlidXRlUmVzb2x2ZXJJbXBsYCkgaXQgbG9va2VkIHRoYXQgZXZlcnl0aGluZyB3ZW50IE9rIHNpbmNlIEkndmUgZ290IGFsbCA0IGF0dHJpYnV0ZXMgd2l0aCB0aGUgZXhwZWN0ZWQgdmFsdWVzIChieSB3YXRjaGluZyB2YXJpYWJsZXMgZHVyaW5nIGJyZWFrcG9pbnQgc2Vzc2lvbnMpIGJ1dCBubyBBc3NlcnRpb25zIHdlcmUgZ2VuZXJhdGVkLg0KDQpTbyBJIGhhZCBhIGxvb2sgaW4gYGNvbmYvYXRyaWJ1dGUtZmlsdGVyLnhtbGAgYW5kIHN1cnByaXNlLCBhdHRyaWJ1dGUgZ2VuZXJhdGlvbiB3YXMgZmlsdGVyZWQgYnkgYSBmaWN0aW91cyBFbnRpdHlJZCBvZiAqaHR0cHM6Ly9zcC5leGFtcGxlLm9yZyouIEJ5IGNoYW5naW5nIHRoZSBhdHRyaWJ1dGUgKnZhbHVlKiB0byB0aGUgcmlnaHQgdmFsdWUgaW4gYDxhZnA6UG9saWN5UmVxdWlyZW1lbnRSdWxlIHhzaTp0eXBlPSJiYXNpYzpBdHRyaWJ1dGVSZXF1ZXN0ZXJTdHJpbmciIHZhbHVlPSI8bXlTUEVudGl0eUlEPiIgLz5gIGV2ZXJ5dGhpbmcgc3RhcnRlZCB0byB3b3JrIGFzIGV4cGVjdGVkLg0K

Wednesday, October 28, 2015

Shibboleth-Basic Installation

Jetty Base

As a prerequisite be sure you understand the Jetty Base concept: a separate directory structure containing just configuration info. In order to implement it you have to add JETTY_BASE environment variable to /etc/default/jetty or to /etc/environment pointing to the above mentioned directory.

Install Steps

Just things that went wrong during my installation (or worth mentioning) are captured here.

Run initial steps as described in general instructions section https://wiki.shibboleth.net/confluence/display/IDP30/Installation#Installation-Non-WindowsInstallation.

Go now to the container specific instructions (https://wiki.shibboleth.net/confluence/display/IDP30/Jetty93) in my case.

Create the JETTY_BASE directory structure as mentioned at and apply step by step the instructions. As I noticed afterwards, there is already a jetty_base directory in the unpacked structure (which is not copied to the target directory by the setup script). Some of the artefacts can be copied from this directory (like jars) but be aware that the content of configuration files is not exactly as what the instructions require. Delete for now all the optional files.

Configure Jetty Modules and JVM Settings

For start.ini:

  • Uncomment and change the line #-Didp.home=/path/to/shibboleth-idp to -Didp.home=/opt/shibboleth-idp even if the comment suggest to let it commented!!!
  • Add the following lines to the end of the file:
    • -Djava.io.tmpdir=tmp
    • -Dorg.eclipse.jetty.LEVEL=DEBUG (so that we have logging info for Jetty start-up)

Configure HTTP Connectors

Be sure that you deploy (and change the name and the password accordingly) a valid server side certificate as configured by the lines added in ssl.ini:

  • jetty.sslContext.keyStorePath=/opt/shibboleth-idp/credentials/idp-browser.p12
  • jetty.sslContext.keyStoreType=PKCS12
  • jetty.sslContext.keyStorePassword=thepasswordgoeshere

So far I didn't do any other configuration (neither logging nor SOAP sections).

Before trying to stop (Jetty is automatically launched by OS if configured as depicted in post used in previous blog) and start be sure you make jetty owner on all files in /opt/shibboleth-idp and opt/jetty directories, otherwise it will not be able to read idp.war or SSL certificate, for instance. Run sudo chown -R jetty:jetty /opt/shibbolet-idp or chmod -R a+r /opt/shibbolet-idp After (successful or not) start have a look in logging directories of jetty_base and shibbolet-idp.

You could also use java -jar opt/jetty//start.jar -DDEBUG=true -Dorg.eclipse.jetty.LEVEL=DEBUG jetty.port=8085 (from jetty_base!; don't forget to check if /etc/environment contains the proper definition for JAVA_HOME).

Other helpful commands:

  • service jetty status -l
  • netstat -tulpn ;use this command to see what processes keep open TCP ports(sometimes Jetty service does not stop/start properly and a java process remains hanged keeping opened ports for HTTP and HTTPS which prevents restarting the service)

jetty Default Environment Variables

I used the following /etc/defaults/jetty file:

JETTY_HOME=/opt/jetty
JETTY_BASE=/opt/shibboleth-idp/jetty-base
NO_START=0
JETTY_HOST=0.0.0.0
JETTY_USER=jetty
  • For a complete list of environment options have a look in /etc/init.d/jetty
  • I suppose most of the options can be moved in start.ini (apart from JETTY_HOME, JETTY_BASE and JETTY_USER, of course) or use specific Jetty configuration options from jetty_base/etc/*.ini (like jetty.ssl.host from ssl.ini)
IyBKZXR0eSBCYXNlDQoNCkFzIGEgcHJlcmVxdWlzaXRlIGJlIHN1cmUgeW91IHVuZGVyc3RhbmQgdGhlIEpldHR5IEJhc2UgY29uY2VwdDogYSBzZXBhcmF0ZSBkaXJlY3Rvcnkgc3RydWN0dXJlIGNvbnRhaW5pbmcganVzdCBjb25maWd1cmF0aW9uIGluZm8uIEluIG9yZGVyIHRvIGltcGxlbWVudCBpdCB5b3UgaGF2ZSB0byBhZGQgSkVUVFlfQkFTRSBlbnZpcm9ubWVudCB2YXJpYWJsZSB0byAvZXRjL2RlZmF1bHQvamV0dHkgb3IgdG8gL2V0Yy9lbnZpcm9ubWVudCBwb2ludGluZyB0byB0aGUgYWJvdmUgbWVudGlvbmVkIGRpcmVjdG9yeS4NCg0KIyBJbnN0YWxsIFN0ZXBzDQoNCkp1c3QgdGhpbmdzIHRoYXQgd2VudCB3cm9uZyBkdXJpbmcgbXkgaW5zdGFsbGF0aW9uIChvciB3b3J0aCBtZW50aW9uaW5nKSBhcmUgY2FwdHVyZWQgaGVyZS4NCg0KUnVuIGluaXRpYWwgc3RlcHMgYXMgZGVzY3JpYmVkIGluIGdlbmVyYWwgaW5zdHJ1Y3Rpb25zIHNlY3Rpb24gW2h0dHBzOi8vd2lraS5zaGliYm9sZXRoLm5ldC9jb25mbHVlbmNlL2Rpc3BsYXkvSURQMzAvSW5zdGFsbGF0aW9uI0luc3RhbGxhdGlvbi1Ob24tV2luZG93c0luc3RhbGxhdGlvbl0oaHR0cHM6Ly93aWtpLnNoaWJib2xldGgubmV0L2NvbmZsdWVuY2UvZGlzcGxheS9JRFAzMC9JbnN0YWxsYXRpb24jSW5zdGFsbGF0aW9uLU5vbi1XaW5kb3dzSW5zdGFsbGF0aW9uKS4NCg0KR28gbm93IHRvIHRoZSBjb250YWluZXIgc3BlY2lmaWMgaW5zdHJ1Y3Rpb25zIChbaHR0cHM6Ly93aWtpLnNoaWJib2xldGgubmV0L2NvbmZsdWVuY2UvZGlzcGxheS9JRFAzMC9KZXR0eTkzXShodHRwczovL3dpa2kuc2hpYmJvbGV0aC5uZXQvY29uZmx1ZW5jZS9kaXNwbGF5L0lEUDMwL0pldHR5OTMpKSBpbiBteSBjYXNlLg0KDQpDcmVhdGUgdGhlIEpFVFRZX0JBU0UgZGlyZWN0b3J5IHN0cnVjdHVyZSBhcyBtZW50aW9uZWQgYXQgIGFuZCBhcHBseSBzdGVwIGJ5IHN0ZXAgdGhlIGluc3RydWN0aW9ucy4gQXMgSSBub3RpY2VkIGFmdGVyd2FyZHMsIHRoZXJlIGlzIGFscmVhZHkgYSBqZXR0eV9iYXNlIGRpcmVjdG9yeSBpbiB0aGUgdW5wYWNrZWQgc3RydWN0dXJlICh3aGljaCBpcyBub3QgY29waWVkIHRvIHRoZSB0YXJnZXQgZGlyZWN0b3J5IGJ5IHRoZSBzZXR1cCBzY3JpcHQpLiBTb21lIG9mIHRoZSBhcnRlZmFjdHMgY2FuIGJlIGNvcGllZCBmcm9tIHRoaXMgZGlyZWN0b3J5IChsaWtlIGphcnMpIGJ1dCBiZSBhd2FyZSB0aGF0IHRoZSBjb250ZW50IG9mIGNvbmZpZ3VyYXRpb24gZmlsZXMgaXMgbm90IGV4YWN0bHkgYXMgd2hhdCB0aGUgaW5zdHJ1Y3Rpb25zIHJlcXVpcmUuICoqRGVsZXRlIGZvciBub3cgYWxsIHRoZSBvcHRpb25hbCBmaWxlcy4qKg0KDQoNCiMjIENvbmZpZ3VyZSBKZXR0eSBNb2R1bGVzIGFuZCBKVk0gU2V0dGluZ3MNCg0KRm9yIGBzdGFydC5pbmlgOg0KDQoqIFVuY29tbWVudCBhbmQgY2hhbmdlIHRoZSBsaW5lIGAjLURpZHAuaG9tZT0vcGF0aC90by9zaGliYm9sZXRoLWlkcGAgdG8gYC1EaWRwLmhvbWU9L29wdC9zaGliYm9sZXRoLWlkcGAgZXZlbiBpZiB0aGUgY29tbWVudCBzdWdnZXN0IHRvIGxldCBpdCBjb21tZW50ZWQhISENCiogQWRkIHRoZSBmb2xsb3dpbmcgbGluZXMgdG8gdGhlIGVuZCBvZiB0aGUgZmlsZTogDQoJKiBgLURqYXZhLmlvLnRtcGRpcj10bXBgDQoJKiBgLURvcmcuZWNsaXBzZS5qZXR0eS5MRVZFTD1ERUJVR2AgKHNvIHRoYXQgd2UgaGF2ZSBsb2dnaW5nIGluZm8gZm9yIEpldHR5IHN0YXJ0LXVwKQ0KDQojIyBDb25maWd1cmUgSFRUUCBDb25uZWN0b3JzDQoNCkJlIHN1cmUgdGhhdCB5b3UgZGVwbG95IChhbmQgY2hhbmdlIHRoZSBuYW1lIGFuZCB0aGUgcGFzc3dvcmQgYWNjb3JkaW5nbHkpIGEgdmFsaWQgc2VydmVyIHNpZGUgY2VydGlmaWNhdGUgYXMgY29uZmlndXJlZCBieSB0aGUgbGluZXMgYWRkZWQgaW4gc3NsLmluaToNCg0KKiBqZXR0eS5zc2xDb250ZXh0LmtleVN0b3JlUGF0aD0vb3B0L3NoaWJib2xldGgtaWRwL2NyZWRlbnRpYWxzLyoqaWRwLWJyb3dzZXIucDEyKioNCiogamV0dHkuc3NsQ29udGV4dC5rZXlTdG9yZVR5cGU9UEtDUzEyDQoqIGpldHR5LnNzbENvbnRleHQua2V5U3RvcmVQYXNzd29yZD0qKnRoZXBhc3N3b3JkZ29lc2hlcmUqKg0KDQoNClNvIGZhciBJIGRpZG4ndCBkbyBhbnkgb3RoZXIgY29uZmlndXJhdGlvbiAobmVpdGhlciBsb2dnaW5nIG5vciBTT0FQIHNlY3Rpb25zKS4NCg0KQmVmb3JlIHRyeWluZyB0byBzdG9wIChKZXR0eSBpcyBhdXRvbWF0aWNhbGx5IGxhdW5jaGVkIGJ5IE9TIGlmIGNvbmZpZ3VyZWQgYXMgZGVwaWN0ZWQgaW4gcG9zdCB1c2VkIGluIHByZXZpb3VzIGJsb2cpIGFuZCBzdGFydCBiZSBzdXJlIHlvdSBtYWtlIGpldHR5IG93bmVyIG9uIGFsbCBmaWxlcyBpbiBgL29wdC9zaGliYm9sZXRoLWlkcGAgYW5kIGBvcHQvamV0dHlgIGRpcmVjdG9yaWVzLCBvdGhlcndpc2UgaXQgd2lsbCBub3QgYmUgYWJsZSB0byByZWFkIGlkcC53YXIgb3IgU1NMIGNlcnRpZmljYXRlLCBmb3IgaW5zdGFuY2UuIFJ1biBgc3VkbyBjaG93biAtUiBqZXR0eTpqZXR0eSAvb3B0L3NoaWJib2xldC1pZHBgIG9yIGBjaG1vZCAtUiBhK3IgL29wdC9zaGliYm9sZXQtaWRwYA0KQWZ0ZXIgKHN1Y2Nlc3NmdWwgb3Igbm90KSBzdGFydCBoYXZlIGEgbG9vayBpbiBsb2dnaW5nIGRpcmVjdG9yaWVzIG9mIGpldHR5X2Jhc2UgYW5kIHNoaWJib2xldC1pZHAuDQoNCllvdSBjb3VsZCBhbHNvIHVzZSBgamF2YSAtamFyIG9wdC9qZXR0eS8vc3RhcnQuamFyICAtRERFQlVHPXRydWUgLURvcmcuZWNsaXBzZS5qZXR0eS5MRVZFTD1ERUJVRyBqZXR0eS5wb3J0PTgwODVgIChmcm9tIGpldHR5X2Jhc2UhOyBkb24ndCBmb3JnZXQgdG8gY2hlY2sgaWYgYC9ldGMvZW52aXJvbm1lbnRgIGNvbnRhaW5zIHRoZSBwcm9wZXIgZGVmaW5pdGlvbiBmb3IgSkFWQV9IT01FKS4NCg0KT3RoZXIgaGVscGZ1bCBjb21tYW5kczoNCg0KKiBgc2VydmljZSBqZXR0eSBzdGF0dXMgLWxgDQoqIGBuZXRzdGF0IC10dWxwbmAgO3VzZSB0aGlzIGNvbW1hbmQgdG8gc2VlIHdoYXQgcHJvY2Vzc2VzIGtlZXAgb3BlbiBUQ1AgcG9ydHMoc29tZXRpbWVzIEpldHR5IHNlcnZpY2UgZG9lcyBub3Qgc3RvcC9zdGFydCBwcm9wZXJseSBhbmQgYSBqYXZhIHByb2Nlc3MgcmVtYWlucyBoYW5nZWQga2VlcGluZyBvcGVuZWQgcG9ydHMgZm9yIEhUVFAgYW5kIEhUVFBTIHdoaWNoIHByZXZlbnRzIHJlc3RhcnRpbmcgdGhlIHNlcnZpY2UpDQoNCiMgamV0dHkgRGVmYXVsdCBFbnZpcm9ubWVudCBWYXJpYWJsZXMNCg0KSSB1c2VkIHRoZSBmb2xsb3dpbmcgYC9ldGMvZGVmYXVsdHMvamV0dHlgIGZpbGU6DQoNCmBgYA0KSkVUVFlfSE9NRT0vb3B0L2pldHR5DQpKRVRUWV9CQVNFPS9vcHQvc2hpYmJvbGV0aC1pZHAvamV0dHktYmFzZQ0KTk9fU1RBUlQ9MA0KSkVUVFlfSE9TVD0wLjAuMC4wDQpKRVRUWV9VU0VSPWpldHR5DQpgYGANCg0KKiBGb3IgYSBjb21wbGV0ZSBsaXN0IG9mIGVudmlyb25tZW50IG9wdGlvbnMgaGF2ZSBhIGxvb2sgaW4gYC9ldGMvaW5pdC5kL2pldHR5YA0KKiBJIHN1cHBvc2UgbW9zdCBvZiB0aGUgb3B0aW9ucyBjYW4gYmUgbW92ZWQgaW4gYHN0YXJ0LmluaWAgKGFwYXJ0IGZyb20gSkVUVFlfSE9NRSwgSkVUVFlfQkFTRSBhbmQgSkVUVFlfVVNFUiwgb2YgY291cnNlKSBvciB1c2Ugc3BlY2lmaWMgSmV0dHkgY29uZmlndXJhdGlvbiBvcHRpb25zIGZyb20gamV0dHlfYmFzZS9ldGMvKi5pbmkgKGxpa2UgYGpldHR5LnNzbC5ob3N0YCBmcm9tIGBzc2wuaW5pYCkNCg==

Shibboleth-Jetty Installation

I decided to use the following supporting software infrastructure:

  • Ubuntu Server 15.10
  • Jetty 9.3

Installing Jetty

I used this post as a reference: http://www.ubuntugeek.com/install-jetty-9-java-servlet-engine-and-webserver-on-ubuntu-15-04-server.html

Please notice that Shibboleth 3 installation instructions recommend usage of Oracle JDK 8 as opposed to OpenJDK so install this one if your Linux distribution does not already has it installed.

sudo add-apt-repository ppa:webupd8team/java
sudo apt-get update
sudo apt-get install oracle-java8-installer

Be sure to set in /etc/environment JAVA_HOME=/usr/bin/java

SSBkZWNpZGVkIHRvIHVzZSB0aGUgZm9sbG93aW5nIHN1cHBvcnRpbmcgc29mdHdhcmUgaW5mcmFzdHJ1Y3R1cmU6DQoNCiogVWJ1bnR1IFNlcnZlciAxNS4xMA0KKiBKZXR0eSA5LjMNCg0KIyBJbnN0YWxsaW5nIEpldHR5DQoNCkkgdXNlZCB0aGlzIHBvc3QgYXMgYSByZWZlcmVuY2U6IFtodHRwOi8vd3d3LnVidW50dWdlZWsuY29tL2luc3RhbGwtamV0dHktOS1qYXZhLXNlcnZsZXQtZW5naW5lLWFuZC13ZWJzZXJ2ZXItb24tdWJ1bnR1LTE1LTA0LXNlcnZlci5odG1sXShodHRwOi8vd3d3LnVidW50dWdlZWsuY29tL2luc3RhbGwtamV0dHktOS1qYXZhLXNlcnZsZXQtZW5naW5lLWFuZC13ZWJzZXJ2ZXItb24tdWJ1bnR1LTE1LTA0LXNlcnZlci5odG1sKQ0KDQpQbGVhc2Ugbm90aWNlIHRoYXQgU2hpYmJvbGV0aCAzIGluc3RhbGxhdGlvbiBpbnN0cnVjdGlvbnMgcmVjb21tZW5kIHVzYWdlIG9mIE9yYWNsZSBKREsgOCBhcyBvcHBvc2VkIHRvIE9wZW5KREsgc28gaW5zdGFsbCB0aGlzIG9uZSBpZiB5b3VyIExpbnV4IGRpc3RyaWJ1dGlvbiBkb2VzIG5vdCBhbHJlYWR5IGhhcyBpdCBpbnN0YWxsZWQuDQoNCmBgYA0Kc3VkbyBhZGQtYXB0LXJlcG9zaXRvcnkgcHBhOndlYnVwZDh0ZWFtL2phdmENCnN1ZG8gYXB0LWdldCB1cGRhdGUNCnN1ZG8gYXB0LWdldCBpbnN0YWxsIG9yYWNsZS1qYXZhOC1pbnN0YWxsZXINCmBgYA0KDQpCZSBzdXJlIHRvIHNldCBpbiAvZXRjL2Vudmlyb25tZW50IGBKQVZBX0hPTUU9L3Vzci9iaW4vamF2YWANCg0K

A Farewell to Gluu

This will be short: trying to go forward another step with gluu I noticed that embedded SAML IdP, Shibbolet, is version 2.x, announced EOL for next year, while latest stable version is 3.1.2. Since my target was only SAML IdP I decided to go directly for Shibbolet.

VGhpcyB3aWxsIGJlIHNob3J0OiB0cnlpbmcgdG8gZ28gZm9yd2FyZCBhbm90aGVyIHN0ZXAgd2l0aCBnbHV1IEkgbm90aWNlZCB0aGF0IGVtYmVkZGVkIFNBTUwgSWRQLCBTaGliYm9sZXQsIGlzIHZlcnNpb24gMi54LCBhbm5vdW5jZWQgRU9MIGZvciBuZXh0IHllYXIsIHdoaWxlIGxhdGVzdCBzdGFibGUgdmVyc2lvbiBpcyAzLjEuMi4gU2luY2UgbXkgdGFyZ2V0IHdhcyBvbmx5IFNBTUwgSWRQIEkgZGVjaWRlZCB0byBnbyBkaXJlY3RseSBmb3IgU2hpYmJvbGV0Lg==

Sunday, October 25, 2015

Gluu (strickes forward :-))

Version

At the moment of writing the current version for OxTrust on GitHub is 3.0.2-SNAPSHOT but the installed version through apt-get (I used Ubuntu 14.0.4 LTS) is 2.3.4.Final

Installation

Installation is straight forward apart from first step: echo "deb http://repo.gluu.org/ubuntu/ trusty main" > /etc/apt/sources.list.d/gluu-repo.list because sudo echo..will not work; anyway, Google is our friend for one more time (as an alternative you can do the echo in another non-secured location and use sudo cp.. afterwards).

Cache Refresh Configuration

Configuration procedure, as described in on-line documentation looks simple. Unfortunately it fails to provide all the required details. Just to mention several inconsistencies:

  • Configuration is done on one form (as described in Configuration->Cache Refresh) while really enabling the functionality is done inConfiguration->Organization (!)
  • There are several mentions like :'Use SSL: Please tick the checkbox because the SSL has to be activated.': nice option since it looks you have no other option...
  • It is very unclear why you have to provide the Inum LDAP Server (described as the internal LDAP of the Gluu Server: if it's internal why should you re-specify it?) and why you have to provide the Server IP address in the Attribute Mapping section? (maybe this configurations have to do with external LDAP usage and some clustering configurations, just guessing)
  • Defining Customer Backend Key and Attributes is somehow redundant (and might induce not consistent configuration) with Attribute Mapping section: what if I define some attributes in first section and use other (as source) in the later?
  • It is not clear what the Enable checkbox meaning is: after Updating the configuration and performing a form refresh it will anyway become unchecked and the configuration file (/opt/gluu-server/opt/apache-tomcat.../conf/oxTrustCacheRefresh.properties) neither the Velocity template (oxTrust/configuration/target/classes/conf/oxTrustCacheRefresh.properties.vm) do not have any reference to it.
  • And it's keep going...

Anyway, here are my findings.

Source code

Fortunately the source code is available (in Maven project format) so you can enable Debug and see what's going on.

Enable debugging by changing the /opt/gluu-server/opt/apache-tomcat.../conf/gluuTomcatWrapper.conf by adding the following lines in # Java Additional Parameters section

wrapper.java.additional.8=-Xdebug
wrapper.java.additional.9=-Xrunjdwp:transport=dt_socket,address=5005,server=y,suspend=n

Restart gluu-server, start Netbeans (for instance), open pom.xml for OxTrust Server, attach debugger and place some breakpoints in org.gluu.oxtrust.ldap.cache.service.CacheRefreshTimer. Method process() is called every minute trying to do a cache refresh. It might be helpful to check also org.gluu.site.ldap.LDAPConnectionProvider in OxLdap project.

You can also check the /opt/gluu-server/opt/apache-tomcat.../logs/oxtrust_cache_refresh.log for errors if something does not work as expected.

Important remark: almost all configuration is kept also in LDAP so you can browse it through any decent LDAP browser:

  • localhost:1636
  • cn=directory manager
  • <password provided for admin user>
  • Use SSL

Have a look at ou=appliances,o=gluu and its descendants

Customer Backend Key and Attributes

  • Key attribute: sAMAccountName (AD as a backend)
  • Object class: user
  • Source attribute:
    • sAMAccountName (I think it's not required since we already have it as key attribute, have to check once again the code)
    • cn
    • memberOf
    • sn (see Attribute mapping section)

Source Backend LDAP Servers

  • We need to fill-in Bind DN since AD does not allow anonymous binding (and have unchecked the related checkbox)
  • Max connections: do not leave it 0 as proposed initial value; Update will not trigger any warning/error but no connections will be created in the pool (see org.gluu.site.ldap.LDAPConnectionProvider )
  • Server: I had to provide it in <IP>:389 format; it didn't work with <FQDN>:389 (even if ping worked I've got an exception about IPV6 name resolution in above mentioned log; trying to force java VM to IPV4 didn't work either)
  • Base DN: watch out that Gluu does perform single level search so you have to provide the exact parent DN; bad luck if you have spread your users in the directory structure (based on Department for instance); source code shows using an array of baseDNs so it might that it supports defining more than one (lik eblank or semicollon separated, just guessing)
  • Enable: God knows what is used for
  • Don't forget to Change Bind Password

While it looks you can add several source servers there is only one Bind Password and list of fetched attributes so this is not really useful apart from a limited workaround to the restriction imposed by the single level search.

Inum LDAP Sever

I tested it only with one server (the Gluu-server internal OpenDJ) so I have no clue what you can achieve by adding several servers/Base DN (anyway there is again only one Bind DN/Password).

  • Bind DN: cn=directory manager
  • Max Connections: 2 (Do not leave it 0)
  • Base DN: ou=people,o=site

Base DN specifies ou=people,o=site; initially users were created here but due to the fact I missed to provide an Attribute mapping for sn (mandatory requirement) after struggling with various configurations and resets I deleted all entries (for users). Since no new entries appeared I looked in the other areas and I found all users (now correctly imported) in ou=people,o=@!A362.9654.1097.E56D!0001!B929.EB26,o=gluu (to mention that among them we have admin which is consistent with the hint for Keep external persons (see below). So understanding is as follows :

  • Current data (gathered as an union from all defined servers)is compared with last snapshot (if this is not present with the data specified as Base DN); updatdd/deleted data is passed to next processing data
  • The configured Base DN is actually where differential data is pushed in the second step of refresh
  • As long as the differential data is validated against configuration and internal LDAP rules it is stored in ou=people,o=@!A362.9654.1097.E56D!0001!B929.EB26,o=gluu

Refresh Method

I used copy and checked Keep external persons (another very nice hint in the documentation :'If you do not enable 'Keep External Person', your 'admin' user including all other test users will be gone after first Cache Refresh iteration.'; very promising apart from not being provided in the Configuration on-line help but in the overview area...)

Attribute Mapping

This will provide for sure headaches since there is no mention about a tough (but normal) requirement: since default target object classes for the newly created (cached) users are gluuPerson, person,organizationalPerson, inetOrgPerson, ox-A36296541097E56D0001B929EB26, top, eduPerson, some of the through inheritance of course, you have to use only the ones defined for the above mentioned classes and include in the target attributes all required ones. At the moment of writing these are just two: cn and sn. Anyway you will get a self explanatory exception message in the above mentioned log.

Remark:The list of default object classes is defined in JSON configuration, accessible through one of the configuration menu entries.

  • I did the following mappings:
    • sAMAccountName->uid (If I remember correctly from debugging sessions this is done automatically anyway based on Key Attribute definition)
    • sAMAccountName->sn (so we have something in required sn)
    • cn->cn
    • memberOf->memberOf
  • Server IP address: the IP address of the gluu-server (what if we use DHCP? I haven't tried with FQDN)
  • Snapshot folder: /tmp which is relative to /opt/gluu-server (service is launched with chroot)
IyBWZXJzaW9uDQoNCkF0IHRoZSBtb21lbnQgb2Ygd3JpdGluZyB0aGUgY3VycmVudCB2ZXJzaW9uIGZvciBPeFRydXN0IG9uIEdpdEh1YiBpcyAzLjAuMi1TTkFQU0hPVCBidXQgdGhlIGluc3RhbGxlZCB2ZXJzaW9uIHRocm91Z2ggYXB0LWdldCAoSSB1c2VkIFVidW50dSAxNC4wLjQgTFRTKSAgaXMgMi4zLjQuRmluYWwNCg0KIyBJbnN0YWxsYXRpb24NCg0KSW5zdGFsbGF0aW9uIGlzIHN0cmFpZ2h0IGZvcndhcmQgYXBhcnQgZnJvbSBmaXJzdCBzdGVwOiBgZWNobyAiZGViIGh0dHA6Ly9yZXBvLmdsdXUub3JnL3VidW50dS8gdHJ1c3R5IG1haW4iID4gL2V0Yy9hcHQvc291cmNlcy5saXN0LmQvZ2x1dS1yZXBvLmxpc3QgYCBiZWNhdXNlIGBzdWRvIGVjaG9gLi53aWxsIG5vdCB3b3JrOyBhbnl3YXksIEdvb2dsZSBpcyBvdXIgZnJpZW5kIGZvciBvbmUgbW9yZSB0aW1lIChhcyBhbiBhbHRlcm5hdGl2ZSB5b3UgY2FuIGRvIHRoZSBlY2hvIGluIGFub3RoZXIgbm9uLXNlY3VyZWQgbG9jYXRpb24gYW5kIHVzZSBgc3VkbyBjcC4uYCBhZnRlcndhcmRzKS4NCg0KIyBDYWNoZSBSZWZyZXNoIENvbmZpZ3VyYXRpb24NCg0KQ29uZmlndXJhdGlvbiBwcm9jZWR1cmUsIGFzIGRlc2NyaWJlZCBpbiBvbi1saW5lIGRvY3VtZW50YXRpb24gbG9va3Mgc2ltcGxlLiBVbmZvcnR1bmF0ZWx5IGl0IGZhaWxzIHRvIHByb3ZpZGUgYWxsIHRoZSByZXF1aXJlZCBkZXRhaWxzLiBKdXN0IHRvIG1lbnRpb24gc2V2ZXJhbCBpbmNvbnNpc3RlbmNpZXM6IA0KKiBDb25maWd1cmF0aW9uIGlzIGRvbmUgb24gb25lIGZvcm0gKGFzIGRlc2NyaWJlZCBpbiBgQ29uZmlndXJhdGlvbi0+Q2FjaGUgUmVmcmVzaGApIHdoaWxlIHJlYWxseSBlbmFibGluZyB0aGUgZnVuY3Rpb25hbGl0eSBpcyBkb25lIGluYCBDb25maWd1cmF0aW9uLT5Pcmdhbml6YXRpb25gICghKQ0KKiBUaGVyZSBhcmUgc2V2ZXJhbCBtZW50aW9ucyBsaWtlIDonKlVzZSBTU0wqOiBQbGVhc2UgdGljayB0aGUgY2hlY2tib3ggYmVjYXVzZSB0aGUgU1NMIGhhcyB0byBiZSBhY3RpdmF0ZWQuJzogbmljZSBvcHRpb24gc2luY2UgaXQgbG9va3MgeW91IGhhdmUgbm8gb3RoZXIgb3B0aW9uLi4uDQoqIEl0IGlzIHZlcnkgdW5jbGVhciB3aHkgeW91IGhhdmUgdG8gcHJvdmlkZSB0aGUgYEludW0gTERBUCBTZXJ2ZXJgIChkZXNjcmliZWQgYXMgYHRoZSBpbnRlcm5hbCBMREFQIG9mIHRoZSBHbHV1IFNlcnZlciBgOiBpZiBpdCdzIGludGVybmFsIHdoeSBzaG91bGQgeW91IHJlLXNwZWNpZnkgaXQ/KSBhbmQgd2h5IHlvdSBoYXZlIHRvIHByb3ZpZGUgdGhlIFNlcnZlciBJUCBhZGRyZXNzIGluIHRoZSBBdHRyaWJ1dGUgTWFwcGluZyBzZWN0aW9uPyAobWF5YmUgdGhpcyBjb25maWd1cmF0aW9ucyBoYXZlIHRvIGRvIHdpdGggZXh0ZXJuYWwgTERBUCB1c2FnZSBhbmQgc29tZSBjbHVzdGVyaW5nIGNvbmZpZ3VyYXRpb25zLCBqdXN0IGd1ZXNzaW5nKQ0KKiBEZWZpbmluZyBgQ3VzdG9tZXIgQmFja2VuZCBLZXkgYW5kIEF0dHJpYnV0ZXNgIGlzIHNvbWVob3cgcmVkdW5kYW50IChhbmQgbWlnaHQgaW5kdWNlIG5vdCBjb25zaXN0ZW50IGNvbmZpZ3VyYXRpb24pIHdpdGggYEF0dHJpYnV0ZSBNYXBwaW5nYCBzZWN0aW9uOiB3aGF0IGlmIEkgZGVmaW5lIHNvbWUgYXR0cmlidXRlcyBpbiBmaXJzdCBzZWN0aW9uIGFuZCB1c2Ugb3RoZXIgKGFzIHNvdXJjZSkgaW4gdGhlIGxhdGVyPyANCiogSXQgaXMgbm90IGNsZWFyIHdoYXQgdGhlIEVuYWJsZSBjaGVja2JveCBtZWFuaW5nIGlzOiBhZnRlciBVcGRhdGluZyB0aGUgY29uZmlndXJhdGlvbiBhbmQgcGVyZm9ybWluZyBhIGZvcm0gcmVmcmVzaCBpdCB3aWxsIGFueXdheSBiZWNvbWUgdW5jaGVja2VkIGFuZCB0aGUgY29uZmlndXJhdGlvbiBmaWxlIChgL29wdC9nbHV1LXNlcnZlci9vcHQvYXBhY2hlLXRvbWNhdC4uLi9jb25mL294VHJ1c3RDYWNoZVJlZnJlc2gucHJvcGVydGllc2ApIG5laXRoZXIgdGhlIFZlbG9jaXR5IHRlbXBsYXRlIChgb3hUcnVzdC9jb25maWd1cmF0aW9uL3RhcmdldC9jbGFzc2VzL2NvbmYvb3hUcnVzdENhY2hlUmVmcmVzaC5wcm9wZXJ0aWVzLnZtYCkgZG8gbm90IGhhdmUgYW55IHJlZmVyZW5jZSB0byBpdC4NCiogQW5kIGl0J3Mga2VlcCBnb2luZy4uLg0KDQpBbnl3YXksIGhlcmUgYXJlIG15IGZpbmRpbmdzLg0KDQojIyBTb3VyY2UgY29kZQ0KDQpGb3J0dW5hdGVseSB0aGUgc291cmNlIGNvZGUgaXMgYXZhaWxhYmxlIChpbiBNYXZlbiBwcm9qZWN0IGZvcm1hdCkgc28geW91IGNhbiBlbmFibGUgRGVidWcgYW5kIHNlZSB3aGF0J3MgZ29pbmcgb24uDQoNCkVuYWJsZSBkZWJ1Z2dpbmcgYnkgY2hhbmdpbmcgdGhlIGAvb3B0L2dsdXUtc2VydmVyL29wdC9hcGFjaGUtdG9tY2F0Li4uL2NvbmYvZ2x1dVRvbWNhdFdyYXBwZXIuY29uZmAgYnkgYWRkaW5nIHRoZSBmb2xsb3dpbmcgbGluZXMgaW4gYCMgSmF2YSBBZGRpdGlvbmFsIFBhcmFtZXRlcnNgIHNlY3Rpb24NCmBgYA0Kd3JhcHBlci5qYXZhLmFkZGl0aW9uYWwuOD0tWGRlYnVnDQp3cmFwcGVyLmphdmEuYWRkaXRpb25hbC45PS1YcnVuamR3cDp0cmFuc3BvcnQ9ZHRfc29ja2V0LGFkZHJlc3M9NTAwNSxzZXJ2ZXI9eSxzdXNwZW5kPW4NCmBgYA0KUmVzdGFydCBnbHV1LXNlcnZlciwgc3RhcnQgTmV0YmVhbnMgKGZvciBpbnN0YW5jZSksIG9wZW4gcG9tLnhtbCBmb3IgT3hUcnVzdCBTZXJ2ZXIsIGF0dGFjaCBkZWJ1Z2dlciBhbmQgcGxhY2Ugc29tZSBicmVha3BvaW50cyBpbiBgb3JnLmdsdXUub3h0cnVzdC5sZGFwLmNhY2hlLnNlcnZpY2UuQ2FjaGVSZWZyZXNoVGltZXJgLiBNZXRob2QgYHByb2Nlc3MoKWAgaXMgY2FsbGVkIGV2ZXJ5IG1pbnV0ZSB0cnlpbmcgdG8gZG8gYSBjYWNoZSByZWZyZXNoLiBJdCBtaWdodCBiZSBoZWxwZnVsIHRvIGNoZWNrIGFsc28gYG9yZy5nbHV1LnNpdGUubGRhcC5MREFQQ29ubmVjdGlvblByb3ZpZGVyYCBpbiBPeExkYXAgcHJvamVjdC4NCg0KWW91IGNhbiBhbHNvIGNoZWNrIHRoZSBgL29wdC9nbHV1LXNlcnZlci9vcHQvYXBhY2hlLXRvbWNhdC4uLi9sb2dzL294dHJ1c3RfY2FjaGVfcmVmcmVzaC5sb2dgIGZvciBlcnJvcnMgaWYgc29tZXRoaW5nIGRvZXMgbm90IHdvcmsgYXMgZXhwZWN0ZWQuDQoNCioqSW1wb3J0YW50IHJlbWFyayoqOiBhbG1vc3QgYWxsIGNvbmZpZ3VyYXRpb24gaXMga2VwdCBhbHNvIGluIExEQVAgc28geW91IGNhbiBicm93c2UgaXQgdGhyb3VnaCBhbnkgZGVjZW50IExEQVAgYnJvd3NlcjoNCiogbG9jYWxob3N0OjE2MzYNCiogY249ZGlyZWN0b3J5IG1hbmFnZXINCiogXDxwYXNzd29yZCBwcm92aWRlZCBmb3IgYWRtaW4gdXNlclw+DQoqIFVzZSBTU0wNCg0KSGF2ZSBhIGxvb2sgYXQgYG91PWFwcGxpYW5jZXMsbz1nbHV1YCBhbmQgaXRzIGRlc2NlbmRhbnRzDQoNCg0KIyMgQ3VzdG9tZXIgQmFja2VuZCBLZXkgYW5kIEF0dHJpYnV0ZXMNCg0KKiBLZXkgYXR0cmlidXRlOiBzQU1BY2NvdW50TmFtZSAoQUQgYXMgYSBiYWNrZW5kKQ0KKiBPYmplY3QgY2xhc3M6IHVzZXINCiogU291cmNlIGF0dHJpYnV0ZToNCgkqIHNBTUFjY291bnROYW1lIChJIHRoaW5rIGl0J3Mgbm90IHJlcXVpcmVkIHNpbmNlIHdlIGFscmVhZHkgaGF2ZSBpdCBhcyBrZXkgYXR0cmlidXRlLCBoYXZlIHRvIGNoZWNrIG9uY2UgYWdhaW4gdGhlIGNvZGUpDQoJKiBjbg0KCSogbWVtYmVyT2YNCgkqIHNuIChzZWUgQXR0cmlidXRlIG1hcHBpbmcgc2VjdGlvbikNCg0KIyMgU291cmNlIEJhY2tlbmQgTERBUCBTZXJ2ZXJzDQoNCiogV2UgbmVlZCB0byBmaWxsLWluIEJpbmQgRE4gc2luY2UgQUQgZG9lcyBub3QgYWxsb3cgYW5vbnltb3VzIGJpbmRpbmcgKGFuZCBoYXZlIHVuY2hlY2tlZCB0aGUgcmVsYXRlZCBjaGVja2JveCkNCiogTWF4IGNvbm5lY3Rpb25zOiBkbyBub3QgbGVhdmUgaXQgMCBhcyBwcm9wb3NlZCBpbml0aWFsIHZhbHVlOyBVcGRhdGUgd2lsbCBub3QgdHJpZ2dlciBhbnkgd2FybmluZy9lcnJvciBidXQgbm8gY29ubmVjdGlvbnMgd2lsbCBiZSBjcmVhdGVkIGluIHRoZSBwb29sIChzZWUgYG9yZy5nbHV1LnNpdGUubGRhcC5MREFQQ29ubmVjdGlvblByb3ZpZGVyYCApDQoqIFNlcnZlcjogSSBoYWQgdG8gcHJvdmlkZSBpdCBpbiBcPElQXD46Mzg5IGZvcm1hdDsgaXQgZGlkbid0IHdvcmsgd2l0aCBcPEZRRE5cPjozODkgKGV2ZW4gaWYgcGluZyB3b3JrZWQgSSd2ZSBnb3QgYW4gZXhjZXB0aW9uIGFib3V0IElQVjYgbmFtZSByZXNvbHV0aW9uIGluIGFib3ZlIG1lbnRpb25lZCBsb2c7IHRyeWluZyB0byBmb3JjZSBqYXZhIFZNIHRvIElQVjQgZGlkbid0IHdvcmsgZWl0aGVyKQ0KKiBCYXNlIEROOiB3YXRjaCBvdXQgdGhhdCBHbHV1IGRvZXMgcGVyZm9ybSAgc2luZ2xlIGxldmVsIHNlYXJjaCBzbyB5b3UgaGF2ZSB0byBwcm92aWRlIHRoZSBleGFjdCBwYXJlbnQgRE47IGJhZCBsdWNrIGlmIHlvdSBoYXZlIHNwcmVhZCB5b3VyIHVzZXJzIGluIHRoZSBkaXJlY3Rvcnkgc3RydWN0dXJlIChiYXNlZCBvbiBEZXBhcnRtZW50IGZvciBpbnN0YW5jZSk7IHNvdXJjZSBjb2RlIHNob3dzIHVzaW5nIGFuIGFycmF5IG9mIGJhc2VETnMgc28gaXQgbWlnaHQgdGhhdCBpdCBzdXBwb3J0cyBkZWZpbmluZyBtb3JlIHRoYW4gb25lIChsaWsgZWJsYW5rIG9yIHNlbWljb2xsb24gc2VwYXJhdGVkLCBqdXN0IGd1ZXNzaW5nKQ0KKiBFbmFibGU6ICoqR29kKioga25vd3Mgd2hhdCBpcyB1c2VkIGZvcg0KKiBEb24ndCBmb3JnZXQgdG8gYENoYW5nZSBCaW5kIFBhc3N3b3JkYA0KDQpXaGlsZSBpdCBsb29rcyB5b3UgY2FuIGFkZCBzZXZlcmFsIHNvdXJjZSBzZXJ2ZXJzIHRoZXJlIGlzIG9ubHkgb25lIEJpbmQgUGFzc3dvcmQgYW5kIGxpc3Qgb2YgZmV0Y2hlZCBhdHRyaWJ1dGVzIHNvIHRoaXMgaXMgbm90IHJlYWxseSB1c2VmdWwgYXBhcnQgZnJvbSBhIGxpbWl0ZWQgd29ya2Fyb3VuZCB0byB0aGUgcmVzdHJpY3Rpb24gaW1wb3NlZCBieSB0aGUgc2luZ2xlIGxldmVsIHNlYXJjaC4NCg0KIyMgSW51bSBMREFQIFNldmVyDQoNCkkgdGVzdGVkIGl0IG9ubHkgd2l0aCBvbmUgc2VydmVyICh0aGUgR2x1dS1zZXJ2ZXIgaW50ZXJuYWwgT3BlbkRKKSBzbyBJIGhhdmUgbm8gY2x1ZSB3aGF0IHlvdSBjYW4gYWNoaWV2ZSBieSBhZGRpbmcgc2V2ZXJhbCBzZXJ2ZXJzL0Jhc2UgRE4gKGFueXdheSB0aGVyZSBpcyBhZ2FpbiBvbmx5IG9uZSBCaW5kIEROL1Bhc3N3b3JkKS4NCg0KKiBCaW5kIEROOiBjbj1kaXJlY3RvcnkgbWFuYWdlcg0KKiBNYXggQ29ubmVjdGlvbnM6IDIgKCoqRG8gbm90IGxlYXZlIGl0IDAqKikNCiogQmFzZSBETjogb3U9cGVvcGxlLG89c2l0ZQ0KDQpCYXNlIEROIHNwZWNpZmllcyBgb3U9cGVvcGxlLG89c2l0ZWA7IGluaXRpYWxseSB1c2VycyB3ZXJlIGNyZWF0ZWQgaGVyZSBidXQgZHVlIHRvIHRoZSBmYWN0IEkgbWlzc2VkIHRvIHByb3ZpZGUgYW4gQXR0cmlidXRlIG1hcHBpbmcgZm9yIHNuIChtYW5kYXRvcnkgcmVxdWlyZW1lbnQpIGFmdGVyIHN0cnVnZ2xpbmcgd2l0aCB2YXJpb3VzIGNvbmZpZ3VyYXRpb25zIGFuZCByZXNldHMgSSBkZWxldGVkIGFsbCBlbnRyaWVzIChmb3IgdXNlcnMpLiBTaW5jZSBubyBuZXcgZW50cmllcyBhcHBlYXJlZCBJIGxvb2tlZCBpbiB0aGUgb3RoZXIgYXJlYXMgYW5kIEkgZm91bmQgYWxsIHVzZXJzIChub3cgY29ycmVjdGx5IGltcG9ydGVkKSBpbiBgb3U9cGVvcGxlLG89QCFBMzYyLjk2NTQuMTA5Ny5FNTZEITAwMDEhQjkyOS5FQjI2LG89Z2x1dWAgKHRvIG1lbnRpb24gdGhhdCBhbW9uZyB0aGVtIHdlIGhhdmUgYWRtaW4gd2hpY2ggaXMgY29uc2lzdGVudCB3aXRoIHRoZSBoaW50IGZvciBgS2VlcCBleHRlcm5hbCBwZXJzb25zYCAoc2VlIGJlbG93KS4gU28gdW5kZXJzdGFuZGluZyBpcyBhcyBmb2xsb3dzIDoNCiANCiogQ3VycmVudCBkYXRhIChnYXRoZXJlZCBhcyBhbiB1bmlvbiBmcm9tIGFsbCBkZWZpbmVkIHNlcnZlcnMpaXMgY29tcGFyZWQgd2l0aCBsYXN0IHNuYXBzaG90IChpZiB0aGlzIGlzIG5vdCBwcmVzZW50IHdpdGggdGhlIGRhdGEgc3BlY2lmaWVkIGFzIEJhc2UgRE4pOyB1cGRhdGRkL2RlbGV0ZWQgZGF0YSBpcyBwYXNzZWQgdG8gbmV4dCBwcm9jZXNzaW5nIGRhdGENCiogVGhlIGNvbmZpZ3VyZWQgQmFzZSBETiBpcyBhY3R1YWxseSB3aGVyZSBkaWZmZXJlbnRpYWwgZGF0YSBpcyBwdXNoZWQgaW4gdGhlIHNlY29uZCBzdGVwIG9mIHJlZnJlc2gNCiogQXMgbG9uZyBhcyB0aGUgZGlmZmVyZW50aWFsIGRhdGEgaXMgdmFsaWRhdGVkIGFnYWluc3QgY29uZmlndXJhdGlvbiBhbmQgaW50ZXJuYWwgTERBUCBydWxlcyBpdCBpcyBzdG9yZWQgaW4gYG91PXBlb3BsZSxvPUAhQTM2Mi45NjU0LjEwOTcuRTU2RCEwMDAxIUI5MjkuRUIyNixvPWdsdXVgDQoNCiMjIFJlZnJlc2ggTWV0aG9kDQoNCkkgdXNlZCBjb3B5IGFuZCBjaGVja2VkIGBLZWVwIGV4dGVybmFsIHBlcnNvbnNgIChhbm90aGVyIHZlcnkgbmljZSBoaW50IGluIHRoZSBkb2N1bWVudGF0aW9uIDonKklmIHlvdSBkbyBub3QgZW5hYmxlICdLZWVwIEV4dGVybmFsIFBlcnNvbicsIHlvdXIgJ2FkbWluJyB1c2VyIGluY2x1ZGluZyBhbGwgb3RoZXIgdGVzdCB1c2VycyB3aWxsIGJlIGdvbmUgYWZ0ZXIgZmlyc3QgQ2FjaGUgUmVmcmVzaCBpdGVyYXRpb24uKic7IHZlcnkgcHJvbWlzaW5nIGFwYXJ0IGZyb20gbm90IGJlaW5nIHByb3ZpZGVkIGluIHRoZSBDb25maWd1cmF0aW9uIG9uLWxpbmUgaGVscCBidXQgaW4gdGhlIG92ZXJ2aWV3IGFyZWEuLi4pDQoNCiMjIEF0dHJpYnV0ZSBNYXBwaW5nDQoNClRoaXMgd2lsbCBwcm92aWRlIGZvciBzdXJlIGhlYWRhY2hlcyBzaW5jZSB0aGVyZSBpcyBubyBtZW50aW9uIGFib3V0IGEgdG91Z2ggKGJ1dCBub3JtYWwpIHJlcXVpcmVtZW50OiBzaW5jZSBkZWZhdWx0IHRhcmdldCBvYmplY3QgY2xhc3NlcyBmb3IgdGhlIG5ld2x5IGNyZWF0ZWQgKGNhY2hlZCkgdXNlcnMgYXJlICBnbHV1UGVyc29uLCBwZXJzb24sb3JnYW5pemF0aW9uYWxQZXJzb24sIGluZXRPcmdQZXJzb24sIG94LUEzNjI5NjU0MTA5N0U1NkQwMDAxQjkyOUVCMjYsIHRvcCwgZWR1UGVyc29uLCBzb21lIG9mIHRoZSB0aHJvdWdoIGluaGVyaXRhbmNlIG9mIGNvdXJzZSwgeW91IGhhdmUgdG8gdXNlIG9ubHkgdGhlIG9uZXMgZGVmaW5lZCBmb3IgdGhlIGFib3ZlIG1lbnRpb25lZCBjbGFzc2VzIGFuZCBpbmNsdWRlIGluIHRoZSB0YXJnZXQgYXR0cmlidXRlcyBhbGwgcmVxdWlyZWQgb25lcy4gQXQgdGhlIG1vbWVudCBvZiB3cml0aW5nIHRoZXNlIGFyZSBqdXN0IHR3bzogY24gYW5kIHNuLiBBbnl3YXkgeW91IHdpbGwgZ2V0IGEgc2VsZiBleHBsYW5hdG9yeSBleGNlcHRpb24gbWVzc2FnZSBpbiB0aGUgYWJvdmUgbWVudGlvbmVkIGxvZy4NCg0KKipSZW1hcms6KipUaGUgbGlzdCBvZiBkZWZhdWx0IG9iamVjdCBjbGFzc2VzIGlzIGRlZmluZWQgaW4gSlNPTiBjb25maWd1cmF0aW9uLCBhY2Nlc3NpYmxlIHRocm91Z2ggb25lIG9mIHRoZSBjb25maWd1cmF0aW9uIG1lbnUgZW50cmllcy4NCg0KKiBJIGRpZCB0aGUgZm9sbG93aW5nIG1hcHBpbmdzOg0KCSogc0FNQWNjb3VudE5hbWUtPnVpZCAoSWYgSSByZW1lbWJlciBjb3JyZWN0bHkgZnJvbSBkZWJ1Z2dpbmcgc2Vzc2lvbnMgdGhpcyBpcyBkb25lIGF1dG9tYXRpY2FsbHkgYW55d2F5IGJhc2VkIG9uIEtleSBBdHRyaWJ1dGUgZGVmaW5pdGlvbikNCgkqIHNBTUFjY291bnROYW1lLT5zbiAoc28gd2UgaGF2ZSBzb21ldGhpbmcgaW4gcmVxdWlyZWQgc24pDQoJKiBjbi0+Y24NCgkqIG1lbWJlck9mLT5tZW1iZXJPZg0KKiBTZXJ2ZXIgSVAgYWRkcmVzczogdGhlIElQIGFkZHJlc3Mgb2YgdGhlIGdsdXUtc2VydmVyICh3aGF0IGlmIHdlIHVzZSBESENQPyBJIGhhdmVuJ3QgdHJpZWQgd2l0aCBGUUROKQ0KKiBTbmFwc2hvdCBmb2xkZXI6IC90bXAgd2hpY2ggaXMgcmVsYXRpdmUgdG8gL29wdC9nbHV1LXNlcnZlciAoc2VydmljZSBpcyBsYXVuY2hlZCB3aXRoIGNocm9vdCkNCg0KDQo=

Sunday, October 18, 2015

OpenAM (strikes back): Install/Configure Jetty for OpenIG-J2EE Agent

I have used Jetty as OpenIG container (version jetty-distribution-8.1.17.v20150415).

Install Jetty as a service

Download the Jetty distribution and unpack it in the traget directory. Follow instructions in http://www.eclipse.org/jetty/documentation/current/startup-windows-service.html

Several remarks:

  • In the install.bat batch change the set PR_JVMOPTIONS= line to set PR_JVMOPTIONS=-Duser.dir="%JETTY_BASE%";-Djetty.port=8081;-Djava.io.tmpdir="C:\jetty\temp";-Djetty.home="%JETTY_HOME%";-Djetty.base="%JETTY_BASE%";-Dopenig.base="C:\jetty\OpenIG";-Xdebug;-Xnoagent;-Djava.compiler=NONE;-Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=5005; so that the used port will be 8081, the debug support will be enabled and the OpenIG base directory will be set up
  • Removing the service can be done with the command prunsrv.exe //DS/JettyService4OpenIG (JettyService4OpenIG is the chosen name for the service);
  • In order to be able to stop the service I had to remove comment the following lines in the install.bat:
    • REM set PR_STOPPARAMS=--stop;STOP.KEY="%STOPKEY%";STOP.PORT=%STOPPORT%;STOP.WAIT=10
    • REM --StartParams="%PR_STARTPARAMS%" ^

Deploy OpenIG

Rename the war to root.war and deploy it to webapps; remove test.xml from contexts (so that the test application is not deployed (with error))

Deploy J2EE Agent

Follow the instructions in https://backstage.forgerock.com/#!/docs/openam-policy-agents/3.5.0/jee-users-guide#chap-jetty in parallel with https://backstage.forgerock.com/#!/docs/openig/3.1.0/gateway-guide#chap-password-capture-replay-tutorial to deploy the agent on the same Jetty instance

Configure the Agent

  • Go to the newly defined realm (let's say it's named MyRealm) and select the Agents tab
  • Select the J2EE tab
  • Add a new Agent; be sure to use the port configured in Jetty (8081 in my case) in defining URLs:

Notices:

  • Even if the agent is defined as part for MyRealm configuration and the property com.sun.identity.agents.config.organization.name = /MyRealm in agents bootstrap properties (OpenSSOAgentBootstrap.properties) is set up properly the generated login URL (at runtime) will not contain the targeted realm so user authentication will take place against Top Level Realm.
  • Password relay will not work with XUI interface due to a bug solved in subsequent 12.0.0 releases (available only to subscribers) so the solution is to switch to classic UI (https://bugster.forgerock.org/jira/browse/OPENAM-5921). See https://backstage.forgerock.com/#!/docs/openam/12.0.0/install-guide/chap-custom-ui on how to disable XUI.
  • Changing /repaly to /replay/* will allow us to integrate more than one legacy applications in the system by using different specific routes in OpenIG; the ideea is that any GET to <OpenIG-URL>/relay/MyApp will trigger the same replay process by redirecting the user to OpenAM login dialog; the redirect back to OpenIG will be at the same URL which now will be forwarded to OpenIG which in turn based on this URL different routes can come into action (see OpenIG specifics below).

Configure OpenIG

There is not obvious at the first glance but we must define the config.js in config subdirectory of the configuread OpenIG base directory (parameter -Dopenig.base, see above) and the specific routes in config\routes.

config.js

{
    "handler": {
        "type": "Router",
        "audit": "global",
        "capture": "all"
    },
    "heap": [
        {
            "name": "LogSink",
            "type": "ConsoleLogSink",
            "config": {
                "level": "DEBUG"
            }
        },
        {
            "name": "JwtSession",
            "type": "JwtSession"
        },
        {
            "name": "ClientHandler",
            "type": "ClientHandler"
        },
        {
            "name": "capture",
            "type": "CaptureDecorator",
            "config": {
                "captureEntity": true,
                "_captureExchange": true
            }
        }
    ]
}

Routes for OTRS

Login route

{
    "handler": {
        "type": "Chain",
        "config": {
            "filters": [
                {
                    "type": "CryptoHeaderFilter",
                    "config": {
                        "messageType": "REQUEST",
                        "operation": "DECRYPT",
                        "algorithm": "DES/ECB/NoPadding",
                        "key": "xxx",
                        "keyType": "DES",
                        "charSet": "utf-8",
                        "headers": [
                            "password"
                        ]
                    }
                },
                {
                    "type": "AssignmentFilter",
                    "config": {
                        "onRequest": [
                            {
                                "target": "${exchange.authInfoUsername}",
                                "value": "${exchange.request.headers['username'][0]}"
                            },
                            {
                                "target": "${exchange.authInfoPassword}",
                                "value": "${exchange.request.headers['password'][0]}"
                            }
                        ]
                    }
                },
                {
                    "type": "HeaderFilter",
                    "config": {
                        "messageType": "REQUEST",
                        "remove": [
                            "password",
                            "username"
                        ]
                    }
                },
                {
                    "type": "StaticRequestFilter",
                    "config": {
                        "method": "POST",
                        "uri": "https://otrs-fqdn/otrs/customer.pl",
                        "form": {
                            "User": [
                                "${exchange.authInfoUsername}"
                            ],
                            "Password": [
                                "${exchange.authInfoPassword}"
                            ],
                            "Action":["Login"],
                            "RequestedURL":[""],
                            "Lang":["en"],
                            "TimeOffset":["-180"]
                        }
                    }
                }
            ],
            "handler": "ClientHandler"
        }
    },
    "condition": "${matches(exchange.request.uri.path, '^/replay/otrs')}"
}

Default route

{
    "handler": "ClientHandler",
    "condition": "${matches(exchange.request.uri.path, '^/otrs')}",
    "baseURI": "https://otrs-fqdn"

}

Routes for othe application (two steps login)

This application is JSF based so the POST at login view must have the session cookie set. The solution is to have a first step requesting GET before so that the session is created.

Login route

{
    "heap": [
        {
            "name": "DispatchHandler",
            "type": "DispatchHandler",
            "config": {
                "bindings": [
                    {
                        "handler": {
                            "type": "Chain",
                            "config": {
                                "filters": [
                                    {
                                        "type": "CryptoHeaderFilter",
                                        "config": {
                                            "messageType": "REQUEST",
                                            "operation": "DECRYPT",
                                            "algorithm": "DES/ECB/NoPadding",
                                            "key": "xxx",
                                            "keyType": "DES",
                                            "charSet": "utf-8",
                                            "headers": [
                                                "password"
                                            ]
                                        }
                                    },
                                    {
                                        "type": "AssignmentFilter",
                                        "config": {
                                            "onRequest": [
                                                {
                                                    "target": "${exchange.authInfoUsername}",
                                                    "value": "${exchange.request.headers['username'][0]}"
                                                },
                                                {
                                                    "target": "${exchange.authInfoPassword}",
                                                    "value": "${exchange.request.headers['password'][0]}"
                                                }
                                            ]
                                        }
                                    },
                                    {
                                        "type": "HeaderFilter",
                                        "config": {
                                            "messageType": "REQUEST",
                                            "remove": [
                                                "password",
                                                "username"
                                            ]
                                        }
                                    },
                                    
                                    {
                                        "type": "StaticRequestFilter",
                                        "config": {
                                            "method": "GET",
                                            "uri": "http://legacy-app-fqdn:8081/MyApp/faces/login.xhtml"
                                        }
                                    },
                                    {
                                        "type": "SwitchFilter",
                                        "config": {
                                            "onResponse": [
                                                {
                                                    "handler": "LoginRequestHandler"
                                                }
                                            ]
                                        }
                                    },
                                    {
                                        "type": "EntityExtractFilter",
                                        "config": {
                                            "messageType": "response",
                                            "target": "${exchange.viewState}",
                                            "bindings": [
                                                {
                                                    "key": "value",
                                                    "pattern":
                                                        "javax\\.faces\\.ViewState\"\\s.*value=\"(.*)\"\\s*autocomplete=",
                                                    "template": "$1"
                                                }
                                            ]
                                        }
                                    },
                                    {
                                        "type": "AssignmentFilter",
                                        "config": {
                                            "onResponse": [
                                                {
                                                    "target": "${exchange.sessionCookie}",
                                                    "value": "${split(exchange.response.headers['Set-Cookie'][0],';')[0]}"
                                                }
                                            ]
                                        }
                                    }

                                ],
                                "handler": "ClientHandler"
                            }
                        }
    
                    }
            
                ]
            }
        },
        {
            "name": "LoginRequestHandler",
            "type": "Chain",
            "config": {
                "filters": [
                
                {
                    "type": "StaticRequestFilter",
                    "config": {
                        "method": "POST",
                        "uri": "http://legacy-app-fqdn:8081/MyApp/faces/login.xhtml",
                        "form": {
                            "loginForm:j_username": [
                                "${exchange.authInfoUsername}"
                            ],
                            "loginForm:j_password": [
                                "${exchange.authInfoPassword}"
                            ],
                            "loginForm":["loginForm"],
                            "loginForm:j_idt17":[""],
                            "javax.faces.ViewState":["${exchange.viewState.value}"]
                        },
                        "headers": {
                            "Cookie": ["${exchange.sessionCookie}"]
                        }
                    }
                },
                {
                     "type": "HeaderFilter",
                     "config": {
                         "messageType": "RESPONSE",
                         "add": {
                             "Set-Cookie": [ "${exchange.sessionCookie}; path=/MyApp" ]
                         }
                     }
                }
                ],
                "handler": "ClientHandler"
            }
        }
    ],
    "handler": "DispatchHandler",
    "condition": "${matches(exchange.request.uri.path, '^/replay/MyApp')}"
}

Default route

{
    "handler": "ClientHandler",
    "condition": "${matches(exchange.request.uri.path, '^/MyApp')}",
    "baseURI": "http://legacy-app-fqdn:8081"

}

Several remarks

As I decided to abandon this solution there were things unconfigured or missconfigured. Just to mention two:

  • Logout process (you might end up with the simulation of SSO screwed up if the users logout of one the application trying to re-log-in as a different user)
  • Sniffing the network during tests I noticed announcement (POSTs if I remember corectly) from OpenAM to J2EE agent, messages not intercepted by the agent and forwarded (or dropped, depending on default router configuration) by OpenIG
SSBoYXZlIHVzZWQgSmV0dHkgYXMgT3BlbklHIGNvbnRhaW5lciAodmVyc2lvbiBqZXR0eS1kaXN0cmlidXRpb24tOC4xLjE3LnYyMDE1MDQxNSkuDQoNCiMgSW5zdGFsbCBKZXR0eSBhcyBhIHNlcnZpY2UNCg0KRG93bmxvYWQgdGhlIEpldHR5IGRpc3RyaWJ1dGlvbiBhbmQgdW5wYWNrIGl0IGluIHRoZSB0cmFnZXQgZGlyZWN0b3J5Lg0KRm9sbG93IGluc3RydWN0aW9ucyBpbiBbaHR0cDovL3d3dy5lY2xpcHNlLm9yZy9qZXR0eS9kb2N1bWVudGF0aW9uL2N1cnJlbnQvc3RhcnR1cC13aW5kb3dzLXNlcnZpY2UuaHRtbF0oaHR0cDovL3d3dy5lY2xpcHNlLm9yZy9qZXR0eS9kb2N1bWVudGF0aW9uL2N1cnJlbnQvc3RhcnR1cC13aW5kb3dzLXNlcnZpY2UuaHRtbCkNCg0KU2V2ZXJhbCByZW1hcmtzOg0KKiBJbiB0aGUgaW5zdGFsbC5iYXQgYmF0Y2ggY2hhbmdlIHRoZSBgc2V0IFBSX0pWTU9QVElPTlM9YCBsaW5lIHRvIGBzZXQgUFJfSlZNT1BUSU9OUz0tRHVzZXIuZGlyPSIlSkVUVFlfQkFTRSUiOy1EamV0dHkucG9ydD04MDgxOy1EamF2YS5pby50bXBkaXI9IkM6XGpldHR5XHRlbXAiOy1EamV0dHkuaG9tZT0iJUpFVFRZX0hPTUUlIjstRGpldHR5LmJhc2U9IiVKRVRUWV9CQVNFJSI7LURvcGVuaWcuYmFzZT0iQzpcamV0dHlcT3BlbklHIjstWGRlYnVnOy1Ybm9hZ2VudDstRGphdmEuY29tcGlsZXI9Tk9ORTstWHJ1bmpkd3A6dHJhbnNwb3J0PWR0X3NvY2tldCxzZXJ2ZXI9eSxzdXNwZW5kPW4sYWRkcmVzcz01MDA1O2Agc28gdGhhdCB0aGUgdXNlZCBwb3J0IHdpbGwgYmUgODA4MSwgdGhlIGRlYnVnIHN1cHBvcnQgd2lsbCBiZSBlbmFibGVkIGFuZCB0aGUgT3BlbklHIGJhc2UgZGlyZWN0b3J5IHdpbGwgYmUgc2V0IHVwDQoqIFJlbW92aW5nIHRoZSBzZXJ2aWNlIGNhbiBiZSBkb25lIHdpdGggdGhlIGNvbW1hbmQgYHBydW5zcnYuZXhlIC8vRFMvSmV0dHlTZXJ2aWNlNE9wZW5JR2AgKEpldHR5U2VydmljZTRPcGVuSUcgaXMgdGhlIGNob3NlbiBuYW1lIGZvciB0aGUgc2VydmljZSk7DQoqIEluIG9yZGVyIHRvIGJlIGFibGUgdG8gc3RvcCB0aGUgc2VydmljZSBJIGhhZCB0byByZW1vdmUgY29tbWVudCB0aGUgZm9sbG93aW5nIGxpbmVzIGluIHRoZSBpbnN0YWxsLmJhdDoNCgkqIGBSRU0gc2V0IFBSX1NUT1BQQVJBTVM9LS1zdG9wO1NUT1AuS0VZPSIlU1RPUEtFWSUiO1NUT1AuUE9SVD0lU1RPUFBPUlQlO1NUT1AuV0FJVD0xMGANCgkqIGBSRU0gIC0tU3RhcnRQYXJhbXM9IiVQUl9TVEFSVFBBUkFNUyUiIF5gDQoNCiMgRGVwbG95IE9wZW5JRw0KDQpSZW5hbWUgdGhlIHdhciB0byBgcm9vdC53YXJgIGFuZCBkZXBsb3kgaXQgdG8gd2ViYXBwczsgcmVtb3ZlIHRlc3QueG1sIGZyb20gY29udGV4dHMgKHNvIHRoYXQgdGhlIHRlc3QgYXBwbGljYXRpb24gaXMgbm90IGRlcGxveWVkICh3aXRoIGVycm9yKSkNCg0KIyBEZXBsb3kgSjJFRSBBZ2VudA0KDQpGb2xsb3cgdGhlIGluc3RydWN0aW9ucyBpbiBbaHR0cHM6Ly9iYWNrc3RhZ2UuZm9yZ2Vyb2NrLmNvbS8jIS9kb2NzL29wZW5hbS1wb2xpY3ktYWdlbnRzLzMuNS4wL2plZS11c2Vycy1ndWlkZSNjaGFwLWpldHR5XShodHRwczovL2JhY2tzdGFnZS5mb3JnZXJvY2suY29tLyMhL2RvY3Mvb3BlbmFtLXBvbGljeS1hZ2VudHMvMy41LjAvamVlLXVzZXJzLWd1aWRlI2NoYXAtamV0dHkpIGluIHBhcmFsbGVsIHdpdGggW2h0dHBzOi8vYmFja3N0YWdlLmZvcmdlcm9jay5jb20vIyEvZG9jcy9vcGVuaWcvMy4xLjAvZ2F0ZXdheS1ndWlkZSNjaGFwLXBhc3N3b3JkLWNhcHR1cmUtcmVwbGF5LXR1dG9yaWFsXShodHRwczovL2JhY2tzdGFnZS5mb3JnZXJvY2suY29tLyMhL2RvY3Mvb3BlbmlnLzMuMS4wL2dhdGV3YXktZ3VpZGUjY2hhcC1wYXNzd29yZC1jYXB0dXJlLXJlcGxheS10dXRvcmlhbCkgdG8gZGVwbG95IHRoZSBhZ2VudCBvbiB0aGUgc2FtZSBKZXR0eSBpbnN0YW5jZQ0KDQojIENvbmZpZ3VyZSB0aGUgQWdlbnQNCg0KKiBHbyB0byB0aGUgbmV3bHkgZGVmaW5lZCByZWFsbSAobGV0J3Mgc2F5IGl0J3MgbmFtZWQgTXlSZWFsbSkgYW5kIHNlbGVjdCB0aGUgQWdlbnRzIHRhYg0KKiBTZWxlY3QgdGhlIEoyRUUgdGFiDQoqIEFkZCBhIG5ldyBBZ2VudDsgYmUgc3VyZSB0byB1c2UgdGhlIHBvcnQgY29uZmlndXJlZCBpbiBKZXR0eSAoODA4MSBpbiBteSBjYXNlKSBpbiBkZWZpbmluZyBVUkxzOg0KCSogU2V0IGBBZ2VudCBGaWx0ZXIgTW9kZWAgdG8gYFNTT19PTkxZYA0KCSogU2V0IHRoZSBgT3BlbkFNIExvZ2luIFVSTGAgdG8gY29udGFpbiB0aGUgYHJlYWxtPU15UmVhbG1gIHF1ZXJ5IHBhcmFtZXRlcioNCgkqIENoYW5nZSB0aGUgYHdlYmRlZmF1bHQueG1sYCBpbiBldGMgc3ViZGlyZWN0b3J5IG9mIEpldHR5IGluc3RhbGxhdGlvbiBhcyBtZW50aW9uZWQgaW4gSjJFRSBhZ2VudCBjb25maWd1cmF0aW9uIChbaHR0cHM6Ly9iYWNrc3RhZ2UuZm9yZ2Vyb2NrLmNvbS8jIS9kb2NzL29wZW5pZy8zLjEuMC9nYXRld2F5LWd1aWRlI2NhcHR1cmUtcmVsYXktc2V0dXAtcGFdKGh0dHBzOi8vYmFja3N0YWdlLmZvcmdlcm9jay5jb20vIyEvZG9jcy9vcGVuaWcvMy4xLjAvZ2F0ZXdheS1ndWlkZSNjYXB0dXJlLXJlbGF5LXNldHVwLXBhKSkgYnV0IHVzZSBgPHVybC1wYXR0ZXJuPi9yZXBsYXkvKjwvdXJsLXBhdHRlcm4+YCBpbnN0ZWFkIG9mIGA8dXJsLXBhdHRlcm4+L3JlcGxheS88L3VybC1wYXR0ZXJuPmAgKHNlZSBiZWxvdy4uLikuDQoNCioqTm90aWNlczoqKiANCiogRXZlbiBpZiB0aGUgYWdlbnQgaXMgZGVmaW5lZCBhcyBwYXJ0IGZvciBNeVJlYWxtIGNvbmZpZ3VyYXRpb24gYW5kICB0aGUgcHJvcGVydHkgYGNvbS5zdW4uaWRlbnRpdHkuYWdlbnRzLmNvbmZpZy5vcmdhbml6YXRpb24ubmFtZSA9IC9NeVJlYWxtYCBpbiBhZ2VudHMgYm9vdHN0cmFwIHByb3BlcnRpZXMgKE9wZW5TU09BZ2VudEJvb3RzdHJhcC5wcm9wZXJ0aWVzKSBpcyBzZXQgdXAgcHJvcGVybHkgdGhlIGdlbmVyYXRlZCBsb2dpbiBVUkwgKGF0IHJ1bnRpbWUpIHdpbGwgbm90IGNvbnRhaW4gdGhlIHRhcmdldGVkIHJlYWxtIHNvIHVzZXIgYXV0aGVudGljYXRpb24gd2lsbCB0YWtlIHBsYWNlIGFnYWluc3QgVG9wIExldmVsIFJlYWxtLg0KKiBQYXNzd29yZCByZWxheSB3aWxsIG5vdCB3b3JrIHdpdGggWFVJIGludGVyZmFjZSBkdWUgdG8gYSBidWcgc29sdmVkIGluIHN1YnNlcXVlbnQgMTIuMC4wIHJlbGVhc2VzIChhdmFpbGFibGUgb25seSB0byBzdWJzY3JpYmVycykgc28gdGhlIHNvbHV0aW9uIGlzIHRvIHN3aXRjaCB0byBjbGFzc2ljIFVJIChbaHR0cHM6Ly9idWdzdGVyLmZvcmdlcm9jay5vcmcvamlyYS9icm93c2UvT1BFTkFNLTU5MjFdKGh0dHBzOi8vYnVnc3Rlci5mb3JnZXJvY2sub3JnL2ppcmEvYnJvd3NlL09QRU5BTS01OTIxKSkuIFNlZSBbaHR0cHM6Ly9iYWNrc3RhZ2UuZm9yZ2Vyb2NrLmNvbS8jIS9kb2NzL29wZW5hbS8xMi4wLjAvaW5zdGFsbC1ndWlkZS9jaGFwLWN1c3RvbS11aV0oaHR0cHM6Ly9iYWNrc3RhZ2UuZm9yZ2Vyb2NrLmNvbS8jIS9kb2NzL29wZW5hbS8xMi4wLjAvaW5zdGFsbC1ndWlkZS9jaGFwLWN1c3RvbS11aSkgb24gaG93IHRvIGRpc2FibGUgWFVJLg0KKiBDaGFuZ2luZyAvcmVwYWx5IHRvIC9yZXBsYXkvKiB3aWxsIGFsbG93IHVzIHRvIGludGVncmF0ZSBtb3JlIHRoYW4gb25lIGxlZ2FjeSBhcHBsaWNhdGlvbnMgaW4gdGhlIHN5c3RlbSBieSB1c2luZyBkaWZmZXJlbnQgc3BlY2lmaWMgcm91dGVzIGluIE9wZW5JRzsgdGhlIGlkZWVhIGlzIHRoYXQgYW55IEdFVCB0byA8T3BlbklHLVVSTD4vcmVsYXkvTXlBcHAgd2lsbCB0cmlnZ2VyIHRoZSBzYW1lIHJlcGxheSBwcm9jZXNzIGJ5IHJlZGlyZWN0aW5nIHRoZSB1c2VyIHRvIE9wZW5BTSBsb2dpbiBkaWFsb2c7IHRoZSByZWRpcmVjdCBiYWNrIHRvIE9wZW5JRyB3aWxsIGJlIGF0IHRoZSBzYW1lIFVSTCB3aGljaCBub3cgd2lsbCBiZSBmb3J3YXJkZWQgdG8gT3BlbklHIHdoaWNoIGluIHR1cm4gYmFzZWQgb24gdGhpcyBVUkwgZGlmZmVyZW50IHJvdXRlcyBjYW4gY29tZSBpbnRvIGFjdGlvbiAoc2VlIE9wZW5JRyBzcGVjaWZpY3MgYmVsb3cpLg0KDQojIENvbmZpZ3VyZSBPcGVuSUcNCg0KVGhlcmUgaXMgbm90IG9idmlvdXMgYXQgdGhlIGZpcnN0IGdsYW5jZSBidXQgd2UgbXVzdCBkZWZpbmUgdGhlIGNvbmZpZy5qcyBpbiBjb25maWcgc3ViZGlyZWN0b3J5IG9mIHRoZSBjb25maWd1cmVhZCBPcGVuSUcgYmFzZSBkaXJlY3RvcnkgKHBhcmFtZXRlciAtRG9wZW5pZy5iYXNlLCBzZWUgYWJvdmUpIGFuZCB0aGUgc3BlY2lmaWMgcm91dGVzIGluIGNvbmZpZ1xyb3V0ZXMuDQoNCg0KIyMgY29uZmlnLmpzDQoNCmBgYEpTT04NCnsNCiAgICAiaGFuZGxlciI6IHsNCiAgICAgICAgInR5cGUiOiAiUm91dGVyIiwNCiAgICAgICAgImF1ZGl0IjogImdsb2JhbCIsDQogICAgICAgICJjYXB0dXJlIjogImFsbCINCiAgICB9LA0KICAgICJoZWFwIjogWw0KICAgICAgICB7DQogICAgICAgICAgICAibmFtZSI6ICJMb2dTaW5rIiwNCiAgICAgICAgICAgICJ0eXBlIjogIkNvbnNvbGVMb2dTaW5rIiwNCiAgICAgICAgICAgICJjb25maWciOiB7DQogICAgICAgICAgICAgICAgImxldmVsIjogIkRFQlVHIg0KICAgICAgICAgICAgfQ0KICAgICAgICB9LA0KICAgICAgICB7DQogICAgICAgICAgICAibmFtZSI6ICJKd3RTZXNzaW9uIiwNCiAgICAgICAgICAgICJ0eXBlIjogIkp3dFNlc3Npb24iDQogICAgICAgIH0sDQogICAgICAgIHsNCiAgICAgICAgICAgICJuYW1lIjogIkNsaWVudEhhbmRsZXIiLA0KICAgICAgICAgICAgInR5cGUiOiAiQ2xpZW50SGFuZGxlciINCiAgICAgICAgfSwNCiAgICAgICAgew0KICAgICAgICAgICAgIm5hbWUiOiAiY2FwdHVyZSIsDQogICAgICAgICAgICAidHlwZSI6ICJDYXB0dXJlRGVjb3JhdG9yIiwNCiAgICAgICAgICAgICJjb25maWciOiB7DQogICAgICAgICAgICAgICAgImNhcHR1cmVFbnRpdHkiOiB0cnVlLA0KICAgICAgICAgICAgICAgICJfY2FwdHVyZUV4Y2hhbmdlIjogdHJ1ZQ0KICAgICAgICAgICAgfQ0KICAgICAgICB9DQogICAgXQ0KfQ0KYGBgDQoNCiMjIFJvdXRlcyBmb3IgT1RSUw0KDQojIyMgTG9naW4gcm91dGUNCg0KYGBgSlNPTg0Kew0KCSJoYW5kbGVyIjogew0KCQkidHlwZSI6ICJDaGFpbiIsDQoJCSJjb25maWciOiB7DQoJCQkiZmlsdGVycyI6IFsNCgkJCQl7DQoJCQkJCSJ0eXBlIjogIkNyeXB0b0hlYWRlckZpbHRlciIsDQoJCQkJCSJjb25maWciOiB7DQoJCQkJCQkibWVzc2FnZVR5cGUiOiAiUkVRVUVTVCIsDQoJCQkJCQkib3BlcmF0aW9uIjogIkRFQ1JZUFQiLA0KCQkJCQkJImFsZ29yaXRobSI6ICJERVMvRUNCL05vUGFkZGluZyIsDQoJCQkJCQkia2V5IjogInh4eCIsDQoJCQkJCQkia2V5VHlwZSI6ICJERVMiLA0KCQkJCQkJImNoYXJTZXQiOiAidXRmLTgiLA0KCQkJCQkJImhlYWRlcnMiOiBbDQoJCQkJCQkJInBhc3N3b3JkIg0KCQkJCQkJXQ0KCQkJCQl9DQoJCQkJfSwNCgkJCQl7DQoJCQkJCSJ0eXBlIjogIkFzc2lnbm1lbnRGaWx0ZXIiLA0KCQkJCQkiY29uZmlnIjogew0KCQkJCQkJIm9uUmVxdWVzdCI6IFsNCgkJCQkJCQl7DQoJCQkJCQkJCSJ0YXJnZXQiOiAiJHtleGNoYW5nZS5hdXRoSW5mb1VzZXJuYW1lfSIsDQoJCQkJCQkJCSJ2YWx1ZSI6ICIke2V4Y2hhbmdlLnJlcXVlc3QuaGVhZGVyc1sndXNlcm5hbWUnXVswXX0iDQoJCQkJCQkJfSwNCgkJCQkJCQl7DQoJCQkJCQkJCSJ0YXJnZXQiOiAiJHtleGNoYW5nZS5hdXRoSW5mb1Bhc3N3b3JkfSIsDQoJCQkJCQkJCSJ2YWx1ZSI6ICIke2V4Y2hhbmdlLnJlcXVlc3QuaGVhZGVyc1sncGFzc3dvcmQnXVswXX0iDQoJCQkJCQkJfQ0KCQkJCQkJXQ0KCQkJCQl9DQoJCQkJfSwNCgkJCQl7DQoJCQkJCSJ0eXBlIjogIkhlYWRlckZpbHRlciIsDQoJCQkJCSJjb25maWciOiB7DQoJCQkJCQkibWVzc2FnZVR5cGUiOiAiUkVRVUVTVCIsDQoJCQkJCQkicmVtb3ZlIjogWw0KCQkJCQkJCSJwYXNzd29yZCIsDQoJCQkJCQkJInVzZXJuYW1lIg0KCQkJCQkJXQ0KCQkJCQl9DQoJCQkJfSwNCgkJCQl7DQoJCQkJCSJ0eXBlIjogIlN0YXRpY1JlcXVlc3RGaWx0ZXIiLA0KCQkJCQkiY29uZmlnIjogew0KCQkJCQkJIm1ldGhvZCI6ICJQT1NUIiwNCgkJCQkJCSJ1cmkiOiAiaHR0cHM6Ly9vdHJzLWZxZG4vb3Rycy9jdXN0b21lci5wbCIsDQoJCQkJCQkiZm9ybSI6IHsNCgkJCQkJCQkiVXNlciI6IFsNCgkJCQkJCQkJIiR7ZXhjaGFuZ2UuYXV0aEluZm9Vc2VybmFtZX0iDQoJCQkJCQkJXSwNCgkJCQkJCQkiUGFzc3dvcmQiOiBbDQoJCQkJCQkJCSIke2V4Y2hhbmdlLmF1dGhJbmZvUGFzc3dvcmR9Ig0KCQkJCQkJCV0sDQoJCQkJCQkJIkFjdGlvbiI6WyJMb2dpbiJdLA0KCQkJCQkJCSJSZXF1ZXN0ZWRVUkwiOlsiIl0sDQoJCQkJCQkJIkxhbmciOlsiZW4iXSwNCgkJCQkJCQkiVGltZU9mZnNldCI6WyItMTgwIl0NCgkJCQkJCX0NCgkJCQkJfQ0KCQkJCX0NCgkJCV0sDQoJCQkiaGFuZGxlciI6ICJDbGllbnRIYW5kbGVyIg0KCQl9DQoJfSwNCgkiY29uZGl0aW9uIjogIiR7bWF0Y2hlcyhleGNoYW5nZS5yZXF1ZXN0LnVyaS5wYXRoLCAnXi9yZXBsYXkvb3RycycpfSINCn0NCmBgYA0KDQojIyMgRGVmYXVsdCByb3V0ZQ0KDQpgYGBKU09ODQp7DQogICAgImhhbmRsZXIiOiAiQ2xpZW50SGFuZGxlciIsDQoJImNvbmRpdGlvbiI6ICIke21hdGNoZXMoZXhjaGFuZ2UucmVxdWVzdC51cmkucGF0aCwgJ14vb3RycycpfSIsDQoJImJhc2VVUkkiOiAiaHR0cHM6Ly9vdHJzLWZxZG4iDQoNCn0NCmBgYA0KDQojIyBSb3V0ZXMgZm9yIG90aGUgYXBwbGljYXRpb24gKHR3byBzdGVwcyBsb2dpbikNCg0KVGhpcyBhcHBsaWNhdGlvbiBpcyBKU0YgYmFzZWQgc28gdGhlIFBPU1QgYXQgbG9naW4gdmlldyBtdXN0IGhhdmUgdGhlIHNlc3Npb24gY29va2llIHNldC4gVGhlIHNvbHV0aW9uIGlzIHRvIGhhdmUgYSBmaXJzdCBzdGVwIHJlcXVlc3RpbmcgR0VUIGJlZm9yZSBzbyB0aGF0IHRoZSBzZXNzaW9uIGlzIGNyZWF0ZWQuDQoNCiMjIyBMb2dpbiByb3V0ZQ0KDQpgYGBKU09ODQp7DQoJImhlYXAiOiBbDQogICAgICAgIHsNCiAgICAgICAgICAgICJuYW1lIjogIkRpc3BhdGNoSGFuZGxlciIsDQogICAgICAgICAgICAidHlwZSI6ICJEaXNwYXRjaEhhbmRsZXIiLA0KICAgICAgICAgICAgImNvbmZpZyI6IHsNCiAgICAgICAgICAgICAgICAiYmluZGluZ3MiOiBbDQogICAgICAgICAgICAgICAgICAgIHsNCgkJCQkJCSJoYW5kbGVyIjogew0KCQkJCQkJCSJ0eXBlIjogIkNoYWluIiwNCgkJCQkJCQkiY29uZmlnIjogew0KCQkJCQkJCQkiZmlsdGVycyI6IFsNCgkJCQkJCQkJCXsNCgkJCQkJCQkJCQkidHlwZSI6ICJDcnlwdG9IZWFkZXJGaWx0ZXIiLA0KCQkJCQkJCQkJCSJjb25maWciOiB7DQoJCQkJCQkJCQkJCSJtZXNzYWdlVHlwZSI6ICJSRVFVRVNUIiwNCgkJCQkJCQkJCQkJIm9wZXJhdGlvbiI6ICJERUNSWVBUIiwNCgkJCQkJCQkJCQkJImFsZ29yaXRobSI6ICJERVMvRUNCL05vUGFkZGluZyIsDQoJCQkJCQkJCQkJCSJrZXkiOiAieHh4IiwNCgkJCQkJCQkJCQkJImtleVR5cGUiOiAiREVTIiwNCgkJCQkJCQkJCQkJImNoYXJTZXQiOiAidXRmLTgiLA0KCQkJCQkJCQkJCQkiaGVhZGVycyI6IFsNCgkJCQkJCQkJCQkJCSJwYXNzd29yZCINCgkJCQkJCQkJCQkJXQ0KCQkJCQkJCQkJCX0NCgkJCQkJCQkJCX0sDQoJCQkJCQkJCQl7DQoJCQkJCQkJCQkJInR5cGUiOiAiQXNzaWdubWVudEZpbHRlciIsDQoJCQkJCQkJCQkJImNvbmZpZyI6IHsNCgkJCQkJCQkJCQkJIm9uUmVxdWVzdCI6IFsNCgkJCQkJCQkJCQkJCXsNCgkJCQkJCQkJCQkJCQkidGFyZ2V0IjogIiR7ZXhjaGFuZ2UuYXV0aEluZm9Vc2VybmFtZX0iLA0KCQkJCQkJCQkJCQkJCSJ2YWx1ZSI6ICIke2V4Y2hhbmdlLnJlcXVlc3QuaGVhZGVyc1sndXNlcm5hbWUnXVswXX0iDQoJCQkJCQkJCQkJCQl9LA0KCQkJCQkJCQkJCQkJew0KCQkJCQkJCQkJCQkJCSJ0YXJnZXQiOiAiJHtleGNoYW5nZS5hdXRoSW5mb1Bhc3N3b3JkfSIsDQoJCQkJCQkJCQkJCQkJInZhbHVlIjogIiR7ZXhjaGFuZ2UucmVxdWVzdC5oZWFkZXJzWydwYXNzd29yZCddWzBdfSINCgkJCQkJCQkJCQkJCX0NCgkJCQkJCQkJCQkJXQ0KCQkJCQkJCQkJCX0NCgkJCQkJCQkJCX0sDQoJCQkJCQkJCQl7DQoJCQkJCQkJCQkJInR5cGUiOiAiSGVhZGVyRmlsdGVyIiwNCgkJCQkJCQkJCQkiY29uZmlnIjogew0KCQkJCQkJCQkJCQkibWVzc2FnZVR5cGUiOiAiUkVRVUVTVCIsDQoJCQkJCQkJCQkJCSJyZW1vdmUiOiBbDQoJCQkJCQkJCQkJCQkicGFzc3dvcmQiLA0KCQkJCQkJCQkJCQkJInVzZXJuYW1lIg0KCQkJCQkJCQkJCQldDQoJCQkJCQkJCQkJfQ0KCQkJCQkJCQkJfSwNCgkJCQkJCQkJCQ0KCQkJCQkJCQkJew0KCQkJCQkJCQkJCSJ0eXBlIjogIlN0YXRpY1JlcXVlc3RGaWx0ZXIiLA0KCQkJCQkJCQkJCSJjb25maWciOiB7DQoJCQkJCQkJCQkJCSJtZXRob2QiOiAiR0VUIiwNCgkJCQkJCQkJCQkJInVyaSI6ICJodHRwOi8vbGVnYWN5LWFwcC1mcWRuOjgwODEvTXlBcHAvZmFjZXMvbG9naW4ueGh0bWwiDQoJCQkJCQkJCQkJfQ0KCQkJCQkJCQkJfSwNCgkJCQkJCQkJCXsNCgkJCQkJCQkJCQkidHlwZSI6ICJTd2l0Y2hGaWx0ZXIiLA0KCQkJCQkJCQkJCSJjb25maWciOiB7DQoJCQkJCQkJCQkJCSJvblJlc3BvbnNlIjogWw0KCQkJCQkJCQkJCQkJew0KCQkJCQkJCQkJCQkJCSJoYW5kbGVyIjogIkxvZ2luUmVxdWVzdEhhbmRsZXIiDQoJCQkJCQkJCQkJCQl9DQoJCQkJCQkJCQkJCV0NCgkJCQkJCQkJCQl9DQoJCQkJCQkJCQl9LA0KCQkJCQkJCQkJew0KCQkJCQkJCQkJCSJ0eXBlIjogIkVudGl0eUV4dHJhY3RGaWx0ZXIiLA0KCQkJCQkJCQkJCSJjb25maWciOiB7DQoJCQkJCQkJCQkJCSJtZXNzYWdlVHlwZSI6ICJyZXNwb25zZSIsDQoJCQkJCQkJCQkJCSJ0YXJnZXQiOiAiJHtleGNoYW5nZS52aWV3U3RhdGV9IiwNCgkJCQkJCQkJCQkJImJpbmRpbmdzIjogWw0KCQkJCQkJCQkJCQkJew0KCQkJCQkJCQkJCQkJCSJrZXkiOiAidmFsdWUiLA0KCQkJCQkJCQkJCQkJCSJwYXR0ZXJuIjoNCgkJCQkJCQkJCQkJCQkJImphdmF4XFwuZmFjZXNcXC5WaWV3U3RhdGVcIlxccy4qdmFsdWU9XCIoLiopXCJcXHMqYXV0b2NvbXBsZXRlPSIsDQoJCQkJCQkJCQkJCQkJInRlbXBsYXRlIjogIiQxIg0KCQkJCQkJCQkJCQkJfQ0KCQkJCQkJCQkJCQldDQoJCQkJCQkJCQkJfQ0KCQkJCQkJCQkJfSwNCgkJCQkJCQkJCXsNCgkJCQkJCQkJCQkidHlwZSI6ICJBc3NpZ25tZW50RmlsdGVyIiwNCgkJCQkJCQkJCQkiY29uZmlnIjogew0KCQkJCQkJCQkJCQkib25SZXNwb25zZSI6IFsNCgkJCQkJCQkJCQkJCXsNCgkJCQkJCQkJCQkJCQkidGFyZ2V0IjogIiR7ZXhjaGFuZ2Uuc2Vzc2lvbkNvb2tpZX0iLA0KCQkJCQkJCQkJCQkJCSJ2YWx1ZSI6ICIke3NwbGl0KGV4Y2hhbmdlLnJlc3BvbnNlLmhlYWRlcnNbJ1NldC1Db29raWUnXVswXSwnOycpWzBdfSINCgkJCQkJCQkJCQkJCX0NCgkJCQkJCQkJCQkJXQ0KCQkJCQkJCQkJCX0NCgkJCQkJCQkJCX0NCg0KCQkJCQkJCQldLA0KCQkJCQkJCQkiaGFuZGxlciI6ICJDbGllbnRIYW5kbGVyIg0KCQkJCQkJCX0NCgkJCQkJCX0NCiAgICANCgkJCQkJfQ0KCQkJDQoJCQkJXQ0KCQkJfQ0KCQl9LA0KCQl7DQogICAgICAgICAgICAibmFtZSI6ICJMb2dpblJlcXVlc3RIYW5kbGVyIiwNCiAgICAgICAgICAgICJ0eXBlIjogIkNoYWluIiwNCiAgICAgICAgICAgICJjb25maWciOiB7DQogICAgICAgICAgICAgICAgImZpbHRlcnMiOiBbDQoJCQkJDQoJCQkJew0KCQkJCQkidHlwZSI6ICJTdGF0aWNSZXF1ZXN0RmlsdGVyIiwNCgkJCQkJImNvbmZpZyI6IHsNCgkJCQkJCSJtZXRob2QiOiAiUE9TVCIsDQoJCQkJCQkidXJpIjogImh0dHA6Ly9sZWdhY3ktYXBwLWZxZG46ODA4MS9NeUFwcC9mYWNlcy9sb2dpbi54aHRtbCIsDQoJCQkJCQkiZm9ybSI6IHsNCgkJCQkJCQkibG9naW5Gb3JtOmpfdXNlcm5hbWUiOiBbDQoJCQkJCQkJCSIke2V4Y2hhbmdlLmF1dGhJbmZvVXNlcm5hbWV9Ig0KCQkJCQkJCV0sDQoJCQkJCQkJImxvZ2luRm9ybTpqX3Bhc3N3b3JkIjogWw0KCQkJCQkJCQkiJHtleGNoYW5nZS5hdXRoSW5mb1Bhc3N3b3JkfSINCgkJCQkJCQldLA0KCQkJCQkJCSJsb2dpbkZvcm0iOlsibG9naW5Gb3JtIl0sDQoJCQkJCQkJImxvZ2luRm9ybTpqX2lkdDE3IjpbIiJdLA0KCQkJCQkJCSJqYXZheC5mYWNlcy5WaWV3U3RhdGUiOlsiJHtleGNoYW5nZS52aWV3U3RhdGUudmFsdWV9Il0NCgkJCQkJCX0sDQoJCQkJCQkiaGVhZGVycyI6IHsNCgkJCQkJCQkiQ29va2llIjogWyIke2V4Y2hhbmdlLnNlc3Npb25Db29raWV9Il0NCgkJCQkJCX0NCgkJCQkJfQ0KCQkJCX0sDQoJCQkJew0KCQkJCQkgInR5cGUiOiAiSGVhZGVyRmlsdGVyIiwNCgkJCQkJICJjb25maWciOiB7DQoJCQkJCQkgIm1lc3NhZ2VUeXBlIjogIlJFU1BPTlNFIiwNCgkJCQkJCSAiYWRkIjogew0KCQkJCQkJCSAiU2V0LUNvb2tpZSI6IFsgIiR7ZXhjaGFuZ2Uuc2Vzc2lvbkNvb2tpZX07IHBhdGg9L015QXBwIiBdDQoJCQkJCQkgfQ0KCQkJCQkgfQ0KCQkJCX0NCiAgICAgICAgICAgICAgICBdLA0KICAgICAgICAgICAgICAgICJoYW5kbGVyIjogIkNsaWVudEhhbmRsZXIiDQoJCQl9DQogICAgICAgIH0NCgldLA0KCSJoYW5kbGVyIjogIkRpc3BhdGNoSGFuZGxlciIsDQoJImNvbmRpdGlvbiI6ICIke21hdGNoZXMoZXhjaGFuZ2UucmVxdWVzdC51cmkucGF0aCwgJ14vcmVwbGF5L015QXBwJyl9Ig0KfQ0KYGBgIA0KDQojIyMgRGVmYXVsdCByb3V0ZQ0KDQpgYGBKU09ODQp7DQogICAgImhhbmRsZXIiOiAiQ2xpZW50SGFuZGxlciIsDQoJImNvbmRpdGlvbiI6ICIke21hdGNoZXMoZXhjaGFuZ2UucmVxdWVzdC51cmkucGF0aCwgJ14vTXlBcHAnKX0iLA0KCSJiYXNlVVJJIjogImh0dHA6Ly9sZWdhY3ktYXBwLWZxZG46ODA4MSINCg0KfQ0KYGBgDQoNCiMgU2V2ZXJhbCByZW1hcmtzDQoNCkFzIEkgZGVjaWRlZCB0byBhYmFuZG9uIHRoaXMgc29sdXRpb24gdGhlcmUgd2VyZSB0aGluZ3MgdW5jb25maWd1cmVkIG9yIG1pc3Njb25maWd1cmVkLiBKdXN0IHRvIG1lbnRpb24gdHdvOg0KKiBMb2dvdXQgcHJvY2VzcyAoeW91IG1pZ2h0IGVuZCB1cCB3aXRoIHRoZSBzaW11bGF0aW9uIG9mIFNTTyBzY3Jld2VkIHVwIGlmIHRoZSB1c2VycyBsb2dvdXQgb2Ygb25lIHRoZSBhcHBsaWNhdGlvbiB0cnlpbmcgdG8gcmUtbG9nLWluIGFzIGEgZGlmZmVyZW50IHVzZXIpDQoqIFNuaWZmaW5nIHRoZSBuZXR3b3JrIGR1cmluZyB0ZXN0cyBJIG5vdGljZWQgYW5ub3VuY2VtZW50IChQT1NUcyBpZiBJIHJlbWVtYmVyIGNvcmVjdGx5KSBmcm9tIE9wZW5BTSB0byBKMkVFIGFnZW50LCBtZXNzYWdlcyBub3QgaW50ZXJjZXB0ZWQgYnkgdGhlIGFnZW50IGFuZCBmb3J3YXJkZWQgKG9yIGRyb3BwZWQsIGRlcGVuZGluZyBvbiBkZWZhdWx0IHJvdXRlciBjb25maWd1cmF0aW9uKSBieSBPcGVuSUc=

OpenAM (strikes back): Install/Configure OpenAM

For a basic installation see http://fmanea.blogspot.ro/2013/10/install-and-configure-openam-for-spring.html up to creating the SP (current installation was done on Tomcat 8.0 as opposed to 7.0 for the above mentioned post).

OpenAM requirements for the container

In order to add required JVM start-up parameters edit Tomcat8.0\bin\service.bat by modifying the line: --JvmOptions "-Xdebug;-Xnoagent;-Djava.compiler=NONE;-Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=5006;-Xmx1024m;... (also adds debug support which should be removed in production environment). Stop the service, run service.bat remove, run service.bat install as administrator to reinstall service, start the sdervice

Add an AD based realm

  • Log in into OpenAM.
  • Go to Access Control.
  • Add a new Realm as child of the Top Level Realm
  • Use this post to configure the Realm.
    • Clear Persistent Search Base DN: text field (in DataStore attributes area) so that you will not get periodic (very often) searches against the AD user entries (sort of periodic browse)
    • In Core Realm Attributes mark User profile to Ignore
    • In Realm's Authentication tab look for the Active Directory module you just configured;edit this entry:
      • Uncheck Return User DN to DataStore: so that sAMAcoounyName is returned instead of user's DN

Important notice

In order to check effects of this area configuration have a look at the classes in com.sun.identity.authentication.modules.ldap (OpenAM Auth LDAP module).

Authorization (not subject of this post series) looks like being handled in com.sun.identity.idm.plugins.ldapv3 package, module OpenAM Core.

PHA+Rm9yIGEgYmFzaWMgaW5zdGFsbGF0aW9uIHNlZSA8YSBocmVmPSJodHRwOi8vZm1hbmVhLmJsb2dzcG90LnJvLzIwMTMvMTAvaW5zdGFsbC1hbmQtY29uZmlndXJlLW9wZW5hbS1mb3Itc3ByaW5nLmh0bWwiPmh0dHA6Ly9mbWFuZWEuYmxvZ3Nwb3Qucm8vMjAxMy8xMC9pbnN0YWxsLWFuZC1jb25maWd1cmUtb3BlbmFtLWZvci1zcHJpbmcuaHRtbDwvYT4gdXAgdG8gY3JlYXRpbmcgdGhlIFNQIChjdXJyZW50IGluc3RhbGxhdGlvbiB3YXMgZG9uZSBvbiBUb21jYXQgOC4wIGFzIG9wcG9zZWQgdG8gNy4wIGZvciB0aGUgYWJvdmUgbWVudGlvbmVkIHBvc3QpLjwvcD4NCjxoMT5PcGVuQU0gcmVxdWlyZW1lbnRzIGZvciB0aGUgY29udGFpbmVyPC9oMT4NCjxwPkluIG9yZGVyIHRvIGFkZCByZXF1aXJlZCBKVk0gc3RhcnQtdXAgcGFyYW1ldGVycyBlZGl0IFRvbWNhdDguMFxiaW5cc2VydmljZS5iYXQgYnkgbW9kaWZ5aW5nIHRoZSBsaW5lOiA8Y29kZT4tLUp2bU9wdGlvbnMgJnF1b3Q7LVhkZWJ1ZzstWG5vYWdlbnQ7LURqYXZhLmNvbXBpbGVyPU5PTkU7LVhydW5qZHdwOnRyYW5zcG9ydD1kdF9zb2NrZXQsc2VydmVyPXksc3VzcGVuZD1uLGFkZHJlc3M9NTAwNjstWG14MTAyNG07Li4uPC9jb2RlPiAoYWxzbyBhZGRzIGRlYnVnIHN1cHBvcnQgd2hpY2ggc2hvdWxkIGJlIHJlbW92ZWQgaW4gcHJvZHVjdGlvbiBlbnZpcm9ubWVudCkuDQpTdG9wIHRoZSBzZXJ2aWNlLCBydW4gPGNvZGU+c2VydmljZS5iYXQgcmVtb3ZlPC9jb2RlPiwgcnVuIDxjb2RlPnNlcnZpY2UuYmF0IGluc3RhbGw8L2NvZGU+IGFzIGFkbWluaXN0cmF0b3IgdG8gcmVpbnN0YWxsIHNlcnZpY2UsIHN0YXJ0IHRoZSBzZGVydmljZTwvcD4NCjxoMT5BZGQgYW4gQUQgYmFzZWQgcmVhbG08L2gxPg0KPHVsPg0KPGxpPkxvZyBpbiBpbnRvIE9wZW5BTS48L2xpPg0KPGxpPkdvIHRvIEFjY2VzcyBDb250cm9sLjwvbGk+DQo8bGk+QWRkIGEgbmV3IFJlYWxtIGFzIGNoaWxkIG9mIHRoZSBUb3AgTGV2ZWwgUmVhbG08L2xpPg0KPGxpPlVzZSA8YSBocmVmPSJodHRwczovL3dpa2lzLmZvcmdlcm9jay5vcmcvY29uZmx1ZW5jZS9kaXNwbGF5L29wZW5hbS9Db25maWd1cmUrT3BlbkFNK3RvK3VzZStBY3RpdmUrRGlyZWN0b3J5K2ZvcitBdXRoZW50aWNhdGlvbithbmQrRGF0YVN0b3JlIj50aGlzIHBvc3Q8L2E+IHRvIGNvbmZpZ3VyZSB0aGUgUmVhbG0uDQo8dWw+DQo8bGk+Q2xlYXIgPGNvZGU+UGVyc2lzdGVudCBTZWFyY2ggQmFzZSBETjo8L2NvZGU+IHRleHQgZmllbGQgKGluIERhdGFTdG9yZSBhdHRyaWJ1dGVzIGFyZWEpIHNvIHRoYXQgeW91IHdpbGwgbm90IGdldCBwZXJpb2RpYyAodmVyeSBvZnRlbikgc2VhcmNoZXMgYWdhaW5zdCB0aGUgQUQgdXNlciBlbnRyaWVzIChzb3J0IG9mIHBlcmlvZGljIGJyb3dzZSk8L2xpPg0KPGxpPkluIENvcmUgUmVhbG0gQXR0cmlidXRlcyBtYXJrIDxjb2RlPlVzZXIgcHJvZmlsZTwvY29kZT4gdG8gSWdub3JlPC9saT4NCjxsaT5JbiBSZWFsbSdzIEF1dGhlbnRpY2F0aW9uIHRhYiBsb29rIGZvciB0aGUgQWN0aXZlIERpcmVjdG9yeSBtb2R1bGUgeW91IGp1c3QgY29uZmlndXJlZDtlZGl0IHRoaXMgZW50cnk6DQo8dWw+DQo8bGk+VW5jaGVjayA8Y29kZT5SZXR1cm4gVXNlciBETiB0byBEYXRhU3RvcmU6PC9jb2RlPiBzbyB0aGF0IHNBTUFjb291bnlOYW1lIGlzIHJldHVybmVkIGluc3RlYWQgb2YgdXNlcidzIEROPC9saT4NCjwvdWw+DQo8L2xpPg0KPC91bD4NCjwvbGk+DQo8L3VsPg0KPGgxPkltcG9ydGFudCBub3RpY2U8L2gxPg0KPHA+SW4gb3JkZXIgdG8gY2hlY2sgZWZmZWN0cyBvZiB0aGlzIGFyZWEgY29uZmlndXJhdGlvbiBoYXZlIGEgbG9vayBhdCB0aGUgY2xhc3NlcyBpbiA8Y29kZT5jb20uc3VuLmlkZW50aXR5LmF1dGhlbnRpY2F0aW9uLm1vZHVsZXMubGRhcDwvY29kZT4gKDxjb2RlPk9wZW5BTSBBdXRoIExEQVAgbW9kdWxlPC9jb2RlPikuPC9wPg0KPHA+QXV0aG9yaXphdGlvbiAobm90IHN1YmplY3Qgb2YgdGhpcyBwb3N0IHNlcmllcykgbG9va3MgbGlrZSBiZWluZyBoYW5kbGVkIGluIDxjb2RlPmNvbS5zdW4uaWRlbnRpdHkuaWRtLnBsdWdpbnMubGRhcHYzPC9jb2RlPiBwYWNrYWdlLCBtb2R1bGUgPGNvZGU+T3BlbkFNIENvcmU8L2NvZGU+LjwvcD4NCg==

OpenAM (strikes back): The Rationale

This post is (I hope) the first from a series describing the almost (!) successful journey to use OpenAM to simulate SSO for a bunch of legacy applications (among them an in house implementation of OTRS).
To begin with the conclusion (so you might abandon reading the series): after successfully implementing the solution I decide to not go further with releasing it into production due to two main facts:
  • While sources are released under CDDL 1.0 license the binaries deployment is restricted to paid subscription (so you would have to compile yourself the binaries;looking at the way the source control is organised (part SVN (looking erratic), part GitHub) this looks not very promising
  • Comments on the way OpenAM is built from architecture point of view (like https://evolveum.com/blog/hacking-openam-level-nightmare/ )

The problem

As I mentioned the main goal was to implement a SSO type solution for legacy web application, main target being an in house implementation of OTRS.
Another tough restriction is to use as repository Active Directory but not as main data store (since our IT guys will not allow OpenAM to update the default schema).

The sketched solution

  • Use OpenAM as Identity Provider
  • Use OpenIG to implement custom rules at reverse proxy level; this is required for two mai purposes:
    • acting as a backend for the JEE agent
    • implementing a two step scenario for applications that require acquiring (!) a session cookie before a password replay POST can be executed
  • Use an JEE agent to implement password replay
VGhpcyBwb3N0IGlzIChJIGhvcGUpIHRoZSBmaXJzdCBmcm9tIGEgc2VyaWVzIGRlc2NyaWJpbmcgdGhlIGFsbW9zdCAoISkgc3VjY2Vzc2Z1bCBqb3VybmV5IHRvIHVzZSBPcGVuQU0gdG8gc2ltdWxhdGUgU1NPIGZvciBhIGJ1bmNoIG9mIGxlZ2FjeSBhcHBsaWNhdGlvbnMgKGFtb25nIHRoZW0gYW4gaW4gaG91c2UgaW1wbGVtZW50YXRpb24gb2YgT1RSUykuDQoNClRvIGJlZ2luIHdpdGggdGhlIGNvbmNsdXNpb24gKHNvIHlvdSBtaWdodCBhYmFuZG9uIHJlYWRpbmcgdGhlIHNlcmllcyk6IGFmdGVyIHN1Y2Nlc3NmdWxseSBpbXBsZW1lbnRpbmcgdGhlICBzb2x1dGlvbiBJIGRlY2lkZSB0byBub3QgZ28gZnVydGhlciB3aXRoIHJlbGVhc2luZyBpdCBpbnRvIHByb2R1Y3Rpb24gZHVlIHRvIHR3byBtYWluIGZhY3RzOg0KKiBXaGlsZSBzb3VyY2VzIGFyZSByZWxlYXNlZCB1bmRlciBDRERMIDEuMCBsaWNlbnNlIHRoZSBiaW5hcmllcyBkZXBsb3ltZW50IGlzIHJlc3RyaWN0ZWQgdG8gcGFpZCBzdWJzY3JpcHRpb24gKHNvIHlvdSB3b3VsZCBoYXZlIHRvIGNvbXBpbGUgeW91cnNlbGYgdGhlIGJpbmFyaWVzO2xvb2tpbmcgYXQgdGhlIHdheSB0aGUgc291cmNlIGNvbnRyb2wgaXMgb3JnYW5pc2VkIChwYXJ0IFNWTiAobG9va2luZyBlcnJhdGljKSwgcGFydCBHaXRIdWIpIHRoaXMgbG9va3Mgbm90IHZlcnkgcHJvbWlzaW5nDQoqIENvbW1lbnRzIG9uIHRoZSB3YXkgT3BlbkFNIGlzIGJ1aWx0IGZyb20gYXJjaGl0ZWN0dXJlIHBvaW50IG9mIHZpZXcgKGxpa2UgW2h0dHBzOi8vZXZvbHZldW0uY29tL2Jsb2cvaGFja2luZy1vcGVuYW0tbGV2ZWwtbmlnaHRtYXJlL10oaHR0cHM6Ly9ldm9sdmV1bS5jb20vYmxvZy9oYWNraW5nLW9wZW5hbS1sZXZlbC1uaWdodG1hcmUvKSApDQoNCiMgVGhlIHByb2JsZW0NCg0KQXMgSSBtZW50aW9uZWQgdGhlIG1haW4gZ29hbCB3YXMgdG8gaW1wbGVtZW50IGEgU1NPIHR5cGUgc29sdXRpb24gZm9yIGxlZ2FjeSB3ZWIgYXBwbGljYXRpb24sIG1haW4gdGFyZ2V0IGJlaW5nIGFuIGluIGhvdXNlIGltcGxlbWVudGF0aW9uIG9mIE9UUlMuDQoNCkFub3RoZXIgdG91Z2ggcmVzdHJpY3Rpb24gaXMgdG8gdXNlIGFzIHJlcG9zaXRvcnkgQWN0aXZlIERpcmVjdG9yeSBidXQgbm90IGFzIG1haW4gZGF0YSBzdG9yZSAoc2luY2Ugb3VyIElUIGd1eXMgd2lsbCBub3QgYWxsb3cgT3BlbkFNIHRvIHVwZGF0ZSB0aGUgZGVmYXVsdCBzY2hlbWEpLg0KDQogIyBUaGUgc2tldGNoZWQgc29sdXRpb24NCg0KKiBVc2UgT3BlbkFNIGFzIElkZW50aXR5IFByb3ZpZGVyDQoqIFVzZSBPcGVuSUcgdG8gaW1wbGVtZW50IGN1c3RvbSBydWxlcyBhdCByZXZlcnNlIHByb3h5IGxldmVsOyB0aGlzIGlzIHJlcXVpcmVkIGZvciB0d28gbWFpIHB1cnBvc2VzOg0KCSogYWN0aW5nIGFzIGEgYmFja2VuZCBmb3IgdGhlIEpFRSBhZ2VudA0KCSogaW1wbGVtZW50aW5nIGEgdHdvIHN0ZXAgc2NlbmFyaW8gZm9yIGFwcGxpY2F0aW9ucyB0aGF0IHJlcXVpcmUgYWNxdWlyaW5nICghKSBhIHNlc3Npb24gY29va2llIGJlZm9yZSBhIHBhc3N3b3JkIHJlcGxheSBQT1NUIGNhbiBiZSBleGVjdXRlZA0KKiBVc2UgYW4gSkVFIGFnZW50IHRvIGltcGxlbWVudCBwYXNzd29yZCByZXBsYXkNCg==