Skip to content

Commit 430278e

Browse files
CopilotSubterrane
andcommitted
Improve SAML2 compatibility to handle namespace conflicts when python3-saml is installed
Co-authored-by: Subterrane <5290140+Subterrane@users.noreply.github.com>
1 parent cb9bd61 commit 430278e

File tree

2 files changed

+91
-38
lines changed

2 files changed

+91
-38
lines changed

onelogin/saml2/auth.py

Lines changed: 46 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -3,50 +3,63 @@
33
"""
44
SAML2 Authentication Module
55
6-
OneLogin_Saml2_Auth is not part of this package. This package (onelogin) is
7-
for OneLogin API management. For SAML2 authentication, you need the
8-
'python3-saml' package.
6+
This module provides compatibility for users expecting to import
7+
OneLogin_Saml2_Auth from onelogin.saml2.auth. However, OneLogin_Saml2_Auth is
8+
actually provided by the 'python3-saml' package, not this onelogin package.
99
10-
To fix this import error:
11-
12-
1. Install the correct package:
13-
pip install python3-saml
14-
15-
2. Import from the correct module:
16-
from onelogin.saml2.auth import OneLogin_Saml2_Auth
17-
18-
OR use the newer syntax:
19-
from onelogin.saml2.auth import Auth as OneLogin_Saml2_Auth
20-
21-
For more information, see: https://github.com/onelogin/python3-saml
10+
This onelogin package is for OneLogin API management, while python3-saml is
11+
for SAML2 authentication integration.
2212
"""
2313

14+
import os
15+
import site
16+
2417

25-
class ImportError(Exception):
26-
"""Custom import error with helpful message"""
27-
pass
18+
def _check_python3_saml_installed():
19+
"""Check if python3-saml is installed by looking for its onelogin.saml2.auth module"""
20+
for site_dir in site.getsitepackages() + [site.getusersitepackages()]:
21+
if site_dir and os.path.exists(site_dir):
22+
potential_path = os.path.join(site_dir, 'onelogin', 'saml2', 'auth.py')
23+
if os.path.exists(potential_path):
24+
return True
25+
return False
2826

2927

3028
def __getattr__(name):
3129
"""
3230
Intercept attempts to import OneLogin_Saml2_Auth and provide helpful
33-
error message.
31+
error message based on whether python3-saml is detected.
3432
"""
3533
if name == "OneLogin_Saml2_Auth":
36-
raise ImportError(
37-
"OneLogin_Saml2_Auth is not available in this package.\n\n"
38-
"This package (onelogin) is for OneLogin API management.\n"
39-
"For SAML2 authentication, you need the 'python3-saml' "
40-
"package.\n\n"
41-
"To fix this:\n"
42-
"1. Install the correct package: pip install python3-saml\n"
43-
"2. Import from: from onelogin.saml2.auth import "
44-
"OneLogin_Saml2_Auth\n\n"
45-
"Note: You may need to uninstall this 'onelogin' package if you "
46-
"only need SAML2 auth:\n"
47-
"pip uninstall onelogin\n\n"
48-
"For more information: https://github.com/onelogin/python3-saml"
49-
)
34+
python3_saml_installed = _check_python3_saml_installed()
35+
36+
if python3_saml_installed:
37+
# python3-saml is installed but there's a namespace conflict
38+
raise ImportError(
39+
"OneLogin_Saml2_Auth is not available due to a package namespace conflict.\n\n"
40+
"You have both 'onelogin' (API management) and 'python3-saml' (SAML authentication) installed.\n"
41+
"These packages both provide 'onelogin' modules, causing a conflict.\n\n"
42+
"To fix this conflict:\n\n"
43+
"OPTION 1 - If you only need SAML authentication:\n"
44+
" 1. Uninstall this package: pip uninstall onelogin\n"
45+
" 2. Then import will work: from onelogin.saml2.auth import OneLogin_Saml2_Auth\n\n"
46+
"OPTION 2 - If you need both packages:\n"
47+
" 1. Import directly from python3-saml's location in site-packages\n"
48+
" 2. See: https://github.com/onelogin/python3-saml for documentation\n\n"
49+
"OPTION 3 - Use virtual environments to separate the packages\n\n"
50+
"To check your installations: pip list | grep -E '(onelogin|python3-saml)'"
51+
)
52+
else:
53+
# python3-saml is not installed
54+
raise ImportError(
55+
"OneLogin_Saml2_Auth is not available in this package.\n\n"
56+
"This package (onelogin) is for OneLogin API management.\n"
57+
"For SAML2 authentication, you need the 'python3-saml' package.\n\n"
58+
"To fix this:\n"
59+
"1. Install the correct package: pip install python3-saml\n"
60+
"2. Import from: from onelogin.saml2.auth import OneLogin_Saml2_Auth\n\n"
61+
"For more information: https://github.com/onelogin/python3-saml"
62+
)
5063

5164
raise AttributeError(
5265
f"module 'onelogin.saml2.auth' has no attribute '{name}'"

test/test_saml2_import_compatibility.py

Lines changed: 45 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@
55
"""
66

77
import pytest
8-
from onelogin.saml2.auth import ImportError
8+
import os
9+
import site
910

1011

1112
class TestSaml2ImportCompatibility:
@@ -20,10 +21,21 @@ def test_import_onelogin_saml2_auth_raises_helpful_error(self):
2021

2122
# Verify the error message contains helpful guidance
2223
error_message = str(exc_info.value)
23-
assert "OneLogin_Saml2_Auth is not available in this package" in error_message
24+
assert "OneLogin_Saml2_Auth is not available" in error_message
2425
assert "python3-saml" in error_message
25-
assert "pip install python3-saml" in error_message
26-
assert "https://github.com/onelogin/python3-saml" in error_message
26+
27+
# The specific message depends on whether python3-saml is detected
28+
python3_saml_installed = self._check_python3_saml_installed()
29+
30+
if python3_saml_installed:
31+
# Should show namespace conflict message
32+
assert "namespace conflict" in error_message
33+
assert "both 'onelogin'" in error_message
34+
assert "pip uninstall onelogin" in error_message
35+
else:
36+
# Should show installation instruction message
37+
assert "pip install python3-saml" in error_message
38+
assert "https://github.com/onelogin/python3-saml" in error_message
2739

2840
def test_getattr_for_nonexistent_attribute_raises_attribute_error(self):
2941
"""Test that accessing non-existent attributes raises AttributeError"""
@@ -52,4 +64,32 @@ def test_saml2_auth_module_has_empty_all_list(self):
5264

5365
import onelogin.saml2.auth
5466
assert hasattr(onelogin.saml2.auth, '__all__')
55-
assert onelogin.saml2.auth.__all__ == []
67+
assert onelogin.saml2.auth.__all__ == []
68+
69+
def test_python3_saml_detection_function(self):
70+
"""Test that the python3-saml detection function works correctly"""
71+
72+
import onelogin.saml2.auth
73+
result = onelogin.saml2.auth._check_python3_saml_installed()
74+
# Should return a boolean
75+
assert isinstance(result, bool)
76+
77+
# Test by checking if the expected path exists manually
78+
expected_exists = False
79+
for site_dir in site.getsitepackages() + [site.getusersitepackages()]:
80+
if site_dir and os.path.exists(site_dir):
81+
potential_path = os.path.join(site_dir, 'onelogin', 'saml2', 'auth.py')
82+
if os.path.exists(potential_path):
83+
expected_exists = True
84+
break
85+
86+
assert result == expected_exists
87+
88+
def _check_python3_saml_installed(self):
89+
"""Helper method to check if python3-saml is installed"""
90+
for site_dir in site.getsitepackages() + [site.getusersitepackages()]:
91+
if site_dir and os.path.exists(site_dir):
92+
potential_path = os.path.join(site_dir, 'onelogin', 'saml2', 'auth.py')
93+
if os.path.exists(potential_path):
94+
return True
95+
return False

0 commit comments

Comments
 (0)