Skip to content

Commit 66d13d0

Browse files
author
Roland Hedberg
committed
Merge pull request #144 from geops/master
Fix for POST requests failing with 500
2 parents 6c1b963 + 8db1a3b commit 66d13d0

File tree

1 file changed

+45
-82
lines changed
  • src/s2repoze/plugins

1 file changed

+45
-82
lines changed

src/s2repoze/plugins/sp.py

Lines changed: 45 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#
2-
"""
3-
A plugin that allows you to use SAML2 SSO as authentication
2+
"""
3+
A plugin that allows you to use SAML2 SSO as authentication
44
and SAML2 attribute aggregations as metadata collector in your
55
WSGI application.
66
@@ -49,46 +49,14 @@
4949

5050

5151
def construct_came_from(environ):
52-
""" The URL that the user used when the process where interupted
52+
""" The URL that the user used when the process where interupted
5353
for single-sign-on processing. """
54-
55-
came_from = environ.get("PATH_INFO")
54+
55+
came_from = environ.get("PATH_INFO")
5656
qstr = environ.get("QUERY_STRING", "")
5757
if qstr:
5858
came_from += '?' + qstr
5959
return came_from
60-
61-
62-
def cgi_field_storage_to_dict(field_storage):
63-
"""Get a plain dictionary, rather than the '.value' system used by the
64-
cgi module."""
65-
66-
params = {}
67-
for key in field_storage.keys():
68-
try:
69-
params[key] = field_storage[key].value
70-
except AttributeError:
71-
if isinstance(field_storage[key], basestring):
72-
params[key] = field_storage[key]
73-
74-
return params
75-
76-
77-
def get_body(environ):
78-
length = int(environ["CONTENT_LENGTH"])
79-
try:
80-
body = environ["wsgi.input"].read(length)
81-
except Exception, excp:
82-
logger.exception("Exception while reading post: %s" % (excp,))
83-
raise
84-
85-
# restore what I might have upset
86-
from StringIO import StringIO
87-
environ['wsgi.input'] = StringIO(body)
88-
environ['s2repoze.body'] = body
89-
90-
return body
91-
9260

9361
def exception_trace(tag, exc, log):
9462
message = traceback.format_exception(*sys.exc_info())
@@ -113,7 +81,7 @@ def __call__(self, environ, start_response):
11381
class SAML2Plugin(object):
11482

11583
implements(IChallenger, IIdentifier, IAuthenticator, IMetadataProvider)
116-
84+
11785
def __init__(self, rememberer_name, config, saml_client, wayf, cache,
11886
sid_store=None, discovery="", idp_query_param="",
11987
sid_store_cert=None,):
@@ -158,27 +126,24 @@ def forget(self, environ, identity):
158126
def _get_post(self, environ):
159127
"""
160128
Get the posted information
161-
129+
162130
:param environ: A dictionary with environment variables
163131
"""
164-
165-
post_env = environ.copy()
166-
post_env['QUERY_STRING'] = ''
167-
168-
_ = get_body(environ)
169-
132+
133+
body= ''
170134
try:
171-
post = cgi.FieldStorage(
172-
fp=environ['wsgi.input'],
173-
environ=post_env,
174-
keep_blank_values=True
175-
)
176-
except Exception, excp:
177-
logger.debug("Exception (II): %s" % (excp,))
178-
raise
179-
135+
length= int(environ.get('CONTENT_LENGTH', '0'))
136+
except ValueError:
137+
length= 0
138+
if length!=0:
139+
body = environ['wsgi.input'].read(length) # get the POST variables
140+
environ['s2repoze.body'] = body # store the request body for later use by pysaml2
141+
environ['wsgi.input'] = StringIO(body) # restore the request body as a stream so that everything seems untouched
142+
143+
post = parse_qs(body) # parse the POST fields into a dict
144+
180145
logger.debug('identify post: %s' % (post,))
181-
146+
182147
return post
183148

184149
def _wayf_redirect(self, came_from):
@@ -190,8 +155,8 @@ def _wayf_redirect(self, came_from):
190155

191156
#noinspection PyUnusedLocal
192157
def _pick_idp(self, environ, came_from):
193-
"""
194-
If more than one idp and if none is selected, I have to do wayf or
158+
"""
159+
If more than one idp and if none is selected, I have to do wayf or
195160
disco
196161
"""
197162

@@ -230,7 +195,7 @@ def _pick_idp(self, environ, came_from):
230195
detail='unknown ECP version')
231196

232197
idps = self.metadata.with_descriptor("idpsso")
233-
198+
234199
logger.info("IdP URL: %s" % idps)
235200

236201
idp_entity_id = query = None
@@ -290,7 +255,7 @@ def _pick_idp(self, environ, came_from):
290255

291256
logger.info("Chosen IdP: '%s'" % idp_entity_id)
292257
return 0, idp_entity_id
293-
258+
294259
#### IChallenger ####
295260
#noinspection PyUnusedLocal
296261
def challenge(self, environ, _status, _app_headers, _forget_headers):
@@ -320,7 +285,7 @@ def challenge(self, environ, _status, _app_headers, _forget_headers):
320285
came_from = construct_came_from(environ)
321286
environ["myapp.came_from"] = came_from
322287
logger.debug("[sp.challenge] RelayState >> '%s'" % came_from)
323-
288+
324289
# Am I part of a virtual organization or more than one ?
325290
try:
326291
vorg_name = environ["myapp.vo"]
@@ -329,7 +294,7 @@ def challenge(self, environ, _status, _app_headers, _forget_headers):
329294
vorg_name = _cli.vorg._name
330295
except AttributeError:
331296
vorg_name = ""
332-
297+
333298
logger.info("[sp.challenge] VO: %s" % vorg_name)
334299

335300
# If more than one idp and if none is selected, I have to do wayf
@@ -373,7 +338,7 @@ def challenge(self, environ, _status, _app_headers, _forget_headers):
373338
req_id, msg_str = _cli.create_authn_request(
374339
dest, vorg=vorg_name, sign=_cli.authn_requests_signed,
375340
message_id=_sid, extensions=extensions)
376-
_sid = req_id
341+
_sid = req_id
377342
else:
378343
req_id, req = _cli.create_authn_request(
379344
dest, vorg=vorg_name, sign=False, extensions=extensions)
@@ -423,7 +388,7 @@ def _construct_identity(self, session_info):
423388
logger.debug("Identity: %s" % identity)
424389

425390
return identity
426-
391+
427392
def _eval_authn_response(self, environ, post, binding=BINDING_HTTP_POST):
428393
logger.info("Got AuthN response, checking..")
429394
logger.info("Outstanding: %s" % (self.outstanding_queries,))
@@ -432,18 +397,18 @@ def _eval_authn_response(self, environ, post, binding=BINDING_HTTP_POST):
432397
# Evaluate the response, returns a AuthnResponse instance
433398
try:
434399
authresp = self.saml_client.parse_authn_request_response(
435-
post["SAMLResponse"], binding, self.outstanding_queries,
400+
post["SAMLResponse"][0], binding, self.outstanding_queries,
436401
self.outstanding_certs)
437402

438403
except Exception, excp:
439404
logger.exception("Exception: %s" % (excp,))
440405
raise
441-
406+
442407
session_info = authresp.session_info()
443408
except TypeError, excp:
444409
logger.exception("Exception: %s" % (excp,))
445410
return None
446-
411+
447412
if session_info["came_from"]:
448413
logger.debug("came_from << %s" % session_info["came_from"])
449414
try:
@@ -478,13 +443,13 @@ def identify(self, environ):
478443
"SAMLResponse" not in query and "SAMLRequest" not in query:
479444
logger.debug('[identify] get or empty post')
480445
return None
481-
446+
482447
# if logger:
483448
# logger.info("ENVIRON: %s" % environ)
484449
# logger.info("self: %s" % (self.__dict__,))
485-
450+
486451
uri = environ.get('REQUEST_URI', construct_url(environ))
487-
452+
488453
logger.debug('[sp.identify] uri: %s' % (uri,))
489454

490455
query = parse_dict_querystring(environ)
@@ -495,15 +460,13 @@ def identify(self, environ):
495460
binding = BINDING_HTTP_REDIRECT
496461
else:
497462
post = self._get_post(environ)
498-
if post.list is None:
499-
post.list = []
500463
binding = BINDING_HTTP_POST
501464

502465
try:
503466
logger.debug('[sp.identify] post keys: %s' % (post.keys(),))
504467
except (TypeError, IndexError):
505468
pass
506-
469+
507470
try:
508471
path_info = environ['PATH_INFO']
509472
logout = False
@@ -514,7 +477,7 @@ def identify(self, environ):
514477
print("logout request received")
515478
try:
516479
response = self.saml_client.handle_logout_request(
517-
post["SAMLRequest"],
480+
post["SAMLRequest"][0],
518481
self.saml_client.users.subjects()[0], binding)
519482
environ['samlsp.pending'] = self._handle_logout(response)
520483
return {}
@@ -536,7 +499,7 @@ def identify(self, environ):
536499
try:
537500
if logout:
538501
response = self.saml_client.parse_logout_request_response(
539-
post["SAMLResponse"], binding)
502+
post["SAMLResponse"][0], binding)
540503
if response:
541504
action = self.saml_client.handle_logout_response(
542505
response)
@@ -552,7 +515,7 @@ def identify(self, environ):
552515
return {}
553516
else:
554517
session_info = self._eval_authn_response(
555-
environ, cgi_field_storage_to_dict(post),
518+
environ, post,
556519
binding=binding)
557520
except Exception, err:
558521
environ["s2repoze.saml_error"] = err
@@ -572,8 +535,8 @@ def identify(self, environ):
572535
exception_trace("sp.identity", exc, logger)
573536
environ["post.fieldstorage"] = post
574537
return {}
575-
576-
if session_info:
538+
539+
if session_info:
577540
environ["s2repoze.sessioninfo"] = session_info
578541
return self._construct_identity(session_info)
579542
else:
@@ -596,12 +559,12 @@ def add_metadata(self, environ, identity):
596559
logger.debug("Issuers: %s" % _cli.users.sources(name_id))
597560
except KeyError:
598561
pass
599-
562+
600563
if "user" not in identity:
601564
identity["user"] = {}
602565
try:
603566
(ava, _) = _cli.users.get_identity(name_id)
604-
#now = time.gmtime()
567+
#now = time.gmtime()
605568
logger.debug("[add_metadata] adds: %s" % ava)
606569
identity["user"].update(ava)
607570
except KeyError:
@@ -625,7 +588,7 @@ def add_metadata(self, environ, identity):
625588
if not identity["user"]:
626589
# remove cookie and demand re-authentication
627590
pass
628-
591+
629592
# used 2 times : one to get the ticket, the other to validate it
630593
@staticmethod
631594
def _service_url(environ, qstr=None):
@@ -635,7 +598,7 @@ def _service_url(environ, qstr=None):
635598
url = construct_url(environ)
636599
return url
637600

638-
#### IAuthenticatorPlugin ####
601+
#### IAuthenticatorPlugin ####
639602
#noinspection PyUnusedLocal
640603
def authenticate(self, environ, identity=None):
641604
if identity:
@@ -672,7 +635,7 @@ def make_plugin(remember_name=None, # plugin for remember
672635
discovery="",
673636
idp_query_param=""
674637
):
675-
638+
676639
if saml_conf is "":
677640
raise ValueError(
678641
'must include saml_conf in configuration')

0 commit comments

Comments
 (0)