Monday, November 2, 2015

Shibboleth-We Are Not Done Yet-Part 1

First thing that popped out in my mind after basic installation/configuration worked was to check how could be the login page customized. But where to find that page?

The answer is simple: login.vm (idp-conf\src\main\resources\views\login.vm), a template based on Velocity. It was easy to find: look at the id assigned to password field (for instance), j_password and do a recursive search in sources directory.

Note: If changing main.css is necessary please notice that the UI part is packed as idp.war...

This is not enough; we want to understand how it works and where to look if we get into trouble.

To start with: a lot of Shibbolet functionality (including UI behaviour) is based on Spring Web Flow. The main configuration file (webflow-config.xml) can be found by looking into web.config.

    <servlet>
        <servlet-name>idp</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>${idp.home}/system/conf/mvc-beans.xml, ${idp.home}/system/conf/webflow-config.xml</param-value>
        </init-param>
...

It defines application web-flows accessible through the registry (org.springframework.webflow.definition.registry.FlowDefinitionRegistryImpl, method getFlowDefinition, spring-webflow-2.4.1.RELEASE.jar).

The flow we are interested in is that with id="SAML2/Unsolicited/SSO". When we access HTTPS://<shibblolet-url>:<https-port>/idp/profile/SAML2/Unsolicited/SSO?providerId=<SP_entityID> DispatcherHandler will finally call org.springframework.webflow.mvc.servlet.FlowHandlerMapping, method getHandlerInternal, parameter HttpServletRequest request. Request properties are as follows:

  • contextPath: "/idp"
  • servletPath: "/profile"
  • pathInfo: "/SAML2/Unsolicited/SSO"

Based on pathInfo the web-flow with the matching id is activated. A breakpoint set in org.springframework.webflow.executor.FlowExecutorImpl, method launchExecution, after FlowDefinition flowDefinition = definitionLocator.getFlowDefinition(flowId); allows us to examine flowDefinition and see the states defined in the work-flow that is about to be executed.

Having a look in the flow definition (path="../system/flows/saml/saml2/sso-unsolicited-flow.xml") is quite confusing for the newbies: no actually flow description (according to what you will see in tutorials) but just two regular Spring beans definition: the point is that any such bean exposing an 'execute' method will be treated as a state-action and the methods will be called in the order of bean definition.

OK, but where comes in action our login.vm? The parent="saml2.sso.abstract" attribute in flow tag definition is the key: our flow inherits its definition from that flow (identified by this id in web-config.xml). Somehow (I haven't checked all the inheritance/definitions chain) the authn/Password sub-flow is activated, sub-flow that contains a view-state referring our template in the view attribute:

    <view-state id="DisplayUsernamePasswordPage" view="login">
        <on-render>
            <evaluate expression="environment" result="viewScope.environment" />
            <evaluate expression="opensamlProfileRequestContext" result="viewScope.profileRequestContext" />
            <evaluate expression="opensamlProfileRequestContext.getSubcontext(T(net.shibboleth.idp.authn.context.AuthenticationContext))" result="viewScope.authenticationContext" />
...

The transition <transition on="proceed" to="ExtractUsernamePasswordFromFormRequest"> is activated when the Login button is pressed (id='_eventId_proceed'-> proceed constant will become available to be evaluated by the transition condition based on the convention id=_eventId_... ).

Action state ExtractUsernamePasswordFromFormRequest will be executed:

    <action-state id="ExtractUsernamePasswordFromFormRequest">
        <evaluate expression="ExtractUsernamePasswordFromFormRequest" />
        <evaluate expression="'proceed'" />
    
        <!-- Let the validate action handle any problems later. -->        
        <transition to="ValidateUsernamePassword" />
    </action-state>

The <evaluate expression="ExtractUsernamePasswordFromFormRequest" /> has the role to call execute method of bean with id="ExtractUsernamePasswordFromFormRequest" ( class defined in module Shibboleth IdP :: Authentication Implementation) while <evaluate expression="'proceed'"/> just makes 'proceed' the current value to be evaluated by subsequent transitions (not actually used here but pattern used in may other places).

Finally we land in ValidateUsernamePasswordAgainstLDAP.execute method.

DQpGaXJzdCB0aGluZyB0aGF0IHBvcHBlZCBvdXQgaW4gbXkgbWluZCBhZnRlciBiYXNpYyBpbnN0YWxsYXRpb24vY29uZmlndXJhdGlvbiB3b3JrZWQgd2FzIHRvIGNoZWNrIGhvdyBjb3VsZCBiZSB0aGUgbG9naW4gcGFnZSBjdXN0b21pemVkLiAgQnV0IHdoZXJlIHRvIGZpbmQgdGhhdCBwYWdlPw0KDQpUaGUgYW5zd2VyIGlzIHNpbXBsZTogbG9naW4udm0gKGBpZHAtY29uZlxzcmNcbWFpblxyZXNvdXJjZXNcdmlld3NcbG9naW4udm1gKSwgYSB0ZW1wbGF0ZSBiYXNlZCBvbiBWZWxvY2l0eS4gSXQgd2FzIGVhc3kgdG8gZmluZDogbG9vayBhdCB0aGUgaWQgYXNzaWduZWQgdG8gcGFzc3dvcmQgZmllbGQgKGZvciBpbnN0YW5jZSksIGBqX3Bhc3N3b3JkYCAgYW5kIGRvIGEgcmVjdXJzaXZlIHNlYXJjaCBpbiAgc291cmNlcyBkaXJlY3RvcnkuDQoNCioqTm90ZSoqOiBJZiBjaGFuZ2luZyAqbWFpbi5jc3MqIGlzIG5lY2Vzc2FyeSBwbGVhc2Ugbm90aWNlIHRoYXQgdGhlIFVJIHBhcnQgaXMgcGFja2VkIGFzIGlkcC53YXIuLi4NCg0KVGhpcyBpcyBub3QgZW5vdWdoOyB3ZSB3YW50IHRvIHVuZGVyc3RhbmQgaG93IGl0IHdvcmtzIGFuZCB3aGVyZSB0byBsb29rIGlmIHdlIGdldCBpbnRvIHRyb3VibGUuDQoNClRvIHN0YXJ0IHdpdGg6IGEgbG90IG9mIFNoaWJib2xldCBmdW5jdGlvbmFsaXR5IChpbmNsdWRpbmcgVUkgYmVoYXZpb3VyKSBpcyBiYXNlZCBvbiBTcHJpbmcgV2ViIEZsb3cuIFRoZSBtYWluIGNvbmZpZ3VyYXRpb24gZmlsZSAoYHdlYmZsb3ctY29uZmlnLnhtbGApIGNhbiBiZSBmb3VuZCBieSBsb29raW5nIGludG8gYHdlYi5jb25maWdgLg0KYGBgeG1sDQogICAgPHNlcnZsZXQ+DQogICAgICAgIDxzZXJ2bGV0LW5hbWU+aWRwPC9zZXJ2bGV0LW5hbWU+DQogICAgICAgIDxzZXJ2bGV0LWNsYXNzPm9yZy5zcHJpbmdmcmFtZXdvcmsud2ViLnNlcnZsZXQuRGlzcGF0Y2hlclNlcnZsZXQ8L3NlcnZsZXQtY2xhc3M+DQogICAgICAgIDxpbml0LXBhcmFtPg0KICAgICAgICAgICAgPHBhcmFtLW5hbWU+Y29udGV4dENvbmZpZ0xvY2F0aW9uPC9wYXJhbS1uYW1lPg0KICAgICAgICAgICAgPHBhcmFtLXZhbHVlPiR7aWRwLmhvbWV9L3N5c3RlbS9jb25mL212Yy1iZWFucy54bWwsICR7aWRwLmhvbWV9L3N5c3RlbS9jb25mL3dlYmZsb3ctY29uZmlnLnhtbDwvcGFyYW0tdmFsdWU+DQogICAgICAgIDwvaW5pdC1wYXJhbT4NCi4uLg0KYGBgDQoNCkl0IGRlZmluZXMgYXBwbGljYXRpb24gd2ViLWZsb3dzIGFjY2Vzc2libGUgdGhyb3VnaCB0aGUgcmVnaXN0cnkgKGBvcmcuc3ByaW5nZnJhbWV3b3JrLndlYmZsb3cuZGVmaW5pdGlvbi5yZWdpc3RyeS5GbG93RGVmaW5pdGlvblJlZ2lzdHJ5SW1wbGAsIG1ldGhvZCBgZ2V0Rmxvd0RlZmluaXRpb25gLCBgc3ByaW5nLXdlYmZsb3ctMi40LjEuUkVMRUFTRS5qYXJgKS4NCg0KVGhlIGZsb3cgd2UgYXJlIGludGVyZXN0ZWQgaW4gaXMgdGhhdCB3aXRoIGBpZD0iU0FNTDIvVW5zb2xpY2l0ZWQvU1NPImAuIFdoZW4gd2UgYWNjZXNzIGBIVFRQUzovLzxzaGliYmxvbGV0LXVybD46PGh0dHBzLXBvcnQ+L2lkcC9wcm9maWxlL1NBTUwyL1Vuc29saWNpdGVkL1NTTz9wcm92aWRlcklkPTxTUF9lbnRpdHlJRD5gIGBEaXNwYXRjaGVySGFuZGxlcmAgd2lsbCBmaW5hbGx5IGNhbGwgYG9yZy5zcHJpbmdmcmFtZXdvcmsud2ViZmxvdy5tdmMuc2VydmxldC5GbG93SGFuZGxlck1hcHBpbmdgLCBtZXRob2QgYGdldEhhbmRsZXJJbnRlcm5hbGAsIHBhcmFtZXRlciBgSHR0cFNlcnZsZXRSZXF1ZXN0IHJlcXVlc3RgLiBSZXF1ZXN0IHByb3BlcnRpZXMgYXJlIGFzIGZvbGxvd3M6DQoNCiogY29udGV4dFBhdGg6ICIvaWRwIg0KKiBzZXJ2bGV0UGF0aDogIi9wcm9maWxlIg0KKiBwYXRoSW5mbzogIi9TQU1MMi9VbnNvbGljaXRlZC9TU08iDQoNCkJhc2VkIG9uIGBwYXRoSW5mb2AgdGhlIHdlYi1mbG93IHdpdGggdGhlIG1hdGNoaW5nIGlkIGlzIGFjdGl2YXRlZC4gQSBicmVha3BvaW50IHNldCBpbiBgb3JnLnNwcmluZ2ZyYW1ld29yay53ZWJmbG93LmV4ZWN1dG9yLkZsb3dFeGVjdXRvckltcGxgLCBtZXRob2QgYGxhdW5jaEV4ZWN1dGlvbmAsIGFmdGVyIGBGbG93RGVmaW5pdGlvbiBmbG93RGVmaW5pdGlvbiA9IGRlZmluaXRpb25Mb2NhdG9yLmdldEZsb3dEZWZpbml0aW9uKGZsb3dJZCk7YCBhbGxvd3MgdXMgdG8gZXhhbWluZSBgZmxvd0RlZmluaXRpb25gIGFuZCBzZWUgdGhlIHN0YXRlcyBkZWZpbmVkIGluIHRoZSB3b3JrLWZsb3cgdGhhdCBpcyBhYm91dCB0byBiZSBleGVjdXRlZC4NCg0KSGF2aW5nIGEgbG9vayBpbiB0aGUgZmxvdyBkZWZpbml0aW9uIChgcGF0aD0iLi4vc3lzdGVtL2Zsb3dzL3NhbWwvc2FtbDIvc3NvLXVuc29saWNpdGVkLWZsb3cueG1sImApIGlzIHF1aXRlIGNvbmZ1c2luZyBmb3IgdGhlIG5ld2JpZXM6IG5vIGFjdHVhbGx5IGZsb3cgZGVzY3JpcHRpb24gKGFjY29yZGluZyB0byB3aGF0IHlvdSB3aWxsIHNlZSBpbiB0dXRvcmlhbHMpIGJ1dCBqdXN0IHR3byByZWd1bGFyIFNwcmluZyBiZWFucyBkZWZpbml0aW9uOiB0aGUgcG9pbnQgaXMgdGhhdCBhbnkgc3VjaCBiZWFuIGV4cG9zaW5nIGFuICdleGVjdXRlJyBtZXRob2Qgd2lsbCBiZSB0cmVhdGVkIGFzIGEgc3RhdGUtYWN0aW9uIGFuZCB0aGUgbWV0aG9kcyB3aWxsIGJlIGNhbGxlZCBpbiB0aGUgb3JkZXIgb2YgYmVhbiBkZWZpbml0aW9uLg0KDQpPSywgYnV0IHdoZXJlIGNvbWVzIGluIGFjdGlvbiBvdXIgYGxvZ2luLnZtYD8gVGhlIGBwYXJlbnQ9InNhbWwyLnNzby5hYnN0cmFjdCJgIGF0dHJpYnV0ZSBpbiBmbG93IHRhZyBkZWZpbml0aW9uIGlzIHRoZSBrZXk6IG91ciBmbG93IGluaGVyaXRzIGl0cyBkZWZpbml0aW9uIGZyb20gdGhhdCBmbG93IChpZGVudGlmaWVkIGJ5IHRoaXMgaWQgaW4gYHdlYi1jb25maWcueG1sYCkuIFNvbWVob3cgKEkgaGF2ZW4ndCBjaGVja2VkIGFsbCB0aGUgaW5oZXJpdGFuY2UvZGVmaW5pdGlvbnMgY2hhaW4pIHRoZSBgYXV0aG4vUGFzc3dvcmRgIHN1Yi1mbG93IGlzIGFjdGl2YXRlZCwgc3ViLWZsb3cgdGhhdCBjb250YWlucyBhIGB2aWV3LXN0YXRlYCByZWZlcnJpbmcgb3VyIHRlbXBsYXRlIGluIHRoZSB2aWV3IGF0dHJpYnV0ZToNCmBgYHhtbA0KICAgIDx2aWV3LXN0YXRlIGlkPSJEaXNwbGF5VXNlcm5hbWVQYXNzd29yZFBhZ2UiIHZpZXc9ImxvZ2luIj4NCiAgICAgICAgPG9uLXJlbmRlcj4NCiAgICAgICAgICAgIDxldmFsdWF0ZSBleHByZXNzaW9uPSJlbnZpcm9ubWVudCIgcmVzdWx0PSJ2aWV3U2NvcGUuZW52aXJvbm1lbnQiIC8+DQogICAgICAgICAgICA8ZXZhbHVhdGUgZXhwcmVzc2lvbj0ib3BlbnNhbWxQcm9maWxlUmVxdWVzdENvbnRleHQiIHJlc3VsdD0idmlld1Njb3BlLnByb2ZpbGVSZXF1ZXN0Q29udGV4dCIgLz4NCiAgICAgICAgICAgIDxldmFsdWF0ZSBleHByZXNzaW9uPSJvcGVuc2FtbFByb2ZpbGVSZXF1ZXN0Q29udGV4dC5nZXRTdWJjb250ZXh0KFQobmV0LnNoaWJib2xldGguaWRwLmF1dGhuLmNvbnRleHQuQXV0aGVudGljYXRpb25Db250ZXh0KSkiIHJlc3VsdD0idmlld1Njb3BlLmF1dGhlbnRpY2F0aW9uQ29udGV4dCIgLz4NCi4uLg0KYGBgICANClRoZSB0cmFuc2l0aW9uIGA8dHJhbnNpdGlvbiBvbj0icHJvY2VlZCIgdG89IkV4dHJhY3RVc2VybmFtZVBhc3N3b3JkRnJvbUZvcm1SZXF1ZXN0Ij5gIGlzIGFjdGl2YXRlZCB3aGVuIHRoZSBMb2dpbiBidXR0b24gaXMgcHJlc3NlZCAoYGlkPSdfZXZlbnRJZF9wcm9jZWVkJ2AtXD4gYHByb2NlZWRgIGNvbnN0YW50IHdpbGwgYmVjb21lIGF2YWlsYWJsZSB0byBiZSBldmFsdWF0ZWQgYnkgdGhlIHRyYW5zaXRpb24gY29uZGl0aW9uIGJhc2VkIG9uIHRoZSBjb252ZW50aW9uIGBpZD1fZXZlbnRJZF8uLi5gICkuDQoNCkFjdGlvbiBzdGF0ZSBgRXh0cmFjdFVzZXJuYW1lUGFzc3dvcmRGcm9tRm9ybVJlcXVlc3RgIHdpbGwgYmUgZXhlY3V0ZWQ6DQpgYGB4bWwNCiAgICA8YWN0aW9uLXN0YXRlIGlkPSJFeHRyYWN0VXNlcm5hbWVQYXNzd29yZEZyb21Gb3JtUmVxdWVzdCI+DQogICAgICAgIDxldmFsdWF0ZSBleHByZXNzaW9uPSJFeHRyYWN0VXNlcm5hbWVQYXNzd29yZEZyb21Gb3JtUmVxdWVzdCIgLz4NCiAgICAgICAgPGV2YWx1YXRlIGV4cHJlc3Npb249Iidwcm9jZWVkJyIgLz4NCiAgICANCiAgICAgICAgPCEtLSBMZXQgdGhlIHZhbGlkYXRlIGFjdGlvbiBoYW5kbGUgYW55IHByb2JsZW1zIGxhdGVyLiAtLT4gICAgICAgIA0KICAgICAgICA8dHJhbnNpdGlvbiB0bz0iVmFsaWRhdGVVc2VybmFtZVBhc3N3b3JkIiAvPg0KICAgIDwvYWN0aW9uLXN0YXRlPg0KYGBgDQpUaGUgYDxldmFsdWF0ZSBleHByZXNzaW9uPSJFeHRyYWN0VXNlcm5hbWVQYXNzd29yZEZyb21Gb3JtUmVxdWVzdCIgLz5gIGhhcyB0aGUgcm9sZSB0byBjYWxsIGBleGVjdXRlYCBtZXRob2Qgb2YgYmVhbiB3aXRoIGBpZD0iRXh0cmFjdFVzZXJuYW1lUGFzc3dvcmRGcm9tRm9ybVJlcXVlc3QiYCAoIGNsYXNzIGRlZmluZWQgaW4gbW9kdWxlIGBTaGliYm9sZXRoIElkUCA6OiBBdXRoZW50aWNhdGlvbiBJbXBsZW1lbnRhdGlvbmApIHdoaWxlIGA8ZXZhbHVhdGUgZXhwcmVzc2lvbj0iJ3Byb2NlZWQnIi8+YCBqdXN0IG1ha2VzIGAncHJvY2VlZCdgIHRoZSBjdXJyZW50IHZhbHVlIHRvIGJlIGV2YWx1YXRlZCBieSBzdWJzZXF1ZW50IHRyYW5zaXRpb25zIChub3QgYWN0dWFsbHkgdXNlZCBoZXJlIGJ1dCBwYXR0ZXJuIHVzZWQgaW4gbWF5IG90aGVyIHBsYWNlcykuDQoNCkZpbmFsbHkgd2UgbGFuZCBpbiBgVmFsaWRhdGVVc2VybmFtZVBhc3N3b3JkQWdhaW5zdExEQVAuZXhlY3V0ZWAgbWV0aG9kLg==

3 comments :

  1. This comment has been removed by the author.

    ReplyDelete
  2. This comment has been removed by the author.

    ReplyDelete
  3. try <base URL>/idp/profile/SAML2/Unsolicited/SSO?providerId= < your ID as specified in metadata file for the Service Provider (should be in metadata subdirectory of your shibboleth installation)>

    ReplyDelete