├── .gitattributes ├── .gitignore ├── .idea ├── django-systemgroups.iml ├── inspectionProfiles │ └── profiles_settings.xml ├── misc.xml ├── modules.xml ├── vcs.xml └── workspace.xml ├── README.md ├── setup.py └── systemgroups ├── __init__.py ├── admin.py ├── apps.py ├── backends.py ├── base.py ├── migrations ├── 0001_initial.py └── __init__.py ├── models.py ├── settings.py ├── systemgroups.py ├── tests.py └── views.py /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | 7 | # Standard to msysgit 8 | *.doc diff=astextplain 9 | *.DOC diff=astextplain 10 | *.docx diff=astextplain 11 | *.DOCX diff=astextplain 12 | *.dot diff=astextplain 13 | *.DOT diff=astextplain 14 | *.pdf diff=astextplain 15 | *.PDF diff=astextplain 16 | *.rtf diff=astextplain 17 | *.RTF diff=astextplain 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | env/ 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | 27 | # PyInstaller 28 | # Usually these files are written by a python script from a template 29 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 30 | *.manifest 31 | *.spec 32 | 33 | # Installer logs 34 | pip-log.txt 35 | pip-delete-this-directory.txt 36 | 37 | # Unit test / coverage reports 38 | htmlcov/ 39 | .tox/ 40 | .coverage 41 | .coverage.* 42 | .cache 43 | nosetests.xml 44 | coverage.xml 45 | *,cover 46 | .hypothesis/ 47 | 48 | # Translations 49 | *.mo 50 | *.pot 51 | 52 | # Django stuff: 53 | *.log 54 | local_settings.py 55 | 56 | # Flask instance folder 57 | instance/ 58 | 59 | # Scrapy stuff: 60 | .scrapy 61 | 62 | # Sphinx documentation 63 | docs/_build/ 64 | 65 | # PyBuilder 66 | target/ 67 | 68 | # IPython Notebook 69 | .ipynb_checkpoints 70 | 71 | # pyenv 72 | .python-version 73 | 74 | # celery beat schedule file 75 | celerybeat-schedule 76 | 77 | # dotenv 78 | .env 79 | 80 | # virtualenv 81 | venv/ 82 | ENV/ 83 | 84 | # Spyder project settings 85 | .spyderproject 86 | 87 | # Rope project settings 88 | .ropeproject 89 | 90 | # ========================= 91 | # Operating System Files 92 | # ========================= 93 | 94 | # OSX 95 | # ========================= 96 | 97 | .DS_Store 98 | .AppleDouble 99 | .LSOverride 100 | 101 | # Thumbnails 102 | ._* 103 | 104 | # Files that might appear in the root of a volume 105 | .DocumentRevisions-V100 106 | .fseventsd 107 | .Spotlight-V100 108 | .TemporaryItems 109 | .Trashes 110 | .VolumeIcon.icns 111 | 112 | # Directories potentially created on remote AFP share 113 | .AppleDB 114 | .AppleDesktop 115 | Network Trash Folder 116 | Temporary Items 117 | .apdisk 118 | 119 | # Windows 120 | # ========================= 121 | 122 | # Windows image file caches 123 | Thumbs.db 124 | ehthumbs.db 125 | 126 | # Folder config file 127 | Desktop.ini 128 | 129 | # Recycle Bin used on file shares 130 | $RECYCLE.BIN/ 131 | 132 | # Windows Installer files 133 | *.cab 134 | *.msi 135 | *.msm 136 | *.msp 137 | 138 | # Windows shortcuts 139 | *.lnk 140 | -------------------------------------------------------------------------------- /.idea/django-systemgroups.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 11 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/workspace.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 17 | 18 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 55 | 56 | 57 | 58 | init_systemgroups 59 | 60 | 61 | 62 | 64 | 65 | 75 | 76 | 77 | 78 | 79 | true 80 | DEFINITION_ORDER 81 | 82 | 83 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 110 | 111 | 114 | 115 | 118 | 119 | 120 | 121 | 124 | 125 | 128 | 129 | 132 | 133 | 136 | 137 | 138 | 139 | 142 | 143 | 146 | 147 | 150 | 151 | 154 | 155 | 158 | 159 | 160 | 161 | 164 | 165 | 168 | 169 | 172 | 173 | 176 | 177 | 180 | 181 | 184 | 185 | 186 | 187 | 190 | 191 | 194 | 195 | 196 | 197 | 200 | 201 | 204 | 205 | 208 | 209 | 210 | 211 | 214 | 215 | 218 | 219 | 222 | 223 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 282 | 283 | 296 | 297 | 314 | 315 | 327 | 328 | project 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 363 | 364 | 383 | 384 | 405 | 406 | 428 | 429 | 453 | 454 | 455 | 457 | 458 | 459 | 460 | 1484115705188 461 | 465 | 466 | 467 | 468 | 469 | 470 | 471 | 472 | 473 | 474 | 475 | 476 | 477 | 478 | 479 | 480 | 481 | 482 | 483 | 484 | 485 | 486 | 487 | 488 | 489 | 490 | 491 | 492 | 494 | 495 | 497 | 498 | 499 | 500 | 501 | 502 | 503 | 504 | 505 | 506 | 507 | 508 | 509 | 510 | 511 | 512 | 513 | 514 | 515 | 516 | 517 | 518 | 519 | 520 | 521 | 522 | 523 | 524 | 525 | 526 | 527 | 528 | 529 | 530 | 531 | 532 | 533 | 534 | 535 | 536 | 537 | 538 | 539 | 540 | 541 | 542 | 543 | 544 | 545 | 546 | 547 | 548 | 549 | 550 | 551 | 552 | 553 | 554 | 555 | 556 | 557 | 558 | 559 | 560 | 561 | 562 | 563 | 564 | 565 | 566 | 567 | 568 | 569 | 570 | 571 | 572 | 573 | 574 | 575 | 576 | 577 | 578 | 579 | 580 | 581 | 582 | 583 | 584 | 585 | 586 | 587 | 588 | 589 | 590 | 591 | 592 | 593 | 594 | 595 | 596 | 597 | 598 | 599 | 600 | 601 | 602 | 603 | 604 | 605 | 606 | 607 | 608 | 609 | 610 | 611 | 612 | 613 | 614 | 615 | 616 | 617 | 618 | 619 | 620 | 621 | 622 | 623 | 624 | 625 | 626 | 627 | 628 | 629 | 630 | 631 | 632 | 633 | 634 | 635 | 636 | 637 | 638 | 639 | 640 | 641 | 642 | 643 | 644 | 645 | 646 | 647 | 648 | 649 | 650 | 651 | 652 | 653 | 654 | 655 | 656 | 657 | 658 | 659 | 660 | 661 | 662 | 663 | 664 | 665 | 666 | 667 | 668 | 669 | 670 | 671 | 672 | 673 | 674 | 675 | 676 | 677 | 678 | 679 | 680 | 681 | 682 | 683 | 684 | 685 | 686 | 687 | 688 | 689 | 690 | 691 | 692 | 693 | 694 | 695 | 696 | 697 | 698 | 699 | 700 | 701 | 702 | 703 | 704 | 705 | 706 | 707 | 708 | 709 | 710 | 711 | 712 | 713 | 714 | 715 | 716 | 717 | 718 | 719 | 720 | 721 | 722 | 723 | 724 | 725 | 726 | 727 | 728 | 729 | 730 | 731 | 732 | 733 | 734 | 735 | 736 | 737 | 738 | 739 | 740 | 741 | 742 | 743 | 744 | 745 | 746 | 747 | 748 | 749 | 750 | 751 | 752 | 753 | 754 | 755 | 756 | 757 | 758 | 759 | 760 | 761 | 762 | 763 | 764 | 765 | 766 | 767 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # django-systemgroups 2 | 基于Django实现的系统组特性。 3 | 4 | 文档参见:http://www.jianshu.com/p/83f6a4827185 5 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | from setuptools import setup 3 | 4 | 5 | VERSION = '1.1.3' 6 | 7 | setup( 8 | name='django-systemgroups', 9 | version=VERSION, 10 | url='https://github.com/Kidwind/django-systemgroups', 11 | author='Kidwind', 12 | author_email='Kidwind@gmail.com', 13 | description="基于Django实现的系统组。", 14 | zip_safe=False, 15 | packages=[ 16 | 'systemgroups', 17 | 'systemgroups.migrations', 18 | ], 19 | keywords='django system groups', 20 | license='BSD', 21 | classifiers=['Development Status :: 3 - Alpha', 22 | 'Environment :: Web Environment', 23 | 'Framework :: Django', 24 | 'Intended Audience :: Developers', 25 | 'License :: OSI Approved :: BSD License', 26 | 'Operating System :: OS Independent', 27 | 'Programming Language :: Python', 28 | 'Topic :: Security', 29 | 'Programming Language :: Python :: 2.7', 30 | 'Programming Language :: Python :: 3.3', 31 | 'Programming Language :: Python :: 3.4', 32 | 'Programming Language :: Python :: 3.5', 33 | ], 34 | ) -------------------------------------------------------------------------------- /systemgroups/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kidwind/django-systemgroups/3cab7aa9bfbce441c13bd5651f1d674eb3a81b57/systemgroups/__init__.py -------------------------------------------------------------------------------- /systemgroups/admin.py: -------------------------------------------------------------------------------- 1 | # coding=UTF-8 2 | from __future__ import unicode_literals 3 | 4 | from django.contrib.auth import get_permission_codename 5 | 6 | 7 | class SystemGroupAdminMixin(object): 8 | def has_change_permission(self, request, obj=None): 9 | opts = self.opts 10 | codename = get_permission_codename('change', opts) 11 | return request.user.has_perm("%s.%s" % (opts.app_label, codename), obj=obj) 12 | 13 | def has_delete_permission(self, request, obj=None): 14 | opts = self.opts 15 | codename = get_permission_codename('delete', opts) 16 | return request.user.has_perm("%s.%s" % (opts.app_label, codename), obj=obj) -------------------------------------------------------------------------------- /systemgroups/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class SystemgroupsConfig(AppConfig): 5 | name = 'systemgroups' 6 | -------------------------------------------------------------------------------- /systemgroups/backends.py: -------------------------------------------------------------------------------- 1 | # coding=UTF-8 2 | from __future__ import unicode_literals 3 | from django.db import models 4 | from .base import get_user_systemgroups, get_user_systemgroups_for_obj, get_groups_permissions 5 | 6 | 7 | class SystemGroupBackend(object): 8 | def authenticate(self, username=None, password=None, **kwargs): 9 | return None 10 | 11 | def has_perm(self, user_obj, perm, obj=None): 12 | return perm in self.get_all_permissions(user_obj, obj) 13 | 14 | def get_all_permissions(self, user_obj, obj=None): 15 | perms = self.get_group_permissions(user_obj, obj) 16 | return perms 17 | 18 | def get_group_permissions(self, user_obj, obj=None): 19 | result_perms = set() 20 | 21 | if not hasattr(user_obj, '_systemgroup_perm_cache'): 22 | groups = get_user_systemgroups(user_obj) 23 | perms = get_groups_permissions(groups) 24 | user_obj._systemgroup_perm_cache = perms 25 | result_perms.update(user_obj._systemgroup_perm_cache) 26 | 27 | if obj is None: 28 | return result_perms 29 | 30 | if isinstance(obj, models.Model) and obj.pk is None: 31 | # 如果 obj 为未持久化的对象,无法进行权限的缓存,则直接获取 user 及 obj 的系统组权限并返回 32 | groups = get_user_systemgroups_for_obj(user_obj, obj) 33 | perms = get_groups_permissions(groups) 34 | result_perms.update(perms) 35 | return result_perms 36 | 37 | if not hasattr(user_obj, '_systemgroup_perm_cache_for_obj'): 38 | user_obj._systemgroup_perm_cache_for_obj = {} 39 | if obj not in user_obj._systemgroup_perm_cache_for_obj: 40 | groups = get_user_systemgroups_for_obj(user_obj, obj) 41 | perms = get_groups_permissions(groups) 42 | user_obj._systemgroup_perm_cache_for_obj[obj] = perms 43 | result_perms.update(user_obj._systemgroup_perm_cache_for_obj[obj]) 44 | return result_perms 45 | 46 | def has_module_perms(self, user_obj, app_label): 47 | """ 48 | Return True if user_obj has any permissions in the given app_label. 49 | """ 50 | if not user_obj.is_active: 51 | return False 52 | for perm in self.get_all_permissions(user_obj): 53 | if perm[:perm.index('.')] == app_label: 54 | return True 55 | return False 56 | -------------------------------------------------------------------------------- /systemgroups/base.py: -------------------------------------------------------------------------------- 1 | # coding=UTF-8 2 | from __future__ import unicode_literals 3 | 4 | import importlib 5 | from django.contrib.auth.models import Permission, Group 6 | from django.core.cache import cache 7 | from django.db.models import signals 8 | 9 | from .settings import SYSTEM_GROUP_IMPLEMENTERS 10 | 11 | 12 | def get_user_systemgroups(user): 13 | """ 14 | 从所有应用中获取指定用户所属的系统组集合。 15 | :param user: 指定的用户。 16 | :return: set 表示的用户所属的系统组名称集合。 17 | """ 18 | imps = SYSTEM_GROUP_IMPLEMENTERS 19 | groups = set() 20 | if not imps: 21 | return groups 22 | for imp in imps: 23 | imp = importlib.import_module(imp) 24 | if hasattr(imp, "get_user_systemgroups"): 25 | groups.update(imp.get_user_systemgroups(user)) 26 | return groups 27 | 28 | 29 | def get_user_systemgroups_for_obj(user, obj): 30 | """ 31 | 从所有应用中获取指定用户相对于指定的对象所属的系统组集合。 32 | :param user: 指定的用户。 33 | :param obj: 相对于指定的对象。 34 | :return: set 表示的用户所属的系统组名称集合。 35 | """ 36 | imps = SYSTEM_GROUP_IMPLEMENTERS 37 | groups = set() 38 | if not imps: 39 | return groups 40 | for imp in imps: 41 | imp = importlib.import_module(imp) 42 | if hasattr(imp, "get_user_systemgroups_for_obj"): 43 | groups.update(imp.get_user_systemgroups_for_obj(user, obj)) 44 | return groups 45 | 46 | 47 | # 组权限缓存 48 | def _build_group_permissions_cache_key(group_name): 49 | """ 50 | 根据组名构建组权限缓存键。 51 | :param group_name: 进行构建的组名。 52 | :return: 组权限缓存键。 53 | """ 54 | return '__group_permissions_%s' % group_name 55 | 56 | 57 | def _get_group_permissions(name): 58 | """ 59 | 获取指定名称的组所拥有的权限集合。 60 | :param name: 组的名称。 61 | :return: 权限集合。 62 | """ 63 | perms = Permission.objects.filter(group__name = name) 64 | perms = perms.values_list('content_type__app_label', 'codename').order_by() 65 | return set(["%s.%s" % (ct, name) for ct, name in perms]) 66 | 67 | 68 | def _group_permissions_cache_clear_callback(sender, instance, **kwargs): 69 | """ 70 | 组权限缓存清除回调。 71 | """ 72 | key = _build_group_permissions_cache_key(instance.name) 73 | cache.delete(key) 74 | signals.post_save.connect(_group_permissions_cache_clear_callback, sender=Group) 75 | signals.post_delete.connect(_group_permissions_cache_clear_callback, sender=Group) 76 | 77 | 78 | def get_group_permissions(name): 79 | """ 80 | 获取指定名称的组所拥有的权限集合。 81 | :param name: 组的名称。 82 | :return: 权限集合。 83 | """ 84 | key = _build_group_permissions_cache_key(name) 85 | perms = None 86 | if not key in cache: 87 | perms = _get_group_permissions(name) 88 | cache.set(key, perms) 89 | else: 90 | perms = cache.get(key) 91 | return perms 92 | 93 | 94 | def get_groups_permissions(names): 95 | """ 96 | 获取指定名称的组所拥有的权限集合。 97 | :param names: 组的名称集合。 98 | :return: 权限集合。 99 | """ 100 | perms = set() 101 | for name in names: 102 | perms.update(get_group_permissions(name)) 103 | return perms -------------------------------------------------------------------------------- /systemgroups/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.10.5 on 2017-01-14 02:32 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations 6 | 7 | from ..systemgroups import SYSTEM_GROUP_EVERYONE, \ 8 | SYSTEM_GROUP_ANONYMOUS, SYSTEM_GROUP_USERS, SYSTEM_GROUP_STAFFS, \ 9 | SYSTEM_GROUP_CREATOR, SYSTEM_GROUP_OWNER 10 | 11 | 12 | def forwards_func(apps, schema_editor): 13 | Group = apps.get_model("auth", "Group") 14 | db_alias = schema_editor.connection.alias 15 | Group.objects.using(db_alias).bulk_create([ 16 | Group(name=SYSTEM_GROUP_EVERYONE), 17 | Group(name=SYSTEM_GROUP_ANONYMOUS), 18 | Group(name=SYSTEM_GROUP_USERS), 19 | Group(name=SYSTEM_GROUP_STAFFS), 20 | Group(name=SYSTEM_GROUP_CREATOR), 21 | Group(name=SYSTEM_GROUP_OWNER), 22 | ]) 23 | 24 | 25 | def reverse_func(apps, schema_editor): 26 | Group = apps.get_model("auth", "Group") 27 | db_alias = schema_editor.connection.alias 28 | Group.objects.using(db_alias).filter(name=SYSTEM_GROUP_EVERYONE).delete() 29 | Group.objects.using(db_alias).filter(name=SYSTEM_GROUP_ANONYMOUS).delete() 30 | Group.objects.using(db_alias).filter(name=SYSTEM_GROUP_USERS).delete() 31 | Group.objects.using(db_alias).filter(name=SYSTEM_GROUP_STAFFS).delete() 32 | Group.objects.using(db_alias).filter(name=SYSTEM_GROUP_CREATOR).delete() 33 | Group.objects.using(db_alias).filter(name=SYSTEM_GROUP_OWNER).delete() 34 | 35 | 36 | class Migration(migrations.Migration): 37 | 38 | dependencies = [ 39 | ] 40 | 41 | operations = [ 42 | migrations.RunPython(forwards_func, reverse_func), 43 | ] 44 | -------------------------------------------------------------------------------- /systemgroups/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Kidwind/django-systemgroups/3cab7aa9bfbce441c13bd5651f1d674eb3a81b57/systemgroups/migrations/__init__.py -------------------------------------------------------------------------------- /systemgroups/models.py: -------------------------------------------------------------------------------- 1 | # coding=UTF-8 2 | from __future__ import unicode_literals 3 | 4 | from django.utils.translation import ugettext as _ 5 | 6 | 7 | class CreatorMixin(object): 8 | """ 9 | 实现创建者的 Model 基类。 10 | """ 11 | def get_creator(self): 12 | """ 13 | 获取对象的创建者,子类重写该方法实现创建者对象的获取。 14 | :return: 当前对象的创建者。 15 | """ 16 | return None 17 | 18 | def set_creator(self, user): 19 | """ 20 | 设置对象的创建者,子类重写该方法实现创建者对象的设置。 21 | :param creator: 要设置为创建者的User对象。 22 | :return: 23 | """ 24 | pass 25 | 26 | 27 | class OwnerMixin(object): 28 | """ 29 | 实现所有者的 Model 基类。 30 | """ 31 | def get_owner(self): 32 | """ 33 | 获取对象的所有者,子类重写该方法实现所有者对象的获取。 34 | :return: 当前对象的所有者。 35 | """ 36 | return None 37 | 38 | def set_owner(self, user): 39 | """ 40 | 设置对象的所有者,子类重写该方法实现所有者对象的设置。 41 | :param owner: 要设置为所有者的User对象。 42 | :return: 43 | """ 44 | pass -------------------------------------------------------------------------------- /systemgroups/settings.py: -------------------------------------------------------------------------------- 1 | # coding=UTF-8 2 | from __future__ import unicode_literals 3 | 4 | from django.conf import settings 5 | 6 | SYSTEM_GROUP_IMPLEMENTERS = getattr(settings, 'SYSTEM_GROUP_IMPLEMENTERS', []) -------------------------------------------------------------------------------- /systemgroups/systemgroups.py: -------------------------------------------------------------------------------- 1 | # coding=UTF-8 2 | from __future__ import unicode_literals 3 | 4 | from django.contrib.auth.models import Group 5 | 6 | from .models import CreatorMixin, OwnerMixin 7 | 8 | 9 | SYSTEM_GROUP_EVERYONE = "Everyone" # 所有人 10 | SYSTEM_GROUP_ANONYMOUS = "Anonymous" # 匿名用户 11 | SYSTEM_GROUP_USERS = "Users" # 用户 12 | SYSTEM_GROUP_STAFFS = "Staffs" # 职员 13 | SYSTEM_GROUP_CREATOR = "Creator" # 创建者 14 | SYSTEM_GROUP_OWNER = "Owner" # 所有者 15 | 16 | 17 | def get_user_systemgroups(user): 18 | """ 19 | 获取指定用户所属的系统组集合。 20 | :param user: 指定的用户。 21 | :return: set 表示的用户所属的系统组名称集合。 22 | """ 23 | groups = set() 24 | groups.add(SYSTEM_GROUP_EVERYONE) 25 | if user.is_anonymous: 26 | groups.add(SYSTEM_GROUP_ANONYMOUS) 27 | else: 28 | groups.add(SYSTEM_GROUP_USERS) 29 | if user.is_staff: 30 | groups.add(SYSTEM_GROUP_STAFFS) 31 | 32 | return groups 33 | 34 | def get_user_systemgroups_for_obj(user, obj): 35 | """ 36 | 获取指定用户相对于指定的对象所属的系统组集合。 37 | :param user: 指定的用户。 38 | :param obj: 相对于指定的对象。 39 | :return: set 表示的用户所属的系统组名称集合。 40 | """ 41 | groups = set() 42 | if isinstance(obj, CreatorMixin) and obj.get_creator() == user: 43 | groups.add(SYSTEM_GROUP_CREATOR) 44 | if isinstance(obj, OwnerMixin) and obj.get_owner() == user: 45 | groups.add(SYSTEM_GROUP_OWNER) 46 | return groups -------------------------------------------------------------------------------- /systemgroups/tests.py: -------------------------------------------------------------------------------- 1 | # coding=UTF-8 2 | from __future__ import unicode_literals 3 | 4 | from django.utils.translation import ugettext_lazy as _ 5 | from django.test import TestCase 6 | from django.contrib.auth import get_user_model, get_permission_codename 7 | from django.contrib.auth.models import Group, AnonymousUser, Permission 8 | from django.conf import settings 9 | from django.db import models 10 | 11 | from .base import get_user_systemgroups, get_user_systemgroups_for_obj 12 | from .systemgroups import SYSTEM_GROUP_EVERYONE, \ 13 | SYSTEM_GROUP_ANONYMOUS, SYSTEM_GROUP_USERS, SYSTEM_GROUP_STAFFS, \ 14 | SYSTEM_GROUP_CREATOR, SYSTEM_GROUP_OWNER 15 | from .models import CreatorMixin, OwnerMixin 16 | from .backends import SystemGroupBackend 17 | 18 | 19 | class Info(CreatorMixin, OwnerMixin, models.Model): 20 | title = models.CharField(max_length=256, verbose_name=_('标题')) 21 | creator = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, verbose_name=_('创建者')) 22 | owner = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, verbose_name=_('所有者')) 23 | 24 | def get_creator(self): 25 | return self.creator 26 | 27 | def set_creator(self, user): 28 | self.creator = user 29 | 30 | def get_owner(self): 31 | return self.owner 32 | 33 | def set_owner(self, user): 34 | self.owner = user 35 | 36 | 37 | class SystemGroupTestCaseMixin(object): 38 | def setUp(self): 39 | User = get_user_model() 40 | 41 | user = User() 42 | user.username = 'tester' 43 | user.set_password('123456') 44 | user.is_staff = False 45 | user.save() 46 | 47 | user2 = User() 48 | user2.username = 'tester2' 49 | user2.set_password('123456') 50 | user2.is_staff = True 51 | user2.save() 52 | 53 | info = Info() 54 | info.title = "测试信息" 55 | info.set_creator(user) 56 | info.set_owner(user2) 57 | info.save() 58 | 59 | self.anonymous_user = AnonymousUser() 60 | self.user = user 61 | self.user2 = user2 62 | self.info = info 63 | 64 | 65 | class SystemGroupTestCase(SystemGroupTestCaseMixin, TestCase): 66 | def test_systemgroup_everyone(self): 67 | self.assertIn(SYSTEM_GROUP_EVERYONE, get_user_systemgroups(self.anonymous_user)) 68 | self.assertIn(SYSTEM_GROUP_EVERYONE, get_user_systemgroups(self.user)) 69 | 70 | def test_systemgroup_anonymous(self): 71 | self.assertIn(SYSTEM_GROUP_ANONYMOUS, get_user_systemgroups(self.anonymous_user)) 72 | self.assertNotIn(SYSTEM_GROUP_ANONYMOUS, get_user_systemgroups(self.user)) 73 | 74 | def test_systemgroup_users(self): 75 | self.assertNotIn(SYSTEM_GROUP_USERS, get_user_systemgroups(self.anonymous_user)) 76 | self.assertIn(SYSTEM_GROUP_USERS, get_user_systemgroups(self.user)) 77 | 78 | def test_systemgroup_staffs(self): 79 | self.assertNotIn(SYSTEM_GROUP_STAFFS, get_user_systemgroups(self.anonymous_user)) 80 | self.assertNotIn(SYSTEM_GROUP_STAFFS, get_user_systemgroups(self.user)) 81 | self.assertIn(SYSTEM_GROUP_STAFFS, get_user_systemgroups(self.user2)) 82 | 83 | def test_systemgroup_creator(self): 84 | self.assertNotIn(SYSTEM_GROUP_CREATOR, get_user_systemgroups_for_obj(self.anonymous_user, self.info)) 85 | self.assertIn(SYSTEM_GROUP_CREATOR, get_user_systemgroups_for_obj(self.user, self.info)) 86 | self.assertNotIn(SYSTEM_GROUP_CREATOR, get_user_systemgroups_for_obj(self.user2, self.info)) 87 | 88 | def test_systemgroup_owner(self): 89 | self.assertNotIn(SYSTEM_GROUP_OWNER, get_user_systemgroups_for_obj(self.anonymous_user, self.info)) 90 | self.assertNotIn(SYSTEM_GROUP_OWNER, get_user_systemgroups_for_obj(self.user, self.info)) 91 | self.assertIn(SYSTEM_GROUP_OWNER, get_user_systemgroups_for_obj(self.user2, self.info)) 92 | 93 | 94 | class SystemGroupBackendTestCase(SystemGroupTestCaseMixin, TestCase): 95 | def setUp(self): 96 | super(SystemGroupBackendTestCase, self).setUp() 97 | 98 | self.permission_change_group = Permission.objects.get_by_natural_key('change_group', Group._meta.app_label, 99 | Group._meta.model_name) 100 | self.permission_delete_group = Permission.objects.get_by_natural_key('delete_group', Group._meta.app_label, 101 | Group._meta.model_name) 102 | self.backend = SystemGroupBackend() 103 | 104 | def test_systemgroup_everyone(self): 105 | group = Group.objects.get_by_natural_key(SYSTEM_GROUP_EVERYONE) 106 | group.permissions.add(self.permission_change_group) 107 | 108 | self.assertTrue(self.backend.has_perm(self.anonymous_user, 'auth.change_group', obj=self.info)) 109 | self.assertFalse(self.backend.has_perm(self.anonymous_user, 'auth.delete_group', obj=self.info)) 110 | self.assertTrue(self.backend.has_perm(self.user, 'auth.change_group', obj=self.info)) 111 | self.assertFalse(self.backend.has_perm(self.user, 'auth.delete_group', obj=self.info)) 112 | 113 | def test_systemgroup_anonymous(self): 114 | group = Group.objects.get_by_natural_key(SYSTEM_GROUP_ANONYMOUS) 115 | group.permissions.add(self.permission_delete_group) 116 | 117 | self.assertFalse(self.backend.has_perm(self.anonymous_user, 'auth.change_group', obj=self.info)) 118 | self.assertTrue(self.backend.has_perm(self.anonymous_user, 'auth.delete_group', obj=self.info)) 119 | self.assertFalse(self.backend.has_perm(self.user, 'auth.change_group', obj=self.info)) 120 | self.assertFalse(self.backend.has_perm(self.user, 'auth.delete_group', obj=self.info)) 121 | 122 | def test_systemgroup_users(self): 123 | group = Group.objects.get_by_natural_key(SYSTEM_GROUP_USERS) 124 | group.permissions.add(self.permission_change_group) 125 | group.permissions.add(self.permission_delete_group) 126 | 127 | self.assertFalse(self.backend.has_perm(self.anonymous_user, 'auth.change_group', obj=self.info)) 128 | self.assertFalse(self.backend.has_perm(self.anonymous_user, 'auth.delete_group', obj=self.info)) 129 | self.assertTrue(self.backend.has_perm(self.user, 'auth.change_group', obj=self.info)) 130 | self.assertTrue(self.backend.has_perm(self.user, 'auth.delete_group', obj=self.info)) 131 | 132 | def test_systemgroup_staffs(self): 133 | group = Group.objects.get_by_natural_key(SYSTEM_GROUP_STAFFS) 134 | group.permissions.add(self.permission_change_group) 135 | group.permissions.add(self.permission_delete_group) 136 | 137 | self.assertFalse(self.backend.has_perm(self.anonymous_user, 'auth.change_group', obj=self.info)) 138 | self.assertFalse(self.backend.has_perm(self.anonymous_user, 'auth.delete_group', obj=self.info)) 139 | self.assertFalse(self.backend.has_perm(self.user, 'auth.change_group', obj=self.info)) 140 | self.assertFalse(self.backend.has_perm(self.user, 'auth.delete_group', obj=self.info)) 141 | self.assertTrue(self.backend.has_perm(self.user2, 'auth.change_group', obj=self.info)) 142 | self.assertTrue(self.backend.has_perm(self.user2, 'auth.delete_group', obj=self.info)) 143 | 144 | def test_systemgroup_creator(self): 145 | group = Group.objects.get_by_natural_key(SYSTEM_GROUP_CREATOR) 146 | group.permissions.add(self.permission_change_group) 147 | group.permissions.add(self.permission_delete_group) 148 | 149 | self.assertFalse(self.backend.has_perm(self.anonymous_user, 'auth.change_group', obj=self.info)) 150 | self.assertFalse(self.backend.has_perm(self.anonymous_user, 'auth.delete_group', obj=self.info)) 151 | self.assertTrue(self.backend.has_perm(self.user, 'auth.change_group', obj=self.info)) 152 | self.assertTrue(self.backend.has_perm(self.user, 'auth.delete_group', obj=self.info)) 153 | self.assertFalse(self.backend.has_perm(self.user2, 'auth.change_group', obj=self.info)) 154 | self.assertFalse(self.backend.has_perm(self.user2, 'auth.delete_group', obj=self.info)) 155 | 156 | def test_systemgroup_owner(self): 157 | group = Group.objects.get_by_natural_key(SYSTEM_GROUP_OWNER) 158 | group.permissions.add(self.permission_change_group) 159 | group.permissions.add(self.permission_delete_group) 160 | 161 | self.assertFalse(self.backend.has_perm(self.anonymous_user, 'auth.change_group', obj=self.info)) 162 | self.assertFalse(self.backend.has_perm(self.anonymous_user, 'auth.delete_group', obj=self.info)) 163 | self.assertFalse(self.backend.has_perm(self.user, 'auth.change_group', obj=self.info)) 164 | self.assertFalse(self.backend.has_perm(self.user, 'auth.delete_group', obj=self.info)) 165 | self.assertTrue(self.backend.has_perm(self.user2, 'auth.change_group', obj=self.info)) 166 | self.assertTrue(self.backend.has_perm(self.user2, 'auth.delete_group', obj=self.info)) -------------------------------------------------------------------------------- /systemgroups/views.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import render 2 | 3 | # Create your views here. 4 | --------------------------------------------------------------------------------