1# -*- coding: utf-8 -*- 2# 3# Copyright (C) 2004-2021 Edgewall Software 4# All rights reserved. 5# 6# This software is licensed as described in the file COPYING, which 7# you should have received as part of this distribution. The terms 8# are also available at https://trac.edgewall.org/wiki/TracLicense. 9# 10# This software consists of voluntary contributions made by many 11# individuals. For the exact contribution history, see the revision 12# history and logs, available at https://trac.edgewall.org/log/. 13 14import unittest 15 16from trac import perm 17from trac.admin.console import TracAdmin 18from trac.admin.test import TracAdminTestCaseBase 19from trac.core import Component, ComponentMeta, TracError, implements 20from trac.resource import Resource 21from trac.test import EnvironmentStub 22 23# IPermissionRequestor implementations 24import trac.about 25import trac.admin.web_ui 26import trac.perm 27import trac.search.web_ui 28import trac.ticket.api 29import trac.ticket.batch 30import trac.ticket.report 31import trac.ticket.roadmap 32import trac.timeline.web_ui 33import trac.versioncontrol.admin 34import trac.versioncontrol.web_ui.browser 35import trac.versioncontrol.web_ui.changeset 36import trac.versioncontrol.web_ui.log 37import trac.web.chrome 38import trac.wiki.web_ui 39 40 41class DefaultPermissionStoreTestCase(unittest.TestCase): 42 43 def setUp(self): 44 self.env = \ 45 EnvironmentStub(enable=[perm.DefaultPermissionStore, 46 perm.DefaultPermissionGroupProvider]) 47 self.store = perm.DefaultPermissionStore(self.env) 48 49 def tearDown(self): 50 self.env.reset_db() 51 52 def test_simple_actions(self): 53 self.env.db_transaction.executemany( 54 "INSERT INTO permission VALUES (%s,%s)", 55 [('john', 'WIKI_MODIFY'), 56 ('john', 'REPORT_ADMIN'), 57 ('kate', 'TICKET_CREATE')]) 58 self.assertEqual(['REPORT_ADMIN', 'WIKI_MODIFY'], 59 self.store.get_user_permissions('john')) 60 self.assertEqual(['TICKET_CREATE'], 61 self.store.get_user_permissions('kate')) 62 63 def test_simple_group(self): 64 self.env.db_transaction.executemany( 65 "INSERT INTO permission VALUES (%s,%s)", 66 [('dev', 'WIKI_MODIFY'), 67 ('dev', 'REPORT_ADMIN'), 68 ('john', 'dev')]) 69 self.assertEqual(['REPORT_ADMIN', 'WIKI_MODIFY'], 70 self.store.get_user_permissions('john')) 71 72 def test_nested_groups(self): 73 self.env.db_transaction.executemany( 74 "INSERT INTO permission VALUES (%s,%s)", 75 [('dev', 'WIKI_MODIFY'), 76 ('dev', 'REPORT_ADMIN'), 77 ('admin', 'dev'), 78 ('john', 'admin')]) 79 self.assertEqual(['REPORT_ADMIN', 'WIKI_MODIFY'], 80 self.store.get_user_permissions('john')) 81 82 def test_mixed_case_group(self): 83 self.env.db_transaction.executemany( 84 "INSERT INTO permission VALUES (%s,%s)", 85 [('Dev', 'WIKI_MODIFY'), 86 ('Dev', 'REPORT_ADMIN'), 87 ('Admin', 'Dev'), 88 ('john', 'Admin')]) 89 self.assertEqual(['REPORT_ADMIN', 'WIKI_MODIFY'], 90 self.store.get_user_permissions('john')) 91 92 def test_builtin_groups(self): 93 self.env.db_transaction.executemany( 94 "INSERT INTO permission VALUES (%s,%s)", 95 [('authenticated', 'WIKI_MODIFY'), 96 ('authenticated', 'REPORT_ADMIN'), 97 ('anonymous', 'TICKET_CREATE')]) 98 self.assertEqual(['REPORT_ADMIN', 'TICKET_CREATE', 'WIKI_MODIFY'], 99 self.store.get_user_permissions('john')) 100 self.assertEqual(['TICKET_CREATE'], 101 self.store.get_user_permissions('anonymous')) 102 103 def test_get_all_permissions(self): 104 self.env.db_transaction.executemany( 105 "INSERT INTO permission VALUES (%s,%s)", 106 [('dev', 'WIKI_MODIFY'), 107 ('dev', 'REPORT_ADMIN'), 108 ('john', 'dev')]) 109 expected = [('dev', 'WIKI_MODIFY'), 110 ('dev', 'REPORT_ADMIN'), 111 ('john', 'dev')] 112 for res in self.store.get_all_permissions(): 113 self.assertIn(res, expected) 114 115 def test_get_permission_groups(self): 116 self.env.db_transaction.executemany( 117 "INSERT INTO permission VALUES (%s,%s)", 118 [('user1', 'group1'), 119 ('group1', 'group2'), 120 ('group2', 'group3'), 121 ('user2', 'group4'), 122 ('user1', 'group5'), 123 ('group6', 'group7'), 124 ('user3', 'group8'), # test recursion 125 ('group8', 'group9'), 126 ('group9', 'group8'), 127 ('user3', 'group11'), 128 ('group11', 'group10'), # test recursion 129 ('group10', 'group11'), 130 ('group10', 'group10')]) 131 self.assertEqual(['group1', 'group2', 'group3', 'group5'], 132 self.store.get_permission_groups('user1')) 133 self.assertEqual(['group4'], 134 self.store.get_permission_groups('user2')) 135 self.assertEqual(['group10', 'group11', 'group8', 'group9'], 136 self.store.get_permission_groups('user3')) 137 138 139class BaseTestCase(unittest.TestCase): 140 141 permission_requestors = [] 142 143 @classmethod 144 def setUpClass(cls): 145 class TestPermissionRequestor(Component): 146 implements(perm.IPermissionRequestor) 147 148 def get_permission_actions(self): 149 return ['TEST_CREATE', 'TEST_DELETE', 'TEST_MODIFY', 150 ('TEST_CREATE', []), 151 ('TEST_ADMIN', ['TEST_CREATE', 'TEST_DELETE']), 152 ('TEST_ADMIN', ['TEST_MODIFY'])] 153 154 cls.permission_requestors = [TestPermissionRequestor] 155 156 @classmethod 157 def tearDownClass(cls): 158 for component in cls.permission_requestors: 159 ComponentMeta.deregister(component) 160 161 162class PermissionErrorTestCase(unittest.TestCase): 163 164 def setUp(self): 165 self.env = EnvironmentStub() 166 167 def test_default_message(self): 168 permission_error = perm.PermissionError() 169 self.assertIsNone(permission_error.action) 170 self.assertIsNone(permission_error.resource) 171 self.assertIsNone(permission_error.env) 172 self.assertEqual("Insufficient privileges to perform this operation.", 173 str(permission_error)) 174 self.assertEqual("Forbidden", permission_error.title) 175 self.assertEqual(str(permission_error), permission_error.message) 176 177 def test_message_specified(self): 178 message = "The message." 179 permission_error = perm.PermissionError(msg=message) 180 self.assertEqual(message, str(permission_error)) 181 182 def test_message_from_action(self): 183 action = 'WIKI_VIEW' 184 permission_error = perm.PermissionError(action) 185 self.assertEqual(action, permission_error.action) 186 self.assertIsNone(permission_error.resource) 187 self.assertIsNone(permission_error.env) 188 self.assertEqual("WIKI_VIEW privileges are required to perform this " 189 "operation. You don't have the required " 190 "permissions.", str(permission_error)) 191 192 def test_message_from_action_and_resource(self): 193 action = 'WIKI_VIEW' 194 resource = Resource('wiki', 'WikiStart') 195 permission_error = perm.PermissionError(action, resource, self.env) 196 self.assertEqual(action, permission_error.action) 197 self.assertEqual(resource, permission_error.resource) 198 self.assertEqual(self.env, permission_error.env) 199 self.assertEqual("WIKI_VIEW privileges are required to perform this " 200 "operation on WikiStart. You don't have the " 201 "required permissions.", str(permission_error)) 202 203 def test_message_from_action_and_resource_without_id(self): 204 action = 'TIMELINE_VIEW' 205 resource = Resource('timeline') 206 permission_error = perm.PermissionError(action, resource, self.env) 207 self.assertEqual(action, permission_error.action) 208 self.assertEqual(resource, permission_error.resource) 209 self.assertEqual(self.env, permission_error.env) 210 self.assertEqual("TIMELINE_VIEW privileges are required to perform " 211 "this operation. You don't have the required " 212 "permissions.", str(permission_error)) 213 214 215class PermissionSystemTestCase(BaseTestCase): 216 217 def setUp(self): 218 self.env = EnvironmentStub(enable=[perm.PermissionSystem, 219 perm.DefaultPermissionGroupProvider, 220 perm.DefaultPermissionStore] + 221 self.permission_requestors) 222 self.perm = perm.PermissionSystem(self.env) 223 224 def tearDown(self): 225 self.env.reset_db() 226 227 def test_get_actions(self): 228 tpr_perms = ['TEST_ADMIN', 'TEST_CREATE', 'TEST_DELETE', 'TEST_MODIFY'] 229 all_perms = tpr_perms + ['TRAC_ADMIN'] 230 self.assertEqual(all_perms, self.perm.get_actions()) 231 self.assertEqual(tpr_perms, 232 self.perm.get_actions(skip=self.perm)) 233 234 def test_actions(self): 235 self.assertEqual(self.perm.get_actions(), self.perm.actions) 236 237 def test_actions_is_lazy(self): 238 actions = self.perm.actions 239 self.assertEqual(id(actions), id(self.perm.actions)) 240 241 def test_get_actions_dict(self): 242 self.assertEqual({ 243 'TEST_ADMIN': ['TEST_CREATE', 'TEST_DELETE', 'TEST_MODIFY'], 244 'TEST_CREATE': [], 245 'TEST_DELETE': [], 246 'TEST_MODIFY': [], 247 'TRAC_ADMIN': ['TEST_ADMIN', 'TEST_CREATE', 'TEST_DELETE', 248 'TEST_MODIFY'], 249 }, self.perm.get_actions_dict()) 250 self.assertEqual({ 251 'TEST_ADMIN': ['TEST_CREATE', 'TEST_DELETE', 'TEST_MODIFY'], 252 'TEST_CREATE': [], 253 'TEST_DELETE': [], 254 'TEST_MODIFY': [], 255 }, self.perm.get_actions_dict(skip=self.perm)) 256 257 def test_all_permissions(self): 258 self.assertEqual({'TRAC_ADMIN': True, 'TEST_CREATE': True, 259 'TEST_DELETE': True, 'TEST_MODIFY': True, 260 'TEST_ADMIN': True}, 261 self.perm.get_user_permissions()) 262 263 def test_simple_permissions(self): 264 self.perm.grant_permission('bob', 'TEST_CREATE') 265 self.perm.grant_permission('jane', 'TEST_DELETE') 266 self.perm.grant_permission('jane', 'TEST_MODIFY') 267 self.assertEqual({'TEST_CREATE': True}, 268 self.perm.get_user_permissions('bob')) 269 self.assertEqual({'TEST_DELETE': True, 'TEST_MODIFY': True}, 270 self.perm.get_user_permissions('jane')) 271 272 def test_meta_permissions(self): 273 self.perm.grant_permission('bob', 'TEST_CREATE') 274 self.perm.grant_permission('jane', 'TEST_ADMIN') 275 self.assertEqual({'TEST_CREATE': True}, 276 self.perm.get_user_permissions('bob')) 277 self.assertEqual({'TEST_CREATE': True, 'TEST_DELETE': True, 278 'TEST_MODIFY': True, 'TEST_ADMIN': True}, 279 self.perm.get_user_permissions('jane')) 280 281 def test_undefined_permissions(self): 282 """Only defined actions are returned in the dictionary.""" 283 self.perm.grant_permission('bob', 'TEST_CREATE') 284 self.perm.grant_permission('jane', 'TEST_DELETE') 285 self.perm.grant_permission('jane', 'TEST_MODIFY') 286 287 self.env.disable_component(self.permission_requestors[0]) 288 289 self.assertEqual({}, self.perm.get_user_permissions('bob')) 290 self.assertEqual({}, self.perm.get_user_permissions('jane')) 291 292 def test_grant_permission_differs_from_action_by_casing(self): 293 """`TracError` is raised when granting a permission that differs 294 from an action by casing. 295 """ 296 self.assertRaises(TracError, self.perm.grant_permission, 'user1', 297 'Test_Create') 298 299 def test_grant_permission_already_granted(self): 300 """`PermissionExistsError` is raised when granting a permission 301 that has already been granted. 302 """ 303 self.perm.grant_permission('user1', 'TEST_CREATE') 304 self.assertRaises(perm.PermissionExistsError, 305 self.perm.grant_permission, 'user1', 'TEST_CREATE') 306 307 def test_grant_permission_already_in_group(self): 308 """`PermissionExistsError` is raised when adding a user to 309 a group of which they are already a member. 310 """ 311 self.perm.grant_permission('user1', 'group1') 312 self.assertRaises(perm.PermissionExistsError, 313 self.perm.grant_permission, 'user1', 'group1') 314 315 def test_get_all_permissions(self): 316 self.perm.grant_permission('bob', 'TEST_CREATE') 317 self.perm.grant_permission('jane', 'TEST_ADMIN') 318 expected = [('bob', 'TEST_CREATE'), 319 ('jane', 'TEST_ADMIN')] 320 for res in self.perm.get_all_permissions(): 321 self.assertIn(res, expected) 322 323 def test_get_groups_dict(self): 324 permissions = [ 325 ('user2', 'group1'), 326 ('user1', 'group1'), 327 ('user3', 'group1'), 328 ('user3', 'group2') 329 ] 330 for perm_ in permissions: 331 self.perm.grant_permission(*perm_) 332 333 groups = self.perm.get_groups_dict() 334 self.assertEqual(2, len(groups)) 335 self.assertEqual(['user1', 'user2', 'user3'], groups['group1']) 336 self.assertEqual(['user3'], groups['group2']) 337 338 def test_get_users_dict(self): 339 permissions = [ 340 ('user2', 'TEST_CREATE'), 341 ('user1', 'TEST_DELETE'), 342 ('user1', 'TEST_ADMIN'), 343 ('user1', 'TEST_CREATE') 344 ] 345 for perm_ in permissions: 346 self.perm.grant_permission(*perm_) 347 348 users = self.perm.get_users_dict() 349 self.assertEqual(2, len(users)) 350 self.assertEqual(['TEST_ADMIN', 'TEST_CREATE', 'TEST_DELETE'], 351 users['user1']) 352 self.assertEqual(['TEST_CREATE'], users['user2']) 353 354 def test_get_permission_groups(self): 355 permissions = [ 356 ('user1', 'group1'), 357 ('group1', 'group2'), 358 ('group2', 'group3'), 359 ('user2', 'group4'), 360 ('user1', 'group5'), 361 ('group6', 'group7'), 362 ('user3', 'group8'), # test recursion 363 ('group8', 'group9'), 364 ('group9', 'group8'), 365 ('user3', 'group11'), 366 ('group11', 'group10'), # test recursion 367 ('group10', 'group11'), 368 ('group10', 'group10'), 369 ] 370 for perm_ in permissions: 371 self.perm.grant_permission(*perm_) 372 373 self.assertEqual(['anonymous', 'authenticated', 'group1', 'group2', 374 'group3', 'group5'], 375 self.perm.get_permission_groups('user1')) 376 self.assertEqual(['anonymous', 'authenticated', 'group4'], 377 self.perm.get_permission_groups('user2')) 378 self.assertEqual(['anonymous', 'authenticated', 'group10', 'group11', 379 'group8', 'group9'], 380 self.perm.get_permission_groups('user3')) 381 382 def test_expand_actions_iter_7467(self): 383 # Check that expand_actions works with iterators (#7467) 384 perms = ['TEST_ADMIN', 'TEST_CREATE', 'TEST_DELETE', 'TEST_MODIFY', 385 'TRAC_ADMIN'] 386 self.assertEqual(perms, self.perm.expand_actions(['TRAC_ADMIN'])) 387 self.assertEqual(perms, self.perm.expand_actions(iter(['TRAC_ADMIN']))) 388 389 390class PermissionCacheTestCase(BaseTestCase): 391 392 def setUp(self): 393 self.env = EnvironmentStub(enable=[perm.DefaultPermissionStore, 394 perm.DefaultPermissionPolicy] + 395 self.permission_requestors) 396 self.env.config.set('trac', 'permission_policies', 397 'DefaultPermissionPolicy') 398 self.perm_system = perm.PermissionSystem(self.env) 399 # by-pass DefaultPermissionPolicy cache: 400 perm.DefaultPermissionPolicy.CACHE_EXPIRY = -1 401 self.perm_system.grant_permission('testuser', 'TEST_MODIFY') 402 self.perm_system.grant_permission('testuser', 'TEST_ADMIN') 403 self.perm = perm.PermissionCache(self.env, 'testuser') 404 405 def tearDown(self): 406 self.env.reset_db() 407 408 def test_contains(self): 409 self.assertIn('TEST_MODIFY', self.perm) 410 self.assertIn('TEST_ADMIN', self.perm) 411 self.assertNotIn('TRAC_ADMIN', self.perm) 412 413 def test_has_permission(self): 414 self.assertTrue(self.perm.has_permission('TEST_MODIFY')) 415 self.assertTrue(self.perm.has_permission('TEST_ADMIN')) 416 self.assertFalse(self.perm.has_permission('TRAC_ADMIN')) 417 418 def test_require(self): 419 self.perm.require('TEST_MODIFY') 420 self.perm.require('TEST_ADMIN') 421 with self.assertRaises(perm.PermissionError): 422 self.perm.require('TRAC_ADMIN') 423 424 def test_assert_permission(self): 425 self.perm.assert_permission('TEST_MODIFY') 426 self.perm.assert_permission('TEST_ADMIN') 427 with self.assertRaises(perm.PermissionError): 428 self.perm.assert_permission('TRAC_ADMIN') 429 430 def test_cache(self): 431 self.perm.require('TEST_MODIFY') 432 self.perm.require('TEST_ADMIN') 433 self.perm_system.revoke_permission('testuser', 'TEST_ADMIN') 434 # Using cached GRANT here 435 self.perm.require('TEST_ADMIN') 436 437 def test_cache_shared(self): 438 # we need to start with an empty cache here (#7201) 439 perm1 = perm.PermissionCache(self.env, 'testcache') 440 perm1 = perm1('ticket', 1) 441 perm2 = perm1('ticket', 1) # share internal cache 442 self.perm_system.grant_permission('testcache', 'TEST_ADMIN') 443 perm1.require('TEST_ADMIN') 444 self.perm_system.revoke_permission('testcache', 'TEST_ADMIN') 445 # Using cached GRANT here (from shared cache) 446 perm2.require('TEST_ADMIN') 447 448 def test_has_permission_on_resource_none(self): 449 """'PERM' in perm(None) should cache the same value as 450 'PERM' in perm(None) (#12597). 451 """ 452 'TEST_ADMIN' in self.perm 453 self.assertEqual(1, len(self.perm._cache)) 454 'TEST_ADMIN' in self.perm(None) 455 self.assertEqual(1, len(self.perm._cache)) 456 457 458class TestPermissionPolicy(Component): 459 implements(perm.IPermissionPolicy) 460 461 def __init__(self): 462 self.allowed = {} 463 self.results = {} 464 465 def grant(self, username, permissions): 466 self.allowed.setdefault(username, set()).update(permissions) 467 468 def revoke(self, username, permissions): 469 self.allowed.setdefault(username, set()).difference_update(permissions) 470 471 def check_permission(self, action, username, resource, perm): 472 result = action in self.allowed.get(username, set()) or None 473 self.results[(username, action)] = result 474 return result 475 476 477class PermissionPolicyTestCase(BaseTestCase): 478 479 def setUp(self): 480 self.env = EnvironmentStub(enable=[perm.DefaultPermissionStore, 481 perm.DefaultPermissionPolicy, 482 TestPermissionPolicy] + 483 self.permission_requestors) 484 self.env.config.set('trac', 'permission_policies', 485 'TestPermissionPolicy') 486 self.policy = TestPermissionPolicy(self.env) 487 self.perm = perm.PermissionCache(self.env, 'testuser') 488 489 def tearDown(self): 490 self.env.reset_db() 491 492 def test_no_permissions(self): 493 self.assertRaises(perm.PermissionError, 494 self.perm.require, 'TEST_MODIFY') 495 self.assertRaises(perm.PermissionError, 496 self.perm.require, 'TEST_ADMIN') 497 self.assertEqual(self.policy.results, 498 {('testuser', 'TEST_MODIFY'): None, 499 ('testuser', 'TEST_ADMIN'): None}) 500 501 def test_grant_revoke_permissions(self): 502 self.policy.grant('testuser', ['TEST_MODIFY', 'TEST_ADMIN']) 503 self.assertIn('TEST_MODIFY', self.perm) 504 self.assertIn('TEST_ADMIN', self.perm) 505 self.assertEqual(self.policy.results, 506 {('testuser', 'TEST_MODIFY'): True, 507 ('testuser', 'TEST_ADMIN'): True}) 508 509 def test_policy_chaining(self): 510 self.env.config.set('trac', 'permission_policies', 511 'TestPermissionPolicy,DefaultPermissionPolicy') 512 self.policy.grant('testuser', ['TEST_MODIFY']) 513 system = perm.PermissionSystem(self.env) 514 system.grant_permission('testuser', 'TEST_ADMIN') 515 516 self.assertEqual(list(system.policies), 517 [self.policy, 518 perm.DefaultPermissionPolicy(self.env)]) 519 self.assertIn('TEST_MODIFY', self.perm) 520 self.assertIn('TEST_ADMIN', self.perm) 521 self.assertEqual(self.policy.results, 522 {('testuser', 'TEST_MODIFY'): True, 523 ('testuser', 'TEST_ADMIN'): None}) 524 525 526class RecursivePolicyTestCase(unittest.TestCase): 527 """Test case for policies that perform recursive permission checks.""" 528 529 permission_policies = [] 530 decisions = [] 531 532 @classmethod 533 def setUpClass(cls): 534 535 class PermissionPolicy1(Component): 536 537 implements(perm.IPermissionPolicy) 538 539 def __init__(self): 540 self.call_count = 0 541 self.decisions = cls.decisions 542 543 def check_permission(self, action, username, resource, perm): 544 self.call_count += 1 545 decision = None 546 if 'ACTION_2' in perm(resource): 547 decision = None 548 elif action == 'ACTION_1': 549 decision = username == 'user1' 550 self.decisions.append(('policy1', action, decision)) 551 return decision 552 553 class PermissionPolicy2(Component): 554 555 implements(perm.IPermissionPolicy) 556 557 def __init__(self): 558 self.call_count = 0 559 self.decisions = cls.decisions 560 561 def check_permission(self, action, username, resource, perm): 562 self.call_count += 1 563 decision = None 564 if action == 'ACTION_2': 565 decision = username == 'user2' 566 self.decisions.append(('policy2', action, decision)) 567 return decision 568 569 cls.permission_policies = [PermissionPolicy1, PermissionPolicy2] 570 571 @classmethod 572 def tearDownClass(cls): 573 from trac.core import ComponentMeta 574 for component in cls.permission_policies: 575 ComponentMeta.deregister(component) 576 577 def setUp(self): 578 self.__class__.decisions = [] 579 self.env = EnvironmentStub(enable=self.permission_policies) 580 self.env.config.set('trac', 'permission_policies', 581 'PermissionPolicy1, PermissionPolicy2') 582 self.ps = perm.PermissionSystem(self.env) 583 584 def tearDown(self): 585 self.env.reset_db() 586 587 def test_user1_allowed_by_policy1(self): 588 """policy1 consulted for ACTION_1. policy1 and policy2 consulted 589 for ACTION_2. 590 """ 591 perm_cache = perm.PermissionCache(self.env, 'user1') 592 self.assertIn('ACTION_1', perm_cache) 593 self.assertEqual(2, self.ps.policies[0].call_count) 594 self.assertEqual(1, self.ps.policies[1].call_count) 595 self.assertEqual([ 596 ('policy1', 'ACTION_2', None), 597 ('policy2', 'ACTION_2', False), 598 ('policy1', 'ACTION_1', True), 599 ], self.decisions) 600 601 def test_user2_denied_by_no_decision(self): 602 """policy1 and policy2 consulted for ACTION_1. policy1 and 603 policy2 consulted for ACTION_2. 604 """ 605 perm_cache = perm.PermissionCache(self.env, 'user2') 606 self.assertNotIn('ACTION_1', perm_cache) 607 self.assertEqual(2, self.ps.policies[0].call_count) 608 self.assertEqual(2, self.ps.policies[1].call_count) 609 self.assertEqual([ 610 ('policy1', 'ACTION_2', None), 611 ('policy2', 'ACTION_2', True), 612 ('policy1', 'ACTION_1', None), 613 ('policy2', 'ACTION_1', None), 614 ], self.decisions) 615 616 def test_user1_denied_by_policy2(self): 617 """policy1 consulted for ACTION_2. policy2 consulted for ACTION_2. 618 """ 619 perm_cache = perm.PermissionCache(self.env, 'user1') 620 self.assertNotIn('ACTION_2', perm_cache) 621 self.assertEqual(1, self.ps.policies[0].call_count) 622 self.assertEqual(1, self.ps.policies[1].call_count) 623 self.assertEqual([ 624 ('policy1', 'ACTION_2', None), 625 ('policy2', 'ACTION_2', False), 626 ], self.decisions) 627 628 def test_user1_allowed_by_policy2(self): 629 """policy1 consulted for ACTION_2. policy2 consulted for ACTION_2. 630 """ 631 perm_cache = perm.PermissionCache(self.env, 'user2') 632 self.assertIn('ACTION_2', perm_cache) 633 self.assertEqual(1, self.ps.policies[0].call_count) 634 self.assertEqual(1, self.ps.policies[1].call_count) 635 self.assertEqual([ 636 ('policy1', 'ACTION_2', None), 637 ('policy2', 'ACTION_2', True), 638 ], self.decisions) 639 640 641class TracAdminTestCase(TracAdminTestCaseBase): 642 643 def setUp(self): 644 self.env = EnvironmentStub(default_data=True) 645 self.admin = TracAdmin() 646 self.admin.env_set('', self.env) 647 648 def tearDown(self): 649 self.env.reset_db() 650 self.env = None 651 652 def test_permission_list_ok(self): 653 """Tests the 'permission list' command in trac-admin.""" 654 rv, output = self.execute('permission list') 655 self.assertEqual(0, rv, output) 656 self.assertExpectedResult(output) 657 658 def test_permission_list_includes_undefined_actions(self): 659 """Undefined actions are included in the User Action table, 660 but not in the Available Actions list. 661 """ 662 self.env.disable_component(trac.search.web_ui.SearchModule) 663 rv, output = self.execute('permission list') 664 self.assertEqual(0, rv, output) 665 self.assertExpectedResult(output) 666 667 def test_permission_add_one_action_ok(self): 668 """ 669 Tests the 'permission add' command in trac-admin. This particular 670 test passes valid arguments to add one permission and checks for 671 success. 672 """ 673 self.execute('permission add test_user WIKI_VIEW') 674 rv, output = self.execute('permission list') 675 self.assertEqual(0, rv, output) 676 self.assertExpectedResult(output) 677 678 def test_permission_add_multiple_actions_ok(self): 679 """ 680 Tests the 'permission add' command in trac-admin. This particular 681 test passes valid arguments to add multiple permissions and checks for 682 success. 683 """ 684 self.execute('permission add test_user LOG_VIEW FILE_VIEW') 685 rv, output = self.execute('permission list') 686 self.assertEqual(0, rv, output) 687 self.assertExpectedResult(output) 688 689 def test_permission_add_already_exists(self): 690 """ 691 Tests the 'permission add' command in trac-admin. This particular 692 test passes a permission that already exists and checks for the 693 message. Other permissions passed are added. 694 """ 695 rv, output = self.execute('permission add anonymous WIKI_CREATE ' 696 'WIKI_VIEW WIKI_MODIFY') 697 self.assertEqual(0, rv, output) 698 rv, output2 = self.execute('permission list') 699 self.assertEqual(0, rv, output2) 700 self.assertExpectedResult(output + output2) 701 702 def test_permission_add_subject_already_in_group(self): 703 """ 704 Tests the 'permission add' command in trac-admin. This particular 705 test passes a group that the subject is already a member of and 706 checks for the message. Other permissions passed are added. 707 """ 708 rv, output1 = self.execute('permission add user1 group2') 709 self.assertEqual(0, rv, output1) 710 rv, output2 = self.execute('permission add user1 group1 group2 ' 711 'group3') 712 self.assertEqual(0, rv, output2) 713 rv, output3 = self.execute('permission list') 714 self.assertEqual(0, rv, output3) 715 self.assertExpectedResult(output2 + output3) 716 717 def test_permission_add_differs_from_action_by_casing(self): 718 """ 719 Tests the 'permission add' command in trac-admin. This particular 720 test passes a permission that differs from an action by casing and 721 checks for the message. None of the permissions in the list are 722 granted. 723 """ 724 rv, output = self.execute('permission add joe WIKI_CREATE ' 725 'Trac_Admin WIKI_MODIFY') 726 self.assertEqual(2, rv, output) 727 rv, output2 = self.execute('permission list') 728 self.assertEqual(0, rv, output2) 729 self.assertExpectedResult(output + output2) 730 731 def test_permission_add_unknown_action(self): 732 """ 733 Tests the 'permission add' command in trac-admin. This particular 734 test tries granting NOT_A_PERM to a user. NOT_A_PERM does not exist 735 in the system. None of the permissions in the list are granted. 736 """ 737 rv, output = self.execute('permission add joe WIKI_CREATE ' 738 'NOT_A_PERM WIKI_MODIFY') 739 self.assertEqual(2, rv, output) 740 rv, output2 = self.execute('permission list') 741 self.assertEqual(0, rv, output2) 742 self.assertExpectedResult(output + output2) 743 744 def test_permission_remove_one_action_ok(self): 745 """ 746 Tests the 'permission remove' command in trac-admin. This particular 747 test passes valid arguments to remove one permission and checks for 748 success. 749 """ 750 self.execute('permission remove anonymous TICKET_MODIFY') 751 rv, output = self.execute('permission list') 752 self.assertEqual(0, rv, output) 753 self.assertExpectedResult(output) 754 755 def test_permission_remove_multiple_actions_ok(self): 756 """ 757 Tests the 'permission remove' command in trac-admin. This particular 758 test passes valid arguments to remove multiple permission and checks 759 for success. 760 """ 761 self.execute('permission remove anonymous WIKI_CREATE WIKI_MODIFY') 762 rv, output = self.execute('permission list') 763 self.assertEqual(0, rv, output) 764 self.assertExpectedResult(output) 765 766 def test_permission_remove_all_actions_for_user(self): 767 """ 768 Tests the 'permission remove' command in trac-admin. This particular 769 test removes all permissions for anonymous. 770 """ 771 self.execute('permission remove anonymous *') 772 rv, output = self.execute('permission list') 773 self.assertEqual(0, rv, output) 774 self.assertExpectedResult(output) 775 776 def test_permission_remove_action_for_all_users(self): 777 """ 778 Tests the 'permission remove' command in trac-admin. This particular 779 test removes the TICKET_CREATE permission from all users. 780 """ 781 self.execute('permission add anonymous TICKET_CREATE') 782 self.execute('permission remove * TICKET_CREATE') 783 rv, output = self.execute('permission list') 784 self.assertEqual(0, rv, output) 785 self.assertExpectedResult(output) 786 787 def test_permission_remove_unknown_user(self): 788 """ 789 Tests the 'permission remove' command in trac-admin. This particular 790 test tries removing a permission from an unknown user. 791 """ 792 rv, output = self.execute('permission remove joe TICKET_VIEW') 793 self.assertEqual(2, rv, output) 794 self.assertExpectedResult(output) 795 796 def test_permission_remove_action_not_granted(self): 797 """ 798 Tests the 'permission remove' command in trac-admin. This particular 799 test tries removing TICKET_CREATE from user anonymous, who doesn't 800 have that permission. 801 """ 802 rv, output = self.execute('permission remove anonymous TICKET_CREATE') 803 self.assertEqual(2, rv, output) 804 self.assertExpectedResult(output) 805 806 def test_permission_remove_action_granted_through_meta_permission(self): 807 """ 808 Tests the 'permission remove' command in trac-admin. This particular 809 test tries removing WIKI_VIEW from a user. WIKI_VIEW has been granted 810 through user anonymous.""" 811 self.execute('permission add joe TICKET_VIEW') 812 rv, output = self.execute('permission remove joe WIKI_VIEW') 813 self.assertEqual(2, rv, output) 814 self.assertExpectedResult(output) 815 816 def test_permission_remove_unknown_action(self): 817 """ 818 Tests the 'permission remove' command in trac-admin. This particular 819 test tries removing NOT_A_PERM from a user. NOT_A_PERM does not exist 820 in the system.""" 821 rv, output = self.execute('permission remove joe NOT_A_PERM') 822 self.assertEqual(2, rv, output) 823 self.assertExpectedResult(output) 824 825 def test_permission_remove_unknown_action_granted(self): 826 """ 827 Tests the 'permission remove' command in trac-admin. This particular 828 test tries removing NOT_A_PERM from a user. NOT_A_PERM does not exist 829 in the system, but the user possesses the permission.""" 830 self.env.db_transaction(""" 831 INSERT INTO permission VALUES (%s, %s) 832 """, ('joe', 'NOT_A_PERM')) 833 rv, output = self.execute('permission remove joe NOT_A_PERM') 834 self.assertEqual(0, rv, output) 835 rv, output = self.execute('permission list') 836 self.assertEqual(0, rv, output) 837 self.assertExpectedResult(output) 838 839 def test_permission_export_ok(self): 840 """ 841 Tests the 'permission export' command in trac-admin. This particular 842 test exports the default permissions to stdout. 843 """ 844 rv, output = self.execute('permission export') 845 self.assertEqual(0, rv, output) 846 self.assertExpectedResult(output) 847 848 def test_permission_import_ok(self): 849 """ 850 Tests the 'permission import' command in trac-admin. This particular 851 test exports additional permissions, removes them and imports them back. 852 """ 853 user = 'test_user\u0250' 854 self.execute('permission add ' + user + ' WIKI_VIEW') 855 self.execute('permission add ' + user + ' TICKET_VIEW') 856 rv, output = self.execute('permission export') 857 self.execute('permission remove ' + user + ' *') 858 rv, output = self.execute('permission import', input=output) 859 self.assertEqual(0, rv, output) 860 self.assertEqual('', output) 861 rv, output = self.execute('permission list') 862 self.assertEqual(0, rv, output) 863 self.assertExpectedResult(output) 864 865 866def test_suite(): 867 suite = unittest.TestSuite() 868 suite.addTest(unittest.makeSuite(DefaultPermissionStoreTestCase)) 869 suite.addTest(unittest.makeSuite(PermissionErrorTestCase)) 870 suite.addTest(unittest.makeSuite(PermissionSystemTestCase)) 871 suite.addTest(unittest.makeSuite(PermissionCacheTestCase)) 872 suite.addTest(unittest.makeSuite(PermissionPolicyTestCase)) 873 suite.addTest(unittest.makeSuite(RecursivePolicyTestCase)) 874 suite.addTest(unittest.makeSuite(TracAdminTestCase)) 875 return suite 876 877 878if __name__ == '__main__': 879 unittest.main(defaultTest='test_suite') 880