From 1904053e416f9518787d51e0e7582ba37bcc99e9 Mon Sep 17 00:00:00 2001 From: roland Date: Sun, 18 Dec 2016 16:15:49 +0100 Subject: [PATCH 1/6] Fixed profile info --- test_tool/test_rp/rplib/flows.yaml | 32 +++++++++++++----------------- 1 file changed, 14 insertions(+), 18 deletions(-) diff --git a/test_tool/test_rp/rplib/flows.yaml b/test_tool/test_rp/rplib/flows.yaml index 8f69a779..52155ced 100644 --- a/test_tool/test_rp/rplib/flows.yaml +++ b/test_tool/test_rp/rplib/flows.yaml @@ -42,7 +42,7 @@ Flows: email: {essential: true} - AccessToken: conditional_execution: - profile: 'C' + profile: C,CT,CI,CIT assert: got_id_token_claims: claims: @@ -144,7 +144,7 @@ Flows: response_type: - - code - - mode-form_post + - id_token - token - - id_token @@ -154,13 +154,10 @@ Flows: - - code - id_token - - - - code - - token exception: NotForMe - AccessToken: conditional_execution: - profile: 'C' + profile: 'C,CT' expect_exception: NotForMe rp-id_token-bad-sig-rs256: desc: Tests if the Relying Party can identify and reject an ID Token with an invalid @@ -191,13 +188,10 @@ Flows: - - code - id_token - - - - code - - token exception: BadSignature - AccessToken: conditional_execution: - profile: 'C' + profile: 'C, CT' expect_exception: BadSignature rp-id_token-bad-sig-es256: desc: The Relying Party should reject invalid asymmetric ID Token signature which @@ -323,11 +317,11 @@ Flows: - - code - id_token - exception: PyoidcError + exception: ParameterError - AccessToken: conditional_execution: profile: 'C, CT' - expect_exception: PyoidcError + expect_exception: ParameterError rp-id_token-kid-absent-single-jwks: desc: If the JWK supplied in jwks_uri only contains a single key the ID Token does not need to contain a kid claim @@ -600,15 +594,17 @@ Flows: condition: response_type: - - - code - id_token - - token - - id_token - token - + - code - id_token + - token + - - code + - id_token exception: ParameterError - AccessToken: conditional_execution: @@ -727,7 +723,7 @@ Flows: got: what: - id_token - - token + - access_token where: AuthorizationResponse rp-response_type-code: desc: Can Make Request with 'code' Response Type @@ -777,7 +773,7 @@ Flows: got: what: - id_token - - token + - access_token where: AuthorizationResponse rp-response_type-code+id_token+token: desc: Can Make Request with 'id_token token' Response Type @@ -795,7 +791,7 @@ Flows: got: what: - id_token - - token + - access_token - code where: AuthorizationResponse rp-response_type-code+id_token: @@ -831,7 +827,7 @@ Flows: assert: got: what: - - token + - access_token - code where: AuthorizationResponse rp-scope-userinfo-claims: From c44bb25d8e0c1699160e91de578faa52a27e75f2 Mon Sep 17 00:00:00 2001 From: roland Date: Sun, 18 Dec 2016 16:17:57 +0100 Subject: [PATCH 2/6] Return state instead of succeed/failed --- src/oidctest/tool.py | 3 ++- test_tool/test_rp/rplib/cl/clrp.py | 25 ++++++++++++++----------- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/src/oidctest/tool.py b/src/oidctest/tool.py index f6885629..5fed2f3d 100644 --- a/src/oidctest/tool.py +++ b/src/oidctest/tool.py @@ -4,6 +4,7 @@ from otest import exception_trace from otest.aus import tool +from otest.check import NOT_APPLICABLE from otest.conversation import Conversation from oidctest import prof_util @@ -40,7 +41,7 @@ def match_profile(self, test_id): def run(self, test_id, **kw_args): if not self.match_profile(test_id): logger.info("Test doesn't match the profile") - return False + return NOT_APPLICABLE redirs = get_redirect_uris(kw_args['client_info']) diff --git a/test_tool/test_rp/rplib/cl/clrp.py b/test_tool/test_rp/rplib/cl/clrp.py index ee3c4667..b54cee2d 100755 --- a/test_tool/test_rp/rplib/cl/clrp.py +++ b/test_tool/test_rp/rplib/cl/clrp.py @@ -16,7 +16,7 @@ from otest.parse_cnf import parse_yaml_conf from otest.common import setup_logger from otest.io import ClIO -from otest.result import Result +from otest.result import Result, SIGN from oidctest.op import func from oidctest.op import check @@ -58,7 +58,7 @@ def run_return_types(test_id, oper_id, kwargs, return_types): sh = SessionHandler(**kwargs) sh.init_session(profile=rtyp) - #res = Result(sh, SimpleProfileHandler) + # res = Result(sh, SimpleProfileHandler) io = ClIO(**kwargs) io.session = sh @@ -66,10 +66,12 @@ def run_return_types(test_id, oper_id, kwargs, return_types): tester = ClTester(io, sh, **kwargs) if single: - if tester.run(test_id, **kwargs): - print('+ {}{}'.format(return_types, test_id)) - else: - print('- {}{}'.format(return_types, test_id)) + _res = tester.run(test_id, **kwargs) + try: + print('{} {}{}'.format(SIGN[_res], return_types, test_id)) + except Exception as err: + print('****'+test_id+'*****') + raise # res.store_test_info() # res.write_info(test_id) return True @@ -96,7 +98,7 @@ def run_return_types(test_id, oper_id, kwargs, return_types): parser.add_argument('-k', dest="insecure", action='store_true') parser.add_argument('-l', dest="log_name") parser.add_argument('-t', dest="test_id") - parser.add_argument('-p', dest="profile") + parser.add_argument('-p', dest="profile", action='append') parser.add_argument('-i', dest="id") parser.add_argument('-g', dest="group") parser.add_argument('-x', dest='exit', action='store_true') @@ -151,11 +153,12 @@ def run_return_types(test_id, oper_id, kwargs, return_types): exit() if cargs.profile: - if cargs.profile not in rtypes: + _rt = set(rtypes).intersection(set(cargs.profile)) + if not _rt: print('Profile not among return_types') exit() else: - rtypes = [cargs.profile] + rtypes = list(_rt) if len(rtypes) == 1: run_return_types(cargs.test_id, cargs.id, kwargs, rtypes) @@ -165,9 +168,9 @@ def run_return_types(test_id, oper_id, kwargs, return_types): exit() else: if cargs.profile: - rtypes = [cargs.profile] + rtypes = cargs.profile else: - rtypes = ['C'] + rtypes = PROFILES _sh = SessionHandler(**kwargs) _sh.init_session(profile=rtypes[0]) From c512bd0d2d3456c06d96cbb8b4e0635ce29ae3ee Mon Sep 17 00:00:00 2001 From: roland Date: Sun, 18 Dec 2016 16:18:20 +0100 Subject: [PATCH 3/6] More specific error --- src/oidctest/endpoints.py | 6 +++++- src/oidctest/op/oper.py | 9 ++++----- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/oidctest/endpoints.py b/src/oidctest/endpoints.py index 74b07f95..b815e2b6 100644 --- a/src/oidctest/endpoints.py +++ b/src/oidctest/endpoints.py @@ -113,7 +113,11 @@ def wsgi_wrapper(environ, start_response, func, session_info, events, jlog): return resp(environ, start_response) except TypeError: resp = args - jlog.info({'response_from': func.__name__, 'response': resp2json(resp)}) + try: + jlog.info({'response_from': func.__name__, + 'response': resp2json(resp)}) + except Exception: + pass events.store(EV_RESPONSE, resp.message) dump_log(session_info, events) return resp(environ, start_response) diff --git a/src/oidctest/op/oper.py b/src/oidctest/op/oper.py index eb22b047..b1a34f11 100644 --- a/src/oidctest/op/oper.py +++ b/src/oidctest/op/oper.py @@ -5,7 +5,6 @@ import sys import time -import copy from Cryptodome.PublicKey import RSA from future.backports.urllib.parse import urlparse @@ -15,7 +14,7 @@ from oic import rndstr from oic.exception import IssuerMismatch -from oic.exception import PyoidcError +from oic.exception import ParameterError from oic.oauth2.message import ErrorResponse from oic.oauth2.util import JSON_ENCODED from oic.oic import ProviderConfigurationResponse @@ -94,7 +93,7 @@ def run(self): _conv.entity.wf.events = _conv.entity.events self.catch_exception(_conv.entity.discover, principal=principal) - if not _conv.events.last_event_type() == EV_EXCEPTION: + if not _conv.events.get(EV_EXCEPTION): issuer = _conv.events.last_item(EV_RESPONSE) _conv.info["issuer"] = issuer _conv.events.store('issuer', issuer, @@ -246,11 +245,11 @@ def _run(self): if _jws_alg == "none": pass elif "kid" not in atr[ - "id_token"].jws_header and not _jws_alg == "HS256": + "id_token"].jws_header and not _jws_alg == "HS256": keys = self.conv.entity.keyjar.keys_by_alg_and_usage( self.conv.info["issuer"], _jws_alg, "ver") if len(keys) > 1: - raise PyoidcError("No 'kid' in id_token header!") + raise ParameterError("No 'kid' in id_token header!") if not same_issuer(self.conv.info["issuer"], atr["id_token"]["iss"]): raise IssuerMismatch(" {} != {}".format(self.conv.info["issuer"], From 1554ddbb08684af826da1eb804675c26e6e82aae Mon Sep 17 00:00:00 2001 From: roland Date: Sun, 18 Dec 2016 16:37:50 +0100 Subject: [PATCH 4/6] Updated dependencies --- setup.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index 84785f52..cae69797 100644 --- a/setup.py +++ b/setup.py @@ -45,8 +45,8 @@ "requests >= 2.0.0", 'future', 'CherryPy', - 'oic >= 0.9.1', - 'otest >= 0.6.2' + 'oic >= 0.9.4', + 'otest >= 0.6.3' ], zip_safe=False, scripts=['script/optest.py', 'script/make_test_dir.py', From 5814a89d32a61ace24725ed09d3e5605902b9f34 Mon Sep 17 00:00:00 2001 From: roland Date: Mon, 19 Dec 2016 15:22:27 +0100 Subject: [PATCH 5/6] Updated docs --- docs/OIDCtesting/OPtest/index.rst | 2 +- docs/OIDCtesting/OPtest/overview.rst | 52 ++++++++++++++-------------- docs/OIDCtesting/OPtest/web.rst | 34 +++++++++++------- 3 files changed, 48 insertions(+), 40 deletions(-) diff --git a/docs/OIDCtesting/OPtest/index.rst b/docs/OIDCtesting/OPtest/index.rst index 14db628f..e269159f 100644 --- a/docs/OIDCtesting/OPtest/index.rst +++ b/docs/OIDCtesting/OPtest/index.rst @@ -3,7 +3,7 @@ How to run OP testing using OIDCtest ==================================== -This tool is for testing an OpenID Connect Provider instance compliance +This tool is for testing an OpenID Connect Provider instances compliance with the standard. :Release: |release| diff --git a/docs/OIDCtesting/OPtest/overview.rst b/docs/OIDCtesting/OPtest/overview.rst index 41104577..e1e6eff4 100644 --- a/docs/OIDCtesting/OPtest/overview.rst +++ b/docs/OIDCtesting/OPtest/overview.rst @@ -4,7 +4,7 @@ An overview of the OP test tool =============================== A basic assumption for the tool is that when you want to test an OpenID -Connect Provider (OP) you may want test one specific aspect a time. +Connect Provider (OP) you may want test one specific aspect at a time. You can therefor make several configurations per OP. It is for instance common to have one configuration per response_type. Following on that you will run one test instance per configuration. @@ -55,24 +55,24 @@ This is the overall pattern:: -h/--help -~~~~~~~~~ +::::::::: -Will print the usage description as show above +Will print the usage description as shown above -k -~~ +:: If nothing else is said the tool will try to verify the certificates used in the HTTPS connection. This will not work if the OP uses self-signed certificates. Hence, the *-f* flag will turn of certification verification. -i -~~ +:: The Issuer identifier of the OP. -f -~~ +:: .. _tt_opt_flow: @@ -81,13 +81,13 @@ manner. If you want to understand more about the test descriptions you can read more about them in :ref:`Test description language`. -p -~~ +:: Which port the test instance should listen on. Each test instance **MUST** have their own port. -M -~~ +:: .. _tt_opt_mako: @@ -97,7 +97,7 @@ to be in the directory from which optest.py is run. If that is not the case you have to give the path here. -S -~~ +:: There are a bunch of static files that the tool must be able to access. These are all the javascirpt files, the png, gif, css files. If nothing @@ -105,20 +105,20 @@ is specified they are expected to be in a directory named 'static' in the directory from which optest.py is run. -s -~~ +:: If the test instance should use HTTPS then set this flag. If so the configuration file must contain specifications of there the certificate and key files are. -t -~~ +:: If you have several configurations for one and the same OP then you can set a name each one of them, this is the *tag*. -m -~~ +:: .. _path2port: @@ -143,7 +143,7 @@ internal port:: and so on. config -~~~~~~ +:::::: .. _tt_config: @@ -192,7 +192,7 @@ more. ENT_PATH ________ -A path to where the test configurations are stored. The confirations are +A path to where the test configurations are stored. The configurations are stored in a tree of the form / like this:: https%3A%2F%2Fexample.com --+-- code @@ -225,7 +225,7 @@ probably non-standard port. Or it can be run behind a `reverse proxy`_ which then converts a external path to an internal port. Stand alone -~~~~~~~~~~~ +::::::::::: Here the test tool is configured to listen to a specific port. It can be any port but common is that it's not one of the system ports. @@ -238,41 +238,41 @@ software to do HTTPS. If for some reason there are problems with verifying the certificates used by the OP, the -k flag kan be use to turn off certificate verification. -Very simple example where there is a flows.yaml file and a configuration +Very simple command example where there is a flows.yaml file and a configuration file named 'config' :: - optest.py -s -f flows.yaml config + optest.py -p 9000 -s -f flows.yaml config Reverse proxy setup -~~~~~~~~~~~~~~~~~~~ +::::::::::::::::::: -If a reverse proxy is in place then the there will be an external URL +If a reverse proxy is used then the there will be an external URL that the RP is known as to the outside but also and internal URL which is only used between the proxy and the test tool. -An example could be that the external URL would be: +An example could be that the external URL is: https://example.com/optest/op1 -while the internal URL would be: +while the internal URL is: http://localhost:8666/ To accomplish this a couple of things has to happen. If you are running -an Apache server as your reverse proxy you can find a desciption of the +an Apache server as your reverse proxy you can find a description of the necessary steps on the `apache reverse proxy`_ page. -You probably want to preconfigure a list of path-to-port mappings. +You probably want to pre-configure a list of path-to-port mappings. Besides doing this in the reverese proxy you should also construct a csv file that contains the `path2port`_ mapping. -If you do that the test tool will construct the correct external URL based -on the *port* specification in the config file and the mapping defined in the +If you do that, the test tool will construct the correct external URL based +on the *port* specification and the mapping defined in the csv file. Since the reverse proxy will probably be used to terminate the HTTPS tunnel the tool will not have to deal with certificates which leaves us with the following simple command:: - optest.py -f flows.yaml -m reverse.csv config + optest.py -p 9000 -f flows.yaml -m reverse.csv config .. _reverse proxy: https://en.wikipedia.org/wiki/Reverse_proxy diff --git a/docs/OIDCtesting/OPtest/web.rst b/docs/OIDCtesting/OPtest/web.rst index ac350e90..16fefd3e 100644 --- a/docs/OIDCtesting/OPtest/web.rst +++ b/docs/OIDCtesting/OPtest/web.rst @@ -31,45 +31,45 @@ The configuration server is again a Python script:: -b -** +:: You should really set this in the configuration file rather then using this option. Anyway this is the base from which the tool will construct the necessary URLs. -c -** +:: More about the test tool configuration :ref:`here ` -f -** +:: The :ref:`flows ` information is passed on to the test tool instance -m -** +:: The :ref:`path2port ` information is passed on to the test tool instance -p -** +:: Which port the configuration server should listen on -t -** +:: Turns on HTTPS support. If set the configuration server will not listen to HTTP calls -M -** +:: The :ref:`Mako dir ` information is passed on to the test tool instance config -****** +:::::: The configuration file looks like this:: @@ -84,9 +84,9 @@ The configuration file looks like this:: #VERIFY_SSL = False BASE_URL = 'http://localhost' - MAKO_DIR = './heart_mako' ENT_PATH = './entities' ENT_INFO = './entity_info' + MAKO_DIR = './heart_mako' FLOWS = ['./flows.yaml'] @@ -100,8 +100,8 @@ SERVER_CERT, SERVER_KEY and CERT_CHAIN Are only necessary if the test instance is supposed to do HTTPS. -BASE -++++ +BASE_URL +++++++++ *passed on to a test tool instace* The base from which the urls, that the test instance (as an RP) publishes, are @@ -131,6 +131,14 @@ This is information about the test instance which is static and should not differ between different test instances. Some of the information here represents default values and may be changed. +MAKO_DIR +++++++++ + +*passed on to a test tool instance* +Where the MAKO template files cna be found. This is the root directory +so within this directory there must be a ht_docs directory with the +actual templates. + FLOWS +++++ @@ -149,7 +157,7 @@ PORT_MAX, PORT_MIN ++++++++++++++++++ Defines the number of test instances that the configuration server can -spin off and which ports that must be used. When all ports are taken +spin off and which ports it can use for these. When all ports are taken no more test instance can be started unless a running test instance is removed. @@ -158,7 +166,7 @@ The web interface ----------------- When you have started a configuration server you can connect to the -port it listens on and wsee this: +port it listens on and see this: .. image:: confserver0.png From 9b9743d50365267ef0358ca6f514d2674bb36005 Mon Sep 17 00:00:00 2001 From: roland Date: Mon, 19 Dec 2016 21:49:14 +0100 Subject: [PATCH 6/6] Updated docs --- docs/OIDCtesting/OPtest/rest.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/OIDCtesting/OPtest/rest.rst b/docs/OIDCtesting/OPtest/rest.rst index f157645f..1c652043 100644 --- a/docs/OIDCtesting/OPtest/rest.rst +++ b/docs/OIDCtesting/OPtest/rest.rst @@ -3,4 +3,5 @@ Configuring the OP test tool using the REST interface ===================================================== - +*Intentionally left blank* +Content to come.