Friday, August 20, 2010

Using JMeter to Test Shibboleth Login to uPortal

The following is a generified JMeter 2.4 test to use as a template so that you can test Shibboleth login to uPortal. This is based heavily on a script provided by Stan F. locally. Note that the last step in the process is to again get the uPortal Login page, as that actually logs you into uPortal, not just Shibboleth.

Save to a file like portal_test.jmx and open in JMeter. Then just edit the file and replace "portal.acme.edu" with your uPortal server, "my.shibboleth.idp.acme.edu" with your Shibboleth IdP, and the two "replace this..." strings with appropriate content. By default, it runs as a monitor (indefinitely), writing errors out as files into the bin directory of JMeter, but you can tweak it however you wish to make it a real load test, rather than a simple monitoring script.

(Note: at first I was going to use the Mailer Visualizer to send emails with info when it failed, but that action still seems fairly limited in 2.4.)

<?xml version="1.0" encoding="UTF-8"?>
<jmeterTestPlan version="1.2" properties="2.1">
  <hashTree>
    <TestPlan guiclass="TestPlanGui" testclass="TestPlan" testname="My Test Plan" enabled="true">
      <boolProp name="TestPlan.functional_mode">false</boolProp>
      <collectionProp name="TestPlan.thread_groups"/>
      <elementProp name="TestPlan.user_defined_variables" elementType="Arguments" guiclass="ArgumentsPanel" testclass="Arguments" testname="Argument List" enabled="true">
        <collectionProp name="Arguments.arguments"/>
      </elementProp>
      <boolProp name="TestPlan.serialize_threadgroups">false</boolProp>
      <stringProp name="TestPlan.comments"></stringProp>
      <stringProp name="TestPlan.user_define_classpath"></stringProp>
    </TestPlan>
    <hashTree>
      <ThreadGroup guiclass="ThreadGroupGui" testclass="ThreadGroup" testname="Thread Group" enabled="true">
        <elementProp name="ThreadGroup.main_controller" elementType="LoopController" guiclass="LoopControlPanel" testclass="LoopController" testname="Loop Controller" enabled="true">
          <boolProp name="LoopController.continue_forever">false</boolProp>
          <intProp name="LoopController.loops">-1</intProp>
        </elementProp>
        <stringProp name="ThreadGroup.num_threads">1</stringProp>
        <stringProp name="ThreadGroup.ramp_time">1</stringProp>
        <longProp name="ThreadGroup.start_time">1276016381000</longProp>
        <longProp name="ThreadGroup.end_time">1276016381000</longProp>
        <boolProp name="ThreadGroup.scheduler">false</boolProp>
        <stringProp name="ThreadGroup.on_sample_error">continue</stringProp>
        <stringProp name="ThreadGroup.duration"></stringProp>
        <stringProp name="ThreadGroup.delay"></stringProp>
      </ThreadGroup>
      <hashTree>
        <GaussianRandomTimer guiclass="GaussianRandomTimerGui" testclass="GaussianRandomTimer" testname="Gaussian Random Timer" enabled="true">
          <stringProp name="ConstantTimer.delay">300</stringProp>
          <stringProp name="RandomTimer.range">300</stringProp>
        </GaussianRandomTimer>
        <hashTree/>
        <CookieManager guiclass="CookiePanel" testclass="CookieManager" testname="HTTP Cookie Manager" enabled="true">
          <collectionProp name="CookieManager.cookies"/>
          <boolProp name="CookieManager.clearEachIteration">true</boolProp>
        </CookieManager>
        <hashTree/>
        <UserParameters guiclass="UserParametersGui" testclass="UserParameters" testname="User Parameters" enabled="true">
          <boolProp name="TestElement.per_iteration">true</boolProp>
          <collectionProp name="UserParameters.names">
            <stringProp name="VIEWSTATE">VIEWSTATE</stringProp>
            <stringProp name="jsessionid">jsessionid</stringProp>
          </collectionProp>
          <collectionProp name="UserParameters.thread_values">
            <collectionProp name="745697">
              <stringProp name=""></stringProp>
              <stringProp name=""></stringProp>
            </collectionProp>
          </collectionProp>
          <boolProp name="UserParameters.per_iteration">false</boolProp>
        </UserParameters>
        <hashTree/>
        <HeaderManager guiclass="HeaderPanel" testclass="HeaderManager" testname="HTTP Header Manager" enabled="true">
          <collectionProp name="HeaderManager.headers">
            <elementProp name="" elementType="Header">
              <stringProp name="Header.name">User-Agent</stringProp>
              <stringProp name="Header.value">Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.6; en-US; rv:1.9.2.3) Gecko/20100401 Firefox/3.6.3</stringProp>
            </elementProp>
            <elementProp name="" elementType="Header">
              <stringProp name="Header.name">Accept</stringProp>
              <stringProp name="Header.value">text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8</stringProp>
            </elementProp>
            <elementProp name="" elementType="Header">
              <stringProp name="Header.name">Accept-Language</stringProp>
              <stringProp name="Header.value">en-us,en;q=0.5</stringProp>
            </elementProp>
            <elementProp name="" elementType="Header">
              <stringProp name="Header.name">Accept-Encoding</stringProp>
              <stringProp name="Header.value">gzip,deflate</stringProp>
            </elementProp>
            <elementProp name="" elementType="Header">
              <stringProp name="Header.name">Accept-Charset</stringProp>
              <stringProp name="Header.value">ISO-8859-1,utf-8;q=0.7,*;q=0.7</stringProp>
            </elementProp>
          </collectionProp>
        </HeaderManager>
        <hashTree/>
        <LoopController guiclass="LoopControlPanel" testclass="LoopController" testname="Step 1" enabled="true">
          <boolProp name="LoopController.continue_forever">true</boolProp>
          <stringProp name="LoopController.loops">1</stringProp>
        </LoopController>
        <hashTree>
          <HTTPSampler guiclass="HttpTestSampleGui" testclass="HTTPSampler" testname="https://portal.acme.edu/uPortal" enabled="true">
            <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" testname="Argument List" enabled="true">
              <collectionProp name="Arguments.arguments"/>
            </elementProp>
            <stringProp name="HTTPSampler.domain">portal.acme.edu</stringProp>
            <stringProp name="HTTPSampler.port">443</stringProp>
            <stringProp name="HTTPSampler.connect_timeout"></stringProp>
            <stringProp name="HTTPSampler.response_timeout"></stringProp>
            <stringProp name="HTTPSampler.protocol">https</stringProp>
            <stringProp name="HTTPSampler.contentEncoding"></stringProp>
            <stringProp name="HTTPSampler.path">/uPortal</stringProp>
            <stringProp name="HTTPSampler.method">GET</stringProp>
            <boolProp name="HTTPSampler.follow_redirects">true</boolProp>
            <boolProp name="HTTPSampler.auto_redirects">false</boolProp>
            <boolProp name="HTTPSampler.use_keepalive">true</boolProp>
            <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp>
            <stringProp name="HTTPSampler.FILE_NAME"></stringProp>
            <stringProp name="HTTPSampler.FILE_FIELD"></stringProp>
            <stringProp name="HTTPSampler.mimetype"></stringProp>
            <boolProp name="HTTPSampler.monitor">false</boolProp>
            <stringProp name="HTTPSampler.embedded_url_re"></stringProp>
          </HTTPSampler>
          <hashTree/>
          <HTTPSampler guiclass="HttpTestSampleGui" testclass="HTTPSampler" testname="https://portal.acme.edu/uPortal/Login" enabled="true">
            <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" testname="Argument List" enabled="true">
              <collectionProp name="Arguments.arguments">
                <elementProp name="Login" elementType="HTTPArgument">
                  <stringProp name="HTTPArgument.always_encode">true</stringProp>
                  <stringProp name="Argument.value">Login</stringProp>
                  <stringProp name="Argument.name">Login</stringProp>
                  <stringProp name="Argument.metadata">=</stringProp>
                  <stringProp name="Argument.use_equals">true</stringProp>
                  <boolProp name="HTTPArgument.use_equals">true</boolProp>
                </elementProp>
              </collectionProp>
            </elementProp>
            <stringProp name="HTTPSampler.domain">portal.acme.edu</stringProp>
            <stringProp name="HTTPSampler.port">443</stringProp>
            <stringProp name="HTTPSampler.connect_timeout"></stringProp>
            <stringProp name="HTTPSampler.response_timeout"></stringProp>
            <stringProp name="HTTPSampler.protocol">https</stringProp>
            <stringProp name="HTTPSampler.contentEncoding"></stringProp>
            <stringProp name="HTTPSampler.path">/uPortal/Login</stringProp>
            <stringProp name="HTTPSampler.method">POST</stringProp>
            <boolProp name="HTTPSampler.follow_redirects">true</boolProp>
            <boolProp name="HTTPSampler.auto_redirects">false</boolProp>
            <boolProp name="HTTPSampler.use_keepalive">true</boolProp>
            <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp>
            <stringProp name="HTTPSampler.FILE_NAME"></stringProp>
            <stringProp name="HTTPSampler.FILE_FIELD"></stringProp>
            <stringProp name="HTTPSampler.mimetype"></stringProp>
            <boolProp name="HTTPSampler.monitor">false</boolProp>
            <stringProp name="HTTPSampler.embedded_url_re"></stringProp>
          </HTTPSampler>
          <hashTree/>
          <HTTPSampler guiclass="HttpTestSampleGui" testclass="HTTPSampler" testname="https://my.shibboleth.idp.acme.edu/idp/Authn/UserPassword" enabled="true">
            <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" testname="Argument List" enabled="true">
              <collectionProp name="Arguments.arguments">
                <elementProp name="j_username" elementType="HTTPArgument">
                  <stringProp name="HTTPArgument.always_encode">true</stringProp>
                  <stringProp name="Argument.value">some_user</stringProp>
                  <stringProp name="Argument.name">j_username</stringProp>
                  <stringProp name="Argument.metadata">=</stringProp>
                  <stringProp name="Argument.use_equals">true</stringProp>
                  <boolProp name="HTTPArgument.use_equals">true</boolProp>
                </elementProp>
                <elementProp name="j_password" elementType="HTTPArgument">
                  <stringProp name="HTTPArgument.always_encode">true</stringProp>
                  <stringProp name="Argument.value">some_password</stringProp>
                  <stringProp name="Argument.name">j_password</stringProp>
                  <stringProp name="Argument.metadata">=</stringProp>
                  <stringProp name="Argument.use_equals">true</stringProp>
                  <boolProp name="HTTPArgument.use_equals">true</boolProp>
                </elementProp>
                <elementProp name="Submit" elementType="HTTPArgument">
                  <stringProp name="HTTPArgument.always_encode">true</stringProp>
                  <stringProp name="Argument.value">Enter</stringProp>
                  <stringProp name="Argument.name">Submit</stringProp>
                  <stringProp name="Argument.metadata">=</stringProp>
                  <stringProp name="Argument.use_equals">true</stringProp>
                  <boolProp name="HTTPArgument.use_equals">true</boolProp>
                </elementProp>
              </collectionProp>
            </elementProp>
            <stringProp name="HTTPSampler.domain">my.shibboleth.idp.acme.edu</stringProp>
            <stringProp name="HTTPSampler.port">443</stringProp>
            <stringProp name="HTTPSampler.connect_timeout"></stringProp>
            <stringProp name="HTTPSampler.response_timeout"></stringProp>
            <stringProp name="HTTPSampler.protocol">https</stringProp>
            <stringProp name="HTTPSampler.contentEncoding"></stringProp>
            <stringProp name="HTTPSampler.path">/idp/Authn/UserPassword</stringProp>
            <stringProp name="HTTPSampler.method">POST</stringProp>
            <boolProp name="HTTPSampler.follow_redirects">true</boolProp>
            <boolProp name="HTTPSampler.auto_redirects">false</boolProp>
            <boolProp name="HTTPSampler.use_keepalive">true</boolProp>
            <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp>
            <stringProp name="HTTPSampler.FILE_NAME"></stringProp>
            <stringProp name="HTTPSampler.FILE_FIELD"></stringProp>
            <stringProp name="HTTPSampler.mimetype"></stringProp>
            <boolProp name="HTTPSampler.monitor">false</boolProp>
            <stringProp name="HTTPSampler.embedded_url_re"></stringProp>
          </HTTPSampler>
          <hashTree>
            <XPathExtractor guiclass="XPathExtractorGui" testclass="XPathExtractor" testname="XPath Extractor" enabled="true">
              <stringProp name="XPathExtractor.default"></stringProp>
              <stringProp name="XPathExtractor.refname">samlresponse</stringProp>
              <stringProp name="XPathExtractor.xpathQuery">/html//input[@name=&quot;SAMLResponse&quot;]/@value</stringProp>
              <boolProp name="XPathExtractor.tolerant">true</boolProp>
              <boolProp name="XPathExtractor.namespace">true</boolProp>
            </XPathExtractor>
            <hashTree/>
            <XPathExtractor guiclass="XPathExtractorGui" testclass="XPathExtractor" testname="XPath Extractor" enabled="true">
              <stringProp name="XPathExtractor.default"></stringProp>
              <stringProp name="XPathExtractor.refname">relaystate</stringProp>
              <stringProp name="XPathExtractor.xpathQuery">/html/input[@name=&quot;RelayState&quot;]/@value</stringProp>
              <boolProp name="XPathExtractor.tolerant">false</boolProp>
              <boolProp name="XPathExtractor.namespace">true</boolProp>
            </XPathExtractor>
            <hashTree/>
            <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true">
              <collectionProp name="Asserion.test_strings">
                <stringProp name="-274265185">press the Continue button</stringProp>
              </collectionProp>
              <stringProp name="Assertion.test_field">Assertion.response_data</stringProp>
              <boolProp name="Assertion.assume_success">false</boolProp>
              <intProp name="Assertion.test_type">2</intProp>
            </ResponseAssertion>
            <hashTree/>
          </hashTree>
          <HTTPSampler guiclass="HttpTestSampleGui" testclass="HTTPSampler" testname="https://portal.acme.edu/Shibboleth.sso/SAML2/POST" enabled="true">
            <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" testname="Argument List" enabled="true">
              <collectionProp name="Arguments.arguments">
                <elementProp name="RelayState" elementType="HTTPArgument">
                  <stringProp name="HTTPArgument.always_encode">true</stringProp>
                  <stringProp name="Argument.value">${relaystate}</stringProp>
                  <stringProp name="Argument.name">RelayState</stringProp>
                  <stringProp name="Argument.metadata">=</stringProp>
                  <stringProp name="Argument.use_equals">true</stringProp>
                  <boolProp name="HTTPArgument.use_equals">true</boolProp>
                </elementProp>
                <elementProp name="SAMLResponse" elementType="HTTPArgument">
                  <stringProp name="HTTPArgument.always_encode">true</stringProp>
                  <stringProp name="Argument.value">${samlresponse}</stringProp>
                  <stringProp name="Argument.name">SAMLResponse</stringProp>
                  <stringProp name="Argument.metadata">=</stringProp>
                  <stringProp name="Argument.use_equals">true</stringProp>
                  <boolProp name="HTTPArgument.use_equals">true</boolProp>
                </elementProp>
              </collectionProp>
            </elementProp>
            <stringProp name="HTTPSampler.domain">portal.acme.edu</stringProp>
            <stringProp name="HTTPSampler.port">443</stringProp>
            <stringProp name="HTTPSampler.connect_timeout"></stringProp>
            <stringProp name="HTTPSampler.response_timeout"></stringProp>
            <stringProp name="HTTPSampler.protocol">https</stringProp>
            <stringProp name="HTTPSampler.contentEncoding"></stringProp>
            <stringProp name="HTTPSampler.path">/Shibboleth.sso/SAML2/POST</stringProp>
            <stringProp name="HTTPSampler.method">POST</stringProp>
            <boolProp name="HTTPSampler.follow_redirects">false</boolProp>
            <boolProp name="HTTPSampler.auto_redirects">false</boolProp>
            <boolProp name="HTTPSampler.use_keepalive">true</boolProp>
            <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp>
            <stringProp name="HTTPSampler.FILE_NAME"></stringProp>
            <stringProp name="HTTPSampler.FILE_FIELD"></stringProp>
            <stringProp name="HTTPSampler.mimetype"></stringProp>
            <boolProp name="HTTPSampler.monitor">false</boolProp>
            <stringProp name="HTTPSampler.embedded_url_re"></stringProp>
          </HTTPSampler>
          <hashTree/>
          <HTTPSampler guiclass="HttpTestSampleGui" testclass="HTTPSampler" testname="https://portal.acme.edu/uPortal/Login" enabled="true">
            <elementProp name="HTTPsampler.Arguments" elementType="Arguments" guiclass="HTTPArgumentsPanel" testclass="Arguments" testname="Argument List" enabled="true">
              <collectionProp name="Arguments.arguments"/>
            </elementProp>
            <stringProp name="HTTPSampler.domain">portal.acme.edu</stringProp>
            <stringProp name="HTTPSampler.port">443</stringProp>
            <stringProp name="HTTPSampler.connect_timeout"></stringProp>
            <stringProp name="HTTPSampler.response_timeout"></stringProp>
            <stringProp name="HTTPSampler.protocol">https</stringProp>
            <stringProp name="HTTPSampler.contentEncoding"></stringProp>
            <stringProp name="HTTPSampler.path">/uPortal/Login</stringProp>
            <stringProp name="HTTPSampler.method">GET</stringProp>
            <boolProp name="HTTPSampler.follow_redirects">true</boolProp>
            <boolProp name="HTTPSampler.auto_redirects">false</boolProp>
            <boolProp name="HTTPSampler.use_keepalive">true</boolProp>
            <boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp>
            <stringProp name="HTTPSampler.FILE_NAME"></stringProp>
            <stringProp name="HTTPSampler.FILE_FIELD"></stringProp>
            <stringProp name="HTTPSampler.mimetype"></stringProp>
            <boolProp name="HTTPSampler.monitor">false</boolProp>
            <stringProp name="HTTPSampler.embedded_url_re"></stringProp>
          </HTTPSampler>
          <hashTree>
            <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true">
              <collectionProp name="Asserion.test_strings">
                <stringProp name="33350373">replace this with text that should be in a fully loaded page</stringProp>
              </collectionProp>
              <stringProp name="Assertion.test_field">Assertion.response_data</stringProp>
              <boolProp name="Assertion.assume_success">false</boolProp>
              <intProp name="Assertion.test_type">16</intProp>
            </ResponseAssertion>
            <hashTree/>
            <ResponseAssertion guiclass="AssertionGui" testclass="ResponseAssertion" testname="Response Assertion" enabled="true">
              <collectionProp name="Asserion.test_strings">
                <stringProp name="-75219048">replace this with text from a portlet that you would only see if you logged in</stringProp>
              </collectionProp>
              <stringProp name="Assertion.test_field">Assertion.response_data</stringProp>
              <boolProp name="Assertion.assume_success">false</boolProp>
              <intProp name="Assertion.test_type">2</intProp>
            </ResponseAssertion>
            <hashTree/>
          </hashTree>
          <UniformRandomTimer guiclass="UniformRandomTimerGui" testclass="UniformRandomTimer" testname="Uniform Random Timer" enabled="true">
            <stringProp name="ConstantTimer.delay">3000</stringProp>
            <stringProp name="RandomTimer.range">15000.0</stringProp>
          </UniformRandomTimer>
          <hashTree/>
        </hashTree>
        <ResultSaver guiclass="ResultSaverGui" testclass="ResultSaver" testname="Save Responses to a file" enabled="true">
          <stringProp name="FileSaver.filename">portal_error</stringProp>
          <boolProp name="FileSaver.errorsonly">true</boolProp>
          <boolProp name="FileSaver.skipautonumber">false</boolProp>
          <boolProp name="FileSaver.skipsuffix">false</boolProp>
          <boolProp name="FileSaver.successonly">false</boolProp>
        </ResultSaver>
        <hashTree/>
      </hashTree>
      <ResultCollector guiclass="ViewResultsFullVisualizer" testclass="ResultCollector" testname="View Results Tree" enabled="false">
        <boolProp name="ResultCollector.error_logging">false</boolProp>
        <objProp>
          <name>saveConfig</name>
          <value class="SampleSaveConfiguration">
            <time>true</time>
            <latency>true</latency>
            <timestamp>true</timestamp>
            <success>true</success>
            <label>true</label>
            <code>true</code>
            <message>true</message>
            <threadName>true</threadName>
            <dataType>true</dataType>
            <encoding>false</encoding>
            <assertions>true</assertions>
            <subresults>true</subresults>
            <responseData>false</responseData>
            <samplerData>false</samplerData>
            <xml>true</xml>
            <fieldNames>false</fieldNames>
            <responseHeaders>false</responseHeaders>
            <requestHeaders>false</requestHeaders>
            <responseDataOnError>false</responseDataOnError>
            <saveAssertionResultsFailureMessage>false</saveAssertionResultsFailureMessage>
            <assertionsResultsToSave>0</assertionsResultsToSave>
            <bytes>true</bytes>
          </value>
        </objProp>
        <stringProp name="filename"></stringProp>
      </ResultCollector>
      <hashTree/>
      <ResultCollector guiclass="SummaryReport" testclass="ResultCollector" testname="Summary Report" enabled="true">
        <boolProp name="ResultCollector.error_logging">false</boolProp>
        <objProp>
          <name>saveConfig</name>
          <value class="SampleSaveConfiguration">
            <time>true</time>
            <latency>true</latency>
            <timestamp>true</timestamp>
            <success>true</success>
            <label>true</label>
            <code>true</code>
            <message>true</message>
            <threadName>true</threadName>
            <dataType>true</dataType>
            <encoding>false</encoding>
            <assertions>true</assertions>
            <subresults>true</subresults>
            <responseData>false</responseData>
            <samplerData>false</samplerData>
            <xml>true</xml>
            <fieldNames>false</fieldNames>
            <responseHeaders>false</responseHeaders>
            <requestHeaders>false</requestHeaders>
            <responseDataOnError>false</responseDataOnError>
            <saveAssertionResultsFailureMessage>false</saveAssertionResultsFailureMessage>
            <assertionsResultsToSave>0</assertionsResultsToSave>
            <bytes>true</bytes>
          </value>
        </objProp>
        <stringProp name="filename"></stringProp>
      </ResultCollector>
      <hashTree/>
    </hashTree>
  </hashTree>
</jmeterTestPlan>

No comments: