Updated documentation
This commit is contained in:
296
doc/howto.rst
Normal file
296
doc/howto.rst
Normal file
@@ -0,0 +1,296 @@
|
||||
.. _howto:
|
||||
|
||||
How to use SAML2test
|
||||
====================
|
||||
|
||||
:Release: |release|
|
||||
:Date: |today|
|
||||
|
||||
Before you can use SAML2test, you must get it installed.
|
||||
If you have not done so yet, read :ref:`install`.
|
||||
|
||||
When you want to test a SAML2 entity with this tool you need 3 things:
|
||||
|
||||
* A configuration of the tool, an example can be found in tests/config_file.py
|
||||
* A metadata file representing the tool
|
||||
* A configuration file that describes how to interact with the entity.
|
||||
The metadata for the entity is part of this file. More about this below.
|
||||
|
||||
Tool configuration
|
||||
::::::::::::::::::
|
||||
|
||||
This is a normal PySAML2 configuration file. You can have more than one and
|
||||
then chose which one to use at run time by supplying the test script with
|
||||
an argument. If no configuration is explicitly chosen the default name is
|
||||
**config_file.py** .
|
||||
|
||||
Interaction configuration file
|
||||
::::::::::::::::::::::::::::::
|
||||
|
||||
The configuration is structured as a Python dictionary.
|
||||
The keys are **entity_id**, **interaction** and **metadata**.
|
||||
|
||||
entity_id
|
||||
.........
|
||||
|
||||
**entity_id** is really only necessary if there is more than one entity
|
||||
represented in the metadata. If not provided and if the **metadata** only
|
||||
describes one entity that entity's entityID is used.
|
||||
|
||||
interaction
|
||||
...........
|
||||
|
||||
The really hard part is the **interaction** part. This is where the
|
||||
the script is told how to fake that there is a human behind the keyboard.
|
||||
|
||||
It consists of a lists of dictionaries with the keys: **matches**,
|
||||
**page-type** and **control**.
|
||||
|
||||
matches
|
||||
-------
|
||||
|
||||
**matches** is used to identify a page or a form within a page.
|
||||
There are four different things that can be used to match the form:
|
||||
|
||||
* url : The action url
|
||||
* title : The title of the form, substring matching is used.
|
||||
* content: Something in the form, again substring matching is used, and finally
|
||||
* class:
|
||||
|
||||
Normally the front-end will pick out the necessary information by
|
||||
using a users interaction with the entity. If you are running this
|
||||
directly from the prompt then you have to provide the information.
|
||||
You can build this information by using the fact that the script will
|
||||
dump any page it doesn't know what to do with.
|
||||
|
||||
An example::
|
||||
|
||||
|
||||
{
|
||||
"matches": {
|
||||
"url": "http://localhost:8088/login",
|
||||
"title": 'IDP test login'
|
||||
},
|
||||
"page-type": "login",
|
||||
"control": {
|
||||
"type": "form",
|
||||
"set": {"login": "roland", "password": "dianakra"}
|
||||
}
|
||||
}
|
||||
|
||||
The action here is to set the control *login* to 'roland' and the control
|
||||
*password* to 'dianakra' and then post the form.
|
||||
|
||||
Or if the server uses HTTP Post binding::
|
||||
|
||||
{
|
||||
"matches": {
|
||||
"url": "http://localhost:8088/sso/redirect",
|
||||
"title": "SAML 2.0 POST"
|
||||
},
|
||||
"control": {
|
||||
"type": "response",
|
||||
"pick": {"form": {"action":"http://localhost:8088/acs"}}
|
||||
}
|
||||
},
|
||||
|
||||
Here the action is just to post the form, no information is added to the form.
|
||||
|
||||
page-type
|
||||
---------
|
||||
|
||||
**page-type** is used to mark the page as *login* or *user-consent*.
|
||||
This is used in specific conversation where one or the other is expected
|
||||
in certain circumstances.
|
||||
|
||||
control
|
||||
-------
|
||||
|
||||
**control** specifies what the script should enter where and which button
|
||||
to press.
|
||||
|
||||
metadata
|
||||
........
|
||||
|
||||
This is then the metadata for the entity to be tested. As noted previously
|
||||
the metadata can actually describe more than one entity. In this case
|
||||
the **entity_id** must be specified explicitly.
|
||||
|
||||
Running the script
|
||||
::::::::::::::::::
|
||||
|
||||
Script parameters::
|
||||
|
||||
$ saml2c.py --help
|
||||
usage: saml2c.py [-h] [-d] [-v] [-C CA_CERTS] [-J JSON_CONFIG_FILE] [-m] [-l]
|
||||
[-c SPCONFIG]
|
||||
[oper]
|
||||
|
||||
positional arguments:
|
||||
oper Which test to run
|
||||
|
||||
optional arguments:
|
||||
-h, --help show this help message and exit
|
||||
-d Print debug information
|
||||
-v Print runtime information
|
||||
-C CA_CERTS CA certs to use to verify HTTPS server certificates, if
|
||||
HTTPS is used and no server CA certs are defined then
|
||||
no cert verification will be done
|
||||
-J JSON_CONFIG_FILE Script configuration
|
||||
-m Return the SP metadata
|
||||
-l List all the test flows as a JSON object
|
||||
-c SPCONFIG Configuration file for the SP
|
||||
|
||||
|
||||
To see what tests are available::
|
||||
|
||||
$ saml2c.py -l
|
||||
[
|
||||
{
|
||||
"id": "basic-authn",
|
||||
"descr": "AuthnRequest using HTTP-redirect",
|
||||
"name": "Absolute basic SAML2 AuthnRequest"
|
||||
}, {
|
||||
"id": "basic-authn-post",
|
||||
"descr": "AuthnRequest using HTTP-POST",
|
||||
"name": "Basic SAML2 AuthnRequest using HTTP POST"
|
||||
}, {
|
||||
"id": "log-in-out",
|
||||
"descr": "AuthnRequest using HTTP-redirect followed by a logout",
|
||||
"name": "Absolute basic SAML2 log in and out"
|
||||
}, {
|
||||
"id": "authn-assertion_id_request",
|
||||
"descr": "AuthnRequest followed by an AssertionIDRequest",
|
||||
"name": "AuthnRequest and then an AssertionIDRequest"
|
||||
}, {
|
||||
"id": "authn-authn_query",
|
||||
"descr": "AuthnRequest followed by an AuthnQuery",
|
||||
"name": "AuthnRequest and then an AuthnQuery"
|
||||
}
|
||||
]
|
||||
|
||||
A typical command would then be (reformated to be more readable)::
|
||||
|
||||
$ saml2c.py -J localhost.json 'log-in-out'
|
||||
{
|
||||
"status": 1,
|
||||
"tests": [
|
||||
{
|
||||
"status": 1,
|
||||
"id": "check-saml2int-metadata",
|
||||
"name": "Checks that the Metadata follows the profile"
|
||||
}, {
|
||||
"status": 1,
|
||||
"id": "check-http-response",
|
||||
"name": "Checks that the HTTP response status is within the 200 or 300 range"
|
||||
}, {
|
||||
"status": 1,
|
||||
"id": "check-http-response",
|
||||
"name": "Checks that the HTTP response status is within the 200 or 300 range"
|
||||
}, {
|
||||
"status": 1,
|
||||
"id": "check-http-response",
|
||||
"name": "Checks that the HTTP response status is within the 200 or 300 range"
|
||||
}, {
|
||||
"status": 1,
|
||||
"id": "check-saml2int-attributes",
|
||||
"name": "Any <saml2:Attribute> elements exchanged via any SAML 2.0 messages, assertions, or metadata MUST contain a NameFormat of urn:oasis:names:tc:SAML:2.0:attrname-format:uri."
|
||||
}, {
|
||||
"status": 1,
|
||||
"id": "verify-content",
|
||||
"name": "Basic content verification class, does required and max/min checks"
|
||||
}, {
|
||||
"status": 1,
|
||||
"id": "check-logout-support",
|
||||
"name": ""
|
||||
}, {
|
||||
"status": 1,
|
||||
"id": "verify-content",
|
||||
"name": "Basic content verification class, does required and max/min checks"
|
||||
}, {
|
||||
"status": 1,
|
||||
"id": "verify-logout",
|
||||
"name": ""
|
||||
}
|
||||
],
|
||||
"id": "log-in-out"
|
||||
}
|
||||
|
||||
First you have the status for the whole test was '1', which is the same as OK,
|
||||
for this test run.
|
||||
The used status code are:
|
||||
|
||||
0. INFORMATION
|
||||
1. OK
|
||||
2. WARNING
|
||||
3. ERROR
|
||||
4. CRITICAL
|
||||
5. INTERACTION
|
||||
|
||||
Then you get all the separate sub tests that has been run during the
|
||||
conversation.
|
||||
|
||||
If things go wrong you will get a trace log dump to stderr.
|
||||
If all goes well but you still want to see all the interaction you can do::
|
||||
|
||||
$ saml2c.py -J localhost.json -d 'basic-authn' 2> tracelog
|
||||
< same output as above >
|
||||
$ cat tracelog
|
||||
0.017364 SAML Request: <?xml version='1.0' encoding='UTF-8'?>
|
||||
<ns0:AuthnRequest xmlns:ns0="urn:oasis:names:tc:SAML:2.0:protocol" xmlns:ns1="urn:oasis:names:tc:SAML:2.0:assertion" AssertionConsumerServiceURL="http://localhost:8087/acs/redirect" Destination="http://localhost:8088/sso/redirect" ID="id-8c9a57670d1bc374898297702285ba74" IssueInstant="2013-01-20T09:02:44Z" ProtocolBinding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" ProviderName="SAML2 test tool" Version="2.0"><ns1:Issuer Format="urn:oasis:names:tc:SAML:2.0:nameid-format:entity">http://localhost:8087/sp.xml</ns1:Issuer><ns0:NameIDPolicy AllowCreate="true" Format="urn:oasis:names:tc:SAML:2.0:nameid-format:persistent" /></ns0:AuthnRequest>
|
||||
0.036136 <-- REDIRECT TO: http://localhost:8088/login?came_from=%2Fsso%2Fredirect&key=331035cf0e26cdefc15759582e34994ac8e54971
|
||||
0.040084 <-- CONTENT:
|
||||
|
||||
|
||||
<html>
|
||||
<head><title>IDP test login</title>
|
||||
<link rel="stylesheet" type="text/css" href="/css/main.css" media="screen">
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||
</head>
|
||||
<body>
|
||||
<div class="header">
|
||||
<h1><a href="/">Login</a></h1>
|
||||
</div>
|
||||
|
||||
|
||||
<h1>Please log in</h1>
|
||||
<p class="description">
|
||||
To register it's quite simple: enter a login and a password
|
||||
</p>
|
||||
|
||||
<form action="/verify" method="post">
|
||||
<input type="hidden" name="key" value="331035cf0e26cdefc15759582e34994ac8e54971"/>
|
||||
<input type="hidden" name="came_from" value="/sso/redirect"/>
|
||||
|
||||
<div class="label">
|
||||
<label for="login">Username</label>
|
||||
</div>
|
||||
<div>
|
||||
<input type="text" name="login" value=""/><br/>
|
||||
</div>
|
||||
|
||||
<div class="label">
|
||||
<label for="password">Password</label>
|
||||
</div>
|
||||
<div>
|
||||
<input type="password" name="password"
|
||||
value=""/>
|
||||
</div>
|
||||
|
||||
<input class="submit" type="submit" name="form.submitted" value="Log In"/>
|
||||
</form>
|
||||
|
||||
<div>
|
||||
<div class="footer">
|
||||
<p>© Copyright 2011 Umeå Universitet </p>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
0.042697 >> login <<
|
||||
0.042715 <-- FUNCTION: select_form
|
||||
0.042744 <-- ARGS: {u'set': {u'login': u'roland', u'password': u'dianakra'}, u'type': u'form', 'location': 'http://localhost:8088/login?came_from=%2Fsso%2Fredirect&key=331035cf0e26cdefc15759582e34994ac8e54971', '_trace_': <idp_test.Trace object at 0x101e79750>, 'features': None}
|
||||
0.055864 <-- REDIRECT TO: http://localhost:8088/sso/redirect?id=zLvrjojPLLgbnDyq&key=331035cf0e26cdefc15759582e34994ac8e54971
|
||||
|
||||
... and so on ...
|
@@ -1,3 +1,5 @@
|
||||
.. _index:
|
||||
|
||||
.. SAML2test documentation master file, created by
|
||||
sphinx-quickstart on Sat Jan 19 11:38:19 2013.
|
||||
You can adapt this file completely to your liking, but it should at least
|
||||
@@ -6,12 +8,16 @@
|
||||
Welcome to SAML2test's documentation!
|
||||
=====================================
|
||||
|
||||
:Release: |release|
|
||||
:Date: |today|
|
||||
|
||||
Contents:
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
:maxdepth: 1
|
||||
|
||||
howto
|
||||
install
|
||||
|
||||
Indices and tables
|
||||
==================
|
||||
|
32
doc/install.rst
Normal file
32
doc/install.rst
Normal file
@@ -0,0 +1,32 @@
|
||||
.. _install:
|
||||
|
||||
Quick install guide
|
||||
===================
|
||||
|
||||
Before you can use SAML2test, you'll need to get it installed. This guide
|
||||
will guide you to a simple, minimal installation.
|
||||
|
||||
Install SAML2test
|
||||
-----------------
|
||||
|
||||
For all this to work you need to have Python installed.
|
||||
The development has been done using 2.7.2.
|
||||
There is no 3.X version yet.
|
||||
|
||||
Prerequisites
|
||||
^^^^^^^^^^^^^
|
||||
|
||||
The big dependency is on pysaml2, which you for the time being must
|
||||
get from github::
|
||||
|
||||
$ git clone https://github.com/rohe/pysaml2.git
|
||||
|
||||
Quick build instructions
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Once you have installed all the necessary prerequisites a simple::
|
||||
|
||||
python setup.py install
|
||||
|
||||
will install the basic code.
|
||||
|
@@ -105,9 +105,6 @@ class SAML2client(object):
|
||||
help="CA certs to use to verify HTTPS server certificates, if HTTPS is used and no server CA certs are defined then no cert verification will be done")
|
||||
self._parser.add_argument('-J', dest="json_config_file",
|
||||
help="Script configuration")
|
||||
self._parser.add_argument('-S', dest="sp_id", help="SP id")
|
||||
self._parser.add_argument("-s", dest="list_sp_id", action="store_true",
|
||||
help="List all the SP variants as a JSON object")
|
||||
self._parser.add_argument('-m', dest="metadata", action='store_true',
|
||||
help="Return the SP metadata")
|
||||
self._parser.add_argument("-l", dest="list", action="store_true",
|
||||
@@ -129,14 +126,7 @@ class SAML2client(object):
|
||||
def sp_configure(self, metadata_construction=False):
|
||||
sys.path.insert(0, ".")
|
||||
mod = import_module(self.args.spconfig)
|
||||
if self.args.sp_id is None:
|
||||
if len(mod.CONFIG) == 1:
|
||||
self.args.sp_id = mod.CONFIG.keys()[0]
|
||||
else:
|
||||
raise Exception("SP id undefined")
|
||||
|
||||
self.sp_config = SPConfig().load(mod.CONFIG[self.args.sp_id],
|
||||
metadata_construction)
|
||||
self.sp_config = SPConfig().load(mod.CONFIG, metadata_construction)
|
||||
|
||||
def setup(self):
|
||||
self.json_config= self.json_config_file()
|
||||
@@ -144,7 +134,6 @@ class SAML2client(object):
|
||||
_jc = self.json_config
|
||||
|
||||
self.interactions = _jc["interaction"]
|
||||
self.entity_id = _jc["entity_id"]
|
||||
|
||||
self.sp_configure()
|
||||
|
||||
@@ -156,6 +145,16 @@ class SAML2client(object):
|
||||
metadata[0] = md
|
||||
self.sp_config.metadata = metadata
|
||||
|
||||
try:
|
||||
self.entity_id = _jc["entity_id"]
|
||||
# Verify its the correct metadata
|
||||
assert self.entity_id in md.entity.keys()
|
||||
except KeyError:
|
||||
if len(md.entity.keys()) == 1:
|
||||
self.entity_id = md.entity.keys()[0]
|
||||
else:
|
||||
raise Exception("Don't know which entity to talk to")
|
||||
|
||||
def test_summation(self, id):
|
||||
status = 0
|
||||
for item in self.test_log:
|
||||
@@ -182,8 +181,6 @@ class SAML2client(object):
|
||||
|
||||
if self.args.metadata:
|
||||
return self.make_meta()
|
||||
elif self.args.list_sp_id:
|
||||
return self.list_conf_id()
|
||||
elif self.args.list:
|
||||
return self.list_operations()
|
||||
else:
|
||||
|
@@ -328,6 +328,9 @@ class CheckSubjectNameIDFormat(Check):
|
||||
return res
|
||||
|
||||
class CheckLogoutSupport(Check):
|
||||
"""
|
||||
Verifies that the tested entity supports single log out
|
||||
"""
|
||||
id = "check-logout-support"
|
||||
msg = "Does not support logout"
|
||||
|
||||
|
@@ -113,7 +113,7 @@ OPERATIONS = {
|
||||
"sequence": [AuthnRequestPost],
|
||||
},
|
||||
'log-in-out': {
|
||||
"name": 'Absolute basic SAML2 AuthnRequest',
|
||||
"name": 'Absolute basic SAML2 log in and out',
|
||||
"descr": ('AuthnRequest using HTTP-redirect followed by a logout'),
|
||||
"sequence": [AuthnRequest, LogOutRequest],
|
||||
},
|
||||
|
@@ -13,58 +13,55 @@ except Exception:
|
||||
#BASE = "http://lingon.ladok.umu.se:8087"
|
||||
BASE = "http://localhost:8087"
|
||||
|
||||
_CONFIG = {
|
||||
"entityid" : "%s/sp.xml" % BASE,
|
||||
"name" : "SAML2 test tool",
|
||||
"description": "Simplest possible",
|
||||
"service": {
|
||||
"sp": {
|
||||
"endpoints":{
|
||||
"assertion_consumer_service": [
|
||||
("%s/acs/post" % BASE, BINDING_HTTP_POST),
|
||||
("%s/acs/redirect" % BASE, BINDING_HTTP_REDIRECT),
|
||||
("%s/acs/artifact" % BASE, BINDING_HTTP_ARTIFACT),
|
||||
("%s/ecp" % BASE, BINDING_PAOS)
|
||||
],
|
||||
"single_logout_service": [
|
||||
("%s/sls" % BASE, BINDING_SOAP)
|
||||
],
|
||||
"artifact_resolution_service":[
|
||||
("%s/ars" % BASE, BINDING_SOAP)
|
||||
],
|
||||
"manage_name_id_service":[
|
||||
("%s/mni" % BASE, BINDING_HTTP_POST),
|
||||
("%s/mni" % BASE, BINDING_HTTP_REDIRECT),
|
||||
("%s/mni" % BASE, BINDING_SOAP),
|
||||
("%s/acs/artifact" % BASE, BINDING_HTTP_ARTIFACT)
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"key_file" : "keys/mykey.pem",
|
||||
"cert_file" : "keys/mycert.pem",
|
||||
"xmlsec_binary" : XMLSEC_BINARY,
|
||||
"subject_data": "subject_data.db",
|
||||
"accepted_time_diff": 60,
|
||||
"attribute_map_dir" : "attributemaps",
|
||||
"organization": {
|
||||
"name": ("AB Exempel", "se"),
|
||||
"display_name": ("AB Exempel", "se"),
|
||||
"url": "http://www.example.org",
|
||||
},
|
||||
"contact_person": [{
|
||||
"given_name": "Roland",
|
||||
"sur_name": "Hedberg",
|
||||
"telephone_number": "+46 70 100 0000",
|
||||
"email_address": ["tech@eample.com",
|
||||
"tech@example.org"],
|
||||
"contact_type": "technical"
|
||||
},
|
||||
],
|
||||
"secret": "0123456789",
|
||||
"only_use_keys_in_metadata": False
|
||||
}
|
||||
|
||||
CONFIG = {
|
||||
"1": _CONFIG
|
||||
"entityid" : "%s/sp.xml" % BASE,
|
||||
"name" : "SAML2 test tool",
|
||||
"description": "Simplest possible",
|
||||
"service": {
|
||||
"sp": {
|
||||
"endpoints":{
|
||||
"assertion_consumer_service": [
|
||||
("%s/acs/post" % BASE, BINDING_HTTP_POST),
|
||||
("%s/acs/redirect" % BASE, BINDING_HTTP_REDIRECT),
|
||||
("%s/acs/artifact" % BASE, BINDING_HTTP_ARTIFACT),
|
||||
("%s/ecp" % BASE, BINDING_PAOS)
|
||||
],
|
||||
"single_logout_service": [
|
||||
("%s/sls" % BASE, BINDING_SOAP)
|
||||
],
|
||||
"artifact_resolution_service":[
|
||||
("%s/ars" % BASE, BINDING_SOAP)
|
||||
],
|
||||
"manage_name_id_service":[
|
||||
("%s/mni" % BASE, BINDING_HTTP_POST),
|
||||
("%s/mni" % BASE, BINDING_HTTP_REDIRECT),
|
||||
("%s/mni" % BASE, BINDING_SOAP),
|
||||
("%s/acs/artifact" % BASE, BINDING_HTTP_ARTIFACT)
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"key_file" : "keys/mykey.pem",
|
||||
"cert_file" : "keys/mycert.pem",
|
||||
"xmlsec_binary" : XMLSEC_BINARY,
|
||||
"subject_data": "subject_data.db",
|
||||
"accepted_time_diff": 60,
|
||||
"attribute_map_dir" : "attributemaps",
|
||||
"organization": {
|
||||
"name": ("AB Exempel", "se"),
|
||||
"display_name": ("AB Exempel", "se"),
|
||||
"url": "http://www.example.org",
|
||||
},
|
||||
"contact_person": [{
|
||||
"given_name": "Roland",
|
||||
"sur_name": "Hedberg",
|
||||
"telephone_number": "+46 70 100 0000",
|
||||
"email_address": ["tech@eample.com",
|
||||
"tech@example.org"],
|
||||
"contact_type": "technical"
|
||||
},
|
||||
],
|
||||
"secret": "0123456789",
|
||||
"only_use_keys_in_metadata": False
|
||||
}
|
||||
|
||||
|
@@ -9,7 +9,6 @@ metadata = open("./idp/idp.xml").read()
|
||||
|
||||
info = {
|
||||
"entity_id": "%s/idp.xml" % BASE,
|
||||
"sp_config": "sp_local_conf.py",
|
||||
"interaction": [
|
||||
{
|
||||
"matches": {
|
||||
|
Reference in New Issue
Block a user