Wednesday, October 28, 2015

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==