blob: cfa3cf5ceeaabeeb876c8ef178fc59867a1fe6e4 [file] [log] [blame]
delacruzramo79e40f42019-10-10 16:36:40 +02001#! /usr/bin/python3
2# -*- coding: utf-8 -*-
3
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
13# implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16
17__author__ = "Pedro de la Cruz Ramos, pedro.delacruzramos@altran.com"
18__date__ = "$2019-10-019"
19
20import unittest
selvi.j0389d9e2023-04-26 12:13:10 +000021import random
delacruzramo79e40f42019-10-10 16:36:40 +020022from unittest import TestCase
David Garciaecb41322021-03-31 19:10:46 +020023from unittest.mock import Mock, patch, call
delacruzramo79e40f42019-10-10 16:36:40 +020024from uuid import uuid4
25from http import HTTPStatus
26from time import time
delacruzramo79e40f42019-10-10 16:36:40 +020027from osm_common import dbbase, fsbase, msgbase
garciadeblas6d83f8f2023-06-19 22:34:49 +020028from osm_common.dbmemory import DbMemory
delacruzramo79e40f42019-10-10 16:36:40 +020029from osm_nbi import authconn, validation
David Garciaecb41322021-03-31 19:10:46 +020030from osm_nbi.admin_topics import (
31 ProjectTopicAuth,
32 RoleTopicAuth,
33 UserTopicAuth,
34 CommonVimWimSdn,
35 VcaTopic,
36)
delacruzramo79e40f42019-10-10 16:36:40 +020037from osm_nbi.engine import EngineException
38from osm_nbi.authconn import AuthconnNotFoundException
garciadeblas6d83f8f2023-06-19 22:34:49 +020039from osm_nbi.authconn_internal import AuthconnInternal
delacruzramo79e40f42019-10-10 16:36:40 +020040
41
42test_pid = str(uuid4())
43test_name = "test-user"
44
45
46def norm(str):
47 """Normalize string for checking"""
garciadeblas4568a372021-03-24 09:19:48 +010048 return " ".join(str.strip().split()).lower()
delacruzramo79e40f42019-10-10 16:36:40 +020049
50
David Garciaecb41322021-03-31 19:10:46 +020051class TestVcaTopic(TestCase):
52 def setUp(self):
53 self.db = Mock(dbbase.DbBase())
54 self.fs = Mock(fsbase.FsBase())
55 self.msg = Mock(msgbase.MsgBase())
56 self.auth = Mock(authconn.Authconn(None, None, None))
57 self.vca_topic = VcaTopic(self.db, self.fs, self.msg, self.auth)
58
59 @patch("osm_nbi.admin_topics.CommonVimWimSdn.format_on_new")
60 def test_format_on_new(self, mock_super_format_on_new):
61 content = {
62 "_id": "id",
63 "secret": "encrypted_secret",
64 "cacert": "encrypted_cacert",
65 }
66 self.db.encrypt.side_effect = ["secret", "cacert"]
67 mock_super_format_on_new.return_value = "1234"
68
69 oid = self.vca_topic.format_on_new(content)
70
71 self.assertEqual(oid, "1234")
72 self.assertEqual(content["secret"], "secret")
73 self.assertEqual(content["cacert"], "cacert")
74 self.db.encrypt.assert_has_calls(
75 [
76 call("encrypted_secret", schema_version="1.11", salt="id"),
77 call("encrypted_cacert", schema_version="1.11", salt="id"),
78 ]
79 )
80 mock_super_format_on_new.assert_called_with(content, None, False)
81
82 @patch("osm_nbi.admin_topics.CommonVimWimSdn.format_on_edit")
83 def test_format_on_edit(self, mock_super_format_on_edit):
84 edit_content = {
85 "_id": "id",
86 "secret": "encrypted_secret",
87 "cacert": "encrypted_cacert",
88 }
89 final_content = {
90 "_id": "id",
91 "schema_version": "1.11",
92 }
93 self.db.encrypt.side_effect = ["secret", "cacert"]
94 mock_super_format_on_edit.return_value = "1234"
95
96 oid = self.vca_topic.format_on_edit(final_content, edit_content)
97
98 self.assertEqual(oid, "1234")
99 self.assertEqual(final_content["secret"], "secret")
100 self.assertEqual(final_content["cacert"], "cacert")
101 self.db.encrypt.assert_has_calls(
102 [
103 call("encrypted_secret", schema_version="1.11", salt="id"),
104 call("encrypted_cacert", schema_version="1.11", salt="id"),
105 ]
106 )
107 mock_super_format_on_edit.assert_called()
108
109 @patch("osm_nbi.admin_topics.CommonVimWimSdn.check_conflict_on_del")
110 def test_check_conflict_on_del(self, mock_check_conflict_on_del):
111 session = {
112 "project_id": "project-id",
113 "force": False,
114 }
115 _id = "vca-id"
116 db_content = {}
117
118 self.db.get_list.return_value = None
119
120 self.vca_topic.check_conflict_on_del(session, _id, db_content)
121
122 self.db.get_list.assert_called_with(
123 "vim_accounts",
garciadeblas4568a372021-03-24 09:19:48 +0100124 {"vca": _id, "_admin.projects_read.cont": "project-id"},
David Garciaecb41322021-03-31 19:10:46 +0200125 )
126 mock_check_conflict_on_del.assert_called_with(session, _id, db_content)
127
128 @patch("osm_nbi.admin_topics.CommonVimWimSdn.check_conflict_on_del")
129 def test_check_conflict_on_del_force(self, mock_check_conflict_on_del):
130 session = {
131 "project_id": "project-id",
132 "force": True,
133 }
134 _id = "vca-id"
135 db_content = {}
136
137 self.vca_topic.check_conflict_on_del(session, _id, db_content)
138
139 self.db.get_list.assert_not_called()
140 mock_check_conflict_on_del.assert_not_called()
141
142 @patch("osm_nbi.admin_topics.CommonVimWimSdn.check_conflict_on_del")
143 def test_check_conflict_on_del_with_conflict(self, mock_check_conflict_on_del):
144 session = {
145 "project_id": "project-id",
146 "force": False,
147 }
148 _id = "vca-id"
149 db_content = {}
150
151 self.db.get_list.return_value = {"_id": "vim", "vca": "vca-id"}
152
153 with self.assertRaises(EngineException) as context:
154 self.vca_topic.check_conflict_on_del(session, _id, db_content)
155 self.assertEqual(
156 context.exception,
157 EngineException(
158 "There is at least one VIM account using this vca",
garciadeblas4568a372021-03-24 09:19:48 +0100159 http_code=HTTPStatus.CONFLICT,
160 ),
David Garciaecb41322021-03-31 19:10:46 +0200161 )
162
163 self.db.get_list.assert_called_with(
164 "vim_accounts",
garciadeblas4568a372021-03-24 09:19:48 +0100165 {"vca": _id, "_admin.projects_read.cont": "project-id"},
David Garciaecb41322021-03-31 19:10:46 +0200166 )
167 mock_check_conflict_on_del.assert_not_called()
168
169
delacruzramo79e40f42019-10-10 16:36:40 +0200170class Test_ProjectTopicAuth(TestCase):
delacruzramo79e40f42019-10-10 16:36:40 +0200171 @classmethod
172 def setUpClass(cls):
173 cls.test_name = "test-project-topic"
174
175 def setUp(self):
176 self.db = Mock(dbbase.DbBase())
177 self.fs = Mock(fsbase.FsBase())
178 self.msg = Mock(msgbase.MsgBase())
tierno9e87a7f2020-03-23 09:24:10 +0000179 self.auth = Mock(authconn.Authconn(None, None, None))
delacruzramo79e40f42019-10-10 16:36:40 +0200180 self.topic = ProjectTopicAuth(self.db, self.fs, self.msg, self.auth)
garciadeblas4568a372021-03-24 09:19:48 +0100181 self.fake_session = {
182 "username": self.test_name,
183 "project_id": (test_pid,),
184 "method": None,
185 "admin": True,
186 "force": False,
187 "public": False,
188 "allow_show_user_project_role": True,
189 }
tiernod7749582020-05-28 10:41:10 +0000190 self.topic.check_quota = Mock(return_value=None) # skip quota
delacruzramo79e40f42019-10-10 16:36:40 +0200191
192 def test_new_project(self):
193 with self.subTest(i=1):
194 rollback = []
195 pid1 = str(uuid4())
196 self.auth.get_project_list.return_value = []
197 self.auth.create_project.return_value = pid1
garciadeblas4568a372021-03-24 09:19:48 +0100198 pid2, oid = self.topic.new(
199 rollback, self.fake_session, {"name": self.test_name, "quotas": {}}
200 )
delacruzramo79e40f42019-10-10 16:36:40 +0200201 self.assertEqual(len(rollback), 1, "Wrong rollback length")
202 self.assertEqual(pid2, pid1, "Wrong project identifier")
203 content = self.auth.create_project.call_args[0][0]
204 self.assertEqual(content["name"], self.test_name, "Wrong project name")
205 self.assertEqual(content["quotas"], {}, "Wrong quotas")
206 self.assertIsNotNone(content["_admin"]["created"], "Wrong creation time")
garciadeblas4568a372021-03-24 09:19:48 +0100207 self.assertEqual(
208 content["_admin"]["modified"],
209 content["_admin"]["created"],
210 "Wrong modification time",
211 )
delacruzramo79e40f42019-10-10 16:36:40 +0200212 with self.subTest(i=2):
213 rollback = []
214 with self.assertRaises(EngineException, msg="Accepted wrong quotas") as e:
garciadeblas4568a372021-03-24 09:19:48 +0100215 self.topic.new(
216 rollback,
217 self.fake_session,
218 {"name": "other-project-name", "quotas": {"baditems": 10}},
219 )
delacruzramo79e40f42019-10-10 16:36:40 +0200220 self.assertEqual(len(rollback), 0, "Wrong rollback length")
garciadeblas4568a372021-03-24 09:19:48 +0100221 self.assertEqual(
222 e.exception.http_code,
223 HTTPStatus.UNPROCESSABLE_ENTITY,
224 "Wrong HTTP status code",
225 )
226 self.assertIn(
227 "format error at 'quotas' 'additional properties are not allowed ('{}' was unexpected)'".format(
228 "baditems"
229 ),
230 norm(str(e.exception)),
231 "Wrong exception text",
232 )
delacruzramo79e40f42019-10-10 16:36:40 +0200233
234 def test_edit_project(self):
235 now = time()
236 pid = str(uuid4())
garciadeblas4568a372021-03-24 09:19:48 +0100237 proj = {
238 "_id": pid,
239 "name": self.test_name,
240 "_admin": {"created": now, "modified": now},
241 }
delacruzramo79e40f42019-10-10 16:36:40 +0200242 with self.subTest(i=1):
243 self.auth.get_project_list.side_effect = [[proj], []]
244 new_name = "new-project-name"
selvi.j0389d9e2023-04-26 12:13:10 +0000245 quotas = {
246 "vnfds": random.SystemRandom().randint(0, 100),
247 "nsds": random.SystemRandom().randint(0, 100),
248 }
garciadeblas4568a372021-03-24 09:19:48 +0100249 self.topic.edit(
250 self.fake_session, pid, {"name": new_name, "quotas": quotas}
251 )
delacruzramo79e40f42019-10-10 16:36:40 +0200252 _id, content = self.auth.update_project.call_args[0]
253 self.assertEqual(_id, pid, "Wrong project identifier")
254 self.assertEqual(content["_id"], pid, "Wrong project identifier")
255 self.assertEqual(content["_admin"]["created"], now, "Wrong creation time")
garciadeblas4568a372021-03-24 09:19:48 +0100256 self.assertGreater(
257 content["_admin"]["modified"], now, "Wrong modification time"
258 )
delacruzramo79e40f42019-10-10 16:36:40 +0200259 self.assertEqual(content["name"], new_name, "Wrong project name")
260 self.assertEqual(content["quotas"], quotas, "Wrong quotas")
261 with self.subTest(i=2):
262 new_name = "other-project-name"
selvi.j0389d9e2023-04-26 12:13:10 +0000263 quotas = {"baditems": random.SystemRandom().randint(0, 100)}
delacruzramo79e40f42019-10-10 16:36:40 +0200264 self.auth.get_project_list.side_effect = [[proj], []]
265 with self.assertRaises(EngineException, msg="Accepted wrong quotas") as e:
garciadeblas4568a372021-03-24 09:19:48 +0100266 self.topic.edit(
267 self.fake_session, pid, {"name": new_name, "quotas": quotas}
268 )
269 self.assertEqual(
270 e.exception.http_code,
271 HTTPStatus.UNPROCESSABLE_ENTITY,
272 "Wrong HTTP status code",
273 )
274 self.assertIn(
275 "format error at 'quotas' 'additional properties are not allowed ('{}' was unexpected)'".format(
276 "baditems"
277 ),
278 norm(str(e.exception)),
279 "Wrong exception text",
280 )
delacruzramo79e40f42019-10-10 16:36:40 +0200281
282 def test_conflict_on_new(self):
283 with self.subTest(i=1):
284 rollback = []
285 pid = str(uuid4())
garciadeblas4568a372021-03-24 09:19:48 +0100286 with self.assertRaises(
287 EngineException, msg="Accepted uuid as project name"
288 ) as e:
delacruzramo79e40f42019-10-10 16:36:40 +0200289 self.topic.new(rollback, self.fake_session, {"name": pid})
290 self.assertEqual(len(rollback), 0, "Wrong rollback length")
garciadeblas4568a372021-03-24 09:19:48 +0100291 self.assertEqual(
292 e.exception.http_code,
293 HTTPStatus.UNPROCESSABLE_ENTITY,
294 "Wrong HTTP status code",
295 )
296 self.assertIn(
297 "project name '{}' cannot have an uuid format".format(pid),
298 norm(str(e.exception)),
299 "Wrong exception text",
300 )
delacruzramo79e40f42019-10-10 16:36:40 +0200301 with self.subTest(i=2):
302 rollback = []
garciadeblas4568a372021-03-24 09:19:48 +0100303 self.auth.get_project_list.return_value = [
304 {"_id": test_pid, "name": self.test_name}
305 ]
306 with self.assertRaises(
307 EngineException, msg="Accepted existing project name"
308 ) as e:
delacruzramo79e40f42019-10-10 16:36:40 +0200309 self.topic.new(rollback, self.fake_session, {"name": self.test_name})
310 self.assertEqual(len(rollback), 0, "Wrong rollback length")
garciadeblas4568a372021-03-24 09:19:48 +0100311 self.assertEqual(
312 e.exception.http_code, HTTPStatus.CONFLICT, "Wrong HTTP status code"
313 )
314 self.assertIn(
315 "project '{}' exists".format(self.test_name),
316 norm(str(e.exception)),
317 "Wrong exception text",
318 )
delacruzramo79e40f42019-10-10 16:36:40 +0200319
320 def test_conflict_on_edit(self):
321 with self.subTest(i=1):
garciadeblas4568a372021-03-24 09:19:48 +0100322 self.auth.get_project_list.return_value = [
323 {"_id": test_pid, "name": self.test_name}
324 ]
delacruzramo79e40f42019-10-10 16:36:40 +0200325 new_name = str(uuid4())
garciadeblas4568a372021-03-24 09:19:48 +0100326 with self.assertRaises(
327 EngineException, msg="Accepted uuid as project name"
328 ) as e:
delacruzramo79e40f42019-10-10 16:36:40 +0200329 self.topic.edit(self.fake_session, test_pid, {"name": new_name})
garciadeblas4568a372021-03-24 09:19:48 +0100330 self.assertEqual(
331 e.exception.http_code,
332 HTTPStatus.UNPROCESSABLE_ENTITY,
333 "Wrong HTTP status code",
334 )
335 self.assertIn(
336 "project name '{}' cannot have an uuid format".format(new_name),
337 norm(str(e.exception)),
338 "Wrong exception text",
339 )
delacruzramo79e40f42019-10-10 16:36:40 +0200340 with self.subTest(i=2):
341 pid = str(uuid4())
342 self.auth.get_project_list.return_value = [{"_id": pid, "name": "admin"}]
garciadeblas4568a372021-03-24 09:19:48 +0100343 with self.assertRaises(
344 EngineException, msg="Accepted renaming of project 'admin'"
345 ) as e:
delacruzramo79e40f42019-10-10 16:36:40 +0200346 self.topic.edit(self.fake_session, pid, {"name": "new-name"})
garciadeblas4568a372021-03-24 09:19:48 +0100347 self.assertEqual(
348 e.exception.http_code, HTTPStatus.CONFLICT, "Wrong HTTP status code"
349 )
350 self.assertIn(
351 "you cannot rename project 'admin'",
352 norm(str(e.exception)),
353 "Wrong exception text",
354 )
delacruzramo79e40f42019-10-10 16:36:40 +0200355 with self.subTest(i=3):
356 new_name = "new-project-name"
garciadeblas4568a372021-03-24 09:19:48 +0100357 self.auth.get_project_list.side_effect = [
358 [{"_id": test_pid, "name": self.test_name}],
359 [{"_id": str(uuid4()), "name": new_name}],
360 ]
361 with self.assertRaises(
362 EngineException, msg="Accepted existing project name"
363 ) as e:
delacruzramo79e40f42019-10-10 16:36:40 +0200364 self.topic.edit(self.fake_session, pid, {"name": new_name})
garciadeblas4568a372021-03-24 09:19:48 +0100365 self.assertEqual(
366 e.exception.http_code, HTTPStatus.CONFLICT, "Wrong HTTP status code"
367 )
368 self.assertIn(
369 "project '{}' is already used".format(new_name),
370 norm(str(e.exception)),
371 "Wrong exception text",
372 )
delacruzramo79e40f42019-10-10 16:36:40 +0200373
374 def test_delete_project(self):
375 with self.subTest(i=1):
376 pid = str(uuid4())
garciadeblas4568a372021-03-24 09:19:48 +0100377 self.auth.get_project.return_value = {
378 "_id": pid,
379 "name": "other-project-name",
380 }
delacruzramo79e40f42019-10-10 16:36:40 +0200381 self.auth.delete_project.return_value = {"deleted": 1}
382 self.auth.get_user_list.return_value = []
383 self.db.get_list.return_value = []
384 rc = self.topic.delete(self.fake_session, pid)
385 self.assertEqual(rc, {"deleted": 1}, "Wrong project deletion return info")
garciadeblas4568a372021-03-24 09:19:48 +0100386 self.assertEqual(
387 self.auth.get_project.call_args[0][0], pid, "Wrong project identifier"
388 )
389 self.assertEqual(
390 self.auth.delete_project.call_args[0][0],
391 pid,
392 "Wrong project identifier",
393 )
delacruzramo79e40f42019-10-10 16:36:40 +0200394
395 def test_conflict_on_del(self):
396 with self.subTest(i=1):
garciadeblas4568a372021-03-24 09:19:48 +0100397 self.auth.get_project.return_value = {
398 "_id": test_pid,
399 "name": self.test_name,
400 }
401 with self.assertRaises(
402 EngineException, msg="Accepted deletion of own project"
403 ) as e:
delacruzramo79e40f42019-10-10 16:36:40 +0200404 self.topic.delete(self.fake_session, self.test_name)
garciadeblas4568a372021-03-24 09:19:48 +0100405 self.assertEqual(
406 e.exception.http_code, HTTPStatus.CONFLICT, "Wrong HTTP status code"
407 )
408 self.assertIn(
409 "you cannot delete your own project",
410 norm(str(e.exception)),
411 "Wrong exception text",
412 )
delacruzramo79e40f42019-10-10 16:36:40 +0200413 with self.subTest(i=2):
414 self.auth.get_project.return_value = {"_id": str(uuid4()), "name": "admin"}
garciadeblas4568a372021-03-24 09:19:48 +0100415 with self.assertRaises(
416 EngineException, msg="Accepted deletion of project 'admin'"
417 ) as e:
delacruzramo79e40f42019-10-10 16:36:40 +0200418 self.topic.delete(self.fake_session, "admin")
garciadeblas4568a372021-03-24 09:19:48 +0100419 self.assertEqual(
420 e.exception.http_code, HTTPStatus.CONFLICT, "Wrong HTTP status code"
421 )
422 self.assertIn(
423 "you cannot delete project 'admin'",
424 norm(str(e.exception)),
425 "Wrong exception text",
426 )
delacruzramo79e40f42019-10-10 16:36:40 +0200427 with self.subTest(i=3):
428 pid = str(uuid4())
429 name = "other-project-name"
430 self.auth.get_project.return_value = {"_id": pid, "name": name}
garciadeblas4568a372021-03-24 09:19:48 +0100431 self.auth.get_user_list.return_value = [
432 {
433 "_id": str(uuid4()),
434 "username": self.test_name,
435 "project_role_mappings": [{"project": pid, "role": str(uuid4())}],
436 }
437 ]
438 with self.assertRaises(
439 EngineException, msg="Accepted deletion of used project"
440 ) as e:
delacruzramo79e40f42019-10-10 16:36:40 +0200441 self.topic.delete(self.fake_session, pid)
garciadeblas4568a372021-03-24 09:19:48 +0100442 self.assertEqual(
443 e.exception.http_code, HTTPStatus.CONFLICT, "Wrong HTTP status code"
444 )
445 self.assertIn(
446 "project '{}' ({}) is being used by user '{}'".format(
447 name, pid, self.test_name
448 ),
449 norm(str(e.exception)),
450 "Wrong exception text",
451 )
delacruzramo79e40f42019-10-10 16:36:40 +0200452 with self.subTest(i=4):
453 self.auth.get_user_list.return_value = []
garciadeblas4568a372021-03-24 09:19:48 +0100454 self.db.get_list.return_value = [
455 {
456 "_id": str(uuid4()),
457 "id": self.test_name,
458 "_admin": {"projects_read": [pid], "projects_write": []},
459 }
460 ]
461 with self.assertRaises(
462 EngineException, msg="Accepted deletion of used project"
463 ) as e:
delacruzramo79e40f42019-10-10 16:36:40 +0200464 self.topic.delete(self.fake_session, pid)
garciadeblas4568a372021-03-24 09:19:48 +0100465 self.assertEqual(
466 e.exception.http_code, HTTPStatus.CONFLICT, "Wrong HTTP status code"
467 )
468 self.assertIn(
469 "project '{}' ({}) is being used by {} '{}'".format(
470 name, pid, "vnf descriptor", self.test_name
471 ),
472 norm(str(e.exception)),
473 "Wrong exception text",
474 )
delacruzramo79e40f42019-10-10 16:36:40 +0200475
476
477class Test_RoleTopicAuth(TestCase):
delacruzramo79e40f42019-10-10 16:36:40 +0200478 @classmethod
479 def setUpClass(cls):
480 cls.test_name = "test-role-topic"
481 cls.test_operations = ["tokens:get"]
482
483 def setUp(self):
484 self.db = Mock(dbbase.DbBase())
485 self.fs = Mock(fsbase.FsBase())
486 self.msg = Mock(msgbase.MsgBase())
tierno9e87a7f2020-03-23 09:24:10 +0000487 self.auth = Mock(authconn.Authconn(None, None, None))
488 self.auth.role_permissions = self.test_operations
489 self.topic = RoleTopicAuth(self.db, self.fs, self.msg, self.auth)
garciadeblas4568a372021-03-24 09:19:48 +0100490 self.fake_session = {
491 "username": test_name,
492 "project_id": (test_pid,),
493 "method": None,
494 "admin": True,
495 "force": False,
496 "public": False,
497 "allow_show_user_project_role": True,
498 }
tiernod7749582020-05-28 10:41:10 +0000499 self.topic.check_quota = Mock(return_value=None) # skip quota
delacruzramo79e40f42019-10-10 16:36:40 +0200500
501 def test_new_role(self):
502 with self.subTest(i=1):
503 rollback = []
504 rid1 = str(uuid4())
505 perms_in = {"tokens": True}
506 perms_out = {"default": False, "admin": False, "tokens": True}
507 self.auth.get_role_list.return_value = []
508 self.auth.create_role.return_value = rid1
garciadeblas4568a372021-03-24 09:19:48 +0100509 rid2, oid = self.topic.new(
510 rollback,
511 self.fake_session,
512 {"name": self.test_name, "permissions": perms_in},
513 )
delacruzramo79e40f42019-10-10 16:36:40 +0200514 self.assertEqual(len(rollback), 1, "Wrong rollback length")
515 self.assertEqual(rid2, rid1, "Wrong project identifier")
516 content = self.auth.create_role.call_args[0][0]
517 self.assertEqual(content["name"], self.test_name, "Wrong role name")
518 self.assertEqual(content["permissions"], perms_out, "Wrong permissions")
519 self.assertIsNotNone(content["_admin"]["created"], "Wrong creation time")
garciadeblas4568a372021-03-24 09:19:48 +0100520 self.assertEqual(
521 content["_admin"]["modified"],
522 content["_admin"]["created"],
523 "Wrong modification time",
524 )
delacruzramo79e40f42019-10-10 16:36:40 +0200525 with self.subTest(i=2):
526 rollback = []
garciadeblas4568a372021-03-24 09:19:48 +0100527 with self.assertRaises(
528 EngineException, msg="Accepted wrong permissions"
529 ) as e:
530 self.topic.new(
531 rollback,
532 self.fake_session,
533 {"name": "other-role-name", "permissions": {"projects": True}},
534 )
delacruzramo79e40f42019-10-10 16:36:40 +0200535 self.assertEqual(len(rollback), 0, "Wrong rollback length")
garciadeblas4568a372021-03-24 09:19:48 +0100536 self.assertEqual(
537 e.exception.http_code,
538 HTTPStatus.UNPROCESSABLE_ENTITY,
539 "Wrong HTTP status code",
540 )
541 self.assertIn(
542 "invalid permission '{}'".format("projects"),
543 norm(str(e.exception)),
544 "Wrong exception text",
545 )
delacruzramo79e40f42019-10-10 16:36:40 +0200546
547 def test_edit_role(self):
548 now = time()
549 rid = str(uuid4())
garciadeblas4568a372021-03-24 09:19:48 +0100550 role = {
551 "_id": rid,
552 "name": self.test_name,
553 "permissions": {"tokens": True},
554 "_admin": {"created": now, "modified": now},
555 }
delacruzramo79e40f42019-10-10 16:36:40 +0200556 with self.subTest(i=1):
557 self.auth.get_role_list.side_effect = [[role], []]
558 self.auth.get_role.return_value = role
559 new_name = "new-role-name"
560 perms_in = {"tokens": False, "tokens:get": True}
garciadeblas4568a372021-03-24 09:19:48 +0100561 perms_out = {
562 "default": False,
563 "admin": False,
564 "tokens": False,
565 "tokens:get": True,
566 }
567 self.topic.edit(
568 self.fake_session, rid, {"name": new_name, "permissions": perms_in}
569 )
delacruzramo79e40f42019-10-10 16:36:40 +0200570 content = self.auth.update_role.call_args[0][0]
571 self.assertEqual(content["_id"], rid, "Wrong role identifier")
572 self.assertEqual(content["_admin"]["created"], now, "Wrong creation time")
garciadeblas4568a372021-03-24 09:19:48 +0100573 self.assertGreater(
574 content["_admin"]["modified"], now, "Wrong modification time"
575 )
delacruzramo79e40f42019-10-10 16:36:40 +0200576 self.assertEqual(content["name"], new_name, "Wrong role name")
577 self.assertEqual(content["permissions"], perms_out, "Wrong permissions")
578 with self.subTest(i=2):
579 new_name = "other-role-name"
580 perms_in = {"tokens": False, "tokens:post": True}
581 self.auth.get_role_list.side_effect = [[role], []]
garciadeblas4568a372021-03-24 09:19:48 +0100582 with self.assertRaises(
583 EngineException, msg="Accepted wrong permissions"
584 ) as e:
585 self.topic.edit(
586 self.fake_session, rid, {"name": new_name, "permissions": perms_in}
587 )
588 self.assertEqual(
589 e.exception.http_code,
590 HTTPStatus.UNPROCESSABLE_ENTITY,
591 "Wrong HTTP status code",
592 )
593 self.assertIn(
594 "invalid permission '{}'".format("tokens:post"),
595 norm(str(e.exception)),
596 "Wrong exception text",
597 )
delacruzramo79e40f42019-10-10 16:36:40 +0200598
599 def test_delete_role(self):
600 with self.subTest(i=1):
601 rid = str(uuid4())
602 role = {"_id": rid, "name": "other-role-name"}
603 self.auth.get_role_list.return_value = [role]
604 self.auth.get_role.return_value = role
605 self.auth.delete_role.return_value = {"deleted": 1}
606 self.auth.get_user_list.return_value = []
607 rc = self.topic.delete(self.fake_session, rid)
608 self.assertEqual(rc, {"deleted": 1}, "Wrong role deletion return info")
garciadeblas4568a372021-03-24 09:19:48 +0100609 self.assertEqual(
610 self.auth.get_role_list.call_args[0][0]["_id"],
611 rid,
612 "Wrong role identifier",
613 )
614 self.assertEqual(
615 self.auth.get_role.call_args[0][0], rid, "Wrong role identifier"
616 )
617 self.assertEqual(
618 self.auth.delete_role.call_args[0][0], rid, "Wrong role identifier"
619 )
delacruzramo79e40f42019-10-10 16:36:40 +0200620
621 def test_conflict_on_new(self):
622 with self.subTest(i=1):
623 rollback = []
624 rid = str(uuid4())
garciadeblas4568a372021-03-24 09:19:48 +0100625 with self.assertRaises(
626 EngineException, msg="Accepted uuid as role name"
627 ) as e:
delacruzramo79e40f42019-10-10 16:36:40 +0200628 self.topic.new(rollback, self.fake_session, {"name": rid})
629 self.assertEqual(len(rollback), 0, "Wrong rollback length")
garciadeblas4568a372021-03-24 09:19:48 +0100630 self.assertEqual(
631 e.exception.http_code,
632 HTTPStatus.UNPROCESSABLE_ENTITY,
633 "Wrong HTTP status code",
634 )
635 self.assertIn(
636 "role name '{}' cannot have an uuid format".format(rid),
637 norm(str(e.exception)),
638 "Wrong exception text",
639 )
delacruzramo79e40f42019-10-10 16:36:40 +0200640 with self.subTest(i=2):
641 rollback = []
garciadeblas4568a372021-03-24 09:19:48 +0100642 self.auth.get_role_list.return_value = [
643 {"_id": str(uuid4()), "name": self.test_name}
644 ]
645 with self.assertRaises(
646 EngineException, msg="Accepted existing role name"
647 ) as e:
delacruzramo79e40f42019-10-10 16:36:40 +0200648 self.topic.new(rollback, self.fake_session, {"name": self.test_name})
649 self.assertEqual(len(rollback), 0, "Wrong rollback length")
garciadeblas4568a372021-03-24 09:19:48 +0100650 self.assertEqual(
651 e.exception.http_code, HTTPStatus.CONFLICT, "Wrong HTTP status code"
652 )
653 self.assertIn(
654 "role name '{}' exists".format(self.test_name),
655 norm(str(e.exception)),
656 "Wrong exception text",
657 )
delacruzramo79e40f42019-10-10 16:36:40 +0200658
659 def test_conflict_on_edit(self):
660 rid = str(uuid4())
661 with self.subTest(i=1):
garciadeblas4568a372021-03-24 09:19:48 +0100662 self.auth.get_role_list.return_value = [
663 {"_id": rid, "name": self.test_name, "permissions": {}}
664 ]
delacruzramo79e40f42019-10-10 16:36:40 +0200665 new_name = str(uuid4())
garciadeblas4568a372021-03-24 09:19:48 +0100666 with self.assertRaises(
667 EngineException, msg="Accepted uuid as role name"
668 ) as e:
delacruzramo79e40f42019-10-10 16:36:40 +0200669 self.topic.edit(self.fake_session, rid, {"name": new_name})
garciadeblas4568a372021-03-24 09:19:48 +0100670 self.assertEqual(
671 e.exception.http_code,
672 HTTPStatus.UNPROCESSABLE_ENTITY,
673 "Wrong HTTP status code",
674 )
675 self.assertIn(
676 "role name '{}' cannot have an uuid format".format(new_name),
677 norm(str(e.exception)),
678 "Wrong exception text",
679 )
delacruzramo79e40f42019-10-10 16:36:40 +0200680 for i, role_name in enumerate(["system_admin", "project_admin"], start=2):
681 with self.subTest(i=i):
682 rid = str(uuid4())
garciadeblas4568a372021-03-24 09:19:48 +0100683 self.auth.get_role.return_value = {
684 "_id": rid,
685 "name": role_name,
686 "permissions": {},
687 }
688 with self.assertRaises(
689 EngineException,
690 msg="Accepted renaming of role '{}'".format(role_name),
691 ) as e:
delacruzramo79e40f42019-10-10 16:36:40 +0200692 self.topic.edit(self.fake_session, rid, {"name": "new-name"})
garciadeblas4568a372021-03-24 09:19:48 +0100693 self.assertEqual(
694 e.exception.http_code,
695 HTTPStatus.FORBIDDEN,
696 "Wrong HTTP status code",
697 )
698 self.assertIn(
699 "you cannot rename role '{}'".format(role_name),
700 norm(str(e.exception)),
701 "Wrong exception text",
702 )
703 with self.subTest(i=i + 1):
delacruzramo79e40f42019-10-10 16:36:40 +0200704 new_name = "new-role-name"
garciadeblas4568a372021-03-24 09:19:48 +0100705 self.auth.get_role_list.side_effect = [
706 [{"_id": rid, "name": self.test_name, "permissions": {}}],
707 [{"_id": str(uuid4()), "name": new_name, "permissions": {}}],
708 ]
709 self.auth.get_role.return_value = {
710 "_id": rid,
711 "name": self.test_name,
712 "permissions": {},
713 }
714 with self.assertRaises(
715 EngineException, msg="Accepted existing role name"
716 ) as e:
delacruzramo79e40f42019-10-10 16:36:40 +0200717 self.topic.edit(self.fake_session, rid, {"name": new_name})
garciadeblas4568a372021-03-24 09:19:48 +0100718 self.assertEqual(
719 e.exception.http_code, HTTPStatus.CONFLICT, "Wrong HTTP status code"
720 )
721 self.assertIn(
722 "role name '{}' exists".format(new_name),
723 norm(str(e.exception)),
724 "Wrong exception text",
725 )
delacruzramo79e40f42019-10-10 16:36:40 +0200726
727 def test_conflict_on_del(self):
728 for i, role_name in enumerate(["system_admin", "project_admin"], start=1):
729 with self.subTest(i=i):
730 rid = str(uuid4())
731 role = {"_id": rid, "name": role_name}
732 self.auth.get_role_list.return_value = [role]
733 self.auth.get_role.return_value = role
garciadeblas4568a372021-03-24 09:19:48 +0100734 with self.assertRaises(
735 EngineException,
736 msg="Accepted deletion of role '{}'".format(role_name),
737 ) as e:
delacruzramo79e40f42019-10-10 16:36:40 +0200738 self.topic.delete(self.fake_session, rid)
garciadeblas4568a372021-03-24 09:19:48 +0100739 self.assertEqual(
740 e.exception.http_code,
741 HTTPStatus.FORBIDDEN,
742 "Wrong HTTP status code",
743 )
744 self.assertIn(
745 "you cannot delete role '{}'".format(role_name),
746 norm(str(e.exception)),
747 "Wrong exception text",
748 )
749 with self.subTest(i=i + 1):
delacruzramo79e40f42019-10-10 16:36:40 +0200750 rid = str(uuid4())
751 name = "other-role-name"
752 role = {"_id": rid, "name": name}
753 self.auth.get_role_list.return_value = [role]
754 self.auth.get_role.return_value = role
garciadeblas4568a372021-03-24 09:19:48 +0100755 self.auth.get_user_list.return_value = [
756 {
757 "_id": str(uuid4()),
758 "username": self.test_name,
759 "project_role_mappings": [{"project": str(uuid4()), "role": rid}],
760 }
761 ]
762 with self.assertRaises(
763 EngineException, msg="Accepted deletion of used role"
764 ) as e:
delacruzramo79e40f42019-10-10 16:36:40 +0200765 self.topic.delete(self.fake_session, rid)
garciadeblas4568a372021-03-24 09:19:48 +0100766 self.assertEqual(
767 e.exception.http_code, HTTPStatus.CONFLICT, "Wrong HTTP status code"
768 )
769 self.assertIn(
770 "role '{}' ({}) is being used by user '{}'".format(
771 name, rid, self.test_name
772 ),
773 norm(str(e.exception)),
774 "Wrong exception text",
775 )
delacruzramo79e40f42019-10-10 16:36:40 +0200776
777
778class Test_UserTopicAuth(TestCase):
delacruzramo79e40f42019-10-10 16:36:40 +0200779 @classmethod
780 def setUpClass(cls):
781 cls.test_name = "test-user-topic"
garciadeblas6d83f8f2023-06-19 22:34:49 +0200782 cls.password = "Test@123"
delacruzramo79e40f42019-10-10 16:36:40 +0200783
784 def setUp(self):
garciadeblas6d83f8f2023-06-19 22:34:49 +0200785 # self.db = Mock(dbbase.DbBase())
786 self.db = DbMemory()
delacruzramo79e40f42019-10-10 16:36:40 +0200787 self.fs = Mock(fsbase.FsBase())
788 self.msg = Mock(msgbase.MsgBase())
tierno9e87a7f2020-03-23 09:24:10 +0000789 self.auth = Mock(authconn.Authconn(None, None, None))
delacruzramo79e40f42019-10-10 16:36:40 +0200790 self.topic = UserTopicAuth(self.db, self.fs, self.msg, self.auth)
garciadeblas4568a372021-03-24 09:19:48 +0100791 self.fake_session = {
792 "username": test_name,
793 "project_id": (test_pid,),
794 "method": None,
795 "admin": True,
796 "force": False,
797 "public": False,
798 "allow_show_user_project_role": True,
799 }
tiernod7749582020-05-28 10:41:10 +0000800 self.topic.check_quota = Mock(return_value=None) # skip quota
delacruzramo79e40f42019-10-10 16:36:40 +0200801
802 def test_new_user(self):
803 uid1 = str(uuid4())
804 pid = str(uuid4())
805 self.auth.get_user_list.return_value = []
806 self.auth.get_project.return_value = {"_id": pid, "name": "some_project"}
807 self.auth.create_user.return_value = {"_id": uid1, "username": self.test_name}
808 with self.subTest(i=1):
809 rollback = []
810 rid = str(uuid4())
811 self.auth.get_role.return_value = {"_id": rid, "name": "some_role"}
812 prms_in = [{"project": "some_project", "role": "some_role"}]
813 prms_out = [{"project": pid, "role": rid}]
garciadeblas4568a372021-03-24 09:19:48 +0100814 uid2, oid = self.topic.new(
815 rollback,
816 self.fake_session,
817 {
818 "username": self.test_name,
garciadeblas6d83f8f2023-06-19 22:34:49 +0200819 "password": self.password,
garciadeblas4568a372021-03-24 09:19:48 +0100820 "project_role_mappings": prms_in,
821 },
822 )
delacruzramo79e40f42019-10-10 16:36:40 +0200823 self.assertEqual(len(rollback), 1, "Wrong rollback length")
824 self.assertEqual(uid2, uid1, "Wrong project identifier")
825 content = self.auth.create_user.call_args[0][0]
826 self.assertEqual(content["username"], self.test_name, "Wrong project name")
garciadeblas6d83f8f2023-06-19 22:34:49 +0200827 self.assertEqual(content["password"], self.password, "Wrong password")
garciadeblas4568a372021-03-24 09:19:48 +0100828 self.assertEqual(
829 content["project_role_mappings"],
830 prms_out,
831 "Wrong project-role mappings",
832 )
delacruzramo79e40f42019-10-10 16:36:40 +0200833 self.assertIsNotNone(content["_admin"]["created"], "Wrong creation time")
garciadeblas4568a372021-03-24 09:19:48 +0100834 self.assertEqual(
835 content["_admin"]["modified"],
836 content["_admin"]["created"],
837 "Wrong modification time",
838 )
delacruzramo79e40f42019-10-10 16:36:40 +0200839 with self.subTest(i=2):
840 rollback = []
841 def_rid = str(uuid4())
842 def_role = {"_id": def_rid, "name": "project_admin"}
843 self.auth.get_role.return_value = def_role
844 self.auth.get_role_list.return_value = [def_role]
845 prms_out = [{"project": pid, "role": def_rid}]
garciadeblas4568a372021-03-24 09:19:48 +0100846 uid2, oid = self.topic.new(
847 rollback,
848 self.fake_session,
849 {
850 "username": self.test_name,
garciadeblas6d83f8f2023-06-19 22:34:49 +0200851 "password": self.password,
garciadeblas4568a372021-03-24 09:19:48 +0100852 "projects": ["some_project"],
853 },
854 )
delacruzramo79e40f42019-10-10 16:36:40 +0200855 self.assertEqual(len(rollback), 1, "Wrong rollback length")
856 self.assertEqual(uid2, uid1, "Wrong project identifier")
857 content = self.auth.create_user.call_args[0][0]
858 self.assertEqual(content["username"], self.test_name, "Wrong project name")
garciadeblas6d83f8f2023-06-19 22:34:49 +0200859 self.assertEqual(content["password"], self.password, "Wrong password")
garciadeblas4568a372021-03-24 09:19:48 +0100860 self.assertEqual(
861 content["project_role_mappings"],
862 prms_out,
863 "Wrong project-role mappings",
864 )
delacruzramo79e40f42019-10-10 16:36:40 +0200865 self.assertIsNotNone(content["_admin"]["created"], "Wrong creation time")
garciadeblas4568a372021-03-24 09:19:48 +0100866 self.assertEqual(
867 content["_admin"]["modified"],
868 content["_admin"]["created"],
869 "Wrong modification time",
870 )
delacruzramo79e40f42019-10-10 16:36:40 +0200871 with self.subTest(i=3):
872 rollback = []
garciadeblas4568a372021-03-24 09:19:48 +0100873 with self.assertRaises(
874 EngineException, msg="Accepted wrong project-role mappings"
875 ) as e:
876 self.topic.new(
877 rollback,
878 self.fake_session,
879 {
880 "username": "other-project-name",
garciadeblas6d83f8f2023-06-19 22:34:49 +0200881 "password": "Other@pwd1",
garciadeblas4568a372021-03-24 09:19:48 +0100882 "project_role_mappings": [{}],
883 },
884 )
delacruzramo79e40f42019-10-10 16:36:40 +0200885 self.assertEqual(len(rollback), 0, "Wrong rollback length")
garciadeblas4568a372021-03-24 09:19:48 +0100886 self.assertEqual(
887 e.exception.http_code,
888 HTTPStatus.UNPROCESSABLE_ENTITY,
889 "Wrong HTTP status code",
890 )
891 self.assertIn(
892 "format error at '{}' '{}'".format(
893 "project_role_mappings:{}", "'{}' is a required property"
894 ).format(0, "project"),
895 norm(str(e.exception)),
896 "Wrong exception text",
897 )
delacruzramo79e40f42019-10-10 16:36:40 +0200898 with self.subTest(i=4):
899 rollback = []
900 with self.assertRaises(EngineException, msg="Accepted wrong projects") as e:
garciadeblas4568a372021-03-24 09:19:48 +0100901 self.topic.new(
902 rollback,
903 self.fake_session,
904 {
905 "username": "other-project-name",
garciadeblas6d83f8f2023-06-19 22:34:49 +0200906 "password": "Other@pwd1",
garciadeblas4568a372021-03-24 09:19:48 +0100907 "projects": [],
908 },
909 )
delacruzramo79e40f42019-10-10 16:36:40 +0200910 self.assertEqual(len(rollback), 0, "Wrong rollback length")
garciadeblas4568a372021-03-24 09:19:48 +0100911 self.assertEqual(
912 e.exception.http_code,
913 HTTPStatus.UNPROCESSABLE_ENTITY,
914 "Wrong HTTP status code",
915 )
916 self.assertIn(
917 "format error at '{}' '{}'".format(
918 "projects", "{} is too short"
919 ).format([]),
920 norm(str(e.exception)),
921 "Wrong exception text",
922 )
delacruzramo79e40f42019-10-10 16:36:40 +0200923
924 def test_edit_user(self):
925 now = time()
926 uid = str(uuid4())
927 pid1 = str(uuid4())
928 rid1 = str(uuid4())
Adurti8d046062024-05-07 06:04:37 +0000929 self.fake_session["user_id"] = uid
garciadeblas4568a372021-03-24 09:19:48 +0100930 prms = [
931 {
932 "project": pid1,
933 "project_name": "project-1",
934 "role": rid1,
935 "role_name": "role-1",
936 }
937 ]
938 user = {
939 "_id": uid,
940 "username": self.test_name,
941 "project_role_mappings": prms,
942 "_admin": {"created": now, "modified": now},
943 }
delacruzramo79e40f42019-10-10 16:36:40 +0200944 with self.subTest(i=1):
945 self.auth.get_user_list.side_effect = [[user], []]
946 self.auth.get_user.return_value = user
947 pid2 = str(uuid4())
948 rid2 = str(uuid4())
garciadeblas4568a372021-03-24 09:19:48 +0100949 self.auth.get_project.side_effect = [
950 {"_id": pid2, "name": "project-2"},
951 {"_id": pid1, "name": "project-1"},
952 ]
953 self.auth.get_role.side_effect = [
954 {"_id": rid2, "name": "role-2"},
955 {"_id": rid1, "name": "role-1"},
956 ]
Adurti8d046062024-05-07 06:04:37 +0000957
958 role = {
959 "_id": rid1,
960 "name": "role-1",
961 "permissions": {"default": False, "admin": False, "roles": True},
962 }
963 self.db.create("users", user)
964 self.db.create("roles", role)
delacruzramo79e40f42019-10-10 16:36:40 +0200965 new_name = "new-user-name"
garciadeblas6d83f8f2023-06-19 22:34:49 +0200966 new_pasw = "New@pwd1"
delacruzramo79e40f42019-10-10 16:36:40 +0200967 add_prms = [{"project": pid2, "role": rid2}]
968 rem_prms = [{"project": pid1, "role": rid1}]
garciadeblas4568a372021-03-24 09:19:48 +0100969 self.topic.edit(
970 self.fake_session,
971 uid,
972 {
973 "username": new_name,
974 "password": new_pasw,
975 "add_project_role_mappings": add_prms,
976 "remove_project_role_mappings": rem_prms,
977 },
978 )
delacruzramo79e40f42019-10-10 16:36:40 +0200979 content = self.auth.update_user.call_args[0][0]
980 self.assertEqual(content["_id"], uid, "Wrong user identifier")
981 self.assertEqual(content["username"], new_name, "Wrong user name")
982 self.assertEqual(content["password"], new_pasw, "Wrong user password")
garciadeblas4568a372021-03-24 09:19:48 +0100983 self.assertEqual(
984 content["add_project_role_mappings"],
985 add_prms,
986 "Wrong project-role mappings to add",
987 )
988 self.assertEqual(
989 content["remove_project_role_mappings"],
990 prms,
991 "Wrong project-role mappings to remove",
992 )
delacruzramo79e40f42019-10-10 16:36:40 +0200993 with self.subTest(i=2):
994 new_name = "other-user-name"
995 new_prms = [{}]
996 self.auth.get_role_list.side_effect = [[user], []]
Frank Brydendeba68e2020-07-27 13:55:11 +0000997 self.auth.get_user_list.side_effect = [[user]]
garciadeblas4568a372021-03-24 09:19:48 +0100998 with self.assertRaises(
999 EngineException, msg="Accepted wrong project-role mappings"
1000 ) as e:
1001 self.topic.edit(
1002 self.fake_session,
1003 uid,
1004 {"username": new_name, "project_role_mappings": new_prms},
1005 )
1006 self.assertEqual(
1007 e.exception.http_code,
1008 HTTPStatus.UNPROCESSABLE_ENTITY,
1009 "Wrong HTTP status code",
1010 )
1011 self.assertIn(
1012 "format error at '{}' '{}'".format(
1013 "project_role_mappings:{}", "'{}' is a required property"
1014 ).format(0, "project"),
1015 norm(str(e.exception)),
1016 "Wrong exception text",
1017 )
selvi.ja9a1fc82022-04-04 06:54:30 +00001018 with self.subTest(i=3):
1019 self.auth.get_user_list.side_effect = [[user], []]
1020 self.auth.get_user.return_value = user
garciadeblas6d83f8f2023-06-19 22:34:49 +02001021 old_password = self.password
1022 new_pasw = "New@pwd1"
selvi.ja9a1fc82022-04-04 06:54:30 +00001023 self.topic.edit(
1024 self.fake_session,
1025 uid,
1026 {
1027 "old_password": old_password,
1028 "password": new_pasw,
1029 },
1030 )
1031 content = self.auth.update_user.call_args[0][0]
garciadeblasf2af4a12023-01-24 16:56:54 +01001032 self.assertEqual(
1033 content["old_password"], old_password, "Wrong old password"
1034 )
selvi.ja9a1fc82022-04-04 06:54:30 +00001035 self.assertEqual(content["password"], new_pasw, "Wrong user password")
delacruzramo79e40f42019-10-10 16:36:40 +02001036
1037 def test_delete_user(self):
1038 with self.subTest(i=1):
1039 uid = str(uuid4())
1040 self.fake_session["username"] = self.test_name
garciadeblas4568a372021-03-24 09:19:48 +01001041 user = user = {
1042 "_id": uid,
1043 "username": "other-user-name",
1044 "project_role_mappings": [],
1045 }
delacruzramo79e40f42019-10-10 16:36:40 +02001046 self.auth.get_user.return_value = user
1047 self.auth.delete_user.return_value = {"deleted": 1}
1048 rc = self.topic.delete(self.fake_session, uid)
1049 self.assertEqual(rc, {"deleted": 1}, "Wrong user deletion return info")
garciadeblas4568a372021-03-24 09:19:48 +01001050 self.assertEqual(
1051 self.auth.get_user.call_args[0][0], uid, "Wrong user identifier"
1052 )
1053 self.assertEqual(
1054 self.auth.delete_user.call_args[0][0], uid, "Wrong user identifier"
1055 )
delacruzramo79e40f42019-10-10 16:36:40 +02001056
1057 def test_conflict_on_new(self):
1058 with self.subTest(i=1):
1059 rollback = []
1060 uid = str(uuid4())
garciadeblas4568a372021-03-24 09:19:48 +01001061 with self.assertRaises(
1062 EngineException, msg="Accepted uuid as username"
1063 ) as e:
1064 self.topic.new(
1065 rollback,
1066 self.fake_session,
1067 {
1068 "username": uid,
garciadeblas6d83f8f2023-06-19 22:34:49 +02001069 "password": self.password,
garciadeblas4568a372021-03-24 09:19:48 +01001070 "projects": [test_pid],
1071 },
1072 )
delacruzramo79e40f42019-10-10 16:36:40 +02001073 self.assertEqual(len(rollback), 0, "Wrong rollback length")
garciadeblas4568a372021-03-24 09:19:48 +01001074 self.assertEqual(
1075 e.exception.http_code,
1076 HTTPStatus.UNPROCESSABLE_ENTITY,
1077 "Wrong HTTP status code",
1078 )
1079 self.assertIn(
1080 "username '{}' cannot have a uuid format".format(uid),
1081 norm(str(e.exception)),
1082 "Wrong exception text",
1083 )
delacruzramo79e40f42019-10-10 16:36:40 +02001084 with self.subTest(i=2):
1085 rollback = []
garciadeblas4568a372021-03-24 09:19:48 +01001086 self.auth.get_user_list.return_value = [
1087 {"_id": str(uuid4()), "username": self.test_name}
1088 ]
1089 with self.assertRaises(
1090 EngineException, msg="Accepted existing username"
1091 ) as e:
1092 self.topic.new(
1093 rollback,
1094 self.fake_session,
1095 {
1096 "username": self.test_name,
garciadeblas6d83f8f2023-06-19 22:34:49 +02001097 "password": self.password,
garciadeblas4568a372021-03-24 09:19:48 +01001098 "projects": [test_pid],
1099 },
1100 )
delacruzramo79e40f42019-10-10 16:36:40 +02001101 self.assertEqual(len(rollback), 0, "Wrong rollback length")
garciadeblas4568a372021-03-24 09:19:48 +01001102 self.assertEqual(
1103 e.exception.http_code, HTTPStatus.CONFLICT, "Wrong HTTP status code"
1104 )
1105 self.assertIn(
1106 "username '{}' is already used".format(self.test_name),
1107 norm(str(e.exception)),
1108 "Wrong exception text",
1109 )
delacruzramo79e40f42019-10-10 16:36:40 +02001110 with self.subTest(i=3):
1111 rollback = []
1112 self.auth.get_user_list.return_value = []
1113 self.auth.get_role_list.side_effect = [[], []]
garciadeblas4568a372021-03-24 09:19:48 +01001114 with self.assertRaises(
1115 AuthconnNotFoundException, msg="Accepted user without default role"
1116 ) as e:
1117 self.topic.new(
1118 rollback,
1119 self.fake_session,
1120 {
1121 "username": self.test_name,
garciadeblas6d83f8f2023-06-19 22:34:49 +02001122 "password": self.password,
garciadeblas4568a372021-03-24 09:19:48 +01001123 "projects": [str(uuid4())],
1124 },
1125 )
delacruzramo79e40f42019-10-10 16:36:40 +02001126 self.assertEqual(len(rollback), 0, "Wrong rollback length")
garciadeblas4568a372021-03-24 09:19:48 +01001127 self.assertEqual(
1128 e.exception.http_code, HTTPStatus.NOT_FOUND, "Wrong HTTP status code"
1129 )
1130 self.assertIn(
1131 "can't find default role for user '{}'".format(self.test_name),
1132 norm(str(e.exception)),
1133 "Wrong exception text",
1134 )
delacruzramo79e40f42019-10-10 16:36:40 +02001135
1136 def test_conflict_on_edit(self):
1137 uid = str(uuid4())
1138 with self.subTest(i=1):
garciadeblas4568a372021-03-24 09:19:48 +01001139 self.auth.get_user_list.return_value = [
1140 {"_id": uid, "username": self.test_name}
1141 ]
delacruzramo79e40f42019-10-10 16:36:40 +02001142 new_name = str(uuid4())
garciadeblas4568a372021-03-24 09:19:48 +01001143 with self.assertRaises(
1144 EngineException, msg="Accepted uuid as username"
1145 ) as e:
delacruzramo79e40f42019-10-10 16:36:40 +02001146 self.topic.edit(self.fake_session, uid, {"username": new_name})
garciadeblas4568a372021-03-24 09:19:48 +01001147 self.assertEqual(
1148 e.exception.http_code,
1149 HTTPStatus.UNPROCESSABLE_ENTITY,
1150 "Wrong HTTP status code",
1151 )
1152 self.assertIn(
1153 "username '{}' cannot have an uuid format".format(new_name),
1154 norm(str(e.exception)),
1155 "Wrong exception text",
1156 )
delacruzramo79e40f42019-10-10 16:36:40 +02001157 with self.subTest(i=2):
garciadeblas4568a372021-03-24 09:19:48 +01001158 self.auth.get_user_list.return_value = [
1159 {"_id": uid, "username": self.test_name}
1160 ]
delacruzramo79e40f42019-10-10 16:36:40 +02001161 self.auth.get_role_list.side_effect = [[], []]
garciadeblas4568a372021-03-24 09:19:48 +01001162 with self.assertRaises(
1163 AuthconnNotFoundException, msg="Accepted user without default role"
1164 ) as e:
delacruzramo79e40f42019-10-10 16:36:40 +02001165 self.topic.edit(self.fake_session, uid, {"projects": [str(uuid4())]})
garciadeblas4568a372021-03-24 09:19:48 +01001166 self.assertEqual(
1167 e.exception.http_code, HTTPStatus.NOT_FOUND, "Wrong HTTP status code"
1168 )
1169 self.assertIn(
1170 "can't find a default role for user '{}'".format(self.test_name),
1171 norm(str(e.exception)),
1172 "Wrong exception text",
1173 )
delacruzramo79e40f42019-10-10 16:36:40 +02001174 with self.subTest(i=3):
1175 admin_uid = str(uuid4())
garciadeblas4568a372021-03-24 09:19:48 +01001176 self.auth.get_user_list.return_value = [
1177 {"_id": admin_uid, "username": "admin"}
1178 ]
1179 with self.assertRaises(
1180 EngineException,
1181 msg="Accepted removing system_admin role from admin user",
1182 ) as e:
1183 self.topic.edit(
1184 self.fake_session,
1185 admin_uid,
1186 {
1187 "remove_project_role_mappings": [
1188 {"project": "admin", "role": "system_admin"}
1189 ]
1190 },
1191 )
1192 self.assertEqual(
1193 e.exception.http_code, HTTPStatus.FORBIDDEN, "Wrong HTTP status code"
1194 )
1195 self.assertIn(
1196 "you cannot remove system_admin role from admin user",
1197 norm(str(e.exception)),
1198 "Wrong exception text",
1199 )
delacruzramo79e40f42019-10-10 16:36:40 +02001200 with self.subTest(i=4):
1201 new_name = "new-user-name"
garciadeblas4568a372021-03-24 09:19:48 +01001202 self.auth.get_user_list.side_effect = [
1203 [{"_id": uid, "name": self.test_name}],
1204 [{"_id": str(uuid4()), "name": new_name}],
1205 ]
1206 with self.assertRaises(
1207 EngineException, msg="Accepted existing username"
1208 ) as e:
delacruzramo79e40f42019-10-10 16:36:40 +02001209 self.topic.edit(self.fake_session, uid, {"username": new_name})
garciadeblas4568a372021-03-24 09:19:48 +01001210 self.assertEqual(
1211 e.exception.http_code, HTTPStatus.CONFLICT, "Wrong HTTP status code"
1212 )
1213 self.assertIn(
1214 "username '{}' is already used".format(new_name),
1215 norm(str(e.exception)),
1216 "Wrong exception text",
1217 )
delacruzramo79e40f42019-10-10 16:36:40 +02001218
1219 def test_conflict_on_del(self):
1220 with self.subTest(i=1):
1221 uid = str(uuid4())
1222 self.fake_session["username"] = self.test_name
garciadeblas4568a372021-03-24 09:19:48 +01001223 user = user = {
1224 "_id": uid,
1225 "username": self.test_name,
1226 "project_role_mappings": [],
1227 }
delacruzramo79e40f42019-10-10 16:36:40 +02001228 self.auth.get_user.return_value = user
garciadeblas4568a372021-03-24 09:19:48 +01001229 with self.assertRaises(
1230 EngineException, msg="Accepted deletion of own user"
1231 ) as e:
delacruzramo79e40f42019-10-10 16:36:40 +02001232 self.topic.delete(self.fake_session, uid)
garciadeblas4568a372021-03-24 09:19:48 +01001233 self.assertEqual(
1234 e.exception.http_code, HTTPStatus.CONFLICT, "Wrong HTTP status code"
1235 )
1236 self.assertIn(
1237 "you cannot delete your own login user",
1238 norm(str(e.exception)),
1239 "Wrong exception text",
1240 )
delacruzramo79e40f42019-10-10 16:36:40 +02001241
garciadeblas6d83f8f2023-06-19 22:34:49 +02001242 def test_user_management(self):
1243 self.config = {
1244 "user_management": True,
1245 "pwd_expire_days": 30,
1246 "max_pwd_attempt": 5,
1247 "account_expire_days": 90,
1248 "version": "dev",
1249 "deviceVendor": "test",
1250 "deviceProduct": "test",
1251 }
1252 self.permissions = {"admin": True, "default": True}
1253 now = time()
1254 rid = str(uuid4())
1255 role = {
1256 "_id": rid,
1257 "name": self.test_name,
1258 "permissions": self.permissions,
1259 "_admin": {"created": now, "modified": now},
1260 }
1261 self.db.create("roles", role)
1262 admin_user = {
1263 "_id": "72cd0cd6-e8e2-482c-9bc2-15b413bb8500",
1264 "username": "admin",
1265 "password": "bf0d9f988ad9b404464cf8c8749b298209b05fd404119bae0c11e247efbbc4cb",
1266 "_admin": {
1267 "created": 1663058370.7721832,
1268 "modified": 1663681183.5651639,
1269 "salt": "37587e7e0c2f4dbfb9416f3fb5543e2b",
1270 "last_token_time": 1666876472.2962265,
1271 "user_status": "always-active",
1272 "retry_count": 0,
1273 },
1274 "project_role_mappings": [
1275 {"project": "a595ce4e-09dc-4b24-9d6f-e723830bc66b", "role": rid}
1276 ],
1277 }
1278 self.db.create("users", admin_user)
1279 with self.subTest(i=1):
1280 self.user_create = AuthconnInternal(self.config, self.db, self.permissions)
1281 user_info = {"username": "user_mgmt_true", "password": "Test@123"}
1282 self.user_create.create_user(user_info)
1283 user = self.db.get_one("users", {"username": user_info["username"]})
1284 self.assertEqual(user["username"], user_info["username"], "Wrong user name")
1285 self.assertEqual(
1286 user["_admin"]["user_status"], "active", "User status is unknown"
1287 )
1288 self.assertIn("password_expire_time", user["_admin"], "Key is not there")
1289 self.assertIn("account_expire_time", user["_admin"], "Key is not there")
1290 with self.subTest(i=2):
1291 self.user_update = AuthconnInternal(self.config, self.db, self.permissions)
1292 locked_user = {
1293 "username": "user_lock",
1294 "password": "c94ba8cfe81985cf5c84dff16d5bac95814ab17e44a8871755eb4cf3a27b7d3d",
1295 "_admin": {
1296 "created": 1667207552.2191198,
1297 "modified": 1667207552.2191815,
1298 "salt": "560a5d51b1d64bb4b9cae0ccff3f1102",
1299 "user_status": "locked",
1300 "password_expire_time": 1667207552.2191815,
Adurti9a0c4be2023-11-08 11:16:32 +00001301 "account_expire_time": now + 60,
garciadeblas6d83f8f2023-06-19 22:34:49 +02001302 "retry_count": 5,
1303 "last_token_time": 1667207552.2191815,
1304 },
1305 "_id": "73bbbb71-ed38-4b79-9f58-ece19e7e32d6",
1306 }
1307 self.db.create("users", locked_user)
1308 user_info = {
1309 "_id": "73bbbb71-ed38-4b79-9f58-ece19e7e32d6",
1310 "system_admin_id": "72cd0cd6-e8e2-482c-9bc2-15b413bb8500",
1311 "unlock": True,
1312 }
1313 self.assertEqual(
1314 locked_user["_admin"]["user_status"], "locked", "User status is unknown"
1315 )
1316 self.user_update.update_user(user_info)
1317 user = self.db.get_one("users", {"username": locked_user["username"]})
1318 self.assertEqual(
1319 user["username"], locked_user["username"], "Wrong user name"
1320 )
1321 self.assertEqual(
1322 user["_admin"]["user_status"], "active", "User status is unknown"
1323 )
1324 self.assertEqual(user["_admin"]["retry_count"], 0, "retry_count is unknown")
1325 with self.subTest(i=3):
1326 self.user_update = AuthconnInternal(self.config, self.db, self.permissions)
1327 expired_user = {
1328 "username": "user_expire",
1329 "password": "c94ba8cfe81985cf5c84dff16d5bac95814ab17e44a8871755eb4cf3a27b7d3d",
1330 "_admin": {
1331 "created": 1665602087.601298,
1332 "modified": 1665636442.1245084,
1333 "salt": "560a5d51b1d64bb4b9cae0ccff3f1102",
1334 "user_status": "expired",
1335 "password_expire_time": 1668248628.2191815,
1336 "account_expire_time": 1666952628.2191815,
1337 "retry_count": 0,
1338 "last_token_time": 1666779828.2171815,
1339 },
1340 "_id": "3266430f-8222-407f-b08f-3a242504ab94",
1341 }
1342 self.db.create("users", expired_user)
1343 user_info = {
1344 "_id": "3266430f-8222-407f-b08f-3a242504ab94",
1345 "system_admin_id": "72cd0cd6-e8e2-482c-9bc2-15b413bb8500",
1346 "renew": True,
1347 }
1348 self.assertEqual(
1349 expired_user["_admin"]["user_status"],
1350 "expired",
1351 "User status is unknown",
1352 )
1353 self.user_update.update_user(user_info)
1354 user = self.db.get_one("users", {"username": expired_user["username"]})
1355 self.assertEqual(
1356 user["username"], expired_user["username"], "Wrong user name"
1357 )
1358 self.assertEqual(
1359 user["_admin"]["user_status"], "active", "User status is unknown"
1360 )
1361 self.assertGreater(
1362 user["_admin"]["account_expire_time"],
1363 expired_user["_admin"]["account_expire_time"],
1364 "User expire time is not get extended",
1365 )
1366 with self.subTest(i=4):
1367 self.config.update({"user_management": False})
1368 self.user_create = AuthconnInternal(self.config, self.db, self.permissions)
1369 user_info = {"username": "user_mgmt_false", "password": "Test@123"}
1370 self.user_create.create_user(user_info)
1371 user = self.db.get_one("users", {"username": user_info["username"]})
1372 self.assertEqual(user["username"], user_info["username"], "Wrong user name")
1373 self.assertEqual(
1374 user["_admin"]["user_status"], "active", "User status is unknown"
1375 )
1376 self.assertNotIn("password_expire_time", user["_admin"], "Key is not there")
1377 self.assertNotIn("account_expire_time", user["_admin"], "Key is not there")
1378
delacruzramo79e40f42019-10-10 16:36:40 +02001379
1380class Test_CommonVimWimSdn(TestCase):
delacruzramo79e40f42019-10-10 16:36:40 +02001381 @classmethod
1382 def setUpClass(cls):
garciadeblas4568a372021-03-24 09:19:48 +01001383 cls.test_name = "test-cim-topic" # CIM = Common Infrastructure Manager
delacruzramo79e40f42019-10-10 16:36:40 +02001384
1385 def setUp(self):
1386 self.db = Mock(dbbase.DbBase())
1387 self.fs = Mock(fsbase.FsBase())
1388 self.msg = Mock(msgbase.MsgBase())
tierno9e87a7f2020-03-23 09:24:10 +00001389 self.auth = Mock(authconn.Authconn(None, None, None))
delacruzramo79e40f42019-10-10 16:36:40 +02001390 self.topic = CommonVimWimSdn(self.db, self.fs, self.msg, self.auth)
1391 # Use WIM schemas for testing because they are the simplest
tiernof5f2e3f2020-03-23 14:42:10 +00001392 self.topic._send_msg = Mock()
delacruzramo79e40f42019-10-10 16:36:40 +02001393 self.topic.topic = "wims"
1394 self.topic.schema_new = validation.wim_account_new_schema
1395 self.topic.schema_edit = validation.wim_account_edit_schema
garciadeblas4568a372021-03-24 09:19:48 +01001396 self.fake_session = {
1397 "username": test_name,
1398 "project_id": (test_pid,),
1399 "method": None,
1400 "admin": True,
1401 "force": False,
1402 "public": False,
1403 "allow_show_user_project_role": True,
1404 }
tiernod7749582020-05-28 10:41:10 +00001405 self.topic.check_quota = Mock(return_value=None) # skip quota
delacruzramo79e40f42019-10-10 16:36:40 +02001406
1407 def test_new_cvws(self):
1408 test_url = "http://0.0.0.0:0"
1409 with self.subTest(i=1):
1410 rollback = []
1411 test_type = "fake"
1412 self.db.get_one.return_value = None
1413 self.db.create.side_effect = lambda self, content: content["_id"]
garciadeblas4568a372021-03-24 09:19:48 +01001414 cid, oid = self.topic.new(
1415 rollback,
1416 self.fake_session,
1417 {"name": self.test_name, "wim_url": test_url, "wim_type": test_type},
1418 )
delacruzramo79e40f42019-10-10 16:36:40 +02001419 self.assertEqual(len(rollback), 1, "Wrong rollback length")
1420 args = self.db.create.call_args[0]
1421 content = args[1]
1422 self.assertEqual(args[0], self.topic.topic, "Wrong topic")
1423 self.assertEqual(content["_id"], cid, "Wrong CIM identifier")
1424 self.assertEqual(content["name"], self.test_name, "Wrong CIM name")
1425 self.assertEqual(content["wim_url"], test_url, "Wrong URL")
1426 self.assertEqual(content["wim_type"], test_type, "Wrong CIM type")
1427 self.assertEqual(content["schema_version"], "1.11", "Wrong schema version")
1428 self.assertEqual(content["op_id"], oid, "Wrong operation identifier")
1429 self.assertIsNotNone(content["_admin"]["created"], "Wrong creation time")
garciadeblas4568a372021-03-24 09:19:48 +01001430 self.assertEqual(
1431 content["_admin"]["modified"],
1432 content["_admin"]["created"],
1433 "Wrong modification time",
1434 )
1435 self.assertEqual(
1436 content["_admin"]["operationalState"],
1437 "PROCESSING",
1438 "Wrong operational state",
1439 )
1440 self.assertEqual(
1441 content["_admin"]["projects_read"],
1442 [test_pid],
1443 "Wrong read-only projects",
1444 )
1445 self.assertEqual(
1446 content["_admin"]["projects_write"],
1447 [test_pid],
1448 "Wrong read/write projects",
1449 )
1450 self.assertIsNone(
1451 content["_admin"]["current_operation"], "Wrong current operation"
1452 )
1453 self.assertEqual(
1454 len(content["_admin"]["operations"]), 1, "Wrong number of operations"
1455 )
delacruzramo79e40f42019-10-10 16:36:40 +02001456 operation = content["_admin"]["operations"][0]
garciadeblas4568a372021-03-24 09:19:48 +01001457 self.assertEqual(
1458 operation["lcmOperationType"], "create", "Wrong operation type"
1459 )
1460 self.assertEqual(
1461 operation["operationState"], "PROCESSING", "Wrong operation state"
1462 )
1463 self.assertGreater(
1464 operation["startTime"],
1465 content["_admin"]["created"],
1466 "Wrong operation start time",
1467 )
1468 self.assertGreater(
1469 operation["statusEnteredTime"],
1470 content["_admin"]["created"],
1471 "Wrong operation status enter time",
1472 )
1473 self.assertEqual(
1474 operation["detailed-status"], "", "Wrong operation detailed status info"
1475 )
1476 self.assertIsNone(
1477 operation["operationParams"], "Wrong operation parameters"
1478 )
tiernob3d0a0e2019-11-13 15:57:51 +00001479 # This test is disabled. From Feature 8030 we admit all WIM/SDN types
1480 # with self.subTest(i=2):
1481 # rollback = []
1482 # test_type = "bad_type"
1483 # with self.assertRaises(EngineException, msg="Accepted wrong CIM type") as e:
1484 # self.topic.new(rollback, self.fake_session,
1485 # {"name": self.test_name, "wim_url": test_url, "wim_type": test_type})
1486 # self.assertEqual(len(rollback), 0, "Wrong rollback length")
1487 # self.assertEqual(e.exception.http_code, HTTPStatus.UNPROCESSABLE_ENTITY, "Wrong HTTP status code")
1488 # self.assertIn("format error at '{}' '{}".format("wim_type", "'{}' is not one of {}").format(test_type,""),
1489 # norm(str(e.exception)), "Wrong exception text")
delacruzramo79e40f42019-10-10 16:36:40 +02001490
1491 def test_conflict_on_new(self):
1492 with self.subTest(i=1):
1493 rollback = []
1494 test_url = "http://0.0.0.0:0"
1495 test_type = "fake"
1496 self.db.get_one.return_value = {"_id": str(uuid4()), "name": self.test_name}
garciadeblas4568a372021-03-24 09:19:48 +01001497 with self.assertRaises(
1498 EngineException, msg="Accepted existing CIM name"
1499 ) as e:
1500 self.topic.new(
1501 rollback,
1502 self.fake_session,
1503 {
1504 "name": self.test_name,
1505 "wim_url": test_url,
1506 "wim_type": test_type,
1507 },
1508 )
delacruzramo79e40f42019-10-10 16:36:40 +02001509 self.assertEqual(len(rollback), 0, "Wrong rollback length")
garciadeblas4568a372021-03-24 09:19:48 +01001510 self.assertEqual(
1511 e.exception.http_code, HTTPStatus.CONFLICT, "Wrong HTTP status code"
1512 )
1513 self.assertIn(
1514 "name '{}' already exists for {}".format(
1515 self.test_name, self.topic.topic
1516 ),
1517 norm(str(e.exception)),
1518 "Wrong exception text",
1519 )
delacruzramo79e40f42019-10-10 16:36:40 +02001520
1521 def test_edit_cvws(self):
1522 now = time()
1523 cid = str(uuid4())
1524 test_url = "http://0.0.0.0:0"
1525 test_type = "fake"
garciadeblas4568a372021-03-24 09:19:48 +01001526 cvws = {
1527 "_id": cid,
1528 "name": self.test_name,
1529 "wim_url": test_url,
1530 "wim_type": test_type,
1531 "_admin": {
1532 "created": now,
1533 "modified": now,
1534 "operations": [{"lcmOperationType": "create"}],
1535 },
1536 }
delacruzramo79e40f42019-10-10 16:36:40 +02001537 with self.subTest(i=1):
1538 new_name = "new-cim-name"
1539 new_url = "https://1.1.1.1:1"
1540 new_type = "onos"
1541 self.db.get_one.side_effect = [cvws, None]
1542 self.db.replace.return_value = {"updated": 1}
1543 # self.db.encrypt.side_effect = [b64str(), b64str()]
garciadeblas4568a372021-03-24 09:19:48 +01001544 self.topic.edit(
1545 self.fake_session,
1546 cid,
1547 {"name": new_name, "wim_url": new_url, "wim_type": new_type},
1548 )
delacruzramo79e40f42019-10-10 16:36:40 +02001549 args = self.db.replace.call_args[0]
1550 content = args[2]
1551 self.assertEqual(args[0], self.topic.topic, "Wrong topic")
1552 self.assertEqual(args[1], cid, "Wrong CIM identifier")
1553 self.assertEqual(content["_id"], cid, "Wrong CIM identifier")
1554 self.assertEqual(content["name"], new_name, "Wrong CIM name")
1555 self.assertEqual(content["wim_type"], new_type, "Wrong CIM type")
1556 self.assertEqual(content["wim_url"], new_url, "Wrong URL")
1557 self.assertEqual(content["_admin"]["created"], now, "Wrong creation time")
garciadeblas4568a372021-03-24 09:19:48 +01001558 self.assertGreater(
1559 content["_admin"]["modified"],
1560 content["_admin"]["created"],
1561 "Wrong modification time",
1562 )
1563 self.assertEqual(
1564 len(content["_admin"]["operations"]), 2, "Wrong number of operations"
1565 )
delacruzramo79e40f42019-10-10 16:36:40 +02001566 operation = content["_admin"]["operations"][1]
garciadeblas4568a372021-03-24 09:19:48 +01001567 self.assertEqual(
1568 operation["lcmOperationType"], "edit", "Wrong operation type"
1569 )
1570 self.assertEqual(
1571 operation["operationState"], "PROCESSING", "Wrong operation state"
1572 )
1573 self.assertGreater(
1574 operation["startTime"],
1575 content["_admin"]["modified"],
1576 "Wrong operation start time",
1577 )
1578 self.assertGreater(
1579 operation["statusEnteredTime"],
1580 content["_admin"]["modified"],
1581 "Wrong operation status enter time",
1582 )
1583 self.assertEqual(
1584 operation["detailed-status"], "", "Wrong operation detailed status info"
1585 )
1586 self.assertIsNone(
1587 operation["operationParams"], "Wrong operation parameters"
1588 )
delacruzramo79e40f42019-10-10 16:36:40 +02001589 with self.subTest(i=2):
Frank Brydendeba68e2020-07-27 13:55:11 +00001590 self.db.get_one.side_effect = [cvws]
delacruzramo79e40f42019-10-10 16:36:40 +02001591 with self.assertRaises(EngineException, msg="Accepted wrong property") as e:
garciadeblas4568a372021-03-24 09:19:48 +01001592 self.topic.edit(
1593 self.fake_session,
1594 str(uuid4()),
1595 {"name": "new-name", "extra_prop": "anything"},
1596 )
1597 self.assertEqual(
1598 e.exception.http_code,
1599 HTTPStatus.UNPROCESSABLE_ENTITY,
1600 "Wrong HTTP status code",
1601 )
1602 self.assertIn(
1603 "format error '{}'".format(
1604 "additional properties are not allowed ('{}' was unexpected)"
1605 ).format("extra_prop"),
1606 norm(str(e.exception)),
1607 "Wrong exception text",
1608 )
delacruzramo79e40f42019-10-10 16:36:40 +02001609
1610 def test_conflict_on_edit(self):
1611 with self.subTest(i=1):
1612 cid = str(uuid4())
1613 new_name = "new-cim-name"
garciadeblas4568a372021-03-24 09:19:48 +01001614 self.db.get_one.side_effect = [
1615 {"_id": cid, "name": self.test_name},
1616 {"_id": str(uuid4()), "name": new_name},
1617 ]
1618 with self.assertRaises(
1619 EngineException, msg="Accepted existing CIM name"
1620 ) as e:
delacruzramo79e40f42019-10-10 16:36:40 +02001621 self.topic.edit(self.fake_session, cid, {"name": new_name})
garciadeblas4568a372021-03-24 09:19:48 +01001622 self.assertEqual(
1623 e.exception.http_code, HTTPStatus.CONFLICT, "Wrong HTTP status code"
1624 )
1625 self.assertIn(
1626 "name '{}' already exists for {}".format(new_name, self.topic.topic),
1627 norm(str(e.exception)),
1628 "Wrong exception text",
1629 )
delacruzramo79e40f42019-10-10 16:36:40 +02001630
1631 def test_delete_cvws(self):
1632 cid = str(uuid4())
1633 ro_pid = str(uuid4())
1634 rw_pid = str(uuid4())
1635 cvws = {"_id": cid, "name": self.test_name}
delacruzramo35c998b2019-11-21 11:09:16 +01001636 self.db.get_list.return_value = []
delacruzramo79e40f42019-10-10 16:36:40 +02001637 with self.subTest(i=1):
garciadeblas4568a372021-03-24 09:19:48 +01001638 cvws["_admin"] = {
1639 "projects_read": [test_pid, ro_pid, rw_pid],
1640 "projects_write": [test_pid, rw_pid],
1641 }
delacruzramo79e40f42019-10-10 16:36:40 +02001642 self.db.get_one.return_value = cvws
1643 oid = self.topic.delete(self.fake_session, cid)
1644 self.assertIsNone(oid, "Wrong operation identifier")
garciadeblas4568a372021-03-24 09:19:48 +01001645 self.assertEqual(
1646 self.db.get_one.call_args[0][0], self.topic.topic, "Wrong topic"
1647 )
1648 self.assertEqual(
1649 self.db.get_one.call_args[0][1]["_id"], cid, "Wrong CIM identifier"
1650 )
1651 self.assertEqual(
1652 self.db.set_one.call_args[0][0], self.topic.topic, "Wrong topic"
1653 )
1654 self.assertEqual(
1655 self.db.set_one.call_args[0][1]["_id"], cid, "Wrong CIM identifier"
1656 )
1657 self.assertEqual(
1658 self.db.set_one.call_args[1]["update_dict"],
1659 None,
1660 "Wrong read-only projects update",
1661 )
1662 self.assertEqual(
1663 self.db.set_one.call_args[1]["pull_list"],
1664 {
1665 "_admin.projects_read": (test_pid,),
1666 "_admin.projects_write": (test_pid,),
1667 },
1668 "Wrong read/write projects update",
1669 )
tiernof5f2e3f2020-03-23 14:42:10 +00001670 self.topic._send_msg.assert_not_called()
delacruzramo35c998b2019-11-21 11:09:16 +01001671 with self.subTest(i=2):
delacruzramo79e40f42019-10-10 16:36:40 +02001672 now = time()
garciadeblas4568a372021-03-24 09:19:48 +01001673 cvws["_admin"] = {
1674 "projects_read": [test_pid],
1675 "projects_write": [test_pid],
1676 "operations": [],
1677 }
delacruzramo79e40f42019-10-10 16:36:40 +02001678 self.db.get_one.return_value = cvws
1679 oid = self.topic.delete(self.fake_session, cid)
garciadeblas4568a372021-03-24 09:19:48 +01001680 self.assertEqual(oid, cid + ":0", "Wrong operation identifier")
1681 self.assertEqual(
1682 self.db.get_one.call_args[0][0], self.topic.topic, "Wrong topic"
1683 )
1684 self.assertEqual(
1685 self.db.get_one.call_args[0][1]["_id"], cid, "Wrong CIM identifier"
1686 )
1687 self.assertEqual(
1688 self.db.set_one.call_args[0][0], self.topic.topic, "Wrong topic"
1689 )
1690 self.assertEqual(
1691 self.db.set_one.call_args[0][1]["_id"], cid, "Wrong user identifier"
1692 )
1693 self.assertEqual(
1694 self.db.set_one.call_args[1]["update_dict"],
1695 {"_admin.to_delete": True},
1696 "Wrong _admin.to_delete update",
1697 )
delacruzramo79e40f42019-10-10 16:36:40 +02001698 operation = self.db.set_one.call_args[1]["push"]["_admin.operations"]
garciadeblas4568a372021-03-24 09:19:48 +01001699 self.assertEqual(
1700 operation["lcmOperationType"], "delete", "Wrong operation type"
1701 )
1702 self.assertEqual(
1703 operation["operationState"], "PROCESSING", "Wrong operation state"
1704 )
1705 self.assertEqual(
1706 operation["detailed-status"], "", "Wrong operation detailed status"
1707 )
1708 self.assertIsNone(
1709 operation["operationParams"], "Wrong operation parameters"
1710 )
1711 self.assertGreater(
1712 operation["startTime"], now, "Wrong operation start time"
1713 )
1714 self.assertGreater(
1715 operation["statusEnteredTime"], now, "Wrong operation status enter time"
1716 )
1717 self.topic._send_msg.assert_called_once_with(
1718 "delete", {"_id": cid, "op_id": cid + ":0"}, not_send_msg=None
1719 )
delacruzramo79e40f42019-10-10 16:36:40 +02001720 with self.subTest(i=3):
garciadeblas4568a372021-03-24 09:19:48 +01001721 cvws["_admin"] = {
1722 "projects_read": [],
1723 "projects_write": [],
1724 "operations": [],
1725 }
delacruzramo79e40f42019-10-10 16:36:40 +02001726 self.db.get_one.return_value = cvws
tiernof5f2e3f2020-03-23 14:42:10 +00001727 self.topic._send_msg.reset_mock()
1728 self.db.get_one.reset_mock()
1729 self.db.del_one.reset_mock()
garciadeblas4568a372021-03-24 09:19:48 +01001730 self.fake_session["force"] = True # to force deletion
1731 self.fake_session["admin"] = True # to force deletion
1732 self.fake_session["project_id"] = [] # to force deletion
delacruzramo79e40f42019-10-10 16:36:40 +02001733 oid = self.topic.delete(self.fake_session, cid)
1734 self.assertIsNone(oid, "Wrong operation identifier")
garciadeblas4568a372021-03-24 09:19:48 +01001735 self.assertEqual(
1736 self.db.get_one.call_args[0][0], self.topic.topic, "Wrong topic"
1737 )
1738 self.assertEqual(
1739 self.db.get_one.call_args[0][1]["_id"], cid, "Wrong CIM identifier"
1740 )
1741 self.assertEqual(
1742 self.db.del_one.call_args[0][0], self.topic.topic, "Wrong topic"
1743 )
1744 self.assertEqual(
1745 self.db.del_one.call_args[0][1]["_id"], cid, "Wrong CIM identifier"
1746 )
1747 self.topic._send_msg.assert_called_once_with(
1748 "deleted", {"_id": cid, "op_id": None}, not_send_msg=None
1749 )
delacruzramo79e40f42019-10-10 16:36:40 +02001750
1751
garciadeblas4568a372021-03-24 09:19:48 +01001752if __name__ == "__main__":
delacruzramo79e40f42019-10-10 16:36:40 +02001753 unittest.main()