]>
Commit | Line | Data |
---|---|---|
11fdf7f2 TL |
1 | # -*- coding: utf-8 -*- |
2 | ||
3 | from __future__ import absolute_import | |
4 | ||
9f95a23c TL |
5 | import time |
6 | ||
7 | from datetime import datetime, timedelta | |
8 | ||
e306af50 | 9 | from tasks.mgr.dashboard.helper import DashboardTestCase, JObj, JLeaf |
11fdf7f2 TL |
10 | |
11 | ||
12 | class UserTest(DashboardTestCase): | |
9f95a23c TL |
13 | @classmethod |
14 | def setUpClass(cls): | |
15 | super(UserTest, cls).setUpClass() | |
16 | cls._ceph_cmd(['dashboard', 'set-pwd-policy-enabled', 'true']) | |
17 | cls._ceph_cmd(['dashboard', 'set-pwd-policy-check-length-enabled', 'true']) | |
18 | cls._ceph_cmd(['dashboard', 'set-pwd-policy-check-oldpwd-enabled', 'true']) | |
19 | cls._ceph_cmd(['dashboard', 'set-pwd-policy-check-username-enabled', 'true']) | |
20 | cls._ceph_cmd(['dashboard', 'set-pwd-policy-check-exclusion-list-enabled', 'true']) | |
21 | cls._ceph_cmd(['dashboard', 'set-pwd-policy-check-complexity-enabled', 'true']) | |
22 | cls._ceph_cmd(['dashboard', 'set-pwd-policy-check-sequential-chars-enabled', 'true']) | |
23 | cls._ceph_cmd(['dashboard', 'set-pwd-policy-check-repetitive-chars-enabled', 'true']) | |
24 | ||
25 | @classmethod | |
26 | def tearDownClass(cls): | |
27 | cls._ceph_cmd(['dashboard', 'set-pwd-policy-check-username-enabled', 'false']) | |
28 | cls._ceph_cmd(['dashboard', 'set-pwd-policy-check-exclusion-list-enabled', 'false']) | |
29 | cls._ceph_cmd(['dashboard', 'set-pwd-policy-check-complexity-enabled', 'false']) | |
30 | cls._ceph_cmd(['dashboard', 'set-pwd-policy-check-sequential-chars-enabled', 'false']) | |
31 | cls._ceph_cmd(['dashboard', 'set-pwd-policy-check-repetitive-chars-enabled', 'false']) | |
32 | super(UserTest, cls).tearDownClass() | |
11fdf7f2 TL |
33 | |
34 | @classmethod | |
9f95a23c TL |
35 | def _create_user(cls, username=None, password=None, name=None, email=None, roles=None, |
36 | enabled=True, pwd_expiration_date=None, pwd_update_required=False): | |
11fdf7f2 TL |
37 | data = {} |
38 | if username: | |
39 | data['username'] = username | |
40 | if password: | |
41 | data['password'] = password | |
42 | if name: | |
43 | data['name'] = name | |
44 | if email: | |
45 | data['email'] = email | |
46 | if roles: | |
47 | data['roles'] = roles | |
9f95a23c TL |
48 | if pwd_expiration_date: |
49 | data['pwdExpirationDate'] = pwd_expiration_date | |
50 | data['pwdUpdateRequired'] = pwd_update_required | |
51 | data['enabled'] = enabled | |
11fdf7f2 TL |
52 | cls._post("/api/user", data) |
53 | ||
9f95a23c TL |
54 | @classmethod |
55 | def _reset_login_to_admin(cls, username=None): | |
56 | cls.logout() | |
57 | if username: | |
58 | cls.delete_user(username) | |
59 | cls.login('admin', 'admin') | |
60 | ||
11fdf7f2 TL |
61 | def test_crud_user(self): |
62 | self._create_user(username='user1', | |
9f95a23c | 63 | password='mypassword10#', |
11fdf7f2 TL |
64 | name='My Name', |
65 | email='my@email.com', | |
66 | roles=['administrator']) | |
67 | self.assertStatus(201) | |
68 | user = self.jsonBody() | |
69 | ||
70 | self._get('/api/user/user1') | |
71 | self.assertStatus(200) | |
72 | self.assertJsonBody({ | |
73 | 'username': 'user1', | |
74 | 'name': 'My Name', | |
75 | 'email': 'my@email.com', | |
76 | 'roles': ['administrator'], | |
9f95a23c TL |
77 | 'lastUpdate': user['lastUpdate'], |
78 | 'enabled': True, | |
79 | 'pwdExpirationDate': None, | |
80 | 'pwdUpdateRequired': False | |
11fdf7f2 TL |
81 | }) |
82 | ||
83 | self._put('/api/user/user1', { | |
84 | 'name': 'My New Name', | |
85 | 'email': 'mynew@email.com', | |
86 | 'roles': ['block-manager'], | |
87 | }) | |
88 | self.assertStatus(200) | |
89 | user = self.jsonBody() | |
90 | self.assertJsonBody({ | |
91 | 'username': 'user1', | |
92 | 'name': 'My New Name', | |
93 | 'email': 'mynew@email.com', | |
94 | 'roles': ['block-manager'], | |
9f95a23c TL |
95 | 'lastUpdate': user['lastUpdate'], |
96 | 'enabled': True, | |
97 | 'pwdExpirationDate': None, | |
98 | 'pwdUpdateRequired': False | |
11fdf7f2 TL |
99 | }) |
100 | ||
101 | self._delete('/api/user/user1') | |
102 | self.assertStatus(204) | |
103 | ||
9f95a23c TL |
104 | def test_crd_disabled_user(self): |
105 | self._create_user(username='klara', | |
106 | password='mypassword10#', | |
107 | name='Klara Musterfrau', | |
108 | email='klara@musterfrau.com', | |
109 | roles=['administrator'], | |
110 | enabled=False) | |
111 | self.assertStatus(201) | |
112 | user = self.jsonBody() | |
113 | ||
114 | # Restart dashboard module. | |
115 | self._unload_module('dashboard') | |
116 | self._load_module('dashboard') | |
117 | time.sleep(10) | |
118 | ||
119 | self._get('/api/user/klara') | |
120 | self.assertStatus(200) | |
121 | self.assertJsonBody({ | |
122 | 'username': 'klara', | |
123 | 'name': 'Klara Musterfrau', | |
124 | 'email': 'klara@musterfrau.com', | |
125 | 'roles': ['administrator'], | |
126 | 'lastUpdate': user['lastUpdate'], | |
127 | 'enabled': False, | |
128 | 'pwdExpirationDate': None, | |
129 | 'pwdUpdateRequired': False | |
130 | }) | |
131 | ||
132 | self._delete('/api/user/klara') | |
133 | self.assertStatus(204) | |
134 | ||
11fdf7f2 TL |
135 | def test_list_users(self): |
136 | self._get('/api/user') | |
137 | self.assertStatus(200) | |
138 | user = self.jsonBody() | |
139 | self.assertEqual(len(user), 1) | |
140 | user = user[0] | |
141 | self.assertJsonBody([{ | |
142 | 'username': 'admin', | |
143 | 'name': None, | |
144 | 'email': None, | |
145 | 'roles': ['administrator'], | |
9f95a23c TL |
146 | 'lastUpdate': user['lastUpdate'], |
147 | 'enabled': True, | |
148 | 'pwdExpirationDate': None, | |
149 | 'pwdUpdateRequired': False | |
11fdf7f2 TL |
150 | }]) |
151 | ||
152 | def test_create_user_already_exists(self): | |
153 | self._create_user(username='admin', | |
9f95a23c | 154 | password='mypassword10#', |
11fdf7f2 TL |
155 | name='administrator', |
156 | email='my@email.com', | |
157 | roles=['administrator']) | |
158 | self.assertStatus(400) | |
159 | self.assertError(code='username_already_exists', | |
160 | component='user') | |
161 | ||
162 | def test_create_user_invalid_role(self): | |
163 | self._create_user(username='user1', | |
9f95a23c | 164 | password='mypassword10#', |
11fdf7f2 TL |
165 | name='My Name', |
166 | email='my@email.com', | |
167 | roles=['invalid-role']) | |
168 | self.assertStatus(400) | |
169 | self.assertError(code='role_does_not_exist', | |
170 | component='user') | |
171 | ||
172 | def test_delete_user_does_not_exist(self): | |
173 | self._delete('/api/user/user2') | |
174 | self.assertStatus(404) | |
175 | ||
176 | @DashboardTestCase.RunAs('test', 'test', [{'user': ['create', 'read', 'update', 'delete']}]) | |
177 | def test_delete_current_user(self): | |
178 | self._delete('/api/user/test') | |
179 | self.assertStatus(400) | |
180 | self.assertError(code='cannot_delete_current_user', | |
181 | component='user') | |
182 | ||
9f95a23c TL |
183 | @DashboardTestCase.RunAs('test', 'test', [{'user': ['create', 'read', 'update', 'delete']}]) |
184 | def test_disable_current_user(self): | |
185 | self._put('/api/user/test', {'enabled': False}) | |
186 | self.assertStatus(400) | |
187 | self.assertError(code='cannot_disable_current_user', | |
188 | component='user') | |
189 | ||
11fdf7f2 TL |
190 | def test_update_user_does_not_exist(self): |
191 | self._put('/api/user/user2', {'name': 'My New Name'}) | |
192 | self.assertStatus(404) | |
193 | ||
194 | def test_update_user_invalid_role(self): | |
195 | self._put('/api/user/admin', {'roles': ['invalid-role']}) | |
196 | self.assertStatus(400) | |
197 | self.assertError(code='role_does_not_exist', | |
198 | component='user') | |
9f95a23c TL |
199 | |
200 | def test_change_password_from_other_user(self): | |
201 | self._post('/api/user/test2/change_password', { | |
202 | 'old_password': 'abc', | |
203 | 'new_password': 'xyz' | |
204 | }) | |
205 | self.assertStatus(400) | |
206 | self.assertError(code='invalid_user_context', component='user') | |
207 | ||
208 | def test_change_password_old_not_match(self): | |
209 | self._post('/api/user/admin/change_password', { | |
210 | 'old_password': 'foo', | |
211 | 'new_password': 'bar' | |
212 | }) | |
213 | self.assertStatus(400) | |
214 | self.assertError(code='invalid_old_password', component='user') | |
215 | ||
216 | def test_change_password_as_old_password(self): | |
217 | self.create_user('test1', 'mypassword10#', ['read-only'], force_password=False) | |
218 | self.login('test1', 'mypassword10#') | |
219 | self._post('/api/user/test1/change_password', { | |
220 | 'old_password': 'mypassword10#', | |
221 | 'new_password': 'mypassword10#' | |
222 | }) | |
223 | self.assertStatus(400) | |
224 | self.assertError('password_policy_validation_failed', 'user', | |
225 | 'Password must not be the same as the previous one.') | |
226 | self._reset_login_to_admin('test1') | |
227 | ||
228 | def test_change_password_contains_username(self): | |
229 | self.create_user('test1', 'mypassword10#', ['read-only'], force_password=False) | |
230 | self.login('test1', 'mypassword10#') | |
231 | self._post('/api/user/test1/change_password', { | |
232 | 'old_password': 'mypassword10#', | |
233 | 'new_password': 'mypasstest1@#' | |
234 | }) | |
235 | self.assertStatus(400) | |
236 | self.assertError('password_policy_validation_failed', 'user', | |
237 | 'Password must not contain username.') | |
238 | self._reset_login_to_admin('test1') | |
239 | ||
240 | def test_change_password_contains_forbidden_words(self): | |
241 | self.create_user('test1', 'mypassword10#', ['read-only'], force_password=False) | |
242 | self.login('test1', 'mypassword10#') | |
243 | self._post('/api/user/test1/change_password', { | |
244 | 'old_password': 'mypassword10#', | |
245 | 'new_password': 'mypassOSD01' | |
246 | }) | |
247 | self.assertStatus(400) | |
248 | self.assertError('password_policy_validation_failed', 'user', | |
249 | 'Password must not contain the keyword "OSD".') | |
250 | self._reset_login_to_admin('test1') | |
251 | ||
252 | def test_change_password_contains_sequential_characters(self): | |
253 | self.create_user('test1', 'mypassword10#', ['read-only'], force_password=False) | |
254 | self.login('test1', 'mypassword10#') | |
255 | self._post('/api/user/test1/change_password', { | |
256 | 'old_password': 'mypassword10#', | |
257 | 'new_password': 'mypass123456!@$' | |
258 | }) | |
259 | self.assertStatus(400) | |
260 | self.assertError('password_policy_validation_failed', 'user', | |
261 | 'Password must not contain sequential characters.') | |
262 | self._reset_login_to_admin('test1') | |
263 | ||
264 | def test_change_password_contains_repetetive_characters(self): | |
265 | self.create_user('test1', 'mypassword10#', ['read-only'], force_password=False) | |
266 | self.login('test1', 'mypassword10#') | |
267 | self._post('/api/user/test1/change_password', { | |
268 | 'old_password': 'mypassword10#', | |
269 | 'new_password': 'aaaaA1@!#' | |
270 | }) | |
271 | self.assertStatus(400) | |
272 | self.assertError('password_policy_validation_failed', 'user', | |
273 | 'Password must not contain repetitive characters.') | |
274 | self._reset_login_to_admin('test1') | |
275 | ||
276 | @DashboardTestCase.RunAs('test1', 'mypassword10#', ['read-only'], False) | |
277 | def test_change_password(self): | |
278 | self._post('/api/user/test1/change_password', { | |
279 | 'old_password': 'mypassword10#', | |
280 | 'new_password': 'newpassword01#' | |
281 | }) | |
282 | self.assertStatus(200) | |
283 | self.logout() | |
284 | self._post('/api/auth', {'username': 'test1', 'password': 'mypassword10#'}) | |
285 | self.assertStatus(400) | |
286 | self.assertError(code='invalid_credentials', component='auth') | |
287 | ||
288 | def test_create_user_password_cli(self): | |
289 | exitcode = self._ceph_cmd_result(['dashboard', 'ac-user-create', | |
290 | 'test1', 'mypassword10#']) | |
291 | self.assertEqual(exitcode, 0) | |
292 | self.delete_user('test1') | |
293 | ||
294 | @DashboardTestCase.RunAs('test2', 'foo_bar_10#', force_password=False, login=False) | |
295 | def test_change_user_password_cli(self): | |
296 | exitcode = self._ceph_cmd_result(['dashboard', 'ac-user-set-password', | |
297 | 'test2', 'foo_new-password01#']) | |
298 | self.assertEqual(exitcode, 0) | |
299 | ||
300 | def test_create_user_password_force_cli(self): | |
301 | exitcode = self._ceph_cmd_result(['dashboard', 'ac-user-create', | |
302 | '--force-password', 'test11', | |
303 | 'bar']) | |
304 | self.assertEqual(exitcode, 0) | |
305 | self.delete_user('test11') | |
306 | ||
307 | @DashboardTestCase.RunAs('test22', 'foo_bar_10#', force_password=False, login=False) | |
308 | def test_change_user_password_force_cli(self): | |
309 | exitcode = self._ceph_cmd_result(['dashboard', 'ac-user-set-password', | |
310 | '--force-password', 'test22', | |
311 | 'bar']) | |
312 | self.assertEqual(exitcode, 0) | |
313 | ||
314 | def test_create_user_password_cli_fail(self): | |
315 | exitcode = self._ceph_cmd_result(['dashboard', 'ac-user-create', 'test3', 'foo']) | |
316 | self.assertNotEqual(exitcode, 0) | |
317 | ||
318 | @DashboardTestCase.RunAs('test4', 'x1z_tst+_10#', force_password=False, login=False) | |
319 | def test_change_user_password_cli_fail(self): | |
320 | exitcode = self._ceph_cmd_result(['dashboard', 'ac-user-set-password', | |
321 | 'test4', 'bar']) | |
322 | self.assertNotEqual(exitcode, 0) | |
323 | ||
324 | def test_create_user_with_pwd_expiration_date(self): | |
325 | future_date = datetime.utcnow() + timedelta(days=10) | |
326 | future_date = int(time.mktime(future_date.timetuple())) | |
327 | ||
328 | self._create_user(username='user1', | |
329 | password='mypassword10#', | |
330 | name='My Name', | |
331 | email='my@email.com', | |
332 | roles=['administrator'], | |
333 | pwd_expiration_date=future_date) | |
334 | self.assertStatus(201) | |
335 | user = self.jsonBody() | |
336 | ||
337 | self._get('/api/user/user1') | |
338 | self.assertStatus(200) | |
339 | self.assertJsonBody({ | |
340 | 'username': 'user1', | |
341 | 'name': 'My Name', | |
342 | 'email': 'my@email.com', | |
343 | 'roles': ['administrator'], | |
344 | 'lastUpdate': user['lastUpdate'], | |
345 | 'enabled': True, | |
346 | 'pwdExpirationDate': future_date, | |
347 | 'pwdUpdateRequired': False | |
348 | }) | |
349 | self._delete('/api/user/user1') | |
350 | ||
351 | def test_create_with_pwd_expiration_date_not_valid(self): | |
352 | past_date = datetime.utcnow() - timedelta(days=10) | |
353 | past_date = int(time.mktime(past_date.timetuple())) | |
354 | ||
355 | self._create_user(username='user1', | |
356 | password='mypassword10#', | |
357 | name='My Name', | |
358 | email='my@email.com', | |
359 | roles=['administrator'], | |
360 | pwd_expiration_date=past_date) | |
361 | self.assertStatus(400) | |
362 | self.assertError(code='pwd_past_expiration_date', component='user') | |
363 | ||
364 | def test_create_with_default_expiration_date(self): | |
365 | future_date_1 = datetime.utcnow() + timedelta(days=9) | |
366 | future_date_1 = int(time.mktime(future_date_1.timetuple())) | |
367 | future_date_2 = datetime.utcnow() + timedelta(days=11) | |
368 | future_date_2 = int(time.mktime(future_date_2.timetuple())) | |
369 | ||
370 | self._ceph_cmd(['dashboard', 'set-user-pwd-expiration-span', '10']) | |
371 | self._create_user(username='user1', | |
372 | password='mypassword10#', | |
373 | name='My Name', | |
374 | email='my@email.com', | |
375 | roles=['administrator']) | |
376 | self.assertStatus(201) | |
377 | ||
378 | user = self._get('/api/user/user1') | |
379 | self.assertStatus(200) | |
380 | self.assertIsNotNone(user['pwdExpirationDate']) | |
381 | self.assertGreater(user['pwdExpirationDate'], future_date_1) | |
382 | self.assertLess(user['pwdExpirationDate'], future_date_2) | |
383 | ||
384 | self._delete('/api/user/user1') | |
385 | self._ceph_cmd(['dashboard', 'set-user-pwd-expiration-span', '0']) | |
386 | ||
387 | def test_pwd_expiration_date_update(self): | |
388 | self._ceph_cmd(['dashboard', 'set-user-pwd-expiration-span', '10']) | |
389 | self.create_user('user1', 'mypassword10#', ['administrator']) | |
390 | ||
391 | user_1 = self._get('/api/user/user1') | |
392 | self.assertStatus(200) | |
393 | ||
394 | self.login('user1', 'mypassword10#') | |
395 | self._post('/api/user/user1/change_password', { | |
396 | 'old_password': 'mypassword10#', | |
397 | 'new_password': 'newpassword01#' | |
398 | }) | |
399 | self.assertStatus(200) | |
400 | ||
401 | # Compare password expiration dates. | |
402 | self._reset_login_to_admin() | |
403 | user_1_pwd_changed = self._get('/api/user/user1') | |
404 | self.assertStatus(200) | |
405 | self.assertLess(user_1['pwdExpirationDate'], user_1_pwd_changed['pwdExpirationDate']) | |
406 | ||
407 | # Cleanup | |
408 | self.delete_user('user1') | |
409 | self._ceph_cmd(['dashboard', 'set-user-pwd-expiration-span', '0']) | |
410 | ||
411 | def test_pwd_update_required(self): | |
412 | self._create_user(username='user1', | |
413 | password='mypassword10#', | |
414 | name='My Name', | |
415 | email='my@email.com', | |
416 | roles=['administrator'], | |
417 | pwd_update_required=True) | |
418 | self.assertStatus(201) | |
419 | ||
420 | user_1 = self._get('/api/user/user1') | |
421 | self.assertStatus(200) | |
422 | self.assertEqual(user_1['pwdUpdateRequired'], True) | |
423 | ||
424 | self.login('user1', 'mypassword10#') | |
425 | self.assertStatus(201) | |
426 | ||
427 | self._get('/api/osd') | |
428 | self.assertStatus(403) | |
429 | self._reset_login_to_admin('user1') | |
430 | ||
431 | def test_pwd_update_required_change_pwd(self): | |
432 | self._create_user(username='user1', | |
433 | password='mypassword10#', | |
434 | name='My Name', | |
435 | email='my@email.com', | |
436 | roles=['administrator'], | |
437 | pwd_update_required=True) | |
438 | self.assertStatus(201) | |
439 | ||
440 | self.login('user1', 'mypassword10#') | |
441 | self._post('/api/user/user1/change_password', { | |
442 | 'old_password': 'mypassword10#', | |
443 | 'new_password': 'newpassword01#' | |
444 | }) | |
445 | ||
446 | self.login('user1', 'newpassword01#') | |
447 | user_1 = self._get('/api/user/user1') | |
448 | self.assertStatus(200) | |
449 | self.assertEqual(user_1['pwdUpdateRequired'], False) | |
450 | self._get('/api/osd') | |
451 | self.assertStatus(200) | |
452 | self._reset_login_to_admin('user1') | |
453 | ||
454 | def test_validate_password_weak(self): | |
455 | self._post('/api/user/validate_password', { | |
456 | 'password': 'mypassword1' | |
457 | }) | |
458 | self.assertStatus(200) | |
459 | self.assertJsonBody({ | |
460 | 'valid': True, | |
461 | 'credits': 11, | |
462 | 'valuation': 'Weak' | |
463 | }) | |
464 | ||
465 | def test_validate_password_ok(self): | |
466 | self._post('/api/user/validate_password', { | |
467 | 'password': 'mypassword1!@' | |
468 | }) | |
469 | self.assertStatus(200) | |
470 | self.assertJsonBody({ | |
471 | 'valid': True, | |
472 | 'credits': 17, | |
473 | 'valuation': 'OK' | |
474 | }) | |
475 | ||
476 | def test_validate_password_strong(self): | |
477 | self._post('/api/user/validate_password', { | |
478 | 'password': 'testpassword0047!@' | |
479 | }) | |
480 | self.assertStatus(200) | |
481 | self.assertJsonBody({ | |
482 | 'valid': True, | |
483 | 'credits': 22, | |
484 | 'valuation': 'Strong' | |
485 | }) | |
486 | ||
487 | def test_validate_password_very_strong(self): | |
488 | self._post('/api/user/validate_password', { | |
489 | 'password': 'testpassword#!$!@$' | |
490 | }) | |
491 | self.assertStatus(200) | |
492 | self.assertJsonBody({ | |
493 | 'valid': True, | |
494 | 'credits': 30, | |
495 | 'valuation': 'Very strong' | |
496 | }) | |
497 | ||
498 | def test_validate_password_fail(self): | |
499 | self._post('/api/user/validate_password', { | |
500 | 'password': 'foo' | |
501 | }) | |
502 | self.assertStatus(200) | |
503 | self.assertJsonBody({ | |
504 | 'valid': False, | |
505 | 'credits': 0, | |
506 | 'valuation': 'Password is too weak.' | |
507 | }) | |
508 | ||
509 | def test_validate_password_fail_name(self): | |
510 | self._post('/api/user/validate_password', { | |
511 | 'password': 'x1zhugo_10', | |
512 | 'username': 'hugo' | |
513 | }) | |
514 | self.assertStatus(200) | |
515 | self.assertJsonBody({ | |
516 | 'valid': False, | |
517 | 'credits': 0, | |
518 | 'valuation': 'Password must not contain username.' | |
519 | }) | |
520 | ||
521 | def test_validate_password_fail_oldpwd(self): | |
522 | self._post('/api/user/validate_password', { | |
523 | 'password': 'x1zt-st10', | |
524 | 'old_password': 'x1zt-st10' | |
525 | }) | |
526 | self.assertStatus(200) | |
527 | self.assertJsonBody({ | |
528 | 'valid': False, | |
529 | 'credits': 0, | |
530 | 'valuation': 'Password must not be the same as the previous one.' | |
531 | }) | |
532 | ||
533 | def test_create_user_pwd_update_required(self): | |
534 | self.create_user('foo', 'bar', cmd_args=['--pwd_update_required']) | |
535 | self._get('/api/user/foo') | |
536 | self.assertStatus(200) | |
537 | self.assertJsonSubset({ | |
538 | 'username': 'foo', | |
539 | 'pwdUpdateRequired': True | |
540 | }) | |
541 | self.delete_user('foo') |