forked from OCA/server-auth
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathtest_auth_oidc_logout.py
136 lines (117 loc) · 6.04 KB
/
test_auth_oidc_logout.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
# Copyright 2021 ACSONE SA/NV <https://acsone.eu>
# License: AGPL-3.0 or later (http://www.gnu.org/licenses/agpl)
import contextlib
import logging
from unittest.mock import Mock
from urllib.parse import parse_qsl, urljoin, urlparse
from werkzeug.urls import url_encode
import odoo
from odoo.tests import common
from odoo.tools.misc import DotDict
from odoo.addons.website.tools import MockRequest
from ..controllers.main import OpenIDLogout
BASE_URL = "http://localhost:%s" % odoo.tools.config["http_port"]
CLIENT_ID = "auth_oidc-test"
LOGIN_PATH = "/web/login"
OIDC_BASE_LOGOUT_URL = "http://keycloak"
OIDC_LOGOUT_PATH = "/logout"
logger = logging.getLogger(__name__)
@contextlib.contextmanager
def create_request(env, user_id, user_logout_func):
with MockRequest(env) as request:
request.httprequest.url_root = BASE_URL + "/"
request.session = DotDict(uid=user_id, logout=user_logout_func)
yield request
class TestOpenIDLogout(common.HttpCase):
@staticmethod
def mock_logout_user(keep_db):
logger.info("Logging out user in Odoo")
def setUp(self):
super().setUp()
# search our test provider and bind the demo user to it
self.provider = self.env["auth.oauth.provider"].search(
[("client_id", "=", CLIENT_ID)]
)
self.assertEqual(len(self.provider), 1)
def _prepare_login_test_user(self, provider_id):
user = self.env.ref("base.user_demo")
user.write({"oauth_provider_id": provider_id, "oauth_uid": user.login})
return user
def _set_test_oidc_logout_url(self, end_session_endpoint):
self.provider.write({"end_session_endpoint": end_session_endpoint})
def test_skip_oidc_logout_for_user(self):
"""Test that oidc logout is skipped if user is not associated to a provider"""
user = self._prepare_login_test_user(None)
with create_request(self.env, user.id, self.mock_logout_user):
resp = OpenIDLogout().logout()
self.assertEqual(LOGIN_PATH, resp.location)
def test_skip_oidc_logout_for_all_users(self):
"""
Test that oidc logout is skipped for all users if provider has no logout url
"""
self.assertFalse(self.provider.end_session_endpoint)
user = self._prepare_login_test_user(self.provider)
with create_request(self.env, user.id, self.mock_logout_user):
resp = OpenIDLogout().logout()
self.assertEqual(LOGIN_PATH, resp.location)
def test_oidc_logout(self):
"""Test that oidc logout"""
self._set_test_oidc_logout_url(urljoin(OIDC_BASE_LOGOUT_URL, OIDC_LOGOUT_PATH))
user = self._prepare_login_test_user(self.provider)
mock_session = Mock()
with MockRequest(self.env) as request:
request.httprequest.url_root = BASE_URL + "/"
request.session = mock_session
mock_session.uid = user.id
resp = OpenIDLogout().logout()
self.assertTrue(resp.location.startswith(OIDC_BASE_LOGOUT_URL))
actual_components = urlparse(resp.location)
self.assertEqual(OIDC_LOGOUT_PATH, actual_components.path)
actual_params = dict(parse_qsl(actual_components.query))
self.assertEqual(CLIENT_ID, actual_params["client_id"])
self.assertEqual(
urljoin(BASE_URL, LOGIN_PATH), actual_params["post_logout_redirect_uri"]
)
mock_session.logout.assert_called_once_with(keep_db=True)
def test_oidc_logout_with_params(self):
"""Test that params both in the logout and redirect urls are preserved"""
logout_url_params = {"param_1": 1, "param_2": 2}
oidc_logout_path = f"{OIDC_LOGOUT_PATH}?{url_encode(logout_url_params)}"
logout_url = urljoin(OIDC_BASE_LOGOUT_URL, oidc_logout_path)
self._set_test_oidc_logout_url(logout_url)
user = self._prepare_login_test_user(self.provider)
with create_request(self.env, user.id, self.mock_logout_user):
redirect_path = "{}?{}".format(
LOGIN_PATH, url_encode({"param_3": 3, "param_4": 4})
)
params = {}
params.update(logout_url_params)
params["client_id"] = CLIENT_ID
params["post_logout_redirect_uri"] = urljoin(BASE_URL, redirect_path)
resp = OpenIDLogout().logout(redirect_path)
self.assertTrue(resp.location.startswith(OIDC_BASE_LOGOUT_URL))
actual_components = urlparse(resp.location)
self.assertEqual(OIDC_LOGOUT_PATH, actual_components.path)
actual_params = dict(parse_qsl(actual_components.query))
self.assertEqual(CLIENT_ID, actual_params["client_id"])
self.assertEqual("1", actual_params["param_1"])
self.assertEqual("2", actual_params["param_2"])
post_logout_url = actual_params["post_logout_redirect_uri"]
self.assertTrue(post_logout_url.startswith(BASE_URL))
post_logout_components = urlparse(post_logout_url)
self.assertEqual(LOGIN_PATH, post_logout_components.path)
post_logout_params = dict(parse_qsl(post_logout_components.query))
self.assertEqual("3", post_logout_params["param_3"])
self.assertEqual("4", post_logout_params["param_4"])
def test_oidc_logout_with_absolute_redirect_url(self):
"""Test that oidc logout allows an absolute redirect url"""
self._set_test_oidc_logout_url(urljoin(OIDC_BASE_LOGOUT_URL, OIDC_LOGOUT_PATH))
user = self._prepare_login_test_user(self.provider)
with create_request(self.env, user.id, self.mock_logout_user):
resp = OpenIDLogout().logout(BASE_URL)
self.assertTrue(resp.location.startswith(OIDC_BASE_LOGOUT_URL))
actual_components = urlparse(resp.location)
self.assertEqual(OIDC_LOGOUT_PATH, actual_components.path)
actual_params = dict(parse_qsl(actual_components.query))
self.assertEqual(CLIENT_ID, actual_params["client_id"])
self.assertEqual(BASE_URL, actual_params["post_logout_redirect_uri"])