From a3dc0e18eb77af0f85499cf0d1fa506da02f3981 Mon Sep 17 00:00:00 2001
From: Nicolas ROCHE <nroche@entrouvert.com>
Date: Wed, 23 Jun 2021 18:16:47 +0200
Subject: [PATCH] views: manage default evaluation result on context failure
 (#55127)

---
 src/authentic2/authenticators.py |  7 ++++++-
 tests/test_login.py              | 33 ++++++++++++++++++++++++--------
 2 files changed, 31 insertions(+), 9 deletions(-)

diff --git a/src/authentic2/authenticators.py b/src/authentic2/authenticators.py
index 0e8e46ad..d43adfa0 100644
--- a/src/authentic2/authenticators.py
+++ b/src/authentic2/authenticators.py
@@ -45,18 +45,23 @@ class BaseAuthenticator(object):
         else:
             return self.show_condition
 
     def shown(self, instance_id=None, ctx=()):
         show_condition = self.get_show_condition(instance_id)
         if not show_condition:
             return True
         ctx = dict(ctx, id=instance_id)
+        # Evaluation failure will return the result of the backend enabled method.
+        # If no enabled method is defined on the backend, evaluation failure will success.
+        on_raise = True
+        if hasattr(self, 'enabled'):
+            on_raise = bool(self.enabled())
         try:
-            return evaluate_condition(show_condition, ctx)
+            return evaluate_condition(show_condition, ctx, on_raise=on_raise)
         except Exception as e:
             logger.error(e)
             return False
 
 
 class LoginPasswordAuthenticator(BaseAuthenticator):
     id = 'password'
     submit_name = 'login-password-submit'
diff --git a/tests/test_login.py b/tests/test_login.py
index 8fb274aa..590929c5 100644
--- a/tests/test_login.py
+++ b/tests/test_login.py
@@ -62,41 +62,58 @@ def test_login_inactive_user(db, app):
     assert '_auth_user_id' not in app.session
     user2.is_active = False
     user2.save()
     with pytest.raises(AssertionError):
         login(app, user1)
     assert '_auth_user_id' not in app.session
 
 
-def test_show_condition(db, app, settings, caplog):
+@pytest.mark.parametrize('enabled', [False, True])
+def test_show_condition(db, app, settings, caplog, enabled):
+    settings.A2_AUTH_PASSWORD_ENABLE = enabled
     response = app.get('/login/')
-    assert 'name="login-password-submit"' in response
+    if enabled:
+        assert 'name="login-password-submit"' in response
+    else:
+        assert 'name="login-password-submit"' not in response
 
     settings.AUTH_FRONTENDS_KWARGS = {'password': {'show_condition': 'False'}}
     response = app.get('/login/')
     # login form must not be displayed
     assert 'name="login-password-submit"' not in response
     assert len(caplog.records) == 0
     # set a condition with error
-    with check_log(caplog, 'name \'unknown\' is not defined'):
-        settings.AUTH_FRONTENDS_KWARGS = {'password': {'show_condition': '\'admin\' in unknown'}}
-        response = app.get('/login/')
+
+    settings.AUTH_FRONTENDS_KWARGS = {'password': {'show_condition': '\'admin\' in unknown'}}
+    response = app.get('/login/')
+    if enabled:
+        assert 'name="login-password-submit"' in response
+    else:
         assert 'name="login-password-submit"' not in response
+    assert len(caplog.records) == 0
 
 
-def test_show_condition_service(db, app, settings):
+@pytest.mark.parametrize('enabled', [False, True])
+def test_show_condition_service(db, app, settings, enabled):
+    settings.A2_AUTH_PASSWORD_ENABLE = enabled
     settings.AUTH_FRONTENDS_KWARGS = {'password': {'show_condition': 'service_slug == \'portal\''}}
     response = app.get('/login/', params={'service': 'portal'})
-    assert 'name="login-password-submit"' not in response
+    if enabled:
+        assert 'name="login-password-submit"' in response
+    else:
+        assert 'name="login-password-submit"' not in response
 
     # Create a service
     models.Service.objects.create(name='Service', slug='portal')
     response = app.get('/login/', params={'service': 'portal'})
-    assert 'name="login-password-submit"' in response
+    if enabled:
+        assert 'name="login-password-submit"' in response
+    else:
+        assert 'name="login-password-submit"' not in response
 
 
 def test_show_condition_with_headers(app, settings):
     settings.A2_AUTH_OIDC_ENABLE = False  # prevent db access by OIDC frontend
     settings.AUTH_FRONTENDS_KWARGS = {'password': {'show_condition': '\'X-Entrouvert\' in headers'}}
     response = app.get('/login/')
     assert 'name="login-password-submit"' not in response
     response = app.get('/login/', headers={'x-entrouvert': '1'})
-- 
2.30.2

