Skip to content

Commit 8d377e1

Browse files
committed
add(tests): new unit tests for authentication
1 parent 14f3745 commit 8d377e1

2 files changed

Lines changed: 276 additions & 0 deletions

File tree

vulture_os/portal/tests/__init__.py

Whitespace-only changes.
Lines changed: 276 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,276 @@
1+
#!/home/vlt-os/env/bin/python
2+
"""This file is part of Vulture OS.
3+
4+
Vulture OS is free software: you can redistribute it and/or modify
5+
it under the terms of the GNU General Public License as published by
6+
the Free Software Foundation, either version 3 of the License, or
7+
(at your option) any later version.
8+
9+
Vulture OS is distributed in the hope that it will be useful,
10+
but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
GNU General Public License for more details.
13+
14+
You should have received a copy of the GNU General Public License
15+
along with Vulture OS. If not, see http://www.gnu.org/licenses/.
16+
"""
17+
18+
__author__ = "Fabien Amelinck"
19+
__credits__ = []
20+
__license__ = "GPLv3"
21+
__version__ = "4.0.0"
22+
__maintainer__ = "Vulture OS"
23+
__email__ = "contact@vultureproject.org"
24+
__doc__ = 'Tests for Portal'
25+
26+
from django.http import HttpResponse
27+
from django.test import TestCase, Client
28+
from authentication.user_portal.models import get_random_cookie_name
29+
from portal.views.responses import set_portal_cookie, split_domain
30+
from unittest.mock import patch
31+
from uuid import uuid4
32+
33+
class TestSetPortalCookie(TestCase):
34+
TEST_CASE_NAME=f"{__name__}"
35+
def setUp(self):
36+
from applications.backend.models import Backend, Server
37+
from authentication.openid.form import OpenIDRepositoryForm
38+
from authentication.portal_template.models import PortalTemplate
39+
from authentication.user_portal.form import UserAuthenticationForm
40+
from services.frontend.models import Frontend, Listener
41+
from system.cluster.models import Config, Node, NetworkAddress, NetworkInterfaceCard, NetworkAddressNIC
42+
from workflow.models import Workflow
43+
44+
self.global_config = Config.objects.create(
45+
portal_cookie_name=get_random_cookie_name()
46+
)
47+
self.node = Node.objects.create(
48+
name=f"node_test_{self.TEST_CASE_NAME}",
49+
)
50+
self.nic = NetworkInterfaceCard.objects.create(
51+
dev = "vtnet0",
52+
node=self.node,
53+
)
54+
self.netaddr = NetworkAddress.objects.create(
55+
name=f"network_address_test_{self.TEST_CASE_NAME}",
56+
type="alias",
57+
ip="127.127.127.127",
58+
prefix_or_netmask="24",
59+
)
60+
NetworkAddressNIC.objects.create(
61+
nic=self.nic,
62+
network_address=self.netaddr,
63+
)
64+
self.frontend_http = Frontend.objects.create(
65+
name=f"frontend_http_test_{self.TEST_CASE_NAME}",
66+
mode="http",
67+
)
68+
self.listener_http = Listener.objects.create(
69+
network_address=self.netaddr,
70+
port=80,
71+
frontend=self.frontend_http,
72+
)
73+
self.backend_http = Backend.objects.create(
74+
name=f"backend_http_test_{self.TEST_CASE_NAME}",
75+
mode="http",
76+
)
77+
self.serv_http = Server.objects.create(
78+
mode="net",
79+
target="10.10.10.10",
80+
port=443,
81+
backend=self.backend_http,
82+
)
83+
repo_form = OpenIDRepositoryForm({
84+
'name': f"openid_test_{self.TEST_CASE_NAME}",
85+
'provider': 'openid',
86+
'provider_url': 'https://openid.example.com',
87+
'client_id': uuid4(),
88+
'client_secret': uuid4(),
89+
'scopes': ['openid']
90+
})
91+
self.openid = repo_form.save()
92+
self.portal_template = PortalTemplate.objects.create(
93+
name=f"portal_template_test_{self.TEST_CASE_NAME}",
94+
)
95+
portal_form = UserAuthenticationForm({
96+
'name': f"portal_with_openid_test_{self.TEST_CASE_NAME}",
97+
'auth_type': 'form',
98+
'lookup_ldap_attr': 'cn',
99+
'lookup_claim_attr': 'username',
100+
'auth_timeout': 900,
101+
'disconnect_url': 'test/disconnect',
102+
# 'enable_external': True,
103+
# 'external_listener': self.frontend_http,
104+
'external_fqdn': 'openid.example.com',
105+
'auth_cookie_name': get_random_cookie_name(),
106+
'portal_template': self.portal_template,
107+
'oauth_client_id': self.openid.client_id,
108+
'oauth_client_secret': self.openid.client_secret,
109+
'oauth_redirect_uris': f'https://www.example.com/oauth2/callback/{self.openid.id_alea}',
110+
'oauth_timeout': 600,
111+
'repositories': [self.openid.pk]
112+
})
113+
self.portal = portal_form.save()
114+
self.workflow = Workflow.objects.create(
115+
name=f"workflow_http_test_{self.TEST_CASE_NAME}",
116+
frontend=self.frontend_http,
117+
backend=self.backend_http,
118+
fqdn="www.example.com",
119+
public_dir="/",
120+
authentication=self.portal,
121+
)
122+
123+
124+
def test_split_domain(self):
125+
url = "https://example.com"
126+
url2 = "http://test.example.com"
127+
url3 = "https://test.example.com:8000"
128+
fqdn = "sub.test.example.com:443"
129+
130+
self.assertEqual(split_domain(url), ".example.com")
131+
self.assertEqual(split_domain(url2), ".example.com")
132+
self.assertEqual(split_domain(url3), ".example.com")
133+
self.assertEqual(split_domain(fqdn), ".test.example.com")
134+
135+
136+
def test_set_portal_cookie(self):
137+
response = HttpResponse()
138+
cookie_name = get_random_cookie_name()
139+
cookie_value = "test_value"
140+
url = "http://example.com"
141+
set_portal_cookie(response, cookie_name, cookie_value, url)
142+
143+
self.assertIsNotNone(response.cookies)
144+
cookie = response.cookies[cookie_name]
145+
146+
self.assertEqual(cookie.value, cookie_value)
147+
self.assertEqual(cookie["domain"], ".example.com")
148+
self.assertEqual(cookie["httponly"], True)
149+
self.assertEqual(cookie["secure"], "")
150+
self.assertEqual(cookie["samesite"], "Lax")
151+
152+
153+
def test_set_portal_cookie_https(self):
154+
response = HttpResponse()
155+
url = "https://example.com"
156+
set_portal_cookie(response, "test_cookie", "test_value", url)
157+
158+
self.assertIsNotNone(response.cookies)
159+
cookie = response.cookies["test_cookie"]
160+
self.assertEqual(cookie["secure"], True)
161+
162+
163+
def test_set_portal_cookie_regressions(self):
164+
response1 = HttpResponse()
165+
response2 = HttpResponse()
166+
cookie_name = get_random_cookie_name()
167+
cookie_value = "test_value"
168+
scheme = "https" if self.frontend_http.has_tls() else "http"
169+
fqdn = self.workflow.fqdn
170+
171+
response1.set_cookie(cookie_name, cookie_value, domain=split_domain(fqdn), httponly=True, secure=scheme=="https", samesite="Lax")
172+
set_portal_cookie(response2, cookie_name, cookie_value, f"{scheme}://{fqdn}")
173+
174+
self.assertIsNotNone(response1.cookies)
175+
self.assertIsNotNone(response2.cookies)
176+
self.assertEqual(response1.cookies[cookie_name].value, response2.cookies[cookie_name].value)
177+
self.assertEqual(response1.cookies[cookie_name]["domain"], response2.cookies[cookie_name]["domain"])
178+
self.assertEqual(response1.cookies[cookie_name]["httponly"], response2.cookies[cookie_name]["httponly"])
179+
self.assertEqual(response1.cookies[cookie_name]["secure"], response2.cookies[cookie_name]["secure"])
180+
self.assertEqual(response1.cookies[cookie_name]["samesite"], response2.cookies[cookie_name]["samesite"])
181+
182+
183+
@patch('portal.system.redis_sessions.REDISPortalSession.delete_key')
184+
@patch('portal.system.redis_sessions.Redis.execute_command')
185+
def test_portal_url(self, mocked_execute_command, mocked_write_in_redis):
186+
mocked_execute_command.return_value = {}
187+
mocked_write_in_redis.return_value = True
188+
cookie_name = self.global_config.portal_cookie_name
189+
190+
c = Client(headers={"host": self.workflow.fqdn, "x-forwarded-proto": "https"})
191+
response = c.get(f"/portal/{self.workflow.id}/")
192+
self.assertEqual(response.status_code, 200)
193+
self.assertIsNotNone(response.cookies)
194+
self.assertIn(cookie_name, response.cookies.keys())
195+
self.assertEqual(response.cookies[cookie_name]["domain"], split_domain(self.workflow.fqdn))
196+
self.assertEqual(response.cookies[cookie_name]["httponly"], True)
197+
self.assertEqual(response.cookies[cookie_name]["secure"], True)
198+
self.assertEqual(response.cookies[cookie_name]["samesite"], "Lax")
199+
200+
201+
def test_retrieve_start_url_from_portal(self):
202+
repo = self.workflow.authentication.repositories.first()
203+
scheme = "https" if self.frontend_http.has_tls() else "http"
204+
fqdn = self.workflow.fqdn
205+
port = self.workflow.frontend.listener_set.first().port
206+
207+
auth_start_url = self.workflow.authentication.get_openid_start_url(
208+
req_scheme=scheme,
209+
workflow_host=f"{fqdn}:{port}" if port not in (443, 80) else fqdn,
210+
workflow_path=self.workflow.public_dir,
211+
repo_id=repo.id
212+
)
213+
214+
self.assertEqual(auth_start_url, f"{scheme}://{fqdn}/oauth2/start/?repo={repo.id}")
215+
216+
217+
def test_retrieve_start_url_from_repo(self):
218+
self.assertIsNot(self.workflow.authentication.openid_repos, [])
219+
repo = self.workflow.authentication.openid_repos[0]
220+
scheme = "https" if self.frontend_http.has_tls() else "http"
221+
fqdn = self.workflow.fqdn
222+
223+
self.assertEqual(f"{scheme}://{fqdn}/{self.openid.start_url}", f"{scheme}://{fqdn}/oauth2/start?repo={repo.id}")
224+
225+
226+
@patch('portal.system.redis_sessions.REDISPortalSession.write_in_redis')
227+
@patch('portal.system.redis_sessions.Redis.execute_command')
228+
@patch('portal.views.logon.OpenIDRepository.retrieve_config')
229+
def test_start_url(self, mocked_retrieve_config, mocked_execute_command, mocked_write_in_redis):
230+
mocked_execute_command.return_value = {}
231+
mocked_write_in_redis.return_value = True
232+
233+
self.assertIsNot(self.workflow.authentication.openid_repos, [])
234+
repo = self.workflow.authentication.openid_repos[0]
235+
scheme = "https" # Forced to https due to InsecureTransportError
236+
fqdn = self.workflow.fqdn
237+
cookie_name = self.global_config.portal_cookie_name
238+
mocked_retrieve_config.return_value = self.portal.generate_openid_config(f"{scheme}://{fqdn}")
239+
240+
c = Client(headers={"host": self.workflow.fqdn, "x-forwarded-proto": "https"})
241+
response = c.get(f"/portal/{self.workflow.id}/oauth2/start/{repo.id}?repo={repo.id}")
242+
self.assertEqual(response.status_code, 302)
243+
self.assertIsNotNone(response.cookies)
244+
self.assertIn(cookie_name, response.cookies.keys())
245+
self.assertEqual(response.cookies[cookie_name]["domain"], split_domain(fqdn))
246+
self.assertEqual(response.cookies[cookie_name]["httponly"], True)
247+
self.assertEqual(response.cookies[cookie_name]["secure"], True)
248+
self.assertEqual(response.cookies[cookie_name]["samesite"], "Lax")
249+
250+
251+
@patch('portal.system.redis_sessions.Redis.execute_command')
252+
@patch('portal.views.logon.OpenIDRepository.retrieve_config')
253+
def test_authorize_url(self, mocked_retrieve_config, mocked_execute_command):
254+
mocked_execute_command.return_value = {}
255+
256+
self.assertIsNot(self.workflow.authentication.openid_repos, [])
257+
repo = self.workflow.authentication.openid_repos[0]
258+
scheme = "https" # Forced to https due to InsecureTransportError
259+
fqdn = self.workflow.fqdn
260+
cookie_name = self.global_config.portal_cookie_name
261+
262+
callback_url = self.workflow.authentication.get_openid_callback_url(scheme, fqdn, self.workflow.public_dir, repo.id_alea)
263+
oauth2_session = repo.get_oauth2_session(callback_url)
264+
265+
mocked_retrieve_config.return_value = self.portal.generate_openid_config(f"{scheme}://{fqdn}")
266+
authorization_url, state = repo.get_authorization_url(oauth2_session)
267+
268+
c = Client(headers={"x-forwarded-proto": "https"})
269+
response = c.get(f"/portal/portal_{self.workflow.id}/oauth2/{authorization_url.split('oauth2/')[1]}")
270+
self.assertEqual(response.status_code, 200)
271+
self.assertIsNotNone(response.cookies)
272+
self.assertIn(cookie_name, response.cookies.keys())
273+
self.assertEqual(response.cookies[cookie_name]["domain"], split_domain(fqdn))
274+
self.assertEqual(response.cookies[cookie_name]["httponly"], True)
275+
self.assertEqual(response.cookies[cookie_name]["secure"], True)
276+
self.assertEqual(response.cookies[cookie_name]["samesite"], "Lax")

0 commit comments

Comments
 (0)