show an error page when create_server fails (#57176)
This commit is contained in:
parent
3f32879520
commit
4941fd7281
|
@ -35,6 +35,10 @@ from . import app_settings
|
|||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class CreateServerError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
def create_metadata(request):
|
||||
entity_id = reverse('mellon_metadata')
|
||||
login_url = reverse(app_settings.LOGIN_URL)
|
||||
|
@ -84,6 +88,8 @@ def create_server(request):
|
|||
server = lasso.Server.newFromBuffers(
|
||||
metadata, private_key_content=private_key, private_key_password=private_key_password
|
||||
)
|
||||
if not server:
|
||||
raise CreateServerError
|
||||
if app_settings.SIGNATURE_METHOD:
|
||||
symbol_name = 'SIGNATURE_METHOD_' + app_settings.SIGNATURE_METHOD.replace('-', '_').upper()
|
||||
if hasattr(lasso, symbol_name):
|
||||
|
|
|
@ -132,6 +132,45 @@ class ProfileMixin:
|
|||
args.append(idp_message)
|
||||
self.log.warning(*args)
|
||||
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
try:
|
||||
return super().dispatch(request, *args, **kwargs)
|
||||
except utils.CreateServerError:
|
||||
return self.failure(
|
||||
request,
|
||||
reason=_(
|
||||
'Unable to initialize a SAML server object, the private key '
|
||||
'is maybe invalid or unreadable, please check its access '
|
||||
'rights and content.'
|
||||
),
|
||||
)
|
||||
|
||||
def failure(self, request, reason='', status_codes=()):
|
||||
'''show error message to user after a login failure'''
|
||||
login = self.profile
|
||||
idp = utils.get_idp(login and login.remoteProviderId)
|
||||
if not idp and login:
|
||||
self.log.warning('entity id %r is unknown', login.remoteProviderId)
|
||||
return HttpResponseBadRequest('entity id %r is unknown' % login.remoteProviderId)
|
||||
error_url = utils.get_setting(idp, 'ERROR_URL')
|
||||
error_redirect_after_timeout = utils.get_setting(idp, 'ERROR_REDIRECT_AFTER_TIMEOUT')
|
||||
if error_url:
|
||||
error_url = resolve_url(error_url)
|
||||
next_url = error_url or self.get_next_url(default=resolve_url(settings.LOGIN_REDIRECT_URL))
|
||||
return self.render(
|
||||
request,
|
||||
'mellon/authentication_failed.html',
|
||||
{
|
||||
'debug': settings.DEBUG,
|
||||
'reason': reason,
|
||||
'status_codes': status_codes,
|
||||
'issuer': login and login.remoteProviderId,
|
||||
'next_url': next_url,
|
||||
'relaystate': login and login.msgRelayState,
|
||||
'error_redirect_after_timeout': error_redirect_after_timeout,
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
class LoginView(ProfileMixin, LogMixin, View):
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
|
@ -197,33 +236,7 @@ class LoginView(ProfileMixin, LogMixin, View):
|
|||
if 'RelayState' in request.POST and utils.is_nonnull(request.POST['RelayState']):
|
||||
login.msgRelayState = request.POST['RelayState']
|
||||
return self.sso_success(request, login)
|
||||
return self.sso_failure(request, reason=idp_message, status_codes=status_codes)
|
||||
|
||||
def sso_failure(self, request, reason='', status_codes=()):
|
||||
'''show error message to user after a login failure'''
|
||||
login = self.profile
|
||||
idp = utils.get_idp(login.remoteProviderId)
|
||||
if not idp:
|
||||
self.log.warning('entity id %r is unknown', login.remoteProviderId)
|
||||
return HttpResponseBadRequest('entity id %r is unknown' % login.remoteProviderId)
|
||||
error_url = utils.get_setting(idp, 'ERROR_URL')
|
||||
error_redirect_after_timeout = utils.get_setting(idp, 'ERROR_REDIRECT_AFTER_TIMEOUT')
|
||||
if error_url:
|
||||
error_url = resolve_url(error_url)
|
||||
next_url = error_url or self.get_next_url(default=resolve_url(settings.LOGIN_REDIRECT_URL))
|
||||
return self.render(
|
||||
request,
|
||||
'mellon/authentication_failed.html',
|
||||
{
|
||||
'debug': settings.DEBUG,
|
||||
'reason': reason,
|
||||
'status_codes': status_codes,
|
||||
'issuer': login.remoteProviderId,
|
||||
'next_url': next_url,
|
||||
'relaystate': login.msgRelayState,
|
||||
'error_redirect_after_timeout': error_redirect_after_timeout,
|
||||
},
|
||||
)
|
||||
return self.failure(request, reason=idp_message, status_codes=status_codes)
|
||||
|
||||
def get_attribute_value(self, attribute, attribute_value):
|
||||
# check attribute_value contains only text
|
||||
|
@ -340,7 +353,7 @@ class LoginView(ProfileMixin, LogMixin, View):
|
|||
Use a cookie to prevent looping forever.
|
||||
"""
|
||||
if RETRY_LOGIN_COOKIE in self.request.COOKIES:
|
||||
response = self.sso_failure(
|
||||
response = self.failure(
|
||||
self.request, reason=_('There were too many redirections with the identity provider.')
|
||||
)
|
||||
response.delete_cookie(RETRY_LOGIN_COOKIE)
|
||||
|
@ -392,7 +405,7 @@ class LoginView(ProfileMixin, LogMixin, View):
|
|||
)
|
||||
except RequestException as e:
|
||||
self.log.warning('unable to reach %r: %s', login.msgUrl, e)
|
||||
return self.sso_failure(
|
||||
return self.failure(
|
||||
request,
|
||||
reason=_('IdP is temporarily down, please try again ' 'later.'),
|
||||
status_codes=status_codes,
|
||||
|
@ -403,7 +416,7 @@ class LoginView(ProfileMixin, LogMixin, View):
|
|||
result.status_code,
|
||||
result.content,
|
||||
)
|
||||
return self.sso_failure(request, reason=idp_message, status_codes=status_codes)
|
||||
return self.failure(request, reason=idp_message, status_codes=status_codes)
|
||||
|
||||
self.log.info('Got SAML Artifact Response', extra={'saml_response': result.content})
|
||||
result.encoding = utils.get_xml_encoding(result.content)
|
||||
|
@ -447,7 +460,7 @@ class LoginView(ProfileMixin, LogMixin, View):
|
|||
return HttpResponseBadRequest('error processing the authentication response: %r' % e)
|
||||
else:
|
||||
return self.sso_success(request, login)
|
||||
return self.sso_failure(request, reason=idp_message, status_codes=status_codes)
|
||||
return self.failure(request, reason=idp_message, status_codes=status_codes)
|
||||
|
||||
def request_discovery_service(self, request, is_passive=False):
|
||||
return_url = request.build_absolute_uri()
|
||||
|
|
|
@ -294,3 +294,19 @@ def test_invalid_msg_on_artifact_resolve(private_settings, client, caplog, artif
|
|||
with HTTMock(html_response):
|
||||
client.get('/login/?SAMLart=%s' % artifact)
|
||||
assert 'ArtifactResolveResponse is malformed' in caplog.text
|
||||
|
||||
|
||||
def test_private_key_unreadable(private_settings, app, tmpdir):
|
||||
private_settings.MELLON_IDENTITY_PROVIDERS = [
|
||||
{
|
||||
'METADATA': open('tests/metadata.xml').read(),
|
||||
}
|
||||
]
|
||||
# set an unreadable private key
|
||||
private_key = tmpdir / 'private.key'
|
||||
with private_key.open(mode='w') as fd:
|
||||
fd.write('1')
|
||||
private_key.chmod(0o000)
|
||||
private_settings.MELLON_PRIVATE_KEY = str(private_key)
|
||||
response = app.get('/login/?next=%2Fwhatever')
|
||||
assert 'Unable to initialize a SAML server object' in response
|
||||
|
|
Loading…
Reference in New Issue