8
from hashlib import sha1
10
from urlparse import parse_qs
11
from Cookie import SimpleCookie
14
from saml2 import server
15
from saml2 import BINDING_HTTP_ARTIFACT
16
from saml2 import BINDING_URI
17
from saml2 import BINDING_PAOS
18
from saml2 import BINDING_SOAP
19
from saml2 import BINDING_HTTP_REDIRECT
20
from saml2 import BINDING_HTTP_POST
21
from saml2 import time_util
23
from saml2.authn_context import AuthnBroker
24
from saml2.authn_context import PASSWORD
25
from saml2.authn_context import UNSPECIFIED
26
from saml2.authn_context import authn_context_class_ref
27
from saml2.extension import pefim
28
from saml2.httputil import Response
29
from saml2.httputil import NotFound
30
from saml2.httputil import geturl
31
from saml2.httputil import get_post
32
from saml2.httputil import Redirect
33
from saml2.httputil import Unauthorized
34
from saml2.httputil import BadRequest
35
from saml2.httputil import ServiceError
36
from saml2.ident import Unknown
37
from saml2.metadata import create_metadata_string
38
from saml2.s_utils import rndstr, exception_trace
39
from saml2.s_utils import UnknownPrincipal
40
from saml2.s_utils import UnsupportedBinding
41
from saml2.s_utils import PolicyError
42
from saml2.sigver import verify_redirect_signature, cert_from_instance, encrypt_cert_from_item
44
logger = logging.getLogger("saml2.idp")
53
def _expiration(timeout, tformat="%a, %d-%b-%Y %H:%M:%S GMT"):
61
return time_util.instant(tformat)
62
elif timeout == "dawn":
63
return time.strftime(tformat, time.gmtime(0))
65
# validity time should match lifetime of assertions
66
return time_util.in_a_while(minutes=timeout, format=tformat)
69
def get_eptid(idp, req_info, session):
70
return idp.eptid.get(idp.config.entityid,
71
req_info.sender(), session["permanent_id"],
72
session["authn_auth"])
74
# -----------------------------------------------------------------------------
77
def dict2list_of_tuples(d):
78
return [(k, v) for k, v in d.items()]
80
# -----------------------------------------------------------------------------
83
class Service(object):
84
def __init__(self, environ, start_response, user=None):
85
self.environ = environ
86
logger.debug("ENVIRON: %s" % environ)
87
self.start_response = start_response
90
def unpack_redirect(self):
91
if "QUERY_STRING" in self.environ:
92
_qs = self.environ["QUERY_STRING"]
93
return dict([(k, v[0]) for k, v in parse_qs(_qs).items()])
97
def unpack_post(self):
98
_dict = parse_qs(get_post(self.environ))
99
logger.debug("unpack_post:: %s" % _dict)
101
return dict([(k, v[0]) for k, v in _dict.items()])
105
def unpack_soap(self):
107
query = get_post(self.environ)
108
return {"SAMLRequest": query, "RelayState": ""}
112
def unpack_either(self):
113
if self.environ["REQUEST_METHOD"] == "GET":
114
_dict = self.unpack_redirect()
115
elif self.environ["REQUEST_METHOD"] == "POST":
116
_dict = self.unpack_post()
119
logger.debug("_dict: %s" % _dict)
122
def operation(self, _dict, binding):
123
logger.debug("_operation: %s" % _dict)
124
if not _dict or not 'SAMLRequest' in _dict:
125
resp = BadRequest('Error parsing request or no request')
126
return resp(self.environ, self.start_response)
129
_encrypt_cert = encrypt_cert_from_item(_dict["req_info"].message)
130
return self.do(_dict["SAMLRequest"], binding,
131
_dict["RelayState"], encrypt_cert=_encrypt_cert)
133
# Can live with no relay state
134
return self.do(_dict["SAMLRequest"], binding)
136
def artifact_operation(self, _dict):
138
resp = BadRequest("Missing query")
139
return resp(self.environ, self.start_response)
141
# exchange artifact for request
142
request = IDP.artifact2message(_dict["SAMLart"], "spsso")
144
return self.do(request, BINDING_HTTP_ARTIFACT,
147
return self.do(request, BINDING_HTTP_ARTIFACT)
149
def response(self, binding, http_args):
150
if binding == BINDING_HTTP_ARTIFACT:
153
resp = Response(http_args["data"], headers=http_args["headers"])
154
return resp(self.environ, self.start_response)
156
def do(self, query, binding, relay_state="", encrypt_cert=None):
160
""" Expects a HTTP-redirect request """
162
_dict = self.unpack_redirect()
163
return self.operation(_dict, BINDING_HTTP_REDIRECT)
166
""" Expects a HTTP-POST request """
168
_dict = self.unpack_post()
169
return self.operation(_dict, BINDING_HTTP_POST)
172
# Can be either by HTTP_Redirect or HTTP_POST
173
_dict = self.unpack_either()
174
return self.artifact_operation(_dict)
178
Single log out using HTTP_SOAP binding
180
logger.debug("- SOAP -")
181
_dict = self.unpack_soap()
182
logger.debug("_dict: %s" % _dict)
183
return self.operation(_dict, BINDING_SOAP)
186
_dict = self.unpack_either()
187
return self.operation(_dict, BINDING_SOAP)
189
# def not_authn(self, key):
195
# loc = "http://%s/login" % (self.environ["HTTP_HOST"])
196
# loc += "?%s" % urllib.urlencode({"came_from": self.environ[
197
# "PATH_INFO"], "key": key})
198
# headers = [('Content-Type', 'text/plain')]
200
# logger.debug("location: %s" % loc)
201
# logger.debug("headers: %s" % headers)
203
# resp = Redirect(loc, headers=headers)
205
# return resp(self.environ, self.start_response)
207
def not_authn(self, key, requested_authn_context):
208
ruri = geturl(self.environ, query=False)
209
return do_authentication(self.environ, self.start_response,
210
authn_context=requested_authn_context,
211
key=key, redirect_uri=ruri)
214
# -----------------------------------------------------------------------------
216
REPOZE_ID_EQUIVALENT = "uid"
217
FORM_SPEC = """<form name="myform" method="post" action="%s">
218
<input type="hidden" name="SAMLResponse" value="%s" />
219
<input type="hidden" name="RelayState" value="%s" />
222
# -----------------------------------------------------------------------------
223
# === Single log in ====
224
# -----------------------------------------------------------------------------
227
class AuthenticationNeeded(Exception):
228
def __init__(self, authn_context=None, *args, **kwargs):
229
Exception.__init__(*args, **kwargs)
230
self.authn_context = authn_context
234
def __init__(self, environ, start_response, user=None):
235
Service.__init__(self, environ, start_response, user)
237
self.response_bindings = None
239
self.binding_out = None
240
self.destination = None
243
def verify_request(self, query, binding):
245
:param query: The SAML query, transport encoded
246
:param binding: Which binding the query came in over
250
logger.info("Missing QUERY")
251
resp = Unauthorized('Unknown user')
252
return resp_args, resp(self.environ, self.start_response)
254
if not self.req_info:
255
self.req_info = IDP.parse_authn_request(query, binding)
257
logger.info("parsed OK")
258
_authn_req = self.req_info.message
259
logger.debug("%s" % _authn_req)
261
self.binding_out, self.destination = IDP.pick_binding(
262
"assertion_consumer_service",
263
bindings=self.response_bindings,
264
entity_id=_authn_req.issuer.text)
266
logger.debug("Binding: %s, destination: %s" % (self.binding_out,
271
resp_args = IDP.response_args(_authn_req)
273
except UnknownPrincipal, excp:
274
_resp = IDP.create_error_response(_authn_req.id,
275
self.destination, excp)
276
except UnsupportedBinding, excp:
277
_resp = IDP.create_error_response(_authn_req.id,
278
self.destination, excp)
280
return resp_args, _resp
282
def do(self, query, binding_in, relay_state="", encrypt_cert=None):
284
resp_args, _resp = self.verify_request(query, binding_in)
285
except UnknownPrincipal, excp:
286
logger.error("UnknownPrincipal: %s" % (excp,))
287
resp = ServiceError("UnknownPrincipal: %s" % (excp,))
288
return resp(self.environ, self.start_response)
289
except UnsupportedBinding, excp:
290
logger.error("UnsupportedBinding: %s" % (excp,))
291
resp = ServiceError("UnsupportedBinding: %s" % (excp,))
292
return resp(self.environ, self.start_response)
295
identity = USERS[self.user].copy()
296
#identity["eduPersonTargetedID"] = get_eptid(IDP, query, session)
297
logger.info("Identity: %s" % (identity,))
299
if REPOZE_ID_EQUIVALENT:
300
identity[REPOZE_ID_EQUIVALENT] = self.user
302
_resp = IDP.create_authn_response(
303
identity, userid=self.user,
304
authn=AUTHN_BROKER[self.environ["idp.authn_ref"]], encrypt_cert=encrypt_cert,
306
except Exception, excp:
307
logging.error(exception_trace(excp))
308
resp = ServiceError("Exception: %s" % (excp,))
309
return resp(self.environ, self.start_response)
311
logger.info("AuthNResponse: %s" % _resp)
312
http_args = IDP.apply_binding(self.binding_out,
313
"%s" % _resp, self.destination,
314
relay_state, response=True)
315
logger.debug("HTTPargs: %s" % http_args)
316
return self.response(self.binding_out, http_args)
318
def _store_request(self, _dict):
319
logger.debug("_store_request: %s" % _dict)
320
key = sha1(_dict["SAMLRequest"]).hexdigest()
321
# store the AuthnRequest
322
IDP.ticket[key] = _dict
326
""" This is the HTTP-redirect endpoint """
328
logger.info("--- In SSO Redirect ---")
329
_info = self.unpack_redirect()
333
_info = IDP.ticket[_key]
334
self.req_info = _info["req_info"]
338
self.req_info = IDP.parse_authn_request(_info["SAMLRequest"],
339
BINDING_HTTP_REDIRECT)
341
resp = BadRequest("Message signature verification failure")
342
return resp(self.environ, self.start_response)
344
_req = self.req_info.message
346
if "SigAlg" in _info and "Signature" in _info: # Signed request
347
issuer = _req.issuer.text
348
_certs = IDP.metadata.certs(issuer, "any", "signing")
351
if verify_redirect_signature(_info, cert):
355
resp = BadRequest("Message signature verification failure")
356
return resp(self.environ, self.start_response)
360
_info["req_info"] = self.req_info
361
key = self._store_request(_info)
362
return self.not_authn(key, _req.requested_authn_context)
364
return self.operation(_info, BINDING_HTTP_REDIRECT)
366
_info["req_info"] = self.req_info
367
key = self._store_request(_info)
368
return self.not_authn(key, _req.requested_authn_context)
370
return self.operation(_info, BINDING_HTTP_REDIRECT)
374
The HTTP-Post endpoint
376
logger.info("--- In SSO POST ---")
377
_info = self.unpack_either()
378
self.req_info = IDP.parse_authn_request(
379
_info["SAMLRequest"], BINDING_HTTP_POST)
380
_req = self.req_info.message
383
_info["req_info"] = self.req_info
384
key = self._store_request(_info)
385
return self.not_authn(key, _req.requested_authn_context)
387
return self.operation(_info, BINDING_HTTP_POST)
389
_info["req_info"] = self.req_info
390
key = self._store_request(_info)
391
return self.not_authn(key, _req.requested_authn_context)
393
# def artifact(self):
394
# # Can be either by HTTP_Redirect or HTTP_POST
395
# _req = self._store_request(self.unpack_either())
396
# if isinstance(_req, basestring):
397
# return self.not_authn(_req)
398
# return self.artifact_operation(_req)
402
logger.info("--- ECP SSO ---")
406
authz_info = self.environ["HTTP_AUTHORIZATION"]
407
if authz_info.startswith("Basic "):
408
_info = base64.b64decode(authz_info[6:])
409
logger.debug("Authz_info: %s" % _info)
411
(user, passwd) = _info.split(":")
412
if PASSWD[user] != passwd:
413
resp = Unauthorized()
416
resp = Unauthorized()
418
resp = Unauthorized()
420
resp = Unauthorized()
423
return resp(self.environ, self.start_response)
425
_dict = self.unpack_soap()
426
self.response_bindings = [BINDING_PAOS]
428
return self.operation(_dict, BINDING_SOAP)
430
# -----------------------------------------------------------------------------
431
# === Authentication ====
432
# -----------------------------------------------------------------------------
435
def do_authentication(environ, start_response, authn_context, key,
438
Display the login form
440
logger.debug("Do authentication")
441
auth_info = AUTHN_BROKER.pick(authn_context)
444
method, reference = auth_info[0]
445
logger.debug("Authn chosen: %s (ref=%s)" % (method, reference))
446
return method(environ, start_response, reference, key, redirect_uri)
448
resp = Unauthorized("No usable authentication method")
449
return resp(environ, start_response)
452
# -----------------------------------------------------------------------------
454
PASSWD = {"haho0032": "qwerty",
455
"roland": "dianakra",
460
def username_password_authn(environ, start_response, reference, key,
463
Display the login form
465
logger.info("The login page")
468
resp = Response(mako_template="login.mako", template_lookup=LOOKUP,
476
"authn_reference": reference,
477
"redirect_uri": redirect_uri
479
logger.info("do_authentication argv: %s" % argv)
480
return resp(environ, start_response, **argv)
483
def verify_username_and_password(dic):
485
# verify username and password
486
if PASSWD[dic["login"][0]] == dic["password"][0]:
487
return True, dic["login"][0]
492
def do_verify(environ, start_response, _):
493
query = parse_qs(get_post(environ))
495
logger.debug("do_verify: %s" % query)
498
_ok, user = verify_username_and_password(query)
504
resp = Unauthorized("Unknown user or wrong password")
507
IDP.cache.uid2user[uid] = user
508
IDP.cache.user2uid[user] = uid
509
logger.debug("Register %s under '%s'" % (user, uid))
511
kaka = set_cookie("idpauthn", "/", uid, query["authn_reference"][0])
513
lox = "%s?id=%s&key=%s" % (query["redirect_uri"][0], uid,
515
logger.debug("Redirect => %s" % lox)
516
resp = Redirect(lox, headers=[kaka], content="text/html")
518
return resp(environ, start_response)
521
def not_found(environ, start_response):
522
"""Called if no URL matches."""
524
return resp(environ, start_response)
527
# -----------------------------------------------------------------------------
528
# === Single log out ===
529
# -----------------------------------------------------------------------------
531
#def _subject_sp_info(req_info):
532
# # look for the subject
533
# subject = req_info.subject_id()
534
# subject = subject.text.strip()
535
# sp_entity_id = req_info.message.issuer.text.strip()
536
# return subject, sp_entity_id
539
def do(self, request, binding, relay_state="", encrypt_cert=None):
540
logger.info("--- Single Log Out Service ---")
542
_, body = request.split("\n")
543
logger.debug("req: '%s'" % body)
544
req_info = IDP.parse_logout_request(body, binding)
545
except Exception, exc:
546
logger.error("Bad request: %s" % exc)
547
resp = BadRequest("%s" % exc)
548
return resp(self.environ, self.start_response)
550
msg = req_info.message
552
lid = IDP.ident.find_local_id(msg.name_id)
553
logger.info("local identifier: %s" % lid)
554
if lid in IDP.cache.user2uid:
555
uid = IDP.cache.user2uid[lid]
556
if uid in IDP.cache.uid2user:
557
del IDP.cache.uid2user[uid]
558
del IDP.cache.user2uid[lid]
559
# remove the authentication
561
IDP.session_db.remove_authn_statements(msg.name_id)
562
except KeyError, exc:
563
logger.error("ServiceError: %s" % exc)
564
resp = ServiceError("%s" % exc)
565
return resp(self.environ, self.start_response)
567
resp = IDP.create_logout_response(msg, [binding])
570
hinfo = IDP.apply_binding(binding, "%s" % resp, "", relay_state)
571
except Exception, exc:
572
logger.error("ServiceError: %s" % exc)
573
resp = ServiceError("%s" % exc)
574
return resp(self.environ, self.start_response)
576
#_tlh = dict2list_of_tuples(hinfo["headers"])
577
delco = delete_cookie(self.environ, "idpauthn")
579
hinfo["headers"].append(delco)
580
logger.info("Header: %s" % (hinfo["headers"],))
581
resp = Response(hinfo["data"], headers=hinfo["headers"])
582
return resp(self.environ, self.start_response)
584
# ----------------------------------------------------------------------------
585
# Manage Name ID service
586
# ----------------------------------------------------------------------------
591
def do(self, query, binding, relay_state="", encrypt_cert=None):
592
logger.info("--- Manage Name ID Service ---")
593
req = IDP.parse_manage_name_id_request(query, binding)
594
request = req.message
596
# Do the necessary stuff
597
name_id = IDP.ident.handle_manage_name_id_request(
598
request.name_id, request.new_id, request.new_encrypted_id,
601
logger.debug("New NameID: %s" % name_id)
603
_resp = IDP.create_manage_name_id_response(request)
605
# It's using SOAP binding
606
hinfo = IDP.apply_binding(BINDING_SOAP, "%s" % _resp, "",
607
relay_state, response=True)
609
resp = Response(hinfo["data"], headers=hinfo["headers"])
610
return resp(self.environ, self.start_response)
612
# ----------------------------------------------------------------------------
613
# === Assertion ID request ===
614
# ----------------------------------------------------------------------------
619
def do(self, aid, binding, relay_state="", encrypt_cert=None):
620
logger.info("--- Assertion ID Service ---")
623
assertion = IDP.create_assertion_id_request_response(aid)
626
return resp(self.environ, self.start_response)
628
hinfo = IDP.apply_binding(BINDING_URI, "%s" % assertion, response=True)
630
logger.debug("HINFO: %s" % hinfo)
631
resp = Response(hinfo["data"], headers=hinfo["headers"])
632
return resp(self.environ, self.start_response)
634
def operation(self, _dict, binding, **kwargs):
635
logger.debug("_operation: %s" % _dict)
636
if not _dict or "ID" not in _dict:
637
resp = BadRequest('Error parsing request or no request')
638
return resp(self.environ, self.start_response)
640
return self.do(_dict["ID"], binding, **kwargs)
643
# ----------------------------------------------------------------------------
644
# === Artifact resolve service ===
645
# ----------------------------------------------------------------------------
648
def do(self, request, binding, relay_state="", encrypt_cert=None):
649
_req = IDP.parse_artifact_resolve(request, binding)
651
msg = IDP.create_artifact_response(_req, _req.artifact.text)
653
hinfo = IDP.apply_binding(BINDING_SOAP, "%s" % msg, "", "",
656
resp = Response(hinfo["data"], headers=hinfo["headers"])
657
return resp(self.environ, self.start_response)
659
# ----------------------------------------------------------------------------
660
# === Authn query service ===
661
# ----------------------------------------------------------------------------
666
def do(self, request, binding, relay_state="", encrypt_cert=None):
667
logger.info("--- Authn Query Service ---")
668
_req = IDP.parse_authn_query(request, binding)
669
_query = _req.message
671
msg = IDP.create_authn_query_response(_query.subject,
672
_query.requested_authn_context,
673
_query.session_index)
675
logger.debug("response: %s" % msg)
676
hinfo = IDP.apply_binding(BINDING_SOAP, "%s" % msg, "", "",
679
resp = Response(hinfo["data"], headers=hinfo["headers"])
680
return resp(self.environ, self.start_response)
683
# ----------------------------------------------------------------------------
684
# === Attribute query service ===
685
# ----------------------------------------------------------------------------
690
def do(self, request, binding, relay_state="", encrypt_cert=None):
691
logger.info("--- Attribute Query Service ---")
693
_req = IDP.parse_attribute_query(request, binding)
694
_query = _req.message
696
name_id = _query.subject.name_id
698
logger.debug("Local uid: %s" % uid)
699
identity = EXTRA[uid]
701
# Comes in over SOAP so only need to construct the response
702
args = IDP.response_args(_query, [BINDING_SOAP])
703
msg = IDP.create_attribute_response(identity,
704
name_id=name_id, **args)
706
logger.debug("response: %s" % msg)
707
hinfo = IDP.apply_binding(BINDING_SOAP, "%s" % msg, "", "",
710
resp = Response(hinfo["data"], headers=hinfo["headers"])
711
return resp(self.environ, self.start_response)
713
# ----------------------------------------------------------------------------
714
# Name ID Mapping service
715
# When an entity that shares an identifier for a principal with an identity
716
# provider wishes to obtain a name identifier for the same principal in a
717
# particular format or federation namespace, it can send a request to
718
# the identity provider using this protocol.
719
# ----------------------------------------------------------------------------
723
def do(self, query, binding, relay_state="", encrypt_cert=None):
724
req = IDP.parse_name_id_mapping_request(query, binding)
725
request = req.message
726
# Do the necessary stuff
728
name_id = IDP.ident.handle_name_id_mapping_request(
729
request.name_id, request.name_id_policy)
731
resp = BadRequest("Unknown entity")
732
return resp(self.environ, self.start_response)
734
resp = BadRequest("Unknown entity")
735
return resp(self.environ, self.start_response)
737
info = IDP.response_args(request)
738
_resp = IDP.create_name_id_mapping_response(name_id, **info)
741
hinfo = IDP.apply_binding(BINDING_SOAP, "%s" % _resp, "", "",
744
resp = Response(hinfo["data"], headers=hinfo["headers"])
745
return resp(self.environ, self.start_response)
748
# ----------------------------------------------------------------------------
750
# ----------------------------------------------------------------------------
751
def info_from_cookie(kaka):
752
logger.debug("KAKA: %s" % kaka)
754
cookie_obj = SimpleCookie(kaka)
755
morsel = cookie_obj.get("idpauthn", None)
758
key, ref = base64.b64decode(morsel.value).split(":")
759
return IDP.cache.uid2user[key], ref
763
logger.debug("No idpauthn cookie")
767
def delete_cookie(environ, name):
768
kaka = environ.get("HTTP_COOKIE", '')
769
logger.debug("delete KAKA: %s" % kaka)
771
cookie_obj = SimpleCookie(kaka)
772
morsel = cookie_obj.get(name, None)
773
cookie = SimpleCookie()
775
cookie[name]['path'] = "/"
776
logger.debug("Expire: %s" % morsel)
777
cookie[name]["expires"] = _expiration("dawn")
778
return tuple(cookie.output().split(": ", 1))
782
def set_cookie(name, _, *args):
783
cookie = SimpleCookie()
784
cookie[name] = base64.b64encode(":".join(args))
785
cookie[name]['path'] = "/"
786
cookie[name]["expires"] = _expiration(5) # 5 minutes from now
787
logger.debug("Cookie expires: %s" % cookie[name]["expires"])
788
return tuple(cookie.output().split(": ", 1))
790
# ----------------------------------------------------------------------------
792
# map urls to functions
795
(r'sso/post$', (SSO, "post")),
796
(r'sso/post/(.*)$', (SSO, "post")),
797
(r'sso/redirect$', (SSO, "redirect")),
798
(r'sso/redirect/(.*)$', (SSO, "redirect")),
799
(r'sso/art$', (SSO, "artifact")),
800
(r'sso/art/(.*)$', (SSO, "artifact")),
802
(r'slo/redirect$', (SLO, "redirect")),
803
(r'slo/redirect/(.*)$', (SLO, "redirect")),
804
(r'slo/post$', (SLO, "post")),
805
(r'slo/post/(.*)$', (SLO, "post")),
806
(r'slo/soap$', (SLO, "soap")),
807
(r'slo/soap/(.*)$', (SLO, "soap")),
809
(r'airs$', (AIDR, "uri")),
810
(r'ars$', (ARS, "soap")),
812
(r'mni/post$', (NMI, "post")),
813
(r'mni/post/(.*)$', (NMI, "post")),
814
(r'mni/redirect$', (NMI, "redirect")),
815
(r'mni/redirect/(.*)$', (NMI, "redirect")),
816
(r'mni/art$', (NMI, "artifact")),
817
(r'mni/art/(.*)$', (NMI, "artifact")),
818
(r'mni/soap$', (NMI, "soap")),
819
(r'mni/soap/(.*)$', (NMI, "soap")),
821
(r'nim$', (NIM, "soap")),
822
(r'nim/(.*)$', (NIM, "soap")),
824
(r'aqs$', (AQS, "soap")),
825
(r'attr$', (ATTR, "soap"))
829
#(r'login?(.*)$', do_authentication),
830
(r'verify?(.*)$', do_verify),
831
(r'sso/ecp$', (SSO, "ecp")),
834
# ----------------------------------------------------------------------------
837
def metadata(environ, start_response):
840
if path is None or len(path) == 0:
841
path = os.path.dirname(os.path.abspath( __file__ ))
844
metadata = create_metadata_string(path+args.config, IDP.config,
845
args.valid, args.cert, args.keyfile,
846
args.id, args.name, args.sign)
847
start_response('200 OK', [('Content-Type', "text/xml")])
849
except Exception as ex:
850
logger.error("An error occured while creating metadata:" + ex.message)
851
return not_found(environ, start_response)
853
def staticfile(environ, start_response):
856
if path is None or len(path) == 0:
857
path = os.path.dirname(os.path.abspath(__file__))
860
path += environ.get('PATH_INFO', '').lstrip('/')
861
start_response('200 OK', [('Content-Type', "text/xml")])
862
return open(path, 'r').read()
863
except Exception as ex:
864
logger.error("An error occured while creating metadata:" + ex.message)
865
return not_found(environ, start_response)
867
def application(environ, start_response):
869
The main WSGI application. Dispatch the current request to
870
the functions from above and store the regular expression
871
captures in the WSGI environment as `myapp.url_args` so that
872
the functions from above can access the url placeholders.
874
If nothing matches call the `not_found` function.
876
:param environ: The HTTP application environment
877
:param start_response: The application to run when the handling of the
879
:return: The response as a list of lines
882
path = environ.get('PATH_INFO', '').lstrip('/')
884
if path == "metadata":
885
return metadata(environ, start_response)
887
kaka = environ.get("HTTP_COOKIE", None)
888
logger.info("<application> PATH: %s" % path)
891
logger.info("= KAKA =")
892
user, authn_ref = info_from_cookie(kaka)
893
environ["idp.authn_ref"] = authn_ref
896
query = parse_qs(environ["QUERY_STRING"])
897
logger.debug("QUERY: %s" % query)
898
user = IDP.cache.uid2user[query["id"][0]]
902
url_patterns = AUTHN_URLS
904
logger.info("-- No USER --")
905
# insert NON_AUTHN_URLS first in case there is no user
906
url_patterns = NON_AUTHN_URLS + url_patterns
908
for regex, callback in url_patterns:
909
match = re.search(regex, path)
910
if match is not None:
912
environ['myapp.url_args'] = match.groups()[0]
914
environ['myapp.url_args'] = path
916
logger.debug("Callback: %s" % (callback,))
917
if isinstance(callback, tuple):
918
cls = callback[0](environ, start_response, user)
919
func = getattr(cls, callback[1])
921
return callback(environ, start_response, user)
923
if re.search(r'static/.*', path) is not None:
924
return staticfile(environ, start_response)
925
return not_found(environ, start_response)
927
# ----------------------------------------------------------------------------
929
# allow uwsgi or gunicorn mount
930
# by moving some initialization out of __name__ == '__main__' section.
931
# uwsgi -s 0.0.0.0:8088 --protocol http --callable application --module idp
933
args = type('Config', (object,), { })
934
args.config = 'idp_conf'
935
args.mako_root = './'
939
from idp_user import USERS
940
from idp_user import EXTRA
941
from mako.lookup import TemplateLookup
943
AUTHN_BROKER = AuthnBroker()
944
AUTHN_BROKER.add(authn_context_class_ref(PASSWORD),
945
username_password_authn, 10,
946
"http://%s" % socket.gethostname())
947
AUTHN_BROKER.add(authn_context_class_ref(UNSPECIFIED),
948
"", 0, "http://%s" % socket.gethostname())
950
IDP = server.Server(args.config, cache=Cache())
953
# ----------------------------------------------------------------------------
955
if __name__ == '__main__':
956
from wsgiref.simple_server import make_server
958
parser = argparse.ArgumentParser()
959
parser.add_argument('-p', dest='path', help='Path to configuration file.')
960
parser.add_argument('-v', dest='valid',
961
help="How long, in days, the metadata is valid from the time of creation")
962
parser.add_argument('-c', dest='cert', help='certificate')
963
parser.add_argument('-i', dest='id',
964
help="The ID of the entities descriptor")
965
parser.add_argument('-k', dest='keyfile',
966
help="A file with a key to sign the metadata with")
967
parser.add_argument('-n', dest='name')
968
parser.add_argument('-s', dest='sign', action='store_true',
969
help="sign the metadata")
970
parser.add_argument('-m', dest='mako_root', default="./")
971
parser.add_argument(dest="config")
972
args = parser.parse_args()
974
_rot = args.mako_root
975
LOOKUP = TemplateLookup(directories=[_rot + 'templates', _rot + 'htdocs'],
976
module_directory=_rot + 'modules',
977
input_encoding='utf-8', output_encoding='utf-8')
981
SRV = make_server('', PORT, application)
982
print "IdP listening on port: %s" % PORT
985
_rot = args.mako_root
986
LOOKUP = TemplateLookup(directories=[_rot + 'templates', _rot + 'htdocs'],
987
module_directory=_rot + 'modules',
988
input_encoding='utf-8', output_encoding='utf-8')