From 622df268a64c12253267e1b6ca86ac0ced4b1cce Mon Sep 17 00:00:00 2001 From: rhoerbe Date: Mon, 11 Aug 2014 14:42:40 +0200 Subject: [PATCH] added option to write response content to separate files outside log files. --- doc/howto.rst | 3 ++- src/sp_test/__init__.py | 15 +++++++++++-- src/sp_test/base.py | 50 +++++++++++++++++++++++++++++++++++++++-- 3 files changed, 63 insertions(+), 5 deletions(-) diff --git a/doc/howto.rst b/doc/howto.rst index a3da557..5fa65de 100644 --- a/doc/howto.rst +++ b/doc/howto.rst @@ -261,7 +261,8 @@ Synopsis:: -h, --help show this help message and exit -H, --prettyprint Status output as human readable python dictionary -J TT_CONFIG_FILE Test target configuration in JSON format - -L, --log Print HTTP log information # TODO: update documentation + -k Print HTTP response contents into separate files + -L, --log Path to the logfile directory -l, --list List all the test flows as a JSON object -m, --metadata Return the SP metadata -O, --operations Operations module (generated from Repository as idp_saml2base.py) diff --git a/src/sp_test/__init__.py b/src/sp_test/__init__.py index 89228fd..4212938 100644 --- a/src/sp_test/__init__.py +++ b/src/sp_test/__init__.py @@ -1,7 +1,9 @@ import json import pprint import argparse +import os.path import sys +import traceback from importlib import import_module from idp_test import SCHEMA @@ -63,11 +65,19 @@ class Client(object): "printed python dict instead of JSON") self._parser.add_argument("-i", dest="insecure", action='store_true', help="Do not verify SSL certificate") + self._parser.add_argument("-I", dest="keysdir", default="keys", + help="Directory for invalid IDP keys") self._parser.add_argument('-J', dest="json_config_file", - help="Script configuration") + help="Test target configuration") + self._parser.add_argument( + '-k', dest='content_log', action='store_true', + help="Log HTTP content in spearate files in directory " + "/, which defaults to the path in -L") self._parser.add_argument( "-l", dest="list", action="store_true", help="List all the test flows as a JSON object") + self._parser.add_argument("-L", dest="logpath", default=".", + help="Path to the logfile directory") self._parser.add_argument('-m', dest="metadata", action='store_true', help="Return the IdP metadata") self._parser.add_argument( @@ -184,7 +194,8 @@ class Client(object): self.interactions, self.json_config, check_factory=self.check_factory, entity_id=self.entity_id, - constraints=self.constraints) + constraints=self.constraints, + commandlineargs = self.args) try: conv.do_sequence_and_tests(oper["sequence"], oper["tests"]) self.test_log = conv.test_output diff --git a/src/sp_test/base.py b/src/sp_test/base.py index 808f852..3b4ae02 100644 --- a/src/sp_test/base.py +++ b/src/sp_test/base.py @@ -1,6 +1,7 @@ import base64 import cookielib import re +import os import traceback import urllib import sys @@ -10,6 +11,7 @@ from saml2 import BINDING_HTTP_REDIRECT, class_name from saml2 import BINDING_HTTP_POST from saml2.request import SERVICE2REQUEST from saml2.sigver import signed_instance_factory, pre_signature_part +from saml2.samlp import HttpParameters from saml2test import CheckError, FatalError from saml2test.check import Check @@ -31,6 +33,9 @@ import logging logger = logging.getLogger(__name__) +FILE_EXT = {"text/html": "html", "test/plain": "txt", "application/json": "json", + "text/xml": "xml", "application/xml": "xml", } + camel2underscore = re.compile('((?<=[a-z0-9])[A-Z]|(?!^)[A-Z](?=[a-z]))') @@ -47,6 +52,7 @@ class Conversation(): self.check_factory = check_factory self.msg_factory = msg_factory self.expect_exception = expect_exception + self.commandlineargs = commandlineargs self.cjar = {"browser": cookielib.CookieJar(), "rp": cookielib.CookieJar(), @@ -144,8 +150,46 @@ class Conversation(): return None def _log_response(self, response): + """Depending on -k argument write content to either logger or extra file + + Create the directory; delete all possibly existing files + Write response content into response_x. (with x incrementing from 0) + """ logger.info("<-- Status: %s" % response.status_code) - logger.info("<-- Content: %s" % response.content) + if response.status_code in [302, 301, 303]: + logger.info("<-- location: %s" % + response.headers._store['location'][1]) + else: + if self.commandlineargs.content_log: + self._content_log_fileno = getattr(self, '_content_log_fileno', 0) + 1 + if not getattr(self, 'logcontentpath', None): + try: + content_type_hdr = response.headers._store['content-type'][1] + l = content_type_hdr.split(';') + ['charset=ISO-8859-1',] + content_type = l[0] + encoding = l[1].split("=") + ext = "." + FILE_EXT[content_type] + except Exception as e: + ext = "" + self._logcontentpath = os.path.join( + self.commandlineargs.logpath, "log", + self.commandlineargs.oper) + if not os.path.exists(self._logcontentpath): + os.makedirs(self._logcontentpath) + for fn in os.listdir(self._logcontentpath): + old_file = os.path.join(self._logcontentpath, fn) + if os.path.isfile(old_file): + os.unlink(old_file) + fn = os.path.join(self._logcontentpath, "response_%d%s" + % (self._content_log_fileno, ext )) + f = open(fn, "w") + f.write(response.content) + f.close() + logger.info("<-- Response content (encoding=%s) in file %s" % + (encoding, fn)) + pass + else: + logger.info("<-- Content: %s" % response.content) def wb_send_GET_startpage(self): """ @@ -193,6 +237,8 @@ class Conversation(): self.saml_request = self.instance._parse_request( _str, SERVICE2REQUEST[self._endpoint], self._endpoint, self._binding) + if self._binding == BINDING_HTTP_REDIRECT: + self.http_parameters = HttpParameters(_dict) def _redirect(self, _response): rdseq = [] @@ -363,7 +409,7 @@ class Conversation(): self.do_flow(flow) except InteractionNeeded: self.test_output.append({"status": INTERACTION, - "message": self.last_content, + "message": "see detail log for response content", "id": "exception", "name": "interaction needed", "url": self.position})