From dfc1252ded8411ad17b662ab6c8b178272aa16d4 Mon Sep 17 00:00:00 2001
From: "Antoine R. Dumont (@ardumont)" <ardumont@softwareheritage.org>
Date: Thu, 4 Mar 2021 17:54:57 +0100
Subject: [PATCH] keycloak: Open direct grant login endpoint

This is needed for the incoming deposit-keycloak integration.

Related to T3079
---
 swh/auth/keycloak.py            | 20 ++++++++++++++++++++
 swh/auth/pytest_plugin.py       |  2 ++
 swh/auth/tests/test_keycloak.py |  5 +++++
 3 files changed, 27 insertions(+)

diff --git a/swh/auth/keycloak.py b/swh/auth/keycloak.py
index 53e8601..72d243c 100644
--- a/swh/auth/keycloak.py
+++ b/swh/auth/keycloak.py
@@ -83,6 +83,26 @@ class KeycloakOpenIDConnect:
             **extra_params,
         )
 
+    def login(
+        self, username: str, password: str, **extra_params: str
+    ) -> Dict[str, Any]:
+        """
+        Get OpenID Connect authentication tokens using Direct Access Grant flow.
+
+        Args:
+            username: an existing username in the realm
+            password: password associated to username
+            extra_params: Extra parameters to add in the authorization request
+                payload.
+        """
+        return self._keycloak.token(
+            grant_type="password",
+            scope="openid",
+            username=username,
+            password=password,
+            **extra_params,
+        )
+
     def refresh_token(self, refresh_token: str) -> Dict[str, Any]:
         """
         Request a new access token from Keycloak using a refresh token.
diff --git a/swh/auth/pytest_plugin.py b/swh/auth/pytest_plugin.py
index f585fdf..f4dc495 100644
--- a/swh/auth/pytest_plugin.py
+++ b/swh/auth/pytest_plugin.py
@@ -124,12 +124,14 @@ class KeycloackOpenIDConnectMock(KeycloakOpenIDConnect):
         # method "Cannot assign to a method affecting mock". Ignore for now.
         self.authorization_code = Mock()  # type: ignore
         self.refresh_token = Mock()  # type: ignore
+        self.login = Mock()  # type: ignore
         self.userinfo = Mock()  # type: ignore
         self.logout = Mock()  # type: ignore
         self.auth_success = auth_success
         if auth_success:
             self.authorization_code.return_value = copy(oidc_profile)
             self.refresh_token.return_value = copy(oidc_profile)
+            self.login.return_value = copy(oidc_profile)
             self.userinfo.return_value = copy(user_info)
         else:
             self.authorization_url = Mock()  # type: ignore
diff --git a/swh/auth/tests/test_keycloak.py b/swh/auth/tests/test_keycloak.py
index 15d5caf..5cf774e 100644
--- a/swh/auth/tests/test_keycloak.py
+++ b/swh/auth/tests/test_keycloak.py
@@ -93,3 +93,8 @@ def test_keycloak_decode_token(keycloak_mock):
         expected_decoded_token.pop(dynamic_valued_key)
 
     assert actual_decoded_data2 == expected_decoded_token
+
+
+def test_keycloak_login(keycloak_mock):
+    actual_response = keycloak_mock.login("username", "password")
+    assert actual_response == OIDC_PROFILE
-- 
GitLab