Added logger specification to the configuration

This commit is contained in:
Roland Hedberg
2011-03-31 12:41:52 +02:00
parent b0a2ab971f
commit ea8cf6dbcb
6 changed files with 217 additions and 13 deletions

View File

@@ -104,6 +104,8 @@ class Saml2Client(object):
self.metadata = self.config.metadata self.metadata = self.config.metadata
self.sec = security_context(config) self.sec = security_context(config)
self.logger = self.config.setup_logger()
if virtual_organization: if virtual_organization:
self.vorg = VirtualOrg(self, virtual_organization) self.vorg = VirtualOrg(self, virtual_organization)
else: else:
@@ -180,6 +182,9 @@ class Saml2Client(object):
except KeyError: except KeyError:
raise Exception("Missing entity_id specification") raise Exception("Missing entity_id specification")
if log is None:
log = self.logger
reply_addr = self._service_url() reply_addr = self._service_url()
resp = None resp = None
@@ -255,7 +260,10 @@ class Saml2Client(object):
request.name_id_policy = name_id_policy request.name_id_policy = name_id_policy
request.issuer = self.issuer(spentityid) request.issuer = self.issuer(spentityid)
if log is None:
log = self.logger
if log: if log:
log.info("REQUEST: %s" % request) log.info("REQUEST: %s" % request)
@@ -315,7 +323,10 @@ class Saml2Client(object):
location = self._sso_location(entityid) location = self._sso_location(entityid)
service_url = self._service_url() service_url = self._service_url()
my_name = self._my_name() my_name = self._my_name()
if log is None:
log = self.logger
if log: if log:
log.info("spentityid: %s" % spentityid) log.info("spentityid: %s" % spentityid)
log.info("location: %s" % location) log.info("location: %s" % location)
@@ -419,7 +430,10 @@ class Saml2Client(object):
:param log: Function to use for logging :param log: Function to use for logging
:return: The attributes returned :return: The attributes returned
""" """
if log is None:
log = self.logger
session_id = sid() session_id = sid()
issuer = self.issuer(issuer_id) issuer = self.issuer(issuer_id)
@@ -448,7 +462,7 @@ class Saml2Client(object):
log.info("Verifying response") log.info("Verifying response")
try: try:
aresp = attribute_response(self.config, "", issuer, log=log) aresp = attribute_response(self.config, issuer, log)
except Exception, exc: except Exception, exc:
if log: if log:
log.error("%s", (exc,)) log.error("%s", (exc,))
@@ -468,8 +482,7 @@ class Saml2Client(object):
return None return None
def construct_logout_request(self, subject_id, destination, def construct_logout_request(self, subject_id, destination,
issuer_entity_id, issuer_entity_id, reason=None, expire=None):
reason=None, expire=None):
""" Constructs a LogoutRequest """ Constructs a LogoutRequest
:param subject_id: The identifier of the subject :param subject_id: The identifier of the subject
@@ -526,7 +539,10 @@ class Saml2Client(object):
if SOAP binding has been used the just the result of that if SOAP binding has been used the just the result of that
conversation. conversation.
""" """
if log is None:
log = self.logger
if log: if log:
log.info("logout request for: %s" % subject_id) log.info("logout request for: %s" % subject_id)
@@ -548,7 +564,9 @@ class Saml2Client(object):
# for all where I can use the SOAP binding, do those first # for all where I can use the SOAP binding, do those first
not_done = entity_ids[:] not_done = entity_ids[:]
response = False response = False
if log is None:
log = self.logger
for entity_id in entity_ids: for entity_id in entity_ids:
response = False response = False
@@ -645,6 +663,9 @@ class Saml2Client(object):
:return: 4-tuple of (session_id of the last sent logout request, :return: 4-tuple of (session_id of the last sent logout request,
response message, response headers and message) response message, response headers and message)
""" """
if log is None:
log = self.logger
if log: if log:
log.info("state: %s" % (self.state,)) log.info("state: %s" % (self.state,))
status = self.state[response.in_response_to] status = self.state[response.in_response_to]
@@ -679,6 +700,8 @@ class Saml2Client(object):
""" """
response = None response = None
if log is None:
log = self.logger
if xmlstr: if xmlstr:
try: try:
@@ -732,6 +755,8 @@ class Saml2Client(object):
""" """
headers = [] headers = []
success = False success = False
if log is None:
log = self.logger
try: try:
saml_request = get['SAMLRequest'] saml_request = get['SAMLRequest']
@@ -779,7 +804,9 @@ class Saml2Client(object):
:param subject_id: the id of the current logged user :param subject_id: the id of the current logged user
:return: What is returned also depends on which binding is used. :return: What is returned also depends on which binding is used.
""" """
if log is None:
log = self.logger
if binding == BINDING_HTTP_REDIRECT: if binding == BINDING_HTTP_REDIRECT:
return self.http_redirect_logout_request(request, subject_id, log) return self.http_redirect_logout_request(request, subject_id, log)

View File

@@ -3,9 +3,15 @@
__author__ = 'rolandh' __author__ = 'rolandh'
import sys import sys
import logging
import logging.handlers
from importlib import import_module from importlib import import_module
from saml2 import BINDING_SOAP, BINDING_HTTP_REDIRECT from saml2 import BINDING_SOAP, BINDING_HTTP_REDIRECT
from saml2 import metadata from saml2 import metadata
from saml2 import root_logger
from saml2.attribute_converter import ac_factory from saml2.attribute_converter import ac_factory
from saml2.assertion import Policy from saml2.assertion import Policy
@@ -16,6 +22,7 @@ COMMON_ARGS = ["entityid", "xmlsec_binary", "debug", "key_file", "cert_file",
"contact_person", "contact_person",
"name_form", "name_form",
"virtual_organization", "virtual_organization",
"logger"
] ]
SP_ARGS = [ SP_ARGS = [
@@ -48,6 +55,24 @@ SPEC = {
"aa": COMMON_ARGS + COMPLEX_ARGS + AA_IDP_ARGS, "aa": COMMON_ARGS + COMPLEX_ARGS + AA_IDP_ARGS,
} }
# --------------- Logging stuff ---------------
LOG_LEVEL = {'debug': logging.DEBUG,
'info': logging.INFO,
'warning': logging.WARNING,
'error': logging.ERROR,
'critical': logging.CRITICAL}
LOG_HANDLER = {
"rotating": logging.handlers.RotatingFileHandler,
"syslog": logging.handlers.SysLogHandler,
"timerotate": logging.handlers.TimedRotatingFileHandler,
}
LOG_FORMAT = "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
# -----------------------------------------------------------------
class Config(object): class Config(object):
def_context = "" def_context = ""
@@ -193,6 +218,52 @@ class Config(object):
except IndexError: except IndexError:
return None return None
def setup_logger(self):
try:
_logconf = self.logger
except KeyError:
return None
if root_logger.level != logging.NOTSET: # Someone got there before me
return root_logger
if "loglevel" in _logconf:
root_logger.setLevel(LOG_LEVEL[_logconf["loglevel"]])
else: # reasonable default
root_logger.setLevel(logging.WARNING)
handler = None
for htyp in LOG_HANDLER:
if htyp in _logconf:
if htyp == "syslog":
args = _logconf[htyp]
if "socktype" in args:
import socket
if args["socktype"] == "dgram":
args["socktype"] = socket.SOCK_DGRAM
elif args["socktype"] == "stream":
args["socktype"] = socket.SOCK_STREAM
else:
raise Exception("Unknown socktype!")
handler = LOG_HANDLER[htyp](**args)
else:
handler = LOG_HANDLER[htyp](**_logconf[htyp])
break
if handler is None:
raise Exception("You have to define a log handler")
if "format" in _logconf:
formatter = logging.Formatter(_logconf["format"])
else:
formatter = logging.Formatter(LOG_FORMAT)
handler.setFormatter(formatter)
root_logger.addHandler(handler)
return root_logger
class SPConfig(Config): class SPConfig(Config):
def_context = "sp" def_context = "sp"
@@ -238,7 +309,7 @@ class SPConfig(Config):
return [] return []
def idps(self, langpref=["en"]): def idps(self, langpref=None):
""" Returns a dictionary of usefull IdPs, the keys being the """ Returns a dictionary of usefull IdPs, the keys being the
entity ID of the service and the names of the services as values entity ID of the service and the names of the services as values
@@ -246,6 +317,9 @@ class SPConfig(Config):
is used. is used.
:return: Dictionary :return: Dictionary
""" """
if langpref is None:
langpref = ["en"]
if self.idp: if self.idp:
return dict([(e, nd[0]) for (e, return dict([(e, nd[0]) for (e,
nd) in self.metadata.idps(langpref).items() if e in self.idp]) nd) in self.metadata.idps(langpref).items() if e in self.idp])

View File

@@ -214,9 +214,13 @@ class Server(object):
self.conf = config self.conf = config
else: else:
raise Exception("Missing configuration") raise Exception("Missing configuration")
if self.log is None:
self.log = self.conf.setup_logger()
self.metadata = self.conf.metadata self.metadata = self.conf.metadata
self.sec = security_context(self.conf, log) self.sec = security_context(self.conf, log)
# if cache: # if cache:
# if isinstance(cache, basestring): # if isinstance(cache, basestring):
# self.cache = Cache(cache) # self.cache = Cache(cache)
@@ -638,7 +642,7 @@ class Server(object):
sp_entity_id = request.issuer.text.strip() sp_entity_id = request.issuer.text.strip()
binding = None binding = None
destination = "" destinations = []
for binding in bindings: for binding in bindings:
destinations = self.conf.single_logout_services(sp_entity_id, destinations = self.conf.single_logout_services(sp_entity_id,
binding) binding)

View File

@@ -40,5 +40,13 @@ CONFIG={
"email_address": ["tech@eample.com", "tech@example.org"], "email_address": ["tech@eample.com", "tech@example.org"],
"contact_type": "technical" "contact_type": "technical"
}, },
] ],
"logger": {
"rotating": {
"filename": "sp.log",
"maxBytes": 100000,
"backupCount": 5,
},
"loglevel": "warning",
}
} }

View File

@@ -0,0 +1,54 @@
__author__ = 'rolandh'
CONFIG={
"entityid" : "urn:mace:example.com:saml:roland:sp",
"name" : "urn:mace:example.com:saml:roland:sp",
"description": "My own SP",
"service": {
"sp": {
"endpoints":{
"assertion_consumer_service": ["http://lingon.catalogix.se:8087/"],
},
"required_attributes": ["surName", "givenName", "mail"],
"optional_attributes": ["title"],
"idp": ["urn:mace:example.com:saml:roland:idp"],
}
},
"debug" : 1,
"key_file" : "test.key",
"cert_file" : "test.pem",
"xmlsec_binary" : "/usr/local/bin/xmlsec1",
"metadata": {
"local": ["idp.xml", "vo_metadata.xml"],
},
"virtual_organization" : {
"urn:mace:example.com:it:tek":{
"nameid_format" : "urn:oid:1.3.6.1.4.1.1466.115.121.1.15-NameID",
"common_identifier": "umuselin",
}
},
"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"
},
],
"logger": {
"syslog": {
"address": ("localhost", 514),
"facility": "local3",
"socktype": "dgram",
},
"loglevel": "info",
}
}

View File

@@ -6,6 +6,9 @@ from saml2.config import SPConfig, IdPConfig, Config
from saml2.metadata import MetaData from saml2.metadata import MetaData
from py.test import raises from py.test import raises
from saml2 import root_logger
import logging
sp1 = { sp1 = {
"entityid" : "urn:mace:umu.se:saml:roland:sp", "entityid" : "urn:mace:umu.se:saml:roland:sp",
"service": { "service": {
@@ -196,6 +199,40 @@ def test_wayf():
idps = c.idps(["se","en"]) idps = c.idps(["se","en"])
assert idps == {'urn:mace:example.com:saml:roland:idp': 'Exempel AB'} assert idps == {'urn:mace:example.com:saml:roland:idp': 'Exempel AB'}
c.setup_logger()
assert root_logger.level != logging.NOTSET
assert root_logger.level == logging.WARNING
assert len(root_logger.handlers) == 1
assert isinstance(root_logger.handlers[0],
logging.handlers.RotatingFileHandler)
handler = root_logger.handlers[0]
assert handler.backupCount == 5
assert handler.maxBytes == 100000
assert handler.mode == "a"
assert root_logger.name == "pySAML2"
assert root_logger.level == 30
def test_conf_syslog():
c = SPConfig().load_file("server_conf_syslog")
c.context = "sp"
# otherwise the logger setting is not changed
root_logger.level == logging.NOTSET
c.setup_logger()
assert root_logger.level != logging.NOTSET
assert root_logger.level == logging.INFO
assert len(root_logger.handlers) == 1
assert isinstance(root_logger.handlers[0],
logging.handlers.SyslogHandler)
handler = root_logger.handlers[0]
assert handler.backupCount == 5
assert handler.maxBytes == 100000
assert handler.mode == "a"
assert root_logger.name == "pySAML2"
assert root_logger.level == 20
#noinspection PyUnresolvedReferences #noinspection PyUnresolvedReferences
def test_3(): def test_3():
cnf = Config() cnf = Config()