From f510c806de3e20cdedd55291cd58dafa59398bec Mon Sep 17 00:00:00 2001
From: Gage Hugo <gagehugo@gmail.com>
Date: Tue, 27 Oct 2020 15:22:04 -0500
Subject: [PATCH] Hide AccountLocked exception from end users

This change hides the AccountLocked exception from being returned
to the end user to hide sensitive information that a potential
malicious person could gain insight from.

The notification handler catches the AccountLocked exception as
before, but after sending the audit notification, it instead
bubbles up Unauthorized rather than AccountLocked.

Co-Authored-By: Samuel de Medeiros Queiroz <samueldmq@gmail.com>

Change-Id: Id51241989b22c52810391f3e8e1cadbf8613d873
Related-Bug: #1688137
(cherry picked from commit ac2631ae33445877094cdae796fbcdce8833a626)
---

diff --git a/keystone/notifications.py b/keystone/notifications.py
index e536ebd..a59b1d0 100644
--- a/keystone/notifications.py
+++ b/keystone/notifications.py
@@ -580,6 +580,8 @@
                                          taxonomy.OUTCOME_FAILURE,
                                          target, self.event_type,
                                          reason=audit_reason)
+                if isinstance(ex, exception.AccountLocked):
+                    raise exception.Unauthorized
                 raise
             except Exception:
                 # For authentication failure send a CADF event as well
diff --git a/keystone/tests/unit/common/test_notifications.py b/keystone/tests/unit/common/test_notifications.py
index b0fb720..308cc01 100644
--- a/keystone/tests/unit/common/test_notifications.py
+++ b/keystone/tests/unit/common/test_notifications.py
@@ -802,7 +802,7 @@
         password = uuid.uuid4().hex
         new_password = uuid.uuid4().hex
         expected_responses = [AssertionError, AssertionError, AssertionError,
-                              exception.AccountLocked]
+                              exception.Unauthorized]
         user_ref = unit.new_user_ref(domain_id=self.domain_id,
                                      password=password)
         user_ref = PROVIDERS.identity_api.create_user(user_ref)
diff --git a/keystone/tests/unit/identity/test_backend_sql.py b/keystone/tests/unit/identity/test_backend_sql.py
index 8c7fb31..0a99002 100644
--- a/keystone/tests/unit/identity/test_backend_sql.py
+++ b/keystone/tests/unit/identity/test_backend_sql.py
@@ -613,7 +613,7 @@
             )
             # test locking out user after max failed attempts
             self._fail_auth_repeatedly(self.user['id'])
-            self.assertRaises(exception.AccountLocked,
+            self.assertRaises(exception.Unauthorized,
                               PROVIDERS.identity_api.authenticate,
                               user_id=self.user['id'],
                               password=uuid.uuid4().hex)
@@ -642,7 +642,7 @@
         with self.make_request():
             # lockout user
             self._fail_auth_repeatedly(self.user['id'])
-            self.assertRaises(exception.AccountLocked,
+            self.assertRaises(exception.Unauthorized,
                               PROVIDERS.identity_api.authenticate,
                               user_id=self.user['id'],
                               password=uuid.uuid4().hex)
@@ -661,7 +661,7 @@
             with self.make_request():
                 # lockout user
                 self._fail_auth_repeatedly(self.user['id'])
-                self.assertRaises(exception.AccountLocked,
+                self.assertRaises(exception.Unauthorized,
                                   PROVIDERS.identity_api.authenticate,
                                   user_id=self.user['id'],
                                   password=uuid.uuid4().hex)
@@ -687,7 +687,7 @@
             with self.make_request():
                 # lockout user
                 self._fail_auth_repeatedly(self.user['id'])
-                self.assertRaises(exception.AccountLocked,
+                self.assertRaises(exception.Unauthorized,
                                   PROVIDERS.identity_api.authenticate,
                                   user_id=self.user['id'],
                                   password=uuid.uuid4().hex)
@@ -697,7 +697,7 @@
                 # repeat failed auth the max times
                 self._fail_auth_repeatedly(self.user['id'])
                 # test user account is locked
-                self.assertRaises(exception.AccountLocked,
+                self.assertRaises(exception.Unauthorized,
                                   PROVIDERS.identity_api.authenticate,
                                   user_id=self.user['id'],
                                   password=uuid.uuid4().hex)
diff --git a/releasenotes/notes/bug-1688137-e4203c9a728690a7.yaml b/releasenotes/notes/bug-1688137-e4203c9a728690a7.yaml
new file mode 100644
index 0000000..bd7a060
--- /dev/null
+++ b/releasenotes/notes/bug-1688137-e4203c9a728690a7.yaml
@@ -0,0 +1,8 @@
+---
+fixes:
+  - |
+    [`bug 1688137 <https://bugs.launchpad.net/keystone/+bug/1688137>`_]
+    Fixed the AccountLocked exception being shown to the end user since
+    it provides some information that could be exploited by a
+    malicious user. The end user will now see Unauthorized instead of
+    AccountLocked, preventing user info oracle exploitation.
