Added logger specification to the configuration
This commit is contained in:
@@ -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)
|
||||||
|
|
||||||
|
|||||||
@@ -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])
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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",
|
||||||
|
}
|
||||||
}
|
}
|
||||||
54
tests/server_conf_syslog.py
Normal file
54
tests/server_conf_syslog.py
Normal 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",
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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()
|
||||||
|
|||||||
Reference in New Issue
Block a user