Skip to content

Commit 2d7d999

Browse files
authoredMar 3, 2025··
Support Keycloak IdP Discovery
Add support for IdP Discovery pages based on Keycloak in login checks
2 parents 39ad4cc + 4fa981f commit 2d7d999

File tree

3 files changed

+63
-7
lines changed

3 files changed

+63
-7
lines changed
 

‎rciam_probes.spec

+3-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
Name: rciam_probes
99
Summary: RCIAM related probes - Complete
1010
Group: grnet/rciam
11-
Version: 2.3.0
11+
Version: 2.4.0
1212
Release: %(echo $GIT_COMMIT_DATE).%(echo $GIT_COMMIT_HASH)%{?dist}
1313
Url: https://github.com/rciam/%{name}
1414
License: Apache-2.0
@@ -135,6 +135,8 @@ rm -rf $RPM_BUILD_ROOT
135135
#fi
136136

137137
%changelog
138+
* Mon Mar 3 2025 Nicolas Liampotis <nliam@grnet.gr> 2.4.0
139+
- Added support for IdP Discovery pages based on Keycloak in login checks
138140
* Wed Sep 3 2024 Nicolas Liampotis <nliam@grnet.gr> 2.3.0
139141
- Added support for IdP Discovery pages based on thiss.io in login checks
140142
* Wed Aug 7 2024 Nicolas Liampotis <nliam@grnet.gr> 2.2.0

‎rciam_probes/probes/checkhealth/checkhealth.py

+59-5
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ def __sp_redirect_disco_n_click(self):
127127
self.__logger.debug('Skipping IdP discovery page')
128128
return
129129

130-
# Detect the discovery type (ssp, thiss)
130+
# Detect the discovery type (thiss, keycloak, or ssp)
131131
disco_type = self.__detect_disco_type()
132132
self.__logger.debug(f'Discovery Service type detected: {disco_type}')
133133

@@ -181,21 +181,75 @@ def __sp_redirect_disco_n_click(self):
181181
# Click the search result
182182
self.__browser.find_element(By.CSS_SELECTOR, result_selector).click()
183183

184+
# Handle Keycloak discovery service with search box
185+
elif disco_type == "keycloak":
186+
for i, idp in enumerate(idp_list):
187+
# Determine search term (use idp_name_list if provided, else extract from idp)
188+
if idp_name_list and i < len(idp_name_list):
189+
search_term = idp_name_list[i]
190+
else:
191+
search_term = urlparse(idp).hostname
192+
193+
self.__logger.debug(f'Searching for IdP: {search_term}')
194+
# Locate the search box
195+
search_box = self.__browser.find_element(By.ID, "kc-providers-filter")
196+
# Ensure the element is clickable or interactable
197+
self.__wait.until(EC.element_to_be_clickable((By.ID, "kc-providers-filter")))
198+
search_box.clear()
199+
search_box.send_keys(search_term)
200+
201+
# Wait for spinner to disappear and results to appear
202+
retries = 3
203+
for attempt in range(retries):
204+
try:
205+
# Wait for spinner to hide (if present)
206+
spinner = self.__browser.find_elements(By.ID, "spinner")
207+
if spinner:
208+
self.__wait.until(lambda driver: "hidden" in driver.find_element(By.ID, "spinner").get_attribute("class"))
209+
# Wait for any IdP button
210+
self.__wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, "a.pf-c-button.kc-social-item")))
211+
# Log found buttons
212+
elements = self.__browser.find_elements(By.CSS_SELECTOR, "a.pf-c-button.kc-social-item")
213+
self.__logger.debug(f"Found {len(elements)} IdP buttons: {[e.text for e in elements]}")
214+
break
215+
except (NoSuchElementException, TimeoutException) as e:
216+
if attempt == retries - 1:
217+
self.__logger.error(f"Failed to load IdP list after {retries} attempts: {str(e)}")
218+
raise RuntimeError("Keycloak IdP list not loaded")
219+
self.__logger.debug(f"Retry {attempt + 1}/{retries} waiting for IdP list: {str(e)}")
220+
time.sleep(3)
221+
222+
# Locate the desired result
223+
result_selector = f"//a[contains(@class, 'pf-c-button') and contains(@class, 'kc-social-item') and .//span[contains(text(), '{search_term}')]]"
224+
try:
225+
self.__wait.until(EC.element_to_be_clickable((By.XPATH, result_selector)))
226+
except (NoSuchElementException, TimeoutException) as e:
227+
self.__logger.error(f"Could not find IdP '{idp}' after searching '{search_term}' in Keycloak: {str(e)}")
228+
raise RuntimeError(f"IdP '{idp}' not found in Keycloak discovery service")
229+
230+
self.__browser.find_element(By.XPATH, result_selector).click()
231+
184232
else:
185233
raise RuntimeError('Unsupported Discovery Service type')
186234

187235
except TimeoutException:
188236
raise RuntimeError('Discovery Service timeout')
189237

190238
def __detect_disco_type(self):
191-
"""Detect whether the discovery type is thiss.io or SimpleSAMLphp."""
239+
"""Detect whether the discovery type is thiss.io, Keycloak or SimpleSAMLphp (default)."""
192240
try:
193241
# Check for elements unique to thiss.io
194242
self.__browser.find_element(By.ID, "searchinput")
195243
return "thiss"
196-
except:
197-
# Fallback to SSP if thiss.io element is not found
198-
return "ssp"
244+
except NoSuchElementException:
245+
try:
246+
# Check for Keycloak (login-pf-page and kc-header)
247+
if (self.__browser.find_element(By.CLASS_NAME, "login-pf-page") and
248+
self.__browser.find_element(By.ID, "kc-header")):
249+
return "keycloak"
250+
except NoSuchElementException:
251+
# Fallback to SimpleSAMLphp
252+
return "ssp"
199253

200254
def __accept_all_ssp_modules(self):
201255
"""

‎setup.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
from os import path
77

88
__name__ = 'rciam_probes'
9-
__version__ = '2.3.0'
9+
__version__ = '2.4.0'
1010

1111
here = path.abspath(path.dirname(__file__))
1212

0 commit comments

Comments
 (0)
Please sign in to comment.