Skip to content

Commit

Permalink
Several improvements in error handling
Browse files Browse the repository at this point in the history
* fix: SPID Error validation 30
* feat: Jinja2 templating system for fancy error pages - added in dependencies
* chore: template refactor
* BREAKAGE: added "static_storage_url" in spidSaml2 config
* chore: demo static server now is in https
  • Loading branch information
peppelinux committed Jun 14, 2021
1 parent e7a6187 commit ad5458d
Show file tree
Hide file tree
Showing 7 changed files with 134 additions and 71 deletions.
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,8 @@ with the help of an additional webserver dedicated for static contents:
You can find these demo pages in `example/static` and edit at your taste.
To get redirection to these pages, or redirection to third-party services, consider the following configuration files:

- `example/proxy_conf.yml`, example: `UNKNOW_ERROR_REDIRECT_PAGE: "http://localhost:9999/error_page.html"`
- `example/plugins/{backends,frontends}/$filename`, example: `disco_srv: "http://172.17.0.1:9999/static/disco.html"`
- `example/proxy_conf.yml`, example: `UNKNOW_ERROR_REDIRECT_PAGE: "https://localhost:9999/error_page.html"`
- `example/plugins/{backends,frontends}/$filename`, example: `disco_srv: "https://172.17.0.1:9999/static/disco.html"`


## Docker image
Expand Down Expand Up @@ -173,7 +173,7 @@ export SATOSA_APP=$VIRTUAL_ENV/lib/$(python -c 'import sys; print(f"python{sys.v
uwsgi --wsgi-file $SATOSA_APP/wsgi.py --https 0.0.0.0:10000,./pki/cert.pem,./pki/privkey.pem --callable app -b 32768
# additional static serve for the demo Discovery Service with Spid button
uwsgi --http 0.0.0.0:9999 --check-static-docroot --check-static ./static/ --static-index disco.html
uwsgi --https 0.0.0.0:9999,./pki/cert.pem,./pki/privkey.pem --check-static-docroot --check-static ./static/ --static-index disco.html
````

#### Get Proxy Metadata for your SP
Expand Down
82 changes: 57 additions & 25 deletions example/backends/spidsaml2.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@
import saml2
import satosa.util as util

from jinja2 import (Environment,
Markup,
FileSystemLoader,
Template,
select_autoescape)
from saml2 import BINDING_HTTP_REDIRECT, BINDING_HTTP_POST
from saml2.response import StatusAuthnFailed
from saml2.authn_context import requested_authn_context
Expand Down Expand Up @@ -63,22 +68,6 @@
"Provider di Identità. "
" Contattare il supporto tecnico per eventuali chiarimenti")

def handle_error(message:str, troubleshoot:str='', err=''):
"""
Todo: Jinja2 tempalte loader and rendering :)
"""
logger.error(f"Failed to parse authn request: {message} {err}")
msg = (
f'<b>{message}</b><br>'
f'{troubleshoot}'
)
return Response(text_type(msg).encode('utf-8'),
content="text/html; charset=utf8")


def handle_spid_anomaly(err_number, err):
return handle_error(**SPID_ANOMALIES[int(err_number)])


class SpidSAMLBackend(SAMLBackend):
"""
Expand Down Expand Up @@ -390,6 +379,39 @@ def authn_request(self, context, entity_id):
context.state, "Failed to construct the AuthnRequest"
) from exc


def handle_error(self, message:str, troubleshoot:str='',
err='', template_path='templates',
template_name='spid_login_error.html'):
"""
Todo: Jinja2 tempalte loader and rendering :)
"""
logger.error(f"Failed to parse authn request: {message} {err}")
loader = Environment(
loader=FileSystemLoader(searchpath=template_path),
autoescape=select_autoescape(['html'])
)
loader.globals.update({
'static': self.config['static_storage_url'],
})
template = loader.get_template(template_name)
result = template.render({
'message': message,
'troubleshoot': troubleshoot

})
# msg = (
# f'<b>{message}</b><br>'
# f'{troubleshoot}'
# )
# text_type(msg).encode('utf-8')
return Response(result, content="text/html; charset=utf8")


def handle_spid_anomaly(self, err_number, err):
return self.handle_error(**SPID_ANOMALIES[int(err_number)])


def authn_response(self, context, binding):
"""
Endpoint for the idp response
Expand All @@ -411,10 +433,20 @@ def authn_response(self, context, binding):
binding, outstanding=self.outstanding_queries)
except StatusAuthnFailed as err:
erdict = re.search(
r'ErrorCode nr(?P<err_code>\d+)', str(err)).groupdict()
return handle_spid_anomaly(erdict['err_code'], err)
r'ErrorCode nr(?P<err_code>\d+)', str(err))
if erdict:
return self.handle_spid_anomaly(erdict.groupdict()['err_code'], err)
else:
return self.handle_error(
**{
'err': err,
'message': 'Autenticazione fallita',
'troubleshoot': "Anomalia riscontrata durante la fase di Autenticazione."
" Contattare il supporto tecnico per eventuali chiarimenti"
}
)
except SignatureError as err:
return handle_error(
return self.handle_error(
**{
'err': err,
'message': 'Autenticazione fallita',
Expand All @@ -423,7 +455,7 @@ def authn_response(self, context, binding):
}
)
except Exception as err:
return handle_error(
return self.handle_error(
**{
'err': err,
'message': 'Anomalia riscontrata nel processo di Autenticazione',
Expand All @@ -436,7 +468,7 @@ def authn_response(self, context, binding):
if req_id not in self.outstanding_queries:
errmsg = "No request with id: {}".format(req_id),
logger.debug(errmsg)
return handle_error(
return self.handle_error(
**{
'message': errmsg,
'troubleshoot': _TROUBLESHOOT_MSG
Expand All @@ -448,7 +480,7 @@ def authn_response(self, context, binding):
if not context.state.get(self.name):
_msg = f"context.state[self.name] KeyError: where self.name is {self.name}"
logger.error(_msg)
return handle_error(
return self.handle_error(
**{
'message': _msg,
'troubleshoot': _TROUBLESHOOT_MSG
Expand All @@ -457,7 +489,7 @@ def authn_response(self, context, binding):
# check if the relay_state matches the cookie state
if context.state[self.name]["relay_state"] != context.request["RelayState"]:
_msg = "State did not match relay state for state"
return handle_error(
return self.handle_error(
**{
'message': _msg,
'troubleshoot': _TROUBLESHOOT_MSG
Expand All @@ -475,7 +507,7 @@ def authn_response(self, context, binding):
# this will get the entity name in state
if len(context.state.keys()) < 2:
_msg = "Inconsistent context.state"
return handle_error(
return self.handle_error(
**{
'message': _msg,
'troubleshoot': _TROUBLESHOOT_MSG
Expand Down Expand Up @@ -512,7 +544,7 @@ def authn_response(self, context, binding):

except Exception as e:
logger.error(e)
return handle_error(e)
return self.handle_error(e)

context.decorate(Context.KEY_BACKEND_METADATA_STORE, self.sp.metadata)
if self.config.get(SAMLBackend.KEY_MEMORIZE_IDP):
Expand Down
Loading

0 comments on commit ad5458d

Please sign in to comment.